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