1 #include <fcntl.h>
2 #include <fstream>
3 #include <libevdev/libevdev.h>
4 #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"
8 #include "gpio_presence.hpp"
9 
10 namespace phosphor
11 {
12 namespace gpio
13 {
14 namespace presence
15 {
16 
17 using namespace phosphor::logging;
18 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
19 
20 constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory";
21 constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager";
22 
23 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
24 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
25 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
26 
27 std::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 
64 void Presence::determinePresence()
65 {
66     auto present = false;
67     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     }
77     if (value > 0)
78     {
79         present = true;
80     }
81 
82     updateInventory(present);
83 }
84 
85 // Callback handler when there is an activity on the FD
86 int 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
97 void 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);
132                 bindOrUnbindDrivers(present);
133             }
134         }
135     }
136 
137     return;
138 }
139 
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",
149                     std::move(invProp));
150     invObj.emplace(std::move(inventory), std::move(invIntf));
151 
152     return invObj;
153 }
154 
155 void Presence::updateInventory(bool present)
156 {
157     ObjectMap invObj = getObjectMap(present);
158 
159     log<level::INFO>("Updating inventory present property",
160                      entry("PRESENT=%d", present),
161                      entry("PATH=%s", inventory));
162 
163     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     }
178 
179 }
180 
181 void 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     }
229 }
230 
231 
232 } // namespace presence
233 } // namespace gpio
234 } // namespace phosphor
235 
236