2.5. V4L2 设备实例

每个设备实例由 struct v4l2_device 表示。非常简单的设备可以直接分配此结构,但大多数情况下,您会将此结构嵌入到更大的结构中。

您必须通过调用以下函数来注册设备实例:

注册将初始化 v4l2_device 结构。如果 dev->driver_data 字段为 NULL,它将被链接到 v4l2_dev 参数。

想要与媒体设备框架集成的驱动程序需要手动设置 dev->driver_data 以指向嵌入 struct v4l2_device 实例的驱动程序特定设备结构。这是通过在注册 V4L2 设备实例之前调用 dev_set_drvdata() 实现的。它们还必须设置 struct v4l2_device mdev 字段以指向已正确初始化和注册的 media_device 实例。

如果 v4l2_dev->name 为空,则它将被设置为从 dev 派生的值(驱动程序名称后跟 bus_id,更准确地说)。如果您在调用 v4l2_device_register() 之前设置它,则它将保持不变。如果 dev 为 NULL,则您必须在调用 v4l2_device_register() 之前设置 v4l2_dev->name。

您可以使用 v4l2_device_set_name() 根据驱动程序名称和驱动程序全局 atomic_t 实例设置名称。这将生成类似 ivtv0ivtv1 等名称。如果名称以数字结尾,则它将插入一个破折号:cx18-0cx18-1 等。此函数返回实例编号。

第一个 dev 参数通常是 struct device 指针,指向 pci_devusb_interfaceplatform_device。dev 为 NULL 的情况很少见,但在 ISA 设备或一个设备创建多个 PCI 设备时会发生这种情况,从而无法将 v4l2_dev 与特定父级关联。

您还可以提供一个 notify() 回调,子设备可以调用该回调来通知您事件。是否需要设置此回调取决于子设备。子设备支持的任何通知都必须在 include/media/subdevice.h 中的标头中定义。

V4L2 设备通过调用以下函数注销:

如果 dev->driver_data 字段指向 v4l2_dev,它将被重置为 NULL。注销还将自动从设备注销所有子设备。

如果您有一个热插拔设备(例如 USB 设备),则当发生断开连接时,父设备将变为无效。由于 v4l2_device 有一个指向该父设备的指针,因此也必须清除它以标记父设备已消失。为此,请调用

不会注销子设备,因此您仍然需要调用 v4l2_device_unregister() 函数。如果您的驱动程序不是热插拔的,则无需调用 v4l2_device_disconnect()

有时您需要迭代特定驱动程序注册的所有设备。如果多个设备驱动程序使用相同的硬件,通常会发生这种情况。例如,ivtvfb 驱动程序是一个帧缓冲驱动程序,它使用 ivtv 硬件。例如,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 buses 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 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),您也可以通过调用以下函数手动增加和减少引用计数:

由于初始引用计数为 1,因此您还需要在 disconnect() 回调(对于 USB 设备)或 remove() 回调(对于例如 PCI 设备)中调用 v4l2_device_put(),否则引用计数将永远不会达到 0。

2.5.1. v4l2_device 函数和数据结构

struct v4l2_device

V4L2 设备驱动程序的主要结构

定义:

struct v4l2_device {
    struct device *dev;
    struct media_device *mdev;
    struct list_head subdevs;
    spinlock_t lock;
    char name[36];
    void (*notify)(struct v4l2_subdev *sd, unsigned int notification, void *arg);
    struct v4l2_ctrl_handler *ctrl_handler;
    struct v4l2_prio_state prio;
    struct kref ref;
    void (*release)(struct v4l2_device *v4l2_dev);
};

成员

dev

指向 struct device 的指针。

mdev

指向 struct media_device 的指针,可以为 NULL。

subdevs

用于跟踪已注册的子设备

lock

锁定此结构;如果此结构嵌入到更大的结构中,驱动程序也可以使用它。

name

唯一的设备名称,默认情况下为驱动程序名称 + 总线 ID

notify

由某些子设备调用的通知操作。

ctrl_handler

控制处理程序。可以为 NULL

prio

设备的优先级状态

ref

跟踪对此结构的引用。

release

当引用计数变为 0 时调用的释放函数。

描述

V4L2 设备的每个实例都应创建 v4l2_device 结构,无论是独立的还是嵌入到更大的结构中。

它允许轻松访问子设备(参见 v4l2-subdev.h)并提供基本的 V4L2 设备级支持。

注意

  1. dev->driver_data 指向此结构。

  2. 如果没有父设备,则 dev 可能为 NULL

