]> oss.titaniummirror.com Git - cp210x.git/commitdiff
Modify package so the resultant module is named cp210x instead of cp2101.
authorsmckown <smckown@986fd584-583e-0410-b54d-b9fe63dff8e5>
Wed, 25 Nov 2009 20:10:53 +0000 (20:10 +0000)
committersmckown <smckown@986fd584-583e-0410-b54d-b9fe63dff8e5>
Wed, 25 Nov 2009 20:10:53 +0000 (20:10 +0000)
This removes the ambiguity associated with the native kernel driver module
name of cp2101.

Makefile
Makefile.dkms
README
debian.in/prerm
src/Makefile
src/Makefile.go
src/Makefile24
src/Makefile26
src/cp2101.c [deleted file]
src/cp210x.c [new file with mode: 0644]

index c05566d75e15c9169b7c5c542d7fed1207cc5849..acaa73809db7abd5f1a46232107af00cea78594d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,9 +4,9 @@
 
 # Change or override these values as necessary for each package release
 
 
 # Change or override these values as necessary for each package release
 
-PKGVER  = 0.11
+PKGVER  = 0.11.1
 DEBDIST = hardy
 DEBDIST = hardy
-DEBRELEASE = 0tmi
+DEBRELEASE = 1tmi
 
 
 # No need to change anything below this line
 
 
 # No need to change anything below this line
index c84885b2887fa0cab4c0b1c83c187c853f1d6a92..b8de4d42c6a79fdb7d04e8efc846b8a0fb18f2c5 100755 (executable)
@@ -6,7 +6,6 @@ all:
        ./configure -kver $(KVER)
        test -f Makefile.config
        $(MAKE) -f Makefile.go
        ./configure -kver $(KVER)
        test -f Makefile.config
        $(MAKE) -f Makefile.go
-       cp cp2101.ko cp210x.ko
        $(STRIP) cp210x.ko
 
 clean:
        $(STRIP) cp210x.ko
 
 clean:
diff --git a/README b/README
index 0c1b0f738f32ec9f044e9fa64243e42268f5c267..da76bda781158a3bc45b1071081a7791f1c7f289 100644 (file)
--- a/README
+++ b/README
@@ -23,14 +23,14 @@ Build a .deb locally is quite a bit simpler and faster:
 * # To install locally, FIRST REMOVE all cp210x-module packages installed. Then,
 * sudo dpkg -i build/cp210x-module*.deb
 
 * # To install locally, FIRST REMOVE all cp210x-module packages installed. Then,
 * sudo dpkg -i build/cp210x-module*.deb
 
-The cp2101.ko file can be built and installed from the source directory as
+The cp210x.ko file can be built and installed from the source directory as
 well, but this isn't recommended:
 
 * Check out the code
 * cd cp210x/src
 * ./configure -kver $(uname -r)
 * make -f Makefile.go all
 well, but this isn't recommended:
 
 * Check out the code
 * cd cp210x/src
 * ./configure -kver $(uname -r)
 * make -f Makefile.go all
-* # The binary is called cp2101.ko
+* # The binary is called cp210x.ko
 * # To install,
 * chmod a+x installmod
 * sudo make -f Makefile.go install
 * # To install,
 * chmod a+x installmod
 * sudo make -f Makefile.go install
@@ -39,7 +39,7 @@ well, but this isn't recommended:
 
 Older kernels are supported, but require one to check out the pre2.6.24
 branch from the repo, then follow any of the steps above.  You may have to
 
 Older kernels are supported, but require one to check out the pre2.6.24
 branch from the repo, then follow any of the steps above.  You may have to
-edit cp2101.c and replace:
+edit cp210x.c and replace:
        #include "usb-serial.h"
 with:
        #include "/path/to/usb-serial.h"
        #include "usb-serial.h"
 with:
        #include "/path/to/usb-serial.h"
index d685190fe7adc391ad87cf3078a5e7b9ca7663fb..e7a02eca309effe5dfda2ca8d56004df45f338bf 100755 (executable)
@@ -7,7 +7,6 @@ set -e
 
 case "$1" in
     remove|upgrade|deconfigure)
 
 case "$1" in
     remove|upgrade|deconfigure)
-      rmmod cp2101 2>/dev/null || true
       rmmod xxROOTNAMExx 2>/dev/null || true
       if [  "`dkms status -m $NAME`" ]; then
          dkms remove -m $NAME -v $VERSION --all
       rmmod xxROOTNAMExx 2>/dev/null || true
       if [  "`dkms status -m $NAME`" ]; then
          dkms remove -m $NAME -v $VERSION --all
index 5d5dbb8028764b886bc49ece9e2ab412de2014f5..4aa7c859cba1cac3acba73a645b6ccbdd4981dcd 100644 (file)
@@ -1,4 +1,4 @@
-# CP2101 Makefile
+# CP210x Makefile
 
 ifeq ("$(src)", "")
        include Makefile.config
 
 ifeq ("$(src)", "")
        include Makefile.config
index 407680ccb0d109c953323147e6de376bd50b23dd..90912fe5ff316f87d77701ea648b79cb10a023b0 100644 (file)
 # mrproper  - remove emacs debris
 #
 
 # mrproper  - remove emacs debris
 #
 
