bus/usb: basic xHCI implementation

This commit is contained in:
Mark Poliakov 2024-02-26 23:04:51 +02:00
parent c4be544a9a
commit 012eb46cb9
37 changed files with 2881 additions and 69 deletions

View File

@ -26,11 +26,14 @@ kernel-arch = { path = "arch" }
# Drivers
ygg_driver_pci = { path = "driver/bus/pci" }
ygg_driver_usb = { path = "driver/bus/usb" }
ygg_driver_block = { path = "driver/block/core" }
ygg_driver_net_core = { path = "driver/net/core" }
ygg_driver_net_loopback = { path = "driver/net/loopback" }
ygg_driver_virtio_net = { path = "driver/virtio/net", features = ["pci"] }
ygg_driver_ahci = { path = "driver/block/ahci" }
ygg_driver_usb_xhci = { path = "driver/usb/xhci" }
ygg_driver_input = { path = "driver/input" }
kernel-fs = { path = "driver/fs/kernel-fs" }
memfs = { path = "driver/fs/memfs" }
@ -64,8 +67,6 @@ yboot-proto = { git = "https://git.alnyan.me/yggdrasil/yboot-proto.git" }
aml = { git = "https://github.com/alnyan/acpi.git", branch = "acpi-system" }
acpi_lib = { git = "https://github.com/alnyan/acpi.git", package = "acpi", branch = "acpi-system" }
acpi-system = { git = "https://github.com/alnyan/acpi-system.git" }
# TODO currently only supported here
xhci_lib = { git = "https://github.com/rust-osdev/xhci.git", package = "xhci" }
ygg_driver_nvme = { path = "driver/block/nvme" }
kernel-arch-x86_64 = { path = "arch/x86_64" }

View File

