vidioc_querycap浅析

源码欣赏

在前几篇博文中我们知道:
用户层调用ioctl(),经过v4l2_ioctl —->video_ioctl2——>__video_do_ioctl()。
__video_do_ioctl这个函数会根据cmd的不同来调用struct v4l2_ioctl_ops结构中定义的不同函数。

    我们现在来讨论cmd为VIDIOC_QUERYCAP命令的过程。
static long __video_do_ioctl(struct file *file,unsigned int cmd, void *arg)
{   
    /*
        根据次设备号为索引值从video_device[]中获取对应的video_device。
        1 : 那么 是在什么时候 填充上的呢?

        这一点,像极了fb_info、fb_info[].套路都是一样的,都是在注册的时候添加到对应的数组中的.然后再其他函数中 在从该数组中 取出来.

        详细对比 请见我以前博文的分析 
                         http://blog.csdn.net/leesagacious/article/details/49851555

        static inline int __must_check video_register_device(struct video_device *vdev,
        int type, int nr)
        {
            return __video_register_device(vdev, type, nr, 1, vdev->fops->owner);
                   {
                       ....
                       for (i = 0; i < VIDEO_NUM_DEVICES; i++)
                            if (video_device[i] == NULL)
                                break;
                       ....
                       vdev->minor = i + minor_offset;
                       ....
                       mutex_lock(&videodev_lock);
                       video_device[vdev->minor] = vdev;
                       mutex_unlock(&videodev_lock);
                   }    
        }
    */
    struct video_device *vfd = video_devdata(file);
    /**
        根据上面获取的vfd,获取v4l2_ioctl_ops
        下面的代码会根据cmd的不同来调用这个v4l2_ioctl_ops结构中定义的函数
    */
    const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
    /**
        从打开文件的私有数据中获取到vivi_fh
        那么,vivi_fh是在什么时候被赋值给file->private_data的呢?
        这个赋值是发生在具体设备驱动的open函数中的,如vivi_open()
        static int vivi_open(struct file *file)
        {
            struct vivi_fh *fh = NULL;
            fh = kzalloc(sizeof(*fh), GFP_KERNEL); 这里其实已经为videobuf_queue分配好了空间
            ....
            file->private_data = fh; //将vivi_fh赋值给file->private_data
            ....
        }   
    */
    void *fh = file->private_data;
    .....
    /**
        查看这个video_device是否提供了v4l2_ioctl_ops,否则出错返回
    */
    if (ops == NULL) {
        printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n",
                vfd->name);
        return ret;
    }
    ....

    switch(cmd) //根据cmd来调用不同的函数
    {
        case VIDIOC_QUERYCAP:
            struct v4l2_capability *cap = (struct v4l2_capability *)arg;
            //如果具体设备的file_operations并没有定义vidioc_querycap这个函数
            if (!ops->vidioc_querycap)
            {
                break;
            }
            /**
            调用vidioc_querycap
            参数 : 
                fh : vivi_fh  在vivi_open()中分配,并放入file->private_data
                cap  : 用户空间传入的struct v4l2_capability 指针
            */
            ret = ops->vidioc_querycap(file, fh, cap);
    }
}
下面看看被调用的vidioc_querycap()函数
/**
查询设备的性能。
参数  :   
            v4l2_capability : v4l2的能力描述符
返回值:
            成功 返回 0 。cap变量中包含了该视频设备所支持的能力信息 
                          如 : V4L2_CAP_VIDEO_CAPTURE 、V4L2_CAP_STREAMING....        
            失败 返回 -1

    应用程序往往打开设备后就执行这个ioctl   

用户空间:
    用户层调用 ioctl(fd,VIDIOC_QUERYCAP,CAP),这个函数会被执行。
        当执行完成这个命令后,CAP变量中包含了该设备的能力信息。
    代码:
        #define ERR_EXIT(m)\
                do{
                    perror(m);
                    exit(EXIT_FAILURE);\
                }while(0)

        struct v4l2_capability cap;
        ret = ioctl(fd,VIDIOC_QUERYCAP,&cap);
        if(ret  == -1)
        {
            ERR_EXIT(ioctl);
        }   
*/
/*
    应用程序用这个命令,用来判断它是否是一个视频捕获设备
*/
static int vidioc_querycap(struct file * file,void *priv,struct v4l2_capability *cap)
{
    /**
        先来看看这个v4l2_capability
        struct v4l2_capability {
            __u8    driver[16];     //驱动名字
            __u8    card[32];       //设备名字
            __u8    bus_info[32];   //设备在系统中的位置 Location of the device in the system
            __u32   version;        //驱动的版本号
            __u32   capabilities;   //设备能力集
            __u32   reserved[4];    //保留字段,驱动必须要将这个数组设置为0
        };
    */
    struct vivi_fh *fh = priv;

    //从vivi_fh中获取vivi_dev
    struct vivi_dev *dev = fh->dev;

    strcpy(cap->driver,"vivi");   //驱动的名字
    strcpy(cap->card,"vivi");     //设备的名字
    //设备在系统中的位置 
    strlcpy(cap->bus_info,dev->v4l2_dev.name,sizeof(cap->bus_info));
    cap->version = VIVI_VERSION; //版本号

    //能力集合
    //V4L2_CAP_VIDEO_CAPTURE   : 是一个视频捕捉设备
    //V4L2_CAP_STREAMING       :支持ioctl系统调用来获取数据
    //V4L2_CAP_READWRITE       :支持read/write系统调用来获取数据   
    cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;

    return 0;
}

