Add remaining BIOS thermal settings

This adds set/get for Fan UCC and fan speed offset.
Also this updates them to the new API.

Tested: was able to get and set parameters using bios
and changes were reflected next bios reading, as well
as in entity-manager

Change-Id: Ifd979e5d7966d05587c3af36791bd79ef358d391
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/src/oemcommands.cpp b/src/oemcommands.cpp
index 8bfc1ce..7896786 100644
--- a/src/oemcommands.cpp
+++ b/src/oemcommands.cpp
@@ -1140,6 +1140,8 @@
     "/xyz/openbmc_project/control/cfm_limit";
 constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
 constexpr const size_t legacyExitAirSensorNumber = 0x2e;
+constexpr const char* pidConfigurationIface =
+    "xyz.openbmc_project.Configuration.Pid";
 
 static std::string getExitAirConfigPath()
 {
@@ -1149,9 +1151,7 @@
                              "/xyz/openbmc_project/object_mapper",
                              "xyz.openbmc_project.ObjectMapper", "GetSubTree");
 
-    method.append(
-        "/", 0,
-        std::array<const char*, 1>{"xyz.openbmc_project.Configuration.Pid"});
+    method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
     std::string path;
     GetSubTreeType resp;
     try
@@ -1174,63 +1174,164 @@
     return path;
 }
 
