[PATCH 8/9] staging/easycap: Eliminate BKL

Mike Thomas rmthomas at sciolus.org
Sun Nov 7 20:09:19 UTC 2010


No locking is required for normal operation of the driver, but locking
is needed to prevent an Oops during some hot-unplugging scenarios.  The
BKL is replaced here by mutex locks together with traps to detect null
pointers following asynchronous device disconnection.

Signed-off-by: Mike Thomas <rmthomas at sciolus.org>
---
 drivers/staging/easycap/Kconfig         |    1 -
 drivers/staging/easycap/Makefile        |    1 +
 drivers/staging/easycap/easycap.h       |   16 +-
 drivers/staging/easycap/easycap_debug.h |    1 +
 drivers/staging/easycap/easycap_ioctl.c |  426 ++++++++++++++++++++++++-------
 drivers/staging/easycap/easycap_low.c   |   55 ++++-
 drivers/staging/easycap/easycap_main.c  |  311 ++++++++++++++++-------
 drivers/staging/easycap/easycap_sound.c |   76 ++++++-
 8 files changed, 695 insertions(+), 192 deletions(-)

diff --git a/drivers/staging/easycap/Kconfig b/drivers/staging/easycap/Kconfig
index 9d5fe4d..bd96f39 100644
--- a/drivers/staging/easycap/Kconfig
+++ b/drivers/staging/easycap/Kconfig
@@ -1,7 +1,6 @@
 config EASYCAP
 	tristate "EasyCAP USB ID 05e1:0408 support"
 	depends on USB && VIDEO_DEV
-	depends on BKL # please fix
 
 	---help---
 	  This is an integrated audio/video driver for EasyCAP cards with
diff --git a/drivers/staging/easycap/Makefile b/drivers/staging/easycap/Makefile
index 8a3d911..f1f2fbe 100644
--- a/drivers/staging/easycap/Makefile
+++ b/drivers/staging/easycap/Makefile
@@ -10,4 +10,5 @@ ccflags-y := -Wall
 ccflags-y += -DEASYCAP_IS_VIDEODEV_CLIENT
 ccflags-y += -DEASYCAP_NEEDS_V4L2_DEVICE_H
 ccflags-y += -DEASYCAP_NEEDS_V4L2_FOPS
+ccflags-y += -DEASYCAP_NEEDS_UNLOCKED_IOCTL
 
diff --git a/drivers/staging/easycap/easycap.h b/drivers/staging/easycap/easycap.h
index e9410b7..762c6ce 100644
--- a/drivers/staging/easycap/easycap.h
+++ b/drivers/staging/easycap/easycap.h
@@ -251,6 +251,12 @@ INTERLACE_MANY
  *  STRUCTURE DEFINITIONS
  */
 /*---------------------------------------------------------------------------*/
+struct easycap_dongle {
+struct easycap *peasycap;
+struct mutex mutex_video;
+struct mutex mutex_audio;
+};
+/*---------------------------------------------------------------------------*/
 struct data_buffer {
 struct list_head list_head;
 void *pgo;
@@ -491,7 +497,10 @@ struct data_buffer audio_buffer[];
 void             easycap_complete(struct urb *);
 int              easycap_open(struct inode *, struct file *);
 int              easycap_release(struct inode *, struct file *);
-long             easycap_ioctl(struct file *, unsigned int,  unsigned long);
+long             easycap_ioctl_noinode(struct file *, unsigned int, \
+								unsigned long);
+int              easycap_ioctl(struct inode *, struct file *, unsigned int, \
+								unsigned long);
 
 /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
 #if defined(EASYCAP_IS_VIDEODEV_CLIENT)
@@ -538,7 +547,10 @@ void             easysnd_complete(struct urb *);
 ssize_t          easysnd_read(struct file *, char __user *, size_t, loff_t *);
 int              easysnd_open(struct inode *, struct file *);
 int              easysnd_release(struct inode *, struct file *);
-long             easysnd_ioctl(struct file *, unsigned int,  unsigned long);
+long             easysnd_ioctl_noinode(struct file *, unsigned int, \
+								unsigned long);
+int              easysnd_ioctl(struct inode *, struct file *, unsigned int, \
+								unsigned long);
 unsigned int     easysnd_poll(struct file *, poll_table *);
 void             easysnd_delete(struct kref *);
 int              submit_audio_urbs(struct easycap *);
diff --git a/drivers/staging/easycap/easycap_debug.h b/drivers/staging/easycap/easycap_debug.h
index 7042fe8..518392e 100644
--- a/drivers/staging/easycap/easycap_debug.h
+++ b/drivers/staging/easycap/easycap_debug.h
@@ -26,3 +26,4 @@
 /*****************************************************************************/
 extern int debug;
 extern int gain;
+extern struct easycap_dongle easycap_dongle[];
diff --git a/drivers/staging/easycap/easycap_ioctl.c b/drivers/staging/easycap/easycap_ioctl.c
index 5e9133b..2f9b3ea 100644
--- a/drivers/staging/easycap/easycap_ioctl.c
+++ b/drivers/staging/easycap/easycap_ioctl.c
@@ -953,11 +953,23 @@ SAM("WARNING: failed to adjust mute: control not found\n");
 return -ENOENT;
 }
 /*****************************************************************************/
