[media] pwc: Allow dqbuf / read to complete while waiting for controls
authorHans de Goede <hdegoede@redhat.com>
Sun, 3 Jul 2011 14:50:51 +0000 (11:50 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Wed, 27 Jul 2011 20:53:45 +0000 (17:53 -0300)
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/pwc/pwc-if.c
drivers/media/video/pwc/pwc-v4l.c
drivers/media/video/pwc/pwc.h

index 1d5a6db..fdf113f 100644 (file)
@@ -1160,6 +1160,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
        pwc_construct(pdev); /* set min/max sizes correct */
 
        mutex_init(&pdev->modlock);
+       mutex_init(&pdev->udevlock);
        spin_lock_init(&pdev->queued_bufs_lock);
        INIT_LIST_HEAD(&pdev->queued_bufs);
 
@@ -1297,6 +1298,7 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
 {
        struct pwc_device *pdev  = usb_get_intfdata(intf);
 
+       mutex_lock(&pdev->udevlock);
        mutex_lock(&pdev->modlock);
 
        usb_set_intfdata(intf, NULL);
@@ -1306,6 +1308,7 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
        pdev->udev = NULL;
 
        mutex_unlock(&pdev->modlock);
+       mutex_unlock(&pdev->udevlock);
 
        pwc_remove_sysfs_files(pdev);
        video_unregister_device(&pdev->vdev);
index 986dd5d..5376572 100644 (file)
@@ -526,8 +526,24 @@ static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
                container_of(ctrl->handler, struct pwc_device, ctrl_handler);
        int ret = 0;
 
-       if (!pdev->udev)
-               return -ENODEV;
+       /*
+        * Sometimes it can take quite long for the pwc to complete usb control
+        * transfers, so release the modlock to give streaming by another
+        * process / thread the chance to continue with a dqbuf.
+        */
+       mutex_unlock(&pdev->modlock);
+
+       /*
+        * Take the udev-lock to protect against the disconnect handler
+        * completing and setting dev->udev to NULL underneath us. Other code
+        * does not need to do this since it is protected by the modlock.
+        */
+       mutex_lock(&pdev->udevlock);
+
+       if (!pdev->udev) {
+               ret = -ENODEV;
+               goto leave;
+       }
 
        switch (ctrl->id) {
        case V4L2_CID_AUTO_WHITE_BALANCE:
@@ -590,6 +606,9 @@ static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
        if (ret)
                PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret);
 
+leave:
+       mutex_unlock(&pdev->udevlock);
+       mutex_lock(&pdev->modlock);
        return ret;
 }
 
@@ -751,8 +770,14 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl)
                container_of(ctrl->handler, struct pwc_device, ctrl_handler);
        int ret = 0;
 
-       if (!pdev->udev)
-               return -ENODEV;
+       /* See the comments on locking in pwc_g_volatile_ctrl */
+       mutex_unlock(&pdev->modlock);
+       mutex_lock(&pdev->udevlock);
+
+       if (!pdev->udev) {
+               ret = -ENODEV;
+               goto leave;
+       }
 
        switch (ctrl->id) {
        case V4L2_CID_BRIGHTNESS:
@@ -841,6 +866,9 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl)
        if (ret)
                PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret);
 
+leave:
+       mutex_unlock(&pdev->udevlock);
+       mutex_lock(&pdev->modlock);
        return ret;
 }
 
index 601a549..e02dbf7 100644 (file)
@@ -200,6 +200,9 @@ struct pwc_device
 
        /* Pointer to our usb_device, may be NULL after unplug */
        struct usb_device *udev;
+       /* Protects the setting of udev to NULL by our disconnect handler */
+       struct mutex udevlock;
+
        /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */
        int type;
        int release;            /* release number */