-ipmi_ret_t ipmiOEMSetFscParameter(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
-                                  ipmi_request_t request,
-                                  ipmi_response_t response,
-                                  ipmi_data_len_t dataLen,
-                                  ipmi_context_t context)
+// flat map to make alphabetical
+static boost::container::flat_map<std::string, PropertyMap> getPidConfigs()
+{
+    boost::container::flat_map<std::string, PropertyMap> ret;
+    auto method =
+        dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
+                             "/xyz/openbmc_project/object_mapper",
+                             "xyz.openbmc_project.ObjectMapper", "GetSubTree");
+
+    method.append("/", 0, std::array<const char*, 1>{pidConfigurationIface});
+    GetSubTreeType resp;
+
+    try
+    {
+        auto reply = dbus.call(method);
+        reply.read(resp);
+    }
+    catch (sdbusplus::exception_t&)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "getFanConfigPaths: mapper error");
+    };
+    for (const auto& [path, objects] : resp)
+    {
+        if (objects.empty())
+        {
+            continue; // should be impossible
+        }
+        ret.emplace(path, getAllDbusProperties(dbus, objects[0].first, path,
+                                               pidConfigurationIface));
+    }
+    return ret;
+}
+
+ipmi::RspType<uint8_t> ipmiOEMGetFanSpeedOffset(void)
+{
+    boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
+    if (data.empty())
+    {
+        return ipmi::responseResponseError();
+    }
+    uint8_t minOffset = std::numeric_limits<uint8_t>::max();
+    for (const auto& [_, pid] : data)
+    {
+        auto findClass = pid.find("Class");
+        if (findClass == pid.end())
+        {
+            phosphor::logging::log<phosphor::logging::level::ERR>(
+                "ipmiOEMGetFscParameter: found illegal pid "
+                "configurations");
+            return ipmi::responseResponseError();
+        }
+        std::string type = std::get<std::string>(findClass->second);
+        if (type == "fan")
+        {
+            auto findOutLimit = pid.find("OutLimitMin");
+            if (findOutLimit == pid.end())
+            {
+                phosphor::logging::log<phosphor::logging::level::ERR>(
+                    "ipmiOEMGetFscParameter: found illegal pid "
+                    "configurations");
+                return ipmi::responseResponseError();
+            }
+            // get the min out of all the offsets
+            minOffset = std::min(
+                minOffset,
+                static_cast<uint8_t>(std::get<double>(findOutLimit->second)));
+        }
+    }
+    if (minOffset == std::numeric_limits<uint8_t>::max())
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "ipmiOEMGetFscParameter: found no fan configurations!");
+        return ipmi::responseResponseError();
+    }
+
+    return ipmi::responseSuccess(minOffset);
+}
+
+ipmi::RspType<> ipmiOEMSetFanSpeedOffset(uint8_t offset)
+{
+    boost::container::flat_map<std::string, PropertyMap> data = getPidConfigs();
+    if (data.empty())
+    {
+
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "ipmiOEMSetFanSpeedOffset: found no pid configurations!");
+        return ipmi::responseResponseError();
+    }
+
+    bool found = false;
+    for (const auto& [path, pid] : data)
+    {
+        auto findClass = pid.find("Class");
+        if (findClass == pid.end())
+        {
+
+            phosphor::logging::log<phosphor::logging::level::ERR>(
+                "ipmiOEMSetFanSpeedOffset: found illegal pid "
+                "configurations");
+            return ipmi::responseResponseError();
+        }
+        std::string type = std::get<std::string>(findClass->second);
+        if (type == "fan")
+        {
+            auto findOutLimit = pid.find("OutLimitMin");
+            if (findOutLimit == pid.end())
+            {
+
+                phosphor::logging::log<phosphor::logging::level::ERR>(
+                    "ipmiOEMSetFanSpeedOffset: found illegal pid "
+                    "configurations");
+                return ipmi::responseResponseError();
+            }
+            ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
+                                  path, pidConfigurationIface, "OutLimitMin",
+                                  static_cast<double>(offset));
+            found = true;
+        }
+    }
+    if (!found)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "ipmiOEMSetFanSpeedOffset: set no fan offsets");
+        return ipmi::responseResponseError();
+    }
+
+    return ipmi::responseSuccess();
+}
+
+ipmi::RspType<> ipmiOEMSetFscParameter(uint8_t command, uint8_t param1,
+                                       uint8_t param2)
 {
     constexpr const size_t disableLimiting = 0x0;
 
-    if (*dataLen < 2)
+    if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
     {
-        phosphor::logging::log<phosphor::logging::level::ERR>(
-            "ipmiOEMSetFscParameter: invalid input len!");
-        *dataLen = 0;
-        return IPMI_CC_REQ_DATA_LEN_INVALID;
-    }
-
-    uint8_t* req = static_cast<uint8_t*>(request);
-
-    if (*req == static_cast<uint8_t>(setFscParamFlags::tcontrol))
-    {
-        if (*dataLen == 3 && req[1] == legacyExitAirSensorNumber)
+        if (param1 == legacyExitAirSensorNumber)
         {
-            *dataLen = 0;
             std::string path = getExitAirConfigPath();
             ipmi::setDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
-                                  path, "xyz.openbmc_project.Configuration.Pid",
-                                  "SetPoint", static_cast<double>(req[2]));
-            return IPMI_CC_OK;
-        }
-        else if (*dataLen == 3)
-        {
-            *dataLen = 0;
-            return IPMI_CC_INVALID_FIELD_REQUEST;
+                                  path, pidConfigurationIface, "SetPoint",
+                                  static_cast<double>(param2));
+            return ipmi::responseSuccess();
         }
         else
         {
-            *dataLen = 0;
-            return IPMI_CC_REQ_DATA_LEN_INVALID;
+            return ipmi::responseParmOutOfRange();
         }
     }
-    else if (*req == static_cast<uint8_t>(setFscParamFlags::cfm))
+    else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
     {
-        if (*dataLen != 3)
-        {
-            phosphor::logging::log<phosphor::logging::level::ERR>(
-                "ipmiOEMSetFscParameter: invalid input len!");
-            *dataLen = 0;
-            return IPMI_CC_REQ_DATA_LEN_INVALID;
-        }
-        *dataLen = 0;
-
-        uint16_t cfm = req[1] | (static_cast<uint16_t>(req[2]) << 8);
+        uint16_t cfm = param1 | (static_cast<uint16_t>(param2) << 8);
 
         // must be greater than 50 based on eps
         if (cfm < 50 && cfm != disableLimiting)
         {
-            return IPMI_CC_PARM_OUT_OF_RANGE;
+            return ipmi::responseParmOutOfRange();
         }
 
         try
@@ -1244,9 +1345,48 @@
             phosphor::logging::log<phosphor::logging::level::ERR>(
                 "ipmiOEMSetFscParameter: can't set cfm setting!",
                 phosphor::logging::entry("ERR=%s", e.what()));
-            return IPMI_CC_UNSPECIFIED_ERROR;
+            return ipmi::responseResponseError();
         }
