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 invObj.emplace(std::move(inventory), std::move(invIntf)); 149 150 return invObj; 151 } 152 153 void Presence::updateInventory(bool present) 154 { 155 ObjectMap invObj = getObjectMap(present); 156 157 log<level::INFO>("Updating inventory present property", 158 entry("PRESENT=%d", present), 159 entry("PATH=%s", inventory.c_str())); 160 161 auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus); 162 163 // Update inventory 164 auto invMsg = bus.new_method_call(invService.c_str(), INVENTORY_PATH, 165 INVENTORY_INTF, "Notify"); 166 invMsg.append(std::move(invObj)); 167 auto invMgrResponseMsg = bus.call(invMsg); 168 if (invMgrResponseMsg.is_method_error()) 169 { 170 log<level::ERR>("Error in inventory manager call to update inventory"); 171 elog<InternalFailure>(); 172 } 173 } 174 175 void Presence::bindOrUnbindDrivers(bool present) 176 { 177 auto action = (present) ? "bind" : "unbind"; 178 179 for (auto& driver : drivers) 180 { 181 auto path = std::get<pathField>(driver) / action; 182 auto device = std::get<deviceField>(driver); 183 184 if (present) 185 { 186 log<level::INFO>("Binding a device driver", 187 entry("PATH=%s", path.c_str()), 188 entry("DEVICE=%s", device.c_str())); 189 } 190 else 191 { 192 log<level::INFO>("Unbinding a device driver", 193 entry("PATH=%s", path.c_str()), 194 entry("DEVICE=%s", device.c_str())); 195 } 196 197 std::ofstream file; 198 199 file.exceptions(std::ofstream::failbit | std::ofstream::badbit | 200 std::ofstream::eofbit); 201 202 try 203 { 204 file.open(path); 205 file << device; 206 file.close(); 207 } 208 catch (std::exception& e) 209 { 210 auto err = errno; 211 212 log<level::ERR>("Failed binding or unbinding a device " 213 "after a card was removed or added", 214 entry("PATH=%s", path.c_str()), 215 entry("DEVICE=%s", device.c_str()), 216 entry("ERRNO=%d", err)); 217 } 218 } 219 } 220 221 } // namespace presence 222 } // namespace gpio 223 } // namespace phosphor 224