2.14. V4L2 事件¶
V4L2 事件提供了一种将事件传递到用户空间的通用方法。驱动程序必须使用 v4l2_fh
才能支持 V4L2 事件。
事件是按文件句柄订阅的。事件规范由 type
组成,并且可以选择与通过 id
字段标识的对象关联。如果未使用,则 id
为 0。因此,事件由 (type, id)
元组唯一标识。
v4l2_fh
结构在其 subscribed
字段上具有已订阅事件的列表。
当用户订阅事件时,会将一个 v4l2_subscribed_event
结构添加到 v4l2_fh
.subscribed
,每个订阅的事件一个。
每个 v4l2_subscribed_event
结构以 v4l2_kevent
环形缓冲区结束,其大小由 v4l2_event_subscribe()
的调用者给出。此环形缓冲区用于存储驱动程序引发的任何事件。
因此,每个 (type, ID)
事件元组都将有自己的 v4l2_kevent
环形缓冲区。这保证了如果驱动程序在短时间内生成大量一种类型的事件,则不会覆盖另一种类型的事件。
但是,如果您获得的一种类型的事件多于 v4l2_kevent
环形缓冲区的大小,则最旧的事件将被删除,并添加新的事件。
v4l2_kevent
结构链接到 v4l2_fh
结构的 available
列表,因此 ioctl VIDIOC_DQEVENT 将知道首先取消排队哪个事件。
最后,如果事件订阅与特定的对象(例如 V4L2 控件)关联,则该对象也需要了解这一点,以便该对象可以引发事件。因此,可以使用 node
字段将 v4l2_subscribed_event
结构链接到此类对象的列表中。
因此,总结如下
struct v4l2_fh
有两个列表:一个是subscribed
事件,另一个是available
事件。struct v4l2_subscribed_event
具有该特定类型的已引发(待处理)事件的环形缓冲区。如果
struct v4l2_subscribed_event
与特定对象关联,则该对象将具有struct v4l2_subscribed_event
的内部列表,以便它知道谁向该对象订阅了事件。
此外,内部的 struct v4l2_subscribed_event
具有 merge()
和 replace()
回调,驱动程序可以设置它们。当引发新事件并且没有更多空间时,将调用这些回调。
replace()
回调允许您将旧事件的有效负载替换为新事件的有效负载,将旧有效负载中的任何相关数据合并到替换它的新有效负载中。当此事件类型的环形缓冲区大小为 1 时,即环形缓冲区中只能存储一个事件时,将调用此回调。
merge()
回调允许您将最旧事件的有效负载合并到第二旧事件的有效负载中。当环形缓冲区的大小大于 1 时,将调用此回调。
这样就不会丢失状态信息,而只是导致该状态的中间步骤。
这些 replace
/merge
回调的一个很好的示例在 v4l2-event.c 中:控件事件的 ctrls_replace()
和 ctrls_merge()
回调。
注意
这些回调可以从中断上下文中调用,因此它们必须很快。
为了将事件排队到视频设备,驱动程序应调用
v4l2_event_queue
(vdev
,ev
)
驱动程序的唯一责任是填写类型和数据字段。其他字段将由 V4L2 填写。
2.14.1. 事件订阅¶
订阅事件通过以下方式进行
v4l2_event_subscribe
(fh
,sub
, elems,ops
)
此函数用于实现 video_device
-> ioctl_ops
-> vidioc_subscribe_event
,但驱动程序必须首先检查驱动程序是否能够生成具有指定事件 ID 的事件,然后应调用 v4l2_event_subscribe()
来订阅事件。
elems 参数是此事件的事件队列的大小。如果为 0,则框架将填充默认值(这取决于事件类型)。
ops 参数允许驱动程序指定一些回调函数
回调函数 |
描述 |
---|---|
add |
当添加新的侦听器时调用(两次订阅同一事件只会导致此回调函数被调用一次) |
del |
当侦听器停止侦听时调用 |
replace |
用事件“new”替换事件“old”。 |
merge |
将事件“old”合并到事件“new”中。 |
所有 4 个回调函数都是可选的,如果您不想指定任何回调函数,则 ops 参数本身可以为 NULL
。
2.14.2. 取消订阅事件¶
通过以下方式取消订阅事件:
v4l2_event_unsubscribe
(fh
,sub
)
此函数用于实现 video_device
-> ioctl_ops
-> vidioc_unsubscribe_event
。驱动程序可以直接调用 v4l2_event_unsubscribe()
,除非它想参与取消订阅过程。
特殊类型 V4L2_EVENT_ALL
可用于取消订阅所有事件。驱动程序可能希望以特殊方式处理此情况。
2.14.3. 检查是否有待处理的事件¶
通过以下方式检查是否有待处理的事件:
此函数返回待处理事件的数量。在实现 poll 时很有用。
2.14.4. 事件的工作原理¶
事件通过 poll 系统调用传递到用户空间。驱动程序可以使用 v4l2_fh
->wait (a wait_queue_head_t) 作为 poll_wait()
的参数。
有标准事件和私有事件。新的标准事件必须使用最小的可用事件类型。驱动程序必须从自己的类中分配事件,从类基址开始。类基址为 V4L2_EVENT_PRIVATE_START
+ n * 1000,其中 n 是最小的可用数字。类中的第一个事件类型保留供将来使用,因此第一个可用的事件类型是“类基址 + 1”。
有关如何使用 V4L2 事件的示例,请参见 OMAP 3 ISP 驱动程序(drivers/media/platform/ti/omap3isp
)。
子设备可以使用 V4L2_DEVICE_NOTIFY_EVENT
直接向 v4l2_device
通知函数发送事件。这允许桥接器将发送事件的子设备映射到与需要被告知此类事件的子设备关联的视频节点。
2.14.4.1. V4L2 事件函数和数据结构¶
-
struct v4l2_kevent¶
内部内核事件结构。
定义:
struct v4l2_kevent {
struct list_head list;
struct v4l2_subscribed_event *sev;
struct v4l2_event event;
u64 ts;
};
成员
list
v4l2_fh->available 列表的列表节点。
sev
指向父 v4l2_subscribed_event 的指针。
event
事件本身。
ts
事件的时间戳。
-
struct v4l2_subscribed_event_ops¶
订阅事件操作。
定义:
struct v4l2_subscribed_event_ops {
int (*add)(struct v4l2_subscribed_event *sev, unsigned int elems);
void (*del)(struct v4l2_subscribed_event *sev);
void (*replace)(struct v4l2_event *old, const struct v4l2_event *new);
void (*merge)(const struct v4l2_event *old, struct v4l2_event *new);
};
成员
add
可选回调,当添加新的侦听器时调用
del
可选回调,当侦听器停止侦听时调用
replace
可选回调,可以用事件“new”替换事件“old”。
merge
可选回调,可以将事件“old”合并到事件“new”中。
-
struct v4l2_subscribed_event¶
表示订阅事件的内部结构。
定义:
struct v4l2_subscribed_event {
struct list_head list;
u32 type;
u32 id;
u32 flags;
struct v4l2_fh *fh;
struct list_head node;
const struct v4l2_subscribed_event_ops *ops;
unsigned int elems;
unsigned int first;
unsigned int in_use;
struct v4l2_kevent events[] ;
};
成员
list
v4l2_fh->subscribed 列表的列表节点。
type
事件类型。
id
关联的对象 ID(例如,控制 ID)。如果没有,则为 0。
flags
v4l2_event_subscription->flags 的副本。
fh
订阅此事件的文件句柄。
node
挂钩到对象的事件列表的列表节点(如果存在)。
ops
v4l2_subscribed_event_ops
elems
事件数组中的元素数量。
first
包含最旧的可用事件的事件的索引。
in_use
排队的事件数量。
events
一个包含 elems 个事件的数组。
参数
struct v4l2_fh *fh
指向
struct v4l2_fh
的指针struct v4l2_event *event
指向 struct v4l2_event 的指针
int nonblocking
如果非零,则等待事件到达
-
void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)¶
将事件排队到视频设备。
参数
struct video_device *vdev
指向
struct video_device
的指针const struct v4l2_event *ev
指向
struct v4l2_event
的指针
描述
该事件将为所有 struct v4l2_fh
文件句柄排队。
注意
驱动程序的唯一责任是填写类型和数据字段。其他字段将由 V4L2 填写。
参数
struct v4l2_fh *fh
指向
struct v4l2_fh
的指针const struct v4l2_event *ev
指向
struct v4l2_event
的指针
描述
该事件将仅为指定的 struct v4l2_fh
文件句柄排队。
注意
驱动程序的唯一责任是填写类型和数据字段。其他字段将由 V4L2 填写。
-
void v4l2_event_wake_all(struct video_device *vdev)¶
唤醒所有文件句柄。
-
int v4l2_event_subscribe(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub, unsigned int elems, const struct v4l2_subscribed_event_ops *ops)¶
订阅一个事件
参数
struct v4l2_fh *fh
指向
struct v4l2_fh
的指针const struct v4l2_event_subscription *sub
指向
struct v4l2_event_subscription
的指针unsigned int elems
事件队列的大小
const struct v4l2_subscribed_event_ops *ops
指向
v4l2_subscribed_event_ops
的指针
描述
注意
如果 elems 为零,框架将填充默认值,当前为 1 个元素。
参数
struct v4l2_fh *fh
指向
struct v4l2_fh
的指针const struct v4l2_event_subscription *sub
指向
struct v4l2_event_subscription
的指针
参数
struct v4l2_fh *fh
指向
struct v4l2_fh
的指针
-
int v4l2_event_subdev_unsubscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub)¶
v4l2_event_unsubscribe()
的子设备变体
参数
struct v4l2_subdev *sd
指向
struct v4l2_subdev
的指针struct v4l2_fh *fh
指向
struct v4l2_fh
的指针struct v4l2_event_subscription *sub
指向
struct v4l2_event_subscription
的指针
描述
注意
此函数应用于 struct v4l2_subdev_core_ops
的 unsubscribe_event
字段。
-
int v4l2_src_change_event_subscribe(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)¶
如果事件是
V4L2_EVENT_SOURCE_CHANGE
,则调用v4l2_event_subscribe()
的辅助函数。
参数
struct v4l2_fh *fh
指向
struct v4l2_fh
的指针const struct v4l2_event_subscription *sub
指向
struct v4l2_event_subscription
的指针
-
int v4l2_src_change_event_subdev_subscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub)¶
v4l2_event_subscribe()
的变体,旨在仅订阅类型为V4L2_EVENT_SOURCE_CHANGE
的事件。
参数
struct v4l2_subdev *sd
指向
struct v4l2_subdev
的指针struct v4l2_fh *fh
指向
struct v4l2_fh
的指针struct v4l2_event_subscription *sub
指向
struct v4l2_event_subscription
的指针