V4L2系列 之 V4L2驱动框架

前言

  本篇文章主要介绍Linux内核中的V4L2框架,本篇文章所用内核版本:linux-4.19

  v4L2 (Video for Linux 2),是linux的一套视频框架,共主体位于内核,可以理解为是整个linux系统上面的视频源捕获驱动框架。其广泛应用在嵌入式设备、移动端以及个人电脑设备上面,市而上使用视频图像采集的设备如:手机、IPC、行车记录仪都会用到这个框架来进行视频采集。

  v4L2允许 应用程序 控制图像传感器以及传输格式,应用程序 借此完成拍照、预览、视频记录等图像传感器数据应用。

  之前,Linux还存在第一版的V4L2,该版本在内核2.6.38版中放弃支持。

一、V4L2驱动框架概览

1、应用层 -》中间层-》驱动层

  linux采用多层次的驱动架构来对接口进行统一与抽象,最低层次的驱动总是直接面向硬件的,而最高层次的驱动被划分为字符、块、网络设备三大类,前两类驱动在文件系统中形成类似文件的“虚拟文件”,又称为“节点node",这些节点拥有不同的名称代表不同的设备,在目录/dev下进行统一管理,系统调用函数如open、close、read等也与普通文件的操作有相似之处,这种接口的一致性是由VFS(虚拟文件系统层)抽象完成的。

  V4L2是关于视频设备的中间驱动层,向上 为 应用程序 访问 视频设备提供了通用接口,向下为设备驱动程序开发提供了统一的V4L2框架。其视频设备节点路径通常为/dev中的videoX。V4L2驱动对用户空间提供“字符设备”的形式,主设备号为81,在用户空间通过各种ioctl调用进行控制,并且可以使用mmap进行内存映射。

V4L2支持多种设备,有以下接口:
√视频采集接口(video capture interface)
√视频输出接口(video output interface)
√直接传输视频接口(video overlay interface) 
√视频间隔消隐信号接口(VBI interface)
√收音机接口(radio interface).

在这里插入图片描述

	应用通过open、ioctl等系统调用操作video设备。在内核空间,Video设备的具体操作
方法由驱动中的struct video_device提供。

	驱动使用video_register_device函数将struct video_device注册到V4L2的核心
层,然后V4L2的核心层再向上注册一个字符设备。这样应用就可以使用系统调用访问虚拟
文件系统中Video设备提供的方法,然后进一步访问V4L2核心层提供的v4l2_fops方法集
合,最后通过struct video_device结构休中的fops和ioctl_ops方法集合访问Video
主设备。

	Video主设备通过V4L2_subdev_call方法访问Video从设备,同时Video从设备可以
通过notify回调方法通知主设备发生了事件。

在这里插入图片描述

2、主要代码文件(Linux 4.19版本内核)

  v4L2的驱动源码在kernel/drivers/media/v4l2-core目录下,主要代码文件有:

(1)v4l2-dev.c //视频设备硬件的操作,包含video_device的注册、释放等,主要包括
//以下函数:
	videodev_init
		register_chrdev_region
		class_register
	videodev_exit
		class_unregister
		unregister_chrdev_region
	__video_register_device
	video_unregister_device
	
(2)v4l2-common.c //一些通用操作,V4l2的子设备一般是摄像头和摄像头控制器,
//它们和主机的控制操作是通过i2c总线完成的。V4l2驱动框架中跟i2c相关的代码在
//v4l2_common.c中
	v4l2_ctrl_query_fill
	v4l2_i2c_subdev_init
	v4l2_i2c_new_subdev_board
	v4l2_i2c_new_subdev
	v4l2_i2c_subdev_addr
	v4l2_i2c_tuner_addrs
	v4l2_spi_subdev_init
	v4l2_spi_new_subdev
	clamp_align
	v4l_bound_align_image
	__v4l2_find_nearest_size
	v4l2_get_timestamp
	v4l2_g_parm_cap
	v4l2_s_parm_cap
	
