Refactor GPIO Presence class

Refactor the GPIO Presence class to use a common class, Evdev,
to inherit from. Evdev does the basic libevdev handling.
A later commit moves monitor.hpp to this common class as well.

Change-Id: I3c872680c88f8f400fefe3af49eb9b84c7491ceb
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
diff --git a/evdev.cpp b/evdev.cpp
new file mode 100644
index 0000000..e8e98e2
--- /dev/null
+++ b/evdev.cpp
@@ -0,0 +1,72 @@
+#include <libevdev/libevdev.h>
+#include <fcntl.h>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include "xyz/openbmc_project/Common/error.hpp"
+#include "evdev.hpp"
+
+namespace phosphor
+{
+namespace gpio
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+// Populate the file descriptor for passed in device
+int Evdev::openDevice()
+{
+    using namespace phosphor::logging;
+
+    auto fd = open(path.c_str(), O_RDONLY | O_NONBLOCK);
+    if (fd < 0)
+    {
+        log<level::ERR>("Failed to open device path",
+                        entry("DEVICEPATH=%s", path.c_str()),
+                        entry("ERRNO=%d", errno));
+        elog<InternalFailure>();
+    }
+    return fd;
+}
+
+// Initializes the event device with the fd
+void Evdev::initEvDev()
+{
+    if (devicePtr)
+    {
+        // Init can be done only once per device
+        return;
+    }
+
+    struct libevdev* evdev = nullptr;
+    auto rc = libevdev_new_from_fd((fd)(), &evdev);
+    if (rc < 0)
+    {
+        log<level::ERR>("Failed to initialize evdev");
+        elog<InternalFailure>();
+        return;
+    }
+
+    // Packing in the unique_ptr
+    devicePtr.reset(evdev);
+}
+
+// Attaches the FD to event loop and registers the callback handler
+void Evdev::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 gpio
+} // namespace phosphor
diff --git a/evdev.hpp b/evdev.hpp
new file mode 100644
index 0000000..b3189f1
--- /dev/null
+++ b/evdev.hpp
@@ -0,0 +1,132 @@
+#pragma once
+#include <string>
+#include <systemd/sd-event.h>
+#include <libevdev/libevdev.h>
+#include "file.hpp"
+
+namespace phosphor
+{
+namespace gpio
+{
+
+/* 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
+{
+    void operator()(struct libevdev* device) const
+    {
+        libevdev_free(device);
+    }
+};
+using EvdevPtr = std::unique_ptr<struct libevdev, FreeEvDev>;
+
+/** @class Evdev
+ *  @brief Responsible for catching GPIO state changes conditions and taking
+ *  actions
+ */
+class Evdev
+{
+
+        using Property = std::string;
+        using Value = sdbusplus::message::variant<bool, std::string>;
+        // Association between property and its value
+        using PropertyMap = std::map<Property, Value>;
+        using Interface = std::string;
+        // Association between interface and the D-Bus property
+        using InterfaceMap = std::map<Interface, PropertyMap>;
+        using Object = sdbusplus::message::object_path;
+        // Association between object and the interface
+        using ObjectMap = std::map<Object, InterfaceMap>;
+
+    public:
+        Evdev() = delete;
+        ~Evdev() = default;
+        Evdev(const Evdev&) = delete;
+        Evdev& operator=(const Evdev&) = delete;
+        Evdev(Evdev&&) = delete;
+        Evdev& operator=(Evdev&&) = delete;
+
+        /** @brief Constructs Evdev object.
+         *
+         *  @param[in] path      - Device path to read for GPIO pin state
+         *  @param[in] key       - GPIO key to monitor
+         *  @param[in] event     - sd_event handler
+         *  @param[in] handler   - IO callback handler. Defaults to one in this
+         *                        class
+         *  @param[in] useEvDev  - Whether to use EvDev to retrieve events
+         */
+        Evdev(const std::string& path,
+              const unsigned int key,
+              EventPtr& event,
+              sd_event_io_handler_t handler,
+              bool useEvDev = true) :
+            path(path),
+            key(key),
+            event(event),
+            callbackHandler(handler),
+            fd(openDevice())
+
+        {
+            if (useEvDev)
+            {
+                // If we are asked to use EvDev, do that initialization.
+                initEvDev();
+            }
+
+            // Register callback handler when FD has some data
+            registerCallback();
+        }
+
+    protected:
+        /** @brief Device path to read for GPIO pin state */
+        const std::string path;
+
+        /** @brief GPIO key to monitor */
+        const unsigned int key;
+
+        /** @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 Initializes evdev handle with the fd */
+        void initEvDev();
+};
+
+} // namespace gpio
+} // namespace phosphor
diff --git a/presence/Makefile.am b/presence/Makefile.am
index 8af030c..7cc80d4 100644
--- a/presence/Makefile.am
+++ b/presence/Makefile.am
@@ -7,6 +7,7 @@
 phosphor_gpio_presence_SOURCES = \
 	main.cpp \
 	argument.cpp \
