aboutsummaryrefslogtreecommitdiff
path: root/embassy-usb-dfu
diff options
context:
space:
mode:
authorKaitlyn Kenwell <[email protected]>2023-12-13 14:53:49 -0500
committerKaitlyn Kenwell <[email protected]>2023-12-13 14:53:49 -0500
commitc2942f2727739d8972ad211721b1bb1804fb7b4a (patch)
tree34ad607895f6cea10ee4f5437e4b8b9f42fabd31 /embassy-usb-dfu
parent2afec225e3eddb5738bbc995baf04e13dd1df9e7 (diff)
fmt
Diffstat (limited to 'embassy-usb-dfu')
-rw-r--r--embassy-usb-dfu/src/application.rs75
-rw-r--r--embassy-usb-dfu/src/bootloader.rs56
-rw-r--r--embassy-usb-dfu/src/consts.rs1
-rw-r--r--embassy-usb-dfu/src/lib.rs5
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
2use embassy_boot::BlockingFirmwareUpdater; 1use embassy_boot::BlockingFirmwareUpdater;
3use embassy_time::{Instant, Duration}; 2use embassy_time::{Duration, Instant};
4use embassy_usb::{Handler, control::{RequestType, Recipient, OutResponse, InResponse}, Builder, driver::Driver}; 3use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
4use embassy_usb::driver::Driver;
5use embassy_usb::{Builder, Handler};
5use embedded_storage::nor_flash::NorFlash; 6use embedded_storage::nor_flash::NorFlash;
6 7
7use crate::consts::{DfuAttributes, Request, Status, State, USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_RT}; 8use 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
10pub struct Control<'d, DFU: NorFlash, STATE: NorFlash> { 14pub struct Control<'d, DFU: NorFlash, STATE: NorFlash> {
@@ -17,7 +21,13 @@ pub struct Control<'d, DFU: NorFlash, STATE: NorFlash> {
17 21
18impl<'d, DFU: NorFlash, STATE: NorFlash> Control<'d, DFU, STATE> { 22impl<'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.
89pub 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) { 109pub 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 @@
1use embassy_boot::BlockingFirmwareUpdater; 1use embassy_boot::BlockingFirmwareUpdater;
2use embassy_usb::{ 2use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
3 control::{InResponse, OutResponse, Recipient, RequestType}, 3use embassy_usb::driver::Driver;
4 driver::Driver, 4use embassy_usb::{Builder, Handler};
5 Builder, Handler, 5use embedded_storage::nor_flash::{NorFlash, NorFlashErrorKind};
6};
7use embedded_storage::nor_flash::{NorFlashErrorKind, NorFlash};
8 6
9use crate::consts::{DfuAttributes, Request, State, Status, USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_DFU, DESC_DFU_FUNCTIONAL}; 7use 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
12pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize> { 13pub 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.
171pub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash, const BLOCK_SIZE: usize>( 160pub 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
2pub(crate) const USB_CLASS_APPN_SPEC: u8 = 0xFE; 1pub(crate) const USB_CLASS_APPN_SPEC: u8 = 0xFE;
3pub(crate) const APPN_SPEC_SUBCLASS_DFU: u8 = 0x01; 2pub(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")]
13pub use self::application::*; 13pub 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))]
16compile_error!("usb-dfu must be compiled with exactly one of `bootloader`, or `application` features"); 19compile_error!("usb-dfu must be compiled with exactly one of `bootloader`, or `application` features");