V4L/DVB (11837): uvcvideo: Start status polling on device open
authorLaurent Pinchart <laurent.pinchart@skynet.be>
Tue, 19 May 2009 13:08:03 +0000 (10:08 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Tue, 16 Jun 2009 21:21:10 +0000 (18:21 -0300)
Most UVC camera include an interrupt endpoint to report control value changes,
video streaming errors and camera button events. The USB controller
continuously polls the interrupt endpoint to retrieve such events. This
prevents the device from being auto-suspended, and thus consumes power.

Reporting video streaming errors don't make sense when the V4L2 device is
closed. Control value changes are probably useless as well if nobody listens to
the events, although caching will probably have to be completely disabled then.
No polling is thus be required when /dev/videoX is not opened.

To enable auto-suspend and save power do not poll the interrupt endpoint until
the device is open. We lose the ability to detect button events if no
application is using the camera.

http://bugzilla.kernel.org/show_bug.cgi?id=11948

Signed-off-by: Laurent Pinchart <laurent.pinchart@skynet.be>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/uvc/uvc_driver.c
drivers/media/video/uvc/uvc_status.c
drivers/media/video/uvc/uvc_v4l2.c
drivers/media/video/uvc/uvcvideo.h

index f4fced34cfd727f80f57a5e297cb3c269376f730..3287454bb368fa8fbfff48aa28b724b967d9bde5 100644 (file)
@@ -1594,6 +1594,7 @@ static int uvc_probe(struct usb_interface *intf,
        INIT_LIST_HEAD(&dev->entities);
        INIT_LIST_HEAD(&dev->streaming);
        kref_init(&dev->kref);
+       atomic_set(&dev->users, 0);
 
        dev->udev = usb_get_dev(udev);
        dev->intf = usb_get_intf(intf);
index 21d87124986b57336afc53debe4315ce17d618a2..f152a9903862abe4780eebe67ba282f2bf6d9eac 100644 (file)
@@ -194,7 +194,7 @@ int uvc_status_init(struct uvc_device *dev)
                dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete,
                dev, interval);
 
-       return usb_submit_urb(dev->int_urb, GFP_KERNEL);
+       return 0;
 }
 
 void uvc_status_cleanup(struct uvc_device *dev)
@@ -205,15 +205,30 @@ void uvc_status_cleanup(struct uvc_device *dev)
        uvc_input_cleanup(dev);
 }
 
-int uvc_status_suspend(struct uvc_device *dev)
+int uvc_status_start(struct uvc_device *dev)
+{
+       if (dev->int_urb == NULL)
+               return 0;
+
+       return usb_submit_urb(dev->int_urb, GFP_KERNEL);
+}
+
+void uvc_status_stop(struct uvc_device *dev)
 {
        usb_kill_urb(dev->int_urb);
+}
+
+int uvc_status_suspend(struct uvc_device *dev)
+{
+       if (atomic_read(&dev->users))
+               usb_kill_urb(dev->int_urb);
+
        return 0;
 }
 
 int uvc_status_resume(struct uvc_device *dev)
 {
-       if (dev->int_urb == NULL)
+       if (dev->int_urb == NULL || atomic_read(&dev->users) == 0)
                return 0;
 
        return usb_submit_urb(dev->int_urb, GFP_NOIO);
index ad7e64ff3add700d4f8bc6866bb8a53cf87113a1..507542dcbdaea0e22c073b5555bb426ecf620daa 100644 (file)
@@ -439,6 +439,15 @@ static int uvc_v4l2_open(struct file *file)
                goto done;
        }
 
+       if (atomic_inc_return(&video->dev->users) == 1) {
+               if ((ret = uvc_status_start(video->dev)) < 0) {
+                       usb_autopm_put_interface(video->dev->intf);
+                       atomic_dec(&video->dev->users);
+                       kfree(handle);
+                       goto done;
+               }
+       }
+
        handle->device = video;
        handle->state = UVC_HANDLE_PASSIVE;
        file->private_data = handle;
@@ -473,6 +482,9 @@ static int uvc_v4l2_release(struct file *file)
        kfree(handle);
        file->private_data = NULL;
 
+       if (atomic_dec_return(&video->dev->users) == 0)
+               uvc_status_stop(video->dev);
+
        usb_autopm_put_interface(video->dev->intf);
        kref_put(&video->dev->kref, uvc_delete);
        return 0;
index e5014e668f9979307996d507d034cdc3fb793e94..daf0744473046c0a8d87127eb4bd1ca2b432d162 100644 (file)
@@ -634,6 +634,7 @@ struct uvc_device {
        enum uvc_device_state state;
        struct kref kref;
        struct list_head list;
+       atomic_t users;
 
        /* Video control interface */
        __u16 uvc_version;
@@ -770,6 +771,8 @@ extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
 /* Status */
 extern int uvc_status_init(struct uvc_device *dev);
 extern void uvc_status_cleanup(struct uvc_device *dev);
+extern int uvc_status_start(struct uvc_device *dev);
+extern void uvc_status_stop(struct uvc_device *dev);
 extern int uvc_status_suspend(struct uvc_device *dev);
 extern int uvc_status_resume(struct uvc_device *dev);