+	../evdev.cpp \
 	gpio_presence.cpp
 
 phosphor_gpio_presence_CXXFLAGS = \
diff --git a/presence/gpio_presence.cpp b/presence/gpio_presence.cpp
index 0390af6..b87e139 100644
--- a/presence/gpio_presence.cpp
+++ b/presence/gpio_presence.cpp
@@ -60,44 +60,6 @@
     return mapperResponse.begin()->first;
 }
 
-// Populate the file descriptor for passed in device
-int Presence::openDevice()
-{
-    using namespace phosphor::logging;
-
-    auto fd = open(path.c_str(), O_RDONLY | O_NONBLOCK);
-    if (fd < 0)
-    {
-        log<level::ERR>("Failed to open device path",
-                        entry("DEVICEPATH=%s", path.c_str()),
-                        entry("ERRNO=%d", errno));
-        elog<InternalFailure>();
-    }
-    return fd;
-}
-
-// Initializes the event device with the fd
-void Presence::initEvDev()
-{
-    if (devicePtr)
-    {
-        // Init can be done only once per device
-        return;
-    }
-
-    struct libevdev* evdev = nullptr;
-    auto rc = libevdev_new_from_fd((fd)(), &evdev);
-    if (rc < 0)
-    {
-        log<level::ERR>("Failed to initialize evdev");
-        elog<InternalFailure>();
-        return;
-    }
-
-    // Packing in the unique_ptr
-    devicePtr.reset(evdev);
-}
-
 void Presence::determinePresence()
 {
     auto present = false;
@@ -213,21 +175,6 @@
     }
 }
 
-// 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 e8f68ca..986dbe6 100644
--- a/presence/gpio_presence.hpp
+++ b/presence/gpio_presence.hpp
@@ -1,8 +1,7 @@
 #pragma once
 #include <string>
 #include <systemd/sd-event.h>
-#include <libevdev/libevdev.h>
-#include "file.hpp"
+#include "evdev.hpp"
 
 namespace phosphor
 {
@@ -11,41 +10,12 @@
 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
-{
-    void operator()(struct libevdev* device) const
-    {
-        libevdev_free(device);
-    }
-};
-using EvdevPtr = std::unique_ptr<struct libevdev, FreeEvDev>;
-
 /** @class Presence
- *  @brief Responsible for determining and monitoring presence of
- *  inventory items and updating D-Bus accordingly.
+ *  @brief Responsible for determining and monitoring presence,
+ *  by monitoring GPIO state changes, of inventory items and
+ *  updating D-Bus accordingly.
  */
-class Presence
+class Presence : public Evdev
 {
 
         using Property = std::string;
@@ -87,20 +57,12 @@
                  const std::string& name,
                  EventPtr& event,
                  sd_event_io_handler_t handler = Presence::processEvents) :
+            Evdev(path, key, event, handler, true),
             bus(bus),
             inventory(inventory),
-            path(path),
-            key(key),
-            name(name),
-            event(event),
-            callbackHandler(handler),
-            fd(openDevice())
-
+            name(name)
         {
-            initEvDev();
             determinePresence();
-            // Register callback handler when FD has some data
-            registerCallback();
         }
 
         /** @brief Callback handler when the FD has some activity on it
@@ -145,42 +107,11 @@
         /** @brief Object path under inventory to display this inventory item */
         const std::string inventory;
 
-        /** @brief Device path to read for GPIO pin state
-                   to determine presence of inventory item */
-        const std::string path;
-
-        /** @brief GPIO key to monitor */
-        const unsigned int key;
-
         /** @brief Pretty name of the inventory item*/
         const std::string name;
 
-        /** @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();
 };
 
 /**