cleanup of drivers/usb/serial/whiteheat.c

Oliver Neukum <[email protected]>
Newsgroups gmane.linux.usb.devel
Organization Novell
Message-ID <[email protected]>
Hi,

this driver was long known to be unclean. This should fix all issues.
However, I have no hardware to test, so I'd appreciate any comment.

	Regards
		Oliver

-----

--- a/drivers/usb/serial/whiteheat.c	2007-03-20 20:34:36.000000000 +0100
+++ b/drivers/usb/serial/whiteheat.c	2007-03-22 13:05:42.000000000 +0100
@@ -74,6 +74,7 @@
 #include <linux/tty_flip.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <asm/uaccess.h>
 #include <asm/termbits.h>
 #include <linux/usb.h>
@@ -203,7 +204,8 @@
 
 
 struct whiteheat_command_private {
-	spinlock_t		lock;
+	struct mutex		mutex;
+	spinlock_t		buflock;
 	__u8			port_running;
 	__u8			command_finished;
 	wait_queue_head_t	wait_command;	/* for handling sleeping while waiting for a command to finish */
@@ -225,6 +227,7 @@
 	spinlock_t		lock;
 	__u8			flags;
 	__u8			mcr;
+	char			shutting_down;
 	struct list_head	rx_urbs_free;
 	struct list_head	rx_urbs_submitted;
 	struct list_head	rx_urb_q;
@@ -495,7 +498,8 @@
 		goto no_command_private;
 	}
 
-	spin_lock_init(&command_info->lock);
+	spin_lock_init(&command_info->buflock);
+	mutex_init(&command_info->mutex);
 	command_info->port_running = 0;
 	init_waitqueue_head(&command_info->wait_command);
 	usb_set_serial_port_data(command_port, command_info);
@@ -552,7 +556,6 @@
 	return -ENOMEM;
 }
 
-
 static void whiteheat_shutdown (struct usb_serial *serial)
 {
 	struct usb_serial_port *command_port;
@@ -573,6 +576,11 @@
 	for (i = 0; i < serial->num_ports; i++) {
 		port = serial->port[i];
 		info = usb_get_serial_port_data(port);
+
+		spin_lock_irq(&info->lock);
+		info->shutting_down = 1;
+		spin_unlock_irq(&info->lock);
+
 		list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) {
 			list_del(tmp);
 			wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
@@ -589,6 +597,7 @@
 			kfree(urb->transfer_buffer);
 			usb_free_urb(urb);
 		}
+		flush_scheduled_work();
 		kfree(info);
 	}
 
@@ -685,21 +694,35 @@
 
 	/* shutdown our bulk reads and writes */
 	spin_lock_irqsave(&info->lock, flags);
+	info->shutting_down = 1;
+	smp_wmb(); 
+	/* this prevents new urbs in the queue
+	 * thus we cannot miss an urb due to dropping
+	 * the lock. We might doubly kill, but that's
+	 * harmless */
 	list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
 		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
 		urb = wrap->urb;
+		spin_unlock_irqrestore(&info->lock, flags);
 		usb_kill_urb(urb);
-		list_move(tmp, &info->rx_urbs_free);
+		spin_lock_irqsave(&info->lock, flags);
 	}
+	/* must walk again - cannot assume they stayed on the list */
+	list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted)
+		list_move(tmp, &info->rx_urbs_free);
+
 	list_for_each_safe(tmp, tmp2, &info->rx_urb_q)
 		list_move(tmp, &info->rx_urbs_free);
 
 	list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) {
 		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
 		urb = wrap->urb;
+		spin_unlock_irqrestore(&info->lock, flags);
 		usb_kill_urb(urb);
-		list_move(tmp, &info->tx_urbs_free);
+		spin_lock_irqsave(&info->lock, flags);
 	}
+	list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted)
+		list_move(tmp, &info->tx_urbs_free);
 	spin_unlock_irqrestore(&info->lock, flags);
 
 	stop_command_port(port->serial);
@@ -724,7 +747,7 @@
 
 	if (count == 0) {
 		dbg("%s - write request of 0 bytes", __FUNCTION__);
-		return (0);
+		return 0;
 	}
 
 	while (count) {
@@ -746,24 +769,28 @@
 
 		urb->dev = serial->dev;
 		urb->transfer_buffer_length = bytes;
-		result = usb_submit_urb(urb, GFP_ATOMIC);
-		if (result) {
-			err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
-			sent = result;
-			spin_lock_irqsave(&info->lock, flags);
+		spin_lock_irqsave(&info->lock, flags);
+		if (!info->shutting_down) {
+			result = usb_submit_urb(urb, GFP_ATOMIC);
+			if (result) {
+				err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
+				list_add(tmp, &info->tx_urbs_free);
+				spin_unlock_irqrestore(&info->lock, flags);
+				break;
+			} else {
+				sent += bytes;
+				count -= bytes;
+				list_add(tmp, &info->tx_urbs_submitted);
+				spin_unlock_irqrestore(&info->lock, flags);
+			}
+		} else {
 			list_add(tmp, &info->tx_urbs_free);
 			spin_unlock_irqrestore(&info->lock, flags);
 			break;
-		} else {
-			sent += bytes;
-			count -= bytes;
-			spin_lock_irqsave(&info->lock, flags);
-			list_add(tmp, &info->tx_urbs_submitted);
-			spin_unlock_irqrestore(&info->lock, flags);
 		}
 	}
 
-	return sent;
+	return sent ? sent : result;
 }
 
 
