CuVoodoo STM32F1 firmware template
usb_cdcacm.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/cm3/nvic.h> // interrupt handler
29 #include <libopencm3/stm32/rcc.h> // real-time control clock library
30 #include <libopencm3/stm32/gpio.h> // general purpose input output library
31 #include <libopencm3/usb/usbd.h> // USB library
32 #include <libopencm3/usb/cdc.h> // USB CDC library
33 #include <libopencm3/usb/dfu.h> // DFU definitions
34 
35 /* own libraries */
36 #include "global.h" // global utilities
37 #include "usb_cdcacm.h" // USB CDC ACM header and definitions
38 
40 #define USB_DATA_TRANSFER_SIZE 64 // 64 is the maximum for full speed devices
41 
42 volatile bool usb_cdcacm_connecting = false;
43 
44 static uint8_t usbd_control_buffer[128] = {0};
45 static usbd_device *usb_device = NULL;
46 static volatile bool first_connection = false;
48 /* output ring buffer, index, and available memory */
49 static uint8_t tx_buffer[256] = {0};
50 static volatile uint16_t tx_i = 0;
51 static volatile uint16_t tx_used = 0;
52 static volatile bool tx_lock = false;
57 static const struct usb_device_descriptor usb_cdcacm_device_descriptor = {
58  .bLength = USB_DT_DEVICE_SIZE,
59  .bDescriptorType = USB_DT_DEVICE,
60  .bcdUSB = 0x0200,
61  .bDeviceClass = USB_CLASS_CDC,
62  .bDeviceSubClass = 0,
63  .bDeviceProtocol = 0,
64  .bMaxPacketSize0 = 64,
65  .idVendor = 0x1209,
66  .idProduct = 0x4256,
67  .bcdDevice = 0x0000,
68  .iManufacturer = 1,
69  .iProduct = 2,
70  .iSerialNumber = 3,
71  .bNumConfigurations = 1,
72 };
73 
77 static const struct usb_endpoint_descriptor usb_cdcacm_data_endpoints[] = {{
78  .bLength = USB_DT_ENDPOINT_SIZE,
79  .bDescriptorType = USB_DT_ENDPOINT,
80  .bEndpointAddress = 0x02,
81  .bmAttributes = USB_ENDPOINT_ATTR_BULK,
82  .wMaxPacketSize = USB_DATA_TRANSFER_SIZE,
83  .bInterval = 1,
84 },{
85  .bLength = USB_DT_ENDPOINT_SIZE,
86  .bDescriptorType = USB_DT_ENDPOINT,
87  .bEndpointAddress = 0x82,
88  .bmAttributes = USB_ENDPOINT_ATTR_BULK,
89  .wMaxPacketSize = USB_DATA_TRANSFER_SIZE,
90  .bInterval = 1,
91 }};
92 
96 static const struct usb_endpoint_descriptor usb_cdcacm_communication_endpoints[] = {{
97  .bLength = USB_DT_ENDPOINT_SIZE,
98  .bDescriptorType = USB_DT_ENDPOINT,
99  .bEndpointAddress = 0x81,
100  .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
101  .wMaxPacketSize = 16,
102  .bInterval = 255,
103 }};
104 
109 static const struct {
110  struct usb_cdc_header_descriptor header;
111  struct usb_cdc_call_management_descriptor call_mgmt;
112  struct usb_cdc_acm_descriptor acm;
113  struct usb_cdc_union_descriptor cdc_union;
114 } __attribute__((packed)) usb_cdcacm_functional_descriptors = {
115  .header = {
116  .bFunctionLength = sizeof(struct usb_cdc_header_descriptor),
117  .bDescriptorType = CS_INTERFACE,
118  .bDescriptorSubtype = USB_CDC_TYPE_HEADER,
119  .bcdCDC = 0x0110,
120  },
121  .call_mgmt = {
122  .bFunctionLength = sizeof(struct usb_cdc_call_management_descriptor),
123  .bDescriptorType = CS_INTERFACE,
124  .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT,
125  .bmCapabilities = 0,
126  .bDataInterface = 1,
127  },
128  .acm = {
129  .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor),
130  .bDescriptorType = CS_INTERFACE,
131  .bDescriptorSubtype = USB_CDC_TYPE_ACM,
132  .bmCapabilities = 0,
133  },
134  .cdc_union = {
135  .bFunctionLength = sizeof(struct usb_cdc_union_descriptor),
136  .bDescriptorType = CS_INTERFACE,
137  .bDescriptorSubtype = USB_CDC_TYPE_UNION,
138  .bControlInterface = 0,
139  .bSubordinateInterface0 = 1,
140  },
141 };
142 
146 static const struct usb_interface_descriptor usb_cdcacm_communication_interface = {
147  .bLength = USB_DT_INTERFACE_SIZE,
148  .bDescriptorType = USB_DT_INTERFACE,
149  .bInterfaceNumber = 0,
150  .bAlternateSetting = 0,
151  .bNumEndpoints = 1,
152  .bInterfaceClass = USB_CLASS_CDC,
153  .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
154  .bInterfaceProtocol = USB_CDC_PROTOCOL_NONE,
155  .iInterface = 0,
159  .extra = &usb_cdcacm_functional_descriptors,
160  .extralen = sizeof(usb_cdcacm_functional_descriptors),
161 };
162 
166 static const struct usb_interface_descriptor usb_cdcacm_data_interface = {
167  .bLength = USB_DT_INTERFACE_SIZE,
168  .bDescriptorType = USB_DT_INTERFACE,
169  .bInterfaceNumber = 1,
170  .bAlternateSetting = 0,
171  .bNumEndpoints = 2,
172  .bInterfaceClass = USB_CLASS_DATA,
173  .bInterfaceSubClass = 0,
174  .bInterfaceProtocol = 0,
175  .iInterface = 0,
177  .endpoint = usb_cdcacm_data_endpoints,
178 };
179 
183 static const struct usb_dfu_descriptor usb_dfu_functional = {
184  .bLength = sizeof(struct usb_dfu_descriptor),
185  .bDescriptorType = DFU_FUNCTIONAL,
186  .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
187  .wDetachTimeout = 200,
188  .wTransferSize = sizeof(usbd_control_buffer),
189  .bcdDFUVersion = 0x0110,
190 };
191 
195 static const struct usb_interface_descriptor usb_dfu_interface = {
196  .bLength = USB_DT_INTERFACE_SIZE,
197  .bDescriptorType = USB_DT_INTERFACE,
198  .bInterfaceNumber = 2,
199  .bAlternateSetting = 0,
200  .bNumEndpoints = 0,
201  .bInterfaceClass = 0xFE,
202  .bInterfaceSubClass = 1,
203  .bInterfaceProtocol = 1,
204  .iInterface = 4,
206  .extra = &usb_dfu_functional,
207  .extralen = sizeof(usb_dfu_functional),
208 };
209 
211 static const struct usb_interface usb_cdcacm_interfaces[] = {{
212  .num_altsetting = 1,
213  .altsetting = &usb_cdcacm_communication_interface,
214 }, {
215  .num_altsetting = 1,
216  .altsetting = &usb_cdcacm_data_interface,
217 }, {
218  .num_altsetting = 1,
219  .altsetting = &usb_dfu_interface,
220 }};
221 
223 static const struct usb_config_descriptor usb_cdcacm_configuration_descriptor = {
224  .bLength = USB_DT_CONFIGURATION_SIZE,
225  .bDescriptorType = USB_DT_CONFIGURATION,
226  .wTotalLength = 0,
227  .bNumInterfaces = LENGTH(usb_cdcacm_interfaces),
228  .bConfigurationValue = 1,
229  .iConfiguration = 0,
230  .bmAttributes = 0x80,
231  .bMaxPower = 0xfa,
232  // end of header
233  .interface = usb_cdcacm_interfaces,
234 };
235 
239 static const char *usb_strings[] = {
240  "CuVoodoo",
241  "BusVoodoo multi-protocol debugging adapter",
242 #if BUSVOODOO_HARDWARE_VERSION==0
243  "0",
244 #else
245  (char[]){'A'+BUSVOODOO_HARDWARE_VERSION-1, 0},
246 #endif
247  "DFU bootloader (runtime mode)",
248 };
249 
251 static void usb_disconnect(void)
252 {
253 #if defined(MAPLE_MINI)
254  // disconnect USB D+ using dedicated DISC line/circuit on PB9
255  rcc_periph_clock_enable(RCC_GPIOB);
256  gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO9);
257  gpio_set(GPIOB, GPIO9);
258  for (uint32_t i = 0; i < 0x2000; i++) { // wait for at least 10 ms
259  __asm__("nop");
260  }
261 #else
262  // pull USB D+ low for a short while
263  rcc_periph_clock_enable(RCC_GPIOA);
264  gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
265  gpio_clear(GPIOA, GPIO12);
266  for (uint32_t i = 0; i < 0x2000; i++) { // wait for at least 10 ms
267  __asm__("nop");
268  }
269 #endif
270 }
271 
276 static void usb_dfu_detach(usbd_device *usbd_dev, struct usb_setup_data *req)
277 {
278  (void)usbd_dev; // variable not used
279  (void)req; // variable not used
280  RCC_CSR |= RCC_CSR_RMVF; // clear reset flag for the bootloader to detect the core reset
281  usb_disconnect(); // USB detach (disconnect to force re-enumeration)
282  scb_reset_core(); // reset device (only the core, to the peripheral stay configured)
283  while (true); // wait for the reset to happen
284 }
285 
295 static enum usbd_request_return_codes usb_cdcacm_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))
296 {
297  if (usb_dfu_interface.bInterfaceNumber==req->wIndex) { // check if request is for DFU
298  switch (req->bRequest) {
299  case DFU_DETACH: // USB detach requested
300  *complete = usb_dfu_detach; // detach after reply
301  break;
302  case DFU_GETSTATUS: // get status
303  (*buf)[0] = DFU_STATUS_OK;; // set OK status
304  (*buf)[1] = 0; // set null poll timeout
305  (*buf)[2] = 0; // set null poll timeout
306  (*buf)[3] = 0; // set null poll timeout
307  (*buf)[4] = STATE_APP_IDLE; // application is running
308  (*buf)[5] = 0; // string not used
309  *len = 6; // set length of buffer to return
310  break;
311  default: // other requests are not supported
312  return USBD_REQ_NOTSUPP;
313  }
314  } else if (usb_cdcacm_communication_interface.bInterfaceNumber==req->wIndex) { // check if request is for CDC
315  switch (req->bRequest) {
316  case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
317  usb_cdcacm_connecting = (0!=req->wValue); // check if terminal is open (windows set the control line state before a terminal opens the port, but with value 0)
318  //bool dtr = (req->wValue & (1 << 0)) ? true : false;
319  //bool rts = (req->wValue & (1 << 1)) ? true : false;
320  /* the Linux cdc_acm driver requires this to be implemented
321  * even though it's optional in the CDC spec, and we don't
322  * advertise it in the ACM functional descriptor.
323  */
324  uint8_t reply[10] = {0};
325  struct usb_cdc_notification *notif = (void *)reply;
326  /* we echo signals back to host as notification. */
327  notif->bmRequestType = 0xA1;
328  notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE;
329  notif->wValue = 0;
330  notif->wIndex = 0;
331  notif->wLength = 2;
332  reply[8] = req->wValue & 3;
333  reply[9] = 0;
334  usbd_ep_write_packet(usbd_dev, usb_cdcacm_communication_endpoints[0].bEndpointAddress, reply, LENGTH(reply)); // send the reply from communication endpoint (we can't use the complete callback because we are not using the current control endpoint)
335  break;
336  case USB_CDC_REQ_SET_LINE_CODING:
337  // ignore if length is wrong
338  if (*len < sizeof(struct usb_cdc_line_coding)) {
339  return USBD_REQ_NOTSUPP;
340  }
341  // line coding is ignored
342  // to get the line coding
343  // struct usb_cdc_line_coding *coding = (struct usb_cdc_line_coding *)*buf;
344  break;
345  default:
346  return USBD_REQ_NOTSUPP;
347  }
348  }
349  return USBD_REQ_HANDLED;
350 }
351 
357 static void usb_cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
358 {
359  (void)ep; // not used
360 
361  char usb_data[USB_DATA_TRANSFER_SIZE] = {0}; // buffer to read data
362  uint16_t usb_length = 0; // length of incoming data
363 
364  // receive data
365  usb_length = usbd_ep_read_packet(usbd_dev, usb_cdcacm_data_endpoints[0].bEndpointAddress, usb_data, sizeof(usb_data));
366  if (usb_length) { // copy received data
367  for (uint16_t i=0; i<usb_length && i<LENGTH(usb_data); i++) { // only until buffer is full
368  user_input_store(usb_data[i]); // store user data
369  }
370  }
371 }
372 
378 static void usb_cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
379 {
380  (void)ep; // not used
381  static bool usb_tx_ongoing = false; // if USB transmission is already ongoing
382 
383  if (!usbd_dev && usb_tx_ongoing) { // putchar is trying to send data but a transmission is already ongoing
384  return;
385  }
386  if (0==tx_used || !first_connection) { // verify if we can send and there is something to send
387  usb_tx_ongoing = false; // transmission ended
388  return;
389  }
390  if (!tx_lock) { // ensure no data is in being put in the buffer
391  tx_lock = true; // get the lock (no dead lock should occur since putchar should not be used in interrupts)
392  usb_tx_ongoing = true; // remember we started transmission
393  uint16_t usb_length = (tx_used > USB_DATA_TRANSFER_SIZE ? USB_DATA_TRANSFER_SIZE : tx_used); // length of data to be transmitted (respect max packet size)
394  usb_length = (usb_length > (LENGTH(tx_buffer)-tx_i) ? LENGTH(tx_buffer)-tx_i : usb_length); // since here we use the source array not as ring buffer, only go up to the end
395  usb_length = usbd_ep_write_packet(usb_device, 0x82, (void*)(&tx_buffer[tx_i]), usb_length); // transmit data (put into USB FIFO)
396  tx_i = (tx_i+usb_length)%LENGTH(tx_buffer); // update location on buffer
397  tx_used -= usb_length; // update used size
398  tx_lock = false; // release lock
399  } else {
400  usbd_ep_write_packet(usb_device, usb_cdcacm_data_endpoints[1].bEndpointAddress, NULL, 0); // trigger empty tx for a later callback
401  }
402  usbd_poll(usb_device); // ensure the data gets sent
403 }
404 
410 static void usb_cdcacm_communication_cb(usbd_device *usbd_dev, uint8_t ep)
411 {
412  (void)ep; // not used
413  (void)usbd_dev; // not used
414 
415  first_connection |= usb_cdcacm_connecting; // check if port has been opened
416  if (tx_used>0 && first_connection) { // if buffer is not empty
417  usb_cdcacm_data_tx_cb(NULL, 0); // send available data
418  }
419 }
420 
425 static void usb_cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
426 {
427  (void)wValue; // not used
428 
429  usbd_ep_setup(usbd_dev, usb_cdcacm_communication_endpoints[0].bEndpointAddress, usb_cdcacm_communication_endpoints[0].bmAttributes, usb_cdcacm_communication_endpoints[0].wMaxPacketSize, usb_cdcacm_communication_cb); // set communication endpoint
430  usbd_ep_setup(usbd_dev, usb_cdcacm_data_endpoints[0].bEndpointAddress, usb_cdcacm_data_endpoints[0].bmAttributes, usb_cdcacm_data_endpoints[0].wMaxPacketSize, usb_cdcacm_data_rx_cb); // set outgoing (from host) data endpoint
431  usbd_ep_setup(usbd_dev, usb_cdcacm_data_endpoints[1].bEndpointAddress, usb_cdcacm_data_endpoints[1].bmAttributes, usb_cdcacm_data_endpoints[1].wMaxPacketSize, usb_cdcacm_data_tx_cb); // set incoming (to host) data endpoint
432 
433  usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, usb_cdcacm_control_request);
434 }
435 
437 {
438  // initialize USB
439  rcc_periph_reset_pulse(RST_USB); // reset USB peripheral
440  usb_disconnect(); // disconnect to force re-enumeration
441  rcc_periph_clock_enable(RCC_GPIOA); // enable clock for GPIO used for USB
442  rcc_periph_clock_enable(RCC_USB); // enable clock for USB domain
444  usbd_register_set_config_callback(usb_device, usb_cdcacm_set_config);
445  nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ); // enable interrupts (to not have to poll all the time)
446 
447  // reset buffer states
448  tx_i = 0;
449  tx_used = 0;
450  tx_lock = false; // release lock
451  usb_cdcacm_connecting = false; // clear flag
452  first_connection = false; // reset first connection detection
453 }
454 
455 void usb_cdcacm_putchar(char c)
456 {
457  if (!usb_device) {
458  return;
459  }
460  while (tx_lock); // wait for lock to be released
461  tx_lock = true; // put lock on transmit buffer
462  if (tx_used<LENGTH(tx_buffer)) { // buffer not full
463  tx_buffer[(tx_i+tx_used)%LENGTH(tx_buffer)] = c; // put character in buffer
464  tx_used++; // update used buffer
465  } else { // buffer full (might be that no terminal is connected to this serial)
466  tx_i = (tx_i+1)%LENGTH(tx_buffer); // shift start
467  tx_buffer[(tx_i+tx_used)%LENGTH(tx_buffer)] = c; // overwrite old data
468  }
469  tx_lock = false; // release lock on transmit buffer
470  usb_cdcacm_data_tx_cb(NULL, 0); // send data over USB when possible
471 }
472 
474 void usb_lp_can_rx0_isr(void) {
475  usbd_poll(usb_device);
476 }
#define USB_DATA_TRANSFER_SIZE
maximum packet size for USB data transfer
Definition: usb_cdcacm.c:40
static void usb_cdcacm_communication_cb(usbd_device *usbd_dev, uint8_t ep)
USB CDC ACM communication callback.
Definition: usb_cdcacm.c:410
struct usb_cdc_header_descriptor header
header
Definition: usb_cdcacm.c:110
static volatile uint16_t tx_used
how much data needs to be transmitted
Definition: usb_cdcacm.c:51
struct usb_cdc_union_descriptor cdc_union
descriptor
Definition: usb_cdcacm.c:113
static const struct usb_interface_descriptor usb_dfu_interface
USB DFU interface descriptor.
Definition: usb_cdcacm.c:195
static const struct usb_interface_descriptor usb_cdcacm_communication_interface
USB CDC interface descriptor.
Definition: usb_cdcacm.c:146
static const struct usb_config_descriptor usb_cdcacm_configuration_descriptor
USB CDC ACM configuration descriptor.
Definition: usb_cdcacm.c:223
void user_input_store(char c)
store user input
Definition: global.c:157
volatile bool usb_cdcacm_connecting
flag set to true when user is connected to USB CDC ACM port (e.g.
Definition: usb_cdcacm.c:42
void usb_cdcacm_putchar(char c)
send character over USB (non-blocking)
Definition: usb_cdcacm.c:455
static const struct usb_endpoint_descriptor usb_cdcacm_communication_endpoints[]
USB CDC ACM communication endpoints.
Definition: usb_cdcacm.c:96
static const struct @1 __attribute__((packed))
USB CDC ACM functional descriptor.
Definition: usb_cdcacm.c:114
static volatile bool first_connection
used to detect when the first connection occurred
Definition: usb_cdcacm.c:46
global definitions and methods (API)
static void usb_dfu_detach(usbd_device *usbd_dev, struct usb_setup_data *req)
DFU detach (disconnect USB and perform core reset)
Definition: usb_cdcacm.c:276
static const struct usb_interface_descriptor usb_cdcacm_data_interface
USB CDC ACM data class interface descriptor.
Definition: usb_cdcacm.c:166
static uint8_t usbd_control_buffer[128]
buffer to be used for control requests
Definition: usb_cdcacm.c:44
void usb_cdcacm_setup(void)
setup USB CDC ACM peripheral
Definition: usb_cdcacm.c:436
static enum usbd_request_return_codes usb_cdcacm_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))
incoming USB CDC ACM control request
Definition: usb_cdcacm.c:295
static const struct usb_dfu_descriptor usb_dfu_functional
USB DFU functional descriptor.
Definition: usb_cdcacm.c:183
struct usb_cdc_acm_descriptor acm
descriptor
Definition: usb_cdcacm.c:112
static volatile bool tx_lock
if the transmit buffer is currently being written
Definition: usb_cdcacm.c:52
static const char * usb_strings[]
USB string table.
Definition: usb_cdcacm.c:239
static const struct usb_device_descriptor usb_cdcacm_device_descriptor
USB CDC ACM device descriptor.
Definition: usb_cdcacm.c:57
static void usb_disconnect(void)
disconnect USB by pulling down D+ to for re-enumerate
Definition: usb_cdcacm.c:251
static volatile uint16_t tx_i
current position if transmitted data
Definition: usb_cdcacm.c:50
void usb_lp_can_rx0_isr(void)
USB interrupt service routine called when data is received.
Definition: usb_cdcacm.c:474
struct usb_cdc_call_management_descriptor call_mgmt
call management descriptor
Definition: usb_cdcacm.c:111
static uint8_t tx_buffer[256]
ring buffer for data to transmit
Definition: usb_cdcacm.c:49
library for USB CDC ACM communication (API)
#define LENGTH(x)
get the length of an array
Definition: global.h:26
static const struct usb_endpoint_descriptor usb_cdcacm_data_endpoints[]
USB CDC ACM data endpoints.
Definition: usb_cdcacm.c:77
static usbd_device * usb_device
structure holding all the info related to the USB device
Definition: usb_cdcacm.c:45
static const struct usb_interface usb_cdcacm_interfaces[]
USB CDC ACM interface descriptor.
Definition: usb_cdcacm.c:211
static void usb_cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
USB CDC ACM data received callback.
Definition: usb_cdcacm.c:357
static void usb_cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
USB CDC ACM data transmitted callback.
Definition: usb_cdcacm.c:378
static void usb_cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
set USB CDC ACM configuration
Definition: usb_cdcacm.c:425