IPMI COMMAND: Get LED status

LED status includes system identify/amber/green status.
They are handled by led.controller and defined by Physical.interface.yaml

uint8_t status; //LED Status
                //[1:0] = Reserved
                //[3:2] = Status(Amber)
                //[5:4] = Status(Green)
                //[7:6] = System Identify
                //Status definitions:
                //00b = Off
                //01b = Blink
                //10b = On
                //11b = invalid
TestBy:  ipmitool raw 0x30 0xb0

Change-Id: Ifce7c037f332cbe3fafd19e4e0acd9da6e06d12b
Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
diff --git a/include/oemcommands.hpp b/include/oemcommands.hpp
index 4270330..01dca7d 100644
--- a/include/oemcommands.hpp
+++ b/include/oemcommands.hpp
@@ -29,6 +29,7 @@
     cmdGetChassisIdentifier = 0x92,
     cmdGetProcessorErrConfig = 0x9A,
     cmdSetProcessorErrConfig = 0x9B,
+    cmdGetLEDStatus = 0xB0,
 };
 
 enum class IPMIIntelOEMReturnCodes
@@ -96,6 +97,14 @@
 static constexpr const char* postCodesIntf =
     "xyz.openbmc_project.State.Boot.PostCode";
 
+static constexpr const char* identifyLEDObjPath =
+    "/xyz/openbmc_project/led/physical/identify";
+static constexpr const char* ledIntf = "xyz.openbmc_project.Led.Physical";
+static constexpr const char* statusAmberObjPath =
+    "/xyz/openbmc_project/led/physical/status_amber";
+static constexpr const char* statusGreenObjPath =
+    "/xyz/openbmc_project/led/physical/status_green";
+
 static constexpr const uint8_t noShutdownOnOCOT = 0;
 static constexpr const uint8_t shutdownOnOCOT = 1;
 static constexpr const uint8_t noShutdownPolicySupported = 0;
@@ -232,4 +241,5 @@
     uint8_t policy;
     uint8_t policySupport;
 };
-#pragma pack(pop)
\ No newline at end of file
+
+#pragma pack(pop)
diff --git a/src/oemcommands.cpp b/src/oemcommands.cpp
index 0806317..724d8e5 100644
--- a/src/oemcommands.cpp
+++ b/src/oemcommands.cpp
@@ -15,6 +15,7 @@
 */
 
 #include "xyz/openbmc_project/Common/error.hpp"
+#include "xyz/openbmc_project/Led/Physical/server.hpp"
 
 #include <ipmid/api.h>
 
@@ -422,7 +423,7 @@
     if (*dataLen != 0)
     {
         phosphor::logging::log<phosphor::logging::level::ERR>(
-            "oem_set_shutdown_policy: invalid input len!");
+            "oem_get_shutdown_policy: invalid input len!");
         *dataLen = 0;
         return IPMI_CC_REQ_DATA_LEN_INVALID;
     }
@@ -494,6 +495,84 @@
     return IPMI_CC_OK;
 }
 
+namespace ledAction
+{
+using namespace sdbusplus::xyz::openbmc_project::Led::server;
+std::map<Physical::Action, uint8_t> actionDbusToIpmi = {
+    {Physical::Action::Off, 0x00},
+    {Physical::Action::On, 0x10},
+    {Physical::Action::Blink, 0x01}};
+
+std::map<uint8_t, std::string> offsetObjPath = {
+    {2, statusAmberObjPath}, {4, statusGreenObjPath}, {6, identifyLEDObjPath}};
+
+} // namespace ledAction
+
+int8_t getLEDState(sdbusplus::bus::bus& bus, const std::string& intf,
+                   const std::string& objPath, uint8_t& state)
+{
+    try
+    {
+        std::string service = getService(bus, intf, objPath);
+        Value stateValue =
+            getDbusProperty(bus, service, objPath, intf, "State");
+        std::string strState =
+            sdbusplus::message::variant_ns::get<std::string>(stateValue);
+        state = ledAction::actionDbusToIpmi.at(
+            sdbusplus::xyz::openbmc_project::Led::server::Physical::
+                convertActionFromString(strState));
+    }
+    catch (sdbusplus::exception::SdBusError& e)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
+        return -1;
+    }
+    return 0;
+}
+
+ipmi_ret_t ipmiOEMGetLEDStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+                               ipmi_request_t request, ipmi_response_t response,
+                               ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+    uint8_t* resp = reinterpret_cast<uint8_t*>(response);
+    // LED Status
+    //[1:0] = Reserved
+    //[3:2] = Status(Amber)
+    //[5:4] = Status(Green)
+    //[7:6] = System Identify
+    // Status definitions:
+    // 00b = Off
+    // 01b = Blink
+    // 10b = On
+    // 11b = invalid
+    if (*dataLen != 0)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "oem_get_led_status: invalid input len!");
+        *dataLen = 0;
+        return IPMI_CC_REQ_DATA_LEN_INVALID;
+    }
+
+    phosphor::logging::log<phosphor::logging::level::DEBUG>("GET led status");
+    *resp = 0;
+    *dataLen = 0;
+    for (auto it = ledAction::offsetObjPath.begin();
+         it != ledAction::offsetObjPath.end(); ++it)
+    {
+        uint8_t state = 0;
+        if (-1 == getLEDState(dbus, ledIntf, it->second, state))
+        {
+            phosphor::logging::log<phosphor::logging::level::ERR>(
+                "oem_get_led_status: fail to get ID LED status!");
+            return IPMI_CC_UNSPECIFIED_ERROR;
+        }
+        *resp |= state << it->first;
+    }
+
+    *dataLen = sizeof(*resp);
+    return IPMI_CC_OK;
+}
+
 static void registerOEMFunctions(void)
 {
     phosphor::logging::log<phosphor::logging::level::INFO>(
@@ -556,6 +635,10 @@
                          static_cast<ipmi_cmd_t>(
                              IPMINetfnIntelOEMGeneralCmd::cmdGetShutdownPolicy),
                          NULL, ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
+    ipmiPrintAndRegister(
+        netfnIntcOEMGeneral,
+        static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetLEDStatus),
+        NULL, ipmiOEMGetLEDStatus, PRIVILEGE_ADMIN);
     return;
 }