Add: Get channel payload support command

Implemented Get Channel Payload Support command (IPMI Spec sec 24.8)
The same is implemented under channel commands, and will be
available for both Host & Net ipmid. Uses channel configuration
to differentiate between session / session-less channel

Tested-by:
1. Verified command executed successfully for session
based channel
ipmitool raw 6 0x4E 0x1 // Command
03 00 15 00 00 00 00 00 // Response
2. Verified sessionless channel error is returned.

Change-Id: I8b294234415b4467aeae6c23c192750471536f4e
Signed-off-by: Saravanan Palanisamy <saravanan.palanisamy@intel.com>
Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com>
diff --git a/user_channel/channel_layer.hpp b/user_channel/channel_layer.hpp
index 94570fb..1a8d64c 100644
--- a/user_channel/channel_layer.hpp
+++ b/user_channel/channel_layer.hpp
@@ -112,6 +112,24 @@
     oem = (1 << 0x5),
 };
 
+// TODO: Remove duplicate 'PayloadType' definition from netipmid's message.hpp
+// to phosphor-ipmi-host/include
+/**
+ * @enum Payload Types (refer spec sec 13.27.3)
+ */
+enum class PayloadType : uint8_t
+{
+    IPMI = 0x00,
+    SOL = 0x01,
+    OPEN_SESSION_REQUEST = 0x10,
+    OPEN_SESSION_RESPONSE = 0x11,
+    RAKP1 = 0x12,
+    RAKP2 = 0x13,
+    RAKP3 = 0x14,
+    RAKP4 = 0x15,
+    INVALID = 0xFF,
+};
+
 /**
  * @enum Access mode for channel access set/get (refer spec
  * sec 22.22 - request byte 2[7:6])
diff --git a/user_channel/channelcommands.cpp b/user_channel/channelcommands.cpp
index 546adb0..d1b275e 100644
--- a/user_channel/channelcommands.cpp
+++ b/user_channel/channelcommands.cpp
@@ -152,6 +152,36 @@
     uint8_t auxChInfo[2];
 } __attribute__((packed));
 
+/** @struct GetChannelPayloadSupportReq
+ *
+ *  Structure for get channel payload support command request (refer spec
+ *  sec 24.8)
+ */
+struct GetChannelPayloadSupportReq
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+    uint8_t chNum : 4;
+    uint8_t reserved : 4;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+    uint8_t reserved : 4;
+    uint8_t chNum : 4;
+#endif
+} __attribute__((packed));
+
+/** @struct GetChannelPayloadSupportResp
+ *
+ *  Structure for get channel payload support command response (refer spec
+ *  sec 24.8)
+ */
+struct GetChannelPayloadSupportResp
+{
+    uint8_t stdPayloadType[2];
+    uint8_t sessSetupPayloadType[2];
+    uint8_t OEMPayloadType[2];
+    uint8_t reserved[2];
+} __attribute__((packed));
+
 ipmi_ret_t ipmiSetChannelAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
                                 ipmi_request_t request,
                                 ipmi_response_t response,
@@ -390,6 +420,81 @@
     return IPMI_CC_OK;
 }
 
+ipmi_ret_t ipmiGetChannelPayloadSupport(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+                                        ipmi_request_t request,
+                                        ipmi_response_t response,
+                                        ipmi_data_len_t data_len,
+                                        ipmi_context_t context)
+{
+    const auto req = static_cast<GetChannelPayloadSupportReq*>(request);
+    size_t reqLength = *data_len;
+
+    *data_len = 0;
+
+    if (reqLength != sizeof(*req))
+    {
+        log<level::DEBUG>("Get channel payload - Invalid Length");
+        return IPMI_CC_REQ_DATA_LEN_INVALID;
+    }
+
+    uint8_t chNum = convertCurrentChannelNum(req->chNum);
+    if (!isValidChannel(chNum) || req->reserved != 0)
+    {
+        log<level::DEBUG>("Get channel payload - Invalid field in request");
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    // Not supported on sessionless channels.
+    if (EChannelSessSupported::none == getChannelSessionSupport(chNum))
+    {
+        log<level::DEBUG>("Get channel payload - Sessionless Channel");
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    // Session support is available in active LAN channels.
+    if ((EChannelSessSupported::none != getChannelSessionSupport(chNum)) &&
+        (!(doesDeviceExist(chNum))))
+    {
+        log<level::DEBUG>("Get channel payload - Device not exist");
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    auto resp = static_cast<GetChannelPayloadSupportResp*>(response);
+
+    std::fill(reinterpret_cast<uint8_t*>(resp),
+              reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0);
+
+    // TODO: Hard coding for now.
+    // Mapping PayloadTypes to 'GetChannelPayloadSupportResp' fields:
+    // --------------------------------------------------------------
+    // Mask all except least 3 significant bits to get a value in the range of
+    // 0-7. This value maps to the bit position of given payload type in 'resp'
+    // fields.
+
+    static constexpr uint8_t payloadByteMask = 0x07;
+    static constexpr uint8_t stdPayloadTypeIPMI =
+        1 << (static_cast<uint8_t>(PayloadType::IPMI) & payloadByteMask);
+    static constexpr uint8_t stdPayloadTypeSOL =
+        1 << (static_cast<uint8_t>(PayloadType::SOL) & payloadByteMask);
+
+    static constexpr uint8_t sessPayloadTypeOpenReq =
+        1 << (static_cast<uint8_t>(PayloadType::OPEN_SESSION_REQUEST) &
+              payloadByteMask);
+    static constexpr uint8_t sessPayloadTypeRAKP1 =
+        1 << (static_cast<uint8_t>(PayloadType::RAKP1) & payloadByteMask);
+    static constexpr uint8_t sessPayloadTypeRAKP3 =
+        1 << (static_cast<uint8_t>(PayloadType::RAKP3) & payloadByteMask);
+
+    resp->stdPayloadType[0] = stdPayloadTypeIPMI | stdPayloadTypeSOL;
+    // RMCP+ Open Session request, RAKP Message1 and RAKP Message3.
+    resp->sessSetupPayloadType[0] =
+        sessPayloadTypeOpenReq | sessPayloadTypeRAKP1 | sessPayloadTypeRAKP3;
+
+    *data_len = sizeof(*resp);
+
+    return IPMI_CC_OK;
+}
+
 void registerChannelFunctions() __attribute__((constructor));
 void registerChannelFunctions()
 {
@@ -403,6 +508,10 @@
 
     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_INFO, NULL,
                            ipmiGetChannelInfo, PRIVILEGE_USER);
+
+    ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_PAYLOAD_SUPPORT,
+                           NULL, ipmiGetChannelPayloadSupport, PRIVILEGE_USER);
+
     return;
 }
 
diff --git a/user_channel/channelcommands.hpp b/user_channel/channelcommands.hpp
index c612d3f..abcead6 100644
--- a/user_channel/channelcommands.hpp
+++ b/user_channel/channelcommands.hpp
@@ -28,6 +28,7 @@
     IPMI_CMD_SET_CHANNEL_ACCESS = 0x40,
     IPMI_CMD_GET_CHANNEL_ACCESS = 0x41,
     IPMI_CMD_GET_CHANNEL_INFO = 0x42,
+    IPMI_CMD_GET_CHANNEL_PAYLOAD_SUPPORT = 0x4E,
 };
 
 void registerChannelFunctions();