blob: 13f8d8301c6aa7eef6fbf252cc078300c522ba74 [file]
#include "tlbmc/hal/gpio/gpio_monitor.h"
#include <gpiod.h>
#include <memory>
#include <utility>
#include "absl/functional/any_invocable.h"
#include "absl/log/log.h"
#include "absl/memory/memory.h"
#include "boost/asio/io_context.hpp" // NOLINT: boost is commonly used in BMC codebase
#include "boost/asio/posix/stream_descriptor.hpp" // NOLINT: boost is commonly used in BMC codebase
#include "boost/asio/post.hpp" // NOLINT: boost is commonly used in BMC codebase
#include "g3/macros.h"
#include "gpio_config.pb.h"
#include "tlbmc/hal/gpio/gpio.h"
namespace milotic_tlbmc {
GpioMonitor::GpioMonitor(
std::unique_ptr<milotic_tlbmc::Gpio> gpio, bool is_one_time_request,
std::shared_ptr<boost::asio::io_context> io_context,
std::unique_ptr<boost::asio::posix::stream_descriptor> event_descriptor)
: gpio_(std::move(gpio)),
is_one_time_request_(is_one_time_request),
io_context_(std::move(io_context)),
event_descriptor_(std::move(event_descriptor)) {
ScheduleEventHandler();
}
GpioMonitor::~GpioMonitor() {
// Stop the io_context before closing the event descriptor. Otherwise, the
// event handler may be triggered after the event descriptor is closed.
io_context_->stop();
event_descriptor_.reset();
}
void GpioMonitor::ScheduleEventHandler() {
event_descriptor_->async_wait(
boost::asio::posix::stream_descriptor::wait_read,
[this](const boost::system::error_code& ec) {
if (ec) {
LOG(ERROR) << "GPIO event handler error: " << ec.message();
return;
}
EventHandler();
});
}
void GpioMonitor::RegisterCallback(
absl::AnyInvocable<void(GpioEventType event)>&& callback) {
boost::asio::post(*io_context_, [this, cb = std::move(callback)]() mutable {
callbacks_.push_back(std::move(cb));
});
}
void GpioMonitor::EventHandler() {
gpiod_line_event gpio_line_event;
// Need to consume the event. Otherwise, the event will be stuck in the
// queue and the descriptor will always be ready for read.
if (gpiod_line_event_read_fd(event_descriptor_->native_handle(),
&gpio_line_event) < 0) {
LOG(ERROR) << ("Failed to read from GPIO event descriptor");
return;
}
if (is_one_time_request_) {
event_descriptor_->close();
} else {
event_descriptor_->async_wait(
boost::asio::posix::stream_descriptor::wait_read,
[this](const boost::system::error_code& ec) {
if (ec) {
LOG(ERROR) << "GPIO event handler error: " << ec.message();
return;
}
EventHandler();
});
}
for (auto& callback : callbacks_) {
if (gpio_line_event.event_type == GPIO_EVENT_RISING) {
callback(GPIO_EVENT_RISING);
} else {
callback(GPIO_EVENT_FALLING);
}
}
}
absl::StatusOr<std::unique_ptr<GpioMonitor>> GpioMonitor::Create(
const GpioConfig& gpio_config,
std::shared_ptr<boost::asio::io_context> io_context) {
ECCLESIA_ASSIGN_OR_RETURN(std::unique_ptr<milotic_tlbmc::Gpio> gpio,
milotic_tlbmc::Gpio::Create(gpio_config));
auto event_descriptor =
std::make_unique<boost::asio::posix::stream_descriptor>(*io_context);
event_descriptor->assign(gpio->GetGpioLineEventFd());
return absl::WrapUnique(new GpioMonitor(
std::move(gpio), gpio_config.gpio_event_config().one_time_request(),
io_context, std::move(event_descriptor)));
}
} // namespace milotic_tlbmc