adcsensor: improve bridge gpio handling

This commit improves bridge gpio handling using name based gpio line
access and polarity setting support.

Tested: P3VBAT sensor reading worked correctly.

Change-Id: Iff174c52dba90cc068f4b5e527d4ab1c381817d0
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 564fce2..60a608b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -151,6 +151,7 @@
 add_executable (adcsensor src/ADCSensorMain.cpp ${ADC_SRC_FILES})
 add_dependencies (adcsensor sdbusplus-project)
 target_link_libraries (adcsensor ${SENSOR_LINK_LIBS})
+target_link_libraries (adcsensor gpiodcxx)
 
 add_executable (cpusensor src/CPUSensorMain.cpp ${CPU_SRC_FILES})
 add_dependencies (cpusensor sdbusplus-project)
diff --git a/include/ADCSensor.hpp b/include/ADCSensor.hpp
index b5a9e9a..e061138 100644
--- a/include/ADCSensor.hpp
+++ b/include/ADCSensor.hpp
@@ -1,10 +1,50 @@
 #pragma once
 
 #include <Thresholds.hpp>
+#include <gpiod.hpp>
 #include <optional>
 #include <sdbusplus/asio/object_server.hpp>
 #include <sensor.hpp>
 
+class BridgeGpio
+{
+  public:
+    BridgeGpio(const std::string& name, const int polarity)
+    {
+        line = gpiod::find_line(name);
+        if (!line)
+        {
+            std::cerr << "Error finding gpio: " << name << "\n";
+        }
+        else
+        {
+            try
+            {
+                line.request({"adcsensor",
+                              gpiod::line_request::DIRECTION_OUTPUT,
+                              polarity == gpiod::line::ACTIVE_HIGH
+                                  ? 0
+                                  : gpiod::line_request::FLAG_ACTIVE_LOW});
+            }
+            catch (std::system_error&)
+            {
+                std::cerr << "Error requesting gpio: " << name << "\n";
+            }
+        }
+    }
+
+    void set(int value)
+    {
+        if (line)
+        {
+            line.set_value(value);
+        }
+    }
+
+  private:
+    gpiod::line line;
+};
+
 class ADCSensor : public Sensor
 {
   public:
@@ -15,7 +55,7 @@
               std::vector<thresholds::Threshold>&& thresholds,
               const double scaleFactor, PowerState readState,
               const std::string& sensorConfiguration,
-              std::optional<int> bridgeGpio);
+              std::optional<BridgeGpio> bridgeGpio);
     ~ADCSensor();
 
   private:
@@ -26,7 +66,7 @@
     std::string path;
     int errCount;
     double scaleFactor;
-    std::optional<int> bridgeGpio;
+    std::optional<BridgeGpio> bridgeGpio;
     PowerState readState;
     thresholds::ThresholdTimer thresholdTimer;
     void setupRead(void);
diff --git a/src/ADCSensor.cpp b/src/ADCSensor.cpp
index 2141ac4..ab1481b 100644
--- a/src/ADCSensor.cpp
+++ b/src/ADCSensor.cpp
@@ -37,24 +37,6 @@
 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,
@@ -63,7 +45,7 @@
                      std::vector<thresholds::Threshold>&& _thresholds,
                      const double scaleFactor, PowerState readState,
                      const std::string& sensorConfiguration,
-                     std::optional<int> bridgeGpio) :
+                     std::optional<BridgeGpio> bridgeGpio) :
     Sensor(boost::replace_all_copy(sensorName, " ", "_"),
            std::move(_thresholds), sensorConfiguration,
            "xyz.openbmc_project.Configuration.ADC", maxReading, minReading),
@@ -111,7 +93,7 @@
 {
     if (bridgeGpio.has_value())
     {
-        setGpio(*bridgeGpio, 1);
+        (*bridgeGpio).set(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
@@ -191,7 +173,7 @@
     inputDev.close();
     if (bridgeGpio.has_value())
     {
-        setGpio(*bridgeGpio, 0);
+        (*bridgeGpio).set(0);
     }
     int fd = open(path.c_str(), O_RDONLY);
     if (fd <= 0)
diff --git a/src/ADCSensorMain.cpp b/src/ADCSensorMain.cpp
index 188aad9..8a88244 100644
--- a/src/ADCSensorMain.cpp
+++ b/src/ADCSensorMain.cpp
@@ -230,21 +230,41 @@
             }
         }
 
-        auto findBridgeGpio = baseConfiguration->second.find("BridgeGpio");
-        std::optional<int> gpioNum;
-
-        if (findBridgeGpio != baseConfiguration->second.end())
+        std::optional<BridgeGpio> bridgeGpio;
+        for (const SensorBaseConfiguration& suppConfig : *sensorData)
         {
-            int gpioPin =
-                std::visit(VariantToIntVisitor(), findBridgeGpio->second);
-            gpioNum = static_cast<std::optional<int>>(gpioPin);
+            if (suppConfig.first.find("BridgeGpio") != std::string::npos)
+            {
+                auto findName = suppConfig.second.find("Name");
+                if (findName != suppConfig.second.end())
+                {
+                    std::string gpioName =
+                        std::visit(VariantToStringVisitor(), findName->second);
+
+                    int polarity = gpiod::line::ACTIVE_HIGH;
+                    auto findPolarity = suppConfig.second.find("Polarity");
+                    if (findPolarity != suppConfig.second.end())
+                    {
+                        if (std::string("Low") ==
+                            std::visit(VariantToStringVisitor(),
+                                       findPolarity->second))
+                        {
+                            polarity = gpiod::line::ACTIVE_LOW;
+                        }
+                    }
+                    bridgeGpio = BridgeGpio(gpioName, polarity);
+                }
+
+                break;
+            }
         }
+
         auto& sensor = sensors[sensorName];
         sensor = nullptr;
         sensor = std::make_unique<ADCSensor>(
             path.string(), objectServer, dbusConnection, io, sensorName,
             std::move(sensorThresholds), scaleFactor, readState, *interfacePath,
-            gpioNum);
+            bridgeGpio);
     }
 }