1 Studying《Linux Media Documentation》
目录
1 MEDIA SUBSYSTEM ADMIN AND USER GUIDE
1.1.2 Building support for a media device
1.1.3 Infrared remote control support in video4linux drivers
1.1.4 Digital TV
1.1.5 Cards List
1.1.5.2 PCI drivers
1.1.6 Video4Linux (V4L) driver-specifific documentation
1.1.6.2 The cafe_ccic driver
1.1.6.7 i.MX Video Capture Driver
1.1.6.8 i.MX7 Video Capture Driver
1.1.6.9 Intel Image Processing Unit 3 (IPU3) Imaging Unit (ImgU) driver
1.1.6.10 The ivtv driver
1.1.6.11 Vaio Picturebook Motion Eye Camera Driver
1.1.6.14 Philips webcams (pwc driver)
1.1.6.15 Qualcomm Camera Subsystem driver
1.1.6.17 Rockchip Image Signal Processor (rkisp1)
1.1.6.19 The Silicon Labs Si470x FM Radio Receivers driver
1.1.6.19 The Silicon Labs Si470x FM Radio Receivers driver
1.1.6.20 The Silicon Labs Si4713 FM Radio Transmitter Driver
1.1.6.22 The Virtual Media Controller Driver (vimc)
1.1.6.23 The Virtual Video Test Driver (vivid)
2 MEDIA SUBSYSTEM KERNEL INTERNAL API
2.2.4 Video device‘s internal representation
2.2.5 V4L2 device instance
2.2.6 V4L2 File handlers
2.2.7 V4L2 sub-devices
2.2.8 V4L2 sub-device userspace API
2.2.11 V4L2 sub-device functions and data structures
2.2.12 V4L2 events
2.2.13 V4L2 Controls
2.2.13.4 Inheriting Sub-device Controls
2.2.13.10 Handling autogain/gain-type Controls with Auto Clusters
2.2.13.17 v4l2_ctrl functions and data structures
2.2.14 Videobuf Framework
2.2.15 V4L2 videobuf2 functions and data structures
Linux媒体文档是涉及到Linux内核中的媒体子系统的一系列文档,旨在帮助开发人员和用户理解和使用该子系统。这些文档包括以下内容:
1. 媒体子系统文档:详细说明媒体子系统的体系结构、编解码器、设备驱动程序、API、架构实现等方面。
2. 设备驱动程序文档:提供有关特定设备的信息,例如数字电视卡、摄像头等,以便开发人员能够正确地编写设备驱动程序。
3. API文档:描述与媒体子系统相关的API函数和数据结构,并提供示例代码。
4. 用户指南:详细说明如何使用Linux媒体子系统,以及如何配置和启动媒体流应用程序。
5. 示例代码:提供实现不同功能的示例代码,有助于开发人员更好地理解和使用媒体子系统。
这些文档通常可在Linux内核源代码的Documentation/media目录下找到。另外,也可以在项目网站上查找相关文档,例如在V4L2和DVB项目网站上。
V4L2:
Linux Kernel Media Documentation — The Linux Kernel documentation
DVB:
Index of /docs
Linux Media Documentation:
https://download.csdn.net/download/u012906122/87743030
1 MEDIA SUBSYSTEM ADMIN AND USER GUIDE
这一部分包含了有关媒体子系统及其支持的驱动程序的使用信息。请参阅以下内容:
Documentation/userspace-api/media/index.rst
• 用于媒体设备上使用的用户空间API。
Documentation/driver-api/media/index.rst
• 提供有关驱动程序开发信息和用于媒体设备的内核API。
1.1 The media subsystem
1.1.1 Introduction
媒体子系统包含对多种不同类型设备的Linux支持,其中包括:
• 音频和视频采集设备;
• 个人电脑和笔记本电脑摄像头;
• 嵌入式硬件中的复杂相机;
• 模拟和数字电视;
• HDMI消费电子控制(CEC);
• 多点触控输入设备;
• 遥控器;
• 媒体编解码器。
由于设备的多样性,媒体子系统提供了几种不同的API:
• 遥控器API;
• HDMI CEC API;
• Video4Linux API;
• 媒体控制器API;
• Video4Linux请求API(实验性);
• 数字电视API(也称为DVB API)。
1.1.2 Building support for a media device
第一步是下载内核源代码,可以通过特定于发行版的源文件或内核的主git树下载。
然而需要注意的是,如果:
• 你是个冒险家,想尝试新的东西;
• 如果你要报告一个错误;
• 如果你正在开发新的补丁。
你应该使用主媒体开发树的主分支:
https://git.linuxtv.org/media_tree.git/
在这种情况下,您可以在LinuxTv维基页面上找到一些有用的信息:
How to Obtain, Build and Install V4L-DVB Device Drivers - LinuxTVWiki
1.1.2.1 Configuring the Linux Kernel
你可以通过以下方式访问内核构建选项菜单:
$ make menuconfig
然后,选择所有想要的选项并退出,保存配置。
更改后的配置将在.config文件中。它看起来像:
...# CONFIG_RC_CORE is not set# CONFIG_CEC_CORE is not setCONFIG_MEDIA_SUPPORT=mCONFIG_MEDIA_SUPPORT_FILTER=y...
媒体子系统由那些菜单配置选项所控制:
Device Drivers ---><M> Remote Controller support --->[ ] HDMI CEC RC integration[ ] Enable CEC error injection support[*] HDMI CEC drivers ---><*> Multimedia support --->
“Remote Controller support”选项启用了对远程控制器的核心支持;
“HDMI CEC RC integration”选项启用了HDMI CEC与Linux的集成,允许通过HDMI CEC接收数据,就像它是由直接连接到机器上的遥控器产生的一样。
“HDMI CEC drivers”选项允许选择通过HDMI接口接收和/或传输CEC代码的平台和USB驱动程序。
最后一个选项(多媒体支持)启用了对摄像头、音视频捕捉设备和电视的支持。
媒体子系统支持可以与主内核一起构建,也可以作为模块构建。对于大多数用例,最好将其构建为模块。
注意:内核不需要使用菜单提供了一个脚本,允许直接启用配置选项。要使用内核模块启用媒体支持和远程控制器支持,您可以使用以下命令:
$ scripts/config -m RC_CORE$ scripts/config -m MEDIA_SUPPORT
Media dependencies
需要注意的是,从干净配置启用上述选项通常是不够的。媒体子系统依赖于其他几个Linux核心支持才能工作。
例如,大多数媒体设备使用串行通信总线与某些外围设备进行通信。这种总线称为I2C(Inter-Integrated Circuit)。为了能够构建对这种硬件的支持,应该启用I2C总线支持,可以通过菜单或以下命令来实现:
./scripts/config -m I2C
另一个例子:远程控制器核心需要输入设备支持,可以通过以下命令启用其支持:
./scripts/config -m INPUT
根据您要启用的特定驱动程序,可能还需要其他的核心功能(例如PCI和/或USB支持)。
Enabling Remote Controller Support
远程控制器菜单允许选择特定设备的驱动程序。其菜单如下所示:
--- Remote Controller support<M> Compile Remote Controller keymap modules[*] LIRC user interface[*]Support for eBPF programs attached to lirc devices[*] Remote controller decoders --->[*] Remote Controller devices --->
“LIRC用户界面”选项增加了在使用lirc程序时的增强功能,通过启用API,允许用户空间从远程控制器接收原始数据。
“支持附加到lirc设备的eBPF程序”选项允许使用特殊的程序(称为eBPF),这些程序将允许应用程序向Linux内核添加额外的远程控制器解码功能。
“远程控制器解码器”选项允许选择Linux内核将识别的协议。除非您要禁用某个特定解码器,否则建议保持所有子选项启用。
“远程控制器设备”选项允许您选择需要支持您的设备所需的驱动程序。
同样的配置也可以通过脚本/配置脚本来设置。例如,为了支持ITE远程控制器驱动程序(在Intel NUC和一些ASUS x86桌面上找到),您可以执行以下操作:
$ scripts/config -e INPUT$ scripts/config -e ACPI$ scripts/config -e MODULES$ scripts/config -m RC_CORE$ scripts/config -e RC_DEVICES$ scripts/config -e RC_DECODERS$ scripts/config -m IR_RC5_DECODER$ scripts/config -m IR_ITE_CIR
Enabling HDMI CEC Support
当驱动程序需要时,HDMI CEC支持会自动设置。因此,您只需要为需要支持它的显卡或现有的HDMI驱动程序之一启用支持即可。
专门的HDMI驱动程序可在HDMI CEC驱动程序菜单中找到:
--- HDMI CEC drivers< > ChromeOS EC CEC driver< > Amlogic Meson AO CEC driver< > Amlogic Meson G12A AO CEC driver< > Generic GPIO-based CEC driver< > Samsung S5P CEC driver< > STMicroelectronics STiH4xx HDMI CEC driver< > STMicroelectronics STM32 HDMI CEC driver< > Tegra HDMI CEC driver< > SECO Boards HDMI CEC driver[ ]SECO Boards IR RC5 support< > Pulse Eight HDMI CEC< > RainShadow Tech HDMI CEC
Enabling Media Support
“媒体”菜单的选项比“远程控制器”菜单的选项更多。一旦选择了“媒体”菜单,您应该会看到以下选项:
--- Media support[ ] Filter media drivers[*] Autoselect ancillary driversMedia device types --->Media core support --->Video4Linux options --->Media controller options --->Digital TV options --->HDMI CEC options --->Media drivers --->Media ancillary drivers --->
除非您确切知道自己在做什么,或者想要为SoC平台构建驱动程序,否则强烈建议保持“自动选择辅助驱动程序”选项打开,因为它将自动选择所需的I2C辅助驱动程序。
现在有两种选择媒体设备驱动程序的方法,如下所述。
Filter media drivers menu
此菜单旨在为PC和笔记本电脑硬件提供简单的设置。它通过让用户指定所需的媒体驱动程序类型来工作,其中包括以下选项:
[ ] Cameras and video grabbers[ ] Analog TV[ ] Digital TV[ ] AM/FM radio receivers/transmitters[ ] Software defined radio[ ] Platform-specific devices[ ] Test drivers
因此,如果您只想添加对摄像头或视频抓取设备的支持,只需选择第一个选项即可。允许选择多个选项。
一旦选择了此菜单上的选项,构建系统将自动选择所需的核心驱动程序以支持所选的功能。
注意:大多数电视卡都是混合的:它们支持模拟电视和数字电视。
如果您有混合卡,则可能需要在菜单中同时启用模拟电视和数字电视。
使用此选项时,媒体支持核心功能的默认设置通常足以为驱动程序提供基本功能。然而,您可以使用以下每个“媒体支持”子菜单下的设置手动启用一些所需的额外(可选)功能:
Media core support --->Video4Linux options --->Media controller options --->Digital TV options --->HDMI CEC options --->
选择所需的过滤器后,与过滤条件匹配的驱动程序将在“媒体支持”->“媒体驱动程序”子菜单中可用。
Media Core Support menu without filtering
如果禁用“过滤媒体驱动程序”菜单,则应显示所有可用于您的系统且其依赖项已满足的驱动程序在“媒体驱动程序”菜单中。
但是请注意,您首先必须确保“媒体核心支持”菜单具备驱动程序所需的所有核心功能,否则相应的设备驱动程序将不会显示。
Example
为了启用模块化媒体核心模块的其中一个在此表中列出的板子的支持,`.config`文件应该包含以下行:
CONFIG_MODULES=yCONFIG_USB=yCONFIG_I2C=yCONFIG_INPUT=yCONFIG_RC_CORE=mCONFIG_MEDIA_SUPPORT=mCONFIG_MEDIA_SUPPORT_FILTER=yCONFIG_MEDIA_ANALOG_TV_SUPPORT=yCONFIG_MEDIA_DIGITAL_TV_SUPPORT=yCONFIG_MEDIA_USB_SUPPORT=yCONFIG_VIDEO_CX231XX=yCONFIG_VIDEO_CX231XX_DVB=y
1.1.2.2 Building and installing a new Kernel
一旦.config文件中拥有了所需的所有内容,构建的关键步骤只需要运行make命令:
$ make
然后安装新内核和模块:
$ sudo make modules_install$ sudo make install
1.1.2.3 Building just the new media drivers and core
从开发树运行新的开发内核通常比较危险,因为它可能有实验性的更改,可能会存在一些bug。因此,有一些方法可以使用替代树来仅构建新驱动程序。
有一个名为Linux内核后向移植项目(Linux Kernel backports project),其中包含了新的驱动程序,面向稳定内核进行编译。
Driver Backports Wiki
LinuxTV开发人员负责维护媒体子系统,他们还维护了一个后向移植树,其中只包含每天从最新内核更新的媒体驱动程序。这样的树可在以下网址获取:media_build.git - Build system to compile media subsystem on legacy kernels
需要注意的是,虽然使用 `media_build` 树进行测试目的应该相对安全,但不能保证它能在任意内核上工作(甚至能否成功构建)。这个树是基于“尽力而为”的原则进行维护,只要时间允许我们修复其中的问题。
如果您在使用过程中发现了任何问题,请随时将补丁提交到Linux媒体子系统的邮件列表:media@vger.kernel.org。如果您提交了一个新的媒体构建补丁,请在电子邮件主题中添加`[PATCH media-build]`。
在使用之前,您需要运行以下命令:
$ ./build
注意:
1)如果`media-build`树被更新,您可能需要运行两次。
2)如果您曾经为不同于当前使用的内核版本构建过它,则需要使用`make distclean`进行清理。
3)默认情况下,它将使用与您正在运行的内核相同的配置选项。
为了选择不同的驱动程序或不同的配置选项,请使用:
$ make menuconfig
然后,你可以编译并安装新内核:
$ make && sudo make install
这会覆盖先前内核使用的媒体驱动程序。
1.1.3 Infrared remote control support in video4linux drivers
红外遥控器支持
Authors: Gerd Hoffffmann, Mauro Carvalho Chehab
1.1.3.1 Basics
大多数模拟和数字电视板都支持遥控器。其中有几个电视板带有微处理器,用于接收红外线载波并将其转换为脉冲/空间序列,然后扫描代码,并将这些代码返回到用户空间(“扫描代码模式”)。其他电视板只返回脉冲/空间序列(“原始模式”)。
扫描代码模式下的遥控器支持是由标准的 Linux 输入层提供的。关于原始模式下的遥控器支持则通过 LIRC 提供。
为了检查支持并测试它,建议下载 v4l-utils 工具集。v4l-utils 提供了两个工具来处理遥控器:
• ir-keytable:提供一种查询遥控器、列出其支持的协议、启用内核中的 IR 解码器支持或切换协议以及测试扫描代码接收的方式;
• ir-ctl:提供按照 LIRC 接口支持原始模式的遥控器的工具。
通常情况下,当检测到 TV 卡时,遥控器模块会自动加载。然而,对于一些设备,您需要手动加载 ir-kbd-i2c 模块。
v4l-utils.git - media (V4L2, DVB and IR) applications and libraries
1.1.3.2 How it works
遥控器模块会将遥控器注册为键盘在Linux输入层中进行识别,也就是说,您可以将遥控器的按键看作普通的按键(如果启用了CONFIG_INPUT_KEYBOARD)。
使用事件设备(CONFIG_INPUT_EVDEV),应用程序可以通过/dev/input/event<n>设备访问遥控器。udev/systemd 将自动创建这些设备。如果您安装了 v4l-utils,它可能会自动加载不同于默认的 keytable。请参阅 v4l-utils ir-keytable.1 的 man 页面以获取详细信息。
ir-keytable 工具非常适合进行故障排除,例如检查输入设备是否真正存在,确定它是哪个设备,检查按遥控器键是否会产生事件等。您也可以使用任何其他更改按键映射的输入实用程序,例如输入 kbd 实用程序。
Using with lircd
最新版本的 lircd 守护程序支持通过事件设备从 Linux 输入层读取事件。它还支持在 lirc 模式下接收 IR 代码。
Using without lircd
Xorg 可以识别数值小于 247 的多个 IR 按键代码。随着 Wayland 的出现,输入驱动程序也得到了更新,并且现在应该接受所有键码。然而,您可能希望将键码重新分配为您喜欢的媒体应用程序所支持的内容。
可以通过在运行时设置 v4l-utils 来加载自己的 keytable 来实现这一点。请参阅 ir-keytable.1 的 man 页面获取详细信息。
1.1.4 Digital TV
1.1.4.1 Using the Digital TV Framework
Introduction
数字电视和模拟电视之间一个重要的区别是,尽管 DVB-T 卡的组件结构与模拟电视卡基本相似,但它们的功能方式却大不相同。这一点可能会让像我这样的不谨慎的人疏忽。
模拟电视的目的是接收和显示模拟电视信号。模拟电视信号(也称为复合视频)是通过交织技术栅格化一系列图像帧(欧洲每秒25帧)的模拟编码。因为交替采用两个场来表示一个帧,所以 PC 的模拟电视卡的目的如下:
• 调谐接收广播信号
• 解调广播信号
• 解复用模拟视频信号和模拟音频信号。
注意:有些国家使用嵌入在调制复合模拟信号中的数字音频信号,使用 NICAM 信令。
• 将模拟视频信号数字化,使得产生的数据流能够被传输到数据总线上。
模拟电视卡产生的数字数据流是由卡上的电路生成的,并且通常以未压缩的形式呈现。对于以每秒25帧、分辨率为768x576、采用24位色彩像素编码的 PAL 电视信号来说,产生了相当数量的数据,必须在 PC 上进行处理后才能在视频监视器屏幕上显示。一些用于 PC 的模拟电视卡配备了内置的 MPEG2 编码器,可允许原始数字数据流以编码和压缩的形式呈现给 PC,类似于数字电视中使用的形式。
简单预算的数字电视卡(DVB-T、C或S)的目的非常简单,仅包括以下几点:
• 调谐接收广播信号。
• 从广播信号中提取编码数字数据流。
• 将编码数字数据流(MPEG2)提供给数据总线。
两者之间的显著区别在于模拟电视卡上的调谐器输出模拟信号,而数字电视卡上的调谐器输出压缩的编码数字数据流。由于信号已经被数字化,所以可以轻松地将此数据流传递到 PC 数据总线上,只需要进行最少量的附加处理,然后提取数字视频和音频数据流,并将它们传输到适当的软件或硬件进行解码和播放。
Getting the card going
在 Linux 下,用于 DVB 的设备驱动程序 API 会通过 devfs 文件系统提供以下设备节点:
• /dev/dvb/adapter0/demux0• /dev/dvb/adapter0/dvr0• /dev/dvb/adapter0/frontend0
/dev/dvb/adapter0/dvr0 设备节点用于读取 MPEG2 数据流,/dev/dvb/adapter0/frontend0 设备节点用于调谐前端调谐器模块。/dev/dvb/adapter0/demux0 用于控制将接收哪些节目。
根据卡的功能集,设备驱动程序 API 还可以公开其他设备节点。
• /dev/dvb/adapter0/ca0• /dev/dvb/adapter0/audio0• /dev/dvb/adapter0/net0• /dev/dvb/adapter0/osd0• /dev/dvb/adapter0/video0
/dev/dvb/adapter0/ca0 用于解码加密的频道。其他设备节点仅在使用 av7110 驱动程序的设备上找到,这个驱动程序已经过时,同时其它额外的 API 也被这些设备所使用,现在已不再推荐使用。
Receiving a digital TV channel
这一节试图解释它的工作原理以及它如何影响数字电视卡的配置。
在这个例子中,我们考虑调谐澳大利亚墨尔本地区的 DVB-T 频道。
目前 Mount Dandenong 发射机广播的频率如下:
表1. Mount Dandenong, Vic, Aus的传输频率。
数字电视扫描工具(如 dvbv5-scan)使用各个国家和地区的一组编译默认值。这些默认值目前作为一个名为 dtv-scan-tables 的单独软件包提供。它的 Git 树位于 LinuxTV.org:dtv-scan-tables.git - Digital TV scan tables
如果没有适合您的表,请在命令行中指定包含传输频率的数据文件。以下是一个示例文件,其中包含上述频道转播器的旧“频道”格式:
# Data file for DVB scan program## C Frequency SymbolRate FEC QAM# S Frequency Polarisation SymbolRate FEC# T Frequency Bandwidth FEC FEC2 QAM Mode Guard HierT 177500000 7MHz AUTO AUTO QAM64 8k 1/16 NONET 184500000 7MHz AUTO AUTO QAM64 8k 1/8 NONET 191625000 7MHz AUTO AUTO QAM64 8k 1/16 NONET 219500000 7MHz AUTO AUTO QAM64 8k 1/16 NONET 226500000 7MHz AUTO AUTO QAM64 8k 1/16 NONET 557625000 7MHz AUTO AUTO QPSK 8k 1/16 NONE
现今,我们更倾向于使用一种较新的格式,这种格式更加冗长并且更易于理解。
使用新格式,“Seven”频道转播器的数据表示如下:
[Seven]DELIVERY_SYSTEM = DVBTFREQUENCY = 177500000BANDWIDTH_HZ = 7000000CODE_RATE_HP = AUTOCODE_RATE_LP = AUTOMODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONEINVERSION = AUTO
完整表格的最新版本,请参见:
au-Melbourne\dvb-t - dtv-scan-tables.git - Digital TV scan tables
当数字电视扫描工具运行时,它将输出一个文件,其中包含卡前端可以锁定到的每个频道转播器中存在的所有音频和视频节目的信息(即强度足够在天线处接收到的信号)。
以下是从墨尔本进行的频道扫描的 dvbv5 工具输出信息的示例:
[ABC HDTV]SERVICE_ID = 560VIDEO_PID = 2307AUDIO_PID = 0DELIVERY_SYSTEM = DVBTFREQUENCY = 226500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 3/4MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[ABC TV Melbourne]SERVICE_ID = 561VIDEO_PID = 512AUDIO_PID = 650DELIVERY_SYSTEM = DVBTFREQUENCY = 226500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 3/4MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[ABC TV 2]SERVICE_ID = 562VIDEO_PID = 512AUDIO_PID = 650DELIVERY_SYSTEM = DVBTFREQUENCY = 226500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 3/4MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[ABC TV 3]SERVICE_ID = 563VIDEO_PID = 512AUDIO_PID = 650DELIVERY_SYSTEM = DVBTFREQUENCY = 226500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 3/4MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[ABC TV 4]SERVICE_ID = 564VIDEO_PID = 512AUDIO_PID = 650DELIVERY_SYSTEM = DVBTFREQUENCY = 226500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 3/4MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[ABC DiG Radio]SERVICE_ID = 566VIDEO_PID = 0AUDIO_PID = 2311DELIVERY_SYSTEM = DVBTFREQUENCY = 226500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 3/4MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[TEN Digital]SERVICE_ID = 1585VIDEO_PID = 512AUDIO_PID = 650DELIVERY_SYSTEM = DVBTFREQUENCY = 219500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 1/2MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[TEN Digital 1]SERVICE_ID = 1586VIDEO_PID = 512AUDIO_PID = 650DELIVERY_SYSTEM = DVBTFREQUENCY = 219500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 1/2MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[TEN Digital 2]SERVICE_ID = 1587VIDEO_PID = 512AUDIO_PID = 650DELIVERY_SYSTEM = DVBTFREQUENCY = 219500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 1/2MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[TEN Digital 3]SERVICE_ID = 1588VIDEO_PID = 512AUDIO_PID = 650DELIVERY_SYSTEM = DVBTFREQUENCY = 219500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 1/2MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[TEN Digital]SERVICE_ID = 1589VIDEO_PID = 512AUDIO_PID = 650DELIVERY_SYSTEM = DVBTFREQUENCY = 219500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 1/2MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[TEN Digital 4]SERVICE_ID = 1590VIDEO_PID = 512AUDIO_PID = 650DELIVERY_SYSTEM = DVBTFREQUENCY = 219500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 1/2MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[TEN Digital]SERVICE_ID = 1591VIDEO_PID = 512AUDIO_PID = 650DELIVERY_SYSTEM = DVBTFREQUENCY = 219500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 1/2MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[TEN HD]SERVICE_ID = 1592VIDEO_PID = 514AUDIO_PID = 0DELIVERY_SYSTEM = DVBTFREQUENCY = 219500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 1/2MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[TEN Digital]SERVICE_ID = 1593VIDEO_PID = 512AUDIO_PID = 650DELIVERY_SYSTEM = DVBTFREQUENCY = 219500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 1/2MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[Nine Digital]SERVICE_ID = 1072VIDEO_PID = 513AUDIO_PID = 660DELIVERY_SYSTEM = DVBTFREQUENCY = 191625000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 1/2MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[Nine Digital HD]SERVICE_ID = 1073VIDEO_PID = 512AUDIO_PID = 0DELIVERY_SYSTEM = DVBTFREQUENCY = 191625000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 1/2MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[Nine Guide]SERVICE_ID = 1074VIDEO_PID = 514AUDIO_PID = 670DELIVERY_SYSTEM = DVBTFREQUENCY = 191625000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 3/4CODE_RATE_LP = 1/2MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/16HIERARCHY = NONE[7 Digital]SERVICE_ID = 1328VIDEO_PID = 769AUDIO_PID = 770DELIVERY_SYSTEM = DVBTFREQUENCY = 177500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 2/3CODE_RATE_LP = 2/3MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/8HIERARCHY = NONE[7 Digital 1]SERVICE_ID = 1329VIDEO_PID = 769AUDIO_PID = 770DELIVERY_SYSTEM = DVBTFREQUENCY = 177500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 2/3CODE_RATE_LP = 2/3MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/8HIERARCHY = NONE[7 Digital 2]SERVICE_ID = 1330VIDEO_PID = 769AUDIO_PID = 770DELIVERY_SYSTEM = DVBTFREQUENCY = 177500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 2/3CODE_RATE_LP = 2/3MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/8HIERARCHY = NONE[7 Digital 3]SERVICE_ID = 1331VIDEO_PID = 769AUDIO_PID = 770DELIVERY_SYSTEM = DVBTFREQUENCY = 177500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 2/3CODE_RATE_LP = 2/3MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/8HIERARCHY = NONE[7 HD Digital]SERVICE_ID = 1332VIDEO_PID = 833AUDIO_PID = 834DELIVERY_SYSTEM = DVBTFREQUENCY = 177500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 2/3CODE_RATE_LP = 2/3MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/8HIERARCHY = NONE[7 Program Guide]SERVICE_ID = 1334VIDEO_PID = 865AUDIO_PID = 866DELIVERY_SYSTEM = DVBTFREQUENCY = 177500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 2/3CODE_RATE_LP = 2/3MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/8HIERARCHY = NONE[SBS HD]SERVICE_ID = 784VIDEO_PID = 102AUDIO_PID = 103DELIVERY_SYSTEM = DVBTFREQUENCY = 536500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 2/3CODE_RATE_LP = 2/3MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/8HIERARCHY = NONE[SBS DIGITAL 1]SERVICE_ID = 785VIDEO_PID = 161AUDIO_PID = 81DELIVERY_SYSTEM = DVBTFREQUENCY = 536500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 2/3CODE_RATE_LP = 2/3MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/8HIERARCHY = NONE[SBS DIGITAL 2]SERVICE_ID = 786VIDEO_PID = 162AUDIO_PID = 83DELIVERY_SYSTEM = DVBTFREQUENCY = 536500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 2/3CODE_RATE_LP = 2/3MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/8HIERARCHY = NONE[SBS EPG]SERVICE_ID = 787VIDEO_PID = 163AUDIO_PID = 85DELIVERY_SYSTEM = DVBTFREQUENCY = 536500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 2/3CODE_RATE_LP = 2/3MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/8HIERARCHY = NONE[SBS RADIO 1]SERVICE_ID = 798VIDEO_PID = 0AUDIO_PID = 201DELIVERY_SYSTEM = DVBTFREQUENCY = 536500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 2/3CODE_RATE_LP = 2/3MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/8HIERARCHY = NONE[SBS RADIO 2]SERVICE_ID = 799VIDEO_PID = 0AUDIO_PID = 202DELIVERY_SYSTEM = DVBTFREQUENCY = 536500000INVERSION = OFFBANDWIDTH_HZ = 7000000CODE_RATE_HP = 2/3CODE_RATE_LP = 2/3MODULATION = QAM/64TRANSMISSION_MODE = 8KGUARD_INTERVAL = 1/8HIERARCHY = NONE
1.1.4.3 FAQ
注意:
1. 在数字电视中,单个物理信道可能内部有不同的内容。规格将每个称为服务。这就是电视用户所说的“频道”。因此,为了避免混淆,在本常见问题解答中我们将传输器称为物理信道,将服务称为逻辑信道。
2. LinuxTV社区维护一些Wiki页面,其中包含许多与媒体子系统相关的信息。如果您在这里找不到您需要的答案,那么您很可能会从这里获取有用的信息。它托管在:LinuxTVWiki
关于Linux数字电视支持的一些非常常见的问题:
1. 调谐后几秒钟信号似乎就消失了。
这不是一个错误,而是一种特性。由于前端具有相当大的功率需求(因此会非常热),如果它们未被使用(即如果前端设备关闭),它们将被关闭。dvb-core模块参数 dvb_shutdown_timeout 允许您更改超时时间(默认为5秒)。将超时时间设置为0将禁用超时功能。
2. 如何观看电视?
与Linux内核一起,数字电视开发人员支持一些简单的实用程序,主要用于测试和演示DVB API的工作原理。这称为 DVB v5 工具,可与 v4l-utils git 存储库一起进行分组:
v4l-utils.git - media (V4L2, DVB and IR) applications and libraries
您可以在LinuxTV wiki中找到更多信息:
DVBv5 Tools - LinuxTVWiki
第一步是获取传输的服务列表。
使用几个现有的工具来完成这项任务。例如,您可以使用 dvbv5-scan 工具。您可以在以下网址找到有关它的更多信息:
dvbv5-scan - LinuxTVWiki
有一些其他的应用程序比如w_scan可以进行盲扫描,尝试找到所有可能的频道,但是这需要大量的时间来运行。同时,一些应用程序,如kaffeine,有其自己的代码来扫描服务,因此您不需要使用外部应用程序获得这样的列表。
大多数此类工具都需要一个包含您的地区可用的信道转换器列表的文件。因此,LinuxTV开发人员维护数字电视信道转换器表格,接收社区的补丁以使它们保持最新。该列表托管在:
dtv-scan-tables.git - Digital TV scan tables
并打包在多个发行版中。
Kaffeine对一些地面标准具有一定的盲扫描支持。尽管它内部包含其自己的副本,但它仍然依赖于DTV扫描表格(如果用户要求,它将下载更新版本)。
如果您很幸运,可以使用提供的信道转换器之一。否则,您可能需要在互联网上寻找此类信息并创建新文件。有几个网站包含物理通道列表。对于有线和卫星电视,通常只需知道如何调谐到一个单独的频道即可使扫描工具识别出其他频道。在某些地方,这也适用于地面传输。
一旦您拥有信道转换器列表,您需要使用类似dvbv5-scan这样的工具生成服务列表。几乎所有现代数字电视卡都没有内置硬件MPEG解码器。因此,由应用程序获取MPEG-TS流,然后将其拆分为音频、视频和其他数据进行解码。
3.数字TV应用有哪些?
有多个媒体播放器应用程序可以调谐数字电视信道,包括Kaffeine、Vlc、mplayer和MythTV。
Kaffeine旨在非常用户友好,并由其中一位内核驱动程序开发人员维护。这些和其他应用程序的综合列表可在以下网址找到:
TV Related Software - LinuxTVWiki
以下是一些最受欢迎的应用程序的链接:
https://apps.kde.org/kaffeine/ KDE媒体播放器,专注于数字电视支持
VDR Wiki Klaus Schmidinger的Video Disk Recorder
LinuxTV.org - Television with Linux 和 LinuxTV GIT repositories数字电视和其他媒体相关应用程序和内核驱动程序。v4l-utils软件包包含多个与数字电视一起使用的瑞士军刀工具。
以下是一些其他数字电视应用程序的链接:
DVB tools download | SourceForge.net Dave Chapman的dvbtools软件包,包括dvbstream和dvbtune
http://www.dbox2.info/在dBox2上的LinuxDVB
http://www.tuxbox.org/ TuxBox CVS许多有趣的DVB应用程序和dBox2 DVB源代码
MPSYS MPSYS:一个MPEG2系统库和工具
Official download of VLC media player, the best Open Source player - VideoLAN Vlc
Entering MPlayer homepage MPlayer
http://xine.sourceforge.net/ 和 xine - A Free Video Player - Home Xine
MythTV, Open Source DVR MythTV-模拟电视和数字电视PVR
dvbsnoop - DVB Stream Analyzer, MPEG Analyzer DVB嗅探器程序,用于监视、分析、调试、转储或查看dvb/mpeg/dsm-cc/mhp流信息(TS、PES、SECTION)
4.如果你无法正确调整信号
可能有很多问题。根据我的个人经验,通常电视卡需要比电视机更强的信号,并且更容易受到噪音的影响。因此,也许你只需要更好的天线或电缆。但是,这也可能是一些硬件或驱动程序问题。
例如,如果你正在使用一个没有模拟模块的Technotrend/Hauppauge DVB-C卡,你可能需要使用模块参数adac=-1(dvb-ttpci.o)。
请参阅linuxtv.org的FAQ页面,这里可能包含一些有价值的信息:
FAQ & Troubleshooting - LinuxTVWiki
如果这些方法都不起作用,可以查看linux-media ML档案,看看其他人是否有类似的问题,而你的硬件和/或数字电视服务提供商:
linux-media.vger.kernel.org archive mirror
如果以上所有措施都不能解决问题,你可以尝试发送电子邮件到linux-media ML,看看其他人能否给出一些解决方案。该邮箱为:
linux-media AT vger.kernel.org。
5.如果dvb_net设备根本没有给你任何数据包
请在dvb0_0接口上运行tcpdump。这会将接口设置为混杂模式,以接受与使用dvbnet实用程序配置的PID相匹配的任何数据包。检查是否有具有你已经使用ifconfig或ip addr配置的IP地址和MAC地址的数据包。
如果tcpdump没有给出任何输出,请检查ifconfig或netstat -ni输出的统计信息。(注意:如果MAC地址错误,dvb_net将不会有任何输入;因此在检查统计信息之前必须先运行tcpdump。)如果根本没有数据包,那么可能PID是错误的。如果有错误数据包,则PID可能是错误的或者流不符合MPE标准(EN 301 192,ETSI - Welcome to the World of Standards!)。你可以使用例如dvbsnoop进行调试。
6.dvb_net设备无法接收到任何多播数据包
如果你的dvb_net设备无法接收到任何多播数据包,首先请检查你的路由是否包括多播地址范围。此外,请确保“通过反向路径查找进行源验证”的功能已禁用:
$ "echo 0 > /proc/sys/net/ipv4/conf/dvb0/rp_filter"
7.这些模块需要加载什么?
为了使它更加灵活并支持不同的硬件组合,媒体子系统是以模块化方式编写的。因此,除了用于主芯片组的数字电视硬件模块外,它还需要加载前端驱动程序和数字电视核心。如果板子还带有遥控器,则还需要远程控制器核心和远程控制器表。如果该板子还支持模拟电视,则需要加载video4linux的核心支持模块。
实际模块名称是Linux内核版本特定的,因为不时会进行更改,以使媒体支持更加灵活。
1.1.4.4 References
数字电视驱动程序的主要开发站点和GIT存储库是https://linuxtv.org。
DVB邮件列表linux-dvb托管在vger上。有关详细信息,请参见Majordomo Lists at VGER.KERNEL.ORG。
还有一些其他旧的列表托管在:LinuxTV.org - Mailing Lists。如果出于历史原因您对此感兴趣,请查看The linux-dvb Archives中的存档。
媒体子系统Wiki托管在LinuxTVWiki上。在那里,您将找到许多关于媒体板的开发和使用的信息。在在邮件列表或IRC频道上询问新手问题之前,请先查看它。
API文档记录在内核树中。您可以在LinuxTV.org - Documentation上以html和pdf格式与其他有用的文档一起找到它。
您还可以在LinuxTV.org - Television with Linux上找到有用的材料。
为了使一些驱动程序正常工作而获得所需的固件,内核树中有一个脚本scripts/get_dvb_firmware。
1.1.5 Cards List
媒体子系统提供对许多PCI和USB驱动程序以及特定于平台的驱动程序的支持。它还包含几个辅助I2C驱动程序。
特定于平台的驱动程序通常存在于嵌入式系统中,或者得到主板的支持。通常,通过OpenFirmware或ACPI来设置它们。
然而,PCI和USB驱动程序与系统板无关,并且可以由用户添加/删除。
您还可以查看Hardware device information - LinuxTVWiki以获取有关支持的卡的更多详细信息。
1.1.5.1 USB drivers
USB板卡是通过称为USB ID的标识符进行识别的。
lsusb命令允许识别USB ID:
$ lsusb...Bus 001 Device 015: ID 046d:082d Logitech, Inc. HD Pro Webcam C920Bus 001 Device 074: ID 2040:b131 HauppaugeBus 001 Device 075: ID 2013:024f PCTV Systems nanoStick T2 290e...
较新的相机设备使用标准的方式通过USB视频类别暴露它们自己,这些相机通过uvc驱动程序自动支持。
旧的相机和电视USB设备使用USB供应商类别:每个供应商都定义了访问设备的自己的方式。本节包含此类供应商类别设备的卡列表。
尽管这不像在PCI上那么常见,但有时同一USB ID会被不同的产品使用。因此,几个媒体驱动程序允许传递card=参数,以设置与特定产品类型的正确设置匹配的卡号。
当前受支持的USB卡(不包括分阶段的驱动程序)如下所示:
AU0828 cards list
cx231xx cards list
EM28xx cards list
TM6000 cards list
Siano cards list
The gspca cards list
gspca网络摄像头驱动程序的模块包括:
• gspca_main:主驱动程序
• gspca_driver:带有以下驱动程序的子驱动程序模块
dvb-usb-dib0700 cards list
dvb-usb-dibusb-mb cards list
dvb-usb-dibusb-mc cards list
dvb-usb-a800 cards list
dvb-usb-af9005 cards list
dvb-usb-az6027 cards list
dvb-usb-cinergyT2 cards list
dvb-usb-cxusb cards list
dvb-usb-digitv cards list
dvb-usb-dtt200u cards list
dvb-usb-dtv5100 cards list
dvb-usb-dw2102 cards list
dvb-usb-gp8psk cards list
dvb-usb-m920x cards list
dvb-usb-nova-t-usb2 cards list
dvb-usb-opera1 cards list
dvb-usb-pctv452e cards list
......
1.1.5.2 PCI drivers
PCI板卡是通过称为PCI ID的标识符进行识别的。PCI ID实际上由两个部分组成:
• 供应商ID和设备ID;
• 子系统ID和子系统设备ID;
lspci -nn命令允许识别供应商/设备的PCI ID:
$ lspci -nn...00:0a.0 Multimedia controller [0480]: Philips Semiconductors SAA7131/SAA7133/SAA7135␣, →Video Broadcast Decoder [1131:7133] (rev d1)00:0b.0 Multimedia controller [0480]: Brooktree Corporation Bt878 Audio Capture␣, →[109e:0878] (rev 11)01:00.0 Multimedia video controller [0400]: Conexant Systems, Inc. CX23887/8 PCIe␣, →Broadcast Audio and Video Decoder with 3D Comb [14f1:8880] (rev 0f)02:01.0 Multimedia video controller [0400]: Internext Compression Inc iTVC15␣, →(CX23415) Video Decoder [4444:0803] (rev 01)02:02.0 Multimedia video controller [0400]: Conexant Systems, Inc. CX23418 Single-, →Chip MPEG-2 Encoder with Integrated Analog Video/Broadcast Audio Decoder [14f1:5b7a]02:03.0 Multimedia video controller [0400]: Brooktree Corporation Bt878 Video Capture␣, →[109e:036e] (rev 11)...
子系统ID可通过lspci -vn命令查看:
$ lspci -vn...00:0a.0 0480: 1131:7133 (rev d1)Subsystem: 1461:f01dFlags: bus master, medium devsel, latency 32, IRQ 209Memory at e2002000 (32-bit, non-prefetchable) [size=2K]Capabilities: [40] Power Management version 2...
在上面的示例中,第一张卡使用saa7134驱动程序,具有与1131:7133相等的供应商/设备PCI ID和与1461:f01d相等的PCI子系统ID(参见Saa7134卡列表)。
不幸的是,有时不同的产品会使用相同的PCI子系统ID。因此,几个媒体驱动程序允许传递card=参数,以设置与特定板卡的正确设置匹配的卡号。
当前支持的PCI/PCIe卡(不包括分阶段的驱动程序)列在下面:
BTTV cards list
......
1.1.5.3 Platform drivers
有一些驱动程序专注于为已包含在主板中且既不使用USB也不使用PCI总线的功能提供支持。这些驱动程序被称为平台驱动程序,在嵌入式设备上非常流行。
当前支持的平台驱动程序(不包括分阶段的驱动程序)列在下面:
mtk_jpeg, mtk-mdp, mtk-vcodec-dec, mtk-vpu
......
1.1.5.4 Radio drivers
有对纯AM/FM收音机的支持,甚至对一些FM广播发射器也有支持。
1.1.5.5 I 2C drivers
I2C(Inter-Integrated Circuit)总线是一种三线总线,用于媒体卡内部的不同芯片之间的通信。虽然总线对Linux内核不可见,但驱动程序需要通过总线发送和接收命令。Linux内核驱动程序抽象层支持为I2C总线内的每个组件实现不同的驱动程序,就像总线对主系统板可见一样。
I2C设备的问题之一是有时相同的设备可能使用不同的I2C硬件工作。例如,美国市场的某些设备带有调谐器,而欧洲市场则有另一个调谐器。一些驱动程序具有tuner = modprobe参数,允许使用不同的调谐器编号以解决此类问题。
当前支持的I2C驱动程序(不包括分阶段驱动程序)列在下面:
Audio decoders, processors and mixers
Audio/Video compression chips
Camera sensor devices
Flash devices
IR I2C driver
Lens drivers
......
1.1.5.6 Firewire driver
媒体子系统还提供数字电视的Firewire驱动程序:
1.1.5.7 Test drivers
为了测试用户空间应用程序,有许多虚拟驱动程序提供测试功能,模拟真实的硬件设备:
1.1.6 Video4Linux (V4L) driver-specifific documentation
1.1.6.1 The bttv driver
Release notes for bttv
对于bttv,你至少需要如下的配置:
./scripts/config -e PCI
./scripts/config -m I2C
./scripts/config -m INPUT
./scripts/config -m MEDIA_SUPPORT
./scripts/config -e MEDIA_PCI_SUPPORT
./scripts/config -e MEDIA_ANALOG_TV_SUPPORT
./scripts/config -e MEDIA_DIGITAL_TV_SUPPORT
./scripts/config -e MEDIA_RADIO_SUPPORT
./scripts/config -e RC_CORE
./scripts/config -m VIDEO_BT848
如果你的板子还有数字TV,还需要:
./scripts/config -m DVB_BT8XX
请参考Documentation/admin-guide/media/bt8xx.rst.
bttv是什么:
bttv是一个Linux内核驱动程序,可用于支持Bt848和Bt878视频捕获卡。它允许用户在Linux系统中处理模拟视频和音频信号,并通过计算机进行编码、解码和压缩。这使得Linux系统能够支持一系列应用程序,例如视频录制、监视、视频会议等。
Make bttv work with your card
如果您已经编译和安装了bttv驱动程序,只需要启动内核就足以尝试探测它。但是,根据模型不同,内核可能需要有关硬件的其他信息,因为设备可能无法直接向内核提供此类信息。如果内核不能自动检测到您的卡,那么bttv可能需要一些insmod选项。对于bttv来说,最重要的insmod选项是“card=n”,用于选择正确的卡类型。如果视频正常但没有声音,则很可能指定了错误(或未指定)的卡类型。支持的卡列表在Documentation/admin-guide/media/bttv-cardlist.rst中。
如果bttv加载非常慢(便宜卡通常没有调谐器),请尝试将以下内容添加到您的模块配置文件中(通常是/etc/modules.conf或位于/etc/modules-load.d/的某个文件,但实际位置取决于您的发行版):
options i2c-algo-bit bit_test=1
有些卡可能需要额外的固件文件才能正常工作。例如,对于WinTV/PVR,您需要从其驱动程序CD中获取一个固件文件,称为:hcwamc.rbf。它位于一个名为pvr45xxx.exe的自解压缩zip文件中。将其放置在/etc/firmware目录中就足以在驱动程序探测模式下自动加载它(例如,在内核引导或通过modprobe命令手动加载驱动程序时)。
如果您的卡未在Documentation/admin-guide/media/bttv-cardlist.rst中列出,或者您无法使音频正常工作,请阅读Still doesn't work?。
Autodetecting cards
bttv使用PCI Subsystem ID来自动检测卡类型。 lspci在第二行列出了Subsystem ID,看起来像这样:
00:0a.0 Multimedia video controller: Brooktree Corporation Bt878 (rev 02)
Subsystem: Hauppauge computer works Inc. WinTV/GO
Flags: bus master, medium devsel, latency 32, IRQ 5
Memory at e2000000 (32-bit, prefetchable) [size=4K]
只有基于bt878的卡才能具有subsystem ID(这并不意味着每张卡都真的有一个)。bt848卡无法具有Subsystem ID,因此无法自动检测。有一个带有ID列表的文档位于Documentation/admin-guide/media/bttv-cardlist.rst中(如果您感兴趣或希望通过邮件发送更新的补丁)。
Still doesn’t work?
在http://vger.kernel.org/vger-lists.html#linux-media上有一个邮件列表。
如果您遇到特定的TV卡问题,请尝试在那里询问,而不是直接给我发送电子邮件。有相同卡的用户会更可能在那里聆听您的问题...
对于声音问题:全世界使用的电视声音系统各不相同。而且也有不同的芯片来解码音频信号。关于声音问题(例如“立体声无法工作”)的报告没有包含关于您的硬件和您所在国家(或至少生活的国家)使用的电视声音方案的详细信息是毫无意义的。
Modprobe options
注意:以下参数列表可能已过时,因为如果有需要,我们可能会添加更多选项。如果有疑问,请使用modinfo <module>检查。
这个命令会打印有关内核模块的各种信息,其中包括完整和最新的insmod选项列表。
bttv
The bt848/878 (grabber chip) driver
insmod args:
tuner
调谐器驱动程序。除非您只想与相机一起使用,或者该板不提供模拟电视调谐,否则您需要它。
insmod args:
tvaudio
为所有简单i2c音频控制芯片(tda/tea*)提供单一的驱动程序。
insmod args:
msp3400
这是用于msp34xx声音处理器芯片的驱动程序。如果您有一张立体声声卡,您可能想要安装这个驱动程序。
bttv bugs
如果某个版本可以工作,而另一个版本则无法工作,那么很有可能是一个驱动程序错误。如果您能够告诉我们最后一个工作版本和第一个故障版本,那将非常有帮助。
如果出现了硬件冻结,您可能无法在日志文件中找到任何信息。唯一捕获内核消息的方法是连接一个串行控制台,并让一些终端应用程序记录这些消息。 我通常使用“screen”。有关设置串行控制台的详细信息,请参见“Documentation/admin-guide/serial-console.rst”。
阅读“Documentation/admin-guide/bug-hunting.rst”以了解如何获取内核保护故障(所谓的“内核 oops”)上打印的寄存器+堆栈转储中的任何有用信息。如果遇到某种死锁,您可以尝试为每个使用sysrq-t命令的进程转储一个调用跟踪(请参见“Documentation/admin-guide/sysrq.rst”)。这样就可以确定某个处于“D”状态的进程被卡住在哪里。
我看到有人报告bttv 0.7.x崩溃,而0.8.x则可靠运行。因此,很可能是bttv 0.7.x中留下的小问题。我不知道具体是哪里出了问题,因为对于我和许多其他人来说,它都很稳定。但是,如果您在0.7.x版本中遇到了问题,可以尝试使用0.8.x版本。
hardware bugs
有些硬件无法处理PCI-PCI传输(即抓取卡=> VGA)。有时,仅因PCI总线负载过高,就会出现Bttv问题。针对已知的不兼容性,bt848/878芯片有一些解决方法,请参阅README.quirks。
有些人报告说增加PCI延迟也有帮助,尽管我不确定这是否真正修复了问题,或者只是使问题发生的可能性变小。Bttv和btaudio都有一个insmod选项,用于设置设备的PCI延迟。
有些主板在多个设备同时进行DMA时无法正确处理。如果是bttv + ide引起的,您可能只在同时访问视频和硬盘时才会遇到冻结。更新IDE驱动程序以获取最新的硬件错误解决方法可能会解决这些问题。
Bttv quirks
以下是bt878数据手册有关bt878芯片PCI错误兼容模式的说明。
triton1 insmod选项在控制寄存器中设置EN_TBFX位。 vsfx insmod选项对EN_VSFX位执行相同的操作。如果您遇到稳定性问题,可以尝试这些选项中的一个,看看它是否能使您的设备稳定工作。
drivers/pci/quirks.c了解这些问题,这样已知的有缺陷的芯片组就会自动启用这些位(请查看内核消息,bttv会告诉您)。
Normal PCI Mode
PCI REQ信号是传入功能请求的逻辑或。内部GNT[0:1]信号与GNT异步进行门控,并通过音频请求信号进行解复用。因此,仲裁器默认为电视功能,在没有总线访问请求时停在那里。这是可以接受的,因为视频将更频繁地请求总线访问。但是,音频将具有最高的总线访问优先级。因此,即使在视频请求之后但在PCI外部仲裁器授予对Bt879的访问权限之前发出请求,音频也将首先访问总线。一旦进入总线,任何一个功能都无法抢占另一个功能。空置整个视频PCI FIFO以将其转移到PCI总线上的持续时间与音频PCI FIFO能够容忍的总线访问延迟相比非常短。
430FX Compatibility Mode
在使用430FX PCI时,以下规则将确保兼容性:
(1)在断言FRAME信号的同时取消REQ信号。
(2)在完成上一个事务之后,不要重新断言REQ信号以请求另一个总线事务。
由于各个总线主机没有直接控制REQ信号,简单的视频和音频请求逻辑或将违反规则。因此,仲裁器和发起者都包含430FX兼容模式逻辑。要启用430FX模式,请按照第104页上的“设备控制寄存器”中所示设置EN_TBFX位。
启用EN_TBFX后,仲裁器确保满足这两个兼容性规则。在PCI仲裁器断言GNT之前,此内部仲裁器仍可以将两个请求逻辑或。但是,一旦GNT被发出,此仲裁器必须锁定其决策,并现在仅将已授予的请求路由到REQ引脚。仲裁器的决策锁定不考虑FRAME的状态,因为它不知道何时会断言FRAME信号(通常-每个发起者都会在GNT信号后的一个周期断言FRAME信号)。当断言FRAME信号时,发起者有责任同时取消其请求。仲裁器有责任允许该请求流向REQ,而不允许其他请求保持REQ断言。在事务结束时可以取消决策锁:例如,在总线处于空闲状态(FRAME和IRDY信号均未断言)时。仲裁器的决策可以继续异步进行,直到再次断言GNT信号。
Supported cards: Bt848/Bt848a/Bt849/Bt878/Bt879 cards
Bt848/Bt848a/Bt849/Bt878/Bt879及普通复合视频输入卡都得到支持。通过软件对VBI样本进行解码,所有卡都支持PAL制式的Teletext和Intercast。
某些具有额外输入复用或其他额外芯片的卡只能部分支持(除非卡制造商给出规格说明)。当此处列出一张卡时,不一定会完全支持该卡。
所有其他卡只不过是具有额外元件,如调谐器、声音解码器、EEPROM、teletext解码器...
MATRIX Vision
MV-Delta是一款Bt848A型卡,具有4个复合视频输入和1个S-VHS输入(与第4个复合视频输入共享)。该卡没有调谐器,但支持Bt848A的所有4个复合视频输入(其中一个与S-VHS输入共享)。如果您只有卫星电视,但是需要通过复合视频输入连接多个调谐器到该卡,那么这张卡非常适合您。
感谢Matrix-Vision公司免费赠送我们2张卡,使Bt848a/Bt849单晶体操作支持成为可能!
......
1.1.6.2 The cafe_ccic driver
Author: Jonathan Corbet <corbet@lwn.net>
Introduction
“cafe_ccic”是Marvell 88ALP01“cafe” CMOS相机控制器的驱动程序。该控制器可以在第一代OLPC系统中找到,这个驱动程序是在OLPC项目的支持下编写的。
目前状态:核心驱动程序可用。它可以生成YUV422、RGB565和RGB444格式的数据。(查看代码的任何人都会看到RGB32,但那只是一个调试辅助工具,很快就会被删除)。VGA和QVGA模式可行;CIF模式存在,但颜色仍然很奇怪。目前已知只有OV7670传感器能够与该控制器配合使用。
要尝试它,请使用以下任意一个命令:
$ mplayer tv:// -tv driver=v4l2:width=640:height=480 -nosound
$ mplayer tv:// -tv driver=v4l2:width=640:height=480:outfmt=bgr16 -nosound
"xawtv"工具可用;gqcam因未知原因无法使用。
Load time options
有一些加载时选项,大多数可以在加载后通过sysfs进行更改:
• alloc_bufs_at_load:默认情况下,驱动程序不会在传输数据之前分配任何DMA缓冲区。如果设置了此选项,则会在模块加载时分配最坏情况大小的缓冲区。此选项将为模块的生命周期锁定内存,但可能会减少以后分配失败的机会。
• dma_buf_size:要分配的DMA缓冲区的大小。请注意,此选项仅在加载时进行咨询;当在运行时分配缓冲区时,它们将根据当前相机设置适当地调整大小。
• n_dma_bufs:控制器可以循环使用两个或三个DMA缓冲区。通常,驱动程序尝试使用三个缓冲区;但是,在更快的系统上,仅使用两个缓冲区也可以正常工作。
• min_buffers:驱动程序将同意使用的最小流式I/O缓冲区数。默认值为1,但在较慢的系统上,将其设置为更高的值(如六)可以实现与mplayer更好的行为。
• max_buffers:流式I/O缓冲区的最大数量;默认值为10。该数字是仔细挑选出来的,不应认为实际上表示太多东西。
• flip:如果设置了此布尔参数,则会指示传感器反转视频图像。是否有意义取决于您的特定摄像头安装方式。
1.1.6.3 The cpia2 driver
Introduction
这是STMicroelectronics的CPiA2(第二代彩色处理器接口ASIC)摄像头的驱动程序。该摄像头以最高vga尺寸输出MJPEG流。它尽可能实现了Video4Linux接口。由于V4L接口不支持压缩格式,因此只能使用启用mjpeg的应用程序与摄像头一起使用。我们修改了gqcam应用程序以查看此流。
该驱动程序实现为两个内核模块。cpia2模块包含相机功能和V4L接口。cpia2_usb模块包含usb特定功能。这样做的主要原因是模块的大小越来越大,因此我将它们分开。不太可能会有并行端口版本。
Features
该驱动程序支持具有Vision stv6410(CIF)和stv6500(VGA)CMOS传感器的相机。我只有vga传感器,因此无法测试另一个。
图像格式:VGA、QVGA、CIF、QCIF和介于一些大小之间的多个尺寸。 VGA和QVGA是VGA相机的本地图像尺寸。 CIF在协处理器中通过缩放QVGA完成。所有其他尺寸都通过剪裁完成。
调色板:YCrCb,使用MJPEG压缩。
可以设置一些压缩参数。
传感器帧速率可调节(高达30fps的CIF,15 fps的VGA)。
在流媒体时可调节亮度、颜色、对比度。
可设置50或60赫兹的闪烁控制。
CIF图像分辨率:
CIF全称为Common Intermediate Format,通用中间格式,是一种标准的视频格式之一,具体指:352 * 288像素,每秒钟25帧,每帧有24位色深的视频。这种格式主要适用于采集于VCD,SVCD等标准普及设计,是一种标准的视频压缩技术。
VGA图像分辨率:
VGA(Video Graphics Array)是IBM于1987年为其PS/2系列计算机创建的一种模拟视频标准。现在,电脑行业已经扩展了这个标准,并将它包含在数百万个产品中。对于分辨率,VGA最高支持640x480,每秒钟60Hz或70Hz的刷新率。
Making and installing the stv672 driver modules
Requirements
Video4Linux必须作为内核的一部分进行编译,或者作为一个模块可用。Video4Linux2会在编译时自动被检测到并可用。
Setup
使用"modprobe cpia2"命令进行加载,使用"modprobe -r cpia2"命令进行卸载。这可能会被您的发行版自动完成。
Driver options
Setting the options
如果您正在使用模块,编辑/etc/modules.conf文件,并添加像这样的选项行:
options cpia2 num_buffers=3 buffer_size=65535
如果驱动程序被编译进内核中,在启动时可以像这样指定它们:
cpia2.num_buffers=3 cpia2.buffer_size=65535
What buffffer size should I use?
图像的最大尺寸取决于您选择的备用方案以及相机实现的帧率。如果压缩引擎能够跟上帧率,最大图像大小如下表所示。
压缩引擎从最大压缩开始,会增加图像质量,直到接近表格中的尺寸。 只要压缩引擎能够跟上帧速率,在短时间内,所有图像的大小都将大约为表中的大小,而不管分辨率如何。
在低交替设置下,压缩引擎可能无法压缩图像到足够小的尺寸,因此需要通过生成较大的图像来降低帧率。
默认值68k对于大多数用户应该是足够的,这将处理任何备用方案,帧速率可达15fps。 对于更低的帧速率,可能需要增加缓冲区大小,以避免由于空间不足而丢失帧。
How many buffffers should I use?
对于正常流媒体,使用3个缓冲区应该可以获得最佳的结果。如果只使用2个缓冲区,相机发送完一个图像后,程序可能刚好开始读取另一个图像。如果发生这种情况,驱动程序必须删除一帧。但如果您的计算机负载过重,可以使用2个缓冲区。在这种情况下,您可能没有以全帧速率进行读取。如果相机能够在读取完成之前发送多个图像,则它可能会在读取完成之前覆盖第三个缓冲区,从而导致图像损坏。单缓冲和双缓冲具有额外的检查以避免覆盖。
Using the camera
我们提供了一个修改过的gqcam应用程序来查看输出。为避免混淆,在这里它称为mview。还有qx5view程序,可以控制qx5显微镜上的灯光。还可以使用MJPEG工具(http://mjpeg.sourceforge.net)录制相机的视频。
1.1.6.4 The cx88 driver
Author: Gerd Hoffffmann
This is a v4l2 device driver for the cx2388x chip.
Current status
video
• Works.
• 不支持置顶显示
audio
• Works. 电视标准检测由驱动程序执行,因为硬件自动检测存在缺陷。
• 通过cx88-alsa支持音频数据DMA(即无需回环电缆即可录制到声卡)。
vbi
• Works.
vbi是什么:
VBI是指垂直消隐区域(Vertical Blanking Interval),是指电视信号中每一帧的顶部和底部之间可视部分之外的黑色区域,它主要用于传输其他视频信息,如字幕、时间戳等。
How to add support for new cards
驱动程序需要一些有关TV卡的配置信息。这些信息在cx88-cards.c中。如果驱动程序运行不良,则可能需要在该文件中为您的卡添加新条目。使用dmesg检查内核日志以查看驱动程序是否识别您的卡。日志中会显示像这样的一行:
cx8800[0]: subsystem: 0070:3400, board: Hauppauge WinTV \
34xxx models [card=1,autodetected]
如果您的卡被列为“board: UNKNOWN/GENERIC”,则驱动程序无法识别它。那么应该怎么办呢?
1)尝试升级到最新的快照版本,也许之间已经添加了。
2)您可以尝试自己创建一个新条目,请查看cx88-cards.c。如果成功了,请用统一的差异格式(“diff -u”)给我们发电子邮件您所做的更改。
3)或者您可以将配置信息发送给我们。我们需要至少以下信息才能添加卡:
• PCI Subsystem ID(上面的“0070:3400”,也可以使用“lspci -v”输出)。
• 该卡使用的调谐器类型。您可以尝试使用tuner=<n> insmod选项进行试错查找。如果您知道它使用的调谐器类型,也可以查看CARDLIST.tuner中的列表。
1.1.6.5 The VPBE V4L2 driver design
Functional partitioning
该系统由以下部分组成:
1. V4L2显示驱动程序
实现了创建video2和video3设备节点,并提供v4l2设备接口来管理VID0和VID1层。
2. 显示控制器
加载了VENC、OSD和外部编码器(如ths8200)。它提供一组API调用到V4L2驱动程序,以设置VENC或外部子设备中的输出/标准。它还提供了一个设备对象,以使用子设备操作从OSD子设备访问服务。在默认输出和标准选择或运行时应用程序通过V4L2 IOCTL更改输出时,将在初始化时间基于这些设置将外部编码器连接到VENC LCD控制器端口。
当连接到外部编码器时,vpbe控制器还负责根据板级特定设置(在board-xxx-evm.c中指定)设置VENC和外部编码器之间的接口。这使得可以与ths8200等外部编码器进行接口处理。setup_if_config()也为此实施了configure_venc()(下一patch的一部分),以设置特定显示分辨率的VENC定时。
作为这个patch系列的一部分,外部编码器的连接、使能和设置是不存在的,它们将成为下一个patch系列的一部分。
3. VENC子设备模块
负责在连接到端口或需要LCD面板定时的端口时设置由内部DAC提供的输出并设置VENC中的定时。当连接外部编码器/ LCD面板时,从特定于板的表中检索特定标准/预设的定时,然后使用这些值在venc中设置定时,使用非标准定时模式。
支持使用VENC的LCD面板显示。例如,要支持Logic PD显示,需要使用支持的分辨率设置LCD控制器端口,并设置点时钟。因此,我们可以将可用输出作为特定于板的条目添加(即将“LogicPD”输出名称添加到board-xxx-evm.c)。可以在特定于板的设置文件中维护各种支持的LCD显示器的定时表以支持各种LCD显示器。目前,该patch的基本驱动程序是存在的,对外部编码器和显示的支持将成为下一个patch系列的一部分。
4. OSD模块
OSD模块实现所有OSD层管理和硬件特定功能。VPBE模块与OSD交互,以启用和禁用OSD的相应功能。
视频里OSD是什么:
在视频中,OSD是指"On Screen Display",即屏幕显示。它是一个用户界面元素,通过在屏幕上显示信息来增强用户体验,例如在电视、摄像机和监视器等设备上显示当前频道、时间和其他参数等信息。
Current status
现已提供一个完全功能齐备、可用的V4L2驱动程序版本。该驱动程序已经通过NTSC和PAL标准以及缓冲流测试验证过了。
1.1.6.6 The Samsung S5P/Exynos4 FIMC driver
Copyright © 2012 - 2013 Samsung Electronics Co., Ltd.
三星SoC应用处理器中可用的FIMC(Fully Interactive Mobile Camera)设备是一种集成了相机主机接口、颜色空间转换器、图像调整以及旋转器的综合摄像头。它还可以通过SoC内部回写数据路径从LCD控制器(FIMD)捕获数据。SoC中有多个FIMC实例(最多4个),具有略微不同的功能,如像素对齐限制、旋转器可用性、支持LCD回写等。该驱动程序位于`drivers/media/platform/exynos4-is`目录下。
Supported SoCs
S5PC100 (mem-to-mem only), S5PV210, Exynos4210
Supported features
FIMC设备支持以下特性:
- 相机并行接口采集(ITU-R.BT601/565);
- 相机串行接口采集(MIPI-CSI2);
- 存储到存储器的处理(颜色空间转换、缩放、镜像和旋转);
- 可在运行时进行动态管道重新配置(将任何FIMC实例重新连接到任何并行视频输入或任何MIPI-CSI前端);
- 运行时电源管理和系统范围挂起/恢复。
Not currently supported
- LCD回写输入
- 每帧时钟门控(mem-to-mem)
User space interfaces
Media device interface
该驱动程序支持媒体控制器API,遵循第四部分-媒体控制器API的定义。媒体设备驱动程序名为“Samsung S5P FIMC”。
该接口的目的是允许在运行时更改将FIMC实例分配给SoC外围相机输入的方式,并可选地控制MIPI-CSIS设备与FIMC实体之间的内部连接。
通过媒体设备接口,可以配置SoC通过多个FIMC实例(例如,用于同时取景器和拍照设置)来捕获传感器的图像数据。可以通过启用/禁用驱动程序在初始化期间创建的媒体连接来重新配置。内部设备拓扑可以通过媒体实体和链接枚举轻松发现。
Memory-to-memory video node
V4L2内存到内存接口在`/dev/video?`设备节点上。这是一个独立的视频设备,它没有媒体端口。但请注意,不允许在同一FIMC实例上同时使用内存到内存节点和捕获视频节点。驱动程序会检测这些情况,但应用程序应该避免这种未定义行为。
Capture video node
该驱动程序支持V4L2视频捕获接口,遵循Interfaces定义。捕获视频节点和内存到内存视频节点仅支持多平面API的捕获。有关更多详细信息,请参见:单平面和多平面API。
Camera capture subdevs
每个FIMC实例都会导出一个子设备节点(`/dev/v4l-subdev?`),每个可用的并在平台级别启用的MIPI-CSI接收器设备也会创建一个子设备节点(最多两个)。
sysfs
为了通过子设备API实现更精确的相机管道控制,驱动程序创建了一个与“s5p-fimc-md”平台设备关联的sysfs条目。该条目路径为:`/sys/platform/devices/s5p-fimc-md/subdev_conf_mode`。
在典型的用例中,可能存在以下捕获管道配置:传感器子设备->mipi-csi子设备->fimc子设备->视频节点。当我们通过用户空间的子设备API配置这些设备时,配置流必须从左到右进行,视频节点配置为最后一个。
当我们不使用子设备用户空间API时,所有属于管道的设备的整个配置都由视频节点驱动程序完成。sysfs条目允许指示捕获节点驱动程序不配置子设备(格式、裁剪),以避免在视频节点上执行最后一个配置步骤时重置子设备的配置。
为了完全支持子设备控制(在开始流媒体之前在用户空间配置子设备):
# echo "sub-dev" > /sys/platform/devices/s5p-fimc-md/subdev_conf_mode
对于V4L2视频节点控制而言(子设备由主机驱动程序内部配置):
# echo "vid-dev" > /sys/platform/devices/s5p-fimc-md/subdev_conf_mode
这是默认选项。
Device mapping to video and subdev device nodes
每个硬件设备实例都有两个关联的视频设备节点 - 视频捕获和mem-to-mem,另外还有一个用于更精确的FIMC捕获子系统控制的子设备节点。此外,每个MIPI-CSIS设备都会创建一个单独的v4l2子设备节点。
如何查找哪个/dev/video?或/dev/v4l-subdev?分配给了哪个设备?
你可以通过grep kernel日志中的相关信息来查找,例如:
# dmesg | grep -i fimc
请注意,udev(如果存在)可能仍然重新排列了视频节点,你可以从`/dev/media?`中使用`media-ctl`工具检索信息:
# media-ctl -p
Build
如果将驱动程序构建为可加载的内核模块(`CONFIG_VIDEO_SAMSUNG_S5P_FIMC=m`),则会创建两个模块(除了核心v4l2模块之外):s5p-fimc.ko和可选的s5p-csis.ko(MIPI-CSI接收器子设备)。
1.1.6.7 i.MX Video Capture Driver
Introduction
Freescale i.MX5/6 包含一个图像处理单元(IPU),它处理图像帧从捕获设备到显示设备的流动。
在图像捕获方面,IPU包括以下内部子单元:
- 图像DMA控制器(IDMAC)
- 相机串行接口(CSI)
- 图像转换器(IC)
- 传感器多FIFO控制器(SMFC)
- 图像旋转器(IRT)
- 视频去隔行或合并块(VDIC)
IDMAC是用于将图像帧从内存传输到设备或者从设备传输到内存的DMA控制器。为视频捕获和显示路径都有各种专用DMA通道存在。在传输过程中,IDMAC还能够进行垂直翻转、8x8块传输(参见IRT描述)、同一颜色空间内的像素分量重新排序(例如UYVY到YUYV),以及打包<->平面转换。 IDMAC还可以通过在传输期间交错偶数和奇数行(不需要VDIC执行动态补偿)来执行简单的去隔行。
CSI是直接与相机传感器通过Parallel、BT.656/1120和MIPI CSI-2总线接口的后端捕获单元。
IC处理颜色空间转换、缩放(下采样和上采样)、水平翻转和90/270度旋转操作。
IC中有三个独立的“任务”,可以同时执行转换:预处理编码、预处理取景器和后处理。在每个任务中,转换分为三个部分:缩小部分、主要部分(上采样、翻转、颜色空间转换和图形平面合并)和旋转部分。
IPU共享IC任务操作时间片。时间片粒度为缩小部分的8个像素突发、主要处理部分的一行图像、旋转部分的一帧图像。
SMFC由四个独立的FIFO组成,每个FIFO都可以通过四个IDMAC通道将捕获的帧直接从传感器并发地传输到内存中。
IRT执行90度和270度的图像旋转操作。旋转操作一次处理8x8像素块。该操作由IDMAC支持,它处理8x8块传输以及块重新排序,并与垂直翻转协调。
VDIC处理将交错视频转换为逐行扫描视频,并支持不同的运动补偿模式(低、中和高运动)。VDIC的去隔行输出帧可以发送到IC预处理取景器任务进行进一步转换。VDIC还包含一个合成器,可以将两个图像平面(带有Alpha混合和颜色键控)进行合并。
除了IPU内部子单元外,还有两个位于IPU之外的单元也参与了i.MX上的视频捕获:
• 具有MIPI CSI-2总线接口的相机传感器的MIPI CSI-2接收器。这是Synopsys DesignWare核心。
• 用于选择多个传感器输入中的一个并将其发送到CSI的两个视频复用器。
要了解更多信息,请参考i.MX5/6最新版本的参考手册1和2。
Features
该驱动程序的一些特点包括:
• 通过媒体控制器API可以配置许多不同的管道,对应于i.MX支持的硬件视频捕获管道。
• 支持并行、BT.565和MIPI CSI-2接口。
• 通过将管道配置到多个视频捕获接口,使用独立实体可以实现并发独立流。
• 通过IC任务subdevs进行缩放、颜色空间转换、水平和垂直翻转以及图像旋转。
• 支持许多像素格式(RGB、打包和平面YUV、部分平面YUV)。
• VDIC subdev支持运动补偿去隔行,具有三种运动补偿模式:低、中、高运动。定义了管道,允许直接从CSI将帧发送到VDIC subdev。今后也将支持通过输出/ mem2mem设备将帧发送到VDIC的存储器缓冲区中。
• 包括Frame Interval Monitor(FIM),可以纠正ADV718x视频解码器的垂直同步问题。
Topology
下面显示了i.MX6Q SabreSD和i.MX6Q SabreAuto的媒体拓扑结构。在下一部分的实体描述中参考这些图表。i.MX5/6的拓扑结构可以与IPUv3 CSI视频复用器上游有所不同,但是从那里向下的内部IPUv3拓扑结构对于所有i.MX5/6平台都是共同的。例如,带有MIPI CSI-2 OV5640传感器的SabreSD需要i.MX6 MIPI CSI-2接收器。但是,SabreAuto只有一个并行bt.656总线上的ADV7180解码器,因此不需要MIPI CSI-2接收器,因此在其图表中缺失。
Fig. 1: Media pipeline graph on i.MX6Q SabreSD
Fig. 2: Media pipeline graph on i.MX6Q SabreAuto
Entities
imx6-mipi-csi2
这是MIPI CSI-2接收器实体。它有一个接收MIPI CSI-2流的汇接口(通常来自MIPI CSI-2相机传感器)。它有四个源接口,对应四个MIPI CSI-2分离的虚拟通道输出。多个源接口可启用以独立地从多个虚拟通道流式传输。这个实体实际上由两个子块组成。一个是MIPI CSI-2核心。这是Synopsys Designware MIPI CSI-2核心。另一个子块是“CSI-2到IPU垫片”。该垫片充当四个虚拟通道流的解复用器,提供四个包含每个虚拟通道的单独并行总线,如下所述路由到CSIs或视频复用器。在i.MX6 Solo/Dual-Lite上,所有四个虚拟通道总线都路由到两个视频复用器。CSI0和CSI1都可以接收任何虚拟通道,由视频复用器选择。在i.MX6 Quad上(Fig.1),虚拟通道0被路由到IPU1-CSI0(在由视频mux选择后),虚拟通道1和2分别硬连到IPU1-CSI1和IPU2-CSI0,而虚拟通道3被路由到IPU2-CSI1(再次由视频mux选择)。
ipuX_csiY_mux
这些是视频复用器。它们有两个或更多的汇接口,用于选择来自带有并行接口的相机传感器或来自imx6-mipi-csi2实体的MIPI CSI-2虚拟通道。它们有一个单一的源接口,路由到一个CSI(ipuX_csiY实体)。在i.MX6 Solo/Dual-Lite上,有两个视频复用器实体。一个位于IPU1-CSI0前面,用于在并行传感器和任何四个MIPI CSI-2虚拟通道之间进行选择(共计五个汇接口)。另一个复用器位于IPU1-CSI1前面,并且具有五个汇接口,用于在并行传感器和任何四个MIPI CSI-2虚拟通道之间进行选择。在i.MX6 Quad上(Fig.1),有两个视频复用器实体。一个位于IPU1-CSI0前面,用于在并行传感器和MIPI CSI-2虚拟通道0之间进行选择(两个汇接口)。另一个复用器位于IPU2-CSI1前面,用于在并行传感器和MIPI CSI-2虚拟通道3之间进行选择(两个汇接口)。
ipuX_csiY
这些是CSI实体。它们有一个单一的汇接口,从上述视频复用器或MIPI CSI-2虚拟通道接收输入。该实体有两个源接口。第一个源接口可以直接连接到ipuX_vdic实体或ipuX_ic_prp实体,使用不需要IDMAC内存缓冲区传输的硬件连接。当直接源接口被路由到ipuX_ic_prp实体时,CSI中的帧可以由一个或两个IC预处理任务进行处理。当直接源接口被路由到ipuX_vdic实体时,VDIC将使用“高运动”模式执行运动补偿去隔行处理(请参阅ipuX_vdic实体的描述)。第二个源接口通过SMFC和IDMAC通道直接将视频帧发送到内存缓冲区,绕过IC预处理。该源接口被路由到一个捕获设备节点,节点名称格式为“ipuX_csiY capture”。注意,由于IDMAC源接口使用了IDMAC通道,因此可以通过IDMAC通道在相同颜色空间内进行像素重排序。例如,如果CSI汇接口以UYVY顺序接收,则与IDMAC源接口链接的捕获设备可以以YUYV顺序捕获。另外,如果CSI汇接口接收打包的YUV格式,捕获设备可以捕获分离的YUV格式,如YUV420。IDMAC源接口的IDMAC通道还支持不带运动补偿的简单交错,如果源接口的场类型为顺序自上而下或自下而上,并且请求的捕获接口场类型设置为隔行(t-b、b-t或未限定的隔行),则激活该功能。捕获接口将强制执行与源接口场顺序相同的场顺序(如果源接口是seq-bt,则为隔行-bt,如果源接口是seq-tb,则为隔行-tb)。有关ipuX_csiY生成的事件,请参见ref:imx_api_ipuX_csiY。
Cropping in ipuX_csiY
CSI支持裁剪传入的原始传感器帧。这是在ipuX_csiY实体中的汇接口上使用裁剪选择子设备API实现的。CSI还支持在宽度和高度上独立地进行固定二分之一缩小。这是在ipuX_csiY实体的汇接口上使用组合选择子设备API实现的。ipuX_csiY源接口处的输出矩形与汇接口处的组合矩形相同。因此,源接口矩形无法进行协商,必须使用汇接口上的组合选择API进行设置(如果希望进行/2缩小,则源接口矩形等于传入矩形)。以下是一个示例,将1280x960的输入帧裁剪为640x480,然后在两个维度上/2缩小为320x240(假设ipu1_csi0链接到ipu1_csi0_mux):
media-ctl -V "'ipu1_csi0_mux':2[fmt:UYVY2X8/1280x960]"
media-ctl -V "'ipu1_csi0':0[crop:(0,0)/640x480]"
media-ctl -V "'ipu1_csi0':0[compose:(0,0)/320x240]"
Frame Skipping in ipuX_csiY
CSI支持通过跳帧进行帧率抽取。帧率抽取是通过在汇接口和源接口处设置帧间隔来指定的。然后,ipuX_csiY实体将应用最佳帧跳过设置到CSI以实现源接口处所需的帧率。以下示例将假定的60 Hz输入帧率减半到IDMAC输出源接口:
media-ctl -V "'ipu1_csi0':0[fmt:UYVY2X8/640x480@1/60]"
media-ctl -V "'ipu1_csi0':2[fmt:UYVY2X8/640x480@1/30]"
media-ctl -V:
`media-ctl -V` 用于显示 Media Controller API 版本信息。这个命令会打印版本号以及其他相关信息,如下所示:
Media Controller API version 5.13.0
如果您想要更详细的 Media Controller API 文档,请参考 Linux 内核文档 (Documentation/media/index.rst)。
http://trac.gateworks.com/wiki/linux/media
Frame Interval Monitor in ipuX_csiY
See ref:imx_api_FIM.
ipuX_vdic
VDIC执行运动补偿去隔行,有三种运动补偿模式:低、中和高运动。这个模式由菜单控制V4L2_CID_DEINTERLACING_MODE指定。VDIC有两个汇接口和一个源接口。直接汇接口从ipuX_csiY的直接接口接收。在这个链接下,VDIC只能在高运动模式下运行。
当IDMAC汇接口被激活时,它会从输出或mem2mem设备节点接收。在这个管道中,VDIC也可以在低和中模式下运行,因为这些模式需要从内存缓冲区接收帧。请注意,输出或mem2mem设备尚未实现,因此此汇接口目前没有链接。
源接口路由到 IC 前处理实体 ipuX_ic_prp。
ipuX_ic_prp
这是IC前处理实体。它充当路由器的作用,从其汇接口将数据路由到其一个或两个源接口。
该实体有一个汇接口。汇接口可以从ipuX_csiY的直接接口接收,也可以从ipuX_vdic接收。
这个实体有两个源接口。一个源接口路由到预处理编码任务实体(ipuX_ic_prpenc),另一个源接口路由到预处理取景器任务实体(ipuX_ic_prpvf)。如果汇接口从ipuX_csiY接收,则两个源接口都可以同时激活。如果汇接口从ipuX_vdic接收,则只能激活到预处理取景器任务实体(ipuX_ic_prpvf)的源接口(VDIC的帧只能由预处理取景器任务处理)。
ipuX_ic_prpenc
这是IC前处理编码实体。它有一个从ipuX_ic_prp接收的汇接口和一个源接口。源接口被路由到一个捕获设备节点,节点名称的格式为“ipuX_ic_prpenc capture”。
该实体执行IC前处理编码任务操作:色彩空间转换、缩放(降采样和升采样)、水平和垂直翻转以及90/270度旋转。翻转和旋转通过标准的V4L2控制进行提供。
像ipuX_csiY IDMAC源一样,这个实体也支持简单的去隔行但不带运动补偿和像素重新排序。
ipuX_ic_prpvf
这是IC前处理取景器实体。它有一个从ipuX_ic_prp接收的汇接口和一个源接口。源接口被路由到一个捕获设备节点,节点名称的格式为“ipuX_ic_prpvf capture”。
该实体的操作与ipuX_ic_prpenc完全相同,具有相同的缩放和CSC操作以及翻转/旋转控制。如果ipuX_ic_prp从ipuX_vdic接收,则它将接收并处理从ipuX_vdic去隔行后的帧。
像ipuX_csiY IDMAC源一样,这个实体支持简单的交织,但没有运动补偿。但是,请注意,如果在管道中包含ipuX_vdic(ipuX_ic_prp从ipuX_vdic接收),则无法在ipuX_ic_prpvf中使用交织,因为ipuX_vdic已经进行了去隔行(带有运动补偿),因此ipuX_vdic的场类型输出只能是none(逐行扫描)类型。
Capture Pipelines
以下描述了管道支持的各种用例。所示的链接不包括后端传感器、视频复用器或MIPI CSI-2接收器链接。这取决于传感器接口的类型(并行或MIPI CSI-2)。因此,这些管道从以下开始:
对于并行传感器:sensor -> ipuX_csiY_mux -> …
对于MIPI CSI-2传感器:sensor -> imx6-mipi-csi2 -> (ipuX_csiY_mux)-> …
imx6-mipi-csi2接收器可能需要在发送到CSI之前将其路由到视频复用器(ipuX_csiY_mux),具体取决于MIPI CSI-2虚拟通道,因此在圆括号中显示了ipuX_csiY_mux。
Unprocessed Video Capture
将帧直接从传感器通过ipuX_csiY IDMAC源端口发送到相机设备接口节点,无需进行任何转换:
-> ipuX_csiY:2 -> ipuX_csiY capture //:2表示的是ipuX_csiY的源接口2(Fig.1)
IC Direct Conversions
这个管道使用前处理编码实体直接将帧从CSI路由到IC,进行高达1024x1024分辨率的缩放、CSC、翻转和图像旋转:
-> ipuX_csiY:1 -> 0:ipuX_ic_prp:1 -> 0:ipuX_ic_prpenc:1 -> ipuX_ic_prpenc capture
Motion Compensated De-interlace
这个管道将帧从CSI直接发送到VDIC实体以支持运动补偿去隔行(仅高动态模式),进行高达1024x1024的缩放、CSC、翻转和旋转:
-> ipuX_csiY:1 -> 0:ipuX_vdic:2 -> 0:ipuX_ic_prp:2 -> 0:ipuX_ic_prpvf:1 -> ipuX_ic_prpvf capture
Usage Notes
为了帮助配置并支持仅从视频设备节点访问控件的V4L2应用程序的向后兼容性,捕获设备接口从当前管道中的活动实体继承控件,因此控件可以直接从子设备(subdev)或活动捕获设备接口中访问。例如,FIM控件可以从ipuX_csiY子设备或活动捕获设备中访问。
以下是Sabre*参考板的具体使用注意事项:
i.MX6Q SabreLite with OV5642 and OV5640
该平台需要采用并行相机接口的OV5642模块和采用MIPI CSI-2接口的OV5640模块。
请注意,如果仅有一个相机模块可用,则可以在设备树中禁用另一个传感器节点。
OV5642模块连接到i.MX内部视频复用器的并行总线输入,连接到IPU1 CSI0。它的i2c总线连接到i2c总线2。
MIPI CSI-2 OV5640模块连接到i.MX内部MIPI CSI-2接收器,接收器的四个虚拟通道输出路由如下(Fig.1):vc0到IPU1 CSI0 mux,vc1直接到IPU1 CSI1,vc2直接到IPU2 CSI0,vc3到IPU2 CSI1 mux。OV5640还连接到SabreLite上的i2c总线2,因此OV5642和OV5640不能共享相同的i2c从地址。
以下是用于配置两个传感器的未处理视频捕获管道的基本示例。
OV5642被路由到ipu1_csi0,OV5640通过MIPI CSI-2虚拟通道1(即imx6-mipi-csi2 pad 2)进行传输,并被路由到ipu1_csi1。两个传感器都被配置为输出640x480的分辨率,OV5642输出YUYV2X8格式,OV5640输出UYVY2X8格式:
# Setup links for OV5642
media-ctl -l "'ov5642 1-0042':0 -> 'ipu1_csi0_mux':1[1]"
media-ctl -l "'ipu1_csi0_mux':2 -> 'ipu1_csi0':0[1]"
media-ctl -l "'ipu1_csi0':2 -> 'ipu1_csi0 capture':0[1]"
# Setup links for OV5640
media-ctl -l "'ov5640 1-0040':0 -> 'imx6-mipi-csi2':0[1]"
media-ctl -l "'imx6-mipi-csi2':2 -> 'ipu1_csi1':0[1]"
media-ctl -l "'ipu1_csi1':2 -> 'ipu1_csi1 capture':0[1]"
# Configure pads for OV5642 pipeline
media-ctl -V "'ov5642 1-0042':0 [fmt:YUYV2X8/640x480 field:none]"
media-ctl -V "'ipu1_csi0_mux':2 [fmt:YUYV2X8/640x480 field:none]"
media-ctl -V "'ipu1_csi0':2 [fmt:AYUV32/640x480 field:none]"
# Configure pads for OV5640 pipeline
media-ctl -V "'ov5640 1-0040':0 [fmt:UYVY2X8/640x480 field:none]"
media-ctl -V "'imx6-mipi-csi2':2 [fmt:UYVY2X8/640x480 field:none]"
media-ctl -V "'ipu1_csi1':2 [fmt:AYUV32/640x480 field:none]"
然后可以在捕获设备节点“ipu1_csi0 capture”和“ipu1_csi1 capture”上独立开始流式传输。v4l2-ctl工具可用于在捕获设备节点上选择任何支持的YUV像素格式,包括平面格式。
i.MX6Q SabreAuto with ADV7180 decoder
在i.MX6Q SabreAuto上,一个板载ADV7180 SD解码器连接到内部视频MUX的并行总线输入,连接到IPU1 CSI0(Fig.2)。
以下是配置从ADV7180视频解码器捕获 NTSC 720x480 输入信号的管道的示例,使用简单交错模式(未转换和没有运动补偿)。adv7180必须输出顺序或交替场(对于NTSC来说是“seq-bt”或“alternate”场类型):
# Setup links
media-ctl -l "'adv7180 3-0021':0 -> 'ipu1_csi0_mux':1[1]"
media-ctl -l "'ipu1_csi0_mux':2 -> 'ipu1_csi0':0[1]"
media-ctl -l "'ipu1_csi0':2 -> 'ipu1_csi0 capture':0[1]"
# Configure pads
media-ctl -V "'adv7180 3-0021':0 [fmt:UYVY2X8/720x480 field:seq-bt]"
media-ctl -V "'ipu1_csi0_mux':2 [fmt:UYVY2X8/720x480]"
media-ctl -V "'ipu1_csi0':2 [fmt:AYUV32/720x480]"
# Configure "ipu1_csi0 capture" interface (assumed at /dev/video4)
v4l2-ctl -d4 --set-fmt-video=field=interlaced_bt
然后可以在/dev/video4上开始流式传输。v4l2-ctl工具也可用于选择/dev/video4上支持的任何YUV像素格式。
以下是配置从ADV7180视频解码器捕获PAL 720x576输入信号,使用运动补偿去交错的管道的示例。adv7180必须输出顺序或交替场(对于PAL来说是“seq-tb”或“alternate”场类型)。
# Setup links
media-ctl -l "'adv7180 3-0021':0 -> 'ipu1_csi0_mux':1[1]"
media-ctl -l "'ipu1_csi0_mux':2 -> 'ipu1_csi0':0[1]"
media-ctl -l "'ipu1_csi0':1 -> 'ipu1_vdic':0[1]"
media-ctl -l "'ipu1_vdic':2 -> 'ipu1_ic_prp':0[1]"
media-ctl -l "'ipu1_ic_prp':2 -> 'ipu1_ic_prpvf':0[1]"
media-ctl -l "'ipu1_ic_prpvf':1 -> 'ipu1_ic_prpvf capture':0[1]"
# Configure pads
media-ctl -V "'adv7180 3-0021':0 [fmt:UYVY2X8/720x576 field:seq-tb]"
media-ctl -V "'ipu1_csi0_mux':2 [fmt:UYVY2X8/720x576]"
media-ctl -V "'ipu1_csi0':1 [fmt:AYUV32/720x576]"
media-ctl -V "'ipu1_vdic':2 [fmt:AYUV32/720x576 field:none]"
media-ctl -V "'ipu1_ic_prp':2 [fmt:AYUV32/720x576 field:none]"
media-ctl -V "'ipu1_ic_prpvf':1 [fmt:AYUV32/720x576 field:none]"
# Configure "ipu1_ic_prpvf capture" interface (assumed at /dev/video2)
v4l2-ctl -d2 --set-fmt-video=field=none
然后可以在/dev/video2上开始流式传输。v4l2-ctl工具也可用于选择/dev/video2上支持的任何YUV像素格式。
此平台可以将复合视频模拟输入连接到ADV7180的Ain1(连接器J42)。
i.MX6DL SabreAuto with ADV7180 decoder
在i.MX6DL SabreAuto上,一个板载ADV7180 SD解码器连接到内部视频MUX的并行总线输入,连接到IPU1 CSI0。
以下是配置从ADV7180视频解码器捕获 NTSC 720x480 输入信号的管道的示例,使用简单交错模式(未转换和没有运动补偿)。adv7180必须输出顺序或交替场(对于NTSC来说是“seq-bt”或“alternate”场类型):
# Setup links
media-ctl -l "'adv7180 4-0021':0 -> 'ipu1_csi0_mux':4[1]"
media-ctl -l "'ipu1_csi0_mux':5 -> 'ipu1_csi0':0[1]"
media-ctl -l "'ipu1_csi0':2 -> 'ipu1_csi0 capture':0[1]"
# Configure pads
media-ctl -V "'adv7180 4-0021':0 [fmt:UYVY2X8/720x480 field:seq-bt]"
media-ctl -V "'ipu1_csi0_mux':5 [fmt:UYVY2X8/720x480]"
media-ctl -V "'ipu1_csi0':2 [fmt:AYUV32/720x480]"
# Configure "ipu1_csi0 capture" interface (assumed at /dev/video0)
v4l2-ctl -d0 --set-fmt-video=field=interlaced_bt
然后可以在/dev/video0上开始流式传输。v4l2-ctl工具也可用于选择/dev/video0上支持的任何YUV像素格式。
以下是配置从ADV7180视频解码器捕获PAL 720x576输入信号,使用运动补偿去交错的管道的示例。adv7180必须输出顺序或交替场(对于PAL来说是“seq-tb”或“alternate”场类型)。
# Setup links
media-ctl -l "'adv7180 4-0021':0 -> 'ipu1_csi0_mux':4[1]"
media-ctl -l "'ipu1_csi0_mux':5 -> 'ipu1_csi0':0[1]"
media-ctl -l "'ipu1_csi0':1 -> 'ipu1_vdic':0[1]"
media-ctl -l "'ipu1_vdic':2 -> 'ipu1_ic_prp':0[1]"
media-ctl -l "'ipu1_ic_prp':2 -> 'ipu1_ic_prpvf':0[1]"
media-ctl -l "'ipu1_ic_prpvf':1 -> 'ipu1_ic_prpvf capture':0[1]"
# Configure pads
media-ctl -V "'adv7180 4-0021':0 [fmt:UYVY2X8/720x576 field:seq-tb]"
media-ctl -V "'ipu1_csi0_mux':5 [fmt:UYVY2X8/720x576]"
media-ctl -V "'ipu1_csi0':1 [fmt:AYUV32/720x576]"
media-ctl -V "'ipu1_vdic':2 [fmt:AYUV32/720x576 field:none]"
media-ctl -V "'ipu1_ic_prp':2 [fmt:AYUV32/720x576 field:none]"
media-ctl -V "'ipu1_ic_prpvf':1 [fmt:AYUV32/720x576 field:none]"
# Configure "ipu1_ic_prpvf capture" interface (assumed at /dev/video2)
v4l2-ctl -d2 --set-fmt-video=field=none
然后可以在/dev/video2上开始流式传输。v4l2-ctl工具也可用于选择/dev/video2上支持的任何YUV像素格式。
此平台可以将复合视频模拟输入连接到ADV7180的Ain1(连接器J42)。
i.MX6Q SabreSD with MIPI CSI-2 OV5640
类似于i.MX6Q SabreLite,i.MX6Q SabreSD也支持IPU1 CSI0上的并行接口OV5642模块和MIPI CSI-2 OV5640模块。OV5642连接到i2c总线1,而OV5640连接到i2c总线2。
SabreSD的设备树包括用于并行OV5642和MIPI CSI-2 OV5640的OF图,但截至本写作时,仅测试了MIPI CSI-2 OV5640,因此OV5642节点当前已禁用。OV5640模块连接到MIPI连接器J5。连接到SabreSD板的OV5640模块的NXP零件号为H120729。
以下是配置未处理的视频捕获管道,从OV5640捕获,传输到MIPI CSI-2虚拟通道0的示例:
# Setup links
media-ctl -l "'ov5640 1-003c':0 -> 'imx6-mipi-csi2':0[1]"
media-ctl -l "'imx6-mipi-csi2':1 -> 'ipu1_csi0_mux':0[1]"
media-ctl -l "'ipu1_csi0_mux':2 -> 'ipu1_csi0':0[1]"
media-ctl -l "'ipu1_csi0':2 -> 'ipu1_csi0 capture':0[1]"
# Configure pads
media-ctl -V "'ov5640 1-003c':0 [fmt:UYVY2X8/640x480]"
media-ctl -V "'imx6-mipi-csi2':1 [fmt:UYVY2X8/640x480]"
media-ctl -V "'ipu1_csi0_mux':0 [fmt:UYVY2X8/640x480]"
media-ctl -V "'ipu1_csi0':0 [fmt:AYUV32/640x480]"
然后可以在“ipu1_csi0 capture”节点上开始流式传输。v4l2-ctl工具可用于选择捕获设备节点上支持的任何像素格式。
要确定与“ipu1_csi0捕获”对应的/dev/video节点是什么:
media-ctl -e "ipu1_csi0 capture"
/dev/video0
此案例中,/dev/video0是流式传输元素。 通过v4l2-ctl启动流式传输:
v4l2-ctl --stream-mmap -d /dev/video0
使用Gstreamer开始流式传输并将内容发送到显示器:
gst-launch-1.0 v4l2src device=/dev/video0 ! kmssink
以下是配置直接转换管道,从OV5640捕获,传输到MIPI CSI-2虚拟通道0的示例。它还显示IC输出的颜色空间转换和缩放。
# Setup links
media-ctl -l "'ov5640 1-003c':0 -> 'imx6-mipi-csi2':0[1]"
media-ctl -l "'imx6-mipi-csi2':1 -> 'ipu1_csi0_mux':0[1]"
media-ctl -l "'ipu1_csi0_mux':2 -> 'ipu1_csi0':0[1]"
media-ctl -l "'ipu1_csi0':1 -> 'ipu1_ic_prp':0[1]"
media-ctl -l "'ipu1_ic_prp':1 -> 'ipu1_ic_prpenc':0[1]"
media-ctl -l "'ipu1_ic_prpenc':1 -> 'ipu1_ic_prpenc capture':0[1]"
# Configure pads
media-ctl -V "'ov5640 1-003c':0 [fmt:UYVY2X8/640x480]"
media-ctl -V "'imx6-mipi-csi2':1 [fmt:UYVY2X8/640x480]"
media-ctl -V "'ipu1_csi0_mux':2 [fmt:UYVY2X8/640x480]"
media-ctl -V "'ipu1_csi0':1 [fmt:AYUV32/640x480]"
media-ctl -V "'ipu1_ic_prp':1 [fmt:AYUV32/640x480]"
media-ctl -V "'ipu1_ic_prpenc':1 [fmt:ARGB8888_1X32/800x600]"
# Set a format at the capture interface
v4l2-ctl -d /dev/video1 --set-fmt-video=pixelformat=RGB3
然后可以在“ipu1_ic_prpenc capture”节点上开始流式传输。
要确定与“ipu1_ic_prpenc capture”对应的/dev/video节点是什么:
media-ctl -e "ipu1_ic_prpenc capture"
/dev/video1
在这种情况下,/dev/video1是流式传输元素。 通过v4l2-ctl启动流式传输:
v4l2-ctl --stream-mmap -d /dev/video1
使用Gstreamer开始流式传输并将内容发送到显示器:
gst-launch-1.0 v4l2src device=/dev/video1 ! kmssink
Known Issues
当使用90度或270度旋转控制以接近IC调整器限制的1024x1024捕获分辨率,并且结合平面像素格式(YUV420、YUV422p)时,帧捕获通常会失败,而IDMAC通道没有结束帧中断。为了解决这个问题,在需要90度或270度旋转时,请使用较低分辨率和/或打包格式(YUYV、RGB3等)。
File list
drivers/staging/media/imx/ nclude/media/imx.h
include/linux/imx-media.h
1.1.6.8 i.MX7 Video Capture Driver
Introduction
与i.MX5/6系列不同,i.MX7不包含图像处理单元(IPU);因此,执行对捕获帧进行操作或操作的能力要少得多。对于图像捕获,i.MX7有三个单元:-CMOS传感器接口(CSI)-视频多路复用器-MIPI CSI-2接收器。
Entities
imx7-mipi-csi2
这是MIPI CSI-2接收器实体。它有一个接收端口,用于从MIPI CSI-2相机传感器接收像素数据。它有一个源端口,对应虚拟通道0。该模块符合三星D-phy的先前版本,并支持两个D-PHY Rx数据通道。
csi-mux
这是视频多路复用器。它有两个接收端口,可以从具有并行接口的任一相机传感器或MIPI CSI-2虚拟通道0中进行选择。它有一个单一的源端口,路由到CSI。
csi
CSI使芯片能够直接连接到外部CMOS图像传感器。CSI可以直接与并行和MIPI CSI-2总线进行接口。它具有256 x 64的FIFO,用于存储接收的图像像素数据,并嵌入DMA控制器以通过AHB总线从FIFO传输数据。该实体具有一个接收端口,从csi-mux实体接收数据,并具有一个单一的源端口,可将视频帧直接路由到内存缓冲区。此端口被路由到一个捕获设备节点。
Usage Notes
为了帮助配置,并向后兼容V4L2应用程序,这些应用程序仅从视频设备节点访问控制,因此捕获设备接口继承当前管道中活动实体的控件,因此可以直接从子设备或活动捕获设备界面访问控件。例如,传感器控件可以从传感器子设备或活动捕获设备中访问。
Warp7 with OV2680
在这个平台上,一个OV2680 MIPI CSI-2模块连接到内部MIPI CSI-2接收器。下面的示例配置了一个视频捕获管道,输出为800x600,格式为BGGR 10位Bayer格式:
# v4l2-ctl --set-fmt-video=width=800,height=600,pixelformat=BG10 \
--set-ctrl bypass_mode=0 \
--set-selection=target=\"Full sensor\",top=0,left=0,width=800,height=600 \
--stream-mmap --stream-count=1 \
--device=/dev/video0
该命令使用v4l2-ctl工具设置视频格式、选择区域,并启动视频流传输。视频数据将从/dev/video0设备节点读取。
# Setup links
media-ctl -l "'ov2680 1-0036':0 -> 'imx7-mipi-csis.0':0[1]"
media-ctl -l "'imx7-mipi-csis.0':1 -> 'csi-mux':1[1]"
media-ctl -l "'csi-mux':2 -> 'csi':0[1]"
media-ctl -l "'csi':1 -> 'csi capture':0[1]"
# Configure pads for pipeline
media-ctl -V "'ov2680 1-0036':0 [fmt:SBGGR10_1X10/800x600 field:none]"
media-ctl -V "'csi-mux':1 [fmt:SBGGR10_1X10/800x600 field:none]"
media-ctl -V "'csi-mux':2 [fmt:SBGGR10_1X10/800x600 field:none]"
media-ctl -V "'imx7-mipi-csis.0':0 [fmt:SBGGR10_1X10/800x600 field:none]"
media-ctl -V "'csi':0 [fmt:SBGGR10_1X10/800x600 field:none]"
完成这些设置后,可以开始流传输。v4l2-ctl工具可用于选择传感器支持的任何分辨率。
i.MX6ULL-EVK with OV5640
在这个平台上,一个并行的OV5640传感器连接到CSI端口。下面的示例配置了一个视频捕获管道,输出为640x480,格式为UYVY8_2X8:
# v4l2-ctl --set-fmt-video=width=640,height=480,pixelformat=UYVY \
--stream-mmap --stream-count=1 \
--device=/dev/video0
该命令使用v4l2-ctl工具设置视频格式,并启动视频流传输。视频数据将从/dev/video0设备节点读取。
# Setup links
media-ctl -l "'ov5640 1-003c':0 -> 'csi':0[1]"
media-ctl -l "'csi':1 -> 'csi capture':0[1]"
# Configure pads for pipeline
media-ctl -v -V "'ov5640 1-003c':0 [fmt:UYVY8_2X8/640x480 field:none]"
完成这些设置后,可以开始流传输:
gst-launch-1.0 -v v4l2src device=/dev/video1 ! video/x-raw,format=UYVY,width=640,
, →height=480 ! v4l2convert ! fbdevsink
1.1.6.9 Intel Image Processing Unit 3 (IPU3) Imaging Unit (ImgU) driver
Introduction
本文件记录了位于drivers/media/pci/intel/ipu3(CIO2)以及drivers/staging/media/ipu3(ImgU)下的Intel IPU3(第3代图像处理单元)Imaging Unit驱动程序。在某些Kaby Lake平台(以及某些Sky Lake平台)中发现的Intel IPU3由两个部分组成,即Imaging Unit(ImgU)和CIO2设备(MIPI CSI2接收器)。
CIO2设备从传感器接收原始Bayer数据,并以特定于IPU3的格式输出帧(供IPU3 ImgU使用)。CIO2驱动程序可在drivers/media/pci/intel/ipu3/ipu3-cio2*中找到,并通过CONFIG_VIDEO_IPU3_CIO2配置选项启用。Imaging Unit(ImgU)负责处理IPU3 CIO2设备捕获的图像。ImgU驱动程序源代码可以在drivers/staging/media/ipu3目录下找到。通过CONFIG_VIDEO_IPU3_IMGU配置选项启用该驱动程序。两个驱动程序模块分别命名为ipu3_csi2和ipu3_imgu。这两个驱动程序都实现了V4L2、Media Controller和V4L2子设备接口。IPU3 CIO2驱动程序支持通过V4L2子设备传感器驱动程序连接到CIO2 MIPI CSI-2接口的相机传感器。这些驱动程序已在Kaby Lake平台(U/Y处理器系列)上进行了测试。
CIO2
CIO2被表示为单个V4L2子设备,提供V4L2子设备接口给用户空间。每个CSI-2接收器都有一个视频节点,整个设备只有一个媒体控制器接口。CIO2包含四个独立的捕获通道,每个通道都有自己的MIPI CSI-2接收器和DMA引擎。每个通道被建模为V4L2子设备,在用户空间作为V4L2子设备节点公开,并有两个插孔:发送端(source)和接收端(sink)。为了方便使用,CIO2驱动程序还包括一些额外的功能,如自动曝光控制、自动白平衡控制等等。这些功能可以通过V4L2控制接口进行配置。此外,CIO2驱动程序还支持视频捕获调试框架,以帮助开发人员进行问题诊断和调试。
V4L2视频接口模型化DMA引擎,并将其作为V4L2视频设备节点公开给用户空间。
Capturing frames in raw Bayer format
CIO2的MIPI CSI2接收器用于从连接到CSI2端口的原始传感器中捕获帧(以打包的原始Bayer格式)。捕获的帧被用作ImgU驱动程序的输入。使用IPU3 ImgU进行图像处理需要使用像raw2pnm2和yavta3这样的工具,因为它们具有以下特定于IPU3的独特要求和/或功能。
- IPU3 CSI2接收器以特定于IPU3的打包原始Bayer格式输出从传感器捕获的帧。
- 必须同时操作多个视频节点。
我们以ov5670传感器连接到CSI2端口0并进行2592x1944图像捕获为例。
使用媒体控制器API,ov5670传感器被配置为以打包的原始Bayer格式发送帧到IPU3 CSI2接收器。
# This example assumes /dev/media0 as the CIO2 media device
export MDEV=/dev/media0
# and that ov5670 sensor is connected to i2c bus 10 with address 0x36
export SDEV=$(media-ctl -d $MDEV -e "ov5670 10-0036")
# Establish the link for the media devices using media-ctl [#f3]_
media-ctl -d $MDEV -l "ov5670:0 -> ipu3-csi2 0:0[1]"
# Set the format for the media devices
media-ctl -d $MDEV -V "ov5670:0 [fmt:SGRBG10/2592x1944]"
media-ctl -d $MDEV -V "ipu3-csi2 0:0 [fmt:SGRBG10/2592x1944]"
media-ctl -d $MDEV -V "ipu3-csi2 0:1 [fmt:SGRBG10/2592x1944]"
一旦媒体管道被配置,就可以使用yavta工具设置所需的传感器特定设置(例如曝光和增益设置)。
yavta -w 0x009e0903 444 $SDEV
yavta -w 0x009e0913 1024 $SDEV
yavta -w 0x009e0911 2046 $SDEV
一旦设置了所需的传感器设置,可以按照以下步骤进行帧捕获。
yavta --data-prefix -u -c10 -n5 -I -s2592x1944 --file=/tmp/frame-#.bin \
-f IPU3_SGRBG10 $(media-ctl -d $MDEV -e "ipu3-cio2 0")
捕获的帧以/tmp/frame-#.bin文件的形式可用。
ImgU
ImgU被表示为两个V4L2子设备,每个子设备都提供了一个 V4L2 子设备接口给用户空间。
每个 V4L2 子设备都表示一个管道,最多可以支持 2 个流。这有助于支持高级相机功能,如连续查找器 (CVF) 和视频快照 (SDV)。
ImgU 包含两个独立的管道,每个管道都建模为一个 V4L2 子设备,作为一个 V4L2 子设备节点暴露给用户空间。
每个管道都有两个接收端口和三个发送端口,具体如下:
每个端口都连接到相应的 V4L2 视频接口,作为一个 V4L2 视频设备节点暴露给用户空间。
Device operation
使用ImgU时,一旦将输入视频节点(使用“ipu3-imgu 0/1”:0,以<entity>:<pad-number>格式)入队到缓冲区(以紧密打包的原始Bayer格式),ImgU就开始处理缓冲区并在相应的输出节点上生成视频输出和统计输出。当将输入视频节点入队到缓冲区时,驱动程序应准备好所有参数、输出和统计节点的缓冲区。
至少应该启用所有输入、主输出、3A统计和取景器视频节点,才能使IPU3开始图像处理。
每个ImgU V4L2子设备具有以下一组视频节点。
input, output and viewfifinder video nodes
输入视频节点接收到的帧(特定于IPU3的紧密打包的原始Bayer格式)由IPU3成像单元处理,并输出到2个视频节点,分别针对不同的目的(主要输出和取景器输出)。
有关IPU3特定的Bayer格式的详细信息,请参见V4L2_PIX_FMT_IPU3_SBGGR10(“ip3b”),V4L2_PIX_FMT_IPU3_SGBRG10(“ip3g”),V4L2_PIX_FMT_IPU3_SGRBG10(“ip3G”),V4L2_PIX_FMT_IPU3_SRGGB10(“ip3r”)。
驱动程序支持定义在V4L2视频捕获接口上的V4L2视频采集接口。只支持多平面API。更多细节请参阅单平面和多平面API。
Parameters video node
参数视频节点接收用于配置ImgU算法如何处理图像的ImgU算法参数。
有关特定于IPU3的处理参数的详细信息,请参见V4L2_META_FMT_IPU3_PARAMS(“ip3p”),V4L2_META_FMT_IPU3_3A(“ip3s”)。
3A statistics video node
3A统计视频节点由ImgU驱动程序用于输出正在由ImgU处理的帧的3A(自动对焦、自动曝光和自动白平衡)统计数据,以供用户空间应用程序使用。用户空间应用程序可以使用此统计数据计算所需的ImgU算法参数。
Configuring the Intel IPU3
可以使用媒体控制器(定义在第IV部分 - 媒体控制器API中)来配置IPU3 ImgU管道。
Running mode and fifirmware binary selection
ImgU基于固件工作,目前ImgU固件支持在单个输入帧数据的时间共享下运行2个管道。每个管道可以以“VIDEO”或“STILL”模式运行,“VIDEO”模式通常用于视频帧捕获,“STILL”用于静态帧捕获。但是,如果您想以较小的系统负载和功率捕获图像,则也可以选择“VIDEO”来捕获静态帧。对于“STILL”模式,ImgU将尝试使用较小的BDS因子并输出更大的Bayer帧以进行进一步的YUV处理,从而获得高质量的图像。此外,“STILL”模式需要XNR3进行降噪,因此“STILL”模式将需要比“VIDEO”模式更多的电源和内存带宽。TNR将在“VIDEO”模式下启用,并由“STILL”模式绕过。ImgU默认以“VIDEO”模式运行,用户可以使用v4l2控件V4L2_CID_INTEL_IPU3_MODE(当前定义为drivers/staging/media/ipu3/include/uapi/intel-ipu3.h)查询和设置运行模式。对于用户而言,“VIDEO”模式和“STILL”模式之间的缓冲队列没有区别,必须启用强制输入和主输出节点,并排队缓冲区,统计信息和取景器队列是可选的。
固件二进制文件将根据当前运行模式进行选择,如果启用了ImgU动态调试,则可以观察到如“using binary if_to_osys_striped”或“using binary if_to_osys_primary_striped”的日志,if_to_osys_striped二进制文件用于“VIDEO”,if_to_osys_primary_striped二进制文件用于“STILL”。
Processing the image in raw Bayer format
Confifiguring ImgU V4L2 subdev for image processing
ImgU V4L2子设备必须使用媒体控制器API进行配置,以使所有视频节点正确设置。让我们以“ipu3-imgu 0”子设备为例。
media-ctl -d $MDEV -r
media-ctl -d $MDEV -l "ipu3-imgu 0 input":0 -> "ipu3-imgu 0":0[1]
media-ctl -d $MDEV -l "ipu3-imgu 0":2 -> "ipu3-imgu 0 output":0[1]
media-ctl -d $MDEV -l "ipu3-imgu 0":3 -> "ipu3-imgu 0 viewfinder":0[1]
media-ctl -d $MDEV -l "ipu3-imgu 0":4 -> "ipu3-imgu 0 3a stat":0[1]
对应的V4L2子设备的管道模式也应按照所需设置(例如,0表示视频模式,1表示静态模式),可以通过控制ID 0x009819a1 设置,如下所示:
yavta -w "0x009819A1 1" /dev/v4l-subdev7
在ImgU流水线中,某些硬件块可以通过裁剪或缩放来改变帧的分辨率,这些硬件块包括输入饲送器(IF)、Bayer缩放器(BDS)和几何畸变校正器(GDC)。还有一个块可以改变帧的分辨率——YUV缩放器,只适用于二次输出。
原始Bayer帧经过这些ImgU流水线硬件块,并将最终处理的图像输出到DDR内存。
Fig. 3: IPU3 resolution change hardware blocks
sensor binning什么意思:
传感器Binning是指将多个相邻像素合并成一个像素的过程,其目的是为了提高图像质量和灵敏度。在传感器binning过程中,要么将几个像素值加权平均,要么将它们简单平均。通过将多个像素合并成一个像素,可以减少图像的噪声和提高图像的信噪比。但是,它也会减少图像的分辨率。传感器binning通常用于低光条件下的拍摄,以提高传感器的感光度。
Input Feeder
输入饲送器(Input Feeder)从传感器获取Bayer帧数据,可以从帧中启用行和列的裁剪,然后将像素存储到设备的内部像素缓冲区中,准备由后续块读取输出。
Bayer Down Scaler
Bayer缩放器(Bayer Down Scaler)能够在Bayer域中进行图像缩放,缩小因子可以在每个轴上配置为1X到1/4X,并且配置步骤为0.03125(1/32)。
Geometric Distortion Correction
几何畸变校正器(Geometric Distortion Correction)用于执行失真和图像滤波的校正。它需要一些额外的滤波器和包络填充像素才能正常工作,因此GDC的输入分辨率应大于输出分辨率。
YUV Scaler
YUV缩放器(YUV Scaler)与BDS类似,但主要在YUV域中进行图像缩放,最多支持1/12X的缩小比例,但不能应用于主输出。在给定的输入分辨率下,ImgU V4L2子设备必须配置所有上述硬件块的支持分辨率。针对给定输入帧的支持分辨率,应将输入饲送器、BDS和GDC块配置为支持的分辨率,因为每个硬件块都有自己的对齐要求。
您必须聪明地配置硬件块的输出分辨率,以满足硬件要求并保持最大的视场。中间分辨率可以通过特定工具生成 - https://github.com/intel/intel-ipu3-pipecfg
该工具可用于生成中间分辨率。可通过查看以下IPU3 ImgU配置表获取更多信息。https://chromium.googlesource.com/chromiumos/overlays/board-overlays/+/master,在baseboard-poppy/media-libs/cros-camera-hal-configs-poppy/files/gcss目录下,graph_settings_ov5670.xml可用作示例。
以下步骤为图像处理准备ImgU管道。
1. 应使用VIDIOC_SUBDEV_S_FMT在pad 0上设置ImgU V4L2子设备数据格式,使用上面获取的GDC宽度和高度。
2. 应使用VIDIOC_SUBDEV_S_SELECTION在pad 0上设置ImgU V4L2子设备裁剪,V4L2_SEL_TGT_CROP作为目标,用作输入饲料器高度和宽度。
3. 应使用VIDIOC_SUBDEV_S_SELECTION在pad 0上设置ImgU V4L2子设备组合,V4L2_SEL_TGT_COMPOSE作为目标,用作BDS高度和宽度。
对于ov5670示例,对于分辨率为2592x1944的输入帧(输入到ImgU子设备pad 0),输入饲料器、BDS和GDC的相应分辨率分别为2592x1944、2592x1944和2560x1920。
完成此操作后,可以将接收到的原始Bayer帧作为以下方式输入到ImgU V4L2子设备中,使用开源应用程序v4l2n。
对于以2592x1945分辨率捕获的图像,期望的输出分辨率为2560x1920,取景器分辨率为2560x1920,则可以使用以下v4l2n命令。这有助于处理原始Bayer帧,并以NV12格式生成主输出和取景器输出的所需结果。
v4l2n --pipe=4 --load=/tmp/frame-#.bin --open=/dev/video4
--fmt=type:VIDEO_OUTPUT_MPLANE,width=2592,height=1944,pixelformat=0X47337069 \
--reqbufs=type:VIDEO_OUTPUT_MPLANE,count:1 --pipe=1 \
--output=/tmp/frames.out --open=/dev/video5 \
--fmt=type:VIDEO_CAPTURE_MPLANE,width=2560,height=1920,pixelformat=NV12 \
--reqbufs=type:VIDEO_CAPTURE_MPLANE,count:1 --pipe=2 \
--output=/tmp/frames.vf --open=/dev/video6 \
--fmt=type:VIDEO_CAPTURE_MPLANE,width=2560,height=1920,pixelformat=NV12 \
--reqbufs=type:VIDEO_CAPTURE_MPLANE,count:1 --pipe=3 --open=/dev/video7 \
--output=/tmp/frames.3A --fmt=type:META_CAPTURE,? \
--reqbufs=count:1,type:META_CAPTURE --pipe=1,2,3,4 --stream=5
v4l2n网址没搜到
你也可以用yavta命令做同样的事情:
yavta --data-prefix -Bcapture-mplane -c10 -n5 -I -s2592x1944 \
--file=frame-#.out-f NV12 /dev/video5 & \
yavta --data-prefix -Bcapture-mplane -c10 -n5 -I -s2592x1944 \
--file=frame-#.vf -f NV12 /dev/video6 & \
yavta --data-prefix -Bmeta-capture -c10 -n5 -I \
--file=frame-#.3a /dev/video7 & \
yavta --data-prefix -Boutput-mplane -c10 -n5 -I -s2592x1944 \
--file=/tmp/frame-in.cio2 -f IPU3_SGRBG10 /dev/video4
其中,/dev/video4、/dev/video5、/dev/video6和/dev/video7这些设备分别指向输入、输出、取景器和3A统计视频节点。在此语境中,输入节点是用于捕获原始图像流的节点,输出节点是用于生成处理后的图像流的节点,取景器节点允许用户查看正在捕获的图像流。3A统计信息节点提供有关自动对焦(AF)、自动曝光(AE)和自动白平衡(AWB)算法的信息。通常,Linux系统中的V4L2设备驱动程序会使用/dev/videoX这样的设备节点。
yavta是什么,官网是?
yavta是一个用于捕获视频帧的命令行工具。它可以在Linux操作系统下使用,并且支持多种格式和类型的视频设备。
yavta官网为:https://github.com/fastr/yavta //这就是最好的v4l2应用程序
Converting the raw Bayer image into YUV domain
在上述步骤之后处理的图像,可以按以下方式转换为YUV域。
Main output frames
raw2pnm -x2560 -y1920 -fNV12 /tmp/frames.out /tmp/frames.out.ppm
raw2pnm是什么:
raw2pnm是一种用于将RAW格式图像转换为PNM格式图像的在线工具。PNM是Portable aNy Map的缩写,是一种无损的位图格式,可以存储灰度图像、伪彩色图像和真彩色图像。该工具可以方便地将RAW格式图像转换为PNM格式,从而使用户能够轻松地编辑和操纵它们。
其中,2560x1920表示输出的分辨率,NV12是视频格式,后跟输入帧和输出PNM文件。
Viewfinder output frames
raw2pnm -x2560 -y1920 -fNV12 /tmp/frames.vf /tmp/frames.vf.ppm
其中,2560x1920表示输出的分辨率,NV12是视频格式,后跟输入帧和输出PNM文件。
Example user space code for IPU3
配置和使用IPU3的用户空间代码可以在以下链接中找到:https://chromium.googlesource.com/chromiumos/platform/arc-camera/+/master/。该源代码可以在hal/intel目录下找到。
Overview of IPU3 pipeline
IPU3管道有多个图像处理阶段,每个阶段都需要一组参数作为输入。管道的主要阶段如下所示:
Fig. 4: IPU3 ImgU Pipeline Diagram
下表是对以上算法的描述:
上表未列出的其他常见缩写:
管道的一些阶段将由运行在ISP处理器上的固件执行,而其他许多阶段将使用一组称为加速器集群(ACC)的固定硬件块来处理像素数据并生成统计信息。由struct ipu3_uapi_acc_param定义的各个算法的ACC参数可以通过嵌入在struct ipu3_uapi_params结构中的struct ipu3_uapi_flags由用户空间选择应用。对于被配置为未启用的参数,驱动程序将忽略相应的结构体,此时算法的现有配置将被保留。
1.1.6.10 The ivtv driver
这是用于Conexant cx23415/6 MPEG编解码器的v4l2设备驱动程序。cx23415可以进行编码和解码,而cx23416只能进行MPEG编码。目前唯一具有完整解码支持的卡是Hauppauge PVR-350。
注意:
1)该驱动程序需要最新的编码器固件(版本为2.06.039,大小为376836字节)。可以从以下网址获取固件:
https://linuxtv.org/downloads/firmware/#conexant
2)“普通”的电视应用程序无法与此驱动程序一起使用,您需要一个可以处理MPEG输入的应用程序,例如mplayer,xine,MythTV等。
IVTV项目的主要目标是为基于iCompression iTVC15或Conexant CX23415/CX23416 MPEG编解码器的视频捕获卡提供“clean room” Linux开源驱动程序的实现。
Features
• 通过调谐器或S-Video/Composite和音频输入线路进行广播视频(和声音)的硬件MPEG2捕获。
• 在硬件支持存在的情况下,支持FM广播电台的硬件MPEG2捕获。
• 支持NTSC、PAL、SECAM并带有立体声音频。
• 支持SAP和双语传输。
• 支持原始VBI(闭路字幕和电视文本)。
• 支持切片VBI(闭路字幕和电视文本),并能够将其插入到捕获的MPEG流中。
• 支持原始YUV和PCM输入。
Additional features for the PVR-350 (CX23415 based)
• 提供硬件MPEG2播放功能。
• 提供全面的OSD(屏幕显示:即覆盖在视频信号上的图形)。
• 提供帧缓冲(允许X应用程序出现在视频设备上)。
• 支持原始YUV输出。
重要提示:如果出现问题,请首先阅读此页面:https://help.ubuntu.com/community/Install_IVTV_Troubleshooting
See also
https://linuxtv.org
IRC
irc://irc.freenode.net/#v4l
Devices
目前最多允许12个ivtv板卡。
没有视频输出功能的卡(即非PVR350卡)缺少vbi8、vbi16、video16和video48设备。它们也不支持用于OSD的帧缓冲设备/dev/fbx。
radio0设备可能存在,也可能不存在,这取决于卡是否具有无线电调谐器。
以下是基本v4l设备列表:
Base devices
对于每张额外的卡,编号都会增加1。例如,/dev/video0被列为“基本”编码捕获设备,因此我们有:
• /dev/video0是第一张卡(卡0)的编码捕获设备
• /dev/video1是第二张卡(卡1)的编码捕获设备
• /dev/video2是第三张卡(卡2)的编码捕获设备
请注意,如果第一张卡没有某个功能(例如没有解码器,因此没有video16),则第二张卡仍将使用video17。
简单规则是“将卡号添加到基本设备号中”。如果您有其他捕获卡(例如WinTV PCI)被先检测到,则必须告诉ivtv模块,以便它从1(或2等)开始计数。否则,设备号可能会令人困惑。ivtv“ivtv_first_minor”模块选项可用于此目的。
• /dev/video0
编码捕获设备。
只读。
从此设备读取会得到MPEG1/2节目流。例如:
cat /dev/video0 > my.mpg (you need to hit ctrl-c to exit)
• /dev/video16
解码器输出设备
只写。仅当存在MPEG解码器(即CX23415)时才存在。
发送到此设备的mpeg2流将显示在所选视频显示器上,音频将显示在线路输出/音频输出中。它仅适用于支持视频输出的卡。
例如:
cat my.mpg >/dev/video16
• /dev/video24
原始音频捕获设备。
只读
从当前选择的调谐器或音频线路输入获取原始音频PCM立体声流。从此设备读取会得到一个原始(带符号的16位小端,48000 Hz,立体声pcm)捕获流。此设备仅捕获音频。将来应该用ALSA设备替换它。请注意,没有相应的原始音频输出设备,解码器固件不支持这个功能。
• /dev/video32
原始视频捕获设备
只读
从当前视频输入获取原始YUV视频输出。YUV格式是一个16x16线性平铺的NV12格式(V4L2_PIX_FMT_NV12_16L16)。
请注意,YUV和PCM流不同步,因此它们的用途有限。
• /dev/video48
原始视频显示设备
只写。仅当存在MPEG解码器(即CX23415)时才存在。
将YUV流写入卡的解码器。
• /dev/radio0
无线电调谐器设备
无法读取或写入
用于启用无线电调谐器并调谐到频率。您不能使用此设备读取或写入音频流。一旦您使用此设备调谐收音机,请使用/dev/video24读取原始pcm流或使用/dev/video0获取带有黑色视频的mpeg2流。
• /dev/vbi0
“垂直空白间隙”(Teletext、CC、WSS等)捕获设备
只读
捕获垂直空白间隙期间发送的原始(或分片)视频数据。这些数据用于编码电视文本、闭路字幕、VPS、宽屏信号、电子节目指南信息和其他服务。
• /dev/vbi8
处理过的vbi反馈设备
只读。仅当存在MPEG解码器(即CX23415)时才存在。
在MPEG流中嵌入的分片VBI数据会在此设备上复制。因此,在/dev/video16上播放录像时,您可以从/dev/vbi8读取嵌入的VBI数据。
• /dev/vbi16
vbi“显示”设备
只写。仅当存在MPEG解码器(即CX23415)时才存在。
可以用于将分片的VBI数据发送到视频输出连接器。
1.1.6.11 Vaio Picturebook Motion Eye Camera Driver
VAIO是什么:
VAIO是一家电脑品牌,其品牌名称来源于英文“Visual Audio Intelligent Organizer”(视听智能组织者)的缩写。VAIO旗下的产品包括笔记本电脑、台式机、平板电脑等。该品牌于1996年由索尼公司创立,在2014年时被出售并成为独立品牌,现由日本公司新日铁住金集团拥有。
这个驱动程序允许Motion Eye相机与video4linux兼容的应用程序一起使用。
此驱动程序需要编译和安装“Sony Laptop Extras”驱动程序(可以在内核配置实用程序的“其他设备”部分中找到),并使用其“camera=1”参数。
它最多可以以320x240的30 fps或640x480的15 fps进行抓取。
仅支持打包的YUV色彩空间。
通过私有API支持MJPEG硬件抓取(请参见下文)。
Hardware supported
该驱动程序支持“MotionEye”相机的第二个版本 :)
第一个版本是直接连接在Neomagic视频卡的视频总线上的,不受支持。
第二个版本由川崎制钢公司制造,由此驱动程序完全支持(PCI供应商/设备为0x136b / 0xff01)
第三个版本出现在最近(或多或少去年)的Picturebook(C1M *型号)中,但不受支持。制造商已根据NDA向开发人员提供了规格(允许GPL驱动程序的开发),但事情发展得不太快(请参见http://r-engine.sourceforge.net/)(PCI供应商/设备为0x10cf / 0x2011)。
第四个模型连接在TR1 * Vaio笔记本电脑的USB总线上。这个相机当前的驱动程序完全不受支持,实际上对于这个相机几乎没有可用的信息(USB供应商/设备为0x054c / 0x0107)。
Driver options
可以使用标准模块参数语法(在将选项传递给模块时为<param>=<value>,在将meye静态链接到内核时为kernel boot行上的meye.<param>=<value>)来向meye驱动程序传递多个选项。这些选项包括:
gbuffers: number of capture buffers, default is 2 (32 max)
gbufsize: size of each capture buffer, default is 614400
video_nr: video device to register (0 = /dev/video0, etc)
Module use
为了在使用时自动加载meye模块,可以将以下行放入您的/etc/modprobe.d/meye.conf文件中:
alias char-major-81 videodev
alias char-major-81-0 meye
options meye gbuffers=32
Usage:
xawtv >= 3.49 (<http://bytesex.org/xawtv/>)
for display and uncompressed video capture:
xawtv -c /dev/video0 -geometry 640x480
or
xawtv -c /dev/video0 -geometry 320x240
motioneye (<http://popies.net/meye/>)
for getting ppm or jpg snapshots, mjpeg video
1.1.6.12 OMAP 3 Image Signal Processor (ISP) driver
OMAP是什么:
OMAP代表Open Multimedia Applications Platform,是由德州仪器(Texas Instruments)开发的一个多媒体应用平台。它由一系列基于ARM架构的系统单芯片组成,可在移动设备、平板电脑、家庭娱乐系统和汽车信息娱乐系统等多种应用场景中使用。OMAP提供了处理器、图像处理、视频编解码、音频处理和通信单元等各种硬件和软件组件,可以支持多种操作系统和软件平台,例如Android、Linux、QT和Windows CE等。OMAP芯片具有低功耗、高性能、低成本和广泛的兼容性等优点,在移动设备市场上占据了重要地位。
Introduction
本文件记录了位于drivers/media/platform/omap3isp下的德州仪器OMAP 3图像信号处理器(ISP)驱动程序。最初的驱动程序由德州仪器编写,但自那以后,在Nokia进行了两次重写。
该驱动程序已成功用于以下OMAP 3版本:
• 3430
• 3530
• 3630
该驱动程序实现了V4L2、媒体控制器和v4l2_subdev界面。 使用内核中的v4l2_subdev界面支持传感器、镜头和闪光灯驱动程序。
Split to subdevs
OMAP 3 ISP被分成V4L2子设备,ISP中的每个块都有一个子设备来代表它。每个子设备为用户空间提供一个V4L2子设备接口。
• OMAP3 ISP CCP2
• OMAP3 ISP CSI2a
• OMAP3 ISP CCDC
• OMAP3 ISP 预览
• OMAP3 ISP 调整大小器
• OMAP3 ISP AEWB
• OMAP3 ISP AF
• OMAP3 ISP 直方图
ISP中的每个可能的链接都由Media controller接口中的链接建模。有关示例程序,请参见:
https://git.ideasonboard.org/?p=media-ctl.git;a=summary
Controlling the OMAP 3 ISP
一般来说,给OMAP 3 ISP的设置在以下帧的开始生效。当传感器在垂直消隐期间变为空闲状态时,会执行此操作。在内存到内存的操作中,管道每次仅运行一帧。将设置应用于帧之间。
ISP中的所有块(不包括CSI-2和可能的CCP2接收器)都要求接收完整的帧。因此,传感器永远不能向ISP发送部分帧。
Autoidle在3430上的某些ISP块存在问题,至少是这样的。只有在omap3isp模块参数autoidle为非零时,才会在3630上启用Autoidle功能。
Technical reference manuals (TRMs) and other documentation
OMAP 3430 TRM: <URL:http://focus.ti.com/pdfs/wtbu/OMAP34xx_ES3.1.x_PUBLIC_TRM_vZM.zip>
参考日期为2011年3月5日。
OMAP 35xx TRM: <URL:http://www.ti.com/litv/pdf/spruf98o>
参考日期为2011年3月5日。
OMAP 3630 TRM: <URL:http://focus.ti.com/pdfs/wtbu/OMAP36xx_ES1.x_PUBLIC_TRM_vQ.zip>
参考日期为2011年3月5日。
DM 3730 TRM: <URL:http://www.ti.com/litv/pdf/sprugn4h>
参考日期为2011年3月6日。
1.1.6.13 OMAP4 ISS Driver
Introduction
OMAP44XX芯片系列包含图像子系统(也称为ISS),其中包含可以分为3个大组的几个组件:
•接口(2个接口:CSI2-A和CSI2-B / CCP2)
• ISP(图像信号处理器)
• SIMCOP(静态图像协处理器)
有关更多信息,请查看1中的“OMAP4430多媒体设备硅修订版2.x”的最新版本。
从AB版本开始,ISS在第8节中进行了详细描述。
该驱动程序目前仅支持CSI2-A / B接口。它利用Media Controller框架2,并且继承了OMAP3 ISP驱动程序的大部分代码(位于drivers/media/platform/omap3isp/*下),但现在不需要IOMMU来进行ISS缓冲区内存映射。
现在仅支持MMAP缓冲区的使用。
Tested platforms
该驱动程序已经在以下平台上进行过测试:
• OMAP4430SDP,搭载ES2.1 GP和SEVM4430-CAM-V1-0(包含IMX060和OV5640,但仅支持后者,输出YUV422帧)。
• TI Blaze MDP,配备OMAP4430 ES2.2 EMU(包含1个IMX060和2个OV5650传感器,但仅支持OV5650,输出RAW10帧)。
• PandaBoard,Rev.A2,配备OMAP4430 ES2.1 GP和OV适配器板,测试了以下传感器:* OV5640 * OV5650。
• 在主线内核上进行了测试:http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=summary,标签为v3.3(提交为c16fa4f2ad19908a47c63d8fa436a1178438c7e7)。
File list
drivers/staging/media/omap4iss/ include/linux/platform_data/media/omap4iss.h
1.1.6.14 Philips webcams (pwc driver)
webcam是usb camera么?
是的,Webcam通常指的是USB摄像头,也称为网络摄像头。
这个文件包含一些有关飞利浦和OEM网络摄像头的附加信息。电子邮件:webcam@smcc.demon.nl,上次更新时间为2004-01-19,网站为http://www.smcc.demon.nl/webcam/。
目前支持以下摄像头:
• 飞利浦PCA645
• 飞利浦PCA646
• 飞利浦PCVC675
• 飞利浦PCVC680
• 飞利浦PCVC690
• 飞利浦PCVC720/40
• 飞利浦PCVC730
• 飞利浦PCVC740
• 飞利浦PCVC750
• Askey VC010
• 创新实验室Webcam 5
• 创新实验室Webcam Pro Ex
• 罗技QuickCam 3000 Pro
• 罗技QuickCam 4000 Pro
• 罗技QuickCam Notebook Pro
• 罗技QuickCam Zoom
• 罗技QuickCam Orbit
• 罗技QuickCam Sphere
• 三星MPC-C10
• 三星MPC-C30
• Sotec Afina Eye
• AME CU-001
• Visionite VCS-UM100
• Visionite VCS-UC300
飞利浦驱动程序的主网页位于上述地址。它包含了许多额外的信息、FAQ和二进制插件“PWCX”。该插件包含了解压缩例程,允许您使用更高的图像尺寸和帧速率;此外,网络摄像头在USB总线上使用的带宽更少(如果您想要同时运行多个相机,则非常方便)。这些例程受NDA保护,因此不能作为源代码进行分发;但是,它的使用完全是可选的。
您可以将此代码构建到内核中,也可以构建为模块。我推荐后者,因为它可以更容易地进行故障排除。通过USB音频类支持内置麦克风。
当您加载模块时,您可以为摄像头设置一些默认设置;某些程序依赖于特定的图像大小或格式,并且可能不知道如何在驱动程序中正确设置它。可用的选项有:
size
该选项可以是“sqcif”、“qsif”、“qcif”、“sif”、“cif”或“vga”,分别对应于图像尺寸为128x96、160x120、176x144、320x240、352x288和640x480(当然,仅针对支持这些分辨率的相机)。
fps
指定所需的帧率,是4-30范围内的整数。
fbufs
表示要用于存储摄像头帧的内部缓冲区的数量。如果从摄像头读取图像的进程有些慢或短暂忙碌,这将有所帮助。但是,在较慢的机器上,它只会产生延迟,因此请谨慎选择。默认值为3,这是合理的。您可以将其设置在2到5之间。
mbufs
是一个1到10之间的整数,它告诉模块为mmap()、VIDIOCCGMBUF、VIDIOCMCAPTURE和其他相关操作保留的缓冲区数量。默认值为2,对于大多数应用程序来说已经足够了(双缓冲)。如果使用支持mmap()的工具进行抓取时遇到了很多“Dumping frame…”消息,则可以增加此值。然而,它并没有真正缓存图像,只是在您的程序落后时给您更多的余地。但是,您需要一个多线程或分叉程序才能真正利用这些缓冲区。绝对最大值为10,但不要将其设置得太高!每个缓冲区占用460 KB的内存,因此除非您有大量内存,否则将其设置为大于4的值是绝对浪费。此内存仅在打开时分配,因此在不使用相机时不会浪费。
power_save
启用后(设置为1),模块将尝试在关闭时关闭摄像头,并在打开时重新激活。这将节约电力并关闭LED灯。但并非所有相机都支持此功能(645和646根本没有节能功能),有些型号也不可用(它们将关闭,但永远无法唤醒)。考虑到这是实验性的,该选项默认禁用。
compression
(仅适用于插件)此选项可控制相机在通过USB总线压缩图像时使用的压缩因子。您可以将参数设置为0到3之间:
0 = prefer uncompressed images; if the requested mode is not available
in an uncompressed format, the driver will silently switch to low
compression.
1 = low compression.
2 = medium compression.
3 = high compression.
高压缩当然需要更少的带宽,但也可能引入一些不必要的伪影。默认值为2,中等压缩。查看网站上的常见问题解答以了解哪些模式需要压缩的概述。
压缩参数不适用于645和646相机以及从它们派生的OEM型号(只有很少数)。大多数相机都支持此参数。
leds
设置需要两个整数,用于定义LED的开/关时间(以毫秒为单位)。其中一个有趣的用途是在使用相机时让LED闪烁。可以通过以下方式实现:
leds=500,500
将会使led每秒闪烁一次,但如下参数:
leds=0,0
LED永远不会亮起,适合进行安静的监视。默认情况下,相机在使用时LED保持点亮状态,并在不再使用相机时关闭。
此参数仅适用于ToUCam系列相机(720、730、740、750)和OEM型号。对于其他相机,该命令将被忽略,并且无法控制LED。
最后:此参数在第一次打开相机设备之前不会生效。在此之前,LED仍然保持点亮状态。
dev_hint
USB设备的一个长期问题是其动态性:您永远不知道摄像头将被分配给哪个设备;它取决于模块加载顺序、集线器配置、设备插入顺序以及月相(即它可能是随机的)。使用此选项,您可以向驱动程序提供提示,告诉它应该使用哪个视频设备节点(/dev/videoX)与特定摄像头配对。如果您有两个相同型号的摄像头,这也很方便。
摄像机由其类型(摄像机型号中的数字,如PCA645、PCVC750VC等)指定,可选地包括序列号(在/sys/kernel/debug/usb/devices中可见)。提示由具有以下格式的字符串组成:
[type[.serialnumber]:]node
方括号表示类型和序列号都是可选的,但是不能指定序列号而不指定类型(这将没有意义)。序列号与类型之间用“.”分隔;节点编号用“:”分隔。
以下是一些例子,以更好地解释这种有点神秘的语法:
值得注意的一些要点:
• 序列号区分大小写,必须完整地写出,包括前导零(它被视为字符串)。
• 如果设备节点已经被占用,注册将失败,摄像头将不可用。
• 您最多可以有64个视频设备;如果要扩展编号,请确保在/dev中有足够的设备节点。 /dev/video9之后是/dev/video10(而不是/dev/videoA)。
• 如果相机与任何dev_hint不匹配,则像以前一样,它将被分配给第一个可用的设备节点。
trace
为了更好地检测问题,现在可以打开模块进行某些调用的“跟踪”;它记录内核日志中的所有项,以调试级别。
跟踪变量是一个位掩码;每个位表示特定的功能。如果您想要跟踪某些内容,请在下表中查找位值,将这些值相加并提供给跟踪变量。
例如,要跟踪open()和read()函数,将8 + 4求和得到12,因此您在insmod或modprobe期间应提供trace=12。如果要关闭初始化和探测跟踪,则将trace设置为0。跟踪的默认值为35(0x23)。
例子:
# modprobe pwc size=cif fps=15 power_save=1
fbufs、mbufs和trace参数是全局的,并适用于所有连接的摄像头。每个摄像头都有自己的缓冲区集。
size和fps仅在打开设备时指定默认值;这是为了适应某些不设置大小的工具。您可以使用Video4Linux ioctl()调用在open()之后更改这些设置。默认值为QCIF大小,帧速率为10 fps。
压缩参数是半全局的;它设置所有摄像机的初始压缩偏好,但是可以使用VIDIOCPWCSCQUAL ioctl()调用为每个摄像机设置此参数。
所有参数都是可选的。
1.1.6.15 Qualcomm Camera Subsystem driver
Introduction
The Qualcomm Camera Subsystem driver是位于drivers/media/platform/qcom/camss下的驱动程序,本文件记录了该驱动程序。
当前版本的驱动程序支持位于Qualcomm MSM8916/APQ8016和MSM8996/APQ8096处理器上的Camera Subsystem。
驱动程序实现了V4L2、Media controller和V4L2子设备接口。支持在内核中使用V4L2子设备接口的摄像头传感器。
该驱动程序是使用Qualcomm Camera Subsystem driver for Android作为参考实现的,在Code Aurora中可以找到此驱动程序。
Qualcomm Camera Subsystem hardware
驱动程序支持的8x16 / 8x96处理器上的Camera Subsystem硬件由以下组成:
• 2 / 3个CSIPHY模块。它们处理CSI2接收器的物理层。每个CSIPHY模块都可以连接一个单独的摄像头传感器;
• 2 / 4个CSID (CSI解码器)模块。它们处理CSI2接收器的协议和应用层。每个CSID都可以解码来自任何CSIPHY的数据流。每个CSID还包含一个TG (Test Generator测试生成器)模块,可用于生成人工输入数据以进行测试;
• ISPIF (ISP接口)模块。处理从CSIDs到VFE输入的数据流路由;
• 1 / 2个VFE (视频前端)模块。包含一系列图像处理硬件块的管道。VFE具有不同的输入接口。PIX (Pixel像素)输入接口将输入数据馈送到图像处理管道。图像处理管道末端还包含缩放和裁剪模块。三个RDI (Raw Dump Interface原始转储接口)输入接口绕过了图像处理管道。VFE还包含AXI总线接口,用于将输出数据写入内存。
高通的IFE和IPE具体解释下:
IFE是指“Image Front End”,中文名为“图像前端”,是高通芯片上的一个硬件处理单元。IFE负责接收来自摄像头传感器的原始数据,并进行处理,包括基于ISP(Image Signal Processor,图像信号处理器)的降噪和色彩校正等功能。IFE可将处理后的图像数据传递给IPE或其他处理器进行进一步处理。
IPE是指 “Image Processing Engine”,中文名为“图像处理引擎”,也是高通芯片上的一个硬件处理单元,主要负责对IFE处理后的图像数据进行二次处理,包括图像去噪、锐化、对比度调整、色彩映射等功能,最终将处理完的图像数据输出给显示屏或编码器。同时,IPE还可以利用GPU进行加速处理,提升处理性能。
总之,IFE和IPE都是高通芯片上的图像处理硬件模块,IFE主要负责处理原始数据,而IPE则进一步对处理后的数据进行优化和增强,两者结合起来可以实现高质量和高效率的图像处理。
Supported functionality
当前版本的驱动程序支持以下功能:
• 通过CSIPHY从相机传感器读取输入数据;
• 在CSID中由TG生成测试输入数据;
• VFE的RDI接口
=将输入数据原始转储到内存
支持的格式包括:
*YUYV/UYVY/YVYU/VYUY(打包的YUV 4:2:2格式 - V4L2_PIX_FMT_YUYV/V4L2_PIX_FMT_UYVY/V4L2_PIX_FMT_YVYU/V4L2_PIX_FMT_VYUY);
*MIPI RAW8(8位Bayer RAW格式 - V4L2_PIX_FMT_SRGGB8/V4L2_PIX_FMT_SGRBG8/V4L2_PIX_FMT_SGBRG8/V4L2_PIX_FMT_SBGGR8);
*MIPI RAW10(10位打包Bayer RAW格式 - V4L2_PIX_FMT_SBGGR10P/V4L2_PIX_FMT_SGBRG10P/V4L2_PIX_FMT_SGRBG10P/V4L2_PIX_FMT_SRGGB10P/V4L2_PIX_FMT_Y10P);
*MIPI RAW12 (12位打包Bayer RAW格式 - V4L2_PIX_FMT_SRGGB12P/V4L2_PIX_FMT_SGBRG12P/V4L2_PIX_FMT_SGRBG12P/V4L2_PIX_FMT_SRGGB12P);
*(仅限8x96)MIPI RAW14(14位打包Bayer RAW格式 - V4L2_PIX_FMT_SRGGB14P/V4L2_PIX_FMT_SGBRG14P/V4L2_PIX_FMT_SGRBG14P/V4L2_PIX_FMT_SRGGB14P);
=(仅限8x96)输入数据的格式转换
支持的输入格式包括:
*MIPI RAW10(10位打包Bayer RAW格式 - V4L2_PIX_FMT_SBGGR10P/V4L2_PIX_FMT_Y10P)
支持的输出格式包括:
*Plain16 RAW10(10位未打包Bayer RAW格式 - V4L2_PIX_FMT_SBGGR10/V4L2_PIX_FMT_Y10)
• VFE的PIX接口
=输入数据的格式转换。
支持的输入格式包括:
*YUYV/UYVY/YVYU/VYUY(打包的YUV 4:2:2格式 - V4L2_PIX_FMT_YUYV/V4L2_PIX_FMT_UYVY/V4L2_PIX_FMT_YVYU/V4L2_PIX_FMT_VYUY),
支持的输出格式包括:
*NV12/NV21(两个平面的YUV 4:2:0格式 - V4L2_PIX_FMT_NV12/V4L2_PIX_FMT_NV21)
*NV16/NV61(两个平面的YUV 4:2:2格式 - V4L2_PIX_FMT_NV16/V4L2_PIX_FMT_NV61)。
*(仅限8x96) YUYV/UYVY/YVYU/VYUY(打包的YUV 4:2:2格式 -V4L2_PIX_FMT_YUYV/V4L2_PIX_FMT_UYVY/V4L2_PIX_FMT_YVYU/V4L2_PIX_FMT_VYUY)。
=缩放支持。
配置VFE编码器比例模块进行最多16倍的缩小。
=裁剪支持。
配置VFE编码器裁剪模块。
• 两个(8x96:三个)数据输入的并发且独立使用——可以是相机传感器和/或TG。
Driver Architecture and Design
驱动程序实现了V4L2子设备接口。为了模拟模块之间的硬件连接并公开一个清晰、逻辑和可用的接口,驱动程序被分成以下V4L2子设备(8x16 / 8x96):
• 2 / 3个CSIPHY子设备 - 每个CSIPHY由单个子设备表示;
• 2 / 4个CSID子设备 - 每个CSID由单个子设备表示;
• 2 / 4个ISPIF子设备 - ISPIF由等于CSID子设备数量的子设备表示;
• 4 / 8个VFE子设备 - 每个VFE由等于输入接口数量的子设备表示(每个VFE有3个RDI和1个PIX)。
将驱动程序拆分成这种特定方式的考虑如下:
• 通过为每个模块设置单独的子设备来表示CSIPHY和CSID模块,可以模拟这些模块之间的硬件连接;
• 通过为每个输入接口设置单独的子设备来表示VFE,使得可以并发且独立地使用输入接口,因为硬件支持这样做;
• 通过为ISPIF设置等于CSID子设备数量的子设备数量,可以在同时使用两个摄像头时创建线性媒体控制器管道。这避免了管道中的分支,否则将需要a)用户空间和b)媒体框架(例如开关机操作)来假设从接收器垫到单个媒体实体上的来源垫的数据流。
每个VFE子设备都链接到一个单独的视频设备节点。媒体控制器管道图如下(连接了两个/三个OV5645相机传感器):
Fig. 5: Media pipeline graph 8x16
Fig. 6: Media pipeline graph 8x96
Implementation
目前支持的功能不需要对硬件进行运行时配置(在流处理过程中更新设置)。每个硬件模块的完整配置都是基于当前活动的媒体链接、格式和控制集在STREAMON ioctl上应用的。
VFE中缩放器模块的输出大小是使用“msm_vfe0_pix”实体的接收器垫上的实际组合选择矩形来配置的。
VFE中裁剪模块的裁剪输出区域是使用“msm_vfe0_pix”实体的来源垫上的实际裁剪选择矩形来配置的。
Documentation
APQ8016是高通的骁龙410处理器,而APQ8096是骁龙820E处理器。
APQ8016 Specifification:
https://developer.qualcomm.com/download/sd410/snapdragon-410-processor-device-specification.pdf
Referenced 2016-11-24.
APQ8096 Specifification:
https://developer.qualcomm.com/download/sd820e/qualcomm-snapdragon-820e-processor-apq8096sge-device-specification.pdf
Referenced 2018-06-22.
1.1.6.17 Rockchip Image Signal Processor (rkisp1)
Introduction
这个文件记录了Rockchip ISP1驱动程序,它是RK3288和RK3399 SoCs的一部分。驱动程序位于drivers/staging/media/rkisp1下,并使用Media-Controller API。
Revisions
这个ISP存在多个较小的修订版本,这些版本在后来的SoCs中得到了引入。可以在UAPI中的枚举rkisp1_cif_isp_version中找到修订版本,并且可以通过ioctl MEDIA_IOC_DEVICE_INFO返回的struct media_device_info中的hw_revision字段读取运行SoC中ISP的版本。
目前使用的版本有:
• RKISP1_V10:至少用于rk3288和rk3399
• RKISP1_V11:在原始供应商代码中声明,但未使用
• RKISP1_V12:至少用于rk3326和px30
• RKISP1_V13:至少用于rk1808
Topology
这个驱动程序具有4个视频设备:
• rkisp1_mainpath:用于检索图像的捕获设备,通常具有更高的分辨率。
• rkisp1_selfpath:用于检索图像的捕获设备。
• rkisp1_stats:发送统计信息的元数据捕获设备。
• rkisp1_params:接收来自用户空间的参数配置的元数据输出设备。
该驱动程序有3个子设备:
• rkisp1_resizer_mainpath:用于调整大小和降低样本频率以适合主路径捕获设备的帧。
• rkisp1_resizer_selfpath:用于调整大小和降低样本频率以适合自身路径捕获设备的帧。
• rkisp1_isp:连接到传感器,负责所有ISP操作。
rkisp1_mainpath, rkisp1_selfpath - Frames Capture Video Nodes
这些是主路径和自身路径捕获设备,用于捕获帧。 这些实体是将帧写入内存的DMA引擎。 自身路径视频设备可以捕获YUV / RGB格式。 其输入为YUV编码流,可以将其转换为RGB格式。 自身路径无法捕获Bayer格式。 主路径可以捕获Bayer和YUV格式,但无法捕获RGB格式。 两个捕获视频都支持V4L2_CAP_IO_MC功能。
rkisp1_resizer_mainpath, rkisp1_resizer_selfpath - Resizers Subdevices Nodes
这些是主路径和自身路径的调整大小实体。 这些实体可以将帧放大或缩小,并且还可以更改YUV采样(例如YUV4:2:2-> YUV4:2:0)。 它们在sink pad上还具有裁剪功能。 调整大小实体只能在YUV:4:2:2格式(MEDIA_BUS_FMT_YUYV8_2X8)上运行。 主路径捕获设备支持以Bayer格式捕获视频。 在这种情况下,主路径的调整大小器设置为“旁路”模式-它只是将帧转发而无需操作。
rkisp1_isp - Image Signal Processing Subdevice Node
这是ISP实体。它连接到接收器端口0上的传感器,并使用CSI-2协议接收帧。 它负责配置CSI-2协议。 它在连接到传感器的sink pad 0和连接到调整大小实体的source pad 2上具有裁剪功能。 在sink pad 0上裁剪定义了从传感器获取的图像区域。 在source pad 2上裁剪定义了用于图像稳定器(IS)的区域。
rkisp1_stats - Statistics Video Node
统计视频节点输出正在由rkisp1处理的帧的3A(自动对焦,自动曝光和自动白平衡)统计数据,以及直方图统计信息。应用程序可以使用这些数据,通过rkisp_params节点重新调整驱动程序,从而提高视频流中的图像质量。缓冲区格式由struct rkisp1_stat_buffer定义,用户空间应将V4L2_META_FMT_RK_ISP1_STAT_3A设置为数据格式。
rkisp1_params - Parameters Video Node
rkisp1_params视频节点从用户空间接收一组参数,以在视频流期间应用于硬件,允许用户空间动态修改诸如黑电平、串扰校正等值。缓冲区格式由struct rkisp1_params_cfg定义,用户空间应将V4L2_META_FMT_RK_ISP1_PARAMS设置为数据格式。
Capturing Video Frames Example
在下面的示例中,“rkisp1_isp”的pad 0连接的传感器是imx219。可以使用以下命令以900x800平面格式YUV 4:2:2从selfpath视频节点捕获视频。它使用所有可能的裁剪能力(见下面的解释)
# set the links
"media-ctl" "-d" "platform:rkisp1" "-r"
"media-ctl" "-d" "platform:rkisp1" "-l" "'imx219 4-0010':0 -> 'rkisp1_isp':0 [1]"
"media-ctl" "-d" "platform:rkisp1" "-l" "'rkisp1_isp':2 -> 'rkisp1_resizer_selfpath':0 [1]"
"media-ctl" "-d" "platform:rkisp1" "-l" "'rkisp1_isp':2 -> 'rkisp1_resizer_mainpath':0 [0]"# set format for imx219 4-0010:0
"media-ctl" "-d" "platform:rkisp1" "--set-v4l2" '"imx219 4-0010":0 [fmt:SRGGB10_1X10/1640x1232]'# set format for rkisp1_isp pads:
"media-ctl" "-d" "platform:rkisp1" "--set-v4l2" '"rkisp1_isp":0 [fmt:SRGGB10_1X10/1640x1232 crop: (0,0)/1600x1200]'
"media-ctl" "-d" "platform:rkisp1" "--set-v4l2" '"rkisp1_isp":2 [fmt:YUYV8_2X8/1600x1200 crop: (0,0)/1500x1100]'# set format for rkisp1_resizer_selfpath pads:
"media-ctl" "-d" "platform:rkisp1" "--set-v4l2" '"rkisp1_resizer_selfpath":0 [fmt:YUYV8_2X8/1500x1100 crop: (300,400)/1400x1000]'
"media-ctl" "-d" "platform:rkisp1" "--set-v4l2" '"rkisp1_resizer_selfpath":1 [fmt:YUYV8_2X8/900x800]'# set format for rkisp1_selfpath:
"v4l2-ctl" "-z" "platform:rkisp1" "-d" "rkisp1_selfpath" "-v" "width=900,height=800,"
"v4l2-ctl" "-z" "platform:rkisp1" "-d" "rkisp1_selfpath" "-v" "pixelformat=422P"# start streaming:
v4l2-ctl "-z" "platform:rkisp1" "-d" "rkisp1_selfpath" "--stream-mmap" "--stream-count" "10"
v4l2-ctl的"--stream-mmap"什么作用:
--stream-mmap Enable mmap streaming
该选项用于启用mmap流式传输。mmap是一种内存映射I/O技术,可以将文件或设备映射到进程的地址空间中。在视频流传输期间,使用mmap流式传输技术可以有效地减少数据拷贝次数,提高性能。
在上述示例中,传感器被配置为Bayer格式:SRGGB10_1X10 / 1640x1232。应该将rkisp1_isp:0 pad配置为与传感器相同的mbus格式和尺寸,否则会出现“EPIPE”错误。因此,它也被配置为SRGGB10_1X10 / 1640x1232。此外,应将rkisp1_isp:0 pad配置为裁剪(0,0)/ 1600x1200。
裁剪尺寸会自动传播到isp源端口rkisp1_isp:2的格式中。在isp源端口上还配置了另一个裁剪操作:(0,0)/ 1500x1100。 Resizer的sink pad rkisp1_resizer_selfpath应该配置为YUYV8_2X8 / 1500x1100格式,以匹配链接另一侧的格式。此外,在其上也配置了裁剪(300,400)/ 1400x1000。
resizer的源pad,rkisp1_resizer_selfpath:1被配置为YUYV8_2X8 / 900x800格式。这意味着resizer首先从接收到的帧中裁剪出一个(300,400)/ 1400x1000的窗口,然后将此窗口缩放到900x800的尺寸。
请注意,上述示例未使用 stats-params 控制循环。因此,捕获的帧不会经过3A算法,可能质量不好,甚至可能显得暗淡和绿色调。在摄像机中,3A代表自动对焦、自动白平衡和自动曝光控制,这些算法可以使图像得到更好的质量。如果您需要更好的图像质量,请使用正确的相机IQ文件,并确保启用3A算法,以便使图像具有更佳的质量。
Configuring Quantization
驱动程序支持对YUV格式进行有限范围和全范围量化,其中有限是默认值。要在它们之间切换,用户空间应该使用Colorspace Conversion API(CSC)对isp(rkisp1_isp:2)源端口2上的子设备进行配置。在此端口上配置的量化是主路径和自路径视频节点捕获的视频帧的量化。请注意,即使在rkisp1_isp:2上将量化设置为全范围,resizer和capture实体仍将始终报告V4L2_QUANTIZATION_DEFAULT。因此,为了获取已配置的量化,应用程序应从pad rkisp1_isp:2获取它。
1.1.6.19 The Silicon Labs Si470x FM Radio Receivers driver
Information from Silicon Labs
Silicon Laboratories是无线电IC的制造商,现在它们是手机中最常用的无线接收器。通常它们与I2C连接。但SiLabs还提供了一个参考设计,将这个IC与一个小型微控制器C8051F321集成在一起,形成一个USB无线电。参考设计的一部分还包括一个二进制和源代码的无线电应用程序。该软件还包含了自动固件升级到最新版本的功能。可以在网站http://www.silabs.com/usbradio下载相关信息。
Supported ICs
以下IC具有非常相似的寄存器集,因此它们现在或将来某个时候都将由驱动程序支持:
• Si4700:FM无线电接收机
• Si4701:FM无线电接收机,RDS支持
• Si4702:FM无线电接收机
• Si4703:FM无线电接收机,RDS支持
• Si4704:FM无线电接收机,不需要外部天线
• Si4705:FM无线电接收机,不需要外部天线,RDS支持,Dig I / O
• Si4706:增强型FM RDS / TMC无线电接收机,不需要外部天线,RDS支持
• Si4707:带SAME解码器的专用气象波段无线电接收机,RDS支持
• Si4708:最小的FM接收器
• Si4709:最小的FM接收器,RDS支持
可以在网站http://www.silabs.com/products/mcu/Pages/USBFMRadioRD.aspx下载有关这些的更多信息。
Supported USB devices
目前已知以下搭载了Silicon Labs si470x芯片的USB收音机(供应商:产品)可以工作:
• 10c4:818a: Silicon Labs USB FM Radio 参考设计
• 06e1:a155: ADS/Tech FM Radio 接收器(以前的Instant FM Music)(RDX-155-EF)
• 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700(FM700)
• 10c5:819a: Sanei Electric, Inc. FM USB收音机(在DealExtreme.com上销售为PCear)
Software
通常情况下,大多数应用程序都在Debian/testing下进行测试:
• fmtools-用于管理FM调谐器卡的实用程序
• gnomeradio-用于GNOME桌面的FM无线电调谐器
• gradio-GTK FM无线电调谐器
• kradio-适用于KDE的舒适收音机应用程序
• radio-基于ncurses的无线电应用程序
• mplayer-适用于Linux的终极电影播放器
• v4l2-ctl-命令行video4linux实用程序集合
例如,您可以使用以下命令:
v4l2-ctl -d /dev/radio0 --set-ctrl=volume=10,mute=0 --set-freq=95.21 --all
还有一个库libv4l可供使用。它将具有用于通过硬件功能(如radio-si470x)或通过实现每个提到的程序中目前正在使用的函数来进行频率搜索的函数。某些时候,无线电程序应该利用libv4l。
为了处理RDS信息,正在进行一个名为http://rdsd.berlios.de/的项目。
目前还没有使TMC句子可读的项目。
Audio Listing
USB音频由ALSA snd_usb_audio模块提供。建议还选择SND_USB_AUDIO,因为这是从收音机获取声音所必需的。要列出设备,请使用以下命令之一重定向声音。请根据您的需求调整音频设备(/dev/dsp*和hw:x,x)。
如果您只想测试音频(质量非常差),可以使用以下命令:
cat /dev/dsp1 > /dev/dsp
If you use sox + OSS try:
sox -2 --endian little -r 96000 -t oss /dev/dsp1 -t oss /dev/dsp
or using sox + alsa:
sox --endian little -c 2 -S -r 96000 -t alsa hw:1 -t alsa -r 96000 hw:0
If you use arts try:
arecord -D hw:1,0 -r96000 -c2 -f S16_LE | artsdsp aplay -B -
If you use mplayer try:
mplayer -radio adevice=hw=1.0:arate=96000 \
-rawaudio rate=96000 \
radio://<frequency>/capture
Module Parameters
加载模块后,您仍可以在sysfs挂载下的/sys/module/radio_si470x/parameters中访问其中的一些。只读文件(0444)的内容不会被更新,即使使用私有视频控件更改空间、频带和DE。其他参数可以在运行时更改。
Errors
如果经常出现-EIO错误,请增加tune_timeout时间。当超时或者达到频带限制时,hw_freq_seek函数会返回-EAGAIN。如果您从snd_usb_audio获得任何错误,请向ALSA人员报告这些错误。
Open Issues
V4L次设备的分配和参数设置并不完美。目前正在讨论解决方案。
有一个用于下载/上传新固件映像的USB接口。可以使用request_firmware接口实现对它的支持。
有一个RDS中断模式。驱动程序已经使用了相同的接口来轮询RDS信息,但目前没有使用中断模式。
有一个LED接口,可用于覆盖固件中编程的LED控制。这可以通过内核中的LED支持函数实现。
Other useful information and links
http://www.silabs.com/usbradio
1.1.6.19 The Silicon Labs Si470x FM Radio Receivers driver
Information from Silicon Labs
Silicon Laboratories是无线电IC的制造商,现在它们是手机中最常用的无线接收器。通常它们与I2C连接。但SiLabs还提供了一个参考设计,将这个IC与一个小型微控制器C8051F321集成在一起,形成一个USB无线电。参考设计的一部分还包括一个二进制和源代码的无线电应用程序。该软件还包含了自动固件升级到最新版本的功能。可以在网站http://www.silabs.com/usbradio下载相关信息。
Supported ICs
以下IC具有非常相似的寄存器集,因此它们现在或将来某个时候都将由驱动程序支持:
• Si4700:FM无线电接收机
• Si4701:FM无线电接收机,RDS支持
• Si4702:FM无线电接收机
• Si4703:FM无线电接收机,RDS支持
• Si4704:FM无线电接收机,不需要外部天线
• Si4705:FM无线电接收机,不需要外部天线,RDS支持,Dig I / O
• Si4706:增强型FM RDS / TMC无线电接收机,不需要外部天线,RDS支持
• Si4707:带SAME解码器的专用气象波段无线电接收机,RDS支持
• Si4708:最小的FM接收器
• Si4709:最小的FM接收器,RDS支持
可以在网站http://www.silabs.com/products/mcu/Pages/USBFMRadioRD.aspx下载有关这些的更多信息。
Supported USB devices
目前已知以下搭载了Silicon Labs si470x芯片的USB收音机(供应商:产品)可以工作:
• 10c4:818a: Silicon Labs USB FM Radio 参考设计
• 06e1:a155: ADS/Tech FM Radio 接收器(以前的Instant FM Music)(RDX-155-EF)
• 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700(FM700)
• 10c5:819a: Sanei Electric, Inc. FM USB收音机(在DealExtreme.com上销售为PCear)
Software
通常情况下,大多数应用程序都在Debian/testing下进行测试:
• fmtools-用于管理FM调谐器卡的实用程序
• gnomeradio-用于GNOME桌面的FM无线电调谐器
• gradio-GTK FM无线电调谐器
• kradio-适用于KDE的舒适收音机应用程序
• radio-基于ncurses的无线电应用程序
• mplayer-适用于Linux的终极电影播放器
• v4l2-ctl-命令行video4linux实用程序集合
例如,您可以使用以下命令:
v4l2-ctl -d /dev/radio0 --set-ctrl=volume=10,mute=0 --set-freq=95.21 --all
还有一个库libv4l可供使用。它将具有用于通过硬件功能(如radio-si470x)或通过实现每个提到的程序中目前正在使用的函数来进行频率搜索的函数。某些时候,无线电程序应该利用libv4l。
为了处理RDS信息,正在进行一个名为http://rdsd.berlios.de/的项目。
目前还没有使TMC句子可读的项目。
Audio Listing
USB音频由ALSA snd_usb_audio模块提供。建议还选择SND_USB_AUDIO,因为这是从收音机获取声音所必需的。要列出设备,请使用以下命令之一重定向声音。请根据您的需求调整音频设备(/dev/dsp*和hw:x,x)。
如果您只想测试音频(质量非常差),可以使用以下命令:
cat /dev/dsp1 > /dev/dsp
If you use sox + OSS try:
sox -2 --endian little -r 96000 -t oss /dev/dsp1 -t oss /dev/dsp
or using sox + alsa:
sox --endian little -c 2 -S -r 96000 -t alsa hw:1 -t alsa -r 96000 hw:0
If you use arts try:
arecord -D hw:1,0 -r96000 -c2 -f S16_LE | artsdsp aplay -B -
If you use mplayer try:
mplayer -radio adevice=hw=1.0:arate=96000 \
-rawaudio rate=96000 \
radio://<frequency>/capture
Module Parameters
加载模块后,您仍可以在sysfs挂载下的/sys/module/radio_si470x/parameters中访问其中的一些。只读文件(0444)的内容不会被更新,即使使用私有视频控件更改空间、频带和DE。其他参数可以在运行时更改。
Errors
如果经常出现-EIO错误,请增加tune_timeout时间。当超时或者达到频带限制时,hw_freq_seek函数会返回-EAGAIN。如果您从snd_usb_audio获得任何错误,请向ALSA人员报告这些错误。
Open Issues
V4L次设备的分配和参数设置并不完美。目前正在讨论解决方案。
有一个用于下载/上传新固件映像的USB接口。可以使用request_firmware接口实现对它的支持。
有一个RDS中断模式。驱动程序已经使用了相同的接口来轮询RDS信息,但目前没有使用中断模式。
有一个LED接口,可用于覆盖固件中编程的LED控制。这可以通过内核中的LED支持函数实现。
Other useful information and links
http://www.silabs.com/usbradio
1.1.6.20 The Silicon Labs Si4713 FM Radio Transmitter Driver
Information about the Device
这个芯片是Silicon Labs的产品。它是一个I2C设备,当前在0x63地址上。基本上,它具有传输和信号噪声级别测量功能。
Si4713集成了FM广播立体声传输的传输功能。该芯片还允许进行集成接收功率扫描,以识别低信号功率的FM频道。
该芯片使用命令和响应进行编程。此外,还有几个属性可以改变该芯片的行为。
用户必须遵守当地有关无线电频率(RF)传输的法规。
Device driver description
处理这个设备有两个模块,一个是I2C设备驱动程序,另一个是平台驱动程序。
I2C设备驱动程序向内核导出一个v4l2-subdev接口。所有属性也可以通过v4l2扩展控件接口访问,使用v4l2-subdev调用(g_ext_ctrls、s_ext_ctrls)即可。
平台设备驱动程序向用户空间导出一个v4l2无线电设备接口。因此,它将I2C设备驱动程序作为子设备以向实际设备发送用户命令。基本上,它是I2C设备驱动程序的包装器。
应用程序可以使用v4l2无线电API指定操作频率、静音状态等。但是大多数属性将存在于扩展控件中。
当v4l2静音属性设置为1(true)时,驱动程序将关闭芯片。
Properties description
可以使用v4l2扩展控件访问属性。这里是来自v4l2-ctl实用程序的输出:
这是它们的摘要:
• Pilot是设备发送的可听到的音调。
• pilot_frequency - 配置立体声导频音调的频率。
• pilot_deviation - 配置导频音调频率偏差水平。
• pilot_enabled - 启用或禁用导频音调特性。
• si4713设备能够对传输信号应用音频压缩。
• acomp_enabled - 启用或禁用音频动态范围控制特性。
• acomp_gain - 为音频动态范围控制设置增益。
• acomp_threshold - 设置音频动态范围控制的阈值级别。
• acomp_attack_time - 设置音频动态范围控制的攻击时间。
• acomp_release_time - 设置音频动态范围控制的释放时间。
• Limiter设置了音频偏差限制器功能。一旦发生超过偏差,就可以调整音频输入的前端增益,并始终防止超过偏差。
• limiter_enabled - 启用或禁用限制器特性。
• limiter_deviation - 配置音频频率偏差水平。
• limiter_release_time - 设置限制器释放时间。
• 调谐功率
• power_level - 设置信号传输的输出功率水平。
• antenna_capacitor - 手动选择天线调谐电容器的值,如果设置为零,则自动选择。
• 与RDS有关
• rds_ps_name - 为传输设置RDS ps name字段。
• rds_radio_text - 为传输设置RDS电台文本。
• rds_pi - 为传输设置RDS PI字段。
• rds_pty - 为传输设置RDS PTY字段。
• 与地区有关
• preemphasis - 设置要应用于传输的预加重。
RNL
这个设备还有一个接口用于测量接收到的噪声级别。要做到这一点,您应该使用ioctl设备节点。下面是一个示例代码:
struct si4713_rnl和SI4713_IOC_MEASURE_RNL在include/linux/platform_data/media/si4713.h中定义。
Stereo/Mono and RDS subchannels
该设备还可以使用可用的子通道进行配置以进行传输。为此,请使用S / G_MODULATOR ioctl并正确配置txsubchans。有关此ioctl的正确使用,请参阅V4L2 API规范。
Testing
通常使用v4l2-ctl工具来测试管理FM调谐器卡。该工具可以在v4l-dvb存储库的v4l2-apps/util目录下找到。
设置RDS ps名称的示例:
v4l2-ctl -d /dev/radio0 -c rds_ps_name="YOUR RDS PS NAME"
这将通过设备节点/dev/radio0配置RDS PS名称为"YOUR RDS PS NAME"。
1.1.6.22 The Virtual Media Controller Driver (vimc)
vimc驱动程序使用V4L2 API和Media API模拟复杂的视频硬件。它具有一个捕获设备和三个子设备:传感器、去贝尔滤波器和缩放器。
Topology
拓扑结构是硬编码的,尽管您可以在vimc-core中修改它并重新编译驱动程序以实现自己的拓扑结构。这是默认的拓扑结构:
Fig. 7: Media pipeline graph on vimc
Configuring the topology
每个子设备都会附带其默认配置(像素格式、高度、宽度等)。需要配置拓扑结构,以便将每个链接的子设备的配置与通过管道流传输帧的配置匹配。如果配置不匹配,则流将失败。 v4l-utils软件包是一组用户空间应用程序,带有可用于配置vimc配置的media-ctl和v4l2-ctl。此命令序列适用于默认拓扑结构:
media-ctl -d platform:vimc -V '"Sensor A":0[fmt:SBGGR8_1X8/640x480]'
media-ctl -d platform:vimc -V '"Debayer A":0[fmt:SBGGR8_1X8/640x480]'
media-ctl -d platform:vimc -V '"Sensor B":0[fmt:SBGGR8_1X8/640x480]'
media-ctl -d platform:vimc -V '"Debayer B":0[fmt:SBGGR8_1X8/640x480]'
v4l2-ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440
v4l2-ctl -z platform:vimc -d "Raw Capture 0" -v pixelformat=BA81
v4l2-ctl -z platform:vimc -d "Raw Capture 1" -v pixelformat=BA81
Subdevices
子设备定义了拓扑结构中实体的行为。根据子设备的不同,实体可以具有多个类型为源或接收器的接口。
vimc-sensor:使用视频测试模式生成多种格式的图像。公开:
• 1个源接口
vimc-debayer:将bayer格式的图像转换为非bayer格式。公开: //raw转yuv???
• 1个接收器接口
• 1个源接口
vimc-scaler:将图像调整大小以满足源接口的分辨率。例如:如果sync接口配置为360x480,并且源接口为1280x720,则会拉伸图像以适应源分辨率。适用于vimc限制内(甚至在必要时缩小图像)。公开:
• 1个接收器接口
• 1个源接口
vimc-capture:公开节点/dev/videoX以允许用户空间捕获流。公开:
• 1个接收器接口
• 1个源接口
1.1.6.23 The Virtual Video Test Driver (vivid)
该驱动程序模拟各种类型的vedio4linux硬件:视频捕获、视频输出、vbi捕获和输出、元数据捕获和输出、无线电接收器和发射器、触摸捕获和软件定义的无线电接收器。此外,还提供了一个简单的帧缓冲设备,用于测试捕获和输出叠加层。
最多可以创建64个vivid实例,每个实例最多具有16个输入和16个输出。每个输入可以是网络摄像头、电视捕获设备、S-Video捕获设备或HDMI捕获设备。每个输出可以是S-Video输出设备或HDMI输出设备。
这些输入和输出的行为与真实硬件设备完全一样。这使您可以将此驱动程序用作应用程序开发的测试输入,因为您可以测试各种功能,而无需特殊硬件。
该文档描述了此驱动程序实现的功能:
• 支持read()/ write()、MMAP、USERPTR和DMABUF流式输入/输出。
• 大量的测试模式及其变化列表
• 工作亮度、对比度、饱和度和色调控件
• 支持alpha颜色分量
• 完整的颜色空间支持,包括有限/完整的RGB范围
• 所有可能的控制类型都存在
• 支持各种像素长宽比和视频长宽比
• 错误注入以测试出现错误时会发生什么
• 可以在任何组合中支持输入和输出的裁剪/合成/缩放
• 可以模拟高达4K的分辨率
• 支持所有标准的YUV和RGB格式,包括两种多平面YUV格式
• 原始和切片的VBI捕获和输出支持
• 电台接收器和发射器支持,包括RDS支持
• 软件定义无线电(SDR)支持
• 捕获和输出叠加支持
• 元数据捕获和输出支持
• 触摸捕获支持
下面将更详细地描述这些功能。
Configuring the driver
该驱动程序默认创建一个实例,具有视频捕获设备(包括网络摄像头、电视、S-Video和HDMI输入)、视频输出设备(包括S-Video和HDMI输出)、一个vbi捕获设备、一个vbi输出设备、一个无线电接收器设备、一个无线电发射器设备和一个SDR设备。
可以使用以下模块选项配置实例数、设备数、视频输入和输出及其类型:
• n_devs:要创建的驱动程序实例数。默认设置为1。最多可以创建64个实例。
• node_types:每个驱动程序实例应创建哪些设备。一个十六进制值数组,每个实例对应一个值。默认值为0x1d3d。每个值都是一个位掩码,含义如下:
- 第0位:视频捕获节点
- 第2-3位:VBI捕获节点:0 = 无,1 = 原始VBI,2 = 切片VBI,3 = 两者
- 第4位:无线电接收器节点
- 第5位:软件定义无线电接收器节点
- 第8位:视频输出节点
- 第10-11位:VBI输出节点:0 = 无,1 = 原始VBI,2 = 切片VBI,3 = 两者
- 第12位:无线电发射器节点
- 第16位:用于测试叠加的帧缓冲区
- 第17位:元数据捕获节点
- 第18位:元数据输出节点
- 第19位:触摸捕获节点
因此,要创建四个实例,前两个实例仅具有一个视频捕获设备,后两个实例仅具有一个视频输出设备,您需要将这些模块选项传递给vivid:
n_devs=4 node_types=0x1,0x1,0x100,0x100
• num_inputs:每个实例的输入数量。默认情况下,为每个视频捕获设备创建4个输入。最多可以创建16个输入,必须至少有一个。
• input_types:每个实例的输入类型,默认值为0xe4。这定义了在为每个驱动程序实例创建输入时的每个输入类型。这是一个十六进制值,具有多达16对位,每对位将类型映射到输入0(位0-1),将类型映射到输入1(位2-3) ,30-31将类型映射到输入15。每对位的含义如下:
- 00:这是一个网络摄像头输入
- 01:这是一个电视调谐器输入
- 10:这是一个S-Video输入
- 11:这是一个HDMI输入
因此,要创建具有8个输入的视频捕获设备,其中输入0是电视调谐器,输入1-3是S-Video输入,而输入4-7是HDMI输入,您需要使用以下模块选项:
num_inputs=8 input_types=0xffa9
• num_outputs:每个实例的输出数量。默认情况下,为每个视频输出设备创建2个输出。最多可创建16个输出,必须至少有一个。
• output_types:每个实例的输出类型,默认值为0x02。这定义了在为每个驱动程序实例创建输出时的每个输出类型。这是一个十六进制值,具有多达16位,每个位将类型映射到输出0(位0),位1将类型映射到输出1,位15将类型映射到输出15。每个位的含义如下:
- 0:这是一个S-Video输出
- 1:这是一个HDMI输出
因此,要创建具有8个输出的视频输出设备,其中输出0-3是S-Video输出,而输出4-7是HDMI输出,您需要使用以下模块选项:
num_outputs=8 output_types=0xf0
• vid_cap_nr:指定每个视频捕获设备所需的videoX起始编号。默认值为-1,这将仅使用第一个可用编号。这允许您将捕获视频节点映射到特定的videoX设备节点。例如:
n_devs=4 vid_cap_nr=2,4,6,8
这将尝试为第一个vivid实例的视频捕获设备分配/dev/video2,为接下来的实例分配video4,直到为最后一个实例分配video8。如果无法成功,则它将使用下一个可用编号。
• vid_out_nr:为每个视频输出设备提供所需的videoX起始编号。默认值为-1,这将仅使用第一个可用编号。
• vbi_cap_nr:为每个vbi捕获设备提供所需的vbiX起始编号。默认值为-1,这将仅使用第一个可用编号。
• vbi_out_nr:为每个vbi输出设备提供所需的vbiX起始编号。默认值为-1,这将仅使用第一个可用编号。
• radio_rx_nr:为每个无线电接收器设备提供所需的radioX起始编号。默认值为-1,这将仅使用第一个可用编号。
• radio_tx_nr:为每个无线电发射器设备提供所需的radioX起始编号。默认值为-1,这将仅使用第一个可用编号。
• sdr_cap_nr:为每个SDR捕获设备提供所需的swradioX起始编号。默认值为-1,这将仅使用第一个可用编号。
• meta_cap_nr:为每个元数据捕获设备提供所需的videoX起始编号。默认值为-1,这将仅使用第一个可用编号。
• meta_out_nr:为每个元数据输出设备提供所需的videoX起始编号。默认值为-1,这将仅使用第一个可用编号。
• touch_cap_nr:为每个触摸捕获设备提供所需的v4l-touchX起始编号。默认值为-1,这将仅使用第一个可用编号。
• ccs_cap_mode:指定每个驱动程序实例允许的视频捕获裁剪/合成/缩放组合。视频捕获设备可以具有任何组合的裁剪、合成和缩放功能,这将告诉vivid驱动程序应该模拟哪些功能。默认情况下,用户可以通过控件选择此选项。该值为-1(由用户控制)或一组三位,每个位启用(1)或禁用(0)其中一个功能:
- 位0:启用裁剪支持。裁剪将只采用传入图像的一部分。
- 位1:启用组合支持。合成将把传入的图像复制到一个更大的缓冲区中。
- 位2:启用缩放支持。缩放可以缩放传入的图像。vivid驱动程序的缩放器可以将大小扩大或缩小到原始大小的四倍。缩放器非常简单且质量低劣。简单性和速度是关键,而不是质量。
请注意,此值会被网络摄像头输入忽略:它们枚举离散的帧大小,这与裁剪、合成或缩放不兼容。
• ccs_out_mode:指定每个驱动程序实例允许的视频输出裁剪/合成/缩放组合。视频输出设备可以具有任何组合的裁剪、合成和缩放功能,这将告诉vivid驱动程序应该模拟哪些功能。默认情况下,用户可以通过控件选择此选项。该值为-1(由用户控制)或一组三位,每个位启用(1)或禁用(0)其中一个功能:
- 位0:启用裁剪支持。裁剪将只采用传出缓冲区的一部分。
- 位1:启用组合支持。合成将把传入的缓冲区复制到一个更大的图像帧中。
- 位2:启用缩放支持。缩放可以缩放传入的缓冲区。vivid驱动程序的缩放器可以将大小扩大或缩小到原始大小的四倍。缩放器非常简单且质量低劣。简单性和速度是关键,而不是质量。
• multiplanar:选择每个设备实例是否支持多平面格式,因此支持V4L2多平面API。默认情况下,设备实例是单平面的。该模块选项可以覆盖每个实例的默认设置。其值为:
* 1:这是单平面实例。
* 2:这是多平面实例。
• vivid_debug:启用驱动程序调试信息。
• no_error_inj:如果设置,则禁用错误注入控件。需要使用此选项才能运行v4l2-compliance等工具。此类工具会测试所有控件,包括“断开连接”等控件,以模拟USB断开连接,从而使设备无法访问,因此之后所有v4l2-compliance测试都将失败。在其他一些情况下,您可能也希望禁用vivid的错误注入支持。如果设置了此选项,那么选择裁剪、合成和缩放行为的控件也会被删除。除非使用ccs_cap_mode和/或ccs_out_mode进行了覆盖,否则默认情况下将启用裁剪、合成和缩放。
• allocators:内存分配器选择,默认为0。它指定将如何分配缓冲区。
- 0:vmalloc
- 1:dma-contig
• cache_hints:指定设备是否应设置队列的用户空间缓存和内存一致性提示功能(V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS)。这些提示仅在使用MMAP流式传输I/O时有效。默认为0。
- 0:禁止提示
- 1:允许提示
综合考虑所有这些模块选项,您可以精确定制驱动程序行为并测试应用程序的各种排列组合。它还非常适合模拟尚未可用的硬件,例如在开发新设备的软件时。
Video Capture
这可能是最常使用的功能。可以使用模块选项num_inputs、input_types和ccs_cap_mode(有关详细信息,请参见第1节)配置视频捕获设备,但默认情况下配置了四个输入:网络摄像头、电视调谐器、SVideo和HDMI输入,每个输入类型一个输入通道。下面对它们进行了更详细的描述。
特别关注新帧变为可用的速率。抖动将在1个jiffie左右(这取决于内核的HZ配置,通常为1/100、1/250或1/1000秒),但长期行为完全遵循帧率。因此,59.94 Hz的帧率与60 Hz的帧率确实是不同的。如果帧率超过内核的HZ值,则会出现丢帧,但帧序列计数将跟踪到这一点,因此在丢帧时序列计数将跳过。
Webcam Input
网络摄像头输入支持三种帧大小:320x180、640x360和1280x720。它支持每秒10、15、25、30、50和60帧的设置。可用的帧速率取决于所选的帧大小:帧大小越大,最大帧速率就越低。切换到网络摄像头输入时,最初选择的色彩空间将是sRGB。
TV and S-Video Inputs
电视输入与S-Video输入之间唯一的区别是电视输入具有调谐器。否则,它们的行为相同。
这些输入还支持音频输入:一个电视和一个Line-In。它们都支持所有电视标准。如果查询标准,则Vivid控件“标准信号模式”和“标准”将确定结果。
这些输入支持所有场设置的组合。特别注意,要忠实地重现不同电视标准下处理场的方式。当生成水平移动图像时,使用隔行格式的时间效应变得清晰可见。对于50 Hz标准,顶部场是最旧的,底部场是最新的。对于60 Hz标准,情况相反:底部场是最旧的,顶部场是最新的。
当您在V4L2_FIELD_ALTERNATE模式下开始捕获时,第一个缓冲区将包含50 Hz标准的顶部场,以及60 Hz标准的底部场。这也是捕获硬件所做的。
最后,对于PAL / SECAM标准,顶行的前半部分包含噪声。这模拟了常放置在其上的广角信号。
切换到电视或S-Video输入时,最初选择的色彩空间将是SMPTE-170M。
像素宽高比取决于电视标准。视频宽高比可以通过“标准纵横比”Vivid控件进行选择。选择是“4x3”,“16x9”,这将提供带有黑边的宽屏视频,以及“16x9 Anamorphic”,这将提供全屏的挤压型宽屏视频,需要相应地调整大小。
电视“调谐器”支持44-958 MHz的频率范围。从49.25 MHz开始,每6MHz提供一个频道。对于每个频道,生成的图像在其周围+/- 0.25 MHz的区间内为彩色,而在+/- 1 MHz的范围内为灰度。超出范围后,只剩下噪声。
VIDIOC_G_TUNER ioctl将返回+/- 0.25 MHz的100%信号强度,+/- 1 MHz的50%信号强度。它还将返回正确的AFC值,以显示频率是否过低或过高。
返回的音频子通道是围绕有效频道频率的+/- 1 MHz范围内的MONO。当频率在距离频道+/- 0.25 MHz处时,它将返回MONO、STEREO、MONO | SAP(用于NTSC)或LANG1 | LANG2(用于其他语言),或STEREO | SAP。
返回哪一个取决于所选择的频道,每个下一个有效频道将循环遍历可能的音频子通道组合。这使您可以通过切换频道来测试各种组合。
最后,对于这些输入,v4l2_timecode结构在出列的v4l2_buffer结构中被填充。
HDMI Input
HDMI输入支持25至600 MHz的像素时钟频率下的所有CEA-861和DMT定时,包括逐行和交替扫描格式。交错格式的场模式始终为V4L2_FIELD_ALTERNATE。对于HDMI输入,场顺序始终是首先顶场,当您开始捕获交错格式时,将首先接收到顶场。
切换到HDMI输入或选择HDMI定时时,最初选择的色彩空间基于格式分辨率:对于分辨率小于或等于720x576的情况,色彩空间设置为SMPTE-170M,对于其他情况,则设置为REC-709(CEA-861定时)或sRGB(VESA DMT定时)。
像素宽高比取决于HDMI定时:对于720x480,它设置为NTSC电视标准,对于720x576,它设置为PAL电视标准,对于其他所有值,返回1:1像素宽高比。
视频宽高比可以通过“DV Timings Aspect Ratio”Vivid控件进行选择。选择是“Source Width x Height”(使用与所选格式相同的比例)、“4x3”或“16x9”,其中任何一种都可能导致有柱形条纹或边框的视频。
对于HDMI输入,可以设置EDID。默认情况下提供简单的EDID。只能为HDMI输入设置EDID。但是,在内部,EDID在所有HDMI输入之间共享。
除物理地址外,EDID数据不进行任何解释。有关更多详细信息,请参见CEC部分。
最多可以有15个HDMI输入(如果有更多,则将减少到15个),因为这是EDID物理地址的限制。
Video Output
视频输出设备可以通过使用模块选项num_outputs、output_types和ccs_out_mode进行配置(有关更详细的信息,请参见第1节),但默认情况下配置了两个输出:一个S-Video和一个HDMI输入,每种输出类型一个。下面将更详细地描述它们。
与视频捕获一样,长期帧速率也是精确的。
S-Video Output
该输出还支持音频输出:“Line-Out 1”和“Line-Out 2”。S-Video输出支持所有电视标准。
此输出支持字段设置的所有组合。
当切换到电视或S-Video输入时,最初选择的色彩空间将为SMPTE-170M。
HDMI Output
HDMI输出支持25至600 MHz像素时钟频率下的所有CEA-861和DMT定时,包括逐行和交替扫描格式。交错格式的场模式始终为V4L2_FIELD_ALTERNATE。
当切换到HDMI输出或选择HDMI定时时,最初选择的色彩空间基于格式分辨率:对于分辨率小于或等于720x576的情况,色彩空间设置为SMPTE-170M,对于其他情况,则设置为REC-709(CEA-861定时)或sRGB(VESA DMT定时)。
像素宽高比取决于HDMI定时:对于720x480,它设置为NTSC电视标准,对于720x576,它设置为PAL电视标准,对于其他所有值,返回1:1像素宽高比。
HDMI输出具有有效的EDID,可以通过VIDIOC_G_EDID获取。最多可以有15个HDMI输出(如果有更多,则将减少到15个),因为这是EDID物理地址的限制。有关更多详细信息,请参见CEC部分。
VBI Capture
VBI捕获设备有三种类型:仅支持原始(未解码)VBI、仅支持切片(已解码)VBI和同时支持两种格式。这是由node_types模块选项确定的。在所有情况下,驱动程序将生成有效的VBI数据:对于60 Hz标准,它将生成Closed Caption和XDS数据。封闭字幕流将每秒钟在“Hello world!”和“Closed captions test”之间交替。XDS流将在每分钟提供当前时间。对于50 Hz标准,它将生成基于实际视频宽高比控制设置和电视信号页码100-159(每帧一个页面)的宽屏信号。
VBI设备仅适用于S-Video和TV输入,如果当前输入为网络摄像头或HDMI,则会返回错误。
VBI Output
VBI输出设备有三种类型:仅支持原始(未解码)VBI、仅支持切片(已解码)VBI和同时支持两种格式。这是由node_types模块选项确定的。
切片VBI输出支持50 Hz标准的宽屏信号和电视信号,支持60 Hz标准的Closed Captioning和XDS。
VBI设备仅适用于S-Video输出,如果当前输出为HDMI,则会返回错误。
Radio Receiver
该无线电接收器模拟了FM / AM / SW接收器。 FM波段还支持RDS。频率范围为:
• FM:64 MHz-108 MHz
• AM:520 kHz-1710 kHz
• SW:2300 kHz-26.1 MHz
对于FM,每1 MHz模拟一个有效频道,对于AM和SW,每100 kHz模拟一个有效频道。信号强度随着频率距离有效频率越远而逐渐降低,直到在+/- 50 kHz(FM)或+/- 5 kHz(AM / SW)的理想频率处变为0%。加载驱动程序时的初始频率设置为95 MHz。
FM接收器也支持RDS,可使用“Block I/O”和“Controls”模式。在“Controls”模式下,RDS信息存储在只读控件中。每次更改频率或请求调谐器状态时,这些控件都会更新。块I / O方法使用read()接口将RDS块传递给应用程序进行解码。RDS信号在频道频率的+/- 12.5 kHz范围内“检测”,距离有效频率越远,随机引入块I / O流中的RDS错误就越多,如果您距离频道频率为+/- 12.5 kHz,则高达50%的所有块都可能发生四种错误中的任意一种:标记为“CORRECTED”的块,标记为“ERROR”的块,标记为“INVALID”的块以及被丢弃的块。
生成的RDS流包含所有包含在0B分组中的标准字段,还包括广播文本和当前时间。
接收器支持硬件频率搜索,可在有界模式,循环模式或两者中进行配置,可通过“广播HW搜索模式”控件进行配置。
Radio Transmitter
该无线电发射器模拟了FM / AM / SW发射器。 FM波段还支持RDS。频率范围为:
• FM:64 MHz-108 MHz
• AM:520 kHz-1710 kHz
• SW:2300 kHz-26.1 MHz
加载驱动程序时的初始频率为95.5 MHz。
FM发射机也支持RDS,可以使用“Block I/O”和“Controls”模式。在“Controls”模式下,传输的RDS信息使用控件进行配置,在“Block I/O”模式下,使用write()将块传递给驱动程序。
Software Defifined Radio Receiver
SDR接收器具有ADC调谐器的三个频带:
• 300 kHz
• 900 kHz-2800 kHz
• 3200 kHz
RF调谐器支持50 MHz-2000 MHz。
生成的数据包含振幅为sqrt(2)的1 kHz音调的同相和正交分量。
Metadata Capture
元数据捕获生成UVC格式的元数据。 PTS和SCR基于vivid控件中设置的值进行传输。
元数据设备仅适用于网络摄像头输入,对于所有其他输入,它将返回一个错误。
Metadata Output
元数据输出可用于设置亮度、对比度、饱和度和色调。
元数据设备仅适用于网络摄像头输出,对于所有其他输出,它将返回一个错误。
Touch Capture
Touch捕获生成模拟单击、双击、三连击、从左到右移动、缩小、放大、手掌按压(模拟在触摸板上按下大面积)以及模拟16个同时触摸点的触摸模式。
Controls
不同设备支持不同的控件。下面的部分将描述每个控件以及支持它们的设备。
User Controls - Test Controls
按钮、布尔、32位整数、64位整数、菜单、字符串、位掩码和整数菜单是表示所有可能的控件类型的控件。 菜单控件和整数菜单控件在其菜单列表中都有“空洞”,这意味着调用VIDIOC_QUERYMENU时,一个或多个菜单项将返回EIN-VAL。 两个菜单控件还具有非零的最小控件值。 这些功能允许您检查您的应用程序是否可以正确处理此类事情。 这些控件适用于每种设备类型。
User Controls - Video Capture
以下控件专用于视频捕获。
亮度、对比度、饱和度和色调控件实际上可以工作并且是标准的。 亮度控件有一个特殊功能:每个视频输入都有自己的亮度值,因此更改输入将恢复该输入的亮度。 此外,每个视频输入使用不同的亮度范围(最小和最大控件值)。 切换输入将导致发送带有V4L2_EVENT_CTRL_CH_RANGE标志设置的控制事件。 这使您可以测试可以更改其范围的控件。
‘增益,自动’和‘增益’控件可用于测试易失性控件:如果设置‘增益,自动’,则增益控件是易失性的,会不断变化。 如果清除‘增益,自动’,则增益控件是正常的控件。
‘水平翻转’和‘垂直翻转’控件可用于翻转图像。 这些控件与‘传感器水平/垂直翻转’ Vivid控件结合使用。
‘Alpha Component’控件可用于为包含alpha通道的格式设置alpha组件。
User Controls - Audio
以下控件专用于视频捕获和输出以及收音机接收器和发射器。
‘音量’和‘静音’音频控件是控制音量和静音音频的此类设备的典型控件。 它们实际上在vivid驱动程序中不起作用。
Vivid Controls
这些vivid自定义控件控制图像生成,错误注入等。
Test Pattern Controls
测试图案控件都是针对视频捕获的。
• 测试图案:选择要使用的测试图案。使用CSC彩条进行颜色空间转换测试:该测试图案中使用的颜色映射到所有颜色空间中的有效颜色。其他测试图案禁用颜色空间转换。
• OSD文本模式:选择是否显示叠加在测试图案上的文本,以及如果显示,则只显示计数器还是完整文本。
• 水平移动:选择测试图案是否应向左或向右移动以及移动速度。
• 垂直移动:同理,为垂直方向选择移动。
• 显示边框:在实际图像的边缘(不包括字母或黑块边距)显示两个像素宽的边框。
• 显示正方形:在图像中央显示一个正方形。如果使用正确的像素和图像纵横比校正显示图像,则监视器上的正方形的宽度和高度应该相同。
• 在图像中插入SAV代码:向图像添加SAV(活动视频开始)代码。这可以用于检查是否意外解释了图像中的这些代码而不是被忽略。
• 在图像中插入EAV代码:对EAV(活动视频结束)代码执行相同的操作。
Capture Feature Selection Controls
这些控件都是针对视频捕获的。
• 摄像头水平翻转:图像水平翻转,设置V4L2_IN_ST_HFLIP输入状态标志。这模拟了传感器倒置的情况。
• 摄像头垂直翻转:图像垂直翻转,设置V4L2_IN_ST_VFLIP输入状态标志。这模拟了传感器倒置的情况。
• 标准纵横比:选择TV或S-Video输入所使用的图像纵横比是否应为4x3、16x9或锥形宽屏。这可能会引入黑边。
• DV计时纵横比:选择HDMI输入所使用的图像纵横比是否应与源宽高比相同,或者是4x3或16x9。这可能会引入黑边或柱形边距。
• 时间戳源:选择何时获取每个缓冲区的时间戳。
• 颜色空间:选择生成图像时应使用哪种颜色空间。仅当选择了CSC彩条测试图案时才适用,否则测试图案将不会转换。这也是您想要的行为,因为75%的彩条实际上应该具有75%的信号强度,并且不应受颜色空间转换的影响。
更改颜色空间将导致发送V4L2_EVENT_SOURCE_CHANGE,因为它模拟检测到颜色空间变化。
• 传输功能:选择生成图像时应使用哪种颜色空间传输功能。仅当选择了CSC彩条测试图案时才适用,否则测试图案将不会转换。这也是您想要的行为,因为75%的彩条实际上应该具有75%的信号强度,并且不应受颜色空间转换的影响。
更改传输函数将导致V4L2_EVENT_SOURCE_CHANGE发送,因为它模拟检测到颜色空间变化。
• Y’CbCr编码:选择生成Y’CbCr图像时应使用哪种Y’CbCr编码。这仅适用于格式设置为Y’CbCr格式而不是RGB格式的情况。
更改Y’CbCr编码将导致发送V4L2_EVENT_SOURCE_CHANGE,因为它模拟检测到颜色空间变化。
• 量化:选择在生成测试图案时应使用哪种RGB或Y’CbCr编码的量化。
更改量化将导致V4L2_EVENT_SOURCE_CHANGE发送,因为它模拟检测到颜色空间变化。
• 有限RGB范围(16-235):选择HDMI源的RGB范围是否应该是有限或全范围。这与数字视频‘Rx RGB量化范围’控制结合使用,可以用于测试如果源提供了错误的量化范围信息会发生什么。有关更多详细信息,请参见该控件的描述。
• 仅对红色应用Alpha通道:将由“Alpha Component”用户控件设置的Alpha通道仅应用于测试图案的红色。
• 启用截取捕获:启用裁剪支持。仅当ccs_cap_mode模块选项设置为默认值-1且no_error_inj模块选项设置为0(默认值)时,此控件才存在。
• 启用合成捕获:启用合成支持。仅当ccs_cap_mode模块选项设置为默认值-1且no_error_inj模块选项设置为0(默认值)时,此控件才存在。
• 启用缩放器捕获:启用缩放器支持(最大4倍缩放和放大)。仅当ccs_cap_mode模块选项设置为默认值-1,而no_error_inj模块选项设置为0(默认值)时,此控件才存在。
• 最大EDID块数:确定驱动程序支持多少个EDID块。请注意,vivid驱动程序实际上不会解释新的EDID数据,它只是存储它。它支持最多256个EDID块,这是标准支持的最大值。
• 帧填充百分比:可用于仅绘制图像的顶部X%。由于每个帧都必须由驱动程序绘制,这会对CPU产生很大的需求。对于大分辨率,这变得棘手。通过仅绘制部分图像,可以减轻此CPU负载。
Output Feature Selection Controls
这些控件都是特定于视频输出的。
• 启用输出裁剪:启用裁剪支持。仅当ccs_out_mode模块选项设置为默认值-1且no_error_inj模块选项设置为0(默认值)时,此控件才存在。
• 启用输出合成:启用合成支持。仅当ccs_out_mode模块选项设置为默认值-1且no_error_inj模块选项设置为0(默认值)时,此控件才存在。
• 启用输出缩放器:启用缩放器支持(最大4倍缩放和放大)。仅当ccs_out_mode模块选项设置为默认值-1,而no_error_inj模块选项设置为0(默认值)时,此控件才存在。
Error Injection Controls
以下两个控件仅适用于视频和VBI捕获。
• 标准信号模式:选择VIDIOC_QUERYSTD的行为:应该返回什么?
更改此控件将导致发送V4L2_EVENT_SOURCE_CHANGE,因为它模拟了改变的输入条件(例如,插入或插出电缆)。
• 标准:选择VIDIOC_QUERYSTD应返回的标准(如果上一个控件设置为“选定标准”)。
更改此控件将导致发送V4L2_EVENT_SOURCE_CHANGE,因为它模拟了改变的输入标准。
以下两个控件仅适用于视频捕获。
• DV定时信号模式:选择VIDIOC_QUERY_DV_TIMINGS的行为:应该返回什么?
更改此控件将导致发送V4L2_EVENT_SOURCE_CHANGE,因为它模拟了改变的输入条件(例如,插入或拔出电缆)。
• DV定时:选择VIDIOC_QUERY_DV_TIMINGS应返回的定时模式(如果上一个控件设置为“选定DV定时”)。
更改此控件将导致发送V4L2_EVENT_SOURCE_CHANGE,因为它模拟了改变的输入定时。
以下控件仅在no_error_inj模块选项设置为0(默认值)时存在。这些控件适用于视频和VBI捕获和输出流以及SDR捕获设备,除了Disconnect控件,它适用于所有设备。
• 包裹序列号:测试在结构体v4l2_buffer中包裹序列号的结果。
• 包裹时间戳:测试在结构体v4l2_buffer中包裹时间戳的结果。
• 丢失缓冲区的百分比:设置从驱动程序中未返回的缓冲区的百分比(即,它们被丢弃)。
• 断开连接:模拟USB断开连接。设备将像已断开连接一样运行。只有在关闭设备节点的所有打开文件句柄后,设备才会再次“连接”。
• 注入V4L2_BUF_FLAG_ERROR:按下该按钮后,驱动程序返回的下一帧将设置错误标志(即,该帧被标记为损坏)。
• 注入VIDIOC_REQBUFS错误:按下该按钮后,下一个REQBUFS或CREATE_BUFS ioctl调用将失败并显示错误。准确地说:videobuf2 queue_setup()操作将返回-EINVAL。
• 注入VIDIOC_QBUF错误:按下该按钮后,下一个VIDIOC_QBUF或VIDIOC_PREPARE_BUFFER ioctl调用将失败并显示错误。准确地说:videobuf2 buf_prepare()操作将返回-EINVAL。
• 注入VIDIOC_STREAMON错误:按下该按钮后,下一个VIDIOC_STREAMON ioctl调用将失败并显示错误。准确地说:videobuf2 start_streaming()操作将返回-EINVAL。
• 注入致命流媒体错误:按下该按钮后,流媒体核心将被标记为遭受致命错误,唯一的恢复方式是停止流媒体。准确地说:调用videobuf2 vb2_queue_error()函数。
VBI Raw Capture Controls
• 隔行VBI格式:如果设置,则原始VBI数据将是交替的,而不是按场组合的形式提供。
Digital Video Controls
• 接收端RGB量化范围:设置HDMI输入的RGB量化检测。这与Vivid的“有限RGB范围(16-235)”控件相结合,可以用来测试如果源提供了错误的量化范围信息会发生什么。可以通过选择HDMI输入,将此控件设置为完整或有限范围,并在“有限RGB范围(16-235)”控件中选择相反的方式进行测试。如果选择“灰色坡道”测试图案,则效果很容易看到。
• 发送端RGB量化范围:设置HDMI输出的RGB量化检测。在Vivid中当前未用于任何功能,但大多数HDMI发射器通常都会具有此控件。
• 传输模式:将HDMI输出的传输模式设置为HDMI或DVI-D。这会影响报告的colorspace,因为DVI_D输出始终使用sRGB。
• 显示器存在:设置HDMI输出上的“显示器”存在。这会影响tx_edid_present、tx_hotplug和tx_rxsense控件。
FM Radio Receiver Controls
这是关于RDS接收器的一些控制选项:
• RDS接收:设置是否启用RDS接收器。
• RDS节目类型:
• RDS PS名称:
• RDS Radio Text:
• RDS交通广播:
• RDS交通节目:
• RDS音乐:这些都是只读控件。如果RDS Rx I/O模式设置为“块I/O”,那么它们也是无效的。如果RDS Rx I/O模式设置为“控件”,那么这些控件报告接收到的RDS数据。
注意:这个vivid实现相当基本:它们只在设置新频率或获取调谐器状态(VIDIOC_G_TUNER)时更新。
• Radio HW Seek Mode:可以是“Bounded”、“Wrap Around”或“Both”。这决定了VIDIOC_S_HW_FREQ_SEEK是否会被频率范围限定或环绕,或者是否可由用户选择。
• Radio Programmable HW Seek:如果设置,则用户可以提供HW Seek的下限和上限。否则将使用频率范围边界。
• 生成RBDS而不是RDS:如果设置,则生成RBDS(美国版RDS)数据而不是RDS(欧洲风格RDS)。这仅影响PICODE和PTY代码。
• RDS Rx I/O模式:这可以是“块I/O”,其中应用程序必须读取RDS块,或者是“控件”,其中RDS数据由上面提到的RDS控件提供。
FM Radio Modulator Controls
这些控制用于设置FM调制器发送的RDS数据:
• RDS节目ID:
• RDS节目类型:
• RDS PS名称:
• RDS Radio Text:
• RDS立体声:
• RDS人工头部:
• RDS压缩:
• RDS动态PTY:
• RDS交通广播:
• RDS交通节目:
• RDS音乐:这些都是控件,用于设置FM调制器发送的RDS数据。
• RDS Tx I/O模式:这可以是“块I/O”,其中应用程序必须使用write()向驱动程序传递RDS块,或者是“控件”,其中RDS数据由上述RDS控件提供。
Metadata Capture Controls
如果设置了以下选项,则生成的元数据流包含:
• 生成PTS:此选项设置时,生成的元数据流将包含演示时间戳。
• 生成SCR:当设置此选项时,生成的元数据流将包含源时钟信息。
Video, VBI and RDS Looping
vivid驱动程序支持将视频输出、VBI输出和RDS输出循环连接到视频输入、VBI输入和RDS输入。对于视频/VBI循环连接,它会模拟连接了一个电缆在输出和输入连接器之间。因此,视频和VBI循环连接只支持在S-Video和HDMI输入和输出之间进行。对于S-Video,VBI才有效,因为对于HDMI没有意义。
由于无线电是无线的,如果收音机接收器频率接近于发射机频率,则始终发生循环连接。在这种情况下,无线电发射机将“覆盖”模拟无线电电台。
目前,循环连接仅在由同一vivid驱动程序实例创建的设备之间受支持。
Video and Sliced VBI looping
目前启用视频/VBI循环连接的方法相当粗糙。在视频捕获和VBI捕获设备的“Vivid”控制类中提供了一个“循环视频”控件。“循环视频”勾选后,视频循环连接将被启用。启用后,任何S-Video或HDMI输入的视频都将显示静态测试图案,直到视频输出已开始。此时,视频输出将被循环连接到视频输入,前提是:
•输入类型与输出类型匹配。因此,HDMI输入无法从S-Video输出接收视频。
•视频输入的分辨率必须与视频输出的分辨率相匹配。因此,无法将50 Hz(720x576)的S-Video输出循环连接到60 Hz(720x480)的S-Video输入,也无法将720p60的HDMI输出循环连接到1080p30输入。
•两侧的像素格式必须相同。否则,驱动程序也必须进行像素格式转换,这太过麻烦。
•两侧的场设置必须相同。同上原因:要求驱动程序从一种场格式转换为另一种场格式会使问题变得更加复杂。这也禁止在输出视频设置为“场替换”时,使用“场顶部”或“场底部”捕获。虽然这种组合是合法的,但它变得过于复杂,无法支持。两侧都必须是“场交替”的才能正常工作。此外,请注意,对于这种特殊情况,在捕获方面的struct v4l2_buffer中的序列和场计数可能不是100%准确。
•不支持场设置V4L2_FIELD_SEQ_TB/BT。虽然可以实现这一点,但这意味着要花费大量的工作来做好这一点。由于这些场值很少使用,因此暂时决定不实现这一点。
•在输入端,应配置S-Video输入的“标准信号模式”或HDMI输入的“DV定时信号模式”,以便向视频输入传递有效信号。
帧速率不一定要匹配,尽管在将来可能会改变。
默认情况下,您将看到覆盖在循环视频上方的OSD文本。可以通过更改视频捕获设备的“OSD文本模式”控制来关闭它。
为了使VBI循环连接起作用,必须满足上述所有要求,并且VBI输出必须配置为切片VBI。VBI捕获端可以配置为原始或分割VBI。请注意,目前仅循环CC/XDS(60 Hz格式)和WSS(50 Hz格式) VBI数据。不会循环传送字幕的VBI数据。
Radio & RDS Looping
根据第6节的介绍,无线电接收机模拟电台在常规频率间隔上的工作。根据接收机的频率,计算出信号强度值(这是通过VIDIOC_G_TUNER返回的)。但它还会查看无线电发射机所设置的频率,如果该频率产生的信号强度高于发射机的设置,则将其用作有效站点。这也包括发射机“传输”的RDS数据(如果有)。这些数据被接收方忠实地接收。请注意,在加载驱动程序时,无线电接收机和发射机的频率不相同,因此最初不会进行循环。
Cropping, Composing, Scaling
。这个驱动程序支持任意组合的裁剪、合成和缩放功能。通常可以通过Vivid控件选择支持的功能,但在加载模块时也可以通过ccs_cap_mode和ccs_out_mode模块选项进行硬编码。有关这些模块选项的详细信息,请参见第1节。
这使您可以测试应用程序的所有这些变化。
请注意,网络摄像头输入永远不支持裁剪、合成或缩放。这仅适用于TV/S-Video/HDMI输入和输出。原因是网络摄像头,包括这个虚拟实现,通常使用VIDIOC_ENUM_FRAMESIZES列出一组离散的帧大小,它不会与裁剪、合成或缩放相结合。这主要是V4L2 API的限制,在这里进行了仔细的重现。
缩放器可以实现的最小分辨率和最大分辨率分别为16x16和(4096 * 4) x (2160 x 4),但它只能缩小或放大4倍或更少。因此,对于1280x720的源分辨率,缩放器可以做到的最小值是320x180,最大值是5120x2880。您可以使用qv4l2测试工具尝试这个,并查看这些依赖关系。
此驱动程序还支持更大的“bytesperline”设置,这是VIDIOC_S_FMT允许的,但很少有驱动程序实现。
缩放器是一个使用粗糙Bresenham算法的简单缩放器。它的设计速度和简单性,而不是质量。
如果裁剪、合成和缩放的组合允许,则可以在运行时更改裁剪和合成矩形。
Formats
这个驱动程序支持所有常规的打包和平面4:4:4、4:2:2和4:2:0 YUYV格式,8、16、24和32 RGB打包格式以及各种多平面格式。对于支持它的格式,可以通过“Alpha Component”用户控件设置alpha分量。如果设置了“仅将Alpha应用于红色”,那么alpha分量只用于颜色红色,否则设为0。
驱动程序必须配置支持多平面格式。默认情况下,驱动程序实例是单平面的。这可以通过设置multiplanar模块选项来更改,请参见第1节中有关该选项的更多详细信息。
如果驱动程序实例正在使用多平面格式/API,则第一个单平面格式(YUYV)和多平面NV16M和NV61M格式将具有一个具有128字节非零data_offset的平面。data_offset很少为非零,因此这是测试应用程序的一个有用特性。
视频输出还将遵守应用程序设置的任何data_offset。
Capture Overlay //捕获覆盖层
注意:捕获覆盖支持的主要目的是测试现有的V4L2捕获覆盖API。实际上,现代硬件更为强大,很少有任何GPU支持这样的覆盖,也基本上不再需要它们。通过在node_types模块选项中设置标志0x10000,vivid驱动程序将创建一个简单的帧缓冲设备,可用于测试此API。是否应该使用此API来编写新的驱动程序值得商榷。
该驱动程序支持具有位图剪切和列表剪切(最多16个矩形)能力的破坏性捕获覆盖。多平面格式不支持覆盖。它还遵守struct v4l2_window字段设置:如果它设置为FIELD_TOP或FIELD_BOTTOM并且捕获设置为FIELD_ALTERNATE,则只会将顶部或底部字段复制到覆盖中。
仅当您同时进行捕获时,覆盖层才能工作。这是vivid的限制,因为它从缓冲区复制到覆盖层,而不是直接填充覆盖层。如果您没有捕获,则没有缓冲区可用于填充。
另外,捕获格式和帧缓冲区的像素格式必须相同,以使覆盖层正常工作。否则,VIDIOC_OVERLAY将返回错误。
为了真正看到发生了什么,您需要创建两个vivid实例:第一个启用帧缓冲区。将第二个实例的捕获覆盖配置为使用第一个的帧缓冲区,然后开始在第二个实例中捕获。对于第一个实例,您设置视频输出的输出覆盖层,打开视频循环和捕获,以查看第二个实例正在写入的混合帧缓冲区覆盖层。此设置需要以下命令:
$ sudo modprobe vivid n_devs=2 node_types=0x10101,0x1
$ v4l2-ctl -d1 --find-fb
/dev/fb1 is the framebuffer associated with base address 0x12800000
$ sudo v4l2-ctl -d2 --set-fbuf fb=1
$ v4l2-ctl -d1 --set-fbuf fb=1
$ v4l2-ctl -d0 --set-fmt-video=pixelformat='AR15'
$ v4l2-ctl -d1 --set-fmt-video-out=pixelformat='AR15'
$ v4l2-ctl -d2 --set-fmt-video=pixelformat='AR15'
$ v4l2-ctl -d0 -i2
$ v4l2-ctl -d2 -i2
$ v4l2-ctl -d2 -c horizontal_movement=4
$ v4l2-ctl -d1 --overlay=1
$ v4l2-ctl -d1 -c loop_video=1
$ v4l2-ctl -d2 --stream-mmap --overlay=1
And from another console:
$ v4l2-ctl -d1 --stream-out-mmap
And yet another console:
$ qv4l2
开始出流。
qv4l2是什么:
qv4l2是一个Linux系统下的视频设备调试和测试工具,它基于Qt界面库开发。qv4l2可以查看摄像头和视频设备的属性,并提供简单的界面来控制和修改这些属性。此外,通过qv4l2还可以进行音视频流的播放和录制测试。
正如您所看到的,这不是那些心志不坚的人可以胜任的……
Output Overlay
注:输出覆盖层主要实现用于测试现有的V4L2输出覆盖API。是否应该为新驱动程序使用此API值得商榷。该驱动程序支持输出覆盖并具有以下功能:
• 位图剪切
• 列表剪辑(最多16个矩形)
• 色度键
• 源色度键
• 全局alpha
• 本地alpha
• 本地反向alpha
对于多平面格式,不支持输出覆盖。此外,捕获格式和帧缓冲区的像素格式必须相同,以使覆盖层正常工作。否则,VIDIOC_OVERLAY将返回错误。
仅当驱动程序被配置为通过在node_types模块选项中设置标志0x10000来创建帧缓冲区时,才支持输出覆盖层。所创建的帧缓冲区的大小为720x576,支持ARGB 1:5:5:5和RGB 5:6:5。
为了看到各种剪辑、色度键或alpha处理能力的效果,您需要打开视频循环,并在捕获端看到结果。剪辑、色度键或alpha处理能力的使用会显著降低视频循环速度,因为每个像素都需要进行大量的检查。
CEC (Consumer Electronics Control)
如果存在HDMI输入,则将创建具有相同数量的输入端口的CEC适配器。这相当于例如具有该数量输入的电视。每个HDMI输出也将创建一个CEC适配器,该适配器连接到相应的输入端口,或者(如果输出多于输入)根本没有连接。换句话说,这相当于将每个输出设备连接到电视的一个输入端口。任何剩余的输出设备都保持未连接状态。
每个输出读取的EDID报告了一个唯一的CEC物理地址,该地址基于输入的EDID的物理地址。因此,如果接收器的EDID具有物理地址A.B.0.0,则每个输出将看到包含物理地址A.B.C.0的EDID,其中C为1到输入数量。如果输出比输入多,则其余输出具有已禁用并报告无效物理地址的CEC适配器。
Some Future Improvements
提醒一下,没有特定的顺序:
• 添加虚拟alsa驱动程序以测试音频
• 添加虚拟子设备和媒体控制器支持
• 支持测试压缩视频
• 添加支持将原始VBI输出循环到原始VBI输入的功能
• 添加支持将电视滑动VBI输出循环到VBI输入的功能
• 循环交替场的视频时修复序列/场编号
• 为视频输出添加V4L2_CID_BG_COLOR支持
• 添加ARGB888叠加支持:更好地测试alpha通道
• 通过传递真正的v4l2_fract来改进tpg代码中的像素方面支持
• 使用每个队列锁和/或每个设备锁以提高吞吐量
• 添加支持从一个具体的输出到另一个具体的输入在vivid实例之间进行循环
• SDR电台应使用与普通无线电接收机相同的“频率”,如果频率与站点频率不匹配,则返回噪声。
• 为RDS生成创建线程,这将有助于特别是对于“控制”RDS Rx I/O模式,因为只读RDS控件可以实时更新。
• 改变EDID应导致热插拔检测仿真发生。
2 MEDIA SUBSYSTEM KERNEL INTERNAL API
本节包含有关多媒体子系统及其支持的驱动程序的使用信息。
请参阅:
Documentation/admin-guide/media/index.rst
• 了解有关多媒体子系统和支持的驱动程序的使用信息;
Documentation/userspace-api/media/index.rst
• 了解在多媒体设备上使用的用户空间API。
2.1 Media Subsystem Profile
2.1.1 Overview
多媒体子系统涵盖了对各种设备的支持:流捕获、模拟和数字电视流、摄像头、遥控器、HDMI CEC和媒体管道控制。
它主要涵盖以下目录的内容:
• drivers/media
• drivers/staging/media
• Documentation/admin-guide/media
• Documentation/driver-api/media
• Documentation/userspace-api/media
• Documentation/devicetree/bindings/media/1
• include/media
多媒体用户空间和内核API都有文档,并且文档必须与API更改保持同步。这意味着,所有添加新功能到子系统的补丁也必须对应地更改相应的API文件。
由于多媒体子系统的规模和广泛的范围,多媒体维护模型是拥有特定子系统方面广泛知识的子维护者。子维护者的任务是审核补丁,如果补丁遵循子系统规则并正确使用多媒体内核和用户空间API,则向用户提供反馈。
多媒体子系统的补丁必须作为纯文本邮件发送到linux-media@vger.kernel.org 所在的多媒体邮件列表。带有HTML的电子邮件将被邮件服务器自动拒绝。最好也将副维护者的副本复制进去。
多媒体的工作流程基于Patchwork,这意味着一旦提交补丁,邮件将首先被邮件列表服务器接受,然后在一段时间后应该出现在以下位置:
• https://patchwork.linuxtv.org/project/linux-media/list/
如果几分钟后未自动显示,请检查您的提交是否有误。请检查电子邮件是否为纯文本,并且您的电子邮件程序是否在投诉或重新提交之前操纵空格。
您可以通过查看以下内容来检查邮件列表服务器是否接受了您的补丁:
• https://lore.kernel.org/linux-media/
2.1.1.1 Media maintainers
在多媒体子系统中,我们有一组高级开发人员负责对驱动程序进行代码审查(也称为子维护者),另一个高级开发人员负责整个子系统。对于核心更改,尽可能多的多媒体维护者进行审核。
负责特定子系统区域的多媒体维护者有:
• 远程控制器(红外线):Sean Young <sean@mess.org>
• HDMI CEC:Hans Verkuil <hverkuil@xs4all.nl>
• 媒体控制器驱动程序:Laurent Pinchart <laurent.pinchart@ideasonboard.com>
• ISP、v4l2异步、v4l2-fwnode、v4l2-flash-led-class和传感器驱动程序:Sakari Ailus <sakari.ailus@linux.intel.com>
• V4L2驱动程序和核心V4L2框架:Hans Verkuil <hverkuil@xs4all.nl>
子系统维护者是:Mauro Carvalho Chehab <mchehab@kernel.org>
多媒体维护者可以根据需要将补丁委派给其他多媒体维护者。在这种情况下,checkpatch的委派字段指示当前负责审核补丁的人。
2.1.2 Submit Checklist Addendum //提交检查清单补遗
更改开放固件/设备树绑定的补丁必须由设备树维护者审核。因此,当这些补丁通过device-tree@vger.kernel.org邮件列表提交时,应将DT维护者添加为抄送人。
https://git.linuxtv.org/v4l-utils.git/上有一组符合性工具,应该用来检查驱动程序是否正确实现了媒体API。
正在开发其他符合性工具,以检查子系统的其他部分。这些测试需要在补丁上游之前通过。
此外,请注意,我们使用以下方式构建内核:
make CF=-D__CHECK_ENDIAN__ CONFIG_DEBUG_SECTION_MISMATCH=y C=1 W=1 CHECK=check_script
Where the check script is:
#!/bin/bash
/devel/smatch/smatch -p=kernel $@ >&2
/devel/sparse/sparse $@ >&2
请注意,不要没有非常好的理由就在您的补丁中引入新的警告。
2.1.2.1 Style Cleanup Patches //代码风格清理补丁
当代码风格更改会影响到其他更改的文件时,欢迎一起进行代码风格清理。
我们可以接受纯粹的独立代码风格清理,但是最好是将其作为整个子系统的一个补丁(如果清理量很小),或者至少按目录分组。例如,如果您正在对drivers/media下的驱动程序进行大规模的清理更改,则请发送一个单独的补丁用于drivers/media/pci下的所有驱动程序,另一个补丁用于drivers/media/usb等。
2.1.2.2 Coding Style Addendum
媒体开发使用checkpatch.pl在strict模式下验证代码风格,例如:
$ ./scripts/checkpatch.pl --strict --max-line-length=80
原则上,补丁应遵循编码样式规则,但如果有充分的理由,可以允许例外。在这种情况下,维护者和评审人员可能会对不解决checkpatch.pl问题的理由进行质疑。请注意,这里的目标是提高代码可读性。在一些情况下,checkpatch.pl可能实际上会指向更糟糕的地方。因此,您应该用好判断力。请注意,仅解决一个checkpatch.pl问题(无论何种类型)可能会导致每行超过80个字符。虽然这并不是严格禁止的,但应努力保持每行80个字符以内。这包括使用重构代码以减少缩进、更短的变量或函数名称,最后但并非最不重要的是简单地换行。特别地,在以下情况下,我们接受超过80列的行:
• 字符串,因为它们不应因长度限制而被打断;
• 当函数或变量名需要具有较大的标识符名称时,可以超过80列,因为这样更难遵守80列的限制;
• 在算术表达式中,当断行使它们更难阅读时;
• 当它们避免行以开放括号或开放方括号结束时。
2.1.3 关键周期日期
新提交可以随时发送,但是如果它们打算在下一个合并窗口中纳入,则应在-rc5之前发送,并在-linux-media分支中通过-rc6稳定。
2.1.4 审查节奏
只要您的补丁在https://patchwork.linuxtv.org上,它迟早会被处理,因此您不需要重新提交补丁。
除了错误修复外,在-rc6和下一个-rc1之间,我们通常不会向开发树中添加新的补丁。
请注意,媒体子系统是一个高流量的子系统,因此我们可能需要一些时间才能审查您的补丁。如果在几周内没有得到反馈,请随时提醒我们或向其他开发人员公开添加Reviewed-by和更重要的Tested-by:标记。
请注意,我们期望对Tested-by:进行详细说明,识别使用了哪些板子以及测试内容是什么。
2.2 Video4Linux devices
2.2.1 Introduction
V4L2驱动程序往往非常复杂,这是由于硬件的复杂性造成的:大多数设备都有多个IC,导出/dev下的多个设备节点,还创建了非V4L2设备,例如DVB、ALSA、FB、I2C和输入(IR)设备。
特别是V4L2驱动程序必须设置支持IC以进行音频/视频复用/编码/解码,这使得其比大多数驱动程序更复杂。通常,这些IC通过一个或多个I2C总线连接到主桥驱动程序,但也可以使用其他总线。这些设备被称为"子设备"。
很长一段时间里,该框架仅限于video_device结构用于创建V4L设备节点和处理视频缓冲区的视频_buf(请注意,本文档不讨论video_buf框架)。
这意味着所有驱动程序都必须自行设置设备实例并连接到子设备。其中一些工作做起来非常复杂,许多驱动程序从未正确执行。
由于缺少框架,也无法重构许多常见代码。因此,该框架设置了所有驱动程序需要的基本构建块,同时该框架应该将常见代码重构为所有驱动程序共享的实用函数变得更容易。
一个好的参考示例是位于samples/v4l/的v4l2-pci-skeleton.c源码。它是一个PCI捕获卡的骨架驱动程序,并演示了如何使用V4L2驱动程序框架。它可以用作实际PCI视频捕获驱动程序的模板。
介绍video_buf框架的文档有哪些:
以下是介绍Linux内核中的video_buf框架的一些文档:
1. video-buf.txt: 该文档提供了关于video_buf框架的一个简要概述,介绍了video_buf核心API和与v4l2中其他API的关系。
2. videobuf-core.txt: 该文档更详细地介绍了如何使用videobuf核心,包括video buffer的分配、映射和解除映射等操作,以及如何从DMA设备进行数据传输。
3. videobuf-dma-sg.txt: 该文档介绍了videobuf-dma-sg核心,它是一个围绕DMA和scatter-gather(SG)缓冲区机制的API。它适用于像视频捕获卡这样的设备,该设备可以通过SG DMA引擎将帧数据流直接存储到用户缓冲区中。
4. videobuf-vmalloc.txt: 该文档介绍了videobuf-vmalloc API,它提供了一个虚拟内存缓冲区池,该池用于存储帧数据,而无需访问DMA引擎。
这些文档都可以在Linux内核的Documentation/video4linux2目录下找到。
2.2.2 Structure of a V4L driver
所有驱动程序都具有以下结构:
1)针对每个设备实例的结构体,其中包含设备状态。
2)初始化和控制子设备(如果有)的方法。
3)创建V4L2设备节点(/dev/videoX、/dev/vbiX和/dev/radioX)并跟踪设备节点特定数据。
4)包含每个文件句柄数据的文件句柄特定结构体;
5)视频缓冲区处理。
这是一个大致的示意图,说明它们之间的关系。
2.2.3 Structure of the V4L2 framework
这个框架与驱动程序结构非常相似:它有一个v4l2_device结构体来表示设备实例数据,一个v4l2_subdev结构体来引用子设备实例,video_device结构体用于存储V4L2设备节点的数据,而v4l2_fh结构体用于跟踪文件句柄实例。V4L2框架还可以可选地与media框架集成。如果驱动程序设置了struct v4l2_device中的mdev字段,子设备和视频节点将自动出现在media框架中作为实体。
2.2.4 Video device‘s internal representation
在/dev目录中实际的设备节点是使用video_device结构体(v4l2-dev.h)创建的。这个结构体可以动态分配,也可以嵌入到较大的结构体中。如果要动态分配它,请使用video_device_alloc()函数:
struct video_device *vdev = video_device_alloc();
if (vdev == NULL)
return -ENOMEM;
vdev->release = video_device_release;
如果将它嵌入到较大的结构体中,那么您必须将release()回调设置为您自己的函数:
struct video_device *vdev = &my_vdev->vdev;
vdev->release = my_vdev_release;
release()回调函数必须被设置,并且在最后一个使用该视频设备的用户退出时会被调用。默认的video_device_release()回调函数目前只是调用了kfree来释放已分配的内存。还有一个video_device_release_empty()函数,它什么也不做(是空的),如果结构体被嵌入到其他结构体中并且释放时没有要做的事情,应该使用这个函数。
除此之外,还应该设置video_device的以下字段:
• `video_device->v4l2_dev`:必须设置为v4l2_device的父设备。
• `video_device->name`:设置为一个描述性的,唯一的名称。
• `video_device->vfl_dir`:对于捕获设备,将其设置为VFL_DIR_RX(VFL_DIR_RX的值为0,因此通常已经是默认值)。对于输出设备,将其设置为VFL_DIR_TX,对于mem2mem(编解码)设备,将其设置为VFL_DIR_M2M。
• `video_device->fops`:设置为v4l2_file_operations结构体。
• `video_device->ioctl_ops`:如果使用v4l2_ioctl_ops简化ioctl维护(建议使用,并且未来可能成为强制性要求!),则将其设置为v4l2_ioctl_ops结构体。video_device->vfl_type和video_device->vfl_dir字段用于禁用不匹配类型/ dir组合的操作。例如,非VBI节点将禁用VBI操作,并且将禁用捕获设备的输出操作。这样可以为vbi和video节点提供一个v4l2_ioctl_ops结构体。
• `video_device->lock`:如果想在驱动程序中进行所有锁定,则将其保留为NULL。否则,将其指向一个mutex_lock结构体的指针,并且在调用video_device->unlocked_ioctl文件操作之前,内核将获取此锁并在操作后释放。有关更多详细信息,请参见下一节。
• `video_device->queue`:指向与该设备节点相关联的struct vb2_queue结构体的指针。如果queue不为NULL,并且queue->lock不为NULL,则使用queue->lock对队列ioctl(VIDIOC_REQBUFS、CREATE_BUFS、QBUF、DQBUF、QUERYBUF、PREPARE_BUF、STREAMON和STREAMOFF)进行排队,而不是使用上面的锁。这样,vb2排队框架不必等待其他ioctl。该队列指针也由vb2辅助函数用于检查排队所有权(即调用它的文件句柄是否允许执行该操作)。
• `video_device->prio`:跟踪优先级。用于实现VIDIOC_G_PRIORITY和VIDIOC_S_PRIORITY。如果保留为NULL,则将使用v4l2_device中的struct v4l2_prio_state。如果要针对每个(组)设备节点使用单独的优先级状态,则可以将其指向自己的struct v4l2_prio_state。
• `video_device->dev_parent`:仅在v4l2_device以NULL作为父设备结构进行注册时设置此字段。这只发生在一个硬件设备具有多个PCI设备,所有PCI设备都共享相同的v4l2_device核心的情况下。cx88驱动程序是这种情况的一个示例:一个核心的v4l2_device结构体,但同时被一个原始视频PCI设备(cx8800)和一个MPEG PCI设备(cx8802)使用。由于v4l2_device无法同时关联两个PCI设备,因此它是没有父设备的。但是,在初始化struct video_device时,您确实知道要使用哪个父PCI设备,因此将dev_device设置为正确的PCI设备。
如果您使用v4l2_ioctl_ops,则应该在v4l2_file_operations结构中将video_device->unlocked_ioctl设置为video_ioctl2()。在某些情况下,您需要告诉核心,您在v4l2_ioctl_ops中指定的某个函数应该被忽略。您可以在调用video_register_device()之前调用此函数来标记这些ioctl:v4l2_disable_ioctl(vdev,cmd)。如果基于外部因素(例如正在使用的卡)需要关闭v4l2_ioctl_ops中的某些功能,而无需创建新的结构,则通常会需要这样做。v4l2_file_operations结构是file_operations的子集。主要区别是省略了inode参数,因为从未使用过。如果需要与媒体框架集成,则必须通过调用media_entity_pads_init()初始化video_device结构中嵌入的media_entity结构(entity字段)。
struct media_pad *pad = &my_vdev->pad;
int err;
err = media_entity_pads_init(&vdev->entity, 1, pad);
pads数组必须先前已初始化。无需手动设置struct media_entity类型和名称字段。在打开/关闭视频设备时,将自动获取/释放对实体的引用。
2.2.4.1 ioctls and locking
V4L核心提供可选的锁定服务。主要服务是video_device结构中的锁字段,它是指向mutex的指针。如果设置了此指针,则unlocked_ioctl将使用它来串行化所有ioctls。如果您使用videobuf2框架,则可以设置第二个锁:video_device->queue->lock。如果设置了该锁,则将使用该锁来代替video_device->lock以串行化所有排队ioctls(请参见上一节以获取这些ioctls的完整列表)。使用不同的锁来处理排队ioctls的优点是,对于某些驱动程序(特别是USB驱动程序),某些命令(例如设置控件)可能需要很长时间,因此您希望为缓冲区排队ioctls使用单独的锁。这样,您的VIDIOC_DQBUF不会停顿(即把buffer相关的操作和其他操作用不同的lock控制),因为驱动程序正在忙于更改摄像头的曝光等。当然,您始终可以通过将两个锁指针保留为空来自己完成所有锁定。如果您使用旧的videobuf框架,则必须将video_device->lock传递给videobuf队列初始化函数:如果videobuf需要等待帧到达,则它将临时解锁该锁,并在之后重新锁定该锁。如果您的驱动程序也在代码中等待,则应该执行相同的操作,以允许其他进程在第一个进程等待某些内容时访问设备节点。
对于videobuf2,您需要实现wait_prepare()和wait_finish()回调以解锁/锁定(如果适用)。如果使用queue->lock指针,则可以使用辅助函数vb2_ops_wait_prepare()和vb2_ops_wait_finish()。在执行热插拔断开连接的实现之前,还应从video_device获取锁,然后调用v4l2_device_disconnect。如果您还使用video_device->queue->lock,则必须先锁定video_device->queue->lock,然后锁定video_device->lock。这样,您就可以确保在调用v4l2_device_disconnect()时没有ioctl正在运行。
videobuf2是什么:
videobuf2是Linux内核中提供的一种视频缓存管理框架,它使得视频采集或播放驱动程序的实现更加简单和可靠。它可以处理多个视频帧的非连续内存分配,支持内存映射和用户空间I / O,可以实现零拷贝,在极大程度上优化了多媒体子系统的性能。并且,它还支持超时、延迟、丢弃和错误恢复等特性,使驱动程序更加健壮。
2.2.4.2 Video device registration
接下来,您需要使用video_register_device()注册视频设备。这将为您创建字符设备。
err = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (err) {
video_device_release(vdev); /* or kfree(my_vdev); */
return err;
}
如果v4l2_device父设备具有非NULL的mdev字段,则视频设备实体将自动与媒体设备注册。注册的设备取决于type参数。以下是不同的设备类型:
- VFL_TYPE_VBI:这个类型适用于垂直切线编码器(VBI)设备,比如电视卡。
- VFL_TYPE_RADIO:这个类型适用于收音机设备,比如FM收音机卡。
- VFL_TYPE_SDR:这个类型适用于软件定义无线电设备,比如RTL-SDR。
......
最后一个参数可以让您在设备节点号(即videoX中的X)使用上获得一定的控制。通常,您会传递-1,以便让v4l2框架选择第一个可用的编号。但有时用户想要选择特定的节点号。驱动程序通常允许用户通过驱动程序模块选项选择特定的设备节点号。然后将该编号传递给此函数,video_register_device将尝试选择该设备节点号。如果该编号已经在使用,则会选择下一个空闲的设备节点号,并将警告发送到内核日志中。
另一个用例是,如果驱动程序创建了许多设备。在这种情况下,将不同的视频设备放置在单独的范围内可能很有用。例如,视频采集设备从0开始,视频输出设备从16开始。因此,您可以使用最后一个参数来指定最小设备节点号,v4l2框架将尝试选择第一个空闲的编号,该编号等于或高于您传递的值。如果失败,则它将选择第一个空闲的编号。
在这种情况下,如果您不关心无法选择指定的设备节点号的警告,则可以调用video_register_device_no_warn()函数。每当创建设备节点时,都会为您创建一些属性。如果您查看/sys/class/video4linux,您会看到这些设备。进入例如video0,您将看到'name'、'dev_debug'和'index'属性。'name'属性是video_device结构体的'name'字段。'dev_debug'属性可用于启用核心调试。有关更详细的信息,请参见下一节。
“index”属性是设备节点的索引:对于每次调用video_register_device(),索引只增加1。您注册的第一个视频设备节点始终从索引0开始。
用户可以设置udev规则,利用索引属性来创建漂亮的设备名称(例如MPEG视频采集设备节点的“mpegX”)。
设备成功注册后,就可以使用以下字段:
• video_device->vfl_type:传递给video_register_device()的设备类型。
• video_device->minor:分配的设备次设备号。
• video_device->num:设备节点号(即videoX中的X)。
• video_device->index:设备索引号。
如果注册失败,则需要调用video_device_release()来释放分配的video_device结构体,或者释放您自己的结构体,如果video_device嵌入在其中。如果注册失败,vdev->release()回调将永远不会被调用,也不应尝试取消注册设备。
2.2.4.3 video device debugging
‘dev_debug’属性是为每个video、vbi、radio或swradio设备在/sys/class/video4linux/<devX>/中创建的,它允许您启用文件操作的日志记录。它是一个位掩码,可以设置以下位:
2.2.4.4 Video device cleanup
当必须删除视频设备节点时,无论是在卸载驱动程序期间还是因为USB设备已断开连接,您都应该使用以下方法注销它们:video_unregister_device()(vdev);这将从sysfs中删除设备节点(导致udev将它们从/dev中删除)。在video_unregister_device()返回之后,无法再进行新的打开操作。但是,在USB设备的情况下,某些应用程序可能仍然会打开其中一个这些设备节点。因此,在取消注册后,除了释放之外,所有文件操作(当然还有)都将返回错误。
当视频设备节点的最后一个用户退出时,就会调用vdev->release()回调函数,您可以在其中进行最终清理。如果已经初始化,请不要忘记清理与视频设备关联的媒体实体:media_entity_cleanup(&vdev->entity); 这可以从释放回调中完成。
2.2.4.5 helper functions
有一些有用的辅助函数:
• 文件和video_device私有数据
您可以使用以下函数在video_device结构中设置/获取驱动程序私有数据:
video_get_drvdata(vdev);
video_set_drvdata(vdev);
请注意,在调用video_register_device()之前,您可以安全地调用video_set_drvdata()。
还有这个函数:
video_devdata(struct file *file);
返回与文件结构相关联的video_device。
video_devdata()函数将video_get_drvdata()与video_devdata()组合在一起:
video_drvdata(struct file *file);
您可以通过以下方式从video_device结构转换为v4l2_device结构:
struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
• 设备节点名称
可以使用以下函数检索video_device节点内核名称:
video_device_node_name(vdev);
该名称用作用户空间工具(例如udev)的提示。应尽可能使用该函数,而不是访问video_device ::num和video_device ::minor字段。
2.2.4.6 video_device functions and data structures
enum vfl_devnode_type
V4L2设备节点类型
常量
VFL_TYPE_VIDEO:视频输入/输出设备
VFL_TYPE_VBI:垂直空白数据(即闭路字幕,电视文字)
VFL_TYPE_RADIO:广播调谐器
VFL_TYPE_SUBDEV:V4L2子设备
VFL_TYPE_SDR:软件定义的无线电调谐器
VFL_TYPE_TOUCH:触摸传感器
VFL_TYPE_MAX:VFL类型数,必须始终位于枚举中的最后一个位置。
enum vfl_devnode_direction
指示struct video_device是接收器、发射器还是mem-to-mem设备。
常量
VFL_DIR_RX:设备是接收器。
VFL_DIR_TX:设备是发射器。
VFL_DIR_M2M:设备是mem到mem设备。
注意
如果枚举vfl_devnode_type为VFL_TYPE_SUBDEV,则忽略。
enum v4l2_video_device_flags
struct video_device使用的标志
常量
V4L2_FL_REGISTERED
表示已注册struct video_device。如果驱动程序想阻止所有未来的设备访问,驱动程序可以清除此标志。video_unregister_device将其清除。
V4L2_FL_USES_V4L2_FH
表示file->private_data指向struct v4l2_fh。当调用v4l2_fh_init()时,内核会设置此标志。所有新驱动程序都应使用它。
V4L2_FL_QUIRK_INVERTED_CROP
一些旧的M2M驱动程序错误地使用了g/s_crop/cropcap:crop和compose被交换了。如果设置了此标志,则在v4l2-ioctl.c的g/s_crop/cropcap函数中交换选定目标。这允许这些驱动程序正确实现选择API,但旧的crop API仍将像预期的那样工作,以保持向后兼容性。从不为新驱动程序设置此标志。
V4L2_FL_SUBDEV_RO_DEVNODE
表示视频设备节点已以只读模式注册。
该标志仅适用于为子设备注册的设备节点,当使用v4l2_device_register_ro_subdev_nodes()注册子设备设备节点时,内核会设置此标志,并由子设备ioctl处理程序用于限制对某些ioctl调用的访问。
struct v4l2_prio_state
存储优先级状态
定义
struct v4l2_prio_state {
atomic_t prios[4];
};
成员
prios数组,用于存储数组优先级
描述
注意:prios数组的大小与v4l2_priority枚举定义的优先级类型数量相匹配。
void v4l2_prio_init(struct v4l2_prio_state *global)
用于初始化struct v4l2_prio_state结构体
参数
struct v4l2_prio_state *global指向struct v4l2_prio_state的指针
int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local, enum v4l2_priority new)
用于更改v4l2文件操作的优先级
参数
struct v4l2_prio_state *global 指向设备节点的struct v4l2_prio_state结构体指针。
enum v4l2_priority *local 指向所需优先级的指针,定义在枚举v4l2_priority中。
enum v4l2_priority new 所请求的优先级类型,由枚举v4l2_priority定义。
描述
注意:此函数仅应由V4L2核心使用。
void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local)
用于实现文件操作符打开的优先级逻辑
参数
struct v4l2_prio_state *global 指向设备节点的struct v4l2_prio_state结构体指针。
enum v4l2_priority *local 指向所需优先级的指针,定义在枚举v4l2_priority中。
描述
注意:此函数仅应由V4L2核心使用。
void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local)
用于实现文件操作符关闭的优先级逻辑
参数
struct v4l2_prio_state *global 指向设备节点的struct v4l2_prio_state结构体指针。
enum v4l2_priority local 所释放的优先级,定义在枚举v4l2_priority中。
描述
注意:此函数仅应由V4L2核心使用。
enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global)
返回全局数组中存储的最大优先级
参数
struct v4l2_prio_state *global 指向设备节点的struct v4l2_prio_state结构体指针。
描述
注意:此函数仅应由V4L2核心使用。
int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local)
用于检查所请求的优先级是否正确
参数
struct v4l2_prio_state *global 指向设备节点的struct v4l2_prio_state结构体指针。
enum v4l2_priority local 所请求的优先级,定义在枚举v4l2_priority中。
描述
返回值:若所请求的优先级比当前最高优先级更低,则返回-EBUSY;否则返回0。注意:此函数仅应由V4L2核心使用。
struct v4l2_file_operations
V4L2设备所使用的文件系统操作集合
定义
struct v4l2_file_operations {
struct module *owner;
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
__poll_t (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
#ifdef CONFIG_COMPAT;
long (*compat_ioctl32) (struct file *, unsigned int, unsigned long);
#endif;
unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct file *);
int (*release) (struct file *);
};
成员
owner 指向struct module结构体的指针。
read 实现read()系统调用所需的操作。
write 实现write()系统调用所需的操作。
poll 实现poll()系统调用所需的操作。
unlocked_ioctl 实现ioctl()系统调用所需的操作。
compat_ioctl32 实现特殊情况下Kernel使用64位指令,但用户空间使用32位指令时所需的ioctl()操作。
get_unmapped_area 被mmap()系统调用调用,用于当%!CONFIG_MMU时。
mmap 实现mmap()系统调用所需的操作。
open 实现open()系统调用所需的操作。
release 实现release()系统调用所需的操作。
描述
注意:这些操作用于实现V4L2驱动程序上的fs struct file_operations。V4L2核心会用一些额外的逻辑覆盖fs操作,以满足该子系统的需求。
struct video_device
是用于创建和管理V4L2设备节点的结构体。它包含以下成员:
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;
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;
char name[32];
enum vfl_devnode_type vfl_type;
enum vfl_devnode_direction vfl_dir;
int minor;
u16 num;
unsigned long flags;
int index;
spinlock_t fh_lock;
struct list_head
fh_list;
int dev_debug;
v4l2_std_id tvnorms;
void (*release)(struct video_device *vdev);
const struct v4l2_ioctl_ops *ioctl_ops;
unsigned long valid_ioctls[BITS_TO_LONGS(BASE_VIDIOC_PRIVATE)];
struct mutex *lock;
};
成员
entity:如果CONFIG_MEDIA_CONTROLLER已定义,则它是一个指向struct media_entity结构体的指针。
intf_devnode:如果CONFIG_MEDIA_CONTROLLER已定义,则它是一个指向struct media_intf_devnode结构体的指针。
pipe:如果CONFIG_MEDIA_CONTROLLER已定义,则它是一个struct media_pipeline结构体。
fops:指向struct v4l2_file_operations的指针,用于管理视频设备的文件系统操作。
device_caps:设备能力,用于v4l2_capabilities。
dev:指向视频设备的struct device结构体。
cdev:字符设备。
v4l2_dev:指向父struct v4l2_device结构体的指针。
dev_parent:指向父struct device结构体的指针。
ctrl_handler:与此设备节点关联的控制句柄。可能为空。
queue:与该设备节点相关联的struct vb2_queue。可能为空。
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:视频设备标志。使用位操作来设置/清除/测试标志。包含一组enum v4l2_video_device_flags。
index:用于区分一个物理设备上的多个索引的属性。
fh_lock:所有v4l2_fhs的锁。
fh_list:struct v4l2_fh的列表。
dev_debug:内部设备调试标志,不供驱动程序使用。
tvnorms:支持的电视制式。
release:视频设备释放()回调。
ioctl_ops:指向struct v4l2_ioctl_ops的指针,包含ioctl回调。
valid_ioctls:此设备的有效ioctls位图。
lock:指向struct mutex的指针,用于序列化锁定。
描述
注意:只有在无法从v4l2_dev中推断出dev_parent时才设置dev_parent。
media_entity_to_video_device(__entity)
函数从嵌入在其上的struct media_entity返回一个struct video_device。
参数:
__entity:指向struct media_entity的指针。
to_video_device(cd)
函数从嵌入在其上的struct device返回一个struct video_device。
参数:
cd:指向struct device的指针。
int __video_register_device(struct video_device *vdev, enum vfl_devnode_type type, int nr, int warn_if_nr_in_use, struct module *owner)
函数用于注册video4linux设备。
参数:
vdev:要注册的struct video_device。
type:要注册的设备类型,由enum vfl_devnode_type定义。
nr:所需的设备节点号,其中0表示/dev/video0,1表示/dev/video1,依此类推;-1表示第一个可用的设备节点号。
warn_if_nr_in_use:如果所需的设备节点号已被使用且选择了另一个设备节点号,则提出警告。
owner:拥有视频设备节点的模块。
描述:
注册代码根据请求的类型分配次要号和设备节点号,并将新设备节点注册到内核中。
假定在分配struct video_device时将其清零,并且不含有任何陈旧的数据。
如果找不到空闲的次要号或设备节点号,或者设备节点注册失败,则返回错误。
成功时返回0。
注意:此函数仅用于V4L2核内部。驱动程序应使用video_register_device()或video_register_device_no_warn()。
int video_register_device(struct video_device *vdev, enum vfl_devnode_type type, int nr)
函数用于注册video4linux设备。
参数:
vdev:要注册的struct video_device。
type:要注册的设备类型,由enum vfl_devnode_type定义。
nr:所需的设备节点号,其中0表示/dev/video0,1表示/dev/video1,依此类推;-1表示第一个可用的设备节点号。
描述:
在内部,它调用__video_register_device()。请参阅其文档以获取更多详细信息。
注意:如果video_register_device失败,则不会调用struct video_device结构的release()回调函数,因此调用方负责释放任何数据。通常,在失败时应调用video_device_release()。
int video_register_device_no_warn(struct video_device *vdev, enum vfl_devnode_type type, int nr)
函数与video_register_device()相同,除了如果所需的设备节点号已被使用,则不会发出警告。
在内部,它调用__video_register_device()。请参阅其文档以获取更多详细信息。
注意:如果video_register_device失败,则不会调用struct video_device结构的release()回调函数,因此调用方负责释放任何数据。通常,在失败时应调用video_device_release()。
void video_unregister_device(struct video_device *vdev)
函数用于注销video4linux设备。
参数:
vdev:要注销的struct video_device。
描述:
如果vdev == NULL或video_is_registered()返回false,则不执行任何操作。
struct video_device * video_device_alloc(void)
函数用于分配struct video_device。
参数:无参数。
描述:成功时返回struct video_device,如果-ENOMEM,则返回NULL。
void video_device_release(struct video_device *vdev)
函数用于释放struct video_device。
参数:指向struct video_device的指针vdev。
描述:也可用于video_device->release()。
void video_device_release_empty(struct video_device *vdev)
函数用于实现video_device->release()回调。
参数:指向struct video_device的指针vdev。
描述:此release函数不执行任何操作。它应该在video_device是静态全局结构时使用。
注意:拥有静态video_device只是最优解的可疑构造。
void v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd)
函数用于标记给定命令未实现。不应使用core锁定。
参数:
vdev:指向struct video_device的指针。
cmd:ioctl命令。
描述:
该函数允许驱动程序提供一个v4l2_ioctl_ops结构,但基于实际找到的特定卡禁用ioctls。
注意:这必须在video_register_device之前调用。另请参阅deter-mine_valid_ioctls()的注释。
void * video_get_drvdata(struct video_device *vdev)
函数用于从struct video_device获取私有数据。
参数:指向struct video_device的指针vdev。
描述:返回指向私有数据的指针。
void video_set_drvdata(struct video_device *vdev, void *data)
函数用于设置struct video_device的私有数据。
参数:
vdev:指向struct video_device的指针。
data:私有数据指针。
描述:设置结构体中的私有数据指针以便将数据与struct video_device关联。
struct video_device * video_devdata(struct file *file)
函数用于从struct file获取struct video_device。
参数:指向struct file的指针file。
描述:返回文件的struct video_device。
void * video_drvdata(struct file *file)
函数用于使用struct file从struct video_device中获取私有数据。
参数:指向struct file的指针file。
描述:此函数结合了video_get_drvdata()和video_devdata(),因为这两个函数经常一起使用。
const char * video_device_node_name(struct video_device *vdev)
函数用于返回视频设备的名称。
参数:指向struct video_device的指针vdev。
描述:返回视频设备名称字符串。
int video_is_registered(struct video_device *vdev)
函数用于检查struct video_device是否已注册。
参数:指向struct video_device的指针vdev。
描述:如果struct video_device已注册,则返回true;否则返回false。
2.2.5 V4L2 device instance
每个设备实例都由struct v4l2_device表示。非常简单的设备只需要分配此结构,但大多数情况下你会在更大的结构里嵌入这个结构体。必须通过调用v4l2_device_register (dev, v4l2_dev)来注册设备实例。
注册将初始化v4l2_device结构。如果dev->driver_data字段为NULL,则将链接到v4l2_dev参数。
希望与媒体设备框架集成的驱动程序需要手动将dev->driver_data设置为指向嵌入struct v4l2_device实例的特定驱动程序结构。在注册V4L2设备实例之前,可以通过dev_set_drvdata()调用来实现此目的。它们还必须将struct v4l2_device mdev字段设置为指向已正确初始化和注册的media_device实例。
如果v4l2_dev->name为空,则会设置为从dev中派生的值(确切地说是驱动程序名称后跟bus_id)。如果你在调用v4l2_device_register()之前设置它,则它将保持不变。如果dev为NULL,则必须在调用v4l2_device_register()之前设置v4l2_dev->name。
可以使用v4l2_device_set_name()基于驱动程序名称和驱动程序全局atomic_t实例来设置名称。这将生成类似于ivtv0、ivtv1等的名称。如果名称以数字结尾,则会插入破折号:cx18-0、cx18-1等。此函数返回实例编号。
第一个dev参数通常是pci_dev、usb_interface或platform_device的struct device指针。dev为NULL很少见,但在ISA设备或一个设备创建多个PCI设备时会出现这种情况,因此无法将v4l2_dev与特定父设备关联起来。
你还可以提供一个notify()回调,可由子设备调用以通知你事件。是否需要设置这个取决于子设备。任何子设备支持的通知都必须在include/media/subdevice.h头文件中定义。
调用v4l2_device_unregister()(v4l2_dev)即可注销V4L2设备。如果dev->driver_data字段指向v4l2_dev,则它将被重置为NULL。注销还将自动从设备注销所有子设备。
如果你有一个可热插拔的设备(例如USB设备),那么当断开连接时父设备将变为无效。由于v4l2_device有一个指向该父设备的指针,因此也必须清除它以标记父项已消失。要做到这一点,请调用v4l2_device_disconnect()(v4l2_dev)。
这不会注销子设备,因此你仍然需要为此调用v4l2_device_unregister()函数。如果你的驱动程序不支持热插拔,则无需调用v4l2_device_disconnect()。
有时你需要迭代特定驱动程序注册的所有设备。这通常是因为多个设备驱动程序使用相同的硬件而产生的。例如,ivtvfb驱动程序是使用ivtv硬件的帧缓冲驱动程序。对于alsa驱动程序也是如此。
你可以按以下方式迭代所有已注册的设备:
static int callback(struct device *dev, void *p)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
/* test if this device was inited */
if (v4l2_dev == NULL)
return 0;
...
return 0;
}
int iterate(void *p)
{
struct device_driver *drv;
int err;
/* Find driver 'ivtv' on the PCI bus.
pci_bus_type is a global. For USB buses use usb_bus_type. */
drv = driver_find("ivtv", &pci_bus_type);
/* iterate over all ivtv device instances */
err = driver_for_each_device(drv, NULL, p, callback);
put_driver(drv);
return err;
}
有时需要保持设备实例的运行计数器。这通常用于将设备实例映射到模块选项数组的索引。
推荐的方法如下:
static atomic_t drv_instance = ATOMIC_INIT(0);
static int drv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
{
...
state->instance = atomic_inc_return(&drv_instance) - 1;
}
如果你有多个设备节点,则很难知道何时可以安全地为可热插拔设备注销v4l2_device。为此,v4l2_device具有引用计数支持。每次调用video_register_device()时增加计数,释放该设备节点时减小计数。当引用计数达到零时,则会调用v4l2_device_release()回调。你可以在那里进行最终清理。
如果创建了其他设备节点(例如ALSA),则也可以通过调用以下方式手动增加和减少引用计数:
v4l2_device_get()(v4l2_dev)。
或:
v4l2_device_put()(v4l2_dev)。
由于初始引用计数为1,因此还需要在disconnect()回调(对于USB设备)或remove()回调(例如PCI设备)中调用v4l2_device_put(),否则引用计数永远不会达到0。
2.2.5.1 v4l2_device functions and data structures
struct v4l2_device
是V4L2设备驱动程序的主要结构体。
定义:
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);
};
它包含以下成员变量:
dev:指向 struct device 的指针。
mdev:指向 struct media_device 的指针,可以为 NULL。
subdevs:用于跟踪已注册的子设备。
lock:锁定该结构体;如果该结构体嵌入到更大的结构体中,则驱动程序也可以使用它。
name:唯一设备名称,默认为驱动程序名称 + 总线 ID。
notify:由某些子设备调用的通知操作。
ctrl_handler:控制处理程序。可能为NULL。
prio:设备的优先级状态。
ref:跟踪对此结构体的引用。
release:当引用计数归零时调用的释放函数。
每个 V4L2 设备实例都应创建 v4l2_device 结构体,可以作为独立结构体或嵌入到更大的结构体中。它允许轻松访问子设备(请参见v4l2-subdev.h)并提供基本的V4L2设备级支持。
有两点需要注意:
1)dev->driver_data 指向此结构体。
2)如果没有父设备,则dev可能为空。
v4l2_device name举个例子:
v4l2_device的name成员变量是唯一设备名称,默认为驱动程序名称 + 总线 ID。举个例子,如果使用的是“my_usb_camera”驱动程序,总线ID为“1-1.2”,则该设备的名称将是“my_usb_camera 1-1.2”。
void v4l2_device_get(struct v4l2_device *v4l2_dev)
函数功能:获取V4L2设备的引用计数
参数:
* struct v4l2_device *v4l2_dev:指向struct v4l2_device的指针
描述:这是一个辅助函数,旨在增加由v4l2_dev指向的struct v4l2_device的使用量。
int v4l2_device_put(struct v4l2_device *v4l2_dev)
函数功能:释放V4L2设备的引用计数。
参数:
* struct v4l2_device *v4l2_dev:指向struct v4l2_device的指针
描述:这是一个辅助函数,旨在减少由v4l2_dev指向的struct v4l2_device的使用量。
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
函数功能:初始化v4l2_dev并使dev->driver_data指向v4l2_dev。
参数:
- struct device *dev:指向struct device的指针。
- struct v4l2_device *v4l2_dev:指向struct v4l2_device的指针。
描述:此函数用于初始化v4l2_dev,并将dev->driver_data指向v4l2_dev。v4l2_dev应该在使用之前已经被初始化,并且至少有一个字符名称作为其成员。dev可以为空,在一些罕见的情况下(ISA设备)会出现这种情况。在这种情况下,调用者必须在调用此函数之前填写v4l2_dev->name字段。该函数成功时返回0,失败时返回一个负错误代码。
int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename, atomic_t *instance)
函数功能:初始化struct v4l2_device的名称字段。
参数:
- struct v4l2_device *v4l2_dev:指向struct v4l2_device的指针。
- const char *basename:设备名称的基本名称。
- atomic_t *instance:指向静态atomic_t变量的指针,该变量用于记录设备实例的使用情况。
描述:v4l2_device_set_name()函数使用驱动程序名称和驱动程序全局的atomic_t实例来初始化struct v4l2_device的名称字段。这个函数将增加实例计数器,并返回在名称中使用的实例值。第一次调用此函数时,名称字段将被设置为foo0,并返回0。如果名称以数字结尾(例如cx18),那么名称将被设置为cx18-0,因为cx180看起来真的很奇怪。该函数成功时返回使用的实例值,失败时返回一个负错误代码。
举例:
static atomic_t drv_instance = ATOMIC_INIT(0);
…
instance = v4l2_device_set_name(&v4l2_dev, “foo”, &drv_instance);
void v4l2_device_disconnect(struct v4l2_device *v4l2_dev)
函数功能:将V4L2设备的状态更改为已断开连接。
参数:
- struct v4l2_device *v4l2_dev:指向struct v4l2_device的指针。
描述:当USB父设备断开连接时应该调用此函数。由于父设备消失,这能确保v4l2_dev没有无效的父指针。注意:此函数将v4l2_dev->dev设置为NULL。在调用此函数之后,如需重新使用v4l2_dev,需要重新注册它。该函数没有返回值。
void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
函数功能:注销所有与v4l2_dev相关的子设备和其他资源。
参数:
- struct v4l2_device *v4l2_dev:指向struct v4l2_device的指针。
描述:此函数用于注销与v4l2_dev相关的所有子设备和其他资源。该函数会构造出一个包含v4l2_dev结构体的列表,并使用此列表反向遍历device_list,以确保在注销v4l2_dev之前任何依赖于它的子设备或驱动程序都已被注销。在注销v4l2_dev时,首先会解除dev->driver_data对v4l2_dev的引用,然后释放v4l2_dev的内存,并递减v4l2_get_minor_index()的返回值。该函数没有返回值。
int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, struct v4l2_subdev *sd)
函数功能:向V4L2设备注册子设备。
参数:
- struct v4l2_device *v4l2_dev:指向struct v4l2_device的指针。
- struct v4l2_subdev *sd:指向struct v4l2_subdev的指针。
描述:当一个子设备被注册到一个V4L2设备时,该子设备模块将被标记为正在使用中。如果该模块已被卸载,任何尝试注册它的操作都会返回错误。该函数首先会调用v4l2_subdev_init()函数对子设备进行初始化,并为其设置驱动程序的名称和父V4L2设备指针。然后,它会检查设备状态以确保设备处于“注册”状态,并将子设备添加到v4l2_dev->subdevs列表中。最后,该函数会增加v4l2_dev的引用计数并返回0(成功),如果出现错误则返回负错误代码。
void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
函数功能:从V4L2设备中注销子设备。
参数:
- struct v4l2_subdev *sd:指向struct v4l2_subdev的指针。
描述:当一个子设备被注销时,该子设备模块将被标记为不在使用中。该函数首先会从sd->dev->v4l2_dev->subdevs列表中删除该子设备,并将其状态设置为“未注册”。然后,如果该子设备当前处于使用状态,则会将其状态设置为“未使用”。最后,该函数会递减v4l2_get_minor_index()的返回值。如果该子设备未被注册,则该函数不会进行任何操作。
int __v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev, bool read_only)
函数功能:为所有在V4L2设备中标记了V4L2_SUBDEV_FL_HAS_DEVNODE标志的子设备注册设备节点。
参数:
- struct v4l2_device *v4l2_dev:指向struct v4l2_device的指针。
- bool read_only:子设备只读标志。如果为true,则在只读模式下注册子设备设备节点;如果为false,则允许完全访问子设备用户空间API。
描述:该函数用于为所有在V4L2设备中标记了V4L2_SUBDEV_FL_HAS_DEVNODE标志的子设备注册相应的设备节点。该函数会遍历v4l2_dev->subdevs列表,检查每个子设备的标志,如果包含V4L2_SUBDEV_FL_HAS_DEVNODE标志,则会调用v4l2_subdev_create_v4l2_devnode()函数为其创建设备节点并将其添加到系统的/dev目录中。如果创建设备节点失败,则该函数会记录错误并返回负错误代码。否则,该函数将继续注册后续子设备的设备节点,直到列表遍历完成或出现错误。最后,该函数将返回0(成功)或负错误代码。
int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
函数功能:以不受限制的方式向子设备用户空间操作注册子设备设备节点。
参数:
- struct v4l2_device *v4l2_dev:指向struct v4l2_device的指针。
描述:该函数调用__v4l2_device_register_subdev_nodes()函数,以不受限制的方式为所有在V4L2设备中标记了V4L2_SUBDEV_FL_HAS_DEVNODE标志的子设备注册相应的设备节点。这意味着用户可以完全访问子设备用户空间API,并能够对其执行读取和写入操作。具体实现细节请参阅__v4l2_device_register_subdev_nodes()函数的文档。
int v4l2_device_register_ro_subdev_nodes(struct v4l2_device *v4l2_dev)
函数功能:以只读模式注册子设备设备节点。
参数:
- struct v4l2_device *v4l2_dev:指向struct v4l2_device的指针。
描述:该函数调用__v4l2_device_register_subdev_nodes()函数,以只读模式为所有在V4L2设备中标记了V4L2_SUBDEV_FL_HAS_DEVNODE标志的子设备注册相应的设备节点。这意味着用户对子设备API的访问将被限制为只读操作,不能执行任何写入操作。具体实现细节请参阅__v4l2_device_register_subdev_nodes()函数的文档。
void v4l2_subdev_notify(struct v4l2_subdev *sd, unsigned int notification, void *arg)
函数功能:向V4L2设备发送通知。
参数:
- struct v4l2_subdev *sd:指向struct v4l2_subdev的指针。
- unsigned int notification:通知类型。请注意,通知类型是特定于驱动程序的。
- void *arg:通知的参数。这些参数对于每个通知类型都是特定的。
描述:该函数用于向V4L2设备发送通知。通知类型和参数是特定于驱动程序的,可以通过notification和arg参数传递。具体实现细节取决于驱动程序的实现。
bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev)
函数功能:测试是否支持请求。
参数:
- struct v4l2_device *v4l2_dev:指向struct v4l2_device的指针。
描述:该函数用于测试V4L2设备是否支持请求。如果支持,将返回true;否则,将返回false。请求是一种机制,允许用户从V4L2设备中获取视频帧以进行处理,并将这些帧发送回设备以进行传输。该函数在设备注册时被调用,以确定是否支持请求,并且驱动程序必须明确地支持请求才能通过请求API与设备进行交互。
v4l2_device_for_each_subdev(sd, v4l2_dev)
函数功能:迭代给定V4L2设备的所有子设备的帮助宏。
参数:
- sd:指针,在宏中将被填充为所有struct v4l2_subdev指针,用作循环的迭代器。
- v4l2_dev:拥有要迭代的子设备的struct v4l2_device。
描述:该宏用于迭代给定V4L2设备的所有子设备。它充当for循环迭代器,并执行下一个语句以使sd变量依次指向每个子设备。在迭代过程中,可以使用sd指针来访问每个子设备的属性和方法。具体实现细节可以参考宏定义。
__v4l2_device_call_subdevs_p(v4l2_dev, sd, cond, o, f, args…)
函数功能:对所有与条件匹配的子设备调用指定操作。
参数:
- v4l2_dev:拥有要迭代的子设备的struct v4l2_device。
- sd:指针,在宏中将被填充为所有struct v4l2_subdev指针,用作循环的迭代器。
- cond:要匹配的条件。
- o:在struct v4l2_subdev_ops中包含f的元素的名称。每个元素将一组操作函数分组。
- f:如果cond匹配,将调用的操作函数。操作函数根据struct v4l2_subdev_ops中的每个元素进行定义。
- args…:f的参数。
描述:该宏用于对所有与条件匹配的子设备调用指定操作。可以在给定条件下匹配一个或多个子设备,并且将调用与操作函数名称相对应的操作函数。操作函数通常用于设置或获取与子设备相关的属性。需要注意的是,在迭代子设备列表时无法添加或删除子设备。具体的操作和实现细节可以参考宏定义。
__v4l2_device_call_subdevs(v4l2_dev, cond, o, f, args…)
函数功能:对所有与条件匹配的子设备调用指定操作。
参数:
- v4l2_dev:拥有要迭代的子设备的struct v4l2_device。
- cond:要匹配的条件。
- o:在struct v4l2_subdev_ops中包含f的元素的名称。每个元素将一组操作函数分组。
- f:如果cond匹配,将调用的操作函数。操作函数根据struct v4l2_subdev_ops中的每个元素进行定义。
- args…:f的参数。
描述:该宏用于对所有与条件匹配的子设备调用指定操作。可以在给定条件下匹配一个或多个子设备,并且将调用与操作函数名称相对应的操作函数。操作函数通常用于设置或获取与子设备相关的属性。需要注意的是,在迭代子设备列表时无法添加或删除子设备。具体的操作和实现细节可以参考宏定义。
__v4l2_device_call_subdevs_until_err_p(v4l2_dev, sd, cond, o, f, args…)
函数功能:对所有与条件匹配的子设备调用指定操作,如果有任何一个子设备返回非0或-ENOIOCTLCMD错误,则终止并返回该错误码,否则返回0。
参数:
- v4l2_dev:拥有要迭代的子设备的struct v4l2_device。
- sd:指针,在宏中将被填充为所有struct v4l2_subdev指针,用作循环的迭代器。
- cond:要匹配的条件。
- o:在struct v4l2_subdev_ops中包含f的元素的名称。每个元素将一组操作函数分组。
- f:如果cond匹配,将调用的操作函数。操作函数根据struct v4l2_subdev_ops中的每个元素进行定义。
- args…:f的参数。
返回值:如果有任何一个子设备返回非0或-ENOIOCTLCMD错误,则返回该错误码,否则返回0。
描述:该宏用于对所有与条件匹配的子设备调用指定操作,并且只要有一个子设备返回错误,则立即停止迭代并返回错误代码。操作函数可以用于设置或获取与子设备相关的属性。需要注意的是,在迭代子设备列表时无法添加或删除子设备。具体的操作和实现细节可以参考宏定义。
__v4l2_device_call_subdevs_until_err(v4l2_dev, cond, o, f, args…)
函数功能:对所有与条件匹配的子设备调用指定的操作函数,如果任何一个子设备返回非0或-ENOIOCTLCMD错误,则立即停止迭代并返回该错误码,否则返回0。
参数:
- v4l2_dev:拥有要迭代的子设备的struct v4l2_device。
- cond:要匹配的条件。
- o:在struct v4l2_subdev_ops中包含f的元素的名称。每个元素将一组操作函数分组。
- f:如果cond匹配,将调用的操作函数。操作函数根据struct v4l2_subdev_ops中的每个元素进行定义。
- args…:f的参数。
返回值:如果有任何一个子设备返回非0或-ENOIOCTLCMD错误,则返回该错误码,否则返回0。
描述:该宏用于对所有与条件匹配的子设备调用指定操作函数,并且只要有一个子设备返回错误,则立即停止迭代并返回错误代码。操作函数可以用于设置或获取与子设备相关的属性。需要注意的是,在迭代子设备列表时无法添加或删除子设备。具体的操作和实现细节可以参考宏定义。
v4l2_device_call_all(v4l2_dev, grpid, o, f, args…)
函数功能:对所有与v4l2_subdev.grp_id匹配的子设备调用指定的操作函数,忽略任何错误。
参数:
- v4l2_dev:拥有要迭代的子设备的struct v4l2_device。
- grpid:要匹配的v4l2_subdev.grp_id。使用0可以匹配所有子设备。
- o:在struct v4l2_subdev_ops中包含f的元素的名称。每个元素将一组操作函数分组。
- f:如果cond匹配,将调用的操作函数。操作函数根据struct v4l2_subdev_ops中的每个元素进行定义。
- args…:f的参数。
返回值:无,忽略所有错误。
描述:该函数用于对所有与v4l2_subdev.grp_id匹配的子设备调用指定的操作函数,忽略任何错误。操作函数可以用于设置或获取与子设备相关的属性。需要注意的是,在迭代子设备列表时无法添加或删除子设备。具体的操作和实现细节可以参考函数定义。
v4l2_device_call_until_err(v4l2_dev, grpid, o, f, args…)
函数功能:对所有与v4l2_subdev.grp_id匹配的子设备调用指定的操作函数,直到发生错误为止。
参数:
- v4l2_dev:拥有要迭代的子设备的struct v4l2_device。
- grpid:要匹配的v4l2_subdev.grp_id。使用0可以匹配所有子设备。
- o:在struct v4l2_subdev_ops中包含f的元素的名称。每个元素将一组操作函数分组。
- f:如果cond匹配,将调用的操作函数。操作函数根据struct v4l2_subdev_ops中的每个元素进行定义。
- args…:f的参数。
返回值:如果有任何一个子设备返回非0或-ENOIOCTLCMD错误,则返回该错误码,否则返回0。
描述:该函数用于对所有与v4l2_subdev.grp_id匹配的子设备调用指定的操作函数,并且只要有一个子设备返回错误,则立即停止迭代并返回错误代码。操作函数可以用于设置或获取与子设备相关的属性。需要注意的是,在迭代子设备列表时无法添加或删除子设备。具体的操作和实现细节可以参考函数定义。
v4l2_device_mask_call_all(v4l2_dev, grpmsk, o, f, args…)
函数功能:对所有与指定位掩码匹配的子设备调用指定的操作函数,忽略任何错误。
参数:
- v4l2_dev:拥有要迭代的子设备的struct v4l2_device。
- grpmsk:要检查的位掩码,用于匹配struct v4l2_subdev->grp_id组ID。使用0可以匹配所有子设备。
- o:在struct v4l2_subdev_ops中包含f的元素的名称。每个元素将一组操作函数分组。
- f:如果cond匹配,将调用的操作函数。操作函数根据struct v4l2_subdev_ops中的每个元素进行定义。
- args…:f的参数。
返回值:无,忽略所有错误。
描述:该函数用于对所有与指定位掩码匹配的子设备调用指定的操作函数,忽略任何错误。操作函数可以用于设置或获取与子设备相关的属性。需要注意的是,在迭代子设备列表时无法添加或删除子设备。具体的操作和实现细节可以参考函数定义。
v4l2_device_mask_call_until_err(v4l2_dev, grpmsk, o, f, args…)
函数功能:对所有与指定位掩码匹配的子设备调用指定的操作函数,直到发生错误为止。
参数:
- v4l2_dev:拥有要迭代的子设备的struct v4l2_device。
- grpmsk:要检查的位掩码,用于匹配struct v4l2_subdev->grp_id组ID。使用0可以匹配所有子设备。
- o:在struct v4l2_subdev_ops中包含f的元素的名称。每个元素将一组操作函数分组。
- f:如果cond匹配,将调用的操作函数。操作函数根据struct v4l2_subdev_ops中的每个元素进行定义。
- args…:f的参数。
返回值:如果有任何一个子设备返回非0或-ENOIOCTLCMD错误,则返回该错误码,否则返回0。
描述:该函数用于对所有与指定位掩码匹配的子设备调用指定的操作函数,并且只要有一个子设备返回错误,则立即停止迭代并返回错误代码。操作函数可以用于设置或获取与子设备相关的属性。需要注意的是,在迭代子设备列表时无法添加或删除子设备。具体的操作和实现细节可以参考函数定义。
v4l2_device_has_op(v4l2_dev, grpid, o, f)
函数功能:检查与指定组ID匹配的任何子设备是否具有给定的操作。
参数:
- v4l2_dev:拥有要迭代的子设备的struct v4l2_device。
- grpid:要匹配的struct v4l2_subdev->grp_id组ID。使用0可以匹配所有子设备。
- o:在struct v4l2_subdev_ops中包含f的元素的名称。每个元素将一组操作函数分组。
- f:如果cond匹配,则调用的操作函数。操作函数根据struct v4l2_subdev_ops中的每个元素进行定义。
返回值:如果至少有一个与指定组ID匹配的子设备具有指定的操作,则返回1,否则返回0。
描述:该函数用于检查与指定组ID匹配的任何子设备是否具有给定的操作。如果至少有一个子设备具有指定的操作,则返回1,否则返回0。该函数可以用于检测特定类型的子设备是否可用,并在需要时执行相应的操作。具体的操作和实现细节可以参考函数定义。
v4l2_device_mask_has_op(v4l2_dev, grpmsk, o, f)
函数功能:检查与指定组ID掩码匹配的子设备是否具有给定的操作。
参数:
- v4l2_dev:拥有要迭代的子设备的struct v4l2_device。
- grpmsk:要检查的位掩码,用于匹配struct v4l2_subdev->grp_id组ID。使用0可以匹配所有子设备。
- o:在struct v4l2_subdev_ops中包含f的元素的名称。每个元素将一组操作函数分组。
- f:如果cond匹配,则调用的操作函数。操作函数根据struct v4l2_subdev_ops中的每个元素进行定义。
返回值:如果至少有一个与指定组ID掩码匹配的子设备具有指定的操作,则返回1,否则返回0。
描述:该函数用于检查与指定组ID掩码匹配的子设备是否具有给定的操作。如果至少有一个子设备具有指定的操作,则返回1,否则返回0。该函数可用于检测是否存在特定类型的子设备,并在需要时执行相应的操作。具体的操作和实现细节可以参考函数定义。
2.2.6 V4L2 File handlers
struct v4l2_fh提供了一种方便的方法来保留文件句柄特定的数据,该数据由V4L2框架使用。
注意:新的驱动程序必须使用struct v4l2_fh,因为它也用于实现优先处理(ioctl VIDIOC_G_PRIORITY,VIDIOC_S_PRIORITY)。
v4l2_fh的用户(在V4L2框架中,而不是驱动程序中)通过测试video_device->flags中的V4L2_FL_USES_V4L2_FH位来确定驱动程序是否将v4l2_fh用作其file->private_data指针。每当调用v4l2_fh_init()时,就会设置此位。
struct v4l2_fh作为驱动程序自己的文件句柄结构的一部分分配,并且在驱动程序的open()函数中将file->private_data设置为它。
在许多情况下,struct v4l2_fh将嵌入到较大的结构中。在这种情况下,您应该调用:
1)在open()中调用v4l2_fh_init()和v4l2_fh_add()。
2)在release()中调用v4l2_fh_del()和v4l2_fh_exit()。
驱动程序可以使用container_of宏提取其自己的文件句柄结构。
示例:
struct my_fh {
int blah;
struct v4l2_fh fh;
};
...
int my_open(struct file *file)
{
struct my_fh *my_fh;
struct video_device *vfd;
int ret;
...
my_fh = kzalloc(sizeof(*my_fh), GFP_KERNEL);
...
v4l2_fh_init(&my_fh->fh, vfd);
...
file->private_data = &my_fh->fh;
v4l2_fh_add(&my_fh->fh);
return 0;
}
int my_release(struct file *file)
{
struct v4l2_fh *fh = file->private_data;
struct my_fh *my_fh = container_of(fh, struct my_fh, fh);
...
v4l2_fh_del(&my_fh->fh);
v4l2_fh_exit(&my_fh->fh);
kfree(my_fh);
return 0;
}
上面是使用的v4l2_fh函数的简要描述:
v4l2_fh_init(fh, vdev)
• 初始化文件句柄。必须在驱动程序的v4l2_file_operations->open()处理程序中执行此操作。
v4l2_fh_add(fh)
• 将v4l2_fh添加到video_device文件句柄列表中。必须在文件句柄完全初始化后调用。
v4l2_fh_del(fh)
• 将文件句柄与video_device分离。现在可以调用文件句柄退出函数。
v4l2_fh_exit(fh)
• 取消初始化文件句柄。取消初始化v4l2_fh内存后可以释放。
如果未嵌入struct v4l2_fh,则可以使用以下辅助函数:
v4l2_fh_open(struct file *filp)
• 这将分配一个struct v4l2_fh,初始化它并将其添加到与文件结构相关联的struct video_device。
v4l2_fh_release(struct file *filp)
• 这将从与文件结构相关联的struct video_device中删除它,取消初始化v4l2_fh并释放它。
这两个函数可以插入v4l2_file_operation的open()和release()操作中。
多个驱动程序需要在打开第一个文件句柄和关闭最后一个文件句柄时执行某些操作。添加了两个辅助函数来检查v4l2_fh结构是否是关联设备节点的唯一打开文件句柄:
v4l2_fh_is_singular(fh)
• 如果文件句柄是唯一打开的文件句柄,则返回1,否则返回0。
v4l2_fh_is_singular_file(struct file *filp)
• 相同,但它使用filp->private_data调用v4l2_fh_is_singular。
2.2.6.1 V4L2 fh functions and data structures
struct v4l2_fh
描述了V4L2文件句柄
定义
struct v4l2_fh {
struct list_head list;
struct video_device *vdev;
struct v4l2_ctrl_handler *ctrl_handler;
enum v4l2_priority prio;
wait_queue_head_t wait;
struct mutex subscribe_lock;
struct list_head subscribed;
struct list_head available;
unsigned int navailable;
u32 sequence;
struct v4l2_m2m_ctx *m2m_ctx;
};
成员
list文件句柄列表
vdev指向struct video_device的指针
ctrl_handler指向struct v4l2_ctrl_handler的指针
prio文件句柄的优先级,由enum v4l2_priority定义
wait事件的等待队列
subscribe_lock对订阅列表进行串行化更改;保证添加和del事件回调有序调用
subscribed订阅事件列表
available等待出队的事件列表
navailable可用事件数在可用列表中
sequence事件序列号
m2m_ctx指向struct v4l2_m2m_ctx的指针
void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
初始化文件句柄。
参数
struct v4l2_fh *fh 指向struct v4l2_fh的指针
struct video_device *vdev 指向struct video_device的指针
描述
使用文件句柄的V4L2框架的部分应在此函数中进行初始化。如果驱动程序使用struct v4l2_fh,则必须从驱动程序的v4l2_file_operations->open()处理程序中调用它。
void v4l2_fh_add(struct v4l2_fh *fh)
将fh添加到video_device的文件句柄列表中。
参数
struct v4l2_fh *fh 指向struct v4l2_fh的指针
描述
注意:必须先初始化fh文件句柄。
int v4l2_fh_open(struct file *filp)
可以用作v4l2_file_operations的open()操作的辅助程序。
参数
struct file *filp 指向struct file的指针
描述
它分配一个v4l2_fh并将其初始化并添加到与文件指针关联的struct video_device中。
void v4l2_fh_del(struct v4l2_fh *fh)
从文件句柄列表中删除文件句柄。
参数
struct v4l2_fh *fh 指向struct v4l2_fh的指针
描述
如果出现错误,filp->private_data将为NULL,否则它将指向struct v4l2_fh。注意:如果驱动程序使用struct v4l2_fh,则必须在v4l2_file_operations->release()处理程序中调用它。
void v4l2_fh_exit(struct v4l2_fh *fh)
释放与文件句柄相关的资源。
参数
struct v4l2_fh *fh 指向struct v4l2_fh的指针
描述
使用v4l2_fh的V4L2框架的部分也必须在此处释放它们的资源。注意:如果驱动程序使用struct v4l2_fh,则必须在v4l2_file_operations->release()处理程序中调用它。
int v4l2_fh_release(struct file *filp)
可以用作v4l2_file_operations的release()操作的辅助程序。
参数
struct file *filp 指向struct file的指针
描述
它删除并退出与文件指针关联的v4l2_fh,并释放它。如果filp->private_data(指向v4l2_fh结构的指针)为NULL,则不执行任何操作。
此函数始终返回0。
int v4l2_fh_is_singular(struct v4l2_fh *fh)
如果此文件句柄是与之关联的video_device打开的唯一文件句柄,则返回1。
参数
struct v4l2_fh *fh 指向struct v4l2_fh的指针
描述
如果fh为NULL,则返回0。
int v4l2_fh_is_singular_file(struct file *filp)
如果此文件句柄是与之关联的video_device打开的唯一文件句柄,则返回1。
参数
struct file *filp 指向struct file的指针
描述
这是使用struct file作为参数的v4l2_fh_is_singular()的辅助函数变体。如果filp->private_data为NULL,则它将返回0。
2.2.7 V4L2 sub-devices
许多驱动程序需要与子设备通信。这些设备可以执行各种任务,但最常见的是处理音频和/或视频复用、编码或解码。对于网络摄像头,常见的子设备是传感器和相机控制器。
通常,这些都是I2C设备,但不一定。为了为驱动程序提供与这些子设备的一致接口,创建了v4l2_subdev结构体(v4l2-subdev.h)。
每个子设备驱动程序必须具有v4l2_subdev结构体。对于简单的子设备,该结构可以是独立的,或者如果需要存储更多状态信息,则可以嵌入到较大的结构体中。通常有一个低级设备结构(例如i2c_client),其中包含由内核设置的设备数据。建议使用v4l2_set_subdevdata()将该指针存储在v4l2_subdev的私有数据中。这使得从v4l2_subdev到实际的特定总线低级设备数据变得容易。
还需要一种从低级结构体到v4l2_subdev的方法。对于常见的i2c_client结构,使用i2c_set_clientdata()调用来存储v4l2_subdev指针,对于其他总线,您可能需要使用其他方法。
桥接器可能还需要存储每个子设备的私有数据,例如指向桥接器特定的每个子设备的私有数据的指针。v4l2_subdev结构提供了主机私有数据,可以通过v4l2_get_subdev_hostdata()和v4l2_set_subdev_hostdata()访问。
从桥接驱动程序的角度来看,您需要加载子设备模块并以某种方式获取v4l2_subdev指针。对于i2c设备,这很容易:您调用i2c_get_clientdata()。对于其他总线,需要进行类似的操作。为在I2C总线上的子设备存在辅助函数,可以为您完成大部分复杂的工作。
每个v4l2_subdev包含函数指针,子设备驱动程序可以实现(如果不适用,则保留为NULL)。由于子设备可以执行许多不同的操作,而您不希望最终得到一个包含大量仅有少数常见操作的巨大ops结构体,因此根据不同的操作类别对函数指针进行排序,每个类别都有自己的ops结构体。顶级ops结构体包含指向类别ops结构体的指针,如果子设备驱动程序不支持该类别的任何操作,则该指针可能为NULL。
它看起来像这样:
struct v4l2_subdev_core_ops {
int (*log_status)(struct v4l2_subdev *sd);
int (*init)(struct v4l2_subdev *sd, u32 val);
...
};
struct v4l2_subdev_tuner_ops {
...
};
struct v4l2_subdev_audio_ops {
...
};
struct v4l2_subdev_video_ops {
...
};
struct v4l2_subdev_pad_ops {
...
};
struct v4l2_subdev_ops {
const struct v4l2_subdev_core_ops
*core;
const struct v4l2_subdev_tuner_ops *tuner;
const struct v4l2_subdev_audio_ops *audio;
const struct v4l2_subdev_video_ops *video;
const struct v4l2_subdev_pad_ops *video;
};
core ops对所有子设备都是通用的,其他类别的实现取决于子设备。例如,视频设备不太可能支持音频操作,反之亦然。这种设置限制了函数指针的数量,同时使添加新操作和类别变得容易。
子设备驱动程序使用以下命令初始化v4l2_subdev结构:
v4l2_subdev_init (sd, &ops)。
此后,您需要使用唯一名称初始化sd->name,并设置模块所有者。如果使用i2c辅助函数,则会为您完成此操作。
如果需要与媒体框架集成,则必须通过调用media_entity_pads_init()来初始化嵌入在v4l2_subdev结构中的media_entity结构(实体字段),如果实体具有端口:
struct media_pad *pads = &my_sd->pads;
int err;
err = media_entity_pads_init(&sd->entity, npads, pads);
pads数组必须先前初始化过。无需手动设置struct media_entity函数和名称字段,但如果需要,则必须初始化revision字段。
当打开/关闭子设备设备节点(如果有)时,实体的引用将自动获取/释放。
在销毁子设备之前,不要忘记清理媒体实体:
media_entity_cleanup(&sd->entity);
如果子设备驱动程序实现了接收端口,子设备驱动程序可以设置v4l2_subdev_pad_ops中的link_validate字段,以提供自己的链接验证函数。对于管道中的每个链接,调用链接结束处的接收端口操作的link_validate pad操作。在这两种情况下,驱动程序仍需负责验证子设备和视频节点之间格式配置的正确性。
如果未设置link_validate op,则默认函数v4l2_subdev_link_validate_default()将代替。此函数确保链接源和接收端口上的宽度、高度和媒体总线像素代码相等。子设备驱动程序也可以自由使用此函数执行上述检查以及他们自己的检查。
2.2.7.1 Subdev registration
目前,有两种将子设备注册到V4L2 core的方法。第一种(传统的)可能性是由桥接驱动程序注册子设备。当桥接驱动程序具有连接到其上的子设备的完整信息并且知道何时注册它们时,可以执行此操作。这通常适用于SoC内部的视频数据处理单元或复杂的PCI(e)板、USB相机中的摄像头传感器或连接到SoC中的情况,它们向桥接驱动程序传递有关它们的信息,通常在其平台数据中。
但是,在某些情况下,子设备必须异步注册到桥接设备。这种配置的示例是基于设备树的系统,其中与子设备相关的信息独立于桥接设备提供系统,例如,在DT中将子设备定义为I2C设备节点时。在第二种情况下使用的API将在下面进一步描述。
使用任何一种注册方法都只会影响探测过程,两种情况下运行时桥接-子设备的交互方式是相同的。
在同步情况下,设备(桥接)驱动程序需要使用以下代码将`v4l2_subdev`注册到`v4l2_device`:
v4l2_device_register_subdev(v4l2_dev, sd)
如果在注册之前子设备模块消失,则此操作可能失败。成功调用此函数后,`subdev->dev`字段将指向`v4l2_device`。
如果`v4l2_device`父设备具有非空`mdev`字段,则子设备实体将自动在媒体设备中注册。
您可以使用以下代码取消注册子设备:
v4l2_device_unregister_subdev(sd)
取消注册后,子设备将从`v4l2_device`注销,`sd->dev`变为`NULL`。这意味着可以安全地卸载子设备模块。重要的是在卸载其模块之前正确注销子设备,以防止任何潜在的问题或冲突。
在异步情况下,子设备探测可以独立于桥接驱动的可用性进行调用。然后,子设备驱动程序必须验证是否满足成功探测的所有要求。其中可能包括检查主时钟是否可用。如果其中任何一个条件不满足,驱动程序可能会决定返回`-EPROBE_DEFER`,请求进一步的探测尝试。一旦满足所有条件,应使用`v4l2_async_register_subdev()`函数注册子设备。取消注册通过调用`v4l2_async_unregister_subdev()`完成。以这种方式注册的子设备将存储在全局子设备列表中,准备由桥接驱动程序选择。
桥接驱动程序必须注册通知对象。这是通过调用`v4l2_async_nf_register()`来完成的。取消注册通知程序需要调用`v4l2_async_nf_unregister()`。前面两个函数都采用两个参数:指向`struct v4l2_device`的指针和指向`struct v4l2_async_notifier`的指针。
在注册通知程序之前,桥接驱动程序必须完成两件事:首先,必须使用`v4l2_async_nf_init()`初始化通知程序。其次,桥接驱动程序可以开始形成子设备描述符列表,该列表是桥接设备运行所需的。根据设备类型和驱动程序的需求,可以使用多个函数将子设备描述符添加到通知程序中。
`v4l2_async_nf_add_fwnode_remote()`和`v4l2_async_nf_add_i2c()`用于桥接和ISP驱动程序,用于向通知程序注册其异步子设备。
`v4l2_async_register_subdev_sensor()`是一个助手函数,用于传感器驱动程序注册自己的异步子设备,但它还会注册通知程序,并为固件中找到的镜头和闪光灯设备进一步注册异步子设备。子设备的通知程序与异步子设备一起取消注册。
这些函数分配一个异步子设备描述符,其类型为嵌入到特定于驱动程序的结构体中的`struct v4l2_async_subdev`。结构体的`&struct v4l2_async_subdev`应该是该结构体的第一个成员:
struct my_async_subdev {
struct v4l2_async_subdev asd;
...
};
struct my_async_subdev *my_asd;
struct fwnode_handle *ep;
...
my_asd = v4l2_async_nf_add_fwnode_remote(¬ifier, ep,
struct my_async_subdev);
fwnode_handle_put(ep);
if (IS_ERR(asd))
return PTR_ERR(asd);
接下来,V4L2 core将使用这些描述符异步匹配已注册的子设备。如果检测到匹配,则会调用`.bound()`通知回调函数。在定位所有子设备之后,将调用`.complete()`回调函数。当从系统中删除子设备时,将调用`.unbind()`方法。这三个回调都是可选的。
2.2.7.2 Calling subdev operations
使用`v4l2_subdev`的优点是它是一个通用的结构体,不包含任何有关底层硬件的知识。因此,驱动程序可能包含几个使用I2C总线的子设备,但也可能包含通过GPIO引脚控制的子设备。这种区别只有在设置设备时才是相关的,但是一旦子设备被注册,它就是完全透明的。
一旦子设备已注册,您可以直接调用ops函数:
err = sd->ops->core->g_std(sd, &norm);
但最好最简单的方法是使用这个宏:
err = v4l2_subdev_call(sd, core, g_std, &norm);
该宏将执行正确的NULL指针检查,并返回-ENODEV(如果sd为空)、-ENOIOCTLCMD(如果sd->core或sd->core->g_std为空),或实际结果sd->ops->core->g_std ops。
还可以调用所有或子设备的子集:
v4l2_device_call_all(v4l2_dev, 0, core, g_std, &norm);
不支持此操作的任何子设备都将被跳过,并且错误结果将被忽略。如果要检查错误,请使用此代码:
err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_std, &norm);
除了-ENOIOCTLCMD之外的任何错误都会导致退出循环并显示该错误。如果没有错误(除了-ENOIOCTLCMD之外),则返回0。
这两个调用的第二个参数是一个组ID。如果是0,那么将调用所有子设备。如果非零,则只调用其组ID与该值匹配的子设备。在桥接驱动程序注册子设备之前,它可以将sd->grp_id设置为任何值(默认为0)。该值由桥接驱动程序拥有,子设备驱动程序永远不会修改或使用它。
组ID赋予了桥接驱动程序更多控制回调如何被调用的能力。例如,板子上可能有多个音频芯片,每个芯片都可以改变音量。但通常只有一个芯片会在用户要改变音量时实际使用。您可以将该子设备的组ID设置为例如AUDIO_CONTROLLER,并在调用v4l2_device_call_all()时指定该组ID值。这确保它只会去到需要它的子设备处。
如果子设备需要通知其v4l2_device父级发生的事件,则可以调用v4l2_subdev_notify(sd, notification, arg)。此宏会检查是否定义了notify()回调函数,如果没有,则返回-ENODEV。否则,返回notify()调用的结果。
2.2.8 V4L2 sub-device userspace API
传统上,桥接驱动程序向用户空间公开一个或多个视频节点,并根据视频节点操作响应中的v4l2_subdev_ops操作来控制子设备。这将底层硬件的复杂性隐藏在应用程序之后。对于复杂的设备,可能需要比视频节点提供的更细粒度的设备控制。在这些情况下,实现媒体控制器API的桥接驱动程序可能选择直接从用户空间访问子设备操作。
可以在/dev中创建名为v4l-subdevX的设备节点以直接访问子设备。如果子设备支持直接用户空间配置,则必须在注册之前设置V4L2_SUBDEV_FL_HAS_DEVNODE标志。
注册子设备后,v4l2_device驱动程序可以通过调用v4l2_device_register_subdev_nodes()为所有标记有V4L2_SUBDEV_FL_HAS_DEVNODE的已注册子设备创建设备节点。这些设备节点将在注销子设备时自动删除。
设备节点处理V4L2 API的子集:
VIDIOC_QUERYCTRL、VIDIOC_QUERYMENU、VIDIOC_G_CTRL、VIDIOC_S_CTRL、VIDIOC_G_EXT_CTRLS、VIDIOC_S_EXT_CTRLS和VIDIOC_TRY_EXT_CTRLS:这些控件IOCTL与V4L2中定义的控件相同。它们的行为完全相同,唯一的例外是它们仅处理在子设备中实现的控件。根据驱动程序,这些控件也可以通过一个(或多个)V4L2设备节点访问。
VIDIOC_DQEVENT、VIDIOC_SUBSCRIBE_EVENT和VIDIOC_UNSUBSCRIBE_EVENT: 事件IOCTL与V4L2中定义的事件相同。它们的行为完全相同,唯一的例外是它们仅处理由子设备生成的事件。根据驱动程序,这些事件也可以由一个(或多个)V4L2设备节点报告。
要使用事件的子设备驱动程序需要在注册子设备之前设置V4L2_SUBDEV_FL_HAS_EVENTS v4l2_subdev.flags。在注册后,事件可以像往常一样排队在v4l2_subdev.devnode设备节点上。为了正确支持事件,还实现了poll()文件操作。
私有ioctl:所有不在上述列表中的ioctl都将通过core::ioctl操作直接传递给子设备驱动程序。
2.2.9 Read-only sub-device userspace API
通过对内核API的直接调用来控制其连接的子设备的桥接驱动程序,实现了由v4l2_subdev_ops结构体实现。这种情况下,通常不希望用户空间能够通过子设备设备节点更改相同的参数,因此通常不会注册任何节点。
有时,通过一个只读的API向用户空间报告当前子设备配置是有用的,该API不允许应用程序更改设备参数,但允许通过子设备设备节点与之进行交互以进行检查。例如,为了实现基于计算摄影的相机,用户空间需要知道每种支持的输出分辨率的详细摄像机传感器配置(跳跃、截取和缩放)。为了支持这样的用例,桥接驱动程序可以通过只读API向用户空间公开子设备操作。
为所有使用V4L2_SUBDEV_FL_HAS_DEVNODE标记注册的子设备创建一个只读设备节点,v4l2_device驱动程序应调用v4l2_device_register_ro_subdev_nodes()。
在使用v4l2_device_register_ro_subdev_nodes()注册的子设备设备节点上,用户空间应用程序对以下ioctl的访问受到限制:
VIDIOC_SUBDEV_S_FMT、VIDIOC_SUBDEV_S_CROP、VIDIOC_SUBDEV_S_SELECTION: 仅允许在只读的子设备设备节点上针对V4L2_SUBDEV_FORMAT_TRY格式和选择矩形进行使用。
VIDIOC_SUBDEV_S_FRAME_INTERVAL、VIDIOC_SUBDEV_S_DV_TIMINGS、VIDIOC_SUBDEV_S_STD: 不允许在只读子设备节点上进行使用。
如果ioctl不被允许,或要修改的格式设置为V4L2_SUBDEV_FORMAT_ACTIVE,则核心将返回负错误代码,并将errno变量设置为-EPERM。
2.2.10 I2C sub-device drivers
由于这些驱动程序非常普遍,因此可以使用特殊的辅助函数来简化对这些驱动程序的使用(v4l2-common.h)。
将v4l2_subdev结构体嵌入为每个I2C设备实例创建的状态结构体中是将v4l2_subdev支持添加到I2C驱动程序中的推荐方法。非常简单的设备没有状态结构体,在这种情况下,可以直接创建一个v4l2_subdev。
典型的状态结构体如下(其中“chipname”被替换为芯片的名称):
struct chipname_state {
struct v4l2_subdev sd;
... /* additional state fields */
};
按照如下初始化v4l2_subdev:
v4l2_i2c_subdev_init(&state->sd, client, subdev_ops);
这个函数将填充v4l2_subdev的所有字段,并确保v4l2_subdev和i2c_client相互指向。您还应该添加一个帮助程序内联函数,以从v4l2_subdev指针转换为chip-name_state结构体:
static inline struct chipname_state *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct chipname_state, sd);
}
使用此函数从v4l2_subdev结构体转换为i2c_client结构体:
struct i2c_client *client = v4l2_get_subdevdata(sd);
使用此函数从i2c_client结构体转换为v4l2_subdev结构体:
struct v4l2_subdev *sd = i2c_get_clientdata(client);
在调用remove()回调函数时,请确保调用v4l2_device_unregister_subdev()(sd)。这将从桥接驱动程序中注销子设备。即使从未注册子设备,也可以安全地调用此函数。
需要这样做的原因是当桥接驱动程序销毁i2c适配器时,会调用该适配器上的i2c设备的remove()回调函数。之后,相应的v4l2_subdev结构将无效,因此必须首先取消注册它们。从remove()回调函数调用v4l2_device_unregister_subdev()(sd)可确保始终正确执行此操作。
桥接驱动程序还有一些可用的帮助函数:
struct v4l2_subdev *sd = v4l2_i2c_new_subdev(v4l2_dev, adapter,
"module_foo", "chipid", 0x36, NULL);
此函数加载给定的模块(如果不需要加载模块,则可以为NULL),并使用给定的i2c_adapter和芯片/地址参数调用i2c_new_client_device()。如果一切顺利,它将使用v4l2_device注册子设备。
您还可以使用v4l2_i2c_new_subdev()的最后一个参数传递可能要探测的I2C地址数组。仅当前一个参数为0时才使用这些探测地址。非零参数意味着您知道确切的i2c地址,因此在这种情况下不会进行探测。
两个函数如果有问题都将返回NULL。
请注意,您传递给v4l2_i2c_new_subdev()的chipid通常与模块名称相同。它允许您指定芯片变体,例如“saa7114”或“saa7115”。但是通常i2c驱动程序会自动检测此内容。芯片ID的使用需要在稍后更仔细地查看。它在i2c驱动程序之间存在差异,并且可能令人困惑。要查看支持哪些芯片变体,可以在i2c驱动程序代码中查找i2c_device_id表。该表列出了所有可能性。
还有一个辅助函数:
v4l2_i2c_new_subdev_board()使用传递给i2c驱动程序的i2c_board_info结构体,替换irq,platform_data和addr参数。如果子设备支持s_config core ops,则在设置好子设备后将使用irq和platform_data参数调用该操作。
v4l2_i2c_new_subdev()函数将调用v4l2_i2c_new_subdev_board(),在内部使用client_type和addr填充i2c_board_info结构体。
2.2.11 V4L2 sub-device functions and data structures
struct v4l2_decode_vbi_line
是用于解码VBI行的结构体。
定义:
struct v4l2_decode_vbi_line {
u32 is_second_field;
u8 *p;
u32 line;
u32 type;
};
它有以下成员:
is_second_field:在第一个(奇数)场景况下设置为0;在第二个(偶数)场景况下设置为1。
p:指向从解码器取出的分片VBI数据的指针。退出时,该指针指向有效载荷的起始位置。
line:分片VBI数据的行号(1-23)。
type:VBI服务类型(V4L2_SLICED_*)。如果没有发现服务,则为0。
enum v4l2_subdev_io_pin_bits
是用于子设备外部IO引脚配置的位常量。常量如下所示:
V4L2_SUBDEV_IO_PIN_DISABLE:禁用引脚配置。假定为启用状态。
V4L2_SUBDEV_IO_PIN_OUTPUT:如果引脚为输出,则设置此位。
V4L2_SUBDEV_IO_PIN_INPUT:如果引脚为输入,则设置此位。
V4L2_SUBDEV_IO_PIN_SET_VALUE:通过struct v4l2_subdev_io_pin_config->value设置输出值。
V4L2_SUBDEV_IO_PIN_ACTIVE_LOW:如果引脚为低电平有效,则将此位置1。否则,假定为高电平有效。
struct v4l2_subdev_io_pin_config
是用于子设备外部IO引脚配置的结构体。
定义:
struct v4l2_subdev_io_pin_config {
u32 flags;
u8 pin;
u8 function;
u8 value;
u8 strength;
};
它有以下成员:
flags:表示此引脚配置的标志位掩码,其位由v4l2_subdev_io_pin_bits枚举定义。
pin:要配置的芯片外部IO引脚。
function:要路由到IO引脚的内部信号pad/功能。
value:引脚的初始值 - 例如GPIO输出值。
strength:引脚驱动强度。
struct v4l2_subdev_core_ops
是用于子设备核心操作回调的结构体。
定义:
struct v4l2_subdev_core_ops {
int (*log_status)(struct v4l2_subdev *sd);
int (*s_io_pin_config)(struct v4l2_subdev *sd, size_t n, struct v4l2_subdev_io_pin_config *pincfg);
int (*init)(struct v4l2_subdev *sd, u32 val);
int (*load_fw)(struct v4l2_subdev *sd);
int (*reset)(struct v4l2_subdev *sd, u32 val);
int (*s_gpio)(struct v4l2_subdev *sd, u32 val);
long (*command)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
#ifdef CONFIG_COMPAT;
long (*compat_ioctl32)(struct v4l2_subdev *sd, unsigned int cmd, unsigned long arg);
#endif;
#ifdef CONFIG_VIDEO_ADV_DEBUG;
int (*g_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);
int (*s_register)(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg);
#endif;
int (*s_power)(struct v4l2_subdev *sd, int on);
int (*interrupt_service_routine)(struct v4l2_subdev *sd, u32 status, bool *handled);
int (*subscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub);
int (*unsubscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub);
};
它包含了多个函数指针,用于处理特定的操作:
log_status:处理VIDIOC_LOG_STATUS() ioctl的日志状态回调代码。
s_io_pin_config:配置一个或多个芯片的IO引脚。此函数需要一个指向‘n’个引脚配置条目的数组指针,每个引脚都需要一个配置条目。此函数可以在子设备初始化期间以外的时间被调用。
init:将传感器寄存器初始化为某种合理的默认值。不应该在新驱动程序中使用,并且应该在现有驱动程序中删除。
load_fw:加载固件。
reset:通用重置命令。参数选择要重置的子系统。传递0将始终重置整个芯片。不应在没有先在linux-media邮件列表上讨论的情况下在新驱动程序中使用。通常不需要重置设备。
s_gpio:设置GPIO引脚。目前非常简单,如果需要,可能需要扩展一个方向参数。
command:由内核驱动程序调用,以调用具有单独回调的subdev驱动程序内部功能。
ioctl:在V4L2 core的ioctl()系统调用处理程序结束时调用。用于支持驱动程序使用的私有IOCTL。
compat_ioctl32:当32位应用程序使用64位内核时,调用以修复从/向用户空间传递的数据。
g_register:处理VIDIOC_DBG_G_REGISTER() ioctl的回调代码。
s_register:处理VIDIOC_DBG_S_REGISTER() ioctl的回调代码。
s_power:将子设备置于省电模式(on == 0)或正常操作模式(on == 1)。
interrupt_service_routine:由桥接芯片的中断服务处理程序调用,当由于此subdev而引发中断状态时,以便此subdev可以处理详细信息。它可能安排稍后执行的工作。必须不休眠。从IRQ上下文调用。
subscribe_event:由驱动程序用于请求控制框架,在控制更改时给它发出警告。
unsubscribe_event:从控制框架中删除事件订阅。
struct v4l2_subdev_tuner_ops
是用于处理当V4L设备在收音机模式下打开时的回调函数集合。
定义:
struct v4l2_subdev_tuner_ops {
int (*standby)(struct v4l2_subdev *sd);
int (*s_radio)(struct v4l2_subdev *sd);
int (*s_frequency)(struct v4l2_subdev *sd, const struct v4l2_frequency *freq);
int (*g_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq);
int (*enum_freq_bands)(struct v4l2_subdev *sd, struct v4l2_frequency_band *band);
int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);
int (*s_tuner)(struct v4l2_subdev *sd, const struct v4l2_tuner *vt);
int (*g_modulator)(struct v4l2_subdev *sd, struct v4l2_modulator *vm);
int (*s_modulator)(struct v4l2_subdev *sd, const struct v4l2_modulator *vm);
int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type);
int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config);
};
它包括以下函数指针:
standby:将调谐器置于待机状态。它将在下一次使用时自动唤醒。
s_radio:切换调谐器到收音机模式。当一个调谐器操作需要在收音机模式下进行处理时,驱动程序应明确调用此函数。这通常在同时具有AM / FM收音机接收器和电视的设备上使用。
s_frequency:用于处理VIDIOC_S_FREQUENCY() ioctl处理程序代码的回调函数。
g_frequency:用于处理VIDIOC_G_FREQUENCY() ioctl处理程序代码的回调函数。freq->type必须填充。通常由video_ioctl2()或桥接驱动程序完成。
enum_freq_bands:用于处理VIDIOC_ENUM_FREQ_BANDS() ioctl处理程序代码的回调函数。
g_tuner:用于处理VIDIOC_G_TUNER() ioctl处理程序代码的回调函数。
s_tuner:用于处理VIDIOC_S_TUNER() ioctl处理程序代码的回调函数。vt->type必须填充。通常由video_ioctl2()或桥接驱动程序完成。
g_modulator:用于处理VIDIOC_G_MODULATOR() ioctl处理程序代码的回调函数。
s_modulator:用于处理VIDIOC_S_MODULATOR() ioctl处理程序代码的回调函数。
s_type_addr:设置调谐器类型及其I2C地址的回调函数。
s_config:设置tda9887特定内容的回调函数,如port1、port2和qss。
注意:对于同时具有AM / FM和电视功能的设备,驱动程序必须明确调用s_radio将调谐器切换到收音机模式,然后处理其他需要在该模式下进行处理的v4l2_subdev_tuner_ops函数。例如:
static void s_frequency(void *priv, const struct v4l2_frequency *f) {...if (f.type == V4L2_TUNER_RADIO)v4l2_device_call_all(v4l2_dev, 0, tuner, s_radio);...v4l2_device_call_all(v4l2_dev, 0, tuner, s_frequency);
}
struct v4l2_subdev_audio_ops
是一组用于处理与音频相关的设置的回调函数。
定义:
struct v4l2_subdev_audio_ops {
int (*s_clock_freq)(struct v4l2_subdev *sd, u32 freq);
int (*s_i2s_clock_freq)(struct v4l2_subdev *sd, u32 freq);
int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);
int (*s_stream)(struct v4l2_subdev *sd, int enable);
};
它包括以下函数指针:
s_clock_freq:设置音频时钟输出的频率(单位为Hz)。用于将音频处理器从视频解码器上同步,确保音频和视频保持同步。常见的频率值是48000、44100或32000 Hz。如果不支持该频率,则返回-EINVAL。
s_i2s_clock_freq:设置I2S速度(单位为bps)。这用于提供一种选择I2S时钟的标准方法,以驱动数字音频流。常见的频率值是1024000和2048000。如果不支持该频率,则返回-EINVAL。
s_routing:定义音频芯片的输入和/或输出引脚以及任何其他配置数据。永远不要在此级别使用用户级输入ID(例如复合、S-Video、调谐器)。一个i2c设备不应该知道一个输入引脚是否连接到复合连接器上,因为在另一个板子或平台上它可能连接到完全不同的东西。调用驱动程序负责将用户级输入映射到i2c设备上的正确引脚。
s_stream:用于通知音频代码流开始或停止。
enum v4l2_mbus_frame_desc_flags是用于描述媒体总线帧属性的位掩码。常见的位域包括:
V4L2_MBUS_FRAME_DESC_FL_LEN_MAX:指示struct v4l2_mbus_frame_desc_entry->length字段指定最大数据长度。
V4L2_MBUS_FRAME_DESC_FL_BLOB:指示格式没有行偏移,即接收器应使用1D DMA。
struct v4l2_mbus_frame_desc_entry
是用于描述媒体总线帧的结构体。
定义:
struct v4l2_mbus_frame_desc_entry {
enum v4l2_mbus_frame_desc_flags flags;
u32 pixelcode;
u32 length;
};
它包括以下成员:
flags:位掩码标志,在enum v4l2_mbus_frame_desc_flags中定义。
pixelcode:当未设置FRAME_DESC_FL_BLOB标志时,表示媒体总线像素代码的有效值。
length:当设置了V4L2_MBUS_FRAME_DESC_FL_LEN_MAX标志时,表示每帧的字节数。
struct v4l2_mbus_frame_desc
是用于描述媒体总线数据帧的结构体。
定义:
struct v4l2_mbus_frame_desc {
struct v4l2_mbus_frame_desc_entry entry[V4L2_FRAME_DESC_ENTRY_MAX];
unsigned short num_entries;
};
它包括以下成员:
entry:帧描述符数组。
num_entries:entry数组中的条目数。
enum v4l2_subdev_pre_streamon_flags是用于pre_streamon子设备核心操作的标志位。常见的标志位如下:
V4L2_SUBDEV_PRE_STREAMON_FL_MANUAL_LP:在调用s_stream()之前将发射器设置为LP-11或LP-111模式。
struct v4l2_subdev_video_ops
是在v4l设备以视频模式打开时使用的回调函数集合。
定义:
struct v4l2_subdev_video_ops {
int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);
int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags);
int (*g_std)(struct v4l2_subdev *sd, v4l2_std_id *norm);
int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm);
int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std);
int (*g_std_output)(struct v4l2_subdev *sd, v4l2_std_id *std);
int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std);
int (*g_tvnorms)(struct v4l2_subdev *sd, v4l2_std_id *std);
int (*g_tvnorms_output)(struct v4l2_subdev *sd, v4l2_std_id *std);
int (*g_input_status)(struct v4l2_subdev *sd, u32 *status);
int (*s_stream)(struct v4l2_subdev *sd, int enable);
int (*g_pixelaspect)(struct v4l2_subdev *sd, struct v4l2_fract *aspect);
int (*g_frame_interval)(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *interval);
int (*s_frame_interval)(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *interval);
int (*s_dv_timings)(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings);
int (*g_dv_timings)(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings);
int (*query_dv_timings)(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings);
int (*s_rx_buffer)(struct v4l2_subdev *sd, void *buf, unsigned int *size);
int (*pre_streamon)(struct v4l2_subdev *sd, u32 flags);
int (*post_streamoff)(struct v4l2_subdev *sd);
};
它包括以下成员:
s_routing:与audio_ops中的s_routing相似,但此版本用于视频设备。
s_crystal_freq:设置以Hz为单位的用于生成时钟的晶体频率。额外的flags字段允许设备特定的配置,如时钟频率分频器等。如果未使用,则将flags设置为0。如果不支持该频率,则返回-EINVAL。
g_std:用于VIDIOC_G_STD() ioctl处理器代码的回调函数。
s_std:用于VIDIOC_S_STD() ioctl处理器代码的回调函数。
s_std_output:为视频输出设备设置v4l2_std_id。对于视频输入设备,这将被忽略。
g_std_output:获取视频输出设备的当前标准。对于视频输入设备,这将被忽略。
querystd:用于VIDIOC_QUERYSTD() ioctl处理器代码的回调函数。
g_tvnorms:获取由视频CAPTURE设备支持的所有标准的v4l2_std_id。对于视频输出设备,这将被忽略。
g_tvnorms_output:获取由视频输出设备支持的所有标准的v4l2_std_id。对于视频捕获设备,这将被忽略。
g_input_status:获取输入状态。与struct v4l2_input中的status字段相同。
s_stream:用于通知驱动程序视频流将要开始或已经停止。
g_pixelaspect:返回像素宽高比的回调函数。
g_frame_interval:用于VIDIOC_SUBDEV_G_FRAME_INTERVAL() ioctl处理器代码的回调函数。
s_frame_interval:用于VIDIOC_SUBDEV_S_FRAME_INTERVAL() ioctl处理器代码的回调函数。
s_dv_timings:在子设备中设置自定义dv时序。当子设备能够在硬件中设置详细的时序信息以生成/检测视频信号时,使用此功能。
g_dv_timings:获取子设备中自定义dv时序。
query_dv_timings:用于VIDIOC_QUERY_DV_TIMINGS() ioctl处理器代码的回调函数。
s_rx_buffer:为子设备设置一个主机分配的内存缓冲区。子设备可以将大小调整为较低的值,并且不能从data开始写入超过size的数据。
pre_streamon:可能会在实际开始流之前调用,以帮助初始化总线。当前使用方法是在流之前将CSI-2发射机设置为LP-11或LP-111模式。参见enum v4l2_subdev_pre_streamon_flags。
pre_streamon应返回错误,如果不能执行由flags参数指示的操作,则返回-EACCES。调用者应为每个成功调用pre_streamon调用post_streamoff。
post_streamoff:在停止流时调用,但仅在之前调用了pre_streamon时才调用。
struct v4l2_subdev_vbi_ops
是在v4l设备以sliced VBI模式打开时使用的回调函数集合。
定义:
struct v4l2_subdev_vbi_ops {
int (*decode_vbi_line)(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi_line);
int (*s_vbi_data)(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *vbi_data);
int (*g_vbi_data)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *vbi_data);
int (*g_sliced_vbi_cap)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap);
int (*s_raw_fmt)(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt);
int (*g_sliced_fmt)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
int (*s_sliced_fmt)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
};
它包括以下成员:
decode_vbi_line:视频解码器需要支持sliced VBI并实现此ioctl。struct v4l2_decode_vbi_line中的p字段设置为解码器生成的VBI数据的起始位置。然后驱动程序会解析sliced VBI数据并相应地设置struct中的其他字段。指针p更新为指向可以逐字复制到struct v4l2_sliced_vbi_data的data字段中的payload的开始位置。如果未找到有效的VBI数据,则返回时type字段设置为0。
s_vbi_data:用于在视频信号上生成VBI信号。使用struct v4l2_sliced_vbi_data填充数据包。请注意,如果将line字段设置为0,则禁用该VBI信号。如果未找到有效的VBI数据,则返回时type字段设置为0。
g_vbi_data:用于从读回寄存器中获取sliced VBI分组。并非所有视频解码器都支持此功能。如果没有可用数据,因为读回寄存器包含无效或错误的数据,则返回-EIO。请注意,您必须填写'id'成员和'field'成员(以确定是从第一场还是第二场CC数据)。
g_sliced_vbi_cap:用于VIDIOC_G_SLICED_VBI_CAP() ioctl处理器代码的回调函数。
s_raw_fmt:设置视频编码器/解码器以使用原始VBI。
g_sliced_fmt:检索当前的sliced VBI设置。
s_sliced_fmt:设置sliced VBI设置。
struct v4l2_subdev_sensor_ops
是v4l2子设备中的传感器操作集合。
定义:
struct v4l2_subdev_sensor_ops {
int (*g_skip_top_lines)(struct v4l2_subdev *sd, u32 *lines);
int (*g_skip_frames)(struct v4l2_subdev *sd, u32 *frames);
};
它包括以下成员:
g_skip_top_lines:需要跳过图像顶部几行的行数。这对于某些传感器是必需的,因为它们总是会破坏输出图像的若干顶部行,或是在这些行中发送元数据。
g_skip_frames:需要跳过的流开始处的帧数。这对于有缺陷的传感器是必需的,因为当它们被打开时会生成错误的帧。
enum v4l2_subdev_ir_mode 描述了支持的红外类型。
常量
V4L2_SUBDEV_IR_MODE_PULSE_WIDTH表示红外使用struct ir_raw_event记录。
struct v4l2_subdev_ir_parameters
是用于IR传输或接收的参数集合。
定义:
struct v4l2_subdev_ir_parameters {
unsigned int bytes_per_data_element;
enum v4l2_subdev_ir_mode mode;
bool enable;
bool interrupt_enable;
bool shutdown;
bool modulation;
u32 max_pulse_width;
unsigned int carrier_freq;
unsigned int duty_cycle;
bool invert_level;
bool invert_carrier_sense;
u32 noise_filter_min_width;
unsigned int carrier_range_lower;
unsigned int carrier_range_upper;
u32 resolution;
};
它包括以下成员:
bytes_per_data_element:每个数据元素中的字节数,在读取或写入调用中使用。
mode:由枚举类型v4l2_subdev_ir_mode定义的IR模式。
enable:如果为true,则设备处于活动状态。
interrupt_enable:如果为true,则启用IR中断。
shutdown:如果为true,则将硬件设置为低功率或无功率状态,如果为false,则为正常模式。
modulation:如果为true,则使用载波;如果为false,则使用基带信号。
max_pulse_width:基带信号的最大脉冲宽度,仅对基带信号有效。
carrier_freq:调制信号的载波频率,仅对调制信号有效。
duty_cycle:占空比百分比,仅对调制信号有效。
invert_level:反转信号电平。
invert_carrier_sense:仅在TX中使用,将0或空格作为载波突发发送。
noise_filter_min_width:有效脉冲的最小时间,以纳秒为单位。仅用于RX。
carrier_range_lower:下限载波范围,以Hz为单位,仅对调制信号有效。仅用于RX。
carrier_range_upper:上限载波范围,以Hz为单位,仅对调制信号有效。仅用于RX。
resolution:以ns为单位的接收分辨率。仅用于RX。
struct v4l2_subdev_ir_ops
该结构体定义了IR子备操作。包括接收端和发送端的操作。
定义:
struct v4l2_subdev_ir_ops {
int (*rx_read)(struct v4l2_subdev *sd, u8 *buf, size_t count, ssize_t *num);
int (*rx_g_parameters)(struct v4l2_subdev *sd, struct v4l2_subdev_ir_parameters *params);
int (*rx_s_parameters)(struct v4l2_subdev *sd, struct v4l2_subdev_ir_parameters *params);
int (*tx_write)(struct v4l2_subdev *sd, u8 *buf, size_t count, ssize_t *num);
int (*tx_g_parameters)(struct v4l2_subdev *sd, struct v4l2_subdev_ir_parameters *params);
int (*tx_s_parameters)(struct v4l2_subdev *sd, struct v4l2_subdev_ir_parameters *params);
};
具体包括以下成员:
1. rx_read函数,用于读取接收到的红外代码或脉冲宽度数据,类似于非阻塞的read()函数。
2. rx_g_parameters函数,用于获取IR接收器的当前工作参数和状态。
3. rx_s_parameters函数,用于设置IR接收器的当前工作参数和状态。建议先调用rx_g_parameters函数以填充当前状态,然后仅更改需要更改的字段。返回时,实际设备工作参数和状态将被返回。请注意,由于硬件限制,实际设置可能与请求设置不匹配,例如当请求36,000 Hz时,实际载波设置为35,904 Hz。当shutdown参数为true时,情况有所不同。将返回上次使用的操作参数,但实际硬件状态可能有所不同,以最小化功耗和处理。
4. tx_write函数,用于写入要传输的代码或脉冲宽度数据,类似于非阻塞的write()函数。
5. tx_g_parameters函数,用于获取IR发送器的当前工作参数和状态。
6. tx_s_parameters函数,用于设置IR发送器的当前工作参数和状态。建议先调用tx_g_parameters函数以填充当前状态,然后仅更改需要更改的字段。返回时,实际设备工作参数和状态将被返回。请注意,由于硬件限制,实际设置可能与请求设置不匹配,例如当请求36,000 Hz时,实际载波设置为35,904 Hz。当shutdown参数为true时,情况有所不同。将返回上次使用的操作参数,但实际硬件状态可能有所不同,以最小化功耗和处理。
struct v4l2_subdev_pad_config
该结构体用于存储子设备的pad信息,包含以下成员:
定义:
struct v4l2_subdev_pad_config {
struct v4l2_mbus_framefmt try_fmt;
struct v4l2_rect try_crop;
struct v4l2_rect try_compose;
};
1. try_fmt,结构体类型为struct v4l2_mbus_framefmt,表示尝试使用的格式
2. try_crop,结构体类型为struct v4l2_rect,表示要用于剪裁的矩形区域
3. try_compose,结构体类型为struct v4l2_rect,表示要用于合成的矩形区域
如果主参数的'which'字段设置为V4L2_SUBDEV_FORMAT_TRY,则只需将此结构体传递给pad操作。对于V4L2_SUBDEV_FORMAT_ACTIVE,可以安全地传递NULL。
struct v4l2_subdev_state
该结构体用于存储子设备的状态信息
定义:
struct v4l2_subdev_state {
struct v4l2_subdev_pad_config *pads;
};
包含以下成员:
1. pads,指向v4l2_subdev_pad_config类型数组的指针。
如果主参数的'which'字段设置为V4L2_SUBDEV_FORMAT_TRY,则只需将此结构体传递给pad操作。对于V4L2_SUBDEV_FORMAT_ACTIVE,可以安全地传递NULL。
struct v4l2_subdev_pad_ops
为v4l2子设备的pad级别操作定义了一组回调函数
定义:
struct v4l2_subdev_pad_ops {
int (*init_cfg)(struct v4l2_subdev *sd, struct v4l2_subdev_state *state);
int (*enum_mbus_code)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code);
int (*enum_frame_size)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_frame_size_enum *fse);
int (*enum_frame_interval)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_frame_interval_enum *fie);
int (*get_fmt)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_format *format);
int (*set_fmt)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_format *format);
int (*get_selection)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel);
int (*set_selection)(struct v4l2_subdev *sd,struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel);
int (*get_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
int (*set_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
int (*dv_timings_cap)(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap);
int (*enum_dv_timings)(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings);
#ifdef CONFIG_MEDIA_CONTROLLER;
int (*link_validate)(struct v4l2_subdev *sd, struct media_link *link,struct v4l2 subdev_format *source_fmt, struct v4l2_subdev_format *sink_fmt);
#endif ;
int (*get_frame_desc)(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *fd);
int (*set_frame_desc)(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_frame_desc *fd);
int (*get_mbus_config)(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_config *config);
int (*set_mbus_config)(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_config *config);
};
包括:
1. init_cfg函数,用于将pad配置初始化为默认值;
2. enum_mbus_code函数,作为VIDIOC_SUBDEV_ENUM_MBUS_CODE() ioctl handler code的回调函数;
3. enum_frame_size函数,作为VIDIOC_SUBDEV_ENUM_FRAME_SIZE() ioctl handler code的回调函数;
4. enum_frame_interval函数,作为VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL() ioctl handler code的回调函数;
5. get_fmt函数,作为VIDIOC_SUBDEV_G_FMT() ioctl handler code的回调函数;
6. set_fmt函数,作为VIDIOC_SUBDEV_S_FMT() ioctl handler code的回调函数;
7. get_selection函数,作为VIDIOC_SUBDEV_G_SELECTION() ioctl handler code的回调函数;
8. set_selection函数,作为VIDIOC_SUBDEV_S_SELECTION() ioctl handler code的回调函数;
9. get_edid函数,作为VIDIOC_SUBDEV_G_EDID() ioctl handler code的回调函数;
10. set_edid函数,作为VIDIOC_SUBDEV_S_EDID() ioctl handler code的回调函数;
11. dv_timings_cap函数,作为VIDIOC_SUBDEV_DV_TIMINGS_CAP() ioctl handler code的回调函数;
12. enum_dv_timings函数,作为VIDIOC_SUBDEV_ENUM_DV_TIMINGS() ioctl handler code的回调函数;
13. link_validate函数,由媒体控制器代码使用,以检查属于管道的链接是否可用于流;
14. get_frame_desc函数,用于获取当前的低级媒体总线帧参数;
15. set_frame_desc函数,用于设置低级媒体总线帧参数,子设备驱动程序可以调整fd数组以适应设备能力;
16. get_mbus_config函数,用于获取远程子设备的媒体总线配置。媒体总线配置通常在子设备探测时从固件接口中检索,立即应用于硬件,并最终由驱动程序调整。远程子设备(通常是视频接收器)应使用此操作查询传输端总线配置,以相应地调整其自己的总线配置。调用者应确保他们从远程端获取了尽可能最新的配置,可能尽可能接近流媒体时间调用此操作。如果已调用的pad索引无效或发生不可恢复的故障,则操作将失败。
17. set_mbus_config函数,用于设置远程子设备的媒体总线配置。该操作旨在允许通过get_mbus_config操作协商媒体总线配置参数。如果所请求的配置不受支持,则操作不会失败,但驱动程序将更新config参数的内容以反映已实际应用于硬件的内容。如果已调用的pad索引无效或发生不可恢复的故障,则操作将失败。
struct v4l2_subdev_ops
定义了v4l2子设备驱动程序需要实现的一组操作。
定义:
struct v4l2_subdev_ops {
const struct v4l2_subdev_core_ops *core;
const struct v4l2_subdev_tuner_ops *tuner;
const struct v4l2_subdev_audio_ops *audio;
const struct v4l2_subdev_video_ops *video;
const struct v4l2_subdev_vbi_ops *vbi;
const struct v4l2_subdev_ir_ops *ir;
const struct v4l2_subdev_sensor_ops *sensor;
const struct v4l2_subdev_pad_ops *pad;
};
这些操作可分为以下几个部分:
1. core:包含一组核心的回调函数,用于v4l2子设备的基本操作,例如在打开/关闭设备、查询设备信息、模块插入/拔出时调用。
2. tuner:包含一组回调函数,用于操作Tuner模块,例如调整频道、改变调谐器的频率范围等操作。
3. audio:包含一组回调函数,用于操作音频模块,例如设置音量、选择输入源等操作。
4. video:包含一组回调函数,用于操作视频模块,例如捕获视频帧、设置像素格式、调整曝光、白平衡等操作。
5. vbi:包含一组回调函数,用于操作VBI(垂直消隐)数据,例如捕获VBI数据、设置数据格式等操作。
6. ir:包含一组回调函数,用于操作红外模块,例如发送/接收红外信号等操作。
7. sensor:包含一组回调函数,用于与图像传感器交互,例如配置传感器参数、启动/停止采集等操作。
8. pad:包含一组回调函数,用于处理不同pad之间的数据流转,例如获取/设置帧格式、获取/设置数据选择等操作。
这些操作函数的实现由子设备驱动程序负责,以完成对应的功能。在初始化v4l2子设备时,可以选择仅实现部分操作,从而适应不同的应用场景。
struct v4l2_subdev_internal_ops
定义了v4l2子设备驱动程序内部使用的一组操作,这些操作函数只能由v4l2框架调用,驱动程序不能直接调用。
定义:
struct v4l2_subdev_internal_ops {
int (*registered)(struct v4l2_subdev *sd);
void (*unregistered)(struct v4l2_subdev *sd);
int (*open)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
int (*close)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
void (*release)(struct v4l2_subdev *sd);
};
1. registered:当子设备被注册到V4L2系统中时,会调用这个函数。在这里完成对子设备结构体(v4l2_subdev)的初始化工作,例如申请内存等。
2. unregistered:在子设备从V4L2系统中注销时,会调用这个函数。在这里进行清理工作,例如释放资源、回收内存等。
3. open:当用户打开设备文件时,会调用这个函数。在这里进行相关的初始化工作,例如设置寄存器、启动数据流等。
4. close:当用户关闭设备文件时,会调用这个函数。在这里进行相关的清理工作,例如停止数据流、关闭中断等。
5. release:当最后一个用户关闭设备文件后,会调用这个函数。在这里进行相关的清理工作,并释放子设备的资源,例如回收内存、关闭相关外设等。
需要注意的是,驱动程序不能直接调用这些操作函数,它们仅供v4l2框架使用,并且需要在子设备的ops结构体中进行注册。驱动程序可以根据需要选择实现这些操作中的部分或全部,以满足不同的应用场景。
struct v4l2_subdev_platform_data
定义了子设备与平台(host device)通信时使用的一些数据和配置信息。
定义:
struct v4l2_subdev_platform_data {
struct regulator_bulk_data *regulators;
int num_regulators;
void *host_priv;
};
1. regulators:用于控制子设备电源的电压、电流,从而实现开关子设备的操作。这是一个可选参数,通常由平台设备提供支持。
2. num_regulators:regulators数组中元素的个数,即需要控制的电源数量。
3. host_priv:用于保存与子设备相关的平台特定数据,例如平台设备的配置寄存器地址等。
这些数据和配置信息通常由平台驱动程序提供给子设备驱动程序。子设备驱动程序可以在初始化过程中读取这些信息,并据此对子设备进行初始化、配置等操作。其中,regulators是一个非常重要的参数,主要用于控制子设备的电源,以确保能够正常工作。
struct v4l2_subdev
定义了V4L2子设备的通用结构体,包含了子设备的各种属性和信息。
定义:
struct v4l2_subdev {
#if defined(CONFIG_MEDIA_CONTROLLER);
struct media_entity entity;
#endif;
struct list_head list;
struct module *owner;
bool owner_v4l2_dev;
u32 flags;
struct v4l2_device *v4l2_dev;
const struct v4l2_subdev_ops *ops;
const struct v4l2_subdev_internal_ops *internal_ops;
struct v4l2_ctrl_handler *ctrl_handler;
char name[V4L2_SUBDEV_NAME_SIZE];
u32 grp_id;
void *dev_priv;
void *host_priv;
struct video_device *devnode;
struct device *dev;
struct fwnode_handle *fwnode;
struct list_head async_list;
struct v4l2_async_subdev *asd;
struct v4l2_async_notifier *notifier;
struct v4l2_async_notifier *subdev_notifier;
struct v4l2_subdev_platform_data *pdata;
};
1. entity:指向struct media_entity结构体的指针,描述了该子设备在媒体设备拓扑中的位置和连接方式。
2. owner:指向struct device_driver结构体中的owner成员,表示该子设备的驱动程序所属的模块。
3. flags:子设备的标志位,用于指示该子设备的类型、特性等信息。例如,是否为I2C或SPI设备,是否需要创建设备节点等。
4. v4l2_dev:指向struct v4l2_device结构体的指针,表示该子设备所属的v4l2设备。
5. ops:指向struct v4l2_subdev_ops结构体的指针,表示该子设备支持的操作函数集合。
6. internal_ops:指向struct v4l2_subdev_internal_ops结构体的指针,包含了驱动程序内部使用的一组操作函数。
7. ctrl_handler:指向struct v4l2_ctrl_handler结构体的指针,用于控制子设备的各种参数和属性。
8. name:子设备的名字,必须是唯一的。
9. grp_id:可以用于将多个类似的子设备进行分组,具体值由驱动程序决定。
10. dev_priv:指向私有数据的指针,用于存储子设备的相关属性和状态。
11. host_priv:指向私有数据的指针,与平台相关,用于描述该子设备在平台中的位置和连接方式。
12. devnode:指向子设备节点的指针,用于创建和访问设备节点文件。
13. dev:指向物理设备的指针,如果该子设备是直接连接到物理设备上的,则指向该物理设备。
14. fwnode:指向fwnode_handle结构体的指针,表示该子设备在平台设备树中的节点。
15. async_list:链接到全局子设备列表或notifier->done列表的子设备列表。
16. asd:指向struct v4l2_async_subdev结构体的指针,用于表示该子设备在异步子设备链表中的位置。
17. notifier:指向struct v4l2_async_notifier结构体的指针,用于管理和通知异步事件的处理。
18. subdev_notifier:指向struct v4l2_subdev_notifier结构体的指针,表示为该子设备隐式注册的子设备notifier。
一般情况下,子设备驱动程序应该使用v4l2_subdev_init()函数或其变种进行初始化。
media_entity_to_v4l2_subdev(ent)
函数返回包含在struct media_entity结构体中的struct v4l2_subdev结构体。
参数ent是指向struct media_entity结构体的指针。
该函数的作用是通过指向media_entity结构体的指针,获取到media_entity所嵌套的v4l2_subdev结构体的指针。这个函数在底层框架中被广泛使用,用于将媒体实体和V4L2子设备关联起来。
vdev_to_v4l2_subdev(vdev)
函数返回包含在struct video_device结构体中的struct v4l2_subdev结构体。
参数vdev是指向struct video_device结构体的指针。
该函数的作用是通过指向video_device结构体的指针,获取到video_device所嵌套的v4l2_subdev结构体的指针。这个函数在Video4Linux框架中被广泛使用,用于将视频设备节点和V4L2子设备关联起来。
struct v4l2_subdev_fh
结构体用于存储每个文件句柄所对应的V4L2子设备信息。
定义:
struct v4l2_subdev_fh {
struct v4l2_fh vfh;
struct module *owner;
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API);
struct v4l2_subdev_state *state;
#endif;
};
该结构体包含以下成员:
1. vfh:指向struct v4l2_fh结构体的指针,表示该文件句柄相关的通用V4L2信息。
2. owner:指向struct module结构体的指针,表示该文件句柄所属的内核模块。
3. state:指向struct v4l2_subdev_state结构体的指针,表示该文件句柄所对应的V4L2子设备状态。这个成员只有在CONFIG_VIDEO_V4L2_SUBDEV_API宏被定义时才可用。
v4l2_subdev_fh结构体被用于管理V4L2子设备与进程之间的绑定关系,通过该结构体中的文件句柄信息,V4L2框架可以准确地知道哪些进程正在访问该子设备,并且可以保证文件操作的互斥性。
to_v4l2_subdev_fh(fh)
函数从嵌套在其中的struct v4l2_fh中返回一个struct v4l2_subdev_fh结构体。
参数
fh:指向struct v4l2_fh结构体的指针。
v4l2_subdev_get_try_format()
函数是一个V4L2子设备驱动中的辅助函数,用于调用struct v4l2_subdev_pad_config->try_fmt函数来获取指定pad的当前尝试格式。该函数返回指向struct v4l2_mbus_framefmt结构体的指针,表示该pad当前正在尝试的格式。
函数参数包括:
1. struct v4l2_subdev *sd:指向要查询的V4L2子设备的struct v4l2_subdev结构体的指针。
2. struct v4l2_subdev_state *state:指向struct v4l2_subdev_state结构体的指针,表示要查询的V4L2子设备状态。
3. unsigned int pad:表示要查询的pad在struct v4l2_subdev_state->pads数组中的索引号。
该函数将会调用struct v4l2_subdev_pad_config结构体中的try_fmt函数,尝试获取该pad的当前尝试格式。该函数是V4L2子设备驱动开发中常用的辅助函数之一,可以让驱动开发人员快速方便地获取设备某个pad当前正在尝试的格式。
v4l2_subdev_get_try_crop
功能描述:这是一个附加函数,调用它将调用“struct v4l2_subdev_pad_config->try_crop”,用于获取与指定的子设备、子设备状态和索引相关联的pad的crop信息。
参数说明:
struct v4l2_subdev *sd,指向struct v4l2_subdev的指针。
struct v4l2_subdev_state *state,指向struct v4l2_subdev_state的指针。
unsigned int pad,指定与所需的crop信息相关联的pad在struct v4l2_subdev_state->pads数组中的索引。
返回值:返回指向包含有关特定pad的crop信息的struct v4l2_rect结构体的指针。
v4l2_subdev_get_try_compose
是一个辅助函数,用于调用`struct v4l2_subdev_pad_config`中的`try_compose`函数。该函数的参数如下:
- `struct v4l2_subdev *sd`:指向需要进行配置的V4L2子设备的指针。
- `struct v4l2_subdev_state *state`:指向V4L2子设备状态结构体的指针。
- `unsigned int pad`:指定要对其进行配置的V4L2子设备的端口的索引。
该函数返回一个指向`v4l2_rect`结构体的指针,表示尝试配置后的子设备裁剪矩形。这个矩形指定了数据流将从视频缓冲区中哪个区域传输。
`v4l2_subdev_get_try_compose`函数不是V4L2 API的公共部分,而是由V4L2子设备驱动程序实现的私有函数。
v4l2_set_subdevdata
是用于设置V4L2子设备的私有数据指针的函数。该函数的参数如下:
- `struct v4l2_subdev *sd`:指向要设置私有数据的V4L2子设备结构体的指针。
- `void *p`:指向要存储的私有设备数据的指针。
该函数允许驱动程序将一个指向私有数据的指针附加到给定的V4L2子设备结构体上。这个指针可以在后续的操作中作为一个“句柄”使用。
例如,当应用程序调用V4L2 API的其他函数时,可以将`struct v4l2_subdev`结构体作为参数传递。驱动程序可以使用存储在`v4l2_subdev`结构体中的私有数据指针来访问与该子设备相关的其他数据。
需要注意的是,`v4l2_set_subdevdata`函数不是V4L2 API的公共部分,而是由V4L2子设备驱动程序实现的私有函数。
v4l2_get_subdevdata
是一个用于获取V4L2子设备私有数据的函数。该函数的参数如下:
- `const struct v4l2_subdev *sd`:指向要获取私有数据的V4L2子设备结构体的常量指针。
该函数返回存储在给定V4L2子设备结构体上的私有数据指针。这个指针可以作为“句柄”使用,以便驱动程序访问与该子设备相关的其他数据。
`v4l2_get_subdevdata`函数允许应用程序检索存储在子设备结构体中的指针。这样,应用程序就可以获得驱动程序内存中的重要信息并进行相关操作。
需要注意的是,`v4l2_get_subdevdata`函数不是V4L2 API的公共部分,而是由V4L2子设备驱动程序实现的私有函数。
v4l2_set_subdev_hostdata
是一个用于设置V4L2子设备主机私有数据的函数。该函数的参数如下:
- `struct v4l2_subdev *sd`:指向要设置主机私有数据的V4L2子设备结构体的指针。
- `void *p`:指向要存储的私有主机数据的指针。
该函数允许主机驱动程序将一个指向主机私有数据的指针附加到给定的V4L2子设备结构体上。这个指针可以在后续的操作中作为一个“句柄”使用。
例如,当应用程序调用V4L2 API的其他函数时,可以将`struct v4l2_subdev`结构体作为参数传递。驱动程序可以使用存储在`v4l2_subdev`结构体中的主机私有数据指针来访问与该子设备相关的其他数据。
需要注意的是,`v4l2_set_subdev_hostdata`函数不是V4L2 API的公共部分,而是由V4L2主机驱动程序实现的私有函数。
v4l2_get_subdev_hostdata
是一个用于获取V4L2子设备主机私有数据的函数。该函数的参数如下:
- `const struct v4l2_subdev *sd`:指向要获取主机私有数据的V4L2子设备结构体的常量指针。
该函数返回存储在给定V4L2子设备结构体上的主机私有数据指针。这个指针可以作为“句柄”使用,以便驱动程序访问与该子设备相关的其他数据。
`v4l2_get_subdev_hostdata`函数允许应用程序检索存储在子设备结构体中的指针。这样,应用程序就可以获得驱动程序内存中的重要信息并进行相关操作。
需要注意的是,`v4l2_get_subdev_hostdata`函数不是V4L2 API的公共部分,而是由V4L2主机驱动程序实现的私有函数。
v4l2_subdev_get_fwnode_pad_1_to_1
是一个用于从V4L2子设备的fwnode endpoint中获取pad number的函数,假设该子设备使用1:1的端口和pad映射关系。该函数的参数如下:
- `struct media_entity *entity`:指向要获取pad number的V4L2子设备的媒体实体结构体的指针。
- `struct fwnode_endpoint *endpoint`:指向已解析的fwnode endpoint的指针。
该函数可以作为子设备的`.get_fwnode_pad`操作使用,当子设备使用1:1映射关系并拥有该endpoint时,该函数将返回endpoint的端口号。
需要注意的是,如果子设备不使用1:1的映射关系或者不拥有该endpoint,该函数将返回负值错误代码。
v4l2_subdev_link_validate_default
是一个用于验证媒体连接的函数。该函数的参数如下:
- `struct v4l2_subdev *sd`:指向要验证连接的V4L2子设备结构体的指针。
- `struct media_link *link`:指向要验证的媒体连接的结构体的指针。
- `struct v4l2_subdev_format *source_fmt`:指向源端格式信息的`v4l2_subdev_format`结构体的指针。
- `struct v4l2_subdev_format *sink_fmt`:指向目标端格式信息的`v4l2_subdev_format`结构体的指针。
该函数确保连接的源端和目标端在宽度、高度和媒体总线像素编码方面是相等的。
需要注意的是,该函数的具体实现可能因驱动程序而异,也可以被覆盖或扩展来实现特定的连接验证逻辑。
v4l2_subdev_link_validate
是一个用于验证媒体连接的函数。该函数的参数如下:
- `struct media_link *link`:指向要验证的媒体连接的结构体的指针。
该函数将调用子设备的`link_validate`操作来验证连接是否适合流式传输。它还在内部调用`v4l2_subdev_link_validate_default`函数以确保连接的源端和目标端在宽度、高度和媒体总线像素编码方面是相等的。
需要注意的是,该函数是V4L2框架中的公共函数,其默认实现将考虑子设备所提供的验证逻辑,也可以被子设备驱动程序覆盖或扩展为特定的连接验证逻辑。
v4l2_subdev_alloc_state
是一个用于分配V4L2子设备状态的函数。该函数的参数如下:
- `struct v4l2_subdev *sd`:指向要为其分配状态的V4L2子设备结构体的指针。
该函数将分配一个新的`v4l2_subdev_state`结构体,用于存储V4L2子设备的状态信息,并将其返回。在使用完毕后,调用`v4l2_subdev_free_state`将释放该状态结构体。
需要注意的是,该函数可用于需要保存V4L2子设备当前状态信息的驱动程序中。通过`v4l2_subdev_get_state`和`v4l2_subdev_set_state`操作,可以获取/设置状态结构体的值。
v4l2_subdev_free_state
是一个用于释放V4L2子设备状态的函数。该函数的参数如下:
- `struct v4l2_subdev_state *state`:指向要释放的`v4l2_subdev_state`结构体的指针。
该函数将释放之前通过`v4l2_subdev_alloc_state`函数分配的`v4l2_subdev_state`结构体所占用的内存空间。
需要注意的是,当不再需要该状态结构体时,必须调用此函数手动释放该结构体。
v4l2_subdev_init
用于初始化V4L2子设备结构体。该函数的参数如下:
- `struct v4l2_subdev *sd`:指向要初始化的V4L2子设备结构体的指针。
- `const struct v4l2_subdev_ops *ops`:指向实现V4L2子设备操作的结构体的指针。
该函数将使用给定的`v4l2_subdev_ops`结构体填充`v4l2_subdev`结构体中的`ops`字段,以便V4L2框架可以调用驱动程序提供的V4L2子设备操作。此外,该函数还会初始化子设备的一些默认值,例如`flags`和`grp_id`等。
需要注意的是,该函数应该在V4L2子设备实例被创建后尽早调用,以确保正确的初始化。
v4l2_subdev_call
用于调用V4L2子设备结构体中的回调函数,从而实现对子设备的访问。该函数的参数如下:
- `struct v4l2_subdev *sd`:指向要访问的V4L2子设备结构体的指针。
- `const struct v4l2_subdev_ops *o`:指向实现V4L2子设备操作的结构体的指针。
- `int f`:要调用的回调函数的索引。
- `…args`:传递给回调函数的参数列表。
该函数使用给定的`v4l2_subdev_ops`结构体和索引`f`来查找要调用的回调函数。然后,它将使用传递的参数列表调用所找到的回调函数。
需要注意的是,该函数应该由V4L2驱动程序内部使用。对于普通用户空间应用程序,通常可以使用更高级别的V4L2 API,例如`ioctl`和`VIDIOC_S_STD`等函数。
v4l2_subdev_has_op
用于检查V4L2子设备结构体中是否定义了指定的回调函数。该函数的参数如下:
- `struct v4l2_subdev *sd`:指向要检查的V4L2子设备结构体的指针。
- `const void *o`:表示回调函数所在的结构体中的一组回调函数。
- `int f`:要检查的回调函数的索引。
该函数使用给定的指针`o`和索引`f`来查找V4L2子设备结构体中是否定义了指定的回调函数。如果找到了,则返回1,否则返回0。此函数通常由V4L2驱动程序内部使用,用户空间应用程序通常不需要直接使用。
需要注意的是,在使用该函数之前,必须保证V4L2子设备结构体已经被正确初始化,并且`v4l2_subdev_ops`结构体已经被正确设置。
v4l2_subdev_notify_event
用于将一个事件通知传递给V4L2子设备的用户空间事件监听器和桥接驱动程序。该函数的参数如下:
- `struct v4l2_subdev *sd`:指向要通知的V4L2子设备结构体的指针。
- `const struct v4l2_event *ev`:要通知的事件结构体的指针。
该函数将使用给定的`v4l2_subdev`结构体和`v4l2_event`结构体来生成一个事件通知,并将其发送到所有已经注册了子设备事件队列的用户空间事件监听器以及桥接驱动程序。在通知桥接驱动程序时,`notify`回调函数将被调用,并且通知类型将设置为`V4L2_DEVICE_NOTIFY_EVENT`。
需要注意的是,在使用该函数之前,必须保证V4L2子设备结构体已经被正确初始化,并且已经注册了事件队列。
2.2.12 V4L2 events
V4L2事件提供了向用户空间传递事件的通用方式。 驱动程序必须使用v4l2_fh才能支持V4L2事件。
事件按文件句柄订阅。 事件规范由类型组成,可选择与通过id字段标识的对象相关联。 如果未使用,则id为0。 因此,事件由(type,id)元组唯一标识。
v4l2_fh结构在其subscribed字段上具有订阅事件列表。
当用户订阅事件时,会向v4l2_fh.subscribed添加一个v4l2_subscribed_event结构,对于每个已订阅的事件都会添加一个。
每个v4l2_subscribed_event结构以一个由v4l2_event_subscribe()的调用者给出大小的v4l2_kevent环形缓冲区结尾。此环形缓冲区用于存储驱动程序引发的任何事件。
因此,每个(type,ID)事件元组都有自己的v4l2_kevent环形缓冲区。这保证了如果驱动程序在短时间内生成大量相同类型的事件,则不会覆盖另一类型的事件。
但是,如果您收到的某种类型的事件比v4l2_kevent环形缓冲区的大小还多,则最旧的事件将被丢弃并添加新事件。
v4l2_kevent结构链接到v4l2_fh结构的可用列表中,以便ioctl VIDIOC_DQEVENT知道首先出队哪个事件。
最后,如果事件订阅与特定对象(例如V4L2控件)相关联,则该对象也需要知道以便该对象可以引发事件。因此,节点字段可以用于将v4l2_subscribed_event结构链接到此类对象的列表中。
因此,总结一下:
• struct v4l2_fh有两个列表:一个是已订阅事件,另一个是可用事件。
• struct v4l2_subscribed_event具有特定类型的已提出(待处理)事件的环形缓冲区。
• 如果struct v4l2_subscribed_event与特定对象相关联,则该对象将具有struct v4l2_subscribed_event的内部列表,以便它知道谁向该对象订阅了事件。
此外,内部的struct v4l2_subscribed_event具有merge()和replace()回调,驱动程序可以设置它们。当提出新事件且没有更多空间时,将调用这些回调函数。
replace()回调允许您使用新事件的有效负载替换旧事件的有效负载,并将旧有效负载中的任何相关数据合并到替换它的新有效负载中。当此事件类型具有大小为1的环形缓冲区时,即仅可以在环形缓冲区中存储一个事件时,将调用replace()回调。
与此不同,merge()回调允许您将最旧的事件有效负载合并到次旧事件有效负载中。当环形缓冲区的大小大于1时,将调用此回调。
这样,就不会丢失任何状态信息,只会丢失导致该状态的中间步骤。
这些replace/merge回调的良好示例可在v4l2-event.c中找到:控件事件的ctrls_replace()和ctrls_merge()回调。
注意:这些回调可能会从中断上下文中调用,因此必须快速执行。
为了将事件排队到视频设备,驱动程序应该调用:
v4l2_event_queue(vdev, ev)
驱动程序仅需填写type和data字段。其他字段将由V4L2填充。
什么是中断上下文:
中断上下文是指在操作系统内核执行中断处理程序期间使用的一组堆栈和寄存器,其目的是在中断处理程序执行期间保存系统状态。由于中断可能随时发生,因此在进入中断处理程序时,处理器将会保存当前的CPU状态,并将控制转移到中断处理程序。在这种情况下,中断上下文就是用来保存该状态并执行中断处理程序的执行环境。中断上下文的执行需要尽可能地快速完成,以便将控制返回到被中断的程序。因此,在中断上下文中只能访问安全的内核代码和数据,不能执行可能会引起死锁或长时间延迟的操作。
2.2.12.1 Event subscription
通过v4l2_event_subscribe(fh, sub, elems, ops)来订阅事件:
这个函数用于实现video_device->ioctl_ops->vidioc_subscribe_event,但是驱动程序必须首先检查驱动程序是否能够生成指定事件ID的事件,然后应该调用v4l2_event_subscribe()来订阅该事件。
elems参数是此事件队列的大小。如果为0,则框架将填充默认值(这取决于事件类型)。
ops参数允许驱动程序指定多个回调函数:
所有4个回调函数都是可选的,如果您不想指定任何回调函数,ops参数本身可以为NULL。
2.2.12.2 Unsubscribing an event
取消订阅事件是通过v4l2_event_unsubscribe(fh, sub)实现的。
这个函数用于实现video_device->ioctl_ops->vidioc_unsubscribe_event。
除非驱动程序想参与取消订阅过程,否则驱动程序可以直接调用v4l2_event_unsubscribe()。
特殊类型V4L2_EVENT_ALL可用于取消订阅所有事件。驱动程序可能希望以特殊方式处理此事件。
2.2.12.3 Check if there’s a pending event
检查是否有待处理事件是通过v4l2_event_pending(fh)实现的。
此函数返回待处理事件的数量。在实现轮询时非常有用。
2.2.12.4 How events work
事件通过轮询系统调用传递给用户空间。驱动程序可以将v4l2_fh->wait(一个wait_queue_head_t)作为poll_wait()的参数使用。
有标准事件和私有事件。新的标准事件必须使用最小可用事件类型。驱动程序必须从其自己的类中分配事件,从类基础开始。类基础是V4L2_EVENT_PRIVATE_START + n * 1000,其中n是最低可用数字。该类中的第一个事件类型保留供将来使用,因此第一个可用的事件类型是“class base + 1”。
如何使用V4L2事件的示例可在OMAP 3 ISP驱动程序(drivers/media/platform/omap3isp)中找到。子设备可以直接使用V4L2_DEVICE_NOTIFY_EVENT向v4l2_device notify函数发送事件。这允许桥将发送事件的子设备映射到需要了解此类事件的与子设备关联的视频节点。
2.2.12.5 V4L2 event functions and data structures
struct v4l2_kevent
内部内核事件结构体。
定义:
struct v4l2_kevent {struct list_head list;struct v4l2_subscribed_event *sev;struct v4l2_event event;u64 ts;
};
成员:
list v4l2_fh->available列表的列表节点。
sev 指向父v4l2_subscribed_event的指针。
event 事件本身。
ts 事件的时间戳。
struct v4l2_subscribed_event_ops
已订阅事件操作。
定义:
struct v4l2_subscribed_event_ops {int (*add)(struct v4l2_subscribed_event *sev, unsigned int elems);void (*del)(struct v4l2_subscribed_event *sev);void (*replace)(struct v4l2_event *old, const struct v4l2_event *new);void (*merge)(const struct v4l2_event *old, struct v4l2_event *new);
};
成员:
add 可选回调,当添加新的侦听器时调用
del 可选回调,当侦听器停止侦听时调用
replace 可替换事件“old”为事件“new”的可选回调。
merge 可将事件“old”合并到事件“new”的可选回调。
struct v4l2_subscribed_event
表示订阅事件的内部结构体。
定义:
struct v4l2_subscribed_event {struct list_head list;u32 type;u32 id;u32 flags;struct v4l2_fh *fh;struct list_head node;const struct v4l2_subscribed_event_ops *ops;unsigned int elems;unsigned int first;unsigned int in_use;struct v4l2_kevent events[];
};
成员:
list v4l2_fh->subscribed列表的列表节点。
type 事件类型。
id 关联的对象ID(例如,控制ID)。如果没有,则为0。
flags v4l2_event_subscription->flags的副本。
fh 订阅此事件的文件句柄。
node 钩入对象事件列表的列表节点(如果有)。
ops v4l2_subscribed_event_ops
elems 事件数组中的元素数。
first 包含最旧可用事件的事件的索引。
in_use 排队事件的数量。
events 一个包含elems事件的数组。
int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event, int nonblocking)
从视频设备中取消排队事件。
参数:
struct v4l2_fh *fh 指向结构体v4l2_fh的指针
struct v4l2_event *event 指向结构体v4l2_event的指针
int nonblocking 如果非零,则等待事件到达
void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
将事件排队到视频设备。
参数:
struct video_device *vdev 指向struct video_device的指针
const struct v4l2_event *ev 指向struct v4l2_event的指针
描述:
事件将为所有struct v4l2_fh文件处理器排队。
注意:驱动程序的唯一责任是填写类型和数据字段。其他字段将由V4L2填充。
void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
将事件排队到视频设备。
参数:
struct v4l2_fh *fh 指向struct v4l2_fh的指针
const struct v4l2_event *ev 指向struct v4l2_event的指针
描述:
事件只会为指定的struct v4l2_fh文件处理器排队。
注意:驱动程序唯一的责任是填写类型和数据字段。其他字段将由V4L2填充。
void v4l2_event_wake_all(struct video_device *vdev)
唤醒所有文件处理器。
参数:
struct video_device *vdev 指向struct video_device的指针
描述:
当注销视频设备时使用。
int v4l2_event_pending(struct v4l2_fh *fh)
检查是否有事件可用。
参数:
struct v4l2_fh *fh 指向struct v4l2_fh的指针
描述:
返回挂起事件的数量。
int v4l2_event_subscribe(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub,
unsigned int elems, const struct v4l2_subscribed_event_ops *ops)
订阅事件。
参数:
struct v4l2_fh *fh 指向struct v4l2_fh的指针
const struct v4l2_event_subscription *sub 指向struct v4l2_event_subscription的指针
unsigned int elems 事件队列的大小
const struct v4l2_subscribed_event_ops *ops 指向v4l2_subscribed_event_ops的指针
描述:
注意:如果elems为零,框架将填写默认值,目前默认值为1个元素。
int v4l2_event_unsubscribe(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
取消订阅事件。
参数:
struct v4l2_fh *fh 指向struct v4l2_fh的指针
const struct v4l2_event_subscription *sub 指向struct v4l2_event_subscription的指针
void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
取消订阅所有事件。
参数:
struct v4l2_fh *fh 指向struct v4l2_fh的指针
int v4l2_event_subdev_unsubscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub)
用于v4l2_event_unsubscribe()的子设备变体。
参数:
struct v4l2_subdev *sd 指向struct v4l2_subdev的指针
struct v4l2_fh *fh 指向struct v4l2_fh的指针
struct v4l2_event_subscription *sub 指向struct v4l2_event_subscription的指针
描述:
注意:此函数应用于struct v4l2_subdev_core_ops中的unsubscribe_event字段。
int v4l2_src_change_event_subscribe(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
这是一个辅助函数,如果事件为V4L2_EVENT_SOURCE_CHANGE,则调用v4l2_event_subscribe()。
参数:
struct v4l2_fh *fh 指向struct v4l2_fh的指针
const struct v4l2_event_subscription *sub 指向struct v4l2_event_subscription的指针
int v4l2_src_change_event_subdev_subscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub)
此函数是v4l2_event_subscribe()的变体,旨在仅订阅类型为V4L2_EVENT_SOURCE_CHANGE的事件。
参数:
struct v4l2_subdev *sd 指向struct v4l2_subdev的指针
struct v4l2_fh *fh 指向struct v4l2_fh的指针
struct v4l2_event_subscription *sub 指向struct v4l2_event_subscription的指针
2.2.13 V4L2 Controls
2.2.13.1 Introduction
V4L2控制API似乎足够简单,但在驱动程序中正确实现时很快变得非常困难。但是,处理控制所需的许多代码实际上并非驱动程序特定,可以移动到V4L核心框架中。
毕竟,驱动程序开发人员感兴趣的仅是:
1)如何添加控件?
2)如何设置控件的值?(即s_ctrl)
偶尔会有:
3)如何获取控件的值?(即g_volatile_ctrl)
4)如何验证用户提出的控件值?(即try_ctrl)
所有其余内容都可以在中央完成。
控件框架是为了在一个中央位置实现V4L2规范关于控件的所有规则而创建的。并尽可能地简化驱动程序开发人员的生活。
请注意,控制框架依赖于V4L2驱动程序中struct v4l2_device和子设备驱动程序中的struct v4l2_subdev的存在。
2.2.13.2 Objects in the framework
有两个主要的对象:
v4l2_ctrl对象描述控制属性并跟踪控制的值(当前值和建议的新值)。
v4l2_ctrl_handler是跟踪控件的对象。它维护了一个v4l2_ctrl对象列表和另一个引用控件的列表,可能引用其他处理程序拥有的控件。
2.2.13.3 Basic usage for V4L2 and sub-device drivers
1) Prepare the driver:
#include <media/v4l2-ctrls.h>
1.1) Add the handler to your driver’s top-level struct:
For V4L2 drivers:
struct foo_dev {
...
struct v4l2_device v4l2_dev;
...
struct v4l2_ctrl_handler ctrl_handler;
...
};
For sub-device drivers:
struct foo_dev {
...
struct v4l2_subdev sd;
...
struct v4l2_ctrl_handler ctrl_handler;
...
};
1.2) Initialize the handler:
v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
第二个参数是一个提示,告诉函数此处理程序预计要处理多少个控件。它将根据此信息分配哈希表。这只是一个提示。
1.3) Hook the control handler into the driver:
For V4L2 drivers:
foo->v4l2_dev.ctrl_handler = &foo->ctrl_handler;
For sub-device drivers:
foo->sd.ctrl_handler = &foo->ctrl_handler;
1.4) Clean up the handler at the end:
v4l2_ctrl_handler_free(&foo->ctrl_handler);
2) Add controls:
You add non-menu controls by calling v4l2_ctrl_new_std():
struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
const struct v4l2_ctrl_ops *ops,
u32 id, s32 min, s32 max, u32 step, s32 def);
Menu and integer menu controls are added by calling v4l2_ctrl_new_std_menu():
struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
const struct v4l2_ctrl_ops *ops,
u32 id, s32 max, s32 skip_mask, s32 def);
Menu controls with a driver specifific menu are added by calling v4l2_ctrl_new_std_menu_items():
struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(
struct v4l2_ctrl_handler *hdl,
const struct v4l2_ctrl_ops *ops, u32 id, s32 max,
s32 skip_mask, s32 def, const char * const *qmenu);
Standard compound controls can be added by calling v4l2_ctrl_new_std_compound():
struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
const struct v4l2_ctrl_ops *ops, u32 id,
const union v4l2_ctrl_ptr p_def);
Integer menu controls with a driver specifific menu can be added by calling v4l2_ctrl_new_int_menu():
struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
const struct v4l2_ctrl_ops *ops,
u32 id, s32 max, s32 def, const s64 *qmenu_int);
These functions are typically called right after the v4l2_ctrl_handler_init():
static const s64 exp_bias_qmenu[] = {
-2, -1, 0, 1, 2
};
static const char * const test_pattern[] = {
"Disabled",
"Vertical Bars",
"Solid Black",
"Solid White",
};
v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, 128);
v4l2_ctrl_new_std_menu(&foo->ctrl_handler, &foo_ctrl_ops,
V4L2_CID_POWER_LINE_FREQUENCY,
V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
v4l2_ctrl_new_int_menu(&foo->ctrl_handler, &foo_ctrl_ops,
V4L2_CID_EXPOSURE_BIAS,
ARRAY_SIZE(exp_bias_qmenu) - 1,
ARRAY_SIZE(exp_bias_qmenu) / 2 - 1,
exp_bias_qmenu);
v4l2_ctrl_new_std_menu_items(&foo->ctrl_handler, &foo_ctrl_ops,
V4L2_CID_TEST_PATTERN, ARRAY_SIZE(test_pattern) - 1, 0,
0, test_pattern);
...
if (foo->ctrl_handler.error) {
int err = foo->ctrl_handler.error;
v4l2_ctrl_handler_free(&foo->ctrl_handler);
return err;
}
v4l2_ctrl_new_std()函数返回指向新控件的v4l2_ctrl指针,但如果您不需要在控件操作之外访问指针,则没有必要存储它。
v4l2_ctrl_new_std()函数将根据控件ID填充大多数字段,除了最小值、最大值、步长和默认值。这些值是驱动程序特定的,而控制属性(如类型、名称、标志)都是全局的。控件的当前值将设置为默认值。
v4l2_ctrl_new_std_menu()函数非常相似,但用于菜单控件。由于菜单控件的最小值始终为0,因此不存在min参数。而且它没有步骤,而是有一个skip_mask参数:如果位X为1,则跳过菜单项X。
v4l2_ctrl_new_int_menu()函数创建一个带有驱动程序特定菜单项的新标准整数菜单控件。它与v4l2_ctrl_new_std_menu不同之处在于它没有掩码参数,而是将最后一个参数作为64位有符号整数数组,形成精确的菜单项列表。
v4l2_ctrl_new_std_menu_items()函数与v4l2_ctrl_new_std_menu非常相似,但还取一个额外的参数qmenu,它是用于定义驱动程序特定菜单项的标准菜单控件。这个控件的一个很好的例子是捕获/显示/传感器设备的测试模式控件,这些设备具有生成测试模式的功能。这些测试模式是硬件特定的,因此菜单的内容将因设备而异。
请注意,如果某些操作失败,函数将返回NULL或错误,并设置ctrl_handler->error为错误代码。如果ctrl_handler->error已经设置,则它将返回并且不执行任何操作。如果v4l2_ctrl_handler_init无法分配内部数据结构,情况也是如此。
这使得初始化处理程序变得容易,只需要添加所有控件,最后检查错误代码即可。节省了大量重复的错误检查。
建议按升序控件ID顺序添加控件:这样会更快一些。
3) Optionally force initial control setup:
v4l2_ctrl_handler_setup(&foo->ctrl_handler);
这将无条件地为所有控件调用s_ctrl。实际上,这会将硬件初始化为默认控件值。建议您这样做,因为这可以确保内部数据结构和硬件同步。
4) Finally: implement the v4l2_ctrl_ops
static const struct v4l2_ctrl_ops foo_ctrl_ops = {
.s_ctrl = foo_s_ctrl,
};
Usually all you need is s_ctrl:
static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct foo *state = container_of(ctrl->handler, struct foo, ctrl_handler);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
write_reg(0x123, ctrl->val);
break;
case V4L2_CID_CONTRAST:
write_reg(0x456, ctrl->val);
break;
}
return 0;
}
控制操作使用v4l2_ctrl指针作为参数进行调用。新的控件值已经过验证,因此您需要做的就是实际更新硬件寄存器。您完成了!对于我们拥有的大多数驱动程序来说,这已足够。无需对控件值进行任何验证,也不需要实现QUERYCTRL、QUERY_EXT_CTRL和QUERYMENU。而且G/S_CTRL以及G/TRY/S_EXT_CTRLS也会自动支持。
注意:其余部分处理更高级的控件主题和场景。在实践中,上述描述的基本用法对于大多数驱动程序来说已经足够。
2.2.13.4 Inheriting Sub-device Controls
当通过调用v4l2_device_register_subdev()向V4L2驱动程序注册子设备并设置v4l2_subdev和v4l2_device的ctrl_handler字段时,子设备的控件将自动在V4L2驱动程序中可用。如果子设备驱动程序包含已存在于V4L2驱动程序中的控件,则会跳过这些控件(因此V4L2驱动程序始终可以覆盖子设备控件)。
这里发生的是,v4l2_device_register_subdev()调用v4l2_ctrl_add_handler(),将子设备的控件添加到v4l2_device的控件中。
2.2.13.5 Accessing Control Values
以下联合体在控件框架内用于访问控件值:
union v4l2_ctrl_ptr {
s32 *p_s32;
s64 *p_s64;
char *p_char;
void *p;
};
v4l2_ctrl结构包含这些字段,可以用于访问当前值和新值:
s32 val;
struct {
s32 val;
} cur;
union v4l2_ctrl_ptr p_new;
union v4l2_ctrl_ptr p_cur;
If the control has a simple s32 type, then:
&ctrl->val == ctrl->p_new.p_s32
&ctrl->cur.val == ctrl->p_cur.p_s32
对于其他类型,请使用ctrl->p_cur.p<something>。基本上,val和cur.val字段可以视为别名,因为它们经常被使用。
在控制操作中,您可以自由地使用它们。二者都很容易理解。p_char指针指向长度为ctrl-> maximum + 1的字符缓冲区,并且始终以0结尾。
除非控件标记为volatile,否则p_cur字段指向当前缓存的控件值。当您创建新控件时,该值变为与默认值相同。调用v4l2_ctrl_handler_setup()后,将该值传递给硬件。通常最好调用此函数。
每当设置新值时,新值都会自动缓存。这意味着大多数驱动程序不需要实现g_volatile_ctrl()操作。例外情况是对于返回易失寄存器(例如连续变化的信号强度读数)的控件,您需要像这样实现g_volatile_ctrl:
static int foo_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->val = read_reg(0x123);
break;
}
}
请注意,在g_volatile_ctrl中也使用了“new value”联合体。通常需要实现g_volatile_ctrl的控件是只读控件。如果它们不是,将不会在控件更改时生成V4L2_EVENT_CTRL_CH_VALUE。
要将控件标记为易失性,必须设置V4L2_CTRL_FLAG_VOLATILE:
ctrl = v4l2_ctrl_new_std(&sd->ctrl_handler, ...);
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
对于try/s_ctrl,新值(即由用户传递的值)会被填充,您可以在try_ctrl中修改这些值或在s_ctrl中设置它们。 “cur”联合体包含当前值,您也可以使用(但无法更改)。
如果s_ctrl返回0(OK),则控件框架将把新的最终值复制到“cur”联合体中。
在g_volatile / s / try_ctrl中,您可以访问由同一处理程序拥有的所有控件的值,因为持有处理程序的锁定。如果您需要访问其他处理程序拥有的控件的值,则必须非常小心,以免引入死锁。
在控制操作之外,您需要通过助手函数来安全地获取或设置驱动程序中单个控件的值:
s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
这些函数与VIDIOC_G/S_CTRL ioctls一样通过控件框架进行操作。但是,请不要在g_volatile / s / try_ctrl内使用这些函数,否则将导致死锁,因为这些助手程序也会锁定处理程序。
您还可以自己获取处理程序锁:
mutex_lock(&state->ctrl_handler.lock);
pr_info("String value is '%s'\n", ctrl1->p_cur.p_char);
pr_info("Integer value is '%s'\n", ctrl2->cur.val);
mutex_unlock(&state->ctrl_handler.lock);
2.2.13.6 Menu Controls
The v4l2_ctrl struct contains this union:
union {
u32 step;
u32 menu_skip_mask;
};
对于菜单控件,使用menu_skip_mask。它允许您轻松地排除某些菜单项。这在VIDIOC_QUERYMENU实现中使用,您可以返回-EINVAL,如果某个菜单项不存在。请注意,VIDIOC_QUERYCTRL始终为菜单控件返回步骤值1。
一个好的例子是MPEG音频层II比特率菜单控件,其中菜单是标准化可能比特率的列表。但是在实践中,硬件实现只支持其中的一个子集。通过设置skip mask,您可以告诉框架应跳过哪些菜单项。将其设置为0表示支持所有菜单项。
您可以通过v4l2_ctrl_config结构为自定义控件设置此掩码,或者通过调用v4l2_ctrl_new_std_menu()设置掩码。
2.2.13.7 Custom Controls
可以使用v4l2_ctrl_new_custom()创建特定于驱动程序的控件:
static const struct v4l2_ctrl_config ctrl_filter = {
.ops = &ctrl_custom_ops,
.id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
.name = "Spatial Filter",
.type = V4L2_CTRL_TYPE_INTEGER,
.flags = V4L2_CTRL_FLAG_SLIDER,
.max = 15,
.step = 1,
};
ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_filter, NULL);
最后一个参数是priv指针,可以设置为特定于驱动程序的私有数据。v4l2_ctrl_config结构体还有一个字段用于设置is_private标志。
如果未设置名称字段,则框架将假定这是一个标准控件,并将相应地填充名称、类型和标志字段。
2.2.13.8 Active and Grabbed Controls
如果控件之间存在更复杂的关系,您可能需要激活和停用控件。例如,如果启用了Chroma AGC控件,则Chroma增益控件无效。也就是说,您可以设置它,但只要自动增益控制处于开启状态,硬件将不会使用其值。通常,用户界面可以禁用这样的输入字段。
您可以使用v4l2_ctrl_activate()设置“active”状态。默认情况下,所有控件都是活动的。请注意,框架不会检查此标志。它纯粹是为了GUI而设计的。该函数通常从s_ctrl内调用。
另一个标志是“grabbed”标志。“grabbed”控件意味着您无法更改它,因为它被某些资源使用。典型的示例是MPEG比特率控件,在捕获正在进行时无法更改。
如果使用v4l2_ctrl_grab()将控件设置为“已抓取”,则如果尝试设置此控件,框架将返回-EBUSY。v4l2_ctrl_grab()函数通常从驱动程序在开始或停止流传输时调用。
2.2.13.9 Control Clusters
默认情况下,所有控件都与其他控件无关。但在更复杂的情况下,您可以从一个控件到另一个控件获取依赖关系。在这种情况下,您需要将它们“聚类”:
struct foo {
struct v4l2_ctrl_handler ctrl_handler;
#define AUDIO_CL_VOLUME (0)
#define AUDIO_CL_MUTE (1)
struct v4l2_ctrl *audio_cluster[2];
...
};state->audio_cluster[AUDIO_CL_VOLUME] =
v4l2_ctrl_new_std(&state->ctrl_handler, ...);
state->audio_cluster[AUDIO_CL_MUTE] =
v4l2_ctrl_new_std(&state->ctrl_handler, ...);
v4l2_ctrl_cluster(ARRAY_SIZE(state->audio_cluster), state->audio_cluster);
从现在开始,只要设置(或“获取”或“尝试”)属于同一聚类的一个或多个控件,就只会调用第一个控件(在此示例中为“volume”)的控件操作。实际上,您创建了一个新的复合控件。类似于C语言中的“结构体”的工作方式。
因此,当使用V4L2_CID_AUDIO_VOLUME作为参数调用s_ctrl时,您应设置属于audio_cluster的所有两个控件:
static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct foo *state = container_of(ctrl->handler, struct foo, ctrl_handler);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME: {
struct v4l2_ctrl *mute = ctrl->cluster[AUDIO_CL_MUTE];
write_reg(0x123, mute->val ? 0 : ctrl->val);
break;
}
case V4L2_CID_CONTRAST:
write_reg(0x456, ctrl->val);
break;
}
return 0;
}
在上面的示例中,以下内容在VOLUME情况下是等效的:
ctrl == ctrl->cluster[AUDIO_CL_VOLUME] == state->audio_cluster[AUDIO_CL_VOLUME]
ctrl->cluster[AUDIO_CL_MUTE] == state->audio_cluster[AUDIO_CL_MUTE]
在实践中,使用此类聚类数组变得非常繁琐。因此,改用以下等效方法:
struct {
/* audio cluster */
struct v4l2_ctrl *volume;
struct v4l2_ctrl *mute;
};
匿名结构用于清楚地“聚类”这两个控件指针,但它没有其他用途。其效果与创建具有两个控件指针的数组相同。因此,您可以只需执行以下操作:
state->volume = v4l2_ctrl_new_std(&state->ctrl_handler, ...);
state->mute = v4l2_ctrl_new_std(&state->ctrl_handler, ...);
v4l2_ctrl_cluster(2, &state->volume);
在foo_s_ctrl中,您可以直接使用这些指针:state->mute->val。
请注意,聚类中的控件可能为NULL。例如,如果由于硬件不支持该特定功能而从未添加静音(mute),则静音(mute)将为NULL。
因此,在这种情况下,我们有一个由2个控件组成的聚类,其中只有1个实例化。唯一的限制是聚类的第一个控件必须始终存在,因为那是该聚类的“主”控件。主控件是标识聚类并提供用于该聚类的v4l2_ctrl_ops结构指针的控件。
显然,聚类数组中的所有控件都必须初始化为有效控件或NULL。
在极少数情况下,您可能想知道集群中实际上是由用户明确设置的哪些控件。为此,可以检查每个控件的“is_new”标志。例如,在音量/静音(mute)聚类的情况下,如果用户仅针对静音(mute)调用VIDIOC_S_CTRL,则静音(mute)控件的“is_new”标志将被设置。如果用户分别调用了静音(mute)和音量控件的VIDIOC_S_EXT_CTRLS,则两个控件的“is_new”标志均为1。
当从v4l2_ctrl_handler_setup()中调用时,“is_new”标记始终为1。
2.2.13.10 Handling autogain/gain-type Controls with Auto Clusters
一种常见类型的控件集群是处理“自动foo/foo”类型的控件。典型的例子是自动增益(autogain)/增益(gain),自动曝光(autoexposure)/曝光(exposure),自动白平衡(autowhitebalance)/红色平衡(red balance)/蓝色平衡(blue balance)。在所有情况下,您都有一个控件,该控件确定另一个控件是由硬件自动处理还是由用户手动控制。
如果聚类处于自动模式,则应将手动控件标记为非活动和易失性。当读取易失性控件时,g_volatile_ctrl操作应返回硬件自动模式设置的值。如果将聚类置于手动模式,则手动控件应再次变为活动状态,并清除易失性标志(因此在手动模式下不再调用g_volatile_ctrl)。此外,在切换到手动模式之前,由自动模式确定的当前值将被复制为新手动值。最后,对于自动控件,应设置V4L2_CTRL_FLAG_UPDATE,因为更改该控件会影响手动控件的控制标志。
为了简化此过程,引入了v4l2_ctrl_cluster的特殊变体:
void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls, u8 manual_val, bool set_volatile);
第一个和第二个参数与v4l2_ctrl_cluster相同。第三个参数告诉框架哪个值将聚类切换到手动模式。最后一个参数将可选地为非自动控件设置V4L2_CTRL_FLAG_VOLATILE。如果为假,则手动控件永远不是易失性的。如果硬件不允许您读取由自动模式确定的当前值(例如,如果autogain已开启,则硬件不允许您获取当前增益值),则通常会使用它。
聚类的第一个控件被认为是“自动”控件。
使用此函数将确保您无需处理所有复杂的标志和易失性处理。
2.2.13.11 VIDIOC_LOG_STATUS Support
这个ioctl允许你将驱动程序的当前状态转储到内核日志中。v4l2_ctrl_handler_log_status(ctrl_handler,prefix)可用于将给定处理程序所拥有的控件的值转储到日志中。你也可以提供一个前缀。如果前缀没有以空格结尾,则会为你添加': '。
2.2.13.12 Different Handlers for Different Video Nodes
通常,V4L2驱动程序只有一个控制处理程序,它是所有视频节点的全局控制处理程序。但是,你也可以为不同的视频节点指定不同的控制处理程序。你可以通过手动设置结构体video_device的ctrl_handler字段来实现。如果没有涉及到子设备,则不会有问题,但是如果有子设备,则需要阻止子设备控制自动合并到全局控制处理程序。你可以通过简单地在struct v4l2_device中设置ctrl_handler字段为NULL来实现此操作。此后,v4l2_device_register_subdev()将不再合并子设备控件。
添加每个子设备后,你需要调用v4l2_ctrl_add_handler手动将子设备的控制处理程序(sd->ctrl_handler)添加到所需的控制处理程序中。该控制处理程序可以是特定于video_device或video_device子集的。例如:收音机设备节点仅具有音频控件,而视频和vbi设备节点共享用于音频和视频控件的相同控制处理程序。
如果你想让一个处理程序(例如一个收音机设备节点)拥有另一个处理程序(例如一个视频设备节点)的子集,那么你应该先将控件添加到第一个处理程序中,将其他控件添加到第二个处理程序中,最后将第一个处理程序添加到第二个处理程序中。例如:
v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_VOLUME, ...);
v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
v4l2_ctrl_add_handler(&video_ctrl_handler, &radio_ctrl_handler, NULL);
v4l2_ctrl_add_handler()的最后一个参数是一个筛选函数,允许你筛选将被添加的控件。如果你想添加所有控件,请将其设置为NULL。或者,你可以将特定的控件添加到处理程序中:
volume = v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_AUDIO_VOLUME, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_BRIGHTNESS, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_CONTRAST, ...);
你不应该为两个处理程序创建两个相同的控件。例如:
v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_AUDIO_MUTE, ...);
这是不好的,因为静音收音机不会改变视频静音控制。规则是每个硬件旋钮都应该有一个控件。
2.2.13.13 Finding Controls
通常,你自己创建了控件,并可以将struct v4l2_ctrl指针存储到自己的结构中。但有时你需要从另一个未拥有的处理程序中查找控件。例如,如果你需要从子设备中查找音量控制。
你可以通过调用v4l2_ctrl_find来实现:
struct v4l2_ctrl *volume;
volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME);
由于v4l2_ctrl_find将锁定处理程序,因此你必须小心在哪里使用它。例如,下面这种做法是不好的:
struct v4l2_ctrl_handler ctrl_handler;
v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
…and in video_ops.s_ctrl:
case V4L2_CID_BRIGHTNESS:
contrast = v4l2_find_ctrl(&ctrl_handler, V4L2_CID_CONTRAST);
...
当框架调用s_ctrl时,ctrl_handler.lock已经被占用,因此尝试从相同的处理程序中查找另一个控件将导致死锁。
因此,不建议在控制操作内部使用此函数。
2.2.13.14 Preventing Controls inheritance
使用v4l2_ctrl_add_handler将一个控制处理程序添加到另一个处理程序时,默认情况下,一个处理程序的所有控件会合并到另一个处理程序中。但是,一个子设备可能具有在某些高级嵌入式系统中有用,但在消费级硬件中使用时并不合适的低级控件。在这种情况下,你想将这些低级别的控件保留在子设备本地。你可以通过简单地将控件的“is_private”标志设置为1来实现:
static const struct v4l2_ctrl_config ctrl_private = {
.ops = &ctrl_custom_ops,
.id = V4L2_CID_...,
.name = "Some Private Control",
.type = V4L2_CTRL_TYPE_INTEGER,
.max = 15,
.step = 1,
.is_private = 1,
};
ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_private, NULL);
现在,在调用v4l2_ctrl_add_handler时,这些控件将被跳过。
2.2.13.15 V4L2_CTRL_TYPE_CTRL_CLASS Controls
这种类型的控件可以被GUI用于获取控制类的名称。一个完整功能的GUI可以创建一个具有多个选项卡的对话框,每个选项卡包含属于特定控制类的控件。每个选项卡的名称可以通过查询特殊ID <control class | 1> 的控件来获得。
驱动程序不必关心此事。只要添加属于新控制类的第一个控件,框架将自动添加此类型的控件。
2.2.13.16 Adding Notify Callbacks
有时,平台或桥接驱动程序需要在子设备驱动程序的控件发生更改时进行通知。你可以通过调用此函数来设置一个通知回调函数:
void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl,
void (*notify)(struct v4l2_ctrl *ctrl, void *priv), void *priv);
每当给定控件更改值时,通知回调函数将被调用,并提供指向控件和通过v4l2_ctrl_notify传递的私有指针。请注意,在调用通知函数时,控制处理程序锁被持有。
每个控制处理程序只能有一个通知函数。任何尝试设置另一个通知函数都将导致WARN_ON警告。
2.2.13.17 v4l2_ctrl functions and data structures
union v4l2_ctrl_ptr
指向控制值的指针。
定义:
union v4l2_ctrl_ptr {
s32 *p_s32;
s64 *p_s64;
u8 *p_u8;
u16 *p_u16;
u32 *p_u32;
char *p_char;
struct v4l2_ctrl_mpeg2_sequence *p_mpeg2_sequence;
struct v4l2_ctrl_mpeg2_picture *p_mpeg2_picture;
struct v4l2_ctrl_mpeg2_quantisation *p_mpeg2_quantisation;
struct v4l2_ctrl_fwht_params *p_fwht_params;
struct v4l2_ctrl_h264_sps *p_h264_sps;
struct v4l2_ctrl_h264_pps *p_h264_pps;
struct v4l2_ctrl_h264_scaling_matrix *p_h264_scaling_matrix;
struct v4l2_ctrl_h264_slice_params *p_h264_slice_params;
struct v4l2_ctrl_h264_decode_params *p_h264_decode_params;
struct v4l2_ctrl_h264_pred_weights *p_h264_pred_weights;
struct v4l2_ctrl_vp8_frame *p_vp8_frame;
struct v4l2_ctrl_hevc_sps *p_hevc_sps;
struct v4l2_ctrl_hevc_pps *p_hevc_pps;
struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params;
struct v4l2_ctrl_vp9_compressed_hdr *p_vp9_compressed_hdr_probs;
struct v4l2_ctrl_vp9_frame *p_vp9_frame;
struct v4l2_ctrl_hdr10_cll_info *p_hdr10_cll;
struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
struct v4l2_area *p_area;
void *p;
const void *p_const;
};
成员:
- p_s32:指向 32 位有符号值的指针。
- p_s64:指向 64 位有符号值的指针。
- p_u8:指向 8 位无符号值的指针。
- p_u16:指向 16 位无符号值的指针。
- p_u32:指向 32 位无符号值的指针。
- p_char:指向字符串的指针。
- p_mpeg2_sequence:指向 MPEG2 序列结构的指针。
- p_mpeg2_picture:指向 MPEG2 图像结构的指针。
- p_mpeg2_quantisation:指向 MPEG2 量化数据结构的指针。
- p_fwht_params:指向 FWHT 无状态参数结构的指针。
- p_h264_sps:指向结构体 v4l2_ctrl_h264_sps 的指针。
- p_h264_pps:指向结构体 v4l2_ctrl_h264_pps 的指针。
- p_h264_scaling_matrix:指向结构体 v4l2_ctrl_h264_scaling_matrix 的指针。
- p_h264_slice_params:指向结构体 v4l2_ctrl_h264_slice_params 的指针。
- p_h264_decode_params:指向结构体 v4l2_ctrl_h264_decode_params 的指针。
- p_h264_pred_weights:指向结构体 v4l2_ctrl_h264_pred_weights 的指针。
- p_vp8_frame:指向 VP8 帧参数结构的指针。
- p_hevc_sps:指向 HEVC 序列参数集结构的指针。
- p_hevc_pps:指向 HEVC 图像参数集结构的指针。
- p_hevc_slice_params:指向 HEVC 切片参数结构的指针。
- p_vp9_compressed_hdr_probs:指向 VP9 帧压缩头概率结构的指针。
- p_vp9_frame:指向 VP9 帧参数结构的指针。
- p_hdr10_cll:指向 HDR10 内容亮度级别结构的指针。
- p_hdr10_mastering:指向 HDR10 主控制显示结构的指针。
- p_area:指向区域的指针。
- p:指向复合值的指针。
- p_const:指向常量复合值的指针。
union v4l2_ctrl_ptr_create(void *ptr)
是一个辅助函数,用于从 void 指针返回一个 v4l2_ctrl_ptr。
参数:
- void *ptr:void 指针。
struct v4l2_ctrl_ops
驱动程序需要提供的控制操作。
定义:
struct v4l2_ctrl_ops {
int (*g_volatile_ctrl)(struct v4l2_ctrl *ctrl);
int (*try_ctrl)(struct v4l2_ctrl *ctrl);
int (*s_ctrl)(struct v4l2_ctrl *ctrl);
};
成员:
- g_volatile_ctrl:获取此控件的新值。通常只适用于易变(通常为只读)控件,例如返回当前信号强度的控件,其值会不断变化。如果未设置,则将返回当前缓存的值。
- try_ctrl:测试控件的值是否有效。仅在通常的 min/max/step 检查不足时有效。
- s_ctrl:实际设置新控件值。s_ctrl 是必需的。这些操作被调用时,ctrl->handler->lock 被持有,因此没有其他人可以访问由该处理程序拥有的控件。
struct v4l2_ctrl_type_ops
驱动程序需要提供的控制类型操作。
定义:
struct v4l2_ctrl_type_ops {
bool (*equal)(const struct v4l2_ctrl *ctrl, u32 idx,union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2);
void (*init)(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr);
void (*log)(const struct v4l2_ctrl *ctrl);
int (*validate)(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr);
};
成员:
- equal:如果两个值相等,则返回 true。
- init:初始化值。
- log:对数值进行日志记录。
- validate:验证值。成功返回 0,否则返回负值。
- v4l2_ctrl_notify_fnc:typedef,表示当控制值发生更改时应调用的函数的通知参数。
void v4l2_ctrl_notify_fnc(struct v4l2_ctrl *ctrl, void *priv)
是一个通知参数,用于传递控制数据和私有数据给 v4l2_ctrl_notify() 和 struct v4l2_ctrl_handler。
参数:
- struct v4l2_ctrl *ctrl:指向 struct v4l2_ctrl 的指针。
- void *priv:控制的私有数据。
struct v4l2_ctrl
这是一个描述视频控制器中控制属性的结构体v4l2_ctrl。
定义:
struct v4l2_ctrl {
struct list_head node;
struct list_head ev_subs;
struct v4l2_ctrl_handler *handler;
struct v4l2_ctrl **cluster;
unsigned int ncontrols;
unsigned int done:1;
unsigned int is_new:1;
unsigned int has_changed:1;
unsigned int is_private:1;
unsigned int is_auto:1;
unsigned int is_int:1;
unsigned int is_string:1;
unsigned int is_ptr:1;
unsigned int is_array:1;
unsigned int has_volatiles:1;
unsigned int call_notify:1;
unsigned int manual_mode_value:8;
const struct v4l2_ctrl_ops *ops;
const struct v4l2_ctrl_type_ops *type_ops;
u32 id;
const char *name;
enum v4l2_ctrl_type type;
s64 minimum, maximum, default_value;
u32 elems;
u32 elem_size;
u32 dims[V4L2_CTRL_MAX_DIMS];
u32 nr_of_dims;
union {
u64 step;
u64 menu_skip_mask;
};
union {
const char * const *qmenu;
const s64 *qmenu_int;
};
unsigned long flags;
void *priv;
s32 val;
struct {
s32 val;
} cur;
union v4l2_ctrl_ptr p_def;
union v4l2_ctrl_ptr p_new;
union v4l2_ctrl_ptr p_cur;
};
具体包括以下成员:
1. node:链表节点。
2. ev_subs:控制事件订阅列表。
3. handler:拥有该控制的处理器。
4. cluster:指向簇数组的起始位置。
5. ncontrols:簇数组中的控件数量。
6. done:内部标志,对于每个已处理的控件都会设置该标志。
7. is_new:当用户为该控件指定新值时设置该标志。从v4l2_ctrl_handler_setup()中调用时也会设置它。驱动程序不应该设置此标志。
8. has_changed:当前值与新值不同时设置该标志。驱动程序不应该使用此标志。
9. is_private:如果设置,则此控件是其处理器私有的,不会添加到任何其他处理器中。驱动程序可以设置此标志。
10. is_auto:如果设置,则此控件选择其他簇成员是“自动”模式还是“手动”模式。这用于自动增益/增益类型簇。驱动程序不应直接设置此标志。
11. is_int:如果设置,则该控件具有简单的整数值(即它使用ctrl->val)。
12. is_string:如果设置,则该控件具有类型V4L2_CTRL_TYPE_STRING。
13. is_ptr:如果设置,则该控件是一个数组和/或具有类型>=V4L2_CTRL_COMPOUND_TYPES和/或类型V4L2_CTRL_TYPE_STRING。换句话说,struct v4l2_ext_control使用字段p来指向数据。
14. is_array:如果设置,则该控件包含一个N维数组。
15. has_volatiles:如果设置,则簇的一个或多个成员是易失性的。驱动程序不应触碰此标志。
16. call_notify:如果设置,则每当控件的值更改时调用处理器的notify函数。
17. manual_mode_value:如果设置了is_auto标志,则这是确定该控件是否处于手动模式的自动控件的值。因此,如果自动控件的值等于此值,则整个簇处于手动模式。驱动程序不应直接设置此标志。
18. ops:控件操作。
19. type_ops:控件类型操作。
20. id:控件ID。
21. name:控件名称。
22. type:控件类型。
23. minimum:控件的最小值。
24. maximum:控件的最大值。
25. default_value:控件的默认值。
26. elems:N维数组中的元素数量。
27. elem_size:控件的大小(以字节为单位)。
28. dims:每个维度的大小。
29. nr_of_dims:维度中的数量。
30. 匿名联合体:包含以下成员:
- step:非菜单控件的步长值。
- menu_skip_mask:菜单控件的跳过掩码。这使得可以轻松跳过无效的菜单项。如果设置了位X,则跳过菜单项X。当然,这仅适用于具有<=32个菜单项的菜单。没有任何菜单接近这个数字,所以这是可以接受的。如果我们需要更多,则必须将其扩展为u64或位阵列。
31. 匿名联合体:包含以下成员:
- qmenu:所有菜单项的const char *数组。空字符串(“”)对应于不存在的菜单项(这是除了上述菜单跳过掩码之外的内容)。最后一个条目必须为NULL。仅在类型为V4L2_CTRL_TYPE_MENU时使用。
- qmenu_int:带有整数菜单项的64位整数数组。数组的大小必须等于菜单大小,例如:ceil(maximum-minimum/step)+1。仅在类型为V4L2_CTRL_TYPE_INTEGER_MENU时使用。
32. flags:控件的标志。
33. priv:控件的私有指针,供驱动程序使用。控制框架不会触碰该指针。请注意,删除控件时不会释放该指针。如果需要,则可以添加一个新的内部位字段以告诉框架释放此指针。
34. val:控件的新s32值。
35. cur:用于存储当前值的结构。
- cur.val:如果该类型通过u32整数表示(请参阅枚举v4l2_ctrl_type),则为控件的当前值。
36. p_def:该控件的默认值,通过提供一个标准方式通过指针访问控制类型的联合体表示(仅针对复合控件)。
37. p_new:该控件的新值,通过提供一个标准方式通过指针访问控制类型的联合体表示。
38. p_cur:该控件的当前值,通过提供一个标准方式通过指针访问控制类型的联合体表示。
struct v4l2_ctrl_ref
struct v4l2_ctrl_ref是一个控制引用结构体
定义:
struct v4l2_ctrl_ref {
struct list_head node;
struct v4l2_ctrl_ref *next;
struct v4l2_ctrl *ctrl;
struct v4l2_ctrl_helper *helper;
bool from_other_dev;
bool req_done;
bool valid_p_req;
union v4l2_ctrl_ptr p_req;
};
具体包括以下成员:
1. node:用于控制ID排序的链表节点。
2. next:哈希桶中的单链表节点。
3. ctrl:控制信息本身。
4. helper:指向辅助结构体的指针。在v4l2-ctrl的prepare_ext_ctrls函数中内部使用。
5. from_other_dev:如果为true,则ctrl是在另一个设备中定义的,而不是在struct v4l2_ctrl_handler中定义的。
6. req_done:内部标志,如果包含此控制引用的控制处理程序已绑定到媒体请求,则在应用控制后设置该标志。这样可以防止重复应用具有多个控件的簇(当簇的第一个控件被应用时,它们都被应用)。
7. valid_p_req:如果设置,则p_req包含请求的控制值。
8. p_req:如果包含此控制引用的控制处理程序已绑定到媒体请求,则该指针指向执行请求时必须应用的控制的值,或者指向请求完成时控件的值。如果valid_p_req为false,则对于此请求未设置此控件,当应用此请求时,也不会更新此控件。
描述:
每个控制处理程序都有一个这些引用的列表。list_head用于保持所有控件按控件ID排序的列表,而next指针用于将控件链接到哈希的桶中。
struct v4l2_ctrl_handler
struct v4l2_ctrl_handler是控制处理程序结构体,用于跟踪所有控制:包括控制处理程序拥有的控制和从其他处理程序继承的控制。
定义:
struct v4l2_ctrl_handler {
struct mutex _lock;
struct mutex *lock;
struct list_head ctrls;
struct list_head ctrl_refs;
struct v4l2_ctrl_ref *cached;
struct v4l2_ctrl_ref **buckets;
v4l2_ctrl_notify_fnc notify;
void *notify_priv;
u16 nr_of_buckets;
int error;
bool request_is_queued;
struct list_head requests;
struct list_head requests_queued;
struct media_request_object req_obj;
};
具体成员包括:
1. _lock:默认的锁。
2. lock:用于控制访问此处理程序及其控件的锁。在初始化后可以由用户替换。
3. ctrls:此处理程序拥有的控件的列表。
4. ctrl_refs:控制引用的列表。
5. cached:最后找到的控制引用。通常需要多次使用相同的控制,因此这是一个简单的优化。
6. buckets:哈希桶,允许快速查找控制。
7. notify:控制更改值时调用的通知回调。请注意,在调用notify函数时,处理程序的锁已被持有!
8. notify_priv:传递给v4l2_ctrl通知回调的参数。
9. nr_of_buckets:数组中桶的总数。
10. error:第一个失败的控制添加的错误代码。
11. request_is_queued:如果请求已排队,则为True。
12. requests:用于跟踪打开的控制处理程序请求对象的列表。对于父控制处理程序(req_obj.ops == NULL),这是列表头。当移除父控件处理程序时,它必须取消绑定并放置所有这些请求,因为它们是指向父项的。
13. requests_queued:排队请求的列表。这决定了这些控件应用的顺序。一旦请求完成,它就会从此列表中删除。
14. req_obj:用于链接到struct media_request的struct media_request_object。此请求对象具有refcount。
struct v4l2_ctrl_config
是控制配置结构体。
定义:
struct v4l2_ctrl_config {
const struct v4l2_ctrl_ops *ops;
const struct v4l2_ctrl_type_ops *type_ops;
u32 id;
const char *name;
enum v4l2_ctrl_type type;
s64 min;
s64 max;
u64 step;
s64 def;
union v4l2_ctrl_ptr p_def;
u32 dims[V4L2_CTRL_MAX_DIMS];
u32 elem_size;
u32 flags;
u64 menu_skip_mask;
const char * const *qmenu;
const s64 *qmenu_int;
unsigned int is_private:1;
};
具体成员包括:
1. ops:控制操作。
2. type_ops:控制类型操作。仅对复合控件需要。
3. id:控制ID。
4. name:控制名称。
5. type:控制类型。
6. min:控制的最小值。
7. max:控制的最大值。
8. step:非菜单控件的控制步长。
9. def:控制的默认值。
10. p_def:复合控件的默认值。
11. dims:每个维度的大小。
12. elem_size:控制的字节大小。
13. flags:控制的标志。
14. menu_skip_mask:适用于菜单控件的跳过掩码,使得跳过无效的菜单项非常容易。如果设置了位X,则跳过菜单项X。当然,这仅适用于具有<= 64个菜单项的菜单。没有接近该数字的菜单,所以这是可以的。如果我们需要更多,那么它将需要扩展为位阵列。
15. qmenu:const char *数组,用于存储所有菜单项。如果数组条目为空字符串(“”),则对应于不存在的菜单项(除上述menu_skip_mask之外)。最后一项必须为NULL。
16. qmenu_int:const s64整数数组,用于存储V4L2_CTRL_TYPE_INTEGER_MENU类型的所有菜单项。
17. is_private:如果设置,则此控件是其处理程序的私有控件,并且不会添加到任何其他处理程序中。
void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, s64 *min, s64 *max, u64 *step, s64 *def, u32 *flags)
void v4l2_ctrl_fill根据控制ID填充控制字段。
参数包括:
1. id:控制ID。
2. name:指向控制名称字符串的指针。
3. type:用于存储控制类型的指针。
4. min:用于存储控制的最小值的指针。
5. max:用于存储控制的最大值的指针。
6. step:用于存储控制步进的指针。
7. def:用于存储控制默认值的指针。
8. flags:用于存储要在控件上使用的标志的指针。
这适用于所有标准V4L2控件。对于非标准控件,它将仅填充给定的参数,并将名称内容设置为NULL。
此函数将覆盖名称、类型和标志的内容。根据类型,min、max、step和def的内容可能会被修改。
注意:不要在驱动程序中使用!它仅用于向后兼容的控制处理。一旦所有驱动程序都转换为使用新的控制框架,此函数将不再被导出。
int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl, unsigned int nr_of_controls_hint, struct lock_class_key *key, const char *name)
int v4l2_ctrl_handler_init_class函数用于初始化控制处理器。
参数包括:
1. hdl:控制处理器。
2. nr_of_controls_hint:此处理器预计引用的控件数量的提示,包括任何继承的控件。它不必精确,但如果偏差太大,那么就会浪费内存(分配了太多的桶)或者控制查找变慢(分配不足的桶,因此需要更多的慢速列表查找)。但是它总是可以工作的。
3. key:如果设置了CONFIG_LOCKDEP,由锁验证器使用的锁类键。
4. name:如果设置了CONFIG_LOCKDEP,由锁验证器使用的名称。
注意:永远不要直接使用此调用,始终使用隐藏键和名称参数的v4l2_ctrl_handler_init()宏。
返回值:如果无法分配桶,则返回错误。此错误也将存储在hdl->error中。
v4l2_ctrl_handler_init(hdl, nr_of_controls_hint)
v4l2_ctrl_handler_init(hdl, nr_of_controls_hint)是一个辅助函数,用于创建一个静态的struct lock_class_key并调用v4l2_ctrl_handler_init_class()。
参数包括:
1. hdl:控制处理器。
2. nr_of_controls_hint:此处理器预计引用的控件数量的提示,包括任何继承的控件。它不必精确,但如果偏差太大,那么就会浪费内存(分配了太多的桶)或者控制查找变慢(分配不足的桶,因此需要更多的慢速列表查找)。但是它总是可以工作的。
描述:这个辅助函数创建一个静态的struct lock_class_key并调用v4l2_ctrl_handler_init_class(),为锁验证器提供一个合适的名称。使用这个辅助函数初始化一个控制处理器。
注意:这个函数将创建一个静态变量,可能会对内存产生影响。
void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
v4l2_ctrl_handler_free函数用于释放控制处理器拥有的所有控件并释放控件列表。
参数包括:
1. hdl:控制处理器。
描述:如果hdl == NULL,那么什么也不做。
void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl)
v4l2_ctrl_lock是一个辅助函数,用于锁定与控制相关的控制处理器。
参数包括:
1. ctrl:要锁定的控制。
描述:v4l2_ctrl_lock是一个辅助函数,用于在访问控制时锁定控制处理器,以避免并发修改问题。
void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl)
v4l2_ctrl_unlock是一个辅助函数,用于解锁与控制相关的控制处理器。
参数包括:
1. ctrl:要解锁的控制。
描述:v4l2_ctrl_unlock是一个辅助函数,用于在访问控制后解锁控制处理器,以避免并发修改问题。
int __v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
__v4l2_ctrl_handler_setup是一个辅助函数,用于调用处理器中所有控制的s_ctrl操作,以将硬件初始化为当前控制值。调用者需要代表__v4l2_ctrl_handler_setup()获取控制处理器互斥锁。
参数包括:
1. hdl:控制处理器。
描述:这个函数将跳过按钮控件和只读控件。如果hdl == NULL,则返回0。
注意:由于该函数是内部使用的,因此不应直接调用它。
int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
v4l2_ctrl_handler_setup函数用于调用处理器中所有控制的s_ctrl操作,以将硬件初始化为当前控制值。
参数包括:
1. hdl:控制处理器。
描述:此函数将跳过按钮控件和只读控件。如果hdl == NULL,则返回0。
void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl, const char *prefix)
v4l2_ctrl_handler_log_status函数用于记录处理器所拥有的所有控制。
参数包括:
1. hdl:控制处理器。
2. prefix:在记录控制值时要使用的前缀。如果前缀不以空格结尾,则会在前缀后添加“: ”。如果prefix == NULL,则不使用前缀。
描述:该函数可用于VIDIOC_LOG_STATUS操作。如果hdl == NULL,则此函数不执行任何操作。
struct v4l2_ctrl * v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_confifig *cfg, void *priv)
v4l2_ctrl_new_custom函数用于分配和初始化一个新的自定义V4L2控制器。
参数包括:
1. hdl:控制处理器。
2. cfg:控制的配置数据。
3. priv:控制的驱动程序特定私有数据。
描述:如果无法分配v4l2_ctrl结构,则返回NULL,并将hdl->error设置为错误代码(如果尚未设置)。
struct v4l2_ctrl * v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, s64 min, s64 max, u64 step, s64 def)
v4l2_ctrl_new_std函数用于分配和初始化一个新的标准V4L2非菜单控制器。
参数包括:
1. hdl:控制处理器。
2. ops:控制器操作集。
3. id:控制器ID。
4. min:控制器的最小值。
5. max:控制器的最大值。
6. step:控制器的步长。
7. def:控制器的默认值。
描述:如果无法分配v4l2_ctrl结构,或者控制器ID不可识别,则返回NULL,并将hdl->error设置为相应的错误代码(如果尚未设置)。如果id是菜单控件,则该函数将返回NULL。在添加菜单控件时请使用v4l2_ctrl_new_std_menu()。
struct v4l2_ctrl * v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, u8 max, u64 mask, u8 def)
v4l2_ctrl_new_std_menu函数用于分配和初始化一个新的标准V4L2菜单控件。
参数包括:
1. hdl:控制处理器。
2. ops:控制器操作集。
3. id:控制器ID。
4. max:控制器的最大值。
5. mask:菜单控件的跳过掩码。这使得跳过无效的菜单项目变得容易。如果设置了第 X 位,则跳过菜单项 X。当然,这仅适用于具有 <= 64 个菜单项的菜单。没有菜单接近这个数字,所以这是可以接受的。如果我们需要更多,那么这将必须扩展为位数组。
6. def:控制器的默认值。
描述:与v4l2_ctrl_new_std()相同,但min设置为0,mask值确定应跳过哪些菜单项。如果id是非菜单控件,则该函数将返回NULL。
struct v4l2_ctrl * v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, u8 max, u64 mask, u8 def, const char * const *qmenu)
v4l2_ctrl_new_std_menu_items函数用于创建一个新的标准V4L2菜单控件,并加入具有驱动程序特定菜单项的菜单。
参数包括:
1. hdl:控制处理器。
2. ops:控制器操作集。
3. id:控制器ID。
4. max:控制器的最大值。
5. mask:菜单控件的跳过掩码。这使得跳过无效的菜单项目变得容易。如果设置了第 X 位,则跳过菜单项 X。当然,这仅适用于具有 <= 64 个菜单项的菜单。没有菜单接近这个数字,所以这是可以接受的。如果我们需要更多,那么这将必须扩展为位数组。
6. def:控制器的默认值。
7. qmenu:新的菜单。
描述:与v4l2_ctrl_new_std_menu()相同,但qmenu将是此控件的驱动程序特定菜单。
struct v4l2_ctrl * v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, const union v4l2_ctrl_ptr p_def)
v4l2_ctrl_new_std_compound函数用于分配并初始化一个新的标准V4L2复合控件。
参数包括:
1. hdl:控制处理器。
2. ops:控制器操作集。
3. id:控制器ID。
4. p_def:控制器的默认值。
描述:与v4l2_ctrl_new_std()相同,但由于p_def字段的支持,支持复合控件。使用v4l2_ctrl_ptr_create()从指针创建p_def。如果复合控制的默认值应为所有零,请使用v4l2_ctrl_ptr_create(NULL)。
struct v4l2_ctrl * v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, u8 max, u8 def, const s64 *qmenu_int)
v4l2_ctrl_new_int_menu函数用于创建一个新的标准V4L2整数菜单控制器。
参数包括:
1. hdl:控制器处理器。
2. ops:控制器操作集。
3. id:控制器ID。
4. max:控制器的最大值。
5. def:控制器的默认值。
6. qmenu_int:控制器的菜单项。
描述:与v4l2_ctrl_new_std_menu()相同,但掩码被设置为0,并且它还需要一个由整数确定菜单项的数组作为参数。如果id引用非整数菜单控件,则此函数将返回NULL。
bool v4l2_ctrl_filter (const struct v4l2_ctrl *ctrl)
v4l2_ctrl_filter函数用于过滤V4L2控制器,确定它是否应该被使用。
参数包括:
1. ctrl:指向struct v4l2_ctrl的指针。
描述:该函数返回一个布尔值,表示控制器是否应该被使用。通常情况下,如果控制器是已选择的、为用户空间可见的或用户空间不能更改的,则可以使用该控制器。
int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl_handler *add, v4l2_ctrl_fifilter fifilter, bool from_other_dev)
v4l2_ctrl_add_handler函数用于将另一个控制器处理器add中的所有控制器添加到hdl控制器处理器中。
参数包括:
1. hdl:控制器处理器。
2. add:要将其控制器添加到hdl控制器处理器的控制器处理器。
3. filter:此函数将筛选应添加哪些控件。
4. from_other_dev:如果为true,则add中的控件是在与hdl不同的设备中定义的。
描述:如果两个处理程序中有一个是NULL指针,则此函数不执行任何操作。如果filter为NULL,则添加所有控制器。否则,仅添加filter返回true的控制器。如果出现错误,hdl->error将设置为错误代码(如果尚未设置)。
bool v4l2_ctrl_radio_filter(const struct v4l2_ctrl *ctrl)
v4l2_ctrl_radio_filter函数是无线电控制器的标准过滤器。
参数包括:
1. ctrl:要进行筛选的控件。
描述:对于无线电设备节点有效的所有控件,此函数将返回true。这些是所有V4L2_CID_AUDIO_*用户控制和所有FM发射机类控制。
此函数通常与v4l2_ctrl_add_handler()一起使用。
void v4l2_ctrl_cluster(unsigned int ncontrols, struct v4l2_ctrl **controls)
v4l2_ctrl_cluster函数用于将控制器群集中的所有控制器标记为属于该群集。
参数包括:
1. ncontrols:此群集中的控制器数。
2. controls:大小为ncontrols的群集控制器数组。
描述:此函数将在控制器对象中设置群集标志。群集中的控制器可以具有相同的值(例如:Video Standard),并且它们通常在UI中共享单个控件的自由度,因此这些控件应该被组合为一个群集。
void v4l2_ctrl_auto_cluster(unsigned int ncontrols, struct v4l2_ctrl **controls, u8 manual_val, bool set_volatile)
v4l2_ctrl_auto_cluster函数用于将控制器群集中的所有控制器标记为属于该群集,并设置为自动/手动模式处理。
参数包括:
1. ncontrols:此群集中的控制器数。
2. controls:大小为ncontrols的群集控制器数组。第一个控制器必须是“auto”控制器(例如:autogain、autoexposure等)。
3. manual_val:在群集中与手动设置相等的第一个控制器的值。
4. set_volatile:如果为true,则除第一个自动控制器外的所有控制器都将是易失性的。
描述:这个函数通常用于控制组,其中一个控制器选择某个自动功能,而其他控制器只有在关闭自动功能(手动模式)时才处于活动状态。典型的例子:自动增益(autogain)和增益(gain),自动白平衡(auto-whitebalance)和红色和蓝色平衡等等。
这种控件的行为如下:
当自动控制器设置为自动时,所有手动控制器都设置为非活动状态,并且所有读取将调用g_volatile_ctrl(如果标记为易失性控制器)。
当自动控制器设置为手动模式时,所有手动控制器将被标记为活动状态,并且所有读取将直接返回当前值而无需通过g_volatile_ctrl。
此外,此函数会在自动控制器上设置V4L2_CTRL_FLAG_UPDATE标志,并在autofoo处于自动模式时,在foo控制器上设置V4L2_CTRL_FLAG_INACTIVE标志。
struct v4l2_ctrl * v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
v4l2_ctrl_find函数用于查找具有给定ID的控制器。
参数包括:
1. hdl:控制器处理程序。
2. id:要查找的控制器ID。
描述:如果hdl == NULL,则此函数也将返回NULL。该函数会锁定处理程序,因此不要从v4l2_ctrl_ops内部使用它。
void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active)
v4l2_ctrl_activate函数用于激活或非激活控制器。
参数包括:
1. ctrl:要(取消)激活的控制器。
2. active:如果控制器应变为活动状态,则为true。
描述:此函数以原子方式设置或清除V4L2_CTRL_FLAG_INACTIVE标志。如果ctrl==NULL,则不执行任何操作。通常会从s_ctrl操作中调用该函数。然后将生成V4L2_EVENT_CTRL事件。该函数假定控制器处理程序已被锁定。
void __v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
__v4l2_ctrl_grab函数是v4l2_ctrl_grab的未锁定变体。
参数包括:
1. ctrl:要(取消)激活的控制器。
2. grabbed:如果控制器应变为抓取状态,则为true。
描述:此函数以原子方式设置或清除V4L2_CTRL_FLAG_GRABBED标志。如果ctrl==NULL,则不执行任何操作。然后将生成V4L2_EVENT_CTRL事件。这通常在驱动程序中启动或停止流时调用。该函数假定调用者已锁定控制器处理程序。
void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
v4l2_ctrl_grab函数用于标记控制器是否被抓取。
参数包括:
1. ctrl:要(取消)激活的控制器。
2. grabbed:如果控制器应变为抓取状态,则为true。
描述:此函数以原子方式设置或清除V4L2_CTRL_FLAG_GRABBED标志。如果ctrl==NULL,则不执行任何操作。然后将生成V4L2_EVENT_CTRL事件。这通常在驱动程序中启动或停止流时调用。该函数假定控制器处理程序未被锁定,并将自己获取锁。
int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, s64 min, s64 max, u64 step, s64 def)
__v4l2_ctrl_modify_range函数是v4l2_ctrl_modify_range的未锁定变体。
参数包括:
1. ctrl:要更新的控制器。
2. min:控制器的最小值。
3. max:控制器的最大值。
4. step:控制器的步长值。
5. def:控制器的默认值。
描述:在运行时更新控件的范围。这适用于控件类型为INTEGER、BOOLEAN、MENU、INTEGER MENU和BITMASK的情况。对于菜单控件,步进值被解释为菜单跳过掩码。如果一个范围参数对于该控制器类型无效,则返回错误。调用者负责代表 __v4l2_ctrl_modify_range() 获取控制器处理程序互斥锁。
int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, s64 min, s64 max, u64 step, s64 def)
v4l2_ctrl_modify_range函数用于更新控制器的范围。
参数包括:
1. ctrl:要更新的控制器。
2. min:控制器的最小值。
3. max:控制器的最大值。
4. step:控制器的步长值。
5. def:控制器的默认值。
描述:在运行时更新控件的范围。这适用于控件类型为INTEGER、BOOLEAN、MENU、INTEGER MENU和BITMASK的情况。对于菜单控件,步进值被解释为菜单跳过掩码。如果一个范围参数对于该控制器类型无效,则返回错误。该函数假定控制器处理程序未被锁定,并将自己获取锁。
void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv)
v4l2_ctrl_notify函数用于为控制器设置通知回调函数。
参数包括:
1. ctrl:控制器。
2. notify:回调函数。
3. priv:传递给回调函数的回调私有句柄参数。
描述:该函数为控制器设置一个回调函数。如果ctrl为NULL,则不执行任何操作。如果notify为NULL,则通知回调将被删除。
只能有一个通知回调函数。如果已经存在另一个通知回调函数,则会发出WARN_ON警告并且该函数将不执行任何操作。
const char * v4l2_ctrl_get_name(u32 id)
v4l2_ctrl_get_name函数用于获取控制器名称。
参数包括:
1. id:控制器ID。
描述:该函数返回给定控制器ID的名称,如果它不是已知控制器,则返回NULL。
const char * const * v4l2_ctrl_get_menu(u32 id)
v4l2_ctrl_get_menu函数用于获取控制器菜单字符串数组。
参数包括:
1. id:控制器ID。
描述:该函数返回给定控制器ID的空终止菜单字符串数组名称,如果它不是已知的菜单控件,则返回NULL。
const s64 * v4l2_ctrl_get_int_menu(u32 id, u32 *len)
v4l2_ctrl_get_int_menu函数用于获取控制器整数菜单数组。
参数包括:
1. id:控制器ID。
2. len:整数数组的大小。
描述:该函数返回给定控制器ID的整数数组,如果它不是已知的整数菜单控件,则返回NULL。
s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl)
v4l2_ctrl_g_ctrl函数用于从驱动程序内部获取控制器的值的帮助函数。
参数包括:
1. ctrl:控制器。
描述:该函数通过控制框架安全地返回控制器的值。此函数将锁定控制器的处理程序,因此无法从v4l2_ctrl_ops函数中使用该函数。
此函数仅适用于整数类型控件。
int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
__v4l2_ctrl_s_ctrl函数是v4l2_ctrl_s_ctrl的未锁定版本。
参数包括:
1. ctrl:控制器。
2. val:新值。
描述:该函数通过控制框架安全地设置控制器的新值。此函数假定控制器的处理程序已经被锁定,允许从v4l2_ctrl_ops函数中使用它。
此函数仅适用于整数类型控件。
int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
v4l2_ctrl_s_ctrl函数用于从驱动程序内部设置控制器的值的帮助函数。
参数包括:
1. ctrl:控制器。
2. val:新值。
描述:该函数通过控制框架安全地设置控制器的新值。此函数将锁定控制器的处理程序,因此无法从v4l2_ctrl_ops函数中使用该函数。
此函数仅适用于整数类型控件。
s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl)
v4l2_ctrl_g_ctrl_int64函数用于从驱动程序内部获取控制器整数值的帮助函数。
参数包括:
1. ctrl:控制器。
描述:该函数通过控制框架安全地返回控制器的整数值(64位)。此函数将锁定控制器的处理程序,因此无法从v4l2_ctrl_ops函数中使用该函数。
该函数仅适用于整数类型控件(64位)。
int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val)
__v4l2_ctrl_s_ctrl_int64函数是v4l2_ctrl_s_ctrl_int64的未锁定版本。
参数包括:
1. ctrl:控制器。
2. val:新值。
描述:该函数通过控制框架安全地设置控制器的新值。此函数假定控制器的处理程序已经被锁定,允许从v4l2_ctrl_ops函数中使用它。
此函数仅适用于64位整数类型控件。
int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val)
v4l2_ctrl_s_ctrl_int64函数用于从驱动程序内部设置64位控制器的值的帮助函数。
参数包括:
1. ctrl:控制器。
2. val:新值。
描述:该函数通过控制框架安全地设置控制器的新值。此函数将锁定控制器的处理程序,因此无法从v4l2_ctrl_ops函数中使用该函数。
此函数仅适用于64位整数类型控件。
int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s)
__v4l2_ctrl_s_ctrl_string函数是v4l2_ctrl_s_ctrl_string的未锁定版本。
参数包括:
1. ctrl:控制器。
2. s:新字符串。
描述:该函数通过控制框架安全地设置控制器的新字符串。此函数假定控制器的处理程序已经被锁定,允许从v4l2_ctrl_ops函数中使用它。
此函数仅适用于字符串类型控件。
int v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s)
v4l2_ctrl_s_ctrl_string函数用于从驱动程序内部设置控制器的字符串值的帮助函数。
参数包括:
1. ctrl:控制器。
2. s:新字符串。
描述:该函数通过控制框架安全地设置控制器的新字符串。此函数将锁定控制器的处理程序,因此无法从v4l2_ctrl_ops函数中使用该函数。
此函数仅适用于字符串类型控件。
int __v4l2_ctrl_s_ctrl_compound(struct v4l2_ctrl *ctrl, enum v4l2_ctrl_type type, const void *p)
__v4l2_ctrl_s_ctrl_compound函数是v4l2_ctrl_s_ctrl_compound的未锁定版本,用于设置复合控制器。
参数包括:
1. ctrl:控制器。
2. type:数据类型。
3. p:新的复合负载。
描述:该函数通过控制框架安全地设置控制器的新复合负载。此函数假定控制器的处理程序已经被锁定,允许从v4l2_ctrl_ops函数中使用它。
此函数仅适用于复合类型控件。
int v4l2_ctrl_s_ctrl_compound(struct v4l2_ctrl *ctrl, enum v4l2_ctrl_type type, const void *p)
v4l2_ctrl_s_ctrl_compound函数用于从驱动程序内部设置复合控制器的帮助函数。
参数包括:
1. ctrl:控制器。
2. type:数据类型。
3. p:新的复合负载。
描述:该函数通过控制框架安全地设置控制器的新复合负载。此函数将锁定控制器的处理程序,因此无法从v4l2_ctrl_ops函数中使用该函数。
此函数仅适用于复合类型控件。
void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new)
v4l2_ctrl_replace函数是用作struct v4l2_subscribed_event_ops replace()回调的函数。
参数包括:
1. old:指向报告的事件的struct v4l2_event指针;
2. new:指向修改后的事件的struct v4l2_event指针。
描述:该函数被用作回调,用于在事件订阅中替换旧的报告事件。该函数将新的事件结构体复制到旧的事件结构体中,以便更新事件信息。
void v4l2_ctrl_merge(const struct v4l2_event *old, struct v4l2_event *new)
v4l2_ctrl_merge函数是用作struct v4l2_subscribed_event_ops merge()回调的函数。
参数包括:
1. old:指向报告的事件的struct v4l2_event指针;
2. new:指向合并后的事件的struct v4l2_event指针。
描述:该函数被用作回调,用于在事件订阅中合并旧的报告事件和新的事件。该函数将旧的事件结构体复制到新的事件结构体中,以保留旧的事件信息。然后,它将新的事件结构体中的任何更改应用于旧的事件结构体中的字段。
int v4l2_ctrl_log_status(struct file *fifile, void *fh)
v4l2_ctrl_log_status函数是用于实现VIDIOC_LOG_STATUS ioctl的帮助函数。
参数包括:
1. file:指向struct file的指针;
2. fh:未使用。仅为与struct v4l2_ioctl_ops.vidioc_log_status期望的参数兼容而保留。
描述:该函数可以作为一个vidioc_log_status函数,只需要转储与文件句柄关联的所有控件即可。它通过遍历与文件句柄关联的控件列表,并打印每个控件的名称、ID、值和范围等信息来实现此操作。
int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
v4l2_ctrl_subscribe_event函数用于订阅事件。
参数包括:
1. fh:指向struct v4l2_fh的指针;
2. sub:指向struct v4l2_event_subscription的指针。
描述:该函数可以作为vidioc_subscribe_event函数使用,仅订阅控件事件。它将sub指定的事件订阅添加到fh指向的文件句柄的事件订阅列表中。在添加订阅后,包括v4l2_ctrl_request、v4l2_ctrl_notify和v4l2_ctrl_subscribe等函数将向订阅列表中的所有事件发送通知。
__poll_t v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait)
v4l2_ctrl_poll函数是用作poll()回调的函数,仅用于轮询控件事件。
参数包括:
1. file:指向struct file的指针;
2. wait:指向struct poll_table_struct的指针。
描述:该函数被用作回调,在应用程序使用poll()函数时轮询与文件句柄关联的控件事件。如果存在未处理的事件,则该函数将返回POLLIN标志。否则,该函数将wait添加到事件订阅列表中,并挂起进程,直到事件通知唤醒它。
int v4l2_ctrl_request_setup(struct media_request *req, struct v4l2_ctrl_handler *parent)
v4l2_ctrl_request_setup函数是用于在请求中应用控件值的帮助函数。
参数包括:
1. req:指向struct media_request的指针;
2. parent:指向父控件处理程序struct v4l2_ctrl_handler的指针(“priv”在media_request_object_find()中)。
描述:这是一个帮助函数,用于使用请求中包含的控件值调用控件处理程序的s_ctrl回调函数。需要注意的是,这种在请求中应用控件值的方法仅适用于内存到内存的设备。函数会首先获取请求中的控件操作,并通过标识符在父控件处理程序中找到对应的控件链表,然后通过调用s_ctrl回调来为控件设置值。
void v4l2_ctrl_request_complete(struct media_request *req, struct v4l2_ctrl_handler *parent)
v4l2_ctrl_request_complete函数用于完成控件处理程序请求对象。
参数包括:
1. req:指向struct media_request的指针;
2. parent:指向父控件处理程序struct v4l2_ctrl_handler的指针(“priv”在media_request_object_find()中)。
描述:该函数用于每个控件处理程序的请求对象,即支持请求的驱动程序的控件处理程序。首先,函数获取控件处理程序中任何易失性控件的值,并将其附加到请求中。然后,该函数完成请求对象。函数完成请求后,请求将进入V4L2驱动程序的请求队列,并等待上下文完成该请求。
struct v4l2_ctrl_handler * v4l2_ctrl_request_hdl_find(struct media_request *req, struct v4l2_ctrl_handler *parent)
v4l2_ctrl_request_hdl_find函数用于在请求中查找控件处理程序。
参数包括:
1. req:指向struct media_request的指针;
2. parent:指向父控件处理程序struct v4l2_ctrl_handler的指针(“priv”在media_request_object_find()中)。
描述:该函数用于查找请求中的控件处理程序,如果未找到则可能返回空指针。完成后,必须使用返回的处理程序指针调用v4l2_ctrl_request_put_hdl()函数。
注意:如果请求不处于VALIDATING或QUEUED状态,则此函数将始终返回NULL。在VALIDATING状态下,req_queue_mutex被持有,因此无法添加或删除对象。在QUEUED状态下,需要驱动程序来确保此点。
void v4l2_ctrl_request_hdl_put(struct v4l2_ctrl_handler *hdl)
v4l2_ctrl_request_hdl_put函数用于释放先前从v4l2_ctrl_request_hdl_find()函数获取的控件处理程序。
参数包括:
1. hdl:要释放的控件处理程序指针。
描述:该函数将释放先前从v4l2_ctrl_request_hdl_find()函数获取的控件处理程序。一旦不再使用,必须调用此函数以避免内存泄漏。
struct v4l2_ctrl * v4l2_ctrl_request_hdl_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
v4l2_ctrl_request_hdl_ctrl_find函数用于查找具有给定ID的控件。
参数包括:
1. hdl:请求中的控件处理程序指针;
2. id:要查找的控件ID。
描述:该函数返回指向控件的指针,如果该控件是请求的一部分,则返回该指针;否则返回空指针。
int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
v4l2_queryctrl函数是用于实现VIDIOC_QUERYCTRL ioctl的辅助函数。
参数包括:
1. hdl:指向struct v4l2_ctrl_handler的指针;
2. qc:指向struct v4l2_queryctrl的指针。
描述:该函数用于查询控件ID,并将其属性存储在struct v4l2_queryctrl中。如果hdl为空指针,则函数返回-EINVAL。
int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctrl *qc)
v4l2_query_ext_ctrl函数是用于实现VIDIOC_QUERY_EXT_CTRL ioctl的辅助函数。
参数包括:
1. hdl:指向struct v4l2_ctrl_handler的指针;
2. qc:指向struct v4l2_query_ext_ctrl的指针。
描述:该函数用于查询扩展控件ID,并将其属性存储在struct v4l2_query_ext_ctrl中。如果hdl为空指针,则函数返回-EINVAL。
int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
v4l2_querymenu函数是用于实现VIDIOC_QUERYMENU ioctl的辅助函数。
参数包括:
1. hdl:指向struct v4l2_ctrl_handler的指针;
2. qm:指向struct v4l2_querymenu的指针。
描述:该函数用于查询控件的菜单,并将其属性存储在struct v4l2_querymenu中。如果hdl为空指针,则函数返回-EINVAL。
int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *ctrl)
v4l2_g_ctrl函数是用于实现VIDIOC_G_CTRL ioctl的辅助函数。
参数包括:
1. hdl:指向struct v4l2_ctrl_handler的指针;
2. ctrl:指向struct v4l2_control的指针。
描述:该函数用于获取控件的当前值,并将其存储在struct v4l2_control中。如果hdl为空指针,则函数返回-EINVAL。
int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, struct v4l2_control *ctrl)
v4l2_s_ctrl函数是用于实现VIDIOC_S_CTRL ioctl的辅助函数。
参数包括:
1. fh:指向struct v4l2_fh的指针;
2. hdl:指向struct v4l2_ctrl_handler的指针;
3. ctrl:指向struct v4l2_control的指针。
描述:该函数用于设置控件的值,并将其存储在struct v4l2_control中。如果hdl为空指针,则函数返回-EINVAL。
int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct video_device *vdev, struct media_device *mdev, struct v4l2_ext_controls *c)
v4l2_g_ext_ctrls函数是用于实现VIDIOC_G_EXT_CTRLS ioctl的辅助函数。
参数包括:
1. hdl:指向struct v4l2_ctrl_handler的指针;
2. vdev:指向struct video_device的指针;
3. mdev:指向struct media_device的指针;
4. c:指向struct v4l2_ext_controls的指针。
描述:该函数用于获取扩展控件组的值,并将控件组属性存储在struct v4l2_ext_controls中。如果hdl为空指针,则函数返回-EINVAL。
int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct video_device *vdev, struct media_device *mdev, struct v4l2_ext_controls *c)
v4l2_try_ext_ctrls函数是用于实现VIDIOC_TRY_EXT_CTRLS ioctl的辅助函数。
参数包括:
1. hdl:指向struct v4l2_ctrl_handler的指针;
2. vdev:指向struct video_device的指针;
3. mdev:指向struct media_device的指针;
4. c:指向struct v4l2_ext_controls的指针。
描述:该函数用于尝试设置扩展控件组的值,并将控件组属性存储在struct v4l2_ext_controls中。如果hdl为空指针,则函数返回-EINVAL。
int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, struct video_device *vdev, struct media_device *mdev, struct v4l2_ext_controls *c)
v4l2_s_ext_ctrls函数是用于实现VIDIOC_S_EXT_CTRLS ioctl的辅助函数。
参数包括:
1. fh:指向struct v4l2_fh的指针;
2. hdl:指向struct v4l2_ctrl_handler的指针;
3. vdev:指向struct video_device的指针;
4. mdev:指向struct media_device的指针;
5. c:指向struct v4l2_ext_controls的指针。
描述:该函数用于设置扩展控件组的值,并将控件组属性存储在struct v4l2_ext_controls中。如果hdl为空指针,则函数返回-EINVAL。
int v4l2_ctrl_subdev_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub)
v4l2_ctrl_subdev_subscribe_event函数是用于实现作为struct v4l2_subdev_core_ops的subscribe_event函数的辅助函数,它仅订阅控制事件。
参数包括:
1. sd:指向struct v4l2_subdev的指针;
2. fh:指向struct v4l2_fh的指针;
3. sub:指向struct v4l2_event_subscription的指针。
描述:该函数用于将控制事件订阅到sd所代表的子设备上。它是一个辅助函数,可以实现为struct v4l2_subdev_core_ops的subscribe_event函数。
int v4l2_ctrl_subdev_log_status(struct v4l2_subdev *sd)
v4l2_ctrl_subdev_log_status函数用于记录由子设备的控制处理程序拥有的所有控件的状态。
参数包括:
1. sd:指向struct v4l2_subdev的指针。
描述:该函数用于记录由sd所代表的子设备中的控制处理程序拥有的所有控件的状态信息。它将打印控件的名称、当前值和范围,以及其他相关信息。
int v4l2_ctrl_new_fwnode_properties(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ctrl_ops, const struct v4l2_fwnode_device_properties *p)
v4l2_ctrl_new_fwnode_properties函数用于注册与设备属性相关的控件,使用包含在参数p中的属性值(如果属性已设置为某个值)。
参数包括:
1. hdl:指向要在其上注册控制的struct v4l2_ctrl_handler的指针;
2. ctrl_ops:指向要注册控制的struct v4l2_ctrl_ops的指针;
3. p:指向struct v4l2_fwnode_device_properties的指针。
描述:该函数用于解析和注册与设备属性相关联的控件。目前,函数支持解析和注册的v4l2控件是:
- V4L2_CID_CAMERA_ORIENTATION - V4L2_CID_CAMERA_SENSOR_ROTATION。
如果调用者已经通过hdl控制处理程序注册了控件,则不会被覆盖。在调用此函数之前,调用者应该先注册自己要处理的控件。
返回值:成功时返回0,失败时返回负数错误代码。
2.2.14 Videobuf Framework
注:videobuf框架已弃用,建议使用videobuf2。不应该在新的驱动程序中使用。
2.2.14.1 Introduction
videobuf层作为V4L2驱动程序和用户空间之间的一种粘合层。它处理为存储视频帧而分配和管理缓冲区。它有一组函数,可以用于实现许多标准POSIX I/O系统调用,包括read()、poll()和mmap()。另一组函数可用于实现与流I/O相关的大部分V4L2 ioctl()调用,包括缓冲区分配、排队和出队以及流控制。使用videobuf对驱动程序作者施加了一些设计决策的限制,但回报是减少了驱动程序中的代码,并且保持了V4L2用户空间API的一致实现。
2.2.14.2 Buffer types
并非所有视频设备都使用相同类型的缓冲区。实际上,至少有三种常见变体:
• 在物理和(内核)虚拟地址空间中分散的缓冲区。几乎所有用户空间缓冲区都是这样的,但当可能时,为内核空间分配这种方式的缓冲区也是很有意义的。不幸的是,这种类型的缓冲区通常需要能够执行分散/聚集DMA操作的硬件来使用。
• 物理上分散但在虚拟上是连续的缓冲区;换句话说,使用vmalloc()分配的缓冲区。这些缓冲区对于DMA操作同样难以使用,但在DMA不可用但虚拟连续缓冲区很方便的情况下可以派上用场。
• 物理上连续的缓冲区。在碎片化的系统上,分配这种类型的缓冲区可能不可靠,但更简单的DMA控制器无法处理其他任何类型的缓冲区。
Videobuf可以与所有三种类型的缓冲区一起工作,但驱动程序的作者必须在开始时选择其中一种类型,并围绕该决策设计驱动程序。
值得注意的是,还有第四种类型的缓冲区,即位于系统视频内存中的"覆盖"缓冲区。虽然覆盖功能在大多数情况下已被认为是过时的,但在某些系统级芯片的驱动程序中,由于性能优势明显,偶尔还会出现使用这种技术的情况。覆盖缓冲区可以作为一种散布式缓冲区来处理,但在内核中实现这种技术的数量非常有限,因此本文档的范围不包括对该技术的详细描述。
DMA控制器要求物理内存连续么?
DMA控制器通常对物理内存的连续性没有硬性要求。传统上,DMA控制器可以使用两种方式来处理内存传输:基于物理内存地址的传输和基于虚拟内存地址的传输。
在基于物理内存地址的传输中,DMA控制器会直接访问物理内存,并且传输的数据必须在物理内存中是连续的。这是因为DMA控制器通过内存控制器直接与物理内存进行通信,需要按照物理内存的布局进行操作。
然而,在现代计算机系统中,基于虚拟内存地址的传输更为常见。在这种情况下,DMA控制器通过内存管理单元(MMU)将虚拟内存地址转换为物理内存地址。由于内存管理单元可以将虚拟内存地址映射到物理内存中的任意位置,因此物理内存的连续性对于DMA控制器的工作不是严格要求。
虽然大多数DMA控制器不要求物理内存连续,但某些特殊情况下可能存在一些限制。例如,某些硬件设备可能要求DMA缓冲区必须是物理内存连续的,以满足其特定的存储器访问要求。在这种情况下,应根据设备的要求来分配连续的物理内存区域。
总之,一般情况下DMA控制器对物理内存的连续性没有严格要求,因为虚拟内存地址转换可以允许非连续的物理内存使用。然而,在特定硬件设备要求下,可能需要分配连续的物理内存区域。
2.2.14.3 Data structures, callbacks, and initialization
根据使用的缓冲区类型不同,驱动程序应包含以下文件之一:
<media/videobuf-dma-sg.h> /* Physically scattered */
<media/videobuf-vmalloc.h> /* vmalloc() buffers */
<media/videobuf-dma-contig.h> /* Physically contiguous */
驱动程序描述V4L2设备的数据结构应该包括`struct videobuf_queue`的一个实例,用于管理缓冲区队列,以及一个`list_head`用于可用缓冲区的队列。还需要一个中断安全的自旋锁,用于保护至少队列。
下一步是编写四个简单的回调函数,以帮助`videobuf`处理缓冲区的管理:
struct videobuf_queue_ops {
int (*buf_setup)(struct videobuf_queue *q,
unsigned int *count, unsigned int *size);
int (*buf_prepare)(struct videobuf_queue *q,
struct videobuf_buffer *vb,
enum v4l2_field field);
void (*buf_queue)(struct videobuf_queue *q,
struct videobuf_buffer *vb);
void (*buf_release)(struct videobuf_queue *q,
struct videobuf_buffer *vb);
};
在I/O过程的早期,当开始流传输时,会调用`buf_setup()`函数;其目的是告诉`videobuf`关于I/O流的信息。`count`参数将是建议使用的缓冲区数量;驱动程序应检查其合理性,并在需要时进行调整。作为实际规则,至少需要两个缓冲区进行正确的流传输,并且通常每个设备都有一个合理的最大值(不能超过32)。`size`参数应设置为每帧数据的预期(最大)大小。
每个缓冲区(以`struct videobuf_buffer`指针的形式)将传递给`buf_prepare()`函数,该函数应正确设置缓冲区的大小、宽度、高度和字段属性。如果缓冲区的状态字段为`VIDEOBUF_NEEDS_INIT`,驱动程序应将其传递给:
int videobuf_iolock(struct videobuf_queue* q, struct videobuf_buffer *vb,
struct v4l2_framebuffer *fbuf);
除其他操作外,此调用通常会为缓冲区分配内存。最后,`buf_prepare()`函数应将缓冲区的状态设置为`VIDEOBUF_PREPARED`。
当缓冲区排队进行I/O时,它会被传递给`buf_queue()`函数,该函数应将其放入驱动程序的可用缓冲区列表,并将其状态设置为`VIDEOBUF_QUEUED`。请注意,此函数在持有队列自旋锁的情况下被调用;如果它尝试再次获取锁,将会停滞不前。是的,这是经验之谈。还要注意,`videobuf`可能会在队列中的第一个缓冲区上等待;将其他缓冲区放在其前面可能会导致问题。因此,请使用`list_add_tail()`函数将缓冲区加入队列。
最后,当不再使用缓冲区时,会调用`buf_release()`函数。驱动程序应确保缓冲区上没有活动的I/O,然后将其传递给相应的释放函数:
/* Scatter/gather drivers */
int videobuf_dma_unmap(struct videobuf_queue *q,
struct videobuf_dmabuf *dma);
int videobuf_dma_free(struct videobuf_dmabuf *dma);
/* vmalloc drivers */
void videobuf_vmalloc_free (struct videobuf_buffer *buf);
/* Contiguous drivers */
void videobuf_dma_contig_free(struct videobuf_queue *q,
struct videobuf_buffer *buf);
确保缓冲区不再处于I/O状态的一种方式是将其传递给:
int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr);
在这里,`vb`表示缓冲区,`non_blocking`表示是否应该使用非阻塞的I/O(在`buf_release()`情况下应该为零),而`intr`控制是否使用可中断的等待。
2.2.14.4 File operations
在这一点上,大部分工作已经完成;其余的工作是将`videobuf`调用嵌入到其他驱动程序回调函数的实现中。第一步是在`open()`函数中初始化`videobuf`队列。要使用的函数取决于所使用的缓冲区类型:
void videobuf_queue_sg_init(struct videobuf_queue *q,
struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int msize,
void *priv);
void videobuf_queue_vmalloc_init(struct videobuf_queue *q,
struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int msize,
void *priv);
void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int msize,
void *priv);
在每种情况下,参数是相同的:`q`是设备的队列结构,`ops`是上述描述的回调函数集,`dev`是此视频设备的设备结构,`irqlock`是一个中断安全的自旋锁,用于保护数据结构的访问,`type`是设备使用的缓冲区类型(例如,摄像头将使用`V4L2_BUF_TYPE_VIDEO_CAPTURE`),`field`描述要捕获的字段(对于渐进式设备通常为`V4L2_FIELD_NONE`),`msize`是围绕`struct videobuf_buffer`使用的任何包含结构的大小,`priv`是一个私有数据指针,出现在`struct videobuf_queue`的`priv_data`字段中。请注意,这些都是无返回值的函数,显然不会发生失败。
V4L2捕获驱动程序可以编写以支持两种API之一:`read()`系统调用和相对复杂的流式机制。作为一般规则,必须同时支持两者,以确保所有应用程序都有与设备配合工作的机会。`Videobuf`使使用相同的代码实现这一点变得容易。为了实现`read()`,驱动程序只需要调用以下其中之一:
ssize_t videobuf_read_one(struct videobuf_queue *q,
char __user *data, size_t count,
loff_t *ppos, int nonblocking);
ssize_t videobuf_read_stream(struct videobuf_queue *q,
char __user *data, size_t count,
loff_t *ppos, int vbihack, int nonblocking);
这两个函数中的任意一个都会将帧数据读入`data`,并返回实际读取的量;不同之处在于,`videobuf_read_one()`只会读取单个帧,而`videobuf_read_stream()`如果需要满足应用程序请求的计数,将读取多个帧。典型的驱动程序`read()`实现将启动捕获引擎,在调用上述函数之前,然后在返回之前停止引擎(尽管更智能的实现可能会预期在不久的将来发生另一个`read()`调用,并保持引擎运行一段时间)。
`poll()`函数通常可以直接调用以下函数来实现:
unsigned int videobuf_poll_stream(struct file *file,
struct videobuf_queue *q,
poll_table *wait);
需要注意的是,最终使用的等待队列将是与第一个可用缓冲区相关联的队列。
当对内核空间缓冲区进行流式I/O时,驱动程序必须支持`mmap()`系统调用,以使用户空间能够访问数据。在许多V4L2驱动程序中,常见复杂的`mmap()`实现简化为单个调用:
int videobuf_mmap_mapper(struct videobuf_queue *q,
struct vm_area_struct *vma);
其他所有的处理都由`videobuf`代码处理。
`release()`函数需要调用两个独立的`videobuf`函数:
void videobuf_stop(struct videobuf_queue *q);
int videobuf_mmap_free(struct videobuf_queue *q);
调用`videobuf_stop()`函数会终止任何正在进行的I/O操作,但是驱动程序仍然需要停止捕获引擎。调用`videobuf_mmap_free()`函数将确保所有缓冲区都已取消映射;如果成功取消映射,它们将都传递给`buf_release()`回调函数。如果仍然存在映射的缓冲区,`videobuf_mmap_free()`函数将返回一个错误代码。显然,其目的是在仍有映射的缓冲区时导致关闭文件描述符失败,但是在2.6.32内核中的每个驱动程序都会忽略它的返回值。
2.2.14.5 ioctl() operations
V4L2 API包括一个非常长的驱动程序回调函数列表,用于响应提供给用户空间的许多`ioctl()`命令。其中一些(与流式I/O相关的)几乎直接转换为`videobuf`调用。相关的辅助函数包括:
int videobuf_reqbufs(struct videobuf_queue *q,
struct v4l2_requestbuffers *req);
int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b);
int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b);
int videobuf_dqbuf(struct videobuf_queue *q, struct v4l2_buffer *b,
int nonblocking);
int videobuf_streamon(struct videobuf_queue *q);
int videobuf_streamoff(struct videobuf_queue *q);
例如,对于`VIDIOC_REQBUFS`调用,将转换为对驱动程序的`vidioc_reqbufs()`回调函数的调用,该回调通常只需要找到正确的`struct videobuf_queue`指针并将其传递给`videobuf_reqbufs()`函数。这些支持函数可以在许多V4L2驱动程序中替代大量的缓冲区管理样板代码。
`vidioc_streamon()`和`vidioc_streamoff()`函数会稍微复杂一些,因为它们还需要处理启动和停止捕获引擎的问题。
2.2.14.6 Buffer allocation
到目前为止,我们已经讨论了缓冲区,但还没有看它们是如何分配的。对于散射/聚集(scatter/gather)情况来说,在这方面最复杂。对于分配,驱动程序可以完全将缓冲区分配工作交给`videobuf`层处理;在这种情况下,缓冲区将作为匿名用户空间页面进行分配,并且确实非常散乱。如果应用程序使用用户空间缓冲区,则不需要分配;`videobuf`层将负责调用`get_user_pages()`并填充散列表数组。
如果驱动程序需要自行进行内存分配,应该在调用`videobuf_reqbufs()`后的`vidioc_reqbufs()`函数中完成。第一步是调用:
struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf);
返回的`videobuf_dmabuf`结构(在`<media/videobuf-dma-sg.h>`中定义)包括一些相关的字段:
struct scatterlist *sglist;
int sglen;
驱动程序必须分配适当大小的散列表数组,并将指针填充到分配的缓冲区的各个部分;`sglen`应设置为数组的长度。
使用`vmalloc()`方法的驱动程序不需要(也不能)关心缓冲区的分配;`videobuf`将处理这些细节。对于连续DMA驱动程序,通常也是如此;`videobuf`在适当的时候使用`dma_alloc_coherent()`分配缓冲区。这意味着这些驱动程序可能随时尝试进行高阶分配,这种操作并不总是保证有效。一些驱动程序通过在系统引导时分配DMA空间来玩一些花招;目前`videobuf`与这些驱动程序不太兼容。
从2.6.31版本开始,连续DMA驱动程序可以与用户提供的缓冲区一起使用,只要该缓冲区是物理连续的。普通的用户空间分配不满足这个条件,但从其他内核驱动程序获取的缓冲区或包含在巨大页面中的缓冲区可以与这些驱动程序一起使用。
2.2.14.7 Filling the buffers
videobuf的最后一部分实现没有直接的回调函数,它是将帧数据放入缓冲区的代码部分,通常是响应来自设备的中断。对于所有类型的驱动程序,该过程大致按照以下步骤进行:
- 获取下一个可用的缓冲区,并确保有人在等待它。
- 获取内存指针,并将视频数据放入其中。
- 将缓冲区标记为已完成,并唤醒等待它的进程。
上述第一步是通过查看驱动程序管理的`list_head`结构完成的,这个结构在`buf_queue()`回调中填充。因为启动引擎和将缓冲区入队是分开的步骤,所以引擎有可能在没有可用缓冲区的情况下运行,尤其是在使用`vmalloc()`的情况下。因此,驱动程序应准备好列表为空的情况。同样有可能还没有进程对缓冲区感兴趣;驱动程序不应在没有进程等待时从列表中移除缓冲区或填充它。可以通过使用`waitqueue_active()`检查缓冲区的`done`字段(一个`wait_queue_head_t`结构)来进行测试。
在对缓冲区进行DMA映射之前,应将缓冲区的状态设置为`VIDEOBUF_ACTIVE`,以确保在设备传输数据时,videobuf层不会尝试对其执行任何操作。
对于散列/聚集驱动程序,所需的内存指针将在上述的scatterlist结构中找到。使用`vmalloc()`方法的驱动程序可以通过以下方式获得内存指针:
void *videobuf_to_vmalloc(struct videobuf_buffer *buf);
对于连续DMA驱动程序,要使用的函数是:
dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf);
连续DMA API在尽可能地隐藏DMA缓冲区的内核空间地址方面做了很多工作,以防止驱动程序访问它。
最后一步是将相关的videobuf_buffer结构的size字段设置为捕获图像的实际大小,将state设置为VIDEOBUF_DONE,然后在done队列上调用wake_up()函数。
此时,缓冲区归videobuf层所有,驱动程序不应再触碰它。
对于对此更多信息感兴趣的开发人员,可以查看相关的头文件;其中声明了一些未在此处讨论的低级函数。另请注意,所有这些调用仅导出为GPL(通用公共许可证)的模块,因此非GPL内核模块将无法使用这些功能。
2.2.15 V4L2 videobuf2 functions and data structures
enum vb2_memory
表示用于在用户空间中使缓冲区可见的内存模型。
常量:
- `VB2_MEMORY_UNKNOWN`:缓冲区状态未知或尚未在用户空间中使用。
- `VB2_MEMORY_MMAP`:缓冲区由内核分配,并通过`mmap()`ioctl进行内存映射。当用户通过`read()`或`write()`系统调用使用缓冲区时,也会使用此模型。
- `VB2_MEMORY_USERPTR`:缓冲区在用户空间中分配,并通过`mmap()`ioctl进行内存映射。
- `VB2_MEMORY_DMABUF`:缓冲区通过DMA缓冲区传递给用户空间。
VB2_MEMORY_MMAP和VB2_MEMORY_USERPTR的区别:
VB2_MEMORY_MMAP 和 VB2_MEMORY_USERPTR 都是 videobuf2 框架支持的不同内存类型,它们之间有以下区别:
1. 内存来源:
- VB2_MEMORY_MMAP:该类型的内存来自用户空间的内存映射(mmap),通过对内存区域的映射,驱动程序可以直接读写用户空间的内存。
- VB2_MEMORY_USERPTR:该类型的内存是由用户空间提供的用户指针(user pointer),驱动程序需要获取用户指针指向的内存进行读写操作。
2. 访问方式:
- VB2_MEMORY_MMAP:通过内存映射,驱动程序可以直接读写用户空间内存,无需进行额外的拷贝操作,因此访问速度较快。
- VB2_MEMORY_USERPTR:驱动程序需要从用户空间获取用户指针指向的内存,并进行必要的数据拷贝。这可能会增加一定的开销,因为数据需要在用户空间和内核空间之间进行复制。
3. 缓存同步:
- VB2_MEMORY_MMAP:由于内存映射和访问过程中使用的是同一块内存,所以缓存同步是相对简单的,不需要进行显式的缓存同步操作。
- VB2_MEMORY_USERPTR:由于数据需要从用户空间复制到内核空间,或者从内核空间复制到用户空间,因此可能涉及到缓存同步的问题,驱动程序需要在代码中进行必要的缓存同步操作,以确保数据的一致性。
选择使用哪种类型的内存取决于具体的应用需求和硬件平台的限制。一般来说,VB2_MEMORY_MMAP 适用于需要高性能的应用场景,而 VB2_MEMORY_USERPTR 则适用于需要更大的灵活性和兼容性的场景。
struct vb2_mem_ops
结构体定义了用于内存管理和内存分配的操作。它包含以下成员:
struct vb2_mem_ops {
void *(*alloc)(struct vb2_buffer *vb,struct device *dev, unsigned long size);
void (*put)(void *buf_priv);
struct dma_buf *(*get_dmabuf)(struct vb2_buffer *vb,void *buf_priv, unsigned long flags);
void *(*get_userptr)(struct vb2_buffer *vb,struct device *dev,unsigned long vaddr, unsigned long size);
void (*put_userptr)(void *buf_priv);
void (*prepare)(void *buf_priv);
void (*finish)(void *buf_priv);
void *(*attach_dmabuf)(struct vb2_buffer *vb,struct device *dev,struct dma_buf *dbuf, unsigned long size);
void (*detach_dmabuf)(void *buf_priv);
int (*map_dmabuf)(void *buf_priv);
void (*unmap_dmabuf)(void *buf_priv);
void *(*vaddr)(struct vb2_buffer *vb, void *buf_priv);
void *(*cookie)(struct vb2_buffer *vb, void *buf_priv);
unsigned int (*num_users)(void *buf_priv);
int (*mmap)(void *buf_priv, struct vm_area_struct *vma);
};
- alloc:分配视频内存,并可选地分配分配器的私有数据。如果失败,则返回 ERR_PTR();如果成功,则返回指向分配器私有每个缓冲区数据的指针。此函数的 size 参数应为页对齐。
- put:通知分配器该缓冲区将不再使用;通常会导致分配器释放缓冲区(如果没有其他使用此缓冲区的用户)。buf_priv 参数是之前从 alloc 回调中返回的分配器私有每个缓冲区结构。
- get_dmabuf:获取硬件操作的用户空间内存;用于 DMABUF 内存类型。
- get_userptr:获取用于硬件操作的用户空间内存;用于 USERPTR 内存类型。vaddr 是在将 USERPTR 类型的视频缓冲区排队时传递给 videobuf 层的地址。成功时应返回与缓冲区关联的分配器私有每个缓冲区结构,失败时返回 ERR_PTR();之后其他结构中的操作将使用返回的私有结构作为 buf_priv 参数。
- put_userptr:通知分配器 USERPTR 缓冲区将不再使用。
- prepare:每次将缓冲区从用户空间传递给驱动程序时调用,用于缓存同步,可选。
- finish:每次将缓冲区从驱动程序传递回用户空间时调用,也是可选的。
- attach_dmabuf:为硬件操作附加一个共享的 struct dma_buf;用于 DMABUF 内存类型。dev 是分配设备,dbuf 是共享的 dma_buf;失败时返回 ERR_PTR();成功时返回分配器私有每个缓冲区结构;此后需要使用该结构进一步访问缓冲区。
- detach_dmabuf:通知缓冲区的导出器当前的 DMABUF 缓冲区不再使用;buf_priv 参数是之前从 attach_dmabuf 回调中返回的分配器私有每个缓冲区结构。
- map_dmabuf:请求对 dmabuf 的访问权限;告知 dmabuf 的分配器此驱动程序将使用 dmabuf。
- unmap_dmabuf:释放对 dmabuf 的访问控制权;通知分配器此驱动程序目前已经使用完 dmabuf。
- vaddr:返回与传递的私有结构关联的给定内存缓冲区的内核虚拟地址;如果不存在这样的映射,则返回 NULL。
- cookie:返回与传递的私有结构关联的给定内存缓冲区的分配器特定 cookie;如果不可用,则返回 NULL。
- num_users:返回内存缓冲区的当前用户数;如果 videobuf 层(实际上是使用它的驱动程序)是唯一的用户,则返回 1。
- mmap:为提供的虚拟内存区域设置用户空间映射,用于给定的内存缓冲区。
这些操作由 videobuf2 核心用于实现每种支持的流式 I/O 方法的内存处理/内存分配器。
注意:
1)USERPTR 类型所需的操作:get_userptr、put_userptr。
2)MMAP 类型所需的操作:alloc、put、num_users、mmap。
3)读/写访问类型所需的操作:alloc、put、num_users、vaddr。
4)DMABUF 类型所需的操作:attach_dmabuf、detach_dmabuf、map_dmabuf、unmap_dmabuf。
struct vb2_plane
结构体定义了视频缓冲区中每个平面(plane)的信息。它包含以下成员:
struct vb2_plane {
void *mem_priv;
struct dma_buf
*dbuf;
unsigned int
dbuf_mapped;
unsigned int
bytesused;
unsigned int
length;
unsigned int
min_length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int
data_offset;
};
- mem_priv:与此平面相关的私有数据。
- dbuf:共享缓冲区对象的 dma_buf。
- dbuf_mapped:标志,用于显示 dbuf 是否已映射。
- bytesused:该平面中数据占用的字节数(载荷大小)。
- length:该平面的大小(不包括载荷)以字节为单位。最大有效大小限制为 MAX_UINT - PAGE_SIZE。
- min_length:该平面所需的最小大小(不包括载荷)以字节为单位。length 始终大于或等于 min_length,并且像 length 一样,受到 MAX_UINT - PAGE_SIZE 的限制。
- m:与内存类型相关的联合数据。
- m.offset:当内存类型为 VB2_MEMORY_MMAP 时,表示该平面相对于设备内存起始位置的偏移量(或者是一个应该传递给在视频节点上调用 mmap() 的 "cookie")。
- m.userptr:当内存类型为 VB2_MEMORY_USERPTR 时,表示指向该平面的用户空间指针。
- m.fd:当内存类型为 VB2_MEMORY_DMABUF 时,表示与该平面关联的用户空间文件描述符。
- data_offset:该平面中数据开始的偏移量;通常为0,除非数据前面有一个头部。
struct vb2_plane 结构体应包含足够的信息,以覆盖 videodev2.h 中 struct v4l2_plane 的所有字段。
enum vb2_io_modes
枚举定义了视频缓冲区的访问方法。它包含以下常量:
- VB2_MMAP:驱动程序支持使用流式 API 的内存映射(MMAP)方式访问缓冲区。
- VB2_USERPTR:驱动程序支持使用流式 API 的用户指针(USERPTR)方式访问缓冲区。
- VB2_READ:驱动程序支持使用 read() 风格的访问方式。
- VB2_WRITE:驱动程序支持使用 write() 风格的访问方式。
- VB2_DMABUF:驱动程序支持使用流式 API 的 DMABUF 方式访问缓冲区。
这些常量表示了不同的访问方法,驱动程序可以选择支持其中的一种或多种访问方法来与应用程序进行数据交互。
enum vb2_buffer_state
枚举定义了当前视频缓冲区的状态。它包含以下常量:
- VB2_BUF_STATE_DEQUEUED:缓冲区由用户空间控制。
- VB2_BUF_STATE_IN_REQUEST:缓冲区在媒体请求中排队。
- VB2_BUF_STATE_PREPARING:缓冲区正在视频缓冲区中准备。
- VB2_BUF_STATE_QUEUED:缓冲区在视频缓冲区中排队,但尚未在驱动程序中排队。
- VB2_BUF_STATE_ACTIVE:缓冲区在驱动程序中排队,并可能用于硬件操作。
- VB2_BUF_STATE_DONE:缓冲区从驱动程序返回到视频缓冲区,但尚未从用户空间中出队。
- VB2_BUF_STATE_ERROR:与上述相同,但是缓冲区上的操作以错误结束,当它被出队时将向用户空间报告该错误。
这些常量表示了视频缓冲区所处的不同状态,可用于跟踪和管理缓冲区在不同阶段的使用情况。
struct vb2_buffer
结构体代表一个视频缓冲区。
定义:
struct vb2_buffer {struct vb2_queue *vb2_queue;unsigned int index;unsigned int type;unsigned int memory;unsigned int num_planes;u64 timestamp;struct media_request *request;struct media_request_object req_obj;
};
成员变量:
- vb2_queue:指向包含此驱动程序所属队列的 struct vb2_queue 的指针。
- index:缓冲区的唯一标识号。
- type:缓冲区的类型。
- memory:实际数据传递的方法。
- num_planes:在内部驱动程序队列中的缓冲区中平面(plane)的数量。
- timestamp:帧的时间戳,以纳秒为单位。
- request:与此缓冲区关联的请求。
- req_obj:用于将该缓冲区绑定到请求的对象。此请求对象具有引用计数。
struct vb2_buffer 结构体表示一个视频缓冲区,并存储了与缓冲区相关的各种属性和信息,包括缓冲区所属的队列、缓冲区的唯一标识、类型、数据传递方法、平面数量、时间戳等。
struct vb2_ops
是一个结构体,用于定义驱动程序的回调函数。
定义:
struct vb2_ops {
int (*queue_setup)(struct vb2_queue *q,unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], struct device *alloc_devs[]);
void (*wait_prepare)(struct vb2_queue *q);
void (*wait_finish)(struct vb2_queue *q);
int (*buf_out_validate)(struct vb2_buffer *vb);
int (*buf_init)(struct vb2_buffer *vb);
int (*buf_prepare)(struct vb2_buffer *vb);
void (*buf_finish)(struct vb2_buffer *vb);
void (*buf_cleanup)(struct vb2_buffer *vb);
int (*start_streaming)(struct vb2_queue *q, unsigned int count);
void (*stop_streaming)(struct vb2_queue *q);
void (*buf_queue)(struct vb2_buffer *vb);
void (*buf_request_complete)(struct vb2_buffer *vb);
};
它包含以下成员:
1. queue_setup:在内存分配之前由VIDIOC_REQBUFS()和VIDIOC_CREATE_BUFS()处理程序调用。如果无法分配所请求的缓冲区的原始数量,则可能会调用两次。驱动程序应在*num_buffers中返回所需的缓冲区数,在*num_planes中返回每个缓冲区的平面数,在sizes[]数组中设置每个平面的大小,并在alloc_devs[]数组中设置每个平面的分配设备(可选)。在从VIDIOC_REQBUFS()调用时,*num_planes == 0,驱动程序必须使用当前配置的格式来确定平面大小,并且*num_buffers是要分配的缓冲区的总数。在从VIDIOC_CREATE_BUFS()调用时,*num_planes != 0,并且它描述了请求的平面数,并且sizes[]包含了请求的平面大小。在这种情况下,*num_buffers将被额外分配给q->num_buffers。如果*num_planes或所请求的大小无效,则回调必须返回-EINVAL。
2. wait_prepare:在需要等待新缓冲区到达之前,释放调用vb2函数时获取的任何锁;必须在ioctl需要等待新缓冲区到达之前调用,以避免阻塞访问类型产生死锁。
3. wait_finish:重新获取在上一个回调中释放的所有锁;必须在等待新缓冲区到达时继续操作之前调用。
4. buf_out_validate:在输出缓冲区被准备或排队到请求时调用;驱动程序可以使用此函数验证用户空间提供的信息;这仅对OUTPUT队列需要。
5. buf_init:在分配缓冲区(在MMAP情况下)或获取新的USERPTR缓冲区后调用一次;驱动程序可以执行其他与缓冲区相关的初始化;如果初始化失败(返回值!= 0),将阻止队列设置成功完成;可选。
6. buf_prepare:每次从用户空间排队缓冲区和VIDIOC_PREPARE_BUF() ioctl调用时调用;驱动程序可以在此回调中执行每个硬件操作之前需要的任何初始化;在此回调中,驱动程序可以访问/修改缓冲区,因为它仍然针对CPU同步;支持VIDIOC_CREATE_BUFS()的驱动程序还必须验证缓冲区大小;如果返回错误,则缓冲区将不会在驱动程序中排队;可选。
7. buf_finish:在每次将缓冲区从驱动程序出队返回给用户空间之前调用;缓冲区已经同步到CPU,因此驱动程序可以访问/修改缓冲区内容;驱动程序可以在用户空间访问缓冲区之前执行任何所需的操作;可选。缓冲区的状态可以是以下之一:DONE和ERROR发生在流媒体进行中时,PREPARED状态发生在队列被取消并且所有等待的缓冲区正在返回其默认的DEQUEUED状态时。通常只有当状态为VB2_BUF_STATE_DONE时才需要执行某些操作,因为在其他情况下缓冲区内容将被忽略。
8. buf_cleanup:在缓冲区被释放之前调用一次;驱动程序可以执行任何额外的清理;可选。
9. start_streaming:用于进入“流媒体”状态的回调函数;在调用start_streaming之前,驱动程序可能会在buf_queue回调之前收到缓冲区;驱动程序可以在硬件失败时返回错误,此时通过调用vb2_buffer_done()并使用VB2_BUF_STATE_QUEUED标记已经由buf_queue回调提供的所有缓冲区来返回缓冲区。如果您需要在开始流传之前至少排队一定数量的缓冲区,则设置vb2_queue->min_buffers_needed。如果这个值非零,则只有在用户空间至少排队了这么多的缓冲区后,才会调用start_streaming。
10. stop_streaming:在需要禁用“流媒体”状态时调用;驱动程序应停止任何DMA事务或等待它们完成,并通过调用vb2_buffer_done()函数将所有从buf_queue回调获得的缓冲区返回,状态为VB2_BUF_STATE_DONE或VB2_BUF_STATE_ERROR;可以使用vb2_wait_for_all_buffers()函数。
11. buf_queue:将缓冲区vb传递给驱动程序;驱动程序可以在此缓冲区上启动硬件操作;驱动程序应使用vb2_buffer_done()函数将缓冲区返回;它始终在调用VIDIOC_STREAMON() ioctl之后调用;如果用户在调用VIDIOC_STREAMON()之前预先排队了缓冲区,则可能在start_streaming回调之前调用。
12. buf_request_complete:一个从未排队到驱动程序的与排队请求相关联的缓冲区被取消。驱动程序将必须标记请求中的关联对象为已完成;如果支持请求,则此回调是必需的。
这些操作在中断上下文中不会调用,除非特别指定。
struct vb2_buf_ops
是一个结构体,用于定义驱动程序特定的回调函数。
定义:
struct vb2_buf_ops {
int (*verify_planes_array)(struct vb2_buffer *vb, const void *pb);
void (*init_buffer)(struct vb2_buffer *vb);
void (*fill_user_buffer)(struct vb2_buffer *vb, void *pb);
int (*fill_vb2_buffer)(struct vb2_buffer *vb, struct vb2_plane *planes);
void (*copy_timestamp)(struct vb2_buffer *vb, const void *pb);
};
它包含以下成员:
1. verify_planes_array:验证用户空间结构中是否包含足够的平面以存储缓冲区。对于每个出队的缓冲区都会调用此函数。
2. init_buffer:给定一个vb2_buffer,在struct vb2_buffer之后初始化额外的数据。对于V4L2来说,这是一个struct vb2_v4l2_buffer。
3. fill_user_buffer:给定一个vb2_buffer,填充用户空间结构。对于V4L2来说,这是一个struct v4l2_buffer。
4. fill_vb2_buffer:给定一个用户空间结构,填充vb2_buffer。如果用户空间结构无效,则此操作将返回错误。
5. copy_timestamp:将时间戳从用户空间结构复制到struct vb2_buffer。
struct vb2_queue
是一个 videobuf 队列的结构体,用来管理视频缓冲区队列。
定义:
struct vb2_queue {
unsigned int
type;
unsigned int
io_modes;
struct device
*dev;
unsigned long
dma_attrs;
unsigned int
bidirectional:1;
unsigned int
fileio_read_once:1;
unsigned int
fileio_write_immediately:1;
unsigned int
allow_zero_bytesused:1;
unsigned int
quirk_poll_must_check_waiting_for_buffers:1;
unsigned int
supports_requests:1;
unsigned int
requires_requests:1;
unsigned int
uses_qbuf:1;
unsigned int
uses_requests:1;
unsigned int
allow_cache_hints:1;
unsigned int
non_coherent_mem:1;
struct mutex
*lock;
void *owner;
const struct vb2_ops
*ops;
const struct vb2_mem_ops
*mem_ops;
const struct vb2_buf_ops
*buf_ops;
void *drv_priv;
u32 subsystem_flags;
unsigned int
buf_struct_size;
u32 timestamp_flags;
gfp_t gfp_flags;
u32 min_buffers_needed;
struct device
*alloc_devs[VB2_MAX_PLANES];
};
成员变量包括:
- type:私有缓冲区类型,由调用者定义。例如,在 V4L2 中应该与 enum v4l2_buf_type 中定义的类型相匹配。
- io_modes:支持的 I/O 方法,参见 enum vb2_io_modes。
- dev:默认分配上下文中要使用的设备,如果驱动程序没有填充 alloc_devs 数组时使用。
- dma_attrs:用于 DMA 的 DMA 属性。
- bidirectional:当设置了此标志时,该队列的缓冲区的 DMA 方向将被覆盖为 DMA_BIDIRECTIONAL 方向。这在硬件(固件)将写入映射为读取(DMA_TO_DEVICE)的缓冲区,或者从映射为写入(DMA_FROM_DEVICE)的缓冲区中读取以满足某些内部硬件限制或添加处理算法所需的填充时非常有用。如果 DMA 映射不是双向的,但硬件(固件)试图访问缓冲区(在相反的方向上),则可能会导致 IOMMU 保护错误。
- fileio_read_once:在读取第一个缓冲区后报告 EOF。
- fileio_write_immediately:每次调用 write() 后立即将缓冲区排队。
- allow_zero_bytesused:允许将 bytesused 设置为 0 传递给驱动程序。
- quirk_poll_must_check_waiting_for_buffers:在 QBUF 没有被调用时,在 poll 时返回 EPOLLERR。这是 vb1 的习惯用法,也被 vb2 采用。
- supports_requests:该队列支持请求 API。
- requires_requests:该队列要求使用请求 API。如果将其设置为 1,则 supports_requests 必须也设置为 1。
- uses_qbuf:使用了 qbuf 来直接排队该队列的缓冲区。首次调用时设置为 1。取消队列时设置为 0。如果为 1,则无法从请求中排队缓冲区。
- uses_requests:使用请求来排队该队列的缓冲区。首次排队请求时设置为 1。取消队列时设置为 0。如果为 1,则无法直接排队缓冲区。
- allow_cache_hints:当设置时,用户空间可以传递缓存管理提示以跳过对于 ->prepare() 或/和 ->finish() 的缓存刷新/失效操作。
- non_coherent_mem:当设置时,队列将尝试使用非一致性内存分配缓冲区。
- lock:指向互斥锁的指针,用于保护 struct vb2_queue。驱动程序可以将其设置为互斥锁,以使 v4l2 核心对队列的 I/O 控制进行序列化。如果驱动程序想自己处理锁定,则应将其设置为 NULL。该锁不会被 videobuf2 核心 API 使用。
- owner:‘拥有’缓冲区的文件句柄,即调用 reqbufs、create_buffers 或启动 fileio 的文件句柄。该字段不被 videobuf2 核心 API 使用,但它允许驱动程序轻松地将所有者文件句柄与队列关联起来。
- ops:驱动程序特定的回调函数。
- mem_ops:内存分配器特定的回调函数。
- buf_ops:在用户空间和内核空间之间传递缓冲区信息的回调函数。
- drv_priv:驱动程序的私有数据。
- subsystem_flags:子系统特定的标志(V4L2/DVB 等)。不被 vb2 核心使用。
- buf_struct_size:驱动程序特定缓冲区结构的大小;“0”表示驱动程序不希望使用自定义缓冲区结构类型。在这种情况下,将使用一个子系统特定的结构体(在 V4L2 的情况下是 sizeof(struct vb2_v4l2_buffer))。驱动程序特定缓冲区结构的第一个字段必须是子系统特定的结构体(例如,在 V4L2 的情况下是 vb2_v4l2_buffer)。
- timestamp_flags:时间戳标志;V4L2_BUF_FLAG_TIMESTAMP_* 和 V4L2_BUF_FLAG_TSTAMP_SRC_*。
- gfp_flags:在分配缓冲区时使用的附加 gfp 标志。通常为 0,但可以是例如 GFP_DMA 或 __GFP_DMA32,以将缓冲区分配到特定的内存区域。
- min_buffers_needed:在可以调用 start_streaming 之前所需的最小缓冲区数。当 DMA 引擎必须至少已经排队了这个数量的缓冲区才能启动时使用。
- alloc_devs:按平面区域的设备内存类型/分配器特定的设备。
bool vb2_queue_allows_cache_hints(struct vb2_queue *q)
如果队列允许缓存和内存一致性提示,则返回true。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
void * vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
返回给定平面的内核虚拟地址。
参数:
- struct vb2_buffer *vb:指向所讨论平面所属的struct vb2_buffer的指针。
- unsigned int plane_no:要返回地址的平面编号。
描述:
如果存在给定平面的映射,则该函数返回该平面的内核虚拟地址;否则返回NULL。
void * vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no)
返回给定平面的分配器特定cookie。
参数:
- struct vb2_buffer *vb:指向所讨论平面所属的struct vb2_buffer的指针。
- unsigned int plane_no:要返回cookie的平面编号。
描述:
如果可用,该函数返回给定平面的分配器特定cookie;否则返回NULL。
分配器应提供一些简单的静态内联函数,用于将此cookie转换为分配器特定类型,以便驱动程序可以直接使用它来访问缓冲区。这可以是物理地址、散列列表的指针或IOMMU映射等。
void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
通知videobuf缓冲区上的操作已完成。
参数:
- struct vb2_buffer *vb:指向要使用的struct vb2_buffer的指针。
- enum vb2_buffer_state state:缓冲区的状态,由enum vb2_buffer_state定义。如果操作成功完成,则为VB2_BUF_STATE_DONE;如果操作以错误完成,则为VB2_BUF_STATE_ERROR;如果已被排队,则为VB2_BUF_STATE_QUEUED。
描述:
驱动程序在缓冲区上的硬件操作完成后,应调用此函数,并可以将缓冲区返回给用户空间。在通过vb2_ops->buf_queue回调函数将其排队回驱动程序之前,驱动程序不能再使用该缓冲区。只能将先前通过vb2_ops->buf_queue排队到驱动程序的缓冲区传递给此函数。
在流处理期间,缓冲区只能以状态DONE或ERROR返回。如果由于某种原因无法启动DMA引擎,则vb2_ops->start_streaming操作也可以返回这些状态。在这种情况下,应将缓冲区以状态QUEUED返回,以将其放回队列中。
void vb2_discard_done(struct vb2_queue *q)
丢弃所有标记为DONE的缓冲区。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
描述:
此函数用于挂起/恢复操作。它丢弃所有已完成的缓冲区,因为在恢复后它们会变得太旧而无法被请求。
在调用此函数之前,驱动程序必须停止硬件,并与中断处理程序和/或延迟工作进行同步,以确保驱动程序和/或硬件不会访问任何缓冲区。
int vb2_wait_for_all_buffers(struct vb2_queue *q)
等待直到所有缓冲区都被返回给vb2。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
描述:
此函数将等待,直到所有已通过vb2_ops->buf_queue提交给驱动程序的缓冲区都被使用vb2_buffer_done()重新返回给vb2。它不调用vb2_ops->wait_prepare/vb2_ops->wait_finish对。它的目的是在拿取所有锁时调用,例如在vb2_ops->stop_streaming回调中。
该函数将阻塞当前线程,直到所有缓冲区都被返回为止。
void vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb)
查询视频缓冲区的信息。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- unsigned int index:缓冲区的id号码。
- void *pb:从用户空间传递的缓冲区结构体。
描述:
Videobuf2的核心帮助函数,用于实现VIDIOC_QUERYBUF()操作。它由VB2内部调用,由API特定的处理程序(如videobuf2-v4l2.h)处理。
传递的缓冲区应该经过验证。
该函数填充了用户空间所需的相关信息。
返回:
成功时返回零;否则返回错误码。
int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, unsigned int flags, unsigned int *count)
启动数据流传输。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- enum vb2_memory memory:内存类型,由enum vb2_memory定义。
- unsigned int flags:辅助队列/缓冲区管理标志。当前仅使用的标志是V4L2_MEMORY_FLAG_NON_COHERENT。
- unsigned int *count:请求的缓冲区数量。
描述:
Videobuf2的核心帮助函数,用于实现VIDIOC_REQBUF()操作。它由VB2内部调用,由API特定的处理程序(如videobuf2-v4l2.h)处理。
此函数的功能包括:
1) 验证从用户空间传递的流参数;
2) 设置队列;
3) 与驱动程序协商在数据流传输期间要使用的缓冲区数量和每个缓冲区的平面数;
4) 根据协商达成的参数分配内部缓冲区结构(struct vb2_buffer);
5) 对于MMAP内存类型,使用在队列初始化期间提供的内存处理/分配例程分配实际的视频内存。
如果req->count为0,则释放所有内存。
如果队列先前通过前一个vb2_core_reqbufs()调用进行了分配并且队列未忙,则将重新分配内存。
返回:
成功时返回零;否则返回错误码。
int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, unsigned int flags, unsigned int *count, unsigned int requested_planes, const unsigned int requested_sizes[])
分配缓冲区和任何必需的辅助结构。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- enum vb2_memory memory:内存类型,由enum vb2_memory定义。
- unsigned int flags:辅助队列/缓冲区管理标志。
- unsigned int *count:请求的缓冲区数量。
- unsigned int requested_planes:请求的平面数。
- const unsigned int requested_sizes[]:具有平面大小的数组。
描述:
Videobuf2的核心帮助函数,用于实现VIDIOC_CREATE_BUFS()操作。它由VB2内部调用,由API特定的处理程序(如videobuf2-v4l2.h)处理。
此函数的功能包括:
1) 验证参数的正确性;
2) 调用vb2_ops->queue_setup队列操作;
3) 执行任何必要的内存分配。
返回:
成功时返回零;否则返回错误码。
int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
将一个缓冲区的所有权从用户空间转移到内核。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- unsigned int index:缓冲区的 ID 号码。
- void *pb:从用户空间传递给驱动程序中的v4l2_ioctl_ops->vidioc_prepare_buf处理程序的缓冲区结构。
描述:
Videobuf2的核心帮助函数,用于实现VIDIOC_PREPARE_BUF()操作。它由VB2内部调用,由API特定的处理程序(如videobuf2-v4l2.h)处理。
传递的缓冲区应已经通过验证。
这个函数调用驱动程序中的vb2_ops->buf_prepare回调(如果提供),在该回调中可以执行驱动程序特定的缓冲区初始化。
返回:
成功时返回零;否则返回错误码。
int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, struct media_request *req)
从用户空间排队一个缓冲区。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- unsigned int index:缓冲区的 ID 号码。
- void *pb:从用户空间传递给驱动程序中的v4l2_ioctl_ops->vidioc_qbuf处理程序的缓冲区结构。
- struct media_request *req:指向struct media_request的指针,可以为NULL。
描述:
Videobuf2的核心帮助函数,用于实现VIDIOC_QBUF()操作。它由VB2内部调用,由API特定的处理程序(如videobuf2-v4l2.h)处理。
这个函数:
1) 如果req非空,则将缓冲区绑定到此媒体请求,并返回。当请求本身被排队时,缓冲区将被准备并排队到驱动程序(即下面的两个步骤)。
2) 如果需要,调用驱动程序中的vb2_ops->buf_prepare回调(如果提供),在该回调中可以执行驱动程序特定的缓冲区初始化。
3) 如果流式处理正在进行,则通过vb2_ops->buf_queue回调将缓冲区排队到驱动程序进行处理。
返回:
成功时返回零;否则返回错误码。
int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, bool nonblocking)
从用户空间出队一个缓冲区。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- unsigned int *pindex:指向缓冲区索引的指针。可以为NULL。
- void *pb:从用户空间传递给驱动程序中的v4l2_ioctl_ops->vidioc_dqbuf处理程序的缓冲区结构。
- bool nonblocking:如果为true,则此调用在没有准备好出队的缓冲区时不会休眠等待。通常,驱动程序将在这里传递(file->f_flags & O_NONBLOCK)。
描述:
Videobuf2的核心帮助函数,用于实现VIDIOC_DQBUF()操作。它由VB2内部调用,由API特定的处理程序(如videobuf2-v4l2.h)处理。
这个函数:
1) 调用驱动程序中的buf_finish回调(如果提供),在此回调中,驱动程序可以在将缓冲区返回给用户空间之前执行任何可能需要的其他操作,例如缓存同步。
2) 填充缓冲区结构的成员与用户空间相关的信息。
返回:
成功时返回零;否则返回错误码。
int vb2_core_streamon(struct vb2_queue *q, unsigned int type)
实现VB2的流打开逻辑。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- unsigned int type:要启动的队列的类型。对于V4L2,这由enum v4l2_buf_type类型定义。
描述:
Videobuf2的核心帮助函数,用于实现VIDIOC_STREAMON()操作。它由VB2内部调用,由API特定的处理程序(如videobuf2-v4l2.h)处理。
此函数用于处理开始数据流的操作。它根据提供的队列类型进行相应的处理,并将其状态设置为流开启。
返回:
成功时返回零;否则返回错误码。
int vb2_core_streamoff(struct vb2_queue *q, unsigned int type)
实现VB2的流关闭逻辑。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- unsigned int type:要停止的队列的类型。对于V4L2,这由enum v4l2_buf_type类型定义。
描述:
Videobuf2的核心帮助函数,用于实现VIDIOC_STREAMOFF()操作。它由VB2内部调用,由API特定的处理程序(如videobuf2-v4l2.h)处理。
此函数用于处理停止数据流的操作。它将相应的队列状态设置为流关闭,并执行任何必要的清理操作,以确保数据流正确停止。
返回:
成功时返回零;否则返回错误码。
int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, unsigned int index, unsigned int plane, unsigned int flags)
将缓冲区导出为文件描述符。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- int *fd:与DMABUF相关联的文件描述符的指针(由驱动程序设置)。
- unsigned int type:缓冲区类型。
- unsigned int index:缓冲区的ID号码。
- unsigned int plane:要导出的平面索引,对于单平面队列为0。
- unsigned int flags:新创建文件的标志,定义在include/uapi/asm-generic/fcntl.h中。目前,唯一使用的标志是O_CLOEXEC。有关更多详情,请参阅open系统调用的手册。
描述:
Videobuf2的核心帮助函数,用于实现VIDIOC_EXPBUF()操作。它由VB2内部调用,由API特定的处理程序(如videobuf2-v4l2.h)处理。
此函数将指定的缓冲区导出为文件描述符。驱动程序将为相关的DMABUF对象创建一个文件描述符,并将其与提供的指针进行关联。
返回:
成功时返回零;否则返回错误码。
int vb2_core_queue_init(struct vb2_queue *q)
初始化一个videobuf2队列。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。这个结构体应该由驱动程序分配。
描述:
vb2_queue结构体应由驱动程序分配。驱动程序负责清除它的内容并为一些必需的条目设置初始值,然后调用此函数。
注意:在调用此函数之前,应设置以下字段的值:vb2_queue->ops、vb2_queue->mem_ops、vb2_queue->type。
该函数的作用是初始化videobuf2队列,为队列分配内部的数据结构,并设置一些必要的默认值。初始化后,队列就可以准备接收数据,并可以通过相关的API进行操作。
返回:
成功时返回零;否则返回错误码。
void vb2_core_queue_release(struct vb2_queue *q)
停止流式传输,释放队列并释放内存。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
描述:
该函数停止流式传输并执行必要的清理工作,包括释放视频缓冲区内存。驱动程序负责释放struct vb2_queue本身。
该函数的作用是停止视频数据的传输,并进行必要的清理操作,包括释放视频缓冲区的内存。在调用此函数之后,队列将不再可用,相关的资源将被释放。
返回:
无返回值。
void vb2_queue_error(struct vb2_queue *q)
在队列上发出致命错误信号。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
描述:
该函数设置一个致命的、不可恢复的错误标志,并唤醒所有在队列上等待的进程。此后,轮询操作将设置EPOLLERR标志,并且队列中的缓冲区入队和出队操作将返回-EIO错误码。取消队列(例如通过vb2_streamoff()或vb2_queue_release()调用)时,错误标志将被清除。因此,驱动程序在启动流之前不应调用此函数,否则错误标志将保持设置状态,直到关闭设备节点并释放队列时。
该函数的作用是在队列上设置一个致命错误标志,表示发生了一个不可恢复的错误。这个错误标志会通知等待在队列上的进程,让它们知道发生了一个致命错误。此后,对队列的轮询操作将返回一个错误值,而缓冲区的入队和出队操作将返回-EIO错误码。当取消队列时,错误标志将被清除。
返回:
无返回值。
int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
将视频缓冲区映射到应用程序的地址空间。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- struct vm_area_struct *vma:指向包含在驱动程序中的mmap文件操作处理程序中传递给vma的struct vm_area_struct的指针。
描述:
此函数应从驱动程序的mmap文件操作处理程序中调用。该函数将一个可用视频缓冲区的一个平面映射到用户空间。要映射在reqbufs上分配的整个视频内存,需要对之前分配的每个平面的每个缓冲区调用此函数一次。当用户空间应用程序调用mmap时,它会传递一个先前由v4l2_ioctl_ops->vidioc_querybuf处理程序返回的偏移量。该偏移量充当一个"cookie",用于标识要映射的平面。该函数查找具有匹配偏移量的平面,并通过提供的内存操作执行映射。此函数的返回值应直接从驱动程序的mmap处理程序中返回。
返回值:
- 成功:返回0或正数,表示映射成功。
- 失败:返回负数,表示映射失败。可以根据具体的错误码进行错误处理。
unsigned long vb2_get_unmapped_area(struct vb2_queue *q, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags)
为给定的缓冲区提供地址映射。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- unsigned long addr:内存地址。
- unsigned long len:缓冲区大小。
- unsigned long pgoff:页偏移量。
- unsigned long flags:内存标志。
描述:
此函数用于在没有MMU平台上为给定的缓冲区提供地址映射。它旨在作为file_operations->get_unmapped_area操作的处理程序使用。当!CONFIG_MMU时,mmap()系统调用例程将调用此函数以获取映射的建议地址。
返回值:
- 成功:返回一个未映射区域的起始地址(unsigned long类型)。
- 失败:返回-EINVAL,表示无效的参数。
__poll_t vb2_core_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
实现poll系统调用的逻辑。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- struct file *file:传递给poll文件操作处理程序的struct file参数。
- poll_table *wait:传递给poll文件操作处理程序的poll_table wait参数。
描述:
此函数为驱动程序实现了poll文件操作处理程序。对于CAPTURE队列,如果一个缓冲区准备好出队,用户空间将被通知视频设备的文件描述符可用于读取。对于OUTPUT队列,如果一个缓冲区准备好出队,文件描述符将被报告为可用于写入。
此函数的返回值应直接从驱动程序的poll处理程序中返回。
返回值:
- 返回__poll_t类型的值,用于指示文件描述符的状态和事件。常见的返回值是POLLIN(文件描述符可读)和POLLOUT(文件描述符可写)。其他的返回值和标志可以根据具体的需求和条件进行设置。
size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, loff_t *ppos, int nonblock)
实现read系统调用的逻辑。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- char __user *data:指向目标用户空间缓冲区的指针。
- size_t count:要读取的字节数。
- loff_t *ppos:文件句柄位置跟踪指针。
- int nonblock:模式选择器(1表示阻塞调用,0表示非阻塞)。
描述:
此函数实现了read文件操作处理程序。它用于从驱动程序的缓冲区中读取数据并将其写入到用户空间提供的缓冲区中。它通过vb2_queue的内部机制从队列中获取数据,并将其拷贝到用户空间缓冲区中。
返回值:
- 返回成功读取的字节数(size_t类型)。
- 如果非阻塞模式下没有可用的数据,则返回0。
- 返回-errno以表示读取操作失败,并相应地设置错误码。
size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, loff_t *ppos, int nonblock)
实现write系统调用的逻辑。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- const char __user *data:指向目标用户空间缓冲区的指针。
- size_t count:要写入的字节数。
- loff_t *ppos:文件句柄位置跟踪指针。
- int nonblock:模式选择器(1表示阻塞调用,0表示非阻塞)。
描述:
此函数实现了write文件操作处理程序。它用于将用户空间提供的数据写入到驱动程序的缓冲区中。它通过vb2_queue的内部机制将数据从用户空间缓冲区拷贝到队列中。
返回值:
- 返回成功写入的字节数(size_t类型)。
- 如果非阻塞模式下无法立即写入所有数据,则返回已写入的部分字节数。
- 返回-errno以表示写入操作失败,并相应地设置错误码。
vb2_thread_fnc函数是用于vb2_thread的回调函数类型。
语法:
int vb2_thread_fnc(struct vb2_buffer *vb, void *priv)
参数:
- struct vb2_buffer *vb:指向struct vb2_buffer的指针。
- void *priv:指向私有数据的指针。
描述:
在线程中出列缓冲区时,会调用此函数。它接收一个指向vb2_buffer的指针以及私有数据的指针作为参数。在函数内部,您可以对缓冲区进行处理和操作。
返回值:
- 返回int类型的值,表示函数执行的结果。通常,返回0表示成功,而其他非零值表示出现错误或异常情况。
int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv, const char *thread_name)
启动一个线程来处理给定的队列。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- vb2_thread_fnc fnc:vb2_thread_fnc回调函数。
- void *priv:传递给回调函数的私有指针。
- const char *thread_name:线程的名称。它将以"vb2-"作为前缀。
描述:
此函数启动一个线程,该线程会持续进行队列入列和出列操作,直到发生错误或调用vb2_thread_stop()函数停止。
注意:
此函数应仅用于videobuf2-dvb支持,不应用于其他情况。如果您认为有其他合适的用例,请先联系Linux Media邮件列表。
int vb2_thread_stop(struct vb2_queue *q)
停止给定队列的线程。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
描述:
此函数停止处理给定队列的线程。它会终止线程的执行,并清理任何相关资源。
注意:
在调用该函数之前,请确保线程已经启动,并且不再需要进一步处理队列。
bool vb2_is_streaming(struct vb2_queue *q)
返回队列的流状态。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
描述:
此函数返回队列当前的流状态。它用于确定队列是否处于流模式。
返回值:
- 如果队列正在流动,则返回true。
- 如果队列不在流动状态,则返回false。
bool vb2_fileio_is_active(struct vb2_queue *q)
如果fileio处于活动状态,则返回true。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
描述:
此函数返回true如果使用read()或write()来流式传输数据,而不是使用流I/O。在几乎所有情况下,这几乎不是一个重要的区别。但在某些罕见情况下,这是重要的区别之一。
其中一个情况是使用read()或write()来流式传输使用V4L2_FIELD_ALTERNATE格式的数据是不允许的,因为你无法将每个缓冲区的字段信息传递给/从用户空间。支持此字段格式的驱动程序应该在vb2_ops->queue_setup操作中检查此情况,并在此函数返回true时拒绝它。
bool vb2_is_busy(struct vb2_queue *q)
返回队列的繁忙状态。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
描述:
此函数检查队列是否分配了任何缓冲区。
返回值:
- 如果队列有任何已分配的缓冲区,则返回true。
- 如果队列没有已分配的缓冲区,则返回false。
void * vb2_get_drv_priv(struct vb2_queue *q)
返回与队列相关联的驱动程序私有数据。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
描述:
此函数返回与给定队列相关联的驱动程序私有数据的指针。驱动程序可以使用此指针来存储和检索与队列操作相关的任何私有数据。
返回值:
- 返回指向驱动程序私有数据的指针(void *)。
void vb2_set_plane_payload(struct vb2_buffer *vb, unsigned int plane_no, unsigned long size)
设置平面plane_no的bytesused。
参数:
- struct vb2_buffer *vb:指向所讨论的平面所属的struct vb2_buffer的指针。
- unsigned int plane_no:要设置payload的平面编号。
- unsigned long size:以字节为单位的payload大小。
描述:
该函数用于设置给定平面的bytesused值。bytesused表示已使用的数据大小,通常用于视频帧中编码后的数据。
注意:
- 在调用此函数之前,应确保平面plane_no已经分配和初始化。
示例用法:
```
struct vb2_buffer *buffer; // 假设有一个vb2_buffer对象
unsigned int plane_no = 0; // 假设要设置的平面编号为0
unsigned long size = 1024; // 假设payload大小为1024字节vb2_set_plane_payload(buffer, plane_no, size);
```
这将把平面0的bytesused设置为1024字节。
unsigned long vb2_get_plane_payload(struct vb2_buffer *vb, unsigned int plane_no)
获取平面plane_no的bytesused。
参数:
- struct vb2_buffer *vb:指向所讨论的平面所属的struct vb2_buffer的指针。
- unsigned int plane_no:要获取payload的平面编号。
描述:
该函数用于获取给定平面的bytesused值。bytesused表示已使用的数据大小,通常用于视频帧中编码后的数据。
注意:
- 在调用此函数之前,应确保平面plane_no已经分配和初始化。
返回值:
- 返回平面plane_no的bytesused值(unsigned long)。
示例用法:
```
struct vb2_buffer *buffer; // 假设有一个vb2_buffer对象
unsigned int plane_no = 0; // 假设要获取的平面编号为0unsigned long payload_size = vb2_get_plane_payload(buffer, plane_no);
```
这将返回平面0的bytesused值,即payload的大小。
unsigned long vb2_plane_size(struct vb2_buffer *vb, unsigned int plane_no)
返回平面plane_no的大小(以字节为单位)。
参数:
- struct vb2_buffer *vb:指向所讨论的平面所属的struct vb2_buffer的指针。
- unsigned int plane_no:要返回大小的平面编号。
描述:
该函数用于获取给定平面的大小。大小表示平面的容量,通常用于视频帧中的数据存储。
注意:
- 在调用此函数之前,应确保平面plane_no已经分配和初始化。
返回值:
- 返回平面plane_no的大小(unsigned long)。
示例用法:
```
struct vb2_buffer *buffer; // 假设有一个vb2_buffer对象
unsigned int plane_no = 0; // 假设要获取大小的平面编号为0unsigned long plane_size = vb2_plane_size(buffer, plane_no);
```
这将返回平面0的大小,即平面的容量大小。
bool vb2_start_streaming_called(struct vb2_queue *q)
返回驱动程序的流式传输状态。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
描述:
该函数用于检查驱动程序的流式传输状态。它返回一个布尔值,指示vb2_start_streaming()方法是否已被调用。
返回值:
- 如果vb2_start_streaming()方法已被调用,则返回true;否则返回false。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue对象bool streaming_status = vb2_start_streaming_called(queue);
```
这将返回驱动程序的流式传输状态。如果vb2_start_streaming()方法已被调用,则streaming_status为true;否则为false。
void vb2_clear_last_buffer_dequeued(struct vb2_queue *q)
清除队列的最后一个已出列缓冲区标志。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
描述:
该函数用于清除队列的最后一个已出列缓冲区标志。通常在每次处理完缓冲区后,可以调用此函数来重置已出列缓冲区的状态。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue对象vb2_clear_last_buffer_dequeued(queue);
```
这将清除队列的最后一个已出列缓冲区标志。
struct vb2_buffer *vb2_get_buffer(struct vb2_queue *q, unsigned int index)
从队列中获取一个缓冲区。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- unsigned int index:要获取的缓冲区的索引。
描述:
该函数通过索引从队列中获取一个缓冲区。请注意,此操作中没有引用计数,因此应考虑缓冲区的生命周期。
返回值:
- 返回获取到的缓冲区的指针,类型为struct vb2_buffer。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue对象
unsigned int buffer_index = 0; // 假设要获取的缓冲区的索引为0struct vb2_buffer *buffer = vb2_get_buffer(queue, buffer_index);
```
这将返回队列中索引为0的缓冲区的指针。
bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
如果缓冲区正在使用中,无法通过VID_IOC_REQBUFS(0)调用释放队列,则返回true。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- struct vb2_buffer *vb:要检查的缓冲区。
描述:
该函数用于检查缓冲区是否正在使用中,即被其他组件或操作占用,并且无法通过调用VID_IOC_REQBUFS(0)来释放队列。
返回值:
- 如果缓冲区正在使用中且队列无法被释放,则返回true;否则返回false。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue对象
struct vb2_buffer *buffer; // 假设有一个vb2_buffer对象bool in_use = vb2_buffer_in_use(queue, buffer);
```
这将检查缓冲区是否正在使用中并且无法释放队列。如果缓冲区正在使用中且队列无法被释放,则in_use为true;否则为false。
int vb2_verify_memory_type(struct vb2_queue *q, enum vb2_memory memory, unsigned int type)
检查内存类型和缓冲区类型是否与队列兼容。
参数:
- struct vb2_queue *q:指向具有videobuf2队列的struct vb2_queue的指针。
- enum vb2_memory memory:内存模型,由enum vb2_memory定义。
- unsigned int type:私有缓冲区类型,其内容由vb2-core调用方定义。例如,对于V4L2,它应与enum v4l2_buf_type中定义的类型匹配。
描述:
此函数用于检查内存类型和缓冲区类型是否与队列兼容。在进行缓冲区操作时,需要确保传递的内存类型和缓冲区类型与队列兼容,以确保正确的操作。
返回值:
- 如果内存类型和缓冲区类型与队列兼容,则返回0;否则返回负数错误代码。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue对象
enum vb2_memory mem_type; // 假设有一个vb2_memory枚举值
unsigned int buffer_type; // 假设有一个私有缓冲区类型int result = vb2_verify_memory_type(queue, mem_type, buffer_type);
if (result < 0) {// 错误处理
} else {// 执行其他操作
}
```
这将检查内存类型和缓冲区类型是否与队列兼容。如果兼容,result将为0,表示通过验证。如果不兼容,result将为负数,表示错误代码,可以根据需要进行适当的错误处理。
bool vb2_request_object_is_buffer(struct media_request_object *obj)
如果对象是缓冲区,则返回true。
参数:
- struct media_request_object *obj:请求对象。
描述:
该函数用于检查给定的请求对象是否是缓冲区。
返回值:
- 如果对象是缓冲区,则返回true;否则返回false。
示例用法:
```
struct media_request_object *request_obj; // 假设有一个media_request_object对象bool is_buffer = vb2_request_object_is_buffer(request_obj);
```
这将检查给定的请求对象是否是缓冲区。如果是缓冲区,则is_buffer为true;否则为false。
unsigned int vb2_request_buffer_cnt(struct media_request *req)
返回请求中缓冲区的数量。
参数:
- struct media_request *req:请求对象。
描述:
该函数用于获取请求中缓冲区的数量。
返回值:
- 返回请求中缓冲区的数量。
示例用法:
```
struct media_request *request; // 假设有一个media_request对象unsigned int buffer_count = vb2_request_buffer_cnt(request);
```
这将返回请求中缓冲区的数量。将数量存储在buffer_count变量中供进一步使用。
struct vb2_v4l2_buffer
v4l2的视频缓冲区信息。
定义
struct vb2_v4l2_buffer {struct vb2_buffer vb2_buf;__u32 flags;__u32 field;struct v4l2_timecode timecode;__u32 sequence;__s32 request_fd;bool is_held;struct vb2_plane planes[VB2_MAX_PLANES];
};
成员
- vb2_buf:嵌入的struct vb2_buffer。
- flags:缓冲区的信息标志。
- field:图像在缓冲区中的场顺序,由enum v4l2_field定义。
- timecode:帧时间码。
- sequence:该帧的序列计数。
- request_fd:与此缓冲区关联的请求文件描述符。
- is_held:如果为true,则表示该捕获缓冲区已被保留。
- planes:平面信息(userptr/fd、长度、已使用字节数、数据偏移量)。
描述
该结构体应包含足够的信息,以涵盖videodev2.h中struct v4l2_buffer的所有字段。
该结构体用于表示v4l2视频缓冲区的信息。它是vb2_buffer结构体的扩展,添加了v4l2特定的字段和平面信息。通过使用这个结构体,可以方便地访问和操作v4l2的视频缓冲区。
int vb2_find_timestamp(const struct vb2_queue *q, u64 timestamp, unsigned int start_idx)
在队列中查找具有给定时间戳的缓冲区。
参数:
- const struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
- u64 timestamp:要查找的时间戳。
- unsigned int start_idx:在缓冲区数组中开始搜索的起始索引(通常为0)。
请注意,可能会有多个具有相同时间戳值的缓冲区,因此可以通过将start_idx设置为先前找到的索引+1来重新开始搜索。
描述:
该函数用于在给定的队列中查找具有给定时间戳的缓冲区。
返回值:
- 返回具有给定时间戳的缓冲区的索引,如果未找到具有时间戳的缓冲区,则返回-1。
示例用法:
```
const struct vb2_queue *queue; // 假设有一个vb2_queue队列对象
u64 timestamp = 1234567890; // 要查找的时间戳
unsigned int start_idx = 0; // 开始搜索的索引int buffer_index = vb2_find_timestamp(queue, timestamp, start_idx);
```
这将在给定的队列中查找具有给定时间戳的缓冲区。如果找到具有时间戳的缓冲区,则将其索引存储在buffer_index变量中;如果未找到具有时间戳的缓冲区,则将-1存储在buffer_index变量中。
int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
这是vb2_core_reqbufs()的包装函数,还会验证内存和类型的值。
参数:
- struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
- struct v4l2_requestbuffers *req:从用户空间传递给驱动程序中v4l2_ioctl_ops->vidioc_reqbufs处理程序的struct v4l2_requestbuffers结构体。
描述:
该函数用于请求分配视频缓冲区,并对内存和类型的值进行验证。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue队列对象
struct v4l2_requestbuffers req; // 假设有一个struct v4l2_requestbuffers结构体对象// 设置req的值
int ret = vb2_reqbufs(queue, &req);
```
注意:在使用此函数之前,应确保已正确设置req结构体的值。该函数将请求为队列分配视频缓冲区,并对内存和类型的值进行验证。返回值ret表示函数执行的结果,成功为0,失败为负数。
int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
这是vb2_core_create_bufs()的包装函数,还会验证内存和类型的值。
参数:
- struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
- struct v4l2_create_buffers *create:从用户空间传递给驱动程序中v4l2_ioctl_ops->vidioc_create_bufs处理程序的struct v4l2_create_buffers结构体。
描述:
该函数用于创建视频缓冲区,并对内存和类型的值进行验证。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue队列对象
struct v4l2_create_buffers create; // 假设有一个struct v4l2_create_buffers结构体对象// 设置create的值
int ret = vb2_create_bufs(queue, &create);
```
注意:在使用此函数之前,应确保已正确设置create结构体的值。该函数将创建视频缓冲区,并对内存和类型的值进行验证。返回值ret表示函数执行的结果,成功为0,失败为负数。
int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev, struct v4l2_buffer *b)
这个函数用于将一个缓冲区从用户空间传递到内核空间。
参数:
- struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
- struct media_device *mdev:指向struct media_device的指针,可以为NULL。
- struct v4l2_buffer *b:从用户空间传递给驱动程序中v4l2_ioctl_ops->vidioc_prepare_buf处理程序的缓冲区结构体。
描述:
该函数应该在驱动程序的v4l2_ioctl_ops->vidioc_prepare_buf ioctl处理程序中被调用。函数执行以下操作:
1)验证传递的缓冲区。
2)调用驱动程序中的vb2_ops->buf_prepare回调函数(如果提供),在此函数中可以执行特定于驱动程序的缓冲区初始化操作。
3)如果b->request_fd非零且mdev->ops->req_queue已设置,则将准备好的缓冲区绑定到请求。
该函数的返回值可直接从驱动程序的v4l2_ioctl_ops->vidioc_prepare_buf处理程序中返回。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue队列对象
struct media_device *media_dev; // 假设有一个media_device对象
struct v4l2_buffer buffer; // 假设有一个v4l2_buffer结构体对象// 设置buffer的值
int ret = vb2_prepare_buf(queue, media_dev, &buffer);
```
注意:在使用此函数之前,应确保已正确设置buffer结构体的值。该函数将验证缓冲区、执行驱动程序中的特定操作,并将准备好的缓冲区绑定到请求(如果适用)。返回值ret表示函数执行的结果,可直接从驱动程序的v4l2_ioctl_ops->vidioc_prepare_buf处理程序中返回。
int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev, struct v4l2_buffer *b)
这个函数用于将一个缓冲区从用户空间入队到队列中。
参数:
- struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
- struct media_device *mdev:指向struct media_device的指针,可以为NULL。
- struct v4l2_buffer *b:从用户空间传递给驱动程序中v4l2_ioctl_ops->vidioc_qbuf处理程序的缓冲区结构体。
描述:
该函数应该在驱动程序的v4l2_ioctl_ops->vidioc_qbuf ioctl处理程序中被调用。函数执行以下操作:
1)验证传递的缓冲区。
2)如果b->request_fd非零且mdev->ops->req_queue已设置,则将缓冲区绑定到请求。
3)如果需要,在驱动程序中调用vb2_ops->buf_prepare回调函数(如果提供),在此函数中可以执行特定于驱动程序的缓冲区初始化操作。
4)如果正在进行流式传输,通过vb2_ops->buf_queue回调函数将缓冲区入队到驱动程序中进行处理。
该函数的返回值可直接从驱动程序的v4l2_ioctl_ops->vidioc_qbuf处理程序中返回。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue队列对象
struct media_device *media_dev; // 假设有一个media_device对象
struct v4l2_buffer buffer; // 假设有一个v4l2_buffer结构体对象// 设置buffer的值
int ret = vb2_qbuf(queue, media_dev, &buffer);
```
注意:在使用此函数之前,应确保已正确设置缓冲区结构体的值。该函数将验证缓冲区、绑定缓冲区到请求(如果适用)、执行驱动程序中的特定操作,并将缓冲区入队到驱动程序进行处理(如果正在进行流式传输)。返回值ret表示函数执行的结果,可直接从驱动程序的v4l2_ioctl_ops->vidioc_qbuf处理程序中返回。
int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb)
这个函数用于将一个缓冲区导出为文件描述符。
参数:
- struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
- struct v4l2_exportbuffer *eb:从用户空间传递给驱动程序中v4l2_ioctl_ops->vidioc_expbuf处理程序的导出缓冲区结构体。
描述:
该函数应该在驱动程序的v4l2_ioctl_ops->vidioc_expbuf ioctl处理程序中被调用。函数执行以下操作:
1)将缓冲区导出为文件描述符,并将文件描述符存储在导出缓冲区结构体的fd字段中。
该函数的返回值可直接从驱动程序的v4l2_ioctl_ops->vidioc_expbuf处理程序中返回。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue队列对象
struct v4l2_exportbuffer export_buffer; // 假设有一个v4l2_exportbuffer结构体对象// 设置export_buffer的值
int ret = vb2_expbuf(queue, &export_buffer);
```
注意:在使用此函数之前,应确保已正确设置导出缓冲区结构体的值。该函数将将缓冲区导出为文件描述符,并将文件描述符存储在导出缓冲区结构体的fd字段中。返回值ret表示函数执行的结果,可直接从驱动程序的v4l2_ioctl_ops->vidioc_expbuf处理程序中返回。
int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
这个函数用于将一个缓冲区从队列中出队到用户空间。
参数:
- struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
- struct v4l2_buffer *b:从用户空间传递给驱动程序中v4l2_ioctl_ops->vidioc_dqbuf处理程序的缓冲区结构体。
- bool nonblocking:表示是否以非阻塞方式出队。
描述:
该函数应该在驱动程序的v4l2_ioctl_ops->vidioc_dqbuf ioctl处理程序中被调用。函数执行以下操作:
1)尝试从队列中出队一个可用缓冲区。
2)如果nonblocking为false并且没有可用缓冲区,则将进程设置为睡眠状态,直到有可用缓冲区或者驱动程序使用vb2_ops->buf_release回调函数释放了某个缓冲区。
3)如果成功出队了一个缓冲区,则将缓冲区的信息存储在传递的缓冲区结构体中。
该函数的返回值可直接从驱动程序的v4l2_ioctl_ops->vidioc_dqbuf处理程序中返回。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue队列对象
struct v4l2_buffer buffer; // 假设有一个v4l2_buffer结构体对象// 设置buffer的值
int ret = vb2_dqbuf(queue, &buffer, false);
```
注意:在使用此函数之前,应确保已正确设置缓冲区结构体的值。该函数将尝试从队列中出队一个可用缓冲区,并将出队的缓冲区的信息存储在传递的缓冲区结构体中。返回值ret表示函数执行的结果,可直接从驱动程序的v4l2_ioctl_ops->vidioc_dqbuf处理程序中返回。
int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
开始数据流传输
参数:
- struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
- enum v4l2_buf_type type:从用户空间传递给驱动程序中vidioc_streamon处理程序的类型参数,由enum v4l2_buf_type定义。
描述:
该函数应该在驱动程序的v4l2_ioctl_ops->vidioc_streamon ioctl处理程序中被调用。函数执行以下操作:
1)验证当前状态。
2)将任何先前排队的缓冲区传递给驱动程序,并开始数据流传输。
该函数的返回值可直接从驱动程序的v4l2_ioctl_ops->vidioc_streamon处理程序中返回。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue队列对象
enum v4l2_buf_type buf_type; // 假设有一个v4l2_buf_type类型的变量// 设置buf_type的值
int ret = vb2_streamon(queue, buf_type);
```
注意:在使用此函数之前,应确保已正确设置缓冲区类型。该函数将验证当前状态,并将任何先前排队的缓冲区传递给驱动程序,然后开始数据流传输。返回值ret表示函数执行的结果,可直接从驱动程序的v4l2_ioctl_ops->vidioc_streamon处理程序中返回。
int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
停止数据流传输
参数:
- struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
- enum v4l2_buf_type type:从用户空间传递给驱动程序中vidioc_streamoff处理程序的类型参数,由enum v4l2_buf_type定义。
描述:
该函数应该在驱动程序的vidioc_streamoff处理程序中被调用。函数执行以下操作:
1)验证当前状态。
2)停止数据流传输,并从队列中出队任何排队的缓冲区,包括先前传递给驱动程序的缓冲区(等待驱动程序完成后)。
此调用可用于暂停播放。该函数的返回值可直接从驱动程序的vidioc_streamoff处理程序中返回。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue队列对象
enum v4l2_buf_type buf_type; // 假设有一个v4l2_buf_type类型的变量// 设置buf_type的值
int ret = vb2_streamoff(queue, buf_type);
```
注意:在使用此函数之前,应确保已正确设置缓冲区类型。该函数将验证当前状态,并停止数据流传输,并将任何排队的缓冲区出队,包括先前传递给驱动程序的缓冲区(等待驱动程序完成后)。返回值ret表示函数执行的结果,可直接从驱动程序的vidioc_streamoff处理程序中返回。
int vb2_queue_init(struct vb2_queue *q)
初始化videobuf2队列
参数:
- struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
描述:
vb2_queue结构体应该由驱动程序进行分配。在调用此函数之前,驱动程序应负责清除其内容并为一些必需的条目设置初始值。
其中,q->ops、q->mem_ops、q->type和q->io_modes是强制要求的。更多信息请参考include/media/videobuf2-core.h中struct vb2_queue的描述。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue队列对象// 在分配和设置其他必需的条目之后,进行队列初始化
int ret = vb2_queue_init(queue);
```
注意:在调用此函数之前,驱动程序应负责分配vb2_queue结构体,并设置其中的必需条目。该函数将根据提供的初始化值对队列进行初始化。返回值ret表示函数执行的结果。
int vb2_queue_init_name(struct vb2_queue *q, const char *name)
使用名称初始化videobuf2队列
参数:
- struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
- const char *name:队列的名称。
描述:
此函数与vb2_queue_init()相同,还会设置队列的名称。队列名称用于日志记录的目的,并且应在所属设备的上下文中唯一标识队列。这对于将内核日志消息归属到正确的队列非常有用,例如m2m设备或其他处理多个队列的设备。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue队列对象
const char *queue_name = "my_vb2_queue"; // 设置队列的名称// 在分配和设置其他必需的条目之后,使用名称进行队列初始化
int ret = vb2_queue_init_name(queue, queue_name);
```
注意:在调用此函数之前,驱动程序应负责分配vb2_queue结构体,并设置其中的必需条目。该函数将根据提供的初始化值对队列进行初始化,并设置队列的名称。返回值ret表示函数执行的结果。
void vb2_queue_release(struct vb2_queue *q)
停止流传输,释放队列并释放内存
参数:
- struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
描述:
此函数停止流传输并执行必要的清理工作,包括释放视频缓冲区内存。驱动程序负责释放vb2_queue结构体本身。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue队列对象// 停止流传输并释放队列及相关内存
vb2_queue_release(queue);
```
注意:该函数将停止流传输并释放与队列相关的资源,但不会负责释放vb2_queue结构体本身。驱动程序应自行负责释放vb2_queue结构体。
int vb2_queue_change_type(struct vb2_queue *q, unsigned int type)
更改非活动vb2_queue的类型
参数:
- struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
- unsigned int type:要更改为的类型(V4L2_BUF_TYPE_VIDEO_*)。
描述:
此函数更改vb2_queue的类型。只有在队列未忙碌(即没有分配缓冲区)时才能进行此操作。
vb2_queue_change_type()可用于使用同一队列支持多个缓冲区类型。驱动程序可以实现v4l2_ioctl_ops.vidioc_reqbufs和v4l2_ioctl_ops.vidioc_create_bufs函数,并在调用vb2_ioctl_reqbufs()或vb2_ioctl_create_bufs()之前调用vb2_queue_change_type(),从而“锁定”缓冲区类型,直到缓冲区被释放。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue队列对象
unsigned int new_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 要更改为的新类型// 更改队列的类型
int ret = vb2_queue_change_type(queue, new_type);
```
注意:在调用此函数之前,请确保队列处于非活动状态,即没有已分配的缓冲区。返回值ret表示函数执行的结果。
__poll_t vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
实现用于poll用户空间操作的函数
参数:
- struct vb2_queue *q:指向带有videobuf2队列的struct vb2_queue的指针。
- struct file *file:传递给poll文件操作处理程序的文件参数。
- poll_table *wait:传递给poll文件操作处理程序的等待参数。
描述:
该函数实现了驱动程序的poll文件操作处理程序。对于CAPTURE队列,如果有缓冲区准备好出队列,用户空间将被通知视频设备的文件描述符可用于读取。对于OUTPUT队列,如果有缓冲区准备好出队列,文件描述符将被报告为可用于写入。
如果驱动程序使用struct v4l2_fh,则vb2_poll()还将检查是否有任何挂起的事件。
该函数的返回值可直接从驱动程序中的poll处理程序返回。
示例用法:
```
struct vb2_queue *queue; // 假设有一个vb2_queue队列对象
struct file *file; // 假设有一个文件对象
poll_table wait; // 假设有一个poll_table对象// 在驱动程序的poll处理程序中调用vb2_poll
__poll_t ret = vb2_poll(queue, file, &wait);// 将返回值直接返回给poll处理程序
return ret;
```
void vb2_video_unregister_device(struct video_device *vdev)
注销视频设备并释放队列
参数:
- struct video_device *vdev:指向struct video_device的指针。
描述:
如果驱动程序使用vb2_fop_release()/_vb2_fop_release(),则应该使用vb2_video_unregister_device()而不是video_unregister_device()。
该函数将调用video_unregister_device(),然后在流传输进行中时释放vb2_queue。这将停止流传输,并简化解绑序列,因为在此调用之后,所有的子设备也将停止流传输。
示例用法:
```
struct video_device *vdev; // 假设有一个视频设备对象// 注销视频设备并释放队列
vb2_video_unregister_device(vdev);
```
注意:在调用此函数之前,请确保在流传输期间停止了流传输。
void vb2_ops_wait_prepare(struct vb2_queue *vq)
锁定struct vb2_queue的辅助函数
参数:
- struct vb2_queue *vq:指向struct vb2_queue的指针。
描述:
注意:只在vq->lock非空时使用。
该函数是一个辅助函数,用于锁定struct vb2_queue对象。在使用该函数之前,请确保vq->lock不为空。
示例用法:
```
struct vb2_queue *vq; // 假设有一个vb2_queue队列对象// 在需要锁定队列的地方调用vb2_ops_wait_prepare
vb2_ops_wait_prepare(vq);
```
注意:在调用此函数之前,请确保vq->lock不为空,以避免空指针异常。
void vb2_ops_wait_finish(struct vb2_queue *vq)
解锁struct vb2_queue的辅助函数
参数:
- struct vb2_queue *vq:指向struct vb2_queue的指针。
描述:
注意:只在vq->lock非空时使用。
该函数是一个辅助函数,用于解锁struct vb2_queue对象。在使用该函数之前,请确保vq->lock不为空。
示例用法:
```
struct vb2_queue *vq; // 假设有一个vb2_queue队列对象// 在需要解锁队列的地方调用vb2_ops_wait_finish
vb2_ops_wait_finish(vq);
```
注意:在调用此函数之前,请确保vq->lock不为空,以避免空指针异常。
struct vb2_vmarea_handler
通用的vma引用计数跟踪处理程序。
定义:
struct vb2_vmarea_handler {refcount_t *refcount;void (*put)(void *arg);void *arg;
};
成员:
- refcount:指向缓冲区中refcount_t条目的指针。
- put:用于减少缓冲区引用计数的回调函数。
- arg:put回调函数的参数。
解释:
vb2_vmarea_handler结构体定义了一个通用的vma引用计数跟踪处理程序。它包含了以下成员:
- refcount:指向缓冲区中refcount_t类型的引用计数条目的指针。该引用计数用于跟踪缓冲区的引用数量。
- put:一个回调函数,用于减少缓冲区的引用计数。通过调用put函数,可以减少对缓冲区的引用计数,当引用计数达到零时,可以采取相应的操作(如释放缓冲区)。
- arg:put回调函数的参数。这是一个可选的参数,可根据需要传递给put回调函数。
示例用法:
```
struct vb2_vmarea_handler handler;
refcount_t refcount;
void my_put_callback(void *arg);// 设置refcount和put回调函数
handler.refcount = &refcount;
handler.put = my_put_callback;
handler.arg = NULL;// 使用vb2_vmarea_handler对象进行引用计数跟踪
// ...在代码中对缓冲区进行操作,并使用handler来增加/减少引用计数...// 在适当的位置,调用put回调函数来减少引用计数
handler.put(handler.arg);
```
注意:在实际使用时,需要确保正确初始化和管理vb2_vmarea_handler对象,并根据具体需求实现相关的put回调函数。