Subject: [PATCH 06 of 15] USB Serial ftdi_sio driver updated
From: Greg KH <[email-masked]>
To: Alan Cox <[email-masked]>
Cc: [email-masked]

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 ([email-masked])
  *          Bill Ryder ([email-masked])
  *
@@ -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 <linux/module.h>
 #include <linux/spinlock.h>
 #include <linux/usb.h>
-
 #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 <[email-masked]>, Bill Ryder <[email-masked]>"
 #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:		&ftdi_vendor_id,	/* the FTDI vendor ID */
 	idProduct:		&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(&serial->port[0].write_wait);
 	
 	priv = serial->port->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->ftdi_type = sio;
+	priv->write_offset = 1;
 	
 	return (0);
 }
@@ -244,15 +259,15 @@
 {
 	struct ftdi_private *priv;
  
-	init_waitqueue_head(&serial->port[0].write_wait);
 
 	priv = serial->port->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->ftdi_type = F8U232AM;
+	priv->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->port[0].open_count > 0) {
 		ftdi_sio_close (&serial->port[0], NULL);
 	}
-	if (serial->port->private){
-		kfree(serial->port->private);
-		serial->port->private = NULL;
+	if (serial->port[0].private){
+		kfree(serial->port[0].private);
+		serial->port[0].private = NULL;
 	}
 }
 
@@ -278,13 +294,12 @@
 { /* ftdi_sio_open */
 	struct termios tmp_termios;
 	struct usb_serial *serial = port->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 (&port->port_lock, flags);
+	down (&port->sem);
 	
 	MOD_INC_USE_COUNT;
 	++port->open_count;
@@ -292,8 +307,6 @@
 	if (!port->active){
 		port->active = 1;
 
-		spin_unlock_irqrestore (&port->port_lock, flags);
-
 		/* This will push the characters through immediately rather 
 		   than queue a task to deliver them */
 		port->tty->low_latency = 1;
@@ -329,63 +342,61 @@
 		result = usb_submit_urb(port->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 (&port->port_lock, flags);
 	}
 
-	return (0);
+	up (&port->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->serial;
+	struct usb_serial *serial = port->serial; /* Checked in usbserial.c */
 	unsigned int c_cflag = port->tty->termios->c_cflag;
 	char buf[1];
-	unsigned long flags;
 
 	dbg( __FUNCTION__);
 
-	spin_lock_irqsave (&port->port_lock, flags);
+	down (&port->sem);
 	--port->open_count;
 
 	if (port->open_count <= 0) {
-		spin_unlock_irqrestore (&port->port_lock, flags);
-		if (c_cflag & HUPCL){
-			/* Disable flow control */
-			if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
-					    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
-					    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
-					    0, 0, 
-					    buf, 0, WDR_TIMEOUT) < 0) {
-				err("error from flowcontrol urb");
-			}	    
-
-			/* drop DTR */
-			if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0), LOW) < 0){
-				err("Error from DTR LOW urb");
-			}
-			/* drop RTS */
-			if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 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->write_urb);
-		usb_unlink_urb (port->read_urb);
+		if (serial->dev) {
+			if (c_cflag & HUPCL){
+				/* Disable flow control */
+				if (usb_control_msg(serial->dev, 
+						    usb_sndctrlpipe(serial->dev, 0),
+						    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+						    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+						    0, 0, buf, 0, WDR_TIMEOUT) < 0) {
+					err("error from flowcontrol urb");
+				}	    
+
+				/* drop DTR */
+				if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0), LOW) < 0){
+					err("Error from DTR LOW urb");
+				}
+				/* drop RTS */
+				if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 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->write_urb);
+			usb_unlink_urb (port->read_urb);
+		}
 		port->active = 0;
 		port->open_count = 0;
 	} else {  
-		spin_unlock_irqrestore (&port->port_lock, flags);
-
 		/* Send a HUP if necessary */
 		if (!(port->tty->termios->c_cflag & CLOCAL)){
 			tty_hangup(port->tty);
 		}
-		
 	}
 
+	up (&port->sem);
 	MOD_DEC_USE_COUNT;
 
 } /* ftdi_sio_close */
@@ -402,10 +413,9 @@
 { /* ftdi_sio_write */
 	struct usb_serial *serial = port->serial;
 	struct ftdi_private *priv = (struct ftdi_private *)port->private;
+	unsigned char *first_byte = port->write_urb->transfer_buffer;
 	int data_offset ;
-	int rc; 
 	int result;
-	DECLARE_WAITQUEUE(wait, current);
 	
 	dbg(__FUNCTION__ " port %d, %d bytes", port->number, count);
 
@@ -414,87 +424,63 @@
 		return 0;
 	}
 	
