Patrick Rudolph | 46a9a5b | 2023-08-10 18:32:05 +0200 | [diff] [blame] | 1 | /** |
| 2 | * Copyright © 2019 Facebook |
| 3 | * Copyright © 2023 9elements GmbH |
| 4 | * |
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | * you may not use this file except in compliance with the License. |
| 7 | * You may obtain a copy of the License at |
| 8 | * |
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | * |
| 11 | * Unless required by applicable law or agreed to in writing, software |
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | * See the License for the specific language governing permissions and |
| 15 | * limitations under the License. |
| 16 | */ |
| 17 | |
| 18 | #include "gpio_presence.hpp" |
| 19 | |
| 20 | #include "xyz/openbmc_project/Common/error.hpp" |
| 21 | |
| 22 | #include <phosphor-logging/elog-errors.hpp> |
| 23 | #include <phosphor-logging/elog.hpp> |
| 24 | #include <phosphor-logging/lg2.hpp> |
| 25 | #include <sdbusplus/bus.hpp> |
| 26 | |
| 27 | namespace phosphor |
| 28 | { |
| 29 | namespace gpio |
| 30 | { |
| 31 | |
| 32 | using namespace phosphor::logging; |
| 33 | using namespace sdbusplus::xyz::openbmc_project::Common::Error; |
| 34 | |
| 35 | constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory"; |
| 36 | constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager"; |
| 37 | |
| 38 | constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; |
| 39 | constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; |
| 40 | constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; |
| 41 | |
| 42 | std::string getService(const std::string& path, const std::string& interface, |
| 43 | sdbusplus::bus_t& bus) |
| 44 | { |
| 45 | auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, |
| 46 | MAPPER_INTERFACE, "GetObject"); |
| 47 | |
| 48 | mapperCall.append(path); |
| 49 | mapperCall.append(std::vector<std::string>({interface})); |
| 50 | |
| 51 | std::map<std::string, std::vector<std::string>> mapperResponse; |
| 52 | try |
| 53 | { |
| 54 | auto mapperResponseMsg = bus.call(mapperCall); |
| 55 | mapperResponseMsg.read(mapperResponse); |
| 56 | } |
| 57 | catch (const sdbusplus::exception_t& e) |
| 58 | { |
| 59 | lg2::error( |
| 60 | "Error in mapper call to get service name, path: {PATH}, interface: {INTERFACE}, error: {ERROR}", |
| 61 | "PATH", path, "INTERFACE", interface, "ERROR", e); |
| 62 | elog<InternalFailure>(); |
| 63 | } |
| 64 | |
| 65 | return mapperResponse.begin()->first; |
| 66 | } |
| 67 | |
| 68 | GpioPresence::ObjectMap GpioPresence::getObjectMap(bool present) |
| 69 | { |
| 70 | ObjectMap invObj; |
| 71 | InterfaceMap invIntf; |
| 72 | PropertyMap invProp; |
| 73 | |
| 74 | invProp.emplace("Present", present); |
| 75 | invProp.emplace("PrettyName", name); |
| 76 | invIntf.emplace("xyz.openbmc_project.Inventory.Item", std::move(invProp)); |
| 77 | // Add any extra interfaces we want to associate with the inventory item |
| 78 | for (auto& iface : interfaces) |
| 79 | { |
| 80 | invIntf.emplace(iface, PropertyMap()); |
| 81 | } |
| 82 | invObj.emplace(std::move(inventory), std::move(invIntf)); |
| 83 | |
| 84 | return invObj; |
| 85 | } |
| 86 | |
| 87 | void GpioPresence::updateInventory(bool present) |
| 88 | { |
| 89 | ObjectMap invObj = getObjectMap(present); |
| 90 | |
| 91 | lg2::info( |
| 92 | "Updating inventory present property value to {PRESENT}, path: {PATH}", |
| 93 | "PRESENT", present, "PATH", inventory); |
| 94 | |
| 95 | auto bus = sdbusplus::bus::new_default(); |
| 96 | auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus); |
| 97 | |
| 98 | // Update inventory |
| 99 | auto invMsg = bus.new_method_call(invService.c_str(), INVENTORY_PATH, |
| 100 | INVENTORY_INTF, "Notify"); |
| 101 | invMsg.append(std::move(invObj)); |
| 102 | try |
| 103 | { |
| 104 | auto invMgrResponseMsg = bus.call(invMsg); |
| 105 | } |
| 106 | catch (const sdbusplus::exception_t& e) |
| 107 | { |
| 108 | lg2::error( |
| 109 | "Error in inventory manager call to update inventory: {ERROR}", |
| 110 | "ERROR", e); |
| 111 | elog<InternalFailure>(); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | void GpioPresence::scheduleEventHandler() |
| 116 | { |
| 117 | std::string gpio = std::string(gpioLineMsg); |
| 118 | |
| 119 | gpioEventDescriptor.async_wait( |
| 120 | boost::asio::posix::stream_descriptor::wait_read, |
| 121 | [this, gpio](const boost::system::error_code& ec) { |
| 122 | if (ec == boost::asio::error::operation_aborted) |
| 123 | { |
| 124 | // we were cancelled |
| 125 | return; |
| 126 | } |
| 127 | if (ec) |
| 128 | { |
| 129 | lg2::error("{GPIO} event handler error: {ERROR}", "GPIO", gpio, |
| 130 | "ERROR", ec.message()); |
| 131 | return; |
| 132 | } |
| 133 | gpioEventHandler(); |
Patrick Williams | d3898c5 | 2023-10-20 11:19:20 -0500 | [diff] [blame^] | 134 | }); |
Patrick Rudolph | 46a9a5b | 2023-08-10 18:32:05 +0200 | [diff] [blame] | 135 | } |
| 136 | |
| 137 | void GpioPresence::cancelEventHandler() |
| 138 | { |
| 139 | gpioEventDescriptor.cancel(); |
| 140 | } |
| 141 | |
| 142 | void GpioPresence::gpioEventHandler() |
| 143 | { |
| 144 | gpiod_line_event gpioLineEvent; |
| 145 | |
| 146 | if (gpiod_line_event_read_fd(gpioEventDescriptor.native_handle(), |
| 147 | &gpioLineEvent) < 0) |
| 148 | { |
| 149 | lg2::error("Failed to read {GPIO} from fd", "GPIO", gpioLineMsg); |
| 150 | return; |
| 151 | } |
| 152 | |
| 153 | if (gpioLineEvent.event_type == GPIOD_LINE_EVENT_RISING_EDGE) |
| 154 | { |
| 155 | lg2::info("{GPIO} Asserted", "GPIO", gpioLineMsg); |
| 156 | } |
| 157 | else |
| 158 | { |
| 159 | lg2::info("{GPIO} Deasserted", "GPIO", gpioLineMsg); |
| 160 | } |
| 161 | updateInventory(gpioLineEvent.event_type == GPIOD_LINE_EVENT_RISING_EDGE); |
| 162 | |
| 163 | /* Schedule a wait event */ |
| 164 | scheduleEventHandler(); |
| 165 | } |
| 166 | |
| 167 | int GpioPresence::requestGPIOEvents() |
| 168 | { |
| 169 | std::string flags; |
| 170 | |
| 171 | /* Request an event to monitor for respected gpio line */ |
| 172 | if (gpiod_line_request(gpioLine, &gpioConfig, 0) < 0) |
| 173 | { |
| 174 | lg2::error("Failed to request {GPIO}: {ERRNO}", "GPIO", gpioLineMsg, |
| 175 | "ERRNO", errno); |
| 176 | return -1; |
| 177 | } |
| 178 | |
| 179 | int gpioLineFd = gpiod_line_event_get_fd(gpioLine); |
| 180 | if (gpioLineFd < 0) |
| 181 | { |
| 182 | lg2::error("Failed to get fd for {GPIO}", "GPIO", gpioLineMsg); |
| 183 | return -1; |
| 184 | } |
| 185 | |
| 186 | if (gpioConfig.flags & GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE) |
| 187 | { |
| 188 | flags += " Bias DISABLE"; |
| 189 | } |
| 190 | else if (gpioConfig.flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP) |
| 191 | { |
| 192 | flags += " Bias PULL_UP"; |
| 193 | } |
| 194 | else if (gpioConfig.flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN) |
| 195 | { |
| 196 | flags += " Bias PULL_DOWN"; |
| 197 | } |
| 198 | |
| 199 | if (gpioConfig.flags & GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW) |
| 200 | { |
| 201 | flags += " ActiveLow"; |
| 202 | } |
| 203 | |
| 204 | if (!flags.empty()) |
| 205 | { |
| 206 | flags = "[" + flags + "]"; |
| 207 | } |
| 208 | |
| 209 | lg2::info("{GPIO} {FLAGS} monitoring started", "GPIO", gpioLineMsg, "FLAGS", |
| 210 | flags); |
| 211 | |
| 212 | /* Assign line fd to descriptor for monitoring */ |
| 213 | gpioEventDescriptor.assign(gpioLineFd); |
| 214 | |
| 215 | updateInventory(gpiod_line_get_value(gpioLine)); |
| 216 | |
| 217 | /* Schedule a wait event */ |
| 218 | scheduleEventHandler(); |
| 219 | |
| 220 | return 0; |
| 221 | } |
| 222 | } // namespace gpio |
| 223 | } // namespace phosphor |