On Thu, 2005-03-24 at 00:31 -0800, Greg KH wrote: > So, fix up the formatting issues (your braces are in the wrong places, > see Documenation/CodingStyle) and send the patch.
Well, here it is with the alterations recommended in CodingStyle. -- Craig Shelley EMail: [EMAIL PROTECTED] Jabber: [EMAIL PROTECTED]
This patch against 2.6.10 provides the driver for the CP2101 and CP2102 USB
to RS232 adaptor chips which are found in some serial converter cables and
usb mobile phone cables.
It provides cp2101.c, and patches Kconfig and Makefile in drivers/usb/serial
Since there is no technical data sheet freely available for these chips, much
of the code in this driver has been based on protocol analysis, assumptions
and guesswork.
The cp210x family of devices have OEM programmable device IDs so updates will
be required as and when new devices are found.
The driver currently only supports configuration of baud rate, data bits,
stop bits and parity mode. While this is enough to enable the use of a wide
range of devices based on this chip, the driver is still incomplete in many
areas such as flow control.
Unfortunately without technical datasheets, implementing these features is
made very difficult indeed.
Signed-off-by: Craig Shelley [EMAIL PROTECTED]
diff -urN a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c
--- a/drivers/usb/serial/cp2101.c 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/usb/serial/cp2101.c 2005-03-24 11:26:42.747160384 +0000
@@ -0,0 +1,593 @@
+/*
+ * Silicon Laboratories CP2101/CP2102 USB to RS232 serial adaptor driver
+ *
+ * Copyright (C) 2005 Craig Shelley ([EMAIL PROTECTED])
+ *
+ * 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.
+ *
+ */
+
+#include <linux/config.h>
+#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 "usb-serial.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.03"
+#define DRIVER_DESC "Silicon Labs CP2101/CP2102 RS232 serial adaptor driver"
+
+/*
+ * Function Prototypes
+ */
+int cp2101_get_config(struct usb_serial_port*, u8);
+int cp2101_set_config(struct usb_serial_port*, u8, u16);
+int cp2101_open(struct usb_serial_port*, struct file*);
+static void cp2101_cleanup(struct usb_serial_port*);
+void cp2101_close(struct usb_serial_port*, struct file*);
+void cp2101_get_termios(struct usb_serial_port*);
+static void cp2101_set_termios(struct usb_serial_port*, struct termios*);
+static void cp2101_break_ctl(struct usb_serial_port*, int);
+static int cp2101_startup (struct usb_serial *);
+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*/
+};
+
+MODULE_DEVICE_TABLE (usb, id_table);
+
+static struct usb_driver cp2101_driver = {
+ .owner = THIS_MODULE,
+ .name = "CP2101",
+ .probe = usb_serial_probe,
+ .disconnect = usb_serial_disconnect,
+ .id_table = id_table,
+};
+
+struct usb_serial_device_type cp2101_device = {
+ .owner = THIS_MODULE,
+ .name = "CP2101",
+ .id_table = id_table,
+ .num_interrupt_in = 0,
+ .num_bulk_in = 0,
+ .num_bulk_out = 0,
+ .num_ports = 1,
+ .open = cp2101_open,
+ .close = cp2101_close,
+ .break_ctl = cp2101_break_ctl,
+ .set_termios = cp2101_set_termios,
+ .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)(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 ???*/
+
+/*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_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
+#define BREAK_ON 0x0000
+#define BREAK_OFF 0x0001
+
+
+int cp2101_get_config(struct usb_serial_port* port, u8 request)
+{
+ struct usb_serial *serial = port->serial;
+ unsigned char buf[4];
+ unsigned int value;
+ int result, i;
+
+ /*For get requests, the request number must be incremented*/
+ request++;
+
+ /*Issue the request, attempting to read 4 bytes*/
+ result = usb_control_msg (serial->dev,usb_rcvctrlpipe (serial->dev, 0),
+ request, REQTYPE_DEVICE_TO_HOST, 0x0000,
+ 0, buf, 4, 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;
+ }
+
+ /*Assemble each byte read into an integer value*/
+ value = 0;
+ for (i=0; i<4 && i<result; i++)
+ value |= (buf[i] << (i * 8));
+
+ dbg( " %s - request=0x%x result=%d value=0x%x",
+ __FUNCTION__, request, result, value);
+
+ return value;
+}
+
+int cp2101_set_config(struct usb_serial_port* port, u8 request, u16 value)
+{
+ 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);
+
+ 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;
+ }
+
+ dbg(" %s - request=0x%x value=0x%x result=%d",
+ __FUNCTION__, request, value, result);
+
+ return 0;
+}
+
+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(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);
+
+ 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);
+ }
+}
+
+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(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*/
+void cp2101_get_termios (struct usb_serial_port *port)
+{
+ unsigned int cflag;
+ int baud;
+ int bits;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if ((!port->tty) || (!port->tty->termios)) {
+ dbg("%s - no tty structures", __FUNCTION__);
+ return;
+ }
+ cflag = port->tty->termios->c_cflag;
+
+ baud = cp2101_get_config(port, CP2101_BAUDRATE);
+ /*Convert to baudrate*/
+ if (baud)
+ baud = BAUD_RATE_GEN_FREQ / baud;
+
+ dbg("%s - baud rate = %d", __FUNCTION__, baud);
+ cflag &= ~CBAUD;
+ switch (baud) {
+ /* The baud rates which are commented out below
+ * appear to be supported by the device
+ * but are non-standard
+ */
+ case 600: cflag |= B600; break;
+ case 1200: cflag |= B1200; break;
+ case 1800: cflag |= B1800; break;
+ case 2400: cflag |= B2400; break;
+ case 4800: cflag |= B4800; break;
+ /*case 7200: cflag |= B7200; break;*/
+ case 9600: cflag |= B9600; break;
+ /*case 14400: cflag |= B14400; break;*/
+ case 19200: cflag |= B19200; break;
+ /*case 28800: cflag |= B28800; break;*/
+ case 38400: cflag |= B38400; break;
+ /*case 55854: cflag |= B55054; break;*/
+ case 57600: cflag |= B57600; break;
+ case 115200: cflag |= B115200; break;
+ /*case 127117: cflag |= B127117; break;*/
+ case 230400: cflag |= B230400; break;
+ case 460800: cflag |= B460800; break;
+ case 921600: cflag |= B921600; break;
+ /*case 3686400: cflag |= B3686400; break;*/
+ default:
+ dbg("%s - Baud rate is not supported, "
+ "using 9600 baud", __FUNCTION__);
+ cflag |= B9600;
+ cp2101_set_config(port, CP2101_BAUDRATE,
+ (BAUD_RATE_GEN_FREQ/9600));
+ break;
+ }
+
+ bits = cp2101_get_config(port, CP2101_BITS);
+ cflag &= ~CSIZE;
+ switch(bits & BITS_DATA_MASK) {
+ 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);
+ 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);
+ 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);
+ 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);
+ break;
+ default:
+ dbg("%s - Unknown parity mode, "
+ "disabling parity", __FUNCTION__);
+ cflag &= ~PARENB;
+ bits &= ~BITS_PARITY_MASK;
+ cp2101_set_config(port, CP2101_BITS, bits);
+ 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);
+ 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);
+ break;
+ }
+
+ port->tty->termios->c_cflag = cflag;
+}
+
+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;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if ((!port->tty) || (!port->tty->termios)) {
+ dbg("%s - no tty structures", __FUNCTION__);
+ return;
+ }
+ cflag = port->tty->termios->c_cflag;
+
+ /* check that they really want us to change something */
+ if (old_termios) {
+ if ((cflag == old_termios->c_cflag) &&
+ (RELEVANT_IFLAG(port->tty->termios->c_iflag)
+ == RELEVANT_IFLAG(old_termios->c_iflag))) {
+ dbg("%s - nothing to change...", __FUNCTION__);
+ return;
+ }
+
+ old_cflag = old_termios->c_cflag;
+ }
+
+ /* If the baud rate is to be updated*/
+ if ((cflag & CBAUD) != (old_cflag & CBAUD)) {
+ switch (cflag & CBAUD) {
+ /* The baud rates which are commented out below
+ * appear to be supported by the device
+ * but are non-standard
+ */
+ case B0: baud = 0; break;
+ case B600: baud = 600; break;
+ case B1200: baud = 1200; break;
+ case B1800: baud = 1800; break;
+ case B2400: baud = 2400; break;
+ case B4800: baud = 4800; break;
+ /*case B7200: baud = 7200; break;*/
+ case B9600: baud = 9600; break;
+ /*ase B14400: baud = 14400; break;*/
+ case B19200: baud = 19200; break;
+ /*case B28800: baud = 28800; break;*/
+ case B38400: baud = 38400; break;
+ /*case B55854: baud = 55054; break;*/
+ case B57600: baud = 57600; break;
+ case B115200: baud = 115200; break;
+ /*case B127117: baud = 127117; break;*/
+ case B230400: baud = 230400; break;
+ case B460800: baud = 460800; break;
+ case B921600: baud = 921600; break;
+ /*case B3686400: baud = 3686400; break;*/
+ default:
+ dev_err(&port->dev, "cp2101 driver does not "
+ "support the baudrate requested\n");
+ break;
+ }
+
+ if (baud) {
+ dbg("%s - Setting baud rate to %d baud", __FUNCTION__,
+ baud);
+ if (cp2101_set_config(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 ((cflag & CSIZE) != (old_cflag & CSIZE)) {
+ bits = cp2101_get_config(port, CP2101_BITS);
+ bits &= ~BITS_DATA_MASK;
+ switch (cflag & CSIZE) {
+ 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))
+ 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);
+ 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))
+ dev_err(&port->dev, "Parity mode not supported "
+ "by device\n");
+ }
+
+ if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
+ bits = cp2101_get_config(port, CP2101_BITS);
+ 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))
+ dev_err(&port->dev, "Number of stop bits requested "
+ "not supported by device\n");
+ }
+}
+
+static void cp2101_break_ctl (struct usb_serial_port *port, int break_state)
+{
+ u16 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);
+}
+
+static int cp2101_startup (struct usb_serial *serial)
+{
+ /*CP2101 buffers behave strangely unless device is reset*/
+ usb_reset_device(serial->dev);
+ return 0;
+}
+
+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 -urN a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
--- a/drivers/usb/serial/Kconfig 2005-03-18 00:39:25.905779352 +0000
+++ b/drivers/usb/serial/Kconfig 2005-03-20 01:12:07.167705904 +0000
@@ -413,6 +413,15 @@
To compile this driver as a module, choose M here: the
module will be called omninet.
+config USB_SERIAL_CP2101
+ tristate "USB CP2101 UART Bridge Controller (EXPERIMENTAL)"
+ depends on USB_SERIAL && EXPERIMENTAL
+ help
+ Say Y here if you want to use a CP2101/CP2102 based USB to RS232 converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cp2101.
+
config USB_EZUSB
bool
depends on USB_SERIAL_KEYSPAN_PDA || USB_SERIAL_XIRCOM || USB_SERIAL_KEYSPAN || USB_SERIAL_WHITEHEAT
diff -urN a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
--- a/drivers/usb/serial/Makefile 2005-03-18 00:37:33.292899112 +0000
+++ b/drivers/usb/serial/Makefile 2005-03-18 01:20:14.211580296 +0000
@@ -33,4 +33,5 @@
obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o
obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o
obj-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda.o
+obj-$(CONFIG_USB_SERIAL_CP2101) += cp2101.o
signature.asc
Description: This is a digitally signed message part
