summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/media/video/v4l2-dev.c10
-rw-r--r--drivers/media/video/v4l2-device.c16
-rw-r--r--include/media/v4l2-device.h11
3 files changed, 37 insertions, 0 deletions
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 6b1ef85d8cc8..1898099ec65a 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -143,6 +143,7 @@ static inline void video_put(struct video_device *vdev)
static void v4l2_device_release(struct device *cd)
{
struct video_device *vdev = to_video_device(cd);
+ struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
mutex_lock(&videodev_lock);
if (video_device[vdev->minor] != vdev) {
@@ -169,6 +170,10 @@ static void v4l2_device_release(struct device *cd)
/* Release video_device and perform other
cleanups as needed. */
vdev->release(vdev);
+
+ /* Decrease v4l2_device refcount */
+ if (v4l2_dev)
+ v4l2_device_put(v4l2_dev);
}
static struct class video_class = {
@@ -676,6 +681,11 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
name_base, nr, video_device_node_name(vdev));
+
+ /* Increase v4l2_device refcount */
+ if (vdev->v4l2_dev)
+ v4l2_device_get(vdev->v4l2_dev);
+
#if defined(CONFIG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */
if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) {
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
index fd7a445ce34c..5aeaf876ba9b 100644
--- a/drivers/media/video/v4l2-device.c
+++ b/drivers/media/video/v4l2-device.c
@@ -37,6 +37,7 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
spin_lock_init(&v4l2_dev->lock);
mutex_init(&v4l2_dev->ioctl_lock);
v4l2_prio_init(&v4l2_dev->prio);
+ kref_init(&v4l2_dev->ref);
v4l2_dev->dev = dev;
if (dev == NULL) {
/* If dev == NULL, then name must be filled in by the caller */
@@ -54,6 +55,21 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
}
EXPORT_SYMBOL_GPL(v4l2_device_register);
+static void v4l2_device_release(struct kref *ref)
+{
+ struct v4l2_device *v4l2_dev =
+ container_of(ref, struct v4l2_device, ref);
+
+ if (v4l2_dev->release)
+ v4l2_dev->release(v4l2_dev);
+}
+
+int v4l2_device_put(struct v4l2_device *v4l2_dev)
+{
+ return kref_put(&v4l2_dev->ref, v4l2_device_release);
+}
+EXPORT_SYMBOL_GPL(v4l2_device_put);
+
int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename,
atomic_t *instance)
{
diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h
index 3b723283da26..bd102cf509ac 100644
--- a/include/media/v4l2-device.h
+++ b/include/media/v4l2-device.h
@@ -60,8 +60,19 @@ struct v4l2_device {
struct v4l2_prio_state prio;
/* BKL replacement mutex. Temporary solution only. */
struct mutex ioctl_lock;
+ /* Keep track of the references to this struct. */
+ struct kref ref;
+ /* Release function that is called when the ref count goes to 0. */
+ void (*release)(struct v4l2_device *v4l2_dev);
};
+static inline void v4l2_device_get(struct v4l2_device *v4l2_dev)
+{
+ kref_get(&v4l2_dev->ref);
+}
+
+int v4l2_device_put(struct v4l2_device *v4l2_dev);
+
/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev.
dev may be NULL in rare cases (ISA devices). In that case you
must fill in the v4l2_dev->name field before calling this function. */