aboutsummaryrefslogtreecommitdiff
path: root/embassy-usb-dfu/src/application.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-usb-dfu/src/application.rs')
-rw-r--r--embassy-usb-dfu/src/application.rs75
1 files changed, 48 insertions, 27 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}