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