diff options
| author | alexmoon <[email protected]> | 2022-04-02 16:35:03 -0400 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-04-06 05:38:11 +0200 |
| commit | 2ce435dc341c0238392df5dab5db9b80db167117 (patch) | |
| tree | 19e64218922a67601540261749af81d2d1d0aac4 | |
| parent | 99f95a33c30b08359fcd72123fea01f4de0903ec (diff) | |
Add basic device state handling for endpoints.
| -rw-r--r-- | embassy-nrf/src/usb.rs | 180 | ||||
| -rw-r--r-- | embassy-usb-serial/src/lib.rs | 5 | ||||
| -rw-r--r-- | embassy-usb/src/driver.rs | 12 | ||||
| -rw-r--r-- | embassy-usb/src/lib.rs | 7 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb_serial.rs | 46 |
5 files changed, 190 insertions, 60 deletions
diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 124316a29..c16e1be00 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs | |||
| @@ -17,6 +17,7 @@ use futures::future::poll_fn; | |||
| 17 | use futures::Future; | 17 | use futures::Future; |
| 18 | 18 | ||
| 19 | pub use embassy_usb; | 19 | pub use embassy_usb; |
| 20 | use pac::usbd::RegisterBlock; | ||
| 20 | 21 | ||
| 21 | use crate::interrupt::Interrupt; | 22 | use crate::interrupt::Interrupt; |
| 22 | use crate::pac; | 23 | use crate::pac; |
| @@ -92,11 +93,11 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 92 | regs.epdatastatus.write(|w| unsafe { w.bits(r) }); | 93 | regs.epdatastatus.write(|w| unsafe { w.bits(r) }); |
| 93 | READY_ENDPOINTS.fetch_or(r, Ordering::AcqRel); | 94 | READY_ENDPOINTS.fetch_or(r, Ordering::AcqRel); |
| 94 | for i in 1..=7 { | 95 | for i in 1..=7 { |
| 95 | if r & (1 << i) != 0 { | 96 | if r & In::mask(i) != 0 { |
| 96 | EP_IN_WAKERS[i - 1].wake(); | 97 | In::waker(i).wake(); |
| 97 | } | 98 | } |
| 98 | if r & (1 << (i + 16)) != 0 { | 99 | if r & Out::mask(i) != 0 { |
| 99 | EP_OUT_WAKERS[i - 1].wake(); | 100 | Out::waker(i).wake(); |
| 100 | } | 101 | } |
| 101 | } | 102 | } |
| 102 | } | 103 | } |
| @@ -272,32 +273,48 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { | |||
| 272 | 273 | ||
| 273 | #[inline] | 274 | #[inline] |
| 274 | fn reset(&mut self) { | 275 | fn reset(&mut self) { |
| 275 | let regs = T::regs(); | 276 | self.set_configured(false); |
| 277 | } | ||
| 276 | 278 | ||
| 277 | // TODO: Initialize ISO buffers | 279 | #[inline] |
| 280 | fn set_configured(&mut self, configured: bool) { | ||
| 281 | let regs = T::regs(); | ||
| 278 | 282 | ||
| 279 | // XXX this is not spec compliant; the endpoints should only be enabled after the device | ||
| 280 | // has been put in the Configured state. However, usb-device provides no hook to do that | ||
| 281 | unsafe { | 283 | unsafe { |
| 282 | regs.epinen.write(|w| w.bits(self.alloc_in.used.into())); | 284 | if configured { |
| 283 | regs.epouten.write(|w| w.bits(self.alloc_out.used.into())); | 285 | // TODO: Initialize ISO buffers |
| 284 | } | ||
| 285 | 286 | ||
| 286 | for i in 1..8 { | 287 | regs.epinen.write(|w| w.bits(self.alloc_in.used.into())); |
| 287 | let out_enabled = self.alloc_out.used & (1 << i) != 0; | 288 | regs.epouten.write(|w| w.bits(self.alloc_out.used.into())); |
| 289 | |||
| 290 | for i in 1..8 { | ||
| 291 | let out_enabled = self.alloc_out.used & (1 << i) != 0; | ||
| 292 | |||
| 293 | // when first enabled, bulk/interrupt OUT endpoints will *not* receive data (the | ||
| 294 | // peripheral will NAK all incoming packets) until we write a zero to the SIZE | ||
| 295 | // register (see figure 203 of the 52840 manual). To avoid that we write a 0 to the | ||
| 296 | // SIZE register | ||
| 297 | if out_enabled { | ||
| 298 | regs.size.epout[i].reset(); | ||
| 299 | } | ||
| 300 | } | ||
| 288 | 301 | ||
| 289 | // when first enabled, bulk/interrupt OUT endpoints will *not* receive data (the | 302 | // IN endpoints (low bits) default to ready. |
| 290 | // peripheral will NAK all incoming packets) until we write a zero to the SIZE | 303 | // OUT endpoints (high bits) default to NOT ready, they become ready when data comes in. |
| 291 | // register (see figure 203 of the 52840 manual). To avoid that we write a 0 to the | 304 | READY_ENDPOINTS.store(0x0000FFFF, Ordering::Release); |
| 292 | // SIZE register | 305 | } else { |
| 293 | if out_enabled { | 306 | // Disable all endpoints except EP0 |
| 294 | regs.size.epout[i].reset(); | 307 | regs.epinen.write(|w| w.bits(0x01)); |
| 308 | regs.epouten.write(|w| w.bits(0x01)); | ||
| 309 | |||
| 310 | READY_ENDPOINTS.store(In::mask(0), Ordering::Release); | ||
| 295 | } | 311 | } |
| 296 | } | 312 | } |
| 297 | 313 | ||
| 298 | // IN endpoints (low bits) default to ready. | 314 | for i in 1..=7 { |
| 299 | // OUT endpoints (high bits) default to NOT ready, they become ready when data comes in. | 315 | In::waker(i).wake(); |
| 300 | READY_ENDPOINTS.store(0x0000FFFF, Ordering::Release); | 316 | Out::waker(i).wake(); |
| 317 | } | ||
| 301 | } | 318 | } |
| 302 | 319 | ||
| 303 | #[inline] | 320 | #[inline] |
| @@ -332,6 +349,46 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { | |||
| 332 | pub enum Out {} | 349 | pub enum Out {} |
| 333 | pub enum In {} | 350 | pub enum In {} |
| 334 | 351 | ||
| 352 | trait EndpointDir { | ||
| 353 | fn waker(i: usize) -> &'static AtomicWaker; | ||
| 354 | fn mask(i: usize) -> u32; | ||
| 355 | fn is_enabled(regs: &RegisterBlock, i: usize) -> bool; | ||
| 356 | } | ||
| 357 | |||
| 358 | impl EndpointDir for In { | ||
| 359 | #[inline] | ||
| 360 | fn waker(i: usize) -> &'static AtomicWaker { | ||
| 361 | &EP_IN_WAKERS[i - 1] | ||
| 362 | } | ||
| 363 | |||
| 364 | #[inline] | ||
| 365 | fn mask(i: usize) -> u32 { | ||
| 366 | 1 << i | ||
| 367 | } | ||
| 368 | |||
| 369 | #[inline] | ||
| 370 | fn is_enabled(regs: &RegisterBlock, i: usize) -> bool { | ||
| 371 | (regs.epinen.read().bits() & (1 << i)) != 0 | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | impl EndpointDir for Out { | ||
| 376 | #[inline] | ||
| 377 | fn waker(i: usize) -> &'static AtomicWaker { | ||
| 378 | &EP_OUT_WAKERS[i - 1] | ||
| 379 | } | ||
| 380 | |||
| 381 | #[inline] | ||
| 382 | fn mask(i: usize) -> u32 { | ||
| 383 | 1 << (i + 16) | ||
| 384 | } | ||
| 385 | |||
| 386 | #[inline] | ||
| 387 | fn is_enabled(regs: &RegisterBlock, i: usize) -> bool { | ||
| 388 | (regs.epouten.read().bits() & (1 << i)) != 0 | ||
| 389 | } | ||
| 390 | } | ||
| 391 | |||
| 335 | pub struct Endpoint<'d, T: Instance, Dir> { | 392 | pub struct Endpoint<'d, T: Instance, Dir> { |
| 336 | _phantom: PhantomData<(&'d mut T, Dir)>, | 393 | _phantom: PhantomData<(&'d mut T, Dir)>, |
| 337 | info: EndpointInfo, | 394 | info: EndpointInfo, |
| @@ -346,7 +403,7 @@ impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> { | |||
| 346 | } | 403 | } |
| 347 | } | 404 | } |
| 348 | 405 | ||
| 349 | impl<'d, T: Instance, Dir> driver::Endpoint for Endpoint<'d, T, Dir> { | 406 | impl<'d, T: Instance, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, T, Dir> { |
| 350 | fn info(&self) -> &EndpointInfo { | 407 | fn info(&self) -> &EndpointInfo { |
| 351 | &self.info | 408 | &self.info |
| 352 | } | 409 | } |
| @@ -358,6 +415,49 @@ impl<'d, T: Instance, Dir> driver::Endpoint for Endpoint<'d, T, Dir> { | |||
| 358 | fn is_stalled(&self) -> bool { | 415 | fn is_stalled(&self) -> bool { |
| 359 | Driver::<T>::is_stalled(self.info.addr) | 416 | Driver::<T>::is_stalled(self.info.addr) |
| 360 | } | 417 | } |
| 418 | |||
| 419 | type WaitEnabledFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a; | ||
| 420 | |||
| 421 | fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { | ||
| 422 | let i = self.info.addr.index(); | ||
| 423 | assert!(i != 0); | ||
| 424 | |||
| 425 | poll_fn(move |cx| { | ||
| 426 | Dir::waker(i).register(cx.waker()); | ||
| 427 | if Dir::is_enabled(T::regs(), i) { | ||
| 428 | Poll::Ready(()) | ||
| 429 | } else { | ||
| 430 | Poll::Pending | ||
| 431 | } | ||
| 432 | }) | ||
| 433 | } | ||
| 434 | } | ||
| 435 | |||
| 436 | impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> { | ||
| 437 | async fn wait_data_ready(&mut self) -> Result<(), ()> | ||
| 438 | where | ||
| 439 | Dir: EndpointDir, | ||
| 440 | { | ||
| 441 | let i = self.info.addr.index(); | ||
| 442 | assert!(i != 0); | ||
| 443 | poll_fn(|cx| { | ||
| 444 | Dir::waker(i).register(cx.waker()); | ||
| 445 | let r = READY_ENDPOINTS.load(Ordering::Acquire); | ||
| 446 | if !Dir::is_enabled(T::regs(), i) { | ||
| 447 | Poll::Ready(Err(())) | ||
| 448 | } else if r & Dir::mask(i) != 0 { | ||
| 449 | Poll::Ready(Ok(())) | ||
| 450 | } else { | ||
| 451 | Poll::Pending | ||
| 452 | } | ||
| 453 | }) | ||
| 454 | .await?; | ||
| 455 | |||
| 456 | // Mark as not ready | ||
| 457 | READY_ENDPOINTS.fetch_and(!Dir::mask(i), Ordering::AcqRel); | ||
| 458 | |||
| 459 | Ok(()) | ||
| 460 | } | ||
| 361 | } | 461 | } |
| 362 | 462 | ||
| 363 | unsafe fn read_dma<T: Instance>(i: usize, buf: &mut [u8]) -> Result<usize, ReadError> { | 463 | unsafe fn read_dma<T: Instance>(i: usize, buf: &mut [u8]) -> Result<usize, ReadError> { |
| @@ -449,20 +549,9 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { | |||
| 449 | let i = self.info.addr.index(); | 549 | let i = self.info.addr.index(); |
| 450 | assert!(i != 0); | 550 | assert!(i != 0); |
| 451 | 551 | ||
| 452 | // Wait until ready | 552 | self.wait_data_ready() |
| 453 | poll_fn(|cx| { | 553 | .await |
| 454 | EP_OUT_WAKERS[i - 1].register(cx.waker()); | 554 | .map_err(|_| ReadError::Disabled)?; |
| 455 | let r = READY_ENDPOINTS.load(Ordering::Acquire); | ||
| 456 | if r & (1 << (i + 16)) != 0 { | ||
| 457 | Poll::Ready(()) | ||
| 458 | } else { | ||
| 459 | Poll::Pending | ||
| 460 | } | ||
| 461 | }) | ||
| 462 | .await; | ||
| 463 | |||
| 464 | // Mark as not ready | ||
| 465 | READY_ENDPOINTS.fetch_and(!(1 << (i + 16)), Ordering::AcqRel); | ||
| 466 | 555 | ||
| 467 | unsafe { read_dma::<T>(i, buf) } | 556 | unsafe { read_dma::<T>(i, buf) } |
| 468 | } | 557 | } |
| @@ -477,20 +566,9 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { | |||
| 477 | let i = self.info.addr.index(); | 566 | let i = self.info.addr.index(); |
| 478 | assert!(i != 0); | 567 | assert!(i != 0); |
| 479 | 568 | ||
| 480 | // Wait until ready. | 569 | self.wait_data_ready() |
| 481 | poll_fn(|cx| { | 570 | .await |
| 482 | EP_IN_WAKERS[i - 1].register(cx.waker()); | 571 | .map_err(|_| WriteError::Disabled)?; |
| 483 | let r = READY_ENDPOINTS.load(Ordering::Acquire); | ||
| 484 | if r & (1 << i) != 0 { | ||
| 485 | Poll::Ready(()) | ||
| 486 | } else { | ||
| 487 | Poll::Pending | ||
| 488 | } | ||
| 489 | }) | ||
| 490 | .await; | ||
| 491 | |||
| 492 | // Mark as not ready | ||
| 493 | READY_ENDPOINTS.fetch_and(!(1 << i), Ordering::AcqRel); | ||
| 494 | 572 | ||
| 495 | unsafe { write_dma::<T>(i, buf) } | 573 | unsafe { write_dma::<T>(i, buf) } |
| 496 | 574 | ||
diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb-serial/src/lib.rs index 8418de0f0..07352fac5 100644 --- a/embassy-usb-serial/src/lib.rs +++ b/embassy-usb-serial/src/lib.rs | |||
| @@ -273,6 +273,11 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | |||
| 273 | pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, ReadError> { | 273 | pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, ReadError> { |
| 274 | self.read_ep.read(data).await | 274 | self.read_ep.read(data).await |
| 275 | } | 275 | } |
| 276 | |||
| 277 | /// Waits for the USB host to enable this interface | ||
| 278 | pub async fn wait_connection(&mut self) { | ||
| 279 | self.read_ep.wait_enabled().await | ||
| 280 | } | ||
| 276 | } | 281 | } |
| 277 | 282 | ||
| 278 | /// Number of stop bits for LineCoding | 283 | /// Number of stop bits for LineCoding |
diff --git a/embassy-usb/src/driver.rs b/embassy-usb/src/driver.rs index 82b59bd1e..6eaa40b0d 100644 --- a/embassy-usb/src/driver.rs +++ b/embassy-usb/src/driver.rs | |||
| @@ -72,6 +72,9 @@ pub trait Bus { | |||
| 72 | /// Sets the device USB address to `addr`. | 72 | /// Sets the device USB address to `addr`. |
| 73 | fn set_device_address(&mut self, addr: u8); | 73 | fn set_device_address(&mut self, addr: u8); |
| 74 | 74 | ||
| 75 | /// Sets the device configured state. | ||
| 76 | fn set_configured(&mut self, configured: bool); | ||
| 77 | |||
| 75 | /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it | 78 | /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it |
| 76 | /// should be prepared to receive data again. Only used during control transfers. | 79 | /// should be prepared to receive data again. Only used during control transfers. |
| 77 | fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool); | 80 | fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool); |
| @@ -105,6 +108,10 @@ pub trait Bus { | |||
| 105 | } | 108 | } |
| 106 | 109 | ||
| 107 | pub trait Endpoint { | 110 | pub trait Endpoint { |
| 111 | type WaitEnabledFuture<'a>: Future<Output = ()> + 'a | ||
| 112 | where | ||
| 113 | Self: 'a; | ||
| 114 | |||
| 108 | /// Get the endpoint address | 115 | /// Get the endpoint address |
| 109 | fn info(&self) -> &EndpointInfo; | 116 | fn info(&self) -> &EndpointInfo; |
| 110 | 117 | ||
| @@ -115,6 +122,9 @@ pub trait Endpoint { | |||
| 115 | /// Gets whether the STALL condition is set for an endpoint. | 122 | /// Gets whether the STALL condition is set for an endpoint. |
| 116 | fn is_stalled(&self) -> bool; | 123 | fn is_stalled(&self) -> bool; |
| 117 | 124 | ||
| 125 | /// Waits for the endpoint to be enabled. | ||
| 126 | fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_>; | ||
| 127 | |||
| 118 | // TODO enable/disable? | 128 | // TODO enable/disable? |
| 119 | } | 129 | } |
| 120 | 130 | ||
| @@ -212,6 +222,7 @@ pub enum WriteError { | |||
| 212 | /// class shouldn't provide more data than the `max_packet_size` it specified when allocating | 222 | /// class shouldn't provide more data than the `max_packet_size` it specified when allocating |
| 213 | /// the endpoint. | 223 | /// the endpoint. |
| 214 | BufferOverflow, | 224 | BufferOverflow, |
| 225 | Disabled, | ||
| 215 | } | 226 | } |
| 216 | 227 | ||
| 217 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | 228 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] |
| @@ -223,4 +234,5 @@ pub enum ReadError { | |||
| 223 | /// should use a buffer that is large enough for the `max_packet_size` it specified when | 234 | /// should use a buffer that is large enough for the `max_packet_size` it specified when |
| 224 | /// allocating the endpoint. | 235 | /// allocating the endpoint. |
| 225 | BufferOverflow, | 236 | BufferOverflow, |
| 237 | Disabled, | ||
| 226 | } | 238 | } |
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index d2d3e5e0a..cf8d12539 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs | |||
| @@ -162,18 +162,21 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 162 | self.remote_wakeup_enabled = true; | 162 | self.remote_wakeup_enabled = true; |
| 163 | self.control.accept(stage) | 163 | self.control.accept(stage) |
| 164 | } | 164 | } |
| 165 | (Request::SET_ADDRESS, 1..=127) => { | 165 | (Request::SET_ADDRESS, addr @ 1..=127) => { |
| 166 | self.pending_address = req.value as u8; | 166 | self.pending_address = addr as u8; |
| 167 | self.bus.set_device_address(self.pending_address); | ||
| 167 | self.control.accept(stage) | 168 | self.control.accept(stage) |
| 168 | } | 169 | } |
| 169 | (Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => { | 170 | (Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => { |
| 170 | self.device_state = UsbDeviceState::Configured; | 171 | self.device_state = UsbDeviceState::Configured; |
| 172 | self.bus.set_configured(true); | ||
| 171 | self.control.accept(stage) | 173 | self.control.accept(stage) |
| 172 | } | 174 | } |
| 173 | (Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => match self.device_state { | 175 | (Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => match self.device_state { |
| 174 | UsbDeviceState::Default => self.control.accept(stage), | 176 | UsbDeviceState::Default => self.control.accept(stage), |
| 175 | _ => { | 177 | _ => { |
| 176 | self.device_state = UsbDeviceState::Addressed; | 178 | self.device_state = UsbDeviceState::Addressed; |
| 179 | self.bus.set_configured(false); | ||
| 177 | self.control.accept(stage) | 180 | self.control.accept(stage) |
| 178 | } | 181 | } |
| 179 | }, | 182 | }, |
diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index cd681c5ce..9437e835f 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs | |||
| @@ -4,12 +4,13 @@ | |||
| 4 | #![feature(type_alias_impl_trait)] | 4 | #![feature(type_alias_impl_trait)] |
| 5 | 5 | ||
| 6 | use core::mem; | 6 | use core::mem; |
| 7 | use defmt::*; | 7 | use defmt::{info, panic}; |
| 8 | use embassy::executor::Spawner; | 8 | use embassy::executor::Spawner; |
| 9 | use embassy_nrf::interrupt; | 9 | use embassy_nrf::interrupt; |
| 10 | use embassy_nrf::pac; | 10 | use embassy_nrf::pac; |
| 11 | use embassy_nrf::usb::Driver; | 11 | use embassy_nrf::usb::{Driver, Instance}; |
| 12 | use embassy_nrf::Peripherals; | 12 | use embassy_nrf::Peripherals; |
| 13 | use embassy_usb::driver::{ReadError, WriteError}; | ||
| 13 | use embassy_usb::{Config, UsbDeviceBuilder}; | 14 | use embassy_usb::{Config, UsbDeviceBuilder}; |
| 14 | use embassy_usb_serial::{CdcAcmClass, State}; | 15 | use embassy_usb_serial::{CdcAcmClass, State}; |
| 15 | use futures::future::join; | 16 | use futures::future::join; |
| @@ -66,12 +67,11 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 66 | 67 | ||
| 67 | // Do stuff with the class! | 68 | // Do stuff with the class! |
| 68 | let echo_fut = async { | 69 | let echo_fut = async { |
| 69 | let mut buf = [0; 64]; | ||
| 70 | loop { | 70 | loop { |
| 71 | let n = class.read_packet(&mut buf).await.unwrap(); | 71 | class.wait_connection().await; |
| 72 | let data = &buf[..n]; | 72 | info!("Connected"); |
| 73 | info!("data: {:x}", data); | 73 | let _ = echo(&mut class).await; |
| 74 | class.write_packet(data).await.unwrap(); | 74 | info!("Disconnected"); |
| 75 | } | 75 | } |
| 76 | }; | 76 | }; |
| 77 | 77 | ||
| @@ -79,3 +79,35 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 79 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | 79 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. |
| 80 | join(usb_fut, echo_fut).await; | 80 | join(usb_fut, echo_fut).await; |
| 81 | } | 81 | } |
| 82 | |||
| 83 | struct Disconnected {} | ||
| 84 | |||
| 85 | impl From<ReadError> for Disconnected { | ||
| 86 | fn from(val: ReadError) -> Self { | ||
| 87 | match val { | ||
| 88 | ReadError::BufferOverflow => panic!("Buffer overflow"), | ||
| 89 | ReadError::Disabled => Disconnected {}, | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | impl From<WriteError> for Disconnected { | ||
| 95 | fn from(val: WriteError) -> Self { | ||
| 96 | match val { | ||
| 97 | WriteError::BufferOverflow => panic!("Buffer overflow"), | ||
| 98 | WriteError::Disabled => Disconnected {}, | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | async fn echo<'d, T: Instance + 'd>( | ||
| 104 | class: &mut CdcAcmClass<'d, Driver<'d, T>>, | ||
| 105 | ) -> Result<(), Disconnected> { | ||
| 106 | let mut buf = [0; 64]; | ||
| 107 | loop { | ||
| 108 | let n = class.read_packet(&mut buf).await?; | ||
| 109 | let data = &buf[..n]; | ||
| 110 | info!("data: {:x}", data); | ||
| 111 | class.write_packet(data).await?; | ||
| 112 | } | ||
| 113 | } | ||
