xref: /aosp_15_r20/external/pigweed/pw_digital_io_linux/public/pw_digital_io_linux/notifier.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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