2.5. V4L2 设备实例¶
每个设备实例都由 struct v4l2_device
表示。非常简单的设备可以直接分配此结构,但大多数情况下,您会将此结构嵌入到更大的结构中。
您必须通过调用以下函数来注册设备实例
v4l2_device_register
(dev,v4l2_dev
)。
注册将初始化 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 实例来设置名称。这将生成诸如 ivtv0
、ivtv1
等名称。如果名称以数字结尾,则它将插入一个破折号:cx18-0
、cx18-1
等。此函数返回实例编号。
第一个 dev
参数通常是 struct device
指针,指向 pci_dev
、usb_interface
或 platform_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 设备级支持。
注意
dev->driver_data 指向此结构体。
如果没有父设备,则 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 设备注销一个子设备。
-
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
中的每个元素,操作函数以组定义。