From: Greg KH <greg@kroah.com>
To: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: linux-usb-devel@lists.sourceforge.net
Subject: [PATCH 3 of 3] USB Whiteheat driver fixes

Hi,

Here's a patch against 2.2.21-rc1 for the USB Whiteheat driver that adds
locking to enable it to work properly on SMP machines.

thanks,

greg k-h


diff -Nru a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
--- a/drivers/usb/serial/whiteheat.c	Tue Mar 12 09:53:09 2002
+++ b/drivers/usb/serial/whiteheat.c	Tue Mar 12 09:53:09 2002
@@ -11,6 +11,9 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  *
+ * (05/30/2001) gkh
+ *	switched from using spinlock to a semaphore, which fixes lots of problems.
+ *
  * (04/08/2001) gb
  *	Identify version on module load.
  * 
@@ -82,7 +85,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.0.0"
+#define DRIVER_VERSION "v1.1"
 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
 #define DRIVER_DESC "USB ConnectTech WhiteHEAT driver"
 
@@ -104,7 +107,7 @@
 static __u16	connecttech_vendor_id			= CONNECT_TECH_VENDOR_ID;
 static __u16	connecttech_whiteheat_fake_product_id	= CONNECT_TECH_FAKE_WHITE_HEAT_ID;
 static __u16	connecttech_whiteheat_product_id	= CONNECT_TECH_WHITE_HEAT_ID;
-struct usb_serial_device_type whiteheat_fake_device = {
+static struct usb_serial_device_type whiteheat_fake_device = {
 	name:			"Connect Tech - WhiteHEAT - (prerenumeration)",
 	idVendor:		&connecttech_vendor_id,			/* the Connect Tech vendor id */
 	idProduct:		&connecttech_whiteheat_fake_product_id,	/* the White Heat initial product id */
@@ -117,7 +120,7 @@
 	num_ports:		1,
 	startup:		whiteheat_startup	
 };
-struct usb_serial_device_type whiteheat_device = {
+static struct usb_serial_device_type whiteheat_device = {
 	name:			"Connect Tech - WhiteHEAT",
 	idVendor:		&connecttech_vendor_id,			/* the Connect Tech vendor id */
 	idProduct:		&connecttech_whiteheat_product_id,	/* the White Heat real product id */
@@ -228,6 +231,7 @@
 	struct usb_serial_port *port;
 	int timeout;
 	__u8 *transfer_buffer;
+	int retval = 0;
 
 	dbg(__FUNCTION__" - command %d", command);
 
@@ -240,9 +244,10 @@
 	memcpy (&transfer_buffer[1], data, datasize);
 	port->write_urb->transfer_buffer_length = datasize + 1;
 	port->write_urb->dev = serial->dev;
-	if (usb_submit_urb (port->write_urb)) {
+	retval = usb_submit_urb (port->write_urb);
+	if (retval) {
 		dbg (__FUNCTION__" - submit urb failed");
-		return -1;
+		goto exit;
 	}
 
 	/* wait for the command to complete */
@@ -253,20 +258,21 @@
 
 	if (info->command_finished == FALSE) {
 		dbg (__FUNCTION__ " - command timed out.");
-		return -1;
+		retval = -ETIMEDOUT;
+		goto exit;
 	}
 
 	if (info->command_finished == WHITEHEAT_CMD_FAILURE) {
 		dbg (__FUNCTION__ " - command failed.");
-		return -1;
+		retval = -EIO;
+		goto exit;
 	}
 
-	if (info->command_finished == WHITEHEAT_CMD_COMPLETE) {
+	if (info->command_finished == WHITEHEAT_CMD_COMPLETE)
 		dbg (__FUNCTION__ " - command completed.");
-		return 0;
-	}
 
-	return 0;
+exit:
+	return retval;
 }
 
 
@@ -275,10 +281,12 @@
 	struct whiteheat_min_set	open_command;
 	struct usb_serial_port 		*command_port;
 	struct whiteheat_private	*info;
-	int				result;
+	int				retval = 0;
 
 	dbg(__FUNCTION__" - port %d", port->number);
 
+	down (&port->sem);
+
 	++port->open_count;
 	MOD_INC_USE_COUNT;
 	
@@ -291,7 +299,8 @@
 			info = (struct whiteheat_private *)kmalloc (sizeof(struct whiteheat_private), GFP_KERNEL);
 			if (info == NULL) {
 				err(__FUNCTION__ " - out of memory");
-				return -ENOMEM;
+				retval = -ENOMEM;
+				goto error_exit;
 			}
 			
 			init_waitqueue_head(&info->wait_command);
@@ -300,27 +309,45 @@
 			command_port->read_urb->complete = command_port_read_callback;
 			command_port->read_urb->dev = port->serial->dev;
 			command_port->tty = port->tty;		/* need this to "fake" our our sanity check macros */
-			usb_submit_urb (command_port->read_urb);
+			retval = usb_submit_urb (command_port->read_urb);
+			if (retval) {
+				err(__FUNCTION__ " - failed submitting read urb, error %d", retval);
+				goto error_exit;
+			}
 		}
 		
 		/* Start reading from the device */
 		port->read_urb->dev = port->serial->dev;
-		result = usb_submit_urb(port->read_urb);
-		if (result)
-			err(__FUNCTION__ " - failed submitting read urb, error %d", result);
+		retval = usb_submit_urb(port->read_urb);
+		if (retval) {
+			err(__FUNCTION__ " - failed submitting read urb, error %d", retval);
+			goto error_exit;
+		}
 	
 		/* send an open port command */
 		/* firmware uses 1 based port numbering */
 		open_command.port = port->number - port->serial->minor + 1;
-		whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command));
+		retval = whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command));
+		if (retval)
+			goto error_exit;
 	
 		/* Need to do device specific setup here (control lines, baud rate, etc.) */
 		/* FIXME!!! */
 	}
 
 	dbg(__FUNCTION__ " - exit");
