<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">From: Greg KH &lt;greg@kroah.com&gt;
To: Alan Cox &lt;alan@lxorguk.ukuu.org.uk&gt;
Cc: linux-usb-devel@lists.sourceforge.net
Subject: [PATCH 06 of 15] USB Serial ftdi_sio driver updated

This patch updates the ftdi_sio driver.


diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
--- a/drivers/usb/serial/ftdi_sio.c	Thu Jan  3 21:41:54 2002
+++ b/drivers/usb/serial/ftdi_sio.c	Thu Jan  3 21:41:54 2002
@@ -1,7 +1,7 @@
 /*
  * USB FTDI SIO driver
  *
- * 	Copyright (C) 1999, 2000
+ * 	Copyright (C) 1999 - 2001
  * 	    Greg Kroah-Hartman (greg@kroah.com)
  *          Bill Ryder (bryder@sgi.com)
  *
@@ -12,9 +12,21 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  *
- * See http://reality.sgi.com/bryder_wellington/ftdi_sio for upto date testing info
+ * See http://ftdi-usb-sio.sourceforge.net for upto date testing info
  *     and extra documentation
  * 
+ * (04/Nov/2001) Bill Ryder
+ *     Fixed bug in read_bulk_callback where incorrect urb buffer was used.
+ *     cleaned up write offset calculation
+ *     added write_room since default values can be incorrect for sio
+ *     changed write_bulk_callback to use same queue_task as other drivers
+ *       (the previous version caused panics)
+ *     Removed port iteration code since the device only has one I/O port and it 
+ *       was wrong anyway.
+ * 
+ * (31/May/2001) gkh
+ *	switched from using spinlock to a semaphore, which fixes lots of problems.
+ *
  * (23/May/2001)   Bill Ryder
  *     Added runtime debug patch (thanx Tyson D Sawyer).
  *     Cleaned up comments for 8U232
@@ -91,7 +103,6 @@
 #include &lt;linux/module.h&gt;
 #include &lt;linux/spinlock.h&gt;
 #include &lt;linux/usb.h&gt;
-
 #ifdef CONFIG_USB_SERIAL_DEBUG
 	static int debug = 1;
 #else
@@ -105,7 +116,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.1.0"
+#define DRIVER_VERSION "v1.2.0"
 #define DRIVER_AUTHOR "Greg Kroah-Hartman &lt;greg@kroah.com&gt;, Bill Ryder &lt;bryder@sgi.com&gt;"
 #define DRIVER_DESC "USB FTDI RS232 Converters Driver"
 
@@ -114,6 +125,7 @@
 struct ftdi_private {
 	ftdi_type_t ftdi_type;
 	__u16 last_set_data_urb_value ; /* the last data state set - needed for doing a break */
+        int write_offset;
 };
 /* function prototypes for a FTDI serial converter */
 static int  ftdi_sio_startup		(struct usb_serial *serial);
@@ -122,6 +134,7 @@
 static int  ftdi_sio_open		(struct usb_serial_port *port, struct file *filp);
 static void ftdi_sio_close		(struct usb_serial_port *port, struct file *filp);
 static int  ftdi_sio_write		(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+static int  ftdi_sio_write_room		(struct usb_serial_port *port);
 static void ftdi_sio_write_bulk_callback (struct urb *urb);
 static void ftdi_sio_read_bulk_callback	(struct urb *urb);
 static void ftdi_sio_set_termios	(struct usb_serial_port *port, struct termios * old);
@@ -149,6 +162,7 @@
 	open:			ftdi_sio_open,
 	close:			ftdi_sio_close,
 	write:			ftdi_sio_write,
+	write_room:		ftdi_sio_write_room,
 	read_bulk_callback:	ftdi_sio_read_bulk_callback,
 	write_bulk_callback:	ftdi_sio_write_bulk_callback,
 	ioctl:			ftdi_sio_ioctl,
@@ -158,7 +172,7 @@
         shutdown:               ftdi_sio_shutdown,
 };
 
