X-Git-Url: https://oss.titaniummirror.com/gitweb?a=blobdiff_plain;f=src%2Fcp210x.c;h=bda81d3badb0537f3e5538581653f245d317b4c9;hb=2ae34928adb5da51a66170a52678bb4a7d2d212a;hp=0df92df9a41894e7c49ffe35e8203299007f7d07;hpb=affe5c7c95eab8dd65c10631bb7435acbae8b1c4;p=cp210x.git diff --git a/src/cp210x.c b/src/cp210x.c index 0df92df..bda81d3 100644 --- a/src/cp210x.c +++ b/src/cp210x.c @@ -1,5 +1,5 @@ /* - * Silicon Laboratories CP2101/CP2102/CP2103 USB to RS232 serial adaptor driver + * Silicon Laboratories CP210x USB to RS232 serial adaptor driver * * Copyright (C) 2005 Craig Shelley (craig@microtron.org.uk) * @@ -11,15 +11,6 @@ * thanks to Karl Hiramoto karl@hiramoto.org. RTSCTS hardware flow * control thanks to Munir Nassar nassarmu@real-time.com * - * Silicon Labs RJM: updated to 0.11 for 2.6.15 and later kernels - * - * Port configuration, usb descriptor and gpio management by R. Steve McKown - * rsmckown@gmail.com. - * - * Outstanding Issues: - * Buffers are not flushed when the port is opened. - * Multiple calls to write() may fail with "Resource temporarily unavailable" - * */ #include @@ -30,138 +21,247 @@ #include #include #include -#include +#include #include +#include "cp210x.h" /* * Version Information */ -#define DRIVER_VERSION "v0.11tmi" -#define DRIVER_DESC "Silicon Labs CP2101/CP2102/CP2103 RS232 serial adaptor driver" +#define DRIVER_VERSION "vxxDEBVERxx" +#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver" /* * Function Prototypes */ -static int cp2101_open(struct usb_serial_port*, struct file*); -static void cp2101_cleanup(struct usb_serial_port*); -static void cp2101_close(struct usb_serial_port*, struct file*); -static void cp2101_get_termios(struct usb_serial_port*); -static void cp2101_set_termios(struct usb_serial_port*, struct ktermios*); -static int cp2101_tiocmget (struct usb_serial_port *, struct file *); -static int cp2101_tiocmset (struct usb_serial_port *, struct file *, +static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *); +static void cp210x_close(struct usb_serial_port *); +static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *); +static void cp210x_get_termios_port(struct usb_serial_port *port, + unsigned int *cflagp, unsigned int *baudp); +static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *, + struct ktermios *); +static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *, + struct ktermios*); +static int cp210x_tiocmget(struct tty_struct *); +static int cp210x_tiocmset(struct tty_struct *, unsigned int, unsigned int); +static int cp210x_tiocmset_port(struct usb_serial_port *port, unsigned int, unsigned int); -static void cp2101_break_ctl(struct usb_serial_port*, int); -static int cp2101_startup (struct usb_serial *); -static void cp2101_shutdown(struct usb_serial*); -static int cp2101_ioctl(struct usb_serial_port *, struct file *, - unsigned int, unsigned long); - -static int debug; - -static struct usb_device_id id_table [] = { +static void cp210x_break_ctl(struct tty_struct *, int); +static int cp210x_startup(struct usb_serial *); +static void cp210x_release(struct usb_serial *); +static int cp210x_ioctl(struct tty_struct *, unsigned int, unsigned long); +static void cp210x_dtr_rts(struct usb_serial_port *p, int on); + +static const struct usb_device_id id_table[] = { + { USB_DEVICE(0x045B, 0x0053) }, /* Renesas RX610 RX-Stick */ { USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */ { USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */ + { USB_DEVICE(0x0489, 0xE003) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */ + { USB_DEVICE(0x0745, 0x1000) }, /* CipherLab USB CCD Barcode Scanner 1000 */ + { USB_DEVICE(0x0846, 0x1100) }, /* NetGear Managed Switch M4100 series, M5300 series, M7100 series */ { USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */ + { USB_DEVICE(0x08FD, 0x000A) }, /* Digianswer A/S , ZigBee/802.15.4 MAC Device */ + { USB_DEVICE(0x0BED, 0x1100) }, /* MEI (TM) Cashflow-SC Bill/Voucher Acceptor */ + { USB_DEVICE(0x0BED, 0x1101) }, /* MEI series 2000 Combo Acceptor */ { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */ { USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */ { USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */ + { USB_DEVICE(0x0FDE, 0xCA05) }, /* OWL Wireless Electricity Monitor CM-160 */ { USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */ { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */ { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */ + { USB_DEVICE(0x10C4, 0x0F91) }, /* Vstabi */ + { USB_DEVICE(0x10C4, 0x1101) }, /* Arkham Technology DS101 Bus Monitor */ + { USB_DEVICE(0x10C4, 0x1601) }, /* Arkham Technology DS101 Adapter */ { USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */ { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ + { USB_DEVICE(0x10C4, 0x8044) }, /* Cygnal Debug Adapter */ + { USB_DEVICE(0x10C4, 0x804E) }, /* Software Bisque Paramount ME build-in converter */ { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ { USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */ { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */ + { USB_DEVICE(0x10C4, 0x806F) }, /* IMS USB to RS422 Converter Cable */ { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */ + { USB_DEVICE(0x10C4, 0x80C4) }, /* Cygnal Integrated Products, Inc., Optris infrared thermometer */ { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */ { USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */ { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */ { USB_DEVICE(0x10C4, 0x8115) }, /* Arygon NFC/Mifare Reader */ { USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */ + { USB_DEVICE(0x10C4, 0x813F) }, /* Tams Master Easy Control */ { USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */ { USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */ + { USB_DEVICE(0x2405, 0x0003) }, /* West Mountain Radio RIGblaster Advantage */ + { USB_DEVICE(0x10C4, 0x8156) }, /* B&G H3000 link cable */ { USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */ + { USB_DEVICE(0x10C4, 0x815F) }, /* Timewave HamLinkUSB */ + { USB_DEVICE(0x10C4, 0x818B) }, /* AVIT Research USB to TTL */ { USB_DEVICE(0x10C4, 0x819F) }, /* MJS USB Toslink Switcher */ { USB_DEVICE(0x10C4, 0x81A6) }, /* ThinkOptics WavIt */ + { USB_DEVICE(0x10C4, 0x81A9) }, /* Multiplex RC Interface */ { USB_DEVICE(0x10C4, 0x81AC) }, /* MSD Dash Hawk */ + { USB_DEVICE(0x10C4, 0x81AD) }, /* INSYS USB Modem */ { USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */ { USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */ { USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */ + { USB_DEVICE(0x10C4, 0x81E8) }, /* Zephyr Bioharness */ + { USB_DEVICE(0x10C4, 0x81F2) }, /* C1007 HF band RFID controller */ { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ { USB_DEVICE(0x10C4, 0x822B) }, /* Modem EDGE(GSM) Comander 2 */ - { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demostration module */ - { USB_DEVICE(0x10c4, 0x8293) }, /* Telegesys ETRX2USB */ + { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */ + { USB_DEVICE(0x10C4, 0x8281) }, /* Nanotec Plug & Drive */ + { USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */ + { USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */ { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ + { USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */ { USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */ + { USB_DEVICE(0x10C4, 0x83D8) }, /* DekTec DTA Plus VHF/UHF Booster/Attenuator */ + { USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */ + { USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */ { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */ + { USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */ + { USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */ + { USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */ + { USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */ + { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */ + { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ + { USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */ + { USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ + { USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */ + { USB_DEVICE(0x10C4, 0xEA80) }, /* Silicon Labs factory default */ + { USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */ { USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */ { USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */ { USB_DEVICE(0x10C4, 0xF003) }, /* Elan Digital Systems USBpulse100 */ { USB_DEVICE(0x10C4, 0xF004) }, /* Elan Digital Systems USBcount50 */ { USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */ + { USB_DEVICE(0x10CE, 0xEA6A) }, /* Silicon Labs MobiData GPRS USB Modem 100EU */ { USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */ + { USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */ + { USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */ + { USB_DEVICE(0x166A, 0x0301) }, /* Clipsal 5800PC C-Bus Wireless PC Interface */ { USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */ + { USB_DEVICE(0x166A, 0x0304) }, /* Clipsal 5000CT2 C-Bus Black and White Touchscreen */ + { USB_DEVICE(0x166A, 0x0305) }, /* Clipsal C-5000CT2 C-Bus Spectrum Colour Touchscreen */ + { USB_DEVICE(0x166A, 0x0401) }, /* Clipsal L51xx C-Bus Architectural Dimmer */ + { USB_DEVICE(0x166A, 0x0101) }, /* Clipsal 5560884 C-Bus Multi-room Audio Matrix Switcher */ { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */ + { USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */ + { USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */ + { USB_DEVICE(0x16DC, 0x0012) }, /* W-IE-NE-R Plein & Baus GmbH MPOD Multi Channel Power Supply */ + { USB_DEVICE(0x16DC, 0x0015) }, /* W-IE-NE-R Plein & Baus GmbH CML Control, Monitoring and Data Logger */ + { USB_DEVICE(0x17A8, 0x0001) }, /* Kamstrup Optical Eye/3-wire */ + { USB_DEVICE(0x17A8, 0x0005) }, /* Kamstrup M-Bus Master MultiPort 250D */ + { USB_DEVICE(0x17F4, 0xAAAA) }, /* Wavesense Jazz blood glucose meter */ + { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ + { USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */ + { USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */ + { USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */ + { USB_DEVICE(0x1E29, 0x0102) }, /* Festo CPX-USB */ + { USB_DEVICE(0x1E29, 0x0501) }, /* Festo CMSP */ + { USB_DEVICE(0x1FB9, 0x0100) }, /* Lake Shore Model 121 Current Source */ + { USB_DEVICE(0x1FB9, 0x0200) }, /* Lake Shore Model 218A Temperature Monitor */ + { USB_DEVICE(0x1FB9, 0x0201) }, /* Lake Shore Model 219 Temperature Monitor */ + { USB_DEVICE(0x1FB9, 0x0202) }, /* Lake Shore Model 233 Temperature Transmitter */ + { USB_DEVICE(0x1FB9, 0x0203) }, /* Lake Shore Model 235 Temperature Transmitter */ + { USB_DEVICE(0x1FB9, 0x0300) }, /* Lake Shore Model 335 Temperature Controller */ + { USB_DEVICE(0x1FB9, 0x0301) }, /* Lake Shore Model 336 Temperature Controller */ + { USB_DEVICE(0x1FB9, 0x0302) }, /* Lake Shore Model 350 Temperature Controller */ + { USB_DEVICE(0x1FB9, 0x0303) }, /* Lake Shore Model 371 AC Bridge */ + { USB_DEVICE(0x1FB9, 0x0400) }, /* Lake Shore Model 411 Handheld Gaussmeter */ + { USB_DEVICE(0x1FB9, 0x0401) }, /* Lake Shore Model 425 Gaussmeter */ + { USB_DEVICE(0x1FB9, 0x0402) }, /* Lake Shore Model 455A Gaussmeter */ + { USB_DEVICE(0x1FB9, 0x0403) }, /* Lake Shore Model 475A Gaussmeter */ + { USB_DEVICE(0x1FB9, 0x0404) }, /* Lake Shore Model 465 Three Axis Gaussmeter */ + { USB_DEVICE(0x1FB9, 0x0600) }, /* Lake Shore Model 625A Superconducting MPS */ + { USB_DEVICE(0x1FB9, 0x0601) }, /* Lake Shore Model 642A Magnet Power Supply */ + { USB_DEVICE(0x1FB9, 0x0602) }, /* Lake Shore Model 648 Magnet Power Supply */ + { USB_DEVICE(0x1FB9, 0x0700) }, /* Lake Shore Model 737 VSM Controller */ + { USB_DEVICE(0x1FB9, 0x0701) }, /* Lake Shore Model 776 Hall Matrix */ + { USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */ + { USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */ + { USB_DEVICE(0x3195, 0xF281) }, /* Link Instruments MSO-28 */ + { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */ { } /* Terminating Entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); -static struct usb_driver cp2101_driver = { - .name = "cp210x", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = id_table, - .no_dynamic_id = 1, +struct cp210x_serial_private { + __u8 bInterfaceNumber; }; -static struct usb_serial_driver cp2101_device = { +static struct usb_serial_driver cp210x_device = { .driver = { .owner = THIS_MODULE, - .name = "cp210x", + .name = "cp210x", }, - .usb_driver = &cp2101_driver, .id_table = id_table, - .num_interrupt_in = 0, - .num_bulk_in = NUM_DONT_CARE, - .num_bulk_out = NUM_DONT_CARE, .num_ports = 1, - .open = cp2101_open, - .close = cp2101_close, - .ioctl = cp2101_ioctl, - .break_ctl = cp2101_break_ctl, - .set_termios = cp2101_set_termios, - .tiocmget = cp2101_tiocmget, - .tiocmset = cp2101_tiocmset, - .attach = cp2101_startup, - .shutdown = cp2101_shutdown, + .bulk_in_size = 256, + .bulk_out_size = 256, + .open = cp210x_open, + .close = cp210x_close, + .ioctl = cp210x_ioctl, + .break_ctl = cp210x_break_ctl, + .set_termios = cp210x_set_termios, + .tiocmget = cp210x_tiocmget, + .tiocmset = cp210x_tiocmset, + .attach = cp210x_startup, + .release = cp210x_release, + .dtr_rts = cp210x_dtr_rts +}; + +static struct usb_serial_driver * const serial_drivers[] = { + &cp210x_device, NULL }; /* Config request types */ -#define REQTYPE_HOST_TO_DEVICE 0x41 -#define REQTYPE_DEVICE_TO_HOST 0xc1 - -/* Config SET requests. To GET, add 1 to the request number */ -#define CP2101_UART 0x00 /* Enable / Disable */ -#define CP2101_BAUDRATE 0x01 /* (BAUD_RATE_GEN_FREQ / baudrate) */ -#define CP2101_BITS 0x03 /* 0x(0)(databits)(parity)(stopbits) */ -#define CP2101_BREAK 0x05 /* On / Off */ -#define CP2101_CONTROL 0x07 /* Flow control line states */ -#define CP2101_MODEMCTL 0x13 /* Modem controls */ -#define CP2101_CONFIG_6 0x19 /* 6 bytes of config data ??? */ - -/* CP2101_UART */ +#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 + +/* CP210X_IFC_ENABLE */ #define UART_ENABLE 0x0001 #define UART_DISABLE 0x0000 -/* CP2101_BAUDRATE */ +/* CP210X_(SET|GET)_BAUDDIV */ #define BAUD_RATE_GEN_FREQ 0x384000 -/* CP2101_BITS */ +/* CP210X_(SET|GET)_LINE_CTL */ #define BITS_DATA_MASK 0X0f00 #define BITS_DATA_5 0X0500 #define BITS_DATA_6 0X0600 @@ -181,11 +281,11 @@ static struct usb_serial_driver cp2101_device = { #define BITS_STOP_1_5 0x0001 #define BITS_STOP_2 0x0002 -/* CP2101_BREAK */ -#define BREAK_ON 0x0000 -#define BREAK_OFF 0x0001 +/* CP210X_SET_BREAK */ +#define BREAK_ON 0x0001 +#define BREAK_OFF 0x0000 -/* CP2101_CONTROL */ +/* CP210X_(SET_MHS|GET_MDMSTS) */ #define CONTROL_DTR 0x0001 #define CONTROL_RTS 0x0002 #define CONTROL_CTS 0x0010 @@ -195,110 +295,147 @@ static struct usb_serial_driver cp2101_device = { #define CONTROL_WRITE_DTR 0x0100 #define CONTROL_WRITE_RTS 0x0200 -/* CP2103 ioctls */ -#define IOCTL_GPIOGET 0x8000 /* Get gpio bits */ -#define IOCTL_GPIOSET 0x8001 /* Set gpio bits */ -#define IOCTL_GPIOBIC 0x8002 /* Clear specific gpio bit(s) */ -#define IOCTL_GPIOBIS 0x8003 /* Set specific gpio bit(s) */ - -/* CP210x ioctls principally used during initial device configuration */ -#define IOCTL_DEVICERESET 0x8004 /* Reset the cp210x */ -#define IOCTL_PORTCONFGET 0x8005 /* Get port configuration */ -#define IOCTL_PORTCONFSET 0x8006 /* Set port configuration */ -#define IOCTL_SETVID 0x8007 /* Set vendor id */ -#define IOCTL_SETPID 0x8008 /* Set product id */ -#define IOCTL_SETMFG 0x8009 /* Set manufacturer string */ -#define IOCTL_SETPRODUCT 0x800a /* Set product string */ -#define IOCTL_SETSERIAL 0x800b /* Set serial number string */ -#define IOCTL_SETDEVVER 0x800c /* set device version id */ -/* FIXME: where is IOCTL_SETMFG? */ - -/* CP2103 GPIO */ -#define GPIO_0 0x01 -#define GPIO_1 0x02 -#define GPIO_2 0x04 -#define GPIO_3 0x08 -#define GPIO_MASK (GPIO_0|GPIO_1|GPIO_2|GPIO_3) - -/* GetDeviceVersion() return codes */ -#define CP210x_CP2101_VERSION 0x01 -#define CP210x_CP2102_VERSION 0x02 -#define CP210x_CP2103_VERSION 0x03 - -/* Return codes */ -#define CP210x_SUCCESS 0x00 -#define CP210x_DEVICE_NOT_FOUND 0xFF -#define CP210x_INVALID_HANDLE 0x01 -#define CP210x_INVALID_PARAMETER 0x02 -#define CP210x_DEVICE_IO_FAILED 0x03 -#define CP210x_FUNCTION_NOT_SUPPORTED 0x04 -#define CP210x_GLOBAL_DATA_ERROR 0x05 -#define CP210x_FILE_ERROR 0x06 -#define CP210x_COMMAND_FAILED 0x08 -#define CP210x_INVALID_ACCESS_TYPE 0x09 - -/* USB descriptor sizes */ -#define CP210x_MAX_DEVICE_STRLEN 256 -#define CP210x_MAX_PRODUCT_STRLEN 126 -#define CP210x_MAX_SERIAL_STRLEN 63 -#define CP210x_MAX_MAXPOWER 250 - -/* Used to pass variable sized buffers between user and kernel space (ioctls) */ -typedef struct { - char* buf; - size_t len; -} cp2101_buffer_t; - -/* Port config definitions */ -typedef struct { - uint16_t mode; /* Push-pull = 1, Open-drain = 0 */ - uint16_t lowPower; - uint16_t latch; /* Logic high = 1, Logic low = 0 */ -} cp2101_port_state_t; - -typedef struct { - cp2101_port_state_t reset; - cp2101_port_state_t suspend; - uint8_t enhancedFxn; -} cp2101_port_config_t; - -#define PORT_CONFIG_LEN 13 /* Because sizeof() will pad to full words */ +/* cp210x_get_partnum() return codes */ +#define CP210x_PART_UNKNOWN 0x00 +#define CP210x_PART_CP2101 0x01 +#define CP210x_PART_CP2102 0x02 +#define CP210x_PART_CP2103 0x03 + +/* Taken from drivers/usb/gadget/usbstring.c */ +static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len) +{ + int count = 0; + u8 c; + u16 uchar; + + /* this insists on correct encodings, though not minimal ones. + * BUT it currently rejects legit 4-byte UTF-8 code points, + * which need surrogate pairs. (Unicode 3.1 can use them.) + */ + while (len != 0 && (c = (u8) *s++) != 0) { + if (unlikely(c & 0x80)) { + // 2-byte sequence: + // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx + if ((c & 0xe0) == 0xc0) { + uchar = (c & 0x1f) << 6; + + c = (u8) *s++; + if ((c & 0xc0) != 0x80) + goto fail; + c &= 0x3f; + uchar |= c; + + // 3-byte sequence (most CJKV characters): + // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx + } else if ((c & 0xf0) == 0xe0) { + uchar = (c & 0x0f) << 12; + + c = (u8) *s++; + if ((c & 0xc0) != 0x80) + goto fail; + c &= 0x3f; + uchar |= c << 6; + + c = (u8) *s++; + if ((c & 0xc0) != 0x80) + goto fail; + c &= 0x3f; + uchar |= c; + + /* no bogus surrogates */ + if (0xd800 <= uchar && uchar <= 0xdfff) + goto fail; + + // 4-byte sequence (surrogate pairs, currently rare): + // 11101110wwwwzzzzyy + 110111yyyyxxxxxx + // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx + // (uuuuu = wwww + 1) + // FIXME accept the surrogate code points (only) + + } else + goto fail; + } else + uchar = c; + *cp++ = uchar; + count++; + len--; + } + return count; +fail: + return -1; +} + /* - * cp2101_buf_from_user - * Copy a buffer from user space, returning the number of bytes copied - * from ubuf.buf into kbuf. klen is the size of the buffer at kbuf. + * make_usb_string + * Populates usbstr with: (len) + (USB_DT_STRING) + unicode(str). Each char in + * usbstr consumes two bytes in unicode (__le16) format. + * Returns the resulting length of usbstr in bytes. + * This function can accept overlapping usbstr and str as long as the overlap + * does not cause data written to usbstr to overwrite data not yet read from + * str. */ -size_t copy_buf_from_user(char *kbuf, unsigned long ubuf, size_t klen) +static int make_usb_string(__le16* usbstr, size_t usblen, const char *src, + size_t srclen) { - cp2101_buffer_t t; + int len; + + /* string descriptors have length, type, then UTF16-LE text */ + if (usblen > 254) + usblen = 254; + len = min(usblen / 2 - 1, srclen); + len = utf8_to_utf16le(src, usbstr + 1, len); + if (len >= 0) { + len = (len + 1) * 2; + ((u8 *)usbstr)[0] = len; + ((u8 *)usbstr)[1] = USB_DT_STRING; + } + return len; +} - if (!kbuf || !ubuf || !klen || - copy_from_user(&t, (cp2101_buffer_t *)ubuf, sizeof(t))) - return 0; - if (t.len < klen) - klen = t.len; - if (!t.buf || !t.len || copy_from_user(kbuf, t.buf, klen)) - return 0; - return klen; +/* + * cp210x_usbstr_from_user + * Populate kbuf with a usb string derived from a user space variable. klen + * is the size of the buffer at kbuf. + * Returns the number of bytes used in kbuf. + */ +static size_t cp210x_usbstr_from_user(__le16 *kbuf, + struct cp210x_buffer __user *ubuf, size_t klen) +{ + struct cp210x_buffer t; + char *str; + size_t slen; + + if (copy_from_user(&t, ubuf, sizeof(t))) + return -EINVAL; + slen = klen / 2 - 1; + if (!t.buf || !t.len || t.len > slen) + return -EINVAL; + if (t.len < slen) + slen = t.len; + str = (char *)kbuf + klen - slen; + if (copy_from_user(str, (__u8 __user *)t.buf, slen)) + return -EINVAL; + return make_usb_string(kbuf, klen, str, slen); } /* - * cp2101_ctlmsg + * cp210x_ctlmsg * A generic usb control message interface. * Returns the actual size of the data read or written within the message, 0 * if no data were read or written, or a negative value to indicate an error. */ -static int cp2101_ctlmsg(struct usb_serial_port* port, u8 request, - u8 requestype, u16 value, u16 index, void* data, u16 size) +static int cp210x_ctlmsg(struct usb_serial_port *port, u8 request, + u8 requestype, u16 value, u16 index, void *data, u16 size) { struct usb_device *dev = port->serial->dev; u8 *tbuf; int ret; - if (!(tbuf = kmalloc(size, GFP_KERNEL))) + tbuf = kmalloc(size, GFP_KERNEL); + if (!tbuf) return -ENOMEM; - if (requestype & 0x80) { + if (requestype & USB_DIR_IN) { ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request, requestype, value, index, tbuf, size, 300); if (ret > 0 && size) @@ -311,119 +448,83 @@ static int cp2101_ctlmsg(struct usb_serial_port* port, u8 request, } kfree(tbuf); if (ret < 0 && ret != -EPIPE) { - dev_printk(KERN_DEBUG, &dev->dev, "cp210x: control failed cmd rqt %u " - "rq %u len %u ret %d\n", requestype, request, size, ret); + dev_dbg(&port->dev, + "cp210x: ctl failed cmd rqt %u rq %u len %u ret %d\n", + requestype, request, size, ret); } return ret; } -static int cp2101_reset(struct usb_serial_port *port) +static int cp210x_reset(struct usb_serial_port *port) { - dbg("%s", __FUNCTION__); + dev_dbg(&port->dev, "%s", __func__); -#if 1 - /* Is this better than usb_device_reset? It may be. Once a client issues - * the reset ioctl, it must disconnect and reconnect, since the USB - * connections are torn down. We also ignore the error return, since - * the part resets and doesn't send one... + /* Instructing the CP210X to reset seems to work more reliably than + * calling usb_reset_device(). */ - cp2101_ctlmsg(port, 0xff, 0x40, 0x0008, 0x00, 0, 0); -#else - usb_reset_device(port->serial->dev); -#endif + cp210x_ctlmsg(port, 0xff, USB_TYPE_VENDOR, + 0x0008, 0x00, NULL, 0); return 0; } -static int cp2101_get_partnum(struct usb_serial_port *port) +static int cp210x_get_partnum(struct usb_serial_port *port) { - static u8 _partnum = 0; - int ret = CP210x_SUCCESS; - if (!_partnum) { + static u8 _partnum = CP210x_PART_UNKNOWN; + + if (_partnum == CP210x_PART_UNKNOWN) { u8 addr = port->serial->dev->actconfig->interface[0]-> cur_altsetting->endpoint[0].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - if (addr == 0x03 || addr == 0x02) /* The part is a cp2101 */ - _partnum = 0x01; + if (addr == 0x03 || addr == 0x02) + _partnum = CP210x_PART_CP2101; else if (addr == 0x01) { /* Must query part to determine part number */ - if (cp2101_ctlmsg(port, 0xff, 0xc0, 0x370b, 0x00, - &_partnum, 1) != 1) - ret = CP210x_DEVICE_IO_FAILED; - } else - ret = CP210x_DEVICE_IO_FAILED; + if (cp210x_ctlmsg(port, 0xff, + USB_DIR_IN|USB_TYPE_VENDOR, + 0x370b, 0x00, &_partnum, 1) != 1) + _partnum = CP210x_PART_UNKNOWN; + } } - dbg("%s - partnum %u err %d", __FUNCTION__, _partnum, ret); - return (ret == CP210x_SUCCESS) ? _partnum : 0; + dev_dbg(&port->dev, "%s - partnum %u", __func__, _partnum); + return _partnum; } -inline int cp2101_setu16(struct usb_serial_port *port, int cmd, +static inline int cp210x_setu16(struct usb_serial_port *port, int cmd, unsigned int value) { - return cp2101_ctlmsg(port, 0xff, 0x40, 0x3700 | (cmd & 0xff), - value, 0, 0); -} - -/* Populates usbstr with: (len) + (0x03) + unicode(str). Each char in str - * takes up two bytes in unicode format, so the resulting len(usbstr) is - * 2 * len(str) + 2. - * Returns the resulting length of the string in usbstr. - * This function can accept overlapping usbstr and str as long as the overlap - * does not cause data written to usbstr to overwrite data not yet read from - * str. - */ -static int make_usb_string(char* usbstr, size_t usblen, char* src, - size_t srclen) -{ - int len = 0; - - if (usbstr && usblen >= 2 && src && *src && srclen) { - char* p; - - /* The usb string format uses first byte as length */ - if (usblen > 256) - usblen = 256; - - p = usbstr + 1; - *p++ = 0x03; - len = 2; - while (srclen && len < usblen) { - *p++ = *src++; - *p++ = 0; - len += 2; - srclen--; - } - *usbstr = (char)len; - } - return len; + return cp210x_ctlmsg(port, 0xff, USB_TYPE_VENDOR, + 0x3700 | (cmd & 0xff), value, NULL, 0); } /* - * cp2101_setstr + * cp210x_setstr * * Set a USB string descriptor using proprietary cp210x control messages. * Return the number of characters actually written. */ -inline int cp2101_setstr(struct usb_serial_port *port, int cmd, char *usbstr) +static int cp210x_setstr(struct usb_serial_port *port, int cmd, __le16 *usbstr) { - unsigned len = usbstr[0]; - int ret = cp2101_ctlmsg(port, 0xff, 0x40, 0x3700 | (cmd & 0xff), 0, - usbstr, len); - dbg("%s - cmd 0x%02x len %d ret %d", __FUNCTION__, cmd, len, ret); + int len = ((u8 *)usbstr)[0]; + int ret = cp210x_ctlmsg(port, 0xff, USB_TYPE_VENDOR, + 0x3700 | (cmd & 0xff), 0, usbstr, len); + dev_dbg(&port->dev, "%s - cmd 0x%02x len %d ret %d", __func__, cmd, + len, ret); return ret; } /* Set all gpio simultaneously */ -static int cp2101_gpioset(struct usb_serial_port *port, u8 gpio) +static int cp210x_gpioset(struct usb_serial_port *port, u8 gpio) { - dbg("%s - port %d, gpio = 0x%.2x", __FUNCTION__, port->number, gpio); + dev_dbg(&port->dev, "%s - port %d, gpio = 0x%.2x", __func__, + port->port_number, gpio); - return cp2101_ctlmsg(port, 0xff, 0x40, 0x37e1, - ((uint16_t)gpio << 8) | GPIO_MASK, 0, 0); + return cp210x_ctlmsg(port, 0xff, USB_TYPE_VENDOR, + 0x37e1, ((uint16_t)gpio << 8) | GPIO_MASK, NULL, 0); } /* Set select gpio bits */ -static int cp2101_gpiosetb(struct usb_serial_port *port, u8 set, u8 clear) +static int cp210x_gpiosetb(struct usb_serial_port *port, u8 set, u8 clear) { u16 gpio = 0; @@ -445,51 +546,49 @@ static int cp2101_gpiosetb(struct usb_serial_port *port, u8 set, u8 clear) if (clear & GPIO_3) gpio = (gpio & ~(GPIO_3 << 8))|GPIO_3; - dbg("%s - port %d, gpiob = 0x%.4x", __FUNCTION__, port->number, gpio); + dev_dbg(&port->dev, "%s - port %d, gpiob = 0x%.4x", __func__, + port->port_number, gpio); - /* FIXME: how about REQTYPE_HOST_TO_DEVICE instead of 0x40? */ - return cp2101_ctlmsg(port, 0xff, 0x40, 0x37e1, gpio, 0, 0); + return cp210x_ctlmsg(port, 0xff, USB_TYPE_VENDOR, + 0x37e1, gpio, NULL, 0); } -static int cp2101_gpioget(struct usb_serial_port *port, u8* gpio) +static int cp210x_gpioget(struct usb_serial_port *port, u8 *gpio) { int ret; - dbg("%s - port %d", __FUNCTION__, port->number); + dev_dbg(&port->dev, "%s - port %d", __func__, port->port_number); - /* FIXME: how about REQTYPE_DEVICE_TO_HOST instead of 0xc0? */ - ret = cp2101_ctlmsg(port, 0xff, 0xc0, 0x00c2, 0, gpio, 1); + ret = cp210x_ctlmsg(port, 0xff, USB_DIR_IN|USB_TYPE_VENDOR, + 0x00c2, 0, gpio, 1); - dbg("%s - gpio = 0x%.2x (%d)", __FUNCTION__, *gpio, ret); + dev_dbg(&port->dev, "%s - gpio = 0x%.2x (%d)", __func__, *gpio, ret); return (ret == 1) ? 0 : -1; } -static int cp2101_portconfset(struct usb_serial_port *port, - cp2101_port_config_t* config) +static int cp210x_portconfset(struct usb_serial_port *port, + struct cp210x_port_config *config) { - cp2101_port_config_t lconfig; + struct cp210x_port_config lconfig; int ret; - dbg("%s", __FUNCTION__); + dev_dbg(&port->dev, "%s", __func__); memcpy(&lconfig, config, sizeof(lconfig)); - /* apparently not implemented yet */ - lconfig.suspend.lowPower = 0; - lconfig.reset.lowPower = 0; - - /* Words from cp2103 are MSB */ + /* Words from cp210x are MSB */ lconfig.reset.mode = cpu_to_be16(config->reset.mode); - /* lconfig.reset.lowPower = cpu_to_be16(config->reset.lowPower); */ lconfig.reset.latch = cpu_to_be16(config->reset.latch); lconfig.suspend.mode = cpu_to_be16(config->suspend.mode); - /* lconfig.suspend.lowPower = cpu_to_be16(config->suspend.lowPower); */ lconfig.suspend.latch = cpu_to_be16(config->suspend.latch); + /* apparently not implemented by CP210X firmware */ + lconfig.suspend.lowPower = 0; + lconfig.reset.lowPower = 0; - ret = cp2101_ctlmsg(port, 0xff, 0x40, 0x370c, 0, &lconfig, - PORT_CONFIG_LEN); - if (ret == PORT_CONFIG_LEN) + ret = cp210x_ctlmsg(port, 0xff, USB_TYPE_VENDOR, 0x370c, + 0, &lconfig, sizeof(struct cp210x_port_config)); + if (ret == sizeof(struct cp210x_port_config)) return 0; else if (ret >= 0) return -1; @@ -497,17 +596,17 @@ static int cp2101_portconfset(struct usb_serial_port *port, return ret; } -static int cp2101_portconfget(struct usb_serial_port *port, - cp2101_port_config_t* config) +static int cp210x_portconfget(struct usb_serial_port *port, + struct cp210x_port_config *config) { int ret; - dbg("%s", __FUNCTION__); + dev_dbg(&port->dev, "%s", __func__); - ret = cp2101_ctlmsg(port, 0xff, 0xc0, 0x370c, 0, config, - PORT_CONFIG_LEN); - if (ret == PORT_CONFIG_LEN) { - /* Words from cp2103 are MSB */ + ret = cp210x_ctlmsg(port, 0xff, USB_DIR_IN|USB_TYPE_VENDOR, + 0x370c, 0, config, sizeof(struct cp210x_port_config)); + if (ret == sizeof(struct cp210x_port_config)) { + /* Words from cp210x are MSB */ config->reset.mode = be16_to_cpu(config->reset.mode); config->reset.lowPower = be16_to_cpu(config->reset.lowPower); config->reset.latch = be16_to_cpu(config->reset.latch); @@ -529,72 +628,73 @@ static int cp2101_portconfget(struct usb_serial_port *port, } /* - * cp2101_get_config - * Reads from the CP2101 configuration registers + * cp210x_get_config + * Reads from the CP210x configuration registers * 'size' is specified in bytes. * 'data' is a pointer to a pre-allocated array of integers large * enough to hold 'size' bytes (with 4 bytes to each integer) */ -static int cp2101_get_config(struct usb_serial_port* port, u8 request, +static int cp210x_get_config(struct usb_serial_port *port, u8 request, unsigned int *data, int size) { struct usb_serial *serial = port->serial; + struct cp210x_serial_private *spriv = usb_get_serial_data(serial); __le32 *buf; int result, i, length; /* Number of integers required to contain the array */ - length = (((size - 1) | 3) + 1)/4; + length = (((size - 1) | 3) + 1) / 4; buf = kcalloc(length, sizeof(__le32), GFP_KERNEL); if (!buf) { - dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__); + dev_err(&port->dev, "%s - out of memory.\n", __func__); return -ENOMEM; } - /* For get requests, the request number must be incremented */ - request++; - /* Issue the request, attempting to read 'size' bytes */ - result = usb_control_msg (serial->dev,usb_rcvctrlpipe (serial->dev, 0), - request, REQTYPE_DEVICE_TO_HOST, 0x0000, - 0, buf, size, 300); + result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + request, REQTYPE_INTERFACE_TO_HOST, 0x0000, + spriv->bInterfaceNumber, buf, size, + USB_CTRL_GET_TIMEOUT); /* Convert data into an array of integers */ - for (i=0; idev, "%s - Unable to send config request, " - "request=0x%x size=%d result=%d\n", - __FUNCTION__, request, size, result); - return -EPROTO; + dev_dbg(&port->dev, "%s - Unable to send config request, request=0x%x size=%d result=%d\n", + __func__, request, size, result); + if (result > 0) + result = -EPROTO; + + return result; } return 0; } /* - * cp2101_set_config - * Writes to the CP2101 configuration registers + * cp210x_set_config + * Writes to the CP210x configuration registers * Values less than 16 bits wide are sent directly * 'size' is specified in bytes. */ -static int cp2101_set_config(struct usb_serial_port* port, u8 request, +static int cp210x_set_config(struct usb_serial_port *port, u8 request, unsigned int *data, int size) { struct usb_serial *serial = port->serial; + struct cp210x_serial_private *spriv = usb_get_serial_data(serial); __le32 *buf; int result, i, length; /* Number of integers required to contain the array */ - length = (((size - 1) | 3) + 1)/4; + length = (((size - 1) | 3) + 1) / 4; buf = kmalloc(length * sizeof(__le32), GFP_KERNEL); if (!buf) { - dev_err(&port->dev, "%s - out of memory.\n", - __FUNCTION__); + dev_err(&port->dev, "%s - out of memory.\n", __func__); return -ENOMEM; } @@ -603,271 +703,262 @@ static int cp2101_set_config(struct usb_serial_port* port, u8 request, buf[i] = cpu_to_le32(data[i]); if (size > 2) { - result = usb_control_msg (serial->dev, + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - request, REQTYPE_HOST_TO_DEVICE, 0x0000, - 0, buf, size, 300); + request, REQTYPE_HOST_TO_INTERFACE, 0x0000, + spriv->bInterfaceNumber, buf, size, + USB_CTRL_SET_TIMEOUT); } else { - result = usb_control_msg (serial->dev, + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - request, REQTYPE_HOST_TO_DEVICE, data[0], - 0, NULL, 0, 300); + request, REQTYPE_HOST_TO_INTERFACE, data[0], + spriv->bInterfaceNumber, NULL, 0, + USB_CTRL_SET_TIMEOUT); } kfree(buf); if ((size > 2 && result != size) || result < 0) { - dev_err(&port->dev, "%s - Unable to send request, " - "request=0x%x size=%d result=%d\n", - __FUNCTION__, request, size, result); - return -EPROTO; + dev_dbg(&port->dev, "%s - Unable to send request, request=0x%x size=%d result=%d\n", + __func__, request, size, result); + if (result > 0) + result = -EPROTO; + + return result; } - /* Single data value */ - result = usb_control_msg (serial->dev, - usb_sndctrlpipe(serial->dev, 0), - request, REQTYPE_HOST_TO_DEVICE, data[0], - 0, NULL, 0, 300); return 0; } /* - * cp2101_set_config_single - * Convenience function for calling cp2101_set_config on single data values + * cp210x_set_config_single + * Convenience function for calling cp210x_set_config on single data values * without requiring an integer pointer */ -static inline int cp2101_set_config_single(struct usb_serial_port* port, +static inline int cp210x_set_config_single(struct usb_serial_port *port, u8 request, unsigned int data) { - return cp2101_set_config(port, request, &data, 2); + return cp210x_set_config(port, request, &data, 2); } -static int cp2101_open (struct usb_serial_port *port, struct file *filp) +/* + * cp210x_quantise_baudrate + * Quantises the baud rate as per AN205 Table 1 + */ +static unsigned int cp210x_quantise_baudrate(unsigned int baud) { - struct usb_serial *serial = port->serial; - int result; - - dbg("%s - port %d", __FUNCTION__, port->number); + if (baud <= 300) + baud = 300; + else if (baud <= 600) baud = 600; + else if (baud <= 1200) baud = 1200; + else if (baud <= 1800) baud = 1800; + else if (baud <= 2400) baud = 2400; + else if (baud <= 4000) baud = 4000; + else if (baud <= 4803) baud = 4800; + else if (baud <= 7207) baud = 7200; + else if (baud <= 9612) baud = 9600; + else if (baud <= 14428) baud = 14400; + else if (baud <= 16062) baud = 16000; + else if (baud <= 19250) baud = 19200; + else if (baud <= 28912) baud = 28800; + else if (baud <= 38601) baud = 38400; + else if (baud <= 51558) baud = 51200; + else if (baud <= 56280) baud = 56000; + else if (baud <= 58053) baud = 57600; + else if (baud <= 64111) baud = 64000; + else if (baud <= 77608) baud = 76800; + else if (baud <= 117028) baud = 115200; + else if (baud <= 129347) baud = 128000; + else if (baud <= 156868) baud = 153600; + else if (baud <= 237832) baud = 230400; + else if (baud <= 254234) baud = 250000; + else if (baud <= 273066) baud = 256000; + else if (baud <= 491520) baud = 460800; + else if (baud <= 567138) baud = 500000; + else if (baud <= 670254) baud = 576000; + else if (baud < 1000000) + baud = 921600; + else if (baud > 2000000) + baud = 2000000; + return baud; +} - if (cp2101_set_config_single(port, CP2101_UART, UART_ENABLE)) { - dev_err(&port->dev, "%s - Unable to enable UART\n", - __FUNCTION__); - return -EPROTO; - } +static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) +{ + int result; - /* Start reading from the device */ - usb_fill_bulk_urb (port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - serial->type->read_bulk_callback, - port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); + result = cp210x_set_config_single(port, CP210X_IFC_ENABLE, + UART_ENABLE); if (result) { - dev_err(&port->dev, "%s - failed resubmitting read urb, " - "error %d\n", __FUNCTION__, result); + dev_err(&port->dev, "%s - Unable to enable UART\n", __func__); return result; } /* Configure the termios structure */ - cp2101_get_termios(port); + cp210x_get_termios(tty, port); - /* Set the DTR and RTS pins low */ - cp2101_tiocmset(port, NULL, TIOCM_DTR | TIOCM_RTS, 0); + /* The baud rate must be initialised on cp2104 */ + if (tty) + cp210x_change_speed(tty, port, NULL); - return 0; + return usb_serial_generic_open(tty, port); } -static void cp2101_cleanup (struct usb_serial_port *port) +static void cp210x_close(struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; - - dbg("%s - port %d", __FUNCTION__, port->number); - - if (serial->dev) { - /* shutdown any bulk reads that might be going on */ - if (serial->num_bulk_out) - usb_kill_urb(port->write_urb); - if (serial->num_bulk_in) - usb_kill_urb(port->read_urb); - } + usb_serial_generic_close(port); + cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE); } -static void cp2101_close (struct usb_serial_port *port, struct file * filp) +static int cp210x_ioctl(struct tty_struct *tty, unsigned int cmd, + unsigned long arg) { - dbg("%s - port %d", __FUNCTION__, port->number); - - /* shutdown our urbs */ - dbg("%s - shutting down urbs", __FUNCTION__); - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); + struct usb_serial_port *port = tty->driver_data; - cp2101_set_config_single(port, CP2101_UART, UART_DISABLE); -} - -static int cp2101_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd); + dev_dbg(&port->dev, "%s (%d) cmd = 0x%04x", __func__, + port->port_number, cmd); switch (cmd) { - + case TIOCMGET: { - int result = cp2101_tiocmget(port, file); - if (copy_to_user(&arg, &result, sizeof(int))) - return -EFAULT; + int result = cp210x_tiocmget(tty); + if (copy_to_user((int __user *)&arg, &result, sizeof(int))) + return -EFAULT; return 0; } break; case TIOCMSET: case TIOCMBIS: - case TIOCMBIC: { int val = 0; - if (copy_from_user(&val, &arg, sizeof(int))) + if (copy_from_user(&val, (int __user *)&arg, sizeof(int))) return -EFAULT; /* this looks wrong: TIOCMSET isn't going to work right */ - if (cp2101_tiocmset(port, file, cmd==TIOCMBIC ? 0 : val, - cmd==TIOCMBIC ? val : 0)) + if (cp210x_tiocmset(tty, (cmd == TIOCMBIC) ? 0 : val, + (cmd == TIOCMBIC) ? val : 0)) return -EFAULT; return 0; } break; - case IOCTL_GPIOGET: - if (cp2101_get_partnum(port) == CP210x_CP2103_VERSION) { + case CP210x_IOCTL_GPIOGET: + if (cp210x_get_partnum(port) == CP210x_PART_CP2103) { u8 gpio = 0; - if (!cp2101_gpioget(port, &gpio) && !copy_to_user((u8*)arg, - &gpio, sizeof(gpio))) + if (!cp210x_gpioget(port, &gpio) && !copy_to_user( + (__u8 __user *)arg, &gpio, + sizeof(gpio))) return 0; } return -EFAULT; break; - case IOCTL_GPIOSET: - if (cp2101_get_partnum(port) == CP210x_CP2103_VERSION && - !cp2101_gpioset(port, arg)) + case CP210x_IOCTL_GPIOSET: + if (cp210x_get_partnum(port) == CP210x_PART_CP2103 && + !cp210x_gpioset(port, arg)) return 0; return -EFAULT; break; - case IOCTL_GPIOBIC: - case IOCTL_GPIOBIS: - if (cp2101_get_partnum(port) == CP210x_CP2103_VERSION && - !cp2101_gpiosetb(port, (cmd==IOCTL_GPIOBIC) ? 0 : arg, - (cmd==IOCTL_GPIOBIC) ? arg : 0)) + case CP210x_IOCTL_GPIOBIC: + case CP210x_IOCTL_GPIOBIS: + if (cp210x_get_partnum(port) == CP210x_PART_CP2103 && + !cp210x_gpiosetb(port, + (cmd == CP210x_IOCTL_GPIOBIC) ? 0 : arg, + (cmd == CP210x_IOCTL_GPIOBIC) ? arg : 0)) return 0; return -EFAULT; break; - case IOCTL_DEVICERESET: - return cp2101_reset(port); + case CP210x_IOCTL_DEVICERESET: + return cp210x_reset(port); break; - case IOCTL_PORTCONFGET: + case CP210x_IOCTL_PORTCONFGET: { - cp2101_port_config_t config; - if (!cp2101_portconfget(port, &config) && !copy_to_user( - (cp2101_port_config_t*)arg, &config, - sizeof(config))) + struct cp210x_port_config config; + if (!cp210x_portconfget(port, &config) && !copy_to_user( + (struct cp210x_port_config __user *)arg, + &config, sizeof(config))) return 0; } return -EFAULT; break; - case IOCTL_PORTCONFSET: + case CP210x_IOCTL_PORTCONFSET: { - cp2101_port_config_t config; - if (!copy_from_user(&config, (cp2101_port_config_t*)arg, + struct cp210x_port_config config; + if (!copy_from_user(&config, (struct cp210x_port_config __user *)arg, sizeof(config)) && - !cp2101_portconfset(port, &config)) + !cp210x_portconfset(port, &config)) return 0; return -EFAULT; } break; - case IOCTL_SETVID: + case CP210x_IOCTL_SETVID: { unsigned int vid; - if (!copy_from_user(&vid, (unsigned int *)arg, + if (!copy_from_user(&vid, (unsigned int __user *)arg, sizeof(unsigned int)) && - !cp2101_setu16(port, 0x01, vid)) + !cp210x_setu16(port, 0x01, vid)) return 0; return -EFAULT; } break; - case IOCTL_SETPID: + case CP210x_IOCTL_SETPID: { unsigned int pid; - if (!copy_from_user(&pid, (unsigned int *)arg, + if (!copy_from_user(&pid, (unsigned int __user *)arg, sizeof(unsigned int)) && - !cp2101_setu16(port, 0x02, pid)) + !cp210x_setu16(port, 0x02, pid)) return 0; return -EFAULT; } break; - case IOCTL_SETMFG: -#if 0 /* Silicon Labs apparently doesn't provide for setting of mfg desc */ + case CP210x_IOCTL_SETPRODUCT: { - char usbstr[CP210x_MAX_MFG_STRLEN * 2 + 2]; - size_t len = copy_buf_from_user(usbstr + sizeof(usbstr) - - CP210x_MAX_MFG_STRLEN, arg, - CP210x_MAX_MFG_STRLEN); - len = make_usb_string(usbstr, sizeof(usbstr), str, len); - if (len && cp2101_setstr(port, 0x00, usbstr) == len) + __le16 usbstr[CP210x_MAX_PRODUCT_STRLEN + 1]; + size_t len = cp210x_usbstr_from_user(usbstr, + (struct cp210x_buffer __user *)arg, + sizeof(usbstr)); + if (len > 0 && cp210x_setstr(port, 0x03, usbstr) == len) return 0; return -EFAULT; } -#endif break; - case IOCTL_SETPRODUCT: + case CP210x_IOCTL_SETSERIAL: { - char usbstr[CP210x_MAX_PRODUCT_STRLEN * 2 + 2]; - char* str = usbstr + sizeof(usbstr) - CP210x_MAX_PRODUCT_STRLEN; - size_t len = copy_buf_from_user(str, arg, - CP210x_MAX_PRODUCT_STRLEN); - len = make_usb_string(usbstr, sizeof(usbstr), str, len); - if (len && cp2101_setstr(port, 0x03, usbstr) == len) + __le16 usbstr[CP210x_MAX_SERIAL_STRLEN + 1]; + size_t len = cp210x_usbstr_from_user(usbstr, + (struct cp210x_buffer __user *)arg, + sizeof(usbstr)); + if (len > 0 && cp210x_setstr(port, 0x04, usbstr) == len) return 0; return -EFAULT; } break; - case IOCTL_SETSERIAL: - { - char usbstr[CP210x_MAX_SERIAL_STRLEN * 2 + 2]; - char* str = usbstr + sizeof(usbstr) - CP210x_MAX_SERIAL_STRLEN; - size_t len = copy_buf_from_user(str, arg, - CP210x_MAX_SERIAL_STRLEN); - len = make_usb_string(usbstr, sizeof(usbstr), str, len); - if (len && cp2101_setstr(port, 0x04, usbstr) == len) - return 0; - return -EFAULT; - } - break; - - case IOCTL_SETDEVVER: + case CP210x_IOCTL_SETDEVVER: { unsigned int ver; - if (!copy_from_user(&ver, (unsigned int *)arg, + if (!copy_from_user(&ver, (unsigned int __user *)arg, sizeof(unsigned int)) && - !cp2101_setu16(port, 0x07, ver)) + !cp210x_setu16(port, 0x07, ver)) return 0; return -EFAULT; } break; default: - dbg("%s not supported = 0x%04x", __FUNCTION__, cmd); + dev_dbg(&port->dev, "%s not supported = 0x%04x", __func__, cmd); break; } @@ -875,306 +966,324 @@ static int cp2101_ioctl(struct usb_serial_port *port, struct file *file, } /* - * cp2101_get_termios + * 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 */ -static void cp2101_get_termios (struct usb_serial_port *port) +static void cp210x_get_termios(struct tty_struct *tty, + struct usb_serial_port *port) { - unsigned int cflag, modem_ctl[4]; - int baud; - int bits; + unsigned int baud; - dbg("%s - port %d", __FUNCTION__, port->number); - - if (!port->tty || !port->tty->termios) { - dbg("%s - no tty structures", __FUNCTION__); - return; + if (tty) { + cp210x_get_termios_port(tty->driver_data, + &tty->termios.c_cflag, &baud); + tty_encode_baud_rate(tty, baud, baud); + } else { + unsigned int cflag; + cflag = 0; + cp210x_get_termios_port(port, &cflag, &baud); } +} - cp2101_get_config(port, CP2101_BAUDRATE, &baud, 2); - /* Convert to baudrate */ - if (baud) - baud = BAUD_RATE_GEN_FREQ / baud; +/* + * cp210x_get_termios_port + * This is the heart of cp210x_get_termios which always uses a &usb_serial_port. + */ +static void cp210x_get_termios_port(struct usb_serial_port *port, + unsigned int *cflagp, unsigned int *baudp) +{ + struct device *dev = &port->dev; + unsigned int cflag, modem_ctl[4]; + unsigned int baud; + unsigned int bits; + + cp210x_get_config(port, CP210X_GET_BAUDRATE, &baud, 4); - dbg("%s - baud rate = %d", __FUNCTION__, baud); + dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud); + *baudp = baud; - tty_encode_baud_rate(port->tty, baud, baud); - cflag = port->tty->termios->c_cflag; + cflag = *cflagp; - cp2101_get_config(port, CP2101_BITS, &bits, 2); + cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2); cflag &= ~CSIZE; - switch(bits & BITS_DATA_MASK) { - case BITS_DATA_5: - dbg("%s - data bits = 5", __FUNCTION__); - cflag |= CS5; - break; - case BITS_DATA_6: - dbg("%s - data bits = 6", __FUNCTION__); - cflag |= CS6; - break; - case BITS_DATA_7: - dbg("%s - data bits = 7", __FUNCTION__); - cflag |= CS7; - break; - case BITS_DATA_8: - dbg("%s - data bits = 8", __FUNCTION__); - cflag |= CS8; - break; - case BITS_DATA_9: - dbg("%s - data bits = 9 (not supported, " - "using 8 data bits)", __FUNCTION__); - cflag |= CS8; - bits &= ~BITS_DATA_MASK; - bits |= BITS_DATA_8; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; - default: - dbg("%s - Unknown number of data bits, " - "using 8", __FUNCTION__); - cflag |= CS8; - bits &= ~BITS_DATA_MASK; - bits |= BITS_DATA_8; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; + switch (bits & BITS_DATA_MASK) { + case BITS_DATA_5: + dev_dbg(dev, "%s - data bits = 5\n", __func__); + cflag |= CS5; + break; + case BITS_DATA_6: + dev_dbg(dev, "%s - data bits = 6\n", __func__); + cflag |= CS6; + break; + case BITS_DATA_7: + dev_dbg(dev, "%s - data bits = 7\n", __func__); + cflag |= CS7; + break; + case BITS_DATA_8: + dev_dbg(dev, "%s - data bits = 8\n", __func__); + cflag |= CS8; + break; + case BITS_DATA_9: + dev_dbg(dev, "%s - data bits = 9 (not supported, using 8 data bits)\n", __func__); + cflag |= CS8; + bits &= ~BITS_DATA_MASK; + bits |= BITS_DATA_8; + cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2); + break; + default: + dev_dbg(dev, "%s - Unknown number of data bits, using 8\n", __func__); + cflag |= CS8; + bits &= ~BITS_DATA_MASK; + bits |= BITS_DATA_8; + cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2); + break; } - switch(bits & BITS_PARITY_MASK) { - case BITS_PARITY_NONE: - dbg("%s - parity = NONE", __FUNCTION__); - cflag &= ~PARENB; - break; - case BITS_PARITY_ODD: - dbg("%s - parity = ODD", __FUNCTION__); - cflag |= (PARENB|PARODD); - break; - case BITS_PARITY_EVEN: - dbg("%s - parity = EVEN", __FUNCTION__); - cflag &= ~PARODD; - cflag |= PARENB; - break; - case BITS_PARITY_MARK: - dbg("%s - parity = MARK (not supported, " - "disabling parity)", __FUNCTION__); - cflag &= ~PARENB; - bits &= ~BITS_PARITY_MASK; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; - case BITS_PARITY_SPACE: - dbg("%s - parity = SPACE (not supported, " - "disabling parity)", __FUNCTION__); - cflag &= ~PARENB; - bits &= ~BITS_PARITY_MASK; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; - default: - dbg("%s - Unknown parity mode, " - "disabling parity", __FUNCTION__); - cflag &= ~PARENB; - bits &= ~BITS_PARITY_MASK; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; + switch (bits & BITS_PARITY_MASK) { + case BITS_PARITY_NONE: + dev_dbg(dev, "%s - parity = NONE\n", __func__); + cflag &= ~PARENB; + break; + case BITS_PARITY_ODD: + dev_dbg(dev, "%s - parity = ODD\n", __func__); + cflag |= (PARENB|PARODD); + break; + case BITS_PARITY_EVEN: + dev_dbg(dev, "%s - parity = EVEN\n", __func__); + cflag &= ~PARODD; + cflag |= PARENB; + break; + case BITS_PARITY_MARK: + dev_dbg(dev, "%s - parity = MARK\n", __func__); + cflag |= (PARENB|PARODD|CMSPAR); + break; + case BITS_PARITY_SPACE: + dev_dbg(dev, "%s - parity = SPACE\n", __func__); + cflag &= ~PARODD; + cflag |= (PARENB|CMSPAR); + break; + default: + dev_dbg(dev, "%s - Unknown parity mode, disabling parity\n", __func__); + cflag &= ~PARENB; + bits &= ~BITS_PARITY_MASK; + cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2); + break; } cflag &= ~CSTOPB; - switch(bits & BITS_STOP_MASK) { - case BITS_STOP_1: - dbg("%s - stop bits = 1", __FUNCTION__); - break; - case BITS_STOP_1_5: - dbg("%s - stop bits = 1.5 (not supported, " - "using 1 stop bit)", __FUNCTION__); - bits &= ~BITS_STOP_MASK; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; - case BITS_STOP_2: - dbg("%s - stop bits = 2", __FUNCTION__); - cflag |= CSTOPB; - break; - default: - dbg("%s - Unknown number of stop bits, " - "using 1 stop bit", __FUNCTION__); - bits &= ~BITS_STOP_MASK; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; + switch (bits & BITS_STOP_MASK) { + case BITS_STOP_1: + dev_dbg(dev, "%s - stop bits = 1\n", __func__); + break; + case BITS_STOP_1_5: + dev_dbg(dev, "%s - stop bits = 1.5 (not supported, using 1 stop bit)\n", __func__); + bits &= ~BITS_STOP_MASK; + cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2); + break; + case BITS_STOP_2: + dev_dbg(dev, "%s - stop bits = 2\n", __func__); + cflag |= CSTOPB; + break; + default: + dev_dbg(dev, "%s - Unknown number of stop bits, using 1 stop bit\n", __func__); + bits &= ~BITS_STOP_MASK; + cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2); + break; } - cp2101_get_config(port, CP2101_MODEMCTL, modem_ctl, 16); + cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16); if (modem_ctl[0] & 0x0008) { - dbg("%s - flow control = CRTSCTS", __FUNCTION__); + dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__); cflag |= CRTSCTS; } else { - dbg("%s - flow control = NONE", __FUNCTION__); + dev_dbg(dev, "%s - flow control = NONE\n", __func__); cflag &= ~CRTSCTS; } - port->tty->termios->c_cflag = cflag; + *cflagp = cflag; } -static void cp2101_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +/* + * 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. + */ +static void cp210x_change_speed(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { - unsigned int cflag, old_cflag; - int baud=0, bits; - unsigned int modem_ctl[4]; + u32 baud; - dbg("%s - port %d", __FUNCTION__, port->number); + baud = tty->termios.c_ospeed; - if (!port->tty || !port->tty->termios) { - dbg("%s - no tty structures", __FUNCTION__); - return; + /* This maps the requested rate to a rate valid on cp2102 or cp2103, + * or to an arbitrary rate in [1M,2M]. + * + * NOTE: B0 is not implemented. + */ + baud = cp210x_quantise_baudrate(baud); + + dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud); + if (cp210x_set_config(port, CP210X_SET_BAUDRATE, &baud, + sizeof(baud))) { + dev_warn(&port->dev, "failed to set baud rate to %u\n", baud); + if (old_termios) + baud = old_termios->c_ospeed; + else + baud = 9600; } - port->tty->termios->c_cflag &= ~CMSPAR; - cflag = port->tty->termios->c_cflag; + tty_encode_baud_rate(tty, baud, baud); +} + +static void cp210x_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) +{ + struct device *dev = &port->dev; + unsigned int cflag, old_cflag; + unsigned int bits; + unsigned int modem_ctl[4]; + + cflag = tty->termios.c_cflag; old_cflag = old_termios->c_cflag; - baud = tty_get_baud_rate(port->tty); - - /* If the baud rate is to be updated*/ - if (baud != tty_termios_baud_rate(old_termios)) { - switch (baud) { - case 0: - case 600: - case 1200: - case 1800: - case 2400: - case 4800: - case 7200: - case 9600: - case 14400: - case 19200: - case 28800: - case 38400: - case 55854: - case 57600: - case 115200: - case 127117: - case 230400: - case 460800: - case 921600: - case 3686400: - break; - default: - baud = 9600; - break; - } - if (baud) { - dbg("%s - Setting baud rate to %d baud", __FUNCTION__, - baud); - if (cp2101_set_config_single(port, CP2101_BAUDRATE, - (BAUD_RATE_GEN_FREQ / baud))) { - dev_err(&port->dev, "Baud rate requested not " - "supported by device\n"); - baud = tty_termios_baud_rate(old_termios); - } - } - } - /* Report back the resulting baud rate */ - tty_encode_baud_rate(port->tty, baud, baud); + if (tty->termios.c_ospeed != old_termios->c_ospeed) + cp210x_change_speed(tty, port, old_termios); /* If the number of data bits is to be updated */ if ((cflag & CSIZE) != (old_cflag & CSIZE)) { - cp2101_get_config(port, CP2101_BITS, &bits, 2); + cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2); bits &= ~BITS_DATA_MASK; switch (cflag & CSIZE) { - case CS5: - bits |= BITS_DATA_5; - dbg("%s - data bits = 5", __FUNCTION__); - break; - case CS6: - bits |= BITS_DATA_6; - dbg("%s - data bits = 6", __FUNCTION__); - break; - case CS7: - bits |= BITS_DATA_7; - dbg("%s - data bits = 7", __FUNCTION__); - break; - case CS8: - bits |= BITS_DATA_8; - dbg("%s - data bits = 8", __FUNCTION__); - break; - /*case CS9: - bits |= BITS_DATA_9; - dbg("%s - data bits = 9", __FUNCTION__); - break;*/ - default: - dev_err(&port->dev, "cp210x driver does not " - "support the number of bits requested," - " using 8 bit mode\n"); - bits |= BITS_DATA_8; - break; + case CS5: + bits |= BITS_DATA_5; + dev_dbg(dev, "%s - data bits = 5\n", __func__); + break; + case CS6: + bits |= BITS_DATA_6; + dev_dbg(dev, "%s - data bits = 6\n", __func__); + break; + case CS7: + bits |= BITS_DATA_7; + dev_dbg(dev, "%s - data bits = 7\n", __func__); + break; + case CS8: + bits |= BITS_DATA_8; + dev_dbg(dev, "%s - data bits = 8\n", __func__); + break; + /*case CS9: + bits |= BITS_DATA_9; + dev_dbg(dev, "%s - data bits = 9\n", __func__); + break;*/ + default: + dev_dbg(dev, "cp210x driver does not support the number of bits requested, using 8 bit mode\n"); + bits |= BITS_DATA_8; + break; } - if (cp2101_set_config(port, CP2101_BITS, &bits, 2)) - dev_err(&port->dev, "Number of data bits requested " - "not supported by device\n"); + if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2)) + dev_dbg(dev, "Number of data bits requested not supported by device\n"); } - if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))) { - cp2101_get_config(port, CP2101_BITS, &bits, 2); + if ((cflag & (PARENB|PARODD|CMSPAR)) != + (old_cflag & (PARENB|PARODD|CMSPAR))) { + cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2); bits &= ~BITS_PARITY_MASK; if (cflag & PARENB) { - if (cflag & PARODD) { - bits |= BITS_PARITY_ODD; - dbg("%s - parity = ODD", __FUNCTION__); + if (cflag & CMSPAR) { + if (cflag & PARODD) { + bits |= BITS_PARITY_MARK; + dev_dbg(dev, "%s - parity = MARK\n", __func__); + } else { + bits |= BITS_PARITY_SPACE; + dev_dbg(dev, "%s - parity = SPACE\n", __func__); + } } else { - bits |= BITS_PARITY_EVEN; - dbg("%s - parity = EVEN", __FUNCTION__); + if (cflag & PARODD) { + bits |= BITS_PARITY_ODD; + dev_dbg(dev, "%s - parity = ODD\n", __func__); + } else { + bits |= BITS_PARITY_EVEN; + dev_dbg(dev, "%s - parity = EVEN\n", __func__); + } } } - if (cp2101_set_config(port, CP2101_BITS, &bits, 2)) - dev_err(&port->dev, "Parity mode not supported " - "by device\n"); + if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2)) + dev_dbg(dev, "Parity mode not supported by device\n"); } if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) { - cp2101_get_config(port, CP2101_BITS, &bits, 2); + cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2); bits &= ~BITS_STOP_MASK; if (cflag & CSTOPB) { bits |= BITS_STOP_2; - dbg("%s - stop bits = 2", __FUNCTION__); + dev_dbg(dev, "%s - stop bits = 2\n", __func__); } else { bits |= BITS_STOP_1; - dbg("%s - stop bits = 1", __FUNCTION__); + dev_dbg(dev, "%s - stop bits = 1\n", __func__); } - if (cp2101_set_config(port, CP2101_BITS, &bits, 2)) - dev_err(&port->dev, "Number of stop bits requested " - "not supported by device\n"); + if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2)) + dev_dbg(dev, "Number of stop bits requested not supported by device\n"); } if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) { - cp2101_get_config(port, CP2101_MODEMCTL, modem_ctl, 16); - dbg("%s - read modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x", - __FUNCTION__, modem_ctl[0], modem_ctl[1], - modem_ctl[2], modem_ctl[3]); + cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16); + dev_dbg(dev, "%s - read modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x\n", + __func__, modem_ctl[0], modem_ctl[1], + modem_ctl[2], modem_ctl[3]); if (cflag & CRTSCTS) { modem_ctl[0] &= ~0x7B; modem_ctl[0] |= 0x09; modem_ctl[1] = 0x80; - dbg("%s - flow control = CRTSCTS", __FUNCTION__); + dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__); } else { modem_ctl[0] &= ~0x7B; modem_ctl[0] |= 0x01; modem_ctl[1] |= 0x40; - dbg("%s - flow control = NONE", __FUNCTION__); + dev_dbg(dev, "%s - flow control = NONE\n", __func__); } - dbg("%s - write modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x", - __FUNCTION__, modem_ctl[0], modem_ctl[1], - modem_ctl[2], modem_ctl[3]); - cp2101_set_config(port, CP2101_MODEMCTL, modem_ctl, 16); + dev_dbg(dev, "%s - write modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x\n", + __func__, modem_ctl[0], modem_ctl[1], + modem_ctl[2], modem_ctl[3]); + cp210x_set_config(port, CP210X_SET_FLOW, modem_ctl, 16); } } -static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file, +static int cp210x_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { - int control = 0; + struct usb_serial_port *port = tty->driver_data; + return cp210x_tiocmset_port(port, set, clear); +} - dbg("%s - port %d", __FUNCTION__, port->number); +static int cp210x_tiocmset_port(struct usb_serial_port *port, + unsigned int set, unsigned int clear) +{ + unsigned int control = 0; if (set & TIOCM_RTS) { control |= CONTROL_RTS; @@ -1193,19 +1302,26 @@ static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file, control |= CONTROL_WRITE_DTR; } - dbg("%s - control = 0x%.4x", __FUNCTION__, control); - - return cp2101_set_config(port, CP2101_CONTROL, &control, 2); + dev_dbg(&port->dev, "%s - control = 0x%.4x\n", __func__, control); + return cp210x_set_config(port, CP210X_SET_MHS, &control, 2); } -static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file) +static void cp210x_dtr_rts(struct usb_serial_port *p, int on) { - int control, result; + if (on) + cp210x_tiocmset_port(p, TIOCM_DTR|TIOCM_RTS, 0); + else + cp210x_tiocmset_port(p, 0, TIOCM_DTR|TIOCM_RTS); +} - dbg("%s - port %d", __FUNCTION__, port->number); +static int cp210x_tiocmget(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + unsigned int control; + int result; - cp2101_get_config(port, CP2101_CONTROL, &control, 1); + cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1); result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0) |((control & CONTROL_RTS) ? TIOCM_RTS : 0) @@ -1214,76 +1330,55 @@ static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file) |((control & CONTROL_RING)? TIOCM_RI : 0) |((control & CONTROL_DCD) ? TIOCM_CD : 0); - dbg("%s - control = 0x%.2x", __FUNCTION__, control); + dev_dbg(&port->dev, "%s - control = 0x%.2x\n", __func__, control); return result; } -static void cp2101_break_ctl (struct usb_serial_port *port, int break_state) +static void cp210x_break_ctl(struct tty_struct *tty, int break_state) { - int state; + struct usb_serial_port *port = tty->driver_data; + unsigned int state; - dbg("%s - port %d", __FUNCTION__, port->number); if (break_state == 0) state = BREAK_OFF; else state = BREAK_ON; - dbg("%s - turning break %s", __FUNCTION__, - state==BREAK_OFF ? "off" : "on"); - cp2101_set_config(port, CP2101_BREAK, &state, 2); + dev_dbg(&port->dev, "%s - turning break %s\n", __func__, + state == BREAK_OFF ? "off" : "on"); + cp210x_set_config(port, CP210X_SET_BREAK, &state, 2); } -static int cp2101_startup (struct usb_serial *serial) +static int cp210x_startup(struct usb_serial *serial) { - /* CP2101 buffers behave strangely unless device is reset */ - usb_reset_device(serial->dev); - return 0; -} + struct usb_host_interface *cur_altsetting; + struct cp210x_serial_private *spriv; -static void cp2101_shutdown (struct usb_serial *serial) -{ - int i; - - dbg("%s", __FUNCTION__); - - /* Stop reads and writes on all ports */ - for (i=0; i < serial->num_ports; ++i) { - cp2101_cleanup(serial->port[i]); - } -} + /* cp210x buffers behave strangely unless device is reset */ + usb_reset_device(serial->dev); -static int __init cp2101_init (void) -{ - int retval; + spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); + if (!spriv) + return -ENOMEM; - retval = usb_serial_register(&cp2101_device); - if (retval) - return retval; /* Failed to register */ + cur_altsetting = serial->interface->cur_altsetting; + spriv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber; - retval = usb_register(&cp2101_driver); - if (retval) { - /* Failed to register */ - usb_serial_deregister(&cp2101_device); - return retval; - } + usb_set_serial_data(serial, spriv); - /* Success */ - info(DRIVER_DESC " " DRIVER_VERSION); return 0; } -static void __exit cp2101_exit (void) +static void cp210x_release(struct usb_serial *serial) { - usb_deregister (&cp2101_driver); - usb_serial_deregister (&cp2101_device); + struct cp210x_serial_private *spriv; + + spriv = usb_get_serial_data(serial); + kfree(spriv); } -module_init(cp2101_init); -module_exit(cp2101_exit); +module_usb_serial_driver(serial_drivers, id_table); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable verbose debugging messages");