WIP usb: better driver structure, hid mouse driver

This commit is contained in:
2025-07-14 16:25:04 +03:00
parent 552de70191
commit 22fba9cb30
26 changed files with 1291 additions and 646 deletions
+58
View File
@@ -0,0 +1,58 @@
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct UsbRoute {
bus: u16,
ports: [u8; 8],
len: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct UsbBusAddress {
pub bus: u16,
pub device: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct UsbInterfaceAddress {
pub device: UsbBusAddress,
pub interface: u8,
}
impl UsbBusAddress {
pub fn with_interface(self, interface: u8) -> UsbInterfaceAddress {
UsbInterfaceAddress {
device: self,
interface,
}
}
}
impl fmt::Display for UsbBusAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<Bus {} Device {}>", self.bus, self.device)
}
}
impl fmt::Display for UsbInterfaceAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"<Bus {} Device {} Interface {}>",
self.device.bus, self.device.device, self.interface
)
}
}
impl fmt::Display for UsbRoute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}-", self.bus)?;
for (i, &port) in self.ports[..self.len as usize].iter().enumerate() {
if i != 0 {
write!(f, ".")?;
}
write!(f, "{port}")?;
}
Ok(())
}
}
+12 -29
View File
@@ -1,21 +1,19 @@
use core::sync::atomic::{AtomicU16, Ordering};
use alloc::{collections::BTreeMap, format, sync::Arc};
use libk::fs::sysfs::{self, object::KObject};
use libk_util::{queue::UnboundedMpmcQueue, sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use crate::{
address::UsbBusAddress,
class_driver,
device::{UsbBusAddress, UsbDevice, UsbDeviceAccess},
device::{UsbDevice, UsbDeviceAccess},
sysfs::{self, UsbBusKObject},
UsbHostController,
};
pub type UsbBusKObject = Arc<KObject<Arc<UsbBusWrapper>>>;
pub type UsbDeviceKObject = Arc<KObject<Arc<UsbDeviceAccess>>>;
pub struct UsbBusWrapper {
hc: Arc<dyn UsbHostController>,
index: u16,
pub(crate) hc: Arc<dyn UsbHostController>,
pub(crate) index: u16,
kobject: OneTimeInit<UsbBusKObject>,
}
@@ -27,30 +25,11 @@ pub struct UsbBusManager {
}
impl UsbBusWrapper {
pub fn register_sysfs_object(self: &Arc<Self>) {
let root = sysfs_usb_root();
let bus_kobject = self.kobject.init(KObject::new(self.clone()));
self.hc.register_sysfs_properties(bus_kobject);
root.add_object(format!("bus{}", self.index), bus_kobject.clone())
.ok();
}
pub fn kobject(&self) -> &UsbBusKObject {
self.kobject.get()
}
}
fn sysfs_usb_root() -> &'static Arc<KObject<()>> {
static USB_ROOT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
USB_ROOT.or_init_with(|| {
let bus_object = sysfs::bus().expect("bus object");
let usb_object = KObject::new(());
bus_object.add_object("usb", usb_object.clone()).ok();
usb_object
})
}
impl UsbBusManager {
pub fn register_bus(hc: Arc<dyn UsbHostController>) -> (u16, Arc<UsbBusWrapper>) {
let i = BUS_MANAGER.last_bus_address.fetch_add(1, Ordering::AcqRel);
@@ -60,7 +39,7 @@ impl UsbBusManager {
kobject: OneTimeInit::new(),
});
BUS_MANAGER.busses.write().insert(i, wrapper.clone());
wrapper.register_sysfs_object();
wrapper.kobject.init(sysfs::register_bus_kobject(&wrapper));
(i, wrapper)
}
@@ -71,7 +50,7 @@ impl UsbBusManager {
.devices
.write()
.insert(device.bus_address(), device.clone());
device.register_sysfs_object();
device.kobject.init(sysfs::register_device_kobject(&device));
QUEUE.push_back(device);
}
@@ -94,7 +73,11 @@ pub async fn bus_handler() {
new_device.bus_address()
);
class_driver::spawn_driver(new_device).await.ok();
let address = new_device.bus_address();
if let Err(error) = class_driver::setup_device(new_device).await {
log::warn!("USB device {address} setup error: {error:?}",);
}
// class_driver::spawn_driver(new_device).await.ok();
}
}
@@ -4,9 +4,12 @@ use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent};
use crate::{device::UsbDeviceAccess, error::UsbError, info::UsbDeviceClass};
use super::{UsbClassInfo, UsbDriver};
use crate::{
class_driver::{UsbInterfaceClass, UsbInterfaceDriver},
device::UsbDeviceAccess,
error::UsbError,
info::UsbInterfaceInfo,
};
pub struct UsbHidKeyboardDriver;
@@ -125,10 +128,14 @@ impl KeyboardState {
}
#[async_trait]
impl UsbDriver for UsbHidKeyboardDriver {
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
impl UsbInterfaceDriver for UsbHidKeyboardDriver {
async fn run(
self: Arc<Self>,
device: Arc<UsbDeviceAccess>,
interface: UsbInterfaceInfo,
) -> Result<(), UsbError> {
// TODO not sure whether to use boot protocol (easy) or GetReport
let config = device.select_configuration(|_| true).await?.unwrap();
let config = device.current_configuration().unwrap();
log::info!("Setup HID keyboard");
let pipe = device
@@ -156,7 +163,7 @@ impl UsbDriver for UsbHidKeyboardDriver {
for &event in events {
log::trace!("Generic Keyboard: {:?}", event);
ygg_driver_input::send_event(event);
ygg_driver_input::send_keyboard_event(event);
}
}
}
@@ -165,12 +172,18 @@ impl UsbDriver for UsbHidKeyboardDriver {
"USB HID Keyboard"
}
fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
log::info!(
"class = {:?}, subclass = {:02x}",
class.class,
class.subclass
);
class.class == UsbDeviceClass::Hid && (class.subclass == 0x00 || class.subclass == 0x01)
fn probe(
&self,
device: &UsbDeviceAccess,
interface: &UsbInterfaceInfo,
class: UsbInterfaceClass,
) -> bool {
let _ = (device, interface);
class
== UsbInterfaceClass {
class: 3,
subclass: 1,
protocol: 1,
}
}
}
@@ -0,0 +1,64 @@
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use yggdrasil_abi::io::{ButtonMask, MouseEvent};
use crate::{
class_driver::{UsbInterfaceClass, UsbInterfaceDriver},
device::UsbDeviceAccess,
error::UsbError,
info::UsbInterfaceInfo,
};
pub struct UsbHidMouseDriver;
#[async_trait]
impl UsbInterfaceDriver for UsbHidMouseDriver {
async fn run(
self: Arc<Self>,
device: Arc<UsbDeviceAccess>,
interface: UsbInterfaceInfo,
) -> Result<(), UsbError> {
let config = device.current_configuration().unwrap();
log::info!("Setup HID mouse");
let pipe = device
.open_interrupt_in_pipe(1, config.endpoints[0].max_packet_size as u16)
.await?;
let mut buffer = [0; 16];
let mut button_state = 0;
loop {
let len = pipe.read(&mut buffer).await?;
if len < 4 {
continue;
}
let event = MouseEvent {
buttons: ButtonMask(buffer[0]),
dx: (buffer[1] as i8) as i32,
dy: (buffer[2] as i8) as i32,
};
ygg_driver_input::send_mouse_event(event);
}
}
fn name(&self) -> &'static str {
"USB HID Mouse"
}
fn probe(
&self,
device: &UsbDeviceAccess,
interface: &UsbInterfaceInfo,
class: UsbInterfaceClass,
) -> bool {
class
== UsbInterfaceClass {
class: 3,
subclass: 1,
protocol: 2,
}
}
}
@@ -1,273 +1,273 @@
use core::mem::MaybeUninit;
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use bytemuck::{Pod, Zeroable};
use libk::{
dma::{DmaBuffer, DmaSliceMut},
error::Error,
};
use ygg_driver_scsi::{transport::ScsiTransport, ScsiEnclosure};
use crate::{
communication::UsbDirection,
device::{UsbDeviceAccess, UsbDeviceDetachHandler},
error::UsbError,
info::{UsbDeviceClass, UsbEndpointType},
pipe::{
control::{ControlTransferSetup, UsbClassSpecificRequest},
normal::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
},
};
use super::{UsbClassInfo, UsbDriver};
pub struct UsbMassStorageDriverBulkOnly;
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
struct Cbw {
signature: u32, // 0x00
tag: u32, // 0x04
transfer_length: u32, // 0x08
flags: u8, // 0x0C
lun: u8, // 0x0D
cb_length: u8, // 0x0E
cb_data: [u8; 16], // 0x0F
// Not sent
_0: u8,
}
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
struct Csw {
signature: u32,
tag: u32,
data_residue: u32,
status: u8,
_0: [u8; 3],
}
struct Bbb {
#[allow(unused)]
device: Arc<UsbDeviceAccess>,
in_pipe: UsbBulkInPipeAccess,
out_pipe: UsbBulkOutPipeAccess,
last_tag: u32,
}
struct DetachHandler(Arc<ScsiEnclosure>);
impl Bbb {
pub fn new(
device: Arc<UsbDeviceAccess>,
in_pipe: UsbBulkInPipeAccess,
out_pipe: UsbBulkOutPipeAccess,
) -> Result<Self, UsbError> {
Ok(Self {
device,
in_pipe,
out_pipe,
last_tag: 0,
})
}
}
impl Bbb {
async fn send_cbw(
&mut self,
lun: u8,
host_to_dev: bool,
command: &[u8],
response_len: usize,
) -> Result<u32, Error> {
self.last_tag = self.last_tag.wrapping_add(1);
let flags = if !host_to_dev { 1 << 7 } else { 0 };
let tag = self.last_tag;
let mut cbw_bytes = [0; 32];
let cbw = bytemuck::from_bytes_mut::<Cbw>(&mut cbw_bytes);
cbw.signature = 0x43425355;
cbw.transfer_length = response_len as u32;
cbw.flags = flags;
cbw.tag = tag;
cbw.lun = lun;
cbw.cb_length = command.len() as u8;
cbw.cb_data[..command.len()].copy_from_slice(command);
self.out_pipe
.write(&cbw_bytes[..31])
.await
.inspect_err(|error| log::error!("msc: CBW send error: {error:?}"))?;
Ok(tag)
}
async fn read_csw(&mut self, tag: u32) -> Result<(), Error> {
let mut csw_bytes = [0; 16];
self.in_pipe
.read_exact(&mut csw_bytes[..13])
.await
.inspect_err(|error| log::error!("msc: CSW receive error: {error:?}"))?;
let csw = bytemuck::from_bytes::<Csw>(&csw_bytes);
if csw.signature != 0x53425355 {
log::warn!("msc: invalid csw signature");
return Err(Error::InvalidArgument);
}
if csw.tag != tag {
let csw_tag = csw.tag;
log::warn!("msc: invalid csw tag (got {}, expected {tag})", csw_tag);
return Err(Error::InvalidArgument);
}
if csw.status != 0x00 {
return Err(Error::InvalidArgument);
}
Ok(())
}
async fn read_response_data(
&mut self,
buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
) -> Result<usize, Error> {
if buffer.len() == 0 {
return Ok(0);
}
let len = self
.in_pipe
.read_dma(buffer)
.await
.inspect_err(|error| log::error!("msc: DMA read error: {error:?}"))?;
Ok(len)
}
}
#[async_trait]
impl ScsiTransport for Bbb {
fn allocate_buffer(&self, size: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
Ok(self.in_pipe.allocate_dma_buffer(size)?)
}
async fn perform_request_raw(
&mut self,
lun: u8,
request_data: &[u8],
response_buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
) -> Result<usize, Error> {
if request_data.len() > 16 || response_buffer.len() > self.max_bytes_per_request() {
return Err(Error::InvalidArgument);
}
let tag = self
.send_cbw(lun, false, request_data, response_buffer.len())
.await?;
let response_len = self.read_response_data(response_buffer).await?;
self.read_csw(tag).await?;
Ok(response_len)
}
fn max_bytes_per_request(&self) -> usize {
32768
}
}
impl UsbDeviceDetachHandler for DetachHandler {
fn handle_device_detach(&self) {
log::info!("Mass storage detached");
self.0.detach();
}
}
#[derive(Debug, Pod, Zeroable, Clone, Copy)]
#[repr(C)]
pub struct BulkOnlyMassStorageReset;
#[derive(Debug, Pod, Zeroable, Clone, Copy)]
#[repr(C)]
pub struct GetMaxLun;
impl UsbClassSpecificRequest for BulkOnlyMassStorageReset {
const BM_REQUEST_TYPE: u8 = 0b00100001;
const B_REQUEST: u8 = 0b11111111;
}
impl UsbClassSpecificRequest for GetMaxLun {
const BM_REQUEST_TYPE: u8 = 0b10100001;
const B_REQUEST: u8 = 0b11111110;
}
#[async_trait]
impl UsbDriver for UsbMassStorageDriverBulkOnly {
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
// TODO filter to only accept BBB config
let config = device.select_configuration(|_| true).await?.unwrap();
// Bulk-in, bulk-out
assert_eq!(config.endpoints.len(), 2);
let control_pipe = device.control_pipe();
let (in_index, in_info) = config
.find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::In))
.ok_or(UsbError::InvalidConfiguration)?;
let (out_index, out_info) = config
.find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::Out))
.ok_or(UsbError::InvalidConfiguration)?;
let in_pipe = device
.open_bulk_in_pipe(in_index, in_info.max_packet_size as u16)
.await?;
let out_pipe = device
.open_bulk_out_pipe(out_index, out_info.max_packet_size as u16)
.await?;
// Perform a Bulk-Only Mass Storage Reset
// TODO interface id?
control_pipe
.control_transfer(ControlTransferSetup {
bm_request_type: BulkOnlyMassStorageReset::BM_REQUEST_TYPE,
b_request: BulkOnlyMassStorageReset::B_REQUEST,
w_value: 0,
w_index: 0,
w_length: 0,
})
.await?;
// Get max LUN
// TODO on devices which do not support multiple LUNs, this command may STALL
let mut buffer = [MaybeUninit::uninit()];
let len = control_pipe
.control_transfer_in(
ControlTransferSetup {
bm_request_type: GetMaxLun::BM_REQUEST_TYPE,
b_request: GetMaxLun::B_REQUEST,
w_value: 0,
w_index: 0,
w_length: 1,
},
&mut buffer,
)
.await?;
let max_lun = if len < 1 {
0
} else {
unsafe { buffer[0].assume_init() }
};
let bbb = Bbb::new(device.clone(), in_pipe, out_pipe)?;
let scsi = ScsiEnclosure::setup(Box::new(bbb), max_lun as usize + 1)
.await
.inspect_err(|error| log::error!("msc: scsi error {error:?}"))
.map_err(|_| UsbError::DriverError)?;
let detach = DetachHandler(scsi.clone());
device.set_detach_handler(Arc::new(detach));
Ok(())
}
fn name(&self) -> &'static str {
"USB Mass Storage"
}
fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
// TODO support other protocols
class.class == UsbDeviceClass::MassStorage && class.interface_protocol_number == 0x50
}
}
// use core::mem::MaybeUninit;
//
// use alloc::{boxed::Box, sync::Arc};
// use async_trait::async_trait;
// use bytemuck::{Pod, Zeroable};
// use libk::{
// dma::{DmaBuffer, DmaSliceMut},
// error::Error,
// };
// use ygg_driver_scsi::{transport::ScsiTransport, ScsiEnclosure};
//
// use crate::{
// communication::UsbDirection,
// device::{UsbDeviceAccess, UsbDeviceDetachHandler},
// error::UsbError,
// info::{UsbDeviceClass, UsbEndpointType},
// pipe::{
// control::{ControlTransferSetup, UsbClassSpecificRequest},
// normal::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
// },
// };
//
// use super::{UsbClassInfo, UsbDriver};
//
// pub struct UsbMassStorageDriverBulkOnly;
//
// #[derive(Debug, Clone, Copy, Zeroable, Pod)]
// #[repr(C)]
// struct Cbw {
// signature: u32, // 0x00
// tag: u32, // 0x04
// transfer_length: u32, // 0x08
// flags: u8, // 0x0C
// lun: u8, // 0x0D
// cb_length: u8, // 0x0E
// cb_data: [u8; 16], // 0x0F
// // Not sent
// _0: u8,
// }
//
// #[derive(Debug, Clone, Copy, Zeroable, Pod)]
// #[repr(C)]
// struct Csw {
// signature: u32,
// tag: u32,
// data_residue: u32,
// status: u8,
// _0: [u8; 3],
// }
//
// struct Bbb {
// #[allow(unused)]
// device: Arc<UsbDeviceAccess>,
// in_pipe: UsbBulkInPipeAccess,
// out_pipe: UsbBulkOutPipeAccess,
// last_tag: u32,
// }
//
// struct DetachHandler(Arc<ScsiEnclosure>);
//
// impl Bbb {
// pub fn new(
// device: Arc<UsbDeviceAccess>,
// in_pipe: UsbBulkInPipeAccess,
// out_pipe: UsbBulkOutPipeAccess,
// ) -> Result<Self, UsbError> {
// Ok(Self {
// device,
// in_pipe,
// out_pipe,
// last_tag: 0,
// })
// }
// }
//
// impl Bbb {
// async fn send_cbw(
// &mut self,
// lun: u8,
// host_to_dev: bool,
// command: &[u8],
// response_len: usize,
// ) -> Result<u32, Error> {
// self.last_tag = self.last_tag.wrapping_add(1);
//
// let flags = if !host_to_dev { 1 << 7 } else { 0 };
// let tag = self.last_tag;
// let mut cbw_bytes = [0; 32];
// let cbw = bytemuck::from_bytes_mut::<Cbw>(&mut cbw_bytes);
//
// cbw.signature = 0x43425355;
// cbw.transfer_length = response_len as u32;
// cbw.flags = flags;
// cbw.tag = tag;
// cbw.lun = lun;
// cbw.cb_length = command.len() as u8;
// cbw.cb_data[..command.len()].copy_from_slice(command);
//
// self.out_pipe
// .write(&cbw_bytes[..31])
// .await
// .inspect_err(|error| log::error!("msc: CBW send error: {error:?}"))?;
//
// Ok(tag)
// }
//
// async fn read_csw(&mut self, tag: u32) -> Result<(), Error> {
// let mut csw_bytes = [0; 16];
// self.in_pipe
// .read_exact(&mut csw_bytes[..13])
// .await
// .inspect_err(|error| log::error!("msc: CSW receive error: {error:?}"))?;
// let csw = bytemuck::from_bytes::<Csw>(&csw_bytes);
//
// if csw.signature != 0x53425355 {
// log::warn!("msc: invalid csw signature");
// return Err(Error::InvalidArgument);
// }
// if csw.tag != tag {
// let csw_tag = csw.tag;
// log::warn!("msc: invalid csw tag (got {}, expected {tag})", csw_tag);
// return Err(Error::InvalidArgument);
// }
// if csw.status != 0x00 {
// return Err(Error::InvalidArgument);
// }
// Ok(())
// }
//
// async fn read_response_data(
// &mut self,
// buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
// ) -> Result<usize, Error> {
// if buffer.len() == 0 {
// return Ok(0);
// }
// let len = self
// .in_pipe
// .read_dma(buffer)
// .await
// .inspect_err(|error| log::error!("msc: DMA read error: {error:?}"))?;
// Ok(len)
// }
// }
//
// #[async_trait]
// impl ScsiTransport for Bbb {
// fn allocate_buffer(&self, size: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
// Ok(self.in_pipe.allocate_dma_buffer(size)?)
// }
//
// async fn perform_request_raw(
// &mut self,
// lun: u8,
// request_data: &[u8],
// response_buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
// ) -> Result<usize, Error> {
// if request_data.len() > 16 || response_buffer.len() > self.max_bytes_per_request() {
// return Err(Error::InvalidArgument);
// }
//
// let tag = self
// .send_cbw(lun, false, request_data, response_buffer.len())
// .await?;
// let response_len = self.read_response_data(response_buffer).await?;
// self.read_csw(tag).await?;
// Ok(response_len)
// }
//
// fn max_bytes_per_request(&self) -> usize {
// 32768
// }
// }
//
// impl UsbDeviceDetachHandler for DetachHandler {
// fn handle_device_detach(&self) {
// log::info!("Mass storage detached");
// self.0.detach();
// }
// }
//
// #[derive(Debug, Pod, Zeroable, Clone, Copy)]
// #[repr(C)]
// pub struct BulkOnlyMassStorageReset;
//
// #[derive(Debug, Pod, Zeroable, Clone, Copy)]
// #[repr(C)]
// pub struct GetMaxLun;
//
// impl UsbClassSpecificRequest for BulkOnlyMassStorageReset {
// const BM_REQUEST_TYPE: u8 = 0b00100001;
// const B_REQUEST: u8 = 0b11111111;
// }
//
// impl UsbClassSpecificRequest for GetMaxLun {
// const BM_REQUEST_TYPE: u8 = 0b10100001;
// const B_REQUEST: u8 = 0b11111110;
// }
//
// #[async_trait]
// impl UsbDriver for UsbMassStorageDriverBulkOnly {
// async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
// // TODO filter to only accept BBB config
// let config = device.select_configuration(|_| true).await?.unwrap();
// // Bulk-in, bulk-out
// assert_eq!(config.endpoints.len(), 2);
// let control_pipe = device.control_pipe();
// let (in_index, in_info) = config
// .find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::In))
// .ok_or(UsbError::InvalidConfiguration)?;
// let (out_index, out_info) = config
// .find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::Out))
// .ok_or(UsbError::InvalidConfiguration)?;
// let in_pipe = device
// .open_bulk_in_pipe(in_index, in_info.max_packet_size as u16)
// .await?;
// let out_pipe = device
// .open_bulk_out_pipe(out_index, out_info.max_packet_size as u16)
// .await?;
//
// // Perform a Bulk-Only Mass Storage Reset
// // TODO interface id?
// control_pipe
// .control_transfer(ControlTransferSetup {
// bm_request_type: BulkOnlyMassStorageReset::BM_REQUEST_TYPE,
// b_request: BulkOnlyMassStorageReset::B_REQUEST,
// w_value: 0,
// w_index: 0,
// w_length: 0,
// })
// .await?;
//
// // Get max LUN
// // TODO on devices which do not support multiple LUNs, this command may STALL
// let mut buffer = [MaybeUninit::uninit()];
// let len = control_pipe
// .control_transfer_in(
// ControlTransferSetup {
// bm_request_type: GetMaxLun::BM_REQUEST_TYPE,
// b_request: GetMaxLun::B_REQUEST,
// w_value: 0,
// w_index: 0,
// w_length: 1,
// },
// &mut buffer,
// )
// .await?;
// let max_lun = if len < 1 {
// 0
// } else {
// unsafe { buffer[0].assume_init() }
// };
//
// let bbb = Bbb::new(device.clone(), in_pipe, out_pipe)?;
// let scsi = ScsiEnclosure::setup(Box::new(bbb), max_lun as usize + 1)
// .await
// .inspect_err(|error| log::error!("msc: scsi error {error:?}"))
// .map_err(|_| UsbError::DriverError)?;
// let detach = DetachHandler(scsi.clone());
// device.set_detach_handler(Arc::new(detach));
//
// Ok(())
// }
//
// fn name(&self) -> &'static str {
// "USB Mass Storage"
// }
//
// fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
// // TODO support other protocols
// class.class == UsbDeviceClass::MassStorage && class.interface_protocol_number == 0x50
// }
// }
+174 -83
View File
@@ -1,117 +1,208 @@
use core::mem::MaybeUninit;
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use async_trait::async_trait;
use libk::task::runtime;
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use crate::{
address::UsbInterfaceAddress,
device::UsbDeviceAccess,
error::UsbError,
info::{UsbDeviceClass, UsbDeviceProtocol},
info::{UsbInterfaceInfo, CLASS_FROM_INTERFACE},
pipe::control::{ControlTransferSetup, UsbClassSpecificRequest},
};
// use alloc::{boxed::Box, sync::Arc, vec::Vec};
// use async_trait::async_trait;
// use libk::task::runtime;
// use libk_util::sync::spin_rwlock::IrqSafeRwLock;
//
// use crate::{
// device::UsbDeviceAccess,
// error::UsbError,
// info::{UsbDeviceClass, UsbDeviceProtocol},
// };
//
pub mod hid_keyboard;
pub mod mass_storage;
#[derive(Debug)]
pub struct UsbClassInfo {
pub class: UsbDeviceClass,
pub mod hid_mouse;
// pub mod mass_storage;
//
// #[derive(Debug)]
// pub struct UsbClassInfo {
// pub class: UsbDeviceClass,
// pub subclass: u8,
// pub protocol: UsbDeviceProtocol,
// pub device_protocol_number: u8,
// pub interface_protocol_number: u8,
// }
//
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UsbInterfaceClass {
pub class: u8,
pub subclass: u8,
pub protocol: UsbDeviceProtocol,
pub device_protocol_number: u8,
pub interface_protocol_number: u8,
pub protocol: u8,
}
#[async_trait]
pub trait UsbDriver: Send + Sync {
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError>;
pub trait UsbInterfaceDriver: Send + Sync {
async fn run(
self: Arc<Self>,
device: Arc<UsbDeviceAccess>,
interface: UsbInterfaceInfo,
) -> Result<(), UsbError>;
fn name(&self) -> &'static str;
fn probe(&self, class: &UsbClassInfo, device: &UsbDeviceAccess) -> bool;
fn probe(
&self,
device: &UsbDeviceAccess,
interface: &UsbInterfaceInfo,
class: UsbInterfaceClass,
) -> bool;
}
async fn extract_class_info(device: &UsbDeviceAccess) -> Result<Option<UsbClassInfo>, UsbError> {
if device.info.num_configurations != 1 {
return Ok(None);
}
let device_info = &device.info;
let config_info = device.query_configuration_info(0).await?;
// async fn extract_class_info(device: &UsbDeviceAccess) -> Result<Option<UsbClassInfo>, UsbError> {
// if device.info.num_configurations != 1 {
// return Ok(None);
// }
// let device_info = &device.info;
// let config_info = device.query_configuration_info(0).await?;
//
// 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
// };
//
// Ok(Some(UsbClassInfo {
// class,
// subclass,
// protocol,
// interface_protocol_number: if_info.interface_protocol_number,
// device_protocol_number: device_info.device_protocol_number,
// }))
// } else {
// Ok(None)
// }
// }
//
// async fn pick_driver(
// device: &UsbDeviceAccess,
// ) -> Result<Option<Arc<dyn UsbDriver + 'static>>, UsbError> {
// let Some(class) = extract_class_info(device).await? else {
// return Ok(None);
// };
//
// for driver in USB_DEVICE_DRIVERS.read().iter() {
// if driver.probe(&class, device) {
// return Ok(Some(driver.clone()));
// }
// }
// Ok(None)
// }
//
// pub async fn spawn_driver(device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
// // if let Some(driver) = pick_driver(&device).await? {
// // runtime::spawn(async move {
// // let name = driver.name();
// // match driver.run(device).await {
// // e @ Err(UsbError::DeviceDisconnected) => {
// // log::warn!(
// // "Driver {:?} did not exit cleanly: device disconnected",
// // name,
// // );
//
// // e
// // }
// // e => e,
// // }
// // })
// // .map_err(UsbError::SystemError)?;
// // }
// Ok(())
// }
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
};
Ok(Some(UsbClassInfo {
class,
subclass,
protocol,
interface_protocol_number: if_info.interface_protocol_number,
device_protocol_number: device_info.device_protocol_number,
}))
} else {
Ok(None)
}
}
async fn pick_driver(
device: &UsbDeviceAccess,
) -> Result<Option<Arc<dyn UsbDriver + 'static>>, UsbError> {
let Some(class) = extract_class_info(device).await? else {
return Ok(None);
async fn setup_interface(
device: &Arc<UsbDeviceAccess>,
address: UsbInterfaceAddress,
interface: &UsbInterfaceInfo,
) -> Result<(), UsbError> {
let class = UsbInterfaceClass {
class: interface.interface_class,
subclass: interface.interface_subclass,
protocol: interface.interface_protocol,
};
for driver in USB_DEVICE_DRIVERS.read().iter() {
if driver.probe(&class, device) {
return Ok(Some(driver.clone()));
}
}
Ok(None)
}
pub async fn spawn_driver(device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
if let Some(driver) = pick_driver(&device).await? {
runtime::spawn(async move {
let name = driver.name();
match driver.run(device).await {
e @ Err(UsbError::DeviceDisconnected) => {
log::warn!(
"Driver {:?} did not exit cleanly: device disconnected",
name,
);
e
let drivers = USB_INTERFACE_DRIVERS.read();
for driver in drivers.iter() {
if driver.probe(device, interface, class) {
let driver = driver.clone();
let device = device.clone();
let interface = interface.clone();
runtime::spawn(async move {
let name = driver.name();
match driver.run(device, interface).await {
e @ Err(UsbError::DeviceDisconnected) => {
log::warn!("{address} did not exit cleanly: device disconnected ({name})");
e
}
e => e,
}
e => e,
}
})
.map_err(UsbError::SystemError)?;
})
.map_err(UsbError::SystemError)?;
break;
}
}
Ok(())
}
pub fn register_driver(driver: Arc<dyn UsbDriver + 'static>) {
pub async fn setup_device(device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
// If device has only one configuration available, use it
// TODO support devices with multiple configurations
let address = device.bus_address();
log::info!("Setup USB device @ {address}");
let Some(config_info) = device.use_default_configuration().await? else {
log::warn!("{address} has multiple configurations, not supported yet",);
return Ok(());
};
// Setup drivers for interfaces
log::info!("{address}: {config_info:#?}");
// TODO device-level drivers
for interface in config_info.interfaces.iter() {
let address = address.with_interface(interface.number);
if let Err(error) = setup_interface(&device, address, interface).await {
log::error!("{}: {:?}", address, error);
}
}
Ok(())
}
pub fn register_driver(driver: Arc<dyn UsbInterfaceDriver + 'static>) {
// TODO check for duplicates
USB_DEVICE_DRIVERS.write().push(driver);
USB_INTERFACE_DRIVERS.write().push(driver);
}
pub fn register_default_class_drivers() {
register_driver(Arc::new(hid_keyboard::UsbHidKeyboardDriver));
register_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
register_driver(Arc::new(hid_mouse::UsbHidMouseDriver));
// register_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
}
static USB_DEVICE_DRIVERS: IrqSafeRwLock<Vec<Arc<dyn UsbDriver + 'static>>> =
static USB_INTERFACE_DRIVERS: IrqSafeRwLock<Vec<Arc<dyn UsbInterfaceDriver + 'static>>> =
IrqSafeRwLock::new(Vec::new());
+23 -19
View File
@@ -2,9 +2,9 @@ use bytemuck::{Pod, Zeroable};
use crate::{
communication::UsbDirection,
device::UsbSpeed,
device::{self, UsbSpeed},
error::UsbError,
info::{UsbDeviceClass, UsbDeviceProtocol, UsbEndpointType, UsbVersion},
info::UsbEndpointType,
};
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
@@ -91,15 +91,15 @@ pub struct UsbOtherSpeedConfiguration {
pub max_power: u8,
}
impl UsbInterfaceDescriptor {
pub fn class(&self) -> UsbDeviceClass {
UsbDeviceClass::try_from(self.interface_class).unwrap_or(UsbDeviceClass::Unknown)
}
pub fn protocol(&self) -> UsbDeviceProtocol {
UsbDeviceProtocol::try_from(self.interface_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
}
}
// 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 {
@@ -127,16 +127,16 @@ impl UsbEndpointDescriptor {
}
impl UsbDeviceDescriptor {
pub fn class(&self) -> UsbDeviceClass {
UsbDeviceClass::try_from(self.device_class).unwrap_or(UsbDeviceClass::Unknown)
}
// pub fn class(&self) -> UsbDeviceClass {
// UsbDeviceClass::try_from(self.device_class).unwrap_or(UsbDeviceClass::Unknown)
// }
pub fn protocol(&self) -> UsbDeviceProtocol {
UsbDeviceProtocol::try_from(self.device_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
}
// pub fn protocol(&self) -> UsbDeviceProtocol {
// UsbDeviceProtocol::try_from(self.device_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
// }
pub fn max_packet_size(&self, version: UsbVersion, speed: UsbSpeed) -> Result<usize, UsbError> {
match (version.is_version_3(), speed, self.max_packet_size_0) {
pub fn max_packet_size(&self, version: u16, speed: UsbSpeed) -> Result<usize, UsbError> {
match (is_version_3(version), speed, self.max_packet_size_0) {
(true, UsbSpeed::Super, 9) => Ok(1 << 9),
(true, _, _) => todo!("Non-GenX speed USB3+ maxpacketsize0"),
(false, _, 8) => Ok(8),
@@ -147,3 +147,7 @@ impl UsbDeviceDescriptor {
}
}
}
pub fn is_version_3(version: u16) -> bool {
version & 0xFF00 == 0x300
}
+72 -141
View File
@@ -1,4 +1,7 @@
use core::{fmt, ops::Deref};
use core::{
fmt,
ops::{Deref, Sub},
};
use alloc::{
boxed::Box,
@@ -8,24 +11,18 @@ use alloc::{
vec::Vec,
};
use async_trait::async_trait;
use libk::{
error::Error,
fs::sysfs::{
attribute::{StringAttribute, StringAttributeOps},
object::KObject,
},
};
use libk::error::Error;
use libk_util::{
sync::spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard},
OneTimeInit,
};
use crate::{
bus::{UsbBusWrapper, UsbDeviceKObject},
address::UsbBusAddress,
bus::UsbBusWrapper,
error::UsbError,
info::{
UsbConfigurationInfo, UsbDeviceClass, UsbDeviceInfo, UsbEndpointInfo, UsbEndpointType,
UsbInterfaceInfo, UsbVersion,
UsbConfigurationInfo, UsbDeviceInfo, UsbEndpointInfo, UsbEndpointType, UsbInterfaceInfo,
},
pipe::{
control::{ConfigurationDescriptorEntry, UsbControlPipeAccess},
@@ -34,24 +31,26 @@ use crate::{
UsbNormalPipeOut,
},
},
sysfs::UsbDeviceKObject,
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,
}
// #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
// pub struct UsbBusAddress {
// pub bus: u16,
// pub device: u8,
// }
pub struct UsbDeviceAccess {
pub device: Arc<dyn UsbDevice>,
pub bus: Arc<UsbBusWrapper>,
pub info: UsbDeviceInfo,
pub current_configuration: IrqSafeRwLock<Option<UsbConfigurationInfo>>,
pub configurations: Vec<UsbConfigurationInfo>,
current_configuration: IrqSafeRwLock<Option<usize>>,
kobject: OneTimeInit<UsbDeviceKObject>,
pub(crate) kobject: OneTimeInit<UsbDeviceKObject>,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
@@ -98,76 +97,6 @@ pub trait UsbDevice: Send + Sync {
}
impl UsbDeviceAccess {
pub(crate) fn register_sysfs_object(self: &Arc<Self>) {
struct Class;
struct Version;
struct IdVendor;
struct IdProduct;
impl StringAttributeOps for Class {
type Data = Arc<UsbDeviceAccess>;
const NAME: &'static str = "class";
fn read(state: &Self::Data) -> Result<String, Error> {
let s = match state.info.device_class {
UsbDeviceClass::Hid => "hid",
UsbDeviceClass::MassStorage => "mass-storage",
UsbDeviceClass::FromInterface => "-",
UsbDeviceClass::Unknown => "unknown",
};
Ok(s.into())
}
}
impl StringAttributeOps for Version {
type Data = Arc<UsbDeviceAccess>;
const NAME: &'static str = "version";
fn read(state: &Self::Data) -> Result<String, Error> {
Ok(state.info.usb_version.to_string())
}
}
impl StringAttributeOps for IdProduct {
type Data = Arc<UsbDeviceAccess>;
const NAME: &'static str = "product";
fn read(state: &Self::Data) -> Result<String, Error> {
Ok(format!("{:x}", state.info.id_product))
}
}
impl StringAttributeOps for IdVendor {
type Data = Arc<UsbDeviceAccess>;
const NAME: &'static str = "vendor";
fn read(state: &Self::Data) -> Result<String, Error> {
Ok(format!("{:x}", state.info.id_vendor))
}
}
let bus_kobject = self.bus.kobject();
let device_kobject = self.kobject.init(KObject::new(self.clone()));
device_kobject
.add_attribute(StringAttribute::from(Class))
.ok();
device_kobject
.add_attribute(StringAttribute::from(Version))
.ok();
device_kobject
.add_attribute(StringAttribute::from(IdVendor))
.ok();
device_kobject
.add_attribute(StringAttribute::from(IdProduct))
.ok();
let address = self.bus_address();
bus_kobject
.add_object(format!("{}", address.device), device_kobject.clone())
.ok();
}
/// Expected device state:
///
/// * Link-layer stuff has been reset and established properly by the HCD
@@ -179,43 +108,36 @@ impl UsbDeviceAccess {
let device_desc = control.query_device_descriptor().await?;
let bcd_usb = device_desc.bcd_usb;
let usb_version = UsbVersion::from_bcd_usb(device_desc.bcd_usb)
.ok_or(UsbError::InvalidDescriptorField)
.inspect_err(|_| {
log::error!(
"{}: unsupported/invalid USB version: {:#x}",
raw.bus_address(),
bcd_usb
)
})?;
let manufacturer = control.query_string(device_desc.manufacturer_str).await?;
let product = control.query_string(device_desc.product_str).await?;
// Query device
let info = UsbDeviceInfo {
manufacturer,
product,
usb_version,
usb_version: device_desc.bcd_usb,
id_vendor: device_desc.id_vendor,
id_product: device_desc.id_product,
device_class: device_desc.class(),
device_class: device_desc.device_class,
device_subclass: device_desc.device_subclass,
device_protocol: device_desc.protocol(),
device_protocol_number: device_desc.device_protocol,
device_protocol: device_desc.device_protocol,
num_configurations: device_desc.num_configurations,
max_packet_size: device_desc.max_packet_size(usb_version, raw.speed())?,
max_packet_size: device_desc.max_packet_size(device_desc.bcd_usb, raw.speed())?,
};
let configurations =
Self::query_configurations(control, device_desc.num_configurations).await?;
Ok(Self {
device: raw,
bus,
info,
current_configuration: IrqSafeRwLock::new(None),
configurations,
kobject: OneTimeInit::new(),
})
@@ -257,45 +179,54 @@ impl UsbDeviceAccess {
Ok(UsbBulkOutPipeAccess(pipe))
}
pub fn read_current_configuration(
&self,
) -> IrqSafeRwLockReadGuard<'_, Option<UsbConfigurationInfo>> {
self.current_configuration.read()
pub fn current_configuration(&self) -> Option<&UsbConfigurationInfo> {
let index = (*self.current_configuration.read())?;
Some(&self.configurations[index])
}
pub async fn select_configuration<F: Fn(&UsbConfigurationInfo) -> bool>(
pub async fn use_default_configuration(
&self,
predicate: F,
) -> Result<Option<UsbConfigurationInfo>, UsbError> {
let mut current_config = self.current_configuration.write();
let control_pipe = self.control_pipe();
for i in 0..self.info.num_configurations {
let info = self.query_configuration_info(i).await?;
if predicate(&info) {
log::debug!("Selected configuration: {:#?}", info);
let config = current_config.insert(info);
control_pipe
.set_configuration(config.config_value as _)
.await?;
return Ok(Some(config.clone()));
}
if self.configurations.len() != 1 {
return Ok(None);
}
Ok(None)
self.set_configuration(0).await.map(Some)
}
pub async fn query_configuration_info(
&self,
index: u8,
) -> Result<UsbConfigurationInfo, UsbError> {
if index >= self.info.num_configurations {
pub async fn set_configuration(&self, index: usize) -> Result<UsbConfigurationInfo, UsbError> {
if index >= self.configurations.len() {
return Err(UsbError::InvalidConfiguration);
}
let mut current = self.current_configuration.write();
let control_pipe = self.control_pipe();
let info = self.configurations[index].clone();
control_pipe
.set_configuration(info.config_value as _)
.await?;
*current = Some(index);
Ok(info)
}
async fn query_configurations(
control_pipe: &UsbControlPipeAccess,
num_configurations: u8,
) -> Result<Vec<UsbConfigurationInfo>, UsbError> {
let mut configurations = Vec::new();
for i in 0..num_configurations {
let configuration = Self::query_configuration(control_pipe, i).await?;
configurations.push(configuration);
}
Ok(configurations)
}
async fn query_configuration(
control_pipe: &UsbControlPipeAccess,
index: u8,
) -> Result<UsbConfigurationInfo, UsbError> {
let query = control_pipe.query_configuration_descriptor(index).await?;
let configuration_name = control_pipe
@@ -321,10 +252,9 @@ impl UsbDeviceAccess {
name,
number: iface.interface_number,
interface_class: iface.class(),
interface_class: iface.interface_class,
interface_subclass: iface.interface_subclass,
interface_protocol: iface.protocol(),
interface_protocol_number: iface.interface_protocol,
interface_protocol: iface.interface_protocol,
});
}
_ => (),
@@ -341,6 +271,13 @@ impl UsbDeviceAccess {
Ok(info)
}
// pub async fn query_configuration_info(
// &self,
// index: u8,
// ) -> Result<UsbConfigurationInfo, UsbError> {
// let control_pipe = self.control_pipe();
// }
pub fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>) {
self.device.set_detach_handler(handler);
}
@@ -353,9 +290,3 @@ impl Deref for UsbDeviceAccess {
&*self.device
}
}
impl fmt::Display for UsbBusAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.bus, self.device)
}
}
+70 -61
View File
@@ -29,41 +29,51 @@ pub enum UsbUsageType {
Reserved,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum UsbVersion {
Usb11,
Usb20,
Usb21,
Usb30,
Usb31,
Usb32,
}
// #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
// pub enum UsbVersion {
// Usb11,
// Usb20,
// Usb21,
// Usb30,
// Usb31,
// Usb32,
// }
primitive_enum! {
pub enum UsbDeviceClass: u8 {
FromInterface = 0x00,
Hid = 0x03,
MassStorage = 0x08,
Unknown = 0xFF,
}
}
pub const CLASS_FROM_INTERFACE: u8 = 0x00;
pub const CLASS_HID: u8 = 0x03;
pub const CLASS_MASS_STORAGE: u8 = 0x08;
primitive_enum! {
pub enum UsbDeviceProtocol: u8 {
FromInterface = 0x00,
Unknown = 0xFF,
}
}
// primitive_enum! {
// pub enum UsbDeviceClass: u8 {
// FromInterface = 0x00,
// Hid = 0x03,
// MassStorage = 0x08,
// 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_class: u8,
pub interface_subclass: u8,
pub interface_protocol: UsbDeviceProtocol,
pub interface_protocol_number: u8,
pub interface_protocol: u8,
// pub name: String,
// pub number: u8,
// pub interface_class: UsbDeviceClass,
// pub interface_subclass: u8,
// pub interface_protocol: UsbDeviceProtocol,
// pub interface_protocol_number: u8,
}
#[derive(Debug, Clone)]
@@ -87,15 +97,14 @@ pub struct UsbDeviceInfo {
pub manufacturer: String,
pub product: String,
pub usb_version: UsbVersion,
pub usb_version: u16,
pub id_vendor: u16,
pub id_product: u16,
pub device_class: UsbDeviceClass,
pub device_class: u8,
pub device_subclass: u8,
pub device_protocol: UsbDeviceProtocol,
pub device_protocol_number: u8,
pub device_protocol: u8,
/// Max packet size for endpoint zero
pub max_packet_size: usize,
@@ -103,37 +112,37 @@ pub struct UsbDeviceInfo {
pub num_configurations: u8,
}
impl UsbVersion {
pub fn is_version_3(&self) -> bool {
matches!(self, Self::Usb30 | Self::Usb31 | Self::Usb32)
}
pub fn from_bcd_usb(value: u16) -> Option<Self> {
match value {
0x110 => Some(UsbVersion::Usb11),
0x200..=0x20F => Some(UsbVersion::Usb20),
0x210..=0x21F => Some(UsbVersion::Usb21),
0x300 => Some(UsbVersion::Usb30),
0x310 => Some(UsbVersion::Usb31),
0x320 => Some(UsbVersion::Usb32),
_ => None,
}
}
}
impl fmt::Display for UsbVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let string = match self {
Self::Usb11 => "USB1.1",
Self::Usb20 => "USB2.0",
Self::Usb21 => "USB2.1",
Self::Usb30 => "USB3.0",
Self::Usb31 => "USB3.1",
Self::Usb32 => "USB3.2",
};
f.write_str(string)
}
}
// impl UsbVersion {
// pub fn is_version_3(&self) -> bool {
// matches!(self, Self::Usb30 | Self::Usb31 | Self::Usb32)
// }
//
// pub fn from_bcd_usb(value: u16) -> Option<Self> {
// match value {
// 0x110 => Some(UsbVersion::Usb11),
// 0x200..=0x20F => Some(UsbVersion::Usb20),
// 0x210..=0x21F => Some(UsbVersion::Usb21),
// 0x300 => Some(UsbVersion::Usb30),
// 0x310 => Some(UsbVersion::Usb31),
// 0x320 => Some(UsbVersion::Usb32),
// _ => None,
// }
// }
// }
//
// impl fmt::Display for UsbVersion {
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// let string = match self {
// Self::Usb11 => "USB1.1",
// Self::Usb20 => "USB2.0",
// Self::Usb21 => "USB2.1",
// Self::Usb30 => "USB3.0",
// Self::Usb31 => "USB3.1",
// Self::Usb32 => "USB3.2",
// };
// f.write_str(string)
// }
// }
impl UsbEndpointInfo {
pub fn is(&self, ty: UsbEndpointType, dir: UsbDirection) -> bool {
+1
View File
@@ -0,0 +1 @@
pub struct UsbInterface {}
+4 -1
View File
@@ -8,17 +8,20 @@
maybe_uninit_fill
)]
use crate::bus::UsbBusKObject;
use crate::sysfs::UsbBusKObject;
extern crate alloc;
pub mod address;
pub mod bus;
pub mod communication;
pub mod descriptor;
pub mod device;
pub mod error;
pub mod info;
pub mod interface;
pub mod pipe;
pub mod sysfs;
pub mod util;
pub mod class_driver;
@@ -37,6 +37,7 @@ pub trait UsbDeviceRequest: Sized + Pod {
pub trait UsbClassSpecificRequest: Sized + Pod {
const BM_REQUEST_TYPE: u8;
const B_REQUEST: u8;
const W_VALUE: u16 = 0;
}
pub trait UsbDescriptorRequest: UsbDeviceRequest {
+129
View File
@@ -0,0 +1,129 @@
use alloc::{format, sync::Arc};
use libk::{
error::Error,
fs::sysfs::{
self,
attribute::{IntegerAttribute, IntegerAttributeFormat, IntegerAttributeOps},
object::KObject,
},
};
use libk_util::OneTimeInit;
use crate::{bus::UsbBusWrapper, device::UsbDeviceAccess};
pub type UsbBusKObject = Arc<KObject<Arc<UsbBusWrapper>>>;
pub type UsbDeviceKObject = Arc<KObject<Arc<UsbDeviceAccess>>>;
pub(crate) fn register_bus_kobject(bus: &Arc<UsbBusWrapper>) -> UsbBusKObject {
let root = sysfs_usb_root();
let bus_kobject = KObject::new(bus.clone());
bus.hc.register_sysfs_properties(&bus_kobject);
root.add_object(format!("{}", bus.index), bus_kobject.clone())
.ok();
bus_kobject
}
pub(crate) fn register_device_kobject(device: &Arc<UsbDeviceAccess>) -> UsbDeviceKObject {
struct Class;
struct Subclass;
struct Protocol;
struct Version;
struct IdVendor;
struct IdProduct;
impl IntegerAttributeOps<u8> for Class {
type Data = Arc<UsbDeviceAccess>;
const NAME: &'static str = "class";
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Hex;
fn read(state: &Self::Data) -> Result<u8, Error> {
Ok(state.info.device_class)
}
}
impl IntegerAttributeOps<u8> for Subclass {
type Data = Arc<UsbDeviceAccess>;
const NAME: &'static str = "subclass";
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Hex;
fn read(state: &Self::Data) -> Result<u8, Error> {
Ok(state.info.device_subclass)
}
}
impl IntegerAttributeOps<u8> for Protocol {
type Data = Arc<UsbDeviceAccess>;
const NAME: &'static str = "protocol";
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Hex;
fn read(state: &Self::Data) -> Result<u8, Error> {
Ok(state.info.device_protocol)
}
}
impl IntegerAttributeOps<u16> for Version {
type Data = Arc<UsbDeviceAccess>;
const NAME: &'static str = "version";
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Hex;
fn read(state: &Self::Data) -> Result<u16, Error> {
Ok(state.info.usb_version)
}
}
impl IntegerAttributeOps<u16> for IdVendor {
type Data = Arc<UsbDeviceAccess>;
const NAME: &'static str = "vendor";
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Hex;
fn read(state: &Self::Data) -> Result<u16, Error> {
Ok(state.info.id_vendor)
}
}
impl IntegerAttributeOps<u16> for IdProduct {
type Data = Arc<UsbDeviceAccess>;
const NAME: &'static str = "product";
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Hex;
fn read(state: &Self::Data) -> Result<u16, Error> {
Ok(state.info.id_product)
}
}
let bus_kobject = device.bus.kobject();
let device_kobject = KObject::new(device.clone());
device_kobject
.add_attribute(IntegerAttribute::from(Class))
.ok();
device_kobject
.add_attribute(IntegerAttribute::from(Subclass))
.ok();
device_kobject
.add_attribute(IntegerAttribute::from(Protocol))
.ok();
device_kobject
.add_attribute(IntegerAttribute::from(Version))
.ok();
device_kobject
.add_attribute(IntegerAttribute::from(IdVendor))
.ok();
device_kobject
.add_attribute(IntegerAttribute::from(IdProduct))
.ok();
let address = device.bus_address();
bus_kobject
.add_object(format!("{}", address.device), device_kobject.clone())
.ok();
device_kobject
}
fn sysfs_usb_root() -> &'static Arc<KObject<()>> {
static USB_ROOT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
USB_ROOT.or_init_with(|| {
let bus_object = sysfs::bus().expect("bus object");
let usb_object = KObject::new(());
bus_object.add_object("usb", usb_object.clone()).ok();
usb_object
})
}
+67 -8
View File
@@ -9,14 +9,20 @@ use async_trait::async_trait;
use device_api::device::Device;
use libk::{device::char::CharDevice, vfs::FileReadiness};
use libk_util::{ring::LossyRingQueue, OneTimeInit};
use yggdrasil_abi::{error::Error, io::KeyboardKeyEvent};
use yggdrasil_abi::{
abi_serde::wire,
error::Error,
io::{KeyboardKeyEvent, MouseEvent},
};
#[derive(Clone, Copy)]
pub struct KeyboardDevice;
#[derive(Clone, Copy)]
pub struct MouseDevice;
impl FileReadiness for KeyboardDevice {
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
INPUT_QUEUE.poll_readable(cx).map(Ok)
KEYBOARD_INPUT_QUEUE.poll_readable(cx).map(Ok)
}
}
@@ -33,7 +39,7 @@ impl CharDevice for KeyboardDevice {
return Ok(0);
}
let ev = INPUT_QUEUE.read().await;
let ev = KEYBOARD_INPUT_QUEUE.read().await;
buf[..4].copy_from_slice(&ev.as_bytes());
@@ -45,7 +51,7 @@ impl CharDevice for KeyboardDevice {
return Ok(0);
}
let ev = INPUT_QUEUE.try_read().ok_or(Error::WouldBlock)?;
let ev = KEYBOARD_INPUT_QUEUE.try_read().ok_or(Error::WouldBlock)?;
buf[..4].copy_from_slice(&ev.as_bytes());
@@ -68,15 +74,68 @@ impl CharDevice for KeyboardDevice {
}
}
static INPUT_QUEUE: LossyRingQueue<KeyboardKeyEvent> = LossyRingQueue::with_capacity(32);
impl FileReadiness for MouseDevice {
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
MOUSE_INPUT_QUEUE.poll_readable(cx).map(Ok)
}
}
impl Device for MouseDevice {
fn display_name(&self) -> &str {
"Mouse input pseudo-device"
}
}
#[async_trait]
impl CharDevice for MouseDevice {
async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
let ev = MOUSE_INPUT_QUEUE.read().await;
let len = wire::to_slice(&ev, buf)?;
Ok(len)
}
fn read_nonblocking(&self, buf: &mut [u8]) -> Result<usize, Error> {
let ev = MOUSE_INPUT_QUEUE.try_read().ok_or(Error::WouldBlock)?;
let len = wire::to_slice(&ev, buf)?;
Ok(len)
}
fn is_writeable(&self) -> bool {
false
}
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
let _ = option;
let _ = buffer;
let _ = len;
Err(Error::InvalidOperation)
}
fn is_terminal(&self) -> bool {
false
}
}
static KEYBOARD_INPUT_QUEUE: LossyRingQueue<KeyboardKeyEvent> = LossyRingQueue::with_capacity(32);
static KEYBOARD_DEVICE: OneTimeInit<Arc<KeyboardDevice>> = OneTimeInit::new();
pub fn setup() -> Arc<KeyboardDevice> {
static MOUSE_INPUT_QUEUE: LossyRingQueue<MouseEvent> = LossyRingQueue::with_capacity(32);
static MOUSE_DEVICE: OneTimeInit<Arc<MouseDevice>> = OneTimeInit::new();
pub fn setup_keyboard() -> Arc<KeyboardDevice> {
KEYBOARD_DEVICE
.or_init_with(|| Arc::new(KeyboardDevice))
.clone()
}
pub fn send_event(ev: KeyboardKeyEvent) {
INPUT_QUEUE.write(ev);
pub fn setup_mouse() -> Arc<MouseDevice> {
MOUSE_DEVICE.or_init_with(|| Arc::new(MouseDevice)).clone()
}
pub fn send_keyboard_event(ev: KeyboardKeyEvent) {
KEYBOARD_INPUT_QUEUE.write(ev);
}
pub fn send_mouse_event(ev: MouseEvent) {
MOUSE_INPUT_QUEUE.write(ev);
}
+6 -7
View File
@@ -25,10 +25,11 @@ use tock_registers::{
};
use ygg_driver_pci::{device::PciDeviceInfo, PciConfigurationSpace};
use ygg_driver_usb::{
address::UsbBusAddress,
bus::{UsbBusManager, UsbBusWrapper},
device::{UsbBusAddress, UsbDeviceAccess, UsbSpeed},
descriptor,
device::{UsbDeviceAccess, UsbSpeed},
error::UsbError,
info::UsbVersion,
pipe::control::UsbControlPipeAccess,
UsbHostController,
};
@@ -67,7 +68,7 @@ struct ScratchpadArray {
}
struct RootHubPort {
version: UsbVersion,
version: u16,
slot_type: u8,
}
@@ -147,9 +148,7 @@ impl Xhci {
for cap in regs.extended_capabilities.iter() {
match cap {
ExtendedCapability::ProtocolSupport(support) => {
let Some(version) = support.usb_revision() else {
continue;
};
let version = support.usb_revision();
for port in support.port_range() {
log::info!("* Port {port}: {version}");
@@ -284,7 +283,7 @@ impl Xhci {
.as_ref()
.ok_or(UsbError::PortInitFailed)?;
let need_reset = !root_hub_port.version.is_version_3();
let need_reset = !descriptor::is_version_3(root_hub_port.version);
if need_reset {
self.reset_port(regs).await?;
+2 -1
View File
@@ -6,8 +6,9 @@ use libk_util::{
};
use xhci_lib::context;
use ygg_driver_usb::{
address::UsbBusAddress,
communication::UsbDirection,
device::{UsbBusAddress, UsbDevice, UsbDeviceDetachHandler, UsbSpeed},
device::{UsbDevice, UsbDeviceDetachHandler, UsbSpeed},
error::UsbError,
info::UsbEndpointType,
pipe::{
+3 -3
View File
@@ -7,7 +7,7 @@ use alloc::vec::Vec;
use libk::error::Error;
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use ygg_driver_usb::{error::UsbError, info::UsbVersion};
use ygg_driver_usb::error::UsbError;
pub struct ProtocolSupport {
words: [u32; 4],
@@ -69,8 +69,8 @@ impl ExtendedCapability {
}
impl ProtocolSupport {
pub fn usb_revision(&self) -> Option<UsbVersion> {
UsbVersion::from_bcd_usb((self.words[0] >> 16) as u16)
pub fn usb_revision(&self) -> u16 {
(self.words[0] >> 16) as u16
}
pub fn slot_type(&self) -> u8 {
@@ -0,0 +1,239 @@
use core::{
any::Any,
marker::PhantomData,
sync::atomic::{AtomicBool, Ordering},
};
use alloc::{
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use yggdrasil_abi::{
error::Error,
io::{FileMode, OpenOptions},
};
use crate::{
fs::sysfs::object::KObject,
vfs::{CommonImpl, Filename, InstanceData, Metadata, Node, NodeFlags, NodeRef, RegularImpl},
};
use super::Attribute;
pub trait IntegerAttributeValue: Copy + Sync + Send + 'static {
fn to_bytes(&self, format: IntegerAttributeFormat) -> Vec<u8>;
}
pub enum IntegerAttributeFormat {
Decimal,
Octal,
Hex,
}
macro_rules! impl_integer_value {
($($ty:ty),+) => {
$(
impl IntegerAttributeValue for $ty {
fn to_bytes(&self, format: IntegerAttributeFormat) -> Vec<u8> {
match format {
IntegerAttributeFormat::Decimal => alloc::format!("{self}"),
IntegerAttributeFormat::Octal => alloc::format!("{self:o}"),
IntegerAttributeFormat::Hex => alloc::format!("{self:x}"),
}.into_bytes()
}
}
)+
};
}
impl_integer_value!(u8, u16, u32, u64);
pub trait IntegerAttributeOps<T: IntegerAttributeValue>: Sync + Send + 'static {
type Data: Send + 'static = ();
const WRITEABLE: bool = false;
const NAME: &'static str;
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Decimal;
fn read(state: &Self::Data) -> Result<T, Error> {
let _ = state;
Err(Error::NotImplemented)
}
fn write(state: &Self::Data, value: T) -> Result<(), Error> {
let _ = state;
let _ = value;
Err(Error::ReadOnly)
}
}
pub struct IntegerAttribute<T: IntegerAttributeValue, V: IntegerAttributeOps<T>>(
PhantomData<(T, V)>,
);
struct IntegerAttributeNode<T: IntegerAttributeValue, V: IntegerAttributeOps<T>> {
object: Arc<KObject<V::Data>>,
_pd: PhantomData<V>,
}
struct IntegerAttributeState<T: IntegerAttributeValue> {
value: IrqSafeRwLock<Vec<u8>>,
modified: AtomicBool,
_pd: PhantomData<T>,
}
impl<T: IntegerAttributeValue, V: IntegerAttributeOps<T>> CommonImpl
for IntegerAttributeNode<T, V>
{
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
Ok(0)
}
fn as_any(&self) -> &dyn Any {
self as _
}
}
impl<T: IntegerAttributeValue, V: IntegerAttributeOps<T>> RegularImpl
for IntegerAttributeNode<T, V>
{
fn open(
&self,
_node: &NodeRef,
opts: OpenOptions,
) -> Result<(u64, Option<InstanceData>), Error> {
if opts.contains(OpenOptions::WRITE) && !V::WRITEABLE {
return Err(Error::ReadOnly);
}
let mut value = V::read(self.object.data())?.to_bytes(V::FORMAT);
value.push(b'\n');
let instance = IntegerAttributeState {
value: IrqSafeRwLock::new(value),
modified: AtomicBool::new(false),
_pd: PhantomData::<T>,
};
Ok((0, Some(Arc::new(instance))))
}
fn close(&self, _node: &NodeRef, instance: Option<&InstanceData>) -> Result<(), Error> {
if V::WRITEABLE {
todo!()
// let instance = instance.ok_or(Error::InvalidFile)?;
// let instance = instance
// .downcast_ref::<StringAttributeState>()
// .ok_or(Error::InvalidFile)?;
// if instance.modified.load(Ordering::Acquire) {
// let value = instance.value.read();
// let value_str =
// core::str::from_utf8(&value[..]).map_err(|_| Error::InvalidArgument)?;
// // Trim whitespace and newlines
// V::write(&self.object.data, value_str.trim())?;
// }
}
Ok(())
}
fn read(
&self,
_node: &NodeRef,
instance: Option<&InstanceData>,
pos: u64,
buf: &mut [u8],
) -> Result<usize, Error> {
let instance = instance.ok_or(Error::InvalidFile)?;
let instance = instance
.downcast_ref::<IntegerAttributeState<T>>()
.ok_or(Error::InvalidFile)?;
let value = instance.value.read();
let len = value.len();
if pos >= len as u64 {
return Ok(0);
}
let pos = pos as usize;
let amount = (len - pos).min(buf.len());
buf[..amount].copy_from_slice(&value[pos..pos + amount]);
Ok(amount)
}
fn write(
&self,
_node: &NodeRef,
instance: Option<&InstanceData>,
pos: u64,
buf: &[u8],
) -> Result<usize, Error> {
todo!()
// if !V::WRITEABLE {
// return Err(Error::InvalidFile);
// }
// let instance = instance.ok_or(Error::InvalidFile)?;
// let instance = instance
// .downcast_ref::<StringAttributeState>()
// .ok_or(Error::InvalidFile)?;
// let mut value = instance.value.write();
// let pos: usize = pos.try_into().map_err(|_| Error::InvalidFile)?;
// if pos > value.len() {
// return Err(Error::InvalidArgument);
// }
// if pos + buf.len() > V::LIMIT {
// return Err(Error::InvalidArgument);
// }
// let amount_copy = (value.len() - pos).min(buf.len());
// value[pos..pos + amount_copy].copy_from_slice(&buf[..amount_copy]);
// if amount_copy < buf.len() {
// value.extend_from_slice(&buf[amount_copy..]);
// }
// instance.modified.store(true, Ordering::Release);
// Ok(buf.len())
}
fn truncate(&self, _node: &NodeRef, _new_size: u64) -> Result<(), Error> {
Ok(())
}
}
impl<T: IntegerAttributeValue, V: IntegerAttributeOps<T>> From<V> for IntegerAttribute<T, V> {
fn from(_value: V) -> Self {
Self(PhantomData)
}
}
impl<T: IntegerAttributeValue, V: IntegerAttributeOps<T>> Attribute<V::Data>
for IntegerAttribute<T, V>
{
fn instantiate(&self, parent: &Arc<KObject<V::Data>>) -> Result<Arc<Node>, Error> {
let mode = match V::WRITEABLE {
false => FileMode::new(0o444),
true => FileMode::new(0o644),
};
Ok(Node::regular(
IntegerAttributeNode {
object: parent.clone(),
_pd: PhantomData::<V>,
},
NodeFlags::IN_MEMORY_PROPS,
Some(Metadata::now_root(mode, 0)),
None,
))
}
// TODO implement this properly
fn name(&self) -> &Filename {
unsafe { Filename::from_str_unchecked(V::NAME) }
}
}
@@ -6,6 +6,7 @@ use crate::vfs::{Filename, NodeRef};
use super::object::KObject;
mod bytes;
mod integer;
mod string;
pub trait Attribute<D>: Sync + Send {
@@ -14,4 +15,7 @@ pub trait Attribute<D>: Sync + Send {
}
pub use bytes::{BytesAttribute, BytesAttributeOps};
pub use integer::{
IntegerAttribute, IntegerAttributeFormat, IntegerAttributeOps, IntegerAttributeValue,
};
pub use string::{StringAttribute, StringAttributeOps};
+1 -1
View File
@@ -140,7 +140,7 @@ impl I686 {
TerminalInput::with_capacity(256)?,
ConsoleWrapper(textfb),
));
let keyboard_input = ygg_driver_input::setup();
let keyboard_input = ygg_driver_input::setup_keyboard();
runtime::spawn(
textfb_console
+1 -1
View File
@@ -108,7 +108,7 @@ impl InterruptHandler for PS2Controller {
inner.e0 = false;
ygg_driver_input::send_event(event);
ygg_driver_input::send_keyboard_event(event);
}
count != 0
+12 -3
View File
@@ -168,11 +168,20 @@ pub fn kernel_main() -> ! {
CPU_INIT_FENCE.wait_all(ArchitectureImpl::cpu_count());
// Add keyboard device
if let Err(error) =
devfs::add_named_char_device(ygg_driver_input::setup(), "kbd", FileMode::new(0o660))
{
if let Err(error) = devfs::add_named_char_device(
ygg_driver_input::setup_keyboard(),
"kbd",
FileMode::new(0o660),
) {
log::error!("Couldn't add keyboard device: {error:?}");
}
if let Err(error) = devfs::add_named_char_device(
ygg_driver_input::setup_mouse(),
"mouse",
FileMode::new(0o440),
) {
log::error!("Couldn't add pointer device: {error:?}");
}
task::init().expect("Failed to initialize the scheduler");
+39
View File
@@ -1,3 +1,5 @@
use abi_serde::{impl_newtype_serde, impl_struct_serde};
/// Describes a key pressed/released on a keyboard device
// Missing docs: self-explanatory names
#[allow(missing_docs)]
@@ -47,6 +49,43 @@ pub enum KeyboardKeyEvent {
Released(KeyboardKey),
}
/// Representation for button press state
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(transparent)]
pub struct ButtonMask(pub u8);
impl_newtype_serde!(ButtonMask);
/// Representation for mouse event
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MouseEvent {
/// Current button state
pub buttons: ButtonMask,
/// X delta
pub dx: i32,
/// Y delta
pub dy: i32,
}
impl_struct_serde!(MouseEvent: [
buttons,
dx,
dy
]);
impl MouseEvent {
/// Returns `true` if the event has corresponding button set
pub fn button(&self, index: usize) -> bool {
if index >= 8 {
false
} else {
self.buttons.0 & (1 << index) != 0
}
}
}
impl KeyboardKey {
/// Converts [KeyboardKey] to its related [KeyboardKeyCode]
pub const fn code(self) -> KeyboardKeyCode {
+1 -1
View File
@@ -13,7 +13,7 @@ pub use crate::generated::{
OpenOptions, PollControl, RawFd, RemoveFlags, TimerOptions, UnmountOptions, UserId,
};
pub use file::{FileMetadataUpdate, FileMetadataUpdateMode, FileTimesUpdate, SeekFrom};
pub use input::{KeyboardKey, KeyboardKeyCode, KeyboardKeyEvent};
pub use input::{ButtonMask, KeyboardKey, KeyboardKeyCode, KeyboardKeyEvent, MouseEvent};
pub use terminal::{
TerminalControlCharacters, TerminalInputOptions, TerminalLineOptions, TerminalOptions,
TerminalOutputOptions, TerminalSize,
+7
View File
@@ -0,0 +1,7 @@
use abi::error::Error;
pub use abi::io::{ButtonMask, MouseEvent};
use abi_serde::wire;
pub fn parse_mouse_event(buffer: &[u8]) -> Result<MouseEvent, Error> {
wire::from_slice(buffer).map_err(Error::from)
}
+1
View File
@@ -13,6 +13,7 @@ pub use abi::option::OptionSizeHint;
pub mod device;
pub mod filesystem;
pub mod input;
pub mod paths;
pub mod terminal;
pub use paths::*;