-struct usb_serial_device_type ftdi_8U232AM_device = {
+static struct usb_serial_device_type ftdi_8U232AM_device = {
 	name:			"FTDI 8U232AM",
 	idVendor:		&amp;ftdi_vendor_id,	/* the FTDI vendor ID */
 	idProduct:		&amp;ftdi_8U232AM_product_id,
@@ -172,6 +186,7 @@
 	open:			ftdi_sio_open,
 	close:			ftdi_sio_close,
 	write:			ftdi_sio_write,
+	write_room:		ftdi_sio_write_room,
 	read_bulk_callback:	ftdi_sio_read_bulk_callback,
 	write_bulk_callback:	ftdi_sio_write_bulk_callback,
 	ioctl:			ftdi_sio_ioctl,
@@ -226,15 +241,15 @@
 {
 	struct ftdi_private *priv;
 	
-	init_waitqueue_head(&amp;serial-&gt;port[0].write_wait);
 	
 	priv = serial-&gt;port-&gt;private = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL);
 	if (!priv){
-		err(__FUNCTION__"- kmalloc(%d) failed.", sizeof(struct ftdi_private));
+		err(__FUNCTION__"- kmalloc(%Zd) failed.", sizeof(struct ftdi_private));
 		return -ENOMEM;
 	}
 
 	priv-&gt;ftdi_type = sio;
+	priv-&gt;write_offset = 1;
 	
 	return (0);
 }
@@ -244,15 +259,15 @@
 {
 	struct ftdi_private *priv;
  
-	init_waitqueue_head(&amp;serial-&gt;port[0].write_wait);
 
 	priv = serial-&gt;port-&gt;private = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL);
 	if (!priv){
-		err(__FUNCTION__"- kmalloc(%d) failed.", sizeof(struct ftdi_private));
+		err(__FUNCTION__"- kmalloc(%Zd) failed.", sizeof(struct ftdi_private));
 		return -ENOMEM;
 	}
 
 	priv-&gt;ftdi_type = F8U232AM;
+	priv-&gt;write_offset = 0;
 	
 	return (0);
 }
@@ -262,13 +277,14 @@
 	
 	dbg (__FUNCTION__);
 
-	/* Close ports if they are open */
+
+	/* stop reads and writes on all ports */
 	while (serial-&gt;port[0].open_count &gt; 0) {
 		ftdi_sio_close (&amp;serial-&gt;port[0], NULL);
 	}
-	if (serial-&gt;port-&gt;private){
-		kfree(serial-&gt;port-&gt;private);
-		serial-&gt;port-&gt;private = NULL;
+	if (serial-&gt;port[0].private){
+		kfree(serial-&gt;port[0].private);
+		serial-&gt;port[0].private = NULL;
 	}
 }
 
