Implements DFU protocol version 1.1a for a usb-device
device.
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.
Maximum USB transfer size is limited to what usb-device
supports
for control enpoint transfers, which is 128
bytes by default.
iString field in DFU_GETSTATUS
is always 0
. Vendor-specific string
error descriptions are not supported.
There are many implementations of tools to flash USB device supporting DFU protocol, for 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]); ```