diff options
| -rw-r--r-- | embassy-nrf/src/usb.rs | 424 | ||||
| -rw-r--r-- | embassy-usb/src/builder.rs | 14 | ||||
| -rw-r--r-- | embassy-usb/src/class.rs | 190 | ||||
| -rw-r--r-- | embassy-usb/src/control.rs | 17 | ||||
| -rw-r--r-- | embassy-usb/src/driver.rs | 42 | ||||
| -rw-r--r-- | embassy-usb/src/lib.rs | 169 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb/cdc_acm.rs | 127 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb/main.rs | 3 |
8 files changed, 699 insertions, 287 deletions
diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 5df5053ac..203f08fc0 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs | |||
| @@ -9,6 +9,7 @@ use embassy::time::{with_timeout, Duration}; | |||
| 9 | use embassy::util::Unborrow; | 9 | use embassy::util::Unborrow; |
| 10 | use embassy::waitqueue::AtomicWaker; | 10 | use embassy::waitqueue::AtomicWaker; |
| 11 | use embassy_hal_common::unborrow; | 11 | use embassy_hal_common::unborrow; |
| 12 | use embassy_usb::control::Request; | ||
| 12 | use embassy_usb::driver::{self, Event, ReadError, WriteError}; | 13 | use embassy_usb::driver::{self, Event, ReadError, WriteError}; |
| 13 | use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; | 14 | use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; |
| 14 | use futures::future::poll_fn; | 15 | use futures::future::poll_fn; |
| @@ -134,6 +135,7 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 134 | impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { | 135 | impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { |
| 135 | type EndpointOut = Endpoint<'d, T, Out>; | 136 | type EndpointOut = Endpoint<'d, T, Out>; |
| 136 | type EndpointIn = Endpoint<'d, T, In>; | 137 | type EndpointIn = Endpoint<'d, T, In>; |
| 138 | type ControlPipe = ControlPipe<'d, T>; | ||
| 137 | type Bus = Bus<'d, T>; | 139 | type Bus = Bus<'d, T>; |
| 138 | 140 | ||
| 139 | fn alloc_endpoint_in( | 141 | fn alloc_endpoint_in( |
| @@ -174,6 +176,19 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { | |||
| 174 | })) | 176 | })) |
| 175 | } | 177 | } |
| 176 | 178 | ||
| 179 | fn alloc_control_pipe( | ||
| 180 | &mut self, | ||
| 181 | max_packet_size: u16, | ||
| 182 | ) -> Result<Self::ControlPipe, driver::EndpointAllocError> { | ||
| 183 | self.alloc_endpoint_out(Some(0x00.into()), EndpointType::Control, max_packet_size, 0)?; | ||
| 184 | self.alloc_endpoint_in(Some(0x80.into()), EndpointType::Control, max_packet_size, 0)?; | ||
| 185 | Ok(ControlPipe { | ||
| 186 | _phantom: PhantomData, | ||
| 187 | max_packet_size, | ||
| 188 | request: None, | ||
| 189 | }) | ||
| 190 | } | ||
| 191 | |||
| 177 | fn enable(self) -> Self::Bus { | 192 | fn enable(self) -> Self::Bus { |
| 178 | let regs = T::regs(); | 193 | let regs = T::regs(); |
| 179 | 194 | ||
| @@ -344,99 +359,110 @@ impl<'d, T: Instance, Dir> driver::Endpoint for Endpoint<'d, T, Dir> { | |||
| 344 | } | 359 | } |
| 345 | } | 360 | } |
| 346 | 361 | ||
| 362 | unsafe fn read_dma<T: Instance>(i: usize, buf: &mut [u8]) -> Result<usize, ReadError> { | ||
| 363 | let regs = T::regs(); | ||
| 364 | |||
| 365 | // Check that the packet fits into the buffer | ||
| 366 | let size = regs.size.epout[0].read().bits() as usize; | ||
| 367 | if size > buf.len() { | ||
| 368 | return Err(ReadError::BufferOverflow); | ||
| 369 | } | ||
| 370 | |||
| 371 | if i == 0 { | ||
| 372 | regs.events_ep0datadone.reset(); | ||
| 373 | } | ||
| 374 | |||
| 375 | let epout = [ | ||
| 376 | ®s.epout0, | ||
| 377 | ®s.epout1, | ||
| 378 | ®s.epout2, | ||
| 379 | ®s.epout3, | ||
| 380 | ®s.epout4, | ||
| 381 | ®s.epout5, | ||
| 382 | ®s.epout6, | ||
| 383 | ®s.epout7, | ||
| 384 | ]; | ||
| 385 | epout[i].ptr.write(|w| w.bits(buf.as_ptr() as u32)); | ||
| 386 | // MAXCNT must match SIZE | ||
| 387 | epout[i].maxcnt.write(|w| w.bits(size as u32)); | ||
| 388 | |||
| 389 | dma_start(); | ||
| 390 | regs.events_endepout[i].reset(); | ||
| 391 | regs.tasks_startepout[i].write(|w| w.tasks_startepout().set_bit()); | ||
| 392 | while regs.events_endepout[i] | ||
| 393 | .read() | ||
| 394 | .events_endepout() | ||
| 395 | .bit_is_clear() | ||
| 396 | {} | ||
| 397 | regs.events_endepout[i].reset(); | ||
| 398 | dma_end(); | ||
| 399 | |||
| 400 | regs.size.epout[i].reset(); | ||
| 401 | |||
| 402 | Ok(size) | ||
| 403 | } | ||
| 404 | |||
| 405 | unsafe fn write_dma<T: Instance>(i: usize, buf: &[u8]) -> Result<(), WriteError> { | ||
| 406 | let regs = T::regs(); | ||
| 407 | if buf.len() > 64 { | ||
| 408 | return Err(WriteError::BufferOverflow); | ||
| 409 | } | ||
| 410 | |||
| 411 | // EasyDMA can't read FLASH, so we copy through RAM | ||
| 412 | let mut ram_buf: MaybeUninit<[u8; 64]> = MaybeUninit::uninit(); | ||
| 413 | let ptr = ram_buf.as_mut_ptr() as *mut u8; | ||
| 414 | core::ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len()); | ||
| 415 | |||
| 416 | let epin = [ | ||
| 417 | ®s.epin0, | ||
| 418 | ®s.epin1, | ||
| 419 | ®s.epin2, | ||
| 420 | ®s.epin3, | ||
| 421 | ®s.epin4, | ||
| 422 | ®s.epin5, | ||
| 423 | ®s.epin6, | ||
| 424 | ®s.epin7, | ||
| 425 | ]; | ||
| 426 | |||
| 427 | // Set the buffer length so the right number of bytes are transmitted. | ||
| 428 | // Safety: `buf.len()` has been checked to be <= the max buffer length. | ||
| 429 | epin[i].ptr.write(|w| w.bits(ptr as u32)); | ||
| 430 | epin[i].maxcnt.write(|w| w.maxcnt().bits(buf.len() as u8)); | ||
| 431 | |||
| 432 | regs.events_endepin[i].reset(); | ||
| 433 | |||
| 434 | dma_start(); | ||
| 435 | regs.tasks_startepin[i].write(|w| w.bits(1)); | ||
| 436 | while regs.events_endepin[i].read().bits() == 0 {} | ||
| 437 | dma_end(); | ||
| 438 | |||
| 439 | Ok(()) | ||
| 440 | } | ||
| 441 | |||
| 347 | impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { | 442 | impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { |
| 348 | type ReadFuture<'a> = impl Future<Output = Result<usize, ReadError>> + 'a where Self: 'a; | 443 | type ReadFuture<'a> = impl Future<Output = Result<usize, ReadError>> + 'a where Self: 'a; |
| 349 | 444 | ||
| 350 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | 445 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { |
| 351 | async move { | 446 | async move { |
| 352 | let regs = T::regs(); | ||
| 353 | let i = self.info.addr.index(); | 447 | let i = self.info.addr.index(); |
| 448 | assert!(i != 0); | ||
| 354 | 449 | ||
| 355 | if i == 0 { | 450 | // Wait until ready |
| 356 | if buf.len() == 0 { | 451 | poll_fn(|cx| { |
| 357 | regs.tasks_ep0status.write(|w| unsafe { w.bits(1) }); | 452 | EP_OUT_WAKERS[i].register(cx.waker()); |
| 358 | return Ok(0); | 453 | let r = READY_ENDPOINTS.load(Ordering::Acquire); |
| 359 | } | 454 | if r & (1 << (i + 16)) != 0 { |
| 360 | 455 | Poll::Ready(()) | |
| 361 | // Wait for SETUP packet | 456 | } else { |
| 362 | regs.events_ep0setup.reset(); | 457 | Poll::Pending |
| 363 | regs.intenset.write(|w| w.ep0setup().set()); | ||
| 364 | poll_fn(|cx| { | ||
| 365 | EP_OUT_WAKERS[0].register(cx.waker()); | ||
| 366 | let regs = T::regs(); | ||
| 367 | if regs.events_ep0setup.read().bits() != 0 { | ||
| 368 | Poll::Ready(()) | ||
| 369 | } else { | ||
| 370 | Poll::Pending | ||
| 371 | } | ||
| 372 | }) | ||
| 373 | .await; | ||
| 374 | |||
| 375 | if buf.len() < 8 { | ||
| 376 | return Err(ReadError::BufferOverflow); | ||
| 377 | } | ||
| 378 | |||
| 379 | buf[0] = regs.bmrequesttype.read().bits() as u8; | ||
| 380 | buf[1] = regs.brequest.read().brequest().bits(); | ||
| 381 | buf[2] = regs.wvaluel.read().wvaluel().bits(); | ||
| 382 | buf[3] = regs.wvalueh.read().wvalueh().bits(); | ||
| 383 | buf[4] = regs.windexl.read().windexl().bits(); | ||
| 384 | buf[5] = regs.windexh.read().windexh().bits(); | ||
| 385 | buf[6] = regs.wlengthl.read().wlengthl().bits(); | ||
| 386 | buf[7] = regs.wlengthh.read().wlengthh().bits(); | ||
| 387 | |||
| 388 | Ok(8) | ||
| 389 | } else { | ||
| 390 | // Wait until ready | ||
| 391 | poll_fn(|cx| { | ||
| 392 | EP_OUT_WAKERS[i].register(cx.waker()); | ||
| 393 | let r = READY_ENDPOINTS.load(Ordering::Acquire); | ||
| 394 | if r & (1 << (i + 16)) != 0 { | ||
| 395 | Poll::Ready(()) | ||
| 396 | } else { | ||
| 397 | Poll::Pending | ||
| 398 | } | ||
| 399 | }) | ||
| 400 | .await; | ||
| 401 | |||
| 402 | // Mark as not ready | ||
| 403 | READY_ENDPOINTS.fetch_and(!(1 << (i + 16)), Ordering::AcqRel); | ||
| 404 | |||
| 405 | // Check that the packet fits into the buffer | ||
| 406 | let size = regs.size.epout[i].read().bits(); | ||
| 407 | if size as usize > buf.len() { | ||
| 408 | return Err(ReadError::BufferOverflow); | ||
| 409 | } | 458 | } |
| 459 | }) | ||
| 460 | .await; | ||
| 410 | 461 | ||
| 411 | let epout = [ | 462 | // Mark as not ready |
| 412 | ®s.epout0, | 463 | READY_ENDPOINTS.fetch_and(!(1 << (i + 16)), Ordering::AcqRel); |
| 413 | ®s.epout1, | ||
| 414 | ®s.epout2, | ||
| 415 | ®s.epout3, | ||
| 416 | ®s.epout4, | ||
| 417 | ®s.epout5, | ||
| 418 | ®s.epout6, | ||
| 419 | ®s.epout7, | ||
| 420 | ]; | ||
| 421 | epout[i] | ||
| 422 | .ptr | ||
| 423 | .write(|w| unsafe { w.bits(buf.as_ptr() as u32) }); | ||
| 424 | // MAXCNT must match SIZE | ||
| 425 | epout[i].maxcnt.write(|w| unsafe { w.bits(size) }); | ||
| 426 | |||
| 427 | dma_start(); | ||
| 428 | regs.events_endepout[i].reset(); | ||
| 429 | regs.tasks_startepout[i].write(|w| w.tasks_startepout().set_bit()); | ||
| 430 | while regs.events_endepout[i] | ||
| 431 | .read() | ||
| 432 | .events_endepout() | ||
| 433 | .bit_is_clear() | ||
| 434 | {} | ||
| 435 | regs.events_endepout[i].reset(); | ||
| 436 | dma_end(); | ||
| 437 | 464 | ||
| 438 | Ok(size as usize) | 465 | unsafe { read_dma::<T>(i, buf) } |
| 439 | } | ||
| 440 | } | 466 | } |
| 441 | } | 467 | } |
| 442 | } | 468 | } |
| @@ -446,87 +472,181 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { | |||
| 446 | 472 | ||
| 447 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | 473 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { |
| 448 | async move { | 474 | async move { |
| 449 | let regs = T::regs(); | ||
| 450 | let i = self.info.addr.index(); | 475 | let i = self.info.addr.index(); |
| 476 | assert!(i != 0); | ||
| 451 | 477 | ||
| 452 | // Wait until ready. | 478 | // Wait until ready. |
| 453 | if i != 0 { | 479 | poll_fn(|cx| { |
| 454 | poll_fn(|cx| { | 480 | EP_IN_WAKERS[i].register(cx.waker()); |
| 455 | EP_IN_WAKERS[i].register(cx.waker()); | 481 | let r = READY_ENDPOINTS.load(Ordering::Acquire); |
| 456 | let r = READY_ENDPOINTS.load(Ordering::Acquire); | 482 | if r & (1 << i) != 0 { |
| 457 | if r & (1 << i) != 0 { | 483 | Poll::Ready(()) |
| 458 | Poll::Ready(()) | 484 | } else { |
| 459 | } else { | 485 | Poll::Pending |
| 460 | Poll::Pending | 486 | } |
| 461 | } | 487 | }) |
| 462 | }) | 488 | .await; |
| 463 | .await; | ||
| 464 | |||
| 465 | // Mark as not ready | ||
| 466 | READY_ENDPOINTS.fetch_and(!(1 << i), Ordering::AcqRel); | ||
| 467 | } | ||
| 468 | 489 | ||
| 469 | if i == 0 { | 490 | // Mark as not ready |
| 470 | regs.events_ep0datadone.reset(); | 491 | READY_ENDPOINTS.fetch_and(!(1 << i), Ordering::AcqRel); |
| 471 | } | 492 | |
| 493 | unsafe { write_dma::<T>(i, buf) } | ||
| 494 | } | ||
| 495 | } | ||
| 496 | } | ||
| 472 | 497 | ||
| 473 | assert!(buf.len() <= 64); | 498 | pub struct ControlPipe<'d, T: Instance> { |
| 474 | 499 | _phantom: PhantomData<&'d mut T>, | |
| 475 | // EasyDMA can't read FLASH, so we copy through RAM | 500 | max_packet_size: u16, |
| 476 | let mut ram_buf: MaybeUninit<[u8; 64]> = MaybeUninit::uninit(); | 501 | request: Option<Request>, |
| 477 | let ptr = ram_buf.as_mut_ptr() as *mut u8; | 502 | } |
| 478 | unsafe { core::ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len()) }; | 503 | |
| 479 | 504 | impl<'d, T: Instance> ControlPipe<'d, T> { | |
| 480 | let epin = [ | 505 | async fn write(&mut self, buf: &[u8], last_chunk: bool) { |
| 481 | ®s.epin0, | 506 | let regs = T::regs(); |
| 482 | ®s.epin1, | 507 | regs.events_ep0datadone.reset(); |
| 483 | ®s.epin2, | 508 | unsafe { |
| 484 | ®s.epin3, | 509 | write_dma::<T>(0, buf).unwrap(); |
| 485 | ®s.epin4, | 510 | } |
| 486 | ®s.epin5, | 511 | |
| 487 | ®s.epin6, | 512 | regs.shorts |
| 488 | ®s.epin7, | 513 | .modify(|_, w| w.ep0datadone_ep0status().bit(last_chunk)); |
| 489 | ]; | 514 | |
| 490 | 515 | regs.intenset.write(|w| w.ep0datadone().set()); | |
| 491 | // Set the buffer length so the right number of bytes are transmitted. | 516 | let res = with_timeout( |
| 492 | // Safety: `buf.len()` has been checked to be <= the max buffer length. | 517 | Duration::from_millis(10), |
| 493 | unsafe { | 518 | poll_fn(|cx| { |
| 494 | epin[i].ptr.write(|w| w.bits(ptr as u32)); | 519 | EP_IN_WAKERS[0].register(cx.waker()); |
| 495 | epin[i].maxcnt.write(|w| w.maxcnt().bits(buf.len() as u8)); | 520 | let regs = T::regs(); |
| 521 | if regs.events_ep0datadone.read().bits() != 0 { | ||
| 522 | Poll::Ready(()) | ||
| 523 | } else { | ||
| 524 | Poll::Pending | ||
| 525 | } | ||
| 526 | }), | ||
| 527 | ) | ||
| 528 | .await; | ||
| 529 | |||
| 530 | if res.is_err() { | ||
| 531 | error!("ControlPipe::write timed out."); | ||
| 532 | } | ||
| 533 | } | ||
| 534 | } | ||
| 535 | |||
| 536 | impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { | ||
| 537 | type SetupFuture<'a> = impl Future<Output = Request> + 'a where Self: 'a; | ||
| 538 | type DataOutFuture<'a> = impl Future<Output = Result<usize, ReadError>> + 'a where Self: 'a; | ||
| 539 | type AcceptInFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a; | ||
| 540 | |||
| 541 | fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a> { | ||
| 542 | async move { | ||
| 543 | assert!(self.request.is_none()); | ||
| 544 | |||
| 545 | let regs = T::regs(); | ||
| 546 | |||
| 547 | // Wait for SETUP packet | ||
| 548 | regs.intenset.write(|w| w.ep0setup().set()); | ||
| 549 | poll_fn(|cx| { | ||
| 550 | EP_OUT_WAKERS[0].register(cx.waker()); | ||
| 551 | let regs = T::regs(); | ||
| 552 | if regs.events_ep0setup.read().bits() != 0 { | ||
| 553 | Poll::Ready(()) | ||
| 554 | } else { | ||
| 555 | Poll::Pending | ||
| 556 | } | ||
| 557 | }) | ||
| 558 | .await; | ||
| 559 | |||
| 560 | // Reset shorts | ||
| 561 | regs.shorts | ||
| 562 | .modify(|_, w| w.ep0datadone_ep0status().clear_bit()); | ||
| 563 | regs.events_ep0setup.reset(); | ||
| 564 | |||
| 565 | let mut buf = [0; 8]; | ||
| 566 | buf[0] = regs.bmrequesttype.read().bits() as u8; | ||
| 567 | buf[1] = regs.brequest.read().brequest().bits(); | ||
| 568 | buf[2] = regs.wvaluel.read().wvaluel().bits(); | ||
| 569 | buf[3] = regs.wvalueh.read().wvalueh().bits(); | ||
| 570 | buf[4] = regs.windexl.read().windexl().bits(); | ||
| 571 | buf[5] = regs.windexh.read().windexh().bits(); | ||
| 572 | buf[6] = regs.wlengthl.read().wlengthl().bits(); | ||
| 573 | buf[7] = regs.wlengthh.read().wlengthh().bits(); | ||
| 574 | |||
| 575 | let req = Request::parse(&buf); | ||
| 576 | |||
| 577 | if req.direction == UsbDirection::Out { | ||
| 578 | regs.tasks_ep0rcvout | ||
| 579 | .write(|w| w.tasks_ep0rcvout().set_bit()); | ||
| 496 | } | 580 | } |
| 497 | 581 | ||
| 498 | regs.events_endepin[i].reset(); | 582 | self.request = Some(req); |
| 499 | 583 | req | |
| 500 | dma_start(); | 584 | } |
| 501 | regs.tasks_startepin[i].write(|w| unsafe { w.bits(1) }); | 585 | } |
| 502 | while regs.events_endepin[i].read().bits() == 0 {} | 586 | |
| 503 | dma_end(); | 587 | fn data_out<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::DataOutFuture<'a> { |
| 504 | 588 | async move { | |
| 505 | if i == 0 { | 589 | let req = self.request.unwrap(); |
| 506 | regs.intenset.write(|w| w.ep0datadone().set()); | 590 | assert_eq!(req.direction, UsbDirection::Out); |
| 507 | let res = with_timeout( | 591 | assert!(req.length > 0); |
| 508 | Duration::from_millis(10), | 592 | assert!(buf.len() >= usize::from(req.length)); |
| 509 | poll_fn(|cx| { | 593 | |
| 510 | EP_IN_WAKERS[0].register(cx.waker()); | 594 | let regs = T::regs(); |
| 511 | let regs = T::regs(); | 595 | |
| 512 | if regs.events_ep0datadone.read().bits() != 0 { | 596 | // Wait until ready |
| 513 | Poll::Ready(()) | 597 | regs.intenset.write(|w| w.ep0datadone().set()); |
| 514 | } else { | 598 | poll_fn(|cx| { |
| 515 | Poll::Pending | 599 | EP_OUT_WAKERS[0].register(cx.waker()); |
| 516 | } | 600 | let regs = T::regs(); |
| 517 | }), | 601 | if regs |
| 518 | ) | 602 | .events_ep0datadone |
| 519 | .await; | 603 | .read() |
| 520 | 604 | .events_ep0datadone() | |
| 521 | if res.is_err() { | 605 | .bit_is_set() |
| 522 | // todo wrong error | 606 | { |
| 523 | return Err(driver::WriteError::BufferOverflow); | 607 | Poll::Ready(()) |
| 608 | } else { | ||
| 609 | Poll::Pending | ||
| 524 | } | 610 | } |
| 611 | }) | ||
| 612 | .await; | ||
| 613 | |||
| 614 | unsafe { read_dma::<T>(0, buf) } | ||
| 615 | } | ||
| 616 | } | ||
| 617 | |||
| 618 | fn accept(&mut self) { | ||
| 619 | let regs = T::regs(); | ||
| 620 | regs.tasks_ep0status | ||
| 621 | .write(|w| w.tasks_ep0status().bit(true)); | ||
| 622 | self.request = None; | ||
| 623 | } | ||
| 624 | |||
| 625 | fn accept_in<'a>(&'a mut self, buf: &'a [u8]) -> Self::AcceptInFuture<'a> { | ||
| 626 | async move { | ||
| 627 | info!("control accept {=[u8]:x}", buf); | ||
| 628 | let req = self.request.unwrap(); | ||
| 629 | assert_eq!(req.direction, UsbDirection::In); | ||
| 630 | |||
| 631 | let req_len = usize::from(req.length); | ||
| 632 | let len = buf.len().min(req_len); | ||
| 633 | let need_zlp = len != req_len && (len % usize::from(self.max_packet_size)) == 0; | ||
| 634 | let mut chunks = buf[0..len] | ||
| 635 | .chunks(usize::from(self.max_packet_size)) | ||
| 636 | .chain(need_zlp.then(|| -> &[u8] { &[] })); | ||
| 637 | while let Some(chunk) = chunks.next() { | ||
| 638 | self.write(chunk, chunks.size_hint().0 == 0).await; | ||
| 525 | } | 639 | } |
| 526 | 640 | ||
| 527 | Ok(()) | 641 | self.request = None; |
| 528 | } | 642 | } |
| 529 | } | 643 | } |
| 644 | |||
| 645 | fn reject(&mut self) { | ||
| 646 | let regs = T::regs(); | ||
| 647 | regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true)); | ||
| 648 | self.request = None; | ||
| 649 | } | ||
| 530 | } | 650 | } |
| 531 | 651 | ||
| 532 | fn dma_start() { | 652 | fn dma_start() { |
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index e92cc8ef2..f0f94b932 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | use super::class::UsbClass; | ||
| 1 | use super::descriptor::{BosWriter, DescriptorWriter}; | 2 | use super::descriptor::{BosWriter, DescriptorWriter}; |
| 2 | use super::driver::{Driver, EndpointAllocError}; | 3 | use super::driver::{Driver, EndpointAllocError}; |
| 3 | use super::types::*; | 4 | use super::types::*; |
| @@ -174,7 +175,10 @@ impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> { | |||
| 174 | } | 175 | } |
| 175 | 176 | ||
| 176 | /// Creates the [`UsbDevice`] instance with the configuration in this builder. | 177 | /// Creates the [`UsbDevice`] instance with the configuration in this builder. |
| 177 | pub fn build(mut self) -> UsbDevice<'d, D> { | 178 | /// |
| 179 | /// If a device has mutliple [`UsbClass`]es, they can be provided as a tuple list: | ||
| 180 | /// `(class1, (class2, (class3, ()))`. | ||
| 181 | pub fn build<C: UsbClass<'d, D>>(mut self, classes: C) -> UsbDevice<'d, D, C> { | ||
| 178 | self.config_descriptor.end_configuration(); | 182 | self.config_descriptor.end_configuration(); |
| 179 | self.bos_descriptor.end_bos(); | 183 | self.bos_descriptor.end_bos(); |
| 180 | 184 | ||
| @@ -184,6 +188,7 @@ impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> { | |||
| 184 | self.device_descriptor.into_buf(), | 188 | self.device_descriptor.into_buf(), |
| 185 | self.config_descriptor.into_buf(), | 189 | self.config_descriptor.into_buf(), |
| 186 | self.bos_descriptor.writer.into_buf(), | 190 | self.bos_descriptor.writer.into_buf(), |
| 191 | classes, | ||
| 187 | ) | 192 | ) |
| 188 | } | 193 | } |
| 189 | 194 | ||
| @@ -268,9 +273,10 @@ impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> { | |||
| 268 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | 273 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not |
| 269 | /// feasibly recoverable. | 274 | /// feasibly recoverable. |
| 270 | #[inline] | 275 | #[inline] |
| 271 | pub fn alloc_control_endpoint_out(&mut self, max_packet_size: u16) -> D::EndpointOut { | 276 | pub fn alloc_control_pipe(&mut self, max_packet_size: u16) -> D::ControlPipe { |
| 272 | self.alloc_endpoint_out(None, EndpointType::Control, max_packet_size, 0) | 277 | self.bus |
| 273 | .expect("alloc_ep failed") | 278 | .alloc_control_pipe(max_packet_size) |
| 279 | .expect("alloc_control_pipe failed") | ||
| 274 | } | 280 | } |
| 275 | 281 | ||
| 276 | /// Allocates a bulk in endpoint. | 282 | /// Allocates a bulk in endpoint. |
diff --git a/embassy-usb/src/class.rs b/embassy-usb/src/class.rs new file mode 100644 index 000000000..97bf7aba1 --- /dev/null +++ b/embassy-usb/src/class.rs | |||
| @@ -0,0 +1,190 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | |||
| 3 | use crate::control::Request; | ||
| 4 | use crate::driver::{ControlPipe, Driver}; | ||
| 5 | |||
| 6 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | pub enum RequestStatus { | ||
| 9 | Unhandled, | ||
| 10 | Accepted, | ||
| 11 | Rejected, | ||
| 12 | } | ||
| 13 | |||
| 14 | impl Default for RequestStatus { | ||
| 15 | fn default() -> Self { | ||
| 16 | RequestStatus::Unhandled | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | /// A trait for implementing USB classes. | ||
| 21 | /// | ||
| 22 | /// All methods are optional callbacks that will be called by | ||
| 23 | /// [`UsbDevice::run()`](crate::UsbDevice::run) | ||
| 24 | pub trait UsbClass<'d, D: Driver<'d>> { | ||
| 25 | type ControlOutFuture<'a>: Future<Output = RequestStatus> + 'a | ||
| 26 | where | ||
| 27 | Self: 'a, | ||
| 28 | 'd: 'a, | ||
| 29 | D: 'a; | ||
| 30 | |||
| 31 | type ControlInFuture<'a>: Future<Output = ControlInRequestStatus> + 'a | ||
| 32 | where | ||
| 33 | Self: 'a, | ||
| 34 | 'd: 'a, | ||
| 35 | D: 'a; | ||
| 36 | |||
| 37 | /// Called after a USB reset after the bus reset sequence is complete. | ||
| 38 | fn reset(&mut self) {} | ||
| 39 | |||
| 40 | /// Called when a control request is received with direction HostToDevice. | ||
| 41 | /// | ||
| 42 | /// All requests are passed to classes in turn, which can choose to accept, ignore or report an | ||
| 43 | /// error. Classes can even choose to override standard requests, but doing that is rarely | ||
| 44 | /// necessary. | ||
| 45 | /// | ||
| 46 | /// When implementing your own class, you should ignore any requests that are not meant for your | ||
| 47 | /// class so that any other classes in the composite device can process them. | ||
| 48 | /// | ||
| 49 | /// # Arguments | ||
| 50 | /// | ||
| 51 | /// * `req` - The request from the SETUP packet. | ||
| 52 | /// * `data` - The data from the request. | ||
| 53 | fn control_out<'a>(&'a mut self, req: Request, data: &'a [u8]) -> Self::ControlOutFuture<'a> | ||
| 54 | where | ||
| 55 | 'd: 'a, | ||
| 56 | D: 'a; | ||
| 57 | |||
| 58 | /// Called when a control request is received with direction DeviceToHost. | ||
| 59 | /// | ||
| 60 | /// All requests are passed to classes in turn, which can choose to accept, ignore or report an | ||
| 61 | /// error. Classes can even choose to override standard requests, but doing that is rarely | ||
| 62 | /// necessary. | ||
| 63 | /// | ||
| 64 | /// See [`ControlIn`] for how to respond to the transfer. | ||
| 65 | /// | ||
| 66 | /// When implementing your own class, you should ignore any requests that are not meant for your | ||
| 67 | /// class so that any other classes in the composite device can process them. | ||
| 68 | /// | ||
| 69 | /// # Arguments | ||
| 70 | /// | ||
| 71 | /// * `req` - The request from the SETUP packet. | ||
| 72 | /// * `control` - The control pipe. | ||
| 73 | fn control_in<'a>( | ||
| 74 | &'a mut self, | ||
| 75 | req: Request, | ||
| 76 | control: ControlIn<'a, 'd, D>, | ||
| 77 | ) -> Self::ControlInFuture<'a> | ||
| 78 | where | ||
| 79 | 'd: 'a; | ||
| 80 | } | ||
| 81 | |||
| 82 | impl<'d, D: Driver<'d>> UsbClass<'d, D> for () { | ||
| 83 | type ControlOutFuture<'a> = impl Future<Output = RequestStatus> + 'a where Self: 'a, 'd: 'a, D: 'a; | ||
| 84 | type ControlInFuture<'a> = impl Future<Output = ControlInRequestStatus> + 'a where Self: 'a, 'd: 'a, D: 'a; | ||
| 85 | |||
| 86 | fn control_out<'a>(&'a mut self, _req: Request, _data: &'a [u8]) -> Self::ControlOutFuture<'a> | ||
| 87 | where | ||
| 88 | 'd: 'a, | ||
| 89 | D: 'a, | ||
| 90 | { | ||
| 91 | async move { RequestStatus::default() } | ||
| 92 | } | ||
| 93 | |||
| 94 | fn control_in<'a>( | ||
| 95 | &'a mut self, | ||
| 96 | _req: Request, | ||
| 97 | control: ControlIn<'a, 'd, D>, | ||
| 98 | ) -> Self::ControlInFuture<'a> | ||
| 99 | where | ||
| 100 | 'd: 'a, | ||
| 101 | D: 'a, | ||
| 102 | { | ||
| 103 | async move { control.ignore() } | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | impl<'d, D: Driver<'d>, Head, Tail> UsbClass<'d, D> for (Head, Tail) | ||
| 108 | where | ||
| 109 | Head: UsbClass<'d, D>, | ||
| 110 | Tail: UsbClass<'d, D>, | ||
| 111 | { | ||
| 112 | type ControlOutFuture<'a> = impl Future<Output = RequestStatus> + 'a where Self: 'a, 'd: 'a, D: 'a; | ||
| 113 | type ControlInFuture<'a> = impl Future<Output = ControlInRequestStatus> + 'a where Self: 'a, 'd: 'a, D: 'a; | ||
| 114 | |||
| 115 | fn control_out<'a>(&'a mut self, req: Request, data: &'a [u8]) -> Self::ControlOutFuture<'a> | ||
| 116 | where | ||
| 117 | 'd: 'a, | ||
| 118 | D: 'a, | ||
| 119 | { | ||
| 120 | async move { | ||
| 121 | match self.0.control_out(req, data).await { | ||
| 122 | RequestStatus::Unhandled => self.1.control_out(req, data).await, | ||
| 123 | status => status, | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | fn control_in<'a>( | ||
| 129 | &'a mut self, | ||
| 130 | req: Request, | ||
| 131 | control: ControlIn<'a, 'd, D>, | ||
| 132 | ) -> Self::ControlInFuture<'a> | ||
| 133 | where | ||
| 134 | 'd: 'a, | ||
| 135 | { | ||
| 136 | async move { | ||
| 137 | match self | ||
| 138 | .0 | ||
| 139 | .control_in(req, ControlIn::new(control.control)) | ||
| 140 | .await | ||
| 141 | { | ||
| 142 | ControlInRequestStatus(RequestStatus::Unhandled) => { | ||
| 143 | self.1.control_in(req, control).await | ||
| 144 | } | ||
| 145 | status => status, | ||
| 146 | } | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | /// Handle for a control IN transfer. When implementing a class, use the methods of this object to | ||
| 152 | /// response to the transfer with either data or an error (STALL condition). To ignore the request | ||
| 153 | /// and pass it on to the next class, call [`Self::ignore()`]. | ||
| 154 | pub struct ControlIn<'a, 'd: 'a, D: Driver<'d>> { | ||
| 155 | control: &'a mut D::ControlPipe, | ||
| 156 | } | ||
| 157 | |||
| 158 | #[derive(Eq, PartialEq, Debug)] | ||
| 159 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 160 | pub struct ControlInRequestStatus(pub(crate) RequestStatus); | ||
| 161 | |||
| 162 | impl ControlInRequestStatus { | ||
| 163 | pub fn status(self) -> RequestStatus { | ||
| 164 | self.0 | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | impl<'a, 'd: 'a, D: Driver<'d>> ControlIn<'a, 'd, D> { | ||
| 169 | pub(crate) fn new(control: &'a mut D::ControlPipe) -> Self { | ||
| 170 | ControlIn { control } | ||
| 171 | } | ||
| 172 | |||
| 173 | /// Ignores the request and leaves it unhandled. | ||
| 174 | pub fn ignore(self) -> ControlInRequestStatus { | ||
| 175 | ControlInRequestStatus(RequestStatus::Unhandled) | ||
| 176 | } | ||
| 177 | |||
| 178 | /// Accepts the transfer with the supplied buffer. | ||
| 179 | pub async fn accept(self, data: &[u8]) -> ControlInRequestStatus { | ||
| 180 | self.control.accept_in(data).await; | ||
| 181 | |||
| 182 | ControlInRequestStatus(RequestStatus::Accepted) | ||
| 183 | } | ||
| 184 | |||
| 185 | /// Rejects the transfer by stalling the pipe. | ||
| 186 | pub fn reject(self) -> ControlInRequestStatus { | ||
| 187 | self.control.reject(); | ||
| 188 | ControlInRequestStatus(RequestStatus::Rejected) | ||
| 189 | } | ||
| 190 | } | ||
diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs index f1148ac76..77bc10aa4 100644 --- a/embassy-usb/src/control.rs +++ b/embassy-usb/src/control.rs | |||
| @@ -2,12 +2,6 @@ use core::mem; | |||
| 2 | 2 | ||
| 3 | use super::types::*; | 3 | use super::types::*; |
| 4 | 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. | 5 | /// Control request type. |
| 12 | #[repr(u8)] | 6 | #[repr(u8)] |
| 13 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | 7 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] |
| @@ -104,15 +98,12 @@ impl Request { | |||
| 104 | /// Standard USB feature Device Remote Wakeup for Set/Clear Feature | 98 | /// Standard USB feature Device Remote Wakeup for Set/Clear Feature |
| 105 | pub const FEATURE_DEVICE_REMOTE_WAKEUP: u16 = 1; | 99 | pub const FEATURE_DEVICE_REMOTE_WAKEUP: u16 = 1; |
| 106 | 100 | ||
| 107 | pub(crate) fn parse(buf: &[u8]) -> Result<Request, ParseError> { | 101 | /// Parses a USB control request from a byte array. |
| 108 | if buf.len() != 8 { | 102 | pub fn parse(buf: &[u8; 8]) -> Request { |
| 109 | return Err(ParseError::InvalidLength); | ||
| 110 | } | ||
| 111 | |||
| 112 | let rt = buf[0]; | 103 | let rt = buf[0]; |
| 113 | let recipient = rt & 0b11111; | 104 | let recipient = rt & 0b11111; |
| 114 | 105 | ||
| 115 | Ok(Request { | 106 | Request { |
| 116 | direction: rt.into(), | 107 | direction: rt.into(), |
| 117 | request_type: unsafe { mem::transmute((rt >> 5) & 0b11) }, | 108 | request_type: unsafe { mem::transmute((rt >> 5) & 0b11) }, |
| 118 | recipient: if recipient <= 3 { | 109 | recipient: if recipient <= 3 { |
| @@ -124,7 +115,7 @@ impl Request { | |||
| 124 | value: (buf[2] as u16) | ((buf[3] as u16) << 8), | 115 | value: (buf[2] as u16) | ((buf[3] as u16) << 8), |
| 125 | index: (buf[4] as u16) | ((buf[5] as u16) << 8), | 116 | index: (buf[4] as u16) | ((buf[5] as u16) << 8), |
| 126 | length: (buf[6] as u16) | ((buf[7] as u16) << 8), | 117 | length: (buf[6] as u16) | ((buf[7] as u16) << 8), |
| 127 | }) | 118 | } |
| 128 | } | 119 | } |
| 129 | 120 | ||
| 130 | /// Gets the descriptor type and index from the value field of a GET_DESCRIPTOR request. | 121 | /// Gets the descriptor type and index from the value field of a GET_DESCRIPTOR request. |
diff --git a/embassy-usb/src/driver.rs b/embassy-usb/src/driver.rs index a7b16efa5..1c6ba1f52 100644 --- a/embassy-usb/src/driver.rs +++ b/embassy-usb/src/driver.rs | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | use core::future::Future; | 1 | use core::future::Future; |
| 2 | 2 | ||
| 3 | use crate::control::Request; | ||
| 4 | |||
| 3 | use super::types::*; | 5 | use super::types::*; |
| 4 | 6 | ||
| 5 | /// Driver for a specific USB peripheral. Implement this to add support for a new hardware | 7 | /// Driver for a specific USB peripheral. Implement this to add support for a new hardware |
| @@ -7,6 +9,7 @@ use super::types::*; | |||
| 7 | pub trait Driver<'a> { | 9 | pub trait Driver<'a> { |
| 8 | type EndpointOut: EndpointOut + 'a; | 10 | type EndpointOut: EndpointOut + 'a; |
| 9 | type EndpointIn: EndpointIn + 'a; | 11 | type EndpointIn: EndpointIn + 'a; |
| 12 | type ControlPipe: ControlPipe + 'a; | ||
| 10 | type Bus: Bus + 'a; | 13 | type Bus: Bus + 'a; |
| 11 | 14 | ||
| 12 | /// Allocates an endpoint and specified endpoint parameters. This method is called by the device | 15 | /// Allocates an endpoint and specified endpoint parameters. This method is called by the device |
| @@ -36,6 +39,11 @@ pub trait Driver<'a> { | |||
| 36 | interval: u8, | 39 | interval: u8, |
| 37 | ) -> Result<Self::EndpointIn, EndpointAllocError>; | 40 | ) -> Result<Self::EndpointIn, EndpointAllocError>; |
| 38 | 41 | ||
| 42 | fn alloc_control_pipe( | ||
| 43 | &mut self, | ||
| 44 | max_packet_size: u16, | ||
| 45 | ) -> Result<Self::ControlPipe, EndpointAllocError>; | ||
| 46 | |||
| 39 | /// Enables and initializes the USB peripheral. Soon after enabling the device will be reset, so | 47 | /// Enables and initializes the USB peripheral. Soon after enabling the device will be reset, so |
| 40 | /// there is no need to perform a USB reset in this method. | 48 | /// there is no need to perform a USB reset in this method. |
| 41 | fn enable(self) -> Self::Bus; | 49 | fn enable(self) -> Self::Bus; |
| @@ -122,6 +130,40 @@ pub trait EndpointOut: Endpoint { | |||
| 122 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a>; | 130 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a>; |
| 123 | } | 131 | } |
| 124 | 132 | ||
| 133 | pub trait ControlPipe { | ||
| 134 | type SetupFuture<'a>: Future<Output = Request> + 'a | ||
| 135 | where | ||
| 136 | Self: 'a; | ||
| 137 | type DataOutFuture<'a>: Future<Output = Result<usize, ReadError>> + 'a | ||
| 138 | where | ||
| 139 | Self: 'a; | ||
| 140 | type AcceptInFuture<'a>: Future<Output = ()> + 'a | ||
| 141 | where | ||
| 142 | Self: 'a; | ||
| 143 | |||
| 144 | /// Reads a single setup packet from the endpoint. | ||
| 145 | fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a>; | ||
| 146 | |||
| 147 | /// Reads the data packet of a control write sequence. | ||
| 148 | /// | ||
| 149 | /// Must be called after `setup()` for requests with `direction` of `Out` | ||
| 150 | /// and `length` greater than zero. | ||
| 151 | /// | ||
| 152 | /// `buf.len()` must be greater than or equal to the request's `length`. | ||
| 153 | fn data_out<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::DataOutFuture<'a>; | ||
| 154 | |||
| 155 | /// Accepts a control request. | ||
| 156 | fn accept(&mut self); | ||
| 157 | |||
| 158 | /// Accepts a control read request with `data`. | ||
| 159 | /// | ||
| 160 | /// `data.len()` must be less than or equal to the request's `length`. | ||
| 161 | fn accept_in<'a>(&'a mut self, data: &'a [u8]) -> Self::AcceptInFuture<'a>; | ||
| 162 | |||
| 163 | /// Rejects a control request. | ||
| 164 | fn reject(&mut self); | ||
| 165 | } | ||
| 166 | |||
| 125 | pub trait EndpointIn: Endpoint { | 167 | pub trait EndpointIn: Endpoint { |
| 126 | type WriteFuture<'a>: Future<Output = Result<(), WriteError>> + 'a | 168 | type WriteFuture<'a>: Future<Output = Result<(), WriteError>> + 'a |
| 127 | where | 169 | where |
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 95f78804d..4082868fb 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs | |||
| @@ -1,16 +1,19 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![feature(generic_associated_types)] | 2 | #![feature(generic_associated_types)] |
| 3 | #![feature(type_alias_impl_trait)] | ||
| 3 | 4 | ||
| 4 | // This mod MUST go first, so that the others see its macros. | 5 | // This mod MUST go first, so that the others see its macros. |
| 5 | pub(crate) mod fmt; | 6 | pub(crate) mod fmt; |
| 6 | 7 | ||
| 7 | mod builder; | 8 | mod builder; |
| 8 | mod control; | 9 | pub mod class; |
| 10 | pub mod control; | ||
| 9 | pub mod descriptor; | 11 | pub mod descriptor; |
| 10 | pub mod driver; | 12 | pub mod driver; |
| 11 | pub mod types; | 13 | pub mod types; |
| 12 | mod util; | 14 | mod util; |
| 13 | 15 | ||
| 16 | use self::class::{RequestStatus, UsbClass}; | ||
| 14 | use self::control::*; | 17 | use self::control::*; |
| 15 | use self::descriptor::*; | 18 | use self::descriptor::*; |
| 16 | use self::driver::*; | 19 | use self::driver::*; |
| @@ -48,10 +51,9 @@ pub const CONFIGURATION_VALUE: u8 = 1; | |||
| 48 | /// The default value for bAlternateSetting for all interfaces. | 51 | /// The default value for bAlternateSetting for all interfaces. |
| 49 | pub const DEFAULT_ALTERNATE_SETTING: u8 = 0; | 52 | pub const DEFAULT_ALTERNATE_SETTING: u8 = 0; |
| 50 | 53 | ||
| 51 | pub struct UsbDevice<'d, D: Driver<'d>> { | 54 | pub struct UsbDevice<'d, D: Driver<'d>, C: UsbClass<'d, D>> { |
| 52 | bus: D::Bus, | 55 | bus: D::Bus, |
| 53 | control_in: D::EndpointIn, | 56 | control: D::ControlPipe, |
| 54 | control_out: D::EndpointOut, | ||
| 55 | 57 | ||
| 56 | config: Config<'d>, | 58 | config: Config<'d>, |
| 57 | device_descriptor: &'d [u8], | 59 | device_descriptor: &'d [u8], |
| @@ -62,32 +64,21 @@ pub struct UsbDevice<'d, D: Driver<'d>> { | |||
| 62 | remote_wakeup_enabled: bool, | 64 | remote_wakeup_enabled: bool, |
| 63 | self_powered: bool, | 65 | self_powered: bool, |
| 64 | pending_address: u8, | 66 | pending_address: u8, |
| 67 | |||
| 68 | classes: C, | ||
| 65 | } | 69 | } |
| 66 | 70 | ||
| 67 | impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | 71 | impl<'d, D: Driver<'d>, C: UsbClass<'d, D>> UsbDevice<'d, D, C> { |
| 68 | pub(crate) fn build( | 72 | pub(crate) fn build( |
| 69 | mut driver: D, | 73 | mut driver: D, |
| 70 | config: Config<'d>, | 74 | config: Config<'d>, |
| 71 | device_descriptor: &'d [u8], | 75 | device_descriptor: &'d [u8], |
| 72 | config_descriptor: &'d [u8], | 76 | config_descriptor: &'d [u8], |
| 73 | bos_descriptor: &'d [u8], | 77 | bos_descriptor: &'d [u8], |
| 78 | classes: C, | ||
| 74 | ) -> Self { | 79 | ) -> Self { |
| 75 | let control_out = driver | 80 | let control = driver |
| 76 | .alloc_endpoint_out( | 81 | .alloc_control_pipe(config.max_packet_size_0 as u16) |
| 77 | Some(0x00.into()), | ||
| 78 | EndpointType::Control, | ||
| 79 | config.max_packet_size_0 as u16, | ||
| 80 | 0, | ||
| 81 | ) | ||
| 82 | .expect("failed to alloc control endpoint"); | ||
| 83 | |||
| 84 | let control_in = driver | ||
| 85 | .alloc_endpoint_in( | ||
| 86 | Some(0x80.into()), | ||
| 87 | EndpointType::Control, | ||
| 88 | config.max_packet_size_0 as u16, | ||
| 89 | 0, | ||
| 90 | ) | ||
| 91 | .expect("failed to alloc control endpoint"); | 82 | .expect("failed to alloc control endpoint"); |
| 92 | 83 | ||
| 93 | // Enable the USB bus. | 84 | // Enable the USB bus. |
| @@ -97,8 +88,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 97 | Self { | 88 | Self { |
| 98 | bus: driver, | 89 | bus: driver, |
| 99 | config, | 90 | config, |
| 100 | control_in, | 91 | control, |
| 101 | control_out, | ||
| 102 | device_descriptor, | 92 | device_descriptor, |
| 103 | config_descriptor, | 93 | config_descriptor, |
| 104 | bos_descriptor, | 94 | bos_descriptor, |
| @@ -106,14 +96,13 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 106 | remote_wakeup_enabled: false, | 96 | remote_wakeup_enabled: false, |
| 107 | self_powered: false, | 97 | self_powered: false, |
| 108 | pending_address: 0, | 98 | pending_address: 0, |
| 99 | classes, | ||
| 109 | } | 100 | } |
| 110 | } | 101 | } |
| 111 | 102 | ||
| 112 | pub async fn run(&mut self) { | 103 | pub async fn run(&mut self) { |
| 113 | let mut buf = [0; 8]; | ||
| 114 | |||
| 115 | loop { | 104 | loop { |
| 116 | let control_fut = self.control_out.read(&mut buf); | 105 | let control_fut = self.control.setup(); |
| 117 | let bus_fut = self.bus.poll(); | 106 | let bus_fut = self.bus.poll(); |
| 118 | match select(bus_fut, control_fut).await { | 107 | match select(bus_fut, control_fut).await { |
| 119 | Either::Left(evt) => match evt { | 108 | Either::Left(evt) => match evt { |
| @@ -124,11 +113,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 124 | self.remote_wakeup_enabled = false; | 113 | self.remote_wakeup_enabled = false; |
| 125 | self.pending_address = 0; | 114 | self.pending_address = 0; |
| 126 | 115 | ||
| 127 | // TODO | 116 | self.classes.reset(); |
| 128 | //self.control.reset(); | ||
| 129 | //for cls in classes { | ||
| 130 | // cls.reset(); | ||
| 131 | //} | ||
| 132 | } | 117 | } |
| 133 | Event::Resume => {} | 118 | Event::Resume => {} |
| 134 | Event::Suspend => { | 119 | Event::Suspend => { |
| @@ -136,16 +121,9 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 136 | self.device_state = UsbDeviceState::Suspend; | 121 | self.device_state = UsbDeviceState::Suspend; |
| 137 | } | 122 | } |
| 138 | }, | 123 | }, |
| 139 | Either::Right(n) => { | 124 | Either::Right(req) => { |
| 140 | let n = n.unwrap(); | ||
| 141 | assert_eq!(n, 8); | ||
| 142 | let req = Request::parse(&buf).unwrap(); | ||
| 143 | info!("control request: {:x}", req); | 125 | info!("control request: {:x}", req); |
| 144 | 126 | ||
| 145 | // Now that we have properly parsed the setup packet, ensure the end-point is no longer in | ||
| 146 | // a stalled state. | ||
| 147 | self.control_out.set_stalled(false); | ||
| 148 | |||
| 149 | match req.direction { | 127 | match req.direction { |
| 150 | UsbDirection::In => self.handle_control_in(req).await, | 128 | UsbDirection::In => self.handle_control_in(req).await, |
| 151 | UsbDirection::Out => self.handle_control_out(req).await, | 129 | UsbDirection::Out => self.handle_control_out(req).await, |
| @@ -155,36 +133,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 155 | } | 133 | } |
| 156 | } | 134 | } |
| 157 | 135 | ||
| 158 | async fn write_chunked(&mut self, data: &[u8]) -> Result<(), driver::WriteError> { | ||
| 159 | for c in data.chunks(8) { | ||
| 160 | self.control_in.write(c).await?; | ||
| 161 | } | ||
| 162 | if data.len() % 8 == 0 { | ||
| 163 | self.control_in.write(&[]).await?; | ||
| 164 | } | ||
| 165 | Ok(()) | ||
| 166 | } | ||
| 167 | |||
| 168 | async fn control_out_accept(&mut self, req: Request) { | ||
| 169 | info!("control out accept"); | ||
| 170 | // status phase | ||
| 171 | // todo: cleanup | ||
| 172 | self.control_out.read(&mut []).await.unwrap(); | ||
| 173 | } | ||
| 174 | |||
| 175 | async fn control_in_accept(&mut self, req: Request, data: &[u8]) { | ||
| 176 | info!("control accept {:x}", data); | ||
| 177 | |||
| 178 | let len = data.len().min(req.length as _); | ||
| 179 | if let Err(e) = self.write_chunked(&data[..len]).await { | ||
| 180 | info!("write_chunked failed: {:?}", e); | ||
| 181 | } | ||
| 182 | |||
| 183 | // status phase | ||
| 184 | // todo: cleanup | ||
| 185 | self.control_out.read(&mut []).await.unwrap(); | ||
| 186 | } | ||
| 187 | |||
| 188 | async fn control_in_accept_writer( | 136 | async fn control_in_accept_writer( |
| 189 | &mut self, | 137 | &mut self, |
| 190 | req: Request, | 138 | req: Request, |
| @@ -193,17 +141,26 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 193 | let mut buf = [0; 256]; | 141 | let mut buf = [0; 256]; |
| 194 | let mut w = DescriptorWriter::new(&mut buf); | 142 | let mut w = DescriptorWriter::new(&mut buf); |
| 195 | f(&mut w); | 143 | f(&mut w); |
| 196 | let pos = w.position(); | 144 | let pos = w.position().min(usize::from(req.length)); |
| 197 | self.control_in_accept(req, &buf[..pos]).await; | 145 | self.control.accept_in(&buf[..pos]).await; |
| 198 | } | ||
| 199 | |||
| 200 | fn control_reject(&mut self, req: Request) { | ||
| 201 | info!("control reject"); | ||
| 202 | self.control_out.set_stalled(true); | ||
| 203 | } | 146 | } |
| 204 | 147 | ||
| 205 | async fn handle_control_out(&mut self, req: Request) { | 148 | async fn handle_control_out(&mut self, req: Request) { |
| 206 | // TODO actually read the data if there's an OUT data phase. | 149 | { |
| 150 | let mut buf = [0; 128]; | ||
| 151 | let data = if req.length > 0 { | ||
| 152 | let size = self.control.data_out(&mut buf).await.unwrap(); | ||
| 153 | &buf[0..size] | ||
| 154 | } else { | ||
| 155 | &[] | ||
| 156 | }; | ||
| 157 | |||
| 158 | match self.classes.control_out(req, data).await { | ||
| 159 | RequestStatus::Accepted => return self.control.accept(), | ||
| 160 | RequestStatus::Rejected => return self.control.reject(), | ||
| 161 | RequestStatus::Unhandled => (), | ||
| 162 | } | ||
| 163 | } | ||
| 207 | 164 | ||
| 208 | const CONFIGURATION_NONE_U16: u16 = CONFIGURATION_NONE as u16; | 165 | const CONFIGURATION_NONE_U16: u16 = CONFIGURATION_NONE as u16; |
| 209 | const CONFIGURATION_VALUE_U16: u16 = CONFIGURATION_VALUE as u16; | 166 | const CONFIGURATION_VALUE_U16: u16 = CONFIGURATION_VALUE as u16; |
| @@ -217,12 +174,12 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 217 | Request::FEATURE_DEVICE_REMOTE_WAKEUP, | 174 | Request::FEATURE_DEVICE_REMOTE_WAKEUP, |
| 218 | ) => { | 175 | ) => { |
| 219 | self.remote_wakeup_enabled = false; | 176 | self.remote_wakeup_enabled = false; |
| 220 | self.control_out_accept(req).await; | 177 | self.control.accept(); |
| 221 | } | 178 | } |
| 222 | 179 | ||
| 223 | (Recipient::Endpoint, Request::CLEAR_FEATURE, Request::FEATURE_ENDPOINT_HALT) => { | 180 | (Recipient::Endpoint, Request::CLEAR_FEATURE, Request::FEATURE_ENDPOINT_HALT) => { |
| 224 | //self.bus.set_stalled(((req.index as u8) & 0x8f).into(), false); | 181 | //self.bus.set_stalled(((req.index as u8) & 0x8f).into(), false); |
| 225 | self.control_out_accept(req).await; | 182 | self.control.accept(); |
| 226 | } | 183 | } |
| 227 | 184 | ||
| 228 | ( | 185 | ( |
| @@ -231,51 +188,61 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 231 | Request::FEATURE_DEVICE_REMOTE_WAKEUP, | 188 | Request::FEATURE_DEVICE_REMOTE_WAKEUP, |
| 232 | ) => { | 189 | ) => { |
| 233 | self.remote_wakeup_enabled = true; | 190 | self.remote_wakeup_enabled = true; |
| 234 | self.control_out_accept(req).await; | 191 | self.control.accept(); |
| 235 | } | 192 | } |
| 236 | 193 | ||
| 237 | (Recipient::Endpoint, Request::SET_FEATURE, Request::FEATURE_ENDPOINT_HALT) => { | 194 | (Recipient::Endpoint, Request::SET_FEATURE, Request::FEATURE_ENDPOINT_HALT) => { |
| 238 | self.bus | 195 | self.bus |
| 239 | .set_stalled(((req.index as u8) & 0x8f).into(), true); | 196 | .set_stalled(((req.index as u8) & 0x8f).into(), true); |
| 240 | self.control_out_accept(req).await; | 197 | self.control.accept(); |
| 241 | } | 198 | } |
| 242 | 199 | ||
| 243 | (Recipient::Device, Request::SET_ADDRESS, 1..=127) => { | 200 | (Recipient::Device, Request::SET_ADDRESS, 1..=127) => { |
| 244 | self.pending_address = req.value as u8; | 201 | self.pending_address = req.value as u8; |
| 245 | 202 | ||
| 246 | // on NRF the hardware auto-handles SET_ADDRESS. | 203 | // on NRF the hardware auto-handles SET_ADDRESS. |
| 247 | self.control_out_accept(req).await; | 204 | self.control.accept(); |
| 248 | } | 205 | } |
| 249 | 206 | ||
| 250 | (Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => { | 207 | (Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => { |
| 251 | self.device_state = UsbDeviceState::Configured; | 208 | self.device_state = UsbDeviceState::Configured; |
| 252 | self.control_out_accept(req).await; | 209 | self.control.accept(); |
| 253 | } | 210 | } |
| 254 | 211 | ||
| 255 | (Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => { | 212 | (Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => { |
| 256 | match self.device_state { | 213 | match self.device_state { |
| 257 | UsbDeviceState::Default => { | 214 | UsbDeviceState::Default => { |
| 258 | self.control_out_accept(req).await; | 215 | self.control.accept(); |
| 259 | } | 216 | } |
| 260 | _ => { | 217 | _ => { |
| 261 | self.device_state = UsbDeviceState::Addressed; | 218 | self.device_state = UsbDeviceState::Addressed; |
| 262 | self.control_out_accept(req).await; | 219 | self.control.accept(); |
| 263 | } | 220 | } |
| 264 | } | 221 | } |
| 265 | } | 222 | } |
| 266 | 223 | ||
| 267 | (Recipient::Interface, Request::SET_INTERFACE, DEFAULT_ALTERNATE_SETTING_U16) => { | 224 | (Recipient::Interface, Request::SET_INTERFACE, DEFAULT_ALTERNATE_SETTING_U16) => { |
| 268 | // TODO: do something when alternate settings are implemented | 225 | // TODO: do something when alternate settings are implemented |
| 269 | self.control_out_accept(req).await; | 226 | self.control.accept(); |
| 270 | } | 227 | } |
| 271 | 228 | ||
| 272 | _ => self.control_reject(req), | 229 | _ => self.control.reject(), |
| 273 | }, | 230 | }, |
| 274 | _ => self.control_reject(req), | 231 | _ => self.control.reject(), |
| 275 | } | 232 | } |
| 276 | } | 233 | } |
| 277 | 234 | ||
| 278 | async fn handle_control_in(&mut self, req: Request) { | 235 | async fn handle_control_in(&mut self, req: Request) { |
| 236 | match self | ||
| 237 | .classes | ||
| 238 | .control_in(req, class::ControlIn::new(&mut self.control)) | ||
| 239 | .await | ||
| 240 | .status() | ||
| 241 | { | ||
| 242 | RequestStatus::Accepted | RequestStatus::Rejected => return, | ||
| 243 | RequestStatus::Unhandled => (), | ||
| 244 | } | ||
| 245 | |||
| 279 | match req.request_type { | 246 | match req.request_type { |
| 280 | RequestType::Standard => match (req.recipient, req.request) { | 247 | RequestType::Standard => match (req.recipient, req.request) { |
| 281 | (Recipient::Device, Request::GET_STATUS) => { | 248 | (Recipient::Device, Request::GET_STATUS) => { |
| @@ -286,12 +253,12 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 286 | if self.remote_wakeup_enabled { | 253 | if self.remote_wakeup_enabled { |
| 287 | status |= 0x0002; | 254 | status |= 0x0002; |
| 288 | } | 255 | } |
| 289 | self.control_in_accept(req, &status.to_le_bytes()).await; | 256 | self.control.accept_in(&status.to_le_bytes()).await; |
| 290 | } | 257 | } |
| 291 | 258 | ||
| 292 | (Recipient::Interface, Request::GET_STATUS) => { | 259 | (Recipient::Interface, Request::GET_STATUS) => { |
| 293 | let status: u16 = 0x0000; | 260 | let status: u16 = 0x0000; |
| 294 | self.control_in_accept(req, &status.to_le_bytes()).await; | 261 | self.control.accept_in(&status.to_le_bytes()).await; |
| 295 | } | 262 | } |
| 296 | 263 | ||
| 297 | (Recipient::Endpoint, Request::GET_STATUS) => { | 264 | (Recipient::Endpoint, Request::GET_STATUS) => { |
| @@ -300,7 +267,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 300 | if self.bus.is_stalled(ep_addr) { | 267 | if self.bus.is_stalled(ep_addr) { |
| 301 | status |= 0x0001; | 268 | status |= 0x0001; |
| 302 | } | 269 | } |
| 303 | self.control_in_accept(req, &status.to_le_bytes()).await; | 270 | self.control.accept_in(&status.to_le_bytes()).await; |
| 304 | } | 271 | } |
| 305 | 272 | ||
| 306 | (Recipient::Device, Request::GET_DESCRIPTOR) => { | 273 | (Recipient::Device, Request::GET_DESCRIPTOR) => { |
| @@ -312,17 +279,17 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 312 | UsbDeviceState::Configured => CONFIGURATION_VALUE, | 279 | UsbDeviceState::Configured => CONFIGURATION_VALUE, |
| 313 | _ => CONFIGURATION_NONE, | 280 | _ => CONFIGURATION_NONE, |
| 314 | }; | 281 | }; |
| 315 | self.control_in_accept(req, &status.to_le_bytes()).await; | 282 | self.control.accept_in(&status.to_le_bytes()).await; |
| 316 | } | 283 | } |
| 317 | 284 | ||
| 318 | (Recipient::Interface, Request::GET_INTERFACE) => { | 285 | (Recipient::Interface, Request::GET_INTERFACE) => { |
| 319 | // TODO: change when alternate settings are implemented | 286 | // TODO: change when alternate settings are implemented |
| 320 | let status = DEFAULT_ALTERNATE_SETTING; | 287 | let status = DEFAULT_ALTERNATE_SETTING; |
| 321 | self.control_in_accept(req, &status.to_le_bytes()).await; | 288 | self.control.accept_in(&status.to_le_bytes()).await; |
| 322 | } | 289 | } |
| 323 | _ => self.control_reject(req), | 290 | _ => self.control.reject(), |
| 324 | }, | 291 | }, |
| 325 | _ => self.control_reject(req), | 292 | _ => self.control.reject(), |
| 326 | } | 293 | } |
| 327 | } | 294 | } |
| 328 | 295 | ||
| @@ -331,11 +298,9 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 331 | let config = self.config.clone(); | 298 | let config = self.config.clone(); |
| 332 | 299 | ||
| 333 | match dtype { | 300 | match dtype { |
| 334 | descriptor_type::BOS => self.control_in_accept(req, self.bos_descriptor).await, | 301 | descriptor_type::BOS => self.control.accept_in(self.bos_descriptor).await, |
| 335 | descriptor_type::DEVICE => self.control_in_accept(req, self.device_descriptor).await, | 302 | descriptor_type::DEVICE => self.control.accept_in(self.device_descriptor).await, |
| 336 | descriptor_type::CONFIGURATION => { | 303 | descriptor_type::CONFIGURATION => self.control.accept_in(self.config_descriptor).await, |
| 337 | self.control_in_accept(req, self.config_descriptor).await | ||
| 338 | } | ||
| 339 | descriptor_type::STRING => { | 304 | descriptor_type::STRING => { |
| 340 | if index == 0 { | 305 | if index == 0 { |
| 341 | self.control_in_accept_writer(req, |w| { | 306 | self.control_in_accept_writer(req, |w| { |
| @@ -363,11 +328,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 363 | self.control_in_accept_writer(req, |w| w.string(s).unwrap()) | 328 | self.control_in_accept_writer(req, |w| w.string(s).unwrap()) |
| 364 | .await; | 329 | .await; |
| 365 | } else { | 330 | } else { |
| 366 | self.control_reject(req) | 331 | self.control.reject() |
| 367 | } | 332 | } |
| 368 | } | 333 | } |
| 369 | } | 334 | } |
| 370 | _ => self.control_reject(req), | 335 | _ => self.control.reject(), |
| 371 | } | 336 | } |
| 372 | } | 337 | } |
| 373 | } | 338 | } |
diff --git a/examples/nrf/src/bin/usb/cdc_acm.rs b/examples/nrf/src/bin/usb/cdc_acm.rs index b7c112ae6..eebf89221 100644 --- a/examples/nrf/src/bin/usb/cdc_acm.rs +++ b/examples/nrf/src/bin/usb/cdc_acm.rs | |||
| @@ -1,5 +1,8 @@ | |||
| 1 | use core::convert::TryInto; | 1 | use core::future::Future; |
| 2 | use core::mem; | 2 | use core::mem; |
| 3 | use defmt::info; | ||
| 4 | use embassy_usb::class::{ControlInRequestStatus, RequestStatus, UsbClass}; | ||
| 5 | use embassy_usb::control::{self, Request}; | ||
| 3 | use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError}; | 6 | use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError}; |
| 4 | use embassy_usb::{driver::Driver, types::*, UsbDeviceBuilder}; | 7 | use embassy_usb::{driver::Driver, types::*, UsbDeviceBuilder}; |
| 5 | 8 | ||
| @@ -39,16 +42,107 @@ const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; | |||
| 39 | /// terminated with a short packet, even if the bulk endpoint is used for stream-like data. | 42 | /// terminated with a short packet, even if the bulk endpoint is used for stream-like data. |
| 40 | pub struct CdcAcmClass<'d, D: Driver<'d>> { | 43 | pub struct CdcAcmClass<'d, D: Driver<'d>> { |
| 41 | // TODO not pub | 44 | // TODO not pub |
| 42 | pub comm_if: InterfaceNumber, | ||
| 43 | pub comm_ep: D::EndpointIn, | 45 | pub comm_ep: D::EndpointIn, |
| 44 | pub data_if: InterfaceNumber, | 46 | pub data_if: InterfaceNumber, |
| 45 | pub read_ep: D::EndpointOut, | 47 | pub read_ep: D::EndpointOut, |
| 46 | pub write_ep: D::EndpointIn, | 48 | pub write_ep: D::EndpointIn, |
| 49 | pub control: CdcAcmControl, | ||
| 50 | } | ||
| 51 | |||
| 52 | pub struct CdcAcmControl { | ||
| 53 | pub comm_if: InterfaceNumber, | ||
| 47 | pub line_coding: LineCoding, | 54 | pub line_coding: LineCoding, |
| 48 | pub dtr: bool, | 55 | pub dtr: bool, |
| 49 | pub rts: bool, | 56 | pub rts: bool, |
| 50 | } | 57 | } |
| 51 | 58 | ||
| 59 | impl<'d, D: Driver<'d>> UsbClass<'d, D> for CdcAcmControl { | ||
| 60 | type ControlOutFuture<'a> = impl Future<Output = RequestStatus> + 'a where Self: 'a, 'd: 'a, D: 'a; | ||
| 61 | type ControlInFuture<'a> = impl Future<Output = ControlInRequestStatus> + 'a where Self: 'a, 'd: 'a, D: 'a; | ||
| 62 | |||
| 63 | fn reset(&mut self) { | ||
| 64 | self.line_coding = LineCoding::default(); | ||
| 65 | self.dtr = false; | ||
| 66 | self.rts = false; | ||
| 67 | } | ||
| 68 | |||
| 69 | fn control_out<'a>( | ||
| 70 | &'a mut self, | ||
| 71 | req: control::Request, | ||
| 72 | data: &'a [u8], | ||
| 73 | ) -> Self::ControlOutFuture<'a> | ||
| 74 | where | ||
| 75 | 'd: 'a, | ||
| 76 | D: 'a, | ||
| 77 | { | ||
| 78 | async move { | ||
| 79 | if !(req.request_type == control::RequestType::Class | ||
| 80 | && req.recipient == control::Recipient::Interface | ||
| 81 | && req.index == u8::from(self.comm_if) as u16) | ||
| 82 | { | ||
| 83 | return RequestStatus::Unhandled; | ||
| 84 | } | ||
| 85 | |||
| 86 | match req.request { | ||
| 87 | REQ_SEND_ENCAPSULATED_COMMAND => { | ||
| 88 | // We don't actually support encapsulated commands but pretend we do for standards | ||
| 89 | // compatibility. | ||
| 90 | RequestStatus::Accepted | ||
| 91 | } | ||
| 92 | REQ_SET_LINE_CODING if data.len() >= 7 => { | ||
| 93 | self.line_coding.data_rate = u32::from_le_bytes(data[0..4].try_into().unwrap()); | ||
| 94 | self.line_coding.stop_bits = data[4].into(); | ||
| 95 | self.line_coding.parity_type = data[5].into(); | ||
| 96 | self.line_coding.data_bits = data[6]; | ||
| 97 | info!("Set line coding to: {:?}", self.line_coding); | ||
| 98 | |||
| 99 | RequestStatus::Accepted | ||
| 100 | } | ||
| 101 | REQ_SET_CONTROL_LINE_STATE => { | ||
| 102 | self.dtr = (req.value & 0x0001) != 0; | ||
| 103 | self.rts = (req.value & 0x0002) != 0; | ||
| 104 | info!("Set dtr {}, rts {}", self.dtr, self.rts); | ||
| 105 | |||
| 106 | RequestStatus::Accepted | ||
| 107 | } | ||
| 108 | _ => RequestStatus::Rejected, | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | fn control_in<'a>( | ||
| 114 | &'a mut self, | ||
| 115 | req: Request, | ||
| 116 | control: embassy_usb::class::ControlIn<'a, 'd, D>, | ||
| 117 | ) -> Self::ControlInFuture<'a> | ||
| 118 | where | ||
| 119 | 'd: 'a, | ||
| 120 | { | ||
| 121 | async move { | ||
| 122 | if !(req.request_type == control::RequestType::Class | ||
| 123 | && req.recipient == control::Recipient::Interface | ||
| 124 | && req.index == u8::from(self.comm_if) as u16) | ||
| 125 | { | ||
| 126 | return control.ignore(); | ||
| 127 | } | ||
| 128 | |||
| 129 | match req.request { | ||
| 130 | // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. | ||
| 131 | REQ_GET_LINE_CODING if req.length == 7 => { | ||
| 132 | info!("Sending line coding"); | ||
| 133 | let mut data = [0; 7]; | ||
| 134 | data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes()); | ||
| 135 | data[4] = self.line_coding.stop_bits as u8; | ||
| 136 | data[5] = self.line_coding.parity_type as u8; | ||
| 137 | data[6] = self.line_coding.data_bits; | ||
| 138 | control.accept(&data).await | ||
| 139 | } | ||
| 140 | _ => control.reject(), | ||
| 141 | } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 52 | impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | 146 | impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { |
| 53 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For | 147 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For |
| 54 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. | 148 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. |
| @@ -133,19 +227,21 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | |||
| 133 | builder.config_descriptor.endpoint(read_ep.info()).unwrap(); | 227 | builder.config_descriptor.endpoint(read_ep.info()).unwrap(); |
| 134 | 228 | ||
| 135 | CdcAcmClass { | 229 | CdcAcmClass { |
| 136 | comm_if, | ||
| 137 | comm_ep, | 230 | comm_ep, |
| 138 | data_if, | 231 | data_if, |
| 139 | read_ep, | 232 | read_ep, |
| 140 | write_ep, | 233 | write_ep, |
| 141 | line_coding: LineCoding { | 234 | control: CdcAcmControl { |
| 142 | stop_bits: StopBits::One, | 235 | comm_if, |
| 143 | data_bits: 8, | 236 | dtr: false, |
| 144 | parity_type: ParityType::None, | 237 | rts: false, |
| 145 | data_rate: 8_000, | 238 | line_coding: LineCoding { |
| 239 | stop_bits: StopBits::One, | ||
| 240 | data_bits: 8, | ||
| 241 | parity_type: ParityType::None, | ||
| 242 | data_rate: 8_000, | ||
| 243 | }, | ||
| 146 | }, | 244 | }, |
| 147 | dtr: false, | ||
| 148 | rts: false, | ||
| 149 | } | 245 | } |
| 150 | } | 246 | } |
| 151 | 247 | ||
| @@ -158,17 +254,17 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | |||
| 158 | /// Gets the current line coding. The line coding contains information that's mainly relevant | 254 | /// Gets the current line coding. The line coding contains information that's mainly relevant |
| 159 | /// for USB to UART serial port emulators, and can be ignored if not relevant. | 255 | /// for USB to UART serial port emulators, and can be ignored if not relevant. |
| 160 | pub fn line_coding(&self) -> &LineCoding { | 256 | pub fn line_coding(&self) -> &LineCoding { |
| 161 | &self.line_coding | 257 | &self.control.line_coding |
| 162 | } | 258 | } |
| 163 | 259 | ||
| 164 | /// Gets the DTR (data terminal ready) state | 260 | /// Gets the DTR (data terminal ready) state |
| 165 | pub fn dtr(&self) -> bool { | 261 | pub fn dtr(&self) -> bool { |
| 166 | self.dtr | 262 | self.control.dtr |
| 167 | } | 263 | } |
| 168 | 264 | ||
| 169 | /// Gets the RTS (request to send) state | 265 | /// Gets the RTS (request to send) state |
| 170 | pub fn rts(&self) -> bool { | 266 | pub fn rts(&self) -> bool { |
| 171 | self.rts | 267 | self.control.rts |
| 172 | } | 268 | } |
| 173 | 269 | ||
| 174 | /// Writes a single packet into the IN endpoint. | 270 | /// Writes a single packet into the IN endpoint. |
| @@ -270,7 +366,7 @@ impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> { | |||
| 270 | */ | 366 | */ |
| 271 | 367 | ||
| 272 | /// Number of stop bits for LineCoding | 368 | /// Number of stop bits for LineCoding |
| 273 | #[derive(Copy, Clone, PartialEq, Eq)] | 369 | #[derive(Copy, Clone, PartialEq, Eq, defmt::Format)] |
| 274 | pub enum StopBits { | 370 | pub enum StopBits { |
| 275 | /// 1 stop bit | 371 | /// 1 stop bit |
| 276 | One = 0, | 372 | One = 0, |
| @@ -293,7 +389,7 @@ impl From<u8> for StopBits { | |||
| 293 | } | 389 | } |
| 294 | 390 | ||
| 295 | /// Parity for LineCoding | 391 | /// Parity for LineCoding |
| 296 | #[derive(Copy, Clone, PartialEq, Eq)] | 392 | #[derive(Copy, Clone, PartialEq, Eq, defmt::Format)] |
| 297 | pub enum ParityType { | 393 | pub enum ParityType { |
| 298 | None = 0, | 394 | None = 0, |
| 299 | Odd = 1, | 395 | Odd = 1, |
| @@ -316,6 +412,7 @@ impl From<u8> for ParityType { | |||
| 316 | /// | 412 | /// |
| 317 | /// This is provided by the host for specifying the standard UART parameters such as baud rate. Can | 413 | /// This is provided by the host for specifying the standard UART parameters such as baud rate. Can |
| 318 | /// be ignored if you don't plan to interface with a physical UART. | 414 | /// be ignored if you don't plan to interface with a physical UART. |
| 415 | #[derive(defmt::Format)] | ||
| 319 | pub struct LineCoding { | 416 | pub struct LineCoding { |
| 320 | stop_bits: StopBits, | 417 | stop_bits: StopBits, |
| 321 | data_bits: u8, | 418 | data_bits: u8, |
diff --git a/examples/nrf/src/bin/usb/main.rs b/examples/nrf/src/bin/usb/main.rs index ecbdc3461..71285579c 100644 --- a/examples/nrf/src/bin/usb/main.rs +++ b/examples/nrf/src/bin/usb/main.rs | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(generic_associated_types)] | ||
| 3 | #![feature(type_alias_impl_trait)] | 4 | #![feature(type_alias_impl_trait)] |
| 4 | 5 | ||
| 5 | #[path = "../../example_common.rs"] | 6 | #[path = "../../example_common.rs"] |
| @@ -58,7 +59,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 58 | let mut class = CdcAcmClass::new(&mut builder, 64); | 59 | let mut class = CdcAcmClass::new(&mut builder, 64); |
| 59 | 60 | ||
| 60 | // Build the builder. | 61 | // Build the builder. |
| 61 | let mut usb = builder.build(); | 62 | let mut usb = builder.build(class.control); |
| 62 | 63 | ||
| 63 | // Run the USB device. | 64 | // Run the USB device. |
| 64 | let fut1 = usb.run(); | 65 | let fut1 = usb.run(); |
