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