[media] v4l: subdev: Add device node support

Create a device node named subdevX for every registered subdev.

As the device node is registered before the subdev core::s_config
function is called, return -EGAIN on open until initialization
completes.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Vimarsh Zutshi <vimarsh.zutshi@gmail.com>
Acked-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Laurent Pinchart 2009-12-09 08:38:49 -03:00 committed by Mauro Carvalho Chehab
parent 0070d91e5b
commit 2096a5dcf9
8 changed files with 158 additions and 22 deletions

View File

@ -319,6 +319,22 @@ controlled through GPIO pins. This distinction is only relevant when setting
up the device, but once the subdev is registered it is completely transparent. up the device, but once the subdev is registered it is completely transparent.
V4L2 sub-device userspace API
-----------------------------
Beside exposing a kernel API through the v4l2_subdev_ops structure, V4L2
sub-devices can also be controlled directly by userspace applications.
Device nodes named v4l-subdevX can be created in /dev to access sub-devices
directly. If a sub-device supports direct userspace configuration it must set
the V4L2_SUBDEV_FL_HAS_DEVNODE flag before being registered.
After registering sub-devices, the v4l2_device driver can create device nodes
for all registered sub-devices marked with V4L2_SUBDEV_FL_HAS_DEVNODE by calling
v4l2_device_register_subdev_nodes(). Those device nodes will be automatically
removed when sub-devices are unregistered.
I2C sub-device drivers I2C sub-device drivers
---------------------- ----------------------

View File

@ -11,7 +11,7 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o
omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
v4l2-event.o v4l2-ctrls.o v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
# V4L2 core modules # V4L2 core modules

View File

