Skip to content

Commit

Permalink
Make USB device config public
Browse files Browse the repository at this point in the history
  • Loading branch information
nickray committed May 9, 2021
1 parent b6037b0 commit 7af4a9e
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "usb-device"
description = "Experimental device-side USB stack for embedded devices."
version = "0.2.8"
version = "0.2.9"
edition = "2018"
readme = "README.md"
keywords = ["no-std", "embedded", "usb"]
Expand Down
178 changes: 178 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/// A USB vendor ID and product ID pair.
pub struct UsbVidPid(pub u16, pub u16);

macro_rules! builder_fields {
( $( $(#[$meta:meta])* $name:ident: $type:ty, )* ) => {
$(
$(#[$meta])*
pub fn $name(mut self, $name: $type) -> Self {
self.$name = $name;
self
}
)*
}
}

/// A USB device configuration.
#[derive(Clone, Copy, Debug)]
pub struct Config<'a> {
/// USB device class
pub device_class: u8,
/// USB device subclass
pub device_sub_class: u8,
/// USB device protocol
pub device_protocol: u8,
/// USB control endpoint maximum packet size
pub max_packet_size_0: u8,
/// USB vendor ID
pub vendor_id: u16,
/// USB product ID
pub product_id: u16,
/// BCD encoded device release
pub device_release: u16,
/// Manufacturer string
pub manufacturer: Option<&'a str>,
/// Product string
pub product: Option<&'a str>,
/// Serial number
pub serial_number: Option<&'a str>,
/// Is device self-powered?
pub self_powered: bool,
/// Does device support remote wakeup?
pub supports_remote_wakeup: bool,
/// Is device composite with IADs?
pub composite_with_iads: bool,
/// Maximum power consumption of device
pub max_power: u8,
}

impl<'a> Config<'a> {

/// Create a USB device configuration with given vendor and product ID,
/// using defaults.
pub fn new(vid_pid: UsbVidPid) -> Self {
Self {
device_class: 0x00,
device_sub_class: 0x00,
device_protocol: 0x00,
max_packet_size_0: 8,
vendor_id: vid_pid.0,
product_id: vid_pid.1,
device_release: 0x0010,
manufacturer: None,
product: None,
serial_number: None,
self_powered: false,
supports_remote_wakeup: false,
composite_with_iads: false,
max_power: 50,
}
}

builder_fields! {
/// Sets the 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)
device_class: u8,

/// Sets the device sub-class code. Depends on class.
///
/// Default: `0x00`
device_sub_class: u8,

/// Sets the device protocol code. Depends on class and sub-class.
///
/// Default: `0x00`
device_protocol: u8,

/// Sets the device release version in BCD.
///
/// Default: `0x0010` ("0.1")
device_release: u16,

/// Sets whether the device may have an external 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`
self_powered: bool,

/// Sets whether the device supports remotely waking up the host is requested.
///
/// Default: `false`
supports_remote_wakeup: bool,
}

/// Configures the device as a composite device with interface association descriptors.
pub fn composite_with_iads(mut self) -> Self {
// Magic values specified in USB-IF ECN on IADs.
self.device_class = 0xEF;
self.device_sub_class = 0x02;
self.device_protocol = 0x01;

self.composite_with_iads = true;
self
}

/// Sets the manufacturer name string descriptor.
///
/// Default: (none)
pub fn manufacturer(mut self, manufacturer: &'a str) -> Self {
self.manufacturer = Some(manufacturer);
self
}

/// Sets the product name string descriptor.
///
/// Default: (none)
pub fn product(mut self, product: &'a str) -> Self {
self.product = Some(product);
self
}

/// Sets the serial number string descriptor.
///
/// Default: (none)
pub fn serial_number(mut self, serial_number: &'a str) -> Self {
self.serial_number = Some(serial_number);
self
}

/// Sets the 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 fn max_packet_size_0(mut self, max_packet_size_0: u8) -> Self {
match max_packet_size_0 {
8 | 16 | 32 | 64 => {}
_ => panic!("invalid max_packet_size_0"),
}

self.max_packet_size_0 = max_packet_size_0;
self
}

/// Sets the 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
pub fn max_power(mut self, max_power_ma: usize) -> Self {
if max_power_ma > 500 {
panic!("max_power is too much")
}

self.max_power = (max_power_ma / 2) as u8;
self
}
}
23 changes: 4 additions & 19 deletions src/device.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::bus::{PollResult, StringIndex, UsbBus, UsbBusAllocator};
use crate::class::{ControlIn, ControlOut, UsbClass};
pub use crate::config::{Config, UsbVidPid};
use crate::control;
use crate::control_pipe::ControlPipe;
use crate::descriptor::{descriptor_type, lang_id, BosWriter, DescriptorWriter};
pub use crate::device_builder::{UsbDeviceBuilder, UsbVidPid};
pub use crate::device_builder::UsbDeviceBuilder;
use crate::endpoint::{EndpointAddress, EndpointType};
use crate::{Result, UsbDirection};

Expand Down Expand Up @@ -40,23 +41,6 @@ pub struct UsbDevice<'a, B: UsbBus> {
pending_address: u8,
}

