The patch number 13199 was added via Jean-Francois Moine <moin...@free.fr>
to http://linuxtv.org/hg/v4l-dvb master development tree.

Kernel patches in this development tree may be modified to be backward
compatible with older kernels. Compatibility modifications will be
removed before inclusion into the mainstream Kernel

If anyone has any objections, please let us know by sending a message to:
        Linux Media Mailing List <linux-me...@vger.kernel.org>

------

merge: v4l-dvb


---

 linux/drivers/media/video/uvc/uvc_ctrl.c   |    7 
 linux/drivers/media/video/uvc/uvc_driver.c |  172 +++++++++++----------
 linux/drivers/media/video/uvc/uvc_v4l2.c   |   42 +----
 linux/drivers/media/video/uvc/uvc_video.c  |   26 ++-
 linux/drivers/media/video/uvc/uvcvideo.h   |    9 -
 5 files changed, 135 insertions(+), 121 deletions(-)

diff -r 3f260d13f511 -r 9c72439a9625 linux/drivers/media/video/uvc/uvc_ctrl.c
--- a/linux/drivers/media/video/uvc/uvc_ctrl.c  Mon Oct 19 12:40:33 2009 +0200
+++ b/linux/drivers/media/video/uvc/uvc_ctrl.c  Tue Oct 20 20:34:11 2009 +0200
@@ -828,6 +828,13 @@
                ret = 0;
                goto out;
 
+       case V4L2_CTRL_TYPE_BUTTON:
+               v4l2_ctrl->minimum = 0;
+               v4l2_ctrl->maximum = 0;
+               v4l2_ctrl->step = 0;
+               ret = 0;
+               goto out;
+
        default:
                break;
        }
diff -r 3f260d13f511 -r 9c72439a9625 linux/drivers/media/video/uvc/uvc_driver.c
--- a/linux/drivers/media/video/uvc/uvc_driver.c        Mon Oct 19 12:40:33 
2009 +0200
+++ b/linux/drivers/media/video/uvc/uvc_driver.c        Tue Oct 20 20:34:11 
2009 +0200
@@ -46,6 +46,7 @@
 unsigned int uvc_no_drop_param;
 static unsigned int uvc_quirks_param;
 unsigned int uvc_trace_param;
+unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
 
 /* ------------------------------------------------------------------------
  * Video formats
@@ -426,7 +427,8 @@
        /* Parse the frame descriptors. Only uncompressed, MJPEG and frame
         * based formats have frame descriptors.
         */
-       while (buflen > 2 && buffer[2] == ftype) {
+       while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+              buffer[2] == ftype) {
                frame = &format->frame[format->nframes];
                if (ftype != UVC_VS_FRAME_FRAME_BASED)
                        n = buflen > 25 ? buffer[25] : 0;
@@ -503,12 +505,14 @@
                buffer += buffer[0];
        }
 
-       if (buflen > 2 && buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
+       if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+           buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
                buflen -= buffer[0];
                buffer += buffer[0];
        }
 
