lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <200709211306.29232.Werner.Cornelius@cornelius-consult.de>
Date:	Fri, 21 Sep 2007 13:06:29 +0200
From:	Werner Cornelius <Werner.Cornelius@...nelius-consult.de>
To:	Greg KH <greg@...ah.com>
Cc:	linux-kernel@...r.kernel.org, mtosatti@...hat.com
Subject: [PATCH] Re: Serial USB-driver for Winchiphead CH340/41 chip

Hello,

attached you will find the patch against the 2.6.23-rc6-mm1

Changed fetaures:

1. All baudrates possible (dynamic baudfactor calculation)
2. Added support of modem control and status lines.

Still missing (due to lack of an usb sniffer):

1. Break control
2. Parity 

Werner

--- linux-2.6.23-rc6-mm1/drivers/usb/serial/ch341.c	2007-09-21 
09:56:56.000000000 +0200
+++ develop/drivers/usb/serial/ch341.c	2007-09-21 10:00:26.000000000 +0200
@@ -1,5 +1,9 @@
 /*
- * Copyright 2007, Frank A Kingswood <frank@...gswood-consulting.co.uk>
+ * Copyright 2007, Frank A Kingswood <frank <at> kingswood-consulting.co.uk>
+ *
+ * Copyright 2007, Werner Cornelius <werner <at> cornelius-consult.de>
+ * for changes/extenions regarding universal baud rate capability and modem
+ * line control/status routines. 
  *
  * ch341.c implements a serial port driver for the Winchiphead CH341.
  *
@@ -21,327 +25,548 @@
 #include <linux/usb/serial.h>
 #include <linux/serial.h>
 
-#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 [] = {
-	{ USB_DEVICE(0x4348, 0x5523) },
-	{ },
+       { USB_DEVICE(0x4348, 0x5523) },
+       { },
 };
 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,
-			     u16 value, u16 index)
+                            u16 value, u16 index)
 {
-	int r;
-	dbg("ch341_control_out(%02x,%02x,%04x,%04x)", USB_DIR_OUT|0x40,
-		(int)request, (int)value, (int)index);
+       int r;
+       dbg("ch341_control_out(%02x,%02x,%04x,%04x)", USB_DIR_OUT|0x40,
+               (int)request, (int)value, (int)index);
 
-	r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
-			    USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
-			    value, index, NULL, 0, DEFAULT_TIMEOUT);
+       r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
+                           USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+                           value, index, NULL, 0, DEFAULT_TIMEOUT);
 
-	return r;
+       return r;
 }
 
 static int ch341_control_in(struct usb_device *dev,
-			    u8 request, u16 value, u16 index,
-			    char *buf, unsigned bufsize)
+                           u8 request, u16 value, u16 index,
+                           char *buf, unsigned bufsize)
 {
-	int r;
-	dbg("ch341_control_in(%02x,%02x,%04x,%04x,%p,%u)", USB_DIR_IN|0x40,
-		(int)request, (int)value, (int)index, buf, (int)bufsize);
-
-	r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
-			    USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
-			    value, index, buf, bufsize, DEFAULT_TIMEOUT);
-	return r;
-}
-
-static int ch341_set_baudrate(struct usb_device *dev,
-			      struct ch341_private *priv)
-{
-	short a, b;
-	int r;
-
-	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:
-		return -EINVAL;
-	}
+       int r;
+       dbg("ch341_control_in(%02x,%02x,%04x,%04x,%p,%u)", USB_DIR_IN|0x40,
+               (int)request, (int)value, (int)index, buf, (int)bufsize);
+
+       r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
+                           USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+                           value, index, buf, bufsize, DEFAULT_TIMEOUT);
+       return r;
+}
+
+int ch341_set_baudrate(struct usb_device *dev, struct ch341_private *priv)
+{
+       short a, b;
+       int r;
+       unsigned long factor;
+       short divisor;
+
+       dbg("ch341_set_baudrate(%d)", priv->baud_rate);
 
-	r = ch341_control_out(dev, 0x9a, 0x1312, a);
-	if (!r)
-		r = ch341_control_out(dev, 0x9a, 0x0f2c, b);
+       if (!priv->baud_rate)
+	   return -EINVAL;
+       
+       factor = (CH341_BAUDBASE_FACTOR / priv->baud_rate);
+       divisor = CH341_BAUDBASE_DIVMAX;
 
-	return r;
+       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);
+
+       return r;
 }
 
-static int ch341_set_handshake(struct usb_device *dev,
-			       struct ch341_private *priv)
+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)
+int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
 {
-	char *buffer;
-	int r;
-	const unsigned size = 8;
+       char *buffer;
+       int r;
+       const unsigned size = 8;
+       unsigned long flags;
 
-	dbg("ch341_get_status()");
+       dbg("ch341_get_status()");
 
-	buffer = kmalloc(size, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
+       buffer = kmalloc(size, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
 
-	r = ch341_control_in(dev, 0x95, 0x0706, 0, buffer, size);
-	if ( r < 0)
-		goto out;
+       r = ch341_control_in(dev, 0x95, 0x0706, 0, buffer, size);
+       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;
+out:   kfree(buffer);
+       return r;
 }
 
 /* -------------------------------------------------------------------------- 
*/
 
