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