blob: ecc4813d56fe524aafa86c7f475501d8d7484f42 [file] [log] [blame]
Gunnar Mills5f101102017-06-29 13:07:39 -05001#include <fcntl.h>
Matt Spinler902d1c32017-09-01 11:03:02 -05002#include <fstream>
3#include <libevdev/libevdev.h>
Gunnar Mills5f101102017-06-29 13:07:39 -05004#include <phosphor-logging/elog.hpp>
5#include <phosphor-logging/log.hpp>
6#include <phosphor-logging/elog-errors.hpp>
7#include "xyz/openbmc_project/Common/error.hpp"
Gunnar Mills72639152017-06-22 15:06:21 -05008#include "gpio_presence.hpp"
9
10namespace phosphor
11{
12namespace gpio
13{
14namespace presence
15{
16
Gunnar Mills5f101102017-06-29 13:07:39 -050017using namespace phosphor::logging;
18using namespace sdbusplus::xyz::openbmc_project::Common::Error;
19
Gunnar Mills80292bb2017-07-05 16:34:51 -050020constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory";
21constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager";
22
23constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
24constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
25constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
26
27std::string getService(const std::string& path,
28 const std::string& interface,
29 sdbusplus::bus::bus& bus)
30{
31 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME,
32 MAPPER_PATH,
33 MAPPER_INTERFACE,
34 "GetObject");
35
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
48
49 std::map<std::string, std::vector<std::string>> mapperResponse;
50 mapperResponseMsg.read(mapperResponse);
51
52 if (mapperResponse.empty())
53 {
54 log<level::ERR>(
55 "Error in mapper response for getting service name",
56 entry("PATH=%s", path.c_str()),
57 entry("INTERFACE=%s", interface.c_str()));
58 elog<InternalFailure>();
59 }
60
61 return mapperResponse.begin()->first;
62}
63
Gunnar Mills5f101102017-06-29 13:07:39 -050064void Presence::determinePresence()
65{
Gunnar Mills80292bb2017-07-05 16:34:51 -050066 auto present = false;
Gunnar Mills5f101102017-06-29 13:07:39 -050067 auto value = static_cast<int>(0);
68 auto fetch_rc = libevdev_fetch_event_value(devicePtr.get(), EV_KEY,
69 key, &value);
70 if (0 == fetch_rc)
71 {
72 log<level::ERR>("Device does not support event type",
73 entry("KEYCODE=%d", key));
74 elog<InternalFailure>();
75 return;
76 }
Gunnar Mills80292bb2017-07-05 16:34:51 -050077 if (value > 0)
78 {
79 present = true;
80 }
81
82 updateInventory(present);
Gunnar Mills5f101102017-06-29 13:07:39 -050083}
84
Gunnar Mills765725e2017-07-06 14:17:44 -050085// Callback handler when there is an activity on the FD
86int Presence::processEvents(sd_event_source* es, int fd,
87 uint32_t revents, void* userData)
88{
89 auto presence = static_cast<Presence*>(userData);
90
91 presence->analyzeEvent();
92 return 0;
93}
94
95
96// Analyzes the GPIO event
97void Presence::analyzeEvent()
98{
99
100 // Data returned
101 struct input_event ev {};
102 int rc = 0;
103
104 // While testing, observed that not having a loop here was leading
105 // into events being missed.
106 while (rc >= 0)
107 {
108 // Wait until no more events are available on the device.
109 rc = libevdev_next_event(devicePtr.get(),
110 LIBEVDEV_READ_FLAG_NORMAL, &ev);
111 if (rc < 0)
112 {
113 // There was an error waiting for events, mostly that there are no
114 // events to be read.. So continue waiting...
115 return;
116 }
117
118 if (rc == LIBEVDEV_READ_STATUS_SUCCESS)
119 {
120 if (ev.type == EV_SYN && ev.code == SYN_REPORT)
121 {
122 continue;
123 }
124 else if (ev.code == key)
125 {
126 auto present = false;
127 if (ev.value > 0)
128 {
129 present = true;
130 }
131 updateInventory(present);
Matt Spinler902d1c32017-09-01 11:03:02 -0500132 bindOrUnbindDrivers(present);
Gunnar Mills765725e2017-07-06 14:17:44 -0500133 }
134 }
135 }
136
137 return;
138}
Gunnar Mills80292bb2017-07-05 16:34:51 -0500139
140Presence::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",
149 std::move(invProp));
150 invObj.emplace(std::move(inventory), std::move(invIntf));
151
152 return invObj;
153}
154
155void Presence::updateInventory(bool present)
156{
157 ObjectMap invObj = getObjectMap(present);
158
Gunnar Mills765725e2017-07-06 14:17:44 -0500159 log<level::INFO>("Updating inventory present property",
160 entry("PRESENT=%d", present),
161 entry("PATH=%s", inventory));
162
Gunnar Mills80292bb2017-07-05 16:34:51 -0500163 auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus);
164
165 // Update inventory
166 auto invMsg = bus.new_method_call(invService.c_str(),
167 INVENTORY_PATH,
168 INVENTORY_INTF,
169 "Notify");
170 invMsg.append(std::move(invObj));
171 auto invMgrResponseMsg = bus.call(invMsg);
172 if (invMgrResponseMsg.is_method_error())
173 {
174 log<level::ERR>(
175 "Error in inventory manager call to update inventory");
176 elog<InternalFailure>();
177 }
Matt Spinler902d1c32017-09-01 11:03:02 -0500178
179}
180
181void Presence::bindOrUnbindDrivers(bool present)
182{
183 auto action = (present) ? "bind" : "unbind";
184
185 for (auto& driver : drivers)
186 {
187 auto path = std::get<pathField>(driver) / action;
188 auto device = std::get<deviceField>(driver);
189
190 if (present)
191 {
192 log<level::INFO>(
193 "Binding a device driver",
194 entry("PATH=%s", path.c_str()),
195 entry("DEVICE=%s", device.c_str()));
196 }
197 else
198 {
199 log<level::INFO>(
200 "Unbinding a device driver",
201 entry("PATH=%s", path.c_str()),
202 entry("DEVICE=%s", device.c_str()));
203 }
204
205 std::ofstream file;
206
207 file.exceptions(
208 std::ofstream::failbit |
209 std::ofstream::badbit |
210 std::ofstream::eofbit);
211
212 try
213 {
214 file.open(path);
215 file << device;
216 file.close();
217 }
218 catch (std::exception& e)
219 {
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
231
Gunnar Mills72639152017-06-22 15:06:21 -0500232} // namespace presence
233} // namespace gpio
234} // namespace phosphor
235