PLDMTOOL: Implement raw option in the pldmtool

raw option enables user sending raw bytes and printing response

HELP OUTPUT:
root@witherspoon-128:/tmp# ./pldmtool -h
PLDM requester tool for OpenBMC
Usage: ./pldmtool [OPTIONS] [—command...] [raw] [GetPLDMTypes] [GetPLDMVersion] [SUBCOMMAND]

Positionals:
  —command TEXT ...           PLDM request command
  raw TEXT                    Send a RAW PLDM request and print response
  GetPLDMTypes TEXT           Get PLDM Type
  GetPLDMVersion TEXT         Get PLDM Version

Options:
  -h,--help                   Print this help message and exit
  -c TEXT ...                   PLDM request command

Subcommands:
  BASE                        PLDM Command Type = BASE
  BIOS                        PLDM Command Type = BIOS
  OEM                         PLDM Command Type = OEM

Currently option to be given as raw instead of --raw because
of bug in the CLI11 :
FIX: required positional arguments and a vector positionals #306

pldm_set_state_effecter_states:
root@witherspoon-128:/tmp# ./pldmtool raw 0x80 0x02 0x39 0x00 0x01 0x00 0x01 0x09
Request Message
08 01 80 02 39 00 01 00 01 09
Success in creating the socket : RC = 3
Success in connecting to socket : RC = 0
Success in sending message type as pldm to mctp : RC = 0
Write to socket successful : RC = 10
Total length:10
Loopback response message:
08 01 80 02 39 00 01 00 01 09
On first recv(),response == request : RC = 0
Total length: 6
Shutdown Socket successful :  RC = 0
Socket recv() successful : RC = 0
Response Message:
08 01 00 02 39 00

GetPLDMVersion:
./pldmtool raw 0x80 0x00 0x03 0x00 0x00 0x00 0x00 0x01 0x00
Request Message
08 01 80 00 03 00 00 00 00 01 00
Success in creating the socket : RC = 3
Success in connecting to socket : RC = 0
Success in sending message type as pldm to mctp : RC = 0
Write to socket successful : RC = 11
Total length:11
Loopback response message:
08 01 80 00 03 00 00 00 00 01 00
On first recv(),response == request : RC = 0
Total length: 15
Shutdown Socket successful :  RC = 0
Socket recv() successful : RC = 0
Response Message:
08 01 00 00 03 00 00 00 00 00 05 f1 f0 f0 00

Signed-off-by: Lakshminarayana R. Kammath <lkammath@in.ibm.com>
Change-Id: I4057108444db8f8809f4310e1ffbb0565adbbf3c
diff --git a/tool/handler.hpp b/tool/handler.hpp
index 4ee7fe8..086c132 100644
--- a/tool/handler.hpp
+++ b/tool/handler.hpp
@@ -16,5 +16,7 @@
         {"GetPLDMTypes",
          [](Args&& args) { return getPLDMTypes(std::move(args)); }},
         {"GetPLDMVersion",
-         [](Args&& args) { return getPLDMVersion(std::move(args)); }}};
+         [](Args&& args) { return getPLDMVersion(std::move(args)); }},
+        {"HandleRawOp",
+         [](Args&& args) { return handleRawOp(std::move(args)); }}};
 };
diff --git a/tool/pldm_base_cmd.cpp b/tool/pldm_base_cmd.cpp
index 3ebd123..b203423 100644
--- a/tool/pldm_base_cmd.cpp
+++ b/tool/pldm_base_cmd.cpp
@@ -70,6 +70,7 @@
     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
                                     PLDM_GET_VERSION_REQ_BYTES);
     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
     uint8_t pldmType = 0x0;
 
     if (!args[1].c_str())
@@ -132,3 +133,112 @@
         return;
     }
 }
