| Alexander Hansen | 8c4b1d9 | 2024-11-04 14:06:24 +0100 | [diff] [blame] | 1 | /* | 
 | 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 |  | 
 | 23 | PHOSPHOR_LOG2_USING; | 
 | 24 |  | 
 | 25 | namespace gpio_presence | 
 | 26 | { | 
 | 27 |  | 
 | 28 | GPIOPresenceManager::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 |  | 
 | 35 | auto GPIOPresenceManager::start() -> void | 
 | 36 | { | 
 | 37 |     ctx.spawn(initialize()); | 
 | 38 | } | 
 | 39 |  | 
 | 40 | auto 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 |  | 
 | 49 | auto 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 |  | 
 | 56 | auto 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 |  | 
 | 64 | auto 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 |  | 
 | 127 | auto 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) | 
 | 136 | auto 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 |  | 
 | 163 | auto 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 |  | 
 | 201 | auto 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 |  | 
 | 215 | auto 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 |  | 
| eldin.lee | 533d490 | 2025-07-30 20:01:24 +0800 | [diff] [blame] | 247 |         // event_read() does not clear the EPOLLIN flag immediately; it is | 
 | 248 |         // cleared during the subsequent epoll check. Therefore, call event_wait | 
 | 249 |         // with a zero timeout to ensure that event_read() is only invoked when | 
 | 250 |         // an event is available, preventing it from blocking. | 
 | 251 |         if (!gpioLines[gpioLine].event_wait(std::chrono::milliseconds(0))) | 
 | 252 |         { | 
 | 253 |             continue; | 
 | 254 |         } | 
 | 255 |  | 
| Alexander Hansen | 8c4b1d9 | 2024-11-04 14:06:24 +0100 | [diff] [blame] | 256 |         gpioLines[gpioLine].event_read(); | 
 | 257 |  | 
 | 258 |         auto lineValue = gpioLines[gpioLine].get_value(); | 
 | 259 |  | 
 | 260 |         if (lineValue < 0) | 
 | 261 |         { | 
 | 262 |             error("Failed to read GPIO line {LINENAME}", "LINENAME", gpioLine); | 
 | 263 |         } | 
 | 264 |  | 
 | 265 |         updatePresence(gpioLine, lineValue == gpiod::line_event::RISING_EDGE); | 
 | 266 |     } | 
 | 267 | } | 
 | 268 |  | 
 | 269 | } // namespace gpio_presence |