Merge pull request #11198 from SteveCEvans/sce_rc2
[betaflight.git] / src / main / drivers / serial_usb_vcp.c
blob0449dcb65693ac452b714e818423b4cea24c454b
1 /*
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)
8 * any later version.
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/>.
21 #include <stdint.h>
22 #include <stdbool.h>
24 #include "platform.h"
26 #ifdef USE_VCP
28 #include "build/build_config.h"
30 #include "common/utils.h"
32 #include "drivers/io.h"
34 #include "pg/usb.h"
36 #if defined(STM32F4)
37 #include "usb_core.h"
38 #include "usbd_cdc_vcp.h"
39 #ifdef USE_USB_CDC_HID
40 #include "usbd_hid_cdc_wrapper.h"
41 #endif
42 #include "usb_io.h"
43 #elif defined(STM32F7) || defined(STM32H7) || defined(STM32G4)
44 #include "vcp_hal/usbd_cdc_interface.h"
45 #include "usb_io.h"
46 #ifdef USE_USB_CDC_HID
47 #include "usbd_ioreq.h"
49 extern USBD_ClassTypeDef USBD_HID_CDC;
50 #endif
51 USBD_HandleTypeDef USBD_Device;
52 #else
53 #include "usb_core.h"
54 #include "usb_init.h"
55 #include "hw_config.h"
56 #endif
58 #include "drivers/time.h"
60 #include "serial.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)
70 UNUSED(instance);
71 UNUSED(baudRate);
73 // TODO implement
76 static void usbVcpSetMode(serialPort_t *instance, portMode_e mode)
78 UNUSED(instance);
79 UNUSED(mode);
81 // TODO implement
84 static void usbVcpSetCtrlLineStateCb(serialPort_t *instance, void (*cb)(void *context, uint16_t ctrlLineState), void *context)
86 UNUSED(instance);
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)
94 UNUSED(instance);
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)
102 UNUSED(instance);
103 return true;
106 static uint32_t usbVcpAvailable(const serialPort_t *instance)
108 UNUSED(instance);
110 return CDC_Receive_BytesAvailable();
113 static uint8_t usbVcpRead(serialPort_t *instance)
115 UNUSED(instance);
117 uint8_t buf[1];
119 while (true) {
120 if (CDC_Receive_DATA(buf, 1))
121 return buf[0];
125 static void usbVcpWriteBuf(serialPort_t *instance, const void *data, int count)
127 UNUSED(instance);
129 if (!(usbIsConnected() && usbIsConfigured())) {
130 return;
133 uint32_t start = millis();
134 const uint8_t *p = data;
135 while (count > 0) {
136 uint32_t txed = CDC_Send_DATA(p, count);
137 count -= txed;
138 p += txed;
140 if (millis() - start > USB_TIMEOUT) {
141 break;
146 static bool usbVcpFlush(vcpPort_t *port)
148 uint32_t count = port->txAt;
149 port->txAt = 0;
151 if (count == 0) {
152 return true;
155 if (!usbIsConnected() || !usbIsConfigured()) {
156 return false;
159 uint32_t start = millis();
160 uint8_t *p = port->txBuf;
161 while (count > 0) {
162 uint32_t txed = CDC_Send_DATA(p, count);
163 count -= txed;
164 p += txed;
166 if (millis() - start > USB_TIMEOUT) {
167 break;
170 return count == 0;
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)) {
179 usbVcpFlush(port);
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)
191 UNUSED(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;
199 usbVcpFlush(port);
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)
221 vcpPort_t *s;
223 IOInit(IOGetByTag(IO_TAG(PA11)), OWNER_USB, 0);
224 IOInit(IOGetByTag(IO_TAG(PA12)), OWNER_USB, 0);
226 #if defined(STM32F4)
227 usbGenerateDisconnectPulse();
229 switch (usbDevConfig()->type) {
230 #ifdef USE_USB_CDC_HID
231 case COMPOSITE:
232 USBD_Init(&USB_OTG_dev, USB_OTG_FS_CORE_ID, &USR_desc, &USBD_HID_CDC_cb, &USR_cb);
233 break;
234 #endif
235 default:
236 USBD_Init(&USB_OTG_dev, USB_OTG_FS_CORE_ID, &USR_desc, &USBD_CDC_cb, &USR_cb);
237 break;
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
249 case COMPOSITE:
250 USBD_RegisterClass(&USBD_Device, &USBD_HID_CDC);
251 break;
252 #endif
253 default:
254 USBD_RegisterClass(&USBD_Device, USBD_CDC_CLASS);
255 break;
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);
265 #ifdef STM32H7
266 HAL_PWREx_EnableUSBVoltageDetector();
267 delay(100); // Cold boot failures observed without this, even when USB cable is not connected
268 #endif
271 #else
272 Set_System();
273 Set_USBClock();
274 USB_Init();
275 USB_Interrupts_Config();
276 #endif
278 s = &vcpPort;
279 s->port.vTable = usbVTable;
281 return (serialPort_t *)s;
284 uint32_t usbVcpGetBaudRate(serialPort_t *instance)
286 UNUSED(instance);
288 return CDC_BaudRate();
291 uint8_t usbVcpIsConnected(void)
293 return usbIsConnected();
295 #endif