-static int ch341_configure(struct usb_device *dev, struct ch341_private 
*priv)
+int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
 {
-	char *buffer;
-	int r;
-	const unsigned size = 8;
-
-	dbg("ch341_configure()");
-
-	buffer = kmalloc(size, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
-
-	/* expect two bytes 0x27 0x00 */
-	r = ch341_control_in(dev, 0x5f, 0, 0, buffer, size);
-	if (r < 0)
-		goto out;
-
-	r = ch341_control_out(dev, 0xa1, 0, 0);
-	if (r < 0)
-		goto out;
-
-	r = ch341_set_baudrate(dev, priv);
-	if (r < 0)
-		goto out;
-
-	/* expect two bytes 0x56 0x00 */
-	r = ch341_control_in(dev, 0x95, 0x2518, 0, buffer, size);
-	if (r < 0)
-		goto out;
-
-	r = ch341_control_out(dev, 0x9a, 0x2518, 0x0050);
-	if (r < 0)
-		goto out;
-
-	/* expect 0xff 0xee */
-	r = ch341_get_status(dev);
-	if (r < 0)
-		goto out;
-
-	r = ch341_control_out(dev, 0xa1, 0x501f, 0xd90a);
-	if (r < 0)
-		goto out;
-
-	r = ch341_set_baudrate(dev, priv);
-	if (r < 0)
-		goto out;
-
-	r = ch341_set_handshake(dev, priv);
-	if (r < 0)
-		goto out;
+       char *buffer;
+       int r;
+       const unsigned size = 8;
+
+       dbg("ch341_configure()");
+
+       buffer = kmalloc(size, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+
+       /* expect two bytes 0x27 0x00 */
+       r = ch341_control_in(dev, 0x5f, 0, 0, buffer, size);
+       if (r < 0)
+               goto out;
+
+       r = ch341_control_out(dev, 0xa1, 0, 0);
+       if (r < 0)
+               goto out;
+
+       r = ch341_set_baudrate(dev, priv);
+       if (r < 0)
+               goto out;
+
+       /* expect two bytes 0x56 0x00 */
+       r = ch341_control_in(dev, 0x95, 0x2518, 0, buffer, size);
+       if (r < 0)
+               goto out;
+
+       r = ch341_control_out(dev, 0x9a, 0x2518, 0x0050);
+       if (r < 0)
+               goto out;
+
+       /* expect 0xff 0xee */
+       r = ch341_get_status(dev, priv);
+       if (r < 0)
+               goto out;
+
+       r = ch341_control_out(dev, 0xa1, 0x501f, 0xd90a);
+       if (r < 0)
+               goto out;
+
+       r = ch341_set_baudrate(dev, priv);
+       if (r < 0)
+               goto out;
+
+       r = ch341_set_handshake(dev, priv->line_control);
+       if (r < 0)
+               goto out;
 
-	/* expect 0x9f 0xee */
-	r = ch341_get_status(dev);
+       /* expect 0x9f 0xee */
+       r = ch341_get_status(dev, priv);
 
-out:	kfree(buffer);
-	return r;
+out:   kfree(buffer);
+       return r;
 }
 
 /* allocate private data */
 static int ch341_attach(struct usb_serial *serial)
 {
-	struct ch341_private *priv;
-	int r;
+       struct ch341_private *priv;
+       int r;
 
-	dbg("ch341_attach()");
+       dbg("ch341_attach()");
 
-	/* private data */
-	priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
+       /* private data */
+       priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
 
-	priv->baud_rate = DEFAULT_BAUD_RATE;
-	priv->dtr = 1;
-	priv->rts = 1;
+       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;
 
-	r = ch341_configure(serial->dev, priv);
-	if (r < 0)
-		goto error;
+       r = ch341_configure(serial->dev, priv);
+       if (r < 0)
+               goto error;
 
-	usb_set_serial_port_data(serial->port[0], priv);
-	return 0;
+       usb_set_serial_port_data(serial->port[0], priv);
+       return 0;
 
-error:	kfree(priv);
-	return r;
+error: kfree(priv);
+       return r;
 }
 
-/* open this device, set default parameters */
-static int ch341_open(struct usb_serial_port *port, struct file *filp)
+static void ch341_close(struct usb_serial_port *port, struct file *filp)
 {
-	struct usb_serial *serial = port->serial;
-	struct ch341_private *priv = usb_get_serial_port_data(serial->port[0]);
-	int r;
-
-	dbg("ch341_open()");
-
-	priv->baud_rate = DEFAULT_BAUD_RATE;
-	priv->dtr = 1;
-	priv->rts = 1;
+	struct ch341_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int c_cflag;
 
-	r = ch341_configure(serial->dev, priv);
-	if (r)
-		goto out;
+	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	r = ch341_set_handshake(serial->dev, priv);
-	if (r)
-		goto out;
+	/* shutdown our urbs */
+	dbg("%s - shutting down urbs", __FUNCTION__);
+	usb_kill_urb(port->write_urb);
+	usb_kill_urb(port->read_urb);
+	usb_kill_urb(port->interrupt_in_urb);
+
+	if (port->tty) {
+		c_cflag = port->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);
+}
 
-	r = ch341_set_baudrate(serial->dev, priv);
-	if (r)
-		goto out;
+/* open this device, set default parameters */
+static int ch341_open(struct usb_serial_port *port, struct file *filp)
+{
+       struct usb_serial *serial = port->serial;
+       struct ch341_private *priv = 
usb_get_serial_port_data(serial->port[0]);
+       int r;
+
+       dbg("ch341_open()");
+
+       priv->baud_rate = DEFAULT_BAUD_RATE;
+       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->line_control);
+       if (r)
+               goto out;
+
+       r = ch341_set_baudrate(serial->dev, priv);
+       if (r)
+               goto out;
+
+       dbg("%s - submitting interrupt urb", __FUNCTION__);
+       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", __FUNCTION__, r);
+	   ch341_close(port, NULL);
+	   return -EPROTO;
+       }
 
-	r = usb_serial_generic_open(port, filp);
+       r = usb_serial_generic_open(port, filp);
 
-out:	return r;
+out:   return r;
 }
 
 /* Old_termios contains the original termios settings and
  * tty->termios contains the new setting to be used.
  */
 static void ch341_set_termios(struct usb_serial_port *port,
-			      struct ktermios *old_termios)
+                             struct ktermios *old_termios)
 {
-	struct ch341_private *priv = usb_get_serial_port_data(port);
-	struct tty_struct *tty = port->tty;
-	unsigned baud_rate;
+       struct ch341_private *priv = usb_get_serial_port_data(port);
+       struct tty_struct *tty = port->tty;
+       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);	   
+       }
+       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]
+        */
+}
 
