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(); 134 }); 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 224