diff --git a/SConscript b/SConscript index 1b6899bb..71b4cd22 100644 --- a/SConscript +++ b/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') diff --git a/class/vendor/cp201x/usbh_cp210x.c b/class/vendor/cp201x/usbh_cp210x.c new file mode 100644 index 00000000..cbd7c0f2 --- /dev/null +++ b/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 +#include + +#include +#include + +#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 +}; diff --git a/class/vendor/cp201x/usbh_cp210x.h b/class/vendor/cp201x/usbh_cp210x.h new file mode 100644 index 00000000..9b457f39 --- /dev/null +++ b/class/vendor/cp201x/usbh_cp210x.h @@ -0,0 +1,99 @@ + +#ifndef USBH_CP210X_H +#define USBH_CP210X_H + +#include + +#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 diff --git a/third_party/rt-thread-4.1.1/dfs/drv_usbh_cp210x_rtt.c b/third_party/rt-thread-4.1.1/dfs/drv_usbh_cp210x_rtt.c new file mode 100644 index 00000000..2cf5eeca --- /dev/null +++ b/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); +}