-       if (buflen > 2 && buffer[2] == UVC_VS_COLORFORMAT) {
+       if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+           buffer[2] == UVC_VS_COLORFORMAT) {
                if (buflen < 6) {
                        uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
                               "interface %d COLORFORMAT error\n",
@@ -749,6 +753,11 @@
                buffer += buffer[0];
        }
 
+       if (buflen)
+               uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+                       "%d has %u bytes of trailing descriptor garbage.\n",
+                       dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
+
        /* Parse the alternate settings to find the maximum bandwidth. */
        for (i = 0; i < intf->num_altsetting; ++i) {
                struct usb_host_endpoint *ep;
@@ -1531,22 +1540,92 @@
  */
 
 /*
+ * Delete the UVC device.
+ *
+ * Called by the kernel when the last reference to the uvc_device structure
+ * is released.
+ *
+ * As this function is called after or during disconnect(), all URBs have
+ * already been canceled by the USB core. There is no need to kill the
+ * interrupt URB manually.
+ */
+static void uvc_delete(struct uvc_device *dev)
+{
+       struct list_head *p, *n;
+
+       usb_put_intf(dev->intf);
+       usb_put_dev(dev->udev);
+
+       uvc_status_cleanup(dev);
+       uvc_ctrl_cleanup_device(dev);
+
+       list_for_each_safe(p, n, &dev->chains) {
+               struct uvc_video_chain *chain;
+               chain = list_entry(p, struct uvc_video_chain, list);
+               kfree(chain);
+       }
+
+       list_for_each_safe(p, n, &dev->entities) {
+               struct uvc_entity *entity;
+               entity = list_entry(p, struct uvc_entity, list);
+               kfree(entity);
+       }
+
+       list_for_each_safe(p, n, &dev->streams) {
+               struct uvc_streaming *streaming;
+               streaming = list_entry(p, struct uvc_streaming, list);
+               usb_driver_release_interface(&uvc_driver.driver,
+                       streaming->intf);
+               usb_put_intf(streaming->intf);
+               kfree(streaming->format);
+               kfree(streaming->header.bmaControls);
+               kfree(streaming);
+       }
+
+       kfree(dev);
+}
+
+static void uvc_release(struct video_device *vdev)
+{
+       struct uvc_streaming *stream = video_get_drvdata(vdev);
+       struct uvc_device *dev = stream->dev;
+
+       video_device_release(vdev);
+
+       /* Decrement the registered streams count and delete the device when it
+        * reaches zero.
+        */
+       if (atomic_dec_and_test(&dev->nstreams))
+               uvc_delete(dev);
+}
+
+/*
  * Unregister the video devices.
  */
 static void uvc_unregister_video(struct uvc_device *dev)
 {
        struct uvc_streaming *stream;
 
+       /* Unregistering all video devices might result in uvc_delete() being
+        * called from inside the loop if there's no open file handle. To avoid
+        * that, increment the stream count before iterating over the streams
+        * and decrement it when done.
+        */
+       atomic_inc(&dev->nstreams);
+
        list_for_each_entry(stream, &dev->streams, list) {
                if (stream->vdev == NULL)
                        continue;
 
-               if (stream->vdev->minor == -1)
-                       video_device_release(stream->vdev);
-               else
-                       video_unregister_device(stream->vdev);
+               video_unregister_device(stream->vdev);
                stream->vdev = NULL;
        }
+
+       /* Decrement the stream count and call uvc_delete explicitly if there
+        * are no stream left.
+        */
+       if (atomic_dec_and_test(&dev->nstreams))
+               uvc_delete(dev);
 }
 
 static int uvc_register_video(struct uvc_device *dev,
@@ -1580,7 +1659,7 @@
        vdev->parent = &dev->intf->dev;
        vdev->minor = -1;
        vdev->fops = &uvc_fops;
-       vdev->release = video_device_release;
+       vdev->release = uvc_release;
        strlcpy(vdev->name, dev->name, sizeof vdev->name);
 
        /* Set the driver data before calling video_register_device, otherwise
@@ -1598,6 +1677,7 @@
                return ret;
        }
 
+       atomic_inc(&dev->nstreams);
        return 0;
 }
 
@@ -1653,61 +1733,6 @@
  * USB probe, disconnect, suspend and resume
  */
 
-/*
- * Delete the UVC device.
- *
- * Called by the kernel when the last reference to the uvc_device structure
- * is released.
- *
- * Unregistering the video devices is done here because every opened instance
- * must be closed before the device can be unregistered. An alternative would
- * have been to use another reference count for uvc_v4l2_open/uvc_release, and
- * unregister the video devices on disconnect when that reference count drops
- * to zero.
- *
- * As this function is called after or during disconnect(), all URBs have
- * already been canceled by the USB core. There is no need to kill the
- * interrupt URB manually.
- */
-void uvc_delete(struct kref *kref)
-{
-       struct uvc_device *dev = container_of(kref, struct uvc_device, kref);
-       struct list_head *p, *n;
-
-       /* Unregister the video devices. */
-       uvc_unregister_video(dev);
-       usb_put_intf(dev->intf);
-       usb_put_dev(dev->udev);
-
-       uvc_status_cleanup(dev);
-       uvc_ctrl_cleanup_device(dev);
-
-       list_for_each_safe(p, n, &dev->chains) {
-               struct uvc_video_chain *chain;
-               chain = list_entry(p, struct uvc_video_chain, list);
-               kfree(chain);
-       }
-
-       list_for_each_safe(p, n, &dev->entities) {
-               struct uvc_entity *entity;
-               entity = list_entry(p, struct uvc_entity, list);
-               kfree(entity);
-       }
-
-       list_for_each_safe(p, n, &dev->streams) {
-               struct uvc_streaming *streaming;
-               streaming = list_entry(p, struct uvc_streaming, list);
-               usb_driver_release_interface(&uvc_driver.driver,
-                       streaming->intf);
-               usb_put_intf(streaming->intf);
-               kfree(streaming->format);
-               kfree(streaming->header.bmaControls);
-               kfree(streaming);
-       }
-
-       kfree(dev);
-}
-
 static int uvc_probe(struct usb_interface *intf,
                     const struct usb_device_id *id)
 {
@@ -1730,7 +1755,7 @@
        INIT_LIST_HEAD(&dev->entities);
        INIT_LIST_HEAD(&dev->chains);
        INIT_LIST_HEAD(&dev->streams);
-       kref_init(&dev->kref);
+       atomic_set(&dev->nstreams, 0);
        atomic_set(&dev->users, 0);
 
        dev->udev = usb_get_dev(udev);
@@ -1792,7 +1817,7 @@
        return 0;
 
 error:
-       kref_put(&dev->kref, uvc_delete);
+       uvc_unregister_video(dev);
        return -ENODEV;
 }
 
@@ -1809,21 +1834,9 @@
            UVC_SC_VIDEOSTREAMING)
                return;
 
-       /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide
-        * lock is needed to prevent uvc_disconnect from releasing its
-        * reference to the uvc_device instance after uvc_v4l2_open() received
-        * the pointer to the device (video_devdata) but before it got the
-        * chance to increase the reference count (kref_get).
-        *
-        * Note that the reference can't be released with the lock held,
-        * otherwise a AB-BA deadlock can occur with videodev_lock that
-        * videodev acquires in videodev_open() and video_unregister_device().
-        */
-       mutex_lock(&uvc_driver.open_mutex);
        dev->state |= UVC_DEV_DISCONNECTED;
-       mutex_unlock(&uvc_driver.open_mutex);
 
-       kref_put(&dev->kref, uvc_delete);
+       uvc_unregister_video(dev);
 }
 
 static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
@@ -2165,7 +2178,6 @@
 
        INIT_LIST_HEAD(&uvc_driver.devices);
        INIT_LIST_HEAD(&uvc_driver.controls);
-       mutex_init(&uvc_driver.open_mutex);
        mutex_init(&uvc_driver.ctrl_mutex);
 
        uvc_ctrl_init();
@@ -2190,6 +2202,8 @@
 MODULE_PARM_DESC(quirks, "Forced device quirks");
 module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(trace, "Trace level bitmask");
+module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
diff -r 3f260d13f511 -r 9c72439a9625 linux/drivers/media/video/uvc/uvc_v4l2.c
--- a/linux/drivers/media/video/uvc/uvc_v4l2.c  Mon Oct 19 12:40:33 2009 +0200
+++ b/linux/drivers/media/video/uvc/uvc_v4l2.c  Tue Oct 20 20:34:11 2009 +0200
@@ -364,37 +364,30 @@
  * unprivileged state. Only a single instance can be in a privileged state at
  * a given time. Trying to perform an operation that requires privileges will
  * automatically acquire the required privileges if possible, or return -EBUSY
- * otherwise. Privileges are dismissed when closing the instance.
+ * otherwise. Privileges are dismissed when closing the instance or when
+ * freeing the video buffers using VIDIOC_REQBUFS.
  *
  * Operations that require privileges are:
  *
  * - VIDIOC_S_INPUT
  * - VIDIOC_S_PARM
  * - VIDIOC_S_FMT
- * - VIDIOC_TRY_FMT
  * - VIDIOC_REQBUFS
  */
 static int uvc_acquire_privileges(struct uvc_fh *handle)
 {
-       int ret = 0;
-
        /* Always succeed if the handle is already privileged. */
        if (handle->state == UVC_HANDLE_ACTIVE)
                return 0;
 
        /* Check if the device already has a privileged handle. */
-       mutex_lock(&uvc_driver.open_mutex);
        if (atomic_inc_return(&handle->stream->active) != 1) {
                atomic_dec(&handle->stream->active);
-               ret = -EBUSY;
-               goto done;
+               return -EBUSY;
        }
 
        handle->state = UVC_HANDLE_ACTIVE;
-
-done:
-       mutex_unlock(&uvc_driver.open_mutex);
-       return ret;
+       return 0;
 }
 
 static void uvc_dismiss_privileges(struct uvc_fh *handle)
@@ -421,18 +414,15 @@
        int ret = 0;
 
        uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
-       mutex_lock(&uvc_driver.open_mutex);
        stream = video_drvdata(file);
 
-       if (stream->dev->state & UVC_DEV_DISCONNECTED) {
-               ret = -ENODEV;
-               goto done;
-       }
+       if (stream->dev->state & UVC_DEV_DISCONNECTED)
+               return -ENODEV;
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
        ret = usb_autopm_get_interface(stream->dev->intf);
        if (ret < 0)
-               goto done;
+               return ret;
 #endif
 
        /* Create the device handle. */
@@ -441,8 +431,7 @@
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
                usb_autopm_put_interface(stream->dev->intf);
 #endif
-               ret = -ENOMEM;
-               goto done;
+               return -ENOMEM;
        }
 
        if (atomic_inc_return(&stream->dev->users) == 1) {
@@ -453,7 +442,7 @@
 #endif
                        atomic_dec(&stream->dev->users);
                        kfree(handle);
-                       goto done;
+                       return ret;
                }
        }
 