@ -29,6 +29,7 @@ pub trait Architecture: Sized {
fn idle_task() -> extern "C" fn(usize) -> !;
fn cpu_count() -> usize;
fn cpu_index<S: Scheduler + 'static>() -> u32;
// Interrupt management
fn interrupt_mask() -> bool;

View File

@ -1,5 +1,11 @@
#![no_std]
#![feature(effects, strict_provenance, asm_const, naked_functions)]
#![feature(
effects,
strict_provenance,
asm_const,
naked_functions,
trait_upcasting
)]
extern crate alloc;
@ -124,6 +130,10 @@ impl Architecture for ArchitectureImpl {
CPU_COUNT.load(Ordering::Acquire)
}
fn cpu_index<S: Scheduler + 'static>() -> u32 {
CpuImpl::<Self, S>::local().id()
}
fn interrupt_mask() -> bool {
let mut flags: u64;
unsafe {

View File

@ -182,12 +182,14 @@ impl InterruptHandler for AhciController {
let is = regs.IS.get();
if is != 0 {
// Clear global interrupt status
regs.IS.set(u32::MAX);
if let Some(ports) = self.ports.try_get() {
// Clear global interrupt status
regs.IS.set(u32::MAX);
for &port in self.ports.get() {
if is & (1 << port.index) != 0 {
port.handle_pending_interrupts();
for &port in ports {
if is & (1 << port.index) != 0 {
port.handle_pending_interrupts();
}
}
}
}

View File

@ -9,6 +9,7 @@ authors = ["Mark Poliakov <mark@alnyan.me>"]
[dependencies]
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
libk-util = { path = "../../../libk/libk-util" }
libk-thread = { path = "../../../libk/libk-thread" }
libk-mm = { path = "../../../libk/libk-mm" }
device-api = { path = "../../../lib/device-api", features = ["derive"] }
vfs = { path = "../../../lib/vfs" }

View File

@ -2,8 +2,8 @@ use core::task::Poll;
use alloc::{boxed::Box, format};
use kernel_fs::devfs;
use libk::cpu_index;
use libk_mm::address::AsPhysicalAddress;
use libk_thread::cpu_index;
use libk_util::waker::QueueWaker;
use ygg_driver_block::{
probe_partitions, IoOperation, IoRequest, IoSubmissionId, NgBlockDevice, NgBlockDeviceWrapper,

View File

@ -17,11 +17,11 @@ use device_api::{
Device,
};
use drive::NvmeDrive;
use libk::{cpu_count, cpu_index, runtime};
use libk_mm::{
address::{IntoRaw, PhysicalAddress},
device::DeviceMemoryIo,
};
use libk_thread::{cpu_count, cpu_index, runtime};
use libk_util::{
sync::{IrqGuard, IrqSafeSpinlock},
OneTimeInit,

18
driver/bus/usb/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "ygg_driver_usb"
version = "0.1.0"
edition = "2021"
authors = ["Mark Poliakov <mark@alnyan.me>"]
[dependencies]
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
device-api = { path = "../../../lib/device-api", features = ["derive"] }
ygg_driver_input = { path = "../../input" }
libk-util = { path = "../../../libk/libk-util" }
libk-mm = { path = "../../../libk/libk-mm" }
libk-thread = { path = "../../../libk/libk-thread" }
log = "0.4.20"
bytemuck = { version = "1.14.0", features = ["derive"] }
futures-util = { version = "0.3.28", default-features = false, features = ["alloc", "async-await"] }

51
driver/bus/usb/src/bus.rs Normal file
View File

@ -0,0 +1,51 @@
use core::sync::atomic::{AtomicU16, Ordering};
use alloc::{collections::BTreeMap, sync::Arc};
use libk_util::{queue::UnboundedMpmcQueue, sync::spin_rwlock::IrqSafeRwLock};
use crate::{
class_driver,
device::{UsbBusAddress, UsbDeviceAccess},
UsbHostController,
};
pub struct UsbBusManager {
busses: IrqSafeRwLock<BTreeMap<u16, &'static dyn UsbHostController>>,
devices: IrqSafeRwLock<BTreeMap<UsbBusAddress, Arc<UsbDeviceAccess>>>,
last_bus_address: AtomicU16,
}
impl UsbBusManager {
pub fn register_bus(hc: &'static dyn UsbHostController) -> u16 {
let i = BUS_MANAGER.last_bus_address.fetch_add(1, Ordering::AcqRel);
BUS_MANAGER.busses.write().insert(i, hc);
i
}
pub fn register_device(device: Arc<UsbDeviceAccess>) {
BUS_MANAGER
.devices
.write()
.insert(device.bus_address(), device.clone());
QUEUE.push_back(device);
}
}
pub async fn bus_handler() {
loop {
let new_device = QUEUE.pop_front().await;
log::debug!("New device connected: {}", new_device.bus_address());
class_driver::spawn_driver(new_device).await.ok();
}
}
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();

View File

@ -0,0 +1,203 @@
use core::mem::MaybeUninit;
use alloc::sync::Arc;
use libk_mm::PageBox;
use libk_thread::runtime;
use yggdrasil_abi::{
error::Error,
io::{KeyboardKey, KeyboardKeyEvent},
};
use crate::{
device::UsbDeviceAccess,
info::{UsbDeviceClass, UsbDeviceProtocol},
UsbDirection,
};
pub async fn spawn_driver(device: Arc<UsbDeviceAccess>) -> Result<(), Error> {
// TODO query all configurations?
let device_info = &device.info;
let config_info = device.query_configuration_info(0).await?;
// Select device class/subclass/protocol based on device/interface
if config_info.interfaces.len() == 1 {
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
};
match (class, subclass, protocol) {
(UsbDeviceClass::Hid, 0x01, _) => runtime::spawn(keyboard_driver(device)),
(_, _, _) => Ok(()),
}
} else {
Ok(())
}
}
pub async fn keyboard_driver(device: Arc<UsbDeviceAccess>) -> Result<(), Error> {
const MODIFIER_MAP: &[KeyboardKey] = &[
KeyboardKey::LControl,
KeyboardKey::LShift,
KeyboardKey::LAlt,
KeyboardKey::Unknown,
KeyboardKey::RControl,
KeyboardKey::RShift,
KeyboardKey::RAlt,
KeyboardKey::Unknown,
];
#[derive(Default)]
struct KeyboardState {
state: [u64; 4],
mods: u8,
}
impl KeyboardState {
pub fn new() -> Self {
Self::default()
}
pub fn translate_key(k: u8) -> KeyboardKey {
match k {
4..=29 => KeyboardKey::Char(k - 4 + b'a'),
30..=38 => KeyboardKey::Char(k - 30 + b'1'),
39 => KeyboardKey::Char(b'0'),
40 => KeyboardKey::Enter,
41 => KeyboardKey::Escape,
42 => KeyboardKey::Backspace,
43 => KeyboardKey::Tab,
44 => KeyboardKey::Char(b' '),
45 => KeyboardKey::Char(b'-'),
46 => KeyboardKey::Char(b'='),
47 => KeyboardKey::Char(b'['),
48 => KeyboardKey::Char(b']'),
49 => KeyboardKey::Char(b'\\'),
51 => KeyboardKey::Char(b';'),
52 => KeyboardKey::Char(b'\''),
53 => KeyboardKey::Char(b'`'),
54 => KeyboardKey::Char(b','),
55 => KeyboardKey::Char(b'.'),
56 => KeyboardKey::Char(b'/'),
58..=69 => KeyboardKey::F(k - 58),
_ => {
log::debug!("Unknown key: {}", k);
KeyboardKey::Unknown
}
}
}
pub fn retain_modifiers(
&mut self,
m: u8,
events: &mut [MaybeUninit<KeyboardKeyEvent>],
) -> usize {
let mut count = 0;
let released = self.mods & !m;
for i in 0..8 {
if released & (1 << i) != 0 {
events[count].write(KeyboardKeyEvent::Released(MODIFIER_MAP[i]));
count += 1;
}
}
self.mods &= m;
count
}
pub fn press_modifiers(
&mut self,
m: u8,
events: &mut [MaybeUninit<KeyboardKeyEvent>],
) -> usize {
let mut count = 0;
let pressed = m & !self.mods;
for i in 0..8 {
if pressed & (1 << i) != 0 {
events[count].write(KeyboardKeyEvent::Pressed(MODIFIER_MAP[i]));
count += 1;
}
}
self.mods = m;
count
}
pub fn retain(
&mut self,
keys: &[u8],
events: &mut [MaybeUninit<KeyboardKeyEvent>],
) -> usize {
let mut count = 0;
for i in 1..256 {
if self.state[i / 64] & (1 << (i % 64)) != 0 {
if !keys.contains(&(i as u8)) {
events[count]
.write(KeyboardKeyEvent::Released(Self::translate_key(i as u8)));
self.state[i / 64] &= !(1 << (i % 64));
count += 1;
}
}
}
count
}
pub fn press(
&mut self,
keys: &[u8],
events: &mut [MaybeUninit<KeyboardKeyEvent>],
) -> usize {
let mut count = 0;
for &k in keys {
let index = (k as usize) / 64;
if self.state[index] & (1 << (k % 64)) == 0 {
self.state[index] |= 1 << (k % 64);
events[count].write(KeyboardKeyEvent::Pressed(Self::translate_key(k)));
count += 1;
}
}
count
}
}
// TODO not sure whether to use boot protocol (easy) or GetReport
let config = device.select_configuration(|_| true).await?.unwrap();
assert_eq!(config.endpoints.len(), 1);
let pipe = device.open_interrupt_pipe(1, UsbDirection::In).await?;
let mut buffer = PageBox::new_slice(0, 8)?;
let mut state = KeyboardState::new();
let mut events = [MaybeUninit::uninit(); 16];
loop {
let mut event_count = 0;
let data = pipe.listen(&mut buffer).await?;
event_count += state.retain_modifiers(data[0], &mut events);
event_count += state.press_modifiers(data[0], &mut events[event_count..]);
event_count += state.retain(&data[2..], &mut events[event_count..]);
event_count += state.press(&data[2..], &mut events[event_count..]);
let events = unsafe { MaybeUninit::slice_assume_init_ref(&events[..event_count]) };
for &event in events {
ygg_driver_input::send_event(event);
}
}
}

View File

@ -0,0 +1,92 @@
use core::{
future::poll_fn,
sync::atomic::{AtomicU32, Ordering},
task::{Context, Poll},
};
use alloc::{sync::Arc, vec::Vec};
use futures_util::task::AtomicWaker;
use libk_mm::address::PhysicalAddress;
use yggdrasil_abi::error::Error;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum UsbDirection {
Out,
In,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct UsbTransferToken(pub u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct UsbTransferResult(pub u32);
pub struct UsbTransferStatus {
pub result: AtomicU32,
pub notify: AtomicWaker,
}
pub struct UsbTransfer {
pub id: UsbTransferToken,
pub length: usize,
pub direction: UsbDirection,
pub elements: Vec<PhysicalAddress>,
pub status: Arc<UsbTransferStatus>,
}
// TODO this is xHCI-specific
impl UsbTransferResult {
pub fn is_success(&self) -> bool {
(self.0 >> 24) & 0xFF == 1
}
pub fn sub_length(&self) -> usize {
(self.0 & 0xFFFFFF) as _
}
}
impl UsbTransfer {
pub async fn wait(&self) -> Result<usize, Error> {
let sub_length = self.status.wait().await?;
Ok(self.length.saturating_sub(sub_length))
}
}
impl UsbTransferStatus {
pub fn new() -> Self {
Self {
result: AtomicU32::new(0),
notify: AtomicWaker::new(),
}
}
pub(crate) async fn wait(&self) -> Result<usize, Error> {
poll_fn(|cx| {
self.poll(cx).map(|v| {
if v.is_success() {
Ok(v.sub_length())
} else {
Err(Error::InvalidOperation)
}
})
})
.await
}
pub fn signal(&self, status: u32) {
self.result.store(status, Ordering::Release);
self.notify.wake();
}
pub fn poll(&self, cx: &mut Context<'_>) -> Poll<UsbTransferResult> {
self.notify.register(cx.waker());
let value = self.result.load(Ordering::Acquire);
if value != 0 {
Poll::Ready(UsbTransferResult(value))
} else {
Poll::Pending
}
}
}

View File

@ -0,0 +1,146 @@
use bytemuck::{Pod, Zeroable};
use yggdrasil_abi::error::Error;
use crate::{
info::{UsbDeviceClass, UsbDeviceProtocol, UsbEndpointType},
UsbDirection,
};
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
#[repr(C, packed)]
pub struct UsbDeviceDescriptor {
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,
pub id_vendor: u16,
pub id_product: u16,
pub bcd_device: u16,
pub manufacturer_str: u8,
pub product_str: u8,
pub serial_number_str: u8,
pub num_configurations: u8,
}
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
#[repr(C, packed)]
pub struct UsbConfigurationDescriptor {
pub length: u8,
pub ty: u8,
pub total_length: u16,
pub num_interfaces: u8,
pub config_val: u8,
pub config_str: u8,
pub attributes: u8,
pub max_power: u8,
}
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
#[repr(C, packed)]
pub struct UsbInterfaceDescriptor {
pub length: u8,
pub ty: u8,
pub interface_number: u8,
pub alternate_setting: u8,
pub num_endpoints: u8,
pub interface_class: u8,
pub interface_subclass: u8,
pub interface_protocol: u8,
pub interface_str: u8,
}
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
#[repr(C, packed)]
pub struct UsbEndpointDescriptor {
pub length: u8,
pub ty: u8,
pub endpoint_address: u8,
pub attributes: u8,
pub max_packet_size: u16,
pub interval: u8,
}
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
#[repr(C, packed)]
pub struct UsbDeviceQualifier {
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,
pub num_configurations: u8,
pub _reserved: u8,
}
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
#[repr(C, packed)]
pub struct UsbOtherSpeedConfiguration {
pub length: u8,
pub ty: u8,
pub total_length: u16,
pub num_interfaces: u8,
pub config_val: u8,
pub config_str: u8,
pub attributes: u8,
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)
}
}
impl UsbEndpointDescriptor {
pub fn direction(&self) -> UsbDirection {
match self.endpoint_address >> 7 {
1 => UsbDirection::In,
0 => UsbDirection::Out,
_ => unreachable!(),
}
}
pub fn number(&self) -> u8 {
assert_ne!(self.endpoint_address & 0xF, 0);
self.endpoint_address & 0xF
}
pub fn transfer_type(&self) -> UsbEndpointType {
match self.attributes & 0x3 {
0 => UsbEndpointType::Control,
1 => UsbEndpointType::Isochronous,
2 => UsbEndpointType::Bulk,
3 => UsbEndpointType::Interrupt,
_ => unreachable!(),
}
}
}
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 max_packet_size(&self) -> Result<usize, Error> {
match self.max_packet_size_0 {
8 => Ok(8),
16 => Ok(16),
32 => Ok(32),
64 => Ok(64),
_ => Err(Error::InvalidArgument),
}
}
}

View File

@ -0,0 +1,191 @@
use core::{fmt, ops::Deref};
use alloc::{boxed::Box, vec::Vec};
use futures_util::future::BoxFuture;
use libk_mm::PageBox;
use libk_util::sync::spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard};
use yggdrasil_abi::error::Error;
use crate::{
info::{UsbConfigurationInfo, UsbDeviceInfo, UsbEndpointInfo, UsbInterfaceInfo},
pipe::{
control::{ConfigurationDescriptorEntry, UsbControlPipeAccess},
interrupt::UsbInterruptPipeAccess,
},
UsbDirection, UsbHostController,
};
// High-level structures for info provided through descriptors
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct UsbBusAddress {
pub bus: u16,
pub device: u8,
}
pub struct UsbDeviceAccess {
pub device: Box<dyn UsbDevice>,
pub info: UsbDeviceInfo,
pub num_configurations: u8,
pub current_configuration: IrqSafeRwLock<Option<UsbConfigurationInfo>>,
}
#[allow(unused)]
pub trait UsbDevice: Send + Sync {
// Endpoint "0"
fn control_pipe(&self) -> Option<&UsbControlPipeAccess>;
fn open_interrupt_pipe<'a>(
&'a self,
number: u8,
direction: UsbDirection,
) -> BoxFuture<Result<UsbInterruptPipeAccess, Error>> {
unimplemented!()
}
fn port_number(&self) -> u8;
fn bus_address(&self) -> UsbBusAddress;
fn controller(&self) -> &'static dyn UsbHostController;
fn debug(&self) {}
}
impl UsbDeviceAccess {
/// 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: Box<dyn UsbDevice>) -> Result<Self, Error> {
let control = raw.control_pipe().ok_or(Error::InvalidOperation)?;
let mut string_buffer = PageBox::new_uninit()?;
let device_desc = control.query_device_descriptor().await?;
let manufacturer = control
.query_string(device_desc.manufacturer_str, &mut string_buffer)
.await?;
let product = control
.query_string(device_desc.product_str, &mut string_buffer)
.await?;
let info = UsbDeviceInfo {
manufacturer,
product,
id_vendor: device_desc.id_vendor,
id_product: device_desc.id_product,
device_class: device_desc.class(),
device_subclass: device_desc.device_subclass,
device_protocol: device_desc.protocol(),
max_packet_size: device_desc.max_packet_size()?,
};
Ok(Self {
device: raw,
info,
num_configurations: device_desc.num_configurations,
current_configuration: IrqSafeRwLock::new(None),
})
}
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>, Error> {
let mut current_config = self.current_configuration.write();
let control_pipe = self.control_pipe().ok_or(Error::InvalidOperation)?;
for i in 0..self.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, Error> {
if index >= self.num_configurations {
return Err(Error::InvalidArgument);
}
let mut string_buffer = PageBox::new_uninit()?;
let control_pipe = self.control_pipe().ok_or(Error::InvalidOperation)?;
let query = control_pipe.query_configuration_descriptor(index).await?;
let configuration_name = control_pipe
.query_string(query.configuration().config_str, &mut string_buffer)
.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, &mut string_buffer)
.await?;
interfaces.push(UsbInterfaceInfo {
name,
number: iface.interface_number,
interface_class: iface.class(),
interface_subclass: iface.interface_subclass,
interface_protocol: iface.protocol(),
});
}
_ => (),
}
}
let info = UsbConfigurationInfo {
name: configuration_name,
config_value: query.configuration().config_val,
interfaces,
endpoints,
};
Ok(info)
}
}
impl Deref for UsbDeviceAccess {
type Target = dyn UsbDevice;
fn deref(&self) -> &Self::Target {
&*self.device
}
}
impl fmt::Display for UsbBusAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.bus, self.device)
}
}

View File

@ -0,0 +1,84 @@
use alloc::{string::String, vec::Vec};
use yggdrasil_abi::primitive_enum;
use crate::UsbDirection;
#[derive(Debug, Clone, Copy)]
pub enum UsbEndpointType {
Control,
Isochronous,
Bulk,
Interrupt,
}
#[derive(Debug, Clone, Copy)]
pub enum UsbSyncType {
NoSync,
Async,
Adaptive,
Sync,
}
#[derive(Debug)]
pub enum UsbUsageType {
Data,
Feedback,
ImplicitFeedbackData,
Reserved,
}
primitive_enum! {
pub enum UsbDeviceClass: u8 {
FromInterface = 0x00,
Hid = 0x03,
Unknown = 0xFF,
}
}
primitive_enum! {
pub enum UsbDeviceProtocol: u8 {
FromInterface = 0x00,
Unknown = 0xFF,
}
}
#[derive(Debug, Clone)]
pub struct UsbInterfaceInfo {
pub name: String,
pub number: u8,
pub interface_class: UsbDeviceClass,
pub interface_subclass: u8,
pub interface_protocol: UsbDeviceProtocol,
}
#[derive(Debug, Clone)]
pub struct UsbEndpointInfo {
pub number: u8,
pub direction: UsbDirection,
pub max_packet_size: usize,
pub ty: UsbEndpointType,
}
#[derive(Debug, Clone)]
pub struct UsbConfigurationInfo {
pub name: 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 id_vendor: u16,
pub id_product: u16,
pub device_class: UsbDeviceClass,
pub device_subclass: u8,
pub device_protocol: UsbDeviceProtocol,
pub max_packet_size: usize,
}

