• (二) V4L2引入(含浅析UVC)



    title: V4L2引入(含浅析UVC)
    date: 2019/4/23 19:00:00
    toc: true

    V4L2引入(含浅析UVC)

    基本框架

    • V4L2全名是video for linux 2之前还有个老版本v4l,也就是video for linux 1.0版本

    • V4L2不仅仅用于摄像头,也用于视频输出接口,收音机接口等,完整的框架可以参考这里

    基本框架图如下:摘录自 Linux摄像头驱动1——vivid

    mark

    代码入手

    我们插入USB,使用dmesg查看usb的输出信息

    uvcvideo: Found UVC 1.00 device USB 2.0 Camera (05a3:9310)
    

    搜索内核源码可以找到相关函数

    cd linux-3.4.2/
    cd drivers/
    $ grep "Found UVC" *  -nR
    media/video/uvc/uvc_driver.c:1848:      uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s 
    

    这里就涉及到UVC这个名词了,所谓的UVC也就是usb video class类的驱动,也就是USB硬件相关的驱动,也就是应该会向上注册这个驱动程序

    UVC流程简述

    这里简单分析下UVC驱动的流程,详细分析到USB摄像头那里,对于UVC驱动也是一个驱动,从入口函数分析下

    static int __init uvc_init(void)
    {
    	int ret;
    
    	uvc_debugfs_init();
    
    	ret = usb_register(&uvc_driver.driver);
    	if (ret < 0) {
    		uvc_debugfs_cleanup();
    		return ret;
    	}
    
    	printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")
    ");
    	return 0;
    }
    

    注册了一个结构体,看下这个结构体的成员

    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,
    	},
    };
    

    按照以往的经验,应该就是先匹配id_table,然后执行probe来初始化,查看下这个probe,深入分析最后是调用cdev注册了一个 v4l2_fopsfile_operation,也就是说最终app的操作函数就是这个啦.

    v4l2_fops > 具体的驱动

    简单流程一览,抓住关键注册函数是uvc_register_chains,而v4l2_device_register并不重要在整体上

    // driversmediavideouvcuvc_driver.c
    uvc_probe
    	v4l2_device_register  //这个不重要,只是进行一些初始化
    	..
    	uvc_register_chains  //这里才是实际的注册v4l2管理结构
    		>uvc_register_terms
    			>uvc_register_video(dev, stream)
    				>video_device_alloc						//分配
        			>vdev->v4l2_dev = &dev->vdev;
    ----			>vdev->fops = &uvc_fops;		//这个是最终的读写函数
    				>vdev->release = uvc_release;
    				>video_set_drvdata(vdev, stream);		// 设置
    				>video_register_device(vdev, VFL_TYPE_GRABBER, -1) //注册
    					// includemediav4l2-dev.h
    					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);
    					}
    					
    ----这个实际的		__video_register_device 注册函数是在  driversmediavideov4l2-dev.c	也就是它为下层提供注册接口的	
    __video_register_device
    	>case VFL_TYPE_GRABBER:
    			name_base = "video";
    	> 获得一个空的次设备号	
    		for (i = 0; i < VIDEO_NUM_DEVICES; i++)
    			if (video_device[i] == NULL) break;
    	> 这里就有字符设备驱动的了
    	>vdev->cdev = cdev_alloc();
    	>vdev->cdev->ops = &v4l2_fops;  //这个也就是具体的 /dev/video的fileoperation
    	>vdev->cdev->owner = owner;
    	>ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
    	
    

    调用流程

    appopen,read,write 最终就会调用到vdev->cdev->ops=v4l2_fops

    // driversmediavideov4l2-dev.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,
    };
    

    open

    vdev = video_devdata(filp);  //return video_device[iminor(file->f_path.dentry->d_inode)];
    	>vdev->fops->open(filp)
    

    read

    这里调用的最终就是vdev->fops->read

    video_device *vdev = video_devdata(filp);
    					> return video_device[iminor(file->f_path.dentry->d_inode)];
    vdev->fops->read(filp, buf, sz, off)
    

    这个是在uvc_register_video中设置的

    vdev->fops = &uvc_fops;
    
    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
    };
    

    v4l2_ioctl

    也就是调用了`uvc_v4l2_ioctl>uvc_v4l2_do_ioctl

    struct video_device *vdev = video_devdata(filp);
    vdev->fops->unlocked_ioctl(filp, cmd, arg)
    

    videodev_init(字符设备驱动注册)

    这个是v4l2-dev.c的入口,这里就是常规的字符设备驱动,这里使用了主设备号81

    static int __init videodev_init(void)
    {
    	dev_t dev = MKDEV(VIDEO_MAJOR, 0);
    	int ret;
    
    	printk(KERN_INFO "Linux video capture interface: v2.00
    ");
    	ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
    	if (ret < 0) {
    		printk(KERN_WARNING "videodev: unable to get major %d
    ",
    				VIDEO_MAJOR);
    		return ret;
    	}
    
    	ret = class_register(&video_class);
    	if (ret < 0) {
    		unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
    		printk(KERN_WARNING "video_dev: class_register failed
    ");
    		return -EIO;
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    UI: 操作导航控制器的视图控制器数组
    UI: 使用 UIViewController 展现和管理视图
    UI: 使用 UINavigationController 实现导航
    UI: 使用 UIActivityViewController 显示分享选项
    UI: 使用 UIActivityViewController 显示分享选项
    UI: 自定义 UISegmentedControl
    UI: UISegmentedControl 做简单选项分组及其自定义
    UI: UISlider 实现指定范围值的选择
    隧道目标识别实验过程(二)标准VOC数据集和制作后的数据集实验结果对比
    隧道目标识别实验过程(一)数据集的制作
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10764119.html
Copyright © 2020-2023  润新知