mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
[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.
|
||||
|
||||
|
||||
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
|
||||
----------------------
|
||||
|
||||
|
@ -11,7 +11,7 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o
|
||||
omap2cam-objs := omap24xxcam.o omap24xxcam-dma.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
|
||||
|
||||
|
@ -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
|
||||
* @type: type of device to register
|
||||
* @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ...
|
||||
* -1 == first free)
|
||||
* @warn_if_nr_in_use: warn if the desired device node number
|
||||
* 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
|
||||
* 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_RADIO - A radio card
|
||||
*
|
||||
* %VFL_TYPE_SUBDEV - A subdevice
|
||||
*/
|
||||
static int __video_register_device(struct video_device *vdev, int type, int nr,
|
||||
int warn_if_nr_in_use)
|
||||
int __video_register_device(struct video_device *vdev, int type, int nr,
|
||||
int warn_if_nr_in_use, struct module *owner)
|
||||
{
|
||||
int i = 0;
|
||||
int ret;
|
||||
@ -469,6 +472,9 @@ static int __video_register_device(struct video_device *vdev, int type, int nr,
|
||||
case VFL_TYPE_RADIO:
|
||||
name_base = "radio";
|
||||
break;
|
||||
case VFL_TYPE_SUBDEV:
|
||||
name_base = "v4l-subdev";
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s called with unknown type: %d\n",
|
||||
__func__, type);
|
||||
@ -552,7 +558,7 @@ static int __video_register_device(struct video_device *vdev, int type, int nr,
|
||||
goto cleanup;
|
||||
}
|
||||
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);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "%s: cdev_add failed\n", __func__);
|
||||
@ -597,18 +603,7 @@ cleanup:
|
||||
vdev->minor = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
EXPORT_SYMBOL(__video_register_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 */
|
||||
if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
|
||||
return -EINVAL;
|
||||
|
||||
/* Warn if we apparently re-register a subdev */
|
||||
WARN_ON(sd->v4l2_dev != NULL);
|
||||
|
||||
if (!try_module_get(sd->owner))
|
||||
return -ENODEV;
|
||||
|
||||
sd->v4l2_dev = v4l2_dev;
|
||||
if (sd->internal_ops && sd->internal_ops->registered) {
|
||||
err = sd->internal_ops->registered(sd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* This just returns 0 if either of the two args is NULL */
|
||||
err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
|
||||
if (err) {
|
||||
@ -141,24 +145,57 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
|
||||
sd->internal_ops->unregistered(sd);
|
||||
return err;
|
||||
}
|
||||
|
||||
spin_lock(&v4l2_dev->lock);
|
||||
list_add_tail(&sd->list, &v4l2_dev->subdevs);
|
||||
spin_unlock(&v4l2_dev->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
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)
|
||||
{
|
||||
/* return if it isn't registered */
|
||||
if (sd == NULL || sd->v4l2_dev == NULL)
|
||||
return;
|
||||
|
||||
spin_lock(&sd->v4l2_dev->lock);
|
||||
list_del(&sd->list);
|
||||
spin_unlock(&sd->v4l2_dev->lock);
|
||||
if (sd->internal_ops && sd->internal_ops->unregistered)
|
||||
sd->internal_ops->unregistered(sd);
|
||||
sd->v4l2_dev = NULL;
|
||||
|
||||
video_unregister_device(&sd->devnode);
|
||||
module_put(sd->owner);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
|
||||
|
60
drivers/media/video/v4l2-subdev.c
Normal file
60
drivers/media/video/v4l2-subdev.c
Normal 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,
|
||||
};
|
@ -21,7 +21,8 @@
|
||||
#define VFL_TYPE_GRABBER 0
|
||||
#define VFL_TYPE_VBI 1
|
||||
#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 video_device;
|
||||
@ -102,15 +103,26 @@ struct video_device
|
||||
/* dev to video-device */
|
||||
#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,
|
||||
the release() callback of the video_device structure is *not* called, so
|
||||
the caller is responsible for freeing any data. Usually that means that
|
||||
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
|
||||
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
|
||||
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. */
|
||||
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. */
|
||||
#define v4l2_device_for_each_subdev(sd, v4l2_dev) \
|
||||
list_for_each_entry(sd, &(v4l2_dev)->subdevs, list)
|
||||
|
@ -22,6 +22,7 @@
|
||||
#define _V4L2_SUBDEV_H
|
||||
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-mediabus.h>
|
||||
|
||||
/* generic v4l2_device notify callback notification values */
|
||||
@ -431,9 +432,11 @@ struct v4l2_subdev_internal_ops {
|
||||
#define V4L2_SUBDEV_NAME_SIZE 32
|
||||
|
||||
/* 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. */
|
||||
#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
|
||||
stand-alone or embedded in a larger struct.
|
||||
@ -455,8 +458,15 @@ struct v4l2_subdev {
|
||||
/* pointer to private data */
|
||||
void *dev_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)
|
||||
{
|
||||
sd->dev_priv = p;
|
||||
|
Loading…
Reference in New Issue
Block a user