xref: /openbmc/phosphor-gpio-monitor/presence/gpio_presence.cpp (revision 1c88803f75c4d5b606345df7bdef318b677f9696)
1dace680fSPatrick Venture #include "gpio_presence.hpp"
2dace680fSPatrick Venture 
3dace680fSPatrick Venture #include "xyz/openbmc_project/Common/error.hpp"
4dace680fSPatrick Venture 
55f101103SGunnar Mills #include <fcntl.h>
6902d1c37SMatt Spinler #include <libevdev/libevdev.h>
7dace680fSPatrick Venture 
8dace680fSPatrick Venture #include <phosphor-logging/elog-errors.hpp>
95f101103SGunnar Mills #include <phosphor-logging/elog.hpp>
102a8848c6SGeorge Liu #include <phosphor-logging/lg2.hpp>
117263915aSGunnar Mills 
1239084b4aSPatrick Williams #include <fstream>
1339084b4aSPatrick Williams 
147263915aSGunnar Mills namespace phosphor
157263915aSGunnar Mills {
167263915aSGunnar Mills namespace gpio
177263915aSGunnar Mills {
187263915aSGunnar Mills namespace presence
197263915aSGunnar Mills {
207263915aSGunnar Mills 
215f101103SGunnar Mills using namespace phosphor::logging;
225f101103SGunnar Mills using namespace sdbusplus::xyz::openbmc_project::Common::Error;
235f101103SGunnar Mills 
2480292bbeSGunnar Mills constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory";
2580292bbeSGunnar Mills constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager";
2680292bbeSGunnar Mills 
2780292bbeSGunnar Mills constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
2880292bbeSGunnar Mills constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
2980292bbeSGunnar Mills constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
3080292bbeSGunnar Mills 
getService(const std::string & path,const std::string & interface,sdbusplus::bus_t & bus)31dace680fSPatrick Venture std::string getService(const std::string& path, const std::string& interface,
32bc5b3751SPatrick Williams                        sdbusplus::bus_t& bus)
3380292bbeSGunnar Mills {
34dace680fSPatrick Venture     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
35dace680fSPatrick Venture                                           MAPPER_INTERFACE, "GetObject");
3680292bbeSGunnar Mills 
3780292bbeSGunnar Mills     mapperCall.append(path);
3880292bbeSGunnar Mills     mapperCall.append(std::vector<std::string>({interface}));
3980292bbeSGunnar Mills 
407abda62cSGeorge Liu     std::map<std::string, std::vector<std::string>> mapperResponse;
417abda62cSGeorge Liu     try
427abda62cSGeorge Liu     {
4380292bbeSGunnar Mills         auto mapperResponseMsg = bus.call(mapperCall);
447abda62cSGeorge Liu         mapperResponseMsg.read(mapperResponse);
457abda62cSGeorge Liu     }
467abda62cSGeorge Liu     catch (const sdbusplus::exception_t& e)
4780292bbeSGunnar Mills     {
482a8848c6SGeorge Liu         lg2::error(
492a8848c6SGeorge Liu             "Error in mapper call to get service name, path: {PATH}, interface: {INTERFACE}, error: {ERROR}",
502a8848c6SGeorge Liu             "PATH", path, "INTERFACE", interface, "ERROR", e);
5180292bbeSGunnar Mills         elog<InternalFailure>();
5280292bbeSGunnar Mills     }
5380292bbeSGunnar Mills 
5480292bbeSGunnar Mills     return mapperResponse.begin()->first;
5580292bbeSGunnar Mills }
5680292bbeSGunnar Mills 
determinePresence()575f101103SGunnar Mills void Presence::determinePresence()
585f101103SGunnar Mills {
5980292bbeSGunnar Mills     auto present = false;
605f101103SGunnar Mills     auto value = static_cast<int>(0);
618377d59cSPatrick Williams     auto fetch_rc =
628377d59cSPatrick Williams         libevdev_fetch_event_value(devicePtr.get(), EV_KEY, key, &value);
635f101103SGunnar Mills     if (0 == fetch_rc)
645f101103SGunnar Mills     {
652a8848c6SGeorge Liu         lg2::error("Device does not support event type, key: {KEYCODE}",
662a8848c6SGeorge Liu                    "KEYCODE", key);
675f101103SGunnar Mills         elog<InternalFailure>();
685f101103SGunnar Mills         return;
695f101103SGunnar Mills     }
7080292bbeSGunnar Mills     if (value > 0)
7180292bbeSGunnar Mills     {
7280292bbeSGunnar Mills         present = true;
735f101103SGunnar Mills     }
745f101103SGunnar Mills 
7580292bbeSGunnar Mills     updateInventory(present);
7680292bbeSGunnar Mills }
7780292bbeSGunnar Mills 
78765725e0SGunnar Mills // Callback handler when there is an activity on the FD
processEvents(sd_event_source *,int,uint32_t,void * userData)7986d16f03SBrad Bishop int Presence::processEvents(sd_event_source*, int, uint32_t, void* userData)
80765725e0SGunnar Mills {
81765725e0SGunnar Mills     auto presence = static_cast<Presence*>(userData);
82765725e0SGunnar Mills 
83765725e0SGunnar Mills     presence->analyzeEvent();
84765725e0SGunnar Mills     return 0;
85765725e0SGunnar Mills }
86765725e0SGunnar Mills 
87765725e0SGunnar Mills // Analyzes the GPIO event
analyzeEvent()88765725e0SGunnar Mills void Presence::analyzeEvent()
89765725e0SGunnar Mills {
90765725e0SGunnar Mills     // Data returned
91*1c88803fSPatrick Williams     struct input_event ev{};
92765725e0SGunnar Mills     int rc = 0;
93765725e0SGunnar Mills 
94765725e0SGunnar Mills     // While testing, observed that not having a loop here was leading
95765725e0SGunnar Mills     // into events being missed.
96765725e0SGunnar Mills     while (rc >= 0)
97765725e0SGunnar Mills     {
98765725e0SGunnar Mills         // Wait until no more events are available on the device.
99dace680fSPatrick Venture         rc = libevdev_next_event(devicePtr.get(), LIBEVDEV_READ_FLAG_NORMAL,
100dace680fSPatrick Venture                                  &ev);
101765725e0SGunnar Mills         if (rc < 0)
102765725e0SGunnar Mills         {
103765725e0SGunnar Mills             // There was an error waiting for events, mostly that there are no
104765725e0SGunnar Mills             // events to be read.. So continue waiting...
105765725e0SGunnar Mills             return;
106765725e0SGunnar Mills         }
107765725e0SGunnar Mills 
108765725e0SGunnar Mills         if (rc == LIBEVDEV_READ_STATUS_SUCCESS)
109765725e0SGunnar Mills         {
110765725e0SGunnar Mills             if (ev.type == EV_SYN && ev.code == SYN_REPORT)
111765725e0SGunnar Mills             {
112765725e0SGunnar Mills                 continue;
113765725e0SGunnar Mills             }
114765725e0SGunnar Mills             else if (ev.code == key)
115765725e0SGunnar Mills             {
116765725e0SGunnar Mills                 auto present = false;
117765725e0SGunnar Mills                 if (ev.value > 0)
118765725e0SGunnar Mills                 {
119765725e0SGunnar Mills                     present = true;
120b08a0f69SBrandon Wyman                     std::this_thread::sleep_for(
121b08a0f69SBrandon Wyman                         std::chrono::milliseconds(delay));
122b08a0f69SBrandon Wyman                     bindOrUnbindDrivers(present);
123b08a0f69SBrandon Wyman                     updateInventory(present);
124765725e0SGunnar Mills                 }
125b08a0f69SBrandon Wyman                 else
126b08a0f69SBrandon Wyman                 {
127765725e0SGunnar Mills                     updateInventory(present);
128902d1c37SMatt Spinler                     bindOrUnbindDrivers(present);
129765725e0SGunnar Mills                 }
130765725e0SGunnar Mills             }
131765725e0SGunnar Mills         }
132b08a0f69SBrandon Wyman     }
133765725e0SGunnar Mills 
134765725e0SGunnar Mills     return;
135765725e0SGunnar Mills }
13680292bbeSGunnar Mills 
getObjectMap(bool present)13780292bbeSGunnar Mills Presence::ObjectMap Presence::getObjectMap(bool present)
13880292bbeSGunnar Mills {
13980292bbeSGunnar Mills     ObjectMap invObj;
14080292bbeSGunnar Mills     InterfaceMap invIntf;
14180292bbeSGunnar Mills     PropertyMap invProp;
14280292bbeSGunnar Mills 
14380292bbeSGunnar Mills     invProp.emplace("Present", present);
14480292bbeSGunnar Mills     invProp.emplace("PrettyName", name);
145dace680fSPatrick Venture     invIntf.emplace("xyz.openbmc_project.Inventory.Item", std::move(invProp));
146206f0040SAnthony Wilson     // Add any extra interfaces we want to associate with the inventory item
147206f0040SAnthony Wilson     for (auto& iface : ifaces)
148206f0040SAnthony Wilson     {
149206f0040SAnthony Wilson         invIntf.emplace(iface, PropertyMap());
150206f0040SAnthony Wilson     }
15180292bbeSGunnar Mills     invObj.emplace(std::move(inventory), std::move(invIntf));
15280292bbeSGunnar Mills 
15380292bbeSGunnar Mills     return invObj;
15480292bbeSGunnar Mills }
15580292bbeSGunnar Mills 
updateInventory(bool present)15680292bbeSGunnar Mills void Presence::updateInventory(bool present)
15780292bbeSGunnar Mills {
15880292bbeSGunnar Mills     ObjectMap invObj = getObjectMap(present);
15980292bbeSGunnar Mills 
1602a8848c6SGeorge Liu     lg2::info(
1612a8848c6SGeorge Liu         "Updating inventory present property value to {PRESENT}, path: {PATH}",
1622a8848c6SGeorge Liu         "PRESENT", present, "PATH", inventory);
163765725e0SGunnar Mills 
16480292bbeSGunnar Mills     auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus);
16580292bbeSGunnar Mills 
16680292bbeSGunnar Mills     // Update inventory
167dace680fSPatrick Venture     auto invMsg = bus.new_method_call(invService.c_str(), INVENTORY_PATH,
168dace680fSPatrick Venture                                       INVENTORY_INTF, "Notify");
16980292bbeSGunnar Mills     invMsg.append(std::move(invObj));
1707abda62cSGeorge Liu     try
17180292bbeSGunnar Mills     {
1727abda62cSGeorge Liu         auto invMgrResponseMsg = bus.call(invMsg);
1737abda62cSGeorge Liu     }
1747abda62cSGeorge Liu     catch (const sdbusplus::exception_t& e)
1757abda62cSGeorge Liu     {
1762a8848c6SGeorge Liu         lg2::error(
1772a8848c6SGeorge Liu             "Error in inventory manager call to update inventory: {ERROR}",
1782a8848c6SGeorge Liu             "ERROR", e);
17980292bbeSGunnar Mills         elog<InternalFailure>();
18080292bbeSGunnar Mills     }
181902d1c37SMatt Spinler }
182902d1c37SMatt Spinler 
bindOrUnbindDrivers(bool present)183902d1c37SMatt Spinler void Presence::bindOrUnbindDrivers(bool present)
184902d1c37SMatt Spinler {
185902d1c37SMatt Spinler     auto action = (present) ? "bind" : "unbind";
186902d1c37SMatt Spinler 
187902d1c37SMatt Spinler     for (auto& driver : drivers)
188902d1c37SMatt Spinler     {
189902d1c37SMatt Spinler         auto path = std::get<pathField>(driver) / action;
190902d1c37SMatt Spinler         auto device = std::get<deviceField>(driver);
191902d1c37SMatt Spinler 
192902d1c37SMatt Spinler         if (present)
193902d1c37SMatt Spinler         {
1942a8848c6SGeorge Liu             lg2::info("Binding a {DEVICE} driver: {PATH}", "DEVICE", device,
1952a8848c6SGeorge Liu                       "PATH", path);
196902d1c37SMatt Spinler         }
197902d1c37SMatt Spinler         else
198902d1c37SMatt Spinler         {
1992a8848c6SGeorge Liu             lg2::info("Unbinding a {DEVICE} driver: {PATH}", "DEVICE", device,
2002a8848c6SGeorge Liu                       "PATH", path);
201902d1c37SMatt Spinler         }
202902d1c37SMatt Spinler 
203902d1c37SMatt Spinler         std::ofstream file;
204902d1c37SMatt Spinler 
205dace680fSPatrick Venture         file.exceptions(std::ofstream::failbit | std::ofstream::badbit |
206902d1c37SMatt Spinler                         std::ofstream::eofbit);
207902d1c37SMatt Spinler 
208902d1c37SMatt Spinler         try
209902d1c37SMatt Spinler         {
210902d1c37SMatt Spinler             file.open(path);
211902d1c37SMatt Spinler             file << device;
212902d1c37SMatt Spinler             file.close();
213902d1c37SMatt Spinler         }
21467554144SPatrick Williams         catch (const std::exception& e)
215902d1c37SMatt Spinler         {
2162a8848c6SGeorge Liu             lg2::error(
2172a8848c6SGeorge Liu                 "Failed binding or unbinding a {DEVICE} after a card was removed or added, path: {PATH}, error: {ERROR}",
2182a8848c6SGeorge Liu                 "DEVICE", device, "PATH", path, "ERROR", e);
219902d1c37SMatt Spinler         }
220902d1c37SMatt Spinler     }
22180292bbeSGunnar Mills }
22280292bbeSGunnar Mills 
2237263915aSGunnar Mills } // namespace presence
2247263915aSGunnar Mills } // namespace gpio
2257263915aSGunnar Mills } // namespace phosphor
226