@ -408,13 +408,14 @@ static int get_index(struct video_device *vdev)
} }
/** /**
* video_register_device - register video4linux devices * __video_register_device - register video4linux devices
* @vdev: video device structure we want to register * @vdev: video device structure we want to register
* @type: type of device to register * @type: type of device to register
* @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ... * @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ...
* -1 == first free) * -1 == first free)
* @warn_if_nr_in_use: warn if the desired device node number * @warn_if_nr_in_use: warn if the desired device node number
* was already in use and another number was chosen instead. * was already in use and another number was chosen instead.
* @owner: module that owns the video device node
* *
* The registration code assigns minor numbers and device node numbers * The registration code assigns minor numbers and device node numbers
* based on the requested type and registers the new device node with * based on the requested type and registers the new device node with
@ -435,9 +436,11 @@ static int get_index(struct video_device *vdev)
* %VFL_TYPE_VBI - Vertical blank data (undecoded) * %VFL_TYPE_VBI - Vertical blank data (undecoded)
* *
* %VFL_TYPE_RADIO - A radio card * %VFL_TYPE_RADIO - A radio card
*
* %VFL_TYPE_SUBDEV - A subdevice
*/ */
static int __video_register_device(struct video_device *vdev, int type, int nr, int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use) int warn_if_nr_in_use, struct module *owner)
{ {
int i = 0; int i = 0;
int ret; int ret;
@ -469,6 +472,9 @@ static int __video_register_device(struct video_device *vdev, int type, int nr,
case VFL_TYPE_RADIO: case VFL_TYPE_RADIO:
name_base = "radio"; name_base = "radio";
break; break;
case VFL_TYPE_SUBDEV:
name_base = "v4l-subdev";
break;
default: default:
printk(KERN_ERR "%s called with unknown type: %d\n", printk(KERN_ERR "%s called with unknown type: %d\n",
__func__, type); __func__, type);
@ -552,7 +558,7 @@ static int __video_register_device(struct video_device *vdev, int type, int nr,
goto cleanup; goto cleanup;
} }
vdev->cdev->ops = &v4l2_fops; vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = vdev->fops->owner; vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "%s: cdev_add failed\n", __func__); printk(KERN_ERR "%s: cdev_add failed\n", __func__);
@ -597,18 +603,7 @@ static int __video_register_device(struct video_device *vdev, int type, int nr,
vdev->minor = -1; vdev->minor = -1;
return ret; return ret;
} }
EXPORT_SYMBOL(__video_register_device);
int video_register_device(struct video_device *vdev, int type, int nr)
{
return __video_register_device(vdev, type, nr, 1);
}
EXPORT_SYMBOL(video_register_device);
int video_register_device_no_warn(struct video_device *vdev, int type, int nr)
{
return __video_register_device(vdev, type, nr, 0);
}
EXPORT_SYMBOL(video_register_device_no_warn);
/** /**
* video_unregister_device - unregister a video4linux device * video_unregister_device - unregister a video4linux device

View File

@ -124,16 +124,20 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
/* Check for valid input */ /* Check for valid input */
if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
return -EINVAL; return -EINVAL;
/* Warn if we apparently re-register a subdev */ /* Warn if we apparently re-register a subdev */
WARN_ON(sd->v4l2_dev != NULL); WARN_ON(sd->v4l2_dev != NULL);
if (!try_module_get(sd->owner)) if (!try_module_get(sd->owner))
return -ENODEV; return -ENODEV;
sd->v4l2_dev = v4l2_dev; sd->v4l2_dev = v4l2_dev;
if (sd->internal_ops && sd->internal_ops->registered) { if (sd->internal_ops && sd->internal_ops->registered) {
err = sd->internal_ops->registered(sd); err = sd->internal_ops->registered(sd);
if (err) if (err)
return err; return err;
} }
/* This just returns 0 if either of the two args is NULL */ /* This just returns 0 if either of the two args is NULL */
err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler); err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
if (err) { if (err) {
@ -141,24 +145,57 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
sd->internal_ops->unregistered(sd); sd->internal_ops->unregistered(sd);
return err; return err;
} }
spin_lock(&v4l2_dev->lock); spin_lock(&v4l2_dev->lock);
list_add_tail(&sd->list, &v4l2_dev->subdevs); list_add_tail(&sd->list, &v4l2_dev->subdevs);
spin_unlock(&v4l2_dev->lock); spin_unlock(&v4l2_dev->lock);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
{
struct video_device *vdev;
struct v4l2_subdev *sd;
int err;
/* Register a device node for every subdev marked with the
* V4L2_SUBDEV_FL_HAS_DEVNODE flag.
*/
list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
continue;
vdev = &sd->devnode;
strlcpy(vdev->name, sd->name, sizeof(vdev->name));
vdev->v4l2_dev = v4l2_dev;
vdev->fops = &v4l2_subdev_fops;
vdev->release = video_device_release_empty;
err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
sd->owner);
if (err < 0)
return err;
}
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes);
void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
{ {
/* return if it isn't registered */ /* return if it isn't registered */
if (sd == NULL || sd->v4l2_dev == NULL) if (sd == NULL || sd->v4l2_dev == NULL)
return; return;
spin_lock(&sd->v4l2_dev->lock); spin_lock(&sd->v4l2_dev->lock);
list_del(&sd->list); list_del(&sd->list);
spin_unlock(&sd->v4l2_dev->lock); spin_unlock(&sd->v4l2_dev->lock);
if (sd->internal_ops && sd->internal_ops->unregistered) if (sd->internal_ops && sd->internal_ops->unregistered)
sd->internal_ops->unregistered(sd); sd->internal_ops->unregistered(sd);
sd->v4l2_dev = NULL; sd->v4l2_dev = NULL;
video_unregister_device(&sd->devnode);
module_put(sd->owner); module_put(sd->owner);
} }
EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);

View File

