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