Add fan profile host support
Implement the fan profile portions of get / set fan
profile ipmi commands. These commands have been overused
with too much debug information, currently we only plan
to support the part that the host needs.
Tested-by: Modified the settings on the host and they
were persisted correctly on the next boot
Change-Id: I5736703123154193b8e193ccb14fc2ffb9f5320b
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/src/oemcommands.cpp b/src/oemcommands.cpp
index 5791b28..cc4a437 100644
--- a/src/oemcommands.cpp
+++ b/src/oemcommands.cpp
@@ -20,6 +20,7 @@
#include <ipmid/api.h>
#include <array>
+#include <boost/container/flat_map.hpp>
#include <boost/process/child.hpp>
#include <boost/process/io.hpp>
#include <commandutils.hpp>
@@ -29,6 +30,7 @@
#include <phosphor-logging/log.hpp>
#include <sdbusplus/bus.hpp>
#include <string>
+#include <variant>
#include <vector>
namespace ipmi
@@ -704,6 +706,146 @@
return IPMI_CC_OK;
}
+constexpr const char* thermalModeInterface =
+ "xyz.openbmc_project.Control.ThermalMode";
+constexpr const char* thermalModePath =
+ "/xyz/openbmc_project/control/thermal_mode";
+
+bool getFanProfileInterface(
+ sdbusplus::bus::bus& bus,
+ boost::container::flat_map<
+ std::string, std::variant<std::vector<std::string>, std::string>>& resp)
+{
+ auto call = bus.new_method_call(settingsBusName, thermalModePath, PROP_INTF,
+ "GetAll");
+ call.append(thermalModeInterface);
+ try
+ {
+ auto data = bus.call(call);
+ data.read(resp);
+ }
+ catch (sdbusplus::exception_t& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "getFanProfileInterface: can't get thermal mode!",
+ phosphor::logging::entry("ERR=%s", e.what()));
+ return false;
+ }
+ return true;
+}
+
+ipmi_ret_t ipmiOEMSetFanConfig(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)
+{
+
+ if (*dataLen < 2 || *dataLen > 7)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmiOEMSetFanConfig: invalid input len!");
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ // todo: tell bios to only send first 2 bytes
+
+ SetFanConfigReq* req = reinterpret_cast<SetFanConfigReq*>(request);
+ boost::container::flat_map<
+ std::string, std::variant<std::vector<std::string>, std::string>>
+ profileData;
+ if (!getFanProfileInterface(dbus, profileData))
+ {
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ std::vector<std::string>* supported =
+ std::get_if<std::vector<std::string>>(&profileData["Supported"]);
+ if (supported == nullptr)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ std::string mode;
+ if (req->flags &
+ (1 << static_cast<uint8_t>(setFanProfileFlags::setPerfAcousMode)))
+ {
+ bool performanceMode =
+ (req->flags & (1 << static_cast<uint8_t>(
+ setFanProfileFlags::performAcousSelect))) > 0;
+
+ if (performanceMode)
+ {
+
+ if (std::find(supported->begin(), supported->end(),
+ "Performance") != supported->end())
+ {
+ mode = "Performance";
+ }
+ }
+ else
+ {
+
+ if (std::find(supported->begin(), supported->end(), "Acoustic") !=
+ supported->end())
+ {
+ mode = "Acoustic";
+ }
+ }
+ if (mode.empty())
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ setDbusProperty(dbus, settingsBusName, thermalModePath,
+ thermalModeInterface, "Current", mode);
+ }
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiOEMGetFanConfig(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)
+{
+
+ if (*dataLen > 1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmiOEMGetFanConfig: invalid input len!");
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ // todo: talk to bios about needing less information
+
+ GetFanConfigResp* resp = reinterpret_cast<GetFanConfigResp*>(response);
+ *dataLen = sizeof(GetFanConfigResp);
+
+ boost::container::flat_map<
+ std::string, std::variant<std::vector<std::string>, std::string>>
+ profileData;
+
+ if (!getFanProfileInterface(dbus, profileData))
+ {
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ std::string* current = std::get_if<std::string>(&profileData["Current"]);
+
+ if (current == nullptr)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmiOEMGetFanConfig: can't get current mode!");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ bool performance = (*current == "Performance");
+
+ if (performance)
+ {
+ resp->flags |= 1 << 2;
+ }
+
+ return IPMI_CC_OK;
+}
+
static void registerOEMFunctions(void)
{
phosphor::logging::log<phosphor::logging::level::INFO>(
@@ -766,6 +908,17 @@
static_cast<ipmi_cmd_t>(
IPMINetfnIntelOEMGeneralCmd::cmdGetShutdownPolicy),
NULL, ipmiOEMGetShutdownPolicy, PRIVILEGE_ADMIN);
+
+ ipmiPrintAndRegister(
+ netfnIntcOEMGeneral,
+ static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdSetFanConfig),
+ NULL, ipmiOEMSetFanConfig, PRIVILEGE_USER);
+
+ ipmiPrintAndRegister(
+ netfnIntcOEMGeneral,
+ static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetFanConfig),
+ NULL, ipmiOEMGetFanConfig, PRIVILEGE_USER);
+
ipmiPrintAndRegister(
netfnIntcOEMGeneral,
static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetLEDStatus),
diff --git a/src/utils.cpp b/src/utils.cpp
index 8dac5ab..7619186 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -1,9 +1,8 @@
-#include "phosphor-ipmi-host/utils.hpp"
-
#include <arpa/inet.h>
#include <dirent.h>
#include <net/if.h>
+#include <phosphor-ipmi-host/utils.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
#include <xyz/openbmc_project/Common/error.hpp>