Update hwmon fan target sysfs entries
Override the default FanSpeed.Target set implementation so when a target
value is written to the FanSpeed interface it is also updated in the
related fan target sysfs file. This sets a particular fan to the given
target speed.
Resolves openbmc/openbmc#962
Resolves openbmc/phosphor-hwmon#1
Change-Id: I867811737269b3f42d2a0dc15b37782a74f147b8
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index b7057ad..1ac9033 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -21,6 +21,7 @@
sensorset.cpp \
mainloop.cpp \
sysfs.cpp \
- env.cpp
+ env.cpp \
+ fan_speed.cpp
SUBDIRS = . test
diff --git a/fan_speed.cpp b/fan_speed.cpp
new file mode 100644
index 0000000..74bbea3
--- /dev/null
+++ b/fan_speed.cpp
@@ -0,0 +1,26 @@
+#include "fan_speed.hpp"
+#include "hwmon.hpp"
+#include "sysfs.hpp"
+
+namespace hwmon
+{
+
+uint64_t FanSpeed::target(uint64_t value)
+{
+ auto curValue = FanSpeedObject::target();
+
+ if (curValue != value)
+ {
+ //Write target out to sysfs
+ curValue = writeSysfsWithCallout(value,
+ sysfsRoot,
+ instance,
+ type,
+ id,
+ entry::target);
+ }
+
+ return FanSpeedObject::target(value);
+}
+
+} // namespace hwmon
diff --git a/fan_speed.hpp b/fan_speed.hpp
new file mode 100644
index 0000000..7c7da3a
--- /dev/null
+++ b/fan_speed.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+#include "interface.hpp"
+
+namespace hwmon
+{
+
+/**
+ * @class FanSpeed
+ * @brief Target fan speed control implementation
+ * @details Derived FanSpeedObject type that writes the target value to sysfs
+ * which in turn sets the fan speed to that target value
+ */
+class FanSpeed : public FanSpeedObject
+{
+ public:
+
+ /**
+ * @brief Constructs FanSpeed Object
+ *
+ * @param[in] sysfsRoot - The hwmon class root
+ * @param[in] instance - The hwmon instance (ex. hwmon1)
+ * @param[in] id - The hwmon id
+ * @param[in] bus - Dbus bus object
+ * @param[in] objPath - Dbus object path
+ * @param[in] defer - Dbus object registration defer
+ */
+ FanSpeed(const std::string& sysfsRoot,
+ const std::string& instance,
+ const std::string& id,
+ sdbusplus::bus::bus& bus,
+ const char* objPath,
+ bool defer) : FanSpeedObject(bus, objPath, defer),
+ sysfsRoot(sysfsRoot),
+ instance(instance),
+ id(id)
+ {
+ // Nothing to do here
+ }
+
+ /**
+ * @brief Set the value of target
+ *
+ * @return Value of target
+ */
+ uint64_t target(uint64_t value) override;
+
+ private:
+ /** @brief hwmon class root */
+ std::string sysfsRoot;
+ /** @brief hwmon instance */
+ std::string instance;
+ /** @brief hwmon type */
+ static constexpr auto type = "fan";
+ /** @brief hwmon id */
+ std::string id;
+};
+
+} // namespace hwmon
diff --git a/mainloop.cpp b/mainloop.cpp
index 7af3096..34970b1 100644
--- a/mainloop.cpp
+++ b/mainloop.cpp
@@ -25,6 +25,7 @@
#include "env.hpp"
#include "thresholds.hpp"
#include "targets.hpp"
+#include "fan_speed.hpp"
// Initialization for Warning Objects
decltype(Thresholds<WarningObject>::setLo) Thresholds<WarningObject>::setLo =
@@ -54,12 +55,6 @@
decltype(Thresholds<CriticalObject>::alarmHi) Thresholds<CriticalObject>::alarmHi =
&CriticalObject::criticalAlarmHigh;
-// Initialization for Target objects
-decltype(Targets<FanSpeedObject>::setTarget)
- Targets<FanSpeedObject>::setTarget = &FanSpeedObject::target;
-decltype(Targets<FanSpeedObject>::getTarget)
- Targets<FanSpeedObject>::getTarget = &FanSpeedObject::target;
-
using namespace std::literals::chrono_literals;
@@ -247,7 +242,7 @@
addThreshold<CriticalObject>(i.first, sensorValue, info);
//TODO openbmc/openbmc#1347
// Handle application restarts to set/refresh fan speed values
- addTarget<FanSpeedObject>(i.first, _hwmonRoot, _instance, info);
+ addTarget<hwmon::FanSpeed>(i.first, _hwmonRoot, _instance, info);
// All the interfaces have been created. Go ahead
// and emit InterfacesAdded.
diff --git a/sysfs.cpp b/sysfs.cpp
index 75a1376..8b67f3e 100644
--- a/sysfs.cpp
+++ b/sysfs.cpp
@@ -16,10 +16,14 @@
#include <cstdlib>
#include <experimental/filesystem>
#include <memory>
-#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <xyz/openbmc_project/Control/Device/error.hpp>
#include "sysfs.hpp"
#include "util.hpp"
+using namespace phosphor::logging;
+
std::string findHwmon(const std::string& ofNode)
{
namespace fs = std::experimental::filesystem;
@@ -89,4 +93,54 @@
return value;
}
+uint64_t writeSysfsWithCallout(const uint64_t& value,
+ const std::string& root,
+ const std::string& instance,
+ const std::string& type,
+ const std::string& id,
+ const std::string& sensor)
+{
+ namespace fs = std::experimental::filesystem;
+
+ std::string valueStr = std::to_string(value);
+ std::ofstream ofs;
+ fs::path instancePath{root};
+ instancePath /= instance;
+ std::string fullPath = make_sysfs_path(instancePath,
+ type, id, sensor);
+
+ ofs.exceptions(std::ofstream::failbit
+ | std::ofstream::badbit
+ | std::ofstream::eofbit);
+ try
+ {
+ ofs.open(fullPath);
+ ofs << valueStr;
+ }
+ catch (const std::exception& e)
+ {
+ // errno should still reflect the error from the failing open
+ // or write system calls that got us here.
+ auto rc = errno;
+ instancePath /= "device";
+ using namespace sdbusplus::xyz::openbmc_project::Control::Device::Error;
+ try
+ {
+ elog<WriteFailure>(
+ xyz::openbmc_project::Control::Device::
+ WriteFailure::CALLOUT_ERRNO(rc),
+ xyz::openbmc_project::Control::Device::
+ WriteFailure::CALLOUT_DEVICE_PATH(
+ fs::canonical(instancePath).c_str()));
+ }
+ catch (WriteFailure& elog)
+ {
+ commit(elog.name());
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ return value;
+}
+
// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/sysfs.hpp b/sysfs.hpp
index a74baaf..cc36ef4 100644
--- a/sysfs.hpp
+++ b/sysfs.hpp
@@ -44,4 +44,24 @@
const std::string& id,
const std::string& sensor);
+ /** @brief Write a hwmon sysfs value
+ *
+ * Calls exit(3) with bad status on failure
+ *
+ * @param[in] value - The value to be written
+ * @param[in] root - The hwmon class root.
+ * @param[in] instance - The hwmon instance (ex. hwmon1).
+ * @param[in] type - The hwmon type (ex. fan).
+ * @param[in] id - The hwmon id (ex. 1).
+ * @param[in] sensor - The hwmon sensor (ex. target).
+ *
+ * @returns - The value written
+ */
+uint64_t writeSysfsWithCallout(const uint64_t& value,
+ const std::string& root,
+ const std::string& instance,
+ const std::string& type,
+ const std::string& id,
+ const std::string& sensor);
+
// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/targets.hpp b/targets.hpp
index 2b0feb3..381ba75 100644
--- a/targets.hpp
+++ b/targets.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <experimental/filesystem>
+#include "fan_speed.hpp"
/** @class Targets
* @brief Target type traits.
@@ -18,11 +19,9 @@
/**@brief Targets specialization for fan speed. */
template <>
-struct Targets<FanSpeedObject>
+struct Targets<hwmon::FanSpeed>
{
static constexpr InterfaceType type = InterfaceType::FAN_SPEED;
- static uint64_t (FanSpeedObject::*const setTarget)(uint64_t);
- static uint64_t (FanSpeedObject::*const getTarget)() const;
};
/** @brief addTarget
@@ -51,13 +50,18 @@
// Check if target sysfs file exists
auto targetPath = hwmonRoot + '/' + instance;
- auto sysfsFile = make_sysfs_path(targetPath,
- sensor.first,
- sensor.second,
- hwmon::entry::target);
- if (fs::exists(sysfsFile))
+ auto sysfsFullPath = make_sysfs_path(targetPath,
+ sensor.first,
+ sensor.second,
+ hwmon::entry::target);
+ if (fs::exists(sysfsFullPath))
{
- auto iface = std::make_shared<T>(bus, objPath.c_str(), deferSignals);
+ auto iface = std::make_shared<T>(hwmonRoot,
+ instance,
+ sensor.second,
+ bus,
+ objPath.c_str(),
+ deferSignals);
auto type = Targets<T>::type;
obj[type] = iface;
}