19
driver/bus/usb/src/lib.rs Normal file
View File

@ -0,0 +1,19 @@
#![no_std]
#![feature(iter_array_chunks, maybe_uninit_slice)]
extern crate alloc;
pub mod bus;
pub mod communication;
pub mod descriptor;
pub mod device;
pub mod info;
pub mod pipe;
pub mod class_driver;
pub use communication::{UsbDirection, UsbTransfer, UsbTransferStatus, UsbTransferToken};
pub trait UsbEndpoint {}
pub trait UsbHostController {}

View File

@ -0,0 +1,323 @@
use core::{
cmp::Ordering,
mem::{size_of, MaybeUninit},
ops::Deref,
};
use alloc::{boxed::Box, string::String};
use bytemuck::{Pod, Zeroable};
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
PageBox,
};
use yggdrasil_abi::error::Error;
use crate::{
descriptor::{
UsbConfigurationDescriptor, UsbDeviceDescriptor, UsbDeviceQualifier, UsbEndpointDescriptor,
UsbInterfaceDescriptor, UsbOtherSpeedConfiguration,
},
UsbDirection, UsbTransfer,
};
use super::UsbGenericPipe;
#[derive(Debug)]
pub struct ControlTransferSetup {
pub bm_request_type: u8,
pub b_request: u8,
pub w_value: u16,
pub w_index: u16,
pub w_length: u16,
}
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
#[repr(C)]
pub struct SetConfiguration;
pub trait UsbDeviceRequest: Sized + Pod {
const BM_REQUEST_TYPE: u8;
const B_REQUEST: u8;
}
pub trait UsbDescriptorRequest: UsbDeviceRequest {
const DESCRIPTOR_TYPE: u8;
}
impl UsbDescriptorRequest for UsbDeviceDescriptor {
const DESCRIPTOR_TYPE: u8 = 1;
}
impl UsbDescriptorRequest for UsbConfigurationDescriptor {
const DESCRIPTOR_TYPE: u8 = 2;
}
impl UsbDescriptorRequest for UsbInterfaceDescriptor {
const DESCRIPTOR_TYPE: u8 = 4;
}
impl UsbDeviceRequest for SetConfiguration {
const BM_REQUEST_TYPE: u8 = 0;
const B_REQUEST: u8 = 0x09;
}
impl<U: UsbDescriptorRequest> UsbDeviceRequest for U {
const BM_REQUEST_TYPE: u8 = 0b10000000;
const B_REQUEST: u8 = 0x06;
}
fn decode_usb_string(bytes: &[u8]) -> Result<String, Error> {
if bytes.len() % 2 != 0 {
return Err(Error::InvalidArgument);
}
char::decode_utf16(
bytes
.into_iter()
.array_chunks::<2>()
.map(|[&a, &b]| u16::from_le_bytes([a, b])),
)
.collect::<Result<String, _>>()
.map_err(|_| Error::InvalidArgument)
}
// Pipe impl
pub trait UsbControlPipe: UsbGenericPipe + Send + Sync {
fn start_transfer(
&self,
setup: ControlTransferSetup,
data: Option<(PhysicalAddress, usize, UsbDirection)>,
) -> Result<UsbTransfer, Error>;
}
pub struct UsbControlPipeAccess(pub Box<dyn UsbControlPipe>);
fn input_buffer<T: Pod>(
data: &mut PageBox<MaybeUninit<T>>,
) -> (PhysicalAddress, usize, UsbDirection) {
(
unsafe { data.as_physical_address() },
size_of::<T>(),
UsbDirection::In,
)
}
#[derive(Debug)]
pub enum ConfigurationDescriptorEntry<'a> {
Configuration(&'a UsbConfigurationDescriptor),
Interface(&'a UsbInterfaceDescriptor),
Endpoint(&'a UsbEndpointDescriptor),
DeviceQualifier(&'a UsbDeviceQualifier),
OtherSpeed(&'a UsbOtherSpeedConfiguration),
Other,
}
pub struct ConfigurationDescriptorIter<'a> {
buffer: &'a PageBox<[u8]>,
offset: usize,
}
pub struct ConfigurationDescriptorQuery {
buffer: PageBox<[u8]>,
}
impl<'a> Iterator for ConfigurationDescriptorIter<'a> {
type Item = ConfigurationDescriptorEntry<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.offset + 2 >= self.buffer.len() {
return None;
}
let desc_len = self.buffer[self.offset] as usize;
let desc_ty = self.buffer[self.offset + 1];
if desc_len == 0 {
return None;
}
let entry = match desc_ty {
0x02 if desc_len == size_of::<UsbConfigurationDescriptor>() => {
ConfigurationDescriptorEntry::Configuration(bytemuck::from_bytes(
&self.buffer[self.offset..self.offset + desc_len],
))
}
0x04 if desc_len == size_of::<UsbInterfaceDescriptor>() => {
ConfigurationDescriptorEntry::Interface(bytemuck::from_bytes(
&self.buffer[self.offset..self.offset + desc_len],
))
}
0x05 if desc_len == size_of::<UsbEndpointDescriptor>() => {
ConfigurationDescriptorEntry::Endpoint(bytemuck::from_bytes(
&self.buffer[self.offset..self.offset + desc_len],
))
}
0x07 if desc_len == size_of::<UsbOtherSpeedConfiguration>() => {
ConfigurationDescriptorEntry::OtherSpeed(bytemuck::from_bytes(
&self.buffer[self.offset..self.offset + desc_len],
))
}
_ => ConfigurationDescriptorEntry::Other,
};
self.offset += desc_len;
Some(entry)
}
}
impl ConfigurationDescriptorQuery {
pub fn configuration(&self) -> &UsbConfigurationDescriptor {
bytemuck::from_bytes(&self.buffer[..size_of::<UsbConfigurationDescriptor>()])
}
pub fn descriptors(&self) -> ConfigurationDescriptorIter<'_> {
ConfigurationDescriptorIter {
buffer: &self.buffer,
offset: 0,
}
}
}
impl UsbControlPipeAccess {
pub async fn perform_value_control(
&self,
setup: ControlTransferSetup,
buffer: Option<(PhysicalAddress, usize, UsbDirection)>,
) -> Result<(), Error> {
let transfer = self.start_transfer(setup, buffer)?;
transfer.status.wait().await?;
self.complete_transfer(transfer);
Ok(())
}
async fn fill_configuation_descriptor(
&self,
index: u8,
buffer: &mut PageBox<[MaybeUninit<u8>]>,
) -> Result<(), Error> {
self.perform_value_control(
ControlTransferSetup {
bm_request_type: 0b10000000,
b_request: 0x06,
w_value: 0x200 | (index as u16),
w_index: 0,
w_length: buffer.len().try_into().unwrap(),
},
Some((
unsafe { buffer.as_physical_address() },
buffer.len(),
UsbDirection::In,
)),
)
.await
}
pub async fn query_configuration_descriptor(
&self,
index: u8,
) -> Result<ConfigurationDescriptorQuery, Error> {
// First, query the real length of the descriptor
let mut buffer = PageBox::new_uninit_slice(size_of::<UsbConfigurationDescriptor>())?;
self.fill_configuation_descriptor(index, &mut buffer)
.await?;
let buffer = unsafe { PageBox::assume_init_slice(buffer) };
let desc: &UsbConfigurationDescriptor = bytemuck::from_bytes(&buffer);
let total_len = desc.total_length as usize;
// Return if everything's ready at this point
match total_len.cmp(&size_of::<UsbConfigurationDescriptor>()) {
Ordering::Less => todo!(),
Ordering::Equal => return Ok(ConfigurationDescriptorQuery { buffer }),
_ => (),
}
// Otherwise, query the rest of the data
let mut buffer = PageBox::new_uninit_slice(total_len)?;
self.fill_configuation_descriptor(index, &mut buffer)
.await?;
let buffer = unsafe { PageBox::assume_init_slice(buffer) };
let desc: &UsbConfigurationDescriptor =
bytemuck::from_bytes(&buffer[..size_of::<UsbConfigurationDescriptor>()]);
let total_len = desc.total_length as usize;
if total_len != buffer.len() {
todo!();
}
Ok(ConfigurationDescriptorQuery { buffer })
}
pub async fn query_device_descriptor(&self) -> Result<PageBox<UsbDeviceDescriptor>, Error> {
let mut output = PageBox::new_uninit()?;
self.perform_value_control(
ControlTransferSetup {
bm_request_type: 0b10000000,
b_request: 0x06,
w_value: 0x100,
w_index: 0,
w_length: size_of::<UsbDeviceDescriptor>() as _,
},
Some(input_buffer(&mut output)),
)
.await?;
Ok(unsafe { output.assume_init() })
}
pub async fn query_string(
&self,
index: u8,
buffer: &mut PageBox<MaybeUninit<[u8; 4096]>>,
) -> Result<String, Error> {
self.perform_value_control(
ControlTransferSetup {
bm_request_type: 0b10000000,
b_request: 0x06,
w_value: 0x300 | (index as u16),
w_index: 0,
w_length: 4096,
},
Some(input_buffer(buffer)),
)
.await?;
let data = unsafe { buffer.assume_init_ref() };
let len = data[0] as usize;
decode_usb_string(&data[2..len])
}
pub async fn perform_action<D: UsbDeviceRequest>(
&self,
w_value: u16,
w_index: u16,
) -> Result<(), Error> {
self.perform_value_control(
ControlTransferSetup {
bm_request_type: D::BM_REQUEST_TYPE,
b_request: D::B_REQUEST,
w_value,
w_index,
w_length: 0,
},
None,
)
.await
}
pub async fn set_configuration(&self, value: u16) -> Result<(), Error> {
self.perform_action::<SetConfiguration>(value, 0).await
}
}
impl Deref for UsbControlPipeAccess {
type Target = dyn UsbControlPipe;
fn deref(&self) -> &Self::Target {
&*self.0
}
}

View File

@ -0,0 +1,32 @@
use core::ops::Deref;
use alloc::boxed::Box;
use libk_mm::PageBox;
use yggdrasil_abi::error::Error;
use crate::UsbTransfer;
use super::UsbGenericPipe;
pub trait UsbInterruptPipe: UsbGenericPipe + Send + Sync {
fn start_read(&self, buffer: &mut PageBox<[u8]>) -> Result<UsbTransfer, Error>;
}
pub struct UsbInterruptPipeAccess(pub Box<dyn UsbInterruptPipe>);
impl UsbInterruptPipeAccess {
// TODO timeout
pub async fn listen<'a>(&self, buffer: &'a mut PageBox<[u8]>) -> Result<&'a [u8], Error> {
let transfer = self.start_read(buffer)?;
let len = transfer.wait().await?;
Ok(&buffer[..len])
}
}
impl Deref for UsbInterruptPipeAccess {
type Target = dyn UsbInterruptPipe;
fn deref(&self) -> &Self::Target {
&*self.0
}
}

