ariel_os_nrf/
extint_registry.rs

1use ariel_os_embassy_common::gpio::input::InterruptError;
2use embassy_nrf::{gpio::Pin, Peripheral};
3use portable_atomic::{AtomicU8, Ordering};
4
5#[cfg(context = "nrf51")]
6const INT_CHANNEL_COUNT: u8 = 4;
7#[cfg(not(context = "nrf51"))]
8const INT_CHANNEL_COUNT: u8 = 8;
9
10pub static EXTINT_REGISTRY: ExtIntRegistry = ExtIntRegistry::new();
11
12pub struct ExtIntRegistry {
13    used_interrupt_channel_count: AtomicU8,
14}
15
16impl ExtIntRegistry {
17    #[expect(clippy::new_without_default)]
18    #[must_use]
19    pub const fn new() -> Self {
20        Self {
21            used_interrupt_channel_count: AtomicU8::new(0),
22        }
23    }
24
25    pub fn use_interrupt_for_pin<PIN: Peripheral<P: Pin>>(
26        &self,
27        _pin: &mut PIN, // Require the caller to have the peripheral
28    ) -> Result<(), InterruptError> {
29        // NOTE(ordering): this acts as a lock, so we use Acquire/Release ordering.
30        let update_res = self.used_interrupt_channel_count.fetch_update(
31            Ordering::AcqRel,
32            Ordering::Acquire,
33            |c| {
34                if c == INT_CHANNEL_COUNT {
35                    None
36                } else {
37                    // This cannot overflow because `INT_CHANNEL_COUNT` is lower than u8::MAX.
38                    Some(c + 1)
39                }
40            },
41        );
42
43        if update_res.is_err() {
44            return Err(InterruptError::NoIntChannelAvailable);
45        }
46
47        Ok(())
48    }
49}