pldmtool: Add GetStatus firmware update command

Tested:

pldmtool fw_update --help
firmware update type commands
Usage: ./pldmtool fw_update [OPTIONS] SUBCOMMAND
Options:
  -h,--help                   Print this help message and exit

Subcommands:
  GetStatus                   Status of the FD

pldmtool fw_update GetStatus -m <eid>
{     "CurrentState": "IDLE",
      "PreviousState": "IDLE",
      "AuxState": "Not applicable in current state",
      "AuxStateStatus": "AuxState is In Progress or Success",
      "ProgressPercent": 101,
      "ReasonCode": "Initialization of firmware device has occurred",
      "UpdateOptionFlagsEnabled": 0
}

Signed-off-by: Tom Joseph <rushtotom@gmail.com>
Change-Id: I2eb41996ab0dcb1d00ac5b8b0e55846b0d4b28a5
diff --git a/pldmtool/meson.build b/pldmtool/meson.build
index 865e9f0..765bdba 100644
--- a/pldmtool/meson.build
+++ b/pldmtool/meson.build
@@ -6,6 +6,7 @@
   'pldm_platform_cmd.cpp',
   'pldm_bios_cmd.cpp',
   'pldm_fru_cmd.cpp',
+  'pldm_fw_update_cmd.cpp',
   'pldmtool.cpp',
 ]
 
