usbd-dfu

Implements DFU protocol version 1.1a for a usb-device device.

About

DFU protocol aims to provide a standard how USB device's firmware can be upgraded. Often, in this case firmware of the device consists of two parts: a large main firmware, and a smaller bootloader. When device is powered on, bootloader starts and either runs main firmware, or enters "firmware update" mode.

Protocol implementation tries to follows DFU 1.1a protocol as specified by AN3156 by STMicroelectronics and USB Device Firmware Upgrade Specification, Revision 1.1.

This library is a protocol implementation only, actual code that programs, erases, or reads memory or flash in not a of the library and is expected to be provided by library user.

Supported operations

Not supported operations

Limitations

DFU utilities

There are many implementations of tools to flash USB device supporting DFU protocol, for example:

Example

The example below tries to focus on DFUClass, parts related to a target controller initialization and configuration (USB, interrupts, GPIO, etc.) are not in the scope of the example.

Check examples for more information.

Also see documentation for usb-device crate, crates that supports target microcontroller and provide a corresponding HAL.

```rust use usbdevice::prelude::*; use usbddfu::*;

// DFUClass will use MyMem to actually read, erase or program the memory. // Here, a set of constant parameters must be set. These parameters // either change how DFUClass behaves, or define host's expectations.

struct MyMem { buffer: [u8; 64], flash_memory: [u8; 1024], }

impl DFUMemIO for MyMem { const MEMINFOSTRING: &'static str = "@Flash/0x00000000/1*1Kg"; const INITIALADDRESSPOINTER : u32 = 0x0; const PAGEPROGRAMTIMEMS : u32 = 8; const PAGEERASETIMEMS : u32 = 50; const FULLERASETIMEMS : u32 = 50; const TRANSFERSIZE: u16 = 64;

fn read_block(&mut self, address: u32, length: usize) -> Result<&[u8], DFUMemError> {
    // TODO: check address value
    let offset = address as usize;
    Ok(&self.flash_memory[offset..offset+length])
}

fn erase_block(&mut self, address: u32) -> Result<(), DFUMemError> {
    // TODO: check address value
    self.flash_memory.fill(0xff);
    // TODO: verify that block is erased successfully
    Ok(())
}

fn erase_all_blocks(&mut self) -> Result<(), DFUMemError> {
    // There is only one block, erase it.
    self.erase_block(0)
}

fn store_write_buffer(&mut self, src:&[u8]) -> Result<(), ()>{
    self.buffer[..src.len()].copy_from_slice(src);
    Ok(())
}

fn program_block(&mut self, address: u32, length: usize) -> Result<(), DFUMemError>{
    // TODO: check address value
    let offset = address as usize;

    // Write buffer to a memory
    self.flash_memory[offset..offset+length].copy_from_slice(&self.buffer[..length]);

    // TODO: verify that memory is programmed correctly
    Ok(())
}

fn manifestation(&mut self) -> Result<(), DFUManifestationError> {
    // Nothing to do to activate FW
    Ok(())
}

}

let mut mymem = MyMem { buffer: [0u8; 64], flashmemory: [0u8; 1024], };

// Create USB device for a target device: // let usbbusalloc = UsbBus::new(peripheral); // let usb_dev = UsbDeviceBuilder::new().build();

// Create DFUClass let mut dfu = DFUClass::new(&usbbusalloc, my_mem);

// usbdev.poll() must be called periodically, usually from USB interrupt handlers. // When USB input/output is done, handlers in MyMem may be called. usbdev.poll(&mut [&mut dfu]); ```