Add gpio-presence-sensor

This is to implement the gpio based hw inventory design [1].

There is a new meson option 'gpio-presence' to enable/disable the
daemon.

Summary of the functionality:

- fetch configuration from EM, according to the configuration interface
- the D-Bus interface is
  xyz.openbmc_project.Configuration.GPIODeviceDetect
- the configuration represents devices for which presence can be
  detected based on gpio values.
- watch gpios for changes
- add/remove the xyz.openbmc_project.Inventory.Source.DevicePresence
  interface on the object path based on gpio values.

References:
[1] https://github.com/openbmc/docs/blob/master/designs/inventory/gpio-based-hardware-inventory.md
[2] https://www.kernel.org/doc/html/latest/admin-guide/gpio/gpio-sim.html

Tested: using linux gpio-sim facility, see below

1. create a fake gpio via [2]
2. configure gpio-presence-sensor as per [1]
3. run the gpio-presence-sensor
4. change the value of the gpio previously configured
5. there should be log output (at debug level)
6. the dbus interfaces exposed should appear/disappear as per [1]

Change-Id: I4cf039b583247581aa5c6c6c59e7fc41ced0bb85
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/src/gpio-presence/device_presence.cpp b/src/gpio-presence/device_presence.cpp
new file mode 100644
index 0000000..44d6e6a
--- /dev/null
+++ b/src/gpio-presence/device_presence.cpp
@@ -0,0 +1,110 @@
+/*
+ * SPDX-FileCopyrightText: Copyright (c) 2022-2024. All rights
+ * reserved. SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "device_presence.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/message/native_types.hpp>
+#include <xyz/openbmc_project/Configuration/GPIODeviceDetect/client.hpp>
+#include <xyz/openbmc_project/Configuration/GPIODeviceDetect/common.hpp>
+#include <xyz/openbmc_project/Inventory/Source/DevicePresence/aserver.hpp>
+
+#include <string>
+#include <vector>
+
+PHOSPHOR_LOG2_USING;
+
+namespace gpio_presence
+{
+
+DevicePresence::DevicePresence(
+    sdbusplus::async::context& ctx, const std::vector<std::string>& gpioNames,
+    const std::vector<uint64_t>& gpioValues, const std::string& deviceName,
+    const std::unordered_map<std::string, bool>& gpioState) :
+    deviceName(deviceName), gpioState(gpioState), ctx(ctx)
+{
+    for (size_t i = 0; i < gpioNames.size(); i++)
+    {
+        GPIO_POLARITY polarity =
+            (gpioValues[i] == 0) ? ACTIVE_LOW : ACTIVE_HIGH;
+        gpioPolarity[gpioNames[i]] = polarity;
+    }
+}
+
+auto DevicePresence::updateGPIOPresence(const std::string& gpioLine) -> void
+{
+    if (!gpioPolarity.contains(gpioLine))
+    {
+        return;
+    }
+
+    updateDbusInterfaces();
+}
+
+auto DevicePresence::getObjPath() const -> sdbusplus::message::object_path
+{
+    sdbusplus::message::object_path objPathBase(
+        "/xyz/openbmc_project/GPIODeviceDetected/");
+    sdbusplus::message::object_path objPath = objPathBase / deviceName;
+    return objPath;
+}
+
+auto DevicePresence::isPresent() -> bool
+{
+    for (auto& [name, polarity] : gpioPolarity)
+    {
+        if (!gpioState.contains(name))
+        {
+            error("GPIO {NAME} not in cached state", "NAME", name);
+            return false;
+        }
+
+        const bool state = gpioState.at(name);
+
+        if (state && polarity == ACTIVE_LOW)
+        {
+            return false;
+        }
+        if (!state && polarity == ACTIVE_HIGH)
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+auto DevicePresence::updateDbusInterfaces() -> void
+{
+    debug("Updating dbus interface for config {OBJPATH}", "OBJPATH",
+          deviceName);
+
+    const bool present = isPresent();
+    sdbusplus::message::object_path objPath = getObjPath();
+
+    if (present && !detectedIface)
+    {
+        info("Detected {NAME} as present, adding dbus interface", "NAME",
+             deviceName);
+
+        detectedIface =
+            std::make_unique<DevicePresenceInterface>(ctx, objPath.str.c_str());
+
+        detectedIface->name(deviceName);
+
+        detectedIface->emit_added();
+    }
+
+    if (!present && detectedIface)
+    {
+        info("Detected {NAME} as absent, removing dbus interface", "NAME",
+             deviceName);
+        detectedIface->emit_removed();
+
+        detectedIface.reset();
+    }
+}
+
+} // namespace gpio_presence