bus/usb: basic xHCI implementation
This commit is contained in:
parent
c4be544a9a
commit
012eb46cb9
@ -26,11 +26,14 @@ kernel-arch = { path = "arch" }
|
|||||||
|
|
||||||
# Drivers
|
# Drivers
|
||||||
ygg_driver_pci = { path = "driver/bus/pci" }
|
ygg_driver_pci = { path = "driver/bus/pci" }
|
||||||
|
ygg_driver_usb = { path = "driver/bus/usb" }
|
||||||
ygg_driver_block = { path = "driver/block/core" }
|
ygg_driver_block = { path = "driver/block/core" }
|
||||||
ygg_driver_net_core = { path = "driver/net/core" }
|
ygg_driver_net_core = { path = "driver/net/core" }
|
||||||
ygg_driver_net_loopback = { path = "driver/net/loopback" }
|
ygg_driver_net_loopback = { path = "driver/net/loopback" }
|
||||||
ygg_driver_virtio_net = { path = "driver/virtio/net", features = ["pci"] }
|
ygg_driver_virtio_net = { path = "driver/virtio/net", features = ["pci"] }
|
||||||
ygg_driver_ahci = { path = "driver/block/ahci" }
|
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" }
|
kernel-fs = { path = "driver/fs/kernel-fs" }
|
||||||
memfs = { path = "driver/fs/memfs" }
|
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" }
|
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_lib = { git = "https://github.com/alnyan/acpi.git", package = "acpi", branch = "acpi-system" }
|
||||||
acpi-system = { git = "https://github.com/alnyan/acpi-system.git" }
|
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" }
|
ygg_driver_nvme = { path = "driver/block/nvme" }
|
||||||
kernel-arch-x86_64 = { path = "arch/x86_64" }
|
kernel-arch-x86_64 = { path = "arch/x86_64" }
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ pub trait Architecture: Sized {
|
|||||||
fn idle_task() -> extern "C" fn(usize) -> !;
|
fn idle_task() -> extern "C" fn(usize) -> !;
|
||||||
|
|
||||||
fn cpu_count() -> usize;
|
fn cpu_count() -> usize;
|
||||||
|
fn cpu_index<S: Scheduler + 'static>() -> u32;
|
||||||
|
|
||||||
// Interrupt management
|
// Interrupt management
|
||||||
fn interrupt_mask() -> bool;
|
fn interrupt_mask() -> bool;
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(effects, strict_provenance, asm_const, naked_functions)]
|
#![feature(
|
||||||
|
effects,
|
||||||
|
strict_provenance,
|
||||||
|
asm_const,
|
||||||
|
naked_functions,
|
||||||
|
trait_upcasting
|
||||||
|
)]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
@ -124,6 +130,10 @@ impl Architecture for ArchitectureImpl {
|
|||||||
CPU_COUNT.load(Ordering::Acquire)
|
CPU_COUNT.load(Ordering::Acquire)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cpu_index<S: Scheduler + 'static>() -> u32 {
|
||||||
|
CpuImpl::<Self, S>::local().id()
|
||||||
|
}
|
||||||
|
|
||||||
fn interrupt_mask() -> bool {
|
fn interrupt_mask() -> bool {
|
||||||
let mut flags: u64;
|
let mut flags: u64;
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -182,12 +182,14 @@ impl InterruptHandler for AhciController {
|
|||||||
|
|
||||||
let is = regs.IS.get();
|
let is = regs.IS.get();
|
||||||
if is != 0 {
|
if is != 0 {
|
||||||
// Clear global interrupt status
|
if let Some(ports) = self.ports.try_get() {
|
||||||
regs.IS.set(u32::MAX);
|
// Clear global interrupt status
|
||||||
|
regs.IS.set(u32::MAX);
|
||||||
|
|
||||||
for &port in self.ports.get() {
|
for &port in ports {
|
||||||
if is & (1 << port.index) != 0 {
|
if is & (1 << port.index) != 0 {
|
||||||
port.handle_pending_interrupts();
|
port.handle_pending_interrupts();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ authors = ["Mark Poliakov <mark@alnyan.me>"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
|
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
|
||||||
libk-util = { path = "../../../libk/libk-util" }
|
libk-util = { path = "../../../libk/libk-util" }
|
||||||
|
libk-thread = { path = "../../../libk/libk-thread" }
|
||||||
libk-mm = { path = "../../../libk/libk-mm" }
|
libk-mm = { path = "../../../libk/libk-mm" }
|
||||||
device-api = { path = "../../../lib/device-api", features = ["derive"] }
|
device-api = { path = "../../../lib/device-api", features = ["derive"] }
|
||||||
vfs = { path = "../../../lib/vfs" }
|
vfs = { path = "../../../lib/vfs" }
|
||||||
|
@ -2,8 +2,8 @@ use core::task::Poll;
|
|||||||
|
|
||||||
use alloc::{boxed::Box, format};
|
use alloc::{boxed::Box, format};
|
||||||
use kernel_fs::devfs;
|
use kernel_fs::devfs;
|
||||||
use libk::cpu_index;
|
|
||||||
use libk_mm::address::AsPhysicalAddress;
|
use libk_mm::address::AsPhysicalAddress;
|
||||||
|
use libk_thread::cpu_index;
|
||||||
use libk_util::waker::QueueWaker;
|
use libk_util::waker::QueueWaker;
|
||||||
use ygg_driver_block::{
|
use ygg_driver_block::{
|
||||||
probe_partitions, IoOperation, IoRequest, IoSubmissionId, NgBlockDevice, NgBlockDeviceWrapper,
|
probe_partitions, IoOperation, IoRequest, IoSubmissionId, NgBlockDevice, NgBlockDeviceWrapper,
|
||||||
|
@ -17,11 +17,11 @@ use device_api::{
|
|||||||
Device,
|
Device,
|
||||||
};
|
};
|
||||||
use drive::NvmeDrive;
|
use drive::NvmeDrive;
|
||||||
use libk::{cpu_count, cpu_index, runtime};
|
|
||||||
use libk_mm::{
|
use libk_mm::{
|
||||||
address::{IntoRaw, PhysicalAddress},
|
address::{IntoRaw, PhysicalAddress},
|
||||||
device::DeviceMemoryIo,
|
device::DeviceMemoryIo,
|
||||||
};
|
};
|
||||||
|
use libk_thread::{cpu_count, cpu_index, runtime};
|
||||||
use libk_util::{
|
use libk_util::{
|
||||||
sync::{IrqGuard, IrqSafeSpinlock},
|
sync::{IrqGuard, IrqSafeSpinlock},
|
||||||
OneTimeInit,
|
OneTimeInit,
|
||||||
|
18
driver/bus/usb/Cargo.toml
Normal file
18
driver/bus/usb/Cargo.toml
Normal 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
51
driver/bus/usb/src/bus.rs
Normal 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();
|
203
driver/bus/usb/src/class_driver/mod.rs
Normal file
203
driver/bus/usb/src/class_driver/mod.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
92
driver/bus/usb/src/communication.rs
Normal file
92
driver/bus/usb/src/communication.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
146
driver/bus/usb/src/descriptor.rs
Normal file
146
driver/bus/usb/src/descriptor.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
191
driver/bus/usb/src/device.rs
Normal file
191
driver/bus/usb/src/device.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
84
driver/bus/usb/src/info.rs
Normal file
84
driver/bus/usb/src/info.rs
Normal 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
19
driver/bus/usb/src/lib.rs
Normal 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 {}
|
323
driver/bus/usb/src/pipe/control.rs
Normal file
323
driver/bus/usb/src/pipe/control.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
32
driver/bus/usb/src/pipe/interrupt.rs
Normal file
32
driver/bus/usb/src/pipe/interrupt.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
12
driver/bus/usb/src/pipe/mod.rs
Normal file
12
driver/bus/usb/src/pipe/mod.rs
Normal 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
11
driver/input/Cargo.toml
Normal 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" }
|
@ -1,15 +1,16 @@
|
|||||||
// TODO
|
#![no_std]
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
use core::task::{Context, Poll};
|
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,
|
error::Error,
|
||||||
io::{DeviceRequest, KeyboardKeyEvent},
|
io::{DeviceRequest, KeyboardKeyEvent},
|
||||||
};
|
};
|
||||||
use libk::block;
|
|
||||||
use libk_util::ring::LossyRingQueue;
|
|
||||||
use vfs::{CharDevice, FileReadiness};
|
|
||||||
|
|
||||||
pub struct KeyboardDevice;
|
pub struct KeyboardDevice;
|
||||||
|
|
22
driver/usb/xhci/Cargo.toml
Normal file
22
driver/usb/xhci/Cargo.toml
Normal 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
484
driver/usb/xhci/src/lib.rs
Normal 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)
|
||||||
|
}
|
88
driver/usb/xhci/src/pipe.rs
Normal file
88
driver/usb/xhci/src/pipe.rs
Normal 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
240
driver/usb/xhci/src/regs.rs
Normal 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
656
driver/usb/xhci/src/ring.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,14 @@
|
|||||||
#![cfg_attr(not(test), no_std)]
|
#![cfg_attr(not(test), no_std)]
|
||||||
#![allow(clippy::new_ret_no_self, clippy::new_without_default)]
|
#![allow(clippy::new_ret_no_self, clippy::new_without_default)]
|
||||||
#![deny(missing_docs)]
|
#![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)]
|
#[cfg(test)]
|
||||||
extern crate hosted_tests;
|
extern crate hosted_tests;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
slice_ptr_get,
|
slice_ptr_get,
|
||||||
step_trait,
|
step_trait,
|
||||||
const_trait_impl,
|
const_trait_impl,
|
||||||
|
maybe_uninit_as_bytes,
|
||||||
effects
|
effects
|
||||||
)]
|
)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
@ -66,11 +67,17 @@ pub struct PageBox<T: ?Sized> {
|
|||||||
|
|
||||||
impl<T> PageBox<T> {
|
impl<T> PageBox<T> {
|
||||||
#[inline]
|
#[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
|
// TODO hardcoded page sizes
|
||||||
let layout = Layout::array::<T>(count).unwrap();
|
let layout = Layout::array::<T>(count).unwrap();
|
||||||
let page_count = (layout.size() + L3_PAGE_SIZE - 1) / L3_PAGE_SIZE;
|
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]
|
#[inline]
|
||||||
@ -96,7 +103,7 @@ impl<T> PageBox<T> {
|
|||||||
where
|
where
|
||||||
T: Copy,
|
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 base_virt_ptr = base.virtualize() as *mut T;
|
||||||
let value = core::ptr::slice_from_raw_parts_mut(base_virt_ptr, count);
|
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> {
|
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 base_virt_ptr = base.virtualize() as *mut MaybeUninit<T>;
|
||||||
let value = core::ptr::slice_from_raw_parts_mut(base_virt_ptr, count);
|
let value = core::ptr::slice_from_raw_parts_mut(base_virt_ptr, count);
|
||||||
let result = PageBox { value, page_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>> {
|
impl<T> PageBox<MaybeUninit<T>> {
|
||||||
/// Consumes the [PageBox], returning a new one with [MaybeUninit] removed.
|
/// Consumes the [PageBox], returning a new one with [MaybeUninit] removed.
|
||||||
///
|
///
|
||||||
@ -184,6 +215,15 @@ impl<T> PageBox<MaybeUninit<T>> {
|
|||||||
|
|
||||||
PageBox { value, page_count }
|
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>]> {
|
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] {
|
pub unsafe fn assume_init_slice_mut(&mut self) -> &mut [T] {
|
||||||
MaybeUninit::slice_assume_init_mut(self.deref_mut())
|
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> {
|
impl<T: ?Sized> AsPhysicalAddress for PageBox<T> {
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use api::__signal_process_group;
|
use api::__signal_process_group;
|
||||||
use kernel_arch::KernelTableManagerImpl;
|
use kernel_arch::{Architecture, ArchitectureImpl, KernelTableManagerImpl};
|
||||||
use libk_mm::phys::GlobalPhysicalAllocator;
|
use libk_mm::phys::GlobalPhysicalAllocator;
|
||||||
|
|
||||||
pub(crate) mod api {
|
pub(crate) mod api {
|
||||||
@ -38,10 +38,21 @@ pub mod types;
|
|||||||
pub type TaskContextImpl =
|
pub type TaskContextImpl =
|
||||||
kernel_arch::TaskContextImpl<KernelTableManagerImpl, GlobalPhysicalAllocator>;
|
kernel_arch::TaskContextImpl<KernelTableManagerImpl, GlobalPhysicalAllocator>;
|
||||||
|
|
||||||
|
use sched::CpuQueue;
|
||||||
pub use types::{AtomicThreadState, ThreadAffinity, ThreadId, ThreadState};
|
pub use types::{AtomicThreadState, ThreadAffinity, ThreadId, ThreadState};
|
||||||
|
|
||||||
use yggdrasil_abi::process::Signal;
|
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) {
|
pub fn signal_process_group(group_id: u32, signal: Signal) {
|
||||||
unsafe { __signal_process_group(group_id, signal) }
|
unsafe { __signal_process_group(group_id, signal) }
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
use core::{
|
use core::{
|
||||||
cell::UnsafeCell,
|
cell::UnsafeCell,
|
||||||
|
future::poll_fn,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
sync::atomic::{AtomicU32, Ordering},
|
sync::atomic::{AtomicBool, AtomicU32, Ordering},
|
||||||
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use crossbeam_queue::ArrayQueue;
|
use crossbeam_queue::ArrayQueue;
|
||||||
use kernel_arch::task::Scheduler;
|
use kernel_arch::task::Scheduler;
|
||||||
use libk_util::sync::LockMethod;
|
use libk_util::{sync::LockMethod, waker::QueueWaker};
|
||||||
use yggdrasil_abi::error::Error;
|
use yggdrasil_abi::error::Error;
|
||||||
|
|
||||||
use crate::{sched::CpuQueue, thread::Thread};
|
use crate::{sched::CpuQueue, thread::Thread};
|
||||||
@ -17,6 +19,16 @@ struct ThreadedMutexInner {
|
|||||||
lock: AtomicU32,
|
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> {
|
pub struct Mutex<T> {
|
||||||
value: UnsafeCell<T>,
|
value: UnsafeCell<T>,
|
||||||
lock: ThreadedMutexInner,
|
lock: ThreadedMutexInner,
|
||||||
@ -27,6 +39,74 @@ pub struct MutexGuard<'a, T> {
|
|||||||
lock: &'a ThreadedMutexInner,
|
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 {
|
impl ThreadedMutexInner {
|
||||||
const UNLOCKED: u32 = 0;
|
const UNLOCKED: u32 = 0;
|
||||||
const LOCKED: u32 = 1;
|
const LOCKED: u32 = 1;
|
||||||
|
@ -16,12 +16,9 @@
|
|||||||
trait_alias
|
trait_alias
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use arch::Cpu;
|
|
||||||
use kernel_arch::{Architecture, ArchitectureImpl, CpuImpl};
|
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
pub use libk_thread::{block, runtime};
|
pub use libk_thread::{block, cpu_count, cpu_index, runtime};
|
||||||
|
|
||||||
pub mod arch;
|
pub mod arch;
|
||||||
|
|
||||||
@ -29,17 +26,6 @@ pub mod device {
|
|||||||
pub use libk_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)]
|
#[repr(C)]
|
||||||
pub struct AlignedTo<Align, Bytes: ?Sized> {
|
pub struct AlignedTo<Align, Bytes: ?Sized> {
|
||||||
pub align: [Align; 0],
|
pub align: [Align; 0],
|
||||||
|
@ -198,14 +198,10 @@ impl ExternalInterruptController for IoApic {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// Directly mapped to a GSI
|
// Directly mapped to a GSI
|
||||||
(
|
(irq - ISA_IRQ_OFFSET, options.level, options.trigger)
|
||||||
(irq - ISA_IRQ_OFFSET) as u32,
|
|
||||||
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!(),
|
Irq::Private(_) => unimplemented!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -225,9 +225,7 @@ impl MessageInterruptController for LocalApic {
|
|||||||
};
|
};
|
||||||
drop(table);
|
drop(table);
|
||||||
|
|
||||||
if handler.handle_irq(Some(vector)) {
|
handler.handle_irq(Some(vector));
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,7 @@ use core::{mem::size_of, ops::DerefMut, ptr::null_mut, sync::atomic::Ordering};
|
|||||||
use abi::error::Error;
|
use abi::error::Error;
|
||||||
use acpi_lib::{mcfg::Mcfg, AcpiTables, InterruptModel};
|
use acpi_lib::{mcfg::Mcfg, AcpiTables, InterruptModel};
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use device_api::{
|
use device_api::{interrupt::Irq, Device};
|
||||||
interrupt::{Irq, MessageInterruptController},
|
|
||||||
Device,
|
|
||||||
};
|
|
||||||
use git_version::git_version;
|
use git_version::git_version;
|
||||||
use kernel_arch_x86_64::{
|
use kernel_arch_x86_64::{
|
||||||
mem::{
|
mem::{
|
||||||
@ -305,6 +302,13 @@ impl X86_64 {
|
|||||||
Some(0x01),
|
Some(0x01),
|
||||||
ygg_driver_ahci::probe,
|
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(
|
ygg_driver_pci::register_vendor_driver(
|
||||||
"Virtio PCI Network Device",
|
"Virtio PCI Network Device",
|
||||||
0x1AF4,
|
0x1AF4,
|
||||||
@ -487,8 +491,3 @@ impl X86_64 {
|
|||||||
pic_slave_cmd.write(0x20);
|
pic_slave_cmd.write(0x20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
fn __message_interrupt_controller() -> &'static dyn MessageInterruptController {
|
|
||||||
Cpu::local().local_apic
|
|
||||||
}
|
|
||||||
|
@ -10,12 +10,9 @@ use device_api::{
|
|||||||
use libk::device::external_interrupt_controller;
|
use libk::device::external_interrupt_controller;
|
||||||
use libk_util::sync::IrqSafeSpinlock;
|
use libk_util::sync::IrqSafeSpinlock;
|
||||||
|
|
||||||
use crate::{
|
use crate::arch::x86_64::{
|
||||||
arch::x86_64::{
|
intrinsics::{IoPort, IoPortAccess},
|
||||||
intrinsics::{IoPort, IoPortAccess},
|
peripherals::ps2::codeset::{CODE_SET_1_00, CODE_SET_1_E0},
|
||||||
peripherals::ps2::codeset::{CODE_SET_1_00, CODE_SET_1_E0},
|
|
||||||
},
|
|
||||||
device::input,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mod codeset;
|
mod codeset;
|
||||||
@ -102,7 +99,7 @@ impl InterruptHandler for PS2Controller {
|
|||||||
KeyboardKeyEvent::Pressed(key)
|
KeyboardKeyEvent::Pressed(key)
|
||||||
};
|
};
|
||||||
|
|
||||||
input::send_event(event);
|
ygg_driver_input::send_event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
count != 0
|
count != 0
|
||||||
|
@ -6,7 +6,6 @@ use libk_util::sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard};
|
|||||||
pub mod bus;
|
pub mod bus;
|
||||||
|
|
||||||
pub mod display;
|
pub mod display;
|
||||||
pub mod input;
|
|
||||||
pub mod power;
|
pub mod power;
|
||||||
pub mod serial;
|
pub mod serial;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
|
@ -8,7 +8,6 @@ use memfs::MemoryFilesystem;
|
|||||||
use vfs::{IoContext, NodeRef};
|
use vfs::{IoContext, NodeRef};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
device::input,
|
|
||||||
fs::{FileBlockAllocator, INITRD_DATA},
|
fs::{FileBlockAllocator, INITRD_DATA},
|
||||||
proc::{self, random},
|
proc::{self, random},
|
||||||
};
|
};
|
||||||
@ -28,6 +27,8 @@ fn setup_root() -> Result<NodeRef, Error> {
|
|||||||
pub fn kinit() -> Result<(), Error> {
|
pub fn kinit() -> Result<(), Error> {
|
||||||
infoln!("In main");
|
infoln!("In main");
|
||||||
|
|
||||||
|
runtime::spawn(ygg_driver_usb::bus::bus_handler())?;
|
||||||
|
|
||||||
ygg_driver_net_loopback::init();
|
ygg_driver_net_loopback::init();
|
||||||
ygg_driver_net_core::start_network_tasks()?;
|
ygg_driver_net_core::start_network_tasks()?;
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ pub fn kinit() -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add keyboard device
|
// 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();
|
random::init();
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#![feature(offset_of)]
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::OpenOptions,
|
fs::OpenOptions,
|
||||||
io::{Read, Seek, SeekFrom, Write},
|
io::{Read, Seek, SeekFrom, Write},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user