Rust XHCI Implementation.
This commit is contained in:
parent
0b95098748
commit
2c271360ce
25 changed files with 1288 additions and 202 deletions
|
|
@ -1,3 +1,5 @@
|
|||
use core::ffi::c_void;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use mammoth::{
|
||||
cap::Capability,
|
||||
|
|
@ -27,7 +29,8 @@ impl AhciController {
|
|||
let pci_device = PciDevice::from_cap(pci_memory).unwrap();
|
||||
|
||||
let hba_vaddr =
|
||||
mem::map_direct_physical_and_leak(pci_device.header().bars[5] as u64, 0x1100);
|
||||
mem::map_direct_physical_and_leak(pci_device.header().bars[5] as u64, 0x1100)
|
||||
as *mut c_void as u64;
|
||||
let hba = unsafe { (hba_vaddr as *mut AhciHba).as_mut().unwrap() };
|
||||
let mut controller = Self {
|
||||
pci_device: Mutex::new(pci_device),
|
||||
|
|
|
|||
|
|
@ -6,3 +6,10 @@ edition = "2024"
|
|||
[dependencies]
|
||||
bitfield-struct = "0.12"
|
||||
mammoth = { path = "../../lib/mammoth/" }
|
||||
pci = { path = "../../lib/pci" }
|
||||
volatile = "0.6.1"
|
||||
yellowstone-yunq = { version = "0.1.0", path = "../../lib/yellowstone" }
|
||||
|
||||
[features]
|
||||
default = ["debug"]
|
||||
debug = []
|
||||
|
|
|
|||
|
|
@ -5,12 +5,47 @@ extern crate alloc;
|
|||
|
||||
mod xhci;
|
||||
|
||||
use mammoth::{debug, define_entry, zion::z_err_t};
|
||||
use alloc::sync::Arc;
|
||||
use mammoth::{
|
||||
cap::Capability,
|
||||
debug, define_entry,
|
||||
sync::Mutex,
|
||||
task::{Executor, Task},
|
||||
zion::z_err_t,
|
||||
};
|
||||
use pci::PciDevice;
|
||||
use xhci::driver::XHCIDriver;
|
||||
|
||||
define_entry!();
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn main() -> z_err_t {
|
||||
debug!("In Voyageurs");
|
||||
#[cfg(feature = "debug")]
|
||||
debug!("Voyageurs Starting.");
|
||||
|
||||
let yellowstone = yellowstone_yunq::from_init_endpoint();
|
||||
|
||||
let xhci_info = yellowstone
|
||||
.get_xhci_info()
|
||||
.expect("Failed to get XHCI info from yellowstone.");
|
||||
|
||||
let pci_device = PciDevice::from_cap(Capability::take(xhci_info.xhci_region)).unwrap();
|
||||
|
||||
let xhci_driver = Arc::new(XHCIDriver::from_pci_device(pci_device));
|
||||
|
||||
let executor = Arc::new(Mutex::new(Executor::new()));
|
||||
|
||||
let driver_clone = xhci_driver.clone();
|
||||
let spawner = executor.clone().lock().new_spawner();
|
||||
let interrupt_thread = mammoth::thread::spawn(move || driver_clone.interrupt_loop(spawner));
|
||||
|
||||
executor
|
||||
.clone()
|
||||
.lock()
|
||||
.spawn(Task::new(async move { xhci_driver.startup().await }));
|
||||
|
||||
executor.clone().lock().run();
|
||||
interrupt_thread.join().unwrap();
|
||||
|
||||
0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
use core::ops::{Index, IndexMut};
|
||||
|
||||
use alloc::{boxed::Box, vec};
|
||||
use mammoth::physical_box::PhysicalBox;
|
||||
|
||||
use crate::xhci::data_structures::TrbRing;
|
||||
use crate::xhci::data_structures::TrbRingSegment;
|
||||
|
||||
#[repr(align(64))]
|
||||
#[derive(Default, Clone)]
|
||||
|
|
@ -19,10 +18,16 @@ pub struct EventRingSegmentTableEntry {
|
|||
}
|
||||
|
||||
impl EventRingSegmentTableEntry {
|
||||
pub fn from_trb_fing(&mut self, trb_ring: &TrbRing) {
|
||||
pub fn from_trb_ring(&mut self, trb_ring: &TrbRingSegment) {
|
||||
mammoth::debug!("RSTE: {:0x}", self as *const _ as usize);
|
||||
self.ring_segment_base_address = trb_ring.physical_address() as u64;
|
||||
assert!(self.ring_segment_base_address % 64 == 0);
|
||||
self.ring_segment_size = trb_ring.len() as u64;
|
||||
unsafe {
|
||||
core::ptr::write_volatile(
|
||||
&mut self.ring_segment_size as *mut u64,
|
||||
trb_ring.len() as u64,
|
||||
)
|
||||
};
|
||||
assert!(self.ring_segment_size >= 16);
|
||||
assert!(self.ring_segment_size <= 4096);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
mod endpoint_context;
|
||||
mod event_ring_segment_table;
|
||||
mod slot_context;
|
||||
mod trb_ring;
|
||||
mod trb;
|
||||
mod trb_ring_segment;
|
||||
|
||||
pub use event_ring_segment_table::*;
|
||||
pub use slot_context::*;
|
||||
pub use trb_ring::*;
|
||||
pub use trb::*;
|
||||
pub use trb_ring_segment::*;
|
||||
|
|
|
|||
234
rust/sys/voyageurs/src/xhci/data_structures/trb.rs
Normal file
234
rust/sys/voyageurs/src/xhci/data_structures/trb.rs
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
use bitfield_struct::{bitenum, bitfield};
|
||||
|
||||
#[bitenum]
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum TrbType {
|
||||
#[fallback]
|
||||
Reserved = 0,
|
||||
Normal = 1,
|
||||
SetupStage = 2,
|
||||
DataStage = 3,
|
||||
StatusStage = 4,
|
||||
Isoch = 5,
|
||||
Link = 6,
|
||||
EventData = 7,
|
||||
NoOp = 8,
|
||||
EnableSlotCommand = 9,
|
||||
DisableSlotCommand = 10,
|
||||
AddressDeviceCommand = 11,
|
||||
ConfigureEndpointCommand = 12,
|
||||
EvaluateContextCommand = 13,
|
||||
ResetEndpointCommand = 14,
|
||||
StopEndpointCommand = 15,
|
||||
SetTRDequeuePointerCommand = 16,
|
||||
ResetDeviceCommand = 17,
|
||||
ForceEventCommand = 18,
|
||||
NegotiateBandwidthCommand = 19,
|
||||
SetLatencyToleranceValueCommand = 20,
|
||||
GetPortBandwidthCommand = 21,
|
||||
ForceHeaderCommand = 22,
|
||||
NoOpCommand = 23,
|
||||
GetExtendedPropertyCommand = 24,
|
||||
SetExtendedPropertyCommand = 25,
|
||||
TransferEvent = 32,
|
||||
CommandCompletionEvent = 33,
|
||||
PortStatusChangeEvent = 34,
|
||||
BandwidthRequestEvent = 35,
|
||||
DoorbellEvent = 36,
|
||||
HostControllerEvent = 37,
|
||||
DeviceNotificationEvent = 38,
|
||||
MFINDEXWrapEvent = 39,
|
||||
}
|
||||
|
||||
#[bitfield(u128)]
|
||||
pub struct TransferRequestBlock {
|
||||
pub parameter: u64,
|
||||
pub status: u32,
|
||||
pub cycle: bool,
|
||||
evaluate_next: bool,
|
||||
flag_2: bool,
|
||||
flag_3: bool,
|
||||
flag_4: bool,
|
||||
flag_5: bool,
|
||||
flag_6: bool,
|
||||
flag_7: bool,
|
||||
flag_8: bool,
|
||||
flag_9: bool,
|
||||
#[bits(6)]
|
||||
pub trb_type: TrbType,
|
||||
control: u16,
|
||||
}
|
||||
|
||||
impl TransferRequestBlock {}
|
||||
|
||||
pub trait TypedTrb
|
||||
where
|
||||
Self: Into<u128> + From<u128> + Copy,
|
||||
{
|
||||
fn from_trb(trb: TransferRequestBlock) -> Self {
|
||||
trb.into_bits().into()
|
||||
}
|
||||
|
||||
fn to_trb(self) -> TransferRequestBlock {
|
||||
Into::<u128>::into(self).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[bitfield(u128)]
|
||||
pub struct TrbNoOp {
|
||||
__: u64,
|
||||
#[bits(22)]
|
||||
__: u32,
|
||||
#[bits(10, default = 0)]
|
||||
interrupter_target: u16,
|
||||
cycle: bool,
|
||||
evaluate_next: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
chain: bool,
|
||||
#[bits(default = true)]
|
||||
interrupt_on_completion: bool,
|
||||
#[bits(4)]
|
||||
__: u8,
|
||||
#[bits(6, default = TrbType::NoOpCommand)]
|
||||
trb_type: TrbType,
|
||||
__: u16,
|
||||
}
|
||||
|
||||
impl TypedTrb for TrbNoOp {}
|
||||
|
||||
#[bitfield(u128)]
|
||||
pub struct TrbLink {
|
||||
/// Ring Segment Pointer Hi and Lo. These fields represent the high order bits of the 64-bit base
|
||||
/// address of the next Ring Segment.
|
||||
/// The memory structure referenced by this physical memory pointer shall begin on a 16-byte
|
||||
/// address boundary.
|
||||
pub ring_segment_pointer: u64,
|
||||
#[bits(22)]
|
||||
__: u32,
|
||||
/// Interrupter Target. This field defines the index of the Interrupter that will receive Transfer
|
||||
/// Events generated by this TRB. Valid values are between 0 and MaxIntrs-1.
|
||||
/// This field is ignored by the xHC on Command Rings.
|
||||
#[bits(10)]
|
||||
pub interrupter_target: u16,
|
||||
/// Cycle bit (C). This bit is used to mark the Enqueue Pointer location of a Transfer or Command
|
||||
/// Ring.
|
||||
pub cycle: bool,
|
||||
/// Toggle Cycle (TC). When set to ‘1’, the xHC shall toggle its interpretation of the Cycle bit. When
|
||||
/// cleared to ‘0’, the xHC shall continue to the next segment using its current interpretation of the
|
||||
/// Cycle bit.
|
||||
pub toggle_cycle: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
/// Chain bit (CH). Set to ‘1’ by software to associate this TRB with the next TRB on the Ring. A
|
||||
/// Transfer Descriptor (TD) is defined as one or more TRBs. The Chain bit is used to identify the
|
||||
/// TRBs that comprise a TD. Refer to section 4.11.7 for more information on Link TRB placement
|
||||
/// within a TD. On a Command Ring this bit is ignored by the xHC.
|
||||
#[bits(default = true)]
|
||||
chain: bool,
|
||||
/// Interrupt On Completion (IOC). If this bit is set to ‘1’, it specifies that when this TRB completes,
|
||||
/// the Host Controller shall notify the system of the completion by placing an Event TRB on the
|
||||
/// Event ring and sending an interrupt at the next interrupt threshold.
|
||||
pub interrupt_on_completion: bool,
|
||||
#[bits(4)]
|
||||
__: u8,
|
||||
/// TRB Type. This field is set to Link TRB type. Refer to Table 6-91 for the definition of the Type
|
||||
/// TRB IDs.
|
||||
#[bits(6, default = TrbType::Link)]
|
||||
trb_type: TrbType,
|
||||
__: u16,
|
||||
}
|
||||
|
||||
impl TypedTrb for TrbLink {}
|
||||
|
||||
#[bitenum]
|
||||
#[repr(u8)]
|
||||
pub enum CommandCompletionCode {
|
||||
#[fallback]
|
||||
Invalid = 0,
|
||||
Success = 1,
|
||||
}
|
||||
|
||||
#[bitfield(u128)]
|
||||
pub struct TrbCommandCompletion {
|
||||
/// Command TRB Pointer Hi and Lo. This field represents the high order bits of the 64-bit address
|
||||
/// of the Command TRB that generated this event. Note that this field is not valid for some
|
||||
/// Completion Code values. Refer to Table 6-90 for specific cases.
|
||||
///
|
||||
/// The memory structure referenced by this physical memory pointer shall be aligned on a 16-byte
|
||||
/// address boundary.
|
||||
pub command_trb_pointer: u64,
|
||||
/// Command Completion Parameter. This field may optionally be set by a command. Refer to
|
||||
/// section 4.6.6.1 for specific usage. If a command does not utilize this field it shall be treated as
|
||||
/// RsvdZ.
|
||||
#[bits(24)]
|
||||
pub command_completion_parameter: u64,
|
||||
/// Completion Code. This field encodes the completion status of the command that generated the
|
||||
/// event. Refer to the respective command definition for a list of the possible Completion Codes
|
||||
/// associated with the command. Refer to section 6.4.5 for an enumerated list of possible error
|
||||
/// conditions.
|
||||
pub completion_code: u8,
|
||||
/// Cycle bit (C). This bit is used to mark the Dequeue Pointer of an Event Ring
|
||||
pub cycle_bit: bool,
|
||||
#[bits(9)]
|
||||
__: u16,
|
||||
/// TRB Type. This field identifies the type of the TRB. Refer to Table 6-91 for the definition of the
|
||||
/// Command Completion Event TRB type ID
|
||||
#[bits(6, default=TrbType::CommandCompletionEvent)]
|
||||
pub trb_type: TrbType,
|
||||
/// VF ID. The ID of the Virtual Function that generated the event. Note that this field is valid only if
|
||||
/// Virtual Functions are enabled. If they are not enabled this field shall be cleared to ‘0’.
|
||||
pub vf_id: u8,
|
||||
/// Slot ID. The Slot ID field shall be updated by the xHC to reflect the slot associated with the
|
||||
/// command that generated the event, with the following exceptions:
|
||||
///
|
||||
/// - The Slot ID shall be cleared to ‘0’ for No Op, Set Latency Tolerance Value, Get Port Bandwidth,
|
||||
/// and Force Event Commands.
|
||||
///
|
||||
/// - The Slot ID shall be set to the ID of the newly allocated Device Slot for the Enable Slot
|
||||
/// Command.
|
||||
///
|
||||
/// - The value of Slot ID shall be vendor defined when generated by a vendor defined command.
|
||||
///
|
||||
/// This value is used as an index in the Device Context Base Address Array to select the Device
|
||||
/// Context of the source device. If this Event is due to a Host Controller Command, then this field
|
||||
/// shall be cleared to ‘0’.
|
||||
pub slot_id: u8,
|
||||
}
|
||||
|
||||
impl TypedTrb for TrbCommandCompletion {}
|
||||
|
||||
#[bitfield(u128)]
|
||||
pub struct TrbPortStatusChangeEvent {
|
||||
#[bits(24)]
|
||||
__: u32,
|
||||
pub port_id: u8,
|
||||
__: u32,
|
||||
#[bits(24)]
|
||||
__: u32,
|
||||
pub completion_code: u8,
|
||||
#[bits(10)]
|
||||
__: u16,
|
||||
#[bits(6, default=TrbType::PortStatusChangeEvent)]
|
||||
trb_type: TrbType,
|
||||
__: u16,
|
||||
}
|
||||
|
||||
impl TypedTrb for TrbPortStatusChangeEvent {}
|
||||
|
||||
#[bitfield(u128)]
|
||||
pub struct TrbEnableSlotCommand {
|
||||
__: u64,
|
||||
__: u32,
|
||||
#[bits(10)]
|
||||
__: u16,
|
||||
#[bits(6, default=TrbType::EnableSlotCommand)]
|
||||
trb_type: TrbType,
|
||||
#[bits(5)]
|
||||
slot_type: u8,
|
||||
#[bits(11)]
|
||||
__: u16,
|
||||
}
|
||||
|
||||
impl TypedTrb for TrbEnableSlotCommand {}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
use core::{
|
||||
ops::{Index, IndexMut},
|
||||
slice::SliceIndex,
|
||||
};
|
||||
|
||||
use bitfield_struct::{bitenum, bitfield};
|
||||
use mammoth::physical_box::PhysicalBox;
|
||||
|
||||
#[bitenum]
|
||||
#[repr(u8)]
|
||||
#[derive(Debug)]
|
||||
pub enum TrbType {
|
||||
#[fallback]
|
||||
Reserved = 0,
|
||||
NoOp = 8,
|
||||
}
|
||||
|
||||
#[bitfield(u128)]
|
||||
pub struct TransferRequestBlock {
|
||||
parameter: u64,
|
||||
status: u32,
|
||||
cycle: bool,
|
||||
evaluate_next: bool,
|
||||
flag_2: bool,
|
||||
flag_3: bool,
|
||||
flag_4: bool,
|
||||
flag_5: bool,
|
||||
flag_6: bool,
|
||||
flag_7: bool,
|
||||
flag_8: bool,
|
||||
flag_9: bool,
|
||||
#[bits(6)]
|
||||
trb_type: u8,
|
||||
control: u16,
|
||||
}
|
||||
|
||||
impl TransferRequestBlock {}
|
||||
|
||||
trait TypedTrb {
|
||||
fn from_trb(trb: TransferRequestBlock) -> Self
|
||||
where
|
||||
Self: From<u128>,
|
||||
{
|
||||
trb.into_bits().into()
|
||||
}
|
||||
|
||||
fn to_trb(self) -> TransferRequestBlock
|
||||
where
|
||||
Self: Into<u128>,
|
||||
{
|
||||
Into::<u128>::into(self).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[bitfield(u128)]
|
||||
pub struct TrbNoOp {
|
||||
__: u64,
|
||||
__: u32,
|
||||
cycle: bool,
|
||||
evaluate_next: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
chain: bool,
|
||||
#[bits(default = true)]
|
||||
interrupt_on_completion: bool,
|
||||
#[bits(4)]
|
||||
__: u8,
|
||||
#[bits(6, default = TrbType::NoOp)]
|
||||
trb_type: TrbType,
|
||||
__: u16,
|
||||
}
|
||||
|
||||
impl TypedTrb for TrbNoOp {}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct TrbRing(PhysicalBox<[TransferRequestBlock]>);
|
||||
|
||||
impl TrbRing {
|
||||
pub fn new(size: usize) -> Self {
|
||||
Self(PhysicalBox::default_with_count(
|
||||
TransferRequestBlock::default(),
|
||||
size,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn physical_address(&self) -> usize {
|
||||
self.0.physical_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Index<I> for TrbRing
|
||||
where
|
||||
I: SliceIndex<[TransferRequestBlock]>,
|
||||
{
|
||||
type Output = I::Output;
|
||||
|
||||
fn index(&self, index: I) -> &Self::Output {
|
||||
&self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> IndexMut<I> for TrbRing
|
||||
where
|
||||
I: SliceIndex<[TransferRequestBlock]>,
|
||||
{
|
||||
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
||||
&mut self.0[index]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
use core::{
|
||||
ops::{Index, IndexMut},
|
||||
slice::SliceIndex,
|
||||
};
|
||||
|
||||
use mammoth::physical_box::PhysicalBox;
|
||||
|
||||
use crate::xhci::data_structures::TransferRequestBlock;
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct TrbRingSegment(PhysicalBox<[TransferRequestBlock]>);
|
||||
|
||||
impl TrbRingSegment {
|
||||
pub fn new(size: usize) -> Self {
|
||||
Self(PhysicalBox::default_with_count(
|
||||
TransferRequestBlock::default(),
|
||||
size,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn physical_address(&self) -> usize {
|
||||
self.0.physical_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Index<I> for TrbRingSegment
|
||||
where
|
||||
I: SliceIndex<[TransferRequestBlock]>,
|
||||
{
|
||||
type Output = I::Output;
|
||||
|
||||
fn index(&self, index: I) -> &Self::Output {
|
||||
&self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> IndexMut<I> for TrbRingSegment
|
||||
where
|
||||
I: SliceIndex<[TransferRequestBlock]>,
|
||||
{
|
||||
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
||||
&mut self.0[index]
|
||||
}
|
||||
}
|
||||
22
rust/sys/voyageurs/src/xhci/device_context_base_array.rs
Normal file
22
rust/sys/voyageurs/src/xhci/device_context_base_array.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
use mammoth::mem::MemoryRegion;
|
||||
|
||||
pub struct DeviceContextBaseArray {
|
||||
#[allow(dead_code)]
|
||||
region: MemoryRegion,
|
||||
physical_addr: usize,
|
||||
}
|
||||
|
||||
impl DeviceContextBaseArray {
|
||||
pub fn new() -> Self {
|
||||
let (region, physical_addr) = MemoryRegion::contiguous_physical(0x1000).unwrap();
|
||||
region.zero_region();
|
||||
Self {
|
||||
region,
|
||||
physical_addr: physical_addr as usize,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn physical_addr(&self) -> usize {
|
||||
self.physical_addr
|
||||
}
|
||||
}
|
||||
331
rust/sys/voyageurs/src/xhci/driver.rs
Normal file
331
rust/sys/voyageurs/src/xhci/driver.rs
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
use core::slice;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use mammoth::cap::Capability;
|
||||
use mammoth::mem::MemoryRegion;
|
||||
use mammoth::sync::Mutex;
|
||||
use mammoth::task::Spawner;
|
||||
use mammoth::task::Task;
|
||||
use mammoth::write_unaligned_volatile;
|
||||
|
||||
use super::registers::{self};
|
||||
use crate::xhci::data_structures::CommandCompletionCode;
|
||||
use crate::xhci::data_structures::TrbCommandCompletion;
|
||||
use crate::xhci::data_structures::TrbEnableSlotCommand;
|
||||
use crate::xhci::data_structures::TrbNoOp;
|
||||
use crate::xhci::data_structures::TrbPortStatusChangeEvent;
|
||||
use crate::xhci::data_structures::TrbType;
|
||||
use crate::xhci::data_structures::TypedTrb;
|
||||
use crate::xhci::device_context_base_array::DeviceContextBaseArray;
|
||||
use crate::xhci::event_ring::EventRing;
|
||||
use crate::xhci::registers::HostControllerOperationalWrapper;
|
||||
use crate::xhci::registers::PortStatusAndControl;
|
||||
use crate::xhci::trb_ring::TrbRing;
|
||||
|
||||
pub struct XHCIDriver {
|
||||
#[allow(dead_code)]
|
||||
pci_device: pci::PciDevice,
|
||||
capabilities: registers::HostControllerCapabilities,
|
||||
operational: HostControllerOperationalWrapper,
|
||||
registers_region: MemoryRegion,
|
||||
command_ring: Mutex<TrbRing<TrbCommandCompletion>>,
|
||||
event_ring: Mutex<EventRing>,
|
||||
device_context_base_array: DeviceContextBaseArray,
|
||||
irq_port_cap: Capability,
|
||||
}
|
||||
|
||||
impl XHCIDriver {
|
||||
pub fn from_pci_device(mut pci_device: pci::PciDevice) -> Self {
|
||||
// however the RTSOFF and DBOFF Registers shall position the Runtime and
|
||||
// Doorbell Registers to reside on their own respective virtual memory pages. The
|
||||
// BAR0 size shall provide space that is sufficient to cover the offset between the
|
||||
// respective register spaces (Capability, Operational, Runtime, etc.) and the
|
||||
// register spaces themselves (e.g. a minimum of 3 virtual memory pages).
|
||||
// If virtualization is not supported, all xHCI register spaces may reside on a single
|
||||
// page pointed to by the BAR0.
|
||||
let three_pages = 0x3000;
|
||||
let address =
|
||||
((pci_device.header().bars[1] as u64) << 32) | (pci_device.header().bars[0] as u64);
|
||||
let registers_region = MemoryRegion::direct_physical(address, three_pages).unwrap();
|
||||
let irq_port_cap = pci_device.register_msi().unwrap();
|
||||
|
||||
let (operational, capabilities) = HostControllerOperationalWrapper::new(address as usize);
|
||||
|
||||
let p1 = capabilities.params_1;
|
||||
mammoth::debug!("Num Port: {:?}", p1);
|
||||
|
||||
let mut driver = Self {
|
||||
pci_device,
|
||||
capabilities,
|
||||
operational,
|
||||
registers_region,
|
||||
command_ring: Mutex::new(TrbRing::new()),
|
||||
event_ring: Mutex::new(EventRing::new()),
|
||||
device_context_base_array: DeviceContextBaseArray::new(),
|
||||
irq_port_cap,
|
||||
};
|
||||
driver.initialize();
|
||||
driver
|
||||
}
|
||||
|
||||
fn interrupters(&self) -> &mut [registers::InterrupterRegisterSet] {
|
||||
// See Table 5-35: Host Controller Runtime Registers
|
||||
const INTERRUPTER_OFFSET_FROM_RUNTIME: u32 = 0x20;
|
||||
let runtime = self.capabilities.runtime_register_space_offset;
|
||||
|
||||
let interrupter_offset = (runtime + INTERRUPTER_OFFSET_FROM_RUNTIME) as usize;
|
||||
|
||||
let params1 = self.capabilities.params_1;
|
||||
|
||||
// SAFETY: The XHCI spec says so?
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
self.registers_region.mut_ptr_at_offset(interrupter_offset),
|
||||
params1.max_interrupters() as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn doorbells(&self) -> &mut [registers::Doorbell] {
|
||||
let doorbell_offset = self.capabilities.doorbell_offset;
|
||||
|
||||
let params1 = self.capabilities.params_1;
|
||||
|
||||
// SAFETY: The XHCI spec says so?
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
self.registers_region
|
||||
.mut_ptr_at_offset(doorbell_offset as usize),
|
||||
params1.max_device_slots() as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize(&mut self) {
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Stopping XHCI Controller.");
|
||||
|
||||
// Stop the host controller.
|
||||
self.operational.update(|mut o| {
|
||||
o.usb_command = o.usb_command.with_run_stop(false);
|
||||
o
|
||||
});
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Waiting for controller to halt.");
|
||||
|
||||
// Sleep until the controller is halted.
|
||||
let mut status = self.operational.status();
|
||||
while !status.host_controller_halted() {
|
||||
// TODO: Sleep for how long?
|
||||
mammoth::syscall::thread_sleep(50).unwrap();
|
||||
status = self.operational.status();
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Resetting Controller.");
|
||||
|
||||
self.operational.update(|mut o| {
|
||||
o.usb_command = o.usb_command.with_host_controller_reset(false);
|
||||
o
|
||||
});
|
||||
|
||||
let mut command: registers::UsbCommand = self.operational.command();
|
||||
while command.host_controller_reset() {
|
||||
// TODO: Sleep for how long?
|
||||
mammoth::syscall::thread_sleep(50).unwrap();
|
||||
command = self.operational.command();
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("XHCI Controller Reset, waiting ready.");
|
||||
|
||||
let mut status = self.operational.status();
|
||||
while status.controller_not_ready() {
|
||||
// TODO: Sleep for how long?
|
||||
mammoth::syscall::thread_sleep(50).unwrap();
|
||||
status = self.operational.status();
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("XHCI Controller Ready.");
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Setting Command Ring");
|
||||
|
||||
self.operational.update(|mut o| {
|
||||
// TODO: Split this struct to make it clearer that we are setting the cycle bit here.
|
||||
o.command_ring_control = (self.command_ring.lock().physical_base_address() | 1) as u64;
|
||||
o
|
||||
});
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Setting DCBA.");
|
||||
|
||||
let params1 = self.capabilities.params_1;
|
||||
self.operational.update(|mut o| {
|
||||
o.device_context_base_address_array_pointer =
|
||||
self.device_context_base_array.physical_addr() as u64;
|
||||
// We tell the controller that we can support as many slots as it does because
|
||||
// we allocate a full 4K page to the DCBA, which is 256 entries and the max
|
||||
// slots are 255.
|
||||
o.configure = o
|
||||
.configure
|
||||
.with_max_device_slots_enabled(params1.max_device_slots());
|
||||
o
|
||||
});
|
||||
|
||||
let params2 = self.capabilities.params_2;
|
||||
|
||||
let max_scratchpad_buffers =
|
||||
(params2.max_scratchpad_buffers_hi() << 5) | params2.max_scratchpad_buffers_lo();
|
||||
assert!(
|
||||
max_scratchpad_buffers == 0,
|
||||
"Unsupported scratchpad buffers."
|
||||
);
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Setting up initial event ring.");
|
||||
|
||||
let interrupter0 = &mut self.interrupters()[0];
|
||||
// SAFETY:
|
||||
// - The HC was halted above.
|
||||
// - THe segment table is size 1.
|
||||
unsafe {
|
||||
let event_ring = self.event_ring.lock();
|
||||
interrupter0.set_event_ring(
|
||||
event_ring.segment_table(),
|
||||
event_ring.erdp_physical_address(),
|
||||
);
|
||||
}
|
||||
write_unaligned_volatile!(
|
||||
interrupter0,
|
||||
interrupter_moderation,
|
||||
registers::InterrupterModeration::new()
|
||||
.with_interrupt_moderation_interval(4000)
|
||||
.with_interrupt_moderation_counter(0)
|
||||
);
|
||||
write_unaligned_volatile!(
|
||||
interrupter0,
|
||||
interrupter_management,
|
||||
registers::InterrupterManagement::new().with_interrupt_enabled(true)
|
||||
);
|
||||
|
||||
self.operational.update(|mut o| {
|
||||
o.usb_command = o
|
||||
.usb_command
|
||||
.with_run_stop(true)
|
||||
.with_interrupter_enable(true);
|
||||
o
|
||||
});
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Enabled interrupts and controller.");
|
||||
}
|
||||
|
||||
pub fn interrupt_loop(self: Arc<Self>, spawner: Spawner) {
|
||||
loop {
|
||||
let _ = mammoth::syscall::port_recv(&self.irq_port_cap, &mut [], &mut []).unwrap();
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Received Interrupt.");
|
||||
self.interrupters()[0].interrupter_management = self.interrupters()[0]
|
||||
.interrupter_management
|
||||
.with_interrupt_pending(true);
|
||||
|
||||
// TODO: Make event ring own its interrupter.
|
||||
let mut event_ring = self.event_ring.lock();
|
||||
|
||||
while let Some(trb) = event_ring.get_next() {
|
||||
event_ring.update_dequeue_pointer(&mut self.interrupters()[0]);
|
||||
match trb.trb_type() {
|
||||
TrbType::TransferEvent => {
|
||||
todo!("Handle Transfer")
|
||||
}
|
||||
TrbType::CommandCompletionEvent => {
|
||||
self.command_ring
|
||||
.lock()
|
||||
.handle_commpletion(TrbCommandCompletion::from_trb(trb));
|
||||
}
|
||||
TrbType::PortStatusChangeEvent => {
|
||||
let trb = TrbPortStatusChangeEvent::from_trb(trb);
|
||||
let self_clone = self.clone();
|
||||
spawner.spawn(Task::new(async move {
|
||||
self_clone.port_status_change(trb).await
|
||||
}));
|
||||
}
|
||||
_ => {
|
||||
panic!("Unhandled event type: {:?}", trb.trb_type());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_command(&self, trb: impl TypedTrb) -> TrbCommandCompletion {
|
||||
let fut = self.command_ring.lock().enqueue_trb(trb.to_trb());
|
||||
self.doorbells()[0].ring_command();
|
||||
fut.await
|
||||
}
|
||||
|
||||
pub async fn startup(&self) {
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Sending no op command.");
|
||||
|
||||
let result = self.send_command(TrbNoOp::new()).await;
|
||||
|
||||
assert!(result.completion_code() == CommandCompletionCode::Success.into_bits());
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Successfully tested no op command.");
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Resetting all connected ports.");
|
||||
for port_index in 0..self.operational.num_ports() {
|
||||
self.operational
|
||||
.update_port_status(port_index, |p| p.clear_change_bits());
|
||||
}
|
||||
|
||||
for port_index in 0..self.operational.num_ports() {
|
||||
let status = self.operational.get_port(port_index).status_and_control;
|
||||
if status.port_power() && status.current_connect_status() {
|
||||
mammoth::debug!("Resetting port {}", port_index);
|
||||
self.operational.update_port_status(port_index, |_| {
|
||||
PortStatusAndControl::new()
|
||||
.with_port_reset(true)
|
||||
.with_port_power(true)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn port_status_change(self: Arc<Self>, status_change: TrbPortStatusChangeEvent) {
|
||||
// Ports are indexed from 1.
|
||||
let port_id = status_change.port_id();
|
||||
let port_index = (port_id - 1) as usize;
|
||||
|
||||
let port_status = self
|
||||
.operational
|
||||
.get_port(port_index as usize)
|
||||
.status_and_control;
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!(
|
||||
"Port status change for port {}, status= {:?}",
|
||||
port_id,
|
||||
port_status
|
||||
);
|
||||
|
||||
if !port_status.port_reset_change() {
|
||||
mammoth::debug!("Unknown port status event, not handling.");
|
||||
return;
|
||||
}
|
||||
self.operational
|
||||
.update_port_status(port_index, |s| s.clear_change_bits());
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
mammoth::debug!("Enabling slot.");
|
||||
|
||||
let resp = self.send_command(TrbEnableSlotCommand::new()).await;
|
||||
assert!(resp.completion_code() == CommandCompletionCode::Success.into_bits());
|
||||
}
|
||||
}
|
||||
75
rust/sys/voyageurs/src/xhci/event_ring.rs
Normal file
75
rust/sys/voyageurs/src/xhci/event_ring.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
use alloc::vec::Vec;
|
||||
|
||||
use crate::xhci::{
|
||||
data_structures::{EventRingSegmentTable, TransferRequestBlock, TrbRingSegment, TrbType},
|
||||
registers::InterrupterRegisterSet,
|
||||
trb_ring::TrbPointer,
|
||||
};
|
||||
|
||||
pub struct EventRing {
|
||||
segment_table: EventRingSegmentTable,
|
||||
segments: Vec<TrbRingSegment>,
|
||||
cycle_bit: bool,
|
||||
trb_pointer: TrbPointer,
|
||||
}
|
||||
|
||||
impl EventRing {
|
||||
pub fn new() -> Self {
|
||||
// Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it
|
||||
// to ‘1’...
|
||||
let cycle_bit = true;
|
||||
let mut event_ring = Self {
|
||||
segment_table: EventRingSegmentTable::new(1),
|
||||
segments: [TrbRingSegment::new(100)].into(),
|
||||
cycle_bit,
|
||||
trb_pointer: TrbPointer::default(),
|
||||
};
|
||||
|
||||
event_ring.segment_table[0].from_trb_ring(&event_ring.segments[0]);
|
||||
|
||||
event_ring
|
||||
}
|
||||
|
||||
pub fn segment_table(&self) -> &EventRingSegmentTable {
|
||||
&self.segment_table
|
||||
}
|
||||
|
||||
pub fn erdp_physical_address(&self) -> usize {
|
||||
self.segments[self.trb_pointer.segment_index].physical_address()
|
||||
+ self.trb_pointer.segment_physical_offset()
|
||||
}
|
||||
|
||||
fn current_trb(&self) -> TransferRequestBlock {
|
||||
// TODO: These should be volatile reads.
|
||||
self.segments[self.trb_pointer.segment_index][self.trb_pointer.segment_offset]
|
||||
}
|
||||
|
||||
fn increment_pointer(&mut self) {
|
||||
self.trb_pointer.segment_offset += 1;
|
||||
|
||||
if self.trb_pointer.segment_offset == self.segments[self.trb_pointer.segment_index].len() {
|
||||
self.trb_pointer.segment_index += 1;
|
||||
self.trb_pointer.segment_offset = 0;
|
||||
|
||||
if self.trb_pointer.segment_index == self.segments.len() {
|
||||
// Wrap around to front.
|
||||
self.trb_pointer.segment_index = 0;
|
||||
self.cycle_bit = !self.cycle_bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_next(&mut self) -> Option<TransferRequestBlock> {
|
||||
let curr = self.current_trb();
|
||||
if curr.cycle() != self.cycle_bit {
|
||||
None
|
||||
} else {
|
||||
self.increment_pointer();
|
||||
Some(curr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_dequeue_pointer(&self, interrupter: &mut InterrupterRegisterSet) {
|
||||
interrupter.update_dequeue_pointer(self.erdp_physical_address());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +1,7 @@
|
|||
pub mod data_structures;
|
||||
pub mod registers;
|
||||
mod data_structures;
|
||||
mod device_context_base_array;
|
||||
pub mod driver;
|
||||
mod event_ring;
|
||||
mod registers;
|
||||
mod trb_ring;
|
||||
|
||||
|
|
|
|||
|
|
@ -321,6 +321,7 @@ pub struct HCCParams2 {
|
|||
///
|
||||
/// These registers are located at the addresses specified in BAR0 and BAR1 in the PCI Header.
|
||||
#[repr(C, packed)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct HostControllerCapabilities {
|
||||
pub cap_length_and_version: HostControllerCapabilitiesLengthAndVersion,
|
||||
pub params_1: HCSParams1,
|
||||
|
|
|
|||
|
|
@ -63,3 +63,19 @@ pub struct Doorbell {
|
|||
/// This field returns ‘0’ when read
|
||||
db_stream_id: u16,
|
||||
}
|
||||
|
||||
impl Doorbell {
|
||||
pub fn ring(&mut self, target: u8) {
|
||||
// SAFETY:
|
||||
// - We know this is a valid reference.
|
||||
unsafe {
|
||||
core::ptr::write_volatile(
|
||||
self as *mut _,
|
||||
Doorbell::new().with_db_target(target).with_db_stream_id(0),
|
||||
);
|
||||
}
|
||||
}
|
||||
pub fn ring_command(&mut self) {
|
||||
self.ring(0)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,13 @@
|
|||
use core::ptr::NonNull;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use bitfield_struct::bitfield;
|
||||
use mammoth::{mem::map_direct_physical_and_leak, sync::Mutex};
|
||||
use volatile::{VolatilePtr, VolatileRef, map_field};
|
||||
|
||||
use crate::xhci::registers::{
|
||||
HostControllerCapabilities, HostControllerUsbPort, PortStatusAndControl,
|
||||
};
|
||||
|
||||
#[bitfield(u32)]
|
||||
pub struct UsbCommand {
|
||||
|
|
@ -273,6 +282,7 @@ pub struct UsbConfigure {
|
|||
/// of the Capability Registers Length (CAPLENGTH) register (refer to Section 5.3.1)
|
||||
/// to the Capability Base address. All registers are multiples of 32 bits in length
|
||||
#[repr(C, packed)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct HostControllerOperational {
|
||||
pub usb_command: UsbCommand,
|
||||
pub usb_status: UsbStatus,
|
||||
|
|
@ -296,3 +306,103 @@ pub struct HostControllerOperational {
|
|||
}
|
||||
|
||||
const _: () = assert!(size_of::<HostControllerOperational>() == 0x3C);
|
||||
|
||||
pub struct HostControllerOperationalWrapper {
|
||||
// TODO: Fix alignment of this type so we can do more targetted reads and writes.
|
||||
operational: Mutex<VolatileRef<'static, HostControllerOperational>>,
|
||||
// TODO: This should maybe be its own structure.
|
||||
ports: Vec<Mutex<VolatileRef<'static, HostControllerUsbPort>>>,
|
||||
}
|
||||
|
||||
impl HostControllerOperationalWrapper {
|
||||
pub fn new(mmio_address: usize) -> (Self, HostControllerCapabilities) {
|
||||
const MAP_SIZE: usize = 0x1000;
|
||||
let caps_ptr: *mut HostControllerCapabilities =
|
||||
map_direct_physical_and_leak(mmio_address as u64, MAP_SIZE as u64);
|
||||
|
||||
// SAFETY:
|
||||
// - The pointer is valid.
|
||||
// - No other thread has access in this block.
|
||||
let capabilities = unsafe {
|
||||
VolatilePtr::new(
|
||||
// UNWRAP: We just constructed this object with a non-null value.
|
||||
NonNull::new(caps_ptr).unwrap(),
|
||||
)
|
||||
.read()
|
||||
};
|
||||
|
||||
let cap_length_and_version = capabilities.cap_length_and_version;
|
||||
// TODO: I don't think we acutally handle this properly.
|
||||
// SAFETY: XHCI Spec says that this resides in a single page of memory which we mapped
|
||||
// above.
|
||||
//
|
||||
// BAR0 Size Allocation
|
||||
// If virtualization is supported, the Capability and Operational Register sets, and
|
||||
// the Extended Capabilities may reside in a single page of virtual memory,
|
||||
let operational_ptr = unsafe {
|
||||
(caps_ptr as *mut u8).add(cap_length_and_version.cap_length() as usize)
|
||||
as *mut HostControllerOperational
|
||||
};
|
||||
|
||||
const PORT_OFFSET: usize = 0x400;
|
||||
|
||||
// FIXME: This calculation is cursed.
|
||||
let ports_addr = unsafe { (operational_ptr as *mut u8).add(PORT_OFFSET) as usize };
|
||||
let ports_space = MAP_SIZE - cap_length_and_version.cap_length() as usize - PORT_OFFSET;
|
||||
let max_ports_we_support = ports_space / size_of::<HostControllerUsbPort>();
|
||||
let params_1 = capabilities.params_1;
|
||||
assert!(
|
||||
params_1.max_ports() as usize <= max_ports_we_support,
|
||||
"TODO: Support more ports."
|
||||
);
|
||||
|
||||
let mut ports = Vec::new();
|
||||
let ports_addr = ports_addr as *mut HostControllerUsbPort;
|
||||
for port_index in 0..params_1.max_ports() {
|
||||
ports.push(unsafe {
|
||||
Mutex::new(VolatileRef::new(
|
||||
NonNull::new(ports_addr.add(port_index as usize)).unwrap(),
|
||||
))
|
||||
});
|
||||
}
|
||||
|
||||
let operational = Self {
|
||||
operational: Mutex::new(unsafe {
|
||||
VolatileRef::new(NonNull::new(operational_ptr).unwrap())
|
||||
}),
|
||||
ports,
|
||||
};
|
||||
|
||||
(operational, capabilities)
|
||||
}
|
||||
|
||||
pub fn update(&self, f: impl Fn(HostControllerOperational) -> HostControllerOperational) {
|
||||
self.operational.lock().as_mut_ptr().update(f);
|
||||
}
|
||||
|
||||
pub fn status(&self) -> UsbStatus {
|
||||
self.operational.lock().as_ptr().read().usb_status
|
||||
}
|
||||
|
||||
pub fn command(&self) -> UsbCommand {
|
||||
self.operational.lock().as_ptr().read().usb_command
|
||||
}
|
||||
|
||||
pub fn get_port(&self, index: usize) -> HostControllerUsbPort {
|
||||
self.ports[index].lock().as_ptr().read()
|
||||
}
|
||||
|
||||
pub fn update_port_status(
|
||||
&self,
|
||||
index: usize,
|
||||
f: impl Fn(PortStatusAndControl) -> PortStatusAndControl,
|
||||
) {
|
||||
let mut port_ref = self.ports[index].lock();
|
||||
let ptr = port_ref.as_mut_ptr();
|
||||
map_field!(ptr.status_and_control).update(f);
|
||||
}
|
||||
|
||||
pub fn num_ports(&self) -> usize {
|
||||
self.ports.len()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,13 +9,15 @@ use bitfield_struct::bitfield;
|
|||
/// XHCI Spec 5.4.8
|
||||
#[bitfield(u32)]
|
||||
pub struct PortStatusAndControl {
|
||||
/// A host controller shall implement one or more port registers. The number of
|
||||
/// port registers implemented by a particular instantiation of a host controller is
|
||||
/// documented in the HCSPARAMS1 register (Section 5.3.3). Software uses this
|
||||
/// information as an input parameter to determine how many ports need to be
|
||||
/// serviced. All ports have the structure defined below.
|
||||
/// Current Connect Status (CCS) – ROS. Default = ‘0’. ‘1’ = A device is connected81 to the port. ‘0’ =
|
||||
/// A device is not connected. This value reflects the current state of the port, and may not
|
||||
/// correspond directly to the event that caused the Connect Status Change (CSC) bit to be set to ‘1’.
|
||||
///
|
||||
/// Refer to sections 4.19.3 and 4.19.4 for more details on the Connect Status Change (CSC)
|
||||
/// assertion conditions.
|
||||
/// This flag is ‘0’ if PP is ‘0’.
|
||||
#[bits(access=RO)]
|
||||
current_connect_status: bool,
|
||||
pub current_connect_status: bool,
|
||||
|
||||
/// Port Enabled/Disabled (PED) – RW1CS. Default = ‘0’. ‘1’ = Enabled. ‘0’ = Disabled.
|
||||
/// Ports may only be enabled by the xHC. Software cannot enable a port by writing a ‘1’ to this flag.
|
||||
|
|
@ -43,9 +45,9 @@ pub struct PortStatusAndControl {
|
|||
/// from ‘1’ to ‘0’ after a successful reset. Refer to Port Reset (PR) bit for more information on how
|
||||
/// the PED bit is managed.
|
||||
///
|
||||
/// Note that when software writes this bit to a ‘1’, it shall also write a ‘0’ to the PR bit82.
|
||||
/// Note that when software writes this bit to a ‘1’, it shall also write a ‘0’ to the PR bit.
|
||||
/// This flag is ‘0’ if PP is ‘0’.
|
||||
port_enabled_disabled: bool,
|
||||
pub port_enabled_disabled: bool,
|
||||
|
||||
__: bool,
|
||||
|
||||
|
|
@ -53,7 +55,7 @@ pub struct PortStatusAndControl {
|
|||
/// condition. ‘0’ = This port does not have an over-current condition. This bit shall automatically
|
||||
/// transition from a ‘1’ to a ‘0’ when the over-current condition is removed.
|
||||
#[bits(access=RO)]
|
||||
over_current_active: bool,
|
||||
pub over_current_active: bool,
|
||||
|
||||
/// Port Reset (PR) – RW1S. Default = ‘0’. ‘1’ = Port Reset signaling is asserted. ‘0’ = Port is not in
|
||||
/// Reset. When software writes a ‘1’ to this bit generating a ‘0’ to ‘1’ transition, the bus reset
|
||||
|
|
@ -65,7 +67,7 @@ pub struct PortStatusAndControl {
|
|||
/// the Enabled state. Refer to sections 4.15.2.3 and 4.19.1.1.
|
||||
///
|
||||
/// This flag is ‘0’ if PP is ‘0’.
|
||||
port_reset: bool,
|
||||
pub port_reset: bool,
|
||||
|
||||
/// Port Link State (PLS) – RWS. Default = RxDetect (‘5’). This field is used to power manage the port
|
||||
/// and reflects its current link state.
|
||||
|
|
@ -128,7 +130,7 @@ pub struct PortStatusAndControl {
|
|||
/// USB2 LPM ECR for more information on USB link power management operation. Refer to section
|
||||
/// 7.2 for supported USB protocols
|
||||
#[bits(4)]
|
||||
port_link_status: u8,
|
||||
pub port_link_status: u8,
|
||||
/// Port Power (PP) – RWS. Default = ‘1’. This flag reflects a port's logical, power control state.
|
||||
/// Because host controllers can implement different methods of port power switching, this flag may
|
||||
/// or may not represent whether (VBus) power is actually applied to the port. When PP equals a '0'
|
||||
|
|
@ -156,7 +158,7 @@ pub struct PortStatusAndControl {
|
|||
///
|
||||
/// Refer to section 5.1.2 in the SSIC Spec for more information.
|
||||
/// Refer to section 4.19.4 for more information.
|
||||
port_power: bool,
|
||||
pub port_power: bool,
|
||||
/// Port Speed (Port Speed) – ROS. Default = ‘0’. This field identifies the speed of the connected
|
||||
/// USB Device. This field is only relevant if a device is connected (CCS = ‘1’) in all other cases this
|
||||
/// field shall indicate Undefined Speed. Refer to section 4.19.3.
|
||||
|
|
@ -168,7 +170,7 @@ pub struct PortStatusAndControl {
|
|||
///
|
||||
/// Note: This field is invalid on a USB2 protocol port until after the port is reset.
|
||||
#[bits(4)]
|
||||
port_speed: u8,
|
||||
pub port_speed: u8,
|
||||
/// Port Indicator Control (PIC) – RWS. Default = 0. Writing to these bits has no effect if the Port
|
||||
/// Indicators (PIND) bit in the HCCPARAMS1 register is a ‘0’. If PIND bit is a ‘1’, then the bit
|
||||
/// encodings are:
|
||||
|
|
@ -182,11 +184,11 @@ pub struct PortStatusAndControl {
|
|||
/// Refer to the USB2 Specification section 11.5.3 for a description on how these bits shall be used.
|
||||
/// This field is ‘0’ if PP is ‘0’
|
||||
#[bits(2)]
|
||||
port_indicator_control: u8,
|
||||
pub port_indicator_control: u8,
|
||||
/// Port Link State Write Strobe (LWS) – RW. Default = ‘0’. When this bit is set to ‘1’ on a write
|
||||
/// reference to this register, this flag enables writes to the PLS field. When ‘0’, write data in PLS field
|
||||
/// is ignored. Reads to this bit return ‘0’
|
||||
port_link_state_write_strobe: bool,
|
||||
pub port_link_state_write_strobe: bool,
|
||||
/// Connect Status Change (CSC) – RW1CS. Default = ‘0’. ‘1’ = Change in CCS. ‘0’ = No change. This
|
||||
/// flag indicates a change has occurred in the port’s Current Connect Status (CCS) or Cold Attach
|
||||
/// Status (CAS) bits. Note that this flag shall not be set if the CCS transition was due to software
|
||||
|
|
@ -196,7 +198,7 @@ pub struct PortStatusAndControl {
|
|||
/// before system software has cleared the changed condition, root hub hardware will be “setting”
|
||||
/// an already-set bit (i.e., the bit will remain ‘1’). Software shall clear this bit by writing a ‘1’ to it.
|
||||
/// Refer to section 4.19.2 for more information on change bit usage.
|
||||
connect_status_change: bool,
|
||||
pub connect_status_change: bool,
|
||||
/// Port Enabled/Disabled Change (PEC) – RW1CS. Default = ‘0’. ‘1’ = change in PED. ‘0’ = No
|
||||
/// change. Note that this flag shall not be set if the PED transition was due to software setting PP to
|
||||
/// ‘0’. Software shall clear this bit by writing a ‘1’ to it. Refer to section 4.19.2 for more information
|
||||
|
|
@ -207,7 +209,7 @@ pub struct PortStatusAndControl {
|
|||
///
|
||||
/// Specification for the definition of a Port Error).
|
||||
/// For a USB3 protocol port, this bit shall never be set to ‘1’.
|
||||
port_enabled_disabled_change: bool,
|
||||
pub port_enabled_disabled_change: bool,
|
||||
/// Warm Port Reset Change (WRC) – RW1CS/RsvdZ. Default = ‘0’. This bit is set when Warm Reset
|
||||
/// processing on this port completes. ‘0’ = No change. ‘1’ = Warm Reset complete. Note that this
|
||||
/// flag shall not be set to ‘1’ if the Warm Reset processing was forced to terminate due to software
|
||||
|
|
@ -215,18 +217,18 @@ pub struct PortStatusAndControl {
|
|||
/// Refer to section 4.19.2 for more information on change bit usage.
|
||||
///
|
||||
/// This bit only applies to USB3 protocol ports. For USB2 protocol ports it shall be RsvdZ.
|
||||
warm_port_reset_change: bool,
|
||||
pub warm_port_reset_change: bool,
|
||||
/// Over-current Change (OCC) – RW1CS. Default = ‘0’. This bit shall be set to a ‘1’ when there is a ‘0’
|
||||
/// to ‘1’ or ‘1’ to ‘0’ transition of Over-current Active (OCA). Software shall clear this bit by writing a
|
||||
/// ‘1’ to it. Refer to section 4.19.2 for more information on change bit usage.
|
||||
over_current_change: bool,
|
||||
pub over_current_change: bool,
|
||||
/// Port Reset Change (PRC) – RW1CS. Default = ‘0’. This flag is set to ‘1’ due to a '1' to '0' transition
|
||||
/// of Port Reset (PR). e.g. when any reset processing (Warm or Hot) on this port is complete. Note
|
||||
/// that this flag shall not be set to ‘1’ if the reset processing was forced to terminate due to software
|
||||
/// clearing PP or PED to '0'. ‘0’ = No change. ‘1’ = Reset complete. Software shall clear this bit by
|
||||
/// writing a '1' to it. Refer to section 4.19.5. Refer to section 4.19.2 for more information on change
|
||||
/// bit usage
|
||||
port_reset_change: bool,
|
||||
pub port_reset_change: bool,
|
||||
/// Port Link State Change (PLC) – RW1CS. Default = ‘0’. This flag is set to ‘1’ due to the following
|
||||
/// PLS transitions:
|
||||
///
|
||||
|
|
@ -256,7 +258,7 @@ pub struct PortStatusAndControl {
|
|||
/// writing a '1' to it. Refer to “PLC Condition:” references in section 4.19.1
|
||||
/// for the specific port state transitions that set this flag. Refer to section
|
||||
/// 4.19.2 for more information on change bit usage.
|
||||
port_link_state_change: bool,
|
||||
pub port_link_state_change: bool,
|
||||
/// Port Config Error Change (CEC) – RW1CS/RsvdZ. Default = ‘0’. This flag indicates that the port
|
||||
/// failed to configure its link partner. 0 = No change. 1 = Port Config Error detected. Software shall
|
||||
/// clear this bit by writing a '1' to it. Refer to section 4.19.2 for more information on change bit
|
||||
|
|
@ -264,7 +266,7 @@ pub struct PortStatusAndControl {
|
|||
///
|
||||
/// Note: This flag is valid only for USB3 protocol ports. For USB2 protocol ports this bit shall be
|
||||
/// RsvdZ.
|
||||
port_config_error_change: bool,
|
||||
pub port_config_error_change: bool,
|
||||
/// Cold Attach Status (CAS) – RO. Default = ‘0’. ‘1’ = Far-end Receiver Terminations were detected
|
||||
/// in the Disconnected state and the Root Hub Port State Machine was unable to advance to the
|
||||
/// Enabled state. Refer to sections 4.19.8 for more details on the Cold Attach Status (CAS) assertion
|
||||
|
|
@ -272,31 +274,48 @@ pub struct PortStatusAndControl {
|
|||
/// transitions to ‘1’.
|
||||
/// This flag is ‘0’ if PP is ‘0’ or for USB2 protocol ports
|
||||
#[bits(access=RO)]
|
||||
cold_attach_status: bool,
|
||||
pub cold_attach_status: bool,
|
||||
/// Wake on Connect Enable (WCE) – RWS. Default = ‘0’. Writing this bit to a ‘1’ enables the port to
|
||||
/// be sensitive to device connects as system wake-up events96. Refer to section 4.15 for operational
|
||||
/// model.
|
||||
wake_on_connect_enable: bool,
|
||||
pub wake_on_connect_enable: bool,
|
||||
/// Wake on Disconnect Enable (WDE) – RWS. Default = ‘0’. Writing this bit to a ‘1’ enables the port
|
||||
/// to be sensitive to device disconnects as system wake-up events. Refer to section 4.15 for
|
||||
/// operational model.
|
||||
wake_on_disconnect_enable: bool,
|
||||
pub wake_on_disconnect_enable: bool,
|
||||
/// Wake on Over-current Enable (WOE) – RWS. Default = ‘0’. Writing this bit to a ‘1’ enables the
|
||||
/// port to be sensitive to over-current conditions as system wake-up events96. Refer to section 4.15
|
||||
/// for operational model.
|
||||
wake_on_overcurrent_enable: bool,
|
||||
pub wake_on_overcurrent_enable: bool,
|
||||
__: bool,
|
||||
__: bool,
|
||||
/// Device Removable97 (DR) - RO. This flag indicates if this port has a removable device attached.
|
||||
/// ‘1’ = Device is non-removable. ‘0’ = Device is removable.
|
||||
#[bits(access=RO)]
|
||||
device_removable: bool,
|
||||
pub device_removable: bool,
|
||||
/// Warm Port Reset (WPR) – RW1S/RsvdZ. Default = ‘0’. When software writes a ‘1’ to this bit, the
|
||||
/// Warm Reset sequence as defined in the USB3 Specification is initiated and the PR flag is set to ‘1’.
|
||||
/// Once initiated, the PR, PRC, and WRC flags shall reflect the progress of the Warm Reset
|
||||
/// sequence. This flag shall always return ‘0’ when read. Refer to section 4.19.5.1.
|
||||
/// This flag only applies to USB3 protocol ports. For USB2 protocol ports it shall be RsvdZ.
|
||||
warm_port_reset: bool,
|
||||
pub warm_port_reset: bool,
|
||||
}
|
||||
|
||||
impl PortStatusAndControl {
|
||||
pub fn clear_change_bits(&self) -> Self {
|
||||
PortStatusAndControl::new()
|
||||
.with_connect_status_change(true)
|
||||
.with_port_enabled_disabled_change(true)
|
||||
.with_warm_port_reset_change(true)
|
||||
.with_over_current_change(true)
|
||||
.with_port_reset_change(true)
|
||||
.with_port_link_state_change(true)
|
||||
.with_port_config_error_change(true)
|
||||
.with_port_power(self.port_power())
|
||||
.with_wake_on_connect_enable(self.wake_on_connect_enable())
|
||||
.with_wake_on_disconnect_enable(self.wake_on_disconnect_enable())
|
||||
.with_wake_on_overcurrent_enable(self.wake_on_overcurrent_enable())
|
||||
}
|
||||
}
|
||||
|
||||
/// XHCI Spec 5.4.9
|
||||
|
|
@ -384,12 +403,13 @@ pub struct PortLinkInfo {
|
|||
/// from the HostControllerOperation address.
|
||||
///
|
||||
/// Where MAX_PORTS is HostControllerCapabilities.params_1.max_ports
|
||||
#[repr(C, packed)]
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct HostControllerUsbPort {
|
||||
status_and_control: PortStatusAndControl,
|
||||
power_management_status_and_control: PortPowerManagementStatusAndControl,
|
||||
link_info: PortLinkInfo,
|
||||
hardware_lpm_control: u32,
|
||||
pub status_and_control: PortStatusAndControl,
|
||||
pub power_management_status_and_control: PortPowerManagementStatusAndControl,
|
||||
pub link_info: PortLinkInfo,
|
||||
pub hardware_lpm_control: u32,
|
||||
}
|
||||
|
||||
const _: () = assert!(size_of::<HostControllerUsbPort>() == 0x10);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use bitfield_struct::bitfield;
|
||||
|
||||
use crate::xhci::data_structures::EventRingSegmentTable;
|
||||
|
||||
/// The Interrupter Management register allows system software to enable, disable,
|
||||
/// and detect xHC interrupts.
|
||||
///
|
||||
|
|
@ -49,16 +51,6 @@ pub struct InterrupterModeration {
|
|||
/// XHCI 5.5.2.3.1
|
||||
#[bitfield(u32)]
|
||||
pub struct EventRingSegmentTableSize {
|
||||
/// Event Ring Segment Table Size – RW. Default = ‘0’. This field identifies the number of valid
|
||||
/// Event Ring Segment Table entries in the Event Ring Segment Table pointed to by the Event Ring
|
||||
/// Segment Table Base Address register. The maximum value supported by an xHC
|
||||
/// implementation for this register is defined by the ERST Max field in the HCSPARAMS2 register
|
||||
/// (5.3.4).
|
||||
/// For Secondary Interrupters: Writing a value of ‘0’ to this field disables the Event Ring. Any events
|
||||
/// targeted at this Event Ring when it is disabled shall result in undefined behavior of the Event
|
||||
/// Ring.
|
||||
/// For the Primary Interrupter: Writing a value of ‘0’ to this field shall result in undefined behavior
|
||||
/// of the Event Ring. The Primary Event Ring cannot be disabled.
|
||||
pub event_ring_segment_table_size: u16,
|
||||
_reserved: u16,
|
||||
}
|
||||
|
|
@ -98,9 +90,18 @@ pub struct EventRingDequePointer {
|
|||
pub struct InterrupterRegisterSet {
|
||||
pub interrupter_management: InterrupterManagement,
|
||||
pub interrupter_moderation: InterrupterModeration,
|
||||
pub event_ring_segement_table_size: EventRingSegmentTableSize,
|
||||
_reserved: u32,
|
||||
|
||||
/// Event Ring Segment Table Size – RW. Default = ‘0’. This field identifies the number of valid
|
||||
/// Event Ring Segment Table entries in the Event Ring Segment Table pointed to by the Event Ring
|
||||
/// Segment Table Base Address register. The maximum value supported by an xHC
|
||||
/// implementation for this register is defined by the ERST Max field in the HCSPARAMS2 register
|
||||
/// (5.3.4).
|
||||
/// For Secondary Interrupters: Writing a value of ‘0’ to this field disables the Event Ring. Any events
|
||||
/// targeted at this Event Ring when it is disabled shall result in undefined behavior of the Event
|
||||
/// Ring.
|
||||
/// For the Primary Interrupter: Writing a value of ‘0’ to this field shall result in undefined behavior
|
||||
/// of the Event Ring. The Primary Event Ring cannot be disabled.
|
||||
event_ring_segment_table_size: u32,
|
||||
___: u32,
|
||||
/// Event Ring Segment Table Base Address Register – RW. Default = ‘0’. This field defines the
|
||||
/// high order bits of the start address of the Event Ring Segment Table.
|
||||
/// Writing this register sets the Event Ring State Machine:EREP Advancement to the Start state.
|
||||
|
|
@ -111,6 +112,54 @@ pub struct InterrupterRegisterSet {
|
|||
/// NOTE: This must be aligned such that bits 0:5 are 0.
|
||||
///
|
||||
/// XHCI 5.5.2.3.2
|
||||
pub event_ring_segment_table_base_address: u64,
|
||||
pub event_ring_deque_pointer: u64,
|
||||
event_ring_segment_table_base_address: u64,
|
||||
event_ring_deque_pointer: u64,
|
||||
}
|
||||
|
||||
impl InterrupterRegisterSet {
|
||||
/// SAFETY:
|
||||
/// - For the primary interrupter HC must be halted.
|
||||
/// - The event rings size must be at most ERST_MAX from HCSPARAMS2
|
||||
pub unsafe fn set_event_ring(
|
||||
&mut self,
|
||||
event_ring_segment_table: &EventRingSegmentTable,
|
||||
event_ring_dequeue_pointer: usize,
|
||||
) {
|
||||
// NOTE: We must write the size before the base address otherwise qemu is unhappy.
|
||||
// Not sure if this is required by the spec.
|
||||
|
||||
// SAFETY:
|
||||
// - We know this address is valid and we have a mut reference to it.
|
||||
unsafe {
|
||||
core::ptr::write_volatile(
|
||||
core::ptr::addr_of!(self.event_ring_segment_table_size) as *mut _,
|
||||
event_ring_segment_table.len() as u32,
|
||||
);
|
||||
|
||||
core::ptr::write_volatile(
|
||||
core::ptr::addr_of!(self.event_ring_segment_table_base_address) as *mut _,
|
||||
event_ring_segment_table.physical_address(),
|
||||
);
|
||||
|
||||
core::ptr::write_volatile(
|
||||
core::ptr::addr_of!(self.event_ring_deque_pointer) as *mut _,
|
||||
event_ring_dequeue_pointer,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_dequeue_pointer(&mut self, event_ring_dequeue_pointer: usize) {
|
||||
// SAFETY:
|
||||
// - We know this address is valid and we have a mut pointer to it.
|
||||
unsafe {
|
||||
// TODO: Preserve lower bits, also update it to make it clear that we
|
||||
// are clearing the EHB bit.
|
||||
core::ptr::write_volatile(
|
||||
core::ptr::addr_of!(self.event_ring_deque_pointer) as *mut _,
|
||||
event_ring_dequeue_pointer | (1 << 3),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const _: () = assert!(size_of::<InterrupterRegisterSet>() == 0x20);
|
||||
|
|
|
|||
163
rust/sys/voyageurs/src/xhci/trb_ring.rs
Normal file
163
rust/sys/voyageurs/src/xhci/trb_ring.rs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
use core::task::{Poll, Waker};
|
||||
|
||||
use alloc::{collections::vec_deque::VecDeque, sync::Arc, vec::Vec};
|
||||
use mammoth::sync::Mutex;
|
||||
|
||||
use crate::xhci::data_structures::{TransferRequestBlock, TrbLink, TrbRingSegment, TypedTrb};
|
||||
|
||||
struct TrbFutureState<T> {
|
||||
/// Physical Address for the enqueued TRB.
|
||||
/// Used for sanity checking.
|
||||
physical_address: usize,
|
||||
|
||||
waker: Option<Waker>,
|
||||
response: Option<T>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TrbFuture<T: TypedTrb> {
|
||||
state: Arc<Mutex<TrbFutureState<T>>>,
|
||||
}
|
||||
|
||||
impl<T: TypedTrb> TrbFuture<T> {
|
||||
fn new(paddr: usize) -> Self {
|
||||
Self {
|
||||
state: Arc::new(Mutex::new(TrbFutureState {
|
||||
physical_address: paddr,
|
||||
waker: None,
|
||||
response: None,
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TypedTrb> Future for TrbFuture<T> {
|
||||
type Output = T;
|
||||
|
||||
fn poll(
|
||||
self: core::pin::Pin<&mut Self>,
|
||||
cx: &mut core::task::Context<'_>,
|
||||
) -> core::task::Poll<Self::Output> {
|
||||
let mut state = self.state.lock();
|
||||
match state.response {
|
||||
Some(trb) => Poll::Ready(trb),
|
||||
None => {
|
||||
state.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone, Debug)]
|
||||
pub struct TrbPointer {
|
||||
/// Index into the vector of trb segments.
|
||||
pub segment_index: usize,
|
||||
/// Index into the specific segment.
|
||||
/// This is a TransferRequestBlock index,
|
||||
/// to get the physical_offset use segment_physical_offset()
|
||||
pub segment_offset: usize,
|
||||
}
|
||||
|
||||
impl TrbPointer {
|
||||
pub fn segment_physical_offset(&self) -> usize {
|
||||
self.segment_offset * size_of::<TransferRequestBlock>()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TrbRing<T: TypedTrb> {
|
||||
segments: Vec<TrbRingSegment>,
|
||||
enqueue_pointer: TrbPointer,
|
||||
cycle_bit: bool,
|
||||
pending_futures: VecDeque<TrbFuture<T>>,
|
||||
}
|
||||
|
||||
impl<T: TypedTrb> TrbRing<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
// TODO: What size and count should this be.
|
||||
segments: alloc::vec![TrbRingSegment::new(100)],
|
||||
enqueue_pointer: TrbPointer::default(),
|
||||
// Start with this as true so we are flipping bits from 0 (default) to 1
|
||||
// to mark the enqueue pointer.
|
||||
cycle_bit: true,
|
||||
pending_futures: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn physical_base_address(&self) -> usize {
|
||||
self.segments[0].physical_address()
|
||||
}
|
||||
|
||||
fn physical_address_of_enqueue_pointer(&self) -> usize {
|
||||
self.segments[self.enqueue_pointer.segment_index].physical_address()
|
||||
+ self.enqueue_pointer.segment_physical_offset()
|
||||
}
|
||||
|
||||
pub fn enqueue_trb(&mut self, trb: TransferRequestBlock) -> TrbFuture<T> {
|
||||
let paddr = self.physical_address_of_enqueue_pointer();
|
||||
*self.next_trb_ref() = trb.with_cycle(self.cycle_bit);
|
||||
self.advance_enqueue_pointer();
|
||||
let future = TrbFuture::new(paddr);
|
||||
self.pending_futures.push_back(future.clone());
|
||||
future
|
||||
}
|
||||
|
||||
fn next_trb_ref(&mut self) -> &mut TransferRequestBlock {
|
||||
&mut self.segments[self.enqueue_pointer.segment_index][self.enqueue_pointer.segment_offset]
|
||||
}
|
||||
|
||||
fn advance_enqueue_pointer(&mut self) {
|
||||
self.enqueue_pointer.segment_offset += 1;
|
||||
|
||||
if self.enqueue_pointer.segment_offset
|
||||
== self.segments[self.enqueue_pointer.segment_index].len() - 1
|
||||
{
|
||||
// We have reached the end of the segment, insert a link trb.
|
||||
|
||||
// Increment the segment index with wrapping.
|
||||
let next_segment_index =
|
||||
if self.enqueue_pointer.segment_index + 1 == self.segments.len() {
|
||||
0
|
||||
} else {
|
||||
self.enqueue_pointer.segment_index + 1
|
||||
};
|
||||
|
||||
let next_segment_pointer = self.segments[next_segment_index].physical_address();
|
||||
let toggle_cycle = next_segment_index == 0;
|
||||
|
||||
*self.next_trb_ref() = TrbLink::new()
|
||||
.with_ring_segment_pointer(next_segment_pointer as u64)
|
||||
.with_cycle(self.cycle_bit)
|
||||
.with_toggle_cycle(toggle_cycle)
|
||||
.to_trb();
|
||||
|
||||
// Flip toggle cycle bit if necessary.
|
||||
self.cycle_bit ^= toggle_cycle;
|
||||
|
||||
self.enqueue_pointer = TrbPointer {
|
||||
segment_index: next_segment_index,
|
||||
segment_offset: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_commpletion(&mut self, completion_trb: T) {
|
||||
let trb = completion_trb.to_trb();
|
||||
let paddr = trb.parameter() as usize;
|
||||
let completion = self.pending_futures.pop_front().unwrap();
|
||||
let mut completion = completion.state.lock();
|
||||
// TODO: Handle recovery scenarios here.
|
||||
assert!(
|
||||
completion.physical_address == paddr,
|
||||
"Got an unexpected command completion. Expected: {:0x}, Got: {:0x}",
|
||||
completion.physical_address,
|
||||
paddr
|
||||
);
|
||||
completion.response = Some(completion_trb);
|
||||
|
||||
if let Some(waker) = &completion.waker {
|
||||
waker.wake_by_ref();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue