blob: 856408eb0f012bb587fc65fe43e0fbb7cd074118 [file] [log] [blame]
Alexander Hansen8c4b1d92024-11-04 14:06:24 +01001/*
2 * SPDX-FileCopyrightText: Copyright (c) 2022-2024.
3 * All rights reserved. SPDX-License-Identifier: Apache-2.0
4 */
5
6#include "gpio_presence_manager.hpp"
7
8#include "device_presence.hpp"
9
10#include <boost/asio/posix/stream_descriptor.hpp>
11#include <gpiod.hpp>
12#include <phosphor-logging/lg2.hpp>
13#include <sdbusplus/async/timer.hpp>
14#include <sdbusplus/message/native_types.hpp>
15#include <xyz/openbmc_project/Configuration/GPIODeviceDetect/client.hpp>
16#include <xyz/openbmc_project/Configuration/GPIODeviceDetect/common.hpp>
17
18#include <memory>
19#include <ranges>
20#include <string>
21#include <utility>
22
23PHOSPHOR_LOG2_USING;
24
25namespace gpio_presence
26{
27
28GPIOPresenceManager::GPIOPresenceManager(sdbusplus::async::context& ctx) :
29 ctx(ctx), manager(ctx, "/"),
30 configProvider(
31 ConfigProvider(ctx, sdbusplus::common::xyz::openbmc_project::
32 configuration::GPIODeviceDetect::interface))
33{}
34
35auto GPIOPresenceManager::start() -> void
36{
37 ctx.spawn(initialize());
38}
39
40auto GPIOPresenceManager::getPresence(const std::string& name) -> bool
41{
42 if (!presenceMap.contains(name))
43 {
44 return false;
45 }
46 return presenceMap.at(name)->isPresent();
47}
48
49auto GPIOPresenceManager::initialize() -> sdbusplus::async::task<void>
50{
51 co_await configProvider.initialize(
52 std::bind_front(&GPIOPresenceManager::addConfigHandler, this),
53 std::bind_front(&GPIOPresenceManager::removeConfig, this));
54}
55
56auto GPIOPresenceManager::setupBusName() const -> std::string
57{
58 debug("requesting dbus name {NAME}", "NAME", service);
59
60 ctx.request_name(service);
61 return service;
62}
63
64auto GPIOPresenceManager::addConfig(const sdbusplus::message::object_path& obj,
65 std::unique_ptr<DevicePresence> config)
66 -> void
67{
68 debug("adding configuration for {NAME}", "NAME", obj);
69 presenceMap.insert_or_assign(obj, std::move(config));
70
71 debug("found valid configuration at object path {OBJPATH}", "OBJPATH", obj);
72
73 auto gpioConfigs = presenceMap[obj]->gpioPolarity;
74
75 // populate fdios
76 for (auto& [gpioName, _] : gpioConfigs)
77 {
78 if (gpioLines.contains(gpioName))
79 {
80 continue;
81 }
82
83 try
84 {
85 gpioLines[gpioName] = gpiod::find_line(gpioName);
86 }
87 catch (std::exception& e)
88 {
89 error("gpiod::find_line failed: {ERROR}", "ERROR", e);
90 return;
91 }
92
93 gpiod::line_request lineConfig;
94 lineConfig.consumer = "gpio-presence";
95 lineConfig.request_type = gpiod::line_request::EVENT_BOTH_EDGES |
96 gpiod::line_request::DIRECTION_INPUT;
97
98 int lineFd = -1;
99 try
100 {
101 gpioLines[gpioName].request(lineConfig);
102
103 lineFd = gpioLines[gpioName].event_get_fd();
104 }
105 catch (std::exception& e)
106 {
107 error("{ERROR}", "ERROR", e);
108 return;
109 }
110 if (lineFd < 0)
111 {
112 error("could not get event fd for gpio '{NAME}'", "NAME", gpioName);
113 return;
114 }
115
116 if (!fdios.contains(gpioName))
117 {
118 fdios.insert(
119 {gpioName,
120 std::make_unique<sdbusplus::async::fdio>(ctx, lineFd)});
121
122 ctx.spawn(readGPIOAsyncEvent(gpioName));
123 }
124 }
125}
126
127auto GPIOPresenceManager::addConfigHandler(sdbusplus::message::object_path obj)
128 -> void
129{
130 // NOLINTBEGIN(performance-unnecessary-value-param)
131 ctx.spawn(addConfigFromDbusAsync(obj));
132 // NOLINTEND(performance-unnecessary-value-param)
133}
134
135// NOLINTBEGIN(performance-unnecessary-value-param)
136auto GPIOPresenceManager::addConfigFromDbusAsync(
137 const sdbusplus::message::object_path obj) -> sdbusplus::async::task<void>
138// NOLINTEND(performance-unnecessary-value-param)
139{
140 auto props = co_await sdbusplus::client::xyz::openbmc_project::
141 configuration::GPIODeviceDetect<>(ctx)
142 .service("xyz.openbmc_project.EntityManager")
143 .path(obj.str)
144 .properties();
145
146 if (props.presence_pin_names.size() != props.presence_pin_values.size())
147 {
148 error(
149 "presence pin names and presence pin values have different sizes");
150 co_return;
151 }
152
153 auto devicePresence = std::make_unique<DevicePresence>(
154 ctx, props.presence_pin_names, props.presence_pin_values, props.name,
155 gpioState);
156
157 if (devicePresence)
158 {
159 addConfig(obj, std::move(devicePresence));
160 }
161}
162
163auto GPIOPresenceManager::removeConfig(const std::string& objPath) -> void
164{
165 if (!presenceMap.contains(objPath))
166 {
167 return;
168 }
169
170 debug("erasing configuration for object path {OBJPATH}", "OBJPATH",
171 objPath);
172 presenceMap.erase(objPath);
173
174 std::set<std::string> gpiosNeeded;
175
176 for (const auto& config : std::views::values(presenceMap))
177 {
178 for (const auto& gpio : std::views::keys(config->gpioPolarity))
179 {
180 gpiosNeeded.insert(gpio);
181 }
182 }
183
184 auto ks = std::views::keys(gpioLines);
185 std::set<std::string> trackedGPIOs{ks.begin(), ks.end()};
186
187 for (const auto& trackedGPIO : trackedGPIOs)
188 {
189 if (gpiosNeeded.contains(trackedGPIO))
190 {
191 continue;
192 }
193
194 gpioLines[trackedGPIO].release();
195
196 gpioLines.erase(trackedGPIO);
197 fdios.erase(fdios.find(trackedGPIO));
198 }
199}
200
201auto GPIOPresenceManager::updatePresence(const std::string& gpioLine,
202 bool state) -> void
203{
204 gpioState.insert_or_assign(gpioLine, state);
205
206 debug("GPIO line {GPIO_NAME} went {GPIO_LEVEL}", "GPIO_NAME", gpioLine,
207 "GPIO_LEVEL", (state) ? "high" : "low");
208
209 for (const auto& config : std::views::values(presenceMap))
210 {
211 config->updateGPIOPresence(gpioLine);
212 }
213}
214
215auto GPIOPresenceManager::readGPIOAsyncEvent(std::string gpioLine)
216 -> sdbusplus::async::task<void>
217{
218 debug("Watching gpio events for {LINENAME}", "LINENAME", gpioLine);
219
220 if (!fdios.contains(gpioLine))
221 {
222 error("fdio for {LINENAME} not found", "LINENAME", gpioLine);
223 co_return;
224 }
225
226 const auto& fdio = fdios[gpioLine];
227
228 try
229 {
230 const int lineValue = gpioLines[gpioLine].get_value();
231
232 updatePresence(gpioLine, lineValue == gpiod::line_event::RISING_EDGE);
233 }
234 catch (std::exception& e)
235 {
236 error("Failed to read GPIO line {LINENAME}", "LINENAME", gpioLine);
237 error("{ERROR}", "ERROR", e);
238 co_return;
239 }
240
241 while (!ctx.stop_requested())
242 {
243 co_await fdio->next();
244
245 debug("Received gpio event for {LINENAME}", "LINENAME", gpioLine);
246
247 gpioLines[gpioLine].event_read();
248
249 auto lineValue = gpioLines[gpioLine].get_value();
250
251 if (lineValue < 0)
252 {
253 error("Failed to read GPIO line {LINENAME}", "LINENAME", gpioLine);
254 }
255
256 updatePresence(gpioLine, lineValue == gpiod::line_event::RISING_EDGE);
257 }
258}
259
260} // namespace gpio_presence