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* es, int fd, uint32_t revents, 85 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 97 // Data returned 98 struct input_event ev 99 { 100 }; 101 int rc = 0; 102 103 // While testing, observed that not having a loop here was leading 104 // into events being missed. 105 while (rc >= 0) 106 { 107 // Wait until no more events are available on the device. 108 rc = libevdev_next_event(devicePtr.get(), LIBEVDEV_READ_FLAG_NORMAL, 109 &ev); 110 if (rc < 0) 111 { 112 // There was an error waiting for events, mostly that there are no 113 // events to be read.. So continue waiting... 114 return; 115 } 116 117 if (rc == LIBEVDEV_READ_STATUS_SUCCESS) 118 { 119 if (ev.type == EV_SYN && ev.code == SYN_REPORT) 120 { 121 continue; 122 } 123 else if (ev.code == key) 124 { 125 auto present = false; 126 if (ev.value > 0) 127 { 128 present = true; 129 } 130 updateInventory(present); 131 bindOrUnbindDrivers(present); 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 auto invMgrResponseMsg = bus.call(invMsg); 173 if (invMgrResponseMsg.is_method_error()) 174 { 175 log<level::ERR>("Error in inventory manager call to update inventory"); 176 elog<InternalFailure>(); 177 } 178 } 179 180 void Presence::bindOrUnbindDrivers(bool present) 181 { 182 auto action = (present) ? "bind" : "unbind"; 183 184 for (auto& driver : drivers) 185 { 186 auto path = std::get<pathField>(driver) / action; 187 auto device = std::get<deviceField>(driver); 188 189 if (present) 190 { 191 log<level::INFO>("Binding a device driver", 192 entry("PATH=%s", path.c_str()), 193 entry("DEVICE=%s", device.c_str())); 194 } 195 else 196 { 197 log<level::INFO>("Unbinding a device driver", 198 entry("PATH=%s", path.c_str()), 199 entry("DEVICE=%s", device.c_str())); 200 } 201 202 std::ofstream file; 203 204 file.exceptions(std::ofstream::failbit | std::ofstream::badbit | 205 std::ofstream::eofbit); 206 207 try 208 { 209 file.open(path); 210 file << device; 211 file.close(); 212 } 213 catch (std::exception& e) 214 { 215 auto err = errno; 216 217 log<level::ERR>("Failed binding or unbinding a device " 218 "after a card was removed or added", 219 entry("PATH=%s", path.c_str()), 220 entry("DEVICE=%s", device.c_str()), 221 entry("ERRNO=%d", err)); 222 } 223 } 224 } 225 226 } // namespace presence 227 } // namespace gpio 228 } // namespace phosphor 229