LP-602 Re-enable rx_status checking in usb_csc to optionally reenable rx after reset
[librepilot.git] / flight / pios / stm32f4xx / pios_usb_cdc.c
blob52513f8bd46ba9ae90a35c2dcc2d30e123d68d1e
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_com_cdc.c
11 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
12 * @brief USB COM functions (STM32 dependent code)
13 * @see The GNU Public License (GPL) Version 3
15 *****************************************************************************/
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25 * for more details.
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include "pios.h"
34 #ifdef PIOS_INCLUDE_USB_CDC
36 #include "pios_usb_cdc_priv.h"
37 #include "pios_usb_board_data.h" /* PIOS_BOARD_*_DATA_LENGTH */
38 #include "pios_usbhook.h" /* PIOS_USBHOOK_* */
39 #include "usb_core.h" /* USB_OTG_EP_RX_VALID */
41 /* Implement COM layer driver API */
42 static void PIOS_USB_CDC_RegisterTxCallback(uint32_t usbcdc_id, pios_com_callback tx_out_cb, uint32_t context);
43 static void PIOS_USB_CDC_RegisterRxCallback(uint32_t usbcdc_id, pios_com_callback rx_in_cb, uint32_t context);
44 static void PIOS_USB_CDC_RegisterCtrlLineCallback(uint32_t usbcdc_id, pios_com_callback_ctrl_line ctrl_line_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_ctrl_line_cb = PIOS_USB_CDC_RegisterCtrlLineCallback,
58 .bind_baud_rate_cb = PIOS_USB_CDC_RegisterBaudRateCallback,
59 .available = PIOS_USB_CDC_Available,
60 .bind_available_cb = PIOS_USB_CDC_RegisterAvailableCallback,
63 enum pios_usb_cdc_dev_magic {
64 PIOS_USB_CDC_DEV_MAGIC = 0xAABBCCDD,
67 struct pios_usb_cdc_dev {
68 enum pios_usb_cdc_dev_magic magic;
69 const struct pios_usb_cdc_cfg *cfg;
71 uint32_t lower_id;
73 pios_com_callback rx_in_cb;
74 uint32_t rx_in_context;
75 pios_com_callback tx_out_cb;
76 uint32_t tx_out_context;
77 pios_com_callback_ctrl_line ctrl_line_cb;
78 uint32_t ctrl_line_context;
79 pios_com_callback_baud_rate baud_rate_cb;
80 uint32_t baud_rate_context;
81 pios_com_callback_available available_cb;
82 uint32_t available_context;
84 bool usb_ctrl_if_enabled;
85 bool usb_data_if_enabled;
87 uint8_t rx_packet_buffer[PIOS_USB_BOARD_CDC_DATA_LENGTH] __attribute__((aligned(4)));
90 * NOTE: This is -1 as somewhat of a hack. It ensures that we always send packets
91 * that are strictly < maxPacketSize for this interface which means we never have
92 * to bother with zero length packets (ZLP).
94 uint8_t tx_packet_buffer[PIOS_USB_BOARD_CDC_DATA_LENGTH - 1] __attribute__((aligned(4)));
95 volatile bool tx_active;
96 volatile bool rx_active;
98 uint8_t ctrl_tx_packet_buffer[PIOS_USB_BOARD_CDC_MGMT_LENGTH] __attribute__((aligned(4)));
100 uint32_t rx_dropped;
101 uint32_t rx_oversize;
104 * Used to hold the current state of the simulated UART. Changes to this
105 * variable may trigger new USB CDC Notification packets to be sent to the host.
107 volatile uint16_t prev_uart_state;
110 static bool PIOS_USB_CDC_validate(struct pios_usb_cdc_dev *usb_cdc_dev)
112 return usb_cdc_dev && (usb_cdc_dev->magic == PIOS_USB_CDC_DEV_MAGIC);
115 #if defined(PIOS_INCLUDE_FREERTOS)
116 static struct pios_usb_cdc_dev *PIOS_USB_CDC_alloc(void)
118 struct pios_usb_cdc_dev *usb_cdc_dev;
120 usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_malloc(sizeof(struct pios_usb_cdc_dev));
121 if (!usb_cdc_dev) {
122 return NULL;
125 memset(usb_cdc_dev, 0, sizeof(struct pios_usb_cdc_dev));
126 usb_cdc_dev->magic = PIOS_USB_CDC_DEV_MAGIC;
127 return usb_cdc_dev;
129 #else
130 static struct pios_usb_cdc_dev pios_usb_cdc_devs[PIOS_USB_CDC_MAX_DEVS];
131 static uint8_t pios_usb_cdc_num_devs;
132 static struct pios_usb_cdc_dev *PIOS_USB_CDC_alloc(void)
134 struct pios_usb_cdc_dev *usb_cdc_dev;
136 if (pios_usb_cdc_num_devs >= PIOS_USB_CDC_MAX_DEVS) {
137 return NULL;
140 usb_cdc_dev = &pios_usb_cdc_devs[pios_usb_cdc_num_devs++];
142 memset(usb_cdc_dev, 0, sizeof(struct pios_usb_cdc_dev));
143 usb_cdc_dev->magic = PIOS_USB_CDC_DEV_MAGIC;
145 return usb_cdc_dev;
147 #endif /* if defined(PIOS_INCLUDE_FREERTOS) */
149 /* Implement USB_IFOPS for CDC Control Interface */
150 static void PIOS_USB_CDC_CTRL_IF_Init(uint32_t usb_cdc_id);
151 static void PIOS_USB_CDC_CTRL_IF_DeInit(uint32_t usb_cdc_id);
152 static bool PIOS_USB_CDC_CTRL_IF_Setup(uint32_t usb_cdc_id, struct usb_setup_request *req);
153 static void PIOS_USB_CDC_CTRL_IF_CtrlDataOut(uint32_t usb_cdc_id, const struct usb_setup_request *req);
155 static struct pios_usb_ifops usb_cdc_ctrl_ifops = {
156 .init = PIOS_USB_CDC_CTRL_IF_Init,
157 .deinit = PIOS_USB_CDC_CTRL_IF_DeInit,
158 .setup = PIOS_USB_CDC_CTRL_IF_Setup,
159 .ctrl_data_out = PIOS_USB_CDC_CTRL_IF_CtrlDataOut,
162 /* Implement USB_IFOPS for CDC Data Interface */
163 static void PIOS_USB_CDC_DATA_IF_Init(uint32_t usb_cdc_id);
164 static void PIOS_USB_CDC_DATA_IF_DeInit(uint32_t usb_cdc_id);
165 static bool PIOS_USB_CDC_DATA_IF_Setup(uint32_t usb_cdc_id, struct usb_setup_request *req);
166 static void PIOS_USB_CDC_DATA_IF_CtrlDataOut(uint32_t usb_cdc_id, const struct usb_setup_request *req);
168 static struct pios_usb_ifops usb_cdc_data_ifops = {
169 .init = PIOS_USB_CDC_DATA_IF_Init,
170 .deinit = PIOS_USB_CDC_DATA_IF_DeInit,
171 .setup = PIOS_USB_CDC_DATA_IF_Setup,
172 .ctrl_data_out = PIOS_USB_CDC_DATA_IF_CtrlDataOut,
175 static uint32_t pios_usb_cdc_id;
177 int32_t PIOS_USB_CDC_Init(uint32_t *usbcdc_id, const struct pios_usb_cdc_cfg *cfg, uint32_t lower_id)
179 PIOS_Assert(usbcdc_id);
180 PIOS_Assert(cfg);
182 struct pios_usb_cdc_dev *usb_cdc_dev;
184 usb_cdc_dev = (struct pios_usb_cdc_dev *)PIOS_USB_CDC_alloc();
185 if (!usb_cdc_dev) {
186 goto out_fail;
189 /* Bind the configuration to the device instance */
190 usb_cdc_dev->cfg = cfg;
191 usb_cdc_dev->lower_id = lower_id;
193 pios_usb_cdc_id = (uint32_t)usb_cdc_dev;
195 /* Tx and Rx are not active yet */
196 usb_cdc_dev->tx_active = false;
197 usb_cdc_dev->rx_active = false;
199 /* Clear stats */
200 usb_cdc_dev->rx_dropped = 0;
201 usb_cdc_dev->rx_oversize = 0;
203 /* Initialize the uart state */
204 usb_cdc_dev->prev_uart_state = 0;
206 /* Register class specific interface callbacks with the USBHOOK layer */
207 usb_cdc_dev->usb_ctrl_if_enabled = false;
208 PIOS_USBHOOK_RegisterIfOps(cfg->ctrl_if, &usb_cdc_ctrl_ifops, (uint32_t)usb_cdc_dev);
210 /* Register class specific interface callbacks with the USBHOOK layer */
211 usb_cdc_dev->usb_data_if_enabled = false;
212 PIOS_USBHOOK_RegisterIfOps(cfg->data_if, &usb_cdc_data_ifops, (uint32_t)usb_cdc_dev);
214 PIOS_USB_RegisterConnectionStateCallback(PIOS_USB_CDC_ChangeConnectionState, (uint32_t)usb_cdc_dev);
216 *usbcdc_id = (uint32_t)usb_cdc_dev;
218 return 0;
220 out_fail:
221 return -1;
224 static bool PIOS_USB_CDC_SendData(struct pios_usb_cdc_dev *usb_cdc_dev)
226 uint16_t bytes_to_tx;
228 if (!usb_cdc_dev->tx_out_cb) {
229 return false;
232 bool need_yield = false;
233 bytes_to_tx = (usb_cdc_dev->tx_out_cb)(usb_cdc_dev->tx_out_context,
234 usb_cdc_dev->tx_packet_buffer,
235 sizeof(usb_cdc_dev->tx_packet_buffer),
236 NULL,
237 &need_yield);
238 if (bytes_to_tx == 0) {
239 return false;
243 * Mark this endpoint as being tx active _before_ actually transmitting
244 * to make sure we don't race with the Tx completion interrupt
246 usb_cdc_dev->tx_active = true;
248 PIOS_USBHOOK_EndpointTx(usb_cdc_dev->cfg->data_tx_ep,
249 usb_cdc_dev->tx_packet_buffer,
250 bytes_to_tx);
252 #if defined(PIOS_INCLUDE_FREERTOS)
253 if (need_yield) {
254 vPortYield();
256 #endif /* PIOS_INCLUDE_FREERTOS */
258 return true;
261 static void PIOS_USB_CDC_RxStart(uint32_t usbcdc_id, uint16_t rx_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 /* Make sure this USB interface has been initialized */
270 if (!usb_cdc_dev->usb_data_if_enabled) {
271 return;
274 if (!PIOS_USB_CheckAvailable(usb_cdc_dev->lower_id)) {
275 return;
278 // If endpoint was stalled and there is now space make it valid
279 if ((PIOS_USBHOOK_EndpointGetStatus(usb_cdc_dev->cfg->data_rx_ep) != USB_OTG_EP_RX_VALID)
280 && (rx_bytes_avail >= PIOS_USB_BOARD_CDC_DATA_LENGTH)) {
281 PIOS_USBHOOK_EndpointRx(usb_cdc_dev->cfg->data_rx_ep,
282 usb_cdc_dev->rx_packet_buffer,
283 sizeof(usb_cdc_dev->rx_packet_buffer));
284 usb_cdc_dev->rx_active = true;
288 static void PIOS_USB_CDC_TxStart(uint32_t usbcdc_id, __attribute__((unused)) uint16_t tx_bytes_avail)
290 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
292 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
294 PIOS_Assert(valid);
296 /* Make sure this USB interface has been initialized */
297 if (!usb_cdc_dev->usb_data_if_enabled) {
298 return;
301 if (!PIOS_USB_CheckAvailable(usb_cdc_dev->lower_id)) {
302 return;
305 if (!usb_cdc_dev->tx_active) {
306 /* Transmitter is not currently active, send a report */
307 PIOS_USB_CDC_SendData(usb_cdc_dev);
311 static void PIOS_USB_CDC_RegisterRxCallback(uint32_t usbcdc_id, pios_com_callback rx_in_cb, uint32_t context)
313 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
315 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
317 PIOS_Assert(valid);
320 * Order is important in these assignments since ISR uses _cb
321 * field to determine if it's ok to dereference _cb and _context
323 usb_cdc_dev->rx_in_context = context;
324 usb_cdc_dev->rx_in_cb = rx_in_cb;
327 static void PIOS_USB_CDC_RegisterTxCallback(uint32_t usbcdc_id, pios_com_callback tx_out_cb, uint32_t context)
329 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
331 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
333 PIOS_Assert(valid);
336 * Order is important in these assignments since ISR uses _cb
337 * field to determine if it's ok to dereference _cb and _context
339 usb_cdc_dev->tx_out_context = context;
340 usb_cdc_dev->tx_out_cb = tx_out_cb;
343 static void PIOS_USB_CDC_RegisterCtrlLineCallback(uint32_t usbcdc_id, pios_com_callback_ctrl_line ctrl_line_cb, uint32_t context)
345 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
347 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
349 PIOS_Assert(valid);
352 * Order is important in these assignments since ISR uses _cb
353 * field to determine if it's ok to dereference _cb and _context
355 usb_cdc_dev->ctrl_line_context = context;
356 usb_cdc_dev->ctrl_line_cb = ctrl_line_cb;
359 static void PIOS_USB_CDC_RegisterBaudRateCallback(uint32_t usbcdc_id, pios_com_callback_baud_rate baud_rate_cb, uint32_t context)
361 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
363 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
365 PIOS_Assert(valid);
368 * Order is important in these assignments since ISR uses _cb
369 * field to determine if it's ok to dereference _cb and _context
371 usb_cdc_dev->baud_rate_context = context;
372 usb_cdc_dev->baud_rate_cb = baud_rate_cb;
375 static bool PIOS_USB_CDC_CTRL_EP_IN_Callback(uint32_t usb_cdc_id, uint8_t epnum, uint16_t len);
377 static void PIOS_USB_CDC_CTRL_IF_Init(uint32_t usb_cdc_id)
379 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usb_cdc_id;
381 if (!PIOS_USB_CDC_validate(usb_cdc_dev)) {
382 return;
385 /* Register endpoint specific callbacks with the USBHOOK layer */
386 PIOS_USBHOOK_RegisterEpInCallback(usb_cdc_dev->cfg->ctrl_tx_ep,
387 sizeof(usb_cdc_dev->ctrl_tx_packet_buffer),
388 PIOS_USB_CDC_CTRL_EP_IN_Callback,
389 (uint32_t)usb_cdc_dev);
390 usb_cdc_dev->usb_ctrl_if_enabled = true;
393 static void PIOS_USB_CDC_CTRL_IF_DeInit(uint32_t usb_cdc_id)
395 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usb_cdc_id;
397 if (!PIOS_USB_CDC_validate(usb_cdc_dev)) {
398 return;
401 /* DeRegister endpoint specific callbacks with the USBHOOK layer */
402 usb_cdc_dev->usb_ctrl_if_enabled = false;
405 static uint8_t cdc_altset;
406 static struct usb_cdc_line_coding line_coding = {
407 .dwDTERate = htousbl(57600),
408 .bCharFormat = USB_CDC_LINE_CODING_STOP_1,
409 .bParityType = USB_CDC_LINE_CODING_PARITY_NONE,
410 .bDataBits = 8,
413 static uint16_t control_line_state;
415 static bool PIOS_USB_CDC_CTRL_IF_Setup(uint32_t usb_cdc_id, struct usb_setup_request *req)
417 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usb_cdc_id;
419 if (!PIOS_USB_CDC_validate(usb_cdc_dev)) {
420 return false;
423 /* Make sure this is a request for an interface we know about */
424 uint8_t ifnum = req->wIndex & 0xFF;
425 if (ifnum != usb_cdc_dev->cfg->ctrl_if) {
426 return false;
429 switch (req->bmRequestType & (USB_REQ_TYPE_MASK | USB_REQ_RECIPIENT_MASK)) {
430 case (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_INTERFACE):
431 switch (req->bRequest) {
432 case USB_REQ_GET_INTERFACE:
433 PIOS_USBHOOK_CtrlTx(&cdc_altset, 1);
434 break;
435 case USB_REQ_SET_INTERFACE:
436 cdc_altset = (uint8_t)(req->wValue);
437 break;
438 default:
439 /* Unhandled standard request */
440 return false;
442 break;
444 break;
445 case (USB_REQ_TYPE_CLASS | USB_REQ_RECIPIENT_INTERFACE):
446 switch (req->bRequest) {
447 case USB_CDC_REQ_SET_LINE_CODING:
448 PIOS_USBHOOK_CtrlRx((uint8_t *)&line_coding, sizeof(line_coding));
449 break;
450 case USB_CDC_REQ_GET_LINE_CODING:
451 PIOS_USBHOOK_CtrlTx((uint8_t *)&line_coding, sizeof(line_coding));
452 break;
453 case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
454 control_line_state = req->wValue;
455 if (usb_cdc_dev->ctrl_line_cb) {
456 (usb_cdc_dev->ctrl_line_cb)(usb_cdc_dev->ctrl_line_context,
457 0xff,
458 control_line_state);
460 break;
461 default:
462 /* Unhandled class request */
463 return false;
465 break;
467 break;
468 default:
469 /* Unhandled request */
470 return false;
473 return true;
476 static uint32_t PIOS_USB_CDC_Available(uint32_t usbcdc_id)
478 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
480 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
482 PIOS_Assert(valid);
484 return PIOS_USB_CheckAvailable(usb_cdc_dev->lower_id) ? COM_AVAILABLE_RXTX : COM_AVAILABLE_NONE;
488 * Called *after* the data has been written to the buffer provided in the setup stage. The
489 * setup request is passed in here again so we know *which* EP0 data out has just completed.
491 static void PIOS_USB_CDC_CTRL_IF_CtrlDataOut(uint32_t usb_cdc_id, const struct usb_setup_request *req)
493 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usb_cdc_id;
495 if (!PIOS_USB_CDC_validate(usb_cdc_dev)) {
496 return;
499 /* Make sure this is a request for an interface we know about */
500 uint8_t ifnum = req->wIndex & 0xFF;
501 if (ifnum != usb_cdc_dev->cfg->ctrl_if) {
502 return;
505 switch (req->bmRequestType & (USB_REQ_TYPE_MASK | USB_REQ_RECIPIENT_MASK)) {
506 case (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_INTERFACE):
507 switch (req->bRequest) {
508 default:
509 /* Unhandled standard request */
510 return;
512 break;
514 break;
515 case (USB_REQ_TYPE_CLASS | USB_REQ_RECIPIENT_INTERFACE):
516 switch (req->bRequest) {
517 case USB_CDC_REQ_SET_LINE_CODING:
518 if (usb_cdc_dev->baud_rate_cb) {
519 usb_cdc_dev->baud_rate_cb(usb_cdc_dev->baud_rate_context, usbltoh(line_coding.dwDTERate));
521 break;
522 default:
523 /* Unhandled class request */
524 return;
526 break;
528 break;
529 default:
530 /* Unhandled request */
531 return;
535 static struct usb_cdc_serial_state_report uart_state = {
536 .bmRequestType = 0xA1,
537 .bNotification = USB_CDC_NOTIFICATION_SERIAL_STATE,
538 .wValue = 0,
539 .wIndex = htousbs(1),
540 .wLength = htousbs(2),
541 .bmUartState = htousbs(0),
544 static bool PIOS_USB_CDC_CTRL_EP_IN_Callback(
545 __attribute__((unused)) uint32_t usb_cdc_id,
546 __attribute__((unused)) uint8_t epnum,
547 __attribute__((unused)) uint16_t len)
549 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
551 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
553 PIOS_Assert(valid);
555 /* Give back UART State Bitmap */
556 /* UART State Bitmap
557 * 15-7: reserved
558 * 6: bOverRun overrun error
559 * 5: bParity parity error
560 * 4: bFraming framing error
561 * 3: bRingSignal RI
562 * 2: bBreak break reception
563 * 1: bTxCarrier DSR
564 * 0: bRxCarrier DCD
567 /* Currently, we only handle TxCarrier and RxCarrier reporting */
568 uint16_t new_uart_state = 0;
569 if (usb_cdc_dev->tx_out_cb) {
570 /* Someone is going to providing FC->PC data, advertise an RxCarrier to the host */
571 new_uart_state |= 0x1;
573 if (usb_cdc_dev->rx_in_cb) {
574 /* Someone is consuming PC->FC data, advertise a TxCarrier to the host */
575 new_uart_state |= 0x2;
578 /* Has anything changed since we last sent a notification? */
579 if ((new_uart_state ^ usb_cdc_dev->prev_uart_state) & 0x3) {
580 usb_cdc_dev->prev_uart_state = new_uart_state;
582 uart_state.bmUartState = htousbs(new_uart_state);
584 PIOS_USBHOOK_EndpointTx(usb_cdc_dev->cfg->ctrl_tx_ep,
585 (uint8_t *)&uart_state,
586 sizeof(uart_state));
589 return true;
592 static bool PIOS_USB_CDC_DATA_EP_IN_Callback(uint32_t usb_cdc_id, uint8_t epnum, uint16_t len);
593 static bool PIOS_USB_CDC_DATA_EP_OUT_Callback(uint32_t usb_cdc_id, uint8_t epnum, uint16_t len);
595 static void PIOS_USB_CDC_DATA_IF_Init(uint32_t usb_cdc_id)
597 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usb_cdc_id;
599 if (!PIOS_USB_CDC_validate(usb_cdc_dev)) {
600 return;
603 /* Register endpoint specific callbacks with the USBHOOK layer */
604 PIOS_USBHOOK_RegisterEpInCallback(usb_cdc_dev->cfg->data_tx_ep,
605 sizeof(usb_cdc_dev->tx_packet_buffer),
606 PIOS_USB_CDC_DATA_EP_IN_Callback,
607 (uint32_t)usb_cdc_dev);
608 PIOS_USBHOOK_RegisterEpOutCallback(usb_cdc_dev->cfg->data_rx_ep,
609 sizeof(usb_cdc_dev->rx_packet_buffer),
610 PIOS_USB_CDC_DATA_EP_OUT_Callback,
611 (uint32_t)usb_cdc_dev);
612 usb_cdc_dev->usb_data_if_enabled = true;
613 usb_cdc_dev->tx_active = false;
614 /* Check if rx was previously active, if so we need to reactivate */
615 if (usb_cdc_dev->rx_active) {
616 PIOS_USBHOOK_EndpointRx(usb_cdc_dev->cfg->data_rx_ep,
617 usb_cdc_dev->rx_packet_buffer,
618 sizeof(usb_cdc_dev->rx_packet_buffer));
622 static void PIOS_USB_CDC_DATA_IF_DeInit(uint32_t usb_cdc_id)
624 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usb_cdc_id;
626 if (!PIOS_USB_CDC_validate(usb_cdc_dev)) {
627 return;
630 /* DeRegister endpoint specific callbacks with the USBHOOK layer */
631 usb_cdc_dev->usb_data_if_enabled = false;
632 PIOS_USBHOOK_DeRegisterEpInCallback(usb_cdc_dev->cfg->data_tx_ep);
633 PIOS_USBHOOK_DeRegisterEpOutCallback(usb_cdc_dev->cfg->data_rx_ep);
636 static bool PIOS_USB_CDC_DATA_IF_Setup(
637 __attribute__((unused)) uint32_t usb_cdc_id,
638 __attribute__((unused)) struct usb_setup_request *req)
640 /* There are no valid EP0 transactions for CDC DATA interfaces */
641 PIOS_Assert(0);
643 return false;
647 * Called *after* the data has been written to the buffer provided in the setup stage. The
648 * setup request is passed in here again so we know *which* EP0 data out has just completed.
650 static void PIOS_USB_CDC_DATA_IF_CtrlDataOut(
651 __attribute__((unused)) uint32_t usb_cdc_id,
652 __attribute__((unused)) const struct usb_setup_request *req)
654 /* CDC DATA interfaces don't have any OUT data stages on the control endpoint */
655 PIOS_Assert(0);
659 * @brief Callback used to indicate a transmission from device INto host completed
660 * Checks if any data remains, pads it into HID packet and sends.
662 static bool PIOS_USB_CDC_DATA_EP_IN_Callback(
663 __attribute__((unused)) uint32_t usb_cdc_id,
664 __attribute__((unused)) uint8_t epnum,
665 __attribute__((unused)) uint16_t len)
667 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
669 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
671 PIOS_Assert(valid);
673 bool rc = PIOS_USB_CDC_SendData(usb_cdc_dev);
674 if (!rc) {
675 /* No additional data was transmitted, note that tx is no longer active */
676 usb_cdc_dev->tx_active = false;
679 return rc;
682 static bool PIOS_USB_CDC_DATA_EP_OUT_Callback(
683 __attribute__((unused)) uint32_t usb_cdc_id,
684 __attribute__((unused)) uint8_t epnum,
685 uint16_t len)
687 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)pios_usb_cdc_id;
689 if (!PIOS_USB_CDC_validate(usb_cdc_dev)) {
690 return false;
693 if (len > sizeof(usb_cdc_dev->rx_packet_buffer)) {
694 len = sizeof(usb_cdc_dev->rx_packet_buffer);
697 if (!usb_cdc_dev->rx_in_cb) {
698 /* No Rx call back registered, disable the receiver */
699 return false;
702 uint16_t headroom;
703 bool need_yield = false;
704 uint16_t bytes_rxed;
705 bytes_rxed = (usb_cdc_dev->rx_in_cb)(usb_cdc_dev->rx_in_context,
706 usb_cdc_dev->rx_packet_buffer,
707 len,
708 &headroom,
709 &need_yield);
711 if (bytes_rxed < len) {
712 /* Lost bytes on rx */
713 usb_cdc_dev->rx_dropped += (len - bytes_rxed);
716 bool rc;
717 if (headroom >= sizeof(usb_cdc_dev->rx_packet_buffer)) {
718 /* We have room for a maximum length message */
719 PIOS_USBHOOK_EndpointRx(usb_cdc_dev->cfg->data_rx_ep,
720 usb_cdc_dev->rx_packet_buffer,
721 sizeof(usb_cdc_dev->rx_packet_buffer));
722 rc = true;
723 usb_cdc_dev->rx_active = true;
724 } else {
725 /* Not enough room left for a message, apply backpressure */
726 rc = false;
727 usb_cdc_dev->rx_active = false;
730 #if defined(PIOS_INCLUDE_FREERTOS)
731 if (need_yield) {
732 vPortYield();
734 #endif /* PIOS_INCLUDE_FREERTOS */
736 return rc;
739 static void PIOS_USB_CDC_ChangeConnectionState(__attribute__((unused)) bool connected, uint32_t usbcdc_id)
741 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
743 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
745 PIOS_Assert(valid);
747 if (usb_cdc_dev->available_cb) {
748 (usb_cdc_dev->available_cb)(usb_cdc_dev->available_context, PIOS_USB_CDC_Available(usbcdc_id));
753 static void PIOS_USB_CDC_RegisterAvailableCallback(uint32_t usbcdc_id, pios_com_callback_available available_cb, uint32_t context)
755 struct pios_usb_cdc_dev *usb_cdc_dev = (struct pios_usb_cdc_dev *)usbcdc_id;
757 bool valid = PIOS_USB_CDC_validate(usb_cdc_dev);
759 PIOS_Assert(valid);
762 * Order is important in these assignments since ISR uses _cb
763 * field to determine if it's ok to dereference _cb and _context
765 usb_cdc_dev->available_context = context;
766 usb_cdc_dev->available_cb = available_cb;
769 #endif /* PIOS_INCLUDE_USB_CDC */