use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
pub const MAX_IRQS: usize = crate::config::MAX_IRQS;
#[cfg(not(loom))]
static HANDLERS: [AtomicUsize; MAX_IRQS] = [const { AtomicUsize::new(0) }; MAX_IRQS];
#[cfg(loom)]
loom::lazy_static! {
static ref HANDLERS: [AtomicUsize; MAX_IRQS] = core::array::from_fn(|_| AtomicUsize::new(0));
}
#[cfg(not(loom))]
static UNTRACED: [AtomicBool; MAX_IRQS] = [const { AtomicBool::new(false) }; MAX_IRQS];
#[cfg(loom)]
loom::lazy_static! {
static ref UNTRACED: [AtomicBool; MAX_IRQS] = core::array::from_fn(|_| AtomicBool::new(false));
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IrqError {
OutOfRange,
}
pub fn register(irq_num: u32, handler: fn()) -> Result<(), IrqError> {
let slot = HANDLERS.get(irq_num as usize).ok_or(IrqError::OutOfRange)?;
slot.store(handler as usize, Ordering::Release);
Ok(())
}
pub fn register_untraced(irq_num: u32, handler: fn()) -> Result<(), IrqError> {
register(irq_num, handler)?;
if let Some(slot) = UNTRACED.get(irq_num as usize) {
slot.store(true, Ordering::Release);
}
Ok(())
}
pub fn unregister(irq_num: u32) {
if let Some(slot) = HANDLERS.get(irq_num as usize) {
slot.store(0, Ordering::Release);
}
if let Some(slot) = UNTRACED.get(irq_num as usize) {
slot.store(false, Ordering::Release);
}
}
pub fn enable(irq_num: u32) {
crate::port::arch::irq_enable(irq_num);
}
pub fn disable(irq_num: u32) {
crate::port::arch::irq_disable(irq_num);
}
pub fn set_priority(irq_num: u32, priority: u8) {
crate::port::arch::irq_set_priority(irq_num, priority);
}
pub fn dispatch(irq_num: u32) {
let Some(slot) = HANDLERS.get(irq_num as usize) else {
return;
};
let ptr = slot.load(Ordering::Acquire);
if ptr == 0 {
return;
}
let raw_ptr = ptr as *const ();
let handler: fn() = unsafe { core::mem::transmute::<*const (), fn()>(raw_ptr) };
#[cfg(feature = "trace")]
{
let traced = !UNTRACED
.get(irq_num as usize)
.map(|f| f.load(Ordering::Acquire))
.unwrap_or(false);
if traced {
crate::trace::isr(irq_num, true);
}
handler();
if traced {
crate::trace::isr(irq_num, false);
}
}
#[cfg(not(feature = "trace"))]
handler();
}
#[cfg(feature = "test-support")]
pub(crate) fn reset_for_test() {
for slot in HANDLERS.iter() {
slot.store(0, Ordering::Relaxed);
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::sync::atomic::{AtomicBool, Ordering as StdOrdering};
static CALLED: AtomicBool = AtomicBool::new(false);
fn handler() {
CALLED.store(true, StdOrdering::Release);
}
#[test]
fn register_and_dispatch() {
crate::kernel_test! {
CALLED.store(false, StdOrdering::Release);
register(3, handler).unwrap();
dispatch(3);
assert!(CALLED.load(StdOrdering::Acquire));
}
}
#[test]
fn dispatch_unregistered_is_noop() {
crate::kernel_test! {
dispatch(1);
}
}
#[test]
fn register_out_of_range() {
crate::kernel_test! {
assert_eq!(register(MAX_IRQS as u32, handler), Err(IrqError::OutOfRange));
}
}
#[test]
fn unregister_makes_dispatch_noop() {
crate::kernel_test! {
CALLED.store(false, StdOrdering::Release);
register(5, handler).unwrap();
unregister(5);
dispatch(5);
assert!(!CALLED.load(StdOrdering::Acquire));
}
}
}