patch-2.4.0-test10 linux/drivers/usb/mdc800.c

Next file: linux/drivers/usb/net1080.c
Previous file: linux/drivers/usb/hub.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test9/linux/drivers/usb/mdc800.c linux/drivers/usb/mdc800.c
@@ -22,7 +22,7 @@
  *	(c) 1999/2000 Henning Zabel <henning@uni-paderborn.de>
  *
  *
- *	The driver brings the USB functions of the MDC800 to Linux.
+ * The driver brings the USB functions of the MDC800 to Linux.
  * To use the Camera you must support the USB Protocoll of the camera
  * to the Kernel Node.
  * The Driver uses a misc device Node. Create it with :
@@ -30,6 +30,16 @@
  *
  * The driver supports only one camera.
  *
+ * version 0.7.5
+ * Fixed potential SMP races with Spinlocks.
+ * Thanks to Oliver Neukum <oliver.neukum@lrz.uni-muenchen.de> who 
+ * noticed the race conditions.
+ * (30/10/2000)
+ *
+ * Fixed: Setting urb->dev before submitting urb.
+ * by Greg KH <greg@kroah.com>
+ * (13/10/2000)
+ *
  * version 0.7.3
  * bugfix : The mdc800->state field gets set to READY after the
  * the diconnect function sets it to NOT_CONNECTED. This makes the
@@ -61,7 +71,7 @@
  * (09/11/1999)
  *
  * version 0.5.0:
- *	first Version that gets a version number. Most of the needed
+ * first Version that gets a version number. Most of the needed
  * functions work.
  * (20/10/1999)
  */
@@ -80,8 +90,8 @@
 
 #include <linux/usb.h>
 
-#define VERSION 	"0.7.3"
-#define RELEASE_DATE 	"(24/04/2000)"
+#define VERSION 	"0.7.5"
+#define RELEASE_DATE 	"(30/10/2000)"
 
 /* Vendor and Product Information */
 #define MDC800_VENDOR_ID 	0x055f
@@ -123,7 +133,7 @@
 
 	int			camera_busy;          // is camera busy ?
 	int 			camera_request_ready; // Status to synchronize with irq
-	char 			camera_response [8];	 // last Bytes send after busy
+	char 			camera_response [8];  // last Bytes send after busy
 
 	purb_t   		write_urb;
 	char*			write_urb_buffer;
@@ -138,16 +148,16 @@
 
 	/* Device Data */
 	char			out [64];	// Answer Buffer
-	int 			out_ptr;		// Index to the first not readen byte
+	int 			out_ptr;	// Index to the first not readen byte
 	int			out_count;	// Bytes in the buffer
 
-	int			open;			// Camera device open ?
-	int			rw_lock;		// Block read <-> write
+	int			open;		// Camera device open ?
+	spinlock_t		io_lock;	// IO -lock	
 
-	char 			in [8];			// Command Input Buffer
+	char 			in [8];		// Command Input Buffer
 	int  			in_count;
 
-	int			pic_index;		// Cache for the Imagesize (-1 for nothing cached )
+	int			pic_index;	// Cache for the Imagesize (-1 for nothing cached )
 	int			pic_len;
 };
 
@@ -253,16 +263,16 @@
 		}
 	}
 	wake_up= ( mdc800->camera_request_ready > 0 )
-				&&
-				(
-					((mdc800->camera_request_ready == 1) && (!mdc800->camera_busy))
-				||
-					((mdc800->camera_request_ready == 2) && data_received)
-				||
-					((mdc800->camera_request_ready == 3) && (mdc800->camera_busy))
-				||
-					(urb->status < 0)
-				);
+		&&
+		(
+			((mdc800->camera_request_ready == 1) && (!mdc800->camera_busy))
+		||
+			((mdc800->camera_request_ready == 2) && data_received)
+		||
+			((mdc800->camera_request_ready == 3) && (mdc800->camera_busy))
+		||
+			(urb->status < 0)
+		);
 
 	if (wake_up)
 	{
@@ -362,7 +372,7 @@
  */
 static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum )
 {
-	int 										i,j;
+	int i,j;
 	struct usb_interface_descriptor	*intf_desc;
 	int irq_interval=0;
 
@@ -432,10 +442,10 @@
 
 	info ("Found Mustek MDC800 on USB.");
 
+	spin_lock (&mdc800->io_lock);
+
 	mdc800->dev=dev;
-	mdc800->state=READY;
 	mdc800->open=0;
-	mdc800->rw_lock=0;
 
 	/* Setup URB Structs */
 	FILL_INT_URB (
@@ -469,6 +479,10 @@
 		mdc800
 	);
 
+	mdc800->state=READY;
+
+	spin_unlock (&mdc800->io_lock);
+	
 	return mdc800;
 }
 
