librfn
An ad-hoc utility library
console_cdcacm.c
Go to the documentation of this file.
1 /*
2  * Part of librfn (a general utility library from redfelineninja.org.uk)
3  *
4  * This file was derived from libopencm3's cdcacm example.
5  *
6  * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
7  * Copyright (C) 2014 Daniel Thompson <daniel@redfelineninja.org.uk>
8  *
9  * This library is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as published
11  * by the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this library. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <errno.h>
24 #include <inttypes.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <libopencm3/stm32/desig.h>
28 #include <libopencm3/stm32/gpio.h>
29 #include <libopencm3/stm32/rcc.h>
30 #include <libopencm3/usb/usbd.h>
31 #include <libopencm3/usb/cdc.h>
32 #include <libopencm3/cm3/scb.h>
33 #include <librfn/console.h>
34 #include <librfn/fibre.h>
35 #include <librfn/ringbuf.h>
36 #include <librfn/time.h>
37 #include <librfn/util.h>
38 
39 #ifndef CONFIG_USB_MAX_POWER
40 #define CONFIG_USB_MAX_POWER 100
41 #endif
42 
43 static const struct usb_device_descriptor desc = {
44  .bLength = USB_DT_DEVICE_SIZE,
45  .bDescriptorType = USB_DT_DEVICE,
46  .bcdUSB = 0x0200,
47  .bDeviceClass = USB_CLASS_CDC,
48  .bDeviceSubClass = 0,
49  .bDeviceProtocol = 0,
50  .bMaxPacketSize0 = 64,
51  .idVendor = 0x6666, /* Prototype product vendor ID */
52  .idProduct = 0x9637, /* dd if=/dev/random bs=2 count=1 | hexdump */
53  .bcdDevice = 0x0200,
54  .iManufacturer = 1,
55  .iProduct = 2,
56  .iSerialNumber = 3,
57  .bNumConfigurations = 1,
58 };
59 
60 /*
61  * This notification endpoint isn't implemented. According to CDC spec it's
62  * optional, but its absence causes a NULL pointer dereference in the
63  * Linux cdc_acm driver.
64  */
65 static const struct usb_endpoint_descriptor comm_endp[] = {{
66  .bLength = USB_DT_ENDPOINT_SIZE,
67  .bDescriptorType = USB_DT_ENDPOINT,
68  .bEndpointAddress = 0x83,
69  .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
70  .wMaxPacketSize = 16,
71  .bInterval = 255,
72 } };
73 
74 static const struct usb_endpoint_descriptor data_endp[] = {{
75  .bLength = USB_DT_ENDPOINT_SIZE,
76  .bDescriptorType = USB_DT_ENDPOINT,
77  .bEndpointAddress = 0x01,
78  .bmAttributes = USB_ENDPOINT_ATTR_BULK,
79  .wMaxPacketSize = 64,
80  .bInterval = 1,
81 }, {
82  .bLength = USB_DT_ENDPOINT_SIZE,
83  .bDescriptorType = USB_DT_ENDPOINT,
84  .bEndpointAddress = 0x82,
85  .bmAttributes = USB_ENDPOINT_ATTR_BULK,
86  .wMaxPacketSize = 64,
87  .bInterval = 1,
88 } };
89 
90 static const struct {
91  struct usb_cdc_header_descriptor header;
92  struct usb_cdc_call_management_descriptor call_mgmt;
93  struct usb_cdc_acm_descriptor acm;
94  struct usb_cdc_union_descriptor cdc_union;
95 } __attribute__((packed)) cdcacm_functional_descriptors = {
96  .header = {
97  .bFunctionLength = sizeof(struct usb_cdc_header_descriptor),
98  .bDescriptorType = CS_INTERFACE,
99  .bDescriptorSubtype = USB_CDC_TYPE_HEADER,
100  .bcdCDC = 0x0110,
101  },
102  .call_mgmt = {
103  .bFunctionLength =
104  sizeof(struct usb_cdc_call_management_descriptor),
105  .bDescriptorType = CS_INTERFACE,
106  .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT,
107  .bmCapabilities = 0,
108  .bDataInterface = 1,
109  },
110  .acm = {
111  .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor),
112  .bDescriptorType = CS_INTERFACE,
113  .bDescriptorSubtype = USB_CDC_TYPE_ACM,
114  .bmCapabilities = 0,
115  },
116  .cdc_union = {
117  .bFunctionLength = sizeof(struct usb_cdc_union_descriptor),
118  .bDescriptorType = CS_INTERFACE,
119  .bDescriptorSubtype = USB_CDC_TYPE_UNION,
120  .bControlInterface = 0,
121  .bSubordinateInterface0 = 1,
122  }
123 };
124 
125 static const struct usb_interface_descriptor comm_iface[] = {{
126  .bLength = USB_DT_INTERFACE_SIZE,
127  .bDescriptorType = USB_DT_INTERFACE,
128  .bInterfaceNumber = 0,
129  .bAlternateSetting = 0,
130  .bNumEndpoints = 1,
131  .bInterfaceClass = USB_CLASS_CDC,
132  .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
133  .bInterfaceProtocol = USB_CDC_PROTOCOL_AT,
134  .iInterface = 0,
135 
136  .endpoint = comm_endp,
137 
138  .extra = &cdcacm_functional_descriptors,
139  .extralen = sizeof(cdcacm_functional_descriptors)
140 } };
141 
142 static const struct usb_interface_descriptor data_iface[] = {{
143  .bLength = USB_DT_INTERFACE_SIZE,
144  .bDescriptorType = USB_DT_INTERFACE,
145  .bInterfaceNumber = 1,
146  .bAlternateSetting = 0,
147  .bNumEndpoints = 2,
148  .bInterfaceClass = USB_CLASS_DATA,
149  .bInterfaceSubClass = 0,
150  .bInterfaceProtocol = 0,
151  .iInterface = 0,
152 
153  .endpoint = data_endp,
154 } };
155 
156 static const struct usb_interface ifaces[] = {{
157  .num_altsetting = 1,
158  .altsetting = comm_iface,
159 }, {
160  .num_altsetting = 1,
161  .altsetting = data_iface,
162 } };
163 
164 static const struct usb_config_descriptor config = {
165  .bLength = USB_DT_CONFIGURATION_SIZE,
166  .bDescriptorType = USB_DT_CONFIGURATION,
167  .wTotalLength = 0,
168  .bNumInterfaces = 2,
169  .bConfigurationValue = 1,
170  .iConfiguration = 0,
171  .bmAttributes = 0x80,
172  .bMaxPower = (CONFIG_USB_MAX_POWER+1) / 2,
173 
174  .interface = ifaces,
175 };
176 
177 static char serial_no[25];
178 
179 static const char * usb_strings[] = {
180  "redfelineninja.org.uk",
181  "CDC-ADM command console",
182  serial_no
183 };
184 
185 static console_t *console;
186 static uint8_t outbuf[1024];
187 static ringbuf_t outring = RINGBUF_VAR_INIT(outbuf, sizeof(outbuf));
188 static usbd_device *usbd_dev;
189 
190 /* Buffer to be used for control requests. */
191 uint8_t usbd_control_buffer[128];
192 
193 /* prototype functions not found in the headers (for -Wmissing-prototypes) */
194 int _write(int fd, char *ptr, int len);
195 static void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue);
196 
197 struct output_task {
199  uint8_t ch;
200 };
201 static int output_fibre(fibre_t *fibre)
202 {
203  struct output_task *t = containerof(fibre, struct output_task, fibre);
204  PT_BEGIN_FIBRE(fibre);
205 
206  /* Initial timeout to allow things to settle. This proved necessary
207  * to make coldboot connections work on STM32F4-Discovery (issue was
208  * not deeply investigated but likely to be due to trying to send
209  * serial output whilst the usb thread is going through the hotplug
210  * sequence).
211  */
212  if (time64_now() < 2000000)
213  PT_WAIT_UNTIL(fibre_timeout(2000000));
214 
215  while (true) {
216  int ch = ringbuf_get(&outring);
217  if (-1 == ch)
218  PT_EXIT();
219 
220  t->ch = ch;
221 
222  while (!usbd_ep_write_packet(usbd_dev, 0x82, &t->ch, 1))
223  PT_YIELD();
224 
225  }
226 
227  PT_END();
228 }
229 static struct output_task output_task = {
230  .fibre = FIBRE_VAR_INIT(output_fibre)
231 };
232 
233 #if defined(STM32F1)
234 static void usb_hwinit(void)
235 {
236  uint32_t t;
237 
238  rcc_periph_clock_enable(RCC_GPIOA);
239  rcc_periph_clock_enable(RCC_GPIOB);
240 
241  /* lower hotplug and leave enough time for the host to notice */
242  gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL,
243  GPIO11 | GPIO12);
244  gpio_clear(GPIOA, GPIO11 | GPIO12);
245  t = time_now() + 10000;
246  while (cyclecmp32(time_now(), t) < 0)
247  ;
248 
249  /* use the device signature as the serial number */
250  desig_get_unique_id_as_string(serial_no, sizeof(serial_no));
251 
252  /* hotplug will automatically be unasserted as the usb cell takes
253  * command of the pins.
254  */
255  usbd_dev =
256  usbd_init(&st_usbfs_v1_usb_driver, &desc, &config, usb_strings, 3,
257  usbd_control_buffer, sizeof(usbd_control_buffer));
258 }
259 #elif defined(STM32F4)
260 static void usb_hwinit(void)
261 {
262  rcc_periph_clock_enable(RCC_GPIOA);
263  rcc_periph_clock_enable(RCC_OTGFS);
264 
265  gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE,
266  GPIO9 | GPIO11 | GPIO12);
267  gpio_set_af(GPIOA, GPIO_AF10, GPIO9 | GPIO11 | GPIO12);
268 
269  usbd_dev = usbd_init(&otgfs_usb_driver, &desc, &config,
270  usb_strings, 2,
271  usbd_control_buffer, sizeof(usbd_control_buffer));
272 }
273 #else
274 #error Unsupported part
275 #endif
276 
277 static int usb_fibre(fibre_t *fibre)
278 {
279  PT_BEGIN_FIBRE(fibre);
280 
281  usb_hwinit();
282 
283  usbd_register_set_config_callback(usbd_dev, cdcacm_set_config);
284 
285  while (true) {
286  usbd_poll(usbd_dev);
287  PT_YIELD();
288  }
289 
290  PT_END();
291 }
292 static fibre_t usb_task = FIBRE_VAR_INIT(usb_fibre);
293 
294 static int cdcacm_control_request(usbd_device *dev,
295  struct usb_setup_data *req, uint8_t **buf, uint16_t *len,
296  void (**complete)(usbd_device *dev, struct usb_setup_data *req))
297 {
298  (void)complete;
299  (void)buf;
300  (void)dev;
301 
302  switch (req->bRequest) {
303  case USB_CDC_REQ_SET_CONTROL_LINE_STATE: {
304  /* The Linux cdc_acm driver requires this to be implemented
305  * even though it's optional in the CDC spec, and we don't
306  * advertise it in the ACM functional descriptor.
307  */
308  return 1;
309  }
310  case USB_CDC_REQ_SET_LINE_CODING:
311  if (*len < sizeof(struct usb_cdc_line_coding)) {
312  return 0;
313  }
314 
315  return 1;
316  }
317  return 0;
318 }
319 
320 static void cdcacm_data_rx_cb(usbd_device *dev, uint8_t ep)
321 {
322  (void)ep;
323 
324  char buf[64];
325  int len = usbd_ep_read_packet(dev, 0x01, buf, 64);
326 
327  for (int i = 0; i < len; i++) {
328  (void)ringbuf_put(&outring, buf[i]);
329  if (buf[i] == '\r') {
330  (void)ringbuf_put(&outring, '\n');
331  console_putchar(console, '\n');
332  } else {
333  console_putchar(console, buf[i]);
334  }
335  }
336  fibre_run_atomic(&output_task.fibre);
337 }
338 
339 static void cdcacm_set_config(usbd_device *dev, uint16_t wValue)
340 {
341  (void)wValue;
342 
343  usbd_ep_setup(dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64,
344  cdcacm_data_rx_cb);
345  usbd_ep_setup(dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, NULL);
346  usbd_ep_setup(dev, 0x83, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
347 
348  usbd_register_control_callback(
349  dev, USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
350  USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, cdcacm_control_request);
351 }
352 
353 int _write(int fd, char *ptr, int len)
354 {
355  if (fd == 1 || fd == 2) {
356  for (int i=0; i<len; i++) {
357  if (ptr[i] == '\n')
358  (void) ringbuf_put(&outring, '\r');
359  (void) ringbuf_put(&outring, ptr[i]);
360  }
361  fibre_run(&output_task.fibre);
362  return 0;
363  }
364 
365  errno = EIO;
366  return -1;
367 }
368 
370 {
371  console = c;
372  fibre_run(&usb_task);
373 
374  /* Going silent, which in this case means not speaking until spoken to,
375  * ensures the console doesn't send output until USB is fully settled
376  * (as proved because the host can send us data)
377  */
378  console_silent(c);
379 }
#define containerof(ptr, type, member)
Definition: util.h:35
struct charlie c
uint64_t time64_now(void)
#define RINGBUF_VAR_INIT(bufp, buf_len)
Static initializer for a ring buffer descriptor.
Definition: ringbuf.h:48
#define FIBRE_VAR_INIT(fn)
Static initializer for a fibre descriptor.
Definition: fibre.h:75
#define PT_EXIT()
Definition: protothreads.h:132
Console descriptor.
Definition: console.h:66
int _write(int fd, char *ptr, int len)
void console_hwinit(console_t *c)
Platform dependant function that will be called during console_init().
Ring buffer descriptor.
Definition: ringbuf.h:38
void console_putchar(console_t *c, char d)
Asynchronously send a character to the command processor.
Definition: console.c:177
#define PT_WAIT_UNTIL(c)
Definition: protothreads.h:116
int ringbuf_get(ringbuf_t *rb)
Extract a byte from the ring buffer.
Definition: ringbuf.c:31
#define CONFIG_USB_MAX_POWER
struct usb_cdc_union_descriptor cdc_union
#define PT_YIELD()
Definition: protothreads.h:124
#define PT_END()
Definition: protothreads.h:100
uint8_t usbd_control_buffer[128]
int32_t cyclecmp32(uint32_t a, uint32_t b)
Compares values that may be subject to overflow.
Definition: util.c:19
struct usb_cdc_acm_descriptor acm
struct usb_cdc_header_descriptor header
bool fibre_run_atomic(fibre_t *f)
Definition: fibre.c:183
uint32_t time_now(void)
#define PT_BEGIN_FIBRE(f)
Fibre aware alternative to PT_BEGIN().
Definition: fibre.h:103
struct usb_cdc_call_management_descriptor call_mgmt
bool ringbuf_put(ringbuf_t *rb, uint8_t d)
Insert a byte into the ring buffer.
Definition: ringbuf.c:58
Fibre descriptor.
Definition: fibre.h:64
void fibre_run(fibre_t *f)
Definition: fibre.c:173
bool fibre_timeout(uint32_t duetime)
Definition: fibre.c:208
struct output_task __attribute__