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
23 #include <chrono>
24 #include <iostream>
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 std::cerr << "Error requesting gpio: " << gpioName << "\n";
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 std::cerr << "Error reading gpio " << gpioName << ": " << e.what()
78 << "\n";
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 std::cerr << "Failed to get " << gpioName << " fd\n";
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 std::cerr << "Failed to get lock for eventPresenceGpio: "
102 << ec.message() << "\n";
103 return;
104 }
105 if (ec)
106 {
107 if (ec != boost::system::errc::bad_file_descriptor)
108 {
109 std::cerr
110 << "Error on event presence device " << self->deviceName
111 << ": " << ec.message() << "\n";
112 }
113 return;
114 }
115 self->read();
116 self->monitorPresence();
117 });
118 }
119
read()120 void EventPresenceGpio::read()
121 {
122 // Read is invoked when an edge event is detected by monitorPresence
123 gpioLine.event_read();
124 updateAndTracePresence(gpioLine.get_value());
125 }
126
PollingPresenceGpio(const std::string & deviceType,const std::string & deviceName,const std::string & gpioName,bool inverted,boost::asio::io_context & io)127 PollingPresenceGpio::PollingPresenceGpio(
128 const std::string& deviceType, const std::string& deviceName,
129 const std::string& gpioName, bool inverted, boost::asio::io_context& io) :
130 PresenceGpio(deviceType, deviceName, gpioName), pollTimer(io)
131 {
132 try
133 {
134 gpioLine.request(
135 {deviceType + "Sensor", gpiod::line_request::DIRECTION_INPUT,
136 inverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
137 updateAndTracePresence(gpioLine.get_value());
138 }
139 catch (const std::system_error& e)
140 {
141 std::cerr << "PollingPresenceGpio: Error reading gpio " << gpioName
142 << ": " << e.what() << "\n";
143 status = false;
144 throw std::runtime_error("Failed to get Polling GPIO fd " + gpioName);
145 }
146 }
147
pollTimerHandler(const std::weak_ptr<PollingPresenceGpio> & weakRef,const boost::system::error_code & ec)148 inline void PollingPresenceGpio::pollTimerHandler(
149 const std::weak_ptr<PollingPresenceGpio>& weakRef,
150 const boost::system::error_code& ec)
151 {
152 std::shared_ptr<PollingPresenceGpio> self = weakRef.lock();
153 if (!self)
154 {
155 std::cerr << "Failed to get lock for pollingPresenceGpio: "
156 << ec.message() << "\n";
157 return;
158 }
159 if (ec)
160 {
161 if (ec != boost::system::errc::bad_file_descriptor)
162 {
163 std::cerr << "GPIO polling timer failed for " << self->gpioName
164 << ": " << ec.what() << ")\n";
165 }
166 return;
167 }
168 self->monitorPresence();
169 }
170
monitorPresence()171 void PollingPresenceGpio::monitorPresence()
172 {
173 // Determine if the value has changed
174 int newStatus = gpioLine.get_value();
175 if (static_cast<int>(status) != newStatus)
176 {
177 updateAndTracePresence(newStatus);
178 }
179
180 std::weak_ptr<PollingPresenceGpio> weakRef = weak_from_this();
181 pollTimer.expires_after(std::chrono::seconds(pollIntervalSec));
182 pollTimer.async_wait([weakRef](const boost::system::error_code& ec) {
183 pollTimerHandler(weakRef, ec);
184 });
185 }
186