(3)v4l2-device.c //V4L2的设备支持,主要是注册v4I2_device,包括以下函数:
	v4l2_device_register
	v4l2_device_unregister
	
	v4l2_device_put
	v4l2_device_release
	
	v4l2_device_register_subdev
	v4l2_device_unregister_subdev
	
	v4l2_device_register_subdev_nodes
	v4l2_device_release_subdev_node	

(4)v4l2-ioctl.c  //处理V4L2的ioctl命令的一个通用的框架。

(5)v412-subdev.c //v4l2子设备

(6)v4l2-mem2mem.c //使用videobuf缓冲区的设备辅助函数。

V4L2缓冲区管理

	V4L2维护着两个缓冲队列:一个用于驱动(INPUT队列),另一个用于用户程序
(OUTPUT队列)。
	缓冲区(由VIDIOC_REQBUFS命令申请)被用户空间的应用程序放入驱动的队
列中(通过VIDIOC_QBUF ioctl命令)以便填充数据。驱动按顺序填充缓冲区后,
缓冲区由INPUT队列放入OUTPUT队列。

	当用户程序调用VIDIOC_DQBUF命令后,驱动会在OUTPUT队列中寻找可用的
缓冲,如果可用则推送到用户程序,不可用则等待直到有可用缓冲后再推送给用
户程序。缓冲区使用完后,必须调用VIDIOC_QBUF将缓冲区重新加入INPUT队列
以便再次填充数据。

	注意驱动程序会独立自主的填充INPUT队列中的缓冲区,如果用户程序对缓
冲数据使用不及时,INPUT队列被填满,驱动暂停等待,会产生丢帧。

二、怎么写V4L2驱动

1、如何写一个设备的驱动?

  以字符设备驱动为例,请阅读我之前所写的一篇文章:字符设备驱动的三种实现方法

2、Video设备主要结构体

  struct v4l2_device:一个硬件设备可能包含多个子设备,比如一个电视除了有capture设备,可能还有VBI设备或者FM tunner。而v4l2_device就是所有这些设备的根节点,负责管理所有的子设备。

/ * *
*struct v4l2_device -用于V4L2设备驱动程序的主结构
*
* @dev:指向设备结构体的指针。
* @mdev:指向结构体media_device的指针,可以为NULL* @subdevs:用于跟踪已注册的子设备
* @lock:锁定这个结构体;如果该结构嵌入到一个更大的结构中,驱动程序也可以使用
* 该结构。
* @name:唯一的设备名称,默认为驱动器名称+总线ID
* @notify:通知进行了某个操作(某些子设备被调用)
* @ctrl_handler:控制处理程序。可能是NULL* @prio:设备的优先级状态
* @ref:跟踪对这个结构体的引用。
* @release:当ref计数变为0时调用的释放函数。
*
* V4L2设备的每个实例都应该创建v4l2_device结构体,无论是独立的还是嵌入到
* 更大的结构体中。
*
*它允许轻松访问子设备(参见V4L2 -subdev.h),并提供基本的V4L2设备级支持。
*
* . .注意::
*
* #) @dev->driver_data指向该结构体。
* #)如果没有父设备,@dev可能是%NULL
* /
struct v4l2_device {
	struct device *dev;
	struct media_device *mdev;
	struct list_head subdevs;
	spinlock_t lock;
	char name[V4L2_DEVICE_NAME_SIZE];
	void (*notify)(struct v4l2_subdev *sd,
			unsigned int notification, void *arg);
	struct v4l2_ctrl_handler *ctrl_handler;
	struct v4l2_prio_state prio;
	struct kref ref;
	void (*release)(struct v4l2_device *v4l2_dev);
};

  struct video_device:这个结构体的主要作用时提供/dev/videoX或/dev/v4l-subdevx设备节点,同时对捕获接口进行抽象,用来描述一个出帧的设备。另外,Video子设备也是继承自该结构体。该结构体包含指向v4l2_file_operations、v4l2_ioctl_ops等的操作对象指针。

