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 设备的所有标记了 V4L2_SUBDEV_FL_HAS_DEVNODE 标志的子设备注册设备节点。

参数

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 的子设备拥有给定的操作。

参数

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)

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

参数

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 中的每个元素,操作函数以组定义。