void v4l2_device_get(struct v4l2_device *v4l2_dev)

获取 V4L2 设备引用

参数

struct v4l2_device *v4l2_dev

指向 v4l2_device 结构的指针

描述

这是一个辅助例程,旨在增加 v4l2_dev 指向的 v4l2_device 结构的使用计数。

int v4l2_device_put(struct v4l2_device *v4l2_dev)

释放 V4L2 设备引用

参数

struct v4l2_device *v4l2_dev

指向 v4l2_device 结构的指针

描述

这是一个辅助例程,旨在减少 v4l2_dev 指向的 v4l2_device 结构的使用计数。

int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)

初始化 v4l2_dev 并使 dev->driver_data 指向 v4l2_dev

参数

struct device *dev

指向 device 结构的指针

struct v4l2_device *v4l2_dev

指向 v4l2_device 结构的指针

描述

注意

在极少数情况下(ISA 设备),dev 可能为 NULL。在这种情况下,调用者必须在调用此函数之前填充 v4l2_dev->name 字段。

int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename, atomic_t *instance)

可选函数,用于初始化 v4l2_device 结构的 name 字段

参数

struct v4l2_device *v4l2_dev

指向 v4l2_device 结构的指针

const char *basename

设备名称的基本名称

atomic_t *instance

指向具有设备驱动程序的实例用法的静态 atomic_t 变量的指针。

描述

v4l2_device_set_name() 使用驱动程序名称和驱动程序全局 atomic_t 实例初始化 v4l2_device 结构的 name 字段。

此函数将递增实例计数器并返回名称中使用的实例值。

首次调用此函数时,name 字段将设置为 foo0,此函数返回 0。如果名称以数字结尾(例如 cx18),则名称将设置为 cx18-0,因为 cx180 看起来会非常奇怪。

示例

static atomic_t drv_instance = ATOMIC_INIT(0);

...

instance = v4l2_device_set_name(&v4l2_dev, “foo”, &drv_instance);

void v4l2_device_disconnect(struct v4l2_device *v4l2_dev)

将 V4L2 设备状态更改为已断开连接。

参数

struct v4l2_device *v4l2_dev

指向 struct v4l2_device 的指针

描述

应在 USB 父设备断开连接时调用。由于父设备消失,这可确保 v4l2_dev 没有无效的父指针。

注意

此函数将 v4l2_dev->dev 设置为 NULL。

void v4l2_device_unregister(struct v4l2_device *v4l2_dev)

注销所有子设备以及与 v4l2_dev 相关的任何其他资源。

参数

struct v4l2_device *v4l2_dev

指向 struct v4l2_device 的指针

v4l2_device_register_subdev

v4l2_device_register_subdev (v4l2_dev, sd)

向 v4l2 设备注册子设备。

参数

v4l2_dev

指向 v4l2_device 结构的指针

sd

指向 struct v4l2_subdev 的指针

描述

注册后,子设备模块将被标记为正在使用。

如果模块不再加载任何注册尝试,则会返回错误。

void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)

从 v4l2 设备注销子设备。

参数

struct v4l2_subdev *sd

指向 struct v4l2_subdev 的指针

描述

注意

如果未注册子设备,也可以调用此函数。在这种情况下,它将不执行任何操作。

int __v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev, bool read_only)

为标记有 V4L2_SUBDEV_FL_HAS_DEVNODE 标志的 v4l2 设备的所有子设备注册设备节点。

参数

struct v4l2_device *v4l2_dev

指向 struct v4l2_device 的指针

bool read_only

子设备只读标志。True 以只读模式注册子设备设备节点,false 以允许完全访问子设备用户空间 API。

int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)

注册具有对子设备用户空间操作的无限制访问权限的子设备设备节点

参数

struct v4l2_device *v4l2_dev

指向 struct v4l2_device 的指针

描述

在内部调用 __v4l2_device_register_subdev_nodes()。有关更多详细信息,请参见其文档。

int v4l2_device_register_ro_subdev_nodes(struct v4l2_device *v4l2_dev)

以只读模式注册子设备设备节点

参数

struct v4l2_device *v4l2_dev

指向 struct v4l2_device 的指针

描述

在内部调用 __v4l2_device_register_subdev_nodes()。有关更多详细信息,请参见其文档。

void v4l2_subdev_notify(struct v4l2_subdev *sd, unsigned int notification, void *arg)

向 v4l2_device 发送通知。

