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