1*4882a593SmuzhiyunChinese translated version of Documentation/driver-api/media/index.rst 2*4882a593Smuzhiyun 3*4882a593SmuzhiyunIf you have any comment or update to the content, please contact the 4*4882a593Smuzhiyunoriginal document maintainer directly. However, if you have a problem 5*4882a593Smuzhiyuncommunicating in English you can also ask the Chinese maintainer for 6*4882a593Smuzhiyunhelp. Contact the Chinese maintainer if this translation is outdated 7*4882a593Smuzhiyunor if there is a problem with the translation. 8*4882a593Smuzhiyun 9*4882a593SmuzhiyunMaintainer: Mauro Carvalho Chehab <mchehab@kernel.org> 10*4882a593SmuzhiyunChinese maintainer: Fu Wei <tekkamanninja@gmail.com> 11*4882a593Smuzhiyun--------------------------------------------------------------------- 12*4882a593SmuzhiyunDocumentation/driver-api/media/index.rst 的中文翻译 13*4882a593Smuzhiyun 14*4882a593Smuzhiyun如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 15*4882a593Smuzhiyun交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 16*4882a593Smuzhiyun译存在问题,请联系中文版维护者。 17*4882a593Smuzhiyun英文版维护者: Mauro Carvalho Chehab <mchehab@kernel.org> 18*4882a593Smuzhiyun中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com> 19*4882a593Smuzhiyun中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> 20*4882a593Smuzhiyun中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> 21*4882a593Smuzhiyun 22*4882a593Smuzhiyun 23*4882a593Smuzhiyun以下为正文 24*4882a593Smuzhiyun--------------------------------------------------------------------- 25*4882a593SmuzhiyunV4L2 驱动框架概览 26*4882a593Smuzhiyun============== 27*4882a593Smuzhiyun 28*4882a593Smuzhiyun本文档描述 V4L2 框架所提供的各种结构和它们之间的关系。 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun 31*4882a593Smuzhiyun介绍 32*4882a593Smuzhiyun---- 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun大部分现代 V4L2 设备由多个 IC 组成,在 /dev 下导出多个设备节点, 35*4882a593Smuzhiyun并同时创建非 V4L2 设备(如 DVB、ALSA、FB、I2C 和红外输入设备)。 36*4882a593Smuzhiyun由于这种硬件的复杂性,V4L2 驱动也变得非常复杂。 37*4882a593Smuzhiyun 38*4882a593Smuzhiyun尤其是 V4L2 必须支持 IC 实现音视频的多路复用和编解码,这就更增加了其 39*4882a593Smuzhiyun复杂性。通常这些 IC 通过一个或多个 I2C 总线连接到主桥驱动器,但也可 40*4882a593Smuzhiyun使用其他总线。这些设备称为“子设备”。 41*4882a593Smuzhiyun 42*4882a593Smuzhiyun长期以来,这个框架仅限于通过 video_device 结构体创建 V4L 设备节点, 43*4882a593Smuzhiyun并使用 video_buf 处理视频缓冲(注:本文不讨论 video_buf 框架)。 44*4882a593Smuzhiyun 45*4882a593Smuzhiyun这意味着所有驱动必须自己设置设备实例并连接到子设备。其中一部分要正确地 46*4882a593Smuzhiyun完成是比较复杂的,使得许多驱动都没有正确地实现。 47*4882a593Smuzhiyun 48*4882a593Smuzhiyun由于框架的缺失,有很多通用代码都不可重复利用。 49*4882a593Smuzhiyun 50*4882a593Smuzhiyun因此,这个框架构建所有驱动都需要的基本结构块,而统一的框架将使通用代码 51*4882a593Smuzhiyun创建成实用函数并在所有驱动中共享变得更加容易。 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun 54*4882a593Smuzhiyun驱动结构 55*4882a593Smuzhiyun------- 56*4882a593Smuzhiyun 57*4882a593Smuzhiyun所有 V4L2 驱动都有如下结构: 58*4882a593Smuzhiyun 59*4882a593Smuzhiyun1) 每个设备实例的结构体--包含其设备状态。 60*4882a593Smuzhiyun 61*4882a593Smuzhiyun2) 初始化和控制子设备的方法(如果有)。 62*4882a593Smuzhiyun 63*4882a593Smuzhiyun3) 创建 V4L2 设备节点 (/dev/videoX、/dev/vbiX 和 /dev/radioX) 64*4882a593Smuzhiyun 并跟踪设备节点的特定数据。 65*4882a593Smuzhiyun 66*4882a593Smuzhiyun4) 特定文件句柄结构体--包含每个文件句柄的数据。 67*4882a593Smuzhiyun 68*4882a593Smuzhiyun5) 视频缓冲处理。 69*4882a593Smuzhiyun 70*4882a593Smuzhiyun以下是它们的初略关系图: 71*4882a593Smuzhiyun 72*4882a593Smuzhiyun device instances(设备实例) 73*4882a593Smuzhiyun | 74*4882a593Smuzhiyun +-sub-device instances(子设备实例) 75*4882a593Smuzhiyun | 76*4882a593Smuzhiyun \-V4L2 device nodes(V4L2 设备节点) 77*4882a593Smuzhiyun | 78*4882a593Smuzhiyun \-filehandle instances(文件句柄实例) 79*4882a593Smuzhiyun 80*4882a593Smuzhiyun 81*4882a593Smuzhiyun框架结构 82*4882a593Smuzhiyun------- 83*4882a593Smuzhiyun 84*4882a593Smuzhiyun该框架非常类似驱动结构:它有一个 v4l2_device 结构用于保存设备 85*4882a593Smuzhiyun实例的数据;一个 v4l2_subdev 结构体代表子设备实例;video_device 86*4882a593Smuzhiyun结构体保存 V4L2 设备节点的数据;将来 v4l2_fh 结构体将跟踪文件句柄 87*4882a593Smuzhiyun实例(暂未尚未实现)。 88*4882a593Smuzhiyun 89*4882a593SmuzhiyunV4L2 框架也可与媒体框架整合(可选的)。如果驱动设置了 v4l2_device 90*4882a593Smuzhiyun结构体的 mdev 域,子设备和视频节点的入口将自动出现在媒体框架中。 91*4882a593Smuzhiyun 92*4882a593Smuzhiyun 93*4882a593Smuzhiyunv4l2_device 结构体 94*4882a593Smuzhiyun---------------- 95*4882a593Smuzhiyun 96*4882a593Smuzhiyun每个设备实例都通过 v4l2_device (v4l2-device.h)结构体来表示。 97*4882a593Smuzhiyun简单设备可以仅分配这个结构体,但在大多数情况下,都会将这个结构体 98*4882a593Smuzhiyun嵌入到一个更大的结构体中。 99*4882a593Smuzhiyun 100*4882a593Smuzhiyun你必须注册这个设备实例: 101*4882a593Smuzhiyun 102*4882a593Smuzhiyun v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); 103*4882a593Smuzhiyun 104*4882a593Smuzhiyun注册操作将会初始化 v4l2_device 结构体。如果 dev->driver_data 域 105*4882a593Smuzhiyun为 NULL,就将其指向 v4l2_dev。 106*4882a593Smuzhiyun 107*4882a593Smuzhiyun需要与媒体框架整合的驱动必须手动设置 dev->driver_data,指向包含 108*4882a593Smuzhiyunv4l2_device 结构体实例的驱动特定设备结构体。这可以在注册 V4L2 设备 109*4882a593Smuzhiyun实例前通过 dev_set_drvdata() 函数完成。同时必须设置 v4l2_device 110*4882a593Smuzhiyun结构体的 mdev 域,指向适当的初始化并注册过的 media_device 实例。 111*4882a593Smuzhiyun 112*4882a593Smuzhiyun如果 v4l2_dev->name 为空,则它将被设置为从 dev 中衍生出的值(为了 113*4882a593Smuzhiyun更加精确,形式为驱动名后跟 bus_id)。如果你在调用 v4l2_device_register 114*4882a593Smuzhiyun前已经设置好了,则不会被修改。如果 dev 为 NULL,则你*必须*在调用 115*4882a593Smuzhiyunv4l2_device_register 前设置 v4l2_dev->name。 116*4882a593Smuzhiyun 117*4882a593Smuzhiyun你可以基于驱动名和驱动的全局 atomic_t 类型的实例编号,通过 118*4882a593Smuzhiyunv4l2_device_set_name() 设置 name。这样会生成类似 ivtv0、ivtv1 等 119*4882a593Smuzhiyun名字。若驱动名以数字结尾,则会在编号和驱动名间插入一个破折号,如: 120*4882a593Smuzhiyuncx18-0、cx18-1 等。此函数返回实例编号。 121*4882a593Smuzhiyun 122*4882a593Smuzhiyun第一个 “dev” 参数通常是一个指向 pci_dev、usb_interface 或 123*4882a593Smuzhiyunplatform_device 的指针。很少使其为 NULL,除非是一个ISA设备或者 124*4882a593Smuzhiyun当一个设备创建了多个 PCI 设备,使得 v4l2_dev 无法与一个特定的父设备 125*4882a593Smuzhiyun关联。 126*4882a593Smuzhiyun 127*4882a593Smuzhiyun你也可以提供一个 notify() 回调,使子设备可以调用它实现事件通知。 128*4882a593Smuzhiyun但这个设置与子设备相关。子设备支持的任何通知必须在 129*4882a593Smuzhiyuninclude/media/<subdevice>.h 中定义一个消息头。 130*4882a593Smuzhiyun 131*4882a593Smuzhiyun注销 v4l2_device 使用如下函数: 132*4882a593Smuzhiyun 133*4882a593Smuzhiyun v4l2_device_unregister(struct v4l2_device *v4l2_dev); 134*4882a593Smuzhiyun 135*4882a593Smuzhiyun如果 dev->driver_data 域指向 v4l2_dev,将会被重置为 NULL。注销同时 136*4882a593Smuzhiyun会自动从设备中注销所有子设备。 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun如果你有一个热插拔设备(如USB设备),则当断开发生时,父设备将无效。 139*4882a593Smuzhiyun由于 v4l2_device 有一个指向父设备的指针必须被清除,同时标志父设备 140*4882a593Smuzhiyun已消失,所以必须调用以下函数: 141*4882a593Smuzhiyun 142*4882a593Smuzhiyun v4l2_device_disconnect(struct v4l2_device *v4l2_dev); 143*4882a593Smuzhiyun 144*4882a593Smuzhiyun这个函数并*不*注销子设备,因此你依然要调用 v4l2_device_unregister() 145*4882a593Smuzhiyun函数。如果你的驱动器并非热插拔的,就没有必要调用 v4l2_device_disconnect()。 146*4882a593Smuzhiyun 147*4882a593Smuzhiyun有时你需要遍历所有被特定驱动注册的设备。这通常发生在多个设备驱动使用 148*4882a593Smuzhiyun同一个硬件的情况下。如:ivtvfb 驱动是一个使用 ivtv 硬件的帧缓冲驱动, 149*4882a593Smuzhiyun同时 alsa 驱动也使用此硬件。 150*4882a593Smuzhiyun 151*4882a593Smuzhiyun你可以使用如下例程遍历所有注册的设备: 152*4882a593Smuzhiyun 153*4882a593Smuzhiyunstatic int callback(struct device *dev, void *p) 154*4882a593Smuzhiyun{ 155*4882a593Smuzhiyun struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); 156*4882a593Smuzhiyun 157*4882a593Smuzhiyun /* 测试这个设备是否已经初始化 */ 158*4882a593Smuzhiyun if (v4l2_dev == NULL) 159*4882a593Smuzhiyun return 0; 160*4882a593Smuzhiyun ... 161*4882a593Smuzhiyun return 0; 162*4882a593Smuzhiyun} 163*4882a593Smuzhiyun 164*4882a593Smuzhiyunint iterate(void *p) 165*4882a593Smuzhiyun{ 166*4882a593Smuzhiyun struct device_driver *drv; 167*4882a593Smuzhiyun int err; 168*4882a593Smuzhiyun 169*4882a593Smuzhiyun /* 在PCI 总线上查找ivtv驱动。 170*4882a593Smuzhiyun pci_bus_type是全局的. 对于USB总线使用usb_bus_type。 */ 171*4882a593Smuzhiyun drv = driver_find("ivtv", &pci_bus_type); 172*4882a593Smuzhiyun /* 遍历所有的ivtv设备实例 */ 173*4882a593Smuzhiyun err = driver_for_each_device(drv, NULL, p, callback); 174*4882a593Smuzhiyun put_driver(drv); 175*4882a593Smuzhiyun return err; 176*4882a593Smuzhiyun} 177*4882a593Smuzhiyun 178*4882a593Smuzhiyun有时你需要一个设备实例的运行计数。这个通常用于映射一个设备实例到一个 179*4882a593Smuzhiyun模块选择数组的索引。 180*4882a593Smuzhiyun 181*4882a593Smuzhiyun推荐方法如下: 182*4882a593Smuzhiyun 183*4882a593Smuzhiyunstatic atomic_t drv_instance = ATOMIC_INIT(0); 184*4882a593Smuzhiyun 185*4882a593Smuzhiyunstatic int drv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) 186*4882a593Smuzhiyun{ 187*4882a593Smuzhiyun ... 188*4882a593Smuzhiyun state->instance = atomic_inc_return(&drv_instance) - 1; 189*4882a593Smuzhiyun} 190*4882a593Smuzhiyun 191*4882a593Smuzhiyun如果你有多个设备节点,对于热插拔设备,知道何时注销 v4l2_device 结构体 192*4882a593Smuzhiyun就比较困难。为此 v4l2_device 有引用计数支持。当调用 video_register_device 193*4882a593Smuzhiyun时增加引用计数,而设备节点释放时减小引用计数。当引用计数为零,则 194*4882a593Smuzhiyunv4l2_device 的release() 回调将被执行。你就可以在此时做最后的清理工作。 195*4882a593Smuzhiyun 196*4882a593Smuzhiyun如果创建了其他设备节点(比如 ALSA),则你可以通过以下函数手动增减 197*4882a593Smuzhiyun引用计数: 198*4882a593Smuzhiyun 199*4882a593Smuzhiyunvoid v4l2_device_get(struct v4l2_device *v4l2_dev); 200*4882a593Smuzhiyun 201*4882a593Smuzhiyun或: 202*4882a593Smuzhiyun 203*4882a593Smuzhiyunint v4l2_device_put(struct v4l2_device *v4l2_dev); 204*4882a593Smuzhiyun 205*4882a593Smuzhiyun由于引用技术初始化为 1 ,你也需要在 disconnect() 回调(对于 USB 设备)中 206*4882a593Smuzhiyun调用 v4l2_device_put,或者 remove() 回调(例如对于 PCI 设备),否则 207*4882a593Smuzhiyun引用计数将永远不会为 0 。 208*4882a593Smuzhiyun 209*4882a593Smuzhiyunv4l2_subdev结构体 210*4882a593Smuzhiyun------------------ 211*4882a593Smuzhiyun 212*4882a593Smuzhiyun许多驱动需要与子设备通信。这些设备可以完成各种任务,但通常他们负责 213*4882a593Smuzhiyun音视频复用和编解码。如网络摄像头的子设备通常是传感器和摄像头控制器。 214*4882a593Smuzhiyun 215*4882a593Smuzhiyun这些一般为 I2C 接口设备,但并不一定都是。为了给驱动提供调用子设备的 216*4882a593Smuzhiyun统一接口,v4l2_subdev 结构体(v4l2-subdev.h)产生了。 217*4882a593Smuzhiyun 218*4882a593Smuzhiyun每个子设备驱动都必须有一个 v4l2_subdev 结构体。这个结构体可以单独 219*4882a593Smuzhiyun代表一个简单的子设备,也可以嵌入到一个更大的结构体中,与更多设备状态 220*4882a593Smuzhiyun信息保存在一起。通常有一个下级设备结构体(比如:i2c_client)包含了 221*4882a593Smuzhiyun内核创建的设备数据。建议使用 v4l2_set_subdevdata() 将这个结构体的 222*4882a593Smuzhiyun指针保存在 v4l2_subdev 的私有数据域(dev_priv)中。这使得通过 v4l2_subdev 223*4882a593Smuzhiyun找到实际的低层总线特定设备数据变得容易。 224*4882a593Smuzhiyun 225*4882a593Smuzhiyun你同时需要一个从低层结构体获取 v4l2_subdev 指针的方法。对于常用的 226*4882a593Smuzhiyuni2c_client 结构体,i2c_set_clientdata() 函数可用于保存一个 v4l2_subdev 227*4882a593Smuzhiyun指针;对于其他总线你可能需要使用其他相关函数。 228*4882a593Smuzhiyun 229*4882a593Smuzhiyun桥驱动中也应保存每个子设备的私有数据,比如一个指向特定桥的各设备私有 230*4882a593Smuzhiyun数据的指针。为此 v4l2_subdev 结构体提供主机私有数据域(host_priv), 231*4882a593Smuzhiyun并可通过 v4l2_get_subdev_hostdata() 和 v4l2_set_subdev_hostdata() 232*4882a593Smuzhiyun访问。 233*4882a593Smuzhiyun 234*4882a593Smuzhiyun从总线桥驱动的视角,驱动加载子设备模块并以某种方式获得 v4l2_subdev 235*4882a593Smuzhiyun结构体指针。对于 i2c 总线设备相对简单:调用 i2c_get_clientdata()。 236*4882a593Smuzhiyun对于其他总线也需要做类似的操作。针对 I2C 总线上的子设备辅助函数帮你 237*4882a593Smuzhiyun完成了大部分复杂的工作。 238*4882a593Smuzhiyun 239*4882a593Smuzhiyun每个 v4l2_subdev 都包含子设备驱动需要实现的函数指针(如果对此设备 240*4882a593Smuzhiyun不适用,可为NULL)。由于子设备可完成许多不同的工作,而在一个庞大的 241*4882a593Smuzhiyun函数指针结构体中通常仅有少数有用的函数实现其功能肯定不合适。所以, 242*4882a593Smuzhiyun函数指针根据其实现的功能被分类,每一类都有自己的函数指针结构体。 243*4882a593Smuzhiyun 244*4882a593Smuzhiyun顶层函数指针结构体包含了指向各类函数指针结构体的指针,如果子设备驱动 245*4882a593Smuzhiyun不支持该类函数中的任何一个功能,则指向该类结构体的指针为NULL。 246*4882a593Smuzhiyun 247*4882a593Smuzhiyun这些结构体定义如下: 248*4882a593Smuzhiyun 249*4882a593Smuzhiyunstruct v4l2_subdev_core_ops { 250*4882a593Smuzhiyun int (*log_status)(struct v4l2_subdev *sd); 251*4882a593Smuzhiyun int (*init)(struct v4l2_subdev *sd, u32 val); 252*4882a593Smuzhiyun ... 253*4882a593Smuzhiyun}; 254*4882a593Smuzhiyun 255*4882a593Smuzhiyunstruct v4l2_subdev_tuner_ops { 256*4882a593Smuzhiyun ... 257*4882a593Smuzhiyun}; 258*4882a593Smuzhiyun 259*4882a593Smuzhiyunstruct v4l2_subdev_audio_ops { 260*4882a593Smuzhiyun ... 261*4882a593Smuzhiyun}; 262*4882a593Smuzhiyun 263*4882a593Smuzhiyunstruct v4l2_subdev_video_ops { 264*4882a593Smuzhiyun ... 265*4882a593Smuzhiyun}; 266*4882a593Smuzhiyun 267*4882a593Smuzhiyunstruct v4l2_subdev_pad_ops { 268*4882a593Smuzhiyun ... 269*4882a593Smuzhiyun}; 270*4882a593Smuzhiyun 271*4882a593Smuzhiyunstruct v4l2_subdev_ops { 272*4882a593Smuzhiyun const struct v4l2_subdev_core_ops *core; 273*4882a593Smuzhiyun const struct v4l2_subdev_tuner_ops *tuner; 274*4882a593Smuzhiyun const struct v4l2_subdev_audio_ops *audio; 275*4882a593Smuzhiyun const struct v4l2_subdev_video_ops *video; 276*4882a593Smuzhiyun const struct v4l2_subdev_pad_ops *video; 277*4882a593Smuzhiyun}; 278*4882a593Smuzhiyun 279*4882a593Smuzhiyun其中 core(核心)函数集通常可用于所有子设备,其他类别的实现依赖于 280*4882a593Smuzhiyun子设备。如视频设备可能不支持音频操作函数,反之亦然。 281*4882a593Smuzhiyun 282*4882a593Smuzhiyun这样的设置在限制了函数指针数量的同时,还使增加新的操作函数和分类 283*4882a593Smuzhiyun变得较为容易。 284*4882a593Smuzhiyun 285*4882a593Smuzhiyun子设备驱动可使用如下函数初始化 v4l2_subdev 结构体: 286*4882a593Smuzhiyun 287*4882a593Smuzhiyun v4l2_subdev_init(sd, &ops); 288*4882a593Smuzhiyun 289*4882a593Smuzhiyun然后,你必须用一个唯一的名字初始化 subdev->name,并初始化模块的 290*4882a593Smuzhiyunowner 域。若使用 i2c 辅助函数,这些都会帮你处理好。 291*4882a593Smuzhiyun 292*4882a593Smuzhiyun若需同媒体框架整合,你必须调用 media_entity_pads_init() 初始化 v4l2_subdev 293*4882a593Smuzhiyun结构体中的 media_entity 结构体(entity 域): 294*4882a593Smuzhiyun 295*4882a593Smuzhiyun struct media_pad *pads = &my_sd->pads; 296*4882a593Smuzhiyun int err; 297*4882a593Smuzhiyun 298*4882a593Smuzhiyun err = media_entity_pads_init(&sd->entity, npads, pads); 299*4882a593Smuzhiyun 300*4882a593Smuzhiyunpads 数组必须预先初始化。无须手动设置 media_entity 的 type 和 301*4882a593Smuzhiyunname 域,但如有必要,revision 域必须初始化。 302*4882a593Smuzhiyun 303*4882a593Smuzhiyun当(任何)子设备节点被打开/关闭,对 entity 的引用将被自动获取/释放。 304*4882a593Smuzhiyun 305*4882a593Smuzhiyun在子设备被注销之后,不要忘记清理 media_entity 结构体: 306*4882a593Smuzhiyun 307*4882a593Smuzhiyun media_entity_cleanup(&sd->entity); 308*4882a593Smuzhiyun 309*4882a593Smuzhiyun如果子设备驱动趋向于处理视频并整合进了媒体框架,必须使用 v4l2_subdev_pad_ops 310*4882a593Smuzhiyun替代 v4l2_subdev_video_ops 实现格式相关的功能。 311*4882a593Smuzhiyun 312*4882a593Smuzhiyun这种情况下,子设备驱动应该设置 link_validate 域,以提供它自身的链接 313*4882a593Smuzhiyun验证函数。链接验证函数应对管道(两端链接的都是 V4L2 子设备)中的每个 314*4882a593Smuzhiyun链接调用。驱动还要负责验证子设备和视频节点间格式配置的正确性。 315*4882a593Smuzhiyun 316*4882a593Smuzhiyun如果 link_validate 操作没有设置,默认的 v4l2_subdev_link_validate_default() 317*4882a593Smuzhiyun函数将会被调用。这个函数保证宽、高和媒体总线像素格式在链接的收发两端 318*4882a593Smuzhiyun都一致。子设备驱动除了它们自己的检测外,也可以自由使用这个函数以执行 319*4882a593Smuzhiyun上面提到的检查。 320*4882a593Smuzhiyun 321*4882a593Smuzhiyun设备(桥)驱动程序必须向 v4l2_device 注册 v4l2_subdev: 322*4882a593Smuzhiyun 323*4882a593Smuzhiyun int err = v4l2_device_register_subdev(v4l2_dev, sd); 324*4882a593Smuzhiyun 325*4882a593Smuzhiyun如果子设备模块在它注册前消失,这个操作可能失败。在这个函数成功返回后, 326*4882a593Smuzhiyunsubdev->dev 域就指向了 v4l2_device。 327*4882a593Smuzhiyun 328*4882a593Smuzhiyun如果 v4l2_device 父设备的 mdev 域为非 NULL 值,则子设备实体将被自动 329*4882a593Smuzhiyun注册为媒体设备。 330*4882a593Smuzhiyun 331*4882a593Smuzhiyun注销子设备则可用如下函数: 332*4882a593Smuzhiyun 333*4882a593Smuzhiyun v4l2_device_unregister_subdev(sd); 334*4882a593Smuzhiyun 335*4882a593Smuzhiyun此后,子设备模块就可卸载,且 sd->dev == NULL。 336*4882a593Smuzhiyun 337*4882a593Smuzhiyun注册之设备后,可通过以下方式直接调用其操作函数: 338*4882a593Smuzhiyun 339*4882a593Smuzhiyun err = sd->ops->core->g_std(sd, &norm); 340*4882a593Smuzhiyun 341*4882a593Smuzhiyun但使用如下宏会比较容易且合适: 342*4882a593Smuzhiyun 343*4882a593Smuzhiyun err = v4l2_subdev_call(sd, core, g_std, &norm); 344*4882a593Smuzhiyun 345*4882a593Smuzhiyun这个宏将会做 NULL 指针检查,如果 subdev 为 NULL,则返回-ENODEV;如果 346*4882a593Smuzhiyunsubdev->core 或 subdev->core->g_std 为 NULL,则返回 -ENOIOCTLCMD; 347*4882a593Smuzhiyun否则将返回 subdev->ops->core->g_std ops 调用的实际结果。 348*4882a593Smuzhiyun 349*4882a593Smuzhiyun有时也可能同时调用所有或一系列子设备的某个操作函数: 350*4882a593Smuzhiyun 351*4882a593Smuzhiyun v4l2_device_call_all(v4l2_dev, 0, core, g_std, &norm); 352*4882a593Smuzhiyun 353*4882a593Smuzhiyun任何不支持此操作的子设备都会被跳过,并忽略错误返回值。但如果你需要 354*4882a593Smuzhiyun检查出错码,则可使用如下函数: 355*4882a593Smuzhiyun 356*4882a593Smuzhiyun err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_std, &norm); 357*4882a593Smuzhiyun 358*4882a593Smuzhiyun除 -ENOIOCTLCMD 外的任何错误都会跳出循环并返回错误值。如果(除 -ENOIOCTLCMD 359*4882a593Smuzhiyun外)没有错误发生,则返回 0。 360*4882a593Smuzhiyun 361*4882a593Smuzhiyun对于以上两个函数的第二个参数为组 ID。如果为 0,则所有子设备都会执行 362*4882a593Smuzhiyun这个操作。如果为非 0 值,则只有那些组 ID 匹配的子设备才会执行此操作。 363*4882a593Smuzhiyun在桥驱动注册一个子设备前,可以设置 sd->grp_id 为任何期望值(默认值为 364*4882a593Smuzhiyun0)。这个值属于桥驱动,且子设备驱动将不会修改和使用它。 365*4882a593Smuzhiyun 366*4882a593Smuzhiyun组 ID 赋予了桥驱动更多对于如何调用回调的控制。例如,电路板上有多个 367*4882a593Smuzhiyun音频芯片,每个都有改变音量的能力。但当用户想要改变音量的时候,通常 368*4882a593Smuzhiyun只有一个会被实际使用。你可以对这样的子设备设置组 ID 为(例如 AUDIO_CONTROLLER) 369*4882a593Smuzhiyun并在调用 v4l2_device_call_all() 时指定它为组 ID 值。这就保证了只有 370*4882a593Smuzhiyun需要的子设备才会执行这个回调。 371*4882a593Smuzhiyun 372*4882a593Smuzhiyun如果子设备需要通知它的 v4l2_device 父设备一个事件,可以调用 373*4882a593Smuzhiyunv4l2_subdev_notify(sd, notification, arg)。这个宏检查是否有一个 374*4882a593Smuzhiyunnotify() 回调被注册,如果没有,返回 -ENODEV。否则返回 notify() 调用 375*4882a593Smuzhiyun结果。 376*4882a593Smuzhiyun 377*4882a593Smuzhiyun使用 v4l2_subdev 的好处在于它是一个通用结构体,且不包含任何底层硬件 378*4882a593Smuzhiyun信息。所有驱动可以包含多个 I2C 总线的子设备,但也有子设备是通过 GPIO 379*4882a593Smuzhiyun控制。这个区别仅在配置设备时有关系,一旦子设备注册完成,对于 v4l2 380*4882a593Smuzhiyun子系统来说就完全透明了。 381*4882a593Smuzhiyun 382*4882a593Smuzhiyun 383*4882a593SmuzhiyunV4L2 子设备用户空间API 384*4882a593Smuzhiyun-------------------- 385*4882a593Smuzhiyun 386*4882a593Smuzhiyun除了通过 v4l2_subdev_ops 结构导出的内核 API,V4L2 子设备也可以直接 387*4882a593Smuzhiyun通过用户空间应用程序来控制。 388*4882a593Smuzhiyun 389*4882a593Smuzhiyun可以在 /dev 中创建名为 v4l-subdevX 设备节点,以通过其直接访问子设备。 390*4882a593Smuzhiyun如果子设备支持用户空间直接配置,必须在注册前设置 V4L2_SUBDEV_FL_HAS_DEVNODE 391*4882a593Smuzhiyun标志。 392*4882a593Smuzhiyun 393*4882a593Smuzhiyun注册子设备之后, v4l2_device 驱动会通过调用 v4l2_device_register_subdev_nodes() 394*4882a593Smuzhiyun函数为所有已注册并设置了 V4L2_SUBDEV_FL_HAS_DEVNODE 的子设备创建 395*4882a593Smuzhiyun设备节点。这些设备节点会在子设备注销时自动删除。 396*4882a593Smuzhiyun 397*4882a593Smuzhiyun这些设备节点处理 V4L2 API 的一个子集。 398*4882a593Smuzhiyun 399*4882a593SmuzhiyunVIDIOC_QUERYCTRL 400*4882a593SmuzhiyunVIDIOC_QUERYMENU 401*4882a593SmuzhiyunVIDIOC_G_CTRL 402*4882a593SmuzhiyunVIDIOC_S_CTRL 403*4882a593SmuzhiyunVIDIOC_G_EXT_CTRLS 404*4882a593SmuzhiyunVIDIOC_S_EXT_CTRLS 405*4882a593SmuzhiyunVIDIOC_TRY_EXT_CTRLS 406*4882a593Smuzhiyun 407*4882a593Smuzhiyun 这些 ioctls 控制与 V4L2 中定义的一致。他们行为相同,唯一的 408*4882a593Smuzhiyun 不同是他们只处理子设备的控制实现。根据驱动程序,这些控制也 409*4882a593Smuzhiyun 可以通过一个(或多个) V4L2 设备节点访问。 410*4882a593Smuzhiyun 411*4882a593SmuzhiyunVIDIOC_DQEVENT 412*4882a593SmuzhiyunVIDIOC_SUBSCRIBE_EVENT 413*4882a593SmuzhiyunVIDIOC_UNSUBSCRIBE_EVENT 414*4882a593Smuzhiyun 415*4882a593Smuzhiyun 这些 ioctls 事件与 V4L2 中定义的一致。他们行为相同,唯一的 416*4882a593Smuzhiyun 不同是他们只处理子设备产生的事件。根据驱动程序,这些事件也 417*4882a593Smuzhiyun 可以通过一个(或多个) V4L2 设备节点上报。 418*4882a593Smuzhiyun 419*4882a593Smuzhiyun 要使用事件通知的子设备驱动,在注册子设备前必须在 v4l2_subdev::flags 420*4882a593Smuzhiyun 中设置 V4L2_SUBDEV_USES_EVENTS 并在 v4l2_subdev::nevents 421*4882a593Smuzhiyun 中初始化事件队列深度。注册完成后,事件会在 v4l2_subdev::devnode 422*4882a593Smuzhiyun 设备节点中像通常一样被排队。 423*4882a593Smuzhiyun 424*4882a593Smuzhiyun 为正确支持事件机制,poll() 文件操作也应被实现。 425*4882a593Smuzhiyun 426*4882a593Smuzhiyun私有 ioctls 427*4882a593Smuzhiyun 428*4882a593Smuzhiyun 不在以上列表中的所有 ioctls 会通过 core::ioctl 操作直接传递 429*4882a593Smuzhiyun 给子设备驱动。 430*4882a593Smuzhiyun 431*4882a593Smuzhiyun 432*4882a593SmuzhiyunI2C 子设备驱动 433*4882a593Smuzhiyun------------- 434*4882a593Smuzhiyun 435*4882a593Smuzhiyun由于这些驱动很常见,所以内特提供了特定的辅助函数(v4l2-common.h)让这些 436*4882a593Smuzhiyun设备的使用更加容易。 437*4882a593Smuzhiyun 438*4882a593Smuzhiyun添加 v4l2_subdev 支持的推荐方法是让 I2C 驱动将 v4l2_subdev 结构体 439*4882a593Smuzhiyun嵌入到为每个 I2C 设备实例创建的状态结构体中。而最简单的设备没有状态 440*4882a593Smuzhiyun结构体,此时可以直接创建一个 v4l2_subdev 结构体。 441*4882a593Smuzhiyun 442*4882a593Smuzhiyun一个典型的状态结构体如下所示(‘chipname’用芯片名代替): 443*4882a593Smuzhiyun 444*4882a593Smuzhiyunstruct chipname_state { 445*4882a593Smuzhiyun struct v4l2_subdev sd; 446*4882a593Smuzhiyun ... /* 附加的状态域*/ 447*4882a593Smuzhiyun}; 448*4882a593Smuzhiyun 449*4882a593Smuzhiyun初始化 v4l2_subdev 结构体的方法如下: 450*4882a593Smuzhiyun 451*4882a593Smuzhiyun v4l2_i2c_subdev_init(&state->sd, client, subdev_ops); 452*4882a593Smuzhiyun 453*4882a593Smuzhiyun这个函数将填充 v4l2_subdev 结构体中的所有域,并保证 v4l2_subdev 和 454*4882a593Smuzhiyuni2c_client 都指向彼此。 455*4882a593Smuzhiyun 456*4882a593Smuzhiyun同时,你也应该为从 v4l2_subdev 指针找到 chipname_state 结构体指针 457*4882a593Smuzhiyun添加一个辅助内联函数。 458*4882a593Smuzhiyun 459*4882a593Smuzhiyunstatic inline struct chipname_state *to_state(struct v4l2_subdev *sd) 460*4882a593Smuzhiyun{ 461*4882a593Smuzhiyun return container_of(sd, struct chipname_state, sd); 462*4882a593Smuzhiyun} 463*4882a593Smuzhiyun 464*4882a593Smuzhiyun使用以下函数可以通过 v4l2_subdev 结构体指针获得 i2c_client 结构体 465*4882a593Smuzhiyun指针: 466*4882a593Smuzhiyun 467*4882a593Smuzhiyun struct i2c_client *client = v4l2_get_subdevdata(sd); 468*4882a593Smuzhiyun 469*4882a593Smuzhiyun而以下函数则相反,通过 i2c_client 结构体指针获得 v4l2_subdev 结构体 470*4882a593Smuzhiyun指针: 471*4882a593Smuzhiyun 472*4882a593Smuzhiyun struct v4l2_subdev *sd = i2c_get_clientdata(client); 473*4882a593Smuzhiyun 474*4882a593Smuzhiyun当 remove()函数被调用前,必须保证先调用 v4l2_device_unregister_subdev(sd)。 475*4882a593Smuzhiyun此操作将会从桥驱动中注销子设备。即使子设备没有注册,调用此函数也是 476*4882a593Smuzhiyun安全的。 477*4882a593Smuzhiyun 478*4882a593Smuzhiyun必须这样做的原因是:当桥驱动注销 i2c 适配器时,remove()回调函数 479*4882a593Smuzhiyun会被那个适配器上的 i2c 设备调用。此后,相应的 v4l2_subdev 结构体 480*4882a593Smuzhiyun就不存在了,所有它们必须先被注销。在 remove()回调函数中调用 481*4882a593Smuzhiyunv4l2_device_unregister_subdev(sd),可以保证执行总是正确的。 482*4882a593Smuzhiyun 483*4882a593Smuzhiyun 484*4882a593Smuzhiyun桥驱动也有一些辅组函数可用: 485*4882a593Smuzhiyun 486*4882a593Smuzhiyunstruct v4l2_subdev *sd = v4l2_i2c_new_subdev(v4l2_dev, adapter, 487*4882a593Smuzhiyun "module_foo", "chipid", 0x36, NULL); 488*4882a593Smuzhiyun 489*4882a593Smuzhiyun这个函数会加载给定的模块(如果没有模块需要加载,可以为 NULL), 490*4882a593Smuzhiyun并用给定的 i2c 适配器结构体指针(i2c_adapter)和 器件地址(chip/address) 491*4882a593Smuzhiyun作为参数调用 i2c_new_client_device()。如果一切顺利,则就在 v4l2_device 492*4882a593Smuzhiyun中注册了子设备。 493*4882a593Smuzhiyun 494*4882a593Smuzhiyun你也可以利用 v4l2_i2c_new_subdev()的最后一个参数,传递一个可能的 495*4882a593SmuzhiyunI2C 地址数组,让函数自动探测。这些探测地址只有在前一个参数为 0 的 496*4882a593Smuzhiyun情况下使用。非零参数意味着你知道准确的 i2c 地址,所以此时无须进行 497*4882a593Smuzhiyun探测。 498*4882a593Smuzhiyun 499*4882a593Smuzhiyun如果出错,两个函数都返回 NULL。 500*4882a593Smuzhiyun 501*4882a593Smuzhiyun注意:传递给 v4l2_i2c_new_subdev()的 chipid 通常与模块名一致。 502*4882a593Smuzhiyun它允许你指定一个芯片的变体,比如“saa7114”或“saa7115”。一般通过 503*4882a593Smuzhiyuni2c 驱动自动探测。chipid 的使用是在今后需要深入了解的事情。这个与 504*4882a593Smuzhiyuni2c 驱动不同,较容易混淆。要知道支持哪些芯片变体,你可以查阅 i2c 505*4882a593Smuzhiyun驱动代码的 i2c_device_id 表,上面列出了所有可能支持的芯片。 506*4882a593Smuzhiyun 507*4882a593Smuzhiyun还有两个辅助函数: 508*4882a593Smuzhiyun 509*4882a593Smuzhiyunv4l2_i2c_new_subdev_cfg:这个函数添加新的 irq 和 platform_data 510*4882a593Smuzhiyun参数,并有‘addr’和‘probed_addrs’参数:如果 addr 非零,则被使用 511*4882a593Smuzhiyun(不探测变体),否则 probed_addrs 中的地址将用于自动探测。 512*4882a593Smuzhiyun 513*4882a593Smuzhiyun例如:以下代码将会探测地址(0x10): 514*4882a593Smuzhiyun 515*4882a593Smuzhiyunstruct v4l2_subdev *sd = v4l2_i2c_new_subdev_cfg(v4l2_dev, adapter, 516*4882a593Smuzhiyun "module_foo", "chipid", 0, NULL, 0, I2C_ADDRS(0x10)); 517*4882a593Smuzhiyun 518*4882a593Smuzhiyunv4l2_i2c_new_subdev_board 使用一个 i2c_board_info 结构体,将其 519*4882a593Smuzhiyun替代 irq、platform_data 和 add r参数传递给 i2c 驱动。 520*4882a593Smuzhiyun 521*4882a593Smuzhiyun如果子设备支持 s_config 核心操作,这个操作会在子设备配置好之后以 irq 和 522*4882a593Smuzhiyunplatform_data 为参数调用。早期的 v4l2_i2c_new_(probed_)subdev 函数 523*4882a593Smuzhiyun同样也会调用 s_config,但仅在 irq 为 0 且 platform_data 为 NULL 时。 524*4882a593Smuzhiyun 525*4882a593Smuzhiyunvideo_device结构体 526*4882a593Smuzhiyun----------------- 527*4882a593Smuzhiyun 528*4882a593Smuzhiyun在 /dev 目录下的实际设备节点根据 video_device 结构体(v4l2-dev.h) 529*4882a593Smuzhiyun创建。此结构体既可以动态分配也可以嵌入到一个更大的结构体中。 530*4882a593Smuzhiyun 531*4882a593Smuzhiyun动态分配方法如下: 532*4882a593Smuzhiyun 533*4882a593Smuzhiyun struct video_device *vdev = video_device_alloc(); 534*4882a593Smuzhiyun 535*4882a593Smuzhiyun if (vdev == NULL) 536*4882a593Smuzhiyun return -ENOMEM; 537*4882a593Smuzhiyun 538*4882a593Smuzhiyun vdev->release = video_device_release; 539*4882a593Smuzhiyun 540*4882a593Smuzhiyun如果将其嵌入到一个大结构体中,则必须自己实现 release()回调。 541*4882a593Smuzhiyun 542*4882a593Smuzhiyun struct video_device *vdev = &my_vdev->vdev; 543*4882a593Smuzhiyun 544*4882a593Smuzhiyun vdev->release = my_vdev_release; 545*4882a593Smuzhiyun 546*4882a593Smuzhiyunrelease()回调必须被设置,且在最后一个 video_device 用户退出之后 547*4882a593Smuzhiyun被调用。 548*4882a593Smuzhiyun 549*4882a593Smuzhiyun默认的 video_device_release()回调只是调用 kfree 来释放之前分配的 550*4882a593Smuzhiyun内存。 551*4882a593Smuzhiyun 552*4882a593Smuzhiyun你应该设置这些域: 553*4882a593Smuzhiyun 554*4882a593Smuzhiyun- v4l2_dev: 设置为 v4l2_device 父设备。 555*4882a593Smuzhiyun 556*4882a593Smuzhiyun- name: 设置为唯一的描述性设备名。 557*4882a593Smuzhiyun 558*4882a593Smuzhiyun- fops: 设置为已有的 v4l2_file_operations 结构体。 559*4882a593Smuzhiyun 560*4882a593Smuzhiyun- ioctl_ops: 如果你使用v4l2_ioctl_ops 来简化 ioctl 的维护 561*4882a593Smuzhiyun (强烈建议使用,且将来可能变为强制性的!),然后设置你自己的 562*4882a593Smuzhiyun v4l2_ioctl_ops 结构体. 563*4882a593Smuzhiyun 564*4882a593Smuzhiyun- lock: 如果你要在驱动中实现所有的锁操作,则设为 NULL 。否则 565*4882a593Smuzhiyun 就要设置一个指向 struct mutex_lock 结构体的指针,这个锁将 566*4882a593Smuzhiyun 在 unlocked_ioctl 文件操作被调用前由内核获得,并在调用返回后 567*4882a593Smuzhiyun 释放。详见下一节。 568*4882a593Smuzhiyun 569*4882a593Smuzhiyun- prio: 保持对优先级的跟踪。用于实现 VIDIOC_G/S_PRIORITY。如果 570*4882a593Smuzhiyun 设置为 NULL,则会使用 v4l2_device 中的 v4l2_prio_state 结构体。 571*4882a593Smuzhiyun 如果要对每个设备节点(组)实现独立的优先级,可以将其指向自己 572*4882a593Smuzhiyun 实现的 v4l2_prio_state 结构体。 573*4882a593Smuzhiyun 574*4882a593Smuzhiyun- parent: 仅在使用 NULL 作为父设备结构体参数注册 v4l2_device 时 575*4882a593Smuzhiyun 设置此参数。只有在一个硬件设备包含多一个 PCI 设备,共享同一个 576*4882a593Smuzhiyun v4l2_device 核心时才会发生。 577*4882a593Smuzhiyun 578*4882a593Smuzhiyun cx88 驱动就是一个例子:一个 v4l2_device 结构体核心,被一个裸的 579*4882a593Smuzhiyun 视频 PCI 设备(cx8800)和一个 MPEG PCI 设备(cx8802)共用。由于 580*4882a593Smuzhiyun v4l2_device 无法与特定的 PCI 设备关联,所有没有设置父设备。但当 581*4882a593Smuzhiyun video_device 配置后,就知道使用哪个父 PCI 设备了。 582*4882a593Smuzhiyun 583*4882a593Smuzhiyun如果你使用 v4l2_ioctl_ops,则应该在 v4l2_file_operations 结构体中 584*4882a593Smuzhiyun设置 .unlocked_ioctl 指向 video_ioctl2。 585*4882a593Smuzhiyun 586*4882a593Smuzhiyun请勿使用 .ioctl!它已被废弃,今后将消失。 587*4882a593Smuzhiyun 588*4882a593Smuzhiyun某些情况下你要告诉核心:你在 v4l2_ioctl_ops 指定的某个函数应被忽略。 589*4882a593Smuzhiyun你可以在 video_device_register 被调用前通过以下函数标记这个 ioctls。 590*4882a593Smuzhiyun 591*4882a593Smuzhiyunvoid v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd); 592*4882a593Smuzhiyun 593*4882a593Smuzhiyun基于外部因素(例如某个板卡已被使用),在不创建新结构体的情况下,你想 594*4882a593Smuzhiyun要关闭 v4l2_ioctl_ops 中某个特性往往需要这个机制。 595*4882a593Smuzhiyun 596*4882a593Smuzhiyunv4l2_file_operations 结构体是 file_operations 的一个子集。其主要 597*4882a593Smuzhiyun区别在于:因 inode 参数从未被使用,它将被忽略。 598*4882a593Smuzhiyun 599*4882a593Smuzhiyun如果需要与媒体框架整合,你必须通过调用 media_entity_pads_init() 初始化 600*4882a593Smuzhiyun嵌入在 video_device 结构体中的 media_entity(entity 域)结构体: 601*4882a593Smuzhiyun 602*4882a593Smuzhiyun struct media_pad *pad = &my_vdev->pad; 603*4882a593Smuzhiyun int err; 604*4882a593Smuzhiyun 605*4882a593Smuzhiyun err = media_entity_pads_init(&vdev->entity, 1, pad); 606*4882a593Smuzhiyun 607*4882a593Smuzhiyunpads 数组必须预先初始化。没有必要手动设置 media_entity 的 type 和 608*4882a593Smuzhiyunname 域。 609*4882a593Smuzhiyun 610*4882a593Smuzhiyun当(任何)子设备节点被打开/关闭,对 entity 的引用将被自动获取/释放。 611*4882a593Smuzhiyun 612*4882a593Smuzhiyunv4l2_file_operations 与锁 613*4882a593Smuzhiyun-------------------------- 614*4882a593Smuzhiyun 615*4882a593Smuzhiyun你可以在 video_device 结构体中设置一个指向 mutex_lock 的指针。通常 616*4882a593Smuzhiyun这既可是一个顶层互斥锁也可为设备节点自身的互斥锁。默认情况下,此锁 617*4882a593Smuzhiyun用于 unlocked_ioctl,但为了使用 ioctls 你通过以下函数可禁用锁定: 618*4882a593Smuzhiyun 619*4882a593Smuzhiyun void v4l2_disable_ioctl_locking(struct video_device *vdev, unsigned int cmd); 620*4882a593Smuzhiyun 621*4882a593Smuzhiyun例如: v4l2_disable_ioctl_locking(vdev, VIDIOC_DQBUF); 622*4882a593Smuzhiyun 623*4882a593Smuzhiyun你必须在注册 video_device 前调用这个函数。 624*4882a593Smuzhiyun 625*4882a593Smuzhiyun特别是对于 USB 驱动程序,某些命令(如设置控制)需要很长的时间,可能 626*4882a593Smuzhiyun需要自行为缓冲区队列的 ioctls 实现锁定。 627*4882a593Smuzhiyun 628*4882a593Smuzhiyun如果你需要更细粒度的锁,你必须设置 mutex_lock 为 NULL,并完全自己实现 629*4882a593Smuzhiyun锁机制。 630*4882a593Smuzhiyun 631*4882a593Smuzhiyun这完全由驱动开发者决定使用何种方法。然而,如果你的驱动存在长延时操作 632*4882a593Smuzhiyun(例如,改变 USB 摄像头的曝光时间可能需要较长时间),而你又想让用户 633*4882a593Smuzhiyun在等待长延时操作完成期间做其他的事,则你最好自己实现锁机制。 634*4882a593Smuzhiyun 635*4882a593Smuzhiyun如果指定一个锁,则所有 ioctl 操作将在这个锁的作用下串行执行。如果你 636*4882a593Smuzhiyun使用 videobuf,则必须将同一个锁传递给 videobuf 队列初始化函数;如 637*4882a593Smuzhiyunvideobuf 必须等待一帧的到达,则可临时解锁并在这之后重新上锁。如果驱动 638*4882a593Smuzhiyun也在代码执行期间等待,则可做同样的工作(临时解锁,再上锁)让其他进程 639*4882a593Smuzhiyun可以在第一个进程阻塞时访问设备节点。 640*4882a593Smuzhiyun 641*4882a593Smuzhiyun在使用 videobuf2 的情况下,必须实现 wait_prepare 和 wait_finish 回调 642*4882a593Smuzhiyun在适当的时候解锁/加锁。进一步来说,如果你在 video_device 结构体中使用 643*4882a593Smuzhiyun锁,则必须在 wait_prepare 和 wait_finish 中对这个互斥锁进行解锁/加锁。 644*4882a593Smuzhiyun 645*4882a593Smuzhiyun热插拔的断开实现也必须在调用 v4l2_device_disconnect 前获得锁。 646*4882a593Smuzhiyun 647*4882a593Smuzhiyunvideo_device注册 648*4882a593Smuzhiyun--------------- 649*4882a593Smuzhiyun 650*4882a593Smuzhiyun接下来你需要注册视频设备:这会为你创建一个字符设备。 651*4882a593Smuzhiyun 652*4882a593Smuzhiyun err = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 653*4882a593Smuzhiyun if (err) { 654*4882a593Smuzhiyun video_device_release(vdev); /* or kfree(my_vdev); */ 655*4882a593Smuzhiyun return err; 656*4882a593Smuzhiyun } 657*4882a593Smuzhiyun 658*4882a593Smuzhiyun如果 v4l2_device 父设备的 mdev 域为非 NULL 值,视频设备实体将自动 659*4882a593Smuzhiyun注册为媒体设备。 660*4882a593Smuzhiyun 661*4882a593Smuzhiyun注册哪种设备是根据类型(type)参数。存在以下类型: 662*4882a593Smuzhiyun 663*4882a593SmuzhiyunVFL_TYPE_VIDEO: 用于视频输入/输出设备的 videoX 664*4882a593SmuzhiyunVFL_TYPE_VBI: 用于垂直消隐数据的 vbiX (例如,隐藏式字幕,图文电视) 665*4882a593SmuzhiyunVFL_TYPE_RADIO: 用于广播调谐器的 radioX 666*4882a593Smuzhiyun 667*4882a593Smuzhiyun最后一个参数让你确定一个所控制设备的设备节点号数量(例如 videoX 中的 X)。 668*4882a593Smuzhiyun通常你可以传入-1,让 v4l2 框架自己选择第一个空闲的编号。但是有时用户 669*4882a593Smuzhiyun需要选择一个特定的节点号。驱动允许用户通过驱动模块参数选择一个特定的 670*4882a593Smuzhiyun设备节点号是很普遍的。这个编号将会传递给这个函数,且 video_register_device 671*4882a593Smuzhiyun将会试图选择这个设备节点号。如果这个编号被占用,下一个空闲的设备节点 672*4882a593Smuzhiyun编号将被选中,并向内核日志中发送一个警告信息。 673*4882a593Smuzhiyun 674*4882a593Smuzhiyun另一个使用场景是当驱动创建多个设备时。这种情况下,对不同的视频设备在 675*4882a593Smuzhiyun编号上使用不同的范围是很有用的。例如,视频捕获设备从 0 开始,视频 676*4882a593Smuzhiyun输出设备从 16 开始。所以你可以使用最后一个参数来指定设备节点号最小值, 677*4882a593Smuzhiyun而 v4l2 框架会试图选择第一个的空闲编号(等于或大于你提供的编号)。 678*4882a593Smuzhiyun如果失败,则它会就选择第一个空闲的编号。 679*4882a593Smuzhiyun 680*4882a593Smuzhiyun由于这种情况下,你会忽略无法选择特定设备节点号的警告,则可调用 681*4882a593Smuzhiyunvideo_register_device_no_warn() 函数避免警告信息的产生。 682*4882a593Smuzhiyun 683*4882a593Smuzhiyun只要设备节点被创建,一些属性也会同时创建。在 /sys/class/video4linux 684*4882a593Smuzhiyun目录中你会找到这些设备。例如进入其中的 video0 目录,你会看到‘name’和 685*4882a593Smuzhiyun‘index’属性。‘name’属性值就是 video_device 结构体中的‘name’域。 686*4882a593Smuzhiyun 687*4882a593Smuzhiyun‘index’属性值就是设备节点的索引值:每次调用 video_register_device(), 688*4882a593Smuzhiyun索引值都递增 1 。第一个视频设备节点总是从索引值 0 开始。 689*4882a593Smuzhiyun 690*4882a593Smuzhiyun用户可以设置 udev 规则,利用索引属性生成花哨的设备名(例如:用‘mpegX’ 691*4882a593Smuzhiyun代表 MPEG 视频捕获设备节点)。 692*4882a593Smuzhiyun 693*4882a593Smuzhiyun在设备成功注册后,就可以使用这些域: 694*4882a593Smuzhiyun 695*4882a593Smuzhiyun- vfl_type: 传递给 video_register_device 的设备类型。 696*4882a593Smuzhiyun- minor: 已指派的次设备号。 697*4882a593Smuzhiyun- num: 设备节点编号 (例如 videoX 中的 X)。 698*4882a593Smuzhiyun- index: 设备索引号。 699*4882a593Smuzhiyun 700*4882a593Smuzhiyun如果注册失败,你必须调用 video_device_release() 来释放已分配的 701*4882a593Smuzhiyunvideo_device 结构体;如果 video_device 是嵌入在自己创建的结构体中, 702*4882a593Smuzhiyun你也必须释放它。vdev->release() 回调不会在注册失败之后被调用, 703*4882a593Smuzhiyun你也不应试图在注册失败后注销设备。 704*4882a593Smuzhiyun 705*4882a593Smuzhiyun 706*4882a593Smuzhiyunvideo_device 注销 707*4882a593Smuzhiyun---------------- 708*4882a593Smuzhiyun 709*4882a593Smuzhiyun当视频设备节点已被移除,不论是卸载驱动还是USB设备断开,你都应注销 710*4882a593Smuzhiyun它们: 711*4882a593Smuzhiyun 712*4882a593Smuzhiyun video_unregister_device(vdev); 713*4882a593Smuzhiyun 714*4882a593Smuzhiyun这个操作将从 sysfs 中移除设备节点(导致 udev 将其从 /dev 中移除)。 715*4882a593Smuzhiyun 716*4882a593Smuzhiyunvideo_unregister_device() 返回之后,就无法完成打开操作。尽管如此, 717*4882a593SmuzhiyunUSB 设备的情况则不同,某些应用程序可能依然打开着其中一个已注销设备 718*4882a593Smuzhiyun节点。所以在注销之后,所有文件操作(当然除了 release )也应返回错误值。 719*4882a593Smuzhiyun 720*4882a593Smuzhiyun当最后一个视频设备节点的用户退出,则 vdev->release() 回调会被调用, 721*4882a593Smuzhiyun并且你可以做最后的清理操作。 722*4882a593Smuzhiyun 723*4882a593Smuzhiyun不要忘记清理与视频设备相关的媒体入口(如果被初始化过): 724*4882a593Smuzhiyun 725*4882a593Smuzhiyun media_entity_cleanup(&vdev->entity); 726*4882a593Smuzhiyun 727*4882a593Smuzhiyun这可以在 release 回调中完成。 728*4882a593Smuzhiyun 729*4882a593Smuzhiyun 730*4882a593Smuzhiyunvideo_device 辅助函数 731*4882a593Smuzhiyun--------------------- 732*4882a593Smuzhiyun 733*4882a593Smuzhiyun一些有用的辅助函数如下: 734*4882a593Smuzhiyun 735*4882a593Smuzhiyun- file/video_device 私有数据 736*4882a593Smuzhiyun 737*4882a593Smuzhiyun你可以用以下函数在 video_device 结构体中设置/获取驱动私有数据: 738*4882a593Smuzhiyun 739*4882a593Smuzhiyunvoid *video_get_drvdata(struct video_device *vdev); 740*4882a593Smuzhiyunvoid video_set_drvdata(struct video_device *vdev, void *data); 741*4882a593Smuzhiyun 742*4882a593Smuzhiyun注意:在调用 video_register_device() 前执行 video_set_drvdata() 743*4882a593Smuzhiyun是安全的。 744*4882a593Smuzhiyun 745*4882a593Smuzhiyun而以下函数: 746*4882a593Smuzhiyun 747*4882a593Smuzhiyunstruct video_device *video_devdata(struct file *file); 748*4882a593Smuzhiyun 749*4882a593Smuzhiyun返回 file 结构体中拥有的的 video_device 指针。 750*4882a593Smuzhiyun 751*4882a593Smuzhiyunvideo_drvdata 辅助函数结合了 video_get_drvdata 和 video_devdata 752*4882a593Smuzhiyun的功能: 753*4882a593Smuzhiyun 754*4882a593Smuzhiyunvoid *video_drvdata(struct file *file); 755*4882a593Smuzhiyun 756*4882a593Smuzhiyun你可以使用如下代码从 video_device 结构体中获取 v4l2_device 结构体 757*4882a593Smuzhiyun指针: 758*4882a593Smuzhiyun 759*4882a593Smuzhiyunstruct v4l2_device *v4l2_dev = vdev->v4l2_dev; 760*4882a593Smuzhiyun 761*4882a593Smuzhiyun- 设备节点名 762*4882a593Smuzhiyun 763*4882a593Smuzhiyunvideo_device 设备节点在内核中的名称可以通过以下函数获得 764*4882a593Smuzhiyun 765*4882a593Smuzhiyunconst char *video_device_node_name(struct video_device *vdev); 766*4882a593Smuzhiyun 767*4882a593Smuzhiyun这个名字被用户空间工具(例如 udev)作为提示信息使用。应尽可能使用 768*4882a593Smuzhiyun此功能,而非访问 video_device::num 和 video_device::minor 域。 769*4882a593Smuzhiyun 770*4882a593Smuzhiyun 771*4882a593Smuzhiyun视频缓冲辅助函数 772*4882a593Smuzhiyun--------------- 773*4882a593Smuzhiyun 774*4882a593Smuzhiyunv4l2 核心 API 提供了一个处理视频缓冲的标准方法(称为“videobuf”)。 775*4882a593Smuzhiyun这些方法使驱动可以通过统一的方式实现 read()、mmap() 和 overlay()。 776*4882a593Smuzhiyun目前在设备上支持视频缓冲的方法有分散/聚集 DMA(videobuf-dma-sg)、 777*4882a593Smuzhiyun线性 DMA(videobuf-dma-contig)以及大多用于 USB 设备的用 vmalloc 778*4882a593Smuzhiyun分配的缓冲(videobuf-vmalloc)。 779*4882a593Smuzhiyun 780*4882a593Smuzhiyun请参阅 Documentation/driver-api/media/v4l2-videobuf.rst,以获得更多关于 videobuf 781*4882a593Smuzhiyun层的使用信息。 782*4882a593Smuzhiyun 783*4882a593Smuzhiyunv4l2_fh 结构体 784*4882a593Smuzhiyun------------- 785*4882a593Smuzhiyun 786*4882a593Smuzhiyunv4l2_fh 结构体提供一个保存用于 V4L2 框架的文件句柄特定数据的简单方法。 787*4882a593Smuzhiyun如果 video_device 标志,新驱动 788*4882a593Smuzhiyun必须使用 v4l2_fh 结构体,因为它也用于实现优先级处理(VIDIOC_G/S_PRIORITY)。 789*4882a593Smuzhiyun 790*4882a593Smuzhiyunv4l2_fh 的用户(位于 V4l2 框架中,并非驱动)可通过测试 791*4882a593Smuzhiyunvideo_device->flags 中的 V4L2_FL_USES_V4L2_FH 位得知驱动是否使用 792*4882a593Smuzhiyunv4l2_fh 作为他的 file->private_data 指针。这个位会在调用 v4l2_fh_init() 793*4882a593Smuzhiyun时被设置。 794*4882a593Smuzhiyun 795*4882a593Smuzhiyunv4l2_fh 结构体作为驱动自身文件句柄结构体的一部分被分配,且驱动在 796*4882a593Smuzhiyun其打开函数中将 file->private_data 指向它。 797*4882a593Smuzhiyun 798*4882a593Smuzhiyun在许多情况下,v4l2_fh 结构体会嵌入到一个更大的结构体中。这钟情况下, 799*4882a593Smuzhiyun应该在 open() 中调用 v4l2_fh_init+v4l2_fh_add,并在 release() 中 800*4882a593Smuzhiyun调用 v4l2_fh_del+v4l2_fh_exit。 801*4882a593Smuzhiyun 802*4882a593Smuzhiyun驱动可以通过使用 container_of 宏提取他们自己的文件句柄结构体。例如: 803*4882a593Smuzhiyun 804*4882a593Smuzhiyunstruct my_fh { 805*4882a593Smuzhiyun int blah; 806*4882a593Smuzhiyun struct v4l2_fh fh; 807*4882a593Smuzhiyun}; 808*4882a593Smuzhiyun 809*4882a593Smuzhiyun... 810*4882a593Smuzhiyun 811*4882a593Smuzhiyunint my_open(struct file *file) 812*4882a593Smuzhiyun{ 813*4882a593Smuzhiyun struct my_fh *my_fh; 814*4882a593Smuzhiyun struct video_device *vfd; 815*4882a593Smuzhiyun int ret; 816*4882a593Smuzhiyun 817*4882a593Smuzhiyun ... 818*4882a593Smuzhiyun 819*4882a593Smuzhiyun my_fh = kzalloc(sizeof(*my_fh), GFP_KERNEL); 820*4882a593Smuzhiyun 821*4882a593Smuzhiyun ... 822*4882a593Smuzhiyun 823*4882a593Smuzhiyun v4l2_fh_init(&my_fh->fh, vfd); 824*4882a593Smuzhiyun 825*4882a593Smuzhiyun ... 826*4882a593Smuzhiyun 827*4882a593Smuzhiyun file->private_data = &my_fh->fh; 828*4882a593Smuzhiyun v4l2_fh_add(&my_fh->fh); 829*4882a593Smuzhiyun return 0; 830*4882a593Smuzhiyun} 831*4882a593Smuzhiyun 832*4882a593Smuzhiyunint my_release(struct file *file) 833*4882a593Smuzhiyun{ 834*4882a593Smuzhiyun struct v4l2_fh *fh = file->private_data; 835*4882a593Smuzhiyun struct my_fh *my_fh = container_of(fh, struct my_fh, fh); 836*4882a593Smuzhiyun 837*4882a593Smuzhiyun ... 838*4882a593Smuzhiyun v4l2_fh_del(&my_fh->fh); 839*4882a593Smuzhiyun v4l2_fh_exit(&my_fh->fh); 840*4882a593Smuzhiyun kfree(my_fh); 841*4882a593Smuzhiyun return 0; 842*4882a593Smuzhiyun} 843*4882a593Smuzhiyun 844*4882a593Smuzhiyun以下是 v4l2_fh 函数使用的简介: 845*4882a593Smuzhiyun 846*4882a593Smuzhiyunvoid v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev) 847*4882a593Smuzhiyun 848*4882a593Smuzhiyun 初始化文件句柄。这*必须*在驱动的 v4l2_file_operations->open() 849*4882a593Smuzhiyun 函数中执行。 850*4882a593Smuzhiyun 851*4882a593Smuzhiyunvoid v4l2_fh_add(struct v4l2_fh *fh) 852*4882a593Smuzhiyun 853*4882a593Smuzhiyun 添加一个 v4l2_fh 到 video_device 文件句柄列表。一旦文件句柄 854*4882a593Smuzhiyun 初始化完成就必须调用。 855*4882a593Smuzhiyun 856*4882a593Smuzhiyunvoid v4l2_fh_del(struct v4l2_fh *fh) 857*4882a593Smuzhiyun 858*4882a593Smuzhiyun 从 video_device() 中解除文件句柄的关联。文件句柄的退出函数也 859*4882a593Smuzhiyun 将被调用。 860*4882a593Smuzhiyun 861*4882a593Smuzhiyunvoid v4l2_fh_exit(struct v4l2_fh *fh) 862*4882a593Smuzhiyun 863*4882a593Smuzhiyun 清理文件句柄。在清理完 v4l2_fh 后,相关内存会被释放。 864*4882a593Smuzhiyun 865*4882a593Smuzhiyun 866*4882a593Smuzhiyun如果 v4l2_fh 不是嵌入在其他结构体中的,则可以用这些辅助函数: 867*4882a593Smuzhiyun 868*4882a593Smuzhiyunint v4l2_fh_open(struct file *filp) 869*4882a593Smuzhiyun 870*4882a593Smuzhiyun 分配一个 v4l2_fh 结构体空间,初始化并将其添加到 file 结构体相关的 871*4882a593Smuzhiyun video_device 结构体中。 872*4882a593Smuzhiyun 873*4882a593Smuzhiyunint v4l2_fh_release(struct file *filp) 874*4882a593Smuzhiyun 875*4882a593Smuzhiyun 从 file 结构体相关的 video_device 结构体中删除 v4l2_fh ,清理 876*4882a593Smuzhiyun v4l2_fh 并释放空间。 877*4882a593Smuzhiyun 878*4882a593Smuzhiyun这两个函数可以插入到 v4l2_file_operation 的 open() 和 release() 879*4882a593Smuzhiyun操作中。 880*4882a593Smuzhiyun 881*4882a593Smuzhiyun 882*4882a593Smuzhiyun某些驱动需要在第一个文件句柄打开和最后一个文件句柄关闭的时候做些 883*4882a593Smuzhiyun工作。所以加入了两个辅助函数以检查 v4l2_fh 结构体是否是相关设备 884*4882a593Smuzhiyun节点打开的唯一文件句柄。 885*4882a593Smuzhiyun 886*4882a593Smuzhiyunint v4l2_fh_is_singular(struct v4l2_fh *fh) 887*4882a593Smuzhiyun 888*4882a593Smuzhiyun 如果此文件句柄是唯一打开的文件句柄,则返回 1 ,否则返回 0 。 889*4882a593Smuzhiyun 890*4882a593Smuzhiyunint v4l2_fh_is_singular_file(struct file *filp) 891*4882a593Smuzhiyun 892*4882a593Smuzhiyun 功能相同,但通过 filp->private_data 调用 v4l2_fh_is_singular。 893*4882a593Smuzhiyun 894*4882a593Smuzhiyun 895*4882a593SmuzhiyunV4L2 事件机制 896*4882a593Smuzhiyun----------- 897*4882a593Smuzhiyun 898*4882a593SmuzhiyunV4L2 事件机制提供了一个通用的方法将事件传递到用户空间。驱动必须使用 899*4882a593Smuzhiyunv4l2_fh 才能支持 V4L2 事件机制。 900*4882a593Smuzhiyun 901*4882a593Smuzhiyun 902*4882a593Smuzhiyun事件通过一个类型和选择 ID 来定义。ID 对应一个 V4L2 对象,例如 903*4882a593Smuzhiyun一个控制 ID。如果未使用,则 ID 为 0。 904*4882a593Smuzhiyun 905*4882a593Smuzhiyun当用户订阅一个事件,驱动会为此分配一些 kevent 结构体。所以每个 906*4882a593Smuzhiyun事件组(类型、ID)都会有自己的一套 kevent 结构体。这保证了如果 907*4882a593Smuzhiyun一个驱动短时间内产生了许多同类事件,不会覆盖其他类型的事件。 908*4882a593Smuzhiyun 909*4882a593Smuzhiyun但如果你收到的事件数量大于同类事件 kevent 的保存数量,则最早的 910*4882a593Smuzhiyun事件将被丢弃,并加入新事件。 911*4882a593Smuzhiyun 912*4882a593Smuzhiyun此外,v4l2_subscribed_event 结构体内部有可供驱动设置的 merge() 和 913*4882a593Smuzhiyunreplace() 回调,这些回调会在新事件产生且没有多余空间的时候被调用。 914*4882a593Smuzhiyunreplace() 回调让你可以将早期事件的净荷替换为新事件的净荷,将早期 915*4882a593Smuzhiyun净荷的相关数据合并到替换进来的新净荷中。当该类型的事件仅分配了一个 916*4882a593Smuzhiyunkevent 结构体时,它将被调用。merge() 回调让你可以合并最早的事件净荷 917*4882a593Smuzhiyun到在它之后的那个事件净荷中。当该类型的事件分配了两个或更多 kevent 918*4882a593Smuzhiyun结构体时,它将被调用。 919*4882a593Smuzhiyun 920*4882a593Smuzhiyun这种方法不会有状态信息丢失,只会导致中间步骤信息丢失。 921*4882a593Smuzhiyun 922*4882a593Smuzhiyun 923*4882a593Smuzhiyun关于 replace/merge 回调的一个不错的例子在 v4l2-event.c 中:用于 924*4882a593Smuzhiyun控制事件的 ctrls_replace() 和 ctrls_merge() 回调。 925*4882a593Smuzhiyun 926*4882a593Smuzhiyun注意:这些回调可以在中断上下文中调用,所以它们必须尽快完成并退出。 927*4882a593Smuzhiyun 928*4882a593Smuzhiyun有用的函数: 929*4882a593Smuzhiyun 930*4882a593Smuzhiyunvoid v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) 931*4882a593Smuzhiyun 932*4882a593Smuzhiyun 将事件加入视频设备的队列。驱动仅负责填充 type 和 data 域。 933*4882a593Smuzhiyun 其他域由 V4L2 填充。 934*4882a593Smuzhiyun 935*4882a593Smuzhiyunint v4l2_event_subscribe(struct v4l2_fh *fh, 936*4882a593Smuzhiyun struct v4l2_event_subscription *sub, unsigned elems, 937*4882a593Smuzhiyun const struct v4l2_subscribed_event_ops *ops) 938*4882a593Smuzhiyun 939*4882a593Smuzhiyun video_device->ioctl_ops->vidioc_subscribe_event 必须检测驱动能 940*4882a593Smuzhiyun 产生特定 id 的事件。然后调用 v4l2_event_subscribe() 来订阅该事件。 941*4882a593Smuzhiyun 942*4882a593Smuzhiyun elems 参数是该事件的队列大小。若为 0,V4L2 框架将会(根据事件类型) 943*4882a593Smuzhiyun 填充默认值。 944*4882a593Smuzhiyun 945*4882a593Smuzhiyun ops 参数允许驱动指定一系列回调: 946*4882a593Smuzhiyun * add: 当添加一个新监听者时调用(重复订阅同一个事件,此回调 947*4882a593Smuzhiyun 仅被执行一次)。 948*4882a593Smuzhiyun * del: 当一个监听者停止监听时调用。 949*4882a593Smuzhiyun * replace: 用‘新’事件替换‘早期‘事件。 950*4882a593Smuzhiyun * merge: 将‘早期‘事件合并到‘新’事件中。 951*4882a593Smuzhiyun 这四个调用都是可选的,如果不想指定任何回调,则 ops 可为 NULL。 952*4882a593Smuzhiyun 953*4882a593Smuzhiyunint v4l2_event_unsubscribe(struct v4l2_fh *fh, 954*4882a593Smuzhiyun struct v4l2_event_subscription *sub) 955*4882a593Smuzhiyun 956*4882a593Smuzhiyun v4l2_ioctl_ops 结构体中的 vidioc_unsubscribe_event 回调函数。 957*4882a593Smuzhiyun 驱动程序可以直接使用 v4l2_event_unsubscribe() 实现退订事件过程。 958*4882a593Smuzhiyun 959*4882a593Smuzhiyun 特殊的 V4L2_EVENT_ALL 类型,可用于退订所有事件。驱动可能在特殊 960*4882a593Smuzhiyun 情况下需要做此操作。 961*4882a593Smuzhiyun 962*4882a593Smuzhiyunint v4l2_event_pending(struct v4l2_fh *fh) 963*4882a593Smuzhiyun 964*4882a593Smuzhiyun 返回未决事件的数量。有助于实现轮询(poll)操作。 965*4882a593Smuzhiyun 966*4882a593Smuzhiyun事件通过 poll 系统调用传递到用户空间。驱动可用 967*4882a593Smuzhiyunv4l2_fh->wait (wait_queue_head_t 类型)作为参数调用 poll_wait()。 968*4882a593Smuzhiyun 969*4882a593Smuzhiyun事件分为标准事件和私有事件。新的标准事件必须使用可用的最小事件类型 970*4882a593Smuzhiyun编号。驱动必须从他们本类型的编号起始处分配事件。类型的编号起始为 971*4882a593SmuzhiyunV4L2_EVENT_PRIVATE_START + n * 1000 ,其中 n 为可用最小编号。每个 972*4882a593Smuzhiyun类型中的第一个事件类型编号是为以后的使用保留的,所以第一个可用事件 973*4882a593Smuzhiyun类型编号是‘class base + 1’。 974*4882a593Smuzhiyun 975*4882a593SmuzhiyunV4L2 事件机制的使用实例可以在 OMAP3 ISP 的驱动 976*4882a593Smuzhiyun(drivers/media/video/omap3isp)中找到。 977