@@ -278,13 +294,12 @@
 { /* ftdi_sio_open */
 	struct termios tmp_termios;
 	struct usb_serial *serial = port-&gt;serial;
-	unsigned long flags;	/* Used for spinlock */
-	int result;
+	int result = 0;
 	char buf[1]; /* Needed for the usb_control_msg I think */
 
 	dbg(__FUNCTION__);
 
-	spin_lock_irqsave (&amp;port-&gt;port_lock, flags);
+	down (&amp;port-&gt;sem);
 	
 	MOD_INC_USE_COUNT;
 	++port-&gt;open_count;
@@ -292,8 +307,6 @@
 	if (!port-&gt;active){
 		port-&gt;active = 1;
 
-		spin_unlock_irqrestore (&amp;port-&gt;port_lock, flags);
-
 		/* This will push the characters through immediately rather 
 		   than queue a task to deliver them */
 		port-&gt;tty-&gt;low_latency = 1;
@@ -329,63 +342,61 @@
 		result = usb_submit_urb(port-&gt;read_urb);
 		if (result)
 			err(__FUNCTION__ " - failed submitting read urb, error %d", result);
-	} else { /* the port was already active - so no initialisation is done */
-		spin_unlock_irqrestore (&amp;port-&gt;port_lock, flags);
 	}
 
-	return (0);
+	up (&amp;port-&gt;sem);
+	return result;
 } /* ftdi_sio_open */
 
 
 static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp)
 { /* ftdi_sio_close */
-	struct usb_serial *serial = port-&gt;serial;
+	struct usb_serial *serial = port-&gt;serial; /* Checked in usbserial.c */
 	unsigned int c_cflag = port-&gt;tty-&gt;termios-&gt;c_cflag;
 	char buf[1];
-	unsigned long flags;
 
 	dbg( __FUNCTION__);
 
-	spin_lock_irqsave (&amp;port-&gt;port_lock, flags);
+	down (&amp;port-&gt;sem);
 	--port-&gt;open_count;
 
 	if (port-&gt;open_count &lt;= 0) {
-		spin_unlock_irqrestore (&amp;port-&gt;port_lock, flags);
-		if (c_cflag &amp; HUPCL){
-			/* Disable flow control */
-			if (usb_control_msg(serial-&gt;dev, usb_sndctrlpipe(serial-&gt;dev, 0),
-					    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
-					    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
-					    0, 0, 
-					    buf, 0, WDR_TIMEOUT) &lt; 0) {
-				err("error from flowcontrol urb");
-			}	    
-
-			/* drop DTR */
-			if (set_dtr(serial-&gt;dev, usb_sndctrlpipe(serial-&gt;dev, 0), LOW) &lt; 0){
-				err("Error from DTR LOW urb");
-			}
-			/* drop RTS */
-			if (set_rts(serial-&gt;dev, usb_sndctrlpipe(serial-&gt;dev, 0),LOW) &lt; 0) {
-				err("Error from RTS LOW urb");
-			}	
-		} /* Note change no line is hupcl is off */
-
-		/* shutdown our bulk reads and writes */
-		usb_unlink_urb (port-&gt;write_urb);
-		usb_unlink_urb (port-&gt;read_urb);
+		if (serial-&gt;dev) {
+			if (c_cflag &amp; HUPCL){
+				/* Disable flow control */
+				if (usb_control_msg(serial-&gt;dev, 
+						    usb_sndctrlpipe(serial-&gt;dev, 0),
+						    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+						    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+						    0, 0, buf, 0, WDR_TIMEOUT) &lt; 0) {
+					err("error from flowcontrol urb");
+				}	    
+
+				/* drop DTR */
+				if (set_dtr(serial-&gt;dev, usb_sndctrlpipe(serial-&gt;dev, 0), LOW) &lt; 0){
+					err("Error from DTR LOW urb");
+				}
+				/* drop RTS */
+				if (set_rts(serial-&gt;dev, usb_sndctrlpipe(serial-&gt;dev, 0),LOW) &lt; 0) {
+					err("Error from RTS LOW urb");
+				}	
+			} /* Note change no line is hupcl is off */
+
+			/* shutdown our bulk reads and writes */
+			/* ***CHECK*** behaviour when there is nothing queued */
+			usb_unlink_urb (port-&gt;write_urb);
+			usb_unlink_urb (port-&gt;read_urb);
+		}
 		port-&gt;active = 0;
 		port-&gt;open_count = 0;
 	} else {  
-		spin_unlock_irqrestore (&amp;port-&gt;port_lock, flags);
-
 		/* Send a HUP if necessary */
 		if (!(port-&gt;tty-&gt;termios-&gt;c_cflag &amp; CLOCAL)){
 			tty_hangup(port-&gt;tty);
 		}
-		
 	}
 
+	up (&amp;port-&gt;sem);
 	MOD_DEC_USE_COUNT;
 
 } /* ftdi_sio_close */
@@ -402,10 +413,9 @@
 { /* ftdi_sio_write */
 	struct usb_serial *serial = port-&gt;serial;
 	struct ftdi_private *priv = (struct ftdi_private *)port-&gt;private;
+	unsigned char *first_byte = port-&gt;write_urb-&gt;transfer_buffer;
 	int data_offset ;
-	int rc; 
 	int result;
-	DECLARE_WAITQUEUE(wait, current);
 	
 	dbg(__FUNCTION__ " port %d, %d bytes", port-&gt;number, count);
 
@@ -414,87 +424,63 @@
 		return 0;
 	}
 	
-	if (priv-&gt;ftdi_type == sio){
-		data_offset = 1;
-	} else {
-		data_offset = 0;
-	}
+	data_offset = priv-&gt;write_offset;
         dbg("data_offset set to %d",data_offset);
 
