yggdrasil/kernel/driver/usb/xhci/src/controller.rs

382 lines
11 KiB
Rust

use core::{future::poll_fn, sync::atomic::Ordering, task::Poll, time::Duration};
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, vec, vec::Vec};
use atomic_enum::atomic_enum;
use device_api::{device::Device, interrupt::InterruptHandler};
use futures_util::task::AtomicWaker;
use libk::task::runtime;
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
PageBox,
};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use ygg_driver_usb::{
bus::UsbBusManager,
device::{UsbBusAddress, UsbDevice, UsbDeviceAccess},
error::UsbError,
pipe::control::UsbControlPipeAccess,
util::UsbAddressAllocator,
UsbHostController,
};
use yggdrasil_abi::error::Error;
use crate::{
device::XhciBusDevice,
pipe::ControlPipe,
regs::{Mapper, PortSpeed, Regs},
ring::{
CommandExecutor, CommandRing, ControlTransferRing, Event, EventRing, EventRingSegmentTable,
GenericTransferRing,
},
XhciContext,
};
#[atomic_enum]
#[derive(PartialEq, Eq)]
pub enum PortState {
Disconnected, // Default + set by "handle detach"
Init, // Set by "port task"
Running, // Set by "port task"
}
struct PortStruct {
state: AtomicPortState,
notify: AtomicWaker,
address: IrqSafeRwLock<Option<UsbBusAddress>>,
}
pub struct Xhci {
regs: Regs,
bus_address: OneTimeInit<u16>,
address_allocator: UsbAddressAllocator,
port_count: usize,
// TODO use to allocate proper contexts
#[allow(unused)]
context_size: usize,
dcbaa: IrqSafeRwLock<PageBox<[PhysicalAddress]>>,
endpoints: IrqSafeRwLock<BTreeMap<(u8, u8), Arc<dyn GenericTransferRing>>>,
event_ring: EventRing,
pub(crate) command_ring: CommandRing,
port_states: Vec<PortStruct>,
}
impl Xhci {
pub fn new(regs: xhci_lib::Registers<Mapper>) -> Result<Self, UsbError> {
let event_ring = EventRing::new(128)?;
let command_ring = CommandRing::new(128)?;
let regs = Regs::from(regs);
let port_count = regs.port_count();
let slot_count = regs.max_slot_count();
let context_size = regs.context_size();
let dcbaa = PageBox::new_slice(PhysicalAddress::ZERO, slot_count + 1)
.map_err(UsbError::MemoryError)?;
Ok(Self {
regs,
bus_address: OneTimeInit::new(),
address_allocator: UsbAddressAllocator::new(),
port_count,
context_size,
event_ring,
command_ring,
dcbaa: IrqSafeRwLock::new(dcbaa),
endpoints: IrqSafeRwLock::new(BTreeMap::new()),
port_states: Vec::from_iter((0..port_count).map(|_| PortStruct {
state: AtomicPortState::new(PortState::Disconnected),
notify: AtomicWaker::new(),
address: IrqSafeRwLock::new(None),
})),
})
}
pub fn register_device_context(&self, slot_id: u8, context: PhysicalAddress) {
self.dcbaa.write()[slot_id as usize] = context;
}
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)) {
endpoint.shutdown();
} else {
log::warn!(
"Endpoint {}:{} does not exist, maybe already shut down?",
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);
}
}
async fn assign_device(
self: Arc<Self>,
speed: PortSpeed,
slot_id: u8,
root_hub_port_number: u8,
) -> Result<Box<dyn UsbDevice>, UsbError> {
let address = self.address_allocator.allocate().unwrap();
let ring = Arc::new(ControlTransferRing::new(slot_id, 1, 128)?);
let context =
XhciContext::new_32byte_address_device(&ring, speed, address, root_hub_port_number)?;
let mut input = context.input.write();
self.register_device_context(slot_id, unsafe { context.output.as_physical_address() });
self.command_ring
.address_device(&*self, slot_id, &mut input)
.await?;
self.register_endpoint(slot_id, 1, ring.clone());
let pipe = ControlPipe::new(self.clone(), slot_id, ring.clone());
// TODO: If the device is a Full-speed one, determine its max packet size for the control
// endpoint
if speed == PortSpeed::Full {
todo!()
}
drop(input);
let bus_address = UsbBusAddress {
bus: *self.bus_address.get(),
device: address,
};
let device = XhciBusDevice {
xhci: self.clone(),
slot_id,
port_id: root_hub_port_number,
bus_address,
speed,
context: Arc::new(context),
rings: IrqSafeRwLock::new(vec![ring]),
control_pipe: UsbControlPipeAccess(Box::new(pipe)),
};
Ok(Box::new(device))
}
async fn port_task(self: Arc<Self>, index: usize) -> Result<(), UsbError> {
let state = &self.port_states[index];
self.reset_port(index).await?;
let regs = self.regs.ports.read(index);
let speed =
PortSpeed::try_from(regs.portsc.port_speed()).map_err(|_| UsbError::PortInitFailed)?;
let slot_id = self.command_ring.enable_slot(self.as_ref()).await?;
let device = self
.clone()
.assign_device(speed, slot_id, (index + 1) as _)
.await?;
let device = UsbDeviceAccess::setup(device).await?;
let old = state.address.write().replace(device.bus_address());
assert!(old.is_none());
UsbBusManager::register_device(Arc::new(device));
state.state.store(PortState::Running, Ordering::Release);
Ok(())
}
fn handle_device_attached(self: Arc<Self>, port: usize) -> Result<(), UsbError> {
log::info!("Port {}: device attached", port);
if let Err(err) = self.port_states[port].state.compare_exchange(
PortState::Disconnected,
PortState::Init,
Ordering::Acquire,
Ordering::Relaxed,
) {
log::warn!("Could not start port init task: port state is {:?}", err);
return Err(UsbError::DeviceBusy);
}
runtime::spawn(async move { self.port_task(port).await }).map_err(UsbError::SystemError)?;
Ok(())
}
fn handle_device_detached(&self, port: usize) -> Result<(), UsbError> {
let state = &self.port_states[port];
match state.state.swap(PortState::Disconnected, Ordering::Release) {
PortState::Init => todo!(),
PortState::Running => {
log::info!("Port {}: device detached", port);
let address = state
.address
.write()
.take()
.expect("Port marked as Running, but has no address");
UsbBusManager::detach_device(address);
state.notify.wake();
Ok(())
}
// Already disconnected
PortState::Disconnected => Ok(()),
}
}
fn handle_port_event(self: Arc<Self>, port: usize) -> Result<(), UsbError> {
let state = &self.port_states[port];
let port_regs = self.regs.ports.read(port);
if port_regs.portsc.connect_status_change() {
if port_regs.portsc.current_connect_status() {
self.handle_device_attached(port)
} else {
self.handle_device_detached(port)
}
} else {
// Some other event
state.notify.wake();
Ok(())
}
}
async fn reset_port(&self, port: usize) -> Result<(), UsbError> {
log::debug!("Reset port {}", port);
self.regs.ports.update(port, |u| {
u.portsc.set_port_reset();
});
// Wait for port reset
// TODO handle disconnect during reset?
let result = runtime::with_timeout(
poll_fn(|cx| {
let state = &self.port_states[port];
state.notify.register(cx.waker());
if !self.regs.ports.read(port).portsc.port_reset() {
Poll::Ready(())
} else {
Poll::Pending
}
}),
Duration::from_secs(1),
)
.await;
match result {
Ok(()) => Ok(()),
Err(_) => Err(UsbError::PortResetFailed),
}
}
fn handle_event(self: Arc<Self>) {
while let Some(event) = self.event_ring.try_dequeue() {
match event {
Event::PortChange(port) => {
self.clone().handle_port_event(port - 1).ok();
}
Event::CommandCompletion { address, reply } => {
self.command_ring.notify(address, reply);
}
Event::Transfer {
address,
slot_id,
endpoint_id,
status,
} => {
self.notify_transfer(slot_id, endpoint_id, address, status);
}
}
}
self.regs
.set_interrupter_0_dequeue_pointer(self.event_ring.dequeue_pointer());
}
}
impl UsbHostController for Xhci {}
impl CommandExecutor for Xhci {
fn ring_doorbell(&self, index: usize, target: u8) {
self.regs.ring_doorbell(index, target);
}
}
impl Device for Xhci {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
log::info!("Init USB xHCI");
self.regs.reset();
self.regs.set_max_slot_count();
let erst = EventRingSegmentTable::for_event_rings(&[&self.event_ring])?;
let dcbaa = self.dcbaa.read();
self.regs
.configure(&dcbaa, &self.command_ring, &self.event_ring, &erst);
let bus = UsbBusManager::register_bus(self.clone());
self.bus_address.init(bus);
for port in 0..self.port_count {
let p = self.regs.ports.read(port);
if p.portsc.current_connect_status() {
self.clone().handle_device_attached(port).ok();
}
}
Ok(())
}
fn display_name(&self) -> &str {
"USB 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() {
self.handle_event();
}
true
} else {
false
}
}
}