V4l2框架分析

V4l2框架分析V4L2 是 Videoforlinu 的简称 为 linux 中关于视频设备的内核驱动

大家好,欢迎来到IT知识分享网。

原文链接:https://blog.csdn.net/welljrj/article/details/

1.V4L2框架概述

V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写。V4L2在设计之初时,是要支持很多广泛的设备的,如声卡, display, FB, I2C, camera等.它们之中只有一部分在本质上是真正的视频设备,也是造成V4l2源码冗余的原因之一。

kernel source code更新速度快,在display新增drm框架, 声卡上有ALSA框架. 目前V4L2主要用于camera驱动,本文也是通过camera驱动讲解V4l2内部原理。

1.1 v4l2设备应用层流程

注册的设备节点有/dev/video和/dev/v4l2-subdev。

应用层操作video设备主要流程如下:

1. 通过打开video设备设置video参数。 2. 设置采集方式。 3. 将数据取出,处理,放回, 可循环处理。 4. 完成相应的任务后关闭。 

1.2 内核V4L2模块

1.2.1 video_device
用于实例化一个/dev/video设备的结构体。里面包含该video的类型, 回调函数,以及操作缓冲的队列。 接触内核v4l2驱动, 理解video_device结构体内部很重要。

1.2.2 v4l2_subdev
用于实例化一个/dev/subdev 设备的结构体。 一般只需通过ioctl设置采样属性即可。 内部实现部分v4l2_subdev_ops回调函数, 也可以用与其他驱动模块通讯.

1.2.3 videobuf2
用于video缓存的分配,释放,出队,入队等。提供多种缓存类型管理。

2. video_device结构体

video_device 即注册 /dev/video# 设备的结构体, 应用层需要使用camera的时候open 与ioctl它。 下面分析video_device的数据结构, 以及内核注册流程。video_device 结构体定义位于v4l2_dev.h

注册video_device代码一般位于 driver/media/platform/[soc 对应的图像处理模块]。

2.1 图像处理模块

这里说图像处理模块为SOC内部集成的一个针对Camera数据信号进行图像处理的硬件模块, 在不同芯片厂商中而不同, 如:i.mx6q 为IPU, i.mx8q为ISI, renesas rcar-m3为VIN。主要作用是接收将CSI 或者 parallel digital信号,并进行图像数据进行格式转换,缩放,色域转换,DMA通道管理等, 然后映射到相应的内存上, 给用户使用。

2.2 video_device处理流程

当总线检测到图像处理设备后,便会进入该设备driver中的probe函数, 在probe函数调用video_register_device注册video_device, 注册前需要先初始化video_device结构体内部成员。

2.2.1 video_device 结构体成员介绍:
① video_device->v4l2_dev: 必须的设置成V4l2_device父设备,该父设备通过v4l2_device_register函数初始化

② video_device->name: 设置一个唯一设备名描述符

③ video_device->fops: 设置成v4l2_file_operations 结构体。 图像处理video驱动一般只实现open release 成员函数,其他交给 V4l2-core 实现。

