在3.1节分析的probe函数中,它的核心函数video_register_device的part3中(v4l2-core/v4l2-dev.c文件),通过vdev->cdev->ops=
&v4l2_fops;将字符设备的结构体cdev的file_operations函数集指向了v4l2_fops,如下所示:
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,
};
以open函数和ioctl函数为例来分析这个底层的调用过程:
当应用程序调用open函数的时候,就会调用这个v4l2_open函数,如下所示:
static int v4l2_open(struct inode *inode, struct file *filp)
{
struct video_device *vdev;
int ret = 0;
/* Check if the video device is available */
mutex_lock(&videodev_lock);
vdev = video_devdata(filp);
/* return ENODEV if the video device has already been removed. */
if (vdev == NULL || !video_is_registered(vdev)) {
mutex_unlock(&videodev_lock);
return -ENODEV;
}
/* 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 (vdev->debug)
printk(KERN_DEBUG "%s: open (%d)
",
video_device_node_name(vdev), ret);
/* decrease the refcount in case of an error */
if (ret)
video_put(vdev);
return ret;
}
看这个函数,它经过一系列的判断和操作,最终通过我标红那一句,调用到video_device结构体里面fops函数集的open函数。(以上这些分析是v4l2-dev.c中的)
体现在mxc_v4l2_capture.c中就是:在init_camera_struct函数中,通过*(cam->video_dev)=
mxc_v4l_template;这一句话,将video_device指向了mxc_v4l_template,所以应用程序调用open函数,最终就会依次调用mxc_v4l_template->fops->open,即
mxc_v4l_template--->mxc_v4l_fops--->mxc_v4l_open。
上面分析的是应用程序如果调用open函数的话,底层函数的执行流程,最终就会执行到我们mxc_v4l2_capture.c中的mxc_v4l_open函数。
同样对于ioctl函数来说,比如应用程序调用了一个ioctl函数,作为一个字符设备,肯定会调用到v4l2_fops结构体中的v4l2_ioctl函数,ret=
vdev->fops->ioctl(filp, cmd, arg);
最终在mxc_v4l2_capture.c中会依次调用mxc_v4l_template--->mxc_v4l_fops--->mxc_v4l_ioctl。
mxc_v4l_ioctl函数如下所示:
static long mxc_v4l_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
pr_debug("In MVC:mxc_v4l_ioctl
");
return video_usercopy(file, cmd, arg, mxc_v4l_do_ioctl);
}
它通过video_usercopy函数,这个video_usercopy函数会进行一些检查及错误分析,最终会调用到mxc_v4l_do_ioctl函数,在mxc_v4l_do_ioctl函数中会根据不同的宏,通过一个switch语句来分别执行不同的过程。