diff options
| author | Kaitlyn Kenwell <[email protected]> | 2023-12-13 14:53:49 -0500 |
|---|---|---|
| committer | Kaitlyn Kenwell <[email protected]> | 2023-12-13 14:53:49 -0500 |
| commit | c2942f2727739d8972ad211721b1bb1804fb7b4a (patch) | |
| tree | 34ad607895f6cea10ee4f5437e4b8b9f42fabd31 /embassy-usb-dfu | |
| parent | 2afec225e3eddb5738bbc995baf04e13dd1df9e7 (diff) | |
fmt
Diffstat (limited to 'embassy-usb-dfu')
| -rw-r--r-- | embassy-usb-dfu/src/application.rs | 75 | ||||
| -rw-r--r-- | embassy-usb-dfu/src/bootloader.rs | 56 | ||||
| -rw-r--r-- | embassy-usb-dfu/src/consts.rs | 1 | ||||
| -rw-r--r-- | embassy-usb-dfu/src/lib.rs | 5 |
4 files changed, 73 insertions, 64 deletions
diff --git a/embassy-usb-dfu/src/application.rs b/embassy-usb-dfu/src/application.rs index 5a52a9fed..2e7bda121 100644 --- a/embassy-usb-dfu/src/application.rs +++ b/embassy-usb-dfu/src/application.rs | |||
| @@ -1,10 +1,14 @@ | |||
| 1 | |||
| 2 | use embassy_boot::BlockingFirmwareUpdater; | 1 | use embassy_boot::BlockingFirmwareUpdater; |
| 3 | use embassy_time::{Instant, Duration}; | 2 | use embassy_time::{Duration, Instant}; |
| 4 | use embassy_usb::{Handler, control::{RequestType, Recipient, OutResponse, InResponse}, Builder, driver::Driver}; | 3 | use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; |
| 4 | use embassy_usb::driver::Driver; | ||
| 5 | use embassy_usb::{Builder, Handler}; | ||
| 5 | use embedded_storage::nor_flash::NorFlash; | 6 | use embedded_storage::nor_flash::NorFlash; |
| 6 | 7 | ||
| 7 | use crate::consts::{DfuAttributes, Request, Status, State, USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_RT}; | 8 | use crate::consts::{ |
| 9 | DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_RT, | ||
| 10 | USB_CLASS_APPN_SPEC, | ||
| 11 | }; | ||
| 8 | 12 | ||
| 9 | /// Internal state for the DFU class | 13 | /// Internal state for the DFU class |
| 10 | pub struct Control<'d, DFU: NorFlash, STATE: NorFlash> { | 14 | pub struct Control<'d, DFU: NorFlash, STATE: NorFlash> { |
| @@ -17,7 +21,13 @@ pub struct Control<'d, DFU: NorFlash, STATE: NorFlash> { | |||
| 17 | 21 | ||
| 18 | impl<'d, DFU: NorFlash, STATE: NorFlash> Control<'d, DFU, STATE> { | 22 | impl<'d, DFU: NorFlash, STATE: NorFlash> Control<'d, DFU, STATE> { |
| 19 | pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes) -> Self { | 23 | pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes) -> Self { |
| 20 | Control { updater, attrs, state: State::AppIdle, detach_start: None, timeout: None } | 24 | Control { |
| 25 | updater, | ||
| 26 | attrs, | ||
| 27 | state: State::AppIdle, | ||
| 28 | detach_start: None, | ||
| 29 | timeout: None, | ||
| 30 | } | ||
| 21 | } | 31 | } |
| 22 | } | 32 | } |
| 23 | 33 | ||
| @@ -27,7 +37,11 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> Handler for Control<'d, DFU, STATE> { | |||
| 27 | let delta = Instant::now() - start; | 37 | let delta = Instant::now() - start; |
| 28 | let timeout = self.timeout.unwrap(); | 38 | let timeout = self.timeout.unwrap(); |
| 29 | #[cfg(feature = "defmt")] | 39 | #[cfg(feature = "defmt")] |
| 30 | defmt::info!("Received RESET with delta = {}, timeout = {}", delta.as_millis(), timeout.as_millis()); | 40 | defmt::info!( |
| 41 | "Received RESET with delta = {}, timeout = {}", | ||
| 42 | delta.as_millis(), | ||
| 43 | timeout.as_millis() | ||
| 44 | ); | ||
| 31 | if delta < timeout { | 45 | if delta < timeout { |
| 32 | self.updater.mark_dfu().expect("Failed to mark DFU mode in bootloader"); | 46 | self.updater.mark_dfu().expect("Failed to mark DFU mode in bootloader"); |
| 33 | cortex_m::asm::dsb(); | 47 | cortex_m::asm::dsb(); |
| @@ -36,7 +50,11 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> Handler for Control<'d, DFU, STATE> { | |||
| 36 | } | 50 | } |
| 37 | } | 51 | } |
| 38 | 52 | ||
| 39 | fn control_out(&mut self, req: embassy_usb::control::Request, _: &[u8]) -> Option<embassy_usb::control::OutResponse> { | 53 | fn control_out( |
| 54 | &mut self, | ||
| 55 | req: embassy_usb::control::Request, | ||
| 56 | _: &[u8], | ||
| 57 | ) -> Option<embassy_usb::control::OutResponse> { | ||
| 40 | if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) { | 58 | if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) { |
| 41 | return None; | 59 | return None; |
| 42 | } | 60 | } |
| @@ -53,13 +71,15 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> Handler for Control<'d, DFU, STATE> { | |||
| 53 | self.state = State::AppDetach; | 71 | self.state = State::AppDetach; |
| 54 | Some(OutResponse::Accepted) | 72 | Some(OutResponse::Accepted) |
| 55 | } | 73 | } |
| 56 | _ => { | 74 | _ => None, |
| 57 | None | ||
| 58 | } | ||
| 59 | } | 75 | } |
| 60 | } | 76 | } |
| 61 | 77 | ||
| 62 | fn control_in<'a>(&'a mut self, req: embassy_usb::control::Request, buf: &'a mut [u8]) -> Option<embassy_usb::control::InResponse<'a>> { | 78 | fn control_in<'a>( |
| 79 | &'a mut self, | ||
| 80 | req: embassy_usb::control::Request, | ||
| 81 | buf: &'a mut [u8], | ||
| 82 | ) -> Option<embassy_usb::control::InResponse<'a>> { | ||
| 63 | if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) { | 83 | if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) { |
| 64 | return None; | 84 | return None; |
| 65 | } | 85 | } |
| @@ -72,31 +92,30 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> Handler for Control<'d, DFU, STATE> { | |||
| 72 | buf[0..6].copy_from_slice(&[Status::Ok as u8, 0x32, 0x00, 0x00, self.state as u8, 0x00]); | 92 | buf[0..6].copy_from_slice(&[Status::Ok as u8, 0x32, 0x00, 0x00, self.state as u8, 0x00]); |
| 73 | Some(InResponse::Accepted(buf)) | 93 | Some(InResponse::Accepted(buf)) |
| 74 | } | 94 | } |
| 75 | _ => None | 95 | _ => None, |
| 76 | } | 96 | } |
| 77 | } | 97 | } |
| 78 | } | 98 | } |
| 79 | 99 | ||
| 80 | /// An implementation of the USB DFU 1.1 runtime protocol | 100 | /// An implementation of the USB DFU 1.1 runtime protocol |
| 81 | /// | 101 | /// |
| 82 | /// This function will add a DFU interface descriptor to the provided Builder, and register the provided Control as a handler for the USB device. The USB builder can be used as normal once this is complete. | 102 | /// This function will add a DFU interface descriptor to the provided Builder, and register the provided Control as a handler for the USB device. The USB builder can be used as normal once this is complete. |
| 83 | /// The handler is responsive to DFU GetStatus and Detach commands. | 103 | /// The handler is responsive to DFU GetStatus and Detach commands. |
| 84 | /// | 104 | /// |
| 85 | /// Once a detach command, followed by a USB reset is received by the host, a magic number will be written into the bootloader state partition to indicate that | 105 | /// Once a detach command, followed by a USB reset is received by the host, a magic number will be written into the bootloader state partition to indicate that |
| 86 | /// it should expose a DFU device, and a software reset will be issued. | 106 | /// it should expose a DFU device, and a software reset will be issued. |
| 87 | /// | 107 | /// |
| 88 | /// 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. | 108 | /// 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. |
| 89 | pub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash>(builder: &mut Builder<'d, D>, handler: &'d mut Control<'d, DFU, STATE>, timeout: Duration) { | 109 | pub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash>( |
| 110 | builder: &mut Builder<'d, D>, | ||
| 111 | handler: &'d mut Control<'d, DFU, STATE>, | ||
| 112 | timeout: Duration, | ||
| 113 | ) { | ||
| 90 | #[cfg(feature = "defmt")] | 114 | #[cfg(feature = "defmt")] |
| 91 | defmt::info!("Application USB DFU initializing"); | 115 | defmt::info!("Application USB DFU initializing"); |
| 92 | let mut func = builder.function(0x00, 0x00, 0x00); | 116 | let mut func = builder.function(0x00, 0x00, 0x00); |
| 93 | let mut iface = func.interface(); | 117 | let mut iface = func.interface(); |
| 94 | let mut alt = iface.alt_setting( | 118 | let mut alt = iface.alt_setting(USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_RT, None); |
| 95 | USB_CLASS_APPN_SPEC, | ||
| 96 | APPN_SPEC_SUBCLASS_DFU, | ||
| 97 | DFU_PROTOCOL_RT, | ||
| 98 | None, | ||
| 99 | ); | ||
| 100 | let timeout = timeout.as_millis() as u16; | 119 | let timeout = timeout.as_millis() as u16; |
| 101 | alt.descriptor( | 120 | alt.descriptor( |
| 102 | DESC_DFU_FUNCTIONAL, | 121 | DESC_DFU_FUNCTIONAL, |
| @@ -104,11 +123,13 @@ pub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash>(builder: &mut | |||
| 104 | handler.attrs.bits(), | 123 | handler.attrs.bits(), |
| 105 | (timeout & 0xff) as u8, | 124 | (timeout & 0xff) as u8, |
| 106 | ((timeout >> 8) & 0xff) as u8, | 125 | ((timeout >> 8) & 0xff) as u8, |
| 107 | 0x40, 0x00, // 64B control buffer size for application side | 126 | 0x40, |
| 108 | 0x10, 0x01, // DFU 1.1 | 127 | 0x00, // 64B control buffer size for application side |
| 128 | 0x10, | ||
| 129 | 0x01, // DFU 1.1 | ||
| 109 | ], | 130 | ], |
| 110 | ); | 131 | ); |
| 111 | 132 | ||
| 112 | drop(func); | 133 | drop(func); |
| 113 | builder.handler(handler); | 134 | builder.handler(handler); |
| 114 | } \ No newline at end of file | 135 | } |
diff --git a/embassy-usb-dfu/src/bootloader.rs b/embassy-usb-dfu/src/bootloader.rs index 7bcb0b258..215058932 100644 --- a/embassy-usb-dfu/src/bootloader.rs +++ b/embassy-usb-dfu/src/bootloader.rs | |||
| @@ -1,12 +1,13 @@ | |||
| 1 | use embassy_boot::BlockingFirmwareUpdater; | 1 | use embassy_boot::BlockingFirmwareUpdater; |
| 2 | use embassy_usb::{ | 2 | use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; |
| 3 | control::{InResponse, OutResponse, Recipient, RequestType}, | 3 | use embassy_usb::driver::Driver; |
| 4 | driver::Driver, | 4 | use embassy_usb::{Builder, Handler}; |
| 5 | Builder, Handler, | 5 | use embedded_storage::nor_flash::{NorFlash, NorFlashErrorKind}; |
| 6 | }; | ||
| 7 | use embedded_storage::nor_flash::{NorFlashErrorKind, NorFlash}; | ||
| 8 | 6 | ||
| 9 | use crate::consts::{DfuAttributes, Request, State, Status, USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_DFU, DESC_DFU_FUNCTIONAL}; | 7 | use crate::consts::{ |
| 8 | DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_DFU, | ||
| 9 | USB_CLASS_APPN_SPEC, | ||
| 10 | }; | ||
| 10 | 11 | ||
| 11 | /// Internal state for USB DFU | 12 | /// Internal state for USB DFU |
| 12 | pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> { | 13 | pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> { |
| @@ -69,17 +70,11 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Handler for Co | |||
| 69 | match e { | 70 | match e { |
| 70 | embassy_boot::FirmwareUpdaterError::Flash(e) => match e { | 71 | embassy_boot::FirmwareUpdaterError::Flash(e) => match e { |
| 71 | NorFlashErrorKind::NotAligned => self.status = Status::ErrWrite, | 72 | NorFlashErrorKind::NotAligned => self.status = Status::ErrWrite, |
| 72 | NorFlashErrorKind::OutOfBounds => { | 73 | NorFlashErrorKind::OutOfBounds => self.status = Status::ErrAddress, |
| 73 | self.status = Status::ErrAddress | ||
| 74 | } | ||
| 75 | _ => self.status = Status::ErrUnknown, | 74 | _ => self.status = Status::ErrUnknown, |
| 76 | }, | 75 | }, |
| 77 | embassy_boot::FirmwareUpdaterError::Signature(_) => { | 76 | embassy_boot::FirmwareUpdaterError::Signature(_) => self.status = Status::ErrVerify, |
| 78 | self.status = Status::ErrVerify | 77 | embassy_boot::FirmwareUpdaterError::BadState => self.status = Status::ErrUnknown, |
| 79 | } | ||
| 80 | embassy_boot::FirmwareUpdaterError::BadState => { | ||
| 81 | self.status = Status::ErrUnknown | ||
| 82 | } | ||
| 83 | } | 78 | } |
| 84 | } | 79 | } |
| 85 | } | 80 | } |
| @@ -101,17 +96,11 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Handler for Co | |||
| 101 | match e { | 96 | match e { |
| 102 | embassy_boot::FirmwareUpdaterError::Flash(e) => match e { | 97 | embassy_boot::FirmwareUpdaterError::Flash(e) => match e { |
| 103 | NorFlashErrorKind::NotAligned => self.status = Status::ErrWrite, | 98 | NorFlashErrorKind::NotAligned => self.status = Status::ErrWrite, |
| 104 | NorFlashErrorKind::OutOfBounds => { | 99 | NorFlashErrorKind::OutOfBounds => self.status = Status::ErrAddress, |
| 105 | self.status = Status::ErrAddress | ||
| 106 | } | ||
| 107 | _ => self.status = Status::ErrUnknown, | 100 | _ => self.status = Status::ErrUnknown, |
| 108 | }, | 101 | }, |
| 109 | embassy_boot::FirmwareUpdaterError::Signature(_) => { | 102 | embassy_boot::FirmwareUpdaterError::Signature(_) => self.status = Status::ErrVerify, |
| 110 | self.status = Status::ErrVerify | 103 | embassy_boot::FirmwareUpdaterError::BadState => self.status = Status::ErrUnknown, |
| 111 | } | ||
| 112 | embassy_boot::FirmwareUpdaterError::BadState => { | ||
| 113 | self.status = Status::ErrUnknown | ||
| 114 | } | ||
| 115 | } | 104 | } |
| 116 | } | 105 | } |
| 117 | } | 106 | } |
| @@ -162,10 +151,10 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> Handler for Co | |||
| 162 | } | 151 | } |
| 163 | 152 | ||
| 164 | /// An implementation of the USB DFU 1.1 protocol | 153 | /// An implementation of the USB DFU 1.1 protocol |
| 165 | /// | 154 | /// |
| 166 | /// This function will add a DFU interface descriptor to the provided Builder, and register the provided Control as a handler for the USB device | 155 | /// This function will add a DFU interface descriptor to the provided Builder, and register the provided Control as a handler for the USB device |
| 167 | /// The handler is responsive to DFU GetState, GetStatus, Abort, and ClrStatus commands, as well as Download if configured by the user. | 156 | /// The handler is responsive to DFU GetState, GetStatus, Abort, and ClrStatus commands, as well as Download if configured by the user. |
| 168 | /// | 157 | /// |
| 169 | /// Once the host has initiated a DFU download operation, the chunks sent by the host will be written to the DFU partition. | 158 | /// Once the host has initiated a DFU download operation, the chunks sent by the host will be written to the DFU partition. |
| 170 | /// Once the final sync in the manifestation phase has been received, the handler will trigger a system reset to swap the new firmware. | 159 | /// Once the final sync in the manifestation phase has been received, the handler will trigger a system reset to swap the new firmware. |
| 171 | pub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize>( | 160 | pub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize>( |
| @@ -174,20 +163,17 @@ pub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash, const BLOCK_SI | |||
| 174 | ) { | 163 | ) { |
| 175 | let mut func = builder.function(0x00, 0x00, 0x00); | 164 | let mut func = builder.function(0x00, 0x00, 0x00); |
| 176 | let mut iface = func.interface(); | 165 | let mut iface = func.interface(); |
| 177 | let mut alt = iface.alt_setting( | 166 | let mut alt = iface.alt_setting(USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_DFU, None); |
| 178 | USB_CLASS_APPN_SPEC, | ||
| 179 | APPN_SPEC_SUBCLASS_DFU, | ||
| 180 | DFU_PROTOCOL_DFU, | ||
| 181 | None, | ||
| 182 | ); | ||
| 183 | alt.descriptor( | 167 | alt.descriptor( |
| 184 | DESC_DFU_FUNCTIONAL, | 168 | DESC_DFU_FUNCTIONAL, |
| 185 | &[ | 169 | &[ |
| 186 | handler.attrs.bits(), | 170 | handler.attrs.bits(), |
| 187 | 0xc4, 0x09, // 2500ms timeout, doesn't affect operation as DETACH not necessary in bootloader code | 171 | 0xc4, |
| 172 | 0x09, // 2500ms timeout, doesn't affect operation as DETACH not necessary in bootloader code | ||
| 188 | (BLOCK_SIZE & 0xff) as u8, | 173 | (BLOCK_SIZE & 0xff) as u8, |
| 189 | ((BLOCK_SIZE & 0xff00) >> 8) as u8, | 174 | ((BLOCK_SIZE & 0xff00) >> 8) as u8, |
| 190 | 0x10, 0x01, // DFU 1.1 | 175 | 0x10, |
| 176 | 0x01, // DFU 1.1 | ||
| 191 | ], | 177 | ], |
| 192 | ); | 178 | ); |
| 193 | 179 | ||
diff --git a/embassy-usb-dfu/src/consts.rs b/embassy-usb-dfu/src/consts.rs index b083af9de..b359a107e 100644 --- a/embassy-usb-dfu/src/consts.rs +++ b/embassy-usb-dfu/src/consts.rs | |||
| @@ -1,4 +1,3 @@ | |||
| 1 | |||
| 2 | pub(crate) const USB_CLASS_APPN_SPEC: u8 = 0xFE; | 1 | pub(crate) const USB_CLASS_APPN_SPEC: u8 = 0xFE; |
| 3 | pub(crate) const APPN_SPEC_SUBCLASS_DFU: u8 = 0x01; | 2 | pub(crate) const APPN_SPEC_SUBCLASS_DFU: u8 = 0x01; |
| 4 | #[allow(unused)] | 3 | #[allow(unused)] |
diff --git a/embassy-usb-dfu/src/lib.rs b/embassy-usb-dfu/src/lib.rs index dcdb11b2a..81ef041f8 100644 --- a/embassy-usb-dfu/src/lib.rs +++ b/embassy-usb-dfu/src/lib.rs | |||
| @@ -12,5 +12,8 @@ mod application; | |||
| 12 | #[cfg(feature = "application")] | 12 | #[cfg(feature = "application")] |
| 13 | pub use self::application::*; | 13 | pub use self::application::*; |
| 14 | 14 | ||
| 15 | #[cfg(any(all(feature = "bootloader", feature = "application"), not(any(feature = "bootloader", feature = "application"))))] | 15 | #[cfg(any( |
| 16 | all(feature = "bootloader", feature = "application"), | ||
| 17 | not(any(feature = "bootloader", feature = "application")) | ||
| 18 | ))] | ||
| 16 | compile_error!("usb-dfu must be compiled with exactly one of `bootloader`, or `application` features"); | 19 | compile_error!("usb-dfu must be compiled with exactly one of `bootloader`, or `application` features"); |