View File

@ -0,0 +1,12 @@
use super::UsbTransfer;
pub mod control;
pub mod interrupt;
pub trait UsbGenericPipe {
fn complete_transfer(&self, transfer: UsbTransfer);
}
pub enum UsbPipe {
Control(control::UsbControlPipeAccess),
}

11
driver/input/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "ygg_driver_input"
version = "0.1.0"
edition = "2021"
[dependencies]
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
libk-util = { path = "../../libk/libk-util" }
libk-thread = { path = "../../libk/libk-thread" }
libk-mm = { path = "../../libk/libk-mm" }
vfs = { path = "../../lib/vfs" }

View File

@ -1,15 +1,16 @@
// TODO
#![allow(missing_docs)]
#![no_std]
extern crate alloc;
use core::task::{Context, Poll};
use abi::{
use libk_thread::block;
use libk_util::ring::LossyRingQueue;
use vfs::{CharDevice, FileReadiness};
use yggdrasil_abi::{
error::Error,
io::{DeviceRequest, KeyboardKeyEvent},
};
use libk::block;
use libk_util::ring::LossyRingQueue;
use vfs::{CharDevice, FileReadiness};
pub struct KeyboardDevice;

View File

@ -0,0 +1,22 @@
[package]
name = "ygg_driver_usb_xhci"
version = "0.1.0"
edition = "2021"
authors = ["Mark Poliakov <mark@alnyan.me>"]
[dependencies]
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
device-api = { path = "../../../lib/device-api", features = ["derive"] }
ygg_driver_pci = { path = "../../bus/pci" }
ygg_driver_usb = { path = "../../bus/usb" }
libk-util = { path = "../../../libk/libk-util" }
libk-mm = { path = "../../../libk/libk-mm" }
libk-thread = { path = "../../../libk/libk-thread" }
xhci_lib = { git = "https://github.com/rust-osdev/xhci.git", package = "xhci" }
log = "0.4.20"
tock-registers = "0.9.0"
bytemuck = { version = "1.14.0", features = ["derive"] }
futures-util = { version = "0.3.28", default-features = false, features = ["alloc", "async-await"] }

484
driver/usb/xhci/src/lib.rs Normal file
View File

@ -0,0 +1,484 @@
#![no_std]
#![feature(iter_array_chunks)]
extern crate alloc;
use core::{
future::poll_fn,
sync::atomic::{AtomicBool, Ordering},
task::{Context, Poll},
time::Duration,
};
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, vec::Vec};
use device_api::{
interrupt::{InterruptAffinity, InterruptHandler},
Device,
};
use futures_util::{future::BoxFuture, FutureExt};
use libk_mm::{
address::{AsPhysicalAddress, IntoRaw, PhysicalAddress},
PageBox,
};
use libk_thread::runtime::{self, FutureTimeout};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, waker::QueueWaker, OneTimeInit};
use pipe::ControlPipe;
use regs::{Mapper, Regs};
use ring::{CommandExecutor, CommandRing, EventRing};
use xhci_lib::context::{self, InputHandler};
use ygg_driver_pci::{
device::{PciDeviceInfo, PreferredInterruptMode},
PciCommandRegister, PciConfigurationSpace,
};
use ygg_driver_usb::{
bus::UsbBusManager,
device::{UsbBusAddress, UsbDevice, UsbDeviceAccess},
info::UsbEndpointType,
pipe::{control::UsbControlPipeAccess, interrupt::UsbInterruptPipeAccess},
UsbDirection, UsbHostController,
};
use yggdrasil_abi::error::Error;
use crate::{
pipe::InterruptPipe,
ring::{Event, EventRingSegmentTable, TransferRing},
};
mod pipe;
mod regs;
mod ring;
pub struct XhciContext<const N: usize> {
pub(crate) input: IrqSafeRwLock<PageBox<context::Input<N>>>,
pub(crate) output: PageBox<context::Device<N>>,
}
// TODO device context information
pub struct XhciBusDevice {
port_id: u8,
slot_id: u8,
bus_address: UsbBusAddress,
xhci: &'static Xhci,
context: Arc<XhciContext<8>>,
control_pipe: UsbControlPipeAccess,
}
pub struct Xhci {
regs: Regs,
bus_address: OneTimeInit<u16>,
port_count: usize,
// TODO use to allocate proper contexts
#[allow(unused)]
context_size: usize,
dcbaa: IrqSafeRwLock<PageBox<[PhysicalAddress]>>,
endpoints: IrqSafeRwLock<BTreeMap<(u8, u8), Arc<TransferRing>>>,
event_ring: EventRing,
command_ring: CommandRing,
spawned_ports: Vec<AtomicBool>,
port_reset_notify: QueueWaker,
}
impl XhciBusDevice {
fn dci(ty: UsbEndpointType, dir: UsbDirection, number: u8) -> usize {
match ty {
UsbEndpointType::Control => (number as usize) * 2 + 1,
_ => {
let off = match dir {
UsbDirection::Out => 0,
UsbDirection::In => 1,
};
(number as usize * 2) + off
}
}
}
async fn setup_endpoint_inner(
&self,
number: u8,
ty: UsbEndpointType,
direction: UsbDirection,
) -> Result<Arc<TransferRing>, Error> {
log::debug!("Setup endpoint #{}: {:?} {:?}", number, ty, direction);
let mut input = self.context.input.write();
let ep_type = match (ty, direction) {
(UsbEndpointType::Interrupt, UsbDirection::In) => context::EndpointType::InterruptIn,
_ => todo!(),
};
// number = 1
// dci = (number * 2) + IN = 1 * 2 + 1 = 3
let dci = Self::dci(ty, direction, number);
let ring = Arc::new(TransferRing::new(self.slot_id, dci as _, 128)?);
{
let control = input.control_mut();
control.set_add_context_flag(0);
control.clear_add_context_flag(1);
control.set_add_context_flag(dci);
}
{
let slot = input.device_mut().slot_mut();
slot.set_context_entries(31);
}
{
let ep_cx = input.device_mut().endpoint_mut(dci);
ep_cx.set_tr_dequeue_pointer(ring.dequeue_pointer().into_raw());
ep_cx.set_dequeue_cycle_state();
ep_cx.set_endpoint_type(ep_type);
ep_cx.set_error_count(3);
// TODO get from endpoint info
ep_cx.set_max_packet_size(8);
}
self.xhci
.command_ring
.configure_endpoint(self.xhci, self.slot_id, &mut input)
.await?;
self.xhci
.register_endpoint(self.slot_id, dci as _, ring.clone());
Ok(ring)
}
}
impl UsbDevice for XhciBusDevice {
fn control_pipe(&self) -> Option<&UsbControlPipeAccess> {
Some(&self.control_pipe)
}
fn port_number(&self) -> u8 {
self.port_id
}
fn bus_address(&self) -> UsbBusAddress {
self.bus_address
}
fn controller(&self) -> &'static dyn UsbHostController {
self.xhci
}
fn open_interrupt_pipe<'a>(
&'a self,
number: u8,
direction: UsbDirection,
) -> BoxFuture<Result<UsbInterruptPipeAccess, Error>> {
async move {
let ring = self
.setup_endpoint_inner(number, UsbEndpointType::Interrupt, direction)
.await?;
let dci = Self::dci(UsbEndpointType::Interrupt, direction, number) + 1;
let pipe = InterruptPipe::input(self.xhci, self.slot_id, number, dci as _, ring);
Ok(UsbInterruptPipeAccess(Box::new(pipe)))
}
.boxed()
}
fn debug(&self) {}
}
impl UsbHostController for Xhci {}
impl XhciContext<8> {
pub fn new_32byte() -> Result<Self, Error> {
let input = PageBox::new(context::Input::new_32byte())?;
let output = PageBox::new(context::Device::new_32byte())?;
Ok(Self {
input: IrqSafeRwLock::new(input),
output,
})
}
}
impl Xhci {
pub fn new(regs: xhci_lib::Registers<Mapper>) -> Result<Self, Error> {
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();
Ok(Self {
regs,
bus_address: OneTimeInit::new(),
port_count,
context_size,
event_ring,
command_ring,
dcbaa: IrqSafeRwLock::new(PageBox::new_slice(PhysicalAddress::ZERO, slot_count + 1)?),
endpoints: IrqSafeRwLock::new(BTreeMap::new()),
spawned_ports: Vec::from_iter((0..port_count).map(|_| AtomicBool::new(false))),
port_reset_notify: QueueWaker::new(),
})
}
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<TransferRing>) {
self.endpoints.write().insert((slot_id, endpoint_id), ring);
}
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(
&'static self,
slot_id: u8,
root_hub_port_number: u8,
) -> Result<Box<dyn UsbDevice>, Error> {
let address = 1;
let ring = Arc::new(TransferRing::new(slot_id, 1, 128)?);
let context = XhciContext::new_32byte()?;
let mut input = context.input.write();
// Setup input context
{
let control = input.control_mut();
control.set_add_context_flag(0);
control.set_add_context_flag(1);
}
{
let slot = input.device_mut().slot_mut();
slot.set_context_entries(1);
slot.set_interrupter_target(0);
slot.set_usb_device_address(address);
slot.set_root_hub_port_number(root_hub_port_number);
}
{
let ep0 = input.device_mut().endpoint_mut(1);
ep0.set_endpoint_type(context::EndpointType::Control);
ep0.set_tr_dequeue_pointer(ring.dequeue_pointer().into_raw());
ep0.set_dequeue_cycle_state();
ep0.set_error_count(3);
}
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, slot_id, ring);
drop(input);
let bus_address = UsbBusAddress {
bus: *self.bus_address.get(),
device: address,
};
let device = XhciBusDevice {
xhci: self,
slot_id,
port_id: root_hub_port_number,
bus_address,
context: Arc::new(context),
control_pipe: UsbControlPipeAccess(Box::new(pipe)),
};
Ok(Box::new(device))
}
async fn port_task(&'static self, index: usize) -> Result<(), Error> {
log::debug!("Task spawned for port {}", index);
self.reset_port(index).await?;
let slot_id = self.command_ring.enable_slot(self).await?;
log::debug!("Enabled slot: {}", slot_id);
let device = self.assign_device(slot_id, (index + 1) as _).await?;
let device = UsbDeviceAccess::setup(device).await?;
UsbBusManager::register_device(Arc::new(device));
Ok(())
}
fn handle_device_attached(&'static self, port: usize) -> Result<(), Error> {
if !self.spawned_ports[port].swap(true, Ordering::Release) {
// Port has not yet been spawned
runtime::spawn(async move { self.port_task(port).await })?;
}
Ok(())
}
fn poll_port_reset(&self, cx: &mut Context<'_>, port: usize) -> Poll<()> {
self.port_reset_notify.register(cx.waker());
if self.regs.ports.read(port).portsc.port_reset_change() {
self.port_reset_notify.remove(cx.waker());
Poll::Ready(())
} else {
Poll::Pending
}
}
async fn reset_port(&self, port: usize) -> Result<(), Error> {
log::debug!("Reset port {}", port);
self.regs.ports.update(port, |u| {
u.portsc.set_port_reset();
});
// Wait for port reset
let status = runtime::run_with_timeout(
Duration::from_secs(1),
poll_fn(|cx| self.poll_port_reset(cx, port)),
)
.await;
match status {
FutureTimeout::Ok(()) => Ok(()),
FutureTimeout::Timeout => Err(Error::TimedOut),
}
}
fn handle_event(&self) {
while let Some(event) = self.event_ring.try_dequeue() {
match event {
Event::PortChange(_) => {
self.port_reset_notify.wake_one();
}
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 CommandExecutor for Xhci {
fn ring_doorbell(&self, index: usize, target: u8) {
self.regs.ring_doorbell(index, target);
}
}
impl Device for Xhci {
unsafe fn init(&'static 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);
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.handle_device_attached(port).ok();
}
}
Ok(())
}
unsafe fn init_irq(&'static self) -> Result<(), Error> {
log::info!("Init USB xHCI IRQ");
Ok(())
}
fn display_name(&self) -> &'static str {
"USB xHCI"
}
}
impl InterruptHandler for Xhci {
fn handle_irq(&self, _vector: Option<usize>) -> bool {
if let Some(status) = self.regs.handle_interrupt() {
if status.event_interrupt() {
self.handle_event();
}
true
} else {
false
}
}
}
pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
// TODO Chip Hardware Reset
let bar0 = info
.config_space
.bar(0)
.expect("xHCI doesn't have BAR0 configured")
.as_memory()
.expect("xHCI's BAR0 is not memory-type");
let mut cmd = PciCommandRegister::from_bits_retain(info.config_space.command());
cmd &= !(PciCommandRegister::DISABLE_INTERRUPTS | PciCommandRegister::ENABLE_IO);
cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER;
info.config_space.set_command(cmd.bits());
let regs = unsafe { xhci_lib::Registers::new(bar0.try_into().unwrap(), Mapper::new()) };
let xhci = Box::leak(Box::new(Xhci::new(regs)?));
info.init_interrupts(PreferredInterruptMode::Msi)?;
info.map_interrupt(InterruptAffinity::Any, xhci)?;
Ok(xhci)
}

