Modify set sensor reading command

The Set Sensor handling code is modified to use generated code
for CPU, Core & DIMM sensors with Presence & Functional
offset.

Change-Id: I3b7fa4da870b745873da4732d457d793f5549ada
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 2b93ae5..53c21da 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -34,7 +34,8 @@
 	transporthandler.cpp \
 	globalhandler.cpp \
 	groupext.cpp \
-	sensor-gen.cpp
+	sensor-gen.cpp \
+	utils.cpp
 
 libapphandler_la_LDFLAGS = $(SYSTEMD_LIBS) $(libmapper_LIBS) $(PHOSPHOR_LOGGING_LIBS) -version-info 0:0:0 -shared
 libapphandler_la_CXXFLAGS = $(SYSTEMD_CFLAGS) $(libmapper_CFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
diff --git a/sensorhandler.cpp b/sensorhandler.cpp
index a40196c..53bb412 100644
--- a/sensorhandler.cpp
+++ b/sensorhandler.cpp
@@ -1,14 +1,19 @@
-#include "sensorhandler.h"
-#include "host-ipmid/ipmid-api.h"
 #include <mapper.h>
 #include <stdio.h>
 #include <string.h>
-#include <stdint.h>
+#include <bitset>
 #include <systemd/sd-bus.h>
+#include "host-ipmid/ipmid-api.h"
+#include <phosphor-logging/log.hpp>
 #include "ipmid.hpp"
+#include "sensorhandler.h"
+#include "types.hpp"
+#include "utils.hpp"
 
 extern int updateSensorRecordFromSSRAESC(const void *);
 extern sd_bus *bus;
+extern const ipmi::sensor::IdInfoMap sensors;
+using namespace phosphor::logging;
 
 void register_netfn_sen_functions()   __attribute__((constructor));
 
@@ -343,22 +348,120 @@
     return rc;
 }
 
+ipmi_ret_t setSensorReading(void *request)
+{
+    auto cmdData = static_cast<SetSensorReadingReq *>(request);
 
+    auto assertionStates =
+            (static_cast<uint16_t>(cmdData->assertOffset8_14)) << 8 |
+            cmdData->assertOffset0_7;
+
+    auto deassertionStates =
+            (static_cast<uint16_t>(cmdData->deassertOffset8_14)) << 8 |
+            cmdData->deassertOffset0_7;
+
+    std::bitset<16> assertionSet(assertionStates);
+    std::bitset<16> deassertionSet(deassertionStates);
+
+    // Check if the Sensor Number is present
+    auto iter = sensors.find(cmdData->number);
+    if (iter == sensors.end())
+    {
+        return IPMI_CC_SENSOR_INVALID;
+    }
+
+    auto& interfaceList = iter->second.sensorInterfaces;
+    if (interfaceList.empty())
+    {
+        log<level::ERR>("Interface List empty for the sensor",
+                entry("Sensor Number = %d", cmdData->number));
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+
+    ipmi::sensor::ObjectMap objects;
+    ipmi::sensor::InterfaceMap interfaces;
+    for (const auto& interface : interfaceList)
+    {
+        for (const auto& property : interface.second)
+        {
+            ipmi::sensor::PropertyMap props;
+            bool valid = false;
+            for (const auto& value : property.second)
+            {
+                if (assertionSet.test(value.first))
+                {
+                    props.emplace(property.first, value.second.assert);
+                    valid = true;
+                }
+                else if (deassertionSet.test(value.first))
+                {
+                    props.emplace(property.first, value.second.deassert);
+                    valid = true;
+                }
+            }
+            if (valid)
+            {
+                interfaces.emplace(interface.first, std::move(props));
+            }
+        }
+    }
+    objects.emplace(iter->second.sensorPath, std::move(interfaces));
+
+    auto bus = sdbusplus::bus::new_default();
+    using namespace std::string_literals;
+    static const auto intf = "xyz.openbmc_project.Inventory.Manager"s;
+    static const auto path = "/xyz/openbmc_project/inventory"s;
+    std::string service;
+
+    try
+    {
+        service = ipmi::getService(bus, intf, path);
+
+        // Update the inventory manager
+        auto pimMsg = bus.new_method_call(service.c_str(),
+                                          path.c_str(),
+                                          intf.c_str(),
+                                          "Notify");
+        pimMsg.append(std::move(objects));
+        auto inventoryMgrResponseMsg = bus.call(pimMsg);
+        if (inventoryMgrResponseMsg.is_method_error())
+        {
+            log<level::ERR>("Error in notify call");
+            return IPMI_CC_UNSPECIFIED_ERROR;
+        }
+    }
+    catch (const std::runtime_error& e)
+    {
+        log<level::ERR>(e.what());
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+
+    return IPMI_CC_OK;
+}
 
 ipmi_ret_t ipmi_sen_set_sensor(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)
 {
     sensor_data_t *reqptr = (sensor_data_t*)request;
-    ipmi_ret_t rc = IPMI_CC_OK;
 
     printf("IPMI SET_SENSOR [0x%02x]\n",reqptr->sennum);
 
-    updateSensorRecordFromSSRAESC(reqptr);
+    /*
+     * This would support the Set Sensor Reading command for the presence
+     * and functional state of Processor, Core & DIMM. For the remaining
+     * sensors the existing support is invoked.
+     */
+    auto ipmiRC = setSensorReading(request);
+
+    if(ipmiRC == IPMI_CC_SENSOR_INVALID)
+    {
+        updateSensorRecordFromSSRAESC(reqptr);
+        ipmiRC = IPMI_CC_OK;
+    }
 
     *data_len=0;
-
-    return rc;
+    return ipmiRC;
 }
 
 
diff --git a/sensorhandler.h b/sensorhandler.h
index ca27b39..e908973 100644
--- a/sensorhandler.h
+++ b/sensorhandler.h
@@ -25,4 +25,23 @@
 int set_sensor_dbus_state_y(uint8_t , const char *, const uint8_t);
 int find_openbmc_path(const char *, const uint8_t , dbus_interface_t *);
 
+/**
+ * @struct SetSensorReadingReq
+ *
+ * IPMI Request data for Set Sensor Reading and Event Status Command
+ */
+struct SetSensorReadingReq
+{
+    uint8_t number;
+    uint8_t operation;
+    uint8_t reading;
+    uint8_t assertOffset0_7;
+    uint8_t assertOffset8_14;
+    uint8_t deassertOffset0_7;
+    uint8_t deassertOffset8_14;
+    uint8_t eventData1;
+    uint8_t eventData2;
+    uint8_t eventData3;
+} __attribute__((packed));
+
 #endif
diff --git a/types.hpp b/types.hpp
index d8a1386..555dce3 100644
--- a/types.hpp
+++ b/types.hpp
@@ -44,5 +44,12 @@
 using Id = uint8_t;
 using IdInfoMap = std::map<Id,Info>;
 
+using PropertyMap = std::map<DbusProperty, Value>;
+
+using InterfaceMap = std::map<DbusInterface, PropertyMap>;
+
+using Object = sdbusplus::message::object_path;
+using ObjectMap = std::map<Object, InterfaceMap>;
+
 }//namespce sensor
 }//namespace ipmi
