blob: e473fea9e2c3ce0d5bcb4ac46da3e44d6a33f0f6 [file] [log] [blame]
Patrick Venturedace6802018-11-01 16:52:10 -07001#include "gpio_presence.hpp"
2
3#include "xyz/openbmc_project/Common/error.hpp"
4
Gunnar Mills5f101102017-06-29 13:07:39 -05005#include <fcntl.h>
Matt Spinler902d1c32017-09-01 11:03:02 -05006#include <libevdev/libevdev.h>
Patrick Venturedace6802018-11-01 16:52:10 -07007
8#include <fstream>
9#include <phosphor-logging/elog-errors.hpp>
Gunnar Mills5f101102017-06-29 13:07:39 -050010#include <phosphor-logging/elog.hpp>
11#include <phosphor-logging/log.hpp>
Gunnar Mills72639152017-06-22 15:06:21 -050012
13namespace phosphor
14{
15namespace gpio
16{
17namespace presence
18{
19
Gunnar Mills5f101102017-06-29 13:07:39 -050020using namespace phosphor::logging;
21using namespace sdbusplus::xyz::openbmc_project::Common::Error;
22
Gunnar Mills80292bb2017-07-05 16:34:51 -050023constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory";
24constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager";
25
26constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
27constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
28constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
29
Patrick Venturedace6802018-11-01 16:52:10 -070030std::string getService(const std::string& path, const std::string& interface,
Patrick Williamsbc5b3752022-07-22 19:26:56 -050031 sdbusplus::bus_t& bus)
Gunnar Mills80292bb2017-07-05 16:34:51 -050032{
Patrick Venturedace6802018-11-01 16:52:10 -070033 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
34 MAPPER_INTERFACE, "GetObject");
Gunnar Mills80292bb2017-07-05 16:34:51 -050035
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
Gunnar Mills80292bb2017-07-05 16:34:51 -050048 std::map<std::string, std::vector<std::string>> mapperResponse;
49 mapperResponseMsg.read(mapperResponse);
50
51 if (mapperResponse.empty())
52 {
Patrick Venturedace6802018-11-01 16:52:10 -070053 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()));
Gunnar Mills80292bb2017-07-05 16:34:51 -050056 elog<InternalFailure>();
57 }
58
59 return mapperResponse.begin()->first;
60}
61
Gunnar Mills5f101102017-06-29 13:07:39 -050062void Presence::determinePresence()
63{
Gunnar Mills80292bb2017-07-05 16:34:51 -050064 auto present = false;
Gunnar Mills5f101102017-06-29 13:07:39 -050065 auto value = static_cast<int>(0);
Patrick Venturedace6802018-11-01 16:52:10 -070066 auto fetch_rc =
67 libevdev_fetch_event_value(devicePtr.get(), EV_KEY, key, &value);
Gunnar Mills5f101102017-06-29 13:07:39 -050068 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 }
Gunnar Mills80292bb2017-07-05 16:34:51 -050075 if (value > 0)
76 {
77 present = true;
78 }
79
80 updateInventory(present);
Gunnar Mills5f101102017-06-29 13:07:39 -050081}
82
Gunnar Mills765725e2017-07-06 14:17:44 -050083// Callback handler when there is an activity on the FD
Brad Bishop86d16f02019-09-19 16:07:33 -040084int Presence::processEvents(sd_event_source*, int, uint32_t, void* userData)
Gunnar Mills765725e2017-07-06 14:17:44 -050085{
86 auto presence = static_cast<Presence*>(userData);
87
88 presence->analyzeEvent();
89 return 0;
90}
91
Gunnar Mills765725e2017-07-06 14:17:44 -050092// Analyzes the GPIO event
93void Presence::analyzeEvent()
94{
95
96 // Data returned
Patrick Venturedace6802018-11-01 16:52:10 -070097 struct input_event ev
98 {
99 };
Gunnar Mills765725e2017-07-06 14:17:44 -0500100 int rc = 0;
101
102 // While testing, observed that not having a loop here was leading
103 // into events being missed.
104 while (rc >= 0)
105 {
106 // Wait until no more events are available on the device.
Patrick Venturedace6802018-11-01 16:52:10 -0700107 rc = libevdev_next_event(devicePtr.get(), LIBEVDEV_READ_FLAG_NORMAL,
108 &ev);
Gunnar Mills765725e2017-07-06 14:17:44 -0500109 if (rc < 0)
110 {
111 // There was an error waiting for events, mostly that there are no
112 // events to be read.. So continue waiting...
113 return;
114 }
115
116 if (rc == LIBEVDEV_READ_STATUS_SUCCESS)
117 {
118 if (ev.type == EV_SYN && ev.code == SYN_REPORT)
119 {
120 continue;
121 }
122 else if (ev.code == key)
123 {
124 auto present = false;
125 if (ev.value > 0)
126 {
127 present = true;
Brandon Wymanb08a0f62021-03-03 19:53:18 -0600128 std::this_thread::sleep_for(
129 std::chrono::milliseconds(delay));
130 bindOrUnbindDrivers(present);
131 updateInventory(present);
Gunnar Mills765725e2017-07-06 14:17:44 -0500132 }
Brandon Wymanb08a0f62021-03-03 19:53:18 -0600133 else
134 {
135 updateInventory(present);
136 bindOrUnbindDrivers(present);
137 }
Gunnar Mills765725e2017-07-06 14:17:44 -0500138 }
139 }
140 }
141
142 return;
143}
Gunnar Mills80292bb2017-07-05 16:34:51 -0500144
145Presence::ObjectMap Presence::getObjectMap(bool present)
146{
147 ObjectMap invObj;
148 InterfaceMap invIntf;
149 PropertyMap invProp;
150
151 invProp.emplace("Present", present);
152 invProp.emplace("PrettyName", name);
Patrick Venturedace6802018-11-01 16:52:10 -0700153 invIntf.emplace("xyz.openbmc_project.Inventory.Item", std::move(invProp));
Anthony Wilson206f0042019-05-02 00:02:23 -0500154 // Add any extra interfaces we want to associate with the inventory item
155 for (auto& iface : ifaces)
156 {
157 invIntf.emplace(iface, PropertyMap());
158 }
Gunnar Mills80292bb2017-07-05 16:34:51 -0500159 invObj.emplace(std::move(inventory), std::move(invIntf));
160
161 return invObj;
162}
163
164void Presence::updateInventory(bool present)
165{
166 ObjectMap invObj = getObjectMap(present);
167
Gunnar Mills765725e2017-07-06 14:17:44 -0500168 log<level::INFO>("Updating inventory present property",
169 entry("PRESENT=%d", present),
Joseph Reynolds32c3f632018-05-17 12:11:18 -0500170 entry("PATH=%s", inventory.c_str()));
Gunnar Mills765725e2017-07-06 14:17:44 -0500171
Gunnar Mills80292bb2017-07-05 16:34:51 -0500172 auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus);
173
174 // Update inventory
Patrick Venturedace6802018-11-01 16:52:10 -0700175 auto invMsg = bus.new_method_call(invService.c_str(), INVENTORY_PATH,
176 INVENTORY_INTF, "Notify");
Gunnar Mills80292bb2017-07-05 16:34:51 -0500177 invMsg.append(std::move(invObj));
178 auto invMgrResponseMsg = bus.call(invMsg);
179 if (invMgrResponseMsg.is_method_error())
180 {
Patrick Venturedace6802018-11-01 16:52:10 -0700181 log<level::ERR>("Error in inventory manager call to update inventory");
Gunnar Mills80292bb2017-07-05 16:34:51 -0500182 elog<InternalFailure>();
183 }
Matt Spinler902d1c32017-09-01 11:03:02 -0500184}
185
186void 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 {
Patrick Venturedace6802018-11-01 16:52:10 -0700197 log<level::INFO>("Binding a device driver",
198 entry("PATH=%s", path.c_str()),
199 entry("DEVICE=%s", device.c_str()));
Matt Spinler902d1c32017-09-01 11:03:02 -0500200 }
201 else
202 {
Patrick Venturedace6802018-11-01 16:52:10 -0700203 log<level::INFO>("Unbinding a device driver",
204 entry("PATH=%s", path.c_str()),
205 entry("DEVICE=%s", device.c_str()));
Matt Spinler902d1c32017-09-01 11:03:02 -0500206 }
207
208 std::ofstream file;
209
Patrick Venturedace6802018-11-01 16:52:10 -0700210 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
211 std::ofstream::eofbit);
Matt Spinler902d1c32017-09-01 11:03:02 -0500212
213 try
214 {
215 file.open(path);
216 file << device;
217 file.close();
218 }
Patrick Williams67554142021-10-06 13:00:15 -0500219 catch (const std::exception& e)
Matt Spinler902d1c32017-09-01 11:03:02 -0500220 {
221 auto err = errno;
222
223 log<level::ERR>("Failed binding or unbinding a device "
224 "after a card was removed or added",
225 entry("PATH=%s", path.c_str()),
226 entry("DEVICE=%s", device.c_str()),
227 entry("ERRNO=%d", err));
228 }
229 }
Gunnar Mills80292bb2017-07-05 16:34:51 -0500230}
231
Gunnar Mills72639152017-06-22 15:06:21 -0500232} // namespace presence
233} // namespace gpio
234} // namespace phosphor