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 using namespace phosphor::logging;
33 
34 void GpioMonitor::scheduleEventHandler()
35 {
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                 std::string msg = gpioLineMsg + "event handler error" +
43                                   std::string(ec.message());
44                 log<level::ERR>(msg.c_str());
45                 return;
46             }
47             gpioEventHandler();
48         });
49 }
50 
51 void GpioMonitor::gpioEventHandler()
52 {
53     gpiod_line_event gpioLineEvent;
54 
55     if (gpiod_line_event_read_fd(gpioEventDescriptor.native_handle(),
56                                  &gpioLineEvent) < 0)
57     {
58         log<level::ERR>("Failed to read gpioLineEvent from fd",
59                         entry("GPIO_LINE=%s", gpioLineMsg.c_str()));
60         return;
61     }
62 
63     std::string logMessage =
64         gpioLineMsg + (gpioLineEvent.event_type == GPIOD_LINE_EVENT_RISING_EDGE
65                            ? " Asserted"
66                            : " Deasserted");
67 
68     log<level::INFO>(logMessage.c_str());
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     /* if not required to continue monitoring then return */
83     if (!continueAfterEvent)
84     {
85         return;
86     }
87 
88     /* Schedule a wait event */
89     scheduleEventHandler();
90 }
91 
92 int GpioMonitor::requestGPIOEvents()
93 {
94 
95     /* Request an event to monitor for respected gpio line */
96     if (gpiod_line_request(gpioLine, &gpioConfig, 0) < 0)
97     {
98         log<level::ERR>("Failed to request gpioLineEvent",
99                         entry("GPIO_LINE=%s", gpioLineMsg.c_str()));
100         return -1;
101     }
102 
103     int gpioLineFd = gpiod_line_event_get_fd(gpioLine);
104     if (gpioLineFd < 0)
105     {
106         log<level::ERR>("Failed to get fd for gpioLineEvent",
107                         entry("GPIO_LINE=%s", gpioLineMsg.c_str()));
108         return -1;
109     }
110 
111     std::string logMsg = gpioLineMsg + " monitoring started";
112     log<level::INFO>(logMsg.c_str());
113 
114     /* Assign line fd to descriptor for monitoring */
115     gpioEventDescriptor.assign(gpioLineFd);
116 
117     /* Schedule a wait event */
118     scheduleEventHandler();
119 
120     return 0;
121 }
122 } // namespace gpio
123 } // namespace phosphor
124