2020-11-24 16:17:37 +01:00
/** library for USB DFU to write on internal flash
2019-12-06 17:41:37 +01:00
* @ file
2017-04-15 13:51:24 +02:00
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2020-06-06 14:35:55 +02:00
* @ copyright SPDX - License - Identifier : GPL - 3.0 - or - later
2020-01-04 14:38:22 +01:00
* @ date 2017 - 2020
2020-11-24 16:17:37 +01:00
* @ note peripherals used : USB
2017-04-15 13:51:24 +02:00
*/
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdlib.h> // general utilities
2020-11-24 16:17:37 +01:00
# include <string.h> // memory utilities
2017-04-15 13:51:24 +02:00
/* 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
2018-02-18 15:21:18 +01:00
# include <libopencm3/stm32/desig.h> // flash size definition
2020-11-24 16:17:37 +01:00
# include <libopencm3/stm32/flash.h> // flash utilities
2017-04-15 13:51:24 +02:00
# include <libopencm3/usb/usbd.h> // USB library
# include <libopencm3/usb/dfu.h> // USB DFU library
2021-09-24 16:00:23 +02:00
# include <libopencm3/usb/dwc/otg_fs.h> // additional USB definitions
2017-04-15 13:51:24 +02:00
# include "global.h" // global utilities
# include "usb_dfu.h" // USB DFU header and definitions
2020-11-24 16:17:37 +01:00
# include "flash_internal.h" // internal flash utilities
2017-04-15 13:51:24 +02:00
2020-11-24 16:17:37 +01:00
static uint8_t usbd_control_buffer [ 1024 ] = { 0 } ; /**< buffer to be used for control requests (must be a flash section divider) */
2017-04-15 13:51:24 +02:00
static usbd_device * usb_device = NULL ; /**< structure holding all the info related to the USB device */
static enum dfu_state usb_dfu_state = STATE_DFU_IDLE ; /**< current DFU state */
static enum dfu_status usb_dfu_status = DFU_STATUS_OK ; /**< current DFU status */
static uint8_t download_data [ sizeof ( usbd_control_buffer ) ] = { 0 } ; /**< downloaded data to be programmed in flash */
static uint16_t download_length = 0 ; /**< length of downloaded data */
2019-01-12 16:22:57 +01:00
static uint32_t download_offset = 0 ; /**< destination offset of where the downloaded data should be flashed */
2020-01-04 14:38:22 +01:00
static uint8_t firmware_head [ 8 ] = { 0xff } ; /**< to store the first bytes of the firmware, to be flashed at the end so we will stay in bootloader mode when the download is interrupted */
2017-04-15 13:51:24 +02:00
2020-11-24 16:17:37 +01:00
/** symbol for beginning of the application
* @ note this symbol will be provided by the bootloader linker script
*/
extern char __application_beginning ;
2017-04-15 13:51:24 +02:00
/** USB DFU device descriptor
* @ note as defined in USB Device Firmware Upgrade specification section 4.2 .1
*/
static const struct usb_device_descriptor usb_dfu_device = {
. bLength = USB_DT_DEVICE_SIZE , /**< the size of this header in bytes, 18 */
. bDescriptorType = USB_DT_DEVICE , /**< a value of 1 indicates that this is a device descriptor */
. bcdUSB = 0x0200 , /**< this device supports USB 2.0 */
. bDeviceClass = 0 , /**< unused */
. bDeviceSubClass = 0 , /**< unused */
. bDeviceProtocol = 0 , /**< unused */
. bMaxPacketSize0 = 64 , /**< packet size for endpoint zero in bytes */
2018-04-03 16:59:02 +02:00
. idVendor = 0x1209 , /**< pid.codes vendor ID */
2018-04-03 17:08:34 +02:00
. idProduct = 0x4356 , /**< CuVoodo product ID within the Vendor ID space */
2018-04-03 16:59:02 +02:00
. bcdDevice = 0x0000 , /**< Device Release Number: board version number */
2017-04-15 13:51:24 +02:00
. iManufacturer = 1 , /**< the index of the string in the string table that represents the name of the manufacturer of this device */
. iProduct = 2 , /**< the index of the string in the string table that represents the name of the product */
2019-12-06 17:41:37 +01:00
. iSerialNumber = 3 , /**< the index of the string in the string table that represents the serial number of this item in string form */
2017-04-15 13:51:24 +02:00
. bNumConfigurations = 1 , /**< the number of possible configurations this device has */
} ;
/** USB DFU functional descriptor
* @ note as defined in USB Device Firmware Upgrade specification section 4.2 .4
*/
static const struct usb_dfu_descriptor usb_dfu_functional = {
. bLength = sizeof ( struct usb_dfu_descriptor ) , /**< provide own size */
. bDescriptorType = DFU_FUNCTIONAL , /**< functional descriptor type */
. bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH , /**< this DFU can download and will detach after download (we don't support manifest for simplicity, technically we could) */
2019-01-12 16:22:57 +01:00
. wDetachTimeout = 0 , /**< maximum time in milliseconds to detach (and reboot) */
2017-04-15 13:51:24 +02:00
. wTransferSize = sizeof ( usbd_control_buffer ) , /**< set max transfer size */
. bcdDFUVersion = 0x0110 , /**< DFU specification version 1.1 used */
} ;
/** USB DFU interface descriptor
* @ note as defined in USB Device Firmware Upgrade specification section 4.2 .3
*/
static const struct usb_interface_descriptor usb_dfu_interface = {
. bLength = USB_DT_INTERFACE_SIZE , /**< size of descriptor in byte */
. bDescriptorType = USB_DT_INTERFACE , /**< interface descriptor type */
. bInterfaceNumber = 0 , /**< this interface is the first (and only) */
. bAlternateSetting = 0 , /**< no alternative settings */
. bNumEndpoints = 0 , /**< only the control pipe at endpoint 0 is used */
. bInterfaceClass = 0xFE , /**< DFU interface class (not defined in libopencm3 dfu lib) */
. bInterfaceSubClass = 1 , /**< DFU interface subclass (not defined in libopencm3 dfu lib) */
. bInterfaceProtocol = 2 , /**< DFU interface mode protocol (not defined in libopencm3 dfu lib) */
2019-12-06 17:41:37 +01:00
. iInterface = 4 , /**< the index of the string in the string table that represents interface description */
2017-04-15 13:51:24 +02:00
. extra = & usb_dfu_functional , /**< point to functional descriptor */
. extralen = sizeof ( usb_dfu_functional ) , /**< size of functional descriptor */
} ;
/** USB DFU interface descriptor list */
static const struct usb_interface usb_dfu_interfaces [ ] = { {
. num_altsetting = 1 , /**< this is the only alternative */
. altsetting = & usb_dfu_interface , /**< point to only interface descriptor */
} } ;
/** USB DFU configuration descriptor
* @ note as defined in USB Device Firmware Upgrade specification section 4.2 .2
*/
static const struct usb_config_descriptor usb_dfu_configuration = {
. bLength = USB_DT_CONFIGURATION_SIZE , /**< the length of this header in bytes */
. bDescriptorType = USB_DT_CONFIGURATION , /**< a value of 2 indicates that this is a configuration descriptor */
. wTotalLength = 0 , /**< total size of the configuration descriptor including all sub interfaces (automatically filled in by the USB stack in libopencm3) */
. bNumInterfaces = LENGTH ( usb_dfu_interfaces ) , /**< the number of interfaces in this configuration */
. bConfigurationValue = 1 , /**< the index of this configuration */
. iConfiguration = 0 , /**< a string index describing this configuration (zero means not provided) */
. bmAttributes = 0x80 , /**< bus powered (1<<7) */
. bMaxPower = 0x32 , /**< the maximum amount of current that this device will draw in 2mA units */
// end of header
. interface = usb_dfu_interfaces , /**< pointer to an array of interfaces */
} ;
2019-12-06 17:41:37 +01:00
/** device ID used as serial number */
2020-11-27 16:04:07 +01:00
static char usb_serial [ ] = " 0123456789ab " ;
2019-12-06 17:41:37 +01:00
2017-04-15 13:51:24 +02:00
/** USB string table
* @ note starts with index 1
*/
static const char * usb_dfu_strings [ ] = {
2018-04-03 16:59:02 +02:00
" CuVoodoo " , /**< manufacturer string */
2020-11-24 16:17:37 +01:00
" CuVoodoo STM32F4xx DFU bootloader " , /**< product string */
2019-12-06 17:41:37 +01:00
( const char * ) usb_serial , /**< device ID used as serial number */
2019-03-26 19:27:40 +01:00
" DFU bootloader (DFU mode) " , /**< DFU interface string */
2017-04-15 13:51:24 +02:00
} ;
/** disconnect USB to force re-enumerate */
static void usb_disconnect ( void )
{
2020-01-03 19:39:41 +01:00
if ( usb_device ) {
usbd_disconnect ( usb_device , true ) ;
}
2017-04-15 13:51:24 +02:00
// pull USB D+ low for a short while
rcc_periph_clock_enable ( RCC_GPIOA ) ;
2020-11-27 16:05:55 +01:00
gpio_mode_setup ( GPIOA , GPIO_MODE_OUTPUT , GPIO_PUPD_NONE , GPIO12 ) ;
gpio_set_output_options ( GPIOA , GPIO_OTYPE_PP , GPIO_OSPEED_2MHZ , GPIO12 ) ;
2017-04-15 13:51:24 +02:00
gpio_clear ( GPIOA , GPIO12 ) ;
2020-01-03 20:16:02 +01:00
// USB disconnected must be at least 10 ms long, at most 100 ms
2020-11-27 16:39:51 +01:00
for ( volatile uint32_t i = 0 ; i < 0x2000 ; i + + ) ;
2017-04-15 13:51:24 +02:00
}
2020-01-04 14:38:22 +01:00
/** disconnect USB and perform system reset
2017-04-15 13:51:24 +02:00
* @ param [ in ] usbd_dev USB device ( unused )
* @ param [ in ] req USB request ( unused )
* @ note this function is called after the corresponding GETSTATUS request
*/
2020-01-04 14:38:22 +01:00
static void usb_dfu_reset ( usbd_device * usbd_dev , struct usb_setup_data * req )
2017-04-15 13:51:24 +02:00
{
( void ) usbd_dev ; // variable not used
( void ) req ; // variable not used
2020-01-04 14:38:22 +01:00
usb_disconnect ( ) ; // USB detach (disconnect to force re-enumeration)
scb_reset_system ( ) ; // reset device
while ( true ) ; // wait for the reset to happen
2017-04-15 13:51:24 +02:00
}
2020-01-04 14:38:22 +01:00
/** flash downloaded data block
2017-04-15 13:51:24 +02:00
* @ param [ in ] usbd_dev USB device ( unused )
* @ param [ in ] req USB request ( unused )
* @ note this function is called after the corresponding GETSTATUS request
*/
2020-01-04 14:38:22 +01:00
static void usb_dfu_flash ( usbd_device * usbd_dev , struct usb_setup_data * req )
2017-04-15 13:51:24 +02:00
{
( void ) usbd_dev ; // variable not used
( void ) req ; // variable not used
2020-01-04 14:38:22 +01:00
led_off ( ) ; // indicate we are processing
2020-11-24 16:17:37 +01:00
static struct flash_internal_section_info_s * erased_section = NULL ; // last erased flash section
const struct flash_internal_section_info_s * target_section = flash_internal_section ( ( uint32_t ) & __application_beginning + download_offset ) ; // get the section where to write the downloaded data
if ( NULL = = target_section ) { // the corresponding section does not exist or is unknown
usb_dfu_status = DFU_STATUS_ERR_ADDRESS ; // tell there is something wrong with the address
usb_dfu_state = STATE_DFU_ERROR ; // tell the is something wrong
led_on ( ) ; // indicate we finished processing
return ;
}
if ( STATE_DFU_MANIFEST ! = usb_dfu_state & & target_section ! = erased_section ) { // we started a new section
// WARNING we except the offset to increment: if the offset lands in a previous section, it will be erased, possibly deleting previously written data
flash_clear_status_flags ( ) ; // clear all errors before performing operation
flash_unlock ( ) ; // unlock flash so we can erase the section
flash_erase_sector ( target_section - > number , 2 ) ; // erase section
flash_lock ( ) ; // lock back after operation completed
if ( FLASH_SR ) { // error during erasure happened (EOP is not set since we did not enable interrupts)
usb_dfu_status = DFU_STATUS_ERR_ERASE ; // tell there is something wrong while erasing
usb_dfu_state = STATE_DFU_ERROR ; // tell the is something wrong
led_on ( ) ; // indicate we finished processing
return ;
}
// verify the section has been erased (recommended by TRM)
bool erased = true ;
for ( uint32_t * address = ( uint32_t * ) target_section - > start ; address < = ( uint32_t * ) target_section - > end ; address + + ) {
if ( 0xffffffff ! = * address ) {
erased = false ;
break ;
}
}
if ( ! erased ) { // error during erasure happened
usb_dfu_status = DFU_STATUS_ERR_ERASE ; // tell there is something wrong while erasing
usb_dfu_state = STATE_DFU_ERROR ; // tell the is something wrong
led_on ( ) ; // indicate we finished processing
return ;
}
erased_section = ( struct flash_internal_section_info_s * ) target_section ; // remember which section has been erased
}
flash_clear_status_flags ( ) ; // clear all errors before performing operation
flash_unlock ( ) ; // unlock flash so we can write to it
flash_program ( ( uint32_t ) & __application_beginning + download_offset , download_data , download_length ) ; // program data
flash_lock ( ) ; // lock back after operation completed
if ( 0 = = FLASH_SR ) { // check if flashing worked (EOP is not set since we did not enable interrupts)
2020-01-04 14:38:22 +01:00
switch ( usb_dfu_state ) {
case STATE_DFU_DNLOAD_SYNC : // download ongoing
case STATE_DFU_DNBUSY : // flashing ongoing
usb_dfu_state = STATE_DFU_DNLOAD_IDLE ; // go back to idle stat to wait for next segment
break ;
case STATE_DFU_MANIFEST : // this was the last part
led_off ( ) ; // indicate the end
usb_dfu_reset ( NULL , NULL ) ; // start reset without waiting for request since we advertised we would detach
break ;
default : // unknown state
usb_dfu_status = DFU_STATUS_ERR_PROG ;
usb_dfu_state = STATE_DFU_ERROR ;
break ;
}
} else { // warn about writing error
usb_dfu_status = DFU_STATUS_ERR_WRITE ;
usb_dfu_state = STATE_DFU_ERROR ;
}
led_on ( ) ; // indicate we finished processing
2017-04-15 13:51:24 +02:00
}
/** handle incoming USB DFU control request
* @ param [ in ] usbd_dev USB device descriptor
* @ param [ in ] req control request information
* @ param [ in ] buf control request data
* @ param [ in ] len control request data length
* @ param [ in ] complete not used
2018-04-28 12:21:32 +02:00
* @ return USBD_REQ_HANDLED if handled correctly , USBD_REQ_NOTSUPP else
2017-04-15 13:51:24 +02:00
* @ note resets device when configured with 5 bits
*/
2018-04-28 12:21:32 +02:00
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 ) )
2017-04-15 13:51:24 +02:00
{
( void ) complete ;
( void ) usbd_dev ; // device is not used
// DFU only requires handling class requests
2019-01-12 16:22:57 +01:00
if ( ( req - > bmRequestType & USB_REQ_TYPE_TYPE ) ! = USB_REQ_TYPE_CLASS ) {
2018-04-28 12:21:32 +02:00
return USBD_REQ_NOTSUPP ;
2017-04-15 13:51:24 +02:00
}
led_off ( ) ; // indicate we are processing request
2018-04-28 12:21:32 +02:00
int to_return = USBD_REQ_HANDLED ; // value to return
2017-04-15 13:51:24 +02:00
switch ( req - > bRequest ) {
case DFU_DNLOAD : // download firmware on flash
2019-01-12 16:22:57 +01:00
if ( STATE_DFU_IDLE ! = usb_dfu_state & & STATE_DFU_DNLOAD_IDLE ! = usb_dfu_state ) { // wrong start to request download
2017-04-15 13:51:24 +02:00
// warn about programming error
usb_dfu_status = DFU_STATUS_ERR_PROG ;
usb_dfu_state = STATE_DFU_ERROR ;
2019-01-12 16:22:57 +01:00
} else if ( STATE_DFU_IDLE = = usb_dfu_state & & ( ( NULL = = len ) | | ( 0 = = * len ) ) ) { // download request should not start empty
2017-04-15 13:51:24 +02:00
// warn about programming error
usb_dfu_status = DFU_STATUS_ERR_PROG ;
usb_dfu_state = STATE_DFU_ERROR ;
2019-01-12 16:22:57 +01:00
} else if ( STATE_DFU_DNLOAD_IDLE = = usb_dfu_state & & ( ( NULL = = len ) | | ( 0 = = * len ) ) ) { // download completed
2017-04-15 13:51:24 +02:00
// go to manifestation phase
usb_dfu_state = STATE_DFU_MANIFEST_SYNC ;
} else { // there is data to be flashed
2019-01-12 16:22:57 +01:00
download_length = * len ; // remember download length
2020-11-24 16:17:37 +01:00
const uint32_t offset = req - > wValue * sizeof ( usbd_control_buffer ) ; // remember offset
if ( offset ! = 0 & & offset < = download_offset ) { // ensure the offset is incrementing (else this break our section erase procedure)
2017-04-15 13:51:24 +02:00
usb_dfu_status = DFU_STATUS_ERR_PROG ;
usb_dfu_state = STATE_DFU_ERROR ;
2020-11-24 16:17:37 +01:00
} else if ( * len > sizeof ( usbd_control_buffer ) | | * len > sizeof ( download_data ) ) { // got too much data
usb_dfu_status = DFU_STATUS_ERR_PROG ;
2018-04-06 19:56:57 +02:00
usb_dfu_state = STATE_DFU_ERROR ;
2020-11-24 16:17:37 +01:00
} else if ( ( uint32_t ) & __application_beginning + download_offset + download_length > = ( uint32_t ) ( FLASH_BASE + desig_get_flash_size ( ) * 1024 ) ) {
2018-04-06 19:56:57 +02:00
// application data is exceeding advertised flash size
2017-04-15 13:51:24 +02:00
usb_dfu_status = DFU_STATUS_ERR_ADDRESS ;
usb_dfu_state = STATE_DFU_ERROR ;
} else {
2020-11-24 16:17:37 +01:00
download_offset = offset ; // remember offset
memcpy ( download_data , * buf , * len ) ; // copy data (length has already been checked))
2020-01-04 14:38:22 +01:00
if ( 0 = = download_offset ) { // this is the beginning of the firmware
2020-11-24 16:17:37 +01:00
// we will keep the beginning of the firmware and flash in the head
2020-01-04 14:38:22 +01:00
// this is because the head is used to check if the bootloader should boot into the application
// but if the download gets interrupted, the head is valid and the application will be started
// but the application is corrupted, and will not allow to go back to the bootloader
// by flashing the firmware head at the end, we ensure the application to boot is valid
// note: the force DFU input/button could be used to start the bootloader when the application is corrupted
for ( uint16_t i = 0 ; i < LENGTH ( firmware_head ) & & i < sizeof ( download_data ) ; i + + ) {
firmware_head [ i ] = download_data [ i ] ; // backup head
2020-11-24 16:17:37 +01:00
download_data [ i ] = 0xff ; // invalided head (with value that still allows to program it later)
2020-01-04 14:38:22 +01:00
}
}
2017-04-15 13:51:24 +02:00
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
2018-04-28 12:21:32 +02:00
to_return = USBD_REQ_NOTSUPP ; // upload no supported
2017-04-15 13:51:24 +02:00
break ;
case DFU_GETSTATUS : // get status
( * buf ) [ 0 ] = usb_dfu_status ; // set status
2019-01-12 16:22:57 +01:00
( * buf ) [ 1 ] = 10 ; // set poll timeout (24 bits, in milliseconds) to small value for periodical poll
2017-04-15 13:51:24 +02:00
( * 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
2019-01-12 16:22:57 +01:00
if ( STATE_DFU_DNLOAD_SYNC = = usb_dfu_state ) {
2017-04-15 13:51:24 +02:00
usb_dfu_state = STATE_DFU_DNBUSY ; // switch to busy state
2019-01-12 16:22:57 +01:00
} else if ( STATE_DFU_MANIFEST_SYNC = = usb_dfu_state ) {
2020-01-04 14:38:22 +01:00
// we still need to flash the saved head of the firmware
for ( uint16_t i = 0 ; i < LENGTH ( firmware_head ) & & i < sizeof ( download_data ) ; i + + ) {
download_data [ i ] = firmware_head [ i ] ; // prepare data to be flashed
}
download_offset = 0 ; // flash the head of the firmware
download_length = sizeof ( firmware_head ) ; // only flash the head
2017-04-15 13:51:24 +02:00
usb_dfu_state = STATE_DFU_MANIFEST ; // go to manifest mode
2020-01-04 14:38:22 +01:00
* complete = usb_dfu_flash ; // flash the head
2017-04-15 13:51:24 +02:00
}
break ;
case DFU_CLRSTATUS : // clear status
2019-01-12 16:22:57 +01:00
if ( STATE_DFU_ERROR = = usb_dfu_state | | DFU_STATUS_OK ! = usb_dfu_status ) { // only clear in case there is an error
2017-04-15 13:51:24 +02:00
usb_dfu_status = DFU_STATUS_OK ; // clear error status
2019-01-12 16:22:57 +01:00
usb_dfu_state = STATE_DFU_IDLE ; // put back in idle state
2017-04-15 13:51:24 +02:00
}
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
2019-01-12 16:22:57 +01:00
usb_dfu_state = STATE_DFU_IDLE ; // put back in idle state (nothing else to do)
2017-04-15 13:51:24 +02:00
break ;
2019-01-12 16:22:57 +01:00
case DFU_DETACH : // this request is only defined in runtime mode
2017-04-15 13:51:24 +02:00
default :
2018-04-28 12:21:32 +02:00
to_return = USBD_REQ_NOTSUPP ;
2017-04-15 13:51:24 +02:00
}
led_on ( ) ; // indicate we finished processing
return to_return ;
}
void usb_dfu_setup ( void )
{
2020-11-27 16:04:07 +01:00
// set serial according to STM32 USB-FS_Device developer kit
// see https://community.st.com/s/question/0D50X0000ADCaTJSQ1/dfu-serial-number
2019-12-06 17:41:37 +01:00
for ( uint8_t i = 0 ; i < LENGTH ( usb_serial ) - 1 ; i + + ) { // write the serial
uint32_t id ; // current ID part
if ( i < 8 ) {
2020-11-27 16:04:07 +01:00
id = DESIG_UNIQUE_ID0 + DESIG_UNIQUE_ID2 ;
2019-12-06 17:41:37 +01:00
} else {
2020-11-27 16:04:07 +01:00
id = DESIG_UNIQUE_ID1 ;
2019-12-06 17:41:37 +01:00
}
// transform into character
char c = ( id > > ( ( 7 - ( i % 8 ) ) * 4 ) ) & 0x0f ; // get nibble
if ( c < 10 ) {
c = ' 0 ' + c ;
} else {
c = ' a ' + ( c - 10 ) ;
}
// set character
usb_serial [ i ] = c ;
}
2020-11-24 16:17:37 +01:00
rcc_periph_reset_pulse ( RST_OTGFS ) ; // reset USB peripheral
2017-04-15 13:51:24 +02:00
rcc_periph_clock_enable ( RCC_GPIOA ) ; // enable clock for GPIO used for USB
2020-11-24 16:17:37 +01:00
gpio_mode_setup ( GPIOA , GPIO_MODE_AF , GPIO_PUPD_NONE , GPIO11 | GPIO12 ) ; // set pin to alternate function
gpio_set_af ( GPIOA , GPIO_AF10 , GPIO11 | GPIO12 ) ; // set alternate function to USB
usb_device = usbd_init ( & otgfs_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
2017-04-15 13:51:24 +02:00
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
2021-09-24 16:00:23 +02:00
OTG_FS_GCCFG | = OTG_GCCFG_NOVBUSSENS | OTG_GCCFG_PWRDWN ; // disable VBUS sensing
OTG_FS_GCCFG & = ~ ( OTG_GCCFG_VBUSBSEN | OTG_GCCFG_VBUSASEN ) ; // force USB device mode
2017-04-15 13:51:24 +02:00
}
void usb_dfu_start ( void )
{
// infinitely poll device to handle requests
while ( true ) {
usbd_poll ( usb_device ) ;
}
}