public inbox for linux-media@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] uvcvideo: RESET_ON_TIMOUT Quirk [Against HG]
@ 2008-07-14  4:38 pat-lkml
  2008-07-14  4:41 ` pat-lkml
  0 siblings, 1 reply; 3+ messages in thread
From: pat-lkml @ 2008-07-14  4:38 UTC (permalink / raw)
  To: Linux and Kernel Video

[-- Attachment #1: Type: text/plain, Size: 635 bytes --]

This patch provides another quirk that causes the driver to reset the
device on -110.  This is a reworking of the patch provided by Michel
Stempin in January.  I've updated it so that, aside from the last_urb 
field, there are no differences in the code executed for non Quirk 
devices.  Also, I fixed the whitespace issues that had crept into my 
previous patch.  There are still some if/else nests that could be 
simplified, but I'll save that for another night.  I've tested it and it 
works for me with my Quickcam Orbit MP.  This version is likely safe for 
inclusion into the tree.

Signed-off-by: Pat Erley <pat-lkml@erley.org>

[-- Attachment #2: reset_on_timeout_quirk_hg.diff --]
[-- Type: text/plain, Size: 9338 bytes --]

diff -r 0ebffe1cc136 linux/drivers/media/video/uvc/uvc_driver.c
--- a/linux/drivers/media/video/uvc/uvc_driver.c	Sat Jul 12 16:50:43 2008 -0300
+++ b/linux/drivers/media/video/uvc/uvc_driver.c	Sun Jul 13 20:21:53 2008 -0400
@@ -142,6 +142,47 @@ static __u32 uvc_colorspace(const __u8 p
 	return 0;
 }
 
+/* 
+ * Reset and Re-Initialize video device
+ */
+int uvc_video_reinit(struct uvc_video_device *video)
+{
+	int ret;
+
+	if ((ret = uvc_usb_reset(video->dev)) < 0)
+		return ret;
+
+	if ((ret = uvc_set_video_ctrl(video, &video->streaming->ctrl, 0)) < 0) {
+		uvc_printk(KERN_DEBUG, "uvc_video_reinit: Unable to commit format "
+			"(%d).\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+int uvc_usb_reset(struct uvc_device *dev)
+{
+	int l, ret;
+
+	l = usb_lock_device_for_reset(dev->udev, dev->intf);
+
+	if (l >= 0) {
+		ret = usb_reset_device(dev->udev);
+		if (l)
+			usb_unlock_device(dev->udev);
+	}
+	else
+	ret = -EBUSY;
+
+	if (ret)
+		uvc_printk(KERN_DEBUG, "uvc_usb_reset: Unable to reset usb device"
+			"(%d).\n", ret);
+	else
+		dev->state &= ~UVC_DEV_IOERROR;
+
+	return ret;
+}
+
 /* Simplify a fraction using a simple continued fraction decomposition. The
  * idea here is to convert fractions such as 333333/10000000 to 1/30 using
  * 32 bit arithmetic only. The algorithm is not perfect and relies upon two
@@ -1444,9 +1485,23 @@ static int uvc_register_video(struct uvc
 	 * parameters.
 	 */
 	if ((ret = uvc_video_init(&dev->video)) < 0) {
-		uvc_printk(KERN_ERR, "Failed to initialize the device "
-			"(%d).\n", ret);
-		return ret;
+		if(dev->quirks & UVC_QUIRK_DEV_RESET_ON_TIMEOUT){
+			uvc_printk(KERN_ERR, "Failed to initialize the device, "
+				"(%d). trying to reset ...\n", ret);
+		
+			if ((ret = uvc_usb_reset(dev)))
+				return ret;
+		
+			if ((ret = uvc_video_init(&dev->video)) < 0) {
+				uvc_printk(KERN_ERR, "Failed to initialize the device "
+					"(%d).\n", ret);
+				return ret;
+			}
+		} else {
+			uvc_printk(KERN_ERR, "Failed to initialize the device "
+				"(%d).\n", ret);
+			return ret;
+		}
 	}
 
 	/* Register the device with V4L. */
@@ -1560,6 +1615,7 @@ static int uvc_probe(struct usb_interfac
 	dev->udev = usb_get_dev(udev);
 	dev->intf = usb_get_intf(intf);
 	dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
+	dev->last_urb = 0;
 	dev->quirks = id->driver_info | uvc_quirks_param;
 
 	if (udev->product != NULL)
@@ -1750,7 +1806,8 @@ static struct usb_device_id uvc_ids[] = 
 	  .idProduct		= 0x08c2,
 	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
 	  .bInterfaceSubClass	= 1,
-	  .bInterfaceProtocol	= 0 },
+	  .bInterfaceProtocol	= 0,
+	  .driver_info          = UVC_QUIRK_DEV_RESET_ON_TIMEOUT },
 	/* Logitech Quickcam Pro for Notebook */
 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
 				| USB_DEVICE_ID_MATCH_INT_INFO,
diff -r 0ebffe1cc136 linux/drivers/media/video/uvc/uvc_status.c
--- a/linux/drivers/media/video/uvc/uvc_status.c	Sat Jul 12 16:50:43 2008 -0300
+++ b/linux/drivers/media/video/uvc/uvc_status.c	Sun Jul 13 20:32:38 2008 -0400
@@ -161,9 +161,24 @@ static void uvc_status_complete(struct u
 	/* Resubmit the URB. */
 	urb->interval = dev->int_ep->desc.bInterval;
 	if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-		uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",
-			ret);
-	}
+		if(dev->quirks & UVC_QUIRK_DEV_RESET_ON_TIMEOUT)
+		{
+			uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",
+				ret);
+		} else {
+			if (ret == -EL2NSYNC) {
+				if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+					uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",
+						ret);
+				}
+			} else {
+				uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",
+					ret);
+
+			}
+		}
+	}
+	dev->last_urb = jiffies;
 }
 
 int uvc_status_init(struct uvc_device *dev)
@@ -171,6 +186,7 @@ int uvc_status_init(struct uvc_device *d
 	struct usb_host_endpoint *ep = dev->int_ep;
 	unsigned int pipe;
 	int interval;
+	int ret = 0;
 
 	if (ep == NULL)
 		return 0;
@@ -195,7 +211,21 @@ int uvc_status_init(struct uvc_device *d
 		dev->status, sizeof dev->status, uvc_status_complete,
 		dev, interval);
 
-	return usb_submit_urb(dev->int_urb, GFP_KERNEL);
+	if(dev->quirks & UVC_QUIRK_DEV_RESET_ON_TIMEOUT) {
+		if ((ret = usb_submit_urb(dev->int_urb, GFP_KERNEL)) < 0) {
+			if (ret == -EL2NSYNC) {
+				if ((ret = usb_submit_urb(dev->int_urb, GFP_KERNEL)) < 0) {
+					uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",
+						ret);
+				}
+			}
+		}
+
+	} else {
+		ret = usb_submit_urb(dev->int_urb, GFP_KERNEL);
+	}
+	dev->last_urb = jiffies;
+	return ret;
 }
 
 void uvc_status_cleanup(struct uvc_device *dev)
@@ -213,9 +243,25 @@ int uvc_status_suspend(struct uvc_device
 
 int uvc_status_resume(struct uvc_device *dev)
 {
+	int ret = 0;
 	if (dev->int_urb == NULL)
-		return 0;
-
-	return usb_submit_urb(dev->int_urb, GFP_NOIO);
-}
-
+		return ret;
+
+	if(dev->quirks & UVC_QUIRK_DEV_RESET_ON_TIMEOUT) {
+		if ((ret = usb_submit_urb(dev->int_urb, GFP_KERNEL)) < 0) {
+			if (ret == -EL2NSYNC) {
+				if ((ret = usb_submit_urb(dev->int_urb, GFP_KERNEL)) < 0) {
+					uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",
+						ret);
+				}
+			}
+		}
+
+	} else {
+		ret = usb_submit_urb(dev->int_urb, GFP_NOIO);
+	}
+
+	dev->last_urb = jiffies;
+	return ret;
+}
+
diff -r 0ebffe1cc136 linux/drivers/media/video/uvc/uvc_v4l2.c
--- a/linux/drivers/media/video/uvc/uvc_v4l2.c	Sat Jul 12 16:50:43 2008 -0300
+++ b/linux/drivers/media/video/uvc/uvc_v4l2.c	Sun Jul 13 20:33:54 2008 -0400
@@ -456,6 +456,13 @@ static int uvc_v4l2_release(struct inode
 		mutex_unlock(&video->queue.mutex);
 	}
 
+	if(video->dev->quirks & UVC_QUIRK_DEV_RESET_ON_TIMEOUT)
+	{
+		/* leave usb device in a clean state */
+			if (video->dev->state & UVC_DEV_IOERROR)
+				uvc_video_reinit(video);
+	}
+
 	/* Release the file handle. */
 	uvc_dismiss_privileges(handle);
 	kfree(handle);
diff -r 0ebffe1cc136 linux/drivers/media/video/uvc/uvc_video.c
--- a/linux/drivers/media/video/uvc/uvc_video.c	Sat Jul 12 16:50:43 2008 -0300
+++ b/linux/drivers/media/video/uvc/uvc_video.c	Sun Jul 13 20:44:41 2008 -0400
@@ -37,18 +37,36 @@ static int __uvc_query_ctrl(struct uvc_d
 	__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
 	unsigned int pipe;
 	int ret;
+	int delayed = 0;
 
 	pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
 			      : usb_sndctrlpipe(dev->udev, 0);
 	type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
 
+	if ((dev->quirks & UVC_QUIRK_DEV_RESET_ON_TIMEOUT) && (dev->last_urb)) {
+		while (time_before(jiffies,dev->last_urb + 2)) {
+			schedule();
+			delayed = 1;
+		}
+	}
+
 	ret = usb_control_msg(dev->udev, pipe, query, type, cs << 8,
 			unit << 8 | intfnum, data, size, timeout);
+	dev->last_urb = jiffies
 
 	if (ret != size) {
 		uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u "
 			"(unit %u) : %d (exp. %u).\n", query, cs, unit, ret,
 			size);
+		if (dev->quirks & UVC_QUIRK_DEV_RESET_ON_TIMEOUT){
+			if (delayed) {
+				uvc_printk(KERN_ERR,"usb_control_msg was delayed\n");
+			} else {
+				uvc_printk(KERN_ERR,"usb_control_msg was NOT delayed\n");
+			}
+			if (ret == -ETIMEDOUT) // reset the device in case of -110 error
+				dev->state |= UVC_DEV_IOERROR;
+		}
 		return -EIO;
 	}
 
@@ -548,9 +566,23 @@ static void uvc_video_complete(struct ur
 	video->decode(urb, video, buf);
 
 	if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-		uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
-			ret);
-	}
+		if (dev->quirks & UVC_QUIRK_DEV_RESET_ON_TIMEOUT) {
+			if (ret == -EL2NSYNC) {
+				if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+					uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
+						ret);
+				}
+			} else {
+				 uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
+				 	ret);
+
+			}
+		} else {
+			uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
+				ret);
+		}
+	}
+	video->dev->last_urb = jiffies;
 }
 
 /*
@@ -802,6 +834,7 @@ static int uvc_init_video(struct uvc_vid
 			return ret;
 		}
 	}
+	video->dev->last_urb = jiffies;
 
 	return 0;
 }
diff -r 0ebffe1cc136 linux/drivers/media/video/uvc/uvcvideo.h
--- a/linux/drivers/media/video/uvc/uvcvideo.h	Sat Jul 12 16:50:43 2008 -0300
+++ b/linux/drivers/media/video/uvc/uvcvideo.h	Sun Jul 13 20:46:40 2008 -0400
@@ -314,6 +314,7 @@ struct uvc_xu_control {
 #define UVC_QUIRK_BUILTIN_ISIGHT	0x00000008
 #define UVC_QUIRK_STREAM_NO_FID		0x00000010
 #define UVC_QUIRK_IGNORE_SELECTOR_UNIT	0x00000020
+#define UVC_QUIRK_DEV_RESET_ON_TIMEOUT	0x00000040
 
 /* Format flags */
 #define UVC_FMT_FLAG_COMPRESSED		0x00000001
@@ -610,6 +611,7 @@ struct uvc_video_device {
 
 enum uvc_device_state {
 	UVC_DEV_DISCONNECTED = 1,
+	UVC_DEV_IOERROR = 2,
 };
 
 struct uvc_device {
@@ -620,6 +622,7 @@ struct uvc_device {
 	char name[32];
 
 	enum uvc_device_state state;
+	unsigned long last_urb;
 	struct kref kref;
 	struct list_head list;
 
@@ -789,6 +792,9 @@ extern struct usb_host_endpoint *uvc_fin
 extern struct usb_host_endpoint *uvc_find_endpoint(
 		struct usb_host_interface *alts, __u8 epaddr);
 
+extern int uvc_video_reinit(struct uvc_video_device *video);
+extern int uvc_usb_reset(struct uvc_device *dev);
+
 /* Quirks support */
 void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
 		struct uvc_buffer *buf);

[-- Attachment #3: Type: text/plain, Size: 164 bytes --]

--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2008-07-14  4:44 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-07-14  4:38 [PATCH] uvcvideo: RESET_ON_TIMOUT Quirk [Against HG] pat-lkml
2008-07-14  4:41 ` pat-lkml
2008-07-14  4:44   ` pat-lkml

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox