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_subscribed_event 具有驱动程序可以设置的 merge()replace() 回调。当引发新事件且没有更多空间时,将调用这些回调。

replace() 回调允许您用新事件的有效负载替换旧事件的有效负载,将来自旧有效负载的任何相关数据合并到替换它的新有效负载中。当此事件类型具有大小为 1 的环形缓冲区时(即,环形缓冲区中只能存储一个事件)会调用它。

merge() 回调允许您将最旧的事件有效负载合并到第二个最旧的事件有效负载中。当环形缓冲区的大小大于 1 时会调用它。

这样,就不会丢失状态信息,而只会丢失导致该状态的中间步骤。

这些 replace/merge 回调的一个很好的例子是在 v4l2-event.c 中:控制事件的 ctrls_replace()ctrls_merge() 回调。

注意

这些回调可以从中断上下文中调用,因此它们必须很快。

为了将事件排队到视频设备,驱动程序应调用

驱动程序的唯一职责是填写类型和数据字段。其他字段将由 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. 取消订阅事件

取消订阅事件是通过

此函数用于实现 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 填写。

void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)

将事件加入到视频设备的队列中。

参数

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)

唤醒所有文件句柄。

参数

struct video_device *vdev

指向 struct video_device 的指针

描述

当注销视频设备时使用。

int v4l2_event_pending(struct v4l2_fh *fh)

检查是否有可用的事件

参数

struct v4l2_fh *fh

指向 struct v4l2_fh 的指针

描述

返回待处理事件的数量。

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 个元素。

int v4l2_event_unsubscribe(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)

取消订阅一个事件

参数

struct v4l2_fh *fh

指向 struct v4l2_fh 的指针

const struct v4l2_event_subscription *sub

指向 struct v4l2_event_subscription 的指针

void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)

取消订阅所有事件

参数

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 的指针