View File

@ -0,0 +1,88 @@
use alloc::sync::Arc;
use libk_mm::{address::PhysicalAddress, PageBox};
use ygg_driver_usb::{
pipe::{
control::{ControlTransferSetup, UsbControlPipe},
interrupt::UsbInterruptPipe,
UsbGenericPipe,
},
UsbDirection, UsbTransfer,
};
use yggdrasil_abi::error::Error;
use crate::{ring::TransferRing, Xhci};
pub struct ControlPipe {
xhci: &'static Xhci,
ring: Arc<TransferRing>,
}
#[allow(unused)]
pub struct InterruptPipe {
xhci: &'static Xhci,
slot_id: u8,
endpoint_id: u8,
dci: u8,
input: Option<Arc<TransferRing>>,
output: Option<Arc<TransferRing>>,
}
impl UsbGenericPipe for ControlPipe {
fn complete_transfer(&self, transfer: UsbTransfer) {
self.ring.complete_transfer(transfer)
}
}
impl UsbControlPipe for ControlPipe {
fn start_transfer(
&self,
setup: ControlTransferSetup,
data: Option<(PhysicalAddress, usize, UsbDirection)>,
) -> Result<UsbTransfer, Error> {
self.ring.start_control_transfer(self.xhci, setup, data)
}
}
impl ControlPipe {
pub fn new(xhci: &'static Xhci, _slot_id: u8, ring: Arc<TransferRing>) -> Self {
Self { xhci, ring }
}
}
impl UsbGenericPipe for InterruptPipe {
fn complete_transfer(&self, transfer: UsbTransfer) {
match transfer.direction {
UsbDirection::Out => todo!(),
UsbDirection::In => todo!(),
}
}
}
impl UsbInterruptPipe for InterruptPipe {
fn start_read(&self, buffer: &mut PageBox<[u8]>) -> Result<UsbTransfer, Error> {
let input = self.input.as_ref().unwrap();
input.start_interrupt_in_transfer(self.xhci, buffer)
}
}
impl InterruptPipe {
pub fn input(
xhci: &'static Xhci,
slot_id: u8,
endpoint_id: u8,
dci: u8,
ring: Arc<TransferRing>,
) -> Self {
Self {
xhci,
slot_id,
endpoint_id,
dci,
input: Some(ring),
output: None,
}
}
}

240
driver/usb/xhci/src/regs.rs Normal file
View File

@ -0,0 +1,240 @@
use core::{cell::UnsafeCell, num::NonZeroUsize};
use alloc::{sync::Arc, vec::Vec};
use libk_mm::{
address::{AsPhysicalAddress, IntoRaw, PhysicalAddress},
device::RawDeviceMemoryMapping,
PageBox,
};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use xhci_lib::{
accessor::{array, marker},
registers::{
operational::UsbStatusRegister, Capability, Doorbell, InterrupterRegisterSet, Operational,
PortRegisterSet,
},
};
use crate::ring::{CommandRing, EventRing, EventRingSegmentTable, GenericRing};
#[derive(Clone)]
pub struct Mapper {
mappings: Vec<Arc<RawDeviceMemoryMapping>>,
}
pub struct LockedArray<T> {
array: UnsafeCell<array::Generic<T, Mapper, marker::ReadWrite>>,
locks: Vec<IrqSafeRwLock<()>>,
}
unsafe impl<T> Sync for LockedArray<T> {}
unsafe impl<T> Send for LockedArray<T> {}
pub struct Regs {
operational: IrqSafeRwLock<Operational<Mapper>>,
interrupters: IrqSafeRwLock<InterrupterRegisterSet<Mapper>>,
capability: Capability<Mapper>,
doorbells: LockedArray<Doorbell>,
pub ports: LockedArray<PortRegisterSet>,
}
impl<T> LockedArray<T> {
#[inline]
#[allow(clippy::mut_from_ref)]
unsafe fn get_mut(&self) -> &mut array::Generic<T, Mapper, marker::ReadWrite> {
&mut *self.array.get()
}
pub fn update<U: FnOnce(&mut T)>(&self, index: usize, f: U) {
let _guard = self.locks[index].write();
unsafe { self.get_mut() }.update_volatile_at(index, f);
}
pub fn read(&self, index: usize) -> T {
let _guard = self.locks[index].read();
unsafe { self.get_mut() }.read_volatile_at(index)
}
}
impl<T> From<array::Generic<T, Mapper, marker::ReadWrite>> for LockedArray<T> {
fn from(value: array::Generic<T, Mapper, marker::ReadWrite>) -> Self {
let locks = Vec::from_iter((0..value.len()).map(|_| IrqSafeRwLock::new(())));
Self {
array: UnsafeCell::new(value),
locks,
}
}
}
impl From<xhci_lib::Registers<Mapper>> for Regs {
fn from(value: xhci_lib::Registers<Mapper>) -> Self {
Self {
operational: IrqSafeRwLock::new(value.operational),
capability: value.capability,
interrupters: IrqSafeRwLock::new(value.interrupter_register_set),
doorbells: LockedArray::from(value.doorbell),
ports: LockedArray::from(value.port_register_set),
}
}
}
impl Regs {
pub fn reset(&self) {
let mut o = self.operational.write();
// TODO Get ownership from firmware
// Stop the controller
o.usbcmd.update_volatile(|u| {
u.clear_run_stop();
});
while !o.usbsts.read_volatile().hc_halted() {
core::hint::spin_loop();
}
// Reset the controller
o.usbcmd.update_volatile(|u| {
u.set_host_controller_reset();
});
while o.usbcmd.read_volatile().host_controller_reset()
|| o.usbsts.read_volatile().controller_not_ready()
{
core::hint::spin_loop();
}
}
pub fn max_slot_count(&self) -> usize {
self.capability
.hcsparams1
.read_volatile()
.number_of_device_slots() as _
}
pub fn set_max_slot_count(&self) -> usize {
let device_slot_count = self.max_slot_count();
let mut o = self.operational.write();
// Set max slots enabled
o.config.update_volatile(|u| {
u.set_max_device_slots_enabled(device_slot_count as _);
});
device_slot_count as _
}
pub fn context_size(&self) -> usize {
match self.capability.hccparams1.read_volatile().context_size() {
true => 64,
false => 32,
}
}
pub fn port_count(&self) -> usize {
self.capability.hcsparams1.read_volatile().number_of_ports() as _
}
pub fn configure(
&self,
dcbaa: &PageBox<[PhysicalAddress]>,
cmd_ring: &CommandRing,
evt_ring: &EventRing,
erst: &EventRingSegmentTable,
) {
let mut o = self.operational.write();
let mut i = self.interrupters.write();
o.dcbaap.update_volatile(|u| unsafe {
u.set(dcbaa.as_physical_address().into_raw());
});
o.crcr.update_volatile(|u| {
u.set_command_ring_pointer(cmd_ring.base().into_raw());
u.set_ring_cycle_state();
});
let mut intr0 = i.interrupter_mut(0);
intr0.erstsz.update_volatile(|u| {
u.set(erst.capacity().try_into().unwrap());
});
intr0.erdp.update_volatile(|u| {
log::debug!("::: Dequeue Pointer: {:#x}", evt_ring.dequeue_pointer());
u.set_event_ring_dequeue_pointer(evt_ring.dequeue_pointer().into_raw());
});
intr0.erstba.update_volatile(|u| {
u.set(erst.physical_address().into_raw());
});
// intr0.imod.update_volatile(|u| {
// u.set_interrupt_moderation_interval(0)
// .set_interrupt_moderation_counter(0);
// });
intr0.iman.update_volatile(|u| {
u.set_interrupt_enable();
});
o.usbcmd.update_volatile(|u| {
u.set_interrupter_enable().set_run_stop();
});
}
pub fn handle_interrupt(&self) -> Option<UsbStatusRegister> {
let mut o = self.operational.write();
let mut i = self.interrupters.write();
let status = o.usbsts.read_volatile();
if !status.event_interrupt() {
return None;
}
o.usbsts.write_volatile(status);
if status.host_system_error() {
return Some(status);
}
// Acknowledge interrupts
let mut intr0 = i.interrupter_mut(0);
intr0.iman.update_volatile(|u| {
u.set_0_interrupt_pending();
});
Some(status)
}
pub fn set_interrupter_0_dequeue_pointer(&self, pointer: PhysicalAddress) {
let mut i = self.interrupters.write();
i.interrupter_mut(0).erdp.update_volatile(|u| {
u.set_event_ring_dequeue_pointer(pointer.into_raw());
u.clear_event_handler_busy();
});
}
pub fn ring_doorbell(&self, index: usize, target: u8) {
self.doorbells.update(index, |u| {
u.set_doorbell_target(target);
});
}
}
impl Mapper {
pub fn new() -> Self {
Self {
mappings: Vec::new(),
}
}
}
impl xhci_lib::accessor::Mapper for Mapper {
unsafe fn map(&mut self, phys_start: usize, bytes: usize) -> NonZeroUsize {
let mapping = RawDeviceMemoryMapping::map(phys_start as u64, bytes, Default::default())
.expect("Could not map an USB xHCI region");
let address = mapping.address;
self.mappings.push(Arc::new(mapping));
NonZeroUsize::new_unchecked(address)
}
fn unmap(&mut self, _virt_start: usize, _bytes: usize) {
// TODO
}
}

