Skip to content

Commit

Permalink
usb_hid: add start of very basic USB HID driver
Browse files Browse the repository at this point in the history
This is the start of a driver for USB HID-class devices. It can detect devices
it can potentially support from the Platform Bus, and then interrogate
their configuration hierachies for HID-class interfaces. Also includes some
definitions for HID descriptors for the future.

Next steps will be actually parsing the HID descriptors (including the Report
descriptors, which look potentially complex), and then working on Interrupt
endpoints to be able to get data from a HID device! I think it would be worth
getting some of the other parts of this infrastructure, including userspace
interrupt delegation and potentially userspace async runtimes, working before
we tackle this.
  • Loading branch information
IsaacWoods committed Feb 17, 2024
1 parent 17cf99d commit 5f8bf94
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 2 deletions.
1 change: 1 addition & 0 deletions Poplar.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ user_tasks = [
"platform_bus user/platform_bus",
"pci_bus user/pci_bus",
"usb_bus_ehci user/usb_bus_ehci",
"usb_hid user/usb_hid",
]

[mq_pro]
Expand Down
7 changes: 7 additions & 0 deletions lib/poplar/src/ddk/dma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ pub struct DmaBuffer {
allocator: Arc<LockedHeap>,
}

impl DmaBuffer {
pub unsafe fn at<T>(&self, offset: usize) -> &T {
assert!((offset + mem::size_of::<T>()) <= self.length);
unsafe { &*(self.ptr.byte_add(offset).cast::<T>().as_ptr()) }
}
}

impl Deref for DmaBuffer {
type Target = [u8];

Expand Down
2 changes: 1 addition & 1 deletion lib/poplar/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![no_std]
#![feature(decl_macro, never_type, allocator_api, ptr_as_uninit)]
#![feature(decl_macro, never_type, allocator_api, ptr_as_uninit, non_null_convenience)]
#![deny(unsafe_op_in_unsafe_fn)]

pub mod caps;
Expand Down
51 changes: 51 additions & 0 deletions lib/usb/src/hid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct HidDescriptor {
pub length: u8,
pub typ: u8,
pub bcd_hid: u16,
pub country_code: CountryCode,
/// The number of included class descriptors. Will be `>=1` as a `Report` descriptor will
/// always be present.
pub num_descriptors: u8,
pub descriptor_typ: u8,
pub descriptor_length: u16,
}

#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum CountryCode {
NotSupported = 0,
Arabic = 1,
Belgian = 2,
CanadianBilingual = 3,
CanadianFrench = 4,
Czech = 5,
Danish = 6,
Finnish = 7,
French = 8,
German = 9,
Greek = 10,
Hebrew = 11,
Hungary = 12,
International = 13,
Italian = 14,
Japan = 15,
Korean = 16,
LatinAmerican = 17,
Dutch = 18,
Norwegian = 19,
Farsi = 20,
Poland = 21,
Portuguese = 22,
Russia = 23,
Slovakia = 24,
Spanish = 25,
Swedish = 26,
SwissFrench = 27,
SwissGerman = 28,
Switzerland = 29,
Taiwan = 30,
Turkish = 31,
// 36-255 are reserved
}
1 change: 1 addition & 0 deletions lib/usb/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![no_std]

pub mod descriptor;
pub mod hid;
pub mod setup;

