wip: experimental async usb stack

This commit is contained in:
Dario Nieuwenhuis 2022-03-09 01:34:35 +01:00
parent c1b3822964
commit 37598a5b37
15 changed files with 2624 additions and 186 deletions

View File

@ -64,6 +64,7 @@ _gpio-p1 = []
embassy = { version = "0.1.0", path = "../embassy" }
embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["nrf"]}
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
embassy-usb = {version = "0.1.0", path = "../embassy-usb" }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.7", git = "https://github.com/embassy-rs/embedded-hal", branch = "embassy2", optional = true}
@ -80,8 +81,6 @@ rand_core = "0.6.3"
fixed = "1.10.0"
embedded-storage = "0.3.0"
cfg-if = "1.0.0"
nrf-usbd = {version = "0.1.1"}
usb-device = "0.2.8"
nrf52805-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52810-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }

View File

@ -1,43 +1,423 @@
#![macro_use]
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy::interrupt::InterruptExt;
use embassy::time::{with_timeout, Duration};
use embassy::util::Unborrow;
use embassy::waitqueue::AtomicWaker;
use embassy_hal_common::unborrow;
use embassy_usb::driver::{self, ReadError, WriteError};
use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection};
use futures::future::poll_fn;
use futures::Future;
use pac::NVIC;
pub use embassy_usb;
use crate::interrupt::Interrupt;
use crate::pac;
use core::marker::PhantomData;
use embassy::util::Unborrow;
use nrf_usbd::{UsbPeripheral, Usbd};
use usb_device::bus::UsbBusAllocator;
static EP0_WAKER: AtomicWaker = AtomicWaker::new();
pub use embassy_hal_common::usb::*;
pub struct UsbBus<'d, T: Instance> {
pub struct Driver<'d, T: Instance> {
phantom: PhantomData<&'d mut T>,
alloc_in: Allocator,
alloc_out: Allocator,
}
unsafe impl<'d, T: Instance> UsbPeripheral for UsbBus<'d, T> {
// todo how to use T::regs
const REGISTERS: *const () = pac::USBD::ptr() as *const ();
}
impl<'d, T: Instance> Driver<'d, T> {
pub fn new(
_usb: impl Unborrow<Target = T> + 'd,
irq: impl Unborrow<Target = T::Interrupt> + 'd,
) -> Self {
unborrow!(irq);
irq.set_handler(Self::on_interrupt);
irq.unpend();
irq.enable();
impl<'d, T: Instance> UsbBus<'d, T> {
pub fn new(_usb: impl Unborrow<Target = T> + 'd) -> UsbBusAllocator<Usbd<UsbBus<'d, T>>> {
let r = T::regs();
r.intenset.write(|w| {
w.sof().set_bit();
w.usbevent().set_bit();
w.ep0datadone().set_bit();
w.ep0setup().set_bit();
w.usbreset().set_bit()
});
Usbd::new(UsbBus {
Self {
phantom: PhantomData,
})
alloc_in: Allocator::new(),
alloc_out: Allocator::new(),
}
}
fn on_interrupt(_: *mut ()) {
let regs = T::regs();
if regs.events_ep0setup.read().bits() != 0 {
regs.intenclr.write(|w| w.ep0setup().clear());
EP0_WAKER.wake();
}
if regs.events_ep0datadone.read().bits() != 0 {
regs.intenclr.write(|w| w.ep0datadone().clear());
EP0_WAKER.wake();
}
}
fn set_stalled(ep_addr: EndpointAddress, stalled: bool) {
let regs = T::regs();
unsafe {
if ep_addr.index() == 0 {
regs.tasks_ep0stall
.write(|w| w.tasks_ep0stall().bit(stalled));
} else {
regs.epstall.write(|w| {
w.ep().bits(ep_addr.index() as u8 & 0b111);
w.io().bit(ep_addr.is_in());
w.stall().bit(stalled)
});
}
}
//if stalled {
// self.busy_in_endpoints &= !(1 << ep_addr.index());
//}
}
fn is_stalled(ep_addr: EndpointAddress) -> bool {
let regs = T::regs();
let i = ep_addr.index();
match ep_addr.direction() {
UsbDirection::Out => regs.halted.epout[i].read().getstatus().is_halted(),
UsbDirection::In => regs.halted.epin[i].read().getstatus().is_halted(),
}
}
}
unsafe impl embassy_hal_common::usb::USBInterrupt for crate::interrupt::USBD {}
impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
type EndpointOut = Endpoint<'d, T, Out>;
type EndpointIn = Endpoint<'d, T, In>;
type Bus = Bus<'d, T>;
fn alloc_endpoint_in(
&mut self,
ep_addr: Option<EndpointAddress>,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
let index = self
.alloc_in
.allocate(ep_addr, ep_type, max_packet_size, interval)?;
let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In);
Ok(Endpoint {
_phantom: PhantomData,
info: EndpointInfo {
addr: ep_addr,
ep_type,
max_packet_size,
interval,
},
})
}
fn alloc_endpoint_out(
&mut self,
ep_addr: Option<EndpointAddress>,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
let index = self
.alloc_out
.allocate(ep_addr, ep_type, max_packet_size, interval)?;
let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out);
Ok(Endpoint {
_phantom: PhantomData,
info: EndpointInfo {
addr: ep_addr,
ep_type,
max_packet_size,
interval,
},
})
}
fn enable(self) -> Self::Bus {
let regs = T::regs();
errata::pre_enable();
regs.enable.write(|w| w.enable().enabled());
// Wait until the peripheral is ready.
while !regs.eventcause.read().ready().is_ready() {}
regs.eventcause.write(|w| w.ready().set_bit()); // Write 1 to clear.
errata::post_enable();
unsafe { NVIC::unmask(pac::Interrupt::USBD) };
// Enable the USB pullup, allowing enumeration.
regs.usbpullup.write(|w| w.connect().enabled());
info!("enabled");
Bus {
phantom: PhantomData,
alloc_in: self.alloc_in,
alloc_out: self.alloc_out,
}
}
}
pub struct Bus<'d, T: Instance> {
phantom: PhantomData<&'d mut T>,
alloc_in: Allocator,
alloc_out: Allocator,
}
impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
#[inline]
fn reset(&mut self) {
let regs = T::regs();
// TODO: Initialize ISO buffers
// XXX this is not spec compliant; the endpoints should only be enabled after the device
// has been put in the Configured state. However, usb-device provides no hook to do that
unsafe {
regs.epinen.write(|w| w.bits(self.alloc_in.used.into()));
regs.epouten.write(|w| w.bits(self.alloc_out.used.into()));
}
for i in 1..8 {
let out_enabled = self.alloc_out.used & (1 << i) != 0;
// when first enabled, bulk/interrupt OUT endpoints will *not* receive data (the
// peripheral will NAK all incoming packets) until we write a zero to the SIZE
// register (see figure 203 of the 52840 manual). To avoid that we write a 0 to the
// SIZE register
if out_enabled {
regs.size.epout[i].reset();
}
}
//self.busy_in_endpoints = 0;
}
#[inline]
fn set_device_address(&mut self, _addr: u8) {
// Nothing to do, the peripheral handles this.
}
fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
Driver::<T>::set_stalled(ep_addr, stalled)
}
fn is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
Driver::<T>::is_stalled(ep_addr)
}
#[inline]
fn suspend(&mut self) {
let regs = T::regs();
regs.lowpower.write(|w| w.lowpower().low_power());
}
#[inline]
fn resume(&mut self) {
let regs = T::regs();
errata::pre_wakeup();
regs.lowpower.write(|w| w.lowpower().force_normal());
}
}
pub enum Out {}
pub enum In {}
pub struct Endpoint<'d, T: Instance, Dir> {
_phantom: PhantomData<(&'d mut T, Dir)>,
info: EndpointInfo,
}
impl<'d, T: Instance, Dir> driver::Endpoint for Endpoint<'d, T, Dir> {
fn info(&self) -> &EndpointInfo {
&self.info
}
fn set_stalled(&self, stalled: bool) {
Driver::<T>::set_stalled(self.info.addr, stalled)
}
fn is_stalled(&self) -> bool {
Driver::<T>::is_stalled(self.info.addr)
}
}
impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
type ReadFuture<'a>
where
Self: 'a,
= impl Future<Output = Result<usize, ReadError>> + 'a;
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
async move {
let regs = T::regs();
if buf.len() == 0 {
regs.tasks_ep0status.write(|w| unsafe { w.bits(1) });
return Ok(0);
}
// Wait for SETUP packet
regs.events_ep0setup.reset();
regs.intenset.write(|w| w.ep0setup().set());
poll_fn(|cx| {
EP0_WAKER.register(cx.waker());
if regs.events_ep0setup.read().bits() != 0 {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
info!("got SETUP");
if buf.len() < 8 {
return Err(ReadError::BufferOverflow);
}
buf[0] = regs.bmrequesttype.read().bits() as u8;
buf[1] = regs.brequest.read().brequest().bits();
buf[2] = regs.wvaluel.read().wvaluel().bits();
buf[3] = regs.wvalueh.read().wvalueh().bits();
buf[4] = regs.windexl.read().windexl().bits();
buf[5] = regs.windexh.read().windexh().bits();
buf[6] = regs.wlengthl.read().wlengthl().bits();
buf[7] = regs.wlengthh.read().wlengthh().bits();
Ok(8)
}
}
}
impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
type WriteFuture<'a>
where
Self: 'a,
= impl Future<Output = Result<(), WriteError>> + 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
async move {
info!("write: {:x}", buf);
let regs = T::regs();
let ptr = buf.as_ptr() as u32;
let len = buf.len() as u32;
regs.epin0.ptr.write(|w| unsafe { w.bits(ptr) });
regs.epin0.maxcnt.write(|w| unsafe { w.bits(len) });
regs.events_ep0datadone.reset();
regs.events_endepin[0].reset();
dma_start();
regs.tasks_startepin[0].write(|w| unsafe { w.bits(1) });
info!("write: waiting for endepin...");
while regs.events_endepin[0].read().bits() == 0 {}
dma_end();
info!("write: waiting for ep0datadone...");
regs.intenset.write(|w| w.ep0datadone().set());
let res = with_timeout(
Duration::from_millis(10),
poll_fn(|cx| {
EP0_WAKER.register(cx.waker());
if regs.events_ep0datadone.read().bits() != 0 {
Poll::Ready(())
} else {
Poll::Pending
}
}),
)
.await;
if res.is_err() {
// todo wrong error
return Err(driver::WriteError::BufferOverflow);
}
info!("write done");
Ok(())
}
}
}
fn dma_start() {
compiler_fence(Ordering::Release);
}
fn dma_end() {
compiler_fence(Ordering::Acquire);
}
struct Allocator {
used: u16,
// Buffers can be up to 64 Bytes since this is a Full-Speed implementation.
lens: [u8; 9],
}
impl Allocator {
fn new() -> Self {
Self {
used: 0,
lens: [0; 9],
}
}
fn allocate(
&mut self,
ep_addr: Option<EndpointAddress>,
ep_type: EndpointType,
max_packet_size: u16,
_interval: u8,
) -> Result<usize, driver::EndpointAllocError> {
// Endpoint addresses are fixed in hardware:
// - 0x80 / 0x00 - Control EP0
// - 0x81 / 0x01 - Bulk/Interrupt EP1
// - 0x82 / 0x02 - Bulk/Interrupt EP2
// - 0x83 / 0x03 - Bulk/Interrupt EP3
// - 0x84 / 0x04 - Bulk/Interrupt EP4
// - 0x85 / 0x05 - Bulk/Interrupt EP5
// - 0x86 / 0x06 - Bulk/Interrupt EP6
// - 0x87 / 0x07 - Bulk/Interrupt EP7
// - 0x88 / 0x08 - Isochronous
// Endpoint directions are allocated individually.
let alloc_index = match ep_type {
EndpointType::Isochronous => 8,
EndpointType::Control => 0,
EndpointType::Interrupt | EndpointType::Bulk => {
// Find rightmost zero bit in 1..=7
let ones = (self.used >> 1).trailing_ones() as usize;
if ones >= 7 {
return Err(driver::EndpointAllocError);
}
ones + 1
}
};
if self.used & (1 << alloc_index) != 0 {
return Err(driver::EndpointAllocError);
}
self.used |= 1 << alloc_index;
self.lens[alloc_index] = max_packet_size as u8;
Ok(alloc_index)
}
}
pub(crate) mod sealed {
use super::*;
@ -63,3 +443,64 @@ macro_rules! impl_usb {
}
};
}
mod errata {
/// Writes `val` to `addr`. Used to apply Errata workarounds.
unsafe fn poke(addr: u32, val: u32) {
(addr as *mut u32).write_volatile(val);
}
/// Reads 32 bits from `addr`.
unsafe fn peek(addr: u32) -> u32 {
(addr as *mut u32).read_volatile()
}
pub fn pre_enable() {
// Works around Erratum 187 on chip revisions 1 and 2.
unsafe {
poke(0x4006EC00, 0x00009375);
poke(0x4006ED14, 0x00000003);
poke(0x4006EC00, 0x00009375);
}
pre_wakeup();
}
pub fn post_enable() {
post_wakeup();
// Works around Erratum 187 on chip revisions 1 and 2.
unsafe {
poke(0x4006EC00, 0x00009375);
poke(0x4006ED14, 0x00000000);
poke(0x4006EC00, 0x00009375);
}
}
pub fn pre_wakeup() {
// Works around Erratum 171 on chip revisions 1 and 2.
unsafe {
if peek(0x4006EC00) == 0x00000000 {
poke(0x4006EC00, 0x00009375);
}
poke(0x4006EC14, 0x000000C0);
poke(0x4006EC00, 0x00009375);
}
}
pub fn post_wakeup() {
// Works around Erratum 171 on chip revisions 1 and 2.
unsafe {
if peek(0x4006EC00) == 0x00000000 {
poke(0x4006EC00, 0x00009375);
}
poke(0x4006EC14, 0x00000000);
poke(0x4006EC00, 0x00009375);
}
}
}