-static int easycap_ioctl_bkl(struct inode *inode, struct file *file,
-			     unsigned int cmd, unsigned long arg)
+/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
+#if ((defined(EASYCAP_IS_VIDEODEV_CLIENT)) || \
+	(defined(EASYCAP_NEEDS_UNLOCKED_IOCTL)))
+long
+easycap_ioctl_noinode(struct file *file, unsigned int cmd, unsigned long arg) {
+	return (long)easycap_ioctl((struct inode *)NULL, file, cmd, arg);
+}
+#endif /*EASYCAP_IS_VIDEODEV_CLIENT||EASYCAP_NEEDS_UNLOCKED_IOCTL*/
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+/*---------------------------------------------------------------------------*/
+int
+easycap_ioctl(struct inode *inode, struct file *file,
+					unsigned int cmd, unsigned long arg)
 {
 struct easycap *peasycap;
 struct usb_device *p;
+int kd;
 
 if (NULL == file) {
 	SAY("ERROR:  file is NULL\n");
@@ -973,6 +985,48 @@ if (NULL == p) {
 	SAM("ERROR: peasycap->pusb_device is NULL\n");
 	return -EFAULT;
 }
+kd = isdongle(peasycap);
+if (0 <= kd && DONGLE_MANY > kd) {
+	if (mutex_lock_interruptible(&easycap_dongle[kd].mutex_video)) {
+		SAY("ERROR: cannot lock easycap_dongle[%i].mutex_video\n", kd);
+		return -ERESTARTSYS;
+	}
+	JOM(4, "locked easycap_dongle[%i].mutex_video\n", kd);
+/*---------------------------------------------------------------------------*/
+/*
+ *  MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap,
+ *  IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
+ *  IF NECESSARY, BAIL OUT.
+*/
+/*---------------------------------------------------------------------------*/
+	if (kd != isdongle(peasycap))
+		return -ERESTARTSYS;
+	if (NULL == file) {
+		SAY("ERROR:  file is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
+		return -ERESTARTSYS;
+	}
+	peasycap = file->private_data;
+	if (NULL == peasycap) {
+		SAY("ERROR:  peasycap is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
+		return -ERESTARTSYS;
+	}
+	p = peasycap->pusb_device;
+	if (NULL == peasycap->pusb_device) {
+		SAM("ERROR: peasycap->pusb_device is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
+		return -ERESTARTSYS;
+	}
+} else {
+/*---------------------------------------------------------------------------*/
+/*
+ *  IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE
+ *  ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED.  BAIL OUT.
+*/
+/*---------------------------------------------------------------------------*/
+	return -ERESTARTSYS;
+}
 /*---------------------------------------------------------------------------*/
 switch (cmd) {
 case VIDIOC_QUERYCAP: {
@@ -984,7 +1038,9 @@ case VIDIOC_QUERYCAP: {
 	JOM(8, "VIDIOC_QUERYCAP\n");
 
 	if (16 <= strlen(EASYCAP_DRIVER_VERSION)) {
-		SAM("ERROR: bad driver version string\n"); return -EINVAL;
+		SAM("ERROR: bad driver version string\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
+		return -EINVAL;
 	}
 	strcpy(&version[0], EASYCAP_DRIVER_VERSION);
 	for (i = 0; i < 3; i++)
@@ -1001,6 +1057,7 @@ case VIDIOC_QUERYCAP: {
 			if (0 != rc) {
 				SAM("ERROR: %i=strict_strtol(%s,.,,)\n", \
 								rc, p1);
+				mutex_unlock(&easycap_dongle[kd].mutex_video);
 				return -EINVAL;
 			}
 			k[i] = (int)lng;
@@ -1030,8 +1087,10 @@ case VIDIOC_QUERYCAP: {
 					&v4l2_capability.bus_info[0]);
 	}
 	if (0 != copy_to_user((void __user *)arg, &v4l2_capability, \
-					sizeof(struct v4l2_capability)))
+					sizeof(struct v4l2_capability))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1042,8 +1101,10 @@ case VIDIOC_ENUMINPUT: {
 	JOM(8, "VIDIOC_ENUMINPUT\n");
 
 	if (0 != copy_from_user(&v4l2_input, (void __user *)arg, \
-					sizeof(struct v4l2_input)))
+					sizeof(struct v4l2_input))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
 	index = v4l2_input.index;
 	memset(&v4l2_input, 0, sizeof(struct v4l2_input));
@@ -1123,13 +1184,16 @@ case VIDIOC_ENUMINPUT: {
 	}
 	default: {
 		JOM(8, "%i=index: exhausts inputs\n", index);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
 	}
 	}
 
 	if (0 != copy_to_user((void __user *)arg, &v4l2_input, \
-						sizeof(struct v4l2_input)))
+						sizeof(struct v4l2_input))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1139,8 +1203,10 @@ case VIDIOC_G_INPUT: {
 	JOM(8, "VIDIOC_G_INPUT\n");
 	index = (__u32)peasycap->input;
 	JOM(8, "user is told: %i\n", index);
-	if (0 != copy_to_user((void __user *)arg, &index, sizeof(__u32)))
+	if (0 != copy_to_user((void __user *)arg, &index, sizeof(__u32))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1151,8 +1217,10 @@ case VIDIOC_S_INPUT:
 
 	JOM(8, "VIDIOC_S_INPUT\n");
 
-	if (0 != copy_from_user(&index, (void __user *)arg, sizeof(__u32)))
+	if (0 != copy_from_user(&index, (void __user *)arg, sizeof(__u32))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
 	JOM(8, "user requests input %i\n", index);
 
@@ -1163,6 +1231,7 @@ case VIDIOC_S_INPUT:
 
 	if ((0 > index) || (INPUT_MANY <= index)) {
 		JOM(8, "ERROR:  bad requested input: %i\n", index);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
 	}
 
@@ -1171,6 +1240,7 @@ case VIDIOC_S_INPUT:
 		JOM(8, "newinput(.,%i) OK\n", (int)index);
 	} else {
 		SAM("ERROR: newinput(.,%i) returned %i\n", (int)index, rc);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
 	}
 	break;
@@ -1178,6 +1248,7 @@ case VIDIOC_S_INPUT:
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 case VIDIOC_ENUMAUDIO: {
 	JOM(8, "VIDIOC_ENUMAUDIO\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
 	return -EINVAL;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1187,18 +1258,24 @@ case VIDIOC_ENUMAUDOUT: {
 	JOM(8, "VIDIOC_ENUMAUDOUT\n");
 
 	if (0 != copy_from_user(&v4l2_audioout, (void __user *)arg, \
-					sizeof(struct v4l2_audioout)))
+					sizeof(struct v4l2_audioout))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
-	if (0 != v4l2_audioout.index)
+	if (0 != v4l2_audioout.index) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
+	}
 	memset(&v4l2_audioout, 0, sizeof(struct v4l2_audioout));
 	v4l2_audioout.index = 0;
 	strcpy(&v4l2_audioout.name[0], "Soundtrack");
 
 	if (0 != copy_to_user((void __user *)arg, &v4l2_audioout, \
-					sizeof(struct v4l2_audioout)))
+					sizeof(struct v4l2_audioout))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1209,8 +1286,10 @@ case VIDIOC_QUERYCTRL: {
 	JOM(8, "VIDIOC_QUERYCTRL\n");
 
 	if (0 != copy_from_user(&v4l2_queryctrl, (void __user *)arg, \
-					sizeof(struct v4l2_queryctrl)))
+					sizeof(struct v4l2_queryctrl))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
 	i1 = 0;
 	while (0xFFFFFFFF != easycap_control[i1].id) {
@@ -1225,18 +1304,21 @@ case VIDIOC_QUERYCTRL: {
 	}
 	if (0xFFFFFFFF == easycap_control[i1].id) {
 		JOM(8, "%i=index: exhausts controls\n", i1);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
 	}
 	if (0 != copy_to_user((void __user *)arg, &v4l2_queryctrl, \
-					sizeof(struct v4l2_queryctrl)))
+					sizeof(struct v4l2_queryctrl))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 case VIDIOC_QUERYMENU: {
 	JOM(8, "VIDIOC_QUERYMENU unsupported\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
 	return -EINVAL;
-	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 case VIDIOC_G_CTRL: {
@@ -1246,11 +1328,13 @@ case VIDIOC_G_CTRL: {
 	pv4l2_control = kzalloc(sizeof(struct v4l2_control), GFP_KERNEL);
 	if (!pv4l2_control) {
 		SAM("ERROR: out of memory\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -ENOMEM;
 	}
 	if (0 != copy_from_user(pv4l2_control, (void __user *)arg, \
 					sizeof(struct v4l2_control))) {
 		kfree(pv4l2_control);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
 	}
 
@@ -1292,12 +1376,14 @@ case VIDIOC_G_CTRL: {
 		SAM("ERROR: unknown V4L2 control: 0x%08X=id\n", \
 							pv4l2_control->id);
 		kfree(pv4l2_control);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
 	}
 	}
 	if (0 != copy_to_user((void __user *)arg, pv4l2_control, \
 					sizeof(struct v4l2_control))) {
 		kfree(pv4l2_control);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
 	}
 	kfree(pv4l2_control);
@@ -1316,8 +1402,10 @@ case VIDIOC_S_CTRL:
 	JOM(8, "VIDIOC_S_CTRL\n");
 
 	if (0 != copy_from_user(&v4l2_control, (void __user *)arg, \
-					sizeof(struct v4l2_control)))
+					sizeof(struct v4l2_control))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
 	switch (v4l2_control.id) {
 	case V4L2_CID_BRIGHTNESS: {
@@ -1366,14 +1454,16 @@ case VIDIOC_S_CTRL:
 	default: {
 		SAM("ERROR: unknown V4L2 control: 0x%08X=id\n", \
 							v4l2_control.id);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
-		}
+	}
 	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 case VIDIOC_S_EXT_CTRLS: {
 	JOM(8, "VIDIOC_S_EXT_CTRLS unsupported\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
 	return -EINVAL;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1384,8 +1474,10 @@ case VIDIOC_ENUM_FMT: {
 	JOM(8, "VIDIOC_ENUM_FMT\n");
 
 	if (0 != copy_from_user(&v4l2_fmtdesc, (void __user *)arg, \
-					sizeof(struct v4l2_fmtdesc)))
+					sizeof(struct v4l2_fmtdesc))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
 	index = v4l2_fmtdesc.index;
 	memset(&v4l2_fmtdesc, 0, sizeof(struct v4l2_fmtdesc));
@@ -1438,12 +1530,15 @@ case VIDIOC_ENUM_FMT: {
 	}
 	default: {
 		JOM(8, "%i=index: exhausts formats\n", index);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
 	}
 	}
 	if (0 != copy_to_user((void __user *)arg, &v4l2_fmtdesc, \
-					sizeof(struct v4l2_fmtdesc)))
+					sizeof(struct v4l2_fmtdesc))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1459,8 +1554,10 @@ case VIDIOC_ENUM_FRAMESIZES: {
 	JOM(8, "VIDIOC_ENUM_FRAMESIZES\n");
 
 	if (0 != copy_from_user(&v4l2_frmsizeenum, (void __user *)arg, \
-					sizeof(struct v4l2_frmsizeenum)))
+					sizeof(struct v4l2_frmsizeenum))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
 	index = v4l2_frmsizeenum.index;
 
@@ -1510,6 +1607,7 @@ case VIDIOC_ENUM_FRAMESIZES: {
 		}
 		default: {
 			JOM(8, "%i=index: exhausts framesizes\n", index);
+			mutex_unlock(&easycap_dongle[kd].mutex_video);
 			return -EINVAL;
 		}
 		}
@@ -1567,13 +1665,16 @@ case VIDIOC_ENUM_FRAMESIZES: {
 		}
 		default: {
 			JOM(8, "%i=index: exhausts framesizes\n", index);
+			mutex_unlock(&easycap_dongle[kd].mutex_video);
 			return -EINVAL;
 		}
 		}
 	}
 	if (0 != copy_to_user((void __user *)arg, &v4l2_frmsizeenum, \
-					sizeof(struct v4l2_frmsizeenum)))
+					sizeof(struct v4l2_frmsizeenum))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1600,6 +1701,7 @@ case VIDIOC_ENUM_FRAMEINTERVALS: {
 
 	if (0 != copy_from_user(&v4l2_frmivalenum, (void __user *)arg, \
 					sizeof(struct v4l2_frmivalenum))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
 	}
 
@@ -1626,12 +1728,15 @@ case VIDIOC_ENUM_FRAMEINTERVALS: {
 	}
 	default: {
 		JOM(8, "%i=index: exhausts frameintervals\n", index);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
 	}
 	}
 	if (0 != copy_to_user((void __user *)arg, &v4l2_frmivalenum, \
-					sizeof(struct v4l2_frmivalenum)))
+					sizeof(struct v4l2_frmivalenum))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1643,24 +1748,28 @@ case VIDIOC_G_FMT: {
 	pv4l2_format = kzalloc(sizeof(struct v4l2_format), GFP_KERNEL);
 	if (!pv4l2_format) {
 		SAM("ERROR: out of memory\n");
-	return -ENOMEM;
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
+		return -ENOMEM;
 	}
 	pv4l2_pix_format = kzalloc(sizeof(struct v4l2_pix_format), GFP_KERNEL);
 	if (!pv4l2_pix_format) {
 		SAM("ERROR: out of memory\n");
 		kfree(pv4l2_format);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -ENOMEM;
 	}
 	if (0 != copy_from_user(pv4l2_format, (void __user *)arg, \
 					sizeof(struct v4l2_format))) {
 		kfree(pv4l2_format);
 		kfree(pv4l2_pix_format);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
 	}
 
 	if (pv4l2_format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
 		kfree(pv4l2_format);
 		kfree(pv4l2_pix_format);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
 	}
 
@@ -1676,6 +1785,7 @@ case VIDIOC_G_FMT: {
 					sizeof(struct v4l2_format))) {
 		kfree(pv4l2_format);
 		kfree(pv4l2_pix_format);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
 	}
 	kfree(pv4l2_format);
@@ -1699,8 +1809,10 @@ case VIDIOC_S_FMT: {
 	}
 
 	if (0 != copy_from_user(&v4l2_format, (void __user *)arg, \
-					sizeof(struct v4l2_format)))
+					sizeof(struct v4l2_format))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
 	best_format = adjust_format(peasycap, \
 					v4l2_format.fmt.pix.width, \
@@ -1709,9 +1821,12 @@ case VIDIOC_S_FMT: {
 					v4l2_format.fmt.pix.field, \
 					try);
 	if (0 > best_format) {
-		if (-EBUSY == best_format)
+		if (-EBUSY == best_format) {
+			mutex_unlock(&easycap_dongle[kd].mutex_video);
 			return -EBUSY;
+		}
 		JOM(8, "WARNING: adjust_format() returned %i\n", best_format);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -ENOENT;
 	}
 /*...........................................................................*/
@@ -1723,8 +1838,10 @@ case VIDIOC_S_FMT: {
 	JOM(8, "user is told: %s\n", &easycap_format[best_format].name[0]);
 
 	if (0 != copy_to_user((void __user *)arg, &v4l2_format, \
-					sizeof(struct v4l2_format)))
+					sizeof(struct v4l2_format))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1734,8 +1851,10 @@ case VIDIOC_CROPCAP: {
 	JOM(8, "VIDIOC_CROPCAP\n");
 
 	if (0 != copy_from_user(&v4l2_cropcap, (void __user *)arg, \
-					sizeof(struct v4l2_cropcap)))
+					sizeof(struct v4l2_cropcap))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
 	if (v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		JOM(8, "v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
@@ -1756,20 +1875,24 @@ case VIDIOC_CROPCAP: {
 	JOM(8, "user is told: %ix%i\n", peasycap->width, peasycap->height);
 
 	if (0 != copy_to_user((void __user *)arg, &v4l2_cropcap, \
-					sizeof(struct v4l2_cropcap)))
+					sizeof(struct v4l2_cropcap))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 case VIDIOC_G_CROP:
 case VIDIOC_S_CROP: {
 	JOM(8, "VIDIOC_G_CROP|VIDIOC_S_CROP  unsupported\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
 	return -EINVAL;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 case VIDIOC_QUERYSTD: {
 	JOM(8, "VIDIOC_QUERYSTD: " \
 			"EasyCAP is incapable of detecting standard\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
 	return -EINVAL;
 	break;
 }
@@ -1790,8 +1913,10 @@ case VIDIOC_ENUMSTD: {
 	JOM(8, "VIDIOC_ENUMSTD\n");
 
 	if (0 != copy_from_user(&v4l2_standard, (void __user *)arg, \
-					sizeof(struct v4l2_standard)))
+					sizeof(struct v4l2_standard))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	index = v4l2_standard.index;
 
 	last3 = last2; last2 = last1; last1 = last0; last0 = index;
@@ -1811,6 +1936,7 @@ case VIDIOC_ENUMSTD: {
 	}
 	if (0xFFFF == peasycap_standard->mask) {
 		JOM(8, "%i=index: exhausts standards\n", index);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
 	}
 	JOM(8, "%i=index: %s\n", index, \
@@ -1821,8 +1947,10 @@ case VIDIOC_ENUMSTD: {
 	v4l2_standard.index = index;
 
 	if (0 != copy_to_user((void __user *)arg, &v4l2_standard, \
-					sizeof(struct v4l2_standard)))
+					sizeof(struct v4l2_standard))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1835,12 +1963,15 @@ case VIDIOC_G_STD: {
 	if (0 > peasycap->standard_offset) {
 		JOM(8, "%i=peasycap->standard_offset\n", \
 					peasycap->standard_offset);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EBUSY;
 	}
 
 	if (0 != copy_from_user(&std_id, (void __user *)arg, \
-						sizeof(v4l2_std_id)))
+						sizeof(v4l2_std_id))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
 	peasycap_standard = &easycap_standard[peasycap->standard_offset];
 	std_id = peasycap_standard->v4l2_standard.id;
@@ -1849,8 +1980,10 @@ case VIDIOC_G_STD: {
 				&peasycap_standard->v4l2_standard.name[0]);
 
 	if (0 != copy_to_user((void __user *)arg, &std_id, \
-						sizeof(v4l2_std_id)))
+						sizeof(v4l2_std_id))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1861,8 +1994,10 @@ case VIDIOC_S_STD: {
 	JOM(8, "VIDIOC_S_STD\n");
 
 	if (0 != copy_from_user(&std_id, (void __user *)arg, \
-						sizeof(v4l2_std_id)))
+						sizeof(v4l2_std_id))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
 	JOM(8, "User requests standard: 0x%08X%08X\n", \
 		(int)((std_id & (((v4l2_std_id)0xFFFFFFFF) << 32)) >> 32), \
@@ -1871,6 +2006,7 @@ case VIDIOC_S_STD: {
 	rc = adjust_standard(peasycap, std_id);
 	if (0 > rc) {
 		JOM(8, "WARNING: adjust_standard() returned %i\n", rc);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -ENOENT;
 	}
 	break;
@@ -1883,13 +2019,19 @@ case VIDIOC_REQBUFS: {
 	JOM(8, "VIDIOC_REQBUFS\n");
 
 	if (0 != copy_from_user(&v4l2_requestbuffers, (void __user *)arg, \
-				sizeof(struct v4l2_requestbuffers)))
+				sizeof(struct v4l2_requestbuffers))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
-	if (v4l2_requestbuffers.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+	if (v4l2_requestbuffers.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
-	if (v4l2_requestbuffers.memory != V4L2_MEMORY_MMAP)
+	}
+	if (v4l2_requestbuffers.memory != V4L2_MEMORY_MMAP) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
+	}
 	nbuffers = v4l2_requestbuffers.count;
 	JOM(8, "                   User requests %i buffers ...\n", nbuffers);
 	if (nbuffers < 2)
@@ -1907,8 +2049,10 @@ case VIDIOC_REQBUFS: {
 	peasycap->frame_buffer_many = nbuffers;
 
 	if (0 != copy_to_user((void __user *)arg, &v4l2_requestbuffers, \
-				sizeof(struct v4l2_requestbuffers)))
+				sizeof(struct v4l2_requestbuffers))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1921,15 +2065,20 @@ case VIDIOC_QUERYBUF: {
 	if (peasycap->video_eof) {
 		JOM(8, "returning -EIO because  %i=video_eof\n", \
 							peasycap->video_eof);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EIO;
 	}
 
 	if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, \
-					sizeof(struct v4l2_buffer)))
+					sizeof(struct v4l2_buffer))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
-	if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+	if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
+	}
 	index = v4l2_buffer.index;
 	if (index < 0 || index >= peasycap->frame_buffer_many)
 		return -EINVAL;
@@ -1958,8 +2107,10 @@ case VIDIOC_QUERYBUF: {
 	JOM(16, "  %10i=length\n", v4l2_buffer.length);
 
 	if (0 != copy_to_user((void __user *)arg, &v4l2_buffer, \
-					sizeof(struct v4l2_buffer)))
+					sizeof(struct v4l2_buffer))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 	break;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -1969,24 +2120,34 @@ case VIDIOC_QBUF: {
 	JOM(8, "VIDIOC_QBUF\n");
 
 	if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, \
-					sizeof(struct v4l2_buffer)))
+					sizeof(struct v4l2_buffer))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
-	if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+	if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
-	if (v4l2_buffer.memory != V4L2_MEMORY_MMAP)
+	}
+	if (v4l2_buffer.memory != V4L2_MEMORY_MMAP) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
+	}
 	if (v4l2_buffer.index < 0 || \
-		 (v4l2_buffer.index >= peasycap->frame_buffer_many))
+		 (v4l2_buffer.index >= peasycap->frame_buffer_many)) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
+	}
 	v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED;
 
 	peasycap->done[v4l2_buffer.index]   = 0;
 	peasycap->queued[v4l2_buffer.index] = V4L2_BUF_FLAG_QUEUED;
 
 	if (0 != copy_to_user((void __user *)arg, &v4l2_buffer, \
-					sizeof(struct v4l2_buffer)))
+					sizeof(struct v4l2_buffer))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
 	JOM(8, ".....   user queueing frame buffer %i\n", \
 						(int)v4l2_buffer.index);
@@ -2017,15 +2178,20 @@ case VIDIOC_DQBUF:
 		JOM(8, "returning -EIO because  " \
 				"%i=video_idle  %i=video_eof\n", \
 				peasycap->video_idle, peasycap->video_eof);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EIO;
 	}
 
 	if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, \
-					sizeof(struct v4l2_buffer)))
+					sizeof(struct v4l2_buffer))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
-	if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+	if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
+	}
 
 	if (true == peasycap->offerfields) {
 		/*-----------------------------------------------------------*/
@@ -2047,6 +2213,7 @@ case VIDIOC_DQBUF:
 
 	if (!peasycap->video_isoc_streaming) {
 		JOM(16, "returning -EIO because video urbs not streaming\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EIO;
 	}
 /*---------------------------------------------------------------------------*/
@@ -2063,12 +2230,15 @@ case VIDIOC_DQBUF:
 			if (-EIO == rcdq) {
 				JOM(8, "returning -EIO because " \
 						"dqbuf() returned -EIO\n");
+				mutex_unlock(&easycap_dongle[kd].mutex_video);
 				return -EIO;
 			}
 		} while (0 != rcdq);
 	} else {
-		if (peasycap->video_eof)
+		if (peasycap->video_eof) {
+			mutex_unlock(&easycap_dongle[kd].mutex_video);
 			return -EIO;
+		}
 	}
 	if (V4L2_BUF_FLAG_DONE != peasycap->done[peasycap->frame_read]) {
 		SAM("ERROR: V4L2_BUF_FLAG_DONE != 0x%08X\n", \
@@ -2155,8 +2325,10 @@ case VIDIOC_DQBUF:
 	JOM(16, "  %10i=length\n", v4l2_buffer.length);
 
 	if (0 != copy_to_user((void __user *)arg, &v4l2_buffer, \
-						sizeof(struct v4l2_buffer)))
+						sizeof(struct v4l2_buffer))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
+	}
 
 	input = peasycap->frame_buffer[peasycap->frame_read][0].input;
 	if (0x08 & input) {
@@ -2187,6 +2359,7 @@ case VIDIOC_STREAMON: {
 		peasycap->merit[i] = 0;
 	if ((struct usb_device *)NULL == peasycap->pusb_device) {
 		SAM("ERROR: peasycap->pusb_device is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
 	}
 	submit_video_urbs(peasycap);
@@ -2202,6 +2375,7 @@ case VIDIOC_STREAMOFF: {
 
 	if ((struct usb_device *)NULL == peasycap->pusb_device) {
 		SAM("ERROR: peasycap->pusb_device is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
 	}
 
@@ -2227,16 +2401,19 @@ case VIDIOC_G_PARM: {
 	pv4l2_streamparm = kzalloc(sizeof(struct v4l2_streamparm), GFP_KERNEL);
 	if (!pv4l2_streamparm) {
 		SAM("ERROR: out of memory\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -ENOMEM;
 	}
 	if (0 != copy_from_user(pv4l2_streamparm, (void __user *)arg, \
 					sizeof(struct v4l2_streamparm))) {
 		kfree(pv4l2_streamparm);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
 	}
 
 	if (pv4l2_streamparm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
 		kfree(pv4l2_streamparm);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EINVAL;
 	}
 	pv4l2_streamparm->parm.capture.capability = 0;
@@ -2262,6 +2439,7 @@ case VIDIOC_G_PARM: {
 	if (0 != copy_to_user((void __user *)arg, pv4l2_streamparm, \
 					sizeof(struct v4l2_streamparm))) {
 		kfree(pv4l2_streamparm);
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
 		return -EFAULT;
 	}
 	kfree(pv4l2_streamparm);
@@ -2270,21 +2448,25 @@ case VIDIOC_G_PARM: {
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 case VIDIOC_S_PARM: {
 	JOM(8, "VIDIOC_S_PARM unsupported\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
 	return -EINVAL;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 case VIDIOC_G_AUDIO: {
 	JOM(8, "VIDIOC_G_AUDIO unsupported\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
 	return -EINVAL;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 case VIDIOC_S_AUDIO: {
 	JOM(8, "VIDIOC_S_AUDIO unsupported\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
 	return -EINVAL;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 case VIDIOC_S_TUNER: {
 	JOM(8, "VIDIOC_S_TUNER unsupported\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
 	return -EINVAL;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -2292,44 +2474,50 @@ case VIDIOC_G_FBUF:
 case VIDIOC_S_FBUF:
 case VIDIOC_OVERLAY: {
 	JOM(8, "VIDIOC_G_FBUF|VIDIOC_S_FBUF|VIDIOC_OVERLAY unsupported\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
 	return -EINVAL;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 case VIDIOC_G_TUNER: {
 	JOM(8, "VIDIOC_G_TUNER unsupported\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
 	return -EINVAL;
 }
 case VIDIOC_G_FREQUENCY:
 case VIDIOC_S_FREQUENCY: {
 	JOM(8, "VIDIOC_G_FREQUENCY|VIDIOC_S_FREQUENCY unsupported\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
 	return -EINVAL;
 }
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 default: {
 	JOM(8, "ERROR: unrecognized V4L2 IOCTL command: 0x%08X\n", cmd);
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
 	return -ENOIOCTLCMD;
 }
 }
+mutex_unlock(&easycap_dongle[kd].mutex_video);
+JOM(4, "unlocked easycap_dongle[%i].mutex_video\n", kd);
 return 0;
 }
-
-long easycap_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-	struct inode *inode = file->f_dentry->d_inode;
-	long ret;
-
-	lock_kernel();
-	ret = easycap_ioctl_bkl(inode, file, cmd, arg);
-	unlock_kernel();
-
-	return ret;
-}
 /*****************************************************************************/
-static int easysnd_ioctl_bkl(struct inode *inode, struct file *file,
-			     unsigned int cmd, unsigned long arg)
+/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
+#if ((defined(EASYCAP_IS_VIDEODEV_CLIENT)) || \
+	(defined(EASYCAP_NEEDS_UNLOCKED_IOCTL)))
+long
+easysnd_ioctl_noinode(struct file *file, unsigned int cmd, unsigned long arg) {
+	return (long)easysnd_ioctl((struct inode *)NULL, file, cmd, arg);
+}
+#endif /*EASYCAP_IS_VIDEODEV_CLIENT||EASYCAP_NEEDS_UNLOCKED_IOCTL*/
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+/*---------------------------------------------------------------------------*/
+int
+easysnd_ioctl(struct inode *inode, struct file *file,
+					unsigned int cmd, unsigned long arg)
 {
 struct easycap *peasycap;
 struct usb_device *p;
+int kd;
 
 if (NULL == file) {
 	SAY("ERROR:  file is NULL\n");
@@ -2345,6 +2533,48 @@ if (NULL == p) {
 	SAM("ERROR: peasycap->pusb_device is NULL\n");
 	return -EFAULT;
 }
+kd = isdongle(peasycap);
+if (0 <= kd && DONGLE_MANY > kd) {
+	if (mutex_lock_interruptible(&easycap_dongle[kd].mutex_audio)) {
+		SAY("ERROR: cannot lock easycap_dongle[%i].mutex_audio\n", kd);
+		return -ERESTARTSYS;
+	}
+	JOM(4, "locked easycap_dongle[%i].mutex_audio\n", kd);
+/*---------------------------------------------------------------------------*/
+/*
+ *  MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap,
+ *  IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
+ *  IF NECESSARY, BAIL OUT.
+*/
+/*---------------------------------------------------------------------------*/
+	if (kd != isdongle(peasycap))
+		return -ERESTARTSYS;
+	if (NULL == file) {
+		SAY("ERROR:  file is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
+		return -ERESTARTSYS;
+	}
+	peasycap = file->private_data;
+	if (NULL == peasycap) {
+		SAY("ERROR:  peasycap is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
+		return -ERESTARTSYS;
+	}
+	p = peasycap->pusb_device;
+	if (NULL == peasycap->pusb_device) {
+		SAM("ERROR: peasycap->pusb_device is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
+		return -ERESTARTSYS;
+	}
+} else {
+/*---------------------------------------------------------------------------*/
+/*
+ *  IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE
+ *  ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED.  BAIL OUT.
+*/
+/*---------------------------------------------------------------------------*/
+	return -ERESTARTSYS;
+}
 /*---------------------------------------------------------------------------*/
 switch (cmd) {
 case SNDCTL_DSP_GETCAPS: {
@@ -2363,8 +2593,10 @@ case SNDCTL_DSP_GETCAPS: {
 		caps = 0x04400000;
 #endif /*UPSAMPLE*/
 
-	if (0 != copy_to_user((void __user *)arg, &caps, sizeof(int)))
+	if (0 != copy_to_user((void __user *)arg, &caps, sizeof(int))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
+	}
 	break;
 }
 case SNDCTL_DSP_GETFMTS: {
@@ -2383,15 +2615,19 @@ case SNDCTL_DSP_GETFMTS: {
 		incoming = AFMT_S16_LE;
 #endif /*UPSAMPLE*/
 
-	if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int)))
+	if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
+	}
 	break;
 }
 case SNDCTL_DSP_SETFMT: {
 	int incoming, outgoing;
 	JOM(8, "SNDCTL_DSP_SETFMT\n");
-	if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int)))
+	if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
+	}
 	JOM(8, "........... %i=incoming\n", incoming);
 
 #if defined(UPSAMPLE)
@@ -2411,8 +2647,11 @@ case SNDCTL_DSP_SETFMT: {
 		JOM(8, "        cf. %i=AFMT_S16_LE\n", AFMT_S16_LE);
 		JOM(8, "        cf. %i=AFMT_U8\n", AFMT_U8);
 		if (0 != copy_to_user((void __user *)arg, &outgoing, \
-								sizeof(int)))
+								sizeof(int))) {
+			mutex_unlock(&easycap_dongle[kd].mutex_audio);
 			return -EFAULT;
+		}
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EINVAL ;
 	}
 	break;
@@ -2420,8 +2659,10 @@ case SNDCTL_DSP_SETFMT: {
 case SNDCTL_DSP_STEREO: {
 	int incoming;
 	JOM(8, "SNDCTL_DSP_STEREO\n");
-	if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int)))
+	if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
+	}
 	JOM(8, "........... %i=incoming\n", incoming);
 
 #if defined(UPSAMPLE)
@@ -2436,15 +2677,19 @@ case SNDCTL_DSP_STEREO: {
 		incoming = 1;
 #endif /*UPSAMPLE*/
 
-	if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int)))
+	if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
+	}
 	break;
 }
 case SNDCTL_DSP_SPEED: {
 	int incoming;
 	JOM(8, "SNDCTL_DSP_SPEED\n");
-	if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int)))
+	if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
+	}
 	JOM(8, "........... %i=incoming\n", incoming);
 
 #if defined(UPSAMPLE)
@@ -2459,27 +2704,35 @@ case SNDCTL_DSP_SPEED: {
 		incoming = 48000;
 #endif /*UPSAMPLE*/
 
-	if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int)))
+	if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
+	}
 	break;
 }
 case SNDCTL_DSP_GETTRIGGER: {
 	int incoming;
 	JOM(8, "SNDCTL_DSP_GETTRIGGER\n");
-	if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int)))
+	if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
+	}
 	JOM(8, "........... %i=incoming\n", incoming);
 
 	incoming = PCM_ENABLE_INPUT;
