linux内核文档翻译之——V4L2-framework.txt


V4L2驱动框架概述
=====================================

这个文本文件讲述V4L2的框架所提供的各种结构它们之间的关系.


介绍
------------

由于硬件复杂v412驱动往往是非常复杂的 大多数设备多个IC,在
/dev目录下有多个设备节点, 创建non-V4L2的设备DVBALSA,FB
I2Cinput(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 mdevsub-devices 和 video节点会自动出现在媒体框架作为实体

struct v4l2_device
------------------
每个
设备实例结构体v4l2_deviceV4L2-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实例这将产生的名字一样,ivtv0ivtv1等。

如果名字最后一个数字然后将它插入一个破折号cx18-0cx18-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字段,但如果需要修改字段必须初始化

参考实体将自动获得/释放时,subdev设备节点如果有的话)打开/关闭

不要忘了清理media实体设备被破坏之前
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桥驱动器控制如何调用回调例如可能有多个音频芯片每一个改变音量但通常

有一个实际上将被用来当用户改变音量


可以设置组IDsubdev例如AUDIO_CONTROLLER指定组ID调用v4l2_device_call_all()

以确保它只会subdev需要如果子设备需要通知事件v4l2_device父母那么它可以调用

v4l2_subdev_notifySD,notification,ARG)


这个宏检查是否有一个notify()的回调定义如果没有返回-ENODEV否则结果通知(调用返回

使用v4l2_subdev优势是它是一个通用结构不包含任何底层硬件知识因此驱动程序可能包含几个subdevs使用I2C总线

通过GPIO引脚控制subdev这种区别是设置设备有关一旦subdev注册是完全透明的


V4L2的设备用户空间的API

-----------------------------

旁边公开通过v4l2_subdev_ops结构内核APIV4L2的子设备也可以直接控制用户空间应用可以直接访问

子设备在/ 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):

struct chipname_state {
	struct v4l2_subdev sd;
	...  /* additional state fields */
};

如下初始化v4l2_subdev结构
	
        v4l2_i2c_subdev_init(&state->sd, client, subdev_ops);

此功能将填补所有领域v4l2_subdev确保v4l2_subdevi2c_client都指向一个

您还应该添加一个辅助内联函数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_subdevi2c_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_clientv4l2_subdev结构

    struct v4l2_subdev *sd = i2c_get_clientdata(client);

确保调用v4l2_device_unregister_subdev(SD)remove()回调被调用
这将
注销桥式驱动器子设备调用此设备即使没有注册是安全的

需要这样做因为当桥式驱动器销毁I2C适配器的remove()回调呼吁该适配器
I2C设备之后相应v4l2_subdev结构是无效的,所以他们必须首先要注销
 v4l2_device_unregister_subdevSD的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

请注意,传递 CHIPIDv4l2_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_ioctl2v4l2_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 typename字段

A reference to the entity will be automatically acquired/released when the
video device is opened/closed.


v4l2_file_operations 和

--------------------------------

可以设置一个指针,结构的video_devicemutex_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
X
VFL_TYPE_VBI: vbiX垂直空白数据(即关闭字幕,图文电视
VFL_TYPE_RADIO: radioX用于无线电调谐器

VFL_TYPE_VTX: vtxX图文电视设备(不建议使用不使用

最后一个参数给你一定数量控制设备使用设备节点(即videoXX通常情况下通过-1V4L2框架选择第一个释放号码
但有时
用户想选择一个特定的节点数量这是常见的驱动程序允许用户通过驱动模块选项选择一个特定的设备节点数量
这一数字然后被传递给这个函数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目录是一个非常有用的参考资料和学习资料。
建议多读!!!



展开阅读全文

没有更多推荐了,返回首页