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::bus& 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 } 129 updateInventory(present); 130 bindOrUnbindDrivers(present); 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 log<level::INFO>("Updating inventory present property", 162 entry("PRESENT=%d", present), 163 entry("PATH=%s", inventory.c_str())); 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 auto invMgrResponseMsg = bus.call(invMsg); 172 if (invMgrResponseMsg.is_method_error()) 173 { 174 log<level::ERR>("Error in inventory manager call to update inventory"); 175 elog<InternalFailure>(); 176 } 177 } 178 179 void Presence::bindOrUnbindDrivers(bool present) 180 { 181 auto action = (present) ? "bind" : "unbind"; 182 183 for (auto& driver : drivers) 184 { 185 auto path = std::get<pathField>(driver) / action; 186 auto device = std::get<deviceField>(driver); 187 188 if (present) 189 { 190 log<level::INFO>("Binding a device driver", 191 entry("PATH=%s", path.c_str()), 192 entry("DEVICE=%s", device.c_str())); 193 } 194 else 195 { 196 log<level::INFO>("Unbinding a device driver", 197 entry("PATH=%s", path.c_str()), 198 entry("DEVICE=%s", device.c_str())); 199 } 200 201 std::ofstream file; 202 203 file.exceptions(std::ofstream::failbit | std::ofstream::badbit | 204 std::ofstream::eofbit); 205 206 try 207 { 208 file.open(path); 209 file << device; 210 file.close(); 211 } 212 catch (std::exception& e) 213 { 214 auto err = errno; 215 216 log<level::ERR>("Failed binding or unbinding a device " 217 "after a card was removed or added", 218 entry("PATH=%s", path.c_str()), 219 entry("DEVICE=%s", device.c_str()), 220 entry("ERRNO=%d", err)); 221 } 222 } 223 } 224 225 } // namespace presence 226 } // namespace gpio 227 } // namespace phosphor 228