mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-10-29 21:30:54 +00:00
feat(usb): make use of ISO endpoint support
This commit is contained in:
parent
d37c482e21
commit
a8ca6713e6
@ -1,8 +1,8 @@
|
||||
use heapless::Vec;
|
||||
|
||||
use crate::config::MAX_HANDLER_COUNT;
|
||||
use crate::descriptor::{BosWriter, DescriptorWriter};
|
||||
use crate::driver::{Driver, Endpoint, EndpointType};
|
||||
use crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageType};
|
||||
use crate::driver::{Driver, Endpoint, EndpointInfo, EndpointType};
|
||||
use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
|
||||
use crate::types::{InterfaceNumber, StringIndex};
|
||||
use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
|
||||
@ -414,7 +414,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
||||
/// 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);
|
||||
self.builder.config_descriptor.write(descriptor_type, descriptor, &[]);
|
||||
}
|
||||
|
||||
/// Add a custom Binary Object Store (BOS) descriptor to this alternate setting.
|
||||
@ -422,26 +422,80 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
||||
self.builder.bos_descriptor.capability(capability_type, capability);
|
||||
}
|
||||
|
||||
fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
|
||||
/// Write a custom endpoint descriptor for a certain endpoint.
|
||||
///
|
||||
/// This can be necessary, if the endpoint descriptors can only be written
|
||||
/// after the endpoint was created. As an example, an endpoint descriptor
|
||||
/// may contain the address of an endpoint that was allocated earlier.
|
||||
pub fn endpoint_descriptor(
|
||||
&mut self,
|
||||
endpoint: &EndpointInfo,
|
||||
synchronization_type: SynchronizationType,
|
||||
usage_type: UsageType,
|
||||
extra_fields: &[u8],
|
||||
) {
|
||||
self.builder
|
||||
.config_descriptor
|
||||
.endpoint(endpoint, synchronization_type, usage_type, extra_fields);
|
||||
}
|
||||
|
||||
/// Allocate an IN endpoint, without writing its descriptor.
|
||||
///
|
||||
/// Used for granular control over the order of endpoint and descriptor creation.
|
||||
pub fn alloc_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_in(
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval_ms: u8,
|
||||
synchronization_type: SynchronizationType,
|
||||
usage_type: UsageType,
|
||||
extra_fields: &[u8],
|
||||
) -> D::EndpointIn {
|
||||
let ep = self.alloc_endpoint_in(ep_type, max_packet_size, interval_ms);
|
||||
self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);
|
||||
|
||||
ep
|
||||
}
|
||||
|
||||
fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
|
||||
/// Allocate an OUT endpoint, without writing its descriptor.
|
||||
///
|
||||
/// Use for granular control over the order of endpoint and descriptor creation.
|
||||
pub fn alloc_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
|
||||
}
|
||||
|
||||
fn endpoint_out(
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval_ms: u8,
|
||||
synchronization_type: SynchronizationType,
|
||||
usage_type: UsageType,
|
||||
extra_fields: &[u8],
|
||||
) -> D::EndpointOut {
|
||||
let ep = self.alloc_endpoint_out(ep_type, max_packet_size, interval_ms);
|
||||
self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);
|
||||
|
||||
ep
|
||||
}
|
||||
@ -451,7 +505,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
||||
/// 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)
|
||||
self.endpoint_in(
|
||||
EndpointType::Bulk,
|
||||
max_packet_size,
|
||||
0,
|
||||
SynchronizationType::NoSynchronization,
|
||||
UsageType::DataEndpoint,
|
||||
&[],
|
||||
)
|
||||
}
|
||||
|
||||
/// Allocate a BULK OUT endpoint and write its descriptor.
|
||||
@ -459,7 +520,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
||||
/// 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)
|
||||
self.endpoint_out(
|
||||
EndpointType::Bulk,
|
||||
max_packet_size,
|
||||
0,
|
||||
SynchronizationType::NoSynchronization,
|
||||
UsageType::DataEndpoint,
|
||||
&[],
|
||||
)
|
||||
}
|
||||
|
||||
/// Allocate a INTERRUPT IN endpoint and write its descriptor.
|
||||
@ -467,24 +535,66 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
||||
/// 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)
|
||||
self.endpoint_in(
|
||||
EndpointType::Interrupt,
|
||||
max_packet_size,
|
||||
interval_ms,
|
||||
SynchronizationType::NoSynchronization,
|
||||
UsageType::DataEndpoint,
|
||||
&[],
|
||||
)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
self.endpoint_out(
|
||||
EndpointType::Interrupt,
|
||||
max_packet_size,
|
||||
interval_ms,
|
||||
SynchronizationType::NoSynchronization,
|
||||
UsageType::DataEndpoint,
|
||||
&[],
|
||||
)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
pub fn endpoint_isochronous_in(
|
||||
&mut self,
|
||||
max_packet_size: u16,
|
||||
interval_ms: u8,
|
||||
synchronization_type: SynchronizationType,
|
||||
usage_type: UsageType,
|
||||
extra_fields: &[u8],
|
||||
) -> D::EndpointIn {
|
||||
self.endpoint_in(
|
||||
EndpointType::Isochronous,
|
||||
max_packet_size,
|
||||
interval_ms,
|
||||
synchronization_type,
|
||||
usage_type,
|
||||
extra_fields,
|
||||
)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
pub fn endpoint_isochronous_out(
|
||||
&mut self,
|
||||
max_packet_size: u16,
|
||||
interval_ms: u8,
|
||||
synchronization_type: SynchronizationType,
|
||||
usage_type: UsageType,
|
||||
extra_fields: &[u8],
|
||||
) -> D::EndpointOut {
|
||||
self.endpoint_out(
|
||||
EndpointType::Isochronous,
|
||||
max_packet_size,
|
||||
interval_ms,
|
||||
synchronization_type,
|
||||
usage_type,
|
||||
extra_fields,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
//! Utilities for writing USB descriptors.
|
||||
use embassy_usb_driver::EndpointType;
|
||||
|
||||
use crate::builder::Config;
|
||||
use crate::driver::EndpointInfo;
|
||||
@ -38,6 +39,40 @@ pub mod capability_type {
|
||||
pub const PLATFORM: u8 = 5;
|
||||
}
|
||||
|
||||
/// USB endpoint synchronization type. The values of this enum can be directly
|
||||
/// cast into `u8` to get the bmAttributes synchronization type bits.
|
||||
/// Values other than `NoSynchronization` are only allowed on isochronous endpoints.
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum SynchronizationType {
|
||||
/// No synchronization is used.
|
||||
NoSynchronization = 0b00,
|
||||
/// Unsynchronized, although sinks provide data rate feedback.
|
||||
Asynchronous = 0b01,
|
||||
/// Synchronized using feedback or feedforward data rate information.
|
||||
Adaptive = 0b10,
|
||||
/// Synchronized to the USB’s SOF.
|
||||
Synchronous = 0b11,
|
||||
}
|
||||
|
||||
/// USB endpoint usage type. The values of this enum can be directly cast into
|
||||
/// `u8` to get the bmAttributes usage type bits.
|
||||
/// Values other than `DataEndpoint` are only allowed on isochronous endpoints.
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum UsageType {
|
||||
/// Use the endpoint for regular data transfer.
|
||||
DataEndpoint = 0b00,
|
||||
/// Endpoint conveys explicit feedback information for one or more data endpoints.
|
||||
FeedbackEndpoint = 0b01,
|
||||
/// A data endpoint that also serves as an implicit feedback endpoint for one or more data endpoints.
|
||||
ImplicitFeedbackDataEndpoint = 0b10,
|
||||
/// Reserved usage type.
|
||||
Reserved = 0b11,
|
||||
}
|
||||
|
||||
/// A writer for USB descriptors.
|
||||
pub(crate) struct DescriptorWriter<'a> {
|
||||
pub buf: &'a mut [u8],
|
||||
@ -65,23 +100,26 @@ impl<'a> DescriptorWriter<'a> {
|
||||
self.position
|
||||
}
|
||||
|
||||
/// Writes an arbitrary (usually class-specific) descriptor.
|
||||
pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8]) {
|
||||
let length = descriptor.len();
|
||||
/// Writes an arbitrary (usually class-specific) descriptor with optional extra fields.
|
||||
pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8], extra_fields: &[u8]) {
|
||||
let descriptor_length = descriptor.len();
|
||||
let extra_fields_length = extra_fields.len();
|
||||
let total_length = descriptor_length + extra_fields_length;
|
||||
|
||||
assert!(
|
||||
(self.position + 2 + length) <= self.buf.len() && (length + 2) <= 255,
|
||||
(self.position + 2 + total_length) <= self.buf.len() && (total_length + 2) <= 255,
|
||||
"Descriptor buffer full"
|
||||
);
|
||||
|
||||
self.buf[self.position] = (length + 2) as u8;
|
||||
self.buf[self.position] = (total_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.buf[start..start + descriptor_length].copy_from_slice(descriptor);
|
||||
self.buf[start + descriptor_length..start + total_length].copy_from_slice(extra_fields);
|
||||
|
||||
self.position = start + length;
|
||||
self.position = start + total_length;
|
||||
}
|
||||
|
||||
pub(crate) fn configuration(&mut self, config: &Config) {
|
||||
@ -99,6 +137,7 @@ impl<'a> DescriptorWriter<'a> {
|
||||
| if config.supports_remote_wakeup { 0x20 } else { 0x00 }, // bmAttributes
|
||||
(config.max_power / 2) as u8, // bMaxPower
|
||||
],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
@ -145,6 +184,7 @@ impl<'a> DescriptorWriter<'a> {
|
||||
function_protocol,
|
||||
0,
|
||||
],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
@ -195,6 +235,7 @@ impl<'a> DescriptorWriter<'a> {
|
||||
interface_protocol, // bInterfaceProtocol
|
||||
str_index, // iInterface
|
||||
],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
@ -204,21 +245,50 @@ impl<'a> DescriptorWriter<'a> {
|
||||
///
|
||||
/// * `endpoint` - Endpoint previously allocated with
|
||||
/// [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder).
|
||||
pub fn endpoint(&mut self, endpoint: &EndpointInfo) {
|
||||
/// * `synchronization_type` - The synchronization type of the endpoint.
|
||||
/// * `usage_type` - The usage type of the endpoint.
|
||||
/// * `extra_fields` - Additional, class-specific entries at the end of the endpoint descriptor.
|
||||
pub fn endpoint(
|
||||
&mut self,
|
||||
endpoint: &EndpointInfo,
|
||||
synchronization_type: SynchronizationType,
|
||||
usage_type: UsageType,
|
||||
extra_fields: &[u8],
|
||||
) {
|
||||
match self.num_endpoints_mark {
|
||||
Some(mark) => self.buf[mark] += 1,
|
||||
None => panic!("you can only call `endpoint` after `interface/interface_alt`."),
|
||||
};
|
||||
|
||||
let mut bm_attributes = endpoint.ep_type as u8;
|
||||
|
||||
// Synchronization types other than `NoSynchronization`,
|
||||
// and usage types other than `DataEndpoint`
|
||||
// are only allowed for isochronous endpoints.
|
||||
if endpoint.ep_type != EndpointType::Isochronous {
|
||||
assert_eq!(synchronization_type, SynchronizationType::NoSynchronization);
|
||||
assert_eq!(usage_type, UsageType::DataEndpoint);
|
||||
} else {
|
||||
if usage_type == UsageType::FeedbackEndpoint {
|
||||
assert_eq!(synchronization_type, SynchronizationType::NoSynchronization)
|
||||
}
|
||||
|
||||
let synchronization_bm_attibutes: u8 = (synchronization_type as u8) << 2;
|
||||
let usage_bm_attibutes: u8 = (usage_type as u8) << 4;
|
||||
|
||||
bm_attributes |= usage_bm_attibutes | synchronization_bm_attibutes;
|
||||
}
|
||||
|
||||
self.write(
|
||||
descriptor_type::ENDPOINT,
|
||||
&[
|
||||
endpoint.addr.into(), // bEndpointAddress
|
||||
endpoint.ep_type as u8, // bmAttributes
|
||||
endpoint.addr.into(), // bEndpointAddress
|
||||
bm_attributes, // bmAttributes
|
||||
endpoint.max_packet_size as u8,
|
||||
(endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize
|
||||
endpoint.interval_ms, // bInterval
|
||||
],
|
||||
extra_fields,
|
||||
);
|
||||
}
|
||||
|
||||
@ -315,6 +385,7 @@ impl<'a> BosWriter<'a> {
|
||||
0x00, 0x00, // wTotalLength
|
||||
0x00, // bNumDeviceCaps
|
||||
],
|
||||
&[],
|
||||
);
|
||||
|
||||
self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4]);
|
||||
|
Loading…
Reference in New Issue
Block a user