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}