-	/* only do something if we have a bulk out endpoint */
-	if (serial-&gt;num_bulk_out) {
-		unsigned char *first_byte = port-&gt;write_urb-&gt;transfer_buffer;
-
-		/* Was seeing a race here, got a read callback, then write callback before
-		   hitting interuptible_sleep_on  - so wrapping in a wait_queue */
-
-		add_wait_queue(&amp;port-&gt;write_wait, &amp;wait);
-		set_current_state (TASK_INTERRUPTIBLE);
-		while (port-&gt;write_urb-&gt;status == -EINPROGRESS) {
-			dbg(__FUNCTION__ " write in progress - retrying");
-			if (signal_pending(current)) {
-				current-&gt;state = TASK_RUNNING;
-				remove_wait_queue(&amp;port-&gt;write_wait, &amp;wait);
-				rc = -ERESTARTSYS;
-				goto err;
-			}
-			schedule();
-		}		
-		remove_wait_queue(&amp;port-&gt;write_wait, &amp;wait);
-		set_current_state(TASK_RUNNING);
+	if (port-&gt;write_urb-&gt;status == -EINPROGRESS) {
+		dbg (__FUNCTION__ " - already writing");
+		return (0);
+	}		
 
-		count += data_offset;
-		count = (count &gt; port-&gt;bulk_out_size) ? port-&gt;bulk_out_size : count;
-		if (count == 0) {
-			return 0;
-		}
+	down(&amp;port-&gt;sem);
+
+	count += data_offset;
+	count = (count &gt; port-&gt;bulk_out_size) ? port-&gt;bulk_out_size : count;
 
 		/* Copy in the data to send */
-		if (from_user) {
-			copy_from_user(port-&gt;write_urb-&gt;transfer_buffer + data_offset , 
-				       buf, count - data_offset );
-		}
-		else {
-			memcpy(port-&gt;write_urb-&gt;transfer_buffer + data_offset,
-			       buf, count - data_offset );
-		}  
-
-		first_byte = port-&gt;write_urb-&gt;transfer_buffer;
-		if (data_offset &gt; 0){
-			/* Write the control byte at the front of the packet*/
-			*first_byte = 1 | ((count-data_offset) &lt;&lt; 2) ; 
+	if (from_user) {
+		if (copy_from_user(port-&gt;write_urb-&gt;transfer_buffer + data_offset,
+				   buf, count - data_offset )){
+			up (&amp;port-&gt;sem);
+			return -EFAULT;
 		}
+	} else {
+		memcpy(port-&gt;write_urb-&gt;transfer_buffer + data_offset,
+		       buf, count - data_offset );
+	}  
+
+	first_byte = port-&gt;write_urb-&gt;transfer_buffer;
+	if (data_offset &gt; 0){
+		/* Write the control byte at the front of the packet*/
+		*first_byte = 1 | ((count-data_offset) &lt;&lt; 2) ; 
+	}
 
-		dbg(__FUNCTION__ " Bytes: %d, First Byte: 0o%03o",count, first_byte[0]);
-		usb_serial_debug_data (__FILE__, __FUNCTION__, count, first_byte);
+	dbg(__FUNCTION__ " Bytes: %d, First Byte: 0x%02x",count, first_byte[0]);
+	usb_serial_debug_data (__FILE__, __FUNCTION__, count, first_byte);
 		
-		/* send the data out the bulk port */
-		FILL_BULK_URB(port-&gt;write_urb, serial-&gt;dev, 
-			      usb_sndbulkpipe(serial-&gt;dev, port-&gt;bulk_out_endpointAddress),
-			      port-&gt;write_urb-&gt;transfer_buffer, count,
-			      ftdi_sio_write_bulk_callback, port);
+	/* send the data out the bulk port */
+	FILL_BULK_URB(port-&gt;write_urb, serial-&gt;dev, 
+		      usb_sndbulkpipe(serial-&gt;dev, port-&gt;bulk_out_endpointAddress),
+		      port-&gt;write_urb-&gt;transfer_buffer, count,
+		      ftdi_sio_write_bulk_callback, port);
 		
-		result = usb_submit_urb(port-&gt;write_urb);
-		if (result) {
-			err(__FUNCTION__ " - failed submitting write urb, error %d", result);
-			return 0;
-		}
-
-		dbg(__FUNCTION__ " write returning: %d", count - data_offset);
-		return (count - data_offset);
+	result = usb_submit_urb(port-&gt;write_urb);
+	if (result) {
+		err(__FUNCTION__ " - failed submitting write urb, error %d", result);
+		up (&amp;port-&gt;sem);
+		return 0;
 	}
-	
-	/* no bulk out, so return 0 bytes written */
-	return 0;
- err: /* error exit */
-	return(rc);
+	up (&amp;port-&gt;sem);
+
+	dbg(__FUNCTION__ " write returning: %d", count - data_offset);
+	return (count - data_offset);
+
 } /* ftdi_sio_write */
 
 static void ftdi_sio_write_bulk_callback (struct urb *urb)
 {
 	struct usb_serial_port *port = (struct usb_serial_port *)urb-&gt;context;
 	struct usb_serial *serial;
-       	struct tty_struct *tty = port-&gt;tty;
 
 	dbg("ftdi_sio_write_bulk_callback");
 
@@ -511,16 +497,29 @@
 		dbg("nonzero write bulk status received: %d", urb-&gt;status);
 		return;
 	}
+	queue_task(&amp;port-&gt;tqueue, &amp;tq_immediate);
+	mark_bh(IMMEDIATE_BH);
 
-	wake_up_interruptible(&amp;port-&gt;write_wait);
-	if ((tty-&gt;flags &amp; (1 &lt;&lt; TTY_DO_WRITE_WAKEUP)) &amp;&amp; tty-&gt;ldisc.write_wakeup)
-		(tty-&gt;ldisc.write_wakeup)(tty);
-
-	wake_up_interruptible(&amp;tty-&gt;write_wait);
-	
 	return;
 } /* ftdi_sio_write_bulk_callback */
 