12
embassy-usb/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "embassy-usb"
version = "0.1.0"
edition = "2018"
[dependencies]
embassy = { version = "0.1.0", path = "../embassy" }
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
cortex-m = "0.7.3"
num-traits = { version = "0.2.14", default-features = false }

347
embassy-usb/src/builder.rs Normal file
View File

@ -0,0 +1,347 @@
use super::descriptor::{BosWriter, DescriptorWriter};
use super::driver::{Driver, EndpointAllocError};
use super::types::*;
use super::UsbDevice;
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config<'a> {
pub(crate) vendor_id: u16,
pub(crate) product_id: u16,
/// Device class code assigned by USB.org. Set to `0xff` for vendor-specific
/// devices that do not conform to any class.
///
/// Default: `0x00` (class code specified by interfaces)
pub device_class: u8,
/// Device sub-class code. Depends on class.
///
/// Default: `0x00`
pub device_sub_class: u8,
/// Device protocol code. Depends on class and sub-class.
///
/// Default: `0x00`
pub device_protocol: u8,
/// Device release version in BCD.
///
/// Default: `0x0010` ("0.1")
pub device_release: u16,
/// Maximum packet size in bytes for the control endpoint 0.
///
/// Valid values are 8, 16, 32 and 64. There's generally no need to change this from the default
/// value of 8 bytes unless a class uses control transfers for sending large amounts of data, in
/// which case using a larger packet size may be more efficient.
///
/// Default: 8 bytes
pub max_packet_size_0: u8,
/// Manufacturer name string descriptor.
///
/// Default: (none)
pub manufacturer: Option<&'a str>,
/// Product name string descriptor.
///
/// Default: (none)
pub product: Option<&'a str>,
/// Serial number string descriptor.
///
/// Default: (none)
pub serial_number: Option<&'a str>,
/// Whether the device supports remotely waking up the host is requested.
///
/// Default: `false`
pub supports_remote_wakeup: bool,
/// Configures the device as a composite device with interface association descriptors.
///
/// If set to `true`, the following fields should have the given values:
///
/// - `device_class` = `0xEF`
/// - `device_sub_class` = `0x02`
/// - `device_protocol` = `0x01`
pub composite_with_iads: bool,
/// Whether the device has its own power source.
///
/// This should be set to `true` even if the device is sometimes self-powered and may not
/// always draw power from the USB bus.
///
/// Default: `false`
///
/// See also: `max_power`
pub self_powered: bool,
/// Maximum current drawn from the USB bus by the device, in milliamps.
///
/// The default is 100 mA. If your device always uses an external power source and never draws
/// power from the USB bus, this can be set to 0.
///
/// See also: `self_powered`
///
/// Default: 100mA
/// Max: 500mA
pub max_power: u16,
}
impl<'a> Config<'a> {
pub fn new(vid: u16, pid: u16) -> Self {
Self {
device_class: 0x00,
device_sub_class: 0x00,
device_protocol: 0x00,
max_packet_size_0: 8,
vendor_id: vid,
product_id: pid,
device_release: 0x0010,
manufacturer: None,
product: None,
serial_number: None,
self_powered: false,
supports_remote_wakeup: false,
composite_with_iads: false,
max_power: 100,
}
}
}
/// Used to build new [`UsbDevice`]s.
pub struct UsbDeviceBuilder<'d, D: Driver<'d>> {
config: Config<'d>,
bus: D,
next_interface_number: u8,
next_string_index: u8,
// TODO make not pub?
pub device_descriptor: DescriptorWriter<'d>,
pub config_descriptor: DescriptorWriter<'d>,
pub bos_descriptor: BosWriter<'d>,
}
impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> {
/// Creates a builder for constructing a new [`UsbDevice`].
pub fn new(
bus: D,
config: Config<'d>,
device_descriptor_buf: &'d mut [u8],
config_descriptor_buf: &'d mut [u8],
bos_descriptor_buf: &'d mut [u8],
) -> Self {
// Magic values specified in USB-IF ECN on IADs.
if config.composite_with_iads
&& (config.device_class != 0xEF
|| config.device_sub_class != 0x02
|| config.device_protocol != 0x01)
{
panic!("if composite_with_iads is set, you must set device_class = 0xEF, device_sub_class = 0x02, device_protocol = 0x01");
}
if config.max_power > 500 {
panic!("The maximum allowed value for `max_power` is 500mA");
}
match config.max_packet_size_0 {
8 | 16 | 32 | 64 => {}
_ => panic!("invalid max_packet_size_0, the allowed values are 8, 16, 32 or 64"),
}
let mut device_descriptor = DescriptorWriter::new(device_descriptor_buf);
let mut config_descriptor = DescriptorWriter::new(config_descriptor_buf);
let mut bos_descriptor = BosWriter::new(DescriptorWriter::new(bos_descriptor_buf));
device_descriptor.device(&config).unwrap();
config_descriptor.configuration(&config).unwrap();
bos_descriptor.bos().unwrap();
UsbDeviceBuilder {
bus,
config,
next_interface_number: 0,
next_string_index: 4,
device_descriptor,
config_descriptor,
bos_descriptor,
}
}
/// Creates the [`UsbDevice`] instance with the configuration in this builder.
pub fn build(mut self) -> UsbDevice<'d, D> {
self.config_descriptor.end_configuration();
self.bos_descriptor.end_bos();
UsbDevice::build(
self.bus,
self.config,
self.device_descriptor.into_buf(),
self.config_descriptor.into_buf(),
self.bos_descriptor.writer.into_buf(),
)
}
/// Allocates a new interface number.
pub fn alloc_interface(&mut self) -> InterfaceNumber {
let number = self.next_interface_number;
self.next_interface_number += 1;
InterfaceNumber::new(number)
}
/// Allocates a new string index.
pub fn alloc_string(&mut self) -> StringIndex {
let index = self.next_string_index;
self.next_string_index += 1;
StringIndex::new(index)
}
/// Allocates an in endpoint.
///
/// This directly delegates to [`Driver::alloc_endpoint_in`], so see that method for details. In most
/// cases classes should call the endpoint type specific methods instead.
pub fn alloc_endpoint_in(
&mut self,
ep_addr: Option<EndpointAddress>,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
) -> Result<D::EndpointIn, EndpointAllocError> {
self.bus
.alloc_endpoint_in(ep_addr, ep_type, max_packet_size, interval)
}
/// Allocates an out endpoint.
///
/// This directly delegates to [`Driver::alloc_endpoint_out`], so see that method for details. In most
/// cases classes should call the endpoint type specific methods instead.
pub fn alloc_endpoint_out(
&mut self,
ep_addr: Option<EndpointAddress>,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
) -> Result<D::EndpointOut, EndpointAllocError> {
self.bus
.alloc_endpoint_out(ep_addr, ep_type, max_packet_size, interval)
}
/// Allocates a control in endpoint.
///
/// This crate implements the control state machine only for endpoint 0. If classes want to
/// support control requests in other endpoints, the state machine must be implemented manually.
/// This should rarely be needed by classes.
///
/// # Arguments
///
/// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64.
///
/// # Panics
///
/// Panics if endpoint allocation fails, because running out of endpoints or memory is not
/// feasibly recoverable.
#[inline]
pub fn alloc_control_endpoint_in(&mut self, max_packet_size: u16) -> D::EndpointIn {
self.alloc_endpoint_in(None, EndpointType::Control, max_packet_size, 0)
.expect("alloc_ep failed")
}
/// Allocates a control out endpoint.
///
/// This crate implements the control state machine only for endpoint 0. If classes want to
/// support control requests in other endpoints, the state machine must be implemented manually.
/// This should rarely be needed by classes.
///
/// # Arguments
///
/// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64.
///
/// # Panics
///
/// Panics if endpoint allocation fails, because running out of endpoints or memory is not
/// feasibly recoverable.
#[inline]
pub fn alloc_control_endpoint_out(&mut self, max_packet_size: u16) -> D::EndpointOut {
self.alloc_endpoint_out(None, EndpointType::Control, max_packet_size, 0)
.expect("alloc_ep failed")
}
/// Allocates a bulk in endpoint.
///
/// # Arguments
///
/// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64.
///
/// # Panics
///
/// Panics if endpoint allocation fails, because running out of endpoints or memory is not
/// feasibly recoverable.
#[inline]
pub fn alloc_bulk_endpoint_in(&mut self, max_packet_size: u16) -> D::EndpointIn {
self.alloc_endpoint_in(None, EndpointType::Bulk, max_packet_size, 0)
.expect("alloc_ep failed")
}
/// Allocates a bulk out endpoint.
///
/// # Arguments
///
/// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64.
///
/// # Panics
///
/// Panics if endpoint allocation fails, because running out of endpoints or memory is not
/// feasibly recoverable.
#[inline]
pub fn alloc_bulk_endpoint_out(&mut self, max_packet_size: u16) -> D::EndpointOut {
self.alloc_endpoint_out(None, EndpointType::Bulk, max_packet_size, 0)
.expect("alloc_ep failed")
}
/// Allocates a bulk in endpoint.
///
/// # Arguments
///
/// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes.
///
/// # Panics
///
/// Panics if endpoint allocation fails, because running out of endpoints or memory is not
/// feasibly recoverable.
#[inline]
pub fn alloc_interrupt_endpoint_in(
&mut self,
max_packet_size: u16,
interval: u8,
) -> D::EndpointIn {
self.alloc_endpoint_in(None, EndpointType::Interrupt, max_packet_size, interval)
.expect("alloc_ep failed")
}
/// Allocates a bulk in endpoint.
///
/// # Arguments
///
/// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes.
///
/// # Panics
///
/// Panics if endpoint allocation fails, because running out of endpoints or memory is not
/// feasibly recoverable.
#[inline]
pub fn alloc_interrupt_endpoint_out(
&mut self,
max_packet_size: u16,
interval: u8,
) -> D::EndpointOut {
self.alloc_endpoint_out(None, EndpointType::Interrupt, max_packet_size, interval)
.expect("alloc_ep failed")
}
}

