Address reviews

This commit is contained in:
Kaitlyn Kenwell 2023-12-14 09:36:22 -05:00 committed by Dario Nieuwenhuis
parent 686ee2cb14
commit 4757257ac0
19 changed files with 307 additions and 41 deletions

View File

@ -213,6 +213,16 @@ pub struct FirmwareState<'d, STATE> {
} }
impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
/// Create a firmware state instance from a FirmwareUpdaterConfig with a buffer for magic content and state partition.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
/// and written to.
pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
Self::new(config.state, aligned)
}
/// Create a firmware state instance with a buffer for magic content and state partition. /// Create a firmware state instance with a buffer for magic content and state partition.
/// ///
/// # Safety /// # Safety

View File

@ -219,6 +219,16 @@ pub struct BlockingFirmwareState<'d, STATE> {
} }
impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
/// Creates a firmware state instance from a FirmwareUpdaterConfig, with a buffer for magic content and state partition.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
/// and written to.
pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
Self::new(config.state, aligned)
}
/// Create a firmware state instance with a buffer for magic content and state partition. /// Create a firmware state instance with a buffer for magic content and state partition.
/// ///
/// # Safety /// # Safety

View File

@ -33,7 +33,7 @@ pub enum State {
Boot, Boot,
/// Bootloader has swapped the active partition with the dfu partition and will attempt boot. /// Bootloader has swapped the active partition with the dfu partition and will attempt boot.
Swap, Swap,
/// Application has received a DFU_DETACH request over USB, and is rebooting into the bootloader to apply a DFU. /// Application has received a request to reboot into DFU mode to apply an update.
DfuDetach, DfuDetach,
} }

View File

