1 #include <libevdev/libevdev.h>
2 #include <fcntl.h>
3 #include <phosphor-logging/elog.hpp>
4 #include <phosphor-logging/log.hpp>
5 #include <phosphor-logging/elog-errors.hpp>
6 #include "xyz/openbmc_project/Common/error.hpp"
7 #include "gpio_presence.hpp"
8 
9 namespace phosphor
10 {
11 namespace gpio
12 {
13 namespace presence
14 {
15 
16 using namespace phosphor::logging;
17 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
18 
19 constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory";
20 constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager";
21 
22 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
23 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
24 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
25 
26 std::string getService(const std::string& path,
27                        const std::string& interface,
28                        sdbusplus::bus::bus& bus)
29 {
30     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME,
31                                           MAPPER_PATH,
32                                           MAPPER_INTERFACE,
33                                           "GetObject");
34 
35     mapperCall.append(path);
36     mapperCall.append(std::vector<std::string>({interface}));
37 
38     auto mapperResponseMsg = bus.call(mapperCall);
39     if (mapperResponseMsg.is_method_error())
40     {
41         log<level::ERR>("Error in mapper call to get service name",
42                         entry("PATH=%s", path.c_str()),
43                         entry("INTERFACE=%s", interface.c_str()));
44         elog<InternalFailure>();
45     }
46 
47 
48     std::map<std::string, std::vector<std::string>> mapperResponse;
49     mapperResponseMsg.read(mapperResponse);
50 
51     if (mapperResponse.empty())
52     {
53         log<level::ERR>(
54             "Error in mapper response for getting service name",
55             entry("PATH=%s", path.c_str()),
56             entry("INTERFACE=%s", interface.c_str()));
57         elog<InternalFailure>();
58     }
59 
60     return mapperResponse.begin()->first;
61 }
62 
63 void Presence::determinePresence()
64 {
65     auto present = false;
66     auto value = static_cast<int>(0);
67     auto fetch_rc = libevdev_fetch_event_value(devicePtr.get(), EV_KEY,
68                     key, &value);
69     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     }
76     if (value > 0)
77     {
78         present = true;
79     }
80 
81     updateInventory(present);
82 }
83 
84 // Callback handler when there is an activity on the FD
85 int Presence::processEvents(sd_event_source* es, int fd,
86                             uint32_t revents, void* userData)
87 {
88     auto presence = static_cast<Presence*>(userData);
89 
90     presence->analyzeEvent();
91     return 0;
92 }
93 
94 
95 // Analyzes the GPIO event
96 void Presence::analyzeEvent()
97 {
98 
99     // Data returned
100     struct input_event ev {};
101     int rc = 0;
102 
103     // While testing, observed that not having a loop here was leading
104     // into events being missed.
105     while (rc >= 0)
106     {
107         // Wait until no more events are available on the device.
108         rc = libevdev_next_event(devicePtr.get(),
109                                  LIBEVDEV_READ_FLAG_NORMAL, &ev);
110         if (rc < 0)
111         {
112             // There was an error waiting for events, mostly that there are no
113             // events to be read.. So continue waiting...
114             return;
115         }
116 
117         if (rc == LIBEVDEV_READ_STATUS_SUCCESS)
118         {
119             if (ev.type == EV_SYN && ev.code == SYN_REPORT)
120             {
121                 continue;
122             }
123             else if (ev.code == key)
124             {
125                 auto present = false;
126                 if (ev.value > 0)
127                 {
128                     present = true;
129                 }
130                 updateInventory(present);
131             }
132         }
133     }
134 
135     return;
136 }
137 
138 Presence::ObjectMap Presence::getObjectMap(bool present)
139 {
140     ObjectMap invObj;
141     InterfaceMap invIntf;
142     PropertyMap invProp;
143 
144     invProp.emplace("Present", present);
145     invProp.emplace("PrettyName", name);
146     invIntf.emplace("xyz.openbmc_project.Inventory.Item",
147                     std::move(invProp));
148     invObj.emplace(std::move(inventory), std::move(invIntf));
149 
150     return invObj;
151 }
152 
153 void Presence::updateInventory(bool present)
154 {
155     ObjectMap invObj = getObjectMap(present);
156 
157     log<level::INFO>("Updating inventory present property",
158                      entry("PRESENT=%d", present),
159                      entry("PATH=%s", inventory));
160 
161     auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus);
162 
163     // Update inventory
164     auto invMsg = bus.new_method_call(invService.c_str(),
165                                       INVENTORY_PATH,
166                                       INVENTORY_INTF,
167                                       "Notify");
168     invMsg.append(std::move(invObj));
169     auto invMgrResponseMsg = bus.call(invMsg);
170     if (invMgrResponseMsg.is_method_error())
171     {
172         log<level::ERR>(
173             "Error in inventory manager call to update inventory");
174         elog<InternalFailure>();
175     }
176 }
177 
178 
179 } // namespace presence
180 } // namespace gpio
181 } // namespace phosphor
182 
183