use anyhow::{anyhow, Context};
use tokio::sync::mpsc;
use tokio::{runtime, select};
use tracing::{debug, info};
use crate::device::bus::BusDeviceRef;
use crate::device::interrupt_line::{DummyInterruptLine, InterruptLine};
use crate::device::pci::constants::xhci::runtime::IMOD_DEFAULT;
use crate::device::xhci::event_ring::EventRing;
use crate::device::xhci::registers::{ErstbaRegister, GenericRwRegister};
use crate::device::xhci::trb::EventTrb;
use std::sync::Arc;
#[derive(Debug)]
pub struct Interrupter {
pub registers: InterrupterRegisters,
msg_sender: mpsc::UnboundedSender<InterrupterMessage>,
}
#[derive(Debug, Clone)]
pub struct InterrupterRegisters {
pub interrupt_management: GenericRwRegister,
pub interrupt_moderation_interval: GenericRwRegister,
pub erst_base_address: ErstbaRegister,
pub erst_size: GenericRwRegister,
pub eventring_dequeue_pointer: GenericRwRegister,
}
impl Default for InterrupterRegisters {
fn default() -> Self {
Self {
interrupt_management: Default::default(),
interrupt_moderation_interval: GenericRwRegister::new(IMOD_DEFAULT),
erst_base_address: Default::default(),
erst_size: Default::default(),
eventring_dequeue_pointer: Default::default(),
}
}
}
#[derive(Debug)]
struct EventWorker {
registers: InterrupterRegisters,
msg_recv: mpsc::UnboundedReceiver<InterrupterMessage>,
interrupt_line: Arc<dyn InterruptLine>,
event_ring: EventRing,
}
#[derive(Debug)]
enum InterrupterMessage {
SendEvent(EventTrb),
UpdateInterruptLine(Arc<dyn InterruptLine>),
}
#[derive(Debug, Clone)]
pub struct EventSender {
sender: mpsc::UnboundedSender<InterrupterMessage>,
}
impl EventSender {
pub fn send(&self, event: EventTrb) -> anyhow::Result<()> {
let msg = InterrupterMessage::SendEvent(event);
self.sender.send(msg).context("event channel closed")?;
Ok(())
}
}
impl Interrupter {
pub fn new(
dma_bus: BusDeviceRef,
async_runtime: &runtime::Handle,
) -> Self {
let (msg_sender, msg_recv) = mpsc::unbounded_channel();
let registers = InterrupterRegisters::default();
let interrupter = Self {
registers: registers.clone(),
msg_sender,
};
let event_ring = EventRing::new(dma_bus);
let worker = EventWorker {
registers,
msg_recv,
interrupt_line: Arc::new(DummyInterruptLine::default()),
event_ring,
};
async_runtime.spawn(worker.run());
interrupter
}
pub fn set_interrupt_line(&self, interrupt_line: Arc<dyn InterruptLine>) -> anyhow::Result<()> {
let msg = InterrupterMessage::UpdateInterruptLine(interrupt_line);
self.msg_sender.send(msg)?;
Ok(())
}
pub fn create_event_sender(&self) -> EventSender {
EventSender {
sender: self.msg_sender.clone(),
}
}
}
impl EventWorker {
async fn next_msg(&mut self) -> anyhow::Result<InterrupterMessage> {
self.msg_recv
.recv()
.await
.ok_or_else(|| anyhow!("event channel closed"))
}
async fn run(mut self) {
match self.run_loop().await {
Ok(_) => unreachable!(),
Err(err) => {
info!("EventWorker stopped {err}");
}
}
}
async fn run_loop(&mut self) -> anyhow::Result<()> {
loop {
select! {
_ = self.registers.erst_base_address.write_notification() => break,
msg = self.msg_recv.recv() => match msg.ok_or_else(|| anyhow!("event channel closed"))? {
InterrupterMessage::SendEvent(_) => {}
InterrupterMessage::UpdateInterruptLine(interrupt_line) => self.interrupt_line = interrupt_line,
},
}
}
self.event_ring.configure(
self.registers.erst_base_address.erstba(),
self.registers.erst_size.read() as u32,
);
loop {
match self.next_msg().await? {
InterrupterMessage::SendEvent(event_trb) => {
self.event_ring.enqueue(
&event_trb,
self.registers.erst_base_address.erstba(),
self.registers.erst_size.read() as u32,
self.registers.eventring_dequeue_pointer.read(),
);
self.interrupt_line.interrupt();
debug!("Sent event: {event_trb:?}");
}
InterrupterMessage::UpdateInterruptLine(interrupt_line) => {
self.interrupt_line = interrupt_line;
debug!("Updated interrupt line");
}
}
}
}
}