2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
28 #include "build/build_config.h"
30 #include "common/utils.h"
32 #include "drivers/io.h"
38 #include "usbd_cdc_vcp.h"
39 #ifdef USE_USB_CDC_HID
40 #include "usbd_hid_cdc_wrapper.h"
43 #elif defined(STM32F7) || defined(STM32H7) || defined(STM32G4)
44 #include "vcp_hal/usbd_cdc_interface.h"
46 #ifdef USE_USB_CDC_HID
47 #include "usbd_ioreq.h"
49 extern USBD_ClassTypeDef USBD_HID_CDC
;
51 USBD_HandleTypeDef USBD_Device
;
55 #include "hw_config.h"
58 #include "drivers/time.h"
61 #include "serial_usb_vcp.h"
64 #define USB_TIMEOUT 50
66 static vcpPort_t vcpPort
;
68 static void usbVcpSetBaudRate(serialPort_t
*instance
, uint32_t baudRate
)
76 static void usbVcpSetMode(serialPort_t
*instance
, portMode_e mode
)
84 static void usbVcpSetCtrlLineStateCb(serialPort_t
*instance
, void (*cb
)(void *context
, uint16_t ctrlLineState
), void *context
)
88 // Register upper driver control line state callback routine with USB driver
89 CDC_SetCtrlLineStateCb((void (*)(void *context
, uint16_t ctrlLineState
))cb
, context
);
92 static void usbVcpSetBaudRateCb(serialPort_t
*instance
, void (*cb
)(serialPort_t
*context
, uint32_t baud
), serialPort_t
*context
)
96 // Register upper driver baud rate callback routine with USB driver
97 CDC_SetBaudRateCb((void (*)(void *context
, uint32_t baud
))cb
, (void *)context
);
100 static bool isUsbVcpTransmitBufferEmpty(const serialPort_t
*instance
)
106 static uint32_t usbVcpAvailable(const serialPort_t
*instance
)
110 return CDC_Receive_BytesAvailable();
113 static uint8_t usbVcpRead(serialPort_t
*instance
)
120 if (CDC_Receive_DATA(buf
, 1))
125 static void usbVcpWriteBuf(serialPort_t
*instance
, const void *data
, int count
)
129 if (!(usbIsConnected() && usbIsConfigured())) {
133 uint32_t start
= millis();
134 const uint8_t *p
= data
;
136 uint32_t txed
= CDC_Send_DATA(p
, count
);
140 if (millis() - start
> USB_TIMEOUT
) {
146 static bool usbVcpFlush(vcpPort_t
*port
)
148 uint32_t count
= port
->txAt
;
155 if (!usbIsConnected() || !usbIsConfigured()) {
159 uint32_t start
= millis();
160 uint8_t *p
= port
->txBuf
;
162 uint32_t txed
= CDC_Send_DATA(p
, count
);
166 if (millis() - start
> USB_TIMEOUT
) {
173 static void usbVcpWrite(serialPort_t
*instance
, uint8_t c
)
175 vcpPort_t
*port
= container_of(instance
, vcpPort_t
, port
);
177 port
->txBuf
[port
->txAt
++] = c
;
178 if (!port
->buffering
|| port
->txAt
>= ARRAYLEN(port
->txBuf
)) {
183 static void usbVcpBeginWrite(serialPort_t
*instance
)
185 vcpPort_t
*port
= container_of(instance
, vcpPort_t
, port
);
186 port
->buffering
= true;
189 static uint32_t usbTxBytesFree(const serialPort_t
*instance
)
192 return CDC_Send_FreeBytes();
195 static void usbVcpEndWrite(serialPort_t
*instance
)
197 vcpPort_t
*port
= container_of(instance
, vcpPort_t
, port
);
198 port
->buffering
= false;
202 static const struct serialPortVTable usbVTable
[] = {
204 .serialWrite
= usbVcpWrite
,
205 .serialTotalRxWaiting
= usbVcpAvailable
,
206 .serialTotalTxFree
= usbTxBytesFree
,
207 .serialRead
= usbVcpRead
,
208 .serialSetBaudRate
= usbVcpSetBaudRate
,
209 .isSerialTransmitBufferEmpty
= isUsbVcpTransmitBufferEmpty
,
210 .setMode
= usbVcpSetMode
,
211 .setCtrlLineStateCb
= usbVcpSetCtrlLineStateCb
,
212 .setBaudRateCb
= usbVcpSetBaudRateCb
,
213 .writeBuf
= usbVcpWriteBuf
,
214 .beginWrite
= usbVcpBeginWrite
,
215 .endWrite
= usbVcpEndWrite
219 serialPort_t
*usbVcpOpen(void)
223 IOInit(IOGetByTag(IO_TAG(PA11
)), OWNER_USB
, 0);
224 IOInit(IOGetByTag(IO_TAG(PA12
)), OWNER_USB
, 0);
227 usbGenerateDisconnectPulse();
229 switch (usbDevConfig()->type
) {
230 #ifdef USE_USB_CDC_HID
232 USBD_Init(&USB_OTG_dev
, USB_OTG_FS_CORE_ID
, &USR_desc
, &USBD_HID_CDC_cb
, &USR_cb
);
236 USBD_Init(&USB_OTG_dev
, USB_OTG_FS_CORE_ID
, &USR_desc
, &USBD_CDC_cb
, &USR_cb
);
239 #elif defined(STM32F7) || defined(STM32H7) || defined(STM32G4)
241 usbGenerateDisconnectPulse();
243 /* Init Device Library */
244 USBD_Init(&USBD_Device
, &VCP_Desc
, 0);
246 /* Add Supported Class */
247 switch (usbDevConfig()->type
) {
248 #ifdef USE_USB_CDC_HID
250 USBD_RegisterClass(&USBD_Device
, &USBD_HID_CDC
);
254 USBD_RegisterClass(&USBD_Device
, USBD_CDC_CLASS
);
258 /* HID Interface doesn't have any callbacks... */
259 /* Add CDC Interface Class */
260 USBD_CDC_RegisterInterface(&USBD_Device
, &USBD_CDC_fops
);
262 /* Start Device Process */
263 USBD_Start(&USBD_Device
);
266 HAL_PWREx_EnableUSBVoltageDetector();
267 delay(100); // Cold boot failures observed without this, even when USB cable is not connected
275 USB_Interrupts_Config();
279 s
->port
.vTable
= usbVTable
;
281 return (serialPort_t
*)s
;
284 uint32_t usbVcpGetBaudRate(serialPort_t
*instance
)
288 return CDC_BaudRate();
291 uint8_t usbVcpIsConnected(void)
293 return usbIsConnected();