@@ -865,10 +892,10 @@
 			break;
 
 		default:
-			break;
+			return -ENOIOCTLCMD;
 	}
 
-	return -ENOIOCTLCMD;
+	return 0;
 }
 
 
@@ -920,7 +947,7 @@
 	spin_unlock_irqrestore(&info->lock, flags);
 
 	dbg ("%s - returns %d", __FUNCTION__, chars);
-	return (chars);
+	return chars;
 }
 
 
@@ -979,7 +1006,6 @@
 	struct whiteheat_command_private *command_info;
 	unsigned char *data = urb->transfer_buffer;
 	int result;
-	unsigned long flags;
 
 	dbg("%s", __FUNCTION__);
 
@@ -995,7 +1021,7 @@
 		dbg ("%s - command_info is NULL, exiting.", __FUNCTION__);
 		return;
 	}
-	spin_lock_irqsave(&command_info->lock, flags);
+	spin_lock(&command_info->buflock);
 
 	if (data[0] == WHITEHEAT_CMD_COMPLETE) {
 		command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
@@ -1017,7 +1043,7 @@
 	/* Continue trying to always read */
 	command_port->read_urb->dev = command_port->serial->dev;
 	result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC);
-	spin_unlock_irqrestore(&command_info->lock, flags);
+	spin_unlock(&command_info->buflock);
 	if (result)
 		dbg("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
 }
@@ -1040,19 +1066,14 @@
 		return;
 	}
 	list_del(&wrap->list);
-	spin_unlock(&info->lock);
 
 	if (urb->status) {
 		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
-		spin_lock(&info->lock);
 		list_add(&wrap->list, &info->rx_urbs_free);
 		spin_unlock(&info->lock);
 		return;
 	}
 
-	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
-
-	spin_lock(&info->lock);
 	list_add_tail(&wrap->list, &info->rx_urb_q);
 	if (info->flags & THROTTLED) {
 		info->flags |= ACTUALLY_THROTTLED;
@@ -1060,6 +1081,7 @@
 		return;
 	}
 	spin_unlock(&info->lock);
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 
 	schedule_work(&info->rx_work);
 }
@@ -1070,10 +1092,12 @@
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 	struct whiteheat_private *info = usb_get_serial_port_data(port);
 	struct whiteheat_urb_wrap *wrap;
+	int status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
 	spin_lock(&info->lock);
+	status = urb->status;
 	wrap = urb_to_wrap(urb, &info->tx_urbs_submitted);
 	if (!wrap) {
 		spin_unlock(&info->lock);
@@ -1083,8 +1107,8 @@
 	list_move(&wrap->list, &info->tx_urbs_free);
 	spin_unlock(&info->lock);
 
-	if (urb->status) {
-		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, status);
 		return;
 	}
 
@@ -1102,13 +1126,12 @@
 	struct whiteheat_private *info;
 	__u8 *transfer_buffer;
 	int retval = 0;
-	unsigned long flags;
 
 	dbg("%s - command %d", __FUNCTION__, command);
 
 	command_port = port->serial->port[COMMAND_PORT];
 	command_info = usb_get_serial_port_data(command_port);
-	spin_lock_irqsave(&command_info->lock, flags);
+	mutex_lock(&command_info->mutex);
 	command_info->command_finished = FALSE;
 	
 	transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer;
@@ -1116,29 +1139,28 @@
 	memcpy (&transfer_buffer[1], data, datasize);
 	command_port->write_urb->transfer_buffer_length = datasize + 1;
 	command_port->write_urb->dev = port->serial->dev;
-	retval = usb_submit_urb (command_port->write_urb, GFP_KERNEL);
+	retval = usb_submit_urb(command_port->write_urb, GFP_NOIO);
 	if (retval) {
 		dbg("%s - submit urb failed", __FUNCTION__);
 		goto exit;
 	}
-	spin_unlock_irqrestore(&command_info->lock, flags);
 
 	/* wait for the command to complete */
 	wait_event_interruptible_timeout(command_info->wait_command, 
 		(command_info->command_finished != FALSE), COMMAND_TIMEOUT);
 
