Merge branch 'xhci-csz' into 'master'
xhci: support 64-bit contexts (CSZ) See merge request redox-os/drivers!283
This commit is contained in:
+20
-3
@@ -22,6 +22,9 @@
|
||||
//! - USB2 - [Universal Serial Bus Specification](https://www.usb.org/document-library/usb-20-specification)
|
||||
//! - USB32 - [Universal Serial Bus 3.2 Specification Revision 1.1](https://usb.org/document-library/usb-32-revision-11-june-2022)
|
||||
//!
|
||||
#![allow(warnings)]
|
||||
#![feature(generic_const_exprs)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
@@ -117,8 +120,11 @@ fn main() {
|
||||
redox_daemon::Daemon::new(daemon).expect("xhcid: failed to daemonize");
|
||||
}
|
||||
|
||||
fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
let mut pcid_handle = PciFunctionHandle::connect_default();
|
||||
//TODO: cleanup CSZ support
|
||||
fn daemon_with_context_size<const N: usize>(
|
||||
daemon: redox_daemon::Daemon,
|
||||
mut pcid_handle: PciFunctionHandle,
|
||||
) -> ! {
|
||||
let pci_config = pcid_handle.config();
|
||||
|
||||
let mut name = pci_config.func.name();
|
||||
@@ -147,7 +153,7 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
daemon.ready().expect("xhcid: failed to notify parent");
|
||||
|
||||
let hci = Arc::new(
|
||||
Xhci::new(scheme_name, address, interrupt_method, pcid_handle)
|
||||
Xhci::<N>::new(scheme_name, address, interrupt_method, pcid_handle)
|
||||
.expect("xhcid: failed to allocate device"),
|
||||
);
|
||||
|
||||
@@ -179,3 +185,14 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
let mut pcid_handle = PciFunctionHandle::connect_default();
|
||||
let address = unsafe { pcid_handle.map_bar(0) }.ptr.as_ptr() as usize;
|
||||
let cap = unsafe { &mut *(address as *mut xhci::CapabilityRegs) };
|
||||
if cap.csz() {
|
||||
daemon_with_context_size::<{ xhci::CONTEXT_64 }>(daemon, pcid_handle)
|
||||
} else {
|
||||
daemon_with_context_size::<{ xhci::CONTEXT_32 }>(daemon, pcid_handle)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +120,10 @@ pub struct CapabilityRegs {
|
||||
pub const HCC_PARAMS1_AC64_BIT: u32 = 1 << HCC_PARAMS1_AC64_SHIFT;
|
||||
/// The shift to use to get the AC64 bit from HCCParams1. See [CapabilityRegs]
|
||||
pub const HCC_PARAMS1_AC64_SHIFT: u8 = 0;
|
||||
/// The mask to use to get the CSZ bit from HCCPARAMS1. See [CapabilityRegs]
|
||||
pub const HCC_PARAMS1_CSZ_BIT: u32 = 1 << HCC_PARAMS1_CSZ_SHIFT;
|
||||
/// The shift to use to get the CSZ bit from HCCParams1. See [CapabilityRegs]
|
||||
pub const HCC_PARAMS1_CSZ_SHIFT: u8 = 2;
|
||||
/// The Mask to use to get the MAXPSASIZE value from HCCParams1. See [CapabilityRegs]
|
||||
pub const HCC_PARAMS1_MAXPSASIZE_MASK: u32 = 0xF000; // 15:12
|
||||
/// The shift to use to get the MAXPSASIZE value from HCCParams1. See [CapabilityRegs]
|
||||
@@ -161,6 +165,11 @@ impl CapabilityRegs {
|
||||
self.hcc_params1.readf(HCC_PARAMS1_AC64_BIT)
|
||||
}
|
||||
|
||||
/// Gets the context size (CSZ) bit from HCCParams1.
|
||||
pub fn csz(&self) -> bool {
|
||||
self.hcc_params1.readf(HCC_PARAMS1_CSZ_BIT)
|
||||
}
|
||||
|
||||
/// Gets the LEC bit from HCCParams2.
|
||||
pub fn lec(&self) -> bool {
|
||||
self.hcc_params2.readf(HCC_PARAMS2_LEC_BIT)
|
||||
|
||||
+49
-26
@@ -10,24 +10,25 @@ use common::dma::Dma;
|
||||
use super::ring::Ring;
|
||||
use super::Xhci;
|
||||
|
||||
pub const CONTEXT_32: usize = 0;
|
||||
pub const CONTEXT_64: usize = 1;
|
||||
|
||||
#[repr(C, packed)]
|
||||
pub struct SlotContext {
|
||||
struct Rsvd64<const N: usize>([[Mmio<u32>; 8]; N]);
|
||||
|
||||
#[repr(C, packed)]
|
||||
pub struct SlotContext<const N: usize> {
|
||||
pub a: Mmio<u32>,
|
||||
pub b: Mmio<u32>,
|
||||
pub c: Mmio<u32>,
|
||||
pub d: Mmio<u32>,
|
||||
_rsvd: [Mmio<u32>; 4],
|
||||
_rsvd64: Rsvd64<N>,
|
||||
}
|
||||
|
||||
pub const SLOT_CONTEXT_STATE_MASK: u32 = 0xF800_0000;
|
||||
pub const SLOT_CONTEXT_STATE_SHIFT: u8 = 27;
|
||||
|
||||
impl SlotContext {
|
||||
pub fn state(&self) -> u8 {
|
||||
((self.d.read() & SLOT_CONTEXT_STATE_MASK) >> SLOT_CONTEXT_STATE_SHIFT) as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum SlotState {
|
||||
EnabledOrDisabled = 0,
|
||||
@@ -37,32 +38,34 @@ pub enum SlotState {
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
pub struct EndpointContext {
|
||||
pub struct EndpointContext<const N: usize> {
|
||||
pub a: Mmio<u32>,
|
||||
pub b: Mmio<u32>,
|
||||
pub trl: Mmio<u32>,
|
||||
pub trh: Mmio<u32>,
|
||||
pub c: Mmio<u32>,
|
||||
_rsvd: [Mmio<u32>; 3],
|
||||
_rsvd64: Rsvd64<N>,
|
||||
}
|
||||
|
||||
pub const ENDPOINT_CONTEXT_STATUS_MASK: u32 = 0x7;
|
||||
|
||||
#[repr(C, packed)]
|
||||
pub struct DeviceContext {
|
||||
pub slot: SlotContext,
|
||||
pub endpoints: [EndpointContext; 31],
|
||||
pub struct DeviceContext<const N: usize> {
|
||||
pub slot: SlotContext<N>,
|
||||
pub endpoints: [EndpointContext<N>; 31],
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
pub struct InputContext {
|
||||
pub struct InputContext<const N: usize> {
|
||||
pub drop_context: Mmio<u32>,
|
||||
pub add_context: Mmio<u32>,
|
||||
_rsvd: [Mmio<u32>; 5],
|
||||
pub control: Mmio<u32>,
|
||||
pub device: DeviceContext,
|
||||
_rsvd64: Rsvd64<N>,
|
||||
pub device: DeviceContext<N>,
|
||||
}
|
||||
impl InputContext {
|
||||
impl<const N: usize> InputContext<N> {
|
||||
pub fn dump_control(&self) {
|
||||
debug!(
|
||||
"INPUT CONTEXT: {} {} [{} {} {} {} {}] {}",
|
||||
@@ -78,19 +81,19 @@ impl InputContext {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DeviceContextList {
|
||||
pub struct DeviceContextList<const N: usize> {
|
||||
pub dcbaa: Dma<[u64; 256]>,
|
||||
pub contexts: Box<[Dma<DeviceContext>]>,
|
||||
pub contexts: Box<[Dma<DeviceContext<N>>]>,
|
||||
}
|
||||
|
||||
impl DeviceContextList {
|
||||
pub fn new(ac64: bool, max_slots: u8) -> Result<DeviceContextList> {
|
||||
let mut dcbaa = unsafe { Xhci::alloc_dma_zeroed_raw::<[u64; 256]>(ac64)? };
|
||||
impl<const N: usize> DeviceContextList<N> {
|
||||
pub fn new(ac64: bool, max_slots: u8) -> Result<Self> {
|
||||
let mut dcbaa = unsafe { Xhci::<N>::alloc_dma_zeroed_raw::<[u64; 256]>(ac64)? };
|
||||
let mut contexts = vec![];
|
||||
|
||||
// Create device context buffers for each slot
|
||||
for i in 0..max_slots as usize {
|
||||
let context: Dma<DeviceContext> = unsafe { Xhci::alloc_dma_zeroed_raw(ac64) }?;
|
||||
let context: Dma<DeviceContext<N>> = unsafe { Xhci::<N>::alloc_dma_zeroed_raw(ac64) }?;
|
||||
dcbaa[i] = context.physical() as u64;
|
||||
contexts.push(context);
|
||||
}
|
||||
@@ -134,19 +137,24 @@ pub struct StreamContextArray {
|
||||
}
|
||||
|
||||
impl StreamContextArray {
|
||||
pub fn new(ac64: bool, count: usize) -> Result<Self> {
|
||||
pub fn new<const N: usize>(ac64: bool, count: usize) -> Result<Self> {
|
||||
unsafe {
|
||||
Ok(Self {
|
||||
contexts: Xhci::alloc_dma_zeroed_unsized_raw(ac64, count)?,
|
||||
contexts: Xhci::<N>::alloc_dma_zeroed_unsized_raw(ac64, count)?,
|
||||
rings: BTreeMap::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn add_ring(&mut self, ac64: bool, stream_id: u16, link: bool) -> Result<()> {
|
||||
pub fn add_ring<const N: usize>(
|
||||
&mut self,
|
||||
ac64: bool,
|
||||
stream_id: u16,
|
||||
link: bool,
|
||||
) -> Result<()> {
|
||||
// NOTE: stream_id 0 is reserved
|
||||
assert_ne!(stream_id, 0);
|
||||
|
||||
let ring = Ring::new(ac64, 16, link)?;
|
||||
let ring = Ring::new::<N>(ac64, 16, link)?;
|
||||
let pointer = ring.register();
|
||||
let sct = StreamContextType::PrimaryRing;
|
||||
|
||||
@@ -182,8 +190,9 @@ pub struct ScratchpadBufferArray {
|
||||
pub pages: Vec<Dma<[u8; PAGE_SIZE]>>,
|
||||
}
|
||||
impl ScratchpadBufferArray {
|
||||
pub fn new(ac64: bool, entries: u16) -> Result<Self> {
|
||||
let mut entries = unsafe { Xhci::alloc_dma_zeroed_unsized_raw(ac64, entries as usize)? };
|
||||
pub fn new<const N: usize>(ac64: bool, entries: u16) -> Result<Self> {
|
||||
let mut entries =
|
||||
unsafe { Xhci::<N>::alloc_dma_zeroed_unsized_raw(ac64, entries as usize)? };
|
||||
|
||||
let pages = entries
|
||||
.iter_mut()
|
||||
@@ -203,3 +212,17 @@ impl ScratchpadBufferArray {
|
||||
self.entries.physical()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use core::mem;
|
||||
|
||||
#[test]
|
||||
fn context_size() {
|
||||
assert_eq!(mem::size_of::<SlotContext<CONTEXT_32>>(), 32);
|
||||
assert_eq!(mem::size_of::<SlotContext<CONTEXT_64>>(), 64);
|
||||
assert_eq!(mem::size_of::<EndpointContext<CONTEXT_32>>(), 32);
|
||||
assert_eq!(mem::size_of::<EndpointContext<CONTEXT_64>>(), 64);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ pub struct DeviceEnumerationRequest {
|
||||
pub port_id: PortId,
|
||||
}
|
||||
|
||||
pub struct DeviceEnumerator {
|
||||
hci: Arc<Xhci>,
|
||||
pub struct DeviceEnumerator<const N: usize> {
|
||||
hci: Arc<Xhci<N>>,
|
||||
request_queue: crossbeam_channel::Receiver<DeviceEnumerationRequest>,
|
||||
}
|
||||
|
||||
impl DeviceEnumerator {
|
||||
pub fn new(hci: Arc<Xhci>) -> Self {
|
||||
impl<const N: usize> DeviceEnumerator<N> {
|
||||
pub fn new(hci: Arc<Xhci<N>>) -> Self {
|
||||
let request_queue = hci.device_enumerator_receiver.clone();
|
||||
DeviceEnumerator { hci, request_queue }
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@ pub struct EventRing {
|
||||
}
|
||||
|
||||
impl EventRing {
|
||||
pub fn new(ac64: bool) -> Result<EventRing> {
|
||||
pub fn new<const N: usize>(ac64: bool) -> Result<EventRing> {
|
||||
let mut ring = EventRing {
|
||||
ste: unsafe { Xhci::alloc_dma_zeroed_unsized_raw(ac64, 1)? },
|
||||
ring: Ring::new(ac64, 256, false)?,
|
||||
ste: unsafe { Xhci::<N>::alloc_dma_zeroed_unsized_raw(ac64, 1)? },
|
||||
ring: Ring::new::<N>(ac64, 256, false)?,
|
||||
};
|
||||
|
||||
ring.ste[0]
|
||||
|
||||
@@ -92,8 +92,8 @@ impl StateKind {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IrqReactor {
|
||||
hci: Arc<Xhci>,
|
||||
pub struct IrqReactor<const N: usize> {
|
||||
hci: Arc<Xhci<N>>,
|
||||
irq_file: Option<File>,
|
||||
irq_receiver: Receiver<NewPendingTrb>,
|
||||
device_enumerator_sender: Sender<DeviceEnumerationRequest>,
|
||||
@@ -104,8 +104,8 @@ pub struct IrqReactor {
|
||||
|
||||
pub type NewPendingTrb = State;
|
||||
|
||||
impl IrqReactor {
|
||||
pub fn new(hci: Arc<Xhci>, irq_file: Option<File>) -> Self {
|
||||
impl<const N: usize> IrqReactor<N> {
|
||||
pub fn new(hci: Arc<Xhci<N>>, irq_file: Option<File>) -> Self {
|
||||
let device_enumerator_sender = hci.device_enumerator_sender.clone();
|
||||
let irq_receiver = hci.irq_reactor_receiver.clone();
|
||||
|
||||
@@ -559,7 +559,7 @@ pub struct EventDoorbell {
|
||||
}
|
||||
|
||||
impl EventDoorbell {
|
||||
pub fn new(hci: &Xhci, index: usize, data: u32) -> Self {
|
||||
pub fn new<const N: usize>(hci: &Xhci<N>, index: usize, data: u32) -> Self {
|
||||
Self {
|
||||
//TODO: simplify this logic, maybe just use a raw pointer?
|
||||
dbs: hci.dbs.clone(),
|
||||
@@ -625,7 +625,7 @@ impl Future for EventTrbFuture {
|
||||
}
|
||||
}
|
||||
|
||||
impl Xhci {
|
||||
impl<const N: usize> Xhci<N> {
|
||||
pub fn get_transfer_trb(&self, paddr: u64, id: RingId) -> Option<Trb> {
|
||||
self.with_ring(id, |ring| ring.phys_addr_to_entry(self.cap.ac64(), paddr))
|
||||
.flatten()
|
||||
|
||||
+64
-50
@@ -43,8 +43,12 @@ mod runtime;
|
||||
pub mod scheme;
|
||||
mod trb;
|
||||
|
||||
use self::capability::CapabilityRegs;
|
||||
use self::context::{DeviceContextList, InputContext, ScratchpadBufferArray, StreamContextArray};
|
||||
pub use self::capability::CapabilityRegs;
|
||||
use self::context::{
|
||||
DeviceContextList, InputContext, ScratchpadBufferArray, StreamContextArray,
|
||||
SLOT_CONTEXT_STATE_MASK, SLOT_CONTEXT_STATE_SHIFT,
|
||||
};
|
||||
pub use self::context::{CONTEXT_32, CONTEXT_64};
|
||||
use self::doorbell::Doorbell;
|
||||
use self::event::EventRing;
|
||||
use self::extended::{CapabilityId, ExtendedCapabilitiesIter, ProtocolSpeed, SupportedProtoCap};
|
||||
@@ -73,7 +77,7 @@ pub enum InterruptMethod {
|
||||
Msi,
|
||||
}
|
||||
|
||||
impl Xhci {
|
||||
impl<const N: usize> Xhci<N> {
|
||||
/// Gets descriptors, before the port state is initiated.
|
||||
async fn get_desc_raw<T>(
|
||||
&self,
|
||||
@@ -246,7 +250,7 @@ impl Xhci {
|
||||
}
|
||||
|
||||
/// The eXtensible Host Controller Interface (XHCI) data structure
|
||||
pub struct Xhci {
|
||||
pub struct Xhci<const N: usize> {
|
||||
// immutable
|
||||
/// The Host Controller Interface Capability Registers. These read-only registers specify the
|
||||
/// limits and capabilities of the host controller implementation (See XHCI section 5.3)
|
||||
@@ -270,7 +274,7 @@ pub struct Xhci {
|
||||
primary_event_ring: Mutex<EventRing>,
|
||||
|
||||
// immutable
|
||||
dev_ctx: DeviceContextList,
|
||||
dev_ctx: DeviceContextList<N>,
|
||||
scratchpad_buf_arr: Option<ScratchpadBufferArray>,
|
||||
|
||||
// used for the extended capabilities, and so far none of them are mutated, and thus no lock.
|
||||
@@ -278,7 +282,7 @@ pub struct Xhci {
|
||||
|
||||
handles: CHashMap<usize, scheme::Handle>,
|
||||
next_handle: AtomicUsize,
|
||||
port_states: CHashMap<PortId, PortState>,
|
||||
port_states: CHashMap<PortId, PortState<N>>,
|
||||
drivers: CHashMap<PortId, Vec<process::Child>>,
|
||||
scheme_name: String,
|
||||
|
||||
@@ -297,19 +301,19 @@ pub struct Xhci {
|
||||
device_enumerator_receiver: Receiver<DeviceEnumerationRequest>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Xhci {}
|
||||
unsafe impl Sync for Xhci {}
|
||||
unsafe impl<const N: usize> Send for Xhci<N> {}
|
||||
unsafe impl<const N: usize> Sync for Xhci<N> {}
|
||||
|
||||
struct PortState {
|
||||
struct PortState<const N: usize> {
|
||||
slot: u8,
|
||||
protocol_speed: &'static ProtocolSpeed,
|
||||
cfg_idx: Option<u8>,
|
||||
input_context: Mutex<Dma<InputContext>>,
|
||||
input_context: Mutex<Dma<InputContext<N>>>,
|
||||
dev_desc: Option<DevDesc>,
|
||||
endpoint_states: BTreeMap<u8, EndpointState>,
|
||||
}
|
||||
|
||||
impl PortState {
|
||||
impl<const N: usize> PortState<N> {
|
||||
//TODO: fetch using endpoint number instead
|
||||
fn get_endp_desc(&self, endp_idx: u8) -> Option<&EndpDesc> {
|
||||
let cfg_idx = self.cfg_idx?;
|
||||
@@ -350,13 +354,13 @@ impl EndpointState {
|
||||
}
|
||||
}
|
||||
|
||||
impl Xhci {
|
||||
impl<const N: usize> Xhci<N> {
|
||||
pub fn new(
|
||||
scheme_name: String,
|
||||
address: usize,
|
||||
interrupt_method: InterruptMethod,
|
||||
pcid_handle: PciFunctionHandle,
|
||||
) -> Result<Xhci> {
|
||||
) -> Result<Self> {
|
||||
//Locate the capability registers from the mapped PCI Bar
|
||||
let cap = unsafe { &mut *(address as *mut CapabilityRegs) };
|
||||
debug!("CAP REGS BASE {:X}", address);
|
||||
@@ -419,7 +423,7 @@ impl Xhci {
|
||||
// Create the command ring with 4096 / 16 (TRB size) entries, so that it uses all of the
|
||||
// DMA allocation (which is at least a 4k page).
|
||||
let entries_per_page = PAGE_SIZE / mem::size_of::<Trb>();
|
||||
let cmd = Ring::new(cap.ac64(), entries_per_page, true)?;
|
||||
let cmd = Ring::new::<N>(cap.ac64(), entries_per_page, true)?;
|
||||
|
||||
let (irq_reactor_sender, irq_reactor_receiver) = crossbeam_channel::unbounded();
|
||||
|
||||
@@ -439,7 +443,7 @@ impl Xhci {
|
||||
scratchpad_buf_arr: None, // initialized in init()
|
||||
|
||||
cmd: Mutex::new(cmd),
|
||||
primary_event_ring: Mutex::new(EventRing::new(cap.ac64())?),
|
||||
primary_event_ring: Mutex::new(EventRing::new::<N>(cap.ac64())?),
|
||||
handles: CHashMap::new(),
|
||||
next_handle: AtomicUsize::new(0),
|
||||
port_states: CHashMap::new(),
|
||||
@@ -469,7 +473,11 @@ impl Xhci {
|
||||
|
||||
// Warm reset
|
||||
debug!("Reset xHC");
|
||||
self.op.get_mut().unwrap().usb_cmd.writef(USB_CMD_HCRST, true);
|
||||
self.op
|
||||
.get_mut()
|
||||
.unwrap()
|
||||
.usb_cmd
|
||||
.writef(USB_CMD_HCRST, true);
|
||||
while self.op.get_mut().unwrap().usb_cmd.readf(USB_CMD_HCRST) {
|
||||
thread::yield_now();
|
||||
}
|
||||
@@ -531,7 +539,11 @@ impl Xhci {
|
||||
debug!("Enabling Primary Interrupter.");
|
||||
int.iman.writef(1 << 1 | 1, true);
|
||||
}
|
||||
self.op.get_mut().unwrap().usb_cmd.writef(USB_CMD_INTE, true);
|
||||
self.op
|
||||
.get_mut()
|
||||
.unwrap()
|
||||
.usb_cmd
|
||||
.writef(USB_CMD_INTE, true);
|
||||
|
||||
// Setup the scratchpad buffers that are required for the xHC to function.
|
||||
self.setup_scratchpads()?;
|
||||
@@ -674,7 +686,7 @@ impl Xhci {
|
||||
if buf_count == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let scratchpad_buf_arr = ScratchpadBufferArray::new(self.cap.ac64(), buf_count)?;
|
||||
let scratchpad_buf_arr = ScratchpadBufferArray::new::<N>(self.cap.ac64(), buf_count)?;
|
||||
self.dev_ctx.dcbaa[0] = scratchpad_buf_arr.register() as u64;
|
||||
debug!(
|
||||
"Setting up {} scratchpads, at {:#0x}",
|
||||
@@ -733,7 +745,8 @@ impl Xhci {
|
||||
}
|
||||
|
||||
pub fn slot_state(&self, slot: usize) -> u8 {
|
||||
self.dev_ctx.contexts[slot].slot.state()
|
||||
((self.dev_ctx.contexts[slot].slot.d.read() & SLOT_CONTEXT_STATE_MASK)
|
||||
>> SLOT_CONTEXT_STATE_SHIFT) as u8
|
||||
}
|
||||
pub unsafe fn alloc_dma_zeroed_raw<T>(_ac64: bool) -> Result<Dma<T>> {
|
||||
// TODO: ac64
|
||||
@@ -792,7 +805,7 @@ impl Xhci {
|
||||
.lookup_psiv(port_id, speed)
|
||||
.expect("Failed to retrieve speed ID");
|
||||
|
||||
let mut input = unsafe { self.alloc_dma_zeroed::<InputContext>()? };
|
||||
let mut input = unsafe { self.alloc_dma_zeroed::<InputContext<N>>()? };
|
||||
|
||||
info!("Attempting to address the device");
|
||||
let mut ring = match self
|
||||
@@ -940,7 +953,7 @@ impl Xhci {
|
||||
|
||||
pub async fn update_max_packet_size(
|
||||
&self,
|
||||
input_context: &mut Dma<InputContext>,
|
||||
input_context: &mut Dma<InputContext<N>>,
|
||||
slot_id: u8,
|
||||
dev_desc: usb::DeviceDescriptor8Byte,
|
||||
) -> Result<()> {
|
||||
@@ -951,11 +964,10 @@ impl Xhci {
|
||||
// For later USB versions, packet_size is the shift
|
||||
1u32 << dev_desc.packet_size
|
||||
};
|
||||
let endp_ctx = &mut input_context.device.endpoints[0];
|
||||
let mut b = endp_ctx.b.read();
|
||||
let mut b = input_context.device.endpoints[0].b.read();
|
||||
b &= 0x0000_FFFF;
|
||||
b |= (new_max_packet_size) << 16;
|
||||
endp_ctx.b.write(b);
|
||||
input_context.device.endpoints[0].b.write(b);
|
||||
|
||||
let (event_trb, command_trb) = self
|
||||
.execute_command(|trb, cycle| {
|
||||
@@ -971,7 +983,7 @@ impl Xhci {
|
||||
|
||||
pub async fn update_default_control_pipe(
|
||||
&self,
|
||||
input_context: &mut Dma<InputContext>,
|
||||
input_context: &mut Dma<InputContext<N>>,
|
||||
slot_id: u8,
|
||||
dev_desc: &DevDesc,
|
||||
) -> Result<()> {
|
||||
@@ -986,11 +998,10 @@ impl Xhci {
|
||||
// For later USB versions, packet_size is the shift
|
||||
1u32 << dev_desc.packet_size
|
||||
};
|
||||
let endp_ctx = &mut input_context.device.endpoints[0];
|
||||
let mut b = endp_ctx.b.read();
|
||||
let mut b = input_context.device.endpoints[0].b.read();
|
||||
b &= 0x0000_FFFF;
|
||||
b |= (new_max_packet_size) << 16;
|
||||
endp_ctx.b.write(b);
|
||||
input_context.device.endpoints[0].b.write(b);
|
||||
|
||||
let (event_trb, command_trb) = self
|
||||
.execute_command(|trb, cycle| {
|
||||
@@ -1007,7 +1018,7 @@ impl Xhci {
|
||||
|
||||
pub async fn address_device(
|
||||
&self,
|
||||
input_context: &mut Dma<InputContext>,
|
||||
input_context: &mut Dma<InputContext<N>>,
|
||||
port: PortId,
|
||||
slot_ty: u8,
|
||||
slot: u8,
|
||||
@@ -1044,19 +1055,17 @@ impl Xhci {
|
||||
}
|
||||
}
|
||||
|
||||
let mut ring = Ring::new(self.cap.ac64(), 16, true)?;
|
||||
let mut ring = Ring::new::<N>(self.cap.ac64(), 16, true)?;
|
||||
|
||||
{
|
||||
input_context.add_context.write(1 << 1 | 1); // Enable the slot (zeroth bit) and the control endpoint (first bit).
|
||||
|
||||
let slot_ctx = &mut input_context.device.slot;
|
||||
|
||||
let route_string = port.route_string;
|
||||
let context_entries = 1u8;
|
||||
let hub = false;
|
||||
|
||||
assert_eq!(route_string & 0x000F_FFFF, route_string);
|
||||
slot_ctx.a.write(
|
||||
input_context.device.slot.a.write(
|
||||
route_string
|
||||
| (u32::from(speed) << 20)
|
||||
| (u32::from(mtt) << 25)
|
||||
@@ -1067,7 +1076,7 @@ impl Xhci {
|
||||
let max_exit_latency = 0u16;
|
||||
let root_hub_port_num = port.root_hub_port_num;
|
||||
let number_of_ports = 0u8;
|
||||
slot_ctx.b.write(
|
||||
input_context.device.slot.b.write(
|
||||
u32::from(max_exit_latency)
|
||||
| (u32::from(root_hub_port_num) << 16)
|
||||
| (u32::from(number_of_ports) << 24),
|
||||
@@ -1078,29 +1087,28 @@ impl Xhci {
|
||||
let interrupter = 0u8;
|
||||
|
||||
assert_eq!(ttt & 0b11, ttt);
|
||||
slot_ctx.c.write(
|
||||
input_context.device.slot.c.write(
|
||||
u32::from(parent_hub_slot_id)
|
||||
| (u32::from(parent_port_num) << 8)
|
||||
| (u32::from(ttt) << 16)
|
||||
| (u32::from(interrupter) << 22),
|
||||
);
|
||||
|
||||
let endp_ctx = &mut input_context.device.endpoints[0];
|
||||
|
||||
let max_error_count = 3u8; // recommended value according to the XHCI spec
|
||||
let ep_ty = 4u8; // control endpoint, bidirectional
|
||||
let max_packet_size: u32 = if protocol_speed.is_lowspeed() || protocol_speed.is_fullspeed() {
|
||||
8
|
||||
} else if protocol_speed.is_highspeed() {
|
||||
64
|
||||
} else {
|
||||
512
|
||||
};
|
||||
let max_packet_size: u32 =
|
||||
if protocol_speed.is_lowspeed() || protocol_speed.is_fullspeed() {
|
||||
8
|
||||
} else if protocol_speed.is_highspeed() {
|
||||
64
|
||||
} else {
|
||||
512
|
||||
};
|
||||
let host_initiate_disable = false; // only applies to streams
|
||||
let max_burst_size = 0u8; // TODO
|
||||
|
||||
assert_eq!(max_error_count & 0b11, max_error_count);
|
||||
endp_ctx.b.write(
|
||||
input_context.device.endpoints[0].b.write(
|
||||
(u32::from(max_error_count) << 1)
|
||||
| (u32::from(ep_ty) << 3)
|
||||
| (u32::from(host_initiate_disable) << 7)
|
||||
@@ -1110,12 +1118,18 @@ impl Xhci {
|
||||
|
||||
let dequeue_cycle_state = true;
|
||||
let tr = ring.register();
|
||||
endp_ctx.trh.write((tr >> 32) as u32);
|
||||
endp_ctx.trl.write((tr as u32) | u32::from(dequeue_cycle_state));
|
||||
input_context.device.endpoints[0]
|
||||
.trh
|
||||
.write((tr >> 32) as u32);
|
||||
input_context.device.endpoints[0]
|
||||
.trl
|
||||
.write((tr as u32) | u32::from(dequeue_cycle_state));
|
||||
|
||||
// The default control pipe can always use 8 bytes
|
||||
let avg_trb_len = 8u8;
|
||||
endp_ctx.c.write(u32::from(avg_trb_len));
|
||||
input_context.device.endpoints[0]
|
||||
.c
|
||||
.write(u32::from(avg_trb_len));
|
||||
}
|
||||
|
||||
let input_context_physical = input_context.physical();
|
||||
@@ -1394,7 +1408,7 @@ impl Xhci {
|
||||
.find(|speed| speed.psiv() == psiv)
|
||||
}
|
||||
}
|
||||
pub fn start_irq_reactor(hci: &Arc<Xhci>, irq_file: Option<File>) {
|
||||
pub fn start_irq_reactor<const N: usize>(hci: &Arc<Xhci<N>>, irq_file: Option<File>) {
|
||||
let hci_clone = Arc::clone(&hci);
|
||||
|
||||
debug!("About to start IRQ reactor");
|
||||
@@ -1405,7 +1419,7 @@ pub fn start_irq_reactor(hci: &Arc<Xhci>, irq_file: Option<File>) {
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn start_device_enumerator(hci: &Arc<Xhci>) {
|
||||
pub fn start_device_enumerator<const N: usize>(hci: &Arc<Xhci<N>>) {
|
||||
let hci_clone = Arc::clone(&hci);
|
||||
|
||||
debug!("About to start Device Enumerator");
|
||||
|
||||
@@ -15,10 +15,10 @@ pub struct Ring {
|
||||
}
|
||||
|
||||
impl Ring {
|
||||
pub fn new(ac64: bool, length: usize, link: bool) -> Result<Ring> {
|
||||
pub fn new<const N: usize>(ac64: bool, length: usize, link: bool) -> Result<Ring> {
|
||||
Ok(Ring {
|
||||
link,
|
||||
trbs: unsafe { Xhci::alloc_dma_zeroed_unsized_raw(ac64, length)? },
|
||||
trbs: unsafe { Xhci::<N>::alloc_dma_zeroed_unsized_raw(ac64, length)? },
|
||||
i: 0,
|
||||
cycle: link,
|
||||
})
|
||||
|
||||
+28
-31
@@ -40,7 +40,10 @@ use syscall::{
|
||||
use super::{port, usb};
|
||||
use super::{EndpointState, PortId, Xhci};
|
||||
|
||||
use super::context::{SlotState, StreamContextArray, StreamContextType};
|
||||
use super::context::{
|
||||
SlotState, StreamContextArray, StreamContextType, CONTEXT_32, CONTEXT_64,
|
||||
SLOT_CONTEXT_STATE_MASK, SLOT_CONTEXT_STATE_SHIFT,
|
||||
};
|
||||
use super::extended::ProtocolSpeed;
|
||||
use super::irq_reactor::{EventDoorbell, RingId};
|
||||
use super::ring::Ring;
|
||||
@@ -543,7 +546,7 @@ impl AnyDescriptor {
|
||||
}
|
||||
}
|
||||
|
||||
impl Xhci {
|
||||
impl<const N: usize> Xhci<N> {
|
||||
async fn new_if_desc(
|
||||
&self,
|
||||
port_id: PortId,
|
||||
@@ -927,13 +930,13 @@ impl Xhci {
|
||||
fn port_state(
|
||||
&self,
|
||||
port: PortId,
|
||||
) -> Result<chashmap::ReadGuard<'_, PortId, super::PortState>> {
|
||||
) -> Result<chashmap::ReadGuard<'_, PortId, super::PortState<N>>> {
|
||||
self.port_states.get(&port).ok_or(Error::new(EBADF))
|
||||
}
|
||||
fn port_state_mut(
|
||||
&self,
|
||||
port: PortId,
|
||||
) -> Result<chashmap::WriteGuard<'_, PortId, super::PortState>> {
|
||||
) -> Result<chashmap::WriteGuard<'_, PortId, super::PortState<N>>> {
|
||||
self.port_states.get_mut(&port).ok_or(Error::new(EBADF))
|
||||
}
|
||||
|
||||
@@ -1103,10 +1106,10 @@ impl Xhci {
|
||||
|
||||
let ring_ptr = if usb_log_max_streams.is_some() {
|
||||
let mut array =
|
||||
StreamContextArray::new(self.cap.ac64(), 1 << (primary_streams + 1))?;
|
||||
StreamContextArray::new::<N>(self.cap.ac64(), 1 << (primary_streams + 1))?;
|
||||
|
||||
// TODO: Use as many stream rings as needed.
|
||||
array.add_ring(self.cap.ac64(), 1, true)?;
|
||||
array.add_ring::<N>(self.cap.ac64(), 1, true)?;
|
||||
let array_ptr = array.register();
|
||||
|
||||
assert_eq!(
|
||||
@@ -1124,7 +1127,7 @@ impl Xhci {
|
||||
|
||||
array_ptr
|
||||
} else {
|
||||
let ring = Ring::new(self.cap.ac64(), 16, true)?;
|
||||
let ring = Ring::new::<N>(self.cap.ac64(), 16, true)?;
|
||||
let ring_ptr = ring.register();
|
||||
|
||||
assert_eq!(
|
||||
@@ -1146,23 +1149,15 @@ impl Xhci {
|
||||
let mut input_context = port_state.input_context.lock().unwrap();
|
||||
input_context.add_context.writef(1 << endp_num_xhc, true);
|
||||
|
||||
let endp_ctx = input_context
|
||||
.device
|
||||
.endpoints
|
||||
.get_mut(endp_num_xhc as usize - 1)
|
||||
.ok_or_else(|| {
|
||||
warn!("failed to find endpoint {}", endp_num_xhc - 1);
|
||||
Error::new(EIO)
|
||||
})?;
|
||||
|
||||
endp_ctx.a.write(
|
||||
let endp_i = endp_num_xhc as usize - 1;
|
||||
input_context.device.endpoints[endp_i].a.write(
|
||||
u32::from(mult) << 8
|
||||
| u32::from(primary_streams) << 10
|
||||
| u32::from(linear_stream_array) << 15
|
||||
| u32::from(interval) << 16
|
||||
| u32::from(max_esit_payload_hi) << 24,
|
||||
);
|
||||
endp_ctx.b.write(
|
||||
input_context.device.endpoints[endp_i].b.write(
|
||||
max_error_count << 1
|
||||
| u32::from(ep_ty) << 3
|
||||
| u32::from(host_initiate_disable) << 7
|
||||
@@ -1170,10 +1165,14 @@ impl Xhci {
|
||||
| u32::from(max_packet_size) << 16,
|
||||
);
|
||||
|
||||
endp_ctx.trl.write(ring_ptr as u32);
|
||||
endp_ctx.trh.write((ring_ptr >> 32) as u32);
|
||||
input_context.device.endpoints[endp_i]
|
||||
.trl
|
||||
.write(ring_ptr as u32);
|
||||
input_context.device.endpoints[endp_i]
|
||||
.trh
|
||||
.write((ring_ptr >> 32) as u32);
|
||||
|
||||
endp_ctx
|
||||
input_context.device.endpoints[endp_i]
|
||||
.c
|
||||
.write(u32::from(avg_trb_len) | (u32::from(max_esit_payload_lo) << 16));
|
||||
|
||||
@@ -2099,7 +2098,7 @@ impl Xhci {
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeSync for &Xhci {
|
||||
impl<const N: usize> SchemeSync for &Xhci<N> {
|
||||
fn open(&mut self, path_str: &str, flags: usize, ctx: &CallerCtx) -> Result<OpenResult> {
|
||||
if ctx.uid != 0 {
|
||||
return Err(Error::new(EACCES));
|
||||
@@ -2237,13 +2236,13 @@ impl SchemeSync for &Xhci {
|
||||
},
|
||||
&mut Handle::PortState(port_num) => {
|
||||
let ps = self.port_states.get(&port_num).ok_or(Error::new(EBADF))?;
|
||||
let state = self
|
||||
let ctx = self
|
||||
.dev_ctx
|
||||
.contexts
|
||||
.get(ps.slot as usize)
|
||||
.ok_or(Error::new(EBADF))?
|
||||
.slot
|
||||
.state();
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
let state = ((ctx.slot.d.read() & SLOT_CONTEXT_STATE_MASK)
|
||||
>> SLOT_CONTEXT_STATE_SHIFT) as u8;
|
||||
|
||||
let string = match state {
|
||||
0 => Some(PortState::EnabledOrDisabled),
|
||||
@@ -2257,7 +2256,7 @@ impl SchemeSync for &Xhci {
|
||||
.unwrap_or("unknown")
|
||||
.as_bytes();
|
||||
|
||||
Ok(Xhci::write_dyn_string(string, buf, offset))
|
||||
Ok(Xhci::<N>::write_dyn_string(string, buf, offset))
|
||||
}
|
||||
&mut Handle::PortReq(port_num, ref mut st) => {
|
||||
let state = std::mem::replace(st, PortReqState::Tmp);
|
||||
@@ -2316,7 +2315,7 @@ impl SchemeSync for &Xhci {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Xhci {
|
||||
impl<const N: usize> Xhci<N> {
|
||||
pub fn on_close(&self, fd: usize) {
|
||||
self.handles.remove(&fd);
|
||||
}
|
||||
@@ -2351,9 +2350,7 @@ impl Xhci {
|
||||
.contexts
|
||||
.get(slot as usize)
|
||||
.ok_or(Error::new(EBADFD))?
|
||||
.endpoints
|
||||
.get(endp_num_xhc as usize - 1)
|
||||
.ok_or(Error::new(EBADFD))?
|
||||
.endpoints[endp_num_xhc as usize - 1]
|
||||
.a
|
||||
.read()
|
||||
& super::context::ENDPOINT_CONTEXT_STATUS_MASK;
|
||||
|
||||
Reference in New Issue
Block a user