+
+/*
+ * Main function that handles the PLDM raw command response callback via mctp
+ *
+ */
+void handleRawOp(vector<std::string>&& args)
+{
+    // Minimu 3 bytes of header data needs to passed. else its a invalid request
+    if (size(args) < 3)
+    {
+        cerr << "Not enough arguments passed."
+                " Minimum, need to pass PLDM header raw data"
+             << endl;
+        return;
+    }
+
+    // Create a request packet and initialize it
+    std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr));
+    requestMsg.clear();
+
+    // Read the raw data passed in the command line
+    // Form request message from the raw data passed
+    for (auto&& rawByte : args)
+    {
+        requestMsg.insert(requestMsg.end(), stoi(rawByte, nullptr, 16));
+    }
+
+    // Validating the payload raw data based on pldm command type
+    uint8_t pldmCmd = requestMsg[2];
+    switch (pldmCmd)
+    {
+        case PLDM_GET_PLDM_VERSION:
+            if (size(requestMsg) !=
+                (sizeof(pldm_msg_hdr) + PLDM_GET_VERSION_REQ_BYTES))
+            {
+                cerr << "Not enough raw data provided." << endl;
+                cerr << "Total length can be = 3 bytes of header + 6 bytes"
+                        " for payload. Please refer spec for details"
+                     << endl;
+                return;
+            }
+            break;
+        case PLDM_GET_TID:
+        case PLDM_GET_PLDM_TYPES:
+            if (size(requestMsg) != sizeof(pldm_msg_hdr))
+            {
+                cerr << "Total length can be = 3 bytes of header + 0 bytes"
+                        " for payload. Please refer spec for details"
+                     << endl;
+                return;
+            }
+            break;
+        case PLDM_GET_PLDM_COMMANDS:
+            if (size(requestMsg) !=
+                sizeof(pldm_msg_hdr) + PLDM_GET_COMMANDS_REQ_BYTES)
+            {
+                cerr << "Total length can be = 3 bytes of header + 5 bytes"
+                        " for payload. Please refer spec for details"
+                     << endl;
+                return;
+            }
+            break;
+        case PLDM_SET_STATE_EFFECTER_STATES:
+            // payload size depends on comp_effecter_count
+            // if count=1 then, request size will be 8 bytes including header
+            // if count=9 then, request size will be 19 bytes including header
+            if ((size(requestMsg) < 8) ||
+                (size(requestMsg) >
+                 sizeof(pldm_msg_hdr) +
+                     PLDM_SET_STATE_EFFECTER_STATES_REQ_BYTES))
+            {
+                cerr << "Total length can be = 3 bytes of header + min/max 8/19"
+                        " bytes for payload. Please refer spec for details"
+                     << endl;
+                return;
+            }
+            break;
+        default:
+            cerr << "Command Not supported/implemented : " << args[2] << endl;
+            cerr << "Contact backend Team" << endl;
+            return;
+    }
+
+    // Add the MCTP type and PLDM entity id at the end
+    requestMsg.insert(requestMsg.begin(), MCTP_MSG_TYPE_PLDM);
+    requestMsg.insert(requestMsg.begin(), PLDM_ENTITY_ID);
+
+    cout << "Request Message" << endl;
+    printBuffer(requestMsg);
+
+    // Create the response message
+    vector<uint8_t> responseMsg;
+
+    // Compares the response with request packet on first socket recv() call.
+    // If above condition is qualified then, reads the actual response from
+    // the socket to output buffer responseMsg.
+    int returnCode = mctpSockSendRecv(requestMsg, responseMsg);
+    if (!returnCode)
+    {
+        cout << "Socket recv() successful : RC = " << returnCode << endl;
+        cout << "Response Message:" << endl;
+        printBuffer(responseMsg);
+    }
+    else
+    {
+        cerr << "Failed to recieve from socket : RC = " << returnCode << endl;
+        return;
+    }
+}
diff --git a/tool/pldm_base_cmd.hpp b/tool/pldm_base_cmd.hpp
index fefdcd1..d709ede 100644
--- a/tool/pldm_base_cmd.hpp
+++ b/tool/pldm_base_cmd.hpp
@@ -24,4 +24,14 @@
  */
 void getPLDMVersion(std::vector<std::string>&& args);
 
