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