-OBJS = cp2101.o
+OBJS = cp210x.o
 
 
-SRC = cp2102.c
+SRC = cp210x.c
 
 
 include ./Makefile.config
 
 KVER=$(KVER1).$(KVER2).$(KVER3)
 
 
 include ./Makefile.config
 
 KVER=$(KVER1).$(KVER2).$(KVER3)
-MODULE=cp2101$(EXT)
+MODULE=cp210x$(EXT)
 MODULES_DIR=$(INSTALL_PREFIX)/lib/modules
 MODULES_VER_DIR=$(MODULES_DIR)/$(KVER)
 
 MODULES_DIR=$(INSTALL_PREFIX)/lib/modules
 MODULES_VER_DIR=$(MODULES_DIR)/$(KVER)
 
index 4a19ad3a1603fe5e209a510b77a87d698020f6b7..58cf9b612d3228704d52a0f88e96128b184163f9 100644 (file)
@@ -2,6 +2,6 @@
 
 EXTRA_CFLAGS += -I$(LINUX_KERNEL)/drivers/usb/serial
 
 
 EXTRA_CFLAGS += -I$(LINUX_KERNEL)/drivers/usb/serial
 
-obj-m += cp2101.o
+obj-m += cp210x.o
 
 include $(TOPDIR)/Rules.make
 
 include $(TOPDIR)/Rules.make
index 2456377412af67d0b1b02c6af961588cb3d02a2b..a3b6425868b8b90d62e635f793a66b0b336144a7 100644 (file)
@@ -2,6 +2,6 @@
 
 EXTRA_CFLAGS += -I$(LINUX_INCLUDE) -I$(LINUX_KERNEL)/drivers/usb/serial -DLINUX26
 
 
 EXTRA_CFLAGS += -I$(LINUX_INCLUDE) -I$(LINUX_KERNEL)/drivers/usb/serial -DLINUX26
 
