IPMI: Virtual sensor support in host ipmid

Resolves openbmc/openbmc#1608

Change-Id: Id76446061fd0fa6dc3dead702538e424293af7ce
Signed-off-by: Dhruvaraj Subhashchandran <dhruvaraj@in.ibm.com>
diff --git a/scripts/sensor-example.yaml b/scripts/sensor-example.yaml
index c17b977..750da95 100755
--- a/scripts/sensor-example.yaml
+++ b/scripts/sensor-example.yaml
@@ -1,12 +1,35 @@
+#sample yaml with documentation
+#Sensor Number
 0xa6:
+  #Sensor Type
   sensorType: 0x0C
+  #There are two types of updates one with Set method and other with
+  #Notify method for inventory updates. The path indicates Inventory path for
+  #the Notify method and Dbus object path for Set method.
   path: /system/chassis/motherboard/dimm0
+  #event reading type
   sensorReadingType: 0x6F
+  #Dbus service interface to make a bus call to update or request value of a
+  #property
+  serviceInterface: xyz.openbmc_project.Inventory.Manager
+  #command data has three fields, sensor reading value, assertion and
+  #deassertion bits and event data, this indicates which data field should
+  #be used. Possible value to be updated.
+  readingType: assertion
+  #List of dbus interfaces associated with the interested properties.
   interfaces:
+    #Dbus interface implementing the interested property.
     xyz.openbmc_project.State.Decorator.OperationalStatus:
+      #DBus property
       Functional:
+        #Offset, for assertion it should be a bit mask to indicate which bit
+        #indicates the property is true or false, in event or reading types
+        #the value will get mapped to a Dbus enum, 0xFF need to give if the
+        #reading or event value need to be updated as it is.
         0x04:
+          #type of the property
           type: bool
+          #mapping from event offset bit in the command to the property value.
           deassert: "true"
           assert: "false"
     xyz.openbmc_project.Inventory.Item:
@@ -15,10 +38,25 @@
           type: bool
           assert: "true"
           deassert: "false"
+0x07:
+  sensorType: 0xC3
+  path: /xyz/openbmc_project/control/host0
+  sensorReadingType: 0x6F
+  serviceInterface: org.freedesktop.DBus.Properties
+  readingType: reading
+  interfaces:
+    xyz.openbmc_project.Control.Boot.RebootAttempts:
+      AttemptsLeft:
+          #A 0xFF indicates the value need to be send to dbus
+          0xFF:
+            type: uint32_t
 0xa8:
   sensorType: 0x0C
   path: /system/chassis/motherboard/dimm1
   sensorReadingType: 0x6F
+  serviceInterface: xyz.openbmc_project.Inventory.Manager
+  readingType: assertion
+  byteOffset: 0x00
   interfaces:
     xyz.openbmc_project.State.Decorator.OperationalStatus:
       Functional:
@@ -36,6 +74,9 @@
   sensorType: 0x0C
   path: /system/chassis/motherboard/dimm2
   sensorReadingType: 0x6F
+  serviceInterface: xyz.openbmc_project.Inventory.Manager
+  readingType: assertion
+  byteOffset: 0x00
   interfaces:
     xyz.openbmc_project.State.Decorator.OperationalStatus:
       Functional:
@@ -53,6 +94,9 @@
   sensorType: 0x0C
   path: /system/chassis/motherboard/dimm3
   sensorReadingType: 0x6F
+  serviceInterface: xyz.openbmc_project.Inventory.Manager
+  readingType: assertion
+  byteOffset: 0x00
   interfaces:
     xyz.openbmc_project.State.Decorator.OperationalStatus:
       Functional:
diff --git a/scripts/writesensor.mako.cpp b/scripts/writesensor.mako.cpp
index cb3be09..41f9d60 100644
--- a/scripts/writesensor.mako.cpp
+++ b/scripts/writesensor.mako.cpp
@@ -2,10 +2,107 @@
 ## into the rendered file; feel free to edit this file.
 
 // !!! WARNING: This is a GENERATED Code..Please do NOT Edit !!!
