19e801a2bSVernon Mauery #include "sol_context.hpp" 29e801a2bSVernon Mauery 3fbcac2e7STom Joseph #include "main.hpp" 4fbcac2e7STom Joseph #include "sd_event_loop.hpp" 5fbcac2e7STom Joseph #include "sol_manager.hpp" 6fbcac2e7STom Joseph 79e801a2bSVernon Mauery #include <phosphor-logging/log.hpp> 89e801a2bSVernon Mauery 9fbcac2e7STom Joseph namespace sol 10fbcac2e7STom Joseph { 11fbcac2e7STom Joseph using namespace phosphor::logging; 12fbcac2e7STom Joseph 13*6f353e86SVernon Mauery Context::Context(std::shared_ptr<boost::asio::io_context> io, 14*6f353e86SVernon Mauery uint8_t maxRetryCount, uint8_t sendThreshold, uint8_t instance, 15*6f353e86SVernon Mauery session::SessionID sessionID) : 16*6f353e86SVernon Mauery accumulateTimer(*io), 17*6f353e86SVernon Mauery retryTimer(*io), maxRetryCount(maxRetryCount), retryCounter(maxRetryCount), 18*6f353e86SVernon Mauery sendThreshold(sendThreshold), payloadInstance(instance), 19*6f353e86SVernon Mauery sessionID(sessionID) 20*6f353e86SVernon Mauery { 21*6f353e86SVernon Mauery session = std::get<session::Manager&>(singletonPool).getSession(sessionID); 22*6f353e86SVernon Mauery enableAccumulateTimer(true); 23*6f353e86SVernon Mauery } 24*6f353e86SVernon Mauery 25*6f353e86SVernon Mauery void Context::enableAccumulateTimer(bool enable) 26*6f353e86SVernon Mauery { 27*6f353e86SVernon Mauery // fetch the timeout from the SOL manager 28*6f353e86SVernon Mauery std::chrono::microseconds interval = 29*6f353e86SVernon Mauery std::get<sol::Manager&>(singletonPool).accumulateInterval; 30*6f353e86SVernon Mauery if (enable) 31*6f353e86SVernon Mauery { 32*6f353e86SVernon Mauery accumulateTimer.expires_after(interval); 33*6f353e86SVernon Mauery accumulateTimer.async_wait([this](const boost::system::error_code& ec) { 34*6f353e86SVernon Mauery if (!ec) 35*6f353e86SVernon Mauery { 36*6f353e86SVernon Mauery charAccTimerHandler(); 37*6f353e86SVernon Mauery } 38*6f353e86SVernon Mauery }); 39*6f353e86SVernon Mauery } 40*6f353e86SVernon Mauery else 41*6f353e86SVernon Mauery { 42*6f353e86SVernon Mauery accumulateTimer.cancel(); 43*6f353e86SVernon Mauery } 44*6f353e86SVernon Mauery } 45*6f353e86SVernon Mauery 46*6f353e86SVernon Mauery void Context::enableRetryTimer(bool enable) 47*6f353e86SVernon Mauery { 48*6f353e86SVernon Mauery if (enable) 49*6f353e86SVernon Mauery { 50*6f353e86SVernon Mauery // fetch the timeout from the SOL manager 51*6f353e86SVernon Mauery std::chrono::microseconds interval = 52*6f353e86SVernon Mauery std::get<sol::Manager&>(singletonPool).retryInterval; 53*6f353e86SVernon Mauery retryTimer.expires_after(interval); 54*6f353e86SVernon Mauery retryTimer.async_wait([this](const boost::system::error_code& ec) { 55*6f353e86SVernon Mauery if (!ec) 56*6f353e86SVernon Mauery { 57*6f353e86SVernon Mauery retryTimerHandler(); 58*6f353e86SVernon Mauery } 59*6f353e86SVernon Mauery }); 60*6f353e86SVernon Mauery } 61*6f353e86SVernon Mauery else 62*6f353e86SVernon Mauery { 63*6f353e86SVernon Mauery retryTimer.cancel(); 64*6f353e86SVernon Mauery } 65*6f353e86SVernon Mauery } 66*6f353e86SVernon Mauery 679e801a2bSVernon Mauery void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum, 689e801a2bSVernon Mauery uint8_t count, bool status, 6970fd29cfSVernon Mauery const std::vector<uint8_t>& input) 70fbcac2e7STom Joseph { 71fbcac2e7STom Joseph uint8_t respAckSeqNum = 0; 72fbcac2e7STom Joseph uint8_t acceptedCount = 0; 73fbcac2e7STom Joseph auto ack = false; 74fbcac2e7STom Joseph 75fbcac2e7STom Joseph /* 76fbcac2e7STom Joseph * Check if the Inbound sequence number is same as the expected one. 77fbcac2e7STom Joseph * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple 78fbcac2e7STom Joseph * outstanding sequence numbers are not supported in this version of the SOL 79fbcac2e7STom Joseph * specification. Retried packets use the same sequence number as the first 80fbcac2e7STom Joseph * packet. 81fbcac2e7STom Joseph */ 82fbcac2e7STom Joseph if (seqNum && (seqNum != seqNums.get(true))) 83fbcac2e7STom Joseph { 84fbcac2e7STom Joseph log<level::INFO>("Out of sequence SOL packet - packet is dropped"); 85fbcac2e7STom Joseph return; 86fbcac2e7STom Joseph } 87fbcac2e7STom Joseph 88fbcac2e7STom Joseph /* 89fbcac2e7STom Joseph * Check if the expected ACK/NACK sequence number is same as the 90fbcac2e7STom Joseph * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence 91fbcac2e7STom Joseph * number is 0, then it is an informational packet. No request packet being 92fbcac2e7STom Joseph * ACK'd or NACK'd. 93fbcac2e7STom Joseph */ 94fbcac2e7STom Joseph if (ackSeqNum && (ackSeqNum != seqNums.get(false))) 95fbcac2e7STom Joseph { 96fbcac2e7STom Joseph log<level::INFO>("Out of sequence ack number - SOL packet is dropped"); 97fbcac2e7STom Joseph return; 98fbcac2e7STom Joseph } 99fbcac2e7STom Joseph 100fbcac2e7STom Joseph /* 101fbcac2e7STom Joseph * Retry the SOL payload packet in the following conditions: 102fbcac2e7STom Joseph * 103fbcac2e7STom Joseph * a) NACK in Operation/Status 104fbcac2e7STom Joseph * b) Accepted Character Count does not match with the sent out SOL payload 105fbcac2e7STom Joseph * c) Non-zero Packet ACK/NACK Sequence Number 106fbcac2e7STom Joseph */ 107fbcac2e7STom Joseph if (status || ((count != expectedCharCount) && ackSeqNum)) 108fbcac2e7STom Joseph { 109fbcac2e7STom Joseph resendPayload(noClear); 110*6f353e86SVernon Mauery enableRetryTimer(false); 111*6f353e86SVernon Mauery enableRetryTimer(true); 112fbcac2e7STom Joseph return; 113fbcac2e7STom Joseph } 114fbcac2e7STom Joseph /* 115fbcac2e7STom Joseph * Clear the sent data once the acknowledgment sequence number matches 116fbcac2e7STom Joseph * and the expected character count matches. 117fbcac2e7STom Joseph */ 118fbcac2e7STom Joseph else if ((count == expectedCharCount) && ackSeqNum) 119fbcac2e7STom Joseph { 120fbcac2e7STom Joseph // Clear the Host Console Buffer 121fbcac2e7STom Joseph std::get<sol::Manager&>(singletonPool).dataBuffer.erase(count); 122fbcac2e7STom Joseph 123fbcac2e7STom Joseph // Once it is acknowledged stop the retry interval timer 124*6f353e86SVernon Mauery enableRetryTimer(false); 125fbcac2e7STom Joseph 126fbcac2e7STom Joseph retryCounter = maxRetryCount; 127fbcac2e7STom Joseph expectedCharCount = 0; 128fbcac2e7STom Joseph payloadCache.clear(); 129fbcac2e7STom Joseph } 130fbcac2e7STom Joseph 131fbcac2e7STom Joseph // Write character data to the Host Console 132fbcac2e7STom Joseph if (!input.empty() && seqNum) 133fbcac2e7STom Joseph { 1349e801a2bSVernon Mauery auto rc = 1359e801a2bSVernon Mauery std::get<sol::Manager&>(singletonPool).writeConsoleSocket(input); 136fbcac2e7STom Joseph if (rc) 137fbcac2e7STom Joseph { 138fbcac2e7STom Joseph log<level::ERR>("Writing to console socket descriptor failed"); 139fbcac2e7STom Joseph ack = true; 140fbcac2e7STom Joseph } 141fbcac2e7STom Joseph else 142fbcac2e7STom Joseph { 143fbcac2e7STom Joseph respAckSeqNum = seqNum; 144fbcac2e7STom Joseph ack = false; 145fbcac2e7STom Joseph acceptedCount = input.size(); 146fbcac2e7STom Joseph } 147fbcac2e7STom Joseph } 148694fc0cfSTom Joseph /* 149694fc0cfSTom Joseph * SOL payload with no character data and valid sequence number can be used 150694fc0cfSTom Joseph * as method to keep the SOL session active. 151694fc0cfSTom Joseph */ 152694fc0cfSTom Joseph else if (input.empty() && seqNum) 153694fc0cfSTom Joseph { 154694fc0cfSTom Joseph respAckSeqNum = seqNum; 155694fc0cfSTom Joseph } 156fbcac2e7STom Joseph 157fbcac2e7STom Joseph if (seqNum != 0) 158fbcac2e7STom Joseph { 159fbcac2e7STom Joseph seqNums.incInboundSeqNum(); 160fbcac2e7STom Joseph prepareResponse(respAckSeqNum, acceptedCount, ack); 161fbcac2e7STom Joseph } 162fbcac2e7STom Joseph else 163fbcac2e7STom Joseph { 164*6f353e86SVernon Mauery enableAccumulateTimer(true); 165fbcac2e7STom Joseph } 166fbcac2e7STom Joseph } 167fbcac2e7STom Joseph 16875e15db4STom Joseph void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack) 16975e15db4STom Joseph { 1709e801a2bSVernon Mauery auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size(); 17175e15db4STom Joseph 17275e15db4STom Joseph /* Sent a ACK only response */ 17375e15db4STom Joseph if (payloadCache.size() != 0 || (bufferSize < sendThreshold)) 17475e15db4STom Joseph { 175*6f353e86SVernon Mauery enableAccumulateTimer(true); 17675e15db4STom Joseph 17770fd29cfSVernon Mauery std::vector<uint8_t> outPayload(sizeof(Payload)); 17875e15db4STom Joseph auto response = reinterpret_cast<Payload*>(outPayload.data()); 17975e15db4STom Joseph response->packetSeqNum = 0; 18075e15db4STom Joseph response->packetAckSeqNum = ackSeqNum; 18175e15db4STom Joseph response->acceptedCharCount = count; 18275e15db4STom Joseph response->outOperation.ack = ack; 18375e15db4STom Joseph sendPayload(outPayload); 18475e15db4STom Joseph return; 18575e15db4STom Joseph } 18675e15db4STom Joseph 18775e15db4STom Joseph auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE); 18875e15db4STom Joseph payloadCache.resize(sizeof(Payload) + readSize); 18975e15db4STom Joseph auto response = reinterpret_cast<Payload*>(payloadCache.data()); 19075e15db4STom Joseph response->packetAckSeqNum = ackSeqNum; 19175e15db4STom Joseph response->acceptedCharCount = count; 19275e15db4STom Joseph response->outOperation.ack = ack; 19375e15db4STom Joseph response->packetSeqNum = seqNums.incOutboundSeqNum(); 19475e15db4STom Joseph 19575e15db4STom Joseph auto handle = std::get<sol::Manager&>(singletonPool).dataBuffer.read(); 19675e15db4STom Joseph std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload)); 19775e15db4STom Joseph expectedCharCount = readSize; 19875e15db4STom Joseph 199*6f353e86SVernon Mauery enableRetryTimer(true); 200*6f353e86SVernon Mauery enableAccumulateTimer(false); 20175e15db4STom Joseph 20275e15db4STom Joseph sendPayload(payloadCache); 20375e15db4STom Joseph } 20475e15db4STom Joseph 2057306314fSTom Joseph int Context::sendOutboundPayload() 2067306314fSTom Joseph { 2077306314fSTom Joseph if (payloadCache.size() != 0) 2087306314fSTom Joseph { 209*6f353e86SVernon Mauery enableAccumulateTimer(true); 2107306314fSTom Joseph return -1; 2117306314fSTom Joseph } 2127306314fSTom Joseph 2137306314fSTom Joseph auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size(); 2147306314fSTom Joseph auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE); 2157306314fSTom Joseph 2167306314fSTom Joseph payloadCache.resize(sizeof(Payload) + readSize); 2177306314fSTom Joseph auto response = reinterpret_cast<Payload*>(payloadCache.data()); 2187306314fSTom Joseph response->packetAckSeqNum = 0; 2197306314fSTom Joseph response->acceptedCharCount = 0; 2207306314fSTom Joseph response->outOperation.ack = false; 2217306314fSTom Joseph response->packetSeqNum = seqNums.incOutboundSeqNum(); 2227306314fSTom Joseph 2237306314fSTom Joseph auto handle = std::get<sol::Manager&>(singletonPool).dataBuffer.read(); 2247306314fSTom Joseph std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload)); 2257306314fSTom Joseph expectedCharCount = readSize; 2267306314fSTom Joseph 227*6f353e86SVernon Mauery enableRetryTimer(true); 228*6f353e86SVernon Mauery enableAccumulateTimer(false); 2297306314fSTom Joseph 2307306314fSTom Joseph sendPayload(payloadCache); 2317306314fSTom Joseph 2327306314fSTom Joseph return 0; 2337306314fSTom Joseph } 2347306314fSTom Joseph 23575e15db4STom Joseph void Context::resendPayload(bool clear) 236fbcac2e7STom Joseph { 2372fd466f4STom Joseph sendPayload(payloadCache); 238fbcac2e7STom Joseph 2392fd466f4STom Joseph if (clear) 2402fd466f4STom Joseph { 2412fd466f4STom Joseph payloadCache.clear(); 2422fd466f4STom Joseph expectedCharCount = 0; 2439e801a2bSVernon Mauery std::get<sol::Manager&>(singletonPool) 2449e801a2bSVernon Mauery .dataBuffer.erase(expectedCharCount); 2452fd466f4STom Joseph } 246fbcac2e7STom Joseph } 247fbcac2e7STom Joseph 24870fd29cfSVernon Mauery void Context::sendPayload(const std::vector<uint8_t>& out) const 249fbcac2e7STom Joseph { 25022596f21STom Joseph message::Handler msgHandler(session->channelPtr, sessionID); 25122596f21STom Joseph 25222596f21STom Joseph msgHandler.sendSOLPayload(out); 253fbcac2e7STom Joseph } 254fbcac2e7STom Joseph 255*6f353e86SVernon Mauery void Context::charAccTimerHandler() 256*6f353e86SVernon Mauery { 257*6f353e86SVernon Mauery auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size(); 258*6f353e86SVernon Mauery 259*6f353e86SVernon Mauery try 260*6f353e86SVernon Mauery { 261*6f353e86SVernon Mauery if (bufferSize > 0) 262*6f353e86SVernon Mauery { 263*6f353e86SVernon Mauery int rc = sendOutboundPayload(); 264*6f353e86SVernon Mauery if (rc == 0) 265*6f353e86SVernon Mauery { 266*6f353e86SVernon Mauery return; 267*6f353e86SVernon Mauery } 268*6f353e86SVernon Mauery } 269*6f353e86SVernon Mauery enableAccumulateTimer(true); 270*6f353e86SVernon Mauery } 271*6f353e86SVernon Mauery catch (std::exception& e) 272*6f353e86SVernon Mauery { 273*6f353e86SVernon Mauery log<level::ERR>(e.what()); 274*6f353e86SVernon Mauery } 275*6f353e86SVernon Mauery } 276*6f353e86SVernon Mauery 277*6f353e86SVernon Mauery void Context::retryTimerHandler() 278*6f353e86SVernon Mauery { 279*6f353e86SVernon Mauery try 280*6f353e86SVernon Mauery { 281*6f353e86SVernon Mauery if (retryCounter) 282*6f353e86SVernon Mauery { 283*6f353e86SVernon Mauery --retryCounter; 284*6f353e86SVernon Mauery enableRetryTimer(true); 285*6f353e86SVernon Mauery resendPayload(sol::Context::noClear); 286*6f353e86SVernon Mauery } 287*6f353e86SVernon Mauery else 288*6f353e86SVernon Mauery { 289*6f353e86SVernon Mauery retryCounter = maxRetryCount; 290*6f353e86SVernon Mauery resendPayload(sol::Context::clear); 291*6f353e86SVernon Mauery enableRetryTimer(false); 292*6f353e86SVernon Mauery enableAccumulateTimer(true); 293*6f353e86SVernon Mauery } 294*6f353e86SVernon Mauery } 295*6f353e86SVernon Mauery catch (std::exception& e) 296*6f353e86SVernon Mauery { 297*6f353e86SVernon Mauery log<level::ERR>(e.what()); 298*6f353e86SVernon Mauery } 299*6f353e86SVernon Mauery } 300fbcac2e7STom Joseph } // namespace sol 301