blob: c6e26b587066259e3ce6c3dd10d8d30102b7e025 [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
Patrick Venturedace6802018-11-01 16:52:10 -07008#include <phosphor-logging/elog-errors.hpp>
Gunnar Mills5f101102017-06-29 13:07:39 -05009#include <phosphor-logging/elog.hpp>
10#include <phosphor-logging/log.hpp>
Gunnar Mills72639152017-06-22 15:06:21 -050011
Patrick Williams39084b42023-05-10 07:50:58 -050012#include <fstream>
13
Gunnar Mills72639152017-06-22 15:06:21 -050014namespace phosphor
15{
16namespace gpio
17{
18namespace presence
19{
20
Gunnar Mills5f101102017-06-29 13:07:39 -050021using namespace phosphor::logging;
22using namespace sdbusplus::xyz::openbmc_project::Common::Error;
23
Gunnar Mills80292bb2017-07-05 16:34:51 -050024constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory";
25constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager";
26
27constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
28constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
29constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
30
Patrick Venturedace6802018-11-01 16:52:10 -070031std::string getService(const std::string& path, const std::string& interface,
Patrick Williamsbc5b3752022-07-22 19:26:56 -050032 sdbusplus::bus_t& bus)
Gunnar Mills80292bb2017-07-05 16:34:51 -050033{
Patrick Venturedace6802018-11-01 16:52:10 -070034 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
35 MAPPER_INTERFACE, "GetObject");
Gunnar Mills80292bb2017-07-05 16:34:51 -050036
37 mapperCall.append(path);
38 mapperCall.append(std::vector<std::string>({interface}));
39
40 auto mapperResponseMsg = bus.call(mapperCall);
41 if (mapperResponseMsg.is_method_error())
42 {
43 log<level::ERR>("Error in mapper call to get service name",
44 entry("PATH=%s", path.c_str()),
45 entry("INTERFACE=%s", interface.c_str()));
46 elog<InternalFailure>();
47 }
48
Gunnar Mills80292bb2017-07-05 16:34:51 -050049 std::map<std::string, std::vector<std::string>> mapperResponse;
50 mapperResponseMsg.read(mapperResponse);
51
52 if (mapperResponse.empty())
53 {
Patrick Venturedace6802018-11-01 16:52:10 -070054 log<level::ERR>("Error in mapper response for getting service name",
55 entry("PATH=%s", path.c_str()),
56 entry("INTERFACE=%s", interface.c_str()));
Gunnar Mills80292bb2017-07-05 16:34:51 -050057 elog<InternalFailure>();
58 }
59
60 return mapperResponse.begin()->first;
61}
62
Gunnar Mills5f101102017-06-29 13:07:39 -050063void Presence::determinePresence()
64{
Gunnar Mills80292bb2017-07-05 16:34:51 -050065 auto present = false;
Gunnar Mills5f101102017-06-29 13:07:39 -050066 auto value = static_cast<int>(0);
Patrick Williams39084b42023-05-10 07:50:58 -050067 auto fetch_rc = libevdev_fetch_event_value(devicePtr.get(), EV_KEY, key,
68 &value);
Gunnar Mills5f101102017-06-29 13:07:39 -050069 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 }
Gunnar Mills80292bb2017-07-05 16:34:51 -050076 if (value > 0)
77 {
78 present = true;
79 }
80
81 updateInventory(present);
Gunnar Mills5f101102017-06-29 13:07:39 -050082}
83
Gunnar Mills765725e2017-07-06 14:17:44 -050084// Callback handler when there is an activity on the FD
Brad Bishop86d16f02019-09-19 16:07:33 -040085int Presence::processEvents(sd_event_source*, int, uint32_t, void* userData)
Gunnar Mills765725e2017-07-06 14:17:44 -050086{
87 auto presence = static_cast<Presence*>(userData);
88
89 presence->analyzeEvent();
90 return 0;
91}
92
Gunnar Mills765725e2017-07-06 14:17:44 -050093// Analyzes the GPIO event
94void Presence::analyzeEvent()
95{
Gunnar Mills765725e2017-07-06 14:17:44 -050096 // Data returned
Patrick Venturedace6802018-11-01 16:52:10 -070097 struct input_event ev
Patrick Williams39084b42023-05-10 07:50:58 -050098 {};
Gunnar Mills765725e2017-07-06 14:17:44 -050099 int rc = 0;
100
101 // While testing, observed that not having a loop here was leading
102 // into events being missed.
103 while (rc >= 0)
104 {
105 // Wait until no more events are available on the device.
Patrick Venturedace6802018-11-01 16:52:10 -0700106 rc = libevdev_next_event(devicePtr.get(), LIBEVDEV_READ_FLAG_NORMAL,
107 &ev);
Gunnar Mills765725e2017-07-06 14:17:44 -0500108 if (rc < 0)
109 {
110 // There was an error waiting for events, mostly that there are no
111 // events to be read.. So continue waiting...
112 return;
113 }
114
115 if (rc == LIBEVDEV_READ_STATUS_SUCCESS)
116 {
117 if (ev.type == EV_SYN && ev.code == SYN_REPORT)
118 {
119 continue;
120 }
121 else if (ev.code == key)
122 {
123 auto present = false;
124 if (ev.value > 0)
125 {
126 present = true;
Brandon Wymanb08a0f62021-03-03 19:53:18 -0600127 std::this_thread::sleep_for(
128 std::chrono::milliseconds(delay));
129 bindOrUnbindDrivers(present);
130 updateInventory(present);
Gunnar Mills765725e2017-07-06 14:17:44 -0500131 }
Brandon Wymanb08a0f62021-03-03 19:53:18 -0600132 else
133 {
134 updateInventory(present);
135 bindOrUnbindDrivers(present);
136 }
Gunnar Mills765725e2017-07-06 14:17:44 -0500137 }
138 }
139 }
140
141 return;
142}
Gunnar Mills80292bb2017-07-05 16:34:51 -0500143
144Presence::ObjectMap Presence::getObjectMap(bool present)
145{
146 ObjectMap invObj;
147 InterfaceMap invIntf;
148 PropertyMap invProp;
149
150 invProp.emplace("Present", present);
151 invProp.emplace("PrettyName", name);
Patrick Venturedace6802018-11-01 16:52:10 -0700152 invIntf.emplace("xyz.openbmc_project.Inventory.Item", std::move(invProp));
Anthony Wilson206f0042019-05-02 00:02:23 -0500153 // Add any extra interfaces we want to associate with the inventory item
154 for (auto& iface : ifaces)
155 {
156 invIntf.emplace(iface, PropertyMap());
157 }
Gunnar Mills80292bb2017-07-05 16:34:51 -0500158 invObj.emplace(std::move(inventory), std::move(invIntf));
159
160 return invObj;
161}
162
163void Presence::updateInventory(bool present)
164{
165 ObjectMap invObj = getObjectMap(present);
166
Gunnar Mills765725e2017-07-06 14:17:44 -0500167 log<level::INFO>("Updating inventory present property",
168 entry("PRESENT=%d", present),
Joseph Reynolds32c3f632018-05-17 12:11:18 -0500169 entry("PATH=%s", inventory.c_str()));
Gunnar Mills765725e2017-07-06 14:17:44 -0500170
Gunnar Mills80292bb2017-07-05 16:34:51 -0500171 auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus);
172
173 // Update inventory
Patrick Venturedace6802018-11-01 16:52:10 -0700174 auto invMsg = bus.new_method_call(invService.c_str(), INVENTORY_PATH,
175 INVENTORY_INTF, "Notify");
Gunnar Mills80292bb2017-07-05 16:34:51 -0500176 invMsg.append(std::move(invObj));
177 auto invMgrResponseMsg = bus.call(invMsg);
178 if (invMgrResponseMsg.is_method_error())
179 {
Patrick Venturedace6802018-11-01 16:52:10 -0700180 log<level::ERR>("Error in inventory manager call to update inventory");
Gunnar Mills80292bb2017-07-05 16:34:51 -0500181 elog<InternalFailure>();
182 }
Matt Spinler902d1c32017-09-01 11:03:02 -0500183}
184
185void Presence::bindOrUnbindDrivers(bool present)
186{
187 auto action = (present) ? "bind" : "unbind";
188
189 for (auto& driver : drivers)
190 {
191 auto path = std::get<pathField>(driver) / action;
192 auto device = std::get<deviceField>(driver);
193
194 if (present)
195 {
Patrick Venturedace6802018-11-01 16:52:10 -0700196 log<level::INFO>("Binding a device driver",
197 entry("PATH=%s", path.c_str()),
198 entry("DEVICE=%s", device.c_str()));
Matt Spinler902d1c32017-09-01 11:03:02 -0500199 }
200 else
201 {
Patrick Venturedace6802018-11-01 16:52:10 -0700202 log<level::INFO>("Unbinding a device driver",
203 entry("PATH=%s", path.c_str()),
204 entry("DEVICE=%s", device.c_str()));
Matt Spinler902d1c32017-09-01 11:03:02 -0500205 }
206
207 std::ofstream file;
208
Patrick Venturedace6802018-11-01 16:52:10 -0700209 file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
210 std::ofstream::eofbit);
Matt Spinler902d1c32017-09-01 11:03:02 -0500211
212 try
213 {
214 file.open(path);
215 file << device;
216 file.close();
217 }
Patrick Williams67554142021-10-06 13:00:15 -0500218 catch (const std::exception& e)
Matt Spinler902d1c32017-09-01 11:03:02 -0500219 {
220 auto err = errno;
221
222 log<level::ERR>("Failed binding or unbinding a device "
223 "after a card was removed or added",
224 entry("PATH=%s", path.c_str()),
225 entry("DEVICE=%s", device.c_str()),
226 entry("ERRNO=%d", err));
227 }
228 }
Gunnar Mills80292bb2017-07-05 16:34:51 -0500229}
230
Gunnar Mills72639152017-06-22 15:06:21 -0500231} // namespace presence
232} // namespace gpio
233} // namespace phosphor