--- linux-2.4.28/drivers/usb/devio.c.orig	2004-11-28 22:24:49.000000000 +0200
+++ linux-2.4.28/drivers/usb/devio.c	2004-12-01 12:47:02.000000000 +0200
@@ -1153,45 +1153,62 @@ static int usbdev_ioctl(struct inode *in
 
 	if (!(file->f_mode & FMODE_WRITE))
 		return -EPERM;
-	down_read(&ps->devsem);
+	down_read(&ps->devsem); /* FIXME: should we set devsem also per "case" 
+				   like exclusive_access to avoid
+				   blocking nonexistent ioctls ? */
 	if (!ps->dev) {
 		up_read(&ps->devsem);
 		return -ENODEV;
 	}
-
-	/*
-	 * grab device's exclusive_access mutex to prevent its driver from
-	 * using this device while it is being accessed by us.
+        /*
+         * Some ioctls don't touch the device and can be called without
+         * grabbing its exclusive_access mutex; they are handled in this
+         * switch.  Other ioctls which need exclusive_access are handled in
+         * usbdev_ioctl_exclusive(), so we grab device's exclusive_access 
+	 * mutex ONLY if needed and WHEN actually needed!!! 
 	 */
-	down(&ps->dev->exclusive_access);
-
 	switch (cmd) {
 	case USBDEVFS_CONTROL:
-		ret = proc_control(ps, (void *)arg);
-		if (ret >= 0)
-			inode->i_mtime = CURRENT_TIME;
+		if (down_interruptible(&ps->dev->exclusive_access) == 0) {
+			ret = proc_control(ps, (void *)arg);
+			up(&ps->dev->exclusive_access);
+			if (ret >= 0)
+				inode->i_mtime = CURRENT_TIME;
+		} else ret = -ERESTARTSYS;
 		break;
 
 	case USBDEVFS_BULK:
-		ret = proc_bulk(ps, (void *)arg);
-		if (ret >= 0)
-			inode->i_mtime = CURRENT_TIME;
+		if (down_interruptible(&ps->dev->exclusive_access) == 0) {
+			ret = proc_bulk(ps, (void *)arg);
+			up(&ps->dev->exclusive_access);
+			if (ret >= 0)
+				inode->i_mtime = CURRENT_TIME;
+		} else ret = -ERESTARTSYS;
 		break;
 
 	case USBDEVFS_RESETEP:
-		ret = proc_resetep(ps, (void *)arg);
-		if (ret >= 0)
-			inode->i_mtime = CURRENT_TIME;
+		if (down_interruptible(&ps->dev->exclusive_access) == 0) {
+			ret = proc_resetep(ps, (void *)arg);
+			up(&ps->dev->exclusive_access);
+			if (ret >= 0)
+				inode->i_mtime = CURRENT_TIME;
+		} else ret = -ERESTARTSYS;
 		break;
 
 	case USBDEVFS_RESET:
-		ret = proc_resetdevice(ps);
+		if (down_interruptible(&ps->dev->exclusive_access) == 0) {
+			ret = proc_resetdevice(ps);
+			up(&ps->dev->exclusive_access);
+		} else ret = -ERESTARTSYS;
 		break;
 	
 	case USBDEVFS_CLEAR_HALT:
-		ret = proc_clearhalt(ps, (void *)arg);
-		if (ret >= 0)
-			inode->i_mtime = CURRENT_TIME;
+		if (down_interruptible(&ps->dev->exclusive_access) == 0) {
+			ret = proc_clearhalt(ps, (void *)arg);
+			up(&ps->dev->exclusive_access);
+			if (ret >= 0)
+				inode->i_mtime = CURRENT_TIME;
+		} else ret = -ERESTARTSYS;
 		break;
 
 	case USBDEVFS_GETDRIVER:
@@ -1203,21 +1220,33 @@ static int usbdev_ioctl(struct inode *in
 		break;
 
 	case USBDEVFS_SETINTERFACE:
-		ret = proc_setintf(ps, (void *)arg);
+		if (down_interruptible(&ps->dev->exclusive_access) == 0) {
+			ret = proc_setintf(ps, (void *)arg);
+			up(&ps->dev->exclusive_access);
+		} else ret = -ERESTARTSYS;
 		break;
 
 	case USBDEVFS_SETCONFIGURATION:
-		ret = proc_setconfig(ps, (void *)arg);
+		if (down_interruptible(&ps->dev->exclusive_access) == 0) {
+			ret = proc_setconfig(ps, (void *)arg);
+			up(&ps->dev->exclusive_access);
+		} else ret = -ERESTARTSYS;
 		break;
 
 	case USBDEVFS_SUBMITURB:
-		ret = proc_submiturb(ps, (void *)arg);
-		if (ret >= 0)
-			inode->i_mtime = CURRENT_TIME;
+		if (down_interruptible(&ps->dev->exclusive_access) == 0) {
+			ret = proc_submiturb(ps, (void *)arg);
+			up(&ps->dev->exclusive_access);
+			if (ret >= 0)
+				inode->i_mtime = CURRENT_TIME;
+		} else ret = -ERESTARTSYS;
 		break;
 
 	case USBDEVFS_DISCARDURB:
-		ret = proc_unlinkurb(ps, (void *)arg);
+		if (down_interruptible(&ps->dev->exclusive_access) == 0) {
+			ret = proc_unlinkurb(ps, (void *)arg);
+			up(&ps->dev->exclusive_access);
+		} else ret = -ERESTARTSYS;
 		break;
 
 	case USBDEVFS_REAPURB:
@@ -1233,18 +1262,26 @@ static int usbdev_ioctl(struct inode *in
 		break;
 
 	case USBDEVFS_CLAIMINTERFACE:
-		ret = proc_claiminterface(ps, (void *)arg);
+		if (down_interruptible(&ps->dev->exclusive_access) == 0) {
+			ret = proc_claiminterface(ps, (void *)arg);
+			up(&ps->dev->exclusive_access);
+		} else ret = -ERESTARTSYS;
 		break;
 
 	case USBDEVFS_RELEASEINTERFACE:
-		ret = proc_releaseinterface(ps, (void *)arg);
+		if (down_interruptible(&ps->dev->exclusive_access) == 0) {
+			ret = proc_releaseinterface(ps, (void *)arg);
+			up(&ps->dev->exclusive_access);
+		} else ret = -ERESTARTSYS;
 		break;
 
 	case USBDEVFS_IOCTL:
-		ret = proc_ioctl(ps, (void *) arg);
+		if (down_interruptible(&ps->dev->exclusive_access) == 0) {
+			ret = proc_ioctl(ps, (void *) arg);
+			up(&ps->dev->exclusive_access);
+		} else ret = -ERESTARTSYS;
 		break;
 	}
-	up(&ps->dev->exclusive_access);
 	up_read(&ps->devsem);
 	if (ret >= 0)
 		inode->i_atime = CURRENT_TIME;
