xref: /openbmc/phosphor-gpio-monitor/gpioMon.cpp (revision ba917cf7cf4163f6a71d8f67609237f013149743)
1 /**
2  * Copyright © 2019 Facebook
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "gpioMon.hpp"
18 
19 #include <phosphor-logging/lg2.hpp>
20 #include <sdbusplus/bus.hpp>
21 
22 namespace phosphor
23 {
24 namespace gpio
25 {
26 
27 /* systemd service to kick start a target. */
28 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
29 constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1";
30 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
31 
32 constexpr auto falling = "FALLING";
33 constexpr auto rising = "RISING";
34 constexpr auto init_high = "INIT_HIGH";
35 constexpr auto init_low = "INIT_LOW";
36 
scheduleEventHandler()37 void GpioMonitor::scheduleEventHandler()
38 {
39     gpioEventDescriptor.async_wait(
40         boost::asio::posix::stream_descriptor::wait_read,
41         [this](const boost::system::error_code& ec) {
42             if (ec)
43             {
44                 lg2::error("{GPIO} event handler error: {ERROR}", "GPIO",
45                            gpioLineMsg, "ERROR", ec.message());
46                 return;
47             }
48             gpioEventHandler();
49         });
50 }
51 
gpioEventHandler()52 void GpioMonitor::gpioEventHandler()
53 {
54     gpiod_line_event gpioLineEvent;
55 
56     if (gpiod_line_event_read_fd(gpioEventDescriptor.native_handle(),
57                                  &gpioLineEvent) < 0)
58     {
59         lg2::error("Failed to read {GPIO} from fd", "GPIO", gpioLineMsg);
60         return;
61     }
62 
63     if (gpioLineEvent.event_type == GPIOD_LINE_EVENT_RISING_EDGE)
64     {
65         lg2::info("{GPIO} Asserted", "GPIO", gpioLineMsg);
66     }
67     else
68     {
69         lg2::info("{GPIO} Deasserted", "GPIO", gpioLineMsg);
70     }
71 
72     /* Execute the target if it is defined. */
73     if (!target.empty())
74     {
75         auto bus = sdbusplus::bus::new_default();
76         auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
77                                           SYSTEMD_INTERFACE, "StartUnit");
78         method.append(target);
79         method.append("replace");
80 
81         bus.call_noreply(method);
82     }
83 
84     std::vector<std::string> targetsToStart;
85     if (gpioLineEvent.event_type == GPIOD_LINE_EVENT_RISING_EDGE)
86     {
87         auto risingFind = targets.find(rising);
88         if (risingFind != targets.end())
89         {
90             targetsToStart = risingFind->second;
91         }
92     }
93     else
94     {
95         auto fallingFind = targets.find(falling);
96         if (fallingFind != targets.end())
97         {
98             targetsToStart = fallingFind->second;
99         }
100     }
101 
102     /* Execute the multi targets if it is defined. */
103     if (!targetsToStart.empty())
104     {
105         auto bus = sdbusplus::bus::new_default();
106         for (auto& tar : targetsToStart)
107         {
108             auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
109                                               SYSTEMD_INTERFACE, "StartUnit");
110             method.append(tar, "replace");
111             bus.call_noreply(method);
112         }
113     }
114 
115     /* if not required to continue monitoring then return */
116     if (!continueAfterEvent)
117     {
118         return;
119     }
120 
121     /* Schedule a wait event */
122     scheduleEventHandler();
123 }
124 
gpioHandleInitialState(bool value)125 void GpioMonitor::gpioHandleInitialState(bool value)
126 {
127     if (auto itr = targets.find(value ? init_high : init_low);
128         itr != targets.end())
129     {
130         auto bus = sdbusplus::bus::new_default();
131         for (const auto& tar : itr->second)
132         {
133             auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
134                                               SYSTEMD_INTERFACE, "StartUnit");
135             method.append(tar, "replace");
136             bus.call_noreply(method);
137         }
138     }
139 }
140 
requestGPIOEvents()141 int GpioMonitor::requestGPIOEvents()
142 {
143     /* Request an event to monitor for respected gpio line */
144     if (gpiod_line_request(gpioLine, &gpioConfig, 0) < 0)
145     {
146         lg2::error("Failed to request {GPIO}", "GPIO", gpioLineMsg);
147         return -1;
148     }
149 
150     int gpioLineFd = gpiod_line_event_get_fd(gpioLine);
151     if (gpioLineFd < 0)
152     {
153         lg2::error("Failed to get fd for {GPIO}", "GPIO", gpioLineMsg);
154         return -1;
155     }
156 
157     int value = gpiod_line_get_value(gpioLine);
158     if (value < 0)
159     {
160         lg2::error("Failed to get value for {GPIO} Error: {ERROR}", "GPIO",
161                    gpioLineMsg, "ERROR", strerror(errno));
162     }
163     else
164     {
165         gpioHandleInitialState(value != 0);
166     }
167 
168     lg2::info("{GPIO} monitoring started", "GPIO", gpioLineMsg);
169 
170     /* Assign line fd to descriptor for monitoring */
171     gpioEventDescriptor.assign(gpioLineFd);
172 
173     /* Schedule a wait event */
174     scheduleEventHandler();
175 
176     return 0;
177 }
178 } // namespace gpio
179 } // namespace phosphor
180