usb: add support for usb hubs
This commit is contained in:
@@ -40,7 +40,7 @@ pub mod transport;
|
||||
|
||||
pub struct ScsiEnclosure {
|
||||
transport: AsyncMutex<ScsiTransportWrapper>,
|
||||
units: Vec<IrqSafeRwLock<Option<Arc<ScsiUnit>>>>,
|
||||
units: Vec<AsyncMutex<Option<Arc<ScsiUnit>>>>,
|
||||
index: OneTimeInit<u32>,
|
||||
shutdown: AtomicBool,
|
||||
}
|
||||
@@ -60,7 +60,7 @@ impl ScsiEnclosure {
|
||||
lun_count: usize,
|
||||
) -> Result<Arc<Self>, Error> {
|
||||
let transport = AsyncMutex::new(ScsiTransportWrapper::new(transport));
|
||||
let units = (0..lun_count).map(|_| IrqSafeRwLock::new(None)).collect();
|
||||
let units = (0..lun_count).map(|_| AsyncMutex::new(None)).collect();
|
||||
let this = Arc::new(Self {
|
||||
transport,
|
||||
units,
|
||||
@@ -74,7 +74,7 @@ impl ScsiEnclosure {
|
||||
if this.probe_lun(i as u8).await
|
||||
&& let Ok(unit) = ScsiUnit::setup(this.clone(), i as u8).await
|
||||
{
|
||||
*this.units[i].write() = Some(unit);
|
||||
*this.units[i].lock().await = Some(unit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ impl ScsiEnclosure {
|
||||
async fn poll(self: &Arc<Self>) {
|
||||
let index = *self.index.get();
|
||||
for lun in 0..self.units.len() {
|
||||
let mut slot = self.units[lun].write();
|
||||
let mut slot = self.units[lun].lock().await;
|
||||
let present = self.probe_lun(lun as u8).await;
|
||||
|
||||
if let Some(unit) = slot.as_ref() {
|
||||
@@ -143,12 +143,12 @@ impl ScsiEnclosure {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detach(&self) {
|
||||
pub async fn detach(&self) {
|
||||
self.shutdown.store(true, Ordering::Release);
|
||||
let index = self.index.try_get().copied();
|
||||
|
||||
for unit in self.units.iter() {
|
||||
if let Some(unit) = unit.write().take() {
|
||||
if let Some(unit) = unit.lock().await.take() {
|
||||
unit.detach();
|
||||
}
|
||||
}
|
||||
@@ -368,10 +368,11 @@ fn register_unit(enclosure_index: u32, lun: u8, unit: Arc<ScsiUnit>) {
|
||||
}
|
||||
|
||||
fn remove_enclosure(index: u32) {
|
||||
log::info!("scsi: enclosure {index} detached");
|
||||
|
||||
let mut devices = SCSI_ENCLOSURES.lock();
|
||||
let mut bitmap = SCSI_BITMAP.lock();
|
||||
|
||||
*bitmap &= !(1 << index);
|
||||
devices.remove(&index);
|
||||
log::info!("scsi: enclosure {index} detached");
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@ use alloc::{collections::BTreeMap, sync::Arc};
|
||||
use libk_util::{queue::UnboundedMpmcQueue, sync::spin_rwlock::IrqSafeRwLock};
|
||||
|
||||
use crate::{
|
||||
UsbHostController, class_driver,
|
||||
class_driver,
|
||||
device::{UsbBusAddress, UsbDeviceAccess},
|
||||
host::UsbHostController,
|
||||
};
|
||||
|
||||
pub struct UsbBusManager {
|
||||
busses: IrqSafeRwLock<BTreeMap<u16, Arc<dyn UsbHostController>>>,
|
||||
devices: IrqSafeRwLock<BTreeMap<UsbBusAddress, Arc<UsbDeviceAccess>>>,
|
||||
|
||||
last_bus_address: AtomicU16,
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@ impl UsbBusManager {
|
||||
QUEUE.push_back(device);
|
||||
}
|
||||
|
||||
pub fn detach_device(address: UsbBusAddress) {
|
||||
pub async fn detach_device(address: UsbBusAddress) {
|
||||
if let Some(device) = BUS_MANAGER.devices.write().remove(&address) {
|
||||
device.handle_detach();
|
||||
device.handle_detach().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,20 +44,26 @@ pub async fn bus_handler() {
|
||||
|
||||
loop {
|
||||
let new_device = QUEUE.pop_front().await;
|
||||
let id_vendor = new_device.device_descriptor.id_vendor;
|
||||
let id_product = new_device.device_descriptor.id_product;
|
||||
log::info!(
|
||||
"New {:?}-speed USB device connected: {}",
|
||||
"{} ({}): {:?}-speed USB device connected: {:?} {:?} ({:04x}:{:04x})",
|
||||
new_device.bus_address(),
|
||||
new_device.port_string(),
|
||||
new_device.speed(),
|
||||
new_device.bus_address()
|
||||
new_device.vendor_str,
|
||||
new_device.product_str,
|
||||
id_vendor,
|
||||
id_product
|
||||
);
|
||||
|
||||
class_driver::spawn_driver(new_device).await.ok();
|
||||
class_driver::spawn_driver(new_device).await;
|
||||
}
|
||||
}
|
||||
|
||||
static BUS_MANAGER: UsbBusManager = UsbBusManager {
|
||||
busses: IrqSafeRwLock::new(BTreeMap::new()),
|
||||
devices: IrqSafeRwLock::new(BTreeMap::new()),
|
||||
|
||||
last_bus_address: AtomicU16::new(0),
|
||||
};
|
||||
static QUEUE: UnboundedMpmcQueue<Arc<UsbDeviceAccess>> = UnboundedMpmcQueue::new();
|
||||
|
||||
+78
-19
@@ -2,13 +2,12 @@ use core::mem::MaybeUninit;
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent};
|
||||
use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent, MouseEvent};
|
||||
|
||||
use crate::{device::UsbDeviceAccess, error::UsbError, info::UsbDeviceClass};
|
||||
|
||||
use super::{UsbClassInfo, UsbDriver};
|
||||
use crate::{class_driver::UsbInterfaceDriver, device::UsbInterfaceAccess, error::UsbError};
|
||||
|
||||
pub struct UsbHidKeyboardDriver;
|
||||
pub struct UsbHidMouseDriver;
|
||||
|
||||
const MODIFIER_MAP: &[KeyboardKey] = &[
|
||||
KeyboardKey::LControl,
|
||||
@@ -125,14 +124,25 @@ impl KeyboardState {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbDriver for UsbHidKeyboardDriver {
|
||||
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();
|
||||
impl UsbInterfaceDriver for UsbHidKeyboardDriver {
|
||||
async fn run(self: Arc<Self>, interface: UsbInterfaceAccess) -> Result<(), UsbError> {
|
||||
log::info!("{}: HID keyboard", interface.address());
|
||||
|
||||
log::info!("Setup HID keyboard");
|
||||
let pipe = device
|
||||
.open_interrupt_in_pipe(1, config.endpoints[0].max_packet_size as u16)
|
||||
let endpoint_infos = interface.endpoints();
|
||||
if endpoint_infos.is_empty() {
|
||||
log::warn!(
|
||||
"{}: no available endpoints in interface description",
|
||||
interface.address()
|
||||
);
|
||||
return Err(UsbError::InvalidConfiguration);
|
||||
}
|
||||
|
||||
let pipe = interface
|
||||
.device()
|
||||
.open_interrupt_in_pipe(
|
||||
endpoint_infos[0].number,
|
||||
endpoint_infos[0].max_packet_size as _,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut buffer = [0; 8];
|
||||
@@ -156,7 +166,7 @@ impl UsbDriver for UsbHidKeyboardDriver {
|
||||
|
||||
for &event in events {
|
||||
log::trace!("Generic Keyboard: {:?}", event);
|
||||
ygg_driver_input::send_event(event);
|
||||
ygg_driver_input::send_keyboard_event(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,12 +175,61 @@ impl UsbDriver for UsbHidKeyboardDriver {
|
||||
"USB HID Keyboard"
|
||||
}
|
||||
|
||||
fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
|
||||
log::info!(
|
||||
"class = {:?}, subclass = {:02x}",
|
||||
class.class,
|
||||
class.subclass
|
||||
);
|
||||
class.class == UsbDeviceClass::Hid && (class.subclass == 0x00 || class.subclass == 0x01)
|
||||
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool {
|
||||
class == 0x03 && subclass == 0x01 && protocol == 0x01
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbInterfaceDriver for UsbHidMouseDriver {
|
||||
async fn run(self: Arc<Self>, interface: UsbInterfaceAccess) -> Result<(), UsbError> {
|
||||
log::info!("{}: HID mouse", interface.address());
|
||||
|
||||
let endpoint_infos = interface.endpoints();
|
||||
if endpoint_infos.is_empty() {
|
||||
log::warn!(
|
||||
"{}: no available endpoints in interface description",
|
||||
interface.address()
|
||||
);
|
||||
return Err(UsbError::InvalidConfiguration);
|
||||
}
|
||||
|
||||
let pipe = interface
|
||||
.device()
|
||||
.open_interrupt_in_pipe(
|
||||
endpoint_infos[0].number,
|
||||
endpoint_infos[0].max_packet_size as _,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut buffer = [0; 8];
|
||||
|
||||
loop {
|
||||
let len = pipe.read(&mut buffer).await?;
|
||||
if len < 4 {
|
||||
continue;
|
||||
}
|
||||
let data = &buffer[..len];
|
||||
|
||||
let buttons = data[0];
|
||||
let dx = data[1] as i8;
|
||||
let dy = data[2] as i8;
|
||||
|
||||
log::trace!("mouse {dx:+},{dy:+} {buttons:08b}");
|
||||
ygg_driver_input::send_mouse_event(MouseEvent {
|
||||
dx,
|
||||
dy,
|
||||
buttons,
|
||||
unused: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"USB HID Mouse"
|
||||
}
|
||||
|
||||
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool {
|
||||
class == 0x03 && subclass == 0x01 && protocol == 0x02
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
use core::{mem::MaybeUninit, num::NonZeroU8, time::Duration};
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use libk::{task::runtime, time::monotonic_time};
|
||||
use yggdrasil_abi::bitflags;
|
||||
|
||||
use crate::{
|
||||
class_driver::UsbDeviceDriver,
|
||||
descriptor::UsbHubDescriptorHeader,
|
||||
device::{UsbDeviceAccess, UsbSpeed},
|
||||
error::UsbError,
|
||||
pipe::control::ControlTransferSetup,
|
||||
};
|
||||
|
||||
pub struct UsbHubDriver;
|
||||
|
||||
const BM_REQUEST_TYPE_GET_HUB_DESCRIPTOR: u8 = 0b10100000;
|
||||
const BM_REQUEST_TYPE_GET_PORT_STATUS: u8 = 0b10100011;
|
||||
const BM_REQUEST_TYPE_PORT_FEATURE: u8 = 0b00100011;
|
||||
|
||||
const B_REQUEST_GET_STATUS: u8 = 0x00;
|
||||
const B_REQUEST_CLEAR_FEATURE: u8 = 0x01;
|
||||
const B_REQUEST_SET_FEATURE: u8 = 0x03;
|
||||
const B_REQUEST_GET_DESCRIPTOR: u8 = 0x06;
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u16)]
|
||||
pub enum PortFeatureSelector {
|
||||
PortConnection = 0,
|
||||
PortEnable = 1,
|
||||
PortSuspend = 2,
|
||||
PortOverCurrent = 3,
|
||||
PortReset = 4,
|
||||
PortPower = 8,
|
||||
PortLowSpeed = 9,
|
||||
CPortConnection = 16,
|
||||
CPortEnable = 17,
|
||||
CPortSuspend = 18,
|
||||
CPortOverCurrent = 19,
|
||||
CPortReset = 20,
|
||||
PortTest = 21,
|
||||
PortIndicator = 22,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct UsbHubPortStatus: u16 {
|
||||
const PORT_CONNECTION: bit 0;
|
||||
const PORT_ENABLE: bit 1;
|
||||
const PORT_SUSPEND: bit 2;
|
||||
const PORT_OVER_CURRENT: bit 3;
|
||||
const PORT_RESET: bit 4;
|
||||
const PORT_POWER: bit 8;
|
||||
const PORT_LOW_SPEED: bit 9;
|
||||
const PORT_HIGH_SPEED: bit 10;
|
||||
const PORT_TEST: bit 11;
|
||||
const PORT_INDICATOR: bit 12;
|
||||
}
|
||||
}
|
||||
bitflags! {
|
||||
pub struct UsbHubPortChange: u16 {
|
||||
const C_PORT_CONNECTION: bit 0;
|
||||
const C_PORT_ENABLE: bit 1;
|
||||
const C_PORT_SUSPEND: bit 2;
|
||||
const C_PORT_OVER_CURRENT: bit 3;
|
||||
const C_PORT_RESET: bit 4;
|
||||
}
|
||||
}
|
||||
|
||||
struct UsbHub {
|
||||
device: Arc<UsbDeviceAccess>,
|
||||
hub_descriptor: UsbHubDescriptorHeader,
|
||||
children: u64,
|
||||
// TODO extra info
|
||||
}
|
||||
|
||||
impl UsbHub {
|
||||
pub async fn setup(device: Arc<UsbDeviceAccess>) -> Result<Self, UsbError> {
|
||||
let control_pipe = device.control_pipe();
|
||||
let mut hub_descriptor = MaybeUninit::uninit();
|
||||
|
||||
let len = control_pipe
|
||||
.control_transfer_in(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: BM_REQUEST_TYPE_GET_HUB_DESCRIPTOR,
|
||||
b_request: B_REQUEST_GET_DESCRIPTOR,
|
||||
w_value: 0,
|
||||
w_index: 0,
|
||||
w_length: size_of::<UsbHubDescriptorHeader>() as _,
|
||||
},
|
||||
hub_descriptor.as_bytes_mut(),
|
||||
)
|
||||
.await?;
|
||||
if len != size_of::<UsbHubDescriptorHeader>() {
|
||||
return Err(UsbError::TruncatedDescriptor(
|
||||
size_of::<UsbHubDescriptorHeader>(),
|
||||
len,
|
||||
));
|
||||
}
|
||||
let hub_descriptor: UsbHubDescriptorHeader = unsafe { hub_descriptor.assume_init() };
|
||||
if hub_descriptor.b_nr_ports < 1 {
|
||||
log::warn!(
|
||||
"{}: ignoring hub with zero downstream ports",
|
||||
device.device.bus_address()
|
||||
);
|
||||
return Err(UsbError::InvalidDescriptorField);
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"{}: setting up {:?}-speed hub, {} downstream ports",
|
||||
device.device.bus_address(),
|
||||
device.device.speed(),
|
||||
hub_descriptor.b_nr_ports
|
||||
);
|
||||
|
||||
device.configure_hub(&hub_descriptor).await?;
|
||||
|
||||
// TODO power on the ports?
|
||||
Ok(Self {
|
||||
device,
|
||||
hub_descriptor,
|
||||
children: 0,
|
||||
})
|
||||
}
|
||||
|
||||
async fn set_port_feature(
|
||||
&mut self,
|
||||
port: NonZeroU8,
|
||||
feature: PortFeatureSelector,
|
||||
) -> Result<(), UsbError> {
|
||||
self.device
|
||||
.control_pipe()
|
||||
.control_transfer(ControlTransferSetup {
|
||||
bm_request_type: BM_REQUEST_TYPE_PORT_FEATURE,
|
||||
b_request: B_REQUEST_SET_FEATURE,
|
||||
w_value: feature as _,
|
||||
w_index: port.get() as _,
|
||||
w_length: 0,
|
||||
})
|
||||
.await
|
||||
}
|
||||
async fn clear_port_feature(
|
||||
&mut self,
|
||||
port: NonZeroU8,
|
||||
feature: PortFeatureSelector,
|
||||
) -> Result<(), UsbError> {
|
||||
self.device
|
||||
.control_pipe()
|
||||
.control_transfer(ControlTransferSetup {
|
||||
bm_request_type: BM_REQUEST_TYPE_PORT_FEATURE,
|
||||
b_request: B_REQUEST_CLEAR_FEATURE,
|
||||
w_value: feature as _,
|
||||
w_index: port.get() as _,
|
||||
w_length: 0,
|
||||
})
|
||||
.await
|
||||
}
|
||||
async fn clear_port_changes(
|
||||
&mut self,
|
||||
port: NonZeroU8,
|
||||
change: UsbHubPortChange,
|
||||
) -> Result<(), UsbError> {
|
||||
if change.contains(UsbHubPortChange::C_PORT_RESET) {
|
||||
self.clear_port_feature(port, PortFeatureSelector::CPortReset)
|
||||
.await?;
|
||||
}
|
||||
if change.contains(UsbHubPortChange::C_PORT_CONNECTION) {
|
||||
self.clear_port_feature(port, PortFeatureSelector::CPortConnection)
|
||||
.await?;
|
||||
}
|
||||
if change.contains(UsbHubPortChange::C_PORT_ENABLE) {
|
||||
self.clear_port_feature(port, PortFeatureSelector::CPortEnable)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_port_status(
|
||||
&mut self,
|
||||
port: NonZeroU8,
|
||||
) -> Result<(UsbHubPortStatus, UsbHubPortChange), UsbError> {
|
||||
let mut buffer = [MaybeUninit::uninit(); 2];
|
||||
let len = self
|
||||
.device
|
||||
.control_pipe()
|
||||
.control_transfer_in(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: BM_REQUEST_TYPE_GET_PORT_STATUS,
|
||||
b_request: B_REQUEST_GET_STATUS,
|
||||
w_value: 0,
|
||||
w_index: port.get() as _,
|
||||
w_length: 4,
|
||||
},
|
||||
buffer.as_bytes_mut(),
|
||||
)
|
||||
.await?;
|
||||
if len != 4 {
|
||||
return Err(UsbError::TruncatedDescriptor(4, len));
|
||||
}
|
||||
let [w0, w1] = unsafe { MaybeUninit::array_assume_init(buffer) };
|
||||
let status = UsbHubPortStatus(w0);
|
||||
let change = UsbHubPortChange(w1);
|
||||
Ok((status, change))
|
||||
}
|
||||
|
||||
async fn reset_port(&mut self, port: NonZeroU8) -> Result<UsbHubPortStatus, UsbError> {
|
||||
self.set_port_feature(port, PortFeatureSelector::PortReset)
|
||||
.await?;
|
||||
|
||||
// Wait for reset to be asserted
|
||||
let deadline = monotonic_time() + Duration::from_secs(3);
|
||||
let mut status;
|
||||
let mut change;
|
||||
|
||||
loop {
|
||||
(status, change) = self.get_port_status(port).await?;
|
||||
|
||||
self.clear_port_changes(port, change).await?;
|
||||
|
||||
if status.contains(UsbHubPortStatus::PORT_RESET)
|
||||
|| status.contains(UsbHubPortStatus::PORT_ENABLE)
|
||||
|| !status.contains(UsbHubPortStatus::PORT_CONNECTION)
|
||||
{
|
||||
// Port reset got asserted or port got enabled
|
||||
break;
|
||||
}
|
||||
|
||||
if monotonic_time() >= deadline {
|
||||
log::warn!("Port reset did not assert in 3 sec");
|
||||
return Ok(status);
|
||||
}
|
||||
}
|
||||
|
||||
if status.contains(UsbHubPortStatus::PORT_ENABLE)
|
||||
|| !status.contains(UsbHubPortStatus::PORT_CONNECTION)
|
||||
{
|
||||
return Ok(status);
|
||||
}
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn setup_connected_port(&mut self, port: NonZeroU8) -> Result<(), UsbError> {
|
||||
let status = self.reset_port(port).await?;
|
||||
if !status.contains(UsbHubPortStatus::PORT_CONNECTION) {
|
||||
log::warn!(
|
||||
"{}: port {} disconnected during reset",
|
||||
self.device.bus_address(),
|
||||
port
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
if !status.contains(UsbHubPortStatus::PORT_ENABLE) {
|
||||
log::warn!("{}: port {} did not reset", self.device.bus_address(), port);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let usb_speed = match (
|
||||
status.contains(UsbHubPortStatus::PORT_LOW_SPEED),
|
||||
status.contains(UsbHubPortStatus::PORT_HIGH_SPEED),
|
||||
) {
|
||||
(false, false) => UsbSpeed::Full,
|
||||
(true, false) => UsbSpeed::Low,
|
||||
(false, true) => UsbSpeed::High,
|
||||
(true, true) => todo!(),
|
||||
};
|
||||
log::info!(
|
||||
"{}: hub port {}: {:?}-speed device connected",
|
||||
self.device.bus_address(),
|
||||
port,
|
||||
usb_speed
|
||||
);
|
||||
let port_string = self.device.port_string().append(port);
|
||||
if let Err(error) = self
|
||||
.device
|
||||
.host_controller()
|
||||
.setup_hub_device(self.device.clone(), port_string, usb_speed)
|
||||
.await
|
||||
{
|
||||
log::error!(
|
||||
"{}: hub port {} ({}) setup failed: {:?}",
|
||||
self.device.bus_address(),
|
||||
port,
|
||||
port_string,
|
||||
error
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
self.children |= 1 << (port.get() - 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn disconnect_port(&mut self, port: NonZeroU8) -> Result<(), UsbError> {
|
||||
let port_string = self.device.port_string().append(port);
|
||||
self.children &= !(1 << (port.get() - 1));
|
||||
self.device
|
||||
.host_controller()
|
||||
.disconnect_device(port_string)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn poll_port(&mut self, port: NonZeroU8) -> Result<(), UsbError> {
|
||||
let (status, change) = self.get_port_status(port).await?;
|
||||
if change.contains(UsbHubPortChange::C_PORT_CONNECTION) {
|
||||
// Clear feature: C_PORT_CONNECTION
|
||||
self.clear_port_feature(port, PortFeatureSelector::CPortConnection)
|
||||
.await?;
|
||||
|
||||
if status.contains(UsbHubPortStatus::PORT_CONNECTION) {
|
||||
self.setup_connected_port(port).await?;
|
||||
} else if let Err(error) = self.disconnect_port(port).await {
|
||||
log::warn!(
|
||||
"{}: hub port {} did not disconnect cleanly: {:?}",
|
||||
self.device.bus_address(),
|
||||
port,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn poll(&mut self) -> bool {
|
||||
for i in 0..self.hub_descriptor.b_nr_ports {
|
||||
let port = NonZeroU8::new(i + 1).unwrap();
|
||||
if let Err(error) = self.poll_port(port).await {
|
||||
log::error!(
|
||||
"{}: hub port {} poll error: {:?}",
|
||||
self.device.bus_address(),
|
||||
port,
|
||||
error
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
async fn cleanup(&mut self) {
|
||||
for i in 0..self.hub_descriptor.b_nr_ports {
|
||||
let port = NonZeroU8::new(i + 1).unwrap();
|
||||
if self.children & (1 << i) != 0 {
|
||||
if let Err(error) = self.disconnect_port(port).await {
|
||||
log::warn!(
|
||||
"{}: downstream {} disconnect error: {:?}",
|
||||
self.device.bus_address(),
|
||||
port,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(mut self) -> Result<(), UsbError> {
|
||||
loop {
|
||||
if !self.poll().await {
|
||||
self.cleanup().await;
|
||||
return Ok(());
|
||||
}
|
||||
runtime::sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbDeviceDriver for UsbHubDriver {
|
||||
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
|
||||
let hub = UsbHub::setup(device).await?;
|
||||
hub.run().await
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"USB Hub"
|
||||
}
|
||||
|
||||
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool {
|
||||
let _ = protocol;
|
||||
class == 0x09 && subclass == 0x00
|
||||
}
|
||||
}
|
||||
@@ -10,17 +10,22 @@ use libk::{
|
||||
use ygg_driver_scsi::{ScsiEnclosure, transport::ScsiTransport};
|
||||
|
||||
use crate::{
|
||||
class_driver::UsbInterfaceDriver,
|
||||
communication::UsbDirection,
|
||||
device::{UsbDeviceAccess, UsbDeviceDetachHandler},
|
||||
device::{UsbDeviceDetachHandler, UsbInterfaceAccess},
|
||||
error::UsbError,
|
||||
info::{UsbDeviceClass, UsbEndpointType},
|
||||
info::UsbEndpointType,
|
||||
pipe::{
|
||||
control::{ControlTransferSetup, UsbClassSpecificRequest},
|
||||
control::ControlTransferSetup,
|
||||
normal::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{UsbClassInfo, UsbDriver};
|
||||
const BM_REQUEST_TYPE_BULK_ONLY_MASS_STORAGE_RESET: u8 = 0b00100001;
|
||||
const BM_REQUEST_TYPE_GET_MAX_LUN: u8 = 0b10100001;
|
||||
|
||||
const B_REQUEST_BULK_ONLY_MASS_STORAGE_RESET: u8 = 0b11111111;
|
||||
const B_REQUEST_GET_MAX_LUN: u8 = 0b11111110;
|
||||
|
||||
pub struct UsbMassStorageDriverBulkOnly;
|
||||
|
||||
@@ -50,7 +55,7 @@ struct Csw {
|
||||
|
||||
struct Bbb {
|
||||
#[allow(unused)]
|
||||
device: Arc<UsbDeviceAccess>,
|
||||
interface: UsbInterfaceAccess,
|
||||
in_pipe: UsbBulkInPipeAccess,
|
||||
out_pipe: UsbBulkOutPipeAccess,
|
||||
last_tag: u32,
|
||||
@@ -60,12 +65,12 @@ struct DetachHandler(Arc<ScsiEnclosure>);
|
||||
|
||||
impl Bbb {
|
||||
pub fn new(
|
||||
device: Arc<UsbDeviceAccess>,
|
||||
interface: UsbInterfaceAccess,
|
||||
in_pipe: UsbBulkInPipeAccess,
|
||||
out_pipe: UsbBulkOutPipeAccess,
|
||||
) -> Result<Self, UsbError> {
|
||||
Ok(Self {
|
||||
device,
|
||||
interface,
|
||||
in_pipe,
|
||||
out_pipe,
|
||||
last_tag: 0,
|
||||
@@ -172,60 +177,49 @@ impl ScsiTransport for Bbb {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbDeviceDetachHandler for DetachHandler {
|
||||
fn handle_device_detach(&self) {
|
||||
log::info!("Mass storage detached");
|
||||
self.0.detach();
|
||||
async fn handle_device_detach(&self) {
|
||||
self.0.detach().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Pod, Zeroable, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct BulkOnlyMassStorageReset;
|
||||
|
||||
#[derive(Debug, Pod, Zeroable, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct GetMaxLun;
|
||||
|
||||
impl UsbClassSpecificRequest for BulkOnlyMassStorageReset {
|
||||
const BM_REQUEST_TYPE: u8 = 0b00100001;
|
||||
const B_REQUEST: u8 = 0b11111111;
|
||||
}
|
||||
|
||||
impl UsbClassSpecificRequest for GetMaxLun {
|
||||
const BM_REQUEST_TYPE: u8 = 0b10100001;
|
||||
const B_REQUEST: u8 = 0b11111110;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbDriver for UsbMassStorageDriverBulkOnly {
|
||||
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);
|
||||
let control_pipe = device.control_pipe();
|
||||
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)
|
||||
impl UsbInterfaceDriver for UsbMassStorageDriverBulkOnly {
|
||||
async fn run(self: Arc<Self>, interface: UsbInterfaceAccess) -> Result<(), UsbError> {
|
||||
let endpoints = interface.endpoints();
|
||||
let bulk_in = endpoints
|
||||
.iter()
|
||||
.find(|ep| ep.ty == UsbEndpointType::Bulk && ep.direction == UsbDirection::In);
|
||||
let bulk_out = endpoints
|
||||
.iter()
|
||||
.find(|ep| ep.ty == UsbEndpointType::Bulk && ep.direction == UsbDirection::Out);
|
||||
|
||||
let (Some(bulk_in), Some(bulk_out)) = (bulk_in, bulk_out) else {
|
||||
log::warn!(
|
||||
"{}: BBB mass storage needs at least 2 bulk endpoints",
|
||||
interface.address()
|
||||
);
|
||||
return Err(UsbError::InvalidConfiguration);
|
||||
};
|
||||
|
||||
let control_pipe = interface.device().control_pipe();
|
||||
let in_pipe = interface
|
||||
.device()
|
||||
.open_bulk_in_pipe(bulk_in.number, bulk_in.max_packet_size as _)
|
||||
.await?;
|
||||
let out_pipe = device
|
||||
.open_bulk_out_pipe(out_index, out_info.max_packet_size as u16)
|
||||
let out_pipe = interface
|
||||
.device()
|
||||
.open_bulk_out_pipe(bulk_out.number, bulk_out.max_packet_size as _)
|
||||
.await?;
|
||||
|
||||
// Perform a Bulk-Only Mass Storage Reset
|
||||
// TODO interface id?
|
||||
control_pipe
|
||||
.control_transfer(ControlTransferSetup {
|
||||
bm_request_type: BulkOnlyMassStorageReset::BM_REQUEST_TYPE,
|
||||
b_request: BulkOnlyMassStorageReset::B_REQUEST,
|
||||
bm_request_type: BM_REQUEST_TYPE_BULK_ONLY_MASS_STORAGE_RESET,
|
||||
b_request: B_REQUEST_BULK_ONLY_MASS_STORAGE_RESET,
|
||||
w_value: 0,
|
||||
w_index: 0,
|
||||
w_index: interface.number() as _,
|
||||
w_length: 0,
|
||||
})
|
||||
.await?;
|
||||
@@ -236,10 +230,10 @@ impl UsbDriver for UsbMassStorageDriverBulkOnly {
|
||||
let len = control_pipe
|
||||
.control_transfer_in(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: GetMaxLun::BM_REQUEST_TYPE,
|
||||
b_request: GetMaxLun::B_REQUEST,
|
||||
bm_request_type: BM_REQUEST_TYPE_GET_MAX_LUN,
|
||||
b_request: B_REQUEST_GET_MAX_LUN,
|
||||
w_value: 0,
|
||||
w_index: 0,
|
||||
w_index: interface.number() as _,
|
||||
w_length: 1,
|
||||
},
|
||||
&mut buffer,
|
||||
@@ -251,13 +245,13 @@ impl UsbDriver for UsbMassStorageDriverBulkOnly {
|
||||
unsafe { buffer[0].assume_init() }
|
||||
};
|
||||
|
||||
let bbb = Bbb::new(device.clone(), in_pipe, out_pipe)?;
|
||||
let bbb = Bbb::new(interface.clone(), in_pipe, out_pipe)?;
|
||||
let scsi = ScsiEnclosure::setup(Box::new(bbb), max_lun as usize + 1)
|
||||
.await
|
||||
.inspect_err(|error| log::error!("msc: scsi error {error:?}"))
|
||||
.map_err(|_| UsbError::DriverError)?;
|
||||
let detach = DetachHandler(scsi.clone());
|
||||
device.set_detach_handler(Arc::new(detach));
|
||||
interface.device().set_detach_handler(Arc::new(detach));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -266,8 +260,9 @@ impl UsbDriver for UsbMassStorageDriverBulkOnly {
|
||||
"USB Mass Storage"
|
||||
}
|
||||
|
||||
fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
|
||||
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool {
|
||||
// TODO support other protocols
|
||||
class.class == UsbDeviceClass::MassStorage && class.interface_protocol_number == 0x50
|
||||
let _ = subclass;
|
||||
class == 0x08 && protocol == 0x50
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,117 +1,141 @@
|
||||
// TODO Split drivers into device and interface drivers
|
||||
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use async_trait::async_trait;
|
||||
use libk::task::runtime;
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
|
||||
use crate::{
|
||||
device::UsbDeviceAccess,
|
||||
device::{UsbDeviceAccess, UsbInterfaceAccess},
|
||||
error::UsbError,
|
||||
info::{UsbDeviceClass, UsbDeviceProtocol},
|
||||
};
|
||||
|
||||
pub mod hid_keyboard;
|
||||
pub mod mass_storage;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UsbClassInfo {
|
||||
pub class: UsbDeviceClass,
|
||||
pub subclass: u8,
|
||||
pub protocol: UsbDeviceProtocol,
|
||||
pub device_protocol_number: u8,
|
||||
pub interface_protocol_number: u8,
|
||||
}
|
||||
mod hid;
|
||||
mod hub;
|
||||
mod mass_storage;
|
||||
|
||||
#[async_trait]
|
||||
pub trait UsbDriver: Send + Sync {
|
||||
pub trait UsbDeviceDriver: Send + Sync {
|
||||
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError>;
|
||||
|
||||
fn name(&self) -> &'static str;
|
||||
fn probe(&self, class: &UsbClassInfo, device: &UsbDeviceAccess) -> bool;
|
||||
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool;
|
||||
}
|
||||
|
||||
async fn extract_class_info(device: &UsbDeviceAccess) -> Result<Option<UsbClassInfo>, UsbError> {
|
||||
if device.info.num_configurations != 1 {
|
||||
return Ok(None);
|
||||
}
|
||||
let device_info = &device.info;
|
||||
let config_info = device.query_configuration_info(0).await?;
|
||||
#[async_trait]
|
||||
pub trait UsbInterfaceDriver: Send + Sync {
|
||||
async fn run(self: Arc<Self>, interface: UsbInterfaceAccess) -> Result<(), UsbError>;
|
||||
|
||||
if !config_info.interfaces.is_empty() {
|
||||
let if_info = &config_info.interfaces[0];
|
||||
|
||||
let class = if device_info.device_class == UsbDeviceClass::FromInterface {
|
||||
if_info.interface_class
|
||||
} else {
|
||||
device_info.device_class
|
||||
};
|
||||
let subclass = if device_info.device_subclass == 0 {
|
||||
if_info.interface_subclass
|
||||
} else {
|
||||
device_info.device_subclass
|
||||
};
|
||||
let protocol = if device_info.device_protocol == UsbDeviceProtocol::FromInterface {
|
||||
if_info.interface_protocol
|
||||
} else {
|
||||
device_info.device_protocol
|
||||
};
|
||||
|
||||
Ok(Some(UsbClassInfo {
|
||||
class,
|
||||
subclass,
|
||||
protocol,
|
||||
interface_protocol_number: if_info.interface_protocol_number,
|
||||
device_protocol_number: device_info.device_protocol_number,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
fn name(&self) -> &'static str;
|
||||
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool;
|
||||
}
|
||||
|
||||
async fn pick_driver(
|
||||
device: &UsbDeviceAccess,
|
||||
) -> Result<Option<Arc<dyn UsbDriver + 'static>>, UsbError> {
|
||||
let Some(class) = extract_class_info(device).await? else {
|
||||
return Ok(None);
|
||||
async fn spawn_device_driver(device: Arc<UsbDeviceAccess>) -> Result<bool, UsbError> {
|
||||
let class = device.device_descriptor.device_class;
|
||||
let subclass = device.device_descriptor.device_subclass;
|
||||
let protocol = device.device_descriptor.device_protocol;
|
||||
let Some(driver) = USB_DEVICE_DRIVERS.read().iter().find_map(|driver| {
|
||||
driver
|
||||
.probe(class, subclass, protocol)
|
||||
.then(|| driver.clone())
|
||||
}) else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
for driver in USB_DEVICE_DRIVERS.read().iter() {
|
||||
if driver.probe(&class, device) {
|
||||
return Ok(Some(driver.clone()));
|
||||
// if let Some(driver) = pick_driver(&device)? {
|
||||
runtime::spawn(async move {
|
||||
let name = driver.name();
|
||||
match driver.run(device).await {
|
||||
e @ Err(UsbError::DeviceDisconnected) => {
|
||||
log::warn!(
|
||||
"Driver {:?} did not exit cleanly: device disconnected",
|
||||
name,
|
||||
);
|
||||
|
||||
e
|
||||
}
|
||||
e => e,
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
})
|
||||
.map_err(UsbError::SystemError)?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub async fn spawn_driver(device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
|
||||
if let Some(driver) = pick_driver(&device).await? {
|
||||
runtime::spawn(async move {
|
||||
let name = driver.name();
|
||||
match driver.run(device).await {
|
||||
e @ Err(UsbError::DeviceDisconnected) => {
|
||||
log::warn!(
|
||||
"Driver {:?} did not exit cleanly: device disconnected",
|
||||
name,
|
||||
);
|
||||
async fn spawn_interface_driver(interface: UsbInterfaceAccess) -> Result<(), UsbError> {
|
||||
let (class, subclass, protocol) = interface.class();
|
||||
let Some(driver) = USB_INTERFACE_DRIVERS.read().iter().find_map(|driver| {
|
||||
driver
|
||||
.probe(class, subclass, protocol)
|
||||
.then(|| driver.clone())
|
||||
}) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
e
|
||||
}
|
||||
e => e,
|
||||
runtime::spawn(async move {
|
||||
let name = driver.name();
|
||||
match driver.run(interface).await {
|
||||
e @ Err(UsbError::DeviceDisconnected) => {
|
||||
log::warn!(
|
||||
"Driver {:?} did not exit cleanly: device disconnected",
|
||||
name,
|
||||
);
|
||||
|
||||
e
|
||||
}
|
||||
})
|
||||
.map_err(UsbError::SystemError)?;
|
||||
}
|
||||
e => e,
|
||||
}
|
||||
})
|
||||
.map_err(UsbError::SystemError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn register_driver(driver: Arc<dyn UsbDriver + 'static>) {
|
||||
pub async fn spawn_driver(device: Arc<UsbDeviceAccess>) {
|
||||
match spawn_device_driver(device.clone()).await {
|
||||
Ok(true) => return,
|
||||
Ok(false) => (),
|
||||
Err(error) => {
|
||||
log::error!(
|
||||
"{}: device driver probe failed: {:?}",
|
||||
device.bus_address(),
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
// Enumerate interfaces
|
||||
for index in 0..device.configuration0_info.interfaces.len() as u8 {
|
||||
let access = device.interface(index);
|
||||
if let Err(error) = spawn_interface_driver(access).await {
|
||||
log::error!(
|
||||
"{}: interface {} driver probe failed: {:?}",
|
||||
device.bus_address(),
|
||||
index,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_device_driver(driver: Arc<dyn UsbDeviceDriver + 'static>) {
|
||||
// TODO check for duplicates
|
||||
USB_DEVICE_DRIVERS.write().push(driver);
|
||||
}
|
||||
|
||||
pub fn register_default_class_drivers() {
|
||||
register_driver(Arc::new(hid_keyboard::UsbHidKeyboardDriver));
|
||||
register_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
|
||||
pub fn register_interface_driver(driver: Arc<dyn UsbInterfaceDriver + 'static>) {
|
||||
USB_INTERFACE_DRIVERS.write().push(driver);
|
||||
}
|
||||
|
||||
static USB_DEVICE_DRIVERS: IrqSafeRwLock<Vec<Arc<dyn UsbDriver + 'static>>> =
|
||||
pub fn register_default_class_drivers() {
|
||||
register_device_driver(Arc::new(hub::UsbHubDriver));
|
||||
register_interface_driver(Arc::new(hid::UsbHidKeyboardDriver));
|
||||
register_interface_driver(Arc::new(hid::UsbHidMouseDriver));
|
||||
register_interface_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
|
||||
// register_driver(Arc::new(hub::UsbHubDriver));
|
||||
// register_driver(Arc::new(hid_keyboard::UsbHidKeyboardDriver));
|
||||
// register_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
|
||||
}
|
||||
|
||||
static USB_DEVICE_DRIVERS: IrqSafeRwLock<Vec<Arc<dyn UsbDeviceDriver + 'static>>> =
|
||||
IrqSafeRwLock::new(Vec::new());
|
||||
static USB_INTERFACE_DRIVERS: IrqSafeRwLock<Vec<Arc<dyn UsbInterfaceDriver + 'static>>> =
|
||||
IrqSafeRwLock::new(Vec::new());
|
||||
|
||||
@@ -4,9 +4,22 @@ use crate::{
|
||||
communication::UsbDirection,
|
||||
device::UsbSpeed,
|
||||
error::UsbError,
|
||||
info::{UsbDeviceClass, UsbDeviceProtocol, UsbEndpointType, UsbVersion},
|
||||
info::{UsbEndpointType, UsbVersion},
|
||||
// info::{UsbDeviceClass, UsbDeviceProtocol, UsbEndpointType, UsbVersion},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
|
||||
#[repr(C, packed)]
|
||||
pub struct UsbDeviceDescriptor0 {
|
||||
pub length: u8,
|
||||
pub ty: u8,
|
||||
pub bcd_usb: u16,
|
||||
pub device_class: u8,
|
||||
pub device_subclass: u8,
|
||||
pub device_protocol: u8,
|
||||
pub max_packet_size_0: u8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
|
||||
#[repr(C, packed)]
|
||||
pub struct UsbDeviceDescriptor {
|
||||
@@ -91,16 +104,27 @@ pub struct UsbOtherSpeedConfiguration {
|
||||
pub max_power: u8,
|
||||
}
|
||||
|
||||
impl UsbInterfaceDescriptor {
|
||||
pub fn class(&self) -> UsbDeviceClass {
|
||||
UsbDeviceClass::try_from(self.interface_class).unwrap_or(UsbDeviceClass::Unknown)
|
||||
}
|
||||
|
||||
pub fn protocol(&self) -> UsbDeviceProtocol {
|
||||
UsbDeviceProtocol::try_from(self.interface_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
|
||||
#[repr(C, packed)]
|
||||
pub struct UsbHubDescriptorHeader {
|
||||
pub b_desc_length: u8,
|
||||
pub b_descriptor_type: u8,
|
||||
pub b_nr_ports: u8,
|
||||
pub w_hub_characteristics: u16,
|
||||
pub b_pwr_on_2_pwr_good: u8,
|
||||
pub b_hub_contr_current: u8,
|
||||
}
|
||||
|
||||
// impl UsbInterfaceDescriptor {
|
||||
// pub fn class(&self) -> UsbDeviceClass {
|
||||
// UsbDeviceClass::try_from(self.interface_class).unwrap_or(UsbDeviceClass::Unknown)
|
||||
// }
|
||||
//
|
||||
// pub fn protocol(&self) -> UsbDeviceProtocol {
|
||||
// UsbDeviceProtocol::try_from(self.interface_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
impl UsbEndpointDescriptor {
|
||||
pub fn direction(&self) -> UsbDirection {
|
||||
match self.endpoint_address >> 7 {
|
||||
@@ -125,16 +149,16 @@ impl UsbEndpointDescriptor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
impl UsbDeviceDescriptor {
|
||||
pub fn class(&self) -> UsbDeviceClass {
|
||||
UsbDeviceClass::try_from(self.device_class).unwrap_or(UsbDeviceClass::Unknown)
|
||||
}
|
||||
|
||||
pub fn protocol(&self) -> UsbDeviceProtocol {
|
||||
UsbDeviceProtocol::try_from(self.device_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
|
||||
}
|
||||
|
||||
// pub fn class(&self) -> UsbDeviceClass {
|
||||
// UsbDeviceClass::try_from(self.device_class).unwrap_or(UsbDeviceClass::Unknown)
|
||||
// }
|
||||
//
|
||||
// pub fn protocol(&self) -> UsbDeviceProtocol {
|
||||
// UsbDeviceProtocol::try_from(self.device_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
|
||||
// }
|
||||
//
|
||||
pub fn max_packet_size(&self, version: UsbVersion, speed: UsbSpeed) -> Result<usize, UsbError> {
|
||||
match (version.is_version_3(), speed, self.max_packet_size_0) {
|
||||
(true, UsbSpeed::Super, 9) => Ok(1 << 9),
|
||||
|
||||
+282
-127
@@ -1,16 +1,27 @@
|
||||
use core::{fmt, ops::Deref};
|
||||
use core::{any::Any, fmt, ops::Deref};
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
string::String,
|
||||
sync::{Arc, Weak},
|
||||
vec::Vec,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use libk_util::sync::spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard};
|
||||
use libk::{
|
||||
error::Error,
|
||||
fs::sysfs::{
|
||||
self,
|
||||
attribute::{StringAttribute, StringAttributeOps},
|
||||
object::KObject,
|
||||
},
|
||||
};
|
||||
use libk_util::OneTimeInit;
|
||||
|
||||
use crate::{
|
||||
UsbHostController,
|
||||
descriptor::{UsbDeviceDescriptor, UsbHubDescriptorHeader},
|
||||
error::UsbError,
|
||||
info::{
|
||||
UsbConfigurationInfo, UsbDeviceInfo, UsbEndpointInfo, UsbEndpointType, UsbInterfaceInfo,
|
||||
UsbVersion,
|
||||
},
|
||||
host::UsbHostController,
|
||||
info::{PortString, UsbConfigurationInfo, UsbEndpointInfo, UsbEndpointType, UsbInterfaceInfo},
|
||||
pipe::{
|
||||
control::{ConfigurationDescriptorEntry, UsbControlPipeAccess},
|
||||
normal::{
|
||||
@@ -21,6 +32,7 @@ use crate::{
|
||||
};
|
||||
|
||||
// High-level structures for info provided through descriptors
|
||||
type UsbDeviceKObject = KObject<Weak<UsbDeviceAccess>>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub struct UsbBusAddress {
|
||||
@@ -28,10 +40,28 @@ pub struct UsbBusAddress {
|
||||
pub device: u8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub struct UsbInterfaceAddress {
|
||||
pub bus: UsbBusAddress,
|
||||
pub interface: u8,
|
||||
}
|
||||
|
||||
pub struct UsbDeviceAccess {
|
||||
pub device: Arc<dyn UsbDevice>,
|
||||
pub info: UsbDeviceInfo,
|
||||
pub current_configuration: IrqSafeRwLock<Option<UsbConfigurationInfo>>,
|
||||
pub device_descriptor: UsbDeviceDescriptor,
|
||||
pub product_str: Option<String>,
|
||||
pub vendor_str: Option<String>,
|
||||
pub configuration0_info: UsbConfigurationInfo,
|
||||
|
||||
kobject: OneTimeInit<Arc<UsbDeviceKObject>>,
|
||||
}
|
||||
|
||||
// USB device, limited in scope by one interface
|
||||
#[derive(Clone)]
|
||||
pub struct UsbInterfaceAccess {
|
||||
device: Arc<UsbDeviceAccess>,
|
||||
interface_index: u8,
|
||||
endpoint_start_index: u8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
@@ -42,8 +72,9 @@ pub enum UsbSpeed {
|
||||
Super,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait UsbDeviceDetachHandler: Send + Sync {
|
||||
fn handle_device_detach(&self);
|
||||
async fn handle_device_detach(&self);
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -66,66 +97,231 @@ pub trait UsbDevice: Send + Sync {
|
||||
ty: UsbEndpointType,
|
||||
) -> Result<Box<dyn UsbNormalPipeOut>, UsbError>;
|
||||
|
||||
fn port_number(&self) -> u8;
|
||||
async fn configure_hub(&self, hub_descriptor: &UsbHubDescriptorHeader) -> Result<(), UsbError>;
|
||||
|
||||
// fn port_number(&self) -> u8;
|
||||
fn port_string(&self) -> &PortString;
|
||||
fn bus_address(&self) -> UsbBusAddress;
|
||||
fn speed(&self) -> UsbSpeed;
|
||||
fn controller_ref(&self) -> &dyn UsbHostController;
|
||||
fn host_controller(&self) -> Arc<dyn UsbHostController>;
|
||||
fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
|
||||
|
||||
fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>);
|
||||
fn handle_detach(&self);
|
||||
async fn handle_detach(&self);
|
||||
|
||||
fn debug(&self) {}
|
||||
}
|
||||
|
||||
impl UsbDeviceAccess {
|
||||
fn usb_kobject() -> Option<&'static Arc<KObject<()>>> {
|
||||
static USB_KOBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||
|
||||
USB_KOBJECT.or_init_with_opt(|| {
|
||||
let bus_kobject = sysfs::bus()?;
|
||||
let usb_kobject = KObject::new(());
|
||||
bus_kobject.add_object("usb", usb_kobject.clone()).ok()?;
|
||||
Some(usb_kobject)
|
||||
})
|
||||
}
|
||||
|
||||
fn setup_kobject(self: &Arc<Self>) -> Result<(), Error> {
|
||||
struct Id;
|
||||
struct DeviceClass;
|
||||
struct VendorString;
|
||||
struct ProductString;
|
||||
|
||||
impl StringAttributeOps for Id {
|
||||
type Data = Weak<UsbDeviceAccess>;
|
||||
const NAME: &'static str = "id";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
let state = state.upgrade().ok_or(Error::InvalidOperation)?;
|
||||
let id_product = state.device_descriptor.id_product;
|
||||
let id_vendor = state.device_descriptor.id_vendor;
|
||||
Ok(alloc::format!("{id_vendor:04x}:{id_product:04x}",))
|
||||
}
|
||||
}
|
||||
|
||||
impl StringAttributeOps for DeviceClass {
|
||||
type Data = Weak<UsbDeviceAccess>;
|
||||
const NAME: &'static str = "device_class";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
let state = state.upgrade().ok_or(Error::InvalidOperation)?;
|
||||
Ok(alloc::format!(
|
||||
"{:02x}:{:02x}:{:02x}",
|
||||
state.device_descriptor.device_class,
|
||||
state.device_descriptor.device_subclass,
|
||||
state.device_descriptor.device_protocol
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl StringAttributeOps for VendorString {
|
||||
type Data = Weak<UsbDeviceAccess>;
|
||||
const NAME: &'static str = "vendor_str";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
let state = state.upgrade().ok_or(Error::InvalidOperation)?;
|
||||
if let Some(vendor_str) = state.vendor_str.as_ref() {
|
||||
Ok(vendor_str.clone())
|
||||
} else {
|
||||
Ok(String::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StringAttributeOps for ProductString {
|
||||
type Data = Weak<UsbDeviceAccess>;
|
||||
const NAME: &'static str = "product_str";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
let state = state.upgrade().ok_or(Error::InvalidOperation)?;
|
||||
if let Some(product_str) = state.product_str.as_ref() {
|
||||
Ok(product_str.clone())
|
||||
} else {
|
||||
Ok(String::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let usb_kobject = Self::usb_kobject().ok_or(Error::DoesNotExist)?;
|
||||
let dev_kobject = KObject::new(Arc::downgrade(self));
|
||||
dev_kobject.add_attribute(StringAttribute::from(Id)).ok();
|
||||
dev_kobject
|
||||
.add_attribute(StringAttribute::from(DeviceClass))
|
||||
.ok();
|
||||
dev_kobject
|
||||
.add_attribute(StringAttribute::from(ProductString))
|
||||
.ok();
|
||||
dev_kobject
|
||||
.add_attribute(StringAttribute::from(VendorString))
|
||||
.ok();
|
||||
let name = alloc::format!("{}", self.device.bus_address());
|
||||
usb_kobject.add_object(name, dev_kobject.clone())?;
|
||||
self.kobject.init(dev_kobject);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Expected device state:
|
||||
///
|
||||
/// * Link-layer stuff has been reset and established properly by the HCD
|
||||
/// * 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: Arc<dyn UsbDevice>) -> Result<Self, UsbError> {
|
||||
let control = raw.control_pipe();
|
||||
pub async fn setup(raw: Arc<dyn UsbDevice>) -> Result<Arc<Self>, UsbError> {
|
||||
let control_pipe = raw.control_pipe();
|
||||
let device_descriptor = control_pipe.query_device_descriptor().await?;
|
||||
if device_descriptor.num_configurations < 1 {
|
||||
return Err(UsbError::InvalidDescriptorField);
|
||||
}
|
||||
let config_descriptor = control_pipe.query_configuration_descriptor(0).await?;
|
||||
|
||||
let device_desc = control.query_device_descriptor().await?;
|
||||
// Use configuration 0
|
||||
control_pipe
|
||||
.set_configuration(config_descriptor.configuration().config_val as _)
|
||||
.await?;
|
||||
|
||||
let bcd_usb = device_desc.bcd_usb;
|
||||
let usb_version = UsbVersion::from_bcd_usb(device_desc.bcd_usb)
|
||||
.ok_or(UsbError::InvalidDescriptorField)
|
||||
.inspect_err(|_| {
|
||||
log::error!(
|
||||
"{}: unsupported/invalid USB version: {:#x}",
|
||||
let vendor_str = control_pipe
|
||||
.query_string(device_descriptor.manufacturer_str)
|
||||
.await
|
||||
.inspect_err(|e| {
|
||||
log::warn!(
|
||||
"{}: manufacturer string query error: {:?}",
|
||||
raw.bus_address(),
|
||||
bcd_usb
|
||||
e
|
||||
)
|
||||
})?;
|
||||
})
|
||||
.ok();
|
||||
let product_str = control_pipe
|
||||
.query_string(device_descriptor.product_str)
|
||||
.await
|
||||
.inspect_err(|e| {
|
||||
log::warn!("{}: product string query error: {:?}", raw.bus_address(), e)
|
||||
})
|
||||
.ok();
|
||||
|
||||
let manufacturer = control.query_string(device_desc.manufacturer_str).await?;
|
||||
let product = control.query_string(device_desc.product_str).await?;
|
||||
// Extract configuration 0 information
|
||||
// let query = control_pipe.query_configuration_descriptor(index).await?;
|
||||
|
||||
let info = UsbDeviceInfo {
|
||||
manufacturer,
|
||||
product,
|
||||
usb_version,
|
||||
let configuration_name = control_pipe
|
||||
.query_string(config_descriptor.configuration().config_str)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
id_vendor: device_desc.id_vendor,
|
||||
id_product: device_desc.id_product,
|
||||
let mut endpoints = Vec::new();
|
||||
let mut interfaces = Vec::new();
|
||||
|
||||
device_class: device_desc.class(),
|
||||
device_subclass: device_desc.device_subclass,
|
||||
device_protocol: device_desc.protocol(),
|
||||
device_protocol_number: device_desc.device_protocol,
|
||||
for desc in config_descriptor.descriptors() {
|
||||
match desc {
|
||||
ConfigurationDescriptorEntry::Interface(iface) => {
|
||||
let name = control_pipe.query_string(iface.interface_str).await.ok();
|
||||
interfaces.push(UsbInterfaceInfo {
|
||||
name,
|
||||
number: iface.interface_number,
|
||||
num_endpoints: iface.num_endpoints,
|
||||
|
||||
num_configurations: device_desc.num_configurations,
|
||||
interface_class: iface.interface_class,
|
||||
interface_subclass: iface.interface_subclass,
|
||||
interface_protocol: iface.interface_protocol,
|
||||
});
|
||||
}
|
||||
ConfigurationDescriptorEntry::Endpoint(ep) => {
|
||||
endpoints.push(UsbEndpointInfo {
|
||||
number: ep.number(),
|
||||
direction: ep.direction(),
|
||||
max_packet_size: ep.max_packet_size as _,
|
||||
ty: ep.transfer_type(),
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
max_packet_size: device_desc.max_packet_size(usb_version, raw.speed())?,
|
||||
interfaces.sort_by_key(|r| r.number);
|
||||
endpoints.sort_by_key(|r| r.number);
|
||||
|
||||
let configuration0_info = UsbConfigurationInfo {
|
||||
name: configuration_name,
|
||||
config_value: config_descriptor.configuration().config_val,
|
||||
interfaces,
|
||||
endpoints,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
let device = Arc::new(Self {
|
||||
device: raw,
|
||||
info,
|
||||
current_configuration: IrqSafeRwLock::new(None),
|
||||
})
|
||||
device_descriptor,
|
||||
product_str,
|
||||
vendor_str,
|
||||
configuration0_info,
|
||||
kobject: OneTimeInit::new(),
|
||||
});
|
||||
|
||||
if let Err(error) = device.setup_kobject() {
|
||||
log::error!(
|
||||
"{} kobject setup error: {:?}",
|
||||
device.device.bus_address(),
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
pub fn interface(self: &Arc<Self>, interface_index: u8) -> UsbInterfaceAccess {
|
||||
let endpoint_start_index = self
|
||||
.configuration0_info
|
||||
.interfaces
|
||||
.iter()
|
||||
.take(interface_index as usize)
|
||||
.map(|r| r.num_endpoints)
|
||||
.sum();
|
||||
|
||||
UsbInterfaceAccess {
|
||||
device: self.clone(),
|
||||
interface_index,
|
||||
endpoint_start_index,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn open_interrupt_in_pipe(
|
||||
@@ -164,90 +360,6 @@ impl UsbDeviceAccess {
|
||||
Ok(UsbBulkOutPipeAccess(pipe))
|
||||
}
|
||||
|
||||
pub fn read_current_configuration(
|
||||
&self,
|
||||
) -> IrqSafeRwLockReadGuard<'_, Option<UsbConfigurationInfo>> {
|
||||
self.current_configuration.read()
|
||||
}
|
||||
|
||||
pub async fn select_configuration<F: Fn(&UsbConfigurationInfo) -> bool>(
|
||||
&self,
|
||||
predicate: F,
|
||||
) -> Result<Option<UsbConfigurationInfo>, UsbError> {
|
||||
let mut current_config = self.current_configuration.write();
|
||||
let control_pipe = self.control_pipe();
|
||||
|
||||
for i in 0..self.info.num_configurations {
|
||||
let info = self.query_configuration_info(i).await?;
|
||||
|
||||
if predicate(&info) {
|
||||
log::debug!("Selected configuration: {:#?}", info);
|
||||
let config = current_config.insert(info);
|
||||
|
||||
control_pipe
|
||||
.set_configuration(config.config_value as _)
|
||||
.await?;
|
||||
|
||||
return Ok(Some(config.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub async fn query_configuration_info(
|
||||
&self,
|
||||
index: u8,
|
||||
) -> Result<UsbConfigurationInfo, UsbError> {
|
||||
if index >= self.info.num_configurations {
|
||||
return Err(UsbError::InvalidConfiguration);
|
||||
}
|
||||
let control_pipe = self.control_pipe();
|
||||
let query = control_pipe.query_configuration_descriptor(index).await?;
|
||||
|
||||
let configuration_name = control_pipe
|
||||
.query_string(query.configuration().config_str)
|
||||
.await?;
|
||||
|
||||
let mut endpoints = Vec::new();
|
||||
let mut interfaces = Vec::new();
|
||||
|
||||
for desc in query.descriptors() {
|
||||
match desc {
|
||||
ConfigurationDescriptorEntry::Endpoint(ep) => {
|
||||
endpoints.push(UsbEndpointInfo {
|
||||
number: ep.number(),
|
||||
direction: ep.direction(),
|
||||
max_packet_size: ep.max_packet_size as _,
|
||||
ty: ep.transfer_type(),
|
||||
});
|
||||
}
|
||||
ConfigurationDescriptorEntry::Interface(iface) => {
|
||||
let name = control_pipe.query_string(iface.interface_str).await?;
|
||||
interfaces.push(UsbInterfaceInfo {
|
||||
name,
|
||||
number: iface.interface_number,
|
||||
|
||||
interface_class: iface.class(),
|
||||
interface_subclass: iface.interface_subclass,
|
||||
interface_protocol: iface.protocol(),
|
||||
interface_protocol_number: iface.interface_protocol,
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
let info = UsbConfigurationInfo {
|
||||
name: configuration_name,
|
||||
config_value: query.configuration().config_val,
|
||||
interfaces,
|
||||
endpoints,
|
||||
};
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
pub fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>) {
|
||||
self.device.set_detach_handler(handler);
|
||||
}
|
||||
@@ -266,3 +378,46 @@ impl fmt::Display for UsbBusAddress {
|
||||
write!(f, "{}:{}", self.bus, self.device)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UsbInterfaceAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}.{}", self.bus, self.interface)
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbInterfaceAccess {
|
||||
fn interface_info(&self) -> &UsbInterfaceInfo {
|
||||
&self.device.configuration0_info.interfaces[self.interface_index as usize]
|
||||
}
|
||||
|
||||
pub fn number(&self) -> u8 {
|
||||
self.interface_index
|
||||
}
|
||||
|
||||
pub fn endpoints(&self) -> &[UsbEndpointInfo] {
|
||||
let info = self.interface_info();
|
||||
let start = self.endpoint_start_index as usize;
|
||||
let end = start + info.num_endpoints as usize;
|
||||
&self.device.configuration0_info.endpoints[start..end]
|
||||
}
|
||||
|
||||
pub fn device(&self) -> &Arc<UsbDeviceAccess> {
|
||||
&self.device
|
||||
}
|
||||
|
||||
pub fn address(&self) -> UsbInterfaceAddress {
|
||||
UsbInterfaceAddress {
|
||||
bus: self.device.bus_address(),
|
||||
interface: self.interface_index,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn class(&self) -> (u8, u8, u8) {
|
||||
let i = self.interface_info();
|
||||
(
|
||||
i.interface_class,
|
||||
i.interface_subclass,
|
||||
i.interface_protocol,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ pub enum TransferError {
|
||||
BufferError,
|
||||
UsbTransactionError,
|
||||
Stall,
|
||||
Shutdown,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
@@ -29,13 +30,17 @@ pub enum UsbError {
|
||||
DeviceBusy,
|
||||
DeviceDisconnected,
|
||||
TransferFailed(TransferError),
|
||||
TruncatedDescriptor(usize, usize),
|
||||
// Driver errors
|
||||
DriverError,
|
||||
}
|
||||
|
||||
impl From<TransferError> for UsbError {
|
||||
fn from(value: TransferError) -> Self {
|
||||
Self::TransferFailed(value)
|
||||
match value {
|
||||
TransferError::Shutdown => Self::DeviceDisconnected,
|
||||
value => Self::TransferFailed(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use device_api::device::Device;
|
||||
|
||||
use crate::{
|
||||
device::{UsbDeviceAccess, UsbSpeed},
|
||||
error::UsbError,
|
||||
info::PortString,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
pub trait UsbHostController: Device + Sync + Send {
|
||||
async fn setup_hub_device(
|
||||
self: Arc<Self>,
|
||||
hub: Arc<UsbDeviceAccess>,
|
||||
port_string: PortString,
|
||||
usb_speed: UsbSpeed,
|
||||
) -> Result<(), UsbError>;
|
||||
async fn disconnect_device(self: Arc<Self>, port_string: PortString) -> Result<(), UsbError>;
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
use core::fmt;
|
||||
use core::{fmt, num::NonZeroU8};
|
||||
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use yggdrasil_abi::primitive_enum;
|
||||
|
||||
use crate::communication::UsbDirection;
|
||||
|
||||
@@ -39,31 +38,21 @@ pub enum UsbVersion {
|
||||
Usb32,
|
||||
}
|
||||
|
||||
primitive_enum! {
|
||||
pub enum UsbDeviceClass: u8 {
|
||||
FromInterface = 0x00,
|
||||
Hid = 0x03,
|
||||
MassStorage = 0x08,
|
||||
Unknown = 0xFF,
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct PortString(u64);
|
||||
|
||||
primitive_enum! {
|
||||
pub enum UsbDeviceProtocol: u8 {
|
||||
FromInterface = 0x00,
|
||||
Unknown = 0xFF,
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PortStringIter(u64);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UsbInterfaceInfo {
|
||||
pub name: String,
|
||||
pub name: Option<String>,
|
||||
pub number: u8,
|
||||
pub num_endpoints: u8,
|
||||
|
||||
pub interface_class: UsbDeviceClass,
|
||||
pub interface_class: u8,
|
||||
pub interface_subclass: u8,
|
||||
pub interface_protocol: UsbDeviceProtocol,
|
||||
pub interface_protocol_number: u8,
|
||||
pub interface_protocol: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -76,33 +65,12 @@ pub struct UsbEndpointInfo {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UsbConfigurationInfo {
|
||||
pub name: String,
|
||||
pub name: Option<String>,
|
||||
pub config_value: u8,
|
||||
pub interfaces: Vec<UsbInterfaceInfo>,
|
||||
pub endpoints: Vec<UsbEndpointInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UsbDeviceInfo {
|
||||
pub manufacturer: String,
|
||||
pub product: String,
|
||||
|
||||
pub usb_version: UsbVersion,
|
||||
|
||||
pub id_vendor: u16,
|
||||
pub id_product: u16,
|
||||
|
||||
pub device_class: UsbDeviceClass,
|
||||
pub device_subclass: u8,
|
||||
pub device_protocol: UsbDeviceProtocol,
|
||||
pub device_protocol_number: u8,
|
||||
|
||||
/// Max packet size for endpoint zero
|
||||
pub max_packet_size: usize,
|
||||
|
||||
pub num_configurations: u8,
|
||||
}
|
||||
|
||||
impl UsbVersion {
|
||||
pub fn is_version_3(&self) -> bool {
|
||||
matches!(self, Self::Usb30 | Self::Usb31 | Self::Usb32)
|
||||
@@ -151,3 +119,59 @@ impl UsbConfigurationInfo {
|
||||
Some((index as u8 + 1, info))
|
||||
}
|
||||
}
|
||||
|
||||
impl PortString {
|
||||
pub const fn new_root_port(root_hub_port_number: NonZeroU8) -> Self {
|
||||
Self(root_hub_port_number.get() as u64)
|
||||
}
|
||||
|
||||
pub const fn root_hub_port_number(&self) -> NonZeroU8 {
|
||||
unsafe { NonZeroU8::new_unchecked((self.0 & 0xF) as u8) }
|
||||
}
|
||||
|
||||
pub fn append(mut self, port: NonZeroU8) -> Self {
|
||||
for i in 0..16 {
|
||||
let pos = i * 4;
|
||||
if (self.0 >> pos) & 0xF == 0 {
|
||||
self.0 |= (port.get() as u64) << pos;
|
||||
return self;
|
||||
}
|
||||
}
|
||||
panic!("Port route string too long");
|
||||
}
|
||||
|
||||
pub fn parent_hub_port_number(&self) -> Option<NonZeroU8> {
|
||||
self.iter().last()
|
||||
}
|
||||
|
||||
pub const fn raw(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> PortStringIter {
|
||||
PortStringIter(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PortString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "<port ")?;
|
||||
for (i, port) in self.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ".")?;
|
||||
}
|
||||
write!(f, "{port}")?;
|
||||
}
|
||||
write!(f, ">")
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for PortStringIter {
|
||||
type Item = NonZeroU8;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let port = NonZeroU8::new((self.0 & 0xF) as u8)?;
|
||||
self.0 >>= 4;
|
||||
Some(port)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
generic_const_exprs,
|
||||
iter_array_chunks,
|
||||
maybe_uninit_as_bytes,
|
||||
maybe_uninit_fill
|
||||
maybe_uninit_fill,
|
||||
maybe_uninit_array_assume_init
|
||||
)]
|
||||
|
||||
extern crate alloc;
|
||||
@@ -14,6 +15,7 @@ pub mod communication;
|
||||
pub mod descriptor;
|
||||
pub mod device;
|
||||
pub mod error;
|
||||
pub mod host;
|
||||
pub mod info;
|
||||
pub mod pipe;
|
||||
pub mod util;
|
||||
@@ -23,5 +25,3 @@ pub mod class_driver;
|
||||
// pub use communication::{UsbControlTransfer, UsbDirection, UsbTransferStatus, UsbTransferToken};
|
||||
|
||||
pub trait UsbEndpoint: Sync {}
|
||||
|
||||
pub trait UsbHostController: Sync + Send {}
|
||||
|
||||
@@ -10,8 +10,8 @@ use libk_mm::PageBox;
|
||||
|
||||
use crate::{
|
||||
descriptor::{
|
||||
UsbConfigurationDescriptor, UsbDeviceDescriptor, UsbDeviceQualifier, UsbEndpointDescriptor,
|
||||
UsbInterfaceDescriptor, UsbOtherSpeedConfiguration,
|
||||
UsbConfigurationDescriptor, UsbDeviceDescriptor, UsbDeviceDescriptor0, UsbDeviceQualifier,
|
||||
UsbEndpointDescriptor, UsbInterfaceDescriptor, UsbOtherSpeedConfiguration,
|
||||
},
|
||||
error::UsbError,
|
||||
};
|
||||
@@ -34,11 +34,6 @@ pub trait UsbDeviceRequest: Sized + Pod {
|
||||
const B_REQUEST: u8;
|
||||
}
|
||||
|
||||
pub trait UsbClassSpecificRequest: Sized + Pod {
|
||||
const BM_REQUEST_TYPE: u8;
|
||||
const B_REQUEST: u8;
|
||||
}
|
||||
|
||||
pub trait UsbDescriptorRequest: UsbDeviceRequest {
|
||||
const DESCRIPTOR_TYPE: u8;
|
||||
}
|
||||
@@ -177,6 +172,25 @@ impl ConfigurationDescriptorQuery {
|
||||
}
|
||||
|
||||
impl UsbControlPipeAccess {
|
||||
pub async fn query_device_descriptor_0(&self) -> Result<UsbDeviceDescriptor0, UsbError> {
|
||||
assert_eq!(size_of::<UsbDeviceDescriptor0>(), 8);
|
||||
let mut buffer = MaybeUninit::uninit();
|
||||
|
||||
self.control_transfer_in(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: 0b10000000,
|
||||
b_request: 0x06,
|
||||
w_value: 0x100,
|
||||
w_index: 0,
|
||||
w_length: size_of::<UsbDeviceDescriptor0>() as _,
|
||||
},
|
||||
buffer.as_bytes_mut(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(unsafe { buffer.assume_init() })
|
||||
}
|
||||
|
||||
pub async fn query_device_descriptor(&self) -> Result<UsbDeviceDescriptor, UsbError> {
|
||||
let mut buffer = MaybeUninit::uninit();
|
||||
|
||||
|
||||
@@ -9,14 +9,19 @@ use async_trait::async_trait;
|
||||
use device_api::device::Device;
|
||||
use libk::{device::char::CharDevice, vfs::FileReadiness};
|
||||
use libk_util::{OneTimeInit, ring::LossyRingQueue};
|
||||
use yggdrasil_abi::{error::Error, io::KeyboardKeyEvent};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{KeyboardKeyEvent, MouseEvent},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct KeyboardDevice;
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MouseDevice;
|
||||
|
||||
impl FileReadiness for KeyboardDevice {
|
||||
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
|
||||
INPUT_QUEUE.poll_readable(cx).map(Ok)
|
||||
KEYBOARD_INPUT_QUEUE.poll_readable(cx).map(Ok)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +38,7 @@ impl CharDevice for KeyboardDevice {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let ev = INPUT_QUEUE.read().await;
|
||||
let ev = KEYBOARD_INPUT_QUEUE.read().await;
|
||||
|
||||
buf[..4].copy_from_slice(&ev.as_bytes());
|
||||
|
||||
@@ -45,7 +50,7 @@ impl CharDevice for KeyboardDevice {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let ev = INPUT_QUEUE.try_read().ok_or(Error::WouldBlock)?;
|
||||
let ev = KEYBOARD_INPUT_QUEUE.try_read().ok_or(Error::WouldBlock)?;
|
||||
|
||||
buf[..4].copy_from_slice(&ev.as_bytes());
|
||||
|
||||
@@ -68,15 +73,84 @@ impl CharDevice for KeyboardDevice {
|
||||
}
|
||||
}
|
||||
|
||||
static INPUT_QUEUE: LossyRingQueue<KeyboardKeyEvent> = LossyRingQueue::with_capacity(32);
|
||||
static KEYBOARD_DEVICE: OneTimeInit<Arc<KeyboardDevice>> = OneTimeInit::new();
|
||||
impl MouseDevice {
|
||||
fn write_report(buf: &mut [u8], report: &MouseEvent) -> Result<usize, Error> {
|
||||
buf[0] = report.dx as u8;
|
||||
buf[1] = report.dy as u8;
|
||||
buf[2] = report.buttons;
|
||||
buf[3] = 0;
|
||||
|
||||
pub fn setup() -> Arc<KeyboardDevice> {
|
||||
Ok(4)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileReadiness for MouseDevice {
|
||||
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
|
||||
MOUSE_INPUT_QUEUE.poll_readable(cx).map(Ok)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for MouseDevice {
|
||||
fn display_name(&self) -> &str {
|
||||
"Mouse input pseudo-device"
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CharDevice for MouseDevice {
|
||||
async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
if buf.len() < 4 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let ev = MOUSE_INPUT_QUEUE.read().await;
|
||||
Self::write_report(buf, &ev)
|
||||
}
|
||||
|
||||
fn read_nonblocking(&self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
if buf.len() < 4 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let ev = MOUSE_INPUT_QUEUE.try_read().ok_or(Error::WouldBlock)?;
|
||||
Self::write_report(buf, &ev)
|
||||
}
|
||||
|
||||
fn is_writeable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
|
||||
let _ = option;
|
||||
let _ = buffer;
|
||||
let _ = len;
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
|
||||
fn is_terminal(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
static KEYBOARD_INPUT_QUEUE: LossyRingQueue<KeyboardKeyEvent> = LossyRingQueue::with_capacity(32);
|
||||
static MOUSE_INPUT_QUEUE: LossyRingQueue<MouseEvent> = LossyRingQueue::with_capacity(64);
|
||||
static KEYBOARD_DEVICE: OneTimeInit<Arc<KeyboardDevice>> = OneTimeInit::new();
|
||||
static MOUSE_DEVICE: OneTimeInit<Arc<MouseDevice>> = OneTimeInit::new();
|
||||
|
||||
pub fn setup_keyboard() -> Arc<KeyboardDevice> {
|
||||
KEYBOARD_DEVICE
|
||||
.or_init_with(|| Arc::new(KeyboardDevice))
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub fn send_event(ev: KeyboardKeyEvent) {
|
||||
INPUT_QUEUE.write(ev);
|
||||
pub fn setup_mouse() -> Arc<MouseDevice> {
|
||||
MOUSE_DEVICE.or_init_with(|| Arc::new(MouseDevice)).clone()
|
||||
}
|
||||
|
||||
pub fn send_mouse_event(ev: MouseEvent) {
|
||||
MOUSE_INPUT_QUEUE.write(ev);
|
||||
}
|
||||
|
||||
pub fn send_keyboard_event(ev: KeyboardKeyEvent) {
|
||||
KEYBOARD_INPUT_QUEUE.write(ev);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
use xhci_lib::context::{self, DeviceHandler, InputHandler};
|
||||
use ygg_driver_usb::error::UsbError;
|
||||
|
||||
use crate::regs::{ContextSize, PortNumber};
|
||||
use crate::regs::ContextSize;
|
||||
|
||||
pub enum XhciDeviceContext {
|
||||
Context32(IrqSafeRwLock<DmaBuffer<context::Device32Byte>>),
|
||||
@@ -61,46 +61,6 @@ impl XhciInputContext {
|
||||
.map_err(UsbError::MemoryError)
|
||||
}
|
||||
|
||||
pub fn new_address_device(
|
||||
dma: &dyn DmaAllocator,
|
||||
size: ContextSize,
|
||||
root_hub_port_number: PortNumber,
|
||||
max_packet_size: usize,
|
||||
speed: u8,
|
||||
dequeue_pointer: BusAddress,
|
||||
) -> Result<Self, UsbError> {
|
||||
let mut cx = Self::new(dma, size)?;
|
||||
|
||||
{
|
||||
let control = cx.control_mut();
|
||||
|
||||
control.set_add_context_flag(0); // Enable slot context
|
||||
control.set_add_context_flag(1); // Enable endpoint 0 context
|
||||
}
|
||||
|
||||
{
|
||||
let slot = cx.device_mut().slot_mut();
|
||||
|
||||
slot.set_context_entries(1);
|
||||
slot.set_interrupter_target(0);
|
||||
slot.set_root_hub_port_number(root_hub_port_number.into());
|
||||
slot.set_speed(speed);
|
||||
}
|
||||
|
||||
{
|
||||
let ep0 = cx.device_mut().endpoint_mut(1);
|
||||
|
||||
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 as u16);
|
||||
ep0.set_tr_dequeue_pointer(dequeue_pointer.into_u64());
|
||||
ep0.set_dequeue_cycle_state();
|
||||
}
|
||||
|
||||
Ok(cx)
|
||||
}
|
||||
|
||||
// pub fn physical_address(&self) -> PhysicalAddress {
|
||||
// match self {
|
||||
// Self::Context32(cx) => unsafe { cx.as_physical_address() },
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use core::{
|
||||
mem::MaybeUninit,
|
||||
sync::atomic::{AtomicU8, Ordering},
|
||||
};
|
||||
use core::{mem::MaybeUninit, sync::atomic::Ordering};
|
||||
|
||||
use alloc::{boxed::Box, collections::btree_map::BTreeMap, sync::Arc, vec::Vec};
|
||||
use async_trait::async_trait;
|
||||
@@ -17,19 +14,21 @@ use libk::{
|
||||
};
|
||||
use libk_util::{
|
||||
OneTimeInit,
|
||||
hash_table::DefaultHashTable,
|
||||
sync::{IrqSafeSpinlock, spin_rwlock::IrqSafeRwLock},
|
||||
};
|
||||
use tock_registers::{
|
||||
LocalRegisterCopy,
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
};
|
||||
use xhci_lib::context;
|
||||
use ygg_driver_pci::{PciConfigurationSpace, device::PciDeviceInfo};
|
||||
use ygg_driver_usb::{
|
||||
UsbHostController,
|
||||
bus::UsbBusManager,
|
||||
device::{UsbBusAddress, UsbDeviceAccess, UsbSpeed},
|
||||
error::UsbError,
|
||||
info::UsbVersion,
|
||||
host::UsbHostController,
|
||||
info::{PortString, UsbVersion},
|
||||
pipe::control::UsbControlPipeAccess,
|
||||
};
|
||||
use yggdrasil_abi::bitflags;
|
||||
@@ -87,7 +86,7 @@ pub struct Xhci {
|
||||
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>,
|
||||
port_slot_map: IrqSafeRwLock<DefaultHashTable<PortString, u8>>,
|
||||
bus_index: OneTimeInit<u16>,
|
||||
port_event_map: EventBitmap,
|
||||
}
|
||||
@@ -175,7 +174,6 @@ impl Xhci {
|
||||
space.write_u32(INTEL_XUSB2PR, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
let port_slot_map = (0..regs.port_count).map(|_| AtomicU8::new(0)).collect();
|
||||
let slots = (0..regs.slot_count)
|
||||
.map(|_| IrqSafeRwLock::new(None))
|
||||
.collect();
|
||||
@@ -196,7 +194,7 @@ impl Xhci {
|
||||
bus_index: OneTimeInit::new(),
|
||||
endpoints: IrqSafeRwLock::new(BTreeMap::new()),
|
||||
slots,
|
||||
port_slot_map,
|
||||
port_slot_map: IrqSafeRwLock::new(DefaultHashTable::new()),
|
||||
port_event_map: EventBitmap::new(),
|
||||
})
|
||||
}
|
||||
@@ -218,7 +216,7 @@ impl Xhci {
|
||||
|
||||
async fn allocate_device_slot(
|
||||
self: &Arc<Self>,
|
||||
port_id: PortNumber,
|
||||
port_string: PortString,
|
||||
slot_type: u8,
|
||||
speed: UsbSpeed,
|
||||
) -> Result<(u8, Arc<XhciBusDevice>, Arc<TransferRing>), UsbError> {
|
||||
@@ -233,7 +231,7 @@ impl Xhci {
|
||||
.insert((slot_id, 1), control_ring.clone());
|
||||
|
||||
let slot = Arc::new(XhciBusDevice {
|
||||
port_id,
|
||||
port_string,
|
||||
slot_id,
|
||||
device_context,
|
||||
control_pipe,
|
||||
@@ -245,7 +243,7 @@ impl Xhci {
|
||||
});
|
||||
|
||||
*self.slots[slot_id as usize].write() = Some(slot.clone());
|
||||
self.port_slot_map[port_id.index()].store(slot_id, Ordering::Release);
|
||||
self.port_slot_map.write().insert(port_string, slot_id);
|
||||
|
||||
Ok((slot_id, slot, control_ring))
|
||||
}
|
||||
@@ -271,12 +269,102 @@ impl Xhci {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn setup_device(
|
||||
self: &Arc<Self>,
|
||||
port_string: PortString,
|
||||
parent_hub_slot_id: Option<u8>,
|
||||
slot_type: u8,
|
||||
usb_speed: UsbSpeed,
|
||||
) -> Result<(), UsbError> {
|
||||
let xhci_route_string = port_string.raw() >> 4;
|
||||
let root_hub_port_number = port_string.root_hub_port_number();
|
||||
let parent_hub_port_number = port_string.parent_hub_port_number();
|
||||
|
||||
let (xhci_speed, max_packet_size_0) = match usb_speed {
|
||||
UsbSpeed::Low => (2, 8),
|
||||
UsbSpeed::Full => (1, 8),
|
||||
UsbSpeed::High => (3, 64),
|
||||
UsbSpeed::Super => (4, 512),
|
||||
};
|
||||
|
||||
let (slot_id, slot, control_ring) = self
|
||||
.allocate_device_slot(port_string, slot_type, usb_speed)
|
||||
.await?;
|
||||
let dequeue_pointer = control_ring.bus_address();
|
||||
|
||||
let mut input_cx = XhciInputContext::new(&*self.dma, self.regs.context_size)?;
|
||||
|
||||
{
|
||||
let control = input_cx.control_mut();
|
||||
|
||||
control.set_add_context_flag(0); // Enable slot context
|
||||
control.set_add_context_flag(1); // Enable endpoint 0 context
|
||||
}
|
||||
|
||||
{
|
||||
let slot = input_cx.device_mut().slot_mut();
|
||||
|
||||
slot.set_context_entries(1);
|
||||
slot.set_interrupter_target(0);
|
||||
slot.set_root_hub_port_number(root_hub_port_number.into());
|
||||
slot.set_speed(xhci_speed);
|
||||
slot.set_route_string(xhci_route_string as _);
|
||||
if let (Some(parent_hub_slot_id), Some(parent_hub_port_number)) =
|
||||
(parent_hub_slot_id, parent_hub_port_number)
|
||||
{
|
||||
slot.set_parent_hub_slot_id(parent_hub_slot_id);
|
||||
slot.set_parent_port_number(parent_hub_port_number.into());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let ep0 = input_cx.device_mut().endpoint_mut(1);
|
||||
|
||||
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_0);
|
||||
ep0.set_tr_dequeue_pointer(dequeue_pointer.into_u64());
|
||||
ep0.set_dequeue_cycle_state();
|
||||
}
|
||||
|
||||
self.command_ring
|
||||
.address_device(&**self, slot_id, &input_cx, false)
|
||||
.await
|
||||
.inspect_err(|error| {
|
||||
log::error!("Port {port_string} Address Device TRB (BSR=0) failed: {error:?}")
|
||||
})?;
|
||||
|
||||
let control_pipe = &slot.control_pipe;
|
||||
let device_descriptor = control_pipe.query_device_descriptor_0().await?;
|
||||
|
||||
if usb_speed == UsbSpeed::Full && device_descriptor.max_packet_size_0 != 8 {
|
||||
// Setup endpoint 0 max packet size
|
||||
slot.set_endpoint0_max_packet_size(device_descriptor.max_packet_size_0)
|
||||
.await?;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO clean up resources if init fails
|
||||
async fn setup_port(
|
||||
async fn setup_root_port(
|
||||
self: &Arc<Self>,
|
||||
regs: &PortRegs,
|
||||
number: PortNumber,
|
||||
) -> Result<(), UsbError> {
|
||||
let port_string = PortString::new_root_port(number.into());
|
||||
let root_hub_port = self.root_hub_ports[number.index()]
|
||||
.as_ref()
|
||||
.ok_or(UsbError::PortInitFailed)?;
|
||||
@@ -292,11 +380,11 @@ impl Xhci {
|
||||
let speed = status.read(PORTSC::PS) as u8;
|
||||
let pls = status.read(PORTSC::PLS);
|
||||
|
||||
let (usb_speed, max_packet_size) = match speed {
|
||||
1 => (UsbSpeed::Full, 8),
|
||||
2 => (UsbSpeed::Low, 8),
|
||||
3 => (UsbSpeed::High, 64),
|
||||
4 => (UsbSpeed::Super, 512),
|
||||
let usb_speed = match speed {
|
||||
1 => UsbSpeed::Full,
|
||||
2 => UsbSpeed::Low,
|
||||
3 => UsbSpeed::High,
|
||||
4 => UsbSpeed::Super,
|
||||
_ => {
|
||||
log::error!("Port {number} invalid speed value {speed}");
|
||||
return Err(UsbError::DeviceDisconnected);
|
||||
@@ -309,53 +397,20 @@ impl Xhci {
|
||||
return Err(UsbError::DeviceDisconnected);
|
||||
}
|
||||
|
||||
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.dma,
|
||||
self.regs.context_size,
|
||||
number,
|
||||
max_packet_size,
|
||||
speed,
|
||||
control_ring.bus_address(),
|
||||
)?;
|
||||
|
||||
self.command_ring
|
||||
.address_device(&**self, slot_id, &input_cx, false)
|
||||
self.setup_device(port_string, None, root_hub_port.slot_type, usb_speed)
|
||||
.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 handle_disconnect(&self, number: PortNumber) -> Result<(), UsbError> {
|
||||
let slot_id = self.port_slot_map[number.index()].swap(0, Ordering::Acquire);
|
||||
if slot_id == 0 {
|
||||
async fn handle_device_disconnect(&self, port_string: PortString) -> Result<(), UsbError> {
|
||||
log::info!("{port_string}: disconnect");
|
||||
let Some(slot_id) = self.port_slot_map.write().remove(&port_string) else {
|
||||
log::warn!("Disconnect on non-existent device: {port_string}");
|
||||
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());
|
||||
UsbBusManager::detach_device(*slot.bus_address.get()).await;
|
||||
|
||||
// Clean up xHC resources
|
||||
for (&endpoint_id, endpoint) in slot.endpoints.read().iter() {
|
||||
@@ -372,6 +427,11 @@ impl Xhci {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_root_port_disconnect(&self, number: PortNumber) -> Result<(), UsbError> {
|
||||
let port_string = PortString::new_root_port(number.into());
|
||||
self.handle_device_disconnect(port_string).await
|
||||
}
|
||||
|
||||
async fn port_handler_task(self: Arc<Self>) -> Result<(), Error> {
|
||||
// Inject notify for ports that are already connected
|
||||
{
|
||||
@@ -418,13 +478,13 @@ impl Xhci {
|
||||
if let Some(connected) = connected {
|
||||
if connected {
|
||||
log::info!("Port {number} connected");
|
||||
if let Err(error) = self.setup_port(regs, number).await {
|
||||
if let Err(error) = self.setup_root_port(regs, number).await {
|
||||
log::error!("Port {number} setup error: {error:?}");
|
||||
}
|
||||
}
|
||||
|
||||
if !connected {
|
||||
if let Err(error) = self.handle_disconnect(number).await {
|
||||
if let Err(error) = self.handle_root_port_disconnect(number).await {
|
||||
log::error!("Port {number} disconnect error: {error:?}");
|
||||
}
|
||||
}
|
||||
@@ -551,4 +611,21 @@ impl InterruptHandler for Xhci {
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbHostController for Xhci {}
|
||||
#[async_trait]
|
||||
impl UsbHostController for Xhci {
|
||||
async fn setup_hub_device(
|
||||
self: Arc<Self>,
|
||||
hub: Arc<UsbDeviceAccess>,
|
||||
port_string: PortString,
|
||||
usb_speed: UsbSpeed,
|
||||
) -> Result<(), UsbError> {
|
||||
let hub_device = hub.device.clone().as_any();
|
||||
let xhci_hub_device = Arc::downcast::<XhciBusDevice>(hub_device).unwrap();
|
||||
let parent_hub_slot_id = xhci_hub_device.slot_id;
|
||||
self.setup_device(port_string, Some(parent_hub_slot_id), 0, usb_speed)
|
||||
.await
|
||||
}
|
||||
async fn disconnect_device(self: Arc<Self>, port_string: PortString) -> Result<(), UsbError> {
|
||||
self.handle_device_disconnect(port_string).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use core::any::Any;
|
||||
|
||||
use alloc::{boxed::Box, collections::btree_map::BTreeMap, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use libk_util::{
|
||||
@@ -6,11 +8,12 @@ use libk_util::{
|
||||
};
|
||||
use xhci_lib::context;
|
||||
use ygg_driver_usb::{
|
||||
UsbHostController,
|
||||
communication::UsbDirection,
|
||||
descriptor::UsbHubDescriptorHeader,
|
||||
device::{UsbBusAddress, UsbDevice, UsbDeviceDetachHandler, UsbSpeed},
|
||||
error::UsbError,
|
||||
info::UsbEndpointType,
|
||||
host::UsbHostController,
|
||||
info::{PortString, UsbEndpointType},
|
||||
pipe::{
|
||||
control::UsbControlPipeAccess,
|
||||
normal::{UsbNormalPipeIn, UsbNormalPipeOut},
|
||||
@@ -21,12 +24,11 @@ use crate::{
|
||||
Xhci,
|
||||
context::{XhciDeviceContext, XhciInputContext},
|
||||
pipe::{NormalInPipe, NormalOutPipe},
|
||||
regs::PortNumber,
|
||||
ring::transfer::TransferRing,
|
||||
};
|
||||
|
||||
pub struct XhciBusDevice {
|
||||
pub(crate) port_id: PortNumber,
|
||||
pub(crate) port_string: PortString,
|
||||
pub(crate) slot_id: u8,
|
||||
pub(crate) speed: UsbSpeed,
|
||||
pub(crate) bus_address: OneTimeInit<UsbBusAddress>,
|
||||
@@ -39,10 +41,27 @@ pub struct XhciBusDevice {
|
||||
pub(crate) detach_handler: IrqSafeSpinlock<Option<Arc<dyn UsbDeviceDetachHandler>>>,
|
||||
}
|
||||
|
||||
impl XhciBusDevice {
|
||||
pub(crate) async fn set_endpoint0_max_packet_size(
|
||||
&self,
|
||||
max_packet_size_0: u8,
|
||||
) -> Result<(), UsbError> {
|
||||
log::info!(
|
||||
"{}: set ep0 max packet size = {max_packet_size_0}",
|
||||
self.port_string
|
||||
);
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbDevice for XhciBusDevice {
|
||||
fn port_number(&self) -> u8 {
|
||||
self.port_id.into()
|
||||
fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
|
||||
self
|
||||
}
|
||||
|
||||
fn port_string(&self) -> &PortString {
|
||||
&self.port_string
|
||||
}
|
||||
|
||||
fn bus_address(&self) -> UsbBusAddress {
|
||||
@@ -53,9 +72,9 @@ impl UsbDevice for XhciBusDevice {
|
||||
&self.control_pipe
|
||||
}
|
||||
|
||||
fn handle_detach(&self) {
|
||||
async fn handle_detach(&self) {
|
||||
if let Some(handler) = self.detach_handler.lock().as_ref() {
|
||||
handler.handle_device_detach();
|
||||
handler.handle_device_detach().await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +82,8 @@ impl UsbDevice for XhciBusDevice {
|
||||
*self.detach_handler.lock() = Some(handler);
|
||||
}
|
||||
|
||||
fn controller_ref(&self) -> &dyn UsbHostController {
|
||||
self.xhci.as_ref()
|
||||
fn host_controller(&self) -> Arc<dyn UsbHostController> {
|
||||
self.xhci.clone()
|
||||
}
|
||||
|
||||
fn debug(&self) {}
|
||||
@@ -98,6 +117,31 @@ impl UsbDevice for XhciBusDevice {
|
||||
.await?;
|
||||
Ok(Box::new(pipe))
|
||||
}
|
||||
|
||||
async fn configure_hub(&self, hub_descriptor: &UsbHubDescriptorHeader) -> Result<(), UsbError> {
|
||||
let mut input_cx = XhciInputContext::new(&*self.xhci.dma, self.xhci.regs.context_size)?;
|
||||
|
||||
{
|
||||
let control = input_cx.control_mut();
|
||||
|
||||
control.set_add_context_flag(0);
|
||||
}
|
||||
{
|
||||
let slot = input_cx.device_mut().slot_mut();
|
||||
|
||||
// TODO TT Think Time
|
||||
// TODO Multi-TT
|
||||
slot.set_hub();
|
||||
slot.set_number_of_ports(hub_descriptor.b_nr_ports);
|
||||
}
|
||||
|
||||
self.xhci
|
||||
.command_ring
|
||||
.evaluate_context(self.xhci.as_ref(), self.slot_id, &input_cx)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl XhciBusDevice {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use core::{fmt, sync::atomic::AtomicU32};
|
||||
use core::{fmt, num::NonZeroU8, sync::atomic::AtomicU32};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use capability::{CapabilityRegs, HCCPARAMS1, HCSPARAMS1, HCSPARAMS2};
|
||||
@@ -41,7 +41,7 @@ pub enum ContextSize {
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct PortNumber(u8);
|
||||
pub struct PortNumber(NonZeroU8);
|
||||
|
||||
impl Regs {
|
||||
const PORT_REGS_OFFSET: usize = 0x400;
|
||||
@@ -162,6 +162,12 @@ pub fn portsc_to_neutral(
|
||||
}
|
||||
|
||||
impl From<PortNumber> for u8 {
|
||||
fn from(value: PortNumber) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PortNumber> for NonZeroU8 {
|
||||
fn from(value: PortNumber) -> Self {
|
||||
value.0
|
||||
}
|
||||
@@ -169,11 +175,11 @@ impl From<PortNumber> for u8 {
|
||||
|
||||
impl PortNumber {
|
||||
pub fn from_index(index: usize) -> Self {
|
||||
Self(index as u8 + 1)
|
||||
Self(NonZeroU8::new(index as u8 + 1).unwrap())
|
||||
}
|
||||
|
||||
pub fn index(&self) -> usize {
|
||||
self.0 as usize - 1
|
||||
u8::from(self.0) as usize - 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -122,6 +122,20 @@ impl CommandRing {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn evaluate_context<E: CommandExecutor>(
|
||||
&self,
|
||||
executor: &E,
|
||||
slot_id: u8,
|
||||
input_context: &XhciInputContext,
|
||||
) -> Result<(), UsbError> {
|
||||
self.submit_and_wait(
|
||||
executor,
|
||||
EvaluateContextTrb::new(input_context.bus_address(), slot_id),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn configure_endpoint<E: CommandExecutor>(
|
||||
&self,
|
||||
executor: &E,
|
||||
@@ -249,6 +263,12 @@ define_bitfields! {
|
||||
}
|
||||
}
|
||||
|
||||
define_bitfields! {
|
||||
pub EvaluateContextCommandFlags: u32 {
|
||||
(24..32) => slot_id
|
||||
}
|
||||
}
|
||||
|
||||
define_bitfields! {
|
||||
pub ConfigureEndpointCommandFlags : u32 {
|
||||
(24..32) => slot_id
|
||||
@@ -299,6 +319,14 @@ pub struct AddressDeviceCommandTrb {
|
||||
pub flags: AddressDeviceCommandFlags,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
#[repr(C, align(16))]
|
||||
pub struct EvaluateContextTrb {
|
||||
pub input_context_address: BusAddress,
|
||||
_0: u32,
|
||||
pub flags: EvaluateContextCommandFlags,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
#[repr(C, align(16))]
|
||||
pub struct ConfigureEndpointCommandTrb {
|
||||
@@ -368,6 +396,16 @@ impl AddressDeviceCommandTrb {
|
||||
}
|
||||
}
|
||||
|
||||
impl EvaluateContextTrb {
|
||||
pub fn new(input_context_address: BusAddress, slot_id: u8) -> Self {
|
||||
Self {
|
||||
input_context_address,
|
||||
_0: 0,
|
||||
flags: EvaluateContextCommandFlags::new(slot_id as _),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigureEndpointCommandTrb {
|
||||
pub fn new(input_context_address: BusAddress, slot_id: u8) -> Self {
|
||||
Self {
|
||||
@@ -422,6 +460,10 @@ impl CommandTrb for AddressDeviceCommandTrb {
|
||||
const TRB_TYPE: u8 = 11;
|
||||
}
|
||||
|
||||
impl CommandTrb for EvaluateContextTrb {
|
||||
const TRB_TYPE: u8 = 13;
|
||||
}
|
||||
|
||||
impl CommandTrb for ConfigureEndpointCommandTrb {
|
||||
const TRB_TYPE: u8 = 12;
|
||||
}
|
||||
|
||||
@@ -420,7 +420,7 @@ impl Transaction {
|
||||
}
|
||||
TransactionEvent::Shutdown => {
|
||||
log::error!("xhci: abort transaction, endpoint shutdown");
|
||||
return Err(TransferError::UsbTransactionError);
|
||||
return Err(TransferError::Shutdown);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -436,7 +436,7 @@ impl Transaction {
|
||||
}
|
||||
TransactionEvent::Shutdown => {
|
||||
log::error!("xhci: abort transaction, endpoint shutdown");
|
||||
return Err(TransferError::UsbTransactionError);
|
||||
return Err(TransferError::Shutdown);
|
||||
}
|
||||
}
|
||||
event.to_result()
|
||||
@@ -481,7 +481,7 @@ impl TransactionEvent {
|
||||
13 => Err(TransferError::ShortPacket((status as usize) & 0xFFFFFF)),
|
||||
code => Err(TransferError::Other(code as u8)),
|
||||
},
|
||||
Self::Shutdown => Err(TransferError::UsbTransactionError),
|
||||
Self::Shutdown => Err(TransferError::Shutdown),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,11 @@ use alloc::vec::Vec;
|
||||
pub type DefaultHashBuilder = core::hash::BuildHasherDefault<ahash::AHasher>;
|
||||
pub type DefaultHashTable<K, V, const N: usize = 32> = HashTable<K, V, DefaultHashBuilder, N>;
|
||||
|
||||
pub struct DrainIter<'a, K, V, H, const N: usize> {
|
||||
hash_table: &'a mut HashTable<K, V, H, N>,
|
||||
bucket_index: usize,
|
||||
}
|
||||
|
||||
pub struct HashTable<K, V, H = DefaultHashBuilder, const N: usize = 32>
|
||||
where
|
||||
[Vec<(K, V)>; N]: Sized,
|
||||
@@ -34,6 +39,13 @@ impl<K, V, H, const N: usize> HashTable<K, V, H, N> {
|
||||
bucket.clear();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drain(&mut self) -> DrainIter<'_, K, V, H, N> {
|
||||
DrainIter {
|
||||
hash_table: self,
|
||||
bucket_index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V, const N: usize> HashTable<K, V, DefaultHashBuilder, N> {
|
||||
@@ -99,3 +111,18 @@ impl<K: Hash + Eq, V, const N: usize> HashTable<K, V, DefaultHashBuilder, N> {
|
||||
bucket.iter().any(|(k, _)| k.borrow() == key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V, H, const N: usize> Iterator for DrainIter<'a, K, V, H, N> {
|
||||
type Item = (K, V);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while self.bucket_index < N {
|
||||
let Some(head) = self.hash_table.buckets[self.bucket_index].pop() else {
|
||||
self.bucket_index += 1;
|
||||
continue;
|
||||
};
|
||||
return Some(head);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ impl InterruptHandler for PS2Controller {
|
||||
|
||||
inner.e0 = false;
|
||||
|
||||
ygg_driver_input::send_event(event);
|
||||
ygg_driver_input::send_keyboard_event(event);
|
||||
}
|
||||
|
||||
count != 0
|
||||
|
||||
+12
-3
@@ -172,11 +172,20 @@ pub fn kernel_main() -> ! {
|
||||
CPU_INIT_FENCE.wait_all(ArchitectureImpl::cpu_count());
|
||||
|
||||
// Add keyboard device
|
||||
if let Err(error) =
|
||||
devfs::add_named_char_device(ygg_driver_input::setup(), "kbd", FileMode::new(0o660))
|
||||
{
|
||||
if let Err(error) = devfs::add_named_char_device(
|
||||
ygg_driver_input::setup_keyboard(),
|
||||
"kbd",
|
||||
FileMode::new(0o660),
|
||||
) {
|
||||
log::error!("Couldn't add keyboard device: {error:?}");
|
||||
}
|
||||
if let Err(error) = devfs::add_named_char_device(
|
||||
ygg_driver_input::setup_mouse(),
|
||||
"mouse",
|
||||
FileMode::new(0o660),
|
||||
) {
|
||||
log::error!("Couldn't add mouse device: {error:?}");
|
||||
}
|
||||
|
||||
task::init().expect("Failed to initialize the scheduler");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user