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 void Presence::determinePresence() 64 { 65 auto present = false; 66 auto value = static_cast<int>(0); 67 auto fetch_rc = libevdev_fetch_event_value(devicePtr.get(), EV_KEY, 68 key, &value); 69 if (0 == fetch_rc) 70 { 71 log<level::ERR>("Device does not support event type", 72 entry("KEYCODE=%d", key)); 73 elog<InternalFailure>(); 74 return; 75 } 76 if (value > 0) 77 { 78 present = true; 79 } 80 81 updateInventory(present); 82 } 83 84 // Callback handler when there is an activity on the FD 85 int Presence::processEvents(sd_event_source* es, int fd, 86 uint32_t revents, void* userData) 87 { 88 auto presence = static_cast<Presence*>(userData); 89 90 presence->analyzeEvent(); 91 return 0; 92 } 93 94 95 // Analyzes the GPIO event 96 void Presence::analyzeEvent() 97 { 98 99 // Data returned 100 struct input_event ev {}; 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(), 109 LIBEVDEV_READ_FLAG_NORMAL, &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 } 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", 147 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)); 160 161 auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus); 162 163 // Update inventory 164 auto invMsg = bus.new_method_call(invService.c_str(), 165 INVENTORY_PATH, 166 INVENTORY_INTF, 167 "Notify"); 168 invMsg.append(std::move(invObj)); 169 auto invMgrResponseMsg = bus.call(invMsg); 170 if (invMgrResponseMsg.is_method_error()) 171 { 172 log<level::ERR>( 173 "Error in inventory manager call to update inventory"); 174 elog<InternalFailure>(); 175 } 176 } 177 178 179 } // namespace presence 180 } // namespace gpio 181 } // namespace phosphor 182 183