xref: /openbmc/dbus-sensors/src/fan/PresenceGpio.cpp (revision e34e123bb6d6976cd5a27ad91202096a3542b9ac)
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