134
embassy-usb/src/control.rs Normal file
View File

@ -0,0 +1,134 @@
use core::mem;
use super::types::*;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ParseError {
InvalidLength,
}
/// Control request type.
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RequestType {
/// Request is a USB standard request. Usually handled by
/// [`UsbDevice`](crate::device::UsbDevice).
Standard = 0,
/// Request is intended for a USB class.
Class = 1,
/// Request is vendor-specific.
Vendor = 2,
/// Reserved.
Reserved = 3,
}
/// Control request recipient.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Recipient {
/// Request is intended for the entire device.
Device = 0,
/// Request is intended for an interface. Generally, the `index` field of the request specifies
/// the interface number.
Interface = 1,
/// Request is intended for an endpoint. Generally, the `index` field of the request specifies
/// the endpoint address.
Endpoint = 2,
/// None of the above.
Other = 3,
/// Reserved.
Reserved = 4,
}
/// A control request read from a SETUP packet.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Request {
/// Direction of the request.
pub direction: UsbDirection,
/// Type of the request.
pub request_type: RequestType,
/// Recipient of the request.
pub recipient: Recipient,
/// Request code. The meaning of the value depends on the previous fields.
pub request: u8,
/// Request value. The meaning of the value depends on the previous fields.
pub value: u16,
/// Request index. The meaning of the value depends on the previous fields.
pub index: u16,
/// Length of the DATA stage. For control OUT transfers this is the exact length of the data the
/// host sent. For control IN transfers this is the maximum length of data the device should
/// return.
pub length: u16,
}
impl Request {
/// Standard USB control request Get Status
pub const GET_STATUS: u8 = 0;
/// Standard USB control request Clear Feature
pub const CLEAR_FEATURE: u8 = 1;
/// Standard USB control request Set Feature
pub const SET_FEATURE: u8 = 3;
/// Standard USB control request Set Address
pub const SET_ADDRESS: u8 = 5;
/// Standard USB control request Get Descriptor
pub const GET_DESCRIPTOR: u8 = 6;
/// Standard USB control request Set Descriptor
pub const SET_DESCRIPTOR: u8 = 7;
/// Standard USB control request Get Configuration
pub const GET_CONFIGURATION: u8 = 8;
/// Standard USB control request Set Configuration
pub const SET_CONFIGURATION: u8 = 9;
/// Standard USB control request Get Interface
pub const GET_INTERFACE: u8 = 10;
/// Standard USB control request Set Interface
pub const SET_INTERFACE: u8 = 11;
/// Standard USB control request Synch Frame
pub const SYNCH_FRAME: u8 = 12;
/// Standard USB feature Endpoint Halt for Set/Clear Feature
pub const FEATURE_ENDPOINT_HALT: u16 = 0;
/// Standard USB feature Device Remote Wakeup for Set/Clear Feature
pub const FEATURE_DEVICE_REMOTE_WAKEUP: u16 = 1;
pub(crate) fn parse(buf: &[u8]) -> Result<Request, ParseError> {
if buf.len() != 8 {
return Err(ParseError::InvalidLength);
}
let rt = buf[0];
let recipient = rt & 0b11111;
Ok(Request {
direction: rt.into(),
request_type: unsafe { mem::transmute((rt >> 5) & 0b11) },
recipient: if recipient <= 3 {
unsafe { mem::transmute(recipient) }
} else {
Recipient::Reserved
},
request: buf[1],
value: (buf[2] as u16) | ((buf[3] as u16) << 8),
index: (buf[4] as u16) | ((buf[5] as u16) << 8),
length: (buf[6] as u16) | ((buf[7] as u16) << 8),
})
}
/// Gets the descriptor type and index from the value field of a GET_DESCRIPTOR request.
pub fn descriptor_type_index(&self) -> (u8, u8) {
((self.value >> 8) as u8, self.value as u8)
}
}

