2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
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
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
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
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 */
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
;
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];
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
));
109 memset(usb_cdc_dev
, 0, sizeof(struct pios_usb_cdc_dev
));
110 usb_cdc_dev
->magic
= PIOS_USB_CDC_DEV_MAGIC
;
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
) {
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
;
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
);
148 struct pios_usb_cdc_dev
*usb_cdc_dev
;
150 usb_cdc_dev
= (struct pios_usb_cdc_dev
*)PIOS_USB_CDC_alloc();
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
;
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
);
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
);
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
);
217 if (!PIOS_USB_CheckAvailable(usb_cdc_dev
->lower_id
)) {
221 // If endpoint was stalled and there is now space make it valid
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
);
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
) {
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
),
244 if (bytes_to_tx
== 0) {
248 UserToPMABufferCopy(usb_cdc_dev
->tx_packet_buffer
,
249 GetEPTxAddr(usb_cdc_dev
->cfg
->data_tx_ep
),
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)
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
);
269 if (!PIOS_USB_CheckAvailable(usb_cdc_dev
->lower_id
)) {
273 if (GetEPTxStatus(usb_cdc_dev
->cfg
->data_tx_ep
) == EP_TX_VALID
) {
274 /* Endpoint is already transmitting */
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
);
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
);
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
),
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
);
321 bool need_yield
= false;
323 rc
= (usb_cdc_dev
->rx_in_cb
)(usb_cdc_dev
->rx_in_context
,
324 usb_cdc_dev
->rx_packet_buffer
,
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
);
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)
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
);
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
;
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
);
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
,
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
);
394 /* No CDC interface is configured */
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
);
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
);
416 /* No CDC interface is configured */
421 pInformation
->Ctrl_Info
.Usb_wLength
= sizeof(line_coding
);
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
,
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
);
445 /* Give back UART State Bitmap */
448 * 6: bOverRun overrun error
449 * 5: bParity parity error
450 * 4: bFraming framing error
452 * 2: bBreak break reception
456 uart_state
.bmUartState
= htousbs(0x0003);
458 UserToPMABufferCopy((uint8_t *)&uart_state
,
459 GetEPTxAddr(usb_cdc_dev
->cfg
->ctrl_tx_ep
),
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
);
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
);
488 /* No CDC interface is configured */
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
);
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
);
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 */