-	if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int)))
+	if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
+	}
 	break;
 }
 case SNDCTL_DSP_SETTRIGGER: {
 	int incoming;
 	JOM(8, "SNDCTL_DSP_SETTRIGGER\n");
-	if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int)))
+	if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
+	}
 	JOM(8, "........... %i=incoming\n", incoming);
 	JOM(8, "........... cf 0x%x=PCM_ENABLE_INPUT " \
 				"0x%x=PCM_ENABLE_OUTPUT\n", \
@@ -2493,12 +2746,16 @@ case SNDCTL_DSP_SETTRIGGER: {
 case SNDCTL_DSP_GETBLKSIZE: {
 	int incoming;
 	JOM(8, "SNDCTL_DSP_GETBLKSIZE\n");
-	if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int)))
+	if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
+	}
 	JOM(8, "........... %i=incoming\n", incoming);
 	incoming = peasycap->audio_bytes_per_fragment;
-	if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int)))
+	if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
+	}
 	break;
 }
 case SNDCTL_DSP_GETISPACE: {
@@ -2512,8 +2769,10 @@ case SNDCTL_DSP_GETISPACE: {
 	audio_buf_info.fragstotal = 0;
 
 	if (0 != copy_to_user((void __user *)arg, &audio_buf_info, \
-								sizeof(int)))
+								sizeof(int))) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
+	}
 	break;
 }
 case 0x00005401:
