1 #pragma once
2 
3 #include "console_buffer.hpp"
4 #include "session.hpp"
5 
6 #include <boost/asio/io_context.hpp>
7 #include <boost/asio/steady_timer.hpp>
8 
9 #include <cstddef>
10 
11 namespace sol
12 {
13 
14 /** @struct Outbound
15  *
16  *  Operation/Status in an outbound SOL payload format(BMC to Remote Console).
17  */
18 struct Outbound
19 {
20 #if BYTE_ORDER == LITTLE_ENDIAN
21     uint8_t testMode:2;        //!< Not supported.
22     uint8_t breakDetected:1;   //!< Not supported.
23     uint8_t transmitOverrun:1; //!< Not supported.
24     uint8_t SOLDeactivating:1; //!< 0 : SOL is active, 1 : SOL deactivated.
25     uint8_t charUnavailable:1; //!< 0 : Available, 1 : Unavailable.
26     uint8_t ack:1;             //!< 0 : ACK, 1 : NACK.
27     uint8_t reserved:1;        //!< Reserved.
28 #endif
29 
30 #if BYTE_ORDER == BIG_ENDIAN
31     uint8_t reserved:1;        //!< Reserved.
32     uint8_t ack:1;             //!< 0 : ACK, 1 : NACK.
33     uint8_t charUnavailable:1; //!< 0 : Available, 1 : Unavailable.
34     uint8_t SOLDeactivating:1; //!< 0 : SOL is active, 1 : SOL deactivated.
35     uint8_t transmitOverrun:1; //!< Not supported.
36     uint8_t breakDetected:1;   //!< Not supported.
37     uint8_t testMode:2;        //!< Not supported.
38 #endif
39 } __attribute__((packed));
40 
41 /** @struct Inbound
42  *
43  *  Operation/Status in an Inbound SOL Payload format(Remote Console to BMC).
44  */
45 struct Inbound
46 {
47 #if BYTE_ORDER == LITTLE_ENDIAN
48     uint8_t flushOut:1;      //!< Not supported.
49     uint8_t flushIn:1;       //!< Not supported.
50     uint8_t dcd:1;           //!< Not supported.
51     uint8_t cts:1;           //!< Not supported.
52     uint8_t generateBreak:1; //!< Not supported.
53     uint8_t ring:1;          //!< Not supported.
54     uint8_t ack:1;           //!< 0 : ACK, 1 : NACK.
55     uint8_t reserved:1;      //!< Reserved.
56 #endif
57 
58 #if BYTE_ORDER == BIG_ENDIAN
59     uint8_t reserved:1;      //!< Reserved.
60     uint8_t ack:1;           //!< 0 : ACK, 1 : NACK.
61     uint8_t ring:1;          //!< Not supported.
62     uint8_t generateBreak:1; //!< Not supported.
63     uint8_t cts:1;           //!< Not supported.
64     uint8_t dcd:1;           //!< Not supported.
65     uint8_t flushIn:1;       //!< Not supported.
66     uint8_t flushOut:1;      //!< Not supported.
67 #endif
68 } __attribute__((packed));
69 
70 /** @struct Payload
71  *
72  *  SOL Payload Data Format.The following fields make up the SOL payload in an
73  *  RMCP+ packet, followed by the console character data.
74  */
75 struct Payload
76 {
77     uint8_t packetSeqNum;      //!< Packet sequence number
78     uint8_t packetAckSeqNum;   //!< Packet ACK/NACK sequence number
79     uint8_t acceptedCharCount; //!< Accepted character count
80     union
81     {
82         uint8_t operation;            //!< Operation/Status
83         struct Outbound outOperation; //!< BMC to Remote Console
84         struct Inbound inOperation;   //!< Remote Console to BMC
85     };
86 } __attribute__((packed));
87 
88 namespace internal
89 {
90 
91 /** @struct SequenceNumbers
92  *
93  *  SOL sequence numbers. At the session level, SOL Payloads share the session
94  *  sequence numbers for authenticated and unauthenticated packets with other
95  *  packets under the IPMI session. At the payload level, SOL packets include
96  *  their own message sequence numbers that are used for tracking missing and
97  *  retried SOL messages. The sequence numbers must be non-zero. Retried
98  *  packets use the same sequence number as the first packet.
99  */
100 struct SequenceNumbers
101 {
102     static constexpr uint8_t MAX_SOL_SEQUENCE_NUMBER = 0x10;
103 
104     /** @brief Get the SOL sequence number.
105      *
106      *  @param[in] inbound - true for inbound sequence number and false for
107      *                       outbound sequence number
108      *
109      *  @return sequence number
110      */
getsol::internal::SequenceNumbers111     auto get(bool inbound = true) const
112     {
113         return inbound ? in : out;
114     }
115 
116     /** @brief Increment the inbound SOL sequence number. */
incInboundSeqNumsol::internal::SequenceNumbers117     void incInboundSeqNum()
118     {
119         if ((++in) == MAX_SOL_SEQUENCE_NUMBER)
120         {
121             in = 1;
122         }
123     }
124 
125     /** @brief Increment the outbound SOL sequence number.
126      *
127      *  @return outbound sequence number to populate the SOL payload.
128      */
incOutboundSeqNumsol::internal::SequenceNumbers129     auto incOutboundSeqNum()
130     {
131         if ((++out) == MAX_SOL_SEQUENCE_NUMBER)
132         {
133             out = 1;
134         }
135 
136         return out;
137     }
138 
139   private:
140     uint8_t in = 1;  //!< Inbound sequence number.
141     uint8_t out = 0; //!< Outbound sequence number, since the first
142                      //!< operation is increment, it is initialised to 0
143 };
144 
145 } // namespace internal
146 
147 /** @class Context
148  *
149  *  Context keeps the state of the SOL session. The information needed to
150  *  maintain the state of the SOL is part of this class. This class provides
151  *  interfaces to handle incoming SOL payload, send response and send outbound
152  *  SOL payload.
153  */
154 class Context : public std::enable_shared_from_this<Context>
155 {
156   public:
157     Context() = delete;
158     ~Context() = default;
159     Context(const Context&) = delete;
160     Context& operator=(const Context&) = delete;
161     Context(Context&&) = delete;
162     Context& operator=(Context&&) = delete;
163 
164     /** @brief Context Factory
165      *
166      *  This is called by the SOL Manager when a SOL payload instance is
167      *  started for the activate payload command. Its purpose is to be able
168      *  to perform post-creation tasks on the object without changing the
169      *  code flow
170      *
171      *  @param[in] io  - boost::asio io context for event scheduling.
172      *  @param[in] maxRetryCount  - Retry count max value.
173      *  @param[in] sendThreshold - Character send threshold.
174      *  @param[in] instance - SOL payload instance.
175      *  @param[in] sessionID - BMC session ID.
176      */
177     static std::shared_ptr<Context> makeContext(
178         std::shared_ptr<boost::asio::io_context> io, uint8_t maxRetryCount,
179         uint8_t sendThreshold, uint8_t instance, session::SessionID sessionID);
180 
181     /** @brief Context Constructor.
182      *
183      *  This should only be used by the Context factory makeContext
184      *  or the accumulate timer will not be initialized properly
185      *
186      *  @param[in] io  - boost::asio io context for event scheduling.
187      *  @param[in] maxRetryCount  - Retry count max value.
188      *  @param[in] sendThreshold - Character send threshold.
189      *  @param[in] instance - SOL payload instance.
190      *  @param[in] sessionID - BMC session ID.
191      */
192     Context(std::shared_ptr<boost::asio::io_context> io, uint8_t maxRetryCount,
193             uint8_t sendThreshold, uint8_t instance,
194             session::SessionID sessionID);
195 
196     static constexpr auto clear = true;
197     static constexpr auto noClear = false;
198 
199     /** @brief accumulate timer */
200     boost::asio::steady_timer accumulateTimer;
201 
202     /** @brief retry timer */
203     boost::asio::steady_timer retryTimer;
204 
205     /** @brief Retry count max value. */
206     const uint8_t maxRetryCount = 0;
207 
208     /** @brief Retry counter. */
209     uint8_t retryCounter = 0;
210 
211     /** @brief Character send threshold. */
212     const uint8_t sendThreshold = 0;
213 
214     /** @brief SOL payload instance. */
215     const uint8_t payloadInstance = 0;
216 
217     /** @brief Session ID. */
218     const session::SessionID sessionID = 0;
219 
220     /** @brief session pointer
221      */
222     std::shared_ptr<session::Session> session;
223 
224     /** @brief enable/disable accumulate timer
225      *
226      *  The timeout interval is managed by the SOL Manager;
227      *  this function only enables or disable the timer
228      *
229      *  @param[in] enable - enable(true) or disable(false) accumulation timer
230      */
231     void enableAccumulateTimer(bool enable);
232 
233     /** @brief enable/disable retry timer
234      *
235      *  The timeout interval is managed by the SOL Manager;
236      *  this function only enables or disable the timer
237      *
238      *  @param[in] enable - enable(true) or disable(false) retry timer
239      */
240     void enableRetryTimer(bool enable);
241 
242     /** @brief Process the Inbound SOL payload.
243      *
244      *  The SOL payload from the remote console is processed and the
245      *  acknowledgment handling is done.
246      *
247      *  @param[in] seqNum - Packet sequence number.
248      *  @param[in] ackSeqNum - Packet ACK/NACK sequence number.
249      *  @param[in] count - Accepted character count.
250      *  @param[in] operation - ACK is false, NACK is true
251      *  @param[in] input - Incoming SOL character data.
252      */
253     void processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum, uint8_t count,
254                                bool status, bool isBreak,
255                                const std::vector<uint8_t>& input);
256 
257     /** @brief Send the outbound SOL payload.
258      *
259      *  @return zero on success and negative value if condition for sending
260      *          the payload fails.
261      */
262     int sendOutboundPayload();
263 
264     /** @brief Resend the SOL payload.
265      *
266      *  @param[in] clear - if true then send the payload and clear the
267      *                     cached payload, if false only send the payload.
268      */
269     void resendPayload(bool clear);
270 
271     /** @brief accumlate timer handler called by timer */
272     void charAccTimerHandler();
273 
274     /** @brief retry timer handler called by timer */
275     void retryTimerHandler();
276 
277   private:
278     /** @brief Expected character count.
279      *
280      *  Expected Sequence number and expected character count is set before
281      *  sending the SOL payload. The check is done against these values when
282      *  an incoming SOL payload is received.
283      */
284     size_t expectedCharCount = 0;
285 
286     /** @brief Inbound and Outbound sequence numbers. */
287     internal::SequenceNumbers seqNums;
288 
289     /** @brief Copy of the last sent SOL payload.
290      *
291      *  A copy of the SOL payload is kept here, so that when a retry needs
292      *  to be attempted the payload is sent again.
293      */
294     std::vector<uint8_t> payloadCache;
295 
296     /**
297      * @brief Send Response for Incoming SOL payload.
298      *
299      * @param[in] ackSeqNum - Packet ACK/NACK Sequence Number.
300      * @param[in] count - Accepted Character Count.
301      * @param[in] ack - Set ACK/NACK in the Operation.
302      */
303     void prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack);
304 
305     /** @brief Send the outgoing SOL payload.
306      *
307      *  @param[in] out - buffer containing the SOL payload.
308      */
309     void sendPayload(const std::vector<uint8_t>& out) const;
310 };
311 
312 } // namespace sol
313