Read GPIO key on startup

Create class Presence which will be responsible for
determining and monitoring presence of inventory items
and updating D-Bus accordingly. With this commit
class Presence only reads the GPIO key on startup,
more to come later.

Change-Id: I647ae11d42a813a103e6d9d8922fd0f5b2155132
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
diff --git a/presence/Makefile.am b/presence/Makefile.am
index f66c0e7..8af030c 100644
--- a/presence/Makefile.am
+++ b/presence/Makefile.am
@@ -10,8 +10,12 @@
 	gpio_presence.cpp
 
 phosphor_gpio_presence_CXXFLAGS = \
-	$(PHOSPHOR_LOGGING_CFLAGS)
+	$(PHOSPHOR_LOGGING_CFLAGS) \
+	$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
+	$(LIBEVDEV_CFLAGS)
 
 phosphor_gpio_presence_LDADD = \
-	$(PHOSPHOR_LOGGING_LIBS)
+	$(PHOSPHOR_LOGGING_LIBS) \
+	$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+	$(LIBEVDEV_LIBS)
 
diff --git a/presence/gpio_presence.cpp b/presence/gpio_presence.cpp
index 11a32cd..710e4ae 100644
--- a/presence/gpio_presence.cpp
+++ b/presence/gpio_presence.cpp
@@ -1,3 +1,9 @@
+#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 "gpio_presence.hpp"
 
 namespace phosphor
@@ -7,6 +13,61 @@
 namespace presence
 {
 
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+// 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 value = static_cast<int>(0);
+    auto fetch_rc = libevdev_fetch_event_value(devicePtr.get(), EV_KEY,
+                    key, &value);
+    if (0 == fetch_rc)
+    {
+        log<level::ERR>("Device does not support event type",
+                        entry("KEYCODE=%d", key));
+        elog<InternalFailure>();
+        return;
+    }
+}
+
 } // namespace presence
 } // namespace gpio
 } // namespace phosphor
diff --git a/presence/gpio_presence.hpp b/presence/gpio_presence.hpp
index 67fd4cb..3faa7e1 100644
--- a/presence/gpio_presence.hpp
+++ b/presence/gpio_presence.hpp
@@ -1,4 +1,7 @@
 #pragma once
+#include <string>
+#include <libevdev/libevdev.h>
+#include "file.hpp"
 
 namespace phosphor
 {
@@ -7,6 +10,87 @@
 namespace presence
 {
 
+/* 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.
+ */
+class Presence
+{
+
+    public:
+        Presence() = delete;
+        ~Presence() = default;
+        Presence(const Presence&) = delete;
+        Presence& operator=(const Presence&) = delete;
+        Presence(Presence&&) = delete;
+        Presence& operator=(Presence&&) = delete;
+
+        /** @brief Constructs Presence object.
+         *
+         *  @param[in] inventory - Object path under inventory
+                                   to display this inventory item
+         *  @param[in] path      - Device path to read for GPIO pin state
+                                   to determine presence of inventory item
+         *  @param[in] key       - GPIO key to monitor
+         *  @param[in] name      - Pretty name of the inventory item
+         */
+        Presence(const std::string& inventory,
+                 const std::string& path,
+                 const unsigned int key,
+                 const std::string& name) :
+            inventory(inventory),
+            path(path),
+            key(key),
+            name(name),
+            fd(openDevice())
+        {
+            initEvDev();
+            determinePresence();
+        }
+
+    private:
+        /**
+         * @brief Read the GPIO device to determine initial presence and set
+         *        present property at D-Bus path.
+         **/
+        void determinePresence();
+
+        /** @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 Opens the device and populates the descriptor */
+        int openDevice();
+
+        /** @brief File descriptor manager */
+        FileDescriptor fd;
+
+        /** @brief Initializes evdev handle with the fd */
+        void initEvDev();
+};
+
 } // namespace presence
 } // namespace gpio
 } // namespace phosphor
diff --git a/presence/main.cpp b/presence/main.cpp
index 3c7c3cf..8935568 100644
--- a/presence/main.cpp
+++ b/presence/main.cpp
@@ -5,6 +5,7 @@
 
 using namespace phosphor::logging;
 using namespace phosphor::gpio;
+using namespace phosphor::gpio::presence;
 
 int main(int argc, char* argv[])
 {
@@ -36,6 +37,7 @@
         std::cerr << "Device path argument required\n";
         options.usage(argv);
     }
+    Presence presence(inventory, path, std::stoul(key), options["name"]);
 
     return 0;
 }