1*61c4878aSAndroid Build Coastguard Worker // Copyright 2024 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker #include "pw_digital_io_linux/notifier.h"
16*61c4878aSAndroid Build Coastguard Worker
17*61c4878aSAndroid Build Coastguard Worker #include <sys/epoll.h>
18*61c4878aSAndroid Build Coastguard Worker #include <sys/eventfd.h>
19*61c4878aSAndroid Build Coastguard Worker #include <unistd.h>
20*61c4878aSAndroid Build Coastguard Worker
21*61c4878aSAndroid Build Coastguard Worker #include <array>
22*61c4878aSAndroid Build Coastguard Worker #include <cerrno>
23*61c4878aSAndroid Build Coastguard Worker #include <cstring>
24*61c4878aSAndroid Build Coastguard Worker #include <mutex>
25*61c4878aSAndroid Build Coastguard Worker #include <utility>
26*61c4878aSAndroid Build Coastguard Worker
27*61c4878aSAndroid Build Coastguard Worker #include "log_errno.h"
28*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
29*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
30*61c4878aSAndroid Build Coastguard Worker #include "pw_status/status.h"
31*61c4878aSAndroid Build Coastguard Worker
32*61c4878aSAndroid Build Coastguard Worker namespace pw::digital_io {
33*61c4878aSAndroid Build Coastguard Worker
34*61c4878aSAndroid Build Coastguard Worker namespace {
35*61c4878aSAndroid Build Coastguard Worker
36*61c4878aSAndroid Build Coastguard Worker using pw::OkStatus;
37*61c4878aSAndroid Build Coastguard Worker
38*61c4878aSAndroid Build Coastguard Worker constexpr void* kCancelToken = nullptr;
39*61c4878aSAndroid Build Coastguard Worker
40*61c4878aSAndroid Build Coastguard Worker // The max number of that the notifier will process in a single iteration.
41*61c4878aSAndroid Build Coastguard Worker inline constexpr auto kMaxEventsPerWake = 16;
42*61c4878aSAndroid Build Coastguard Worker
43*61c4878aSAndroid Build Coastguard Worker } // namespace
44*61c4878aSAndroid Build Coastguard Worker
Create()45*61c4878aSAndroid Build Coastguard Worker pw::Result<std::shared_ptr<LinuxGpioNotifier>> LinuxGpioNotifier::Create() {
46*61c4878aSAndroid Build Coastguard Worker // Create file descriptors
47*61c4878aSAndroid Build Coastguard Worker int raw_epoll_fd = epoll_create1(0);
48*61c4878aSAndroid Build Coastguard Worker if (raw_epoll_fd < 0) {
49*61c4878aSAndroid Build Coastguard Worker LOG_ERROR_WITH_ERRNO("Failed to initialize epoll descriptor:", errno);
50*61c4878aSAndroid Build Coastguard Worker return pw::Status::Internal();
51*61c4878aSAndroid Build Coastguard Worker }
52*61c4878aSAndroid Build Coastguard Worker auto epoll_fd = OwnedFd(raw_epoll_fd);
53*61c4878aSAndroid Build Coastguard Worker
54*61c4878aSAndroid Build Coastguard Worker int raw_event_fd = eventfd(0, 0);
55*61c4878aSAndroid Build Coastguard Worker if (raw_event_fd < 0) {
56*61c4878aSAndroid Build Coastguard Worker LOG_ERROR_WITH_ERRNO("Failed to initialize event descriptor:", errno);
57*61c4878aSAndroid Build Coastguard Worker return pw::Status::Internal();
58*61c4878aSAndroid Build Coastguard Worker }
59*61c4878aSAndroid Build Coastguard Worker auto event_fd = OwnedFd(raw_event_fd);
60*61c4878aSAndroid Build Coastguard Worker
61*61c4878aSAndroid Build Coastguard Worker // Attempt to register event_fd with epoll_fd
62*61c4878aSAndroid Build Coastguard Worker epoll_event event = {
63*61c4878aSAndroid Build Coastguard Worker .events = EPOLLIN,
64*61c4878aSAndroid Build Coastguard Worker .data = {.ptr = kCancelToken},
65*61c4878aSAndroid Build Coastguard Worker };
66*61c4878aSAndroid Build Coastguard Worker if (epoll_ctl(epoll_fd.fd(), EPOLL_CTL_ADD, event_fd.fd(), &event) != 0) {
67*61c4878aSAndroid Build Coastguard Worker // There is no reason this should ever fail, except for a bug!
68*61c4878aSAndroid Build Coastguard Worker LOG_ERROR_WITH_ERRNO("Failed to add cancel event to epoll descriptor:",
69*61c4878aSAndroid Build Coastguard Worker errno);
70*61c4878aSAndroid Build Coastguard Worker return pw::Status::Internal();
71*61c4878aSAndroid Build Coastguard Worker }
72*61c4878aSAndroid Build Coastguard Worker
73*61c4878aSAndroid Build Coastguard Worker // Initialization succeeded - create the object.
74*61c4878aSAndroid Build Coastguard Worker return std::shared_ptr<LinuxGpioNotifier>(
75*61c4878aSAndroid Build Coastguard Worker new LinuxGpioNotifier(std::move(epoll_fd), std::move(event_fd)));
76*61c4878aSAndroid Build Coastguard Worker }
77*61c4878aSAndroid Build Coastguard Worker
~LinuxGpioNotifier()78*61c4878aSAndroid Build Coastguard Worker LinuxGpioNotifier::~LinuxGpioNotifier() {
79*61c4878aSAndroid Build Coastguard Worker PW_CHECK_INT_EQ( // Crash OK: prevent use-after-free via registered lines.
80*61c4878aSAndroid Build Coastguard Worker registered_line_count_,
81*61c4878aSAndroid Build Coastguard Worker 0,
82*61c4878aSAndroid Build Coastguard Worker "Destroying notifier with registered lines");
83*61c4878aSAndroid Build Coastguard Worker // fds closed automatically
84*61c4878aSAndroid Build Coastguard Worker }
85*61c4878aSAndroid Build Coastguard Worker
RegisterLine(int fd,LinuxGpioNotifier::Handler & handler)86*61c4878aSAndroid Build Coastguard Worker pw::Status LinuxGpioNotifier::RegisterLine(
87*61c4878aSAndroid Build Coastguard Worker int fd, LinuxGpioNotifier::Handler& handler) {
88*61c4878aSAndroid Build Coastguard Worker // Register for event notifications. Note that it's not clear from the
89*61c4878aSAndroid Build Coastguard Worker // documentation if EPOLLIN or EPOLLPRI is needed here, but EPOLLPRI shows up
90*61c4878aSAndroid Build Coastguard Worker // in all the examples online, and EPOLLIN is useful for testing.
91*61c4878aSAndroid Build Coastguard Worker epoll_event event = {
92*61c4878aSAndroid Build Coastguard Worker .events = EPOLLIN | EPOLLPRI,
93*61c4878aSAndroid Build Coastguard Worker .data = {.ptr = &handler},
94*61c4878aSAndroid Build Coastguard Worker };
95*61c4878aSAndroid Build Coastguard Worker if (epoll_ctl(epoll_fd_.fd(), EPOLL_CTL_ADD, fd, &event) != 0) {
96*61c4878aSAndroid Build Coastguard Worker switch (errno) {
97*61c4878aSAndroid Build Coastguard Worker case EBADF:
98*61c4878aSAndroid Build Coastguard Worker PW_LOG_WARN("The fd [%d] is invalid", fd);
99*61c4878aSAndroid Build Coastguard Worker return pw::Status::InvalidArgument();
100*61c4878aSAndroid Build Coastguard Worker case EEXIST:
101*61c4878aSAndroid Build Coastguard Worker PW_LOG_WARN(
102*61c4878aSAndroid Build Coastguard Worker "The fd [%d] is already registered with epoll descriptor [%d]",
103*61c4878aSAndroid Build Coastguard Worker fd,
104*61c4878aSAndroid Build Coastguard Worker epoll_fd_.fd());
105*61c4878aSAndroid Build Coastguard Worker return pw::Status::FailedPrecondition();
106*61c4878aSAndroid Build Coastguard Worker case ENOSPC:
107*61c4878aSAndroid Build Coastguard Worker PW_LOG_WARN("No space to add fd [%d] to epoll descriptor [%d]",
108*61c4878aSAndroid Build Coastguard Worker fd,
109*61c4878aSAndroid Build Coastguard Worker epoll_fd_.fd());
110*61c4878aSAndroid Build Coastguard Worker return pw::Status::ResourceExhausted();
111*61c4878aSAndroid Build Coastguard Worker }
112*61c4878aSAndroid Build Coastguard Worker // Other errors are likely the result of bugs and should never happen.
113*61c4878aSAndroid Build Coastguard Worker LOG_ERROR_WITH_ERRNO("Failed to add fd [%d] to epoll descriptor [%d]:",
114*61c4878aSAndroid Build Coastguard Worker errno,
115*61c4878aSAndroid Build Coastguard Worker fd,
116*61c4878aSAndroid Build Coastguard Worker epoll_fd_.fd());
117*61c4878aSAndroid Build Coastguard Worker return pw::Status::Internal();
118*61c4878aSAndroid Build Coastguard Worker }
119*61c4878aSAndroid Build Coastguard Worker ++registered_line_count_;
120*61c4878aSAndroid Build Coastguard Worker return OkStatus();
121*61c4878aSAndroid Build Coastguard Worker }
122*61c4878aSAndroid Build Coastguard Worker
UnregisterLine(int fd)123*61c4878aSAndroid Build Coastguard Worker pw::Status LinuxGpioNotifier::UnregisterLine(int fd) {
124*61c4878aSAndroid Build Coastguard Worker // Unregister from event notifications.
125*61c4878aSAndroid Build Coastguard Worker epoll_event unused{}; // See BUGS under epoll_ctl(2).
126*61c4878aSAndroid Build Coastguard Worker if (epoll_ctl(epoll_fd_.fd(), EPOLL_CTL_DEL, fd, &unused) != 0) {
127*61c4878aSAndroid Build Coastguard Worker switch (errno) {
128*61c4878aSAndroid Build Coastguard Worker case ENOENT:
129*61c4878aSAndroid Build Coastguard Worker // The file descriptor was not registered.
130*61c4878aSAndroid Build Coastguard Worker return pw::Status::NotFound();
131*61c4878aSAndroid Build Coastguard Worker case EBADF:
132*61c4878aSAndroid Build Coastguard Worker PW_LOG_WARN("The fd [%d] is invalid", fd);
133*61c4878aSAndroid Build Coastguard Worker return pw::Status::InvalidArgument();
134*61c4878aSAndroid Build Coastguard Worker }
135*61c4878aSAndroid Build Coastguard Worker // Other errors are likely the result of bugs and should never happen.
136*61c4878aSAndroid Build Coastguard Worker LOG_ERROR_WITH_ERRNO("Failed to remove fd [%d] from epoll descriptor [%d]:",
137*61c4878aSAndroid Build Coastguard Worker errno,
138*61c4878aSAndroid Build Coastguard Worker fd,
139*61c4878aSAndroid Build Coastguard Worker epoll_fd_.fd());
140*61c4878aSAndroid Build Coastguard Worker return pw::Status::Internal();
141*61c4878aSAndroid Build Coastguard Worker }
142*61c4878aSAndroid Build Coastguard Worker --registered_line_count_;
143*61c4878aSAndroid Build Coastguard Worker return OkStatus();
144*61c4878aSAndroid Build Coastguard Worker }
145*61c4878aSAndroid Build Coastguard Worker
CancelWait()146*61c4878aSAndroid Build Coastguard Worker void LinuxGpioNotifier::CancelWait() {
147*61c4878aSAndroid Build Coastguard Worker uint64_t value = 1;
148*61c4878aSAndroid Build Coastguard Worker // Note this is used in tests only, and failure to cancel will hang the test
149*61c4878aSAndroid Build Coastguard Worker // or leak a thread - depending on if the test tries to join the thread.
150*61c4878aSAndroid Build Coastguard Worker ssize_t result = cancel_event_fd_.write(&value, sizeof(value));
151*61c4878aSAndroid Build Coastguard Worker PW_DCHECK_INT_EQ(result,
152*61c4878aSAndroid Build Coastguard Worker sizeof(value),
153*61c4878aSAndroid Build Coastguard Worker "Failed to write cancel event: " ERRNO_FORMAT_STRING,
154*61c4878aSAndroid Build Coastguard Worker ERRNO_FORMAT_ARGS(errno));
155*61c4878aSAndroid Build Coastguard Worker }
156*61c4878aSAndroid Build Coastguard Worker
WaitForEvents(int timeout_ms)157*61c4878aSAndroid Build Coastguard Worker pw::Result<unsigned int> LinuxGpioNotifier::WaitForEvents(int timeout_ms) {
158*61c4878aSAndroid Build Coastguard Worker // Block until there is at least 1 file descriptor with an event.
159*61c4878aSAndroid Build Coastguard Worker std::array<epoll_event, kMaxEventsPerWake> events{};
160*61c4878aSAndroid Build Coastguard Worker int event_count;
161*61c4878aSAndroid Build Coastguard Worker for (;;) {
162*61c4878aSAndroid Build Coastguard Worker errno = 0;
163*61c4878aSAndroid Build Coastguard Worker event_count =
164*61c4878aSAndroid Build Coastguard Worker epoll_wait(epoll_fd_.fd(), events.data(), events.size(), timeout_ms);
165*61c4878aSAndroid Build Coastguard Worker if (event_count > 0) {
166*61c4878aSAndroid Build Coastguard Worker break;
167*61c4878aSAndroid Build Coastguard Worker }
168*61c4878aSAndroid Build Coastguard Worker if (event_count == 0) {
169*61c4878aSAndroid Build Coastguard Worker return pw::Status::DeadlineExceeded();
170*61c4878aSAndroid Build Coastguard Worker }
171*61c4878aSAndroid Build Coastguard Worker if (errno == EINTR) {
172*61c4878aSAndroid Build Coastguard Worker // Call was interrupted by a signal to the thread. Restart it.
173*61c4878aSAndroid Build Coastguard Worker // NOTE: We don't attempt to update timeout_ms.
174*61c4878aSAndroid Build Coastguard Worker continue;
175*61c4878aSAndroid Build Coastguard Worker }
176*61c4878aSAndroid Build Coastguard Worker LOG_CRITICAL_WITH_ERRNO("Failed to wait on epoll descriptor:", errno);
177*61c4878aSAndroid Build Coastguard Worker return pw::Status::Internal();
178*61c4878aSAndroid Build Coastguard Worker }
179*61c4878aSAndroid Build Coastguard Worker
180*61c4878aSAndroid Build Coastguard Worker // Process any lines that have events. Note that if event_count =
181*61c4878aSAndroid Build Coastguard Worker // kMaxEvents and there are more events waiting, we will get them on the
182*61c4878aSAndroid Build Coastguard Worker // next loop.
183*61c4878aSAndroid Build Coastguard Worker for (int i = 0; i < event_count; i++) {
184*61c4878aSAndroid Build Coastguard Worker if (events[i].data.ptr == kCancelToken) {
185*61c4878aSAndroid Build Coastguard Worker return pw::Status::Cancelled();
186*61c4878aSAndroid Build Coastguard Worker }
187*61c4878aSAndroid Build Coastguard Worker static_cast<Handler*>(events[i].data.ptr)->HandleEvents();
188*61c4878aSAndroid Build Coastguard Worker }
189*61c4878aSAndroid Build Coastguard Worker
190*61c4878aSAndroid Build Coastguard Worker // Must be positive due to (event_count > 0) check above.
191*61c4878aSAndroid Build Coastguard Worker return event_count;
192*61c4878aSAndroid Build Coastguard Worker }
193*61c4878aSAndroid Build Coastguard Worker
Run()194*61c4878aSAndroid Build Coastguard Worker void LinuxGpioNotifier::Run() {
195*61c4878aSAndroid Build Coastguard Worker for (;;) {
196*61c4878aSAndroid Build Coastguard Worker auto status = WaitForEvents(-1);
197*61c4878aSAndroid Build Coastguard Worker if (!status.ok()) {
198*61c4878aSAndroid Build Coastguard Worker break;
199*61c4878aSAndroid Build Coastguard Worker }
200*61c4878aSAndroid Build Coastguard Worker }
201*61c4878aSAndroid Build Coastguard Worker }
202*61c4878aSAndroid Build Coastguard Worker
203*61c4878aSAndroid Build Coastguard Worker } // namespace pw::digital_io
204