@@ -2523,25 +2782,16 @@ case 0x00005404:
 case 0x00005405:
 case 0x00005406: {
 	JOM(8, "SNDCTL_TMR_...: 0x%08X unsupported\n", cmd);
+	mutex_unlock(&easycap_dongle[kd].mutex_audio);
 	return -ENOIOCTLCMD;
 }
 default: {
 	JOM(8, "ERROR: unrecognized DSP IOCTL command: 0x%08X\n", cmd);
+	mutex_unlock(&easycap_dongle[kd].mutex_audio);
 	return -ENOIOCTLCMD;
 }
 }
+mutex_unlock(&easycap_dongle[kd].mutex_audio);
 return 0;
 }
-
-long easysnd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-	struct inode *inode = file->f_dentry->d_inode;
-	long ret;
-
-	lock_kernel();
-	ret = easysnd_ioctl_bkl(inode, file, cmd, arg);
-	unlock_kernel();
-
-	return ret;
-}
 /*****************************************************************************/
diff --git a/drivers/staging/easycap/easycap_low.c b/drivers/staging/easycap/easycap_low.c
index 7d778ba..b75db82 100644
--- a/drivers/staging/easycap/easycap_low.c
+++ b/drivers/staging/easycap/easycap_low.c
@@ -38,8 +38,8 @@
 */
 /****************************************************************************/
 
-#include "easycap_debug.h"
 #include "easycap.h"
