Merge #720
720: USB: New builder API r=Dirbaio a=Dirbaio usb: improved descriptor building API The same API call allocates interfaces/endpoints/etc and writes their descriptors. This means less API calls, and less possibility to screw things up. DescriptorWriter is now private. Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
This commit is contained in:
commit
0f1a364cd9
9 changed files with 248 additions and 267 deletions
|
@ -16,7 +16,7 @@ use embassy_usb::driver::EndpointOut;
|
||||||
use embassy_usb::{
|
use embassy_usb::{
|
||||||
control::{ControlHandler, InResponse, OutResponse, Request, RequestType},
|
control::{ControlHandler, InResponse, OutResponse, Request, RequestType},
|
||||||
driver::{Driver, Endpoint, EndpointError, EndpointIn},
|
driver::{Driver, Endpoint, EndpointError, EndpointIn},
|
||||||
UsbDeviceBuilder,
|
Builder,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "usbd-hid")]
|
#[cfg(feature = "usbd-hid")]
|
||||||
|
@ -98,7 +98,7 @@ pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build<'d, D: Driver<'d>>(
|
fn build<'d, D: Driver<'d>>(
|
||||||
builder: &mut UsbDeviceBuilder<'d, D>,
|
builder: &mut Builder<'d, D>,
|
||||||
state: &'d mut State<'d>,
|
state: &'d mut State<'d>,
|
||||||
config: Config<'d>,
|
config: Config<'d>,
|
||||||
with_out_endpoint: bool,
|
with_out_endpoint: bool,
|
||||||
|
@ -110,23 +110,13 @@ fn build<'d, D: Driver<'d>>(
|
||||||
));
|
));
|
||||||
|
|
||||||
let len = config.report_descriptor.len();
|
let len = config.report_descriptor.len();
|
||||||
let if_num = builder.alloc_interface_with_handler(control);
|
|
||||||
let ep_in = builder.alloc_interrupt_endpoint_in(config.max_packet_size, config.poll_ms);
|
|
||||||
let ep_out = if with_out_endpoint {
|
|
||||||
Some(builder.alloc_interrupt_endpoint_out(config.max_packet_size, config.poll_ms))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
builder.config_descriptor.interface(
|
let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE);
|
||||||
if_num,
|
let mut iface = func.interface(Some(control));
|
||||||
USB_CLASS_HID,
|
let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE);
|
||||||
USB_SUBCLASS_NONE,
|
|
||||||
USB_PROTOCOL_NONE,
|
|
||||||
);
|
|
||||||
|
|
||||||
// HID descriptor
|
// HID descriptor
|
||||||
builder.config_descriptor.write(
|
alt.descriptor(
|
||||||
HID_DESC_DESCTYPE_HID,
|
HID_DESC_DESCTYPE_HID,
|
||||||
&[
|
&[
|
||||||
// HID Class spec version
|
// HID Class spec version
|
||||||
|
@ -144,10 +134,12 @@ fn build<'d, D: Driver<'d>>(
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
builder.config_descriptor.endpoint(ep_in.info());
|
let ep_in = alt.endpoint_interrupt_in(config.max_packet_size, config.poll_ms);
|
||||||
if let Some(ep_out) = &ep_out {
|
let ep_out = if with_out_endpoint {
|
||||||
builder.config_descriptor.endpoint(ep_out.info());
|
Some(alt.endpoint_interrupt_out(config.max_packet_size, config.poll_ms))
|
||||||
}
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
(ep_out, ep_in, &state.out_report_offset)
|
(ep_out, ep_in, &state.out_report_offset)
|
||||||
}
|
}
|
||||||
|
@ -160,11 +152,7 @@ impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize>
|
||||||
/// This will allocate one IN and one OUT endpoints. If you only need writing (sending)
|
/// This will allocate one IN and one OUT endpoints. If you only need writing (sending)
|
||||||
/// HID reports, consider using [`HidWriter::new`] instead, which allocates an IN endpoint only.
|
/// HID reports, consider using [`HidWriter::new`] instead, which allocates an IN endpoint only.
|
||||||
///
|
///
|
||||||
pub fn new(
|
pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self {
|
||||||
builder: &mut UsbDeviceBuilder<'d, D>,
|
|
||||||
state: &'d mut State<'d>,
|
|
||||||
config: Config<'d>,
|
|
||||||
) -> Self {
|
|
||||||
let (ep_out, ep_in, offset) = build(builder, state, config, true);
|
let (ep_out, ep_in, offset) = build(builder, state, config, true);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -246,11 +234,7 @@ impl<'d, D: Driver<'d>, const N: usize> HidWriter<'d, D, N> {
|
||||||
/// HID reports. A lower value means better throughput & latency, at the expense
|
/// HID reports. A lower value means better throughput & latency, at the expense
|
||||||
/// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for
|
/// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for
|
||||||
/// high performance uses, and a value of 255 is good for best-effort usecases.
|
/// high performance uses, and a value of 255 is good for best-effort usecases.
|
||||||
pub fn new(
|
pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self {
|
||||||
builder: &mut UsbDeviceBuilder<'d, D>,
|
|
||||||
state: &'d mut State<'d>,
|
|
||||||
config: Config<'d>,
|
|
||||||
) -> Self {
|
|
||||||
let (ep_out, ep_in, _offset) = build(builder, state, config, false);
|
let (ep_out, ep_in, _offset) = build(builder, state, config, false);
|
||||||
|
|
||||||
assert!(ep_out.is_none());
|
assert!(ep_out.is_none());
|
||||||
|
|
|
@ -11,7 +11,7 @@ use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
use embassy::blocking_mutex::CriticalSectionMutex;
|
use embassy::blocking_mutex::CriticalSectionMutex;
|
||||||
use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request};
|
use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request};
|
||||||
use embassy_usb::driver::{Endpoint, EndpointError, EndpointIn, EndpointOut};
|
use embassy_usb::driver::{Endpoint, EndpointError, EndpointIn, EndpointOut};
|
||||||
use embassy_usb::{driver::Driver, types::*, UsbDeviceBuilder};
|
use embassy_usb::{driver::Driver, types::*, Builder};
|
||||||
|
|
||||||
/// This should be used as `device_class` when building the `UsbDevice`.
|
/// This should be used as `device_class` when building the `UsbDevice`.
|
||||||
pub const USB_CLASS_CDC: u8 = 0x02;
|
pub const USB_CLASS_CDC: u8 = 0x02;
|
||||||
|
@ -163,7 +163,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
|
||||||
/// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
|
/// 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.
|
/// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
builder: &mut UsbDeviceBuilder<'d, D>,
|
builder: &mut Builder<'d, D>,
|
||||||
state: &'d mut State<'d>,
|
state: &'d mut State<'d>,
|
||||||
max_packet_size: u16,
|
max_packet_size: u16,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -175,26 +175,15 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
|
||||||
|
|
||||||
assert!(builder.control_buf_len() >= 7);
|
assert!(builder.control_buf_len() >= 7);
|
||||||
|
|
||||||
let comm_if = builder.alloc_interface_with_handler(control);
|
let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE);
|
||||||
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(
|
// Control interface
|
||||||
comm_if,
|
let mut iface = func.interface(Some(control));
|
||||||
2,
|
let comm_if = iface.interface_number();
|
||||||
USB_CLASS_CDC,
|
let data_if = u8::from(comm_if) + 1;
|
||||||
CDC_SUBCLASS_ACM,
|
let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE);
|
||||||
CDC_PROTOCOL_NONE,
|
|
||||||
);
|
alt.descriptor(
|
||||||
builder.config_descriptor.interface(
|
|
||||||
comm_if,
|
|
||||||
USB_CLASS_CDC,
|
|
||||||
CDC_SUBCLASS_ACM,
|
|
||||||
CDC_PROTOCOL_NONE,
|
|
||||||
);
|
|
||||||
builder.config_descriptor.write(
|
|
||||||
CS_INTERFACE,
|
CS_INTERFACE,
|
||||||
&[
|
&[
|
||||||
CDC_TYPE_HEADER, // bDescriptorSubtype
|
CDC_TYPE_HEADER, // bDescriptorSubtype
|
||||||
|
@ -202,14 +191,14 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
|
||||||
0x01, // bcdCDC (1.10)
|
0x01, // bcdCDC (1.10)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
builder.config_descriptor.write(
|
alt.descriptor(
|
||||||
CS_INTERFACE,
|
CS_INTERFACE,
|
||||||
&[
|
&[
|
||||||
CDC_TYPE_ACM, // bDescriptorSubtype
|
CDC_TYPE_ACM, // bDescriptorSubtype
|
||||||
0x00, // bmCapabilities
|
0x00, // bmCapabilities
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
builder.config_descriptor.write(
|
alt.descriptor(
|
||||||
CS_INTERFACE,
|
CS_INTERFACE,
|
||||||
&[
|
&[
|
||||||
CDC_TYPE_UNION, // bDescriptorSubtype
|
CDC_TYPE_UNION, // bDescriptorSubtype
|
||||||
|
@ -217,7 +206,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
|
||||||
data_if.into(), // bSubordinateInterface
|
data_if.into(), // bSubordinateInterface
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
builder.config_descriptor.write(
|
alt.descriptor(
|
||||||
CS_INTERFACE,
|
CS_INTERFACE,
|
||||||
&[
|
&[
|
||||||
CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype
|
CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype
|
||||||
|
@ -225,13 +214,15 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
|
||||||
data_if.into(), // bDataInterface
|
data_if.into(), // bDataInterface
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
builder.config_descriptor.endpoint(comm_ep.info());
|
|
||||||
|
|
||||||
builder
|
let comm_ep = alt.endpoint_interrupt_in(8, 255);
|
||||||
.config_descriptor
|
|
||||||
.interface(data_if, USB_CLASS_CDC_DATA, 0x00, 0x00);
|
// Data interface
|
||||||
builder.config_descriptor.endpoint(write_ep.info());
|
let mut iface = func.interface(None);
|
||||||
builder.config_descriptor.endpoint(read_ep.info());
|
let data_if = iface.interface_number();
|
||||||
|
let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NONE);
|
||||||
|
let read_ep = alt.endpoint_bulk_out(max_packet_size);
|
||||||
|
let write_ep = alt.endpoint_bulk_in(max_packet_size);
|
||||||
|
|
||||||
CdcAcmClass {
|
CdcAcmClass {
|
||||||
_comm_ep: comm_ep,
|
_comm_ep: comm_ep,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use heapless::Vec;
|
||||||
|
|
||||||
use super::control::ControlHandler;
|
use super::control::ControlHandler;
|
||||||
use super::descriptor::{BosWriter, DescriptorWriter};
|
use super::descriptor::{BosWriter, DescriptorWriter};
|
||||||
use super::driver::{Driver, EndpointAllocError};
|
use super::driver::{Driver, Endpoint};
|
||||||
use super::types::*;
|
use super::types::*;
|
||||||
use super::DeviceStateHandler;
|
use super::DeviceStateHandler;
|
||||||
use super::UsbDevice;
|
use super::UsbDevice;
|
||||||
|
@ -117,8 +117,8 @@ impl<'a> Config<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to build new [`UsbDevice`]s.
|
/// [`UsbDevice`] builder.
|
||||||
pub struct UsbDeviceBuilder<'d, D: Driver<'d>> {
|
pub struct Builder<'d, D: Driver<'d>> {
|
||||||
config: Config<'d>,
|
config: Config<'d>,
|
||||||
handler: Option<&'d dyn DeviceStateHandler>,
|
handler: Option<&'d dyn DeviceStateHandler>,
|
||||||
interfaces: Vec<(u8, &'d mut dyn ControlHandler), MAX_INTERFACE_COUNT>,
|
interfaces: Vec<(u8, &'d mut dyn ControlHandler), MAX_INTERFACE_COUNT>,
|
||||||
|
@ -128,13 +128,12 @@ pub struct UsbDeviceBuilder<'d, D: Driver<'d>> {
|
||||||
next_interface_number: u8,
|
next_interface_number: u8,
|
||||||
next_string_index: u8,
|
next_string_index: u8,
|
||||||
|
|
||||||
// TODO make not pub?
|
device_descriptor: DescriptorWriter<'d>,
|
||||||
pub device_descriptor: DescriptorWriter<'d>,
|
config_descriptor: DescriptorWriter<'d>,
|
||||||
pub config_descriptor: DescriptorWriter<'d>,
|
bos_descriptor: BosWriter<'d>,
|
||||||
pub bos_descriptor: BosWriter<'d>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> {
|
impl<'d, D: Driver<'d>> Builder<'d, D> {
|
||||||
/// Creates a builder for constructing a new [`UsbDevice`].
|
/// Creates a builder for constructing a new [`UsbDevice`].
|
||||||
///
|
///
|
||||||
/// `control_buf` is a buffer used for USB control request data. It should be sized
|
/// `control_buf` is a buffer used for USB control request data. It should be sized
|
||||||
|
@ -175,7 +174,7 @@ impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> {
|
||||||
config_descriptor.configuration(&config);
|
config_descriptor.configuration(&config);
|
||||||
bos_descriptor.bos();
|
bos_descriptor.bos();
|
||||||
|
|
||||||
UsbDeviceBuilder {
|
Builder {
|
||||||
driver,
|
driver,
|
||||||
handler,
|
handler,
|
||||||
config,
|
config,
|
||||||
|
@ -207,36 +206,12 @@ impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the size of the control request data buffer. Can be used by
|
/// Returns the size of the control request data buffer. Can be used by
|
||||||
/// classes to validate the buffer is large enough for their needs.
|
/// classes to validate the buffer is large enough for their needs.
|
||||||
pub fn control_buf_len(&self) -> usize {
|
pub fn control_buf_len(&self) -> usize {
|
||||||
self.control_buf.len()
|
self.control_buf.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates a new interface number, with a handler that will be called
|
|
||||||
/// for all the control requests directed to it.
|
|
||||||
pub fn alloc_interface_with_handler(
|
|
||||||
&mut self,
|
|
||||||
handler: &'d mut dyn ControlHandler,
|
|
||||||
) -> InterfaceNumber {
|
|
||||||
let number = self.next_interface_number;
|
|
||||||
self.next_interface_number += 1;
|
|
||||||
|
|
||||||
if self.interfaces.push((number, handler)).is_err() {
|
|
||||||
panic!("max class count reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
InterfaceNumber::new(number)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allocates a new string index.
|
/// Allocates a new string index.
|
||||||
pub fn alloc_string(&mut self) -> StringIndex {
|
pub fn alloc_string(&mut self) -> StringIndex {
|
||||||
let index = self.next_string_index;
|
let index = self.next_string_index;
|
||||||
|
@ -245,146 +220,212 @@ impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> {
|
||||||
StringIndex::new(index)
|
StringIndex::new(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates an in endpoint.
|
/// Add an USB function.
|
||||||
///
|
///
|
||||||
/// This directly delegates to [`Driver::alloc_endpoint_in`], so see that method for details. In most
|
/// If [`Config::composite_with_iads`] is set, this will add an IAD descriptor
|
||||||
/// cases classes should call the endpoint type specific methods instead.
|
/// with the given class/subclass/protocol, associating all the child interfaces.
|
||||||
pub fn alloc_endpoint_in(
|
///
|
||||||
|
/// If it's not set, no IAD descriptor is added.
|
||||||
|
pub fn function(
|
||||||
|
&mut self,
|
||||||
|
class: u8,
|
||||||
|
subclass: u8,
|
||||||
|
protocol: u8,
|
||||||
|
) -> FunctionBuilder<'_, 'd, D> {
|
||||||
|
let iface_count_index = if self.config.composite_with_iads {
|
||||||
|
self.config_descriptor.iad(
|
||||||
|
InterfaceNumber::new(self.next_interface_number),
|
||||||
|
0,
|
||||||
|
class,
|
||||||
|
subclass,
|
||||||
|
protocol,
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(self.config_descriptor.position() - 5)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
FunctionBuilder {
|
||||||
|
builder: self,
|
||||||
|
iface_count_index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Function builder.
|
||||||
|
///
|
||||||
|
/// A function is a logical grouping of interfaces that perform a given USB function.
|
||||||
|
/// If [`Config::composite_with_iads`] is set, each function will have an IAD descriptor.
|
||||||
|
/// If not, functions will not be visible as descriptors.
|
||||||
|
pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> {
|
||||||
|
builder: &'a mut Builder<'d, D>,
|
||||||
|
iface_count_index: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
|
||||||
|
/// Add an interface to the function.
|
||||||
|
///
|
||||||
|
/// Interface numbers are guaranteed to be allocated consecutively, starting from 0.
|
||||||
|
pub fn interface(
|
||||||
|
&mut self,
|
||||||
|
handler: Option<&'d mut dyn ControlHandler>,
|
||||||
|
) -> InterfaceBuilder<'_, 'd, D> {
|
||||||
|
if let Some(i) = self.iface_count_index {
|
||||||
|
self.builder.config_descriptor.buf[i] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let number = self.builder.next_interface_number;
|
||||||
|
self.builder.next_interface_number += 1;
|
||||||
|
|
||||||
|
if let Some(handler) = handler {
|
||||||
|
if self.builder.interfaces.push((number, handler)).is_err() {
|
||||||
|
panic!("max interface count reached")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InterfaceBuilder {
|
||||||
|
builder: self.builder,
|
||||||
|
interface_number: InterfaceNumber::new(number),
|
||||||
|
next_alt_setting_number: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interface builder.
|
||||||
|
pub struct InterfaceBuilder<'a, 'd, D: Driver<'d>> {
|
||||||
|
builder: &'a mut Builder<'d, D>,
|
||||||
|
interface_number: InterfaceNumber,
|
||||||
|
next_alt_setting_number: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> {
|
||||||
|
/// Get the interface number.
|
||||||
|
pub fn interface_number(&self) -> InterfaceNumber {
|
||||||
|
self.interface_number
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an alternate setting to the interface and write its descriptor.
|
||||||
|
///
|
||||||
|
/// Alternate setting numbers are guaranteed to be allocated consecutively, starting from 0.
|
||||||
|
///
|
||||||
|
/// The first alternate setting, with number 0, is the default one.
|
||||||
|
pub fn alt_setting(
|
||||||
|
&mut self,
|
||||||
|
class: u8,
|
||||||
|
subclass: u8,
|
||||||
|
protocol: u8,
|
||||||
|
) -> InterfaceAltBuilder<'_, 'd, D> {
|
||||||
|
let number = self.next_alt_setting_number;
|
||||||
|
self.next_alt_setting_number += 1;
|
||||||
|
|
||||||
|
self.builder.config_descriptor.interface_alt(
|
||||||
|
self.interface_number,
|
||||||
|
number,
|
||||||
|
class,
|
||||||
|
subclass,
|
||||||
|
protocol,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
InterfaceAltBuilder {
|
||||||
|
builder: self.builder,
|
||||||
|
interface_number: self.interface_number,
|
||||||
|
alt_setting_number: number,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interface alternate setting builder.
|
||||||
|
pub struct InterfaceAltBuilder<'a, 'd, D: Driver<'d>> {
|
||||||
|
builder: &'a mut Builder<'d, D>,
|
||||||
|
interface_number: InterfaceNumber,
|
||||||
|
alt_setting_number: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
||||||
|
/// Get the interface number.
|
||||||
|
pub fn interface_number(&self) -> InterfaceNumber {
|
||||||
|
self.interface_number
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the alternate setting number.
|
||||||
|
pub fn alt_setting_number(&self) -> u8 {
|
||||||
|
self.alt_setting_number
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a custom descriptor to this alternate setting.
|
||||||
|
///
|
||||||
|
/// Descriptors are written in the order builder functions are called. Note that some
|
||||||
|
/// classes care about the order.
|
||||||
|
pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) {
|
||||||
|
self.builder
|
||||||
|
.config_descriptor
|
||||||
|
.write(descriptor_type, descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint_in(
|
||||||
&mut self,
|
&mut self,
|
||||||
ep_addr: Option<EndpointAddress>,
|
ep_addr: Option<EndpointAddress>,
|
||||||
ep_type: EndpointType,
|
ep_type: EndpointType,
|
||||||
max_packet_size: u16,
|
max_packet_size: u16,
|
||||||
interval: u8,
|
interval: u8,
|
||||||
) -> Result<D::EndpointIn, EndpointAllocError> {
|
|
||||||
self.driver
|
|
||||||
.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.driver
|
|
||||||
.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_pipe(&mut self, max_packet_size: u16) -> D::ControlPipe {
|
|
||||||
self.driver
|
|
||||||
.alloc_control_pipe(max_packet_size)
|
|
||||||
.expect("alloc_control_pipe 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 {
|
) -> D::EndpointIn {
|
||||||
self.alloc_endpoint_in(None, EndpointType::Interrupt, max_packet_size, interval)
|
let ep = self
|
||||||
.expect("alloc_ep failed")
|
.builder
|
||||||
|
.driver
|
||||||
|
.alloc_endpoint_in(ep_addr, ep_type, max_packet_size, interval)
|
||||||
|
.expect("alloc_endpoint_in failed");
|
||||||
|
|
||||||
|
self.builder.config_descriptor.endpoint(ep.info());
|
||||||
|
|
||||||
|
ep
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates a bulk in endpoint.
|
fn endpoint_out(
|
||||||
///
|
|
||||||
/// # 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,
|
&mut self,
|
||||||
|
ep_addr: Option<EndpointAddress>,
|
||||||
|
ep_type: EndpointType,
|
||||||
max_packet_size: u16,
|
max_packet_size: u16,
|
||||||
interval: u8,
|
interval: u8,
|
||||||
) -> D::EndpointOut {
|
) -> D::EndpointOut {
|
||||||
self.alloc_endpoint_out(None, EndpointType::Interrupt, max_packet_size, interval)
|
let ep = self
|
||||||
.expect("alloc_ep failed")
|
.builder
|
||||||
|
.driver
|
||||||
|
.alloc_endpoint_out(ep_addr, ep_type, max_packet_size, interval)
|
||||||
|
.expect("alloc_endpoint_out failed");
|
||||||
|
|
||||||
|
self.builder.config_descriptor.endpoint(ep.info());
|
||||||
|
|
||||||
|
ep
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a BULK IN endpoint and write its descriptor.
|
||||||
|
///
|
||||||
|
/// Descriptors are written in the order builder functions are called. Note that some
|
||||||
|
/// classes care about the order.
|
||||||
|
pub fn endpoint_bulk_in(&mut self, max_packet_size: u16) -> D::EndpointIn {
|
||||||
|
self.endpoint_in(None, EndpointType::Bulk, max_packet_size, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a BULK OUT endpoint and write its descriptor.
|
||||||
|
///
|
||||||
|
/// Descriptors are written in the order builder functions are called. Note that some
|
||||||
|
/// classes care about the order.
|
||||||
|
pub fn endpoint_bulk_out(&mut self, max_packet_size: u16) -> D::EndpointOut {
|
||||||
|
self.endpoint_out(None, EndpointType::Bulk, max_packet_size, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a INTERRUPT IN endpoint and write its descriptor.
|
||||||
|
///
|
||||||
|
/// Descriptors are written in the order builder functions are called. Note that some
|
||||||
|
/// classes care about the order.
|
||||||
|
pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn {
|
||||||
|
self.endpoint_in(None, EndpointType::Interrupt, max_packet_size, interval)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a INTERRUPT OUT endpoint and write its descriptor.
|
||||||
|
pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut {
|
||||||
|
self.endpoint_out(None, EndpointType::Interrupt, max_packet_size, interval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,12 +33,11 @@ pub mod capability_type {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A writer for USB descriptors.
|
/// A writer for USB descriptors.
|
||||||
pub struct DescriptorWriter<'a> {
|
pub(crate) struct DescriptorWriter<'a> {
|
||||||
buf: &'a mut [u8],
|
pub buf: &'a mut [u8],
|
||||||
position: usize,
|
position: usize,
|
||||||
num_interfaces_mark: Option<usize>,
|
num_interfaces_mark: Option<usize>,
|
||||||
num_endpoints_mark: Option<usize>,
|
num_endpoints_mark: Option<usize>,
|
||||||
write_iads: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DescriptorWriter<'a> {
|
impl<'a> DescriptorWriter<'a> {
|
||||||
|
@ -48,7 +47,6 @@ impl<'a> DescriptorWriter<'a> {
|
||||||
position: 0,
|
position: 0,
|
||||||
num_interfaces_mark: None,
|
num_interfaces_mark: None,
|
||||||
num_endpoints_mark: None,
|
num_endpoints_mark: None,
|
||||||
write_iads: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,8 +104,6 @@ impl<'a> DescriptorWriter<'a> {
|
||||||
pub(crate) fn configuration(&mut self, config: &Config) {
|
pub(crate) fn configuration(&mut self, config: &Config) {
|
||||||
self.num_interfaces_mark = Some(self.position + 4);
|
self.num_interfaces_mark = Some(self.position + 4);
|
||||||
|
|
||||||
self.write_iads = config.composite_with_iads;
|
|
||||||
|
|
||||||
self.write(
|
self.write(
|
||||||
descriptor_type::CONFIGURATION,
|
descriptor_type::CONFIGURATION,
|
||||||
&[
|
&[
|
||||||
|
@ -160,10 +156,6 @@ impl<'a> DescriptorWriter<'a> {
|
||||||
function_sub_class: u8,
|
function_sub_class: u8,
|
||||||
function_protocol: u8,
|
function_protocol: u8,
|
||||||
) {
|
) {
|
||||||
if !self.write_iads {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.write(
|
self.write(
|
||||||
descriptor_type::IAD,
|
descriptor_type::IAD,
|
||||||
&[
|
&[
|
||||||
|
@ -177,33 +169,6 @@ impl<'a> DescriptorWriter<'a> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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,
|
|
||||||
) {
|
|
||||||
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
|
/// Writes a interface descriptor with a specific alternate setting and
|
||||||
/// interface string identifier.
|
/// interface string identifier.
|
||||||
///
|
///
|
||||||
|
|
|
@ -19,8 +19,8 @@ use self::descriptor::*;
|
||||||
use self::driver::{Bus, Driver, Event};
|
use self::driver::{Bus, Driver, Event};
|
||||||
use self::types::*;
|
use self::types::*;
|
||||||
|
|
||||||
|
pub use self::builder::Builder;
|
||||||
pub use self::builder::Config;
|
pub use self::builder::Config;
|
||||||
pub use self::builder::UsbDeviceBuilder;
|
|
||||||
|
|
||||||
/// The global state of the USB device.
|
/// The global state of the USB device.
|
||||||
///
|
///
|
||||||
|
|
|
@ -17,7 +17,7 @@ use embassy_nrf::pac;
|
||||||
use embassy_nrf::usb::Driver;
|
use embassy_nrf::usb::Driver;
|
||||||
use embassy_nrf::Peripherals;
|
use embassy_nrf::Peripherals;
|
||||||
use embassy_usb::control::OutResponse;
|
use embassy_usb::control::OutResponse;
|
||||||
use embassy_usb::{Config, DeviceStateHandler, UsbDeviceBuilder};
|
use embassy_usb::{Builder, Config, DeviceStateHandler};
|
||||||
use embassy_usb_hid::{HidReaderWriter, ReportId, RequestHandler, State};
|
use embassy_usb_hid::{HidReaderWriter, ReportId, RequestHandler, State};
|
||||||
use futures::future::join;
|
use futures::future::join;
|
||||||
use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
|
use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
|
||||||
|
@ -77,7 +77,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
|
||||||
let mut state = State::new();
|
let mut state = State::new();
|
||||||
|
|
||||||
let mut builder = UsbDeviceBuilder::new(
|
let mut builder = Builder::new(
|
||||||
driver,
|
driver,
|
||||||
config,
|
config,
|
||||||
&mut device_descriptor,
|
&mut device_descriptor,
|
||||||
|
|
|
@ -12,7 +12,7 @@ use embassy_nrf::pac;
|
||||||
use embassy_nrf::usb::Driver;
|
use embassy_nrf::usb::Driver;
|
||||||
use embassy_nrf::Peripherals;
|
use embassy_nrf::Peripherals;
|
||||||
use embassy_usb::control::OutResponse;
|
use embassy_usb::control::OutResponse;
|
||||||
use embassy_usb::{Config, UsbDeviceBuilder};
|
use embassy_usb::{Builder, Config};
|
||||||
use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State};
|
use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State};
|
||||||
use futures::future::join;
|
use futures::future::join;
|
||||||
use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
|
use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
|
||||||
|
@ -54,7 +54,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
|
||||||
let mut state = State::new();
|
let mut state = State::new();
|
||||||
|
|
||||||
let mut builder = UsbDeviceBuilder::new(
|
let mut builder = Builder::new(
|
||||||
driver,
|
driver,
|
||||||
config,
|
config,
|
||||||
&mut device_descriptor,
|
&mut device_descriptor,
|
||||||
|
|
|
@ -11,7 +11,7 @@ use embassy_nrf::pac;
|
||||||
use embassy_nrf::usb::{Driver, Instance};
|
use embassy_nrf::usb::{Driver, Instance};
|
||||||
use embassy_nrf::Peripherals;
|
use embassy_nrf::Peripherals;
|
||||||
use embassy_usb::driver::EndpointError;
|
use embassy_usb::driver::EndpointError;
|
||||||
use embassy_usb::{Config, UsbDeviceBuilder};
|
use embassy_usb::{Builder, Config};
|
||||||
use embassy_usb_serial::{CdcAcmClass, State};
|
use embassy_usb_serial::{CdcAcmClass, State};
|
||||||
use futures::future::join;
|
use futures::future::join;
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
|
||||||
let mut state = State::new();
|
let mut state = State::new();
|
||||||
|
|
||||||
let mut builder = UsbDeviceBuilder::new(
|
let mut builder = Builder::new(
|
||||||
driver,
|
driver,
|
||||||
config,
|
config,
|
||||||
&mut device_descriptor,
|
&mut device_descriptor,
|
||||||
|
|
|
@ -12,7 +12,7 @@ use embassy_nrf::usb::Driver;
|
||||||
use embassy_nrf::Peripherals;
|
use embassy_nrf::Peripherals;
|
||||||
use embassy_nrf::{interrupt, peripherals};
|
use embassy_nrf::{interrupt, peripherals};
|
||||||
use embassy_usb::driver::EndpointError;
|
use embassy_usb::driver::EndpointError;
|
||||||
use embassy_usb::{Config, UsbDevice, UsbDeviceBuilder};
|
use embassy_usb::{Builder, Config, UsbDevice};
|
||||||
use embassy_usb_serial::{CdcAcmClass, State};
|
use embassy_usb_serial::{CdcAcmClass, State};
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
use defmt_rtt as _; // global logger
|
||||||
|
@ -72,7 +72,7 @@ async fn main(spawner: Spawner, p: Peripherals) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create embassy-usb DeviceBuilder using the driver and config.
|
// Create embassy-usb DeviceBuilder using the driver and config.
|
||||||
let mut builder = UsbDeviceBuilder::new(
|
let mut builder = Builder::new(
|
||||||
driver,
|
driver,
|
||||||
config,
|
config,
|
||||||
&mut res.device_descriptor,
|
&mut res.device_descriptor,
|
||||||
|
|
Loading…
Reference in a new issue