学习目标:根据vivi驱动架构和linux-2.6.31/linux-2.6.31.14/drivers/media/video/uvc/Uvc_driver.c驱动源码,分析usb摄像头驱动程序框架;

一、摄像头拓扑结构

由数据手册USB_Video_Example 1.5和UVC 1.5 Class specification分析摄像头的拓扑结构,如下图所示:

usb摄像头功能由摄像头接口完成,每个摄像头功能包括一个 VideoControl interface (VC)和几个VideoStreaming interface (VS)。

1) CS主要用于控制,它又抽象出两个概念:unit和termiral。unit用于里面的相关控制。termiral用于内外连接。其中,unit中包括:a.  Selector Unit (SU) 选择(CT (Camera Terminal) 和IT(Input Terminal))哪一路 。b. Processing Unit (PU) 用于亮度等属性参数控制。

2) VS用于传输,来读视频数据,。需要设置类型type和格式format,选择实时传输还是批量传输Bulk。

二、usb摄像头源码框架分析 

在Linux内核里自带usb摄像头驱动程序,它支持uvc规格的摄像头(uvc即usb video class),进入源码程序分析。

1、进入uvc_init入口函数

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

注册了 uvc_driver结构体,根据.id_table匹配后,进入uvc_probe函数:
uvc_register_video(dev)

  -->vdev = video_device_alloc();

   vdev->fops = &uvc_fops;

   vdev->release = video_device_release;

   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,
.ioctl = uvc_v4l2_ioctl,
.read = uvc_v4l2_read,
.mmap = uvc_v4l2_mmap,
.poll = uvc_v4l2_poll,
};

由此可知,写一个USB摄像头驱动程序需要以下步骤:
1)构造一个usb_driver结构体(uvc结构体)
2)设置
   probe:
        2.1. 分配video_device:video_device_alloc
        2.2. 设置
           .fops
           .ioctl_ops (里面需要设置11项)
           如果要用内核提供的缓冲区操作函数,还需要构造一个videobuf_queue_ops
        2.3. 注册: video_register_device     
  id_table: 表示支持哪些USB设备     
3)注册: usb_register

 2、分析UVC驱动调用过程

const struct v4l2_file_operations uvc_fops = {
.owner = THIS_MODULE,
.open = uvc_v4l2_open,
.release = uvc_v4l2_release,
.ioctl = uvc_v4l2_ioctl,
.read = uvc_v4l2_read,
.mmap = uvc_v4l2_mmap,
.poll = uvc_v4l2_poll,
};

1) open函数

  uvc_v4l2_open //一系列设置

2) uvc_v4l2_ioctl函数

-->video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);//将用户提供的参数拷贝到内核态,并调用uvc_v4l2_do_ioctl函数

  -->uvc_v4l2_do_ioctl

    struct video_device *vdev = video_devdata(file);//根据次设备号,找到video_device

    switch (cmd) { //根据cmd执行一系列的ioctl函数
     /* Query capabilities */
     case VIDIOC_QUERYCAP:

     .......

接下来具体分析ioctl过程:

1)VIDIOC_QUERYCAP

if (video->streaming->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;

// video->streaming->type 应该是在设备被枚举时分析描述符时设置的。(对用功能拓扑的VS)

2)VIDIOC_ENUM_FMT // format数组应是在设备被枚举时设置的

  format = &video->streaming->format[fmt->index];

3)VIDIOC_G_FMT

  --> uvc_v4l2_get_format(video, arg); // USB摄像头支持多种格式fromat, 每种格式下有多种frame(比如分辨率)

     struct uvc_format *format = video->streaming->cur_format;
     struct uvc_frame *frame = video->streaming->cur_frame;

4)VIDIOC_TRY_FMT

  -->uvc_v4l2_try_format(video, arg, &probe, NULL, NULL);

    /* Check if the hardware supports the requested format. */
    /* Find the closest image size.*/找到最接近的图像格式

5)VIDIOC_S_FMT  // 只是把参数保存起来,还没有发给USB摄像头

  -->uvc_v4l2_set_format(video, arg);

    uvc_v4l2_try_format(video, fmt, &probe, &format, &frame);

     memcpy(&video->streaming->ctrl, &probe, sizeof probe);
     video->streaming->cur_format = format;
     video->streaming->cur_frame = frame;

6)VIDIOC_REQBUFS

  -->uvc_alloc_buffers(&video->queue, rb->count, bufsize);

       unsigned int bufsize = video->streaming->ctrl.dwMaxVideoFrameSize; //bufsize已经设置并获取,vmalloc时只需传进来应用程序的rb->count数值

    /* 根据rb->count来分配多少个buf*/

    for (; nbuffers > 0; --nbuffers) {
      mem = vmalloc_32(nbuffers * bufsize);
      if (mem != NULL)
       break;
     }   

7)VIDIOC_QUERYBUF

  -->uvc_query_buffer(&video->queue, buf);

__uvc_query_buffer
                memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf);  // 复制参数

