From 5f7fb877be14da92803f0b5b60955e071ebe2d58 Mon Sep 17 00:00:00 2001
From: Thierry <thierry.merle@free.fr>
Date: Mon, 4 Dec 2006 08:31:17 -0300
Subject: V4L/DVB (4928): Usbvision_v4l2 robustness on disconnect

This patch corrects 2 bugs (causes kernel oops) that occur when
unplugging the peripheral whereas nobody has opened it yet :
- do not call usbvision_stop_isoc if usbvision_init_isoc has not been called
- do not call wakeup_interruptible on waitqueues that did not have been
initialized with init_waitqueue_head

Signed-off-by: Thierry MERLE <thierry.merle@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
---
 drivers/media/video/usbvision/usbvision-core.c | 17 ++++++++---------
 drivers/media/video/usbvision/usbvision.h      |  7 ++++---
 2 files changed, 12 insertions(+), 12 deletions(-)

(limited to 'drivers/media/video/usbvision')

diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c
index 06e8e67..5a61ed7 100644
--- a/drivers/media/video/usbvision/usbvision-core.c
+++ b/drivers/media/video/usbvision/usbvision-core.c
@@ -2287,7 +2287,7 @@ static void usbvision_isocIrq(struct urb *urb, struct pt_regs *regs)
 
 	/* Manage streaming interruption */
 	if (usbvision->streaming == Stream_Interrupt) {
-		usbvision->streaming = Stream_Off;
+		usbvision->streaming = Stream_Idle;
 		if ((*f)) {
 			(*f)->grabstate = FrameState_Ready;
 			(*f)->scanstate = ScanState_Scanning;
@@ -3092,7 +3092,7 @@ static int usbvision_stream_interrupt(struct usb_usbvision *usbvision)
 
 	usbvision->streaming = Stream_Interrupt;
 	ret = wait_event_timeout(usbvision->wait_stream,
-				 (usbvision->streaming == Stream_Off),
+				 (usbvision->streaming == Stream_Idle),
 				 msecs_to_jiffies(USBVISION_NUMSBUF*USBVISION_URB_FRAMES));
 	return ret;
 }
@@ -3579,7 +3579,7 @@ static int usbvision_init_isoc(struct usb_usbvision *usbvision)
 		}
 	}
 
-	usbvision->streaming = Stream_On;
+	usbvision->streaming = Stream_Idle;
 	PDEBUG(DBG_ISOC, "%s: streaming=1 usbvision->video_endp=$%02x", __FUNCTION__, usbvision->video_endp);
 	return 0;
 }
@@ -3595,8 +3595,7 @@ static void usbvision_stop_isoc(struct usb_usbvision *usbvision)
 {
 	int bufIdx, errCode, regValue;
 
-	// FIXME : removed the streaming==Stream_Off. This field has not the same signification than before !
-	if (usbvision->dev == NULL)
+	if ((usbvision->streaming == Stream_Off) || (usbvision->dev == NULL))
 		return;
 
 	/* Unschedule all of the iso td's */
@@ -4292,7 +4291,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file,
 				return -EINVAL;
 
 			if (list_empty(&(usbvision->outqueue))) {
-				if (usbvision->streaming == Stream_Off)
+				if (usbvision->streaming == Stream_Idle)
 					return -EINVAL;
 				ret = wait_event_interruptible
 					(usbvision->wait_frame,
@@ -5665,6 +5664,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us
 	usbvision->isocPacketSize = 0;
 	usbvision->usb_bandwidth = 0;
 	usbvision->user = 0;
+	usbvision->streaming = Stream_Off;
 
 	usbvision_register_video(usbvision);
 	usbvision_configure_video(usbvision);
@@ -5713,13 +5713,12 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf)
 	usb_put_dev(usbvision->dev);
 	usbvision->dev = NULL;	// USB device is no more
 
-	wake_up_interruptible(&usbvision->wait_frame);
-	wake_up_interruptible(&usbvision->wait_stream);
-
 	up(&usbvision->lock);
 
 	if (usbvision->user) {
 		info("%s: In use, disconnect pending", __FUNCTION__);
+		wake_up_interruptible(&usbvision->wait_frame);
+		wake_up_interruptible(&usbvision->wait_stream);
 	}
 	else {
 		usbvision_release(usbvision);
diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h
index 870c0cc..ef83a5c 100644
--- a/drivers/media/video/usbvision/usbvision.h
+++ b/drivers/media/video/usbvision/usbvision.h
@@ -227,9 +227,10 @@ enum FrameState {
 
 /* stream states */
 enum StreamState {
-	Stream_Off,
-	Stream_Interrupt,
-	Stream_On,
+	Stream_Off,		/* Driver streaming is completely OFF */
+	Stream_Idle,		/* Driver streaming is ready to be put ON by the application */
+	Stream_Interrupt,	/* Driver streaming must be interrupted */
+	Stream_On,		/* Driver streaming is put ON by the application */
 };
 
 enum IsocState {
-- 
cgit v1.1