vidioc_querycap浅析_第1张图片

下面是 源码中调用的过程。

    这个函数 就是 要调用 vdev->fops->unlocked_iotcl().
    uvc : 
        uvc_init()
        {
            ret = usb_register(&uvc_driver.driver);
        }

        struct uvc_driver uvc_driver = {
            .driver = {
                .name       = "uvcvideo",
                .probe      = uvc_probe,
                .disconnect = uvc_disconnect,
                .suspend    = uvc_suspend,
                .resume     = uvc_resume,
                .reset_resume   = uvc_reset_resume,
                .id_table   = uvc_ids,
                .supports_autosuspend = 1,
            },
        };
        //probe被调用
        static int uvc_probe(struct usb_interface *intf,const struct usb_device_id *id)
        {
            if (uvc_register_chains(dev) < 0){
                    goto error;
            }
        }

        static int uvc_register_chains(struct uvc_device *dev)
        {
                list_for_each_entry(chain, &dev->chains, list) {
                    ret = uvc_register_terms(dev, chain);
                    if (ret < 0){
                        return ret;
                    }
                }   
        }

        static int uvc_register_terms(struct uvc_device *dev,struct uvc_video_chain *chain)
        {
            list_for_each_entry(term, &chain->entities, chain) {
                ret = uvc_register_video(dev, stream);
            }
        }

        static int uvc_register_video(struct uvc_device *dev,struct uvc_streaming *stream)
        {
            ...
            vdev = video_device_alloc();
            vdev->v4l2_dev = &dev->vdev;
            vdev->fops = &uvc_fops;           //看 这个uvc_fops中定义的函数会被调用到
            vdev->release = uvc_release;
            strlcpy(vdev->name, dev->name, sizeof vdev->name);

            ..
            ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
        }

        const struct v4l2_file_operations uvc_fops = {
                .owner      = THIS_MODULE,
                .open       = uvc_v4l2_open,
                .release    = uvc_v4l2_release,
                .unlocked_ioctl = uvc_v4l2_ioctl,   //这个函数。
            #ifdef CONFIG_COMPAT
                .compat_ioctl32 = uvc_v4l2_compat_ioctl32,
            #endif
                .read       = uvc_v4l2_read,
                .mmap       = uvc_v4l2_mmap,
                .poll       = uvc_v4l2_poll,
            #ifndef CONFIG_MMU
                .get_unmapped_area = uvc_v4l2_get_unmapped_area,
            #endif
        };

        那么,这个函数终于被调用到了
        static long uvc_v4l2_ioctl(struct file *file,unsigned int cmd, unsigned long arg)
        {
                if (uvc_trace_param & UVC_TRACE_IOCTL) {
                    uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl(");
                    v4l_printk_ioctl(cmd);
                    printk(")\n");
                }
                                                 //这个函数会被调用         
                return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
        }

        video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,v4l2_kioctl func)
        {
                ...
                ...
                err = func(file, cmd, parg);
                ...
        }

        static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
        {
                struct video_device *vdev = video_devdata(file);
                ...
                switch (cmd) {
                    case VIDIOC_QUERYCAP:
                    {
                            struct v4l2_capability *cap = arg;
                            /**
                                填充这个结构体
                            */
                            memset(cap, 0, sizeof *cap);

                            strlcpy(cap->driver, "uvcvideo", sizeof cap->driver);

                            strlcpy(cap->card, vdev->name, sizeof cap->card);

                            usb_make_path(stream->dev->udev,cap->bus_info, sizeof(cap->bus_info));

                            cap->version = LINUX_VERSION_CODE;

                            if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE){
                                cap->capabilities = V4L2_CAP_VIDEO_CAPTURE| V4L2_CAP_STREAMING;
                            }
                            else{
                                cap->capabilities = V4L2_CAP_VIDEO_OUTPUT| V4L2_CAP_STREAMING;
                            }
                            break;
                    }
                    case ...
                    case ...    
                }
        }

实验验证

vidioc_querycap浅析_第2张图片

你可能感兴趣的:(linux驱动)