aboutsummaryrefslogtreecommitdiff
path: root/embassy-usb-driver/src
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-01-05 02:20:05 +0100
committerGitHub <[email protected]>2023-01-05 02:20:05 +0100
commitb72da125eb064e15cad0da87f5ec09a27080d6be (patch)
tree9c5c4bd7e15d278ee2a3745373154ed36480c782 /embassy-usb-driver/src
parentf339e8518f7ec11da93dfb615ed8aa0ed009358f (diff)
parent0ecc54f58c7a75bbaf89c875a23d500137dd52b6 (diff)
Merge pull request #1144 from embassy-rs/usb-control-docs
usb/driver: document ControlPipe.
Diffstat (limited to 'embassy-usb-driver/src')
-rw-r--r--embassy-usb-driver/src/lib.rs64
1 files changed, 64 insertions, 0 deletions
diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs
index 9300ff812..71e5267c6 100644
--- a/embassy-usb-driver/src/lib.rs
+++ b/embassy-usb-driver/src/lib.rs
@@ -215,6 +215,70 @@ pub trait EndpointOut: Endpoint {
215 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>; 215 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>;
216} 216}
217 217
218/// Trait for USB control pipe.
219///
220/// The USB control pipe owns both OUT ep 0 and IN ep 0 in a single
221/// unit, and manages them together to implement the control pipe state machine.
222///
223/// The reason this is a separate trait instead of using EndpointOut/EndpointIn is that
224/// many USB peripherals treat the control pipe endpoints differently (different registers,
225/// different procedures), usually to accelerate processing in hardware somehow. A separate
226/// trait allows the driver to handle it specially.
227///
228/// The call sequences made by the USB stack to the ControlPipe are the following:
229///
230/// - control in/out with len=0:
231///
232/// ```not_rust
233/// setup()
234/// (...processing...)
235/// accept() or reject()
236/// ```
237///
238/// - control out with len != 0:
239///
240/// ```not_rust
241/// setup()
242/// data_out(first=true, last=false)
243/// data_out(first=false, last=false)
244/// ...
245/// data_out(first=false, last=false)
246/// data_out(first=false, last=true)
247/// (...processing...)
248/// accept() or reject()
249/// ```
250///
251/// - control in with len != 0, accepted:
252///
253/// ```not_rust
254/// setup()
255/// (...processing...)
256/// data_in(first=true, last=false)
257/// data_in(first=false, last=false)
258/// ...
259/// data_in(first=false, last=false)
260/// data_in(first=false, last=true)
261/// (NO `accept()`!!! This is because calling `data_in` already implies acceptance.)
262/// ```
263///
264/// - control in with len != 0, rejected:
265///
266/// ```not_rust
267/// setup()
268/// (...processing...)
269/// reject()
270/// ```
271///
272/// The driver is responsible for handling the status stage. The stack DOES NOT do zero-length
273/// calls to `data_in` or `data_out` for the status zero-length packet. The status stage should
274/// be triggered by either `accept()`, or `data_in` with `last = true`.
275///
276/// Note that the host can abandon a control request and send a new STATUS packet any time. If
277/// a STATUS packet arrives at any time during `data_out`, `data_in`, `accept` or `reject`,
278/// the driver must immediately return (with `EndpointError::Disabled` from `data_in`, `data_out`)
279/// to let the stack call `setup()` again to start handling the new control request. Not doing
280/// so will cause things to get stuck, because the host will never read/send the packet we're
281/// waiting for.
218pub trait ControlPipe { 282pub trait ControlPipe {
219 /// Maximum packet size for the control pipe 283 /// Maximum packet size for the control pipe
220 fn max_packet_size(&self) -> usize; 284 fn max_packet_size(&self) -> usize;