@@ -462,11 +451,7 @@
        handle->state = UVC_HANDLE_PASSIVE;
        file->private_data = handle;
 
-       kref_get(&stream->dev->kref);
-
-done:
-       mutex_unlock(&uvc_driver.open_mutex);
-       return ret;
+       return 0;
 }
 
 static int uvc_v4l2_release(struct file *file)
@@ -498,7 +483,6 @@
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
        usb_autopm_put_interface(stream->dev->intf);
 #endif
-       kref_put(&stream->dev->kref, uvc_delete);
        return 0;
 }
 
@@ -739,9 +723,6 @@
        {
                struct uvc_streaming_control probe;
 
-               if ((ret = uvc_acquire_privileges(handle)) < 0)
-                       return ret;
-
                return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL);
        }
 
@@ -896,6 +877,9 @@
                if (ret < 0)
                        return ret;
 
+               if (ret == 0)
+                       uvc_dismiss_privileges(handle);
+
                rb->count = ret;
                ret = 0;
                break;
diff -r 3f260d13f511 -r 9c72439a9625 linux/drivers/media/video/uvc/uvc_video.c
--- a/linux/drivers/media/video/uvc/uvc_video.c Mon Oct 19 12:40:33 2009 +0200
+++ b/linux/drivers/media/video/uvc/uvc_video.c Tue Oct 20 20:34:11 2009 +0200
@@ -134,7 +134,7 @@
 
        ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum,
                probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
