2.2.12 V4L2 events

V4L2事件提供了向用户空间传递事件的通用方式。 驱动程序必须使用v4l2_fh才能支持V4L2事件。
事件按文件句柄订阅。 事件规范由类型组成,可选择与通过id字段标识的对象相关联。 如果未使用,则id为0。 因此,事件由(type,id)元组唯一标识。
v4l2_fh结构在其subscribed字段上具有订阅事件列表。
当用户订阅事件时,会向v4l2_fh.subscribed添加一个v4l2_subscribed_event结构,对于每个已订阅的事件都会添加一个。
每个v4l2_subscribed_event结构以一个由v4l2_event_subscribe()的调用者给出大小的v4l2_kevent环形缓冲区结尾。此环形缓冲区用于存储驱动程序引发的任何事件。
因此,每个(type,ID)事件元组都有自己的v4l2_kevent环形缓冲区。这保证了如果驱动程序在短时间内生成大量相同类型的事件,则不会覆盖另一类型的事件。
但是,如果您收到的某种类型的事件比v4l2_kevent环形缓冲区的大小还多,则最旧的事件将被丢弃并添加新事件。
v4l2_kevent结构链接到v4l2_fh结构的可用列表中,以便ioctl VIDIOC_DQEVENT知道首先出队哪个事件。
最后,如果事件订阅与特定对象(例如V4L2控件)相关联,则该对象也需要知道以便该对象可以引发事件。因此,节点字段可以用于将v4l2_subscribed_event结构链接到此类对象的列表中。
因此,总结一下:
• struct v4l2_fh有两个列表:一个是已订阅事件,另一个是可用事件。
• struct v4l2_subscribed_event具有特定类型的已提出(待处理)事件的环形缓冲区。
• 如果struct v4l2_subscribed_event与特定对象相关联,则该对象将具有struct v4l2_subscribed_event的内部列表,以便它知道谁向该对象订阅了事件。
此外,内部的struct v4l2_subscribed_event具有merge()和replace()回调,驱动程序可以设置它们。当提出新事件且没有更多空间时,将调用这些回调函数。
replace()回调允许您使用新事件的有效负载替换旧事件的有效负载,并将旧有效负载中的任何相关数据合并到替换它的新有效负载中。当此事件类型具有大小为1的环形缓冲区时,即仅可以在环形缓冲区中存储一个事件时,将调用replace()回调。
与此不同,merge()回调允许您将最旧的事件有效负载合并到次旧事件有效负载中。当环形缓冲区的大小大于1时,将调用此回调。
这样,就不会丢失任何状态信息,只会丢失导致该状态的中间步骤。
这些replace/merge回调的良好示例可在v4l2-event.c中找到:控件事件的ctrls_replace()和ctrls_merge()回调。
注意:这些回调可能会从中断上下文中调用,因此必须快速执行。
为了将事件排队到视频设备,驱动程序应该调用:
v4l2_event_queue(vdev, ev)
驱动程序仅需填写type和data字段。其他字段将由V4L2填充。
什么是中断上下文:
中断上下文是指在操作系统内核执行中断处理程序期间使用的一组堆栈和寄存器,其目的是在中断处理程序执行期间保存系统状态。由于中断可能随时发生,因此在进入中断处理程序时,处理器将会保存当前的CPU状态,并将控制转移到中断处理程序。在这种情况下,中断上下文就是用来保存该状态并执行中断处理程序的执行环境。中断上下文的执行需要尽可能地快速完成,以便将控制返回到被中断的程序。因此,在中断上下文中只能访问安全的内核代码和数据,不能执行可能会引起死锁或长时间延迟的操作。
2.2.12.1 Event subscription
通过v4l2_event_subscribe(fh, sub, elems, ops)来订阅事件:
这个函数用于实现video_device->ioctl_ops->vidioc_subscribe_event,但是驱动程序必须首先检查驱动程序是否能够生成指定事件ID的事件,然后应该调用v4l2_event_subscribe()来订阅该事件。
elems参数是此事件队列的大小。如果为0,则框架将填充默认值(这取决于事件类型)。
ops参数允许驱动程序指定多个回调函数:

所有4个回调函数都是可选的,如果您不想指定任何回调函数,ops参数本身可以为NULL。
2.2.12.2 Unsubscribing an event
取消订阅事件是通过v4l2_event_unsubscribe(fh, sub)实现的。
这个函数用于实现video_device->ioctl_ops->vidioc_unsubscribe_event。
除非驱动程序想参与取消订阅过程,否则驱动程序可以直接调用v4l2_event_unsubscribe()。
特殊类型V4L2_EVENT_ALL可用于取消订阅所有事件。驱动程序可能希望以特殊方式处理此事件。
2.2.12.3 Check if there’s a pending event
检查是否有待处理事件是通过v4l2_event_pending(fh)实现的。
此函数返回待处理事件的数量。在实现轮询时非常有用。
2.2.12.4 How events work
事件通过轮询系统调用传递给用户空间。驱动程序可以将v4l2_fh->wait(一个wait_queue_head_t)作为poll_wait()的参数使用。
有标准事件和私有事件。新的标准事件必须使用最小可用事件类型。驱动程序必须从其自己的类中分配事件,从类基础开始。类基础是V4L2_EVENT_PRIVATE_START + n * 1000,其中n是最低可用数字。该类中的第一个事件类型保留供将来使用,因此第一个可用的事件类型是“class base + 1”。
如何使用V4L2事件的示例可在OMAP 3 ISP驱动程序(drivers/media/platform/omap3isp)中找到。子设备可以直接使用V4L2_DEVICE_NOTIFY_EVENT向v4l2_device notify函数发送事件。这允许桥将发送事件的子设备映射到需要了解此类事件的与子设备关联的视频节点。
2.2.12.5 V4L2 event functions and data structures
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 事件数组中的元素数。
first 包含最旧可用事件的事件的索引。
in_use 排队事件的数量。
events 一个包含elems事件的数组。
int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event, int nonblocking)
从视频设备中取消排队事件。
参数:
struct v4l2_fh *fh 指向结构体v4l2_fh的指针
struct v4l2_event *event 指向结构体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的指针
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值