diff --git a/pldmtool/pldm_fw_update_cmd.cpp b/pldmtool/pldm_fw_update_cmd.cpp
new file mode 100644
index 0000000..857c2e6
--- /dev/null
+++ b/pldmtool/pldm_fw_update_cmd.cpp
@@ -0,0 +1,142 @@
+#include "pldm_fw_update_cmd.hpp"
+
+#include "libpldm/firmware_update.h"
+
+#include "common/utils.hpp"
+#include "pldm_cmd_helper.hpp"
+
+namespace pldmtool
+{
+
+namespace fw_update
+{
+
+namespace
+{
+
+using namespace pldmtool::helper;
+
+std::vector<std::unique_ptr<CommandInterface>> commands;
+
+} // namespace
+
+const std::map<uint8_t, std::string> fdStateMachine{
+    {PLDM_FD_STATE_IDLE, "IDLE"},
+    {PLDM_FD_STATE_LEARN_COMPONENTS, "LEARN COMPONENTS"},
+    {PLDM_FD_STATE_READY_XFER, "READY XFER"},
+    {PLDM_FD_STATE_DOWNLOAD, "DOWNLOAD"},
+    {PLDM_FD_STATE_VERIFY, "VERIFY"},
+    {PLDM_FD_STATE_APPLY, "APPLY"},
+    {PLDM_FD_STATE_ACTIVATE, "ACTIVATE"}};
+
+const std::map<uint8_t, const char*> fdAuxState{
+    {PLDM_FD_OPERATION_IN_PROGRESS, "Operation in progress"},
+    {PLDM_FD_OPERATION_SUCCESSFUL, "Operation successful"},
+    {PLDM_FD_OPERATION_FAILED, "Operation Failed"},
+    {PLDM_FD_IDLE_LEARN_COMPONENTS_READ_XFER,
+     "Not applicable in current state"}};
+
+const std::map<uint8_t, const char*> fdAuxStateStatus{
+    {PLDM_FD_AUX_STATE_IN_PROGRESS_OR_SUCCESS,
+     "AuxState is In Progress or Success"},
+    {PLDM_FD_TIMEOUT, "Timeout occurred while performing action"},
+    {PLDM_FD_GENERIC_ERROR, "Generic Error has occured"}};
+
+const std::map<uint8_t, const char*> fdReasonCode{
+    {PLDM_FD_INITIALIZATION, "Initialization of firmware device has occurred"},
+    {PLDM_FD_ACTIVATE_FW, "ActivateFirmware command was received"},
+    {PLDM_FD_CANCEL_UPDATE, "CancelUpdate command was received"},
+    {PLDM_FD_TIMEOUT_LEARN_COMPONENT,
+     "Timeout occurred when in LEARN COMPONENT state"},
+    {PLDM_FD_TIMEOUT_READY_XFER, "Timeout occurred when in READY XFER state"},
+    {PLDM_FD_TIMEOUT_DOWNLOAD, "Timeout occurred when in DOWNLOAD state"},
+    {PLDM_FD_TIMEOUT_VERIFY, "Timeout occurred when in VERIFY state"},
+    {PLDM_FD_TIMEOUT_APPLY, "Timeout occurred when in APPLY state"}};
+
+class GetStatus : public CommandInterface
+{
+  public:
+    ~GetStatus() = default;
+    GetStatus() = delete;
+    GetStatus(const GetStatus&) = delete;
+    GetStatus(GetStatus&&) = default;
+    GetStatus& operator=(const GetStatus&) = delete;
+    GetStatus& operator=(GetStatus&&) = default;
+
+    using CommandInterface::CommandInterface;
+
+    std::pair<int, std::vector<uint8_t>> createRequestMsg() override
+    {
+        std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
+                                        PLDM_GET_STATUS_REQ_BYTES);
+        auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+        auto rc = encode_get_status_req(instanceId, request,
+                                        PLDM_GET_STATUS_REQ_BYTES);
+        return {rc, requestMsg};
+    }
+
+    void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
+    {
+        uint8_t completionCode = 0;
+        uint8_t currentState = 0;
+        uint8_t previousState = 0;
+        uint8_t auxState = 0;
+        uint8_t auxStateStatus = 0;
+        uint8_t progressPercent = 0;
+        uint8_t reasonCode = 0;
+        bitfield32_t updateOptionFlagsEnabled{0};
+
+        auto rc = decode_get_status_resp(
+            responsePtr, payloadLength, &completionCode, &currentState,
+            &previousState, &auxState, &auxStateStatus, &progressPercent,
+            &reasonCode, &updateOptionFlagsEnabled);
+        if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
+        {
+            std::cerr << "Response Message Error: "
+                      << "rc=" << rc << ",cc=" << (int)completionCode << "\n";
+            return;
+        }
+
+        ordered_json data;
+        data["CurrentState"] = fdStateMachine.at(currentState);
+        data["PreviousState"] = fdStateMachine.at(previousState);
+        data["AuxState"] = fdAuxState.at(auxState);
+        if (auxStateStatus >= PLDM_FD_VENDOR_DEFINED_STATUS_CODE_START &&
+            auxStateStatus <= PLDM_FD_VENDOR_DEFINED_STATUS_CODE_END)
+        {
+            data["AuxStateStatus"] = auxStateStatus;
+        }
+        else
+        {
+            data["AuxStateStatus"] = fdAuxStateStatus.at(auxStateStatus);
+        }
+        data["ProgressPercent"] = progressPercent;
+        if (reasonCode >= PLDM_FD_STATUS_VENDOR_DEFINED_MIN &&
+            reasonCode <= PLDM_FD_STATUS_VENDOR_DEFINED_MAX)
+        {
+            data["ReasonCode"] = reasonCode;
+        }
+        else
+        {
+            data["ReasonCode"] = fdReasonCode.at(reasonCode);
+        }
+        data["UpdateOptionFlagsEnabled"] = updateOptionFlagsEnabled.value;
+
+        pldmtool::helper::DisplayInJson(data);
+    }
+};
+
+void registerCommand(CLI::App& app)
+{
+    auto fwUpdate =
+        app.add_subcommand("fw_update", "firmware update type commands");
+    fwUpdate->require_subcommand(1);
+
+    auto getStatus = fwUpdate->add_subcommand("GetStatus", "Status of the FD");
+    commands.push_back(
+        std::make_unique<GetStatus>("fw_update", "GetStatus", getStatus));
+}
+
+} // namespace fw_update
+
+} // namespace pldmtool
\ No newline at end of file
diff --git a/pldmtool/pldm_fw_update_cmd.hpp b/pldmtool/pldm_fw_update_cmd.hpp
new file mode 100644
index 0000000..a1e2e5d
--- /dev/null
+++ b/pldmtool/pldm_fw_update_cmd.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <CLI/CLI.hpp>
+
+namespace pldmtool
+{
+
+namespace fw_update
+{
+
+void registerCommand(CLI::App& app);
+
+} // namespace fw_update
+
+} // namespace pldmtool
diff --git a/pldmtool/pldmtool.cpp b/pldmtool/pldmtool.cpp
index f52c1e8..45cd9f8 100644
--- a/pldmtool/pldmtool.cpp
+++ b/pldmtool/pldmtool.cpp
@@ -2,6 +2,7 @@
 #include "pldm_bios_cmd.hpp"
 #include "pldm_cmd_helper.hpp"
 #include "pldm_fru_cmd.hpp"
+#include "pldm_fw_update_cmd.hpp"
 #include "pldm_platform_cmd.hpp"
 #include "pldmtool/oem/ibm/pldm_oem_ibm.hpp"
 
@@ -72,6 +73,7 @@
     pldmtool::bios::registerCommand(app);
     pldmtool::platform::registerCommand(app);
     pldmtool::fru::registerCommand(app);
+    pldmtool::fw_update::registerCommand(app);
 
 #ifdef OEM_IBM
     pldmtool::oem_ibm::registerCommand(app);