use core::marker::PhantomData;
use crate::{
kernel::{cfg::CfgBuilder, interrupt, Port, PortThreading},
utils::{for_times::Nat, ComptimeVec},
};
impl<System: Port> interrupt::InterruptLine<System> {
pub const fn build() -> CfgInterruptLineBuilder<System> {
CfgInterruptLineBuilder::new()
}
}
#[must_use = "must call `finish()` to complete registration"]
pub struct CfgInterruptLineBuilder<System> {
_phantom: PhantomData<System>,
line: Option<interrupt::InterruptNum>,
priority: Option<interrupt::InterruptPriority>,
enabled: bool,
}
impl<System: Port> CfgInterruptLineBuilder<System> {
const fn new() -> Self {
Self {
_phantom: PhantomData,
line: None,
priority: None,
enabled: false,
}
}
pub const fn line(self, line: interrupt::InterruptNum) -> Self {
assert!(self.line.is_none(), "`line` is specified twice");
Self {
line: Some(line),
..self
}
}
pub const fn priority(self, priority: interrupt::InterruptPriority) -> Self {
assert!(self.priority.is_none(), "`priority` is specified twice");
Self {
priority: Some(priority),
..self
}
}
pub const fn enabled(self, enabled: bool) -> Self {
Self { enabled, ..self }
}
pub const fn finish(self, cfg: &mut CfgBuilder<System>) -> interrupt::InterruptLine<System> {
let inner = &mut cfg.inner;
let line_num = if let Some(line) = self.line {
line
} else {
panic!("`line` is not specified");
};
let i = if let Some(i) = vec_position!(inner.interrupt_lines, |il| il.num == line_num) {
i
} else {
inner.interrupt_lines.push(CfgBuilderInterruptLine {
num: line_num,
priority: None,
enabled: false,
});
inner.interrupt_lines.len() - 1
};
let cfg_interrupt_line = inner.interrupt_lines.get_mut(i);
if let Some(priority) = self.priority {
assert!(
cfg_interrupt_line.priority.is_none(),
"`priority` is already specified for this interrupt line"
);
cfg_interrupt_line.priority = Some(priority);
}
if self.enabled {
cfg_interrupt_line.enabled = true;
}
interrupt::InterruptLine::from_num(line_num)
}
}
#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub struct CfgBuilderInterruptLine {
num: interrupt::InterruptNum,
priority: Option<interrupt::InterruptPriority>,
enabled: bool,
}
impl CfgBuilderInterruptLine {
pub(super) const fn is_initially_managed<System: Port>(&self) -> bool {
if let Some(priority) = self.priority {
let range = System::MANAGED_INTERRUPT_PRIORITY_RANGE;
priority >= range.start && priority < range.end
} else {
false
}
}
pub const fn to_init<System>(&self) -> interrupt::InterruptLineInit<System> {
interrupt::InterruptLineInit {
line: interrupt::InterruptLine::from_num(self.num),
priority: if let Some(i) = self.priority { i } else { 0 },
flags: {
let mut f = 0;
if self.priority.is_some() {
f |= interrupt::InterruptLineInitFlags::SET_PRIORITY.bits();
}
if self.enabled {
f |= interrupt::InterruptLineInitFlags::ENABLE.bits();
}
interrupt::InterruptLineInitFlags::from_bits_truncate(f)
},
}
}
}
impl<System: Port> interrupt::InterruptHandler<System> {
pub const fn build() -> CfgInterruptHandlerBuilder<System> {
CfgInterruptHandlerBuilder::new()
}
}
pub struct CfgInterruptHandlerBuilder<System> {
_phantom: PhantomData<System>,
line: Option<interrupt::InterruptNum>,
start: Option<fn(usize)>,
param: usize,
priority: i32,
unmanaged: bool,
}
impl<System: Port> CfgInterruptHandlerBuilder<System> {
const fn new() -> Self {
Self {
_phantom: PhantomData,
line: None,
start: None,
param: 0,
priority: 0,
unmanaged: false,
}
}
pub const fn start(self, start: fn(usize)) -> Self {
Self {
start: Some(start),
..self
}
}
pub const fn param(self, param: usize) -> Self {
Self { param, ..self }
}
pub const fn line(self, line: interrupt::InterruptNum) -> Self {
assert!(self.line.is_none(), "`line` is specified twice");
Self {
line: Some(line),
..self
}
}
pub const fn priority(self, priority: i32) -> Self {
Self { priority, ..self }
}
pub const unsafe fn unmanaged(self) -> Self {
Self {
unmanaged: true,
..self
}
}
pub const fn finish(self, cfg: &mut CfgBuilder<System>) -> interrupt::InterruptHandler<System> {
let inner = &mut cfg.inner;
let line_num = if let Some(line) = self.line {
line
} else {
panic!("`line` is not specified");
};
let order = inner.interrupt_handlers.len();
inner.interrupt_handlers.push(CfgBuilderInterruptHandler {
line: line_num,
start: if let Some(x) = self.start {
x
} else {
panic!("`start` is not specified")
},
param: self.param,
priority: self.priority,
unmanaged: self.unmanaged,
order,
});
interrupt::InterruptHandler::new()
}
}
#[doc(hidden)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CfgBuilderInterruptHandler {
line: interrupt::InterruptNum,
start: fn(usize),
param: usize,
priority: i32,
unmanaged: bool,
order: usize,
}
pub(super) const fn panic_if_unmanaged_safety_is_violated<System: Port>(
interrupt_lines: &ComptimeVec<CfgBuilderInterruptLine>,
interrupt_handlers: &ComptimeVec<CfgBuilderInterruptHandler>,
) {
let mut i = 0;
while i < interrupt_handlers.len() {
let handler = interrupt_handlers.get(i);
i += 1;
if handler.unmanaged {
continue;
}
let is_line_assumed_managed = {
let lines = System::MANAGED_INTERRUPT_LINES;
let mut i = 0;
loop {
if i < lines.len() {
if lines[i] == handler.line {
break true;
}
i += 1;
} else {
break false;
}
}
};
let managed_line_i = vec_position!(interrupt_lines, |line| line.num == handler.line
&& line.is_initially_managed::<System>());
let is_line_managed = managed_line_i.is_some() || is_line_assumed_managed;
assert!(
is_line_managed,
"An interrupt handler that is not marked with `unmanaged` \
is attached to an interrupt line whose priority value is \
unspecified or doesn't fall within a managed range."
);
}
}
pub(super) const fn sort_handlers(
interrupt_handlers: &mut ComptimeVec<CfgBuilderInterruptHandler>,
) {
sort_unstable_by!(
interrupt_handlers.len(),
|i| interrupt_handlers.get_mut(i),
|x, y| if x.line != y.line {
x.line < y.line
} else if x.priority != y.priority {
x.priority < y.priority
} else {
x.order < y.order
}
);
}
pub type InterruptHandlerFn = unsafe extern "C" fn();
#[derive(Debug)]
pub struct InterruptHandlerTable<T: ?Sized = [Option<InterruptHandlerFn>]> {
storage: T,
}
impl InterruptHandlerTable {
#[inline]
pub const fn get(&self, line: interrupt::InterruptNum) -> Option<InterruptHandlerFn> {
if line < self.storage.len() {
self.storage[line]
} else {
None
}
}
}
type ProtoCombinedHandlerFn = fn();
#[doc(hidden)]
pub trait CfgBuilderInterruptHandlerList {
type NumHandlers: Nat;
const HANDLERS: &'static [CfgBuilderInterruptHandler];
}
struct MakeCombinedHandlers<System, Handlers, const NUM_HANDLERS: usize>(
PhantomData<(System, Handlers)>,
);
trait MakeCombinedHandlersTrait {
type System: Port;
type NumHandlers: Nat;
const HANDLERS: &'static [CfgBuilderInterruptHandler];
const NUM_HANDLERS: usize;
const PROTO_COMBINED_HANDLERS: &'static [ProtoCombinedHandlerFn];
const COMBINED_HANDLERS: &'static [Option<InterruptHandlerFn>];
}
impl<System: Port, Handlers: CfgBuilderInterruptHandlerList, const NUM_HANDLERS: usize>
MakeCombinedHandlersTrait for MakeCombinedHandlers<System, Handlers, NUM_HANDLERS>
{
type System = System;
type NumHandlers = Handlers::NumHandlers;
const HANDLERS: &'static [CfgBuilderInterruptHandler] = Handlers::HANDLERS;
const NUM_HANDLERS: usize = NUM_HANDLERS;
const PROTO_COMBINED_HANDLERS: &'static [ProtoCombinedHandlerFn] =
&Self::PROTO_COMBINED_HANDLERS_ARRAY;
const COMBINED_HANDLERS: &'static [Option<InterruptHandlerFn>] = &Self::COMBINED_HANDLERS_ARRAY;
}
impl<System: Port, Handlers: CfgBuilderInterruptHandlerList, const NUM_HANDLERS: usize>
MakeCombinedHandlers<System, Handlers, NUM_HANDLERS>
{
const PROTO_COMBINED_HANDLERS_ARRAY: [ProtoCombinedHandlerFn; NUM_HANDLERS] =
Self::proto_combined_handlers_array();
const fn proto_combined_handlers_array() -> [ProtoCombinedHandlerFn; NUM_HANDLERS] {
const_array_from_fn! {
fn iter<[T: MakeCombinedHandlersTrait], I: Nat>(ref mut cell: T) -> ProtoCombinedHandlerFn {
#[inline(always)]
fn proto_combined_handler<T: MakeCombinedHandlersTrait, I: Nat>() {
let handler = T::HANDLERS[I::N];
(handler.start)(handler.param);
let next_i = I::N + 1;
if next_i >= T::NUM_HANDLERS || T::HANDLERS[next_i].line != handler.line {
return;
}
if T::System::is_cpu_lock_active() {
unsafe { T::System::leave_cpu_lock() };
}
T::PROTO_COMBINED_HANDLERS[next_i]();
}
proto_combined_handler::<T, I>
}
(0..NUM_HANDLERS).map(|i| iter::<[Self], i>(Self(PhantomData))).collect::<[_; Handlers::NumHandlers]>()
}
}
const COMBINED_HANDLERS_ARRAY: [Option<InterruptHandlerFn>; NUM_HANDLERS] =
Self::combined_handlers_array();
const fn combined_handlers_array() -> [Option<InterruptHandlerFn>; NUM_HANDLERS] {
const_array_from_fn! {
fn iter<[T: MakeCombinedHandlersTrait], I: Nat>(ref mut cell: T) -> Option<InterruptHandlerFn> {
extern "C" fn combined_handler<T: MakeCombinedHandlersTrait, I: Nat>() {
T::PROTO_COMBINED_HANDLERS[I::N]();
}
let handler = T::HANDLERS[I::N];
let is_first_handler_of_line = if I::N == 0 {
true
} else {
T::HANDLERS[I::N - 1].line != handler.line
};
if is_first_handler_of_line {
Some(combined_handler::<T, I> as InterruptHandlerFn)
} else {
None
}
}
(0..NUM_HANDLERS).map(|i| iter::<[Self], i>(Self(PhantomData))).collect::<[_; Handlers::NumHandlers]>()
}
}
}
#[doc(hidden)]
pub const unsafe fn new_interrupt_handler_table<
System: Port,
NumLines: Nat,
Handlers: CfgBuilderInterruptHandlerList,
const NUM_LINES: usize,
const NUM_HANDLERS: usize,
>() -> InterruptHandlerTable<[Option<InterruptHandlerFn>; NUM_LINES]> {
assert!(NumLines::N == NUM_LINES);
assert!(Handlers::NumHandlers::N == NUM_HANDLERS);
let mut i = 0;
while i < NUM_HANDLERS {
let handler = Handlers::HANDLERS[i];
assert!(handler.line < NUM_LINES);
i += 1;
}
let storage = const_array_from_fn! {
fn iter<[T: MakeCombinedHandlersTrait], I: Nat>(ref mut cell: T) -> Option<InterruptHandlerFn> {
let line = I::N;
let i = lower_bound!(T::NUM_HANDLERS, |i| T::HANDLERS[i].line < line);
if i >= T::NUM_HANDLERS || T::HANDLERS[i].line != line {
None
} else {
let handler = T::COMBINED_HANDLERS[i];
assert!(handler.is_some());
handler
}
}
(0..NUM_LINES).map(|i| iter::<[MakeCombinedHandlers<
System,
Handlers,
NUM_HANDLERS,
>], i>(MakeCombinedHandlers(PhantomData))).collect::<[_; NumLines]>()
};
InterruptHandlerTable { storage }
}
#[doc(hidden)]
pub const fn num_required_interrupt_line_slots(handlers: &[CfgBuilderInterruptHandler]) -> usize {
let mut i = 0;
let mut out = 0;
while i < handlers.len() {
if handlers[i].line + 1 > out {
out = handlers[i].line + 1;
}
i += 1;
}
out
}