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