mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 16:23:10 +00:00
usb: merge Control logic into main code.
Now that control stuff is called from just one place, there's no need to keep it as a separate struct.
This commit is contained in:
parent
7ed462a657
commit
02ae1138e1
@ -1,7 +1,6 @@
|
|||||||
use core::mem;
|
use core::mem;
|
||||||
|
|
||||||
use super::types::*;
|
use super::types::*;
|
||||||
use crate::driver::{self, EndpointError};
|
|
||||||
|
|
||||||
/// Control request type.
|
/// Control request type.
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
@ -194,125 +193,3 @@ pub trait ControlHandler {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Typestate representing a ControlPipe in the DATA IN stage
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub(crate) struct DataInStage {
|
|
||||||
pub(crate) length: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Typestate representing a ControlPipe in the DATA OUT stage
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub(crate) struct DataOutStage {
|
|
||||||
length: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Typestate representing a ControlPipe in the STATUS stage
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub(crate) struct StatusStage {}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub(crate) enum Setup {
|
|
||||||
DataIn(Request, DataInStage),
|
|
||||||
DataOut(Request, DataOutStage),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct ControlPipe<C: driver::ControlPipe> {
|
|
||||||
control: C,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: driver::ControlPipe> ControlPipe<C> {
|
|
||||||
pub(crate) fn new(control: C) -> Self {
|
|
||||||
ControlPipe { control }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn setup(&mut self) -> Setup {
|
|
||||||
let req = self.control.setup().await;
|
|
||||||
trace!("control request: {:02x}", req);
|
|
||||||
|
|
||||||
match (req.direction, req.length) {
|
|
||||||
(UsbDirection::Out, n) => Setup::DataOut(
|
|
||||||
req,
|
|
||||||
DataOutStage {
|
|
||||||
length: usize::from(n),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(UsbDirection::In, n) => Setup::DataIn(
|
|
||||||
req,
|
|
||||||
DataInStage {
|
|
||||||
length: usize::from(n),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn data_out<'a>(
|
|
||||||
&mut self,
|
|
||||||
buf: &'a mut [u8],
|
|
||||||
stage: DataOutStage,
|
|
||||||
) -> Result<(&'a [u8], StatusStage), EndpointError> {
|
|
||||||
if stage.length == 0 {
|
|
||||||
Ok((&[], StatusStage {}))
|
|
||||||
} else {
|
|
||||||
let req_length = stage.length;
|
|
||||||
let max_packet_size = self.control.max_packet_size();
|
|
||||||
let mut total = 0;
|
|
||||||
|
|
||||||
for chunk in buf.chunks_mut(max_packet_size) {
|
|
||||||
let size = self.control.data_out(chunk).await?;
|
|
||||||
total += size;
|
|
||||||
if size < max_packet_size || total == req_length {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = &buf[0..total];
|
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
trace!(" control out data: {:02x}", res);
|
|
||||||
#[cfg(not(feature = "defmt"))]
|
|
||||||
trace!(" control out data: {:02x?}", res);
|
|
||||||
|
|
||||||
Ok((res, StatusStage {}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn accept_in(&mut self, buf: &[u8], stage: DataInStage) {
|
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
trace!(" control in accept {:02x}", buf);
|
|
||||||
#[cfg(not(feature = "defmt"))]
|
|
||||||
trace!(" control in accept {:02x?}", buf);
|
|
||||||
|
|
||||||
let req_len = stage.length;
|
|
||||||
let len = buf.len().min(req_len);
|
|
||||||
let max_packet_size = self.control.max_packet_size();
|
|
||||||
let need_zlp = len != req_len && (len % usize::from(max_packet_size)) == 0;
|
|
||||||
|
|
||||||
let mut chunks = buf[0..len]
|
|
||||||
.chunks(max_packet_size)
|
|
||||||
.chain(need_zlp.then(|| -> &[u8] { &[] }));
|
|
||||||
|
|
||||||
while let Some(chunk) = chunks.next() {
|
|
||||||
match self.control.data_in(chunk, chunks.size_hint().0 == 0).await {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("control accept_in failed: {:?}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn accept(&mut self, _: StatusStage) {
|
|
||||||
trace!(" control accept");
|
|
||||||
self.control.accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn reject(&mut self) {
|
|
||||||
trace!(" control reject");
|
|
||||||
self.control.reject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -16,6 +16,7 @@ use embassy::util::{select, Either};
|
|||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
|
|
||||||
use crate::descriptor_reader::foreach_endpoint;
|
use crate::descriptor_reader::foreach_endpoint;
|
||||||
|
use crate::driver::ControlPipe;
|
||||||
|
|
||||||
use self::control::*;
|
use self::control::*;
|
||||||
use self::descriptor::*;
|
use self::descriptor::*;
|
||||||
@ -101,7 +102,7 @@ struct Interface<'d> {
|
|||||||
|
|
||||||
pub struct UsbDevice<'d, D: Driver<'d>> {
|
pub struct UsbDevice<'d, D: Driver<'d>> {
|
||||||
control_buf: &'d mut [u8],
|
control_buf: &'d mut [u8],
|
||||||
control: ControlPipe<D::ControlPipe>,
|
control: D::ControlPipe,
|
||||||
inner: Inner<'d, D>,
|
inner: Inner<'d, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +145,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
control_buf,
|
control_buf,
|
||||||
control: ControlPipe::new(control),
|
control,
|
||||||
inner: Inner {
|
inner: Inner {
|
||||||
bus,
|
bus,
|
||||||
config,
|
config,
|
||||||
@ -192,52 +193,12 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
while !self.inner.suspended {
|
||||||
let control_fut = self.control.setup();
|
let control_fut = self.control.setup();
|
||||||
let bus_fut = self.inner.bus.poll();
|
let bus_fut = self.inner.bus.poll();
|
||||||
match select(bus_fut, control_fut).await {
|
match select(bus_fut, control_fut).await {
|
||||||
Either::First(evt) => {
|
Either::First(evt) => self.inner.handle_bus_event(evt),
|
||||||
self.inner.handle_bus_event(evt);
|
Either::Second(req) => self.handle_control(req).await,
|
||||||
if self.inner.suspended {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Either::Second(req) => match req {
|
|
||||||
Setup::DataIn(req, mut stage) => {
|
|
||||||
// If we don't have an address yet, respond with max 1 packet.
|
|
||||||
// The host doesn't know our EP0 max packet size yet, and might assume
|
|
||||||
// a full-length packet is a short packet, thinking we're done sending data.
|
|
||||||
// See https://github.com/hathach/tinyusb/issues/184
|
|
||||||
const DEVICE_DESCRIPTOR_LEN: u8 = 18;
|
|
||||||
if self.inner.pending_address == 0
|
|
||||||
&& self.inner.config.max_packet_size_0 < DEVICE_DESCRIPTOR_LEN
|
|
||||||
&& (self.inner.config.max_packet_size_0 as usize) < stage.length
|
|
||||||
{
|
|
||||||
trace!("received control req while not addressed: capping response to 1 packet.");
|
|
||||||
stage.length = self.inner.config.max_packet_size_0 as _;
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.inner.handle_control_in(req, &mut self.control_buf) {
|
|
||||||
InResponse::Accepted(data) => self.control.accept_in(data, stage).await,
|
|
||||||
InResponse::Rejected => self.control.reject(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Setup::DataOut(req, stage) => {
|
|
||||||
let (data, stage) =
|
|
||||||
match self.control.data_out(self.control_buf, stage).await {
|
|
||||||
Ok(data) => data,
|
|
||||||
Err(_) => {
|
|
||||||
warn!("usb: failed to read CONTROL OUT data stage.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match self.inner.handle_control_out(req, data) {
|
|
||||||
OutResponse::Accepted => self.control.accept(stage),
|
|
||||||
OutResponse::Rejected => self.control.reject(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,6 +249,86 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
|
|||||||
Err(RemoteWakeupError::InvalidState)
|
Err(RemoteWakeupError::InvalidState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_control(&mut self, req: Request) {
|
||||||
|
trace!("control request: {:02x}", req);
|
||||||
|
|
||||||
|
match req.direction {
|
||||||
|
UsbDirection::In => self.handle_control_in(req).await,
|
||||||
|
UsbDirection::Out => self.handle_control_out(req).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_control_in(&mut self, req: Request) {
|
||||||
|
let mut resp_length = req.length as usize;
|
||||||
|
let max_packet_size = self.control.max_packet_size();
|
||||||
|
|
||||||
|
// If we don't have an address yet, respond with max 1 packet.
|
||||||
|
// The host doesn't know our EP0 max packet size yet, and might assume
|
||||||
|
// a full-length packet is a short packet, thinking we're done sending data.
|
||||||
|
// See https://github.com/hathach/tinyusb/issues/184
|
||||||
|
const DEVICE_DESCRIPTOR_LEN: usize = 18;
|
||||||
|
if self.inner.pending_address == 0
|
||||||
|
&& max_packet_size < DEVICE_DESCRIPTOR_LEN
|
||||||
|
&& (max_packet_size as usize) < resp_length
|
||||||
|
{
|
||||||
|
trace!("received control req while not addressed: capping response to 1 packet.");
|
||||||
|
resp_length = max_packet_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.inner.handle_control_in(req, &mut self.control_buf) {
|
||||||
|
InResponse::Accepted(data) => {
|
||||||
|
let len = data.len().min(resp_length);
|
||||||
|
let need_zlp = len != resp_length && (len % usize::from(max_packet_size)) == 0;
|
||||||
|
|
||||||
|
let mut chunks = data[0..len]
|
||||||
|
.chunks(max_packet_size)
|
||||||
|
.chain(need_zlp.then(|| -> &[u8] { &[] }));
|
||||||
|
|
||||||
|
while let Some(chunk) = chunks.next() {
|
||||||
|
match self.control.data_in(chunk, chunks.size_hint().0 == 0).await {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("control accept_in failed: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InResponse::Rejected => self.control.reject(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_control_out(&mut self, req: Request) {
|
||||||
|
let req_length = req.length as usize;
|
||||||
|
let max_packet_size = self.control.max_packet_size();
|
||||||
|
let mut total = 0;
|
||||||
|
|
||||||
|
for chunk in self.control_buf[..req_length].chunks_mut(max_packet_size) {
|
||||||
|
let size = match self.control.data_out(chunk).await {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("usb: failed to read CONTROL OUT data stage: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
total += size;
|
||||||
|
if size < max_packet_size || total == req_length {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = &self.control_buf[0..total];
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
trace!(" control out data: {:02x}", data);
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
trace!(" control out data: {:02x?}", data);
|
||||||
|
|
||||||
|
match self.inner.handle_control_out(req, data) {
|
||||||
|
OutResponse::Accepted => self.control.accept(),
|
||||||
|
OutResponse::Rejected => self.control.reject(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, D: Driver<'d>> Inner<'d, D> {
|
impl<'d, D: Driver<'d>> Inner<'d, D> {
|
||||||
|
Loading…
Reference in New Issue
Block a user