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