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 usb_tx_ongoing = false;
47 static volatile bool first_connection = false;
49 /* output ring buffer, index, and available memory */
50 static uint8_t tx_buffer[512] = {0};
51 static volatile uint16_t tx_i = 0;
52 static volatile uint16_t tx_used = 0;
53 static volatile bool tx_lock = false;
58 static const struct usb_device_descriptor usb_cdcacm_device_descriptor = {
59  .bLength = USB_DT_DEVICE_SIZE,
60  .bDescriptorType = USB_DT_DEVICE,
61  .bcdUSB = 0x0200,
62  .bDeviceClass = USB_CLASS_CDC,
63  .bDeviceSubClass = 0,
64  .bDeviceProtocol = 0,
65  .bMaxPacketSize0 = 64,
66  .idVendor = 0x1209,
67  .idProduct = 0x4256,
68  .bcdDevice = 0x0000,
69  .iManufacturer = 1,
70  .iProduct = 2,
71  .iSerialNumber = 0,
72  .bNumConfigurations = 1,
73 };
74 
78 static const struct usb_endpoint_descriptor usb_cdcacm_data_endpoints[] = {{
79  .bLength = USB_DT_ENDPOINT_SIZE,
80  .bDescriptorType = USB_DT_ENDPOINT,
81  .bEndpointAddress = 0x02,
82  .bmAttributes = USB_ENDPOINT_ATTR_BULK,
83  .wMaxPacketSize = USB_DATA_TRANSFER_SIZE,
84  .bInterval = 1,
85 },{
86  .bLength = USB_DT_ENDPOINT_SIZE,
87  .bDescriptorType = USB_DT_ENDPOINT,
88  .bEndpointAddress = 0x82,
89  .bmAttributes = USB_ENDPOINT_ATTR_BULK,
90  .wMaxPacketSize = USB_DATA_TRANSFER_SIZE,
91  .bInterval = 1,
92 }};
93 
97 static const struct usb_endpoint_descriptor usb_cdcacm_communication_endpoints[] = {{
98  .bLength = USB_DT_ENDPOINT_SIZE,
99  .bDescriptorType = USB_DT_ENDPOINT,
100  .bEndpointAddress = 0x81,
101  .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
102  .wMaxPacketSize = 16,
103  .bInterval = 255,
104 }};
105 
110 static const struct {
111  struct usb_cdc_header_descriptor header;
112  struct usb_cdc_call_management_descriptor call_mgmt;
113  struct usb_cdc_acm_descriptor acm;
114  struct usb_cdc_union_descriptor cdc_union;
115 } __attribute__((packed)) usb_cdcacm_functional_descriptors = {
116  .header = {
117  .bFunctionLength = sizeof(struct usb_cdc_header_descriptor),
118  .bDescriptorType = CS_INTERFACE,
119  .bDescriptorSubtype = USB_CDC_TYPE_HEADER,
120  .bcdCDC = 0x0110,
121  },
122  .call_mgmt = {
123  .bFunctionLength = sizeof(struct usb_cdc_call_management_descriptor),
124  .bDescriptorType = CS_INTERFACE,
125  .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT,
126  .bmCapabilities = 0,
127  .bDataInterface = 1,
128  },
129  .acm = {
130  .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor),
131  .bDescriptorType = CS_INTERFACE,
132  .bDescriptorSubtype = USB_CDC_TYPE_ACM,
133  .bmCapabilities = 0,
134  },
135  .cdc_union = {
136  .bFunctionLength = sizeof(struct usb_cdc_union_descriptor),
137  .bDescriptorType = CS_INTERFACE,
138  .bDescriptorSubtype = USB_CDC_TYPE_UNION,
139  .bControlInterface = 0,
140  .bSubordinateInterface0 = 1,
141  },
142 };
143 
147 static const struct usb_interface_descriptor usb_cdcacm_communication_interface = {
148  .bLength = USB_DT_INTERFACE_SIZE,
149  .bDescriptorType = USB_DT_INTERFACE,
150  .bInterfaceNumber = 0,
151  .bAlternateSetting = 0,
152  .bNumEndpoints = 1,
153  .bInterfaceClass = USB_CLASS_CDC,
154  .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
155  .bInterfaceProtocol = USB_CDC_PROTOCOL_NONE,
156  .iInterface = 0,
157 
159 
160  .extra = &usb_cdcacm_functional_descriptors,
161  .extralen = sizeof(usb_cdcacm_functional_descriptors),
162 };
163 
167 static const struct usb_interface_descriptor usb_cdcacm_data_interface = {
168  .bLength = USB_DT_INTERFACE_SIZE,
169  .bDescriptorType = USB_DT_INTERFACE,
170  .bInterfaceNumber = 1,
171  .bAlternateSetting = 0,
172  .bNumEndpoints = 2,
173  .bInterfaceClass = USB_CLASS_DATA,
174  .bInterfaceSubClass = 0,
175  .bInterfaceProtocol = 0,
176  .iInterface = 0,
178  .endpoint = usb_cdcacm_data_endpoints,
179 };
180 
184 static const struct usb_dfu_descriptor usb_dfu_functional = {
185  .bLength = sizeof(struct usb_dfu_descriptor),
186  .bDescriptorType = DFU_FUNCTIONAL,
187  .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
188  .wDetachTimeout = 200,
189  .wTransferSize = sizeof(usbd_control_buffer),
190  .bcdDFUVersion = 0x0110,
191 };
192 
196 static const struct usb_interface_descriptor usb_dfu_interface = {
197  .bLength = USB_DT_INTERFACE_SIZE,
198  .bDescriptorType = USB_DT_INTERFACE,
199  .bInterfaceNumber = 2,
200  .bAlternateSetting = 0,
201  .bNumEndpoints = 0,
202  .bInterfaceClass = 0xFE,
203  .bInterfaceSubClass = 1,
204  .bInterfaceProtocol = 1,
205  .iInterface = 3,
207  .extra = &usb_dfu_functional,
208  .extralen = sizeof(usb_dfu_functional),
209 };
210 
212 static const struct usb_interface usb_cdcacm_interfaces[] = {{
213  .num_altsetting = 1,
214  .altsetting = &usb_cdcacm_communication_interface,
215 }, {
216  .num_altsetting = 1,
217  .altsetting = &usb_cdcacm_data_interface,
218 }, {
219  .num_altsetting = 1,
220  .altsetting = &usb_dfu_interface,
221 }};
222 
224 static const struct usb_config_descriptor usb_cdcacm_configuration_descriptor = {
225  .bLength = USB_DT_CONFIGURATION_SIZE,
226  .bDescriptorType = USB_DT_CONFIGURATION,
227  .wTotalLength = 0,
228  .bNumInterfaces = LENGTH(usb_cdcacm_interfaces),
229  .bConfigurationValue = 1,
230  .iConfiguration = 0,
231  .bmAttributes = 0x80,
232  .bMaxPower = 0xfa,
233  // end of header
234  .interface = usb_cdcacm_interfaces,
235 };
236 
240 static const char *usb_strings[] = {
241  "CuVoodoo",
242  "BusVoodoo multi-protocol debugging adapter",
243  "DFU bootloader (runtime mode)",
244 };
245 
247 static void usb_disconnect(void)
248 {
249 #if defined(MAPLE_MINI)
250  // disconnect USB D+ using dedicated DISC line/circuit on PB9
251  rcc_periph_clock_enable(RCC_GPIOB);
252  gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO9);
253  gpio_set(GPIOB, GPIO9);
254  for (uint32_t i = 0; i < 0x2000; i++) { // wait for at least 10 ms
255  __asm__("nop");
256  }
257 #else
258  // pull USB D+ low for a short while
259  rcc_periph_clock_enable(RCC_GPIOA);
260  gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
261  gpio_clear(GPIOA, GPIO12);
262  for (uint32_t i = 0; i < 0x2000; i++) { // wait for at least 10 ms
263  __asm__("nop");
264  }
265 #endif
266 }
267 
272 static void usb_reset(usbd_device *usbd_dev, struct usb_setup_data *req)
273 {
274  (void)usbd_dev; // variable not used
275  (void)req; // variable not used
276 
277  usb_disconnect(); // USB detach (disconnect to force re-enumeration)
278  scb_reset_system(); // reset device
279  while (true); // wait for the reset to happen
280 }
281 
286 static void usb_dfu_detach(usbd_device *usbd_dev, struct usb_setup_data *req)
287 {
288  (void)usbd_dev; // variable not used
289  (void)req; // variable not used
290  RCC_CSR |= RCC_CSR_RMVF; // clear reset flag for the bootloader to detect the core reset
291  usb_disconnect(); // USB detach (disconnect to force re-enumeration)
292  scb_reset_core(); // reset device (only the core, to the peripheral stay configured)
293  while (true); // wait for the reset to happen
294 }
295 
301 static void usb_cdcacm_control_cb(usbd_device *usbd_dev, struct usb_setup_data *req)
302 {
303  first_connection |= (0!=req->wValue); // check if port has been opened (windows set the control line state before a terminal opens the port, but with value 0)
304  if (usbd_dev && tx_used>0 && !usb_tx_ongoing && first_connection) { // if buffer is not empty
305  usbd_ep_write_packet(usbd_dev, 0x82, NULL, 0); // trigger tx callback
306  }
307 }
308 
318 static int 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))
319 {
320  if (usb_dfu_interface.bInterfaceNumber==req->wIndex) { // check if request is for DFU
321  switch (req->bRequest) {
322  case DFU_DETACH: // USB detach requested
323  *complete = usb_dfu_detach; // detach after reply
324  break;
325  case DFU_GETSTATUS: // get status
326  (*buf)[0] = DFU_STATUS_OK;; // set OK status
327  (*buf)[1] = 0; // set null poll timeout
328  (*buf)[2] = 0; // set null poll timeout
329  (*buf)[3] = 0; // set null poll timeout
330  (*buf)[4] = STATE_APP_IDLE; // application is running
331  (*buf)[5] = 0; // string not used
332  *len = 6; // set length of buffer to return
333  break;
334  default: // other requests are not supported
335  return 0;
336  }
337  } else {
338  switch (req->bRequest) {
339  case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
340  usb_cdcacm_connecting = (0!=req->wValue); // check if terminal is open
341  //bool dtr = (req->wValue & (1 << 0)) ? true : false;
342  //bool rts = (req->wValue & (1 << 1)) ? true : false;
343  /* the Linux cdc_acm driver requires this to be implemented
344  * even though it's optional in the CDC spec, and we don't
345  * advertise it in the ACM functional descriptor.
346  */
347  uint8_t reply[10] = {0};
348  struct usb_cdc_notification *notif = (void *)reply;
349  /* we echo signals back to host as notification. */
350  notif->bmRequestType = 0xA1;
351  notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE;
352  notif->wValue = 0;
353  notif->wIndex = 0;
354  notif->wLength = 2;
355  reply[8] = req->wValue & 3;
356  reply[9] = 0;
357  usbd_ep_write_packet(usbd_dev, 0x81, reply, LENGTH(reply)); // send the reply
358  *complete = usb_cdcacm_control_cb; // check state once reply is transmitted
359  break;
360  case USB_CDC_REQ_SET_LINE_CODING:
361  // ignore if length is wrong
362  if (*len < sizeof(struct usb_cdc_line_coding)) {
363  return 0;
364  }
365  // get the line coding
366  struct usb_cdc_line_coding *coding = (struct usb_cdc_line_coding *)*buf;
367  /* reset device is the data bits is set to 5
368  * to reset the device from the host you can use stty --file /dev/ttyACM0 raw cs5
369  */
370  if (coding->bDataBits==5) {
371  *complete = usb_reset; // perform reset after reply
372  }
373  break;
374  default:
375  return 0;
376  }
377  }
378  return 1;
379 }
380 
386 static void usb_cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
387 {
388  (void)ep; // not used
389 
390  char usb_data[USB_DATA_TRANSFER_SIZE] = {0}; // buffer to read data
391  uint16_t usb_length = 0; // length of incoming data
392 
393  // receive data
394  usb_length = usbd_ep_read_packet(usbd_dev, 0x02, usb_data, sizeof(usb_data));
395  if (usb_length) { // copy received data
396  for (uint16_t i=0; i<usb_length && i<LENGTH(usb_data); i++) { // only until buffer is full
397  user_input_store(usb_data[i]); // store user data
398  }
399  }
400 }
401 
407 static void usb_cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
408 {
409  (void)ep; // not used
410 
411  if (!usbd_dev || 0==tx_used || !first_connection) { // verify if we can send and there is something to send
412  usb_tx_ongoing = false; // transmission ended
413  return;
414  }
415  if (!tx_lock) {
416  usb_tx_ongoing = true; // remember we started transmission
417  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)
418  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
419  while (usb_length != usbd_ep_write_packet(usb_device, 0x82, (void*)(&tx_buffer[tx_i]), usb_length)); // ensure data is written into transmit buffer
420  tx_i = (tx_i+usb_length)%LENGTH(tx_buffer); // update location on buffer
421  tx_used -= usb_length; // update used size
422  } else {
423  usbd_ep_write_packet(usb_device, 0x82, NULL, 0); // trigger empty tx for a later callback
424  }
425  usbd_poll(usb_device); // ensure the data gets sent
426 }
427 
432 static void usb_cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
433 {
434  (void)wValue; // not used
435 
436  usbd_ep_setup(usbd_dev, usb_cdcacm_communication_endpoints[0].bEndpointAddress, usb_cdcacm_communication_endpoints[0].bmAttributes, usb_cdcacm_communication_endpoints[0].wMaxPacketSize, NULL); // set communication endpoint
437  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
438  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
439 
440  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);
441 }
442 
444 {
445  // initialize USB
446  rcc_periph_reset_pulse(RST_USB); // reset USB peripheral
447  usb_disconnect(); // disconnect to force re-enumeration
448  rcc_periph_clock_enable(RCC_GPIOA); // enable clock for GPIO used for USB
449  rcc_periph_clock_enable(RCC_USB); // enable clock for USB domain
451  usbd_register_set_config_callback(usb_device, usb_cdcacm_set_config);
452  nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ); // enable interrupts (to not have to poll all the time)
453 
454  // reset buffer states
455  tx_i = 0;
456  tx_used = 0;
457  tx_lock = false; // release lock
458  usb_cdcacm_connecting = false; // clear flag
459  usb_tx_ongoing = false; // clear transmission lock
460  first_connection = false; // reset first connection detection
461 }
462 
463 void usb_cdcacm_putchar(char c)
464 {
465  if (!usb_device) {
466  return;
467  }
468  tx_lock = true; // put lock on transmit buffer
469  if (tx_used<LENGTH(tx_buffer)) { // buffer not full
470  tx_buffer[(tx_i+tx_used)%LENGTH(tx_buffer)] = c; // put character in buffer
471  tx_used++; // update used buffer
472  } else { // buffer full (might be that no terminal is connected to this serial)
473  tx_i = (tx_i+1)%LENGTH(tx_buffer); // shift start
474  tx_buffer[(tx_i+tx_used)%LENGTH(tx_buffer)] = c; // overwrite old data
475  }
476  tx_lock = false; // release lock on transmit buffer
477  if (tx_used>0 && usb_device && !usb_tx_ongoing) { // if buffer is not empty anymore
478  usbd_ep_write_packet(usb_device, 0x82, NULL, 0); // trigger tx callback
479  }
480 }
481 
483 void usb_lp_can_rx0_isr(void) {
484  usbd_poll(usb_device);
485 }
#define USB_DATA_TRANSFER_SIZE
maximum packet size for USB data transfer
Definition: usb_cdcacm.c:40
struct usb_cdc_header_descriptor header
header
Definition: usb_cdcacm.c:111
static volatile uint16_t tx_used
how much data needs to be transmitted
Definition: usb_cdcacm.c:52
struct usb_cdc_union_descriptor cdc_union
descriptor
Definition: usb_cdcacm.c:114
static const struct usb_interface_descriptor usb_dfu_interface
USB DFU interface descriptor.
Definition: usb_cdcacm.c:196
static const struct usb_interface_descriptor usb_cdcacm_communication_interface
USB CDC interface descriptor.
Definition: usb_cdcacm.c:147
static const struct usb_config_descriptor usb_cdcacm_configuration_descriptor
USB CDC ACM configuration descriptor.
Definition: usb_cdcacm.c:224
void user_input_store(char c)
store user input
Definition: global.c:199
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:463
static const struct usb_endpoint_descriptor usb_cdcacm_communication_endpoints[]
USB CDC ACM communication endpoints.
Definition: usb_cdcacm.c:97
static const struct @1 __attribute__((packed))
USB CDC ACM functional descriptor.
Definition: usb_cdcacm.c:115
static volatile bool first_connection
used to detect when the first connection occurred
Definition: usb_cdcacm.c:47
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:286
static const struct usb_interface_descriptor usb_cdcacm_data_interface
USB CDC ACM data class interface descriptor.
Definition: usb_cdcacm.c:167
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:443
static const struct usb_dfu_descriptor usb_dfu_functional
USB DFU functional descriptor.
Definition: usb_cdcacm.c:184
static void usb_cdcacm_control_cb(usbd_device *usbd_dev, struct usb_setup_data *req)
USB CDC ACM control callback.
Definition: usb_cdcacm.c:301
struct usb_cdc_acm_descriptor acm
descriptor
Definition: usb_cdcacm.c:113
static volatile bool tx_lock
if the transmit buffer is currently being written
Definition: usb_cdcacm.c:53
static const char * usb_strings[]
USB string table.
Definition: usb_cdcacm.c:240
static const struct usb_device_descriptor usb_cdcacm_device_descriptor
USB CDC ACM device descriptor.
Definition: usb_cdcacm.c:58
static void usb_reset(usbd_device *usbd_dev, struct usb_setup_data *req)
disconnect USB and perform system reset
Definition: usb_cdcacm.c:272
static void usb_disconnect(void)
disconnect USB by pulling down D+ to for re-enumerate
Definition: usb_cdcacm.c:247
static volatile uint16_t tx_i
current position if transmitted data
Definition: usb_cdcacm.c:51
void usb_lp_can_rx0_isr(void)
USB interrupt service routine called when data is received.
Definition: usb_cdcacm.c:483
static int 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:318
struct usb_cdc_call_management_descriptor call_mgmt
call management descriptor
Definition: usb_cdcacm.c:112
static volatile bool usb_tx_ongoing
if USB transmission is already ongoing
Definition: usb_cdcacm.c:46
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:78
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:212
static void usb_cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
USB CDC ACM data received callback.
Definition: usb_cdcacm.c:386
static void usb_cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
USB CDC ACM data transmitted callback.
Definition: usb_cdcacm.c:407
static void usb_cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
set USB CDC ACM configuration
Definition: usb_cdcacm.c:432
static uint8_t tx_buffer[512]
ring buffer for data to transmit
Definition: usb_cdcacm.c:50