Working CDC-ACM host->device
This commit is contained in:
@ -2,40 +2,6 @@ use core::future::Future;
|
||||
|
||||
use super::types::*;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct EndpointAllocError;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
||||
/// Operation is unsupported by the driver.
|
||||
pub struct Unsupported;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
||||
/// Errors returned by [`EndpointIn::write`]
|
||||
pub enum WriteError {
|
||||
/// The packet is too long to fit in the
|
||||
/// transmission buffer. This is generally an error in the class implementation, because the
|
||||
/// class shouldn't provide more data than the `max_packet_size` it specified when allocating
|
||||
/// the endpoint.
|
||||
BufferOverflow,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
||||
/// Errors returned by [`EndpointOut::read`]
|
||||
pub enum ReadError {
|
||||
/// The received packet is too long to
|
||||
/// fit in `buf`. This is generally an error in the class implementation, because the class
|
||||
/// should use a buffer that is large enough for the `max_packet_size` it specified when
|
||||
/// allocating the endpoint.
|
||||
BufferOverflow,
|
||||
}
|
||||
|
||||
/// Driver for a specific USB peripheral. Implement this to add support for a new hardware
|
||||
/// platform.
|
||||
pub trait Driver<'a> {
|
||||
@ -82,6 +48,12 @@ pub trait Driver<'a> {
|
||||
}
|
||||
|
||||
pub trait Bus {
|
||||
type PollFuture<'a>: Future<Output = Event> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn poll<'a>(&'a mut self) -> Self::PollFuture<'a>;
|
||||
|
||||
/// Called when the host resets the device. This will be soon called after
|
||||
/// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Reset`]. This method should
|
||||
/// reset the state of all endpoints and peripheral flags back to a state suitable for
|
||||
@ -158,3 +130,50 @@ pub trait EndpointIn: Endpoint {
|
||||
/// Writes a single packet of data to the endpoint.
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Event returned by [`Bus::poll`].
|
||||
pub enum Event {
|
||||
/// The USB reset condition has been detected.
|
||||
Reset,
|
||||
|
||||
/// A USB suspend request has been detected or, in the case of self-powered devices, the device
|
||||
/// has been disconnected from the USB bus.
|
||||
Suspend,
|
||||
|
||||
/// A USB resume request has been detected after being suspended or, in the case of self-powered
|
||||
/// devices, the device has been connected to the USB bus.
|
||||
Resume,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct EndpointAllocError;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Operation is unsupported by the driver.
|
||||
pub struct Unsupported;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Errors returned by [`EndpointIn::write`]
|
||||
pub enum WriteError {
|
||||
/// The packet is too long to fit in the
|
||||
/// transmission buffer. This is generally an error in the class implementation, because the
|
||||
/// class shouldn't provide more data than the `max_packet_size` it specified when allocating
|
||||
/// the endpoint.
|
||||
BufferOverflow,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Errors returned by [`EndpointOut::read`]
|
||||
pub enum ReadError {
|
||||
/// The received packet is too long to
|
||||
/// fit in `buf`. This is generally an error in the class implementation, because the class
|
||||
/// should use a buffer that is large enough for the `max_packet_size` it specified when
|
||||
/// allocating the endpoint.
|
||||
BufferOverflow,
|
||||
}
|
||||
|
@ -9,11 +9,13 @@ mod control;
|
||||
pub mod descriptor;
|
||||
pub mod driver;
|
||||
pub mod types;
|
||||
mod util;
|
||||
|
||||
use self::control::*;
|
||||
use self::descriptor::*;
|
||||
use self::driver::*;
|
||||
use self::types::*;
|
||||
use self::util::*;
|
||||
|
||||
pub use self::builder::Config;
|
||||
pub use self::builder::UsbDeviceBuilder;
|
||||
@ -47,7 +49,7 @@ pub const CONFIGURATION_VALUE: u8 = 1;
|
||||
pub const DEFAULT_ALTERNATE_SETTING: u8 = 0;
|
||||
|
||||
pub struct UsbDevice<'d, D: Driver<'d>> {
|
||||
driver: D::Bus,
|
||||
bus: D::Bus,
|
||||
control_in: D::EndpointIn,
|
||||
control_out: D::EndpointOut,
|
||||
|
||||
@ -93,7 +95,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
|
||||
let driver = driver.enable();
|
||||
|
||||
Self {
|
||||
driver,
|
||||
bus: driver,
|
||||
config,
|
||||
control_in,
|
||||
control_out,
|
||||
@ -108,20 +110,47 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) {
|
||||
let mut buf = [0; 8];
|
||||
|
||||
loop {
|
||||
let mut buf = [0; 8];
|
||||
let n = self.control_out.read(&mut buf).await.unwrap();
|
||||
assert_eq!(n, 8);
|
||||
let req = Request::parse(&buf).unwrap();
|
||||
info!("setup request: {:x}", req);
|
||||
let control_fut = self.control_out.read(&mut buf);
|
||||
let bus_fut = self.bus.poll();
|
||||
match select(bus_fut, control_fut).await {
|
||||
Either::Left(evt) => match evt {
|
||||
Event::Reset => {
|
||||
self.bus.reset();
|
||||
|
||||
// Now that we have properly parsed the setup packet, ensure the end-point is no longer in
|
||||
// a stalled state.
|
||||
self.control_out.set_stalled(false);
|
||||
self.device_state = UsbDeviceState::Default;
|
||||
self.remote_wakeup_enabled = false;
|
||||
self.pending_address = 0;
|
||||
|
||||
match req.direction {
|
||||
UsbDirection::In => self.handle_control_in(req).await,
|
||||
UsbDirection::Out => self.handle_control_out(req).await,
|
||||
// TODO
|
||||
//self.control.reset();
|
||||
//for cls in classes {
|
||||
// cls.reset();
|
||||
//}
|
||||
}
|
||||
Event::Resume => {}
|
||||
Event::Suspend => {
|
||||
self.bus.suspend();
|
||||
self.device_state = UsbDeviceState::Suspend;
|
||||
}
|
||||
},
|
||||
Either::Right(n) => {
|
||||
let n = n.unwrap();
|
||||
assert_eq!(n, 8);
|
||||
let req = Request::parse(&buf).unwrap();
|
||||
info!("control request: {:x}", req);
|
||||
|
||||
// Now that we have properly parsed the setup packet, ensure the end-point is no longer in
|
||||
// a stalled state.
|
||||
self.control_out.set_stalled(false);
|
||||
|
||||
match req.direction {
|
||||
UsbDirection::In => self.handle_control_in(req).await,
|
||||
UsbDirection::Out => self.handle_control_out(req).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,7 +234,8 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
|
||||
}
|
||||
|
||||
(Recipient::Endpoint, Request::SET_FEATURE, Request::FEATURE_ENDPOINT_HALT) => {
|
||||
//self.bus.set_stalled(((req.index as u8) & 0x8f).into(), true);
|
||||
self.bus
|
||||
.set_stalled(((req.index as u8) & 0x8f).into(), true);
|
||||
self.control_out_accept(req).await;
|
||||
}
|
||||
|
||||
@ -266,7 +296,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
|
||||
(Recipient::Endpoint, Request::GET_STATUS) => {
|
||||
let ep_addr: EndpointAddress = ((req.index as u8) & 0x8f).into();
|
||||
let mut status: u16 = 0x0000;
|
||||
if self.driver.is_stalled(ep_addr) {
|
||||
if self.bus.is_stalled(ep_addr) {
|
||||
status |= 0x0001;
|
||||
}
|
||||
self.control_in_accept(req, &status.to_le_bytes()).await;
|
||||
|
45
embassy-usb/src/util.rs
Normal file
45
embassy-usb/src/util.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Either<A, B> {
|
||||
Left(A),
|
||||
Right(B),
|
||||
}
|
||||
|
||||
pub fn select<A, B>(a: A, b: B) -> Select<A, B>
|
||||
where
|
||||
A: Future,
|
||||
B: Future,
|
||||
{
|
||||
Select { a, b }
|
||||
}
|
||||
|
||||
pub struct Select<A, B> {
|
||||
a: A,
|
||||
b: B,
|
||||
}
|
||||
|
||||
impl<A: Unpin, B: Unpin> Unpin for Select<A, B> {}
|
||||
|
||||
impl<A, B> Future for Select<A, B>
|
||||
where
|
||||
A: Future,
|
||||
B: Future,
|
||||
{
|
||||
type Output = Either<A::Output, B::Output>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let a = unsafe { Pin::new_unchecked(&mut this.a) };
|
||||
let b = unsafe { Pin::new_unchecked(&mut this.b) };
|
||||
match a.poll(cx) {
|
||||
Poll::Ready(x) => Poll::Ready(Either::Left(x)),
|
||||
Poll::Pending => match b.poll(cx) {
|
||||
Poll::Ready(x) => Poll::Ready(Either::Right(x)),
|
||||
Poll::Pending => Poll::Pending,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user