mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-22 06:42:32 +00:00
Abstract chip reset logic, add Reset impls for cortex-m and esp32c3
This commit is contained in:
parent
cbc8ccc51e
commit
9f9f6e75bb
@ -15,15 +15,16 @@ categories = [
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "2.4.1"
|
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 }
|
defmt = { version = "0.3.5", optional = true }
|
||||||
embassy-boot = { version = "0.1.1", path = "../embassy-boot/boot" }
|
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-futures = { version = "0.1.1", path = "../embassy-futures" }
|
||||||
embassy-sync = { version = "0.5.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.5.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.2.0", path = "../embassy-time" }
|
embassy-time = { version = "0.2.0", path = "../embassy-time" }
|
||||||
embassy-usb = { version = "0.1.0", path = "../embassy-usb", default-features = false }
|
embassy-usb = { version = "0.1.0", path = "../embassy-usb", default-features = false }
|
||||||
embedded-storage = { version = "0.3.1" }
|
embedded-storage = { version = "0.3.1" }
|
||||||
|
esp32c3-hal = { version = "0.13.0", optional = true, default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
bootloader = []
|
bootloader = []
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use embassy_boot::BlockingFirmwareState;
|
use embassy_boot::BlockingFirmwareState;
|
||||||
use embassy_time::{Duration, Instant};
|
use embassy_time::{Duration, Instant};
|
||||||
use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
|
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,
|
DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_RT,
|
||||||
USB_CLASS_APPN_SPEC,
|
USB_CLASS_APPN_SPEC,
|
||||||
};
|
};
|
||||||
|
use crate::Reset;
|
||||||
|
|
||||||
/// Internal state for the DFU class
|
/// 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>,
|
firmware_state: BlockingFirmwareState<'d, STATE>,
|
||||||
attrs: DfuAttributes,
|
attrs: DfuAttributes,
|
||||||
state: State,
|
state: State,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
detach_start: Option<Instant>,
|
detach_start: Option<Instant>,
|
||||||
|
_rst: PhantomData<RST>,
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
pub fn new(firmware_state: BlockingFirmwareState<'d, STATE>, attrs: DfuAttributes) -> Self {
|
||||||
Control {
|
Control {
|
||||||
firmware_state,
|
firmware_state,
|
||||||
@ -27,11 +31,12 @@ impl<'d, STATE: NorFlash> Control<'d, STATE> {
|
|||||||
state: State::AppIdle,
|
state: State::AppIdle,
|
||||||
detach_start: None,
|
detach_start: None,
|
||||||
timeout: 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) {
|
fn reset(&mut self) {
|
||||||
if let Some(start) = self.detach_start {
|
if let Some(start) = self.detach_start {
|
||||||
let delta = Instant::now() - start;
|
let delta = Instant::now() - start;
|
||||||
@ -45,7 +50,7 @@ impl<'d, STATE: NorFlash> Handler for Control<'d, STATE> {
|
|||||||
self.firmware_state
|
self.firmware_state
|
||||||
.mark_dfu()
|
.mark_dfu()
|
||||||
.expect("Failed to mark DFU mode in bootloader");
|
.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.
|
/// 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.
|
/// 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>,
|
builder: &mut Builder<'d, D>,
|
||||||
handler: &'d mut Control<'d, STATE>,
|
handler: &'d mut Control<'d, STATE, RST>,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
) {
|
) {
|
||||||
let mut func = builder.function(0x00, 0x00, 0x00);
|
let mut func = builder.function(0x00, 0x00, 0x00);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater};
|
use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater};
|
||||||
use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
|
use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
|
||||||
use embassy_usb::driver::Driver;
|
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,
|
DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_DFU,
|
||||||
USB_CLASS_APPN_SPEC,
|
USB_CLASS_APPN_SPEC,
|
||||||
};
|
};
|
||||||
|
use crate::Reset;
|
||||||
|
|
||||||
/// Internal state for USB DFU
|
/// 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>,
|
updater: BlockingFirmwareUpdater<'d, DFU, STATE>,
|
||||||
attrs: DfuAttributes,
|
attrs: DfuAttributes,
|
||||||
state: State,
|
state: State,
|
||||||
status: Status,
|
status: Status,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
|
_rst: PhantomData<RST>,
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes) -> Self {
|
||||||
Self {
|
Self {
|
||||||
updater,
|
updater,
|
||||||
@ -26,6 +30,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Control<'d, DF
|
|||||||
state: State::DfuIdle,
|
state: State::DfuIdle,
|
||||||
status: Status::Ok,
|
status: Status::Ok,
|
||||||
offset: 0,
|
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(
|
fn control_out(
|
||||||
&mut self,
|
&mut self,
|
||||||
req: embassy_usb::control::Request,
|
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]);
|
buf[0..6].copy_from_slice(&[self.status as u8, 0x32, 0x00, 0x00, self.state as u8, 0x00]);
|
||||||
match self.state {
|
match self.state {
|
||||||
State::DlSync => self.state = State::Download,
|
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 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.
|
/// 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>,
|
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 func = builder.function(0x00, 0x00, 0x00);
|
||||||
let mut iface = func.interface();
|
let mut iface = func.interface();
|
||||||
|
@ -18,3 +18,34 @@ pub use self::application::*;
|
|||||||
not(any(feature = "bootloader", feature = "application"))
|
not(any(feature = "bootloader", feature = "application"))
|
||||||
))]
|
))]
|
||||||
compile_error!("usb-dfu must be compiled with exactly one of `bootloader`, or `application` features");
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = [] }
|
||||||
embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" }
|
embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" }
|
||||||
embassy-usb = { version = "0.1.0", path = "../../../../embassy-usb" }
|
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 = { version = "0.3", optional = true }
|
||||||
defmt-rtt = { version = "0.4", optional = true }
|
defmt-rtt = { version = "0.4", optional = true }
|
||||||
|
@ -16,7 +16,7 @@ use embassy_sync::blocking_mutex::Mutex;
|
|||||||
use embassy_time::Duration;
|
use embassy_time::Duration;
|
||||||
use embassy_usb::Builder;
|
use embassy_usb::Builder;
|
||||||
use embassy_usb_dfu::consts::DfuAttributes;
|
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 _;
|
use panic_reset as _;
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
@ -57,7 +57,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
&mut control_buf,
|
&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();
|
let mut dev = builder.build();
|
||||||
dev.run().await
|
dev.run().await
|
||||||
|
@ -17,7 +17,7 @@ cortex-m-rt = { version = "0.7" }
|
|||||||
embedded-storage = "0.3.1"
|
embedded-storage = "0.3.1"
|
||||||
embedded-storage-async = "0.4.0"
|
embedded-storage-async = "0.4.0"
|
||||||
cfg-if = "1.0.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-usb = { version = "0.1.0", path = "../../../../embassy-usb", default-features = false }
|
||||||
embassy-futures = { version = "0.1.1", path = "../../../../embassy-futures" }
|
embassy-futures = { version = "0.1.1", path = "../../../../embassy-futures" }
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ use embassy_stm32::{bind_interrupts, peripherals, usb};
|
|||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
use embassy_usb::Builder;
|
use embassy_usb::Builder;
|
||||||
use embassy_usb_dfu::consts::DfuAttributes;
|
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 {
|
bind_interrupts!(struct Irqs {
|
||||||
USB_LP => usb::InterruptHandler<peripherals::USB>;
|
USB_LP => usb::InterruptHandler<peripherals::USB>;
|
||||||
@ -64,7 +64,7 @@ fn main() -> ! {
|
|||||||
&mut control_buf,
|
&mut control_buf,
|
||||||
);
|
);
|
||||||
|
|
||||||
usb_dfu::<_, _, _, 4096>(&mut builder, &mut state);
|
usb_dfu::<_, _, _, ResetImmediate, 4096>(&mut builder, &mut state);
|
||||||
|
|
||||||
let mut dev = builder.build();
|
let mut dev = builder.build();
|
||||||
embassy_futures::block_on(dev.run());
|
embassy_futures::block_on(dev.run());
|
||||||
|
Loading…
Reference in New Issue
Block a user