| /* |
| // Copyright (c) 2021 Intel Corporation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| */ |
| #pragma once |
| #include <boost/asio/posix/stream_descriptor.hpp> |
| #include <boost/asio/steady_timer.hpp> |
| #include <error_monitors/base_monitor.hpp> |
| #include <gpiod.hpp> |
| #include <host_error_monitor.hpp> |
| #include <sdbusplus/asio/object_server.hpp> |
| |
| #include <iostream> |
| |
| namespace host_error_monitor::base_gpio_poll_monitor |
| { |
| static constexpr bool debug = false; |
| |
| enum class AssertValue |
| { |
| lowAssert = 0, |
| highAssert = 1, |
| }; |
| |
| class BaseGPIOPollMonitor : public host_error_monitor::base_monitor::BaseMonitor |
| { |
| AssertValue assertValue; |
| size_t pollingTimeMs; |
| size_t timeoutMs; |
| |
| boost::asio::steady_timer pollingTimer; |
| std::chrono::steady_clock::time_point timeoutTime; |
| |
| gpiod::line line; |
| boost::asio::posix::stream_descriptor event; |
| |
| virtual void logEvent() |
| {} |
| |
| bool requestEvents() |
| { |
| line = gpiod::find_line(signalName); |
| if (!line) |
| { |
| std::cerr << "Failed to find the " << signalName << " line\n"; |
| return false; |
| } |
| |
| try |
| { |
| line.request( |
| {"host-error-monitor", gpiod::line_request::EVENT_BOTH_EDGES}); |
| } |
| catch (std::exception&) |
| { |
| std::cerr << "Failed to request events for " << signalName << "\n"; |
| return false; |
| } |
| |
| int lineFd = line.event_get_fd(); |
| if (lineFd < 0) |
| { |
| std::cerr << "Failed to get " << signalName << " fd\n"; |
| return false; |
| } |
| |
| event.assign(lineFd); |
| |
| return true; |
| } |
| |
| bool asserted() |
| { |
| if constexpr (debug) |
| { |
| std::cerr << "Checking " << signalName << " state\n"; |
| } |
| |
| if (hostIsOff()) |
| { |
| if constexpr (debug) |
| { |
| std::cerr << "Host is off\n"; |
| } |
| return false; |
| } |
| |
| return (line.get_value() == static_cast<int>(assertValue)); |
| } |
| |
| public: |
| virtual void assertHandler() |
| { |
| std::cerr << signalName << " asserted for " << std::to_string(timeoutMs) |
| << " ms\n"; |
| logEvent(); |
| } |
| |
| virtual void deassertHandler() |
| {} |
| |
| private: |
| void flushEvents() |
| { |
| if constexpr (debug) |
| { |
| std::cerr << "Flushing " << signalName << " events\n"; |
| } |
| |
| while (true) |
| { |
| try |
| { |
| line.event_read(); |
| } |
| catch (std::system_error) |
| { |
| break; |
| } |
| } |
| } |
| |
| void waitForEvent() |
| { |
| if constexpr (debug) |
| { |
| std::cerr << "Wait for " << signalName << "\n"; |
| } |
| |
| event.async_wait( |
| boost::asio::posix::stream_descriptor::wait_read, |
| [this](const boost::system::error_code ec) { |
| if (ec) |
| { |
| // operation_aborted is expected if wait is canceled. |
| if (ec != boost::asio::error::operation_aborted) |
| { |
| std::cerr << signalName |
| << " wait error: " << ec.message() << "\n"; |
| } |
| return; |
| } |
| |
| if constexpr (debug) |
| { |
| std::cerr << signalName << " event ready\n"; |
| } |
| |
| startPolling(); |
| }); |
| } |
| |
| public: |
| virtual void startPolling() |
| { |
| timeoutTime = std::chrono::steady_clock::now() + |
| std::chrono::duration<int, std::milli>(timeoutMs); |
| poll(); |
| } |
| |
| private: |
| void poll() |
| { |
| if constexpr (debug) |
| { |
| std::cerr << "Polling " << signalName << "\n"; |
| } |
| |
| flushEvents(); |
| |
| if (!asserted()) |
| { |
| if constexpr (debug) |
| { |
| std::cerr << signalName << " not asserted\n"; |
| } |
| |
| deassertHandler(); |
| waitForEvent(); |
| return; |
| } |
| if constexpr (debug) |
| { |
| std::cerr << signalName << " asserted\n"; |
| } |
| |
| if (std::chrono::steady_clock::now() > timeoutTime) |
| { |
| assertHandler(); |
| waitForEvent(); |
| return; |
| } |
| |
| pollingTimer.expires_after(std::chrono::milliseconds(pollingTimeMs)); |
| pollingTimer.async_wait([this](const boost::system::error_code ec) { |
| if (ec) |
| { |
| // operation_aborted is expected if timer is canceled before |
| // completion. |
| if (ec != boost::asio::error::operation_aborted) |
| { |
| std::cerr << signalName |
| << " polling async_wait failed: " << ec.message() |
| << "\n"; |
| } |
| return; |
| } |
| poll(); |
| }); |
| } |
| |
| public: |
| BaseGPIOPollMonitor(boost::asio::io_context& io, |
| std::shared_ptr<sdbusplus::asio::connection> conn, |
| const std::string& signalName, AssertValue assertValue, |
| size_t pollingTimeMs, size_t timeoutMs) : |
| BaseMonitor(io, conn, signalName), |
| pollingTimer(io), event(io), assertValue(assertValue), |
| pollingTimeMs(pollingTimeMs), timeoutMs(timeoutMs) |
| { |
| if (!requestEvents()) |
| { |
| return; |
| } |
| event.non_blocking(true); |
| valid = true; |
| } |
| |
| void hostOn() override |
| { |
| event.cancel(); |
| startPolling(); |
| } |
| |
| size_t getTimeoutMs() |
| { |
| return timeoutMs; |
| } |
| |
| void setTimeoutMs(size_t newTimeoutMs) |
| { |
| timeoutMs = newTimeoutMs; |
| } |
| }; |
| } // namespace host_error_monitor::base_gpio_poll_monitor |