static const struct v4l2_file_operations rvin_fops = { 
    .owner = THIS_MODULE, .unlocked_ioctl = video_ioctl2, .open = rvin_open, // soc 驱动实现 .release = rvin_release, // soc 驱动实现 .poll = vb2_fop_poll, //videobuf2内部实现 .mmap = vb2_fop_mmap, //videobuf2内部实现 .read = vb2_fop_read, //videobuf2内部实现 }; 

④ video_device->ioctl_ops: 设置成v4l2_ioctl_ops结构, 该结构体成员为应用层操作video_device_fd句柄的ioctl(VIDEOC_XXX) ,所回调的函数指针。 我们配置/dev/video主要通过使用ioctl(VIDEOC_XXX), 命令宏如:VIDIOC_S_FMT, VIDIOC_G_FMT, VIDIOC_REQBUFS, VIDIOC_QUERYBUF, VIDIOC_DQBUF, VIDIOC_STREAMOFF。

对应v4l2_ioctl_ops内部的函数指针成员为:

⑤ video_device->queue: 指向该video设备节点的vb2_queue指针。 很关键的一个成员,与缓冲数据操作有关。 详细分析在下一节。

3. video_buf2

videobuf2 是v4l2驱动一个重要组成部分, 用来管理和存储video帧缓存。

3.1 与video device的关系

① video_device结构体内部有vb2_queue结构体成员指针。

② 在应用层操作/dev/video# 句柄的函数read, poll, mmap(),ioctl等,即与帧缓存相关的函数。涉及到videobuf2。

③ video_device结构体内部有 v4l2_ioctl_ops结构体, v4l2_ioctl_ops内部函数成员的实现在V4l2-core中的videobuf2-v4l2.c 。 在videobuf2-core.c中又会回调平台图像处理器驱动实现vb2_ops。

在这里插入图片描述

3.2 Buffer 类型

3.2.1 vmalloc
使用虚拟连续内存. 完全不需要考虑内存分配问题. videobuf2内部完成所有细节。 但是性能最低。

3.2.2 Physically contiguous dma
物理地址连续内存传输,传输完一个block物理地址连续内存后,发生中断。 大多数capture中使用,使用较为简单,性能中等。

3.2.3 Physically scattered
一种特殊类型的流DMA映射机制–发散/汇聚映射。该机制允许一次为多个缓冲区创建DMA映射,它是用一个链表描述物理不连续的存储器,然后把链表首地址告诉dma master。dma master传输完一块物理连续的数据后,就不用再发中断了,而是根据链表传输下一块物理连续的数据。性能最高,实现起来复杂。

3.2.4 如何区分
通过调用的以下那个头文件, 即可查出soc V4l2使用的内存类型…

<media/videobuf-dma-sg.h> /* Physically scattered */

<media/videobuf-vmalloc.h> /* vmalloc() buffers */

<media/videobuf-dma-contig.h> /* Physically contiguous */

3.3 vb2_ops

提供 videobuf2-core 回调的函数的结构体体vb2_ops 

3.3.1 vb2_ops 结构体
上文提到v4l2-core的videobuf2-core.c中,需要回调图像处理驱动中的实现的函数。 这些函数在vb2_ops结构体中, 如下图所示:

static const struct vb2_ops rvin_qops = { 
    .queue_setup = vin_queue_setup, .buf_prepare = vin_buffer_prepare, .buf_queue = vin_buffer_queue, .start_streaming = vin_start_streaming, .stop_streaming =vin_stop_streaming, .wait_prepare = vin_ops_wait_prepare, .wait_finish = vin_ops_wait_finish, }; 

3.3.2 videobuf2-core回调vb2_ops
call_qop(q, queue_setup…);
call_vb_qop(vb, buf_prepare, vb);
call_void_vb_qop(vb, buf_queue, vb);
call_qop(q, start_streaming, …);
call_void_qop(q, stop_streaming, q);
call_void_qop(q, wait_prepare, q);
call_void_qop(q, wait_finish, q);






3.4 mmap

  • 应用层调用mmap 后,系统调用sys_mmap。 v4l2-core调用vdev->fops->mmap. vdev
    是由soc平台驱动注册。
  • 在soc驱动中, 将V4l2-core 中的videobuf2-v4l2中的vb2_fop_mmap赋值给
    V4l2_fops->mmap。 vb2_fop_mmap调用了videobuf2-core中的vb2_mmap
  • vb2_mmap 又会调用call_memop(vb,mmap,…)。
    call_memop中有的需要在soc驱动中的初始化vb2_queue->mem_ops为vb2_dma_contig_memops。
    vb2_dma_contig_memops中的vb2_dc_mmap即为最终回调。 之后操作vm_area

    小结: 上面的mmap流程分析, 可谓三进三出。 v4l2-core需要调用soc驱动注册的video device以及回调其内部实现的函数, 而soc 驱动初始化时有需要v4l2-core定义好的好的结构体和成员函数。 随着内部成员的增多,功能多样化。这要来来回回的调用必定增多,所以分析起来也很复杂。

4. subdev

4.1 概念

在camera驱动中,subdev一般指与video device 相关的外围senor子设备, 可以是csi接口,fpdlink,camera内部isp等。大部分为I2c_subdev。 subdev可用于应用层的调用,以及驱动间的交互。 video device 可组织和控制V4l2_subdev。

4.2 subdev注册流程

4.2.1 v4l2_subdev结构体
每个subdev驱动必须实现一个v4l2_subdev结构体, 这个结构体可以单独存在,或者嵌入到其他更大的结构体中,如果该subdev需要储存更多信息。

如果集成了media framework, 必须通过media_entity_pads_init()初始化media_entity,作为media framework 构建单元。 而且subdev驱动还需实现v4l2_subdev_video_ops的v4l2_subdev_pad_ops。 用于 ioctl 的handler,或者其他设备驱动回调。 部分回调函数对应如下:

在这里插入图片描述

v4l2_subdev_video_ops与 video device 中v4l2_ioctl_ops结构体内部成员有很多看上去相似的地方。 确实在v4l2头文件中, 许多 V4l2_xxx_ops结构体成员看上去类似, 再加上这些ops 要被其他driver或者user 的调用, 如果是新手, 很容易混淆。

那么如何理解V4l2_xxx_ops中的逻辑?

① 得区分它是那个subdev的 ops。 可以通过打印subdev指针区分。

② 分析这个ops的功能。 是set还是get? ops是否对sensor的寄存器更改了? 还是没有进行任何操作。

③ 然后在分析它的调用逻辑。 执行完之后, 成功和错误分别会执行些什么?

④ 最好做个笔记, 描述某些特殊情况, 例如某些图像处理器无法处理某些特定格式。

4.2.2 v4l2_subdev注册函数调用
① 给包含v4l2_subdev的数据结构kzalloc()一片内存出来

② 调用v4l2_subdev_init函数初始化V4l2_subdev结构体成员

③ v4l2_async_register_subdev 异步注册, 没有真正的注册. 缓存到某个变量中

⑤ 主设备调用组织subdev后,调用v4l2_device_register_subdev_nodes函数真正注册subdev节点

那么主设备是如何组织subdev的呢? 这事就需要 media framework, V4L2 asynchronous 模块, V4L2 fwnode绑定分析模块辅助,以及设备树。 逻辑复杂,第6节v4l2设备节点组织流程中有分析。

4.3 应用层操作subdev

小结: V4l2_subdev是video设备的一个子设备, 在操作video时,一般需要先配置它,或者获取它的参数。 subdev驱动如果在probe初始化或者定义回调函数时有问题,导致主设备video 也无法正常工作。

5. media framework

5.1 概念

Media framework的目标之一构建媒体设备内部的拓扑, 并且在运行时配置该设备。 V4L2为了实现它, 将与V4L2 相关的设备抽象化成一个个media entity, 然后通过pad 将 entity 连接(link) 起来, 组成一个media pipeline。 然后使pipeline可控化。

① Entity

是一个多媒体硬件设备的基础构块。它可以虚拟的设备为:cmos senor外围设备, soc 图像处理模块, dma通道,或者硬件接口(CSI, parallel)。

② Pad

是一个可以使一个或者多个entity交互的可连接端点,单独存在没有意义,可以理解为一个硬件设备对外的接口。

③ Link

是两个pads的有向连接,并且是点对点的

④ pipeline

这样说还是有点抽象,下面以adv7482 sensor 外设作为例子来说明。

简单介绍以下内部的模块, 左侧有输入接口如: AIN1-AIN8为模拟输入接口, RX0~RX2为hdmi差分输入接口. 右侧有CSI-2 transmiter A, CSI-2 transmiter B输出接口. 内部模块负责信号检测,模数转换, 始终管理, 色域转换等.

这种sensor controller 可以抽象为多个subdev 负责不同模块之间的处理, 再与SOC 内部模块交互,组成可用的一个可用video节点. 但是单纯控制subdev没有数据流的概念,很难分析逻辑层次和拓扑关系, 这时就需要media framework 抽象化这些设备的拓扑,组成一个media graph.

adv7482与renesas rcar-m3 SOC组成的media graph, 如下图所示:

在这里插入图片描述

media pipeline 是在media graph 选出来的一个通道, 可以这么理解media graph是一张地图, pipeline就是其中的一条规划好的路线.

在驱动实现中,一个由三个media device: adv7482 sensor , SOC中的CSI, SOC 图像处理模块VIN[0-7]. 组成的pipeline如下:

当然pad 与entity 的属性还可以设置其他不同的值,这样link其他可以组成非常多的不同形式pipeline, 如:

①ADV7186可以选择8个camera模拟信号输入通道

②SOC中的CSI模块到VIN模块有4个DAM channel

③SOC VIN模块可以设置不同的视频数据格式

不同平台的SOC, 图像处理模块设计不同,media graph也不一样.具体详见下一节v4l2设备节点组织流程中有分析.

6. v4l2设备节点组织流程

在media framework一文中,应用层根据不同需要, 可以通过设置media entity属性,组织不同的模块,构建一个media pipeline.

media pipeline 是在media control graph 选出来的一个通道, 可以这么理解media graph是一张地图, pipeline就是其中的一条规划好的路线. 而media control graph需要 内核组织media device节点去构建.在V4L2中, 需要 media framework, V4L2 asyn模块, 以及设备树等模块构建media control graph. 下面是V4l2设备节点组织图.

在这里插入图片描述

6.1 设备树解析

设备树通过endpoint组织每一个点到点的连接.

6.2 驱动组织流程

6.2.1 sensor驱动异步注册流程
在sensor ti 964驱动的ds90ub964_probe函数中,解析设备树获取 ti964 endpoint, 异步注册subdev.下面分析流程:

① 通过解析device tree 中endpoint获取fwnode赋值给sd->fwnode. fwnode成员是用来做match的,在SOC中的CSI驱动中会分析到.

在这里插入图片描述

② 初始化sd->media_entity,设置flag为 MEDIA_PAD_FL_SOURCE. 有source就需要sink,这个是gstream的概念, source与sink分别类似于camera与screen.

在这里插入图片描述

6.3 驱动注册流程分析

在SOC CSI的驱动的rcsi2_probe函数中. CSI 解析设备树获取 ti964 endpoint, 并通过匹配sd->fwnode

6.3.1 通过 fwnode_graph_get_remote_endpoint获取ti964 的endpoint
在这里插入图片描述

6.3.2 调用__v4l2_async_subdev_notifier_register 注册notifier.

在这里插入图片描述

6.3.3 v4l2_async_notifier_try_all_subdevs 流程分析
在这里插入图片描述

① 遍历静态链表头subdev_list.

② v4l2_async_find_match查找match到的asd

i. v4l2_device_register_subdev调用subdev驱动中实现的registered函数: sd->internal_ops->registered

ii. v4l2_async_notifier_call_bound调用驱动中实现的bound函数:notifier->ops->bound

CSI driver 实现为:rcar_csi2_notify_bound

iii. 查找该sub-device的notifier

iv. 又调用v4l2_async_notifier_try_all_subdevs是不是晕了? 两个函数相互调用!!!

最后分析下 __v4l2_async_subdev_notifier_register 中 v4l2_async_notifier_try_complete

流程:

v4l2_async_notifier_try_complete

-> v4l2_async_notifier_call_complete

小结: __v4l2_async_subdev_notifier_register跟之前的v4l2_async_register_subdev 逻辑很相似,再加上两个函数相互调用,这也是V4L2-async容易搞混淆的地方.

6.4 video注册设备节点流

与csi异步机制机制一样vin probe中注册一个async_notifier

6.4.1 Video 解析整个endpoint

video驱动解析设备树遍历CSI endpoint,从probe开始如下:

rcar_vin_probe

-> rvin_group_init

->rvin_group_graph_parse

这里很多逻辑涉及到SOC的图像处理模块与CSI接口模块的之间硬件通道,不同的soc逻辑差异很大. 但一般都是通过设备树节点,获取subdev对应的entity,组成一个 media graph.

6.4.2 v4l2_async_notifier_operations回调
在这里插入图片描述

6.4.3 match到相应的subdev 后回调v4l2_async_notifier_operations 中的bound 和 complete

 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 

原文链接:https://blog.csdn.net/welljrj/article/details/

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/120713.html

(0)
上一篇 2025-10-27 22:20
下一篇 2025-10-27 22:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信