View File

@ -0,0 +1,387 @@
use super::builder::Config;
use super::{types::*, CONFIGURATION_VALUE, DEFAULT_ALTERNATE_SETTING};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
BufferFull,
InvalidState,
}
/// Standard descriptor types
#[allow(missing_docs)]
pub mod descriptor_type {
pub const DEVICE: u8 = 1;
pub const CONFIGURATION: u8 = 2;
pub const STRING: u8 = 3;
pub const INTERFACE: u8 = 4;
pub const ENDPOINT: u8 = 5;
pub const IAD: u8 = 11;
pub const BOS: u8 = 15;
pub const CAPABILITY: u8 = 16;
}
/// String descriptor language IDs.
pub mod lang_id {
/// English (US)
///
/// Recommended for use as the first language ID for compatibility.
pub const ENGLISH_US: u16 = 0x0409;
}
/// Standard capability descriptor types
#[allow(missing_docs)]
pub mod capability_type {
pub const WIRELESS_USB: u8 = 1;
pub const USB_2_0_EXTENSION: u8 = 2;
pub const SS_USB_DEVICE: u8 = 3;
pub const CONTAINER_ID: u8 = 4;
pub const PLATFORM: u8 = 5;
}
/// A writer for USB descriptors.
pub struct DescriptorWriter<'a> {
buf: &'a mut [u8],
position: usize,
num_interfaces_mark: Option<usize>,
num_endpoints_mark: Option<usize>,
write_iads: bool,
}
impl<'a> DescriptorWriter<'a> {
pub(crate) fn new(buf: &'a mut [u8]) -> Self {
DescriptorWriter {
buf,
position: 0,
num_interfaces_mark: None,
num_endpoints_mark: None,
write_iads: false,
}
}
pub fn into_buf(self) -> &'a mut [u8] {
&mut self.buf[..self.position]
}
/// Gets the current position in the buffer, i.e. the number of bytes written so far.
pub fn position(&self) -> usize {
self.position
}
/// Writes an arbitrary (usually class-specific) descriptor.
pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8]) -> Result<(), Error> {
let length = descriptor.len();
if (self.position + 2 + length) > self.buf.len() || (length + 2) > 255 {
return Err(Error::BufferFull);
}
self.buf[self.position] = (length + 2) as u8;
self.buf[self.position + 1] = descriptor_type;
let start = self.position + 2;
self.buf[start..start + length].copy_from_slice(descriptor);
self.position = start + length;
Ok(())
}
pub(crate) fn device(&mut self, config: &Config) -> Result<(), Error> {
self.write(
descriptor_type::DEVICE,
&[
0x10,
0x02, // bcdUSB 2.1
config.device_class, // bDeviceClass
config.device_sub_class, // bDeviceSubClass
config.device_protocol, // bDeviceProtocol
config.max_packet_size_0, // bMaxPacketSize0
config.vendor_id as u8,
(config.vendor_id >> 8) as u8, // idVendor
config.product_id as u8,
(config.product_id >> 8) as u8, // idProduct
config.device_release as u8,
(config.device_release >> 8) as u8, // bcdDevice
config.manufacturer.map_or(0, |_| 1), // iManufacturer
config.product.map_or(0, |_| 2), // iProduct
config.serial_number.map_or(0, |_| 3), // iSerialNumber
1, // bNumConfigurations
],
)
}
pub(crate) fn configuration(&mut self, config: &Config) -> Result<(), Error> {
self.num_interfaces_mark = Some(self.position + 4);
self.write_iads = config.composite_with_iads;
self.write(
descriptor_type::CONFIGURATION,
&[
0,
0, // wTotalLength
0, // bNumInterfaces
CONFIGURATION_VALUE, // bConfigurationValue
0, // iConfiguration
0x80 | if config.self_powered { 0x40 } else { 0x00 }
| if config.supports_remote_wakeup {
0x20
} else {
0x00
}, // bmAttributes
(config.max_power / 2) as u8, // bMaxPower
],
)
}
pub(crate) fn end_class(&mut self) {
self.num_endpoints_mark = None;
}
pub(crate) fn end_configuration(&mut self) {
let position = self.position as u16;
self.buf[2..4].copy_from_slice(&position.to_le_bytes());
}
/// Writes a interface association descriptor. Call from `UsbClass::get_configuration_descriptors`
/// before writing the USB class or function's interface descriptors if your class has more than
/// one interface and wants to play nicely with composite devices on Windows. If the USB device
/// hosting the class was not configured as composite with IADs enabled, calling this function
/// does nothing, so it is safe to call from libraries.
///
/// # Arguments
///
/// * `first_interface` - Number of the function's first interface, previously allocated with
/// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface).
/// * `interface_count` - Number of interfaces in the function.
/// * `function_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices
/// that do not conform to any class.
/// * `function_sub_class` - Sub-class code. Depends on class.
/// * `function_protocol` - Protocol code. Depends on class and sub-class.
pub fn iad(
&mut self,
first_interface: InterfaceNumber,
interface_count: u8,
function_class: u8,
function_sub_class: u8,
function_protocol: u8,
) -> Result<(), Error> {
if !self.write_iads {
return Ok(());
}
self.write(
descriptor_type::IAD,
&[
first_interface.into(), // bFirstInterface
interface_count, // bInterfaceCount
function_class,
function_sub_class,
function_protocol,
0,
],
)?;
Ok(())
}
/// Writes a interface descriptor.
///
/// # Arguments
///
/// * `number` - Interface number previously allocated with
/// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface).
/// * `interface_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices
/// that do not conform to any class.
/// * `interface_sub_class` - Sub-class code. Depends on class.
/// * `interface_protocol` - Protocol code. Depends on class and sub-class.
pub fn interface(
&mut self,
number: InterfaceNumber,
interface_class: u8,
interface_sub_class: u8,
interface_protocol: u8,
) -> Result<(), Error> {
self.interface_alt(
number,
DEFAULT_ALTERNATE_SETTING,
interface_class,
interface_sub_class,
interface_protocol,
None,
)
}
/// Writes a interface descriptor with a specific alternate setting and
/// interface string identifier.
///
/// # Arguments
///
/// * `number` - Interface number previously allocated with
/// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface).
/// * `alternate_setting` - Number of the alternate setting
/// * `interface_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices
/// that do not conform to any class.
/// * `interface_sub_class` - Sub-class code. Depends on class.
/// * `interface_protocol` - Protocol code. Depends on class and sub-class.
/// * `interface_string` - Index of string descriptor describing this interface
pub fn interface_alt(
&mut self,
number: InterfaceNumber,
alternate_setting: u8,
interface_class: u8,
interface_sub_class: u8,
interface_protocol: u8,
interface_string: Option<StringIndex>,
) -> Result<(), Error> {
if alternate_setting == DEFAULT_ALTERNATE_SETTING {
match self.num_interfaces_mark {
Some(mark) => self.buf[mark] += 1,
None => return Err(Error::InvalidState),
};
}
let str_index = interface_string.map_or(0, Into::into);
self.num_endpoints_mark = Some(self.position + 4);
self.write(
descriptor_type::INTERFACE,
&[
number.into(), // bInterfaceNumber
alternate_setting, // bAlternateSetting
0, // bNumEndpoints
interface_class, // bInterfaceClass
interface_sub_class, // bInterfaceSubClass
interface_protocol, // bInterfaceProtocol
str_index, // iInterface
],
)?;
Ok(())
}
/// Writes an endpoint descriptor.
///
/// # Arguments
///
/// * `endpoint` - Endpoint previously allocated with
/// [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder).
pub fn endpoint(&mut self, endpoint: &EndpointInfo) -> Result<(), Error> {
match self.num_endpoints_mark {
Some(mark) => self.buf[mark] += 1,
None => return Err(Error::InvalidState),
};
self.write(
descriptor_type::ENDPOINT,
&[
endpoint.addr.into(), // bEndpointAddress
endpoint.ep_type as u8, // bmAttributes
endpoint.max_packet_size as u8,
(endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize
endpoint.interval, // bInterval
],
)?;
Ok(())
}
/// Writes a string descriptor.
pub(crate) fn string(&mut self, string: &str) -> Result<(), Error> {
let mut pos = self.position;
if pos + 2 > self.buf.len() {
return Err(Error::BufferFull);
}
self.buf[pos] = 0; // length placeholder
self.buf[pos + 1] = descriptor_type::STRING;
pos += 2;
for c in string.encode_utf16() {
if pos >= self.buf.len() {
return Err(Error::BufferFull);
}
self.buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());
pos += 2;
}
self.buf[self.position] = (pos - self.position) as u8;
self.position = pos;
Ok(())
}
}
/// A writer for Binary Object Store descriptor.
pub struct BosWriter<'a> {
pub(crate) writer: DescriptorWriter<'a>,
num_caps_mark: Option<usize>,
}
impl<'a> BosWriter<'a> {
pub(crate) fn new(writer: DescriptorWriter<'a>) -> Self {
Self {
writer: writer,
num_caps_mark: None,
}
}
pub(crate) fn bos(&mut self) -> Result<(), Error> {
self.num_caps_mark = Some(self.writer.position + 4);
self.writer.write(
descriptor_type::BOS,
&[
0x00, 0x00, // wTotalLength
0x00, // bNumDeviceCaps
],
)?;
self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4])?;
Ok(())
}
/// Writes capability descriptor to a BOS
///
/// # Arguments
///
/// * `capability_type` - Type of a capability
/// * `data` - Binary data of the descriptor
pub fn capability(&mut self, capability_type: u8, data: &[u8]) -> Result<(), Error> {
match self.num_caps_mark {
Some(mark) => self.writer.buf[mark] += 1,
None => return Err(Error::InvalidState),
}
let mut start = self.writer.position;
let blen = data.len();
if (start + blen + 3) > self.writer.buf.len() || (blen + 3) > 255 {
return Err(Error::BufferFull);
}
self.writer.buf[start] = (blen + 3) as u8;
self.writer.buf[start + 1] = descriptor_type::CAPABILITY;
self.writer.buf[start + 2] = capability_type;
start += 3;
self.writer.buf[start..start + blen].copy_from_slice(data);
self.writer.position = start + blen;
Ok(())
}
pub(crate) fn end_bos(&mut self) {
self.num_caps_mark = None;
let position = self.writer.position as u16;
self.writer.buf[2..4].copy_from_slice(&position.to_le_bytes());
}
}

