usb: add ft232 driver

This commit is contained in:
2026-02-03 17:28:15 +02:00
parent 6b5dd9f673
commit ed9d7a7145
7 changed files with 381 additions and 11 deletions
@@ -377,8 +377,8 @@ impl UsbDeviceDriver for UsbHubDriver {
"USB Hub"
}
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool {
let _ = protocol;
fn probe(&self, class: u8, subclass: u8, protocol: u8, vid: u16, pid: u16) -> bool {
let _ = (protocol, vid, pid);
class == 0x09 && subclass == 0x00
}
}
@@ -12,13 +12,14 @@ use crate::{
mod hid;
mod hub;
mod mass_storage;
mod serial;
#[async_trait]
pub trait UsbDeviceDriver: Send + Sync {
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError>;
fn name(&self) -> &'static str;
fn probe(&self, class: u8, subclass: u8, protocol: u8) -> bool;
fn probe(&self, class: u8, subclass: u8, protocol: u8, vid: u16, pid: u16) -> bool;
}
#[async_trait]
@@ -33,9 +34,11 @@ async fn spawn_device_driver(device: Arc<UsbDeviceAccess>) -> Result<bool, UsbEr
let class = device.device_descriptor.device_class;
let subclass = device.device_descriptor.device_subclass;
let protocol = device.device_descriptor.device_protocol;
let vid = device.device_descriptor.id_vendor;
let pid = device.device_descriptor.id_product;
let Some(driver) = USB_DEVICE_DRIVERS.read().iter().find_map(|driver| {
driver
.probe(class, subclass, protocol)
.probe(class, subclass, protocol, vid, pid)
.then(|| driver.clone())
}) else {
return Ok(false);
@@ -127,6 +130,7 @@ pub fn register_interface_driver(driver: Arc<dyn UsbInterfaceDriver + 'static>)
pub fn register_default_class_drivers() {
register_device_driver(Arc::new(hub::UsbHubDriver));
register_device_driver(Arc::new(serial::FT232Driver));
register_interface_driver(Arc::new(hid::UsbHidKeyboardDriver));
register_interface_driver(Arc::new(hid::UsbHidMouseDriver));
register_interface_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
@@ -0,0 +1,311 @@
use core::sync::atomic::{AtomicBool, Ordering};
use alloc::{boxed::Box, collections::btree_map::BTreeMap, sync::Arc};
use async_trait::async_trait;
use libk::{
block,
error::Error,
fs::devfs,
vfs::{Terminal, TerminalInput, TerminalOutput},
};
use libk_util::sync::{IrqSafeSpinlock, spin_rwlock::IrqSafeRwLock};
use yggdrasil_abi::{
io::{FileMode, TerminalOptions, TerminalOutputOptions},
process::ProcessId,
};
use crate::{
class_driver::UsbDeviceDriver,
communication::UsbDirection,
device::{UsbDeviceAccess, UsbDeviceDetachHandler},
error::UsbError,
info::UsbEndpointType,
pipe::{control::ControlTransferSetup, normal::UsbBulkOutPipeAccess},
};
pub struct FT232Driver;
struct FT232Device {
device: Arc<UsbDeviceAccess>,
bulk_out: UsbBulkOutPipeAccess,
baud_rate: IrqSafeRwLock<u32>,
}
pub struct UsbSerialDeviceWrapper {
device: Arc<dyn UsbSerialDevice>,
index: u32,
disconnected: AtomicBool,
}
#[async_trait]
pub trait UsbSerialDevice: Send + Sync + 'static {
async fn setup(&self) -> Result<(), UsbError>;
async fn set_baud_rate(&self, baud: u32) -> Result<(), UsbError>;
fn baud_rate(&self) -> u32;
async fn write(&self, buffer: &[u8], options: TerminalOutputOptions)
-> Result<usize, UsbError>;
fn display_name(&self) -> &str;
fn device(&self) -> &Arc<UsbDeviceAccess>;
}
#[async_trait]
impl UsbDeviceDetachHandler for UsbSerialDeviceWrapper {
async fn handle_device_detach(&self) {
self.disconnected.store(true, Ordering::Release);
log::info!("USB serial #{} disconnected", self.index);
remove_usb_serial(self.index);
}
}
impl TerminalOutput for UsbSerialDeviceWrapper {
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
self.write_multiple(&[byte], options)?;
Ok(())
}
fn write_multiple(
&self,
bytes: &[u8],
options: &TerminalOutputOptions,
) -> Result<usize, Error> {
if self.disconnected.load(Ordering::Acquire) {
return Err(Error::InvalidOperation);
}
let result = block!(self.device.write(bytes, *options).await)?;
match result {
Ok(len) => Ok(len),
Err(UsbError::MemoryError(err) | UsbError::SystemError(err)) => Err(err),
Err(error) => {
log::warn!("{}: write error: {:?}", self.device.display_name(), error);
Err(Error::InvalidOperation)
}
}
}
fn baud_rate(&self) -> u32 {
self.device.baud_rate()
}
fn set_baud_rate(&self, baud: u32) -> Result<(), Error> {
if self.disconnected.load(Ordering::Acquire) {
return Err(Error::InvalidOperation);
}
let result = block!(self.device.set_baud_rate(baud).await)?;
match result {
Ok(()) => Ok(()),
Err(UsbError::MemoryError(err) | UsbError::SystemError(err)) => Err(err),
Err(error) => {
log::warn!(
"{}: baud rate set error: {:?}",
self.device.display_name(),
error
);
Err(Error::InvalidOperation)
}
}
}
fn open(&self, pid: ProcessId) -> Result<(), Error> {
if self.disconnected.load(Ordering::Acquire) {
return Err(Error::InvalidOperation);
}
let _ = pid;
let result = block! { self.device.setup().await }?;
match result {
Ok(()) => Ok(()),
Err(UsbError::MemoryError(err) | UsbError::SystemError(err)) => Err(err),
Err(error) => {
log::warn!("{}: setup error: {:?}", self.device.display_name(), error);
Err(Error::InvalidOperation)
}
}
}
}
impl FT232Device {
const FTDI_RESET: u8 = 0;
const FTDI_SET_BAUD_RATE: u8 = 3;
async fn ftdi_reset(&self, port: u16) -> Result<(), UsbError> {
self.device
.control_pipe()
.control_transfer(ControlTransferSetup {
bm_request_type: 0b01000000,
b_request: Self::FTDI_RESET,
w_value: 0,
w_index: port,
w_length: 0,
})
.await
}
async fn ftdi_set_baud_rate(&self, port: u16, baud: u32) -> Result<(), UsbError> {
let w_value = match baud {
300 => 0x2710,
600 => 0x1388,
1200 => 0x09C4,
2400 => 0x04E2,
4800 => 0x0271,
9600 => 0x4138,
19200 => 0x809C,
38400 => 0xC04E,
57600 => 0x0034,
115200 => 0x001A,
230400 => 0x000D,
460800 => 0x4006,
921600 => 0x8003,
_ => {
log::warn!("ft232: unsupported baud rate {baud}");
return Err(UsbError::InvalidConfiguration);
}
};
self.device
.control_pipe()
.control_transfer(ControlTransferSetup {
bm_request_type: 0b01000000,
b_request: Self::FTDI_SET_BAUD_RATE,
w_value,
w_index: port,
w_length: 0,
})
.await
}
}
#[async_trait]
impl UsbSerialDevice for FT232Device {
async fn setup(&self) -> Result<(), UsbError> {
log::info!("ft232: setup");
Ok(())
}
async fn set_baud_rate(&self, baud: u32) -> Result<(), UsbError> {
self.ftdi_set_baud_rate(0, baud).await?;
// *self.baud_rate.write() = baud;
log::info!("ft232: set baud rate {baud}");
Ok(())
}
fn baud_rate(&self) -> u32 {
*self.baud_rate.read()
}
async fn write(
&self,
buffer: &[u8],
options: TerminalOutputOptions,
) -> Result<usize, UsbError> {
if options.contains(TerminalOutputOptions::NL_TO_CRNL) {
for &byte in buffer {
if byte == b'\n' {
self.bulk_out.write(b"\r").await?;
}
self.bulk_out.write(&[byte]).await?;
}
Ok(buffer.len())
} else {
self.bulk_out.write(buffer).await
}
}
fn display_name(&self) -> &str {
"FT232 Serial Converter"
}
fn device(&self) -> &Arc<UsbDeviceAccess> {
&self.device
}
}
#[async_trait]
impl UsbDeviceDriver for FT232Driver {
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
let interface = device.interface(0);
let endpoints = interface.endpoints();
let bulk_in = endpoints
.iter()
.find(|ep| ep.ty == UsbEndpointType::Bulk && ep.direction == UsbDirection::In)
.ok_or(UsbError::InvalidConfiguration)?;
let bulk_out = endpoints
.iter()
.find(|ep| ep.ty == UsbEndpointType::Bulk && ep.direction == UsbDirection::Out)
.ok_or(UsbError::InvalidConfiguration)?;
let bulk_in = device
.open_bulk_in_pipe(bulk_in.number, bulk_in.max_packet_size as _)
.await?;
let bulk_out = device
.open_bulk_out_pipe(bulk_out.number, bulk_out.max_packet_size as _)
.await?;
let ft232 = Arc::new(FT232Device {
device,
bulk_out,
baud_rate: IrqSafeRwLock::new(115200),
});
ft232.ftdi_reset(0).await?;
let terminal = register_usb_serial(ft232)?;
let mut buffer = [0; 3];
loop {
let len = bulk_in.read(&mut buffer).await?;
if len < 2 {
continue;
}
for &byte in &buffer[2..len] {
terminal.write_to_input(byte);
}
}
}
fn name(&self) -> &'static str {
"FT232 Serial Converter"
}
fn probe(&self, class: u8, subclass: u8, protocol: u8, vid: u16, pid: u16) -> bool {
let _ = (class, subclass, protocol);
vid == 0x0403 && pid == 0x6001
}
}
static USB_SERIALS: IrqSafeSpinlock<BTreeMap<u32, Arc<Terminal<Arc<UsbSerialDeviceWrapper>>>>> =
IrqSafeSpinlock::new(BTreeMap::new());
fn register_usb_serial(
serial: Arc<dyn UsbSerialDevice>,
) -> Result<Arc<Terminal<Arc<UsbSerialDeviceWrapper>>>, UsbError> {
let mut serials = USB_SERIALS.lock();
for i in 0..64 {
if serials.contains_key(&i) {
continue;
}
let wrapper = Arc::new(UsbSerialDeviceWrapper {
device: serial,
index: i,
disconnected: AtomicBool::new(false),
});
wrapper.device.device().set_detach_handler(wrapper.clone());
let input = TerminalInput::with_capacity(64).expect("Couldn't allocate input buffer");
let terminal = Arc::new(Terminal::from_parts(
TerminalOptions::const_default(),
input,
wrapper,
));
serials.insert(i, terminal.clone());
let name = alloc::format!("ttyUSB{i}");
devfs::add_named_char_device(terminal.clone(), name.clone(), FileMode::new(0o600)).ok();
return Ok(terminal);
}
Err(UsbError::DriverError)
}
fn remove_usb_serial(index: u32) {
let serial = USB_SERIALS.lock().remove(&index);
if serial.is_none() {
log::warn!("usb-serial #{index} doesn't exist in the table");
}
let name = alloc::format!("ttyUSB{index}");
devfs::remove_node(name).ok();
}
+4 -4
View File
@@ -41,11 +41,11 @@ pub trait CharDevice: Device + FileReadiness {
Err(Error::NotImplemented)
}
fn lock(&self, process: ProcessId) -> Result<(), Error> {
fn open(&self, process: ProcessId) -> Result<(), Error> {
let _ = process;
Ok(())
}
fn release(&self, process: ProcessId) -> Result<(), Error> {
fn close(&self, process: ProcessId) -> Result<(), Error> {
let _ = process;
Ok(())
}
@@ -137,10 +137,10 @@ impl CharDevice for I2CDevice {
}
}
fn lock(&self, process: ProcessId) -> Result<(), Error> {
fn open(&self, process: ProcessId) -> Result<(), Error> {
I2CDevice::lock(self, process)
}
fn release(&self, process: ProcessId) -> Result<(), Error> {
fn close(&self, process: ProcessId) -> Result<(), Error> {
I2CDevice::release(self, process)
}
}
+1 -1
View File
@@ -108,6 +108,6 @@ impl CharFile {
impl Drop for CharFile {
fn drop(&mut self) {
// TODO doesn't work with fork
self.device.release(self.pid).ok();
self.device.close(self.pid).ok();
}
}
+1 -1
View File
@@ -256,7 +256,7 @@ impl File {
) -> Result<Arc<Self>, Error> {
let pid = Thread::current().process_id();
device.lock(pid)?;
device.open(pid)?;
let read = opts.contains(OpenOptions::READ);
let write = opts.contains(OpenOptions::WRITE);
+56 -1
View File
@@ -1,5 +1,6 @@
use core::{
mem::MaybeUninit,
ops::Deref,
sync::atomic::{AtomicBool, Ordering},
task::{Context, Poll},
};
@@ -19,7 +20,7 @@ use yggdrasil_abi::{
device::{self, TerminalRequestVariant},
},
option::RequestValue,
process::{ProcessGroupId, Signal},
process::{ProcessGroupId, ProcessId, Signal},
};
use crate::{device::char::CharDevice, task::process::Process};
@@ -82,6 +83,15 @@ pub trait TerminalOutput: Sync + Send {
Err(Error::NotImplemented)
}
}
fn open(&self, pid: ProcessId) -> Result<(), Error> {
let _ = pid;
Ok(())
}
fn close(&self, pid: ProcessId) -> Result<(), Error> {
let _ = pid;
Ok(())
}
}
struct InputBuffer {
@@ -424,6 +434,13 @@ impl<O: TerminalOutput> CharDevice for Terminal<O> {
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
self.handle_device_request(option, buffer, len)
}
fn open(&self, pid: ProcessId) -> Result<(), Error> {
self.output.open(pid)
}
fn close(&self, pid: ProcessId) -> Result<(), Error> {
self.output.close(pid)
}
}
impl<O: TerminalOutput> FileReadiness for Terminal<O> {
@@ -438,6 +455,44 @@ impl<O: TerminalOutput> Device for Terminal<O> {
}
}
impl<T: TerminalOutput> TerminalOutput for Arc<T> {
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
self.deref().write(byte, options)
}
fn write_multiple(
&self,
bytes: &[u8],
options: &TerminalOutputOptions,
) -> Result<usize, Error> {
self.deref().write_multiple(bytes, options)
}
fn notify_readers(&self) {
self.deref().notify_readers();
}
fn size(&self) -> TerminalSize {
self.deref().size()
}
fn set_size(&self, size: TerminalSize) -> Result<(), Error> {
self.deref().set_size(size)
}
fn baud_rate(&self) -> u32 {
self.deref().baud_rate()
}
fn set_baud_rate(&self, baud: u32) -> Result<(), Error> {
self.deref().set_baud_rate(baud)
}
fn open(&self, pid: ProcessId) -> Result<(), Error> {
self.deref().open(pid)
}
fn close(&self, pid: ProcessId) -> Result<(), Error> {
self.deref().close(pid)
}
}
pub fn read_all(source: &mut RingBuffer<u8>, target: &mut [u8], eof: Option<u8>) -> usize {
let mut pos = 0;
while pos < target.len()