[PATCH] USB: CP2101 Add support for flow control
authorCraig Shelley <craig@microtron.org.uk>
Thu, 26 May 2005 23:09:56 +0000 (00:09 +0100)
committerGreg KH <gregkh@suse.de>
Fri, 3 Jun 2005 07:04:30 +0000 (00:04 -0700)
Added support to get/set flow control line levels using TIOCMGET and
TIOCMSET.
Added support for RTSCTS hardware flow control.
cp2101_get_config and cp2101_set_config modified to support long request
strings, required for configuring flow control.

Signed-off-by: Craig Shelley craig@microtron.org.uk
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/serial/cp2101.c

index 7e9bb63eb466d3e09e608b22b4073448845e6802..4ace9964fc6bd36a22c0a82da2fc432be4eef5f7 100644 (file)
@@ -7,6 +7,14 @@
  *     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
+ *
+ * Outstanding Issues:
+ *  Buffers are not flushed when the port is opened.
+ *  Multiple calls to write() may fail with "Resource temporarily unavailable"
+ *
  */
 
 #include <linux/config.h>
@@ -24,7 +32,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.03"
+#define DRIVER_VERSION "v0.04"
 #define DRIVER_DESC "Silicon Labs CP2101/CP2102 RS232 serial adaptor driver"
 
 /*
@@ -35,6 +43,9 @@ 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 termios*);
+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*);
@@ -43,9 +54,10 @@ static void cp2101_shutdown(struct usb_serial*);
 static int debug;
 
 static struct usb_device_id id_table [] = {
-       {USB_DEVICE(0x10c4, 0xea60) },  /*Silicon labs factory default*/
-       {USB_DEVICE(0x10ab, 0x10c5) },  /*Siemens MC60 Cable*/
-       { } /* Terminating Entry*/
+       { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
+       { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
+       { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
+       { } /* Terminating Entry */
 };
 
 MODULE_DEVICE_TABLE (usb, id_table);
@@ -70,32 +82,35 @@ static struct usb_serial_device_type cp2101_device = {
        .close                  = cp2101_close,
        .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*/
+/* 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)(data bits)(parity)(stop bits)*/
-#define CP2101_BREAK           0x05    /*On / Off*/
-#define CP2101_DTRRTS          0x07    /*101 / 202  ???*/
-#define CP2101_CONFIG_16       0x13    /*16 bytes of config data ???*/
-#define CP2101_CONFIG_6                0x19    /*6 bytes of config data ???*/
+/* 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*/
+/* CP2101_UART */
 #define UART_ENABLE            0x0001
 #define UART_DISABLE           0x0000
 
-/*CP2101_BAUDRATE*/
+/* CP2101_BAUDRATE */
 #define BAUD_RATE_GEN_FREQ     0x384000
 
-/*CP2101_BITS*/
+/* 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
@@ -112,64 +127,137 @@ static struct usb_serial_device_type cp2101_device = {
 #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
 
-static int cp2101_get_config(struct usb_serial_port* port, u8 request)
+/*
+ * 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;
-       unsigned char buf[4];
-       unsigned int value;
-       int result, i;
+       u32 *buf;
+       int result, i, length;
+
+       /* Number of integers required to contain the array */
+       length = (((size - 1) | 3) + 1)/4;
+
+       buf = kmalloc (length * sizeof(u32), GFP_KERNEL);
+       memset(buf, 0, length * sizeof(u32));
+
+       if (!buf) {
+               dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__);
+               return -ENOMEM;
+       }
 
-       /*For get requests, the request number must be incremented*/
+       /* For get requests, the request number must be incremented */
        request++;
 
-       /*Issue the request, attempting to read 4 bytes*/
+       /* 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, 4, 300);
+                               0, buf, size, 300);
 
-       if (result < 0) {
-               dev_err(&port->dev, "%s - Unable to send config request, "
-                               "request=0x%x result=%d\n",
-                               __FUNCTION__, request, result);
-               return result;
-       }
+       /* Convert data into an array of integers */
+       for (i=0; i<length; i++)
+               data[i] = le32_to_cpu(buf[i]);
 
-       /*Assemble each byte read into an integer value*/
-       value = 0;
-       for (i=0; i<4 && i<result; i++)
-               value |= (buf[i] << (i * 8));
+       kfree(buf);
 
-       dbg( " %s - request=0x%x result=%d value=0x%x",
-                       __FUNCTION__, request, result, value);
+       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 value;
+       return 0;
 }
 
-static int cp2101_set_config(struct usb_serial_port* port, u8 request, u16 value)
+/*
+ * 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;
-       int result;
-       result = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0),
-                       request, REQTYPE_HOST_TO_DEVICE, value,
-                       0, NULL, 0, 300);
+       u32 *buf;
+       int result, i, length;
 
-       if (result <0) {
-               dev_err(&port->dev, "%s - Unable to send config request, "
-                               "request=0x%x value=0x%x result=%d\n",
-                               __FUNCTION__, request, value, result);
-               return result;
+       /* Number of integers required to contain the array */
+       length = (((size - 1) | 3) + 1)/4;
+
+       buf = kmalloc(length * sizeof(u32), 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);
        }
 
