5_摄像头驱动_USB摄像头驱动框架分析

2023年 1月 30日 68点热度 0人点赞

在上一节课中,我们写了一个虚拟摄像头驱动程序,里面的数据使我们虚构出来的。

现在我们要写USB摄像头驱动程序了,它里面就涉及到硬件的操作,比如说我们想要设置亮度的时候,需要把亮度的参数发给硬件,我们需要得到真正的视频数据的时候需要去访问硬件得到数据。

但是它们的框架应该是一样的。

写一个 USB 摄像头驱动程序。

回顾一下,怎么写摄像头驱动程序:

  1. 分配 video_device: video_device_alloc

  2. 设置

    • .fops
    • .ioctl_ops (里面至少需要设置11项,设置各种属性亮度,对比度,获得视频数据)
    • 如果要用内核提供的缓冲区操作函数,还需要构造一个 videobuf_queue_ops
  3. 注册:video_register_device

  4. 构造一个usb_driver结构体

  5. 设置这个 driver 结构体,需要完成 probe 函数

    • probe 函数
      1. 分配 video_device: video_device_alloc
      2. 设置
        • .fops
        • .ioctl_ops (里面至少需要设置11项,设置各种属性亮度,对比度,获得视频数据)
        • 如果要用内核提供的缓冲区操作函数,还需要构造一个 videobuf_queue_ops
      3. 注册:video_register_device
    • id_table:表示支持哪些USB设备
  6. 注册

Linux 已经自带有 USB 摄像头驱动程序,它支持带 UVC 规格的摄像头,所谓 UVC :Usb Video Class,表示某一类 USB 设置,基本上 Linux 上即插即用的 USB 摄像头就是符合这个规范的,就不需要自己去安装驱动程序。

Linux 中自带的 UVC 驱动程序非常非常的复杂,这里我会带领大家从零开始,直到能写出一个具备基本功能的摄像头驱动程序。

UVC 驱动程序的位置:drivers/media/video/uvc/uvc_driver.c, 这个目录下的所有文件都是 uvc 的驱动程序

uvcvideo.ko: 里面有多个 .c 文件。

入口函数的位置:

// 入口函数
static int __init uvc_init(void) {
    usb_register(&uvc_driver.driver);
}

struct uvc_driver 结构体, 系统已经给我们创建好了,

uvc_register.c 分析

1. usb_register(&uvc_driver.driver);
2. uvc_probe
    uvc_register_video
        vdev = video_device_alloc();
        vdev->fops = &uvc_fops;
        video_register_device

分析一个驱动程序最好的方法就是跟踪应用程序对它的调用过程,在讲调用过程之前,先来了解一下USB摄像头硬件的内部框架。

先来下载 UVC 的规格书,我们只需要看 example和specification,下载地址 www.usb.org uvc 规格书。

usb video example 给出了两个例子,
uvc specification: 有详细说明

在 usb 摄像头内部分为两个部分:

  1. video control interface: 做一些控制作用的,用于控制, VC
  2. video streaming interface: 读取数据的,用于传输,VS

VC 内部抽象出两个概念:unit 和 terminals

SU: select uinit,多路输入,选择哪一路

PU:process unit, user control auto control, other

IT: input terminal

CT: camera terminal

OT: output terminal

unit:里面的才是unit

terminal:用于内外连接

VC里含有多个 Unit/Termial等功能模块,可以通过访问这些模块进行控制,比如调亮度

分析 uvc驱动的调用过程

v4l2_file_operations uvc_fops

open

1. open
    - uvc_v4l2_open

11个ioctl: 都没有对设备的usb操作,只是在枚举的时候进行一些数据处理

VIDIOC_QUERYCAP:
VIDIOC_ENUM_FMT:
VIDIOC_G_FMT:
VIDIOC_TRY_FMT:找到最接近的 image size
VIDIOC_S_FMT:只是把参数保存起来,还没有发给usb摄像头
VIDIOC_REQBUFS: 分配多少个缓冲区,每个缓冲区的大小
VIDIOC_QUERYBUF:
mmap:将buf映射到用户空间
VIDIOC_QBUF:  入队列
VIDIOC_STREAMON: 启动摄像头,将所设置的参数发给硬件,并启动摄像头,commit
poll函数:uvc_v4l2_poll
            uvc_queue_poll
VIDIOC_DQBUF: 将数据从队列中取出来
VIDIOC_STREAMOFF: 停止的时候,不想用就关闭它

videostreaming interface:

  • type
  • format

分析设置亮度的过程

ioctl: VIDIOC_S_CTRL
    __uvc_ctrl_commit
        intfnum: 控制接口,streaming 就是流控制接口

entity->id 表示哪个 unit 或者哪个 terminal, 在程序中 unit 和 terminal 都统称为实体 entity

VideoStreaming Interface 用户获得视频数据,也可以用来选择 format/frame, 一个format 支持多种 frame,frame 用来表示分辨率等信息,可以通过类似的函数访问。

我们在设置 format 时, 只是简单的使用数据,这些数据是哪儿来的?

应该是设备被枚举时设置的,也就是分析它的描述符时设置的。

UVC 驱动的重点在于:

  1. 描述符的分析
  2. 属性的控制,
  3. 格式的选择,
  4. 数据的获得

rainbow

这个人很懒,什么都没留下

文章评论