v4l2中ioctl的调用流程

leesagacious 原创,欢迎转载
这里写图片描述
这里写图片描述

vivi入口函数: vivi_init( )
vivi_init(void)

    static int __init vivi_init(void)
    {
        .....
        for(i = 0;i < n_devs;i++){ //这里的n_devs的值是 1 
            //调用了这个函数,来创建设备
          ret = vivi_create_instance(i); 
        }
        ....
    }
vivi_create_instance(int inst)
    static int __init vivi_create_instance(int inst)
    {
        struct video_device * vfd;//这是一个核心的结构,对应视频设备节点
        ......
        //动态分配了一个video_device,用kzalloc实现
        vfd  = video_device_alloc();
        if(!vfd){
            goto unreg_dev;
        }
        /**这里vfd被设置为vivi_template了,
           在后面的代码中会把这个vfd根据次设备号为索引值放入到video_device[]中,
           在其他函数依据次设备号从这个数组中获取vide_device,获取的就是vivi_template    
        */
        *vfd = vivi_templaate;
        /**注册video_device,
            VFL_TYPE_GRABBER:下面的代码会根据传入的这个类型,使用switch语句 来选择设备的名字和次设备号(i + minor_offset)
            node_nr 值为-1,表示第一个可用的数字,比如/dev/video0 /dev/video1
            那么接下来的这个设备就是video2了,下面详细讲解
        */
        ret = video_register_device(vfd,VFL_TYPE_GRABBER,node_nr);
    }
注册video_device :video_register_device()
int video_register_device(struct video_device *vdev, int type, int nr)
{
    //调用这个函数来注册video_device
    return __video_register_device(vdev, type, nr, 1);
}
static int __video_register_device()
{
    ....
    vdev->minor = -1; //次设备号被设置为-1,表示这个video_device还没有被注册过
    WARN_ON(!vdev->release){
        if(!vdev->release){  //如果要注册的video_device没有提供release函数,就会报错。
            return -EINVAL;
        }
    }
    //根据传入的类型,来选择设备的名字,传入的类型是VFL_TYPE_GRABBER
    switch (type) {
        case VFL_TYPE_GRABBER:
            name_base = "video";
            break;
        case VFL_TYPE_VTX:
            name_base = "vtx";
            break;
        case VFL_TYPE_VBI:
            name_base = "vbi";
            break;
        case VFL_TYPE_RADIO:
            name_base = "radio";
            break;
        default:
            printk(KERN_ERR "%s called with unknown type: %d\n",
                   __func__, type);
            return -EINVAL;
    }
    .....
    vdev->cdev = cdev_alloc(); //动态分配cdev,cdev一般被嵌入一个风大的结构体中,用来代表字符设备的通用信息
    if(vdev->cdev == NULL){
      return -ENOMEM;
      goto cleanup;
    }
    /**
     cdev的file_operations被设置为了v4l2_fops
     应用程序调用open、read、ioctl....系统调用时,v4l2_fops中对应的函数会被调用,对应的函数又会去调用具体设备提供的函数
    */
    vdev->cdev->ops = &v4l2_fops; 
    vdev->cdev->owner = owner;
    ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);//添加到内核
    .....
    //任何一个设备的注册都要经过这个函数,注册过程请见另一篇博文
    ret = device_register(&vdev->dev); //注册这个设备
    ....
    //获取互斥锁----访问临界区-----释放互斥锁
    mutex_lock(&videodev_lock);   
    /**
        依据次设备号位下标,将video_device(这里就是vivi_template)放入到video_device[]中,
        static struct video_device *video_device[256];
        以后在其他函数中依据次设备号从这个数组中获取的都是vivi_tempalte
    */
    video_device[vdev->minor] = vdev;
    mutex_unlock(&videodev_lock); //释放互斥锁
}
vivi_template

根据上面的分析,vivi_template在入口函数中就已经被放入到了video_device[]中了,其他函数依据其次设备号为索引值从video_device[]中获取到的就是vivi_template,
看看这个结构体张什么样

    static struct video_device vivi_template = {
    .name       = "vivi",
    .fops           = &vivi_fops, //file_operations,为vfs提供调用接口
    //查询设备性能、设置、获取、列:举数据格式、制式、缓冲区、启动摄像头.....涉及到整个数据的获取过程
    .ioctl_ops  = &vivi_ioctl_ops,
    .release    = video_device_release,
    //用于一些制式的操作。对于摄像头驱动,不是必需的
    .tvnorms              = V4L2_STD_525_60,
    .current_norm         = V4L2_STD_NTSC_M,
};
通用的file_operations : v4l2_fops
/**
    一下定义的这些函数,对应与应用程序调用open、ioclt系统调用
    看我们要讨论的unlocked_ioctl ,它对应与应用程序中的ioctl()
*/
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,
};
v4l2_ioctl

当应用程序调用ioctl的时候,v4l2_fops 结构中定义的v4l2_ioctl函数会被调用

    static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
        /**根据次设备号位下标,从video_device[]数组中取出video_device,
           这里取出来的是vivi_tempalte
        */ 
        struct video_device *vdev = video_devdata(filp);
        /**
            如果vivi_template的v4l2_file_operations中定义了unlocked_ioctl
            就调用这个unlocked_ioctl
        */
        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);
    }
vivi_template 中的fops
   static const struct v4l2_file_operations vivi_fops = {
    .owner      = THIS_MODULE,
    .open           = v4l2_fh_open,
    .release        = vivi_close,
    .read           = vivi_read,
    .poll       = vivi_poll,
    /**
        经过上面的分析,当应用程序调用ioctl的时候
        这个函数会被调用
    */
    .unlocked_ioctl = video_ioctl2, 
    .mmap           = vivi_mmap,
};
video_ioctl2
long video_ioctl2(struct file *file,unsigned int cmd, unsigned long arg)
{   //根据命令cmd的不同,调用__video_do_ioctl,将数据拷贝到内核空间
    return video_usercopy(file, cmd, arg, __video_do_ioctl);
}
__video_do_ioctl
static long __video_do_ioctl(struct file *file,unsigned int cmd, void *arg)
{   //依据次设备号位下标,从video_device[]数组中取出来vivi_tempalte
    struct video_device *vfd = video_devdata(file);
    //获取vivi_tempalte的ioctl_ops
    const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
    ......
    case VIDIOC_QUERYCAP: //cmd命令
    {
        //v4l2设备的能力结构体
        struct v4l2_capability *cap = (struct v4l2_capability *)arg;
        if (!ops->vidioc_querycap)
            break;
        cap->version = LINUX_VERSION_CODE;
        /**
            看这里,根据不同的cmd,这里调用到了ioctl_ops中定义的vidioc_querycap
        */
        ret = ops->vidioc_querycap(file, fh, cap);
        ...
        break;
    }
}

在入口函数vivi_init()中,
1 : video_device被设置成了vivi_template
2 :vdev->cdev->ops = &v4l2_fops;
3 : video_device[vdev->minor] = vdev;
当用户空间调用ioctl的时候:
1 : v4l2_fops中的v4l2_ioctl会被调用

在v4l2_ioctl()中
vivi_template->fops->unlocked_ioctl(filp, cmd, arg);
即 video_ioctl2( )函数会被调用

在video_ioctl2( )中,__
video_do_ioctl( )函数会被调用

在video_do_ioctl()函数中,
根据cmd来调用vivi_template中vivi_ioctl_ops结构体其中对应的函数

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值