Add phosphor-multi-gpio-presence
The new service works like phosphor-gpio-presence, but uses libgpiod
and can monitor an arbitrary amount of GPIOs.
The driver loading feature hasn't been ported to the new service.
Change-Id: I412345f804208e48eec40ec020b3a0d8f668a34b
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
diff --git a/multi-presence/gpio_presence.cpp b/multi-presence/gpio_presence.cpp
new file mode 100644
index 0000000..338248f
--- /dev/null
+++ b/multi-presence/gpio_presence.cpp
@@ -0,0 +1,223 @@
+/**
+ * Copyright © 2019 Facebook
+ * Copyright © 2023 9elements GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gpio_presence.hpp"
+
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/bus.hpp>
+
+namespace phosphor
+{
+namespace gpio
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory";
+constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager";
+
+constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
+constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
+constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
+
+std::string getService(const std::string& path, const std::string& interface,
+ sdbusplus::bus_t& bus)
+{
+ auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
+ MAPPER_INTERFACE, "GetObject");
+
+ mapperCall.append(path);
+ mapperCall.append(std::vector<std::string>({interface}));
+
+ std::map<std::string, std::vector<std::string>> mapperResponse;
+ try
+ {
+ auto mapperResponseMsg = bus.call(mapperCall);
+ mapperResponseMsg.read(mapperResponse);
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ lg2::error(
+ "Error in mapper call to get service name, path: {PATH}, interface: {INTERFACE}, error: {ERROR}",
+ "PATH", path, "INTERFACE", interface, "ERROR", e);
+ elog<InternalFailure>();
+ }
+
+ return mapperResponse.begin()->first;
+}
+
+GpioPresence::ObjectMap GpioPresence::getObjectMap(bool present)
+{
+ ObjectMap invObj;
+ InterfaceMap invIntf;
+ PropertyMap invProp;
+
+ invProp.emplace("Present", present);
+ invProp.emplace("PrettyName", name);
+ invIntf.emplace("xyz.openbmc_project.Inventory.Item", std::move(invProp));
+ // Add any extra interfaces we want to associate with the inventory item
+ for (auto& iface : interfaces)
+ {
+ invIntf.emplace(iface, PropertyMap());
+ }
+ invObj.emplace(std::move(inventory), std::move(invIntf));
+
+ return invObj;
+}
+
+void GpioPresence::updateInventory(bool present)
+{
+ ObjectMap invObj = getObjectMap(present);
+
+ lg2::info(
+ "Updating inventory present property value to {PRESENT}, path: {PATH}",
+ "PRESENT", present, "PATH", inventory);
+
+ auto bus = sdbusplus::bus::new_default();
+ auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus);
+
+ // Update inventory
+ auto invMsg = bus.new_method_call(invService.c_str(), INVENTORY_PATH,
+ INVENTORY_INTF, "Notify");
+ invMsg.append(std::move(invObj));
+ try
+ {
+ auto invMgrResponseMsg = bus.call(invMsg);
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ lg2::error(
+ "Error in inventory manager call to update inventory: {ERROR}",
+ "ERROR", e);
+ elog<InternalFailure>();
+ }
+}
+
+void GpioPresence::scheduleEventHandler()
+{
+ std::string gpio = std::string(gpioLineMsg);
+
+ gpioEventDescriptor.async_wait(
+ boost::asio::posix::stream_descriptor::wait_read,
+ [this, gpio](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ // we were cancelled
+ return;
+ }
+ if (ec)
+ {
+ lg2::error("{GPIO} event handler error: {ERROR}", "GPIO", gpio,
+ "ERROR", ec.message());
+ return;
+ }
+ gpioEventHandler();
+ });
+}
+
+void GpioPresence::cancelEventHandler()
+{
+ gpioEventDescriptor.cancel();
+}
+
+void GpioPresence::gpioEventHandler()
+{
+ gpiod_line_event gpioLineEvent;
+
+ if (gpiod_line_event_read_fd(gpioEventDescriptor.native_handle(),
+ &gpioLineEvent) < 0)
+ {
+ lg2::error("Failed to read {GPIO} from fd", "GPIO", gpioLineMsg);
+ return;
+ }
+
+ if (gpioLineEvent.event_type == GPIOD_LINE_EVENT_RISING_EDGE)
+ {
+ lg2::info("{GPIO} Asserted", "GPIO", gpioLineMsg);
+ }
+ else
+ {
+ lg2::info("{GPIO} Deasserted", "GPIO", gpioLineMsg);
+ }
+ updateInventory(gpioLineEvent.event_type == GPIOD_LINE_EVENT_RISING_EDGE);
+
+ /* Schedule a wait event */
+ scheduleEventHandler();
+}
+
+int GpioPresence::requestGPIOEvents()
+{
+ std::string flags;
+
+ /* Request an event to monitor for respected gpio line */
+ if (gpiod_line_request(gpioLine, &gpioConfig, 0) < 0)
+ {
+ lg2::error("Failed to request {GPIO}: {ERRNO}", "GPIO", gpioLineMsg,
+ "ERRNO", errno);
+ return -1;
+ }
+
+ int gpioLineFd = gpiod_line_event_get_fd(gpioLine);
+ if (gpioLineFd < 0)
+ {
+ lg2::error("Failed to get fd for {GPIO}", "GPIO", gpioLineMsg);
+ return -1;
+ }
+
+ if (gpioConfig.flags & GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE)
+ {
+ flags += " Bias DISABLE";
+ }
+ else if (gpioConfig.flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP)
+ {
+ flags += " Bias PULL_UP";
+ }
+ else if (gpioConfig.flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN)
+ {
+ flags += " Bias PULL_DOWN";
+ }
+
+ if (gpioConfig.flags & GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW)
+ {
+ flags += " ActiveLow";
+ }
+
+ if (!flags.empty())
+ {
+ flags = "[" + flags + "]";
+ }
+
+ lg2::info("{GPIO} {FLAGS} monitoring started", "GPIO", gpioLineMsg, "FLAGS",
+ flags);
+
+ /* Assign line fd to descriptor for monitoring */
+ gpioEventDescriptor.assign(gpioLineFd);
+
+ updateInventory(gpiod_line_get_value(gpioLine));
+
+ /* Schedule a wait event */
+ scheduleEventHandler();
+
+ return 0;
+}
+} // namespace gpio
+} // namespace phosphor