dbus-sdr: Add the function of reading VR sensor event.

ipmiSenGetSensorEventStatus() now handles VR type sensor. It reads the
profile status from VR daemon and set the corresponding event bit to
IPMI package.

tested: by ipmi cmd: 0x04 0x2B
```
HOST:
$ /tmp# ./ipmitool raw 0x04 0x2B "0xb4"
 00 02

BMC:
journalctl --since "1 min ago" | grep ipmid
Jan 01 00:09:16 $HOST ipmid[3078]: VR sensor $SENSOR_NAME mode is: [1] 800MHz
```

Signed-off-by: Hao Jiang <jianghao@google.com>
Change-Id: I491765ec6c840836e0d69122e29d9a86a3dea82a
Signed-off-by: Willy Tu <wltu@google.com>
diff --git a/dbus-sdr/sensorcommands.cpp b/dbus-sdr/sensorcommands.cpp
index f15b80b..0e513c5 100644
--- a/dbus-sdr/sensorcommands.cpp
+++ b/dbus-sdr/sensorcommands.cpp
@@ -43,6 +43,11 @@
 
 namespace ipmi
 {
+
+using phosphor::logging::entry;
+using phosphor::logging::level;
+using phosphor::logging::log;
+
 static constexpr int sensorMapUpdatePeriod = 10;
 static constexpr int sensorMapSdrUpdatePeriod = 60;
 
@@ -271,15 +276,14 @@
 
 namespace sensor
 {
-// Calculate VR Mode from input IPMI discrete event bytes
-static std::optional<std::string>
-    calculateVRMode(uint15_t assertOffset,
-                    const ipmi::DbusInterfaceMap::mapped_type& VRObject)
+// Read VR profiles from sensor(daemon) interface
+static std::optional<std::vector<std::string>>
+    getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object)
 {
     // get VR mode profiles from Supported Interface
-    auto supportedProperty = VRObject.find("Supported");
-    if (supportedProperty == VRObject.end() ||
-        VRObject.find("Selected") == VRObject.end())
+    auto supportedProperty = object.find("Supported");
+    if (supportedProperty == object.end() ||
+        object.find("Selected") == object.end())
     {
         phosphor::logging::log<phosphor::logging::level::ERR>(
             "Missing the required Supported and Selected properties");
@@ -295,6 +299,20 @@
             "property is not array of string");
         return std::nullopt;
     }
+    return *profilesPtr;
+}
+
+// Calculate VR Mode from input IPMI discrete event bytes
+static std::optional<std::string>
+    calculateVRMode(uint15_t assertOffset,
+                    const ipmi::DbusInterfaceMap::mapped_type& VRObject)
+{
+    // get VR mode profiles from Supported Interface
+    auto profiles = getSupportedVrProfiles(VRObject);
+    if (!profiles)
+    {
+        return std::nullopt;
+    }
 
     // interpret IPMI cmd bits into profiles' index
     long unsigned int index = 0;
@@ -316,14 +334,14 @@
         index++;
     }
 
-    if (index >= profilesPtr->size())
+    if (index >= profiles->size())
     {
         phosphor::logging::log<phosphor::logging::level::ERR>(
             "profile index out of boundary");
         return std::nullopt;
     }
 
-    return profilesPtr->at(index);
+    return profiles->at(index);
 }
 
 // Calculate sensor value from IPMI reading byte
@@ -390,6 +408,74 @@
     return name;
 }
 
+bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection,
+                      const std::string& path,
+                      const ipmi::DbusInterfaceMap::mapped_type& object,
+                      std::bitset<16>& assertions)
+{
+    auto profiles = sensor::getSupportedVrProfiles(object);
+    if (!profiles)
+    {
+        return false;
+    }
+    ipmi::Value modeVariant;
+
+    auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface,
+                              "Selected", modeVariant);
+    if (ec)
+    {
+        log<level::ERR>("Failed to get property",
+                        entry("PROPERTY=%s", "Selected"),
+                        entry("PATH=%s", path.c_str()),
+                        entry("INTERFACE=%s", sensor::sensorInterface),
+                        entry("WHAT=%s", ec.message().c_str()));
+        return false;
+    }
+
+    auto mode = std::get_if<std::string>(&modeVariant);
+    if (mode == nullptr)
+    {
+        log<level::ERR>("property is not a string",
+                        entry("PROPERTY=%s", "Selected"),
+                        entry("PATH=%s", path.c_str()),
+                        entry("INTERFACE=%s", sensor::sensorInterface));
+        return false;
+    }
+
+    auto itr = std::find(profiles->begin(), profiles->end(), *mode);
+    if (itr == profiles->end())
+    {
+        using namespace phosphor::logging;
+        log<level::ERR>("VR mode doesn't match any of its profiles",
+                        entry("PATH=%s", path.c_str()));
+        return false;
+    }
+    std::size_t index =
+        static_cast<std::size_t>(std::distance(profiles->begin(), itr));
+
+    // map index to reponse event assertion bit.
+    if (index < 8)
+    {
+        assertions.set(1u << index);
+    }
+    else if (index < 15)
+    {
+        assertions.set(1u << (index - 8));
+    }
+    else
+    {
+        log<level::ERR>("VR profile index reaches max assertion bit",
+                        entry("PATH=%s", path.c_str()),
+                        entry("INDEX=%uz", index));
+        return false;
+    }
+    if constexpr (debug)
+    {
+        std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path)
+                  << " mode is: [" << index << "] " << *mode << std::endl;
+    }
+    return true;
+}
 } // namespace sensor
 
 ipmi::RspType<> ipmiSenPlatformEvent(uint8_t generatorID, uint8_t evmRev,
@@ -1101,14 +1187,33 @@
             phosphor::logging::entry("SENSOR=%s", path.c_str()));
         return ipmi::responseResponseError();
     }
+
+    uint8_t sensorEventStatus =
+        static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
+    std::bitset<16> assertions = 0;
+    std::bitset<16> deassertions = 0;
+
+    // handle VR typed sensor
+    auto vrInterface = sensorMap.find(sensor::vrInterface);
+    if (vrInterface != sensorMap.end())
+    {
+        if (!sensor::getVrEventStatus(ctx, connection, path,
+                                      vrInterface->second, assertions))
+        {
+            return ipmi::responseResponseError();
+        }
+
+        // both Event Message and Sensor Scanning are disable for VR.
+        sensorEventStatus = 0;
+        return ipmi::responseSuccess(sensorEventStatus, assertions,
+                                     deassertions);
+    }
+
     auto warningInterface =
         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning");
     auto criticalInterface =
         sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical");
 
-    uint8_t sensorEventStatus =
-        static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable);
-
     std::optional<bool> criticalDeassertHigh =
         thresholdDeassertMap[path]["CriticalAlarmHigh"];
     std::optional<bool> criticalDeassertLow =
@@ -1118,9 +1223,6 @@
     std::optional<bool> warningDeassertLow =
         thresholdDeassertMap[path]["WarningAlarmLow"];
 
-    std::bitset<16> assertions = 0;
-    std::bitset<16> deassertions = 0;
-
     if (criticalDeassertHigh && !*criticalDeassertHigh)
     {
         deassertions.set(static_cast<size_t>(