diff --git a/embassy-usb-dfu/Cargo.toml b/embassy-usb-dfu/Cargo.toml index 62398afb..02146c64 100644 --- a/embassy-usb-dfu/Cargo.toml +++ b/embassy-usb-dfu/Cargo.toml @@ -15,15 +15,16 @@ categories = [ [dependencies] bitflags = "2.4.1" -cortex-m = { version = "0.7.7", features = ["inline-asm"] } +cortex-m = { version = "0.7.7", features = ["inline-asm"], optional = true } defmt = { version = "0.3.5", optional = true } embassy-boot = { version = "0.1.1", path = "../embassy-boot/boot" } -embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } +# embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } embassy-futures = { version = "0.1.1", path = "../embassy-futures" } embassy-sync = { version = "0.5.0", path = "../embassy-sync" } embassy-time = { version = "0.2.0", path = "../embassy-time" } embassy-usb = { version = "0.1.0", path = "../embassy-usb", default-features = false } embedded-storage = { version = "0.3.1" } +esp32c3-hal = { version = "0.13.0", optional = true, default-features = false } [features] bootloader = [] diff --git a/embassy-usb-dfu/src/application.rs b/embassy-usb-dfu/src/application.rs index 5ff8f90f..75689db2 100644 --- a/embassy-usb-dfu/src/application.rs +++ b/embassy-usb-dfu/src/application.rs @@ -1,3 +1,5 @@ +use core::marker::PhantomData; + use embassy_boot::BlockingFirmwareState; use embassy_time::{Duration, Instant}; use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; @@ -9,17 +11,19 @@ use crate::consts::{ DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_RT, USB_CLASS_APPN_SPEC, }; +use crate::Reset; /// Internal state for the DFU class -pub struct Control<'d, STATE: NorFlash> { +pub struct Control<'d, STATE: NorFlash, RST: Reset> { firmware_state: BlockingFirmwareState<'d, STATE>, attrs: DfuAttributes, state: State, timeout: Option, detach_start: Option, + _rst: PhantomData, } -impl<'d, STATE: NorFlash> Control<'d, STATE> { +impl<'d, STATE: NorFlash, RST: Reset> Control<'d, STATE, RST> { pub fn new(firmware_state: BlockingFirmwareState<'d, STATE>, attrs: DfuAttributes) -> Self { Control { firmware_state, @@ -27,11 +31,12 @@ impl<'d, STATE: NorFlash> Control<'d, STATE> { state: State::AppIdle, detach_start: None, timeout: None, + _rst: PhantomData, } } } -impl<'d, STATE: NorFlash> Handler for Control<'d, STATE> { +impl<'d, STATE: NorFlash, RST: Reset> Handler for Control<'d, STATE, RST> { fn reset(&mut self) { if let Some(start) = self.detach_start { let delta = Instant::now() - start; @@ -45,7 +50,7 @@ impl<'d, STATE: NorFlash> Handler for Control<'d, STATE> { self.firmware_state .mark_dfu() .expect("Failed to mark DFU mode in bootloader"); - cortex_m::peripheral::SCB::sys_reset(); + RST::sys_reset() } } } @@ -103,9 +108,9 @@ impl<'d, STATE: NorFlash> Handler for Control<'d, STATE> { /// it should expose a DFU device, and a software reset will be issued. /// /// To apply USB DFU updates, the bootloader must be capable of recognizing the DFU magic and exposing a device to handle the full DFU transaction with the host. -pub fn usb_dfu<'d, D: Driver<'d>, STATE: NorFlash>( +pub fn usb_dfu<'d, D: Driver<'d>, STATE: NorFlash, RST: Reset>( builder: &mut Builder<'d, D>, - handler: &'d mut Control<'d, STATE>, + handler: &'d mut Control<'d, STATE, RST>, timeout: Duration, ) { let mut func = builder.function(0x00, 0x00, 0x00); diff --git a/embassy-usb-dfu/src/bootloader.rs b/embassy-usb-dfu/src/bootloader.rs index 99384d96..d41e6280 100644 --- a/embassy-usb-dfu/src/bootloader.rs +++ b/embassy-usb-dfu/src/bootloader.rs @@ -1,3 +1,5 @@ +use core::marker::PhantomData; + use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater}; use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; use embassy_usb::driver::Driver; @@ -8,17 +10,19 @@ use crate::consts::{ DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_DFU, USB_CLASS_APPN_SPEC, }; +use crate::Reset; /// Internal state for USB DFU -pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> { +pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> { updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes, state: State, status: Status, offset: usize, + _rst: PhantomData, } -impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Control<'d, DFU, STATE, BLOCK_SIZE> { +impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Control<'d, DFU, STATE, RST, BLOCK_SIZE> { pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes) -> Self { Self { updater, @@ -26,6 +30,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Control<'d, DF state: State::DfuIdle, status: Status::Ok, offset: 0, + _rst: PhantomData, } } @@ -36,7 +41,9 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Control<'d, DF } } -impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Handler for Control<'d, DFU, STATE, BLOCK_SIZE> { +impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Handler + for Control<'d, DFU, STATE, RST, BLOCK_SIZE> +{ fn control_out( &mut self, req: embassy_usb::control::Request, @@ -131,7 +138,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Handler for Co buf[0..6].copy_from_slice(&[self.status as u8, 0x32, 0x00, 0x00, self.state as u8, 0x00]); match self.state { State::DlSync => self.state = State::Download, - State::ManifestSync => cortex_m::peripheral::SCB::sys_reset(), + State::ManifestSync => RST::sys_reset(), _ => {} } @@ -157,9 +164,9 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Handler for Co /// /// Once the host has initiated a DFU download operation, the chunks sent by the host will be written to the DFU partition. /// Once the final sync in the manifestation phase has been received, the handler will trigger a system reset to swap the new firmware. -pub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize>( +pub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize>( builder: &mut Builder<'d, D>, - handler: &'d mut Control<'d, DFU, STATE, BLOCK_SIZE>, + handler: &'d mut Control<'d, DFU, STATE, RST, BLOCK_SIZE>, ) { let mut func = builder.function(0x00, 0x00, 0x00); let mut iface = func.interface(); diff --git a/embassy-usb-dfu/src/lib.rs b/embassy-usb-dfu/src/lib.rs index ae0fbbd4..283905de 100644 --- a/embassy-usb-dfu/src/lib.rs +++ b/embassy-usb-dfu/src/lib.rs @@ -18,3 +18,34 @@ pub use self::application::*; not(any(feature = "bootloader", feature = "application")) ))] compile_error!("usb-dfu must be compiled with exactly one of `bootloader`, or `application` features"); + +/// Provides a platform-agnostic interface for initiating a system reset. +/// +/// This crate exposes `ResetImmediate` when compiled with cortex-m or esp32c3 support, which immediately issues a +/// reset request without interfacing with any other peripherals. +/// +/// If alternate behaviour is desired, a custom implementation of Reset can be provided as a type argument to the usb_dfu function. +pub trait Reset { + fn sys_reset() -> !; +} + +#[cfg(feature = "esp32c3-hal")] +pub struct ResetImmediate; + +#[cfg(feature = "esp32c3-hal")] +impl Reset for ResetImmediate { + fn sys_reset() -> ! { + esp32c3_hal::reset::software_reset(); + loop {} + } +} + +#[cfg(feature = "cortex-m")] +pub struct ResetImmediate; + +#[cfg(feature = "cortex-m")] +impl Reset for ResetImmediate { + fn sys_reset() -> ! { + cortex_m::peripheral::SCB::sys_reset() + } +} diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml index 0ed0b75e..f6beea49 100644 --- a/examples/boot/application/stm32wb-dfu/Cargo.toml +++ b/examples/boot/application/stm32wb-dfu/Cargo.toml @@ -12,7 +12,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", feature embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = [] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } embassy-usb = { version = "0.1.0", path = "../../../../embassy-usb" } -embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application"] } +embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application", "cortex-m"] } defmt = { version = "0.3", optional = true } defmt-rtt = { version = "0.4", optional = true } diff --git a/examples/boot/application/stm32wb-dfu/src/main.rs b/examples/boot/application/stm32wb-dfu/src/main.rs index cdac903b..fbecbf23 100644 --- a/examples/boot/application/stm32wb-dfu/src/main.rs +++ b/examples/boot/application/stm32wb-dfu/src/main.rs @@ -16,7 +16,7 @@ use embassy_sync::blocking_mutex::Mutex; use embassy_time::Duration; use embassy_usb::Builder; use embassy_usb_dfu::consts::DfuAttributes; -use embassy_usb_dfu::{usb_dfu, Control}; +use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate}; use panic_reset as _; bind_interrupts!(struct Irqs { @@ -57,7 +57,7 @@ async fn main(_spawner: Spawner) { &mut control_buf, ); - usb_dfu::<_, _>(&mut builder, &mut state, Duration::from_millis(2500)); + usb_dfu::<_, _, ResetImmediate>(&mut builder, &mut state, Duration::from_millis(2500)); let mut dev = builder.build(); dev.run().await diff --git a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml index fde9eb57..e849eb53 100644 --- a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml +++ b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml @@ -17,7 +17,7 @@ cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.1" embedded-storage-async = "0.4.0" cfg-if = "1.0.0" -embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["bootloader"] } +embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["bootloader", "cortex-m"] } embassy-usb = { version = "0.1.0", path = "../../../../embassy-usb", default-features = false } embassy-futures = { version = "0.1.1", path = "../../../../embassy-futures" } diff --git a/examples/boot/bootloader/stm32wb-dfu/src/main.rs b/examples/boot/bootloader/stm32wb-dfu/src/main.rs index db7039e8..a7ab813b 100644 --- a/examples/boot/bootloader/stm32wb-dfu/src/main.rs +++ b/examples/boot/bootloader/stm32wb-dfu/src/main.rs @@ -14,7 +14,7 @@ use embassy_stm32::{bind_interrupts, peripherals, usb}; use embassy_sync::blocking_mutex::Mutex; use embassy_usb::Builder; use embassy_usb_dfu::consts::DfuAttributes; -use embassy_usb_dfu::{usb_dfu, Control}; +use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate}; bind_interrupts!(struct Irqs { USB_LP => usb::InterruptHandler; @@ -64,7 +64,7 @@ fn main() -> ! { &mut control_buf, ); - usb_dfu::<_, _, _, 4096>(&mut builder, &mut state); + usb_dfu::<_, _, _, ResetImmediate, 4096>(&mut builder, &mut state); let mut dev = builder.build(); embassy_futures::block_on(dev.run());