diff --git a/advanced/firmware/Cargo.lock b/advanced/firmware/Cargo.lock index e5b308e..c004d4d 100644 --- a/advanced/firmware/Cargo.lock +++ b/advanced/firmware/Cargo.lock @@ -60,7 +60,7 @@ version = "0.1.0" [[package]] name = "cortex-m" version = "0.6.2" -source = "git+https://github.com/rust-embedded/cortex-m#3136e01e708413774d7be2868705e1782c910027" +source = "git+https://github.com/rust-embedded/cortex-m#c9c7539233954822a6132f4bc13e5763371b5cb2" dependencies = [ "bare-metal", "bitfield", @@ -118,6 +118,7 @@ name = "dk" version = "0.1.0" dependencies = [ "cortex-m", + "cortex-m-rt", "embedded-hal", "log", "nrf52840-hal", @@ -143,6 +144,7 @@ dependencies = [ "cortex-m-rt", "cortex-m-rtfm", "dk", + "heapless", "log", "panic-log", "quote", @@ -201,9 +203,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" +checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" dependencies = [ "autocfg", ] @@ -226,7 +228,7 @@ checksum = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" [[package]] name = "nrf-hal-common" version = "0.10.0" -source = "git+https://github.com/japaric/nrf-hal?branch=radio#443662d20591c3bef26a4fff2de03de90fec51aa" +source = "git+https://github.com/japaric/nrf-hal?branch=radio#d624e80e5724e4709081ed65abaf63271fe1eca7" dependencies = [ "cast", "cortex-m", @@ -241,7 +243,7 @@ dependencies = [ [[package]] name = "nrf52840-hal" version = "0.10.0" -source = "git+https://github.com/japaric/nrf-hal?branch=radio#443662d20591c3bef26a4fff2de03de90fec51aa" +source = "git+https://github.com/japaric/nrf-hal?branch=radio#d624e80e5724e4709081ed65abaf63271fe1eca7" dependencies = [ "cast", "cortex-m", @@ -274,18 +276,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ "proc-macro2", ] @@ -362,9 +364,9 @@ checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" [[package]] name = "syn" -version = "1.0.25" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f14a640819f79b72a710c0be059dce779f9339ae046c8bef12c361d56702146f" +checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" dependencies = [ "proc-macro2", "quote", @@ -396,7 +398,7 @@ version = "0.1.0" [[package]] name = "usb2" version = "0.0.0" -source = "git+https://github.com/japaric/usb2#c2b8118e667026f690c847782ebc5809aba749fe" +source = "git+https://github.com/japaric/usb2#6a8c4b1ed0db005dcf19694684524b33cc04abd2" [[package]] name = "vcell" diff --git a/advanced/firmware/Cargo.toml b/advanced/firmware/Cargo.toml index df3ab52..2bbda37 100644 --- a/advanced/firmware/Cargo.toml +++ b/advanced/firmware/Cargo.toml @@ -16,6 +16,7 @@ cortex-m-rt = "0.6.12" # TODO switch to RTIC before public release cortex-m-rtfm = "0.5.1" dk = { path = "../../boards/dk" } +heapless = "0.5.5" log = "0.4.8" panic-log = { path = "../../common/panic-log" } usb = { path = "../common/usb" } diff --git a/advanced/firmware/src/bin/rtic-usb-3.rs b/advanced/firmware/src/bin/rtic-usb-3.rs index f4449a4..87e4c64 100644 --- a/advanced/firmware/src/bin/rtic-usb-3.rs +++ b/advanced/firmware/src/bin/rtic-usb-3.rs @@ -16,15 +16,10 @@ const APP: () = { struct Resources { usbd: USBD, ep0in: Ep0In, - #[init([0; 64])] - buffer: [u8; 64], } - #[init(resources = [buffer])] - fn init(cx: init::Context) -> init::LateResources { - let buffer: &'static mut [u8; 64] = cx.resources.buffer; - let ep0in = Ep0In::new(buffer); - + #[init] + fn init(_cx: init::Context) -> init::LateResources { let board = dk::init().unwrap(); usbd::init(board.power, &board.usbd); @@ -32,8 +27,8 @@ const APP: () = { usbd::connect(&board.usbd); init::LateResources { + ep0in: board.ep0in, usbd: board.usbd, - ep0in, } } diff --git a/advanced/firmware/src/bin/rtic-usb-4.rs b/advanced/firmware/src/bin/rtic-usb-4.rs index cc24432..9f9053a 100644 --- a/advanced/firmware/src/bin/rtic-usb-4.rs +++ b/advanced/firmware/src/bin/rtic-usb-4.rs @@ -15,18 +15,13 @@ use usb2::{GetDescriptor as Descriptor, StandardRequest as Request}; // crates.i #[rtfm::app(device = dk)] const APP: () = { struct Resources { - #[init([0; 64])] - buffer: [u8; 64], usbd: USBD, ep0in: Ep0In, state: State, } - #[init(resources = [buffer])] - fn init(cx: init::Context) -> init::LateResources { - let buffer: &'static mut [u8; 64] = cx.resources.buffer; - let ep0in = Ep0In::new(buffer); - + #[init] + fn init(_cx: init::Context) -> init::LateResources { let board = dk::init().unwrap(); usbd::init(board.power, &board.usbd); @@ -36,7 +31,7 @@ const APP: () = { init::LateResources { usbd: board.usbd, state: State::Default, - ep0in, + ep0in: board.ep0in, } } @@ -72,7 +67,7 @@ fn on_event(usbd: &USBD, ep0in: &mut Ep0In, state: &mut State, event: Event) { } } -fn ep0setup(usbd: &USBD, ep0in: &mut Ep0In, state: &mut State) -> Result<(), ()> { +fn ep0setup(usbd: &USBD, ep0in: &mut Ep0In, _state: &mut State) -> Result<(), ()> { let bmrequesttype = usbd.bmrequesttype.read().bits() as u8; let brequest = usbd.brequest.read().brequest().bits(); let wlength = usbd::wlength(usbd); diff --git a/advanced/firmware/src/bin/rtic-usb-final.rs b/advanced/firmware/src/bin/rtic-usb-final.rs index b9345ab..ffd39dd 100644 --- a/advanced/firmware/src/bin/rtic-usb-final.rs +++ b/advanced/firmware/src/bin/rtic-usb-final.rs @@ -1,6 +1,8 @@ #![no_main] #![no_std] +use core::num::NonZeroU8; + use dk::{ peripheral::USBD, usbd::{self, Ep0In, Event}, @@ -18,10 +20,7 @@ const APP: () = { #[init] fn init(_cx: init::Context) -> init::LateResources { - static mut BUFFER: [u8; 64] = [0; 64]; - let board = dk::init().unwrap(); - let ep0in = Ep0In::new(BUFFER); usbd::init(board.power, &board.usbd); @@ -30,7 +29,7 @@ const APP: () = { init::LateResources { usbd: board.usbd, state: State::Default, - ep0in, + ep0in: board.ep0in, } } @@ -106,9 +105,11 @@ fn ep0setup(usbd: &USBD, state: &mut State, ep0in: &mut Ep0In) -> Result<(), ()> GetDescriptor::Configuration { index } => { if index == 0 { - let desc = usb2::configuration::Descriptor { + let mut full_desc = heapless::Vec::::new(); + + let conf_desc = usb2::configuration::Descriptor { wTotalLength: usb2::configuration::Descriptor::SIZE.into(), - bNumInterfaces: 0, + bNumInterfaces: NonZeroU8::new(1).unwrap(), bConfigurationValue: core::num::NonZeroU8::new(CONFIG_VAL).unwrap(), iConfiguration: None, bmAttributes: usb2::configuration::bmAttributes { @@ -118,8 +119,22 @@ fn ep0setup(usbd: &USBD, state: &mut State, ep0in: &mut Ep0In) -> Result<(), ()> bMaxPower: 250, // 500 mA }; - let bytes = desc.bytes(); - ep0in.start(&bytes[..core::cmp::min(bytes.len(), length.into())], usbd)?; + let iface_desc = usb2::interface::Descriptor { + bInterfaceNumber: 0, + bAlternativeSetting: 0, + bNumEndpoints: 0, + bInterfaceClass: 0, + bInterfaceSubClass: 0, + bInterfaceProtocol: 0, + iInterface: None, + }; + + full_desc.extend_from_slice(&conf_desc.bytes()).unwrap(); + full_desc.extend_from_slice(&iface_desc.bytes()).unwrap(); + ep0in.start( + &full_desc[..core::cmp::min(full_desc.len(), length.into())], + usbd, + )?; } else { // out of bounds access: stall the endpoint return Err(()); diff --git a/boards/dk/src/lib.rs b/boards/dk/src/lib.rs index 0428d60..73bb649 100644 --- a/boards/dk/src/lib.rs +++ b/boards/dk/src/lib.rs @@ -1,5 +1,7 @@ //! Hardware Abstraction Layer (HAL) for the nRF52840 Development Kit +#![deny(missing_docs)] +#![deny(warnings)] #![no_std] use core::{ @@ -21,7 +23,10 @@ use hal::{ use log::{LevelFilter, Log}; use rtt_target::{rprintln, rtt_init_print}; -use crate::peripheral::{POWER, USBD}; +use crate::{ + peripheral::{POWER, USBD}, + usbd::Ep0In, +}; mod errata; pub mod peripheral; @@ -29,13 +34,20 @@ pub mod usbd; /// Components on the board pub struct Board { + /// LEDs pub leds: Leds, + /// Radio interface // TODO put behind feature flag (off in advanced workshop) pub radio: Radio<'static>, + /// Timer pub timer: Timer, + /// USBD (Universal Serial Bus Device) peripheral // TODO put behind feature flag (off in beginner workshop) pub usbd: USBD, + /// POWER (Power Supply) peripheral pub power: POWER, + /// USB control endpoint 0 + pub ep0in: Ep0In, } /// All LEDs on the board @@ -149,7 +161,8 @@ pub fn init() -> Result { cortex_m::Peripherals::take(), hal::target::Peripherals::take(), ) { - // NOTE(unsafe) this branch runs at most once + // NOTE(static mut) this branch runs at most once + static mut EP0IN_BUF: [u8; 64] = [0; 64]; static mut CLOCKS: Option> = None; @@ -210,6 +223,7 @@ pub fn init() -> Result { timer: Timer { inner: timer }, usbd: periph.USBD, power: periph.POWER, + ep0in: unsafe { Ep0In::new(&mut EP0IN_BUF) }, }) } else { Err(()) diff --git a/boards/dk/src/peripheral.rs b/boards/dk/src/peripheral.rs index 9ed015e..e6fe8d0 100644 --- a/boards/dk/src/peripheral.rs +++ b/boards/dk/src/peripheral.rs @@ -1 +1,3 @@ +//! Low level access to the nRF52840 peripheral + pub use hal::target::{POWER, USBD}; diff --git a/boards/dk/src/usbd.rs b/boards/dk/src/usbd.rs index f109bef..6fca8e3 100644 --- a/boards/dk/src/usbd.rs +++ b/boards/dk/src/usbd.rs @@ -1,6 +1,6 @@ -use core::sync::atomic::{self, Ordering}; +//! USBD peripheral -use cortex_m::asm; +use core::sync::atomic::{self, Ordering}; use crate::{ errata, @@ -14,17 +14,23 @@ pub struct Ep0In { } impl Ep0In { - pub fn new(buffer: &'static mut [u8; 64]) -> Self { + /// # Safety + /// Must be created at most once (singleton) + pub(crate) unsafe fn new(buffer: &'static mut [u8; 64]) -> Self { Self { buffer, busy: false, } } + /// Starts a data transfer over endpoint 0 + /// + /// # Panics + /// + /// This function panics if the last transfer was not finished by calling the `end` function pub fn start(&mut self, bytes: &[u8], usbd: &USBD) -> Result<(), ()> { if self.busy { - log::error!("EP0IN: last transfer not completed"); - return Err(()); + panic!("EP0IN: last transfer has not completed"); } if bytes.len() > self.buffer.len() { @@ -56,12 +62,17 @@ impl Ep0In { Ok(()) } + /// Completes a data transfer + /// + /// This function must be called after the EP0DATADONE event is raised + /// + /// # Panics + /// + /// This function panics if called before `start` or before the EP0DATADONE event is raised by + /// the hardware pub fn end(&mut self, usbd: &USBD) { if usbd.events_ep0datadone.read().bits() == 0 { - log::error!("Ep0In.end called before the EP0DATADONE event was raised"); - loop { - asm::bkpt(); - } + panic!("Ep0In.end called before the EP0DATADONE event was raised"); } else { // DMA transfer complete dma_end(); @@ -77,7 +88,7 @@ impl Ep0In { // // This function call *must* be *followed* by a memory *store* operation. Memory operations that // follow this function call will *not* be moved, by the compiler or the instruction pipeline, to -// *after* the function call. +// *after* the function call. fn dma_start() { atomic::fence(Ordering::Release); } @@ -87,21 +98,28 @@ fn dma_start() { // // This function call *must* be *preceded* by a memory *load* operation. Memory operations that // follow this function call will *not* be moved, by the compiler or the instruction pipeline, to -// *before* the function call. +// *before* the function call. fn dma_end() { atomic::fence(Ordering::Acquire); } +/// Initializes the USBD peripheral // NOTE will be called from user code; at that point the high frequency clock source has already // been configured to use to the external crystal // Reference: section 6.35.4 of the nRF52840 Product Specification pub fn init(power: POWER, usbd: &USBD) { + let mut once = true; + // wait until the USB has been connected while power.events_usbdetected.read().bits() == 0 { - power.events_usbdetected.reset(); + if once { + log::info!("waiting for USB connection on port J3"); + once = false; + } continue; } + power.events_usbdetected.reset(); // workaround silicon bug unsafe { errata::e187a() } @@ -147,6 +165,7 @@ pub fn disconnect(usbd: &USBD) { usbd.usbpullup.reset(); } +/// Stalls endpoint 0 pub fn ep0stall(usbd: &USBD) { usbd.tasks_ep0stall.write(|w| w.tasks_ep0stall().set_bit()); } @@ -176,7 +195,7 @@ pub fn next_event(usbd: &USBD) -> Option { } if usbd.events_ep0datadone.read().bits() != 0 { - // this will be cleared elsewhere + // this will be cleared by the `Ep0In.end` method // usbd.events_ep0datadone.reset(); return Some(Event::UsbEp0DataDone); @@ -195,21 +214,22 @@ pub fn next_event(usbd: &USBD) -> Option { pub fn todo(usbd: &USBD) { disconnect(usbd); log::error!("unimplemented"); - loop { - asm::bkpt() - } + crate::exit() } +/// Reads the WLENGTHL and WLENGTHH registers and returns the 16-bit WLENGTH component of a setup packet pub fn wlength(usbd: &USBD) -> u16 { u16::from(usbd.wlengthl.read().wlengthl().bits()) | u16::from(usbd.wlengthh.read().wlengthh().bits()) << 8 } +/// Reads the WINDEXL and WINDEXH registers and returns the 16-bit WINDEX component of a setup packet pub fn windex(usbd: &USBD) -> u16 { u16::from(usbd.windexl.read().windexl().bits()) | u16::from(usbd.windexh.read().windexh().bits()) << 8 } +/// Reads the WVALUEL and WVALUEH registers and returns the 16-bit WVALUE component of a setup packet pub fn wvalue(usbd: &USBD) -> u16 { u16::from(usbd.wvaluel.read().wvaluel().bits()) | u16::from(usbd.wvalueh.read().wvalueh().bits()) << 8