use core::{fmt, hash, marker::PhantomData};
use super::{
raw, raw_cfg, Cfg, ClearInterruptLineError, EnableInterruptLineError, PendInterruptLineError,
QueryInterruptLineError, SetInterruptLinePriorityError,
};
use crate::{
closure::{Closure, IntoClosureConst},
utils::{for_times::Nat, slice_sort_unstable_by, ComptimeVec, Init, PhantomInvariant},
};
pub use raw::{InterruptNum, InterruptPriority};
pub struct InterruptLine<System: raw::KernelInterruptLine>(InterruptNum, PhantomInvariant<System>);
impl<System: raw::KernelInterruptLine> Clone for InterruptLine<System> {
#[inline]
fn clone(&self) -> Self {
Self(self.0, self.1)
}
}
impl<System: raw::KernelInterruptLine> Copy for InterruptLine<System> {}
impl<System: raw::KernelInterruptLine> PartialEq for InterruptLine<System> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<System: raw::KernelInterruptLine> Eq for InterruptLine<System> {}
impl<System: raw::KernelInterruptLine> hash::Hash for InterruptLine<System> {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
hash::Hash::hash(&self.0, state);
}
}
impl<System: raw::KernelInterruptLine> fmt::Debug for InterruptLine<System> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("InterruptLine").field(&self.0).finish()
}
}
impl<System: raw::KernelInterruptLine> InterruptLine<System> {
#[inline]
pub const fn from_num(num: InterruptNum) -> Self {
Self(num, Init::INIT)
}
#[inline]
pub const fn num(self) -> InterruptNum {
self.0
}
}
impl<System: raw::KernelInterruptLine> InterruptLine<System> {
pub const fn define() -> InterruptLineDefiner<System> {
InterruptLineDefiner::new()
}
#[inline(never)]
pub fn set_priority(
self,
value: InterruptPriority,
) -> Result<(), SetInterruptLinePriorityError> {
if !System::RAW_MANAGED_INTERRUPT_PRIORITY_RANGE.contains(&value) {
return Err(SetInterruptLinePriorityError::BadParam);
}
unsafe { self.set_priority_unchecked(value) }
}
#[inline]
pub unsafe fn set_priority_unchecked(
self,
value: InterruptPriority,
) -> Result<(), SetInterruptLinePriorityError> {
unsafe { System::raw_interrupt_line_set_priority(self.0, value) }
}
#[inline]
pub fn enable(self) -> Result<(), EnableInterruptLineError> {
unsafe { System::raw_interrupt_line_enable(self.0) }
}
#[inline]
pub fn disable(self) -> Result<(), EnableInterruptLineError> {
unsafe { System::raw_interrupt_line_disable(self.0) }
}
#[inline]
pub fn pend(self) -> Result<(), PendInterruptLineError> {
unsafe { System::raw_interrupt_line_pend(self.0) }
}
#[inline]
pub fn clear(self) -> Result<(), ClearInterruptLineError> {
unsafe { System::raw_interrupt_line_clear(self.0) }
}
#[inline]
pub fn is_pending(self) -> Result<bool, QueryInterruptLineError> {
unsafe { System::raw_interrupt_line_is_pending(self.0) }
}
}
pub struct StaticInterruptHandler<System: raw::KernelInterruptLine>(PhantomInvariant<System>);
impl<System: raw::KernelInterruptLine> fmt::Debug for StaticInterruptHandler<System> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("InterruptHandler")
}
}
impl<System: raw::KernelInterruptLine> Clone for StaticInterruptHandler<System> {
#[inline]
fn clone(&self) -> Self {
Self(self.0)
}
}
impl<System: raw::KernelInterruptLine> Copy for StaticInterruptHandler<System> {}
impl<System: raw::KernelInterruptLine> StaticInterruptHandler<System> {
const fn new() -> Self {
Self(PhantomData)
}
pub const fn define() -> InterruptHandlerDefiner<System> {
InterruptHandlerDefiner::new()
}
}
#[must_use = "must call `finish()` to complete registration"]
pub struct InterruptLineDefiner<System: raw::KernelInterruptLine> {
_phantom: PhantomInvariant<System>,
line: Option<InterruptNum>,
priority: Option<InterruptPriority>,
enabled: bool,
}
impl<System: raw::KernelInterruptLine> InterruptLineDefiner<System> {
const fn new() -> Self {
Self {
_phantom: Init::INIT,
line: None,
priority: None,
enabled: false,
}
}
pub const fn line(self, line: InterruptNum) -> Self {
assert!(self.line.is_none(), "`line` is specified twice");
Self {
line: Some(line),
..self
}
}
pub const fn priority(self, priority: 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<C: ~const raw_cfg::CfgInterruptLine<System = System>>(
self,
cfg: &mut Cfg<C>,
) -> InterruptLine<System> {
let line_num = self.line.expect("`line` is not specified");
let i = if let Some(i) = vec_position!(cfg.interrupt_lines, |il| il.num == line_num) {
i
} else {
cfg.interrupt_lines.push(CfgInterruptLineInfo {
num: line_num,
priority: None,
enabled: false,
});
cfg.interrupt_lines.len() - 1
};
let cfg_interrupt_line = &mut cfg.interrupt_lines[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;
}
InterruptLine::from_num(line_num)
}
}
pub struct InterruptHandlerDefiner<System: raw::KernelInterruptLine> {
_phantom: PhantomInvariant<System>,
line: Option<InterruptNum>,
start: Option<Closure>,
priority: i32,
unmanaged: bool,
}
impl<System: raw::KernelInterruptLine> InterruptHandlerDefiner<System> {
const fn new() -> Self {
Self {
_phantom: Init::INIT,
line: None,
start: None,
priority: 0,
unmanaged: false,
}
}
pub const fn start<C: ~const IntoClosureConst>(self, start: C) -> Self {
Self {
start: Some(start.into_closure_const()),
..self
}
}
pub const fn line(self, line: 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<C: ~const raw_cfg::CfgInterruptLine<System = System>>(
self,
cfg: &mut Cfg<C>,
) -> StaticInterruptHandler<System> {
let line_num = self.line.expect("`line` is not specified");
InterruptLine::define().line(line_num).finish(cfg);
let order = cfg.interrupt_handlers.len();
cfg.interrupt_handlers.push(CfgInterruptHandler {
line: line_num,
start: self.start.expect("`start` is not specified"),
priority: self.priority,
unmanaged: self.unmanaged,
order,
});
StaticInterruptHandler::new()
}
}
#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub(super) struct CfgInterruptLineInfo {
pub(super) num: InterruptNum,
pub(super) priority: Option<InterruptPriority>,
pub(super) enabled: bool,
}
impl CfgInterruptLineInfo {
const fn is_initially_managed<System: raw::KernelInterruptLine>(&self) -> bool {
if let Some(priority) = self.priority {
let range = System::RAW_MANAGED_INTERRUPT_PRIORITY_RANGE;
priority >= range.start && priority < range.end
} else {
false
}
}
}
#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub struct CfgInterruptHandler {
line: InterruptNum,
start: Closure,
priority: i32,
unmanaged: bool,
order: usize,
}
pub(super) const fn panic_if_unmanaged_safety_is_violated<System: raw::KernelInterruptLine>(
interrupt_lines: &ComptimeVec<CfgInterruptLineInfo>,
interrupt_handlers: &ComptimeVec<CfgInterruptHandler>,
) {
let mut i = 0;
while i < interrupt_handlers.len() {
let handler = &interrupt_handlers[i];
i += 1;
if handler.unmanaged {
continue;
}
let is_line_assumed_managed = {
let lines = System::RAW_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<CfgInterruptHandler>) {
slice_sort_unstable_by(
interrupt_handlers.as_mut_slice(),
closure!(|x: &CfgInterruptHandler, y: &CfgInterruptHandler| -> bool {
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();
type ProtoCombinedHandlerFn = fn();
#[doc(hidden)]
pub trait CfgInterruptHandlerList {
type NumHandlers: Nat;
const HANDLERS: &'static [CfgInterruptHandler];
}
struct MakeCombinedHandlers<System, Handlers, const NUM_HANDLERS: usize>(
PhantomInvariant<(System, Handlers)>,
);
trait MakeCombinedHandlersTrait {
type System: raw::KernelBase;
type NumHandlers: Nat;
const HANDLERS: &'static [CfgInterruptHandler];
const NUM_HANDLERS: usize;
const PROTO_COMBINED_HANDLERS: &'static [ProtoCombinedHandlerFn];
const COMBINED_HANDLERS: &'static [Option<InterruptHandlerFn>];
}
impl<System: raw::KernelBase, Handlers: CfgInterruptHandlerList, const NUM_HANDLERS: usize>
MakeCombinedHandlersTrait for MakeCombinedHandlers<System, Handlers, NUM_HANDLERS>
{
type System = System;
type NumHandlers = Handlers::NumHandlers;
const HANDLERS: &'static [CfgInterruptHandler] = 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: raw::KernelBase, Handlers: CfgInterruptHandlerList, const NUM_HANDLERS: usize>
MakeCombinedHandlers<System, Handlers, NUM_HANDLERS>
{
const 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.call();
let next_i = I::N + 1;
if next_i >= T::NUM_HANDLERS || T::HANDLERS[next_i].line != handler.line {
return;
}
use raw::KernelBase;
if T::System::raw_has_cpu_lock() {
let _ = unsafe { T::System::raw_release_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] = {
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: raw::KernelBase,
NumLines: Nat,
Handlers: CfgInterruptHandlerList,
const NUM_LINES: usize,
const NUM_HANDLERS: usize,
>() -> [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;
}
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]>()
}
}
#[doc(hidden)]
pub const fn num_required_interrupt_line_slots(handlers: &[CfgInterruptHandler]) -> 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
}