Rust XHCI Register Types.
This commit is contained in:
parent
b9cd550a63
commit
22b43de6dc
10 changed files with 1596 additions and 0 deletions
505
rust/sys/voyageurs/src/xhci/registers/host_controller.rs
Normal file
505
rust/sys/voyageurs/src/xhci/registers/host_controller.rs
Normal file
|
|
@ -0,0 +1,505 @@
|
|||
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 {
|
||||
/// Run/Stop (R/S) – RW. Default = ‘0’. ‘1’ = Run. ‘0’ = Stop. When set to a ‘1’, the xHC proceeds with
|
||||
/// execution of the schedule. The xHC continues execution as long as this bit is set to a ‘1’. When
|
||||
/// this bit is cleared to ‘0’, the xHC completes any current or queued commands or TDs, and any
|
||||
/// USB transactions associated with them, then halts.
|
||||
///
|
||||
/// Refer to section 5.4.1.1 for more information on how R/S shall be managed.
|
||||
///
|
||||
/// The xHC shall halt within 16 ms. after software clears the Run/Stop bit if the above conditions
|
||||
/// have been met.
|
||||
///
|
||||
/// The HCHalted (HCH) bit in the USBSTS register indicates when the xHC has finished its pending
|
||||
/// pipelined transactions and has entered the stopped state. Software shall not write a ‘1’ to this
|
||||
/// flag unless the xHC is in the Halted state (i.e. HCH in the USBSTS register is ‘1’). Doing so may
|
||||
/// yield undefined results. Writing a ‘0’ to this flag when the xHC is in the Running state (i.e. HCH =
|
||||
/// ‘0’) and any Event Rings are in the Event Ring Full state (refer to section 4.9.4) may result in lost
|
||||
/// events.
|
||||
///
|
||||
/// When this register is exposed by a Virtual Function (VF), this bit only controls the run state of
|
||||
/// the xHC instance presented by the selected VF. Refer to section 8 for more information.
|
||||
pub run_stop: bool,
|
||||
/// Host Controller Reset (HCRST) – RW. Default = ‘0’. This control bit is used by software to reset
|
||||
/// the host controller. The effects of this bit on the xHC and the Root Hub registers are similar to a
|
||||
/// Chip Hardware Reset.
|
||||
///
|
||||
/// When software writes a ‘1’ to this bit, the Host Controller resets its internal pipelines, timers,
|
||||
/// counters, state machines, etc. to their initial value. Any transaction currently in progress on the
|
||||
/// USB is immediately terminated. A USB reset shall not be driven on USB2 downstream ports,
|
||||
/// however a Hot or Warm Reset79 shall be initiated on USB3 Root Hub downstream ports.
|
||||
///
|
||||
/// PCI Configuration registers are not affected by this reset. All operational registers, including port
|
||||
/// registers and port state machines are set to their initial values. Software shall reinitialize the
|
||||
/// host controller as described in Section 4.2 in order to return the host controller to an
|
||||
/// operational state.
|
||||
///
|
||||
/// This bit is cleared to ‘0’ by the Host Controller when the reset process is complete. Software
|
||||
/// cannot terminate the reset process early by writing a ‘0’ to this bit and shall not write any xHC
|
||||
/// Operational or Runtime registers until while HCRST is ‘1’. Note, the completion of the xHC reset
|
||||
/// process is not gated by the Root Hub port reset process.
|
||||
///
|
||||
/// Software shall not set this bit to ‘1’ when the HCHalted (HCH) bit in the USBSTS register is a ‘0’.
|
||||
/// Attempting to reset an actively running host controller may result in undefined behavior.
|
||||
///
|
||||
/// When this register is exposed by a Virtual Function (VF), this bit only resets the xHC instance
|
||||
/// presented by the selected VF. Refer to section 8 for more information
|
||||
pub host_controller_reset: bool,
|
||||
/// Interrupter Enable (INTE) – RW. Default = ‘0’. This bit provides system software with a means of
|
||||
/// enabling or disabling the host system interrupts generated by Interrupters. When this bit is a ‘1’,
|
||||
/// then Interrupter host system interrupt generation is allowed, e.g. the xHC shall issue an interrupt
|
||||
/// at the next interrupt threshold if the host system interrupt mechanism (e.g. MSI, MSI-X, etc.) is
|
||||
/// enabled. The interrupt is acknowledged by a host system interrupt specific mechanism.
|
||||
///
|
||||
/// When this register is exposed by a Virtual Function (VF), this bit only enables the set of
|
||||
/// Interrupters assigned to the selected VF. Refer to section 7.7.2 for more information.
|
||||
pub interrupter_enable: bool,
|
||||
/// Host System Error Enable (HSEE) – RW. Default = ‘0’. When this bit is a ‘1’, and the HSE bit in
|
||||
/// the USBSTS register is a ‘1’, the xHC shall assert out-of-band error signaling to the host. The
|
||||
/// signaling is acknowledged by software clearing the HSE bit. Refer to section 4.10.2.6 for more
|
||||
/// information.
|
||||
/// When this register is exposed by a Virtual Function (VF), the effect of the assertion of this bit on
|
||||
/// the Physical Function (PF0) is determined by the VMM. Refer to section 8 for more information
|
||||
pub host_system_error_enable: bool,
|
||||
#[bits(3)]
|
||||
__: u8,
|
||||
/// Light Host Controller Reset (LHCRST) – RO or RW. Optional normative. Default = ‘0’. If the Light
|
||||
/// HC Reset Capability (LHRC) bit in the HCCPARAMS1 register is ‘1’, then this flag allows the driver
|
||||
/// to reset the xHC without affecting the state of the ports.
|
||||
///
|
||||
/// A system software read of this bit as ‘0’ indicates the Light Host Controller Reset has completed
|
||||
/// and it is safe for software to re-initialize the xHC. A software read of this bit as a ‘1’ indicates the
|
||||
/// Light Host Controller Reset has not yet completed.
|
||||
///
|
||||
/// If not implemented, a read of this flag shall always return a ‘0’.
|
||||
///
|
||||
/// All registers in the Aux Power well shall maintain the values that had been asserted prior to the
|
||||
/// Light Host Controller Reset. Refer to section 4.23.1 for more information.
|
||||
///
|
||||
/// When this register is exposed by a Virtual Function (VF), this bit only generates a Light Reset to
|
||||
/// the xHC instance presented by the selected VF, e.g. Disable the VFs’ device slots and set the
|
||||
/// associated VF Run bit to Stopped. Refer to section 8 for more information.
|
||||
pub light_host_controller_reset: bool,
|
||||
/// Controller Save State (CSS) - RW. Default = ‘0’. When written by software with ‘1’ and HCHalted
|
||||
/// (HCH) = ‘1’, then the xHC shall save any internal state (that may be restored by a subsequent
|
||||
/// Restore State operation) and if FSC = '1' any cached Slot, Endpoint, Stream, or other Context
|
||||
/// information (so that software may save it). When written by software with ‘1’ and HCHalted
|
||||
/// (HCH) = ‘0’, or written with ‘0’, no Save State operation shall be performed. This flag always
|
||||
/// returns ‘0’ when read. Refer to the Save State Status (SSS) flag in the USBSTS register for
|
||||
/// information on Save State completion. Refer to section 4.23.2 for more information on xHC
|
||||
///
|
||||
/// Save/Restore operation. Note that undefined behavior may occur if a Save State operation is
|
||||
/// initiated while Restore State Status (RSS) = ‘1’.
|
||||
///
|
||||
/// When this register is exposed by a Virtual Function (VF), this bit only controls saving the state of
|
||||
/// the xHC instance presented by the selected VF. Refer to section 8 for more information.
|
||||
pub controller_save_state: bool,
|
||||
/// Controller Restore State (CRS) - RW. Default = ‘0’. When set to ‘1’, and HCHalted (HCH) = ‘1’,
|
||||
/// then the xHC shall perform a Restore State operation and restore its internal state. When set to
|
||||
/// ‘1’ and Run/Stop (R/S) = ‘1’ or HCHalted (HCH) = ‘0’, or when cleared to ‘0’, no Restore State
|
||||
/// operation shall be performed. This flag always returns ‘0’ when read. Refer to the Restore State
|
||||
/// Status (RSS) flag in the USBSTS register for information on Restore State completion. Refer to
|
||||
/// section 4.23.2 for more information. Note that undefined behavior may occur if a Restore State
|
||||
/// operation is initiated while Save State Status (SSS) = ‘1’.
|
||||
/// When this register is exposed by a Virtual Function (VF), this bit only controls restoring the state
|
||||
/// of the xHC instance presented by the selected VF. Refer to section 8 for more information.
|
||||
pub controller_restore_state: bool,
|
||||
/// Enable Wrap Event (EWE) - RW. Default = ‘0’. When set to ‘1’, the xHC shall generate a MFINDEX
|
||||
/// Wrap Event every time the MFINDEX register transitions from 03FFFh to 0. When cleared to ‘0’
|
||||
/// no MFINDEX Wrap Events are generated. Refer to section 4.14.2 for more information.
|
||||
///
|
||||
/// When this register is exposed by a Virtual Function (VF), the generation of MFINDEX Wrap
|
||||
/// Events to VFs shall be emulated by the VMM.
|
||||
pub enable_wrap_event: bool,
|
||||
/// Enable U3 MFINDEX Stop (EU3S) - RW. Default = ‘0’. When set to ‘1’, the xHC may stop the
|
||||
/// MFINDEX counting action if all Root Hub ports are in the U3, Disconnected, Disabled, or
|
||||
/// Powered-off state. When cleared to ‘0’ the xHC may stop the MFINDEX counting action if all
|
||||
/// Root Hub ports are in the Disconnected, Disabled, Training, or Powered-off state. Refer to
|
||||
/// section 4.14.2 for more information
|
||||
pub enable_u3_mfindex_stop: bool,
|
||||
___: bool,
|
||||
/// CEM Enable (CME) - RW. Default = '0'. When set to '1', a Max Exit Latency Too Large Capability
|
||||
/// Error may be returned by a Configure Endpoint Command. When cleared to '0', a Max Exit
|
||||
/// Latency Too Large Capability Error shall not be returned by a Configure Endpoint Command.
|
||||
/// This bit is Reserved if CMC = ‘0’. Refer to section 4.23.5.2.2 for more information.
|
||||
pub cem_enable: bool,
|
||||
/// Extended TBC Enable (ETE). This flag indicates that the host controller implementation is
|
||||
/// enabled to support Transfer Burst Count (TBC) values greater that 4 in isoch TDs. When this bit
|
||||
/// is ‘1’, the Isoch TRB TD Size/TBC field presents the TBC value, and the TBC/RsvdZ field is RsvdZ.
|
||||
/// When this bit is ‘0’, the TDSize/TCB field presents the TD Size value, and the TBC/RsvdZ field
|
||||
/// presents the TBC value. This bit may be set only if ETC = ‘1’. Refer to section 4.11.2.3 for more
|
||||
/// information.
|
||||
pub extended_tbc_enable: bool,
|
||||
/// Extended TBC TRB Status Enable (TSC_EN). This flag indicates that the host controller
|
||||
/// implementation is enabled to support ETC_TSC capability. When this is ‘1’, TRBSts field in the
|
||||
/// TRB updated to indicate if it is last transfer TRB in the TD. This bit may be set only if
|
||||
/// ETC_TSC=’1’. Refer to section 4.11.2.3 for more information.
|
||||
pub extended_tbc_trb_status_enable: bool,
|
||||
/// VTIO Enable (VTIOE) – RW. Default = ‘0’. When set to ‘1’, XHCI HW will enable its VTIO
|
||||
/// capability and begin to use the information provided via that VTIO Registers to determine its
|
||||
/// DMA-ID. When cleared to ‘0’, XHCI HW will use the Primary DMA-ID for all accesses. This bit
|
||||
/// may be set only if VTC = ‘1’.
|
||||
pub vtio_enable: bool,
|
||||
#[bits(15)]
|
||||
____: u16,
|
||||
}
|
||||
|
||||
#[bitfield(u32)]
|
||||
pub struct UsbStatus {
|
||||
/// HCHalted (HCH) – RO. Default = ‘1’. This bit is a ‘0’ whenever the Run/Stop (R/S) bit is a ‘1’. The
|
||||
/// xHC sets this bit to ‘1’ after it has stopped executing as a result of the Run/Stop (R/S) bit being
|
||||
/// cleared to ‘0’, either by software or by the xHC hardware (e.g. internal error).
|
||||
///
|
||||
/// If this bit is '1', then SOFs, microSOFs, or Isochronous Timestamp Packets (ITP) shall not be
|
||||
/// generated by the xHC, and any received Transaction Packet shall be dropped.
|
||||
///
|
||||
/// When this register is exposed by a Virtual Function (VF), this bit only reflects the Halted state of
|
||||
/// the xHC instance presented by the selected VF. Refer to section 8 for more information
|
||||
#[bits(access=RO)]
|
||||
pub host_controller_halted: bool,
|
||||
__: bool,
|
||||
/// Host System Error (HSE) – RW1C. Default = ‘0’. The xHC sets this bit to ‘1’ when a serious error
|
||||
/// is detected, either internal to the xHC or during a host system access involving the xHC module.
|
||||
/// (In a PCI system, conditions that set this bit to ‘1’ include PCI Parity error, PCI Master Abort, and
|
||||
/// PCI Target Abort.) When this error occurs, the xHC clears the Run/Stop (R/S) bit in the USBCMD
|
||||
/// register to prevent further execution of the scheduled TDs. If the HSEE bit in the USBCMD
|
||||
/// register is a ‘1’, the xHC shall also assert out-of-band error signaling to the host. Refer to section
|
||||
/// 4.10.2.6 for more information.
|
||||
/// When this register is exposed by a Virtual Function (VF), the assertion of this bit affects all VFs
|
||||
/// and reflects the Host System Error state of the Physical Function (PF0). Refer to section 8 for
|
||||
/// more information.
|
||||
pub host_system_error: bool,
|
||||
/// Event Interrupt (EINT) – RW1C. Default = ‘0’. The xHC sets this bit to ‘1’ when the Interrupt
|
||||
/// Pending (IP) bit of any Interrupter transitions from ‘0’ to ‘1’. Refer to section 7.1.2 for use.
|
||||
/// Software that uses EINT shall clear it prior to clearing any IP flags. A race condition may occur if
|
||||
/// software clears the IP flags then clears the EINT flag, and between the operations another IP ‘0’
|
||||
/// to '1' transition occurs. In this case the new IP transition shall be lost.
|
||||
/// When this register is exposed by a Virtual Function (VF), this bit is the logical 'OR' of the IP bits
|
||||
/// for the Interrupters assigned to the selected VF. And it shall be cleared to ‘0’ when all associated
|
||||
/// interrupter IP bits are cleared, i.e. all the VF’s Interrupter Event Ring(s) are empty. Refer to
|
||||
/// section 8 for more information
|
||||
pub event_interrupt: bool,
|
||||
/// Port Change Detect (PCD) – RW1C. Default = ‘0’. The xHC sets this bit to a ‘1’ when any port has
|
||||
/// a change bit transition from a ‘0’ to a ‘1’.
|
||||
///
|
||||
/// This bit is allowed to be maintained in the Aux Power well. Alternatively, it is also acceptable
|
||||
/// that on a D3 to D0 transition of the xHC, this bit is loaded with the OR of all of the PORTSC
|
||||
/// change bits. Refer to section 4.19.3.
|
||||
///
|
||||
/// This bit provides system software an efficient means of determining if there has been Root Hub
|
||||
/// port activity. Refer to section 4.15.2.3 for more information.
|
||||
///
|
||||
/// When this register is exposed by a Virtual Function (VF), the VMM determines the state of this
|
||||
/// bit as a function of the Root Hub Ports associated with the Device Slots assigned to the selected
|
||||
/// VF. Refer to section 8 for more information.
|
||||
pub port_change_detect: bool,
|
||||
#[bits(3)]
|
||||
__: u8,
|
||||
/// Save State Status (SSS) - RO. Default = ‘0’. When the Controller Save State (CSS) flag in the
|
||||
/// USBCMD register is written with ‘1’ this bit shall be set to ‘1’ and remain 1 while the xHC saves
|
||||
/// its internal state. When the Save State operation is complete, this bit shall be cleared to ‘0’.
|
||||
/// Refer to section 4.23.2 for more information.
|
||||
///
|
||||
/// When this register is exposed by a Virtual Function (VF), the VMM determines the state of this
|
||||
/// bit as a function of the saving the state for the selected VF. Refer to section 8 for more
|
||||
/// information.
|
||||
#[bits(access=RO)]
|
||||
pub save_state_status: bool,
|
||||
/// Restore State Status (RSS) - RO. Default = ‘0’. When the Controller Restore State (CRS) flag in
|
||||
/// the USBCMD register is written with ‘1’ this bit shall be set to ‘1’ and remain 1 while the xHC
|
||||
/// restores its internal state. When the Restore State operation is complete, this bit shall be
|
||||
/// cleared to ‘0’. Refer to section 4.23.2 for more information.
|
||||
///
|
||||
/// When this register is exposed by a Virtual Function (VF), the VMM determines the state of this
|
||||
/// bit as a function of the restoring the state for the selected VF. Refer to section 8 for more
|
||||
/// information.
|
||||
#[bits(access=RO)]
|
||||
pub restore_state_status: bool,
|
||||
/// Save/Restore Error (SRE) - RW1C. Default = ‘0’. If an error occurs during a Save or Restore
|
||||
/// operation this bit shall be set to ‘1’. This bit shall be cleared to ‘0’ when a Save or Restore
|
||||
/// operation is initiated or when written with ‘1’. Refer to section 4.23.2 for more information.
|
||||
/// When this register is exposed by a Virtual Function (VF), the VMM determines the state of this
|
||||
/// bit as a function of the Save/Restore completion status for the selected VF. Refer to section 8
|
||||
/// for more information.
|
||||
pub save_restore_error: bool,
|
||||
/// Controller Not Ready (CNR) – RO. Default = ‘1’. ‘0’ = Ready and ‘1’ = Not Ready. Software shall
|
||||
/// not write any Doorbell or Operational register of the xHC, other than the USBSTS register, until
|
||||
/// CNR = ‘0’. This flag is set by the xHC after a Chip Hardware Reset and cleared when the xHC is
|
||||
/// ready to begin accepting register writes. This flag shall remain cleared (‘0’) until the next Chip
|
||||
/// Hardware Reset.
|
||||
#[bits(access=RO)]
|
||||
pub controller_not_ready: bool,
|
||||
/// Host Controller Error (HCE) – RO. Default = 0. 0’ = No internal xHC error conditions exist and ‘1’
|
||||
/// = Internal xHC error condition. This flag shall be set to indicate that an internal error condition
|
||||
/// has been detected which requires software to reset and reinitialize the xHC. Refer to section
|
||||
/// 4.24.1 for more information.
|
||||
#[bits(access=RO)]
|
||||
pub host_controller_error: bool,
|
||||
#[bits(19)]
|
||||
__: u32,
|
||||
}
|
||||
|
||||
impl UsbStatus {
|
||||
// Returns a copy of this object that can be written without overwritting flags that are RW1C.
|
||||
fn preserving_flags(&self) -> UsbStatus {
|
||||
self.with_host_system_error(false)
|
||||
.with_event_interrupt(false)
|
||||
.with_port_change_detect(false)
|
||||
.with_save_restore_error(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal data structure to ensure 64 bit reads and writes.
|
||||
#[bitfield(u64)]
|
||||
struct CommandAndStatus {
|
||||
#[bits(32)]
|
||||
usb_command: UsbCommand,
|
||||
#[bits(32)]
|
||||
usb_status: UsbStatus,
|
||||
}
|
||||
|
||||
impl CommandAndStatus {
|
||||
fn update_command(&self, f: impl Fn(UsbCommand) -> UsbCommand) -> CommandAndStatus {
|
||||
CommandAndStatus::new()
|
||||
.with_usb_command(f(self.usb_command()))
|
||||
.with_usb_status(self.usb_status().preserving_flags())
|
||||
}
|
||||
|
||||
fn update_status(&self, f: impl Fn(UsbStatus) -> UsbStatus) -> CommandAndStatus {
|
||||
self.with_usb_status(f(self.usb_status()).preserving_flags())
|
||||
}
|
||||
}
|
||||
|
||||
#[bitfield(u64)]
|
||||
struct PageSize {
|
||||
///Page Size – RO. Default = Implementation defined. This field defines the page size supported by
|
||||
/// the xHC implementation. This xHC supports a page size of 2^(n+12) if bit n is Set. For example, if
|
||||
/// bit 0 is Set, the xHC supports 4k byte page sizes.
|
||||
///
|
||||
/// For a Virtual Function, this register reflects the page size selected in the System Page Size field
|
||||
/// of the SR-IOV Extended Capability structure. For the Physical Function 0, this register reflects
|
||||
/// the implementation dependent default xHC page size.
|
||||
///
|
||||
/// Various xHC resources reference PAGESIZE to describe their minimum alignment requirements.
|
||||
///
|
||||
/// The maximum possible page size is 128M.
|
||||
#[bits(access=RO)]
|
||||
page_size: u32,
|
||||
__: u32,
|
||||
}
|
||||
|
||||
#[bitfield(u64)]
|
||||
struct DeviceNotificationControl {
|
||||
__: u32,
|
||||
/// This register is used by software to enable or disable the reporting of the
|
||||
/// reception of specific USB Device Notification Transaction Packets. A Notification
|
||||
/// Enable (Nx, where x = 0 to 15) flag is defined for each of the 16 possible de vice
|
||||
/// notification types. If a flag is set for a specific notification type, a Device
|
||||
/// Notification Event shall be generated when the respective notification packet is
|
||||
/// received. After reset all notifications are disabled. Refer to section 6.4.2.7
|
||||
device_notification_control: u32,
|
||||
}
|
||||
|
||||
#[bitfield(u64)]
|
||||
pub struct UsbConfigure {
|
||||
/// Max Device Slots Enabled (MaxSlotsEn) – RW. Default = ‘0’. This field specifies the maximum
|
||||
/// number of enabled Device Slots. Valid values are in the range of 0 to MaxSlots. Enabled Devices
|
||||
/// Slots are allocated contiguously. e.g. A value of 16 specifies that Device Slots 1 to 16 are active.
|
||||
///
|
||||
/// A value of ‘0’ disables all Device Slots. A disabled Device Slot shall not respond to Doorbell
|
||||
/// Register references.
|
||||
///
|
||||
/// This field shall not be modified by software if the xHC is running (Run/Stop (R/S) = ‘1’)
|
||||
pub max_device_slots_enabled: u8,
|
||||
/// U3 Entry Enable (U3E) – RW. Default = '0'. When set to '1', the xHC shall assert the PLC flag ('1')
|
||||
/// when a Root Hub port transitions to the U3 State. Refer to section 4.15.1 for more information.
|
||||
pub u3_entry_enable: bool,
|
||||
/// Configuration Information Enable (CIE) - RW. Default = '0'. When set to '1', the software shall
|
||||
/// initialize the Configuration Value, Interface Number, and Alternate Setting fields in the Input
|
||||
/// Control Context when it is associated with a Configure Endpoint Command. When this bit is '0',
|
||||
/// the extended Input Control Context fields are not supported. Refer to section 6.2.5.1 for more
|
||||
/// information.
|
||||
pub configuration_information_enable: bool,
|
||||
|
||||
#[bits(22)]
|
||||
__: u32,
|
||||
|
||||
// Pad to 64 bits for the purposes of reads and writes.
|
||||
__: u32,
|
||||
}
|
||||
|
||||
/// XHCI Spec Section 5.4
|
||||
///
|
||||
/// The base address of this register space is referred to as Operational Base. The
|
||||
/// Operational Base shall be Dword aligned and is calculated by adding the value
|
||||
/// 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)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct HostControllerOperational {
|
||||
command_and_status: CommandAndStatus,
|
||||
page_size: PageSize,
|
||||
device_notification_control: DeviceNotificationControl,
|
||||
/// Bit 0: Ring Cycle State (RW)
|
||||
/// Bit 1: Command Stop (RW1S)
|
||||
/// Bit 2: Command Abort (RW1S)
|
||||
/// Bit 3: Command Ring Running (RO)
|
||||
command_ring_control: u64,
|
||||
__: u64,
|
||||
___: u64,
|
||||
/// The Device Context Base Address Array Pointer Register identifies the base
|
||||
/// address of the Device Context Base Address Array.
|
||||
/// The memory structure referenced by this physical memory pointer is assumed to
|
||||
/// be physically contiguous and 64-byte aligned.
|
||||
device_context_base_address_array_pointer: u64,
|
||||
configure: UsbConfigure,
|
||||
}
|
||||
|
||||
const _: () = assert!(size_of::<HostControllerOperational>() == 0x40);
|
||||
|
||||
pub struct HostControllerOperationalWrapper {
|
||||
operational: Mutex<VolatileRef<'static, HostControllerOperational>>,
|
||||
// TODO: This should maybe be its own structure.
|
||||
ports: Vec<Mutex<VolatileRef<'static, HostControllerUsbPort>>>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl HostControllerOperationalWrapper {
|
||||
pub fn new(mmio_address: usize) -> (Self, HostControllerCapabilities) {
|
||||
const MAP_SIZE: usize = 0x1000;
|
||||
let caps_ptr: NonNull<HostControllerCapabilities> =
|
||||
map_direct_physical_and_leak(mmio_address, MAP_SIZE);
|
||||
|
||||
// SAFETY:
|
||||
// - The pointer is valid.
|
||||
// - No other thread has access in this block.
|
||||
let capabilities = unsafe { VolatilePtr::new(caps_ptr).read() };
|
||||
|
||||
assert!(
|
||||
capabilities.cap_params_1.supports_64_bit(),
|
||||
"We only support 64 bit XHCI"
|
||||
);
|
||||
|
||||
// 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 cap_length_and_version = capabilities.cap_length_and_version;
|
||||
let operational_ptr = unsafe {
|
||||
(caps_ptr.as_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 max_ports = capabilities.params_1.max_ports();
|
||||
assert!(
|
||||
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..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 read_command(&self) -> UsbCommand {
|
||||
let locked = self.operational.lock();
|
||||
let op = locked.as_ptr();
|
||||
map_field!(op.command_and_status).read().usb_command()
|
||||
}
|
||||
|
||||
pub fn update_command(&self, f: impl Fn(UsbCommand) -> UsbCommand) {
|
||||
let mut locked = self.operational.lock();
|
||||
let op = locked.as_mut_ptr();
|
||||
map_field!(op.command_and_status).update(|c_and_s| c_and_s.update_command(f));
|
||||
}
|
||||
|
||||
pub fn read_status(&self) -> UsbStatus {
|
||||
let locked = self.operational.lock();
|
||||
let op = locked.as_ptr();
|
||||
map_field!(op.command_and_status).read().usb_status()
|
||||
}
|
||||
|
||||
pub fn update_status(&self, f: impl Fn(UsbStatus) -> UsbStatus) {
|
||||
let mut locked = self.operational.lock();
|
||||
let op = locked.as_mut_ptr();
|
||||
map_field!(op.command_and_status).update(|c_and_s| c_and_s.update_status(f));
|
||||
}
|
||||
|
||||
pub fn set_device_context_base_address_array_pointer(&self, pointer: usize) {
|
||||
let mut locked = self.operational.lock();
|
||||
let op = locked.as_mut_ptr();
|
||||
map_field!(op.device_context_base_address_array_pointer).write(pointer as u64);
|
||||
}
|
||||
|
||||
pub fn set_command_ring_dequeue_pointer(&self, pointer: usize, cycle_bit: bool) {
|
||||
// TODO: Assert that the command ring is not running here.
|
||||
let mut locked = self.operational.lock();
|
||||
let op = locked.as_mut_ptr();
|
||||
map_field!(op.command_ring_control).write(pointer as u64 | cycle_bit as u64);
|
||||
}
|
||||
|
||||
pub fn read_configure(&self) -> UsbConfigure {
|
||||
let locked = self.operational.lock();
|
||||
let op = locked.as_ptr();
|
||||
map_field!(op.configure).read()
|
||||
}
|
||||
|
||||
pub fn update_configure(&self, f: impl Fn(UsbConfigure) -> UsbConfigure) {
|
||||
let mut locked = self.operational.lock();
|
||||
let op = locked.as_mut_ptr();
|
||||
map_field!(op.configure).update(f);
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue