crab-usb 0.11.0

A usb host for embedded systems, written in Rust.
Documentation
use alloc::vec::Vec;

use dma_api::{CoherentArray, DmaDirection};
use mbarrier::mb;
use xhci::ring::trb::event::Allowed;

use super::ring::{Ring, TRBS_PER_SEGMENT};
use crate::{err::*, osal::Kernel};

#[repr(C)]
#[derive(Clone, Copy)]
struct EventRingSte {
    addr: u64,
    size: u16,
    _reserved: [u8; 6],
}

pub(crate) struct EventRing {
    segments: Vec<Ring>,
    segment_index: usize,
    trb_index: usize,
    cycle: bool,
    ste: CoherentArray<EventRingSte>,
}

const EVENT_RING_SEGMENTS: usize = 2;

impl EventRing {
    pub fn new(max_segments: usize, dma: &Kernel) -> Result<Self> {
        let segment_count = EVENT_RING_SEGMENTS.min(max_segments.max(1));
        let mut segments = Vec::with_capacity(segment_count);
        for _ in 0..segment_count {
            segments.push(Ring::new_segment(false, DmaDirection::Bidirectional, dma)?);
        }

        let mut ste = dma
            .coherent_array_zero_with_align(segment_count, 64)
            .map_err(|_| USBError::NoMemory)?;

        for (index, segment) in segments.iter().enumerate() {
            ste.set_cpu(
                index,
                EventRingSte {
                    addr: segment.bus_addr().raw(),
                    size: TRBS_PER_SEGMENT as _,
                    _reserved: [0; 6],
                },
            );
        }

        Ok(Self {
            segments,
            segment_index: 0,
            trb_index: 0,
            cycle: true,
            ste,
        })
    }

    pub fn next(&mut self) -> Option<Allowed> {
        let data = self.current_data();

        let allowed = Allowed::try_from(data.to_raw()).ok()?;

        if self.cycle != allowed.cycle_bit() {
            return None;
        }
        mb();
        self.inc_deque();
        Some(allowed)
    }

    pub fn has_pending_event(&mut self) -> bool {
        let data = self.current_data();
        let Ok(allowed) = Allowed::try_from(data.to_raw()) else {
            return false;
        };
        self.cycle == allowed.cycle_bit()
    }

    pub fn erdp(&self) -> u64 {
        self.current_segment().trb_bus_addr(self.trb_index).raw() & 0xFFFF_FFFF_FFFF_FFF0
    }

    pub fn erst_dequeue_pointer(&self) -> u64 {
        self.erdp() | (self.segment_index as u64 & 0x7)
    }

    pub fn segment_index(&self) -> u8 {
        (self.segment_index & 0x7) as u8
    }

    pub fn erstba(&self) -> u64 {
        self.ste.dma_addr().as_u64()
    }

    pub fn len(&self) -> usize {
        self.ste.len()
    }

    pub fn info(&self) -> EventRingInfo {
        EventRingInfo {
            erstz: self.len() as _,
            erdp: self.erst_dequeue_pointer(),
            erstba: self.erstba(),
        }
    }

    fn current_segment(&self) -> &Ring {
        &self.segments[self.segment_index]
    }

    fn current_data(&self) -> super::ring::TrbData {
        self.current_segment()
            .read_trb(self.trb_index)
            .expect("event ring TRB index out of bounds")
    }

    fn inc_deque(&mut self) {
        self.trb_index += 1;
        if self.trb_index >= TRBS_PER_SEGMENT {
            self.trb_index = 0;
            self.segment_index += 1;
            if self.segment_index >= self.segments.len() {
                self.segment_index = 0;
                self.cycle = !self.cycle;
            }
        }
    }
}

pub(crate) struct EventRingInfo {
    pub(crate) erstz: u16,
    pub(crate) erdp: u64,
    pub(crate) erstba: u64,
}