Add support for GPIO polling for presence detection
The current PresenceGpio implementation uses event driven detection by
using the gpioLine.event_read method to detect GPIO signal changes, and
then to determine the presence status.
This commit will add support for a PollingPresenceGpio for GPIOs that
do not support events. It will use a pollTimer to periodically read the
GPIO status (every second).
The monitorPresence() function should be called to initiate the
monitoring of the GPIO.
TEST: Tested with multiple GPIOs including disabling to simulate
removing of device and enable to re-detect device. Unable to test Event
driven GPIO due to no hw available.
Change-Id: If46e884ad237dfe909a9373773c8302a0844ac90
Signed-off-by: Chris Cain <cjcain@us.ibm.com>
diff --git a/src/fan/PresenceGpio.cpp b/src/fan/PresenceGpio.cpp
index 6c528d7..d917c82 100644
--- a/src/fan/PresenceGpio.cpp
+++ b/src/fan/PresenceGpio.cpp
@@ -20,20 +20,36 @@
#include <boost/asio/posix/stream_descriptor.hpp>
#include <gpiod.hpp>
+#include <chrono>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <system_error>
+static constexpr unsigned int pollIntervalSec = 1;
+
+PresenceGpio::PresenceGpio(const std::string& deviceType,
+ const std::string& deviceName,
+ const std::string& gpioName) :
+ deviceType(deviceType), deviceName(deviceName), gpioName(gpioName)
+{
+ gpioLine = gpiod::find_line(gpioName);
+ if (!gpioLine)
+ {
+ std::cerr << "Error requesting gpio: " << gpioName << "\n";
+ throw std::runtime_error("Failed to find GPIO " + gpioName);
+ }
+}
+
PresenceGpio::~PresenceGpio()
{
gpioLine.release();
}
-void PresenceGpio::updateAndTracePresence()
+void PresenceGpio::updateAndTracePresence(int newValue)
{
- status = (gpioLine.get_value() != 0);
+ status = (newValue != 0);
if (status)
{
logPresent(deviceName);
@@ -45,41 +61,32 @@
}
EventPresenceGpio::EventPresenceGpio(
- const std::string& iDeviceType, const std::string& iDeviceName,
+ const std::string& deviceType, const std::string& deviceName,
const std::string& gpioName, bool inverted, boost::asio::io_context& io) :
- PresenceGpio(iDeviceType, iDeviceName), gpioFd(io)
+ PresenceGpio(deviceType, deviceName, gpioName), gpioFd(io)
{
- gpioLine = gpiod::find_line(gpioName);
- if (!gpioLine)
- {
- std::cerr << "Error requesting gpio: " << gpioName << "\n";
- return;
- }
-
try
{
gpioLine.request(
{deviceType + "Sensor", gpiod::line_request::EVENT_BOTH_EDGES,
inverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
- updateAndTracePresence();
-
- int gpioLineFd = gpioLine.event_get_fd();
- if (gpioLineFd < 0)
- {
- std::cerr << "Failed to get " << gpioName << " fd\n";
- throw std::runtime_error("Failed to get GPIO fd " + gpioName);
- }
-
- gpioFd.assign(gpioLineFd);
+ updateAndTracePresence(gpioLine.get_value());
}
catch (const std::system_error& e)
{
std::cerr << "Error reading gpio " << gpioName << ": " << e.what()
<< "\n";
- return;
+ throw std::runtime_error("Failed to read GPIO fd " + gpioName);
}
- monitorPresence();
+ int gpioLineFd = gpioLine.event_get_fd();
+ if (gpioLineFd < 0)
+ {
+ std::cerr << "Failed to get " << gpioName << " fd\n";
+ throw std::runtime_error("Failed to get GPIO fd " + gpioName);
+ }
+
+ gpioFd.assign(gpioLineFd);
}
void EventPresenceGpio::monitorPresence()
@@ -114,5 +121,65 @@
{
// Read is invoked when an edge event is detected by monitorPresence
gpioLine.event_read();
- updateAndTracePresence();
+ updateAndTracePresence(gpioLine.get_value());
+}
+
+PollingPresenceGpio::PollingPresenceGpio(
+ const std::string& deviceType, const std::string& deviceName,
+ const std::string& gpioName, bool inverted, boost::asio::io_context& io) :
+ PresenceGpio(deviceType, deviceName, gpioName), pollTimer(io)
+{
+ try
+ {
+ gpioLine.request(
+ {deviceType + "Sensor", gpiod::line_request::DIRECTION_INPUT,
+ inverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
+ updateAndTracePresence(gpioLine.get_value());
+ }
+ catch (const std::system_error& e)
+ {
+ std::cerr << "PollingPresenceGpio: Error reading gpio " << gpioName
+ << ": " << e.what() << "\n";
+ status = false;
+ throw std::runtime_error("Failed to get Polling GPIO fd " + gpioName);
+ }
+}
+
+inline void PollingPresenceGpio::pollTimerHandler(
+ const std::weak_ptr<PollingPresenceGpio>& weakRef,
+ const boost::system::error_code& ec)
+{
+ std::shared_ptr<PollingPresenceGpio> self = weakRef.lock();
+ if (!self)
+ {
+ std::cerr << "Failed to get lock for pollingPresenceGpio: "
+ << ec.message() << "\n";
+ return;
+ }
+ if (ec)
+ {
+ if (ec != boost::system::errc::bad_file_descriptor)
+ {
+ std::cerr << "GPIO polling timer failed for " << self->gpioName
+ << ": " << ec.what() << ")\n";
+ }
+ return;
+ }
+ self->monitorPresence();
+}
+
+void PollingPresenceGpio::monitorPresence()
+{
+ // Determine if the value has changed
+ int newStatus = gpioLine.get_value();
+ if (static_cast<int>(status) != newStatus)
+ {
+ updateAndTracePresence(newStatus);
+ }
+
+ std::weak_ptr<PollingPresenceGpio> weakRef = weak_from_this();
+ pollTimer.expires_after(std::chrono::seconds(pollIntervalSec));
+ pollTimer.async_wait([weakRef](const boost::system::error_code& ec) {
+ pollTimerHandler(weakRef, ec);
+ });
}