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

代码入手

我们插入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 ")\n");
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并不重要在整体上

// drivers\media\video\uvc\uvc_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) //注册
// \include\media\v4l2-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 注册函数是在 drivers\media\video\v4l2-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

// \drivers\media\video\v4l2-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\n");
ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
if (ret < 0) {
printk(KERN_WARNING "videodev: unable to get major %d\n",
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\n");
return -EIO;
} return 0;
}

(二) V4L2引入(含浅析UVC)的更多相关文章

  1. Android 手势识别类 ( 二 ) GestureDetector 源码浅析

    前言:Android 关于手势的操作提供两种形式:一种是针对用户手指在屏幕上划出的动作而进行移动的检测,这些手势的检测通过android提供的监听器来实现:另一种是用 户手指在屏幕上滑动而形成一定的不 ...

  2. 实战build-react(二)-------引入Ant Design(增加)

    https://blog.csdn.net/zhan_lijian/article/details/85271906(copy) 1.肯定参考facebook关于react官网咯 快速搭建 creat ...

  3. Vue管理系统前端系列二相关工具引入及封装

    目录 sass-loader/vuex 等的引入说明 引入 element 引入 axios 1.基本使用 2.封装使用 2.1 开发环境配置请求地址 2.2 配置代理 2.3 添加接口相关文件 sa ...

  4. 自己实现简单的AOP(二)引入Attribute 为方法指定增强对象

    话续前文 : 自己实现简单的AOP(一)简介 在前一篇文章中,对AOP的实现方式做了一个简单介绍.接下来,引入Attribute 为方法指定增强对象,由此实现一个简单的AOP. 注意:指定的是增强对象 ...

  5. Multimodal —— 看图说话(Image Caption)任务的论文笔记(二)引入attention机制

    在上一篇博客中介绍的论文"Show and tell"所提出的NIC模型采用的是最"简单"的encoder-decoder框架,模型上没有什么新花样,使用CNN ...

  6. 实战build-react(二)-------引入Ant Design

    安装 Ant Design  npm install antd --save 或 yarn add antd 注释:https://www.jianshu.com/p/21caf40ee93e(cop ...

  7. iOS-启动项目(二)引入第三方库

    摘要 项目中很大几率会用到第三方库,通过 Pod 方式引入第三方库是效率很高的方式,这里介绍一个新的项目搭建 Pod 方式的环境,方便项目中引入第三方库文件. 刚创建的项目中如果需要用到第三方库,常用 ...

  8. 【转】Android下编译jni库的二种方法(含示例)

    原文网址:http://blog.sina.com.cn/s/blog_3e3fcadd01011384.html 总结如下:两种方法是:1)使用Android源码中的Make系统2)使用NDK(从N ...

  9. 【转】Android下编译jni库的二种方法(含示例) -- 不错

    原文网址:http://blog.sina.com.cn/s/blog_3e3fcadd01011384.html 总结如下:两种方法是:1)使用Android源码中的Make系统2)使用NDK(从N ...

随机推荐

  1. SQLServer删除登录帐户

    删除登陆账户注意事项 不能删除正在登录的登录名. 也不能删除拥有任何安全对象.服务器级对象或 SQL Server 代理作业的登录名. 可以删除数据库用户映射到的登录名,但是这会创建孤立用户. 有关详 ...

  2. Excel自定义公式,类似VLOOKUP的查询

    Excel在使用VLOOKUP时,当检索值超过255长度的时候就会报错,没法正常检索. 官方提供的办法是通过INDEX和MATCH公式组合使用来解决. 微软官方方案 1,公式 =INDEX($A$5: ...

  3. RocketMQ4.3.x对顺序消息的理解

    1.RocketMQ消息队列简单介绍 这里简单介绍一下RocketMQ的消息队列的模型 一个topic对应多个队列如下图: 生产者和消费者分别向队列中发送和消费消息,生产者和消费者都可以是多个,通过组 ...

  4. JavaScript中编码函数escape,encodeURI,encodeURIComponent

    第一:escape():对字符串进行编码,escape()不编码的字符:@*/+ 第二:encodeURI() 函数可把字符串作为 URI 进行编码.不会进行转义的:;/?:@&=+$,# 第 ...

  5. GL-inet路由器当主控制作WIFI视频小车

    以前也用单片机做过WIFI小车,但是单片机没有自带WIFI,仍然需要用到小路由器作为图传和控制信号传输.既然肯定要用到路由器,那何不直接用路由器作为主控呢,这样就省掉了单片机.这次作为主控的GL-in ...

  6. Reason的介绍和搭建Reason开发环境

    Reason介绍 Reason是在Ocaml语言的基础上修改而来,专门提供给前端开发者使用. Reason是函数式编程语言,使用Bucklescript编译器编译成javascript语言. 在我看来 ...

  7. 不能完整读取txt文件问题

    txt文件内容 5 1.3 0.4 3.4 -1.7 16.7 0.89 14.17 4.8 1.34 0.42 3.36 -2 16.2 0.9 14.8 4.9 1.30 0.37 3.51 -1 ...

  8. 【算法】C语言趣味程序设计编程百例精解

    C语言趣味程序设计编程百例精解 C/C++语言经典.实用.趣味程序设计编程百例精解(1)  https://wenku.baidu.com/view/b9f683c08bd63186bcebbc3c. ...

  9. fuser:command not found

    yum 安装fuser命令 yum install -y psmisc

  10. Excel vba中访问ASP.NET MVC项目,记录访问时间,文件名称

    每30秒连接一次服务器,连接成功单元格变绿色,连接失败变红色,状态单元格为17行,2列 1,打开excel文件,进入vba编辑器,新建一个modules模块,在里面先写一个每30秒执行一次ConnSe ...