mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-09-29 21:31:08 +00:00
Merge pull request #3598 from Gerharddc/main
embassy-dfu-usb: Improve debuggability
This commit is contained in:
commit
9ff8c70009
@ -42,4 +42,4 @@ esp32c3-hal = { version = "0.13.0", optional = true, default-features = false }
|
||||
[features]
|
||||
dfu = []
|
||||
application = []
|
||||
defmt = ["dep:defmt"]
|
||||
defmt = ["dep:defmt", "embassy-boot/defmt", "embassy-usb/defmt"]
|
||||
|
@ -7,30 +7,29 @@ pub(crate) const DFU_PROTOCOL_DFU: u8 = 0x02;
|
||||
pub(crate) const DFU_PROTOCOL_RT: u8 = 0x01;
|
||||
pub(crate) const DESC_DFU_FUNCTIONAL: u8 = 0x21;
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
defmt::bitflags! {
|
||||
pub struct DfuAttributes: u8 {
|
||||
const WILL_DETACH = 0b0000_1000;
|
||||
const MANIFESTATION_TOLERANT = 0b0000_0100;
|
||||
const CAN_UPLOAD = 0b0000_0010;
|
||||
const CAN_DOWNLOAD = 0b0000_0001;
|
||||
}
|
||||
macro_rules! define_dfu_attributes {
|
||||
($macro:path) => {
|
||||
$macro! {
|
||||
/// Attributes supported by the DFU controller.
|
||||
pub struct DfuAttributes: u8 {
|
||||
/// Generate WillDetache sequence on bus.
|
||||
const WILL_DETACH = 0b0000_1000;
|
||||
/// Device can communicate during manifestation phase.
|
||||
const MANIFESTATION_TOLERANT = 0b0000_0100;
|
||||
/// Capable of upload.
|
||||
const CAN_UPLOAD = 0b0000_0010;
|
||||
/// Capable of download.
|
||||
const CAN_DOWNLOAD = 0b0000_0001;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
define_dfu_attributes!(defmt::bitflags);
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
bitflags::bitflags! {
|
||||
/// Attributes supported by the DFU controller.
|
||||
pub struct DfuAttributes: u8 {
|
||||
/// Generate WillDetache sequence on bus.
|
||||
const WILL_DETACH = 0b0000_1000;
|
||||
/// Device can communicate during manifestation phase.
|
||||
const MANIFESTATION_TOLERANT = 0b0000_0100;
|
||||
/// Capable of upload.
|
||||
const CAN_UPLOAD = 0b0000_0010;
|
||||
/// Capable of download.
|
||||
const CAN_DOWNLOAD = 0b0000_0001;
|
||||
}
|
||||
}
|
||||
define_dfu_attributes!(bitflags::bitflags);
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
|
@ -1,6 +1,6 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater};
|
||||
use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterError};
|
||||
use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
|
||||
use embassy_usb::driver::Driver;
|
||||
use embassy_usb::{Builder, Handler};
|
||||
@ -19,6 +19,7 @@ pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_S
|
||||
state: State,
|
||||
status: Status,
|
||||
offset: usize,
|
||||
buf: AlignedBuffer<BLOCK_SIZE>,
|
||||
_rst: PhantomData<RST>,
|
||||
}
|
||||
|
||||
@ -31,6 +32,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Co
|
||||
state: State::DfuIdle,
|
||||
status: Status::Ok,
|
||||
offset: 0,
|
||||
buf: AlignedBuffer([0; BLOCK_SIZE]),
|
||||
_rst: PhantomData,
|
||||
}
|
||||
}
|
||||
@ -42,6 +44,20 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Co
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FirmwareUpdaterError> for Status {
|
||||
fn from(e: FirmwareUpdaterError) -> Self {
|
||||
match e {
|
||||
FirmwareUpdaterError::Flash(e) => match e {
|
||||
NorFlashErrorKind::NotAligned => Status::ErrWrite,
|
||||
NorFlashErrorKind::OutOfBounds => Status::ErrAddress,
|
||||
_ => Status::ErrUnknown,
|
||||
},
|
||||
FirmwareUpdaterError::Signature(_) => Status::ErrVerify,
|
||||
FirmwareUpdaterError::BadState => Status::ErrUnknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Handler
|
||||
for Control<'d, DFU, STATE, RST, BLOCK_SIZE>
|
||||
{
|
||||
@ -51,65 +67,67 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha
|
||||
data: &[u8],
|
||||
) -> Option<embassy_usb::control::OutResponse> {
|
||||
if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) {
|
||||
debug!("Unknown out request: {:?}", req);
|
||||
return None;
|
||||
}
|
||||
match Request::try_from(req.request) {
|
||||
Ok(Request::Abort) => {
|
||||
info!("Abort requested");
|
||||
self.reset_state();
|
||||
Some(OutResponse::Accepted)
|
||||
}
|
||||
Ok(Request::Dnload) if self.attrs.contains(DfuAttributes::CAN_DOWNLOAD) => {
|
||||
if req.value == 0 {
|
||||
info!("Download starting");
|
||||
self.state = State::Download;
|
||||
self.offset = 0;
|
||||
}
|
||||
|
||||
let mut buf = AlignedBuffer([0; BLOCK_SIZE]);
|
||||
buf.as_mut()[..data.len()].copy_from_slice(data);
|
||||
if self.state != State::Download {
|
||||
error!("Unexpected DNLOAD while chip is waiting for a GETSTATUS");
|
||||
self.status = Status::ErrUnknown;
|
||||
self.state = State::Error;
|
||||
return Some(OutResponse::Rejected);
|
||||
}
|
||||
|
||||
if data.len() > BLOCK_SIZE {
|
||||
error!("USB data len exceeded block size");
|
||||
self.status = Status::ErrUnknown;
|
||||
self.state = State::Error;
|
||||
return Some(OutResponse::Rejected);
|
||||
}
|
||||
|
||||
debug!("Copying {} bytes to buffer", data.len());
|
||||
self.buf.as_mut()[..data.len()].copy_from_slice(data);
|
||||
|
||||
let final_transfer = req.length == 0;
|
||||
if final_transfer {
|
||||
debug!("Receiving final transfer");
|
||||
|
||||
if req.length == 0 {
|
||||
match self.updater.mark_updated() {
|
||||
Ok(_) => {
|
||||
self.status = Status::Ok;
|
||||
self.state = State::ManifestSync;
|
||||
info!("Update complete");
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error completing update: {}", e);
|
||||
self.state = State::Error;
|
||||
match e {
|
||||
embassy_boot::FirmwareUpdaterError::Flash(e) => match e {
|
||||
NorFlashErrorKind::NotAligned => self.status = Status::ErrWrite,
|
||||
NorFlashErrorKind::OutOfBounds => self.status = Status::ErrAddress,
|
||||
_ => self.status = Status::ErrUnknown,
|
||||
},
|
||||
embassy_boot::FirmwareUpdaterError::Signature(_) => self.status = Status::ErrVerify,
|
||||
embassy_boot::FirmwareUpdaterError::BadState => self.status = Status::ErrUnknown,
|
||||
}
|
||||
self.status = e.into();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if self.state != State::Download {
|
||||
// Unexpected DNLOAD while chip is waiting for a GETSTATUS
|
||||
self.status = Status::ErrUnknown;
|
||||
self.state = State::Error;
|
||||
return Some(OutResponse::Rejected);
|
||||
}
|
||||
match self.updater.write_firmware(self.offset, buf.as_ref()) {
|
||||
debug!("Writing {} bytes at {}", data.len(), self.offset);
|
||||
match self.updater.write_firmware(self.offset, self.buf.as_ref()) {
|
||||
Ok(_) => {
|
||||
self.status = Status::Ok;
|
||||
self.state = State::DlSync;
|
||||
self.offset += data.len();
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error writing firmware: {:?}", e);
|
||||
self.state = State::Error;
|
||||
match e {
|
||||
embassy_boot::FirmwareUpdaterError::Flash(e) => match e {
|
||||
NorFlashErrorKind::NotAligned => self.status = Status::ErrWrite,
|
||||
NorFlashErrorKind::OutOfBounds => self.status = Status::ErrAddress,
|
||||
_ => self.status = Status::ErrUnknown,
|
||||
},
|
||||
embassy_boot::FirmwareUpdaterError::Signature(_) => self.status = Status::ErrVerify,
|
||||
embassy_boot::FirmwareUpdaterError::BadState => self.status = Status::ErrUnknown,
|
||||
}
|
||||
self.status = e.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -118,6 +136,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha
|
||||
}
|
||||
Ok(Request::Detach) => Some(OutResponse::Accepted), // Device is already in DFU mode
|
||||
Ok(Request::ClrStatus) => {
|
||||
info!("Clear status requested");
|
||||
self.reset_state();
|
||||
Some(OutResponse::Accepted)
|
||||
}
|
||||
@ -131,6 +150,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha
|
||||
buf: &'a mut [u8],
|
||||
) -> Option<embassy_usb::control::InResponse<'a>> {
|
||||
if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) {
|
||||
debug!("Unknown in request: {:?}", req);
|
||||
return None;
|
||||
}
|
||||
match Request::try_from(req.request) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user