160
embassy-usb/src/driver.rs Normal file
View File

@ -0,0 +1,160 @@
use core::future::Future;
use super::types::*;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EndpointAllocError;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Operation is unsupported by the driver.
pub struct Unsupported;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Errors returned by [`EndpointIn::write`]
pub enum WriteError {
/// The packet is too long to fit in the
/// transmission buffer. This is generally an error in the class implementation, because the
/// class shouldn't provide more data than the `max_packet_size` it specified when allocating
/// the endpoint.
BufferOverflow,
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Errors returned by [`EndpointOut::read`]
pub enum ReadError {
/// The received packet is too long to
/// fit in `buf`. This is generally an error in the class implementation, because the class
/// should use a buffer that is large enough for the `max_packet_size` it specified when
/// allocating the endpoint.
BufferOverflow,
}
/// Driver for a specific USB peripheral. Implement this to add support for a new hardware
/// platform.
pub trait Driver<'a> {
type EndpointOut: EndpointOut + 'a;
type EndpointIn: EndpointIn + 'a;
type Bus: Bus + 'a;
/// Allocates an endpoint and specified endpoint parameters. This method is called by the device
/// and class implementations to allocate endpoints, and can only be called before
/// [`enable`](UsbBus::enable) is called.
///
/// # Arguments
///
/// * `ep_addr` - A static endpoint address to allocate. If Some, the implementation should
/// attempt to return an endpoint with the specified address. If None, the implementation
/// should return the next available one.
/// * `max_packet_size` - Maximum packet size in bytes.
/// * `interval` - Polling interval parameter for interrupt endpoints.
fn alloc_endpoint_out(
&mut self,
ep_addr: Option<EndpointAddress>,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
) -> Result<Self::EndpointOut, EndpointAllocError>;
fn alloc_endpoint_in(
&mut self,
ep_addr: Option<EndpointAddress>,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
) -> Result<Self::EndpointIn, EndpointAllocError>;
/// Enables and initializes the USB peripheral. Soon after enabling the device will be reset, so
/// there is no need to perform a USB reset in this method.
fn enable(self) -> Self::Bus;
/// Indicates that `set_device_address` must be called before accepting the corresponding
/// control transfer, not after.
///
/// The default value for this constant is `false`, which corresponds to the USB 2.0 spec, 9.4.6
const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false;
}
pub trait Bus {
/// Called when the host resets the device. This will be soon called after
/// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Reset`]. This method should
/// reset the state of all endpoints and peripheral flags back to a state suitable for
/// enumeration, as well as ensure that all endpoints previously allocated with alloc_ep are
/// initialized as specified.
fn reset(&mut self);
/// Sets the device USB address to `addr`.
fn set_device_address(&mut self, addr: u8);
/// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it
/// should be prepared to receive data again. Only used during control transfers.
fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool);
/// Gets whether the STALL condition is set for an endpoint. Only used during control transfers.
fn is_stalled(&mut self, ep_addr: EndpointAddress) -> bool;
/// Causes the USB peripheral to enter USB suspend mode, lowering power consumption and
/// preparing to detect a USB wakeup event. This will be called after
/// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Suspend`]. The device will
/// continue be polled, and it shall return a value other than `Suspend` from `poll` when it no
/// longer detects the suspend condition.
fn suspend(&mut self);
/// Resumes from suspend mode. This may only be called after the peripheral has been previously
/// suspended.
fn resume(&mut self);
/// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the
/// device.
///
/// The default implementation just returns `Unsupported`.
///
/// # Errors
///
/// * [`Unsupported`](crate::UsbError::Unsupported) - This UsbBus implementation doesn't support
/// simulating a disconnect or it has not been enabled at creation time.
fn force_reset(&mut self) -> Result<(), Unsupported> {
Err(Unsupported)
}
}
pub trait Endpoint {
/// Get the endpoint address
fn info(&self) -> &EndpointInfo;
/// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it
/// should be prepared to receive data again.
fn set_stalled(&self, stalled: bool);
/// Gets whether the STALL condition is set for an endpoint.
fn is_stalled(&self) -> bool;
// TODO enable/disable?
}
pub trait EndpointOut: Endpoint {
type ReadFuture<'a>: Future<Output = Result<usize, ReadError>> + 'a
where
Self: 'a;
/// Reads a single packet of data from the endpoint, and returns the actual length of
/// the packet.
///
/// This should also clear any NAK flags and prepare the endpoint to receive the next packet.
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a>;
}
pub trait EndpointIn: Endpoint {
type WriteFuture<'a>: Future<Output = Result<(), WriteError>> + 'a
where
Self: 'a;
/// Writes a single packet of data to the endpoint.
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a>;
}

225
embassy-usb/src/fmt.rs Normal file
View File

