Skip to content

Commit 40c1ce9

Browse files
committed
Separate read_setup() from read()
This change was made in response to an issue found on the Microchip SAMD21, where an expected OUT Zero Length Packet (ZLP) was sometimes overwritten by a subsequent SETUP transaction. The USB spec requires that devices accept SETUP transactions, and in the SAMD parts this behaviour is implemented in hardware. To allow the control pipe implementation to distinguish between OUT data and SETUP data, the read() method was changed to return an error if SETUP data was present, and a new read_setup() was added that expects SETUP data not OUT data.
1 parent 3e20a0d commit 40c1ce9

File tree

6 files changed

+80
-8
lines changed

6 files changed

+80
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1515

1616
### Changed
1717

18+
* [breaking] Bus API now has separate `read()` and `read_setup()` methods for OUT and SETUP data.
1819
* [breaking] The control pipe is now provided in the `UsbDeviceBuilder` API to allow for user-provided control
1920
pipes. This makes it so that control pipes have configurable sizing.
2021
* Don't require UsbBus to be Sync. If a UsbBus is not Sync, it can still be used to make a UsbDevice, but that UsbDevice will not be Sync (ensuring soundness).

src/bus.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ pub trait UsbBus: Sized {
8181
/// Implementations may also return other errors if applicable.
8282
fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> Result<usize>;
8383

84-
/// Reads a single packet of data from the specified endpoint and returns the actual length of
85-
/// the packet.
84+
/// Reads a single packet of non-SETUP data from the specified endpoint and returns the length
85+
/// of the packet.
8686
///
8787
/// This should also clear any NAK flags and prepare the endpoint to receive the next packet.
8888
///
@@ -97,10 +97,32 @@ pub trait UsbBus: Sized {
9797
/// fit in `buf`. This is generally an error in the class implementation, because the class
9898
/// should use a buffer that is large enough for the `max_packet_size` it specified when
9999
/// allocating the endpoint.
100+
/// * [`InvalidState`](crate::UsbError::InvalidState) - The received packet is a SETUP
101+
/// transaction, and needs to be read through [`read_setup()`](UsbBus::read_setup()) instead.
100102
///
101103
/// Implementations may also return other errors if applicable.
102104
fn read(&self, ep_addr: EndpointAddress, buf: &mut [u8]) -> Result<usize>;
103105

106+
/// Reads a packet of SETUP data from the specified endpoint, returns the length of the packet
107+
///
108+
/// This is a distinct method from [`read()`](UsbBus::read()) because the USB spec states that
109+
/// the function (device) must accept the SETUP transaction, which in some implementations can
110+
/// result in a SETUP transaction overwriting data from an earlier OUT transaction. Separate
111+
/// read methods allow the control pipe implementation to detect this overwriting.
112+
///
113+
/// # Errors
114+
///
115+
/// * [`InvalidEndpoint`](crate::UsbError::InvalidEndpoint) - The `ep_addr` does not point to a
116+
/// valid endpoint that was previously allocated with [`UsbBus::alloc_ep`].
117+
/// * [`WouldBlock`](crate::UsbError::WouldBlock) - There is no SETUP packet to be read. Note
118+
/// that this is different from a received zero-length packet, which is valid in USB. A
119+
/// zero-length packet will return `Ok(0)`.
120+
/// * [`BufferOverflow`](crate::UsbError::BufferOverflow) - The received packet is too long to
121+
/// fit in `buf`. This is generally an error in the class implementation, because the class
122+
/// should use a buffer that is large enough for the `max_packet_size` it specified when
123+
/// allocating the endpoint.
124+
fn read_setup(&self, ep_addr: EndpointAddress, buf: &mut [u8]) -> Result<usize>;
125+
104126
/// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it
105127
/// should be prepared to receive data again.
106128
fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool);

src/control_pipe.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ impl<B: UsbBus> ControlPipe<'_, B> {
6565
}
6666

