diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-03-09 01:34:35 +0100 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-04-06 05:38:11 +0200 |
| commit | 37598a5b3792ec1b763b5c16fe422c9e1347d7d6 (patch) | |
| tree | 558d06652cd95998e2d7b4eebc8e85329aa17a6b | |
| parent | c1b382296434e762d16a36d658d2f308358e3f87 (diff) | |
wip: experimental async usb stack
| -rw-r--r-- | embassy-nrf/Cargo.toml | 3 | ||||
| -rw-r--r-- | embassy-nrf/src/usb.rs | 487 | ||||
| -rw-r--r-- | embassy-usb/Cargo.toml | 12 | ||||
| -rw-r--r-- | embassy-usb/src/builder.rs | 347 | ||||
| -rw-r--r-- | embassy-usb/src/control.rs | 134 | ||||
| -rw-r--r-- | embassy-usb/src/descriptor.rs | 387 | ||||
| -rw-r--r-- | embassy-usb/src/driver.rs | 160 | ||||
| -rw-r--r-- | embassy-usb/src/fmt.rs | 225 | ||||
| -rw-r--r-- | embassy-usb/src/lib.rs | 342 | ||||
| -rw-r--r-- | embassy-usb/src/types.rs | 138 | ||||
| -rw-r--r-- | examples/nrf/Cargo.toml | 5 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb/cdc_acm.rs | 356 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb/main.rs | 53 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb_uart.rs | 89 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb_uart_io.rs | 66 |
15 files changed, 2621 insertions, 183 deletions
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 5e69a8878..36c61c651 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml | |||
| @@ -64,6 +64,7 @@ _gpio-p1 = [] | |||
| 64 | embassy = { version = "0.1.0", path = "../embassy" } | 64 | embassy = { version = "0.1.0", path = "../embassy" } |
| 65 | embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["nrf"]} | 65 | embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["nrf"]} |
| 66 | embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } | 66 | embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } |
| 67 | embassy-usb = {version = "0.1.0", path = "../embassy-usb" } | ||
| 67 | 68 | ||
| 68 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 69 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 69 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.7", git = "https://github.com/embassy-rs/embedded-hal", branch = "embassy2", optional = true} | 70 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.7", git = "https://github.com/embassy-rs/embedded-hal", branch = "embassy2", optional = true} |
| @@ -80,8 +81,6 @@ rand_core = "0.6.3" | |||
| 80 | fixed = "1.10.0" | 81 | fixed = "1.10.0" |
| 81 | embedded-storage = "0.3.0" | 82 | embedded-storage = "0.3.0" |
| 82 | cfg-if = "1.0.0" | 83 | cfg-if = "1.0.0" |
| 83 | nrf-usbd = {version = "0.1.1"} | ||
| 84 | usb-device = "0.2.8" | ||
| 85 | 84 | ||
| 86 | nrf52805-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } | 85 | nrf52805-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } |
| 87 | nrf52810-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } | 86 | nrf52810-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } |
diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index deab94544..0f7d68d8c 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs | |||
| @@ -1,43 +1,423 @@ | |||
| 1 | #![macro_use] | 1 | #![macro_use] |
| 2 | 2 | ||
| 3 | use crate::interrupt::Interrupt; | ||
| 4 | use crate::pac; | ||
| 5 | |||
| 6 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 5 | use core::task::Poll; | ||
| 6 | use embassy::interrupt::InterruptExt; | ||
| 7 | use embassy::time::{with_timeout, Duration}; | ||
| 7 | use embassy::util::Unborrow; | 8 | use embassy::util::Unborrow; |
| 8 | use nrf_usbd::{UsbPeripheral, Usbd}; | 9 | use embassy::waitqueue::AtomicWaker; |
| 9 | use usb_device::bus::UsbBusAllocator; | 10 | use embassy_hal_common::unborrow; |
| 11 | use embassy_usb::driver::{self, ReadError, WriteError}; | ||
| 12 | use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; | ||
| 13 | use futures::future::poll_fn; | ||
| 14 | use futures::Future; | ||
| 15 | use pac::NVIC; | ||
| 16 | |||
| 17 | pub use embassy_usb; | ||
| 18 | |||
| 19 | use crate::interrupt::Interrupt; | ||
| 20 | use crate::pac; | ||
| 10 | 21 | ||
| 11 | pub use embassy_hal_common::usb::*; | 22 | static EP0_WAKER: AtomicWaker = AtomicWaker::new(); |
| 12 | 23 | ||
| 13 | pub struct UsbBus<'d, T: Instance> { | 24 | pub struct Driver<'d, T: Instance> { |
| 14 | phantom: PhantomData<&'d mut T>, | 25 | phantom: PhantomData<&'d mut T>, |
| 26 | alloc_in: Allocator, | ||
| 27 | alloc_out: Allocator, | ||
| 15 | } | 28 | } |
| 16 | 29 | ||
| 17 | unsafe impl<'d, T: Instance> UsbPeripheral for UsbBus<'d, T> { | 30 | impl<'d, T: Instance> Driver<'d, T> { |
| 18 | // todo how to use T::regs | 31 | pub fn new( |
| 19 | const REGISTERS: *const () = pac::USBD::ptr() as *const (); | 32 | _usb: impl Unborrow<Target = T> + 'd, |
| 33 | irq: impl Unborrow<Target = T::Interrupt> + 'd, | ||
| 34 | ) -> Self { | ||
| 35 | unborrow!(irq); | ||
| 36 | irq.set_handler(Self::on_interrupt); | ||
| 37 | irq.unpend(); | ||
| 38 | irq.enable(); | ||
| 39 | |||
| 40 | Self { | ||
| 41 | phantom: PhantomData, | ||
| 42 | alloc_in: Allocator::new(), | ||
| 43 | alloc_out: Allocator::new(), | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | fn on_interrupt(_: *mut ()) { | ||
| 48 | let regs = T::regs(); | ||
| 49 | |||
| 50 | if regs.events_ep0setup.read().bits() != 0 { | ||
| 51 | regs.intenclr.write(|w| w.ep0setup().clear()); | ||
| 52 | EP0_WAKER.wake(); | ||
| 53 | } | ||
| 54 | if regs.events_ep0datadone.read().bits() != 0 { | ||
| 55 | regs.intenclr.write(|w| w.ep0datadone().clear()); | ||
| 56 | EP0_WAKER.wake(); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | fn set_stalled(ep_addr: EndpointAddress, stalled: bool) { | ||
| 61 | let regs = T::regs(); | ||
| 62 | |||
| 63 | unsafe { | ||
| 64 | if ep_addr.index() == 0 { | ||
| 65 | regs.tasks_ep0stall | ||
| 66 | .write(|w| w.tasks_ep0stall().bit(stalled)); | ||
| 67 | } else { | ||
| 68 | regs.epstall.write(|w| { | ||
| 69 | w.ep().bits(ep_addr.index() as u8 & 0b111); | ||
| 70 | w.io().bit(ep_addr.is_in()); | ||
| 71 | w.stall().bit(stalled) | ||
| 72 | }); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | //if stalled { | ||
| 77 | // self.busy_in_endpoints &= !(1 << ep_addr.index()); | ||
| 78 | //} | ||
| 79 | } | ||
| 80 | |||
| 81 | fn is_stalled(ep_addr: EndpointAddress) -> bool { | ||
| 82 | let regs = T::regs(); | ||
| 83 | |||
| 84 | let i = ep_addr.index(); | ||
| 85 | match ep_addr.direction() { | ||
| 86 | UsbDirection::Out => regs.halted.epout[i].read().getstatus().is_halted(), | ||
| 87 | UsbDirection::In => regs.halted.epin[i].read().getstatus().is_halted(), | ||
| 88 | } | ||
| 89 | } | ||
| 20 | } | 90 | } |
| 21 | 91 | ||
| 22 | impl<'d, T: Instance> UsbBus<'d, T> { | 92 | impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { |
| 23 | pub fn new(_usb: impl Unborrow<Target = T> + 'd) -> UsbBusAllocator<Usbd<UsbBus<'d, T>>> { | 93 | type EndpointOut = Endpoint<'d, T, Out>; |
| 24 | let r = T::regs(); | 94 | type EndpointIn = Endpoint<'d, T, In>; |
| 95 | type Bus = Bus<'d, T>; | ||
| 25 | 96 | ||
| 26 | r.intenset.write(|w| { | 97 | fn alloc_endpoint_in( |
| 27 | w.sof().set_bit(); | 98 | &mut self, |
| 28 | w.usbevent().set_bit(); | 99 | ep_addr: Option<EndpointAddress>, |
| 29 | w.ep0datadone().set_bit(); | 100 | ep_type: EndpointType, |
| 30 | w.ep0setup().set_bit(); | 101 | max_packet_size: u16, |
| 31 | w.usbreset().set_bit() | 102 | interval: u8, |
| 32 | }); | 103 | ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { |
| 104 | let index = self | ||
| 105 | .alloc_in | ||
| 106 | .allocate(ep_addr, ep_type, max_packet_size, interval)?; | ||
| 107 | let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In); | ||
| 108 | Ok(Endpoint { | ||
| 109 | _phantom: PhantomData, | ||
| 110 | info: EndpointInfo { | ||
| 111 | addr: ep_addr, | ||
| 112 | ep_type, | ||
| 113 | max_packet_size, | ||
| 114 | interval, | ||
| 115 | }, | ||
| 116 | }) | ||
| 117 | } | ||
| 33 | 118 | ||
| 34 | Usbd::new(UsbBus { | 119 | fn alloc_endpoint_out( |
| 35 | phantom: PhantomData, | 120 | &mut self, |
| 121 | ep_addr: Option<EndpointAddress>, | ||
| 122 | ep_type: EndpointType, | ||
| 123 | max_packet_size: u16, | ||
| 124 | interval: u8, | ||
| 125 | ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { | ||
| 126 | let index = self | ||
| 127 | .alloc_out | ||
| 128 | .allocate(ep_addr, ep_type, max_packet_size, interval)?; | ||
| 129 | let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out); | ||
| 130 | Ok(Endpoint { | ||
| 131 | _phantom: PhantomData, | ||
| 132 | info: EndpointInfo { | ||
| 133 | addr: ep_addr, | ||
| 134 | ep_type, | ||
| 135 | max_packet_size, | ||
| 136 | interval, | ||
| 137 | }, | ||
| 36 | }) | 138 | }) |
| 37 | } | 139 | } |
| 140 | |||
| 141 | fn enable(self) -> Self::Bus { | ||
| 142 | let regs = T::regs(); | ||
| 143 | |||
| 144 | errata::pre_enable(); | ||
| 145 | |||
| 146 | regs.enable.write(|w| w.enable().enabled()); | ||
| 147 | |||
| 148 | // Wait until the peripheral is ready. | ||
| 149 | while !regs.eventcause.read().ready().is_ready() {} | ||
| 150 | regs.eventcause.write(|w| w.ready().set_bit()); // Write 1 to clear. | ||
| 151 | |||
| 152 | errata::post_enable(); | ||
| 153 | |||
| 154 | unsafe { NVIC::unmask(pac::Interrupt::USBD) }; | ||
| 155 | |||
| 156 | // Enable the USB pullup, allowing enumeration. | ||
| 157 | regs.usbpullup.write(|w| w.connect().enabled()); | ||
| 158 | info!("enabled"); | ||
| 159 | |||
| 160 | Bus { | ||
| 161 | phantom: PhantomData, | ||
| 162 | alloc_in: self.alloc_in, | ||
| 163 | alloc_out: self.alloc_out, | ||
| 164 | } | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | pub struct Bus<'d, T: Instance> { | ||
| 169 | phantom: PhantomData<&'d mut T>, | ||
| 170 | alloc_in: Allocator, | ||
| 171 | alloc_out: Allocator, | ||
| 172 | } | ||
| 173 | |||
| 174 | impl<'d, T: Instance> driver::Bus for Bus<'d, T> { | ||
| 175 | #[inline] | ||
| 176 | fn reset(&mut self) { | ||
| 177 | let regs = T::regs(); | ||
| 178 | |||
| 179 | // TODO: Initialize ISO buffers | ||
| 180 | |||
| 181 | // XXX this is not spec compliant; the endpoints should only be enabled after the device | ||
| 182 | // has been put in the Configured state. However, usb-device provides no hook to do that | ||
| 183 | unsafe { | ||
| 184 | regs.epinen.write(|w| w.bits(self.alloc_in.used.into())); | ||
| 185 | regs.epouten.write(|w| w.bits(self.alloc_out.used.into())); | ||
| 186 | } | ||
| 187 | |||
| 188 | for i in 1..8 { | ||
| 189 | let out_enabled = self.alloc_out.used & (1 << i) != 0; | ||
| 190 | |||
| 191 | // when first enabled, bulk/interrupt OUT endpoints will *not* receive data (the | ||
| 192 | // peripheral will NAK all incoming packets) until we write a zero to the SIZE | ||
| 193 | // register (see figure 203 of the 52840 manual). To avoid that we write a 0 to the | ||
| 194 | // SIZE register | ||
| 195 | if out_enabled { | ||
| 196 | regs.size.epout[i].reset(); | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | //self.busy_in_endpoints = 0; | ||
| 201 | } | ||
| 202 | |||
| 203 | #[inline] | ||
| 204 | fn set_device_address(&mut self, _addr: u8) { | ||
| 205 | // Nothing to do, the peripheral handles this. | ||
| 206 | } | ||
| 207 | |||
| 208 | fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { | ||
| 209 | Driver::<T>::set_stalled(ep_addr, stalled) | ||
| 210 | } | ||
| 211 | |||
| 212 | fn is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { | ||
| 213 | Driver::<T>::is_stalled(ep_addr) | ||
| 214 | } | ||
| 215 | |||
| 216 | #[inline] | ||
| 217 | fn suspend(&mut self) { | ||
| 218 | let regs = T::regs(); | ||
| 219 | regs.lowpower.write(|w| w.lowpower().low_power()); | ||
| 220 | } | ||
| 221 | |||
| 222 | #[inline] | ||
| 223 | fn resume(&mut self) { | ||
| 224 | let regs = T::regs(); | ||
| 225 | |||
| 226 | errata::pre_wakeup(); | ||
| 227 | |||
| 228 | regs.lowpower.write(|w| w.lowpower().force_normal()); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | pub enum Out {} | ||
| 233 | pub enum In {} | ||
| 234 | |||
| 235 | pub struct Endpoint<'d, T: Instance, Dir> { | ||
| 236 | _phantom: PhantomData<(&'d mut T, Dir)>, | ||
| 237 | info: EndpointInfo, | ||
| 238 | } | ||
| 239 | |||
| 240 | impl<'d, T: Instance, Dir> driver::Endpoint for Endpoint<'d, T, Dir> { | ||
| 241 | fn info(&self) -> &EndpointInfo { | ||
| 242 | &self.info | ||
| 243 | } | ||
| 244 | |||
| 245 | fn set_stalled(&self, stalled: bool) { | ||
| 246 | Driver::<T>::set_stalled(self.info.addr, stalled) | ||
| 247 | } | ||
| 248 | |||
| 249 | fn is_stalled(&self) -> bool { | ||
| 250 | Driver::<T>::is_stalled(self.info.addr) | ||
| 251 | } | ||
| 38 | } | 252 | } |
| 39 | 253 | ||
| 40 | unsafe impl embassy_hal_common::usb::USBInterrupt for crate::interrupt::USBD {} | 254 | impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { |
| 255 | type ReadFuture<'a> | ||
| 256 | where | ||
| 257 | Self: 'a, | ||
| 258 | = impl Future<Output = Result<usize, ReadError>> + 'a; | ||
| 259 | |||
| 260 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||
| 261 | async move { | ||
| 262 | let regs = T::regs(); | ||
| 263 | |||
| 264 | if buf.len() == 0 { | ||
| 265 | regs.tasks_ep0status.write(|w| unsafe { w.bits(1) }); | ||
| 266 | return Ok(0); | ||
| 267 | } | ||
| 268 | |||
| 269 | // Wait for SETUP packet | ||
| 270 | regs.events_ep0setup.reset(); | ||
| 271 | regs.intenset.write(|w| w.ep0setup().set()); | ||
| 272 | poll_fn(|cx| { | ||
| 273 | EP0_WAKER.register(cx.waker()); | ||
| 274 | if regs.events_ep0setup.read().bits() != 0 { | ||
| 275 | Poll::Ready(()) | ||
| 276 | } else { | ||
| 277 | Poll::Pending | ||
| 278 | } | ||
| 279 | }) | ||
| 280 | .await; | ||
| 281 | info!("got SETUP"); | ||
| 282 | |||
| 283 | if buf.len() < 8 { | ||
| 284 | return Err(ReadError::BufferOverflow); | ||
| 285 | } | ||
| 286 | |||
| 287 | buf[0] = regs.bmrequesttype.read().bits() as u8; | ||
| 288 | buf[1] = regs.brequest.read().brequest().bits(); | ||
| 289 | buf[2] = regs.wvaluel.read().wvaluel().bits(); | ||
| 290 | buf[3] = regs.wvalueh.read().wvalueh().bits(); | ||
| 291 | buf[4] = regs.windexl.read().windexl().bits(); | ||
| 292 | buf[5] = regs.windexh.read().windexh().bits(); | ||
| 293 | buf[6] = regs.wlengthl.read().wlengthl().bits(); | ||
| 294 | buf[7] = regs.wlengthh.read().wlengthh().bits(); | ||
| 295 | |||
| 296 | Ok(8) | ||
| 297 | } | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { | ||
| 302 | type WriteFuture<'a> | ||
| 303 | where | ||
| 304 | Self: 'a, | ||
| 305 | = impl Future<Output = Result<(), WriteError>> + 'a; | ||
| 306 | |||
| 307 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | ||
| 308 | async move { | ||
| 309 | info!("write: {:x}", buf); | ||
| 310 | |||
| 311 | let regs = T::regs(); | ||
| 312 | |||
| 313 | let ptr = buf.as_ptr() as u32; | ||
| 314 | let len = buf.len() as u32; | ||
| 315 | regs.epin0.ptr.write(|w| unsafe { w.bits(ptr) }); | ||
| 316 | regs.epin0.maxcnt.write(|w| unsafe { w.bits(len) }); | ||
| 317 | |||
| 318 | regs.events_ep0datadone.reset(); | ||
| 319 | regs.events_endepin[0].reset(); | ||
| 320 | |||
| 321 | dma_start(); | ||
| 322 | |||
| 323 | regs.tasks_startepin[0].write(|w| unsafe { w.bits(1) }); | ||
| 324 | info!("write: waiting for endepin..."); | ||
| 325 | while regs.events_endepin[0].read().bits() == 0 {} | ||
| 326 | |||
| 327 | dma_end(); | ||
| 328 | |||
| 329 | info!("write: waiting for ep0datadone..."); | ||
| 330 | regs.intenset.write(|w| w.ep0datadone().set()); | ||
| 331 | let res = with_timeout( | ||
| 332 | Duration::from_millis(10), | ||
| 333 | poll_fn(|cx| { | ||
| 334 | EP0_WAKER.register(cx.waker()); | ||
| 335 | if regs.events_ep0datadone.read().bits() != 0 { | ||
| 336 | Poll::Ready(()) | ||
| 337 | } else { | ||
| 338 | Poll::Pending | ||
| 339 | } | ||
| 340 | }), | ||
| 341 | ) | ||
| 342 | .await; | ||
| 343 | |||
| 344 | if res.is_err() { | ||
| 345 | // todo wrong error | ||
| 346 | return Err(driver::WriteError::BufferOverflow); | ||
| 347 | } | ||
| 348 | |||
| 349 | info!("write done"); | ||
| 350 | |||
| 351 | Ok(()) | ||
| 352 | } | ||
| 353 | } | ||
| 354 | } | ||
| 355 | |||
| 356 | fn dma_start() { | ||
| 357 | compiler_fence(Ordering::Release); | ||
| 358 | } | ||
| 359 | |||
| 360 | fn dma_end() { | ||
| 361 | compiler_fence(Ordering::Acquire); | ||
| 362 | } | ||
| 363 | |||
| 364 | struct Allocator { | ||
| 365 | used: u16, | ||
| 366 | // Buffers can be up to 64 Bytes since this is a Full-Speed implementation. | ||
| 367 | lens: [u8; 9], | ||
| 368 | } | ||
| 369 | |||
| 370 | impl Allocator { | ||
| 371 | fn new() -> Self { | ||
| 372 | Self { | ||
| 373 | used: 0, | ||
| 374 | lens: [0; 9], | ||
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 378 | fn allocate( | ||
| 379 | &mut self, | ||
| 380 | ep_addr: Option<EndpointAddress>, | ||
| 381 | ep_type: EndpointType, | ||
| 382 | max_packet_size: u16, | ||
| 383 | _interval: u8, | ||
| 384 | ) -> Result<usize, driver::EndpointAllocError> { | ||
| 385 | // Endpoint addresses are fixed in hardware: | ||
| 386 | // - 0x80 / 0x00 - Control EP0 | ||
| 387 | // - 0x81 / 0x01 - Bulk/Interrupt EP1 | ||
| 388 | // - 0x82 / 0x02 - Bulk/Interrupt EP2 | ||
| 389 | // - 0x83 / 0x03 - Bulk/Interrupt EP3 | ||
| 390 | // - 0x84 / 0x04 - Bulk/Interrupt EP4 | ||
| 391 | // - 0x85 / 0x05 - Bulk/Interrupt EP5 | ||
| 392 | // - 0x86 / 0x06 - Bulk/Interrupt EP6 | ||
| 393 | // - 0x87 / 0x07 - Bulk/Interrupt EP7 | ||
| 394 | // - 0x88 / 0x08 - Isochronous | ||
| 395 | |||
| 396 | // Endpoint directions are allocated individually. | ||
| 397 | |||
| 398 | let alloc_index = match ep_type { | ||
| 399 | EndpointType::Isochronous => 8, | ||
| 400 | EndpointType::Control => 0, | ||
| 401 | EndpointType::Interrupt | EndpointType::Bulk => { | ||
| 402 | // Find rightmost zero bit in 1..=7 | ||
| 403 | let ones = (self.used >> 1).trailing_ones() as usize; | ||
| 404 | if ones >= 7 { | ||
| 405 | return Err(driver::EndpointAllocError); | ||
| 406 | } | ||
| 407 | ones + 1 | ||
| 408 | } | ||
| 409 | }; | ||
| 410 | |||
| 411 | if self.used & (1 << alloc_index) != 0 { | ||
| 412 | return Err(driver::EndpointAllocError); | ||
| 413 | } | ||
| 414 | |||
| 415 | self.used |= 1 << alloc_index; | ||
| 416 | self.lens[alloc_index] = max_packet_size as u8; | ||
| 417 | |||
| 418 | Ok(alloc_index) | ||
| 419 | } | ||
| 420 | } | ||
| 41 | 421 | ||
| 42 | pub(crate) mod sealed { | 422 | pub(crate) mod sealed { |
| 43 | use super::*; | 423 | use super::*; |
| @@ -63,3 +443,64 @@ macro_rules! impl_usb { | |||
| 63 | } | 443 | } |
| 64 | }; | 444 | }; |
| 65 | } | 445 | } |
| 446 | |||
| 447 | mod errata { | ||
| 448 | |||
| 449 | /// Writes `val` to `addr`. Used to apply Errata workarounds. | ||
| 450 | unsafe fn poke(addr: u32, val: u32) { | ||
| 451 | (addr as *mut u32).write_volatile(val); | ||
| 452 | } | ||
| 453 | |||
| 454 | /// Reads 32 bits from `addr`. | ||
| 455 | unsafe fn peek(addr: u32) -> u32 { | ||
| 456 | (addr as *mut u32).read_volatile() | ||
| 457 | } | ||
| 458 | |||
| 459 | pub fn pre_enable() { | ||
| 460 | // Works around Erratum 187 on chip revisions 1 and 2. | ||
| 461 | unsafe { | ||
| 462 | poke(0x4006EC00, 0x00009375); | ||
| 463 | poke(0x4006ED14, 0x00000003); | ||
| 464 | poke(0x4006EC00, 0x00009375); | ||
| 465 | } | ||
| 466 | |||
| 467 | pre_wakeup(); | ||
| 468 | } | ||
| 469 | |||
| 470 | pub fn post_enable() { | ||
| 471 | post_wakeup(); | ||
| 472 | |||
| 473 | // Works around Erratum 187 on chip revisions 1 and 2. | ||
| 474 | unsafe { | ||
| 475 | poke(0x4006EC00, 0x00009375); | ||
| 476 | poke(0x4006ED14, 0x00000000); | ||
| 477 | poke(0x4006EC00, 0x00009375); | ||
| 478 | } | ||
| 479 | } | ||
| 480 | |||
| 481 | pub fn pre_wakeup() { | ||
| 482 | // Works around Erratum 171 on chip revisions 1 and 2. | ||
| 483 | |||
| 484 | unsafe { | ||
| 485 | if peek(0x4006EC00) == 0x00000000 { | ||
| 486 | poke(0x4006EC00, 0x00009375); | ||
| 487 | } | ||
| 488 | |||
| 489 | poke(0x4006EC14, 0x000000C0); | ||
| 490 | poke(0x4006EC00, 0x00009375); | ||
| 491 | } | ||
| 492 | } | ||
| 493 | |||
| 494 | pub fn post_wakeup() { | ||
| 495 | // Works around Erratum 171 on chip revisions 1 and 2. | ||
| 496 | |||
| 497 | unsafe { | ||
| 498 | if peek(0x4006EC00) == 0x00000000 { | ||
| 499 | poke(0x4006EC00, 0x00009375); | ||
| 500 | } | ||
| 501 | |||
| 502 | poke(0x4006EC14, 0x00000000); | ||
| 503 | poke(0x4006EC00, 0x00009375); | ||
| 504 | } | ||
| 505 | } | ||
| 506 | } | ||
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml new file mode 100644 index 000000000..dfdc8fbac --- /dev/null +++ b/embassy-usb/Cargo.toml | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | [package] | ||
| 2 | name = "embassy-usb" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2018" | ||
| 5 | |||
| 6 | [dependencies] | ||
| 7 | embassy = { version = "0.1.0", path = "../embassy" } | ||
| 8 | |||
| 9 | defmt = { version = "0.3", optional = true } | ||
| 10 | log = { version = "0.4.14", optional = true } | ||
| 11 | cortex-m = "0.7.3" | ||
| 12 | num-traits = { version = "0.2.14", default-features = false } | ||
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs new file mode 100644 index 000000000..e92cc8ef2 --- /dev/null +++ b/embassy-usb/src/builder.rs | |||
| @@ -0,0 +1,347 @@ | |||
| 1 | use super::descriptor::{BosWriter, DescriptorWriter}; | ||
| 2 | use super::driver::{Driver, EndpointAllocError}; | ||
| 3 | use super::types::*; | ||
| 4 | use super::UsbDevice; | ||
| 5 | |||
| 6 | #[derive(Debug, Copy, Clone)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | #[non_exhaustive] | ||
| 9 | pub struct Config<'a> { | ||
| 10 | pub(crate) vendor_id: u16, | ||
| 11 | pub(crate) product_id: u16, | ||
| 12 | |||
| 13 | /// Device class code assigned by USB.org. Set to `0xff` for vendor-specific | ||
| 14 | /// devices that do not conform to any class. | ||
| 15 | /// | ||
| 16 | /// Default: `0x00` (class code specified by interfaces) | ||
| 17 | pub device_class: u8, | ||
| 18 | |||
| 19 | /// Device sub-class code. Depends on class. | ||
| 20 | /// | ||
| 21 | /// Default: `0x00` | ||
| 22 | pub device_sub_class: u8, | ||
| 23 | |||
| 24 | /// Device protocol code. Depends on class and sub-class. | ||
| 25 | /// | ||
| 26 | /// Default: `0x00` | ||
| 27 | pub device_protocol: u8, | ||
| 28 | |||
| 29 | /// Device release version in BCD. | ||
| 30 | /// | ||
| 31 | /// Default: `0x0010` ("0.1") | ||
| 32 | pub device_release: u16, | ||
| 33 | |||
| 34 | /// Maximum packet size in bytes for the control endpoint 0. | ||
| 35 | /// | ||
| 36 | /// Valid values are 8, 16, 32 and 64. There's generally no need to change this from the default | ||
| 37 | /// value of 8 bytes unless a class uses control transfers for sending large amounts of data, in | ||
| 38 | /// which case using a larger packet size may be more efficient. | ||
| 39 | /// | ||
| 40 | /// Default: 8 bytes | ||
| 41 | pub max_packet_size_0: u8, | ||
| 42 | |||
| 43 | /// Manufacturer name string descriptor. | ||
| 44 | /// | ||
| 45 | /// Default: (none) | ||
| 46 | pub manufacturer: Option<&'a str>, | ||
| 47 | |||
| 48 | /// Product name string descriptor. | ||
| 49 | /// | ||
| 50 | /// Default: (none) | ||
| 51 | pub product: Option<&'a str>, | ||
| 52 | |||
| 53 | /// Serial number string descriptor. | ||
| 54 | /// | ||
| 55 | /// Default: (none) | ||
| 56 | pub serial_number: Option<&'a str>, | ||
| 57 | |||
| 58 | /// Whether the device supports remotely waking up the host is requested. | ||
| 59 | /// | ||
| 60 | /// Default: `false` | ||
| 61 | pub supports_remote_wakeup: bool, | ||
| 62 | |||
| 63 | /// Configures the device as a composite device with interface association descriptors. | ||
| 64 | /// | ||
| 65 | /// If set to `true`, the following fields should have the given values: | ||
| 66 | /// | ||
| 67 | /// - `device_class` = `0xEF` | ||
| 68 | /// - `device_sub_class` = `0x02` | ||
| 69 | /// - `device_protocol` = `0x01` | ||
| 70 | pub composite_with_iads: bool, | ||
| 71 | |||
| 72 | /// Whether the device has its own power source. | ||
| 73 | /// | ||
| 74 | /// This should be set to `true` even if the device is sometimes self-powered and may not | ||
| 75 | /// always draw power from the USB bus. | ||
| 76 | /// | ||
| 77 | /// Default: `false` | ||
| 78 | /// | ||
| 79 | /// See also: `max_power` | ||
| 80 | pub self_powered: bool, | ||
| 81 | |||
| 82 | /// Maximum current drawn from the USB bus by the device, in milliamps. | ||
| 83 | /// | ||
| 84 | /// The default is 100 mA. If your device always uses an external power source and never draws | ||
| 85 | /// power from the USB bus, this can be set to 0. | ||
| 86 | /// | ||
| 87 | /// See also: `self_powered` | ||
| 88 | /// | ||
| 89 | /// Default: 100mA | ||
| 90 | /// Max: 500mA | ||
| 91 | pub max_power: u16, | ||
| 92 | } | ||
| 93 | |||
| 94 | impl<'a> Config<'a> { | ||
| 95 | pub fn new(vid: u16, pid: u16) -> Self { | ||
| 96 | Self { | ||
| 97 | device_class: 0x00, | ||
| 98 | device_sub_class: 0x00, | ||
| 99 | device_protocol: 0x00, | ||
| 100 | max_packet_size_0: 8, | ||
| 101 | vendor_id: vid, | ||
| 102 | product_id: pid, | ||
| 103 | device_release: 0x0010, | ||
| 104 | manufacturer: None, | ||
| 105 | product: None, | ||
| 106 | serial_number: None, | ||
| 107 | self_powered: false, | ||
| 108 | supports_remote_wakeup: false, | ||
| 109 | composite_with_iads: false, | ||
| 110 | max_power: 100, | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | /// Used to build new [`UsbDevice`]s. | ||
| 116 | pub struct UsbDeviceBuilder<'d, D: Driver<'d>> { | ||
| 117 | config: Config<'d>, | ||
| 118 | |||
| 119 | bus: D, | ||
| 120 | next_interface_number: u8, | ||
| 121 | next_string_index: u8, | ||
| 122 | |||
| 123 | // TODO make not pub? | ||
| 124 | pub device_descriptor: DescriptorWriter<'d>, | ||
| 125 | pub config_descriptor: DescriptorWriter<'d>, | ||
| 126 | pub bos_descriptor: BosWriter<'d>, | ||
| 127 | } | ||
| 128 | |||
| 129 | impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> { | ||
| 130 | /// Creates a builder for constructing a new [`UsbDevice`]. | ||
| 131 | pub fn new( | ||
| 132 | bus: D, | ||
| 133 | config: Config<'d>, | ||
| 134 | device_descriptor_buf: &'d mut [u8], | ||
| 135 | config_descriptor_buf: &'d mut [u8], | ||
| 136 | bos_descriptor_buf: &'d mut [u8], | ||
| 137 | ) -> Self { | ||
| 138 | // Magic values specified in USB-IF ECN on IADs. | ||
| 139 | if config.composite_with_iads | ||
| 140 | && (config.device_class != 0xEF | ||
| 141 | || config.device_sub_class != 0x02 | ||
| 142 | || config.device_protocol != 0x01) | ||
| 143 | { | ||
| 144 | panic!("if composite_with_iads is set, you must set device_class = 0xEF, device_sub_class = 0x02, device_protocol = 0x01"); | ||
| 145 | } | ||
| 146 | |||
| 147 | if config.max_power > 500 { | ||
| 148 | panic!("The maximum allowed value for `max_power` is 500mA"); | ||
| 149 | } | ||
| 150 | |||
| 151 | match config.max_packet_size_0 { | ||
| 152 | 8 | 16 | 32 | 64 => {} | ||
| 153 | _ => panic!("invalid max_packet_size_0, the allowed values are 8, 16, 32 or 64"), | ||
| 154 | } | ||
| 155 | |||
| 156 | let mut device_descriptor = DescriptorWriter::new(device_descriptor_buf); | ||
| 157 | let mut config_descriptor = DescriptorWriter::new(config_descriptor_buf); | ||
| 158 | let mut bos_descriptor = BosWriter::new(DescriptorWriter::new(bos_descriptor_buf)); | ||
| 159 | |||
| 160 | device_descriptor.device(&config).unwrap(); | ||
| 161 | config_descriptor.configuration(&config).unwrap(); | ||
| 162 | bos_descriptor.bos().unwrap(); | ||
| 163 | |||
| 164 | UsbDeviceBuilder { | ||
| 165 | bus, | ||
| 166 | config, | ||
| 167 | next_interface_number: 0, | ||
| 168 | next_string_index: 4, | ||
| 169 | |||
| 170 | device_descriptor, | ||
| 171 | config_descriptor, | ||
| 172 | bos_descriptor, | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Creates the [`UsbDevice`] instance with the configuration in this builder. | ||
| 177 | pub fn build(mut self) -> UsbDevice<'d, D> { | ||
| 178 | self.config_descriptor.end_configuration(); | ||
| 179 | self.bos_descriptor.end_bos(); | ||
| 180 | |||
| 181 | UsbDevice::build( | ||
| 182 | self.bus, | ||
| 183 | self.config, | ||
| 184 | self.device_descriptor.into_buf(), | ||
| 185 | self.config_descriptor.into_buf(), | ||
| 186 | self.bos_descriptor.writer.into_buf(), | ||
| 187 | ) | ||
| 188 | } | ||
| 189 | |||
| 190 | /// Allocates a new interface number. | ||
| 191 | pub fn alloc_interface(&mut self) -> InterfaceNumber { | ||
| 192 | let number = self.next_interface_number; | ||
| 193 | self.next_interface_number += 1; | ||
| 194 | |||
| 195 | InterfaceNumber::new(number) | ||
| 196 | } | ||
| 197 | |||
| 198 | /// Allocates a new string index. | ||
| 199 | pub fn alloc_string(&mut self) -> StringIndex { | ||
| 200 | let index = self.next_string_index; | ||
| 201 | self.next_string_index += 1; | ||
| 202 | |||
| 203 | StringIndex::new(index) | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Allocates an in endpoint. | ||
| 207 | /// | ||
| 208 | /// This directly delegates to [`Driver::alloc_endpoint_in`], so see that method for details. In most | ||
| 209 | /// cases classes should call the endpoint type specific methods instead. | ||
| 210 | pub fn alloc_endpoint_in( | ||
| 211 | &mut self, | ||
| 212 | ep_addr: Option<EndpointAddress>, | ||
| 213 | ep_type: EndpointType, | ||
| 214 | max_packet_size: u16, | ||
| 215 | interval: u8, | ||
| 216 | ) -> Result<D::EndpointIn, EndpointAllocError> { | ||
| 217 | self.bus | ||
| 218 | .alloc_endpoint_in(ep_addr, ep_type, max_packet_size, interval) | ||
| 219 | } | ||
| 220 | |||
| 221 | /// Allocates an out endpoint. | ||
| 222 | /// | ||
| 223 | /// This directly delegates to [`Driver::alloc_endpoint_out`], so see that method for details. In most | ||
| 224 | /// cases classes should call the endpoint type specific methods instead. | ||
| 225 | pub fn alloc_endpoint_out( | ||
| 226 | &mut self, | ||
| 227 | ep_addr: Option<EndpointAddress>, | ||
| 228 | ep_type: EndpointType, | ||
| 229 | max_packet_size: u16, | ||
| 230 | interval: u8, | ||
| 231 | ) -> Result<D::EndpointOut, EndpointAllocError> { | ||
| 232 | self.bus | ||
| 233 | .alloc_endpoint_out(ep_addr, ep_type, max_packet_size, interval) | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Allocates a control in endpoint. | ||
| 237 | /// | ||
| 238 | /// This crate implements the control state machine only for endpoint 0. If classes want to | ||
| 239 | /// support control requests in other endpoints, the state machine must be implemented manually. | ||
| 240 | /// This should rarely be needed by classes. | ||
| 241 | /// | ||
| 242 | /// # Arguments | ||
| 243 | /// | ||
| 244 | /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64. | ||
| 245 | /// | ||
| 246 | /// # Panics | ||
| 247 | /// | ||
| 248 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 249 | /// feasibly recoverable. | ||
| 250 | #[inline] | ||
| 251 | pub fn alloc_control_endpoint_in(&mut self, max_packet_size: u16) -> D::EndpointIn { | ||
| 252 | self.alloc_endpoint_in(None, EndpointType::Control, max_packet_size, 0) | ||
| 253 | .expect("alloc_ep failed") | ||
| 254 | } | ||
| 255 | |||
| 256 | /// Allocates a control out endpoint. | ||
| 257 | /// | ||
| 258 | /// This crate implements the control state machine only for endpoint 0. If classes want to | ||
| 259 | /// support control requests in other endpoints, the state machine must be implemented manually. | ||
| 260 | /// This should rarely be needed by classes. | ||
| 261 | /// | ||
| 262 | /// # Arguments | ||
| 263 | /// | ||
| 264 | /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64. | ||
| 265 | /// | ||
| 266 | /// # Panics | ||
| 267 | /// | ||
| 268 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 269 | /// feasibly recoverable. | ||
| 270 | #[inline] | ||
| 271 | pub fn alloc_control_endpoint_out(&mut self, max_packet_size: u16) -> D::EndpointOut { | ||
| 272 | self.alloc_endpoint_out(None, EndpointType::Control, max_packet_size, 0) | ||
| 273 | .expect("alloc_ep failed") | ||
| 274 | } | ||
| 275 | |||
| 276 | /// Allocates a bulk in endpoint. | ||
| 277 | /// | ||
| 278 | /// # Arguments | ||
| 279 | /// | ||
| 280 | /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64. | ||
| 281 | /// | ||
| 282 | /// # Panics | ||
| 283 | /// | ||
| 284 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 285 | /// feasibly recoverable. | ||
| 286 | #[inline] | ||
| 287 | pub fn alloc_bulk_endpoint_in(&mut self, max_packet_size: u16) -> D::EndpointIn { | ||
| 288 | self.alloc_endpoint_in(None, EndpointType::Bulk, max_packet_size, 0) | ||
| 289 | .expect("alloc_ep failed") | ||
| 290 | } | ||
| 291 | |||
| 292 | /// Allocates a bulk out endpoint. | ||
| 293 | /// | ||
| 294 | /// # Arguments | ||
| 295 | /// | ||
| 296 | /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64. | ||
| 297 | /// | ||
| 298 | /// # Panics | ||
| 299 | /// | ||
| 300 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 301 | /// feasibly recoverable. | ||
| 302 | #[inline] | ||
| 303 | pub fn alloc_bulk_endpoint_out(&mut self, max_packet_size: u16) -> D::EndpointOut { | ||
| 304 | self.alloc_endpoint_out(None, EndpointType::Bulk, max_packet_size, 0) | ||
| 305 | .expect("alloc_ep failed") | ||
| 306 | } | ||
| 307 | |||
| 308 | /// Allocates a bulk in endpoint. | ||
| 309 | /// | ||
| 310 | /// # Arguments | ||
| 311 | /// | ||
| 312 | /// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes. | ||
| 313 | /// | ||
| 314 | /// # Panics | ||
| 315 | /// | ||
| 316 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 317 | /// feasibly recoverable. | ||
| 318 | #[inline] | ||
| 319 | pub fn alloc_interrupt_endpoint_in( | ||
| 320 | &mut self, | ||
| 321 | max_packet_size: u16, | ||
| 322 | interval: u8, | ||
| 323 | ) -> D::EndpointIn { | ||
| 324 | self.alloc_endpoint_in(None, EndpointType::Interrupt, max_packet_size, interval) | ||
| 325 | .expect("alloc_ep failed") | ||
| 326 | } | ||
| 327 | |||
| 328 | /// Allocates a bulk in endpoint. | ||
| 329 | /// | ||
| 330 | /// # Arguments | ||
| 331 | /// | ||
| 332 | /// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes. | ||
| 333 | /// | ||
| 334 | /// # Panics | ||
| 335 | /// | ||
| 336 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 337 | /// feasibly recoverable. | ||
| 338 | #[inline] | ||
| 339 | pub fn alloc_interrupt_endpoint_out( | ||
| 340 | &mut self, | ||
| 341 | max_packet_size: u16, | ||
| 342 | interval: u8, | ||
| 343 | ) -> D::EndpointOut { | ||
| 344 | self.alloc_endpoint_out(None, EndpointType::Interrupt, max_packet_size, interval) | ||
| 345 | .expect("alloc_ep failed") | ||
| 346 | } | ||
| 347 | } | ||
diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs new file mode 100644 index 000000000..f1148ac76 --- /dev/null +++ b/embassy-usb/src/control.rs | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | use core::mem; | ||
| 2 | |||
| 3 | use super::types::*; | ||
| 4 | |||
| 5 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 6 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 7 | pub enum ParseError { | ||
| 8 | InvalidLength, | ||
| 9 | } | ||
| 10 | |||
| 11 | /// Control request type. | ||
| 12 | #[repr(u8)] | ||
| 13 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 14 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 15 | pub enum RequestType { | ||
| 16 | /// Request is a USB standard request. Usually handled by | ||
| 17 | /// [`UsbDevice`](crate::device::UsbDevice). | ||
| 18 | Standard = 0, | ||
| 19 | /// Request is intended for a USB class. | ||
| 20 | Class = 1, | ||
| 21 | /// Request is vendor-specific. | ||
| 22 | Vendor = 2, | ||
| 23 | /// Reserved. | ||
| 24 | Reserved = 3, | ||
| 25 | } | ||
| 26 | |||
| 27 | /// Control request recipient. | ||
| 28 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 29 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 30 | pub enum Recipient { | ||
| 31 | /// Request is intended for the entire device. | ||
| 32 | Device = 0, | ||
| 33 | /// Request is intended for an interface. Generally, the `index` field of the request specifies | ||
| 34 | /// the interface number. | ||
| 35 | Interface = 1, | ||
| 36 | /// Request is intended for an endpoint. Generally, the `index` field of the request specifies | ||
| 37 | /// the endpoint address. | ||
| 38 | Endpoint = 2, | ||
| 39 | /// None of the above. | ||
| 40 | Other = 3, | ||
| 41 | /// Reserved. | ||
| 42 | Reserved = 4, | ||
| 43 | } | ||
| 44 | |||
| 45 | /// A control request read from a SETUP packet. | ||
| 46 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 47 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 48 | pub struct Request { | ||
| 49 | /// Direction of the request. | ||
| 50 | pub direction: UsbDirection, | ||
| 51 | /// Type of the request. | ||
| 52 | pub request_type: RequestType, | ||
| 53 | /// Recipient of the request. | ||
| 54 | pub recipient: Recipient, | ||
| 55 | /// Request code. The meaning of the value depends on the previous fields. | ||
| 56 | pub request: u8, | ||
| 57 | /// Request value. The meaning of the value depends on the previous fields. | ||
| 58 | pub value: u16, | ||
| 59 | /// Request index. The meaning of the value depends on the previous fields. | ||
| 60 | pub index: u16, | ||
| 61 | /// Length of the DATA stage. For control OUT transfers this is the exact length of the data the | ||
| 62 | /// host sent. For control IN transfers this is the maximum length of data the device should | ||
| 63 | /// return. | ||
| 64 | pub length: u16, | ||
| 65 | } | ||
| 66 | |||
| 67 | impl Request { | ||
| 68 | /// Standard USB control request Get Status | ||
| 69 | pub const GET_STATUS: u8 = 0; | ||
| 70 | |||
| 71 | /// Standard USB control request Clear Feature | ||
| 72 | pub const CLEAR_FEATURE: u8 = 1; | ||
| 73 | |||
| 74 | /// Standard USB control request Set Feature | ||
| 75 | pub const SET_FEATURE: u8 = 3; | ||
| 76 | |||
| 77 | /// Standard USB control request Set Address | ||
| 78 | pub const SET_ADDRESS: u8 = 5; | ||
| 79 | |||
| 80 | /// Standard USB control request Get Descriptor | ||
| 81 | pub const GET_DESCRIPTOR: u8 = 6; | ||
| 82 | |||
| 83 | /// Standard USB control request Set Descriptor | ||
| 84 | pub const SET_DESCRIPTOR: u8 = 7; | ||
| 85 | |||
| 86 | /// Standard USB control request Get Configuration | ||
| 87 | pub const GET_CONFIGURATION: u8 = 8; | ||
| 88 | |||
| 89 | /// Standard USB control request Set Configuration | ||
| 90 | pub const SET_CONFIGURATION: u8 = 9; | ||
| 91 | |||
| 92 | /// Standard USB control request Get Interface | ||
| 93 | pub const GET_INTERFACE: u8 = 10; | ||
| 94 | |||
| 95 | /// Standard USB control request Set Interface | ||
| 96 | pub const SET_INTERFACE: u8 = 11; | ||
| 97 | |||
| 98 | /// Standard USB control request Synch Frame | ||
| 99 | pub const SYNCH_FRAME: u8 = 12; | ||
| 100 | |||
| 101 | /// Standard USB feature Endpoint Halt for Set/Clear Feature | ||
| 102 | pub const FEATURE_ENDPOINT_HALT: u16 = 0; | ||
| 103 | |||
| 104 | /// Standard USB feature Device Remote Wakeup for Set/Clear Feature | ||
| 105 | pub const FEATURE_DEVICE_REMOTE_WAKEUP: u16 = 1; | ||
| 106 | |||
| 107 | pub(crate) fn parse(buf: &[u8]) -> Result<Request, ParseError> { | ||
| 108 | if buf.len() != 8 { | ||
| 109 | return Err(ParseError::InvalidLength); | ||
| 110 | } | ||
| 111 | |||
| 112 | let rt = buf[0]; | ||
| 113 | let recipient = rt & 0b11111; | ||
| 114 | |||
| 115 | Ok(Request { | ||
| 116 | direction: rt.into(), | ||
| 117 | request_type: unsafe { mem::transmute((rt >> 5) & 0b11) }, | ||
| 118 | recipient: if recipient <= 3 { | ||
| 119 | unsafe { mem::transmute(recipient) } | ||
| 120 | } else { | ||
| 121 | Recipient::Reserved | ||
| 122 | }, | ||
| 123 | request: buf[1], | ||
| 124 | value: (buf[2] as u16) | ((buf[3] as u16) << 8), | ||
| 125 | index: (buf[4] as u16) | ((buf[5] as u16) << 8), | ||
| 126 | length: (buf[6] as u16) | ((buf[7] as u16) << 8), | ||
| 127 | }) | ||
| 128 | } | ||
| 129 | |||
| 130 | /// Gets the descriptor type and index from the value field of a GET_DESCRIPTOR request. | ||
| 131 | pub fn descriptor_type_index(&self) -> (u8, u8) { | ||
| 132 | ((self.value >> 8) as u8, self.value as u8) | ||
| 133 | } | ||
| 134 | } | ||
diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs new file mode 100644 index 000000000..746c6b828 --- /dev/null +++ b/embassy-usb/src/descriptor.rs | |||
| @@ -0,0 +1,387 @@ | |||
| 1 | use super::builder::Config; | ||
| 2 | use super::{types::*, CONFIGURATION_VALUE, DEFAULT_ALTERNATE_SETTING}; | ||
| 3 | |||
| 4 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 5 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 6 | pub enum Error { | ||
| 7 | BufferFull, | ||
| 8 | InvalidState, | ||
| 9 | } | ||
| 10 | |||
| 11 | /// Standard descriptor types | ||
| 12 | #[allow(missing_docs)] | ||
| 13 | pub mod descriptor_type { | ||
| 14 | pub const DEVICE: u8 = 1; | ||
| 15 | pub const CONFIGURATION: u8 = 2; | ||
| 16 | pub const STRING: u8 = 3; | ||
| 17 | pub const INTERFACE: u8 = 4; | ||
| 18 | pub const ENDPOINT: u8 = 5; | ||
| 19 | pub const IAD: u8 = 11; | ||
| 20 | pub const BOS: u8 = 15; | ||
| 21 | pub const CAPABILITY: u8 = 16; | ||
| 22 | } | ||
| 23 | |||
| 24 | /// String descriptor language IDs. | ||
| 25 | pub mod lang_id { | ||
| 26 | /// English (US) | ||
| 27 | /// | ||
| 28 | /// Recommended for use as the first language ID for compatibility. | ||
| 29 | pub const ENGLISH_US: u16 = 0x0409; | ||
| 30 | } | ||
| 31 | |||
| 32 | /// Standard capability descriptor types | ||
| 33 | #[allow(missing_docs)] | ||
| 34 | pub mod capability_type { | ||
| 35 | pub const WIRELESS_USB: u8 = 1; | ||
| 36 | pub const USB_2_0_EXTENSION: u8 = 2; | ||
| 37 | pub const SS_USB_DEVICE: u8 = 3; | ||
| 38 | pub const CONTAINER_ID: u8 = 4; | ||
| 39 | pub const PLATFORM: u8 = 5; | ||
| 40 | } | ||
| 41 | |||
| 42 | /// A writer for USB descriptors. | ||
| 43 | pub struct DescriptorWriter<'a> { | ||
| 44 | buf: &'a mut [u8], | ||
| 45 | position: usize, | ||
| 46 | num_interfaces_mark: Option<usize>, | ||
| 47 | num_endpoints_mark: Option<usize>, | ||
| 48 | write_iads: bool, | ||
| 49 | } | ||
| 50 | |||
| 51 | impl<'a> DescriptorWriter<'a> { | ||
| 52 | pub(crate) fn new(buf: &'a mut [u8]) -> Self { | ||
| 53 | DescriptorWriter { | ||
| 54 | buf, | ||
| 55 | position: 0, | ||
| 56 | num_interfaces_mark: None, | ||
| 57 | num_endpoints_mark: None, | ||
| 58 | write_iads: false, | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | pub fn into_buf(self) -> &'a mut [u8] { | ||
| 63 | &mut self.buf[..self.position] | ||
| 64 | } | ||
| 65 | |||
| 66 | /// Gets the current position in the buffer, i.e. the number of bytes written so far. | ||
| 67 | pub fn position(&self) -> usize { | ||
| 68 | self.position | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Writes an arbitrary (usually class-specific) descriptor. | ||
| 72 | pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8]) -> Result<(), Error> { | ||
| 73 | let length = descriptor.len(); | ||
| 74 | |||
| 75 | if (self.position + 2 + length) > self.buf.len() || (length + 2) > 255 { | ||
| 76 | return Err(Error::BufferFull); | ||
| 77 | } | ||
| 78 | |||
| 79 | self.buf[self.position] = (length + 2) as u8; | ||
| 80 | self.buf[self.position + 1] = descriptor_type; | ||
| 81 | |||
| 82 | let start = self.position + 2; | ||
| 83 | |||
| 84 | self.buf[start..start + length].copy_from_slice(descriptor); | ||
| 85 | |||
| 86 | self.position = start + length; | ||
| 87 | |||
| 88 | Ok(()) | ||
| 89 | } | ||
| 90 | |||
| 91 | pub(crate) fn device(&mut self, config: &Config) -> Result<(), Error> { | ||
| 92 | self.write( | ||
| 93 | descriptor_type::DEVICE, | ||
| 94 | &[ | ||
| 95 | 0x10, | ||
| 96 | 0x02, // bcdUSB 2.1 | ||
| 97 | config.device_class, // bDeviceClass | ||
| 98 | config.device_sub_class, // bDeviceSubClass | ||
| 99 | config.device_protocol, // bDeviceProtocol | ||
| 100 | config.max_packet_size_0, // bMaxPacketSize0 | ||
| 101 | config.vendor_id as u8, | ||
| 102 | (config.vendor_id >> 8) as u8, // idVendor | ||
| 103 | config.product_id as u8, | ||
| 104 | (config.product_id >> 8) as u8, // idProduct | ||
| 105 | config.device_release as u8, | ||
| 106 | (config.device_release >> 8) as u8, // bcdDevice | ||
| 107 | config.manufacturer.map_or(0, |_| 1), // iManufacturer | ||
| 108 | config.product.map_or(0, |_| 2), // iProduct | ||
| 109 | config.serial_number.map_or(0, |_| 3), // iSerialNumber | ||
| 110 | 1, // bNumConfigurations | ||
| 111 | ], | ||
| 112 | ) | ||
| 113 | } | ||
| 114 | |||
| 115 | pub(crate) fn configuration(&mut self, config: &Config) -> Result<(), Error> { | ||
| 116 | self.num_interfaces_mark = Some(self.position + 4); | ||
| 117 | |||
| 118 | self.write_iads = config.composite_with_iads; | ||
| 119 | |||
| 120 | self.write( | ||
| 121 | descriptor_type::CONFIGURATION, | ||
| 122 | &[ | ||
| 123 | 0, | ||
| 124 | 0, // wTotalLength | ||
| 125 | 0, // bNumInterfaces | ||
| 126 | CONFIGURATION_VALUE, // bConfigurationValue | ||
| 127 | 0, // iConfiguration | ||
| 128 | 0x80 | if config.self_powered { 0x40 } else { 0x00 } | ||
| 129 | | if config.supports_remote_wakeup { | ||
| 130 | 0x20 | ||
| 131 | } else { | ||
| 132 | 0x00 | ||
| 133 | }, // bmAttributes | ||
| 134 | (config.max_power / 2) as u8, // bMaxPower | ||
| 135 | ], | ||
| 136 | ) | ||
| 137 | } | ||
| 138 | |||
| 139 | pub(crate) fn end_class(&mut self) { | ||
| 140 | self.num_endpoints_mark = None; | ||
| 141 | } | ||
| 142 | |||
| 143 | pub(crate) fn end_configuration(&mut self) { | ||
| 144 | let position = self.position as u16; | ||
| 145 | self.buf[2..4].copy_from_slice(&position.to_le_bytes()); | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Writes a interface association descriptor. Call from `UsbClass::get_configuration_descriptors` | ||
| 149 | /// before writing the USB class or function's interface descriptors if your class has more than | ||
| 150 | /// one interface and wants to play nicely with composite devices on Windows. If the USB device | ||
| 151 | /// hosting the class was not configured as composite with IADs enabled, calling this function | ||
| 152 | /// does nothing, so it is safe to call from libraries. | ||
| 153 | /// | ||
| 154 | /// # Arguments | ||
| 155 | /// | ||
| 156 | /// * `first_interface` - Number of the function's first interface, previously allocated with | ||
| 157 | /// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface). | ||
| 158 | /// * `interface_count` - Number of interfaces in the function. | ||
| 159 | /// * `function_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices | ||
| 160 | /// that do not conform to any class. | ||
| 161 | /// * `function_sub_class` - Sub-class code. Depends on class. | ||
| 162 | /// * `function_protocol` - Protocol code. Depends on class and sub-class. | ||
| 163 | pub fn iad( | ||
| 164 | &mut self, | ||
| 165 | first_interface: InterfaceNumber, | ||
| 166 | interface_count: u8, | ||
| 167 | function_class: u8, | ||
| 168 | function_sub_class: u8, | ||
| 169 | function_protocol: u8, | ||
| 170 | ) -> Result<(), Error> { | ||
| 171 | if !self.write_iads { | ||
| 172 | return Ok(()); | ||
| 173 | } | ||
| 174 | |||
| 175 | self.write( | ||
| 176 | descriptor_type::IAD, | ||
| 177 | &[ | ||
| 178 | first_interface.into(), // bFirstInterface | ||
| 179 | interface_count, // bInterfaceCount | ||
| 180 | function_class, | ||
| 181 | function_sub_class, | ||
| 182 | function_protocol, | ||
| 183 | 0, | ||
| 184 | ], | ||
| 185 | )?; | ||
| 186 | |||
| 187 | Ok(()) | ||
| 188 | } | ||
| 189 | |||
| 190 | /// Writes a interface descriptor. | ||
| 191 | /// | ||
| 192 | /// # Arguments | ||
| 193 | /// | ||
| 194 | /// * `number` - Interface number previously allocated with | ||
| 195 | /// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface). | ||
| 196 | /// * `interface_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices | ||
| 197 | /// that do not conform to any class. | ||
| 198 | /// * `interface_sub_class` - Sub-class code. Depends on class. | ||
| 199 | /// * `interface_protocol` - Protocol code. Depends on class and sub-class. | ||
| 200 | pub fn interface( | ||
| 201 | &mut self, | ||
| 202 | number: InterfaceNumber, | ||
| 203 | interface_class: u8, | ||
| 204 | interface_sub_class: u8, | ||
| 205 | interface_protocol: u8, | ||
| 206 | ) -> Result<(), Error> { | ||
| 207 | self.interface_alt( | ||
| 208 | number, | ||
| 209 | DEFAULT_ALTERNATE_SETTING, | ||
| 210 | interface_class, | ||
| 211 | interface_sub_class, | ||
| 212 | interface_protocol, | ||
| 213 | None, | ||
| 214 | ) | ||
| 215 | } | ||
| 216 | |||
| 217 | /// Writes a interface descriptor with a specific alternate setting and | ||
| 218 | /// interface string identifier. | ||
| 219 | /// | ||
| 220 | /// # Arguments | ||
| 221 | /// | ||
| 222 | /// * `number` - Interface number previously allocated with | ||
| 223 | /// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface). | ||
| 224 | /// * `alternate_setting` - Number of the alternate setting | ||
| 225 | /// * `interface_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices | ||
| 226 | /// that do not conform to any class. | ||
| 227 | /// * `interface_sub_class` - Sub-class code. Depends on class. | ||
| 228 | /// * `interface_protocol` - Protocol code. Depends on class and sub-class. | ||
| 229 | /// * `interface_string` - Index of string descriptor describing this interface | ||
| 230 | |||
| 231 | pub fn interface_alt( | ||
| 232 | &mut self, | ||
| 233 | number: InterfaceNumber, | ||
| 234 | alternate_setting: u8, | ||
| 235 | interface_class: u8, | ||
| 236 | interface_sub_class: u8, | ||
| 237 | interface_protocol: u8, | ||
| 238 | interface_string: Option<StringIndex>, | ||
| 239 | ) -> Result<(), Error> { | ||
| 240 | if alternate_setting == DEFAULT_ALTERNATE_SETTING { | ||
| 241 | match self.num_interfaces_mark { | ||
| 242 | Some(mark) => self.buf[mark] += 1, | ||
| 243 | None => return Err(Error::InvalidState), | ||
| 244 | }; | ||
| 245 | } | ||
| 246 | |||
| 247 | let str_index = interface_string.map_or(0, Into::into); | ||
| 248 | |||
| 249 | self.num_endpoints_mark = Some(self.position + 4); | ||
| 250 | |||
| 251 | self.write( | ||
| 252 | descriptor_type::INTERFACE, | ||
| 253 | &[ | ||
| 254 | number.into(), // bInterfaceNumber | ||
| 255 | alternate_setting, // bAlternateSetting | ||
| 256 | 0, // bNumEndpoints | ||
| 257 | interface_class, // bInterfaceClass | ||
| 258 | interface_sub_class, // bInterfaceSubClass | ||
| 259 | interface_protocol, // bInterfaceProtocol | ||
| 260 | str_index, // iInterface | ||
| 261 | ], | ||
| 262 | )?; | ||
| 263 | |||
| 264 | Ok(()) | ||
| 265 | } | ||
| 266 | |||
| 267 | /// Writes an endpoint descriptor. | ||
| 268 | /// | ||
| 269 | /// # Arguments | ||
| 270 | /// | ||
| 271 | /// * `endpoint` - Endpoint previously allocated with | ||
| 272 | /// [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder). | ||
| 273 | pub fn endpoint(&mut self, endpoint: &EndpointInfo) -> Result<(), Error> { | ||
| 274 | match self.num_endpoints_mark { | ||
| 275 | Some(mark) => self.buf[mark] += 1, | ||
| 276 | None => return Err(Error::InvalidState), | ||
| 277 | }; | ||
| 278 | |||
| 279 | self.write( | ||
| 280 | descriptor_type::ENDPOINT, | ||
| 281 | &[ | ||
| 282 | endpoint.addr.into(), // bEndpointAddress | ||
| 283 | endpoint.ep_type as u8, // bmAttributes | ||
| 284 | endpoint.max_packet_size as u8, | ||
| 285 | (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize | ||
| 286 | endpoint.interval, // bInterval | ||
| 287 | ], | ||
| 288 | )?; | ||
| 289 | |||
| 290 | Ok(()) | ||
| 291 | } | ||
| 292 | |||
| 293 | /// Writes a string descriptor. | ||
| 294 | pub(crate) fn string(&mut self, string: &str) -> Result<(), Error> { | ||
| 295 | let mut pos = self.position; | ||
| 296 | |||
| 297 | if pos + 2 > self.buf.len() { | ||
| 298 | return Err(Error::BufferFull); | ||
| 299 | } | ||
| 300 | |||
| 301 | self.buf[pos] = 0; // length placeholder | ||
| 302 | self.buf[pos + 1] = descriptor_type::STRING; | ||
| 303 | |||
| 304 | pos += 2; | ||
| 305 | |||
| 306 | for c in string.encode_utf16() { | ||
| 307 | if pos >= self.buf.len() { | ||
| 308 | return Err(Error::BufferFull); | ||
| 309 | } | ||
| 310 | |||
| 311 | self.buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes()); | ||
| 312 | pos += 2; | ||
| 313 | } | ||
| 314 | |||
| 315 | self.buf[self.position] = (pos - self.position) as u8; | ||
| 316 | |||
| 317 | self.position = pos; | ||
| 318 | |||
| 319 | Ok(()) | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | /// A writer for Binary Object Store descriptor. | ||
| 324 | pub struct BosWriter<'a> { | ||
| 325 | pub(crate) writer: DescriptorWriter<'a>, | ||
| 326 | num_caps_mark: Option<usize>, | ||
| 327 | } | ||
| 328 | |||
| 329 | impl<'a> BosWriter<'a> { | ||
| 330 | pub(crate) fn new(writer: DescriptorWriter<'a>) -> Self { | ||
| 331 | Self { | ||
| 332 | writer: writer, | ||
| 333 | num_caps_mark: None, | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | pub(crate) fn bos(&mut self) -> Result<(), Error> { | ||
| 338 | self.num_caps_mark = Some(self.writer.position + 4); | ||
| 339 | self.writer.write( | ||
| 340 | descriptor_type::BOS, | ||
| 341 | &[ | ||
| 342 | 0x00, 0x00, // wTotalLength | ||
| 343 | 0x00, // bNumDeviceCaps | ||
| 344 | ], | ||
| 345 | )?; | ||
| 346 | |||
| 347 | self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4])?; | ||
| 348 | |||
| 349 | Ok(()) | ||
| 350 | } | ||
| 351 | |||
| 352 | /// Writes capability descriptor to a BOS | ||
| 353 | /// | ||
| 354 | /// # Arguments | ||
| 355 | /// | ||
| 356 | /// * `capability_type` - Type of a capability | ||
| 357 | /// * `data` - Binary data of the descriptor | ||
| 358 | pub fn capability(&mut self, capability_type: u8, data: &[u8]) -> Result<(), Error> { | ||
| 359 | match self.num_caps_mark { | ||
| 360 | Some(mark) => self.writer.buf[mark] += 1, | ||
| 361 | None => return Err(Error::InvalidState), | ||
| 362 | } | ||
| 363 | |||
| 364 | let mut start = self.writer.position; | ||
| 365 | let blen = data.len(); | ||
| 366 | |||
| 367 | if (start + blen + 3) > self.writer.buf.len() || (blen + 3) > 255 { | ||
| 368 | return Err(Error::BufferFull); | ||
| 369 | } | ||
| 370 | |||
| 371 | self.writer.buf[start] = (blen + 3) as u8; | ||
| 372 | self.writer.buf[start + 1] = descriptor_type::CAPABILITY; | ||
| 373 | self.writer.buf[start + 2] = capability_type; | ||
| 374 | |||
| 375 | start += 3; | ||
| 376 | self.writer.buf[start..start + blen].copy_from_slice(data); | ||
| 377 | self.writer.position = start + blen; | ||
| 378 | |||
| 379 | Ok(()) | ||
| 380 | } | ||
| 381 | |||
| 382 | pub(crate) fn end_bos(&mut self) { | ||
| 383 | self.num_caps_mark = None; | ||
| 384 | let position = self.writer.position as u16; | ||
| 385 | self.writer.buf[2..4].copy_from_slice(&position.to_le_bytes()); | ||
| 386 | } | ||
| 387 | } | ||
diff --git a/embassy-usb/src/driver.rs b/embassy-usb/src/driver.rs new file mode 100644 index 000000000..ed4edb576 --- /dev/null +++ b/embassy-usb/src/driver.rs | |||
| @@ -0,0 +1,160 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | |||
| 3 | use super::types::*; | ||
| 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 | ||
| 40 | /// platform. | ||
| 41 | pub trait Driver<'a> { | ||
| 42 | type EndpointOut: EndpointOut + 'a; | ||
| 43 | type EndpointIn: EndpointIn + 'a; | ||
| 44 | type Bus: Bus + 'a; | ||
| 45 | |||
| 46 | /// Allocates an endpoint and specified endpoint parameters. This method is called by the device | ||
| 47 | /// and class implementations to allocate endpoints, and can only be called before | ||
| 48 | /// [`enable`](UsbBus::enable) is called. | ||
| 49 | /// | ||
| 50 | /// # Arguments | ||
| 51 | /// | ||
| 52 | /// * `ep_addr` - A static endpoint address to allocate. If Some, the implementation should | ||
| 53 | /// attempt to return an endpoint with the specified address. If None, the implementation | ||
| 54 | /// should return the next available one. | ||
| 55 | /// * `max_packet_size` - Maximum packet size in bytes. | ||
| 56 | /// * `interval` - Polling interval parameter for interrupt endpoints. | ||
| 57 | fn alloc_endpoint_out( | ||
| 58 | &mut self, | ||
| 59 | ep_addr: Option<EndpointAddress>, | ||
| 60 | ep_type: EndpointType, | ||
| 61 | max_packet_size: u16, | ||
| 62 | interval: u8, | ||
| 63 | ) -> Result<Self::EndpointOut, EndpointAllocError>; | ||
| 64 | |||
| 65 | fn alloc_endpoint_in( | ||
| 66 | &mut self, | ||
| 67 | ep_addr: Option<EndpointAddress>, | ||
| 68 | ep_type: EndpointType, | ||
| 69 | max_packet_size: u16, | ||
| 70 | interval: u8, | ||
| 71 | ) -> Result<Self::EndpointIn, EndpointAllocError>; | ||
| 72 | |||
| 73 | /// Enables and initializes the USB peripheral. Soon after enabling the device will be reset, so | ||
| 74 | /// there is no need to perform a USB reset in this method. | ||
| 75 | fn enable(self) -> Self::Bus; | ||
| 76 | |||
| 77 | /// Indicates that `set_device_address` must be called before accepting the corresponding | ||
| 78 | /// control transfer, not after. | ||
| 79 | /// | ||
| 80 | /// The default value for this constant is `false`, which corresponds to the USB 2.0 spec, 9.4.6 | ||
| 81 | const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false; | ||
| 82 | } | ||
| 83 | |||
| 84 | pub trait Bus { | ||
| 85 | /// 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 | ||
| 87 | /// reset the state of all endpoints and peripheral flags back to a state suitable for | ||
| 88 | /// enumeration, as well as ensure that all endpoints previously allocated with alloc_ep are | ||
| 89 | /// initialized as specified. | ||
| 90 | fn reset(&mut self); | ||
| 91 | |||
| 92 | /// Sets the device USB address to `addr`. | ||
| 93 | fn set_device_address(&mut self, addr: u8); | ||
| 94 | |||
| 95 | /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it | ||
| 96 | /// should be prepared to receive data again. Only used during control transfers. | ||
| 97 | fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool); | ||
| 98 | |||
| 99 | /// Gets whether the STALL condition is set for an endpoint. Only used during control transfers. | ||
| 100 | fn is_stalled(&mut self, ep_addr: EndpointAddress) -> bool; | ||
| 101 | |||
| 102 | /// Causes the USB peripheral to enter USB suspend mode, lowering power consumption and | ||
| 103 | /// preparing to detect a USB wakeup event. This will be called after | ||
| 104 | /// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Suspend`]. The device will | ||
| 105 | /// continue be polled, and it shall return a value other than `Suspend` from `poll` when it no | ||
| 106 | /// longer detects the suspend condition. | ||
| 107 | fn suspend(&mut self); | ||
| 108 | |||
| 109 | /// Resumes from suspend mode. This may only be called after the peripheral has been previously | ||
| 110 | /// suspended. | ||
| 111 | fn resume(&mut self); | ||
| 112 | |||
| 113 | /// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the | ||
| 114 | /// device. | ||
| 115 | /// | ||
| 116 | /// The default implementation just returns `Unsupported`. | ||
| 117 | /// | ||
| 118 | /// # Errors | ||
| 119 | /// | ||
| 120 | /// * [`Unsupported`](crate::UsbError::Unsupported) - This UsbBus implementation doesn't support | ||
| 121 | /// simulating a disconnect or it has not been enabled at creation time. | ||
| 122 | fn force_reset(&mut self) -> Result<(), Unsupported> { | ||
| 123 | Err(Unsupported) | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | pub trait Endpoint { | ||
| 128 | /// Get the endpoint address | ||
| 129 | fn info(&self) -> &EndpointInfo; | ||
| 130 | |||
| 131 | /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it | ||
| 132 | /// should be prepared to receive data again. | ||
| 133 | fn set_stalled(&self, stalled: bool); | ||
| 134 | |||
| 135 | /// Gets whether the STALL condition is set for an endpoint. | ||
| 136 | fn is_stalled(&self) -> bool; | ||
| 137 | |||
| 138 | // TODO enable/disable? | ||
| 139 | } | ||
| 140 | |||
| 141 | pub trait EndpointOut: Endpoint { | ||
| 142 | type ReadFuture<'a>: Future<Output = Result<usize, ReadError>> + 'a | ||
| 143 | where | ||
| 144 | Self: 'a; | ||
| 145 | |||
| 146 | /// Reads a single packet of data from the endpoint, and returns the actual length of | ||
| 147 | /// the packet. | ||
| 148 | /// | ||
| 149 | /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. | ||
| 150 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a>; | ||
| 151 | } | ||
| 152 | |||
| 153 | pub trait EndpointIn: Endpoint { | ||
| 154 | type WriteFuture<'a>: Future<Output = Result<(), WriteError>> + 'a | ||
| 155 | where | ||
| 156 | Self: 'a; | ||
| 157 | |||
| 158 | /// Writes a single packet of data to the endpoint. | ||
| 159 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a>; | ||
| 160 | } | ||
diff --git a/embassy-usb/src/fmt.rs b/embassy-usb/src/fmt.rs new file mode 100644 index 000000000..066970813 --- /dev/null +++ b/embassy-usb/src/fmt.rs | |||
| @@ -0,0 +1,225 @@ | |||
| 1 | #![macro_use] | ||
| 2 | #![allow(unused_macros)] | ||
| 3 | |||
| 4 | #[cfg(all(feature = "defmt", feature = "log"))] | ||
| 5 | compile_error!("You may not enable both `defmt` and `log` features."); | ||
| 6 | |||
| 7 | macro_rules! assert { | ||
| 8 | ($($x:tt)*) => { | ||
| 9 | { | ||
| 10 | #[cfg(not(feature = "defmt"))] | ||
| 11 | ::core::assert!($($x)*); | ||
| 12 | #[cfg(feature = "defmt")] | ||
| 13 | ::defmt::assert!($($x)*); | ||
| 14 | } | ||
| 15 | }; | ||
| 16 | } | ||
| 17 | |||
| 18 | macro_rules! assert_eq { | ||
| 19 | ($($x:tt)*) => { | ||
| 20 | { | ||
| 21 | #[cfg(not(feature = "defmt"))] | ||
| 22 | ::core::assert_eq!($($x)*); | ||
| 23 | #[cfg(feature = "defmt")] | ||
| 24 | ::defmt::assert_eq!($($x)*); | ||
| 25 | } | ||
| 26 | }; | ||
| 27 | } | ||
| 28 | |||
| 29 | macro_rules! assert_ne { | ||
| 30 | ($($x:tt)*) => { | ||
| 31 | { | ||
| 32 | #[cfg(not(feature = "defmt"))] | ||
| 33 | ::core::assert_ne!($($x)*); | ||
| 34 | #[cfg(feature = "defmt")] | ||
| 35 | ::defmt::assert_ne!($($x)*); | ||
| 36 | } | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | |||
| 40 | macro_rules! debug_assert { | ||
| 41 | ($($x:tt)*) => { | ||
| 42 | { | ||
| 43 | #[cfg(not(feature = "defmt"))] | ||
| 44 | ::core::debug_assert!($($x)*); | ||
| 45 | #[cfg(feature = "defmt")] | ||
| 46 | ::defmt::debug_assert!($($x)*); | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | } | ||
| 50 | |||
| 51 | macro_rules! debug_assert_eq { | ||
| 52 | ($($x:tt)*) => { | ||
| 53 | { | ||
| 54 | #[cfg(not(feature = "defmt"))] | ||
| 55 | ::core::debug_assert_eq!($($x)*); | ||
| 56 | #[cfg(feature = "defmt")] | ||
| 57 | ::defmt::debug_assert_eq!($($x)*); | ||
| 58 | } | ||
| 59 | }; | ||
| 60 | } | ||
| 61 | |||
| 62 | macro_rules! debug_assert_ne { | ||
| 63 | ($($x:tt)*) => { | ||
| 64 | { | ||
| 65 | #[cfg(not(feature = "defmt"))] | ||
| 66 | ::core::debug_assert_ne!($($x)*); | ||
| 67 | #[cfg(feature = "defmt")] | ||
| 68 | ::defmt::debug_assert_ne!($($x)*); | ||
| 69 | } | ||
| 70 | }; | ||
| 71 | } | ||
| 72 | |||
| 73 | macro_rules! todo { | ||
| 74 | ($($x:tt)*) => { | ||
| 75 | { | ||
| 76 | #[cfg(not(feature = "defmt"))] | ||
| 77 | ::core::todo!($($x)*); | ||
| 78 | #[cfg(feature = "defmt")] | ||
| 79 | ::defmt::todo!($($x)*); | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | } | ||
| 83 | |||
| 84 | macro_rules! unreachable { | ||
| 85 | ($($x:tt)*) => { | ||
| 86 | { | ||
| 87 | #[cfg(not(feature = "defmt"))] | ||
| 88 | ::core::unreachable!($($x)*); | ||
| 89 | #[cfg(feature = "defmt")] | ||
| 90 | ::defmt::unreachable!($($x)*); | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | } | ||
| 94 | |||
| 95 | macro_rules! panic { | ||
| 96 | ($($x:tt)*) => { | ||
| 97 | { | ||
| 98 | #[cfg(not(feature = "defmt"))] | ||
| 99 | ::core::panic!($($x)*); | ||
| 100 | #[cfg(feature = "defmt")] | ||
| 101 | ::defmt::panic!($($x)*); | ||
| 102 | } | ||
| 103 | }; | ||
| 104 | } | ||
| 105 | |||
| 106 | macro_rules! trace { | ||
| 107 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 108 | { | ||
| 109 | #[cfg(feature = "log")] | ||
| 110 | ::log::trace!($s $(, $x)*); | ||
| 111 | #[cfg(feature = "defmt")] | ||
| 112 | ::defmt::trace!($s $(, $x)*); | ||
| 113 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 114 | let _ = ($( & $x ),*); | ||
| 115 | } | ||
| 116 | }; | ||
| 117 | } | ||
| 118 | |||
| 119 | macro_rules! debug { | ||
| 120 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 121 | { | ||
| 122 | #[cfg(feature = "log")] | ||
| 123 | ::log::debug!($s $(, $x)*); | ||
| 124 | #[cfg(feature = "defmt")] | ||
| 125 | ::defmt::debug!($s $(, $x)*); | ||
| 126 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 127 | let _ = ($( & $x ),*); | ||
| 128 | } | ||
| 129 | }; | ||
| 130 | } | ||
| 131 | |||
| 132 | macro_rules! info { | ||
| 133 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 134 | { | ||
| 135 | #[cfg(feature = "log")] | ||
| 136 | ::log::info!($s $(, $x)*); | ||
| 137 | #[cfg(feature = "defmt")] | ||
| 138 | ::defmt::info!($s $(, $x)*); | ||
| 139 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 140 | let _ = ($( & $x ),*); | ||
| 141 | } | ||
| 142 | }; | ||
| 143 | } | ||
| 144 | |||
| 145 | macro_rules! warn { | ||
| 146 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 147 | { | ||
| 148 | #[cfg(feature = "log")] | ||
| 149 | ::log::warn!($s $(, $x)*); | ||
| 150 | #[cfg(feature = "defmt")] | ||
| 151 | ::defmt::warn!($s $(, $x)*); | ||
| 152 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 153 | let _ = ($( & $x ),*); | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | } | ||
| 157 | |||
| 158 | macro_rules! error { | ||
| 159 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 160 | { | ||
| 161 | #[cfg(feature = "log")] | ||
| 162 | ::log::error!($s $(, $x)*); | ||
| 163 | #[cfg(feature = "defmt")] | ||
| 164 | ::defmt::error!($s $(, $x)*); | ||
| 165 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 166 | let _ = ($( & $x ),*); | ||
| 167 | } | ||
| 168 | }; | ||
| 169 | } | ||
| 170 | |||
| 171 | #[cfg(feature = "defmt")] | ||
| 172 | macro_rules! unwrap { | ||
| 173 | ($($x:tt)*) => { | ||
| 174 | ::defmt::unwrap!($($x)*) | ||
| 175 | }; | ||
| 176 | } | ||
| 177 | |||
| 178 | #[cfg(not(feature = "defmt"))] | ||
| 179 | macro_rules! unwrap { | ||
| 180 | ($arg:expr) => { | ||
| 181 | match $crate::fmt::Try::into_result($arg) { | ||
| 182 | ::core::result::Result::Ok(t) => t, | ||
| 183 | ::core::result::Result::Err(e) => { | ||
| 184 | ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | }; | ||
| 188 | ($arg:expr, $($msg:expr),+ $(,)? ) => { | ||
| 189 | match $crate::fmt::Try::into_result($arg) { | ||
| 190 | ::core::result::Result::Ok(t) => t, | ||
| 191 | ::core::result::Result::Err(e) => { | ||
| 192 | ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 199 | pub struct NoneError; | ||
| 200 | |||
| 201 | pub trait Try { | ||
| 202 | type Ok; | ||
| 203 | type Error; | ||
| 204 | fn into_result(self) -> Result<Self::Ok, Self::Error>; | ||
| 205 | } | ||
| 206 | |||
| 207 | impl<T> Try for Option<T> { | ||
| 208 | type Ok = T; | ||
| 209 | type Error = NoneError; | ||
| 210 | |||
| 211 | #[inline] | ||
| 212 | fn into_result(self) -> Result<T, NoneError> { | ||
| 213 | self.ok_or(NoneError) | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | impl<T, E> Try for Result<T, E> { | ||
| 218 | type Ok = T; | ||
| 219 | type Error = E; | ||
| 220 | |||
| 221 | #[inline] | ||
| 222 | fn into_result(self) -> Self { | ||
| 223 | self | ||
| 224 | } | ||
| 225 | } | ||
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs new file mode 100644 index 000000000..397db96c4 --- /dev/null +++ b/embassy-usb/src/lib.rs | |||
| @@ -0,0 +1,342 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![feature(generic_associated_types)] | ||
| 3 | |||
| 4 | // This mod MUST go first, so that the others see its macros. | ||
| 5 | pub(crate) mod fmt; | ||
| 6 | |||
| 7 | mod builder; | ||
| 8 | mod control; | ||
| 9 | pub mod descriptor; | ||
| 10 | pub mod driver; | ||
| 11 | pub mod types; | ||
| 12 | |||
| 13 | use self::control::*; | ||
| 14 | use self::descriptor::*; | ||
| 15 | use self::driver::*; | ||
| 16 | use self::types::*; | ||
| 17 | |||
| 18 | pub use self::builder::Config; | ||
| 19 | pub use self::builder::UsbDeviceBuilder; | ||
| 20 | |||
| 21 | /// The global state of the USB device. | ||
| 22 | /// | ||
| 23 | /// In general class traffic is only possible in the `Configured` state. | ||
| 24 | #[repr(u8)] | ||
| 25 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] | ||
| 26 | pub enum UsbDeviceState { | ||
| 27 | /// The USB device has just been created or reset. | ||
| 28 | Default, | ||
| 29 | |||
| 30 | /// The USB device has received an address from the host. | ||
| 31 | Addressed, | ||
| 32 | |||
| 33 | /// The USB device has been configured and is fully functional. | ||
| 34 | Configured, | ||
| 35 | |||
| 36 | /// The USB device has been suspended by the host or it has been unplugged from the USB bus. | ||
| 37 | Suspend, | ||
| 38 | } | ||
| 39 | |||
| 40 | /// The bConfiguration value for the not configured state. | ||
| 41 | pub const CONFIGURATION_NONE: u8 = 0; | ||
| 42 | |||
| 43 | /// The bConfiguration value for the single configuration supported by this device. | ||
| 44 | pub const CONFIGURATION_VALUE: u8 = 1; | ||
| 45 | |||
| 46 | /// The default value for bAlternateSetting for all interfaces. | ||
| 47 | pub const DEFAULT_ALTERNATE_SETTING: u8 = 0; | ||
| 48 | |||
| 49 | pub struct UsbDevice<'d, D: Driver<'d>> { | ||
| 50 | driver: D::Bus, | ||
| 51 | control_in: D::EndpointIn, | ||
| 52 | control_out: D::EndpointOut, | ||
| 53 | |||
| 54 | config: Config<'d>, | ||
| 55 | device_descriptor: &'d [u8], | ||
| 56 | config_descriptor: &'d [u8], | ||
| 57 | bos_descriptor: &'d [u8], | ||
| 58 | |||
| 59 | device_state: UsbDeviceState, | ||
| 60 | remote_wakeup_enabled: bool, | ||
| 61 | self_powered: bool, | ||
| 62 | pending_address: u8, | ||
| 63 | } | ||
| 64 | |||
| 65 | impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | ||
| 66 | pub(crate) fn build( | ||
| 67 | mut driver: D, | ||
| 68 | config: Config<'d>, | ||
| 69 | device_descriptor: &'d [u8], | ||
| 70 | config_descriptor: &'d [u8], | ||
| 71 | bos_descriptor: &'d [u8], | ||
| 72 | ) -> Self { | ||
| 73 | let control_out = driver | ||
| 74 | .alloc_endpoint_out( | ||
| 75 | Some(0x00.into()), | ||
| 76 | EndpointType::Control, | ||
| 77 | config.max_packet_size_0 as u16, | ||
| 78 | 0, | ||
| 79 | ) | ||
| 80 | .expect("failed to alloc control endpoint"); | ||
| 81 | |||
| 82 | let control_in = driver | ||
| 83 | .alloc_endpoint_in( | ||
| 84 | Some(0x80.into()), | ||
| 85 | EndpointType::Control, | ||
| 86 | config.max_packet_size_0 as u16, | ||
| 87 | 0, | ||
| 88 | ) | ||
| 89 | .expect("failed to alloc control endpoint"); | ||
| 90 | |||
| 91 | // Enable the USB bus. | ||
| 92 | // This prevent further allocation by consuming the driver. | ||
| 93 | let driver = driver.enable(); | ||
| 94 | |||
| 95 | Self { | ||
| 96 | driver, | ||
| 97 | config, | ||
| 98 | control_in, | ||
| 99 | control_out, | ||
| 100 | device_descriptor, | ||
| 101 | config_descriptor, | ||
| 102 | bos_descriptor, | ||
| 103 | device_state: UsbDeviceState::Default, | ||
| 104 | remote_wakeup_enabled: false, | ||
| 105 | self_powered: false, | ||
| 106 | pending_address: 0, | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | pub async fn run(&mut self) { | ||
| 111 | loop { | ||
| 112 | let mut buf = [0; 8]; | ||
| 113 | let n = self.control_out.read(&mut buf).await.unwrap(); | ||
| 114 | assert_eq!(n, 8); | ||
| 115 | let req = Request::parse(&buf).unwrap(); | ||
| 116 | info!("setup request: {:x}", req); | ||
| 117 | |||
| 118 | // Now that we have properly parsed the setup packet, ensure the end-point is no longer in | ||
| 119 | // a stalled state. | ||
| 120 | self.control_out.set_stalled(false); | ||
| 121 | |||
| 122 | match req.direction { | ||
| 123 | UsbDirection::In => self.handle_control_in(req).await, | ||
| 124 | UsbDirection::Out => self.handle_control_out(req).await, | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | async fn write_chunked(&mut self, data: &[u8]) -> Result<(), driver::WriteError> { | ||
| 130 | for c in data.chunks(8) { | ||
| 131 | self.control_in.write(c).await?; | ||
| 132 | } | ||
| 133 | if data.len() % 8 == 0 { | ||
| 134 | self.control_in.write(&[]).await?; | ||
| 135 | } | ||
| 136 | Ok(()) | ||
| 137 | } | ||
| 138 | |||
| 139 | async fn control_out_accept(&mut self, req: Request) { | ||
| 140 | info!("control out accept"); | ||
| 141 | // status phase | ||
| 142 | // todo: cleanup | ||
| 143 | self.control_out.read(&mut []).await.unwrap(); | ||
| 144 | } | ||
| 145 | |||
| 146 | async fn control_in_accept(&mut self, req: Request, data: &[u8]) { | ||
| 147 | info!("control accept {:x}", data); | ||
| 148 | |||
| 149 | let len = data.len().min(req.length as _); | ||
| 150 | if let Err(e) = self.write_chunked(&data[..len]).await { | ||
| 151 | info!("write_chunked failed: {:?}", e); | ||
| 152 | } | ||
| 153 | |||
| 154 | // status phase | ||
| 155 | // todo: cleanup | ||
| 156 | self.control_out.read(&mut []).await.unwrap(); | ||
| 157 | } | ||
| 158 | |||
| 159 | async fn control_in_accept_writer( | ||
| 160 | &mut self, | ||
| 161 | req: Request, | ||
| 162 | f: impl FnOnce(&mut DescriptorWriter), | ||
| 163 | ) { | ||
| 164 | let mut buf = [0; 256]; | ||
| 165 | let mut w = DescriptorWriter::new(&mut buf); | ||
| 166 | f(&mut w); | ||
| 167 | let pos = w.position(); | ||
| 168 | self.control_in_accept(req, &buf[..pos]).await; | ||
| 169 | } | ||
| 170 | |||
| 171 | fn control_reject(&mut self, req: Request) { | ||
| 172 | self.control_out.set_stalled(true); | ||
| 173 | } | ||
| 174 | |||
| 175 | async fn handle_control_out(&mut self, req: Request) { | ||
| 176 | // TODO actually read the data if there's an OUT data phase. | ||
| 177 | |||
| 178 | const CONFIGURATION_NONE_U16: u16 = CONFIGURATION_NONE as u16; | ||
| 179 | const CONFIGURATION_VALUE_U16: u16 = CONFIGURATION_VALUE as u16; | ||
| 180 | const DEFAULT_ALTERNATE_SETTING_U16: u16 = DEFAULT_ALTERNATE_SETTING as u16; | ||
| 181 | |||
| 182 | match req.request_type { | ||
| 183 | RequestType::Standard => match (req.recipient, req.request, req.value) { | ||
| 184 | ( | ||
| 185 | Recipient::Device, | ||
| 186 | Request::CLEAR_FEATURE, | ||
| 187 | Request::FEATURE_DEVICE_REMOTE_WAKEUP, | ||
| 188 | ) => { | ||
| 189 | self.remote_wakeup_enabled = false; | ||
| 190 | self.control_out_accept(req).await; | ||
| 191 | } | ||
| 192 | |||
| 193 | (Recipient::Endpoint, Request::CLEAR_FEATURE, Request::FEATURE_ENDPOINT_HALT) => { | ||
| 194 | //self.bus.set_stalled(((req.index as u8) & 0x8f).into(), false); | ||
| 195 | self.control_out_accept(req).await; | ||
| 196 | } | ||
| 197 | |||
| 198 | ( | ||
| 199 | Recipient::Device, | ||
| 200 | Request::SET_FEATURE, | ||
| 201 | Request::FEATURE_DEVICE_REMOTE_WAKEUP, | ||
| 202 | ) => { | ||
| 203 | self.remote_wakeup_enabled = true; | ||
| 204 | self.control_out_accept(req).await; | ||
| 205 | } | ||
| 206 | |||
| 207 | (Recipient::Endpoint, Request::SET_FEATURE, Request::FEATURE_ENDPOINT_HALT) => { | ||
| 208 | //self.bus.set_stalled(((req.index as u8) & 0x8f).into(), true); | ||
| 209 | self.control_out_accept(req).await; | ||
| 210 | } | ||
| 211 | |||
| 212 | (Recipient::Device, Request::SET_ADDRESS, 1..=127) => { | ||
| 213 | self.pending_address = req.value as u8; | ||
| 214 | |||
| 215 | // on NRF the hardware auto-handles SET_ADDRESS. | ||
| 216 | self.control_out_accept(req).await; | ||
| 217 | } | ||
| 218 | |||
| 219 | (Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => { | ||
| 220 | self.device_state = UsbDeviceState::Configured; | ||
| 221 | self.control_out_accept(req).await; | ||
| 222 | } | ||
| 223 | |||
| 224 | (Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => { | ||
| 225 | match self.device_state { | ||
| 226 | UsbDeviceState::Default => { | ||
| 227 | self.control_out_accept(req).await; | ||
| 228 | } | ||
| 229 | _ => { | ||
| 230 | self.device_state = UsbDeviceState::Addressed; | ||
| 231 | self.control_out_accept(req).await; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | (Recipient::Interface, Request::SET_INTERFACE, DEFAULT_ALTERNATE_SETTING_U16) => { | ||
| 237 | // TODO: do something when alternate settings are implemented | ||
| 238 | self.control_out_accept(req).await; | ||
| 239 | } | ||
| 240 | |||
| 241 | _ => self.control_reject(req), | ||
| 242 | }, | ||
| 243 | _ => self.control_reject(req), | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | async fn handle_control_in(&mut self, req: Request) { | ||
| 248 | match req.request_type { | ||
| 249 | RequestType::Standard => match (req.recipient, req.request) { | ||
| 250 | (Recipient::Device, Request::GET_STATUS) => { | ||
| 251 | let mut status: u16 = 0x0000; | ||
| 252 | if self.self_powered { | ||
| 253 | status |= 0x0001; | ||
| 254 | } | ||
| 255 | if self.remote_wakeup_enabled { | ||
| 256 | status |= 0x0002; | ||
| 257 | } | ||
| 258 | self.control_in_accept(req, &status.to_le_bytes()).await; | ||
| 259 | } | ||
| 260 | |||
| 261 | (Recipient::Interface, Request::GET_STATUS) => { | ||
| 262 | let status: u16 = 0x0000; | ||
| 263 | self.control_in_accept(req, &status.to_le_bytes()).await; | ||
| 264 | } | ||
| 265 | |||
| 266 | (Recipient::Endpoint, Request::GET_STATUS) => { | ||
| 267 | let ep_addr: EndpointAddress = ((req.index as u8) & 0x8f).into(); | ||
| 268 | let mut status: u16 = 0x0000; | ||
| 269 | if self.driver.is_stalled(ep_addr) { | ||
| 270 | status |= 0x0001; | ||
| 271 | } | ||
| 272 | self.control_in_accept(req, &status.to_le_bytes()).await; | ||
| 273 | } | ||
| 274 | |||
| 275 | (Recipient::Device, Request::GET_DESCRIPTOR) => { | ||
| 276 | self.handle_get_descriptor(req).await; | ||
| 277 | } | ||
| 278 | |||
| 279 | (Recipient::Device, Request::GET_CONFIGURATION) => { | ||
| 280 | let status = match self.device_state { | ||
| 281 | UsbDeviceState::Configured => CONFIGURATION_VALUE, | ||
| 282 | _ => CONFIGURATION_NONE, | ||
| 283 | }; | ||
| 284 | self.control_in_accept(req, &status.to_le_bytes()).await; | ||
| 285 | } | ||
| 286 | |||
| 287 | (Recipient::Interface, Request::GET_INTERFACE) => { | ||
| 288 | // TODO: change when alternate settings are implemented | ||
| 289 | let status = DEFAULT_ALTERNATE_SETTING; | ||
| 290 | self.control_in_accept(req, &status.to_le_bytes()).await; | ||
| 291 | } | ||
| 292 | _ => self.control_reject(req), | ||
| 293 | }, | ||
| 294 | _ => self.control_reject(req), | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | async fn handle_get_descriptor(&mut self, req: Request) { | ||
| 299 | let (dtype, index) = req.descriptor_type_index(); | ||
| 300 | let config = self.config.clone(); | ||
| 301 | |||
| 302 | match dtype { | ||
| 303 | descriptor_type::BOS => self.control_in_accept(req, self.bos_descriptor).await, | ||
| 304 | descriptor_type::DEVICE => self.control_in_accept(req, self.device_descriptor).await, | ||
| 305 | descriptor_type::CONFIGURATION => { | ||
| 306 | self.control_in_accept(req, self.config_descriptor).await | ||
| 307 | } | ||
| 308 | descriptor_type::STRING => { | ||
| 309 | if index == 0 { | ||
| 310 | self.control_in_accept_writer(req, |w| { | ||
| 311 | w.write(descriptor_type::STRING, &lang_id::ENGLISH_US.to_le_bytes()) | ||
| 312 | .unwrap(); | ||
| 313 | }) | ||
| 314 | .await | ||
| 315 | } else { | ||
| 316 | let s = match index { | ||
| 317 | 1 => self.config.manufacturer, | ||
| 318 | 2 => self.config.product, | ||
| 319 | 3 => self.config.serial_number, | ||
| 320 | _ => { | ||
| 321 | let index = StringIndex::new(index); | ||
| 322 | let lang_id = req.index; | ||
| 323 | None | ||
| 324 | //classes | ||
| 325 | // .iter() | ||
| 326 | // .filter_map(|cls| cls.get_string(index, lang_id)) | ||
| 327 | // .nth(0) | ||
| 328 | } | ||
| 329 | }; | ||
| 330 | |||
| 331 | if let Some(s) = s { | ||
| 332 | self.control_in_accept_writer(req, |w| w.string(s).unwrap()) | ||
| 333 | .await; | ||
| 334 | } else { | ||
| 335 | self.control_reject(req) | ||
| 336 | } | ||
| 337 | } | ||
| 338 | } | ||
| 339 | _ => self.control_reject(req), | ||
| 340 | } | ||
| 341 | } | ||
| 342 | } | ||
diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs new file mode 100644 index 000000000..9d00e46cb --- /dev/null +++ b/embassy-usb/src/types.rs | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | /// Direction of USB traffic. Note that in the USB standard the direction is always indicated from | ||
| 2 | /// the perspective of the host, which is backward for devices, but the standard directions are used | ||
| 3 | /// for consistency. | ||
| 4 | /// | ||
| 5 | /// The values of the enum also match the direction bit used in endpoint addresses and control | ||
| 6 | /// request types. | ||
| 7 | #[repr(u8)] | ||
| 8 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 9 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 10 | pub enum UsbDirection { | ||
| 11 | /// Host to device (OUT) | ||
| 12 | Out = 0x00, | ||
| 13 | /// Device to host (IN) | ||
| 14 | In = 0x80, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl From<u8> for UsbDirection { | ||
| 18 | fn from(value: u8) -> Self { | ||
| 19 | unsafe { core::mem::transmute(value & 0x80) } | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | /// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the | ||
| 24 | /// transfer bmAttributes transfer type bits. | ||
| 25 | #[repr(u8)] | ||
| 26 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 27 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 28 | pub enum EndpointType { | ||
| 29 | /// Control endpoint. Used for device management. Only the host can initiate requests. Usually | ||
| 30 | /// used only endpoint 0. | ||
| 31 | Control = 0b00, | ||
| 32 | /// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet. | ||
| 33 | Isochronous = 0b01, | ||
| 34 | /// Bulk endpoint. Used for large amounts of best-effort reliable data. | ||
| 35 | Bulk = 0b10, | ||
| 36 | /// Interrupt endpoint. Used for small amounts of time-critical reliable data. | ||
| 37 | Interrupt = 0b11, | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Type-safe endpoint address. | ||
| 41 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] | ||
| 42 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 43 | pub struct EndpointAddress(u8); | ||
| 44 | |||
| 45 | impl From<u8> for EndpointAddress { | ||
| 46 | #[inline] | ||
| 47 | fn from(addr: u8) -> EndpointAddress { | ||
| 48 | EndpointAddress(addr) | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | impl From<EndpointAddress> for u8 { | ||
| 53 | #[inline] | ||
| 54 | fn from(addr: EndpointAddress) -> u8 { | ||
| 55 | addr.0 | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | impl EndpointAddress { | ||
| 60 | const INBITS: u8 = UsbDirection::In as u8; | ||
| 61 | |||
| 62 | /// Constructs a new EndpointAddress with the given index and direction. | ||
| 63 | #[inline] | ||
| 64 | pub fn from_parts(index: usize, dir: UsbDirection) -> Self { | ||
| 65 | EndpointAddress(index as u8 | dir as u8) | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Gets the direction part of the address. | ||
| 69 | #[inline] | ||
| 70 | pub fn direction(&self) -> UsbDirection { | ||
| 71 | if (self.0 & Self::INBITS) != 0 { | ||
| 72 | UsbDirection::In | ||
| 73 | } else { | ||
| 74 | UsbDirection::Out | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Returns true if the direction is IN, otherwise false. | ||
| 79 | #[inline] | ||
| 80 | pub fn is_in(&self) -> bool { | ||
| 81 | (self.0 & Self::INBITS) != 0 | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Returns true if the direction is OUT, otherwise false. | ||
| 85 | #[inline] | ||
| 86 | pub fn is_out(&self) -> bool { | ||
| 87 | (self.0 & Self::INBITS) == 0 | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Gets the index part of the endpoint address. | ||
| 91 | #[inline] | ||
| 92 | pub fn index(&self) -> usize { | ||
| 93 | (self.0 & !Self::INBITS) as usize | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 98 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 99 | pub struct EndpointInfo { | ||
| 100 | pub addr: EndpointAddress, | ||
| 101 | pub ep_type: EndpointType, | ||
| 102 | pub max_packet_size: u16, | ||
| 103 | pub interval: u8, | ||
| 104 | } | ||
| 105 | |||
| 106 | /// A handle for a USB interface that contains its number. | ||
| 107 | #[derive(Copy, Clone, Eq, PartialEq)] | ||
| 108 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 109 | pub struct InterfaceNumber(u8); | ||
| 110 | |||
| 111 | impl InterfaceNumber { | ||
| 112 | pub(crate) fn new(index: u8) -> InterfaceNumber { | ||
| 113 | InterfaceNumber(index) | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | impl From<InterfaceNumber> for u8 { | ||
| 118 | fn from(n: InterfaceNumber) -> u8 { | ||
| 119 | n.0 | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | /// A handle for a USB string descriptor that contains its index. | ||
| 124 | #[derive(Copy, Clone, Eq, PartialEq)] | ||
| 125 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 126 | pub struct StringIndex(u8); | ||
| 127 | |||
| 128 | impl StringIndex { | ||
| 129 | pub(crate) fn new(index: u8) -> StringIndex { | ||
| 130 | StringIndex(index) | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | impl From<StringIndex> for u8 { | ||
| 135 | fn from(i: StringIndex) -> u8 { | ||
| 136 | i.0 | ||
| 137 | } | ||
| 138 | } | ||
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index a704eb3bc..fb846b3a9 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml | |||
| @@ -10,7 +10,8 @@ nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits"] | |||
| 10 | 10 | ||
| 11 | [dependencies] | 11 | [dependencies] |
| 12 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } | 12 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } | 13 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 14 | 15 | ||
| 15 | defmt = "0.3" | 16 | defmt = "0.3" |
| 16 | defmt-rtt = "0.3" | 17 | defmt-rtt = "0.3" |
| @@ -22,5 +23,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa | |||
| 22 | rand = { version = "0.8.4", default-features = false } | 23 | rand = { version = "0.8.4", default-features = false } |
| 23 | embedded-storage = "0.3.0" | 24 | embedded-storage = "0.3.0" |
| 24 | 25 | ||
| 25 | usb-device = "0.2" | ||
| 26 | usbd-serial = "0.1.1" | ||
diff --git a/examples/nrf/src/bin/usb/cdc_acm.rs b/examples/nrf/src/bin/usb/cdc_acm.rs new file mode 100644 index 000000000..345d00389 --- /dev/null +++ b/examples/nrf/src/bin/usb/cdc_acm.rs | |||
| @@ -0,0 +1,356 @@ | |||
| 1 | use core::convert::TryInto; | ||
| 2 | use core::mem; | ||
| 3 | use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError}; | ||
| 4 | use embassy_usb::{driver::Driver, types::*, UsbDeviceBuilder}; | ||
| 5 | |||
| 6 | /// This should be used as `device_class` when building the `UsbDevice`. | ||
| 7 | pub const USB_CLASS_CDC: u8 = 0x02; | ||
| 8 | |||
| 9 | const USB_CLASS_CDC_DATA: u8 = 0x0a; | ||
| 10 | const CDC_SUBCLASS_ACM: u8 = 0x02; | ||
| 11 | const CDC_PROTOCOL_NONE: u8 = 0x00; | ||
| 12 | |||
| 13 | const CS_INTERFACE: u8 = 0x24; | ||
| 14 | const CDC_TYPE_HEADER: u8 = 0x00; | ||
| 15 | const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01; | ||
| 16 | const CDC_TYPE_ACM: u8 = 0x02; | ||
| 17 | const CDC_TYPE_UNION: u8 = 0x06; | ||
| 18 | |||
| 19 | const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00; | ||
| 20 | #[allow(unused)] | ||
| 21 | const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01; | ||
| 22 | const REQ_SET_LINE_CODING: u8 = 0x20; | ||
| 23 | const REQ_GET_LINE_CODING: u8 = 0x21; | ||
| 24 | const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; | ||
| 25 | |||
| 26 | /// Packet level implementation of a CDC-ACM serial port. | ||
| 27 | /// | ||
| 28 | /// This class can be used directly and it has the least overhead due to directly reading and | ||
| 29 | /// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial | ||
| 30 | /// port. The following constraints must be followed if you use this class directly: | ||
| 31 | /// | ||
| 32 | /// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes, and the | ||
| 33 | /// method will return a `WouldBlock` error if there is no packet to be read. | ||
| 34 | /// - `write_packet` must not be called with a buffer larger than max_packet_size bytes, and the | ||
| 35 | /// method will return a `WouldBlock` error if the previous packet has not been sent yet. | ||
| 36 | /// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the | ||
| 37 | /// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP) | ||
| 38 | /// can be sent if there is no other data to send. This is because USB bulk transactions must be | ||
| 39 | /// terminated with a short packet, even if the bulk endpoint is used for stream-like data. | ||
| 40 | pub struct CdcAcmClass<'d, D: Driver<'d>> { | ||
| 41 | comm_if: InterfaceNumber, | ||
| 42 | comm_ep: D::EndpointIn, | ||
| 43 | data_if: InterfaceNumber, | ||
| 44 | read_ep: D::EndpointOut, | ||
| 45 | write_ep: D::EndpointIn, | ||
| 46 | line_coding: LineCoding, | ||
| 47 | dtr: bool, | ||
| 48 | rts: bool, | ||
| 49 | } | ||
| 50 | |||
| 51 | impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | ||
| 52 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For | ||
| 53 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. | ||
| 54 | pub fn new(builder: &mut UsbDeviceBuilder<'d, D>, max_packet_size: u16) -> Self { | ||
| 55 | let comm_if = builder.alloc_interface(); | ||
| 56 | let comm_ep = builder.alloc_interrupt_endpoint_in(8, 255); | ||
| 57 | let data_if = builder.alloc_interface(); | ||
| 58 | let read_ep = builder.alloc_bulk_endpoint_out(max_packet_size); | ||
| 59 | let write_ep = builder.alloc_bulk_endpoint_in(max_packet_size); | ||
| 60 | |||
| 61 | builder | ||
| 62 | .config_descriptor | ||
| 63 | .iad( | ||
| 64 | comm_if, | ||
| 65 | 2, | ||
| 66 | USB_CLASS_CDC, | ||
| 67 | CDC_SUBCLASS_ACM, | ||
| 68 | CDC_PROTOCOL_NONE, | ||
| 69 | ) | ||
| 70 | .unwrap(); | ||
| 71 | |||
| 72 | builder | ||
| 73 | .config_descriptor | ||
| 74 | .interface(comm_if, USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE) | ||
| 75 | .unwrap(); | ||
| 76 | |||
| 77 | builder | ||
| 78 | .config_descriptor | ||
| 79 | .write( | ||
| 80 | CS_INTERFACE, | ||
| 81 | &[ | ||
| 82 | CDC_TYPE_HEADER, // bDescriptorSubtype | ||
| 83 | 0x10, | ||
| 84 | 0x01, // bcdCDC (1.10) | ||
| 85 | ], | ||
| 86 | ) | ||
| 87 | .unwrap(); | ||
| 88 | |||
| 89 | builder | ||
| 90 | .config_descriptor | ||
| 91 | .write( | ||
| 92 | CS_INTERFACE, | ||
| 93 | &[ | ||
| 94 | CDC_TYPE_ACM, // bDescriptorSubtype | ||
| 95 | 0x00, // bmCapabilities | ||
| 96 | ], | ||
| 97 | ) | ||
| 98 | .unwrap(); | ||
| 99 | |||
| 100 | builder | ||
| 101 | .config_descriptor | ||
| 102 | .write( | ||
| 103 | CS_INTERFACE, | ||
| 104 | &[ | ||
| 105 | CDC_TYPE_UNION, // bDescriptorSubtype | ||
| 106 | comm_if.into(), // bControlInterface | ||
| 107 | data_if.into(), // bSubordinateInterface | ||
| 108 | ], | ||
| 109 | ) | ||
| 110 | .unwrap(); | ||
| 111 | |||
| 112 | builder | ||
| 113 | .config_descriptor | ||
| 114 | .write( | ||
| 115 | CS_INTERFACE, | ||
| 116 | &[ | ||
| 117 | CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype | ||
| 118 | 0x00, // bmCapabilities | ||
| 119 | data_if.into(), // bDataInterface | ||
| 120 | ], | ||
| 121 | ) | ||
| 122 | .unwrap(); | ||
| 123 | |||
| 124 | builder.config_descriptor.endpoint(comm_ep.info()).unwrap(); | ||
| 125 | |||
| 126 | builder | ||
| 127 | .config_descriptor | ||
| 128 | .interface(data_if, USB_CLASS_CDC_DATA, 0x00, 0x00) | ||
| 129 | .unwrap(); | ||
| 130 | |||
| 131 | builder.config_descriptor.endpoint(write_ep.info()).unwrap(); | ||
| 132 | builder.config_descriptor.endpoint(read_ep.info()).unwrap(); | ||
| 133 | |||
| 134 | CdcAcmClass { | ||
| 135 | comm_if, | ||
| 136 | comm_ep, | ||
| 137 | data_if, | ||
| 138 | read_ep, | ||
| 139 | write_ep, | ||
| 140 | line_coding: LineCoding { | ||
| 141 | stop_bits: StopBits::One, | ||
| 142 | data_bits: 8, | ||
| 143 | parity_type: ParityType::None, | ||
| 144 | data_rate: 8_000, | ||
| 145 | }, | ||
| 146 | dtr: false, | ||
| 147 | rts: false, | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | /// Gets the maximum packet size in bytes. | ||
| 152 | pub fn max_packet_size(&self) -> u16 { | ||
| 153 | // The size is the same for both endpoints. | ||
| 154 | self.read_ep.info().max_packet_size | ||
| 155 | } | ||
| 156 | |||
| 157 | /// Gets the current line coding. The line coding contains information that's mainly relevant | ||
| 158 | /// for USB to UART serial port emulators, and can be ignored if not relevant. | ||
| 159 | pub fn line_coding(&self) -> &LineCoding { | ||
| 160 | &self.line_coding | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Gets the DTR (data terminal ready) state | ||
| 164 | pub fn dtr(&self) -> bool { | ||
| 165 | self.dtr | ||
| 166 | } | ||
| 167 | |||
| 168 | /// Gets the RTS (request to send) state | ||
| 169 | pub fn rts(&self) -> bool { | ||
| 170 | self.rts | ||
| 171 | } | ||
| 172 | |||
| 173 | /// Writes a single packet into the IN endpoint. | ||
| 174 | pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), WriteError> { | ||
| 175 | self.write_ep.write(data).await | ||
| 176 | } | ||
| 177 | |||
| 178 | /// Reads a single packet from the OUT endpoint. | ||
| 179 | pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, ReadError> { | ||
| 180 | self.read_ep.read(data).await | ||
| 181 | } | ||
| 182 | |||
| 183 | /// Gets the address of the IN endpoint. | ||
| 184 | pub(crate) fn write_ep_address(&self) -> EndpointAddress { | ||
| 185 | self.write_ep.info().addr | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | /* | ||
| 190 | impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> { | ||
| 191 | fn get_configuration_descriptors(&self, builder.config_descriptor: &mut Descriptorbuilder.config_descriptor) -> Result<()> { | ||
| 192 | |||
| 193 | Ok(()) | ||
| 194 | } | ||
| 195 | |||
| 196 | fn reset(&mut self) { | ||
| 197 | self.line_coding = LineCoding::default(); | ||
| 198 | self.dtr = false; | ||
| 199 | self.rts = false; | ||
| 200 | } | ||
| 201 | |||
| 202 | fn control_in(&mut self, xfer: ControlIn<B>) { | ||
| 203 | let req = xfer.request(); | ||
| 204 | |||
| 205 | if !(req.request_type == control::RequestType::Class | ||
| 206 | && req.recipient == control::Recipient::Interface | ||
| 207 | && req.index == u8::from(self.comm_if) as u16) | ||
| 208 | { | ||
| 209 | return; | ||
| 210 | } | ||
| 211 | |||
| 212 | match req.request { | ||
| 213 | // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. | ||
| 214 | REQ_GET_LINE_CODING if req.length == 7 => { | ||
| 215 | xfer.accept(|data| { | ||
| 216 | data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes()); | ||
| 217 | data[4] = self.line_coding.stop_bits as u8; | ||
| 218 | data[5] = self.line_coding.parity_type as u8; | ||
| 219 | data[6] = self.line_coding.data_bits; | ||
| 220 | |||
| 221 | Ok(7) | ||
| 222 | }) | ||
| 223 | .ok(); | ||
| 224 | } | ||
| 225 | _ => { | ||
| 226 | xfer.reject().ok(); | ||
| 227 | } | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | fn control_out(&mut self, xfer: ControlOut<B>) { | ||
| 232 | let req = xfer.request(); | ||
| 233 | |||
| 234 | if !(req.request_type == control::RequestType::Class | ||
| 235 | && req.recipient == control::Recipient::Interface | ||
| 236 | && req.index == u8::from(self.comm_if) as u16) | ||
| 237 | { | ||
| 238 | return; | ||
| 239 | } | ||
| 240 | |||
| 241 | match req.request { | ||
| 242 | REQ_SEND_ENCAPSULATED_COMMAND => { | ||
| 243 | // We don't actually support encapsulated commands but pretend we do for standards | ||
| 244 | // compatibility. | ||
| 245 | xfer.accept().ok(); | ||
| 246 | } | ||
| 247 | REQ_SET_LINE_CODING if xfer.data().len() >= 7 => { | ||
| 248 | self.line_coding.data_rate = | ||
| 249 | u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap()); | ||
| 250 | self.line_coding.stop_bits = xfer.data()[4].into(); | ||
| 251 | self.line_coding.parity_type = xfer.data()[5].into(); | ||
| 252 | self.line_coding.data_bits = xfer.data()[6]; | ||
| 253 | |||
| 254 | xfer.accept().ok(); | ||
| 255 | } | ||
| 256 | REQ_SET_CONTROL_LINE_STATE => { | ||
| 257 | self.dtr = (req.value & 0x0001) != 0; | ||
| 258 | self.rts = (req.value & 0x0002) != 0; | ||
| 259 | |||
| 260 | xfer.accept().ok(); | ||
| 261 | } | ||
| 262 | _ => { | ||
| 263 | xfer.reject().ok(); | ||
| 264 | } | ||
| 265 | }; | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | */ | ||
| 270 | |||
| 271 | /// Number of stop bits for LineCoding | ||
| 272 | #[derive(Copy, Clone, PartialEq, Eq)] | ||
| 273 | pub enum StopBits { | ||
| 274 | /// 1 stop bit | ||
| 275 | One = 0, | ||
| 276 | |||
| 277 | /// 1.5 stop bits | ||
| 278 | OnePointFive = 1, | ||
| 279 | |||
| 280 | /// 2 stop bits | ||
| 281 | Two = 2, | ||
| 282 | } | ||
| 283 | |||
| 284 | impl From<u8> for StopBits { | ||
| 285 | fn from(value: u8) -> Self { | ||
| 286 | if value <= 2 { | ||
| 287 | unsafe { mem::transmute(value) } | ||
| 288 | } else { | ||
| 289 | StopBits::One | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Parity for LineCoding | ||
| 295 | #[derive(Copy, Clone, PartialEq, Eq)] | ||
| 296 | pub enum ParityType { | ||
| 297 | None = 0, | ||
| 298 | Odd = 1, | ||
| 299 | Event = 2, | ||
| 300 | Mark = 3, | ||
| 301 | Space = 4, | ||
| 302 | } | ||
| 303 | |||
| 304 | impl From<u8> for ParityType { | ||
| 305 | fn from(value: u8) -> Self { | ||
| 306 | if value <= 4 { | ||
| 307 | unsafe { mem::transmute(value) } | ||
| 308 | } else { | ||
| 309 | ParityType::None | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | /// Line coding parameters | ||
| 315 | /// | ||
| 316 | /// This is provided by the host for specifying the standard UART parameters such as baud rate. Can | ||
| 317 | /// be ignored if you don't plan to interface with a physical UART. | ||
| 318 | pub struct LineCoding { | ||
| 319 | stop_bits: StopBits, | ||
| 320 | data_bits: u8, | ||
| 321 | parity_type: ParityType, | ||
| 322 | data_rate: u32, | ||
| 323 | } | ||
| 324 | |||
| 325 | impl LineCoding { | ||
| 326 | /// Gets the number of stop bits for UART communication. | ||
| 327 | pub fn stop_bits(&self) -> StopBits { | ||
| 328 | self.stop_bits | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Gets the number of data bits for UART communication. | ||
| 332 | pub fn data_bits(&self) -> u8 { | ||
| 333 | self.data_bits | ||
| 334 | } | ||
| 335 | |||
| 336 | /// Gets the parity type for UART communication. | ||
| 337 | pub fn parity_type(&self) -> ParityType { | ||
| 338 | self.parity_type | ||
| 339 | } | ||
| 340 | |||
| 341 | /// Gets the data rate in bits per second for UART communication. | ||
| 342 | pub fn data_rate(&self) -> u32 { | ||
| 343 | self.data_rate | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | impl Default for LineCoding { | ||
| 348 | fn default() -> Self { | ||
| 349 | LineCoding { | ||
| 350 | stop_bits: StopBits::One, | ||
| 351 | data_bits: 8, | ||
| 352 | parity_type: ParityType::None, | ||
| 353 | data_rate: 8_000, | ||
| 354 | } | ||
| 355 | } | ||
| 356 | } | ||
diff --git a/examples/nrf/src/bin/usb/main.rs b/examples/nrf/src/bin/usb/main.rs new file mode 100644 index 000000000..21ca2ba4f --- /dev/null +++ b/examples/nrf/src/bin/usb/main.rs | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | #[path = "../../example_common.rs"] | ||
| 6 | mod example_common; | ||
| 7 | |||
| 8 | mod cdc_acm; | ||
| 9 | |||
| 10 | use core::mem; | ||
| 11 | use defmt::*; | ||
| 12 | use embassy::executor::Spawner; | ||
| 13 | use embassy_nrf::interrupt; | ||
| 14 | use embassy_nrf::pac; | ||
| 15 | use embassy_nrf::usb::{self, Driver}; | ||
| 16 | use embassy_nrf::Peripherals; | ||
| 17 | use embassy_usb::{Config, UsbDeviceBuilder}; | ||
| 18 | |||
| 19 | use crate::cdc_acm::CdcAcmClass; | ||
| 20 | |||
| 21 | #[embassy::main] | ||
| 22 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 23 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 24 | let power: pac::POWER = unsafe { mem::transmute(()) }; | ||
| 25 | |||
| 26 | info!("Enabling ext hfosc..."); | ||
| 27 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 28 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 29 | |||
| 30 | info!("Waiting for vbus..."); | ||
| 31 | while !power.usbregstatus.read().vbusdetect().is_vbus_present() {} | ||
| 32 | info!("vbus OK"); | ||
| 33 | |||
| 34 | let irq = interrupt::take!(USBD); | ||
| 35 | let driver = Driver::new(p.USBD, irq); | ||
| 36 | let config = Config::new(0xc0de, 0xcafe); | ||
| 37 | let mut device_descriptor = [0; 256]; | ||
| 38 | let mut config_descriptor = [0; 256]; | ||
| 39 | let mut bos_descriptor = [0; 256]; | ||
| 40 | |||
| 41 | let mut builder = UsbDeviceBuilder::new( | ||
| 42 | driver, | ||
| 43 | config, | ||
| 44 | &mut device_descriptor, | ||
| 45 | &mut config_descriptor, | ||
| 46 | &mut bos_descriptor, | ||
| 47 | ); | ||
| 48 | |||
| 49 | let mut class = CdcAcmClass::new(&mut builder, 64); | ||
| 50 | |||
| 51 | let mut usb = builder.build(); | ||
| 52 | usb.run().await; | ||
| 53 | } | ||
diff --git a/examples/nrf/src/bin/usb_uart.rs b/examples/nrf/src/bin/usb_uart.rs deleted file mode 100644 index d283dccd1..000000000 --- a/examples/nrf/src/bin/usb_uart.rs +++ /dev/null | |||
| @@ -1,89 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy::executor::Spawner; | ||
| 7 | use embassy::interrupt::InterruptExt; | ||
| 8 | use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; | ||
| 9 | use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial}; | ||
| 10 | use embassy_nrf::{interrupt, Peripherals}; | ||
| 11 | use futures::pin_mut; | ||
| 12 | use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; | ||
| 13 | |||
| 14 | use defmt_rtt as _; // global logger | ||
| 15 | use panic_probe as _; // print out panic messages | ||
| 16 | |||
| 17 | #[embassy::main] | ||
| 18 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 19 | let mut rx_buffer = [0u8; 64]; | ||
| 20 | // we send back input + cr + lf | ||
| 21 | let mut tx_buffer = [0u8; 66]; | ||
| 22 | |||
| 23 | let usb_bus = UsbBus::new(p.USBD); | ||
| 24 | |||
| 25 | let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); | ||
| 26 | |||
| 27 | let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) | ||
| 28 | .manufacturer("Fake company") | ||
| 29 | .product("Serial port") | ||
| 30 | .serial_number("TEST") | ||
| 31 | .device_class(0x02) | ||
| 32 | .build(); | ||
| 33 | |||
| 34 | let irq = interrupt::take!(USBD); | ||
| 35 | irq.set_priority(interrupt::Priority::P3); | ||
| 36 | |||
| 37 | let mut state = State::new(); | ||
| 38 | let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; | ||
| 39 | pin_mut!(usb); | ||
| 40 | |||
| 41 | let (mut reader, mut writer) = usb.as_ref().take_serial_0(); | ||
| 42 | |||
| 43 | info!("usb initialized!"); | ||
| 44 | |||
| 45 | unwrap!( | ||
| 46 | writer | ||
| 47 | .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") | ||
| 48 | .await | ||
| 49 | ); | ||
| 50 | |||
| 51 | let mut buf = [0u8; 64]; | ||
| 52 | loop { | ||
| 53 | let mut n = 0; | ||
| 54 | |||
| 55 | async { | ||
| 56 | loop { | ||
| 57 | let char = unwrap!(reader.read_byte().await); | ||
| 58 | |||
| 59 | // throw away, read more on cr, exit on lf | ||
| 60 | if char == b'\r' { | ||
| 61 | continue; | ||
| 62 | } else if char == b'\n' { | ||
| 63 | break; | ||
| 64 | } | ||
| 65 | |||
| 66 | buf[n] = char; | ||
| 67 | n += 1; | ||
| 68 | |||
| 69 | // stop if we're out of room | ||
| 70 | if n == buf.len() { | ||
| 71 | break; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | .await; | ||
| 76 | |||
| 77 | if n > 0 { | ||
| 78 | for char in buf[..n].iter_mut() { | ||
| 79 | // upper case | ||
| 80 | if 0x61 <= *char && *char <= 0x7a { | ||
| 81 | *char &= !0x20; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | unwrap!(writer.write_all(&buf[..n]).await); | ||
| 85 | unwrap!(writer.write_all(b"\r\n").await); | ||
| 86 | unwrap!(writer.flush().await); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
diff --git a/examples/nrf/src/bin/usb_uart_io.rs b/examples/nrf/src/bin/usb_uart_io.rs deleted file mode 100644 index ef2629844..000000000 --- a/examples/nrf/src/bin/usb_uart_io.rs +++ /dev/null | |||
| @@ -1,66 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy::executor::Spawner; | ||
| 7 | use embassy::interrupt::InterruptExt; | ||
| 8 | use embassy::io::{read_line, AsyncWriteExt}; | ||
| 9 | use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial}; | ||
| 10 | use embassy_nrf::{interrupt, Peripherals}; | ||
| 11 | use futures::pin_mut; | ||
| 12 | use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; | ||
| 13 | |||
| 14 | use defmt_rtt as _; // global logger | ||
| 15 | use panic_probe as _; // print out panic messages | ||
| 16 | |||
| 17 | #[embassy::main] | ||
| 18 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 19 | let mut rx_buffer = [0u8; 64]; | ||
| 20 | // we send back input + cr + lf | ||
| 21 | let mut tx_buffer = [0u8; 66]; | ||
| 22 | |||
| 23 | let usb_bus = UsbBus::new(p.USBD); | ||
| 24 | |||
| 25 | let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); | ||
| 26 | |||
| 27 | let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) | ||
| 28 | .manufacturer("Fake company") | ||
| 29 | .product("Serial port") | ||
| 30 | .serial_number("TEST") | ||
| 31 | .device_class(0x02) | ||
| 32 | .build(); | ||
| 33 | |||
| 34 | let irq = interrupt::take!(USBD); | ||
| 35 | irq.set_priority(interrupt::Priority::P3); | ||
| 36 | |||
| 37 | let mut state = State::new(); | ||
| 38 | let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; | ||
| 39 | pin_mut!(usb); | ||
| 40 | |||
| 41 | let (mut reader, mut writer) = usb.as_ref().take_serial_0(); | ||
| 42 | |||
| 43 | info!("usb initialized!"); | ||
| 44 | |||
| 45 | unwrap!( | ||
| 46 | writer | ||
| 47 | .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") | ||
| 48 | .await | ||
| 49 | ); | ||
| 50 | |||
| 51 | let mut buf = [0u8; 64]; | ||
| 52 | loop { | ||
| 53 | let n = unwrap!(read_line(&mut reader, &mut buf).await); | ||
| 54 | |||
| 55 | for char in buf[..n].iter_mut() { | ||
| 56 | // upper case | ||
| 57 | if 0x61 <= *char && *char <= 0x7a { | ||
| 58 | *char &= !0x20; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | unwrap!(writer.write_all(&buf[..n]).await); | ||
| 63 | unwrap!(writer.write_all(b"\r\n").await); | ||
| 64 | unwrap!(writer.flush().await); | ||
| 65 | } | ||
| 66 | } | ||