/ * *
* struct video_device——用于创建和管理V4L2设备节点的结构。
*
* @entity: &struct media_entity
* @intf_devnode:指向&struct media_intf_devnode的指针
* @pipe: &struct media_pipeline
* @fops:指向视频设备的&struct v4l2_file_operations的指针
* @device_caps: v4l2_capabilities中使用的设备能力
* @dev: &struct设备用于视频设备
* @cdev:字符设备
* @v4l2_dev:指向struct v4l2_device父设备的指针
* @dev_parent:指向&结构设备父设备的指针
* @ctrl_handler:与该设备节点关联的控制处理程序。可能为NULL* @queue: &struct vb2_queue与该设备节点相关联。可能为NULL* @prio:指向带有设备优先级状态的struct v4l2_prio_state的指针。
* 如果为NULL,则使用v4l2_dev->prio。
* @name:视频设备名称
* @vfl_type: V4L设备类型,由&enum vfl_devnode_type定义
* @vfl_dir: V4L接收器、发射器或m2m
* @minor:设备节点“minor”。如果注册失败,则设置为-1
* @num:视频设备节点编号
* @flags:视频设备标志。使用bitops来设置/清除/测试标志。
* 包含一组&enum v4l2_video_device_flags* @index:属性用于区分一个物理设备上的多个索引
* @fh_lock:对所有v4l2_fhs进行锁
* @fh_list: struct v4l2_fh的列表
* @dev_debug:内部设备调试标志,驱动程序不使用
* @tvnorm:支持的电视规范
*
* @release:视频设备release()回调函数
* @ioctl_ops:带有ioctl回调函数的指向&struct v4l2_ioctl_ops的指针
*
* @valid_ioctls:该设备有效的ioctl的位图
* @lock:指向& &struct mutex 序列化锁的指针
*
* . .注意::
*只有在无法从@v4l2_dev推导出@dev_parent时才设置它。
* /
* 
struct video_device
{
#if defined(CONFIG_MEDIA_CONTROLLER)
	struct media_entity entity;
	struct media_intf_devnode *intf_devnode;
	struct media_pipeline pipe;
#endif
	const struct v4l2_file_operations *fops;

	u32 device_caps;

	/* sysfs */
	struct device dev;
	struct cdev *cdev;

	struct v4l2_device *v4l2_dev;
	struct device *dev_parent;

	struct v4l2_ctrl_handler *ctrl_handler;

	struct vb2_queue *queue;

	struct v4l2_prio_state *prio;

	/* device info */
	char name[32];
	enum vfl_devnode_type vfl_type;
	enum vfl_devnode_direction vfl_dir;
	int minor;
	u16 num;
	unsigned long flags;
	int index;

	/* V4L2 file handles */
	spinlock_t		fh_lock;
	struct list_head	fh_list;

	int dev_debug;

	v4l2_std_id tvnorms;

	/* callbacks */
	void (*release)(struct video_device *vdev);
	const struct v4l2_ioctl_ops *ioctl_ops;
	DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);

	struct mutex *lock;
};

3、怎么写V4L2驱动

(1)分配/设置/注册v4l2_device(调用函数注册v4l2_device_register),有辅助作用,提供自旋锁以及引用计数
(2)分配video_device:video_device_alloc()kzalloc()(3)设置video_device:.fops、.ioctl_ops、dev;
(4)注册video_device: video_register_device()

参考阅读:https://blog.csdn.net/seiyaaa/article/details/120199720
Video设备注册时的执行流程可总结如下:
1.设置设备注销时资源释放回调和v4l2_device结构体。

2.检查设备类型并确定设备节点基本名称。

3.设置设备类型、次设备号及设备节点数量。

4.将video_device结构体指针保存到全局video_device数组中。

5.根据设备类型验证哪那些ioctl函数可以使用。

6.分配字符设备结构体。

7.设置字符设备的操作函数集合为v4l2_fops。

8.将video设备注册为字符设备,并注册设备;

9.设置设备引用计数为0时的回调函数,该函数主要的工作是删除注册的字符设备,
回调v4l2_device中的release函数(通常是video_device_release函数)释放
video_device结构体内存,最后减少v4l2_device的引用计数;

