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