参数

struct v4l2_subdev *sd

指向 struct v4l2_subdev 的指针

unsigned int notification

通知类型。请注意,通知类型是驱动程序特定的。

void *arg

通知的参数。这些参数特定于每种通知类型。

bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev)

测试是否支持请求。

参数

struct v4l2_device *v4l2_dev

指向 struct v4l2_device 的指针

v4l2_device_for_each_subdev

v4l2_device_for_each_subdev (sd, v4l2_dev)

辅助宏,用于迭代给定 v4l2_device 的所有子设备。

参数

sd

指针,宏将使用所有 struct v4l2_subdev 指针填充该指针,作为循环的迭代器使用。

v4l2_dev

struct v4l2_device 拥有要迭代的子设备。

描述

此宏迭代 v4l2_dev 设备拥有的所有子设备。它充当 for 循环迭代器,并执行下一个语句,其中 sd 变量依次指向每个子设备。

__v4l2_device_call_subdevs_p

__v4l2_device_call_subdevs_p (v4l2_dev, sd, cond, o, f, args...)

为所有匹配条件的子设备调用指定的操作。

参数

v4l2_dev

struct v4l2_device 拥有要迭代的子设备。

sd

指针,宏将使用所有 struct v4l2_subdev 指针填充该指针,作为循环的迭代器使用。

cond

要匹配的条件

o

struct v4l2_subdev_ops 中包含 f 的元素的名称。那里的每个元素都对一组操作函数进行分组。

f

如果 cond 匹配,将调用的操作函数。操作函数根据 struct v4l2_subdev_ops 中的每个元素,按组定义。

args...

f 的参数。

描述

忽略任何错误。

注意

在遍历子设备列表时,无法添加或删除子设备。

__v4l2_device_call_subdevs

__v4l2_device_call_subdevs (v4l2_dev, cond, o, f, args...)

为所有匹配条件的子设备调用指定的操作。

参数

v4l2_dev

struct v4l2_device 拥有要迭代的子设备。

cond

要匹配的条件

o

struct v4l2_subdev_ops 中包含 f 的元素的名称。那里的每个元素都对一组操作函数进行分组。

f

如果 cond 匹配,将调用的操作函数。操作函数根据 struct v4l2_subdev_ops 中的每个元素,按组定义。

args...

f 的参数。

描述

忽略任何错误。

注意

在遍历子设备列表时,无法添加或删除子设备。

__v4l2_device_call_subdevs_until_err_p

__v4l2_device_call_subdevs_until_err_p (v4l2_dev, sd, cond, o, f, args...)

为所有匹配条件的子设备调用指定的操作。

参数

v4l2_dev

struct v4l2_device 拥有要迭代的子设备。

sd

指针,宏将使用与 v4l2_dev 关联的所有 struct v4l2_subdev 子设备填充该指针。

cond

要匹配的条件

o

struct v4l2_subdev_ops 中包含 f 的元素的名称。那里的每个元素都对一组操作函数进行分组。

f

如果 cond 匹配,将调用的操作函数。操作函数根据 struct v4l2_subdev_ops 中的每个元素,按组定义。

args...

f 的参数。

描述

如果操作为任何子设备返回除 0 或 -ENOIOCTLCMD 以外的错误,则中止并返回该错误代码,否则返回零。

注意

在遍历子设备列表时,无法添加或删除子设备。

__v4l2_device_call_subdevs_until_err

__v4l2_device_call_subdevs_until_err (v4l2_dev, cond, o, f, args...)

为所有匹配条件的子设备调用指定的操作。

参数

v4l2_dev

struct v4l2_device 拥有要迭代的子设备。

cond

要匹配的条件

o

struct v4l2_subdev_ops 中包含 f 的元素的名称。那里的每个元素都对一组操作函数进行分组。

f

如果 cond 匹配,将调用的操作函数。操作函数根据 struct v4l2_subdev_ops 中的每个元素,按组定义。

args...

f 的参数。

描述

如果操作为任何子设备返回除 0 或 -ENOIOCTLCMD 以外的错误,则中止并返回该错误代码,否则返回零。

注意

在遍历子设备列表时,无法添加或删除子设备。

v4l2_device_call_all

v4l2_device_call_all (v4l2_dev, grpid, o, f, args...)

为所有匹配 v4l2_subdev.grp_id 的子设备调用指定的操作,如桥接驱动程序分配的。

参数

v4l2_dev

struct v4l2_device 拥有要迭代的子设备。