-               size, UVC_CTRL_STREAMING_TIMEOUT);
+               size, uvc_timeout_param);
 
        if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) {
                /* Some cameras, mostly based on Bison Electronics chipsets,
@@ -238,7 +238,7 @@
 
        ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum,
                probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
-               size, UVC_CTRL_STREAMING_TIMEOUT);
+               size, uvc_timeout_param);
        if (ret != size) {
                uvc_printk(KERN_ERR, "Failed to set UVC %s control : "
                        "%d (exp. %u).\n", probe ? "probe" : "commit",
@@ -773,8 +773,9 @@
        /* Retry allocations until one succeed. */
        for (; npackets > 1; npackets /= 2) {
                for (i = 0; i < UVC_URBS; ++i) {
+                       stream->urb_size = psize * npackets;
                        stream->urb_buffer[i] = usb_buffer_alloc(
-                               stream->dev->udev, psize * npackets,
+                               stream->dev->udev, stream->urb_size,
                                gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]);
                        if (!stream->urb_buffer[i]) {
                                uvc_free_urb_buffers(stream);
@@ -783,11 +784,15 @@
                }
 
                if (i == UVC_URBS) {
-                       stream->urb_size = psize * npackets;
+                       uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers "
+                               "of %ux%u bytes each.\n", UVC_URBS, npackets,
+                               psize);
                        return npackets;
                }
        }
 
