在上一节课中,我们写了一个虚拟摄像头驱动程序,里面的数据使我们虚构出来的。
现在我们要写USB摄像头驱动程序了,它里面就涉及到硬件的操作,比如说我们想要设置亮度的时候,需要把亮度的参数发给硬件,我们需要得到真正的视频数据的时候需要去访问硬件得到数据。
但是它们的框架应该是一样的。
写一个 USB 摄像头驱动程序。
回顾一下,怎么写摄像头驱动程序:
-
分配 video_device: video_device_alloc
-
设置
.fops
.ioctl_ops
(里面至少需要设置11项,设置各种属性亮度,对比度,获得视频数据)- 如果要用内核提供的缓冲区操作函数,还需要构造一个 videobuf_queue_ops
-
注册:video_register_device
-
构造一个usb_driver结构体
-
设置这个 driver 结构体,需要完成 probe 函数
- probe 函数
- 分配 video_device: video_device_alloc
- 设置
.fops
.ioctl_ops
(里面至少需要设置11项,设置各种属性亮度,对比度,获得视频数据)- 如果要用内核提供的缓冲区操作函数,还需要构造一个 videobuf_queue_ops
- 注册:video_register_device
- id_table:表示支持哪些USB设备
- probe 函数
-
注册
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 摄像头内部分为两个部分:
- video control interface: 做一些控制作用的,用于控制, VC
- 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 驱动的重点在于:
- 描述符的分析
- 属性的控制,
- 格式的选择,
- 数据的获得
文章评论