+/** @brief Handler for Raw PLDM commands
+ *
+ *
+ *  @param[in]  args - Argument to be passed to the handler
+ *              e.g :  PLDM raw commands.
+ *
+ *  @return - None
+ */
+void handleRawOp(std::vector<std::string>&& args);
+
 #endif /* PLDM_BASE_CMD_H */
diff --git a/tool/pldm_cmd_helper.hpp b/tool/pldm_cmd_helper.hpp
index 6a4f087..8032c9a 100644
--- a/tool/pldm_cmd_helper.hpp
+++ b/tool/pldm_cmd_helper.hpp
@@ -15,6 +15,7 @@
 #include <iostream>
 
 #include "libpldm/base.h"
+#include "libpldm/platform.h"
 
 using namespace pldm::responder::utils;
 
diff --git a/tool/pldmtool.cpp b/tool/pldmtool.cpp
index 6956845..c3e2186 100644
--- a/tool/pldmtool.cpp
+++ b/tool/pldmtool.cpp
@@ -11,24 +11,71 @@
     // bool verbose_flag = false;
     // app.add_flag("-v, --verbose", verbose_flag, "Output debug logs ");
     std::vector<std::string> args{};
-    app.add_option("-c, —command", args, "PLDM request command")->required();
+    app.add_option("-c, —command", args, "  PLDM request command");
+
+    std::string rawCmd;
+    app.add_option("raw", rawCmd, "Send a RAW PLDM request and print response");
 
     std::string pldmCmdName;
     app.add_option("GetPLDMTypes", pldmCmdName, "Get PLDM Type");
     app.add_option("GetPLDMVersion", pldmCmdName, "Get PLDM Version");
 
-    app.add_subcommand("base", "PLDM Command Type = base");
-    app.add_subcommand("bios", "PLDM Command Type = bios");
-    app.add_subcommand("oem", "PLDM Command Type = oem");
+    app.add_subcommand("BASE", "PLDM Command Type = BASE");
+    app.add_subcommand("BIOS", "PLDM Command Type = BIOS");
+    app.add_subcommand("OEM", "PLDM Command Type = OEM");
 
     CLI11_PARSE(app, argc, argv);
 
-    Handler handler;
-
-    // Parse args to program
-    std::string cmdName = args[0];
+    std::string cmdName;
     int rc = 0;
 
+    if (args[0] != "raw")
+    {
+        // Parse args to program
+        cmdName = args[0];
+    }
+    else
+    {
+        // removing 'raw' from the args list since it is positional argument
+        // and not an input value.
+        args.erase(args.begin());
+
+        // loop through the remaining argument list
+        for (auto&& item : args)
+        {
+
+            if (item[0] == '0' && (item[1] == 'x' || item[1] == 'X'))
+            {
+
+                // Erase 0x from input
+                item.erase(0, 2);
+
+                // Check for hex input value validity
+                if (std::all_of(item.begin(), item.end(), ::isxdigit))
+                {
+
+                    // Parse args to program
+                    cmdName = "HandleRawOp";
+                }
+                else
+                {
+                    std::cerr << item << " contains non hex digits. Re-enter"
+                              << std::endl;
+                    rc = -1;
+                    return rc;
+                }
+            }
+            else
+            {
+                std::cout << item << " Input hex value starting with 0x "
+                          << std::endl;
+                rc = -1;
+                return rc;
+            }
+        }
+    }
+
+    Handler handler;
     try
     {
         handler.dispatcher.at(cmdName)(std::move(args));
@@ -38,5 +85,5 @@
         std::cerr << cmdName << " is not supported!" << std::endl;
         rc = -1;
     }
-    return (rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+    return rc;
 }