10.增加video_device所属v4l2_device的引用计数。

11.设置已注册标志V4L2_FL_REGISTERED。

Video设备访问流程

(1)首先通过系统调用访问/dev/videox用户空间设备节点。

(2)进入到内核空间,访问字符设备struct file_operations中的方法。对于Video
设备,该操作集合被v4L2子系统初始化为v4l2_fops集合。

(3)通过v4L2子系统提供的v4l2_fops集合,可直接调用底层驱动实现的Video主设备
struct v4l2_file_operations方法,对于ioctl方法,则需要借助中间函数
__video_do_ioctl调用底层驱动实现的struct v4l2_ioctl_ops中的ioctl功能。
struct v4l2_file_operations方法和struct v4l2_ioctl_ops方法属于主设备
方法,需要主设备的驱动实现。

( 4) struct v4l2_file_operationsstruct v4l2_ioctl_ops中的函数都可以
通过v4l2_subdev_call调用video从设备struct v4l2_subdev_core_opsstruct
v4l2_subdev_video_opsstruct v4l2_subdev_pad_ops等方法,这些方法都要在
从设备驱动中实现。

三、V4L2的调试工具

1、v4l2-ctl

  为便于开发,常见的Linux发行版会附带一个v4l2-ctl的命令行工具,可以用来测试摄像头子系统。该工具可以列出系统内的设备列表,查询设备能力,调整设备属性以及设置像素格式、分辨率、帧率等,同时也可以执行捕捉图像等动作。

  参考阅读:v4l2-ctl基本使用方法

2、dev_debug

V4L2调试
由丁video系统的配置较复杂,为便于调试,V4L2提供了简单但庞大的用户空间调试手段,用于跟踪框架或用户空间API的信息。
椎架的调试信息可通过下述命令开启(通过dmesg查看):
# echo 0x3 > /sys/module/videobuf2_v4l2/parameters/debugt
# echo 0x3 > /sys/module/videobuf2_common/parameters/debug

V4L2的用户空问API跟踪通过下述命令开启:
# echo 0x3 >/sys/class/video4linux/video0/dev_debug

在这里插入图片描述

3、v4l2-compliance

  Video设备要工作正常,驱动兼容性是一个重要的方面,v4l2-compliance 工具可以通过测试V4L2设备的各个方面来判断其驱动兼容性。

  使用方法请阅读:v4l-utils之v4l2-compliance

  初步了解V4L2的驱动框架后,下一篇将以《虚拟摄像头驱动:drivers\media\platform\vivid》进行详细分析解读,进一步深入理解,敬请期待.

本文内容主要来自百度百科以及韦东山老师的课程笔记,如有侵权,联系删除!欢迎各位在评论区指导交流!!!