8)mmap //查询buf参数后使用mmap把buf映射给应用程序

  uvc_v4l2_mmap

9)VIDIOC_QBUF 

  -->uvc_queue_buffer(&video->queue, arg);

     list_add_tail(&buf->stream, &queue->mainqueue);
     list_add_tail(&buf->queue, &queue->irqqueue);

10)VIDIOC_STREAMON

  -->uvc_video_enable(video, 1)  // 把所设置的参数发给硬件,然后启动摄像头

    /* Commit the streaming parameters.  /* 传输
      uvc_commit_video(video, &video->streaming->ctrl) //*在前面VIDIOC_S_FMT赋值的video->streaming->ctrl

      -->uvc_set_video_ctrl(video, probe, 0);///* 设置格式fromat, frame */

        ret = __uvc_query_ctrl(video->dev,/* 哪一个USB设备 */ SET_CUR, 0,  video->streaming->intfnum, /* 哪一个接口: VS */
                  probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
                  uvc_timeout_param);

注意:此处用到了__uvc_query_ctrl函数

        /* *启动 Initialize isochronous/bulk URBs and allocate transfer buffers. */

        uvc_init_video(video, GFP_KERNEL);

        uvc_init_video_isoc(video, ep, gfp_flags);(实时传输)/uvc_init_video_bulk(video, ep, gfp_flags)(批量传输)

          -->usb_alloc_urb

          设置urb结构体(urb用于usb数据传输的结构体)

           urb->complete = uvc_video_complete;(收到数据后此函数被调用,它又调用video->decode(urb, video, buf); ==>uvc_video_decode_isoc/uvc_video_encode_bulk => uvc_queue_next_buffer => wake_up(&buf->wait);)

      -->usb_submit_urb(video->urb[i], gfp_flags)) ; /* Submit the URBs. */
11) poll
        --> uvc_v4l2_poll           
            uvc_queue_poll
                poll_wait(file, &buf->wait, wait);  // 休眠等待有数据   

12)  VIDIOC_DQBUF //有数据时,把buf从队列中取出,并从原始队列里面删除

   --> uvc_dequeue_buffer(&video->queue, arg, file->f_flags & O_NONBLOCK);

      list_del(&buf->stream);  

13)VIDIOC_STREAMOFF

   uvc_video_enable(video, 0); //0表示关闭

   uvc_uninit_video(video, 1);

  --> usb_kill_urb(urb);
     usb_free_urb(urb);

-----以上均涉及到VS(Video Streaming Interface)接口的操作,不涉及Video Control Interface-----

下面以设置亮度过程为例,操作VC(Video Control Interface)接口(实质上是向VC发包):

ioctl: VIDIOC_S_CTRL

--->uvc_ctrl_set(video, &xctrl);//设置
     uvc_ctrl_commit(video); //传送
                __uvc_ctrl_commit(video, 0);
                    uvc_ctrl_commit_entity(video->dev, entity, rollback);
                   ret = uvc_query_ctrl(dev  /* 哪一个USB设备 */, SET_CUR, ctrl->entity->id  /* 哪一个unit/terminal */,
                    dev->intfnum /* 哪一个接口: VC interface */, ctrl->info->selector,
                    uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),ctrl->info->size);

三、总结

1. UVC设备是基于2个interface: VideoControl Interface VC, VideoStreaming Interface VS 进行操作的;

2. 1) VC 用于控制属性参数,比如设置亮度。它内部有多个Unit/Terminal(在程序里Unit/Terminal都称为entity)
   可以通过类似的函数来访问:
                   ret = uvc_query_ctrl(dev  /* 哪一个USB设备 */, SET_CUR, ctrl->entity->id  /* 哪一个unit/terminal */,
                    dev->intfnum /* 哪一个接口: VC interface */, ctrl->info->selector,
                    uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
                    ctrl->info->size);

  2) VS用于获得视频数据,也可以用来选择fromat/frame(VS可能有多种format, 一个format支持多种frame, frame用来表示分辨率等信息)
    可以通过类似的函数来访问:
                     ret = __uvc_query_ctrl(video->dev /* 哪一个USB设备 */, SET_CUR, 0,
                      video->streaming->intfnum  /* 哪一个接口: VS */,
                      probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
                      uvc_timeout_param);

3. 设置FORMAT时只是简单的使用video->streaming->format[fmt->index]等数据,这些数据应是设备被枚举时设置的,也就是分析它的描述符时设置的。

4. UVC驱动的重点在于: usb摄像头描述符的分析
   属性的控制: 通过VideoControl Interface(VC)来设置
   格式的选择:通过VideoStreaming Interface(VS)来设置
   数据的获得:通过VideoStreaming Interface(VS)的URB-->(usb设备驱动数据传输的结构体格式)来获得。


参考:百问网(韦东山视频)

