sensors: Align source structure away from anti-patterns
The anti-patterns document comments on source structure, specifically
on placing internal headers in a parallel subtree[1]. dbus-sensors is an
example of violating this anti-pattern, so fix it.
[1]: https://github.com/openbmc/docs/blob/master/anti-patterns.md#placing-internal-headers-in-a-parallel-subtree
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Change-Id: I50ecaddd53fa9c9b7a0441af9de5e60bd94e47c6
diff --git a/src/ADCSensor.cpp b/src/ADCSensor.cpp
index fe2c8a7..2fc35e0 100644
--- a/src/ADCSensor.cpp
+++ b/src/ADCSensor.cpp
@@ -14,9 +14,10 @@
// limitations under the License.
*/
+#include "ADCSensor.hpp"
+
#include <unistd.h>
-#include <ADCSensor.hpp>
#include <boost/asio/read_until.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
diff --git a/src/ADCSensor.hpp b/src/ADCSensor.hpp
new file mode 100644
index 0000000..72fcd90
--- /dev/null
+++ b/src/ADCSensor.hpp
@@ -0,0 +1,92 @@
+#pragma once
+
+#include "Thresholds.hpp"
+#include "sensor.hpp"
+
+#include <boost/asio/streambuf.hpp>
+#include <gpiod.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <memory>
+#include <optional>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+class BridgeGpio
+{
+ public:
+ BridgeGpio(const std::string& name, const int polarity,
+ const float setupTime) :
+ setupTimeMs(static_cast<unsigned int>(setupTime * 1000))
+ {
+ line = gpiod::find_line(name);
+ if (!line)
+ {
+ std::cerr << "Error finding gpio: " << name << "\n";
+ }
+ else
+ {
+ try
+ {
+ line.request({"adcsensor",
+ gpiod::line_request::DIRECTION_OUTPUT,
+ polarity == gpiod::line::ACTIVE_HIGH
+ ? 0
+ : gpiod::line_request::FLAG_ACTIVE_LOW});
+ }
+ catch (const std::system_error&)
+ {
+ std::cerr << "Error requesting gpio: " << name << "\n";
+ }
+ }
+ }
+
+ void set(int value)
+ {
+ if (line)
+ {
+ try
+ {
+ line.set_value(value);
+ }
+ catch (const std::system_error& exc)
+ {
+ std::cerr << "Error set_value: " << exc.what() << "\n";
+ }
+ }
+ }
+
+ unsigned int setupTimeMs;
+
+ private:
+ gpiod::line line;
+};
+
+class ADCSensor : public Sensor, public std::enable_shared_from_this<ADCSensor>
+{
+ public:
+ ADCSensor(const std::string& path,
+ sdbusplus::asio::object_server& objectServer,
+ std::shared_ptr<sdbusplus::asio::connection>& conn,
+ boost::asio::io_service& io, const std::string& sensorName,
+ std::vector<thresholds::Threshold>&& thresholds,
+ double scaleFactor, float pollRate, PowerState readState,
+ const std::string& sensorConfiguration,
+ std::optional<BridgeGpio>&& bridgeGpio);
+ ~ADCSensor() override;
+ void setupRead(void);
+
+ private:
+ sdbusplus::asio::object_server& objServer;
+ boost::asio::posix::stream_descriptor inputDev;
+ boost::asio::steady_timer waitTimer;
+ std::shared_ptr<boost::asio::streambuf> readBuf;
+ std::string path;
+ double scaleFactor;
+ unsigned int sensorPollMs;
+ std::optional<BridgeGpio> bridgeGpio;
+ thresholds::ThresholdTimer thresholdTimer;
+ void handleResponse(const boost::system::error_code& err);
+ void checkThresholds(void) override;
+};
diff --git a/src/ADCSensorMain.cpp b/src/ADCSensorMain.cpp
index 3cbb5f6..f184d23 100644
--- a/src/ADCSensorMain.cpp
+++ b/src/ADCSensorMain.cpp
@@ -14,9 +14,10 @@
// limitations under the License.
*/
-#include <ADCSensor.hpp>
-#include <Utils.hpp>
-#include <VariantVisitors.hpp>
+#include "ADCSensor.hpp"
+#include "Utils.hpp"
+#include "VariantVisitors.hpp"
+
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/container/flat_set.hpp>
diff --git a/src/ChassisIntrusionSensor.cpp b/src/ChassisIntrusionSensor.cpp
index 2bb04ce..f7e7783 100644
--- a/src/ChassisIntrusionSensor.cpp
+++ b/src/ChassisIntrusionSensor.cpp
@@ -14,11 +14,12 @@
// limitations under the License.
*/
+#include "ChassisIntrusionSensor.hpp"
+
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
-#include <ChassisIntrusionSensor.hpp>
#include <boost/asio/io_service.hpp>
#include <sdbusplus/asio/object_server.hpp>
diff --git a/src/ChassisIntrusionSensor.hpp b/src/ChassisIntrusionSensor.hpp
new file mode 100644
index 0000000..8954033
--- /dev/null
+++ b/src/ChassisIntrusionSensor.hpp
@@ -0,0 +1,63 @@
+#pragma once
+
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <gpiod.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <memory>
+#include <string>
+
+enum IntrusionSensorType
+{
+ pch,
+ gpio
+};
+
+class ChassisIntrusionSensor
+{
+ public:
+ ChassisIntrusionSensor(
+ boost::asio::io_service& io,
+ std::shared_ptr<sdbusplus::asio::dbus_interface> iface);
+
+ ~ChassisIntrusionSensor();
+
+ void start(IntrusionSensorType type, int busId, int slaveAddr,
+ bool gpioInverted);
+
+ private:
+ std::shared_ptr<sdbusplus::asio::dbus_interface> mIface;
+ std::shared_ptr<sdbusplus::asio::connection> mDbusConn;
+
+ IntrusionSensorType mType{IntrusionSensorType::gpio};
+
+ // intrusion status. 0: not intruded, 1: intruded
+ std::string mValue = "unknown";
+ std::string mOldValue = "unknown";
+
+ // valid if it is PCH register via i2c
+ int mBusId{-1};
+ int mSlaveAddr{-1};
+ boost::asio::steady_timer mPollTimer;
+
+ // valid if it is via GPIO
+ bool mGpioInverted{false};
+ std::string mPinName = "CHASSIS_INTRUSION";
+ gpiod::line mGpioLine;
+ boost::asio::posix::stream_descriptor mGpioFd;
+
+ // common members
+ bool mOverridenState = false;
+ bool mInternalSet = false;
+
+ bool mInitialized = false;
+
+ void updateValue(const std::string& newValue);
+ static int i2cReadFromPch(int busId, int slaveAddr);
+ void pollSensorStatusByPch();
+ void readGpio();
+ void pollSensorStatusByGpio();
+ void initGpioDeviceFile();
+ int setSensorValue(const std::string& req, std::string& propertyValue);
+};
diff --git a/src/DeviceMgmt.cpp b/src/DeviceMgmt.cpp
index f137908..ffc0376 100644
--- a/src/DeviceMgmt.cpp
+++ b/src/DeviceMgmt.cpp
@@ -1,4 +1,4 @@
-#include <DeviceMgmt.hpp>
+#include "DeviceMgmt.hpp"
#include <filesystem>
#include <fstream>
diff --git a/src/DeviceMgmt.hpp b/src/DeviceMgmt.hpp
new file mode 100644
index 0000000..07b28b5
--- /dev/null
+++ b/src/DeviceMgmt.hpp
@@ -0,0 +1,56 @@
+#pragma once
+
+#include "Utils.hpp"
+
+#include <boost/container/flat_map.hpp>
+
+#include <functional>
+#include <optional>
+#include <string_view>
+
+struct I2CDeviceType
+{
+ const char* name;
+ bool createsHWMon;
+};
+
+using I2CDeviceTypeMap =
+ boost::container::flat_map<std::string, I2CDeviceType, std::less<>>;
+
+struct I2CDeviceParams
+{
+ I2CDeviceParams(const I2CDeviceType& type, uint64_t bus, uint64_t address) :
+ type(&type), bus(bus), address(address){};
+
+ const I2CDeviceType* type;
+ uint64_t bus;
+ uint64_t address;
+
+ bool devicePresent(void) const;
+ bool deviceStatic(void) const;
+};
+
+std::optional<I2CDeviceParams>
+ getI2CDeviceParams(const I2CDeviceTypeMap& dtmap,
+ const SensorBaseConfigMap& cfg);
+
+class I2CDevice
+{
+ public:
+ explicit I2CDevice(I2CDeviceParams params);
+ ~I2CDevice();
+
+ private:
+ I2CDeviceParams params;
+
+ int create(void) const;
+ int destroy(void) const;
+};
+
+// HACK: this declaration "should" live in Utils.hpp, but that leads to a
+// tangle of header-dependency hell because each header needs types declared
+// in the other.
+std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
+ setupPropertiesChangedMatches(
+ sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap,
+ const std::function<void(sdbusplus::message_t&)>& handler);
diff --git a/src/ExitAirTempSensor.cpp b/src/ExitAirTempSensor.cpp
index 7f947e5..d126def 100644
--- a/src/ExitAirTempSensor.cpp
+++ b/src/ExitAirTempSensor.cpp
@@ -14,9 +14,11 @@
// limitations under the License.
*/
-#include <ExitAirTempSensor.hpp>
-#include <Utils.hpp>
-#include <VariantVisitors.hpp>
+#include "ExitAirTempSensor.hpp"
+
+#include "Utils.hpp"
+#include "VariantVisitors.hpp"
+
#include <boost/algorithm/string/replace.hpp>
#include <boost/container/flat_map.hpp>
#include <sdbusplus/asio/connection.hpp>
diff --git a/src/ExitAirTempSensor.hpp b/src/ExitAirTempSensor.hpp
new file mode 100644
index 0000000..5bd9735
--- /dev/null
+++ b/src/ExitAirTempSensor.hpp
@@ -0,0 +1,84 @@
+#pragma once
+#include <boost/container/flat_map.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <sensor.hpp>
+
+#include <chrono>
+#include <limits>
+#include <memory>
+#include <string>
+#include <vector>
+
+struct ExitAirTempSensor;
+struct CFMSensor : public Sensor, std::enable_shared_from_this<CFMSensor>
+{
+ std::vector<std::string> tachs;
+ double c1 = 0.0;
+ double c2 = 0.0;
+ double maxCFM = 0.0;
+ double tachMinPercent = 0.0;
+ double tachMaxPercent = 0.0;
+
+ std::shared_ptr<ExitAirTempSensor> parent;
+
+ CFMSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
+ const std::string& name, const std::string& sensorConfiguration,
+ sdbusplus::asio::object_server& objectServer,
+ std::vector<thresholds::Threshold>&& thresholdData,
+ std::shared_ptr<ExitAirTempSensor>& parent);
+ ~CFMSensor() override;
+
+ bool calculate(double& /*value*/);
+ void updateReading(void);
+ void setupMatches(void);
+ void createMaxCFMIface(void);
+ void addTachRanges(const std::string& serviceName, const std::string& path);
+ void checkThresholds(void) override;
+ uint64_t getMaxRpm(uint64_t cfmMax) const;
+
+ private:
+ std::vector<sdbusplus::bus::match_t> matches;
+ boost::container::flat_map<std::string, double> tachReadings;
+ boost::container::flat_map<std::string, std::pair<double, double>>
+ tachRanges;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> pwmLimitIface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> cfmLimitIface;
+ sdbusplus::asio::object_server& objServer;
+};
+
+struct ExitAirTempSensor :
+ public Sensor,
+ std::enable_shared_from_this<ExitAirTempSensor>
+{
+
+ double powerFactorMin = 0.0;
+ double powerFactorMax = 0.0;
+ double qMin = 0.0;
+ double qMax = 0.0;
+ double alphaS = 0.0;
+ double alphaF = 0.0;
+ double pOffset = 0.0;
+
+ ExitAirTempSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
+ const std::string& name,
+ const std::string& sensorConfiguration,
+ sdbusplus::asio::object_server& objectServer,
+ std::vector<thresholds::Threshold>&& thresholdData);
+ ~ExitAirTempSensor() override;
+
+ void checkThresholds(void) override;
+ void updateReading(void);
+ void setupMatches(void);
+
+ private:
+ double lastReading = 0.0;
+
+ std::vector<sdbusplus::bus::match_t> matches;
+ double inletTemp = std::numeric_limits<double>::quiet_NaN();
+ boost::container::flat_map<std::string, double> powerReadings;
+
+ sdbusplus::asio::object_server& objServer;
+ std::chrono::time_point<std::chrono::steady_clock> lastTime;
+ static double getTotalCFM(void);
+ bool calculate(double& val);
+};
diff --git a/src/ExternalSensor.hpp b/src/ExternalSensor.hpp
new file mode 100644
index 0000000..20dd2f0
--- /dev/null
+++ b/src/ExternalSensor.hpp
@@ -0,0 +1,66 @@
+#pragma once
+
+#include "Thresholds.hpp"
+#include "sensor.hpp"
+
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <chrono>
+#include <string>
+#include <vector>
+
+class ExternalSensor :
+ public Sensor,
+ public std::enable_shared_from_this<ExternalSensor>
+{
+ public:
+ ExternalSensor(const std::string& objectType,
+ sdbusplus::asio::object_server& objectServer,
+ std::shared_ptr<sdbusplus::asio::connection>& conn,
+ const std::string& sensorName,
+ const std::string& sensorUnits,
+ std::vector<thresholds::Threshold>&& thresholdsIn,
+ const std::string& sensorConfiguration, double maxReading,
+ double minReading, double timeoutSecs,
+ const PowerState& powerState);
+ ~ExternalSensor() override;
+
+ // Call this immediately after calling the constructor
+ void initWriteHook(
+ std::function<void(std::chrono::steady_clock::time_point now)>&&
+ writeHookIn);
+
+ // Returns true if sensor has external Value that is subject to timeout
+ bool isAliveAndPerishable(void) const;
+
+ // Returns true if AliveAndPerishable and timeout has not yet happened
+ bool
+ isAliveAndFresh(const std::chrono::steady_clock::time_point& now) const;
+
+ // Marks the time when Value successfully received from external source
+ void writeBegin(const std::chrono::steady_clock::time_point& now);
+
+ // Marks sensor as timed out, replacing Value with floating-point "NaN"
+ void writeInvalidate(void);
+
+ // Returns amount of time elapsed since last writeBegin() happened
+ std::chrono::steady_clock::duration
+ ageElapsed(const std::chrono::steady_clock::time_point& now) const;
+
+ // Returns amount of time remaining until sensor timeout will happen
+ std::chrono::steady_clock::duration
+ ageRemaining(const std::chrono::steady_clock::time_point& now) const;
+
+ private:
+ sdbusplus::asio::object_server& objServer;
+
+ std::chrono::steady_clock::time_point writeLast;
+ std::chrono::steady_clock::duration writeTimeout;
+ bool writeAlive{false};
+ bool writePerishable;
+ std::function<void(const std::chrono::steady_clock::time_point& now)>
+ writeHook;
+
+ void checkThresholds(void) override;
+ void externalSetTrigger(void);
+};
diff --git a/src/FanMain.cpp b/src/FanMain.cpp
index 7260131..2c51396 100644
--- a/src/FanMain.cpp
+++ b/src/FanMain.cpp
@@ -14,10 +14,11 @@
// limitations under the License.
*/
-#include <PwmSensor.hpp>
-#include <TachSensor.hpp>
-#include <Utils.hpp>
-#include <VariantVisitors.hpp>
+#include "PwmSensor.hpp"
+#include "TachSensor.hpp"
+#include "Utils.hpp"
+#include "VariantVisitors.hpp"
+
#include <boost/algorithm/string/replace.hpp>
#include <boost/container/flat_map.hpp>
#include <boost/container/flat_set.hpp>
diff --git a/src/FileHandle.cpp b/src/FileHandle.cpp
index af32192..227ce23 100644
--- a/src/FileHandle.cpp
+++ b/src/FileHandle.cpp
@@ -1,8 +1,8 @@
+#include "FileHandle.hpp"
+
#include <fcntl.h>
#include <unistd.h>
-#include <FileHandle.hpp>
-
#include <iostream>
#include <stdexcept>
diff --git a/src/FileHandle.hpp b/src/FileHandle.hpp
new file mode 100644
index 0000000..a2a75d8
--- /dev/null
+++ b/src/FileHandle.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <filesystem>
+#include <ios>
+
+// An RAII compliant object for holding a posix file descriptor
+class FileHandle
+{
+ private:
+ int fd;
+
+ public:
+ FileHandle() = delete;
+ FileHandle(const FileHandle&) = delete;
+ FileHandle& operator=(const FileHandle&) = delete;
+ FileHandle(FileHandle&& /*in*/) noexcept;
+ FileHandle& operator=(FileHandle&& /*in*/) noexcept;
+
+ explicit FileHandle(const std::filesystem::path& name,
+ std::ios_base::openmode mode = std::ios_base::in |
+ std::ios_base::out);
+
+ explicit FileHandle(int fd);
+
+ ~FileHandle();
+ int handle() const;
+};
\ No newline at end of file
diff --git a/src/HwmonTempMain.cpp b/src/HwmonTempMain.cpp
index e777c42..7ac03a3 100644
--- a/src/HwmonTempMain.cpp
+++ b/src/HwmonTempMain.cpp
@@ -14,9 +14,10 @@
// limitations under the License.
*/
-#include <DeviceMgmt.hpp>
-#include <HwmonTempSensor.hpp>
-#include <Utils.hpp>
+#include "DeviceMgmt.hpp"
+#include "HwmonTempSensor.hpp"
+#include "Utils.hpp"
+
#include <boost/algorithm/string/replace.hpp>
#include <boost/container/flat_map.hpp>
#include <boost/container/flat_set.hpp>
diff --git a/src/HwmonTempSensor.cpp b/src/HwmonTempSensor.cpp
index 144f9d9..b4042ff 100644
--- a/src/HwmonTempSensor.cpp
+++ b/src/HwmonTempSensor.cpp
@@ -14,9 +14,10 @@
// limitations under the License.
*/
+#include "HwmonTempSensor.hpp"
+
#include <unistd.h>
-#include <HwmonTempSensor.hpp>
#include <boost/asio/read_until.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
diff --git a/src/HwmonTempSensor.hpp b/src/HwmonTempSensor.hpp
new file mode 100644
index 0000000..27ffccd
--- /dev/null
+++ b/src/HwmonTempSensor.hpp
@@ -0,0 +1,65 @@
+#pragma once
+
+#include "DeviceMgmt.hpp"
+#include "Thresholds.hpp"
+#include "sensor.hpp"
+
+#include <boost/asio/random_access_file.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <string>
+#include <vector>
+
+struct SensorParams
+{
+ double minValue;
+ double maxValue;
+ double offsetValue;
+ double scaleValue;
+ std::string units;
+ std::string typeName;
+};
+
+class HwmonTempSensor :
+ public Sensor,
+ public std::enable_shared_from_this<HwmonTempSensor>
+{
+ public:
+ HwmonTempSensor(const std::string& path, const std::string& objectType,
+ sdbusplus::asio::object_server& objectServer,
+ std::shared_ptr<sdbusplus::asio::connection>& conn,
+ boost::asio::io_service& io, const std::string& sensorName,
+ std::vector<thresholds::Threshold>&& thresholds,
+ const struct SensorParams& thisSensorParameters,
+ float pollRate, const std::string& sensorConfiguration,
+ PowerState powerState,
+ const std::shared_ptr<I2CDevice>& i2cDevice);
+ ~HwmonTempSensor() override;
+ void setupRead(void);
+ void activate(const std::string& newPath,
+ const std::shared_ptr<I2CDevice>& newI2CDevice);
+ void deactivate(void);
+ bool isActive(void);
+
+ std::shared_ptr<I2CDevice> getI2CDevice() const
+ {
+ return i2cDevice;
+ }
+
+ private:
+ // Ordering is important here; readBuf is first so that it's not destroyed
+ // while async operations from other member fields might still be using it.
+ std::array<char, 128> readBuf{};
+ std::shared_ptr<I2CDevice> i2cDevice;
+ sdbusplus::asio::object_server& objServer;
+ boost::asio::random_access_file inputDev;
+ boost::asio::steady_timer waitTimer;
+ std::string path;
+ double offsetValue;
+ double scaleValue;
+ unsigned int sensorPollMs;
+
+ void handleResponse(const boost::system::error_code& err, size_t bytesRead);
+ void restartRead();
+ void checkThresholds(void) override;
+};
diff --git a/src/IntelCPUSensor.cpp b/src/IntelCPUSensor.cpp
index 3410574..2995d60 100644
--- a/src/IntelCPUSensor.cpp
+++ b/src/IntelCPUSensor.cpp
@@ -14,10 +14,12 @@
// limitations under the License.
*/
+#include "IntelCPUSensor.hpp"
+
+#include "Utils.hpp"
+
#include <unistd.h>
-#include <IntelCPUSensor.hpp>
-#include <Utils.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/asio/read_until.hpp>
#include <sdbusplus/asio/connection.hpp>
diff --git a/src/IntelCPUSensor.hpp b/src/IntelCPUSensor.hpp
new file mode 100644
index 0000000..399e765
--- /dev/null
+++ b/src/IntelCPUSensor.hpp
@@ -0,0 +1,116 @@
+#pragma once
+
+#include "Thresholds.hpp"
+#include "Utils.hpp"
+
+#include <boost/asio/streambuf.hpp>
+#include <boost/container/flat_map.hpp>
+#include <gpiod.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sensor.hpp>
+
+#include <filesystem>
+#include <fstream>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <variant>
+#include <vector>
+
+class IntelCPUSensor :
+ public Sensor,
+ public std::enable_shared_from_this<IntelCPUSensor>
+{
+ public:
+ IntelCPUSensor(const std::string& path, const std::string& objectType,
+ sdbusplus::asio::object_server& objectServer,
+ std::shared_ptr<sdbusplus::asio::connection>& conn,
+ boost::asio::io_service& io, const std::string& sensorName,
+ std::vector<thresholds::Threshold>&& thresholds,
+ const std::string& configuration, int cpuId, bool show,
+ double dtsOffset);
+ ~IntelCPUSensor() override;
+ static constexpr unsigned int sensorScaleFactor = 1000;
+ static constexpr unsigned int sensorPollMs = 1000;
+ static constexpr size_t warnAfterErrorCount = 10;
+ static constexpr const char* labelTcontrol = "Tcontrol";
+ void setupRead(void);
+
+ private:
+ sdbusplus::asio::object_server& objServer;
+ boost::asio::streambuf readBuf;
+ boost::asio::posix::stream_descriptor inputDev;
+ boost::asio::steady_timer waitTimer;
+ std::string nameTcontrol;
+ std::string path;
+ double privTcontrol;
+ double dtsOffset;
+ bool show;
+ size_t pollTime;
+ bool loggedInterfaceDown = false;
+ uint8_t minMaxReadCounter{0};
+ int fd{};
+ void handleResponse(const boost::system::error_code& err);
+ void checkThresholds(void) override;
+ void updateMinMaxValues(void);
+ void restartRead(void);
+};
+
+extern boost::container::flat_map<std::string, std::shared_ptr<IntelCPUSensor>>
+ gCpuSensors;
+
+// this is added to intelcpusensor.hpp to avoid having every sensor have to link
+// against libgpiod, if another sensor needs it we may move it to utils
+inline bool cpuIsPresent(const SensorBaseConfigMap& gpioConfig)
+{
+ static boost::container::flat_map<std::string, bool> cpuPresence;
+
+ auto findName = gpioConfig.find("Name");
+ if (findName == gpioConfig.end())
+ {
+ return false;
+ }
+ std::string gpioName =
+ std::visit(VariantToStringVisitor(), findName->second);
+
+ auto findIndex = cpuPresence.find(gpioName);
+ if (findIndex != cpuPresence.end())
+ {
+ return findIndex->second;
+ }
+
+ bool activeHigh = true;
+ auto findPolarity = gpioConfig.find("Polarity");
+ if (findPolarity != gpioConfig.end())
+ {
+ if (std::string("Low") ==
+ std::visit(VariantToStringVisitor(), findPolarity->second))
+ {
+ activeHigh = false;
+ }
+ }
+
+ auto line = gpiod::find_line(gpioName);
+ if (!line)
+ {
+ std::cerr << "Error requesting gpio: " << gpioName << "\n";
+ return false;
+ }
+
+ bool resp = false;
+ try
+ {
+ line.request({"cpusensor", gpiod::line_request::DIRECTION_INPUT,
+ activeHigh ? 0 : gpiod::line_request::FLAG_ACTIVE_LOW});
+ resp = (line.get_value() != 0);
+ }
+ catch (const std::system_error&)
+ {
+ std::cerr << "Error reading gpio: " << gpioName << "\n";
+ return false;
+ }
+
+ cpuPresence[gpioName] = resp;
+
+ return resp;
+}
diff --git a/src/IntelCPUSensorMain.cpp b/src/IntelCPUSensorMain.cpp
index aa06628..ecc36dc 100644
--- a/src/IntelCPUSensorMain.cpp
+++ b/src/IntelCPUSensorMain.cpp
@@ -14,11 +14,12 @@
// limitations under the License.
*/
+#include "IntelCPUSensor.hpp"
+#include "Utils.hpp"
+#include "VariantVisitors.hpp"
+
#include <fcntl.h>
-#include <IntelCPUSensor.hpp>
-#include <Utils.hpp>
-#include <VariantVisitors.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/container/flat_map.hpp>
#include <boost/container/flat_set.hpp>
diff --git a/src/IntrusionSensorMain.cpp b/src/IntrusionSensorMain.cpp
index 3b63b6e..fb8510c 100644
--- a/src/IntrusionSensorMain.cpp
+++ b/src/IntrusionSensorMain.cpp
@@ -14,8 +14,9 @@
// limitations under the License.
*/
-#include <ChassisIntrusionSensor.hpp>
-#include <Utils.hpp>
+#include "ChassisIntrusionSensor.hpp"
+#include "Utils.hpp"
+
#include <boost/asio/io_service.hpp>
#include <boost/container/flat_map.hpp>
#include <phosphor-logging/lg2.hpp>
diff --git a/src/IpmbSDRSensor.cpp b/src/IpmbSDRSensor.cpp
index 1470340..73cfb43 100644
--- a/src/IpmbSDRSensor.cpp
+++ b/src/IpmbSDRSensor.cpp
@@ -1,4 +1,4 @@
-#include <IpmbSDRSensor.hpp>
+#include "IpmbSDRSensor.hpp"
const constexpr char* ipmbService = "xyz.openbmc_project.Ipmi.Channel.Ipmb";
const constexpr char* ipmbDbusPath = "/xyz/openbmc_project/Ipmi/Channel/Ipmb";
diff --git a/src/IpmbSDRSensor.hpp b/src/IpmbSDRSensor.hpp
new file mode 100644
index 0000000..4284e7a
--- /dev/null
+++ b/src/IpmbSDRSensor.hpp
@@ -0,0 +1,118 @@
+#pragma once
+
+#include <sensor.hpp>
+
+using IpmbMethodType =
+ std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>;
+
+enum class SDRType
+{
+ sdrType01 = 1,
+ sdrType02 = 2,
+ sdrType03 = 3
+};
+
+namespace sdr
+{
+// IPMB Commands
+static constexpr uint8_t netfnStorageReq = 0x0a;
+static constexpr uint8_t cmdStorageGetSdrInfo = 0x20;
+static constexpr uint8_t cmdStorageReserveSdr = 0x22;
+static constexpr uint8_t cmdStorageGetSdr = 0x23;
+
+// Get SDR Commands
+static constexpr uint8_t sdrNxtRecLSB = 0;
+static constexpr uint8_t sdrNxtRecMSB = 1;
+static constexpr uint8_t perCountByte = 16;
+
+// Sensor Record Bytes
+static constexpr uint8_t sdrType = 5;
+static constexpr uint8_t dataLengthByte = 6;
+static constexpr uint8_t sdrSensorNum = 9;
+
+} // namespace sdr
+
+namespace sdrtype01
+{
+// Negative Handle Commands
+static constexpr uint8_t maxPosReadingMargin = 127;
+static constexpr uint8_t twosCompVal = 128;
+static constexpr double thermalConst = 256;
+
+static constexpr uint8_t sdrSensNoThres = 0;
+static constexpr uint8_t sensorCapability = 13;
+static constexpr uint8_t sdrNegHandle = 24;
+static constexpr uint8_t sdrUnitType = 25;
+static constexpr uint8_t sdrLinearByte = 27;
+
+// SDR Type 1 Thresholds Commands
+static constexpr uint8_t mDataByte = 28;
+static constexpr uint8_t mTolDataByte = 29;
+static constexpr uint8_t bDataByte = 30;
+static constexpr uint8_t bAcuDataByte = 31;
+static constexpr uint8_t rbExpDataByte = 33;
+static constexpr uint8_t upperCriticalThreshold = 43;
+static constexpr uint8_t lowerCriticalThreshold = 46;
+static constexpr uint8_t nameLengthByte = 53;
+
+} // namespace sdrtype01
+
+struct SensorInfo
+{
+ std::string sensorReadName;
+ uint8_t sensorUnit = 0;
+ double thresUpperCri = 0;
+ double thresLowerCri = 0;
+ uint8_t sensorNumber = 0;
+ uint8_t sensCap = 0;
+};
+
+struct SensorValConversion
+{
+ uint16_t mValue = 0;
+ double bValue = 0;
+ double expoVal = 0;
+ uint8_t negRead = 0;
+};
+
+inline std::map<int, std::vector<SensorInfo>> sensorRecord;
+inline std::map<int, std::map<uint8_t, SensorValConversion>> sensorValRecord;
+
+class IpmbSDRDevice : public std::enable_shared_from_this<IpmbSDRDevice>
+{
+ public:
+ IpmbSDRDevice(std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+ uint8_t cmdAddr);
+
+ uint8_t commandAddress = 0;
+ int hostIndex = 0;
+
+ std::shared_ptr<sdbusplus::asio::connection> conn;
+
+ std::vector<uint8_t> sdrData;
+ uint16_t validRecordCount = 1;
+ uint8_t iCnt = 0;
+ uint8_t nextRecordIDLSB = 0;
+ uint8_t nextRecordIDMSB = 0;
+
+ std::vector<uint8_t> sdrCommandData = {};
+
+ void getSDRRepositoryInfo();
+
+ void reserveSDRRepository(uint16_t recordCount);
+
+ void getSDRSensorData(uint16_t recordCount, uint8_t resrvIDLSB,
+ uint8_t resrvIDMSB);
+
+ void handleSDRData(const std::vector<uint8_t>& data, uint16_t recordCount,
+ uint8_t resrvIDLSB, uint8_t resrvIDMSB);
+
+ void checkSDRData(std::vector<uint8_t>& sdrDataBytes,
+ uint8_t dataLength) const;
+
+ static void checkSDRType01Threshold(std::vector<uint8_t>& sdrDataBytes,
+ int busIndex, std::string tempName);
+
+ inline static double sensorValCalculation(uint16_t mValue, double bValue,
+ double expValue, double value);
+};
diff --git a/src/IpmbSensor.cpp b/src/IpmbSensor.cpp
index ba39214..9bc884e 100644
--- a/src/IpmbSensor.cpp
+++ b/src/IpmbSensor.cpp
@@ -14,10 +14,12 @@
// limitations under the License.
*/
-#include <IpmbSDRSensor.hpp>
-#include <IpmbSensor.hpp>
-#include <Utils.hpp>
-#include <VariantVisitors.hpp>
+#include "IpmbSensor.hpp"
+
+#include "IpmbSDRSensor.hpp"
+#include "Utils.hpp"
+#include "VariantVisitors.hpp"
+
#include <boost/container/flat_map.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
diff --git a/src/IpmbSensor.hpp b/src/IpmbSensor.hpp
new file mode 100644
index 0000000..6467b57
--- /dev/null
+++ b/src/IpmbSensor.hpp
@@ -0,0 +1,128 @@
+#pragma once
+#include <boost/asio/steady_timer.hpp>
+#include <boost/container/flat_map.hpp>
+#include <sensor.hpp>
+
+#include <chrono>
+#include <limits>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+enum class IpmbType
+{
+ none,
+ meSensor,
+ PXE1410CVR,
+ IR38363VR,
+ ADM1278HSC,
+ mpsVR
+};
+
+enum class IpmbSubType
+{
+ none,
+ temp,
+ curr,
+ power,
+ volt,
+ util
+};
+
+enum class ReadingFormat
+{
+ byte0,
+ byte3,
+ elevenBit,
+ elevenBitShift,
+ linearElevenBit
+};
+
+namespace ipmi
+{
+namespace sensor
+{
+constexpr uint8_t netFn = 0x04;
+constexpr uint8_t getSensorReading = 0x2d;
+
+static bool isValid(const std::vector<uint8_t>& data)
+{
+ constexpr auto readingUnavailableBit = 5;
+
+ // Proper 'Get Sensor Reading' response has at least 4 bytes, including
+ // Completion Code. Our IPMB stack strips Completion Code from payload so we
+ // compare here against the rest of payload
+ if (data.size() < 3)
+ {
+ return false;
+ }
+
+ // Per IPMI 'Get Sensor Reading' specification
+ if ((data[1] & (1 << readingUnavailableBit)) != 0)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace sensor
+namespace me_bridge
+{
+constexpr uint8_t netFn = 0x2e;
+constexpr uint8_t sendRawPmbus = 0xd9;
+} // namespace me_bridge
+} // namespace ipmi
+
+using IpmbMethodType =
+ std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>;
+
+struct IpmbSensor :
+ public Sensor,
+ public std::enable_shared_from_this<IpmbSensor>
+{
+ IpmbSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
+ boost::asio::io_service& io, const std::string& name,
+ const std::string& sensorConfiguration,
+ sdbusplus::asio::object_server& objectServer,
+ std::vector<thresholds::Threshold>&& thresholdData,
+ uint8_t deviceAddress, uint8_t hostSMbusIndex, float pollRate,
+ std::string& sensorTypeName);
+ ~IpmbSensor() override;
+
+ void checkThresholds(void) override;
+ void read(void);
+ void init(void);
+ std::string getSubTypeUnits(void) const;
+ void loadDefaults(void);
+ void runInitCmd(void);
+ bool processReading(const std::vector<uint8_t>& data, double& resp);
+ void parseConfigValues(const SensorBaseConfigMap& entry);
+ bool sensorClassType(const std::string& sensorClass);
+ void sensorSubType(const std::string& sensorTypeName);
+
+ IpmbType type = IpmbType::none;
+ IpmbSubType subType = IpmbSubType::none;
+ double scaleVal = 1.0;
+ double offsetVal = 0.0;
+ uint8_t commandAddress = 0;
+ uint8_t netfn = 0;
+ uint8_t command = 0;
+ uint8_t deviceAddress = 0;
+ uint8_t errorCount = 0;
+ uint8_t hostSMbusIndex = 0;
+ std::vector<uint8_t> commandData;
+ std::optional<uint8_t> initCommand;
+ std::vector<uint8_t> initData;
+ int sensorPollMs;
+
+ ReadingFormat readingFormat = ReadingFormat::byte0;
+
+ private:
+ void sendIpmbRequest(void);
+ sdbusplus::asio::object_server& objectServer;
+ boost::asio::steady_timer waitTimer;
+ void ipmbRequestCompletionCb(const boost::system::error_code& ec,
+ const IpmbMethodType& response);
+};
diff --git a/src/MCUTempSensor.cpp b/src/MCUTempSensor.cpp
index f3ada3c..a5d964d 100644
--- a/src/MCUTempSensor.cpp
+++ b/src/MCUTempSensor.cpp
@@ -13,9 +13,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
*/
-#include <MCUTempSensor.hpp>
-#include <Utils.hpp>
-#include <VariantVisitors.hpp>
+
+#include "MCUTempSensor.hpp"
+
+#include "Utils.hpp"
+#include "VariantVisitors.hpp"
+
#include <boost/container/flat_map.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
diff --git a/src/MCUTempSensor.hpp b/src/MCUTempSensor.hpp
new file mode 100644
index 0000000..c4c2406
--- /dev/null
+++ b/src/MCUTempSensor.hpp
@@ -0,0 +1,34 @@
+#pragma once
+#include <boost/asio/steady_timer.hpp>
+#include <boost/container/flat_map.hpp>
+#include <sensor.hpp>
+
+#include <chrono>
+#include <limits>
+#include <memory>
+#include <string>
+#include <vector>
+
+struct MCUTempSensor : public Sensor
+{
+ MCUTempSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
+ boost::asio::io_service& io, const std::string& name,
+ const std::string& sensorConfiguration,
+ sdbusplus::asio::object_server& objectServer,
+ std::vector<thresholds::Threshold>&& thresholdData,
+ uint8_t busId, uint8_t mcuAddress, uint8_t tempReg);
+ ~MCUTempSensor() override;
+
+ void checkThresholds(void) override;
+ void read(void);
+ void init(void);
+
+ uint8_t busId;
+ uint8_t mcuAddress;
+ uint8_t tempReg;
+
+ private:
+ int getMCURegsInfoWord(uint8_t regs, int16_t* pu16data) const;
+ sdbusplus::asio::object_server& objectServer;
+ boost::asio::steady_timer waitTimer;
+};
diff --git a/src/NVMeBasicContext.hpp b/src/NVMeBasicContext.hpp
new file mode 100644
index 0000000..b17fc05
--- /dev/null
+++ b/src/NVMeBasicContext.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "NVMeContext.hpp"
+
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/posix/stream_descriptor.hpp>
+
+#include <thread>
+
+class NVMeBasicContext : public NVMeContext
+{
+ public:
+ NVMeBasicContext(boost::asio::io_service& io, int rootBus);
+ ~NVMeBasicContext() override = default;
+ void pollNVMeDevices() override;
+ void readAndProcessNVMeSensor() override;
+ void processResponse(std::shared_ptr<NVMeSensor>& sensor, void* msg,
+ size_t len) override;
+
+ private:
+ NVMeBasicContext(boost::asio::io_service& io, int rootBus, int cmdOut,
+ int streamIn, int streamOut, int cmdIn);
+ boost::asio::io_service& io;
+
+ // The IO thread must be destructed after the stream descriptors, so
+ // initialise it first. http://eel.is/c++draft/class.base.init#note-6
+ //
+ // Providing a stop-source to the thread execution function isn't
+ // particularly useful as it will spend most of its time blocked in a system
+ // call - ioctl() for the actual device communication, or read() and write()
+ // on the pipes associated with reqStream and respStream. Rather than trying
+ // to force a stop, rely on read()/write() failures from closed pipes to
+ // coerce it to exit and thus allow completion of the join().
+ std::jthread thread;
+
+ // Destruction of the stream descriptors has the effect of issuing cancel(),
+ // destroying the closure of the callback where we might be carrying
+ // weak_ptrs to `this`.
+ // https://www.boost.org/doc/libs/1_79_0/doc/html/boost_asio/reference/posix__basic_descriptor/_basic_descriptor.html
+ boost::asio::posix::stream_descriptor reqStream;
+ boost::asio::posix::stream_descriptor respStream;
+
+ enum
+ {
+ NVME_MI_BASIC_SFLGS_DRIVE_NOT_READY = 0x40,
+ NVME_MI_BASIC_SFLGS_DRIVE_FUNCTIONAL = 0x20,
+ };
+};
diff --git a/src/NVMeContext.hpp b/src/NVMeContext.hpp
new file mode 100644
index 0000000..5be03e0
--- /dev/null
+++ b/src/NVMeContext.hpp
@@ -0,0 +1,109 @@
+#pragma once
+
+#include "NVMeSensor.hpp"
+
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/steady_timer.hpp>
+
+#include <memory>
+#include <stdexcept>
+
+class NVMeContext : public std::enable_shared_from_this<NVMeContext>
+{
+ public:
+ NVMeContext(boost::asio::io_service& io, int rootBus) :
+ scanTimer(io), rootBus(rootBus), pollCursor(sensors.end())
+ {
+ if (rootBus < 0)
+ {
+ throw std::invalid_argument(
+ "Invalid root bus: Bus ID must not be negative");
+ }
+ }
+
+ virtual ~NVMeContext()
+ {
+ close();
+ }
+
+ void addSensor(const std::shared_ptr<NVMeSensor>& sensor)
+ {
+ sensors.emplace_back(sensor);
+ }
+
+ std::optional<std::shared_ptr<NVMeSensor>>
+ getSensorAtPath(const std::string& path)
+ {
+ for (auto& sensor : sensors)
+ {
+ if (sensor->configurationPath == path)
+ {
+ return sensor;
+ }
+ }
+
+ return std::nullopt;
+ }
+
+ // Post-condition: The sensor list does not contain the provided sensor
+ // Post-condition: pollCursor is a valid iterator for the sensor list
+ void removeSensor(const std::shared_ptr<NVMeSensor>& sensor)
+ {
+ // Locate the sensor that we're removing in the sensor list
+ auto found = std::find(sensors.begin(), sensors.end(), sensor);
+
+ // If we failed to find the sensor in the list the post-condition is
+ // already satisfied
+ if (found == sensors.end())
+ {
+ return;
+ }
+
+ // We've found the sensor in the list
+
+ // If we're not actively polling the sensor list, then remove the sensor
+ if (pollCursor == sensors.end())
+ {
+ sensors.erase(found);
+ return;
+ }
+
+ // We're actively polling the sensor list
+
+ // If we're not polling the specific sensor that has been removed, then
+ // remove the sensor
+ if (*pollCursor != *found)
+ {
+ sensors.erase(found);
+ return;
+ }
+
+ // We're polling the sensor that is being removed
+
+ // Remove the sensor and update the poll cursor so the cursor remains
+ // valid
+ pollCursor = sensors.erase(found);
+ }
+
+ virtual void close()
+ {
+ scanTimer.cancel();
+ }
+
+ virtual void pollNVMeDevices() = 0;
+
+ virtual void readAndProcessNVMeSensor() = 0;
+
+ virtual void processResponse(std::shared_ptr<NVMeSensor>& sensor, void* msg,
+ size_t len) = 0;
+
+ protected:
+ boost::asio::steady_timer scanTimer;
+ int rootBus; // Root bus for this drive
+ std::list<std::shared_ptr<NVMeSensor>> sensors;
+ std::list<std::shared_ptr<NVMeSensor>>::iterator pollCursor;
+};
+
+using NVMEMap = boost::container::flat_map<int, std::shared_ptr<NVMeContext>>;
+
+NVMEMap& getNVMEMap(void);
diff --git a/src/NVMeSensor.cpp b/src/NVMeSensor.cpp
index d32d166..4301158 100644
--- a/src/NVMeSensor.cpp
+++ b/src/NVMeSensor.cpp
@@ -14,7 +14,7 @@
// limitations under the License.
*/
-#include <NVMeSensor.hpp>
+#include "NVMeSensor.hpp"
#include <iostream>
diff --git a/src/NVMeSensor.hpp b/src/NVMeSensor.hpp
new file mode 100644
index 0000000..4e0000f
--- /dev/null
+++ b/src/NVMeSensor.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <boost/asio/io_service.hpp>
+#include <sensor.hpp>
+
+class NVMeSensor : public Sensor
+{
+ public:
+ static constexpr const char* sensorType = "NVME1000";
+
+ NVMeSensor(sdbusplus::asio::object_server& objectServer,
+ boost::asio::io_service& io,
+ std::shared_ptr<sdbusplus::asio::connection>& conn,
+ const std::string& sensorName,
+ std::vector<thresholds::Threshold>&& thresholds,
+ const std::string& sensorConfiguration, int busNumber);
+ ~NVMeSensor() override;
+
+ NVMeSensor& operator=(const NVMeSensor& other) = delete;
+
+ bool sample();
+
+ int bus;
+
+ private:
+ const unsigned int scanDelayTicks = 5 * 60;
+ sdbusplus::asio::object_server& objServer;
+ unsigned int scanDelay{0};
+
+ void checkThresholds(void) override;
+};
diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp
index 96a3017..9ad0706 100644
--- a/src/NVMeSensorMain.cpp
+++ b/src/NVMeSensorMain.cpp
@@ -14,9 +14,10 @@
// limitations under the License.
*/
-#include <NVMeBasicContext.hpp>
-#include <NVMeContext.hpp>
-#include <NVMeSensor.hpp>
+#include "NVMeBasicContext.hpp"
+#include "NVMeContext.hpp"
+#include "NVMeSensor.hpp"
+
#include <boost/asio/steady_timer.hpp>
#include <optional>
diff --git a/src/PSUEvent.cpp b/src/PSUEvent.cpp
index 27bb8b1..ce8301e 100644
--- a/src/PSUEvent.cpp
+++ b/src/PSUEvent.cpp
@@ -14,8 +14,10 @@
// limitations under the License.
*/
-#include <PSUEvent.hpp>
-#include <SensorPaths.hpp>
+#include "PSUEvent.hpp"
+
+#include "SensorPaths.hpp"
+
#include <boost/asio/io_service.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/container/flat_map.hpp>
diff --git a/src/PSUEvent.hpp b/src/PSUEvent.hpp
new file mode 100644
index 0000000..12fc2ef
--- /dev/null
+++ b/src/PSUEvent.hpp
@@ -0,0 +1,102 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#pragma once
+
+#include "Utils.hpp"
+
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/random_access_file.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <boost/container/flat_map.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <array>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+class PSUSubEvent : public std::enable_shared_from_this<PSUSubEvent>
+{
+ public:
+ PSUSubEvent(std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface,
+ const std::string& path,
+ std::shared_ptr<sdbusplus::asio::connection>& conn,
+ boost::asio::io_service& io, const PowerState& powerState,
+ const std::string& groupEventName, const std::string& eventName,
+ std::shared_ptr<std::set<std::string>> asserts,
+ std::shared_ptr<std::set<std::string>> combineEvent,
+ std::shared_ptr<bool> state, const std::string& psuName,
+ double pollRate);
+ ~PSUSubEvent();
+
+ std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface;
+ std::shared_ptr<std::set<std::string>> asserts;
+ std::shared_ptr<std::set<std::string>> combineEvent;
+ std::shared_ptr<bool> assertState;
+ void setupRead(void);
+
+ private:
+ int value = 0;
+ size_t errCount{0};
+ std::string path;
+ std::string eventName;
+
+ PowerState readState;
+ boost::asio::steady_timer waitTimer;
+ std::shared_ptr<std::array<char, 128>> buffer;
+ void restartRead();
+ void handleResponse(const boost::system::error_code& err,
+ size_t bytesTransferred);
+ void updateValue(const int& newValue);
+ boost::asio::random_access_file inputDev;
+ std::string psuName;
+ std::string groupEventName;
+ std::string fanName;
+ std::string assertMessage;
+ std::string deassertMessage;
+ std::shared_ptr<sdbusplus::asio::connection> systemBus;
+ unsigned int eventPollMs = defaultEventPollMs;
+ static constexpr unsigned int defaultEventPollMs = 1000;
+ static constexpr size_t warnAfterErrorCount = 10;
+};
+
+class PSUCombineEvent
+{
+ public:
+ PSUCombineEvent(
+ sdbusplus::asio::object_server& objectServer,
+ std::shared_ptr<sdbusplus::asio::connection>& conn,
+ boost::asio::io_service& io, const std::string& psuName,
+ const PowerState& powerState,
+ boost::container::flat_map<std::string, std::vector<std::string>>&
+ eventPathList,
+ boost::container::flat_map<
+ std::string,
+ boost::container::flat_map<std::string, std::vector<std::string>>>&
+ groupEventPathList,
+ const std::string& combineEventName, double pollRate);
+ ~PSUCombineEvent();
+
+ sdbusplus::asio::object_server& objServer;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface;
+ boost::container::flat_map<std::string,
+ std::vector<std::shared_ptr<PSUSubEvent>>>
+ events;
+ std::vector<std::shared_ptr<std::set<std::string>>> asserts;
+ std::vector<std::shared_ptr<bool>> states;
+};
diff --git a/src/PSUSensor.cpp b/src/PSUSensor.cpp
index 6e2934f..986fe59 100644
--- a/src/PSUSensor.cpp
+++ b/src/PSUSensor.cpp
@@ -14,9 +14,10 @@
// limitations under the License.
*/
+#include "PSUSensor.hpp"
+
#include <unistd.h>
-#include <PSUSensor.hpp>
#include <boost/asio/random_access_file.hpp>
#include <boost/asio/read_until.hpp>
#include <sdbusplus/asio/connection.hpp>
diff --git a/src/PSUSensor.hpp b/src/PSUSensor.hpp
new file mode 100644
index 0000000..379b9d9
--- /dev/null
+++ b/src/PSUSensor.hpp
@@ -0,0 +1,71 @@
+#pragma once
+
+#include "PwmSensor.hpp"
+#include "Thresholds.hpp"
+#include "sensor.hpp"
+
+#include <boost/asio/random_access_file.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <array>
+#include <memory>
+#include <string>
+#include <utility>
+
+class PSUSensor : public Sensor, public std::enable_shared_from_this<PSUSensor>
+{
+ public:
+ PSUSensor(const std::string& path, const std::string& objectType,
+ sdbusplus::asio::object_server& objectServer,
+ std::shared_ptr<sdbusplus::asio::connection>& conn,
+ boost::asio::io_service& io, const std::string& sensorName,
+ std::vector<thresholds::Threshold>&& thresholds,
+ const std::string& sensorConfiguration,
+ const PowerState& powerState, const std::string& sensorUnits,
+ unsigned int factor, double max, double min, double offset,
+ const std::string& label, size_t tSize, double pollRate);
+ ~PSUSensor() override;
+ void setupRead(void);
+
+ private:
+ // Note, this buffer is a shared_ptr because during a read, its lifetime
+ // might have to outlive the PSUSensor class if the object gets destroyed
+ // while in the middle of a read operation
+ std::shared_ptr<std::array<char, 128>> buffer;
+ sdbusplus::asio::object_server& objServer;
+ boost::asio::random_access_file inputDev;
+ boost::asio::steady_timer waitTimer;
+ std::string path;
+ unsigned int sensorFactor;
+ double sensorOffset;
+ thresholds::ThresholdTimer thresholdTimer;
+ void restartRead();
+ void handleResponse(const boost::system::error_code& err, size_t bytesRead);
+ void checkThresholds(void) override;
+ unsigned int sensorPollMs = defaultSensorPollMs;
+
+ static constexpr size_t warnAfterErrorCount = 10;
+
+ public:
+ static constexpr double defaultSensorPoll = 1.0;
+ static constexpr unsigned int defaultSensorPollMs =
+ static_cast<unsigned int>(defaultSensorPoll * 1000);
+};
+
+class PSUProperty
+{
+ public:
+ PSUProperty(std::string name, double max, double min, unsigned int factor,
+ double offset) :
+ labelTypeName(std::move(name)),
+ maxReading(max), minReading(min), sensorScaleFactor(factor),
+ sensorOffset(offset)
+ {}
+ ~PSUProperty() = default;
+
+ std::string labelTypeName;
+ double maxReading;
+ double minReading;
+ unsigned int sensorScaleFactor;
+ double sensorOffset;
+};
diff --git a/src/PSUSensorMain.cpp b/src/PSUSensorMain.cpp
index 38f92a7..ad01a3e 100644
--- a/src/PSUSensorMain.cpp
+++ b/src/PSUSensorMain.cpp
@@ -14,9 +14,10 @@
// limitations under the License.
*/
-#include <PSUEvent.hpp>
-#include <PSUSensor.hpp>
-#include <Utils.hpp>
+#include "PSUEvent.hpp"
+#include "PSUSensor.hpp"
+#include "Utils.hpp"
+
#include <boost/algorithm/string/replace.hpp>
#include <boost/container/flat_map.hpp>
#include <boost/container/flat_set.hpp>
diff --git a/src/PwmSensor.cpp b/src/PwmSensor.cpp
index 0ee8c88..ad54dfe 100644
--- a/src/PwmSensor.cpp
+++ b/src/PwmSensor.cpp
@@ -13,8 +13,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
*/
-#include <PwmSensor.hpp>
-#include <Utils.hpp>
+
+#include "PwmSensor.hpp"
+
+#include "Utils.hpp"
+
#include <sdbusplus/asio/object_server.hpp>
#include <fstream>
diff --git a/src/PwmSensor.hpp b/src/PwmSensor.hpp
new file mode 100644
index 0000000..f70079b
--- /dev/null
+++ b/src/PwmSensor.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "sensor.hpp"
+
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <memory>
+#include <string>
+
+class PwmSensor
+{
+ public:
+ PwmSensor(const std::string& name, const std::string& sysPath,
+ std::shared_ptr<sdbusplus::asio::connection>& conn,
+ sdbusplus::asio::object_server& objectServer,
+ const std::string& sensorConfiguration,
+ const std::string& sensorType, bool isValueMutable = false);
+ ~PwmSensor();
+
+ private:
+ std::string sysPath;
+ sdbusplus::asio::object_server& objectServer;
+ std::string name;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> sensorInterface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> controlInterface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> association;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> valueMutabilityInterface;
+ double pwmMax;
+ void setValue(uint32_t value);
+ uint32_t getValue(bool errThrow = true);
+};
diff --git a/src/SensorPaths.cpp b/src/SensorPaths.cpp
index 7eb8d09..bd8f173 100644
--- a/src/SensorPaths.cpp
+++ b/src/SensorPaths.cpp
@@ -1,4 +1,4 @@
-#include <SensorPaths.hpp>
+#include "SensorPaths.hpp"
#include <cstring>
#include <regex>
diff --git a/src/SensorPaths.hpp b/src/SensorPaths.hpp
new file mode 100644
index 0000000..e2475fa
--- /dev/null
+++ b/src/SensorPaths.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <cstring>
+#include <regex>
+#include <string>
+
+namespace sensor_paths
+{
+
+// This is an allowlist of the units a sensor can measure. Should be in sync
+// with
+// phosphor-dbus-interfaces/blob/master/yaml/xyz/openbmc_project/Sensor/Value.interface.yaml#L38
+
+constexpr const char* unitDegreesC =
+ "xyz.openbmc_project.Sensor.Value.Unit.DegreesC";
+constexpr const char* unitRPMs = "xyz.openbmc_project.Sensor.Value.Unit.RPMS";
+constexpr const char* unitVolts = "xyz.openbmc_project.Sensor.Value.Unit.Volts";
+constexpr const char* unitMeters =
+ "xyz.openbmc_project.Sensor.Value.Unit.Meters";
+constexpr const char* unitAmperes =
+ "xyz.openbmc_project.Sensor.Value.Unit.Amperes";
+constexpr const char* unitWatts = "xyz.openbmc_project.Sensor.Value.Unit.Watts";
+constexpr const char* unitJoules =
+ "xyz.openbmc_project.Sensor.Value.Unit.Joules";
+constexpr const char* unitPercent =
+ "xyz.openbmc_project.Sensor.Value.Unit.Percent";
+constexpr const char* unitCFM = "xyz.openbmc_project.Sensor.Value.Unit.CFM";
+constexpr const char* unitPascals =
+ "xyz.openbmc_project.Sensor.Value.Unit.Pascals";
+
+std::string getPathForUnits(const std::string& units);
+
+std::string escapePathForDbus(const std::string& name);
+
+} // namespace sensor_paths
diff --git a/src/TachSensor.cpp b/src/TachSensor.cpp
index 35185cd..9a1ff7c 100644
--- a/src/TachSensor.cpp
+++ b/src/TachSensor.cpp
@@ -14,10 +14,12 @@
// limitations under the License.
*/
+#include "TachSensor.hpp"
+
+#include "Utils.hpp"
+
#include <unistd.h>
-#include <TachSensor.hpp>
-#include <Utils.hpp>
#include <boost/asio/read_until.hpp>
#include <gpiod.hpp>
#include <sdbusplus/asio/connection.hpp>
diff --git a/src/TachSensor.hpp b/src/TachSensor.hpp
new file mode 100644
index 0000000..92c5202
--- /dev/null
+++ b/src/TachSensor.hpp
@@ -0,0 +1,126 @@
+#pragma once
+
+#include "Thresholds.hpp"
+#include "sensor.hpp"
+
+#include <boost/asio/random_access_file.hpp>
+#include <boost/container/flat_map.hpp>
+#include <boost/container/flat_set.hpp>
+#include <gpiod.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+
+class PresenceSensor
+{
+ public:
+ PresenceSensor(const std::string& gpioName, bool inverted,
+ boost::asio::io_service& io, const std::string& name);
+ ~PresenceSensor();
+
+ void monitorPresence(void);
+ void read(void);
+ bool getValue(void) const;
+
+ private:
+ bool status = true;
+ gpiod::line gpioLine;
+ boost::asio::posix::stream_descriptor gpioFd;
+ std::string name;
+};
+
+namespace redundancy
+{
+constexpr const char* full = "Full";
+constexpr const char* degraded = "Degraded";
+constexpr const char* failed = "Failed";
+} // namespace redundancy
+
+class RedundancySensor
+{
+ public:
+ RedundancySensor(size_t count, const std::vector<std::string>& children,
+ sdbusplus::asio::object_server& objectServer,
+ const std::string& sensorConfiguration);
+ ~RedundancySensor();
+
+ void update(const std::string& name, bool failed);
+
+ private:
+ size_t count;
+ std::string state = redundancy::full;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> iface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> association;
+ sdbusplus::asio::object_server& objectServer;
+ boost::container::flat_map<std::string, bool> statuses;
+};
+
+class TachSensor :
+ public Sensor,
+ public std::enable_shared_from_this<TachSensor>
+{
+ public:
+ TachSensor(const std::string& path, const std::string& objectType,
+ sdbusplus::asio::object_server& objectServer,
+ std::shared_ptr<sdbusplus::asio::connection>& conn,
+ std::unique_ptr<PresenceSensor>&& presence,
+ std::optional<RedundancySensor>* redundancy,
+ boost::asio::io_service& io, const std::string& fanName,
+ std::vector<thresholds::Threshold>&& thresholds,
+ const std::string& sensorConfiguration,
+ const std::pair<double, double>& limits,
+ const PowerState& powerState,
+ const std::optional<std::string>& led);
+ ~TachSensor() override;
+ void setupRead();
+
+ private:
+ // Ordering is important here; readBuf is first so that it's not destroyed
+ // while async operations from other member fields might still be using it.
+ std::array<char, 128> readBuf{};
+ sdbusplus::asio::object_server& objServer;
+ std::optional<RedundancySensor>* redundancy;
+ std::unique_ptr<PresenceSensor> presence;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> itemAssoc;
+ boost::asio::random_access_file inputDev;
+ boost::asio::steady_timer waitTimer;
+ std::string path;
+ std::optional<std::string> led;
+ bool ledState = false;
+
+ void handleResponse(const boost::system::error_code& err, size_t bytesRead);
+ void restartRead(size_t pollTime);
+ void checkThresholds(void) override;
+};
+
+inline void logFanInserted(const std::string& device)
+{
+ const auto* msg = "OpenBMC.0.1.FanInserted";
+ lg2::error("Fan Inserted", "REDFISH_MESSAGE_ID", msg,
+ "REDFISH_MESSAGE_ARGS", device);
+}
+
+inline void logFanRemoved(const std::string& device)
+{
+ const auto* msg = "OpenBMC.0.1.FanRemoved";
+ lg2::error("Fan Removed", "REDFISH_MESSAGE_ID", msg, "REDFISH_MESSAGE_ARGS",
+ device);
+}
+
+inline void logFanRedundancyLost(void)
+{
+ const auto* msg = "OpenBMC.0.1.FanRedundancyLost";
+ lg2::error("Fan Inserted", "REDFISH_MESSAGE_ID", msg);
+}
+
+inline void logFanRedundancyRestored(void)
+{
+ const auto* msg = "OpenBMC.0.1.FanRedundancyRegained";
+ lg2::error("Fan Removed", "REDFISH_MESSAGE_ID", msg);
+}
diff --git a/src/Thresholds.cpp b/src/Thresholds.cpp
index ee0456d..aa73063 100644
--- a/src/Thresholds.cpp
+++ b/src/Thresholds.cpp
@@ -1,8 +1,10 @@
-#include <Thresholds.hpp>
-#include <VariantVisitors.hpp>
+#include "Thresholds.hpp"
+
+#include "VariantVisitors.hpp"
+#include "sensor.hpp"
+
#include <boost/algorithm/string/replace.hpp>
#include <boost/container/flat_map.hpp>
-#include <sensor.hpp>
#include <array>
#include <cmath>
diff --git a/src/Thresholds.hpp b/src/Thresholds.hpp
new file mode 100644
index 0000000..cd89efc
--- /dev/null
+++ b/src/Thresholds.hpp
@@ -0,0 +1,155 @@
+#pragma once
+
+#include "Utils.hpp"
+
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <nlohmann/json.hpp>
+
+#include <list>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+struct Sensor;
+namespace thresholds
+{
+enum class Level
+{
+ WARNING,
+ CRITICAL,
+ PERFORMANCELOSS,
+ SOFTSHUTDOWN,
+ HARDSHUTDOWN,
+ ERROR
+};
+enum class Direction
+{
+ HIGH,
+ LOW,
+ ERROR
+};
+struct Threshold
+{
+ Threshold(
+ const Level& lev, const Direction& dir, const double& val,
+ const double hysteresis = std::numeric_limits<double>::quiet_NaN(),
+ bool write = true) :
+ level(lev),
+ direction(dir), value(val), hysteresis(hysteresis), writeable(write)
+ {}
+ Level level;
+ Direction direction;
+ double value;
+ double hysteresis;
+ bool writeable;
+
+ bool operator==(const Threshold& rhs) const
+ {
+ return (level == rhs.level && direction == rhs.direction &&
+ value == rhs.value);
+ }
+};
+
+void assertThresholds(Sensor* sensor, double assertValue,
+ thresholds::Level level, thresholds::Direction direction,
+ bool assert);
+
+struct TimerUsed
+{
+ bool used;
+ Level level;
+ Direction direction;
+ bool assert;
+};
+
+using TimerPair = std::pair<struct TimerUsed, boost::asio::steady_timer>;
+
+struct ThresholdTimer
+{
+
+ explicit ThresholdTimer(boost::asio::io_service& ioService) : io(ioService)
+ {}
+
+ bool hasActiveTimer(const Threshold& threshold, bool assert)
+ {
+ for (TimerPair& timer : timers)
+ {
+ if (timer.first.used)
+ {
+ if ((timer.first.level == threshold.level) &&
+ (timer.first.direction == threshold.direction) &&
+ (timer.first.assert == assert))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ void stopTimer(const Threshold& threshold, bool assert)
+ {
+ struct TimerUsed timerUsed = {};
+ for (TimerPair& timer : timers)
+ {
+ timerUsed = timer.first;
+ if (timerUsed.used)
+ {
+ if ((timerUsed.level == threshold.level) &&
+ (timerUsed.direction == threshold.direction) &&
+ (timerUsed.assert == assert))
+ {
+ timer.second.cancel();
+ }
+ }
+ }
+ }
+
+ void startTimer(const std::weak_ptr<Sensor>& weakSensor,
+ const Threshold& threshold, bool assert,
+ double assertValue);
+
+ boost::asio::io_service& io;
+ std::list<TimerPair> timers;
+};
+
+bool parseThresholdsFromConfig(
+ const SensorData& sensorData,
+ std::vector<thresholds::Threshold>& thresholdVector,
+ const std::string* matchLabel = nullptr, const int* sensorIndex = nullptr);
+
+bool parseThresholdsFromAttr(
+ std::vector<thresholds::Threshold>& thresholdVector,
+ const std::string& inputPath, const double& scaleFactor,
+ const double& offset = 0);
+
+struct ThresholdDefinition
+{
+ Level level;
+ uint8_t sevOrder;
+ const char* levelName;
+};
+
+constexpr static std::array<thresholds::ThresholdDefinition, 5> thresProp = {
+ {{Level::WARNING, 0, "Warning"},
+ {Level::CRITICAL, 1, "Critical"},
+ {Level::PERFORMANCELOSS, 2, "PerformanceLoss"},
+ {Level::SOFTSHUTDOWN, 3, "SoftShutdown"},
+ {Level::HARDSHUTDOWN, 4, "HardShutdown"}}};
+
+std::string getInterface(Level level);
+
+void persistThreshold(const std::string& path, const std::string& baseInterface,
+ const thresholds::Threshold& threshold,
+ std::shared_ptr<sdbusplus::asio::connection>& conn,
+ size_t thresholdCount, const std::string& label);
+
+void updateThresholds(Sensor* sensor);
+// returns false if a critical threshold has been crossed, true otherwise
+bool checkThresholds(Sensor* sensor);
+void checkThresholdsPowerDelay(const std::weak_ptr<Sensor>& weakSensor,
+ ThresholdTimer& thresholdTimer);
+
+} // namespace thresholds
diff --git a/src/Utils.cpp b/src/Utils.cpp
index eb4620d..56428bc 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -14,10 +14,12 @@
// limitations under the License.
*/
+#include "Utils.hpp"
+
#include "dbus-sensor_config.h"
-#include <DeviceMgmt.hpp>
-#include <Utils.hpp>
+#include "DeviceMgmt.hpp"
+
#include <boost/container/flat_map.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
diff --git a/src/Utils.hpp b/src/Utils.hpp
new file mode 100644
index 0000000..444030c
--- /dev/null
+++ b/src/Utils.hpp
@@ -0,0 +1,390 @@
+#pragma once
+
+#include "VariantVisitors.hpp"
+
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <boost/container/flat_map.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/message/types.hpp>
+
+#include <filesystem>
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <regex>
+#include <span>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <variant>
+#include <vector>
+
+const constexpr char* jsonStore = "/var/configuration/flattened.json";
+const constexpr char* inventoryPath = "/xyz/openbmc_project/inventory";
+const constexpr char* entityManagerName = "xyz.openbmc_project.EntityManager";
+
+constexpr const char* cpuInventoryPath =
+ "/xyz/openbmc_project/inventory/system/chassis/motherboard";
+const std::regex illegalDbusRegex("[^A-Za-z0-9_]");
+
+using BasicVariantType =
+ std::variant<std::vector<std::string>, std::string, int64_t, uint64_t,
+ double, int32_t, uint32_t, int16_t, uint16_t, uint8_t, bool>;
+using SensorBaseConfigMap =
+ boost::container::flat_map<std::string, BasicVariantType>;
+using SensorBaseConfiguration = std::pair<std::string, SensorBaseConfigMap>;
+using SensorData = boost::container::flat_map<std::string, SensorBaseConfigMap>;
+using ManagedObjectType =
+ boost::container::flat_map<sdbusplus::message::object_path, SensorData>;
+
+using GetSubTreeType = std::vector<
+ std::pair<std::string,
+ std::vector<std::pair<std::string, std::vector<std::string>>>>>;
+using Association = std::tuple<std::string, std::string, std::string>;
+
+inline std::string escapeName(const std::string& sensorName)
+{
+ return boost::replace_all_copy(sensorName, " ", "_");
+}
+
+enum class PowerState
+{
+ on,
+ biosPost,
+ always,
+ chassisOn
+};
+
+std::optional<std::string> openAndRead(const std::string& hwmonFile);
+std::optional<std::string>
+ getFullHwmonFilePath(const std::string& directory,
+ const std::string& hwmonBaseName,
+ const std::set<std::string>& permitSet);
+std::set<std::string> getPermitSet(const SensorBaseConfigMap& config);
+bool findFiles(const std::filesystem::path& dirPath,
+ std::string_view matchString,
+ std::vector<std::filesystem::path>& foundPaths,
+ int symlinkDepth = 1);
+bool isPowerOn(void);
+bool hasBiosPost(void);
+bool isChassisOn(void);
+void setupPowerMatchCallback(
+ const std::shared_ptr<sdbusplus::asio::connection>& conn,
+ std::function<void(PowerState type, bool state)>&& callback);
+void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn);
+bool getSensorConfiguration(
+ const std::string& type,
+ const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+ ManagedObjectType& resp, bool useCache);
+
+bool getSensorConfiguration(
+ const std::string& type,
+ const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
+ ManagedObjectType& resp);
+
+void createAssociation(
+ std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
+ const std::string& path);
+
+// replaces limits if MinReading and MaxReading are found.
+void findLimits(std::pair<double, double>& limits,
+ const SensorBaseConfiguration* data);
+
+bool readingStateGood(const PowerState& powerState);
+
+constexpr const char* configInterfacePrefix =
+ "xyz.openbmc_project.Configuration.";
+
+inline std::string configInterfaceName(const std::string& type)
+{
+ return std::string(configInterfacePrefix) + type;
+}
+
+namespace mapper
+{
+constexpr const char* busName = "xyz.openbmc_project.ObjectMapper";
+constexpr const char* path = "/xyz/openbmc_project/object_mapper";
+constexpr const char* interface = "xyz.openbmc_project.ObjectMapper";
+constexpr const char* subtree = "GetSubTree";
+} // namespace mapper
+
+namespace properties
+{
+constexpr const char* interface = "org.freedesktop.DBus.Properties";
+constexpr const char* get = "Get";
+constexpr const char* set = "Set";
+} // namespace properties
+
+namespace power
+{
+const static constexpr char* busname = "xyz.openbmc_project.State.Host";
+const static constexpr char* interface = "xyz.openbmc_project.State.Host";
+const static constexpr char* path = "/xyz/openbmc_project/state/host0";
+const static constexpr char* property = "CurrentHostState";
+} // namespace power
+
+namespace chassis
+{
+const static constexpr char* busname = "xyz.openbmc_project.State.Chassis";
+const static constexpr char* interface = "xyz.openbmc_project.State.Chassis";
+const static constexpr char* path = "/xyz/openbmc_project/state/chassis0";
+const static constexpr char* property = "CurrentPowerState";
+const static constexpr char* sOn = "On";
+} // namespace chassis
+
+namespace post
+{
+const static constexpr char* busname =
+ "xyz.openbmc_project.State.OperatingSystem";
+const static constexpr char* interface =
+ "xyz.openbmc_project.State.OperatingSystem.Status";
+const static constexpr char* path = "/xyz/openbmc_project/state/os";
+const static constexpr char* property = "OperatingSystemState";
+} // namespace post
+
+namespace association
+{
+const static constexpr char* interface =
+ "xyz.openbmc_project.Association.Definitions";
+} // namespace association
+
+template <typename T>
+inline T loadVariant(const SensorBaseConfigMap& data, const std::string& key)
+{
+ auto it = data.find(key);
+ if (it == data.end())
+ {
+ std::cerr << "Configuration missing " << key << "\n";
+ throw std::invalid_argument("Key Missing");
+ }
+ if constexpr (std::is_same_v<T, double>)
+ {
+ return std::visit(VariantToDoubleVisitor(), it->second);
+ }
+ else if constexpr (std::is_unsigned_v<T>)
+ {
+ return std::visit(VariantToUnsignedIntVisitor(), it->second);
+ }
+ else if constexpr (std::is_same_v<T, std::string>)
+ {
+ return std::visit(VariantToStringVisitor(), it->second);
+ }
+ else
+ {
+ static_assert(!std::is_same_v<T, T>, "Type Not Implemented");
+ }
+}
+
+inline void setReadState(const std::string& str, PowerState& val)
+{
+
+ if (str == "On")
+ {
+ val = PowerState::on;
+ }
+ else if (str == "BiosPost")
+ {
+ val = PowerState::biosPost;
+ }
+ else if (str == "Always")
+ {
+ val = PowerState::always;
+ }
+ else if (str == "ChassisOn")
+ {
+ val = PowerState::chassisOn;
+ }
+}
+
+inline PowerState getPowerState(const SensorBaseConfigMap& cfg)
+{
+ PowerState state = PowerState::always;
+ auto findPowerState = cfg.find("PowerState");
+ if (findPowerState != cfg.end())
+ {
+ std::string powerState =
+ std::visit(VariantToStringVisitor(), findPowerState->second);
+ setReadState(powerState, state);
+ }
+ return state;
+}
+
+inline float getPollRate(const SensorBaseConfigMap& cfg, float dflt)
+{
+ float pollRate = dflt;
+ auto findPollRate = cfg.find("PollRate");
+ if (findPollRate != cfg.end())
+ {
+ pollRate = std::visit(VariantToFloatVisitor(), findPollRate->second);
+ if (!std::isfinite(pollRate) || pollRate <= 0.0F)
+ {
+ pollRate = dflt; // poll time invalid, fall back to default
+ }
+ }
+ return pollRate;
+}
+
+inline void setLed(const std::shared_ptr<sdbusplus::asio::connection>& conn,
+ const std::string& name, bool on)
+{
+ conn->async_method_call(
+ [name](const boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr << "Failed to set LED " << name << "\n";
+ }
+ },
+ "xyz.openbmc_project.LED.GroupManager",
+ "/xyz/openbmc_project/led/groups/" + name, properties::interface,
+ properties::set, "xyz.openbmc_project.Led.Group", "Asserted",
+ std::variant<bool>(on));
+}
+
+void createInventoryAssoc(
+ const std::shared_ptr<sdbusplus::asio::connection>& conn,
+ const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
+ const std::string& path);
+
+struct GetSensorConfiguration :
+ std::enable_shared_from_this<GetSensorConfiguration>
+{
+ GetSensorConfiguration(
+ std::shared_ptr<sdbusplus::asio::connection> connection,
+ std::function<void(ManagedObjectType& resp)>&& callbackFunc) :
+ dbusConnection(std::move(connection)),
+ callback(std::move(callbackFunc))
+ {}
+
+ void getPath(const std::string& path, const std::string& interface,
+ const std::string& owner, size_t retries = 5)
+ {
+ if (retries > 5)
+ {
+ retries = 5;
+ }
+ std::shared_ptr<GetSensorConfiguration> self = shared_from_this();
+
+ self->dbusConnection->async_method_call(
+ [self, path, interface, owner, retries](
+ const boost::system::error_code ec, SensorBaseConfigMap& data) {
+ if (ec)
+ {
+ std::cerr << "Error getting " << path << ": retries left"
+ << retries - 1 << "\n";
+ if (retries == 0U)
+ {
+ return;
+ }
+ auto timer = std::make_shared<boost::asio::steady_timer>(
+ self->dbusConnection->get_io_context());
+ timer->expires_after(std::chrono::seconds(10));
+ timer->async_wait([self, timer, path, interface, owner,
+ retries](boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr << "Timer error!\n";
+ return;
+ }
+ self->getPath(path, interface, owner, retries - 1);
+ });
+ return;
+ }
+
+ self->respData[path][interface] = std::move(data);
+ },
+ owner, path, "org.freedesktop.DBus.Properties", "GetAll",
+ interface);
+ }
+
+ void getConfiguration(const std::vector<std::string>& types,
+ size_t retries = 0)
+ {
+ if (retries > 5)
+ {
+ retries = 5;
+ }
+
+ std::vector<std::string> interfaces(types.size());
+ for (const auto& type : types)
+ {
+ interfaces.push_back(configInterfaceName(type));
+ }
+
+ std::shared_ptr<GetSensorConfiguration> self = shared_from_this();
+ dbusConnection->async_method_call(
+ [self, interfaces, retries](const boost::system::error_code ec,
+ const GetSubTreeType& ret) {
+ if (ec)
+ {
+ std::cerr << "Error calling mapper\n";
+ if (retries == 0U)
+ {
+ return;
+ }
+ auto timer = std::make_shared<boost::asio::steady_timer>(
+ self->dbusConnection->get_io_context());
+ timer->expires_after(std::chrono::seconds(10));
+ timer->async_wait([self, timer, interfaces,
+ retries](boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr << "Timer error!\n";
+ return;
+ }
+ self->getConfiguration(interfaces, retries - 1);
+ });
+
+ return;
+ }
+ for (const auto& [path, objDict] : ret)
+ {
+ if (objDict.empty())
+ {
+ return;
+ }
+ const std::string& owner = objDict.begin()->first;
+
+ for (const std::string& interface : objDict.begin()->second)
+ {
+ // anything that starts with a requested configuration
+ // is good
+ if (std::find_if(interfaces.begin(), interfaces.end(),
+ [interface](const std::string& possible) {
+ return interface.starts_with(possible);
+ }) == interfaces.end())
+ {
+ continue;
+ }
+ self->getPath(path, interface, owner);
+ }
+ }
+ },
+ mapper::busName, mapper::path, mapper::interface, mapper::subtree,
+ "/", 0, interfaces);
+ }
+
+ ~GetSensorConfiguration()
+ {
+ callback(respData);
+ }
+
+ std::shared_ptr<sdbusplus::asio::connection> dbusConnection;
+ std::function<void(ManagedObjectType& resp)> callback;
+ ManagedObjectType respData;
+};
+
+// The common scheme for sysfs files naming is: <type><number>_<item>.
+// This function returns optionally these 3 elements as a tuple.
+std::optional<std::tuple<std::string, std::string, std::string>>
+ splitFileName(const std::filesystem::path& filePath);
+std::optional<double> readFile(const std::string& thresholdFile,
+ const double& scaleFactor);
+void setupManufacturingModeMatch(sdbusplus::asio::connection& conn);
+bool getManufacturingMode();
+std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
+ setupPropertiesChangedMatches(
+ sdbusplus::asio::connection& bus, std::span<const char* const> types,
+ const std::function<void(sdbusplus::message_t&)>& handler);
diff --git a/src/VariantVisitors.hpp b/src/VariantVisitors.hpp
new file mode 100644
index 0000000..27185ca
--- /dev/null
+++ b/src/VariantVisitors.hpp
@@ -0,0 +1,69 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#pragma once
+#include <boost/type_index.hpp>
+
+#include <stdexcept>
+#include <string>
+#include <variant>
+
+namespace details
+{
+
+template <typename U>
+struct VariantToNumericVisitor
+{
+ template <typename T>
+ U operator()(const T& t) const
+ {
+ if constexpr (std::is_arithmetic_v<T>)
+ {
+ return static_cast<U>(t);
+ }
+ throw std::invalid_argument(
+ "Cannot translate type " +
+ boost::typeindex::type_id<T>().pretty_name() + " to " +
+ boost::typeindex::type_id<U>().pretty_name());
+ }
+};
+
+} // namespace details
+
+using VariantToFloatVisitor = details::VariantToNumericVisitor<float>;
+using VariantToIntVisitor = details::VariantToNumericVisitor<int>;
+using VariantToUnsignedIntVisitor =
+ details::VariantToNumericVisitor<unsigned int>;
+using VariantToDoubleVisitor = details::VariantToNumericVisitor<double>;
+
+struct VariantToStringVisitor
+{
+ template <typename T>
+ std::string operator()(const T& t) const
+ {
+ if constexpr (std::is_same_v<T, std::string>)
+ {
+ return t;
+ }
+ else if constexpr (std::is_arithmetic_v<T>)
+ {
+ return std::to_string(t);
+ }
+ throw std::invalid_argument(
+ "Cannot translate type " +
+ boost::typeindex::type_id<T>().pretty_name() + " to string");
+ }
+};
diff --git a/src/dbus-sensor_config.h.in b/src/dbus-sensor_config.h.in
new file mode 100644
index 0000000..6dc9931
--- /dev/null
+++ b/src/dbus-sensor_config.h.in
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <cstdint>
+
+// clang-format off
+constexpr const int validateUnsecureFeature = @VALIDATION_UNSECURE_FEATURE@;
+
+constexpr const int insecureSensorOverride = @INSECURE_UNRESTRICTED_SENSOR_OVERRIDE@;
+// clang-format on
\ No newline at end of file
diff --git a/src/meson.build b/src/meson.build
index 9d44db3..6f47fb0 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,3 +1,60 @@
+conf_data = configuration_data()
+conf_data.set10('VALIDATION_UNSECURE_FEATURE', get_option('validate-unsecure-feature').enabled())
+conf_data.set10('INSECURE_UNRESTRICTED_SENSOR_OVERRIDE', get_option('insecure-sensor-override').enabled())
+configure_file(input: 'dbus-sensor_config.h.in',
+ output: 'dbus-sensor_config.h',
+ configuration: conf_data)
+
+thresholds_a = static_library(
+ 'thresholds_a',
+ 'Thresholds.cpp',
+ dependencies: default_deps,
+)
+
+thresholds_dep = declare_dependency(
+ link_with: [ thresholds_a ],
+ dependencies: default_deps,
+)
+
+utils_a = static_library(
+ 'utils_a',
+ [
+ 'FileHandle.cpp',
+ 'SensorPaths.cpp',
+ 'Utils.cpp',
+ ],
+ dependencies: default_deps,
+)
+
+utils_dep = declare_dependency(
+ link_with: [ utils_a ],
+ dependencies: [ sdbusplus ],
+)
+
+devicemgmt_a = static_library(
+ 'devicemgmt_a',
+ [
+ 'DeviceMgmt.cpp',
+ ],
+ dependencies: default_deps,
+)
+
+devicemgmt_dep = declare_dependency(
+ link_with: [ devicemgmt_a ],
+ dependencies: default_deps,
+)
+
+pwmsensor_a = static_library(
+ 'pwmsensor_a',
+ 'PwmSensor.cpp',
+ dependencies: [ default_deps, thresholds_dep ],
+)
+
+pwmsensor_dep = declare_dependency(
+ link_with: [ pwmsensor_a ],
+ dependencies: [ default_deps, thresholds_dep ],
+)
+
peci_incdirs = []
if not meson.get_compiler('cpp').has_header('linux/peci-ioctl.h')
peci_incdirs = ['../include']
@@ -15,8 +72,6 @@
utils_dep,
],
cpp_args: uring_args,
- implicit_include_directories: false,
- include_directories: '../include',
install: true,
)
endif
@@ -32,10 +87,7 @@
thresholds_dep,
utils_dep,
],
- implicit_include_directories: false,
- include_directories: [
- '../include'
- ] + peci_incdirs,
+ include_directories: peci_incdirs,
install: true,
)
endif
@@ -50,8 +102,6 @@
utils_dep,
],
cpp_args: uring_args,
- implicit_include_directories: false,
- include_directories: '../include',
install: true,
)
endif
@@ -69,8 +119,6 @@
utils_dep,
],
cpp_args: uring_args,
- implicit_include_directories: false,
- include_directories: '../include',
install: true,
)
endif
@@ -87,8 +135,6 @@
utils_dep,
],
cpp_args: uring_args,
- implicit_include_directories: false,
- include_directories: '../include',
install: true,
)
endif
@@ -105,8 +151,6 @@
utils_dep,
],
cpp_args: uring_args,
- implicit_include_directories: false,
- include_directories: '../include',
install: true,
)
endif
@@ -122,8 +166,6 @@
utils_dep,
],
cpp_args: uring_args,
- implicit_include_directories: false,
- include_directories: '../include',
install: true,
)
endif
@@ -139,8 +181,6 @@
utils_dep,
],
cpp_args: uring_args,
- implicit_include_directories: false,
- include_directories: '../include',
install: true,
)
endif
@@ -156,8 +196,6 @@
sources: nvme_srcs,
dependencies: nvme_deps,
cpp_args: uring_args,
- implicit_include_directories: false,
- include_directories: '../include',
install: true,
)
endif
@@ -175,8 +213,6 @@
utils_dep,
],
cpp_args: uring_args,
- implicit_include_directories: false,
- include_directories: '../include',
install: true,
)
endif
@@ -192,8 +228,6 @@
utils_dep,
],
cpp_args: uring_args,
- implicit_include_directories: false,
- include_directories: '../include',
install: true,
)
endif
diff --git a/src/sensor.hpp b/src/sensor.hpp
new file mode 100644
index 0000000..d2ba8af
--- /dev/null
+++ b/src/sensor.hpp
@@ -0,0 +1,584 @@
+#pragma once
+
+#include "dbus-sensor_config.h"
+
+#include "SensorPaths.hpp"
+#include "Thresholds.hpp"
+#include "Utils.hpp"
+
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/exception.hpp>
+
+#include <limits>
+#include <memory>
+#include <string>
+#include <vector>
+
+constexpr size_t sensorFailedPollTimeMs = 5000;
+
+// Enable useful logging with sensor instrumentation
+// This is intentionally not DEBUG, avoid clash with usage in .cpp files
+constexpr bool enableInstrumentation = false;
+
+constexpr const char* sensorValueInterface = "xyz.openbmc_project.Sensor.Value";
+constexpr const char* valueMutabilityInterfaceName =
+ "xyz.openbmc_project.Sensor.ValueMutability";
+constexpr const char* availableInterfaceName =
+ "xyz.openbmc_project.State.Decorator.Availability";
+constexpr const char* operationalInterfaceName =
+ "xyz.openbmc_project.State.Decorator.OperationalStatus";
+constexpr const size_t errorThreshold = 5;
+
+struct SensorInstrumentation
+{
+ // These are for instrumentation for debugging
+ int numCollectsGood = 0;
+ int numCollectsMiss = 0;
+ int numStreakGreats = 0;
+ int numStreakMisses = 0;
+ double minCollected = 0.0;
+ double maxCollected = 0.0;
+};
+
+struct SetSensorError : sdbusplus::exception_t
+{
+ const char* name() const noexcept override
+ {
+ return "xyz.openbmc_project.Common.Errors.NotAllowed";
+ }
+ const char* description() const noexcept override
+ {
+ return "Not allowed to set property value.";
+ }
+ int get_errno() const noexcept override
+ {
+ return EACCES;
+ }
+};
+
+struct Sensor
+{
+ Sensor(const std::string& name,
+ std::vector<thresholds::Threshold>&& thresholdData,
+ const std::string& configurationPath, const std::string& objectType,
+ bool isSettable, bool isMutable, const double max, const double min,
+ std::shared_ptr<sdbusplus::asio::connection>& conn,
+ PowerState readState = PowerState::always) :
+ name(sensor_paths::escapePathForDbus(name)),
+ configurationPath(configurationPath),
+ objectType(configInterfaceName(objectType)),
+ isSensorSettable(isSettable), isValueMutable(isMutable), maxValue(max),
+ minValue(min), thresholds(std::move(thresholdData)),
+ hysteresisTrigger((max - min) * 0.01),
+ hysteresisPublish((max - min) * 0.0001), dbusConnection(conn),
+ readState(readState),
+ instrumentation(enableInstrumentation
+ ? std::make_unique<SensorInstrumentation>()
+ : nullptr)
+ {}
+ virtual ~Sensor() = default;
+ virtual void checkThresholds(void) = 0;
+ std::string name;
+ std::string configurationPath;
+ std::string objectType;
+ bool isSensorSettable;
+
+ /* A flag indicates if properties of xyz.openbmc_project.Sensor.Value
+ * interface are mutable. If mutable, then
+ * xyz.openbmc_project.Sensor.ValueMutability interface will be
+ * instantiated.
+ */
+ bool isValueMutable;
+ double maxValue;
+ double minValue;
+ std::vector<thresholds::Threshold> thresholds;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> sensorInterface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> association;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> availableInterface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> operationalInterface;
+ std::shared_ptr<sdbusplus::asio::dbus_interface> valueMutabilityInterface;
+ double value = std::numeric_limits<double>::quiet_NaN();
+ double rawValue = std::numeric_limits<double>::quiet_NaN();
+ bool overriddenState = false;
+ bool internalSet = false;
+ double hysteresisTrigger;
+ double hysteresisPublish;
+ std::shared_ptr<sdbusplus::asio::connection> dbusConnection;
+ PowerState readState;
+ size_t errCount{0};
+ std::unique_ptr<SensorInstrumentation> instrumentation;
+
+ // This member variable provides a hook that can be used to receive
+ // notification whenever this Sensor's value is externally set via D-Bus.
+ // If interested, assign your own lambda to this variable, during
+ // construction of your Sensor subclass. See ExternalSensor for example.
+ std::function<void()> externalSetHook;
+
+ using Level = thresholds::Level;
+ using Direction = thresholds::Direction;
+
+ std::array<std::shared_ptr<sdbusplus::asio::dbus_interface>,
+ thresholds::thresProp.size()>
+ thresholdInterfaces;
+
+ std::shared_ptr<sdbusplus::asio::dbus_interface>
+ getThresholdInterface(Level lev)
+ {
+ size_t index = static_cast<size_t>(lev);
+ if (index >= thresholdInterfaces.size())
+ {
+ std::cout << "Unknown threshold level \n";
+ return nullptr;
+ }
+ std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
+ thresholdInterfaces[index];
+ return interface;
+ }
+
+ void updateInstrumentation(double readValue) const
+ {
+ // Do nothing if this feature is not enabled
+ if constexpr (!enableInstrumentation)
+ {
+ return;
+ }
+ if (!instrumentation)
+ {
+ return;
+ }
+
+ // Save some typing
+ auto& inst = *instrumentation;
+
+ // Show constants if first reading (even if unsuccessful)
+ if ((inst.numCollectsGood == 0) && (inst.numCollectsMiss == 0))
+ {
+ std::cerr << "Sensor " << name << ": Configuration min=" << minValue
+ << ", max=" << maxValue << ", type=" << objectType
+ << ", path=" << configurationPath << "\n";
+ }
+
+ // Sensors can use "nan" to indicate unavailable reading
+ if (!std::isfinite(readValue))
+ {
+ // Only show this if beginning a new streak
+ if (inst.numStreakMisses == 0)
+ {
+ std::cerr << "Sensor " << name
+ << ": Missing reading, Reading counts good="
+ << inst.numCollectsGood
+ << ", miss=" << inst.numCollectsMiss
+ << ", Prior good streak=" << inst.numStreakGreats
+ << "\n";
+ }
+
+ inst.numStreakGreats = 0;
+ ++(inst.numCollectsMiss);
+ ++(inst.numStreakMisses);
+
+ return;
+ }
+
+ // Only show this if beginning a new streak and not the first time
+ if ((inst.numStreakGreats == 0) && (inst.numCollectsGood != 0))
+ {
+ std::cerr << "Sensor " << name
+ << ": Recovered reading, Reading counts good="
+ << inst.numCollectsGood
+ << ", miss=" << inst.numCollectsMiss
+ << ", Prior miss streak=" << inst.numStreakMisses << "\n";
+ }
+
+ // Initialize min/max if the first successful reading
+ if (inst.numCollectsGood == 0)
+ {
+ std::cerr << "Sensor " << name << ": First reading=" << readValue
+ << "\n";
+
+ inst.minCollected = readValue;
+ inst.maxCollected = readValue;
+ }
+
+ inst.numStreakMisses = 0;
+ ++(inst.numCollectsGood);
+ ++(inst.numStreakGreats);
+
+ // Only provide subsequent output if new min/max established
+ if (readValue < inst.minCollected)
+ {
+ std::cerr << "Sensor " << name << ": Lowest reading=" << readValue
+ << "\n";
+
+ inst.minCollected = readValue;
+ }
+
+ if (readValue > inst.maxCollected)
+ {
+ std::cerr << "Sensor " << name << ": Highest reading=" << readValue
+ << "\n";
+
+ inst.maxCollected = readValue;
+ }
+ }
+
+ int setSensorValue(const double& newValue, double& oldValue)
+ {
+ if (!internalSet)
+ {
+ if (insecureSensorOverride == 0 && !isSensorSettable &&
+ !getManufacturingMode())
+ {
+ throw SetSensorError();
+ }
+
+ oldValue = newValue;
+ overriddenState = true;
+ // check thresholds for external set
+ value = newValue;
+ checkThresholds();
+
+ // Trigger the hook, as an external set has just happened
+ if (externalSetHook)
+ {
+ externalSetHook();
+ }
+ }
+ else if (!overriddenState)
+ {
+ oldValue = newValue;
+ }
+ return 1;
+ }
+
+ void setInitialProperties(const std::string& unit,
+ const std::string& label = std::string(),
+ size_t thresholdSize = 0)
+ {
+ if (readState == PowerState::on || readState == PowerState::biosPost ||
+ readState == PowerState::chassisOn)
+ {
+ setupPowerMatch(dbusConnection);
+ }
+
+ createAssociation(association, configurationPath);
+
+ sensorInterface->register_property("Unit", unit);
+ sensorInterface->register_property("MaxValue", maxValue);
+ sensorInterface->register_property("MinValue", minValue);
+ sensorInterface->register_property(
+ "Value", value, [this](const double& newValue, double& oldValue) {
+ return setSensorValue(newValue, oldValue);
+ });
+
+ fillMissingThresholds();
+
+ for (auto& threshold : thresholds)
+ {
+ if (std::isnan(threshold.hysteresis))
+ {
+ threshold.hysteresis = hysteresisTrigger;
+ }
+
+ std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
+ getThresholdInterface(threshold.level);
+
+ if (!iface)
+ {
+ std::cout << "trying to set uninitialized interface\n";
+ continue;
+ }
+
+ std::string level =
+ propertyLevel(threshold.level, threshold.direction);
+ std::string alarm =
+ propertyAlarm(threshold.level, threshold.direction);
+
+ if ((level.empty()) || (alarm.empty()))
+ {
+ continue;
+ }
+ size_t thresSize =
+ label.empty() ? thresholds.size() : thresholdSize;
+ iface->register_property(
+ level, threshold.value,
+ [&, label, thresSize](const double& request, double& oldValue) {
+ oldValue = request; // todo, just let the config do this?
+ threshold.value = request;
+ thresholds::persistThreshold(configurationPath, objectType,
+ threshold, dbusConnection,
+ thresSize, label);
+ // Invalidate previously remembered value,
+ // so new thresholds will be checked during next update,
+ // even if sensor reading remains unchanged.
+ value = std::numeric_limits<double>::quiet_NaN();
+
+ // Although tempting, don't call checkThresholds() from here
+ // directly. Let the regular sensor monitor call the same
+ // using updateValue(), which can check conditions like
+ // poweron, etc., before raising any event.
+ return 1;
+ });
+ iface->register_property(alarm, false);
+ }
+ if (!sensorInterface->initialize())
+ {
+ std::cerr << "error initializing value interface\n";
+ }
+
+ for (auto& thresIface : thresholdInterfaces)
+ {
+ if (thresIface)
+ {
+ if (!thresIface->initialize(true))
+ {
+ std::cerr << "Error initializing threshold interface \n";
+ }
+ }
+ }
+
+ if (isValueMutable)
+ {
+ valueMutabilityInterface =
+ std::make_shared<sdbusplus::asio::dbus_interface>(
+ dbusConnection, sensorInterface->get_object_path(),
+ valueMutabilityInterfaceName);
+ valueMutabilityInterface->register_property("Mutable", true);
+ if (!valueMutabilityInterface->initialize())
+ {
+ std::cerr
+ << "error initializing sensor value mutability interface\n";
+ valueMutabilityInterface = nullptr;
+ }
+ }
+
+ if (!availableInterface)
+ {
+ availableInterface =
+ std::make_shared<sdbusplus::asio::dbus_interface>(
+ dbusConnection, sensorInterface->get_object_path(),
+ availableInterfaceName);
+ availableInterface->register_property(
+ "Available", true, [this](const bool propIn, bool& old) {
+ if (propIn == old)
+ {
+ return 1;
+ }
+ old = propIn;
+ if (!propIn)
+ {
+ updateValue(std::numeric_limits<double>::quiet_NaN());
+ }
+ return 1;
+ });
+ availableInterface->initialize();
+ }
+ if (!operationalInterface)
+ {
+ operationalInterface =
+ std::make_shared<sdbusplus::asio::dbus_interface>(
+ dbusConnection, sensorInterface->get_object_path(),
+ operationalInterfaceName);
+ operationalInterface->register_property("Functional", true);
+ operationalInterface->initialize();
+ }
+ }
+
+ static std::string propertyLevel(const Level lev, const Direction dir)
+ {
+ for (const thresholds::ThresholdDefinition& prop :
+ thresholds::thresProp)
+ {
+ if (prop.level == lev)
+ {
+ if (dir == Direction::HIGH)
+ {
+ return std::string(prop.levelName) + "High";
+ }
+ if (dir == Direction::LOW)
+ {
+ return std::string(prop.levelName) + "Low";
+ }
+ }
+ }
+ return "";
+ }
+
+ static std::string propertyAlarm(const Level lev, const Direction dir)
+ {
+ for (const thresholds::ThresholdDefinition& prop :
+ thresholds::thresProp)
+ {
+ if (prop.level == lev)
+ {
+ if (dir == Direction::HIGH)
+ {
+ return std::string(prop.levelName) + "AlarmHigh";
+ }
+ if (dir == Direction::LOW)
+ {
+ return std::string(prop.levelName) + "AlarmLow";
+ }
+ }
+ }
+ return "";
+ }
+
+ bool readingStateGood() const
+ {
+ return ::readingStateGood(readState);
+ }
+
+ void markFunctional(bool isFunctional)
+ {
+ if (operationalInterface)
+ {
+ operationalInterface->set_property("Functional", isFunctional);
+ }
+ if (isFunctional)
+ {
+ errCount = 0;
+ }
+ else
+ {
+ updateValue(std::numeric_limits<double>::quiet_NaN());
+ }
+ }
+
+ void markAvailable(bool isAvailable)
+ {
+ if (availableInterface)
+ {
+ availableInterface->set_property("Available", isAvailable);
+ errCount = 0;
+ }
+ }
+
+ void incrementError()
+ {
+ if (!readingStateGood())
+ {
+ markAvailable(false);
+ return;
+ }
+
+ if (errCount >= errorThreshold)
+ {
+ return;
+ }
+
+ errCount++;
+ if (errCount == errorThreshold)
+ {
+ std::cerr << "Sensor " << name << " reading error!\n";
+ markFunctional(false);
+ }
+ }
+
+ bool inError() const
+ {
+ return errCount >= errorThreshold;
+ }
+
+ void updateValue(const double& newValue)
+ {
+ // Ignore if overriding is enabled
+ if (overriddenState)
+ {
+ return;
+ }
+
+ if (!readingStateGood())
+ {
+ markAvailable(false);
+ updateValueProperty(std::numeric_limits<double>::quiet_NaN());
+ return;
+ }
+
+ updateValueProperty(newValue);
+ updateInstrumentation(newValue);
+
+ // Always check thresholds after changing the value,
+ // as the test against hysteresisTrigger now takes place in
+ // the thresholds::checkThresholds() method,
+ // which is called by checkThresholds() below,
+ // in all current implementations of sensors that have thresholds.
+ checkThresholds();
+ if (!std::isnan(newValue))
+ {
+ markFunctional(true);
+ markAvailable(true);
+ }
+ }
+
+ void updateProperty(
+ std::shared_ptr<sdbusplus::asio::dbus_interface>& interface,
+ double& oldValue, const double& newValue,
+ const char* dbusPropertyName) const
+ {
+ if (requiresUpdate(oldValue, newValue))
+ {
+ oldValue = newValue;
+ if (interface &&
+ !(interface->set_property(dbusPropertyName, newValue)))
+ {
+ std::cerr << "error setting property " << dbusPropertyName
+ << " to " << newValue << "\n";
+ }
+ }
+ }
+
+ bool requiresUpdate(const double& lVal, const double& rVal) const
+ {
+ const auto lNan = std::isnan(lVal);
+ const auto rNan = std::isnan(rVal);
+ if (lNan || rNan)
+ {
+ return (lNan != rNan);
+ }
+ return std::abs(lVal - rVal) > hysteresisPublish;
+ }
+
+ private:
+ // If one of the thresholds for a dbus interface is provided
+ // we have to set the other one as dbus properties are never
+ // optional.
+ void fillMissingThresholds()
+ {
+ for (thresholds::Threshold& thisThreshold : thresholds)
+ {
+ bool foundOpposite = false;
+ thresholds::Direction opposite = thresholds::Direction::HIGH;
+ if (thisThreshold.direction == thresholds::Direction::HIGH)
+ {
+ opposite = thresholds::Direction::LOW;
+ }
+ for (thresholds::Threshold& otherThreshold : thresholds)
+ {
+ if (thisThreshold.level != otherThreshold.level)
+ {
+ continue;
+ }
+ if (otherThreshold.direction != opposite)
+ {
+ continue;
+ }
+ foundOpposite = true;
+ break;
+ }
+ if (foundOpposite)
+ {
+ continue;
+ }
+ thresholds.emplace_back(thisThreshold.level, opposite,
+ std::numeric_limits<double>::quiet_NaN());
+ }
+ }
+
+ void updateValueProperty(const double& newValue)
+ {
+ // Indicate that it is internal set call, not an external overwrite
+ internalSet = true;
+ updateProperty(sensorInterface, value, newValue, "Value");
+ internalSet = false;
+ }
+};