[ADC-GPIO]Enabling ADC-Gpio Bridge

Commit 2: bridge
In case a channel has a bridge circuit,
we have to turn the bridge on prior to reading a value at least for one scan cycle
to get a valid value.
Add support for ADC channel controlled by GPIO.
For example,ADC channel8, connected to CMOS battery, is controlled by a GPIO.
Need set 1 to gpio when Channel8 sampling, and set 0 to gpio when finish sampling.

Another Commit 1:Enable ADC Gpio bridge configuration

Tested:
P3VBAT previous value is 0.4V
P3VBAT sensor reading  now:3.0843V

Change-Id: I64325845dfd4a931d0beda28993f6b15fa31ae95
Signed-off-by: Zhu, Yunge <yunge.zhu@linux.intel.com>
diff --git a/include/ADCSensor.hpp b/include/ADCSensor.hpp
index fbb256b..539e0da 100644
--- a/include/ADCSensor.hpp
+++ b/include/ADCSensor.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <Thresholds.hpp>
+#include <optional>
 #include <sdbusplus/asio/object_server.hpp>
 #include <sensor.hpp>
 
@@ -13,7 +14,8 @@
               boost::asio::io_service& io, const std::string& sensorName,
               std::vector<thresholds::Threshold>&& thresholds,
               const double scaleFactor, PowerState readState,
-              const std::string& sensorConfiguration);
+              const std::string& sensorConfiguration,
+              std::optional<int> bridgeGpio);
     ~ADCSensor();
 
   private:
@@ -23,6 +25,7 @@
     boost::asio::streambuf readBuf;
     int errCount;
     double scaleFactor;
+    std::optional<int> bridgeGpio;
     PowerState readState;
     thresholds::ThresholdTimer thresholdTimer;
     void setupRead(void);
diff --git a/src/ADCSensor.cpp b/src/ADCSensor.cpp
index 505bbfa..a8d98ae 100644
--- a/src/ADCSensor.cpp
+++ b/src/ADCSensor.cpp
@@ -20,21 +20,41 @@
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/algorithm/string/replace.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
+#include <filesystem>
+#include <fstream>
 #include <iostream>
 #include <limits>
+#include <optional>
 #include <sdbusplus/asio/connection.hpp>
 #include <sdbusplus/asio/object_server.hpp>
 #include <string>
-
 static constexpr unsigned int sensorPollMs = 500;
 static constexpr size_t warnAfterErrorCount = 10;
-
+static constexpr unsigned int gpioBridgeEnableMs = 20;
 // scaling factor from hwmon
 static constexpr unsigned int sensorScaleFactor = 1000;
 
 static constexpr double roundFactor = 10000; // 3 decimal places
 static constexpr double maxReading = 20;
 static constexpr double minReading = 0;
+static constexpr const char* sysGpioPath = "/sys/class/gpio/gpio";
+static constexpr const char* postfixValue = "/value";
+
+void setGpio(int gpioN, int value)
+{
+    std::string device = sysGpioPath + std::to_string(gpioN) + postfixValue;
+    std::fstream gpioFile;
+
+    gpioFile.open(device, std::ios::out);
+
+    if (!gpioFile.good())
+    {
+        std::cerr << "Error opening device " << device << "\n";
+        return;
+    }
+    gpioFile << std::to_string(value);
+    gpioFile.close();
+}
 
 ADCSensor::ADCSensor(const std::string& path,
                      sdbusplus::asio::object_server& objectServer,
@@ -42,13 +62,14 @@
                      boost::asio::io_service& io, const std::string& sensorName,
                      std::vector<thresholds::Threshold>&& _thresholds,
                      const double scaleFactor, PowerState readState,
-                     const std::string& sensorConfiguration) :
+                     const std::string& sensorConfiguration,
+                     std::optional<int> bridgeGpio) :
     Sensor(boost::replace_all_copy(sensorName, " ", "_"), path,
            std::move(_thresholds), sensorConfiguration,
            "xyz.openbmc_project.Configuration.ADC", maxReading, minReading),
     objServer(objectServer), scaleFactor(scaleFactor),
     readState(std::move(readState)), inputDev(io, open(path.c_str(), O_RDONLY)),
-    waitTimer(io), errCount(0), thresholdTimer(io, this)
+    waitTimer(io), errCount(0), thresholdTimer(io, this), bridgeGpio(bridgeGpio)
 {
     sensorInterface = objectServer.add_interface(
         "/xyz/openbmc_project/sensors/voltage/" + name,
@@ -88,10 +109,33 @@
 
 void ADCSensor::setupRead(void)
 {
-    boost::asio::async_read_until(
-        inputDev, readBuf, '\n',
-        [&](const boost::system::error_code& ec,
-            std::size_t /*bytes_transfered*/) { handleResponse(ec); });
+    if (bridgeGpio.has_value())
+    {
+        setGpio(*bridgeGpio, 1);
+        // In case a channel has a bridge circuit,we have to turn the bridge on
+        // prior to reading a value at least for one scan cycle to get a valid
+        // value. Guarantee that the HW signal can be stable, the HW signal
+        // could be instability.
+        waitTimer.expires_from_now(
+            boost::posix_time::milliseconds(gpioBridgeEnableMs));
+        waitTimer.async_wait([&](const boost::system::error_code& ec) {
+            if (ec == boost::asio::error::operation_aborted)
+            {
+                return; // we're being canceled
+            }
+            boost::asio::async_read_until(
+                inputDev, readBuf, '\n',
+                [&](const boost::system::error_code& ec,
+                    std::size_t /*bytes_transfered*/) { handleResponse(ec); });
+        });
+    }
+    else
+    {
+        boost::asio::async_read_until(
+            inputDev, readBuf, '\n',
+            [&](const boost::system::error_code& ec,
+                std::size_t /*bytes_transfered*/) { handleResponse(ec); });
+    }
 }
 
 void ADCSensor::handleResponse(const boost::system::error_code& err)
@@ -145,6 +189,10 @@
 
     responseStream.clear();
     inputDev.close();
+    if (bridgeGpio.has_value())
+    {
+        setGpio(*bridgeGpio, 0);
+    }
     int fd = open(path.c_str(), O_RDONLY);
     if (fd <= 0)
     {
diff --git a/src/ADCSensorMain.cpp b/src/ADCSensorMain.cpp
index 98b41a0..61ebd6c 100644
--- a/src/ADCSensorMain.cpp
+++ b/src/ADCSensorMain.cpp
@@ -23,6 +23,7 @@
 #include <boost/algorithm/string/replace.hpp>
 #include <boost/container/flat_set.hpp>
 #include <fstream>
+#include <optional>
 #include <regex>
 #include <sdbusplus/asio/connection.hpp>
 #include <sdbusplus/asio/object_server.hpp>
@@ -212,10 +213,20 @@
             setReadState(powerState, readState);
         }
 
+        auto findBridgeGpio = baseConfiguration->second.find("BridgeGpio");
+        std::optional<int> gpioNum;
+
+        if (findBridgeGpio != baseConfiguration->second.end())
+        {
+            int gpioPin =
+                std::visit(VariantToIntVisitor(), findBridgeGpio->second);
+            gpioNum = static_cast<std::optional<int>>(gpioPin);
+        }
+
         sensors[sensorName] = std::make_unique<ADCSensor>(
             path.string(), objectServer, dbusConnection, io, sensorName,
-            std::move(sensorThresholds), scaleFactor, readState,
-            *interfacePath);
+            std::move(sensorThresholds), scaleFactor, readState, *interfacePath,
+            gpioNum);
     }
 }