2.5 USB摄像头驱动程序框架的更多相关文章

  1. USB摄像头驱动框架分析

    usb摄像头驱动程序,里面涉及硬件的操作.比如说,想设置亮度的时候,需要把亮度的参数发给硬件.去得到真正视频数据的时候,需要访问硬件得到数据.usb摄像头驱动程序框架与虚拟摄像头驱动程序的框架是一样的 ...

  2. USB摄像头驱动框架分析(五)

    一.USB摄像头驱动框架如下所示:1.构造一个usb_driver2.设置   probe:        2.1. 分配video_device:video_device_alloc        ...

  3. Linux驱动之USB总线驱动程序框架简析

    通用串行总线(USB)是主机和外围设备之间的一种连接.USB总线规范有1.1版和2.0版,当然现在已经有了3.0版本.USB1.1支持两种传输速度:低速为1.5Mbps,高速为12Mbps.USB2. ...

  4. 25、写一个USB摄像头驱动程序(有ioctrl分析)

    videobuf2-core.h中的vb2_buffer,记录了v4l2_buffer ,驱动可以对vb2_buffer的v4l2_buffer进行操控, vb2_buffer是v4l2框架层的代码, ...

  5. Linux USB摄像头驱动【转】

    本文转载自:http://www.itdadao.com/articles/c15a509940p0.html 在 cortex-a8 中,可接入摄像头的接口通常可以分为两种, CAMERA 接口和 ...

  6. USB设备驱动程序学习笔记(一)

    现象:把USB设备接到PC1. 右下角弹出"发现android phone"2. 跳出一个对话框,提示你安装驱动程序 问1. 既然还没有"驱动程序",为何能知道 ...

  7. Linux USB 摄像头驱动

    在 cortex-a8 中,可接入摄像头的接口通常可以分为两种, CAMERA 接口和 USB 接口的摄像头.这一章主要是介绍 USB 摄像头的设备驱动程序.在我们印象中,驱动程序都是一个萝卜一个坑, ...

  8. 12、USB设备驱动程序

    linux-3.4.2\driver\hid\usbhid\usbmouse.c 内核只带USB驱动程序 (hub和usb是两个不同的设备,hub在内核上电的过程中在usb_hub_init函数中调用 ...

  9. Linux笔记(固定USB摄像头硬件端口,绑定前后置摄像头)

    在Android的系统会有前置摄像头和后置摄像头的定义,摄像头分为SOC类型的摄像头和USB这一类的摄像头,接下要分析就是USB摄像头这一类 . 一般在android或者linux系统中分析一个模块, ...

随机推荐

  1. SQL一次性查询一个字段不同条件下的统计结果

    参考了一下这篇文章:https://blog.csdn.net/xichenguan/article/details/51764100 , 感谢原作者 有两个表,分别存放了[操作员]和[单据],要根据 ...

  2. margin的用法

    margin塌陷问题 当时说到了盒模型,盒模型包含着margin,为什么要在这里说margin呢?因为元素和元素在垂直方向上margin里面有坑. 我们来看一个例子: html结构: <div ...

  3. 打通版微社区(3):在Web服务器上部署memcache For DZ3.2

    写在前面:首先这个数据库加速程序的原理,是将数据库内容缓存到Web服务器的内存上,也就是内存换速度.我本次微社区的应用其实应该用不了这个,只是看到好多DZ论坛部署的都安装了这个,我就练手一下以便不时之 ...

  4. 用POP动画模拟真实秒钟摆动效果

    用POP动画模拟真实秒钟摆动效果 静态图: 动画图: 此处用到了POP中的Spring系列动画,现提供源码如下: SecondClockView.h 与 SecondClockView.m // // ...

  5. C++ Primer(6) 模板和泛型编程(上)

    问题聚焦: 泛型编程是独立于变量类型的方式编写代码: 模板是泛型编程的基础. 本篇主要介绍模板的基础知识,包括:模板的定义和模板的实例化. 1 模版定义 必要性: Demo int compare(c ...

  6. 省事之通用Makefile模版

    现在编译方案都偏爱使用cmake解决问题,这两条做unity插件,还是用Makefile,居然忘得光光,好记性不如烂笔头. 后面,翻箱倒柜找到以前为炼金术写的Makefiel,发现还真是挺好用,贴出来 ...

  7. August 09th 2017 Week 32nd Wednesday

    Find hope from despair, life will become brilliant. 从绝望中寻找希望,人生终将辉煌. Have you ever seen the movie Ba ...

  8. pipenv

    一. 1. 使用pip安装pipenv及其相关依赖 pip install pipenv 2. 将目录更改为包含你的Python项目的文件夹,并启动Pipenv cd my_project pipen ...

  9. Xcode 下载地址 与Macos版本要求

    Xcode下载地址:https://developer.apple.com/download/more/ 参考文档:https://zh.wikipedia.org/wiki/Xcode

  10. Coursera 算法二 week 5 BurrowsWheeler

    本打算周末完成这次作业,但没想到遇到了hard deadline,刚开始看不懂题意,后来发现算法4书上有个类似的问题,才理解了题意.最后晚上加班,上课加班,还好在11:35也就是课程结束前25分钟完成 ...