+       uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes "
+               "per packet).\n", psize);
        return 0;
 }
 
@@ -938,10 +943,12 @@
                bandwidth = stream->ctrl.dwMaxPayloadTransferSize;
 
                if (bandwidth == 0) {
-                       uvc_printk(KERN_WARNING, "device %s requested null "
-                               "bandwidth, defaulting to lowest.\n",
-                               stream->dev->name);
+                       uvc_trace(UVC_TRACE_VIDEO, "Device requested null "
+                               "bandwidth, defaulting to lowest.\n");
                        bandwidth = 1;
+               } else {
+                       uvc_trace(UVC_TRACE_VIDEO, "Device requested %u "
+                               "B/frame bandwidth.\n", bandwidth);
                }
 
                for (i = 0; i < intf->num_altsetting; ++i) {
@@ -958,8 +965,11 @@
                                break;
                }
 
-               if (i >= intf->num_altsetting)
+               if (i >= intf->num_altsetting) {
+                       uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting "
+                               "for requested bandwidth.\n");
                        return -EIO;
+               }
 
                ret = usb_set_interface(stream->dev->udev, intfnum, i);
                if (ret < 0)
diff -r 3f260d13f511 -r 9c72439a9625 linux/drivers/media/video/uvc/uvcvideo.h
--- a/linux/drivers/media/video/uvc/uvcvideo.h  Mon Oct 19 12:40:33 2009 +0200
+++ b/linux/drivers/media/video/uvc/uvcvideo.h  Tue Oct 20 20:34:11 2009 +0200
@@ -149,7 +149,7 @@
 #define UVC_MAX_STATUS_SIZE    16
 
 #define UVC_CTRL_CONTROL_TIMEOUT       300
-#define UVC_CTRL_STREAMING_TIMEOUT     1000
+#define UVC_CTRL_STREAMING_TIMEOUT     3000
 
 /* Devices quirks */
 #define UVC_QUIRK_STATUS_INTERVAL      0x00000001
@@ -476,7 +476,6 @@
        char name[32];
 
        enum uvc_device_state state;
-       struct kref kref;
        struct list_head list;
        atomic_t users;
 
@@ -489,6 +488,7 @@
 
        /* Video Streaming interfaces */
        struct list_head streams;
+       atomic_t nstreams;
 
        /* Status Interrupt Endpoint */
        struct usb_host_endpoint *int_ep;
@@ -512,8 +512,6 @@
 struct uvc_driver {
        struct usb_driver driver;
 
-       struct mutex open_mutex;        /* protects from open/disconnect race */
-
        struct list_head devices;       /* struct uvc_device list */
        struct list_head controls;      /* struct uvc_control_info list */
        struct mutex ctrl_mutex;        /* protects controls and devices
@@ -534,12 +532,14 @@
 #define UVC_TRACE_FRAME                (1 << 7)
 #define UVC_TRACE_SUSPEND      (1 << 8)
 #define UVC_TRACE_STATUS       (1 << 9)
+#define UVC_TRACE_VIDEO                (1 << 10)
 
 #define UVC_WARN_MINMAX                0
 #define UVC_WARN_PROBE_DEF     1
 
 extern unsigned int uvc_no_drop_param;
 extern unsigned int uvc_trace_param;
+extern unsigned int uvc_timeout_param;
 
 #define uvc_trace(flag, msg...) \
        do { \
@@ -572,7 +572,6 @@
 
 /* Core driver */
 extern struct uvc_driver uvc_driver;
-extern void uvc_delete(struct kref *kref);
 
 /* Video buffers queue management. */
 extern void uvc_queue_init(struct uvc_video_queue *queue,


---

Patch is available at: 
http://linuxtv.org/hg/v4l-dvb/rev/9c72439a96253062919f63247198604860a65bcb

_______________________________________________
linuxtv-commits mailing list
linuxtv-commits@linuxtv.org
http://www.linuxtv.org/cgi-bin/mailman/listinfo/linuxtv-commits

Reply via email to