use alloc::{string::String, sync::Arc};
use axvm_types::{InterruptTriggerMode, IrqLineId};
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum IrqError {
#[error(
"IRQ line {line:?} uses {actual:?} triggering, but {operation} requires {expected:?} \
triggering"
)]
InvalidTriggerMode {
line: IrqLineId,
operation: &'static str,
expected: InterruptTriggerMode,
actual: InterruptTriggerMode,
},
#[error("invalid IRQ line {line:?} during {operation}: {detail}")]
InvalidLine {
line: IrqLineId,
operation: &'static str,
detail: String,
},
#[error("unsupported IRQ operation {operation} on line {line:?}: {detail}")]
Unsupported {
line: IrqLineId,
operation: &'static str,
detail: String,
},
#[error("IRQ backend operation {operation} failed for line {line:?}: {detail}")]
Backend {
line: IrqLineId,
operation: &'static str,
detail: String,
},
}
pub type IrqResult<T = ()> = Result<T, IrqError>;
pub trait IrqSink: Send + Sync {
fn set_level(&self, line: IrqLineId, asserted: bool) -> IrqResult;
fn pulse(&self, line: IrqLineId) -> IrqResult;
}
#[derive(Clone)]
pub struct IrqLine(Arc<IrqLineInner>);
struct IrqLineInner {
id: IrqLineId,
trigger: InterruptTriggerMode,
sink: Arc<dyn IrqSink>,
}
impl IrqLine {
pub fn new(id: IrqLineId, trigger: InterruptTriggerMode, sink: Arc<dyn IrqSink>) -> Self {
Self(Arc::new(IrqLineInner { id, trigger, sink }))
}
pub fn raise(&self) -> IrqResult {
if self.0.trigger != InterruptTriggerMode::LevelTriggered {
return Err(IrqError::InvalidTriggerMode {
line: self.0.id,
operation: "raise",
expected: InterruptTriggerMode::LevelTriggered,
actual: self.0.trigger,
});
}
self.0.sink.set_level(self.0.id, true)
}
pub fn lower(&self) -> IrqResult {
if self.0.trigger != InterruptTriggerMode::LevelTriggered {
return Err(IrqError::InvalidTriggerMode {
line: self.0.id,
operation: "lower",
expected: InterruptTriggerMode::LevelTriggered,
actual: self.0.trigger,
});
}
self.0.sink.set_level(self.0.id, false)
}
pub fn pulse(&self) -> IrqResult {
if self.0.trigger != InterruptTriggerMode::EdgeTriggered {
return Err(IrqError::InvalidTriggerMode {
line: self.0.id,
operation: "pulse",
expected: InterruptTriggerMode::EdgeTriggered,
actual: self.0.trigger,
});
}
self.0.sink.pulse(self.0.id)
}
}