Skip to content

Commit

Permalink
feat(usb): Prepare for USB Audio
Browse files Browse the repository at this point in the history
Allow separate endpoint/descriptor creation
Add synchronization and usage type attributes to isochronous endpoints
Fix isochronous endpoint even/odd frame numbers
Fix inconsistent register setter naming
  • Loading branch information
elagil committed Aug 28, 2024
1 parent b88dc13 commit d0b1fb4
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 33 deletions.
30 changes: 30 additions & 0 deletions embassy-usb-synopsys-otg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,21 @@ impl<'d> embassy_usb_driver::EndpointOut for Endpoint<'d, Out> {
w.set_pktcnt(1);
});

if self.info.ep_type == EndpointType::Isochronous {
// Isochronous endpoints must set the correct even/odd frame bit to
// correspond with the next frame's number.
let frame_number = self.regs.dsts().read().fnsof();
let frame_is_odd = frame_number & 0x01 == 1;

self.regs.doepctl(index).modify(|r| {
if frame_is_odd {
r.set_sd0pid_sevnfrm(true);
} else {
r.set_sd1pid_soddfrm(true);
}
});
}

// Clear NAK to indicate we are ready to receive more data
self.regs.doepctl(index).modify(|w| {
w.set_cnak(true);
Expand Down Expand Up @@ -1155,6 +1170,21 @@ impl<'d> embassy_usb_driver::EndpointIn for Endpoint<'d, In> {
w.set_xfrsiz(buf.len() as _);
});

if self.info.ep_type == EndpointType::Isochronous {
// Isochronous endpoints must set the correct even/odd frame bit to
// correspond with the next frame's number.
let frame_number = self.regs.dsts().read().fnsof();
let frame_is_odd = frame_number & 0x01 == 1;

self.regs.diepctl(index).modify(|r| {
if frame_is_odd {
r.set_sd0pid_sevnfrm(true);
} else {
r.set_sd1pid_soddfrm(true);
}
});
}

// Enable endpoint
self.regs.diepctl(index).modify(|w| {
w.set_cnak(true);
Expand Down
16 changes: 8 additions & 8 deletions embassy-usb-synopsys-otg/src/otg_v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -783,15 +783,15 @@ pub mod regs {
pub fn set_sd0pid_sevnfrm(&mut self, val: bool) {
self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize);
}
#[doc = "SODDFRM/SD1PID"]
#[doc = "SD1PID/SODDFRM"]
#[inline(always)]
pub const fn soddfrm_sd1pid(&self) -> bool {
pub const fn sd1pid_soddfrm(&self) -> bool {
let val = (self.0 >> 29usize) & 0x01;
val != 0
}
#[doc = "SODDFRM/SD1PID"]
#[doc = "SD1PID/SODDFRM"]
#[inline(always)]
pub fn set_soddfrm_sd1pid(&mut self, val: bool) {
pub fn set_sd1pid_soddfrm(&mut self, val: bool) {
self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize);
}
#[doc = "EPDIS"]
Expand Down Expand Up @@ -1162,15 +1162,15 @@ pub mod regs {
pub fn set_sd0pid_sevnfrm(&mut self, val: bool) {
self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize);
}
#[doc = "SODDFRM"]
#[doc = "SD1PID/SODDFRM"]
#[inline(always)]
pub const fn soddfrm(&self) -> bool {
pub const fn sd1pid_soddfrm(&self) -> bool {
let val = (self.0 >> 29usize) & 0x01;
val != 0
}
#[doc = "SODDFRM"]
#[doc = "SD1PID/SODDFRM"]
#[inline(always)]
pub fn set_soddfrm(&mut self, val: bool) {
pub fn set_sd1pid_soddfrm(&mut self, val: bool) {
self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize);
}
#[doc = "EPDIS"]
Expand Down
140 changes: 125 additions & 15 deletions embassy-usb/src/builder.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -414,34 +414,88 @@ 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.
pub fn bos_capability(&mut self, capability_type: u8, capability: &[u8]) {
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
}
Expand All @@ -451,40 +505,96 @@ 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.
///
/// 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.
///
/// 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,
)
}
}
Loading

0 comments on commit d0b1fb4

Please sign in to comment.