-	dbg("ch341_set_termios()");
+static int ch341_tiocmset(struct usb_serial_port *port, struct file *file,
+			  unsigned int set, unsigned int clear)
+{
+	struct ch341_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	u8 control;
 
-	if (!tty || !tty->termios)
+	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)", __FUNCTION__, 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", __FUNCTION__,
+		    urb->status);
 		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __FUNCTION__,
+		    urb->status);
+		goto exit;
+	}
 
-	baud_rate = tty_get_baud_rate(tty);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+			      urb->actual_length, urb->transfer_buffer);
 
-	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;
+	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",
+			__FUNCTION__, 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,
+			unsigned int cmd, unsigned long arg)
+{
+	dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd);
+
+	switch (cmd) {
+		case TIOCMIWAIT:
+			dbg("%s (%d) TIOCMIWAIT", __FUNCTION__,  port->number);
+			return wait_modem_info(port, arg);
+
+		default:
+			dbg("%s not supported = 0x%04x", __FUNCTION__, cmd);
+			break;
 	}
 
-	ch341_set_baudrate(port->serial->dev, priv);
+	return -ENOIOCTLCMD;
+}
+
+static int ch341_tiocmget(struct usb_serial_port *port, struct file *file)
+{
+	struct ch341_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	u8 mcr;
+	u8 status;
+	unsigned int result;
+
+	dbg("%s (%d)", __FUNCTION__, 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", __FUNCTION__, result);
 
-	/* Unimplemented:
-	 * (cflag & CSIZE) : data bits [5, 8]
-	 * (cflag & PARENB) : parity {NONE, EVEN, ODD}
-	 * (cflag & CSTOPB) : stop bits [1, 2]
-	 */
+	return result;
 }
 
 static struct usb_driver ch341_driver = {
-	.name		= "ch341",
-	.probe		= usb_serial_probe,
-	.disconnect	= usb_serial_disconnect,
-	.id_table	= id_table,
-	.no_dynamic_id	= 1,
+       .name           = "ch341",
+       .probe          = usb_serial_probe,
+       .disconnect     = usb_serial_disconnect,
+       .id_table       = id_table,
+       .no_dynamic_id  = 1,
 };
 
 static struct usb_serial_driver ch341_device = {
-	.driver = {
-		.owner	= THIS_MODULE,
-		.name	= "ch341-uart",
-	},
-	.id_table         = id_table,
-	.usb_driver       = &ch341_driver,
-	.num_interrupt_in = NUM_DONT_CARE,
-	.num_bulk_in      = 1,
-	.num_bulk_out     = 1,
-	.num_ports        = 1,
-	.open             = ch341_open,
-	.set_termios      = ch341_set_termios,
-	.attach           = ch341_attach,
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "ch341-uart",
+       },
+       .id_table         = id_table,
+       .usb_driver       = &ch341_driver,
+       .num_interrupt_in = 1, //NUM_DONT_CARE,
+       .num_bulk_in      = 1,
+       .num_bulk_out     = 1,
+       .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)
 {
-	int retval;
+       int retval;
 
-	retval = usb_serial_register(&ch341_device);
-	if (retval)
-		return retval;
-	retval = usb_register(&ch341_driver);
-	if (retval)
-		usb_serial_deregister(&ch341_device);
-	return retval;
+       retval = usb_serial_register(&ch341_device);
+       if (retval)
+               return retval;
+       retval = usb_register(&ch341_driver);
+       if (retval)
+               usb_serial_deregister(&ch341_device);
+       return retval;
 }
 
 static void __exit ch341_exit(void)
 {
-	usb_deregister(&ch341_driver);
-	usb_serial_deregister(&ch341_device);
+       usb_deregister(&ch341_driver);
+       usb_serial_deregister(&ch341_device);
 }
 
 module_init(ch341_init);
