4 * @brief usb device CDC class Virtual Com Port handler
8 * Copyright (C) 2023 Geehy Semiconductor
10 * You may not use this file except in compliance with the
11 * GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
13 * The program is only for reference, which is distributed in the hope
14 * that it will be useful and instructional for customers to develop
15 * their software. Unless required by applicable law or agreed to in
16 * writing, the program is distributed on an "AS IS" BASIS, WITHOUT
17 * ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
19 * and limitations under the License.
22 /* Includes ***************************************************************/
23 #include "usbd_cdc_vcp.h"
25 /* Private includes *******************************************************/
30 #include "build/atomic.h"
32 #include "drivers/nvic.h"
33 #include "drivers/serial_usb_vcp.h"
34 #include "drivers/time.h"
36 /* Private macro **********************************************************/
37 #define APP_RX_DATA_SIZE 2048
38 #define APP_TX_DATA_SIZE 2048
40 #define APP_TX_BLOCK_SIZE 512
42 /* Private variables ******************************************************/
43 volatile uint8_t cdcTxBuffer
[APP_RX_DATA_SIZE
];
44 volatile uint8_t cdcRxBuffer
[APP_TX_DATA_SIZE
];
46 volatile uint32_t cdcTxBufPtrIn
= 0;/* Increment this pointer or roll it back to
47 start address when data are received over USART */
48 volatile uint32_t cdcTxBufPtrOut
= 0; /* Increment this pointer or roll it back to
49 start address when data are sent over USB */
51 uint32_t rxAvailable
= 0;
52 uint8_t* rxBuffPtr
= NULL
;
54 /* Private typedef ********************************************************/
56 static USBD_STA_T
USBD_CDC_ItfInit(void);
57 static USBD_STA_T
USBD_CDC_ItfDeInit(void);
58 static USBD_STA_T
USBD_CDC_ItfCtrl(uint8_t command
, uint8_t *buffer
, uint16_t length
);
59 static USBD_STA_T
USBD_CDC_ItfSend(uint8_t *buffer
, uint16_t length
);
60 static USBD_STA_T
USBD_CDC_ItfSendEnd(uint8_t epNum
, uint8_t *buffer
, uint32_t *length
);
61 static USBD_STA_T
USBD_CDC_ItfReceive(uint8_t *buffer
, uint32_t *length
);
62 static USBD_STA_T
USBD_CDC_ItfSOF(void);
65 /* USB CDC interface handler */
66 USBD_CDC_INTERFACE_T USBD_CDC_INTERFACE
=
78 /* CDC Line Code Information */
79 USBD_CDC_LINE_CODING_T LineCoding
=
81 115200, /*!< baud rate */
82 0x00, /*!< stop bits 1 */
83 0x00, /*!< parity none */
84 0x08, /*!< word length 8 bits */
87 /* Private function prototypes ********************************************/
88 static void (*ctrlLineStateCb
)(void *context
, uint16_t ctrlLineState
);
89 static void *ctrlLineStateCbContext
;
90 static void (*baudRateCb
)(void *context
, uint32_t baud
);
91 static void *baudRateCbContext
;
93 /* External variables *****************************************************/
94 extern USBD_INFO_T gUsbDevice
;
96 /* External functions *****************************************************/
99 * @brief USB device initializes CDC media handler
103 * @retval USB device operation status
105 static USBD_STA_T
USBD_CDC_ItfInit(void)
107 USBD_STA_T usbStatus
= USBD_OK
;
109 USBD_CDC_ConfigRxBuffer(&gUsbDevice
, (uint8_t *)cdcRxBuffer
);
110 USBD_CDC_ConfigTxBuffer(&gUsbDevice
, (uint8_t *)cdcTxBuffer
, 0);
112 ctrlLineStateCb
= NULL
;
119 * @brief USB device deinitializes CDC media handler
123 * @retval USB device operation status
125 static USBD_STA_T
USBD_CDC_ItfDeInit(void)
127 USBD_STA_T usbStatus
= USBD_OK
;
133 * @brief USB device CDC interface control request handler
135 * @param command: Command code
137 * @param buffer: Command data buffer
139 * @param length: Command data length
141 * @retval USB device operation status
143 static USBD_STA_T
USBD_CDC_ItfCtrl(uint8_t command
, uint8_t *buffer
, uint16_t length
)
145 USBD_STA_T usbStatus
= USBD_OK
;
146 LINE_CODING
* plc
= (LINE_CODING
*)buffer
;
150 case USBD_CDC_SEND_ENCAPSULATED_COMMAND
:
153 case USBD_CDC_GET_ENCAPSULATED_RESPONSE
:
156 case USBD_CDC_SET_COMM_FEATURE
:
159 case USBD_CDC_GET_COMM_FEATURE
:
162 case USBD_CDC_CLEAR_COMM_FEATURE
:
165 /* Line Coding Data Structure
166 * | Offset(Byte) | Field | Length | Desc |
167 * | 0 | dwDTERate | 4 | Data Terminal rate |
168 * | 4 | bCharFormat | 1 | Stop bits |
170 * (1 : 1.5 Stop bits)
172 * | 5 | bParityType | 1 | Parity |
178 * | 6 | bDataBits | 1 | Data bits |
185 case USBD_CDC_SET_LINE_CODING
:
186 if (buffer
&& (length
== sizeof(*plc
))) {
187 LineCoding
.baudRate
= plc
->bitrate
;
188 LineCoding
.format
= plc
->format
;
189 LineCoding
.parityType
= plc
->paritytype
;
190 LineCoding
.WordLen
= plc
->datatype
;
192 // If a callback is provided, tell the upper driver of changes in baud rate
194 baudRateCb(baudRateCbContext
, LineCoding
.baudRate
);
199 case USBD_CDC_GET_LINE_CODING
:
200 if (buffer
&& (length
== sizeof(*plc
))) {
201 plc
->bitrate
= LineCoding
.baudRate
;
202 plc
->format
= LineCoding
.format
;
203 plc
->paritytype
= LineCoding
.parityType
;
204 plc
->datatype
= LineCoding
.WordLen
;
208 case USBD_CDC_SET_CONTROL_LINE_STATE
:
209 // If a callback is provided, tell the upper driver of changes in DTR/RTS state
210 if (buffer
&& (length
== sizeof(uint16_t))) {
211 if (ctrlLineStateCb
) {
212 ctrlLineStateCb(ctrlLineStateCbContext
, *((uint16_t *)buffer
));
217 case USBD_CDC_SEND_BREAK
:
228 * @brief USB device CDC interface send handler
230 * @param buffer: Command data buffer
232 * @param length: Command data length
234 * @retval USB device operation status
236 static USBD_STA_T
USBD_CDC_ItfSend(uint8_t *buffer
, uint16_t length
)
238 USBD_STA_T usbStatus
= USBD_OK
;
240 USBD_CDC_INFO_T
*usbDevCDC
= (USBD_CDC_INFO_T
*)gUsbDevice
.devClass
[gUsbDevice
.classID
]->classData
;
242 if(usbDevCDC
->cdcTx
.state
!= USBD_CDC_XFER_IDLE
)
247 USBD_CDC_ConfigTxBuffer(&gUsbDevice
, buffer
, length
);
249 usbStatus
= USBD_CDC_TxPacket(&gUsbDevice
);
255 * @brief USB device CDC interface send end event handler
257 * @param epNum: endpoint number
259 * @param buffer: Command data buffer
261 * @param length: Command data length
263 * @retval USB device operation status
265 static USBD_STA_T
USBD_CDC_ItfSendEnd(uint8_t epNum
, uint8_t *buffer
, uint32_t *length
)
267 USBD_STA_T usbStatus
= USBD_OK
;
277 * @brief USB device CDC interface receive handler
279 * @param buffer: Command data buffer
281 * @param length: Command data length
283 * @retval USB device operation status
285 static USBD_STA_T
USBD_CDC_ItfReceive(uint8_t *buffer
, uint32_t *length
)
287 USBD_STA_T usbStatus
= USBD_OK
;
289 // USBD_CDC_ConfigRxBuffer(&gUsbDevice, &buffer[0]);
290 rxAvailable
= *length
;
293 // Received an empty packet, trigger receiving the next packet.
294 // This will happen after a packet that's exactly 64 bytes is received.
295 // The USB protocol requires that an empty (0 byte) packet immediately follow.
296 USBD_CDC_RxPacket(&gUsbDevice
);
302 static USBD_STA_T
USBD_CDC_ItfSOF(void)
304 static uint32_t FrameCount
= 0;
307 static uint32_t lastBuffsize
= 0;
309 USBD_CDC_INFO_T
*usbDevCDC
= (USBD_CDC_INFO_T
*)gUsbDevice
.devClass
[gUsbDevice
.classID
]->classData
;
311 if (FrameCount
++ == USBD_CDC_FS_INTERVAL
)
315 if (usbDevCDC
->cdcTx
.state
== USBD_CDC_XFER_IDLE
) {
316 // endpoint has finished transmitting previous block
318 bool needZeroLengthPacket
= lastBuffsize
% 64 == 0;
320 // move the ring buffer tail based on the previous succesful transmission
321 cdcTxBufPtrOut
+= lastBuffsize
;
322 if (cdcTxBufPtrOut
== APP_TX_DATA_SIZE
) {
327 if (needZeroLengthPacket
) {
328 USBD_CDC_ConfigTxBuffer(&gUsbDevice
, (uint8_t*)&cdcTxBuffer
[cdcTxBufPtrOut
], 0);
332 if (cdcTxBufPtrOut
!= cdcTxBufPtrIn
) {
333 if (cdcTxBufPtrOut
> cdcTxBufPtrIn
) { /* Roll-back */
334 buffsize
= APP_TX_DATA_SIZE
- cdcTxBufPtrOut
;
336 buffsize
= cdcTxBufPtrIn
- cdcTxBufPtrOut
;
338 if (buffsize
> APP_TX_BLOCK_SIZE
) {
339 buffsize
= APP_TX_BLOCK_SIZE
;
342 USBD_CDC_ConfigTxBuffer(&gUsbDevice
, (uint8_t*)&cdcTxBuffer
[cdcTxBufPtrOut
], buffsize
);
344 if (USBD_CDC_TxPacket(&gUsbDevice
) == USBD_OK
) {
345 lastBuffsize
= buffsize
;
354 /*******************************************************************************
355 * Function Name : Send DATA .
356 * Description : send the data received from the STM32 to the PC through USB
357 * Input : buffer to send, and the length of the buffer.
360 *******************************************************************************/
361 uint32_t CDC_Send_DATA(const uint8_t *ptrBuffer
, uint32_t sendLength
)
363 for (uint32_t i
= 0; i
< sendLength
; i
++) {
364 while (CDC_Send_FreeBytes() == 0) {
365 // block until there is free space in the ring buffer
368 ATOMIC_BLOCK(NVIC_BUILD_PRIORITY(6, 0)) { // Paranoia
369 cdcTxBuffer
[cdcTxBufPtrIn
] = ptrBuffer
[i
];
370 cdcTxBufPtrIn
= (cdcTxBufPtrIn
+ 1) % APP_TX_DATA_SIZE
;
376 /*******************************************************************************
377 * Function Name : Receive DATA .
378 * Description : receive the data from the PC to STM32 and send it through USB
382 *******************************************************************************/
383 uint32_t CDC_Receive_DATA(uint8_t* recvBuf
, uint32_t len
)
386 if ( (rxBuffPtr
!= NULL
))
388 while ((rxAvailable
> 0) && count
< len
)
390 recvBuf
[count
] = rxBuffPtr
[0];
395 USBD_CDC_RxPacket(&gUsbDevice
);
401 uint32_t CDC_Receive_BytesAvailable(void)
406 uint32_t CDC_Send_FreeBytes(void)
409 return the bytes free in the circular buffer
411 functionally equivalent to:
412 (APP_Rx_ptr_out > APP_Rx_ptr_in ? APP_Rx_ptr_out - APP_Rx_ptr_in : APP_RX_DATA_SIZE - APP_Rx_ptr_in + APP_Rx_ptr_in)
413 but without the impact of the condition check.
417 ATOMIC_BLOCK(NVIC_BUILD_PRIORITY(6, 0)) {
418 freeBytes
= ((cdcTxBufPtrOut
- cdcTxBufPtrIn
) + (-((int)(cdcTxBufPtrOut
<= cdcTxBufPtrIn
)) & APP_TX_DATA_SIZE
)) - 1;
424 /*******************************************************************************
425 * Function Name : usbIsConfigured.
426 * Description : Determines if USB VCP is configured or not
429 * Return : True if configured.
430 *******************************************************************************/
431 uint8_t usbIsConfigured(void)
433 return (gUsbDevice
.devState
== USBD_DEV_CONFIGURE
);
436 /*******************************************************************************
437 * Function Name : usbIsConnected.
438 * Description : Determines if USB VCP is connected ot not
441 * Return : True if connected.
442 *******************************************************************************/
443 uint8_t usbIsConnected(void)
445 return (gUsbDevice
.devState
!= USBD_DEV_DEFAULT
);
448 /*******************************************************************************
449 * Function Name : CDC_BaudRate.
450 * Description : Get the current baud rate
453 * Return : Baud rate in bps
454 *******************************************************************************/
455 uint32_t CDC_BaudRate(void)
457 return LineCoding
.baudRate
;
460 /*******************************************************************************
461 * Function Name : CDC_SetBaudRateCb
462 * Description : Set a callback to call when baud rate changes
463 * Input : callback function and context.
466 *******************************************************************************/
467 void CDC_SetBaudRateCb(void (*cb
)(void *context
, uint32_t baud
), void *context
)
469 baudRateCbContext
= context
;
473 /*******************************************************************************
474 * Function Name : CDC_SetCtrlLineStateCb
475 * Description : Set a callback to call when control line state changes
476 * Input : callback function and context.
479 *******************************************************************************/
480 void CDC_SetCtrlLineStateCb(void (*cb
)(void *context
, uint16_t ctrlLineState
), void *context
)
482 ctrlLineStateCbContext
= context
;
483 ctrlLineStateCb
= cb
;