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. 检查是否有待处理的事件¶
检查是否有待处理的事件是通过
此函数返回待处理事件的数量。在实现轮询时很有用。
2.14.4. 事件如何工作¶
事件通过轮询系统调用传递到用户空间。驱动程序可以使用 v4l2_fh
->wait (一个 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
可选回调,可以将事件 “old” 替换为事件 “new”。
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
events 数组中的元素数。
first
包含最旧可用事件的 events 的索引。
in_use
已排队事件的数量。
events
一个包含 elems 事件的数组。
-
int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event, int nonblocking)¶
从视频设备中取消排队事件。
参数
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
的指针