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 
scheduleEventHandler()35 void GpioMonitor::scheduleEventHandler()
36 {
37     gpioEventDescriptor.async_wait(
38         boost::asio::posix::stream_descriptor::wait_read,
39         [this](const boost::system::error_code& ec) {
40             if (ec)
41             {
42                 lg2::error("{GPIO} event handler error: {ERROR}", "GPIO",
43                            gpioLineMsg, "ERROR", ec.message());
44                 return;
45             }
46             gpioEventHandler();
47         });
48 }
49 
gpioEventHandler()50 void GpioMonitor::gpioEventHandler()
51 {
52     gpiod_line_event gpioLineEvent;
53 
54     if (gpiod_line_event_read_fd(gpioEventDescriptor.native_handle(),
55                                  &gpioLineEvent) < 0)
56     {
57         lg2::error("Failed to read {GPIO} from fd", "GPIO", gpioLineMsg);
58         return;
59     }
60 
61     if (gpioLineEvent.event_type == GPIOD_LINE_EVENT_RISING_EDGE)
62     {
63         lg2::info("{GPIO} Asserted", "GPIO", gpioLineMsg);
64     }
65     else
66     {
67         lg2::info("{GPIO} Deasserted", "GPIO", gpioLineMsg);
68     }
69 
70     /* Execute the target if it is defined. */
71     if (!target.empty())
72     {
73         auto bus = sdbusplus::bus::new_default();
74         auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
75                                           SYSTEMD_INTERFACE, "StartUnit");
76         method.append(target);
77         method.append("replace");
78 
79         bus.call_noreply(method);
80     }
81 
82     std::vector<std::string> targetsToStart;
83     if (gpioLineEvent.event_type == GPIOD_LINE_EVENT_RISING_EDGE)
84     {
85         auto risingFind = targets.find(rising);
86         if (risingFind != targets.end())
87         {
88             targetsToStart = risingFind->second;
89         }
90     }
91     else
92     {
93         auto fallingFind = targets.find(falling);
94         if (fallingFind != targets.end())
95         {
96             targetsToStart = fallingFind->second;
97         }
98     }
99 
100     /* Execute the multi targets if it is defined. */
101     if (!targetsToStart.empty())
102     {
103         auto bus = sdbusplus::bus::new_default();
104         for (auto& tar : targetsToStart)
105         {
106             auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
107                                               SYSTEMD_INTERFACE, "StartUnit");
108             method.append(tar, "replace");
109             bus.call_noreply(method);
110         }
111     }
112 
113     /* if not required to continue monitoring then return */
114     if (!continueAfterEvent)
115     {
116         return;
117     }
118 
119     /* Schedule a wait event */
120     scheduleEventHandler();
121 }
122 
requestGPIOEvents()123 int GpioMonitor::requestGPIOEvents()
124 {
125     /* Request an event to monitor for respected gpio line */
126     if (gpiod_line_request(gpioLine, &gpioConfig, 0) < 0)
127     {
128         lg2::error("Failed to request {GPIO}", "GPIO", gpioLineMsg);
129         return -1;
130     }
131 
132     int gpioLineFd = gpiod_line_event_get_fd(gpioLine);
133     if (gpioLineFd < 0)
134     {
135         lg2::error("Failed to get fd for {GPIO}", "GPIO", gpioLineMsg);
136         return -1;
137     }
138 
139     lg2::info("{GPIO} monitoring started", "GPIO", gpioLineMsg);
140 
141     /* Assign line fd to descriptor for monitoring */
142     gpioEventDescriptor.assign(gpioLineFd);
143 
144     /* Schedule a wait event */
145     scheduleEventHandler();
146 
147     return 0;
148 }
149 } // namespace gpio
150 } // namespace phosphor
151