@ -0,0 +1,225 @@
#![macro_use]
#![allow(unused_macros)]
#[cfg(all(feature = "defmt", feature = "log"))]
compile_error!("You may not enable both `defmt` and `log` features.");
macro_rules! assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert!($($x)*);
}
};
}
macro_rules! assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_eq!($($x)*);
}
};
}
macro_rules! assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_ne!($($x)*);
}
};
}
macro_rules! debug_assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert!($($x)*);
}
};
}
macro_rules! debug_assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_eq!($($x)*);
}
};
}
macro_rules! debug_assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_ne!($($x)*);
}
};
}
macro_rules! todo {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::todo!($($x)*);
#[cfg(feature = "defmt")]
::defmt::todo!($($x)*);
}
};
}
macro_rules! unreachable {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}
macro_rules! panic {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::panic!($($x)*);
#[cfg(feature = "defmt")]
::defmt::panic!($($x)*);
}
};
}
macro_rules! trace {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::trace!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::trace!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! debug {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::debug!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::debug!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! info {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::info!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::info!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! warn {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::warn!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::warn!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! error {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::error!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::error!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
#[cfg(feature = "defmt")]
macro_rules! unwrap {
($($x:tt)*) => {
::defmt::unwrap!($($x)*)
};
}
#[cfg(not(feature = "defmt"))]
macro_rules! unwrap {
($arg:expr) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
}
}
};
($arg:expr, $($msg:expr),+ $(,)? ) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
}
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NoneError;
pub trait Try {
type Ok;
type Error;
fn into_result(self) -> Result<Self::Ok, Self::Error>;
}
impl<T> Try for Option<T> {
type Ok = T;
type Error = NoneError;
#[inline]
fn into_result(self) -> Result<T, NoneError> {
self.ok_or(NoneError)
}
}
impl<T, E> Try for Result<T, E> {
type Ok = T;
type Error = E;
#[inline]
fn into_result(self) -> Self {
self
}
}

342
embassy-usb/src/lib.rs Normal file
View File

@ -0,0 +1,342 @@
#![no_std]
#![feature(generic_associated_types)]
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;
mod builder;
mod control;
pub mod descriptor;
pub mod driver;
pub mod types;
use self::control::*;
use self::descriptor::*;
use self::driver::*;
use self::types::*;
pub use self::builder::Config;
pub use self::builder::UsbDeviceBuilder;
/// The global state of the USB device.
///
/// In general class traffic is only possible in the `Configured` state.
#[repr(u8)]
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum UsbDeviceState {
/// The USB device has just been created or reset.
Default,
/// The USB device has received an address from the host.
Addressed,
/// The USB device has been configured and is fully functional.
Configured,
/// The USB device has been suspended by the host or it has been unplugged from the USB bus.
Suspend,
}
/// The bConfiguration value for the not configured state.
pub const CONFIGURATION_NONE: u8 = 0;
/// The bConfiguration value for the single configuration supported by this device.
pub const CONFIGURATION_VALUE: u8 = 1;
/// The default value for bAlternateSetting for all interfaces.
pub const DEFAULT_ALTERNATE_SETTING: u8 = 0;
pub struct UsbDevice<'d, D: Driver<'d>> {
driver: D::Bus,
control_in: D::EndpointIn,
control_out: D::EndpointOut,
config: Config<'d>,
device_descriptor: &'d [u8],
config_descriptor: &'d [u8],
bos_descriptor: &'d [u8],
device_state: UsbDeviceState,
remote_wakeup_enabled: bool,
self_powered: bool,
pending_address: u8,
}
impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
pub(crate) fn build(
mut driver: D,
config: Config<'d>,
device_descriptor: &'d [u8],
config_descriptor: &'d [u8],
bos_descriptor: &'d [u8],
) -> Self {
let control_out = driver
.alloc_endpoint_out(
Some(0x00.into()),
EndpointType::Control,
config.max_packet_size_0 as u16,
0,
)
.expect("failed to alloc control endpoint");
let control_in = driver
.alloc_endpoint_in(
Some(0x80.into()),
EndpointType::Control,
config.max_packet_size_0 as u16,
0,
)
.expect("failed to alloc control endpoint");
// Enable the USB bus.
// This prevent further allocation by consuming the driver.
let driver = driver.enable();
Self {
driver,
config,
control_in,
control_out,
device_descriptor,
config_descriptor,
bos_descriptor,
device_state: UsbDeviceState::Default,
remote_wakeup_enabled: false,
self_powered: false,
pending_address: 0,
}
}
pub async fn run(&mut self) {
loop {
let mut buf = [0; 8];
let n = self.control_out.read(&mut buf).await.unwrap();
assert_eq!(n, 8);
let req = Request::parse(&buf).unwrap();
info!("setup request: {:x}", req);
// Now that we have properly parsed the setup packet, ensure the end-point is no longer in
// a stalled state.
self.control_out.set_stalled(false);
match req.direction {
UsbDirection::In => self.handle_control_in(req).await,
UsbDirection::Out => self.handle_control_out(req).await,
}
}
}
async fn write_chunked(&mut self, data: &[u8]) -> Result<(), driver::WriteError> {
for c in data.chunks(8) {
self.control_in.write(c).await?;
}
if data.len() % 8 == 0 {
self.control_in.write(&[]).await?;
}
Ok(())
}
async fn control_out_accept(&mut self, req: Request) {
info!("control out accept");
// status phase
// todo: cleanup
self.control_out.read(&mut []).await.unwrap();
}
async fn control_in_accept(&mut self, req: Request, data: &[u8]) {
info!("control accept {:x}", data);
let len = data.len().min(req.length as _);
if let Err(e) = self.write_chunked(&data[..len]).await {
info!("write_chunked failed: {:?}", e);
}
// status phase
// todo: cleanup
self.control_out.read(&mut []).await.unwrap();
}
async fn control_in_accept_writer(
&mut self,
req: Request,
f: impl FnOnce(&mut DescriptorWriter),
) {
let mut buf = [0; 256];
let mut w = DescriptorWriter::new(&mut buf);
f(&mut w);
let pos = w.position();
self.control_in_accept(req, &buf[..pos]).await;
}
fn control_reject(&mut self, req: Request) {
self.control_out.set_stalled(true);
}
async fn handle_control_out(&mut self, req: Request) {
// TODO actually read the data if there's an OUT data phase.
const CONFIGURATION_NONE_U16: u16 = CONFIGURATION_NONE as u16;
const CONFIGURATION_VALUE_U16: u16 = CONFIGURATION_VALUE as u16;
const DEFAULT_ALTERNATE_SETTING_U16: u16 = DEFAULT_ALTERNATE_SETTING as u16;
match req.request_type {
RequestType::Standard => match (req.recipient, req.request, req.value) {
(
Recipient::Device,
Request::CLEAR_FEATURE,
Request::FEATURE_DEVICE_REMOTE_WAKEUP,
) => {
self.remote_wakeup_enabled = false;
self.control_out_accept(req).await;
}
(Recipient::Endpoint, Request::CLEAR_FEATURE, Request::FEATURE_ENDPOINT_HALT) => {
//self.bus.set_stalled(((req.index as u8) & 0x8f).into(), false);
self.control_out_accept(req).await;
}
(
Recipient::Device,
Request::SET_FEATURE,
Request::FEATURE_DEVICE_REMOTE_WAKEUP,
) => {
self.remote_wakeup_enabled = true;
self.control_out_accept(req).await;
}
(Recipient::Endpoint, Request::SET_FEATURE, Request::FEATURE_ENDPOINT_HALT) => {
//self.bus.set_stalled(((req.index as u8) & 0x8f).into(), true);
self.control_out_accept(req).await;
}
(Recipient::Device, Request::SET_ADDRESS, 1..=127) => {
self.pending_address = req.value as u8;
// on NRF the hardware auto-handles SET_ADDRESS.
self.control_out_accept(req).await;
}
(Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => {
self.device_state = UsbDeviceState::Configured;
self.control_out_accept(req).await;
}
(Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => {
match self.device_state {
UsbDeviceState::Default => {
self.control_out_accept(req).await;
}
_ => {
self.device_state = UsbDeviceState::Addressed;
self.control_out_accept(req).await;
}
}
}
(Recipient::Interface, Request::SET_INTERFACE, DEFAULT_ALTERNATE_SETTING_U16) => {
// TODO: do something when alternate settings are implemented
self.control_out_accept(req).await;
}
_ => self.control_reject(req),
},
_ => self.control_reject(req),
}
}
async fn handle_control_in(&mut self, req: Request) {
match req.request_type {
RequestType::Standard => match (req.recipient, req.request) {
(Recipient::Device, Request::GET_STATUS) => {
let mut status: u16 = 0x0000;
if self.self_powered {
status |= 0x0001;
}
if self.remote_wakeup_enabled {
status |= 0x0002;
}
self.control_in_accept(req, &status.to_le_bytes()).await;
}
(Recipient::Interface, Request::GET_STATUS) => {
let status: u16 = 0x0000;
self.control_in_accept(req, &status.to_le_bytes()).await;
}
(Recipient::Endpoint, Request::GET_STATUS) => {
let ep_addr: EndpointAddress = ((req.index as u8) & 0x8f).into();
let mut status: u16 = 0x0000;
if self.driver.is_stalled(ep_addr) {
status |= 0x0001;
}
self.control_in_accept(req, &status.to_le_bytes()).await;
}
(Recipient::Device, Request::GET_DESCRIPTOR) => {
self.handle_get_descriptor(req).await;
}
(Recipient::Device, Request::GET_CONFIGURATION) => {
let status = match self.device_state {
UsbDeviceState::Configured => CONFIGURATION_VALUE,
_ => CONFIGURATION_NONE,
};
self.control_in_accept(req, &status.to_le_bytes()).await;
}
(Recipient::Interface, Request::GET_INTERFACE) => {
// TODO: change when alternate settings are implemented
let status = DEFAULT_ALTERNATE_SETTING;
self.control_in_accept(req, &status.to_le_bytes()).await;
}
_ => self.control_reject(req),
},
_ => self.control_reject(req),
}
}
async fn handle_get_descriptor(&mut self, req: Request) {
let (dtype, index) = req.descriptor_type_index();
let config = self.config.clone();
match dtype {
descriptor_type::BOS => self.control_in_accept(req, self.bos_descriptor).await,
descriptor_type::DEVICE => self.control_in_accept(req, self.device_descriptor).await,
descriptor_type::CONFIGURATION => {
self.control_in_accept(req, self.config_descriptor).await
}
descriptor_type::STRING => {
if index == 0 {
self.control_in_accept_writer(req, |w| {
w.write(descriptor_type::STRING, &lang_id::ENGLISH_US.to_le_bytes())
.unwrap();
})
.await
} else {
let s = match index {
1 => self.config.manufacturer,
2 => self.config.product,
3 => self.config.serial_number,
_ => {
let index = StringIndex::new(index);
let lang_id = req.index;
None
//classes
// .iter()
// .filter_map(|cls| cls.get_string(index, lang_id))
// .nth(0)
}
};
if let Some(s) = s {
self.control_in_accept_writer(req, |w| w.string(s).unwrap())
.await;
} else {
self.control_reject(req)
}
}
}
_ => self.control_reject(req),
}
}
}

138
embassy-usb/src/types.rs Normal file
View File

@ -0,0 +1,138 @@
/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from
/// the perspective of the host, which is backward for devices, but the standard directions are used
/// for consistency.
///
/// The values of the enum also match the direction bit used in endpoint addresses and control
/// request types.
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum UsbDirection {
/// Host to device (OUT)
Out = 0x00,
/// Device to host (IN)
In = 0x80,
}
impl From<u8> for UsbDirection {
fn from(value: u8) -> Self {
unsafe { core::mem::transmute(value & 0x80) }
}
}
/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the
/// transfer bmAttributes transfer type bits.
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum EndpointType {
/// Control endpoint. Used for device management. Only the host can initiate requests. Usually
/// used only endpoint 0.
Control = 0b00,
/// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet.
Isochronous = 0b01,
/// Bulk endpoint. Used for large amounts of best-effort reliable data.
Bulk = 0b10,
/// Interrupt endpoint. Used for small amounts of time-critical reliable data.
Interrupt = 0b11,
}
/// Type-safe endpoint address.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EndpointAddress(u8);
impl From<u8> for EndpointAddress {
#[inline]
fn from(addr: u8) -> EndpointAddress {
EndpointAddress(addr)
}
}
impl From<EndpointAddress> for u8 {
#[inline]
fn from(addr: EndpointAddress) -> u8 {
addr.0
}
}
impl EndpointAddress {
const INBITS: u8 = UsbDirection::In as u8;
/// Constructs a new EndpointAddress with the given index and direction.
#[inline]
pub fn from_parts(index: usize, dir: UsbDirection) -> Self {
EndpointAddress(index as u8 | dir as u8)
}
/// Gets the direction part of the address.
#[inline]
pub fn direction(&self) -> UsbDirection {
if (self.0 & Self::INBITS) != 0 {
UsbDirection::In
} else {
UsbDirection::Out
}
}
/// Returns true if the direction is IN, otherwise false.
#[inline]
pub fn is_in(&self) -> bool {
(self.0 & Self::INBITS) != 0
}
/// Returns true if the direction is OUT, otherwise false.
#[inline]
pub fn is_out(&self) -> bool {
(self.0 & Self::INBITS) == 0
}
/// Gets the index part of the endpoint address.
#[inline]
pub fn index(&self) -> usize {
(self.0 & !Self::INBITS) as usize
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EndpointInfo {
pub addr: EndpointAddress,
pub ep_type: EndpointType,
pub max_packet_size: u16,
pub interval: u8,
}
/// A handle for a USB interface that contains its number.
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InterfaceNumber(u8);
impl InterfaceNumber {
pub(crate) fn new(index: u8) -> InterfaceNumber {
InterfaceNumber(index)
}
}
impl From<InterfaceNumber> for u8 {
fn from(n: InterfaceNumber) -> u8 {
n.0
}
}
/// A handle for a USB string descriptor that contains its index.
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct StringIndex(u8);
impl StringIndex {
pub(crate) fn new(index: u8) -> StringIndex {
StringIndex(index)
}
}
impl From<StringIndex> for u8 {
fn from(i: StringIndex) -> u8 {
i.0
}
}

View File

@ -10,7 +10,8 @@ nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits"]
[dependencies]
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] }
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
defmt = "0.3"
defmt-rtt = "0.3"
@ -22,5 +23,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa
rand = { version = "0.8.4", default-features = false }
embedded-storage = "0.3.0"
usb-device = "0.2"
usbd-serial = "0.1.1"

View File

@ -0,0 +1,356 @@
use core::convert::TryInto;
use core::mem;
use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError};
use embassy_usb::{driver::Driver, types::*, UsbDeviceBuilder};
/// This should be used as `device_class` when building the `UsbDevice`.
pub const USB_CLASS_CDC: u8 = 0x02;
const USB_CLASS_CDC_DATA: u8 = 0x0a;
const CDC_SUBCLASS_ACM: u8 = 0x02;
const CDC_PROTOCOL_NONE: u8 = 0x00;
const CS_INTERFACE: u8 = 0x24;
const CDC_TYPE_HEADER: u8 = 0x00;
const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01;
const CDC_TYPE_ACM: u8 = 0x02;
const CDC_TYPE_UNION: u8 = 0x06;
const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;
#[allow(unused)]
const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;
const REQ_SET_LINE_CODING: u8 = 0x20;
const REQ_GET_LINE_CODING: u8 = 0x21;
const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
/// Packet level implementation of a CDC-ACM serial port.
///
/// This class can be used directly and it has the least overhead due to directly reading and
/// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial
/// port. The following constraints must be followed if you use this class directly:
///
/// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes, and the
/// method will return a `WouldBlock` error if there is no packet to be read.
/// - `write_packet` must not be called with a buffer larger than max_packet_size bytes, and the
/// method will return a `WouldBlock` error if the previous packet has not been sent yet.
/// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the
/// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)
/// can be sent if there is no other data to send. This is because USB bulk transactions must be
/// terminated with a short packet, even if the bulk endpoint is used for stream-like data.
pub struct CdcAcmClass<'d, D: Driver<'d>> {
comm_if: InterfaceNumber,
comm_ep: D::EndpointIn,
data_if: InterfaceNumber,
read_ep: D::EndpointOut,
write_ep: D::EndpointIn,
line_coding: LineCoding,
dtr: bool,
rts: bool,
}
impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
/// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
/// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
pub fn new(builder: &mut UsbDeviceBuilder<'d, D>, max_packet_size: u16) -> Self {
let comm_if = builder.alloc_interface();
let comm_ep = builder.alloc_interrupt_endpoint_in(8, 255);
let data_if = builder.alloc_interface();
let read_ep = builder.alloc_bulk_endpoint_out(max_packet_size);
let write_ep = builder.alloc_bulk_endpoint_in(max_packet_size);
builder
.config_descriptor
.iad(
comm_if,
2,
USB_CLASS_CDC,
CDC_SUBCLASS_ACM,
CDC_PROTOCOL_NONE,
)
.unwrap();
builder
.config_descriptor
.interface(comm_if, USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE)
.unwrap();
builder
.config_descriptor
.write(
CS_INTERFACE,
&[
CDC_TYPE_HEADER, // bDescriptorSubtype
0x10,
0x01, // bcdCDC (1.10)
],
)
.unwrap();
builder
.config_descriptor
.write(
CS_INTERFACE,
&[
CDC_TYPE_ACM, // bDescriptorSubtype
0x00, // bmCapabilities
],
)
.unwrap();
builder
.config_descriptor
.write(
CS_INTERFACE,
&[
CDC_TYPE_UNION, // bDescriptorSubtype
comm_if.into(), // bControlInterface
data_if.into(), // bSubordinateInterface
],
)
.unwrap();
builder
.config_descriptor
.write(
CS_INTERFACE,
&[
CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype
0x00, // bmCapabilities
data_if.into(), // bDataInterface
],
)
.unwrap();
builder.config_descriptor.endpoint(comm_ep.info()).unwrap();
builder
.config_descriptor
.interface(data_if, USB_CLASS_CDC_DATA, 0x00, 0x00)
.unwrap();
builder.config_descriptor.endpoint(write_ep.info()).unwrap();
builder.config_descriptor.endpoint(read_ep.info()).unwrap();
CdcAcmClass {
comm_if,
comm_ep,
data_if,
read_ep,
write_ep,
line_coding: LineCoding {
stop_bits: StopBits::One,
data_bits: 8,
parity_type: ParityType::None,
data_rate: 8_000,
},
dtr: false,
rts: false,
}
}
/// Gets the maximum packet size in bytes.
pub fn max_packet_size(&self) -> u16 {
// The size is the same for both endpoints.
self.read_ep.info().max_packet_size
}
/// Gets the current line coding. The line coding contains information that's mainly relevant
/// for USB to UART serial port emulators, and can be ignored if not relevant.
pub fn line_coding(&self) -> &LineCoding {
&self.line_coding
}
/// Gets the DTR (data terminal ready) state
pub fn dtr(&self) -> bool {
self.dtr
}
/// Gets the RTS (request to send) state
pub fn rts(&self) -> bool {
self.rts
}
/// Writes a single packet into the IN endpoint.
pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), WriteError> {
self.write_ep.write(data).await
}
/// Reads a single packet from the OUT endpoint.
pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, ReadError> {
self.read_ep.read(data).await
}
/// Gets the address of the IN endpoint.
pub(crate) fn write_ep_address(&self) -> EndpointAddress {
self.write_ep.info().addr
}
}
/*
impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> {
fn get_configuration_descriptors(&self, builder.config_descriptor: &mut Descriptorbuilder.config_descriptor) -> Result<()> {
Ok(())
}
fn reset(&mut self) {
self.line_coding = LineCoding::default();
self.dtr = false;
self.rts = false;
}
fn control_in(&mut self, xfer: ControlIn<B>) {
let req = xfer.request();
if !(req.request_type == control::RequestType::Class
&& req.recipient == control::Recipient::Interface
&& req.index == u8::from(self.comm_if) as u16)
{
return;
}
match req.request {
// REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
REQ_GET_LINE_CODING if req.length == 7 => {
xfer.accept(|data| {
data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes());
data[4] = self.line_coding.stop_bits as u8;
data[5] = self.line_coding.parity_type as u8;
data[6] = self.line_coding.data_bits;
Ok(7)
})
.ok();
}
_ => {
xfer.reject().ok();
}
}
}
fn control_out(&mut self, xfer: ControlOut<B>) {
let req = xfer.request();
if !(req.request_type == control::RequestType::Class
&& req.recipient == control::Recipient::Interface
&& req.index == u8::from(self.comm_if) as u16)
{
return;
}
match req.request {
REQ_SEND_ENCAPSULATED_COMMAND => {
// We don't actually support encapsulated commands but pretend we do for standards
// compatibility.
xfer.accept().ok();
}
REQ_SET_LINE_CODING if xfer.data().len() >= 7 => {
self.line_coding.data_rate =
u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap());
self.line_coding.stop_bits = xfer.data()[4].into();
self.line_coding.parity_type = xfer.data()[5].into();
self.line_coding.data_bits = xfer.data()[6];
xfer.accept().ok();
}
REQ_SET_CONTROL_LINE_STATE => {
self.dtr = (req.value & 0x0001) != 0;
self.rts = (req.value & 0x0002) != 0;
xfer.accept().ok();
}
_ => {
xfer.reject().ok();
}
};
}
}
*/
/// Number of stop bits for LineCoding
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum StopBits {
/// 1 stop bit
One = 0,
/// 1.5 stop bits
OnePointFive = 1,
/// 2 stop bits
Two = 2,
}
impl From<u8> for StopBits {
fn from(value: u8) -> Self {
if value <= 2 {
unsafe { mem::transmute(value) }
} else {
StopBits::One
}
}
}
/// Parity for LineCoding
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum ParityType {
None = 0,
Odd = 1,
Event = 2,
Mark = 3,
Space = 4,
}
impl From<u8> for ParityType {
fn from(value: u8) -> Self {
if value <= 4 {
unsafe { mem::transmute(value) }
} else {
ParityType::None
}
}
}
/// Line coding parameters
///
/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can
/// be ignored if you don't plan to interface with a physical UART.
pub struct LineCoding {
stop_bits: StopBits,
data_bits: u8,
parity_type: ParityType,
data_rate: u32,
}
impl LineCoding {
/// Gets the number of stop bits for UART communication.
pub fn stop_bits(&self) -> StopBits {
self.stop_bits
}
/// Gets the number of data bits for UART communication.
pub fn data_bits(&self) -> u8 {
self.data_bits
}
/// Gets the parity type for UART communication.
pub fn parity_type(&self) -> ParityType {
self.parity_type
}
/// Gets the data rate in bits per second for UART communication.
pub fn data_rate(&self) -> u32 {
self.data_rate
}
}
impl Default for LineCoding {
fn default() -> Self {
LineCoding {
stop_bits: StopBits::One,
data_bits: 8,
parity_type: ParityType::None,
data_rate: 8_000,
}
}
}