-obj-m := cp2101.o
+obj-m := cp210x.o
 
 
-cp2101-objs := $(OBJS)
+cp210x-objs := $(OBJS)
diff --git a/src/cp2101.c b/src/cp2101.c
deleted file mode 100644 (file)
index 7ad61da..0000000
+++ /dev/null
@@ -1,1289 +0,0 @@
-/*
- * Silicon Laboratories CP2101/CP2102/CP2103 USB to RS232 serial adaptor driver
- *
- * Copyright (C) 2005 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
- *
- * 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 <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/usb.h>
-#include <asm/uaccess.h>
-#include <linux/usb/serial.h>
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v0.11rsm"
-#define DRIVER_DESC "Silicon Labs CP2101/CP2102/CP2103 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 *,
-               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 [] = {
-       { USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */
-       { USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */
-       { USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */
-       { 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, 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, 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, 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, 0x8341) }, /* Siemens MC35PU GPRS Modem */
-       { USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */
-       { 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(0x13AD, 0x9999) }, /* Baltech card reader */
-       { 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 */
-       { } /* Terminating Entry */
-};
-
-MODULE_DEVICE_TABLE (usb, id_table);
-
-static struct usb_driver cp2101_driver = {
-       .name           = "cp2101",
-       .probe          = usb_serial_probe,
-       .disconnect     = usb_serial_disconnect,
-       .id_table       = id_table,
-       .no_dynamic_id  =       1,
-};
-
-static struct usb_serial_driver cp2101_device = {
-       .driver = {
-               .owner =        THIS_MODULE,
-               .name =         "cp2101",
-       },
-       .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,
-};
-
-/* 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 UART_ENABLE            0x0001
-#define UART_DISABLE           0x0000
-
-/* CP2101_BAUDRATE */
-#define BAUD_RATE_GEN_FREQ     0x384000
-
-/* CP2101_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
-
-/* CP2101_BREAK */
-#define BREAK_ON               0x0000
-#define BREAK_OFF              0x0001
-
-/* CP2101_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
-
-/* 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 */
-
-/*
- * 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.
- */
-size_t copy_buf_from_user(char *kbuf, unsigned long ubuf, size_t klen)
-{
-       cp2101_buffer_t t;
-
-       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;
-}
-
-/*
- * cp2101_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)
-{
-       struct usb_device *dev = port->serial->dev;
-       u8 *tbuf;
-       int ret;
-
-       if (!(tbuf = kmalloc(size, GFP_KERNEL)))
-               return -ENOMEM;
-       if (requestype & 0x80) {
-               ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
-                               requestype, value, index, tbuf, size, 300);
-               if (ret > 0 && size)
-                       memcpy(data, tbuf, size);
-       } else {
-               if (size)
-                       memcpy(tbuf, data, size);
-               ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
-                               requestype, value, index, tbuf, size, 300);
-       }
-       kfree(tbuf);
-       if (ret < 0 && ret != -EPIPE) {
-               dev_printk(KERN_DEBUG, &dev->dev, "cp2101: control 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)
-{
-       dbg("%s", __FUNCTION__);
-
-#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...
-        */
-       cp2101_ctlmsg(port, 0xff, 0x40, 0x0008, 0x00, 0, 0);
-#else
-       usb_reset_device(port->serial->dev);
-#endif
-       return 0;
-}
-
-static int cp2101_get_partnum(struct usb_serial_port *port)
-{
-       static u8 _partnum = 0;
-       int ret = CP210x_SUCCESS;
-       if (!_partnum) {
-               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;
-               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;
-       }
-       dbg("%s - partnum %u err %d", __FUNCTION__, _partnum, ret);
-       return (ret == CP210x_SUCCESS) ? _partnum : 0;
-}
-
-inline int cp2101_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;
-}
-
-/*
- * cp2101_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)
-{
-       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);
-       return ret;
-}
-
-/* Set all gpio simultaneously */
-static int cp2101_gpioset(struct usb_serial_port *port, u8 gpio)
-{
-       dbg("%s - port %d, gpio = 0x%.2x", __FUNCTION__, port->number, gpio);
-
-       return cp2101_ctlmsg(port, 0xff, 0x40, 0x37e1,
-                       ((uint16_t)gpio << 8) | GPIO_MASK, 0, 0);
-}
-
-/* Set select gpio bits */
-static int cp2101_gpiosetb(struct usb_serial_port *port, u8 set, u8 clear)
-{
-       u16 gpio = 0;
-
-       /* The bitmask is in the LSB, the values in the MSB */
-       if (set & GPIO_0)
-               gpio |= (GPIO_0 << 8)|GPIO_0;
-       if (set & GPIO_1)
-               gpio |= (GPIO_1 << 8)|GPIO_1;
-       if (set & GPIO_2)
-               gpio |= (GPIO_2 << 8)|GPIO_2;
-       if (set & GPIO_3)
-               gpio |= (GPIO_3 << 8)|GPIO_3;
-       if (clear & GPIO_0)
-               gpio = (gpio & ~(GPIO_0 << 8))|GPIO_0;
-       if (clear & GPIO_1)
-               gpio = (gpio & ~(GPIO_1 << 8))|GPIO_1;
-       if (clear & GPIO_2)
-               gpio = (gpio & ~(GPIO_2 << 8))|GPIO_2;
-       if (clear & GPIO_3)
-               gpio = (gpio & ~(GPIO_3 << 8))|GPIO_3;
-
-       dbg("%s - port %d, gpiob = 0x%.4x", __FUNCTION__, port->number, gpio);
-
-       /* FIXME: how about REQTYPE_HOST_TO_DEVICE instead of 0x40? */
-       return cp2101_ctlmsg(port, 0xff, 0x40, 0x37e1, gpio, 0, 0);
-}
-
-static int cp2101_gpioget(struct usb_serial_port *port, u8* gpio)
-{
-       int ret;
-
-       dbg("%s - port %d", __FUNCTION__, port->number);
-
-       /* FIXME: how about REQTYPE_DEVICE_TO_HOST instead of 0xc0? */
-       ret = cp2101_ctlmsg(port, 0xff, 0xc0, 0x00c2, 0, gpio, 1);
-
-       dbg("%s - gpio = 0x%.2x (%d)", __FUNCTION__, *gpio, ret);
-
-       return (ret == 1) ? 0 : -1;
-}
-
-static int cp2101_portconfset(struct usb_serial_port *port,
-               cp2101_port_config_t* config)
-{
-       cp2101_port_config_t lconfig;
-       int ret;
-
-       dbg("%s", __FUNCTION__);
-
-       memcpy(&lconfig, config, sizeof(lconfig));
-
-       /* apparently not implemented yet */
-       lconfig.suspend.lowPower = 0;
-       lconfig.reset.lowPower = 0;
-
-       /* Words from cp2103 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);
-
-       ret = cp2101_ctlmsg(port, 0xff, 0x40, 0x370c, 0, &lconfig,
-                       PORT_CONFIG_LEN);
-       if (ret == PORT_CONFIG_LEN)
-               return 0;
-       else if (ret >= 0)
-               return -1;
-       else
-               return ret;
-}
-
-static int cp2101_portconfget(struct usb_serial_port *port,
-               cp2101_port_config_t* config)
-{
-       int ret;
-
-       dbg("%s", __FUNCTION__);
-
-       ret = cp2101_ctlmsg(port, 0xff, 0xc0, 0x370c, 0, config,
-                       PORT_CONFIG_LEN);
-       if (ret == PORT_CONFIG_LEN) {
-               /* Words from cp2103 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);
-               config->suspend.mode = be16_to_cpu(config->suspend.mode);
-               config->suspend.lowPower =
-                               be16_to_cpu(config->suspend.lowPower);
-               config->suspend.latch = be16_to_cpu(config->suspend.latch);
-
-               /* apparently not implemented yet */
-               config->reset.lowPower = 0;
-               config->suspend.lowPower = 0;
-
-               return 0;
-       } else if (ret >= 0)
-               return -1;
-       else
-               return ret;
-
-}
-
-/*
- * cp2101_get_config
- * Reads from the CP2101 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,
-               unsigned int *data, int size)
-{
-       struct usb_serial *serial = port->serial;
-       __le32 *buf;
-       int result, i, length;
-
-       /* Number of integers required to contain the array */
-       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__);
-               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);
-
-       /* Convert data into an array of integers */
-       for (i=0; i<length; i++)
-               data[i] = le32_to_cpu(buf[i]);
-
-       kfree(buf);
-
-       if (result != size) {
-               dev_err(&port->dev, "%s - Unable to send config request, "
-                               "request=0x%x size=%d result=%d\n",
-                               __FUNCTION__, request, size, result);
-               return -EPROTO;
-       }
-
-       return 0;
-}
-
-/*
- * cp2101_set_config
- * Writes to the CP2101 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,
-               unsigned int *data, int size)
-{
-       struct usb_serial *serial = port->serial;
-       __le32 *buf;
-       int result, i, length;
-
-       /* Number of integers required to contain the array */
-       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__);
-               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) {
-               dev_err(&port->dev, "%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;
-}
-
-/*
- * cp2101_set_config_single
- * Convenience function for calling cp2101_set_config on single data values
- * without requiring an integer pointer
- */
-static inline int cp2101_set_config_single(struct usb_serial_port* port,
-               u8 request, unsigned int data)
-{
-       return cp2101_set_config(port, request, &data, 2);
-}
-
-static int cp2101_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 (cp2101_set_config_single(port, CP2101_UART, UART_ENABLE)) {
-               dev_err(&port->dev, "%s - Unable to enable UART\n",
-                               __FUNCTION__);
-               return -EPROTO;
-       }
-
-       /* 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);
-       if (result) {
-               dev_err(&port->dev, "%s - failed resubmitting read urb, "
-                               "error %d\n", __FUNCTION__, result);
-               return result;
-       }
-
-       /* Configure the termios structure */
-       cp2101_get_termios(port);
-
-       /* Set the DTR and RTS pins low */
-       cp2101_tiocmset(port, NULL, TIOCM_DTR | TIOCM_RTS, 0);
-
-       return 0;
-}
-
-static void cp2101_cleanup (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);
-       }
-}
-
-static void cp2101_close (struct usb_serial_port *port, struct file * filp)
-{
-       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);
-
-       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);
-
-       switch (cmd) {
-               
-       case TIOCMGET:
-       {
-               int result = cp2101_tiocmget(port, file);
-               if (copy_to_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)))
-                       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))
-                       return -EFAULT;
-               return 0;
-       }
-       break;
-
-       case IOCTL_GPIOGET:
-       if (cp2101_get_partnum(port) == CP210x_CP2103_VERSION) {
-               u8 gpio = 0;
-               if (!cp2101_gpioget(port, &gpio) && !copy_to_user((u8*)arg,
-                                       &gpio, sizeof(gpio)))
-                       return 0;
-       }
-       return -EFAULT;
-       break;
-
-       case IOCTL_GPIOSET:
-       if (cp2101_get_partnum(port) == CP210x_CP2103_VERSION &&
-                       !cp2101_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))
-               return 0;
-       return -EFAULT;
-       break;
-
-       case IOCTL_DEVICERESET:
-       return cp2101_reset(port);
-       break;
-
-       case IOCTL_PORTCONFGET:
-       {
-               cp2101_port_config_t config;
-               if (!cp2101_portconfget(port, &config) && !copy_to_user(
-                                       (cp2101_port_config_t*)arg, &config,
-                                       sizeof(config)))
-                       return 0;
-       }
-       return -EFAULT;
-       break;
-
-       case IOCTL_PORTCONFSET:
-       {
-       cp2101_port_config_t config;
-       if (!copy_from_user(&config, (cp2101_port_config_t*)arg,
-                       sizeof(config)) &&
-                       !cp2101_portconfset(port, &config))
-               return 0;
-       return -EFAULT;
-       }
-       break;
-
-       case IOCTL_SETVID:
-       {
-               unsigned int vid;
-               if (!copy_from_user(&vid, (unsigned int *)arg,
-                               sizeof(unsigned int)) &&
-                               !cp2101_setu16(port, 0x01, vid))
-                       return 0;
-               return -EFAULT;
-       }
-       break;
-
-       case IOCTL_SETPID:
-       {
-               unsigned int pid;
-               if (!copy_from_user(&pid, (unsigned int *)arg,
-                               sizeof(unsigned int)) &&
-                               !cp2101_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 */
-       {
-               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)
-                       return 0;
-               return -EFAULT;
-       }
-#endif
-       break;
-
-       case IOCTL_SETPRODUCT:
-       {
-               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)
-                       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:
-       {
-               unsigned int ver;
-               if (!copy_from_user(&ver, (unsigned int *)arg,
-                               sizeof(unsigned int)) &&
-                               !cp2101_setu16(port, 0x07, ver))
-                       return 0;
-               return -EFAULT;
-       }
-       break;
-
-       default:
-       dbg("%s not supported = 0x%04x", __FUNCTION__, cmd);
-       break;
-       }
-
-       return -ENOIOCTLCMD;
-}
-
-/*
- * cp2101_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)
-{
-       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;
-       }
-
-       cp2101_get_config(port, CP2101_BAUDRATE, &baud, 2);
-       /* Convert to baudrate */
-       if (baud)
-               baud = BAUD_RATE_GEN_FREQ / baud;
-
-       dbg("%s - baud rate = %d", __FUNCTION__, baud);
-
-       tty_encode_baud_rate(port->tty, baud, baud);
-       cflag = port->tty->termios->c_cflag;
-
-       cp2101_get_config(port, CP2101_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;
-                       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_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;
-       }
-
-       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;
-       }
-
-       cp2101_get_config(port, CP2101_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 cp2101_set_termios (struct usb_serial_port *port,
-               struct ktermios *old_termios)
-{
-       unsigned int cflag, old_cflag;
-       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;
-       }
-       port->tty->termios->c_cflag &= ~CMSPAR;
-
-       cflag = port->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 the number of data bits is to be updated */
-       if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
-               cp2101_get_config(port, CP2101_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:
-                               dev_err(&port->dev, "cp2101 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 ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))) {
-               cp2101_get_config(port, CP2101_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 (cp2101_set_config(port, CP2101_BITS, &bits, 2))
-                       dev_err(&port->dev, "Parity mode not supported "
-                                       "by device\n");
-       }
-
-       if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
-               cp2101_get_config(port, CP2101_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 (cp2101_set_config(port, CP2101_BITS, &bits, 2))
-                       dev_err(&port->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]);
-
-               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]);
-               cp2101_set_config(port, CP2101_MODEMCTL, modem_ctl, 16);
-       }
-
-}
-
-static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file,
-               unsigned int set, unsigned int clear)
-{
-       int control = 0;
-
-       dbg("%s - port %d", __FUNCTION__, port->number);
-
-       if (set & TIOCM_RTS) {
-               control |= CONTROL_RTS;
-               control |= CONTROL_WRITE_RTS;
-       }
-       if (set & TIOCM_DTR) {
-               control |= CONTROL_DTR;
-               control |= CONTROL_WRITE_DTR;
-       }
-       if (clear & TIOCM_RTS) {
-               control &= ~CONTROL_RTS;
-               control |= CONTROL_WRITE_RTS;
-       }
-       if (clear & TIOCM_DTR) {
-               control &= ~CONTROL_DTR;
-               control |= CONTROL_WRITE_DTR;
-       }
-
-       dbg("%s - control = 0x%.4x", __FUNCTION__, control);
-
-       return cp2101_set_config(port, CP2101_CONTROL, &control, 2);
-
-}
-
-static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file)
-{
-       int control, result;
-
-       dbg("%s - port %d", __FUNCTION__, port->number);
-
-       cp2101_get_config(port, CP2101_CONTROL, &control, 1);
-
-       result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0)
-               |((control & CONTROL_RTS) ? TIOCM_RTS : 0)
-               |((control & CONTROL_CTS) ? TIOCM_CTS : 0)
-               |((control & CONTROL_DSR) ? TIOCM_DSR : 0)
-               |((control & CONTROL_RING)? TIOCM_RI  : 0)
-               |((control & CONTROL_DCD) ? TIOCM_CD  : 0);
-
-       dbg("%s - control = 0x%.2x", __FUNCTION__, control);
-
-       return result;
-}
-
-static void cp2101_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");
-       cp2101_set_config(port, CP2101_BREAK, &state, 2);
-}
-
-static int cp2101_startup (struct usb_serial *serial)
-{
-       /* CP2101 buffers behave strangely unless device is reset */
-       usb_reset_device(serial->dev);
-       return 0;
-}
-
-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]);
-       }
-}
-
-static int __init cp2101_init (void)
-{
-       int retval;
-
-       retval = usb_serial_register(&cp2101_device);
-       if (retval)
-               return retval; /* Failed to register */
-
-       retval = usb_register(&cp2101_driver);
-       if (retval) {
-               /* Failed to register */
-               usb_serial_deregister(&cp2101_device);
-               return retval;
-       }
-
-       /* Success */
-       info(DRIVER_DESC " " DRIVER_VERSION);
-       return 0;
-}
-
-static void __exit cp2101_exit (void)
-{
-       usb_deregister (&cp2101_driver);
-       usb_serial_deregister (&cp2101_device);
-}
-
-module_init(cp2101_init);
-module_exit(cp2101_exit);
-
-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");
diff --git a/src/cp210x.c b/src/cp210x.c
new file mode 100644 (file)
index 0000000..0df92df
--- /dev/null
@@ -0,0 +1,1289 @@
+/*
+ * Silicon Laboratories CP2101/CP2102/CP2103 USB to RS232 serial adaptor driver
+ *
+ * Copyright (C) 2005 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
+ *
+ * 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 <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+#include <linux/usb/serial.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.11tmi"
+#define DRIVER_DESC "Silicon Labs CP2101/CP2102/CP2103 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 *,
+               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 [] = {
+       { USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */
+       { USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */
+       { USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */
+       { 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, 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, 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, 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, 0x8341) }, /* Siemens MC35PU GPRS Modem */
+       { USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */
+       { 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(0x13AD, 0x9999) }, /* Baltech card reader */
+       { 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 */
+       { } /* Terminating Entry */
+};
+
+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,
+};
+
+static struct usb_serial_driver cp2101_device = {
+       .driver = {
+               .owner =        THIS_MODULE,
+               .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,
+};
+
+/* 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 UART_ENABLE            0x0001
+#define UART_DISABLE           0x0000
+
+/* CP2101_BAUDRATE */
+#define BAUD_RATE_GEN_FREQ     0x384000
+
+/* CP2101_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
+
+/* CP2101_BREAK */
+#define BREAK_ON               0x0000
+#define BREAK_OFF              0x0001
+
+/* CP2101_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
+
+/* 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 */
+
+/*
+ * 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.
+ */
+size_t copy_buf_from_user(char *kbuf, unsigned long ubuf, size_t klen)
+{
+       cp2101_buffer_t t;
+
+       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;
+}
+
+/*
+ * cp2101_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)
+{
+       struct usb_device *dev = port->serial->dev;
+       u8 *tbuf;
+       int ret;
+
+       if (!(tbuf = kmalloc(size, GFP_KERNEL)))
+               return -ENOMEM;
+       if (requestype & 0x80) {
+               ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
+                               requestype, value, index, tbuf, size, 300);
+               if (ret > 0 && size)
+                       memcpy(data, tbuf, size);
+       } else {
+               if (size)
+                       memcpy(tbuf, data, size);
+               ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
+                               requestype, value, index, tbuf, size, 300);
+       }
+       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);
+       }
+       return ret;
+}
+
+static int cp2101_reset(struct usb_serial_port *port)
+{
+       dbg("%s", __FUNCTION__);
+
+#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...
+        */
+       cp2101_ctlmsg(port, 0xff, 0x40, 0x0008, 0x00, 0, 0);
+#else
+       usb_reset_device(port->serial->dev);
+#endif
+       return 0;
+}
+
+static int cp2101_get_partnum(struct usb_serial_port *port)
+{
+       static u8 _partnum = 0;
+       int ret = CP210x_SUCCESS;
+       if (!_partnum) {
+               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;
+               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;
+       }
+       dbg("%s - partnum %u err %d", __FUNCTION__, _partnum, ret);
+       return (ret == CP210x_SUCCESS) ? _partnum : 0;
+}
+
+inline int cp2101_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;
+}
+
+/*
+ * cp2101_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)
+{
+       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);
+       return ret;
+}
+
+/* Set all gpio simultaneously */
+static int cp2101_gpioset(struct usb_serial_port *port, u8 gpio)
+{
+       dbg("%s - port %d, gpio = 0x%.2x", __FUNCTION__, port->number, gpio);
+
+       return cp2101_ctlmsg(port, 0xff, 0x40, 0x37e1,
+                       ((uint16_t)gpio << 8) | GPIO_MASK, 0, 0);
+}
+
+/* Set select gpio bits */
+static int cp2101_gpiosetb(struct usb_serial_port *port, u8 set, u8 clear)
+{
+       u16 gpio = 0;
+
+       /* The bitmask is in the LSB, the values in the MSB */
+       if (set & GPIO_0)
+               gpio |= (GPIO_0 << 8)|GPIO_0;
+       if (set & GPIO_1)
+               gpio |= (GPIO_1 << 8)|GPIO_1;
+       if (set & GPIO_2)
+               gpio |= (GPIO_2 << 8)|GPIO_2;
+       if (set & GPIO_3)
+               gpio |= (GPIO_3 << 8)|GPIO_3;
+       if (clear & GPIO_0)
+               gpio = (gpio & ~(GPIO_0 << 8))|GPIO_0;
+       if (clear & GPIO_1)
+               gpio = (gpio & ~(GPIO_1 << 8))|GPIO_1;
+       if (clear & GPIO_2)
+               gpio = (gpio & ~(GPIO_2 << 8))|GPIO_2;
+       if (clear & GPIO_3)
+               gpio = (gpio & ~(GPIO_3 << 8))|GPIO_3;
+
+       dbg("%s - port %d, gpiob = 0x%.4x", __FUNCTION__, port->number, gpio);
+
+       /* FIXME: how about REQTYPE_HOST_TO_DEVICE instead of 0x40? */
+       return cp2101_ctlmsg(port, 0xff, 0x40, 0x37e1, gpio, 0, 0);
+}
+
+static int cp2101_gpioget(struct usb_serial_port *port, u8* gpio)
+{
+       int ret;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       /* FIXME: how about REQTYPE_DEVICE_TO_HOST instead of 0xc0? */
+       ret = cp2101_ctlmsg(port, 0xff, 0xc0, 0x00c2, 0, gpio, 1);
+
+       dbg("%s - gpio = 0x%.2x (%d)", __FUNCTION__, *gpio, ret);
+
+       return (ret == 1) ? 0 : -1;
+}
+
+static int cp2101_portconfset(struct usb_serial_port *port,
+               cp2101_port_config_t* config)
+{
+       cp2101_port_config_t lconfig;
+       int ret;
+
+       dbg("%s", __FUNCTION__);
+
+       memcpy(&lconfig, config, sizeof(lconfig));
+
+       /* apparently not implemented yet */
+       lconfig.suspend.lowPower = 0;
+       lconfig.reset.lowPower = 0;
+
+       /* Words from cp2103 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);
+
+       ret = cp2101_ctlmsg(port, 0xff, 0x40, 0x370c, 0, &lconfig,
+                       PORT_CONFIG_LEN);
+       if (ret == PORT_CONFIG_LEN)
+               return 0;
+       else if (ret >= 0)
+               return -1;
+       else
+               return ret;
+}
+
+static int cp2101_portconfget(struct usb_serial_port *port,
+               cp2101_port_config_t* config)
+{
+       int ret;
+
+       dbg("%s", __FUNCTION__);
+
+       ret = cp2101_ctlmsg(port, 0xff, 0xc0, 0x370c, 0, config,
+                       PORT_CONFIG_LEN);
+       if (ret == PORT_CONFIG_LEN) {
+               /* Words from cp2103 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);
+               config->suspend.mode = be16_to_cpu(config->suspend.mode);
+               config->suspend.lowPower =
+                               be16_to_cpu(config->suspend.lowPower);
+               config->suspend.latch = be16_to_cpu(config->suspend.latch);
+
+               /* apparently not implemented yet */
+               config->reset.lowPower = 0;
+               config->suspend.lowPower = 0;
+
+               return 0;
+       } else if (ret >= 0)
+               return -1;
+       else
+               return ret;
+
+}
+
+/*
+ * cp2101_get_config
+ * Reads from the CP2101 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,
+               unsigned int *data, int size)
+{
+       struct usb_serial *serial = port->serial;
+       __le32 *buf;
+       int result, i, length;
+
+       /* Number of integers required to contain the array */
+       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__);
+               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);
+
+       /* Convert data into an array of integers */
+       for (i=0; i<length; i++)
+               data[i] = le32_to_cpu(buf[i]);
+
+       kfree(buf);
+
+       if (result != size) {
+               dev_err(&port->dev, "%s - Unable to send config request, "
+                               "request=0x%x size=%d result=%d\n",
+                               __FUNCTION__, request, size, result);
+               return -EPROTO;
+       }
+
+       return 0;
+}
+
+/*
+ * cp2101_set_config
+ * Writes to the CP2101 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,
+               unsigned int *data, int size)
+{
+       struct usb_serial *serial = port->serial;
+       __le32 *buf;
+       int result, i, length;
+
+       /* Number of integers required to contain the array */
+       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__);
+               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) {
+               dev_err(&port->dev, "%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;
+}
+
+/*
+ * cp2101_set_config_single
+ * Convenience function for calling cp2101_set_config on single data values
+ * without requiring an integer pointer
+ */
+static inline int cp2101_set_config_single(struct usb_serial_port* port,
+               u8 request, unsigned int data)
+{
+       return cp2101_set_config(port, request, &data, 2);
+}
+
+static int cp2101_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 (cp2101_set_config_single(port, CP2101_UART, UART_ENABLE)) {
+               dev_err(&port->dev, "%s - Unable to enable UART\n",
+                               __FUNCTION__);
+               return -EPROTO;
+       }
+
+       /* 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);
+       if (result) {
+               dev_err(&port->dev, "%s - failed resubmitting read urb, "
+                               "error %d\n", __FUNCTION__, result);
+               return result;
+       }
+
+       /* Configure the termios structure */
+       cp2101_get_termios(port);
+
+       /* Set the DTR and RTS pins low */
+       cp2101_tiocmset(port, NULL, TIOCM_DTR | TIOCM_RTS, 0);
+
+       return 0;
+}
+
+static void cp2101_cleanup (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);
+       }
+}
+
+static void cp2101_close (struct usb_serial_port *port, struct file * filp)
+{
+       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);
+
+       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);
+
+       switch (cmd) {
+               
+       case TIOCMGET:
+       {
+               int result = cp2101_tiocmget(port, file);
+               if (copy_to_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)))
+                       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))
+                       return -EFAULT;
+               return 0;
+       }
+       break;
+
+       case IOCTL_GPIOGET:
+       if (cp2101_get_partnum(port) == CP210x_CP2103_VERSION) {
+               u8 gpio = 0;
+               if (!cp2101_gpioget(port, &gpio) && !copy_to_user((u8*)arg,
+                                       &gpio, sizeof(gpio)))
+                       return 0;
+       }
+       return -EFAULT;
+       break;
+
+       case IOCTL_GPIOSET:
+       if (cp2101_get_partnum(port) == CP210x_CP2103_VERSION &&
+                       !cp2101_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))
+               return 0;
+       return -EFAULT;
+       break;
+
+       case IOCTL_DEVICERESET:
+       return cp2101_reset(port);
+       break;
+
+       case IOCTL_PORTCONFGET:
+       {
+               cp2101_port_config_t config;
+               if (!cp2101_portconfget(port, &config) && !copy_to_user(
+                                       (cp2101_port_config_t*)arg, &config,
+                                       sizeof(config)))
+                       return 0;
+       }
+       return -EFAULT;
+       break;
+
+       case IOCTL_PORTCONFSET:
+       {
+       cp2101_port_config_t config;
+       if (!copy_from_user(&config, (cp2101_port_config_t*)arg,
+                       sizeof(config)) &&
+                       !cp2101_portconfset(port, &config))
+               return 0;
+       return -EFAULT;
+       }
+       break;
+
+       case IOCTL_SETVID:
+       {
+               unsigned int vid;
+               if (!copy_from_user(&vid, (unsigned int *)arg,
+                               sizeof(unsigned int)) &&
+                               !cp2101_setu16(port, 0x01, vid))
+                       return 0;
+               return -EFAULT;
+       }
+       break;
+
+       case IOCTL_SETPID:
+       {
+               unsigned int pid;
+               if (!copy_from_user(&pid, (unsigned int *)arg,
+                               sizeof(unsigned int)) &&
+                               !cp2101_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 */
+       {
+               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)
+                       return 0;
+               return -EFAULT;
+       }
+#endif
+       break;
+
+       case IOCTL_SETPRODUCT:
+       {
+               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)
+                       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:
+       {
+               unsigned int ver;
+               if (!copy_from_user(&ver, (unsigned int *)arg,
+                               sizeof(unsigned int)) &&
+                               !cp2101_setu16(port, 0x07, ver))
+                       return 0;
+               return -EFAULT;
+       }
+       break;
+
+       default:
+       dbg("%s not supported = 0x%04x", __FUNCTION__, cmd);
+       break;
+       }
+
+       return -ENOIOCTLCMD;
+}
+
+/*
+ * cp2101_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)
+{
+       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;
+       }
+
+       cp2101_get_config(port, CP2101_BAUDRATE, &baud, 2);
+       /* Convert to baudrate */
+       if (baud)
+               baud = BAUD_RATE_GEN_FREQ / baud;
+
+       dbg("%s - baud rate = %d", __FUNCTION__, baud);
+
+       tty_encode_baud_rate(port->tty, baud, baud);
+       cflag = port->tty->termios->c_cflag;
+
+       cp2101_get_config(port, CP2101_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;
+                       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_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;
+       }
+
+       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;
+       }
+
+       cp2101_get_config(port, CP2101_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 cp2101_set_termios (struct usb_serial_port *port,
+               struct ktermios *old_termios)
+{
+       unsigned int cflag, old_cflag;
+       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;
+       }
+       port->tty->termios->c_cflag &= ~CMSPAR;
+
+       cflag = port->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 the number of data bits is to be updated */
+       if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
+               cp2101_get_config(port, CP2101_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:
+                               dev_err(&port->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 ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))) {
+               cp2101_get_config(port, CP2101_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 (cp2101_set_config(port, CP2101_BITS, &bits, 2))
+                       dev_err(&port->dev, "Parity mode not supported "
+                                       "by device\n");
+       }
+
+       if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
+               cp2101_get_config(port, CP2101_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 (cp2101_set_config(port, CP2101_BITS, &bits, 2))
+                       dev_err(&port->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]);
+
+               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]);
+               cp2101_set_config(port, CP2101_MODEMCTL, modem_ctl, 16);
+       }
+
+}
+
+static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file,
+               unsigned int set, unsigned int clear)
+{
+       int control = 0;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (set & TIOCM_RTS) {
+               control |= CONTROL_RTS;
+               control |= CONTROL_WRITE_RTS;
+       }
+       if (set & TIOCM_DTR) {
+               control |= CONTROL_DTR;
+               control |= CONTROL_WRITE_DTR;
+       }
+       if (clear & TIOCM_RTS) {
+               control &= ~CONTROL_RTS;
+               control |= CONTROL_WRITE_RTS;
+       }
+       if (clear & TIOCM_DTR) {
+               control &= ~CONTROL_DTR;
+               control |= CONTROL_WRITE_DTR;
+       }
+
+       dbg("%s - control = 0x%.4x", __FUNCTION__, control);
+
+       return cp2101_set_config(port, CP2101_CONTROL, &control, 2);
+
+}
+
+static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file)
+{
+       int control, result;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       cp2101_get_config(port, CP2101_CONTROL, &control, 1);
+
+       result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0)
+               |((control & CONTROL_RTS) ? TIOCM_RTS : 0)
+               |((control & CONTROL_CTS) ? TIOCM_CTS : 0)
+               |((control & CONTROL_DSR) ? TIOCM_DSR : 0)
+               |((control & CONTROL_RING)? TIOCM_RI  : 0)
+               |((control & CONTROL_DCD) ? TIOCM_CD  : 0);
+
+       dbg("%s - control = 0x%.2x", __FUNCTION__, control);
+
+       return result;
+}
+
+static void cp2101_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");
+       cp2101_set_config(port, CP2101_BREAK, &state, 2);
+}
+
+static int cp2101_startup (struct usb_serial *serial)
+{
+       /* CP2101 buffers behave strangely unless device is reset */
+       usb_reset_device(serial->dev);
+       return 0;
+}
+
+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]);
+       }
+}
+
+static int __init cp2101_init (void)
+{
+       int retval;
+
+       retval = usb_serial_register(&cp2101_device);
+       if (retval)
+               return retval; /* Failed to register */
+
+       retval = usb_register(&cp2101_driver);
+       if (retval) {
+               /* Failed to register */
+               usb_serial_deregister(&cp2101_device);
+               return retval;
+       }
+
+       /* Success */
+       info(DRIVER_DESC " " DRIVER_VERSION);
+       return 0;
+}
+
+static void __exit cp2101_exit (void)
+{
+       usb_deregister (&cp2101_driver);
+       usb_serial_deregister (&cp2101_device);
+}
+
+module_init(cp2101_init);
+module_exit(cp2101_exit);
+
+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");