-        return IPMI_CC_OK;
+        return ipmi::responseSuccess();
+    }
+    else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
+    {
+        constexpr const size_t maxDomainCount = 8;
+        uint8_t requestedDomainMask = param1;
+        boost::container::flat_map data = getPidConfigs();
+        if (data.empty())
+        {
+
+            phosphor::logging::log<phosphor::logging::level::ERR>(
+                "ipmiOEMSetFscParameter: found no pid configurations!");
+            return ipmi::responseResponseError();
+        }
+        size_t count = 0;
+        for (const auto& [path, pid] : data)
+        {
+            auto findClass = pid.find("Class");
+            if (findClass == pid.end())
+            {
+
+                phosphor::logging::log<phosphor::logging::level::ERR>(
+                    "ipmiOEMSetFscParameter: found illegal pid "
+                    "configurations");
+                return ipmi::responseResponseError();
+            }
+            std::string type = std::get<std::string>(findClass->second);
+            if (type == "fan")
+            {
+                if (requestedDomainMask & (1 << count))
+                {
+                    ipmi::setDbusProperty(
+                        dbus, "xyz.openbmc_project.EntityManager", path,
+                        pidConfigurationIface, "OutLimitMax",
+                        static_cast<double>(param2));
+                }
+                count++;
+            }
+        }
+        return ipmi::responseSuccess();
     }
     else
     {
@@ -1254,75 +1394,115 @@
         // tcontrol is handled in peci now
         // fan speed offset not implemented yet
         // domain pwm limit not implemented
-        *dataLen = 0;
-        return IPMI_CC_PARM_OUT_OF_RANGE;
+        return ipmi::responseParmOutOfRange();
     }
 }
 
