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/log.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 log<level::ERR>("Error in mapper call to get service name", 49 entry("PATH=%s", path.c_str()), 50 entry("INTERFACE=%s", interface.c_str()), 51 entry("ERROR=%s", e.what())); 52 elog<InternalFailure>(); 53 } 54 55 return mapperResponse.begin()->first; 56 } 57 58 void Presence::determinePresence() 59 { 60 auto present = false; 61 auto value = static_cast<int>(0); 62 auto fetch_rc = libevdev_fetch_event_value(devicePtr.get(), EV_KEY, key, 63 &value); 64 if (0 == fetch_rc) 65 { 66 log<level::ERR>("Device does not support event type", 67 entry("KEYCODE=%d", key)); 68 elog<InternalFailure>(); 69 return; 70 } 71 if (value > 0) 72 { 73 present = true; 74 } 75 76 updateInventory(present); 77 } 78 79 // Callback handler when there is an activity on the FD 80 int Presence::processEvents(sd_event_source*, int, uint32_t, void* userData) 81 { 82 auto presence = static_cast<Presence*>(userData); 83 84 presence->analyzeEvent(); 85 return 0; 86 } 87 88 // Analyzes the GPIO event 89 void Presence::analyzeEvent() 90 { 91 // Data returned 92 struct input_event ev 93 {}; 94 int rc = 0; 95 96 // While testing, observed that not having a loop here was leading 97 // into events being missed. 98 while (rc >= 0) 99 { 100 // Wait until no more events are available on the device. 101 rc = libevdev_next_event(devicePtr.get(), LIBEVDEV_READ_FLAG_NORMAL, 102 &ev); 103 if (rc < 0) 104 { 105 // There was an error waiting for events, mostly that there are no 106 // events to be read.. So continue waiting... 107 return; 108 } 109 110 if (rc == LIBEVDEV_READ_STATUS_SUCCESS) 111 { 112 if (ev.type == EV_SYN && ev.code == SYN_REPORT) 113 { 114 continue; 115 } 116 else if (ev.code == key) 117 { 118 auto present = false; 119 if (ev.value > 0) 120 { 121 present = true; 122 std::this_thread::sleep_for( 123 std::chrono::milliseconds(delay)); 124 bindOrUnbindDrivers(present); 125 updateInventory(present); 126 } 127 else 128 { 129 updateInventory(present); 130 bindOrUnbindDrivers(present); 131 } 132 } 133 } 134 } 135 136 return; 137 } 138 139 Presence::ObjectMap Presence::getObjectMap(bool present) 140 { 141 ObjectMap invObj; 142 InterfaceMap invIntf; 143 PropertyMap invProp; 144 145 invProp.emplace("Present", present); 146 invProp.emplace("PrettyName", name); 147 invIntf.emplace("xyz.openbmc_project.Inventory.Item", std::move(invProp)); 148 // Add any extra interfaces we want to associate with the inventory item 149 for (auto& iface : ifaces) 150 { 151 invIntf.emplace(iface, PropertyMap()); 152 } 153 invObj.emplace(std::move(inventory), std::move(invIntf)); 154 155 return invObj; 156 } 157 158 void Presence::updateInventory(bool present) 159 { 160 ObjectMap invObj = getObjectMap(present); 161 162 log<level::INFO>("Updating inventory present property", 163 entry("PRESENT=%d", present), 164 entry("PATH=%s", inventory.c_str())); 165 166 auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus); 167 168 // Update inventory 169 auto invMsg = bus.new_method_call(invService.c_str(), INVENTORY_PATH, 170 INVENTORY_INTF, "Notify"); 171 invMsg.append(std::move(invObj)); 172 try 173 { 174 auto invMgrResponseMsg = bus.call(invMsg); 175 } 176 catch (const sdbusplus::exception_t& e) 177 { 178 log<level::ERR>("Error in inventory manager call to update inventory", 179 entry("ERROR=%s", e.what())); 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 log<level::INFO>("Binding a device driver", 196 entry("PATH=%s", path.c_str()), 197 entry("DEVICE=%s", device.c_str())); 198 } 199 else 200 { 201 log<level::INFO>("Unbinding a device driver", 202 entry("PATH=%s", path.c_str()), 203 entry("DEVICE=%s", device.c_str())); 204 } 205 206 std::ofstream file; 207 208 file.exceptions(std::ofstream::failbit | std::ofstream::badbit | 209 std::ofstream::eofbit); 210 211 try 212 { 213 file.open(path); 214 file << device; 215 file.close(); 216 } 217 catch (const std::exception& e) 218 { 219 auto err = errno; 220 221 log<level::ERR>("Failed binding or unbinding a device " 222 "after a card was removed or added", 223 entry("PATH=%s", path.c_str()), 224 entry("DEVICE=%s", device.c_str()), 225 entry("ERRNO=%d", err)); 226 } 227 } 228 } 229 230 } // namespace presence 231 } // namespace gpio 232 } // namespace phosphor 233