usb_dfu.c
Go to the documentation of this file.
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* standard libraries */
#include <stdint.h> // standard integer types
#include <stdlib.h> // general utilities
/* STM32 (including CM3) libraries */
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
#include <libopencm3/cm3/scb.h> // reset utilities
#include <libopencm3/stm32/rcc.h> // real-time control clock library
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/desig.h> // flash size definition
#include <libopencm3/usb/usbd.h> // USB library
#include <libopencm3/usb/dfu.h> // USB DFU library
#include "global.h" // global utilities
#include "usb_dfu.h" // USB DFU header and definitions
#include "flash_internal.h" // flash reading/writing utilities
static uint8_t usbd_control_buffer[1024] = {0};
static usbd_device *usb_device = NULL;
static enum dfu_state usb_dfu_state = STATE_DFU_IDLE;
static enum dfu_status usb_dfu_status = DFU_STATUS_OK;
static uint8_t download_data[sizeof(usbd_control_buffer)] = {0};
static uint16_t download_length = 0;
static uint32_t flash_pointer = 0;
static const struct usb_device_descriptor usb_dfu_device = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x1209,
.idProduct = 0x4256,
.bcdDevice = 0x0000,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 0,
.bNumConfigurations = 1,
};
static const struct usb_dfu_descriptor usb_dfu_functional = {
.bLength = sizeof(struct usb_dfu_descriptor),
.bDescriptorType = DFU_FUNCTIONAL,
.bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
.wDetachTimeout = 200,
.wTransferSize = sizeof(usbd_control_buffer),
.bcdDFUVersion = 0x0110,
};
static const struct usb_interface_descriptor usb_dfu_interface = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = 0xFE,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 2,
.iInterface = 3,
.extra = &usb_dfu_functional,
.extralen = sizeof(usb_dfu_functional),
};
static const struct usb_interface usb_dfu_interfaces[] = {{
.num_altsetting = 1,
.altsetting = &usb_dfu_interface,
}};
static const struct usb_config_descriptor usb_dfu_configuration = {
.bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0,
.bNumInterfaces = LENGTH(usb_dfu_interfaces),
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0x80,
.bMaxPower = 0x32,
// end of header
.interface = usb_dfu_interfaces,
};
static const char *usb_dfu_strings[] = {
"CuVoodoo",
"BusVoodoo multi-protocol debugging adapter ",
"DFU bootloader (DFU mode)",
};
static void usb_disconnect(void)
{
#if defined(MAPLE_MINI)
// disconnect USB D+ using dedicated DISC line/circuit on PB9
rcc_periph_clock_enable(RCC_GPIOB);
gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO9);
gpio_set(GPIOB, GPIO9);
for (uint32_t i = 0; i < 0x2000; i++) {
__asm__("nop");
}
gpio_clear(GPIOB, GPIO9);
#else
// pull USB D+ low for a short while
rcc_periph_clock_enable(RCC_GPIOA);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
gpio_clear(GPIOA, GPIO12);
for (uint32_t i = 0; i < 0x2000; i++) {
__asm__("nop");
}
#endif
}
static void usb_dfu_flash(usbd_device *usbd_dev, struct usb_setup_data *req)
{
(void)usbd_dev; // variable not used
(void)req; // variable not used
led_off(); // indicate we are processing
if (flash_internal_write(flash_pointer, download_data, download_length)) { // write downloaded data
flash_pointer += download_length; // go to next segment
usb_dfu_state = STATE_DFU_DNLOAD_IDLE; // go back to idle stat to wait for next segment
} else { // warn about writing error
usb_dfu_status = DFU_STATUS_ERR_WRITE;
usb_dfu_state = STATE_DFU_ERROR;
}
led_on(); // indicate we finished processing
}
static void usb_dfu_reset(usbd_device *usbd_dev, struct usb_setup_data *req)
{
(void)usbd_dev; // variable not used
(void)req; // variable not used
usb_disconnect(); // USB detach (disconnect to force re-enumeration)
scb_reset_system(); // reset device
while (true); // wait for the reset to happen
}
static enum usbd_request_return_codes usb_dfu_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req))
{
(void)complete;
(void)usbd_dev; // device is not used
// DFU only requires handling class requests
if ((req->bmRequestType & USB_REQ_TYPE_TYPE)!=USB_REQ_TYPE_CLASS) {
return USBD_REQ_NOTSUPP;
}
led_off(); // indicate we are processing request
int to_return = USBD_REQ_HANDLED; // value to return
switch (req->bRequest) {
case DFU_DETACH: // USB detach requested
*complete = usb_dfu_reset; // reset after reply
break;
case DFU_DNLOAD: // download firmware on flash
if (STATE_DFU_IDLE!=usb_dfu_state && STATE_DFU_DNLOAD_IDLE!=usb_dfu_state) { // wrong start to request download
// warn about programming error
usb_dfu_status = DFU_STATUS_ERR_PROG;
usb_dfu_state = STATE_DFU_ERROR;
} else if (STATE_DFU_IDLE==usb_dfu_state && ((NULL==len) || (0 == *len))) { // download request should not start empty
// warn about programming error
usb_dfu_status = DFU_STATUS_ERR_PROG;
usb_dfu_state = STATE_DFU_ERROR;
} else if (STATE_DFU_DNLOAD_IDLE==usb_dfu_state && ((NULL==len) || (0 == *len))) { // download completed
// go to manifestation phase
usb_dfu_state = STATE_DFU_MANIFEST_SYNC;
} else { // there is data to be flashed
if (*len%2) {
// we can only write half words
usb_dfu_status = DFU_STATUS_ERR_PROG;
usb_dfu_state = STATE_DFU_ERROR;
} else if ((uint32_t)&__application_end>=FLASH_BASE && flash_pointer+*len>=(uint32_t)&__application_end) {
// application data is exceeding enforced flash size for application
usb_dfu_status = DFU_STATUS_ERR_ADDRESS;
usb_dfu_state = STATE_DFU_ERROR;
} else if ((uint32_t)&__application_end<FLASH_BASE && flash_pointer+*len>=(uint32_t)(FLASH_BASE+DESIG_FLASH_SIZE*1024)) {
// application data is exceeding advertised flash size
usb_dfu_status = DFU_STATUS_ERR_ADDRESS;
usb_dfu_state = STATE_DFU_ERROR;
} else {
// save downloaded data to be flashed
for (uint16_t i=0; i<*len && i<sizeof(download_data); i++) {
download_data[i] = (*buf)[i];
}
download_length = *len;
usb_dfu_state = STATE_DFU_DNLOAD_SYNC; // go to sync state
*complete = usb_dfu_flash; // start flashing the downloaded data
}
}
break;
case DFU_UPLOAD: // upload firmware from flash
to_return = USBD_REQ_NOTSUPP; // upload no supported
break;
case DFU_GETSTATUS: // get status
(*buf)[0] = usb_dfu_status; // set status
(*buf)[1] = 100; // set poll timeout (24 bits, in milliseconds) to small value for periodical poll
(*buf)[2] = 0; // set poll timeout (24 bits, in milliseconds) to small value for periodical poll
(*buf)[3] = 0; // set poll timeout (24 bits, in milliseconds) to small value for periodical poll
(*buf)[4] = usb_dfu_state; // set state
(*buf)[5] = 0; // string not used
*len = 6; // set length of buffer to return
if (STATE_DFU_DNLOAD_SYNC==usb_dfu_state) {
usb_dfu_state = STATE_DFU_DNBUSY; // switch to busy state
} else if (STATE_DFU_MANIFEST_SYNC==usb_dfu_state) {
usb_dfu_state = STATE_DFU_MANIFEST; // go to manifest mode
led_off(); // indicate the end
*complete = usb_dfu_reset; // start reset without waiting for request since we advertised we would detach
}
break;
case DFU_CLRSTATUS: // clear status
if (STATE_DFU_ERROR==usb_dfu_state || DFU_STATUS_OK!=usb_dfu_status) { // only clear in case there is an error
usb_dfu_status = DFU_STATUS_OK; // clear error status
usb_dfu_state = STATE_APP_IDLE; // put back in idle state
}
break;
case DFU_GETSTATE: // get state
(*buf)[0] = usb_dfu_state; // return state
*len = 1; // only state needs to be provided
break;
case DFU_ABORT: // abort current operation
usb_dfu_state = STATE_APP_IDLE; // put back in idle state (nothing else to do)
flash_pointer = (uint32_t)&__application_beginning; // reset download location
break;
default:
to_return = USBD_REQ_NOTSUPP;
}
led_on(); // indicate we finished processing
return to_return;
}
void usb_dfu_setup(void)
{
flash_pointer = (uint32_t)&__application_beginning; // set download destination to beginning of application in flash
rcc_periph_reset_pulse(RST_USB); // reset USB peripheral
usb_disconnect(); // disconnect to force re-enumeration
rcc_periph_clock_enable(RCC_GPIOA); // enable clock for GPIO used for USB
rcc_periph_clock_enable(RCC_USB); // enable clock for USB domain
usb_device = usbd_init(&st_usbfs_v1_usb_driver, &usb_dfu_device, &usb_dfu_configuration, usb_dfu_strings, LENGTH(usb_dfu_strings), usbd_control_buffer, sizeof(usbd_control_buffer)); // configure USB device
usbd_register_control_callback(usb_device, USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, usb_dfu_control_request); // set control request handling DFU operations
}
void usb_dfu_start(void)
{
// infinitely poll device to handle requests
while (true) {
usbd_poll(usb_device);
}
}
usb_dfu_status
static enum dfu_status usb_dfu_status
current DFU status
Definition: usb_dfu.c:41
usb_dfu_configuration
static const struct usb_config_descriptor usb_dfu_configuration
USB DFU configuration descriptor.
Definition: usb_dfu.c:105
flash_pointer
static uint32_t flash_pointer
where the downloaded data should be flashed
Definition: usb_dfu.c:45
usb_dfu_strings
static const char * usb_dfu_strings[]
USB string table.
Definition: usb_dfu.c:121
usb_device
static usbd_device * usb_device
structure holding all the info related to the USB device
Definition: usb_dfu.c:39
__application_beginning
char __application_beginning
symbol for beginning of the application
led_off
void led_off(void)
switch off board LED
Definition: global.c:80
download_length
static uint16_t download_length
length of downloaded data
Definition: usb_dfu.c:44
global.h
global definitions and methods (API)
usb_disconnect
static void usb_disconnect(void)
disconnect USB to force re-enumerate
Definition: usb_dfu.c:128
usb_dfu_start
void usb_dfu_start(void)
start USB DFU handling
Definition: usb_dfu.c:297
usb_dfu_setup
void usb_dfu_setup(void)
setup USB DFU peripheral
Definition: usb_dfu.c:286
flash_internal.h
library to read/write internal flash (API)
download_data
static uint8_t download_data[sizeof(usbd_control_buffer)]
downloaded data to be programmed in flash
Definition: usb_dfu.c:43
usb_dfu_state
static enum dfu_state usb_dfu_state
current DFU state
Definition: usb_dfu.c:40
usb_dfu.h
library for USB DFU to write on internal flash (API)
usbd_control_buffer
static uint8_t usbd_control_buffer[1024]
buffer to be used for control requests (fit to flash page size)
Definition: usb_dfu.c:38
usb_dfu_flash
static void usb_dfu_flash(usbd_device *usbd_dev, struct usb_setup_data *req)
flash downloaded data block
Definition: usb_dfu.c:155
usb_dfu_reset
static void usb_dfu_reset(usbd_device *usbd_dev, struct usb_setup_data *req)
disconnect USB and perform system reset
Definition: usb_dfu.c:175
usb_dfu_control_request
static enum usbd_request_return_codes usb_dfu_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, void(**complete)(usbd_device *usbd_dev, struct usb_setup_data *req))
handle incoming USB DFU control request
Definition: usb_dfu.c:193
usb_dfu_interface
static const struct usb_interface_descriptor usb_dfu_interface
USB DFU interface descriptor.
Definition: usb_dfu.c:82
usb_dfu_device
static const struct usb_device_descriptor usb_dfu_device
USB DFU device descriptor.
Definition: usb_dfu.c:50
LENGTH
#define LENGTH(x)
get the length of an array
Definition: global.h:26
flash_internal_write
bool flash_internal_write(uint32_t address, uint8_t *buffer, size_t size)
write data to internal flash
Definition: flash_internal.c:75
usb_dfu_interfaces
static const struct usb_interface usb_dfu_interfaces[]
USB DFU interface descriptor list.
Definition: usb_dfu.c:97
__application_end
char __application_end
symbol for end of the application
led_on
void led_on(void)
switch on board LED
Definition: global.c:67
usb_dfu_functional
static const struct usb_dfu_descriptor usb_dfu_functional
USB DFU functional descriptor.
Definition: usb_dfu.c:70