| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- use core::cell::UnsafeCell;
- use core::ptr::NonNull;
- use core::task::{Poll, Waker};
- use eonix_mm::address::{Addr as _, PAddr};
- use eonix_mm::paging::Folio as _;
- use eonix_sync::{Spin, SpinIrq as _};
- use super::command_table::CommandTable;
- use super::CommandHeader;
- use crate::kernel::constants::EIO;
- use crate::kernel::mem::FolioOwned;
- use crate::KResult;
- pub struct CommandList {
- base: NonNull<u8>,
- _page: FolioOwned,
- }
- unsafe impl Send for CommandList {}
- unsafe impl Sync for CommandList {}
- pub struct CommandSlot<'a> {
- cmdheader: &'a UnsafeCell<CommandHeader>,
- /// [`Self::control`] might be used in irq handlers.
- control: &'a Spin<SlotControl>,
- }
- unsafe impl Send for CommandSlot<'_> {}
- unsafe impl Sync for CommandSlot<'_> {}
- struct SlotControl {
- state: SlotState,
- waker: Option<Waker>,
- }
- #[derive(Debug, PartialEq, Eq, Clone, Copy)]
- enum SlotState {
- Idle,
- Working,
- Finished,
- Error,
- }
- impl CommandList {
- fn cmdheaders(&self) -> &[UnsafeCell<CommandHeader>; 32] {
- unsafe { self.base.cast().as_ref() }
- }
- fn controls_ptr(base: NonNull<u8>) -> NonNull<Spin<SlotControl>> {
- // 24 bytes for SlotControl and extra 8 bytes for Spin.
- const_assert_eq!(size_of::<Spin<SlotControl>>(), 32);
- unsafe { base.add(size_of::<UnsafeCell<CommandHeader>>() * 32).cast() }
- }
- fn controls(&self) -> &[Spin<SlotControl>; 32] {
- unsafe { Self::controls_ptr(self.base).cast().as_ref() }
- }
- pub fn cmdlist_base(&self) -> PAddr {
- self._page.start()
- }
- pub fn recv_fis_base(&self) -> PAddr {
- self._page.start()
- + (size_of::<UnsafeCell<CommandHeader>>() + size_of::<Spin<SlotControl>>()) * 32
- }
- pub fn get(&self, index: usize) -> CommandSlot {
- CommandSlot {
- cmdheader: &self.cmdheaders()[index],
- control: &self.controls()[index],
- }
- }
- pub fn new() -> Self {
- let mut page = FolioOwned::alloc();
- page.as_bytes_mut().fill(0);
- let base = page.get_ptr();
- let controls_ptr = Self::controls_ptr(base);
- for i in 0..32 {
- unsafe {
- controls_ptr.add(i).write(Spin::new(SlotControl {
- state: SlotState::Idle,
- waker: None,
- }));
- }
- }
- Self {
- base: page.get_ptr(),
- _page: page,
- }
- }
- }
- impl Drop for CommandList {
- fn drop(&mut self) {
- let controls_ptr = Self::controls_ptr(self.base);
- for i in 0..32 {
- unsafe {
- controls_ptr.add(i).drop_in_place();
- }
- }
- }
- }
- impl CommandSlot<'_> {
- pub fn handle_irq(&self) {
- // We are already in the IRQ handler.
- let mut control = self.control.lock();
- assert_eq!(control.state, SlotState::Working);
- let cmdheader = unsafe {
- // SAFETY: The IRQ handler is only called after the command
- // is finished.
- &mut *self.cmdheader.get()
- };
- // TODO: Check errors.
- cmdheader.bytes_transferred = 0;
- cmdheader.prdt_length = 0;
- control.state = SlotState::Finished;
- if let Some(waker) = control.waker.take() {
- waker.wake();
- }
- }
- pub fn prepare_command(&self, cmdtable: &CommandTable, write: bool) {
- let mut control = self.control.lock_irq();
- assert_eq!(control.state, SlotState::Idle);
- let cmdheader = unsafe {
- // SAFETY: We are in the idle state.
- &mut *self.cmdheader.get()
- };
- cmdheader.first = 0x05; // FIS type
- if write {
- cmdheader.first |= 0x40;
- }
- cmdheader.second = 0x00;
- cmdheader.prdt_length = cmdtable.prdt_len() as u16;
- cmdheader.bytes_transferred = 0;
- cmdheader.command_table_base = cmdtable.base().addr() as u64;
- cmdheader._reserved = [0; 4];
- control.state = SlotState::Working;
- }
- pub async fn wait_finish(&self) -> KResult<()> {
- core::future::poll_fn(|ctx| {
- let mut control = self.control.lock_irq();
- match control.state {
- SlotState::Idle => unreachable!("Poll called in idle state"),
- SlotState::Working => {
- control.waker = Some(ctx.waker().clone());
- Poll::Pending
- }
- SlotState::Finished => {
- control.state = SlotState::Idle;
- Poll::Ready(Ok(()))
- }
- SlotState::Error => {
- control.state = SlotState::Idle;
- // TODO: Report errors.
- Poll::Ready(Err(EIO))
- }
- }
- })
- .await
- }
- }
|