xref: /openbmc/phosphor-gpio-monitor/multi-presence/gpio_presence.cpp (revision 75ff16717de9a7b3beeda9f3cace9456cad98156)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 
4 #include "gpio_presence.hpp"
5 
6 #include "xyz/openbmc_project/Common/error.hpp"
7 
8 #include <phosphor-logging/elog-errors.hpp>
9 #include <phosphor-logging/elog.hpp>
10 #include <phosphor-logging/lg2.hpp>
11 #include <sdbusplus/bus.hpp>
12 
13 namespace phosphor
14 {
15 namespace gpio
16 {
17 
18 using namespace phosphor::logging;
19 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
20 
21 constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory";
22 constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager";
23 
24 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
25 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
26 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
27 
getService(const std::string & path,const std::string & interface,sdbusplus::bus_t & bus)28 std::string getService(const std::string& path, const std::string& interface,
29                        sdbusplus::bus_t& bus)
30 {
31     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
32                                           MAPPER_INTERFACE, "GetObject");
33 
34     mapperCall.append(path);
35     mapperCall.append(std::vector<std::string>({interface}));
36 
37     std::map<std::string, std::vector<std::string>> mapperResponse;
38     try
39     {
40         auto mapperResponseMsg = bus.call(mapperCall);
41         mapperResponseMsg.read(mapperResponse);
42     }
43     catch (const sdbusplus::exception_t& e)
44     {
45         lg2::error(
46             "Error in mapper call to get service name, path: {PATH}, interface: {INTERFACE}, error: {ERROR}",
47             "PATH", path, "INTERFACE", interface, "ERROR", e);
48         elog<InternalFailure>();
49     }
50 
51     return mapperResponse.begin()->first;
52 }
53 
getObjectMap(bool present)54 GpioPresence::ObjectMap GpioPresence::getObjectMap(bool present)
55 {
56     ObjectMap invObj;
57     InterfaceMap invIntf;
58     PropertyMap invProp;
59 
60     invProp.emplace("Present", present);
61     invProp.emplace("PrettyName", name);
62     invIntf.emplace("xyz.openbmc_project.Inventory.Item", std::move(invProp));
63     // Add any extra interfaces we want to associate with the inventory item
64     for (auto& iface : interfaces)
65     {
66         invIntf.emplace(iface, PropertyMap());
67     }
68     invObj.emplace(std::move(inventory), std::move(invIntf));
69 
70     return invObj;
71 }
72 
updateInventory(bool present)73 void GpioPresence::updateInventory(bool present)
74 {
75     ObjectMap invObj = getObjectMap(present);
76 
77     lg2::info(
78         "Updating inventory present property value to {PRESENT}, path: {PATH}",
79         "PRESENT", present, "PATH", inventory);
80 
81     auto bus = sdbusplus::bus::new_default();
82     auto invService = getService(INVENTORY_PATH, INVENTORY_INTF, bus);
83 
84     // Update inventory
85     auto invMsg = bus.new_method_call(invService.c_str(), INVENTORY_PATH,
86                                       INVENTORY_INTF, "Notify");
87     invMsg.append(std::move(invObj));
88     try
89     {
90         auto invMgrResponseMsg = bus.call(invMsg);
91     }
92     catch (const sdbusplus::exception_t& e)
93     {
94         lg2::error(
95             "Error in inventory manager call to update inventory: {ERROR}",
96             "ERROR", e);
97         elog<InternalFailure>();
98     }
99 }
100 
scheduleEventHandler()101 void GpioPresence::scheduleEventHandler()
102 {
103     std::string gpio = std::string(gpioLineMsg);
104 
105     gpioEventDescriptor.async_wait(
106         boost::asio::posix::stream_descriptor::wait_read,
107         [this, gpio](const boost::system::error_code& ec) {
108             if (ec == boost::asio::error::operation_aborted)
109             {
110                 // we were cancelled
111                 return;
112             }
113             if (ec)
114             {
115                 lg2::error("{GPIO} event handler error: {ERROR}", "GPIO", gpio,
116                            "ERROR", ec.message());
117                 return;
118             }
119             gpioEventHandler();
120         });
121 }
122 
cancelEventHandler()123 void GpioPresence::cancelEventHandler()
124 {
125     gpioEventDescriptor.cancel();
126 }
127 
gpioEventHandler()128 void GpioPresence::gpioEventHandler()
129 {
130     gpiod_line_event gpioLineEvent;
131 
132     if (gpiod_line_event_read_fd(gpioEventDescriptor.native_handle(),
133                                  &gpioLineEvent) < 0)
134     {
135         lg2::error("Failed to read {GPIO} from fd", "GPIO", gpioLineMsg);
136         return;
137     }
138 
139     if (gpioLineEvent.event_type == GPIOD_LINE_EVENT_RISING_EDGE)
140     {
141         lg2::info("{GPIO} Asserted", "GPIO", gpioLineMsg);
142     }
143     else
144     {
145         lg2::info("{GPIO} Deasserted", "GPIO", gpioLineMsg);
146     }
147     updateInventory(gpioLineEvent.event_type == GPIOD_LINE_EVENT_RISING_EDGE);
148 
149     /* Schedule a wait event */
150     scheduleEventHandler();
151 }
152 
requestGPIOEvents()153 int GpioPresence::requestGPIOEvents()
154 {
155     std::string flags;
156 
157     /* Request an event to monitor for respected gpio line */
158     if (gpiod_line_request(gpioLine, &gpioConfig, 0) < 0)
159     {
160         lg2::error("Failed to request {GPIO}: {ERRNO}", "GPIO", gpioLineMsg,
161                    "ERRNO", errno);
162         return -1;
163     }
164 
165     int gpioLineFd = gpiod_line_event_get_fd(gpioLine);
166     if (gpioLineFd < 0)
167     {
168         lg2::error("Failed to get fd for {GPIO}", "GPIO", gpioLineMsg);
169         return -1;
170     }
171 
172     if (gpioConfig.flags & GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE)
173     {
174         flags += " Bias DISABLE";
175     }
176     else if (gpioConfig.flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP)
177     {
178         flags += " Bias PULL_UP";
179     }
180     else if (gpioConfig.flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN)
181     {
182         flags += " Bias PULL_DOWN";
183     }
184 
185     if (gpioConfig.flags & GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW)
186     {
187         flags += " ActiveLow";
188     }
189 
190     if (!flags.empty())
191     {
192         flags = "[" + flags + "]";
193     }
194 
195     lg2::info("{GPIO} {FLAGS} monitoring started", "GPIO", gpioLineMsg, "FLAGS",
196               flags);
197 
198     /* Assign line fd to descriptor for monitoring */
199     gpioEventDescriptor.assign(gpioLineFd);
200 
201     updateInventory(gpiod_line_get_value(gpioLine));
202 
203     /* Schedule a wait event */
204     scheduleEventHandler();
205 
206     return 0;
207 }
208 } // namespace gpio
209 } // namespace phosphor
210