Attention Handler gpio monitor

Register an async-io service for monitoring the attention GPIO and use the attention handler as the GPIO event handler. The GPIO monitor will respond to changes on the attention GPIO and in response will call the attention handler base logic to handle the attention events.

Signed-off-by: Ben Tyner <ben.tyner@ibm.com>
Change-Id: I3cdc0cb34bc3067b54cb64ab49d98fa2182e7fa1
diff --git a/attn/attn_handler.hpp b/attn/attn_handler.hpp
index 2be6b07..e8fb312 100644
--- a/attn/attn_handler.hpp
+++ b/attn/attn_handler.hpp
@@ -1,5 +1,8 @@
 #pragma once
 
+namespace attn
+{
+
 /**
  * @brief The main attention handler logic
  *
@@ -19,3 +22,5 @@
  *            Recoverable: TBD
  */
 void attnHandler();
+
+} // namespace attn
diff --git a/attn/attn_main.cpp b/attn/attn_main.cpp
index dda1fc8..e4766f0 100644
--- a/attn/attn_main.cpp
+++ b/attn/attn_main.cpp
@@ -1,6 +1,7 @@
 #include <libpdbg.h>
 
 #include <attn_handler.hpp>
+#include <attn_monitor.hpp>
 
 /**
  * @brief Attention handler application main()
@@ -14,8 +15,34 @@
 {
     int rc = 0; // return code
 
+    gpiod_line* line; // gpio line to monitor
+
+    boost::asio::io_service io; // async io monitoring service
+
     // initialize pdbg targets
     pdbg_targets_init(nullptr);
 
+    // GPIO line configuration (falling edge, active low)
+    struct gpiod_line_request_config config
+    {
+        "attention", GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE, 0
+    };
+
+    // get handle to attention GPIO line
+    line = gpiod_line_get("gpiochip0", 74);
+
+    if (nullptr == line)
+    {
+        rc = 1; // error
+    }
+    else
+    {
+        // Creating a vector of one gpio to monitor
+        std::vector<std::unique_ptr<attn::AttnMonitor>> gpios;
+        gpios.push_back(std::make_unique<attn::AttnMonitor>(line, config, io));
+
+        io.run(); // start GPIO monitor
+    }
+
     return rc;
 }
diff --git a/attn/attn_monitor.cpp b/attn/attn_monitor.cpp
new file mode 100644
index 0000000..2db8af1
--- /dev/null
+++ b/attn/attn_monitor.cpp
@@ -0,0 +1,102 @@
+#include "attn_monitor.hpp"
+
+#include "attn_handler.hpp"
+
+#include <phosphor-logging/log.hpp>
+
+using namespace phosphor::logging;
+
+namespace attn
+{
+
+/** @brief Register a callback for gpio event */
+void AttnMonitor::scheduleGPIOEvent()
+{
+    std::string logMessage = "[ATTN] ... waiting for events ...";
+    log<level::INFO>(logMessage.c_str());
+
+    // Register async callback, note that callback is a
+    // lambda function with "this" pointer captured
+    iv_gpioEventDescriptor.async_wait(
+        boost::asio::posix::stream_descriptor::wait_read,
+        [this](const boost::system::error_code& ec) {
+            if (ec)
+            {
+                std::string logMessage = "[ATTN] ATTN GPIO Async error: " +
+                                         std::string(ec.message());
+                log<level::INFO>(logMessage.c_str());
+            }
+            else
+            {
+                handleGPIOEvent(); // gpio trigger detected
+            }
+            return;
+        }); // register async callback
+}
+
+/** @brief Handle the GPIO state change event */
+void AttnMonitor::handleGPIOEvent()
+{
+    gpiod_line_event gpioEvent;
+    std::string logMessage;
+
+    if (gpiod_line_event_read_fd(iv_gpioEventDescriptor.native_handle(),
+                                 &gpioEvent) < 0)
+    {
+        logMessage = "[ATTN] ATTN GPIO Failed can't read file descriptor!";
+        log<level::INFO>(logMessage.c_str());
+    }
+    else
+    {
+        switch (gpiod_line_get_value(iv_gpioLine))
+        {
+            // active attention when gpio == 0
+            case 0:
+                attnHandler();
+                break;
+
+            // gpio == 1, GPIO handler should not be executing
+            case 1:
+                logMessage = "[ATTN] ATTN GPIO sync!";
+                log<level::INFO>(logMessage.c_str());
+                break;
+
+            // unexpected value
+            default:
+                logMessage = "[ATTN] ATTN GPIO read unexpected valuel!";
+                log<level::INFO>(logMessage.c_str());
+        }
+    }
+    scheduleGPIOEvent(); // continue monitoring gpio
+}
+
+/** @brief Request a GPIO line for monitoring attention events */
+void AttnMonitor::requestGPIOEvent()
+{
+    if (0 != gpiod_line_request(iv_gpioLine, &iv_gpioConfig, 0))
+    {
+        std::string logMessage = "[ATTN] failed request for GPIO";
+        log<level::INFO>(logMessage.c_str());
+    }
+    else
+    {
+        int gpioLineFd;
+
+        gpioLineFd = gpiod_line_event_get_fd(iv_gpioLine);
+        if (gpioLineFd < 0)
+        {
+            std::string logMessage = "[ATTN] failed to get file descriptor";
+            log<level::INFO>(logMessage.c_str());
+        }
+        else
+        {
+            // Register file descriptor for monitoring
+            iv_gpioEventDescriptor.assign(gpioLineFd);
+
+            // Start monitoring
+            scheduleGPIOEvent();
+        }
+    }
+}
+
+} // namespace attn
diff --git a/attn/attn_monitor.hpp b/attn/attn_monitor.hpp
new file mode 100644
index 0000000..5231d7e
--- /dev/null
+++ b/attn/attn_monitor.hpp
@@ -0,0 +1,71 @@
+#pragma once
+
+#include <gpiod.h>
+
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/posix/stream_descriptor.hpp>
+
+namespace attn
+{
+
+/**
+ *  @brief Responsible for monitoring attention GPIO state change
+ */
+class AttnMonitor
+{
+  public:
+    AttnMonitor()  = delete;
+    ~AttnMonitor() = default;
+
+    /** @brief Constructs AttnMonitor object.
+     *
+     * The AttnMonitor constructor will create a new object and start
+     * the objects associated GPIO listener.
+     *
+     * @param line     GPIO line handle
+     * @param config   configuration of line
+     * @param io       io service
+     */
+    AttnMonitor(gpiod_line* line, gpiod_line_request_config& config,
+                boost::asio::io_service& io) :
+        iv_gpioLine(line),
+        iv_gpioConfig(config), iv_gpioEventDescriptor(io)
+    {
+
+        requestGPIOEvent(); // registers the event handler
+    }
+
+    // delete copy constructor
+    AttnMonitor(const AttnMonitor&) = delete;
+
+    // delete assignment operator
+    AttnMonitor& operator=(const AttnMonitor&) = delete;
+
+    // delere move copy consructor
+    AttnMonitor(AttnMonitor&&) = delete;
+
+    // delete move assignment operator
+    AttnMonitor& operator=(AttnMonitor&&) = delete;
+
+  private: // instance variables
+    /** @brief gpiod handle to gpio line */
+    gpiod_line* iv_gpioLine;
+
+    /** @brief gpiod line config data */
+    gpiod_line_request_config iv_gpioConfig;
+
+    /** @brief GPIO event descriptor */
+    boost::asio::posix::stream_descriptor iv_gpioEventDescriptor;
+
+  private: // class methods
+    /** @brief schedule a gpio event handler */
+    void scheduleGPIOEvent();
+
+    /** @brief handle the GPIO event */
+    void handleGPIOEvent();
+
+    /** @brief register for a gpio event */
+    void requestGPIOEvent();
+};
+
+} // namespace attn
diff --git a/attn/meson.build b/attn/meson.build
index d215067..5419c7a 100644
--- a/attn/meson.build
+++ b/attn/meson.build
@@ -1,9 +1,17 @@
 # needed to find external libraries not registered with package manager
 cmplr = meson.get_compiler('cpp')
 
+# async gpio monitor needs boost library
+boost_args = ['-DBOOST_ASIO_DISABLE_THREADS',
+              '-DBOOST_ERROR_CODE_HEADER_ONLY',
+              '-DBOOST_SYSTEM_NO_DEPRECATED']
+
 # dependency to link dbus support
 sdbusplus = dependency('sdbusplus', version : '>=1.0')
 
+# dependency to link gpiod support
+libgpiod = dependency('libgpiod', version : '>=1.4.1')
+
 # dependency to link libpdbg support
 libpdbg = cmplr.find_library('pdbg')
 
@@ -12,7 +20,8 @@
 no_whole_archive = declare_dependency(link_args : '-Wl,--no-whole-archive')
 
 executable('attn_handler',
-           'attn_main.cpp', 'attn_handler.cpp',
+           'attn_main.cpp', 'attn_handler.cpp', 'attn_monitor.cpp',
            dependencies : [whole_archive, libpdbg,
-                           no_whole_archive, sdbusplus],
+                           no_whole_archive, sdbusplus, libgpiod],
+           cpp_args : boost_args,
            install : true)