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