diff --git a/advanced/common/usb/src/lib.rs b/advanced/common/usb/src/lib.rs index 3b455b3..4b8ff2e 100644 --- a/advanced/common/usb/src/lib.rs +++ b/advanced/common/usb/src/lib.rs @@ -4,7 +4,6 @@ #![deny(warnings)] #![no_std] -#[cfg(TODO)] use core::num::NonZeroU8; /// Standard USB request @@ -21,7 +20,6 @@ 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, @@ -43,14 +41,48 @@ 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; + const GET_DESCRIPTOR: u8 = 6; // todo deleteme + + // TODO löschen und durch instructions ersetzen + if bmrequesttype == 0b10000000 && brequest == GET_DESCRIPTOR { + // see table 9-5 + const DEVICE: u8 = 1; + + let desc_ty = (wvalue >> 8) as u8; + let desc_index = wvalue as u8; + let langid = windex; + + if desc_ty == DEVICE && desc_index == 0 && langid == 0 { + Ok(Request::GetDescriptor { + descriptor: Descriptor::Device, + length: wlength, + }) + } else { + Err(()) + } + } else if bmrequesttype == 0b00000000 && brequest == SET_ADDRESS { + + // Set the device address for all future accesses. + // Needed to successfully init when using Apple devices. + if wvalue < 128 && windex == 0 && wlength == 0 { + Ok(Request::SetAddress { + address: NonZeroU8::new(wvalue as u8), + }) + } else { + Err(()) + } + } else { + Err(()) + } + } } @@ -69,9 +101,27 @@ pub enum Descriptor { // there are even more descriptor types but we don't need to support them } +/// Device address assigned by the host; will be in the range 1..=127 +pub type Address = NonZeroU8; +/// The state of the USB device +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum DeviceState { + /// The default state + Default, + /// The address-ed state + Address(Address), + /// The configured state + Configured { + /// The address of the device + address: Address, + /// The configuration value + value: NonZeroU8, + }, +} + + #[cfg(test)] mod tests { - #[cfg(TODO)] use core::num::NonZeroU8; use crate::{Descriptor, Request}; diff --git a/advanced/firmware/src/bin/usb-2-solution.rs b/advanced/firmware/src/bin/usb-2-solution.rs index 4cec5dc..c49d430 100644 --- a/advanced/firmware/src/bin/usb-2-solution.rs +++ b/advanced/firmware/src/bin/usb-2-solution.rs @@ -6,11 +6,12 @@ use dk::{ usbd::{self, Event}, }; use panic_log as _; // panic handler -use usb::{Descriptor, Request}; +use usb::{Descriptor, Request, DeviceState}; #[rtic::app(device = dk)] const APP: () = { struct Resources { + state: DeviceState, usbd: USBD, } @@ -20,20 +21,24 @@ const APP: () = { usbd::init(board.power, &board.usbd); - init::LateResources { usbd: board.usbd } + init::LateResources { + usbd: board.usbd, + state: DeviceState::Default, + } } - #[task(binds = USBD, resources = [usbd])] + #[task(binds = USBD, resources = [state, usbd])] fn main(cx: main::Context) { let usbd = cx.resources.usbd; + let state = cx.resources.state; // used to store the device address sent by the host while let Some(event) = usbd::next_event(usbd) { - on_event(usbd, event) + on_event(usbd, state, event) } } }; -fn on_event(usbd: &USBD, event: Event) { +fn on_event(usbd: &USBD, state: &mut DeviceState, event: Event) { log::info!("USB: {:?} @ {:?}", event, dk::uptime()); match event { @@ -62,16 +67,31 @@ 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 => { + // todo handle less indentedly? + if let Ok(request) = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength) { + 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() } + Request::SetAddress { address } => { + log::info!("SETUP: device state is {:?}", state); + + // todo hide this in a helper? + // todo check for state configured -> err + if let Some(address) = address { + log::info!("SETUP: assigning address {}", address); + *state = DeviceState::Address(address); + } else { + log::info!("SETUP: address was None; assigning Default"); + *state = DeviceState::Default; + } + } + _ => unreachable!(), // we don't handle any other Requests } } else { unreachable!() // don't care about this for now diff --git a/advanced/firmware/src/bin/usb-4.rs b/advanced/firmware/src/bin/usb-4.rs index f79983d..8aa68f9 100644 --- a/advanced/firmware/src/bin/usb-4.rs +++ b/advanced/firmware/src/bin/usb-4.rs @@ -103,7 +103,6 @@ fn ep0setup(usbd: &USBD, ep0in: &mut Ep0In, _state: &mut usb2::State) -> Result< // TODO Configuration descriptor // Descriptor::Configuration => todo!(), }, - // TODO // Request::SetAddress { .. } => todo!(), // Request::SetConfiguration { .. } => todo!(),