Trait embassy_usb_driver::ControlPipe
source · pub trait ControlPipe {
// Required methods
fn max_packet_size(&self) -> usize;
async fn setup(&mut self) -> [u8; 8];
async fn data_out(
&mut self,
buf: &mut [u8],
first: bool,
last: bool
) -> Result<usize, EndpointError>;
async fn data_in(
&mut self,
data: &[u8],
first: bool,
last: bool
) -> Result<(), EndpointError>;
async fn accept(&mut self);
async fn reject(&mut self);
async fn accept_set_address(&mut self, addr: u8);
}
Expand description
USB control pipe trait.
The USB control pipe owns both OUT endpoint 0 and IN endpoint 0 in a single unit, and manages them together to implement the control pipe state machine.
The reason this is a separate trait instead of using EndpointOut/EndpointIn is that many USB peripherals treat the control pipe endpoints differently (different registers, different procedures), usually to accelerate processing in hardware somehow. A separate trait allows the driver to handle it specially.
The call sequences made by the USB stack to the ControlPipe are the following:
- control in/out with len=0:
setup()
(...processing...)
accept() or reject()
- control out for setting the device address:
setup()
(...processing...)
accept_set_address(addr) or reject()
- control out with len != 0:
setup()
data_out(first=true, last=false)
data_out(first=false, last=false)
...
data_out(first=false, last=false)
data_out(first=false, last=true)
(...processing...)
accept() or reject()
- control in with len != 0, accepted:
setup()
(...processing...)
data_in(first=true, last=false)
data_in(first=false, last=false)
...
data_in(first=false, last=false)
data_in(first=false, last=true)
(NO `accept()`!!! This is because calling `data_in` already implies acceptance.)
- control in with len != 0, rejected:
setup()
(...processing...)
reject()
The driver is responsible for handling the status stage. The stack DOES NOT do zero-length
calls to data_in
or data_out
for the status zero-length packet. The status stage should
be triggered by either accept()
, or data_in
with last = true
.
Note that the host can abandon a control request and send a new SETUP packet any time. If
a SETUP packet arrives at any time during data_out
, data_in
, accept
or reject
,
the driver must immediately return (with EndpointError::Disabled
from data_in
, data_out
)
to let the stack call setup()
again to start handling the new control request. Not doing
so will cause things to get stuck, because the host will never read/send the packet we’re
waiting for.
Required Methods§
sourcefn max_packet_size(&self) -> usize
fn max_packet_size(&self) -> usize
Maximum packet size for the control pipe
sourceasync fn data_out(
&mut self,
buf: &mut [u8],
first: bool,
last: bool
) -> Result<usize, EndpointError>
async fn data_out( &mut self, buf: &mut [u8], first: bool, last: bool ) -> Result<usize, EndpointError>
Read a DATA OUT packet into buf
in response to a control write request.
Must be called after setup()
for requests with direction
of Out
and length
greater than zero.
sourceasync fn data_in(
&mut self,
data: &[u8],
first: bool,
last: bool
) -> Result<(), EndpointError>
async fn data_in( &mut self, data: &[u8], first: bool, last: bool ) -> Result<(), EndpointError>
Send a DATA IN packet with data
in response to a control read request.
If last_packet
is true, the STATUS packet will be ACKed following the transfer of data
.
sourceasync fn accept(&mut self)
async fn accept(&mut self)
Accept a control request.
Causes the STATUS packet for the current request to be ACKed.
sourceasync fn reject(&mut self)
async fn reject(&mut self)
Reject a control request.
Sets a STALL condition on the pipe to indicate an error.
sourceasync fn accept_set_address(&mut self, addr: u8)
async fn accept_set_address(&mut self, addr: u8)
Accept SET_ADDRESS control and change bus address.
For most drivers this function should firstly call accept()
and then change the bus address.
However, there are peripherals (Synopsys USB OTG) that have reverse order.