Merge pull request #15 from ferrous-systems/usb_set_address

pre-implement USB SET_ADDRESS request handling
This commit is contained in:
Lotte Steenbrink 2020-07-03 17:08:00 +02:00 committed by GitHub
commit 2dd5d8d73f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 236 additions and 148 deletions

View file

@ -256,10 +256,12 @@ 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: 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. 3. when you have successfully received a GET_DESCRIPTOR request for a Device descriptor you are done and can move to the next section.
Alternatively, you can start from `usb-2` instead of `usb-1`. In either case, the tasks are the same. Alternatively, you can start from `usb-2` instead of `usb-1`. In either case, the tasks are the same.
@ -269,6 +271,7 @@ If you are logging like the `usb-2` starter code does then you should see an out
``` console ``` console
INFO:usb_2 -- USB: UsbReset @ 438.842772ms INFO:usb_2 -- USB: UsbReset @ 438.842772ms
INFO:usb_2 -- USB: UsbEp0Setup @ 514.984128ms INFO:usb_2 -- USB: UsbEp0Setup @ 514.984128ms
...
INFO:usb_2 -- SETUP: bmrequesttype: 128, brequest: 6, wlength: 64, windex: 0, wvalue: 256 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 -- GET_DESCRIPTOR Device [length=64]
INFO:usb_2 -- Goal reached; move to the next section INFO:usb_2 -- Goal reached; move to the next section

View file