use core::mem;
Expand Down
2 changes: 1 addition & 1 deletion seed/seed_riscv/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ pub fn seed_main(hart_id: u64, fdt_ptr: *const u8) -> ! {
* Load desired early tasks.
*/
// TODO: load from config file
for name in &["hello_world", "platform_bus", "pci_bus", "usb_bus_ehci"] {
for name in &["hello_world", "platform_bus", "pci_bus", "usb_bus_ehci", "usb_hid"] {
let file = if let Some(ref mut ramdisk) = ramdisk {
ramdisk.load(&name).unwrap()
} else {
Expand Down
11 changes: 11 additions & 0 deletions user/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions user/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ members = [
"tests/test_syscalls",
"hello_world",
"usb_bus_ehci",
"usb_hid",
]
resolver = "2"

Expand Down
12 changes: 12 additions & 0 deletions user/usb_hid/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "usb_hid"
version = "0.1.0"
authors = ["Isaac Woods"]
edition = "2021"

[dependencies]
std = { path = "../../lib/std", features = ["ddk"] }
log = "0.4"
platform_bus = { path = "../platform_bus" }
ptah = { path = "../../lib/ptah" }
usb = { path = "../../lib/usb" }
80 changes: 80 additions & 0 deletions user/usb_hid/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#![feature(never_type)]

use log::info;
use platform_bus::{BusDriverMessage, DeviceDriverMessage, DeviceDriverRequest, Filter, Property};
use std::poplar::{
caps::{CapabilitiesRepr, CAP_EARLY_LOGGING, CAP_PADDING, CAP_SERVICE_USER},
channel::Channel,
early_logger::EarlyLogger,
syscall,
};
use usb::descriptor::InterfaceDescriptor;

pub fn main() {
log::set_logger(&EarlyLogger).unwrap();
log::set_max_level(log::LevelFilter::Trace);
info!("USB HID Driver is running!");

// This allows us to talk to the PlatformBus as a bus driver (to register our abstract devices).
let platform_bus_bus_channel: Channel<BusDriverMessage, !> =
Channel::from_handle(syscall::subscribe_to_service("platform_bus.bus_driver").unwrap());
// This allows us to talk to the PlatformBus as a device driver (to find supported USB devices).
let platform_bus_device_channel: Channel<DeviceDriverMessage, DeviceDriverRequest> =
Channel::from_handle(syscall::subscribe_to_service("platform_bus.device_driver").unwrap());

// Tell PlatformBus that we're interested in USB devices that are specified per-interface
// (we need to parse their configurations to tell if they're HID devices). A HID device is not
// supposed to indicate its class at the device level so we don't need to test for that.
platform_bus_device_channel
.send(&DeviceDriverMessage::RegisterInterest(vec![
Filter::Matches(String::from("usb.device_class"), Property::Integer(0x00)),
Filter::Matches(String::from("usb.device_subclass"), Property::Integer(0x00)),
]))
.unwrap();

// TODO: we need to be able to support multiple devices, so this needs to be spawned as a task
// that loops round listening permanently
let (_device_info, handoff_info) = loop {
match platform_bus_device_channel.try_receive().unwrap() {
Some(DeviceDriverRequest::QuerySupport(device_name, device_info)) => {
info!("Platform bus asked if we can support device {} with info {:?}", device_name, device_info);
// TODO: consider each config if multiple?
let configuration = device_info.get_as_bytes("usb.config0").unwrap();
info!("USB config: {:x?}", configuration);

struct Visitor(pub bool);
impl usb::ConfigurationVisitor for Visitor {
fn visit_interface(&mut self, descriptor: &InterfaceDescriptor) {
// Check if this interface indicates a HID class device
if descriptor.interface_class == 3 {
info!("Found a HID class interface!");
self.0 = true;
}
}
}

let supported = {
let mut visitor = Visitor(false);
usb::walk_configuration(configuration, &mut visitor);
visitor.0
};
platform_bus_device_channel
.send(&DeviceDriverMessage::CanSupport(device_name, supported))
.unwrap();
}
Some(DeviceDriverRequest::HandoffDevice(device_name, device_info, handoff_info)) => {
break (device_info, handoff_info);
}
None => std::poplar::syscall::yield_to_kernel(),
}
};

loop {
std::poplar::syscall::yield_to_kernel();
}
}

#[used]
#[link_section = ".caps"]
pub static mut CAPS: CapabilitiesRepr<4> =
CapabilitiesRepr::new([CAP_EARLY_LOGGING, CAP_SERVICE_USER, CAP_PADDING, CAP_PADDING]);

0 comments on commit 5f8bf94

Please sign in to comment.