diff --git a/advanced/README.md b/advanced/README.md index ae62584..e187bf0 100644 --- a/advanced/README.md +++ b/advanced/README.md @@ -256,9 +256,11 @@ So that's what we'll do here. In the `advanced/common/usb` folder you'll find st To sum up the work to do here: -1. write a SETUP data parser in `advanced/common/usb`. You only need to handle the GET_DESCRIPTOR request and make the `get_descriptor_device` test pass for now. +1. write a SETUP data parser in `advanced/common/usb`. You only need to handle the GET_DESCRIPTOR request and make the `get_descriptor_device` test pass for now. Note that the parser already handles SET_ADDRESS requests. -2. modify `usb-1` to read (USBD registers) and parse the SETUP data when the EPSETUP event is received. +2. modify `usb-1` to read (USBD registers) and parse the SETUP data when the EPSETUP event is received. + +> Note: If you're using a Mac, you need to catch `SetAddress` requests returned by the parser as these are sent before the first GetDescriptor request. You can handle them by doing nothing. 3. when you have successfully received a GET_DESCRIPTOR request for a Device descriptor you are done and can move to the next section. @@ -269,6 +271,7 @@ If you are logging like the `usb-2` starter code does then you should see an out ``` console INFO:usb_2 -- USB: UsbReset @ 438.842772ms INFO:usb_2 -- USB: UsbEp0Setup @ 514.984128ms +... INFO:usb_2 -- SETUP: bmrequesttype: 128, brequest: 6, wlength: 64, windex: 0, wvalue: 256 INFO:usb_2 -- GET_DESCRIPTOR Device [length=64] INFO:usb_2 -- Goal reached; move to the next section diff --git a/advanced/common/usb/src/get-descriptor-configuration.rs b/advanced/common/usb/src/get-descriptor-configuration.rs index 388de88..3d531b6 100644 --- a/advanced/common/usb/src/get-descriptor-configuration.rs +++ b/advanced/common/usb/src/get-descriptor-configuration.rs @@ -1,13 +1,15 @@ //! Some USB 2.0 data types -// NOTE this is a solution to exercise `usb-2` +// NOTE this is a solution to exercise `usb-3` #![deny(missing_docs)] #![deny(warnings)] #![no_std] -#[cfg(TODO)] use core::num::NonZeroU8; +/// Device address assigned by the host; will be in the range 1..=127 +pub type Address = NonZeroU8; + /// Standard USB request #[derive(Clone, Copy, Debug, PartialEq)] pub enum Request { @@ -22,10 +24,9 @@ pub enum Request { /// SET_ADDRESS // see section 9.4.6 of the USB specification - #[cfg(TODO)] SetAddress { /// New device address, in the range `1..=127` - address: Option, + address: Option
, }, /// SET_CONFIGURATION @@ -50,7 +51,8 @@ impl Request { windex: u16, wlength: u16, ) -> Result { - // see table 9-4 + // see table 9-4 (USB specification) + const SET_ADDRESS: u8 = 5; const GET_DESCRIPTOR: u8 = 6; if bmrequesttype == 0b10000000 && brequest == GET_DESCRIPTOR { @@ -75,6 +77,18 @@ impl Request { } else { Err(()) } + } else if bmrequesttype == 0b00000000 && brequest == SET_ADDRESS { + // Set the device address for all future accesses. + // (Needed to successfully init when conected to Apple devices) + // Section 9.4.6 Set Address of the USB specification explains which values for wvalue, + // windex and wlength are valid. + if wvalue < 128 && windex == 0 && wlength == 0 { + Ok(Request::SetAddress { + address: NonZeroU8::new(wvalue as u8), + }) + } else { + Err(()) + } } else { Err(()) } @@ -97,7 +111,6 @@ pub enum Descriptor { #[cfg(test)] mod tests { - #[cfg(TODO)] use core::num::NonZeroU8; use crate::{Descriptor, Request}; @@ -122,23 +135,6 @@ mod tests { // ^^^^ } - #[test] - fn get_descriptor_configuration() { - // OK: GET_DESCRIPTOR Configuration 0 [length=9] - assert_eq!( - Request::parse(0b1000_0000, 0x06, 0x02_00, 0, 9), - Ok(Request::GetDescriptor { - descriptor: Descriptor::Configuration { index: 0 }, - length: 9 - }) - ); - - // has language ID but shouldn't - assert!(Request::parse(0b1000_0000, 0x06, 0x02_00, 1033, 9).is_err()); - // ^^^^ - } - - #[cfg(TODO)] #[test] fn set_address() { // OK: SET_ADDRESS 16 @@ -168,6 +164,22 @@ mod tests { // ^ } + #[test] + fn get_descriptor_configuration() { + // OK: GET_DESCRIPTOR Configuration 0 [length=9] + assert_eq!( + Request::parse(0b1000_0000, 0x06, 0x02_00, 0, 9), + Ok(Request::GetDescriptor { + descriptor: Descriptor::Configuration { index: 0 }, + length: 9 + }) + ); + + // has language ID but shouldn't + assert!(Request::parse(0b1000_0000, 0x06, 0x02_00, 1033, 9).is_err()); + // ^^^^ + } + #[cfg(TODO)] #[test] fn set_configuration() { diff --git a/advanced/common/usb/src/get-descriptor-device.rs b/advanced/common/usb/src/get-descriptor-device.rs index 36fb245..fd1c210 100644 --- a/advanced/common/usb/src/get-descriptor-device.rs +++ b/advanced/common/usb/src/get-descriptor-device.rs @@ -5,9 +5,11 @@ #![deny(warnings)] #![no_std] -#[cfg(TODO)] use core::num::NonZeroU8; +/// Device address assigned by the host; will be in the range 1..=127 +pub type Address = NonZeroU8; + /// Standard USB request #[derive(Clone, Copy, Debug, PartialEq)] pub enum Request { @@ -22,10 +24,9 @@ pub enum Request { /// SET_ADDRESS // see section 9.4.6 of the USB specification - #[cfg(TODO)] SetAddress { /// New device address, in the range `1..=127` - address: Option, + address: Option
, }, /// SET_CONFIGURATION @@ -50,17 +51,24 @@ impl Request { windex: u16, wlength: u16, ) -> Result { - // see table 9-4 + // see table 9-4 (USB specification) + const SET_ADDRESS: u8 = 5; const GET_DESCRIPTOR: u8 = 6; + if bmrequesttype == 0b10000000 && brequest == GET_DESCRIPTOR { // see table 9-5 const DEVICE: u8 = 1; + // 1. get descriptor type and descriptor index from wValue let desc_ty = (wvalue >> 8) as u8; let desc_index = wvalue as u8; let langid = windex; + // 2. confirm that the descriptor + // - is of type DEVICE and + // - has descriptor index 0 (i.e. it is the first implemented descriptor for this type) and + // - has wIndex 0 (i.e. no language ID since it's not a string descriptor) if desc_ty == DEVICE && desc_index == 0 && langid == 0 { Ok(Request::GetDescriptor { descriptor: Descriptor::Device, @@ -69,6 +77,18 @@ impl Request { } else { Err(()) } + } else if bmrequesttype == 0b00000000 && brequest == SET_ADDRESS { + // Set the device address for all future accesses. + // (Needed to successfully init when conected to Apple devices) + // Section 9.4.6 Set Address of the USB specification explains which values for wvalue, + // windex and wlength are valid. + if wvalue < 128 && windex == 0 && wlength == 0 { + Ok(Request::SetAddress { + address: NonZeroU8::new(wvalue as u8), + }) + } else { + Err(()) + } } else { Err(()) } @@ -92,7 +112,6 @@ pub enum Descriptor { #[cfg(test)] mod tests { - #[cfg(TODO)] use core::num::NonZeroU8; use crate::{Descriptor, Request}; @@ -117,24 +136,6 @@ mod tests { // ^^^^ } - #[cfg(TODO)] - #[test] - fn get_descriptor_configuration() { - // OK: GET_DESCRIPTOR Configuration 0 [length=9] - assert_eq!( - Request::parse(0b1000_0000, 0x06, 0x02_00, 0, 9), - Ok(Request::GetDescriptor { - descriptor: Descriptor::Configuration { index: 0 }, - length: 9 - }) - ); - - // has language ID but shouldn't - assert!(Request::parse(0b1000_0000, 0x06, 0x02_00, 1033, 9).is_err()); - // ^^^^ - } - - #[cfg(TODO)] #[test] fn set_address() { // OK: SET_ADDRESS 16 @@ -164,6 +165,23 @@ mod tests { // ^ } + #[cfg(TODO)] + #[test] + fn get_descriptor_configuration() { + // OK: GET_DESCRIPTOR Configuration 0 [length=9] + assert_eq!( + Request::parse(0b1000_0000, 0x06, 0x02_00, 0, 9), + Ok(Request::GetDescriptor { + descriptor: Descriptor::Configuration { index: 0 }, + length: 9 + }) + ); + + // has language ID but shouldn't + assert!(Request::parse(0b1000_0000, 0x06, 0x02_00, 1033, 9).is_err()); + // ^^^^ + } + #[cfg(TODO)] #[test] fn set_configuration() { diff --git a/advanced/common/usb/src/lib.rs b/advanced/common/usb/src/lib.rs index 3b455b3..2685091 100644 --- a/advanced/common/usb/src/lib.rs +++ b/advanced/common/usb/src/lib.rs @@ -4,9 +4,11 @@ #![deny(warnings)] #![no_std] -#[cfg(TODO)] use core::num::NonZeroU8; +/// Device address assigned by the host; will be in the range 1..=127 +pub type Address = NonZeroU8; + /// Standard USB request #[derive(Clone, Copy, Debug, PartialEq)] pub enum Request { @@ -21,10 +23,9 @@ pub enum Request { /// SET_ADDRESS // see section 9.4.6 of the USB specification - #[cfg(TODO)] SetAddress { /// New device address, in the range `1..=127` - address: Option, + address: Option
, }, /// SET_CONFIGURATION @@ -43,14 +44,39 @@ impl Request { /// Returns `Err` if the SETUP data doesn't match a supported standard request // see section 9.4 of the USB specification; in particular tables 9-3, 9-4 and 9-5 pub fn parse( - _bmrequesttype: u8, - _brequest: u8, - _wvalue: u16, - _windex: u16, - _wlength: u16, + bmrequesttype: u8, + brequest: u8, + wvalue: u16, + windex: u16, + wlength: u16, ) -> Result { - // FIXME - Err(()) + // see table 9-4 (USB specification) + const SET_ADDRESS: u8 = 5; + + // TODO implement another branch handling GET_DESCRIPTOR requests: + + // 1. get descriptor type and descriptor index from wValue + + // 2. confirm that the descriptor + // - is of type DEVICE and + // - has descriptor index 0 (i.e. it is the first implemented descriptor for this type) and + // - has wIndex 0 (i.e. no language ID since it's not a string descriptor) + + if bmrequesttype == 0b00000000 && brequest == SET_ADDRESS { + // Set the device address for all future accesses. + // (Needed to successfully init when conected to Apple devices) + // Section 9.4.6 Set Address of the USB specification explains which values for wvalue, + // windex and wlength are valid. + if wvalue < 128 && windex == 0 && wlength == 0 { + Ok(Request::SetAddress { + address: NonZeroU8::new(wvalue as u8), + }) + } else { + Err(()) + } + } else { + Err(()) + } } } @@ -71,7 +97,6 @@ pub enum Descriptor { #[cfg(test)] mod tests { - #[cfg(TODO)] use core::num::NonZeroU8; use crate::{Descriptor, Request}; @@ -96,24 +121,6 @@ mod tests { // ^^^^ } - #[cfg(TODO)] - #[test] - fn get_descriptor_configuration() { - // OK: GET_DESCRIPTOR Configuration 0 [length=9] - assert_eq!( - Request::parse(0b1000_0000, 0x06, 0x02_00, 0, 9), - Ok(Request::GetDescriptor { - descriptor: Descriptor::Configuration { index: 0 }, - length: 9 - }) - ); - - // has language ID but shouldn't - assert!(Request::parse(0b1000_0000, 0x06, 0x02_00, 1033, 9).is_err()); - // ^^^^ - } - - #[cfg(TODO)] #[test] fn set_address() { // OK: SET_ADDRESS 16 @@ -143,6 +150,23 @@ mod tests { // ^ } + #[cfg(TODO)] + #[test] + fn get_descriptor_configuration() { + // OK: GET_DESCRIPTOR Configuration 0 [length=9] + assert_eq!( + Request::parse(0b1000_0000, 0x06, 0x02_00, 0, 9), + Ok(Request::GetDescriptor { + descriptor: Descriptor::Configuration { index: 0 }, + length: 9 + }) + ); + + // has language ID but shouldn't + assert!(Request::parse(0b1000_0000, 0x06, 0x02_00, 1033, 9).is_err()); + // ^^^^ + } + #[cfg(TODO)] #[test] fn set_configuration() { diff --git a/advanced/firmware/src/bin/usb-2-solution.rs b/advanced/firmware/src/bin/usb-2-solution.rs index 4cec5dc..a49c948 100644 --- a/advanced/firmware/src/bin/usb-2-solution.rs +++ b/advanced/firmware/src/bin/usb-2-solution.rs @@ -62,19 +62,23 @@ fn on_event(usbd: &USBD, event: Event) { wvalue ); - if let Ok(Request::GetDescriptor { descriptor, length }) = - Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) - { - match descriptor { - Descriptor::Device => { - log::info!("GET_DESCRIPTOR Device [length={}]", length); + let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) + .expect("Error parsing request"); + match request { + Request::GetDescriptor { descriptor, length } + if descriptor == Descriptor::Device => + { + log::info!("GET_DESCRIPTOR Device [length={}]", length); - log::info!("Goal reached; move to the next section"); - dk::exit() - } + log::info!("Goal reached; move to the next section"); + dk::exit() } - } else { - unreachable!() // don't care about this for now + Request::SetAddress { .. } => { + // On Mac OS you'll get this request before the GET_DESCRIPTOR request so we + // need to catch it here. We'll properly handle this request later + // but for now it's OK to do nothing. + } + _ => unreachable!(), // we don't handle any other Requests } } } diff --git a/advanced/firmware/src/bin/usb-2.rs b/advanced/firmware/src/bin/usb-2.rs index b63310a..abcf6df 100644 --- a/advanced/firmware/src/bin/usb-2.rs +++ b/advanced/firmware/src/bin/usb-2.rs @@ -60,20 +60,26 @@ fn on_event(_usbd: &USBD, event: Event) { wvalue ); - // FIXME modify `advanced/common/usb` to make this work - if let Ok(Request::GetDescriptor { descriptor, length }) = - Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) - { - match descriptor { - Descriptor::Device => { - log::info!("GET_DESCRIPTOR Device [length={}]", length); + let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) + .expect("Error parsing request"); + match request { + Request::GetDescriptor { descriptor, length } + if descriptor == Descriptor::Device => + { + // TODO modify `Request::parse()` in `advanced/common/usb/lib.rs` + // so that this branch is reached - log::info!("Goal reached; move to the next section"); - dk::exit() - } + log::info!("GET_DESCRIPTOR Device [length={}]", length); + + log::info!("Goal reached; move to the next section"); + dk::exit() } - } else { - unreachable!() // don't care about this for now + Request::SetAddress { .. } => { + // On Mac OS you'll get this request before the GET_DESCRIPTOR request so we + // need to catch it here. We'll properly handle this request later + // but for now it's OK to do nothing. + } + _ => unreachable!(), // we don't handle any other Requests } } } diff --git a/advanced/firmware/src/bin/usb-3-solution.rs b/advanced/firmware/src/bin/usb-3-solution.rs index fbc405f..31b4ab4 100644 --- a/advanced/firmware/src/bin/usb-3-solution.rs +++ b/advanced/firmware/src/bin/usb-3-solution.rs @@ -64,35 +64,42 @@ fn on_event(usbd: &USBD, ep0in: &mut Ep0In, event: Event) { wvalue ); - if let Ok(Request::GetDescriptor { descriptor, length }) = - Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) - { - match descriptor { - Descriptor::Device => { - log::info!("GET_DESCRIPTOR Device [length={}]", length); + let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) + .expect("Error parsing request"); + match request { + Request::GetDescriptor { descriptor, length } + if descriptor == Descriptor::Device => + { + log::info!("GET_DESCRIPTOR Device [length={}]", length); - let desc = usb2::device::Descriptor { - bDeviceClass: 0, - bDeviceProtocol: 0, - bDeviceSubClass: 0, - bMaxPacketSize0: usb2::device::bMaxPacketSize0::B64, - bNumConfigurations: core::num::NonZeroU8::new(1).unwrap(), - bcdDevice: 0x01_00, // 1.00 - iManufacturer: None, - iProduct: None, - iSerialNumber: None, - idProduct: consts::PID, - idVendor: consts::VID, - }; - let desc_bytes = desc.bytes(); - let resp = - &desc_bytes[..core::cmp::min(desc_bytes.len(), usize::from(length))]; - ep0in.start(&resp, usbd); - } + let desc = usb2::device::Descriptor { + bDeviceClass: 0, + bDeviceProtocol: 0, + bDeviceSubClass: 0, + bMaxPacketSize0: usb2::device::bMaxPacketSize0::B64, + bNumConfigurations: core::num::NonZeroU8::new(1).unwrap(), + bcdDevice: 0x01_00, // 1.00 + iManufacturer: None, + iProduct: None, + iSerialNumber: None, + idProduct: consts::PID, + idVendor: consts::VID, + }; + let desc_bytes = desc.bytes(); + let resp = &desc_bytes[..core::cmp::min(desc_bytes.len(), usize::from(length))]; + ep0in.start(&resp, usbd); + } + Request::SetAddress { .. } => { + // On Mac OS you'll get this request before the GET_DESCRIPTOR request so we + // need to catch it here. We'll properly handle this request later + // but for now it's OK to do nothing. + } + _ => { + log::error!( + "unknown request (goal achieved if GET_DESCRIPTOR Device was handled)" + ); + dk::exit() } - } else { - log::error!("unknown request (goal achieved if GET_DESCRIPTOR Device was handled)"); - dk::exit() } } } diff --git a/advanced/firmware/src/bin/usb-3.rs b/advanced/firmware/src/bin/usb-3.rs index a68b0bc..9a43ccb 100644 --- a/advanced/firmware/src/bin/usb-3.rs +++ b/advanced/firmware/src/bin/usb-3.rs @@ -64,22 +64,30 @@ fn on_event(usbd: &USBD, ep0in: &mut Ep0In, event: Event) { wvalue ); - if let Ok(Request::GetDescriptor { descriptor, length }) = - Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) - { - match descriptor { - Descriptor::Device => { - log::info!("GET_DESCRIPTOR Device [length={}]", length); + let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) + .expect("Error parsing request"); + match request { + Request::GetDescriptor { descriptor, length } + if descriptor == Descriptor::Device => + { + log::info!("GET_DESCRIPTOR Device [length={}]", length); - // FIXME send back a valid device descriptor, truncated to `length` bytes - // let desc = usb2::device::Descriptor { .. }; - let resp = []; - ep0in.start(&resp, usbd); - } + // TODO send back a valid device descriptor, truncated to `length` bytes + // let desc = usb2::device::Descriptor { .. }; + let resp = []; + ep0in.start(&resp, usbd); + } + Request::SetAddress { .. } => { + // On Mac OS you'll get this request before the GET_DESCRIPTOR request so we + // need to catch it here. We'll properly handle this request later + // but for now it's OK to do nothing. + } + _ => { + log::error!( + "unknown request (goal achieved if GET_DESCRIPTOR Device was handled)" + ); + dk::exit() } - } else { - log::error!("unknown request (goal achieved if GET_DESCRIPTOR Device was handled)"); - dk::exit() } } } diff --git a/advanced/firmware/src/bin/usb-4.rs b/advanced/firmware/src/bin/usb-4.rs index f79983d..b99f05f 100644 --- a/advanced/firmware/src/bin/usb-4.rs +++ b/advanced/firmware/src/bin/usb-4.rs @@ -77,10 +77,11 @@ fn ep0setup(usbd: &USBD, ep0in: &mut Ep0In, _state: &mut usb2::State) -> Result< wvalue ); - let req = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)?; - log::info!("{:?}", req); + let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) + .expect("Error parsing request"); + log::info!("EP0: {:?}", request); - match req { + match request { Request::GetDescriptor { descriptor, length } => match descriptor { Descriptor::Device => { let desc = usb2::device::Descriptor { @@ -100,12 +101,17 @@ fn ep0setup(usbd: &USBD, ep0in: &mut Ep0In, _state: &mut usb2::State) -> Result< let _ = ep0in.start(&bytes[..core::cmp::min(bytes.len(), length.into())], usbd); } - // TODO Configuration descriptor - // Descriptor::Configuration => todo!(), + // TODO implement Configuration descriptor + // Descriptor::Configuration { .. } => todo!(), }, + Request::SetAddress { .. } => { + // On Mac OS you'll get this request before the GET_DESCRIPTOR request so we + // need to catch it here. - // TODO - // Request::SetAddress { .. } => todo!(), + // TODO: handle this request properly now. + todo!() + } + // TODO handle SET_CONFIGURATION request // Request::SetConfiguration { .. } => todo!(), }