+	up (&port->sem);
+	
+	return retval;
+
+error_exit:
+	--port->open_count;
+	MOD_DEC_USE_COUNT;
+
+	dbg(__FUNCTION__ " - error_exit");
+	up (&port->sem);
 	
-	return 0;
+	return retval;
 }
 
 
@@ -330,6 +357,7 @@
 	
 	dbg(__FUNCTION__ " - port %d", port->number);
 	
+	down (&port->sem);
 	--port->open_count;
 
 	if (port->open_count <= 0) {
@@ -347,6 +375,7 @@
 		port->active = 0;
 	}
 	MOD_DEC_USE_COUNT;
+	up (&port->sem);
 }
 
 
@@ -360,25 +389,28 @@
 
 static void whiteheat_set_termios (struct usb_serial_port *port, struct termios *old_termios)
 {
-	unsigned int cflag = port->tty->termios->c_cflag;
+	unsigned int cflag;
 	struct whiteheat_port_settings port_settings;
 
 	dbg(__FUNCTION__ " -port %d", port->number);
 
+	down (&port->sem);
+
+	if ((!port->tty) || (!port->tty->termios)) {
+		dbg(__FUNCTION__" - no tty structures");
+		goto exit;
+	}
+	
+	cflag = port->tty->termios->c_cflag;
 	/* check that they really want us to change something */
 	if (old_termios) {
 		if ((cflag == old_termios->c_cflag) &&
 		    (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
 			dbg(__FUNCTION__ " - nothing to change...");
-			return;
+			goto exit;
 		}
 	}
 
-	if ((!port->tty) || (!port->tty->termios)) {
-		dbg(__FUNCTION__" - no tty structures");
-		return;
-	}
-	
 	/* set the port number */
 	/* firmware uses 1 based port numbering */
 	port_settings.port = port->number + 1;
@@ -443,6 +475,8 @@
 	/* now send the message to the device */
 	whiteheat_send_cmd (port->serial, WHITEHEAT_SETUP_PORT, (__u8 *)&port_settings, sizeof(port_settings));
 	
+exit:
+	up (&port->sem);
 	return;
 }