요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
V4L2 events
-----------
The V4L2 events provide a generic way to pass events to user space.
The driver must use :c:type:`v4l2_fh` to be able to support V4L2 events.
Events are subscribed per-filehandle. An event specification consists of a
``type`` and is optionally associated with an object identified through the
``id`` field. If unused, then the ``id`` is 0. So an event is uniquely
identified by the ``(type, id)`` tuple.
The :c:type:`v4l2_fh` struct has a list of subscribed events on its
``subscribed`` field.
When the user subscribes to an event, a :c:type:`v4l2_subscribed_event`
struct is added to :c:type:`v4l2_fh`\ ``.subscribed``, one for every
subscribed event.
Each :c:type:`v4l2_subscribed_event` struct ends with a
:c:type:`v4l2_kevent` ringbuffer, with the size given by the caller
of :c:func:`v4l2_event_subscribe`. This ringbuffer is used to store any events
raised by the driver.
So every ``(type, ID)`` event tuple will have its own
:c:type:`v4l2_kevent` ringbuffer. This guarantees that if a driver is
generating lots of events of one type in a short time, then that will
not overwrite events of another type.
But if you get more events of one type than the size of the
:c:type:`v4l2_kevent` ringbuffer, then the oldest event will be dropped
and the new one added.
The :c:type:`v4l2_kevent` struct links into the ``available``
list of the :c:type:`v4l2_fh` struct so :ref:`VIDIOC_DQEVENT` will
know which event to dequeue first.
Finally, if the event subscription is associated with a particular object
such as a V4L2 control, then that object needs to know about that as well
so that an event can be raised by that object. So the ``node`` field can
be used to link the :c:type:`v4l2_subscribed_event` struct into a list of
such objects.
So to summarize:
- struct v4l2_fh has two lists: one of the ``subscribed`` events,
and one of the ``available`` events.
- struct v4l2_subscribed_event has a ringbuffer of raised
(pending) events of that particular type.
- If struct v4l2_subscribed_event is associated with a specific
object, then that object will have an internal list of
struct v4l2_subscribed_event so it knows who subscribed an
event to that object.
Furthermore, the internal struct v4l2_subscribed_event has
``merge()`` and ``replace()`` callbacks which drivers can set. These
callbacks are called when a new event is raised and there is no more room.
The ``replace()`` callback allows you to replace the payload of the old event
with that of the new event, merging any relevant data from the old payload
into the new payload that replaces it. It is called when this event type has
a ringbuffer with size is one, i.e. only one event can be stored in the
ringbuffer.
The ``merge()`` callback allows you to merge the oldest event payload into
that of the second-oldest event payload. It is called when
the ringbuffer has size is greater than one.
This way no status information is lost, just the intermediate steps leading
up to that state.
A good example of these ``replace``/``merge`` callbacks is in v4l2-event.c:
``ctrls_replace()`` and ``ctrls_merge()`` callbacks for the control event.
.. note::
these callbacks can be called from interrupt context, so they must
be fast.
In order to queue events to video device, drivers should call:
:c:func:`v4l2_event_queue <v4l2_event_queue>`
(:c:type:`vdev <video_device>`, :c:type:`ev <v4l2_event>`)
The driver's only responsibility is to fill in the type and the data fields.
The other fields will be filled in by V4L2.
Event subscription
~~~~~~~~~~~~~~~~~~
Subscribing to an event is via:
:c:func:`v4l2_event_subscribe <v4l2_event_subscribe>`
(:c:type:`fh <v4l2_fh>`, :c:type:`sub <v4l2_event_subscription>` ,
elems, :c:type:`ops <v4l2_subscribed_event_ops>`)
This function is used to implement :c:type:`video_device`->
:c:type:`ioctl_ops <v4l2_ioctl_ops>`-> ``vidioc_subscribe_event``,
but the driver must check first if the driver is able to produce events
with specified event id, and then should call
:c:func:`v4l2_event_subscribe` to subscribe the event.
The elems argument is the size of the event queue for this event. If it is 0,
then the framework will fill in a default value (this depends on the event
type).
The ops argument allows the driver to specify a number of callbacks:
.. tabularcolumns:: |p{1.5cm}|p{16.0cm}|
======== ==============================================================
Callback Description
======== ==============================================================
add called when a new listener gets added (subscribing to the same
event twice will only cause this callback to get called once)
del called when a listener stops listening
replace replace event 'old' with event 'new'.
merge merge event 'old' into event 'new'.
======== ==============================================================
All 4 callbacks are optional, if you don't want to specify any callbacks
the ops argument itself maybe ``NULL``.
Unsubscribing an event
~~~~~~~~~~~~~~~~~~~~~~
Unsubscribing to an event is via:
:c:func:`v4l2_event_unsubscribe <v4l2_event_unsubscribe>`
(:c:type:`fh <v4l2_fh>`, :c:type:`sub <v4l2_event_subscription>`)
This function is used to implement :c:type:`video_device`->
:c:type:`ioctl_ops <v4l2_ioctl_ops>`-> ``vidioc_unsubscribe_event``.
A driver may call :c:func:`v4l2_event_unsubscribe` directly unless it
wants to be involved in unsubscription process.
The special type ``V4L2_EVENT_ALL`` may be used to unsubscribe all events. The
drivers may want to handle this in a special way.
Check if there's a pending event
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Checking if there's a pending event is via:
:c:func:`v4l2_event_pending <v4l2_event_pending>`
(:c:type:`fh <v4l2_fh>`)
This function returns the number of pending events. Useful when implementing
poll.
How events work
~~~~~~~~~~~~~~~
Events are delivered to user space through the poll system call. The driver
can use :c:type:`v4l2_fh`->wait (a wait_queue_head_t) as the argument for
``poll_wait()``.
There are standard and private events. New standard events must use the
smallest available event type. The drivers must allocate their events from
their own class starting from class base. Class base is
``V4L2_EVENT_PRIVATE_START`` + n * 1000 where n is the lowest available number.
The first event type in the class is reserved for future use, so the first
available event type is 'class base + 1'.
An example on how the V4L2 events may be used can be found in the OMAP
3 ISP driver (``drivers/media/platform/ti/omap3isp``).
A subdev can directly send an event to the :c:type:`v4l2_device` notify
function with ``V4L2_DEVICE_NOTIFY_EVENT``. This allows the bridge to map
the subdev that sends the event to the video node(s) associated with the
subdev that need to be informed about such an event.
V4L2 event functions and data structures
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. kernel-doc:: include/media/v4l2-event.h
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
V4L2 event 자료구조와 ring buffer
1-44V4L2 event는 event를 userspace에 전달하는 일반적인 방법입니다. Driver가 V4L2 event를 지원하려면 `v4l2_fh`를 사용해야 합니다.
Event subscription은 file handle별로 관리합니다. Event 명세는 `type`과 선택적인 object `id`로 이루어지며, `id`를 사용하지 않으면 0입니다. 따라서 하나의 event는 `(type, id)` tuple로 고유하게 식별됩니다.
`v4l2_fh.subscribed`는 구독한 event 목록입니다. 사용자가 event를 구독할 때마다 해당 event를 나타내는 `v4l2_subscribed_event`가 이 목록에 추가됩니다.
각 `v4l2_subscribed_event` 끝에는 `v4l2_event_subscribe()` 호출자가 지정한 크기의 `v4l2_kevent` ring buffer가 있습니다. Driver가 발생시킨 event는 이 buffer에 저장됩니다.
각 `(type, id)` tuple은 독립 ring buffer를 가지므로 한 종류의 event가 짧은 시간에 많이 발생해도 다른 종류의 event를 덮어쓰지 않습니다. 같은 종류의 event가 ring buffer 크기를 넘으면 가장 오래된 event를 버리고 새 event를 추가합니다.
`v4l2_kevent`는 `v4l2_fh.available` 목록에도 연결되므로 `VIDIOC_DQEVENT`가 먼저 dequeue할 event를 알 수 있습니다.
Subscription이 V4L2 control 같은 특정 object와 연결되면 그 object도 구독자를 알아야 event를 발생시킬 수 있습니다. `node` field는 `v4l2_subscribed_event`를 해당 object의 내부 목록에 연결할 때 사용합니다.
File handle의 구독 목록과 이용 가능 목록, tuple별 ring buffer가 함께 동작합니다.
.. SPDX-License-Identifier: GPL-2.0
V4L2 events
-----------
The V4L2 events provide a generic way to pass events to user space.
The driver must use :c:type:`v4l2_fh` to be able to support V4L2 events.
Events are subscribed per-filehandle. An event specification consists of a
``type`` and is optionally associated with an object identified through the
``id`` field. If unused, then the ``id`` is 0. So an event is uniquely
identified by the ``(type, id)`` tuple.
The :c:type:`v4l2_fh` struct has a list of subscribed events on its
``subscribed`` field.
When the user subscribes to an event, a :c:type:`v4l2_subscribed_event`
struct is added to :c:type:`v4l2_fh`\ ``.subscribed``, one for every
subscribed event.
Each :c:type:`v4l2_subscribed_event` struct ends with a
:c:type:`v4l2_kevent` ringbuffer, with the size given by the caller
of :c:func:`v4l2_event_subscribe`. This ringbuffer is used to store any events
raised by the driver.
So every ``(type, ID)`` event tuple will have its own
:c:type:`v4l2_kevent` ringbuffer. This guarantees that if a driver is
generating lots of events of one type in a short time, then that will
not overwrite events of another type.
But if you get more events of one type than the size of the
:c:type:`v4l2_kevent` ringbuffer, then the oldest event will be dropped
and the new one added.
The :c:type:`v4l2_kevent` struct links into the ``available``
list of the :c:type:`v4l2_fh` struct so :ref:`VIDIOC_DQEVENT` will
know which event to dequeue first.
Finally, if the event subscription is associated with a particular object
such as a V4L2 control, then that object needs to know about that as well
so that an event can be raised by that object. So the ``node`` field can
be used to link the :c:type:`v4l2_subscribed_event` struct into a list of
such objects.
Event overflow 병합과 queue
45-89요약하면 `v4l2_fh`에는 구독한 event의 `subscribed` 목록과 발생하여 대기 중인 event의 `available` 목록이 있습니다. `v4l2_subscribed_event`는 특정 종류의 pending event ring buffer를 가지며, 특정 object와 연결된 경우 object도 자신에게 등록된 subscription 목록을 가집니다.
내부 `v4l2_subscribed_event`에는 driver가 설정할 수 있는 `merge()`와 `replace()` callback도 있습니다. 새 event가 발생했지만 ring buffer에 빈 공간이 없을 때 호출됩니다.
Ring buffer 크기가 1이면 `replace()`가 기존 event payload를 새 payload로 바꾸면서 필요한 기존 상태를 새 payload에 병합합니다. 크기가 1보다 크면 `merge()`가 가장 오래된 event payload를 두 번째로 오래된 payload에 합칩니다.
이 방식은 최종 상태 정보는 잃지 않고 그 상태에 이르는 중간 단계만 압축합니다. Control event의 예는 `v4l2-event.c`의 `ctrls_replace()`와 `ctrls_merge()`입니다.
이 callback들은 interrupt context에서 호출될 수 있으므로 빨라야 합니다.
Video device에 event를 queue하려면 `v4l2_event_queue(vdev, ev)`를 호출합니다. Driver는 event의 type과 data field만 채우며 나머지 field는 V4L2가 채웁니다.
So to summarize:
- struct v4l2_fh has two lists: one of the ``subscribed`` events,
and one of the ``available`` events.
- struct v4l2_subscribed_event has a ringbuffer of raised
(pending) events of that particular type.
- If struct v4l2_subscribed_event is associated with a specific
object, then that object will have an internal list of
struct v4l2_subscribed_event so it knows who subscribed an
event to that object.
Furthermore, the internal struct v4l2_subscribed_event has
``merge()`` and ``replace()`` callbacks which drivers can set. These
callbacks are called when a new event is raised and there is no more room.
The ``replace()`` callback allows you to replace the payload of the old event
with that of the new event, merging any relevant data from the old payload
into the new payload that replaces it. It is called when this event type has
a ringbuffer with size is one, i.e. only one event can be stored in the
ringbuffer.
The ``merge()`` callback allows you to merge the oldest event payload into
that of the second-oldest event payload. It is called when
the ringbuffer has size is greater than one.
This way no status information is lost, just the intermediate steps leading
up to that state.
A good example of these ``replace``/``merge`` callbacks is in v4l2-event.c:
``ctrls_replace()`` and ``ctrls_merge()`` callbacks for the control event.
.. note::
these callbacks can be called from interrupt context, so they must
be fast.
In order to queue events to video device, drivers should call:
:c:func:`v4l2_event_queue <v4l2_event_queue>`
(:c:type:`vdev <video_device>`, :c:type:`ev <v4l2_event>`)
The driver's only responsibility is to fill in the type and the data fields.
The other fields will be filled in by V4L2.
Event subscription
90-126Event 구독은 `v4l2_event_subscribe(fh, sub, elems, ops)`로 수행합니다. 이 함수는 `video_device->ioctl_ops->vidioc_subscribe_event`를 구현할 때 사용합니다.
Driver는 먼저 지정된 event ID의 event를 실제로 만들 수 있는지 검사한 뒤 `v4l2_event_subscribe()`를 호출해야 합니다.
`elems`는 이 event의 queue 크기입니다. 0이면 framework가 event 종류에 따른 기본값을 채웁니다.
`ops`는 네 가지 선택적 callback을 지정합니다. `add`는 새 listener가 추가될 때 호출되며 같은 event를 두 번 구독해도 한 번만 호출됩니다. `del`은 listener가 구독을 중지할 때 호출됩니다. `replace`는 old event를 new event로 교체하고 `merge`는 old event를 new event에 병합합니다.
네 callback은 모두 선택 사항이며 아무 callback도 필요 없으면 `ops` 자체를 `NULL`로 전달할 수 있습니다.
Event subscription
~~~~~~~~~~~~~~~~~~
Subscribing to an event is via:
:c:func:`v4l2_event_subscribe <v4l2_event_subscribe>`
(:c:type:`fh <v4l2_fh>`, :c:type:`sub <v4l2_event_subscription>` ,
elems, :c:type:`ops <v4l2_subscribed_event_ops>`)
This function is used to implement :c:type:`video_device`->
:c:type:`ioctl_ops <v4l2_ioctl_ops>`-> ``vidioc_subscribe_event``,
but the driver must check first if the driver is able to produce events
with specified event id, and then should call
:c:func:`v4l2_event_subscribe` to subscribe the event.
The elems argument is the size of the event queue for this event. If it is 0,
then the framework will fill in a default value (this depends on the event
type).
The ops argument allows the driver to specify a number of callbacks:
.. tabularcolumns:: |p{1.5cm}|p{16.0cm}|
======== ==============================================================
Callback Description
======== ==============================================================
add called when a new listener gets added (subscribing to the same
event twice will only cause this callback to get called once)
del called when a listener stops listening
replace replace event 'old' with event 'new'.
merge merge event 'old' into event 'new'.
======== ==============================================================
All 4 callbacks are optional, if you don't want to specify any callbacks
the ops argument itself maybe ``NULL``.
Event 구독 해제
127-142Event 구독 해제는 `v4l2_event_unsubscribe(fh, sub)`로 수행하며 `video_device->ioctl_ops->vidioc_unsubscribe_event` 구현에 사용합니다.
Driver가 구독 해제 과정에 직접 관여할 필요가 없다면 `v4l2_event_unsubscribe()`를 그대로 호출할 수 있습니다.
특수 type `V4L2_EVENT_ALL`은 모든 event 구독을 해제합니다. Driver는 이 값을 별도 방식으로 처리할 수 있습니다.
단일 event 또는 모든 event subscription을 file handle에서 제거합니다.
Unsubscribing an event
~~~~~~~~~~~~~~~~~~~~~~
Unsubscribing to an event is via:
:c:func:`v4l2_event_unsubscribe <v4l2_event_unsubscribe>`
(:c:type:`fh <v4l2_fh>`, :c:type:`sub <v4l2_event_subscription>`)
This function is used to implement :c:type:`video_device`->
:c:type:`ioctl_ops <v4l2_ioctl_ops>`-> ``vidioc_unsubscribe_event``.
A driver may call :c:func:`v4l2_event_unsubscribe` directly unless it
wants to be involved in unsubscription process.
The special type ``V4L2_EVENT_ALL`` may be used to unsubscribe all events. The
drivers may want to handle this in a special way.
Pending event 확인
143-154Pending event 수는 `v4l2_event_pending(fh)`으로 확인합니다.
이 함수가 반환하는 pending event 개수는 file operation의 poll을 구현할 때 유용합니다.
Check if there's a pending event
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Checking if there's a pending event is via:
:c:func:`v4l2_event_pending <v4l2_event_pending>`
(:c:type:`fh <v4l2_fh>`)
This function returns the number of pending events. Useful when implementing
poll.
Event 전달과 private type
155-176Event는 poll system call을 통해 userspace로 전달합니다. Driver는 `poll_wait()` 인자로 `v4l2_fh->wait`의 `wait_queue_head_t`를 사용할 수 있습니다.
Event에는 표준 event와 private event가 있습니다. 새 표준 event는 사용 가능한 가장 작은 event type을 사용해야 합니다.
Driver 전용 event는 driver 고유 class에서 할당합니다. Class base는 `V4L2_EVENT_PRIVATE_START + n * 1000`이며 `n`은 사용 가능한 가장 작은 번호입니다. Class의 첫 event type은 미래 사용을 위해 예약되므로 실제 첫 event는 `class base + 1`입니다.
V4L2 event 사용 예는 OMAP3 ISP driver인 `drivers/media/platform/ti/omap3isp`에서 볼 수 있습니다.
Sub-device는 `V4L2_DEVICE_NOTIFY_EVENT`와 함께 `v4l2_device`의 notify 함수에 event를 직접 보낼 수 있습니다. Bridge는 event를 보낸 sub-device를 연결된 video node에 대응시켜 통지가 필요한 node로 전달합니다.
Queue된 event는 poll로 userspace에 전달되거나 sub-device에서 bridge를 거쳐 관련 node에 매핑됩니다.
How events work
~~~~~~~~~~~~~~~
Events are delivered to user space through the poll system call. The driver
can use :c:type:`v4l2_fh`->wait (a wait_queue_head_t) as the argument for
``poll_wait()``.
There are standard and private events. New standard events must use the
smallest available event type. The drivers must allocate their events from
their own class starting from class base. Class base is
``V4L2_EVENT_PRIVATE_START`` + n * 1000 where n is the lowest available number.
The first event type in the class is reserved for future use, so the first
available event type is 'class base + 1'.
An example on how the V4L2 events may be used can be found in the OMAP
3 ISP driver (``drivers/media/platform/ti/omap3isp``).
A subdev can directly send an event to the :c:type:`v4l2_device` notify
function with ``V4L2_DEVICE_NOTIFY_EVENT``. This allows the bridge to map
the subdev that sends the event to the video node(s) associated with the
subdev that need to be informed about such an event.
V4L2 event 함수와 자료구조
177-181`include/media/v4l2-event.h`의 kernel-doc에서 V4L2 event 함수와 자료구조의 상세 API를 제공합니다.
V4L2 event functions and data structures
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. kernel-doc:: include/media/v4l2-event.h
요약과 해설
v4l2-event.rst:1-181`v4l2_fh`의 subscription별 ring buffer는 event 종류를 격리하고, merge·replace callback은 overflow 때 최종 상태를 보존합니다.