[media] gspca: Call sd_stop0 on disconnect
authorHans de Goede <hdegoede@redhat.com>
Mon, 7 May 2012 09:25:30 +0000 (06:25 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 14 May 2012 12:32:18 +0000 (09:32 -0300)
This is necessary to ensure that worker-threads accessing the device
are stopped before our disconnect handler returns.

This causes a problem with stream_off calling sd_stop0 a second time
when the device handle is closed. This is fixed by setting
gscpa_dev->streaming to 0 on disconnect.

Note that now stream_off will never be called on a disconnected device,
and the present check can thus be removed from stream_off.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/gspca/gspca.c

index 8b97f777ddf4ccfaeb4657df8ca9b98379046a99..b7cb9977f778e10ca576c2b81b22e0b32ab19562 100644 (file)
@@ -595,16 +595,12 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
 static void gspca_stream_off(struct gspca_dev *gspca_dev)
 {
        gspca_dev->streaming = 0;
-       if (gspca_dev->present) {
-               if (gspca_dev->sd_desc->stopN)
-                       gspca_dev->sd_desc->stopN(gspca_dev);
-               destroy_urbs(gspca_dev);
-               gspca_input_destroy_urb(gspca_dev);
-               gspca_set_alt0(gspca_dev);
-               gspca_input_create_urb(gspca_dev);
-       }
-
-       /* always call stop0 to free the subdriver's resources */
+       if (gspca_dev->sd_desc->stopN)
+               gspca_dev->sd_desc->stopN(gspca_dev);
+       destroy_urbs(gspca_dev);
+       gspca_input_destroy_urb(gspca_dev);
+       gspca_set_alt0(gspca_dev);
+       gspca_input_create_urb(gspca_dev);
        if (gspca_dev->sd_desc->stop0)
                gspca_dev->sd_desc->stop0(gspca_dev);
        PDEBUG(D_STREAM, "stream off OK");
@@ -2369,7 +2365,6 @@ void gspca_disconnect(struct usb_interface *intf)
        usb_set_intfdata(intf, NULL);
        gspca_dev->dev = NULL;
        gspca_dev->present = 0;
-       wake_up_interruptible(&gspca_dev->wq);
        destroy_urbs(gspca_dev);
 
 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
@@ -2380,6 +2375,11 @@ void gspca_disconnect(struct usb_interface *intf)
                input_unregister_device(input_dev);
        }
 #endif
+       /* Free subdriver's streaming resources / stop sd workqueue(s) */
+       if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming)
+               gspca_dev->sd_desc->stop0(gspca_dev);
+       gspca_dev->streaming = 0;
+       wake_up_interruptible(&gspca_dev->wq);
 
        v4l2_device_disconnect(&gspca_dev->v4l2_dev);
        video_unregister_device(&gspca_dev->vdev);