@ -1,4 +1,4 @@
use embassy_boot::BlockingFirmwareUpdater; 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};
use embassy_usb::driver::Driver; use embassy_usb::driver::Driver;
@ -11,18 +11,18 @@ use crate::consts::{
}; };
/// Internal state for the DFU class /// Internal state for the DFU class
pub struct Control<'d, DFU: NorFlash, STATE: NorFlash> { pub struct Control<'d, STATE: NorFlash> {
updater: BlockingFirmwareUpdater<'d, DFU, 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>,
} }
impl<'d, DFU: NorFlash, STATE: NorFlash> Control<'d, DFU, STATE> { impl<'d, STATE: NorFlash> Control<'d, STATE> {
pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes) -> Self { pub fn new(firmware_state: BlockingFirmwareState<'d, STATE>, attrs: DfuAttributes) -> Self {
Control { Control {
updater, firmware_state,
attrs, attrs,
state: State::AppIdle, state: State::AppIdle,
detach_start: None, detach_start: None,
@ -31,19 +31,20 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> Control<'d, DFU, STATE> {
} }
} }
impl<'d, DFU: NorFlash, STATE: NorFlash> Handler for Control<'d, DFU, STATE> { impl<'d, STATE: NorFlash> Handler for Control<'d, STATE> {
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;
let timeout = self.timeout.unwrap(); let timeout = self.timeout.unwrap();
#[cfg(feature = "defmt")] trace!(
defmt::info!(
"Received RESET with delta = {}, timeout = {}", "Received RESET with delta = {}, timeout = {}",
delta.as_millis(), delta.as_millis(),
timeout.as_millis() timeout.as_millis()
); );
if delta < timeout { if delta < timeout {
self.updater.mark_dfu().expect("Failed to mark DFU mode in bootloader"); self.firmware_state
.mark_dfu()
.expect("Failed to mark DFU mode in bootloader");
cortex_m::asm::dsb(); cortex_m::asm::dsb();
cortex_m::peripheral::SCB::sys_reset(); cortex_m::peripheral::SCB::sys_reset();
} }
@ -59,13 +60,11 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> Handler for Control<'d, DFU, STATE> {
return None; return None;
} }
#[cfg(feature = "defmt")] trace!("Received request {}", req);
defmt::info!("Received request {}", req);
match Request::try_from(req.request) { match Request::try_from(req.request) {
Ok(Request::Detach) => { Ok(Request::Detach) => {
#[cfg(feature = "defmt")] trace!("Received DETACH, awaiting USB reset");
defmt::info!("Received DETACH, awaiting USB reset");
self.detach_start = Some(Instant::now()); self.detach_start = Some(Instant::now());
self.timeout = Some(Duration::from_millis(req.value as u64)); self.timeout = Some(Duration::from_millis(req.value as u64));
self.state = State::AppDetach; self.state = State::AppDetach;
@ -84,8 +83,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> Handler for Control<'d, DFU, STATE> {
return None; return None;
} }
#[cfg(feature = "defmt")] trace!("Received request {}", req);
defmt::info!("Received request {}", req);
match Request::try_from(req.request) { match Request::try_from(req.request) {
Ok(Request::GetStatus) => { Ok(Request::GetStatus) => {
@ -106,13 +104,11 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> Handler for Control<'d, DFU, 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>, DFU: NorFlash, STATE: NorFlash>( pub fn usb_dfu<'d, D: Driver<'d>, STATE: NorFlash>(
builder: &mut Builder<'d, D>, builder: &mut Builder<'d, D>,
handler: &'d mut Control<'d, DFU, STATE>, handler: &'d mut Control<'d, STATE>,
timeout: Duration, timeout: Duration,
) { ) {
#[cfg(feature = "defmt")]
defmt::info!("Application USB DFU initializing");
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();
let mut alt = iface.alt_setting(USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_RT, None); let mut alt = iface.alt_setting(USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_RT, None);

View File

@ -1,4 +1,4 @@
use embassy_boot::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;
use embassy_usb::{Builder, Handler}; use embassy_usb::{Builder, Handler};
@ -56,8 +56,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Handler for Co
self.offset = 0; self.offset = 0;
} }
let mut buf = [0; BLOCK_SIZE]; let mut buf = AlignedBuffer([0; BLOCK_SIZE]);
buf[..data.len()].copy_from_slice(data); buf.as_mut()[..data.len()].copy_from_slice(data);
if req.length == 0 { if req.length == 0 {
match self.updater.mark_updated() { match self.updater.mark_updated() {
@ -85,7 +85,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Handler for Co
self.state = State::Error; self.state = State::Error;
return Some(OutResponse::Rejected); return Some(OutResponse::Rejected);
} }
match self.updater.write_firmware(self.offset, &buf[..]) { match self.updater.write_firmware(self.offset, buf.as_ref()) {
Ok(_) => { Ok(_) => {
self.status = Status::Ok; self.status = Status::Ok;
self.state = State::DlSync; self.state = State::DlSync;

258
embassy-usb-dfu/src/fmt.rs Normal file
View File

@ -0,0 +1,258 @@
#![macro_use]
#![allow(unused_macros)]
use core::fmt::{Debug, Display, LowerHex};
#[cfg(all(feature = "defmt", feature = "log"))]
compile_error!("You may not enable both `defmt` and `log` features.");
macro_rules! assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert!($($x)*);
}
};
}
macro_rules! assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_eq!($($x)*);
}
};
}
macro_rules! assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_ne!($($x)*);
}
};
}
macro_rules! debug_assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert!($($x)*);
}
};
}
macro_rules! debug_assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_eq!($($x)*);
}
};
}
macro_rules! debug_assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_ne!($($x)*);
}
};
}
macro_rules! todo {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::todo!($($x)*);
#[cfg(feature = "defmt")]
::defmt::todo!($($x)*);
}
};
}
#[cfg(not(feature = "defmt"))]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
};
}
macro_rules! panic {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::panic!($($x)*);
#[cfg(feature = "defmt")]
::defmt::panic!($($x)*);
}
};
}
macro_rules! trace {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::trace!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::trace!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! debug {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::debug!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::debug!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! info {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::info!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::info!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! warn {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::warn!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::warn!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! error {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::error!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::error!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
#[cfg(feature = "defmt")]
macro_rules! unwrap {
($($x:tt)*) => {
::defmt::unwrap!($($x)*)
};
}
#[cfg(not(feature = "defmt"))]
macro_rules! unwrap {
($arg:expr) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
}
}
};
($arg:expr, $($msg:expr),+ $(,)? ) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
}
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NoneError;
pub trait Try {
type Ok;
type Error;
fn into_result(self) -> Result<Self::Ok, Self::Error>;
}
impl<T> Try for Option<T> {
type Ok = T;
type Error = NoneError;
#[inline]
fn into_result(self) -> Result<T, NoneError> {
self.ok_or(NoneError)
}
}
impl<T, E> Try for Result<T, E> {
type Ok = T;
type Error = E;
#[inline]
fn into_result(self) -> Self {
self
}
}
#[allow(unused)]
pub(crate) struct Bytes<'a>(pub &'a [u8]);
impl<'a> Debug for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
impl<'a> Display for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
impl<'a> LowerHex for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
#[cfg(feature = "defmt")]
impl<'a> defmt::Format for Bytes<'a> {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "{:02x}", self.0)
}
}

View File

@ -1,4 +1,5 @@
#![no_std] #![no_std]
mod fmt;
pub mod consts; pub mod consts;

View File

@ -1,9 +0,0 @@
[unstable]
build-std = ["core"]
build-std-features = ["panic_immediate_abort"]
[build]
target = "thumbv7em-none-eabi"
[env]
DEFMT_LOG = "trace"

View File

@ -25,6 +25,7 @@ cortex-m-rt = "0.7.0"
[features] [features]
defmt = [ defmt = [
"dep:defmt", "dep:defmt",
"dep:defmt-rtt",
"embassy-stm32/defmt", "embassy-stm32/defmt",
"embassy-boot-stm32/defmt", "embassy-boot-stm32/defmt",
"embassy-sync/defmt", "embassy-sync/defmt",

View File

@ -26,13 +26,12 @@ fn main() -> ! {
config.rcc = WPAN_DEFAULT; config.rcc = WPAN_DEFAULT;
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
// Uncomment this if you are debugging the bootloader with debugger/RTT attached, // Prevent a hard fault when accessing flash 'too early' after boot.
// as it prevents a hard fault when accessing flash 'too early' after boot. #[cfg(feature = "defmt")]
/* for _ in 0..10000000 {
for i in 0..10000000 { cortex_m::asm::nop();
cortex_m::asm::nop(); }
}
*/
let layout = Flash::new_blocking(p.FLASH).into_blocking_regions(); let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();
let flash = Mutex::new(RefCell::new(layout.bank1_region)); let flash = Mutex::new(RefCell::new(layout.bank1_region));