--- linux-2.6.23-rc6-mm1/Documentation/usb/usb-serial.txt	2007-09-21 
09:56:55.000000000 +0200
+++ develop/Documentation/usb/usb-serial.txt	2007-09-21 10:03:18.000000000 
+0200
@@ -437,6 +437,8 @@
   The manufacturer's website: http://www.winchiphead.com/.
   For any questions or problems with this driver, please contact
   frank@...gswood-consulting.co.uk.
+  Extensions for universal baudrate settings and modem status/control have
+  been added by werner <at> cornelius-consult.de.
 
 
 Generic Serial driver


Am Donnerstag, 20. September 2007 02:03 schrieb Greg KH:
> On Fri, Sep 14, 2007 at 02:38:55PM +0200, Werner Cornelius wrote:
> > Hello,
> >
> > I know that there has been a patch for the Winchiphead CH340/41 USB to
> > serial converter chips on the net, but they have been implemented with
> > only a basic feature and limited baudrates due to the lack of any
> > datasheets.
> >
> > I picked these patches up and added all common baudrates as well as the
> > complete modem control and status handling to be able of using this
> > adapter in nearly all applications.
>
> Care to just send me a patch against the -mm tree, for these new
> features?  The driver, with the proper attribution from the original
> author, is already in that tree and will be going into the 2.6.24
> release.
>
> thanks,
>
> greg k-h

Signed-off-by: Werner Cornelius <Werner.Cornelius@...nelius-consult.de>
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