18c2ecf20Sopenharmony_ciChinese translated version of Documentation/driver-api/media/index.rst
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ciIf you have any comment or update to the content, please contact the
48c2ecf20Sopenharmony_cioriginal document maintainer directly.  However, if you have a problem
58c2ecf20Sopenharmony_cicommunicating in English you can also ask the Chinese maintainer for
68c2ecf20Sopenharmony_cihelp.  Contact the Chinese maintainer if this translation is outdated
78c2ecf20Sopenharmony_cior if there is a problem with the translation.
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ciMaintainer: Mauro Carvalho Chehab <mchehab@kernel.org>
108c2ecf20Sopenharmony_ciChinese maintainer: Fu Wei <tekkamanninja@gmail.com>
118c2ecf20Sopenharmony_ci---------------------------------------------------------------------
128c2ecf20Sopenharmony_ciDocumentation/driver-api/media/index.rst 的中文翻译
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
158c2ecf20Sopenharmony_ci交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
168c2ecf20Sopenharmony_ci译存在问题,请联系中文版维护者。
178c2ecf20Sopenharmony_ci英文版维护者: Mauro Carvalho Chehab <mchehab@kernel.org>
188c2ecf20Sopenharmony_ci中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
198c2ecf20Sopenharmony_ci中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
208c2ecf20Sopenharmony_ci中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci以下为正文
248c2ecf20Sopenharmony_ci---------------------------------------------------------------------
258c2ecf20Sopenharmony_ciV4L2 驱动框架概览
268c2ecf20Sopenharmony_ci==============
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci本文档描述 V4L2 框架所提供的各种结构和它们之间的关系。
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci介绍
328c2ecf20Sopenharmony_ci----
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci大部分现代 V4L2 设备由多个 IC 组成,在 /dev 下导出多个设备节点,
358c2ecf20Sopenharmony_ci并同时创建非 V4L2 设备(如 DVB、ALSA、FB、I2C 和红外输入设备)。
368c2ecf20Sopenharmony_ci由于这种硬件的复杂性,V4L2 驱动也变得非常复杂。
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci尤其是 V4L2 必须支持 IC 实现音视频的多路复用和编解码,这就更增加了其
398c2ecf20Sopenharmony_ci复杂性。通常这些 IC 通过一个或多个 I2C 总线连接到主桥驱动器,但也可
408c2ecf20Sopenharmony_ci使用其他总线。这些设备称为“子设备”。
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci长期以来,这个框架仅限于通过 video_device 结构体创建 V4L 设备节点,
438c2ecf20Sopenharmony_ci并使用 video_buf 处理视频缓冲(注:本文不讨论 video_buf 框架)。
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci这意味着所有驱动必须自己设置设备实例并连接到子设备。其中一部分要正确地
468c2ecf20Sopenharmony_ci完成是比较复杂的,使得许多驱动都没有正确地实现。
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci由于框架的缺失,有很多通用代码都不可重复利用。
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci因此,这个框架构建所有驱动都需要的基本结构块,而统一的框架将使通用代码
518c2ecf20Sopenharmony_ci创建成实用函数并在所有驱动中共享变得更加容易。
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci驱动结构
558c2ecf20Sopenharmony_ci-------
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci所有 V4L2 驱动都有如下结构:
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci1) 每个设备实例的结构体--包含其设备状态。
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci2) 初始化和控制子设备的方法(如果有)。
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci3) 创建 V4L2 设备节点 (/dev/videoX/dev/vbiX/dev/radioX)
648c2ecf20Sopenharmony_ci   并跟踪设备节点的特定数据。
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci4) 特定文件句柄结构体--包含每个文件句柄的数据。
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci5) 视频缓冲处理。
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci以下是它们的初略关系图:
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci    device instances(设备实例)
738c2ecf20Sopenharmony_ci      |
748c2ecf20Sopenharmony_ci      +-sub-device instances(子设备实例)
758c2ecf20Sopenharmony_ci      |
768c2ecf20Sopenharmony_ci      \-V4L2 device nodes(V4L2 设备节点)
778c2ecf20Sopenharmony_ci	  |
788c2ecf20Sopenharmony_ci	  \-filehandle instances(文件句柄实例)
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci框架结构
828c2ecf20Sopenharmony_ci-------
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci该框架非常类似驱动结构:它有一个 v4l2_device 结构用于保存设备
858c2ecf20Sopenharmony_ci实例的数据;一个 v4l2_subdev 结构体代表子设备实例;video_device
868c2ecf20Sopenharmony_ci结构体保存 V4L2 设备节点的数据;将来 v4l2_fh 结构体将跟踪文件句柄
878c2ecf20Sopenharmony_ci实例(暂未尚未实现)。
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ciV4L2 框架也可与媒体框架整合(可选的)。如果驱动设置了 v4l2_device
908c2ecf20Sopenharmony_ci结构体的 mdev 域,子设备和视频节点的入口将自动出现在媒体框架中。
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_civ4l2_device 结构体
948c2ecf20Sopenharmony_ci----------------
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci每个设备实例都通过 v4l2_device (v4l2-device.h)结构体来表示。
978c2ecf20Sopenharmony_ci简单设备可以仅分配这个结构体,但在大多数情况下,都会将这个结构体
988c2ecf20Sopenharmony_ci嵌入到一个更大的结构体中。
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci你必须注册这个设备实例:
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci注册操作将会初始化 v4l2_device 结构体。如果 dev->driver_data 域
1058c2ecf20Sopenharmony_ci为 NULL,就将其指向 v4l2_dev。
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci需要与媒体框架整合的驱动必须手动设置 dev->driver_data,指向包含
1088c2ecf20Sopenharmony_civ4l2_device 结构体实例的驱动特定设备结构体。这可以在注册 V4L2 设备
1098c2ecf20Sopenharmony_ci实例前通过 dev_set_drvdata() 函数完成。同时必须设置 v4l2_device
1108c2ecf20Sopenharmony_ci结构体的 mdev 域,指向适当的初始化并注册过的 media_device 实例。
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci如果 v4l2_dev->name 为空,则它将被设置为从 dev 中衍生出的值(为了
1138c2ecf20Sopenharmony_ci更加精确,形式为驱动名后跟 bus_id)。如果你在调用 v4l2_device_register
1148c2ecf20Sopenharmony_ci前已经设置好了,则不会被修改。如果 dev 为 NULL,则你*必须*在调用
1158c2ecf20Sopenharmony_civ4l2_device_register 前设置 v4l2_dev->name。
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci你可以基于驱动名和驱动的全局 atomic_t 类型的实例编号,通过
1188c2ecf20Sopenharmony_civ4l2_device_set_name() 设置 name。这样会生成类似 ivtv0、ivtv1 等
1198c2ecf20Sopenharmony_ci名字。若驱动名以数字结尾,则会在编号和驱动名间插入一个破折号,如:
1208c2ecf20Sopenharmony_cicx18-0、cx18-1 等。此函数返回实例编号。
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci第一个 “dev” 参数通常是一个指向 pci_dev、usb_interface 或
1238c2ecf20Sopenharmony_ciplatform_device 的指针。很少使其为 NULL,除非是一个ISA设备或者
1248c2ecf20Sopenharmony_ci当一个设备创建了多个 PCI 设备,使得 v4l2_dev 无法与一个特定的父设备
1258c2ecf20Sopenharmony_ci关联。
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci你也可以提供一个 notify() 回调,使子设备可以调用它实现事件通知。
1288c2ecf20Sopenharmony_ci但这个设置与子设备相关。子设备支持的任何通知必须在
1298c2ecf20Sopenharmony_ciinclude/media/<subdevice>.h 中定义一个消息头。
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci注销 v4l2_device 使用如下函数:
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	v4l2_device_unregister(struct v4l2_device *v4l2_dev);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci如果 dev->driver_data 域指向 v4l2_dev,将会被重置为 NULL。注销同时
1368c2ecf20Sopenharmony_ci会自动从设备中注销所有子设备。
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci如果你有一个热插拔设备(如USB设备),则当断开发生时,父设备将无效。
1398c2ecf20Sopenharmony_ci由于 v4l2_device 有一个指向父设备的指针必须被清除,同时标志父设备
1408c2ecf20Sopenharmony_ci已消失,所以必须调用以下函数:
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	v4l2_device_disconnect(struct v4l2_device *v4l2_dev);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci这个函数并*不*注销子设备,因此你依然要调用 v4l2_device_unregister()
1458c2ecf20Sopenharmony_ci函数。如果你的驱动器并非热插拔的,就没有必要调用 v4l2_device_disconnect()。
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci有时你需要遍历所有被特定驱动注册的设备。这通常发生在多个设备驱动使用
1488c2ecf20Sopenharmony_ci同一个硬件的情况下。如:ivtvfb 驱动是一个使用 ivtv 硬件的帧缓冲驱动,
1498c2ecf20Sopenharmony_ci同时 alsa 驱动也使用此硬件。
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci你可以使用如下例程遍历所有注册的设备:
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic int callback(struct device *dev, void *p)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* 测试这个设备是否已经初始化 */
1588c2ecf20Sopenharmony_ci	if (v4l2_dev == NULL)
1598c2ecf20Sopenharmony_ci		return 0;
1608c2ecf20Sopenharmony_ci	...
1618c2ecf20Sopenharmony_ci	return 0;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ciint iterate(void *p)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct device_driver *drv;
1678c2ecf20Sopenharmony_ci	int err;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* 在PCI 总线上查找ivtv驱动。
1708c2ecf20Sopenharmony_ci	   pci_bus_type是全局的. 对于USB总线使用usb_bus_type。 */
1718c2ecf20Sopenharmony_ci	drv = driver_find("ivtv", &pci_bus_type);
1728c2ecf20Sopenharmony_ci	/* 遍历所有的ivtv设备实例 */
1738c2ecf20Sopenharmony_ci	err = driver_for_each_device(drv, NULL, p, callback);
1748c2ecf20Sopenharmony_ci	put_driver(drv);
1758c2ecf20Sopenharmony_ci	return err;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci有时你需要一个设备实例的运行计数。这个通常用于映射一个设备实例到一个
1798c2ecf20Sopenharmony_ci模块选择数组的索引。
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci推荐方法如下:
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic atomic_t drv_instance = ATOMIC_INIT(0);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int drv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	...
1888c2ecf20Sopenharmony_ci	state->instance = atomic_inc_return(&drv_instance) - 1;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci如果你有多个设备节点,对于热插拔设备,知道何时注销 v4l2_device 结构体
1928c2ecf20Sopenharmony_ci就比较困难。为此 v4l2_device 有引用计数支持。当调用 video_register_device
1938c2ecf20Sopenharmony_ci时增加引用计数,而设备节点释放时减小引用计数。当引用计数为零,则
1948c2ecf20Sopenharmony_civ4l2_device 的release() 回调将被执行。你就可以在此时做最后的清理工作。
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci如果创建了其他设备节点(比如 ALSA),则你可以通过以下函数手动增减
1978c2ecf20Sopenharmony_ci引用计数:
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_civoid v4l2_device_get(struct v4l2_device *v4l2_dev);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci或:
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciint v4l2_device_put(struct v4l2_device *v4l2_dev);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci由于引用技术初始化为 1 ,你也需要在 disconnect() 回调(对于 USB 设备)中
2068c2ecf20Sopenharmony_ci调用 v4l2_device_put,或者 remove() 回调(例如对于 PCI 设备),否则
2078c2ecf20Sopenharmony_ci引用计数将永远不会为 0 。
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_civ4l2_subdev结构体
2108c2ecf20Sopenharmony_ci------------------
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci许多驱动需要与子设备通信。这些设备可以完成各种任务,但通常他们负责
2138c2ecf20Sopenharmony_ci音视频复用和编解码。如网络摄像头的子设备通常是传感器和摄像头控制器。
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci这些一般为 I2C 接口设备,但并不一定都是。为了给驱动提供调用子设备的
2168c2ecf20Sopenharmony_ci统一接口,v4l2_subdev 结构体(v4l2-subdev.h)产生了。
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci每个子设备驱动都必须有一个 v4l2_subdev 结构体。这个结构体可以单独
2198c2ecf20Sopenharmony_ci代表一个简单的子设备,也可以嵌入到一个更大的结构体中,与更多设备状态
2208c2ecf20Sopenharmony_ci信息保存在一起。通常有一个下级设备结构体(比如:i2c_client)包含了
2218c2ecf20Sopenharmony_ci内核创建的设备数据。建议使用 v4l2_set_subdevdata() 将这个结构体的
2228c2ecf20Sopenharmony_ci指针保存在 v4l2_subdev 的私有数据域(dev_priv)中。这使得通过 v4l2_subdev
2238c2ecf20Sopenharmony_ci找到实际的低层总线特定设备数据变得容易。
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci你同时需要一个从低层结构体获取 v4l2_subdev 指针的方法。对于常用的
2268c2ecf20Sopenharmony_cii2c_client 结构体,i2c_set_clientdata() 函数可用于保存一个 v4l2_subdev
2278c2ecf20Sopenharmony_ci指针;对于其他总线你可能需要使用其他相关函数。
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci桥驱动中也应保存每个子设备的私有数据,比如一个指向特定桥的各设备私有
2308c2ecf20Sopenharmony_ci数据的指针。为此 v4l2_subdev 结构体提供主机私有数据域(host_priv),
2318c2ecf20Sopenharmony_ci并可通过 v4l2_get_subdev_hostdata() 和 v4l2_set_subdev_hostdata()
2328c2ecf20Sopenharmony_ci访问。
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci从总线桥驱动的视角,驱动加载子设备模块并以某种方式获得 v4l2_subdev
2358c2ecf20Sopenharmony_ci结构体指针。对于 i2c 总线设备相对简单:调用 i2c_get_clientdata()。
2368c2ecf20Sopenharmony_ci对于其他总线也需要做类似的操作。针对 I2C 总线上的子设备辅助函数帮你
2378c2ecf20Sopenharmony_ci完成了大部分复杂的工作。
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci每个 v4l2_subdev 都包含子设备驱动需要实现的函数指针(如果对此设备
2408c2ecf20Sopenharmony_ci不适用,可为NULL)。由于子设备可完成许多不同的工作,而在一个庞大的
2418c2ecf20Sopenharmony_ci函数指针结构体中通常仅有少数有用的函数实现其功能肯定不合适。所以,
2428c2ecf20Sopenharmony_ci函数指针根据其实现的功能被分类,每一类都有自己的函数指针结构体。
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci顶层函数指针结构体包含了指向各类函数指针结构体的指针,如果子设备驱动
2458c2ecf20Sopenharmony_ci不支持该类函数中的任何一个功能,则指向该类结构体的指针为NULL。
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci这些结构体定义如下:
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistruct v4l2_subdev_core_ops {
2508c2ecf20Sopenharmony_ci	int (*log_status)(struct v4l2_subdev *sd);
2518c2ecf20Sopenharmony_ci	int (*init)(struct v4l2_subdev *sd, u32 val);
2528c2ecf20Sopenharmony_ci	...
2538c2ecf20Sopenharmony_ci};
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistruct v4l2_subdev_tuner_ops {
2568c2ecf20Sopenharmony_ci	...
2578c2ecf20Sopenharmony_ci};
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistruct v4l2_subdev_audio_ops {
2608c2ecf20Sopenharmony_ci	...
2618c2ecf20Sopenharmony_ci};
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistruct v4l2_subdev_video_ops {
2648c2ecf20Sopenharmony_ci	...
2658c2ecf20Sopenharmony_ci};
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistruct v4l2_subdev_pad_ops {
2688c2ecf20Sopenharmony_ci	...
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistruct v4l2_subdev_ops {
2728c2ecf20Sopenharmony_ci	const struct v4l2_subdev_core_ops  *core;
2738c2ecf20Sopenharmony_ci	const struct v4l2_subdev_tuner_ops *tuner;
2748c2ecf20Sopenharmony_ci	const struct v4l2_subdev_audio_ops *audio;
2758c2ecf20Sopenharmony_ci	const struct v4l2_subdev_video_ops *video;
2768c2ecf20Sopenharmony_ci	const struct v4l2_subdev_pad_ops *video;
2778c2ecf20Sopenharmony_ci};
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci其中 core(核心)函数集通常可用于所有子设备,其他类别的实现依赖于
2808c2ecf20Sopenharmony_ci子设备。如视频设备可能不支持音频操作函数,反之亦然。
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci这样的设置在限制了函数指针数量的同时,还使增加新的操作函数和分类
2838c2ecf20Sopenharmony_ci变得较为容易。
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci子设备驱动可使用如下函数初始化 v4l2_subdev 结构体:
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	v4l2_subdev_init(sd, &ops);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci然后,你必须用一个唯一的名字初始化 subdev->name,并初始化模块的
2908c2ecf20Sopenharmony_ciowner 域。若使用 i2c 辅助函数,这些都会帮你处理好。
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci若需同媒体框架整合,你必须调用 media_entity_pads_init() 初始化 v4l2_subdev
2938c2ecf20Sopenharmony_ci结构体中的 media_entity 结构体(entity 域):
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	struct media_pad *pads = &my_sd->pads;
2968c2ecf20Sopenharmony_ci	int err;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	err = media_entity_pads_init(&sd->entity, npads, pads);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cipads 数组必须预先初始化。无须手动设置 media_entity 的 type 和
3018c2ecf20Sopenharmony_ciname 域,但如有必要,revision 域必须初始化。
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci当(任何)子设备节点被打开/关闭,对 entity 的引用将被自动获取/释放。
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci在子设备被注销之后,不要忘记清理 media_entity 结构体:
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	media_entity_cleanup(&sd->entity);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci如果子设备驱动趋向于处理视频并整合进了媒体框架,必须使用 v4l2_subdev_pad_ops
3108c2ecf20Sopenharmony_ci替代 v4l2_subdev_video_ops 实现格式相关的功能。
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci这种情况下,子设备驱动应该设置 link_validate 域,以提供它自身的链接
3138c2ecf20Sopenharmony_ci验证函数。链接验证函数应对管道(两端链接的都是 V4L2 子设备)中的每个
3148c2ecf20Sopenharmony_ci链接调用。驱动还要负责验证子设备和视频节点间格式配置的正确性。
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci如果 link_validate 操作没有设置,默认的 v4l2_subdev_link_validate_default()
3178c2ecf20Sopenharmony_ci函数将会被调用。这个函数保证宽、高和媒体总线像素格式在链接的收发两端
3188c2ecf20Sopenharmony_ci都一致。子设备驱动除了它们自己的检测外,也可以自由使用这个函数以执行
3198c2ecf20Sopenharmony_ci上面提到的检查。
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci设备(桥)驱动程序必须向 v4l2_device 注册 v4l2_subdev:
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	int err = v4l2_device_register_subdev(v4l2_dev, sd);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci如果子设备模块在它注册前消失,这个操作可能失败。在这个函数成功返回后,
3268c2ecf20Sopenharmony_cisubdev->dev 域就指向了 v4l2_device。
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci如果 v4l2_device 父设备的 mdev 域为非 NULL 值,则子设备实体将被自动
3298c2ecf20Sopenharmony_ci注册为媒体设备。
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci注销子设备则可用如下函数:
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(sd);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci此后,子设备模块就可卸载,且 sd->dev == NULL。
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci注册之设备后,可通过以下方式直接调用其操作函数:
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	err = sd->ops->core->g_std(sd, &norm);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci但使用如下宏会比较容易且合适:
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	err = v4l2_subdev_call(sd, core, g_std, &norm);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci这个宏将会做 NULL 指针检查,如果 subdev 为 NULL,则返回-ENODEV;如果
3468c2ecf20Sopenharmony_cisubdev->core 或 subdev->core->g_std 为 NULL,则返回 -ENOIOCTLCMD;
3478c2ecf20Sopenharmony_ci否则将返回 subdev->ops->core->g_std ops 调用的实际结果。
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci有时也可能同时调用所有或一系列子设备的某个操作函数:
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	v4l2_device_call_all(v4l2_dev, 0, core, g_std, &norm);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci任何不支持此操作的子设备都会被跳过,并忽略错误返回值。但如果你需要
3548c2ecf20Sopenharmony_ci检查出错码,则可使用如下函数:
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_std, &norm);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci除 -ENOIOCTLCMD 外的任何错误都会跳出循环并返回错误值。如果(除 -ENOIOCTLCMD
3598c2ecf20Sopenharmony_ci外)没有错误发生,则返回 0。
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci对于以上两个函数的第二个参数为组 ID。如果为 0,则所有子设备都会执行
3628c2ecf20Sopenharmony_ci这个操作。如果为非 0 值,则只有那些组 ID 匹配的子设备才会执行此操作。
3638c2ecf20Sopenharmony_ci在桥驱动注册一个子设备前,可以设置 sd->grp_id 为任何期望值(默认值为
3648c2ecf20Sopenharmony_ci0)。这个值属于桥驱动,且子设备驱动将不会修改和使用它。
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci组 ID 赋予了桥驱动更多对于如何调用回调的控制。例如,电路板上有多个
3678c2ecf20Sopenharmony_ci音频芯片,每个都有改变音量的能力。但当用户想要改变音量的时候,通常
3688c2ecf20Sopenharmony_ci只有一个会被实际使用。你可以对这样的子设备设置组 ID 为(例如 AUDIO_CONTROLLER)
3698c2ecf20Sopenharmony_ci并在调用 v4l2_device_call_all() 时指定它为组 ID 值。这就保证了只有
3708c2ecf20Sopenharmony_ci需要的子设备才会执行这个回调。
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci如果子设备需要通知它的 v4l2_device 父设备一个事件,可以调用
3738c2ecf20Sopenharmony_civ4l2_subdev_notify(sd, notification, arg)。这个宏检查是否有一个
3748c2ecf20Sopenharmony_cinotify() 回调被注册,如果没有,返回 -ENODEV。否则返回 notify() 调用
3758c2ecf20Sopenharmony_ci结果。
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci使用 v4l2_subdev 的好处在于它是一个通用结构体,且不包含任何底层硬件
3788c2ecf20Sopenharmony_ci信息。所有驱动可以包含多个 I2C 总线的子设备,但也有子设备是通过 GPIO
3798c2ecf20Sopenharmony_ci控制。这个区别仅在配置设备时有关系,一旦子设备注册完成,对于 v4l2
3808c2ecf20Sopenharmony_ci子系统来说就完全透明了。
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ciV4L2 子设备用户空间API
3848c2ecf20Sopenharmony_ci--------------------
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci除了通过 v4l2_subdev_ops 结构导出的内核 API,V4L2 子设备也可以直接
3878c2ecf20Sopenharmony_ci通过用户空间应用程序来控制。
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci可以在 /dev 中创建名为 v4l-subdevX 设备节点,以通过其直接访问子设备。
3908c2ecf20Sopenharmony_ci如果子设备支持用户空间直接配置,必须在注册前设置 V4L2_SUBDEV_FL_HAS_DEVNODE
3918c2ecf20Sopenharmony_ci标志。
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci注册子设备之后, v4l2_device 驱动会通过调用 v4l2_device_register_subdev_nodes()
3948c2ecf20Sopenharmony_ci函数为所有已注册并设置了 V4L2_SUBDEV_FL_HAS_DEVNODE 的子设备创建
3958c2ecf20Sopenharmony_ci设备节点。这些设备节点会在子设备注销时自动删除。
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci这些设备节点处理 V4L2 API 的一个子集。
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ciVIDIOC_QUERYCTRL
4008c2ecf20Sopenharmony_ciVIDIOC_QUERYMENU
4018c2ecf20Sopenharmony_ciVIDIOC_G_CTRL
4028c2ecf20Sopenharmony_ciVIDIOC_S_CTRL
4038c2ecf20Sopenharmony_ciVIDIOC_G_EXT_CTRLS
4048c2ecf20Sopenharmony_ciVIDIOC_S_EXT_CTRLS
4058c2ecf20Sopenharmony_ciVIDIOC_TRY_EXT_CTRLS
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	这些 ioctls 控制与 V4L2 中定义的一致。他们行为相同,唯一的
4088c2ecf20Sopenharmony_ci	不同是他们只处理子设备的控制实现。根据驱动程序,这些控制也
4098c2ecf20Sopenharmony_ci	可以通过一个(或多个) V4L2 设备节点访问。
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ciVIDIOC_DQEVENT
4128c2ecf20Sopenharmony_ciVIDIOC_SUBSCRIBE_EVENT
4138c2ecf20Sopenharmony_ciVIDIOC_UNSUBSCRIBE_EVENT
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	这些  ioctls 事件与 V4L2 中定义的一致。他们行为相同,唯一的
4168c2ecf20Sopenharmony_ci	不同是他们只处理子设备产生的事件。根据驱动程序,这些事件也
4178c2ecf20Sopenharmony_ci	可以通过一个(或多个) V4L2 设备节点上报。
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	要使用事件通知的子设备驱动,在注册子设备前必须在 v4l2_subdev::flags
4208c2ecf20Sopenharmony_ci	中设置 V4L2_SUBDEV_USES_EVENTS 并在 v4l2_subdev::nevents
4218c2ecf20Sopenharmony_ci	中初始化事件队列深度。注册完成后,事件会在 v4l2_subdev::devnode
4228c2ecf20Sopenharmony_ci	设备节点中像通常一样被排队。
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	为正确支持事件机制,poll() 文件操作也应被实现。
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci私有 ioctls
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	不在以上列表中的所有 ioctls 会通过 core::ioctl 操作直接传递
4298c2ecf20Sopenharmony_ci	给子设备驱动。
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ciI2C 子设备驱动
4338c2ecf20Sopenharmony_ci-------------
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci由于这些驱动很常见,所以内特提供了特定的辅助函数(v4l2-common.h)让这些
4368c2ecf20Sopenharmony_ci设备的使用更加容易。
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci添加 v4l2_subdev 支持的推荐方法是让 I2C 驱动将 v4l2_subdev 结构体
4398c2ecf20Sopenharmony_ci嵌入到为每个 I2C 设备实例创建的状态结构体中。而最简单的设备没有状态
4408c2ecf20Sopenharmony_ci结构体,此时可以直接创建一个 v4l2_subdev 结构体。
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci一个典型的状态结构体如下所示(‘chipname’用芯片名代替):
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_cistruct chipname_state {
4458c2ecf20Sopenharmony_ci	struct v4l2_subdev sd;
4468c2ecf20Sopenharmony_ci	...  /* 附加的状态域*/
4478c2ecf20Sopenharmony_ci};
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci初始化 v4l2_subdev 结构体的方法如下:
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	v4l2_i2c_subdev_init(&state->sd, client, subdev_ops);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci这个函数将填充 v4l2_subdev 结构体中的所有域,并保证 v4l2_subdev 和
4548c2ecf20Sopenharmony_cii2c_client 都指向彼此。
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci同时,你也应该为从 v4l2_subdev 指针找到 chipname_state 结构体指针
4578c2ecf20Sopenharmony_ci添加一个辅助内联函数。
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic inline struct chipname_state *to_state(struct v4l2_subdev *sd)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	return container_of(sd, struct chipname_state, sd);
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci使用以下函数可以通过 v4l2_subdev 结构体指针获得 i2c_client 结构体
4658c2ecf20Sopenharmony_ci指针:
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci而以下函数则相反,通过 i2c_client 结构体指针获得 v4l2_subdev 结构体
4708c2ecf20Sopenharmony_ci指针:
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = i2c_get_clientdata(client);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci当 remove()函数被调用前,必须保证先调用 v4l2_device_unregister_subdev(sd)。
4758c2ecf20Sopenharmony_ci此操作将会从桥驱动中注销子设备。即使子设备没有注册,调用此函数也是
4768c2ecf20Sopenharmony_ci安全的。
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci必须这样做的原因是:当桥驱动注销 i2c 适配器时,remove()回调函数
4798c2ecf20Sopenharmony_ci会被那个适配器上的 i2c 设备调用。此后,相应的 v4l2_subdev 结构体
4808c2ecf20Sopenharmony_ci就不存在了,所有它们必须先被注销。在 remove()回调函数中调用
4818c2ecf20Sopenharmony_civ4l2_device_unregister_subdev(sd),可以保证执行总是正确的。
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci桥驱动也有一些辅组函数可用:
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistruct v4l2_subdev *sd = v4l2_i2c_new_subdev(v4l2_dev, adapter,
4878c2ecf20Sopenharmony_ci	       "module_foo", "chipid", 0x36, NULL);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci这个函数会加载给定的模块(如果没有模块需要加载,可以为 NULL),
4908c2ecf20Sopenharmony_ci并用给定的 i2c 适配器结构体指针(i2c_adapter)和 器件地址(chip/address4918c2ecf20Sopenharmony_ci作为参数调用 i2c_new_client_device()。如果一切顺利,则就在 v4l2_device
4928c2ecf20Sopenharmony_ci中注册了子设备。
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci你也可以利用 v4l2_i2c_new_subdev()的最后一个参数,传递一个可能的
4958c2ecf20Sopenharmony_ciI2C 地址数组,让函数自动探测。这些探测地址只有在前一个参数为 0 的
4968c2ecf20Sopenharmony_ci情况下使用。非零参数意味着你知道准确的 i2c 地址,所以此时无须进行
4978c2ecf20Sopenharmony_ci探测。
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci如果出错,两个函数都返回 NULL。
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci注意:传递给 v4l2_i2c_new_subdev()的 chipid 通常与模块名一致。
5028c2ecf20Sopenharmony_ci它允许你指定一个芯片的变体,比如“saa7114”或“saa7115”。一般通过
5038c2ecf20Sopenharmony_cii2c 驱动自动探测。chipid 的使用是在今后需要深入了解的事情。这个与
5048c2ecf20Sopenharmony_cii2c 驱动不同,较容易混淆。要知道支持哪些芯片变体,你可以查阅 i2c
5058c2ecf20Sopenharmony_ci驱动代码的 i2c_device_id 表,上面列出了所有可能支持的芯片。
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci还有两个辅助函数:
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_civ4l2_i2c_new_subdev_cfg:这个函数添加新的 irq 和 platform_data
5108c2ecf20Sopenharmony_ci参数,并有‘addr’和‘probed_addrs’参数:如果 addr 非零,则被使用
5118c2ecf20Sopenharmony_ci(不探测变体),否则 probed_addrs 中的地址将用于自动探测。
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci例如:以下代码将会探测地址(0x10):
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cistruct v4l2_subdev *sd = v4l2_i2c_new_subdev_cfg(v4l2_dev, adapter,
5168c2ecf20Sopenharmony_ci	       "module_foo", "chipid", 0, NULL, 0, I2C_ADDRS(0x10));
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_civ4l2_i2c_new_subdev_board 使用一个 i2c_board_info 结构体,将其
5198c2ecf20Sopenharmony_ci替代 irq、platform_data 和 add r参数传递给 i2c 驱动。
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci如果子设备支持 s_config 核心操作,这个操作会在子设备配置好之后以 irq 和
5228c2ecf20Sopenharmony_ciplatform_data 为参数调用。早期的 v4l2_i2c_new_(probed_)subdev 函数
5238c2ecf20Sopenharmony_ci同样也会调用 s_config,但仅在 irq 为 0 且 platform_data 为 NULL 时。
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_civideo_device结构体
5268c2ecf20Sopenharmony_ci-----------------
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci在 /dev 目录下的实际设备节点根据 video_device 结构体(v4l2-dev.h)
5298c2ecf20Sopenharmony_ci创建。此结构体既可以动态分配也可以嵌入到一个更大的结构体中。
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci动态分配方法如下:
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	struct video_device *vdev = video_device_alloc();
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	if (vdev == NULL)
5368c2ecf20Sopenharmony_ci		return -ENOMEM;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	vdev->release = video_device_release;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci如果将其嵌入到一个大结构体中,则必须自己实现 release()回调。
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	struct video_device *vdev = &my_vdev->vdev;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	vdev->release = my_vdev_release;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cirelease()回调必须被设置,且在最后一个 video_device 用户退出之后
5478c2ecf20Sopenharmony_ci被调用。
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci默认的 video_device_release()回调只是调用 kfree 来释放之前分配的
5508c2ecf20Sopenharmony_ci内存。
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci你应该设置这些域:
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci- v4l2_dev: 设置为 v4l2_device 父设备。
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci- name: 设置为唯一的描述性设备名。
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci- fops: 设置为已有的 v4l2_file_operations 结构体。
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci- ioctl_ops: 如果你使用v4l2_ioctl_ops 来简化 ioctl 的维护
5618c2ecf20Sopenharmony_ci  (强烈建议使用,且将来可能变为强制性的!),然后设置你自己的
5628c2ecf20Sopenharmony_ci  v4l2_ioctl_ops 结构体.
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci- lock: 如果你要在驱动中实现所有的锁操作,则设为 NULL 。否则
5658c2ecf20Sopenharmony_ci  就要设置一个指向 struct mutex_lock 结构体的指针,这个锁将
5668c2ecf20Sopenharmony_ci  在 unlocked_ioctl 文件操作被调用前由内核获得,并在调用返回后
5678c2ecf20Sopenharmony_ci  释放。详见下一节。
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci- prio: 保持对优先级的跟踪。用于实现 VIDIOC_G/S_PRIORITY。如果
5708c2ecf20Sopenharmony_ci  设置为 NULL,则会使用 v4l2_device 中的 v4l2_prio_state 结构体。
5718c2ecf20Sopenharmony_ci  如果要对每个设备节点(组)实现独立的优先级,可以将其指向自己
5728c2ecf20Sopenharmony_ci  实现的 v4l2_prio_state 结构体。
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci- parent: 仅在使用 NULL 作为父设备结构体参数注册 v4l2_device 时
5758c2ecf20Sopenharmony_ci  设置此参数。只有在一个硬件设备包含多一个 PCI 设备,共享同一个
5768c2ecf20Sopenharmony_ci  v4l2_device 核心时才会发生。
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci  cx88 驱动就是一个例子:一个 v4l2_device 结构体核心,被一个裸的
5798c2ecf20Sopenharmony_ci  视频 PCI 设备(cx8800)和一个 MPEG PCI 设备(cx8802)共用。由于
5808c2ecf20Sopenharmony_ci  v4l2_device 无法与特定的 PCI 设备关联,所有没有设置父设备。但当
5818c2ecf20Sopenharmony_ci  video_device 配置后,就知道使用哪个父 PCI 设备了。
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci如果你使用 v4l2_ioctl_ops,则应该在 v4l2_file_operations 结构体中
5848c2ecf20Sopenharmony_ci设置 .unlocked_ioctl 指向 video_ioctl2。
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci请勿使用 .ioctl!它已被废弃,今后将消失。
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci某些情况下你要告诉核心:你在 v4l2_ioctl_ops 指定的某个函数应被忽略。
5898c2ecf20Sopenharmony_ci你可以在 video_device_register 被调用前通过以下函数标记这个 ioctls。
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_civoid v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci基于外部因素(例如某个板卡已被使用),在不创建新结构体的情况下,你想
5948c2ecf20Sopenharmony_ci要关闭 v4l2_ioctl_ops 中某个特性往往需要这个机制。
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_civ4l2_file_operations 结构体是 file_operations 的一个子集。其主要
5978c2ecf20Sopenharmony_ci区别在于:因 inode 参数从未被使用,它将被忽略。
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci如果需要与媒体框架整合,你必须通过调用 media_entity_pads_init() 初始化
6008c2ecf20Sopenharmony_ci嵌入在 video_device 结构体中的 media_entity(entity 域)结构体:
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	struct media_pad *pad = &my_vdev->pad;
6038c2ecf20Sopenharmony_ci	int err;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	err = media_entity_pads_init(&vdev->entity, 1, pad);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cipads 数组必须预先初始化。没有必要手动设置 media_entity 的 type 和
6088c2ecf20Sopenharmony_ciname 域。
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci当(任何)子设备节点被打开/关闭,对 entity 的引用将被自动获取/释放。
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_civ4l2_file_operations 与锁
6138c2ecf20Sopenharmony_ci--------------------------
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci你可以在 video_device 结构体中设置一个指向 mutex_lock 的指针。通常
6168c2ecf20Sopenharmony_ci这既可是一个顶层互斥锁也可为设备节点自身的互斥锁。默认情况下,此锁
6178c2ecf20Sopenharmony_ci用于 unlocked_ioctl,但为了使用 ioctls 你通过以下函数可禁用锁定:
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	void v4l2_disable_ioctl_locking(struct video_device *vdev, unsigned int cmd);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci例如: v4l2_disable_ioctl_locking(vdev, VIDIOC_DQBUF);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci你必须在注册 video_device 前调用这个函数。
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci特别是对于 USB 驱动程序,某些命令(如设置控制)需要很长的时间,可能
6268c2ecf20Sopenharmony_ci需要自行为缓冲区队列的 ioctls 实现锁定。
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci如果你需要更细粒度的锁,你必须设置 mutex_lock 为 NULL,并完全自己实现
6298c2ecf20Sopenharmony_ci锁机制。
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci这完全由驱动开发者决定使用何种方法。然而,如果你的驱动存在长延时操作
6328c2ecf20Sopenharmony_ci(例如,改变 USB 摄像头的曝光时间可能需要较长时间),而你又想让用户
6338c2ecf20Sopenharmony_ci在等待长延时操作完成期间做其他的事,则你最好自己实现锁机制。
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci如果指定一个锁,则所有 ioctl 操作将在这个锁的作用下串行执行。如果你
6368c2ecf20Sopenharmony_ci使用 videobuf,则必须将同一个锁传递给 videobuf 队列初始化函数;如
6378c2ecf20Sopenharmony_civideobuf 必须等待一帧的到达,则可临时解锁并在这之后重新上锁。如果驱动
6388c2ecf20Sopenharmony_ci也在代码执行期间等待,则可做同样的工作(临时解锁,再上锁)让其他进程
6398c2ecf20Sopenharmony_ci可以在第一个进程阻塞时访问设备节点。
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci在使用 videobuf2 的情况下,必须实现 wait_prepare 和 wait_finish 回调
6428c2ecf20Sopenharmony_ci在适当的时候解锁/加锁。进一步来说,如果你在 video_device 结构体中使用
6438c2ecf20Sopenharmony_ci锁,则必须在 wait_prepare 和 wait_finish 中对这个互斥锁进行解锁/加锁。
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci热插拔的断开实现也必须在调用 v4l2_device_disconnect 前获得锁。
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_civideo_device注册
6488c2ecf20Sopenharmony_ci---------------
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci接下来你需要注册视频设备:这会为你创建一个字符设备。
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	err = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
6538c2ecf20Sopenharmony_ci	if (err) {
6548c2ecf20Sopenharmony_ci		video_device_release(vdev); /* or kfree(my_vdev); */
6558c2ecf20Sopenharmony_ci		return err;
6568c2ecf20Sopenharmony_ci	}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci如果 v4l2_device 父设备的 mdev 域为非 NULL 值,视频设备实体将自动
6598c2ecf20Sopenharmony_ci注册为媒体设备。
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci注册哪种设备是根据类型(type)参数。存在以下类型:
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ciVFL_TYPE_VIDEO: 用于视频输入/输出设备的 videoX
6648c2ecf20Sopenharmony_ciVFL_TYPE_VBI: 用于垂直消隐数据的 vbiX (例如,隐藏式字幕,图文电视)
6658c2ecf20Sopenharmony_ciVFL_TYPE_RADIO: 用于广播调谐器的 radioX
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci最后一个参数让你确定一个所控制设备的设备节点号数量(例如 videoX 中的 X)。
6688c2ecf20Sopenharmony_ci通常你可以传入-1,让 v4l2 框架自己选择第一个空闲的编号。但是有时用户
6698c2ecf20Sopenharmony_ci需要选择一个特定的节点号。驱动允许用户通过驱动模块参数选择一个特定的
6708c2ecf20Sopenharmony_ci设备节点号是很普遍的。这个编号将会传递给这个函数,且 video_register_device
6718c2ecf20Sopenharmony_ci将会试图选择这个设备节点号。如果这个编号被占用,下一个空闲的设备节点
6728c2ecf20Sopenharmony_ci编号将被选中,并向内核日志中发送一个警告信息。
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci另一个使用场景是当驱动创建多个设备时。这种情况下,对不同的视频设备在
6758c2ecf20Sopenharmony_ci编号上使用不同的范围是很有用的。例如,视频捕获设备从 0 开始,视频
6768c2ecf20Sopenharmony_ci输出设备从 16 开始。所以你可以使用最后一个参数来指定设备节点号最小值,
6778c2ecf20Sopenharmony_ci而 v4l2 框架会试图选择第一个的空闲编号(等于或大于你提供的编号)。
6788c2ecf20Sopenharmony_ci如果失败,则它会就选择第一个空闲的编号。
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci由于这种情况下,你会忽略无法选择特定设备节点号的警告,则可调用
6818c2ecf20Sopenharmony_civideo_register_device_no_warn() 函数避免警告信息的产生。
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci只要设备节点被创建,一些属性也会同时创建。在 /sys/class/video4linux
6848c2ecf20Sopenharmony_ci目录中你会找到这些设备。例如进入其中的 video0 目录,你会看到‘name’和
6858c2ecf20Sopenharmony_ci‘index’属性。‘name’属性值就是 video_device 结构体中的‘name’域。
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci‘index’属性值就是设备节点的索引值:每次调用 video_register_device(),
6888c2ecf20Sopenharmony_ci索引值都递增 1 。第一个视频设备节点总是从索引值 0 开始。
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci用户可以设置 udev 规则,利用索引属性生成花哨的设备名(例如:用‘mpegX’
6918c2ecf20Sopenharmony_ci代表 MPEG 视频捕获设备节点)。
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci在设备成功注册后,就可以使用这些域:
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci- vfl_type: 传递给 video_register_device 的设备类型。
6968c2ecf20Sopenharmony_ci- minor: 已指派的次设备号。
6978c2ecf20Sopenharmony_ci- num: 设备节点编号 (例如 videoX 中的 X)。
6988c2ecf20Sopenharmony_ci- index: 设备索引号。
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci如果注册失败,你必须调用 video_device_release() 来释放已分配的
7018c2ecf20Sopenharmony_civideo_device 结构体;如果 video_device 是嵌入在自己创建的结构体中,
7028c2ecf20Sopenharmony_ci你也必须释放它。vdev->release() 回调不会在注册失败之后被调用,
7038c2ecf20Sopenharmony_ci你也不应试图在注册失败后注销设备。
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_civideo_device 注销
7078c2ecf20Sopenharmony_ci----------------
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci当视频设备节点已被移除,不论是卸载驱动还是USB设备断开,你都应注销
7108c2ecf20Sopenharmony_ci它们:
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	video_unregister_device(vdev);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci这个操作将从 sysfs 中移除设备节点(导致 udev 将其从 /dev 中移除)。
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_civideo_unregister_device() 返回之后,就无法完成打开操作。尽管如此,
7178c2ecf20Sopenharmony_ciUSB 设备的情况则不同,某些应用程序可能依然打开着其中一个已注销设备
7188c2ecf20Sopenharmony_ci节点。所以在注销之后,所有文件操作(当然除了 release )也应返回错误值。
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci当最后一个视频设备节点的用户退出,则 vdev->release() 回调会被调用,
7218c2ecf20Sopenharmony_ci并且你可以做最后的清理操作。
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci不要忘记清理与视频设备相关的媒体入口(如果被初始化过):
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	media_entity_cleanup(&vdev->entity);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci这可以在 release 回调中完成。
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_civideo_device 辅助函数
7318c2ecf20Sopenharmony_ci---------------------
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci一些有用的辅助函数如下:
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci- file/video_device 私有数据
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci你可以用以下函数在 video_device 结构体中设置/获取驱动私有数据:
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_civoid *video_get_drvdata(struct video_device *vdev);
7408c2ecf20Sopenharmony_civoid video_set_drvdata(struct video_device *vdev, void *data);
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci注意:在调用 video_register_device() 前执行 video_set_drvdata()
7438c2ecf20Sopenharmony_ci是安全的。
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci而以下函数:
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_cistruct video_device *video_devdata(struct file *file);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci返回 file 结构体中拥有的的 video_device 指针。
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_civideo_drvdata 辅助函数结合了 video_get_drvdata 和 video_devdata
7528c2ecf20Sopenharmony_ci的功能:
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_civoid *video_drvdata(struct file *file);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci你可以使用如下代码从 video_device 结构体中获取 v4l2_device 结构体
7578c2ecf20Sopenharmony_ci指针:
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_cistruct v4l2_device *v4l2_dev = vdev->v4l2_dev;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci- 设备节点名
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_civideo_device 设备节点在内核中的名称可以通过以下函数获得
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ciconst char *video_device_node_name(struct video_device *vdev);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci这个名字被用户空间工具(例如 udev)作为提示信息使用。应尽可能使用
7688c2ecf20Sopenharmony_ci此功能,而非访问 video_device::num 和 video_device::minor 域。
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci视频缓冲辅助函数
7728c2ecf20Sopenharmony_ci---------------
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_civ4l2 核心 API 提供了一个处理视频缓冲的标准方法(称为“videobuf”)。
7758c2ecf20Sopenharmony_ci这些方法使驱动可以通过统一的方式实现 read()、mmap() 和 overlay()。
7768c2ecf20Sopenharmony_ci目前在设备上支持视频缓冲的方法有分散/聚集 DMA(videobuf-dma-sg)、
7778c2ecf20Sopenharmony_ci线性 DMA(videobuf-dma-contig)以及大多用于 USB 设备的用 vmalloc
7788c2ecf20Sopenharmony_ci分配的缓冲(videobuf-vmalloc)。
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci请参阅 Documentation/driver-api/media/v4l2-videobuf.rst,以获得更多关于 videobuf
7818c2ecf20Sopenharmony_ci层的使用信息。
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_civ4l2_fh 结构体
7848c2ecf20Sopenharmony_ci-------------
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_civ4l2_fh 结构体提供一个保存用于 V4L2 框架的文件句柄特定数据的简单方法。
7878c2ecf20Sopenharmony_ci如果 video_device 标志,新驱动
7888c2ecf20Sopenharmony_ci必须使用 v4l2_fh 结构体,因为它也用于实现优先级处理(VIDIOC_G/S_PRIORITY)。
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_civ4l2_fh 的用户(位于 V4l2 框架中,并非驱动)可通过测试
7918c2ecf20Sopenharmony_civideo_device->flags 中的 V4L2_FL_USES_V4L2_FH 位得知驱动是否使用
7928c2ecf20Sopenharmony_civ4l2_fh 作为他的 file->private_data 指针。这个位会在调用 v4l2_fh_init()
7938c2ecf20Sopenharmony_ci时被设置。
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_civ4l2_fh 结构体作为驱动自身文件句柄结构体的一部分被分配,且驱动在
7968c2ecf20Sopenharmony_ci其打开函数中将 file->private_data 指向它。
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci在许多情况下,v4l2_fh 结构体会嵌入到一个更大的结构体中。这钟情况下,
7998c2ecf20Sopenharmony_ci应该在 open() 中调用 v4l2_fh_init+v4l2_fh_add,并在 release() 中
8008c2ecf20Sopenharmony_ci调用 v4l2_fh_del+v4l2_fh_exit。
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci驱动可以通过使用 container_of 宏提取他们自己的文件句柄结构体。例如:
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistruct my_fh {
8058c2ecf20Sopenharmony_ci	int blah;
8068c2ecf20Sopenharmony_ci	struct v4l2_fh fh;
8078c2ecf20Sopenharmony_ci};
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci...
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ciint my_open(struct file *file)
8128c2ecf20Sopenharmony_ci{
8138c2ecf20Sopenharmony_ci	struct my_fh *my_fh;
8148c2ecf20Sopenharmony_ci	struct video_device *vfd;
8158c2ecf20Sopenharmony_ci	int ret;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	...
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	my_fh = kzalloc(sizeof(*my_fh), GFP_KERNEL);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	...
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	v4l2_fh_init(&my_fh->fh, vfd);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	...
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	file->private_data = &my_fh->fh;
8288c2ecf20Sopenharmony_ci	v4l2_fh_add(&my_fh->fh);
8298c2ecf20Sopenharmony_ci	return 0;
8308c2ecf20Sopenharmony_ci}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ciint my_release(struct file *file)
8338c2ecf20Sopenharmony_ci{
8348c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
8358c2ecf20Sopenharmony_ci	struct my_fh *my_fh = container_of(fh, struct my_fh, fh);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	...
8388c2ecf20Sopenharmony_ci	v4l2_fh_del(&my_fh->fh);
8398c2ecf20Sopenharmony_ci	v4l2_fh_exit(&my_fh->fh);
8408c2ecf20Sopenharmony_ci	kfree(my_fh);
8418c2ecf20Sopenharmony_ci	return 0;
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci以下是 v4l2_fh 函数使用的简介:
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_civoid v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci  初始化文件句柄。这*必须*在驱动的 v4l2_file_operations->open()
8498c2ecf20Sopenharmony_ci  函数中执行。
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_civoid v4l2_fh_add(struct v4l2_fh *fh)
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci  添加一个 v4l2_fh 到 video_device 文件句柄列表。一旦文件句柄
8548c2ecf20Sopenharmony_ci  初始化完成就必须调用。
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_civoid v4l2_fh_del(struct v4l2_fh *fh)
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci  从 video_device() 中解除文件句柄的关联。文件句柄的退出函数也
8598c2ecf20Sopenharmony_ci  将被调用。
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_civoid v4l2_fh_exit(struct v4l2_fh *fh)
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci  清理文件句柄。在清理完 v4l2_fh 后,相关内存会被释放。
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci如果 v4l2_fh 不是嵌入在其他结构体中的,则可以用这些辅助函数:
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ciint v4l2_fh_open(struct file *filp)
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci  分配一个 v4l2_fh 结构体空间,初始化并将其添加到 file 结构体相关的
8718c2ecf20Sopenharmony_ci  video_device 结构体中。
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ciint v4l2_fh_release(struct file *filp)
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci  从 file 结构体相关的 video_device 结构体中删除 v4l2_fh ,清理
8768c2ecf20Sopenharmony_ci  v4l2_fh 并释放空间。
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci这两个函数可以插入到 v4l2_file_operation 的 open() 和 release()
8798c2ecf20Sopenharmony_ci操作中。
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci某些驱动需要在第一个文件句柄打开和最后一个文件句柄关闭的时候做些
8838c2ecf20Sopenharmony_ci工作。所以加入了两个辅助函数以检查 v4l2_fh 结构体是否是相关设备
8848c2ecf20Sopenharmony_ci节点打开的唯一文件句柄。
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ciint v4l2_fh_is_singular(struct v4l2_fh *fh)
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci  如果此文件句柄是唯一打开的文件句柄,则返回 1 ,否则返回 0 。
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ciint v4l2_fh_is_singular_file(struct file *filp)
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci  功能相同,但通过 filp->private_data 调用 v4l2_fh_is_singular。
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ciV4L2 事件机制
8968c2ecf20Sopenharmony_ci-----------
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ciV4L2 事件机制提供了一个通用的方法将事件传递到用户空间。驱动必须使用
8998c2ecf20Sopenharmony_civ4l2_fh 才能支持 V4L2 事件机制。
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci事件通过一个类型和选择 ID 来定义。ID 对应一个 V4L2 对象,例如
9038c2ecf20Sopenharmony_ci一个控制 ID。如果未使用,则 ID 为 0。
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci当用户订阅一个事件,驱动会为此分配一些 kevent 结构体。所以每个
9068c2ecf20Sopenharmony_ci事件组(类型、ID)都会有自己的一套 kevent 结构体。这保证了如果
9078c2ecf20Sopenharmony_ci一个驱动短时间内产生了许多同类事件,不会覆盖其他类型的事件。
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci但如果你收到的事件数量大于同类事件 kevent 的保存数量,则最早的
9108c2ecf20Sopenharmony_ci事件将被丢弃,并加入新事件。
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci此外,v4l2_subscribed_event 结构体内部有可供驱动设置的 merge() 和
9138c2ecf20Sopenharmony_cireplace() 回调,这些回调会在新事件产生且没有多余空间的时候被调用。
9148c2ecf20Sopenharmony_cireplace() 回调让你可以将早期事件的净荷替换为新事件的净荷,将早期
9158c2ecf20Sopenharmony_ci净荷的相关数据合并到替换进来的新净荷中。当该类型的事件仅分配了一个
9168c2ecf20Sopenharmony_cikevent 结构体时,它将被调用。merge() 回调让你可以合并最早的事件净荷
9178c2ecf20Sopenharmony_ci到在它之后的那个事件净荷中。当该类型的事件分配了两个或更多 kevent
9188c2ecf20Sopenharmony_ci结构体时,它将被调用。
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci这种方法不会有状态信息丢失,只会导致中间步骤信息丢失。
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci关于 replace/merge 回调的一个不错的例子在 v4l2-event.c 中:用于
9248c2ecf20Sopenharmony_ci控制事件的 ctrls_replace() 和 ctrls_merge() 回调。
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci注意:这些回调可以在中断上下文中调用,所以它们必须尽快完成并退出。
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci有用的函数:
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_civoid v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci  将事件加入视频设备的队列。驱动仅负责填充 type 和 data 域。
9338c2ecf20Sopenharmony_ci  其他域由 V4L2 填充。
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ciint v4l2_event_subscribe(struct v4l2_fh *fh,
9368c2ecf20Sopenharmony_ci			 struct v4l2_event_subscription *sub, unsigned elems,
9378c2ecf20Sopenharmony_ci			 const struct v4l2_subscribed_event_ops *ops)
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci  video_device->ioctl_ops->vidioc_subscribe_event 必须检测驱动能
9408c2ecf20Sopenharmony_ci  产生特定 id 的事件。然后调用 v4l2_event_subscribe() 来订阅该事件。
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci  elems 参数是该事件的队列大小。若为 0,V4L2 框架将会(根据事件类型)
9438c2ecf20Sopenharmony_ci  填充默认值。
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci  ops 参数允许驱动指定一系列回调:
9468c2ecf20Sopenharmony_ci  * add:     当添加一个新监听者时调用(重复订阅同一个事件,此回调
9478c2ecf20Sopenharmony_ci             仅被执行一次)。
9488c2ecf20Sopenharmony_ci  * del:     当一个监听者停止监听时调用。
9498c2ecf20Sopenharmony_ci  * replace: 用‘新’事件替换‘早期‘事件。
9508c2ecf20Sopenharmony_ci  * merge:   将‘早期‘事件合并到‘新’事件中。
9518c2ecf20Sopenharmony_ci  这四个调用都是可选的,如果不想指定任何回调,则 ops 可为 NULL。
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ciint v4l2_event_unsubscribe(struct v4l2_fh *fh,
9548c2ecf20Sopenharmony_ci			   struct v4l2_event_subscription *sub)
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci  v4l2_ioctl_ops 结构体中的 vidioc_unsubscribe_event 回调函数。
9578c2ecf20Sopenharmony_ci  驱动程序可以直接使用 v4l2_event_unsubscribe() 实现退订事件过程。
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci  特殊的 V4L2_EVENT_ALL 类型,可用于退订所有事件。驱动可能在特殊
9608c2ecf20Sopenharmony_ci  情况下需要做此操作。
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ciint v4l2_event_pending(struct v4l2_fh *fh)
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci  返回未决事件的数量。有助于实现轮询(poll)操作。
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci事件通过 poll 系统调用传递到用户空间。驱动可用
9678c2ecf20Sopenharmony_civ4l2_fh->wait (wait_queue_head_t 类型)作为参数调用 poll_wait()。
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci事件分为标准事件和私有事件。新的标准事件必须使用可用的最小事件类型
9708c2ecf20Sopenharmony_ci编号。驱动必须从他们本类型的编号起始处分配事件。类型的编号起始为
9718c2ecf20Sopenharmony_ciV4L2_EVENT_PRIVATE_START + n * 1000 ,其中 n 为可用最小编号。每个
9728c2ecf20Sopenharmony_ci类型中的第一个事件类型编号是为以后的使用保留的,所以第一个可用事件
9738c2ecf20Sopenharmony_ci类型编号是‘class base + 1’。
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ciV4L2 事件机制的使用实例可以在 OMAP3 ISP 的驱动
9768c2ecf20Sopenharmony_ci(drivers/media/video/omap3isp)中找到。
977