View File

@ -0,0 +1,53 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
#[path = "../../example_common.rs"]
mod example_common;
mod cdc_acm;
use core::mem;
use defmt::*;
use embassy::executor::Spawner;
use embassy_nrf::interrupt;
use embassy_nrf::pac;
use embassy_nrf::usb::{self, Driver};
use embassy_nrf::Peripherals;
use embassy_usb::{Config, UsbDeviceBuilder};
use crate::cdc_acm::CdcAcmClass;
#[embassy::main]
async fn main(_spawner: Spawner, p: Peripherals) {
let clock: pac::CLOCK = unsafe { mem::transmute(()) };
let power: pac::POWER = unsafe { mem::transmute(()) };
info!("Enabling ext hfosc...");
clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) });
while clock.events_hfclkstarted.read().bits() != 1 {}
info!("Waiting for vbus...");
while !power.usbregstatus.read().vbusdetect().is_vbus_present() {}
info!("vbus OK");
let irq = interrupt::take!(USBD);
let driver = Driver::new(p.USBD, irq);
let config = Config::new(0xc0de, 0xcafe);
let mut device_descriptor = [0; 256];
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
let mut builder = UsbDeviceBuilder::new(
driver,
config,
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
);
let mut class = CdcAcmClass::new(&mut builder, 64);
let mut usb = builder.build();
usb.run().await;
}

