1 // Copyright 2024 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 #pragma once 16 17 #include <atomic> 18 #include <memory> 19 20 #include "pw_digital_io_linux/internal/owned_fd.h" 21 #include "pw_result/result.h" 22 #include "pw_status/status.h" 23 #include "pw_thread/thread_core.h" 24 25 namespace pw::digital_io { 26 27 // LinuxGpioNotifier waits for interrupts from a set of GPIO lines. The notifier 28 // is able to listen for interrupts across multiple GPIO chips, and multiple 29 // notifiers are able to listen for interrupts on different lines from one chip. 30 // 31 // Most applications will have one notifier running on a high thread priority. 32 // It is expected that the interrupt handlers are light-weight and will not 33 // block the notification thread. However, multiple notifiers can be created, 34 // potentially with different thread priorities. 35 // 36 // All methods of this class are thread-safe, and they can be called directly 37 // from within the interrupt handler itself. 38 // 39 class LinuxGpioNotifier : public pw::thread::ThreadCore { 40 using OwnedFd = internal::OwnedFd; 41 42 public: 43 // Handler for GPIO line events. 44 class Handler { 45 public: 46 virtual ~Handler() = default; 47 48 // Handle events that occurred on this line. Any unhandled events will cause 49 // the handler to be invoked again. 50 virtual void HandleEvents() = 0; 51 }; 52 53 // Create a new notifier or return status Internal on unexpected error. 54 static pw::Result<std::shared_ptr<LinuxGpioNotifier>> Create(); 55 56 ~LinuxGpioNotifier() override; 57 58 LinuxGpioNotifier(const LinuxGpioNotifier&) = delete; 59 LinuxGpioNotifier& operator=(const LinuxGpioNotifier&) = delete; 60 61 // Register a file descriptor to listen for notifications on. Invoke the 62 // given handler when there are any events on the line. 63 // 64 // The handler must remain valid until UnregisterLine is called. 65 // 66 // Returns: 67 // 68 // - OK: the descriptor was added to the epoll instance 69 // - InvalidArgument: the fd is not a valid file descriptor 70 // - FailedPrecondition: the descriptor is already registered 71 // - ResourceExhausted: epoll instance reached the limit imposed by 72 // /proc/sys/fs/epoll/max_user_watches 73 // - Internal: an unexpected error occurred 74 // 75 pw::Status RegisterLine(int fd, Handler& handler); 76 77 // Unregister a file descriptor. No-op if the descriptor is not registered. 78 // 79 // Returns: 80 // 81 // OK: the descriptor was unregistered, or it was not registered. 82 // InvalidArgument: the fd is not a valid file descriptor 83 // Internal: an unexpected error occurred 84 // 85 pw::Status UnregisterLine(int fd); 86 87 // Cancels any pending wait for events. 88 // 89 // This causes a blocking WaitForEvents() call to return Cancelled. 90 // It also causes Run() to return, and if this notifier is used with 91 // a Thread, that thread will exit. 92 // 93 // This method is only intended to be used in tests. 94 // 95 void CancelWait(); 96 97 // Synchronously wait for events across all registered lines. 98 // 99 // Args: 100 // 101 // timeout_ms: Timeout, in milliseconds, to wait. 102 // 0 means don't wait at all (nonblocking). 103 // -1 means wait forever. 104 // 105 // Returns: 106 // 107 // Ok: At least one event was handled. The result contains the number of 108 // events handled. 109 // Cancelled: Wait was cancelled due to CancelWait() call. 110 // DeadlineExceeded: The timeout expired before any events were consumed. 111 // Internal: An unexpected error occurred. 112 pw::Result<unsigned int> WaitForEvents(int timeout_ms = -1); 113 114 // The main entry point for the notification thread. 115 // 116 // This will run forever until CancelWait() is called. 117 // 118 // This is public so callers can choose to synchronously handle notifications 119 // themselves without using a thread. 120 void Run() override; 121 122 private: 123 // Construct a notifier using the provided epoll fd and event fd. 124 // The event fd must be pre-registered with the epoll fd. LinuxGpioNotifier(OwnedFd && epoll_fd,OwnedFd && event_fd)125 LinuxGpioNotifier(OwnedFd&& epoll_fd, OwnedFd&& event_fd) 126 : epoll_fd_(std::move(epoll_fd)), cancel_event_fd_(std::move(event_fd)) {} 127 128 // The epoll file descriptor that lists the line descriptors to wait for. 129 // This descriptor is initialized by Init(). After initialization, the 130 // descriptor can be manipulated with epoll_* functions without any additional 131 // synchronization. 132 OwnedFd epoll_fd_; 133 134 // An eventfd that is used to signal cancellation to the thread waiting on 135 // notification. 136 OwnedFd cancel_event_fd_; 137 138 // An atomic counter of registered handles, which is used purely for 139 // diagnostic purposes to detect incorrect usage of the API. This is necessary 140 // because we can't inspect the epoll descriptor set. Note that atomics are 141 // safe to use on cores that are capable of running Linux. 142 std::atomic_int registered_line_count_ = 0; 143 }; 144 145 } // namespace pw::digital_io 146