update credits
[librepilot.git] / flight / pios / stm32f30x / pios_usb_cdc.c
blob186fb95b4882afcb65aaba9a78770f76251b7774
1 /**
2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
4 * @{
5 * @addtogroup PIOS_USB_COM USB COM Functions
6 * @brief PIOS USB COM implementation for CDC interfaces
7 * @notes This implements a CDC Serial Port
8 * @{
10 * @file pios_usb_cdc.c
11 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017.
12 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
13 * @brief USB COM functions (STM32 dependent code)
14 * @see The GNU Public License (GPL) Version 3
16 *****************************************************************************/
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 3 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
26 * for more details.
28 * You should have received a copy of the GNU General Public License along
29 * with this program; if not, write to the Free Software Foundation, Inc.,
30 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #include "pios.h"
35 #ifdef PIOS_INCLUDE_USB_CDC
37 #include "pios_usb_cdc_priv.h"
38 #include "pios_usb_board_data.h" /* PIOS_BOARD_*_DATA_LENGTH */
40 /* STM32 USB Library Definitions */
41 #include "usb_lib.h"
43 static void PIOS_USB_CDC_RegisterTxCallback(uint32_t usbcdc_id, pios_com_callback tx_out_cb, uint32_t context);
44 static void PIOS_USB_CDC_RegisterRxCallback(uint32_t usbcdc_id, pios_com_callback rx_in_cb, uint32_t context);
45 static void PIOS_USB_CDC_RegisterBaudRateCallback(uint32_t usbcdc_id, pios_com_callback_baud_rate baud_rate_cb, uint32_t context);
46 static void PIOS_USB_CDC_RegisterAvailableCallback(uint32_t usbcdc_id, pios_com_callback_available baud_rate_cb, uint32_t context);
47 static void PIOS_USB_CDC_ChangeConnectionState(bool connected, uint32_t usbcdc_id);
48 static void PIOS_USB_CDC_TxStart(uint32_t usbcdc_id, uint16_t tx_bytes_avail);
49 static void PIOS_USB_CDC_RxStart(uint32_t usbcdc_id, uint16_t rx_bytes_avail);
50 static uint32_t PIOS_USB_CDC_Available(uint32_t usbcdc_id);
52 const struct pios_com_driver pios_usb_cdc_com_driver = {
53 .tx_start = PIOS_USB_CDC_TxStart,
54 .rx_start = PIOS_USB_CDC_RxStart,
55 .bind_tx_cb = PIOS_USB_CDC_RegisterTxCallback,
56 .bind_rx_cb = PIOS_USB_CDC_RegisterRxCallback,
57 .bind_baud_rate_cb = PIOS_USB_CDC_RegisterBaudRateCallback,
58 .available = PIOS_USB_CDC_Available,
59 .bind_available_cb = PIOS_USB_CDC_RegisterAvailableCallback,
62 enum pios_usb_cdc_dev_magic {
63 PIOS_USB_CDC_DEV_MAGIC = 0xAABBCCDD,
66 struct pios_usb_cdc_dev {
67 enum pios_usb_cdc_dev_magic magic;
68 const struct pios_usb_cdc_cfg *cfg;
70 uint32_t lower_id;
72 pios_com_callback rx_in_cb;
73 uint32_t rx_in_context;
74 pios_com_callback tx_out_cb;
75 uint32_t tx_out_context;
77 pios_com_callback_baud_rate baud_rate_cb;
78 uint32_t baud_rate_context;
79 pios_com_callback_available available_cb;
80 uint32_t available_context;
82 uint8_t rx_packet_buffer[PIOS_USB_BOARD_CDC_DATA_LENGTH];
84 * NOTE: This is -1 as somewhat of a hack. It ensures that we always send packets
85 * that are strictly < maxPacketSize for this interface which means we never have
86 * to bother with zero length packets (ZLP).
88 uint8_t tx_packet_buffer[PIOS_USB_BOARD_CDC_DATA_LENGTH - 1];
90 uint32_t rx_dropped;
91 uint32_t rx_oversize;
94 static bool PIOS_USB_CDC_validate(struct pios_usb_cdc_dev *usb_cdc_dev)
96 return usb_cdc_dev->magic == PIOS_USB_CDC_DEV_MAGIC;
99 #if defined(PIOS_INCLUDE_FREERTOS)
100 static struct pios_usb_cdc_dev *PIOS_USB_CDC_alloc(void)
102 struct pios_usb_cdc_dev *usb_cdc_dev;
104 usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_fastheapmalloc(sizeof(struct pios_usb_cdc_dev));
105 if (!usb_cdc_dev) {
106 return NULL;
109 memset(usb_cdc_dev, 0, sizeof(struct pios_usb_cdc_dev));
110 usb_cdc_dev->magic = PIOS_USB_CDC_DEV_MAGIC;
111 return usb_cdc_dev;
113 #else
114 static struct pios_usb_cdc_dev pios_usb_cdc_devs[PIOS_USB_CDC_MAX_DEVS];
115 static uint8_t pios_usb_cdc_num_devs;
116 static struct pios_usb_cdc_dev *PIOS_USB_CDC_alloc(void)
118 struct pios_usb_cdc_dev *usb_cdc_dev;
120 if (pios_usb_cdc_num_devs >= PIOS_USB_CDC_MAX_DEVS) {
121 return NULL;
124 usb_cdc_dev = &pios_usb_cdc_devs[pios_usb_cdc_num_devs++];
126 memset(usb_cdc_dev, 0, sizeof(struct pios_usb_cdc_dev));
127 usb_cdc_dev->magic = PIOS_USB_CDC_DEV_MAGIC;
129 return usb_cdc_dev;
131 #endif /* if defined(PIOS_INCLUDE_FREERTOS) */
133 static void PIOS_USB_CDC_DATA_EP_IN_Callback(void);
134 static void PIOS_USB_CDC_DATA_EP_OUT_Callback(void);
135 static void PIOS_USB_CDC_CTRL_EP_IN_Callback(void);
137 static uint32_t pios_usb_cdc_id;
139 /* Need a better way to pull these in */
140 extern void(*pEpInt_IN[7]) (void);
141 extern void(*pEpInt_OUT[7]) (void);
143 int32_t PIOS_USB_CDC_Init(uint32_t *usbcdc_id, const struct pios_usb_cdc_cfg *cfg, uint32_t lower_id)
145 PIOS_Assert(usbcdc_id);
146 PIOS_Assert(cfg);
148 struct pios_usb_cdc_dev *usb_cdc_dev;
150 usb_cdc_dev = (struct pios_usb_cdc_dev *)PIOS_USB_CDC_alloc();
151 if (!usb_cdc_dev) {
152 goto out_fail;
155 /* Bind the configuration to the device instance */
156 usb_cdc_dev->cfg = cfg;
157 usb_cdc_dev->lower_id = lower_id;
159 pios_usb_cdc_id = (uint32_t)usb_cdc_dev;
161 /* Bind lower level callbacks into the USB infrastructure */
162 pEpInt_OUT[cfg->ctrl_tx_ep - 1] = PIOS_USB_CDC_CTRL_EP_IN_Callback;
163 pEpInt_IN[cfg->data_tx_ep - 1] = PIOS_USB_CDC_DATA_EP_IN_Callback;
164 pEpInt_OUT[cfg->data_rx_ep - 1] = PIOS_USB_CDC_DATA_EP_OUT_Callback;
166 PIOS_USB_RegisterConnectionStateCallback(PIOS_USB_CDC_ChangeConnectionState, (uint32_t)usb_cdc_dev);
168 *usbcdc_id = (uint32_t)usb_cdc_dev;
170 return 0;
172 out_fail:
173 return -1;
177 static void PIOS_USB_CDC_RegisterRxCallback(uint32_t usbcdc_id, pios_com_callback rx_in_cb, uint32_t context)
179 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
181 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
183 PIOS_Assert(valid);
186 * Order is important in these assignments since ISR uses _cb
187 * field to determine if it's ok to dereference _cb and _context
189 usb_cdc_dev->rx_in_context = context;
190 usb_cdc_dev->rx_in_cb = rx_in_cb;
193 static void PIOS_USB_CDC_RegisterTxCallback(uint32_t usbcdc_id, pios_com_callback tx_out_cb, uint32_t context)
195 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
197 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
199 PIOS_Assert(valid);
202 * Order is important in these assignments since ISR uses _cb
203 * field to determine if it's ok to dereference _cb and _context
205 usb_cdc_dev->tx_out_context = context;
206 usb_cdc_dev->tx_out_cb = tx_out_cb;
209 static void PIOS_USB_CDC_RxStart(uint32_t usbcdc_id, uint16_t rx_bytes_avail)
211 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
213 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
215 PIOS_Assert(valid);
217 if (!PIOS_USB_CheckAvailable(usb_cdc_dev->lower_id)) {
218 return;
221 // If endpoint was stalled and there is now space make it valid
222 PIOS_IRQ_Disable();
223 if ((GetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep) != EP_RX_VALID) &&
224 (rx_bytes_avail >= sizeof(usb_cdc_dev->rx_packet_buffer))) {
225 SetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep, EP_RX_VALID);
227 PIOS_IRQ_Enable();
230 static void PIOS_USB_CDC_SendData(struct pios_usb_cdc_dev *usb_cdc_dev)
232 uint16_t bytes_to_tx;
234 if (!usb_cdc_dev->tx_out_cb) {
235 return;
238 bool need_yield = false;
239 bytes_to_tx = (usb_cdc_dev->tx_out_cb)(usb_cdc_dev->tx_out_context,
240 usb_cdc_dev->tx_packet_buffer,
241 sizeof(usb_cdc_dev->tx_packet_buffer),
242 NULL,
243 &need_yield);
244 if (bytes_to_tx == 0) {
245 return;
248 UserToPMABufferCopy(usb_cdc_dev->tx_packet_buffer,
249 GetEPTxAddr(usb_cdc_dev->cfg->data_tx_ep),
250 bytes_to_tx);
251 SetEPTxCount(usb_cdc_dev->cfg->data_tx_ep, bytes_to_tx);
252 SetEPTxValid(usb_cdc_dev->cfg->data_tx_ep);
254 #if defined(PIOS_INCLUDE_FREERTOS)
255 if (need_yield) {
256 vPortYield();
258 #endif /* PIOS_INCLUDE_FREERTOS */
261 static void PIOS_USB_CDC_TxStart(uint32_t usbcdc_id, __attribute__((unused)) uint16_t tx_bytes_avail)
263 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
265 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
267 PIOS_Assert(valid);
269 if (!PIOS_USB_CheckAvailable(usb_cdc_dev->lower_id)) {
270 return;
273 if (GetEPTxStatus(usb_cdc_dev->cfg->data_tx_ep) == EP_TX_VALID) {
274 /* Endpoint is already transmitting */
275 return;
278 PIOS_USB_CDC_SendData(usb_cdc_dev);
281 static void PIOS_USB_CDC_DATA_EP_IN_Callback(void)
283 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
285 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
287 PIOS_Assert(valid);
289 PIOS_USB_CDC_SendData(usb_cdc_dev);
292 static void PIOS_USB_CDC_DATA_EP_OUT_Callback(void)
294 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
296 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
298 PIOS_Assert(valid);
300 uint32_t DataLength;
302 /* Get the number of received data on the selected Endpoint */
303 DataLength = GetEPRxCount(usb_cdc_dev->cfg->data_rx_ep);
304 if (DataLength > sizeof(usb_cdc_dev->rx_packet_buffer)) {
305 usb_cdc_dev->rx_oversize++;
306 DataLength = sizeof(usb_cdc_dev->rx_packet_buffer);
309 /* Use the memory interface function to read from the selected endpoint */
310 PMAToUserBufferCopy((uint8_t *)usb_cdc_dev->rx_packet_buffer,
311 GetEPRxAddr(usb_cdc_dev->cfg->data_rx_ep),
312 DataLength);
314 if (!usb_cdc_dev->rx_in_cb) {
315 /* No Rx call back registered, disable the receiver */
316 SetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep, EP_RX_NAK);
317 return;
320 uint16_t headroom;
321 bool need_yield = false;
322 uint16_t rc;
323 rc = (usb_cdc_dev->rx_in_cb)(usb_cdc_dev->rx_in_context,
324 usb_cdc_dev->rx_packet_buffer,
325 DataLength,
326 &headroom,
327 &need_yield);
329 if (rc < DataLength) {
330 /* Lost bytes on rx */
331 usb_cdc_dev->rx_dropped += (DataLength - rc);
334 if (headroom >= sizeof(usb_cdc_dev->rx_packet_buffer)) {
335 /* We have room for a maximum length message */
336 SetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep, EP_RX_VALID);
337 } else {
338 /* Not enough room left for a message, apply backpressure */
339 SetEPRxStatus(usb_cdc_dev->cfg->data_rx_ep, EP_RX_NAK);
342 #if defined(PIOS_INCLUDE_FREERTOS)
343 if (need_yield) {
344 vPortYield();
346 #endif /* PIOS_INCLUDE_FREERTOS */
349 static uint16_t control_line_state;
350 RESULT PIOS_USB_CDC_SetControlLineState(void)
352 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
354 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
356 if (!valid) {
357 /* No CDC interface is configured */
358 return USB_UNSUPPORT;
361 uint8_t wValue0 = pInformation->USBwValue0;
362 uint8_t wValue1 = pInformation->USBwValue1;
364 control_line_state = wValue1 << 8 | wValue0;
366 return USB_SUCCESS;
369 static uint32_t PIOS_USB_CDC_Available(uint32_t usbcdc_id)
371 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
373 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
375 PIOS_Assert(valid);
377 return PIOS_USB_CheckAvailable(usb_cdc_dev->lower_id) ? COM_AVAILABLE_RXTX : COM_AVAILABLE_NONE;
380 static struct usb_cdc_line_coding line_coding = {
381 .dwDTERate = htousbl(57600),
382 .bCharFormat = USB_CDC_LINE_CODING_STOP_1,
383 .bParityType = USB_CDC_LINE_CODING_PARITY_NONE,
384 .bDataBits = 8,
387 uint8_t *PIOS_USB_CDC_SetLineCoding(uint16_t Length)
389 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
391 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
393 if (!valid) {
394 /* No CDC interface is configured */
395 return NULL;
398 if (Length == 0) {
399 /* Report the number of bytes we're prepared to consume */
400 pInformation->Ctrl_Info.Usb_wLength = sizeof(line_coding);
401 pInformation->Ctrl_Info.Usb_rLength = sizeof(line_coding);
402 return NULL;
403 } else {
404 /* Give out a pointer to the data struct */
405 return (uint8_t *)&line_coding;
409 const uint8_t *PIOS_USB_CDC_GetLineCoding(uint16_t Length)
411 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
413 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
415 if (!valid) {
416 /* No CDC interface is configured */
417 return NULL;
420 if (Length == 0) {
421 pInformation->Ctrl_Info.Usb_wLength = sizeof(line_coding);
422 return NULL;
423 } else {
424 return (uint8_t *)&line_coding;
428 struct usb_cdc_serial_state_report uart_state = {
429 .bmRequestType = 0xA1,
430 .bNotification = USB_CDC_NOTIFICATION_SERIAL_STATE,
431 .wValue = 0,
432 .wIndex = htousbs(1),
433 .wLength = htousbs(2),
434 .bmUartState = htousbs(0),
437 static void PIOS_USB_CDC_CTRL_EP_IN_Callback(void)
439 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
441 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
443 PIOS_Assert(valid);
445 /* Give back UART State Bitmap */
446 /* UART State Bitmap
447 * 15-7: reserved
448 * 6: bOverRun overrun error
449 * 5: bParity parity error
450 * 4: bFraming framing error
451 * 3: bRingSignal RI
452 * 2: bBreak break reception
453 * 1: bTxCarrier DSR
454 * 0: bRxCarrier DCD
456 uart_state.bmUartState = htousbs(0x0003);
458 UserToPMABufferCopy((uint8_t *)&uart_state,
459 GetEPTxAddr(usb_cdc_dev->cfg->ctrl_tx_ep),
460 sizeof(uart_state));
461 SetEPTxCount(usb_cdc_dev->cfg->ctrl_tx_ep, PIOS_USB_BOARD_CDC_MGMT_LENGTH);
462 SetEPTxValid(usb_cdc_dev->cfg->ctrl_tx_ep);
465 static void PIOS_USB_CDC_RegisterBaudRateCallback(uint32_t usbcdc_id, pios_com_callback_baud_rate baud_rate_cb, uint32_t context)
467 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
469 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
471 PIOS_Assert(valid);
474 * Order is important in these assignments since ISR uses _cb
475 * field to determine if it's ok to dereference _cb and _context
477 usb_cdc_dev->baud_rate_context = context;
478 usb_cdc_dev->baud_rate_cb = baud_rate_cb;
481 void PIOS_USB_CDC_SetLineCoding_Completed()
483 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
485 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
487 if (!valid) {
488 /* No CDC interface is configured */
489 return;
492 if (usb_cdc_dev->baud_rate_cb) {
493 usb_cdc_dev->baud_rate_cb(usb_cdc_dev->baud_rate_context, usbltoh(line_coding.dwDTERate));
497 static void PIOS_USB_CDC_ChangeConnectionState(__attribute__((unused)) bool connected, uint32_t usbcdc_id)
499 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
501 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
503 PIOS_Assert(valid);
505 if (usb_cdc_dev->available_cb) {
506 (usb_cdc_dev->available_cb)(usb_cdc_dev->available_context, PIOS_USB_CDC_Available(usbcdc_id));
510 static void PIOS_USB_CDC_RegisterAvailableCallback(uint32_t usbcdc_id, pios_com_callback_available available_cb, uint32_t context)
512 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
514 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
516 PIOS_Assert(valid);
519 * Order is important in these assignments since ISR uses _cb
520 * field to determine if it's ok to dereference _cb and _context
522 usb_cdc_dev->available_context = context;
523 usb_cdc_dev->available_cb = available_cb;
526 #endif /* PIOS_INCLUDE_USB_CDC */