+#include "easycap_debug.h"
 
 /*--------------------------------------------------------------------------*/
 const struct stk1160config { int reg; int set; } stk1160configPAL[256] = {
@@ -248,6 +248,9 @@ int
 confirm_resolution(struct usb_device *p)
 {
 __u8 get0, get1, get2, get3, get4, get5, get6, get7;
+
+if (NULL == p)
+	return -ENODEV;
 GET(p, 0x0110, &get0);
 GET(p, 0x0111, &get1);
 GET(p, 0x0112, &get2);
@@ -288,6 +291,8 @@ confirm_stream(struct usb_device *p)
 __u16 get2;
 __u8 igot;
 
+if (NULL == p)
+	return -ENODEV;
 GET(p, 0x0100, &igot);  get2 = 0x80 & igot;
 if (0x80 == get2)
 	JOT(8, "confirm_stream:  OK\n");
@@ -301,6 +306,8 @@ setup_stk(struct usb_device *p, bool ntsc)
 {
 int i0;
 
+if (NULL == p)
+	return -ENODEV;
 i0 = 0;
 if (true == ntsc) {
 	while (0xFFF != stk1160configNTSC[i0].reg) {
@@ -324,6 +331,8 @@ setup_saa(struct usb_device *p, bool ntsc)
 {
 int i0, ir;
 
+if (NULL == p)
+	return -ENODEV;
 i0 = 0;
 if (true == ntsc) {
 	while (0xFF != saa7113configNTSC[i0].reg) {
@@ -346,6 +355,8 @@ write_000(struct usb_device *p, __u16 set2, __u16 set0)
 {
 __u8 igot0, igot2;
 
+if (NULL == p)
+	return -ENODEV;
 GET(p, 0x0002, &igot2);
 GET(p, 0x0000, &igot0);
 SET(p, 0x0002, set2);
@@ -356,6 +367,8 @@ return 0;
 int
 write_saa(struct usb_device *p, __u16 reg0, __u16 set0)
 {
+if (NULL == p)
+	return -ENODEV;
 SET(p, 0x200, 0x00);
 SET(p, 0x204, reg0);
 SET(p, 0x205, set0);
@@ -379,6 +392,8 @@ __u8 igot;
 __u16 got502, got503;
 __u16 set502, set503;
 
+if (NULL == p)
+	return -ENODEV;
 SET(p, 0x0504, reg0);
 SET(p, 0x0500, 0x008B);
 
@@ -414,6 +429,8 @@ read_vt(struct usb_device *p, __u16 reg0)
 __u8 igot;
 __u16 got502, got503;
 
+if (NULL == p)
+	return -ENODEV;
 SET(p, 0x0504, reg0);
 SET(p, 0x0500, 0x008B);
 
@@ -433,6 +450,8 @@ return (got503 << 8) | got502;
 int
 write_300(struct usb_device *p)
 {
+if (NULL == p)
+	return -ENODEV;
 SET(p, 0x300, 0x0012);
 SET(p, 0x350, 0x002D);
 SET(p, 0x351, 0x0001);
@@ -453,6 +472,8 @@ check_saa(struct usb_device *p, bool ntsc)
 {
 int i0, ir, rc;
 
+if (NULL == p)
+	return -ENODEV;
 i0 = 0;
 rc = 0;
 if (true == ntsc) {
@@ -501,6 +522,8 @@ merit_saa(struct usb_device *p)
 {
 int rc;
 
+if (NULL == p)
+	return -ENODEV;
 rc = read_saa(p, 0x1F);
 if ((0 > rc) || (0x02 & rc))
 	return 1 ;
@@ -521,6 +544,8 @@ const int max = 5, marktime = PATIENCE/5;
  *              3     FOR NON-INTERLACED   60 Hz
 */
 /*--------------------------------------------------------------------------*/
+if (NULL == p)
+	return -ENODEV;
 j = 0;
 while (max > j) {
 	rc = read_saa(p, 0x1F);
@@ -565,6 +590,8 @@ check_stk(struct usb_device *p, bool ntsc)
 {
 int i0, ir;
 
+if (NULL == p)
+	return -ENODEV;
 i0 = 0;
 if (true == ntsc) {
 	while (0xFFF != stk1160configNTSC[i0].reg) {
@@ -637,6 +664,8 @@ read_saa(struct usb_device *p, __u16 reg0)
 {
 __u8 igot;
 
+if (NULL == p)
+	return -ENODEV;
 SET(p, 0x208, reg0);
 SET(p, 0x200, 0x20);
 if (0 != wait_i2c(p))
@@ -651,6 +680,8 @@ read_stk(struct usb_device *p, __u32 reg0)
 {
 __u8 igot;
 
+if (NULL == p)
+	return -ENODEV;
 igot = 0;
 GET(p, reg0, &igot);
 return igot;
@@ -679,6 +710,8 @@ select_input(struct usb_device *p, int input, int mode)
 {
 int ir;
 
+if (NULL == p)
+	return -ENODEV;
 stop_100(p);
 switch (input) {
 case 0:
@@ -781,6 +814,8 @@ set_resolution(struct usb_device *p, \
 {
 __u16 u0x0111, u0x0113, u0x0115, u0x0117;
 
+if (NULL == p)
+	return -ENODEV;
 u0x0111 = ((0xFF00 & set0) >> 8);
 u0x0113 = ((0xFF00 & set1) >> 8);
 u0x0115 = ((0xFF00 & set2) >> 8);
@@ -804,6 +839,8 @@ start_100(struct usb_device *p)
 __u16 get116, get117, get0;
 __u8 igot116, igot117, igot;
 
+if (NULL == p)
+	return -ENODEV;
 GET(p, 0x0116, &igot116);
 get116 = igot116;
 GET(p, 0x0117, &igot117);
@@ -827,6 +864,8 @@ stop_100(struct usb_device *p)
 __u16 get0;
 __u8 igot;
 
+if (NULL == p)
+	return -ENODEV;
 GET(p, 0x0100, &igot);
 get0 = igot;
 SET(p, 0x0100, (0x7F & get0));
@@ -846,6 +885,8 @@ __u8 igot;
 const int max = 2;
 int k;
 
+if (NULL == p)
+	return -ENODEV;
 for (k = 0;  k < max;  k++) {
 	GET(p, 0x0201, &igot);  get0 = igot;
 	switch (get0) {
@@ -872,8 +913,7 @@ __u16 igot;
 int rc0, rc1;
 
 if (!pusb_device)
-	return -EFAULT;
-
+	return -ENODEV;
 rc1 = 0;  igot = 0;
 rc0 = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), \
 		(__u8)0x01, \
@@ -936,8 +976,7 @@ regget(struct usb_device *pusb_device, __u16 index, void *pvoid)
 int ir;
 
 if (!pusb_device)
-	return -EFAULT;
-
+	return -ENODEV;
 ir = usb_control_msg(pusb_device, usb_rcvctrlpipe(pusb_device, 0), \
 		(__u8)0x00, \
 		(__u8)(USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE), \
@@ -952,6 +991,8 @@ return 0xFF & ir;
 int
 wakeup_device(struct usb_device *pusb_device)
 {
+if (!pusb_device)
+	return -ENODEV;
 return usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), \
 		(__u8)USB_REQ_SET_FEATURE, \
 		(__u8)(USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE), \
@@ -1056,6 +1097,8 @@ check_vt(struct usb_device *pusb_device)
 {
 int igot;
 
+if (!pusb_device)
+	return -ENODEV;
 igot = read_vt(pusb_device, 0x0002);
 if (0 > igot)
 	SAY("ERROR: failed to read VT1612A register 0x02\n");
@@ -1128,7 +1171,7 @@ int igot;
 __u8 u8;
 __u16 mute;
 
-if ((struct usb_device *)NULL == pusb_device)
+if (NULL == pusb_device)
 	return -ENODEV;
 if (0 > loud)
 	loud = 0;
diff --git a/drivers/staging/easycap/easycap_main.c b/drivers/staging/easycap/easycap_main.c
index ee5c8d9..67ae755 100644
--- a/drivers/staging/easycap/easycap_main.c
+++ b/drivers/staging/easycap/easycap_main.c
@@ -45,11 +45,14 @@ module_param(gain, int, S_IRUGO | S_IWUSR);
  *  IS CALLED SUCCESSIVELY FOR INTERFACES 0, 1, 2 AND THE POINTER peasycap
  *  ALLOCATED DURING THE PROBING OF INTERFACE 0 MUST BE REMEMBERED WHEN
  *  PROBING INTERFACES 1 AND 2.
+ *
+ *  IOCTL LOCKING IS DONE AT MODULE LEVEL, NOT DEVICE LEVEL.
 */
 /*---------------------------------------------------------------------------*/
 
-struct easycap *peasycap_dongle[DONGLE_MANY];
+struct easycap_dongle easycap_dongle[DONGLE_MANY];
 static int dongle_this;
+static int dongle_done;
 
 /*---------------------------------------------------------------------------*/
 /*
@@ -80,7 +83,11 @@ const struct file_operations easycap_fops = {
 	.owner		= THIS_MODULE,
 	.open		= easycap_open,
 	.release	= easycap_release,
-	.unlocked_ioctl	= easycap_ioctl,
+#if defined(EASYCAP_NEEDS_UNLOCKED_IOCTL)
+	.unlocked_ioctl	= easycap_ioctl_noinode,
+#else
+	.ioctl		= easycap_ioctl,
+#endif /*EASYCAP_NEEDS_UNLOCKED_IOCTL*/
 	.poll		= easycap_poll,
 	.mmap		= easycap_mmap,
 	.llseek		= no_llseek,
@@ -103,7 +110,11 @@ const struct v4l2_file_operations v4l2_fops = {
 	.owner		= THIS_MODULE,
 	.open		= easycap_open_noinode,
 	.release	= easycap_release_noinode,
-	.unlocked_ioctl	= easycap_ioctl,
+#if defined(EASYCAP_NEEDS_UNLOCKED_IOCTL)
+	.unlocked_ioctl	= easycap_ioctl_noinode,
+#else
+	.ioctl		= easycap_ioctl,
+#endif /*EASYCAP_NEEDS_UNLOCKED_IOCTL*/
 	.poll		= easycap_poll,
 	.mmap		= easycap_mmap,
 };
@@ -120,7 +131,11 @@ const struct file_operations easysnd_fops = {
 	.owner		= THIS_MODULE,
 	.open		= easysnd_open,
 	.release	= easysnd_release,
-	.unlocked_ioctl	= easysnd_ioctl,
+#if defined(EASYCAP_NEEDS_UNLOCKED_IOCTL)
+	.unlocked_ioctl	= easysnd_ioctl_noinode,
+#else
+	.ioctl		= easysnd_ioctl,
+#endif /*EASYCAP_NEEDS_UNLOCKED_IOCTL*/
 	.read		= easysnd_read,
 	.llseek		= no_llseek,
 };
@@ -139,10 +154,10 @@ int
 isdongle(struct easycap *peasycap)
 {
 int k;
-if ((struct easycap *)NULL == peasycap)
+if (NULL == peasycap)
 	return -2;
 for (k = 0; k < DONGLE_MANY; k++) {
-	if (peasycap_dongle[k] == peasycap) {
+	if (easycap_dongle[k].peasycap == peasycap) {
 		peasycap->isdongle = k;
 		return k;
 	}
@@ -196,11 +211,10 @@ if ((struct video_device *)NULL == pvideo_device) {
 peasycap = (struct easycap *)video_get_drvdata(pvideo_device);
 #endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
 /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
-if ((struct easycap *)NULL == peasycap) {
+if (NULL == peasycap) {
 	SAY("ERROR: peasycap is NULL\n");
 	return -EFAULT;
 }
-file->private_data = peasycap;
 if (NULL == peasycap->pusb_device) {
 	SAM("ERROR: peasycap->pusb_device is NULL\n");
 	return -EFAULT;
@@ -208,6 +222,7 @@ if (NULL == peasycap->pusb_device) {
 	JOM(16, "0x%08lX=peasycap->pusb_device\n", \
 					(long int)peasycap->pusb_device);
 }
+file->private_data = peasycap;
 rc = wakeup_device(peasycap->pusb_device);
 if (0 == rc)
 	JOM(8, "wakeup_device() OK\n");
@@ -243,7 +258,7 @@ struct easycap_standard const *peasycap_standard;
 int i, rc, input, rate;
 bool ntsc, other;
 
-if ((struct easycap *)NULL == peasycap) {
+if (NULL == peasycap) {
 	SAY("ERROR: peasycap is NULL\n");
 	return -EFAULT;
 }
@@ -251,7 +266,7 @@ input = peasycap->input;
 
 /*---------------------------------------------------------------------------*/
 /*
- *  IF THE SAA7113H HAS ALREADY ACQUIRED LOCK, USE ITS HARDWARE-DETECTED
+ *  IF THE SAA7113H HAS ALREADY ACQUIRED SYNC, USE ITS HARDWARE-DETECTED
  *  FIELD FREQUENCY TO DISTINGUISH NTSC FROM PAL.  THIS IS ESSENTIAL FOR
  *  gstreamer AND OTHER USERSPACE PROGRAMS WHICH MAY NOT ATTEMPT TO INITIATE
  *  A SWITCH BETWEEN PAL AND NTSC.
@@ -447,7 +462,7 @@ if (NULL == peasycap) {
 }
 JOM(8, "%i=input sought\n", input);
 
-if ((0 > input) &&(INPUT_MANY <= input))
+if (0 > input && INPUT_MANY <= input)
 	return -ENOENT;
 inputnow = peasycap->input;
 if (input == inputnow)
@@ -581,7 +596,7 @@ if (input == peasycap->inputset[input].input) {
 	return -ENOENT;
 }
 /*---------------------------------------------------------------------------*/
-if ((struct usb_device *)NULL == peasycap->pusb_device) {
+if (NULL == peasycap->pusb_device) {
 	SAM("ERROR: peasycap->pusb_device is NULL\n");
 	return -ENODEV;
 }
@@ -617,16 +632,16 @@ struct list_head *plist_head;
 int j, isbad, nospc, m, rc;
 int isbuf;
 
-if ((struct easycap *)NULL == peasycap) {
+if (NULL == peasycap) {
 	SAY("ERROR: peasycap is NULL\n");
 	return -EFAULT;
 }
 
-if ((struct list_head *)NULL == peasycap->purb_video_head) {
+if (NULL == peasycap->purb_video_head) {
 	SAY("ERROR: peasycap->urb_video_head uninitialized\n");
 	return -EFAULT;
 }
-if ((struct usb_device *)NULL == peasycap->pusb_device) {
+if (NULL == peasycap->pusb_device) {
 	SAY("ERROR: peasycap->pusb_device is NULL\n");
 	return -ENODEV;
 }
@@ -768,7 +783,7 @@ int m;
 struct list_head *plist_head;
 struct data_urb *pdata_urb;
 
-if ((struct easycap *)NULL == peasycap) {
+if (NULL == peasycap) {
 	SAY("ERROR: peasycap is NULL\n");
 	return -EFAULT;
 }
@@ -780,8 +795,8 @@ if (peasycap->video_isoc_streaming) {
 		list_for_each(plist_head, (peasycap->purb_video_head)) {
 			pdata_urb = list_entry(plist_head, struct data_urb, \
 								list_head);
-			if ((struct data_urb *)NULL != pdata_urb) {
-				if ((struct urb *)NULL != pdata_urb->purb) {
+			if (NULL != pdata_urb) {
+				if (NULL != pdata_urb->purb) {
 					usb_kill_urb(pdata_urb->purb);
 					m++;
 				}
@@ -864,15 +879,17 @@ return 0;
 /*****************************************************************************/
 /*--------------------------------------------------------------------------*/
 /*
- *  THIS FUNCTION IS CALLED FROM WITHIN easycap_usb_disconnect().
- *  BY THIS STAGE THE DEVICE HAS ALREADY BEEN PHYSICALLY UNPLUGGED.
- *  peasycap->pusb_device IS NO LONGER VALID AND SHOULD HAVE BEEN SET TO NULL.
+ *  THIS FUNCTION IS CALLED FROM WITHIN easycap_usb_disconnect() AND IS
+ *  PROTECTED BY SEMAPHORES SET AND CLEARED BY easycap_usb_disconnect().
+ *
+ *  BY THIS STAGE THE DEVICE HAS ALREADY BEEN PHYSICALLY UNPLUGGED, SO
+ *  peasycap->pusb_device IS NO LONGER VALID.
  */
 /*---------------------------------------------------------------------------*/
 void
 easycap_delete(struct kref *pkref)
 {
-int k, m, gone;
+int k, m, gone, kd;
 int allocation_video_urb, allocation_video_page, allocation_video_struct;
 int allocation_audio_urb, allocation_audio_page, allocation_audio_struct;
 int registered_video, registered_audio;
@@ -883,10 +900,11 @@ struct list_head *plist_head, *plist_next;
 JOT(4, "\n");
 
 peasycap = container_of(pkref, struct easycap, kref);
-if ((struct easycap *)NULL == peasycap) {
+if (NULL == peasycap) {
 	SAM("ERROR: peasycap is NULL: cannot perform deletions\n");
 	return;
 }
+kd = isdongle(peasycap);
 /*---------------------------------------------------------------------------*/
 /*
  *  FREE VIDEO.
@@ -925,8 +943,6 @@ if ((struct list_head *)NULL != peasycap->purb_video_head) {
 	JOM(4, "%i video data_urb structures freed\n", m);
 	JOM(4, "setting peasycap->purb_video_head=NULL\n");
 	peasycap->purb_video_head = (struct list_head *)NULL;
-	} else {
-JOM(4, "peasycap->purb_video_head is NULL\n");
 }
 /*---------------------------------------------------------------------------*/
 JOM(4, "freeing video isoc buffers.\n");
@@ -1051,15 +1067,16 @@ allocation_audio_urb    = peasycap->allocation_audio_urb;
 allocation_audio_page   = peasycap->allocation_audio_page;
 allocation_audio_struct = peasycap->allocation_audio_struct;
 registered_audio        = peasycap->registered_audio;
-m = 0;
-if ((struct easycap *)NULL != peasycap) {
-	kfree(peasycap);  peasycap = (struct easycap *)NULL;
+
+kfree(peasycap);
+if (0 <= kd && DONGLE_MANY > kd) {
+	easycap_dongle[kd].peasycap = (struct easycap *)NULL;
+	JOT(4, "   null-->easycap_dongle[%i].peasycap\n", kd);
 	allocation_video_struct -= sizeof(struct easycap);
-	m++;
+} else {
+	SAY("ERROR: cannot purge easycap_dongle[].peasycap");
 }
-JOT(4, "%i easycap structure freed\n", m);
 /*---------------------------------------------------------------------------*/
-
 SAY("%8i= video urbs     after all deletions\n", allocation_video_urb);
 SAY("%8i= video pages    after all deletions\n", allocation_video_page);
 SAY("%8i= video structs  after all deletions\n", allocation_video_struct);
@@ -1076,27 +1093,75 @@ return;
 unsigned int easycap_poll(struct file *file, poll_table *wait)
 {
 struct easycap *peasycap;
+int rc, kd;
 
 JOT(8, "\n");
 
 if (NULL == ((poll_table *)wait))
 	JOT(8, "WARNING:  poll table pointer is NULL ... continuing\n");
-if (NULL == ((struct file *)file)) {
+if ((struct file *)NULL == file) {
 	SAY("ERROR:  file pointer is NULL\n");
-	return -EFAULT;
+	return -ERESTARTSYS;
 }
 peasycap = file->private_data;
 if (NULL == peasycap) {
 	SAY("ERROR:  peasycap is NULL\n");
 	return -EFAULT;
 }
+if (NULL == peasycap->pusb_device) {
+	SAY("ERROR:  peasycap->pusb_device is NULL\n");
+	return -EFAULT;
+}
+/*---------------------------------------------------------------------------*/
+kd = isdongle(peasycap);
+if (0 <= kd && DONGLE_MANY > kd) {
+	if (mutex_lock_interruptible(&easycap_dongle[kd].mutex_video)) {
+		SAY("ERROR: cannot down easycap_dongle[%i].mutex_video\n", kd);
+		return -ERESTARTSYS;
+	}
+	JOM(4, "locked easycap_dongle[%i].mutex_video\n", kd);
+	/*-------------------------------------------------------------------*/
+	/*
+	 *  MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER
+	 *  peasycap, IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
+	 *  IF NECESSARY, BAIL OUT.
+	*/
+	/*-------------------------------------------------------------------*/
+	if (kd != isdongle(peasycap))
+		return -ERESTARTSYS;
+	if (NULL == file) {
+		SAY("ERROR:  file is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
+		return -ERESTARTSYS;
+	}
+	peasycap = file->private_data;
+	if (NULL == peasycap) {
+		SAY("ERROR:  peasycap is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
+		return -ERESTARTSYS;
+	}
+	if (NULL == peasycap->pusb_device) {
+		SAM("ERROR: peasycap->pusb_device is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
+	return -ERESTARTSYS;
+	}
+} else
+	/*-------------------------------------------------------------------*/
+	/*
+	 *  IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap
+	 *  BEFORE THE ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL
+	 *  HAVE FAILED.  BAIL OUT.
+	*/
+	/*-------------------------------------------------------------------*/
+	return -ERESTARTSYS;
+/*---------------------------------------------------------------------------*/
+rc = easycap_dqbuf(peasycap, 0);
 peasycap->polled = 1;
-
-if (0 == easycap_dqbuf(peasycap, 0))
+mutex_unlock(&easycap_dongle[kd].mutex_video);
+if (0 == rc)
 	return POLLIN | POLLRDNORM;
 else
 	return POLLERR;
-
 }
 /*****************************************************************************/
 /*---------------------------------------------------------------------------*/
@@ -1115,6 +1180,10 @@ if (NULL == peasycap) {
 	SAY("ERROR:  peasycap is NULL\n");
 	return -EFAULT;
 }
+if (NULL == peasycap->pusb_device) {
+	SAY("ERROR:  peasycap->pusb_device is NULL\n");
+	return -EFAULT;
+}
 ifield = 0;
 JOM(8, "%i=ifield\n", ifield);
 /*---------------------------------------------------------------------------*/
@@ -1122,9 +1191,9 @@ JOM(8, "%i=ifield\n", ifield);
  *  CHECK FOR LOST INPUT SIGNAL.
  *
  *  FOR THE FOUR-CVBS EasyCAP, THIS DOES NOT WORK AS EXPECTED.
- *  IF INPUT 0 IS PRESENT AND LOCKED, UNPLUGGING INPUT 4 DOES NOT RESULT IN
- *  SETTING BIT 0x40 ON REGISTER 0x1F, PRESUMABLY BECAUSE THERE IS FLYWHEELING
- *  ON INPUT 0.  THE UPSHOT IS:
+ *  IF INPUT 0 IS PRESENT AND SYNC ACQUIRED, UNPLUGGING INPUT 4 DOES NOT
+ *  RESULT IN SETTING BIT 0x40 ON REGISTER 0x1F, PRESUMABLY BECAUSE THERE
+ *  IS FLYWHEELING ON INPUT 0.  THE UPSHOT IS:
  *
  *    INPUT 0   PLUGGED, INPUT 4   PLUGGED => SCREEN 0 OK,   SCREEN 4 OK
  *    INPUT 0   PLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 OK,   SCREEN 4 BLACK
@@ -1342,7 +1411,7 @@ int rc, bytesperpixel, multiplier, much, more, over, rump, caches, input;
 __u8 mask, margin;
 bool odd, isuy, decimatepixel, offerfields, badinput;
 
-if ((struct easycap *)NULL == peasycap) {
+if (NULL == peasycap) {
 	SAY("ERROR: peasycap is NULL\n");
 	return -EFAULT;
 }
@@ -1447,13 +1516,11 @@ while (cz < wz) {
 					much) / 2;
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 			if (1 < bytesperpixel) {
-				if ((rad * \
-					2) < (much * \
-						bytesperpixel)) {
+				if (rad * 2 < much * bytesperpixel) {
 					/*
 					**   INJUDICIOUS ALTERATION OF THIS
-					**   BLOCK WILL CAUSE BREAKAGE.
-					**   BEWARE.
+					**   STATEMENT BLOCK WILL CAUSE
+					**   BREAKAGE.  BEWARE.
 					**/
 					rad2 = rad + bytesperpixel - 1;
 					much = ((((2 * \
@@ -1483,7 +1550,6 @@ while (cz < wz) {
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 			if (rump)
 				caches++;
-
 				if (true == badinput) {
 					JOM(8, "ERROR: 0x%02X=->field_buffer" \
 						"[%i][%i].input, " \
@@ -1492,7 +1558,6 @@ while (cz < wz) {
 						[kex][mex].input, kex, mex, \
 						(0x08|peasycap->input));
 				}
-
 			rc = redaub(peasycap, pad, pex, much, more, \
 							mask, margin, isuy);
 			if (0 > rc) {
@@ -1575,12 +1640,11 @@ while (cz < wz) {
 					much) / 4;
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 			if (1 < bytesperpixel) {
-				if ((rad * 4) < (much * \
-						bytesperpixel)) {
+				if (rad * 4 < much * bytesperpixel) {
 					/*
 					**   INJUDICIOUS ALTERATION OF THIS
-					**   BLOCK WILL CAUSE BREAKAGE.
-					**   BEWARE.
+					**   STATEMENT BLOCK WILL CAUSE
+					**   BREAKAGE.  BEWARE.
 					**/
 					rad2 = rad + bytesperpixel - 1;
 					much = ((((2 * rad2)/bytesperpixel)/2)\
@@ -1620,7 +1684,6 @@ while (cz < wz) {
 						[kex][mex].input, kex, mex, \
 						(0x08|peasycap->input));
 				}
-
 			rc = redaub(peasycap, pad, pex, much, more, \
 							mask, margin, isuy);
 			if (0 > rc) {
@@ -3297,8 +3360,8 @@ return;
  *                                  FIXME
  *
  *
- *  THIS FUNCTION ASSUMES THAT, ON EACH AND EVERY OCCASION THAT THE DEVICE IS
- *  PHYSICALLY PLUGGED IN, INTERFACE 0 IS PROBED FIRST.
+ *  THIS FUNCTION ASSUMES THAT, ON EACH AND EVERY OCCASION THAT THE EasyCAP
+ *  IS PHYSICALLY PLUGGED IN, INTERFACE 0 IS PROBED FIRST.
  *  IF THIS IS NOT TRUE, THERE IS THE POSSIBILITY OF AN Oops.
  *
  *  THIS HAS NEVER BEEN A PROBLEM IN PRACTICE, BUT SOMETHING SEEMS WRONG HERE.
@@ -3338,6 +3401,16 @@ __s32 value;
 struct easycap_format *peasycap_format;
 
 JOT(4, "\n");
+
+if (!dongle_done) {
+	dongle_done = 1;
+	for (k = 0; k < DONGLE_MANY; k++) {
+		easycap_dongle[k].peasycap = (struct easycap *)NULL;
+		mutex_init(&easycap_dongle[k].mutex_video);
+		mutex_init(&easycap_dongle[k].mutex_audio);
+	}
+}
+
 peasycap = (struct easycap *)NULL;
 
 if ((struct usb_interface *)NULL == pusb_interface) {
@@ -3481,12 +3554,20 @@ if (0 == bInterfaceNumber) {
 	init_waitqueue_head(&peasycap->wq_audio);
 
 	for (dongle_this = 0; dongle_this < DONGLE_MANY; dongle_this++) {
-		if ((struct easycap *)NULL == peasycap_dongle[dongle_this]) {
-				peasycap_dongle[dongle_this] = peasycap;
-				JOM(8, "intf[%i]: peasycap-->easycap" \
+		if (NULL == easycap_dongle[dongle_this].peasycap) {
+			if (0 == mutex_is_locked(&easycap_dongle\
+						[dongle_this].mutex_video)) {
+				if (0 == mutex_is_locked(&easycap_dongle\
+						[dongle_this].mutex_audio)) {
+					easycap_dongle\
+						[dongle_this].peasycap = \
+								peasycap;
+					JOM(8, "intf[%i]: peasycap-->easycap" \
 						"_dongle[%i].peasycap\n", \
 						bInterfaceNumber, dongle_this);
-			break;
+					break;
+				}
+			}
 		}
 	}
 	if (DONGLE_MANY <= dongle_this) {
@@ -3665,15 +3746,15 @@ if (0 == bInterfaceNumber) {
 	 *  FOR INTERFACES 1 AND 2 THE POINTER peasycap IS OBTAINED BY ASSUMING
 	 *  THAT dongle_this HAS NOT CHANGED SINCE INTERFACE 0 WAS PROBED.  IF
 	 *  THIS IS NOT THE CASE, FOR EXAMPLE WHEN TWO EASYCAPs ARE PLUGGED IN
-	 *  SIMULTANEOUSLY, THERE WILL BE VERY SERIOUS TROUBLE.
+	 *  SIMULTANEOUSLY, THERE WILL BE SERIOUS TROUBLE.
 	*/
 /*---------------------------------------------------------------------------*/
 	if ((0 > dongle_this) || (DONGLE_MANY <= dongle_this)) {
 		SAY("ERROR: bad dongle count\n");
 		return -EFAULT;
 	}
-	peasycap = peasycap_dongle[dongle_this];
-	JOT(8, "intf[%i]: peasycap_dongle[%i]-->peasycap\n", \
+	peasycap = easycap_dongle[dongle_this].peasycap;
+	JOT(8, "intf[%i]: easycap_dongle[%i].peasycap-->peasycap\n", \
 						bInterfaceNumber, dongle_this);
 
 	if ((struct easycap *)NULL == peasycap) {
@@ -3721,6 +3802,7 @@ if ((USB_CLASS_VIDEO == bInterfaceClass) || \
  */
 /*---------------------------------------------------------------------------*/
 isokalt = 0;
+
 for (i = 0; i < pusb_interface->num_altsetting; i++) {
 	pusb_host_interface = &(pusb_interface->altsetting[i]);
 	if ((struct usb_host_interface *)NULL == pusb_host_interface) {
@@ -4245,6 +4327,9 @@ case 0: {
 	}
 /*---------------------------------------------------------------------------*/
 /*
+ *                                   FIXME
+ *
+ *
  *  THIS IS BELIEVED TO BE HARMLESS, BUT MAY WELL BE UNNECESSARY OR WRONG:
 */
 /*---------------------------------------------------------------------------*/
@@ -4587,9 +4672,8 @@ return 0;
 /*****************************************************************************/
 /*---------------------------------------------------------------------------*/
 /*
- *  WHEN THIS FUNCTION IS CALLED THE DEVICE HAS ALREADY BEEN PHYSICALLY
- *  UNPLUGGED.
- *  HENCE peasycap->pusb_device IS NO LONGER VALID AND MUST BE SET TO NULL.
+ *  WHEN THIS FUNCTION IS CALLED THE EasyCAP HAS ALREADY BEEN PHYSICALLY
+ *  UNPLUGGED.  HENCE peasycap->pusb_device IS NO LONGER VALID.
  */
 /*---------------------------------------------------------------------------*/
 void
@@ -4602,7 +4686,7 @@ struct easycap *peasycap;
 
 struct list_head *plist_head;
 struct data_urb *pdata_urb;
-int minor, m;
+int minor, m, kd;
 
 JOT(4, "\n");
 
@@ -4691,49 +4775,75 @@ default:
 /*--------------------------------------------------------------------------*/
 /*
  *  DEREGISTER
+ *
+ *  THIS PROCEDURE WILL BLOCK UNTIL easycap_poll(), VIDEO IOCTL AND AUDIO
+ *  IOCTL ARE ALL UNLOCKED.  IF THIS IS NOT DONE AN Oops CAN OCCUR WHEN
+ *  AN EasyCAP IS UNPLUGGED WHILE THE URBS ARE RUNNING.  BEWARE.
  */
 /*--------------------------------------------------------------------------*/
+kd = isdongle(peasycap);
 switch (bInterfaceNumber) {
 case 0: {
+	if (0 <= kd && DONGLE_MANY > kd) {
+		wake_up_interruptible(&peasycap->wq_video);
+		JOM(4, "about to lock easycap_dongle[%i].mutex_video\n", kd);
+		if (mutex_lock_interruptible(&easycap_dongle[kd].\
+								mutex_video)) {
+			SAY("ERROR: cannot lock easycap_dongle[%i]." \
+							"mutex_video\n", kd);
+			return;
+		}
+		JOM(4, "locked easycap_dongle[%i].mutex_video\n", kd);
+	} else
+		SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd);
+/*---------------------------------------------------------------------------*/
 #if (!defined(EASYCAP_IS_VIDEODEV_CLIENT))
 	if ((struct easycap *)NULL == peasycap) {
 		SAM("ERROR: peasycap has become NULL\n");
 	} else {
-		lock_kernel();
 		usb_deregister_dev(pusb_interface, &easycap_class);
 		(peasycap->registered_video)--;
-
 		JOM(4, "intf[%i]: usb_deregister_dev()\n", bInterfaceNumber);
-		unlock_kernel();
 		SAM("easycap detached from minor #%d\n", minor);
 	}
 /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
 #else
-	if ((struct easycap *)NULL == peasycap)
-		SAM("ERROR: peasycap has become NULL\n");
-	else {
-		lock_kernel();
-		video_unregister_device(&peasycap->video_device);
-		(peasycap->registered_video)--;
-		unlock_kernel();
-		JOM(4, "unregistered with videodev: %i=minor\n", \
+	video_unregister_device(&peasycap->video_device);
+	JOM(4, "unregistered with videodev: %i=minor\n", \
 						peasycap->video_device.minor);
-	}
+	(peasycap->registered_video)--;
 /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
 #endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
+	if (0 <= kd && DONGLE_MANY > kd) {
+		mutex_unlock(&easycap_dongle[kd].mutex_video);
+		JOM(4, "unlocked easycap_dongle[%i].mutex_video\n", kd);
+	}
 	break;
 }
 case 2: {
-	lock_kernel();
+	if (0 <= kd && DONGLE_MANY > kd) {
+		wake_up_interruptible(&peasycap->wq_audio);
+		JOM(4, "about to lock easycap_dongle[%i].mutex_audio\n", kd);
+		if (mutex_lock_interruptible(&easycap_dongle[kd].\
+								mutex_audio)) {
+			SAY("ERROR: cannot lock easycap_dongle[%i]." \
+							"mutex_audio\n", kd);
+			return;
+		}
+		JOM(4, "locked easycap_dongle[%i].mutex_audio\n", kd);
+	} else
+		SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd);
 
 	usb_deregister_dev(pusb_interface, &easysnd_class);
-	if ((struct easycap *)NULL != peasycap)
-		(peasycap->registered_audio)--;
+	(peasycap->registered_audio)--;
 
 	JOM(4, "intf[%i]: usb_deregister_dev()\n", bInterfaceNumber);
-	unlock_kernel();
-
 	SAM("easysnd detached from minor #%d\n", minor);
+
+	if (0 <= kd && DONGLE_MANY > kd) {
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
+		JOM(4, "unlocked easycap_dongle[%i].mutex_audio\n", kd);
+	}
 	break;
 }
 default:
@@ -4744,24 +4854,41 @@ default:
  *  CALL easycap_delete() IF NO REMAINING REFERENCES TO peasycap
  */
 /*---------------------------------------------------------------------------*/
-if ((struct easycap *)NULL == peasycap) {
-	SAM("ERROR: peasycap has become NULL\n");
-	SAM("cannot call kref_put()\n");
-	SAM("ending unsuccessfully: may cause memory leak\n");
-	return;
-}
 if (!peasycap->kref.refcount.counter) {
-	SAM("ERROR: peasycap->kref.refcount.counter is zero " \
+	SAM("ERROR: peasycap->kref.refcount.counter is zero "
 						"so cannot call kref_put()\n");
 	SAM("ending unsuccessfully: may cause memory leak\n");
 	return;
 }
-JOM(4, "intf[%i]: kref_put() with %i=peasycap->kref.refcount.counter\n", \
+if (0 <= kd && DONGLE_MANY > kd) {
+	JOM(4, "about to lock easycap_dongle[%i].mutex_video\n", kd);
+	if (mutex_lock_interruptible(&easycap_dongle[kd].mutex_video)) {
+		SAY("ERROR: cannot down easycap_dongle[%i].mutex_video\n", kd);
+		SAM("ending unsuccessfully: may cause memory leak\n");
+	return;
+	}
+	JOM(4, "locked easycap_dongle[%i].mutex_video\n", kd);
+	JOM(4, "about to lock easycap_dongle[%i].mutex_audio\n", kd);
+	if (mutex_lock_interruptible(&easycap_dongle[kd].mutex_audio)) {
+		SAY("ERROR: cannot down easycap_dongle[%i].mutex_audio\n", kd);
+		mutex_unlock(&(easycap_dongle[kd].mutex_video));
+		JOM(4, "unlocked easycap_dongle[%i].mutex_video\n", kd);
+		SAM("ending unsuccessfully: may cause memory leak\n");
+		return;
+	}
+	JOM(4, "locked easycap_dongle[%i].mutex_audio\n", kd);
+}
+JOM(4, "intf[%i]: %i=peasycap->kref.refcount.counter\n", \
 		bInterfaceNumber, (int)peasycap->kref.refcount.counter);
 kref_put(&peasycap->kref, easycap_delete);
-JOM(4, "intf[%i]: kref_put() done.\n", bInterfaceNumber);
+JOT(4, "intf[%i]: kref_put() done.\n", bInterfaceNumber);
+if (0 <= kd && DONGLE_MANY > kd) {
+	mutex_unlock(&(easycap_dongle[kd].mutex_audio));
+	JOT(4, "unlocked easycap_dongle[%i].mutex_audio\n", kd);
+	mutex_unlock(&easycap_dongle[kd].mutex_video);
+	JOT(4, "unlocked easycap_dongle[%i].mutex_video\n", kd);
+}
 /*---------------------------------------------------------------------------*/
-
 JOM(4, "ends\n");
 return;
 }
diff --git a/drivers/staging/easycap/easycap_sound.c b/drivers/staging/easycap/easycap_sound.c
index 0b4b60b..dc97516 100644
--- a/drivers/staging/easycap/easycap_sound.c
+++ b/drivers/staging/easycap/easycap_sound.c
@@ -636,7 +636,7 @@ else
 
 if ((struct usb_device *)NULL == peasycap->pusb_device) {
 	SAM("ERROR: peasycap->pusb_device has become NULL\n");
-	return -EFAULT;
+	return -ENODEV;
 }
 /*---------------------------------------------------------------------------*/
 if ((struct usb_device *)NULL == peasycap->pusb_device) {
@@ -695,7 +695,7 @@ long long int above, below, mean;
 struct signed_div_result sdr;
 unsigned char *p0;
 long int kount1, more, rc, l0, lm;
-int fragment;
+int fragment, kd;
 struct easycap *peasycap;
 struct data_buffer *pdata_buffer;
 size_t szret;
@@ -713,20 +713,76 @@ size_t szret;
 
 JOT(8, "===== easysnd_read(): kount=%i, *poff=%i\n", (int)kount, (int)(*poff));
 
-peasycap = (struct easycap *)(file->private_data);
+if (NULL == file) {
+	SAY("ERROR:  file is NULL\n");
+	return -ERESTARTSYS;
+}
+peasycap = file->private_data;
 if (NULL == peasycap) {
 	SAY("ERROR in easysnd_read(): peasycap is NULL\n");
 	return -EFAULT;
 }
+if (NULL == peasycap->pusb_device) {
+	SAY("ERROR in easysnd_read(): peasycap->pusb_device is NULL\n");
+	return -EFAULT;
+}
+kd = isdongle(peasycap);
+if (0 <= kd && DONGLE_MANY > kd) {
+	if (mutex_lock_interruptible(&(easycap_dongle[kd].mutex_audio))) {
+		SAY("ERROR: cannot lock easycap_dongle[%i].mutex_audio\n", kd);
+		return -ERESTARTSYS;
+	}
+	JOM(4, "locked easycap_dongle[%i].mutex_audio\n", kd);
+/*---------------------------------------------------------------------------*/
+/*
+ *  MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap,
+ *  IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
+ *  IF NECESSARY, BAIL OUT.
+*/
+/*---------------------------------------------------------------------------*/
+	if (kd != isdongle(peasycap))
+		return -ERESTARTSYS;
+	if (NULL == file) {
+		SAY("ERROR:  file is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
+		return -ERESTARTSYS;
+	}
+	peasycap = file->private_data;
+	if (NULL == peasycap) {
+		SAY("ERROR:  peasycap is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
+		return -ERESTARTSYS;
+	}
+	if (NULL == peasycap->pusb_device) {
+		SAM("ERROR: peasycap->pusb_device is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
+		return -ERESTARTSYS;
+	}
+} else {
+/*---------------------------------------------------------------------------*/
+/*
+ *  IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE
+ *  ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED.  BAIL OUT.
+*/
+/*---------------------------------------------------------------------------*/
+	return -ERESTARTSYS;
+}
 /*---------------------------------------------------------------------------*/
+if (file->f_flags & O_NONBLOCK)
+	JOT(16, "NONBLOCK  kount=%i, *poff=%i\n", (int)kount, (int)(*poff));
+else
+	JOT(8, "BLOCKING  kount=%i, *poff=%i\n", (int)kount, (int)(*poff));
+
 if ((0 > peasycap->audio_read) || \
 		(peasycap->audio_buffer_page_many <= peasycap->audio_read)) {
 	SAM("ERROR: peasycap->audio_read out of range\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_audio);
 	return -EFAULT;
 }
 pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read];
 if ((struct data_buffer *)NULL == pdata_buffer) {
 	SAM("ERROR: pdata_buffer is NULL\n");
+	mutex_unlock(&easycap_dongle[kd].mutex_audio);
 	return -EFAULT;
 }
 JOM(12, "before wait, %i=frag read  %i=frag fill\n", \
@@ -738,6 +794,7 @@ while ((fragment == (peasycap->audio_fill / \
 		(0 == (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo)))) {
 	if (file->f_flags & O_NONBLOCK) {
 		JOM(16, "returning -EAGAIN as instructed\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EAGAIN;
 	}
 	rc = wait_event_interruptible(peasycap->wq_audio, \
@@ -747,21 +804,25 @@ while ((fragment == (peasycap->audio_fill / \
 		(0 < (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo))))));
 	if (0 != rc) {
 		SAM("aborted by signal\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -ERESTARTSYS;
 	}
 	if (peasycap->audio_eof) {
 		JOM(8, "returning 0 because  %i=audio_eof\n", \
 							peasycap->audio_eof);
 		kill_audio_urbs(peasycap);
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return 0;
 	}
 	if (peasycap->audio_idle) {
 		JOM(16, "returning 0 because  %i=audio_idle\n", \
 							peasycap->audio_idle);
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return 0;
 	}
 	if (!peasycap->audio_isoc_streaming) {
 		JOM(16, "returning 0 because audio urbs not streaming\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return 0;
 	}
 }
@@ -773,15 +834,18 @@ while (fragment == (peasycap->audio_read / \
 				peasycap->audio_pages_per_fragment)) {
 	if (NULL == pdata_buffer->pgo) {
 		SAM("ERROR: pdata_buffer->pgo is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
 	}
 	if (NULL == pdata_buffer->pto) {
 		SAM("ERROR: pdata_buffer->pto is NULL\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
 	}
 	kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo);
 	if (0 > kount1) {
 		SAM("easysnd_read: MISTAKE: kount1 is negative\n");
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -ERESTARTSYS;
 	}
 	if (!kount1) {
@@ -799,19 +863,23 @@ while (fragment == (peasycap->audio_read / \
 			(peasycap->audio_buffer_page_many <= \
 					peasycap->audio_read)) {
 			SAM("ERROR: peasycap->audio_read out of range\n");
+			mutex_unlock(&easycap_dongle[kd].mutex_audio);
 			return -EFAULT;
 		}
 		pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read];
 		if ((struct data_buffer *)NULL == pdata_buffer) {
 			SAM("ERROR: pdata_buffer is NULL\n");
+			mutex_unlock(&easycap_dongle[kd].mutex_audio);
 			return -EFAULT;
 		}
 		if (NULL == pdata_buffer->pgo) {
 			SAM("ERROR: pdata_buffer->pgo is NULL\n");
+			mutex_unlock(&easycap_dongle[kd].mutex_audio);
 			return -EFAULT;
 		}
 		if (NULL == pdata_buffer->pto) {
 			SAM("ERROR: pdata_buffer->pto is NULL\n");
+			mutex_unlock(&easycap_dongle[kd].mutex_audio);
 			return -EFAULT;
 		}
 		kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo);
@@ -840,6 +908,7 @@ while (fragment == (peasycap->audio_read / \
 	rc = copy_to_user(puserspacebuffer, pdata_buffer->pto, more);
 	if (0 != rc) {
 		SAM("ERROR: copy_to_user() returned %li\n", rc);
+		mutex_unlock(&easycap_dongle[kd].mutex_audio);
 		return -EFAULT;
 	}
 	*poff += (loff_t)more;
@@ -902,6 +971,7 @@ JOM(8, "audio streaming at %lli bytes/second\n", sdr.quotient);
 peasycap->dnbydt = sdr.quotient;
 
 JOM(8, "returning %li\n", (long int)szret);
+mutex_unlock(&easycap_dongle[kd].mutex_audio);
 return szret;
 }
 /*****************************************************************************/
-- 
1.5.6.5






More information about the devel mailing list