@@ -539,18 +553,21 @@
 static int mdc800_device_open (struct inode* inode, struct file *file)
 {
 	int retval=0;
+	int errn=0;
+
+	spin_lock (&mdc800->io_lock);
 	
 	if (mdc800->state == NOT_CONNECTED)
 	{
-		return -EBUSY;
+		errn=-EBUSY;
+		goto error_out;
 	}
-
 	if (mdc800->open)
 	{
-		return -EBUSY;
+		errn=-EBUSY;
+		goto error_out;
 	}
 
-	mdc800->rw_lock=0;
 	mdc800->in_count=0;
 	mdc800->out_count=0;
 	mdc800->out_ptr=0;
@@ -562,16 +579,20 @@
 	mdc800->camera_request_ready=0;
 
 	retval=0;
+	mdc800->irq_urb->dev = mdc800->dev;
 	if (usb_submit_urb (mdc800->irq_urb))
 	{
 		err ("request USB irq fails (submit_retval=%i urb_status=%i).",retval, mdc800->irq_urb->status);
-		return -EIO;
+		errn = -EIO;
+		goto error_out;
 	}
 
 	mdc800->open=1;
-
 	dbg ("Mustek MDC800 device opened.");
-	return 0;
+
+error_out:
+	spin_unlock (&mdc800->io_lock);
+	return errn;
 }
 
 
@@ -583,7 +604,7 @@
 	int retval=0;
 	dbg ("Mustek MDC800 device closed.");
 
-	lock_kernel();
+	spin_lock (&mdc800->io_lock);
 	if (mdc800->open && (mdc800->state != NOT_CONNECTED))
 	{
 		mdc800->open=0;
@@ -595,7 +616,7 @@
 	{
 		retval=-EIO;
 	}
-	unlock_kernel();
+	spin_unlock (&mdc800->io_lock);
 
 	return retval;
 }
@@ -609,23 +630,29 @@
 	int   left=len, sts=len; /* single transfer size */
 	char* ptr=buf;
 
+	spin_lock (&mdc800->io_lock);
 	if (mdc800->state == NOT_CONNECTED)
+	{
+		spin_unlock (&mdc800->io_lock);
 		return -EBUSY;
-
+	}
 	if (mdc800->state == WORKING)
 	{
 		warn ("Illegal State \"working\" reached during read ?!");
+		spin_unlock (&mdc800->io_lock);
 		return -EBUSY;
 	}
-
-	if (!mdc800->open || mdc800->rw_lock)
+	if (!mdc800->open)
+	{
+		spin_unlock (&mdc800->io_lock);
 		return -EBUSY;
-	mdc800->rw_lock=1;
+	}
 
 	while (left)
 	{
-		if (signal_pending (current)) {
-			mdc800->rw_lock=0;
+		if (signal_pending (current)) 
+		{
+			spin_unlock (&mdc800->io_lock);
 			return -EINTR;
 		}
 
@@ -640,24 +667,25 @@
 				mdc800->out_ptr=0;
 
 				/* Download -> Request new bytes */
+				mdc800->download_urb->dev = mdc800->dev;
 				if (usb_submit_urb (mdc800->download_urb))
 				{
 					err ("Can't submit download urb (status=%i)",mdc800->download_urb->status);
-					mdc800->rw_lock=0;
+					spin_unlock (&mdc800->io_lock);
 					return len-left;
 				}
 				interruptible_sleep_on_timeout (&mdc800->download_wait, TO_DOWNLOAD_GET_READY*HZ/1000);
 				if (mdc800->download_urb->status != 0)
 				{
 					err ("request download-bytes fails (status=%i)",mdc800->download_urb->status);
-					mdc800->rw_lock=0;
+					spin_unlock (&mdc800->io_lock);
 					return len-left;
 				}
 			}
 			else
 			{
 				/* No more bytes -> that's an error*/
-				mdc800->rw_lock=0;
+				spin_unlock (&mdc800->io_lock);
 				return -EIO;
 			}
 		}
