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