Move GPIO presence object to its own files

This commit moves and renames the PresenceSensor to PresenceGpio
from TachSensors to its own file so that it can be used for more
than just fans.
It is currently only used for GPIO presence monitoring for fans.

This commit maintains the same functionality as the original code.
Once the objects is created for a GPIO, it will be monitoring for
gpiod::line_request::EVENT_BOTH_EDGES changes. The user can call
isPresent() anytime to read the presence status.

Interface:
    EventPresenceGpio(const std::string& iDeviceType,
                      const std::string& iDeviceName,
                      const std::string& gpioName, bool inverted,
                      boost::asio::io_context& io);
Sample usage:
    presenceGpio = std::make_unique<EventPresenceGpio>(
        "Fan", "Fan4b", "FAN4_PRESENCE_R_N", true, io);
    if (presenceGpio->isPresent())
    {
        // Fan is present
    }

Testing: Unable to test this update because no hardware supporting
event driven GPIO detection available.

Change-Id: I1f1a4cbab39d3e3ab38b30288f6aa199ee0cfe3c
Signed-off-by: Chris Cain <cjcain@us.ibm.com>
diff --git a/src/FanMain.cpp b/src/FanMain.cpp
index 4087472..736d07d 100644
--- a/src/FanMain.cpp
+++ b/src/FanMain.cpp
@@ -14,6 +14,7 @@
 // limitations under the License.
 */
 
+#include "PresenceGpio.hpp"
 #include "PwmSensor.hpp"
 #include "TachSensor.hpp"
 #include "Thresholds.hpp"
@@ -274,8 +275,8 @@
         tachSensors,
     boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>&
         pwmSensors,
-    boost::container::flat_map<std::string, std::weak_ptr<PresenceSensor>>&
-        presenceSensors,
+    boost::container::flat_map<std::string, std::weak_ptr<PresenceGpio>>&
+        presenceGpios,
     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
     const std::shared_ptr<boost::container::flat_set<std::string>>&
         sensorsChanged,
@@ -283,7 +284,7 @@
 {
     auto getter = std::make_shared<GetSensorConfiguration>(
         dbusConnection,
-        [&io, &objectServer, &tachSensors, &pwmSensors, &presenceSensors,
+        [&io, &objectServer, &tachSensors, &pwmSensors, &presenceGpios,
          &dbusConnection,
          sensorsChanged](const ManagedObjectType& sensorConfigurations) {
             bool firstScan = sensorsChanged == nullptr;
@@ -435,7 +436,7 @@
                 auto presenceConfig =
                     sensorData->find(cfgIntf + std::string(".Presence"));
 
-                std::shared_ptr<PresenceSensor> presenceSensor(nullptr);
+                std::shared_ptr<PresenceGpio> presenceGpio(nullptr);
 
                 // presence sensors are optional
                 if (presenceConfig != sensorData->end())
@@ -457,22 +458,23 @@
 
                         if (pinName != nullptr)
                         {
-                            auto findPresenceSensor =
-                                presenceSensors.find(*pinName);
-                            if (findPresenceSensor != presenceSensors.end())
+                            auto findPresenceGpio =
+                                presenceGpios.find(*pinName);
+                            if (findPresenceGpio != presenceGpios.end())
                             {
-                                auto p = findPresenceSensor->second.lock();
+                                auto p = findPresenceGpio->second.lock();
                                 if (p)
                                 {
-                                    presenceSensor = p;
+                                    presenceGpio = p;
                                 }
                             }
-                            if (!presenceSensor)
+                            if (!presenceGpio)
                             {
-                                presenceSensor =
-                                    std::make_shared<PresenceSensor>(
-                                        *pinName, inverted, io, sensorName);
-                                presenceSensors[*pinName] = presenceSensor;
+                                presenceGpio =
+                                    std::make_shared<EventPresenceGpio>(
+                                        "Fan", sensorName, *pinName, inverted,
+                                        io);
+                                presenceGpios[*pinName] = presenceGpio;
                             }
                         }
                         else
@@ -582,7 +584,7 @@
                 tachSensor = nullptr;
                 tachSensor = std::make_shared<TachSensor>(
                     path.string(), baseType, objectServer, dbusConnection,
-                    presenceSensor, redundancy, io, sensorName,
+                    presenceGpio, redundancy, io, sensorName,
                     std::move(sensorThresholds), *interfacePath, limits,
                     powerState, led);
                 tachSensor->setupRead();
@@ -617,14 +619,14 @@
         tachSensors;
     boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
         pwmSensors;
-    boost::container::flat_map<std::string, std::weak_ptr<PresenceSensor>>
-        presenceSensors;
+    boost::container::flat_map<std::string, std::weak_ptr<PresenceGpio>>
+        presenceGpios;
     auto sensorsChanged =
         std::make_shared<boost::container::flat_set<std::string>>();
 
     boost::asio::post(io, [&]() {
-        createSensors(io, objectServer, tachSensors, pwmSensors,
-                      presenceSensors, systemBus, nullptr);
+        createSensors(io, objectServer, tachSensors, pwmSensors, presenceGpios,
+                      systemBus, nullptr);
     });
 
     boost::asio::steady_timer filterTimer(io);
@@ -651,7 +653,7 @@
                     return;
                 }
                 createSensors(io, objectServer, tachSensors, pwmSensors,
-                              presenceSensors, systemBus, sensorsChanged, 5);
+                              presenceGpios, systemBus, sensorsChanged, 5);
             });
         };
 