-	if (priv->ftdi_type == sio){
-		data_offset = 1;
-	} else {
-		data_offset = 0;
-	}
+	data_offset = priv->write_offset;
         dbg("data_offset set to %d",data_offset);
 
-	/* only do something if we have a bulk out endpoint */
-	if (serial->num_bulk_out) {
-		unsigned char *first_byte = port->write_urb->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(&port->write_wait, &wait);
-		set_current_state (TASK_INTERRUPTIBLE);
-		while (port->write_urb->status == -EINPROGRESS) {
-			dbg(__FUNCTION__ " write in progress - retrying");
-			if (signal_pending(current)) {
-				current->state = TASK_RUNNING;
-				remove_wait_queue(&port->write_wait, &wait);
-				rc = -ERESTARTSYS;
-				goto err;
-			}
-			schedule();
-		}		
-		remove_wait_queue(&port->write_wait, &wait);
-		set_current_state(TASK_RUNNING);
+	if (port->write_urb->status == -EINPROGRESS) {
+		dbg (__FUNCTION__ " - already writing");
+		return (0);
+	}		
 
-		count += data_offset;
-		count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
-		if (count == 0) {
-			return 0;
-		}
+	down(&port->sem);
+
+	count += data_offset;
+	count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
 
 		/* Copy in the data to send */
-		if (from_user) {
-			copy_from_user(port->write_urb->transfer_buffer + data_offset , 
-				       buf, count - data_offset );
-		}
-		else {
-			memcpy(port->write_urb->transfer_buffer + data_offset,
-			       buf, count - data_offset );
-		}  
-
-		first_byte = port->write_urb->transfer_buffer;
-		if (data_offset > 0){
-			/* Write the control byte at the front of the packet*/
-			*first_byte = 1 | ((count-data_offset) << 2) ; 
+	if (from_user) {
+		if (copy_from_user(port->write_urb->transfer_buffer + data_offset,
+				   buf, count - data_offset )){
+			up (&port->sem);
+			return -EFAULT;
 		}
+	} else {
+		memcpy(port->write_urb->transfer_buffer + data_offset,
+		       buf, count - data_offset );
+	}  
+
+	first_byte = port->write_urb->transfer_buffer;
+	if (data_offset > 0){
+		/* Write the control byte at the front of the packet*/
+		*first_byte = 1 | ((count-data_offset) << 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->write_urb, serial->dev, 
-			      usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
-			      port->write_urb->transfer_buffer, count,
-			      ftdi_sio_write_bulk_callback, port);
+	/* send the data out the bulk port */
+	FILL_BULK_URB(port->write_urb, serial->dev, 
+		      usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
+		      port->write_urb->transfer_buffer, count,
+		      ftdi_sio_write_bulk_callback, port);
 		
-		result = usb_submit_urb(port->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->write_urb);
+	if (result) {
+		err(__FUNCTION__ " - failed submitting write urb, error %d", result);
+		up (&port->sem);
+		return 0;
 	}
-	
-	/* no bulk out, so return 0 bytes written */
-	return 0;
- err: /* error exit */
-	return(rc);
+	up (&port->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->context;
 	struct usb_serial *serial;
-       	struct tty_struct *tty = port->tty;
 
 	dbg("ftdi_sio_write_bulk_callback");
 
@@ -511,16 +497,29 @@
 		dbg("nonzero write bulk status received: %d", urb->status);
 		return;
 	}
+	queue_task(&port->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
 
-	wake_up_interruptible(&port->write_wait);
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
-		(tty->ldisc.write_wakeup)(tty);
-
-	wake_up_interruptible(&tty->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->private;
+	int room;
+	if ( port->write_urb->status == -EINPROGRESS) {
+		/* There is a race here with the _write routines but it won't hurt */
+		room = 0;
+	} else { 
+		room = port->bulk_out_size - priv->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->context;
@@ -533,7 +532,7 @@
 	int i;
 	int result;
 
-	dbg(__FUNCTION__);
+	dbg(__FUNCTION__ " - port %d", port->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->dev, 
+	FILL_BULK_URB(port->read_urb, serial->dev, 
 		      usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
-		      urb->transfer_buffer, urb->transfer_buffer_length,
+		      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
 		      ftdi_sio_read_bulk_callback, port);
 
-	result = usb_submit_urb(urb);
+	result = usb_submit_urb(port->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 & TIOCM_DTR) ? HIGH : LOW);
 		if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),urb_value) < 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 & TIOCM_DTR){
 			if ((ret = set_dtr(serial->dev, 
 					   usb_sndctrlpipe(serial->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 & TIOCM_DTR){
 			if ((ret = set_dtr(serial->dev, 
 					   usb_sndctrlpipe(serial->dev, 0),