xref: /aosp_15_r20/external/pigweed/pw_digital_io/public/pw_digital_io/digital_io.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2021 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 #pragma once
15 
16 #include "pw_assert/check.h"
17 #include "pw_digital_io/internal/conversions.h"
18 #include "pw_function/function.h"
19 #include "pw_result/result.h"
20 #include "pw_status/status.h"
21 #include "pw_status/try.h"
22 
23 namespace pw::digital_io {
24 
25 // The logical state of a digital line.
26 enum class State : bool {
27   kActive = true,
28   kInactive = false,
29 };
30 
31 // The triggering configuration for an interrupt handler.
32 enum class InterruptTrigger : int {
33   // Trigger on transition from kInactive to kActive.
34   kActivatingEdge,
35   // Trigger on transition from kActive to kInactive.
36   kDeactivatingEdge,
37   // Trigger on any state transition between kActive and kInactive.
38   kBothEdges,
39 };
40 
41 // Interrupt handling function. The argument contains the latest known state of
42 // the line. It is backend-specific if, when, and how this state is updated.
43 using InterruptHandler = ::pw::Function<void(State sampled_state)>;
44 
45 /// A digital I/O line that may support input, output, and interrupts, but makes
46 /// no guarantees about whether any operations are supported. You must check the
47 /// various `provides_*` flags before calling optional methods. Unsupported
48 /// methods invoke `PW_CRASH`.
49 ///
50 /// All methods are potentially blocking. Unless otherwise specified, access
51 /// from multiple threads to a single line must be externally synchronized - for
52 /// example using `pw::Borrowable`. Unless otherwise specified, none of the
53 /// methods are safe to call from an interrupt handler. Therefore, this
54 /// abstraction may not be suitable for bitbanging and other low-level uses of
55 /// GPIO.
56 ///
57 /// Note that the initial state of a line is not guaranteed to be consistent
58 /// with either the "enabled" or "disabled" state. Users of the API who need to
59 /// ensure the line is disabled (ex. output not driving the line) should call
60 /// `Disable()`.
61 ///
62 /// This class should almost never be used in APIs directly. Instead, use one of
63 /// the derived classes that explicitly supports the functionality that your
64 /// API needs.
65 ///
66 /// This class cannot be extended directly. Instead, extend one of the
67 /// derived classes that explicitly support the functionality that you want to
68 /// implement.
69 class DigitalIoOptional {
70  public:
71   virtual ~DigitalIoOptional() = default;
72 
73   /// @returns `true` if input (getting state) is supported.
provides_input()74   constexpr bool provides_input() const { return config_.input; }
75   /// @returns `true` if output (setting state) is supported.
provides_output()76   constexpr bool provides_output() const { return config_.output; }
77   /// @returns `true` if interrupt handlers can be registered.
provides_interrupt()78   constexpr bool provides_interrupt() const { return config_.interrupt; }
79 
80   /// Gets the state of the line.
81   ///
82   /// @warning This method is not thread-safe and cannot be used in interrupt
83   /// handlers.
84   ///
85   /// @returns @rst
86   ///
87   /// .. pw-status-codes::
88   ///
89   ///    OK: Returns an active or inactive state.
90   ///
91   ///    FAILED_PRECONDITION: The line has not been enabled.
92   ///
93   /// Returns Other status codes as defined by the backend.
94   ///
95   /// @endrst
GetState()96   Result<State> GetState() { return DoGetState(); }
97 
98   /// Sets the state of the line.
99   ///
100   /// Callers are responsible to wait for the voltage level to settle after this
101   /// call returns.
102   ///
103   /// @warning This method is not thread-safe and cannot be used in interrupt
104   /// handlers.
105   ///
106   /// @returns @rst
107   ///
108   /// .. pw-status-codes::
109   ///
110   ///    OK: The state has been set.
111   ///
112   ///    FAILED_PRECONDITION: The line has not been enabled.
113   ///
114   /// Returns other status codes as defined by the backend.
115   ///
116   /// @endrst
SetState(State state)117   Status SetState(State state) { return DoSetState(state); }
118 
119   /// Checks if the line is in the active state.
120   ///
121   /// The line is in the active state when `GetState()` returns
122   /// `State::kActive`.
123   ///
124   /// @warning This method is not thread-safe and cannot be used in interrupt
125   /// handlers.
126   ///
127   /// @returns @rst
128   ///
129   /// .. pw-status-codes::
130   ///
131   ///    OK: ``true`` if the line is in the active state, otherwise ``false``.
132   ///
133   ///    FAILED_PRECONDITION: The line has not been enabled.
134   ///
135   /// Returns other status codes as defined by the backend.
136   ///
137   /// @endrst
IsStateActive()138   Result<bool> IsStateActive() {
139     PW_TRY_ASSIGN(const State state, GetState());
140     return state == State::kActive;
141   }
142 
143   /// Sets the line to the active state. Equivalent to
144   /// `SetState(State::kActive)`.
145   ///
146   /// Callers are responsible to wait for the voltage level to settle after this
147   /// call returns.
148   ///
149   /// @warning This method is not thread-safe and cannot be used in interrupt
150   /// handlers.
151   ///
152   /// @returns @rst
153   ///
154   /// .. pw-status-codes::
155   ///
156   ///    OK: The state has been set.
157   ///
158   ///    FAILED_PRECONDITION: The line has not been enabled.
159   ///
160   /// Returns other status codes as defined by the backend.
161   ///
162   /// @endrst
SetStateActive()163   Status SetStateActive() { return SetState(State::kActive); }
164 
165   /// Sets the line to the inactive state. Equivalent to
166   /// `SetState(State::kInactive)`.
167   ///
168   /// Callers are responsible to wait for the voltage level to settle after
169   /// this call returns.
170   ///
171   /// @warning This method is not thread-safe and cannot be used in interrupt
172   /// handlers.
173   ///
174   /// @returns @rst
175   ///
176   /// .. pw-status-codes::
177   ///
178   ///    OK: The state has been set.
179   ///
180   ///    FAILED_PRECONDITION: The line has not been enabled.
181   ///
182   /// Returns other status codes as defined by the backend.
183   ///
184   /// @endrst
SetStateInactive()185   Status SetStateInactive() { return SetState(State::kInactive); }
186 
187   /// Sets an interrupt handler to execute when an interrupt is triggered, and
188   /// configures the condition for triggering the interrupt.
189   ///
190   /// The handler is executed in a backend-specific context—this may be a
191   /// system interrupt handler or a shared notification thread. Do not do any
192   /// blocking or expensive work in the handler. The only universally safe
193   /// operations are the IRQ-safe functions on `pw_sync` primitives.
194   ///
195   /// In particular, it is NOT safe to get the state of a `DigitalIo`
196   /// line—either from this line or any other `DigitalIoOptional`
197   /// instance—inside the handler.
198   ///
199   /// @warning This method is not thread-safe and cannot be used in interrupt
200   /// handlers.
201   ///
202   /// @pre No handler is currently set.
203   ///
204   /// @returns @rst
205   ///
206   /// .. pw-status-codes::
207   ///
208   ///    OK: The interrupt handler was configured.
209   ///
210   ///    INVALID_ARGUMENT: The handler is empty.
211   ///
212   /// Returns other status codes as defined by the backend.
213   ///
214   /// @endrst
SetInterruptHandler(InterruptTrigger trigger,InterruptHandler && handler)215   Status SetInterruptHandler(InterruptTrigger trigger,
216                              InterruptHandler&& handler) {
217     if (handler == nullptr) {
218       return Status::InvalidArgument();
219     }
220     return DoSetInterruptHandler(trigger, std::move(handler));
221   }
222 
223   /// Clears the interrupt handler and disables any existing interrupts that
224   /// are enabled.
225   ///
226   /// @warning This method is not thread-safe and cannot be used in interrupt
227   /// handlers.
228   ///
229   /// @returns @rst
230   ///
231   /// .. pw-status-codes::
232   ///
233   ///    OK: The interrupt handler was cleared.
234   ///
235   /// Returns other status codes as defined by the backend.
236   ///
237   /// @endrst
ClearInterruptHandler()238   Status ClearInterruptHandler() {
239     PW_TRY(DisableInterruptHandler());
240     return DoSetInterruptHandler(InterruptTrigger::kActivatingEdge, nullptr);
241   }
242 
243   /// Enables interrupts which will trigger the interrupt handler.
244   ///
245   /// @warning This method is not thread-safe and cannot be used in interrupt
246   /// handlers.
247   ///
248   /// @pre A handler has been set using `SetInterruptHandler()`.
249   ///
250   /// @returns @rst
251   ///
252   /// .. pw-status-codes::
253   ///
254   ///    OK: The interrupt handler was configured.
255   ///
256   ///    FAILED_PRECONDITION: The line has not been enabled.
257   ///
258   /// Returns other status codes as defined by the backend.
259   ///
260   /// @endrst
EnableInterruptHandler()261   Status EnableInterruptHandler() { return DoEnableInterruptHandler(true); }
262 
263   /// Disables the interrupt handler. This is a no-op if interrupts are
264   /// disabled.
265   ///
266   /// This method can be called inside the interrupt handler for this line
267   /// without any external synchronization. However, the exact behavior is
268   /// backend-specific. There may be queued events that will trigger the handler
269   /// again after this call returns.
270   ///
271   /// @returns @rst
272   ///
273   /// .. pw-status-codes::
274   ///
275   ///    OK: The interrupt handler was disabled.
276   ///
277   /// Returns other status codes as defined by the backend.
278   ///
279   /// @endrst
DisableInterruptHandler()280   Status DisableInterruptHandler() { return DoEnableInterruptHandler(false); }
281 
282   /// Enables the line to initialize it into the default state as determined by
283   /// the backend.
284   ///
285   /// This may enable pull-up/down resistors, drive the line high/low, etc.
286   /// The line must be enabled before getting/setting the state or enabling
287   /// interrupts. Callers are responsible for waiting for the voltage level to
288   /// settle after this call returns.
289   ///
290   /// @warning This method is not thread-safe and cannot be used in interrupt
291   /// handlers.
292   ///
293   /// @returns @rst
294   ///
295   /// .. pw-status-codes::
296   ///
297   ///    OK: The line is enabled and ready for use.
298   ///
299   /// Returns other status codes as defined by the backend.
300   ///
301   /// @endrst
Enable()302   Status Enable() { return DoEnable(true); }
303 
304   /// Disables the line to power down any pull-up/down resistors and disconnect
305   /// from any voltage sources.
306   ///
307   /// This is usually done to save power. Interrupt handlers are automatically
308   /// disabled.
309   ///
310   /// @warning This method is not thread-safe and cannot be used in interrupt
311   /// handlers.
312   ///
313   /// @returns @rst
314   ///
315   /// .. pw-status-codes::
316   ///
317   ///    OK: The line is disabled.
318   ///
319   /// Returns other status codes as defined by the backend.
320   ///
321   /// @endrst
Disable()322   Status Disable() {
323     if (provides_interrupt()) {
324       PW_TRY(DisableInterruptHandler());
325     }
326     return DoEnable(false);
327   }
328 
329  private:
330   friend class DigitalInterrupt;
331   friend class DigitalIn;
332   friend class DigitalInInterrupt;
333   friend class DigitalOut;
334   friend class DigitalOutInterrupt;
335   friend class DigitalInOut;
336   friend class DigitalInOutInterrupt;
337 
338   // Private constructor so that only friends can extend us.
DigitalIoOptional(internal::Provides config)339   constexpr DigitalIoOptional(internal::Provides config) : config_(config) {}
340 
341   /// Enables the line to initialize it into the default state as determined by
342   /// the backend or disables the line to power down any pull-up/down resistors
343   /// and disconnect from any voltage sources.
344   ///
345   /// This may enable pull-up/down resistors, drive the line high/low, etc.
346   /// The line must be enabled before getting/setting the state or enabling
347   /// interrupts. Callers are responsible for waiting for the voltage level to
348   /// settle after this call returns.
349   ///
350   /// Calling DoEnable(true) on an already-enabled line should be a no-op, it
351   /// shouldn't reset the line back to the "default state".
352   ///
353   /// Calling DoEnable(false) should force the line into the disabled state,
354   /// If the line was not initialized at object construction time.
355   ///
356   /// @pre This method cannot be used in interrupt contexts.
357   /// @pre When disabling, the interrupt handler must already be disabled.
358   ///
359   /// @returns @rst
360   ///
361   /// .. pw-status-codes::
362   ///
363   ///    OK: The line is enabled and ready for use.
364   ///
365   /// Returns other status codes as defined by the backend.
366   ///
367   /// @endrst
368   virtual Status DoEnable(bool enable) = 0;
369 
370   /// Gets the state of the line.
371   ///
372   /// @pre This method cannot be used in interrupt contexts.
373   ///
374   /// @returns @rst
375   ///
376   /// .. pw-status-codes::
377   ///
378   ///    OK: An active or inactive state.
379   ///
380   ///    FAILED_PRECONDITION: The line has not been enabled.
381   ///
382   /// Returns other status codes as defined by the backend.
383   ///
384   /// @endrst
385   virtual Result<State> DoGetState() = 0;
386 
387   /// Sets the state of the line.
388   ///
389   /// Callers are responsible to wait for the voltage level to settle after this
390   /// call returns.
391   ///
392   /// @pre This method cannot be used in interrupt contexts.
393   ///
394   /// @returns @rst
395   ///
396   /// .. pw-status-codes::
397   ///
398   ///    OK: The state has been set.
399   ///
400   ///    FAILED_PRECONDITION: The line has not been enabled.
401   ///
402   /// Returns other status codes as defined by the backend.
403   ///
404   /// @endrst
405   virtual Status DoSetState(State level) = 0;
406 
407   /// Sets or clears an interrupt handler to execute when an interrupt is
408   /// triggered, and configures the condition for triggering the interrupt.
409   ///
410   /// The handler is executed in a backend-specific context—this may be a
411   /// system interrupt handler or a shared notification thread.
412   ///
413   /// The implementation is expected to provide the handler the last known state
414   /// of the input. The intention is to either sample the current state and
415   /// provide that or if not possible provide the state which triggerred the
416   /// interrupt (e.g. active for activating edge, and inactive for deactivating
417   /// edge).
418   ///
419   /// The handler is cleared by passing an empty handler, this can be checked by
420   /// comparing the handler to a nullptr. The implementation must guarantee that
421   /// the handler is not currently executing and (and will never execute again)
422   /// after returning from DoSetInterruptHandler(_, nullptr).
423   ///
424   /// @pre This method cannot be used in interrupt contexts.
425   /// @pre If setting a handler, no handler is permitted to be currently set.
426   /// @pre When cleaing a handler, the interrupt handler must be disabled.
427   ///
428   /// @returns @rst
429   ///
430   /// .. pw-status-codes::
431   ///
432   ///    OK: The interrupt handler was configured.
433   ///
434   /// Returns other status codes as defined by the backend.
435   ///
436   /// @endrst
437   virtual Status DoSetInterruptHandler(InterruptTrigger trigger,
438                                        InterruptHandler&& handler) = 0;
439 
440   /// Enables or disables interrupts which will trigger the interrupt handler.
441   ///
442   /// @warning This interrupt handler disabling must be both thread-safe and,
443   ///          interrupt-safe, however enabling is not interrupt-safe and not
444   ///          thread-safe.
445   ///
446   /// @pre When enabling, a handler must have been set using
447   ///      `DoSetInterruptHandler()`.
448   /// @pre Interrupt handler enabling cannot be used in interrupt contexts.
449   ///
450   /// @returns @rst
451   ///
452   /// .. pw-status-codes::
453   ///
454   ///    OK: The interrupt handler was configured.
455   ///
456   ///    FAILED_PRECONDITION: The line has not been enabled.
457   ///
458   /// Returns other status codes as defined by the backend.
459   ///
460   /// @endrst
461   virtual Status DoEnableInterruptHandler(bool enable) = 0;
462 
463   // The configuration of this line.
464   const internal::Provides config_;
465 };
466 
467 // A digital I/O line that supports only interrupts.
468 //
469 // The input and output methods are hidden and must not be called.
470 //
471 // Use this class in APIs when only interrupt functionality is required.
472 // Extend this class to implement a line that only supports interrupts.
473 //
474 class DigitalInterrupt
475     : public DigitalIoOptional,
476       public internal::Conversions<DigitalInterrupt, DigitalIoOptional> {
477  public:
478   // Available functionality
479   using DigitalIoOptional::ClearInterruptHandler;
480   using DigitalIoOptional::DisableInterruptHandler;
481   using DigitalIoOptional::EnableInterruptHandler;
482   using DigitalIoOptional::SetInterruptHandler;
483 
484  protected:
DigitalInterrupt()485   constexpr DigitalInterrupt()
486       : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalInterrupt>()) {}
487 
488  private:
489   // Unavailable functionality
490   using DigitalIoOptional::provides_input;
491   using DigitalIoOptional::provides_interrupt;
492   using DigitalIoOptional::provides_output;
493 
494   using DigitalIoOptional::GetState;
495   using DigitalIoOptional::IsStateActive;
496   using DigitalIoOptional::SetState;
497   using DigitalIoOptional::SetStateActive;
498   using DigitalIoOptional::SetStateInactive;
499 
500   // These overrides invoke PW_CRASH.
501   Status DoSetState(State) final;
502   Result<State> DoGetState() final;
503 };
504 
505 // A digital I/O line that supports only input (getting state).
506 //
507 // The output and interrupt methods are hidden and must not be called.
508 //
509 // Use this class in APIs when only input functionality is required.
510 // Extend this class to implement a line that only supports getting state.
511 //
512 class DigitalIn : public DigitalIoOptional,
513                   public internal::Conversions<DigitalIn, DigitalIoOptional> {
514  public:
515   // Available functionality
516   using DigitalIoOptional::GetState;
517   using DigitalIoOptional::IsStateActive;
518 
519  protected:
DigitalIn()520   constexpr DigitalIn()
521       : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalIn>()) {}
522 
523  private:
524   // Unavailable functionality
525   using DigitalIoOptional::provides_input;
526   using DigitalIoOptional::provides_interrupt;
527   using DigitalIoOptional::provides_output;
528 
529   using DigitalIoOptional::ClearInterruptHandler;
530   using DigitalIoOptional::DisableInterruptHandler;
531   using DigitalIoOptional::EnableInterruptHandler;
532   using DigitalIoOptional::SetInterruptHandler;
533   using DigitalIoOptional::SetState;
534   using DigitalIoOptional::SetStateActive;
535   using DigitalIoOptional::SetStateInactive;
536 
537   // These overrides invoke PW_CRASH.
538   Status DoSetState(State) final;
539   Status DoSetInterruptHandler(InterruptTrigger, InterruptHandler&&) final;
540   Status DoEnableInterruptHandler(bool) final;
541 };
542 
543 // An input line that supports interrupts.
544 //
545 // The output methods are hidden and must not be called.
546 //
547 // Use in APIs when input and interrupt functionality is required.
548 //
549 // Extend this class to implement a line that supports input (getting state) and
550 // listening for interrupts at the same time.
551 //
552 class DigitalInInterrupt
553     : public DigitalIoOptional,
554       public internal::Conversions<DigitalInInterrupt, DigitalIoOptional> {
555  public:
556   // Available functionality
557   using DigitalIoOptional::ClearInterruptHandler;
558   using DigitalIoOptional::DisableInterruptHandler;
559   using DigitalIoOptional::EnableInterruptHandler;
560   using DigitalIoOptional::GetState;
561   using DigitalIoOptional::IsStateActive;
562   using DigitalIoOptional::SetInterruptHandler;
563 
564  protected:
DigitalInInterrupt()565   constexpr DigitalInInterrupt()
566       : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalInInterrupt>()) {}
567 
568  private:
569   // Unavailable functionality
570   using DigitalIoOptional::provides_input;
571   using DigitalIoOptional::provides_interrupt;
572   using DigitalIoOptional::provides_output;
573 
574   using DigitalIoOptional::SetState;
575   using DigitalIoOptional::SetStateActive;
576   using DigitalIoOptional::SetStateInactive;
577 
578   // These overrides invoke PW_CRASH.
579   Status DoSetState(State) final;
580 };
581 
582 // A digital I/O line that supports only output (setting state).
583 //
584 // Input and interrupt functions are hidden and must not be called.
585 //
586 // Use in APIs when only output functionality is required.
587 // Extend this class to implement a line that supports output only.
588 //
589 class DigitalOut : public DigitalIoOptional,
590                    public internal::Conversions<DigitalOut, DigitalIoOptional> {
591  public:
592   // Available functionality
593   using DigitalIoOptional::SetState;
594   using DigitalIoOptional::SetStateActive;
595   using DigitalIoOptional::SetStateInactive;
596 
597  protected:
DigitalOut()598   constexpr DigitalOut()
599       : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalOut>()) {}
600 
601  private:
602   // Unavailable functionality
603   using DigitalIoOptional::provides_input;
604   using DigitalIoOptional::provides_interrupt;
605   using DigitalIoOptional::provides_output;
606 
607   using DigitalIoOptional::ClearInterruptHandler;
608   using DigitalIoOptional::DisableInterruptHandler;
609   using DigitalIoOptional::EnableInterruptHandler;
610   using DigitalIoOptional::GetState;
611   using DigitalIoOptional::IsStateActive;
612   using DigitalIoOptional::SetInterruptHandler;
613 
614   // These overrides invoke PW_CRASH.
615   Result<State> DoGetState() final;
616   Status DoSetInterruptHandler(InterruptTrigger, InterruptHandler&&) final;
617   Status DoEnableInterruptHandler(bool) final;
618 };
619 
620 // A digital I/O line that supports output and interrupts.
621 //
622 // Input methods are hidden and must not be called.
623 //
624 // Use in APIs when output and interrupt functionality is required. For
625 // example, to represent a two-way signalling line.
626 //
627 // Extend this class to implement a line that supports both output and
628 // listening for interrupts at the same time.
629 //
630 class DigitalOutInterrupt
631     : public DigitalIoOptional,
632       public internal::Conversions<DigitalOutInterrupt, DigitalIoOptional> {
633  public:
634   // Available functionality
635   using DigitalIoOptional::ClearInterruptHandler;
636   using DigitalIoOptional::DisableInterruptHandler;
637   using DigitalIoOptional::EnableInterruptHandler;
638   using DigitalIoOptional::SetInterruptHandler;
639   using DigitalIoOptional::SetState;
640   using DigitalIoOptional::SetStateActive;
641   using DigitalIoOptional::SetStateInactive;
642 
643  protected:
DigitalOutInterrupt()644   constexpr DigitalOutInterrupt()
645       : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalOutInterrupt>()) {}
646 
647  private:
648   // Unavailable functionality
649   using DigitalIoOptional::provides_input;
650   using DigitalIoOptional::provides_interrupt;
651   using DigitalIoOptional::provides_output;
652 
653   using DigitalIoOptional::GetState;
654   using DigitalIoOptional::IsStateActive;
655 
656   // These overrides invoke PW_CRASH.
657   Result<State> DoGetState() final;
658 };
659 
660 // A digital I/O line that supports both input and output.
661 //
662 // Use in APIs when both input and output functionality is required. For
663 // example, to represent a line which is shared by multiple controllers.
664 //
665 // Extend this class to implement a line that supports both input and output at
666 // the same time.
667 //
668 class DigitalInOut
669     : public DigitalIoOptional,
670       public internal::Conversions<DigitalInOut, DigitalIoOptional> {
671  public:
672   // Available functionality
673   using DigitalIoOptional::GetState;
674   using DigitalIoOptional::IsStateActive;
675   using DigitalIoOptional::SetState;
676   using DigitalIoOptional::SetStateActive;
677   using DigitalIoOptional::SetStateInactive;
678 
679  protected:
DigitalInOut()680   constexpr DigitalInOut()
681       : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalInOut>()) {}
682 
683  private:
684   // Unavailable functionality
685   using DigitalIoOptional::provides_input;
686   using DigitalIoOptional::provides_interrupt;
687   using DigitalIoOptional::provides_output;
688 
689   using DigitalIoOptional::ClearInterruptHandler;
690   using DigitalIoOptional::DisableInterruptHandler;
691   using DigitalIoOptional::EnableInterruptHandler;
692   using DigitalIoOptional::SetInterruptHandler;
693 
694   // These overrides invoke PW_CRASH.
695   Status DoSetInterruptHandler(InterruptTrigger, InterruptHandler&&) final;
696   Status DoEnableInterruptHandler(bool) final;
697 };
698 
699 // A line that supports input, output, and interrupts.
700 //
701 // Use in APIs when input, output, and interrupts are required. For example to
702 // represent a two-way shared line with state transition notifications.
703 //
704 // Extend this class to implement a line that supports all the functionality at
705 // the same time.
706 //
707 class DigitalInOutInterrupt
708     : public DigitalIoOptional,
709       public internal::Conversions<DigitalInOutInterrupt, DigitalIoOptional> {
710  public:
711   // Available functionality
712   using DigitalIoOptional::ClearInterruptHandler;
713   using DigitalIoOptional::DisableInterruptHandler;
714   using DigitalIoOptional::EnableInterruptHandler;
715   using DigitalIoOptional::GetState;
716   using DigitalIoOptional::IsStateActive;
717   using DigitalIoOptional::SetInterruptHandler;
718   using DigitalIoOptional::SetState;
719   using DigitalIoOptional::SetStateActive;
720   using DigitalIoOptional::SetStateInactive;
721 
722  protected:
DigitalInOutInterrupt()723   constexpr DigitalInOutInterrupt()
724       : DigitalIoOptional(internal::AlwaysProvidedBy<DigitalInOutInterrupt>()) {
725   }
726 
727  private:
728   // Unavailable functionality
729   using DigitalIoOptional::provides_input;
730   using DigitalIoOptional::provides_interrupt;
731   using DigitalIoOptional::provides_output;
732 };
733 
734 }  // namespace pw::digital_io
735