656
driver/usb/xhci/src/ring.rs Normal file
View File

@ -0,0 +1,656 @@
use core::{
future::poll_fn,
mem::{size_of, MaybeUninit},
sync::atomic::{AtomicU64, Ordering},
task::Poll,
};
use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
use libk_mm::{
address::{AsPhysicalAddress, FromRaw, IntoRaw, PhysicalAddress},
PageBox,
};
use libk_util::{
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock},
waker::QueueWaker,
};
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields,
registers::InMemoryRegister,
};
use xhci_lib::context::Input;
use ygg_driver_usb::{
pipe::control::ControlTransferSetup, UsbDirection, UsbTransfer, UsbTransferStatus,
UsbTransferToken,
};
use yggdrasil_abi::error::Error;
#[derive(Debug, Clone, Copy)]
pub struct CommandReply {
pub status: u32,
pub slot_id: u8,
}
impl CommandReply {
pub fn completion_code(&self) -> u8 {
(self.status >> 24) as u8
}
}
pub enum Event {
PortChange(usize),
CommandCompletion {
address: PhysicalAddress,
reply: CommandReply,
},
Transfer {
address: PhysicalAddress,
slot_id: u8,
endpoint_id: u8,
status: u32,
},
}
pub trait CommandExecutor {
fn ring_doorbell(&self, index: usize, target: u8);
}
register_bitfields![
u32,
pub TrbFlags [
CYCLE OFFSET(0) NUMBITS(1) [],
LINKSEG OFFSET(1) NUMBITS(1) [],
INTERRUPT_ON_COMPLETION OFFSET(5) NUMBITS(1) [],
IMMEDIATE_DATA OFFSET(6) NUMBITS(1) [],
TYPE OFFSET(10) NUMBITS(6) [
// Transfer ring types
NORMAL = 1,
SETUP = 2,
DATA = 3,
STATUS = 4,
ISOCH = 5,
LINK = 6,
EVENT = 7,
NOOP = 8,
// Command ring types
COMMAND_ENABLE_SLOT = 9,
COMMAND_DISABLE_SLOT = 10,
COMMAND_ADDRESS_DEVICE = 11,
COMMAND_CONFIGURE_ENDPOINT = 12,
// Event ring types
EVENT_XFER = 32,
EVENT_CMD_COMPLETE = 33,
EVENT_PORT_CHANGE = 34,
EVENT_BW_REQUEST = 35,
EVENT_DOORBELL = 36,
EVENT_HOST_CTRL = 37,
EVENT_DEVICE_NOTIFY = 38,
EVENT_MFINDEX_WRAP = 39
],
TRANSFER_TYPE OFFSET(16) NUMBITS(2) [
OUT = 2,
IN = 3,
],
TRANSFER_DIRECTION OFFSET(16) NUMBITS(1) [
OUT = 0,
IN = 1,
],
ENDPOINT_ID OFFSET(16) NUMBITS(4) [],
SLOT_TYPE OFFSET(16) NUMBITS(4) [],
SLOT_ID OFFSET(24) NUMBITS(8) [],
],
];
#[repr(C, align(16))]
pub struct Trb {
pub address: PhysicalAddress,
pub status: u32,
pub flags: InMemoryRegister<u32, TrbFlags::Register>,
}
#[repr(C, align(16))]
pub struct EventRingSegment {
address: PhysicalAddress,
// Number of TRBs supported by the ring segment. Valid values are 16 to 4096
size: u16,
_0: u16,
_1: u32,
}
pub struct EventRingSegmentTable {
entries: PageBox<[EventRingSegment]>,
}
impl EventRingSegmentTable {
pub fn for_event_rings(rings: &[&EventRing]) -> Result<Self, Error> {
let entries = PageBox::from_iter_exact(rings.iter().map(|ring| EventRingSegment {
address: ring.base(),
size: ring.capacity().try_into().unwrap(),
_0: 0,
_1: 0,
}))?;
for entry in entries.iter() {
log::debug!("ERST... = {:#x}", entry.address);
}
Ok(Self { entries })
}
pub fn physical_address(&self) -> PhysicalAddress {
unsafe { self.entries.as_physical_address() }
}
pub fn capacity(&self) -> usize {
self.entries.len()
}
}
pub trait GenericRing {
fn capacity(&self) -> usize;
fn base(&self) -> PhysicalAddress;
}
struct EventRingInner {
trbs: PageBox<[MaybeUninit<Trb>]>,
dequeue_index: usize,
cycle_bit: bool,
}
struct CommandRingInner {
trbs: PageBox<[MaybeUninit<Trb>]>,
enqueue_index: usize,
#[allow(unused)]
dequeue_index: usize,
cycle_bit: bool,
}
pub struct EventRing {
inner: IrqSafeSpinlock<EventRingInner>,
capacity: usize,
}
struct TransferRingInner {
trbs: PageBox<[MaybeUninit<Trb>]>,
enqueue_index: usize,
dequeue_index: usize,
cycle_bit: bool,
}
pub struct CommandRing {
inner: IrqSafeSpinlock<CommandRingInner>,
// TODO maybe use Vec of "slots"?
completions: IrqSafeRwLock<BTreeMap<PhysicalAddress, CommandReply>>,
completion_notify: QueueWaker,
capacity: usize,
}
impl GenericRing for EventRing {
fn base(&self) -> PhysicalAddress {
unsafe { self.inner.lock().trbs.as_physical_address() }
}
fn capacity(&self) -> usize {
self.capacity
}
}
impl EventRingInner {
fn try_dequeue(&mut self) -> Option<Event> {
let trb = unsafe { self.trbs[self.dequeue_index].assume_init_ref() };
// TRB cannot be consumed -- its cycle bit not toggled
let trb_cycle = trb.flags.matches_all(TrbFlags::CYCLE::SET);
if trb_cycle != self.cycle_bit {
return None;
}
self.dequeue_index += 1;
if self.dequeue_index == self.trbs.len() {
self.dequeue_index = 0;
self.cycle_bit = !self.cycle_bit;
}
match trb.flags.read_as_enum(TrbFlags::TYPE) {
Some(TrbFlags::TYPE::Value::EVENT_PORT_CHANGE) => Some(Event::PortChange(
IntoRaw::<usize>::into_raw(trb.address) & 0xFFusize,
)),
Some(TrbFlags::TYPE::Value::EVENT_CMD_COMPLETE) => Some(Event::CommandCompletion {
address: trb.address,
reply: CommandReply {
status: trb.status,
slot_id: trb.flags.read(TrbFlags::SLOT_ID) as _,
},
}),
Some(TrbFlags::TYPE::Value::EVENT_XFER) => Some(Event::Transfer {
address: trb.address,
slot_id: trb.flags.read(TrbFlags::SLOT_ID) as _,
endpoint_id: trb.flags.read(TrbFlags::ENDPOINT_ID) as _,
status: trb.status,
}),
e => {
log::debug!("Unknown TRB ty: {:?}", e);
None
}
}
}
}
impl EventRing {
pub fn new(capacity: usize) -> Result<Self, Error> {
let trbs = PageBox::new_zeroed_slice(capacity)?;
Ok(Self {
inner: IrqSafeSpinlock::new(EventRingInner {
trbs,
dequeue_index: 0,
cycle_bit: true,
}),
capacity,
})
}
pub fn try_dequeue(&self) -> Option<Event> {
self.inner.lock().try_dequeue()
}
pub fn dequeue_pointer(&self) -> PhysicalAddress {
let i = self.inner.lock();
unsafe { i.trbs.as_physical_address() }.add(i.dequeue_index * size_of::<Trb>())
}
}
impl GenericRing for CommandRing {
fn base(&self) -> PhysicalAddress {
unsafe { self.inner.lock().trbs.as_physical_address() }
}
fn capacity(&self) -> usize {
self.capacity
}
}
impl CommandRingInner {
fn enqueue(&mut self, trb: Trb) -> PhysicalAddress {
trb.flags.modify(TrbFlags::CYCLE.val(self.cycle_bit as _));
self.trbs[self.enqueue_index].write(trb);
let address =
unsafe { self.trbs.as_physical_address() }.add(self.enqueue_index * size_of::<Trb>());
// Move to the next TRB slot
self.enqueue_index += 1;
if self.enqueue_index >= self.trbs.len() {
todo!();
}
address
}
}
impl CommandRing {
pub fn new(capacity: usize) -> Result<Self, Error> {
let mut trbs = PageBox::new_zeroed_slice(capacity)?;
let base = unsafe { trbs.as_physical_address() };
let last: &mut Trb = unsafe { trbs.last_mut().unwrap().assume_init_mut() };
last.address = base;
last.flags
.write(TrbFlags::TYPE::LINK + TrbFlags::LINKSEG::SET + TrbFlags::CYCLE::SET);
Ok(Self {
inner: IrqSafeSpinlock::new(CommandRingInner {
trbs,
enqueue_index: 0,
dequeue_index: 0,
cycle_bit: true,
}),
completions: IrqSafeRwLock::new(BTreeMap::new()),
completion_notify: QueueWaker::new(),
capacity,
})
}
pub fn enqueue(&self, trb: Trb) -> PhysicalAddress {
let mut inner = self.inner.lock();
let address = inner.enqueue(trb);
log::info!("CommandRing::enqueue({:#x})", address);
address
}
pub async fn address_device<E: CommandExecutor>(
&self,
executor: &E,
slot_id: u8,
input: &mut PageBox<Input<8>>,
) -> Result<(), Error> {
let reply = self
.submit_and_wait(
executor,
Trb {
address: unsafe { input.as_physical_address() },
status: 0,
flags: InMemoryRegister::new(
(TrbFlags::SLOT_ID.val(slot_id as _)
+ TrbFlags::TYPE::COMMAND_ADDRESS_DEVICE)
.into(),
),
},
)
.await;
if reply.completion_code() == 1 {
Ok(())
} else {
Err(Error::InvalidOperation)
}
}
pub async fn configure_endpoint<E: CommandExecutor>(
&self,
executor: &E,
slot_id: u8,
input: &mut PageBox<Input<8>>,
) -> Result<(), Error> {
let reply = self
.submit_and_wait(
executor,
Trb {
address: unsafe { input.as_physical_address() },
status: 0,
flags: InMemoryRegister::new(
(TrbFlags::SLOT_ID.val(slot_id as _)
+ TrbFlags::TYPE::COMMAND_CONFIGURE_ENDPOINT)
.into(),
),
},
)
.await;
if reply.completion_code() == 1 {
Ok(())
} else {
Err(Error::InvalidOperation)
}
}
pub async fn enable_slot<E: CommandExecutor>(&self, executor: &E) -> Result<u8, Error> {
let reply = self
.submit_and_wait(
executor,
Trb {
address: PhysicalAddress::ZERO,
status: 0,
flags: InMemoryRegister::new(
(TrbFlags::SLOT_TYPE.val(0) + TrbFlags::TYPE::COMMAND_ENABLE_SLOT).into(),
),
},
)
.await;
if reply.completion_code() == 1 {
Ok(reply.slot_id)
} else {
Err(Error::InvalidOperation)
}
}
pub async fn submit_and_wait<E: CommandExecutor>(
&self,
executor: &E,
trb: Trb,
) -> CommandReply {
let token = self.enqueue(trb);
executor.ring_doorbell(0, 0);
poll_fn(|cx| {
self.completion_notify.register(cx.waker());
if let Some(status) = self.get_completion(token) {
self.completion_notify.remove(cx.waker());
Poll::Ready(status)
} else {
Poll::Pending
}
})
.await
}
pub fn get_completion(&self, address: PhysicalAddress) -> Option<CommandReply> {
self.completions.write().remove(&address)
}
pub fn notify(&self, address: PhysicalAddress, reply: CommandReply) {
log::info!("CommandRing::notify({:#x}, {:#x?})", address, reply);
self.completions.write().insert(address, reply);
self.completion_notify.wake_all();
}
}
impl GenericRing for TransferRing {
fn base(&self) -> PhysicalAddress {
unsafe { self.inner.lock().trbs.as_physical_address() }
}
fn capacity(&self) -> usize {
self.capacity
}
}
impl TransferRingInner {
fn enqueue(&mut self, trb: Trb) -> PhysicalAddress {
trb.flags.modify(TrbFlags::CYCLE.val(self.cycle_bit as _));
self.trbs[self.enqueue_index].write(trb);
let address =
unsafe { self.trbs.as_physical_address() }.add(self.enqueue_index * size_of::<Trb>());
// Move to the next TRB slot
self.enqueue_index += 1;
if self.enqueue_index >= self.trbs.len() {
todo!();
}
address
}
}
pub struct TransferRing {
inner: IrqSafeSpinlock<TransferRingInner>,
capacity: usize,
// TODO this is inefficient and ugly
pending_trbs: IrqSafeRwLock<BTreeMap<PhysicalAddress, UsbTransferToken>>,
completions: IrqSafeRwLock<BTreeMap<UsbTransferToken, Arc<UsbTransferStatus>>>,
slot_id: u8,
ep_id: u8,
transfer_id: AtomicU64,
}
impl TransferRing {
pub fn new(slot_id: u8, ep_id: u8, capacity: usize) -> Result<Self, Error> {
let mut trbs = PageBox::new_zeroed_slice(capacity)?;
let base = unsafe { trbs.as_physical_address() };
let last: &mut Trb = unsafe { trbs.last_mut().unwrap().assume_init_mut() };
last.address = base;
last.flags
.write(TrbFlags::TYPE::LINK + TrbFlags::LINKSEG::SET + TrbFlags::CYCLE::SET);
Ok(Self {
inner: IrqSafeSpinlock::new(TransferRingInner {
trbs,
enqueue_index: 0,
dequeue_index: 0,
cycle_bit: true,
}),
completions: IrqSafeRwLock::new(BTreeMap::new()),
pending_trbs: IrqSafeRwLock::new(BTreeMap::new()),
slot_id,
ep_id,
capacity,
transfer_id: AtomicU64::new(0),
})
}
pub fn enqueue(&self, trb: Trb) -> PhysicalAddress {
let mut inner = self.inner.lock();
let address = inner.enqueue(trb);
address
}
pub fn new_transfer(&self) -> UsbTransferToken {
let id = self.transfer_id.fetch_add(1, Ordering::AcqRel);
UsbTransferToken(id)
}
pub fn complete_transfer(&self, transfer: UsbTransfer) {
let mut pending = self.pending_trbs.write();
for trb in transfer.elements {
pending.remove(&trb);
}
self.completions.write().remove(&transfer.id);
}
pub fn enqueue_transfer<I: IntoIterator<Item = Trb>>(
&self,
trbs: I,
length: usize,
) -> UsbTransfer {
let mut pending = self.pending_trbs.write();
let id = self.new_transfer();
let mut addresses = Vec::new();
for trb in trbs {
let address = self.enqueue(trb);
addresses.push(address);
pending.insert(address, id);
}
let status = Arc::new(UsbTransferStatus::new());
let transfer = UsbTransfer {
id,
length,
direction: UsbDirection::In,
elements: addresses,
status: status.clone(),
};
self.completions.write().insert(id, status);
transfer
}
fn control_setup_trb(ctl: ControlTransferSetup) -> Trb {
let word0 = (ctl.bm_request_type as u32)
| ((ctl.b_request as u32) << 8)
| ((ctl.w_value as u32) << 16);
let word1 = (ctl.w_index as u32) | ((ctl.w_length as u32) << 16);
Trb {
address: PhysicalAddress::from_raw((word0 as u64) | ((word1 as u64) << 32)),
status: 8,
flags: InMemoryRegister::new(
(TrbFlags::TYPE::SETUP
+ TrbFlags::IMMEDIATE_DATA::SET
+ TrbFlags::TRANSFER_TYPE::IN)
.into(),
),
}
}
fn data_trb(address: PhysicalAddress, len: usize, direction: UsbDirection) -> Trb {
let dir = match direction {
UsbDirection::Out => TrbFlags::TRANSFER_DIRECTION::OUT,
UsbDirection::In => TrbFlags::TRANSFER_DIRECTION::IN,
};
Trb {
address,
status: len.try_into().unwrap(),
flags: InMemoryRegister::new((TrbFlags::TYPE::DATA + dir).into()),
}
}
fn control_status_trb() -> Trb {
Trb {
address: PhysicalAddress::ZERO,
status: 0,
flags: InMemoryRegister::new(
(TrbFlags::TYPE::STATUS
+ TrbFlags::TRANSFER_DIRECTION::IN
+ TrbFlags::INTERRUPT_ON_COMPLETION::SET)
.into(),
),
}
}
pub fn start_interrupt_in_transfer<E: CommandExecutor>(
&self,
executor: &E,
buffer: &mut PageBox<[u8]>,
) -> Result<UsbTransfer, Error> {
let data_trb = Trb {
address: unsafe { buffer.as_physical_address() },
status: buffer.len() as _,
flags: InMemoryRegister::new(
(TrbFlags::TRANSFER_DIRECTION::IN
+ TrbFlags::INTERRUPT_ON_COMPLETION::SET
+ TrbFlags::TYPE::NORMAL)
.into(),
),
};
let transfer = self.enqueue_transfer(core::iter::once(data_trb), buffer.len());
executor.ring_doorbell(self.slot_id as _, self.ep_id);
Ok(transfer)
}
pub fn start_control_transfer<E: CommandExecutor>(
&self,
executor: &E,
setup: ControlTransferSetup,
buffer: Option<(PhysicalAddress, usize, UsbDirection)>,
) -> Result<UsbTransfer, Error> {
let setup_trb = Self::control_setup_trb(setup);
let data_trb = buffer.map(|(address, len, dir)| Self::data_trb(address, len, dir));
let status_trb = Self::control_status_trb();
let transfer = self.enqueue_transfer(
core::iter::once(setup_trb)
.chain(data_trb)
.chain(core::iter::once(status_trb)),
0,
);
executor.ring_doorbell(self.slot_id as _, self.ep_id);
Ok(transfer)
}
pub fn dequeue_pointer(&self) -> PhysicalAddress {
let inner = self.inner.lock();
unsafe { inner.trbs.as_physical_address() }.add(inner.dequeue_index * size_of::<Trb>())
}
pub fn notify(&self, address: PhysicalAddress, value: u32) {
if value == 0 {
return;
}
let completions = self.completions.read();
if let Some(&token) = self.pending_trbs.read().get(&address) {
let Some(status) = completions.get(&token) else {
log::warn!(
"Notification received for non-existent transfer: {:?}",
token
);
return;
};
status.signal(value);
}
}
}