diff --git a/src/PresenceGpio.cpp b/src/PresenceGpio.cpp
new file mode 100644
index 0000000..6c528d7
--- /dev/null
+++ b/src/PresenceGpio.cpp
@@ -0,0 +1,118 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include "PresenceGpio.hpp"
+
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/posix/stream_descriptor.hpp>
+#include <gpiod.hpp>
+
+#include <iostream>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <system_error>
+
+PresenceGpio::~PresenceGpio()
+{
+    gpioLine.release();
+}
+
+void PresenceGpio::updateAndTracePresence()
+{
+    status = (gpioLine.get_value() != 0);
+    if (status)
+    {
+        logPresent(deviceName);
+    }
+    else
+    {
+        logRemoved(deviceName);
+    }
+}
+
+EventPresenceGpio::EventPresenceGpio(
+    const std::string& iDeviceType, const std::string& iDeviceName,
+    const std::string& gpioName, bool inverted, boost::asio::io_context& io) :
+    PresenceGpio(iDeviceType, iDeviceName), gpioFd(io)
+{
+    gpioLine = gpiod::find_line(gpioName);
+    if (!gpioLine)
+    {
+        std::cerr << "Error requesting gpio: " << gpioName << "\n";
+        return;
+    }
+
+    try
+    {
+        gpioLine.request(
+            {deviceType + "Sensor", gpiod::line_request::EVENT_BOTH_EDGES,
+             inverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
+        updateAndTracePresence();
+
+        int gpioLineFd = gpioLine.event_get_fd();
+        if (gpioLineFd < 0)
+        {
+            std::cerr << "Failed to get " << gpioName << " fd\n";
+            throw std::runtime_error("Failed to get GPIO fd " + gpioName);
+        }
+
+        gpioFd.assign(gpioLineFd);
+    }
+    catch (const std::system_error& e)
+    {
+        std::cerr << "Error reading gpio " << gpioName << ": " << e.what()
+                  << "\n";
+        return;
+    }
+
+    monitorPresence();
+}
+
+void EventPresenceGpio::monitorPresence()
+{
+    std::weak_ptr<EventPresenceGpio> weakRef = weak_from_this();
+    gpioFd.async_wait(
+        boost::asio::posix::stream_descriptor::wait_read,
+        [weakRef](const boost::system::error_code& ec) {
+            std::shared_ptr<EventPresenceGpio> self = weakRef.lock();
+            if (!self)
+            {
+                std::cerr << "Failed to get lock for eventPresenceGpio: "
+                          << ec.message() << "\n";
+                return;
+            }
+            if (ec)
+            {
+                if (ec != boost::system::errc::bad_file_descriptor)
+                {
+                    std::cerr
+                        << "Error on event presence device " << self->deviceName
+                        << ": " << ec.message() << "\n";
+                }
+                return;
+            }
+            self->read();
+            self->monitorPresence();
+        });
+}
+
+void EventPresenceGpio::read()
+{
+    // Read is invoked when an edge event is detected by monitorPresence
+    gpioLine.event_read();
+    updateAndTracePresence();
+}
diff --git a/src/PresenceGpio.hpp b/src/PresenceGpio.hpp
new file mode 100644
index 0000000..54805cf
--- /dev/null
+++ b/src/PresenceGpio.hpp
@@ -0,0 +1,64 @@
+#pragma once
+
+#include "sensor.hpp"
+
+#include <gpiod.hpp>
+#include <phosphor-logging/lg2.hpp>
+
+class PresenceGpio
+{
+  public:
+    PresenceGpio(const std::string& type, const std::string& name) :
+        deviceType(type), deviceName(name) {};
+    PresenceGpio(const PresenceGpio&) = delete;
+    PresenceGpio& operator=(const PresenceGpio&) = delete;
+    virtual ~PresenceGpio() = 0;
+
+    bool isPresent() const
+    {
+        return status;
+    }
+
+  protected:
+    gpiod::line gpioLine;
+    bool status = false;
+    std::string deviceType;
+    std::string deviceName;
+
+    virtual void monitorPresence() = 0;
+
+    void logPresent(const std::string& device)
+    {
+        std::string summary = deviceType + " " + deviceName + " Inserted";
+        std::string msg = "OpenBMC.0.1." + deviceType + "Inserted";
+        lg2::info(summary.c_str(), "REDFISH_MESSAGE_ID", msg.c_str(),
+                  "REDFISH_MESSAGE_ARGS", device);
+    }
+
+    void logRemoved(const std::string& device)
+    {
+        std::string summary = deviceType + " " + deviceName + " Removed";
+        std::string msg = "OpenBMC.0.1." + deviceType + "Removed";
+        lg2::error(summary.c_str(), "REDFISH_MESSAGE_ID", msg.c_str(),
+                   "REDFISH_MESSAGE_ARGS", device);
+    }
+
+    void updateAndTracePresence();
+};
+
+class EventPresenceGpio :
+    public PresenceGpio,
+    public std::enable_shared_from_this<EventPresenceGpio>
+{
+  public:
+    EventPresenceGpio(const std::string& iDeviceType,
+                      const std::string& iDeviceName,
+                      const std::string& gpioName, bool inverted,
+                      boost::asio::io_context& io);
+
+  private:
+    boost::asio::posix::stream_descriptor gpioFd;
+
+    void monitorPresence() override;
+    void read();
+};
diff --git a/src/TachSensor.cpp b/src/TachSensor.cpp
index 7a57f5a..7908488 100644
--- a/src/TachSensor.cpp
+++ b/src/TachSensor.cpp
@@ -16,6 +16,7 @@
 
 #include "TachSensor.hpp"
 
+#include "PresenceGpio.hpp"
 #include "SensorPaths.hpp"
 #include "Thresholds.hpp"
 #include "Utils.hpp"
@@ -24,9 +25,7 @@
 #include <boost/asio/buffer.hpp>
 #include <boost/asio/error.hpp>
 #include <boost/asio/io_context.hpp>
-#include <boost/asio/posix/stream_descriptor.hpp>
 #include <boost/asio/random_access_file.hpp>
-#include <gpiod.hpp>
 #include <sdbusplus/asio/connection.hpp>
 #include <sdbusplus/asio/object_server.hpp>
 
@@ -48,7 +47,7 @@
     const std::string& path, const std::string& objectType,
     sdbusplus::asio::object_server& objectServer,
     std::shared_ptr<sdbusplus::asio::connection>& conn,
-    std::shared_ptr<PresenceSensor>& presenceSensor,
+    std::shared_ptr<PresenceGpio>& presenceGpio,
     std::optional<RedundancySensor>* redundancy, boost::asio::io_context& io,
     const std::string& fanName,
     std::vector<thresholds::Threshold>&& thresholdsIn,
@@ -58,7 +57,7 @@
     Sensor(escapeName(fanName), std::move(thresholdsIn), sensorConfiguration,
            objectType, false, false, limits.second, limits.first, conn,
            powerState),
-    objServer(objectServer), redundancy(redundancy), presence(presenceSensor),
+    objServer(objectServer), redundancy(redundancy), presence(presenceGpio),
     inputDev(io, path, boost::asio::random_access_file::read_only),
     waitTimer(io), path(path), led(ledIn)
 {
@@ -158,7 +157,7 @@
     size_t pollTime = pwmPollMs;
     if (presence)
     {
-        if (!presence->getValue())
+        if (!presence->isPresent())
         {
             markAvailable(false);
             missing = true;
@@ -213,91 +212,6 @@
     }
 }
 
-PresenceSensor::PresenceSensor(const std::string& gpioName, bool inverted,
-                               boost::asio::io_context& io,
-                               const std::string& name) :
-    gpioLine(gpiod::find_line(gpioName)), gpioFd(io), name(name)
-{
-    if (!gpioLine)
-    {
-        std::cerr << "Error requesting gpio: " << gpioName << "\n";
-        status = false;
-        return;
-    }
-
-    try
-    {
-        gpioLine.request({"FanSensor", gpiod::line_request::EVENT_BOTH_EDGES,
-                          inverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
-        status = (gpioLine.get_value() != 0);
-
-        int gpioLineFd = gpioLine.event_get_fd();
-        if (gpioLineFd < 0)
-        {
-            std::cerr << "Failed to get " << gpioName << " fd\n";
-            return;
-        }
-
-        gpioFd.assign(gpioLineFd);
-    }
-    catch (const std::system_error&)
-    {
-        std::cerr << "Error reading gpio: " << gpioName << "\n";
-        status = false;
-        return;
-    }
-
-    monitorPresence();
-}
-
-PresenceSensor::~PresenceSensor()
-{
-    gpioFd.close();
-    gpioLine.release();
-}
-
-void PresenceSensor::monitorPresence()
-{
-    gpioFd.async_wait(boost::asio::posix::stream_descriptor::wait_read,
-                      [this](const boost::system::error_code& ec) {
-                          if (ec == boost::system::errc::bad_file_descriptor)
-                          {
-                              return; // we're being destroyed
-                          }
-                          if (ec)
-                          {
-                              std::cerr << "Error on presence sensor " << name
-                                        << " \n";
-                              ;
-                          }
-                          else
-                          {
-                              read();
-                          }
-                          monitorPresence();
-                      });
-}
-
-void PresenceSensor::read()
-{
-    gpioLine.event_read();
-    status = (gpioLine.get_value() != 0);
-    // Read is invoked when an edge event is detected by monitorPresence
-    if (status)
-    {
-        logFanInserted(name);
-    }
-    else
-    {
-        logFanRemoved(name);
-    }
-}
-
-bool PresenceSensor::getValue() const
-{
-    return status;
-}
-
 RedundancySensor::RedundancySensor(size_t count,
                                    const std::vector<std::string>& children,
                                    sdbusplus::asio::object_server& objectServer,
diff --git a/src/TachSensor.hpp b/src/TachSensor.hpp
index 15ed2e4..d2ca101 100644
--- a/src/TachSensor.hpp
+++ b/src/TachSensor.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "PresenceGpio.hpp"
 #include "Thresholds.hpp"
 #include "sensor.hpp"
 
@@ -16,24 +17,6 @@
 #include <utility>
 #include <vector>
 
-class PresenceSensor
-{
-  public:
-    PresenceSensor(const std::string& gpioName, bool inverted,
-                   boost::asio::io_context& io, const std::string& name);
-    ~PresenceSensor();
-
-    void monitorPresence();
-    void read();
-    bool getValue() const;
-
-  private:
-    bool status = true;
-    gpiod::line gpioLine;
-    boost::asio::posix::stream_descriptor gpioFd;
-    std::string name;
-};
-
 namespace redundancy
 {
 constexpr const char* full = "Full";
@@ -58,6 +41,18 @@
     std::shared_ptr<sdbusplus::asio::dbus_interface> association;
     sdbusplus::asio::object_server& objectServer;
     boost::container::flat_map<std::string, bool> statuses;
+
+    static void logFanRedundancyLost()
+    {
+        const auto* msg = "OpenBMC.0.1.FanRedundancyLost";
+        lg2::error("Fan Inserted", "REDFISH_MESSAGE_ID", msg);
+    }
+
+    static void logFanRedundancyRestored()
+    {
+        const auto* msg = "OpenBMC.0.1.FanRedundancyRegained";
+        lg2::error("Fan Removed", "REDFISH_MESSAGE_ID", msg);
+    }
 };
 
 class TachSensor :
@@ -68,7 +63,7 @@
     TachSensor(const std::string& path, const std::string& objectType,
                sdbusplus::asio::object_server& objectServer,
                std::shared_ptr<sdbusplus::asio::connection>& conn,
-               std::shared_ptr<PresenceSensor>& presence,
+               std::shared_ptr<PresenceGpio>& presence,
                std::optional<RedundancySensor>* redundancy,
                boost::asio::io_context& io, const std::string& fanName,
                std::vector<thresholds::Threshold>&& thresholds,
@@ -85,7 +80,7 @@
     std::array<char, 128> readBuf{};
     sdbusplus::asio::object_server& objServer;
     std::optional<RedundancySensor>* redundancy;
-    std::shared_ptr<PresenceSensor> presence;
+    std::shared_ptr<PresenceGpio> presence;
     std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
     std::shared_ptr<sdbusplus::asio::dbus_interface> itemAssoc;
     boost::asio::random_access_file inputDev;
@@ -98,29 +93,3 @@
     void restartRead(size_t pollTime);
     void checkThresholds() override;
 };
-
-inline void logFanInserted(const std::string& device)
-{
-    const auto* msg = "OpenBMC.0.1.FanInserted";
-    lg2::error("Fan Inserted", "REDFISH_MESSAGE_ID", msg,
-               "REDFISH_MESSAGE_ARGS", device);
-}
-
-inline void logFanRemoved(const std::string& device)
-{
-    const auto* msg = "OpenBMC.0.1.FanRemoved";
-    lg2::error("Fan Removed", "REDFISH_MESSAGE_ID", msg, "REDFISH_MESSAGE_ARGS",
-               device);
-}
-
-inline void logFanRedundancyLost()
-{
-    const auto* msg = "OpenBMC.0.1.FanRedundancyLost";
-    lg2::error("Fan Inserted", "REDFISH_MESSAGE_ID", msg);
-}
-
-inline void logFanRedundancyRestored()
-{
-    const auto* msg = "OpenBMC.0.1.FanRedundancyRegained";
-    lg2::error("Fan Removed", "REDFISH_MESSAGE_ID", msg);
-}
diff --git a/src/meson.build b/src/meson.build
index 6fb66a1..12f8ee5 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -121,6 +121,7 @@
     executable(
         'fansensor',
         'FanMain.cpp',
+        'PresenceGpio.cpp',
         'TachSensor.cpp',
         'PwmSensor.cpp',
         dependencies: [