dcmi: Implement set power limit command

Change-Id: I6427a0715a4bba5e18a5e483309a23f8493ea370
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/dcmihandler.cpp b/dcmihandler.cpp
index 2e0204d..11a3730 100644
--- a/dcmihandler.cpp
+++ b/dcmihandler.cpp
@@ -74,6 +74,27 @@
     return pcapEnabled.get<bool>();
 }
 
+void setPcap(sdbusplus::bus::bus& bus, const uint32_t powerCap)
+{
+    auto service = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH);
+
+    auto method = bus.new_method_call(service.c_str(),
+                                      PCAP_PATH,
+                                      "org.freedesktop.DBus.Properties",
+                                      "Set");
+
+    method.append(PCAP_INTERFACE, POWER_CAP_PROP);
+    method.append(sdbusplus::message::variant<uint32_t>(powerCap));
+
+    auto reply = bus.call(method);
+
+    if (reply.is_method_error())
+    {
+        log<level::ERR>("Error in setPcap property");
+        elog<InternalFailure>();
+    }
+}
+
 void readAssetTagObjectTree(dcmi::assettag::ObjectTree& objectTree)
 {
     static constexpr auto mapperBusName = "xyz.openbmc_project.ObjectMapper";
@@ -228,6 +249,45 @@
     }
 }
 
+ipmi_ret_t setPowerLimit(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)
+{
+    auto requestData = reinterpret_cast<const dcmi::SetPowerLimitRequest*>
+                   (request);
+    std::vector<uint8_t> outPayload(sizeof(dcmi::SetPowerLimitResponse));
+    auto responseData = reinterpret_cast<dcmi::SetPowerLimitResponse*>
+            (outPayload.data());
+
+    if (requestData->groupID != dcmi::groupExtId)
+    {
+        *data_len = 0;
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    sdbusplus::bus::bus sdbus {ipmid_get_sd_bus_connection()};
+
+    // Only process the power limit requested in watts.
+    try
+    {
+        dcmi::setPcap(sdbus, requestData->powerLimit);
+    }
+    catch (InternalFailure& e)
+    {
+        *data_len = 0;
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+
+    log<level::INFO>("Set Power Cap",
+                     entry("POWERCAP=%u", requestData->powerLimit));
+
+    responseData->groupID = dcmi::groupExtId;
+    memcpy(response, outPayload.data(), outPayload.size());
+    *data_len = outPayload.size();
+
+    return IPMI_CC_OK;
+}
+
 ipmi_ret_t getAssetTag(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)
@@ -370,6 +430,11 @@
     ipmi_register_callback(NETFUN_GRPEXT, IPMI_CMD_DCMI_GET_POWER_LIMIT, NULL, getPowerLimit,
                            PRIVILEGE_USER);
 
+    // <Set Power Limit>
+    printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_GRPEXT, IPMI_CMD_DCMI_SET_POWER_LIMIT);
+    ipmi_register_callback(NETFUN_GRPEXT, IPMI_CMD_DCMI_SET_POWER_LIMIT, NULL, setPowerLimit,
+                           PRIVILEGE_OPERATOR);
+
     // <Get Asset Tag>
     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_GRPEXT, IPMI_CMD_DCMI_GET_ASSET_TAG);
     ipmi_register_callback(NETFUN_GRPEXT, IPMI_CMD_DCMI_GET_ASSET_TAG, NULL, getAssetTag,
diff --git a/dcmihandler.hpp b/dcmihandler.hpp
index 8ebc660..e318294 100644
--- a/dcmihandler.hpp
+++ b/dcmihandler.hpp
@@ -11,6 +11,7 @@
 {
     // Get capability bits
     IPMI_CMD_DCMI_GET_POWER_LIMIT = 0x03,
+    IPMI_CMD_DCMI_SET_POWER_LIMIT = 0x04,
     IPMI_CMD_DCMI_GET_ASSET_TAG = 0x06,
     IPMI_CMD_DCMI_SET_ASSET_TAG = 0x08,
 };
@@ -145,6 +146,38 @@
     uint16_t samplingPeriod;    //!< Statistics sampling period in seconds.
 } __attribute__((packed));
 
+/** @brief Set the power cap value
+ *
+ *  @param[in] bus - dbus connection
+ *  @param[in] powerCap - power cap value
+ */
+void setPcap(sdbusplus::bus::bus& bus, const uint32_t powerCap);
+
+/** @struct SetPowerLimitRequest
+ *
+ *  DCMI payload for Set Power Limit command request.
+ */
+struct SetPowerLimitRequest
+{
+    uint8_t groupID;            //!< Group extension identification.
+    uint16_t reserved;          //!< Reserved
+    uint8_t reserved1;          //!< Reserved
+    uint8_t exceptionAction;    //!< Exception action.
+    uint16_t powerLimit;        //!< Power limit requested in watts.
+    uint32_t correctionTime;    //!< Correction time limit in milliseconds.
+    uint16_t reserved2;         //!< Reserved.
+    uint16_t samplingPeriod;    //!< Statistics sampling period in seconds.
+} __attribute__((packed));
+
+/** @struct SetPowerLimitResponse
+ *
+ *  DCMI payload for Set Power Limit command response.
+ */
+struct SetPowerLimitResponse
+{
+    uint8_t groupID;            //!< Group extension identification.
+} __attribute__((packed));
+
 } // namespace dcmi
 
 #endif