-       dbg(" %s - request=0x%x value=0x%x result=%d",
-                       __FUNCTION__, request, value, result);
+       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;
@@ -177,7 +265,7 @@ static int cp2101_open (struct usb_serial_port *port, struct file *filp)
 
        dbg("%s - port %d", __FUNCTION__, port->number);
 
-       if (cp2101_set_config(port, CP2101_UART, UART_ENABLE)) {
+       if (cp2101_set_config_single(port, CP2101_UART, UART_ENABLE)) {
                dev_err(&port->dev, "%s - Unable to enable UART\n",
                                __FUNCTION__);
                return -EPROTO;
@@ -198,9 +286,12 @@ static int cp2101_open (struct usb_serial_port *port, struct file *filp)
                return result;
        }
 
-       /*Configure the termios structure*/
+       /* 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;
 }
 
@@ -228,16 +319,18 @@ static void cp2101_close (struct usb_serial_port *port, struct file * filp)
        usb_kill_urb(port->write_urb);
        usb_kill_urb(port->read_urb);
 
-       cp2101_set_config(port, CP2101_UART, UART_DISABLE);
+       cp2101_set_config_single(port, CP2101_UART, UART_DISABLE);
 }
 
-/* cp2101_get_termios*/
-/* Reads the baud rate, data bits, parity and stop bits from the device*/
-/* Corrects any unsupported values*/
-/* Configures the termios structure to reflect the state of the device*/
+/*
+ * 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;
+       unsigned int cflag, modem_ctl[4];
        int baud;
        int bits;
 
@@ -249,15 +342,16 @@ static void cp2101_get_termios (struct usb_serial_port *port)
        }
        cflag = port->tty->termios->c_cflag;
 
-       baud = cp2101_get_config(port, CP2101_BAUDRATE);
-       /*Convert to baudrate*/
+       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);
        cflag &= ~CBAUD;
        switch (baud) {
-               /* The baud rates which are commented out below
+               /*
+                * The baud rates which are commented out below
                 * appear to be supported by the device
                 * but are non-standard
                 */
@@ -284,14 +378,18 @@ static void cp2101_get_termios (struct usb_serial_port *port)
                        dbg("%s - Baud rate is not supported, "
                                        "using 9600 baud", __FUNCTION__);
                        cflag |= B9600;
-                       cp2101_set_config(port, CP2101_BAUDRATE,
+                       cp2101_set_config_single(port, CP2101_BAUDRATE,
                                        (BAUD_RATE_GEN_FREQ/9600));
                        break;
        }
 
-       bits = cp2101_get_config(port, CP2101_BITS);
+       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;
@@ -310,7 +408,7 @@ static void cp2101_get_termios (struct usb_serial_port *port)
                        cflag |= CS8;
                        bits &= ~BITS_DATA_MASK;
                        bits |= BITS_DATA_8;
-                       cp2101_set_config(port, CP2101_BITS, bits);
+                       cp2101_set_config(port, CP2101_BITS, &bits, 2);
                        break;
                default:
                        dbg("%s - Unknown number of data bits, "
@@ -318,7 +416,7 @@ static void cp2101_get_termios (struct usb_serial_port *port)
                        cflag |= CS8;
                        bits &= ~BITS_DATA_MASK;
                        bits |= BITS_DATA_8;
-                       cp2101_set_config(port, CP2101_BITS, bits);
+                       cp2101_set_config(port, CP2101_BITS, &bits, 2);
                        break;
        }
 
@@ -341,21 +439,21 @@ static void cp2101_get_termios (struct usb_serial_port *port)
                                        "disabling parity)", __FUNCTION__);
                        cflag &= ~PARENB;
                        bits &= ~BITS_PARITY_MASK;
-                       cp2101_set_config(port, CP2101_BITS, bits);
+                       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);
+                       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);
+                       cp2101_set_config(port, CP2101_BITS, &bits, 2);
                        break;
        }
 
@@ -366,9 +464,9 @@ static void cp2101_get_termios (struct usb_serial_port *port)
                        break;
                case BITS_STOP_1_5:
                        dbg("%s - stop bits = 1.5 (not supported, "
-                                       "using 1 stop bit", __FUNCTION__);
+                                       "using 1 stop bit)", __FUNCTION__);
                        bits &= ~BITS_STOP_MASK;
-                       cp2101_set_config(port, CP2101_BITS, bits);
+                       cp2101_set_config(port, CP2101_BITS, &bits, 2);
                        break;
                case BITS_STOP_2:
                        dbg("%s - stop bits = 2", __FUNCTION__);
@@ -378,10 +476,19 @@ static void cp2101_get_termios (struct usb_serial_port *port)
                        dbg("%s - Unknown number of stop bits, "
                                        "using 1 stop bit", __FUNCTION__);
                        bits &= ~BITS_STOP_MASK;
