use super::{command_table::CommandTable, CommandHeader}; use crate::KResult; use core::pin::pin; use eonix_mm::address::Addr as _; use eonix_sync::{Spin, SpinIrq as _, WaitList}; pub struct CommandSlot<'a> { /// # Usage /// `inner.cmdheader` might be used in irq handler. So in order to wait for /// commands to finish, we should use `lock_irq` on `inner` inner: Spin>, wait_list: WaitList, } struct CommandSlotInner<'a> { state: SlotState, cmdheader: &'a mut CommandHeader, } #[derive(Debug, PartialEq, Eq, Clone, Copy)] enum SlotState { Idle, Working, Finished, Error, } impl<'a> CommandSlot<'a> { pub fn new(cmdheader: &'a mut CommandHeader) -> Self { Self { inner: Spin::new(CommandSlotInner { state: SlotState::Idle, cmdheader, }), wait_list: WaitList::new(), } } pub fn handle_irq(&self) { let mut inner = self.inner.lock(); debug_assert_eq!(inner.state, SlotState::Working); // TODO: Check errors. inner.state = SlotState::Finished; inner.cmdheader.bytes_transferred = 0; inner.cmdheader.prdt_length = 0; self.wait_list.notify_all(); } pub fn prepare_command(&self, cmdtable: &CommandTable, write: bool) { let mut inner = self.inner.lock_irq(); let cmdheader = &mut inner.cmdheader; cmdheader.first = 0x05; // FIS type if write { cmdheader.first |= 0x40; } cmdheader.second = 0x00; cmdheader.prdt_length = cmdtable.prdt_len(); cmdheader.bytes_transferred = 0; cmdheader.command_table_base = cmdtable.base().addr() as u64; cmdheader._reserved = [0; 4]; inner.state = SlotState::Working; } pub async fn wait_finish(&self) -> KResult<()> { let mut inner = loop { let inner = self.inner.lock_irq(); if inner.state != SlotState::Working { break inner; } let mut wait = pin!(self.wait_list.prepare_to_wait()); wait.as_mut().add_to_wait_list(); if inner.state != SlotState::Working { break inner; } drop(inner); wait.await; }; inner.state = SlotState::Idle; Ok(()) } }