usbd_dfu/class.rs
1use core::cmp::min;
2use core::marker::PhantomData;
3use usb_device::{class_prelude::*, control::Request};
4
5const USB_CLASS_APPLICATION_SPECIFIC: u8 = 0xFE;
6const USB_SUBCLASS_DFU: u8 = 0x01;
7
8#[allow(dead_code)]
9const USB_PROTOCOL_RUN_TIME: u8 = 0x01;
10const USB_PROTOCOL_DFU_MODE: u8 = 0x02;
11
12#[allow(dead_code)]
13const DFU_DETACH: u8 = 0x00;
14const DFU_DNLOAD: u8 = 0x01;
15const DFU_UPLOAD: u8 = 0x02;
16const DFU_GETSTATUS: u8 = 0x03;
17const DFU_CLRSTATUS: u8 = 0x04;
18const DFU_GETSTATE: u8 = 0x05;
19const DFU_ABORT: u8 = 0x06;
20
21const DESC_DESCTYPE_DFU: u8 = 0x21;
22
23const HAS_READ_UNPROTECT: bool = false;
24
25#[repr(u8)]
26#[derive(Clone, Copy, PartialEq, Eq)]
27enum DFUState {
28 /// Device is running its normal application.
29 #[allow(dead_code)]
30 AppIdle = 0,
31 /// Device is running its normal application, has received the DFU_DETACH request, and is waiting for a USB reset.
32 #[allow(dead_code)]
33 AppDetach = 1,
34 /// Device is operating in the DFU mode and is waiting for requests.
35 DfuIdle = 2,
36 /// Device has received a block and is waiting for the host to solicit the status via DFU_GETSTATUS.
37 DfuDnloadSync = 3,
38 /// Device is programming a control-write block into its nonvolatile memories.
39 DfuDnBusy = 4,
40 /// Device is processing a download operation. Expecting DFU_DNLOAD requests.
41 DfuDnloadIdle = 5,
42 /// Device has received the final block of firmware from the hostand is waiting for receipt of DFU_GETSTATUS to begin the Manifestation phase; or device has completed the Manifestation phase and is waiting for receipt of DFU_GETSTATUS. (Devices that can enter this state after the Manifestation phase set bmAttributes bit bitManifestationTolerant to 1.)
43 DfuManifestSync = 6,
44 /// Device is in the Manifestation phase. (Not all devices will be able to respond to DFU_GETSTATUS when in this state.)
45 DfuManifest = 7,
46 /// Device has programmed its memories and is waiting for a USB reset or a power on reset. (Devices that must enter this state clear bitManifestationTolerant to 0.)
47 DfuManifestWaitReset = 8,
48 /// The device is processing an upload operation. Expecting DFU_UPLOAD requests.
49 DfuUploadIdle = 9,
50 /// An error has occurred. Awaiting the DFU_CLRSTATUS request.
51 DfuError = 10,
52}
53
54#[repr(u8)]
55#[derive(Clone, Copy, PartialEq, Eq)]
56enum DFUStatusCode {
57 /// No error condition is present.
58 OK = 0x00,
59 /// File is not targeted for use by this device.
60 ErrTarget = 0x01,
61 /// File is for this device but fails some vendor-specific verification test.
62 ErrFile = 0x02,
63 /// Device is unable to write memory.
64 ErrWrite = 0x03,
65 /// Memory erase function failed.
66 ErrErase = 0x04,
67 /// Memory erase check failed.
68 ErrCheckErased = 0x05,
69 /// Program memory function failed.
70 ErrProg = 0x06,
71 /// Programmed memory failed verification.
72 ErrVerify = 0x07,
73 /// Cannot program memory due to received address that is out of range.
74 ErrAddress = 0x08,
75 /// Received DFU_DNLOAD with wLength = 0, but device does not think it has all of the data yet.
76 ErrNotdone = 0x09,
77 /// Device’s firmware is corrupt. It cannot return to run-time (non-DFU) operations.
78 ErrFirmware = 0x0A,
79 /// iString indicates a vendor-specific error.
80 ErrVendor = 0x0B,
81 /// Device detected unexpected USB reset signaling.
82 ErrUsbr = 0x0C,
83 /// Device detected unexpected power on reset.
84 ErrPOR = 0x0D,
85 /// Something went wrong, but the device does not know what it was.
86 ErrUnknown = 0x0E,
87 /// Device stalled an unexpected request.
88 ErrStalledPkt = 0x0F,
89}
90
91#[repr(u8)]
92enum DnloadCommand {
93 GetCommands = 0x00,
94 SetAddressPointer = 0x21,
95 Erase = 0x41,
96 ReadUnprotect = 0x92,
97}
98
99/// Errors that may happen when working with the memory
100/// (reading, erasing, writting). These will be translated
101/// to a corresponding error codes in DFU protocol.
102#[repr(u8)]
103pub enum DFUMemError {
104 /// File is not targeted for use by this device.
105 Target = DFUStatusCode::ErrTarget as u8,
106 /// File is for this device but fails some vendor-specific verification test.
107 File = DFUStatusCode::ErrFile as u8,
108 /// Device is unable to write memory.
109 Write = DFUStatusCode::ErrWrite as u8,
110 /// Memory erase function failed.
111 Erase = DFUStatusCode::ErrErase as u8,
112 /// Memory erase check failed.
113 CheckErased = DFUStatusCode::ErrCheckErased as u8,
114 /// Program memory function failed.
115 Prog = DFUStatusCode::ErrProg as u8,
116 /// Programmed memory failed verification.
117 Verify = DFUStatusCode::ErrVerify as u8,
118 /// Something went wrong, but the device does not know what it was.
119 Unknown = DFUStatusCode::ErrUnknown as u8,
120 /// Cannot program memory due to received address that is out of range.
121 Address = DFUStatusCode::ErrAddress as u8,
122 /// A vendor-specific error. iString in DFU_GETSTATUS reply will always be 0.
123 ErrVendor = DFUStatusCode::ErrVendor as u8,
124}
125
126/// Errors that may happen when device enter Manifestation phase
127#[repr(u8)]
128pub enum DFUManifestationError {
129 /// File is not targeted for use by this device.
130 Target = DFUStatusCode::ErrTarget as u8,
131 /// File is for this device but fails some vendor-specific verification test.
132 File = DFUStatusCode::ErrFile as u8,
133 /// Received DFU_DNLOAD with wLength = 0, but device does not think it has all of the data yet.
134 NotDone = DFUStatusCode::ErrNotdone as u8,
135 /// Device’s firmware is corrupt. It cannot return to run-time (non-DFU) operations.
136 Firmware = DFUStatusCode::ErrFirmware as u8,
137 /// A vendor-specific error. iString in DFU_GETSTATUS reply will always be 0.
138 ErrVendor = DFUStatusCode::ErrVendor as u8,
139 /// Something went wrong, but the device does not know what it was.
140 Unknown = DFUStatusCode::ErrUnknown as u8,
141}
142
143/// Trait that describes the abstraction used to access memory on a device. [`DFUClass`] will call corresponding
144/// functions and will use provided constants to tailor DFU features and, for example time interval values that
145/// are used in the protocol.
146///
147/// In this context we use "page" to mean the smallest region of flash memory which can be erased, and "block"
148/// to mean an amount of memory which is to be used for reading or programming.
149pub trait DFUMemIO {
150 /// Specifies the default value of Address Pointer
151 ///
152 /// Usually, it's start address of a memory region.
153 ///
154 const INITIAL_ADDRESS_POINTER: u32;
155
156 /// Specifies USB interface descriptor string. It should describe a memory region this interface works with.
157 ///
158 /// *Disclaimer*: I haven't found the specification, this is what it looks like from dfu-util sources.
159 ///
160 /// The string is formatted as follows:
161 ///
162 /// @ *name*/*address*/*area*[,*area*...]
163 ///
164 /// > `@` (at sign), `/` (slash), `,` (coma) symbols
165 ///
166 /// > *name* - Region name, e.g. "Flash"
167 ///
168 /// > *address* - Memory address of a regions, e.g. "0x08000000"
169 ///
170 /// > *area* - count of pages, page size, and supported operations for the region, e.g. 8*1Ke - 8 pages of 1024 bytes, available for reading and writing.
171 ///
172 /// Page size supports these suffixes: **K**, **M**, **G**, or ` ` (space) for bytes.
173 ///
174 /// And a letter that specifies region's supported operation:
175 ///
176 /// | letter | Read | Erase | Write |
177 /// |--------|------|-------|-------|
178 /// | **a** | + | | |
179 /// | **b** | | + | |
180 /// | **c** | + | + | |
181 /// | **d** | | | + |
182 /// | **e** | + | | + |
183 /// | **f** | | + | + |
184 /// | **g** | + | + | + |
185 ///
186 /// For example:
187 /// ```text
188 /// @Flash/0x08000000/16*1Ka,48*1Kg
189 /// ```
190 ///
191 /// Denotes a memory region named "Flash", with a starting address `0x08000000`,
192 /// the first 16 pages with a size 1K are available only for reading, and the next
193 /// 48 1K-pages are avaiable for reading, erase, and write operations.
194 const MEM_INFO_STRING: &'static str;
195
196 /// If set, DFU descriptor will have *bitCanDnload* bit set. Default is `true`.
197 ///
198 /// Should be set to true if firmware download (host to device) is supported.
199 const HAS_DOWNLOAD: bool = true;
200
201 /// If set, DFU descriptor will have *bitCanUpload* bit set. Default is `true`.
202 ///
203 /// Should be set to true if firmware upload (device to host) is supported.
204 const HAS_UPLOAD: bool = true;
205
206 /// If set, DFU descriptor will have *bitManifestationTolerant* bit set. Default is `true`.
207 ///
208 /// See also [`MANIFESTATION_TIME_MS`](DFUMemIO::MANIFESTATION_TIME_MS).
209 const MANIFESTATION_TOLERANT: bool = true;
210
211 // /// Remove device's flash read protection. This operation should erase
212 // /// memory contents.
213 // const HAS_READ_UNPROTECT : bool = false;
214
215 /// Time in milliseconds host must wait before issuing the next command after
216 /// block program request.
217 ///
218 /// This is the time that program of one block or [`TRANSFER_SIZE`](DFUMemIO::TRANSFER_SIZE) bytes
219 /// takes.
220 ///
221 /// DFU programs data as follows:
222 ///
223 /// > 1. Host transfers `TRANSFER_SIZE` bytes to a device
224 /// > 2. Device stores this data in a buffer
225 /// > 3. Host issues `DFU_GETSTATUS` command, confirms that device state is correct,
226 /// > and checks the reply for 24-bit value how much time it must wait
227 /// > before issuing the next command. Device, after submitting a reply
228 /// > starts program operation.
229 /// > 4. After waiting for a specified number of milliseconds, host continues to send new commands.
230 const PROGRAM_TIME_MS: u32;
231
232 /// Similar to [`PROGRAM_TIME_MS`](DFUMemIO::PROGRAM_TIME_MS), but for a page erase operation.
233 const ERASE_TIME_MS: u32;
234
235 /// Similar to [`PROGRAM_TIME_MS`](DFUMemIO::PROGRAM_TIME_MS), but for a full erase operation.
236 const FULL_ERASE_TIME_MS: u32;
237
238 /// Time in milliseconds host must wait after submitting the final firware download
239 /// (host to device) command. Default is `1` ms.
240 ///
241 /// DFU protocol allows the device to enter a Manifestation state when it can activate
242 /// the uploaded firmware.
243 ///
244 /// After the activation is completed, device may need to reset (if
245 /// [`MANIFESTATION_TOLERANT`](DFUMemIO::MANIFESTATION_TOLERANT) is `false`), or it can return to IDLE state
246 /// (if `MANIFESTATION_TOLERANT` is `true`)
247 ///
248 /// See also [`PROGRAM_TIME_MS`](DFUMemIO::PROGRAM_TIME_MS).
249 const MANIFESTATION_TIME_MS: u32 = 1;
250
251 /// wDetachTimeOut field in DFU descriptor. Default value: `250` ms.
252 ///
253 /// Probably unused if device does not support DFU in run-time mode to
254 /// handle `DFU_DETACH` command.
255 ///
256 /// Time in milliseconds that device will wait after receipt of `DFU_DETACH` request
257 /// if USB reset request is not received before reverting to a normal operation.
258 const DETACH_TIMEOUT: u16 = 250;
259
260 /// Expected transfer size. Default value: `128` bytes.
261 ///
262 /// This is the maximum size of a block for [`read()`](DFUMemIO::read) and [`program()`](DFUMemIO::program) functions.
263 ///
264 /// This also sets `wTransferSize` in DFU Functional descriptor.
265 ///
266 /// Host should use exactly this transfer size for all blocks except for the last one.
267 /// The last block can be shorter. If transfer size is too large, Host may choose
268 /// to use a smaller block size, in this case firmware may be written incorrectly
269 /// if Host is not using `Set Address Pointer` command.
270 ///
271 /// All DFU transfers use Control endpoint only.
272 ///
273 /// **Warning**: must be less or equal of `usb-device`'s control endpoint buffer size (usually `128` bytes),
274 /// otherwise data transfers may fail for no obvious reason.
275 const TRANSFER_SIZE: u16 = 128;
276
277 // /// Not supported, implementation would probably need some
278 // /// non-trivial locking.
279 // const MEMIO_IN_USB_INTERRUPT: bool = true;
280
281 /// Collect data which comes from USB, possibly in chunks, to a buffer in RAM.
282 ///
283 /// [`DFUClass`] does not have an internal memory buffer for a read/write operations,
284 /// incoming data should be stored in a buffer managed by this trait's implementation.
285 ///
286 /// This function should not write data to Flash or trigger memory Erase.
287 ///
288 /// The same buffer may be shared for both write and read operations.
289 /// DFU protocol will not trigger block write while sending data to host, and
290 /// will ensure that buffer has valid data before program operation is requested.
291 ///
292 /// This function is called from `usb_dev.poll([])` (USB interrupt context).
293 ///
294 fn store_write_buffer(&mut self, src: &[u8]) -> Result<(), ()>;
295
296 /// Read memory and return it to device.
297 ///
298 /// If Upload operation is supported ([`HAS_UPLOAD`](DFUMemIO::HAS_UPLOAD) is `true`), this function
299 /// returns memory contents to a host.
300 ///
301 /// Implementation must check that address is in a target region and that the
302 /// whole block fits in this region too.
303 ///
304 /// This function is called from `usb_dev.poll([])` (USB interrupt context).
305 ///
306 fn read(&mut self, address: u32, length: usize) -> Result<&[u8], DFUMemError>;
307
308 /// Trigger block program.
309 ///
310 /// Implementation must check that address is in a target region and that the
311 /// whole block fits in this region too.
312 ///
313 /// This function is called from `usb_dev.poll([])` (USB interrupt context).
314 // / This function by default is called from USB interrupt context, depending on
315 // / [`MEMIO_IN_USB_INTERRUPT`](DFUMemIO::MEMIO_IN_USB_INTERRUPT) value.
316 ///
317 fn program(&mut self, address: u32, length: usize) -> Result<(), DFUMemError>;
318
319 /// Trigger page erase.
320 ///
321 /// Implementation must ensure that address is valid, or return an error.
322 ///
323 /// This function is called from `usb_dev.poll([])` (USB interrupt context).
324 // / This function by default is called from USB interrupt context, depending on
325 // / [`MEMIO_IN_USB_INTERRUPT`](DFUMemIO::MEMIO_IN_USB_INTERRUPT) value.
326 ///
327 fn erase(&mut self, address: u32) -> Result<(), DFUMemError>;
328
329 /// Trigger full erase.
330 ///
331 /// This function is called from `usb_dev.poll([])` (USB interrupt context).
332 // / This function by default is called from USB interrupt context, depending on
333 // / [`MEMIO_IN_USB_INTERRUPT`](DFUMemIO::MEMIO_IN_USB_INTERRUPT) value.
334 ///
335 fn erase_all(&mut self) -> Result<(), DFUMemError>;
336
337 /// Finish writing firmware to a persistent storage, and optionally activate it.
338 ///
339 /// This funciton should return if [`MANIFESTATION_TOLERANT`](DFUMemIO::MANIFESTATION_TOLERANT) is `true`.
340 ///
341 /// This funciton should not return `Ok()` if `MANIFESTATION_TOLERANT` is `false`.
342 /// Instead device should activate and start new main firmware.
343 ///
344 /// This function is called from `usb_dev.poll([])` (USB interrupt context).
345 // / This function by default is called from USB interrupt context, depending on
346 // / [`MEMIO_IN_USB_INTERRUPT`](DFUMemIO::MEMIO_IN_USB_INTERRUPT) value.
347 ///
348 fn manifestation(&mut self) -> Result<(), DFUManifestationError>;
349
350 /// Called every time when USB is reset.
351 ///
352 /// After firmware update is done, device should switch to an application
353 /// firmware if it's possible and this function should not return.
354 ///
355 /// Handler will need to distinguish between actual host resets and
356 /// when the device connects the first time at startup to avoid
357 /// device reset and revert to main firmware at boot.
358 ///
359 /// If firmware is corrupt, this funciton should return and DFU will switch
360 /// to ERROR state so host could try to recover. This is the default.
361 ///
362 /// This function is called from `usb_dev.poll([])` (USB interrupt context).
363 ///
364 fn usb_reset(&mut self) {}
365}
366
367impl From<DFUMemError> for DFUStatusCode {
368 fn from(e: DFUMemError) -> Self {
369 match e {
370 DFUMemError::File => DFUStatusCode::ErrFile,
371 DFUMemError::Target => DFUStatusCode::ErrTarget,
372 DFUMemError::Address => DFUStatusCode::ErrAddress,
373 DFUMemError::CheckErased => DFUStatusCode::ErrCheckErased,
374 DFUMemError::Erase => DFUStatusCode::ErrErase,
375 DFUMemError::Write => DFUStatusCode::ErrWrite,
376 DFUMemError::Prog => DFUStatusCode::ErrProg,
377 DFUMemError::Verify => DFUStatusCode::ErrVerify,
378 DFUMemError::Unknown => DFUStatusCode::ErrUnknown,
379 DFUMemError::ErrVendor => DFUStatusCode::ErrVendor,
380 }
381 }
382}
383
384impl From<DFUManifestationError> for DFUStatusCode {
385 fn from(e: DFUManifestationError) -> Self {
386 match e {
387 DFUManifestationError::NotDone => DFUStatusCode::ErrNotdone,
388 DFUManifestationError::Firmware => DFUStatusCode::ErrFirmware,
389 DFUManifestationError::Unknown => DFUStatusCode::ErrUnknown,
390 DFUManifestationError::ErrVendor => DFUStatusCode::ErrVendor,
391 DFUManifestationError::File => DFUStatusCode::ErrFile,
392 DFUManifestationError::Target => DFUStatusCode::ErrTarget,
393 }
394 }
395}
396
397/// DFU protocol USB class implementation for usb-device library.
398pub struct DFUClass<B: UsbBus, M: DFUMemIO> {
399 if_num: InterfaceNumber,
400 status: DFUStatus,
401 interface_string: StringIndex,
402 _bus: PhantomData<B>,
403 mem: M,
404}
405
406#[derive(Clone, Copy, PartialEq, Eq)]
407enum Command {
408 None,
409 EraseAll,
410 Erase(u32),
411 SetAddressPointer(u32),
412 ReadUnprotect,
413 WriteMemory { block_num: u16, len: u16 },
414 LeaveDFU,
415}
416
417#[derive(Clone, Copy)]
418struct DFUStatus {
419 status: DFUStatusCode,
420 poll_timeout: u32,
421 state: DFUState,
422 address_pointer: u32,
423 command: Command,
424 pending: Command,
425}
426
427impl DFUStatus {
428 pub fn new(addr: u32) -> Self {
429 Self {
430 status: DFUStatusCode::OK,
431 poll_timeout: 0,
432 state: DFUState::DfuIdle,
433 address_pointer: addr,
434 command: Command::None,
435 pending: Command::None,
436 }
437 }
438
439 fn new_state_ok(&mut self, state: DFUState) {
440 self.new_state_status(state, DFUStatusCode::OK);
441 }
442
443 fn new_state_status(&mut self, state: DFUState, status: DFUStatusCode) {
444 self.status = status;
445 self.state = state;
446 }
447
448 fn state(&self) -> DFUState {
449 self.state
450 }
451}
452
453impl From<DFUStatus> for [u8; 6] {
454 fn from(dfu: DFUStatus) -> Self {
455 [
456 // bStatus
457 dfu.status as u8,
458 // bwPollTimeout
459 (dfu.poll_timeout & 0xff) as u8,
460 ((dfu.poll_timeout >> 8) & 0xff) as u8,
461 ((dfu.poll_timeout >> 16) & 0xff) as u8,
462 // bState
463 dfu.state as u8,
464 // iString: Index of status description in string table.
465 0,
466 ]
467 }
468}
469
470impl<B: UsbBus, M: DFUMemIO> UsbClass<B> for DFUClass<B, M> {
471 fn get_configuration_descriptors(
472 &self,
473 writer: &mut DescriptorWriter,
474 ) -> usb_device::Result<()> {
475 writer.interface_alt(
476 self.if_num,
477 0,
478 USB_CLASS_APPLICATION_SPECIFIC,
479 USB_SUBCLASS_DFU,
480 USB_PROTOCOL_DFU_MODE,
481 Some(self.interface_string),
482 )?;
483
484 // DFU Functional descriptor
485 writer.write(
486 DESC_DESCTYPE_DFU,
487 &[
488 // bmAttributes
489 // Bit 7: bitAcceleratedST
490 (if false {0x80} else {0}) |
491 // Bit 4-6: Reserved
492 // Bit 3: bitWillDetach
493 (if true {0x8} else {0}) |
494 // Bit 2: bitManifestationTolerant
495 (if M::MANIFESTATION_TOLERANT {0x4} else {0}) |
496 // Bit 1: bitCanUpload
497 (if M::HAS_UPLOAD {0x2} else {0}) |
498 // Bit 0: bitCanDnload
499 (if M::HAS_DOWNLOAD {0x1} else {0}),
500 // wDetachTimeOut
501 (M::DETACH_TIMEOUT & 0xff) as u8,
502 (M::DETACH_TIMEOUT >> 8) as u8,
503 // wTransferSize
504 (M::TRANSFER_SIZE & 0xff) as u8,
505 (M::TRANSFER_SIZE >> 8) as u8,
506 // bcdDFUVersion
507 0x1a,
508 0x01,
509 ],
510 )?;
511
512 //
513
514 Ok(())
515 }
516
517 fn get_string(&self, index: StringIndex, lang_id: LangID) -> Option<&str> {
518 if index == self.interface_string && (lang_id == LangID::EN_US || u16::from(lang_id) == 0) {
519 return Some(M::MEM_INFO_STRING);
520 }
521 None
522 }
523
524 // Handle control requests to the host.
525 fn control_in(&mut self, xfer: ControlIn<B>) {
526 let req = *xfer.request();
527
528 if req.request_type != control::RequestType::Class {
529 return;
530 }
531
532 if req.recipient != control::Recipient::Interface {
533 return;
534 }
535
536 if req.index != u8::from(self.if_num) as u16 {
537 return;
538 }
539
540 match req.request {
541 DFU_UPLOAD => {
542 self.upload(xfer, req);
543 }
544 DFU_GETSTATUS => {
545 self.get_status(xfer, req);
546 }
547 DFU_GETSTATE => {
548 self.get_state(xfer, req);
549 }
550 _ => {
551 xfer.reject().ok();
552 }
553 }
554 }
555
556 // Handle a control request from the host.
557 fn control_out(&mut self, xfer: ControlOut<B>) {
558 let req = *xfer.request();
559
560 if req.request_type != control::RequestType::Class {
561 return;
562 }
563
564 if req.recipient != control::Recipient::Interface {
565 return;
566 }
567
568 if req.index != u8::from(self.if_num) as u16 {
569 return;
570 }
571
572 match req.request {
573 //DFU_DETACH => {},
574 DFU_DNLOAD => {
575 self.download(xfer, req);
576 }
577 DFU_CLRSTATUS => {
578 self.clear_status(xfer);
579 }
580 DFU_ABORT => {
581 self.abort(xfer);
582 }
583 _ => {
584 xfer.reject().ok();
585 }
586 }
587 }
588
589 fn reset(&mut self) {
590 // may not return
591 self.mem.usb_reset();
592
593 // Try to signal possible error to a host.
594 // Not exactly clear what status should be.
595 match self.status.state() {
596 DFUState::DfuUploadIdle
597 | DFUState::DfuDnloadIdle
598 | DFUState::DfuDnloadSync
599 | DFUState::DfuDnBusy
600 | DFUState::DfuError
601 | DFUState::DfuManifest
602 | DFUState::DfuManifestSync => {
603 self.status
604 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrUsbr);
605 }
606 DFUState::DfuIdle
607 | DFUState::AppDetach
608 | DFUState::AppIdle
609 | DFUState::DfuManifestWaitReset => {}
610 }
611 }
612
613 fn poll(&mut self) {
614 self.update_impl();
615 }
616}
617
618impl<B: UsbBus, M: DFUMemIO> DFUClass<B, M> {
619 /// Creates a new DFUClass with the provided UsbBus and
620 /// DFUMemIO
621 pub fn new(alloc: &UsbBusAllocator<B>, mem: M) -> Self {
622 Self {
623 if_num: alloc.interface(),
624 status: DFUStatus::new(M::INITIAL_ADDRESS_POINTER),
625 interface_string: alloc.string(),
626 _bus: PhantomData,
627 mem,
628 }
629 }
630
631 /// This function will consume self and return the owned memory
632 /// argument that was moved in the call to new()
633 pub fn release(self) -> M {
634 self.mem
635 }
636
637 /// This function may be called just after `DFUClass::new()` to
638 /// set DFU error state to "Device detected unexpected power on reset"
639 /// instead of the usual `dfuIdle`.
640 pub fn set_unexpected_reset_state(&mut self) {
641 self.status
642 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrPOR);
643 }
644
645 /// This function may be called just after `DFUClass::new()` to
646 /// set DFU error state to "Device’s firmware is corrupt. It cannot return to run-time (non-DFU) operations"
647 /// instead of the usual `dfuIdle`.
648 pub fn set_firmware_corrupted_state(&mut self) {
649 self.status
650 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrFirmware);
651 }
652
653 /// Return current Address Pointer value.
654 pub fn get_address_pointer(&self) -> u32 {
655 self.status.address_pointer
656 }
657
658 fn clear_status(&mut self, xfer: ControlOut<B>) {
659 match self.status.state() {
660 DFUState::DfuError => {
661 self.status.command = Command::None;
662 self.status.pending = Command::None;
663 self.status.new_state_ok(DFUState::DfuIdle);
664 xfer.accept().ok();
665 }
666 _ => {
667 self.status
668 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrStalledPkt);
669 xfer.reject().ok();
670 }
671 }
672 }
673
674 fn abort(&mut self, xfer: ControlOut<B>) {
675 match self.status.state() {
676 DFUState::DfuIdle
677 | DFUState::DfuUploadIdle
678 | DFUState::DfuDnloadIdle
679 | DFUState::DfuDnloadSync
680 | DFUState::DfuManifestSync => {
681 self.status.command = Command::None;
682 self.status.pending = Command::None;
683 self.status.new_state_ok(DFUState::DfuIdle);
684 xfer.accept().ok();
685 }
686 DFUState::AppDetach
687 | DFUState::AppIdle
688 | DFUState::DfuDnBusy
689 | DFUState::DfuManifest
690 | DFUState::DfuManifestWaitReset
691 | DFUState::DfuError => {
692 xfer.reject().ok();
693 }
694 }
695 }
696
697 fn download(&mut self, xfer: ControlOut<B>, req: Request) {
698 let initial_state = self.status.state();
699
700 if initial_state != DFUState::DfuIdle && initial_state != DFUState::DfuDnloadIdle {
701 self.status
702 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrStalledPkt);
703 xfer.reject().ok();
704 return;
705 }
706
707 if req.length == 0 {
708 self.status.command = Command::LeaveDFU;
709 self.status.new_state_ok(DFUState::DfuManifestSync);
710 xfer.accept().ok();
711 return;
712 }
713
714 if req.value > 1 {
715 let data = xfer.data();
716 if !data.is_empty() {
717 // store the whole buffer, chunked operation in not supported
718 match self.mem.store_write_buffer(data) {
719 Err(_) => {
720 self.status
721 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrStalledPkt);
722 xfer.reject().ok();
723 }
724 Ok(_) => {
725 let block_num = req.value - 2;
726 self.status.command = Command::WriteMemory {
727 block_num,
728 len: data.len() as u16,
729 };
730 self.status.new_state_ok(DFUState::DfuDnloadSync);
731 xfer.accept().ok();
732 }
733 }
734 return;
735 }
736 } else if req.value == 0 {
737 let data = xfer.data();
738 if req.length >= 1 {
739 let command = data[0];
740
741 if command == DnloadCommand::SetAddressPointer as u8 {
742 if req.length == 5 {
743 let addr = (data[1] as u32)
744 | ((data[2] as u32) << 8)
745 | ((data[3] as u32) << 16)
746 | ((data[4] as u32) << 24);
747 self.status.command = Command::SetAddressPointer(addr);
748 self.status.new_state_ok(DFUState::DfuDnloadSync);
749 xfer.accept().ok();
750 return;
751 }
752 } else if command == DnloadCommand::Erase as u8 {
753 if req.length == 5 {
754 let addr = (data[1] as u32)
755 | ((data[2] as u32) << 8)
756 | ((data[3] as u32) << 16)
757 | ((data[4] as u32) << 24);
758 self.status.command = Command::Erase(addr);
759 self.status.new_state_ok(DFUState::DfuDnloadSync);
760 xfer.accept().ok();
761 return;
762 } else if req.length == 1 {
763 self.status.command = Command::EraseAll;
764 self.status.new_state_ok(DFUState::DfuDnloadSync);
765 xfer.accept().ok();
766 return;
767 }
768 } else if HAS_READ_UNPROTECT && command == DnloadCommand::ReadUnprotect as u8 {
769 self.status.command = Command::ReadUnprotect;
770 self.status.new_state_ok(DFUState::DfuDnloadSync);
771 xfer.accept().ok();
772 return;
773 }
774 }
775 }
776
777 self.status
778 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrStalledPkt);
779 xfer.reject().ok();
780 }
781
782 fn upload(&mut self, xfer: ControlIn<B>, req: Request) {
783 let initial_state = self.status.state();
784
785 if initial_state != DFUState::DfuIdle && initial_state != DFUState::DfuUploadIdle {
786 self.status
787 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrStalledPkt);
788 xfer.reject().ok();
789 return;
790 }
791
792 if req.value == 0 {
793 // Get command
794 let commands = [
795 DnloadCommand::GetCommands as u8,
796 DnloadCommand::SetAddressPointer as u8,
797 DnloadCommand::Erase as u8,
798 // XXX read unprotect
799 ];
800
801 if req.length as usize >= commands.len() {
802 self.status.new_state_ok(DFUState::DfuIdle);
803 xfer.accept_with(&commands).ok();
804 return;
805 }
806 } else if req.value > 1 {
807 // upload command
808 let block_num = req.value - 2;
809 let transfer_size = min(M::TRANSFER_SIZE, req.length);
810
811 if let Some(address) = self
812 .status
813 .address_pointer
814 .checked_add((block_num as u32) * (M::TRANSFER_SIZE as u32))
815 {
816 match self.mem.read(address, transfer_size as usize) {
817 Ok(b) => {
818 if b.len() < M::TRANSFER_SIZE as usize {
819 // short frame, back to idle
820 self.status.new_state_ok(DFUState::DfuIdle);
821 } else {
822 self.status.new_state_ok(DFUState::DfuUploadIdle);
823 }
824 xfer.accept_with(b).ok();
825 return;
826 }
827 Err(e) => {
828 self.status.new_state_status(DFUState::DfuError, e.into());
829 xfer.reject().ok();
830 return;
831 }
832 }
833 } else {
834 // overflow
835 self.status
836 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrAddress);
837 xfer.reject().ok();
838 return;
839 }
840 }
841
842 self.status
843 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrStalledPkt);
844 xfer.reject().ok();
845 }
846
847 fn get_state(&mut self, xfer: ControlIn<B>, req: Request) {
848 // return current state, without any state transition
849 if req.length > 0 {
850 let v = self.status.state() as u8;
851 xfer.accept_with(&[v]).ok();
852 } else {
853 self.status
854 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrStalledPkt);
855 xfer.reject().ok();
856 }
857 }
858
859 fn get_status(&mut self, xfer: ControlIn<B>, req: Request) {
860 if req.length >= 6 && self.process() {
861 self.status.poll_timeout = self.expected_timeout();
862 let v: [u8; 6] = self.status.into();
863 xfer.accept_with(&v).ok();
864 return;
865 }
866
867 self.status
868 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrStalledPkt);
869 xfer.reject().ok();
870 }
871
872 fn expected_timeout(&self) -> u32 {
873 match self.status.pending {
874 Command::WriteMemory {
875 block_num: _,
876 len: _,
877 } => M::PROGRAM_TIME_MS,
878 Command::EraseAll => M::FULL_ERASE_TIME_MS,
879 Command::Erase(_) => M::ERASE_TIME_MS,
880 Command::LeaveDFU => M::MANIFESTATION_TIME_MS,
881 _ => 0,
882 }
883 }
884
885 // ///
886 // /// Handle some DFU state transitions, and call `DFUMemIO`'s erase, program,
887 // /// and manifestation functions.
888 // ///
889 // /// This function will be called internally by if [`M::MEMIO_IN_USB_INTERRUPT`](DFUMemIO::MEMIO_IN_USB_INTERRUPT)
890 // /// is `true` (default) as one of a final steps of `usb_dev.poll([...])` which is itself usually called
891 // /// from USB interrupt.
892 // ///
893 // /// This function must be called if [`M::MEMIO_IN_USB_INTERRUPT`](DFUMemIO::MEMIO_IN_USB_INTERRUPT) is `false`
894 // /// and erase, program, and manifestation should be called from a different context than `usb_dev.poll([...])`.
895 // ///
896 // pub fn update(&mut self) {
897 // debug_assert!(!M::MEMIO_IN_USB_INTERRUPT, "not requried with MEMIO_IN_USB_INTERRUPT");
898 // if !M::MEMIO_IN_USB_INTERRUPT {
899 // self.update_impl()
900 // }
901 // }
902
903 // /// Returns `true` if [`update()`](DFUClass::update) needs to be called to
904 // /// process a pending operation.
905 // pub fn update_pending(&self) -> bool {
906 // match self.status.pending {
907 // Command::None => false,
908 // _ => true,
909 // }
910 // }
911
912 fn update_impl(&mut self) {
913 match self.status.pending {
914 Command::EraseAll => match self.mem.erase_all() {
915 Err(e) => self.status.new_state_status(DFUState::DfuError, e.into()),
916 Ok(_) => self.status.new_state_ok(DFUState::DfuDnloadSync),
917 },
918 Command::Erase(b) => match self.mem.erase(b) {
919 Err(e) => self.status.new_state_status(DFUState::DfuError, e.into()),
920 Ok(_) => self.status.new_state_ok(DFUState::DfuDnloadSync),
921 },
922 Command::LeaveDFU => {
923 // may not return
924 let mr = self.mem.manifestation();
925
926 match mr {
927 Err(e) => self.status.new_state_status(DFUState::DfuError, e.into()),
928 Ok(_) => {
929 if M::MANIFESTATION_TOLERANT {
930 self.status.new_state_ok(DFUState::DfuManifestSync)
931 } else {
932 self.status.new_state_ok(DFUState::DfuManifestWaitReset)
933 }
934 }
935 }
936 }
937 Command::ReadUnprotect => {
938 // XXX not implemented
939 // self.status.state = DFUState::DfuDnloadSync;
940 self.status
941 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrStalledPkt)
942 }
943 Command::WriteMemory { block_num, len } => {
944 if let Some(pointer) = self
945 .status
946 .address_pointer
947 .checked_add((block_num as u32) * (M::TRANSFER_SIZE as u32))
948 {
949 match self.mem.program(pointer, len as usize) {
950 Err(e) => self.status.new_state_status(DFUState::DfuError, e.into()),
951 Ok(_) => self.status.new_state_ok(DFUState::DfuDnloadSync),
952 }
953 } else {
954 // overflow
955 self.status
956 .new_state_status(DFUState::DfuError, DFUStatusCode::ErrAddress);
957 }
958 }
959 Command::SetAddressPointer(p) => {
960 self.status.address_pointer = p;
961 self.status.new_state_ok(DFUState::DfuDnloadSync)
962 }
963 Command::None => {}
964 }
965 self.status.pending = Command::None;
966 }
967
968 fn process(&mut self) -> bool {
969 let initial_state = self.status.state();
970 if initial_state == DFUState::DfuDnloadSync {
971 match self.status.command {
972 Command::WriteMemory {
973 block_num: _,
974 len: _,
975 }
976 | Command::SetAddressPointer(_)
977 | Command::ReadUnprotect
978 | Command::EraseAll
979 | Command::Erase(_) => {
980 self.status.pending = self.status.command;
981 self.status.command = Command::None;
982 self.status.new_state_ok(DFUState::DfuDnBusy);
983 }
984 //Command::None => {}
985 _ => {
986 self.status.new_state_ok(DFUState::DfuDnloadIdle);
987 }
988 }
989 } else if initial_state == DFUState::DfuManifestSync {
990 match self.status.command {
991 Command::None => {
992 if M::MANIFESTATION_TOLERANT {
993 // Leave manifestation, back to Idle
994 self.status.command = Command::None;
995 self.status.new_state_ok(DFUState::DfuIdle);
996 }
997 }
998 _ => {
999 // Start manifestation
1000 self.status.pending = self.status.command;
1001 self.status.command = Command::None;
1002 self.status.new_state_ok(DFUState::DfuManifest);
1003 }
1004 }
1005 } else if initial_state == DFUState::DfuDnBusy {
1006 return false;
1007 }
1008
1009 true
1010 }
1011}