View File

@ -3,7 +3,14 @@
#![cfg_attr(not(test), no_std)]
#![allow(clippy::new_ret_no_self, clippy::new_without_default)]
#![deny(missing_docs)]
#![feature(if_let_guard, maybe_uninit_slice, trait_alias, let_chains, new_uninit)]
#![feature(
if_let_guard,
maybe_uninit_slice,
trait_alias,
let_chains,
new_uninit,
trait_upcasting
)]
#[cfg(test)]
extern crate hosted_tests;

View File

@ -4,6 +4,7 @@
slice_ptr_get,
step_trait,
const_trait_impl,
maybe_uninit_as_bytes,
effects
)]
#![no_std]
@ -66,11 +67,17 @@ pub struct PageBox<T: ?Sized> {
impl<T> PageBox<T> {
#[inline]
fn alloc_slice(count: usize) -> Result<(PhysicalAddress, usize), Error> {
fn alloc_slice(count: usize, zeroed: bool) -> Result<(PhysicalAddress, usize), Error> {
// TODO hardcoded page sizes
let layout = Layout::array::<T>(count).unwrap();
let page_count = (layout.size() + L3_PAGE_SIZE - 1) / L3_PAGE_SIZE;
Ok((phys::alloc_pages_contiguous(page_count)?, page_count))
let base = phys::alloc_pages_contiguous(page_count)?;
if zeroed {
let ptr = base.virtualize() as *mut u8;
let slice = unsafe { core::slice::from_raw_parts_mut(ptr, page_count * L3_PAGE_SIZE) };
slice.fill(0);
}
Ok((base, page_count))
}
#[inline]
@ -96,7 +103,7 @@ impl<T> PageBox<T> {
where
T: Copy,
{
let (base, page_count) = Self::alloc_slice(count)?;
let (base, page_count) = Self::alloc_slice(count, false)?;
let base_virt_ptr = base.virtualize() as *mut T;
let value = core::ptr::slice_from_raw_parts_mut(base_virt_ptr, count);
@ -130,7 +137,16 @@ impl<T> PageBox<T> {
}
pub fn new_uninit_slice(count: usize) -> Result<PageBox<[MaybeUninit<T>]>, Error> {
let (base, page_count) = PageBox::<MaybeUninit<T>>::alloc_slice(count)?;
let (base, page_count) = PageBox::<MaybeUninit<T>>::alloc_slice(count, false)?;
let base_virt_ptr = base.virtualize() as *mut MaybeUninit<T>;
let value = core::ptr::slice_from_raw_parts_mut(base_virt_ptr, count);
let result = PageBox { value, page_count };
result.trace_created();
Ok(result)
}
pub fn new_zeroed_slice(count: usize) -> Result<PageBox<[MaybeUninit<T>]>, Error> {
let (base, page_count) = PageBox::<MaybeUninit<T>>::alloc_slice(count, true)?;
let base_virt_ptr = base.virtualize() as *mut MaybeUninit<T>;
let value = core::ptr::slice_from_raw_parts_mut(base_virt_ptr, count);
let result = PageBox { value, page_count };
@ -166,6 +182,21 @@ impl<T: ?Sized> PageBox<T> {
}
}
impl<T> PageBox<[T]> {
pub fn from_iter_exact<I: IntoIterator<Item = T>>(it: I) -> Result<Self, Error>
where
I::IntoIter: ExactSizeIterator,
{
let it = it.into_iter();
let mut slice = PageBox::new_uninit_slice(it.len())?;
for (i, item) in it.enumerate() {
slice[i].write(item);
}
let slice = unsafe { slice.assume_init_slice() };
Ok(slice)
}
}
impl<T> PageBox<MaybeUninit<T>> {
/// Consumes the [PageBox], returning a new one with [MaybeUninit] removed.
///
@ -184,6 +215,15 @@ impl<T> PageBox<MaybeUninit<T>> {
PageBox { value, page_count }
}
pub unsafe fn into_byte_slice(self) -> PageBox<[u8]> {
let page_count = self.page_count;
let value = MaybeUninit::slice_assume_init_mut(MaybeUninit::as_bytes_mut(&mut *self.value));
core::mem::forget(self);
PageBox { value, page_count }
}
}
impl<T> PageBox<[MaybeUninit<T>]> {
@ -221,6 +261,18 @@ impl<T> PageBox<[MaybeUninit<T>]> {
pub unsafe fn assume_init_slice_mut(&mut self) -> &mut [T] {
MaybeUninit::slice_assume_init_mut(self.deref_mut())
}
/// Fills a slice of MaybeUninit<T> with zeroes.
///
/// # Safety
///
/// Unsafe: will not drop possibly previously written data. Only meant for [Copy] and other
/// trivial types.
pub unsafe fn zero(p: &mut Self) {
let ptr = p.as_mut_ptr() as *mut u8;
let slice = core::slice::from_raw_parts_mut(ptr, p.page_count * L3_PAGE_SIZE);
slice.fill(0);
}
}
impl<T: ?Sized> AsPhysicalAddress for PageBox<T> {

View File

@ -12,7 +12,7 @@
extern crate alloc;
use api::__signal_process_group;
use kernel_arch::KernelTableManagerImpl;
use kernel_arch::{Architecture, ArchitectureImpl, KernelTableManagerImpl};
use libk_mm::phys::GlobalPhysicalAllocator;
pub(crate) mod api {
@ -38,10 +38,21 @@ pub mod types;
pub type TaskContextImpl =
kernel_arch::TaskContextImpl<KernelTableManagerImpl, GlobalPhysicalAllocator>;
use sched::CpuQueue;
pub use types::{AtomicThreadState, ThreadAffinity, ThreadId, ThreadState};
use yggdrasil_abi::process::Signal;
/// Returns local CPU index
#[inline]
pub fn cpu_index() -> u32 {
ArchitectureImpl::cpu_index::<CpuQueue>()
}
pub fn cpu_count() -> usize {
ArchitectureImpl::cpu_count()
}
pub fn signal_process_group(group_id: u32, signal: Signal) {
unsafe { __signal_process_group(group_id, signal) }
}

View File

@ -1,13 +1,15 @@
use core::{
cell::UnsafeCell,
future::poll_fn,
ops::{Deref, DerefMut},
sync::atomic::{AtomicU32, Ordering},
sync::atomic::{AtomicBool, AtomicU32, Ordering},
task::{Context, Poll},
};
use alloc::sync::Arc;
use crossbeam_queue::ArrayQueue;
use kernel_arch::task::Scheduler;
use libk_util::sync::LockMethod;
use libk_util::{sync::LockMethod, waker::QueueWaker};
use yggdrasil_abi::error::Error;
use crate::{sched::CpuQueue, thread::Thread};
@ -17,6 +19,16 @@ struct ThreadedMutexInner {
lock: AtomicU32,
}
pub struct AsyncMutex<T> {
value: UnsafeCell<T>,
waker: QueueWaker,
lock: AtomicBool,
}
pub struct AsyncMutexGuard<'a, T> {
mutex: &'a AsyncMutex<T>,
}
pub struct Mutex<T> {
value: UnsafeCell<T>,
lock: ThreadedMutexInner,
@ -27,6 +39,74 @@ pub struct MutexGuard<'a, T> {
lock: &'a ThreadedMutexInner,
}
impl<T> AsyncMutex<T> {
pub fn new(value: T) -> Self {
Self {
value: UnsafeCell::new(value),
waker: QueueWaker::new(),
lock: AtomicBool::new(false),
}
}
pub fn try_lock(&self) -> bool {
self.lock
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
}
pub fn poll_lock(&self, cx: &mut Context<'_>) -> Poll<AsyncMutexGuard<T>> {
self.waker.register(cx.waker());
if self.try_lock() {
self.waker.remove(cx.waker());
return Poll::Ready(AsyncMutexGuard { mutex: self });
}
Poll::Pending
}
pub async fn lock(&self) -> AsyncMutexGuard<T> {
poll_fn(|cx| self.poll_lock(cx)).await
}
pub unsafe fn force_unlock(&self) {
self.lock.store(false, Ordering::Release);
self.waker.wake_one();
}
#[inline]
pub fn get(&self) -> *mut T {
self.value.get()
}
}
unsafe impl<T> Sync for AsyncMutex<T> {}
unsafe impl<'a, T> Send for AsyncMutexGuard<'a, T> {}
unsafe impl<'a, T> Sync for AsyncMutexGuard<'a, T> {}
impl<'a, T> Deref for AsyncMutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.mutex.get() }
}
}
impl<'a, T> DerefMut for AsyncMutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.mutex.get() }
}
}
impl<'a, T> Drop for AsyncMutexGuard<'a, T> {
fn drop(&mut self) {
unsafe {
self.mutex.force_unlock();
}
}
}
impl ThreadedMutexInner {
const UNLOCKED: u32 = 0;
const LOCKED: u32 = 1;

View File

@ -16,12 +16,9 @@
trait_alias
)]
use arch::Cpu;
use kernel_arch::{Architecture, ArchitectureImpl, CpuImpl};
extern crate alloc;
pub use libk_thread::{block, runtime};
pub use libk_thread::{block, cpu_count, cpu_index, runtime};
pub mod arch;
@ -29,17 +26,6 @@ pub mod device {
pub use libk_device::*;
}
#[inline]
pub fn cpu_count() -> usize {
ArchitectureImpl::cpu_count()
}
/// Returns local CPU index
#[inline]
pub fn cpu_index() -> u32 {
Cpu::try_local().as_deref().map(CpuImpl::id).unwrap_or(0)
}
#[repr(C)]
pub struct AlignedTo<Align, Bytes: ?Sized> {
pub align: [Align; 0],

View File

@ -198,14 +198,10 @@ impl ExternalInterruptController for IoApic {
)
} else {
// Directly mapped to a GSI
(
(irq - ISA_IRQ_OFFSET) as u32,
options.level,
options.trigger,
)
(irq - ISA_IRQ_OFFSET, options.level, options.trigger)
}
}
Irq::External(irq) => (irq as u32, options.level, options.trigger),
Irq::External(irq) => (irq, options.level, options.trigger),
Irq::Private(_) => unimplemented!(),
};

