diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-03-09 23:06:27 +0100 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-04-06 05:38:11 +0200 |
| commit | 77ceced036d574c7d67259b85e1d61b96e82d0d3 (patch) | |
| tree | 4f1a250f92d59922ae7ceda0779aae16cfa767bb /embassy-usb | |
| parent | 37598a5b3792ec1b763b5c16fe422c9e1347d7d6 (diff) | |
Working CDC-ACM host->device
Diffstat (limited to 'embassy-usb')
| -rw-r--r-- | embassy-usb/Cargo.toml | 2 | ||||
| -rw-r--r-- | embassy-usb/src/driver.rs | 87 | ||||
| -rw-r--r-- | embassy-usb/src/lib.rs | 64 | ||||
| -rw-r--r-- | embassy-usb/src/util.rs | 45 |
4 files changed, 146 insertions, 52 deletions
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index dfdc8fbac..5a5a6d7ab 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "embassy-usb" | 2 | name = "embassy-usb" |
| 3 | version = "0.1.0" | 3 | version = "0.1.0" |
| 4 | edition = "2018" | 4 | edition = "2021" |
| 5 | 5 | ||
| 6 | [dependencies] | 6 | [dependencies] |
| 7 | embassy = { version = "0.1.0", path = "../embassy" } | 7 | embassy = { version = "0.1.0", path = "../embassy" } |
diff --git a/embassy-usb/src/driver.rs b/embassy-usb/src/driver.rs index ed4edb576..a7b16efa5 100644 --- a/embassy-usb/src/driver.rs +++ b/embassy-usb/src/driver.rs | |||
| @@ -2,40 +2,6 @@ use core::future::Future; | |||
| 2 | 2 | ||
| 3 | use super::types::*; | 3 | use super::types::*; |
| 4 | 4 | ||
| 5 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 6 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 7 | pub struct EndpointAllocError; | ||
| 8 | |||
| 9 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 10 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 11 | |||
| 12 | /// Operation is unsupported by the driver. | ||
| 13 | pub struct Unsupported; | ||
| 14 | |||
| 15 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 16 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 17 | |||
| 18 | /// Errors returned by [`EndpointIn::write`] | ||
| 19 | pub enum WriteError { | ||
| 20 | /// The packet is too long to fit in the | ||
| 21 | /// transmission buffer. This is generally an error in the class implementation, because the | ||
| 22 | /// class shouldn't provide more data than the `max_packet_size` it specified when allocating | ||
| 23 | /// the endpoint. | ||
| 24 | BufferOverflow, | ||
| 25 | } | ||
| 26 | |||
| 27 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 28 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 29 | |||
| 30 | /// Errors returned by [`EndpointOut::read`] | ||
| 31 | pub enum ReadError { | ||
| 32 | /// The received packet is too long to | ||
| 33 | /// fit in `buf`. This is generally an error in the class implementation, because the class | ||
| 34 | /// should use a buffer that is large enough for the `max_packet_size` it specified when | ||
| 35 | /// allocating the endpoint. | ||
| 36 | BufferOverflow, | ||
| 37 | } | ||
| 38 | |||
| 39 | /// Driver for a specific USB peripheral. Implement this to add support for a new hardware | 5 | /// Driver for a specific USB peripheral. Implement this to add support for a new hardware |
| 40 | /// platform. | 6 | /// platform. |
| 41 | pub trait Driver<'a> { | 7 | pub trait Driver<'a> { |
| @@ -82,6 +48,12 @@ pub trait Driver<'a> { | |||
| 82 | } | 48 | } |
| 83 | 49 | ||
| 84 | pub trait Bus { | 50 | pub trait Bus { |
| 51 | type PollFuture<'a>: Future<Output = Event> + 'a | ||
| 52 | where | ||
| 53 | Self: 'a; | ||
| 54 | |||
| 55 | fn poll<'a>(&'a mut self) -> Self::PollFuture<'a>; | ||
| 56 | |||
| 85 | /// Called when the host resets the device. This will be soon called after | 57 | /// Called when the host resets the device. This will be soon called after |
| 86 | /// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Reset`]. This method should | 58 | /// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Reset`]. This method should |
| 87 | /// reset the state of all endpoints and peripheral flags back to a state suitable for | 59 | /// reset the state of all endpoints and peripheral flags back to a state suitable for |
| @@ -158,3 +130,50 @@ pub trait EndpointIn: Endpoint { | |||
| 158 | /// Writes a single packet of data to the endpoint. | 130 | /// Writes a single packet of data to the endpoint. |
| 159 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a>; | 131 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a>; |
| 160 | } | 132 | } |
| 133 | |||
| 134 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 135 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 136 | /// Event returned by [`Bus::poll`]. | ||
| 137 | pub enum Event { | ||
| 138 | /// The USB reset condition has been detected. | ||
| 139 | Reset, | ||
| 140 | |||
| 141 | /// A USB suspend request has been detected or, in the case of self-powered devices, the device | ||
| 142 | /// has been disconnected from the USB bus. | ||
| 143 | Suspend, | ||
| 144 | |||
| 145 | /// A USB resume request has been detected after being suspended or, in the case of self-powered | ||
| 146 | /// devices, the device has been connected to the USB bus. | ||
| 147 | Resume, | ||
| 148 | } | ||
| 149 | |||
| 150 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 151 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 152 | pub struct EndpointAllocError; | ||
| 153 | |||
| 154 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 155 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 156 | /// Operation is unsupported by the driver. | ||
| 157 | pub struct Unsupported; | ||
| 158 | |||
| 159 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 160 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 161 | /// Errors returned by [`EndpointIn::write`] | ||
| 162 | pub enum WriteError { | ||
| 163 | /// The packet is too long to fit in the | ||
| 164 | /// transmission buffer. This is generally an error in the class implementation, because the | ||
| 165 | /// class shouldn't provide more data than the `max_packet_size` it specified when allocating | ||
| 166 | /// the endpoint. | ||
| 167 | BufferOverflow, | ||
| 168 | } | ||
| 169 | |||
| 170 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 171 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 172 | /// Errors returned by [`EndpointOut::read`] | ||
| 173 | pub enum ReadError { | ||
| 174 | /// The received packet is too long to | ||
| 175 | /// fit in `buf`. This is generally an error in the class implementation, because the class | ||
| 176 | /// should use a buffer that is large enough for the `max_packet_size` it specified when | ||
| 177 | /// allocating the endpoint. | ||
| 178 | BufferOverflow, | ||
| 179 | } | ||
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 397db96c4..33f3d4712 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs | |||
| @@ -9,11 +9,13 @@ mod control; | |||
| 9 | pub mod descriptor; | 9 | pub mod descriptor; |
| 10 | pub mod driver; | 10 | pub mod driver; |
| 11 | pub mod types; | 11 | pub mod types; |
| 12 | mod util; | ||
| 12 | 13 | ||
| 13 | use self::control::*; | 14 | use self::control::*; |
| 14 | use self::descriptor::*; | 15 | use self::descriptor::*; |
| 15 | use self::driver::*; | 16 | use self::driver::*; |
| 16 | use self::types::*; | 17 | use self::types::*; |
| 18 | use self::util::*; | ||
| 17 | 19 | ||
| 18 | pub use self::builder::Config; | 20 | pub use self::builder::Config; |
| 19 | pub use self::builder::UsbDeviceBuilder; | 21 | pub use self::builder::UsbDeviceBuilder; |
| @@ -47,7 +49,7 @@ pub const CONFIGURATION_VALUE: u8 = 1; | |||
| 47 | pub const DEFAULT_ALTERNATE_SETTING: u8 = 0; | 49 | pub const DEFAULT_ALTERNATE_SETTING: u8 = 0; |
| 48 | 50 | ||
| 49 | pub struct UsbDevice<'d, D: Driver<'d>> { | 51 | pub struct UsbDevice<'d, D: Driver<'d>> { |
| 50 | driver: D::Bus, | 52 | bus: D::Bus, |
| 51 | control_in: D::EndpointIn, | 53 | control_in: D::EndpointIn, |
| 52 | control_out: D::EndpointOut, | 54 | control_out: D::EndpointOut, |
| 53 | 55 | ||
| @@ -93,7 +95,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 93 | let driver = driver.enable(); | 95 | let driver = driver.enable(); |
| 94 | 96 | ||
| 95 | Self { | 97 | Self { |
| 96 | driver, | 98 | bus: driver, |
| 97 | config, | 99 | config, |
| 98 | control_in, | 100 | control_in, |
| 99 | control_out, | 101 | control_out, |
| @@ -108,20 +110,47 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 108 | } | 110 | } |
| 109 | 111 | ||
| 110 | pub async fn run(&mut self) { | 112 | pub async fn run(&mut self) { |
| 113 | let mut buf = [0; 8]; | ||
| 114 | |||
| 111 | loop { | 115 | loop { |
| 112 | let mut buf = [0; 8]; | 116 | let control_fut = self.control_out.read(&mut buf); |
| 113 | let n = self.control_out.read(&mut buf).await.unwrap(); | 117 | let bus_fut = self.bus.poll(); |
| 114 | assert_eq!(n, 8); | 118 | match select(bus_fut, control_fut).await { |
| 115 | let req = Request::parse(&buf).unwrap(); | 119 | Either::Left(evt) => match evt { |
| 116 | info!("setup request: {:x}", req); | 120 | Event::Reset => { |
| 117 | 121 | self.bus.reset(); | |
| 118 | // Now that we have properly parsed the setup packet, ensure the end-point is no longer in | 122 | |
| 119 | // a stalled state. | 123 | self.device_state = UsbDeviceState::Default; |
| 120 | self.control_out.set_stalled(false); | 124 | self.remote_wakeup_enabled = false; |
| 121 | 125 | self.pending_address = 0; | |
| 122 | match req.direction { | 126 | |
| 123 | UsbDirection::In => self.handle_control_in(req).await, | 127 | // TODO |
| 124 | UsbDirection::Out => self.handle_control_out(req).await, | 128 | //self.control.reset(); |
| 129 | //for cls in classes { | ||
| 130 | // cls.reset(); | ||
| 131 | //} | ||
| 132 | } | ||
| 133 | Event::Resume => {} | ||
| 134 | Event::Suspend => { | ||
| 135 | self.bus.suspend(); | ||
| 136 | self.device_state = UsbDeviceState::Suspend; | ||
| 137 | } | ||
| 138 | }, | ||
| 139 | Either::Right(n) => { | ||
| 140 | let n = n.unwrap(); | ||
| 141 | assert_eq!(n, 8); | ||
| 142 | let req = Request::parse(&buf).unwrap(); | ||
| 143 | info!("control request: {:x}", req); | ||
| 144 | |||
| 145 | // Now that we have properly parsed the setup packet, ensure the end-point is no longer in | ||
| 146 | // a stalled state. | ||
| 147 | self.control_out.set_stalled(false); | ||
| 148 | |||
| 149 | match req.direction { | ||
| 150 | UsbDirection::In => self.handle_control_in(req).await, | ||
| 151 | UsbDirection::Out => self.handle_control_out(req).await, | ||
| 152 | } | ||
| 153 | } | ||
| 125 | } | 154 | } |
| 126 | } | 155 | } |
| 127 | } | 156 | } |
| @@ -205,7 +234,8 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 205 | } | 234 | } |
| 206 | 235 | ||
| 207 | (Recipient::Endpoint, Request::SET_FEATURE, Request::FEATURE_ENDPOINT_HALT) => { | 236 | (Recipient::Endpoint, Request::SET_FEATURE, Request::FEATURE_ENDPOINT_HALT) => { |
| 208 | //self.bus.set_stalled(((req.index as u8) & 0x8f).into(), true); | 237 | self.bus |
| 238 | .set_stalled(((req.index as u8) & 0x8f).into(), true); | ||
| 209 | self.control_out_accept(req).await; | 239 | self.control_out_accept(req).await; |
| 210 | } | 240 | } |
| 211 | 241 | ||
| @@ -266,7 +296,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 266 | (Recipient::Endpoint, Request::GET_STATUS) => { | 296 | (Recipient::Endpoint, Request::GET_STATUS) => { |
| 267 | let ep_addr: EndpointAddress = ((req.index as u8) & 0x8f).into(); | 297 | let ep_addr: EndpointAddress = ((req.index as u8) & 0x8f).into(); |
| 268 | let mut status: u16 = 0x0000; | 298 | let mut status: u16 = 0x0000; |
| 269 | if self.driver.is_stalled(ep_addr) { | 299 | if self.bus.is_stalled(ep_addr) { |
| 270 | status |= 0x0001; | 300 | status |= 0x0001; |
| 271 | } | 301 | } |
| 272 | self.control_in_accept(req, &status.to_le_bytes()).await; | 302 | self.control_in_accept(req, &status.to_le_bytes()).await; |
diff --git a/embassy-usb/src/util.rs b/embassy-usb/src/util.rs new file mode 100644 index 000000000..18cc875c6 --- /dev/null +++ b/embassy-usb/src/util.rs | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use core::task::{Context, Poll}; | ||
| 4 | |||
| 5 | #[derive(Debug, Clone)] | ||
| 6 | pub enum Either<A, B> { | ||
| 7 | Left(A), | ||
| 8 | Right(B), | ||
| 9 | } | ||
| 10 | |||
| 11 | pub fn select<A, B>(a: A, b: B) -> Select<A, B> | ||
| 12 | where | ||
| 13 | A: Future, | ||
| 14 | B: Future, | ||
| 15 | { | ||
| 16 | Select { a, b } | ||
| 17 | } | ||
| 18 | |||
| 19 | pub struct Select<A, B> { | ||
| 20 | a: A, | ||
| 21 | b: B, | ||
| 22 | } | ||
| 23 | |||
| 24 | impl<A: Unpin, B: Unpin> Unpin for Select<A, B> {} | ||
| 25 | |||
| 26 | impl<A, B> Future for Select<A, B> | ||
| 27 | where | ||
| 28 | A: Future, | ||
| 29 | B: Future, | ||
| 30 | { | ||
| 31 | type Output = Either<A::Output, B::Output>; | ||
| 32 | |||
| 33 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 34 | let this = unsafe { self.get_unchecked_mut() }; | ||
| 35 | let a = unsafe { Pin::new_unchecked(&mut this.a) }; | ||
| 36 | let b = unsafe { Pin::new_unchecked(&mut this.b) }; | ||
| 37 | match a.poll(cx) { | ||
| 38 | Poll::Ready(x) => Poll::Ready(Either::Left(x)), | ||
| 39 | Poll::Pending => match b.poll(cx) { | ||
| 40 | Poll::Ready(x) => Poll::Ready(Either::Right(x)), | ||
| 41 | Poll::Pending => Poll::Pending, | ||
| 42 | }, | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
