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