@ -0,0 +1,60 @@
/*
* V4L2 subdevice support.
*
* Copyright (C) 2010 Nokia Corporation
*
* Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
static int subdev_open(struct file *file)
{
return 0;
}
static int subdev_close(struct file *file)
{
return 0;
}
static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
switch (cmd) {
default:
return -ENOIOCTLCMD;
}
return 0;
}
static long subdev_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
return video_usercopy(file, cmd, arg, subdev_do_ioctl);
}
const struct v4l2_file_operations v4l2_subdev_fops = {
.owner = THIS_MODULE,
.open = subdev_open,
.unlocked_ioctl = subdev_ioctl,
.release = subdev_close,
};

View File

@ -21,7 +21,8 @@
#define VFL_TYPE_GRABBER 0 #define VFL_TYPE_GRABBER 0
#define VFL_TYPE_VBI 1 #define VFL_TYPE_VBI 1
#define VFL_TYPE_RADIO 2 #define VFL_TYPE_RADIO 2
#define VFL_TYPE_MAX 3 #define VFL_TYPE_SUBDEV 3
#define VFL_TYPE_MAX 4
struct v4l2_ioctl_callbacks; struct v4l2_ioctl_callbacks;
struct video_device; struct video_device;
@ -102,15 +103,26 @@ struct video_device
/* dev to video-device */ /* dev to video-device */
#define to_video_device(cd) container_of(cd, struct video_device, dev) #define to_video_device(cd) container_of(cd, struct video_device, dev)
int __must_check __video_register_device(struct video_device *vdev, int type,
int nr, int warn_if_nr_in_use, struct module *owner);
/* Register video devices. Note that if video_register_device fails, /* Register video devices. Note that if video_register_device fails,
the release() callback of the video_device structure is *not* called, so the release() callback of the video_device structure is *not* called, so
the caller is responsible for freeing any data. Usually that means that the caller is responsible for freeing any data. Usually that means that
you call video_device_release() on failure. */ you call video_device_release() on failure. */
int __must_check video_register_device(struct video_device *vdev, int type, int nr); static inline int __must_check video_register_device(struct video_device *vdev,
int type, int nr)
{
return __video_register_device(vdev, type, nr, 1, vdev->fops->owner);
}
/* Same as video_register_device, but no warning is issued if the desired /* Same as video_register_device, but no warning is issued if the desired
device node number was already in use. */ device node number was already in use. */
int __must_check video_register_device_no_warn(struct video_device *vdev, int type, int nr); static inline int __must_check video_register_device_no_warn(
struct video_device *vdev, int type, int nr)
{
return __video_register_device(vdev, type, nr, 0, vdev->fops->owner);
}
/* Unregister video devices. Will do nothing if vdev == NULL or /* Unregister video devices. Will do nothing if vdev == NULL or
video_is_registered() returns false. */ video_is_registered() returns false. */

View File

@ -96,6 +96,12 @@ int __must_check v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
wasn't registered. In that case it will do nothing. */ wasn't registered. In that case it will do nothing. */
void v4l2_device_unregister_subdev(struct v4l2_subdev *sd); void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);
/* Register device nodes for all subdev of the v4l2 device that are marked with
* the V4L2_SUBDEV_FL_HAS_DEVNODE flag.
*/
int __must_check
v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev);
/* Iterate over all subdevs. */ /* Iterate over all subdevs. */
#define v4l2_device_for_each_subdev(sd, v4l2_dev) \ #define v4l2_device_for_each_subdev(sd, v4l2_dev) \
list_for_each_entry(sd, &(v4l2_dev)->subdevs, list) list_for_each_entry(sd, &(v4l2_dev)->subdevs, list)

View File

@ -22,6 +22,7 @@
#define _V4L2_SUBDEV_H #define _V4L2_SUBDEV_H
#include <media/v4l2-common.h> #include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-mediabus.h> #include <media/v4l2-mediabus.h>
/* generic v4l2_device notify callback notification values */ /* generic v4l2_device notify callback notification values */
@ -431,9 +432,11 @@ struct v4l2_subdev_internal_ops {
#define V4L2_SUBDEV_NAME_SIZE 32 #define V4L2_SUBDEV_NAME_SIZE 32
/* Set this flag if this subdev is a i2c device. */ /* Set this flag if this subdev is a i2c device. */
#define V4L2_SUBDEV_FL_IS_I2C (1U << 0) #define V4L2_SUBDEV_FL_IS_I2C (1U << 0)
/* Set this flag if this subdev is a spi device. */ /* Set this flag if this subdev is a spi device. */
#define V4L2_SUBDEV_FL_IS_SPI (1U << 1) #define V4L2_SUBDEV_FL_IS_SPI (1U << 1)
/* Set this flag if this subdev needs a device node. */
#define V4L2_SUBDEV_FL_HAS_DEVNODE (1U << 2)
/* Each instance of a subdev driver should create this struct, either /* Each instance of a subdev driver should create this struct, either
stand-alone or embedded in a larger struct. stand-alone or embedded in a larger struct.
@ -455,8 +458,15 @@ struct v4l2_subdev {
/* pointer to private data */ /* pointer to private data */
void *dev_priv; void *dev_priv;
void *host_priv; void *host_priv;
/* subdev device node */
struct video_device devnode;
}; };
#define vdev_to_v4l2_subdev(vdev) \
container_of(vdev, struct v4l2_subdev, devnode)
extern const struct v4l2_file_operations v4l2_subdev_fops;
static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p)
{ {
sd->dev_priv = p; sd->dev_priv = p;