oem-ibm: Implement Host lamp test interface

IBM has a feature called LampTest and what it does is this:
  - BMC would set the state of LEDs to [ON] on all the BMC accessible
    LEDs.
  - For LEDs that are not accessible by BMC, a message is sent to the
    Host, so that the Host can exercise all the LEDs on the connected
    drawers and anything else that the BMC does not have access to.

This commit adds support to send message to Host when BMC has to tell
the Host to exercise LEDs accessible by host.

When Host sees the effector is set, it goes ahead and turns [ON] all
the LEDs for a specified time and automatically restores the states
at timer expiration. BMC does not have to tell the Host to stop the
test and it is part of Host's lamp test requirement.

TESTED: By setting the asserted property of the LED groups object

busctl set-property xyz.openbmc_project.LED.GroupManager
/xyz/openbmc_project/led/groups/lamp_test
xyz.openbmc_project.Led.Group Asserted b true

And verifying that the setEffecterStates call was made to the
remote state effecter to turn on all LEDs

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I990dce28d3017f20c73ff9029fced0e7ac84868f
diff --git a/oem/ibm/host-bmc/host_lamp_test.cpp b/oem/ibm/host-bmc/host_lamp_test.cpp
new file mode 100644
index 0000000..57f9610
--- /dev/null
+++ b/oem/ibm/host-bmc/host_lamp_test.cpp
@@ -0,0 +1,164 @@
+
+#include "host_lamp_test.hpp"
+
+#include "common/types.hpp"
+#include "common/utils.hpp"
+
+#include <libpldm/entity.h>
+#include <libpldm/platform.h>
+#include <libpldm/state_set.h>
+
+PHOSPHOR_LOG2_USING;
+
+namespace pldm
+{
+namespace led
+{
+
+bool HostLampTest::asserted() const
+{
+    return sdbusplus::xyz::openbmc_project::Led::server::Group::asserted();
+}
+
+bool HostLampTest::asserted(bool value)
+{
+    // When setting the asserted property to true, need to notify the PHYP to
+    // start lamp test.
+    // When setting the asserted property to true again, need to notify the PHYP
+    // and PHYP will restart the lamp test.
+    // When setting the asserted property to false, just update the asserted
+    // property, do not need to notify the PHYP to stop lamp test, PHYP will
+    // wait for timeout to stop.
+    if (!value &&
+        value ==
+            sdbusplus::xyz::openbmc_project::Led::server::Group::asserted())
+    {
+        return value;
+    }
+
+    if (value)
+    {
+        if (effecterID == 0)
+        {
+            effecterID = getEffecterID();
+        }
+
+        if (effecterID != 0)
+        {
+            // Call setHostStateEffecter method to notify PHYP to start lamp
+            // test. If user set Asserted to true again, need to notify PHYP to
+            // start lamptest again.
+            uint8_t rc = setHostStateEffecter(effecterID);
+            if (rc)
+            {
+                throw sdbusplus::exception::SdBusError(
+                    static_cast<int>(std::errc::invalid_argument),
+                    "Set Host State Effector to start lamp test failed");
+            }
+            else
+            {
+                return sdbusplus::xyz::openbmc_project::Led::server::Group::
+                    asserted(value);
+            }
+        }
+        else
+        {
+            throw sdbusplus::exception::SdBusError(
+                static_cast<int>(std::errc::invalid_argument),
+                "Get Effecter ID failed, effecter = 0");
+        }
+    }
+
+    return sdbusplus::xyz::openbmc_project::Led::server::Group::asserted(value);
+}
+
+uint16_t HostLampTest::getEffecterID()
+{
+    uint16_t effecterID = 0;
+
+    if (!pdrRepo)
+    {
+        return effecterID;
+    }
+
+    // INDICATOR is a logical entity, so the bit 15 in entity type is set.
+    pdr::EntityType entityType = PLDM_ENTITY_INDICATOR | 0x8000;
+
+    auto stateEffecterPDRs = pldm::utils::findStateEffecterPDR(
+        mctp_eid, entityType,
+        static_cast<uint16_t>(PLDM_STATE_SET_IDENTIFY_STATE), pdrRepo);
+
+    if (stateEffecterPDRs.empty())
+    {
+        error(
+            "Lamp Test: The state set PDR can not be found, entityType = {ENTITY_TYP}",
+            "ENTITY_TYP", entityType);
+        return effecterID;
+    }
+
+    for (const auto& rep : stateEffecterPDRs)
+    {
+        auto pdr = reinterpret_cast<const pldm_state_effecter_pdr*>(rep.data());
+        effecterID = pdr->effecter_id;
+        break;
+    }
+
+    return effecterID;
+}
+
+uint8_t HostLampTest::setHostStateEffecter(uint16_t effecterID)
+{
+    constexpr uint8_t effecterCount = 1;
+    auto instanceId = instanceIdDb.next(mctp_eid);
+
+    std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + sizeof(effecterID) +
+                                    sizeof(effecterCount) +
+                                    sizeof(set_effecter_state_field));
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    set_effecter_state_field stateField{PLDM_REQUEST_SET,
+                                        PLDM_STATE_SET_IDENTIFY_STATE_ASSERTED};
+    auto rc = encode_set_state_effecter_states_req(
+        instanceId, effecterID, effecterCount, &stateField, request);
+    if (rc != PLDM_SUCCESS)
+    {
+        instanceIdDb.free(mctp_eid, instanceId);
+        error("Failed to encode_set_state_effecter_states_req, rc = {RC}", "RC",
+              static_cast<int>(rc));
+        return rc;
+    }
+
+    auto setStateEffecterStatesResponseHandler =
+        [](mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen) {
+        if (!response || !respMsgLen)
+        {
+            error(
+                "Failed to receive response for the Set State Effecter States");
+            return;
+        }
+
+        uint8_t completionCode{};
+        auto rc = decode_set_state_effecter_states_resp(response, respMsgLen,
+                                                        &completionCode);
+
+        if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
+        {
+            error(
+                "Failed to decode_set_state_effecter_states_resp: rc={RC}, cc={CC}",
+                "RC", rc, "CC", static_cast<unsigned>(completionCode));
+        }
+    };
+
+    rc = handler->registerRequest(
+        mctp_eid, instanceId, PLDM_PLATFORM, PLDM_SET_STATE_EFFECTER_STATES,
+        std::move(requestMsg),
+        std::move(setStateEffecterStatesResponseHandler));
+    if (rc != PLDM_SUCCESS)
+    {
+        error("Failed to send the the Set State Effecter States request");
+    }
+
+    return rc;
+}
+
+} // namespace led
+} // namespace pldm