usbd_dfu/lib.rs
1#![cfg_attr(not(test), no_std)]
2#![warn(missing_docs)]
3//!
4//! Implements DFU protocol version 1.1a for a `usb-device` device.
5//!
6//! ## About
7//!
8//! DFU protocol aims to provide a standard how USB device's firmware
9//! can be upgraded. Often, in this case firmware of the device
10//! consists of two parts: a large main firmware, and a smaller
11//! bootloader. When device is powered on, bootloader starts
12//! and either runs main firmware, or enters "firmware update"
13//! mode.
14//!
15//! Protocol implementation tries to follows DFU 1.1a protocol as
16//! specified by AN3156 by STMicroelectronics and
17//! USB Device Firmware Upgrade Specification, Revision 1.1.
18//!
19//! This library is a protocol implementation only, actual code
20//! that programs, erases, or reads memory or flash in not a
21//! of the library and is expected to be provided by library
22//! user.
23//!
24//! ### Supported operations
25//!
26//! * Read (device to host) - upload command
27//! * Write (host to device) - download command
28//! * Erase
29//! * Erase All
30//!
31//! ### Not supported operations
32//!
33//! * Read Unprotect - erase everything and remove read protection.
34//!
35//! ### Limitations
36//!
37//! * Maximum USB transfer size is limited to what `usb-device` supports
38//! for control enpoint transfers, which is `128` bytes by default.
39//!
40//! * iString field in `DFU_GETSTATUS` is always `0`. Vendor-specific string
41//! error descriptions are not supported.
42//!
43//! ## DFU utilities
44//!
45//! There are many implementations of tools to flash USB device
46//! supporting DFU protocol, for example:
47//!
48//! * [dfu](https://crates.io/crates/dfu) and [dfu-flasher](https://crates.io/crates/dfu-flasher)
49//! * [dfu-programmer](https://dfu-programmer.github.io/)
50//! * [dfu-util](http://dfu-util.sourceforge.net/)
51//! * others
52//!
53//! ## License
54//!
55//! This project is licensed under [MIT License](https://opensource.org/licenses/MIT)
56//! ([LICENSE](https://github.com/vitalyvb/usbd-dfu/blob/main/LICENSE)).
57//!
58//! ### Contribution
59//!
60//! Unless you explicitly state otherwise, any contribution intentionally
61//! submitted for inclusion in the work by you shall be licensed as above,
62//! without any additional terms or conditions.
63//!
64//! ## Example
65//!
66//! The example below tries to focus on [`DFUClass`], parts related to a target
67//! controller initialization and configuration (USB, interrupts, GPIO, etc.)
68//! are not in the scope of the example.
69//!
70//! Check examples for more information.
71//!
72//! Also see documentation for `usb-device` crate, crates that supports
73//! target microcontroller and provide a corresponding HAL.
74//!
75//! ```no_run
76//! use usb_device::prelude::*;
77//! use usbd_dfu::*;
78//! #
79//! # use usb_device::bus::UsbBusAllocator;
80//! #
81//! # pub struct DummyUsbBus { }
82//! # impl usb_device::bus::UsbBus for DummyUsbBus {
83//! # fn alloc_ep(&mut self, _: usb_device::UsbDirection, _: Option<usb_device::endpoint::EndpointAddress>,
84//! # _: usb_device::endpoint::EndpointType, _: u16, _: u8) -> usb_device::Result<usb_device::endpoint::EndpointAddress> { todo!() }
85//! # fn enable(&mut self) { todo!() }
86//! # fn reset(&self) { todo!() }
87//! # fn set_device_address(&self, _: u8) { todo!() }
88//! # fn write(&self, _: usb_device::endpoint::EndpointAddress, _: &[u8]) -> usb_device::Result<usize> { todo!() }
89//! # fn read(&self, _: usb_device::endpoint::EndpointAddress, _: &mut [u8]) -> usb_device::Result<usize> { todo!() }
90//! # fn set_stalled(&self, _: usb_device::endpoint::EndpointAddress, _: bool) { todo!() }
91//! # fn is_stalled(&self, _: usb_device::endpoint::EndpointAddress) -> bool { todo!() }
92//! # fn suspend(&self) { todo!() }
93//! # fn resume(&self) { todo!() }
94//! # fn poll(&self) -> usb_device::bus::PollResult { todo!() }
95//! # }
96//! #
97//! # let usb_bus_alloc: UsbBusAllocator<DummyUsbBus> = unsafe { core::mem::MaybeUninit::<UsbBusAllocator<DummyUsbBus>>::uninit().assume_init() };
98//! # let mut usb_dev = UsbDeviceBuilder::new(&usb_bus_alloc, UsbVidPid(0, 0)).build();
99//!
100//! // DFUClass will use MyMem to actually read, erase or program the memory.
101//! // Here, a set of constant parameters must be set. These parameters
102//! // either change how DFUClass behaves, or define host's expectations.
103//!
104//! struct MyMem {
105//! buffer: [u8; 64],
106//! flash_memory: [u8; 1024],
107//! }
108//!
109//! impl DFUMemIO for MyMem {
110//! const MEM_INFO_STRING: &'static str = "@Flash/0x00000000/1*1Kg";
111//! const INITIAL_ADDRESS_POINTER: u32 = 0x0;
112//! const PROGRAM_TIME_MS: u32 = 8;
113//! const ERASE_TIME_MS: u32 = 50;
114//! const FULL_ERASE_TIME_MS: u32 = 50;
115//! const TRANSFER_SIZE: u16 = 64;
116//!
117//! fn read(&mut self, address: u32, length: usize) -> Result<&[u8], DFUMemError> {
118//! // TODO: check address value
119//! let offset = address as usize;
120//! Ok(&self.flash_memory[offset..offset+length])
121//! }
122//!
123//! fn erase(&mut self, address: u32) -> Result<(), DFUMemError> {
124//! // TODO: check address value
125//! self.flash_memory.fill(0xff);
126//! // TODO: verify that block is erased successfully
127//! Ok(())
128//! }
129//!
130//! fn erase_all(&mut self) -> Result<(), DFUMemError> {
131//! // There is only one block, erase it.
132//! self.erase(0)
133//! }
134//!
135//! fn store_write_buffer(&mut self, src:&[u8]) -> Result<(), ()>{
136//! self.buffer[..src.len()].copy_from_slice(src);
137//! Ok(())
138//! }
139//!
140//! fn program(&mut self, address: u32, length: usize) -> Result<(), DFUMemError>{
141//! // TODO: check address value
142//! let offset = address as usize;
143//!
144//! // Write buffer to a memory
145//! self.flash_memory[offset..offset+length].copy_from_slice(&self.buffer[..length]);
146//!
147//! // TODO: verify that memory is programmed correctly
148//! Ok(())
149//! }
150//!
151//! fn manifestation(&mut self) -> Result<(), DFUManifestationError> {
152//! // Nothing to do to activate FW
153//! Ok(())
154//! }
155//! }
156//!
157//! let mut my_mem = MyMem {
158//! buffer: [0u8; 64],
159//! flash_memory: [0u8; 1024],
160//! };
161//!
162//! // Create USB device for a target device:
163//! // let usb_bus_alloc = UsbBus::new(peripheral);
164//! // let usb_dev = UsbDeviceBuilder::new().build();
165//!
166//! // Create DFUClass
167//! let mut dfu = DFUClass::new(&usb_bus_alloc, my_mem);
168//!
169//! // usb_dev.poll() must be called periodically, usually from USB interrupt handlers.
170//! // When USB input/output is done, handlers in MyMem may be called.
171//! usb_dev.poll(&mut [&mut dfu]);
172//! ```
173//!
174//! ### Example bootloader implementation
175//!
176//! See [usbd-dfu-example](https://github.com/vitalyvb/usbd-dfu-example) for a functioning example.
177//!
178
179/// DFU protocol module
180pub mod class;
181
182#[doc(inline)]
183pub use crate::class::{DFUClass, DFUManifestationError, DFUMemError, DFUMemIO};