Implement Liquid Leak Detector

Implement liquid leak detector based on design
https://gerrit.openbmc.org/c/openbmc/docs/+/73152.

Related PRs:
https://gerrit.openbmc.org/c/openbmc/phosphor-dbus-interfaces/+/73151
https://gerrit.openbmc.org/c/openbmc/phosphor-dbus-interfaces/+/73707
https://gerrit.openbmc.org/c/openbmc/sdbusplus/+/75461
https://gerrit.openbmc.org/c/openbmc/phosphor-dbus-interfaces/+/75999

Tested:
```
>> busctl tree xyz.openbmc_project.LeakDetector
└─ /xyz
  └─ /xyz/openbmc_project
    └─ /xyz/openbmc_project/state
      └─ /xyz/openbmc_project/state/leak
        └─ /xyz/openbmc_project/state/leak/detector
          ├─ /xyz/openbmc_project/state/leak/detector/TrayDetector1
          └─ /xyz/openbmc_project/state/leak/detector/TrayDetector2

>> echo pull-up > /sys/devices/platform/gpio-sim.0/gpiochip2/sim_gpio0/pull

>> curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/Systems/system/LogServices/EventLog/Entries
{
  "@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/Entries",
  "@odata.type": "#LogEntryCollection.LogEntryCollection",
  "Description": "Collection of System Event Log Entries",
  "Members": [
  	...
    {
      "@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/Entries/2",
      "@odata.type": "#LogEntry.v1_9_0.LogEntry",
      "AdditionalDataURI": "/redfish/v1/Systems/system/LogServices/EventLog/Entries/2/attachment",
      "Created": "2024-05-10T06:40:30.423+00:00",
      "EntryType": "Event",
      "Id": "2",
      "Message": "xyz.openbmc_project.State.Leak.Detector.LeakDetectedWarning",
      "Modified": "2024-05-10T06:40:30.423+00:00",
      "Name": "System Event Log Entry",
      "Resolved": false,
      "Severity": "Warning"
    }
  ],
  "Members@odata.count": 2,
  "Name": "System Event Log Entries"
}

>> echo pull-down > /sys/devices/platform/gpio-sim.0/gpiochip2/sim_gpio0/pull

>> curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/Systems/system/LogServices/EventLog/Entries
{
  "@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/Entries",
  "@odata.type": "#LogEntryCollection.LogEntryCollection",
  "Description": "Collection of System Event Log Entries",
  "Members": [
	...
    {
      "@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/Entries/2",
      "@odata.type": "#LogEntry.v1_9_0.LogEntry",
      "AdditionalDataURI": "/redfish/v1/Systems/system/LogServices/EventLog/Entries/2/attachment",
      "Created": "2024-05-10T06:40:30.423+00:00",
      "EntryType": "Event",
      "Id": "2",
      "Message": "xyz.openbmc_project.State.Leak.Detector.LeakDetectedWarning",
      "Modified": "2024-05-10T06:42:23.989+00:00",
      "Name": "System Event Log Entry",
      "Resolved": true,
      "Severity": "Warning"
    },
    {
      "@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/Entries/3",
      "@odata.type": "#LogEntry.v1_9_0.LogEntry",
      "AdditionalDataURI": "/redfish/v1/Systems/system/LogServices/EventLog/Entries/3/attachment",
      "Created": "2024-05-10T06:42:24.024+00:00",
      "EntryType": "Event",
      "Id": "3",
      "Message": "xyz.openbmc_project.State.Leak.Detector.LeakDetectedNormal",
      "Modified": "2024-05-10T06:42:24.024+00:00",
      "Name": "System Event Log Entry",
      "Resolved": false,
      "Severity": "OK"
    }
  ],
  "Members@odata.count": 3,
  "Name": "System Event Log Entries"
}
```

Change-Id: Id6981deb314ca6852c5e31b932b28e601c2f3976
Signed-off-by: Jagpal Singh Gill <paligill@gmail.com>
diff --git a/src/GPIOInterface.cpp b/src/GPIOInterface.cpp
new file mode 100644
index 0000000..0bd0f4c
--- /dev/null
+++ b/src/GPIOInterface.cpp
@@ -0,0 +1,94 @@
+#include "GPIOInterface.hpp"
+
+#include <gpiod.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/async.hpp>
+
+#include <exception>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <utility>
+
+namespace gpio
+{
+
+PHOSPHOR_LOG2_USING;
+
+GPIOInterface::GPIOInterface(sdbusplus::async::context& ctx,
+                             const std::string& consumerName,
+                             const std::string& pinName, bool activeLow,
+                             Callback_t updateStateCallback) :
+    ctx(ctx), pinName(pinName),
+    updateStateCallback(std::move(updateStateCallback))
+{
+    if (!this->updateStateCallback)
+    {
+        throw std::runtime_error("updateStateCallback is not set");
+    }
+    line = gpiod::find_line(pinName);
+    if (!line)
+    {
+        throw std::runtime_error("Failed to find GPIO line for " + pinName);
+    }
+    try
+    {
+        line.request({consumerName, gpiod::line_request::EVENT_BOTH_EDGES,
+                      activeLow ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
+    }
+    catch (std::exception& e)
+    {
+        throw std::runtime_error("Failed to request line for " + pinName +
+                                 " with error " + e.what());
+    }
+
+    int lineFd = line.event_get_fd();
+    if (lineFd < 0)
+    {
+        throw std::runtime_error(
+            "Failed to get event fd for GPIO line " + pinName);
+    }
+
+    fdioInstance = std::make_unique<sdbusplus::async::fdio>(ctx, lineFd);
+}
+
+auto GPIOInterface::start() -> sdbusplus::async::task<>
+{
+    // Start the async read for the GPIO line
+    ctx.spawn(readGPIOAsyncEvent());
+
+    // Read the initial GPIO value
+    co_await readGPIOAsync();
+}
+
+auto GPIOInterface::readGPIOAsync() -> sdbusplus::async::task<>
+{
+    auto lineValue = line.get_value();
+    if (lineValue < 0)
+    {
+        error("Failed to read GPIO line {LINENAME}", "LINENAME", pinName);
+        co_return;
+    }
+    co_await updateStateCallback(lineValue == gpiod::line_event::RISING_EDGE);
+
+    co_return;
+}
+
+auto GPIOInterface::readGPIOAsyncEvent() -> sdbusplus::async::task<>
+{
+    while (!ctx.stop_requested())
+    {
+        // Wait for the fd event for the line to change
+        co_await fdioInstance->next();
+
+        line.event_read();
+        auto lineValue = line.get_value();
+
+        co_await updateStateCallback(
+            lineValue == gpiod::line_event::RISING_EDGE);
+    }
+
+    co_return;
+}
+
+} // namespace gpio