[media] v4l2-device: add kref and a release function

The video_device struct has proper ref counting and its release function
will be called when the last user releases it. But no such support was
available for struct v4l2_device. This made it hard to determine when a
USB driver can release the device if it has multiple device nodes.

With one device node it is easy of course, since when the device node is
released, the whole device can be released.

This patch adds refcounting to v4l2_device. When registering device nodes
the v4l2_device refcount will be increased, when releasing device nodes
it will be decreased. The (optional) release function will be called when
the last device node was released.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Hans Verkuil 2011-03-12 06:37:19 -03:00 committed by Mauro Carvalho Chehab
parent a64bb4b711
commit bedf8bcf6b
3 changed files with 37 additions and 0 deletions

View File

@ -143,6 +143,7 @@ static inline void video_put(struct video_device *vdev)
static void v4l2_device_release(struct device *cd) static void v4l2_device_release(struct device *cd)
{ {
struct video_device *vdev = to_video_device(cd); struct video_device *vdev = to_video_device(cd);
struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
mutex_lock(&videodev_lock); mutex_lock(&videodev_lock);
if (video_device[vdev->minor] != vdev) { if (video_device[vdev->minor] != vdev) {
@ -169,6 +170,10 @@ static void v4l2_device_release(struct device *cd)
/* Release video_device and perform other /* Release video_device and perform other
cleanups as needed. */ cleanups as needed. */
vdev->release(vdev); vdev->release(vdev);
/* Decrease v4l2_device refcount */
if (v4l2_dev)
v4l2_device_put(v4l2_dev);
} }
static struct class video_class = { 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) if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__, printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
name_base, nr, video_device_node_name(vdev)); 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) #if defined(CONFIG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */ /* Part 5: Register the entity. */
if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) { if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) {

View File

@ -37,6 +37,7 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
spin_lock_init(&v4l2_dev->lock); spin_lock_init(&v4l2_dev->lock);
mutex_init(&v4l2_dev->ioctl_lock); mutex_init(&v4l2_dev->ioctl_lock);
v4l2_prio_init(&v4l2_dev->prio); v4l2_prio_init(&v4l2_dev->prio);
kref_init(&v4l2_dev->ref);
v4l2_dev->dev = dev; v4l2_dev->dev = dev;
if (dev == NULL) { if (dev == NULL) {
/* If dev == NULL, then name must be filled in by the caller */ /* 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); 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, int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename,
atomic_t *instance) atomic_t *instance)
{ {

View File

@ -60,8 +60,19 @@ struct v4l2_device {
struct v4l2_prio_state prio; struct v4l2_prio_state prio;
/* BKL replacement mutex. Temporary solution only. */ /* BKL replacement mutex. Temporary solution only. */
struct mutex ioctl_lock; 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. /* 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 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. */ must fill in the v4l2_dev->name field before calling this function. */