-ipmi_ret_t ipmiOEMGetFscParameter(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
-                                  ipmi_request_t request,
-                                  ipmi_response_t response,
-                                  ipmi_data_len_t dataLen,
-                                  ipmi_context_t context)
+ipmi::RspType<
+    std::variant<uint8_t, std::array<uint8_t, 2>, std::array<uint16_t, 2>>>
+    ipmiOEMGetFscParameter(uint8_t command, std::optional<uint8_t> param)
 {
     constexpr uint8_t legacyDefaultExitAirLimit = -128;
 
-    if (*dataLen < 1)
+    if (command == static_cast<uint8_t>(setFscParamFlags::tcontrol))
     {
-        phosphor::logging::log<phosphor::logging::level::ERR>(
-            "ipmiOEMGetFscParameter: invalid input len!");
-        *dataLen = 0;
-        return IPMI_CC_REQ_DATA_LEN_INVALID;
-    }
-
-    uint8_t* req = static_cast<uint8_t*>(request);
-
-    if (*req == static_cast<uint8_t>(setFscParamFlags::tcontrol))
-    {
-        if (*dataLen != 2)
+        if (!param)
         {
-            phosphor::logging::log<phosphor::logging::level::ERR>(
-                "ipmiOEMGetFscParameter: invalid input len!");
-            *dataLen = 0;
-            return IPMI_CC_REQ_DATA_LEN_INVALID;
+            return ipmi::responseReqDataLenInvalid();
         }
 
-        if (req[1] != legacyExitAirSensorNumber)
+        if (*param != legacyExitAirSensorNumber)
         {
-            return IPMI_CC_PARM_OUT_OF_RANGE;
+            return ipmi::responseParmOutOfRange();
         }
         uint8_t setpoint = legacyDefaultExitAirLimit;
         std::string path = getExitAirConfigPath();
         if (path.size())
         {
-            Value val = ipmi::getDbusProperty(
-                dbus, "xyz.openbmc_project.EntityManager", path,
-                "xyz.openbmc_project.Configuration.Pid", "SetPoint");
+            Value val =
+                ipmi::getDbusProperty(dbus, "xyz.openbmc_project.EntityManager",
+                                      path, pidConfigurationIface, "SetPoint");
             setpoint = std::floor(std::get<double>(val) + 0.5);
         }
 
         // old implementation used to return the "default" and current, we
         // don't make the default readily available so just make both the
         // same
-        auto resp = static_cast<uint8_t*>(response);
-        resp[0] = setpoint;
-        resp[1] = setpoint;
 
-        *dataLen = 2;
-        return IPMI_CC_OK;
+        return ipmi::responseSuccess(
+            std::array<uint8_t, 2>{setpoint, setpoint});
     }
-    else if (*req == static_cast<uint8_t>(setFscParamFlags::cfm))
+    else if (command == static_cast<uint8_t>(setFscParamFlags::maxPwm))
+    {
+        constexpr const size_t maxDomainCount = 8;
+
+        if (!param)
+        {
+            return ipmi::responseReqDataLenInvalid();
+        }
+        uint8_t requestedDomain = *param;
+        if (requestedDomain >= maxDomainCount)
+        {
+            return ipmi::responseInvalidFieldRequest();
+        }
+
+        boost::container::flat_map data = getPidConfigs();
+        if (data.empty())
+        {
+            phosphor::logging::log<phosphor::logging::level::ERR>(
+                "ipmiOEMGetFscParameter: found no pid configurations!");
+            return ipmi::responseResponseError();
+        }
+        size_t count = 0;
+        for (const auto& [_, pid] : data)
+        {
+            auto findClass = pid.find("Class");
+            if (findClass == pid.end())
+            {
+                phosphor::logging::log<phosphor::logging::level::ERR>(
+                    "ipmiOEMGetFscParameter: found illegal pid "
+                    "configurations");
+                return ipmi::responseResponseError();
+            }
+            std::string type = std::get<std::string>(findClass->second);
+            if (type == "fan")
+            {
+                if (requestedDomain == count)
+                {
+                    auto findOutLimit = pid.find("OutLimitMax");
+                    if (findOutLimit == pid.end())
+                    {
+                        phosphor::logging::log<phosphor::logging::level::ERR>(
+                            "ipmiOEMGetFscParameter: found illegal pid "
+                            "configurations");
+                        return ipmi::responseResponseError();
+                    }
+
+                    return ipmi::responseSuccess(
+                        static_cast<uint8_t>(std::floor(
+                            std::get<double>(findOutLimit->second) + 0.5)));
+                }
+                else
+                {
+                    count++;
+                }
+            }
+        }
+
+        return ipmi::responseInvalidFieldRequest();
+    }
+    else if (command == static_cast<uint8_t>(setFscParamFlags::cfm))
     {
 
         /*
         DataLen should be 1, but host is sending us an extra bit. As the
-        previous behavior didn't seem to prevent this, ignore the check for now.
+        previous behavior didn't seem to prevent this, ignore the check for
+        now.
 
-        if (*dataLen != 1)
+        if (param)
         {
             phosphor::logging::log<phosphor::logging::level::ERR>(
                 "ipmiOEMGetFscParameter: invalid input len!");
-            *dataLen = 0;
             return IPMI_CC_REQ_DATA_LEN_INVALID;
         }
         */
@@ -1340,51 +1520,28 @@
         catch (sdbusplus::exception_t& e)
         {
             phosphor::logging::log<phosphor::logging::level::ERR>(
-                "ipmiOEMSetFscParameter: can't get cfm setting!",
+                "ipmiOEMGetFscParameter: can't get cfm setting!",
                 phosphor::logging::entry("ERR=%s", e.what()));
-            *dataLen = 0;
-            return IPMI_CC_UNSPECIFIED_ERROR;
+            return ipmi::responseResponseError();
         }
 
-        auto cfmLim = std::get_if<double>(&cfmLimit);
-        if (cfmLim == nullptr ||
-            *cfmLim > std::numeric_limits<uint16_t>::max() || *cfmLim < 0)
-        {
-            phosphor::logging::log<phosphor::logging::level::ERR>(
-                "ipmiOEMSetFscParameter: cfm limit out of range!");
-            *dataLen = 0;
-            return IPMI_CC_UNSPECIFIED_ERROR;
-        }
+        double cfmMax = std::get<double>(cfmMaximum);
+        double cfmLim = std::get<double>(cfmLimit);
 
-        auto cfmMax = std::get_if<double>(&cfmMaximum);
-        if (cfmMax == nullptr ||
-            *cfmMax > std::numeric_limits<uint16_t>::max() || *cfmMax < 0)
-        {
-            phosphor::logging::log<phosphor::logging::level::ERR>(
-                "ipmiOEMSetFscParameter: cfm max out of range!");
-            *dataLen = 0;
-            return IPMI_CC_UNSPECIFIED_ERROR;
-        }
-        *cfmLim = std::floor(*cfmLim + 0.5);
-        *cfmMax = std::floor(*cfmMax + 0.5);
-        uint16_t resp = static_cast<uint16_t>(*cfmLim);
-        uint16_t* ptr = static_cast<uint16_t*>(response);
-        ptr[0] = resp;
-        resp = static_cast<uint16_t>(*cfmMax);
-        ptr[1] = resp;
+        cfmLim = std::floor(cfmLim + 0.5);
+        cfmMax = std::floor(cfmMax + 0.5);
+        uint16_t cfmLimResp = static_cast<uint16_t>(cfmLim);
+        uint16_t cfmMaxResp = static_cast<uint16_t>(cfmMax);
 
-        *dataLen = 4;
-
-        return IPMI_CC_OK;
+        return ipmi::responseSuccess(
+            std::array<uint16_t, 2>{cfmLimResp, cfmMaxResp});
     }
+
     else
     {
         // todo other command parts possibly
-        // fan speed offset not implemented yet
         // domain pwm limit not implemented
-        *dataLen = 0;
-
-        return IPMI_CC_PARM_OUT_OF_RANGE;
+        return ipmi::responseParmOutOfRange();
     }
 }
 
