Add fan presence support

Async montitor GPIO to get fan presence.

Tested-by: Pulled every fan on a 2U and monitored
presence.

Change-Id: Ibad2251e5c6a0ce480812051c103785f79fa60bb
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/sensors/src/TachSensor.cpp b/sensors/src/TachSensor.cpp
index e4ff3fd..f7c6fc0 100644
--- a/sensors/src/TachSensor.cpp
+++ b/sensors/src/TachSensor.cpp
@@ -21,6 +21,7 @@
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/algorithm/string/replace.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
+#include <fstream>
 #include <iostream>
 #include <limits>
 #include <sdbusplus/asio/connection.hpp>
@@ -33,11 +34,13 @@
 TachSensor::TachSensor(const std::string &path,
                        sdbusplus::asio::object_server &objectServer,
                        std::shared_ptr<sdbusplus::asio::connection> &conn,
+                       std::unique_ptr<PresenceSensor> &&presence,
                        boost::asio::io_service &io, const std::string &fanName,
                        std::vector<thresholds::Threshold> &&_thresholds,
                        const std::string &sensorConfiguration) :
     Sensor(),
     path(path), objServer(objectServer), dbusConnection(conn),
+    presence(std::move(presence)),
     name(boost::replace_all_copy(fanName, " ", "_")),
     configuration(sensorConfiguration),
     inputDev(io, open(path.c_str(), O_RDONLY)), waitTimer(io), errCount(0),
@@ -90,46 +93,59 @@
     {
         return; // we're being destroyed
     }
+    bool missing = false;
+    if (presence)
+    {
+        if (!presence->getValue())
+        {
+            sensorInterface->set_property(
+                "Value", std::numeric_limits<double>::quiet_NaN());
+            missing = true;
+        }
+    }
     std::istream responseStream(&readBuf);
-    if (!err)
+    if (!missing)
     {
-        std::string response;
-        try
+        if (!err)
         {
-            std::getline(responseStream, response);
-            float nvalue = std::stof(response);
-            responseStream.clear();
-            if (nvalue != value)
+            std::string response;
+            try
             {
-                updateValue(nvalue);
+                std::getline(responseStream, response);
+                float nvalue = std::stof(response);
+                responseStream.clear();
+                if (nvalue != value)
+                {
+                    updateValue(nvalue);
+                }
+                errCount = 0;
             }
-            errCount = 0;
-        }
-        catch (const std::invalid_argument &)
-        {
-            errCount++;
-        }
-    }
-    else
-    {
-
-        errCount++;
-    }
-    // only send value update once
-    if (errCount == warnAfterErrorCount)
-    {
-        // only an error if power is on
-        if (isPowerOn(dbusConnection))
-        {
-            std::cerr << "Failure to read sensor " << name << " at " << path
-                      << "\n";
-            updateValue(0);
+            catch (const std::invalid_argument &)
+            {
+                errCount++;
+            }
         }
         else
         {
-            errCount = 0; // check power again in 10 cycles
-            sensorInterface->set_property(
-                "Value", std::numeric_limits<double>::quiet_NaN());
+
+            errCount++;
+        }
+        // only send value update once
+        if (errCount == warnAfterErrorCount)
+        {
+            // only an error if power is on
+            if (isPowerOn(dbusConnection))
+            {
+                std::cerr << "Failure to read sensor " << name << " at " << path
+                          << "\n";
+                updateValue(0);
+            }
+            else
+            {
+                errCount = 0; // check power again in 10 cycles
+                sensorInterface->set_property(
+                    "Value", std::numeric_limits<double>::quiet_NaN());
+            }
         }
     }
     responseStream.clear();
@@ -140,7 +156,12 @@
         return; // we're no longer valid
     }
     inputDev.assign(fd);
-    waitTimer.expires_from_now(boost::posix_time::milliseconds(pwmPollMs));
+    size_t pollTime = pwmPollMs;
+    if (missing)
+    {
+        pollTime *= 10;
+    }
+    waitTimer.expires_from_now(boost::posix_time::milliseconds(pollTime));
     waitTimer.async_wait([&](const boost::system::error_code &ec) {
         if (ec == boost::asio::error::operation_aborted)
         {
@@ -240,3 +261,85 @@
         std::cerr << "error initializing critical threshold interface\n";
     }
 }
+
+PresenceSensor::PresenceSensor(const size_t index, bool inverted,
+                               boost::asio::io_service &io) :
+    inverted(inverted),
+    inputDev(io)
+{
+    // todo: use gpiodaemon
+    std::string device = gpioPath + std::string("gpio") + std::to_string(index);
+    fd = open((device + "/value").c_str(), O_RDONLY);
+    if (fd < 0)
+    {
+        std::cerr << "Error opening gpio " << index << "\n";
+        return;
+    }
+
+    std::ofstream deviceFile(device + "/edge");
+    if (!deviceFile.good())
+    {
+        std::cerr << "Error setting edge " << device << "\n";
+        return;
+    }
+    deviceFile << "both";
+    deviceFile.close();
+
+    inputDev.assign(boost::asio::ip::tcp::v4(), fd);
+    monitorPresence();
+    read();
+}
+
+PresenceSensor::~PresenceSensor()
+{
+    inputDev.close();
+    close(fd);
+}
+
+void PresenceSensor::monitorPresence(void)
+{
+    inputDev.async_wait(boost::asio::ip::tcp::socket::wait_error,
+                        [this](const boost::system::error_code &ec) {
+                            if (ec == boost::system::errc::bad_file_descriptor)
+                            {
+                                return; // we're being destroyed
+                            }
+                            else if (ec)
+                            {
+                                std::cerr
+                                    << "Error on presence sensor socket\n";
+                            }
+                            else
+                            {
+                                read();
+                            }
+                            monitorPresence();
+                        });
+}
+
+void PresenceSensor::read(void)
+{
+    constexpr size_t readSize = sizeof("0");
+    std::string readBuf;
+    readBuf.resize(readSize);
+    lseek(fd, 0, SEEK_SET);
+    size_t r = ::read(fd, readBuf.data(), readSize);
+    if (r != 1)
+    {
+        std::cerr << "Error reading gpio\n";
+    }
+    else
+    {
+        bool value = std::stoi(readBuf);
+        if (inverted)
+        {
+            value = !value;
+        }
+        status = value;
+    }
+}
+
+bool PresenceSensor::getValue(void)
+{
+    return status;
+}
\ No newline at end of file