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