-                       cp2101_set_config(port, CP2101_BITS, bits);
+                       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;
 }
 
@@ -389,8 +496,8 @@ static void cp2101_set_termios (struct usb_serial_port *port,
                struct termios *old_termios)
 {
        unsigned int cflag, old_cflag=0;
-       int baud=0;
-       int bits;
+       int baud=0, bits;
+       unsigned int modem_ctl[4];
 
        dbg("%s - port %d", __FUNCTION__, port->number);
 
@@ -400,7 +507,7 @@ static void cp2101_set_termios (struct usb_serial_port *port,
        }
        cflag = port->tty->termios->c_cflag;
 
-       /* check that they really want us to change something */
+       /* Check that they really want us to change something */
        if (old_termios) {
                if ((cflag == old_termios->c_cflag) &&
                                (RELEVANT_IFLAG(port->tty->termios->c_iflag)
@@ -415,7 +522,8 @@ static void cp2101_set_termios (struct usb_serial_port *port,
        /* If the baud rate is to be updated*/
        if ((cflag & CBAUD) != (old_cflag & CBAUD)) {
                switch (cflag & CBAUD) {
-                       /* The baud rates which are commented out below
+                       /*
+                        * The baud rates which are commented out below
                         * appear to be supported by the device
                         * but are non-standard
                         */
@@ -448,18 +556,22 @@ static void cp2101_set_termios (struct usb_serial_port *port,
                if (baud) {
                        dbg("%s - Setting baud rate to %d baud", __FUNCTION__,
                                        baud);
-                       if (cp2101_set_config(port, CP2101_BAUDRATE,
+                       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");
                }
        }
 
-       /*If the number of data bits is to be updated*/
+       /* If the number of data bits is to be updated */
        if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
-               bits = cp2101_get_config(port, CP2101_BITS);
+               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__);
@@ -483,13 +595,13 @@ static void cp2101_set_termios (struct usb_serial_port *port,
                                bits |= BITS_DATA_8;
                                break;
                }
-               if (cp2101_set_config(port, CP2101_BITS, bits))
+               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))) {
-               bits = cp2101_get_config(port, CP2101_BITS);
+               cp2101_get_config(port, CP2101_BITS, &bits, 2);
                bits &= ~BITS_PARITY_MASK;
                if (cflag & PARENB) {
                        if (cflag & PARODD) {
@@ -500,13 +612,13 @@ static void cp2101_set_termios (struct usb_serial_port *port,
                                dbg("%s - parity = EVEN", __FUNCTION__);
                        }
                }
-               if (cp2101_set_config(port, CP2101_BITS, bits))
+               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)) {
-               bits = cp2101_get_config(port, CP2101_BITS);
+               cp2101_get_config(port, CP2101_BITS, &bits, 2);
                bits &= ~BITS_STOP_MASK;
                if (cflag & CSTOPB) {
                        bits |= BITS_STOP_2;
@@ -515,15 +627,90 @@ static void cp2101_set_termios (struct usb_serial_port *port,
                        bits |= BITS_STOP_1;
                        dbg("%s - stop bits = 1", __FUNCTION__);
                }
-               if (cp2101_set_config(port, CP2101_BITS, bits))
+               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)
 {
-       u16 state;
+       int state;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
        if (break_state == 0)
@@ -532,12 +719,12 @@ static void cp2101_break_ctl (struct usb_serial_port *port, int break_state)
                state = BREAK_ON;
        dbg("%s - turning break %s", __FUNCTION__,
                        state==BREAK_OFF ? "off" : "on");
-       cp2101_set_config(port, CP2101_BREAK, state);
+       cp2101_set_config(port, CP2101_BREAK, &state, 2);
 }
 
 static int cp2101_startup (struct usb_serial *serial)
 {
-       /*CP2101 buffers behave strangely unless device is reset*/
+       /* CP2101 buffers behave strangely unless device is reset */
        usb_reset_device(serial->dev);
        return 0;
 }
@@ -548,7 +735,7 @@ static void cp2101_shutdown (struct usb_serial *serial)
 
        dbg("%s", __FUNCTION__);
 
-       /* stop reads and writes on all ports */
+       /* Stop reads and writes on all ports */
        for (i=0; i < serial->num_ports; ++i) {
                cp2101_cleanup(serial->port[i]);
        }
@@ -560,16 +747,16 @@ static int __init cp2101_init (void)
 
        retval = usb_serial_register(&cp2101_device);
        if (retval)
-               return retval; /*Failed to register*/
+               return retval; /* Failed to register */
 
        retval = usb_register(&cp2101_driver);
        if (retval) {
-               /*Failed to register*/
+               /* Failed to register */
                usb_serial_deregister(&cp2101_device);
                return retval;
        }
 
-       /*Success*/
+       /* Success */
        info(DRIVER_DESC " " DRIVER_VERSION);
        return 0;
 }