Sfoglia il codice sorgente

Silicon Laboratories CP210x USB to RS232 serial adaptor driver is suppported

lenovo 2 anni fa
parent
commit
f672591d58

+ 5 - 0
SConscript

@@ -114,6 +114,11 @@ if GetDepend(['PKG_CHERRYUSB_HOST']):
     if GetDepend(['PKG_CHERRYUSB_HOST_TEMPLATE']):
         src += Glob('demo/usb_host.c')
 
+    if GetDepend(['PKG_CHERRYUSB_HOST_CP210X']):
+        path += [cwd + '/class/vendor/cp201x']
+        src += Glob('class/vendor/cp201x/usbh_cp210x.c')
+        src += Glob('third_party/rt-thread-4.1.1/dfs/drv_usbh_cp210x_rtt.c')
+
 group = DefineGroup('CherryUSB', src, depend = ['PKG_USING_CHERRYUSB'], CPPPATH = path, CPPDEFINES = CPPDEFINES)
 
 Return('group')

+ 1763 - 0
class/vendor/cp201x/usbh_cp210x.c

@@ -0,0 +1,1763 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Silicon Laboratories CP210x USB to RS232 serial adaptor driver
+ *
+ * Copyright (C) 2005 Craig Shelley (craig@microtron.org.uk)
+ *
+ * Support to set flow control line levels using TIOCMGET and TIOCMSET
+ * thanks to Karl Hiramoto karl@hiramoto.org. RTSCTS hardware flow
+ * control thanks to Munir Nassar nassarmu@real-time.com
+ *
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <string.h>
+#include <limits.h>
+
+#include "usbh_cp210x.h"
+
+#define u16 uint16_t
+#define u32 uint32_t
+#define u8  uint8_t
+
+#ifndef container_of
+#define container_of(p, t, m) \
+    ((t *)((char *)p - (char *)(&(((t *)0)->m))))
+#endif
+
+#define dev_dbg(...)  do {} while (0)
+#define dev_err  USB_LOG_ERR
+#define dev_warn USB_LOG_WRN
+
+#warning FIXME: le32_to_cpu
+#define le32_to_cpu(le32_val) (le32_val)
+#define le16_to_cpu(v) (v)
+#define cpu_to_le32(v) (v)
+	
+
+#define USB_CTRL_SET_TIMEOUT 1
+#define USB_CTRL_GET_TIMEOUT 1
+
+#define TIOCSTI    0x5412
+#define TIOCMGET   0x5415
+#define TIOCMBIS   0x5416
+#define TIOCMBIC   0x5417
+#define TIOCMSET   0x5418
+#define TIOCM_LE   0x001
+#define TIOCM_DTR  0x002
+#define TIOCM_RTS  0x004
+#define TIOCM_ST   0x008
+#define TIOCM_SR   0x010
+#define TIOCM_CTS  0x020
+#define TIOCM_CAR  0x040
+#define TIOCM_RNG  0x080
+#define TIOCM_DSR  0x100
+#define TIOCM_CD   TIOCM_CAR
+#define TIOCM_RI   TIOCM_RNG
+#define TIOCM_OUT1 0x2000
+#define TIOCM_OUT2 0x4000
+#define TIOCM_LOOP 0x8000
+
+/* c_cc characters */
+#define VEOF     0
+#define VEOL     1
+#define VEOL2    2
+#define VERASE   3
+#define VWERASE  4
+#define VKILL    5
+#define VREPRINT 6
+#define VSWTC    7
+#define VINTR    8
+#define VQUIT    9
+#define VSUSP    10
+#define VSTART   12
+#define VSTOP    13
+#define VLNEXT   14
+#define VDISCARD 15
+#define VMIN     16
+#define VTIME    17
+
+/* c_iflag bits */
+#define IGNBRK  0000001
+#define BRKINT  0000002
+#define IGNPAR  0000004
+#define PARMRK  0000010
+#define INPCK   0000020
+#define ISTRIP  0000040
+#define INLCR   0000100
+#define IGNCR   0000200
+#define ICRNL   0000400
+#define IXON    0001000
+#define IXOFF   0002000
+#define IXANY   0004000
+#define IUCLC   0010000
+#define IMAXBEL 0020000
+#define IUTF8   0040000
+
+/* c_oflag bits */
+#define OPOST 0000001
+#define ONLCR 0000002
+#define OLCUC 0000004
+
+#define OCRNL  0000010
+#define ONOCR  0000020
+#define ONLRET 0000040
+
+#define OFILL  00000100
+#define OFDEL  00000200
+#define NLDLY  00001400
+#define NL0    00000000
+#define NL1    00000400
+#define NL2    00001000
+#define NL3    00001400
+#define TABDLY 00006000
+#define TAB0   00000000
+#define TAB1   00002000
+#define TAB2   00004000
+#define TAB3   00006000
+#define CRDLY  00030000
+#define CR0    00000000
+#define CR1    00010000
+#define CR2    00020000
+#define CR3    00030000
+#define FFDLY  00040000
+#define FF0    00000000
+#define FF1    00040000
+#define BSDLY  00100000
+#define BS0    00000000
+#define BS1    00100000
+#define VTDLY  00200000
+#define VT0    00000000
+#define VT1    00200000
+/*
+ * Should be equivalent to TAB3, see description of TAB3 in
+ * POSIX.1-2008, Ch. 11.2.3 "Output Modes"
+ */
+#define XTABS TAB3
+
+/* c_cflag bit meaning */
+#define CBAUD    0000037
+#define B0       0000000 /* hang up */
+#define B50      0000001
+#define B75      0000002
+#define B110     0000003
+#define B134     0000004
+#define B150     0000005
+#define B200     0000006
+#define B300     0000007
+#define B600     0000010
+#define B1200    0000011
+#define B1800    0000012
+#define B2400    0000013
+#define B4800    0000014
+#define B9600    0000015
+#define B19200   0000016
+#define B38400   0000017
+#define EXTA     B19200
+#define EXTB     B38400
+#define CBAUDEX  0000000
+#define B57600   00020
+#define B115200  00021
+#define B230400  00022
+#define B460800  00023
+#define B500000  00024
+#define B576000  00025
+#define B921600  00026
+#define B1000000 00027
+#define B1152000 00030
+#define B1500000 00031
+#define B2000000 00032
+#define B2500000 00033
+#define B3000000 00034
+#define B3500000 00035
+#define B4000000 00036
+#define BOTHER   00037
+
+#define CSIZE 00001400
+#define CS5   00000000
+#define CS6   00000400
+#define CS7   00001000
+#define CS8   00001400
+
+#define CSTOPB 00002000
+#define CREAD  00004000
+#define PARENB 00010000
+#define PARODD 00020000
+#define HUPCL  00040000
+
+#define CLOCAL  00100000
+#define CMSPAR  010000000000 /* mark or space (stick) parity */
+#define CRTSCTS 020000000000 /* flow control */
+
+#define CIBAUD  07600000
+#define IBSHIFT 16
+
+/* c_lflag bits */
+#define ISIG    0x00000080
+#define ICANON  0x00000100
+#define XCASE   0x00004000
+#define ECHO    0x00000008
+#define ECHOE   0x00000002
+#define ECHOK   0x00000004
+#define ECHONL  0x00000010
+#define NOFLSH  0x80000000
+#define TOSTOP  0x00400000
+#define ECHOCTL 0x00000040
+#define ECHOPRT 0x00000020
+#define ECHOKE  0x00000001
+#define FLUSHO  0x00800000
+#define PENDIN  0x20000000
+#define IEXTEN  0x00000400
+#define EXTPROC 0x10000000
+
+/* Values for the ACTION argument to `tcflow'.  */
+#define TCOOFF 0
+#define TCOON  1
+#define TCIOFF 2
+#define TCION  3
+
+/* Values for the QUEUE_SELECTOR argument to `tcflush'.  */
+#define TCIFLUSH  0
+#define TCOFLUSH  1
+#define TCIOFLUSH 2
+
+/* Values for the OPTIONAL_ACTIONS argument to `tcsetattr'.  */
+#define TCSANOW   0
+#define TCSADRAIN 1
+#define TCSAFLUSH 2
+
+/* c_cc characters */
+#define VEOF     0
+#define VEOL     1
+#define VEOL2    2
+#define VERASE   3
+#define VWERASE  4
+#define VKILL    5
+#define VREPRINT 6
+#define VSWTC    7
+#define VINTR    8
+#define VQUIT    9
+#define VSUSP    10
+#define VSTART   12
+#define VSTOP    13
+#define VLNEXT   14
+#define VDISCARD 15
+#define VMIN     16
+#define VTIME    17
+
+/* c_iflag bits */
+#define IGNBRK  0000001
+#define BRKINT  0000002
+#define IGNPAR  0000004
+#define PARMRK  0000010
+#define INPCK   0000020
+#define ISTRIP  0000040
+#define INLCR   0000100
+#define IGNCR   0000200
+#define ICRNL   0000400
+#define IXON    0001000
+#define IXOFF   0002000
+#define IXANY   0004000
+#define IUCLC   0010000
+#define IMAXBEL 0020000
+#define IUTF8   0040000
+
+/* c_oflag bits */
+#define OPOST 0000001
+#define ONLCR 0000002
+#define OLCUC 0000004
+
+#define OCRNL  0000010
+#define ONOCR  0000020
+#define ONLRET 0000040
+
+#define OFILL  00000100
+#define OFDEL  00000200
+#define NLDLY  00001400
+#define NL0    00000000
+#define NL1    00000400
+#define NL2    00001000
+#define NL3    00001400
+#define TABDLY 00006000
+#define TAB0   00000000
+#define TAB1   00002000
+#define TAB2   00004000
+#define TAB3   00006000
+#define CRDLY  00030000
+#define CR0    00000000
+#define CR1    00010000
+#define CR2    00020000
+#define CR3    00030000
+#define FFDLY  00040000
+#define FF0    00000000
+#define FF1    00040000
+#define BSDLY  00100000
+#define BS0    00000000
+#define BS1    00100000
+#define VTDLY  00200000
+#define VT0    00000000
+#define VT1    00200000
+/*
+ * Should be equivalent to TAB3, see description of TAB3 in
+ * POSIX.1-2008, Ch. 11.2.3 "Output Modes"
+ */
+#define XTABS TAB3
+
+/* c_cflag bit meaning */
+#define CBAUD    0000037
+#define B0       0000000 /* hang up */
+#define B50      0000001
+#define B75      0000002
+#define B110     0000003
+#define B134     0000004
+#define B150     0000005
+#define B200     0000006
+#define B300     0000007
+#define B600     0000010
+#define B1200    0000011
+#define B1800    0000012
+#define B2400    0000013
+#define B4800    0000014
+#define B9600    0000015
+#define B19200   0000016
+#define B38400   0000017
+#define EXTA     B19200
+#define EXTB     B38400
+#define CBAUDEX  0000000
+#define B57600   00020
+#define B115200  00021
+#define B230400  00022
+#define B460800  00023
+#define B500000  00024
+#define B576000  00025
+#define B921600  00026
+#define B1000000 00027
+#define B1152000 00030
+#define B1500000 00031
+#define B2000000 00032
+#define B2500000 00033
+#define B3000000 00034
+#define B3500000 00035
+#define B4000000 00036
+#define BOTHER   00037
+
+#define CSIZE 00001400
+#define CS5   00000000
+#define CS6   00000400
+#define CS7   00001000
+#define CS8   00001400
+
+#define CSTOPB 00002000
+#define CREAD  00004000
+#define PARENB 00010000
+#define PARODD 00020000
+#define HUPCL  00040000
+
+#define CLOCAL  00100000
+#define CMSPAR  010000000000 /* mark or space (stick) parity */
+#define CRTSCTS 020000000000 /* flow control */
+
+#define CIBAUD  07600000
+#define IBSHIFT 16
+
+/* c_lflag bits */
+#define ISIG    0x00000080
+#define ICANON  0x00000100
+#define XCASE   0x00004000
+#define ECHO    0x00000008
+#define ECHOE   0x00000002
+#define ECHOK   0x00000004
+#define ECHONL  0x00000010
+#define NOFLSH  0x80000000
+#define TOSTOP  0x00400000
+#define ECHOCTL 0x00000040
+#define ECHOPRT 0x00000020
+#define ECHOKE  0x00000001
+#define FLUSHO  0x00800000
+#define PENDIN  0x20000000
+#define IEXTEN  0x00000400
+#define EXTPROC 0x10000000
+
+/* Values for the ACTION argument to `tcflow'.  */
+#define TCOOFF 0
+#define TCOON  1
+#define TCIOFF 2
+#define TCION  3
+
+/* Values for the QUEUE_SELECTOR argument to `tcflush'.  */
+#define TCIFLUSH  0
+#define TCOFLUSH  1
+#define TCIOFLUSH 2
+
+/* Values for the OPTIONAL_ACTIONS argument to `tcsetattr'.  */
+#define TCSANOW   0
+#define TCSADRAIN 1
+#define TCSAFLUSH 2
+
+/* c_cc characters */
+#define VEOF     0
+#define VEOL     1
+#define VEOL2    2
+#define VERASE   3
+#define VWERASE  4
+#define VKILL    5
+#define VREPRINT 6
+#define VSWTC    7
+#define VINTR    8
+#define VQUIT    9
+#define VSUSP    10
+#define VSTART   12
+#define VSTOP    13
+#define VLNEXT   14
+#define VDISCARD 15
+#define VMIN     16
+#define VTIME    17
+
+/* c_iflag bits */
+#define IGNBRK  0000001
+#define BRKINT  0000002
+#define IGNPAR  0000004
+#define PARMRK  0000010
+#define INPCK   0000020
+#define ISTRIP  0000040
+#define INLCR   0000100
+#define IGNCR   0000200
+#define ICRNL   0000400
+#define IXON    0001000
+#define IXOFF   0002000
+#define IXANY   0004000
+#define IUCLC   0010000
+#define IMAXBEL 0020000
+#define IUTF8   0040000
+
+/* c_oflag bits */
+#define OPOST 0000001
+#define ONLCR 0000002
+#define OLCUC 0000004
+
+#define OCRNL  0000010
+#define ONOCR  0000020
+#define ONLRET 0000040
+
+#define OFILL  00000100
+#define OFDEL  00000200
+#define NLDLY  00001400
+#define NL0    00000000
+#define NL1    00000400
+#define NL2    00001000
+#define NL3    00001400
+#define TABDLY 00006000
+#define TAB0   00000000
+#define TAB1   00002000
+#define TAB2   00004000
+#define TAB3   00006000
+#define CRDLY  00030000
+#define CR0    00000000
+#define CR1    00010000
+#define CR2    00020000
+#define CR3    00030000
+#define FFDLY  00040000
+#define FF0    00000000
+#define FF1    00040000
+#define BSDLY  00100000
+#define BS0    00000000
+#define BS1    00100000
+#define VTDLY  00200000
+#define VT0    00000000
+#define VT1    00200000
+/*
+ * Should be equivalent to TAB3, see description of TAB3 in
+ * POSIX.1-2008, Ch. 11.2.3 "Output Modes"
+ */
+#define XTABS TAB3
+
+/* c_cflag bit meaning */
+#define CBAUD    0000037
+#define B0       0000000 /* hang up */
+#define B50      0000001
+#define B75      0000002
+#define B110     0000003
+#define B134     0000004
+#define B150     0000005
+#define B200     0000006
+#define B300     0000007
+#define B600     0000010
+#define B1200    0000011
+#define B1800    0000012
+#define B2400    0000013
+#define B4800    0000014
+#define B9600    0000015
+#define B19200   0000016
+#define B38400   0000017
+#define EXTA     B19200
+#define EXTB     B38400
+#define CBAUDEX  0000000
+#define B57600   00020
+#define B115200  00021
+#define B230400  00022
+#define B460800  00023
+#define B500000  00024
+#define B576000  00025
+#define B921600  00026
+#define B1000000 00027
+#define B1152000 00030
+#define B1500000 00031
+#define B2000000 00032
+#define B2500000 00033
+#define B3000000 00034
+#define B3500000 00035
+#define B4000000 00036
+#define BOTHER   00037
+
+#define CSIZE 00001400
+#define CS5   00000000
+#define CS6   00000400
+#define CS7   00001000
+#define CS8   00001400
+
+#define CSTOPB 00002000
+#define CREAD  00004000
+#define PARENB 00010000
+#define PARODD 00020000
+#define HUPCL  00040000
+
+#define CLOCAL  00100000
+#define CMSPAR  010000000000 /* mark or space (stick) parity */
+#define CRTSCTS 020000000000 /* flow control */
+
+#define CIBAUD  07600000
+#define IBSHIFT 16
+
+/* c_lflag bits */
+#define ISIG    0x00000080
+#define ICANON  0x00000100
+#define XCASE   0x00004000
+#define ECHO    0x00000008
+#define ECHOE   0x00000002
+#define ECHOK   0x00000004
+#define ECHONL  0x00000010
+#define NOFLSH  0x80000000
+#define TOSTOP  0x00400000
+#define ECHOCTL 0x00000040
+#define ECHOPRT 0x00000020
+#define ECHOKE  0x00000001
+#define FLUSHO  0x00800000
+#define PENDIN  0x20000000
+#define IEXTEN  0x00000400
+#define EXTPROC 0x10000000
+
+/* Values for the ACTION argument to `tcflow'.  */
+#define TCOOFF 0
+#define TCOON  1
+#define TCIOFF 2
+#define TCION  3
+
+/* Values for the QUEUE_SELECTOR argument to `tcflush'.  */
+#define TCIFLUSH  0
+#define TCOFLUSH  1
+#define TCIOFLUSH 2
+
+/* Values for the OPTIONAL_ACTIONS argument to `tcsetattr'.  */
+#define TCSANOW   0
+#define TCSADRAIN 1
+#define TCSAFLUSH 2
+
+#define __packed
+
+#undef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+#define clamp(val, lo, hi) min(max(val, lo), hi)
+
+#define BITS_PER_LONG 32
+
+#define GENMASK(h, l) \
+    (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
+
+#define DIV_ROUND_CLOSEST(x, d) (((x) + ((d) / 2)) / (d))
+
+#define true           1
+#define false          0
+
+static uint16_t swab16(uint16_t v)
+{
+    return ((v & 0xff) << 8) | ((v & 0xff00) >> 8);
+}
+
+int usb_rcvctrlpipe(struct usb_serial_port *port, int useless)
+{
+    return port->ctrlpipe_rx;
+}
+int usb_sndctrlpipe(struct usb_serial_port *port, int useless)
+{
+    return port->ctrlpipe_tx;
+}
+
+void cp210x_get_termios(struct tty_struct *tty);
+void cp210x_change_speed(struct tty_struct *tty);
+
+void cp210x_get_termios(struct tty_struct *tty);
+void cp210x_get_termios_port(struct usb_serial_port *port, tcflag_t *cflagp, unsigned int *baudp);
+
+int usb_control_msg(struct usb_serial_port *port, int pipe, int req, u8 type, u16 val, u8 interface_num, void *dmabuf, int bufsize, int flags);
+
+/* Config request types */
+#define REQTYPE_HOST_TO_INTERFACE 0x41
+#define REQTYPE_INTERFACE_TO_HOST 0xc1
+#define REQTYPE_HOST_TO_DEVICE    0x40
+#define REQTYPE_DEVICE_TO_HOST    0xc0
+
+/* Config request codes */
+#define CP210X_IFC_ENABLE      0x00
+#define CP210X_SET_BAUDDIV     0x01
+#define CP210X_GET_BAUDDIV     0x02
+#define CP210X_SET_LINE_CTL    0x03
+#define CP210X_GET_LINE_CTL    0x04
+#define CP210X_SET_BREAK       0x05
+#define CP210X_IMM_CHAR        0x06
+#define CP210X_SET_MHS         0x07
+#define CP210X_GET_MDMSTS      0x08
+#define CP210X_SET_XON         0x09
+#define CP210X_SET_XOFF        0x0A
+#define CP210X_SET_EVENTMASK   0x0B
+#define CP210X_GET_EVENTMASK   0x0C
+#define CP210X_SET_CHAR        0x0D
+#define CP210X_GET_CHARS       0x0E
+#define CP210X_GET_PROPS       0x0F
+#define CP210X_GET_COMM_STATUS 0x10
+#define CP210X_RESET           0x11
+#define CP210X_PURGE           0x12
+#define CP210X_SET_FLOW        0x13
+#define CP210X_GET_FLOW        0x14
+#define CP210X_EMBED_EVENTS    0x15
+#define CP210X_GET_EVENTSTATE  0x16
+#define CP210X_SET_CHARS       0x19
+#define CP210X_GET_BAUDRATE    0x1D
+#define CP210X_SET_BAUDRATE    0x1E
+#define CP210X_VENDOR_SPECIFIC 0xFF
+
+/* CP210X_IFC_ENABLE */
+#define UART_ENABLE  0x0001
+#define UART_DISABLE 0x0000
+
+/* CP210X_(SET|GET)_BAUDDIV */
+#define BAUD_RATE_GEN_FREQ 0x384000
+
+/* CP210X_(SET|GET)_LINE_CTL */
+#define BITS_DATA_MASK 0X0f00
+#define BITS_DATA_5    0X0500
+#define BITS_DATA_6    0X0600
+#define BITS_DATA_7    0X0700
+#define BITS_DATA_8    0X0800
+#define BITS_DATA_9    0X0900
+
+#define BITS_PARITY_MASK  0x00f0
+#define BITS_PARITY_NONE  0x0000
+#define BITS_PARITY_ODD   0x0010
+#define BITS_PARITY_EVEN  0x0020
+#define BITS_PARITY_MARK  0x0030
+#define BITS_PARITY_SPACE 0x0040
+
+#define BITS_STOP_MASK 0x000f
+#define BITS_STOP_1    0x0000
+#define BITS_STOP_1_5  0x0001
+#define BITS_STOP_2    0x0002
+
+/* CP210X_SET_BREAK */
+#define BREAK_ON  0x0001
+#define BREAK_OFF 0x0000
+
+/* CP210X_(SET_MHS|GET_MDMSTS) */
+#define CONTROL_DTR       0x0001
+#define CONTROL_RTS       0x0002
+#define CONTROL_CTS       0x0010
+#define CONTROL_DSR       0x0020
+#define CONTROL_RING      0x0040
+#define CONTROL_DCD       0x0080
+#define CONTROL_WRITE_DTR 0x0100
+#define CONTROL_WRITE_RTS 0x0200
+
+/* CP210X_VENDOR_SPECIFIC values */
+#define CP210X_READ_2NCONFIG  0x000E
+#define CP210X_READ_LATCH     0x00C2
+#define CP210X_GET_PARTNUM    0x370B
+#define CP210X_GET_PORTCONFIG 0x370C
+#define CP210X_GET_DEVICEMODE 0x3711
+#define CP210X_WRITE_LATCH    0x37E1
+
+/* Part number definitions */
+#define CP210X_PARTNUM_CP2101        0x01
+#define CP210X_PARTNUM_CP2102        0x02
+#define CP210X_PARTNUM_CP2103        0x03
+#define CP210X_PARTNUM_CP2104        0x04
+#define CP210X_PARTNUM_CP2105        0x05
+#define CP210X_PARTNUM_CP2108        0x08
+#define CP210X_PARTNUM_CP2102N_QFN28 0x20
+#define CP210X_PARTNUM_CP2102N_QFN24 0x21
+#define CP210X_PARTNUM_CP2102N_QFN20 0x22
+#define CP210X_PARTNUM_UNKNOWN       0xFF
+
+#define __le32 int32_t
+/* CP210X_GET_COMM_STATUS returns these 0x13 bytes */
+struct cp210x_comm_status {
+    __le32 ulErrors;
+    __le32 ulHoldReasons;
+    __le32 ulAmountInInQueue;
+    __le32 ulAmountInOutQueue;
+    u8 bEofReceived;
+    u8 bWaitForImmediate;
+    u8 bReserved;
+} __packed;
+
+/*
+ * CP210X_PURGE - 16 bits passed in wValue of USB request.
+ * SiLabs app note AN571 gives a strange description of the 4 bits:
+ * bit 0 or bit 2 clears the transmit queue and 1 or 3 receive.
+ * writing 1 to all, however, purges cp2108 well enough to avoid the hang.
+ */
+#define PURGE_ALL 0x000f
+
+/* CP210X_GET_FLOW/CP210X_SET_FLOW read/write these 0x10 bytes */
+struct cp210x_flow_ctl {
+    __le32 ulControlHandshake;
+    __le32 ulFlowReplace;
+    __le32 ulXonLimit;
+    __le32 ulXoffLimit;
+} __packed;
+
+#undef BIT
+#undef BIT_ULL
+#undef BIT_MASK
+#undef BIT_WORD
+#undef BIT_ULL_MASK
+#undef BITS_PER_BYTE
+#define BIT(nr)          (1UL << (nr))
+#define BIT_ULL(nr)      (1ULL << (nr))
+#define BIT_MASK(nr)     (1UL << ((nr) % BITS_PER_LONG))
+#define BIT_WORD(nr)     ((nr) / BITS_PER_LONG)
+#define BIT_ULL_MASK(nr) (1ULL << ((nr) % BITS_PER_LONG_LONG))
+#define BIT_ULL_WORD(nr) ((nr) / BITS_PER_LONG_LONG)
+#define BITS_PER_BYTE    8
+
+/* cp210x_flow_ctl::ulControlHandshake */
+#define CP210X_SERIAL_DTR_MASK         GENMASK(1, 0)
+#define CP210X_SERIAL_DTR_SHIFT(_mode) (_mode)
+#define CP210X_SERIAL_CTS_HANDSHAKE    BIT(3)
+#define CP210X_SERIAL_DSR_HANDSHAKE    BIT(4)
+#define CP210X_SERIAL_DCD_HANDSHAKE    BIT(5)
+#define CP210X_SERIAL_DSR_SENSITIVITY  BIT(6)
+
+/* values for cp210x_flow_ctl::ulControlHandshake::CP210X_SERIAL_DTR_MASK */
+#define CP210X_SERIAL_DTR_INACTIVE 0
+#define CP210X_SERIAL_DTR_ACTIVE   1
+#define CP210X_SERIAL_DTR_FLOW_CTL 2
+
+/* cp210x_flow_ctl::ulFlowReplace */
+#define CP210X_SERIAL_AUTO_TRANSMIT    BIT(0)
+#define CP210X_SERIAL_AUTO_RECEIVE     BIT(1)
+#define CP210X_SERIAL_ERROR_CHAR       BIT(2)
+#define CP210X_SERIAL_NULL_STRIPPING   BIT(3)
+#define CP210X_SERIAL_BREAK_CHAR       BIT(4)
+#define CP210X_SERIAL_RTS_MASK         GENMASK(7, 6)
+#define CP210X_SERIAL_RTS_SHIFT(_mode) (_mode << 6)
+#define CP210X_SERIAL_XOFF_CONTINUE    BIT(31)
+
+/* values for cp210x_flow_ctl::ulFlowReplace::CP210X_SERIAL_RTS_MASK */
+#define CP210X_SERIAL_RTS_INACTIVE 0
+#define CP210X_SERIAL_RTS_ACTIVE   1
+#define CP210X_SERIAL_RTS_FLOW_CTL 2
+
+/* CP210X_VENDOR_SPECIFIC, CP210X_GET_DEVICEMODE call reads these 0x2 bytes. */
+struct cp210x_pin_mode {
+    u8 eci;
+    u8 sci;
+} __packed;
+
+#define CP210X_PIN_MODE_MODEM 0
+#define CP210X_PIN_MODE_GPIO  BIT(0)
+
+/*
+ * CP210X_VENDOR_SPECIFIC, CP210X_GET_PORTCONFIG call reads these 0xf bytes.
+ * Structure needs padding due to unused/unspecified bytes.
+ */
+#define __le16 int16_t
+
+struct cp210x_config {
+    __le16 gpio_mode;
+    u8 __pad0[2];
+    __le16 reset_state;
+    u8 __pad1[4];
+    __le16 suspend_state;
+    u8 sci_cfg;
+    u8 eci_cfg;
+    u8 device_cfg;
+} __packed;
+
+/* GPIO modes */
+#define CP210X_SCI_GPIO_MODE_OFFSET 9
+#define CP210X_SCI_GPIO_MODE_MASK   GENMASK(11, 9)
+
+#define CP210X_ECI_GPIO_MODE_OFFSET 2
+#define CP210X_ECI_GPIO_MODE_MASK   GENMASK(3, 2)
+
+/* CP2105 port configuration values */
+#define CP2105_GPIO0_TXLED_MODE BIT(0)
+#define CP2105_GPIO1_RXLED_MODE BIT(1)
+#define CP2105_GPIO1_RS485_MODE BIT(2)
+
+/* CP2102N configuration array indices */
+#define CP210X_2NCONFIG_CONFIG_VERSION_IDX 2
+#define CP210X_2NCONFIG_GPIO_MODE_IDX      581
+#define CP210X_2NCONFIG_GPIO_RSTLATCH_IDX  587
+#define CP210X_2NCONFIG_GPIO_CONTROL_IDX   600
+
+/* CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these 0x2 bytes. */
+struct cp210x_gpio_write {
+    u8 mask;
+    u8 state;
+} __packed;
+
+/*
+ * Helper to get interface number when we only have struct usb_serial.
+ */
+static u8 cp210x_interface_num(struct usb_serial_port *port)
+{
+    return port->bInterfaceNumber;
+}
+
+/*
+ * Reads a variable-sized block of CP210X_ registers, identified by req.
+ * Returns data into buf in native USB byte order.
+ */
+static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req,
+                                 void *buf, int bufsize)
+{
+    int result;
+
+    result = usb_control_msg(port, usb_rcvctrlpipe(port, 0),
+                             req, REQTYPE_INTERFACE_TO_HOST, 0,
+                             port->bInterfaceNumber, buf, bufsize,
+                             USB_CTRL_SET_TIMEOUT);
+    if (result == bufsize) {
+        result = 0;
+    } else {
+        dev_err("failed get req 0x%x size %d status: %d\n",
+                (uint8_t)req, bufsize, result);
+        if (result >= 0)
+            result = -EIO;
+
+        /*
+		 * FIXME Some callers don't bother to check for error,
+		 * at least give them consistent junk until they are fixed
+		 */
+        memset(buf, 0, bufsize);
+    }
+
+    return result;
+}
+
+/*
+ * Reads any 32-bit CP210X_ register identified by req.
+ */
+static int cp210x_read_u32_reg(struct usb_serial_port *port, u8 req, u32 *val)
+{
+    __le32 le32_val;
+    int err;
+
+    err = cp210x_read_reg_block(port, req, &le32_val, sizeof(le32_val));
+    if (err) {
+        /*
+		 * FIXME Some callers don't bother to check for error,
+		 * at least give them consistent junk until they are fixed
+		 */
+        *val = 0;
+        return err;
+    }
+
+    *val = le32_to_cpu(le32_val);
+
+    return 0;
+}
+
+/*
+ * Reads any 16-bit CP210X_ register identified by req.
+ */
+static int cp210x_read_u16_reg(struct usb_serial_port *port, u8 req, u16 *val)
+{
+    __le16 le16_val;
+    int err;
+
+    err = cp210x_read_reg_block(port, req, &le16_val, sizeof(le16_val));
+    if (err)
+        return err;
+
+    *val = le16_to_cpu(le16_val);
+
+    return 0;
+}
+
+/*
+ * Reads any 8-bit CP210X_ register identified by req.
+ */
+static int cp210x_read_u8_reg(struct usb_serial_port *port, u8 req, u8 *val)
+{
+    return cp210x_read_reg_block(port, req, val, sizeof(*val));
+}
+
+/*
+ * Reads a variable-sized vendor block of CP210X_ registers, identified by val.
+ * Returns data into buf in native USB byte order.
+ */
+static int cp210x_read_vendor_block(struct usb_serial_port *port, u8 type, u16 val,
+                                    void *buf, int bufsize)
+{
+    int result;
+
+    result = usb_control_msg(port, usb_rcvctrlpipe(port, 0),
+                             CP210X_VENDOR_SPECIFIC, type, val,
+                             cp210x_interface_num(port), buf, bufsize,
+                             USB_CTRL_GET_TIMEOUT);
+    if (result == bufsize) {
+        return 0;
+    } else {
+        if (result >= 0)
+            return -1;
+    }
+
+    return -2;
+}
+
+/*
+ * Writes any 16-bit CP210X_ register (req) whose value is passed
+ * entirely in the wValue field of the USB request.
+ */
+static int cp210x_write_u16_reg(struct usb_serial_port *port, u8 req, u16 val)
+{
+    int result;
+
+    result = usb_control_msg(port, usb_sndctrlpipe(port, 0),
+                             req, REQTYPE_HOST_TO_INTERFACE, val,
+                             port->bInterfaceNumber, NULL, 0,
+                             USB_CTRL_SET_TIMEOUT);
+    if (result < 0) {
+        USB_LOG_ERR("failed set request 0x%x status: %d\n",
+                    req, result);
+    }
+
+    return result;
+}
+
+/*
+ * Writes a variable-sized block of CP210X_ registers, identified by req.
+ * Data in buf must be in native USB byte order.
+ */
+static int cp210x_write_reg_block(struct usb_serial_port *port, u8 req,
+                                  void *buf, int bufsize)
+{
+    int result;
+
+    result = usb_control_msg(port, usb_sndctrlpipe(port, 0),
+                             req, REQTYPE_HOST_TO_INTERFACE, 0,
+                             port->bInterfaceNumber, buf, bufsize,
+                             USB_CTRL_SET_TIMEOUT);
+
+    if (result == bufsize) {
+        result = 0;
+    } else {
+        USB_LOG_ERR("failed set req 0x%x size %d status: %d\n",
+                    req, bufsize, result);
+        if (result >= 0)
+            result = -EIO;
+    }
+
+    return result;
+}
+
+/*
+ * Writes any 32-bit CP210X_ register identified by req.
+ */
+static int cp210x_write_u32_reg(struct usb_serial_port *port, u8 req, u32 val)
+{
+    __le32 le32_val;
+
+    le32_val = cpu_to_le32(val);
+
+    return cp210x_write_reg_block(port, req, &le32_val, sizeof(le32_val));
+}
+
+/*
+ * Detect CP2108 GET_LINE_CTL bug and activate workaround.
+ * Write a known good value 0x800, read it back.
+ * If it comes back swapped the bug is detected.
+ * Preserve the original register value.
+ */
+static int cp210x_detect_swapped_line_ctl(struct usb_serial_port *port)
+{
+    u16 line_ctl_save;
+    u16 line_ctl_test;
+    int err;
+
+    err = cp210x_read_u16_reg(port, CP210X_GET_LINE_CTL, &line_ctl_save);
+    if (err) {
+        USB_LOG_ERR("Error, read reg GET_LINE_CTL\n");
+        return err;
+    }
+
+    err = cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, 0x800);
+    if (err) {
+        USB_LOG_ERR("Error, write reg SET_LINE_CTL\n");
+        return err;
+    }
+
+    err = cp210x_read_u16_reg(port, CP210X_GET_LINE_CTL, &line_ctl_test);
+    if (err) {
+        USB_LOG_ERR("Error, read reg GET_LINE_CTL\n");
+        return err;
+    }
+
+    if (line_ctl_test == 8) {
+        port->has_swapped_line_ctl = true;
+        line_ctl_save = swab16(line_ctl_save);
+    }
+
+    return cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, line_ctl_save);
+}
+
+/*
+ * Must always be called instead of cp210x_read_u16_reg(CP210X_GET_LINE_CTL)
+ * to workaround cp2108 bug and get correct value.
+ */
+static int cp210x_get_line_ctl(struct usb_serial_port *port, u16 *ctl)
+{
+    int err;
+
+    err = cp210x_read_u16_reg(port, CP210X_GET_LINE_CTL, ctl);
+    if (err)
+        return err;
+
+    /* Workaround swapped bytes in 16-bit value from CP210X_GET_LINE_CTL */
+    if (port->has_swapped_line_ctl)
+        *ctl = swab16(*ctl);
+
+    return 0;
+}
+
+int cp210x_open(struct tty_struct *tty)
+{
+    int result;
+    struct usb_serial_port *port = &tty->driver_data;
+
+    result = cp210x_write_u16_reg(port, CP210X_IFC_ENABLE, UART_ENABLE);
+    if (result) {
+        USB_LOG_ERR("%s - Unable to enable UART\n", __func__);
+        return result;
+    }
+
+    /* Configure the termios structure */
+    cp210x_get_termios(tty);
+
+    /* The baud rate must be initialised on cp2104 */
+    if (tty)
+        cp210x_change_speed(tty);
+
+    return 0;
+}
+
+void cp210x_close(struct usb_serial_port *port)
+{
+    /* Clear both queues; cp2108 needs this to avoid an occasional hang */
+    cp210x_write_u16_reg(port, CP210X_PURGE, PURGE_ALL);
+
+    cp210x_write_u16_reg(port, CP210X_IFC_ENABLE, UART_DISABLE);
+}
+
+/*
+ * Read how many bytes are waiting in the TX queue.
+ */
+static int cp210x_get_tx_queue_byte_count(struct usb_serial_port *port,
+                                          u32 *count)
+{
+    struct cp210x_comm_status sts;
+    int result;
+
+    result = usb_control_msg(port, usb_rcvctrlpipe(port, 0),
+                             CP210X_GET_COMM_STATUS, REQTYPE_INTERFACE_TO_HOST,
+                             0, port->bInterfaceNumber, &sts, sizeof(sts),
+                             USB_CTRL_GET_TIMEOUT);
+    if (result == sizeof(sts)) {
+        *count = le32_to_cpu(sts.ulAmountInOutQueue);
+        result = 0;
+    } else {
+        dev_err("failed to get comm status: %d\n", result);
+        if (result >= 0)
+            result = -EIO;
+    }
+
+    return result;
+}
+
+bool cp210x_tx_empty(struct usb_serial_port *port)
+{
+    int err;
+    u32 count;
+
+    err = cp210x_get_tx_queue_byte_count(port, &count);
+    if (err)
+        return true;
+
+    return !count;
+}
+
+/*
+ * cp210x_get_termios
+ * Reads the baud rate, data bits, parity, stop bits and flow control mode
+ * from the device, corrects any unsupported values, and configures the
+ * termios structure to reflect the state of the device
+ */
+void cp210x_get_termios(struct tty_struct *tty)
+{
+    unsigned int baud;
+    struct usb_serial_port *port = &tty->driver_data;
+
+    if (tty) {
+        cp210x_get_termios_port(&tty->driver_data,
+                                &tty->termios.c_cflag, &baud);
+    } else {
+        tcflag_t cflag;
+        cflag = 0;
+        cp210x_get_termios_port(port, &cflag, &baud);
+    }
+}
+
+/*
+ * cp210x_get_termios_port
+ * This is the heart of cp210x_get_termios which always uses a &usb_serial_port.
+ */
+void cp210x_get_termios_port(struct usb_serial_port *port, tcflag_t *cflagp, unsigned int *baudp)
+{
+    tcflag_t cflag;
+    struct cp210x_flow_ctl flow_ctl;
+    u32 baud;
+    u16 bits;
+    u32 ctl_hs;
+
+    cp210x_read_u32_reg(port, CP210X_GET_BAUDRATE, &baud);
+
+    dev_dbg("%s - baud rate = %d\n", __func__, baud);
+    *baudp = baud;
+
+    cflag = *cflagp;
+
+    cp210x_get_line_ctl(port, &bits);
+    cflag &= ~CSIZE;
+    switch (bits & BITS_DATA_MASK) {
+        case BITS_DATA_5:
+            dev_dbg("%s - data bits = 5\n", __func__);
+            cflag |= CS5;
+            break;
+        case BITS_DATA_6:
+            dev_dbg("%s - data bits = 6\n", __func__);
+            cflag |= CS6;
+            break;
+        case BITS_DATA_7:
+            dev_dbg("%s - data bits = 7\n", __func__);
+            cflag |= CS7;
+            break;
+        case BITS_DATA_8:
+            dev_dbg("%s - data bits = 8\n", __func__);
+            cflag |= CS8;
+            break;
+        case BITS_DATA_9:
+            dev_dbg("%s - data bits = 9 (not supported, using 8 data bits)\n", __func__);
+            cflag |= CS8;
+            bits &= ~BITS_DATA_MASK;
+            bits |= BITS_DATA_8;
+            cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits);
+            break;
+        default:
+            dev_dbg("%s - Unknown number of data bits, using 8\n", __func__);
+            cflag |= CS8;
+            bits &= ~BITS_DATA_MASK;
+            bits |= BITS_DATA_8;
+            cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits);
+            break;
+    }
+
+    switch (bits & BITS_PARITY_MASK) {
+        case BITS_PARITY_NONE:
+            dev_dbg("%s - parity = NONE\n", __func__);
+            cflag &= ~PARENB;
+            break;
+        case BITS_PARITY_ODD:
+            dev_dbg("%s - parity = ODD\n", __func__);
+            cflag |= (PARENB | PARODD);
+            break;
+        case BITS_PARITY_EVEN:
+            dev_dbg("%s - parity = EVEN\n", __func__);
+            cflag &= ~PARODD;
+            cflag |= PARENB;
+            break;
+        case BITS_PARITY_MARK:
+            dev_dbg("%s - parity = MARK\n", __func__);
+            cflag |= (PARENB | PARODD | CMSPAR);
+            break;
+        case BITS_PARITY_SPACE:
+            dev_dbg("%s - parity = SPACE\n", __func__);
+            cflag &= ~PARODD;
+            cflag |= (PARENB | CMSPAR);
+            break;
+        default:
+            dev_dbg("%s - Unknown parity mode, disabling parity\n", __func__);
+            cflag &= ~PARENB;
+            bits &= ~BITS_PARITY_MASK;
+            cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits);
+            break;
+    }
+
+    cflag &= ~CSTOPB;
+    switch (bits & BITS_STOP_MASK) {
+        case BITS_STOP_1:
+            dev_dbg("%s - stop bits = 1\n", __func__);
+            break;
+        case BITS_STOP_1_5:
+            dev_dbg("%s - stop bits = 1.5 (not supported, using 1 stop bit)\n", __func__);
+            bits &= ~BITS_STOP_MASK;
+            cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits);
+            break;
+        case BITS_STOP_2:
+            dev_dbg("%s - stop bits = 2\n", __func__);
+            cflag |= CSTOPB;
+            break;
+        default:
+            dev_dbg("%s - Unknown number of stop bits, using 1 stop bit\n", __func__);
+            bits &= ~BITS_STOP_MASK;
+            cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits);
+            break;
+    }
+
+    cp210x_read_reg_block(port, CP210X_GET_FLOW, &flow_ctl,
+                          sizeof(flow_ctl));
+    ctl_hs = le32_to_cpu(flow_ctl.ulControlHandshake);
+    if (ctl_hs & CP210X_SERIAL_CTS_HANDSHAKE) {
+        dev_dbg("%s - flow control = CRTSCTS\n", __func__);
+        cflag |= CRTSCTS;
+    } else {
+        dev_dbg("%s - flow control = NONE\n", __func__);
+        cflag &= ~CRTSCTS;
+    }
+
+    *cflagp = cflag;
+}
+
+struct cp210x_rate {
+    speed_t rate;
+    speed_t high;
+};
+
+static const struct cp210x_rate cp210x_an205_table1[] = {
+    { 300, 300 },
+    { 600, 600 },
+    { 1200, 1200 },
+    { 1800, 1800 },
+    { 2400, 2400 },
+    { 4000, 4000 },
+    { 4800, 4803 },
+    { 7200, 7207 },
+    { 9600, 9612 },
+    { 14400, 14428 },
+    { 16000, 16062 },
+    { 19200, 19250 },
+    { 28800, 28912 },
+    { 38400, 38601 },
+    { 51200, 51558 },
+    { 56000, 56280 },
+    { 57600, 58053 },
+    { 64000, 64111 },
+    { 76800, 77608 },
+    { 115200, 117028 },
+    { 128000, 129347 },
+    { 153600, 156868 },
+    { 230400, 237832 },
+    { 250000, 254234 },
+    { 256000, 273066 },
+    { 460800, 491520 },
+    { 500000, 567138 },
+    { 576000, 670254 },
+    { 921600, UINT_MAX }
+};
+
+/*
+ * Quantises the baud rate as per AN205 Table 1
+ */
+static speed_t cp210x_get_an205_rate(speed_t baud)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(cp210x_an205_table1); ++i) {
+        if (baud <= cp210x_an205_table1[i].high)
+            break;
+    }
+
+    return cp210x_an205_table1[i].rate;
+}
+
+static speed_t cp210x_get_actual_rate(struct usb_serial_port *port, speed_t baud)
+{
+    unsigned int prescale = 1;
+    unsigned int div;
+
+    baud = clamp(baud, 300u, port->max_speed);
+
+    if (baud <= 365)
+        prescale = 4;
+
+    div = DIV_ROUND_CLOSEST(48000000, 2 * prescale * baud);
+    baud = 48000000 / (2 * prescale * div);
+
+    return baud;
+}
+
+/*
+ * CP2101 supports the following baud rates:
+ *
+ *	300, 600, 1200, 1800, 2400, 4800, 7200, 9600, 14400, 19200, 28800,
+ *	38400, 56000, 57600, 115200, 128000, 230400, 460800, 921600
+ *
+ * CP2102 and CP2103 support the following additional rates:
+ *
+ *	4000, 16000, 51200, 64000, 76800, 153600, 250000, 256000, 500000,
+ *	576000
+ *
+ * The device will map a requested rate to a supported one, but the result
+ * of requests for rates greater than 1053257 is undefined (see AN205).
+ *
+ * CP2104, CP2105 and CP2110 support most rates up to 2M, 921k and 1M baud,
+ * respectively, with an error less than 1%. The actual rates are determined
+ * by
+ *
+ *	div = round(freq / (2 x prescale x request))
+ *	actual = freq / (2 x prescale x div)
+ *
+ * For CP2104 and CP2105 freq is 48Mhz and prescale is 4 for request <= 365bps
+ * or 1 otherwise.
+ * For CP2110 freq is 24Mhz and prescale is 4 for request <= 300bps or 1
+ * otherwise.
+ */
+void cp210x_change_speed(struct tty_struct *tty)
+{
+    struct usb_serial_port *port = &tty->driver_data;
+    u32 baud;
+
+    baud = tty->termios.c_ospeed;
+
+    /*
+	 * This maps the requested rate to the actual rate, a valid rate on
+	 * cp2102 or cp2103, or to an arbitrary rate in [1M, max_speed].
+	 *
+	 * NOTE: B0 is not implemented.
+	 */
+    if (port->use_actual_rate)
+        baud = cp210x_get_actual_rate(port, baud);
+    else if (baud < 1000000)
+        baud = cp210x_get_an205_rate(baud);
+    else if (baud > port->max_speed)
+        baud = port->max_speed;
+
+    dev_dbg("%s - setting baud rate to %u\n", __func__, baud);
+    if (cp210x_write_u32_reg(port, CP210X_SET_BAUDRATE, baud)) {
+        dev_warn("failed to set baud rate to %u\n", baud);
+        baud = 9600;
+    }
+}
+
+void cp210x_set_termios(struct tty_struct *tty)
+{
+    unsigned int cflag;
+    u16 bits;
+    struct usb_serial_port *port = &tty->driver_data;
+
+    cflag = tty->termios.c_cflag;
+
+    cp210x_change_speed(tty);
+
+    /* If the number of data bits is to be updated */
+    cp210x_get_line_ctl(port, &bits);
+    bits &= ~BITS_DATA_MASK;
+    switch (cflag & CSIZE) {
+        case CS5:
+            bits |= BITS_DATA_5;
+            dev_dbg("%s - data bits = 5\n", __func__);
+            break;
+        case CS6:
+            bits |= BITS_DATA_6;
+            dev_dbg("%s - data bits = 6\n", __func__);
+            break;
+        case CS7:
+            bits |= BITS_DATA_7;
+            dev_dbg("%s - data bits = 7\n", __func__);
+            break;
+        case CS8:
+        default:
+            bits |= BITS_DATA_8;
+            dev_dbg("%s - data bits = 8\n", __func__);
+            break;
+    }
+    if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits))
+        dev_dbg("Number of data bits requested not supported by device%s\n", "");
+
+    cp210x_get_line_ctl(port, &bits);
+    bits &= ~BITS_PARITY_MASK;
+    if (cflag & PARENB) {
+        if (cflag & CMSPAR) {
+            if (cflag & PARODD) {
+                bits |= BITS_PARITY_MARK;
+                dev_dbg("%s - parity = MARK\n", __func__);
+            } else {
+                bits |= BITS_PARITY_SPACE;
+                dev_dbg(, "%s - parity = SPACE\n", __func__);
+            }
+        } else {
+            if (cflag & PARODD) {
+                bits |= BITS_PARITY_ODD;
+                dev_dbg("%s - parity = ODD\n", __func__);
+            } else {
+                bits |= BITS_PARITY_EVEN;
+                dev_dbg("%s - parity = EVEN\n", __func__);
+            }
+        }
+    }
+    if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits))
+        dev_dbg("Parity mode not supported by device%s\n", "");
+
+    cp210x_get_line_ctl(port, &bits);
+    bits &= ~BITS_STOP_MASK;
+    if (cflag & CSTOPB) {
+        bits |= BITS_STOP_2;
+        dev_dbg("%s - stop bits = 2\n", __func__);
+    } else {
+        bits |= BITS_STOP_1;
+        dev_dbg("%s - stop bits = 1\n", __func__);
+    }
+    if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits))
+        dev_dbg("Number of stop bits requested not supported by device%s\n", "");
+
+    {
+        struct cp210x_flow_ctl flow_ctl;
+        u32 ctl_hs;
+        u32 flow_repl;
+
+        cp210x_read_reg_block(port, CP210X_GET_FLOW, &flow_ctl,
+                              sizeof(flow_ctl));
+        ctl_hs = le32_to_cpu(flow_ctl.ulControlHandshake);
+        flow_repl = le32_to_cpu(flow_ctl.ulFlowReplace);
+        dev_dbg("%s - read ulControlHandshake=0x%08x, ulFlowReplace=0x%08x\n",
+                __func__, ctl_hs, flow_repl);
+
+        ctl_hs &= ~CP210X_SERIAL_DSR_HANDSHAKE;
+        ctl_hs &= ~CP210X_SERIAL_DCD_HANDSHAKE;
+        ctl_hs &= ~CP210X_SERIAL_DSR_SENSITIVITY;
+        ctl_hs &= ~CP210X_SERIAL_DTR_MASK;
+        ctl_hs |= CP210X_SERIAL_DTR_SHIFT(CP210X_SERIAL_DTR_ACTIVE);
+        if (cflag & CRTSCTS) {
+            ctl_hs |= CP210X_SERIAL_CTS_HANDSHAKE;
+
+            flow_repl &= ~CP210X_SERIAL_RTS_MASK;
+            flow_repl |= CP210X_SERIAL_RTS_SHIFT(
+                CP210X_SERIAL_RTS_FLOW_CTL);
+            dev_dbg("%s - flow control = CRTSCTS\n", __func__);
+        } else {
+            ctl_hs &= ~CP210X_SERIAL_CTS_HANDSHAKE;
+
+            flow_repl &= ~CP210X_SERIAL_RTS_MASK;
+            flow_repl |= CP210X_SERIAL_RTS_SHIFT(
+                CP210X_SERIAL_RTS_ACTIVE);
+            dev_dbg("%s - flow control = NONE\n", __func__);
+        }
+
+        dev_dbg("%s - write ulControlHandshake=0x%08x, ulFlowReplace=0x%08x\n",
+                __func__, ctl_hs, flow_repl);
+        flow_ctl.ulControlHandshake = cpu_to_le32(ctl_hs);
+        flow_ctl.ulFlowReplace = cpu_to_le32(flow_repl);
+        cp210x_write_reg_block(port, CP210X_SET_FLOW, &flow_ctl,
+                               sizeof(flow_ctl));
+    }
+}
+
+int cp210x_tiocmset_port(struct usb_serial_port *port, unsigned int set, unsigned int clear);
+
+int cp210x_tiocmset(struct tty_struct *tty,
+                    unsigned int set, unsigned int clear)
+{
+    struct usb_serial_port *port = 0;
+    return cp210x_tiocmset_port(port, set, clear);
+}
+
+int cp210x_tiocmset_port(struct usb_serial_port *port, unsigned int set, unsigned int clear)
+{
+    u16 control = 0;
+
+    if (set & TIOCM_RTS) {
+        control |= CONTROL_RTS;
+        control |= CONTROL_WRITE_RTS;
+    }
+    if (set & TIOCM_DTR) {
+        control |= CONTROL_DTR;
+        control |= CONTROL_WRITE_DTR;
+    }
+    if (clear & TIOCM_RTS) {
+        control &= ~CONTROL_RTS;
+        control |= CONTROL_WRITE_RTS;
+    }
+    if (clear & TIOCM_DTR) {
+        control &= ~CONTROL_DTR;
+        control |= CONTROL_WRITE_DTR;
+    }
+
+    return cp210x_write_u16_reg(port, CP210X_SET_MHS, control);
+}
+
+void cp210x_dtr_rts(struct usb_serial_port *p, int on)
+{
+    if (on)
+        cp210x_tiocmset_port(p, TIOCM_DTR | TIOCM_RTS, 0);
+    else
+        cp210x_tiocmset_port(p, 0, TIOCM_DTR | TIOCM_RTS);
+}
+
+int cp210x_tiocmget(struct tty_struct *tty)
+{
+    struct usb_serial_port *port = &tty->driver_data;
+    u8 control;
+    int result;
+
+    result = cp210x_read_u8_reg(port, CP210X_GET_MDMSTS, &control);
+    if (result)
+        return result;
+
+    result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0) | ((control & CONTROL_RTS) ? TIOCM_RTS : 0) | ((control & CONTROL_CTS) ? TIOCM_CTS : 0) | ((control & CONTROL_DSR) ? TIOCM_DSR : 0) | ((control & CONTROL_RING) ? TIOCM_RI : 0) | ((control & CONTROL_DCD) ? TIOCM_CD : 0);
+
+    dev_dbg("%s - control = 0x%.2x\n", __func__, control);
+
+    return result;
+}
+
+void cp210x_break_ctl(struct tty_struct *tty, int break_state)
+{
+    struct usb_serial_port *port = &tty->driver_data;
+    u16 state;
+
+    if (break_state == 0)
+        state = BREAK_OFF;
+    else
+        state = BREAK_ON;
+    dev_dbg("%s - turning break %s\n", __func__,
+            state == BREAK_OFF ? "off" : "on");
+    cp210x_write_u16_reg(port, CP210X_SET_BREAK, state);
+}
+
+int cp210x_port_probe(struct usb_serial_port *port)
+{
+    int ret;
+
+    ret = cp210x_detect_swapped_line_ctl(port);
+    if (ret) {
+        return ret;
+    }
+
+    return 0;
+}
+
+void cp210x_init_max_speed(struct usb_serial_port *port)
+{
+    bool use_actual_rate = false;
+    speed_t max;
+
+    switch (port->partnum) {
+        case CP210X_PARTNUM_CP2101:
+            max = 921600;
+            break;
+        case CP210X_PARTNUM_CP2102:
+        case CP210X_PARTNUM_CP2103:
+            //max = 1000000;
+            max = 9600;
+            break;
+        case CP210X_PARTNUM_CP2104:
+            use_actual_rate = true;
+            max = 2000000;
+            break;
+        case CP210X_PARTNUM_CP2108:
+            max = 2000000;
+            break;
+        case CP210X_PARTNUM_CP2105:
+            if (cp210x_interface_num(port) == 0) {
+                use_actual_rate = true;
+                max = 2000000; /* ECI */
+            } else {
+                max = 921600;  /* SCI */
+            }
+            break;
+        case CP210X_PARTNUM_CP2102N_QFN28:
+        case CP210X_PARTNUM_CP2102N_QFN24:
+        case CP210X_PARTNUM_CP2102N_QFN20:
+            use_actual_rate = true;
+            max = 3000000;
+            break;
+        default:
+            max = 2000000;
+            break;
+    }
+
+    max = 9600;
+
+    port->max_speed = max;
+    port->use_actual_rate = use_actual_rate;
+    dev_dbg("max_speed=%d\n", max);
+    dev_dbg("use_actual_rate=%d\n", use_actual_rate);
+}
+
+int cp210x_attach(struct usb_serial_port *port)
+{
+    int result;
+    port->partnum = CP210X_PARTNUM_UNKNOWN;
+
+    result = cp210x_read_vendor_block(port, REQTYPE_DEVICE_TO_HOST,
+                                      CP210X_GET_PARTNUM, &port->partnum,
+                                      sizeof(port->partnum));
+    if (result < 0) {
+        dev_warn(
+            "querying part number failed%s\n", "");
+        port->partnum = CP210X_PARTNUM_UNKNOWN;
+    }
+
+    switch (port->partnum) {
+        case CP210X_PARTNUM_CP2101:
+        case CP210X_PARTNUM_CP2102:
+        case CP210X_PARTNUM_CP2103:
+        case CP210X_PARTNUM_CP2104:
+        case CP210X_PARTNUM_CP2108:
+        case CP210X_PARTNUM_CP2105:
+        case CP210X_PARTNUM_CP2102N_QFN28:
+        case CP210X_PARTNUM_CP2102N_QFN24:
+        case CP210X_PARTNUM_CP2102N_QFN20:
+            cp210x_init_max_speed(port);
+            return 0;
+        default:
+            return -1;
+    }
+}
+
+int usb_control_msg(struct usb_serial_port *port, int pipe, int req, u8 type, u16 val, u8 interface_num, void *dmabuf, int bufsize, int flags)
+{
+    struct usbh_cp210x *p_device = container_of(port, struct usbh_cp210x, drv_data.driver_data);
+    struct usb_setup_packet *setup = p_device->hport->setup;
+
+    setup->bmRequestType = type;
+    setup->bRequest = req;
+    setup->wValue = val;
+    setup->wIndex = interface_num;
+    setup->wLength = bufsize;
+    int len = usbh_control_transfer(p_device->hport->ep0, setup, dmabuf);
+    if (len > 0) {
+        return len - 8;
+    }
+    return len;
+}
+
+static int __usbh_cp210x_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+    struct usb_endpoint_descriptor *ep_desc;
+    struct usbh_cp210x *p_cp210x = usb_malloc(sizeof(struct usbh_cp210x));
+    if (0 == p_cp210x) {
+        return -ENOSYS;
+    }
+    memset(p_cp210x, 0x00, sizeof(*p_cp210x));
+    p_cp210x->hport = hport;
+    p_cp210x->intf = intf;
+    p_cp210x->index = hport->port - 1;
+    hport->config.intf[intf].priv = p_cp210x;
+
+    p_cp210x->drv_data.driver_data.bInterfaceNumber = 1;
+    p_cp210x->drv_data.driver_data.ctrlpipe_rx = 0x80;
+    p_cp210x->drv_data.driver_data.ctrlpipe_tx = 0x00;
+    p_cp210x->drv_data.driver_data.max_speed = 9600;
+    p_cp210x->drv_data.driver_data.use_actual_rate = 1;
+    p_cp210x->drv_data.driver_data.bInterfaceNumber = 1;
+    p_cp210x->drv_data.driver_data.has_swapped_line_ctl = 0;
+
+    USB_LOG_INFO("%s hub %d port %d\n", "---- attach ----", hport->parent->index, hport->port);
+    if (0 != cp210x_attach(&p_cp210x->drv_data.driver_data)) {
+        USB_LOG_INFO("%s\n", "Device NOT supported!");
+        return -EIO;
+    }
+    cp210x_port_probe(&p_cp210x->drv_data.driver_data);
+    cp210x_open(&p_cp210x->drv_data);
+    cp210x_break_ctl(&p_cp210x->drv_data, 0);
+
+    memset(&p_cp210x->drv_data.termios, 0, sizeof(p_cp210x->drv_data.termios));
+    p_cp210x->drv_data.termios.c_iflag = 0;
+    p_cp210x->drv_data.termios.c_oflag = 0;
+    p_cp210x->drv_data.termios.c_cflag = CS8;
+    p_cp210x->drv_data.termios.c_lflag = 0;
+    p_cp210x->drv_data.termios.c_cc[0] = 0;
+    p_cp210x->drv_data.termios.c_ospeed = 9600;
+    cp210x_set_termios(&p_cp210x->drv_data);
+
+    for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+        ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
+        if (ep_desc->bEndpointAddress & 0x80) {
+            usbh_hport_activate_epx(&p_cp210x->bulkin, hport, ep_desc);
+        } else {
+            usbh_hport_activate_epx(&p_cp210x->bulkout, hport, ep_desc);
+        }
+    }
+
+    drv_usbh_cp210x_run(p_cp210x);
+
+    return 0;
+}
+
+__WEAK void drv_usbh_cp210x_run(struct usbh_cp210x *p_device)
+{
+}
+
+__WEAK void drv_usbh_cp210x_stop(struct usbh_cp210x *p_device)
+{
+}
+
+static int __usbh_cp210x_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+    struct usbh_cp210x *p_device = (struct usbh_cp210x *)hport->config.intf[intf].priv;
+    if (p_device) {
+        drv_usbh_cp210x_stop(p_device);
+        if (p_device->bulkin) {
+            usbh_pipe_free(p_device->bulkin);
+        }
+        if (p_device->bulkout) {
+            usbh_pipe_free(p_device->bulkout);
+        }
+        memset(p_device, 0, sizeof(*p_device));
+        usb_free(p_device);
+    }
+    return 0;
+}
+
+static const struct usbh_class_driver cp210x_class_driver = {
+    .driver_name = "cp210x",
+    .connect = __usbh_cp210x_connect,
+    .disconnect = __usbh_cp210x_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info cp210x_class_info = {
+    .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+    .class = 0xff,    // usbh_cp210x_static_device_CLASS_MASS_STORAGE,
+    .subclass = 0x00, //MSC_SUBCLASS_SCSI,
+    .protocol = 0x00, //MSC_PROTOCOL_BULK_ONLY,
+    .vid = 0x00,
+    .pid = 0x00,
+    .class_driver = &cp210x_class_driver
+};

+ 99 - 0
class/vendor/cp201x/usbh_cp210x.h

@@ -0,0 +1,99 @@
+
+#ifndef USBH_CP210X_H
+#define USBH_CP210X_H
+
+#include <stdint.h>
+
+#include "usbh_core.h"
+
+typedef int32_t speed_t;
+typedef int32_t tcflag_t;
+
+#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver"
+
+struct usb_serial_port{
+	uint8_t partnum;
+	uint8_t ctrlpipe_rx;
+	uint8_t ctrlpipe_tx;
+	int32_t max_speed;
+	speed_t use_actual_rate;
+	uint8_t bInterfaceNumber;
+	int     has_swapped_line_ctl;
+};
+
+
+typedef unsigned char cc_t;
+
+#define NCCS 19
+struct termios {
+	tcflag_t c_iflag;		/* input mode flags */
+	tcflag_t c_oflag;		/* output mode flags */
+	tcflag_t c_cflag;		/* control mode flags */
+	tcflag_t c_lflag;		/* local mode flags */
+	cc_t c_cc[NCCS];		/* control characters */
+	cc_t c_line;			/* line discipline (== c_cc[19]) */
+	speed_t c_ispeed;		/* input speed */
+	speed_t c_ospeed;		/* output speed */
+};
+
+/* Alpha has identical termios and termios2 */
+
+struct termios2 {
+	tcflag_t c_iflag;		/* input mode flags */
+	tcflag_t c_oflag;		/* output mode flags */
+	tcflag_t c_cflag;		/* control mode flags */
+	tcflag_t c_lflag;		/* local mode flags */
+	cc_t c_cc[NCCS];		/* control characters */
+	cc_t c_line;			/* line discipline (== c_cc[19]) */
+	speed_t c_ispeed;		/* input speed */
+	speed_t c_ospeed;		/* output speed */
+};
+
+/* Alpha has matching termios and ktermios */
+
+struct ktermios {
+	tcflag_t c_iflag;		/* input mode flags */
+	tcflag_t c_oflag;		/* output mode flags */
+	tcflag_t c_cflag;		/* control mode flags */
+	tcflag_t c_lflag;		/* local mode flags */
+	cc_t c_cc[NCCS];		/* control characters */
+	cc_t c_line;			/* line discipline (== c_cc[19]) */
+	speed_t c_ispeed;		/* input speed */
+	speed_t c_ospeed;		/* output speed */
+};
+
+
+struct tty_struct{
+    struct usb_serial_port driver_data;
+    struct termios termios;
+};
+
+int cp210x_attach(struct usb_serial_port *port);
+int cp210x_port_probe(struct usb_serial_port *port);
+void cp210x_break_ctl(struct tty_struct *tty, int break_state);
+int cp210x_open(struct tty_struct *tty);
+int cp210x_tiocmget(struct tty_struct *tty);
+void cp210x_set_termios(struct tty_struct *tty);
+void cp210x_change_speed(struct tty_struct *tty);
+void cp210x_dtr_rts(struct usb_serial_port *port, int on);
+
+struct usbh_cp210x
+{
+    struct usbh_hubport *hport;
+
+    uint8_t intf;
+    usbh_pipe_t bulkin;
+    usbh_pipe_t bulkout;
+    struct usbh_urb bulkin_urb;
+    struct usbh_urb bulkout_urb;
+
+    struct tty_struct drv_data;
+    int index;
+};
+
+
+/* weak defined function */
+void drv_usbh_cp210x_run(struct usbh_cp210x *p_device);
+void drv_usbh_cp210x_stop(struct usbh_cp210x *p_device);
+
+#endif

+ 299 - 0
third_party/rt-thread-4.1.1/dfs/drv_usbh_cp210x_rtt.c

@@ -0,0 +1,299 @@
+/**
+ * @file usbh_cp210x.c
+ * @author 262666882@qq.com
+ * @brief 从linux驱动移植过来的,支持cp210x芯片。目前只做了简单测试,基本功能可用。
+ * @version 0.1
+ * @date 2023-07-05
+ * 
+ * @copyright Copyright (c) 2022
+ * 
+ */
+
+#include "rtthread.h"
+#include "rtdevice.h"
+
+#include "usbh_cp210x.h"
+
+struct usbh_cp210x_static_device {
+    struct rt_device parent;
+
+    struct usbh_cp210x *p_device;
+    struct rt_mutex lock;
+};
+
+#define CS5 00000000
+#define CS6 00000400
+#define CS7 00001000
+#define CS8 00001400
+
+#define CSTOPB 00002000
+#define PARENB 00010000
+#define PARODD 00020000
+
+#define CMSPAR 010000000000 /* mark or space (stick) parity */
+
+#define DEV_COUNT 4
+static struct usbh_cp210x_static_device g_devices[DEV_COUNT];
+
+void drv_usbh_cp210x_run(struct usbh_cp210x *p_device)
+{
+    struct usbh_cp210x_static_device *p_dev_static = g_devices + p_device->index;
+    rt_mutex_take(&p_dev_static->lock, RT_WAITING_FOREVER);
+    p_dev_static->p_device = p_device;
+    rt_mutex_release(&p_dev_static->lock);
+}
+
+void drv_usbh_cp210x_stop(struct usbh_cp210x *p_device)
+{
+    int index = p_device->index;
+    rt_mutex_take(&g_devices[index].lock, RT_WAITING_FOREVER);
+    g_devices[index].p_device = 0;
+    rt_mutex_release(&g_devices[index].lock);
+}
+
+#if 1 /** FIXME: not tested */
+static void __cp210x_set(struct usbh_cp210x *p_dev, int brate, int bits, int stopb, int parity, int hwctrl)
+{
+    memset(&p_dev->drv_data.termios, 0, sizeof(p_dev->drv_data.termios));
+    p_dev->drv_data.termios.c_iflag = 0;
+    p_dev->drv_data.termios.c_oflag = 0;
+    if (bits == 8)
+        p_dev->drv_data.termios.c_cflag = CS8;
+    else if (bits == 7)
+        p_dev->drv_data.termios.c_cflag = CS7;
+    else if (bits == 6)
+        p_dev->drv_data.termios.c_cflag = CS6;
+    else if (bits == 5)
+        p_dev->drv_data.termios.c_cflag = CS5;
+    else
+        p_dev->drv_data.termios.c_cflag = CS8;
+
+    int c_cflag_p = 0;
+    switch (parity) {
+        case PARITY_NONE:
+            break;
+        case PARITY_EVEN:
+            c_cflag_p = PARENB;
+            break;
+        case PARITY_ODD:
+            c_cflag_p = PARENB | PARODD;
+            break;
+            //case PARITY_SPACE: c_cflag_p=PARENB|CMSPAR; break;
+            //case PARITY_MARK: c_cflag_p=PARENB|CMSPAR|PARODD; break;
+    }
+    int stopbits = 0; /* 1 stopbit default */
+    if (stopb == 2) {
+        stopbits = CSTOPB;
+    }
+    p_dev->drv_data.termios.c_cflag |= c_cflag_p | stopbits;
+
+    p_dev->drv_data.termios.c_lflag = 0;
+    p_dev->drv_data.termios.c_cc[0] = 0;
+    p_dev->drv_data.termios.c_ospeed = brate;
+    cp210x_set_termios(&p_dev->drv_data);
+    cp210x_break_ctl(&p_dev->drv_data, hwctrl);
+}
+#endif
+
+static rt_err_t __init(struct rt_device *dev)
+{
+    rt_err_t result = RT_EOK;
+    /*struct usbh_cp210x_static_device *p_this;*/
+
+    RT_ASSERT(dev != RT_NULL);
+    /*p_this = (struct usbh_cp210x_static_device *)dev;*/
+    return result;
+}
+
+static rt_err_t __open(struct rt_device *dev, rt_uint16_t oflag)
+{
+    rt_uint16_t stream_flag = 0;
+
+    RT_ASSERT(dev != RT_NULL);
+
+    /* keep steam flag */
+    if ((oflag & RT_DEVICE_FLAG_STREAM) || (dev->open_flag & RT_DEVICE_FLAG_STREAM))
+        stream_flag = RT_DEVICE_FLAG_STREAM;
+
+    /* get open flags */
+    dev->open_flag = oflag & 0xff;
+    /* set stream flag */
+    dev->open_flag |= stream_flag;
+    //dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
+
+    return RT_EOK;
+}
+
+static rt_err_t __close(struct rt_device *dev)
+{
+    struct usbh_cp210x_static_device *p_this;
+
+    RT_ASSERT(dev != RT_NULL);
+    p_this = (struct usbh_cp210x_static_device *)dev;
+    (void)p_this;
+
+    /* this device has more reference count */
+    if (dev->ref_count > 1)
+        return RT_EOK;
+
+    dev->flag &= ~RT_DEVICE_FLAG_ACTIVATED;
+
+    return RT_EOK;
+}
+
+static rt_ssize_t __read(struct rt_device *dev,
+                         rt_off_t pos,
+                         void *buffer,
+                         rt_size_t size)
+{
+    struct usbh_cp210x_static_device *p_this;
+    rt_ssize_t ret;
+
+    RT_ASSERT(dev != RT_NULL);
+    if (size == 0)
+        return 0;
+
+    p_this = (struct usbh_cp210x_static_device *)dev;
+
+    rt_mutex_take(&p_this->lock, RT_WAITING_FOREVER);
+    struct usbh_cp210x *p_device = p_this->p_device;
+    if (!p_device) {
+        rt_mutex_release(&p_this->lock);
+        return 0;
+    }
+    struct usbh_urb *urb = &p_device->bulkout_urb;
+    memset(urb, 0, sizeof(struct usbh_urb));
+
+    usbh_bulk_urb_fill(urb, p_device->bulkin, buffer, size, 500, NULL, NULL);
+    ret = usbh_submit_urb(urb);
+    rt_mutex_release(&p_this->lock);
+    return ret;
+}
+
+static rt_ssize_t __write(struct rt_device *dev,
+                          rt_off_t pos,
+                          const void *buffer,
+                          rt_size_t size)
+{
+    struct usbh_cp210x_static_device *p_this;
+    rt_ssize_t ret;
+
+    RT_ASSERT(dev != RT_NULL);
+    if (size == 0)
+        return 0;
+
+    p_this = (struct usbh_cp210x_static_device *)dev;
+
+    rt_mutex_take(&p_this->lock, RT_WAITING_FOREVER);
+    struct usbh_cp210x *p_device = p_this->p_device;
+    if (!p_device) {
+        rt_mutex_release(&p_this->lock);
+        return 0;
+    }
+    struct usbh_urb *urb = &p_device->bulkout_urb;
+    memset(urb, 0, sizeof(struct usbh_urb));
+
+    usbh_bulk_urb_fill(urb, p_device->bulkout, (uint8_t *)buffer, size, 500, NULL, NULL);
+    ret = usbh_submit_urb(urb);
+    rt_mutex_release(&p_this->lock);
+
+    return ret;
+}
+
+static rt_err_t __control(struct rt_device *dev,
+                          int cmd,
+                          void *args)
+{
+    rt_err_t ret = RT_EOK;
+    struct usbh_cp210x_static_device *p_this;
+
+    RT_ASSERT(dev != RT_NULL);
+    p_this = (struct usbh_cp210x_static_device *)dev;
+
+    (void)p_this;
+
+    rt_mutex_take(&p_this->lock, RT_WAITING_FOREVER);
+    struct usbh_cp210x *p_device = p_this->p_device;
+    if (!p_device) {
+        rt_mutex_release(&p_this->lock);
+        return 0;
+    }
+
+    switch (cmd) {
+        case RT_DEVICE_CTRL_SUSPEND:
+            /* suspend device */
+            dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
+            break;
+
+        case RT_DEVICE_CTRL_RESUME:
+            /* resume device */
+            dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
+            break;
+
+        case RT_DEVICE_CTRL_CONFIG:
+/** FIXME: not tested */
+#if 1
+            if (args) {
+                struct serial_configure *pconfig = (struct serial_configure *)args;
+                __cp210x_set(p_device, pconfig->baud_rate, pconfig->data_bits, pconfig->stop_bits, pconfig->parity, pconfig->flowcontrol);
+            }
+#endif
+            break;
+        default:
+            break;
+    }
+
+    rt_mutex_release(&p_this->lock);
+    return ret;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops p_this_ops = {
+    __init,
+    __open,
+    __close,
+    __read,
+    __write,
+    __control
+};
+#endif
+
+static void __device_register(const char *name, int index)
+{
+    rt_uint32_t flag = 0;
+    struct rt_device *device;
+
+    struct usbh_cp210x_static_device *p_dev = g_devices + index;
+
+    memset(p_dev, 0, sizeof(*p_dev));
+
+    device = &(p_dev->parent);
+
+    device->type = RT_Device_Class_Char;
+    device->rx_indicate = RT_NULL;
+    device->tx_complete = RT_NULL;
+
+#ifdef RT_USING_DEVICE_OPS
+    device->ops = &p_this_ops;
+#else
+    device->init = __init;
+    device->open = __open;
+    device->close = __close;
+    device->read = __read;
+    device->write = __write;
+    device->control = __control;
+#endif
+    device->user_data = 0;
+    rt_mutex_init(&p_dev->lock, "USBx", 0);
+
+    /* register a character device */
+    rt_device_register(device, name, flag);
+}
+
+void register_all_ttyusb_devices(void)
+{
+    __device_register("ttyU0", 0);
+    __device_register("ttyU1", 1);
+    __device_register("ttyU2", 2);
+    __device_register("ttyU3", 3);
+}