From 765725e057046784ceb4e7595e44f2e4c2ef6ae2 Mon Sep 17 00:00:00 2001 From: Gunnar Mills Date: Thu, 6 Jul 2017 14:17:44 -0500 Subject: Monitor for GPIO state change Use libevdev to monitor for GPIO state change and update item present accordingly. Change-Id: I1959a5ea09a7570c096b05d78a190394767a5ddd Signed-off-by: Gunnar Mills --- presence/gpio_presence.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++++++ presence/gpio_presence.hpp | 61 ++++++++++++++++++++++++++++++++++++++- presence/main.cpp | 27 +++++++++++++++-- 3 files changed, 157 insertions(+), 3 deletions(-) diff --git a/presence/gpio_presence.cpp b/presence/gpio_presence.cpp index ba897d0..0390af6 100644 --- a/presence/gpio_presence.cpp +++ b/presence/gpio_presence.cpp @@ -119,6 +119,59 @@ void Presence::determinePresence() updateInventory(present); } +// Callback handler when there is an activity on the FD +int Presence::processEvents(sd_event_source* es, int fd, + uint32_t revents, void* userData) +{ + auto presence = static_cast(userData); + + presence->analyzeEvent(); + return 0; +} + + +// Analyzes the GPIO event +void Presence::analyzeEvent() +{ + + // Data returned + struct input_event ev {}; + int rc = 0; + + // While testing, observed that not having a loop here was leading + // into events being missed. + while (rc >= 0) + { + // Wait until no more events are available on the device. + rc = libevdev_next_event(devicePtr.get(), + LIBEVDEV_READ_FLAG_NORMAL, &ev); + if (rc < 0) + { + // There was an error waiting for events, mostly that there are no + // events to be read.. So continue waiting... + return; + } + + if (rc == LIBEVDEV_READ_STATUS_SUCCESS) + { + if (ev.type == EV_SYN && ev.code == SYN_REPORT) + { + continue; + } + else if (ev.code == key) + { + auto present = false; + if (ev.value > 0) + { + present = true; + } + updateInventory(present); + } + } + } + + return; +} Presence::ObjectMap Presence::getObjectMap(bool present) { @@ -139,6 +192,10 @@ void Presence::updateInventory(bool present) { ObjectMap invObj = getObjectMap(present); + log("Updating inventory present property", + entry("PRESENT=%d", present), + entry("PATH=%s", inventory)); + auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus); // Update inventory @@ -156,6 +213,21 @@ void Presence::updateInventory(bool present) } } +// Attaches the FD to event loop and registers the callback handler +void Presence::registerCallback() +{ + decltype(eventSource.get()) sourcePtr = nullptr; + auto rc = sd_event_add_io(event.get(), &sourcePtr, (fd)(), + EPOLLIN, callbackHandler, this); + eventSource.reset(sourcePtr); + + if (rc < 0) + { + log("Failed to register callback handler", + entry("ERROR=%s", strerror(-rc))); + elog(); + } +} } // namespace presence } // namespace gpio diff --git a/presence/gpio_presence.hpp b/presence/gpio_presence.hpp index 47d2dc7..e8f68ca 100644 --- a/presence/gpio_presence.hpp +++ b/presence/gpio_presence.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include "file.hpp" @@ -10,6 +11,26 @@ namespace gpio namespace presence { +/* Need a custom deleter for freeing up sd_event */ +struct EventDeleter +{ + void operator()(sd_event* event) const + { + event = sd_event_unref(event); + } +}; +using EventPtr = std::unique_ptr; + +/* Need a custom deleter for freeing up sd_event_source */ +struct EventSourceDeleter +{ + void operator()(sd_event_source* eventSource) const + { + eventSource = sd_event_source_unref(eventSource); + } +}; +using EventSourcePtr = std::unique_ptr; + /* Need a custom deleter for freeing up evdev struct */ struct FreeEvDev { @@ -55,23 +76,46 @@ class Presence to determine presence of inventory item * @param[in] key - GPIO key to monitor * @param[in] name - Pretty name of the inventory item + * @param[in] event - sd_event handler + * @param[in] handler - IO callback handler. Defaults to one in this + * class */ Presence(sdbusplus::bus::bus& bus, const std::string& inventory, const std::string& path, const unsigned int key, - const std::string& name) : + const std::string& name, + EventPtr& event, + sd_event_io_handler_t handler = Presence::processEvents) : bus(bus), inventory(inventory), path(path), key(key), name(name), + event(event), + callbackHandler(handler), fd(openDevice()) + { initEvDev(); determinePresence(); + // Register callback handler when FD has some data + registerCallback(); } + /** @brief Callback handler when the FD has some activity on it + * + * @param[in] es - Populated event source + * @param[in] fd - Associated File descriptor + * @param[in] revents - Type of event + * @param[in] userData - User data that was passed during registration + * + * @return - 0 or positive number on success and negative + * errno otherwise + */ + static int processEvents(sd_event_source* es, int fd, + uint32_t revents, void* userData); + private: /** * @brief Update the present property for the inventory item. @@ -114,12 +158,27 @@ class Presence /** @brief Event structure */ EvdevPtr devicePtr; + /** @brief Monitor to sd_event */ + EventPtr& event; + + /** @brief Callback handler when the FD has some data */ + sd_event_io_handler_t callbackHandler; + + /** @brief event source */ + EventSourcePtr eventSource; + /** @brief Opens the device and populates the descriptor */ int openDevice(); + /** @brief attaches FD to events and sets up callback handler */ + void registerCallback(); + /** @brief File descriptor manager */ FileDescriptor fd; + /** @brief Analyzes the GPIO event and update present property*/ + void analyzeEvent(); + /** @brief Initializes evdev handle with the fd */ void initEvDev(); }; diff --git a/presence/main.cpp b/presence/main.cpp index f6cb665..6116540 100644 --- a/presence/main.cpp +++ b/presence/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "argument.hpp" #include "gpio_presence.hpp" @@ -39,9 +40,31 @@ int main(int argc, char* argv[]) } auto bus = sdbusplus::bus::new_default(); + auto rc = 0; + sd_event* event = nullptr; + rc = sd_event_default(&event); + if (rc < 0) + { + log("Error creating a default sd_event handler"); + return rc; + } + EventPtr eventP{event}; + event = nullptr; + auto name = options["name"]; - Presence presence(bus, inventory, path, std::stoul(key), name); + Presence presence(bus, inventory, path, std::stoul(key), name, eventP); - return 0; + while (true) + { + // -1 denotes wait forever + rc = sd_event_run(eventP.get(), (uint64_t) - 1); + if (rc < 0) + { + log("Failure in processing request", + entry("ERROR=%s", strerror(-rc))); + break; + } + } + return rc; } -- cgit v1.2.1