@@ -1475,15 +1632,27 @@
         static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetFanConfig),
         NULL, ipmiOEMGetFanConfig, PRIVILEGE_USER);
 
-    ipmiPrintAndRegister(netfnIntcOEMGeneral,
-                         static_cast<ipmi_cmd_t>(
-                             IPMINetfnIntelOEMGeneralCmd::cmdSetFscParameter),
-                         NULL, ipmiOEMSetFscParameter, PRIVILEGE_USER);
+    ipmi::registerHandler(
+        ipmi::prioOemBase, netfnIntcOEMGeneral,
+        static_cast<ipmi::Cmd>(
+            IPMINetfnIntelOEMGeneralCmd::cmdGetFanSpeedOffset),
+        ipmi::Privilege::User, ipmiOEMGetFanSpeedOffset);
 
-    ipmiPrintAndRegister(netfnIntcOEMGeneral,
-                         static_cast<ipmi_cmd_t>(
-                             IPMINetfnIntelOEMGeneralCmd::cmdGetFscParameter),
-                         NULL, ipmiOEMGetFscParameter, PRIVILEGE_USER);
+    ipmi::registerHandler(
+        ipmi::prioOemBase, netfnIntcOEMGeneral,
+        static_cast<ipmi::Cmd>(
+            IPMINetfnIntelOEMGeneralCmd::cmdSetFanSpeedOffset),
+        ipmi::Privilege::User, ipmiOEMSetFanSpeedOffset);
+
+    ipmi::registerHandler(
+        ipmi::prioOemBase, netfnIntcOEMGeneral,
+        static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdSetFscParameter),
+        ipmi::Privilege::User, ipmiOEMSetFscParameter);
+
+    ipmi::registerHandler(
+        ipmi::prioOemBase, netfnIntcOEMGeneral,
+        static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdGetFscParameter),
+        ipmi::Privilege::User, ipmiOEMGetFscParameter);
 
     ipmiPrintAndRegister(
         netfnIntcOEMGeneral,