summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@skynet.be>2009-05-19 15:08:03 +0200
committerLaurent Pinchart <laurent.pinchart@skynet.be>2009-05-19 15:08:03 +0200
commit39d53fcd39df81baf92cc74a0de40d5a1cbbd9bf (patch)
tree0807ee11ccd8332a4bbf59db30ae43baf87a686c /linux/drivers/media/video
parentb872a134312bd4a2a97b537ab0b547a021d594df (diff)
downloadmediapointer-dvb-s2-39d53fcd39df81baf92cc74a0de40d5a1cbbd9bf.tar.gz
mediapointer-dvb-s2-39d53fcd39df81baf92cc74a0de40d5a1cbbd9bf.tar.bz2
uvcvideo: Start status polling on device open
From: Laurent Pinchart <laurent.pinchart@skynet.be> 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 Priority: normal Signed-off-by: Laurent Pinchart <laurent.pinchart@skynet.be>
Diffstat (limited to 'linux/drivers/media/video')
-rw-r--r--linux/drivers/media/video/uvc/uvc_driver.c1
-rw-r--r--linux/drivers/media/video/uvc/uvc_status.c21
-rw-r--r--linux/drivers/media/video/uvc/uvc_v4l2.c14
-rw-r--r--linux/drivers/media/video/uvc/uvcvideo.h3
4 files changed, 36 insertions, 3 deletions
diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c
index 1768389c4..13bd05576 100644
--- a/linux/drivers/media/video/uvc/uvc_driver.c
+++ b/linux/drivers/media/video/uvc/uvc_driver.c
@@ -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);
diff --git a/linux/drivers/media/video/uvc/uvc_status.c b/linux/drivers/media/video/uvc/uvc_status.c
index 1e1bda413..b05df63b4 100644
--- a/linux/drivers/media/video/uvc/uvc_status.c
+++ b/linux/drivers/media/video/uvc/uvc_status.c
@@ -206,7 +206,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)
@@ -217,15 +217,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);
diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c
index bc13dbf05..160c01314 100644
--- a/linux/drivers/media/video/uvc/uvc_v4l2.c
+++ b/linux/drivers/media/video/uvc/uvc_v4l2.c
@@ -443,6 +443,17 @@ 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) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+ usb_autopm_put_interface(video->dev->intf);
+#endif
+ atomic_dec(&video->dev->users);
+ kfree(handle);
+ goto done;
+ }
+ }
+
handle->device = video;
handle->state = UVC_HANDLE_PASSIVE;
file->private_data = handle;
@@ -477,6 +488,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);
+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
usb_autopm_put_interface(video->dev->intf);
#endif
diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h
index 53d5c9e0c..6b254fd39 100644
--- a/linux/drivers/media/video/uvc/uvcvideo.h
+++ b/linux/drivers/media/video/uvc/uvcvideo.h
@@ -635,6 +635,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;
@@ -771,6 +772,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);