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/config_provider.cpp b/src/gpio-presence/config_provider.cpp
new file mode 100644
index 0000000..7948802
--- /dev/null
+++ b/src/gpio-presence/config_provider.cpp
@@ -0,0 +1,149 @@
+/*
+ * SPDX-FileCopyrightText: Copyright (c) 2022-2024. All rights
+ * reserved. SPDX-License-Identifier: Apache-2.0
+ */
+#include "config_provider.hpp"
+
+#include <boost/container/flat_map.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/async/match.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <xyz/openbmc_project/ObjectMapper/client.hpp>
+
+#include <ranges>
+#include <string>
+
+PHOSPHOR_LOG2_USING;
+
+using VariantType =
+ std::variant<std::vector<std::string>, std::string, int64_t, uint64_t,
+ double, int32_t, uint32_t, int16_t, uint16_t, uint8_t, bool>;
+using ConfigMap = boost::container::flat_map<std::string, VariantType>;
+using ConfigData = boost::container::flat_map<std::string, ConfigMap>;
+
+namespace gpio_presence
+{
+
+ConfigProvider::ConfigProvider(sdbusplus::async::context& ctx,
+ const std::string& interface) :
+ interface(interface), ctx(ctx)
+{}
+
+auto ConfigProvider::initialize(AddedCallback addConfig,
+ RemovedCallback removeConfig)
+ -> sdbusplus::async::task<void>
+{
+ ctx.spawn(handleInterfacesAdded(addConfig));
+ ctx.spawn(handleInterfacesRemoved(removeConfig));
+
+ co_await getConfig(addConfig);
+}
+
+auto ConfigProvider::getConfig(AddedCallback addConfig)
+ -> sdbusplus::async::task<void>
+{
+ auto client = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>(ctx)
+ .service("xyz.openbmc_project.ObjectMapper")
+ .path("/xyz/openbmc_project/object_mapper");
+
+ debug("calling 'GetSubTree' to find instances of {INTF}", "INTF",
+ interface);
+
+ using SubTreeType =
+ std::map<std::string, std::map<std::string, std::vector<std::string>>>;
+ SubTreeType res = {};
+
+ try
+ {
+ std::vector<std::string> interfaces = {interface};
+ res = co_await client.get_sub_tree("/xyz/openbmc_project/inventory", 0,
+ interfaces);
+ }
+ catch (std::exception& e)
+ {
+ error("Failed GetSubTree call for configuration interface: {ERR}",
+ "ERR", e);
+ }
+
+ if (res.empty())
+ {
+ co_return;
+ }
+
+ // call the user callback for all the device that is already available
+ for (auto& [path, serviceInterfaceMap] : res)
+ {
+ for (const auto& service :
+ std::ranges::views::keys(serviceInterfaceMap))
+ {
+ debug("found configuration interface at {SERVICE} {PATH} {INTF}",
+ "SERVICE", service, "PATH", path, "INTF", interface);
+
+ addConfig(path);
+ }
+ }
+}
+
+namespace rules_intf = sdbusplus::bus::match::rules;
+
+const auto senderRule = rules_intf::sender("xyz.openbmc_project.EntityManager");
+
+auto ConfigProvider::handleInterfacesAdded(AddedCallback addConfig)
+ -> sdbusplus::async::task<void>
+{
+ debug("setting up dbus match for interfaces added");
+
+ sdbusplus::async::match addedMatch(
+ ctx, rules_intf::interfacesAdded() + senderRule);
+
+ while (!ctx.stop_requested())
+ {
+ auto [objPath, intfMap] =
+ co_await addedMatch
+ .next<sdbusplus::message::object_path, ConfigData>();
+
+ debug("Detected interface added on {OBJPATH}", "OBJPATH", objPath);
+
+ if (!std::ranges::contains(std::views::keys(intfMap), interface))
+ {
+ continue;
+ }
+
+ try
+ {
+ addConfig(objPath);
+ }
+ catch (std::exception& e)
+ {
+ error("Incomplete or invalid config found: {ERR}", "ERR", e);
+ }
+ }
+};
+
+auto ConfigProvider::handleInterfacesRemoved(RemovedCallback removeConfig)
+ -> sdbusplus::async::task<void>
+{
+ debug("setting up dbus match for interfaces removed");
+
+ sdbusplus::async::match removedMatch(
+ ctx, rules_intf::interfacesRemoved() + senderRule);
+
+ while (!ctx.stop_requested())
+ {
+ auto [objectPath, interfaces] =
+ co_await removedMatch.next<sdbusplus::message::object_path,
+ std::vector<std::string>>();
+
+ if (!std::ranges::contains(interfaces, interface))
+ {
+ continue;
+ }
+
+ debug("Detected interface {INTF} removed on {OBJPATH}", "INTF",
+ interface, "OBJPATH", objectPath);
+
+ removeConfig(objectPath);
+ }
+};
+
+} // namespace gpio_presence