summaryrefslogtreecommitdiff
path: root/drivers/media/v4l2-core/v4l2-dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-dev.c')
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c735
1 files changed, 486 insertions, 249 deletions
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index c8859d6ff6ad..10a126e50c1c 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -1,21 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Video capture interface for Linux version 2
*
* A generic video device interface for the LINUX operating system
* using a set of device structures/vectors for low level operations.
*
- * 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; either version
- * 2 of the License, or (at your option) any later version.
- *
* Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
- * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
+ * Mauro Carvalho Chehab <mchehab@kernel.org> (version 2)
*
* Fixes: 20000516 Claudio Matsuoka <claudio@conectiva.com>
* - Added procfs support
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
@@ -25,37 +24,44 @@
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/slab.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
#define VIDEO_NUM_DEVICES 256
#define VIDEO_NAME "video4linux"
+#define dprintk(fmt, arg...) do { \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
+
/*
* sysfs stuff
*/
-static ssize_t show_index(struct device *cd,
- struct device_attribute *attr, char *buf)
+static ssize_t index_show(struct device *cd,
+ struct device_attribute *attr, char *buf)
{
struct video_device *vdev = to_video_device(cd);
return sprintf(buf, "%i\n", vdev->index);
}
+static DEVICE_ATTR_RO(index);
-static ssize_t show_debug(struct device *cd,
- struct device_attribute *attr, char *buf)
+static ssize_t dev_debug_show(struct device *cd,
+ struct device_attribute *attr, char *buf)
{
struct video_device *vdev = to_video_device(cd);
- return sprintf(buf, "%i\n", vdev->debug);
+ return sprintf(buf, "%i\n", vdev->dev_debug);
}
-static ssize_t set_debug(struct device *cd, struct device_attribute *attr,
- const char *buf, size_t len)
+static ssize_t dev_debug_store(struct device *cd, struct device_attribute *attr,
+ const char *buf, size_t len)
{
struct video_device *vdev = to_video_device(cd);
int res = 0;
@@ -65,29 +71,34 @@ static ssize_t set_debug(struct device *cd, struct device_attribute *attr,
if (res)
return res;
- vdev->debug = value;
+ vdev->dev_debug = value;
return len;
}
+static DEVICE_ATTR_RW(dev_debug);
-static ssize_t show_name(struct device *cd,
+static ssize_t name_show(struct device *cd,
struct device_attribute *attr, char *buf)
{
struct video_device *vdev = to_video_device(cd);
return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name);
}
+static DEVICE_ATTR_RO(name);
-static struct device_attribute video_device_attrs[] = {
- __ATTR(name, S_IRUGO, show_name, NULL),
- __ATTR(debug, 0644, show_debug, set_debug),
- __ATTR(index, S_IRUGO, show_index, NULL),
- __ATTR_NULL
+static struct attribute *video_device_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_dev_debug.attr,
+ &dev_attr_index.attr,
+ NULL,
};
+ATTRIBUTE_GROUPS(video_device);
+
+static struct dentry *v4l2_debugfs_root_dir;
/*
* Active devices
*/
-static struct video_device *video_device[VIDEO_NUM_DEVICES];
+static struct video_device *video_devices[VIDEO_NUM_DEVICES];
static DEFINE_MUTEX(videodev_lock);
static DECLARE_BITMAP(devnode_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES);
@@ -98,7 +109,7 @@ static DECLARE_BITMAP(devnode_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES);
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* Return the bitmap corresponding to vfl_type. */
-static inline unsigned long *devnode_bits(int vfl_type)
+static inline unsigned long *devnode_bits(enum vfl_devnode_type vfl_type)
{
/* Any types not assigned to fixed minor ranges must be mapped to
one single bitmap for the purposes of finding a free node number
@@ -109,7 +120,7 @@ static inline unsigned long *devnode_bits(int vfl_type)
}
#else
/* Return the bitmap corresponding to vfl_type. */
-static inline unsigned long *devnode_bits(int vfl_type)
+static inline unsigned long *devnode_bits(enum vfl_devnode_type vfl_type)
{
return devnode_nums[vfl_type];
}
@@ -169,14 +180,14 @@ static void v4l2_device_release(struct device *cd)
struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
mutex_lock(&videodev_lock);
- if (WARN_ON(video_device[vdev->minor] != vdev)) {
+ if (WARN_ON(video_devices[vdev->minor] != vdev)) {
/* should not happen */
mutex_unlock(&videodev_lock);
return;
}
/* Free up this device for reuse */
- video_device[vdev->minor] = NULL;
+ video_devices[vdev->minor] = NULL;
/* Delete the cdev on this minor as well */
cdev_del(vdev->cdev);
@@ -190,9 +201,12 @@ static void v4l2_device_release(struct device *cd)
mutex_unlock(&videodev_lock);
#if defined(CONFIG_MEDIA_CONTROLLER)
- if (v4l2_dev && v4l2_dev->mdev &&
- vdev->vfl_type != VFL_TYPE_SUBDEV)
- media_device_unregister_entity(&vdev->entity);
+ if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
+ /* Remove interfaces and interface links */
+ media_devnode_remove(vdev->intf_devnode);
+ if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
+ media_device_unregister_entity(&vdev->entity);
+ }
#endif
/* Do not call v4l2_device_put if there is no release callback set.
@@ -203,7 +217,7 @@ static void v4l2_device_release(struct device *cd)
* TODO: In the long run all drivers that use v4l2_device should use the
* v4l2_device release callback. This check will then be unnecessary.
*/
- if (v4l2_dev && v4l2_dev->release == NULL)
+ if (v4l2_dev->release == NULL)
v4l2_dev = NULL;
/* Release video_device and perform other
@@ -215,14 +229,14 @@ static void v4l2_device_release(struct device *cd)
v4l2_device_put(v4l2_dev);
}
-static struct class video_class = {
+static const struct class video_class = {
.name = VIDEO_NAME,
- .dev_attrs = video_device_attrs,
+ .dev_groups = video_device_groups,
};
struct video_device *video_devdata(struct file *file)
{
- return video_device[iminor(file_inode(file))];
+ return video_devices[iminor(file_inode(file))];
}
EXPORT_SYMBOL(video_devdata);
@@ -300,8 +314,9 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf,
return -EINVAL;
if (video_is_registered(vdev))
ret = vdev->fops->read(filp, buf, sz, off);
- if (vdev->debug)
- printk(KERN_DEBUG "%s: read: %zd (%d)\n",
+ if ((vdev->dev_debug & V4L2_DEV_DEBUG_FOP) &&
+ (vdev->dev_debug & V4L2_DEV_DEBUG_STREAMING))
+ dprintk("%s: read: %zd (%d)\n",
video_device_node_name(vdev), sz, ret);
return ret;
}
@@ -316,24 +331,28 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf,
return -EINVAL;
if (video_is_registered(vdev))
ret = vdev->fops->write(filp, buf, sz, off);
- if (vdev->debug)
- printk(KERN_DEBUG "%s: write: %zd (%d)\n",
+ if ((vdev->dev_debug & V4L2_DEV_DEBUG_FOP) &&
+ (vdev->dev_debug & V4L2_DEV_DEBUG_STREAMING))
+ dprintk("%s: write: %zd (%d)\n",
video_device_node_name(vdev), sz, ret);
return ret;
}
-static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
+static __poll_t v4l2_poll(struct file *filp, struct poll_table_struct *poll)
{
struct video_device *vdev = video_devdata(filp);
- unsigned int res = POLLERR | POLLHUP;
+ __poll_t res = EPOLLERR | EPOLLHUP | EPOLLPRI;
- if (!vdev->fops->poll)
- return DEFAULT_POLLMASK;
- if (video_is_registered(vdev))
- res = vdev->fops->poll(filp, poll);
- if (vdev->debug)
- printk(KERN_DEBUG "%s: poll: %08x\n",
- video_device_node_name(vdev), res);
+ if (video_is_registered(vdev)) {
+ if (!vdev->fops->poll)
+ res = DEFAULT_POLLMASK;
+ else
+ res = vdev->fops->poll(filp, poll);
+ }
+ if (vdev->dev_debug & V4L2_DEV_DEBUG_POLL)
+ dprintk("%s: poll: %08x %08x\n",
+ video_device_node_name(vdev), res,
+ poll_requested_events(poll));
return res;
}
@@ -343,47 +362,8 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
int ret = -ENODEV;
if (vdev->fops->unlocked_ioctl) {
- struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd);
-
- if (lock && mutex_lock_interruptible(lock))
- return -ERESTARTSYS;
if (video_is_registered(vdev))
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
- if (lock)
- mutex_unlock(lock);
- } else if (vdev->fops->ioctl) {
- /* This code path is a replacement for the BKL. It is a major
- * hack but it will have to do for those drivers that are not
- * yet converted to use unlocked_ioctl.
- *
- * There are two options: if the driver implements struct
- * v4l2_device, then the lock defined there is used to
- * serialize the ioctls. Otherwise the v4l2 core lock defined
- * below is used. This lock is really bad since it serializes
- * completely independent devices.
- *
- * Both variants suffer from the same problem: if the driver
- * sleeps, then it blocks all ioctls since the lock is still
- * held. This is very common for VIDIOC_DQBUF since that
- * normally waits for a frame to arrive. As a result any other
- * ioctl calls will proceed very, very slowly since each call
- * will have to wait for the VIDIOC_QBUF to finish. Things that
- * should take 0.01s may now take 10-20 seconds.
- *
- * The workaround is to *not* take the lock for VIDIOC_DQBUF.
- * This actually works OK for videobuf-based drivers, since
- * videobuf will take its own internal lock.
- */
- static DEFINE_MUTEX(v4l2_ioctl_mutex);
- struct mutex *m = vdev->v4l2_dev ?
- &vdev->v4l2_dev->ioctl_lock : &v4l2_ioctl_mutex;
-
- if (cmd != VIDIOC_DQBUF && mutex_lock_interruptible(m))
- return -ERESTARTSYS;
- if (video_is_registered(vdev))
- ret = vdev->fops->ioctl(filp, cmd, arg);
- if (cmd != VIDIOC_DQBUF)
- mutex_unlock(m);
} else
ret = -ENOTTY;
@@ -405,8 +385,8 @@ static unsigned long v4l2_get_unmapped_area(struct file *filp,
if (!video_is_registered(vdev))
return -ENODEV;
ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
- if (vdev->debug)
- printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n",
+ if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
+ dprintk("%s: get_unmapped_area (%d)\n",
video_device_node_name(vdev), ret);
return ret;
}
@@ -421,8 +401,8 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
return -ENODEV;
if (video_is_registered(vdev))
ret = vdev->fops->mmap(filp, vm);
- if (vdev->debug)
- printk(KERN_DEBUG "%s: mmap (%d)\n",
+ if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
+ dprintk("%s: mmap (%d)\n",
video_device_node_name(vdev), ret);
return ret;
}
@@ -431,7 +411,7 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
static int v4l2_open(struct inode *inode, struct file *filp)
{
struct video_device *vdev;
- int ret = 0;
+ int ret;
/* Check if the video device is available */
mutex_lock(&videodev_lock);
@@ -444,16 +424,27 @@ static int v4l2_open(struct inode *inode, struct file *filp)
/* and increase the device refcount */
video_get(vdev);
mutex_unlock(&videodev_lock);
- if (vdev->fops->open) {
- if (video_is_registered(vdev))
- ret = vdev->fops->open(filp);
- else
- ret = -ENODEV;
+
+ if (!video_is_registered(vdev)) {
+ ret = -ENODEV;
+ goto done;
+ }
+
+ ret = vdev->fops->open(filp);
+ if (ret)
+ goto done;
+
+ /* All drivers must use v4l2_fh. */
+ if (WARN_ON(!test_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags))) {
+ vdev->fops->release(filp);
+ ret = -ENODEV;
}
- if (vdev->debug)
- printk(KERN_DEBUG "%s: open (%d)\n",
+done:
+ if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
+ dprintk("%s: open (%d)\n",
video_device_node_name(vdev), ret);
+
/* decrease the refcount in case of an error */
if (ret)
video_put(vdev);
@@ -464,12 +455,24 @@ static int v4l2_open(struct inode *inode, struct file *filp)
static int v4l2_release(struct inode *inode, struct file *filp)
{
struct video_device *vdev = video_devdata(filp);
- int ret = 0;
+ int ret;
- if (vdev->fops->release)
+ /*
+ * We need to serialize the release() with queueing new requests.
+ * The release() may trigger the cancellation of a streaming
+ * operation, and that should not be mixed with queueing a new
+ * request at the same time.
+ */
+ if (v4l2_device_supports_requests(vdev->v4l2_dev)) {
+ mutex_lock(&vdev->v4l2_dev->mdev->req_queue_mutex);
+ ret = vdev->fops->release(filp);
+ mutex_unlock(&vdev->v4l2_dev->mdev->req_queue_mutex);
+ } else {
ret = vdev->fops->release(filp);
- if (vdev->debug)
- printk(KERN_DEBUG "%s: release\n",
+ }
+
+ if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
+ dprintk("%s: release\n",
video_device_node_name(vdev));
/* decrease the refcount unconditionally since the release()
@@ -491,7 +494,6 @@ static const struct file_operations v4l2_fops = {
#endif
.release = v4l2_release,
.poll = v4l2_poll,
- .llseek = no_llseek,
};
/**
@@ -517,18 +519,17 @@ static int get_index(struct video_device *vdev)
bitmap_zero(used, VIDEO_NUM_DEVICES);
for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
- if (video_device[i] != NULL &&
- video_device[i]->v4l2_dev == vdev->v4l2_dev) {
- set_bit(video_device[i]->index, used);
+ if (video_devices[i] != NULL &&
+ video_devices[i]->v4l2_dev == vdev->v4l2_dev) {
+ __set_bit(video_devices[i]->index, used);
}
}
return find_first_zero_bit(used, VIDEO_NUM_DEVICES);
}
-#define SET_VALID_IOCTL(ops, cmd, op) \
- if (ops->op) \
- set_bit(_IOC_NR(cmd), valid_ioctls)
+#define SET_VALID_IOCTL(ops, cmd, op) \
+ do { if ((ops)->op) __set_bit(_IOC_NR(cmd), valid_ioctls); } while (0)
/* This determines which ioctls are actually implemented in the driver.
It's a one-time thing which simplifies video_ioctl2 as it can just do
@@ -545,89 +546,101 @@ static int get_index(struct video_device *vdev)
*/
static void determine_valid_ioctls(struct video_device *vdev)
{
+ const u32 vid_caps = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+ V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE;
+ const u32 meta_caps = V4L2_CAP_META_CAPTURE |
+ V4L2_CAP_META_OUTPUT;
DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);
const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops;
- bool is_vid = vdev->vfl_type == VFL_TYPE_GRABBER;
+ bool is_vid = vdev->vfl_type == VFL_TYPE_VIDEO &&
+ (vdev->device_caps & vid_caps);
bool is_vbi = vdev->vfl_type == VFL_TYPE_VBI;
bool is_radio = vdev->vfl_type == VFL_TYPE_RADIO;
+ bool is_sdr = vdev->vfl_type == VFL_TYPE_SDR;
+ bool is_tch = vdev->vfl_type == VFL_TYPE_TOUCH;
+ bool is_meta = vdev->vfl_type == VFL_TYPE_VIDEO &&
+ (vdev->device_caps & meta_caps);
bool is_rx = vdev->vfl_dir != VFL_DIR_TX;
bool is_tx = vdev->vfl_dir != VFL_DIR_RX;
+ bool is_io_mc = vdev->device_caps & V4L2_CAP_IO_MC;
+ bool has_streaming = vdev->device_caps & V4L2_CAP_STREAMING;
+ bool is_edid = vdev->device_caps & V4L2_CAP_EDID;
bitmap_zero(valid_ioctls, BASE_VIDIOC_PRIVATE);
/* vfl_type and vfl_dir independent ioctls */
SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap);
- if (ops->vidioc_g_priority ||
- test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags))
- set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls);
- if (ops->vidioc_s_priority ||
- test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags))
- set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls);
- SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon);
- SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
+ __set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls);
+
/* Note: the control handler can also be passed through the filehandle,
and that can't be tested here. If the bit for these control ioctls
is set, then the ioctl is valid. But if it is 0, then it can still
be valid if the filehandle passed the control handler. */
- if (vdev->ctrl_handler || ops->vidioc_queryctrl)
- set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls);
- if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls);
- if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_query_ext_ctrl)
+ __set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_query_ext_ctrl)
+ __set_bit(_IOC_NR(VIDIOC_QUERY_EXT_CTRL), valid_ioctls);
if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls);
if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls)
+ __set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls)
+ __set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls);
if (vdev->ctrl_handler || ops->vidioc_try_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls);
if (vdev->ctrl_handler || ops->vidioc_querymenu)
- set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls);
- SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency);
- SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency);
+ __set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls);
+ if (!is_tch) {
+ SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency);
+ SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency);
+ }
SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status);
#ifdef CONFIG_VIDEO_ADV_DEBUG
- set_bit(_IOC_NR(VIDIOC_DBG_G_CHIP_INFO), valid_ioctls);
- set_bit(_IOC_NR(VIDIOC_DBG_G_REGISTER), valid_ioctls);
- set_bit(_IOC_NR(VIDIOC_DBG_S_REGISTER), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_DBG_G_CHIP_INFO), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_DBG_G_REGISTER), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_DBG_S_REGISTER), valid_ioctls);
#endif
/* yes, really vidioc_subscribe_event */
SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event);
SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event);
SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event);
if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator)
- set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls);
if (is_vid) {
/* video specific ioctls */
if ((is_rx && (ops->vidioc_enum_fmt_vid_cap ||
- ops->vidioc_enum_fmt_vid_cap_mplane ||
ops->vidioc_enum_fmt_vid_overlay)) ||
- (is_tx && (ops->vidioc_enum_fmt_vid_out ||
- ops->vidioc_enum_fmt_vid_out_mplane)))
- set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
+ (is_tx && ops->vidioc_enum_fmt_vid_out))
+ __set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
ops->vidioc_g_fmt_vid_cap_mplane ||
ops->vidioc_g_fmt_vid_overlay)) ||
(is_tx && (ops->vidioc_g_fmt_vid_out ||
ops->vidioc_g_fmt_vid_out_mplane ||
ops->vidioc_g_fmt_vid_out_overlay)))
- set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
ops->vidioc_s_fmt_vid_cap_mplane ||
ops->vidioc_s_fmt_vid_overlay)) ||
(is_tx && (ops->vidioc_s_fmt_vid_out ||
ops->vidioc_s_fmt_vid_out_mplane ||
ops->vidioc_s_fmt_vid_out_overlay)))
- set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
ops->vidioc_try_fmt_vid_cap_mplane ||
ops->vidioc_try_fmt_vid_overlay)) ||
(is_tx && (ops->vidioc_try_fmt_vid_out ||
ops->vidioc_try_fmt_vid_out_mplane ||
ops->vidioc_try_fmt_vid_out_overlay)))
- set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
@@ -640,27 +653,77 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd);
SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes);
SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals);
- } else if (is_vbi) {
+ if (ops->vidioc_g_selection &&
+ !test_bit(_IOC_NR(VIDIOC_G_SELECTION), vdev->valid_ioctls)) {
+ __set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls);
+ }
+ if (ops->vidioc_s_selection &&
+ !test_bit(_IOC_NR(VIDIOC_S_SELECTION), vdev->valid_ioctls))
+ __set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
+ SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
+ }
+ if (is_meta && is_rx) {
+ /* metadata capture specific ioctls */
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_FMT, vidioc_enum_fmt_meta_cap);
+ SET_VALID_IOCTL(ops, VIDIOC_G_FMT, vidioc_g_fmt_meta_cap);
+ SET_VALID_IOCTL(ops, VIDIOC_S_FMT, vidioc_s_fmt_meta_cap);
+ SET_VALID_IOCTL(ops, VIDIOC_TRY_FMT, vidioc_try_fmt_meta_cap);
+ } else if (is_meta && is_tx) {
+ /* metadata output specific ioctls */
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_FMT, vidioc_enum_fmt_meta_out);
+ SET_VALID_IOCTL(ops, VIDIOC_G_FMT, vidioc_g_fmt_meta_out);
+ SET_VALID_IOCTL(ops, VIDIOC_S_FMT, vidioc_s_fmt_meta_out);
+ SET_VALID_IOCTL(ops, VIDIOC_TRY_FMT, vidioc_try_fmt_meta_out);
+ }
+ if (is_vbi) {
/* vbi specific ioctls */
if ((is_rx && (ops->vidioc_g_fmt_vbi_cap ||
ops->vidioc_g_fmt_sliced_vbi_cap)) ||
(is_tx && (ops->vidioc_g_fmt_vbi_out ||
ops->vidioc_g_fmt_sliced_vbi_out)))
- set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
if ((is_rx && (ops->vidioc_s_fmt_vbi_cap ||
ops->vidioc_s_fmt_sliced_vbi_cap)) ||
(is_tx && (ops->vidioc_s_fmt_vbi_out ||
ops->vidioc_s_fmt_sliced_vbi_out)))
- set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
if ((is_rx && (ops->vidioc_try_fmt_vbi_cap ||
ops->vidioc_try_fmt_sliced_vbi_cap)) ||
(is_tx && (ops->vidioc_try_fmt_vbi_out ||
ops->vidioc_try_fmt_sliced_vbi_out)))
- set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap);
+ } else if (is_tch) {
+ /* touch specific ioctls */
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_FMT, vidioc_enum_fmt_vid_cap);
+ SET_VALID_IOCTL(ops, VIDIOC_G_FMT, vidioc_g_fmt_vid_cap);
+ SET_VALID_IOCTL(ops, VIDIOC_S_FMT, vidioc_s_fmt_vid_cap);
+ SET_VALID_IOCTL(ops, VIDIOC_TRY_FMT, vidioc_try_fmt_vid_cap);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input);
+ SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input);
+ SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input);
+ SET_VALID_IOCTL(ops, VIDIOC_G_PARM, vidioc_g_parm);
+ SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm);
+ } else if (is_sdr && is_rx) {
+ /* SDR receiver specific ioctls */
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_FMT, vidioc_enum_fmt_sdr_cap);
+ SET_VALID_IOCTL(ops, VIDIOC_G_FMT, vidioc_g_fmt_sdr_cap);
+ SET_VALID_IOCTL(ops, VIDIOC_S_FMT, vidioc_s_fmt_sdr_cap);
+ SET_VALID_IOCTL(ops, VIDIOC_TRY_FMT, vidioc_try_fmt_sdr_cap);
+ } else if (is_sdr && is_tx) {
+ /* SDR transmitter specific ioctls */
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_FMT, vidioc_enum_fmt_sdr_out);
+ SET_VALID_IOCTL(ops, VIDIOC_G_FMT, vidioc_g_fmt_sdr_out);
+ SET_VALID_IOCTL(ops, VIDIOC_S_FMT, vidioc_s_fmt_sdr_out);
+ SET_VALID_IOCTL(ops, VIDIOC_TRY_FMT, vidioc_try_fmt_sdr_out);
}
- if (!is_radio) {
- /* ioctls valid for video or vbi */
+
+ if (has_streaming) {
+ /* ioctls valid for streaming I/O */
SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
@@ -668,95 +731,186 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf);
SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs);
SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
+ SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon);
+ SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
+ /* VIDIOC_CREATE_BUFS support is mandatory to enable VIDIOC_REMOVE_BUFS */
+ if (ops->vidioc_create_bufs)
+ SET_VALID_IOCTL(ops, VIDIOC_REMOVE_BUFS, vidioc_remove_bufs);
+ }
+
+ if (is_vid || is_vbi || is_meta) {
+ /* ioctls valid for video, vbi and metadata */
if (ops->vidioc_s_std)
- set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std);
SET_VALID_IOCTL(ops, VIDIOC_G_STD, vidioc_g_std);
if (is_rx) {
SET_VALID_IOCTL(ops, VIDIOC_QUERYSTD, vidioc_querystd);
- SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input);
- SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input);
- SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input);
+ if (is_io_mc) {
+ __set_bit(_IOC_NR(VIDIOC_ENUMINPUT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_G_INPUT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_S_INPUT), valid_ioctls);
+ } else {
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input);
+ SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input);
+ SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input);
+ }
SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDIO, vidioc_enumaudio);
SET_VALID_IOCTL(ops, VIDIOC_G_AUDIO, vidioc_g_audio);
SET_VALID_IOCTL(ops, VIDIOC_S_AUDIO, vidioc_s_audio);
SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings);
+ SET_VALID_IOCTL(ops, VIDIOC_S_EDID, vidioc_s_edid);
}
if (is_tx) {
- SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output);
- SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output);
- SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output);
+ if (is_io_mc) {
+ __set_bit(_IOC_NR(VIDIOC_ENUMOUTPUT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_G_OUTPUT), valid_ioctls);
+ __set_bit(_IOC_NR(VIDIOC_S_OUTPUT), valid_ioctls);
+ } else {
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output);
+ SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output);
+ SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output);
+ }
SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDOUT, vidioc_enumaudout);
SET_VALID_IOCTL(ops, VIDIOC_G_AUDOUT, vidioc_g_audout);
SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout);
}
- if (ops->vidioc_g_crop || ops->vidioc_g_selection)
- set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls);
- if (ops->vidioc_s_crop || ops->vidioc_s_selection)
- set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
- SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
- SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
- if (ops->vidioc_cropcap || ops->vidioc_g_selection)
- set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls);
- if (ops->vidioc_g_parm || (vdev->vfl_type == VFL_TYPE_GRABBER &&
- ops->vidioc_g_std))
- set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
+ if (ops->vidioc_g_parm || ops->vidioc_g_std)
+ __set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm);
SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings);
SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings);
SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings);
SET_VALID_IOCTL(ops, VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap);
+ SET_VALID_IOCTL(ops, VIDIOC_G_EDID, vidioc_g_edid);
}
- if (is_tx) {
- /* transmitter only ioctls */
+ if (is_tx && (is_radio || is_sdr)) {
+ /* radio transmitter only ioctls */
SET_VALID_IOCTL(ops, VIDIOC_G_MODULATOR, vidioc_g_modulator);
SET_VALID_IOCTL(ops, VIDIOC_S_MODULATOR, vidioc_s_modulator);
}
- if (is_rx) {
+ if (is_rx && !is_tch) {
/* receiver only ioctls */
SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner);
SET_VALID_IOCTL(ops, VIDIOC_S_TUNER, vidioc_s_tuner);
SET_VALID_IOCTL(ops, VIDIOC_S_HW_FREQ_SEEK, vidioc_s_hw_freq_seek);
}
+ if (is_edid) {
+ SET_VALID_IOCTL(ops, VIDIOC_G_EDID, vidioc_g_edid);
+ if (is_tx) {
+ SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output);
+ SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output);
+ }
+ if (is_rx) {
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input);
+ SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input);
+ SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input);
+ SET_VALID_IOCTL(ops, VIDIOC_S_EDID, vidioc_s_edid);
+ }
+ }
bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls,
BASE_VIDIOC_PRIVATE);
}
-/**
- * __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
- * the kernel.
- *
- * This function assumes that struct video_device was zeroed when it
- * was allocated and does not contain any stale date.
- *
- * An error is returned if no free minor or device node number could be
- * found, or if the registration of the device node failed.
- *
- * Zero is returned on success.
- *
- * Valid types are
- *
- * %VFL_TYPE_GRABBER - A frame grabber
- *
- * %VFL_TYPE_VBI - Vertical blank data (undecoded)
- *
- * %VFL_TYPE_RADIO - A radio card
- *
- * %VFL_TYPE_SUBDEV - A subdevice
- */
-int __video_register_device(struct video_device *vdev, int type, int nr,
- int warn_if_nr_in_use, struct module *owner)
+static int video_register_media_controller(struct video_device *vdev)
+{
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ u32 intf_type;
+ int ret;
+
+ /* Memory-to-memory devices are more complex and use
+ * their own function to register its mc entities.
+ */
+ if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M)
+ return 0;
+
+ vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
+ vdev->entity.function = MEDIA_ENT_F_UNKNOWN;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_VIDEO:
+ intf_type = MEDIA_INTF_T_V4L_VIDEO;
+ vdev->entity.function = MEDIA_ENT_F_IO_V4L;
+ break;
+ case VFL_TYPE_VBI:
+ intf_type = MEDIA_INTF_T_V4L_VBI;
+ vdev->entity.function = MEDIA_ENT_F_IO_VBI;
+ break;
+ case VFL_TYPE_SDR:
+ intf_type = MEDIA_INTF_T_V4L_SWRADIO;
+ vdev->entity.function = MEDIA_ENT_F_IO_SWRADIO;
+ break;
+ case VFL_TYPE_TOUCH:
+ intf_type = MEDIA_INTF_T_V4L_TOUCH;
+ vdev->entity.function = MEDIA_ENT_F_IO_V4L;
+ break;
+ case VFL_TYPE_RADIO:
+ intf_type = MEDIA_INTF_T_V4L_RADIO;
+ /*
+ * Radio doesn't have an entity at the V4L2 side to represent
+ * radio input or output. Instead, the audio input/output goes
+ * via either physical wires or ALSA.
+ */
+ break;
+ case VFL_TYPE_SUBDEV:
+ intf_type = MEDIA_INTF_T_V4L_SUBDEV;
+ /* Entity will be created via v4l2_device_register_subdev() */
+ break;
+ default:
+ return 0;
+ }
+
+ if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) {
+ vdev->entity.name = vdev->name;
+
+ /* Needed just for backward compatibility with legacy MC API */
+ vdev->entity.info.dev.major = VIDEO_MAJOR;
+ vdev->entity.info.dev.minor = vdev->minor;
+
+ ret = media_device_register_entity(vdev->v4l2_dev->mdev,
+ &vdev->entity);
+ if (ret < 0) {
+ pr_warn("%s: media_device_register_entity failed\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ vdev->intf_devnode = media_devnode_create(vdev->v4l2_dev->mdev,
+ intf_type,
+ 0, VIDEO_MAJOR,
+ vdev->minor);
+ if (!vdev->intf_devnode) {
+ media_device_unregister_entity(&vdev->entity);
+ return -ENOMEM;
+ }
+
+ if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) {
+ struct media_link *link;
+
+ link = media_create_intf_link(&vdev->entity,
+ &vdev->intf_devnode->intf,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (!link) {
+ media_devnode_remove(vdev->intf_devnode);
+ media_device_unregister_entity(&vdev->entity);
+ return -ENOMEM;
+ }
+ }
+
+ /* FIXME: how to create the other interface links? */
+
+#endif
+ return 0;
+}
+
+int __video_register_device(struct video_device *vdev,
+ enum vfl_devnode_type type,
+ int nr, int warn_if_nr_in_use,
+ struct module *owner)
{
int i = 0;
int ret;
@@ -774,6 +928,12 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
/* the v4l2_dev pointer MUST be present */
if (WARN_ON(!vdev->v4l2_dev))
return -EINVAL;
+ /* the device_caps field MUST be set for all but subdevs */
+ if (WARN_ON(type != VFL_TYPE_SUBDEV && !vdev->device_caps))
+ return -EINVAL;
+ /* the open and release file operations are mandatory */
+ if (WARN_ON(!vdev->fops || !vdev->fops->open || !vdev->fops->release))
+ return -EINVAL;
/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock);
@@ -781,7 +941,7 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
/* Part 1: check device type */
switch (type) {
- case VFL_TYPE_GRABBER:
+ case VFL_TYPE_VIDEO:
name_base = "video";
break;
case VFL_TYPE_VBI:
@@ -793,8 +953,15 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
case VFL_TYPE_SUBDEV:
name_base = "v4l-subdev";
break;
+ case VFL_TYPE_SDR:
+ /* Use device name 'swradio' because 'sdr' was already taken. */
+ name_base = "swradio";
+ break;
+ case VFL_TYPE_TOUCH:
+ name_base = "v4l-touch";
+ break;
default:
- printk(KERN_ERR "%s called with unknown type: %d\n",
+ pr_err("%s called with unknown type: %d\n",
__func__, type);
return -EINVAL;
}
@@ -818,7 +985,7 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
* of 128-191 and just pick the first free minor there
* (new style). */
switch (type) {
- case VFL_TYPE_GRABBER:
+ case VFL_TYPE_VIDEO:
minor_offset = 0;
minor_cnt = 64;
break;
@@ -843,7 +1010,7 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
if (nr == minor_cnt)
nr = devnode_find(vdev, 0, minor_cnt);
if (nr == minor_cnt) {
- printk(KERN_ERR "could not get a free device node number\n");
+ pr_err("could not get a free device node number\n");
mutex_unlock(&videodev_lock);
return -ENFILE;
}
@@ -854,21 +1021,26 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
/* The device node number and minor numbers are independent, so
we just find the first free minor number. */
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
- if (video_device[i] == NULL)
+ if (video_devices[i] == NULL)
break;
if (i == VIDEO_NUM_DEVICES) {
mutex_unlock(&videodev_lock);
- printk(KERN_ERR "could not get a free minor\n");
+ pr_err("could not get a free minor\n");
return -ENFILE;
}
#endif
vdev->minor = i + minor_offset;
vdev->num = nr;
- devnode_set(vdev);
/* Should not happen since we thought this minor was free */
- WARN_ON(video_device[vdev->minor] != NULL);
+ if (WARN_ON(video_devices[vdev->minor])) {
+ mutex_unlock(&videodev_lock);
+ pr_err("video_device not empty!\n");
+ return -ENFILE;
+ }
+ devnode_set(vdev);
vdev->index = get_index(vdev);
+ video_devices[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
if (vdev->ioctl_ops)
@@ -884,7 +1056,7 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
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__);
+ pr_err("%s: cdev_add failed\n", __func__);
kfree(vdev->cdev);
vdev->cdev = NULL;
goto cleanup;
@@ -894,44 +1066,30 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
vdev->dev.parent = vdev->dev_parent;
+ vdev->dev.release = v4l2_device_release;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
+
+ /* Increase v4l2_device refcount */
+ v4l2_device_get(vdev->v4l2_dev);
+
+ mutex_lock(&videodev_lock);
ret = device_register(&vdev->dev);
if (ret < 0) {
- printk(KERN_ERR "%s: device_register failed\n", __func__);
- goto cleanup;
+ mutex_unlock(&videodev_lock);
+ pr_err("%s: device_register failed\n", __func__);
+ put_device(&vdev->dev);
+ return ret;
}
- /* Register the release callback that will be called when the last
- reference to the device goes away. */
- vdev->dev.release = v4l2_device_release;
if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
- printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
+ pr_warn("%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 &&
- vdev->vfl_type != VFL_TYPE_SUBDEV) {
- vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
- vdev->entity.name = vdev->name;
- vdev->entity.info.v4l.major = VIDEO_MAJOR;
- vdev->entity.info.v4l.minor = vdev->minor;
- ret = media_device_register_entity(vdev->v4l2_dev->mdev,
- &vdev->entity);
- if (ret < 0)
- printk(KERN_WARNING
- "%s: media_device_register_entity failed\n",
- __func__);
- }
-#endif
+ ret = video_register_media_controller(vdev);
+
/* Part 6: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
- mutex_lock(&videodev_lock);
- video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
return 0;
@@ -940,6 +1098,7 @@ cleanup:
mutex_lock(&videodev_lock);
if (vdev->cdev)
cdev_del(vdev->cdev);
+ video_devices[vdev->minor] = NULL;
devnode_clear(vdev);
mutex_unlock(&videodev_lock);
/* Mark this video device as never having been registered. */
@@ -967,10 +1126,93 @@ void video_unregister_device(struct video_device *vdev)
*/
clear_bit(V4L2_FL_REGISTERED, &vdev->flags);
mutex_unlock(&videodev_lock);
+ v4l2_event_wake_all(vdev);
device_unregister(&vdev->dev);
}
EXPORT_SYMBOL(video_unregister_device);
+#ifdef CONFIG_DEBUG_FS
+struct dentry *v4l2_debugfs_root(void)
+{
+ if (!v4l2_debugfs_root_dir)
+ v4l2_debugfs_root_dir = debugfs_create_dir("v4l2", NULL);
+ return v4l2_debugfs_root_dir;
+}
+EXPORT_SYMBOL_GPL(v4l2_debugfs_root);
+#endif
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+
+__must_check int video_device_pipeline_start(struct video_device *vdev,
+ struct media_pipeline *pipe)
+{
+ struct media_entity *entity = &vdev->entity;
+
+ if (entity->num_pads != 1)
+ return -ENODEV;
+
+ return media_pipeline_start(&entity->pads[0], pipe);
+}
+EXPORT_SYMBOL_GPL(video_device_pipeline_start);
+
+__must_check int __video_device_pipeline_start(struct video_device *vdev,
+ struct media_pipeline *pipe)
+{
+ struct media_entity *entity = &vdev->entity;
+
+ if (entity->num_pads != 1)
+ return -ENODEV;
+
+ return __media_pipeline_start(&entity->pads[0], pipe);
+}
+EXPORT_SYMBOL_GPL(__video_device_pipeline_start);
+
+void video_device_pipeline_stop(struct video_device *vdev)
+{
+ struct media_entity *entity = &vdev->entity;
+
+ if (WARN_ON(entity->num_pads != 1))
+ return;
+
+ return media_pipeline_stop(&entity->pads[0]);
+}
+EXPORT_SYMBOL_GPL(video_device_pipeline_stop);
+
+void __video_device_pipeline_stop(struct video_device *vdev)
+{
+ struct media_entity *entity = &vdev->entity;
+
+ if (WARN_ON(entity->num_pads != 1))
+ return;
+
+ return __media_pipeline_stop(&entity->pads[0]);
+}
+EXPORT_SYMBOL_GPL(__video_device_pipeline_stop);
+
+__must_check int video_device_pipeline_alloc_start(struct video_device *vdev)
+{
+ struct media_entity *entity = &vdev->entity;
+
+ if (entity->num_pads != 1)
+ return -ENODEV;
+
+ return media_pipeline_alloc_start(&entity->pads[0]);
+}
+EXPORT_SYMBOL_GPL(video_device_pipeline_alloc_start);
+
+struct media_pipeline *video_device_pipeline(struct video_device *vdev)
+{
+ struct media_entity *entity = &vdev->entity;
+
+ if (WARN_ON(entity->num_pads != 1))
+ return NULL;
+
+ return media_pad_pipeline(&entity->pads[0]);
+}
+EXPORT_SYMBOL_GPL(video_device_pipeline);
+
+#endif /* CONFIG_MEDIA_CONTROLLER */
+
/*
* Initialise video for linux
*/
@@ -979,10 +1221,10 @@ static int __init videodev_init(void)
dev_t dev = MKDEV(VIDEO_MAJOR, 0);
int ret;
- printk(KERN_INFO "Linux video capture interface: v2.00\n");
+ pr_info("Linux video capture interface: v2.00\n");
ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
if (ret < 0) {
- printk(KERN_WARNING "videodev: unable to get major %d\n",
+ pr_warn("videodev: unable to get major %d\n",
VIDEO_MAJOR);
return ret;
}
@@ -990,7 +1232,7 @@ static int __init videodev_init(void)
ret = class_register(&video_class);
if (ret < 0) {
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
- printk(KERN_WARNING "video_dev: class_register failed\n");
+ pr_warn("video_dev: class_register failed\n");
return -EIO;
}
@@ -1003,19 +1245,14 @@ static void __exit videodev_exit(void)
class_unregister(&video_class);
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
+ debugfs_remove_recursive(v4l2_debugfs_root_dir);
+ v4l2_debugfs_root_dir = NULL;
}
subsys_initcall(videodev_init);
module_exit(videodev_exit)
-MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2");
+MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@kernel.org>, Bill Dirks, Justin Schoeman, Gerd Knorr");
+MODULE_DESCRIPTION("Video4Linux2 core driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR);
-
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */