一、通过指令 "strace -o xawtv.log xawtv" 得到以下调用信息:
// 1~7都是在v4l2_open里调用
1. open
2. ioctl(4, VIDIOC_QUERYCAP

// 3~7 都是在get_device_capabilities里调用
3. for()
        ioctl(4, VIDIOC_ENUMINPUT   // 列举输入源,VIDIOC_ENUMINPUT/VIDIOC_G_INPUT/VIDIOC_S_INPUT不是必需的
4. for()
        ioctl(4, VIDIOC_ENUMSTD      // 列举标准(制式), 不是必需的
5. for()       
        ioctl(4, VIDIOC_ENUM_FMT    // 列举格式

6. ioctl(4, VIDIOC_G_PARM
7. for()
        ioctl(4, VIDIOC_QUERYCTRL    // 查询属性(比如说亮度值最小值、最大值、默认值)

// 8~10都是通过v4l2_read_attr来调用的       
8. ioctl(4, VIDIOC_G_STD               // 获得当前使用的标准(制式), 不是必需的
9. ioctl(4, VIDIOC_G_INPUT
10. ioctl(4, VIDIOC_G_CTRL            // 获得当前属性, 比如亮度是多少

11. ioctl(4, VIDIOC_TRY_FMT          // 试试能否支持某种格式
12. ioctl(4, VIDIOC_S_FMT              // 设置摄像头使用某种格式

// 13~16在v4l2_start_streaming
13. ioctl(4, VIDIOC_REQBUFS             // 请求系统分配缓冲区
14. for()
        ioctl(4, VIDIOC_QUERYBUF         // 查询所分配的缓冲区
        mmap       
15. for ()
        ioctl(4, VIDIOC_QBUF                // 把缓冲区放入队列       
16. ioctl(4, VIDIOC_STREAMON          // 启动摄像头

// 17里都是通过v4l2_write_attr来调用的
17. for ()
        ioctl(4, VIDIOC_S_CTRL            // 设置属性
    ioctl(4, VIDIOC_S_INPUT              // 设置输入源
    ioctl(4, VIDIOC_S_STD                 // 设置标准(制式), 不是必需的

// v4l2_nextframe > v4l2_waiton   
18. v4l2_queue_all  //全部放入buffer
    v4l2_waiton   
        for ()
        {
            select(5, [4], NULL, NULL, {5, 0})    = 1 (in [4], left {4, 985979})
            ioctl(4, VIDIOC_DQBUF                // de-queue, 把缓冲区从队列中取出
            // 处理, 之以已经通过mmap获得了缓冲区的地址, 就可以直接访问数据       
            ioctl(4, VIDIOC_QBUF                 // 把缓冲区放入队列
        }

xawtv的几大函数:
1. v4l2_open
2. v4l2_read_attr/v4l2_write_attr  //读写属性
3. v4l2_start_streaming                //申请buffer
4. v4l2_nextframe/v4l2_waiton

二、摄像头驱动程序必需的11个ioctl:  /drivers/media/video/vivi.c   -> vivi_ioctl_ops(结构体),修改测试得知:
 .vidioc_querycap      = vidioc_querycap,  // 表示它是一个摄像头设备

/* 用于列举、获得、测试、设置摄像头的数据的格式 */
 .vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
 .vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
 .vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
 .vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,

/* 缓冲区操作: 申请/查询/放入队列/取出队列 */
 .vidioc_reqbufs       = vidioc_reqbufs,
 .vidioc_querybuf      = vidioc_querybuf,
 .vidioc_qbuf          = vidioc_qbuf,
 .vidioc_dqbuf         = vidioc_dqbuf,

// 启动/停止
 .vidioc_streamon      = vidioc_streamon,
 .vidioc_streamoff     = vidioc_streamoff, 
    
继续分析数据的获取过程:
1. 请求分配缓冲区: ioctl(4, VIDIOC_REQBUFS                             // 请求系统分配缓冲区
                          videobuf_reqbufs(队列, v4l2_requestbuffers)   // 队列在open函数用videobuf_queue_vmalloc_init初始化
                          // 注意:这个IOCTL只是分配缓冲区的头部信息,真正的缓存还没有分配

2. 查询映射缓冲区:
ioctl(4, VIDIOC_QUERYBUF       // 查询所分配的缓冲区
        videobuf_querybuf           // 获得缓冲区的数据格式、大小、每一行长度、高度           
mmap(参数里有"大小")              // 在这里才分配缓存
        v4l2_mmap
            vivi_mmap
                videobuf_mmap_mapper
                    videobuf-vmalloc.c里的__videobuf_mmap_mapper
                            mem->vmalloc = vmalloc_user(pages);   // 在这里才给缓冲区分配空间

3. 把缓冲区放入队列:
ioctl(4, VIDIOC_QBUF               // 把缓冲区放入队列       
    videobuf_qbuf
        q->ops->buf_prepare(q, buf, field);           // 调用驱动程序提供的函数做些预处理
        list_add_tail(&buf->stream, &q->stream);  // 把缓冲区放入队列的尾部
        q->ops->buf_queue(q, buf);                     // 调用驱动程序提供的"入队列函数"

4. 启动摄像头
ioctl(4, VIDIOC_STREAMON
    videobuf_streamon
        q->streaming = 1;

5. 用select查询是否有数据:select(5, [4], NULL, NULL, {5, 0}) = 1 (in [4], left {4, 985979})
          // 驱动程序里必定有: 产生数据、唤醒进程
          v4l2_poll
                vdev->fops->poll
                    vivi_poll  
                        videobuf_poll_stream
                   // 从队列的头部获得缓冲区
                   buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
                            
                   // 如果没有数据则休眠                   
                   poll_wait(file, &buf->done, wait);

谁来产生数据、谁来唤醒它?
    内核线程vivi_thread每30MS执行一次,它调用
    vivi_thread_tick
        vivi_fillbuff(fh, buf);      // 构造数据
        wake_up(&buf->vb.done);  // 唤醒进程

6. 有数据后从队列里取出缓冲区
// 有那么多缓冲区,APP如何知道哪一个缓冲区有数据?  调用 VIDIOC_DQBUF
ioctl(4, VIDIOC_DQBUF
    vidioc_dqbuf  
        // 在队列里获得有数据的缓冲区
        retval = stream_next_buffer(q, &buf, nonblocking);
       
        // 把它从队列中删掉
        list_del(&buf->stream);
       
        // 把这个缓冲区的状态返回给APP
        videobuf_status(q, b, buf, q->type);
       
7. 应用程序根据VIDIOC_DQBUF所得到缓冲区状态,知道是哪一个缓冲区有数据
   就去读对应的地址(该地址来自前面的mmap)。

===> vivi.c缓冲区操作过程: ①VIDIOC_REQBUFS(分配头部信息) -> ②VIDIOC_QUERYBUF(返回属性)  / mmap(映射地址,分配实际空间) ->

          ③ VIDIOC_QBUF(把缓冲区放入队列) -> ④VIDIOC_STREAMON(启动摄像头)-> ⑤用select查询是否有数据:在队列头一个buf上操作 ->

          ⑥ VIDIOC_DQBUF(返回队列头的buf并从队列中删除) -> ⑦ VIDIOC_DQBUF(重新放回队列-③)

怎么写摄像头驱动程序:
1. 分配结构体:video_device:video_device_alloc
2. 设置
   .fops
   .ioctl_ops (里面需要设置11项)
   如果要用内核提供的缓冲区操作函数,还需要构造一个videobuf_queue_ops
3. 注册: video_register_device

Linux摄像头驱动学习之:(二)通过虚拟驱动vivi分析摄像头驱动的更多相关文章

  1. 通过虚拟驱动vivi分析摄像头驱动

    Linux摄像头驱动学习之:(二)通过虚拟驱动vivi分析摄像头驱动 一.通过指令 "strace -o xawtv.log xawtv" 得到以下调用信息: // 1~7都是在v ...

  2. linux驱动学习(二) Makefile高级【转】

    转自:http://blog.csdn.net/ghostyu/article/details/6866863 版权声明:本文为博主原创文章,未经博主允许不得转载. 在我前一篇写的[ linux驱动学 ...

  3. Linux音频驱动学习之:(1)ASOC分析

    一.音频架构概述 (1)ALSA是Advanced Linux Sound Architecture 的缩写,目前已经成为了linux的主流音频体系结构,想了解更多的关于ALSA的这一开源项目的信息和 ...

  4. ucore操作系统学习笔记(二) ucore lab2物理内存管理分析

    一.lab2物理内存管理介绍 操作系统的一个主要职责是管理硬件资源,并向应用程序提供具有良好抽象的接口来使用这些资源. 而内存作为重要的计算机硬件资源,也必然需要被操作系统统一的管理.最初没有操作系统 ...

  5. Linux 网卡驱动学习(二)(网络驱动接口小结)

    [摘要]前文我们分析了一个虚拟硬件的网络驱动例子,从中我们看到了网络设备的一些接口,其实网络设备驱动和块设备驱动的功能比较类似,都是发送和接收数据包(数据请求).当然它们实际是有很多不同的. 1.引言 ...

  6. Linux内核驱动学习(二)添加自定义菜单到内核源码menuconfig

    文章目录 目标 drivers/Kconfig demo下的Kconfig 和 Makefile Kconfig Makefile demo_gpio.c 目标 Kernel:Linux 4.4 我编 ...

  7. linux内核学习之二 一个精简内核的分析(基于时间片轮转)

    一   实验过程及效果 1.准备好相关的代码,分别是mymain.c,mypcb.h,myinterrupt.c ,如下图,make make成功: 在qemu创建的虚拟环境下的运行效果:(使用的命令 ...

  8. Linux USB驱动学习总结(一)---- USB基本概念及驱动架构

    USB,Universal Serial Bus(通用串行总线),是一个外部总线标准,用于规范电脑与外部设备的连接和通讯.是应用在PC领域的接口技术.USB接口支持设备的即插即用和热插拔功能.USB是 ...

  9. 对Linux命令进一步学习vim(二)

    今天,进一步学习Linux相关的命令,可能会有重复的地方,但学习本来就是不断重复的过程.故作小记! 1.安装了:vim  ,,,一款Linux爱好者经常用到的ide sudo apt-get inst ...

随机推荐

  1. Android 进入页面默认定位到ListView的解决方法

    由于ListView会默认去获取焦点,如果说ListView在页面的下方的话,那么点击条目进入新页面并退出,那么这时候就会定位到ListView这里,而不是展示头部.   解决这个问题,只需要在Lis ...

  2. c++ 对象内存分配和虚函数

    1. c++类对象(不含虚函数)在内存中的分布 c++类中有四种成员:静态数据.非静态数据.静态函数.非静态函数. 1. 非静态数据成员放在每个对象内部,作为对象专有的数据成员 2. 静态数据成员被抽 ...

  3. 最大似然估计(Maximum Likelihood,ML)

    先不要想其他的,首先要在大脑里形成概念! 最大似然估计是什么意思?呵呵,完全不懂字面意思,似然是个啥啊?其实似然是likelihood的文言翻译,就是可能性的意思,所以Maximum Likeliho ...

  4. 第二章 Python基本元素:数字、字符串和变量

    Python有哪些内置的数据类型: True False #布尔型 42 100000000 #整型 3.14159 1.0e8 #浮点型 abcdes #字符串 2.1 变量.名字和对象 pytho ...

  5. 基于SourceTree 下的 Git Flow 模型

    基于SourceTree 下的 Git Flow 模型 1. sourceTree  是一个开源的git 图形管理工具,可下载mac版本,windows版本 2. Git Flow 是一套使用Git进 ...

  6. 编译spock proxy

    今天把spock proxy编译通过并且运行了.大家如果在编译这款类似于MySQL proxy的软件遇到问题时,可以联系我.微信onesoft007

  7. Cmd Markdown 高阶语法手册

    『Cmd 技术渲染的沙箱页面,点击此处编写自己的文档』 Cmd Markdown 高阶语法手册 1. 内容目录 在段落中填写 [TOC] 以显示全文内容的目录结构. [TOC] 2. 标签分类 在编辑 ...

  8. 任性,新建对象不用new

    先看最简单的一个例子: window.meng = window.meng || {}; (function () { /** * * @param {Number}width * @param {N ...

  9. 20145218《Java程序设计》第一周学习总结

    20145218 <Java程序设计>第一周学习总结 教材学习内容总结 今天下午看了Java学习的视频,感觉很是新奇,之前觉得Java学起来是艰难枯燥的,但通过第一章的学习觉得如果自己可以 ...

  10. pvresize - Unix, Linux Command

    NAME pvresize - resize a disk or partition in use by LVM2 SYNOPSIS pvresize [-d|--debug] [-h|--help] ...