View File

@ -225,9 +225,7 @@ impl MessageInterruptController for LocalApic {
};
drop(table);
if handler.handle_irq(Some(vector)) {
break;
}
handler.handle_irq(Some(vector));
i += 1;
}

View File

@ -4,10 +4,7 @@ use core::{mem::size_of, ops::DerefMut, ptr::null_mut, sync::atomic::Ordering};
use abi::error::Error;
use acpi_lib::{mcfg::Mcfg, AcpiTables, InterruptModel};
use alloc::boxed::Box;
use device_api::{
interrupt::{Irq, MessageInterruptController},
Device,
};
use device_api::{interrupt::Irq, Device};
use git_version::git_version;
use kernel_arch_x86_64::{
mem::{
@ -305,6 +302,13 @@ impl X86_64 {
Some(0x01),
ygg_driver_ahci::probe,
);
ygg_driver_pci::register_class_driver(
"USB xHCI",
0x0C,
Some(0x03),
Some(0x30),
ygg_driver_usb_xhci::probe,
);
ygg_driver_pci::register_vendor_driver(
"Virtio PCI Network Device",
0x1AF4,
@ -487,8 +491,3 @@ impl X86_64 {
pic_slave_cmd.write(0x20);
}
}
#[no_mangle]
fn __message_interrupt_controller() -> &'static dyn MessageInterruptController {
Cpu::local().local_apic
}

View File

@ -10,12 +10,9 @@ use device_api::{
use libk::device::external_interrupt_controller;
use libk_util::sync::IrqSafeSpinlock;
use crate::{
arch::x86_64::{
intrinsics::{IoPort, IoPortAccess},
peripherals::ps2::codeset::{CODE_SET_1_00, CODE_SET_1_E0},
},
device::input,
use crate::arch::x86_64::{
intrinsics::{IoPort, IoPortAccess},
peripherals::ps2::codeset::{CODE_SET_1_00, CODE_SET_1_E0},
};
mod codeset;
@ -102,7 +99,7 @@ impl InterruptHandler for PS2Controller {
KeyboardKeyEvent::Pressed(key)
};
input::send_event(event);
ygg_driver_input::send_event(event);
}
count != 0

View File

@ -6,7 +6,6 @@ use libk_util::sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard};
pub mod bus;
pub mod display;
pub mod input;
pub mod power;
pub mod serial;
pub mod timer;

View File

@ -8,7 +8,6 @@ use memfs::MemoryFilesystem;
use vfs::{IoContext, NodeRef};
use crate::{
device::input,
fs::{FileBlockAllocator, INITRD_DATA},
proc::{self, random},
};
@ -28,6 +27,8 @@ fn setup_root() -> Result<NodeRef, Error> {
pub fn kinit() -> Result<(), Error> {
infoln!("In main");
runtime::spawn(ygg_driver_usb::bus::bus_handler())?;
ygg_driver_net_loopback::init();
ygg_driver_net_core::start_network_tasks()?;
@ -41,7 +42,7 @@ pub fn kinit() -> Result<(), Error> {
}
// Add keyboard device
devfs::add_named_char_device(&input::KEYBOARD_DEVICE, "kbd".to_owned())?;
devfs::add_named_char_device(&ygg_driver_input::KEYBOARD_DEVICE, "kbd".to_owned())?;
random::init();

View File

@ -1,5 +1,3 @@
#![feature(offset_of)]
use std::{
fs::OpenOptions,
io::{Read, Seek, SeekFrom, Write},