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