From ae27d84351f1f3568118318a8c40ff3a154bd629 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Wed, 25 Mar 2009 11:32:59 +0100 Subject: USB: ftdi_sio: add vendor/project id for JETI specbos 1201 spectrometer Signed-off-by: Peter Korsgaard Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio.h | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'drivers/usb/serial') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index dcc87aaa862..8100f1d2590 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -668,6 +668,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(DE_VID, WHT_PID) }, { USB_DEVICE(ADI_VID, ADI_GNICE_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index daaf63db0b5..c09f658a448 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -912,6 +912,13 @@ #define ADI_VID 0x0456 #define ADI_GNICE_PID 0xF000 +/* + * JETI SPECTROMETER SPECBOS 1201 + * http://www.jeti.com/products/sys/scb/scb1201.php + */ +#define JETI_VID 0x0c6c +#define JETI_SPC1201_PID 0x04b2 + /* * BmRequestType: 1100 0000b * bRequest: FTDI_E2_READ -- cgit v1.2.3 From c00deaa542100a697acb5be200b9f54317c1cf69 Mon Sep 17 00:00:00 2001 From: Pascal Terjan Date: Tue, 7 Apr 2009 14:40:42 +0200 Subject: USB: option: Add ids for D-Link DWM-652 3.5G modem This patch allows D-Link DWM-652 3.5G modem to work. It is an express card but was only tested with the provided usb adapter as I don't have machines with express card connector. /dev/ttyUSB{0,1,2} get created, and using comgt on ttyUSB1 works fine : [root@plop tmp]# comgt -d /dev/ttyUSB1 -e Enter PIN number: XXXX Waiting for Registration..(120 sec max). Registered on Home network: "Orange France",2 Signal Quality: 15,99 From: Pascal Terjan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/usb/serial') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index d560c0b54e6..47bd070f24b 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -300,6 +300,10 @@ static int option_resume(struct usb_serial *serial); #define BENQ_VENDOR_ID 0x04a5 #define BENQ_PRODUCT_H10 0x4068 +#define DLINK_VENDOR_ID 0x1186 +#define DLINK_PRODUCT_DWM_652 0x3e04 + + static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -516,6 +520,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628) }, { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH) }, { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) }, + { USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) }, { USB_DEVICE(0x1da5, 0x4515) }, /* BenQ H20 */ { } /* Terminating entry */ }; -- cgit v1.2.3 From f05932c0caf40c43af8a2c21adf7c46a00c436c4 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sat, 4 Apr 2009 17:24:24 +0100 Subject: USB: qcserial: Add extra device IDs Add a set of device IDs from the Windows drivers. These aren't complete (there's a couple of cases where a QDL device is identified without the associated modem being identified), but it's better than the current situation. Signed-off-by: Matthew Garrett Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/usb/serial') diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index e6d6b0c17fd..7528b8d57f1 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -26,6 +26,27 @@ static struct usb_device_id id_table[] = { {USB_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ {USB_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */ {USB_DEVICE(0x03f0, 0x201d)}, /* HP un2400 Gobi QDL Device */ + {USB_DEVICE(0x04da, 0x250d)}, /* Panasonic Gobi Modem device */ + {USB_DEVICE(0x04da, 0x250c)}, /* Panasonic Gobi QDL device */ + {USB_DEVICE(0x413c, 0x8172)}, /* Dell Gobi Modem device */ + {USB_DEVICE(0x413c, 0x8171)}, /* Dell Gobi QDL device */ + {USB_DEVICE(0x1410, 0xa001)}, /* Novatel Gobi Modem device */ + {USB_DEVICE(0x1410, 0xa008)}, /* Novatel Gobi QDL device */ + {USB_DEVICE(0x0b05, 0x1776)}, /* Asus Gobi Modem device */ + {USB_DEVICE(0x0b05, 0x1774)}, /* Asus Gobi QDL device */ + {USB_DEVICE(0x19d2, 0xfff3)}, /* ONDA Gobi Modem device */ + {USB_DEVICE(0x19d2, 0xfff2)}, /* ONDA Gobi QDL device */ + {USB_DEVICE(0x1557, 0x0a80)}, /* OQO Gobi QDL device */ + {USB_DEVICE(0x05c6, 0x9001)}, /* Generic Gobi Modem device */ + {USB_DEVICE(0x05c6, 0x9002)}, /* Generic Gobi Modem device */ + {USB_DEVICE(0x05c6, 0x9202)}, /* Generic Gobi Modem device */ + {USB_DEVICE(0x05c6, 0x9203)}, /* Generic Gobi Modem device */ + {USB_DEVICE(0x05c6, 0x9222)}, /* Generic Gobi Modem device */ + {USB_DEVICE(0x05c6, 0x9008)}, /* Generic Gobi QDL device */ + {USB_DEVICE(0x05c6, 0x9201)}, /* Generic Gobi QDL device */ + {USB_DEVICE(0x05c6, 0x9221)}, /* Generic Gobi QDL device */ + {USB_DEVICE(0x05c6, 0x9231)}, /* Generic Gobi QDL device */ + {USB_DEVICE(0x1f45, 0x0001)}, /* Unknown Gobi QDL device */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); -- cgit v1.2.3 From c7877e1983f051cc267e1ec8c232a02d23c0a82a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 7 Apr 2009 11:56:32 -0700 Subject: Revert USB: usb-serial ch341: support for DTR/RTS/CTS Reverts commit 664d5df92e88b6ef091048a802b3750f4e989180 as the commit log information was not complete, and we didn't have a proper signed-off-by by the author of the original BSD code. Cc: Werner Cornelius Cc: Boris Hajduk Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ch341.c | 374 +++++++++------------------------------------ 1 file changed, 72 insertions(+), 302 deletions(-) (limited to 'drivers/usb/serial') diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index ab4cc277aa6..d33d39d9b7e 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -1,7 +1,5 @@ /* * Copyright 2007, Frank A Kingswood - * Copyright 2007, Werner Cornelius - * Copyright 2009, Boris Hajduk * * ch341.c implements a serial port driver for the Winchiphead CH341. * @@ -23,39 +21,9 @@ #include #include -#define DEFAULT_BAUD_RATE 9600 +#define DEFAULT_BAUD_RATE 2400 #define DEFAULT_TIMEOUT 1000 -/* flags for IO-Bits */ -#define CH341_BIT_RTS (1 << 6) -#define CH341_BIT_DTR (1 << 5) - -/******************************/ -/* interrupt pipe definitions */ -/******************************/ -/* always 4 interrupt bytes */ -/* first irq byte normally 0x08 */ -/* second irq byte base 0x7d + below */ -/* third irq byte base 0x94 + below */ -/* fourth irq byte normally 0xee */ - -/* second interrupt byte */ -#define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */ - -/* status returned in third interrupt answer byte, inverted in data - from irq */ -#define CH341_BIT_CTS 0x01 -#define CH341_BIT_DSR 0x02 -#define CH341_BIT_RI 0x04 -#define CH341_BIT_DCD 0x08 -#define CH341_BITS_MODEM_STAT 0x0f /* all bits */ - -/*******************************/ -/* baudrate calculation factor */ -/*******************************/ -#define CH341_BAUDBASE_FACTOR 1532620800 -#define CH341_BAUDBASE_DIVMAX 3 - static int debug; static struct usb_device_id id_table [] = { @@ -66,12 +34,9 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE(usb, id_table); struct ch341_private { - spinlock_t lock; /* access lock */ - wait_queue_head_t delta_msr_wait; /* wait queue for modem status */ - unsigned baud_rate; /* set baud rate */ - u8 line_control; /* set line control value RTS/DTR */ - u8 line_status; /* active status of modem control inputs */ - u8 multi_status_change; /* status changed multiple since last call */ + unsigned baud_rate; + u8 dtr; + u8 rts; }; static int ch341_control_out(struct usb_device *dev, u8 request, @@ -107,28 +72,37 @@ static int ch341_set_baudrate(struct usb_device *dev, { short a, b; int r; - unsigned long factor; - short divisor; dbg("ch341_set_baudrate(%d)", priv->baud_rate); - - if (!priv->baud_rate) + switch (priv->baud_rate) { + case 2400: + a = 0xd901; + b = 0x0038; + break; + case 4800: + a = 0x6402; + b = 0x001f; + break; + case 9600: + a = 0xb202; + b = 0x0013; + break; + case 19200: + a = 0xd902; + b = 0x000d; + break; + case 38400: + a = 0x6403; + b = 0x000a; + break; + case 115200: + a = 0xcc03; + b = 0x0008; + break; + default: return -EINVAL; - factor = (CH341_BAUDBASE_FACTOR / priv->baud_rate); - divisor = CH341_BAUDBASE_DIVMAX; - - while ((factor > 0xfff0) && divisor) { - factor >>= 3; - divisor--; } - if (factor > 0xfff0) - return -EINVAL; - - factor = 0x10000 - factor; - a = (factor & 0xff00) | divisor; - b = factor & 0xff; - r = ch341_control_out(dev, 0x9a, 0x1312, a); if (!r) r = ch341_control_out(dev, 0x9a, 0x0f2c, b); @@ -136,18 +110,19 @@ static int ch341_set_baudrate(struct usb_device *dev, return r; } -static int ch341_set_handshake(struct usb_device *dev, u8 control) +static int ch341_set_handshake(struct usb_device *dev, + struct ch341_private *priv) { - dbg("ch341_set_handshake(0x%02x)", control); - return ch341_control_out(dev, 0xa4, ~control, 0); + dbg("ch341_set_handshake(%d,%d)", priv->dtr, priv->rts); + return ch341_control_out(dev, 0xa4, + ~((priv->dtr?1<<5:0)|(priv->rts?1<<6:0)), 0); } -static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv) +static int ch341_get_status(struct usb_device *dev) { char *buffer; int r; const unsigned size = 8; - unsigned long flags; dbg("ch341_get_status()"); @@ -159,15 +134,10 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv) if (r < 0) goto out; - /* setup the private status if available */ - if (r == 2) { - r = 0; - spin_lock_irqsave(&priv->lock, flags); - priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT; - priv->multi_status_change = 0; - spin_unlock_irqrestore(&priv->lock, flags); - } else - r = -EPROTO; + /* Not having the datasheet for the CH341, we ignore the bytes returned + * from the device. Return error if the device did not respond in time. + */ + r = 0; out: kfree(buffer); return r; @@ -210,7 +180,7 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) goto out; /* expect 0xff 0xee */ - r = ch341_get_status(dev, priv); + r = ch341_get_status(dev); if (r < 0) goto out; @@ -222,12 +192,12 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) if (r < 0) goto out; - r = ch341_set_handshake(dev, priv->line_control); + r = ch341_set_handshake(dev, priv); if (r < 0) goto out; /* expect 0x9f 0xee */ - r = ch341_get_status(dev, priv); + r = ch341_get_status(dev); out: kfree(buffer); return r; @@ -246,10 +216,9 @@ static int ch341_attach(struct usb_serial *serial) if (!priv) return -ENOMEM; - spin_lock_init(&priv->lock); - init_waitqueue_head(&priv->delta_msr_wait); priv->baud_rate = DEFAULT_BAUD_RATE; - priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR; + priv->dtr = 1; + priv->rts = 1; r = ch341_configure(serial->dev, priv); if (r < 0) @@ -262,35 +231,6 @@ error: kfree(priv); return r; } -static void ch341_close(struct tty_struct *tty, struct usb_serial_port *port, - struct file *filp) -{ - struct ch341_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - unsigned int c_cflag; - - dbg("%s - port %d", __func__, port->number); - - /* shutdown our urbs */ - dbg("%s - shutting down urbs", __func__); - usb_kill_urb(port->write_urb); - usb_kill_urb(port->read_urb); - usb_kill_urb(port->interrupt_in_urb); - - if (tty) { - c_cflag = tty->termios->c_cflag; - if (c_cflag & HUPCL) { - /* drop DTR and RTS */ - spin_lock_irqsave(&priv->lock, flags); - priv->line_control = 0; - spin_unlock_irqrestore(&priv->lock, flags); - ch341_set_handshake(port->serial->dev, 0); - } - } - wake_up_interruptible(&priv->delta_msr_wait); -} - - /* open this device, set default parameters */ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) @@ -302,13 +242,14 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port, dbg("ch341_open()"); priv->baud_rate = DEFAULT_BAUD_RATE; - priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR; + priv->dtr = 1; + priv->rts = 1; r = ch341_configure(serial->dev, priv); if (r) goto out; - r = ch341_set_handshake(serial->dev, priv->line_control); + r = ch341_set_handshake(serial->dev, priv); if (r) goto out; @@ -316,16 +257,6 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port, if (r) goto out; - dbg("%s - submitting interrupt urb", __func__); - port->interrupt_in_urb->dev = serial->dev; - r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); - if (r) { - dev_err(&port->dev, "%s - failed submitting interrupt urb," - " error %d\n", __func__, r); - ch341_close(tty, port, NULL); - return -EPROTO; - } - r = usb_serial_generic_open(tty, port, filp); out: return r; @@ -339,194 +270,38 @@ static void ch341_set_termios(struct tty_struct *tty, { struct ch341_private *priv = usb_get_serial_port_data(port); unsigned baud_rate; - unsigned long flags; dbg("ch341_set_termios()"); - if (!tty || !tty->termios) - return; - baud_rate = tty_get_baud_rate(tty); - priv->baud_rate = baud_rate; - - if (baud_rate) { - spin_lock_irqsave(&priv->lock, flags); - priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS); - spin_unlock_irqrestore(&priv->lock, flags); - ch341_set_baudrate(port->serial->dev, priv); - } else { - spin_lock_irqsave(&priv->lock, flags); - priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS); - spin_unlock_irqrestore(&priv->lock, flags); + switch (baud_rate) { + case 2400: + case 4800: + case 9600: + case 19200: + case 38400: + case 115200: + priv->baud_rate = baud_rate; + break; + default: + dbg("Rate %d not supported, using %d", + baud_rate, DEFAULT_BAUD_RATE); + priv->baud_rate = DEFAULT_BAUD_RATE; } - ch341_set_handshake(port->serial->dev, priv->line_control); + ch341_set_baudrate(port->serial->dev, priv); /* Unimplemented: * (cflag & CSIZE) : data bits [5, 8] * (cflag & PARENB) : parity {NONE, EVEN, ODD} * (cflag & CSTOPB) : stop bits [1, 2] */ -} - -static int ch341_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct usb_serial_port *port = tty->driver_data; - struct ch341_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - u8 control; - - spin_lock_irqsave(&priv->lock, flags); - if (set & TIOCM_RTS) - priv->line_control |= CH341_BIT_RTS; - if (set & TIOCM_DTR) - priv->line_control |= CH341_BIT_DTR; - if (clear & TIOCM_RTS) - priv->line_control &= ~CH341_BIT_RTS; - if (clear & TIOCM_DTR) - priv->line_control &= ~CH341_BIT_DTR; - control = priv->line_control; - spin_unlock_irqrestore(&priv->lock, flags); - - return ch341_set_handshake(port->serial->dev, control); -} - -static void ch341_read_int_callback(struct urb *urb) -{ - struct usb_serial_port *port = (struct usb_serial_port *) urb->context; - unsigned char *data = urb->transfer_buffer; - unsigned int actual_length = urb->actual_length; - int status; - - dbg("%s (%d)", __func__, port->number); - - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __func__, - urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", __func__, - urb->status); - goto exit; - } - - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, urb->transfer_buffer); - - if (actual_length >= 4) { - struct ch341_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - priv->line_status = (~(data[2])) & CH341_BITS_MODEM_STAT; - if ((data[1] & CH341_MULT_STAT)) - priv->multi_status_change = 1; - spin_unlock_irqrestore(&priv->lock, flags); - wake_up_interruptible(&priv->delta_msr_wait); - } - -exit: - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) - dev_err(&urb->dev->dev, - "%s - usb_submit_urb failed with result %d\n", - __func__, status); -} - -static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) -{ - struct ch341_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - u8 prevstatus; - u8 status; - u8 changed; - u8 multi_change = 0; - - spin_lock_irqsave(&priv->lock, flags); - prevstatus = priv->line_status; - priv->multi_status_change = 0; - spin_unlock_irqrestore(&priv->lock, flags); - - while (!multi_change) { - interruptible_sleep_on(&priv->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - - spin_lock_irqsave(&priv->lock, flags); - status = priv->line_status; - multi_change = priv->multi_status_change; - spin_unlock_irqrestore(&priv->lock, flags); - - changed = prevstatus ^ status; - - if (((arg & TIOCM_RNG) && (changed & CH341_BIT_RI)) || - ((arg & TIOCM_DSR) && (changed & CH341_BIT_DSR)) || - ((arg & TIOCM_CD) && (changed & CH341_BIT_DCD)) || - ((arg & TIOCM_CTS) && (changed & CH341_BIT_CTS))) { - return 0; - } - prevstatus = status; - } - - return 0; -} - -/*static int ch341_ioctl(struct usb_serial_port *port, struct file *file,*/ -static int ch341_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct usb_serial_port *port = tty->driver_data; - dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd); - - switch (cmd) { - case TIOCMIWAIT: - dbg("%s (%d) TIOCMIWAIT", __func__, port->number); - return wait_modem_info(port, arg); - - default: - dbg("%s not supported = 0x%04x", __func__, cmd); - break; - } - - return -ENOIOCTLCMD; -} - -static int ch341_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct usb_serial_port *port = tty->driver_data; - struct ch341_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - u8 mcr; - u8 status; - unsigned int result; - - dbg("%s (%d)", __func__, port->number); - - spin_lock_irqsave(&priv->lock, flags); - mcr = priv->line_control; - status = priv->line_status; - spin_unlock_irqrestore(&priv->lock, flags); - - result = ((mcr & CH341_BIT_DTR) ? TIOCM_DTR : 0) - | ((mcr & CH341_BIT_RTS) ? TIOCM_RTS : 0) - | ((status & CH341_BIT_CTS) ? TIOCM_CTS : 0) - | ((status & CH341_BIT_DSR) ? TIOCM_DSR : 0) - | ((status & CH341_BIT_RI) ? TIOCM_RI : 0) - | ((status & CH341_BIT_DCD) ? TIOCM_CD : 0); - - dbg("%s - result = %x", __func__, result); - return result; + /* Copy back the old hardware settings */ + tty_termios_copy_hw(tty->termios, old_termios); + /* And re-encode with the new baud */ + tty_encode_baud_rate(tty, baud_rate, baud_rate); } @@ -564,17 +339,12 @@ static struct usb_serial_driver ch341_device = { .owner = THIS_MODULE, .name = "ch341-uart", }, - .id_table = id_table, - .usb_driver = &ch341_driver, - .num_ports = 1, - .open = ch341_open, - .close = ch341_close, - .ioctl = ch341_ioctl, - .set_termios = ch341_set_termios, - .tiocmget = ch341_tiocmget, - .tiocmset = ch341_tiocmset, - .read_int_callback = ch341_read_int_callback, - .attach = ch341_attach, + .id_table = id_table, + .usb_driver = &ch341_driver, + .num_ports = 1, + .open = ch341_open, + .set_termios = ch341_set_termios, + .attach = ch341_attach, }; static int __init ch341_init(void) -- cgit v1.2.3 From f4c1a8379a6d1ba39ac7b87f75d1352ef05cffff Mon Sep 17 00:00:00 2001 From: Werner Cornelius Date: Fri, 16 Jan 2009 21:02:41 +0100 Subject: USB: usb-serial ch341: support for DTR/RTS/CTS commit 664d5df92e88b6ef091048a802b3750f4e989180 upstream. Fixup of Werner Cornelius patch to the ch341 USB-serial driver, which adds: - support all baudrates, not just a hard-coded set - support for controlling DTR, RTS and CTS Features still missing: - character length other than 8 bits - parity settings - break control I adapted his patch for the new usb_serial API introduced in 2.6.25-git8 by Alan Cox on 22 July 2008. Non-compliance to the new API was a reason for refusing a similar patch from Tollef Fog Heen. Usage example by Tollef Fog Heen : TEMPer USB thermometer based on a patch by: From: Tollef Fog Heen * Implement support for all baud rates rather than just a hard coded set. * Make it possible to control status and control lines * Grab a bunch of #defines from FreeBSD to reduce the number of magic numbers in the file Signed-off-by: Werner Cornelius Signed-off-by: Boris Hajduk Cc: Alan Cox Signed-off-by: Tollef Fog Heen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ch341.c | 374 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 302 insertions(+), 72 deletions(-) (limited to 'drivers/usb/serial') diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index d33d39d9b7e..ab4cc277aa6 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -1,5 +1,7 @@ /* * Copyright 2007, Frank A Kingswood + * Copyright 2007, Werner Cornelius + * Copyright 2009, Boris Hajduk * * ch341.c implements a serial port driver for the Winchiphead CH341. * @@ -21,9 +23,39 @@ #include #include -#define DEFAULT_BAUD_RATE 2400 +#define DEFAULT_BAUD_RATE 9600 #define DEFAULT_TIMEOUT 1000 +/* flags for IO-Bits */ +#define CH341_BIT_RTS (1 << 6) +#define CH341_BIT_DTR (1 << 5) + +/******************************/ +/* interrupt pipe definitions */ +/******************************/ +/* always 4 interrupt bytes */ +/* first irq byte normally 0x08 */ +/* second irq byte base 0x7d + below */ +/* third irq byte base 0x94 + below */ +/* fourth irq byte normally 0xee */ + +/* second interrupt byte */ +#define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */ + +/* status returned in third interrupt answer byte, inverted in data + from irq */ +#define CH341_BIT_CTS 0x01 +#define CH341_BIT_DSR 0x02 +#define CH341_BIT_RI 0x04 +#define CH341_BIT_DCD 0x08 +#define CH341_BITS_MODEM_STAT 0x0f /* all bits */ + +/*******************************/ +/* baudrate calculation factor */ +/*******************************/ +#define CH341_BAUDBASE_FACTOR 1532620800 +#define CH341_BAUDBASE_DIVMAX 3 + static int debug; static struct usb_device_id id_table [] = { @@ -34,9 +66,12 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE(usb, id_table); struct ch341_private { - unsigned baud_rate; - u8 dtr; - u8 rts; + spinlock_t lock; /* access lock */ + wait_queue_head_t delta_msr_wait; /* wait queue for modem status */ + unsigned baud_rate; /* set baud rate */ + u8 line_control; /* set line control value RTS/DTR */ + u8 line_status; /* active status of modem control inputs */ + u8 multi_status_change; /* status changed multiple since last call */ }; static int ch341_control_out(struct usb_device *dev, u8 request, @@ -72,37 +107,28 @@ static int ch341_set_baudrate(struct usb_device *dev, { short a, b; int r; + unsigned long factor; + short divisor; dbg("ch341_set_baudrate(%d)", priv->baud_rate); - switch (priv->baud_rate) { - case 2400: - a = 0xd901; - b = 0x0038; - break; - case 4800: - a = 0x6402; - b = 0x001f; - break; - case 9600: - a = 0xb202; - b = 0x0013; - break; - case 19200: - a = 0xd902; - b = 0x000d; - break; - case 38400: - a = 0x6403; - b = 0x000a; - break; - case 115200: - a = 0xcc03; - b = 0x0008; - break; - default: + + if (!priv->baud_rate) return -EINVAL; + factor = (CH341_BAUDBASE_FACTOR / priv->baud_rate); + divisor = CH341_BAUDBASE_DIVMAX; + + while ((factor > 0xfff0) && divisor) { + factor >>= 3; + divisor--; } + if (factor > 0xfff0) + return -EINVAL; + + factor = 0x10000 - factor; + a = (factor & 0xff00) | divisor; + b = factor & 0xff; + r = ch341_control_out(dev, 0x9a, 0x1312, a); if (!r) r = ch341_control_out(dev, 0x9a, 0x0f2c, b); @@ -110,19 +136,18 @@ static int ch341_set_baudrate(struct usb_device *dev, return r; } -static int ch341_set_handshake(struct usb_device *dev, - struct ch341_private *priv) +static int ch341_set_handshake(struct usb_device *dev, u8 control) { - dbg("ch341_set_handshake(%d,%d)", priv->dtr, priv->rts); - return ch341_control_out(dev, 0xa4, - ~((priv->dtr?1<<5:0)|(priv->rts?1<<6:0)), 0); + dbg("ch341_set_handshake(0x%02x)", control); + return ch341_control_out(dev, 0xa4, ~control, 0); } -static int ch341_get_status(struct usb_device *dev) +static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv) { char *buffer; int r; const unsigned size = 8; + unsigned long flags; dbg("ch341_get_status()"); @@ -134,10 +159,15 @@ static int ch341_get_status(struct usb_device *dev) if (r < 0) goto out; - /* Not having the datasheet for the CH341, we ignore the bytes returned - * from the device. Return error if the device did not respond in time. - */ - r = 0; + /* setup the private status if available */ + if (r == 2) { + r = 0; + spin_lock_irqsave(&priv->lock, flags); + priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT; + priv->multi_status_change = 0; + spin_unlock_irqrestore(&priv->lock, flags); + } else + r = -EPROTO; out: kfree(buffer); return r; @@ -180,7 +210,7 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) goto out; /* expect 0xff 0xee */ - r = ch341_get_status(dev); + r = ch341_get_status(dev, priv); if (r < 0) goto out; @@ -192,12 +222,12 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) if (r < 0) goto out; - r = ch341_set_handshake(dev, priv); + r = ch341_set_handshake(dev, priv->line_control); if (r < 0) goto out; /* expect 0x9f 0xee */ - r = ch341_get_status(dev); + r = ch341_get_status(dev, priv); out: kfree(buffer); return r; @@ -216,9 +246,10 @@ static int ch341_attach(struct usb_serial *serial) if (!priv) return -ENOMEM; + spin_lock_init(&priv->lock); + init_waitqueue_head(&priv->delta_msr_wait); priv->baud_rate = DEFAULT_BAUD_RATE; - priv->dtr = 1; - priv->rts = 1; + priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR; r = ch341_configure(serial->dev, priv); if (r < 0) @@ -231,6 +262,35 @@ error: kfree(priv); return r; } +static void ch341_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) +{ + struct ch341_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + unsigned int c_cflag; + + dbg("%s - port %d", __func__, port->number); + + /* shutdown our urbs */ + dbg("%s - shutting down urbs", __func__); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); + usb_kill_urb(port->interrupt_in_urb); + + if (tty) { + c_cflag = tty->termios->c_cflag; + if (c_cflag & HUPCL) { + /* drop DTR and RTS */ + spin_lock_irqsave(&priv->lock, flags); + priv->line_control = 0; + spin_unlock_irqrestore(&priv->lock, flags); + ch341_set_handshake(port->serial->dev, 0); + } + } + wake_up_interruptible(&priv->delta_msr_wait); +} + + /* open this device, set default parameters */ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) @@ -242,14 +302,13 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port, dbg("ch341_open()"); priv->baud_rate = DEFAULT_BAUD_RATE; - priv->dtr = 1; - priv->rts = 1; + priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR; r = ch341_configure(serial->dev, priv); if (r) goto out; - r = ch341_set_handshake(serial->dev, priv); + r = ch341_set_handshake(serial->dev, priv->line_control); if (r) goto out; @@ -257,6 +316,16 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port, if (r) goto out; + dbg("%s - submitting interrupt urb", __func__); + port->interrupt_in_urb->dev = serial->dev; + r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (r) { + dev_err(&port->dev, "%s - failed submitting interrupt urb," + " error %d\n", __func__, r); + ch341_close(tty, port, NULL); + return -EPROTO; + } + r = usb_serial_generic_open(tty, port, filp); out: return r; @@ -270,38 +339,194 @@ static void ch341_set_termios(struct tty_struct *tty, { struct ch341_private *priv = usb_get_serial_port_data(port); unsigned baud_rate; + unsigned long flags; dbg("ch341_set_termios()"); + if (!tty || !tty->termios) + return; + baud_rate = tty_get_baud_rate(tty); - switch (baud_rate) { - case 2400: - case 4800: - case 9600: - case 19200: - case 38400: - case 115200: - priv->baud_rate = baud_rate; - break; - default: - dbg("Rate %d not supported, using %d", - baud_rate, DEFAULT_BAUD_RATE); - priv->baud_rate = DEFAULT_BAUD_RATE; + priv->baud_rate = baud_rate; + + if (baud_rate) { + spin_lock_irqsave(&priv->lock, flags); + priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS); + spin_unlock_irqrestore(&priv->lock, flags); + ch341_set_baudrate(port->serial->dev, priv); + } else { + spin_lock_irqsave(&priv->lock, flags); + priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS); + spin_unlock_irqrestore(&priv->lock, flags); } - ch341_set_baudrate(port->serial->dev, priv); + ch341_set_handshake(port->serial->dev, priv->line_control); /* Unimplemented: * (cflag & CSIZE) : data bits [5, 8] * (cflag & PARENB) : parity {NONE, EVEN, ODD} * (cflag & CSTOPB) : stop bits [1, 2] */ +} + +static int ch341_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct usb_serial_port *port = tty->driver_data; + struct ch341_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + u8 control; + + spin_lock_irqsave(&priv->lock, flags); + if (set & TIOCM_RTS) + priv->line_control |= CH341_BIT_RTS; + if (set & TIOCM_DTR) + priv->line_control |= CH341_BIT_DTR; + if (clear & TIOCM_RTS) + priv->line_control &= ~CH341_BIT_RTS; + if (clear & TIOCM_DTR) + priv->line_control &= ~CH341_BIT_DTR; + control = priv->line_control; + spin_unlock_irqrestore(&priv->lock, flags); + + return ch341_set_handshake(port->serial->dev, control); +} + +static void ch341_read_int_callback(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *) urb->context; + unsigned char *data = urb->transfer_buffer; + unsigned int actual_length = urb->actual_length; + int status; + + dbg("%s (%d)", __func__, port->number); + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __func__, + urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __func__, + urb->status); + goto exit; + } + + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, urb->transfer_buffer); + + if (actual_length >= 4) { + struct ch341_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + priv->line_status = (~(data[2])) & CH341_BITS_MODEM_STAT; + if ((data[1] & CH341_MULT_STAT)) + priv->multi_status_change = 1; + spin_unlock_irqrestore(&priv->lock, flags); + wake_up_interruptible(&priv->delta_msr_wait); + } + +exit: + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) + dev_err(&urb->dev->dev, + "%s - usb_submit_urb failed with result %d\n", + __func__, status); +} + +static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) +{ + struct ch341_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + u8 prevstatus; + u8 status; + u8 changed; + u8 multi_change = 0; + + spin_lock_irqsave(&priv->lock, flags); + prevstatus = priv->line_status; + priv->multi_status_change = 0; + spin_unlock_irqrestore(&priv->lock, flags); + + while (!multi_change) { + interruptible_sleep_on(&priv->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + + spin_lock_irqsave(&priv->lock, flags); + status = priv->line_status; + multi_change = priv->multi_status_change; + spin_unlock_irqrestore(&priv->lock, flags); + + changed = prevstatus ^ status; + + if (((arg & TIOCM_RNG) && (changed & CH341_BIT_RI)) || + ((arg & TIOCM_DSR) && (changed & CH341_BIT_DSR)) || + ((arg & TIOCM_CD) && (changed & CH341_BIT_DCD)) || + ((arg & TIOCM_CTS) && (changed & CH341_BIT_CTS))) { + return 0; + } + prevstatus = status; + } + + return 0; +} + +/*static int ch341_ioctl(struct usb_serial_port *port, struct file *file,*/ +static int ch341_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct usb_serial_port *port = tty->driver_data; + dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd); + + switch (cmd) { + case TIOCMIWAIT: + dbg("%s (%d) TIOCMIWAIT", __func__, port->number); + return wait_modem_info(port, arg); + + default: + dbg("%s not supported = 0x%04x", __func__, cmd); + break; + } + + return -ENOIOCTLCMD; +} + +static int ch341_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct usb_serial_port *port = tty->driver_data; + struct ch341_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + u8 mcr; + u8 status; + unsigned int result; + + dbg("%s (%d)", __func__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + mcr = priv->line_control; + status = priv->line_status; + spin_unlock_irqrestore(&priv->lock, flags); + + result = ((mcr & CH341_BIT_DTR) ? TIOCM_DTR : 0) + | ((mcr & CH341_BIT_RTS) ? TIOCM_RTS : 0) + | ((status & CH341_BIT_CTS) ? TIOCM_CTS : 0) + | ((status & CH341_BIT_DSR) ? TIOCM_DSR : 0) + | ((status & CH341_BIT_RI) ? TIOCM_RI : 0) + | ((status & CH341_BIT_DCD) ? TIOCM_CD : 0); + + dbg("%s - result = %x", __func__, result); - /* Copy back the old hardware settings */ - tty_termios_copy_hw(tty->termios, old_termios); - /* And re-encode with the new baud */ - tty_encode_baud_rate(tty, baud_rate, baud_rate); + return result; } @@ -339,12 +564,17 @@ static struct usb_serial_driver ch341_device = { .owner = THIS_MODULE, .name = "ch341-uart", }, - .id_table = id_table, - .usb_driver = &ch341_driver, - .num_ports = 1, - .open = ch341_open, - .set_termios = ch341_set_termios, - .attach = ch341_attach, + .id_table = id_table, + .usb_driver = &ch341_driver, + .num_ports = 1, + .open = ch341_open, + .close = ch341_close, + .ioctl = ch341_ioctl, + .set_termios = ch341_set_termios, + .tiocmget = ch341_tiocmget, + .tiocmset = ch341_tiocmset, + .read_int_callback = ch341_read_int_callback, + .attach = ch341_attach, }; static int __init ch341_init(void) -- cgit v1.2.3 From d45e230bf03850f17394a760dfa003d986a67729 Mon Sep 17 00:00:00 2001 From: "Dr. Greg Wettstein" Date: Sat, 11 Apr 2009 09:12:08 -0500 Subject: USB device codes for Motorola phone. The v950 appears to be a ruggedized version of the Motorola Razor phone. Tethering to the phone to use it in 'phone as modem' mode requires the use of the specialized moto-modem driver which layers over the usb-serial driver. Support for the v950 was added simply adding the device ID's for the phone. Signed-off-by: Dr. Greg Wettstein Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/moto_modem.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb/serial') diff --git a/drivers/usb/serial/moto_modem.c b/drivers/usb/serial/moto_modem.c index 2e8e05462ef..b66b71ccd12 100644 --- a/drivers/usb/serial/moto_modem.c +++ b/drivers/usb/serial/moto_modem.c @@ -25,6 +25,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x05c6, 0x3197) }, /* unknown Motorola phone */ { USB_DEVICE(0x0c44, 0x0022) }, /* unknown Mororola phone */ { USB_DEVICE(0x22b8, 0x2a64) }, /* Motorola KRZR K1m */ + { USB_DEVICE(0x22b8, 0x2c64) }, /* Motorola V950 phone */ { }, }; MODULE_DEVICE_TABLE(usb, id_table); -- cgit v1.2.3