一、V4L2视频设备驱动基础
1.V4L2是专门为Linux设备设计的整合视频框架(其主要核心在Linux内核,相当于Linux操作系统上层的视频源捕获驱动框架)。为上层访问系统底层的视频设备提供一个统一的标准接口。V4L2驱动框架能够支持多种类型:如视频I/O,radio设备、VBI设备。
V4L2支持以下主要设备类型,通过不同设备节点区分:
视频捕获设备(Video Capture)
例如USB摄像头(UVC兼容设备)、MIPI摄像头等,设备节点为/dev/videoX
,是V4L2最常用的功能。
示例应用:实时视频采集、视频会议。视频输出设备(Video Output)
用于向显示器、电视等输出视频信号,部分平台(如i.MX 6/7)支持此功能,但i.MX 8仅支持采集。垂直消隐期设备(VBI)
处理视频消隐期间的数据(如电视字幕),设备节点为/dev/vbiX
。广播设备(Radio)
支持AM/FM调谐器,设备节点为/dev/radioX
。视频覆盖接口(Video Overlay)
允许视频数据直接从捕获设备传输到显示设备,无需CPU处理。
- 用户空间 - /dev/videoX:这是用户空间访问摄像头设备的接口文件。应用程序通过对
/dev/videoX
(X 为具体数字,代表不同视频设备)进行读写、控制等操作,来获取摄像头数据或配置摄像头参数。 - 内核空间 - V4L2 设备号:用于标识 V4L2 设备,是内核区分不同设备的依据。系统通过设备号来定位和管理对应的 V4L2 设备驱动程序。
- 内核空间 - 平台 V4L2 驱动:V4L2_dev/V4L2_device :与具体硬件平台相关的驱动部分,负责注册设备到 V4L2 驱动核心层,实现设备与核心层之间的交互,并提供一些平台相关的功能支持和硬件操作接口。
- 内核空间 - V4L2 驱动核心层:V4L2 框架的核心部分,提供了通用的视频设备驱动接口和功能。它管理和协调平台 V4L2 驱动以及具体传感器设备驱动之间的交互,对设备进行统一的管理和调度。
- 内核空间 - 字符设备驱动 (cdev):V4L2 设备在 Linux 系统中以字符设备的形式存在,字符设备驱动负责提供基本的设备文件操作接口(如 open、read、write 等),使得用户空间能够通过文件操作的方式访问 V4L2 设备。
- 内核空间 - 具体传感器设备的驱动 V4L2_subdev:针对摄像头具体传感器硬件的驱动,负责初始化传感器、读取传感器数据,并且与 V4L2 驱动核心层交互,将传感器数据转换为 V4L2 框架可处理的格式。
- 硬件层面 - 摄像头传感器硬件设备:实际的物理硬件,负责捕捉光线并将其转换为电信号,进而产生原始的图像数据,是整个视频采集的源头 。
2.V4L2常用结构体
a.v4l2_device:(属于整个输入设备的总结构体,有多少个输入设备就会有多少个v4l1抽象)。
b.v4l2_subdev:基于v4l2_device之下,代表一个设备的子设备。
c.vb2_queue:v4l2设备的混存管理操作。
d.video_device:专门用于为用户空间提供设备节点,就相当于提供系统调用的API函数(ioctl、open等等)。
e. v4l2_file_operations:定义了用户空间对 V4L2 设备进行文件操作(如打开、关闭、读写等)的函数接口,是用户空间与内核中 V4L2 设备交互的文件操作桥梁。
f. v4l2_ioctl_ops:规定了通过 ioctl 系统调用对 V4L2 设备进行控制操作的函数集合,用于实现对设备各种属性和功能的控制与配置。
g. v4l2_subdev:作为 v4l2_device 的子设备抽象,代表一个 V4L2 设备中的具体功能模块,如摄像头的传感器、ISP 芯片等,负责特定子功能的实现和管理。
h. v4l2_subdev_ops:定义了针对 v4l2_subdev 子设备的操作方法集,涵盖了对子设备的初始化、配置、控制等操作的函数接口,实现对子设备的具体操作逻辑。
1. v4l2_device
功能作用:
v4l2_device
是整个 V4L2 框架的核心结构体,用于表示一个 V4L2 设备实例。它是整个输入设备的总结构体,相当于 V4L2 框架的入口,管理所有子设备(如 ISP、CSI、MIPI 等)。- 它包含指向子设备链表的指针、媒体设备(
media_device
)的指针、锁、通知操作、控制处理器等数据结构。- 当
v4l2_device
被移除时,系统会自动注销所有注册的子设备。使用方法:
- 在驱动程序中,首先需要通过
v4l2_device_register()
函数注册一个v4l2_device
实例。- 驱动程序需要为每个设备分配一个
v4l2_device
实例,并将其嵌入到更大的结构体中,以便与其他框架(如媒体设备框架)集成。- 注册完成后,可以通过
v4l2_device_get()
获取对该实例的引用。示例:
- 假设一个 USB 摄像头驱动程序,它会为摄像头创建一个
v4l2_device
实例。该实例会管理摄像头的所有功能模块(如 ISP【专门用于处理图像的芯片】、CSI【摄像头串行接口,是一种用于连接摄像头传感器和处理器的接口标准】),并通过v4l2_device_register()
注册到系统中。用户可以通过/dev/videoX
节点访问这些功能模块。2. v4l2_subdev
功能作用:
v4l2_subdev
是基于v4l2_device
的子设备结构体,用于抽象一个设备的子设备(如 ISP、CSI、MIPI 等)。它是 V4L2 框架中用于管理子设备的核心结构体。- 它包含对子设备操作的函数集合(
ops
)和控制函数(ctrls
),具体实现由硬件驱动完成。- 子设备通过挂载到
v4l2_device
的子设备链表中进行统一管理。使用方法:
- 在驱动程序中,需要为每个子设备创建一个
v4l2_subdev
实例,并将其挂载到对应的v4l2_device
上。- 驱动程序需要实现
ops
和ctrls
函数,以支持子设备的操作和控制。- 子设备可以通过
v4l2_subdev_register()
注册到系统中,并通过v4l2_subdev_get()
获取引用。示例:
- 在一个摄像头驱动程序中,ISP(图像信号处理器)和 CSI(摄像头接口)可以分别作为两个子设备。它们会创建各自的
v4l2_subdev
实例,并挂载到主v4l2_device
上。用户可以通过/dev/videoX
节点访问这些子设备的功能。3. vb2_queue
功能作用:
vb2_queue
是 V4L2 设备的缓存管理操作结构体,用于管理视频缓冲区(buffer)的分配、提交和完成操作。- 它与
vb2_v4l2_buffer
结合使用,用于实现数据流的实际逻辑和 DMA 操作。使用方法:
- 在驱动程序中,需要为每个视频缓冲区队列创建一个
vb2_queue
实例,并将其与vb2_v4l2_buffer
结合使用。- 驱动程序需要实现缓冲区的分配、提交和完成回调函数,以支持数据流的处理。
- 用户可以通过
/dev/videoX
节点访问这些缓冲区队列,进行数据读写操作。示例:
- 在一个视频解码器驱动程序中,可以为解码器创建一个
vb2_queue
实例。用户通过/dev/videoX
节点提交视频帧缓冲区,驱动程序会将缓冲区分配给解码器进行解码处理。4. video_device
功能作用:
video_device
是专门用于为用户空间提供设备节点(如/dev/videoX
)的结构体,相当于提供系统调用 API 函数(如ioctl
、open
等)。- 它包含指向
v4l2_device
的指针、字符设备节点信息以及文件操作函数集合(如fops
和ioctl_ops
)。使用方法:
- 在驱动程序中,需要为每个设备节点创建一个
video_device
实例,并将其与v4l2_device
连接。- 驱动程序需要实现文件操作函数(如
open
、release
、ioctl
等),以支持用户空间的访问。- 用户可以通过
/dev/videoX
节点访问设备的功能。示例:
- 在一个摄像头驱动程序中,可以为摄像头创建一个
video_device
实例,并将其与主v4l2_device
连接。用户通过/dev/videoX
节点访问摄像头的功能,如打开摄像头、获取视频帧等。5. v4l2_file_operations
- 功能作用:
- v4l2_file_operations 是用于定义用户空间对 V4L2 设备进行文件操作的结构体。
- 为用户空间程序提供与 V4L2 设备交互的基本接口,包含打开、关闭、读取、写入等操作的函数指针。
- 驱动程序通过实现这些函数,处理用户空间对设备的文件操作请求,保障设备正常使用和数据正确传输。
- 使用方法:
- 在驱动程序中,需定义一个 v4l2_file_operations 结构体实例,并为各成员函数指针赋值,实现具体操作逻辑。
- 将该结构体实例与 video_device 结构体关联,让用户空间通过设备节点的文件操作能调用到相应处理函数。
- 示例:
- 在 USB 摄像头驱动程序中,定义 v4l2_file_operations 结构体实例。
- 实现 open 函数用于初始化摄像头设备,read 函数用于从摄像头读取视频数据。
- 将其与摄像头的 video_device 关联后,用户通过 /dev/videoX 节点执行 open 和 read 系统调用时,可调用驱动程序中的对应函数,完成对摄像头的操作和数据读取。
6. v4l2_ioctl_ops
- 功能作用:
- v4l2_ioctl_ops 是用于定义通过 ioctl 系统调用对 V4L2 设备进行控制操作的结构体。
- ioctl 是向设备驱动程序发送各种控制命令的系统调用,该结构体包含处理不同 ioctl 命令的函数指针。
- 驱动程序借助这些函数,依据用户发送的命令对设备进行配置、查询状态、执行特定操作等。
- 使用方法:
- 在驱动程序中,要定义一个 v4l2_ioctl_ops 结构体实例,并为各成员函数指针赋值,实现具体的 ioctl 命令处理逻辑。
- 把该结构体实例与 video_device 结构体关联,确保用户空间通过设备节点发送的 ioctl 命令能被正确处理。
- 示例:
- 在视频采集设备驱动程序中,定义 v4l2_ioctl_ops 结构体实例。
- 实现 VIDIOC_S_FMT 函数用于设置视频采集格式,VIDIOC_G_FMT 函数用于获取当前视频采集格式。
- 将其与设备的 video_device 关联后,用户通过 /dev/videoX 节点执行相应 ioctl 命令时,驱动程序会调用对应函数完成格式设置或查询操作。
7. v4l2_subdev_ops
- 功能作用:
- v4l2_subdev_ops 是用于定义对 v4l2_subdev 子设备进行操作的结构体。
- 包含处理子设备初始化、参数设置、状态获取等各种操作的函数指针。
- 驱动程序通过实现这些函数,对 V4L2 设备中的子设备(如 ISP、CSI 等)进行精确控制和管理。
- 使用方法:
- 在驱动程序中,需为每个 v4l2_subdev 子设备定义一个 v4l2_subdev_ops 结构体实例,并为各成员函数指针赋值,实现具体的子设备操作逻辑。
- 将该结构体实例与对应的 v4l2_subdev 关联,使得对子设备的操作能调用到相应处理函数。
- 示例:
- 在摄像头驱动程序中,针对 ISP 子设备,定义 v4l2_subdev_ops 结构体实例。
- 实现 s_stream 函数用于启动或停止 ISP 的数据流处理,g_ctrl 函数用于获取 ISP 的某个控制参数。
- 将其与 ISP 的 v4l2_subdev 关联后,当需要对 ISP 进行操作时,可调用这些函数,完成对 ISP 的控制和状态查询。
v4l2_subdev_ops
- 功能作用:
- v4l2_subdev_ops 是专门为 v4l2_subdev 子设备量身定制的操作方法集结构体。在 V4L2 框架里,一个完整的设备往往由多个子设备构成,像摄像头设备可能包含图像传感器、图像信号处理器(ISP)等子设备。v4l2_subdev 用于抽象这些子设备,而 v4l2_subdev_ops 则负责定义对这些子设备进行操作的具体方式。
- 它涵盖了一系列关键操作的函数接口,其中包括子设备的初始化操作。在系统启动或者设备接入时,需要对各个子设备进行初始化设置,使其处于正常工作状态,例如初始化传感器的参数、配置 ISP 的工作模式等。
- 使用方法:
- 在编写驱动程序时,首先要为每个 v4l2_subdev 子设备定义一个 v4l2_subdev_ops 结构体实例。这个实例就像是一个操作指南,明确了针对该子设备的各种操作函数。
- 为 v4l2_subdev_ops 结构体中的各个成员函数指针赋值,也就是实现具体的操作逻辑。例如,对于初始化函数,要编写代码完成子设备的硬件初始化、寄存器配置等工作;对于配置函数,要根据传入的参数对相应的子设备参数进行修改;对于控制函数,要实现启动、停止等操作的具体代码。
- 将定义好并赋值完成的 v4l2_subdev_ops 结构体实例与对应的 v4l2_subdev 子设备进行关联。这一步通常是通过设置 v4l2_subdev 结构体中的 ops 成员来完成的。关联之后,当需要对该子设备进行操作时,系统就会根据这个关联关系调用 v4l2_subdev_ops 中定义的相应函数。
- 示例:
- 以一个复杂的摄像头驱动程序为例,其中包含图像传感器和 ISP 这两个重要的子设备。
- 对于图像传感器子设备,定义一个 v4l2_subdev_ops 结构体实例 sensor_ops。在这个实例中,实现 init 函数用于对图像传感器进行初始化,比如设置传感器的分辨率、帧率等参数;实现 config 函数用于根据不同的拍摄场景(如白天、夜晚)动态调整传感器的曝光、增益等参数;实现 control 函数用于启动或停止传感器的数据采集。
3.V4L2对uvc免驱usb设备的编程框架主要采集USB摄像头。即V4L2(Video4Linux2)是一个用于Linux系统的视频设备驱动框架,它提供了一套API和接口,使得开发者可以方便地对视频设备进行编程操作。对于UVC(USB Video Class)免驱USB设备,如USB摄像头,V4L2提供了一种标准化的方式来采集视频数据。开发者可以利用V4L2提供的接口来打开设备、设置属性、采集数据等,而不需要为每个具体的设备编写特定的驱动程序。
4.采集方式:打开视频设备(设置视频设备属性:绽放、裁剪等)。在Linux编程中,直接使用ioctl()函数对设备I/O通道进行管理。
在Linux编程中,打开视频设备后,可以设置该视频设备的属性,例如裁剪、缩放等。这一步是可选的。在Linux编程中,一般使用ioctl函数来对设备的I/O通道进行管理。
具体解释如下:
打开视频设备:首先需要通过系统调用
open()
打开视频设备文件,例如/dev/video0
,这将返回一个文件描述符(fd)。设置视频设备属性:在打开视频设备后,可以设置该设备的一些属性,比如裁剪(crop)、缩放(scale)等。这些设置是可选的,可以根据具体需求来决定是否进行。
使用ioctl函数管理I/O通道:
ioctl()
函数是Linux系统中用于设备控制操作的系统调用,允许执行设备特定的操作。在视频采集过程中,ioctl()
函数常用于设置视频格式、查询设备能力、请求和释放缓冲区等操作。ioctl()
函数的原型如下:int ioctl(int fd, unsigned long request, ...);
fd
:设备文件描述符,由open()
函数返回。request
:具体的命令标志符,用于指定要执行的操作。...
:根据request
的不同,可能需要传递额外的参数。通过
ioctl()
函数,用户程序可以与内核中的设备驱动程序进行交互,从而实现对视频设备的精细控制。例如,可以使用VIDIOC_S_FMT
命令来设置视频格式,使用VIDIOC_REQBUFS
命令来请求帧缓冲区等。
5.V4L2操作流程:打开设备-->获取设备capability-->选择视频输入-->设置视频格式以及帧格式-->向驱动申请帧缓冲(不超过5个)-->申请物理内存-->开始视频采集-->出队列以取得已经采集数据的帧缓冲,获得原始采集数据-->停止视频采集-->关闭视频设备.