V4L2驱动框架概述 ===================================== 这个文本文件讲述V4L2的框架所提供的各种结构和它们之间的关系. 介绍 ------------由于硬件的复杂性v412驱动往往是非常复杂的: 大多数设备有多个IC,在/dev目录下有多个设备节点, 并也创建non-V4L2的设备,如DVB,ALSA,FB, I2C和input(IR)设备。 特别是v412驱动设置配套的IC做音频/视频多路复用/编码/解码,使得它更 比大多数复杂的事实。通常这些芯片连接到主桥驱动器通过一个或多个I2C 总线,但也可以使用其他总线。这种设备被称为“子设备”。 很长一段时间有限制的video_device结构框架创建v4l设备节点和video_buf 的的视频缓冲区处理(请注意,本文档不讨论video_buf框架)。 这意味着,所有驱动程序必须做的设备实例的设置和连接子设备本身。 这部分是相当复杂,做应该做的事,很多驱动程序不这样做是正确的。
由于缺乏一个框架也有很多共同的代码不可重构。 因此,这个框架的基本构建块,所有的驱动程序需要与此相同的框架应更容 易进入所有驱动程序共享的实用功能重构的通用代码。 驱动程序的结构 --------------------- 所有的驱动程序有以下结构: 1) 每个设备包含设备状态的实例结构。 2) 子设备的初始化和命令方式(如果有). 3) 创建V4L2的设备节点 (/dev/videoX, /dev/vbiX and /dev/radioX) 和跟踪设备节点的具体数据。 4)文件句柄特定的结构,包含每个文件句柄数据; 5) 视频缓冲处理。device instances(设备实例)这是一个粗略的示意图,这一切是如何涉及的:
|
+-sub-device instances(子设备实例)
|
\-V4L2 device nodes(V4L2的设备节点)
|
\-filehandle instances(文件句柄实例)
框架结构
--------------------------
框架类似于驱动程序的结构:它是一个 v4l2_device结构的设备实例的数据,
v4l2_subdev结构子设备实例的video_device结构存储V4L2的设备节点的数据,
并在将来v4l2_fh结构将保持跟踪的文件句柄实例(这是尚未实现)。也可以选择集成框架的V4L2媒体框架。如果设置一个驱动程序的结构
v4l2_device mdev,sub-devices 和 video节点会自动出现在媒体框架作为实体。
struct v4l2_device
------------------
每个设备实例结构体v4l2_device(V4L2-device.h中)的代表。只是很简单的
设备可以分配这个结构,但大多数的时候你会把这个结构体嵌入到一个更大的结构体中。你必须注册设备的实例:v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); 注册将初始化 v4l2_device 结构体. 如果 dev->driver_data字段是空, 它将连接到 v4l2_dev. 要与媒体设备框架集成的驱动程序需要设置DEV-> driver_data手动指向驱动程序特定的设备结构 嵌入结构体v4l2_device实例。这是通过一个dev_set_drvdata()调用之前注册V4L2的设备实例。 他们必须还设置结构体v4l2_device MDEV领域指向一个正确初始化和注册media_device实例。 如果v4l2_dev->name是空,那么这将被设置为从dev取得一个值(驱动程序名称后的 bus_id要准确)。 如果你在调用v4l2_device_register之前设置它,那么它没有改变。 如果dev是NULL,那么你‘必须’设置v4l2_dev>name在调用v4l2_device_register前。你可以使用v4l2_device_set_name()设置名称根据驱动程序的名字和
driver-global atomic_t实例。这将产生的名字一样,ivtv0,ivtv1,等。
如果名字最后一个数字,然后将它插入一个破折号:cx18-0,cx18-1,等
这个函数返回的实例数量。 第一个参数‘dev’通常是一个pci_dev的struct device的指针,
但它是ISA设备或一个设备创建多个PCI设备时这是罕见的DEV为NULL, 因此makingit不可能联想到一个特定的父母v4l2_dev。 您也可以提供一个notify()回调子设备,可以通过调用通知你的事件。 取决于你是否需要设置子设备。一个子设备支持的任何通知必须在头文件中定义include/media/<subdevice>.h. 注销: v4l2_device_unregister(struct v4l2_device *v4l2_dev); 如果dev-> driver_data字段指向v4l2_dev,它将被重置为NULL。 注销也将自动注销从设备所有子设备。 如果你有一个可热插拔设备(如USB设备),然后发生断开连接的时候父设备将变为无效。 由于v4l2_device有指向父设备的指针,它被清除,以及标记父设备消失。 要做到这一点调用: v4l2_device_disconnect(struct v4l2_device *v4l2_dev); 这并*不*注销subdevs,所以你仍然需要调用该的v4l2_device_unregister()函数。 如果你的驱动是不能热插拔,则有无需调用v4l2_device_disconnect()。 有时你需要遍历一个特定的驱动程序注册的所有设备。这是通常情况下如果多个设备驱动程序使用相同的硬件。
例如ivtvfb驱动程序是一个使用IVTV硬件framebuffer驱动。
同样是真实的,例如ALSA驱动程序。您可以遍历所有注册的设备如下:
static int callback(struct device *dev, void *p)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
/* test if this device was inited */
if (v4l2_dev == NULL)
return 0;
...
return 0;
}
int iterate(void *p)
{
struct device_driver *drv;
int err;
/* Find driver 'ivtv' on the PCI bus.
pci_bus_type is a global. For USB busses use usb_bus_type. */
drv = driver_find("ivtv", &pci_bus_type);
/* iterate over all ivtv device instances */
err = driver_for_each_device(drv, NULL, p, callback);
put_driver(drv);
return err;
}有时你需要保持一个设备实例运行计数器。这是常用的映射设备实例模块选项数组索引。
建议的方法如下:
static atomic_t drv_instance = ATOMIC_INIT(0);
static int __devinit drv_probe(struct pci_dev *pdev,
const struct pci_device_id *pci_id)
{
...
state->instance = atomic_inc_return(&drv_instance) - 1;
}如果你有多个设备节点然后它可以是很难知道它是安全注销v4l2_device时候。
v4l2_device引用计数的支持就是对于这样做的目的。当video_register_device 时被称之为增加引用计数,有设备节点被释放时减少引用计数。 当引用计数达到零,则v4l2_device回调release()。你可以做最后的清理。如果其他设备节点(例如ALSA)的创建,然后你可以增加和减少以及手动调用引用计数:
void v4l2_device_get(struct v4l2_device *v4l2_dev);
or:
int v4l2_device_put(struct v4l2_device *v4l2_dev);
struct v4l2_subdev
------------------
许多驱动程序需要与子设备进行通信。这些设备可以完成所有这类任务
但他们最常用的处理音频 和/或 视频混流,编码或解码。对于常见子设备摄像头传感器和摄像头控制器。
通常情况下,这些I2C器件,但不一定。以这些子设备的驱动程序提供一致的接口v4l2_subdev结构
(V4L2 subdev.h)创建。
每个子设备驱动程序必须有一个v4l2_subdev结构。这个结构可以独立简单的设备或者
如果需要存储更多的状态信息它可能被嵌入在一个更大的结构。一般有一个低级别 设备结构(如i2c_client),其中包含设备的数据为内核设置。它建议在v4l2_subdev使用v4l2_set_subdevdata()的私人数据存储的指针。
这使得很容易地从一个v4l2_subdev实际低级别的总线特定的器件数据。
你还需要很长的路要走从低级结构v4l2_subdev。对于常见的i2c_client结构的i2c_set_clientdata()调用用来存储v4l2_subdev指针,
为其他总线您可能必须使用其他方法。桥接可能还需要存储每subdev私人数据如桥特定每个-subdev私人数据指针。
v4l2_subdev结构提供与v4l2_get_subdev_hostdata()和v4l2_set_subdev_hostdata()可以访问目的主机私人数据。
从桥式驱动器角度你加载的子设备模块和某种取得的v4l2_subdev指针。
对于I2C器件这是很容易:
你调用i2c_get_clientdata()。对于其他总线类似的东西需要做。Helper函数做你最棘手的工作,
这一个I2C总线上存在的子设备。每个v4l2_subdev包含函数指针的子设备驱动程序可以实现(或保留NULL,如果它不适用)。
由于子设备可以做很多不同的东西,你不想结束一个巨大的OPS结构其中只有少数的OPS通常执行,函数指针进行排序按类别,
每个类别都有其自己的OPS结构。顶层OPS结构包含的类别OPS结构,这可能是NULL如果在subdev驱动程序不支持任何从该类别指针。
它看起来像这样:
struct v4l2_subdev_core_ops {
int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
int (*log_status)(struct v4l2_subdev *sd);
int (*init)(struct v4l2_subdev *sd, u32 val);
...
};
struct v4l2_subdev_tuner_ops {
...
};
struct v4l2_subdev_audio_ops {
...
};
struct v4l2_subdev_video_ops {
...
};
struct v4l2_subdev_ops {
const struct v4l2_subdev_core_ops *core;
const struct v4l2_subdev_tuner_ops *tuner;
const struct v4l2_subdev_audio_ops *audio;
const struct v4l2_subdev_video_ops *video;
};
是共同所有子设备的核心操作函数,其他类别的执行取决于子设备。例如视频设备是不大可能支持音频操作,反之亦然。 此设置限制的函数指针,同时还使其易于添加新操作函数和类别。 子设备驱动初始化的v4l2_subdev结构使用: v4l2_subdev_init(sd, &ops); 之后,您需要初始化一个独特的名字subdev->name和设置模块所有者。这样做是为了如果你使用的I2C辅助功能。 如果框架媒介整合是必要的,你必须初始化的media_entity结构通过调用media_entity_init()嵌入在v4l2_subdev结构(实体领域): struct media_pad *pads = &my_sd->pads; int err; err = media_entity_init(&sd->entity, npads, pads, 0); pads数组必须此前已初始化。有没有需要手动设置结构media_entity类型和name字段,但如果需要修改字段必须初始化。不要忘了清理的media实体子设备被破坏之前:参考实体将自动获得/释放时,subdev设备节点(如果有的话)打开/关闭。
media_entity_cleanup(&sd->entity);
设备(桥)驱动程序需要注册用v4l2_subdevv4l2_device:
int err = v4l2_device_register_subdev(v4l2_dev, sd);
这可能会失败,如果subdev模块消失之前,它可以注册。
之后,这个功能调用成功的subdev-> dev字段指向了v4l2_device。 如果的v4l2_device父设备具有非空MDEV字段,
分设备实体将被自动注册用media设备。 你可以注销一个子设备使用:
v4l2_device_unregister_subdev(sd);
事后subdev模块可以卸载和 sd->dev == NULL. 你可以直接调用操作函数:
err = sd->ops->core->g_chip_ident(sd, &chip);但但是使用这个宏将更好和更容易: err = v4l2_subdev_call(sd, core, g_chip_ident, &chip); 宏将到右边的空指针检查,并如果subdev是NULL返回-ENODEV, -ENOIOCTLCMD if either subdev->core or subdev->core->g_chip_ident is NULL, 或实际的结果 subdev->ops->core->g_chip_ident ops. 它也可以调用全部或部分子设备: v4l2_device_call_all(v4l2_dev, 0, core, g_chip_ident, &chip); 不支持这个OPS的任何子设备跳过错误的效果将被忽略。如果你想检查是否有错误,使用这个: err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_chip_ident, &chip); Any error except -ENOIOCTLCMD will exit the loop with that error. If no errors (except -ENOIOCTLCMD) occurred, then 0 is returned. 这两个调用的第二个参数是一组ID。如果为0,然后所有subdevs被调用。如果不为零,那么只有那些其组ID匹配值将被调用。前桥驱动注册一个subdev它可以设置SD-> grp_id
任何值是想要(这是默认为0)。这个值是由桥式驱动器和拥有子设备驱动程序将不会修改或使用它。
这组ID给桥驱动器控制如何调用回调。例如,可能有多个音频芯片板,每一个有改变音量。但通常只
有一个实际上将被用来当用户想改变音量。
你可以设置组ID,subdev例如AUDIO_CONTROLLER和指定组ID值时调用v4l2_device_call_all()。
以确保它只会去的subdev需要。如果子设备需要通知其事件v4l2_device父母,那么它可以调用
v4l2_subdev_notify(SD,notification,ARG)。
这个宏检查是否有一个notify()的回调定义,如果没有返回-ENODEV。否则结果的通知()调用返回。
使用v4l2_subdev优势是它是一个通用结构,不包含任何对底层硬件知识。因此驱动程序可能包含几个subdevs使用I2C总线,
但也通过GPIO引脚控制subdev。这种区别是设置设备时只有关,但一旦subdev注册是是完全透明的。
V4L2的子设备用户空间的API
-----------------------------
旁边公开通过v4l2_subdev_ops结构内核API,V4L2的子设备也可以直接控制用户空间应用。可以直接访问
子设备在/ dev中创建名为V4L-subdevX设备节点。
如果一个子设备支持直接用户空间的配置它必须设定以前注册的V4L2_SUBDEV_FL_HAS_DEVNODE标志。
子设备登记后,v4l2_device司机可以由调用v4l2_device_register_subdev_nodes()与
V4L2_SUBDEV_FL_HAS_DEVNODE标记所有注册子设备创建设备节点。子设备未注册时将被自动删除这些
设备节点。设备节点处理的V4L2的API一个子集。
VIDIOC_QUERYCTRL
VIDIOC_QUERYMENU
VIDIOC_G_CTRL
VIDIOC_S_CTRL
VIDIOC_G_EXT_CTRLS
VIDIOC_S_EXT_CTRLS
VIDIOC_TRY_EXT_CTRLS控制ioctls函数调用在V4L2的定义是相同的。他们的行为是相同的,唯一的例外,
他们只处理子设备执行控制。这些控制可以根据驱动程序,也可以通过一个(或几个)V4L2
的设备节点访问。
VIDIOC_DQEVENT
VIDIOC_SUBSCRIBE_EVENT
VIDIOC_UNSUBSCRIBE_EVENT
事件ioctls函数调用在V4L2的定义是相同。他们的行为是相同的,唯一的例外他们只分设备产生事件处理。
根据驱动程序,这些事件也可以报一个(或几个)V4L2的设备节点。
子设备驱动程序要使用的事件需要设置的V4L2_SUBDEV_USES_EVENTS v4l2_subdev::flags
和初始化之前注册的子设备v4l2_subdev:: nevents事件队列深度。注册事件后可以像往常一样
排队的v4l2_subdev:: devnode设备节点。
要正确地支持事件,poll()的文件操作实施。Private ioctls没有在上述列表所有ioctls函数调用会直接传递
到子设备驱动程序通过core::ioctl操作。
I2C sub-device drivers
----------------------
由于这些驱动程序是很常见的,特殊的辅助功能是可以缓解使用这些驱动程序(V4L2-common.h)。
对加入v4l2_subdev支持I2C驱动的推荐的方法是嵌入到每个I2C设备实例创建状态结构v4l2_subdev结构。
很简单的设备,没有状态结构,在这种情况下,你可以直接创建一个v4l2_subdev。
一个典型的状态结构看起来像这样(where 'chipname' is replaced bythe name of the chip):
此功能将填补所有领域v4l2_subdev和确保在v4l2_subdev和i2c_client都指向一个。struct chipname_state { struct v4l2_subdev sd; ... /* additional state fields */ };如下初始化v4l2_subdev结构: v4l2_i2c_subdev_init(&state->sd, client, subdev_ops);
您还应该添加一个辅助内联函数从v4l2_subdev指针的去到chipname_state结构:
struct chipname_state {
struct v4l2_subdev sd;
... /* additional state fields */
};
如下初始化v4l2_subdev结构:
v4l2_i2c_subdev_init(&state->sd, client, subdev_ops);
此功能将填补所有领域v4l2_subdev和保证的v4l2_subdev和i2c_client都指向一个。
您还应该添加一个辅助inline函数从v4l2_subdev指针的去到chipname_state结构:
static inline struct chipname_state *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct chipname_state, sd);
}
使用这个从v4l2_subdev结构i2c_client结构:
struct i2c_client *client = v4l2_get_subdevdata(sd);
这从一个i2c_client到v4l2_subdev结构:
struct v4l2_subdev *sd = i2c_get_clientdata(client);
确保调用v4l2_device_unregister_subdev(SD),remove()回调时被调用。
这将注销桥式驱动器的子设备。调用此子设备,即使是没有注册的,它是安全的。
你需要这样做,因为当桥式驱动器的销毁I2C适配器的remove()回调呼吁该适配器
上的I2C设备。之后相应v4l2_subdev结构是无效的,所以他们必须首先要注销。
v4l2_device_unregister_subdev(SD)的remove()调用的回调保证,这是一直在做正确。
桥式驱动器也有一些辅助功能,它可以使用:
struct v4l2_subdev *sd = v4l2_i2c_new_subdev(v4l2_dev, adapter,
"module_foo", "chipid", 0x36, NULL);
加载模块(如果没有需要加载的模块,可以为NULL)并调用i2c_adapter和
芯片/地址参数的i2c_new_device()。如果一切顺利,那么它注册这个v4l2_device 子设备。
您还可以使用最后一个参数的v4l2_i2c_new_subdev()传递一个可能的I2C地址的数组,
它应该探究。这些探头地址仅用于如果前面的参数是0。一个非零的说法意味着,
你知道确切的I2C地址,所以在这种情况下,没有探测将发生。
如果出错了,这两个函数返回NULL。
请注意,你传递 CHIPID给v4l2_i2c_new_subdev()通常是作为相同模块的名称。它允许你指定一个芯片的变型,例如
“SAA7114”或“SAA7115”。一般来说,虽然I2C驱动程序会自动检测这个。
使用CHIPID是需要看着在晚些时候更紧密的东西。它不同于之间的I2C驱动程序和这样可能会造成混淆。
看到支持该芯片的变种,你可以在I2C驱动程序代码为i2c_device_id表。这将列出所有的可能性。
有两个辅助函数:
v4l2_i2c_new_subdev_cfg: this function adds new irq and platform_data
arguments and has both 'addr' and 'probed_addrs' arguments: if addr is not
0 then that will be used (non-probing variant), otherwise the probed_addrs
are probed.
For example: this will probe for address 0x10:
struct v4l2_subdev *sd = v4l2_i2c_new_subdev_cfg(v4l2_dev, adapter,
"module_foo", "chipid", 0, NULL, 0, I2C_ADDRS(0x10));
v4l2_i2c_new_subdev_board uses an i2c_board_info struct which is passed
to the i2c driver and replaces the irq, platform_data and addr arguments.
If the subdev supports the s_config core ops, then that op is called with
the irq and platform_data arguments after the subdev was setup. The older
v4l2_i2c_new_(probed_)subdev functions will call s_config as well, but with
irq set to 0 and platform_data set to NULL.
struct video_device
-------------------
实际设备节点在/ dev目录使用的video_device结构(V4L2 dev.h)创建。
这个结构可以动态分配或嵌入在一个更大的结构。要动态分配使用:
To allocate it dynamically use:
struct video_device *vdev = video_device_alloc();
if (vdev == NULL)
return -ENOMEM;
vdev->release = video_device_release;
如果是嵌入到一个更大的结构,那么你必须设置的release()函数回调到自己的函数:
struct video_device *vdev = &my_vdev->vdev;
vdev->release = my_vdev_release;释放回调必须设置时被称为最后一个用户的视频设备出口。的默认video_device_release()
回调只是调用kfree释放分配的内存。
你也应该设定这些字段:
- v4l2_dev: 设置这个v4l2_device父设备。
- name: 设置为描述性和独特的东西。
- fops: 设置这个v4l2_file_operations结构。
- ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance
(highly recommended to use this and it might become compulsory in the
future!), then set this to your v4l2_ioctl_ops struct.
- lock: leave to NULL if you want to do all the locking in the driver.
Otherwise you give it a pointer to a struct mutex_lock and before any
of the v4l2_file_operations is called this lock will be taken by the
core and released afterwards.
- prio: keeps track of the priorities. Used to implement VIDIOC_G/S_PRIORITY.
If left to NULL, then it will use the struct v4l2_prio_state in v4l2_device.
If you want to have a separate priority state per (group of) device node(s),
then you can point it to your own struct v4l2_prio_state.
- parent: you only set this if v4l2_device was registered with NULL as
the parent device struct. This only happens in cases where one hardware
device has multiple PCI devices that all share the same v4l2_device core.
The cx88 driver is an example of this: one core v4l2_device struct, but
it is used by both an raw video PCI device (cx8800) and a MPEG PCI device
(cx8802). Since the v4l2_device cannot be associated with a particular
PCI device it is setup without a parent device. But when the struct
video_device is setup you do know which parent PCI device to use.
- flags: optional. Set to V4L2_FL_USE_FH_PRIO if you want to let the framework
handle the VIDIOC_G/S_PRIORITY ioctls. This requires that you use struct
v4l2_fh. Eventually this flag will disappear once all drivers use the core
priority handling. But for now it has to be set explicitly.
如果使用v4l2_ioctl_ops,那么你应该设置。unlocked_ioctl video_ioctl2在v4l2_file_operations结构。
不要使用。IOCTL!将在未来过时。
v4l2_file_operations结构的file_operations的一个子集。主要区别在于该inode参数被忽略,因为它从来没有使用过。如果框架传媒的整合是必要的,你必须初始化media_entity结构嵌入式通过调用media_entity_init()的 video_device结构(entity字段): struct media_pad *pad = &my_vdev->pad; int err; err = media_entity_init(&vdev->entity, 1, pad, 0); 垫阵列必须先前已初始化。有没有需要手动设置的结构media_entity type和name字段。 A reference to the entity will be automatically acquired/released when the video device is opened/closed.
v4l2_file_operations 和 锁
--------------------------------
你可以设置一个指针,在结构的video_device中mutex_lock。
通常这将是一个顶层互斥或互斥每个设备节点。如果你想更细致的锁那么你必须将其设置为NULL和你自己的锁定。
如果锁被指定那么所有的文件操作将被序列化该锁。如果您使用videobuf那么你
必须通过相同的锁的videobuf队列初始化函数:如果videobuf等待一帧到达,
然后它会暂时解除锁定和重新锁定它之后。如果您的驱动程序代码中的等待,
那么你应该做同样允许其他进程访问设备节点的第一道工序,而在等待着什么。
一个热插拔断开实施也应采取之前调用v4l2_device_disconnect锁。
video_device 注册
-------------------------
err = video_register_device(vdev, VFL_TYPE_GRABBER, -1);接下来,您注册的视频设备:这会为您创建的字符设备
if (err) {
video_device_release(vdev); /* or kfree(my_vdev); */
return err;
}
如果的v4l2_device其父节点设备具有非空MDEV字段,视频设备的实体将被自动注册用media设 备。
注册哪个设备依赖于类型参数。存在以下几种类型:
VFL_TYPE_GRABBER: 对视频输入/输出设备video
XVFL_TYPE_VBI: vbiX垂直空白数据(即关闭字幕,图文电视)
VFL_TYPE_RADIO: radioX用于无线电调谐器VFL_TYPE_VTX: vtxX图文电视设备(不建议使用,不使用)
最后一个参数给你一定数量的控制了设备使用设备节点(即在videoXX)。通常情况下你将通过-1让V4L2框架选择第一个释放号码。
但有时用户想选择一个特定的节点数量。这是常见的驱动程序允许用户通过驱动模块选项选择一个特定的设备节点数量。
这一数字然后被传递给这个函数和video_register_device将尝试选择该设备节点数量。
如果该号码已在使用中然后,下一个空闲的设备节点数量将被选中,它会发出警告的内核日志。另使用情况是,如果一个驱动程序创建许多设备。在这种情况下它可以是有用的放置在不同范围不同视频设备。
例如,视频捕获设备开始为0,视频输出设备在16开始。
所以,你可以使用的最后一个参数指定一个最低设备节点数量和V4L2框架将尽量挑选的第释放号码是相同或更高你通过什么。
如果失败,那么它只会选择第一个释放号码。由于在这种情况下,你不关心不能够选择指定的设备节点数量有关警告,
可以调用的函数video_register_device_no_warn()代替。每当创建一个设备节点的一些属性也为您创建。
如果你看在/ sys/class/video4linux你看到的设备。去到如video0,
你会看到“name”和“index”属性。 'name'属性的'name'字段的video_device结构。“index”属性是设备节点索引:
每个调用video_register_device()指数只上升1。寄存器总是第一个视频设备节点索引0开始。用户可以设置udev的规则,
利用索引属性使花哨的设备名称(如“mpegX MPEG视频捕捉设备节点)。之后该设备已成功注册,那么你可以使用这些字段:
- vfl_type: 设备类型传递到video_register_device。
- minor: 分配装置的次设备号。
- num: 设备节点数目(i.e. the X in videoX).
- index: 设备索引号。 如果注册失败,那么你需要调用video_device_release()来释放分配的video_device结构,或自己结构的video_device如果被嵌入。的VDEV>发布()回调将永远不会被调用,
如果注册失败,也不应该你曾经尝试注销的设备如果注册失败。
video_device 清除
--------------------
当被删除,在卸载的驱动程序,或因为被断开USB设备,视频设备节点,那么你应该注销他们:
video_unregister_device(vdev);
This will remove the device nodes from sysfs (causing udev to remove them
from /dev).
After video_unregister_device() returns no new opens can be done. However,
in the case of USB devices some application might still have one of these
device nodes open. So after the unregister all file operations (except
release, of course) will return an error as well.
当最后一个用户的视频设备节点退出,然后这个VDEV - >release()
回调被调用,你可以做最后的清理。
不要忘了清理媒体视频设备的实体相关的,如果它已经被初始化:
media_entity_cleanup(&vdev->entity);
This can be done from the release callback.
video_device 辅助函数
-----------------------------
有一些有用的辅助功能:
- file/video_device private data
You can set/get driver private data in the video_device struct using:
void *video_get_drvdata(struct video_device *vdev);
void video_set_drvdata(struct video_device *vdev, void *data);
Note that you can safely call video_set_drvdata() before calling
video_register_device().
And this function:
struct video_device *video_devdata(struct file *file);
returns the video_device belonging to the file struct.
The video_drvdata function combines video_get_drvdata with video_devdata:
void *video_drvdata(struct file *file);
You can go from a video_device struct to the v4l2_device struct using:
struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
- Device node name
The video_device node kernel name can be retrieved using
const char *video_device_node_name(struct video_device *vdev);
userspace工具,如udev的名字被用来作为一个提示。应在可能的情况下使用,
而不是访问的video_device:: num和的video_device::minor字段的功能。
video buffer 辅助函数
-----------------------------
The v4l2 core API provides a set of standard methods (called "videobuf")
for dealing with video buffers. Those methods allow a driver to implement
read(), mmap() and overlay() in a consistent way. There are currently
methods for using video buffers on devices that supports DMA with
scatter/gather method (videobuf-dma-sg), DMA with linear access
(videobuf-dma-contig), and vmalloced buffers, mostly used on USB drivers
(videobuf-vmalloc).
Please see Documentation/video4linux/videobuf for more information on how
to use the videobuf layer.
struct v4l2_fh
--------------
结构v4l2_fh提供了一种轻松地保持文件句柄是由V4L2的框架使用具体数据。
新的驱动程序必须使用struct v4l2_fh,因为它也可以用来实现优先处理
(VIDIOC_G/ S_PRIORITY)如果的video_device标志V4L2_FL_USE_FH_PRIO也。
(在V4L2的框架,而不是驱动器)v4l2_fh用户知道驱动程序是否使用通过测试
V4L2_FL_USES_V4L2_FH位的video_device-> flags中v4l2_fh作为其文件> private_data
的指针。每当v4l2_fh_init()被调用时设置此位。
struct v4l2_fh is allocated as a part of the driver's own file handle
structure and file->private_data is set to it in the driver's open
function by the driver.
In many cases the struct v4l2_fh will be embedded in a larger structure.
In that case you should call v4l2_fh_init+v4l2_fh_add in open() and
v4l2_fh_del+v4l2_fh_exit in release().
Drivers can extract their own file handle structure by using the container_of
macro. Example:
struct my_fh {
int blah;
struct v4l2_fh fh;
};
...
int my_open(struct file *file)
{
struct my_fh *my_fh;
struct video_device *vfd;
int ret;
...
my_fh = kzalloc(sizeof(*my_fh), GFP_KERNEL);
...
ret = v4l2_fh_init(&my_fh->fh, vfd);
if (ret) {
kfree(my_fh);
return ret;
}
...
file->private_data = &my_fh->fh;
v4l2_fh_add(&my_fh->fh);
return 0;
}
int my_release(struct file *file)
{
struct v4l2_fh *fh = file->private_data;
struct my_fh *my_fh = container_of(fh, struct my_fh, fh);
...
v4l2_fh_del(&my_fh->fh);
v4l2_fh_exit(&my_fh->fh);
kfree(my_fh);
return 0;
}
下面是一个使用的v4l2_fh功能的简短说明:
int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
Initialise the file handle. This *MUST* be performed in the driver's
v4l2_file_operations->open() handler.
void v4l2_fh_add(struct v4l2_fh *fh)
添加v4l2_fh的video_device文件句柄列表。必须调用一次完全初始化文件句柄。
void v4l2_fh_del(struct v4l2_fh *fh)
Unassociate the file handle from video_device(). The file handle
exit function may now be called.
void v4l2_fh_exit(struct v4l2_fh *fh)
Uninitialise the file handle. After uninitialisation the v4l2_fh
memory can be freed.
多个驱动程序需要做一些事情时,第一个文件句柄打开和关闭最后一个文件句柄时。
增加了两个辅助功能检查,是否v4l2_fh结构是唯一的打开文件句柄相关联的设备节点:
int v4l2_fh_is_singular(struct v4l2_fh *fh)
Returns 1 if the file handle is the only open file handle, else 0.
int v4l2_fh_is_singular_file(struct file *filp)
Same, but it calls v4l2_fh_is_singular with filp->private_data.
V4L2 events
-----------
V4L2的事件提供了一个通用的方式来传递事件到用户空间。
必须使用v4l2_fh的驱动程序能够支持V4L2的事件。Useful functions:
- v4l2_event_alloc()
使用事件驱动程序必须分配的文件句柄的事件。驱动程序通过调用函数不止一次,
可以保证至少n个总事件已分配。功能可能无法在原子上下文中被调用。
- v4l2_event_queue()
视频设备的队列中的事件。驱动程序的唯一责任是填写的类型和数据字段。V4L2的其他领域将被填充。
- v4l2_event_subscribe()
的video_device-> ioctl_ops> vidioc_subscribe_event必须检查驱动程序是否能
够产生与特定事件ID的事件。然后,它调用v4l2_event_subscribe()订阅该事件。
- v4l2_event_unsubscribe()
在结构v4l2_ioctl_ops vidioc_unsubscribe_event。一个驱动程序可以直接使用
v4l2_event_unsubscribe(),除非它想在取消订阅过程中涉及。
在特殊类型V4L2_EVENT_ALL的可能被用来取消所有活动。
驱动程序可能需要一种特殊的方式来处理。- v4l2_event_pending() 返回挂起的事件数量。实施调查时非常有用。Events are delivered to user space through the poll system call. The driver
can use v4l2_fh->events->wait wait_queue_head_t as the argument for
poll_wait().
There are standard and private events. New standard events must use the
smallest available event type. The drivers must allocate their events from
their own class starting from class base. Class base is
V4L2_EVENT_PRIVATE_START + n * 1000 where n is the lowest available number.
The first event type in the class is reserved for future use, so the first
available event type is 'class base + 1'.
An example on how the V4L2 events may be used can be found in the OMAP
3 ISP driver available at <URL:http://gitorious.org/omap3camera> as of
writing this.
V4L2-framework.txt 源文档在/Documentation/video4linux目录下. 内核自带的 Documentation目录是一个非常有用的参考资料和学习资料。 建议多读!!!