1 #include "gpio_presence.hpp" 2 3 #include "xyz/openbmc_project/Common/error.hpp" 4 5 #include <fcntl.h> 6 #include <libevdev/libevdev.h> 7 8 #include <phosphor-logging/elog-errors.hpp> 9 #include <phosphor-logging/elog.hpp> 10 #include <phosphor-logging/lg2.hpp> 11 12 #include <fstream> 13 14 namespace phosphor 15 { 16 namespace gpio 17 { 18 namespace presence 19 { 20 21 using namespace phosphor::logging; 22 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 23 24 constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory"; 25 constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager"; 26 27 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 28 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 29 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; 30 31 std::string getService(const std::string& path, const std::string& interface, 32 sdbusplus::bus_t& bus) 33 { 34 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 35 MAPPER_INTERFACE, "GetObject"); 36 37 mapperCall.append(path); 38 mapperCall.append(std::vector<std::string>({interface})); 39 40 std::map<std::string, std::vector<std::string>> mapperResponse; 41 try 42 { 43 auto mapperResponseMsg = bus.call(mapperCall); 44 mapperResponseMsg.read(mapperResponse); 45 } 46 catch (const sdbusplus::exception_t& e) 47 { 48 lg2::error( 49 "Error in mapper call to get service name, path: {PATH}, interface: {INTERFACE}, error: {ERROR}", 50 "PATH", path, "INTERFACE", interface, "ERROR", e); 51 elog<InternalFailure>(); 52 } 53 54 return mapperResponse.begin()->first; 55 } 56 57 void Presence::determinePresence() 58 { 59 auto present = false; 60 auto value = static_cast<int>(0); 61 auto fetch_rc = 62 libevdev_fetch_event_value(devicePtr.get(), EV_KEY, key, &value); 63 if (0 == fetch_rc) 64 { 65 lg2::error("Device does not support event type, key: {KEYCODE}", 66 "KEYCODE", key); 67 elog<InternalFailure>(); 68 return; 69 } 70 if (value > 0) 71 { 72 present = true; 73 } 74 75 updateInventory(present); 76 } 77 78 // Callback handler when there is an activity on the FD 79 int Presence::processEvents(sd_event_source*, int, uint32_t, void* userData) 80 { 81 auto presence = static_cast<Presence*>(userData); 82 83 presence->analyzeEvent(); 84 return 0; 85 } 86 87 // Analyzes the GPIO event 88 void Presence::analyzeEvent() 89 { 90 // Data returned 91 struct input_event ev 92 {}; 93 int rc = 0; 94 95 // While testing, observed that not having a loop here was leading 96 // into events being missed. 97 while (rc >= 0) 98 { 99 // Wait until no more events are available on the device. 100 rc = libevdev_next_event(devicePtr.get(), LIBEVDEV_READ_FLAG_NORMAL, 101 &ev); 102 if (rc < 0) 103 { 104 // There was an error waiting for events, mostly that there are no 105 // events to be read.. So continue waiting... 106 return; 107 } 108 109 if (rc == LIBEVDEV_READ_STATUS_SUCCESS) 110 { 111 if (ev.type == EV_SYN && ev.code == SYN_REPORT) 112 { 113 continue; 114 } 115 else if (ev.code == key) 116 { 117 auto present = false; 118 if (ev.value > 0) 119 { 120 present = true; 121 std::this_thread::sleep_for( 122 std::chrono::milliseconds(delay)); 123 bindOrUnbindDrivers(present); 124 updateInventory(present); 125 } 126 else 127 { 128 updateInventory(present); 129 bindOrUnbindDrivers(present); 130 } 131 } 132 } 133 } 134 135 return; 136 } 137 138 Presence::ObjectMap Presence::getObjectMap(bool present) 139 { 140 ObjectMap invObj; 141 InterfaceMap invIntf; 142 PropertyMap invProp; 143 144 invProp.emplace("Present", present); 145 invProp.emplace("PrettyName", name); 146 invIntf.emplace("xyz.openbmc_project.Inventory.Item", std::move(invProp)); 147 // Add any extra interfaces we want to associate with the inventory item 148 for (auto& iface : ifaces) 149 { 150 invIntf.emplace(iface, PropertyMap()); 151 } 152 invObj.emplace(std::move(inventory), std::move(invIntf)); 153 154 return invObj; 155 } 156 157 void Presence::updateInventory(bool present) 158 { 159 ObjectMap invObj = getObjectMap(present); 160 161 lg2::info( 162 "Updating inventory present property value to {PRESENT}, path: {PATH}", 163 "PRESENT", present, "PATH", inventory); 164 165 auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus); 166 167 // Update inventory 168 auto invMsg = bus.new_method_call(invService.c_str(), INVENTORY_PATH, 169 INVENTORY_INTF, "Notify"); 170 invMsg.append(std::move(invObj)); 171 try 172 { 173 auto invMgrResponseMsg = bus.call(invMsg); 174 } 175 catch (const sdbusplus::exception_t& e) 176 { 177 lg2::error( 178 "Error in inventory manager call to update inventory: {ERROR}", 179 "ERROR", e); 180 elog<InternalFailure>(); 181 } 182 } 183 184 void Presence::bindOrUnbindDrivers(bool present) 185 { 186 auto action = (present) ? "bind" : "unbind"; 187 188 for (auto& driver : drivers) 189 { 190 auto path = std::get<pathField>(driver) / action; 191 auto device = std::get<deviceField>(driver); 192 193 if (present) 194 { 195 lg2::info("Binding a {DEVICE} driver: {PATH}", "DEVICE", device, 196 "PATH", path); 197 } 198 else 199 { 200 lg2::info("Unbinding a {DEVICE} driver: {PATH}", "DEVICE", device, 201 "PATH", path); 202 } 203 204 std::ofstream file; 205 206 file.exceptions(std::ofstream::failbit | std::ofstream::badbit | 207 std::ofstream::eofbit); 208 209 try 210 { 211 file.open(path); 212 file << device; 213 file.close(); 214 } 215 catch (const std::exception& e) 216 { 217 lg2::error( 218 "Failed binding or unbinding a {DEVICE} after a card was removed or added, path: {PATH}, error: {ERROR}", 219 "DEVICE", device, "PATH", path, "ERROR", e); 220 } 221 } 222 } 223 224 } // namespace presence 225 } // namespace gpio 226 } // namespace phosphor 227