+
+static int ftdi_sio_write_room( struct usb_serial_port *port )
+{
+	struct ftdi_private *priv = (struct ftdi_private *)port-&gt;private;
+	int room;
+	if ( port-&gt;write_urb-&gt;status == -EINPROGRESS) {
+		/* There is a race here with the _write routines but it won't hurt */
+		room = 0;
+	} else { 
+		room = port-&gt;bulk_out_size - priv-&gt;write_offset;
+	}
+	return(room);
+
+
+} /* ftdi_sio_write_room */
+
+
 static void ftdi_sio_read_bulk_callback (struct urb *urb)
 { /* ftdi_sio_serial_buld_callback */
 	struct usb_serial_port *port = (struct usb_serial_port *)urb-&gt;context;
@@ -533,7 +532,7 @@
 	int i;
 	int result;
 
-	dbg(__FUNCTION__);
+	dbg(__FUNCTION__ " - port %d", port-&gt;number);
 
 	if (port_paranoia_check (port, "ftdi_sio_read_bulk_callback")) {
 		return;
@@ -625,12 +624,12 @@
 #endif
 
 	/* Continue trying to always read  */
-	FILL_BULK_URB(urb, serial-&gt;dev, 
+	FILL_BULK_URB(port-&gt;read_urb, serial-&gt;dev, 
 		      usb_rcvbulkpipe(serial-&gt;dev, port-&gt;bulk_in_endpointAddress),
-		      urb-&gt;transfer_buffer, urb-&gt;transfer_buffer_length,
+		      port-&gt;read_urb-&gt;transfer_buffer, port-&gt;read_urb-&gt;transfer_buffer_length,
 		      ftdi_sio_read_bulk_callback, port);
 
-	result = usb_submit_urb(urb);
+	result = usb_submit_urb(port-&gt;read_urb);
 	if (result)
 		err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
 
@@ -638,7 +637,7 @@
 } /* ftdi_sio_serial_read_bulk_callback */
 
 
-__u16 translate_baudrate_to_ftdi(unsigned int cflag, ftdi_type_t ftdi_type) 
+static __u16 translate_baudrate_to_ftdi(unsigned int cflag, ftdi_type_t ftdi_type) 
 { /* translate_baudrate_to_ftdi */
 	
 	__u16 urb_value = ftdi_sio_b9600;
@@ -883,7 +882,8 @@
 
 	case TIOCMSET: /* Turns on and off the lines as specified by the mask */
 		dbg(__FUNCTION__ " TIOCMSET");
-		if ((ret = get_user(mask, (unsigned long *) arg))) return ret;
+		if (get_user(mask, (unsigned long *) arg))
+			return -EFAULT;
 		urb_value = ((mask &amp; TIOCM_DTR) ? HIGH : LOW);
 		if (set_dtr(serial-&gt;dev, usb_sndctrlpipe(serial-&gt;dev, 0),urb_value) &lt; 0){
 			err("Error from DTR set urb (TIOCMSET)");
@@ -896,7 +896,8 @@
 					
 	case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */
 		dbg(__FUNCTION__ " TIOCMBIS");
- 	        if ((ret = get_user(mask, (unsigned long *) arg))) return ret;
+ 	        if (get_user(mask, (unsigned long *) arg))
+			return -EFAULT;
   	        if (mask &amp; TIOCM_DTR){
 			if ((ret = set_dtr(serial-&gt;dev, 
 					   usb_sndctrlpipe(serial-&gt;dev, 0),
@@ -917,7 +918,8 @@
 
 	case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */
 		dbg(__FUNCTION__ " TIOCMBIC");
- 	        if ((ret = get_user(mask, (unsigned long *) arg))) return ret;
+ 	        if (get_user(mask, (unsigned long *) arg))
+			return -EFAULT;
   	        if (mask &amp; TIOCM_DTR){
 			if ((ret = set_dtr(serial-&gt;dev, 
 					   usb_sndctrlpipe(serial-&gt;dev, 0),

</pre></body></html>