I am working on a USB Host implementation, working with the STM32G0B1/C1 family.
Currently I have a basic working example doing enumeration of a HID keyboard and I can receive keystrokes.
I am looking into how to fit this best in embassy (assuming embassy would like to add usbhost functionality), and there are quite some decisions to make about what should go where and about the API surface for each layer.
This is what I have now
- Simple HIDKeyboard implementation. Could serve as an example project
- UsbHost (knows about control requests, enumeration, parsing descriptors).
- USBHostDriverTrait that is used by USBHostDriver and imlpemented by HAL
- USBHostDriver HAL implementation for stm32 with
usb_v4 IP
STM32 USBHostDriver
Via cfg_attr the HAL choses between 'usb' and 'otg' IP. My current usbhost hal implementation is for usb_v4. As far as I know only _v4 supports host. How to fit in this functionality in with the current usb module structure? I guess we don't want to expose this for non-v4 IP.
My current host implementation is independent of the usb driver implementation. For usb_v4 you could have both, but you cannot use them at the same time since it is the same peripheral. One way is to specify compile time which one you want.
Which is what I did hardcoded during development:
//! Universal Serial Bus (USB)
// #[cfg_attr(usb, path = "usb.rs")]
// #[cfg_attr(otg, path = "otg.rs")]
// mod _version;
// pub use _version::*;
#[cfg_attr(usb, path = "usb_host.rs")]
mod _host_version;
pub use _host_version::*;
Simplest way is to add a usb-host config option to select usb_host.rs instead.
In theory one may wish to switch between host and device runtime, that wouldn't be supported like this.
Another advantage is that this approach does not risk breaking any existing usb functionality. Can always combine it later.
Alternatively if the "usb" implementation supports both device and host then you have to deal with not allowing that for _v1 - _v3. And you end up with a lot of if host { some host logic } else { device logic }
Any ideas?
USBHostDriverTrait
embassy-usb-driver contains the traits for usb device implementations. Currently I have added a host module to this crate for the host traits.
I called it USBHostDriverTrait to differentiate with the USBHostDriver but better naming is desirable.
So far I ended up with this API:
pub trait USBHostDriverTrait {
type ChannelIn: ChannelIn;
type ChannelOut: ChannelOut;
async fn bus_reset(&mut self);
async fn wait_for_device_connect(&mut self);
async fn wait_for_device_disconnect(&mut self);
async fn control_request_out(&mut self, addr: u8, bytes: &[u8]);
async fn control_request_in(&mut self, addr: u8, bytes: &[u8], dest: &mut [u8]) -> Result<usize, ()>;
fn reconfigure_channel0(&mut self, max_packet_size: u16, dev_addr: u8) -> Result<(), ()>;
fn alloc_channel_in(&mut self, desc: &EndpointDescriptor) -> Result<Self::ChannelIn, ()>;
fn alloc_channel_out(&mut self, desc: &EndpointDescriptor) -> Result<Self::ChannelOut, ()>;
}
pub trait ChannelIn {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>;
}
pub trait ChannelOut {
async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>;
}
USBHost
The embassy-usb crate implements various Device Classes. Would USBHost fit in here and how? Or better in a new separate crate such as embassy-usb-host? There are of course some shared data types such as descriptors, but not much shared logic I believe.
Also wondering what would be the preferred way to go about the PR process? Everything in one go is a lot to review. but you kinda need all layers for context I think
Happy to discuss!
I am working on a USB Host implementation, working with the STM32G0B1/C1 family.
Currently I have a basic working example doing enumeration of a HID keyboard and I can receive keystrokes.
I am looking into how to fit this best in embassy (assuming embassy would like to add usbhost functionality), and there are quite some decisions to make about what should go where and about the API surface for each layer.
This is what I have now
usb_v4IPSTM32 USBHostDriver
Via
cfg_attrthe HAL choses between'usb'and'otg'IP. My current usbhost hal implementation is forusb_v4. As far as I know only_v4supports host. How to fit in this functionality in with the current usb module structure? I guess we don't want to expose this for non-v4 IP.My current host implementation is independent of the usb driver implementation. For
usb_v4you could have both, but you cannot use them at the same time since it is the same peripheral. One way is to specify compile time which one you want.Which is what I did hardcoded during development:
Simplest way is to add a
usb-hostconfig option to selectusb_host.rsinstead.In theory one may wish to switch between host and device runtime, that wouldn't be supported like this.
Another advantage is that this approach does not risk breaking any existing usb functionality. Can always combine it later.
Alternatively if the
"usb"implementation supports both device and host then you have to deal with not allowing that for_v1-_v3. And you end up with a lot ofif host { some host logic } else { device logic }Any ideas?
USBHostDriverTrait
embassy-usb-drivercontains the traits for usb device implementations. Currently I have added ahostmodule to this crate for the host traits.I called it
USBHostDriverTraitto differentiate with theUSBHostDriverbut better naming is desirable.So far I ended up with this API:
USBHost
The
embassy-usbcrate implements various Device Classes. WouldUSBHostfit in here and how? Or better in a new separate crate such asembassy-usb-host? There are of course some shared data types such as descriptors, but not much shared logic I believe.Also wondering what would be the preferred way to go about the PR process? Everything in one go is a lot to review. but you kinda need all layers for context I think
Happy to discuss!