diff -Nru a/Documentation/Configure.help b/Documentation/Configure.help --- a/Documentation/Configure.help 2010-02-12 16:38:56.000000000 +0000 +++ b/Documentation/Configure.help 2010-02-12 16:41:39.000000000 +0000 @@ -15558,6 +15558,15 @@ The module will be called omninet.o. If you want to compile it as a module, say M here and read . +CONFIG_USB_SERIAL_CP210X + Say Y here if you want to use a serial converter based on the SiLabs + CP2101, CP2102, CP2103 (and possibly others) serial converter chips. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called cp210x.o. If you want to compile it as a + module, say M here and read . + USB Digi International AccelePort USB Serial Driver CONFIG_USB_SERIAL_DIGI_ACCELEPORT Say Y here if you want to use Digi AccelePort USB 2 or 4 devices, diff -Nru a/drivers/usb/serial/Config.in b/drivers/usb/serial/Config.in --- a/drivers/usb/serial/Config.in 2010-02-12 16:39:59.000000000 +0000 +++ b/drivers/usb/serial/Config.in 2010-02-12 16:41:39.000000000 +0000 @@ -41,6 +41,7 @@ dep_tristate ' USB REINER SCT cyberJack pinpad/e-com chipcard reader (EXPERIMENTAL)' CONFIG_USB_SERIAL_CYBERJACK $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL dep_tristate ' USB Xircom / Entregra Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_XIRCOM $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL dep_tristate ' USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_OMNINET $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL + dep_tristate ' USB SiLabs CP210x Driver' CONFIG_USB_SERIAL_CP210X $CONFIG_USB_SERIAL fi endmenu diff -Nru a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c --- a/drivers/usb/serial/cp210x.c 1970-01-01 01:00:00.000000000 +0100 +++ b/drivers/usb/serial/cp210x.c 2010-02-12 18:32:28.000000000 +0000 @@ -0,0 +1,1090 @@ +/* + * Silicon Laboratories CP2101/CP2102/CP2103 USB to RS232 serial adaptor driver + * + * Copyright (C) 2010 Craig Shelley (craig@microtron.org.uk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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 + * Original backport to 2.4 by andreas 'randy' weinberger randy@ebv.com + * Several fixes and enhancements by Bill Pfutzenreuter BPfutzenreuter@itsgames.com + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef CONFIG_USB_SERIAL_DEBUG + static int debug = 1; +#else + static int debug = 0; +#endif + +#include "usb-serial.h" +static int product, vendor; +static spinlock_t write_list_lock; + +struct cp210x_packet { + char *data; + size_t len; + size_t written; + struct list_head list; +}; + +struct cp210x_private { + int active; + int queue_len; + int free_len; + struct list_head queue; + struct list_head freelist; + struct termios termios; + unsigned long line_state; +}; + + +/* + * Version Information + */ +#define DRIVER_VERSION "v0.11_2.4-backport" +#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver" +#define DRIVER_AUTHOR "Craig Shelley " +/* + * Function Prototypes + */ +static int cp210x_open(struct usb_serial_port*, struct file*); +static void cp210x_cleanup(struct usb_serial_port*); +static void cp210x_close(struct usb_serial_port*, struct file*); +static void cp210x_get_termios(struct usb_serial_port*); +static void cp210x_set_termios(struct usb_serial_port*, struct termios*); +static void cp210x_break_ctl(struct usb_serial_port*, int break_state); +static int cp210x_ioctl (struct usb_serial_port *port, struct file *file, + unsigned int cmd, unsigned long arg); +static void cp210x_shutdown(struct usb_serial*); +static void cp210x_write_gather(struct usb_serial_port *port); +static void cp210x_read_bulk_callback (struct urb *urb); +static void cp210x_write_bulk_callback(struct urb *urb); + + +static int debug; + +static struct usb_device_id id_table [] = { + { USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */ + { USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */ + { USB_DEVICE(0x0745, 0x1000) }, /* CipherLab USB CCD Barcode Scanner 1000 */ + { 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(0x0FCF, 0x1003) }, /* Dynastream ANT development board */ + { USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */ + { USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */ + { 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, 0x8053) }, /* Enfora EDG1228 */ + { USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */ + { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */ + { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */ + { 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(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */ + { USB_DEVICE(0x10C4, 0x819F) }, /* MJS USB Toslink Switcher */ + { USB_DEVICE(0x10C4, 0x81A6) }, /* ThinkOptics WavIt */ + { USB_DEVICE(0x10C4, 0x81AC) }, /* MSD Dash Hawk */ + { 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, 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, 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, 0x8411) }, /* Kyocera GPS Module */ + { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */ + { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ + { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ + { 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, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */ + { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */ + { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ + { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */ + { } /* Terminating Entry */ +}; + +MODULE_DEVICE_TABLE (usb, id_table); + +static struct usb_serial_device_type cp210x_device = { + .owner = THIS_MODULE, + .name = "CP210X", + .id_table = id_table, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = 0, + .num_bulk_out = 0, + .num_ports = 1, + .open = cp210x_open, + .close = cp210x_close, + .break_ctl = cp210x_break_ctl, + .ioctl = cp210x_ioctl, + .set_termios = cp210x_set_termios, + .shutdown = cp210x_shutdown +}; + +#define URBDATA_SIZE 4096 +#define URBDATA_QUEUE_MAX (64 * 1024) +#define PACKET_SIZE 256 +#define KP_RETRIES 100 + +/* 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 CP210X_UART 0x00 /* Enable / Disable */ +#define CP210X_BAUDRATE 0x01 /* (BAUD_RATE_GEN_FREQ / baudrate) */ +#define CP210X_BITS 0x03 /* 0x(0)(databits)(parity)(stopbits) */ +#define CP210X_BREAK 0x05 /* On / Off */ +#define CP210X_CONTROL 0x07 /* Flow control line states */ +#define CP210X_MODEMCTL 0x13 /* Modem controls */ +#define CP210X_CONFIG_6 0x19 /* 6 bytes of config data ??? */ + +/* CP210X_UART */ +#define UART_ENABLE 0x0001 +#define UART_DISABLE 0x0000 + +/* CP210X_BAUDRATE */ +#define BAUD_RATE_GEN_FREQ 0x384000 + +/* CP210X_BITS */ +#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_BREAK */ +#define BREAK_ON 0x0000 +#define BREAK_OFF 0x0001 + +/* CP210X_CONTROL */ +#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_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 cp210x_get_config(struct usb_serial_port* port, u8 request, + unsigned int *data, int size) +{ + struct usb_serial *serial = port->serial; + u32 *buf; + int result, i, length; + + if ( serial->dev == NULL ) { + dbg("%s - serial->dev = NULL (usb disconnected)", __FUNCTION__ ); + result = -ENXIO; + } + + /* Number of integers required to contain the array */ + length = (((size - 1) | 3) + 1)/4; + + buf = kmalloc (length * sizeof(u32), GFP_KERNEL); + if (!buf) { + err("%s - out of memory.\n", __FUNCTION__); + return -ENOMEM; + } + + memset(buf, 0, length * sizeof(u32)); + + /* 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); + + /* Convert data into an array of integers */ + for (i=0; iserial; + u32 *buf; + int result, i, length; + + if ( serial->dev == NULL ) { + dbg("%s - serial->dev = NULL (usb disconnected)", __FUNCTION__ ); + result = -ENXIO; + } + + /* Number of integers required to contain the array */ + length = (((size - 1) | 3) + 1)/4; + + buf = kmalloc(length * sizeof(u32), GFP_KERNEL); + if (!buf) { + err( "%s - out of memory.\n", + __FUNCTION__); + return -ENOMEM; + } + + /* Array of integers into bytes */ + for (i = 0; i < length; i++) + buf[i] = cpu_to_le32(data[i]); + + if (size > 2) { + result = usb_control_msg (serial->dev, + usb_sndctrlpipe(serial->dev, 0), + request, REQTYPE_HOST_TO_DEVICE, 0x0000, + 0, buf, size, 300); + } else { + result = usb_control_msg (serial->dev, + usb_sndctrlpipe(serial->dev, 0), + request, REQTYPE_HOST_TO_DEVICE, data[0], + 0, NULL, 0, 300); + } + + kfree(buf); + + if ((size > 2 && result != size) || result < 0) { + err( "%s - Unable to send request, " + "request=0x%x size=%d result=%d\n", + __FUNCTION__, request, size, result); + return -EPROTO; + } + + /* 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; +} + +/* + * cp210x_set_config_single + * Convenience function for calling cp210x_set_config on single data values + * without requiring an integer pointer + */ +static inline int cp210x_set_config_single(struct usb_serial_port* port, + u8 request, unsigned int data) +{ + return cp210x_set_config(port, request, &data, 2); +} + +static int cp210x_open (struct usb_serial_port *port, struct file *filp) +{ + struct usb_serial *serial = port->serial; + int result; + + dbg("%s - port %d", __FUNCTION__, port->number); + + if (serial->dev == NULL) { + dbg("%s - serial->dev = NULL (usb disconnected)", __FUNCTION__ ); + return -ENXIO; + } + + if (cp210x_set_config_single(port, CP210X_UART, UART_ENABLE)) { + err( "%s - Unable to enable UART\n", + __FUNCTION__); + return -EPROTO; + } + + /* Start reading from the device */ + 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, + cp210x_read_bulk_callback, port); + result = usb_submit_urb(port->read_urb); + if (result) { + err( "%s - failed resubmitting read urb, " + "error %d\n", __FUNCTION__, result); + return result; + } + + /* Configure the termios structure */ + cp210x_get_termios(port); + + /* Set the DTR and RTS pins low */ + cp210x_set_config_single(port, CP210X_CONTROL, + CONTROL_DTR | CONTROL_WRITE_DTR | + CONTROL_RTS | CONTROL_WRITE_RTS); + + return 0; +} + +static void cp210x_cleanup (struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + + dbg("%s - port %d", __FUNCTION__, port->number); + + if (serial->dev == NULL) { + dbg("%s - serial->dev = NULL (usb disconnected)", __FUNCTION__ ); + return; + } + + /* shutdown any bulk reads that might be going on */ + if (serial->num_bulk_out) + usb_unlink_urb(port->write_urb); + if (serial->num_bulk_in) + usb_unlink_urb(port->read_urb); +} + +static void cp210x_close (struct usb_serial_port *port, struct file * filp) +{ + struct usb_serial *serial = port->serial; + + dbg("%s - port %d", __FUNCTION__, port->number); + + /* shutdown our urbs */ + dbg("%s - shutting down urbs", __FUNCTION__); + usb_unlink_urb(port->write_urb); + usb_unlink_urb(port->read_urb); + + if (serial->dev == NULL) { + dbg("%s - serial->dev = NULL (usb disconnected)", __FUNCTION__ ); + return; + } + + /* Set the DTR and RTS pins High */ + dbg("%s - cp210x_set_config_single, DTR disabled", __FUNCTION__); + cp210x_set_config_single(port, CP210X_CONTROL, + CONTROL_WRITE_DTR | + CONTROL_WRITE_RTS); + + dbg("%s - cp210x_set_config_single, UART_DISABLE", __FUNCTION__); + cp210x_set_config_single(port, CP210X_UART, UART_DISABLE); +} + +/* + * 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 cp210x_get_termios (struct usb_serial_port *port) +{ + unsigned int cflag, modem_ctl[4]; + int baud; + int bits; + + dbg("%s - port %d", __FUNCTION__, port->number); + + if ((!port->tty) || (!port->tty->termios)) { + dbg("%s - no tty structures", __FUNCTION__); + return; + } + cflag = port->tty->termios->c_cflag; + + cp210x_get_config(port, CP210X_BAUDRATE, &baud, 2); + /* Convert to baudrate */ + if (baud) + baud = BAUD_RATE_GEN_FREQ / baud; + + dbg("%s - baud rate = %d", __FUNCTION__, baud); + cflag &= ~CBAUD; + switch (baud) { + /* + * The baud rates which are commented out below + * appear to be supported by the device + * but are non-standard + */ + case 600: cflag |= B600; break; + case 1200: cflag |= B1200; break; + case 1800: cflag |= B1800; break; + case 2400: cflag |= B2400; break; + case 4800: cflag |= B4800; break; + /*case 7200: cflag |= B7200; break;*/ + case 9600: cflag |= B9600; break; + /*case 14400: cflag |= B14400; break;*/ + case 19200: cflag |= B19200; break; + /*case 28800: cflag |= B28800; break;*/ + case 38400: cflag |= B38400; break; + /*case 55854: cflag |= B55054; break;*/ + case 57600: cflag |= B57600; break; + case 115200: cflag |= B115200; break; + /*case 127117: cflag |= B127117; break;*/ + case 230400: cflag |= B230400; break; + case 460800: cflag |= B460800; break; + case 921600: cflag |= B921600; break; + /*case 3686400: cflag |= B3686400; break;*/ + default: + dbg("%s - Baud rate is not supported, " + "using 9600 baud", __FUNCTION__); + cflag |= B9600; + cp210x_set_config_single(port, CP210X_BAUDRATE, + (BAUD_RATE_GEN_FREQ/9600)); + break; + } + + cp210x_get_config(port, CP210X_BITS, &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; + cp210x_set_config(port, CP210X_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; + cp210x_set_config(port, CP210X_BITS, &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; + cp210x_set_config(port, CP210X_BITS, &bits, 2); + break; + case BITS_PARITY_SPACE: + dbg("%s - parity = SPACE (not supported, " + "disabling parity)", __FUNCTION__); + cflag &= ~PARENB; + bits &= ~BITS_PARITY_MASK; + cp210x_set_config(port, CP210X_BITS, &bits, 2); + break; + default: + dbg("%s - Unknown parity mode, " + "disabling parity", __FUNCTION__); + cflag &= ~PARENB; + bits &= ~BITS_PARITY_MASK; + cp210x_set_config(port, CP210X_BITS, &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; + cp210x_set_config(port, CP210X_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; + cp210x_set_config(port, CP210X_BITS, &bits, 2); + break; + } + + cp210x_get_config(port, CP210X_MODEMCTL, modem_ctl, 16); + if (modem_ctl[0] & 0x0008) { + dbg("%s - flow control = CRTSCTS", __FUNCTION__); + cflag |= CRTSCTS; + } else { + dbg("%s - flow control = NONE", __FUNCTION__); + cflag &= ~CRTSCTS; + } + + port->tty->termios->c_cflag = cflag; +} + +static void cp210x_set_termios (struct usb_serial_port *port, + struct termios *old_termios) +{ + unsigned int cflag, old_cflag=0; + int baud=0, bits; + unsigned int modem_ctl[4]; + + dbg("%s - port %d", __FUNCTION__, port->number); + + if ((!port->tty) || (!port->tty->termios)) { + dbg("%s - no tty structures", __FUNCTION__); + return; + } + cflag = port->tty->termios->c_cflag; + + /* Check that they really want us to change something */ + if (old_termios) { + if ((cflag == old_termios->c_cflag) && + (RELEVANT_IFLAG(port->tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) { + dbg("%s - nothing to change...", __FUNCTION__); + return; + } + + old_cflag = old_termios->c_cflag; + } + + /* If the baud rate is to be updated*/ + if ((cflag & CBAUD) != (old_cflag & CBAUD)) { + switch (cflag & CBAUD) { + /* + * The baud rates which are commented out below + * appear to be supported by the device + * but are non-standard + */ + case B0: baud = 0; break; + case B600: baud = 600; break; + case B1200: baud = 1200; break; + case B1800: baud = 1800; break; + case B2400: baud = 2400; break; + case B4800: baud = 4800; break; + /*case B7200: baud = 7200; break;*/ + case B9600: baud = 9600; break; + /*ase B14400: baud = 14400; break;*/ + case B19200: baud = 19200; break; + /*case B28800: baud = 28800; break;*/ + case B38400: baud = 38400; break; + /*case B55854: baud = 55054; break;*/ + case B57600: baud = 57600; break; + case B115200: baud = 115200; break; + /*case B127117: baud = 127117; break;*/ + case B230400: baud = 230400; break; + case B460800: baud = 460800; break; + case B921600: baud = 921600; break; + /*case B3686400: baud = 3686400; break;*/ + default: + err( "cp210x driver does not " + "support the baudrate requested\n"); + break; + } + + if (baud) { + dbg("%s - Setting baud rate to %d baud", __FUNCTION__, + baud); + if (cp210x_set_config_single(port, CP210X_BAUDRATE, + (BAUD_RATE_GEN_FREQ / baud))) + err( "Baud rate requested not " + "supported by device\n"); + } + } + + /* If the number of data bits is to be updated */ + if ((cflag & CSIZE) != (old_cflag & CSIZE)) { + cp210x_get_config(port, CP210X_BITS, &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: + err( "cp210x driver does not " + "support the number of bits requested," + " using 8 bit mode\n"); + bits |= BITS_DATA_8; + break; + } + if (cp210x_set_config(port, CP210X_BITS, &bits, 2)) + err( "Number of data bits requested " + "not supported by device\n"); + } + + if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))) { + cp210x_get_config(port, CP210X_BITS, &bits, 2); + bits &= ~BITS_PARITY_MASK; + if (cflag & PARENB) { + if (cflag & PARODD) { + bits |= BITS_PARITY_ODD; + dbg("%s - parity = ODD", __FUNCTION__); + } else { + bits |= BITS_PARITY_EVEN; + dbg("%s - parity = EVEN", __FUNCTION__); + } + } + if (cp210x_set_config(port, CP210X_BITS, &bits, 2)) + err( "Parity mode not supported " + "by device\n"); + } + + if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) { + cp210x_get_config(port, CP210X_BITS, &bits, 2); + bits &= ~BITS_STOP_MASK; + if (cflag & CSTOPB) { + bits |= BITS_STOP_2; + dbg("%s - stop bits = 2", __FUNCTION__); + } else { + bits |= BITS_STOP_1; + dbg("%s - stop bits = 1", __FUNCTION__); + } + if (cp210x_set_config(port, CP210X_BITS, &bits, 2)) + err( "Number of stop bits requested " + "not supported by device\n"); + } + + if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) { + cp210x_get_config(port, CP210X_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]); + + if (cflag & CRTSCTS) { + modem_ctl[0] &= ~0x7B; + modem_ctl[0] |= 0x09; + modem_ctl[1] = 0x80; + dbg("%s - flow control = CRTSCTS", __FUNCTION__); + } else { + modem_ctl[0] &= ~0x7B; + modem_ctl[0] |= 0x01; + modem_ctl[1] |= 0x40; + dbg("%s - flow control = NONE", __FUNCTION__); + } + + 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]); + cp210x_set_config(port, CP210X_MODEMCTL, modem_ctl, 16); + } + +} + +static void cp210x_break_ctl (struct usb_serial_port *port, int break_state) +{ + 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"); + cp210x_set_config(port, CP210X_BREAK, &state, 2); +} + +static int cp210x_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) +{ + int status, control, update, retval; + struct usb_serial *serial = port->serial; + + dbg("%s -port %d", __FUNCTION__, port->number); + + if (serial->dev == NULL) { + dbg("%s - serial->dev = NULL (usb disconnected)", __FUNCTION__ ); + return -ENXIO; + } + + retval = cp210x_get_config(port, CP210X_CONTROL, &control, 1); + if (retval) return retval; + + status = ((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); + + if (cmd == TIOCMSET || cmd == TIOCMBIS || cmd == TIOCMBIC) { + if (access_ok(VERIFY_READ, &update, sizeof(int))) { + if (copy_from_user(&update, (unsigned int *)arg, sizeof(int))) { + dbg("%s(TIOCMGET) - copy_from_user 0x%08lX error %d", __FUNCTION__, arg, retval ); + return -EINVAL; + } + } else { + //this is NOT user space, but Kernel space (from another driver) + update = *(int *)arg; + } + } + + switch (cmd) { + case TIOCMGET: + dbg("%s(TIOCMGET) - control = 0x%.2x status = 0x%02X", __FUNCTION__, control, status); + if (access_ok(VERIFY_WRITE, (unsigned int *)arg, sizeof(int))) { + if (copy_to_user((unsigned int *)arg, &status, sizeof(int))) { + dbg("%s(TIOCMGET) - copy_to_user 0x%08lX error %d", __FUNCTION__, arg, retval ); + retval = -EFAULT; + } + } else { + //this is NOT user space, but Kernel space (from another driver) + *(int *)arg = status; + } + break; + + case TIOCMBIS: + if (update & TIOCM_RTS) { + control |= CONTROL_RTS; + control |= CONTROL_WRITE_RTS; + } + if (update & TIOCM_DTR) { + control |= CONTROL_DTR; + control |= CONTROL_WRITE_DTR; + } + + dbg("%s(TIOCMBIS) - control = 0x%.4x", __FUNCTION__, control); + retval = cp210x_set_config(port, CP210X_CONTROL, &control, 2); + break; + + case TIOCMBIC: + if (update & TIOCM_RTS) { + control &= ~CONTROL_RTS; + control |= CONTROL_WRITE_RTS; + } + if (update & TIOCM_DTR) { + control &= ~CONTROL_DTR; + control |= CONTROL_WRITE_DTR; + } + dbg("%s(TIOCMIC) - control = 0x%.4x", __FUNCTION__, control); + retval = cp210x_set_config(port, CP210X_CONTROL, &control, 2); + break; + + case TIOCMSET: + if ( (~status) & update & TIOCM_RTS) { + control |= CONTROL_RTS; + control |= CONTROL_WRITE_RTS; + } + if ( (~status) & update & TIOCM_DTR) { + control |= CONTROL_DTR; + control |= CONTROL_WRITE_DTR; + } + if ( status & (~update) & TIOCM_RTS) { + control &= ~CONTROL_RTS; + control |= CONTROL_WRITE_RTS; + } + if ( status & (~update) & TIOCM_DTR) { + control &= ~CONTROL_DTR; + control |= CONTROL_WRITE_DTR; + } + + dbg("%s(TIOCMSET) - control = 0x%.4x", __FUNCTION__, control); + retval = cp210x_set_config(port, CP210X_CONTROL, &control, 2); + break; + + default: + dbg("%s(0x%.4x) - Unknown ioctl", __FUNCTION__, cmd); + retval = -ENOIOCTLCMD; + break; + + } + + return retval; +} + +static void cp210x_shutdown (struct usb_serial *serial) +{ + struct usb_serial_port *port; + int i; + + dbg("%s usb disconnect", __FUNCTION__); + + // Stop reads and writes on all ports + for (i=0; i < serial->num_ports; ++i) { + port = &serial->port[i]; + if ( port != NULL ) { + cp210x_cleanup( port ); + } + } +} + +static void cp210x_read_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); + struct tty_struct *tty; + unsigned char *data = urb->transfer_buffer; + int i, result; + + if (port_paranoia_check(port, __FUNCTION__)) + return; + + dbg("%s - port %d", __FUNCTION__, port->number); + + if (!serial) { + dbg("%s - bad serial pointer, exiting", __FUNCTION__); + return; + } + + if (urb->status) { + dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + return; + } + + usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); + + tty = port->tty; + if (tty && urb->actual_length) { + for (i = 0; i < urb->actual_length ; ++i) { + /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */ + if(tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty_flip_buffer_push(tty); + } + /* this doesn't actually push the data through unless tty->low_latency is set */ + tty_insert_flip_char(tty, data[i], 0); + } + tty_flip_buffer_push(tty); + } + + if (serial->dev == NULL) { + dbg("%s - serial->dev = NULL (usb disconnected)", __FUNCTION__ ); + return; + } + + /* Continue trying to always read */ + 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, + cp210x_read_bulk_callback, port); + result = usb_submit_urb(port->read_urb); + if (result) + err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); + return; +} + +static void cp210x_write_gather(struct usb_serial_port *port) +{ + struct cp210x_private *priv = (struct cp210x_private *)port->private; + struct usb_serial *serial = port->serial; + int count, room; + struct cp210x_packet *pkt; + struct urb *urb = port->write_urb; + struct list_head *tmp; + + if (serial->dev == NULL) { + dbg("%s - serial->dev = NULL (usb disconnected)", __FUNCTION__ ); + return; + } + + if (urb->status == -EINPROGRESS) { + /* Should never happen */ + err("%s - flushing while urb is active !", __FUNCTION__); + return; + } + room = URBDATA_SIZE; + for (tmp = priv->queue.next; tmp != &priv->queue;) { + pkt = list_entry(tmp, struct cp210x_packet, list); + tmp = tmp->next; + count = min(room, (int)(pkt->len - pkt->written)); + memcpy(urb->transfer_buffer + (URBDATA_SIZE - room), + pkt->data + pkt->written, count); + room -= count; + pkt->written += count; + priv->queue_len -= count; + if (pkt->written == pkt->len) { + list_del(&pkt->list); + list_add(&pkt->list, &priv->freelist); + priv->free_len += PACKET_SIZE; + } + if (room == 0) { + break; + } + } + + count = URBDATA_SIZE - room; + FILL_BULK_URB(port->write_urb, serial->dev, + usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, count, cp210x_write_bulk_callback, + port); + return; +} + +static void cp210x_write_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct cp210x_private *priv = (struct cp210x_private *)port->private; + struct usb_serial *serial = port->serial; + unsigned long flags; + int result; + + if (port_paranoia_check (port, __FUNCTION__)) { + return; + } + + dbg("%s - port %d", __FUNCTION__, port->number); + + if ( serial->dev == NULL) { + dbg("%s - port->serial->dev = NULL (usb disconnected)", __FUNCTION__ ); + return; + } + + if (urb->status) { + dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + } + + spin_lock_irqsave(&write_list_lock, flags); + if (!list_empty(&priv->queue)) { + cp210x_write_gather(port); + spin_unlock_irqrestore(&write_list_lock, flags); + result = usb_submit_urb(port->write_urb); + if (result) { + err("%s - failed submitting write urb, error %d", __FUNCTION__, result); + } + } else { + priv->active = 0; + spin_unlock_irqrestore(&write_list_lock, flags); + } + queue_task(&port->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + return; +} + +static int __init cp210x_init (void) +{ + int retval; + + spin_lock_init(&write_list_lock); + if (vendor) { + id_table[0].idVendor = vendor; + id_table[0].idProduct = product; + } + + retval = usb_serial_register(&cp210x_device); + if (retval) + return retval; /* Failed to register */ + + /* Success */ + info(DRIVER_DESC " " DRIVER_VERSION); + return 0; +} + +static void __exit cp210x_exit (void) +{ + usb_serial_deregister (&cp210x_device); +} + +module_init(cp210x_init); +module_exit(cp210x_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); + +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Enable verbose debug messages"); + +MODULE_PARM(vendor, "h"); +MODULE_PARM_DESC(vendor, "User specified USB idVendor"); + +MODULE_PARM(product, "h"); +MODULE_PARM_DESC(product, "User specified USB idProduct"); diff -Nru a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile --- a/drivers/usb/serial/Makefile 2010-02-12 16:41:11.000000000 +0000 +++ b/drivers/usb/serial/Makefile 2010-02-12 16:41:39.000000000 +0000 @@ -15,6 +15,7 @@ obj-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda.o obj-$(CONFIG_USB_SERIAL_KEYSPAN) += keyspan.o obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o +obj-$(CONFIG_USB_SERIAL_CP210X) += cp210x.o obj-$(CONFIG_USB_SERIAL_DIGI_ACCELEPORT) += digi_acceleport.o obj-$(CONFIG_USB_SERIAL_BELKIN) += belkin_sa.o obj-$(CONFIG_USB_SERIAL_EMPEG) += empeg.o