xhci: rework xhci driver, now works on real hw
This commit is contained in:
parent
d687051c48
commit
87c7614fd8
35
Cargo.lock
generated
35
Cargo.lock
generated
@ -8,7 +8,7 @@ version = "10.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a21cd0131c25c438e19cd6a774adf7e3f64f7f4d723022882facc2dee0f8bc9"
|
||||
dependencies = [
|
||||
"tock-registers 0.9.0",
|
||||
"tock-registers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1125,7 +1125,7 @@ dependencies = [
|
||||
"libk-mm-interface",
|
||||
"memtables",
|
||||
"static_assertions",
|
||||
"tock-registers 0.9.0",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
@ -1149,7 +1149,7 @@ dependencies = [
|
||||
"libk-mm-interface",
|
||||
"log",
|
||||
"static_assertions",
|
||||
"tock-registers 0.9.0",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
@ -1173,7 +1173,7 @@ dependencies = [
|
||||
"log",
|
||||
"memtables",
|
||||
"static_assertions",
|
||||
"tock-registers 0.9.0",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
@ -1186,7 +1186,7 @@ dependencies = [
|
||||
"kernel-arch-interface",
|
||||
"log",
|
||||
"static_assertions",
|
||||
"tock-registers 0.9.0",
|
||||
"tock-registers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1201,7 +1201,7 @@ dependencies = [
|
||||
"log",
|
||||
"memtables",
|
||||
"static_assertions",
|
||||
"tock-registers 0.9.0",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
@ -2105,12 +2105,6 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tock-registers"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c"
|
||||
|
||||
[[package]]
|
||||
name = "tock-registers"
|
||||
version = "0.9.0"
|
||||
@ -2648,7 +2642,7 @@ dependencies = [
|
||||
"log",
|
||||
"memoffset 0.9.1",
|
||||
"static_assertions",
|
||||
"tock-registers 0.9.0",
|
||||
"tock-registers",
|
||||
"ygg_driver_pci",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
@ -2702,7 +2696,7 @@ dependencies = [
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers 0.9.0",
|
||||
"tock-registers",
|
||||
"ygg_driver_net_core",
|
||||
"ygg_driver_pci",
|
||||
"yggdrasil-abi",
|
||||
@ -2722,7 +2716,7 @@ dependencies = [
|
||||
"libk-util",
|
||||
"log",
|
||||
"static_assertions",
|
||||
"tock-registers 0.9.0",
|
||||
"tock-registers",
|
||||
"ygg_driver_pci",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
@ -2739,7 +2733,7 @@ dependencies = [
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers 0.9.0",
|
||||
"tock-registers",
|
||||
"ygg_driver_acpi",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
@ -2778,6 +2772,7 @@ dependencies = [
|
||||
name = "ygg_driver_usb_xhci"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"atomic_enum",
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
@ -2786,7 +2781,7 @@ dependencies = [
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers 0.8.1",
|
||||
"tock-registers",
|
||||
"xhci",
|
||||
"ygg_driver_pci",
|
||||
"ygg_driver_usb",
|
||||
@ -2802,7 +2797,7 @@ dependencies = [
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers 0.9.0",
|
||||
"tock-registers",
|
||||
"ygg_driver_pci",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
@ -2832,7 +2827,7 @@ dependencies = [
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers 0.9.0",
|
||||
"tock-registers",
|
||||
"ygg_driver_net_core",
|
||||
"ygg_driver_pci",
|
||||
"ygg_driver_virtio_core",
|
||||
@ -2890,7 +2885,7 @@ dependencies = [
|
||||
"memtables",
|
||||
"prettyplease",
|
||||
"static_assertions",
|
||||
"tock-registers 0.9.0",
|
||||
"tock-registers",
|
||||
"vmalloc",
|
||||
"yboot-proto",
|
||||
"ygg_driver_acpi",
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![feature(const_trait_impl, let_chains, if_let_guard, maybe_uninit_slice)]
|
||||
#![allow(missing_docs)]
|
||||
#![no_std]
|
||||
// TODO
|
||||
#![allow(unused)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
|
@ -8,7 +8,7 @@ use alloc::{
|
||||
boxed::Box, collections::btree_map::BTreeMap, format, string::String, sync::Arc, vec::Vec,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use command::{ScsiInquiry, ScsiReadCapacity, ScsiRequestSense, ScsiTestUnitReady};
|
||||
use command::{ScsiReadCapacity, ScsiRequestSense, ScsiTestUnitReady};
|
||||
use device_api::device::Device;
|
||||
use libk::{
|
||||
device::{block::BlockDevice, manager::probe_partitions},
|
||||
@ -44,8 +44,6 @@ impl ScsiDevice {
|
||||
pub async fn setup<T: ScsiTransport + 'static>(transport: T) -> Result<Arc<Self>, Error> {
|
||||
let mut transport = ScsiTransportWrapper::new(transport);
|
||||
|
||||
transport.perform_command(0, ScsiInquiry).await?;
|
||||
|
||||
let mut attempts = 5;
|
||||
let mut timeout = 100;
|
||||
while attempts > 0 {
|
||||
@ -63,6 +61,8 @@ impl ScsiDevice {
|
||||
break;
|
||||
}
|
||||
|
||||
log::warn!("scsi: unit not ready [{attempts}/5]");
|
||||
|
||||
runtime::sleep(Duration::from_millis(timeout)).await;
|
||||
timeout *= 2;
|
||||
attempts -= 1;
|
||||
@ -72,6 +72,9 @@ impl ScsiDevice {
|
||||
return Err(Error::DoesNotExist);
|
||||
}
|
||||
|
||||
// TODO INQUIRY fails for real USB flash drives
|
||||
// transport.perform_command(0, ScsiInquiry).await?;
|
||||
|
||||
let capacity_info = transport.perform_command(0, ScsiReadCapacity).await?;
|
||||
log::info!(
|
||||
"scsi: lba_size={}, lba_count={}",
|
||||
@ -142,7 +145,7 @@ impl BlockDevice for ScsiDevice {
|
||||
}
|
||||
|
||||
fn max_blocks_per_request(&self) -> usize {
|
||||
8
|
||||
65536 / self.lba_size
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,6 +310,22 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> PowerManagementData<'s, S> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_pme_en(&self, state: bool) {
|
||||
let pmcsr = self.space.read_u16(self.offset + 4);
|
||||
let new = if state {
|
||||
pmcsr | (1 << 8)
|
||||
} else {
|
||||
pmcsr & !(1 << 8)
|
||||
};
|
||||
if pmcsr == new {
|
||||
return;
|
||||
}
|
||||
|
||||
log::info!("Set PMCSR.PME_En = {state}");
|
||||
|
||||
self.space.write_u16(self.offset + 4, new);
|
||||
}
|
||||
|
||||
pub fn get_device_power_state(&self) -> DevicePowerState {
|
||||
let pmcsr = self.space.read_u16(self.offset + 4);
|
||||
match pmcsr & 0x3 {
|
||||
|
35
kernel/driver/bus/pci/src/macros.rs
Normal file
35
kernel/driver/bus/pci/src/macros.rs
Normal file
@ -0,0 +1,35 @@
|
||||
pub macro pci_driver_match {
|
||||
(class ($class:literal:$subclass:literal:$prog_if:literal)) => {
|
||||
$crate::driver::PciMatch::Class($class, Some($subclass), Some($prog_if))
|
||||
},
|
||||
(class ($class:literal:$subclass:literal)) => {
|
||||
$crate::driver::PciMatch::Class($class, Some($subclass), None)
|
||||
},
|
||||
(class $class:literal) => {
|
||||
$crate::driver::PciMatch::Class($class, None, None)
|
||||
},
|
||||
(device ($vendor:literal:$device:literal)) => {
|
||||
$crate::driver::PciMatch::Vendor($vendor, $device)
|
||||
}
|
||||
}
|
||||
|
||||
pub macro pci_driver(
|
||||
matches: [$($kind:ident $match:tt),+],
|
||||
driver: $driver:tt
|
||||
) {
|
||||
#[link_section = ".init_array"]
|
||||
#[used]
|
||||
static __REGISTER_FN: extern "C" fn() = __register_fn;
|
||||
|
||||
extern "C" fn __register_fn() {
|
||||
struct Driver;
|
||||
impl $crate::driver::PciDriver for Driver $driver
|
||||
static DRIVER: Driver = Driver;
|
||||
|
||||
log::info!("register pci driver: {:?}", $crate::driver::PciDriver::driver_name(&Driver));
|
||||
$(
|
||||
let pmatch = $crate::macros::pci_driver_match!($kind $match);
|
||||
$crate::driver::register_match(pmatch, &DRIVER);
|
||||
)+
|
||||
}
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use futures_util::{future::BoxFuture, FutureExt};
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use libk_mm::PageBox;
|
||||
use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent};
|
||||
|
||||
@ -126,17 +125,16 @@ impl KeyboardState {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbDriver for UsbHidKeyboardDriver {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
device: Arc<UsbDeviceAccess>,
|
||||
) -> BoxFuture<'static, Result<(), UsbError>> {
|
||||
async move {
|
||||
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
|
||||
// TODO not sure whether to use boot protocol (easy) or GetReport
|
||||
let config = device.select_configuration(|_| true).await?.unwrap();
|
||||
assert_eq!(config.endpoints.len(), 1);
|
||||
|
||||
let pipe = device.open_interrupt_in_pipe(1).await?;
|
||||
log::info!("Setup HID keyboard");
|
||||
let pipe = device
|
||||
.open_interrupt_in_pipe(1, config.endpoints[0].max_packet_size as u16)
|
||||
.await?;
|
||||
|
||||
let mut buffer = PageBox::new_slice(0, 8).map_err(UsbError::MemoryError)?;
|
||||
let mut state = KeyboardState::new();
|
||||
@ -145,29 +143,35 @@ impl UsbDriver for UsbHidKeyboardDriver {
|
||||
loop {
|
||||
let mut event_count = 0;
|
||||
|
||||
let data = pipe.read(&mut buffer).await?;
|
||||
let len = pipe.read(buffer.as_slice_mut()).await?;
|
||||
if len < 8 {
|
||||
continue;
|
||||
}
|
||||
|
||||
event_count += state.retain_modifiers(data[0], &mut events);
|
||||
event_count += state.press_modifiers(data[0], &mut events[event_count..]);
|
||||
event_count += state.retain(&data[2..], &mut events[event_count..]);
|
||||
event_count += state.press(&data[2..], &mut events[event_count..]);
|
||||
event_count += state.retain_modifiers(buffer[0], &mut events);
|
||||
event_count += state.press_modifiers(buffer[0], &mut events[event_count..]);
|
||||
event_count += state.retain(&buffer[2..], &mut events[event_count..]);
|
||||
event_count += state.press(&buffer[2..], &mut events[event_count..]);
|
||||
|
||||
let events = unsafe { MaybeUninit::slice_assume_init_ref(&events[..event_count]) };
|
||||
|
||||
for &event in events {
|
||||
log::trace!("Generic Keyboard: {:?}", event);
|
||||
log::info!("Generic Keyboard: {:?}", event);
|
||||
ygg_driver_input::send_event(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"USB HID Keyboard"
|
||||
}
|
||||
|
||||
fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
|
||||
class.class == UsbDeviceClass::Hid && class.subclass == 0x01
|
||||
log::info!(
|
||||
"class = {:?}, subclass = {:02x}",
|
||||
class.class,
|
||||
class.subclass
|
||||
);
|
||||
class.class == UsbDeviceClass::Hid && (class.subclass == 0x00 || class.subclass == 0x01)
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use futures_util::{future::BoxFuture, FutureExt};
|
||||
use libk::error::Error;
|
||||
use libk_mm::PageBox;
|
||||
use ygg_driver_scsi::{transport::ScsiTransport, ScsiDevice};
|
||||
|
||||
use crate::{
|
||||
communication::UsbDirection,
|
||||
device::{UsbDeviceAccess, UsbDeviceDetachHandler},
|
||||
error::UsbError,
|
||||
info::UsbDeviceClass,
|
||||
info::{UsbDeviceClass, UsbEndpointType},
|
||||
pipe::{
|
||||
bulk::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
|
||||
control::UsbClassSpecificRequest,
|
||||
normal::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
|
||||
},
|
||||
};
|
||||
|
||||
@ -99,7 +99,8 @@ impl Bbb {
|
||||
|
||||
self.out_pipe
|
||||
.write(self.buffer.as_slice().subslice(..31))
|
||||
.await?;
|
||||
.await
|
||||
.inspect_err(|error| log::error!("msc: out pipe error: {error:?}"))?;
|
||||
|
||||
Ok(tag)
|
||||
}
|
||||
@ -107,7 +108,8 @@ impl Bbb {
|
||||
async fn read_csw(&mut self, tag: u32) -> Result<(), Error> {
|
||||
self.in_pipe
|
||||
.read(self.buffer.as_slice_mut().subslice_mut(..13))
|
||||
.await?;
|
||||
.await
|
||||
.inspect_err(|error| log::error!("msc: in pipe error: {error:?}"))?;
|
||||
|
||||
let csw = bytemuck::from_bytes::<Csw>(&self.buffer[..size_of::<Csw>()]);
|
||||
if csw.signature != 0x53425355 {
|
||||
@ -128,12 +130,13 @@ impl Bbb {
|
||||
|
||||
async fn read_response_data(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
// TODO limit by max_packet_size
|
||||
let bytes = self
|
||||
let len = self
|
||||
.in_pipe
|
||||
.read(self.buffer.as_slice_mut().subslice_mut(..buffer.len()))
|
||||
.await?;
|
||||
buffer[..bytes.len()].copy_from_slice(bytes);
|
||||
Ok(bytes.len())
|
||||
.await
|
||||
.inspect_err(|error| log::error!("msc: in pipe error: {error:?}"))?;
|
||||
buffer[..len].copy_from_slice(&self.buffer[..len]);
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,20 +181,27 @@ impl UsbClassSpecificRequest for BulkOnlyMassStorageReset {
|
||||
const B_REQUEST: u8 = 0b11111111;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbDriver for UsbMassStorageDriverBulkOnly {
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
device: Arc<UsbDeviceAccess>,
|
||||
) -> BoxFuture<'static, Result<(), UsbError>> {
|
||||
async move {
|
||||
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
|
||||
// TODO filter to only accept BBB config
|
||||
let config = device.select_configuration(|_| true).await?.unwrap();
|
||||
// Bulk-in, bulk-out
|
||||
assert_eq!(config.endpoints.len(), 2);
|
||||
// TODO those indices may be different
|
||||
let control_pipe = device.control_pipe();
|
||||
let in_pipe = device.open_bulk_in_pipe(1).await?;
|
||||
let out_pipe = device.open_bulk_out_pipe(2).await?;
|
||||
let (in_index, in_info) = config
|
||||
.find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::In))
|
||||
.ok_or(UsbError::InvalidConfiguration)?;
|
||||
let (out_index, out_info) = config
|
||||
.find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::Out))
|
||||
.ok_or(UsbError::InvalidConfiguration)?;
|
||||
let in_pipe = device
|
||||
.open_bulk_in_pipe(in_index, in_info.max_packet_size as u16)
|
||||
.await?;
|
||||
let out_pipe = device
|
||||
.open_bulk_out_pipe(out_index, out_info.max_packet_size as u16)
|
||||
.await?;
|
||||
|
||||
// Perform a Bulk-Only Mass Storage Reset
|
||||
// TODO interface id?
|
||||
@ -213,8 +223,6 @@ impl UsbDriver for UsbMassStorageDriverBulkOnly {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"USB Mass Storage"
|
||||
|
@ -1,5 +1,5 @@
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use futures_util::future::BoxFuture;
|
||||
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use async_trait::async_trait;
|
||||
use libk::task::runtime;
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
|
||||
@ -21,24 +21,22 @@ pub struct UsbClassInfo {
|
||||
pub interface_protocol_number: u8,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait UsbDriver: Send + Sync {
|
||||
fn name(&self) -> &'static str;
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
device: Arc<UsbDeviceAccess>,
|
||||
) -> BoxFuture<'static, Result<(), UsbError>>;
|
||||
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError>;
|
||||
|
||||
fn name(&self) -> &'static str;
|
||||
fn probe(&self, class: &UsbClassInfo, device: &UsbDeviceAccess) -> bool;
|
||||
}
|
||||
|
||||
async fn extract_class_info(device: &UsbDeviceAccess) -> Result<Option<UsbClassInfo>, UsbError> {
|
||||
if device.num_configurations != 1 {
|
||||
if device.info.num_configurations != 1 {
|
||||
return Ok(None);
|
||||
}
|
||||
let device_info = &device.info;
|
||||
let config_info = device.query_configuration_info(0).await?;
|
||||
|
||||
if config_info.interfaces.len() == 1 {
|
||||
if config_info.interfaces.len() >= 1 {
|
||||
let if_info = &config_info.interfaces[0];
|
||||
|
||||
let class = if device_info.device_class == UsbDeviceClass::FromInterface {
|
||||
|
@ -1,149 +1,11 @@
|
||||
use core::{
|
||||
future::poll_fn,
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use futures_util::task::AtomicWaker;
|
||||
use libk_mm::address::PhysicalAddress;
|
||||
|
||||
use crate::error::{TransferError, UsbError};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum UsbDirection {
|
||||
Out,
|
||||
In,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct UsbTransferToken(pub u64);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct UsbTransferResult(pub u32);
|
||||
|
||||
pub struct UsbTransferStatus {
|
||||
pub result: AtomicU32,
|
||||
pub notify: AtomicWaker,
|
||||
}
|
||||
|
||||
pub struct UsbControlTransfer {
|
||||
pub id: UsbTransferToken,
|
||||
pub length: usize,
|
||||
pub direction: UsbDirection,
|
||||
pub elements: Vec<PhysicalAddress>,
|
||||
pub status: Arc<UsbTransferStatus>,
|
||||
}
|
||||
|
||||
pub struct UsbInterruptTransfer {
|
||||
pub address: PhysicalAddress,
|
||||
pub length: usize,
|
||||
pub direction: UsbDirection,
|
||||
pub status: Arc<UsbTransferStatus>,
|
||||
}
|
||||
|
||||
pub struct UsbBulkTransfer {
|
||||
pub address: PhysicalAddress,
|
||||
pub length: usize,
|
||||
pub direction: UsbDirection,
|
||||
pub status: Arc<UsbTransferStatus>,
|
||||
}
|
||||
|
||||
impl UsbDirection {
|
||||
pub const fn is_device_to_host(self) -> bool {
|
||||
matches!(self, UsbDirection::In)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this is xHCI-specific
|
||||
impl UsbTransferResult {
|
||||
pub fn is_aborted(&self) -> bool {
|
||||
self.0 == u32::MAX
|
||||
}
|
||||
|
||||
pub fn is_success(&self) -> bool {
|
||||
(self.0 >> 24) & 0xFF == 1
|
||||
}
|
||||
|
||||
pub fn sub_length(&self) -> usize {
|
||||
(self.0 & 0xFFFFFF) as _
|
||||
}
|
||||
|
||||
pub fn error_code(&self) -> TransferError {
|
||||
match self.0 >> 24 {
|
||||
0 => TransferError::InvalidTransfer,
|
||||
2 => TransferError::BufferError,
|
||||
4 => TransferError::UsbTransactionError,
|
||||
6 => TransferError::Stall,
|
||||
13 => TransferError::ShortPacket(self.0 & 0xFFFFFF),
|
||||
_ => TransferError::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbControlTransfer {
|
||||
pub async fn wait(&self) -> Result<usize, UsbError> {
|
||||
let sub_length = self.status.wait().await?;
|
||||
Ok(self.length.saturating_sub(sub_length))
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbInterruptTransfer {
|
||||
pub async fn wait(&self) -> Result<usize, UsbError> {
|
||||
let sub_length = self.status.wait().await?;
|
||||
Ok(self.length.saturating_sub(sub_length))
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbBulkTransfer {
|
||||
pub async fn wait(&self) -> Result<usize, UsbError> {
|
||||
let sub_length = self.status.wait().await?;
|
||||
Ok(self.length.saturating_sub(sub_length))
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbTransferStatus {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
result: AtomicU32::new(0),
|
||||
notify: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn wait(&self) -> Result<usize, UsbError> {
|
||||
poll_fn(|cx| {
|
||||
self.poll(cx).map(|v| {
|
||||
if v.is_success() {
|
||||
Ok(v.sub_length())
|
||||
} else if v.is_aborted() {
|
||||
Err(UsbError::DeviceDisconnected)
|
||||
} else {
|
||||
Err(UsbError::TransferFailed(v.error_code()))
|
||||
}
|
||||
})
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn signal(&self, status: u32) {
|
||||
self.result.store(status, Ordering::Release);
|
||||
self.notify.wake();
|
||||
}
|
||||
|
||||
pub fn abort(&self) {
|
||||
self.result.store(u32::MAX, Ordering::Release);
|
||||
self.notify.wake();
|
||||
}
|
||||
|
||||
pub fn poll(&self, cx: &mut Context<'_>) -> Poll<UsbTransferResult> {
|
||||
self.notify.register(cx.waker());
|
||||
let value = self.result.load(Ordering::Acquire);
|
||||
if value != 0 {
|
||||
Poll::Ready(UsbTransferResult(value))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
use crate::{
|
||||
communication::UsbDirection,
|
||||
device::UsbSpeed,
|
||||
error::UsbError,
|
||||
info::{UsbDeviceClass, UsbDeviceProtocol, UsbEndpointType, UsbVersion},
|
||||
UsbDirection,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
|
||||
|
@ -1,17 +1,22 @@
|
||||
use core::{fmt, ops::Deref};
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use futures_util::future::BoxFuture;
|
||||
use async_trait::async_trait;
|
||||
use libk_mm::PageBox;
|
||||
use libk_util::sync::spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard};
|
||||
|
||||
use crate::{
|
||||
error::UsbError,
|
||||
info::{UsbConfigurationInfo, UsbDeviceInfo, UsbEndpointInfo, UsbInterfaceInfo, UsbVersion},
|
||||
info::{
|
||||
UsbConfigurationInfo, UsbDeviceInfo, UsbEndpointInfo, UsbEndpointType, UsbInterfaceInfo,
|
||||
UsbVersion,
|
||||
},
|
||||
pipe::{
|
||||
bulk::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
|
||||
control::{ConfigurationDescriptorEntry, UsbControlPipeAccess},
|
||||
interrupt::UsbInterruptInPipeAccess,
|
||||
normal::{
|
||||
UsbBulkInPipeAccess, UsbBulkOutPipeAccess, UsbInterruptInPipeAccess, UsbNormalPipeIn,
|
||||
UsbNormalPipeOut,
|
||||
},
|
||||
},
|
||||
UsbHostController,
|
||||
};
|
||||
@ -25,9 +30,8 @@ pub struct UsbBusAddress {
|
||||
}
|
||||
|
||||
pub struct UsbDeviceAccess {
|
||||
pub device: Box<dyn UsbDevice>,
|
||||
pub device: Arc<dyn UsbDevice>,
|
||||
pub info: UsbDeviceInfo,
|
||||
pub num_configurations: u8,
|
||||
pub current_configuration: IrqSafeRwLock<Option<UsbConfigurationInfo>>,
|
||||
}
|
||||
|
||||
@ -43,23 +47,25 @@ pub trait UsbDeviceDetachHandler: Send + Sync {
|
||||
fn handle_device_detach(&self);
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[allow(unused)]
|
||||
pub trait UsbDevice: Send + Sync {
|
||||
// Endpoint "0"
|
||||
fn control_pipe(&self) -> &UsbControlPipeAccess;
|
||||
|
||||
fn open_interrupt_in_pipe(
|
||||
async fn open_normal_in_pipe(
|
||||
&self,
|
||||
number: u8,
|
||||
) -> BoxFuture<Result<UsbInterruptInPipeAccess, UsbError>> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn open_bulk_in_pipe(&self, number: u8) -> BoxFuture<Result<UsbBulkInPipeAccess, UsbError>> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn open_bulk_out_pipe(&self, number: u8) -> BoxFuture<Result<UsbBulkOutPipeAccess, UsbError>> {
|
||||
unimplemented!()
|
||||
}
|
||||
max_packet_size: u16,
|
||||
ty: UsbEndpointType,
|
||||
) -> Result<Box<dyn UsbNormalPipeIn>, UsbError>;
|
||||
|
||||
async fn open_normal_out_pipe(
|
||||
&self,
|
||||
number: u8,
|
||||
max_packet_size: u16,
|
||||
ty: UsbEndpointType,
|
||||
) -> Result<Box<dyn UsbNormalPipeOut>, UsbError>;
|
||||
|
||||
fn port_number(&self) -> u8;
|
||||
fn bus_address(&self) -> UsbBusAddress;
|
||||
@ -79,7 +85,7 @@ impl UsbDeviceAccess {
|
||||
/// * Device is not yet configured
|
||||
/// * Control pipe for the device has been properly set up
|
||||
/// * Device has been assigned a bus address
|
||||
pub async fn setup(raw: Box<dyn UsbDevice>) -> Result<Self, UsbError> {
|
||||
pub async fn setup(raw: Arc<dyn UsbDevice>) -> Result<Self, UsbError> {
|
||||
let control = raw.control_pipe();
|
||||
let mut string_buffer = PageBox::new_uninit().map_err(UsbError::MemoryError)?;
|
||||
|
||||
@ -116,17 +122,54 @@ impl UsbDeviceAccess {
|
||||
device_protocol: device_desc.protocol(),
|
||||
device_protocol_number: device_desc.device_protocol,
|
||||
|
||||
num_configurations: device_desc.num_configurations,
|
||||
|
||||
max_packet_size: device_desc.max_packet_size(usb_version, raw.speed())?,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
device: raw,
|
||||
info,
|
||||
num_configurations: device_desc.num_configurations,
|
||||
current_configuration: IrqSafeRwLock::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn open_interrupt_in_pipe(
|
||||
&self,
|
||||
number: u8,
|
||||
max_packet_size: u16,
|
||||
) -> Result<UsbInterruptInPipeAccess, UsbError> {
|
||||
let pipe = self
|
||||
.device
|
||||
.open_normal_in_pipe(number, max_packet_size, UsbEndpointType::Interrupt)
|
||||
.await?;
|
||||
Ok(UsbInterruptInPipeAccess(pipe))
|
||||
}
|
||||
|
||||
pub async fn open_bulk_in_pipe(
|
||||
&self,
|
||||
number: u8,
|
||||
max_packet_size: u16,
|
||||
) -> Result<UsbBulkInPipeAccess, UsbError> {
|
||||
let pipe = self
|
||||
.device
|
||||
.open_normal_in_pipe(number, max_packet_size, UsbEndpointType::Bulk)
|
||||
.await?;
|
||||
Ok(UsbBulkInPipeAccess(pipe))
|
||||
}
|
||||
|
||||
pub async fn open_bulk_out_pipe(
|
||||
&self,
|
||||
number: u8,
|
||||
max_packet_size: u16,
|
||||
) -> Result<UsbBulkOutPipeAccess, UsbError> {
|
||||
let pipe = self
|
||||
.device
|
||||
.open_normal_out_pipe(number, max_packet_size, UsbEndpointType::Bulk)
|
||||
.await?;
|
||||
Ok(UsbBulkOutPipeAccess(pipe))
|
||||
}
|
||||
|
||||
pub fn read_current_configuration(
|
||||
&self,
|
||||
) -> IrqSafeRwLockReadGuard<Option<UsbConfigurationInfo>> {
|
||||
@ -140,7 +183,7 @@ impl UsbDeviceAccess {
|
||||
let mut current_config = self.current_configuration.write();
|
||||
let control_pipe = self.control_pipe();
|
||||
|
||||
for i in 0..self.num_configurations {
|
||||
for i in 0..self.info.num_configurations {
|
||||
let info = self.query_configuration_info(i).await?;
|
||||
|
||||
if predicate(&info) {
|
||||
@ -162,7 +205,7 @@ impl UsbDeviceAccess {
|
||||
&self,
|
||||
index: u8,
|
||||
) -> Result<UsbConfigurationInfo, UsbError> {
|
||||
if index >= self.num_configurations {
|
||||
if index >= self.info.num_configurations {
|
||||
return Err(UsbError::InvalidConfiguration);
|
||||
}
|
||||
let mut string_buffer = PageBox::new_uninit().map_err(UsbError::MemoryError)?;
|
||||
|
@ -1,13 +1,13 @@
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TransferError {
|
||||
InvalidTransfer,
|
||||
ShortPacket(u32),
|
||||
ShortPacket(usize),
|
||||
BufferError,
|
||||
UsbTransactionError,
|
||||
Stall,
|
||||
Other,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -25,6 +25,7 @@ pub enum UsbError {
|
||||
InvalidConfiguration,
|
||||
InvalidDescriptorField,
|
||||
// Runtime errors
|
||||
TimedOut,
|
||||
DeviceBusy,
|
||||
DeviceDisconnected,
|
||||
TransferFailed(TransferError),
|
||||
@ -32,6 +33,12 @@ pub enum UsbError {
|
||||
DriverError,
|
||||
}
|
||||
|
||||
impl From<TransferError> for UsbError {
|
||||
fn from(value: TransferError) -> Self {
|
||||
Self::TransferFailed(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UsbError> for Error {
|
||||
fn from(value: UsbError) -> Self {
|
||||
match value {
|
||||
|
@ -3,9 +3,9 @@ use core::fmt;
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use yggdrasil_abi::primitive_enum;
|
||||
|
||||
use crate::UsbDirection;
|
||||
use crate::communication::UsbDirection;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum UsbEndpointType {
|
||||
Control,
|
||||
Isochronous,
|
||||
@ -33,6 +33,7 @@ pub enum UsbUsageType {
|
||||
pub enum UsbVersion {
|
||||
Usb11,
|
||||
Usb20,
|
||||
Usb21,
|
||||
Usb30,
|
||||
Usb31,
|
||||
Usb32,
|
||||
@ -98,6 +99,8 @@ pub struct UsbDeviceInfo {
|
||||
|
||||
/// Max packet size for endpoint zero
|
||||
pub max_packet_size: usize,
|
||||
|
||||
pub num_configurations: u8,
|
||||
}
|
||||
|
||||
impl UsbVersion {
|
||||
@ -108,7 +111,8 @@ impl UsbVersion {
|
||||
pub fn from_bcd_usb(value: u16) -> Option<Self> {
|
||||
match value {
|
||||
0x110 => Some(UsbVersion::Usb11),
|
||||
0x200 => Some(UsbVersion::Usb20),
|
||||
0x200..=0x20F => Some(UsbVersion::Usb20),
|
||||
0x210..=0x21F => Some(UsbVersion::Usb21),
|
||||
0x300 => Some(UsbVersion::Usb30),
|
||||
0x310 => Some(UsbVersion::Usb31),
|
||||
0x320 => Some(UsbVersion::Usb32),
|
||||
@ -122,6 +126,7 @@ impl fmt::Display for UsbVersion {
|
||||
let string = match self {
|
||||
Self::Usb11 => "USB1.1",
|
||||
Self::Usb20 => "USB2.0",
|
||||
Self::Usb21 => "USB2.1",
|
||||
Self::Usb30 => "USB3.0",
|
||||
Self::Usb31 => "USB3.1",
|
||||
Self::Usb32 => "USB3.2",
|
||||
@ -129,3 +134,20 @@ impl fmt::Display for UsbVersion {
|
||||
f.write_str(string)
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbEndpointInfo {
|
||||
pub fn is(&self, ty: UsbEndpointType, dir: UsbDirection) -> bool {
|
||||
self.ty == ty && self.direction == dir
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbConfigurationInfo {
|
||||
pub fn find_endpoint<P: Fn(&UsbEndpointInfo) -> bool>(
|
||||
&self,
|
||||
predicate: P,
|
||||
) -> Option<(u8, &UsbEndpointInfo)> {
|
||||
let index = self.endpoints.iter().position(predicate)?;
|
||||
let info = &self.endpoints[index];
|
||||
Some((index as u8 + 1, info))
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ pub mod util;
|
||||
|
||||
pub mod class_driver;
|
||||
|
||||
pub use communication::{UsbControlTransfer, UsbDirection, UsbTransferStatus, UsbTransferToken};
|
||||
// pub use communication::{UsbControlTransfer, UsbDirection, UsbTransferStatus, UsbTransferToken};
|
||||
|
||||
pub trait UsbEndpoint: Sync {}
|
||||
|
||||
|
@ -1,55 +0,0 @@
|
||||
use core::ops::Deref;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use libk_mm::PageSlice;
|
||||
|
||||
use crate::{communication::UsbBulkTransfer, error::UsbError};
|
||||
|
||||
use super::UsbGenericPipe;
|
||||
|
||||
pub trait UsbBulkInPipe: UsbGenericPipe + Send + Sync {
|
||||
fn start_read(&self, buffer: &mut PageSlice<u8>) -> Result<UsbBulkTransfer, UsbError>;
|
||||
fn complete_transfer(&self, transfer: UsbBulkTransfer);
|
||||
}
|
||||
|
||||
pub trait UsbBulkOutPipe: UsbGenericPipe + Send + Sync {
|
||||
fn start_write(&self, buffer: &PageSlice<u8>) -> Result<UsbBulkTransfer, UsbError>;
|
||||
fn complete_transfer(&self, transfer: UsbBulkTransfer);
|
||||
}
|
||||
|
||||
pub struct UsbBulkInPipeAccess(pub Box<dyn UsbBulkInPipe>);
|
||||
pub struct UsbBulkOutPipeAccess(pub Box<dyn UsbBulkOutPipe>);
|
||||
|
||||
impl UsbBulkInPipeAccess {
|
||||
pub async fn read<'a>(&self, buffer: &'a mut PageSlice<u8>) -> Result<&'a [u8], UsbError> {
|
||||
let transfer = self.start_read(buffer)?;
|
||||
let len = transfer.wait().await?;
|
||||
self.complete_transfer(transfer);
|
||||
Ok(&buffer[..len])
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for UsbBulkInPipeAccess {
|
||||
type Target = dyn UsbBulkInPipe;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbBulkOutPipeAccess {
|
||||
pub async fn write<'a>(&self, buffer: &'a PageSlice<u8>) -> Result<(), UsbError> {
|
||||
let transfer = self.start_write(buffer)?;
|
||||
transfer.wait().await?;
|
||||
self.complete_transfer(transfer);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for UsbBulkOutPipeAccess {
|
||||
type Target = dyn UsbBulkOutPipe;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
@ -5,23 +5,19 @@ use core::{
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, string::String};
|
||||
use async_trait::async_trait;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use libk_mm::{
|
||||
address::{AsPhysicalAddress, PhysicalAddress},
|
||||
PageBox,
|
||||
};
|
||||
use libk_mm::{PageBox, PageSlice};
|
||||
|
||||
use crate::{
|
||||
communication::UsbDirection,
|
||||
descriptor::{
|
||||
UsbConfigurationDescriptor, UsbDeviceDescriptor, UsbDeviceQualifier, UsbEndpointDescriptor,
|
||||
UsbInterfaceDescriptor, UsbOtherSpeedConfiguration,
|
||||
},
|
||||
error::UsbError,
|
||||
UsbControlTransfer, UsbDirection,
|
||||
};
|
||||
|
||||
use super::UsbGenericPipe;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ControlTransferSetup {
|
||||
pub bm_request_type: u8,
|
||||
@ -88,28 +84,17 @@ fn decode_usb_string(bytes: &[u8]) -> Result<String, UsbError> {
|
||||
|
||||
// Pipe impl
|
||||
|
||||
pub trait UsbControlPipe: UsbGenericPipe + Send + Sync {
|
||||
fn start_transfer(
|
||||
#[async_trait]
|
||||
pub trait UsbControlPipe: Send + Sync {
|
||||
async fn control_transfer(
|
||||
&self,
|
||||
setup: ControlTransferSetup,
|
||||
data: Option<(PhysicalAddress, usize, UsbDirection)>,
|
||||
) -> Result<UsbControlTransfer, UsbError>;
|
||||
|
||||
fn complete_transfer(&self, transfer: UsbControlTransfer);
|
||||
data: Option<(&mut PageSlice<MaybeUninit<u8>>, UsbDirection)>,
|
||||
) -> Result<usize, UsbError>;
|
||||
}
|
||||
|
||||
pub struct UsbControlPipeAccess(pub Box<dyn UsbControlPipe>);
|
||||
|
||||
fn input_buffer<T: Pod>(
|
||||
data: &mut PageBox<MaybeUninit<T>>,
|
||||
) -> (PhysicalAddress, usize, UsbDirection) {
|
||||
(
|
||||
unsafe { data.as_physical_address() },
|
||||
size_of::<T>(),
|
||||
UsbDirection::In,
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ConfigurationDescriptorEntry<'a> {
|
||||
Configuration(&'a UsbConfigurationDescriptor),
|
||||
@ -188,15 +173,40 @@ impl ConfigurationDescriptorQuery {
|
||||
}
|
||||
|
||||
impl UsbControlPipeAccess {
|
||||
pub async fn perform_value_control(
|
||||
&self,
|
||||
setup: ControlTransferSetup,
|
||||
buffer: Option<(PhysicalAddress, usize, UsbDirection)>,
|
||||
) -> Result<(), UsbError> {
|
||||
let transfer = self.start_transfer(setup, buffer)?;
|
||||
transfer.status.wait().await?;
|
||||
self.complete_transfer(transfer);
|
||||
Ok(())
|
||||
pub async fn query_device_descriptor2(&self) -> Result<PageBox<UsbDeviceDescriptor>, UsbError> {
|
||||
let mut output = PageBox::new_uninit().map_err(UsbError::MemoryError)?;
|
||||
|
||||
self.control_transfer(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: 0b10000000,
|
||||
b_request: 0xFF,
|
||||
// b_request: 0x06,
|
||||
w_value: 0x100,
|
||||
w_index: 0,
|
||||
w_length: size_of::<UsbDeviceDescriptor>() as _,
|
||||
},
|
||||
Some((PageBox::as_bytes_mut(&mut output), UsbDirection::In)),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(unsafe { output.assume_init() })
|
||||
}
|
||||
pub async fn query_device_descriptor(&self) -> Result<PageBox<UsbDeviceDescriptor>, UsbError> {
|
||||
let mut output = PageBox::new_uninit().map_err(UsbError::MemoryError)?;
|
||||
|
||||
self.control_transfer(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: 0b10000000,
|
||||
b_request: 0x06,
|
||||
w_value: 0x100,
|
||||
w_index: 0,
|
||||
w_length: size_of::<UsbDeviceDescriptor>() as _,
|
||||
},
|
||||
Some((PageBox::as_bytes_mut(&mut output), UsbDirection::In)),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(unsafe { output.assume_init() })
|
||||
}
|
||||
|
||||
async fn fill_configuation_descriptor(
|
||||
@ -204,7 +214,7 @@ impl UsbControlPipeAccess {
|
||||
index: u8,
|
||||
buffer: &mut PageBox<[MaybeUninit<u8>]>,
|
||||
) -> Result<(), UsbError> {
|
||||
self.perform_value_control(
|
||||
self.control_transfer(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: 0b10000000,
|
||||
b_request: 0x06,
|
||||
@ -212,13 +222,33 @@ impl UsbControlPipeAccess {
|
||||
w_index: 0,
|
||||
w_length: buffer.len().try_into().unwrap(),
|
||||
},
|
||||
Some((
|
||||
unsafe { buffer.as_physical_address() },
|
||||
buffer.len(),
|
||||
UsbDirection::In,
|
||||
)),
|
||||
Some((buffer.as_slice_mut(), UsbDirection::In)),
|
||||
)
|
||||
.await
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn query_string(
|
||||
&self,
|
||||
index: u8,
|
||||
buffer: &mut PageBox<MaybeUninit<[u8; 4096]>>,
|
||||
) -> Result<String, UsbError> {
|
||||
self.control_transfer(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: 0b10000000,
|
||||
b_request: 0x06,
|
||||
w_value: 0x300 | (index as u16),
|
||||
w_index: 0,
|
||||
w_length: 4096,
|
||||
},
|
||||
Some((PageBox::as_bytes_mut(buffer), UsbDirection::In)),
|
||||
)
|
||||
.await?;
|
||||
let data = unsafe { buffer.assume_init_ref() };
|
||||
|
||||
let len = data[0] as usize;
|
||||
|
||||
decode_usb_string(&data[2..len])
|
||||
}
|
||||
|
||||
pub async fn query_configuration_descriptor(
|
||||
@ -259,52 +289,12 @@ impl UsbControlPipeAccess {
|
||||
Ok(ConfigurationDescriptorQuery { buffer })
|
||||
}
|
||||
|
||||
pub async fn query_device_descriptor(&self) -> Result<PageBox<UsbDeviceDescriptor>, UsbError> {
|
||||
let mut output = PageBox::new_uninit().map_err(UsbError::MemoryError)?;
|
||||
self.perform_value_control(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: 0b10000000,
|
||||
b_request: 0x06,
|
||||
w_value: 0x100,
|
||||
w_index: 0,
|
||||
w_length: size_of::<UsbDeviceDescriptor>() as _,
|
||||
},
|
||||
Some(input_buffer(&mut output)),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(unsafe { output.assume_init() })
|
||||
}
|
||||
|
||||
pub async fn query_string(
|
||||
&self,
|
||||
index: u8,
|
||||
buffer: &mut PageBox<MaybeUninit<[u8; 4096]>>,
|
||||
) -> Result<String, UsbError> {
|
||||
self.perform_value_control(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: 0b10000000,
|
||||
b_request: 0x06,
|
||||
w_value: 0x300 | (index as u16),
|
||||
w_index: 0,
|
||||
w_length: 4096,
|
||||
},
|
||||
Some(input_buffer(buffer)),
|
||||
)
|
||||
.await?;
|
||||
let data = unsafe { buffer.assume_init_ref() };
|
||||
|
||||
let len = data[0] as usize;
|
||||
|
||||
decode_usb_string(&data[2..len])
|
||||
}
|
||||
|
||||
pub async fn perform_action<D: UsbDeviceRequest>(
|
||||
&self,
|
||||
w_value: u16,
|
||||
w_index: u16,
|
||||
) -> Result<(), UsbError> {
|
||||
self.perform_value_control(
|
||||
self.control_transfer(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: D::BM_REQUEST_TYPE,
|
||||
b_request: D::B_REQUEST,
|
||||
@ -314,7 +304,8 @@ impl UsbControlPipeAccess {
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn class_specific_request<D: UsbClassSpecificRequest>(
|
||||
@ -322,7 +313,7 @@ impl UsbControlPipeAccess {
|
||||
w_value: u16,
|
||||
w_index: u16,
|
||||
) -> Result<(), UsbError> {
|
||||
self.perform_value_control(
|
||||
self.control_transfer(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: D::BM_REQUEST_TYPE,
|
||||
b_request: D::B_REQUEST,
|
||||
@ -332,7 +323,8 @@ impl UsbControlPipeAccess {
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_configuration(&self, value: u16) -> Result<(), UsbError> {
|
||||
|
@ -1,32 +0,0 @@
|
||||
use core::ops::Deref;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use libk_mm::PageBox;
|
||||
|
||||
use crate::{communication::UsbInterruptTransfer, error::UsbError};
|
||||
|
||||
use super::UsbGenericPipe;
|
||||
|
||||
pub trait UsbInterruptInPipe: UsbGenericPipe + Send + Sync {
|
||||
fn start_read(&self, buffer: &mut PageBox<[u8]>) -> Result<UsbInterruptTransfer, UsbError>;
|
||||
fn complete_transfer(&self, transfer: UsbInterruptTransfer);
|
||||
}
|
||||
|
||||
pub struct UsbInterruptInPipeAccess(pub Box<dyn UsbInterruptInPipe>);
|
||||
|
||||
impl UsbInterruptInPipeAccess {
|
||||
pub async fn read<'a>(&self, buffer: &'a mut PageBox<[u8]>) -> Result<&'a [u8], UsbError> {
|
||||
let transfer = self.start_read(buffer)?;
|
||||
let len = transfer.wait().await?;
|
||||
self.complete_transfer(transfer);
|
||||
Ok(&buffer[..len])
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for UsbInterruptInPipeAccess {
|
||||
type Target = dyn UsbInterruptInPipe;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
@ -1,9 +1,2 @@
|
||||
pub mod bulk;
|
||||
pub mod control;
|
||||
pub mod interrupt;
|
||||
|
||||
pub trait UsbGenericPipe {}
|
||||
|
||||
pub enum UsbPipe {
|
||||
Control(control::UsbControlPipeAccess),
|
||||
}
|
||||
pub mod normal;
|
||||
|
62
kernel/driver/bus/usb/src/pipe/normal.rs
Normal file
62
kernel/driver/bus/usb/src/pipe/normal.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use core::ops::Deref;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use async_trait::async_trait;
|
||||
use libk_mm::PageSlice;
|
||||
|
||||
use crate::error::{TransferError, UsbError};
|
||||
|
||||
#[async_trait]
|
||||
pub trait UsbNormalPipeIn: Send + Sync {
|
||||
async fn read(&self, buffer: &mut PageSlice<u8>) -> Result<usize, UsbError>;
|
||||
async fn read_exact(&self, buffer: &mut PageSlice<u8>) -> Result<(), UsbError> {
|
||||
match self.read(buffer).await {
|
||||
Ok(len) if len == buffer.len() => Ok(()),
|
||||
Ok(len) => Err(UsbError::TransferFailed(TransferError::ShortPacket(len))),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait UsbNormalPipeOut: Send + Sync {
|
||||
async fn write(&self, buffer: &PageSlice<u8>) -> Result<usize, UsbError>;
|
||||
}
|
||||
|
||||
pub struct UsbBulkInPipeAccess(pub Box<dyn UsbNormalPipeIn>);
|
||||
pub struct UsbBulkOutPipeAccess(pub Box<dyn UsbNormalPipeOut>);
|
||||
|
||||
pub struct UsbInterruptInPipeAccess(pub Box<dyn UsbNormalPipeIn>);
|
||||
pub struct UsbInterruptOutPipeAccess(pub Box<dyn UsbNormalPipeOut>);
|
||||
|
||||
impl Deref for UsbBulkInPipeAccess {
|
||||
type Target = dyn UsbNormalPipeIn;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for UsbBulkOutPipeAccess {
|
||||
type Target = dyn UsbNormalPipeOut;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for UsbInterruptInPipeAccess {
|
||||
type Target = dyn UsbNormalPipeIn;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for UsbInterruptOutPipeAccess {
|
||||
type Target = dyn UsbNormalPipeOut;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.deref()
|
||||
}
|
||||
}
|
@ -16,8 +16,9 @@ libk = { path = "../../../libk" }
|
||||
|
||||
xhci_lib = { git = "https://github.com/rust-osdev/xhci.git", package = "xhci" }
|
||||
|
||||
atomic_enum = "0.3.0"
|
||||
log = "0.4.22"
|
||||
tock-registers = "0.8.1"
|
||||
bytemuck = { version = "1.16.1", features = ["derive"] }
|
||||
futures-util = { version = "0.3.30", default-features = false, features = ["alloc", "async-await"] }
|
||||
async-trait.workspace = true
|
||||
log.workspace = true
|
||||
atomic_enum.workspace = true
|
||||
tock-registers.workspace = true
|
||||
bytemuck.workspace = true
|
||||
futures-util.workspace = true
|
||||
|
@ -8,10 +8,7 @@ use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
use xhci_lib::context::{self, DeviceHandler, InputHandler};
|
||||
use ygg_driver_usb::error::UsbError;
|
||||
|
||||
use crate::{
|
||||
controller::{ContextSize, PortNumber},
|
||||
regs::PortSpeed,
|
||||
};
|
||||
use crate::regs::{ContextSize, PortNumber};
|
||||
|
||||
pub enum XhciDeviceContext {
|
||||
Context32(IrqSafeRwLock<PageBox<context::Device32Byte>>),
|
||||
@ -66,10 +63,9 @@ impl XhciInputContext {
|
||||
|
||||
pub fn new_address_device(
|
||||
size: ContextSize,
|
||||
bus_address: u8,
|
||||
root_hub_port_number: PortNumber,
|
||||
speed: PortSpeed,
|
||||
max_packet_size: Option<usize>, // if not set, a default one for the given speed is used
|
||||
max_packet_size: usize,
|
||||
speed: u8,
|
||||
dequeue_pointer: PhysicalAddress,
|
||||
) -> Result<Self, UsbError> {
|
||||
let mut cx = Self::new(size)?;
|
||||
@ -86,9 +82,8 @@ impl XhciInputContext {
|
||||
|
||||
slot.set_context_entries(1);
|
||||
slot.set_interrupter_target(0);
|
||||
slot.set_usb_device_address(bus_address);
|
||||
slot.set_root_hub_port_number(root_hub_port_number.into());
|
||||
slot.set_speed(speed.into());
|
||||
slot.set_speed(speed);
|
||||
}
|
||||
|
||||
{
|
||||
@ -97,9 +92,7 @@ impl XhciInputContext {
|
||||
ep0.set_endpoint_type(context::EndpointType::Control);
|
||||
ep0.set_error_count(3);
|
||||
// Use the provided max_packet_size, or the default one for the given speed
|
||||
ep0.set_max_packet_size(
|
||||
max_packet_size.unwrap_or(speed.default_max_packet_size()) as u16
|
||||
);
|
||||
ep0.set_max_packet_size(max_packet_size as u16);
|
||||
ep0.set_tr_dequeue_pointer(dequeue_pointer.into_u64());
|
||||
ep0.set_dequeue_cycle_state();
|
||||
}
|
||||
|
@ -1,13 +1,9 @@
|
||||
use core::{
|
||||
fmt,
|
||||
mem::MaybeUninit,
|
||||
num::NonZeroU8,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
use core::sync::atomic::{AtomicU8, Ordering};
|
||||
|
||||
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, vec, vec::Vec};
|
||||
use alloc::{boxed::Box, collections::btree_map::BTreeMap, sync::Arc, vec::Vec};
|
||||
use async_trait::async_trait;
|
||||
use device_api::{device::Device, interrupt::InterruptHandler};
|
||||
use libk::task::runtime;
|
||||
use libk::{error::Error, task::runtime};
|
||||
use libk_mm::{
|
||||
address::{AsPhysicalAddress, PhysicalAddress},
|
||||
PageBox,
|
||||
@ -16,492 +12,384 @@ use libk_util::{
|
||||
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock},
|
||||
OneTimeInit,
|
||||
};
|
||||
use xhci_lib::ExtendedCapability;
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
LocalRegisterCopy,
|
||||
};
|
||||
use ygg_driver_pci::device::PciDeviceInfo;
|
||||
use ygg_driver_usb::{
|
||||
bus::UsbBusManager,
|
||||
device::{UsbBusAddress, UsbDeviceAccess},
|
||||
device::{UsbBusAddress, UsbDeviceAccess, UsbSpeed},
|
||||
error::UsbError,
|
||||
info::UsbVersion,
|
||||
pipe::control::UsbControlPipeAccess,
|
||||
util::UsbAddressAllocator,
|
||||
UsbHostController,
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
context::{XhciDeviceContext, XhciInputContext},
|
||||
device::XhciBusDevice,
|
||||
pipe::ControlPipe,
|
||||
regs::{Mapper, PortSpeed, Regs},
|
||||
regs::{
|
||||
self,
|
||||
extended::ExtendedCapability,
|
||||
operational::{PortRegs, CONFIG, PORTSC, USBCMD, USBSTS},
|
||||
PortNumber, Regs,
|
||||
},
|
||||
ring::{
|
||||
CommandExecutor, CommandRing, ControlTransferRing, Event, EventRing, EventRingSegmentTable,
|
||||
GenericTransferRing,
|
||||
command::CommandRing,
|
||||
event::{Event, EventRing, EventRingSegmentTable},
|
||||
transfer::TransferRing,
|
||||
CommandExecutor, GenericRing,
|
||||
},
|
||||
util::EventBitmap,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ContextSize {
|
||||
Context32,
|
||||
Context64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct PortNumber(NonZeroU8);
|
||||
|
||||
impl PortNumber {
|
||||
pub fn new(port: u8) -> Option<Self> {
|
||||
NonZeroU8::new(port).map(Self)
|
||||
}
|
||||
|
||||
pub fn from_port_index(index: usize) -> Option<Self> {
|
||||
let as_u8 = (index + 1).try_into().ok()?;
|
||||
Some(Self(unsafe { NonZeroU8::new_unchecked(as_u8) }))
|
||||
}
|
||||
|
||||
pub fn port_index(&self) -> usize {
|
||||
u8::from(self.0) as usize - 1
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
struct Scratchpads {
|
||||
buffers: Vec<PageBox<[MaybeUninit<u8>]>>,
|
||||
struct ScratchpadArray {
|
||||
buffers: Vec<PageBox<[u8]>>,
|
||||
array: PageBox<[PhysicalAddress]>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
struct RootHubPort {
|
||||
version: UsbVersion,
|
||||
slot_type: u8,
|
||||
max_hub_depth: Option<usize>,
|
||||
}
|
||||
|
||||
pub struct Xhci {
|
||||
regs: Regs,
|
||||
|
||||
bus_address: OneTimeInit<u16>,
|
||||
address_allocator: UsbAddressAllocator,
|
||||
|
||||
port_count: usize,
|
||||
pub(crate) context_size: ContextSize,
|
||||
pub(crate) regs: Regs,
|
||||
#[allow(unused)]
|
||||
pci: PciDeviceInfo,
|
||||
|
||||
dcbaa: IrqSafeRwLock<PageBox<[PhysicalAddress]>>,
|
||||
endpoints: IrqSafeRwLock<BTreeMap<(u8, u8), Arc<dyn GenericTransferRing>>>,
|
||||
event_ring: EventRing,
|
||||
pub(crate) command_ring: CommandRing,
|
||||
#[allow(unused)]
|
||||
scratchpads: Option<Scratchpads>,
|
||||
|
||||
root_hub_map: BTreeMap<PortNumber, RootHubPort>,
|
||||
scratchpads: Option<ScratchpadArray>,
|
||||
pub(crate) command_ring: CommandRing,
|
||||
event_ring: EventRing,
|
||||
erst: EventRingSegmentTable,
|
||||
|
||||
root_hub_ports: Vec<Option<RootHubPort>>,
|
||||
pub(crate) endpoints: IrqSafeRwLock<BTreeMap<(u8, u8), Arc<TransferRing>>>,
|
||||
pub(crate) slots: Vec<IrqSafeRwLock<Option<Arc<XhciBusDevice>>>>,
|
||||
pub(crate) port_slot_map: Vec<AtomicU8>,
|
||||
bus_index: OneTimeInit<u16>,
|
||||
port_event_map: EventBitmap,
|
||||
}
|
||||
|
||||
impl Scratchpads {
|
||||
pub fn new(count: usize) -> Result<Option<Self>, UsbError> {
|
||||
if count == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let buffers = (0..count)
|
||||
.map(|_| PageBox::new_uninit_slice(4096))
|
||||
impl ScratchpadArray {
|
||||
pub fn new(capacity: usize, element_size: usize) -> Result<Self, UsbError> {
|
||||
let buffers = (0..capacity)
|
||||
.map(|_| PageBox::new_slice(0, element_size))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(UsbError::MemoryError)?;
|
||||
let array = PageBox::new_slice_with(|i| unsafe { buffers[i].as_physical_address() }, count)
|
||||
let array =
|
||||
PageBox::new_slice_with(|i| unsafe { buffers[i].as_physical_address() }, capacity)
|
||||
.map_err(UsbError::MemoryError)?;
|
||||
|
||||
Ok(Some(Self { buffers, array }))
|
||||
Ok(Self { buffers, array })
|
||||
}
|
||||
}
|
||||
|
||||
impl Xhci {
|
||||
// Extract all info about the xHC, but don't do any init besides performing a BIOS->OS handoff
|
||||
pub fn new(
|
||||
regs: xhci_lib::Registers<Mapper>,
|
||||
mut extended: xhci_lib::extended_capabilities::List<Mapper>,
|
||||
) -> Result<Self, UsbError> {
|
||||
let event_ring = EventRing::new(128)?;
|
||||
let command_ring = CommandRing::new(128)?;
|
||||
|
||||
let regs = Regs::from(regs);
|
||||
|
||||
let scratchpad_count = regs.scratchpad_count();
|
||||
let port_count = regs.port_count();
|
||||
let slot_count = regs.max_slot_count();
|
||||
let context_size = regs.context_size();
|
||||
let context_size = match context_size {
|
||||
32 => ContextSize::Context32,
|
||||
64 => ContextSize::Context64,
|
||||
_ => {
|
||||
log::error!("Unhandled context size: {context_size}");
|
||||
return Err(UsbError::InvalidConfiguration);
|
||||
}
|
||||
};
|
||||
|
||||
let scratchpads = Scratchpads::new(scratchpad_count)?;
|
||||
let mut dcbaa = PageBox::new_slice(PhysicalAddress::ZERO, slot_count + 1)
|
||||
pub fn new(pci: PciDeviceInfo, regs: Regs) -> Result<Self, UsbError> {
|
||||
let mut dcbaa = PageBox::new_slice(PhysicalAddress::ZERO, regs.slot_count + 1)
|
||||
.map_err(UsbError::MemoryError)?;
|
||||
let command_ring = CommandRing::new(128)?;
|
||||
let event_ring = EventRing::new(128)?;
|
||||
let erst = EventRingSegmentTable::for_event_rings(&[&event_ring])?;
|
||||
|
||||
if let Some(scratchpads) = scratchpads.as_ref() {
|
||||
dcbaa[0] = unsafe { scratchpads.array.as_physical_address() };
|
||||
}
|
||||
|
||||
let mut root_hub_map = BTreeMap::new();
|
||||
for cap in extended.into_iter() {
|
||||
let Ok(cap) = cap else {
|
||||
continue;
|
||||
// Setup scratch buffers
|
||||
// TODO: Linux seems to just ignore the PAGESIZE, it's (1 << 0) everywhere
|
||||
let scratchpads = if regs.scratch_count != 0 {
|
||||
let array = ScratchpadArray::new(regs.scratch_count, 0x1000)?;
|
||||
dcbaa[0] = unsafe { array.array.as_physical_address() };
|
||||
Some(array)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut root_hub_ports: Vec<Option<_>> = (0..regs.port_count).map(|_| None).collect();
|
||||
|
||||
for cap in regs.extended_capabilities.iter() {
|
||||
match cap {
|
||||
ExtendedCapability::UsbLegacySupport(mut legsup) => {
|
||||
let mut handoff = false;
|
||||
legsup.usblegsup.update_volatile(|legsup| {
|
||||
if legsup.hc_bios_owned_semaphore() {
|
||||
handoff = true;
|
||||
legsup.set_hc_os_owned_semaphore();
|
||||
}
|
||||
});
|
||||
|
||||
if !handoff {
|
||||
continue;
|
||||
}
|
||||
log::info!("xhci: BIOS->OS handoff started");
|
||||
|
||||
let mut timeout = 10000000;
|
||||
while timeout > 0 {
|
||||
let status = legsup.usblegsup.read_volatile();
|
||||
if !status.hc_bios_owned_semaphore() && status.hc_os_owned_semaphore() {
|
||||
break;
|
||||
}
|
||||
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
}
|
||||
if timeout == 0 {
|
||||
log::error!("xhci: BIOS->OS handoff failed");
|
||||
return Err(UsbError::DeviceBusy);
|
||||
}
|
||||
log::info!("xhci: BIOS->OS handoff finished");
|
||||
}
|
||||
ExtendedCapability::XhciSupportedProtocol(proto) => {
|
||||
let header = proto.header.read_volatile();
|
||||
|
||||
if header.name_string() != 0x20425355 {
|
||||
log::warn!("Skip unknown xHCI supported protocol capability");
|
||||
continue;
|
||||
}
|
||||
let version_major = header.major_revision();
|
||||
let version_minor = header.minor_revision();
|
||||
let slot_type = header.protocol_slot_type();
|
||||
let max_hub_depth = header.hub_depth();
|
||||
let version = ((version_major as u16) << 8) | (version_minor as u16);
|
||||
let Some(version) = UsbVersion::from_bcd_usb(version) else {
|
||||
log::warn!("Skip unknown xHCI supported protocol revision: {version_major:x}.{version_minor:x}");
|
||||
ExtendedCapability::ProtocolSupport(support) => {
|
||||
let Some(version) = support.usb_revision() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let port_range = header.compatible_port_offset()
|
||||
..header.compatible_port_offset() + header.compatible_port_count();
|
||||
log::info!("Ports {port_range:?}: USB {version_major:x}.{version_minor:x}, slot type {slot_type}, max hub depth {max_hub_depth}");
|
||||
|
||||
for port in port_range {
|
||||
let Some(number) = PortNumber::new(port) else {
|
||||
continue;
|
||||
};
|
||||
root_hub_map.insert(
|
||||
number,
|
||||
RootHubPort {
|
||||
for port in support.port_range() {
|
||||
log::info!("* Port {port}: {version}");
|
||||
root_hub_ports[port as usize - 1] = Some(RootHubPort {
|
||||
version,
|
||||
slot_type,
|
||||
max_hub_depth: (max_hub_depth != 0)
|
||||
.then_some(max_hub_depth as usize),
|
||||
},
|
||||
);
|
||||
slot_type: support.slot_type(),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
ExtendedCapability::LegacySupport(legsup) => {
|
||||
legsup.write().perform_bios_handoff(10000000)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let port_slot_map = (0..regs.port_count).map(|_| AtomicU8::new(0)).collect();
|
||||
let slots = (0..regs.slot_count)
|
||||
.map(|_| IrqSafeRwLock::new(None))
|
||||
.collect();
|
||||
|
||||
Ok(Self {
|
||||
regs,
|
||||
|
||||
bus_address: OneTimeInit::new(),
|
||||
address_allocator: UsbAddressAllocator::new(),
|
||||
|
||||
port_count,
|
||||
context_size,
|
||||
|
||||
event_ring,
|
||||
command_ring,
|
||||
pci,
|
||||
|
||||
dcbaa: IrqSafeRwLock::new(dcbaa),
|
||||
endpoints: IrqSafeRwLock::new(BTreeMap::new()),
|
||||
scratchpads,
|
||||
root_hub_map,
|
||||
command_ring,
|
||||
event_ring,
|
||||
erst,
|
||||
|
||||
root_hub_ports,
|
||||
bus_index: OneTimeInit::new(),
|
||||
endpoints: IrqSafeRwLock::new(BTreeMap::new()),
|
||||
slots,
|
||||
port_slot_map,
|
||||
port_event_map: EventBitmap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn register_device_context(&self, slot_id: u8, context: PhysicalAddress) {
|
||||
self.dcbaa.write()[slot_id as usize] = context;
|
||||
fn notify_endpoint(&self, slot_id: u8, endpoint_id: u8, address: PhysicalAddress, status: u32) {
|
||||
if let Some(endpoint) = self.endpoints.read().get(&(slot_id, endpoint_id)) {
|
||||
endpoint.notify(address, status);
|
||||
} else {
|
||||
log::warn!("Endpoint {slot_id}:{endpoint_id} does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_endpoint(
|
||||
&self,
|
||||
slot_id: u8,
|
||||
endpoint_id: u8,
|
||||
ring: Arc<dyn GenericTransferRing>,
|
||||
) {
|
||||
self.endpoints.write().insert((slot_id, endpoint_id), ring);
|
||||
}
|
||||
|
||||
pub fn shutdown_endpoint(&self, slot_id: u8, endpoint_id: u8) {
|
||||
if let Some(endpoint) = self.endpoints.write().remove(&(slot_id, endpoint_id)) {
|
||||
fn kill_endpoint(&self, slot_id: u8, endpoint_id: u8) {
|
||||
let endpoint = self.endpoints.write().remove(&(slot_id, endpoint_id));
|
||||
if let Some(endpoint) = endpoint {
|
||||
endpoint.shutdown();
|
||||
} else {
|
||||
log::warn!(
|
||||
"Endpoint {}:{} does not exist, maybe already shut down?",
|
||||
}
|
||||
}
|
||||
|
||||
async fn allocate_device_slot(
|
||||
self: &Arc<Self>,
|
||||
port_id: PortNumber,
|
||||
slot_type: u8,
|
||||
speed: UsbSpeed,
|
||||
) -> Result<(u8, Arc<XhciBusDevice>, Arc<TransferRing>), UsbError> {
|
||||
let device_context = XhciDeviceContext::new(self.regs.context_size)?;
|
||||
let slot_id = self.command_ring.enable_slot(&**self, slot_type).await?;
|
||||
let (control_pipe, control_ring) = ControlPipe::new(self.clone(), slot_id, 1, 128)?;
|
||||
let control_pipe = UsbControlPipeAccess(Box::new(control_pipe));
|
||||
|
||||
self.dcbaa.write()[slot_id as usize] = device_context.physical_address();
|
||||
self.endpoints
|
||||
.write()
|
||||
.insert((slot_id, 1), control_ring.clone());
|
||||
|
||||
let slot = Arc::new(XhciBusDevice {
|
||||
port_id,
|
||||
slot_id,
|
||||
endpoint_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify_transfer(
|
||||
&self,
|
||||
slot_id: u8,
|
||||
endpoint_id: u8,
|
||||
address: PhysicalAddress,
|
||||
status: u32,
|
||||
) {
|
||||
if let Some(ep) = self.endpoints.read().get(&(slot_id, endpoint_id)) {
|
||||
ep.notify(address, status);
|
||||
} else {
|
||||
log::warn!("No endpoint slot={slot_id}, ep={endpoint_id}");
|
||||
}
|
||||
}
|
||||
|
||||
async fn reset_port(&self, port: PortNumber) -> Result<(), UsbError> {
|
||||
let index = port.port_index();
|
||||
|
||||
// Set port reset and wait for it to clear
|
||||
self.regs.ports.update(index, |regs| {
|
||||
regs.portsc.set_port_reset();
|
||||
device_context,
|
||||
control_pipe,
|
||||
speed,
|
||||
detach_handler: IrqSafeSpinlock::new(None),
|
||||
endpoints: IrqSafeRwLock::new(BTreeMap::from_iter([(1, control_ring.clone())])),
|
||||
xhci: self.clone(),
|
||||
bus_address: OneTimeInit::new(),
|
||||
});
|
||||
|
||||
// TODO timeout
|
||||
loop {
|
||||
self.port_event_map.wait_specific(index).await;
|
||||
*self.slots[slot_id as usize].write() = Some(slot.clone());
|
||||
self.port_slot_map[port_id.index()].store(slot_id, Ordering::Release);
|
||||
|
||||
let mut status = None;
|
||||
self.regs.ports.update(index, |regs| {
|
||||
// Port became disconnected
|
||||
if !regs.portsc.port_enabled_disabled() || !regs.portsc.current_connect_status() {
|
||||
log::warn!("xhci: port {port} disconnected during reset");
|
||||
status = Some(Err(UsbError::DeviceDisconnected));
|
||||
return;
|
||||
Ok((slot_id, slot, control_ring))
|
||||
}
|
||||
|
||||
if !regs.portsc.port_reset() {
|
||||
regs.portsc.clear_port_reset_change();
|
||||
status = Some(Ok(()));
|
||||
}
|
||||
});
|
||||
// TODO proper timeout
|
||||
async fn reset_port(&self, regs: &PortRegs) -> Result<(), UsbError> {
|
||||
// Reset the port
|
||||
regs.modify_portsc_preserving(PORTSC::PR::SET);
|
||||
|
||||
if let Some(status) = status {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
let mut timeout = 10000000;
|
||||
while timeout > 0 {
|
||||
let status = regs.PORTSC.extract();
|
||||
|
||||
if status.matches_all(PORTSC::PR::CLEAR) {
|
||||
break;
|
||||
}
|
||||
|
||||
async fn setup_connected_port(self: Arc<Self>, port: PortNumber) -> Result<(), UsbError> {
|
||||
// TODO cleanup after a failed device init:
|
||||
// * Deallocate the bus address
|
||||
// * Issue a Disable Slot TRB
|
||||
// * Remove Device Context from DCBAA
|
||||
// * Deregister the device's Default Control Endpoint
|
||||
timeout -= 1;
|
||||
}
|
||||
|
||||
let index = port.port_index();
|
||||
let root_hub_port = self
|
||||
.root_hub_map
|
||||
.get(&port)
|
||||
// Clear PRC
|
||||
regs.modify_portsc_preserving(PORTSC::PRC::SET);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO clean up resources if init fails
|
||||
async fn setup_port(
|
||||
self: &Arc<Self>,
|
||||
regs: &PortRegs,
|
||||
number: PortNumber,
|
||||
) -> Result<(), UsbError> {
|
||||
let root_hub_port = self.root_hub_ports[number.index()]
|
||||
.as_ref()
|
||||
.ok_or(UsbError::PortInitFailed)?;
|
||||
|
||||
log::info!(
|
||||
"xhci: setup {} device at port {port}",
|
||||
root_hub_port.version
|
||||
);
|
||||
let need_reset = !root_hub_port.version.is_version_3();
|
||||
|
||||
if root_hub_port.version == UsbVersion::Usb20 {
|
||||
// Port needs a reset first
|
||||
log::info!("xhci: reset port {port}");
|
||||
self.reset_port(port).await?;
|
||||
if need_reset {
|
||||
self.reset_port(regs).await?;
|
||||
}
|
||||
|
||||
let speed = PortSpeed::try_from(self.regs.ports.read(index).portsc.port_speed())
|
||||
.map_err(|_| UsbError::PortInitFailed)?;
|
||||
let status = regs.PORTSC.extract();
|
||||
|
||||
log::info!("xhci: port {port} effective speed {speed:?}");
|
||||
let speed = status.read(PORTSC::PS) as u8;
|
||||
let pls = status.read(PORTSC::PLS);
|
||||
|
||||
// Allocate a device slot
|
||||
let slot_id = self
|
||||
.command_ring
|
||||
.enable_slot(&*self, root_hub_port.slot_type)
|
||||
.await?;
|
||||
|
||||
// Allocate some address for the device
|
||||
let bus_address = self.address_allocator.allocate()?;
|
||||
|
||||
// Allocate a default endpoint ring
|
||||
let control_ring = Arc::new(ControlTransferRing::new(slot_id, 1, 128)?);
|
||||
|
||||
// Register control endpoint
|
||||
self.register_endpoint(slot_id, 1, control_ring.clone());
|
||||
|
||||
let control_pipe = UsbControlPipeAccess(Box::new(ControlPipe::new(
|
||||
self.clone(),
|
||||
slot_id,
|
||||
control_ring.clone(),
|
||||
)));
|
||||
|
||||
// Setup Device Context
|
||||
let device_cx = XhciDeviceContext::new(self.context_size)?;
|
||||
self.register_device_context(slot_id, device_cx.physical_address());
|
||||
|
||||
// Issue an Address Device TRB with BSR=1 first
|
||||
let input_cx = XhciInputContext::new_address_device(
|
||||
self.context_size,
|
||||
bus_address,
|
||||
port,
|
||||
speed,
|
||||
None,
|
||||
control_ring.dequeue_pointer(),
|
||||
)?;
|
||||
self.command_ring
|
||||
.address_device(&*self, slot_id, input_cx.physical_address(), true)
|
||||
.await?;
|
||||
|
||||
let state = device_cx.map(|cx| cx.slot().slot_state());
|
||||
log::info!("xhci: port {port} slot {slot_id} state after BSR=1: {state:?}");
|
||||
|
||||
// After an Address Device with BSR=1, retrieving a Device Descriptor is possible
|
||||
// This is needed for Full-speed devices, where the max_packet_size is 8 at first, but
|
||||
// is determined by the Device Descriptor when it's available
|
||||
let device_descriptor = control_pipe.query_device_descriptor().await?;
|
||||
let max_packet_size =
|
||||
device_descriptor.max_packet_size(root_hub_port.version, speed.into())?;
|
||||
|
||||
log::info!("xhci: port {port}:");
|
||||
log::info!(" * max_packet_size = {max_packet_size}");
|
||||
let max_packet_size = Some(max_packet_size);
|
||||
|
||||
// Reset the control endpoint for a proper dequeue pointer and a proper DCS
|
||||
control_ring.reset();
|
||||
|
||||
// Issue an Address Device TRB with BSR=0
|
||||
let input_cx = XhciInputContext::new_address_device(
|
||||
self.context_size,
|
||||
bus_address,
|
||||
port,
|
||||
speed,
|
||||
max_packet_size,
|
||||
control_ring.dequeue_pointer(),
|
||||
)?;
|
||||
self.command_ring
|
||||
.address_device(&*self, slot_id, input_cx.physical_address(), false)
|
||||
.await
|
||||
.inspect_err(|error| {
|
||||
log::error!("xhci: port {port} Address Device (BSR=0) error: {error:?}")
|
||||
})?;
|
||||
|
||||
let state = device_cx.map(|cx| cx.slot().slot_state());
|
||||
log::info!("xhci: port {port} slot {slot_id} state after BSR=0: {state:?}");
|
||||
|
||||
log::info!("xhci: port {port} device addressed ({bus_address})");
|
||||
|
||||
// Hand off the device to the general USB stack
|
||||
|
||||
let bus_address = UsbBusAddress {
|
||||
bus: *self.bus_address.get(),
|
||||
device: bus_address,
|
||||
let (usb_speed, max_packet_size) = match speed {
|
||||
1 => (UsbSpeed::Full, 8),
|
||||
2 => (UsbSpeed::Low, 8),
|
||||
3 => (UsbSpeed::High, 64),
|
||||
4 => (UsbSpeed::Super, 512),
|
||||
_ => {
|
||||
log::error!("Port {number} invalid speed value {speed}");
|
||||
return Err(UsbError::DeviceDisconnected);
|
||||
}
|
||||
};
|
||||
|
||||
let device = Box::new(XhciBusDevice {
|
||||
xhci: self.clone(),
|
||||
slot_id,
|
||||
port_id: port.into(),
|
||||
bus_address,
|
||||
speed,
|
||||
control_pipe,
|
||||
device_context: device_cx,
|
||||
rings: IrqSafeRwLock::new(vec![control_ring]),
|
||||
detach_handler: IrqSafeSpinlock::new(None),
|
||||
});
|
||||
let device = UsbDeviceAccess::setup(device).await?;
|
||||
// PLS != 0 after reset
|
||||
if pls != 0 {
|
||||
log::error!("Port {number} pls!=0 after reset");
|
||||
return Err(UsbError::DeviceDisconnected);
|
||||
}
|
||||
|
||||
UsbBusManager::register_device(Arc::new(device));
|
||||
let (slot_id, slot, control_ring) = self
|
||||
.allocate_device_slot(number, root_hub_port.slot_type, usb_speed)
|
||||
.await?;
|
||||
|
||||
let input_cx = XhciInputContext::new_address_device(
|
||||
self.regs.context_size,
|
||||
number,
|
||||
max_packet_size,
|
||||
speed,
|
||||
control_ring.base(),
|
||||
)?;
|
||||
self.command_ring
|
||||
.address_device(&**self, slot_id, input_cx.physical_address(), false)
|
||||
.await
|
||||
.inspect_err(|error| {
|
||||
log::error!("Port {number} Address Device TRB (BSR=0) failed: {error:?}")
|
||||
})?;
|
||||
|
||||
// TODO update max_packet_size for default control endpoint after fetching the device
|
||||
// descriptor
|
||||
|
||||
let bus_address = slot
|
||||
.device_context
|
||||
.map(|device| device.slot().usb_device_address());
|
||||
slot.bus_address.init(UsbBusAddress {
|
||||
bus: *self.bus_index.get(),
|
||||
device: bus_address,
|
||||
});
|
||||
|
||||
let device = UsbDeviceAccess::setup(slot).await?;
|
||||
UsbBusManager::register_device(device.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn port_manager_task(self: Arc<Self>) -> Result<(), UsbError> {
|
||||
// Inject events for the root hub ports
|
||||
for (&port, _) in self.root_hub_map.iter() {
|
||||
self.port_event_map.signal(port.port_index());
|
||||
async fn handle_disconnect(&self, number: PortNumber) -> Result<(), UsbError> {
|
||||
let slot_id = self.port_slot_map[number.index()].swap(0, Ordering::Acquire);
|
||||
if slot_id == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
log::info!("Port {number} disconnected");
|
||||
|
||||
let slot = self.slots[slot_id as usize].write().take();
|
||||
if let Some(slot) = slot {
|
||||
UsbBusManager::detach_device(*slot.bus_address.get());
|
||||
|
||||
// Clean up xHC resources
|
||||
for (&endpoint_id, endpoint) in slot.endpoints.read().iter() {
|
||||
endpoint.shutdown();
|
||||
self.kill_endpoint(slot_id, endpoint_id);
|
||||
}
|
||||
} else {
|
||||
log::warn!("No device in slot {slot_id}");
|
||||
}
|
||||
|
||||
let mut show_disconnect_mask = 0;
|
||||
loop {
|
||||
let events = self.port_event_map.wait_any(self.port_count).await;
|
||||
// TODO stop endpoints on the xHC side?
|
||||
self.command_ring.disable_slot(self, slot_id).await?;
|
||||
|
||||
for port_index in events {
|
||||
let port = PortNumber::from_port_index(port_index).unwrap();
|
||||
|
||||
let mut connected = None;
|
||||
|
||||
self.regs.ports.update(port_index, |regs| {
|
||||
if regs.portsc.port_enabled_disabled_change() {
|
||||
regs.portsc.clear_port_enabled_disabled_change();
|
||||
Ok(())
|
||||
}
|
||||
if regs.portsc.connect_status_change() {
|
||||
regs.portsc.clear_connect_status_change();
|
||||
connected = Some(regs.portsc.current_connect_status());
|
||||
}
|
||||
});
|
||||
|
||||
let Some(state) = connected else {
|
||||
async fn port_handler_task(self: Arc<Self>) -> Result<(), Error> {
|
||||
// Inject notify for ports that are already connected
|
||||
{
|
||||
let ports = self.regs.ports.write();
|
||||
for (index, port) in self.root_hub_ports.iter().enumerate() {
|
||||
if port.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ports[index].PORTSC.matches_all(PORTSC::CCS::SET) {
|
||||
self.port_event_map.signal(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
let events = self.port_event_map.wait_any(self.regs.port_count).await;
|
||||
|
||||
for index in events {
|
||||
let number = PortNumber::from_index(index);
|
||||
let ports = self.regs.ports.write();
|
||||
let regs = &ports[index];
|
||||
|
||||
let status = regs.PORTSC.extract();
|
||||
let neutral = regs::portsc_to_neutral(status);
|
||||
let mut clear = LocalRegisterCopy::<u32, PORTSC::Register>::new(0);
|
||||
|
||||
// Clear affected change bits
|
||||
let connected = if status.matches_all(PORTSC::CSC::SET) {
|
||||
clear.modify(PORTSC::CSC::SET);
|
||||
Some(status.matches_all(PORTSC::CCS::SET))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if status.matches_all(PORTSC::PEC::SET) {
|
||||
clear.modify(PORTSC::PEC::SET);
|
||||
}
|
||||
if status.matches_all(PORTSC::PLC::SET) {
|
||||
clear.modify(PORTSC::PLC::SET);
|
||||
}
|
||||
|
||||
if state {
|
||||
if show_disconnect_mask & (1 << port_index) == 0 {
|
||||
log::info!("xhci: port {port} connected");
|
||||
if let Err(error) = self.clone().setup_connected_port(port).await {
|
||||
show_disconnect_mask &= !(1 << port_index);
|
||||
log::error!("xhci: port {port} setup failed: {error:?}");
|
||||
} else {
|
||||
show_disconnect_mask |= 1 << port_index;
|
||||
regs.PORTSC.set(neutral.get() | clear.get());
|
||||
|
||||
if let Some(connected) = connected {
|
||||
if connected {
|
||||
log::info!("Port {number} connected");
|
||||
if let Err(error) = self.setup_port(regs, number).await {
|
||||
log::error!("Port {number} setup error: {error:?}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if show_disconnect_mask & (1 << port_index) != 0 {
|
||||
log::warn!("xhci: port {port} disconnected");
|
||||
show_disconnect_mask &= !(1 << port_index);
|
||||
|
||||
if !connected {
|
||||
if let Err(error) = self.handle_disconnect(number).await {
|
||||
log::error!("Port {number} disconnect error: {error:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(self: Arc<Self>) {
|
||||
fn handle_event(&self) {
|
||||
while let Some(event) = self.event_ring.try_dequeue() {
|
||||
match event {
|
||||
Event::PortChange(port) => {
|
||||
if port > 0 {
|
||||
self.port_event_map.signal(port - 1);
|
||||
Event::PortChange(number) => {
|
||||
if number > 0 {
|
||||
self.port_event_map.signal(number - 1);
|
||||
}
|
||||
}
|
||||
Event::CommandCompletion { address, reply } => {
|
||||
@ -513,79 +401,98 @@ impl Xhci {
|
||||
endpoint_id,
|
||||
status,
|
||||
} => {
|
||||
self.notify_transfer(slot_id, endpoint_id, address, status);
|
||||
self.notify_endpoint(slot_id, endpoint_id, address, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.regs
|
||||
.set_interrupter_0_dequeue_pointer(self.event_ring.dequeue_pointer());
|
||||
.runtime
|
||||
.write()
|
||||
.set_interrupter_dequeue_pointer(0, self.event_ring.dequeue_pointer());
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbHostController for Xhci {}
|
||||
|
||||
#[async_trait]
|
||||
impl CommandExecutor for Xhci {
|
||||
fn ring_doorbell(&self, index: usize, target: u8) {
|
||||
self.regs.ring_doorbell(index, target);
|
||||
self.regs.doorbells[index].store(target as u32, Ordering::Release);
|
||||
}
|
||||
|
||||
async fn reset_endpoint(
|
||||
&self,
|
||||
slot_id: u8,
|
||||
endpoint_id: u8,
|
||||
dequeue_pointer: PhysicalAddress,
|
||||
dequeue_cycle: bool,
|
||||
) -> Result<(), UsbError> {
|
||||
log::warn!("xhci: reset stalled endpoint {slot_id}:{endpoint_id}");
|
||||
|
||||
self.command_ring
|
||||
.reset_endpoint(&*self, slot_id, endpoint_id, false)
|
||||
.await?;
|
||||
self.command_ring
|
||||
.set_tr_dequeue_pointer(&*self, slot_id, endpoint_id, dequeue_pointer, dequeue_cycle)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Xhci {
|
||||
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
|
||||
static XHCI_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
log::info!("Init USB xHCI");
|
||||
self.regs.hc_reset(10000000)?;
|
||||
log::info!("xHC reset complete");
|
||||
|
||||
if XHCI_COUNT.fetch_add(1, Ordering::Release) != 0 {
|
||||
log::warn!("Skip second xhci init");
|
||||
return Ok(());
|
||||
}
|
||||
// Configure the HC
|
||||
let dcbaap = unsafe { self.dcbaa.read().as_physical_address() };
|
||||
let cr_base = self.command_ring.base();
|
||||
|
||||
self.regs.reset();
|
||||
self.regs.set_max_slot_count();
|
||||
let op = self.regs.operational.write();
|
||||
let rt = self.regs.runtime.write();
|
||||
|
||||
let erst = EventRingSegmentTable::for_event_rings(&[&self.event_ring])?;
|
||||
let dcbaa = self.dcbaa.read();
|
||||
log::info!("xhci: configure HC");
|
||||
op.CONFIG
|
||||
.modify(CONFIG::MaxSlotsEn.val(self.regs.slot_count as u32));
|
||||
op.set_dcbaap(dcbaap);
|
||||
op.set_crcr(cr_base, true);
|
||||
|
||||
self.regs
|
||||
.configure(&dcbaa, &self.command_ring, &self.event_ring, &erst);
|
||||
log::info!("xhci: configure interrupter");
|
||||
rt.configure_interrupter(0, &self.event_ring, &self.erst);
|
||||
|
||||
// Enable interrupts and start the HC
|
||||
log::info!("xhci: start HC");
|
||||
op.USBCMD.modify(USBCMD::INTE::SET + USBCMD::HSEE::SET);
|
||||
op.USBCMD.modify(USBCMD::RS::SET);
|
||||
op.wait_usbsts_bit(USBSTS::CNR::CLEAR, 100000000)?;
|
||||
|
||||
let bus = UsbBusManager::register_bus(self.clone());
|
||||
self.bus_address.init(bus);
|
||||
self.bus_index.init(bus);
|
||||
|
||||
// Start the port manager task
|
||||
runtime::spawn(self.clone().port_manager_task())?;
|
||||
runtime::spawn(self.clone().port_handler_task()).ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"USB xHCI"
|
||||
"xHCI"
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Xhci {
|
||||
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
|
||||
if let Some(status) = self.regs.handle_interrupt() {
|
||||
if status.event_interrupt() {
|
||||
let status = self.regs.handle_interrupt(0);
|
||||
|
||||
if status.matches_all(USBSTS::HSE::SET) {
|
||||
log::error!("xhci: Host System Error occurred");
|
||||
}
|
||||
|
||||
if status.matches_all(USBSTS::EINT::SET) {
|
||||
self.handle_event();
|
||||
}
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PortNumber {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PortNumber> for u8 {
|
||||
fn from(value: PortNumber) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
impl UsbHostController for Xhci {}
|
||||
|
@ -1,47 +1,177 @@
|
||||
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use futures_util::{future::BoxFuture, FutureExt};
|
||||
use libk_util::sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock};
|
||||
use alloc::{boxed::Box, collections::btree_map::BTreeMap, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use libk_util::{
|
||||
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock},
|
||||
OneTimeInit,
|
||||
};
|
||||
use xhci_lib::context;
|
||||
use ygg_driver_usb::{
|
||||
communication::UsbDirection,
|
||||
device::{UsbBusAddress, UsbDevice, UsbDeviceDetachHandler, UsbSpeed},
|
||||
error::UsbError,
|
||||
info::UsbEndpointType,
|
||||
pipe::{
|
||||
bulk::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
|
||||
control::UsbControlPipeAccess,
|
||||
interrupt::UsbInterruptInPipeAccess,
|
||||
normal::{UsbNormalPipeIn, UsbNormalPipeOut},
|
||||
},
|
||||
UsbDirection, UsbHostController,
|
||||
UsbHostController,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
context::{XhciDeviceContext, XhciInputContext},
|
||||
pipe::{BulkInPipe, BulkOutPipe, InterruptInPipe},
|
||||
regs::PortSpeed,
|
||||
ring::{
|
||||
transfer::{BulkInTransferRing, BulkOutTransferRing, InterruptInTransferRing},
|
||||
GenericTransferRing,
|
||||
},
|
||||
pipe::{NormalInPipe, NormalOutPipe},
|
||||
regs::PortNumber,
|
||||
ring::transfer::TransferRing,
|
||||
Xhci,
|
||||
};
|
||||
|
||||
pub struct XhciBusDevice {
|
||||
pub(crate) port_id: u8,
|
||||
pub(crate) port_id: PortNumber,
|
||||
pub(crate) slot_id: u8,
|
||||
pub(crate) bus_address: UsbBusAddress,
|
||||
|
||||
pub(crate) speed: PortSpeed,
|
||||
pub(crate) speed: UsbSpeed,
|
||||
pub(crate) bus_address: OneTimeInit<UsbBusAddress>,
|
||||
|
||||
pub(crate) xhci: Arc<Xhci>,
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) device_context: XhciDeviceContext,
|
||||
pub(crate) rings: IrqSafeRwLock<Vec<Arc<dyn GenericTransferRing>>>,
|
||||
pub(crate) endpoints: IrqSafeRwLock<BTreeMap<u8, Arc<TransferRing>>>,
|
||||
pub(crate) control_pipe: UsbControlPipeAccess,
|
||||
pub(crate) detach_handler: IrqSafeSpinlock<Option<Arc<dyn UsbDeviceDetachHandler>>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbDevice for XhciBusDevice {
|
||||
fn port_number(&self) -> u8 {
|
||||
self.port_id.into()
|
||||
}
|
||||
|
||||
fn bus_address(&self) -> UsbBusAddress {
|
||||
*self.bus_address.get()
|
||||
}
|
||||
|
||||
fn control_pipe(&self) -> &UsbControlPipeAccess {
|
||||
&self.control_pipe
|
||||
}
|
||||
|
||||
fn handle_detach(&self) {
|
||||
if let Some(handler) = self.detach_handler.lock().as_ref() {
|
||||
handler.handle_device_detach();
|
||||
}
|
||||
}
|
||||
|
||||
fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>) {
|
||||
*self.detach_handler.lock() = Some(handler);
|
||||
}
|
||||
|
||||
fn controller_ref(&self) -> &dyn UsbHostController {
|
||||
self.xhci.as_ref()
|
||||
}
|
||||
|
||||
fn debug(&self) {}
|
||||
|
||||
fn speed(&self) -> UsbSpeed {
|
||||
self.speed
|
||||
}
|
||||
|
||||
async fn open_normal_in_pipe(
|
||||
&self,
|
||||
number: u8,
|
||||
max_packet_size: u16,
|
||||
ty: UsbEndpointType,
|
||||
) -> Result<Box<dyn UsbNormalPipeIn>, UsbError> {
|
||||
let dci = Self::dci(ty, UsbDirection::In, number) as u8;
|
||||
let (pipe, ring) = NormalInPipe::new(self.xhci.clone(), self.slot_id, dci, 128)?;
|
||||
self.add_endpoint(number, ring.clone(), ty, UsbDirection::In, max_packet_size)
|
||||
.await?;
|
||||
Ok(Box::new(pipe))
|
||||
}
|
||||
|
||||
async fn open_normal_out_pipe(
|
||||
&self,
|
||||
number: u8,
|
||||
max_packet_size: u16,
|
||||
ty: UsbEndpointType,
|
||||
) -> Result<Box<dyn UsbNormalPipeOut>, UsbError> {
|
||||
let dci = Self::dci(ty, UsbDirection::Out, number) as u8;
|
||||
let (pipe, ring) = NormalOutPipe::new(self.xhci.clone(), self.slot_id, dci, 128)?;
|
||||
self.add_endpoint(number, ring.clone(), ty, UsbDirection::Out, max_packet_size)
|
||||
.await?;
|
||||
Ok(Box::new(pipe))
|
||||
}
|
||||
}
|
||||
|
||||
impl XhciBusDevice {
|
||||
pub async fn add_endpoint(
|
||||
&self,
|
||||
endpoint_number: u8,
|
||||
ring: Arc<TransferRing>,
|
||||
ty: UsbEndpointType,
|
||||
direction: UsbDirection,
|
||||
max_packet_size: u16,
|
||||
) -> Result<(), UsbError> {
|
||||
let dci = Self::dci(ty, direction, endpoint_number) as u8;
|
||||
|
||||
log::debug!(
|
||||
"Setup endpoint dci #{}: {} {:?} {:?}",
|
||||
dci,
|
||||
endpoint_number,
|
||||
ty,
|
||||
direction
|
||||
);
|
||||
|
||||
let ep_type = match (ty, direction) {
|
||||
(UsbEndpointType::Interrupt, UsbDirection::In) => context::EndpointType::InterruptIn,
|
||||
(UsbEndpointType::Interrupt, UsbDirection::Out) => context::EndpointType::InterruptOut,
|
||||
(UsbEndpointType::Bulk, UsbDirection::In) => context::EndpointType::BulkIn,
|
||||
(UsbEndpointType::Bulk, UsbDirection::Out) => context::EndpointType::BulkOut,
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
let mut input_cx = XhciInputContext::new(self.xhci.regs.context_size)?;
|
||||
|
||||
{
|
||||
let control = input_cx.control_mut();
|
||||
|
||||
control.set_add_context_flag(0);
|
||||
control.set_add_context_flag(dci as usize);
|
||||
}
|
||||
|
||||
{
|
||||
let slot = input_cx.device_mut().slot_mut();
|
||||
|
||||
slot.set_context_entries(dci + 1);
|
||||
}
|
||||
|
||||
{
|
||||
let endpoint = input_cx.device_mut().endpoint_mut(dci as usize);
|
||||
|
||||
endpoint.set_endpoint_type(ep_type);
|
||||
endpoint.set_error_count(3);
|
||||
// TODO Pick a better value here
|
||||
endpoint.set_interval(3);
|
||||
endpoint.set_max_packet_size(max_packet_size);
|
||||
endpoint.set_tr_dequeue_pointer(ring.base().into_u64());
|
||||
endpoint.set_dequeue_cycle_state();
|
||||
}
|
||||
|
||||
self.xhci
|
||||
.command_ring
|
||||
.configure_endpoint(
|
||||
self.xhci.as_ref(),
|
||||
self.slot_id,
|
||||
input_cx.physical_address(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.endpoints.write().insert(dci, ring.clone());
|
||||
self.xhci
|
||||
.endpoints
|
||||
.write()
|
||||
.insert((self.slot_id, dci), ring);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dci(ty: UsbEndpointType, dir: UsbDirection, number: u8) -> usize {
|
||||
match ty {
|
||||
UsbEndpointType::Control => (number as usize) * 2 + 1,
|
||||
@ -55,159 +185,4 @@ impl XhciBusDevice {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn setup_endpoint_inner(
|
||||
&self,
|
||||
ring: Arc<dyn GenericTransferRing>,
|
||||
dci: u8,
|
||||
ty: UsbEndpointType,
|
||||
direction: UsbDirection,
|
||||
) -> Result<(), UsbError> {
|
||||
log::debug!("Setup endpoint dci #{}: {:?} {:?}", dci, ty, direction);
|
||||
|
||||
let ep_type = match (ty, direction) {
|
||||
(UsbEndpointType::Interrupt, UsbDirection::In) => context::EndpointType::InterruptIn,
|
||||
(UsbEndpointType::Interrupt, UsbDirection::Out) => context::EndpointType::InterruptOut,
|
||||
(UsbEndpointType::Bulk, UsbDirection::In) => context::EndpointType::BulkIn,
|
||||
(UsbEndpointType::Bulk, UsbDirection::Out) => context::EndpointType::BulkOut,
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
let mut input_cx = XhciInputContext::new(self.xhci.context_size)?;
|
||||
|
||||
{
|
||||
let control = input_cx.control_mut();
|
||||
|
||||
control.set_add_context_flag(0);
|
||||
control.set_add_context_flag(dci as _);
|
||||
}
|
||||
|
||||
{
|
||||
let slot = input_cx.device_mut().slot_mut();
|
||||
|
||||
slot.set_context_entries(31);
|
||||
slot.set_interrupter_target(0);
|
||||
slot.set_usb_device_address(self.bus_address.device);
|
||||
slot.set_root_hub_port_number(self.port_id);
|
||||
slot.set_speed(self.speed.into());
|
||||
}
|
||||
|
||||
{
|
||||
let ep_cx = input_cx.device_mut().endpoint_mut(dci as _);
|
||||
|
||||
ep_cx.set_endpoint_type(ep_type);
|
||||
ep_cx.set_error_count(3);
|
||||
// TODO
|
||||
ep_cx.set_max_packet_size(8);
|
||||
ep_cx.set_tr_dequeue_pointer(ring.dequeue_pointer().into_u64());
|
||||
ep_cx.set_dequeue_cycle_state();
|
||||
}
|
||||
|
||||
self.xhci
|
||||
.command_ring
|
||||
.configure_endpoint(
|
||||
self.xhci.as_ref(),
|
||||
self.slot_id,
|
||||
input_cx.physical_address(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.rings.write().push(ring.clone());
|
||||
self.xhci.register_endpoint(self.slot_id, dci, ring);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbDevice for XhciBusDevice {
|
||||
fn control_pipe(&self) -> &UsbControlPipeAccess {
|
||||
&self.control_pipe
|
||||
}
|
||||
|
||||
fn port_number(&self) -> u8 {
|
||||
self.port_id
|
||||
}
|
||||
|
||||
fn bus_address(&self) -> UsbBusAddress {
|
||||
self.bus_address
|
||||
}
|
||||
|
||||
fn controller_ref(&self) -> &dyn UsbHostController {
|
||||
self.xhci.as_ref()
|
||||
}
|
||||
|
||||
fn open_interrupt_in_pipe(
|
||||
&self,
|
||||
number: u8,
|
||||
) -> BoxFuture<Result<UsbInterruptInPipeAccess, UsbError>> {
|
||||
async move {
|
||||
let dci = Self::dci(UsbEndpointType::Interrupt, UsbDirection::In, number) as u8;
|
||||
let ring = Arc::new(InterruptInTransferRing::new(self.slot_id, dci as _, 128)?);
|
||||
|
||||
self.setup_endpoint_inner(
|
||||
ring.clone(),
|
||||
dci,
|
||||
UsbEndpointType::Interrupt,
|
||||
UsbDirection::In,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let pipe = InterruptInPipe::new(self.xhci.clone(), self.slot_id, number, dci, ring);
|
||||
|
||||
Ok(UsbInterruptInPipeAccess(Box::new(pipe)))
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn open_bulk_in_pipe(&self, number: u8) -> BoxFuture<Result<UsbBulkInPipeAccess, UsbError>> {
|
||||
async move {
|
||||
let dci = Self::dci(UsbEndpointType::Bulk, UsbDirection::In, number) as u8;
|
||||
let ring = Arc::new(BulkInTransferRing::new(self.slot_id, dci as _, 128)?);
|
||||
|
||||
self.setup_endpoint_inner(ring.clone(), dci, UsbEndpointType::Bulk, UsbDirection::In)
|
||||
.await?;
|
||||
|
||||
let pipe = BulkInPipe::new(self.xhci.clone(), self.slot_id, number, dci, ring);
|
||||
|
||||
Ok(UsbBulkInPipeAccess(Box::new(pipe)))
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn open_bulk_out_pipe(&self, number: u8) -> BoxFuture<Result<UsbBulkOutPipeAccess, UsbError>> {
|
||||
async move {
|
||||
let dci = Self::dci(UsbEndpointType::Bulk, UsbDirection::Out, number) as u8;
|
||||
let ring = Arc::new(BulkOutTransferRing::new(self.slot_id, dci as _, 128)?);
|
||||
|
||||
self.setup_endpoint_inner(ring.clone(), dci, UsbEndpointType::Bulk, UsbDirection::Out)
|
||||
.await?;
|
||||
|
||||
let pipe = BulkOutPipe::new(self.xhci.clone(), self.slot_id, number, dci, ring);
|
||||
|
||||
Ok(UsbBulkOutPipeAccess(Box::new(pipe)))
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>) {
|
||||
*self.detach_handler.lock() = Some(handler);
|
||||
}
|
||||
|
||||
fn handle_detach(&self) {
|
||||
log::info!("Device detach handler");
|
||||
for ring in self.rings.write().drain(..) {
|
||||
self.xhci
|
||||
.shutdown_endpoint(ring.slot_id(), ring.endpoint_id());
|
||||
}
|
||||
|
||||
if let Some(handler) = self.detach_handler.lock().take() {
|
||||
handler.handle_device_detach();
|
||||
}
|
||||
}
|
||||
|
||||
fn speed(&self) -> UsbSpeed {
|
||||
self.speed.into()
|
||||
}
|
||||
|
||||
fn debug(&self) {}
|
||||
}
|
||||
|
@ -1,14 +1,17 @@
|
||||
#![no_std]
|
||||
#![allow(clippy::new_without_default)]
|
||||
#![feature(iter_array_chunks)]
|
||||
#![feature(iter_array_chunks, let_chains)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use controller::Xhci;
|
||||
use device_api::{device::Device, interrupt::InterruptAffinity};
|
||||
use regs::Mapper;
|
||||
use xhci_lib::extended_capabilities;
|
||||
use regs::Regs;
|
||||
// use regs::Mapper;
|
||||
// use xhci_lib::extended_capabilities;
|
||||
use ygg_driver_pci::{
|
||||
capability::{DevicePowerState, PowerManagementCapability},
|
||||
device::{PciDeviceInfo, PreferredInterruptMode},
|
||||
PciCommandRegister, PciConfigurationSpace,
|
||||
};
|
||||
@ -21,7 +24,6 @@ mod pipe;
|
||||
mod regs;
|
||||
mod ring;
|
||||
mod util;
|
||||
pub use controller::Xhci;
|
||||
|
||||
pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
|
||||
// TODO Chip Hardware Reset
|
||||
@ -32,22 +34,27 @@ pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
|
||||
.as_memory()
|
||||
.expect("xHCI's BAR0 is not memory-type");
|
||||
|
||||
if let Some(power) = info.config_space.capability::<PowerManagementCapability>() {
|
||||
log::info!("xHC has power management capability");
|
||||
let power_state = power.get_device_power_state();
|
||||
|
||||
if power_state != DevicePowerState::D0 {
|
||||
power.set_device_power_state(DevicePowerState::D0);
|
||||
}
|
||||
// Enable PME# signal generation
|
||||
power.set_pme_en(true);
|
||||
}
|
||||
|
||||
let mut cmd = PciCommandRegister::from_bits_retain(info.config_space.command());
|
||||
cmd &= !(PciCommandRegister::DISABLE_INTERRUPTS | PciCommandRegister::ENABLE_IO);
|
||||
cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER;
|
||||
info.config_space.set_command(cmd.bits());
|
||||
|
||||
let base = bar0.try_into_usize().unwrap();
|
||||
let mapper = Mapper::new();
|
||||
let regs = unsafe { xhci_lib::Registers::new(base, mapper.clone()) };
|
||||
let extended = unsafe {
|
||||
extended_capabilities::List::new(base, regs.capability.hccparams1.read_volatile(), mapper)
|
||||
}
|
||||
.ok_or(Error::InvalidArgument)
|
||||
.inspect_err(|_| log::error!("Cannot proceed with xhci init: no extended capabilities"))?;
|
||||
let xhci = Arc::new(Xhci::new(regs, extended)?);
|
||||
|
||||
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
|
||||
|
||||
let regs = Regs::map(bar0)?;
|
||||
let xhci = Arc::new(Xhci::new(info.clone(), regs)?);
|
||||
|
||||
info.map_interrupt(InterruptAffinity::Any, xhci.clone())?;
|
||||
|
||||
Ok(xhci)
|
||||
|
@ -1,170 +1,139 @@
|
||||
use alloc::sync::Arc;
|
||||
use libk_mm::{address::PhysicalAddress, PageBox, PageSlice};
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use libk_mm::{address::AsPhysicalAddress, PageSlice};
|
||||
use ygg_driver_usb::{
|
||||
communication::{UsbBulkTransfer, UsbInterruptTransfer},
|
||||
error::UsbError,
|
||||
communication::UsbDirection,
|
||||
error::{TransferError, UsbError},
|
||||
pipe::{
|
||||
bulk::{UsbBulkInPipe, UsbBulkOutPipe},
|
||||
control::{ControlTransferSetup, UsbControlPipe},
|
||||
interrupt::UsbInterruptInPipe,
|
||||
UsbGenericPipe,
|
||||
normal::{UsbNormalPipeIn, UsbNormalPipeOut},
|
||||
},
|
||||
UsbControlTransfer, UsbDirection,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ring::{
|
||||
transfer::{BulkInTransferRing, BulkOutTransferRing, InterruptInTransferRing},
|
||||
ControlTransferRing,
|
||||
},
|
||||
Xhci,
|
||||
};
|
||||
use crate::{controller::Xhci, ring::transfer::TransferRing};
|
||||
|
||||
pub struct ControlPipe {
|
||||
xhci: Arc<Xhci>,
|
||||
ring: Arc<ControlTransferRing>,
|
||||
pub(crate) ring: Arc<TransferRing>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct InterruptInPipe {
|
||||
pub struct NormalInPipe {
|
||||
xhci: Arc<Xhci>,
|
||||
|
||||
slot_id: u8,
|
||||
endpoint_id: u8,
|
||||
dci: u8,
|
||||
|
||||
ring: Arc<InterruptInTransferRing>,
|
||||
pub(crate) ring: Arc<TransferRing>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct BulkInPipe {
|
||||
pub struct NormalOutPipe {
|
||||
xhci: Arc<Xhci>,
|
||||
|
||||
slot_id: u8,
|
||||
endpoint_id: u8,
|
||||
dci: u8,
|
||||
|
||||
ring: Arc<BulkInTransferRing>,
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub struct BulkOutPipe {
|
||||
xhci: Arc<Xhci>,
|
||||
|
||||
slot_id: u8,
|
||||
endpoint_id: u8,
|
||||
dci: u8,
|
||||
|
||||
ring: Arc<BulkOutTransferRing>,
|
||||
}
|
||||
|
||||
impl UsbGenericPipe for ControlPipe {}
|
||||
|
||||
impl UsbControlPipe for ControlPipe {
|
||||
fn start_transfer(
|
||||
&self,
|
||||
setup: ControlTransferSetup,
|
||||
data: Option<(PhysicalAddress, usize, UsbDirection)>,
|
||||
) -> Result<UsbControlTransfer, UsbError> {
|
||||
self.ring.start_transfer(self.xhci.as_ref(), setup, data)
|
||||
}
|
||||
|
||||
fn complete_transfer(&self, transfer: UsbControlTransfer) {
|
||||
self.ring.complete_transfer(transfer)
|
||||
}
|
||||
pub(crate) ring: Arc<TransferRing>,
|
||||
}
|
||||
|
||||
impl ControlPipe {
|
||||
pub fn new(xhci: Arc<Xhci>, _slot_id: u8, ring: Arc<ControlTransferRing>) -> Self {
|
||||
Self { xhci, ring }
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbGenericPipe for InterruptInPipe {}
|
||||
|
||||
impl UsbInterruptInPipe for InterruptInPipe {
|
||||
fn start_read(&self, buffer: &mut PageBox<[u8]>) -> Result<UsbInterruptTransfer, UsbError> {
|
||||
self.ring.start_transfer(self.xhci.as_ref(), buffer)
|
||||
}
|
||||
|
||||
fn complete_transfer(&self, transfer: UsbInterruptTransfer) {
|
||||
self.ring.complete_transfer(transfer)
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptInPipe {
|
||||
pub fn new(
|
||||
xhci: Arc<Xhci>,
|
||||
slot_id: u8,
|
||||
endpoint_id: u8,
|
||||
dci: u8,
|
||||
ring: Arc<InterruptInTransferRing>,
|
||||
) -> Self {
|
||||
capacity: usize,
|
||||
) -> Result<(Self, Arc<TransferRing>), UsbError> {
|
||||
let ring = Arc::new(TransferRing::new(slot_id, endpoint_id, capacity)?);
|
||||
Ok((
|
||||
Self {
|
||||
xhci,
|
||||
slot_id,
|
||||
endpoint_id,
|
||||
dci,
|
||||
ring: ring.clone(),
|
||||
},
|
||||
ring,
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbGenericPipe for BulkInPipe {}
|
||||
|
||||
impl UsbBulkInPipe for BulkInPipe {
|
||||
fn start_read(&self, buffer: &mut PageSlice<u8>) -> Result<UsbBulkTransfer, UsbError> {
|
||||
self.ring.start_transfer(self.xhci.as_ref(), buffer)
|
||||
}
|
||||
|
||||
fn complete_transfer(&self, transfer: UsbBulkTransfer) {
|
||||
self.ring.complete_transfer(transfer)
|
||||
}
|
||||
}
|
||||
|
||||
impl BulkInPipe {
|
||||
impl NormalInPipe {
|
||||
pub fn new(
|
||||
xhci: Arc<Xhci>,
|
||||
slot_id: u8,
|
||||
endpoint_id: u8,
|
||||
dci: u8,
|
||||
ring: Arc<BulkInTransferRing>,
|
||||
) -> Self {
|
||||
capacity: usize,
|
||||
) -> Result<(Self, Arc<TransferRing>), UsbError> {
|
||||
let ring = Arc::new(TransferRing::new(slot_id, endpoint_id, capacity)?);
|
||||
Ok((
|
||||
Self {
|
||||
xhci,
|
||||
slot_id,
|
||||
endpoint_id,
|
||||
dci,
|
||||
ring: ring.clone(),
|
||||
},
|
||||
ring,
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbGenericPipe for BulkOutPipe {}
|
||||
|
||||
impl UsbBulkOutPipe for BulkOutPipe {
|
||||
fn start_write(&self, buffer: &PageSlice<u8>) -> Result<UsbBulkTransfer, UsbError> {
|
||||
self.ring.start_transfer(self.xhci.as_ref(), buffer)
|
||||
}
|
||||
|
||||
fn complete_transfer(&self, transfer: UsbBulkTransfer) {
|
||||
self.ring.complete_transfer(transfer)
|
||||
}
|
||||
}
|
||||
|
||||
impl BulkOutPipe {
|
||||
impl NormalOutPipe {
|
||||
pub fn new(
|
||||
xhci: Arc<Xhci>,
|
||||
slot_id: u8,
|
||||
endpoint_id: u8,
|
||||
dci: u8,
|
||||
ring: Arc<BulkOutTransferRing>,
|
||||
) -> Self {
|
||||
capacity: usize,
|
||||
) -> Result<(Self, Arc<TransferRing>), UsbError> {
|
||||
let ring = Arc::new(TransferRing::new(slot_id, endpoint_id, capacity)?);
|
||||
Ok((
|
||||
Self {
|
||||
xhci,
|
||||
slot_id,
|
||||
endpoint_id,
|
||||
dci,
|
||||
ring: ring.clone(),
|
||||
},
|
||||
ring,
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbControlPipe for ControlPipe {
|
||||
async fn control_transfer(
|
||||
&self,
|
||||
setup: ControlTransferSetup,
|
||||
data: Option<(&mut PageSlice<MaybeUninit<u8>>, UsbDirection)>,
|
||||
) -> Result<usize, UsbError> {
|
||||
let data_len = data.as_ref().map_or(0, |(data, _)| data.len());
|
||||
let result = self
|
||||
.ring
|
||||
.control_transfer(self.xhci.as_ref(), setup, data)
|
||||
.await;
|
||||
allow_short_packet(data_len, result)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbNormalPipeIn for NormalInPipe {
|
||||
async fn read(&self, buffer: &mut PageSlice<u8>) -> Result<usize, UsbError> {
|
||||
let data_len = buffer.len();
|
||||
let result = self
|
||||
.ring
|
||||
.normal_transfer(
|
||||
self.xhci.as_ref(),
|
||||
unsafe { buffer.as_physical_address() },
|
||||
buffer.len(),
|
||||
)
|
||||
.await;
|
||||
allow_short_packet(data_len, result)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbNormalPipeOut for NormalOutPipe {
|
||||
async fn write(&self, buffer: &PageSlice<u8>) -> Result<usize, UsbError> {
|
||||
self.ring
|
||||
.normal_transfer(
|
||||
self.xhci.as_ref(),
|
||||
unsafe { buffer.as_physical_address() },
|
||||
buffer.len(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
fn allow_short_packet(data_len: usize, result: Result<usize, UsbError>) -> Result<usize, UsbError> {
|
||||
match result {
|
||||
// Short packets are okay for control transfers
|
||||
Err(UsbError::TransferFailed(TransferError::ShortPacket(residual))) => {
|
||||
Ok(data_len.saturating_sub(residual))
|
||||
}
|
||||
result => result,
|
||||
}
|
||||
}
|
||||
|
@ -1,335 +0,0 @@
|
||||
use core::{cell::UnsafeCell, num::NonZeroUsize};
|
||||
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use libk_mm::{
|
||||
address::{AsPhysicalAddress, PhysicalAddress},
|
||||
device::RawDeviceMemoryMapping,
|
||||
PageBox,
|
||||
};
|
||||
use libk_util::sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock};
|
||||
use xhci_lib::{
|
||||
accessor::{array, marker},
|
||||
registers::{
|
||||
operational::UsbStatusRegister, Capability, Doorbell, InterrupterRegisterSet, Operational,
|
||||
PortRegisterSet,
|
||||
},
|
||||
};
|
||||
use ygg_driver_usb::device::UsbSpeed;
|
||||
use yggdrasil_abi::primitive_enum;
|
||||
|
||||
use crate::ring::{CommandRing, EventRing, EventRingSegmentTable, GenericRing};
|
||||
|
||||
struct MapperInner {
|
||||
mappings: Vec<(RawDeviceMemoryMapping, usize)>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Mapper {
|
||||
mapper: Arc<IrqSafeSpinlock<MapperInner>>,
|
||||
}
|
||||
|
||||
pub struct LockedArray<T> {
|
||||
array: UnsafeCell<array::Generic<T, Mapper, marker::ReadWrite>>,
|
||||
locks: Vec<IrqSafeRwLock<()>>,
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for LockedArray<T> {}
|
||||
unsafe impl<T> Send for LockedArray<T> {}
|
||||
|
||||
pub struct Regs {
|
||||
operational: IrqSafeRwLock<Operational<Mapper>>,
|
||||
interrupters: IrqSafeRwLock<InterrupterRegisterSet<Mapper>>,
|
||||
capability: Capability<Mapper>,
|
||||
doorbells: LockedArray<Doorbell>,
|
||||
pub ports: LockedArray<PortRegisterSet>,
|
||||
}
|
||||
|
||||
impl<T> LockedArray<T> {
|
||||
#[inline]
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
unsafe fn get_mut(&self) -> &mut array::Generic<T, Mapper, marker::ReadWrite> {
|
||||
&mut *self.array.get()
|
||||
}
|
||||
|
||||
pub fn update<U: FnOnce(&mut T)>(&self, index: usize, f: U) {
|
||||
let _guard = self.locks[index].write();
|
||||
unsafe { self.get_mut() }.update_volatile_at(index, f);
|
||||
}
|
||||
|
||||
pub fn read(&self, index: usize) -> T {
|
||||
let _guard = self.locks[index].read();
|
||||
unsafe { self.get_mut() }.read_volatile_at(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<array::Generic<T, Mapper, marker::ReadWrite>> for LockedArray<T> {
|
||||
fn from(value: array::Generic<T, Mapper, marker::ReadWrite>) -> Self {
|
||||
let locks = Vec::from_iter((0..value.len()).map(|_| IrqSafeRwLock::new(())));
|
||||
|
||||
Self {
|
||||
array: UnsafeCell::new(value),
|
||||
locks,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<xhci_lib::Registers<Mapper>> for Regs {
|
||||
fn from(value: xhci_lib::Registers<Mapper>) -> Self {
|
||||
Self {
|
||||
operational: IrqSafeRwLock::new(value.operational),
|
||||
capability: value.capability,
|
||||
interrupters: IrqSafeRwLock::new(value.interrupter_register_set),
|
||||
doorbells: LockedArray::from(value.doorbell),
|
||||
ports: LockedArray::from(value.port_register_set),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
pub fn reset(&self) {
|
||||
let mut o = self.operational.write();
|
||||
|
||||
// TODO Get ownership from firmware
|
||||
|
||||
// Stop the controller
|
||||
o.usbcmd.update_volatile(|u| {
|
||||
u.clear_run_stop();
|
||||
});
|
||||
|
||||
while !o.usbsts.read_volatile().hc_halted() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
// Reset the controller
|
||||
o.usbcmd.update_volatile(|u| {
|
||||
u.set_host_controller_reset();
|
||||
});
|
||||
while o.usbcmd.read_volatile().host_controller_reset()
|
||||
|| o.usbsts.read_volatile().controller_not_ready()
|
||||
{
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_slot_count(&self) -> usize {
|
||||
self.capability
|
||||
.hcsparams1
|
||||
.read_volatile()
|
||||
.number_of_device_slots() as _
|
||||
}
|
||||
|
||||
pub fn set_max_slot_count(&self) -> usize {
|
||||
let device_slot_count = self.max_slot_count();
|
||||
let mut o = self.operational.write();
|
||||
// Set max slots enabled
|
||||
o.config.update_volatile(|u| {
|
||||
u.set_max_device_slots_enabled(device_slot_count as _);
|
||||
});
|
||||
|
||||
device_slot_count as _
|
||||
}
|
||||
|
||||
pub fn context_size(&self) -> usize {
|
||||
match self.capability.hccparams1.read_volatile().context_size() {
|
||||
true => 64,
|
||||
false => 32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn port_count(&self) -> usize {
|
||||
self.capability.hcsparams1.read_volatile().number_of_ports() as _
|
||||
}
|
||||
|
||||
pub fn scratchpad_count(&self) -> usize {
|
||||
self.capability
|
||||
.hcsparams2
|
||||
.read_volatile()
|
||||
.max_scratchpad_buffers() as usize
|
||||
}
|
||||
|
||||
pub fn configure(
|
||||
&self,
|
||||
dcbaa: &PageBox<[PhysicalAddress]>,
|
||||
cmd_ring: &CommandRing,
|
||||
evt_ring: &EventRing,
|
||||
erst: &EventRingSegmentTable,
|
||||
) {
|
||||
let mut o = self.operational.write();
|
||||
let mut i = self.interrupters.write();
|
||||
|
||||
o.dcbaap.update_volatile(|u| unsafe {
|
||||
u.set(dcbaa.as_physical_address().into());
|
||||
});
|
||||
o.crcr.update_volatile(|u| {
|
||||
u.set_command_ring_pointer(cmd_ring.base().into());
|
||||
u.set_ring_cycle_state();
|
||||
});
|
||||
|
||||
let mut intr0 = i.interrupter_mut(0);
|
||||
intr0.erstsz.update_volatile(|u| {
|
||||
u.set(erst.capacity().try_into().unwrap());
|
||||
});
|
||||
intr0.erdp.update_volatile(|u| {
|
||||
log::debug!("::: Dequeue Pointer: {:#x}", evt_ring.dequeue_pointer());
|
||||
u.set_event_ring_dequeue_pointer(evt_ring.dequeue_pointer().into());
|
||||
});
|
||||
intr0.erstba.update_volatile(|u| {
|
||||
u.set(erst.physical_address().into());
|
||||
});
|
||||
// intr0.imod.update_volatile(|u| {
|
||||
// u.set_interrupt_moderation_interval(0)
|
||||
// .set_interrupt_moderation_counter(0);
|
||||
// });
|
||||
intr0.iman.update_volatile(|u| {
|
||||
u.set_interrupt_enable();
|
||||
});
|
||||
|
||||
o.usbcmd.update_volatile(|u| {
|
||||
u.set_interrupter_enable().set_run_stop();
|
||||
});
|
||||
|
||||
// Wait for the controller to come out of "not ready" state
|
||||
while o.usbsts.read_volatile().controller_not_ready() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_interrupt(&self) -> Option<UsbStatusRegister> {
|
||||
let mut o = self.operational.write();
|
||||
let mut i = self.interrupters.write();
|
||||
|
||||
let status = o.usbsts.read_volatile();
|
||||
|
||||
if !status.event_interrupt() {
|
||||
return None;
|
||||
}
|
||||
|
||||
o.usbsts.write_volatile(status);
|
||||
|
||||
if status.host_system_error() {
|
||||
return Some(status);
|
||||
}
|
||||
|
||||
// Acknowledge interrupts
|
||||
let mut intr0 = i.interrupter_mut(0);
|
||||
intr0.iman.update_volatile(|u| {
|
||||
u.clear_interrupt_pending();
|
||||
});
|
||||
|
||||
Some(status)
|
||||
}
|
||||
|
||||
pub fn set_interrupter_0_dequeue_pointer(&self, pointer: PhysicalAddress) {
|
||||
let mut i = self.interrupters.write();
|
||||
|
||||
i.interrupter_mut(0).erdp.update_volatile(|u| {
|
||||
u.set_event_ring_dequeue_pointer(pointer.into());
|
||||
u.clear_event_handler_busy();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn ring_doorbell(&self, index: usize, target: u8) {
|
||||
self.doorbells.update(index, |u| {
|
||||
u.set_doorbell_target(target);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Mapper {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mapper: Arc::new(IrqSafeSpinlock::new(MapperInner {
|
||||
mappings: Vec::new(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl xhci_lib::accessor::Mapper for Mapper {
|
||||
// FIXME really slow, but at least reduces the number of unneeded mappings
|
||||
unsafe fn map(&mut self, phys_start: usize, bytes: usize) -> NonZeroUsize {
|
||||
let mut mapper = self.mapper.lock();
|
||||
for (mapping, refcount) in mapper.mappings.iter_mut() {
|
||||
if phys_start as u64 >= mapping.physical_base
|
||||
&& ((phys_start + bytes) as u64)
|
||||
<= mapping.physical_base + (mapping.page_size * mapping.page_count) as u64
|
||||
{
|
||||
*refcount += 1;
|
||||
return NonZeroUsize::new_unchecked(
|
||||
mapping.base_address + phys_start - mapping.physical_base as usize,
|
||||
);
|
||||
}
|
||||
}
|
||||
let mapping = RawDeviceMemoryMapping::map(phys_start as u64, bytes, Default::default())
|
||||
.inspect_err(|error| {
|
||||
log::error!(
|
||||
"Cannot map xHC MMIO region {:#x?}: {error:?}",
|
||||
phys_start..phys_start + bytes
|
||||
)
|
||||
})
|
||||
.expect("Could not map an USB xHCI region");
|
||||
let address = mapping.address;
|
||||
log::info!("xhci: map {:#x} -> {:#x}", mapping.base_address, address);
|
||||
mapper.mappings.push((mapping, 1));
|
||||
NonZeroUsize::new_unchecked(address)
|
||||
}
|
||||
|
||||
fn unmap(&mut self, _virt_start: usize, _bytes: usize) {
|
||||
// let mut mapper = self.mapper.lock();
|
||||
// let index = mapper.mappings.iter().position(|(mapping, _)| {
|
||||
// virt_start >= mapping.base_address
|
||||
// && virt_start + bytes
|
||||
// <= mapping.base_address + mapping.page_count * mapping.page_size
|
||||
// });
|
||||
|
||||
// if let Some(index) = index {
|
||||
// let (entry, refcount) = &mut mapper.mappings[index];
|
||||
// *refcount -= 1;
|
||||
// if *refcount == 0 {
|
||||
// log::info!("xhci: unmap {:#x}", entry.base_address);
|
||||
// mapper.mappings.remove(index);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// Register value definitions
|
||||
|
||||
primitive_enum! {
|
||||
pub enum PortSpeed: u8 {
|
||||
Full = 1,
|
||||
Low = 2,
|
||||
High = 3,
|
||||
SuperGen1x1 = 4,
|
||||
SuperGen2x1 = 5,
|
||||
SuperGen1x2 = 6,
|
||||
SuperGen2x2 = 7,
|
||||
}
|
||||
}
|
||||
|
||||
impl PortSpeed {
|
||||
pub fn default_max_packet_size(&self) -> usize {
|
||||
match self {
|
||||
Self::Low => 8,
|
||||
Self::High => 64,
|
||||
Self::SuperGen1x1 | Self::SuperGen1x2 | Self::SuperGen2x1 | Self::SuperGen2x2 => 512,
|
||||
|
||||
// See Section 4.3., point 7. of the initialization list
|
||||
Self::Full => 8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PortSpeed> for UsbSpeed {
|
||||
fn from(value: PortSpeed) -> Self {
|
||||
match value {
|
||||
PortSpeed::Low => UsbSpeed::Low,
|
||||
PortSpeed::Full => UsbSpeed::Full,
|
||||
PortSpeed::High => UsbSpeed::High,
|
||||
PortSpeed::SuperGen1x1
|
||||
| PortSpeed::SuperGen1x2
|
||||
| PortSpeed::SuperGen2x1
|
||||
| PortSpeed::SuperGen2x2 => UsbSpeed::Super,
|
||||
}
|
||||
}
|
||||
}
|
66
kernel/driver/usb/xhci/src/regs/capability.rs
Normal file
66
kernel/driver/usb/xhci/src/regs/capability.rs
Normal file
@ -0,0 +1,66 @@
|
||||
use tock_registers::{register_bitfields, register_structs, registers::ReadOnly};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
pub HCSPARAMS1 [
|
||||
MaxSlots OFFSET(0) NUMBITS(8) [],
|
||||
MaxIntrs OFFSET(8) NUMBITS(11) [],
|
||||
MaxPorts OFFSET(24) NUMBITS(8) [],
|
||||
],
|
||||
pub HCSPARAMS2 [
|
||||
IST OFFSET(0) NUMBITS(4) [],
|
||||
ERSTMax OFFSET(4) NUMBITS(4) [],
|
||||
MaxScratchpadBufsHi OFFSET(21) NUMBITS(5) [],
|
||||
ScratchpadRestore OFFSET(26) NUMBITS(1) [],
|
||||
MaxScratchpadBufsLo OFFSET(27) NUMBITS(5) [],
|
||||
],
|
||||
pub HCSPARAMS3 [
|
||||
U1ExitLatency OFFSET(0) NUMBITS(8) [],
|
||||
U2ExitLatency OFFSET(16) NUMBITS(16) [],
|
||||
],
|
||||
pub HCCPARAMS1 [
|
||||
AC64 OFFSET(0) NUMBITS(1) [],
|
||||
BNC OFFSET(1) NUMBITS(1) [],
|
||||
CSZ OFFSET(2) NUMBITS(1) [],
|
||||
PPC OFFSET(3) NUMBITS(1) [],
|
||||
PIND OFFSET(4) NUMBITS(1) [],
|
||||
LHRC OFFSET(5) NUMBITS(1) [],
|
||||
LTC OFFSET(6) NUMBITS(1) [],
|
||||
NSS OFFSET(7) NUMBITS(1) [],
|
||||
PAE OFFSET(8) NUMBITS(1) [],
|
||||
SPC OFFSET(9) NUMBITS(1) [],
|
||||
SEC OFFSET(10) NUMBITS(1) [],
|
||||
CFC OFFSET(11) NUMBITS(1) [],
|
||||
MaxPSASize OFFSET(12) NUMBITS(4) [],
|
||||
XECP OFFSET(16) NUMBITS(16) [],
|
||||
],
|
||||
pub HCCPARAMS2 [
|
||||
U3C OFFSET(0) NUMBITS(1) [],
|
||||
CMC OFFSET(1) NUMBITS(1) [],
|
||||
FSC OFFSET(2) NUMBITS(1) [],
|
||||
CTC OFFSET(3) NUMBITS(1) [],
|
||||
LEC OFFSET(4) NUMBITS(1) [],
|
||||
CIC OFFSET(5) NUMBITS(1) [],
|
||||
ETC OFFSET(6) NUMBITS(1) [],
|
||||
ETC_TSC OFFSET(7) NUMBITS(1) [],
|
||||
GSC OFFSET(8) NUMBITS(1) [],
|
||||
VTC OFFSET(9) NUMBITS(1) [],
|
||||
],
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub CapabilityRegs {
|
||||
(0x00 => pub CAPLENGTH: ReadOnly<u8>),
|
||||
(0x01 => _0),
|
||||
(0x02 => pub HCIVERSION: ReadOnly<u16>),
|
||||
(0x04 => pub HCSPARAMS1: ReadOnly<u32, HCSPARAMS1::Register>),
|
||||
(0x08 => pub HCSPARAMS2: ReadOnly<u32, HCSPARAMS2::Register>),
|
||||
(0x0C => pub HCSPARAMS3: ReadOnly<u32, HCSPARAMS3::Register>),
|
||||
(0x10 => pub HCCPARAMS1: ReadOnly<u32, HCCPARAMS1::Register>),
|
||||
(0x14 => pub DBOFF: ReadOnly<u32>),
|
||||
(0x18 => pub RTSOFF: ReadOnly<u32>),
|
||||
(0x1C => pub HCCPARAMS2: ReadOnly<u32, HCCPARAMS2::Register>),
|
||||
(0x20 => @END),
|
||||
}
|
||||
}
|
122
kernel/driver/usb/xhci/src/regs/extended.rs
Normal file
122
kernel/driver/usb/xhci/src/regs/extended.rs
Normal file
@ -0,0 +1,122 @@
|
||||
use core::{
|
||||
ops::Range,
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use libk::error::Error;
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
use ygg_driver_usb::{error::UsbError, info::UsbVersion};
|
||||
|
||||
pub struct ProtocolSupport {
|
||||
words: [u32; 4],
|
||||
}
|
||||
|
||||
pub struct LegacySupport(DeviceMemoryIo<'static, [AtomicU32; 4]>);
|
||||
|
||||
pub enum ExtendedCapability {
|
||||
LegacySupport(IrqSafeRwLock<LegacySupport>),
|
||||
ProtocolSupport(ProtocolSupport),
|
||||
}
|
||||
|
||||
impl ExtendedCapability {
|
||||
pub fn parse_list(
|
||||
base: PhysicalAddress,
|
||||
extended_capability_offset: usize,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let mut extended_capabilities = Vec::new();
|
||||
|
||||
if extended_capability_offset != 0 {
|
||||
let extended_capability_base = base.add(extended_capability_offset);
|
||||
let mut current = extended_capability_base;
|
||||
|
||||
loop {
|
||||
let header =
|
||||
unsafe { DeviceMemoryIo::<[AtomicU32; 4]>::map(current, Default::default()) }?;
|
||||
let word0 = header[0].load(Ordering::Acquire);
|
||||
let id = word0 as u8;
|
||||
let next_pointer = ((word0 >> 8) & 0xFF) as usize;
|
||||
|
||||
match id {
|
||||
0x01 => extended_capabilities.push(ExtendedCapability::LegacySupport(
|
||||
IrqSafeRwLock::new(LegacySupport(header)),
|
||||
)),
|
||||
0x02 => {
|
||||
let words = [
|
||||
word0,
|
||||
header[1].load(Ordering::Acquire),
|
||||
header[2].load(Ordering::Acquire),
|
||||
header[3].load(Ordering::Acquire),
|
||||
];
|
||||
extended_capabilities.push(ExtendedCapability::ProtocolSupport(
|
||||
ProtocolSupport { words },
|
||||
))
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if next_pointer == 0 {
|
||||
break;
|
||||
} else {
|
||||
current = current.add(next_pointer << 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(extended_capabilities)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProtocolSupport {
|
||||
pub fn usb_revision(&self) -> Option<UsbVersion> {
|
||||
UsbVersion::from_bcd_usb((self.words[0] >> 16) as u16)
|
||||
}
|
||||
|
||||
pub fn slot_type(&self) -> u8 {
|
||||
(self.words[3] & 0x1F) as u8
|
||||
}
|
||||
|
||||
pub fn port_range(&self) -> Range<u8> {
|
||||
let port_offset = self.words[2] as u8;
|
||||
let port_count = (self.words[2] >> 8) as u8;
|
||||
|
||||
port_offset.max(1)..port_offset.saturating_add(port_count)
|
||||
}
|
||||
}
|
||||
|
||||
impl LegacySupport {
|
||||
fn is_bios_owned(&self) -> bool {
|
||||
self.0[0].load(Ordering::Acquire) & (1 << 16) != 0
|
||||
}
|
||||
|
||||
fn set_os_owned(&mut self) {
|
||||
self.0[0].fetch_or(1 << 24, Ordering::Release);
|
||||
}
|
||||
|
||||
pub fn perform_bios_handoff(&mut self, mut timeout_cycles: u64) -> Result<(), UsbError> {
|
||||
if !self.is_bios_owned() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Clear SMI events
|
||||
self.0[1].fetch_and(
|
||||
!((1 << 0) | (1 << 4) | (1 << 13) | (1 << 14) | (1 << 15)),
|
||||
Ordering::Release,
|
||||
);
|
||||
|
||||
// Set OS owned semaphore
|
||||
self.set_os_owned();
|
||||
while timeout_cycles > 0 && self.is_bios_owned() {
|
||||
core::hint::spin_loop();
|
||||
timeout_cycles -= 1;
|
||||
}
|
||||
|
||||
if timeout_cycles > 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
log::error!("xhci: BIOS handoff timed out");
|
||||
Err(UsbError::DeviceBusy)
|
||||
}
|
||||
}
|
||||
}
|
184
kernel/driver/usb/xhci/src/regs/mod.rs
Normal file
184
kernel/driver/usb/xhci/src/regs/mod.rs
Normal file
@ -0,0 +1,184 @@
|
||||
use core::{fmt, sync::atomic::AtomicU32};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use capability::{CapabilityRegs, HCCPARAMS1, HCSPARAMS1, HCSPARAMS2};
|
||||
use extended::ExtendedCapability;
|
||||
use libk::error::Error;
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
use operational::{OperationalRegs, PortRegs, PORTSC, USBCMD, USBSTS};
|
||||
use runtime::{RuntimeRegs, IMAN};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
LocalRegisterCopy,
|
||||
};
|
||||
|
||||
pub mod capability;
|
||||
pub mod extended;
|
||||
pub mod operational;
|
||||
pub mod runtime;
|
||||
|
||||
pub struct Regs {
|
||||
#[allow(unused)]
|
||||
pub(crate) capability: DeviceMemoryIo<'static, CapabilityRegs>,
|
||||
pub(crate) operational: IrqSafeRwLock<DeviceMemoryIo<'static, OperationalRegs>>,
|
||||
pub(crate) runtime: IrqSafeRwLock<DeviceMemoryIo<'static, RuntimeRegs>>,
|
||||
pub(crate) doorbells: DeviceMemoryIo<'static, [AtomicU32]>,
|
||||
pub(crate) extended_capabilities: Vec<ExtendedCapability>,
|
||||
// TODO per-port locks
|
||||
pub(crate) ports: IrqSafeRwLock<DeviceMemoryIo<'static, [PortRegs]>>,
|
||||
pub(crate) port_count: usize,
|
||||
pub(crate) slot_count: usize,
|
||||
pub(crate) scratch_count: usize,
|
||||
pub(crate) context_size: ContextSize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum ContextSize {
|
||||
Context32,
|
||||
Context64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct PortNumber(u8);
|
||||
|
||||
impl Regs {
|
||||
const PORT_REGS_OFFSET: usize = 0x400;
|
||||
|
||||
pub fn map(base: PhysicalAddress) -> Result<Self, Error> {
|
||||
let capability =
|
||||
unsafe { DeviceMemoryIo::<CapabilityRegs>::map(base, Default::default()) }?;
|
||||
|
||||
let operational_offset = capability.CAPLENGTH.get() as usize;
|
||||
let doorbell_offset = capability.DBOFF.get() & !0x3;
|
||||
let runtime_offset = capability.RTSOFF.get() & !0x1F;
|
||||
let extended_capability_offset =
|
||||
(capability.HCCPARAMS1.read(HCCPARAMS1::XECP) as usize) << 2;
|
||||
|
||||
let port_count = capability.HCSPARAMS1.read(HCSPARAMS1::MaxPorts) as usize;
|
||||
let slot_count = capability.HCSPARAMS1.read(HCSPARAMS1::MaxSlots) as usize;
|
||||
|
||||
let operational_base = base.add(operational_offset);
|
||||
let doorbell_base = base.add(doorbell_offset as usize);
|
||||
let runtime_base = base.add(runtime_offset as usize);
|
||||
|
||||
let scratch_lo = capability.HCSPARAMS2.read(HCSPARAMS2::MaxScratchpadBufsLo);
|
||||
let scratch_hi = capability.HCSPARAMS2.read(HCSPARAMS2::MaxScratchpadBufsHi);
|
||||
let scratch_count = ((scratch_hi << 5) | scratch_lo) as usize;
|
||||
|
||||
let context_size = if capability.HCCPARAMS1.matches_all(HCCPARAMS1::CSZ::SET) {
|
||||
ContextSize::Context64
|
||||
} else {
|
||||
ContextSize::Context32
|
||||
};
|
||||
|
||||
let operational = unsafe {
|
||||
DeviceMemoryIo::<OperationalRegs>::map(operational_base, Default::default())
|
||||
}?;
|
||||
let runtime =
|
||||
unsafe { DeviceMemoryIo::<RuntimeRegs>::map(runtime_base, Default::default()) }?;
|
||||
|
||||
let ports = unsafe {
|
||||
DeviceMemoryIo::map_slice(
|
||||
operational_base.add(Self::PORT_REGS_OFFSET),
|
||||
port_count,
|
||||
Default::default(),
|
||||
)
|
||||
}?;
|
||||
let doorbells =
|
||||
unsafe { DeviceMemoryIo::map_slice(doorbell_base, 256, Default::default()) }?;
|
||||
let extended_capabilities =
|
||||
ExtendedCapability::parse_list(base, extended_capability_offset)?;
|
||||
|
||||
Ok(Self {
|
||||
capability,
|
||||
operational: IrqSafeRwLock::new(operational),
|
||||
runtime: IrqSafeRwLock::new(runtime),
|
||||
doorbells,
|
||||
ports: IrqSafeRwLock::new(ports),
|
||||
extended_capabilities,
|
||||
|
||||
port_count,
|
||||
slot_count,
|
||||
scratch_count,
|
||||
context_size,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn hc_reset(&self, timeout_cycles: u64) -> Result<(), Error> {
|
||||
let op = self.operational.write();
|
||||
|
||||
// Wait for CNR to get cleared
|
||||
op.wait_usbsts_bit(USBSTS::CNR::CLEAR, timeout_cycles)?;
|
||||
|
||||
// Clear run/stop and wait for the HC to halt
|
||||
op.USBCMD.modify(USBCMD::RS::CLEAR);
|
||||
op.wait_usbsts_bit(USBSTS::HCH::SET, timeout_cycles)?;
|
||||
|
||||
// Reset the HC, wait for CNR to get cleared again
|
||||
op.USBCMD.modify(USBCMD::HCRST::SET);
|
||||
op.wait_usbsts_bit(USBSTS::CNR::CLEAR, timeout_cycles)?;
|
||||
|
||||
// Halt the HC again
|
||||
op.USBCMD.modify(USBCMD::RS::CLEAR);
|
||||
op.wait_usbsts_bit(USBSTS::HCH::SET, timeout_cycles)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn handle_interrupt(&self, vector: usize) -> LocalRegisterCopy<u32, USBSTS::Register> {
|
||||
let op = self.operational.write();
|
||||
let rt = self.runtime.write();
|
||||
|
||||
let status = op.USBSTS.extract();
|
||||
|
||||
// Clear the RW1C bits
|
||||
op.USBSTS.set(status.get());
|
||||
|
||||
// Acknowledge interrupts for given vector (vector == interrupter index)
|
||||
let interrupter = &rt.IRn[vector];
|
||||
|
||||
interrupter.IMAN.modify(IMAN::IP::SET);
|
||||
|
||||
status
|
||||
}
|
||||
}
|
||||
|
||||
pub fn portsc_to_neutral(
|
||||
portsc: LocalRegisterCopy<u32, PORTSC::Register>,
|
||||
) -> LocalRegisterCopy<u32, PORTSC::Register> {
|
||||
const RO_MASK: u32 = (1 << 0) | (1 << 3) | (0xF << 10) | (1 << 30);
|
||||
const RWS_MASK: u32 = (0xF << 5)
|
||||
| (1 << 9)
|
||||
| (0x3 << 14)
|
||||
| (1 << 25)
|
||||
| (1 << 26)
|
||||
| (1 << 27)
|
||||
| (1 << 30)
|
||||
| (1 << 31);
|
||||
|
||||
portsc.bitand(RO_MASK | RWS_MASK)
|
||||
}
|
||||
|
||||
impl Into<u8> for PortNumber {
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PortNumber {
|
||||
pub fn from_index(index: usize) -> Self {
|
||||
Self(index as u8 + 1)
|
||||
}
|
||||
|
||||
pub fn index(&self) -> usize {
|
||||
self.0 as usize - 1
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PortNumber {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
140
kernel/driver/usb/xhci/src/regs/operational.rs
Normal file
140
kernel/driver/usb/xhci/src/regs/operational.rs
Normal file
@ -0,0 +1,140 @@
|
||||
use libk::error::Error;
|
||||
use libk_mm::address::PhysicalAddress;
|
||||
use tock_registers::{
|
||||
fields::FieldValue,
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
pub USBCMD [
|
||||
RS OFFSET(0) NUMBITS(1) [],
|
||||
HCRST OFFSET(1) NUMBITS(1) [],
|
||||
INTE OFFSET(2) NUMBITS(1) [],
|
||||
HSEE OFFSET(3) NUMBITS(1) [],
|
||||
LHCRST OFFSET(7) NUMBITS(1) [],
|
||||
CSS OFFSET(8) NUMBITS(1) [],
|
||||
CRS OFFSET(9) NUMBITS(1) [],
|
||||
EWE OFFSET(10) NUMBITS(1) [],
|
||||
EU3S OFFSET(11) NUMBITS(1) [],
|
||||
CME OFFSET(13) NUMBITS(1) [],
|
||||
ETE OFFSET(14) NUMBITS(1) [],
|
||||
TSC_EN OFFSET(15) NUMBITS(1) [],
|
||||
VTIOE OFFSET(16) NUMBITS(1) [],
|
||||
],
|
||||
pub USBSTS [
|
||||
HCH OFFSET(0) NUMBITS(1) [],
|
||||
HSE OFFSET(2) NUMBITS(1) [],
|
||||
EINT OFFSET(3) NUMBITS(1) [],
|
||||
PCD OFFSET(4) NUMBITS(1) [],
|
||||
SSS OFFSET(8) NUMBITS(1) [],
|
||||
RSS OFFSET(9) NUMBITS(1) [],
|
||||
SRE OFFSET(10) NUMBITS(1) [],
|
||||
CNR OFFSET(11) NUMBITS(1) [],
|
||||
HCE OFFSET(12) NUMBITS(1) [],
|
||||
],
|
||||
pub CONFIG [
|
||||
MaxSlotsEn OFFSET(0) NUMBITS(8) [],
|
||||
U3E OFFSET(8) NUMBITS(1) [],
|
||||
CIE OFFSET(9) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
// Port registers
|
||||
pub PORTSC [
|
||||
CCS OFFSET(0) NUMBITS(1) [],
|
||||
PED OFFSET(1) NUMBITS(1) [],
|
||||
OCA OFFSET(3) NUMBITS(1) [],
|
||||
PR OFFSET(4) NUMBITS(1) [],
|
||||
PLS OFFSET(5) NUMBITS(4) [],
|
||||
PP OFFSET(9) NUMBITS(1) [],
|
||||
PS OFFSET(10) NUMBITS(4) [],
|
||||
PIC OFFSET(14) NUMBITS(2) [],
|
||||
LWS OFFSET(16) NUMBITS(1) [],
|
||||
CSC OFFSET(17) NUMBITS(1) [],
|
||||
PEC OFFSET(18) NUMBITS(1) [],
|
||||
WRC OFFSET(19) NUMBITS(1) [],
|
||||
OCC OFFSET(20) NUMBITS(1) [],
|
||||
PRC OFFSET(21) NUMBITS(1) [],
|
||||
PLC OFFSET(22) NUMBITS(1) [],
|
||||
CEC OFFSET(23) NUMBITS(1) [],
|
||||
],
|
||||
pub PORTLI [
|
||||
LEC OFFSET(0) NUMBITS(16) [],
|
||||
RLC OFFSET(16) NUMBITS(4) [],
|
||||
TLC OFFSET(20) NUMBITS(4) [],
|
||||
],
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub OperationalRegs {
|
||||
(0x000 => pub USBCMD: ReadWrite<u32, USBCMD::Register>),
|
||||
(0x004 => pub USBSTS: ReadWrite<u32, USBSTS::Register>),
|
||||
(0x008 => pub PAGESIZE: ReadOnly<u32>),
|
||||
(0x00C => _0),
|
||||
(0x014 => pub DNCTRL: ReadWrite<u32>),
|
||||
(0x018 => pub CRCR: ReadWrite<u64>),
|
||||
// (0x018 => pub CRCR_LO: ReadWrite<u32>),
|
||||
// (0x01C => pub CRCR_HI: ReadWrite<u32>),
|
||||
(0x020 => _1),
|
||||
(0x030 => pub DCBAAP: ReadWrite<u64>),
|
||||
// (0x030 => pub DCBAAP_LO: ReadWrite<u32>),
|
||||
// (0x034 => pub DCBAAP_HI: ReadWrite<u32>),
|
||||
(0x038 => pub CONFIG: ReadWrite<u32, CONFIG::Register>),
|
||||
(0x03C => _2),
|
||||
(0x400 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub PortRegs {
|
||||
(0x00 => pub PORTSC: ReadWrite<u32, PORTSC::Register>),
|
||||
(0x04 => pub PORTPMSC: ReadWrite<u32>),
|
||||
(0x08 => pub PORTLI: ReadWrite<u32, PORTLI::Register>),
|
||||
(0x0C => pub PORTHLPMC: ReadWrite<u32>),
|
||||
(0x10 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
impl OperationalRegs {
|
||||
pub fn wait_usbsts_bit(
|
||||
&self,
|
||||
bit: FieldValue<u32, USBSTS::Register>,
|
||||
mut timeout: u64,
|
||||
) -> Result<(), Error> {
|
||||
while timeout > 0 && !self.USBSTS.matches_all(bit) {
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
}
|
||||
if timeout > 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::TimedOut)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_dcbaap(&self, value: PhysicalAddress) {
|
||||
let value = value.into_u64();
|
||||
self.DCBAAP.set(value);
|
||||
}
|
||||
|
||||
pub fn set_crcr(&self, value: PhysicalAddress, dcs: bool) {
|
||||
let mut value = value.into_u64();
|
||||
if dcs {
|
||||
value |= 1;
|
||||
}
|
||||
self.CRCR.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
impl PortRegs {
|
||||
pub fn modify_portsc_preserving(&self, value: FieldValue<u32, PORTSC::Register>) {
|
||||
let neutral = super::portsc_to_neutral(self.PORTSC.extract());
|
||||
self.PORTSC.set(neutral.get() | value.value);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for PortRegs {}
|
69
kernel/driver/usb/xhci/src/regs/runtime.rs
Normal file
69
kernel/driver/usb/xhci/src/regs/runtime.rs
Normal file
@ -0,0 +1,69 @@
|
||||
use libk_mm::address::PhysicalAddress;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::ReadWrite,
|
||||
};
|
||||
|
||||
use crate::ring::{EventRing, EventRingSegmentTable};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
pub IMAN [
|
||||
IP OFFSET(0) NUMBITS(1) [],
|
||||
IE OFFSET(1) NUMBITS(1) [],
|
||||
],
|
||||
pub IMOD [
|
||||
IMODI OFFSET(0) NUMBITS(16) [],
|
||||
IMODC OFFSET(16) NUMBITS(16) [],
|
||||
],
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub InterrupterRegs {
|
||||
(0x00 => pub IMAN: ReadWrite<u32, IMAN::Register>),
|
||||
(0x04 => pub IMOD: ReadWrite<u32, IMOD::Register>),
|
||||
(0x08 => pub ERSTSZ: ReadWrite<u32>),
|
||||
(0x0C => _0),
|
||||
(0x10 => pub ERSTBA: ReadWrite<u64>),
|
||||
(0x18 => pub ERDP: ReadWrite<u64>),
|
||||
(0x20 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub RuntimeRegs {
|
||||
(0x0000 => pub MFINDEX: ReadWrite<u32>),
|
||||
(0x0004 => _0),
|
||||
// NOTE there're 1024 of them, but I'm not going to use all of those anyway, so use 1023
|
||||
// for a nice round number
|
||||
(0x0020 => pub IRn: [InterrupterRegs; 1023]),
|
||||
(0x8000 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeRegs {
|
||||
pub fn configure_interrupter(
|
||||
&self,
|
||||
index: usize,
|
||||
event_ring: &EventRing,
|
||||
erst: &EventRingSegmentTable,
|
||||
) {
|
||||
let interrupter = &self.IRn[index];
|
||||
|
||||
let erdp = event_ring.dequeue_pointer().into_u64();
|
||||
let erstba = erst.physical_address().into_u64();
|
||||
|
||||
interrupter.ERSTSZ.set(erst.capacity() as u32);
|
||||
interrupter.ERSTBA.set(erstba);
|
||||
interrupter.ERDP.set(erdp);
|
||||
interrupter.IMAN.write(IMAN::IE::SET);
|
||||
}
|
||||
|
||||
pub fn set_interrupter_dequeue_pointer(&self, index: usize, erdp: PhysicalAddress) {
|
||||
let _ = self.IRn[index].ERDP.get();
|
||||
self.IRn[index].ERDP.set(erdp.into_u64() | (1 << 3));
|
||||
}
|
||||
}
|
@ -138,6 +138,42 @@ impl CommandRing {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn reset_endpoint<E: CommandExecutor>(
|
||||
&self,
|
||||
executor: &E,
|
||||
slot_id: u8,
|
||||
endpoint_id: u8,
|
||||
preserve_transaction_state: bool,
|
||||
) -> Result<(), UsbError> {
|
||||
self.submit_and_wait(
|
||||
executor,
|
||||
ResetEndpointCommandTrb::new(slot_id, endpoint_id, preserve_transaction_state),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_tr_dequeue_pointer<E: CommandExecutor>(
|
||||
&self,
|
||||
executor: &E,
|
||||
slot_id: u8,
|
||||
endpoint_id: u8,
|
||||
dequeue_pointer: PhysicalAddress,
|
||||
dequeue_cycle: bool,
|
||||
) -> Result<(), UsbError> {
|
||||
self.submit_and_wait(
|
||||
executor,
|
||||
SetTrDequeuePointerCommandTrb::new(
|
||||
slot_id,
|
||||
endpoint_id,
|
||||
dequeue_pointer,
|
||||
dequeue_cycle,
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn enable_slot<E: CommandExecutor>(
|
||||
&self,
|
||||
executor: &E,
|
||||
@ -221,6 +257,21 @@ define_bitfields! {
|
||||
}
|
||||
}
|
||||
|
||||
define_bitfields! {
|
||||
pub ResetEndpointCommandFlags: u32 {
|
||||
(24..32) => slot_id,
|
||||
(16..21) => endpoint_id,
|
||||
9 => tsp,
|
||||
}
|
||||
}
|
||||
|
||||
define_bitfields! {
|
||||
pub SetTrDequeuePointerCommandFlags: u32 {
|
||||
(24..32) => slot_id,
|
||||
(16..21) => endpoint_id,
|
||||
}
|
||||
}
|
||||
|
||||
define_bitfields! {
|
||||
pub RawCommandFlags : u32 {
|
||||
(10..16) => ty + set_ty,
|
||||
@ -258,6 +309,21 @@ pub struct ConfigureEndpointCommandTrb {
|
||||
pub flags: ConfigureEndpointCommandFlags,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
#[repr(C, align(16))]
|
||||
pub struct ResetEndpointCommandTrb {
|
||||
_0: [u32; 3],
|
||||
pub flags: ResetEndpointCommandFlags,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
#[repr(C, align(16))]
|
||||
pub struct SetTrDequeuePointerCommandTrb {
|
||||
pub value: u64,
|
||||
_0: u32,
|
||||
pub flags: SetTrDequeuePointerCommandFlags,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
#[repr(C, align(16))]
|
||||
pub struct RawCommandTrb {
|
||||
@ -314,6 +380,38 @@ impl ConfigureEndpointCommandTrb {
|
||||
}
|
||||
}
|
||||
|
||||
impl ResetEndpointCommandTrb {
|
||||
pub fn new(slot_id: u8, endpoint_id: u8, preserve_transaction_state: bool) -> Self {
|
||||
Self {
|
||||
_0: [0; 3],
|
||||
flags: ResetEndpointCommandFlags::new(
|
||||
slot_id as _,
|
||||
endpoint_id as _,
|
||||
preserve_transaction_state,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SetTrDequeuePointerCommandTrb {
|
||||
pub fn new(
|
||||
slot_id: u8,
|
||||
endpoint_id: u8,
|
||||
dequeue_pointer: PhysicalAddress,
|
||||
dequeue_cycle: bool,
|
||||
) -> Self {
|
||||
let mut value = dequeue_pointer.into_u64();
|
||||
if dequeue_cycle {
|
||||
value |= 1;
|
||||
}
|
||||
Self {
|
||||
value,
|
||||
_0: 0,
|
||||
flags: SetTrDequeuePointerCommandFlags::new(slot_id as _, endpoint_id as _),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandTrb for EnableSlotCommandTrb {
|
||||
const TRB_TYPE: u8 = 9;
|
||||
}
|
||||
@ -329,3 +427,11 @@ impl CommandTrb for AddressDeviceCommandTrb {
|
||||
impl CommandTrb for ConfigureEndpointCommandTrb {
|
||||
const TRB_TYPE: u8 = 12;
|
||||
}
|
||||
|
||||
impl CommandTrb for ResetEndpointCommandTrb {
|
||||
const TRB_TYPE: u8 = 14;
|
||||
}
|
||||
|
||||
impl CommandTrb for SetTrDequeuePointerCommandTrb {
|
||||
const TRB_TYPE: u8 = 16;
|
||||
}
|
||||
|
@ -1,17 +1,31 @@
|
||||
//use bytemuck::{Pod, Zeroable};
|
||||
//use libk_mm::address::PhysicalAddress;
|
||||
//use yggdrasil_abi::define_bitfields;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use async_trait::async_trait;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use libk_mm::address::PhysicalAddress;
|
||||
use ygg_driver_usb::error::UsbError;
|
||||
use yggdrasil_abi::define_bitfields;
|
||||
|
||||
pub mod command;
|
||||
pub mod event;
|
||||
pub mod transfer;
|
||||
|
||||
pub use command::CommandRing;
|
||||
pub use event::{Event, EventRing, EventRingSegmentTable};
|
||||
pub use transfer::ControlTransferRing;
|
||||
pub use event::{EventRing, EventRingSegmentTable};
|
||||
|
||||
#[async_trait]
|
||||
pub trait CommandExecutor {
|
||||
fn ring_doorbell(&self, index: usize, target: u8);
|
||||
|
||||
async fn reset_endpoint(
|
||||
&self,
|
||||
slot_id: u8,
|
||||
endpoint_id: u8,
|
||||
dequeue_pointer: PhysicalAddress,
|
||||
dequeue_cycle: bool,
|
||||
) -> Result<(), UsbError>;
|
||||
}
|
||||
|
||||
pub trait GenericRing {
|
||||
@ -19,15 +33,6 @@ pub trait GenericRing {
|
||||
fn base(&self) -> PhysicalAddress;
|
||||
}
|
||||
|
||||
pub trait GenericTransferRing: GenericRing + Send + Sync {
|
||||
fn slot_id(&self) -> u8;
|
||||
fn endpoint_id(&self) -> u8;
|
||||
|
||||
fn dequeue_pointer(&self) -> PhysicalAddress;
|
||||
fn notify(&self, address: PhysicalAddress, status: u32);
|
||||
fn shutdown(&self);
|
||||
}
|
||||
|
||||
define_bitfields! {
|
||||
pub LinkTrbFlags : u32 {
|
||||
(10..16) => ty,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -46,18 +46,18 @@ impl EventBitmap {
|
||||
self.waker.wake();
|
||||
}
|
||||
|
||||
pub async fn wait_specific(&self, bit: usize) {
|
||||
poll_fn(|cx| {
|
||||
let state = self.bitmap.fetch_and(!(1 << bit), Ordering::Acquire);
|
||||
if state & (1 << bit) != 0 {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
self.waker.register(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
// pub async fn wait_specific(&self, bit: usize) {
|
||||
// poll_fn(|cx| {
|
||||
// let state = self.bitmap.fetch_and(!(1 << bit), Ordering::Acquire);
|
||||
// if state & (1 << bit) != 0 {
|
||||
// Poll::Ready(())
|
||||
// } else {
|
||||
// self.waker.register(cx.waker());
|
||||
// Poll::Pending
|
||||
// }
|
||||
// })
|
||||
// .await
|
||||
// }
|
||||
|
||||
pub async fn wait_any(&self, max: usize) -> impl Iterator<Item = usize> {
|
||||
poll_fn(|cx| {
|
||||
|
@ -158,8 +158,9 @@ impl<T: ?Sized> AsPhysicalAddress for DeviceMemoryIo<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized> Send for DeviceMemoryIo<'_, T> {}
|
||||
impl<T: ?Sized> !Sync for DeviceMemoryIo<'_, T> {}
|
||||
unsafe impl<T: ?Sized + Send> Send for DeviceMemoryIo<'_, T> {}
|
||||
unsafe impl<T: ?Sized + Sync> Sync for DeviceMemoryIo<'_, T> {}
|
||||
//impl<T: ?Sized> !Sync for DeviceMemoryIo<'_, T> {}
|
||||
|
||||
impl<'a, T: Sized> DeviceMemoryIoMut<'a, T> {
|
||||
/// Maps a physical address as device memory to a slice `[T; len]`
|
||||
|
@ -322,7 +322,7 @@ impl PanicLoggerSink<'_> {
|
||||
self.log(
|
||||
&log::Record::builder()
|
||||
.level(log::Level::Error)
|
||||
.target("raw")
|
||||
.target(":raw")
|
||||
.args(args)
|
||||
.build(),
|
||||
)
|
||||
@ -432,7 +432,7 @@ pub fn disable_early_sinks() {
|
||||
}
|
||||
|
||||
/// Print a trace message coming from a process
|
||||
pub fn program_trace(process: &Process, thread: &Thread, message: &str) {
|
||||
pub fn program_trace(process: &Process, _thread: &Thread, message: &str) {
|
||||
log::debug!(
|
||||
target: ":program",
|
||||
"{} ({}) {message}\n",
|
||||
|
@ -62,14 +62,14 @@ pub fn register_pci_drivers() {
|
||||
// ygg_driver_nvme::probe,
|
||||
// );
|
||||
// XXX: i686 hangs in interrupt handler
|
||||
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
||||
ygg_driver_pci::register_class_driver(
|
||||
"AHCI Controller",
|
||||
0x01,
|
||||
Some(0x06),
|
||||
Some(0x01),
|
||||
ygg_driver_ahci::probe,
|
||||
);
|
||||
// #[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
||||
// ygg_driver_pci::register_class_driver(
|
||||
// "AHCI Controller",
|
||||
// 0x01,
|
||||
// Some(0x06),
|
||||
// Some(0x01),
|
||||
// ygg_driver_ahci::probe,
|
||||
// );
|
||||
ygg_driver_pci::register_class_driver(
|
||||
"USB xHCI",
|
||||
0x0C,
|
||||
|
@ -1,8 +1,8 @@
|
||||
//! Kernel panic handler code
|
||||
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||
|
||||
use device_api::interrupt::{IpiDeliveryTarget, IpiMessage};
|
||||
use kernel_arch::{sync::hack_locks, Architecture, ArchitectureImpl};
|
||||
// use device_api::interrupt::{IpiDeliveryTarget, IpiMessage};
|
||||
use kernel_arch::{Architecture, ArchitectureImpl};
|
||||
use libk::{
|
||||
arch::{Cpu, LocalCpu},
|
||||
debug::{self, PanicLoggerSink},
|
||||
@ -65,17 +65,18 @@ pub(crate) fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
|
||||
.compare_exchange(false, true, Ordering::Release, Ordering::Acquire)
|
||||
.is_ok()
|
||||
{
|
||||
// Let other CPUs know we're screwed
|
||||
if unsafe {
|
||||
PLATFORM
|
||||
.send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Panic)
|
||||
.unwrap_or(false)
|
||||
} {
|
||||
let ap_count = ArchitectureImpl::cpu_count() - 1;
|
||||
PANIC_HANDLED_FENCE.wait_all(ap_count);
|
||||
}
|
||||
// TODO IPI leads to triple fault sometimes in x86-64
|
||||
// // Let other CPUs know we're screwed
|
||||
// if unsafe {
|
||||
// PLATFORM
|
||||
// .send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Panic)
|
||||
// .unwrap_or(false)
|
||||
// } {
|
||||
// let ap_count = ArchitectureImpl::cpu_count() - 1;
|
||||
// PANIC_HANDLED_FENCE.wait_all(ap_count);
|
||||
// }
|
||||
|
||||
unsafe { hack_locks() };
|
||||
// unsafe { hack_locks() };
|
||||
|
||||
dump_panic_info(&cpu, pi);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user