Implement API to handle inbound SOL Payload

Change-Id: I4bcb98568d84ba384ac11b7777d936c7d5fc124e
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 6843a7e..35e5353 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -45,7 +45,8 @@
 	sol/sol_context.hpp \
 	sol/sol_manager.hpp \
 	sd_event_loop.cpp \
-	sol/sol_manager.cpp
+	sol/sol_manager.cpp \
+	sol/sol_context.cpp
 
 netipmid_CPPFLAGS = -DNET_IPMID_LIB_PATH=\"/usr/lib/net-ipmid/\"
 netipmid_LDFLAGS = $(SYSTEMD_LIBS) $(CRYPTO_LIBS) $(libmapper_LIBS) $(PHOSPHOR_LOGGING_LIBS) $(LIBADD_DLOPEN) -export-dynamic
diff --git a/sol/sol_context.cpp b/sol/sol_context.cpp
new file mode 100644
index 0000000..b81281b
--- /dev/null
+++ b/sol/sol_context.cpp
@@ -0,0 +1,123 @@
+#include <phosphor-logging/log.hpp>
+#include "main.hpp"
+#include "sd_event_loop.hpp"
+#include "sol_context.hpp"
+#include "sol_manager.hpp"
+
+namespace sol
+{
+
+using namespace phosphor::logging;
+
+void Context::processInboundPayload(uint8_t seqNum,
+                                    uint8_t ackSeqNum,
+                                    uint8_t count,
+                                    bool status,
+                                    const Buffer& input)
+{
+    uint8_t respAckSeqNum = 0;
+    uint8_t acceptedCount = 0;
+    auto ack = false;
+
+    /*
+     * Check if the Inbound sequence number is same as the expected one.
+     * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
+     * outstanding sequence numbers are not supported in this version of the SOL
+     * specification. Retried packets use the same sequence number as the first
+     * packet.
+     */
+    if(seqNum && (seqNum != seqNums.get(true)))
+    {
+        log<level::INFO>("Out of sequence SOL packet - packet is dropped");
+        return;
+    }
+
+    /*
+     * Check if the expected ACK/NACK sequence number is same as the
+     * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
+     * number is 0, then it is an informational packet. No request packet being
+     * ACK'd or NACK'd.
+     */
+    if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
+    {
+        log<level::INFO>("Out of sequence ack number - SOL packet is dropped");
+        return;
+    }
+
+    /*
+     * Retry the SOL payload packet in the following conditions:
+     *
+     * a) NACK in Operation/Status
+     * b) Accepted Character Count does not match with the sent out SOL payload
+     * c) Non-zero Packet ACK/NACK Sequence Number
+     */
+    if (status || ((count != expectedCharCount) && ackSeqNum))
+    {
+        resendPayload(noClear);
+        std::get<eventloop::EventLoop&>(singletonPool).switchTimer
+                (payloadInstance, eventloop::Timers::RETRY, false);
+        std::get<eventloop::EventLoop&>(singletonPool).switchTimer
+                (payloadInstance, eventloop::Timers::RETRY, true);
+        return;
+    }
+    /*
+     * Clear the sent data once the acknowledgment sequence number matches
+     * and the expected character count matches.
+     */
+    else if ((count == expectedCharCount) && ackSeqNum)
+    {
+        // Clear the Host Console Buffer
+        std::get<sol::Manager&>(singletonPool).dataBuffer.erase(count);
+
+        // Once it is acknowledged stop the retry interval timer
+        std::get<eventloop::EventLoop&>(singletonPool).switchTimer(
+                payloadInstance, eventloop::Timers::RETRY, false);
+
+        retryCounter = maxRetryCount;
+        expectedCharCount = 0;
+        payloadCache.clear();
+    }
+
+    // Write character data to the Host Console
+    if (!input.empty() && seqNum)
+    {
+        auto rc = std::get<sol::Manager&>(singletonPool).writeConsoleSocket(
+                input);
+        if (rc)
+        {
+            log<level::ERR>("Writing to console socket descriptor failed");
+            ack = true;
+        }
+        else
+        {
+            respAckSeqNum = seqNum;
+            ack = false;
+            acceptedCount = input.size();
+        }
+    }
+
+    if (seqNum != 0)
+    {
+        seqNums.incInboundSeqNum();
+        prepareResponse(respAckSeqNum, acceptedCount, ack);
+    }
+    else
+    {
+        std::get<eventloop::EventLoop&>(singletonPool).switchTimer
+                (payloadInstance, eventloop::Timers::ACCUMULATE, true);
+    }
+}
+
+void Context::prepareResponse(uint8_t ackSeqNum,
+                              uint8_t count,
+                              bool ack)
+{
+
+}
+
+void Context::resendPayload(bool clear)
+{
+
+}
+
+} // namespace sol
diff --git a/sol/sol_context.hpp b/sol/sol_context.hpp
index 81ad19b..9de88c9 100644
--- a/sol/sol_context.hpp
+++ b/sol/sol_context.hpp
@@ -6,6 +6,80 @@
 namespace sol
 {
 
+/** @struct Outbound
+ *
+ *  Operation/Status in an outbound SOL payload format(BMC to Remote Console).
+ */
+struct Outbound
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+    uint8_t testMode: 2;            //!< Not supported.
+    uint8_t breakDetected: 1;       //!< Not supported.
+    uint8_t transmitOverrun: 1;     //!< Not supported.
+    uint8_t SOLDeactivating: 1;     //!< 0 : SOL is active, 1 : SOL deactivated.
+    uint8_t charUnavailable: 1;     //!< 0 : Available, 1 : Unavailable.
+    uint8_t ack: 1;                 //!< 0 : ACK, 1 : NACK.
+    uint8_t reserved: 1;            //!< Reserved.
+#endif
+
+#if BYTE_ORDER == BIG_ENDIAN
+    uint8_t reserved: 1;        //!< Reserved.
+    uint8_t ack: 1;             //!< 0 : ACK, 1 : NACK.
+    uint8_t charUnavailable: 1; //!< 0 : Available, 1 : Unavailable.
+    uint8_t SOLDeactivating: 1; //!< 0 : SOL is active, 1 : SOL deactivated.
+    uint8_t transmitOverrun: 1; //!< Not supported.
+    uint8_t breakDetected: 1;   //!< Not supported.
+    uint8_t testMode: 2;        //!< Not supported.
+#endif
+} __attribute__((packed));
+
+/** @struct Inbound
+ *
+ *  Operation/Status in an Inbound SOL Payload format(Remote Console to BMC).
+ */
+struct Inbound
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+    uint8_t flushOut: 1;        //!< Not supported.
+    uint8_t flushIn: 1;         //!< Not supported.
+    uint8_t dcd: 1;             //!< Not supported.
+    uint8_t cts: 1;             //!< Not supported.
+    uint8_t generateBreak: 1;   //!< Not supported.
+    uint8_t ring: 1;            //!< Not supported.
+    uint8_t ack: 1;             //!< 0 : ACK, 1 : NACK.
+    uint8_t reserved: 1;        //!< Reserved.
+#endif
+
+#if BYTE_ORDER == BIG_ENDIAN
+    uint8_t reserved: 1;        //!< Reserved.
+    uint8_t ack: 1;             //!< 0 : ACK, 1 : NACK.
+    uint8_t ring: 1;            //!< Not supported.
+    uint8_t generateBreak: 1;   //!< Not supported.
+    uint8_t cts: 1;             //!< Not supported.
+    uint8_t dcd: 1;             //!< Not supported.
+    uint8_t flushIn: 1;         //!< Not supported.
+    uint8_t flushOut: 1;        //!< Not supported.
+#endif
+} __attribute__((packed));
+
+/** @struct Payload
+ *
+ *  SOL Payload Data Format.The following fields make up the SOL payload in an
+ *  RMCP+ packet, followed by the console character data.
+ */
+struct Payload
+{
+    uint8_t packetSeqNum;               //!< Packet sequence number
+    uint8_t packetAckSeqNum;            //!< Packet ACK/NACK sequence number
+    uint8_t acceptedCharCount;          //!< Accepted character count
+    union
+    {
+        uint8_t operation;              //!<Operation/Status
+        struct Outbound outOperation;   //!<BMC to Remote Console
+        struct Inbound inOperation;     //!<Remote Console to BMC
+    };
+} __attribute__((packed));
+
 namespace internal
 {