pub(crate) struct Config<'a> {
pub device_class: u8,
pub device_sub_class: u8,
pub device_protocol: u8,
pub max_packet_size_0: u8,
pub vendor_id: u16,
pub product_id: u16,
pub device_release: u16,
pub manufacturer: Option<&'a str>,
pub product: Option<&'a str>,
pub serial_number: Option<&'a str>,
pub self_powered: bool,
pub supports_remote_wakeup: bool,
pub composite_with_iads: bool,
pub max_power: u8,
}

/// The bConfiguration value for the not configured state.
pub const CONFIGURATION_NONE: u8 = 0;

Expand All @@ -69,7 +53,8 @@ pub const DEFAULT_ALTERNATE_SETTING: u8 = 0;
type ClassList<'a, B> = [&'a mut dyn UsbClass<B>];

impl<B: UsbBus> UsbDevice<'_, B> {
pub(crate) fn build<'a>(alloc: &'a UsbBusAllocator<B>, config: Config<'a>) -> UsbDevice<'a, B> {
/// Build USB device with given allocator and configuration.
pub fn build<'a>(alloc: &'a UsbBusAllocator<B>, config: Config<'a>) -> UsbDevice<'a, B> {
let control_out = alloc
.alloc(
Some(0x00.into()),
Expand Down
6 changes: 2 additions & 4 deletions src/device_builder.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use crate::bus::{UsbBus, UsbBusAllocator};
use crate::device::{Config, UsbDevice};

/// A USB vendor ID and product ID pair.
pub struct UsbVidPid(pub u16, pub u16);
pub use crate::config::UsbVidPid;
use crate::{config::Config, device::UsbDevice};

/// Used to build new [`UsbDevice`]s.
pub struct UsbDeviceBuilder<'a, B: UsbBus> {
Expand Down
11 changes: 7 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ pub mod bus;
/// and `write` to read and write data.
pub mod class;

/// USB device configuration.
mod config;

/// USB endpoints.
pub mod endpoint;

Expand Down Expand Up @@ -146,10 +149,10 @@ pub mod endpoint;
/// // pair. Additional builder arguments can specify parameters such as device class code or
/// // product name. If using an existing class, remember to check the class crate documentation
/// // for correct values.
/// let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x5824, 0x27dd))
/// let usb_config = UsbDeviceConfig::new(UsbVidPid(0x5824, 0x27dd))
/// .product("Serial port")
/// .device_class(usb_serial::DEVICE_CLASS)
/// .build();
/// .device_class(usb_serial::DEVICE_CLASS);
/// let mut usb_dev = UsbDevice::build(&usb_bus, usb_config);
///
/// // At this point the USB peripheral is enabled and a connected host will attempt to enumerate
/// // it.
Expand Down Expand Up @@ -178,7 +181,7 @@ mod device_builder;

/// Prelude for device implementors.
pub mod prelude {
pub use crate::device::{UsbDevice, UsbDeviceBuilder, UsbDeviceState, UsbVidPid};
pub use crate::device::{UsbDevice, Config as UsbDeviceConfig, UsbDeviceBuilder, UsbDeviceState, UsbVidPid};
pub use crate::UsbError;
}

Expand Down

0 comments on commit 7af4a9e

Please sign in to comment.