Skip to content

Commit 46a354c

Browse files
authored
Merge pull request #60 from ianrrees/isochronous
Add enumerations and allocator for isochronous endpoints
2 parents d0b201e + ddfa116 commit 46a354c

File tree

7 files changed

+160
-14
lines changed

7 files changed

+160
-14
lines changed

CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

88
## [Unreleased]
9+
### Added
10+
* New enums and allocators for Isochronous endpoints
911

10-
...
12+
### Changed
13+
* `EndpointType` enum now has fields for isochronous synchronization and usage.
1114

1215
## [0.2.9] - 2022-08-02
1316

src/bus.rs

+40-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::endpoint::{Endpoint, EndpointAddress, EndpointDirection, EndpointType};
1+
use crate::endpoint::{
2+
Endpoint, EndpointAddress, EndpointDirection, EndpointType, IsochronousSynchronizationType,
3+
IsochronousUsageType,
4+
};
25
use crate::{Result, UsbDirection, UsbError};
36
use core::cell::RefCell;
47
use core::mem;
@@ -244,6 +247,41 @@ impl<B: UsbBus> UsbBusAllocator<B> {
244247
.expect("alloc_ep failed")
245248
}
246249

250+
/// Allocates an isochronous endpoint.
251+
///
252+
/// # Arguments
253+
///
254+
/// * `synchronization` - Type of synchronization used by the endpoint
255+
/// * `usage` - Whether the endpoint is data, explicit feedback, or data+implicit feedback
256+
/// * `payload_size` - Payload size in bytes.
257+
/// * `interval` - Interval for polling, expressed in frames/microframes.
258+
///
259+
/// See USB 2.0 section 9.6.6.
260+
///
261+
/// # Panics
262+
///
263+
/// Panics if endpoint allocation fails, because running out of endpoints or memory is not
264+
/// feasibly recoverable.
265+
#[inline]
266+
pub fn isochronous<D: EndpointDirection>(
267+
&self,
268+
synchronization: IsochronousSynchronizationType,
269+
usage: IsochronousUsageType,
270+
payload_size: u16,
271+
interval: u8,
272+
) -> Endpoint<'_, B, D> {
273+
self.alloc(
274+
None,
275+
EndpointType::Isochronous {
276+
synchronization,
277+
usage,
278+
},
279+
payload_size,
280+
interval,
281+
)
282+
.expect("alloc_ep failed")
283+
}
284+
247285
/// Allocates a bulk endpoint.
248286
///
249287
/// # Arguments
@@ -263,6 +301,7 @@ impl<B: UsbBus> UsbBusAllocator<B> {
263301
/// Allocates an interrupt endpoint.
264302
///
265303
/// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes.
304+
/// * `interval` - Polling interval.
266305
///
267306
/// # Panics
268307
///

src/descriptor.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ impl DescriptorWriter<'_> {
324324
let mps = endpoint.max_packet_size();
325325

326326
buf[0] = endpoint.address().into();
327-
buf[1] = endpoint.ep_type() as u8;
327+
buf[1] = endpoint.ep_type().to_bm_attributes();
328328
buf[2] = mps as u8;
329329
buf[3] = (mps >> 8) as u8;
330330
buf[4] = endpoint.interval();

src/endpoint.rs

+68-8
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,81 @@ pub type EndpointOut<'a, B> = Endpoint<'a, B, Out>;
2929
/// A device-to-host (IN) endpoint.
3030
pub type EndpointIn<'a, B> = Endpoint<'a, B, In>;
3131

32-
/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the
33-
/// transfer bmAttributes transfer type bits.
34-
#[repr(u8)]
32+
/// Isochronous transfers employ one of three synchronization schemes. See USB 2.0 spec 5.12.4.1.
33+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
34+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35+
pub enum IsochronousSynchronizationType {
36+
/// Synchronization is not implemented for this endpoint.
37+
NoSynchronization,
38+
/// Source and Sink sample clocks are free running.
39+
Asynchronous,
40+
/// Source sample clock is locked to Sink, Sink sample clock is locked to data flow.
41+
Adaptive,
42+
/// Source and Sink sample clocks are locked to USB SOF.
43+
Synchronous,
44+
}
45+
46+
/// Intended use of an isochronous endpoint, see USB 2.0 spec sections 5.12 and 9.6.6.
47+
/// Associations between data and feedback endpoints are described in section 9.6.6.
48+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
49+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
50+
pub enum IsochronousUsageType {
51+
/// Endpoint is used for isochronous data.
52+
Data,
53+
/// Feedback for synchronization.
54+
Feedback,
55+
/// Endpoint is data and provides implicit feedback for synchronization.
56+
ImplicitFeedbackData,
57+
}
58+
59+
/// USB endpoint transfer type.
3560
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
3661
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
3762
pub enum EndpointType {
3863
/// Control endpoint. Used for device management. Only the host can initiate requests. Usually
3964
/// used only endpoint 0.
40-
Control = 0b00,
41-
/// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet.
42-
Isochronous = 0b01,
65+
Control,
66+
/// Isochronous endpoint. Used for time-critical unreliable data.
67+
///
68+
/// See USB 2.0 spec section 5.12 "Special Considerations for Isochronous Transfers"
69+
Isochronous {
70+
/// Synchronization model used for the data stream that this endpoint relates to.
71+
synchronization: IsochronousSynchronizationType,
72+
/// Endpoint's role in the synchronization model selected by [Self::Isochronous::synchronization].
73+
usage: IsochronousUsageType,
74+
},
4375
/// Bulk endpoint. Used for large amounts of best-effort reliable data.
44-
Bulk = 0b10,
76+
Bulk,
4577
/// Interrupt endpoint. Used for small amounts of time-critical reliable data.
46-
Interrupt = 0b11,
78+
Interrupt,
79+
}
80+
81+
impl EndpointType {
82+
/// Format EndpointType for use in bmAttributes transfer type field USB 2.0 spec section 9.6.6
83+
pub fn to_bm_attributes(&self) -> u8 {
84+
match self {
85+
EndpointType::Control => 0b00,
86+
EndpointType::Isochronous {
87+
synchronization,
88+
usage,
89+
} => {
90+
let sync_bits = match synchronization {
91+
IsochronousSynchronizationType::NoSynchronization => 0b00,
92+
IsochronousSynchronizationType::Asynchronous => 0b01,
93+
IsochronousSynchronizationType::Adaptive => 0b10,
94+
IsochronousSynchronizationType::Synchronous => 0b11,
95+
};
96+
let usage_bits = match usage {
97+
IsochronousUsageType::Data => 0b00,
98+
IsochronousUsageType::Feedback => 0b01,
99+
IsochronousUsageType::ImplicitFeedbackData => 0b10,
100+
};
101+
(usage_bits << 4) | (sync_bits << 2) | 0b01
102+
}
103+
EndpointType::Bulk => 0b10,
104+
EndpointType::Interrupt => 0b11,
105+
}
106+
}
47107
}
48108

49109
/// Handle for a USB endpoint. The endpoint direction is constrained by the `D` type argument, which

src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,10 @@ pub mod class_prelude {
190190
pub use crate::class::{ControlIn, ControlOut, UsbClass};
191191
pub use crate::control;
192192
pub use crate::descriptor::{BosWriter, DescriptorWriter};
193-
pub use crate::endpoint::{EndpointAddress, EndpointIn, EndpointOut, EndpointType};
193+
pub use crate::endpoint::{
194+
EndpointAddress, EndpointIn, EndpointOut, EndpointType, IsochronousSynchronizationType,
195+
IsochronousUsageType,
196+
};
194197
pub use crate::UsbError;
195198
}
196199

src/test_class.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub struct TestClass<'a, B: UsbBus> {
3232
ep_bulk_out: EndpointOut<'a, B>,
3333
ep_interrupt_in: EndpointIn<'a, B>,
3434
ep_interrupt_out: EndpointOut<'a, B>,
35+
ep_iso_in: EndpointIn<'a, B>,
3536
control_buf: [u8; sizes::BUFFER],
3637
bulk_buf: [u8; sizes::BUFFER],
3738
interrupt_buf: [u8; sizes::BUFFER],
@@ -72,6 +73,12 @@ impl<B: UsbBus> TestClass<'_, B> {
7273
ep_bulk_out: alloc.bulk(sizes::BULK_ENDPOINT),
7374
ep_interrupt_in: alloc.interrupt(sizes::INTERRUPT_ENDPOINT, 1),
7475
ep_interrupt_out: alloc.interrupt(sizes::INTERRUPT_ENDPOINT, 1),
76+
ep_iso_in: alloc.isochronous(
77+
IsochronousSynchronizationType::Asynchronous,
78+
IsochronousUsageType::ImplicitFeedbackData,
79+
500, // These last two args are arbitrary in this usage, they
80+
1, // let the host know how much bandwidth to reserve.
81+
),
7582
control_buf: [0; sizes::BUFFER],
7683
bulk_buf: [0; sizes::BUFFER],
7784
interrupt_buf: [0; sizes::BUFFER],
@@ -218,7 +225,7 @@ impl<B: UsbBus> UsbClass<B> for TestClass<'_, B> {
218225
writer.endpoint(&self.ep_interrupt_in)?;
219226
writer.endpoint(&self.ep_interrupt_out)?;
220227
writer.interface_alt(self.iface, 1, 0xff, 0x01, 0x00, Some(self.interface_string))?;
221-
228+
writer.endpoint(&self.ep_iso_in)?;
222229
Ok(())
223230
}
224231

