DCMI: Read power value from the configured D-Bus object path

1) Parse the power_reading JSON configuration file to get
   the D-Bus object path for the power reading.
2) Read the power reading value from the specified D-Bus object.

Change-Id: I4b9f0703318e8db2c86d0b337e524eee6da33d08
Signed-off-by: Marri Devender Rao <devenrao@in.ibm.com>
diff --git a/configure.ac b/configure.ac
index 0e124a3..ac42e1d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -152,6 +152,11 @@
       [CONTROL_HOST_OBJ_MGR="/xyz/openbmc_project/control"])
 AC_DEFINE_UNQUOTED([CONTROL_HOST_OBJ_MGR], ["$CONTROL_HOST_OBJ_MGR"], [The Control Host D-Bus Object Manager])
 
+# Power reading sensor configuration file
+AC_ARG_VAR(POWER_READING_SENSOR, [Power reading sensor configuration file])
+AS_IF([test "x$POWER_READING_SENSOR" == "x"],[POWER_READING_SENSOR="/usr/share/ipmi-providers/power_reading.json"])
+AC_DEFINE_UNQUOTED([POWER_READING_SENSOR], ["$POWER_READING_SENSOR"], [Power reading sensor configuration file])
+
 # Create configured output
 AC_CONFIG_FILES([Makefile test/Makefile softoff/Makefile softoff/test/Makefile])
 AC_OUTPUT
diff --git a/dcmihandler.cpp b/dcmihandler.cpp
index 8b47845..3c77ed5 100644
--- a/dcmihandler.cpp
+++ b/dcmihandler.cpp
@@ -3,6 +3,7 @@
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/log.hpp>
 #include <sdbusplus/bus.hpp>
+#include <nlohmann/json.hpp>
 #include "utils.hpp"
 #include <stdio.h>
 #include <string.h>
@@ -11,6 +12,7 @@
 #include <bitset>
 #include <cmath>
 #include "xyz/openbmc_project/Common/error.hpp"
+#include "config.h"
 
 using namespace phosphor::logging;
 using InternalFailure =
@@ -29,6 +31,10 @@
 constexpr auto DCMI_SPEC_MINOR_VERSION = 5;
 constexpr auto DCMI_CAP_JSON_FILE = "/usr/share/ipmi-providers/dcmi_cap.json";
 
+constexpr auto SENSOR_VALUE_INTF = "xyz.openbmc_project.Sensor.Value";
+constexpr auto SENSOR_VALUE_PROP = "Value";
+constexpr auto SENSOR_SCALE_PROP = "Scale";
+
 using namespace phosphor::logging;
 
 namespace dcmi
@@ -1004,6 +1010,68 @@
     return IPMI_CC_OK;
 }
 
+int64_t getPowerReading(sdbusplus::bus::bus& bus)
+{
+    std::ifstream sensorFile(POWER_READING_SENSOR);
+    std::string objectPath;
+    if (!sensorFile.is_open())
+    {
+        log<level::ERR>("Power reading configuration file not found",
+                    entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
+        elog<InternalFailure>();
+    }
+
+    auto data = nlohmann::json::parse(sensorFile, nullptr, false);
+    if (data.is_discarded())
+    {
+        log<level::ERR>("Error in parsing configuration file",
+                    entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
+        elog<InternalFailure>();
+    }
+
+    objectPath = data.value("path", "");
+    if (objectPath.empty())
+    {
+        log<level::ERR>("Power sensor D-Bus object path is empty",
+                        entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
+        elog<InternalFailure>();
+    }
+
+    auto service = ipmi::getService(bus, SENSOR_VALUE_INTF, objectPath);
+
+    //Read the sensor value and scale properties
+    auto properties = ipmi::getAllDbusProperties(
+                            bus, service, objectPath, SENSOR_VALUE_INTF);
+    auto power = properties[SENSOR_VALUE_PROP].get<int64_t>();
+    auto scale = properties[SENSOR_SCALE_PROP].get<int64_t>();
+
+    // Power reading needs to be scaled with the Scale value using the formula
+    // Value * 10^Scale.
+    power *= std::pow(10, scale);
+
+    return power;
+}
+
+ipmi_ret_t getPowerReading(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)
+{
+    ipmi_ret_t rc = IPMI_CC_OK;
+    sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+    try
+    {
+        getPowerReading(bus);
+    }
+    catch (InternalFailure& e)
+    {
+        log<level::ERR>("Error in reading power sensor value",
+                        entry("INTERFACE=%s", SENSOR_VALUE_INTF),
+                        entry("PROPERTY=%s", SENSOR_VALUE_PROP));
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+    return rc;
+}
+
 void register_netfn_dcmi_functions()
 {
     // <Get Power Limit>
@@ -1062,6 +1130,9 @@
     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_TEMP_READINGS,
                            NULL, getTempReadings, PRIVILEGE_USER);
 
+    // <Get Power Reading>
+    ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_READING,
+                           NULL, getPowerReading, PRIVILEGE_USER);
     return;
 }
 // 956379
diff --git a/dcmihandler.hpp b/dcmihandler.hpp
index bd2c6e1..4e95eba 100644
--- a/dcmihandler.hpp
+++ b/dcmihandler.hpp
@@ -14,6 +14,7 @@
 {
     // Get capability bits
     GET_CAPABILITIES = 0x01,
+    GET_POWER_READING = 0x02,
     GET_POWER_LIMIT = 0x03,
     SET_POWER_LIMIT = 0x04,
     APPLY_POWER_LIMIT = 0x05,
@@ -427,6 +428,47 @@
                                                    uint8_t instanceStart);
 }
 
+/** @brief Read power reading from power reading sensor object
+ *
+ *  @param[in] bus - dbus connection
+ *
+ *  @return total power reading
+ */
+int64_t getPowerReading(sdbusplus::bus::bus& bus);
+
+/** @struct GetPowerReadingRequest
+ *
+ *  DCMI Get Power Reading command request.
+ *  Refer DCMI specification Version 1.1 Section 6.6.1
+ */
+struct GetPowerReadingRequest
+{
+    uint8_t groupID;        //!< Group extension identification.
+    uint8_t mode;           //!< Mode
+    uint8_t modeAttribute;  //!< Mode Attributes
+} __attribute__((packed));
+
+/** @struct GetPowerReadingResponse
+ *
+ *  DCMI Get Power Reading command response.
+ *  Refer DCMI specification Version 1.1 Section 6.6.1
+ */
+struct GetPowerReadingResponse
+{
+    uint8_t groupID;            //!< Group extension identification.
+    uint16_t currentPower;      //!< Current power in watts
+    uint16_t minimumPower;      //!< Minimum power over sampling duration
+                                //!< in watts
+    uint16_t maximumPower;      //!< Maximum power over sampling duration
+                                //!< in watts
+    uint16_t averagePower;      //!< Average power over sampling duration
+                                //!< in watts
+    uint32_t timeStamp;         //!< IPMI specification based time stamp
+    uint32_t timeFrame;         //!< Statistics reporting time period in milli
+                                //!< seconds.
+    uint8_t powerReadingState;  //!< Power Reading State
+} __attribute__((packed));
+
 } // namespace dcmi
 
 #endif