V4L/DVB: hdpvr: fix disconnect sequence
authorHans Verkuil <hverkuil@xs4all.nl>
Sun, 2 May 2010 11:01:04 +0000 (08:01 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Wed, 19 May 2010 15:58:57 +0000 (12:58 -0300)
Disconnecting the HDPVR caused a kernel oops if lockdep was enabled.
In addition, if an app still had video0 open and attempted to call ioctl
when the device was already disconnected the system would crash.

Move the freeing and cleanup code to the release function: that is the
right place for it since you know when you get there that nobody is
using the device.

Also removed usb_set_intfdata: v4l2_device_register sets this already
to v4l2_dev.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/hdpvr/hdpvr-core.c
drivers/media/video/hdpvr/hdpvr-video.c
drivers/media/video/hdpvr/hdpvr.h

index 2fc9865fd4864da357767869fc0fbffd8569025f..830d47b05e1dba6f97520613e0299d217125cb67 100644 (file)
@@ -373,9 +373,6 @@ static int hdpvr_probe(struct usb_interface *interface,
        }
 #endif /* CONFIG_I2C */
 
-       /* save our data pointer in this interface device */
-       usb_set_intfdata(interface, dev);
-
        /* let the user know what node this device is now attached to */
        v4l2_info(&dev->v4l2_dev, "device now attached to %s\n",
                  video_device_node_name(dev->video_dev));
@@ -391,44 +388,24 @@ error:
 
 static void hdpvr_disconnect(struct usb_interface *interface)
 {
-       struct hdpvr_device *dev;
-
-       dev = usb_get_intfdata(interface);
-       usb_set_intfdata(interface, NULL);
+       struct hdpvr_device *dev = to_hdpvr_dev(usb_get_intfdata(interface));
 
+       v4l2_info(&dev->v4l2_dev, "device %s disconnected\n",
+                 video_device_node_name(dev->video_dev));
        /* prevent more I/O from starting and stop any ongoing */
        mutex_lock(&dev->io_mutex);
        dev->status = STATUS_DISCONNECTED;
-       v4l2_device_disconnect(&dev->v4l2_dev);
-       video_unregister_device(dev->video_dev);
        wake_up_interruptible(&dev->wait_data);
        wake_up_interruptible(&dev->wait_buffer);
        mutex_unlock(&dev->io_mutex);
+       v4l2_device_disconnect(&dev->v4l2_dev);
        msleep(100);
        flush_workqueue(dev->workqueue);
        mutex_lock(&dev->io_mutex);
        hdpvr_cancel_queue(dev);
-       destroy_workqueue(dev->workqueue);
        mutex_unlock(&dev->io_mutex);
-
-       /* deregister I2C adapter */
-#ifdef CONFIG_I2C
-       mutex_lock(&dev->i2c_mutex);
-       if (dev->i2c_adapter)
-               i2c_del_adapter(dev->i2c_adapter);
-       kfree(dev->i2c_adapter);
-       dev->i2c_adapter = NULL;
-       mutex_unlock(&dev->i2c_mutex);
-#endif /* CONFIG_I2C */
-
+       video_unregister_device(dev->video_dev);
        atomic_dec(&dev_nr);
-
-       v4l2_info(&dev->v4l2_dev, "device %s disconnected\n",
-                 video_device_node_name(dev->video_dev));
-
-       v4l2_device_unregister(&dev->v4l2_dev);
-       kfree(dev->usbc_buf);
-       kfree(dev);
 }
 
 
index 196f82de48f032c20b9141d320985fc3ac05181d..d2f0ee29737fd63ec5baa22d5791e56ae38d5a56 100644 (file)
@@ -1214,6 +1214,24 @@ static void hdpvr_device_release(struct video_device *vdev)
        struct hdpvr_device *dev = video_get_drvdata(vdev);
 
        hdpvr_delete(dev);
+       mutex_lock(&dev->io_mutex);
+       destroy_workqueue(dev->workqueue);
+       mutex_unlock(&dev->io_mutex);
+
+       v4l2_device_unregister(&dev->v4l2_dev);
+
+       /* deregister I2C adapter */
+#ifdef CONFIG_I2C
+       mutex_lock(&dev->i2c_mutex);
+       if (dev->i2c_adapter)
+               i2c_del_adapter(dev->i2c_adapter);
+       kfree(dev->i2c_adapter);
+       dev->i2c_adapter = NULL;
+       mutex_unlock(&dev->i2c_mutex);
+#endif /* CONFIG_I2C */
+
+       kfree(dev->usbc_buf);
+       kfree(dev);
 }
 
 static const struct video_device hdpvr_video_template = {
index 49ae25d83d10a32afe636929784f75c3efd4ccf9..b0f046df3cd8d4bbb5a052ffe1ecd46aff12f7b7 100644 (file)
@@ -111,6 +111,11 @@ struct hdpvr_device {
        u8                      *usbc_buf;
 };
 
+static inline struct hdpvr_device *to_hdpvr_dev(struct v4l2_device *v4l2_dev)
+{
+       return container_of(v4l2_dev, struct hdpvr_device, v4l2_dev);
+}
+
 
 /* buffer one bulk urb of data */
 struct hdpvr_buffer {