-	spin_lock_irqsave(&command_info->lock, flags);
 
+	spin_lock_irq(&command_info->buflock);
 	if (command_info->command_finished == FALSE) {
 		dbg("%s - command timed out.", __FUNCTION__);
 		retval = -ETIMEDOUT;
-		goto exit;
+		goto exit2;
 	}
 
 	if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) {
 		dbg("%s - command failed.", __FUNCTION__);
 		retval = -EIO;
-		goto exit;
+		goto exit2;
 	}
 
 	if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) {
@@ -1150,9 +1172,10 @@
 				break;
 		}
 	}
-
+exit2:
+	spin_unlock_irq(&command_info->buflock);
 exit:
-	spin_unlock_irqrestore(&command_info->lock, flags);
+	mutex_unlock(&command_info->mutex);
 	return retval;
 }
 
@@ -1305,12 +1328,11 @@
 {
 	struct usb_serial_port *command_port;
 	struct whiteheat_command_private *command_info;
-	unsigned long flags;
 	int retval = 0;
 	
 	command_port = serial->port[COMMAND_PORT];
 	command_info = usb_get_serial_port_data(command_port);
-	spin_lock_irqsave(&command_info->lock, flags);
+	mutex_lock(&command_info->mutex);
 	if (!command_info->port_running) {
 		/* Work around HCD bugs */
 		usb_clear_halt(serial->dev, command_port->read_urb->pipe);
@@ -1325,7 +1347,7 @@
 	command_info->port_running++;
 
 exit:
-	spin_unlock_irqrestore(&command_info->lock, flags);
+	mutex_unlock(&command_info->mutex);
 	return retval;
 }
 
@@ -1334,15 +1356,14 @@
 {
 	struct usb_serial_port *command_port;
 	struct whiteheat_command_private *command_info;
-	unsigned long flags;
 
 	command_port = serial->port[COMMAND_PORT];
 	command_info = usb_get_serial_port_data(command_port);
-	spin_lock_irqsave(&command_info->lock, flags);
+	mutex_lock(&command_info->mutex);
 	command_info->port_running--;
 	if (!command_info->port_running)
 		usb_kill_urb(command_port->read_urb);
-	spin_unlock_irqrestore(&command_info->lock, flags);
+	mutex_unlock(&command_info->mutex);
 }
 
 
@@ -1363,15 +1384,22 @@
 		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
 		urb = wrap->urb;
 		urb->dev = port->serial->dev;
-		retval = usb_submit_urb(urb, GFP_KERNEL);
+		retval = usb_submit_urb(urb, GFP_ATOMIC);
 		if (retval) {
+			info->shutting_down = 1;
+			smp_wmb();
 			list_add(tmp, &info->rx_urbs_free);
 			list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
 				wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
 				urb = wrap->urb;
+				spin_unlock_irqrestore(&info->lock, flags);
 				usb_kill_urb(urb);
-				list_move(tmp, &info->rx_urbs_free);
+				spin_lock_irqsave(&info->lock, flags);
 			}
+			list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted)
+				list_move(tmp, &info->rx_urbs_free);
+			smp_wmb();
+			info->shutting_down = 0;
 			break;
 		}
 		list_add(tmp, &info->rx_urbs_submitted);
@@ -1426,7 +1454,6 @@
 
 	list_for_each_safe(tmp, tmp2, &info->rx_urb_q) {
 		list_del(tmp);
-		spin_unlock_irqrestore(&info->lock, flags);
 
 		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
 		urb = wrap->urb;
@@ -1435,9 +1462,7 @@
 			int len = tty_buffer_request_room(tty, urb->actual_length);
 			/* This stuff can go away now I suspect */
 			if (unlikely(len < urb->actual_length)) {
-				spin_lock_irqsave(&info->lock, flags);
 				list_add(tmp, &info->rx_urb_q);
-				spin_unlock_irqrestore(&info->lock, flags);
 				tty_flip_buffer_push(tty);
 				schedule_work(&info->rx_work);
 				return;
@@ -1447,15 +1472,16 @@
 		}
 
 		urb->dev = port->serial->dev;
-		result = usb_submit_urb(urb, GFP_ATOMIC);
+		if (!info->shutting_down)
+			result = usb_submit_urb(urb, GFP_ATOMIC);
+		else
+			result = -EBUSY;
 		if (result) {
 			err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
-			spin_lock_irqsave(&info->lock, flags);
 			list_add(tmp, &info->rx_urbs_free);
 			continue;
 		}
 
-		spin_lock_irqsave(&info->lock, flags);
 		list_add(tmp, &info->rx_urbs_submitted);
 	}
 	spin_unlock_irqrestore(&info->lock, flags);

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.