diff options
Diffstat (limited to 'embassy-usb-dfu')
| -rw-r--r-- | embassy-usb-dfu/Cargo.toml | 5 | ||||
| -rw-r--r-- | embassy-usb-dfu/src/application.rs | 17 | ||||
| -rw-r--r-- | embassy-usb-dfu/src/bootloader.rs | 19 | ||||
| -rw-r--r-- | embassy-usb-dfu/src/lib.rs | 31 |
4 files changed, 58 insertions, 14 deletions
diff --git a/embassy-usb-dfu/Cargo.toml b/embassy-usb-dfu/Cargo.toml index 62398afbc..02146c646 100644 --- a/embassy-usb-dfu/Cargo.toml +++ b/embassy-usb-dfu/Cargo.toml | |||
| @@ -15,15 +15,16 @@ categories = [ | |||
| 15 | 15 | ||
| 16 | [dependencies] | 16 | [dependencies] |
| 17 | bitflags = "2.4.1" | 17 | bitflags = "2.4.1" |
| 18 | cortex-m = { version = "0.7.7", features = ["inline-asm"] } | 18 | cortex-m = { version = "0.7.7", features = ["inline-asm"], optional = true } |
| 19 | defmt = { version = "0.3.5", optional = true } | 19 | defmt = { version = "0.3.5", optional = true } |
| 20 | embassy-boot = { version = "0.1.1", path = "../embassy-boot/boot" } | 20 | embassy-boot = { version = "0.1.1", path = "../embassy-boot/boot" } |
| 21 | embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } | 21 | # embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } |
| 22 | embassy-futures = { version = "0.1.1", path = "../embassy-futures" } | 22 | embassy-futures = { version = "0.1.1", path = "../embassy-futures" } |
| 23 | embassy-sync = { version = "0.5.0", path = "../embassy-sync" } | 23 | embassy-sync = { version = "0.5.0", path = "../embassy-sync" } |
| 24 | embassy-time = { version = "0.2.0", path = "../embassy-time" } | 24 | embassy-time = { version = "0.2.0", path = "../embassy-time" } |
| 25 | embassy-usb = { version = "0.1.0", path = "../embassy-usb", default-features = false } | 25 | embassy-usb = { version = "0.1.0", path = "../embassy-usb", default-features = false } |
| 26 | embedded-storage = { version = "0.3.1" } | 26 | embedded-storage = { version = "0.3.1" } |
| 27 | esp32c3-hal = { version = "0.13.0", optional = true, default-features = false } | ||
| 27 | 28 | ||
| 28 | [features] | 29 | [features] |
| 29 | bootloader = [] | 30 | bootloader = [] |
diff --git a/embassy-usb-dfu/src/application.rs b/embassy-usb-dfu/src/application.rs index 5ff8f90f8..75689db26 100644 --- a/embassy-usb-dfu/src/application.rs +++ b/embassy-usb-dfu/src/application.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 1 | use embassy_boot::BlockingFirmwareState; | 3 | use embassy_boot::BlockingFirmwareState; |
| 2 | use embassy_time::{Duration, Instant}; | 4 | use embassy_time::{Duration, Instant}; |
| 3 | use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; | 5 | use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; |
| @@ -9,17 +11,19 @@ use crate::consts::{ | |||
| 9 | DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_RT, | 11 | DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_RT, |
| 10 | USB_CLASS_APPN_SPEC, | 12 | USB_CLASS_APPN_SPEC, |
| 11 | }; | 13 | }; |
| 14 | use crate::Reset; | ||
| 12 | 15 | ||
| 13 | /// Internal state for the DFU class | 16 | /// Internal state for the DFU class |
| 14 | pub struct Control<'d, STATE: NorFlash> { | 17 | pub struct Control<'d, STATE: NorFlash, RST: Reset> { |
| 15 | firmware_state: BlockingFirmwareState<'d, STATE>, | 18 | firmware_state: BlockingFirmwareState<'d, STATE>, |
| 16 | attrs: DfuAttributes, | 19 | attrs: DfuAttributes, |
| 17 | state: State, | 20 | state: State, |
| 18 | timeout: Option<Duration>, | 21 | timeout: Option<Duration>, |
| 19 | detach_start: Option<Instant>, | 22 | detach_start: Option<Instant>, |
| 23 | _rst: PhantomData<RST>, | ||
| 20 | } | 24 | } |
| 21 | 25 | ||
| 22 | impl<'d, STATE: NorFlash> Control<'d, STATE> { | 26 | impl<'d, STATE: NorFlash, RST: Reset> Control<'d, STATE, RST> { |
| 23 | pub fn new(firmware_state: BlockingFirmwareState<'d, STATE>, attrs: DfuAttributes) -> Self { | 27 | pub fn new(firmware_state: BlockingFirmwareState<'d, STATE>, attrs: DfuAttributes) -> Self { |
| 24 | Control { | 28 | Control { |
| 25 | firmware_state, | 29 | firmware_state, |
| @@ -27,11 +31,12 @@ impl<'d, STATE: NorFlash> Control<'d, STATE> { | |||
| 27 | state: State::AppIdle, | 31 | state: State::AppIdle, |
| 28 | detach_start: None, | 32 | detach_start: None, |
| 29 | timeout: None, | 33 | timeout: None, |
| 34 | _rst: PhantomData, | ||
| 30 | } | 35 | } |
| 31 | } | 36 | } |
| 32 | } | 37 | } |
| 33 | 38 | ||
| 34 | impl<'d, STATE: NorFlash> Handler for Control<'d, STATE> { | 39 | impl<'d, STATE: NorFlash, RST: Reset> Handler for Control<'d, STATE, RST> { |
| 35 | fn reset(&mut self) { | 40 | fn reset(&mut self) { |
| 36 | if let Some(start) = self.detach_start { | 41 | if let Some(start) = self.detach_start { |
| 37 | let delta = Instant::now() - start; | 42 | let delta = Instant::now() - start; |
| @@ -45,7 +50,7 @@ impl<'d, STATE: NorFlash> Handler for Control<'d, STATE> { | |||
| 45 | self.firmware_state | 50 | self.firmware_state |
| 46 | .mark_dfu() | 51 | .mark_dfu() |
| 47 | .expect("Failed to mark DFU mode in bootloader"); | 52 | .expect("Failed to mark DFU mode in bootloader"); |
| 48 | cortex_m::peripheral::SCB::sys_reset(); | 53 | RST::sys_reset() |
| 49 | } | 54 | } |
| 50 | } | 55 | } |
| 51 | } | 56 | } |
| @@ -103,9 +108,9 @@ impl<'d, STATE: NorFlash> Handler for Control<'d, STATE> { | |||
| 103 | /// it should expose a DFU device, and a software reset will be issued. | 108 | /// it should expose a DFU device, and a software reset will be issued. |
| 104 | /// | 109 | /// |
| 105 | /// 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. | 110 | /// 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. |
| 106 | pub fn usb_dfu<'d, D: Driver<'d>, STATE: NorFlash>( | 111 | pub fn usb_dfu<'d, D: Driver<'d>, STATE: NorFlash, RST: Reset>( |
| 107 | builder: &mut Builder<'d, D>, | 112 | builder: &mut Builder<'d, D>, |
| 108 | handler: &'d mut Control<'d, STATE>, | 113 | handler: &'d mut Control<'d, STATE, RST>, |
| 109 | timeout: Duration, | 114 | timeout: Duration, |
| 110 | ) { | 115 | ) { |
| 111 | let mut func = builder.function(0x00, 0x00, 0x00); | 116 | 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 99384d961..d41e6280d 100644 --- a/embassy-usb-dfu/src/bootloader.rs +++ b/embassy-usb-dfu/src/bootloader.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 1 | use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater}; | 3 | use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater}; |
| 2 | use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; | 4 | use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; |
| 3 | use embassy_usb::driver::Driver; | 5 | use embassy_usb::driver::Driver; |
| @@ -8,17 +10,19 @@ use crate::consts::{ | |||
| 8 | DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_DFU, | 10 | DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_DFU, |
| 9 | USB_CLASS_APPN_SPEC, | 11 | USB_CLASS_APPN_SPEC, |
| 10 | }; | 12 | }; |
| 13 | use crate::Reset; | ||
| 11 | 14 | ||
| 12 | /// Internal state for USB DFU | 15 | /// Internal state for USB DFU |
| 13 | pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> { | 16 | pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> { |
| 14 | updater: BlockingFirmwareUpdater<'d, DFU, STATE>, | 17 | updater: BlockingFirmwareUpdater<'d, DFU, STATE>, |
| 15 | attrs: DfuAttributes, | 18 | attrs: DfuAttributes, |
| 16 | state: State, | 19 | state: State, |
| 17 | status: Status, | 20 | status: Status, |
| 18 | offset: usize, | 21 | offset: usize, |
| 22 | _rst: PhantomData<RST>, | ||
| 19 | } | 23 | } |
| 20 | 24 | ||
| 21 | impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Control<'d, DFU, STATE, BLOCK_SIZE> { | 25 | impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Control<'d, DFU, STATE, RST, BLOCK_SIZE> { |
| 22 | pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes) -> Self { | 26 | pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes) -> Self { |
| 23 | Self { | 27 | Self { |
| 24 | updater, | 28 | updater, |
| @@ -26,6 +30,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Control<'d, DF | |||
| 26 | state: State::DfuIdle, | 30 | state: State::DfuIdle, |
| 27 | status: Status::Ok, | 31 | status: Status::Ok, |
| 28 | offset: 0, | 32 | offset: 0, |
| 33 | _rst: PhantomData, | ||
| 29 | } | 34 | } |
| 30 | } | 35 | } |
| 31 | 36 | ||
| @@ -36,7 +41,9 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Control<'d, DF | |||
| 36 | } | 41 | } |
| 37 | } | 42 | } |
| 38 | 43 | ||
| 39 | impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Handler for Control<'d, DFU, STATE, BLOCK_SIZE> { | 44 | impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Handler |
| 45 | for Control<'d, DFU, STATE, RST, BLOCK_SIZE> | ||
| 46 | { | ||
| 40 | fn control_out( | 47 | fn control_out( |
| 41 | &mut self, | 48 | &mut self, |
| 42 | req: embassy_usb::control::Request, | 49 | req: embassy_usb::control::Request, |
| @@ -131,7 +138,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Handler for Co | |||
| 131 | buf[0..6].copy_from_slice(&[self.status as u8, 0x32, 0x00, 0x00, self.state as u8, 0x00]); | 138 | buf[0..6].copy_from_slice(&[self.status as u8, 0x32, 0x00, 0x00, self.state as u8, 0x00]); |
| 132 | match self.state { | 139 | match self.state { |
| 133 | State::DlSync => self.state = State::Download, | 140 | State::DlSync => self.state = State::Download, |
| 134 | State::ManifestSync => cortex_m::peripheral::SCB::sys_reset(), | 141 | State::ManifestSync => RST::sys_reset(), |
| 135 | _ => {} | 142 | _ => {} |
| 136 | } | 143 | } |
| 137 | 144 | ||
| @@ -157,9 +164,9 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Handler for Co | |||
| 157 | /// | 164 | /// |
| 158 | /// Once the host has initiated a DFU download operation, the chunks sent by the host will be written to the DFU partition. | 165 | /// Once the host has initiated a DFU download operation, the chunks sent by the host will be written to the DFU partition. |
| 159 | /// Once the final sync in the manifestation phase has been received, the handler will trigger a system reset to swap the new firmware. | 166 | /// Once the final sync in the manifestation phase has been received, the handler will trigger a system reset to swap the new firmware. |
| 160 | pub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize>( | 167 | pub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize>( |
| 161 | builder: &mut Builder<'d, D>, | 168 | builder: &mut Builder<'d, D>, |
| 162 | handler: &'d mut Control<'d, DFU, STATE, BLOCK_SIZE>, | 169 | handler: &'d mut Control<'d, DFU, STATE, RST, BLOCK_SIZE>, |
| 163 | ) { | 170 | ) { |
| 164 | let mut func = builder.function(0x00, 0x00, 0x00); | 171 | let mut func = builder.function(0x00, 0x00, 0x00); |
| 165 | let mut iface = func.interface(); | 172 | let mut iface = func.interface(); |
diff --git a/embassy-usb-dfu/src/lib.rs b/embassy-usb-dfu/src/lib.rs index ae0fbbd4c..283905de9 100644 --- a/embassy-usb-dfu/src/lib.rs +++ b/embassy-usb-dfu/src/lib.rs | |||
| @@ -18,3 +18,34 @@ pub use self::application::*; | |||
| 18 | not(any(feature = "bootloader", feature = "application")) | 18 | not(any(feature = "bootloader", feature = "application")) |
| 19 | ))] | 19 | ))] |
| 20 | compile_error!("usb-dfu must be compiled with exactly one of `bootloader`, or `application` features"); | 20 | compile_error!("usb-dfu must be compiled with exactly one of `bootloader`, or `application` features"); |
| 21 | |||
| 22 | /// Provides a platform-agnostic interface for initiating a system reset. | ||
| 23 | /// | ||
| 24 | /// This crate exposes `ResetImmediate` when compiled with cortex-m or esp32c3 support, which immediately issues a | ||
| 25 | /// reset request without interfacing with any other peripherals. | ||
| 26 | /// | ||
| 27 | /// If alternate behaviour is desired, a custom implementation of Reset can be provided as a type argument to the usb_dfu function. | ||
| 28 | pub trait Reset { | ||
| 29 | fn sys_reset() -> !; | ||
| 30 | } | ||
| 31 | |||
| 32 | #[cfg(feature = "esp32c3-hal")] | ||
| 33 | pub struct ResetImmediate; | ||
| 34 | |||
| 35 | #[cfg(feature = "esp32c3-hal")] | ||
| 36 | impl Reset for ResetImmediate { | ||
| 37 | fn sys_reset() -> ! { | ||
| 38 | esp32c3_hal::reset::software_reset(); | ||
| 39 | loop {} | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | #[cfg(feature = "cortex-m")] | ||
| 44 | pub struct ResetImmediate; | ||
| 45 | |||
| 46 | #[cfg(feature = "cortex-m")] | ||
| 47 | impl Reset for ResetImmediate { | ||
| 48 | fn sys_reset() -> ! { | ||
| 49 | cortex_m::peripheral::SCB::sys_reset() | ||
| 50 | } | ||
| 51 | } | ||