diff --git a/utils.cpp b/utils.cpp
new file mode 100644
index 0000000..1823819
--- /dev/null
+++ b/utils.cpp
@@ -0,0 +1,38 @@
+#include "utils.hpp"
+
+namespace ipmi
+{
+
+std::string getService(sdbusplus::bus::bus& bus,
+                       const std::string& intf,
+                       const std::string& path)
+{
+    auto mapperCall = bus.new_method_call("xyz.openbmc_project.ObjectMapper",
+                                          "/xyz/openbmc_project/ObjectMapper",
+                                          "xyz.openbmc_project.ObjectMapper",
+                                          "GetObject");
+
+    mapperCall.append(path);
+    mapperCall.append(std::vector<std::string>({intf}));
+
+    auto mapperResponseMsg = bus.call(mapperCall);
+
+    if (mapperResponseMsg.is_method_error())
+    {
+        throw std::runtime_error("ERROR in mapper call");
+    }
+
+    std::map<std::string, std::vector<std::string>> mapperResponse;
+    mapperResponseMsg.read(mapperResponse);
+
+    if (mapperResponse.begin() == mapperResponse.end())
+    {
+        throw std::runtime_error("ERROR in reading the mapper response");
+    }
+
+    return mapperResponse.begin()->first;
+}
+
+} // namespace ipmi
+
+
diff --git a/utils.hpp b/utils.hpp
new file mode 100644
index 0000000..98be82a
--- /dev/null
+++ b/utils.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <sdbusplus/server.hpp>
+
+namespace ipmi
+{
+
+/**
+ * @brief Get the DBUS Service name for the input dbus path
+ *
+ * @param[in] bus - DBUS Bus Object
+ * @param[in] intf - DBUS Interface
+ * @param[in] path - DBUS Object Path
+ *
+ */
+std::string getService(sdbusplus::bus::bus& bus,
+                       const std::string& intf,
+                       const std::string& path);
+} // namespace ipmi
+
+