grpid

struct v4l2_subdev->grp_id 要匹配的组 ID。使用 0 匹配所有组。

o

struct v4l2_subdev_ops 中包含 f 的元素的名称。那里的每个元素都对一组操作函数进行分组。

f

如果 cond 匹配,将调用的操作函数。操作函数根据 struct v4l2_subdev_ops 中的每个元素,按组定义。

args...

f 的参数。

描述

忽略任何错误。

注意

在遍历子设备列表时,无法添加或删除子设备。

v4l2_device_call_until_err

v4l2_device_call_until_err (v4l2_dev, grpid, o, f, args...)

为所有匹配 v4l2_subdev.grp_id 的子设备调用指定的操作,如桥接驱动程序分配的,直到发生错误。

参数

v4l2_dev

struct v4l2_device 拥有要迭代的子设备。

grpid

struct v4l2_subdev->grp_id 要匹配的组 ID。使用 0 匹配所有组。

o

struct v4l2_subdev_ops 中包含 f 的元素的名称。那里的每个元素都对一组操作函数进行分组。

f

如果 cond 匹配,将调用的操作函数。操作函数根据 struct v4l2_subdev_ops 中的每个元素,按组定义。

args...

f 的参数。

描述

如果操作为任何子设备返回除 0 或 -ENOIOCTLCMD 以外的错误,则中止并返回该错误代码,否则返回零。

注意

在遍历子设备列表时,无法添加或删除子设备。

v4l2_device_mask_call_all

v4l2_device_mask_call_all (v4l2_dev, grpmsk, o, f, args...)

为组 ID 匹配指定位掩码的所有子设备调用指定的操作。

参数

v4l2_dev

struct v4l2_device 拥有要迭代的子设备。

grpmsk

要对照 struct v4l2_subdev->grp_id 组 ID 检查的位掩码。使用 0 匹配所有组。

o

struct v4l2_subdev_ops 中包含 f 的元素的名称。那里的每个元素都对一组操作函数进行分组。

f

如果 cond 匹配,将调用的操作函数。操作函数根据 struct v4l2_subdev_ops 中的每个元素,按组定义。

args...

f 的参数。

描述

忽略任何错误。

注意

在遍历子设备列表时,无法添加或删除子设备。

v4l2_device_mask_call_until_err

v4l2_device_mask_call_until_err (v4l2_dev, grpmsk, o, f, args...)

为组 ID 匹配指定位掩码的所有子设备调用指定的操作。

参数

v4l2_dev

struct v4l2_device 拥有要迭代的子设备。

grpmsk

要对照 struct v4l2_subdev->grp_id 组 ID 检查的位掩码。使用 0 匹配所有组。

o

struct v4l2_subdev_ops 中包含 f 的元素的名称。那里的每个元素都对一组操作函数进行分组。

f

如果 cond 匹配,将调用的操作函数。操作函数根据 struct v4l2_subdev_ops 中的每个元素,按组定义。

args...

f 的参数。

描述

如果操作为任何子设备返回除 0 或 -ENOIOCTLCMD 以外的错误,则中止并返回该错误代码,否则返回零。

注意

在遍历子设备列表时,无法添加或删除子设备。

v4l2_device_has_op

v4l2_device_has_op (v4l2_dev, grpid, o, f)

检查是否任何匹配 grpid 的子设备具有给定的 ops。

参数

v4l2_dev

struct v4l2_device 拥有要迭代的子设备。

grpid

struct v4l2_subdev->grp_id 要匹配的组 ID。使用 0 匹配所有组。

o

struct v4l2_subdev_ops 中包含 f 的元素的名称。那里的每个元素都对一组操作函数进行分组。

f

如果 cond 匹配,将调用的操作函数。操作函数根据 struct v4l2_subdev_ops 中的每个元素,按组定义。

v4l2_device_mask_has_op

v4l2_device_mask_has_op (v4l2_dev, grpmsk, o, f)

检查是否任何匹配组掩码的子设备具有给定的 ops。

参数

v4l2_dev

struct v4l2_device 拥有要迭代的子设备。

grpmsk

要对照 struct v4l2_subdev->grp_id 组 ID 检查的位掩码。使用 0 匹配所有组。

o

struct v4l2_subdev_ops 中包含 f 的元素的名称。那里的每个元素都对一组操作函数进行分组。

f

如果 cond 匹配,将调用的操作函数。操作函数根据 struct v4l2_subdev_ops 中的每个元素,按组定义。