CuVoodoo STM32F1 firmware template
usb_dfu.c
Go to the documentation of this file.
1 /* This program is free software: you can redistribute it and/or modify
2  * it under the terms of the GNU General Public License as published by
3  * the Free Software Foundation, either version 3 of the License, or
4  * (at your option) any later version.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public License
12  * along with this program. If not, see <http://www.gnu.org/licenses/>.
13  *
14  */
21 /* standard libraries */
22 #include <stdint.h> // standard integer types
23 #include <stdlib.h> // general utilities
24 
25 /* STM32 (including CM3) libraries */
26 #include <libopencmsis/core_cm3.h> // Cortex M3 utilities
27 #include <libopencm3/cm3/scb.h> // reset utilities
28 #include <libopencm3/stm32/rcc.h> // real-time control clock library
29 #include <libopencm3/stm32/gpio.h> // general purpose input output library
30 #include <libopencm3/stm32/desig.h> // flash size definition
31 #include <libopencm3/usb/usbd.h> // USB library
32 #include <libopencm3/usb/dfu.h> // USB DFU library
33 
34 #include "global.h" // global utilities
35 #include "usb_dfu.h" // USB DFU header and definitions
36 #include "flash_internal.h" // flash reading/writing utilities
37 
38 static uint8_t usbd_control_buffer[1024] = {0};
39 static usbd_device *usb_device = NULL;
40 static enum dfu_state usb_dfu_state = STATE_DFU_IDLE;
41 static enum dfu_status usb_dfu_status = DFU_STATUS_OK;
43 static uint8_t download_data[sizeof(usbd_control_buffer)] = {0};
44 static uint16_t download_length = 0;
45 static uint32_t flash_pointer = 0;
50 static const struct usb_device_descriptor usb_dfu_device = {
51  .bLength = USB_DT_DEVICE_SIZE,
52  .bDescriptorType = USB_DT_DEVICE,
53  .bcdUSB = 0x0200,
54  .bDeviceClass = 0,
55  .bDeviceSubClass = 0,
56  .bDeviceProtocol = 0,
57  .bMaxPacketSize0 = 64,
58  .idVendor = 0x1209,
59  .idProduct = 0x4256,
60  .bcdDevice = 0x0000,
61  .iManufacturer = 1,
62  .iProduct = 2,
63  .iSerialNumber = 0,
64  .bNumConfigurations = 1,
65 };
66 
70 static const struct usb_dfu_descriptor usb_dfu_functional = {
71  .bLength = sizeof(struct usb_dfu_descriptor),
72  .bDescriptorType = DFU_FUNCTIONAL,
73  .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
74  .wDetachTimeout = 200,
75  .wTransferSize = sizeof(usbd_control_buffer),
76  .bcdDFUVersion = 0x0110,
77 };
78 
82 static const struct usb_interface_descriptor usb_dfu_interface = {
83  .bLength = USB_DT_INTERFACE_SIZE,
84  .bDescriptorType = USB_DT_INTERFACE,
85  .bInterfaceNumber = 0,
86  .bAlternateSetting = 0,
87  .bNumEndpoints = 0,
88  .bInterfaceClass = 0xFE,
89  .bInterfaceSubClass = 1,
90  .bInterfaceProtocol = 2,
91  .iInterface = 3,
92  .extra = &usb_dfu_functional,
93  .extralen = sizeof(usb_dfu_functional),
94 };
95 
97 static const struct usb_interface usb_dfu_interfaces[] = {{
98  .num_altsetting = 1,
99  .altsetting = &usb_dfu_interface,
100 }};
101 
105 static const struct usb_config_descriptor usb_dfu_configuration = {
106  .bLength = USB_DT_CONFIGURATION_SIZE,
107  .bDescriptorType = USB_DT_CONFIGURATION,
108  .wTotalLength = 0,
109  .bNumInterfaces = LENGTH(usb_dfu_interfaces),
110  .bConfigurationValue = 1,
111  .iConfiguration = 0,
112  .bmAttributes = 0x80,
113  .bMaxPower = 0x32,
114  // end of header
115  .interface = usb_dfu_interfaces,
116 };
117 
121 static const char *usb_dfu_strings[] = {
122  "CuVoodoo",
123  "BusVoodoo multi-protocol debugging adapter ",
124  "DFU bootloader (DFU mode)",
125 };
126 
128 static void usb_disconnect(void)
129 {
130 #if defined(MAPLE_MINI)
131  // disconnect USB D+ using dedicated DISC line/circuit on PB9
132  rcc_periph_clock_enable(RCC_GPIOB);
133  gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO9);
134  gpio_set(GPIOB, GPIO9);
135  for (uint32_t i = 0; i < 0x2000; i++) {
136  __asm__("nop");
137  }
138  gpio_clear(GPIOB, GPIO9);
139 #else
140  // pull USB D+ low for a short while
141  rcc_periph_clock_enable(RCC_GPIOA);
142  gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
143  gpio_clear(GPIOA, GPIO12);
144  for (uint32_t i = 0; i < 0x2000; i++) {
145  __asm__("nop");
146  }
147 #endif
148 }
149 
155 static void usb_dfu_flash(usbd_device *usbd_dev, struct usb_setup_data *req)
156 {
157  (void)usbd_dev; // variable not used
158  (void)req; // variable not used
159  led_off(); // indicate we are processing
160  if (flash_internal_write(flash_pointer, download_data, download_length)) { // write downloaded data
161  flash_pointer += download_length; // go to next segment
162  usb_dfu_state = STATE_DFU_DNLOAD_IDLE; // go back to idle stat to wait for next segment
163  } else { // warn about writing error
164  usb_dfu_status = DFU_STATUS_ERR_WRITE;
165  usb_dfu_state = STATE_DFU_ERROR;
166  }
167  led_on(); // indicate we finished processing
168 }
169 
175 static void usb_dfu_reset(usbd_device *usbd_dev, struct usb_setup_data *req)
176 {
177  (void)usbd_dev; // variable not used
178  (void)req; // variable not used
179  usb_disconnect(); // USB detach (disconnect to force re-enumeration)
180  scb_reset_system(); // reset device
181  while (true); // wait for the reset to happen
182 }
183 
193 static int 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))
194 {
195  (void)complete;
196  (void)usbd_dev; // device is not used
197 
198  // DFU only requires handling class requests
199  if ((req->bmRequestType & USB_REQ_TYPE_TYPE)!=USB_REQ_TYPE_CLASS) {
200  return 0;
201  }
202 
203  led_off(); // indicate we are processing request
204  int to_return = 1; // value to return
205  switch (req->bRequest) {
206  case DFU_DETACH: // USB detach requested
207  *complete = usb_dfu_reset; // reset after reply
208  break;
209  case DFU_DNLOAD: // download firmware on flash
210  if (STATE_DFU_IDLE!=usb_dfu_state && STATE_DFU_DNLOAD_IDLE!=usb_dfu_state) { // wrong start to request download
211  // warn about programming error
212  usb_dfu_status = DFU_STATUS_ERR_PROG;
213  usb_dfu_state = STATE_DFU_ERROR;
214  } else if (STATE_DFU_IDLE==usb_dfu_state && ((NULL==len) || (0 == *len))) { // download request should not start empty
215  // warn about programming error
216  usb_dfu_status = DFU_STATUS_ERR_PROG;
217  usb_dfu_state = STATE_DFU_ERROR;
218  } else if (STATE_DFU_DNLOAD_IDLE==usb_dfu_state && ((NULL==len) || (0 == *len))) { // download completed
219  // go to manifestation phase
220  usb_dfu_state = STATE_DFU_MANIFEST_SYNC;
221  } else { // there is data to be flashed
222  if (*len%2) {
223  // we can only write half words
224  usb_dfu_status = DFU_STATUS_ERR_PROG;
225  usb_dfu_state = STATE_DFU_ERROR;
226 #if defined(__application_end)
227  } else if (flash_pointer+*len>=(uint32_t)&__application_end) {
228 #else
229  } else if (flash_pointer+*len>=(uint32_t)(FLASH_BASE+DESIG_FLASH_SIZE*1024)) {
230 #endif
231  // application data is too large
232  usb_dfu_status = DFU_STATUS_ERR_ADDRESS;
233  usb_dfu_state = STATE_DFU_ERROR;
234  } else {
235  // save downloaded data to be flashed
236  for (uint16_t i=0; i<*len && i<sizeof(download_data); i++) {
237  download_data[i] = (*buf)[i];
238  }
239  download_length = *len;
240  usb_dfu_state = STATE_DFU_DNLOAD_SYNC; // go to sync state
241  *complete = usb_dfu_flash; // start flashing the downloaded data
242  }
243  }
244  break;
245  case DFU_UPLOAD: // upload firmware from flash
246  to_return = 0; // upload no supported
247  break;
248  case DFU_GETSTATUS: // get status
249  (*buf)[0] = usb_dfu_status; // set status
250  (*buf)[1] = 100; // set poll timeout (24 bits, in milliseconds) to small value for periodical poll
251  (*buf)[2] = 0; // set poll timeout (24 bits, in milliseconds) to small value for periodical poll
252  (*buf)[3] = 0; // set poll timeout (24 bits, in milliseconds) to small value for periodical poll
253  (*buf)[4] = usb_dfu_state; // set state
254  (*buf)[5] = 0; // string not used
255  *len = 6; // set length of buffer to return
256  if (STATE_DFU_DNLOAD_SYNC==usb_dfu_state) {
257  usb_dfu_state = STATE_DFU_DNBUSY; // switch to busy state
258  } else if (STATE_DFU_MANIFEST_SYNC==usb_dfu_state) {
259  usb_dfu_state = STATE_DFU_MANIFEST; // go to manifest mode
260  led_off(); // indicate the end
261  *complete = usb_dfu_reset; // start reset without waiting for request since we advertised we would detach
262  }
263  break;
264  case DFU_CLRSTATUS: // clear status
265  if (STATE_DFU_ERROR==usb_dfu_state || DFU_STATUS_OK!=usb_dfu_status) { // only clear in case there is an error
266  usb_dfu_status = DFU_STATUS_OK; // clear error status
267  usb_dfu_state = STATE_APP_IDLE; // put back in idle state
268  }
269  break;
270  case DFU_GETSTATE: // get state
271  (*buf)[0] = usb_dfu_state; // return state
272  *len = 1; // only state needs to be provided
273  break;
274  case DFU_ABORT: // abort current operation
275  usb_dfu_state = STATE_APP_IDLE; // put back in idle state (nothing else to do)
276  flash_pointer = (uint32_t)&__application_beginning; // reset download location
277  break;
278  default:
279  to_return = 0;
280  }
281  led_on(); // indicate we finished processing
282 
283  return to_return;
284 }
285 
286 void usb_dfu_setup(void)
287 {
288  flash_pointer = (uint32_t)&__application_beginning; // set download destination to beginning of application in flash
289  rcc_periph_reset_pulse(RST_USB); // reset USB peripheral
290  usb_disconnect(); // disconnect to force re-enumeration
291  rcc_periph_clock_enable(RCC_GPIOA); // enable clock for GPIO used for USB
292  rcc_periph_clock_enable(RCC_USB); // enable clock for USB domain
293  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
294  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
295 }
296 
297 void usb_dfu_start(void)
298 {
299  // infinitely poll device to handle requests
300  while (true) {
301  usbd_poll(usb_device);
302  }
303 }
static enum dfu_status usb_dfu_status
current DFU status
Definition: usb_dfu.c:41
static const struct usb_config_descriptor usb_dfu_configuration
USB DFU configuration descriptor.
Definition: usb_dfu.c:105
static uint32_t flash_pointer
where the downloaded data should be flashed
Definition: usb_dfu.c:45
static const char * usb_dfu_strings[]
USB string table.
Definition: usb_dfu.c:121
static usbd_device * usb_device
structure holding all the info related to the USB device
Definition: usb_dfu.c:39
void led_off(void)
switch off board LED
Definition: global.c:82
uint32_t __application_end
symbol for end of the application
static uint16_t download_length
length of downloaded data
Definition: usb_dfu.c:44
global definitions and methods (API)
static void usb_disconnect(void)
disconnect USB to force re-enumerate
Definition: usb_dfu.c:128
void usb_dfu_start(void)
start USB DFU handling
Definition: usb_dfu.c:297
void usb_dfu_setup(void)
setup USB DFU peripheral
Definition: usb_dfu.c:286
library to read/write internal flash (API)
static uint8_t download_data[sizeof(usbd_control_buffer)]
downloaded data to be programmed in flash
Definition: usb_dfu.c:43
static enum dfu_state usb_dfu_state
current DFU state
Definition: usb_dfu.c:40
library for USB DFU to write on internal flash (API)
static uint8_t usbd_control_buffer[1024]
buffer to be used for control requests (fit to flash page size)
Definition: usb_dfu.c:38
static void usb_dfu_flash(usbd_device *usbd_dev, struct usb_setup_data *req)
flash downloaded data block
Definition: usb_dfu.c:155
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
uint32_t __application_beginning
symbol for beginning of the application
static const struct usb_interface_descriptor usb_dfu_interface
USB DFU interface descriptor.
Definition: usb_dfu.c:82
static const struct usb_device_descriptor usb_dfu_device
USB DFU device descriptor.
Definition: usb_dfu.c:50
#define LENGTH(x)
get the length of an array
Definition: global.h:26
static int 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
bool flash_internal_write(uint32_t address, uint8_t *buffer, size_t size)
write data to internal flash
static const struct usb_interface usb_dfu_interfaces[]
USB DFU interface descriptor list.
Definition: usb_dfu.c:97
void led_on(void)
switch on board LED
Definition: global.c:68
static const struct usb_dfu_descriptor usb_dfu_functional
USB DFU functional descriptor.
Definition: usb_dfu.c:70