Add fan presence support
Async montitor GPIO to get fan presence.
Tested-by: Pulled every fan on a 2U and monitored
presence.
Change-Id: Ibad2251e5c6a0ce480812051c103785f79fa60bb
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/sensors/CMakeLists.txt b/sensors/CMakeLists.txt
index 55db096..a1c80e4 100644
--- a/sensors/CMakeLists.txt
+++ b/sensors/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required (VERSION 2.8.10 FATAL_ERROR)
set (BUILD_SHARED_LIBRARIES OFF)
include (ExternalProject)
-set (CMAKE_CXX_STANDARD 14)
+set (CMAKE_CXX_STANDARD 17)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lstdc++fs")
set (CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
diff --git a/sensors/include/TachSensor.hpp b/sensors/include/TachSensor.hpp
index 33d2da0..2dc6216 100644
--- a/sensors/include/TachSensor.hpp
+++ b/sensors/include/TachSensor.hpp
@@ -4,6 +4,25 @@
#include <sdbusplus/asio/object_server.hpp>
#include <sensor.hpp>
+constexpr const char *gpioPath = "/sys/class/gpio/";
+class PresenceSensor
+{
+
+ public:
+ PresenceSensor(const size_t index, bool inverted,
+ boost::asio::io_service &io);
+ ~PresenceSensor();
+
+ void monitorPresence(void);
+ void read(void);
+ bool getValue(void);
+
+ private:
+ bool status = true;
+ bool inverted;
+ boost::asio::ip::tcp::socket inputDev;
+ int fd;
+};
class TachSensor : public Sensor
{
public:
@@ -12,6 +31,7 @@
TachSensor(const std::string &path,
sdbusplus::asio::object_server &objectServer,
std::shared_ptr<sdbusplus::asio::connection> &conn,
+ std::unique_ptr<PresenceSensor> &&presence,
boost::asio::io_service &io, const std::string &fanName,
std::vector<thresholds::Threshold> &&thresholds,
const std::string &sensorConfiguration);
@@ -21,6 +41,7 @@
std::string path;
sdbusplus::asio::object_server &objServer;
std::shared_ptr<sdbusplus::asio::connection> dbusConnection;
+ std::unique_ptr<PresenceSensor> presence;
boost::asio::posix::stream_descriptor inputDev;
boost::asio::deadline_timer waitTimer;
boost::asio::streambuf readBuf;
diff --git a/sensors/src/FanMain.cpp b/sensors/src/FanMain.cpp
index 4fa9ff2..542ca78 100644
--- a/sensors/src/FanMain.cpp
+++ b/sensors/src/FanMain.cpp
@@ -213,8 +213,35 @@
<< "\n";
}
+ auto presenceConfig =
+ sensorData->find(baseType + std::string(".Presence"));
+
+ std::unique_ptr<PresenceSensor> presenceSensor(nullptr);
+
+ // presence sensors are optional
+ if (presenceConfig != sensorData->end())
+ {
+ auto findIndex = presenceConfig->second.find("Index");
+ auto findPolarity = presenceConfig->second.find("Polarity");
+
+ if (findIndex == presenceConfig->second.end() ||
+ findPolarity == presenceConfig->second.end())
+ {
+ std::cerr << "Malformed Presence Configuration\n";
+ }
+ else
+ {
+ size_t index = variant_ns::get<uint64_t>(findIndex->second);
+ bool inverted =
+ variant_ns::get<std::string>(findPolarity->second) == "Low";
+ presenceSensor =
+ std::make_unique<PresenceSensor>(index, inverted, io);
+ }
+ }
+
tachSensors[sensorName] = std::make_unique<TachSensor>(
- path.string(), objectServer, dbusConnection, io, sensorName,
+ path.string(), objectServer, dbusConnection,
+ std::move(presenceSensor), io, sensorName,
std::move(sensorThresholds), *interfacePath);
}
std::vector<fs::path> pwms;
diff --git a/sensors/src/TachSensor.cpp b/sensors/src/TachSensor.cpp
index e4ff3fd..f7c6fc0 100644
--- a/sensors/src/TachSensor.cpp
+++ b/sensors/src/TachSensor.cpp
@@ -21,6 +21,7 @@
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
+#include <fstream>
#include <iostream>
#include <limits>
#include <sdbusplus/asio/connection.hpp>
@@ -33,11 +34,13 @@
TachSensor::TachSensor(const std::string &path,
sdbusplus::asio::object_server &objectServer,
std::shared_ptr<sdbusplus::asio::connection> &conn,
+ std::unique_ptr<PresenceSensor> &&presence,
boost::asio::io_service &io, const std::string &fanName,
std::vector<thresholds::Threshold> &&_thresholds,
const std::string &sensorConfiguration) :
Sensor(),
path(path), objServer(objectServer), dbusConnection(conn),
+ presence(std::move(presence)),
name(boost::replace_all_copy(fanName, " ", "_")),
configuration(sensorConfiguration),
inputDev(io, open(path.c_str(), O_RDONLY)), waitTimer(io), errCount(0),
@@ -90,46 +93,59 @@
{
return; // we're being destroyed
}
+ bool missing = false;
+ if (presence)
+ {
+ if (!presence->getValue())
+ {
+ sensorInterface->set_property(
+ "Value", std::numeric_limits<double>::quiet_NaN());
+ missing = true;
+ }
+ }
std::istream responseStream(&readBuf);
- if (!err)
+ if (!missing)
{
- std::string response;
- try
+ if (!err)
{
- std::getline(responseStream, response);
- float nvalue = std::stof(response);
- responseStream.clear();
- if (nvalue != value)
+ std::string response;
+ try
{
- updateValue(nvalue);
+ std::getline(responseStream, response);
+ float nvalue = std::stof(response);
+ responseStream.clear();
+ if (nvalue != value)
+ {
+ updateValue(nvalue);
+ }
+ errCount = 0;
}
- errCount = 0;
- }
- catch (const std::invalid_argument &)
- {
- errCount++;
- }
- }
- else
- {
-
- errCount++;
- }
- // only send value update once
- if (errCount == warnAfterErrorCount)
- {
- // only an error if power is on
- if (isPowerOn(dbusConnection))
- {
- std::cerr << "Failure to read sensor " << name << " at " << path
- << "\n";
- updateValue(0);
+ catch (const std::invalid_argument &)
+ {
+ errCount++;
+ }
}
else
{
- errCount = 0; // check power again in 10 cycles
- sensorInterface->set_property(
- "Value", std::numeric_limits<double>::quiet_NaN());
+
+ errCount++;
+ }
+ // only send value update once
+ if (errCount == warnAfterErrorCount)
+ {
+ // only an error if power is on
+ if (isPowerOn(dbusConnection))
+ {
+ std::cerr << "Failure to read sensor " << name << " at " << path
+ << "\n";
+ updateValue(0);
+ }
+ else
+ {
+ errCount = 0; // check power again in 10 cycles
+ sensorInterface->set_property(
+ "Value", std::numeric_limits<double>::quiet_NaN());
+ }
}
}
responseStream.clear();
@@ -140,7 +156,12 @@
return; // we're no longer valid
}
inputDev.assign(fd);
- waitTimer.expires_from_now(boost::posix_time::milliseconds(pwmPollMs));
+ size_t pollTime = pwmPollMs;
+ if (missing)
+ {
+ pollTime *= 10;
+ }
+ waitTimer.expires_from_now(boost::posix_time::milliseconds(pollTime));
waitTimer.async_wait([&](const boost::system::error_code &ec) {
if (ec == boost::asio::error::operation_aborted)
{
@@ -240,3 +261,85 @@
std::cerr << "error initializing critical threshold interface\n";
}
}
+
+PresenceSensor::PresenceSensor(const size_t index, bool inverted,
+ boost::asio::io_service &io) :
+ inverted(inverted),
+ inputDev(io)
+{
+ // todo: use gpiodaemon
+ std::string device = gpioPath + std::string("gpio") + std::to_string(index);
+ fd = open((device + "/value").c_str(), O_RDONLY);
+ if (fd < 0)
+ {
+ std::cerr << "Error opening gpio " << index << "\n";
+ return;
+ }
+
+ std::ofstream deviceFile(device + "/edge");
+ if (!deviceFile.good())
+ {
+ std::cerr << "Error setting edge " << device << "\n";
+ return;
+ }
+ deviceFile << "both";
+ deviceFile.close();
+
+ inputDev.assign(boost::asio::ip::tcp::v4(), fd);
+ monitorPresence();
+ read();
+}
+
+PresenceSensor::~PresenceSensor()
+{
+ inputDev.close();
+ close(fd);
+}
+
+void PresenceSensor::monitorPresence(void)
+{
+ inputDev.async_wait(boost::asio::ip::tcp::socket::wait_error,
+ [this](const boost::system::error_code &ec) {
+ if (ec == boost::system::errc::bad_file_descriptor)
+ {
+ return; // we're being destroyed
+ }
+ else if (ec)
+ {
+ std::cerr
+ << "Error on presence sensor socket\n";
+ }
+ else
+ {
+ read();
+ }
+ monitorPresence();
+ });
+}
+
+void PresenceSensor::read(void)
+{
+ constexpr size_t readSize = sizeof("0");
+ std::string readBuf;
+ readBuf.resize(readSize);
+ lseek(fd, 0, SEEK_SET);
+ size_t r = ::read(fd, readBuf.data(), readSize);
+ if (r != 1)
+ {
+ std::cerr << "Error reading gpio\n";
+ }
+ else
+ {
+ bool value = std::stoi(readBuf);
+ if (inverted)
+ {
+ value = !value;
+ }
+ status = value;
+ }
+}
+
+bool PresenceSensor::getValue(void)
+{
+ return status;
+}
\ No newline at end of file