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 #include <cstddef>
9 
10 namespace sol
11 {
12 
13 /** @struct Outbound
14  *
15  *  Operation/Status in an outbound SOL payload format(BMC to Remote Console).
16  */
17 struct Outbound
18 {
19 #if BYTE_ORDER == LITTLE_ENDIAN
20     uint8_t testMode : 2;        //!< Not supported.
21     uint8_t breakDetected : 1;   //!< Not supported.
22     uint8_t transmitOverrun : 1; //!< Not supported.
23     uint8_t SOLDeactivating : 1; //!< 0 : SOL is active, 1 : SOL deactivated.
24     uint8_t charUnavailable : 1; //!< 0 : Available, 1 : Unavailable.
25     uint8_t ack : 1;             //!< 0 : ACK, 1 : NACK.
26     uint8_t reserved : 1;        //!< Reserved.
27 #endif
28 
29 #if BYTE_ORDER == BIG_ENDIAN
30     uint8_t reserved : 1;        //!< Reserved.
31     uint8_t ack : 1;             //!< 0 : ACK, 1 : NACK.
32     uint8_t charUnavailable : 1; //!< 0 : Available, 1 : Unavailable.
33     uint8_t SOLDeactivating : 1; //!< 0 : SOL is active, 1 : SOL deactivated.
34     uint8_t transmitOverrun : 1; //!< Not supported.
35     uint8_t breakDetected : 1;   //!< Not supported.
36     uint8_t testMode : 2;        //!< Not supported.
37 #endif
38 } __attribute__((packed));
39 
40 /** @struct Inbound
41  *
42  *  Operation/Status in an Inbound SOL Payload format(Remote Console to BMC).
43  */
44 struct Inbound
45 {
46 #if BYTE_ORDER == LITTLE_ENDIAN
47     uint8_t flushOut : 1;      //!< Not supported.
48     uint8_t flushIn : 1;       //!< Not supported.
49     uint8_t dcd : 1;           //!< Not supported.
50     uint8_t cts : 1;           //!< Not supported.
51     uint8_t generateBreak : 1; //!< Not supported.
52     uint8_t ring : 1;          //!< Not supported.
53     uint8_t ack : 1;           //!< 0 : ACK, 1 : NACK.
54     uint8_t reserved : 1;      //!< Reserved.
55 #endif
56 
57 #if BYTE_ORDER == BIG_ENDIAN
58     uint8_t reserved : 1;      //!< Reserved.
59     uint8_t ack : 1;           //!< 0 : ACK, 1 : NACK.
60     uint8_t ring : 1;          //!< Not supported.
61     uint8_t generateBreak : 1; //!< Not supported.
62     uint8_t cts : 1;           //!< Not supported.
63     uint8_t dcd : 1;           //!< Not supported.
64     uint8_t flushIn : 1;       //!< Not supported.
65     uint8_t flushOut : 1;      //!< Not supported.
66 #endif
67 } __attribute__((packed));
68 
69 /** @struct Payload
70  *
71  *  SOL Payload Data Format.The following fields make up the SOL payload in an
72  *  RMCP+ packet, followed by the console character data.
73  */
74 struct Payload
75 {
76     uint8_t packetSeqNum;      //!< Packet sequence number
77     uint8_t packetAckSeqNum;   //!< Packet ACK/NACK sequence number
78     uint8_t acceptedCharCount; //!< Accepted character count
79     union
80     {
81         uint8_t operation;            //!< Operation/Status
82         struct Outbound outOperation; //!< BMC to Remote Console
83         struct Inbound inOperation;   //!< Remote Console to BMC
84     };
85 } __attribute__((packed));
86 
87 namespace internal
88 {
89 
90 /** @struct SequenceNumbers
91  *
92  *  SOL sequence numbers. At the session level, SOL Payloads share the session
93  *  sequence numbers for authenticated and unauthenticated packets with other
94  *  packets under the IPMI session. At the payload level, SOL packets include
95  *  their own message sequence numbers that are used for tracking missing and
96  *  retried SOL messages. The sequence numbers must be non-zero. Retried
97  *  packets use the same sequence number as the first packet.
98  */
99 struct SequenceNumbers
100 {
101     static constexpr uint8_t MAX_SOL_SEQUENCE_NUMBER = 0x10;
102 
103     /** @brief Get the SOL sequence number.
104      *
105      *  @param[in] inbound - true for inbound sequence number and false for
106      *                       outbound sequence number
107      *
108      *  @return sequence number
109      */
110     auto get(bool inbound = true) const
111     {
112         return inbound ? in : out;
113     }
114 
115     /** @brief Increment the inbound SOL sequence number. */
116     void incInboundSeqNum()
117     {
118         if ((++in) == MAX_SOL_SEQUENCE_NUMBER)
119         {
120             in = 1;
121         }
122     }
123 
124     /** @brief Increment the outbound SOL sequence number.
125      *
126      *  @return outbound sequence number to populate the SOL payload.
127      */
128     auto incOutboundSeqNum()
129     {
130         if ((++out) == MAX_SOL_SEQUENCE_NUMBER)
131         {
132             out = 1;
133         }
134 
135         return out;
136     }
137 
138   private:
139     uint8_t in = 1;  //!< Inbound sequence number.
140     uint8_t out = 0; //!< Outbound sequence number, since the first
141                      //!< operation is increment, it is initialised to 0
142 };
143 
144 } // namespace internal
145 
146 /** @class Context
147  *
148  *  Context keeps the state of the SOL session. The information needed to
149  *  maintain the state of the SOL is part of this class. This class provides
150  *  interfaces to handle incoming SOL payload, send response and send outbound
151  *  SOL payload.
152  */
153 class Context : public std::enable_shared_from_this<Context>
154 {
155   public:
156     Context() = delete;
157     ~Context() = default;
158     Context(const Context&) = delete;
159     Context& operator=(const Context&) = delete;
160     Context(Context&&) = delete;
161     Context& operator=(Context&&) = delete;
162 
163     /** @brief Context Factory
164      *
165      *  This is called by the SOL Manager when a SOL payload instance is
166      *  started for the activate payload command. Its purpose is to be able
167      *  to perform post-creation tasks on the object without changing the
168      *  code flow
169      *
170      *  @param[in] io  - boost::asio io context for event scheduling.
171      *  @param[in] maxRetryCount  - Retry count max value.
172      *  @param[in] sendThreshold - Character send threshold.
173      *  @param[in] instance - SOL payload instance.
174      *  @param[in] sessionID - BMC session ID.
175      */
176     static std::shared_ptr<Context>
177         makeContext(std::shared_ptr<boost::asio::io_context> io,
178                     uint8_t maxRetryCount, uint8_t sendThreshold,
179                     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, const std::vector<uint8_t>& input);
255 
256     /** @brief Send the outbound SOL payload.
257      *
258      *  @return zero on success and negative value if condition for sending
259      *          the payload fails.
260      */
261     int sendOutboundPayload();
262 
263     /** @brief Resend the SOL payload.
264      *
265      *  @param[in] clear - if true then send the payload and clear the
266      *                     cached payload, if false only send the payload.
267      */
268     void resendPayload(bool clear);
269 
270     /** @brief accumlate timer handler called by timer */
271     void charAccTimerHandler();
272 
273     /** @brief retry timer handler called by timer */
274     void retryTimerHandler();
275 
276   private:
277     /** @brief Expected character count.
278      *
279      *  Expected Sequence number and expected character count is set before
280      *  sending the SOL payload. The check is done against these values when
281      *  an incoming SOL payload is received.
282      */
283     size_t expectedCharCount = 0;
284 
285     /** @brief Inbound and Outbound sequence numbers. */
286     internal::SequenceNumbers seqNums;
287 
288     /** @brief Copy of the last sent SOL payload.
289      *
290      *  A copy of the SOL payload is kept here, so that when a retry needs
291      *  to be attempted the payload is sent again.
292      */
293     std::vector<uint8_t> payloadCache;
294 
295     /**
296      * @brief Send Response for Incoming SOL payload.
297      *
298      * @param[in] ackSeqNum - Packet ACK/NACK Sequence Number.
299      * @param[in] count - Accepted Character Count.
300      * @param[in] ack - Set ACK/NACK in the Operation.
301      */
302     void prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack);
303 
304     /** @brief Send the outgoing SOL payload.
305      *
306      *  @param[in] out - buffer containing the SOL payload.
307      */
308     void sendPayload(const std::vector<uint8_t>& out) const;
309 };
310 
311 } // namespace sol
312