zoukankan      html  css  js  c++  java
  • (二) 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;
    }
    
  • 相关阅读:
    【java】对象赋值给另一个对象
    spring boot系列(五)spring boot 配置spring data jpa (查询方法)
    Spring Data JPA 查询
    Spring Data JPA 介绍
    OpenID简介
    OAUTH协议介绍
    URL encoding(URL编码)
    RESTful 介绍
    spring boot系列(四)spring boot 配置spring data jpa (保存修改删除方法)
    spring boot 启动报 java.lang.NoClassDefFoundError: ch/qos/logback/core/spi/LifeCycle 错误
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10764119.html
Copyright © 2011-2022 走看看