blob: eee430f7d6bd7367d46ebab85b6492a811bc77ac [file] [log] [blame]
Jason M. Bills778ac172020-12-04 16:37:11 -08001/*
2// Copyright (c) 2021 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#pragma once
17#include <boost/asio/posix/stream_descriptor.hpp>
18#include <boost/asio/steady_timer.hpp>
19#include <error_monitors/base_monitor.hpp>
20#include <gpiod.hpp>
21#include <host_error_monitor.hpp>
22#include <sdbusplus/asio/object_server.hpp>
23
24#include <iostream>
25
26namespace host_error_monitor::base_gpio_poll_monitor
27{
28static constexpr bool debug = false;
29
30enum class AssertValue
31{
32 lowAssert = 0,
33 highAssert = 1,
34};
35
36class BaseGPIOPollMonitor : public host_error_monitor::base_monitor::BaseMonitor
37{
38 AssertValue assertValue;
39 size_t pollingTimeMs;
40 size_t timeoutMs;
41
42 boost::asio::steady_timer pollingTimer;
43 std::chrono::steady_clock::time_point timeoutTime;
44
45 gpiod::line line;
46 boost::asio::posix::stream_descriptor event;
47
48 virtual void logEvent()
49 {}
50
51 bool requestEvents()
52 {
53 line = gpiod::find_line(signalName);
54 if (!line)
55 {
56 std::cerr << "Failed to find the " << signalName << " line\n";
57 return false;
58 }
59
60 try
61 {
62 line.request(
63 {"host-error-monitor", gpiod::line_request::EVENT_BOTH_EDGES});
64 }
65 catch (std::exception&)
66 {
67 std::cerr << "Failed to request events for " << signalName << "\n";
68 return false;
69 }
70
71 int lineFd = line.event_get_fd();
72 if (lineFd < 0)
73 {
74 std::cerr << "Failed to get " << signalName << " fd\n";
75 return false;
76 }
77
78 event.assign(lineFd);
79
80 return true;
81 }
82
83 bool asserted()
84 {
85 if constexpr (debug)
86 {
87 std::cerr << "Checking " << signalName << " state\n";
88 }
89
90 if (hostIsOff())
91 {
92 if constexpr (debug)
93 {
94 std::cerr << "Host is off\n";
95 }
96 return false;
97 }
98
99 return (line.get_value() == static_cast<int>(assertValue));
100 }
101
102 public:
103 virtual void assertHandler()
104 {
105 std::cerr << signalName << " asserted for " << std::to_string(timeoutMs)
106 << " ms\n";
107 logEvent();
108 }
109
110 virtual void deassertHandler()
111 {}
112
113 private:
114 void flushEvents()
115 {
116 if constexpr (debug)
117 {
118 std::cerr << "Flushing " << signalName << " events\n";
119 }
120
121 while (true)
122 {
123 try
124 {
125 line.event_read();
126 }
127 catch (std::system_error)
128 {
129 break;
130 }
131 }
132 }
133
134 void waitForEvent()
135 {
136 if constexpr (debug)
137 {
138 std::cerr << "Wait for " << signalName << "\n";
139 }
140
141 event.async_wait(
142 boost::asio::posix::stream_descriptor::wait_read,
143 [this](const boost::system::error_code ec) {
144 if (ec)
145 {
146 // operation_aborted is expected if wait is canceled.
147 if (ec != boost::asio::error::operation_aborted)
148 {
149 std::cerr << signalName
150 << " wait error: " << ec.message() << "\n";
151 }
152 return;
153 }
154
155 if constexpr (debug)
156 {
157 std::cerr << signalName << " event ready\n";
158 }
159
160 startPolling();
161 });
162 }
163
164 public:
165 virtual void startPolling()
166 {
167 timeoutTime = std::chrono::steady_clock::now() +
168 std::chrono::duration<int, std::milli>(timeoutMs);
169 poll();
170 }
171
172 private:
173 void poll()
174 {
175 if constexpr (debug)
176 {
177 std::cerr << "Polling " << signalName << "\n";
178 }
179
180 flushEvents();
181
182 if (!asserted())
183 {
184 if constexpr (debug)
185 {
186 std::cerr << signalName << " not asserted\n";
187 }
188
189 deassertHandler();
190 waitForEvent();
191 return;
192 }
193 if constexpr (debug)
194 {
195 std::cerr << signalName << " asserted\n";
196 }
197
198 if (std::chrono::steady_clock::now() > timeoutTime)
199 {
200 assertHandler();
201 waitForEvent();
202 return;
203 }
204
205 pollingTimer.expires_after(std::chrono::milliseconds(pollingTimeMs));
206 pollingTimer.async_wait([this](const boost::system::error_code ec) {
207 if (ec)
208 {
209 // operation_aborted is expected if timer is canceled before
210 // completion.
211 if (ec != boost::asio::error::operation_aborted)
212 {
213 std::cerr << signalName
214 << " polling async_wait failed: " << ec.message()
215 << "\n";
216 }
217 return;
218 }
219 poll();
220 });
221 }
222
223 public:
224 BaseGPIOPollMonitor(boost::asio::io_service& io,
225 std::shared_ptr<sdbusplus::asio::connection> conn,
226 const std::string& signalName, AssertValue assertValue,
227 size_t pollingTimeMs, size_t timeoutMs) :
228 BaseMonitor(io, conn, signalName),
229 pollingTimer(io), event(io), assertValue(assertValue),
230 pollingTimeMs(pollingTimeMs), timeoutMs(timeoutMs)
231 {
232 if (!requestEvents())
233 {
234 return;
235 }
236 event.non_blocking(true);
237 valid = true;
238 }
239
240 void hostOn() override
241 {
242 event.cancel();
243 startPolling();
244 }
245
246 size_t getTimeoutMs()
247 {
248 return timeoutMs;
249 }
250
251 void setTimeoutMs(size_t newTimeoutMs)
252 {
253 timeoutMs = newTimeoutMs;
254 }
255};
256} // namespace host_error_monitor::base_gpio_poll_monitor