xref: /openbmc/linux/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
1ff768f59SMauro Carvalho ChehabChinese translated version of Documentation/driver-api/media/index.rst
29544a2daSSeongJae Park
39544a2daSSeongJae ParkIf you have any comment or update to the content, please contact the
49544a2daSSeongJae Parkoriginal document maintainer directly.  However, if you have a problem
59544a2daSSeongJae Parkcommunicating in English you can also ask the Chinese maintainer for
69544a2daSSeongJae Parkhelp.  Contact the Chinese maintainer if this translation is outdated
79544a2daSSeongJae Parkor if there is a problem with the translation.
89544a2daSSeongJae Park
932590819SMauro Carvalho ChehabMaintainer: Mauro Carvalho Chehab <mchehab@kernel.org>
109544a2daSSeongJae ParkChinese maintainer: Fu Wei <tekkamanninja@gmail.com>
119544a2daSSeongJae Park---------------------------------------------------------------------
12ff768f59SMauro Carvalho ChehabDocumentation/driver-api/media/index.rst 的中文翻译
139544a2daSSeongJae Park
149544a2daSSeongJae Park如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
159544a2daSSeongJae Park交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
169544a2daSSeongJae Park译存在问题,请联系中文版维护者。
1732590819SMauro Carvalho Chehab英文版维护者: Mauro Carvalho Chehab <mchehab@kernel.org>
189544a2daSSeongJae Park中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
199544a2daSSeongJae Park中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
209544a2daSSeongJae Park中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
219544a2daSSeongJae Park
229544a2daSSeongJae Park
239544a2daSSeongJae Park以下为正文
249544a2daSSeongJae Park---------------------------------------------------------------------
259544a2daSSeongJae ParkV4L2 驱动框架概览
269544a2daSSeongJae Park==============
279544a2daSSeongJae Park
289544a2daSSeongJae Park本文档描述 V4L2 框架所提供的各种结构和它们之间的关系。
299544a2daSSeongJae Park
309544a2daSSeongJae Park
319544a2daSSeongJae Park介绍
329544a2daSSeongJae Park----
339544a2daSSeongJae Park
349544a2daSSeongJae Park大部分现代 V4L2 设备由多个 IC 组成,在 /dev 下导出多个设备节点,
359544a2daSSeongJae Park并同时创建非 V4L2 设备(如 DVB、ALSA、FB、I2C 和红外输入设备)。
369544a2daSSeongJae Park由于这种硬件的复杂性,V4L2 驱动也变得非常复杂。
379544a2daSSeongJae Park
389544a2daSSeongJae Park尤其是 V4L2 必须支持 IC 实现音视频的多路复用和编解码,这就更增加了其
399544a2daSSeongJae Park复杂性。通常这些 IC 通过一个或多个 I2C 总线连接到主桥驱动器,但也可
409544a2daSSeongJae Park使用其他总线。这些设备称为“子设备”。
419544a2daSSeongJae Park
429544a2daSSeongJae Park长期以来,这个框架仅限于通过 video_device 结构体创建 V4L 设备节点,
439544a2daSSeongJae Park并使用 video_buf 处理视频缓冲(注:本文不讨论 video_buf 框架)。
449544a2daSSeongJae Park
459544a2daSSeongJae Park这意味着所有驱动必须自己设置设备实例并连接到子设备。其中一部分要正确地
469544a2daSSeongJae Park完成是比较复杂的,使得许多驱动都没有正确地实现。
479544a2daSSeongJae Park
489544a2daSSeongJae Park由于框架的缺失,有很多通用代码都不可重复利用。
499544a2daSSeongJae Park
509544a2daSSeongJae Park因此,这个框架构建所有驱动都需要的基本结构块,而统一的框架将使通用代码
519544a2daSSeongJae Park创建成实用函数并在所有驱动中共享变得更加容易。
529544a2daSSeongJae Park
539544a2daSSeongJae Park
549544a2daSSeongJae Park驱动结构
559544a2daSSeongJae Park-------
569544a2daSSeongJae Park
579544a2daSSeongJae Park所有 V4L2 驱动都有如下结构:
589544a2daSSeongJae Park
599544a2daSSeongJae Park1) 每个设备实例的结构体--包含其设备状态。
609544a2daSSeongJae Park
619544a2daSSeongJae Park2) 初始化和控制子设备的方法(如果有)。
629544a2daSSeongJae Park
639544a2daSSeongJae Park3) 创建 V4L2 设备节点 (/dev/videoX/dev/vbiX/dev/radioX)
649544a2daSSeongJae Park   并跟踪设备节点的特定数据。
659544a2daSSeongJae Park
669544a2daSSeongJae Park4) 特定文件句柄结构体--包含每个文件句柄的数据。
679544a2daSSeongJae Park
689544a2daSSeongJae Park5) 视频缓冲处理。
699544a2daSSeongJae Park
709544a2daSSeongJae Park以下是它们的初略关系图:
719544a2daSSeongJae Park
729544a2daSSeongJae Park    device instances(设备实例)
739544a2daSSeongJae Park      |
749544a2daSSeongJae Park      +-sub-device instances(子设备实例)
759544a2daSSeongJae Park      |
769544a2daSSeongJae Park      \-V4L2 device nodes(V4L2 设备节点)
779544a2daSSeongJae Park	  |
789544a2daSSeongJae Park	  \-filehandle instances(文件句柄实例)
799544a2daSSeongJae Park
809544a2daSSeongJae Park
819544a2daSSeongJae Park框架结构
829544a2daSSeongJae Park-------
839544a2daSSeongJae Park
849544a2daSSeongJae Park该框架非常类似驱动结构:它有一个 v4l2_device 结构用于保存设备
859544a2daSSeongJae Park实例的数据;一个 v4l2_subdev 结构体代表子设备实例;video_device
869544a2daSSeongJae Park结构体保存 V4L2 设备节点的数据;将来 v4l2_fh 结构体将跟踪文件句柄
879544a2daSSeongJae Park实例(暂未尚未实现)。
889544a2daSSeongJae Park
899544a2daSSeongJae ParkV4L2 框架也可与媒体框架整合(可选的)。如果驱动设置了 v4l2_device
909544a2daSSeongJae Park结构体的 mdev 域,子设备和视频节点的入口将自动出现在媒体框架中。
919544a2daSSeongJae Park
929544a2daSSeongJae Park
939544a2daSSeongJae Parkv4l2_device 结构体
949544a2daSSeongJae Park----------------
959544a2daSSeongJae Park
969544a2daSSeongJae Park每个设备实例都通过 v4l2_device (v4l2-device.h)结构体来表示。
979544a2daSSeongJae Park简单设备可以仅分配这个结构体,但在大多数情况下,都会将这个结构体
989544a2daSSeongJae Park嵌入到一个更大的结构体中。
999544a2daSSeongJae Park
1009544a2daSSeongJae Park你必须注册这个设备实例:
1019544a2daSSeongJae Park
1029544a2daSSeongJae Park	v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
1039544a2daSSeongJae Park
1049544a2daSSeongJae Park注册操作将会初始化 v4l2_device 结构体。如果 dev->driver_data 域
1059544a2daSSeongJae Park为 NULL,就将其指向 v4l2_dev。
1069544a2daSSeongJae Park
1079544a2daSSeongJae Park需要与媒体框架整合的驱动必须手动设置 dev->driver_data,指向包含
1089544a2daSSeongJae Parkv4l2_device 结构体实例的驱动特定设备结构体。这可以在注册 V4L2 设备
1099544a2daSSeongJae Park实例前通过 dev_set_drvdata() 函数完成。同时必须设置 v4l2_device
1109544a2daSSeongJae Park结构体的 mdev 域,指向适当的初始化并注册过的 media_device 实例。
1119544a2daSSeongJae Park
1129544a2daSSeongJae Park如果 v4l2_dev->name 为空,则它将被设置为从 dev 中衍生出的值(为了
1139544a2daSSeongJae Park更加精确,形式为驱动名后跟 bus_id)。如果你在调用 v4l2_device_register
1149544a2daSSeongJae Park前已经设置好了,则不会被修改。如果 dev 为 NULL,则你*必须*在调用
1159544a2daSSeongJae Parkv4l2_device_register 前设置 v4l2_dev->name。
1169544a2daSSeongJae Park
1179544a2daSSeongJae Park你可以基于驱动名和驱动的全局 atomic_t 类型的实例编号,通过
1189544a2daSSeongJae Parkv4l2_device_set_name() 设置 name。这样会生成类似 ivtv0、ivtv1 等
1199544a2daSSeongJae Park名字。若驱动名以数字结尾,则会在编号和驱动名间插入一个破折号,如:
1209544a2daSSeongJae Parkcx18-0、cx18-1 等。此函数返回实例编号。
1219544a2daSSeongJae Park
1229544a2daSSeongJae Park第一个 “dev” 参数通常是一个指向 pci_dev、usb_interface 或
1239544a2daSSeongJae Parkplatform_device 的指针。很少使其为 NULL,除非是一个ISA设备或者
1249544a2daSSeongJae Park当一个设备创建了多个 PCI 设备,使得 v4l2_dev 无法与一个特定的父设备
1259544a2daSSeongJae Park关联。
1269544a2daSSeongJae Park
1279544a2daSSeongJae Park你也可以提供一个 notify() 回调,使子设备可以调用它实现事件通知。
1289544a2daSSeongJae Park但这个设置与子设备相关。子设备支持的任何通知必须在
1299544a2daSSeongJae Parkinclude/media/<subdevice>.h 中定义一个消息头。
1309544a2daSSeongJae Park
1319544a2daSSeongJae Park注销 v4l2_device 使用如下函数:
1329544a2daSSeongJae Park
1339544a2daSSeongJae Park	v4l2_device_unregister(struct v4l2_device *v4l2_dev);
1349544a2daSSeongJae Park
1359544a2daSSeongJae Park如果 dev->driver_data 域指向 v4l2_dev,将会被重置为 NULL。注销同时
1369544a2daSSeongJae Park会自动从设备中注销所有子设备。
1379544a2daSSeongJae Park
1389544a2daSSeongJae Park如果你有一个热插拔设备(如USB设备),则当断开发生时,父设备将无效。
1399544a2daSSeongJae Park由于 v4l2_device 有一个指向父设备的指针必须被清除,同时标志父设备
1409544a2daSSeongJae Park已消失,所以必须调用以下函数:
1419544a2daSSeongJae Park
1429544a2daSSeongJae Park	v4l2_device_disconnect(struct v4l2_device *v4l2_dev);
1439544a2daSSeongJae Park
1449544a2daSSeongJae Park这个函数并*不*注销子设备,因此你依然要调用 v4l2_device_unregister()
1459544a2daSSeongJae Park函数。如果你的驱动器并非热插拔的,就没有必要调用 v4l2_device_disconnect()。
1469544a2daSSeongJae Park
1479544a2daSSeongJae Park有时你需要遍历所有被特定驱动注册的设备。这通常发生在多个设备驱动使用
1489544a2daSSeongJae Park同一个硬件的情况下。如:ivtvfb 驱动是一个使用 ivtv 硬件的帧缓冲驱动,
1499544a2daSSeongJae Park同时 alsa 驱动也使用此硬件。
1509544a2daSSeongJae Park
1519544a2daSSeongJae Park你可以使用如下例程遍历所有注册的设备:
1529544a2daSSeongJae Park
1539544a2daSSeongJae Parkstatic int callback(struct device *dev, void *p)
1549544a2daSSeongJae Park{
1559544a2daSSeongJae Park	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
1569544a2daSSeongJae Park
1579544a2daSSeongJae Park	/* 测试这个设备是否已经初始化 */
1589544a2daSSeongJae Park	if (v4l2_dev == NULL)
1599544a2daSSeongJae Park		return 0;
1609544a2daSSeongJae Park	...
1619544a2daSSeongJae Park	return 0;
1629544a2daSSeongJae Park}
1639544a2daSSeongJae Park
1649544a2daSSeongJae Parkint iterate(void *p)
1659544a2daSSeongJae Park{
1669544a2daSSeongJae Park	struct device_driver *drv;
1679544a2daSSeongJae Park	int err;
1689544a2daSSeongJae Park
1699544a2daSSeongJae Park	/* 在PCI 总线上查找ivtv驱动。
1709544a2daSSeongJae Park	   pci_bus_type是全局的. 对于USB总线使用usb_bus_type。 */
1719544a2daSSeongJae Park	drv = driver_find("ivtv", &pci_bus_type);
1729544a2daSSeongJae Park	/* 遍历所有的ivtv设备实例 */
1739544a2daSSeongJae Park	err = driver_for_each_device(drv, NULL, p, callback);
1749544a2daSSeongJae Park	put_driver(drv);
1759544a2daSSeongJae Park	return err;
1769544a2daSSeongJae Park}
1779544a2daSSeongJae Park
1789544a2daSSeongJae Park有时你需要一个设备实例的运行计数。这个通常用于映射一个设备实例到一个
1799544a2daSSeongJae Park模块选择数组的索引。
1809544a2daSSeongJae Park
1819544a2daSSeongJae Park推荐方法如下:
1829544a2daSSeongJae Park
1839544a2daSSeongJae Parkstatic atomic_t drv_instance = ATOMIC_INIT(0);
1849544a2daSSeongJae Park
1859544a2daSSeongJae Parkstatic int drv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
1869544a2daSSeongJae Park{
1879544a2daSSeongJae Park	...
1889544a2daSSeongJae Park	state->instance = atomic_inc_return(&drv_instance) - 1;
1899544a2daSSeongJae Park}
1909544a2daSSeongJae Park
1919544a2daSSeongJae Park如果你有多个设备节点,对于热插拔设备,知道何时注销 v4l2_device 结构体
1929544a2daSSeongJae Park就比较困难。为此 v4l2_device 有引用计数支持。当调用 video_register_device
1939544a2daSSeongJae Park时增加引用计数,而设备节点释放时减小引用计数。当引用计数为零,则
1949544a2daSSeongJae Parkv4l2_device 的release() 回调将被执行。你就可以在此时做最后的清理工作。
1959544a2daSSeongJae Park
1969544a2daSSeongJae Park如果创建了其他设备节点(比如 ALSA),则你可以通过以下函数手动增减
1979544a2daSSeongJae Park引用计数:
1989544a2daSSeongJae Park
1999544a2daSSeongJae Parkvoid v4l2_device_get(struct v4l2_device *v4l2_dev);
2009544a2daSSeongJae Park
2019544a2daSSeongJae Park或:
2029544a2daSSeongJae Park
2039544a2daSSeongJae Parkint v4l2_device_put(struct v4l2_device *v4l2_dev);
2049544a2daSSeongJae Park
2059544a2daSSeongJae Park由于引用技术初始化为 1 ,你也需要在 disconnect() 回调(对于 USB 设备)中
2069544a2daSSeongJae Park调用 v4l2_device_put,或者 remove() 回调(例如对于 PCI 设备),否则
2079544a2daSSeongJae Park引用计数将永远不会为 0 。
2089544a2daSSeongJae Park
2099544a2daSSeongJae Parkv4l2_subdev结构体
2109544a2daSSeongJae Park------------------
2119544a2daSSeongJae Park
2129544a2daSSeongJae Park许多驱动需要与子设备通信。这些设备可以完成各种任务,但通常他们负责
2139544a2daSSeongJae Park音视频复用和编解码。如网络摄像头的子设备通常是传感器和摄像头控制器。
2149544a2daSSeongJae Park
2159544a2daSSeongJae Park这些一般为 I2C 接口设备,但并不一定都是。为了给驱动提供调用子设备的
2169544a2daSSeongJae Park统一接口,v4l2_subdev 结构体(v4l2-subdev.h)产生了。
2179544a2daSSeongJae Park
2189544a2daSSeongJae Park每个子设备驱动都必须有一个 v4l2_subdev 结构体。这个结构体可以单独
2199544a2daSSeongJae Park代表一个简单的子设备,也可以嵌入到一个更大的结构体中,与更多设备状态
2209544a2daSSeongJae Park信息保存在一起。通常有一个下级设备结构体(比如:i2c_client)包含了
2219544a2daSSeongJae Park内核创建的设备数据。建议使用 v4l2_set_subdevdata() 将这个结构体的
2229544a2daSSeongJae Park指针保存在 v4l2_subdev 的私有数据域(dev_priv)中。这使得通过 v4l2_subdev
2239544a2daSSeongJae Park找到实际的低层总线特定设备数据变得容易。
2249544a2daSSeongJae Park
2259544a2daSSeongJae Park你同时需要一个从低层结构体获取 v4l2_subdev 指针的方法。对于常用的
2269544a2daSSeongJae Parki2c_client 结构体,i2c_set_clientdata() 函数可用于保存一个 v4l2_subdev
2279544a2daSSeongJae Park指针;对于其他总线你可能需要使用其他相关函数。
2289544a2daSSeongJae Park
2299544a2daSSeongJae Park桥驱动中也应保存每个子设备的私有数据,比如一个指向特定桥的各设备私有
2309544a2daSSeongJae Park数据的指针。为此 v4l2_subdev 结构体提供主机私有数据域(host_priv),
2319544a2daSSeongJae Park并可通过 v4l2_get_subdev_hostdata() 和 v4l2_set_subdev_hostdata()
2329544a2daSSeongJae Park访问。
2339544a2daSSeongJae Park
2349544a2daSSeongJae Park从总线桥驱动的视角,驱动加载子设备模块并以某种方式获得 v4l2_subdev
2359544a2daSSeongJae Park结构体指针。对于 i2c 总线设备相对简单:调用 i2c_get_clientdata()。
2369544a2daSSeongJae Park对于其他总线也需要做类似的操作。针对 I2C 总线上的子设备辅助函数帮你
2379544a2daSSeongJae Park完成了大部分复杂的工作。
2389544a2daSSeongJae Park
2399544a2daSSeongJae Park每个 v4l2_subdev 都包含子设备驱动需要实现的函数指针(如果对此设备
2409544a2daSSeongJae Park不适用,可为NULL)。由于子设备可完成许多不同的工作,而在一个庞大的
2419544a2daSSeongJae Park函数指针结构体中通常仅有少数有用的函数实现其功能肯定不合适。所以,
2429544a2daSSeongJae Park函数指针根据其实现的功能被分类,每一类都有自己的函数指针结构体。
2439544a2daSSeongJae Park
2449544a2daSSeongJae Park顶层函数指针结构体包含了指向各类函数指针结构体的指针,如果子设备驱动
2459544a2daSSeongJae Park不支持该类函数中的任何一个功能,则指向该类结构体的指针为NULL。
2469544a2daSSeongJae Park
2479544a2daSSeongJae Park这些结构体定义如下:
2489544a2daSSeongJae Park
2499544a2daSSeongJae Parkstruct v4l2_subdev_core_ops {
2509544a2daSSeongJae Park	int (*log_status)(struct v4l2_subdev *sd);
2519544a2daSSeongJae Park	int (*init)(struct v4l2_subdev *sd, u32 val);
2529544a2daSSeongJae Park	...
2539544a2daSSeongJae Park};
2549544a2daSSeongJae Park
2559544a2daSSeongJae Parkstruct v4l2_subdev_tuner_ops {
2569544a2daSSeongJae Park	...
2579544a2daSSeongJae Park};
2589544a2daSSeongJae Park
2599544a2daSSeongJae Parkstruct v4l2_subdev_audio_ops {
2609544a2daSSeongJae Park	...
2619544a2daSSeongJae Park};
2629544a2daSSeongJae Park
2639544a2daSSeongJae Parkstruct v4l2_subdev_video_ops {
2649544a2daSSeongJae Park	...
2659544a2daSSeongJae Park};
2669544a2daSSeongJae Park
2679544a2daSSeongJae Parkstruct v4l2_subdev_pad_ops {
2689544a2daSSeongJae Park	...
2699544a2daSSeongJae Park};
2709544a2daSSeongJae Park
2719544a2daSSeongJae Parkstruct v4l2_subdev_ops {
2729544a2daSSeongJae Park	const struct v4l2_subdev_core_ops  *core;
2739544a2daSSeongJae Park	const struct v4l2_subdev_tuner_ops *tuner;
2749544a2daSSeongJae Park	const struct v4l2_subdev_audio_ops *audio;
2759544a2daSSeongJae Park	const struct v4l2_subdev_video_ops *video;
2769544a2daSSeongJae Park	const struct v4l2_subdev_pad_ops *video;
2779544a2daSSeongJae Park};
2789544a2daSSeongJae Park
2799544a2daSSeongJae Park其中 core(核心)函数集通常可用于所有子设备,其他类别的实现依赖于
2809544a2daSSeongJae Park子设备。如视频设备可能不支持音频操作函数,反之亦然。
2819544a2daSSeongJae Park
2829544a2daSSeongJae Park这样的设置在限制了函数指针数量的同时,还使增加新的操作函数和分类
2839544a2daSSeongJae Park变得较为容易。
2849544a2daSSeongJae Park
2859544a2daSSeongJae Park子设备驱动可使用如下函数初始化 v4l2_subdev 结构体:
2869544a2daSSeongJae Park
2879544a2daSSeongJae Park	v4l2_subdev_init(sd, &ops);
2889544a2daSSeongJae Park
2899544a2daSSeongJae Park然后,你必须用一个唯一的名字初始化 subdev->name,并初始化模块的
2909544a2daSSeongJae Parkowner 域。若使用 i2c 辅助函数,这些都会帮你处理好。
2919544a2daSSeongJae Park
2929544a2daSSeongJae Park若需同媒体框架整合,你必须调用 media_entity_pads_init() 初始化 v4l2_subdev
2939544a2daSSeongJae Park结构体中的 media_entity 结构体(entity 域):
2949544a2daSSeongJae Park
2959544a2daSSeongJae Park	struct media_pad *pads = &my_sd->pads;
2969544a2daSSeongJae Park	int err;
2979544a2daSSeongJae Park
2989544a2daSSeongJae Park	err = media_entity_pads_init(&sd->entity, npads, pads);
2999544a2daSSeongJae Park
3009544a2daSSeongJae Parkpads 数组必须预先初始化。无须手动设置 media_entity 的 type 和
3019544a2daSSeongJae Parkname 域,但如有必要,revision 域必须初始化。
3029544a2daSSeongJae Park
3039544a2daSSeongJae Park当(任何)子设备节点被打开/关闭,对 entity 的引用将被自动获取/释放。
3049544a2daSSeongJae Park
3059544a2daSSeongJae Park在子设备被注销之后,不要忘记清理 media_entity 结构体:
3069544a2daSSeongJae Park
3079544a2daSSeongJae Park	media_entity_cleanup(&sd->entity);
3089544a2daSSeongJae Park
3099544a2daSSeongJae Park如果子设备驱动趋向于处理视频并整合进了媒体框架,必须使用 v4l2_subdev_pad_ops
3109544a2daSSeongJae Park替代 v4l2_subdev_video_ops 实现格式相关的功能。
3119544a2daSSeongJae Park
3129544a2daSSeongJae Park这种情况下,子设备驱动应该设置 link_validate 域,以提供它自身的链接
3139544a2daSSeongJae Park验证函数。链接验证函数应对管道(两端链接的都是 V4L2 子设备)中的每个
3149544a2daSSeongJae Park链接调用。驱动还要负责验证子设备和视频节点间格式配置的正确性。
3159544a2daSSeongJae Park
3169544a2daSSeongJae Park如果 link_validate 操作没有设置,默认的 v4l2_subdev_link_validate_default()
3179544a2daSSeongJae Park函数将会被调用。这个函数保证宽、高和媒体总线像素格式在链接的收发两端
3189544a2daSSeongJae Park都一致。子设备驱动除了它们自己的检测外,也可以自由使用这个函数以执行
3199544a2daSSeongJae Park上面提到的检查。
3209544a2daSSeongJae Park
3219544a2daSSeongJae Park设备(桥)驱动程序必须向 v4l2_device 注册 v4l2_subdev:
3229544a2daSSeongJae Park
3239544a2daSSeongJae Park	int err = v4l2_device_register_subdev(v4l2_dev, sd);
3249544a2daSSeongJae Park
3259544a2daSSeongJae Park如果子设备模块在它注册前消失,这个操作可能失败。在这个函数成功返回后,
3269544a2daSSeongJae Parksubdev->dev 域就指向了 v4l2_device。
3279544a2daSSeongJae Park
3289544a2daSSeongJae Park如果 v4l2_device 父设备的 mdev 域为非 NULL 值,则子设备实体将被自动
3299544a2daSSeongJae Park注册为媒体设备。
3309544a2daSSeongJae Park
3319544a2daSSeongJae Park注销子设备则可用如下函数:
3329544a2daSSeongJae Park
3339544a2daSSeongJae Park	v4l2_device_unregister_subdev(sd);
3349544a2daSSeongJae Park
3359544a2daSSeongJae Park此后,子设备模块就可卸载,且 sd->dev == NULL。
3369544a2daSSeongJae Park
3379544a2daSSeongJae Park注册之设备后,可通过以下方式直接调用其操作函数:
3389544a2daSSeongJae Park
3399544a2daSSeongJae Park	err = sd->ops->core->g_std(sd, &norm);
3409544a2daSSeongJae Park
3419544a2daSSeongJae Park但使用如下宏会比较容易且合适:
3429544a2daSSeongJae Park
3439544a2daSSeongJae Park	err = v4l2_subdev_call(sd, core, g_std, &norm);
3449544a2daSSeongJae Park
3459544a2daSSeongJae Park这个宏将会做 NULL 指针检查,如果 subdev 为 NULL,则返回-ENODEV;如果
3469544a2daSSeongJae Parksubdev->core 或 subdev->core->g_std 为 NULL,则返回 -ENOIOCTLCMD;
3479544a2daSSeongJae Park否则将返回 subdev->ops->core->g_std ops 调用的实际结果。
3489544a2daSSeongJae Park
3499544a2daSSeongJae Park有时也可能同时调用所有或一系列子设备的某个操作函数:
3509544a2daSSeongJae Park
3519544a2daSSeongJae Park	v4l2_device_call_all(v4l2_dev, 0, core, g_std, &norm);
3529544a2daSSeongJae Park
3539544a2daSSeongJae Park任何不支持此操作的子设备都会被跳过,并忽略错误返回值。但如果你需要
3549544a2daSSeongJae Park检查出错码,则可使用如下函数:
3559544a2daSSeongJae Park
3569544a2daSSeongJae Park	err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_std, &norm);
3579544a2daSSeongJae Park
3589544a2daSSeongJae Park除 -ENOIOCTLCMD 外的任何错误都会跳出循环并返回错误值。如果(除 -ENOIOCTLCMD
3599544a2daSSeongJae Park外)没有错误发生,则返回 0。
3609544a2daSSeongJae Park
3619544a2daSSeongJae Park对于以上两个函数的第二个参数为组 ID。如果为 0,则所有子设备都会执行
3629544a2daSSeongJae Park这个操作。如果为非 0 值,则只有那些组 ID 匹配的子设备才会执行此操作。
3639544a2daSSeongJae Park在桥驱动注册一个子设备前,可以设置 sd->grp_id 为任何期望值(默认值为
3649544a2daSSeongJae Park0)。这个值属于桥驱动,且子设备驱动将不会修改和使用它。
3659544a2daSSeongJae Park
3669544a2daSSeongJae Park组 ID 赋予了桥驱动更多对于如何调用回调的控制。例如,电路板上有多个
3679544a2daSSeongJae Park音频芯片,每个都有改变音量的能力。但当用户想要改变音量的时候,通常
3689544a2daSSeongJae Park只有一个会被实际使用。你可以对这样的子设备设置组 ID 为(例如 AUDIO_CONTROLLER)
3699544a2daSSeongJae Park并在调用 v4l2_device_call_all() 时指定它为组 ID 值。这就保证了只有
3709544a2daSSeongJae Park需要的子设备才会执行这个回调。
3719544a2daSSeongJae Park
3729544a2daSSeongJae Park如果子设备需要通知它的 v4l2_device 父设备一个事件,可以调用
3739544a2daSSeongJae Parkv4l2_subdev_notify(sd, notification, arg)。这个宏检查是否有一个
3749544a2daSSeongJae Parknotify() 回调被注册,如果没有,返回 -ENODEV。否则返回 notify() 调用
3759544a2daSSeongJae Park结果。
3769544a2daSSeongJae Park
3779544a2daSSeongJae Park使用 v4l2_subdev 的好处在于它是一个通用结构体,且不包含任何底层硬件
3789544a2daSSeongJae Park信息。所有驱动可以包含多个 I2C 总线的子设备,但也有子设备是通过 GPIO
3799544a2daSSeongJae Park控制。这个区别仅在配置设备时有关系,一旦子设备注册完成,对于 v4l2
3809544a2daSSeongJae Park子系统来说就完全透明了。
3819544a2daSSeongJae Park
3829544a2daSSeongJae Park
3839544a2daSSeongJae ParkV4L2 子设备用户空间API
3849544a2daSSeongJae Park--------------------
3859544a2daSSeongJae Park
3869544a2daSSeongJae Park除了通过 v4l2_subdev_ops 结构导出的内核 API,V4L2 子设备也可以直接
3879544a2daSSeongJae Park通过用户空间应用程序来控制。
3889544a2daSSeongJae Park
3899544a2daSSeongJae Park可以在 /dev 中创建名为 v4l-subdevX 设备节点,以通过其直接访问子设备。
3909544a2daSSeongJae Park如果子设备支持用户空间直接配置,必须在注册前设置 V4L2_SUBDEV_FL_HAS_DEVNODE
3919544a2daSSeongJae Park标志。
3929544a2daSSeongJae Park
3939544a2daSSeongJae Park注册子设备之后, v4l2_device 驱动会通过调用 v4l2_device_register_subdev_nodes()
3949544a2daSSeongJae Park函数为所有已注册并设置了 V4L2_SUBDEV_FL_HAS_DEVNODE 的子设备创建
3959544a2daSSeongJae Park设备节点。这些设备节点会在子设备注销时自动删除。
3969544a2daSSeongJae Park
3979544a2daSSeongJae Park这些设备节点处理 V4L2 API 的一个子集。
3989544a2daSSeongJae Park
3999544a2daSSeongJae ParkVIDIOC_QUERYCTRL
4009544a2daSSeongJae ParkVIDIOC_QUERYMENU
4019544a2daSSeongJae ParkVIDIOC_G_CTRL
4029544a2daSSeongJae ParkVIDIOC_S_CTRL
4039544a2daSSeongJae ParkVIDIOC_G_EXT_CTRLS
4049544a2daSSeongJae ParkVIDIOC_S_EXT_CTRLS
4059544a2daSSeongJae ParkVIDIOC_TRY_EXT_CTRLS
4069544a2daSSeongJae Park
4079544a2daSSeongJae Park	这些 ioctls 控制与 V4L2 中定义的一致。他们行为相同,唯一的
4089544a2daSSeongJae Park	不同是他们只处理子设备的控制实现。根据驱动程序,这些控制也
4099544a2daSSeongJae Park	可以通过一个(或多个) V4L2 设备节点访问。
4109544a2daSSeongJae Park
4119544a2daSSeongJae ParkVIDIOC_DQEVENT
4129544a2daSSeongJae ParkVIDIOC_SUBSCRIBE_EVENT
4139544a2daSSeongJae ParkVIDIOC_UNSUBSCRIBE_EVENT
4149544a2daSSeongJae Park
4159544a2daSSeongJae Park	这些  ioctls 事件与 V4L2 中定义的一致。他们行为相同,唯一的
4169544a2daSSeongJae Park	不同是他们只处理子设备产生的事件。根据驱动程序,这些事件也
4179544a2daSSeongJae Park	可以通过一个(或多个) V4L2 设备节点上报。
4189544a2daSSeongJae Park
4199544a2daSSeongJae Park	要使用事件通知的子设备驱动,在注册子设备前必须在 v4l2_subdev::flags
4209544a2daSSeongJae Park	中设置 V4L2_SUBDEV_USES_EVENTS 并在 v4l2_subdev::nevents
4219544a2daSSeongJae Park	中初始化事件队列深度。注册完成后,事件会在 v4l2_subdev::devnode
4229544a2daSSeongJae Park	设备节点中像通常一样被排队。
4239544a2daSSeongJae Park
4249544a2daSSeongJae Park	为正确支持事件机制,poll() 文件操作也应被实现。
4259544a2daSSeongJae Park
4269544a2daSSeongJae Park私有 ioctls
4279544a2daSSeongJae Park
4289544a2daSSeongJae Park	不在以上列表中的所有 ioctls 会通过 core::ioctl 操作直接传递
4299544a2daSSeongJae Park	给子设备驱动。
4309544a2daSSeongJae Park
4319544a2daSSeongJae Park
4329544a2daSSeongJae ParkI2C 子设备驱动
4339544a2daSSeongJae Park-------------
4349544a2daSSeongJae Park
4359544a2daSSeongJae Park由于这些驱动很常见,所以内特提供了特定的辅助函数(v4l2-common.h)让这些
4369544a2daSSeongJae Park设备的使用更加容易。
4379544a2daSSeongJae Park
4389544a2daSSeongJae Park添加 v4l2_subdev 支持的推荐方法是让 I2C 驱动将 v4l2_subdev 结构体
4399544a2daSSeongJae Park嵌入到为每个 I2C 设备实例创建的状态结构体中。而最简单的设备没有状态
4409544a2daSSeongJae Park结构体,此时可以直接创建一个 v4l2_subdev 结构体。
4419544a2daSSeongJae Park
4429544a2daSSeongJae Park一个典型的状态结构体如下所示(‘chipname’用芯片名代替):
4439544a2daSSeongJae Park
4449544a2daSSeongJae Parkstruct chipname_state {
4459544a2daSSeongJae Park	struct v4l2_subdev sd;
4469544a2daSSeongJae Park	...  /* 附加的状态域*/
4479544a2daSSeongJae Park};
4489544a2daSSeongJae Park
4499544a2daSSeongJae Park初始化 v4l2_subdev 结构体的方法如下:
4509544a2daSSeongJae Park
4519544a2daSSeongJae Park	v4l2_i2c_subdev_init(&state->sd, client, subdev_ops);
4529544a2daSSeongJae Park
4539544a2daSSeongJae Park这个函数将填充 v4l2_subdev 结构体中的所有域,并保证 v4l2_subdev 和
4549544a2daSSeongJae Parki2c_client 都指向彼此。
4559544a2daSSeongJae Park
4569544a2daSSeongJae Park同时,你也应该为从 v4l2_subdev 指针找到 chipname_state 结构体指针
4579544a2daSSeongJae Park添加一个辅助内联函数。
4589544a2daSSeongJae Park
4599544a2daSSeongJae Parkstatic inline struct chipname_state *to_state(struct v4l2_subdev *sd)
4609544a2daSSeongJae Park{
4619544a2daSSeongJae Park	return container_of(sd, struct chipname_state, sd);
4629544a2daSSeongJae Park}
4639544a2daSSeongJae Park
4649544a2daSSeongJae Park使用以下函数可以通过 v4l2_subdev 结构体指针获得 i2c_client 结构体
4659544a2daSSeongJae Park指针:
4669544a2daSSeongJae Park
4679544a2daSSeongJae Park	struct i2c_client *client = v4l2_get_subdevdata(sd);
4689544a2daSSeongJae Park
4699544a2daSSeongJae Park而以下函数则相反,通过 i2c_client 结构体指针获得 v4l2_subdev 结构体
4709544a2daSSeongJae Park指针:
4719544a2daSSeongJae Park
4729544a2daSSeongJae Park	struct v4l2_subdev *sd = i2c_get_clientdata(client);
4739544a2daSSeongJae Park
4749544a2daSSeongJae Park当 remove()函数被调用前,必须保证先调用 v4l2_device_unregister_subdev(sd)。
4759544a2daSSeongJae Park此操作将会从桥驱动中注销子设备。即使子设备没有注册,调用此函数也是
4769544a2daSSeongJae Park安全的。
4779544a2daSSeongJae Park
4789544a2daSSeongJae Park必须这样做的原因是:当桥驱动注销 i2c 适配器时,remove()回调函数
4799544a2daSSeongJae Park会被那个适配器上的 i2c 设备调用。此后,相应的 v4l2_subdev 结构体
4809544a2daSSeongJae Park就不存在了,所有它们必须先被注销。在 remove()回调函数中调用
4819544a2daSSeongJae Parkv4l2_device_unregister_subdev(sd),可以保证执行总是正确的。
4829544a2daSSeongJae Park
4839544a2daSSeongJae Park
4849544a2daSSeongJae Park桥驱动也有一些辅组函数可用:
4859544a2daSSeongJae Park
4869544a2daSSeongJae Parkstruct v4l2_subdev *sd = v4l2_i2c_new_subdev(v4l2_dev, adapter,
4879544a2daSSeongJae Park	       "module_foo", "chipid", 0x36, NULL);
4889544a2daSSeongJae Park
4899544a2daSSeongJae Park这个函数会加载给定的模块(如果没有模块需要加载,可以为 NULL),
4909544a2daSSeongJae Park并用给定的 i2c 适配器结构体指针(i2c_adapter)和 器件地址(chip/address491*6feb76dbSWolfram Sang作为参数调用 i2c_new_client_device()。如果一切顺利,则就在 v4l2_device
4929544a2daSSeongJae Park中注册了子设备。
4939544a2daSSeongJae Park
4949544a2daSSeongJae Park你也可以利用 v4l2_i2c_new_subdev()的最后一个参数,传递一个可能的
4959544a2daSSeongJae ParkI2C 地址数组,让函数自动探测。这些探测地址只有在前一个参数为 0 的
4969544a2daSSeongJae Park情况下使用。非零参数意味着你知道准确的 i2c 地址,所以此时无须进行
4979544a2daSSeongJae Park探测。
4989544a2daSSeongJae Park
4999544a2daSSeongJae Park如果出错,两个函数都返回 NULL。
5009544a2daSSeongJae Park
5019544a2daSSeongJae Park注意:传递给 v4l2_i2c_new_subdev()的 chipid 通常与模块名一致。
5029544a2daSSeongJae Park它允许你指定一个芯片的变体,比如“saa7114”或“saa7115”。一般通过
5039544a2daSSeongJae Parki2c 驱动自动探测。chipid 的使用是在今后需要深入了解的事情。这个与
5049544a2daSSeongJae Parki2c 驱动不同,较容易混淆。要知道支持哪些芯片变体,你可以查阅 i2c
5059544a2daSSeongJae Park驱动代码的 i2c_device_id 表,上面列出了所有可能支持的芯片。
5069544a2daSSeongJae Park
5079544a2daSSeongJae Park还有两个辅助函数:
5089544a2daSSeongJae Park
5099544a2daSSeongJae Parkv4l2_i2c_new_subdev_cfg:这个函数添加新的 irq 和 platform_data
5109544a2daSSeongJae Park参数,并有‘addr’和‘probed_addrs’参数:如果 addr 非零,则被使用
5119544a2daSSeongJae Park(不探测变体),否则 probed_addrs 中的地址将用于自动探测。
5129544a2daSSeongJae Park
5139544a2daSSeongJae Park例如:以下代码将会探测地址(0x10):
5149544a2daSSeongJae Park
5159544a2daSSeongJae Parkstruct v4l2_subdev *sd = v4l2_i2c_new_subdev_cfg(v4l2_dev, adapter,
5169544a2daSSeongJae Park	       "module_foo", "chipid", 0, NULL, 0, I2C_ADDRS(0x10));
5179544a2daSSeongJae Park
5189544a2daSSeongJae Parkv4l2_i2c_new_subdev_board 使用一个 i2c_board_info 结构体,将其
5199544a2daSSeongJae Park替代 irq、platform_data 和 add r参数传递给 i2c 驱动。
5209544a2daSSeongJae Park
5219544a2daSSeongJae Park如果子设备支持 s_config 核心操作,这个操作会在子设备配置好之后以 irq 和
5229544a2daSSeongJae Parkplatform_data 为参数调用。早期的 v4l2_i2c_new_(probed_)subdev 函数
5239544a2daSSeongJae Park同样也会调用 s_config,但仅在 irq 为 0 且 platform_data 为 NULL 时。
5249544a2daSSeongJae Park
5259544a2daSSeongJae Parkvideo_device结构体
5269544a2daSSeongJae Park-----------------
5279544a2daSSeongJae Park
5289544a2daSSeongJae Park在 /dev 目录下的实际设备节点根据 video_device 结构体(v4l2-dev.h)
5299544a2daSSeongJae Park创建。此结构体既可以动态分配也可以嵌入到一个更大的结构体中。
5309544a2daSSeongJae Park
5319544a2daSSeongJae Park动态分配方法如下:
5329544a2daSSeongJae Park
5339544a2daSSeongJae Park	struct video_device *vdev = video_device_alloc();
5349544a2daSSeongJae Park
5359544a2daSSeongJae Park	if (vdev == NULL)
5369544a2daSSeongJae Park		return -ENOMEM;
5379544a2daSSeongJae Park
5389544a2daSSeongJae Park	vdev->release = video_device_release;
5399544a2daSSeongJae Park
5409544a2daSSeongJae Park如果将其嵌入到一个大结构体中,则必须自己实现 release()回调。
5419544a2daSSeongJae Park
5429544a2daSSeongJae Park	struct video_device *vdev = &my_vdev->vdev;
5439544a2daSSeongJae Park
5449544a2daSSeongJae Park	vdev->release = my_vdev_release;
5459544a2daSSeongJae Park
5469544a2daSSeongJae Parkrelease()回调必须被设置,且在最后一个 video_device 用户退出之后
5479544a2daSSeongJae Park被调用。
5489544a2daSSeongJae Park
5499544a2daSSeongJae Park默认的 video_device_release()回调只是调用 kfree 来释放之前分配的
5509544a2daSSeongJae Park内存。
5519544a2daSSeongJae Park
5529544a2daSSeongJae Park你应该设置这些域:
5539544a2daSSeongJae Park
5549544a2daSSeongJae Park- v4l2_dev: 设置为 v4l2_device 父设备。
5559544a2daSSeongJae Park
5569544a2daSSeongJae Park- name: 设置为唯一的描述性设备名。
5579544a2daSSeongJae Park
5589544a2daSSeongJae Park- fops: 设置为已有的 v4l2_file_operations 结构体。
5599544a2daSSeongJae Park
5609544a2daSSeongJae Park- ioctl_ops: 如果你使用v4l2_ioctl_ops 来简化 ioctl 的维护
5619544a2daSSeongJae Park  (强烈建议使用,且将来可能变为强制性的!),然后设置你自己的
5629544a2daSSeongJae Park  v4l2_ioctl_ops 结构体.
5639544a2daSSeongJae Park
5649544a2daSSeongJae Park- lock: 如果你要在驱动中实现所有的锁操作,则设为 NULL 。否则
5659544a2daSSeongJae Park  就要设置一个指向 struct mutex_lock 结构体的指针,这个锁将
5669544a2daSSeongJae Park  在 unlocked_ioctl 文件操作被调用前由内核获得,并在调用返回后
5679544a2daSSeongJae Park  释放。详见下一节。
5689544a2daSSeongJae Park
5699544a2daSSeongJae Park- prio: 保持对优先级的跟踪。用于实现 VIDIOC_G/S_PRIORITY。如果
5709544a2daSSeongJae Park  设置为 NULL,则会使用 v4l2_device 中的 v4l2_prio_state 结构体。
5719544a2daSSeongJae Park  如果要对每个设备节点(组)实现独立的优先级,可以将其指向自己
5729544a2daSSeongJae Park  实现的 v4l2_prio_state 结构体。
5739544a2daSSeongJae Park
5749544a2daSSeongJae Park- parent: 仅在使用 NULL 作为父设备结构体参数注册 v4l2_device 时
5759544a2daSSeongJae Park  设置此参数。只有在一个硬件设备包含多一个 PCI 设备,共享同一个
5769544a2daSSeongJae Park  v4l2_device 核心时才会发生。
5779544a2daSSeongJae Park
5789544a2daSSeongJae Park  cx88 驱动就是一个例子:一个 v4l2_device 结构体核心,被一个裸的
5799544a2daSSeongJae Park  视频 PCI 设备(cx8800)和一个 MPEG PCI 设备(cx8802)共用。由于
5809544a2daSSeongJae Park  v4l2_device 无法与特定的 PCI 设备关联,所有没有设置父设备。但当
5819544a2daSSeongJae Park  video_device 配置后,就知道使用哪个父 PCI 设备了。
5829544a2daSSeongJae Park
5839544a2daSSeongJae Park如果你使用 v4l2_ioctl_ops,则应该在 v4l2_file_operations 结构体中
5849544a2daSSeongJae Park设置 .unlocked_ioctl 指向 video_ioctl2。
5859544a2daSSeongJae Park
5869544a2daSSeongJae Park请勿使用 .ioctl!它已被废弃,今后将消失。
5879544a2daSSeongJae Park
5889544a2daSSeongJae Park某些情况下你要告诉核心:你在 v4l2_ioctl_ops 指定的某个函数应被忽略。
5899544a2daSSeongJae Park你可以在 video_device_register 被调用前通过以下函数标记这个 ioctls。
5909544a2daSSeongJae Park
5919544a2daSSeongJae Parkvoid v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd);
5929544a2daSSeongJae Park
5939544a2daSSeongJae Park基于外部因素(例如某个板卡已被使用),在不创建新结构体的情况下,你想
5949544a2daSSeongJae Park要关闭 v4l2_ioctl_ops 中某个特性往往需要这个机制。
5959544a2daSSeongJae Park
5969544a2daSSeongJae Parkv4l2_file_operations 结构体是 file_operations 的一个子集。其主要
5979544a2daSSeongJae Park区别在于:因 inode 参数从未被使用,它将被忽略。
5989544a2daSSeongJae Park
5999544a2daSSeongJae Park如果需要与媒体框架整合,你必须通过调用 media_entity_pads_init() 初始化
6009544a2daSSeongJae Park嵌入在 video_device 结构体中的 media_entity(entity 域)结构体:
6019544a2daSSeongJae Park
6029544a2daSSeongJae Park	struct media_pad *pad = &my_vdev->pad;
6039544a2daSSeongJae Park	int err;
6049544a2daSSeongJae Park
6059544a2daSSeongJae Park	err = media_entity_pads_init(&vdev->entity, 1, pad);
6069544a2daSSeongJae Park
6079544a2daSSeongJae Parkpads 数组必须预先初始化。没有必要手动设置 media_entity 的 type 和
6089544a2daSSeongJae Parkname 域。
6099544a2daSSeongJae Park
6109544a2daSSeongJae Park当(任何)子设备节点被打开/关闭,对 entity 的引用将被自动获取/释放。
6119544a2daSSeongJae Park
6129544a2daSSeongJae Parkv4l2_file_operations 与锁
6139544a2daSSeongJae Park--------------------------
6149544a2daSSeongJae Park
6159544a2daSSeongJae Park你可以在 video_device 结构体中设置一个指向 mutex_lock 的指针。通常
6169544a2daSSeongJae Park这既可是一个顶层互斥锁也可为设备节点自身的互斥锁。默认情况下,此锁
6179544a2daSSeongJae Park用于 unlocked_ioctl,但为了使用 ioctls 你通过以下函数可禁用锁定:
6189544a2daSSeongJae Park
6199544a2daSSeongJae Park	void v4l2_disable_ioctl_locking(struct video_device *vdev, unsigned int cmd);
6209544a2daSSeongJae Park
6219544a2daSSeongJae Park例如: v4l2_disable_ioctl_locking(vdev, VIDIOC_DQBUF);
6229544a2daSSeongJae Park
6239544a2daSSeongJae Park你必须在注册 video_device 前调用这个函数。
6249544a2daSSeongJae Park
6259544a2daSSeongJae Park特别是对于 USB 驱动程序,某些命令(如设置控制)需要很长的时间,可能
6269544a2daSSeongJae Park需要自行为缓冲区队列的 ioctls 实现锁定。
6279544a2daSSeongJae Park
6289544a2daSSeongJae Park如果你需要更细粒度的锁,你必须设置 mutex_lock 为 NULL,并完全自己实现
6299544a2daSSeongJae Park锁机制。
6309544a2daSSeongJae Park
6319544a2daSSeongJae Park这完全由驱动开发者决定使用何种方法。然而,如果你的驱动存在长延时操作
6329544a2daSSeongJae Park(例如,改变 USB 摄像头的曝光时间可能需要较长时间),而你又想让用户
6339544a2daSSeongJae Park在等待长延时操作完成期间做其他的事,则你最好自己实现锁机制。
6349544a2daSSeongJae Park
6359544a2daSSeongJae Park如果指定一个锁,则所有 ioctl 操作将在这个锁的作用下串行执行。如果你
6369544a2daSSeongJae Park使用 videobuf,则必须将同一个锁传递给 videobuf 队列初始化函数;如
6379544a2daSSeongJae Parkvideobuf 必须等待一帧的到达,则可临时解锁并在这之后重新上锁。如果驱动
6389544a2daSSeongJae Park也在代码执行期间等待,则可做同样的工作(临时解锁,再上锁)让其他进程
6399544a2daSSeongJae Park可以在第一个进程阻塞时访问设备节点。
6409544a2daSSeongJae Park
6419544a2daSSeongJae Park在使用 videobuf2 的情况下,必须实现 wait_prepare 和 wait_finish 回调
6429544a2daSSeongJae Park在适当的时候解锁/加锁。进一步来说,如果你在 video_device 结构体中使用
6439544a2daSSeongJae Park锁,则必须在 wait_prepare 和 wait_finish 中对这个互斥锁进行解锁/加锁。
6449544a2daSSeongJae Park
6459544a2daSSeongJae Park热插拔的断开实现也必须在调用 v4l2_device_disconnect 前获得锁。
6469544a2daSSeongJae Park
6479544a2daSSeongJae Parkvideo_device注册
6489544a2daSSeongJae Park---------------
6499544a2daSSeongJae Park
6509544a2daSSeongJae Park接下来你需要注册视频设备:这会为你创建一个字符设备。
6519544a2daSSeongJae Park
652238e4a5bSHans Verkuil	err = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
6539544a2daSSeongJae Park	if (err) {
6549544a2daSSeongJae Park		video_device_release(vdev); /* or kfree(my_vdev); */
6559544a2daSSeongJae Park		return err;
6569544a2daSSeongJae Park	}
6579544a2daSSeongJae Park
6589544a2daSSeongJae Park如果 v4l2_device 父设备的 mdev 域为非 NULL 值,视频设备实体将自动
6599544a2daSSeongJae Park注册为媒体设备。
6609544a2daSSeongJae Park
6619544a2daSSeongJae Park注册哪种设备是根据类型(type)参数。存在以下类型:
6629544a2daSSeongJae Park
663238e4a5bSHans VerkuilVFL_TYPE_VIDEO: 用于视频输入/输出设备的 videoX
6649544a2daSSeongJae ParkVFL_TYPE_VBI: 用于垂直消隐数据的 vbiX (例如,隐藏式字幕,图文电视)
6659544a2daSSeongJae ParkVFL_TYPE_RADIO: 用于广播调谐器的 radioX
6669544a2daSSeongJae Park
6679544a2daSSeongJae Park最后一个参数让你确定一个所控制设备的设备节点号数量(例如 videoX 中的 X)。
6689544a2daSSeongJae Park通常你可以传入-1,让 v4l2 框架自己选择第一个空闲的编号。但是有时用户
6699544a2daSSeongJae Park需要选择一个特定的节点号。驱动允许用户通过驱动模块参数选择一个特定的
6709544a2daSSeongJae Park设备节点号是很普遍的。这个编号将会传递给这个函数,且 video_register_device
6719544a2daSSeongJae Park将会试图选择这个设备节点号。如果这个编号被占用,下一个空闲的设备节点
6729544a2daSSeongJae Park编号将被选中,并向内核日志中发送一个警告信息。
6739544a2daSSeongJae Park
6749544a2daSSeongJae Park另一个使用场景是当驱动创建多个设备时。这种情况下,对不同的视频设备在
6759544a2daSSeongJae Park编号上使用不同的范围是很有用的。例如,视频捕获设备从 0 开始,视频
6769544a2daSSeongJae Park输出设备从 16 开始。所以你可以使用最后一个参数来指定设备节点号最小值,
6779544a2daSSeongJae Park而 v4l2 框架会试图选择第一个的空闲编号(等于或大于你提供的编号)。
6789544a2daSSeongJae Park如果失败,则它会就选择第一个空闲的编号。
6799544a2daSSeongJae Park
6809544a2daSSeongJae Park由于这种情况下,你会忽略无法选择特定设备节点号的警告,则可调用
6819544a2daSSeongJae Parkvideo_register_device_no_warn() 函数避免警告信息的产生。
6829544a2daSSeongJae Park
6839544a2daSSeongJae Park只要设备节点被创建,一些属性也会同时创建。在 /sys/class/video4linux
6849544a2daSSeongJae Park目录中你会找到这些设备。例如进入其中的 video0 目录,你会看到‘name’和
6859544a2daSSeongJae Park‘index’属性。‘name’属性值就是 video_device 结构体中的‘name’域。
6869544a2daSSeongJae Park
6879544a2daSSeongJae Park‘index’属性值就是设备节点的索引值:每次调用 video_register_device(),
6889544a2daSSeongJae Park索引值都递增 1 。第一个视频设备节点总是从索引值 0 开始。
6899544a2daSSeongJae Park
6909544a2daSSeongJae Park用户可以设置 udev 规则,利用索引属性生成花哨的设备名(例如:用‘mpegX’
6919544a2daSSeongJae Park代表 MPEG 视频捕获设备节点)。
6929544a2daSSeongJae Park
6939544a2daSSeongJae Park在设备成功注册后,就可以使用这些域:
6949544a2daSSeongJae Park
6959544a2daSSeongJae Park- vfl_type: 传递给 video_register_device 的设备类型。
6969544a2daSSeongJae Park- minor: 已指派的次设备号。
6979544a2daSSeongJae Park- num: 设备节点编号 (例如 videoX 中的 X)。
6989544a2daSSeongJae Park- index: 设备索引号。
6999544a2daSSeongJae Park
7009544a2daSSeongJae Park如果注册失败,你必须调用 video_device_release() 来释放已分配的
7019544a2daSSeongJae Parkvideo_device 结构体;如果 video_device 是嵌入在自己创建的结构体中,
7029544a2daSSeongJae Park你也必须释放它。vdev->release() 回调不会在注册失败之后被调用,
7039544a2daSSeongJae Park你也不应试图在注册失败后注销设备。
7049544a2daSSeongJae Park
7059544a2daSSeongJae Park
7069544a2daSSeongJae Parkvideo_device 注销
7079544a2daSSeongJae Park----------------
7089544a2daSSeongJae Park
7099544a2daSSeongJae Park当视频设备节点已被移除,不论是卸载驱动还是USB设备断开,你都应注销
7109544a2daSSeongJae Park它们:
7119544a2daSSeongJae Park
7129544a2daSSeongJae Park	video_unregister_device(vdev);
7139544a2daSSeongJae Park
7149544a2daSSeongJae Park这个操作将从 sysfs 中移除设备节点(导致 udev 将其从 /dev 中移除)。
7159544a2daSSeongJae Park
7169544a2daSSeongJae Parkvideo_unregister_device() 返回之后,就无法完成打开操作。尽管如此,
7179544a2daSSeongJae ParkUSB 设备的情况则不同,某些应用程序可能依然打开着其中一个已注销设备
7189544a2daSSeongJae Park节点。所以在注销之后,所有文件操作(当然除了 release )也应返回错误值。
7199544a2daSSeongJae Park
7209544a2daSSeongJae Park当最后一个视频设备节点的用户退出,则 vdev->release() 回调会被调用,
7219544a2daSSeongJae Park并且你可以做最后的清理操作。
7229544a2daSSeongJae Park
7239544a2daSSeongJae Park不要忘记清理与视频设备相关的媒体入口(如果被初始化过):
7249544a2daSSeongJae Park
7259544a2daSSeongJae Park	media_entity_cleanup(&vdev->entity);
7269544a2daSSeongJae Park
7279544a2daSSeongJae Park这可以在 release 回调中完成。
7289544a2daSSeongJae Park
7299544a2daSSeongJae Park
7309544a2daSSeongJae Parkvideo_device 辅助函数
7319544a2daSSeongJae Park---------------------
7329544a2daSSeongJae Park
7339544a2daSSeongJae Park一些有用的辅助函数如下:
7349544a2daSSeongJae Park
7359544a2daSSeongJae Park- file/video_device 私有数据
7369544a2daSSeongJae Park
7379544a2daSSeongJae Park你可以用以下函数在 video_device 结构体中设置/获取驱动私有数据:
7389544a2daSSeongJae Park
7399544a2daSSeongJae Parkvoid *video_get_drvdata(struct video_device *vdev);
7409544a2daSSeongJae Parkvoid video_set_drvdata(struct video_device *vdev, void *data);
7419544a2daSSeongJae Park
7429544a2daSSeongJae Park注意:在调用 video_register_device() 前执行 video_set_drvdata()
7439544a2daSSeongJae Park是安全的。
7449544a2daSSeongJae Park
7459544a2daSSeongJae Park而以下函数:
7469544a2daSSeongJae Park
7479544a2daSSeongJae Parkstruct video_device *video_devdata(struct file *file);
7489544a2daSSeongJae Park
7499544a2daSSeongJae Park返回 file 结构体中拥有的的 video_device 指针。
7509544a2daSSeongJae Park
7519544a2daSSeongJae Parkvideo_drvdata 辅助函数结合了 video_get_drvdata 和 video_devdata
7529544a2daSSeongJae Park的功能:
7539544a2daSSeongJae Park
7549544a2daSSeongJae Parkvoid *video_drvdata(struct file *file);
7559544a2daSSeongJae Park
7569544a2daSSeongJae Park你可以使用如下代码从 video_device 结构体中获取 v4l2_device 结构体
7579544a2daSSeongJae Park指针:
7589544a2daSSeongJae Park
7599544a2daSSeongJae Parkstruct v4l2_device *v4l2_dev = vdev->v4l2_dev;
7609544a2daSSeongJae Park
7619544a2daSSeongJae Park- 设备节点名
7629544a2daSSeongJae Park
7639544a2daSSeongJae Parkvideo_device 设备节点在内核中的名称可以通过以下函数获得
7649544a2daSSeongJae Park
7659544a2daSSeongJae Parkconst char *video_device_node_name(struct video_device *vdev);
7669544a2daSSeongJae Park
7679544a2daSSeongJae Park这个名字被用户空间工具(例如 udev)作为提示信息使用。应尽可能使用
7689544a2daSSeongJae Park此功能,而非访问 video_device::num 和 video_device::minor 域。
7699544a2daSSeongJae Park
7709544a2daSSeongJae Park
7719544a2daSSeongJae Park视频缓冲辅助函数
7729544a2daSSeongJae Park---------------
7739544a2daSSeongJae Park
7749544a2daSSeongJae Parkv4l2 核心 API 提供了一个处理视频缓冲的标准方法(称为“videobuf”)。
7759544a2daSSeongJae Park这些方法使驱动可以通过统一的方式实现 read()、mmap() 和 overlay()。
7769544a2daSSeongJae Park目前在设备上支持视频缓冲的方法有分散/聚集 DMA(videobuf-dma-sg)、
7779544a2daSSeongJae Park线性 DMA(videobuf-dma-contig)以及大多用于 USB 设备的用 vmalloc
7789544a2daSSeongJae Park分配的缓冲(videobuf-vmalloc)。
7799544a2daSSeongJae Park
780ff768f59SMauro Carvalho Chehab请参阅 Documentation/driver-api/media/v4l2-videobuf.rst,以获得更多关于 videobuf
7819544a2daSSeongJae Park层的使用信息。
7829544a2daSSeongJae Park
7839544a2daSSeongJae Parkv4l2_fh 结构体
7849544a2daSSeongJae Park-------------
7859544a2daSSeongJae Park
7869544a2daSSeongJae Parkv4l2_fh 结构体提供一个保存用于 V4L2 框架的文件句柄特定数据的简单方法。
7879544a2daSSeongJae Park如果 video_device 标志,新驱动
7889544a2daSSeongJae Park必须使用 v4l2_fh 结构体,因为它也用于实现优先级处理(VIDIOC_G/S_PRIORITY)。
7899544a2daSSeongJae Park
7909544a2daSSeongJae Parkv4l2_fh 的用户(位于 V4l2 框架中,并非驱动)可通过测试
7919544a2daSSeongJae Parkvideo_device->flags 中的 V4L2_FL_USES_V4L2_FH 位得知驱动是否使用
7929544a2daSSeongJae Parkv4l2_fh 作为他的 file->private_data 指针。这个位会在调用 v4l2_fh_init()
7939544a2daSSeongJae Park时被设置。
7949544a2daSSeongJae Park
7959544a2daSSeongJae Parkv4l2_fh 结构体作为驱动自身文件句柄结构体的一部分被分配,且驱动在
7969544a2daSSeongJae Park其打开函数中将 file->private_data 指向它。
7979544a2daSSeongJae Park
7989544a2daSSeongJae Park在许多情况下,v4l2_fh 结构体会嵌入到一个更大的结构体中。这钟情况下,
7999544a2daSSeongJae Park应该在 open() 中调用 v4l2_fh_init+v4l2_fh_add,并在 release() 中
8009544a2daSSeongJae Park调用 v4l2_fh_del+v4l2_fh_exit。
8019544a2daSSeongJae Park
8029544a2daSSeongJae Park驱动可以通过使用 container_of 宏提取他们自己的文件句柄结构体。例如:
8039544a2daSSeongJae Park
8049544a2daSSeongJae Parkstruct my_fh {
8059544a2daSSeongJae Park	int blah;
8069544a2daSSeongJae Park	struct v4l2_fh fh;
8079544a2daSSeongJae Park};
8089544a2daSSeongJae Park
8099544a2daSSeongJae Park...
8109544a2daSSeongJae Park
8119544a2daSSeongJae Parkint my_open(struct file *file)
8129544a2daSSeongJae Park{
8139544a2daSSeongJae Park	struct my_fh *my_fh;
8149544a2daSSeongJae Park	struct video_device *vfd;
8159544a2daSSeongJae Park	int ret;
8169544a2daSSeongJae Park
8179544a2daSSeongJae Park	...
8189544a2daSSeongJae Park
8199544a2daSSeongJae Park	my_fh = kzalloc(sizeof(*my_fh), GFP_KERNEL);
8209544a2daSSeongJae Park
8219544a2daSSeongJae Park	...
8229544a2daSSeongJae Park
8239544a2daSSeongJae Park	v4l2_fh_init(&my_fh->fh, vfd);
8249544a2daSSeongJae Park
8259544a2daSSeongJae Park	...
8269544a2daSSeongJae Park
8279544a2daSSeongJae Park	file->private_data = &my_fh->fh;
8289544a2daSSeongJae Park	v4l2_fh_add(&my_fh->fh);
8299544a2daSSeongJae Park	return 0;
8309544a2daSSeongJae Park}
8319544a2daSSeongJae Park
8329544a2daSSeongJae Parkint my_release(struct file *file)
8339544a2daSSeongJae Park{
8349544a2daSSeongJae Park	struct v4l2_fh *fh = file->private_data;
8359544a2daSSeongJae Park	struct my_fh *my_fh = container_of(fh, struct my_fh, fh);
8369544a2daSSeongJae Park
8379544a2daSSeongJae Park	...
8389544a2daSSeongJae Park	v4l2_fh_del(&my_fh->fh);
8399544a2daSSeongJae Park	v4l2_fh_exit(&my_fh->fh);
8409544a2daSSeongJae Park	kfree(my_fh);
8419544a2daSSeongJae Park	return 0;
8429544a2daSSeongJae Park}
8439544a2daSSeongJae Park
8449544a2daSSeongJae Park以下是 v4l2_fh 函数使用的简介:
8459544a2daSSeongJae Park
8469544a2daSSeongJae Parkvoid v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
8479544a2daSSeongJae Park
8489544a2daSSeongJae Park  初始化文件句柄。这*必须*在驱动的 v4l2_file_operations->open()
8499544a2daSSeongJae Park  函数中执行。
8509544a2daSSeongJae Park
8519544a2daSSeongJae Parkvoid v4l2_fh_add(struct v4l2_fh *fh)
8529544a2daSSeongJae Park
8539544a2daSSeongJae Park  添加一个 v4l2_fh 到 video_device 文件句柄列表。一旦文件句柄
8549544a2daSSeongJae Park  初始化完成就必须调用。
8559544a2daSSeongJae Park
8569544a2daSSeongJae Parkvoid v4l2_fh_del(struct v4l2_fh *fh)
8579544a2daSSeongJae Park
8589544a2daSSeongJae Park  从 video_device() 中解除文件句柄的关联。文件句柄的退出函数也
8599544a2daSSeongJae Park  将被调用。
8609544a2daSSeongJae Park
8619544a2daSSeongJae Parkvoid v4l2_fh_exit(struct v4l2_fh *fh)
8629544a2daSSeongJae Park
8639544a2daSSeongJae Park  清理文件句柄。在清理完 v4l2_fh 后,相关内存会被释放。
8649544a2daSSeongJae Park
8659544a2daSSeongJae Park
8669544a2daSSeongJae Park如果 v4l2_fh 不是嵌入在其他结构体中的,则可以用这些辅助函数:
8679544a2daSSeongJae Park
8689544a2daSSeongJae Parkint v4l2_fh_open(struct file *filp)
8699544a2daSSeongJae Park
8709544a2daSSeongJae Park  分配一个 v4l2_fh 结构体空间,初始化并将其添加到 file 结构体相关的
8719544a2daSSeongJae Park  video_device 结构体中。
8729544a2daSSeongJae Park
8739544a2daSSeongJae Parkint v4l2_fh_release(struct file *filp)
8749544a2daSSeongJae Park
8759544a2daSSeongJae Park  从 file 结构体相关的 video_device 结构体中删除 v4l2_fh ,清理
8769544a2daSSeongJae Park  v4l2_fh 并释放空间。
8779544a2daSSeongJae Park
8789544a2daSSeongJae Park这两个函数可以插入到 v4l2_file_operation 的 open() 和 release()
8799544a2daSSeongJae Park操作中。
8809544a2daSSeongJae Park
8819544a2daSSeongJae Park
8829544a2daSSeongJae Park某些驱动需要在第一个文件句柄打开和最后一个文件句柄关闭的时候做些
8839544a2daSSeongJae Park工作。所以加入了两个辅助函数以检查 v4l2_fh 结构体是否是相关设备
8849544a2daSSeongJae Park节点打开的唯一文件句柄。
8859544a2daSSeongJae Park
8869544a2daSSeongJae Parkint v4l2_fh_is_singular(struct v4l2_fh *fh)
8879544a2daSSeongJae Park
8889544a2daSSeongJae Park  如果此文件句柄是唯一打开的文件句柄,则返回 1 ,否则返回 0 。
8899544a2daSSeongJae Park
8909544a2daSSeongJae Parkint v4l2_fh_is_singular_file(struct file *filp)
8919544a2daSSeongJae Park
8929544a2daSSeongJae Park  功能相同,但通过 filp->private_data 调用 v4l2_fh_is_singular。
8939544a2daSSeongJae Park
8949544a2daSSeongJae Park
8959544a2daSSeongJae ParkV4L2 事件机制
8969544a2daSSeongJae Park-----------
8979544a2daSSeongJae Park
8989544a2daSSeongJae ParkV4L2 事件机制提供了一个通用的方法将事件传递到用户空间。驱动必须使用
8999544a2daSSeongJae Parkv4l2_fh 才能支持 V4L2 事件机制。
9009544a2daSSeongJae Park
9019544a2daSSeongJae Park
9029544a2daSSeongJae Park事件通过一个类型和选择 ID 来定义。ID 对应一个 V4L2 对象,例如
9039544a2daSSeongJae Park一个控制 ID。如果未使用,则 ID 为 0。
9049544a2daSSeongJae Park
9059544a2daSSeongJae Park当用户订阅一个事件,驱动会为此分配一些 kevent 结构体。所以每个
9069544a2daSSeongJae Park事件组(类型、ID)都会有自己的一套 kevent 结构体。这保证了如果
9079544a2daSSeongJae Park一个驱动短时间内产生了许多同类事件,不会覆盖其他类型的事件。
9089544a2daSSeongJae Park
9099544a2daSSeongJae Park但如果你收到的事件数量大于同类事件 kevent 的保存数量,则最早的
9109544a2daSSeongJae Park事件将被丢弃,并加入新事件。
9119544a2daSSeongJae Park
9129544a2daSSeongJae Park此外,v4l2_subscribed_event 结构体内部有可供驱动设置的 merge() 和
9139544a2daSSeongJae Parkreplace() 回调,这些回调会在新事件产生且没有多余空间的时候被调用。
9149544a2daSSeongJae Parkreplace() 回调让你可以将早期事件的净荷替换为新事件的净荷,将早期
9159544a2daSSeongJae Park净荷的相关数据合并到替换进来的新净荷中。当该类型的事件仅分配了一个
9169544a2daSSeongJae Parkkevent 结构体时,它将被调用。merge() 回调让你可以合并最早的事件净荷
9179544a2daSSeongJae Park到在它之后的那个事件净荷中。当该类型的事件分配了两个或更多 kevent
9189544a2daSSeongJae Park结构体时,它将被调用。
9199544a2daSSeongJae Park
9209544a2daSSeongJae Park这种方法不会有状态信息丢失,只会导致中间步骤信息丢失。
9219544a2daSSeongJae Park
9229544a2daSSeongJae Park
9239544a2daSSeongJae Park关于 replace/merge 回调的一个不错的例子在 v4l2-event.c 中:用于
9249544a2daSSeongJae Park控制事件的 ctrls_replace() 和 ctrls_merge() 回调。
9259544a2daSSeongJae Park
9269544a2daSSeongJae Park注意:这些回调可以在中断上下文中调用,所以它们必须尽快完成并退出。
9279544a2daSSeongJae Park
9289544a2daSSeongJae Park有用的函数:
9299544a2daSSeongJae Park
9309544a2daSSeongJae Parkvoid v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
9319544a2daSSeongJae Park
9329544a2daSSeongJae Park  将事件加入视频设备的队列。驱动仅负责填充 type 和 data 域。
9339544a2daSSeongJae Park  其他域由 V4L2 填充。
9349544a2daSSeongJae Park
9359544a2daSSeongJae Parkint v4l2_event_subscribe(struct v4l2_fh *fh,
9369544a2daSSeongJae Park			 struct v4l2_event_subscription *sub, unsigned elems,
9379544a2daSSeongJae Park			 const struct v4l2_subscribed_event_ops *ops)
9389544a2daSSeongJae Park
9399544a2daSSeongJae Park  video_device->ioctl_ops->vidioc_subscribe_event 必须检测驱动能
9409544a2daSSeongJae Park  产生特定 id 的事件。然后调用 v4l2_event_subscribe() 来订阅该事件。
9419544a2daSSeongJae Park
9429544a2daSSeongJae Park  elems 参数是该事件的队列大小。若为 0,V4L2 框架将会(根据事件类型)
9439544a2daSSeongJae Park  填充默认值。
9449544a2daSSeongJae Park
9459544a2daSSeongJae Park  ops 参数允许驱动指定一系列回调:
9469544a2daSSeongJae Park  * add:     当添加一个新监听者时调用(重复订阅同一个事件,此回调
9479544a2daSSeongJae Park             仅被执行一次)。
9489544a2daSSeongJae Park  * del:     当一个监听者停止监听时调用。
9499544a2daSSeongJae Park  * replace: 用‘新’事件替换‘早期‘事件。
9509544a2daSSeongJae Park  * merge:   将‘早期‘事件合并到‘新’事件中。
9519544a2daSSeongJae Park  这四个调用都是可选的,如果不想指定任何回调,则 ops 可为 NULL。
9529544a2daSSeongJae Park
9539544a2daSSeongJae Parkint v4l2_event_unsubscribe(struct v4l2_fh *fh,
9549544a2daSSeongJae Park			   struct v4l2_event_subscription *sub)
9559544a2daSSeongJae Park
9569544a2daSSeongJae Park  v4l2_ioctl_ops 结构体中的 vidioc_unsubscribe_event 回调函数。
9579544a2daSSeongJae Park  驱动程序可以直接使用 v4l2_event_unsubscribe() 实现退订事件过程。
9589544a2daSSeongJae Park
9599544a2daSSeongJae Park  特殊的 V4L2_EVENT_ALL 类型,可用于退订所有事件。驱动可能在特殊
9609544a2daSSeongJae Park  情况下需要做此操作。
9619544a2daSSeongJae Park
9629544a2daSSeongJae Parkint v4l2_event_pending(struct v4l2_fh *fh)
9639544a2daSSeongJae Park
9649544a2daSSeongJae Park  返回未决事件的数量。有助于实现轮询(poll)操作。
9659544a2daSSeongJae Park
9669544a2daSSeongJae Park事件通过 poll 系统调用传递到用户空间。驱动可用
9679544a2daSSeongJae Parkv4l2_fh->wait (wait_queue_head_t 类型)作为参数调用 poll_wait()。
9689544a2daSSeongJae Park
9699544a2daSSeongJae Park事件分为标准事件和私有事件。新的标准事件必须使用可用的最小事件类型
9709544a2daSSeongJae Park编号。驱动必须从他们本类型的编号起始处分配事件。类型的编号起始为
9719544a2daSSeongJae ParkV4L2_EVENT_PRIVATE_START + n * 1000 ,其中 n 为可用最小编号。每个
9729544a2daSSeongJae Park类型中的第一个事件类型编号是为以后的使用保留的,所以第一个可用事件
9739544a2daSSeongJae Park类型编号是‘class base + 1’。
9749544a2daSSeongJae Park
9759544a2daSSeongJae ParkV4L2 事件机制的使用实例可以在 OMAP3 ISP 的驱动
9769544a2daSSeongJae Park(drivers/media/video/omap3isp)中找到。
977