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