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 <gmills@us.ibm.com>
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 @@
     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<Presence*>(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 @@
 {
     ObjectMap invObj = getObjectMap(present);
 
+    log<level::INFO>("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 @@
     }
 }
 
+// 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<level::ERR>("Failed to register callback handler",
+                        entry("ERROR=%s", strerror(-rc)));
+        elog<InternalFailure>();
+    }
+}
 
 } // 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 <string>
+#include <systemd/sd-event.h>
 #include <libevdev/libevdev.h>
 #include "file.hpp"
 
@@ -10,6 +11,26 @@
 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<sd_event, EventDeleter>;
+
+/* 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<sd_event_source, EventSourceDeleter>;
+
 /* Need a custom deleter for freeing up evdev struct */
 struct FreeEvDev
 {
@@ -55,23 +76,46 @@
                                    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 @@
         /** @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 <iostream>
+#include <systemd/sd-event.h>
 #include <phosphor-logging/log.hpp>
 #include "argument.hpp"
 #include "gpio_presence.hpp"
@@ -39,9 +40,31 @@
     }
 
     auto bus = sdbusplus::bus::new_default();
-    auto name = options["name"];
-    Presence presence(bus, inventory, path, std::stoul(key), name);
+    auto rc = 0;
+    sd_event* event = nullptr;
+    rc = sd_event_default(&event);
+    if (rc < 0)
+    {
+        log<level::ERR>("Error creating a default sd_event handler");
+        return rc;
+    }
+    EventPtr eventP{event};
+    event = nullptr;
 
-    return 0;
+    auto name = options["name"];
+    Presence presence(bus, inventory, path, std::stoul(key), name, eventP);
+
+    while (true)
+    {
+        // -1 denotes wait forever
+        rc = sd_event_run(eventP.get(), (uint64_t) - 1);
+        if (rc < 0)
+        {
+            log<level::ERR>("Failure in processing request",
+                            entry("ERROR=%s", strerror(-rc)));
+            break;
+        }
+    }
+    return rc;
 }