-
+<%
+from collections import defaultdict
+readingTypes = { 'reading': 'cmdData.reading',
+                 'assertion': '0',
+                 'eventdata1': 'cmdData.eventData1',
+                 'eventdata2': 'cmdData.eventData2',
+                 'eventdata3': 'cmdData.eventData3'}
+funcProps = {}
+%>\
+%for key in sensorDict.iterkeys():
+<%
+    sensor = sensorDict[key]
+    sensorType = sensor["sensorType"]
+    serviceInterface = sensor["serviceInterface"]
+    readingType = sensor["readingType"]
+    if serviceInterface == "org.freedesktop.DBus.Properties":
+        command = "Set"
+    elif serviceInterface == "xyz.openbmc_project.Inventory.Manager":
+        command = "Notify"
+    else:
+        assert "Un-supported interface: serviceInterface"
+    endif
+    sensorInterface = serviceInterface
+    updateFunc = "sensor_set::sensor_type_" + str(sensorType) + "::update"
+    funcProps[sensorType] = {}
+    funcProps[sensorType].update({"command" : command})
+    funcProps[sensorType].update({"path" : sensor["path"]})
+    funcProps[sensorType].update({"serviceInterface" : serviceInterface})
+    funcProps[sensorType].update({"updateFunc" : updateFunc})
+    funcProps[sensorType].update({"readingType" : readingType})
+    funcProps[sensorType].update({"source" : readingTypes[readingType]})
+    funcProps[sensorType].update({"interfaces" : sensor["interfaces"]})
+    if command == "Set":
+        for interface, props in funcProps[sensorType]["interfaces"].items():
+            sensorInterface = interface
+    funcProps[sensorType].update({"sensorInterface" : sensorInterface})
+%>\
+% endfor
+#include <bitset>
 #include "types.hpp"
-using namespace ipmi::sensor;
+#include "host-ipmid/ipmid-api.h"
+#include <phosphor-logging/elog-errors.hpp>
+#include "xyz/openbmc_project/Common/error.hpp"
+#include <phosphor-logging/log.hpp>
+#include "sensordatahandler.hpp"
 
+namespace ipmi
+{
+namespace sensor
+{
+
+
+namespace sensor_set
+{
+% for sensorType, funcProp in funcProps.iteritems():
+namespace sensor_type_${sensorType}
+{
+
+ipmi_ret_t update(const SetSensorReadingReq& cmdData,
+                  const Info& sensorInfo)
+{
+    auto msg = ${(funcProp["command"]).lower()}::makeDbusMsg(
+                    "${funcProp['serviceInterface']}",
+                     sensorInfo.sensorPath,
+                    "${funcProp['command']}",
+                    "${funcProp['sensorInterface']}");
+
+    auto interfaceList = sensorInfo.sensorInterfaces;
+% for interface, properties in funcProp["interfaces"].iteritems():
+    % for dbus_property, property_value in properties.iteritems():
+        % for offset, values in property_value.iteritems():
+            % if offset == 0xFF:
+<%                funcName = "appendReadingData"%>\
+<%                param = "static_cast<"+values["type"]+">("+funcProp["source"]+")"%>\
+            %  elif funcProp["readingType"] == "assertion":
+<%                funcName = "appendAssertion"%>\
+<%                param = "sensorInfo.sensorPath, cmdData"%>\
+            % else:
+<%                funcName = "appendDiscreteSignalData"%>\
+<%                param = funcProp["source"]%>\
+            % endif
+        % endfor
+    % endfor
+% endfor
+    auto result = ${(funcProp["command"]).lower()}::${funcName}(msg,
+                        interfaceList,
+                        ${param});
+    if (result != IPMI_CC_OK)
+    {
+        return result;
+    }
+    return updateToDbus(msg);
+}
+}//namespace sensor_type_${sensorType}
+
+% endfor
+}//namespace sensor_get
+}//namespace sensor
+}//namespace ipmi
+
+using namespace ipmi::sensor;
 extern const IdInfoMap sensors = {
 % for key in sensorDict.iterkeys():
    % if key:
@@ -19,24 +116,30 @@
        multiplier = sensor.get("multiplierM", 1)
        offset = sensor.get("offsetB", 0)
        exp = sensor.get("bExp", 0)
+       valueReadingType = sensor["readingType"]
+       updateFunc = funcProps[sensorType]["updateFunc"]
 %>
         ${sensorType},"${path}",${readingType},${multiplier},${offset},${exp},
-        ${offset * pow(10,exp)},{
+        ${offset * pow(10,exp)},${updateFunc},{
     % for interface,properties in interfaces.iteritems():
             {"${interface}",{
             % for dbus_property,property_value in properties.iteritems():
                 {"${dbus_property}",{
                 % for offset,values in property_value.iteritems():
                     { ${offset},{
-                        <% valueType = values["type"] %>\
-                     % for name,value in values.iteritems():
+                        % if offset == 0xFF:
+                            }},
+<%                          continue %>\
+                        % endif
+<%                          valueType = values["type"] %>\
+                    % for name,value in values.iteritems():
                         % if name == "type":
-                             <% continue %>\
+<%                          continue %>\
                         % endif
                         % if valueType == "string":
                            std::string("${value}"),
                         % elif valueType == "bool":
-                           <% value = str(value).lower() %>\
+<%                         value = str(value).lower() %>\
                            ${value},
                         % else:
                            ${value},