1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! Getting interrupts to the code waiting on them.
//!
//! Every `async` thing in this crate and in `rpi-hal` is parked on a
//! waker that some interrupt has to fire. `rpi-hal`'s `rt` feature
//! installs the vector table, which saves the caller-saved registers and
//! branches to one symbol, `__irq_handler` — and that symbol is the
//! application's to define.
//!
//! # The failure this exists to prevent
//!
//! `rpi-hal`'s default `__irq_handler` is a weak `bx lr`. An application
//! that forgets to define one therefore *links and boots*. The first
//! `embassy-time` deadline then fires, nothing acknowledges the System
//! Timer's Compare 1 match, the interrupt controller goes on asserting,
//! and the core re-enters the handler forever. Every task stops at that
//! instant.
//!
//! It is not a hang in whatever ran last, and it looks exactly like one.
//! Nothing reports it, and there is no link error to catch it — which is
//! why this module offers two ways to be right and no way to be silently
//! wrong.
//!
//! # Which one to use
//!
//! **An application with no interrupt sources of its own** turns on the
//! `irq-dispatch` feature and writes nothing: it defines
//! `__irq_handler` for you, calling [`dispatch`](crate::irq::dispatch).
//! (Qualified because a module's `//!` links are merged with the
//! `pub mod` declaration's and resolve in the crate root's scope, where
//! a bare `dispatch` is not in sight.)
//!
//! **An application with its own sources** leaves the feature off and
//! composes, which is the only way to reach a source this crate has
//! never heard of:
//!
//! ```ignore
//! #[unsafe(no_mangle)]
//! pub extern "C" fn __irq_handler() {
//! rpi_hal_embassy::irq::dispatch();
//!
//! let lic = Lic::new(unsafe { pac::Peripherals::steal() }.LIC);
//! if lic.is_uart_pending() {
//! my_uart_handler();
//! }
//! }
//! ```
//!
//! Turning the feature on *and* defining `__irq_handler` is a duplicate
//! definition and fails to link, which is the intended way to find out
//! that both were asked for.
use Lic;
use pac;
/// Services every interrupt source this crate and `rpi-hal` know about.
///
/// Safe to call with nothing pending, and safe to call from a handler
/// that goes on to check sources of its own: each source below is gated
/// on its own pending bit, and each entry point it calls is documented as
/// harmless when there is nothing to do.
///
/// # What it covers
///
/// The System Timer's Compare 1 always — that is this crate's
/// `embassy-time` driver, and the one source an application using this
/// crate is certain to have.
///
/// The rest only under the `async` feature, which is what turns on
/// `rpi-hal`'s interrupt-driven drivers in the first place: USB, GPIO
/// edges, I2C, UART and the SD controller. Without it there are no
/// futures parked on any of them and nothing to wake.
///
/// # What it does not cover
///
/// Anything an application drives itself. The sources here are the ones
/// whose wakers live in this crate or in `rpi-hal`; a peripheral the
/// application programmed is one only it can acknowledge, and the
/// composed form in this module's documentation is how that is reached.
///
/// # Why each source is checked, not chained
///
/// More than one can be pending on a single entry, and a handler that
/// stops at the first one leaves the others asserting — which is the
/// same livelock as having no handler at all, arrived at from a handler
/// that looked right. So these are independent `if`s rather than an
/// `else if` chain, and each source is serviced by the crate that owns
/// it.
/// The `__irq_handler` the vector table branches to, for an application
/// with no interrupt sources of its own.
///
/// Behind `irq-dispatch` rather than unconditional because this is a
/// *definition*, not a hook: an application that has its own
/// `__irq_handler` and turns this on gets a duplicate-symbol link error,
/// and there is no sensible way for the crate to guess which of the two
/// was meant. Opting in is how an application says it has none.
// `unsafe(no_mangle)` rather than the bare form rpi-hal's own examples
// use: this crate is edition 2024, which requires the wrapper.
pub extern "C"