@ -1,13 +1,15 @@
//! Some USB 2.0 data types //! 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(missing_docs)]
#![deny(warnings)] #![deny(warnings)]
#![no_std] #![no_std]
#[cfg(TODO)]
use core::num::NonZeroU8; use core::num::NonZeroU8;
/// Device address assigned by the host; will be in the range 1..=127
pub type Address = NonZeroU8;
/// Standard USB request /// Standard USB request
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Request { pub enum Request {
@ -22,10 +24,9 @@ pub enum Request {
/// SET_ADDRESS /// SET_ADDRESS
// see section 9.4.6 of the USB specification // see section 9.4.6 of the USB specification
#[cfg(TODO)]
SetAddress { SetAddress {
/// New device address, in the range `1..=127` /// New device address, in the range `1..=127`
address: Option<NonZeroU8>, address: Option<Address>,
}, },
/// SET_CONFIGURATION /// SET_CONFIGURATION
@ -50,7 +51,8 @@ impl Request {
windex: u16, windex: u16,
wlength: u16, wlength: u16,
) -> Result<Self, ()> { ) -> Result<Self, ()> {
// see table 9-4 // see table 9-4 (USB specification)
const SET_ADDRESS: u8 = 5;
const GET_DESCRIPTOR: u8 = 6; const GET_DESCRIPTOR: u8 = 6;
if bmrequesttype == 0b10000000 && brequest == GET_DESCRIPTOR { if bmrequesttype == 0b10000000 && brequest == GET_DESCRIPTOR {
@ -75,6 +77,18 @@ impl Request {
} else { } else {
Err(()) 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 { } else {
Err(()) Err(())
} }
@ -97,7 +111,6 @@ pub enum Descriptor {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[cfg(TODO)]
use core::num::NonZeroU8; use core::num::NonZeroU8;
use crate::{Descriptor, Request}; 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] #[test]
fn set_address() { fn set_address() {
// OK: SET_ADDRESS 16 // 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)] #[cfg(TODO)]
#[test] #[test]
fn set_configuration() { fn set_configuration() {

View file

@ -5,9 +5,11 @@
#![deny(warnings)] #![deny(warnings)]
#![no_std] #![no_std]
#[cfg(TODO)]
use core::num::NonZeroU8; use core::num::NonZeroU8;
/// Device address assigned by the host; will be in the range 1..=127
pub type Address = NonZeroU8;
/// Standard USB request /// Standard USB request
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Request { pub enum Request {
@ -22,10 +24,9 @@ pub enum Request {
/// SET_ADDRESS /// SET_ADDRESS
// see section 9.4.6 of the USB specification // see section 9.4.6 of the USB specification
#[cfg(TODO)]
SetAddress { SetAddress {
/// New device address, in the range `1..=127` /// New device address, in the range `1..=127`
address: Option<NonZeroU8>, address: Option<Address>,
}, },
/// SET_CONFIGURATION /// SET_CONFIGURATION
@ -50,17 +51,24 @@ impl Request {
windex: u16, windex: u16,
wlength: u16, wlength: u16,
) -> Result<Self, ()> { ) -> Result<Self, ()> {
// see table 9-4 // see table 9-4 (USB specification)
const SET_ADDRESS: u8 = 5;
const GET_DESCRIPTOR: u8 = 6; const GET_DESCRIPTOR: u8 = 6;
if bmrequesttype == 0b10000000 && brequest == GET_DESCRIPTOR { if bmrequesttype == 0b10000000 && brequest == GET_DESCRIPTOR {
// see table 9-5 // see table 9-5
const DEVICE: u8 = 1; const DEVICE: u8 = 1;
// 1. get descriptor type and descriptor index from wValue
let desc_ty = (wvalue >> 8) as u8; let desc_ty = (wvalue >> 8) as u8;
let desc_index = wvalue as u8; let desc_index = wvalue as u8;
let langid = windex; 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 { if desc_ty == DEVICE && desc_index == 0 && langid == 0 {
Ok(Request::GetDescriptor { Ok(Request::GetDescriptor {
descriptor: Descriptor::Device, descriptor: Descriptor::Device,
@ -69,6 +77,18 @@ impl Request {
} else { } else {
Err(()) 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 { } else {
Err(()) Err(())
} }
@ -92,7 +112,6 @@ pub enum Descriptor {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[cfg(TODO)]
use core::num::NonZeroU8; use core::num::NonZeroU8;
use crate::{Descriptor, Request}; 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] #[test]
fn set_address() { fn set_address() {
// OK: SET_ADDRESS 16 // 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)] #[cfg(TODO)]
#[test] #[test]
fn set_configuration() { fn set_configuration() {

View file

@ -4,9 +4,11 @@
#![deny(warnings)] #![deny(warnings)]
#![no_std] #![no_std]
#[cfg(TODO)]
use core::num::NonZeroU8; use core::num::NonZeroU8;
/// Device address assigned by the host; will be in the range 1..=127
pub type Address = NonZeroU8;
/// Standard USB request /// Standard USB request
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Request { pub enum Request {
@ -21,10 +23,9 @@ pub enum Request {
/// SET_ADDRESS /// SET_ADDRESS
// see section 9.4.6 of the USB specification // see section 9.4.6 of the USB specification
#[cfg(TODO)]
SetAddress { SetAddress {
/// New device address, in the range `1..=127` /// New device address, in the range `1..=127`
address: Option<NonZeroU8>, address: Option<Address>,
}, },
/// SET_CONFIGURATION /// SET_CONFIGURATION
@ -43,14 +44,39 @@ impl Request {
/// Returns `Err` if the SETUP data doesn't match a supported standard 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 // see section 9.4 of the USB specification; in particular tables 9-3, 9-4 and 9-5
pub fn parse( pub fn parse(
_bmrequesttype: u8, bmrequesttype: u8,
_brequest: u8, brequest: u8,
_wvalue: u16, wvalue: u16,
_windex: u16, windex: u16,
_wlength: u16, wlength: u16,
) -> Result<Self, ()> { ) -> Result<Self, ()> {
// FIXME // see table 9-4 (USB specification)
Err(()) 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)] #[cfg(test)]
mod tests { mod tests {
#[cfg(TODO)]
use core::num::NonZeroU8; use core::num::NonZeroU8;
use crate::{Descriptor, Request}; 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] #[test]
fn set_address() { fn set_address() {
// OK: SET_ADDRESS 16 // 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)] #[cfg(TODO)]
#[test] #[test]
fn set_configuration() { fn set_configuration() {

View file

@ -62,19 +62,23 @@ fn on_event(usbd: &USBD, event: Event) {
wvalue wvalue
); );
if let Ok(Request::GetDescriptor { descriptor, length }) = let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) .expect("Error parsing request");
{ match request {
match descriptor { Request::GetDescriptor { descriptor, length }
Descriptor::Device => { if descriptor == Descriptor::Device =>
log::info!("GET_DESCRIPTOR Device [length={}]", length); {
log::info!("GET_DESCRIPTOR Device [length={}]", length);
log::info!("Goal reached; move to the next section"); log::info!("Goal reached; move to the next section");
dk::exit() dk::exit()
}
} }
} else { Request::SetAddress { .. } => {
unreachable!() // don't care about this for now // 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
} }
} }
} }

View file

@ -60,20 +60,26 @@ fn on_event(_usbd: &USBD, event: Event) {
wvalue wvalue
); );
// FIXME modify `advanced/common/usb` to make this work let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
if let Ok(Request::GetDescriptor { descriptor, length }) = .expect("Error parsing request");
Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) match request {
{ Request::GetDescriptor { descriptor, length }
match descriptor { if descriptor == Descriptor::Device =>
Descriptor::Device => { {
log::info!("GET_DESCRIPTOR Device [length={}]", length); // 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"); log::info!("GET_DESCRIPTOR Device [length={}]", length);
dk::exit()
} log::info!("Goal reached; move to the next section");
dk::exit()
} }
} else { Request::SetAddress { .. } => {
unreachable!() // don't care about this for now // 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
} }
} }
} }

View file

@ -64,35 +64,42 @@ fn on_event(usbd: &USBD, ep0in: &mut Ep0In, event: Event) {
wvalue wvalue
); );
if let Ok(Request::GetDescriptor { descriptor, length }) = let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) .expect("Error parsing request");
{ match request {
match descriptor { Request::GetDescriptor { descriptor, length }
Descriptor::Device => { if descriptor == Descriptor::Device =>
log::info!("GET_DESCRIPTOR Device [length={}]", length); {
log::info!("GET_DESCRIPTOR Device [length={}]", length);
let desc = usb2::device::Descriptor { let desc = usb2::device::Descriptor {
bDeviceClass: 0, bDeviceClass: 0,
bDeviceProtocol: 0, bDeviceProtocol: 0,
bDeviceSubClass: 0, bDeviceSubClass: 0,
bMaxPacketSize0: usb2::device::bMaxPacketSize0::B64, bMaxPacketSize0: usb2::device::bMaxPacketSize0::B64,
bNumConfigurations: core::num::NonZeroU8::new(1).unwrap(), bNumConfigurations: core::num::NonZeroU8::new(1).unwrap(),
bcdDevice: 0x01_00, // 1.00 bcdDevice: 0x01_00, // 1.00
iManufacturer: None, iManufacturer: None,
iProduct: None, iProduct: None,
iSerialNumber: None, iSerialNumber: None,
idProduct: consts::PID, idProduct: consts::PID,
idVendor: consts::VID, idVendor: consts::VID,
}; };
let desc_bytes = desc.bytes(); let desc_bytes = desc.bytes();
let resp = let resp = &desc_bytes[..core::cmp::min(desc_bytes.len(), usize::from(length))];
&desc_bytes[..core::cmp::min(desc_bytes.len(), usize::from(length))]; ep0in.start(&resp, usbd);
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()
} }
} }
} }

View file

@ -64,22 +64,30 @@ fn on_event(usbd: &USBD, ep0in: &mut Ep0In, event: Event) {
wvalue wvalue
); );
if let Ok(Request::GetDescriptor { descriptor, length }) = let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) .expect("Error parsing request");
{ match request {
match descriptor { Request::GetDescriptor { descriptor, length }
Descriptor::Device => { if descriptor == Descriptor::Device =>
log::info!("GET_DESCRIPTOR Device [length={}]", length); {
log::info!("GET_DESCRIPTOR Device [length={}]", length);
// FIXME send back a valid device descriptor, truncated to `length` bytes // TODO send back a valid device descriptor, truncated to `length` bytes
// let desc = usb2::device::Descriptor { .. }; // let desc = usb2::device::Descriptor { .. };
let resp = []; let resp = [];
ep0in.start(&resp, usbd); 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()
} }
} }
} }

View file

@ -77,10 +77,11 @@ fn ep0setup(usbd: &USBD, ep0in: &mut Ep0In, _state: &mut usb2::State) -> Result<
wvalue wvalue
); );
let req = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)?; let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
log::info!("{:?}", req); .expect("Error parsing request");
log::info!("EP0: {:?}", request);
match req { match request {
Request::GetDescriptor { descriptor, length } => match descriptor { Request::GetDescriptor { descriptor, length } => match descriptor {
Descriptor::Device => { Descriptor::Device => {
let desc = usb2::device::Descriptor { 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); let _ = ep0in.start(&bytes[..core::cmp::min(bytes.len(), length.into())], usbd);
} }
// TODO Configuration descriptor // TODO implement Configuration descriptor
// Descriptor::Configuration => todo!(), // 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 // TODO: handle this request properly now.
// Request::SetAddress { .. } => todo!(), todo!()
}
// TODO handle SET_CONFIGURATION request
// Request::SetConfiguration { .. } => todo!(), // Request::SetConfiguration { .. } => todo!(),
} }