LED clock
 All Files Functions Variables Macros Groups Pages
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  */
22 /* standard libraries */
23 #include <stdint.h> // standard integer types
24 #include <stdio.h> // standard I/O facilities
25 #include <stdlib.h> // general utilities
26 
27 /* STM32 (including CM3) libraries */
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/cm3/nvic.h> // interrupt handler
31 #include <libopencm3/cm3/scb.h> // reset utilities
32 #include <libopencmsis/core_cm3.h> // Cortex M3 utilities
33 #include <libopencm3/usb/usbd.h> // USB library
34 #include <libopencm3/usb/cdc.h> // USB CDC library
35 #include <libopencm3/cm3/sync.h> // synchronisation utilities
36 
37 #include "global.h" // global utilities
38 #include "usb_cdcacm.h" // USB CDC ACM header and definitions
39 
43 static const struct usb_device_descriptor device_descriptor = {
44  .bLength = USB_DT_DEVICE_SIZE, // the size of this header in bytes, 18
45  .bDescriptorType = USB_DT_DEVICE, // a value of 1 indicates that this is a device descriptor
46  .bcdUSB = 0x0200, // this device supports USB 2.0
47  .bDeviceClass = USB_CLASS_CDC, // use the CDC device class
48  .bDeviceSubClass = 0, // unused
49  .bDeviceProtocol = 0, // unused
50  .bMaxPacketSize0 = 64, // packet size for endpoint zero in bytes
51  .idVendor = 0xc440, // Vendor ID (CuVo...)
52  .idProduct = 0x0d00, // product ID within the Vendor ID space (...odoo)
53  .bcdDevice = 0x0100, // version number for the device
54  .iManufacturer = 1, // the index of the string in the string table that represents the name of the manufacturer of this device.
55  .iProduct = 2, // the index of the string in the string table that represents the name of the product
56  .iSerialNumber = 3, // the index of the string in the string table that represents the serial number of this item in string form.
57  .bNumConfigurations = 1, // the number of possible configurations this device has
58 };
59 
63 static const struct usb_endpoint_descriptor data_endpoints[] = {{
64  .bLength = USB_DT_ENDPOINT_SIZE, // the size of the endpoint descriptor in bytes
65  .bDescriptorType = USB_DT_ENDPOINT, // a value of 5 indicates that this describes an endpoint
66  .bEndpointAddress = 0x01, // OUT (from host) direction (0<<7), endpoint 1
67  .bmAttributes = USB_ENDPOINT_ATTR_BULK, // bulk mode
68  .wMaxPacketSize = 64, // maximum packet size
69  .bInterval = 1, // the frequency, in number of frames, that we're going to be sending data
70 },{
71  .bLength = USB_DT_ENDPOINT_SIZE, // the size of the endpoint descriptor in bytes
72  .bDescriptorType = USB_DT_ENDPOINT, // a value of 5 indicates that this describes an endpoint
73  .bEndpointAddress = 0x82, // IN (to host) direction (1<<7), endpoint 2
74  .bmAttributes = USB_ENDPOINT_ATTR_BULK, // bulk mode
75  .wMaxPacketSize = 64, // maximum packet size
76  .bInterval = 1, // the frequency, in number of frames, that we're going to be sending data
77 }};
78 
82 static const struct usb_endpoint_descriptor communication_endpoints[] = {{
83  .bLength = USB_DT_ENDPOINT_SIZE, // the size of the endpoint descriptor in bytes
84  .bDescriptorType = USB_DT_ENDPOINT, // a value of 5 indicates that this describes an endpoint
85  .bEndpointAddress = 0x83, // IN (to host) direction (1<<7), endpoint 3
86  .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, // interrupt mode
87  .wMaxPacketSize = 16, // maximum packet size
88  .bInterval = 255, // the frequency, in number of frames, that we're going to be sending data
89 }};
90 
95 static const struct {
96  struct usb_cdc_header_descriptor header;
97  struct usb_cdc_call_management_descriptor call_mgmt;
98  struct usb_cdc_acm_descriptor acm;
99  struct usb_cdc_union_descriptor cdc_union;
100 } __attribute__((packed)) cdcacm_functional_descriptors = {
101  .header = {
102  .bFunctionLength = sizeof(struct usb_cdc_header_descriptor),
103  .bDescriptorType = CS_INTERFACE,
104  .bDescriptorSubtype = USB_CDC_TYPE_HEADER,
105  .bcdCDC = 0x0110,
106  },
107  .call_mgmt = {
108  .bFunctionLength = sizeof(struct usb_cdc_call_management_descriptor),
109  .bDescriptorType = CS_INTERFACE,
110  .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT,
111  .bmCapabilities = 0,
112  .bDataInterface = 1,
113  },
114  .acm = {
115  .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor),
116  .bDescriptorType = CS_INTERFACE,
117  .bDescriptorSubtype = USB_CDC_TYPE_ACM,
118  .bmCapabilities = 0,
119  },
120  .cdc_union = {
121  .bFunctionLength = sizeof(struct usb_cdc_union_descriptor),
122  .bDescriptorType = CS_INTERFACE,
123  .bDescriptorSubtype = USB_CDC_TYPE_UNION,
124  .bControlInterface = 0,
125  .bSubordinateInterface0 = 1,
126  },
127 };
128 
132 static const struct usb_interface_descriptor communication_interface[] = {{
133  .bLength = USB_DT_INTERFACE_SIZE,
134  .bDescriptorType = USB_DT_INTERFACE,
135  .bInterfaceNumber = 0,
136  .bAlternateSetting = 0,
137  .bNumEndpoints = 1,
138  .bInterfaceClass = USB_CLASS_CDC,
139  .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
140  .bInterfaceProtocol = USB_CDC_PROTOCOL_NONE,
141  .iInterface = 0,
142 
143  .endpoint = communication_endpoints,
144 
145  .extra = &cdcacm_functional_descriptors,
146  .extralen = sizeof(cdcacm_functional_descriptors),
147 }};
148 
152 static const struct usb_interface_descriptor data_interface[] = {{
153  .bLength = USB_DT_INTERFACE_SIZE,
154  .bDescriptorType = USB_DT_INTERFACE,
155  .bInterfaceNumber = 1,
156  .bAlternateSetting = 0,
157  .bNumEndpoints = 2,
158  .bInterfaceClass = USB_CLASS_DATA,
159  .bInterfaceSubClass = 0,
160  .bInterfaceProtocol = 0,
161  .iInterface = 0,
162 
163  .endpoint = data_endpoints,
164 }};
165 
167 static const struct usb_interface interfaces[] = {{
168  .num_altsetting = 1,
169  .altsetting = communication_interface,
170 }, {
171  .num_altsetting = 1,
172  .altsetting = data_interface,
173 }};
174 
176 static const struct usb_config_descriptor config = {
177  .bLength = USB_DT_CONFIGURATION_SIZE, // the length of this header in bytes
178  .bDescriptorType = USB_DT_CONFIGURATION, // a value of 2 indicates that this is a configuration descriptor
179  .wTotalLength = 0, // this should hold the total size of the configuration descriptor including all sub interfaces. it is automatically filled in by the USB stack in libopencm3
180  .bNumInterfaces = 2, // the number of interfaces in this configuration
181  .bConfigurationValue = 1, // the index of this configuration
182  .iConfiguration = 0, // a string index describing this configuration (zero means not provided)
183  .bmAttributes = 0x80, // self powered (0<<6), supports remote wakeup (0<<5)
184  .bMaxPower = 0x32, // the maximum amount of current that this device will draw in 2mA units
185  // end of header
186  .interface = interfaces, // pointer to an array of interfaces
187 };
188 
192 static const char *usb_strings[] = {
193  "CuVoodoo",
194  "CDC-ACM",
195  "STM32F1",
196 };
197 
198 static uint8_t usbd_control_buffer[128] = {0};
199 static usbd_device *usb_device = NULL;
200 static bool connected = false;
202 /* input and output ring buffer, indexes, and available memory */
203 static uint8_t rx_buffer[CDCACM_BUFFER] = {0};
204 static volatile uint8_t rx_i = 0;
205 static volatile uint8_t rx_used = 0;
206 mutex_t rx_lock = MUTEX_UNLOCKED;
207 static uint8_t tx_buffer[CDCACM_BUFFER] = {0};
208 static volatile uint8_t tx_i = 0;
209 static volatile uint8_t tx_used = 0;
210 mutex_t tx_lock = MUTEX_UNLOCKED;
211 volatile uint8_t cdcacm_received = 0; // same as rx_used, but since the user can write this variable we don't rely on it
212 
214 static void usb_disconnect(void)
215 {
216  /* short USB disconnect to force re-enumerate */
217 #if defined(SYSTEM_BOARD) || defined(BLUE_PILL)
218  // pull USB D+ low for a short while
219  rcc_periph_clock_enable(RCC_GPIOA);
220  gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
221  gpio_clear(GPIOA, GPIO12);
222  for (uint32_t i = 0; i < 0x2000; i++) {
223  __asm__("nop");
224  }
225 #elif defined(MAPLE_MINI)
226  // disconnect USB D+ using dedicated DISC line/circuit on PB9
227  rcc_periph_clock_enable(RCC_GPIOB);
228  gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO9);
229  gpio_set(GPIOB, GPIO9);
230  for (uint32_t i = 0; i < 0x2000; i++) {
231  __asm__("nop");
232  }
233  gpio_clear(GPIOB, GPIO9);
234 #endif
235 }
236 
246 static int 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))
247 {
248  (void)complete;
249  (void)buf;
250  (void)usbd_dev;
251 
252  switch (req->bRequest) {
253  case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
254  connected = req->wValue ? true : false; // check if terminal is open
255  //bool dtr = (req->wValue & (1 << 0)) ? true : false;
256  //bool rts = (req->wValue & (1 << 1)) ? true : false;
257  /* this Linux cdc_acm driver requires this to be implemented
258  * even though it's optional in the CDC spec, and we don't
259  * advertise it in the ACM functional descriptor.
260  */
261  uint8_t reply[10] = {0};
262  struct usb_cdc_notification *notif = (void *)reply;
263  /* we echo signals back to host as notification. */
264  notif->bmRequestType = 0xA1;
265  notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE;
266  notif->wValue = 0;
267  notif->wIndex = 0;
268  notif->wLength = 2;
269  reply[8] = req->wValue & 3;
270  reply[9] = 0;
271  usbd_ep_write_packet(usbd_dev, 0x83, reply, LENGTH(reply));
272  break;
273  case USB_CDC_REQ_SET_LINE_CODING:
274  // ignore if length is wrong
275  if (*len < sizeof(struct usb_cdc_line_coding)) {
276  return 0;
277  }
278  // get the line coding
279  struct usb_cdc_line_coding *coding = (struct usb_cdc_line_coding *)*buf;
280  /* reset device is the data bits is set to 5
281  * this is used to allowing rebooting the device in DFU mode for reflashing
282  * to reset the device from the host you can use stty --file /dev/ttyACM0 115200 raw cs5
283  */
284  if (coding->bDataBits==5) {
285  usb_disconnect(); // force re-enumerate after reset
286  scb_reset_system(); // reset device
287  while (true); // wait for the reset to happen
288  }
289  break;
290  default:
291  return 0;
292  }
293  return 1;
294 }
295 
300 static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
301 {
302  (void)ep;
303 
304  char usb_data[64] = {0}; // buffer to read data
305  uint16_t usb_length = 0; // length of incoming data
306 
307  /* receive data */
308  usb_length = usbd_ep_read_packet(usbd_dev, 0x01, usb_data, sizeof(usb_data));
309  if (usb_length) { // copy received data
310  for (uint16_t i=0; i<usb_length && rx_used<LENGTH(rx_buffer); i++) { // only until buffer is full
311  rx_buffer[(rx_i+rx_used)%LENGTH(rx_buffer)] = usb_data[i]; // put character in buffer
312  rx_used++; // update used buffer
313  }
314  cdcacm_received = rx_used; // update available data
315  }
316 }
317 
322 static void cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
323 {
324  (void)ep;
325  (void)usbd_dev;
326 
327  if (!usbd_dev || !connected || !tx_used) { // verify if we can send and there is something to send
328  return;
329  }
330  if (mutex_trylock(&tx_lock)) { // try to get lock
331  uint8_t usb_length = (tx_used > 64 ? 64 : tx_used); // length of data to be transmitted (respect max packet size)
332  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
333  while (usb_length != usbd_ep_write_packet(usb_device, 0x82, (void*)(&tx_buffer[tx_i]), usb_length)); // ensure data is written into transmit buffer
334  tx_i = (tx_i+usb_length)%LENGTH(tx_buffer); // update location on buffer
335  tx_used -= usb_length; // update used size
336  mutex_unlock(&tx_lock); // release lock
337  } else {
338  usbd_ep_write_packet(usb_device, 0x82, NULL, 0); // trigger empty tx for a later callback
339  }
340  usbd_poll(usb_device); // ensure the data gets sent
341 }
342 
347 static void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
348 {
349  (void)wValue;
350 
351  usbd_ep_setup(usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_rx_cb);
352  usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_tx_cb);
353  usbd_ep_setup(usbd_dev, 0x83, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
354 
355  usbd_register_control_callback( usbd_dev, USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, cdcacm_control_request);
356 }
357 
358 void cdcacm_setup(void)
359 {
360  connected = false; // start with USB not connected
361  usb_disconnect(); // force re-enumerate (useful after a restart or if there is a bootloader using another USB profile)
362 
363  /* initialize USB */
364  usb_device = usbd_init(&st_usbfs_v1_usb_driver, &device_descriptor, &config, usb_strings, 3, usbd_control_buffer, sizeof(usbd_control_buffer));
365  usbd_register_set_config_callback(usb_device, cdcacm_set_config);
366 
367  /* enable interrupts (to not have to poll all the time) */
368  nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ); // without this USB isn't detected by the host
369 
370  /* reset buffer states */
371  rx_i = 0;
372  rx_used = 0;
373  mutex_unlock(&rx_lock);
374  cdcacm_received = 0;
375  tx_i = 0;
376  tx_used = 0;
377  mutex_unlock(&tx_lock);
378 }
379 
380 char cdcacm_getchar(void)
381 {
382  while (!rx_used) { // idle until data is available
383  __WFI(); // sleep until interrupt (not sure if it's a good idea here)
384  }
385  char to_return = rx_buffer[rx_i]; // get the next available character
386  rx_i = (rx_i+1)%LENGTH(rx_buffer); // update used buffer
387  rx_used--; // update used buffer
388  cdcacm_received = rx_used; // update available data
389  return to_return;
390 }
391 
392 void cdcacm_putchar(char c)
393 {
394  if (!usb_device || !connected) {
395  return;
396  }
397  mutex_lock(&tx_lock); // get lock to prevent race condition
398  if (tx_used<LENGTH(tx_buffer)) { // buffer not full
399  tx_buffer[(tx_i+tx_used)%LENGTH(tx_buffer)] = c; // put character in buffer
400  tx_used++; // update used buffer
401  } else { // buffer full (might be that no terminal is connected to this serial)
402  tx_i = (tx_i+1)%LENGTH(tx_buffer); // shift start
403  tx_buffer[(tx_i+tx_used)%LENGTH(tx_buffer)] = c; // overwrite old data
404  }
405  mutex_unlock(&tx_lock); // release lock
406  usbd_ep_write_packet(usb_device, 0x82, NULL, 0); // trigger tx callback
407 }
408 
410 void usb_lp_can_rx0_isr(void) {
411  usbd_poll(usb_device);
412 }
static int 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:246
static const struct usb_interface interfaces[]
USB CDC ACM interface descriptor.
Definition: usb_cdcacm.c:167
static volatile uint8_t tx_i
Definition: usb_cdcacm.c:208
void cdcacm_putchar(char c)
send character over USB (non-blocking)
Definition: usb_cdcacm.c:392
static uint8_t rx_buffer[CDCACM_BUFFER]
Definition: usb_cdcacm.c:203
static const struct @0 __attribute__((packed))
USB CDC ACM functional descriptor.
Definition: usb_cdcacm.c:100
global definitions and methods
static void cdcacm_data_tx_cb(usbd_device *usbd_dev, uint8_t ep)
USB CDC ACM data transmitted callback.
Definition: usb_cdcacm.c:322
static const struct usb_config_descriptor config
USB CDC ACM configuration descriptor.
Definition: usb_cdcacm.c:176
static const struct usb_endpoint_descriptor data_endpoints[]
USB CDC ACM data endpoints.
Definition: usb_cdcacm.c:63
void cdcacm_setup(void)
setup USB CDC ACM peripheral
Definition: usb_cdcacm.c:358
static uint8_t usbd_control_buffer[128]
Definition: usb_cdcacm.c:198
static volatile uint8_t rx_used
Definition: usb_cdcacm.c:205
mutex_t rx_lock
Definition: usb_cdcacm.c:206
static const struct usb_interface_descriptor communication_interface[]
USB CDC interface descriptor.
Definition: usb_cdcacm.c:132
volatile uint8_t cdcacm_received
Definition: usb_cdcacm.c:211
static const char * usb_strings[]
USB string table.
Definition: usb_cdcacm.c:192
static void usb_disconnect(void)
disconnect USB by pulling down D+ to for re-enumerate
Definition: usb_cdcacm.c:214
static const struct usb_interface_descriptor data_interface[]
USB CDC ACM data class interface descriptor.
Definition: usb_cdcacm.c:152
static void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
set USB CDC ACM configuration
Definition: usb_cdcacm.c:347
static bool connected
Definition: usb_cdcacm.c:200
void usb_lp_can_rx0_isr(void)
USB interrupt service routine called when data is received.
Definition: usb_cdcacm.c:410
static const struct usb_endpoint_descriptor communication_endpoints[]
USB CDC ACM communication endpoints.
Definition: usb_cdcacm.c:82
static uint8_t tx_buffer[CDCACM_BUFFER]
Definition: usb_cdcacm.c:207
library for USB CDC ACM communication (API)
#define LENGTH(x)
Definition: global.h:26
static const struct usb_device_descriptor device_descriptor
USB CDC ACM device descriptor.
Definition: usb_cdcacm.c:43
static volatile uint8_t rx_i
Definition: usb_cdcacm.c:204
#define CDCACM_BUFFER
Definition: usb_cdcacm.h:23
static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
USB CDC ACM data received callback.
Definition: usb_cdcacm.c:300
static volatile uint8_t tx_used
Definition: usb_cdcacm.c:209
static usbd_device * usb_device
Definition: usb_cdcacm.c:199
mutex_t tx_lock
Definition: usb_cdcacm.c:210
char cdcacm_getchar(void)
get character received over USB (blocking)
Definition: usb_cdcacm.c:380