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