11月19日

V4L2 Ioctl调用流程

上次培训时讲到,V4L2的功能大部分都是通过ioctl导出给用户使用的。那么今天就看看这些ioctl是如何对应到驱动中的代码的。

下面以VIDIOC_QUERYCAP为例讲解。

static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.get_unmapped_area = v4l2_get_unmapped_area,
.mmap = v4l2_mmap,
.unlocked_ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l2_compat_ioctl32,
#endif
.release = v4l2_release,
.poll = v4l2_poll,
.llseek = no_llseek,
};

vdev->cdev->ops = &v4l2_fops;
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__);
    kfree(vdev->cdev);
    vdev->cdev = NULL;
    goto cleanup;
}

从上面的代码可以看出来,v4l2在注册字符设备时,将该字符设备的file_operations赋值为v4l2_fops。下面看看v4l2_ioctl的实现。

static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct video_device *vdev = video_devdata(filp);
int ret = -ENODEV;

if (vdev->fops->unlocked_ioctl) {
    if (vdev->lock && mutex_lock_interruptible(vdev->lock))
        return -ERESTARTSYS;
    if (video_is_registered(vdev))
        ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
    if (vdev->lock)
        mutex_unlock(vdev->lock);
} else if (vdev->fops->ioctl) {
    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;

return ret;
}

可以看到v4l2_ioctl的处理分为两种情况:unlocked_ioctl和正常的ioctl(具体区别后面在仔细讲)。不论哪种情况,都会调用vdev->fops->ioctl函数。看一下soc_camera驱动中此函数是如何实现的:

static struct v4l2_file_operations soc_camera_fops = {
.owner              = THIS_MODULE,
.open               = soc_camera_open,
.release    = soc_camera_close,
.unlocked_ioctl     = video_ioctl2,
.read               = soc_camera_read,
.mmap               = soc_camera_mmap,
.poll               = soc_camera_poll,
};

一般情况下,unlocked_ioctl都是指向video_ioctl2函数的。那么我们接下来看看video_ioctl2的代码:

long video_ioctl2(struct file *file,
    unsigned int cmd, unsigned long arg)
{
    return video_usercopy(file, cmd, arg, __video_do_ioctl);
}

video_ioctl2函数接着调用video_usercopy函数。video_usercopy函数会对用户传递过来的参数检查,将用户空间的数据拷贝到内核空间,调用我们指定的__video_do_ioctl函数,并将内核空间的数据拷贝到用户空间。

接下来终于到分析关键函数了,__video_do_ioctl :

static long __video_do_ioctl(struct file *file,
    unsigned int cmd, void *arg)
{
    struct video_device *vfd = video_devdata(file);
    const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
    void *fh = file->private_data;
    struct v4l2_fh *vfh = NULL;
    int use_fh_prio = 0;
    long ret_prio = 0;
    long ret = -ENOTTY;

    if (ops == NULL) {
        printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n",
                vfd->name);
        return ret;
    }

    if ((vfd->debug & V4L2_DEBUG_IOCTL) &&
                !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) {
        v4l_print_ioctl(vfd->name, cmd);
        printk(KERN_CONT "\n");
    }

    if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
        vfh = file->private_data;
        use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
    }

    if (use_fh_prio)
        ret_prio = v4l2_prio_check(vfd->prio, vfh->prio);

    switch (cmd) {

    /* --- capabilities ------------------------------------------ */
    case VIDIOC_QUERYCAP:
    {
        struct v4l2_capability *cap = (struct v4l2_capability *)arg;

        if (!ops->vidioc_querycap)
            break;

        cap->version = LINUX_VERSION_CODE;
        ret = ops->vidioc_querycap(file, fh, cap);
        if (!ret)
            dbgarg(cmd, "driver=%s, card=%s, bus=%s, "
                    "version=0x%08x, "
                    "capabilities=0x%08x, "
                    "device_caps=0x%08x\n",
                    cap->driver, cap->card, cap->bus_info,
                    cap->version,
                    cap->capabilities,
                    cap->device_caps);
        break;
        }
    ......
    return ret;
}

由于代码太长,只节选了VIDIOC_QUERYCAP的代码。__video_do_ioctl做完参数检查后,就是判断cmd的值并作相应处理。基本上所有的case的操作都一样:判断ops结构体中所对应的函数是否有效,若有效则使用用户指定的参数调用,否则返回-ENOTTY.

Table Of Contents

Previous topic

11月18日

Next topic

11月20日

This Page