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 #include "busvoodoo_global.h" // to know version
39 #include "print.h" // to set serial number based on version and ID
40 
42 #define USB_DATA_TRANSFER_SIZE 64 // 64 is the maximum for full speed devices
43 
44 volatile bool usb_cdcacm_connecting = false;
45 
46 static uint8_t usbd_control_buffer[128] = {0};
47 static usbd_device *usb_device = NULL;
48 static volatile bool first_connection = false;
50 /* output ring buffer, index, and available memory */
51 static uint8_t tx_buffer[256] = {0};
52 static volatile uint16_t tx_i = 0;
53 static volatile uint16_t tx_used = 0;
54 static volatile bool tx_lock = false;
59 static const struct usb_device_descriptor usb_cdcacm_device_descriptor = {
60  .bLength = USB_DT_DEVICE_SIZE,
61  .bDescriptorType = USB_DT_DEVICE,
62  .bcdUSB = 0x0200,
63  .bDeviceClass = USB_CLASS_CDC,
64  .bDeviceSubClass = 0,
65  .bDeviceProtocol = 0,
66  .bMaxPacketSize0 = 64,
67  .idVendor = 0x1209,
68  .idProduct = 0x4256,
69  .bcdDevice = 0x0000,
70  .iManufacturer = 1,
71  .iProduct = 2,
72  .iSerialNumber = 3,
73  .bNumConfigurations = 1,
74 };
75 
79 static const struct usb_endpoint_descriptor usb_cdcacm_data_endpoints[] = {{
80  .bLength = USB_DT_ENDPOINT_SIZE,
81  .bDescriptorType = USB_DT_ENDPOINT,
82  .bEndpointAddress = 0x02,
83  .bmAttributes = USB_ENDPOINT_ATTR_BULK,
84  .wMaxPacketSize = USB_DATA_TRANSFER_SIZE,
85  .bInterval = 1,
86 },{
87  .bLength = USB_DT_ENDPOINT_SIZE,
88  .bDescriptorType = USB_DT_ENDPOINT,
89  .bEndpointAddress = 0x82,
90  .bmAttributes = USB_ENDPOINT_ATTR_BULK,
91  .wMaxPacketSize = USB_DATA_TRANSFER_SIZE,
92  .bInterval = 1,
93 }};
94 
98 static const struct usb_endpoint_descriptor usb_cdcacm_communication_endpoints[] = {{
99  .bLength = USB_DT_ENDPOINT_SIZE,
100  .bDescriptorType = USB_DT_ENDPOINT,
101  .bEndpointAddress = 0x81,
102  .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
103  .wMaxPacketSize = 16,
104  .bInterval = 255,
105 }};
106 
111 static const struct {
112  struct usb_cdc_header_descriptor header;
113  struct usb_cdc_call_management_descriptor call_mgmt;
114  struct usb_cdc_acm_descriptor acm;
115  struct usb_cdc_union_descriptor cdc_union;
116 } __attribute__((packed)) usb_cdcacm_functional_descriptors = {
117  .header = {
118  .bFunctionLength = sizeof(struct usb_cdc_header_descriptor),
119  .bDescriptorType = CS_INTERFACE,
120  .bDescriptorSubtype = USB_CDC_TYPE_HEADER,
121  .bcdCDC = 0x0110,
122  },
123  .call_mgmt = {
124  .bFunctionLength = sizeof(struct usb_cdc_call_management_descriptor),
125  .bDescriptorType = CS_INTERFACE,
126  .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT,
127  .bmCapabilities = 0,
128  .bDataInterface = 1,
129  },
130  .acm = {
131  .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor),
132  .bDescriptorType = CS_INTERFACE,
133  .bDescriptorSubtype = USB_CDC_TYPE_ACM,
134  .bmCapabilities = 0,
135  },
136  .cdc_union = {
137  .bFunctionLength = sizeof(struct usb_cdc_union_descriptor),
138  .bDescriptorType = CS_INTERFACE,
139  .bDescriptorSubtype = USB_CDC_TYPE_UNION,
140  .bControlInterface = 0,
141  .bSubordinateInterface0 = 1,
142  },
143 };
144 
148 static const struct usb_interface_descriptor usb_cdcacm_communication_interface = {
149  .bLength = USB_DT_INTERFACE_SIZE,
150  .bDescriptorType = USB_DT_INTERFACE,
151  .bInterfaceNumber = 0,
152  .bAlternateSetting = 0,
153  .bNumEndpoints = 1,
154  .bInterfaceClass = USB_CLASS_CDC,
155  .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
156  .bInterfaceProtocol = USB_CDC_PROTOCOL_NONE,
157  .iInterface = 0,
161  .extra = &usb_cdcacm_functional_descriptors,
162  .extralen = sizeof(usb_cdcacm_functional_descriptors),
163 };
164 
168 static const struct usb_interface_descriptor usb_cdcacm_data_interface = {
169  .bLength = USB_DT_INTERFACE_SIZE,
170  .bDescriptorType = USB_DT_INTERFACE,
171  .bInterfaceNumber = 1,
172  .bAlternateSetting = 0,
173  .bNumEndpoints = 2,
174  .bInterfaceClass = USB_CLASS_DATA,
175  .bInterfaceSubClass = 0,
176  .bInterfaceProtocol = 0,
177  .iInterface = 0,
179  .endpoint = usb_cdcacm_data_endpoints,
180 };
181 
185 static const struct usb_dfu_descriptor usb_dfu_functional = {
186  .bLength = sizeof(struct usb_dfu_descriptor),
187  .bDescriptorType = DFU_FUNCTIONAL,
188  .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
189  .wDetachTimeout = 200,
190  .wTransferSize = sizeof(usbd_control_buffer),
191  .bcdDFUVersion = 0x0110,
192 };
193 
197 static const struct usb_interface_descriptor usb_dfu_interface = {
198  .bLength = USB_DT_INTERFACE_SIZE,
199  .bDescriptorType = USB_DT_INTERFACE,
200  .bInterfaceNumber = 2,
201  .bAlternateSetting = 0,
202  .bNumEndpoints = 0,
203  .bInterfaceClass = 0xFE,
204  .bInterfaceSubClass = 1,
205  .bInterfaceProtocol = 1,
206  .iInterface = 4,
208  .extra = &usb_dfu_functional,
209  .extralen = sizeof(usb_dfu_functional),
210 };
211 
213 static const struct usb_interface usb_cdcacm_interfaces[] = {{
214  .num_altsetting = 1,
215  .altsetting = &usb_cdcacm_communication_interface,
216 }, {
217  .num_altsetting = 1,
218  .altsetting = &usb_cdcacm_data_interface,
219 }, {
220  .num_altsetting = 1,
221  .altsetting = &usb_dfu_interface,
222 }};
223 
225 static const struct usb_config_descriptor usb_cdcacm_configuration_descriptor = {
226  .bLength = USB_DT_CONFIGURATION_SIZE,
227  .bDescriptorType = USB_DT_CONFIGURATION,
228  .wTotalLength = 0,
229  .bNumInterfaces = LENGTH(usb_cdcacm_interfaces),
230  .bConfigurationValue = 1,
231  .iConfiguration = 0,
232  .bmAttributes = 0x80,
233  .bMaxPower = 0xfa,
234  // end of header
235  .interface = usb_cdcacm_interfaces,
236 };
237 
241 static const char *usb_strings[] = {
242  "CuVoodoo",
243  "BusVoodoo multi-protocol debugging adapter",
244 #if BUSVOODOO_HARDWARE_VERSION==0
245  "0",
246 #else
247  (char[]){'A'+BUSVOODOO_HARDWARE_VERSION-1, 0},
248 #endif
249  "DFU bootloader (runtime mode)",
250 };
251 
253 static void usb_disconnect(void)
254 {
255 #if defined(MAPLE_MINI)
256  // disconnect USB D+ using dedicated DISC line/circuit on PB9
257  rcc_periph_clock_enable(RCC_GPIOB);
258  gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO9);
259  gpio_set(GPIOB, GPIO9);
260  for (uint32_t i = 0; i < 0x2000; i++) { // wait for at least 10 ms
261  __asm__("nop");
262  }
263 #else
264  // pull USB D+ low for a short while
265  rcc_periph_clock_enable(RCC_GPIOA);
266  gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
267  gpio_clear(GPIOA, GPIO12);
268  for (uint32_t i = 0; i < 0x2000; i++) { // wait for at least 10 ms
269  __asm__("nop");
270  }
271 #endif
272 }
273 
278 static void usb_dfu_detach(usbd_device *usbd_dev, struct usb_setup_data *req)
279 {
280  (void)usbd_dev; // variable not used
281  (void)req; // variable not used
282  RCC_CSR |= RCC_CSR_RMVF; // clear reset flag for the bootloader to detect the core reset
283  usb_disconnect(); // USB detach (disconnect to force re-enumeration)
284  scb_reset_core(); // reset device (only the core, to the peripheral stay configured)
285  while (true); // wait for the reset to happen
286 }
287 
297 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))
298 {
299  if (usb_dfu_interface.bInterfaceNumber==req->wIndex) { // check if request is for DFU
300  switch (req->bRequest) {
301  case DFU_DETACH: // USB detach requested
302  *complete = usb_dfu_detach; // detach after reply
303  break;
304  case DFU_GETSTATUS: // get status
305  (*buf)[0] = DFU_STATUS_OK;; // set OK status
306  (*buf)[1] = 0; // set null poll timeout
307  (*buf)[2] = 0; // set null poll timeout
308  (*buf)[3] = 0; // set null poll timeout
309  (*buf)[4] = STATE_APP_IDLE; // application is running
310  (*buf)[5] = 0; // string not used
311  *len = 6; // set length of buffer to return
312  break;
313  default: // other requests are not supported
314  return USBD_REQ_NOTSUPP;
315  }
316  } else if (usb_cdcacm_communication_interface.bInterfaceNumber==req->wIndex) { // check if request is for CDC
317  switch (req->bRequest) {
318  case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
319  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)
320  //bool dtr = (req->wValue & (1 << 0)) ? true : false;
321  //bool rts = (req->wValue & (1 << 1)) ? true : false;
322  /* the Linux cdc_acm driver requires this to be implemented
323  * even though it's optional in the CDC spec, and we don't
324  * advertise it in the ACM functional descriptor.
325  */
326  uint8_t reply[10] = {0};
327  struct usb_cdc_notification *notif = (void *)reply;
328  /* we echo signals back to host as notification. */
329  notif->bmRequestType = 0xA1;
330  notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE;
331  notif->wValue = 0;
332  notif->wIndex = 0;
333  notif->wLength = 2;
334  reply[8] = req->wValue & 3;
335  reply[9] = 0;
336  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)
337  break;
338  case USB_CDC_REQ_SET_LINE_CODING:
339  // ignore if length is wrong
340  if (*len < sizeof(struct usb_cdc_line_coding)) {
341  return USBD_REQ_NOTSUPP;
342  }
343  // line coding is ignored
344  // to get the line coding
345  // struct usb_cdc_line_coding *coding = (struct usb_cdc_line_coding *)*buf;
346  break;
347  default:
348  return USBD_REQ_NOTSUPP;
349  }
350  }
351  return USBD_REQ_HANDLED;
352 }
353 
359 static void usb_cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
360 {
361  (void)ep; // not used
362 
363  char usb_data[USB_DATA_TRANSFER_SIZE] = {0}; // buffer to read data
364  uint16_t usb_length = 0; // length of incoming data
365 
366  // receive data
367  usb_length = usbd_ep_read_packet(usbd_dev, usb_cdcacm_data_endpoints[0].bEndpointAddress, usb_data, sizeof(usb_data));
368  if (usb_length) { // copy received data
369  for (uint16_t i=0; i<usb_length && i<LENGTH(usb_data); i++) { // only until buffer is full
370  user_input_store(usb_data[i]); // store user data
371  }
372  }
373 }
374 
380 static void usb_cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
381 {
382  (void)ep; // not used
383  static bool usb_tx_ongoing = false; // if USB transmission is already ongoing
384 
385  if (!usbd_dev && usb_tx_ongoing) { // putchar is trying to send data but a transmission is already ongoing
386  return;
387  }
388  if (0==tx_used || !first_connection) { // verify if we can send and there is something to send
389  usb_tx_ongoing = false; // transmission ended
390  return;
391  }
392  if (!tx_lock) { // ensure no data is in being put in the buffer
393  tx_lock = true; // get the lock (no dead lock should occur since putchar should not be used in interrupts)
394  usb_tx_ongoing = true; // remember we started transmission
395  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)
396  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
397  usb_length = usbd_ep_write_packet(usb_device, 0x82, (void*)(&tx_buffer[tx_i]), usb_length); // transmit data (put into USB FIFO)
398  tx_i = (tx_i+usb_length)%LENGTH(tx_buffer); // update location on buffer
399  tx_used -= usb_length; // update used size
400  tx_lock = false; // release lock
401  } else {
402  usbd_ep_write_packet(usb_device, usb_cdcacm_data_endpoints[1].bEndpointAddress, NULL, 0); // trigger empty tx for a later callback
403  }
404  usbd_poll(usb_device); // ensure the data gets sent
405 }
406 
412 static void usb_cdcacm_communication_cb(usbd_device *usbd_dev, uint8_t ep)
413 {
414  (void)ep; // not used
415  (void)usbd_dev; // not used
416 
417  first_connection |= usb_cdcacm_connecting; // check if port has been opened
418  if (tx_used>0 && first_connection) { // if buffer is not empty
419  usb_cdcacm_data_tx_cb(NULL, 0); // send available data
420  }
421 }
422 
427 static void usb_cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
428 {
429  (void)wValue; // not used
430 
431  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
432  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
433  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
434 
435  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);
436 }
437 
439 {
440  // set serial
441  static char serial[] = "H_YYYY-MM-DD_AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
442  snprintf(serial, sizeof(serial), "%c_%04u-%02u-%02u_%08x%08x%08x", busvoodoo_version, BUILD_YEAR, BUILD_MONTH, BUILD_DAY, DESIG_UNIQUE_ID0, DESIG_UNIQUE_ID1, DESIG_UNIQUE_ID2);
443  usb_strings[2] = serial;
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  first_connection = false; // reset first connection detection
460 }
461 
462 void usb_cdcacm_putchar(char c)
463 {
464  if (!usb_device) {
465  return;
466  }
467  while (tx_lock); // wait for lock to be released
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  usb_cdcacm_data_tx_cb(NULL, 0); // send data over USB when possible
478 }
479 
481 void usb_lp_can_rx0_isr(void) {
482  usbd_poll(usb_device);
483 }
#define USB_DATA_TRANSFER_SIZE
maximum packet size for USB data transfer
Definition: usb_cdcacm.c:42
static void usb_cdcacm_communication_cb(usbd_device *usbd_dev, uint8_t ep)
USB CDC ACM communication callback.
Definition: usb_cdcacm.c:412
struct usb_cdc_header_descriptor header
header
Definition: usb_cdcacm.c:112
BusVoodoo global definitions and methods (API)
static volatile uint16_t tx_used
how much data needs to be transmitted
Definition: usb_cdcacm.c:53
struct usb_cdc_union_descriptor cdc_union
descriptor
Definition: usb_cdcacm.c:115
static const struct usb_interface_descriptor usb_dfu_interface
USB DFU interface descriptor.
Definition: usb_cdcacm.c:197
static const struct usb_interface_descriptor usb_cdcacm_communication_interface
USB CDC interface descriptor.
Definition: usb_cdcacm.c:148
static const struct usb_config_descriptor usb_cdcacm_configuration_descriptor
USB CDC ACM configuration descriptor.
Definition: usb_cdcacm.c:225
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:44
void usb_cdcacm_putchar(char c)
send character over USB (non-blocking)
Definition: usb_cdcacm.c:462
static const struct usb_endpoint_descriptor usb_cdcacm_communication_endpoints[]
USB CDC ACM communication endpoints.
Definition: usb_cdcacm.c:98
static const struct @1 __attribute__((packed))
USB CDC ACM functional descriptor.
Definition: usb_cdcacm.c:116
static volatile bool first_connection
used to detect when the first connection occurred
Definition: usb_cdcacm.c:48
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:278
static const struct usb_interface_descriptor usb_cdcacm_data_interface
USB CDC ACM data class interface descriptor.
Definition: usb_cdcacm.c:168
static uint8_t usbd_control_buffer[128]
buffer to be used for control requests
Definition: usb_cdcacm.c:46
void usb_cdcacm_setup(void)
setup USB CDC ACM peripheral
Definition: usb_cdcacm.c:438
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:297
static const struct usb_dfu_descriptor usb_dfu_functional
USB DFU functional descriptor.
Definition: usb_cdcacm.c:185
struct usb_cdc_acm_descriptor acm
descriptor
Definition: usb_cdcacm.c:114
static volatile bool tx_lock
if the transmit buffer is currently being written
Definition: usb_cdcacm.c:54
static const char * usb_strings[]
USB string table.
Definition: usb_cdcacm.c:241
static const struct usb_device_descriptor usb_cdcacm_device_descriptor
USB CDC ACM device descriptor.
Definition: usb_cdcacm.c:59
#define BUILD_MONTH
build month as number if known, or 0 if unknown
Definition: global.h:94
static void usb_disconnect(void)
disconnect USB by pulling down D+ to for re-enumerate
Definition: usb_cdcacm.c:253
static volatile uint16_t tx_i
current position if transmitted data
Definition: usb_cdcacm.c:52
void usb_lp_can_rx0_isr(void)
USB interrupt service routine called when data is received.
Definition: usb_cdcacm.c:481
struct usb_cdc_call_management_descriptor call_mgmt
call management descriptor
Definition: usb_cdcacm.c:113
static uint8_t tx_buffer[256]
ring buffer for data to transmit
Definition: usb_cdcacm.c:51
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:79
static usbd_device * usb_device
structure holding all the info related to the USB device
Definition: usb_cdcacm.c:47
static const struct usb_interface usb_cdcacm_interfaces[]
USB CDC ACM interface descriptor.
Definition: usb_cdcacm.c:213
#define BUILD_YEAR
build year as number if known, or 0 if unknown
Definition: global.h:92
static void usb_cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
USB CDC ACM data received callback.
Definition: usb_cdcacm.c:359
char busvoodoo_version
version of the hardware board
static void usb_cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
USB CDC ACM data transmitted callback.
Definition: usb_cdcacm.c:380
#define BUILD_DAY
build day as number if known, or 0 if unknown
Definition: global.h:96
static void usb_cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
set USB CDC ACM configuration
Definition: usb_cdcacm.c:427