View File

@ -1,89 +0,0 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::{info, unwrap};
use embassy::executor::Spawner;
use embassy::interrupt::InterruptExt;
use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial};
use embassy_nrf::{interrupt, Peripherals};
use futures::pin_mut;
use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
use defmt_rtt as _; // global logger
use panic_probe as _; // print out panic messages
#[embassy::main]
async fn main(_spawner: Spawner, p: Peripherals) {
let mut rx_buffer = [0u8; 64];
// we send back input + cr + lf
let mut tx_buffer = [0u8; 66];
let usb_bus = UsbBus::new(p.USBD);
let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer);
let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
.manufacturer("Fake company")
.product("Serial port")
.serial_number("TEST")
.device_class(0x02)
.build();
let irq = interrupt::take!(USBD);
irq.set_priority(interrupt::Priority::P3);
let mut state = State::new();
let usb = unsafe { Usb::new(&mut state, device, serial, irq) };
pin_mut!(usb);
let (mut reader, mut writer) = usb.as_ref().take_serial_0();
info!("usb initialized!");
unwrap!(
writer
.write_all(b"\r\nInput returned upper cased on CR+LF\r\n")
.await
);
let mut buf = [0u8; 64];
loop {
let mut n = 0;
async {
loop {
let char = unwrap!(reader.read_byte().await);
// throw away, read more on cr, exit on lf
if char == b'\r' {
continue;
} else if char == b'\n' {
break;
}
buf[n] = char;
n += 1;
// stop if we're out of room
if n == buf.len() {
break;
}
}
}
.await;
if n > 0 {
for char in buf[..n].iter_mut() {
// upper case
if 0x61 <= *char && *char <= 0x7a {
*char &= !0x20;
}
}
unwrap!(writer.write_all(&buf[..n]).await);
unwrap!(writer.write_all(b"\r\n").await);
unwrap!(writer.flush().await);
}
}
}

View File

@ -1,66 +0,0 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::{info, unwrap};
use embassy::executor::Spawner;
use embassy::interrupt::InterruptExt;
use embassy::io::{read_line, AsyncWriteExt};
use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial};
use embassy_nrf::{interrupt, Peripherals};
use futures::pin_mut;
use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
use defmt_rtt as _; // global logger
use panic_probe as _; // print out panic messages
#[embassy::main]
async fn main(_spawner: Spawner, p: Peripherals) {
let mut rx_buffer = [0u8; 64];
// we send back input + cr + lf
let mut tx_buffer = [0u8; 66];
let usb_bus = UsbBus::new(p.USBD);
let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer);
let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
.manufacturer("Fake company")
.product("Serial port")
.serial_number("TEST")
.device_class(0x02)
.build();
let irq = interrupt::take!(USBD);
irq.set_priority(interrupt::Priority::P3);
let mut state = State::new();
let usb = unsafe { Usb::new(&mut state, device, serial, irq) };
pin_mut!(usb);
let (mut reader, mut writer) = usb.as_ref().take_serial_0();
info!("usb initialized!");
unwrap!(
writer
.write_all(b"\r\nInput returned upper cased on CR+LF\r\n")
.await
);
let mut buf = [0u8; 64];
loop {
let n = unwrap!(read_line(&mut reader, &mut buf).await);
for char in buf[..n].iter_mut() {
// upper case
if 0x61 <= *char && *char <= 0x7a {
*char &= !0x20;
}
}
unwrap!(writer.write_all(&buf[..n]).await);
unwrap!(writer.write_all(b"\r\n").await);
unwrap!(writer.flush().await);
}
}