tests/test_class_host/tests.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::device::*;
22
use rand::prelude::*;
3-
use rusb::{request_type, Direction, Recipient, RequestType};
3+
use rusb::{request_type, Direction, Recipient, RequestType, TransferType};
44
use std::cmp::max;
55
use std::fmt::Write;
66
use std::time::{Duration, Instant};
@@ -163,6 +163,40 @@ fn interface_descriptor(dev, _out) {
163163
test_class::INTERFACE_STRING);
164164
}
165165

166+
fn iso_endpoint_descriptors(dev, _out) {
167+
// Tests that an isochronous endpoint descriptor is present in the first
168+
// alternate setting, but not in the default setting.
169+
let iface = dev.config_descriptor
170+
.interfaces()
171+
.find(|i| i.number() == 0)
172+
.expect("interface not found");
173+
174+
let mut iso_ep_count = 0;
175+
for iface_descriptor in iface.descriptors() {
176+
if iface_descriptor.setting_number() == 0 {
177+
// Default setting - no isochronous endpoints allowed. Per USB 2.0
178+
// spec rev 2.0, 5.6.3 Isochronous Transfer Packet Size Constraints:
179+
//
180+
// All device default interface settings must not include any
181+
// isochronous endpoints with non-zero data payload sizes (specified
182+
// via wMaxPacketSize in the endpoint descriptor)
183+
let issue = iface_descriptor
184+
.endpoint_descriptors()
185+
.find(|ep| ep.transfer_type() == TransferType::Isochronous
186+
&& ep.max_packet_size() != 0);
187+
if let Some(ep) = issue {
188+
panic!("Endpoint {} is isochronous and in the default setting",
189+
ep.number());
190+
}
191+
} else {
192+
iso_ep_count += iface_descriptor.endpoint_descriptors()
193+
.filter(|ep| ep.transfer_type() == TransferType::Isochronous)
194+
.count();
195+
}
196+
}
197+
assert!(iso_ep_count > 0, "At least one isochronous endpoint is expected");
198+
}
199+
166200
fn bulk_loopback(dev, _out) {
167201
let mut lens = vec![0, 1, 2, 32, 63, 64, 65, 127, 128, 129];
168202
if dev.is_high_speed() {

0 commit comments

Comments
 (0)