embassy/embassy-usb/src/builder.rs
2023-03-27 18:21:11 +02:00

507 lines
17 KiB
Rust

use heapless::Vec;
use crate::config::*;
use crate::descriptor::{BosWriter, DescriptorWriter};
use crate::driver::{Driver, Endpoint, EndpointType};
#[cfg(feature = "msos-descriptor")]
use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
use crate::types::*;
use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
/// Configuration used when creating [UsbDevice].
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> {
/// Create default configuration with the provided vid and pid values.
pub fn new(vid: u16, pid: u16) -> Self {
Self {
device_class: 0x00,
device_sub_class: 0x00,
device_protocol: 0x00,
max_packet_size_0: 64,
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,
}
}
}
/// [`UsbDevice`] builder.
pub struct Builder<'d, D: Driver<'d>> {
config: Config<'d>,
handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,
interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
control_buf: &'d mut [u8],
driver: D,
next_string_index: u8,
device_descriptor: DescriptorWriter<'d>,
config_descriptor: DescriptorWriter<'d>,
bos_descriptor: BosWriter<'d>,
#[cfg(feature = "msos-descriptor")]
msos_descriptor: MsOsDescriptorWriter<'d>,
}
impl<'d, D: Driver<'d>> Builder<'d, D> {
/// Creates a builder for constructing a new [`UsbDevice`].
///
/// `control_buf` is a buffer used for USB control request data. It should be sized
/// large enough for the length of the largest control request (in or out)
/// anticipated by any class added to the device.
pub fn new(
driver: D,
config: Config<'d>,
device_descriptor_buf: &'d mut [u8],
config_descriptor_buf: &'d mut [u8],
bos_descriptor_buf: &'d mut [u8],
#[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8],
control_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);
config_descriptor.configuration(&config);
bos_descriptor.bos();
Builder {
driver,
config,
interfaces: Vec::new(),
handlers: Vec::new(),
control_buf,
next_string_index: STRING_INDEX_CUSTOM_START,
device_descriptor,
config_descriptor,
bos_descriptor,
#[cfg(feature = "msos-descriptor")]
msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf),
}
}
/// Creates the [`UsbDevice`] instance with the configuration in this builder.
pub fn build(mut self) -> UsbDevice<'d, D> {
#[cfg(feature = "msos-descriptor")]
let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor);
self.config_descriptor.end_configuration();
self.bos_descriptor.end_bos();
// Log the number of allocator bytes actually used in descriptor buffers
info!("USB: device_descriptor used: {}", self.device_descriptor.position());
info!("USB: config_descriptor used: {}", self.config_descriptor.position());
info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position());
#[cfg(feature = "msos-descriptor")]
info!("USB: msos_descriptor used: {}", msos_descriptor.len());
info!("USB: control_buf size: {}", self.control_buf.len());
UsbDevice::build(
self.driver,
self.config,
self.handlers,
self.device_descriptor.into_buf(),
self.config_descriptor.into_buf(),
self.bos_descriptor.writer.into_buf(),
self.interfaces,
self.control_buf,
#[cfg(feature = "msos-descriptor")]
msos_descriptor,
)
}
/// Returns the size of the control request data buffer. Can be used by
/// classes to validate the buffer is large enough for their needs.
pub fn control_buf_len(&self) -> usize {
self.control_buf.len()
}
/// Add an USB function.
///
/// If [`Config::composite_with_iads`] is set, this will add an IAD descriptor
/// with the given class/subclass/protocol, associating all the child interfaces.
///
/// 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 first_interface = InterfaceNumber::new(self.interfaces.len() as u8);
let iface_count_index = if self.config.composite_with_iads {
self.config_descriptor
.iad(first_interface, 0, class, subclass, protocol);
Some(self.config_descriptor.position() - 5)
} else {
None
};
FunctionBuilder {
builder: self,
iface_count_index,
#[cfg(feature = "msos-descriptor")]
first_interface,
}
}
/// Add a Handler.
///
/// The Handler is called on some USB bus events, and to handle all control requests not already
/// handled by the USB stack.
pub fn handler(&mut self, handler: &'d mut dyn Handler) {
if self.handlers.push(handler).is_err() {
panic!(
"embassy-usb: handler list full. Increase the `max_handler_count` compile-time setting. Current value: {}",
MAX_HANDLER_COUNT
)
}
}
/// Allocates a new string index.
pub fn string(&mut self) -> StringIndex {
let index = self.next_string_index;
self.next_string_index += 1;
StringIndex::new(index)
}
#[cfg(feature = "msos-descriptor")]
/// Add an MS OS 2.0 Descriptor Set.
///
/// Panics if called more than once.
pub fn msos_descriptor(&mut self, windows_version: u32, vendor_code: u8) {
self.msos_descriptor.header(windows_version, vendor_code);
}
#[cfg(feature = "msos-descriptor")]
/// Add an MS OS 2.0 Device Level Feature Descriptor.
pub fn msos_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {
self.msos_descriptor.device_feature(desc);
}
#[cfg(feature = "msos-descriptor")]
/// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that
/// do not add their own.
pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> {
&mut self.msos_descriptor
}
}
/// 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>,
#[cfg(feature = "msos-descriptor")]
first_interface: InterfaceNumber,
}
impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> {
fn drop(&mut self) {
#[cfg(feature = "msos-descriptor")]
self.builder.msos_descriptor.end_function();
}
}
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) -> InterfaceBuilder<'_, 'd, D> {
if let Some(i) = self.iface_count_index {
self.builder.config_descriptor.buf[i] += 1;
}
let number = self.builder.interfaces.len() as _;
let iface = Interface {
current_alt_setting: 0,
num_alt_settings: 0,
};
if self.builder.interfaces.push(iface).is_err() {
panic!(
"embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}",
MAX_INTERFACE_COUNT
)
}
InterfaceBuilder {
builder: self.builder,
interface_number: InterfaceNumber::new(number),
next_alt_setting_number: 0,
}
}
#[cfg(feature = "msos-descriptor")]
/// Add an MS OS 2.0 Function Level Feature Descriptor.
pub fn msos_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {
if !self.builder.msos_descriptor.is_in_config_subset() {
self.builder.msos_descriptor.configuration(0);
}
if !self.builder.msos_descriptor.is_in_function_subset() {
self.builder.msos_descriptor.function(self.first_interface);
}
#[cfg(feature = "msos-descriptor")]
self.builder.msos_descriptor.function_feature(desc);
}
}
/// 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
}
/// Allocates a new string index.
pub fn string(&mut self) -> StringIndex {
self.builder.string()
}
/// 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,
interface_string: Option<StringIndex>,
) -> InterfaceAltBuilder<'_, 'd, D> {
let number = self.next_alt_setting_number;
self.next_alt_setting_number += 1;
self.builder.interfaces[self.interface_number.0 as usize].num_alt_settings += 1;
self.builder.config_descriptor.interface_alt(
self.interface_number,
number,
class,
subclass,
protocol,
interface_string,
);
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, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
let ep = self
.builder
.driver
.alloc_endpoint_in(ep_type, max_packet_size, interval_ms)
.expect("alloc_endpoint_in failed");
self.builder.config_descriptor.endpoint(ep.info());
ep
}
fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
let ep = self
.builder
.driver
.alloc_endpoint_out(ep_type, max_packet_size, interval_ms)
.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(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(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_ms: u8) -> D::EndpointIn {
self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval_ms)
}
/// Allocate a INTERRUPT OUT endpoint and write its descriptor.
pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval_ms)
}
/// Allocate a ISOCHRONOUS 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_isochronous_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval_ms)
}
/// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor.
pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval_ms)
}
}