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 int rc = 0; 93 94 // While testing, observed that not having a loop here was leading 95 // into events being missed. 96 while (rc >= 0) 97 { 98 // Wait until no more events are available on the device. 99 rc = libevdev_next_event(devicePtr.get(), LIBEVDEV_READ_FLAG_NORMAL, 100 &ev); 101 if (rc < 0) 102 { 103 // There was an error waiting for events, mostly that there are no 104 // events to be read.. So continue waiting... 105 return; 106 } 107 108 if (rc == LIBEVDEV_READ_STATUS_SUCCESS) 109 { 110 if (ev.type == EV_SYN && ev.code == SYN_REPORT) 111 { 112 continue; 113 } 114 else if (ev.code == key) 115 { 116 auto present = false; 117 if (ev.value > 0) 118 { 119 present = true; 120 std::this_thread::sleep_for( 121 std::chrono::milliseconds(delay)); 122 bindOrUnbindDrivers(present); 123 updateInventory(present); 124 } 125 else 126 { 127 updateInventory(present); 128 bindOrUnbindDrivers(present); 129 } 130 } 131 } 132 } 133 134 return; 135 } 136 137 Presence::ObjectMap Presence::getObjectMap(bool present) 138 { 139 ObjectMap invObj; 140 InterfaceMap invIntf; 141 PropertyMap invProp; 142 143 invProp.emplace("Present", present); 144 invProp.emplace("PrettyName", name); 145 invIntf.emplace("xyz.openbmc_project.Inventory.Item", std::move(invProp)); 146 // Add any extra interfaces we want to associate with the inventory item 147 for (auto& iface : ifaces) 148 { 149 invIntf.emplace(iface, PropertyMap()); 150 } 151 invObj.emplace(std::move(inventory), std::move(invIntf)); 152 153 return invObj; 154 } 155 156 void Presence::updateInventory(bool present) 157 { 158 ObjectMap invObj = getObjectMap(present); 159 160 lg2::info( 161 "Updating inventory present property value to {PRESENT}, path: {PATH}", 162 "PRESENT", present, "PATH", inventory); 163 164 auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus); 165 166 // Update inventory 167 auto invMsg = bus.new_method_call(invService.c_str(), INVENTORY_PATH, 168 INVENTORY_INTF, "Notify"); 169 invMsg.append(std::move(invObj)); 170 try 171 { 172 auto invMgrResponseMsg = bus.call(invMsg); 173 } 174 catch (const sdbusplus::exception_t& e) 175 { 176 lg2::error( 177 "Error in inventory manager call to update inventory: {ERROR}", 178 "ERROR", e); 179 elog<InternalFailure>(); 180 } 181 } 182 183 void Presence::bindOrUnbindDrivers(bool present) 184 { 185 auto action = (present) ? "bind" : "unbind"; 186 187 for (auto& driver : drivers) 188 { 189 auto path = std::get<pathField>(driver) / action; 190 auto device = std::get<deviceField>(driver); 191 192 if (present) 193 { 194 lg2::info("Binding a {DEVICE} driver: {PATH}", "DEVICE", device, 195 "PATH", path); 196 } 197 else 198 { 199 lg2::info("Unbinding a {DEVICE} driver: {PATH}", "DEVICE", device, 200 "PATH", path); 201 } 202 203 std::ofstream file; 204 205 file.exceptions(std::ofstream::failbit | std::ofstream::badbit | 206 std::ofstream::eofbit); 207 208 try 209 { 210 file.open(path); 211 file << device; 212 file.close(); 213 } 214 catch (const std::exception& e) 215 { 216 lg2::error( 217 "Failed binding or unbinding a {DEVICE} after a card was removed or added, path: {PATH}, error: {ERROR}", 218 "DEVICE", device, "PATH", path, "ERROR", e); 219 } 220 } 221 } 222 223 } // namespace presence 224 } // namespace gpio 225 } // namespace phosphor 226