<think>好的,我现在要介绍V4L2驱动框架。首先,我需要确认自己对V4L2的理解是否正确。V4L2是Video for Linux 2的缩写,是Linux内核中负责视频设备驱动框架,主要用于摄像头、视频采集卡等设备。用户可能需要了解其架构、主要组件、工作流程以及应用场景。 接下来,我应该从基本概念入手,解释V4L2的作用和重要性。然后,需要分解其核心组件,比如设备节点、API接口、缓冲区管理等。可能还需要提到V4L2支持的设备类型,比如摄像头、收音机等,但重点在视频设备。 然后,驱动框架的结构是关键。用户可能想知道驱动开发者如何利用V4L2框架开发驱动程序,比如结构体v4l2_device、v4l2_subdev的作用,以及如何注册设备到内核。这里需要确保术语准确,比如video_device结构体、媒体控制器框架的整合。 另外,用户可能对V4L2的用户空间接口感兴趣,比如如何使用ioctl调用与驱动交互,常见的命令如VIDIOC_QUERYCAP、VIDIOC_REQBUFS等。但用户的问题是驱动框架介绍,所以这部分可能需要简要提及,重点放在内核驱动部分。 可能还需要比较V4L2和其他框架的区别,比如与DRM框架的不同应用场景。不过如果用户没有提到,可能不需要深入。 需要检查是否有遗漏的部分,比如V4L2的子设备(subdev)概念,以及管道配置、视频数据流的处理流程(如DMA缓冲区、内存映射等)。此外,媒体控制器在复杂设备中的作用也很重要,比如多子设备的管理。 需要确保技术细节准确,比如V4L2驱动中的关键结构体和函数,例如v4l2_file_operations、v4l2_ioctl_ops等。同时,要说明驱动开发的基本步骤,如初始化、注册设备、设置操作集、处理缓冲区等。 可能需要注意避免过于深入代码细节,而保持框架层面的介绍。同时,要组织内容结构清晰,分点说明,帮助用户逐步理解。 最后,检查是否有错误,比如V4L2是否支持音频设备(其实主要是视频,但可能包括相关音频流),或者是否所有视频设备都通过V4L2驱动。需要确认这些点,确保信息准确。</think>V4L2(Video for Linux 2)是 Linux 内核中用于支持视频和音频设备的标准框架,主要用于摄像头、采集卡、TV调谐器等设备。以下是其核心介绍: --- ### 一、框架定位 - **作用**:提供统一的用户空间 API 和内核驱动模型,管理视频输入/输出、流媒体和控制功能。 - **适用设备**:摄像头、视频采集卡、编解码器、收音机、SDR(软件定义无线电)等。 --- ### 二、核心组件 1. **设备节点** 用户通过 `/dev/videoX` 节点访问设备,每个节点对应一个视频设备。 2. **内核对象** - `struct video_device`:表示一个视频设备,关联操作函数集(`v4l2_file_operations`)。 - `struct v4l2_subdev`:描述子设备(如传感器、ISP),支持模块化硬件设计。 - `struct v4l2_device`:管理主设备与子设备的层级关系。 3. **缓冲区管理** - 支持多种内存类型:DMA 缓冲区(`DMABUF`)、用户空间内存(`USERPTR`)等。 - 关键机制:通过 `videobuf2` 框架实现高效数据流传输。 --- ### 三、驱动开发流程 1. **初始化** - 注册 `v4l2_device` 主设备。 - 创建并配置 `video_device`,绑定操作集(如 `v4l2_ioctl_ops`)。 2. **子设备管理** - 使用 `v4l2_subdev` 注册传感器、ISP 等子模块。 - 通过媒体控制器框架(Media Controller)建立硬件管道链接。 3. **流控制** - 实现缓冲区申请(`VIDIOC_REQBUFS`)、启停流(`VIDIOC_STREAMON/OFF`)。 - 处理数据流中断或 DMA 传输。 --- ### 四、用户空间交互 - **IOCTL 命令**:用户通过 `ioctl()` 调用控制设备,例如: ```c VIDIOC_QUERYCAP // 查询设备能力 VIDIOC_S_FMT // 设置视频格式 VIDIOC_REQBUFS // 申请缓冲区 ``` - **数据流模式**:支持 `mmap()` 内存映射或 `read()` 直接读取。 --- ### 五、典型应用场景 1. **摄像头采集** 通过 `Video Capture` 接口获取图像数据。 2. **视频输出** 使用 `Video Output` 接口驱动显示器或编码器。 3. **复杂媒体链路** 结合媒体控制器配置多设备管道(如:传感器 → ISP → 编码器)。 --- ### 六、与其他框架的关系 - **与 DRM 框架**:DRM 负责显示输出,V4L2 负责采集输入。 - **与 Media Controller**:用于管理多子设备拓扑结构。 --- ### 七、示例驱动结构 ```c static struct video_device my_vdev = { .name = "my_camera", .fops = &my_fops, .ioctl_ops = &my_ioctl_ops, }; static int __init my_driver_init(void) { v4l2_device_register(NULL, &my_v4l2_dev); video_register_device(&my_vdev, VFL_TYPE_VIDEO, -1); // 注册子设备与配置媒体链路 } ``` --- 通过上述分层设计,V4L2 实现了硬件无关性,驱动开发者只需关注硬件特有操作,用户空间则可跨设备调用统一 API。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小嵌同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值