@@ -671,7 +699,7 @@
 		}
 	}
 
-	mdc800->rw_lock=0;
+	spin_unlock (&mdc800->io_lock);
 	return len-left;
 }
 
@@ -686,17 +714,23 @@
 {
 	int i=0;
 
+	spin_lock (&mdc800->io_lock);
 	if (mdc800->state != READY)
+	{
+		spin_unlock (&mdc800->io_lock);
 		return -EBUSY;
-
-	if (!mdc800->open || mdc800->rw_lock)
+	}
+	if (!mdc800->open )
+	{
+		spin_unlock (&mdc800->io_lock);
 		return -EBUSY;
-	mdc800->rw_lock=1;
+	}
 
 	while (i<len)
 	{
-		if (signal_pending (current)) {
-			mdc800->rw_lock=0;
+		if (signal_pending (current)) 
+		{
+			spin_unlock (&mdc800->io_lock);
 			return -EINTR;
 		}
 
@@ -718,7 +752,7 @@
 		else
 		{
 			err ("Command is to long !\n");
-			mdc800->rw_lock=0;
+			spin_unlock (&mdc800->io_lock);
 			return -EIO;
 		}
 
@@ -730,7 +764,7 @@
 			if (mdc800_usb_waitForIRQ (0,TO_GET_READY))
 			{
 				err ("Camera didn't get ready.\n");
-				mdc800->rw_lock=0;
+				spin_unlock (&mdc800->io_lock);
 				return -EIO;
 			}
 
@@ -738,17 +772,18 @@
 
 			mdc800->state=WORKING;
 			memcpy (mdc800->write_urb->transfer_buffer, mdc800->in,8);
+			mdc800->write_urb->dev = mdc800->dev;
 			if (usb_submit_urb (mdc800->write_urb))
 			{
 				err ("submitting write urb fails (status=%i)", mdc800->write_urb->status);
-				mdc800->rw_lock=0;
+				spin_unlock (&mdc800->io_lock);
 				return -EIO;
 			}
 			interruptible_sleep_on_timeout (&mdc800->write_wait, TO_WRITE_GET_READY*HZ/1000);
 			if (mdc800->state == WORKING)
 			{
 				usb_unlink_urb (mdc800->write_urb);
-				mdc800->rw_lock=0;
+				spin_unlock (&mdc800->io_lock);
 				return -EIO;
 			}
 
@@ -760,7 +795,7 @@
 					{
 						err ("call 0x07 before 0x05,0x3e");
 						mdc800->state=READY;
-						mdc800->rw_lock=0;
+						spin_unlock (&mdc800->io_lock);
 						return -EIO;
 					}
 					mdc800->pic_len=-1;
@@ -779,7 +814,7 @@
 						if (mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ))
 						{
 							err ("requesting answer from irq fails");
-							mdc800->rw_lock=0;
+							spin_unlock (&mdc800->io_lock);
 							return -EIO;
 						}
 
@@ -807,7 +842,7 @@
 						if (mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND))
 						{
 							err ("Command Timeout.");
-							mdc800->rw_lock=0;
+							spin_unlock (&mdc800->io_lock);
 							return -EIO;
 						}
 					}
@@ -817,7 +852,7 @@
 		}
 		i++;
 	}
-	mdc800->rw_lock=0;
+	spin_unlock (&mdc800->io_lock);
 	return i;
 }
 
@@ -866,10 +901,11 @@
 	/* Allocate Memory */
 	try (mdc800=kmalloc (sizeof (struct mdc800_data), GFP_KERNEL));
 
+	memset(mdc800, 0, sizeof(struct mdc800_data));
 	mdc800->dev=0;
 	mdc800->open=0;
 	mdc800->state=NOT_CONNECTED;
-	memset(mdc800, 0, sizeof(struct mdc800_data));
+	spin_lock_init (&mdc800->io_lock);
 
 	init_waitqueue_head (&mdc800->irq_wait);
 	init_waitqueue_head (&mdc800->write_wait);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)