11月19日 ========== V4L2 Ioctl调用流程 ++++++++++++++++++++++ 上次培训时讲到,V4L2的功能大部分都是通过ioctl导出给用户使用的。那么今天就看看这些ioctl是如何对应到驱动中的代码的。 下面以VIDIOC_QUERYCAP为例讲解。 .. code-block:: c 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的实现。 .. code-block:: c 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驱动中此函数是如何实现的: .. code-block:: c :emphasize-lines: 5 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的代码: .. code-block:: c 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 : .. code-block:: c 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.