mirror of https://gitee.com/openkylin/linux.git
[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:
parent
0070d91e5b
commit
2096a5dcf9
|
@ -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
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
|
@ -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. */
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue