1 /*
2 // Copyright (c) 2018 Intel Corporation
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 "PresenceGpio.hpp"
18
19 #include <boost/asio/io_context.hpp>
20 #include <boost/asio/posix/stream_descriptor.hpp>
21 #include <gpiod.hpp>
22 #include <phosphor-logging/lg2.hpp>
23
24 #include <chrono>
25 #include <memory>
26 #include <stdexcept>
27 #include <string>
28 #include <system_error>
29
30 static constexpr unsigned int pollIntervalSec = 1;
31
PresenceGpio(const std::string & deviceType,const std::string & deviceName,const std::string & gpioName)32 PresenceGpio::PresenceGpio(const std::string& deviceType,
33 const std::string& deviceName,
34 const std::string& gpioName) :
35 deviceType(deviceType), deviceName(deviceName), gpioName(gpioName)
36 {
37 gpioLine = gpiod::find_line(gpioName);
38 if (!gpioLine)
39 {
40 lg2::error("Error requesting gpio: '{NAME}'", "NAME", gpioName);
41 throw std::runtime_error("Failed to find GPIO " + gpioName);
42 }
43 }
44
~PresenceGpio()45 PresenceGpio::~PresenceGpio()
46 {
47 gpioLine.release();
48 }
49
updateAndTracePresence(int newValue)50 void PresenceGpio::updateAndTracePresence(int newValue)
51 {
52 status = (newValue != 0);
53 if (status)
54 {
55 logPresent(deviceName);
56 }
57 else
58 {
59 logRemoved(deviceName);
60 }
61 }
62
EventPresenceGpio(const std::string & deviceType,const std::string & deviceName,const std::string & gpioName,bool inverted,boost::asio::io_context & io)63 EventPresenceGpio::EventPresenceGpio(
64 const std::string& deviceType, const std::string& deviceName,
65 const std::string& gpioName, bool inverted, boost::asio::io_context& io) :
66 PresenceGpio(deviceType, deviceName, gpioName), gpioFd(io)
67 {
68 try
69 {
70 gpioLine.request(
71 {deviceType + "Sensor", gpiod::line_request::EVENT_BOTH_EDGES,
72 inverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
73 updateAndTracePresence(gpioLine.get_value());
74 }
75 catch (const std::system_error& e)
76 {
77 lg2::error("Error reading gpio '{NAME}': '{ERR}'", "NAME", gpioName,
78 "ERR", e);
79 throw std::runtime_error("Failed to read GPIO fd " + gpioName);
80 }
81
82 int gpioLineFd = gpioLine.event_get_fd();
83 if (gpioLineFd < 0)
84 {
85 lg2::error("Failed to get '{NAME}' fd", "NAME", gpioName);
86 throw std::runtime_error("Failed to get GPIO fd " + gpioName);
87 }
88
89 gpioFd.assign(gpioLineFd);
90 }
91
monitorPresence()92 void EventPresenceGpio::monitorPresence()
93 {
94 std::weak_ptr<EventPresenceGpio> weakRef = weak_from_this();
95 gpioFd.async_wait(
96 boost::asio::posix::stream_descriptor::wait_read,
97 [weakRef](const boost::system::error_code& ec) {
98 std::shared_ptr<EventPresenceGpio> self = weakRef.lock();
99 if (!self)
100 {
101 lg2::error(
102 "Failed to get lock for eventPresenceGpio: '{ERROR_MESSAGE}'",
103 "ERROR_MESSAGE", ec.message());
104 return;
105 }
106 if (ec)
107 {
108 if (ec != boost::system::errc::bad_file_descriptor)
109 {
110 lg2::error(
111 "Error on event presence device '{NAME}': '{ERROR_MESSAGE}'",
112 "NAME", self->deviceName, "ERROR_MESSAGE",
113 ec.message());
114 }
115 return;
116 }
117 self->read();
118 self->monitorPresence();
119 });
120 }
121
read()122 void EventPresenceGpio::read()
123 {
124 // Read is invoked when an edge event is detected by monitorPresence
125 gpioLine.event_read();
126 updateAndTracePresence(gpioLine.get_value());
127 }
128
PollingPresenceGpio(const std::string & deviceType,const std::string & deviceName,const std::string & gpioName,bool inverted,boost::asio::io_context & io)129 PollingPresenceGpio::PollingPresenceGpio(
130 const std::string& deviceType, const std::string& deviceName,
131 const std::string& gpioName, bool inverted, boost::asio::io_context& io) :
132 PresenceGpio(deviceType, deviceName, gpioName), pollTimer(io)
133 {
134 try
135 {
136 gpioLine.request(
137 {deviceType + "Sensor", gpiod::line_request::DIRECTION_INPUT,
138 inverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
139 updateAndTracePresence(gpioLine.get_value());
140 }
141 catch (const std::system_error& e)
142 {
143 lg2::error("PollingPresenceGpio: Error reading gpio '{NAME}': '{ERR}'",
144 "NAME", gpioName, "ERR", e);
145 status = false;
146 throw std::runtime_error("Failed to get Polling GPIO fd " + gpioName);
147 }
148 }
149
pollTimerHandler(const std::weak_ptr<PollingPresenceGpio> & weakRef,const boost::system::error_code & ec)150 inline void PollingPresenceGpio::pollTimerHandler(
151 const std::weak_ptr<PollingPresenceGpio>& weakRef,
152 const boost::system::error_code& ec)
153 {
154 std::shared_ptr<PollingPresenceGpio> self = weakRef.lock();
155 if (!self)
156 {
157 lg2::error(
158 "Failed to get lock for pollingPresenceGpio: '{ERROR_MESSAGE}'",
159 "ERROR_MESSAGE", ec.message());
160 return;
161 }
162 if (ec)
163 {
164 if (ec != boost::system::errc::bad_file_descriptor)
165 {
166 lg2::error(
167 "GPIO polling timer failed for '{NAME}': '{ERROR_MESSAGE}'",
168 "NAME", self->gpioName, "ERROR_MESSAGE", ec.message());
169 }
170 return;
171 }
172 self->monitorPresence();
173 }
174
monitorPresence()175 void PollingPresenceGpio::monitorPresence()
176 {
177 // Determine if the value has changed
178 int newStatus = gpioLine.get_value();
179 if (static_cast<int>(status) != newStatus)
180 {
181 updateAndTracePresence(newStatus);
182 }
183
184 std::weak_ptr<PollingPresenceGpio> weakRef = weak_from_this();
185 pollTimer.expires_after(std::chrono::seconds(pollIntervalSec));
186 pollTimer.async_wait([weakRef](const boost::system::error_code& ec) {
187 pollTimerHandler(weakRef, ec);
188 });
189 }
190