6767
pub fn handle_setup(&mut self) -> Option<Request> {
68-
let count = match self.ep_out.read(&mut self.buf[..]) {
68+
let count = match self.ep_out.read_setup(&mut self.buf[..]) {
6969
Ok(count) => {
7070
usb_trace!("Read {} bytes on EP0-OUT: {:?}", count, &self.buf[..count]);
7171
count
@@ -165,7 +165,15 @@ impl<B: UsbBus> ControlPipe<'_, B> {
165165
"Control transfer completed. Current state: {:?}",
166166
self.state
167167
);
168-
self.ep_out.read(&mut [])?;
168+
match self.ep_out.read(&mut []) {
169+
Ok(_) => {}
170+
Err(UsbError::InvalidState) => {
171+
// Host sent a new SETUP transaction, which may have overwritten the ZLP
172+
}
173+
Err(err) => {
174+
return Err(err);
175+
}
176+
}
169177
self.state = ControlState::Idle;
170178
}
171179
_ => {

src/dummy.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ impl UsbBus for DummyUsbBus {
5656
unimplemented!()
5757
}
5858

59+
fn read_setup(
60+
&self,
61+
ep_addr: crate::class_prelude::EndpointAddress,
62+
buf: &mut [u8],
63+
) -> crate::Result<usize> {
64+
unimplemented!()
65+
}
66+
5967
fn reset(&self) {
6068
unimplemented!()
6169
}

src/endpoint.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::bus::UsbBus;
2-
use crate::{Result, UsbDirection};
2+
use crate::{Result, UsbDirection, UsbError};
33
use core::marker::PhantomData;
44
use portable_atomic::{AtomicPtr, Ordering};
55

@@ -197,9 +197,10 @@ impl<B: UsbBus> Endpoint<'_, B, In> {
197197
}
198198

199199
impl<B: UsbBus> Endpoint<'_, B, Out> {
200-
/// Reads a single packet of data from the specified endpoint and returns the actual length of
201-
/// the packet. The buffer should be large enough to fit at least as many bytes as the
202-
/// `max_packet_size` specified when allocating the endpoint.
200+
/// Reads a single packet of data and returns the length of the packet
201+
///
202+
/// The buffer should be large enough to fit at least as many bytes as the `max_packet_size`
203+
/// specified when allocating the endpoint.
203204
///
204205
/// # Errors
205206
///
@@ -211,9 +212,37 @@ impl<B: UsbBus> Endpoint<'_, B, Out> {
211212
/// USB. A zero-length packet will return `Ok(0)`.
212213
/// * [`BufferOverflow`](crate::UsbError::BufferOverflow) - The received packet is too long to
213214
/// fit in `data`. This is generally an error in the class implementation.
215+
/// * [`InvalidState`](crate::UsbError::InvalidState) - The received packet is a SETUP
216+
/// transaction, and needs to be read through [`read_setup()`](Endpoint::read_setup())
217+
/// instead.
214218
pub fn read(&self, data: &mut [u8]) -> Result<usize> {
215219
self.bus().read(self.address, data)
216220
}
221+
222+
/// Reads a single packet of SETUP data and returns the length of the packet
223+
///
224+
/// The buffer should be large enough to fit at least as many bytes as the `max_packet_size`
225+
/// specified when allocating the endpoint. See [`UsbBus::read_setup()`] for rationale for two
226+
/// distinct read methods.
227+
///
228+
/// # Errors
229+
///
230+
/// Note: USB bus implementation errors are directly passed through, so be prepared to handle
231+
/// other errors as well.
232+
///
233+
/// * [`WouldBlock`](crate::UsbError::WouldBlock) - There is no packet to be read. Note that
234+
/// this is different from a received zero-length packet, which is valid and significant in
235+
/// USB. A zero-length packet will return `Ok(0)`.
236+
/// * [`BufferOverflow`](crate::UsbError::BufferOverflow) - The received packet is too long to
237+
/// fit in `data`. This is generally an error in the class implementation.
238+
pub fn read_setup(&self, data: &mut [u8]) -> Result<usize> {
239+
// SETUP transactions can only occur on control endpoints
240+
if self.ep_type != EndpointType::Control {
241+
Err(UsbError::InvalidEndpoint)
242+
} else {
243+
self.bus().read_setup(self.address, data)
244+
}
245+
}
217246
}
218247

219248
/// Type-safe endpoint address.

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,10 @@ fn _ensure_sync() {
258258
Err(UsbError::InvalidEndpoint)
259259
}
260260

261+
fn read_setup(&self, _ep_addr: EndpointAddress, _buf: &mut [u8]) -> Result<usize> {
262+
Err(UsbError::InvalidEndpoint)
263+
}
264+
261265
fn set_stalled(&self, _ep_addr: EndpointAddress, _stalled: bool) {}
262266
fn is_stalled(&self, _ep_addr: EndpointAddress) -> bool {
263267
false

0 commit comments

Comments
 (0)