요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
.. c:namespace:: V4L
.. _dmabuf:
************************************
Streaming I/O (DMA buffer importing)
************************************
The DMABUF framework provides a generic method for sharing buffers
between multiple devices. Device drivers that support DMABUF can export
a DMA buffer to userspace as a file descriptor (known as the exporter
role), import a DMA buffer from userspace using a file descriptor
previously exported for a different or the same device (known as the
importer role), or both. This section describes the DMABUF importer role
API in V4L2.
Refer to :ref:`DMABUF exporting <VIDIOC_EXPBUF>` for details about
exporting V4L2 buffers as DMABUF file descriptors.
Input and output devices support the streaming I/O method when the
``V4L2_CAP_STREAMING`` flag in the ``capabilities`` field of struct
:c:type:`v4l2_capability` returned by the
:ref:`VIDIOC_QUERYCAP <VIDIOC_QUERYCAP>` ioctl is set. Whether
importing DMA buffers through DMABUF file descriptors is supported is
determined by calling the :ref:`VIDIOC_REQBUFS <VIDIOC_REQBUFS>`
ioctl with the memory type set to ``V4L2_MEMORY_DMABUF``.
This I/O method is dedicated to sharing DMA buffers between different
devices, which may be V4L devices or other video-related devices (e.g.
DRM). Buffers (planes) are allocated by a driver on behalf of an
application. Next, these buffers are exported to the application as file
descriptors using an API which is specific for an allocator driver. Only
such file descriptor are exchanged. The descriptors and meta-information
are passed in struct :c:type:`v4l2_buffer` (or in struct
:c:type:`v4l2_plane` in the multi-planar API case). The
driver must be switched into DMABUF I/O mode by calling the
:ref:`VIDIOC_REQBUFS <VIDIOC_REQBUFS>` with the desired buffer type.
Example: Initiating streaming I/O with DMABUF file descriptors
==============================================================
.. code-block:: c
struct v4l2_requestbuffers reqbuf;
memset(&reqbuf, 0, sizeof (reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_DMABUF;
reqbuf.count = 1;
if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
if (errno == EINVAL)
printf("Video capturing or DMABUF streaming is not supported\\n");
else
perror("VIDIOC_REQBUFS");
exit(EXIT_FAILURE);
}
The buffer (plane) file descriptor is passed on the fly with the
:ref:`VIDIOC_QBUF <VIDIOC_QBUF>` ioctl. In case of multiplanar
buffers, every plane can be associated with a different DMABUF
descriptor. Although buffers are commonly cycled, applications can pass
a different DMABUF descriptor at each :ref:`VIDIOC_QBUF <VIDIOC_QBUF>` call.
Example: Queueing DMABUF using single plane API
===============================================
.. code-block:: c
int buffer_queue(int v4lfd, int index, int dmafd)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_DMABUF;
buf.index = index;
buf.m.fd = dmafd;
if (ioctl(v4lfd, VIDIOC_QBUF, &buf) == -1) {
perror("VIDIOC_QBUF");
return -1;
}
return 0;
}
Example 3.6. Queueing DMABUF using multi plane API
==================================================
.. code-block:: c
int buffer_queue_mp(int v4lfd, int index, int dmafd[], int n_planes)
{
struct v4l2_buffer buf;
struct v4l2_plane planes[VIDEO_MAX_PLANES];
int i;
memset(&buf, 0, sizeof buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
buf.memory = V4L2_MEMORY_DMABUF;
buf.index = index;
buf.m.planes = planes;
buf.length = n_planes;
memset(&planes, 0, sizeof planes);
for (i = 0; i < n_planes; ++i)
buf.m.planes[i].m.fd = dmafd[i];
if (ioctl(v4lfd, VIDIOC_QBUF, &buf) == -1) {
perror("VIDIOC_QBUF");
return -1;
}
return 0;
}
Captured or displayed buffers are dequeued with the
:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl. The driver can unlock the
buffer at any time between the completion of the DMA and this ioctl. The
memory is also unlocked when
:ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` is called,
:ref:`VIDIOC_REQBUFS <VIDIOC_REQBUFS>`, or when the device is closed.
For capturing applications it is customary to enqueue a number of empty
buffers, to start capturing and enter the read loop. Here the
application waits until a filled buffer can be dequeued, and re-enqueues
the buffer when the data is no longer needed. Output applications fill
and enqueue buffers, when enough buffers are stacked up output is
started. In the write loop, when the application runs out of free
buffers it must wait until an empty buffer can be dequeued and reused.
Two methods exist to suspend execution of the application until one or
more buffers can be dequeued. By default :ref:`VIDIOC_DQBUF
<VIDIOC_QBUF>` blocks when no buffer is in the outgoing queue. When the
``O_NONBLOCK`` flag was given to the :c:func:`open()` function,
:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` returns immediately with an ``EAGAIN``
error code when no buffer is available. The
:c:func:`select()` and :c:func:`poll()`
functions are always available.
To start and stop capturing or displaying applications call the
:ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>` and
:ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` ioctls.
.. note::
:ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` removes all buffers from
both queues and unlocks all buffers as a side effect. Since there is no
notion of doing anything "now" on a multitasking system, if an
application needs to synchronize with another event it should examine
the struct :c:type:`v4l2_buffer` ``timestamp`` of captured or
outputted buffers.
Drivers implementing DMABUF importing I/O must support the
:ref:`VIDIOC_REQBUFS <VIDIOC_REQBUFS>`, :ref:`VIDIOC_QBUF <VIDIOC_QBUF>`,
:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>`, :ref:`VIDIOC_STREAMON
<VIDIOC_STREAMON>` and :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` ioctls,
and the :c:func:`select()` and :c:func:`poll()`
functions.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
DMA-BUF 공유와 importer mode
1-39DMA-BUF framework는 여러 device가 buffer를 공유하는 범용 방법을 제공합니다. DMA-BUF 지원 driver는 DMA buffer를 file descriptor로 user space에 내보내는 exporter, 같은 장치나 다른 장치에서 먼저 export한 descriptor를 가져오는 importer, 또는 두 역할을 모두 구현할 수 있습니다. 이 절은 V4L2 importer API를 설명합니다.
V4L2 buffer를 DMA-BUF file descriptor로 export하는 절차는 `DMABUF exporting <VIDIOC_EXPBUF>` 절을 참조합니다.
Input/output device가 streaming I/O를 지원하면 `VIDIOC_QUERYCAP`이 반환한 `v4l2_capability.capabilities`에 `V4L2_CAP_STREAMING`이 설정됩니다. DMA-BUF descriptor import 지원 여부는 memory type을 `V4L2_MEMORY_DMABUF`로 설정해 `VIDIOC_REQBUFS`를 호출하여 판별합니다.
이 방식은 V4L 장치와 DRM 같은 다른 video 관련 장치 사이에서 DMA buffer를 공유하도록 설계됐습니다. Driver가 application을 대신해 buffer 또는 plane을 할당하고 allocator driver 전용 API로 file descriptor를 export합니다. 장치 사이에는 descriptor만 교환합니다.
Descriptor와 meta-information은 single-planar API에서는 `v4l2_buffer`, multi-planar API에서는 `v4l2_plane`에 전달합니다. 원하는 buffer type으로 `VIDIOC_REQBUFS`를 호출해야 driver가 DMA-BUF I/O mode로 전환됩니다.
Buffer 소유권과 file descriptor 이동 방향입니다.
실제 pixel data를 복사하지 않고 descriptor로 같은 storage를 참조합니다.
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
.. c:namespace:: V4L
.. _dmabuf:
************************************
Streaming I/O (DMA buffer importing)
************************************
The DMABUF framework provides a generic method for sharing buffers
between multiple devices. Device drivers that support DMABUF can export
a DMA buffer to userspace as a file descriptor (known as the exporter
role), import a DMA buffer from userspace using a file descriptor
previously exported for a different or the same device (known as the
importer role), or both. This section describes the DMABUF importer role
API in V4L2.
Refer to :ref:`DMABUF exporting <VIDIOC_EXPBUF>` for details about
exporting V4L2 buffers as DMABUF file descriptors.
Input and output devices support the streaming I/O method when the
``V4L2_CAP_STREAMING`` flag in the ``capabilities`` field of struct
:c:type:`v4l2_capability` returned by the
:ref:`VIDIOC_QUERYCAP <VIDIOC_QUERYCAP>` ioctl is set. Whether
importing DMA buffers through DMABUF file descriptors is supported is
determined by calling the :ref:`VIDIOC_REQBUFS <VIDIOC_REQBUFS>`
ioctl with the memory type set to ``V4L2_MEMORY_DMABUF``.
This I/O method is dedicated to sharing DMA buffers between different
devices, which may be V4L devices or other video-related devices (e.g.
DRM). Buffers (planes) are allocated by a driver on behalf of an
application. Next, these buffers are exported to the application as file
descriptors using an API which is specific for an allocator driver. Only
such file descriptor are exchanged. The descriptors and meta-information
are passed in struct :c:type:`v4l2_buffer` (or in struct
:c:type:`v4l2_plane` in the multi-planar API case). The
driver must be switched into DMABUF I/O mode by calling the
:ref:`VIDIOC_REQBUFS <VIDIOC_REQBUFS>` with the desired buffer type.
DMA-BUF streaming 초기화 예제
40-66예제는 `v4l2_requestbuffers reqbuf`를 0으로 초기화한 뒤 `type = V4L2_BUF_TYPE_VIDEO_CAPTURE`, `memory = V4L2_MEMORY_DMABUF`, `count = 1`을 설정합니다.
`ioctl(fd, VIDIOC_REQBUFS, &reqbuf)`가 실패하고 `errno == EINVAL`이면 video capture 또는 DMA-BUF streaming을 지원하지 않는다는 뜻입니다. 다른 오류는 `perror("VIDIOC_REQBUFS")`로 보고하고 process를 종료합니다.
초기화 예제의 핵심 field입니다.
REQBUFS 호출이 지원 확인과 mode 전환을 함께 수행합니다.
Example: Initiating streaming I/O with DMABUF file descriptors
==============================================================
.. code-block:: c
struct v4l2_requestbuffers reqbuf;
memset(&reqbuf, 0, sizeof (reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_DMABUF;
reqbuf.count = 1;
if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
if (errno == EINVAL)
printf("Video capturing or DMABUF streaming is not supported\\n");
else
perror("VIDIOC_REQBUFS");
exit(EXIT_FAILURE);
}
The buffer (plane) file descriptor is passed on the fly with the
:ref:`VIDIOC_QBUF <VIDIOC_QBUF>` ioctl. In case of multiplanar
buffers, every plane can be associated with a different DMABUF
descriptor. Although buffers are commonly cycled, applications can pass
a different DMABUF descriptor at each :ref:`VIDIOC_QBUF <VIDIOC_QBUF>` call.
Single-plane DMA-BUF queue
67-89Buffer 또는 plane file descriptor는 `VIDIOC_QBUF` 호출 때마다 즉석에서 전달합니다. Buffer를 순환 재사용하는 경우가 많지만 application은 QBUF 호출마다 다른 DMA-BUF descriptor를 전달할 수도 있습니다.
Single-plane `buffer_queue()` 예제는 `v4l2_buffer`를 0으로 초기화하고 capture type, `V4L2_MEMORY_DMABUF`, buffer `index`를 설정한 뒤 `buf.m.fd = dmafd`로 descriptor를 넣습니다. `VIDIOC_QBUF` 실패 시 -1, 성공 시 0을 반환합니다.
v4l2_buffer에 채우는 값입니다.
Example: Queueing DMABUF using single plane API
===============================================
.. code-block:: c
int buffer_queue(int v4lfd, int index, int dmafd)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_DMABUF;
buf.index = index;
buf.m.fd = dmafd;
if (ioctl(v4lfd, VIDIOC_QBUF, &buf) == -1) {
perror("VIDIOC_QBUF");
return -1;
}
return 0;
}
Multi-plane DMA-BUF queue
90-120Multi-planar buffer에서는 각 plane을 서로 다른 DMA-BUF descriptor와 연결할 수 있습니다.
`buffer_queue_mp()` 예제는 `v4l2_buffer`와 `v4l2_plane planes[VIDEO_MAX_PLANES]`를 준비합니다. Type은 `V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE`, memory는 `V4L2_MEMORY_DMABUF`이며 `buf.m.planes = planes`, `buf.length = n_planes`로 plane array를 연결합니다.
Plane array를 0으로 초기화한 다음 `i = 0 ... n_planes - 1` loop에서 `buf.m.planes[i].m.fd = dmafd[i]`를 설정하고 `VIDIOC_QBUF`로 한 buffer의 모든 plane을 enqueue합니다.
Buffer 공통 정보와 plane별 descriptor 배치입니다.
각 plane descriptor를 하나의 queued buffer로 묶습니다.
Example 3.6. Queueing DMABUF using multi plane API
==================================================
.. code-block:: c
int buffer_queue_mp(int v4lfd, int index, int dmafd[], int n_planes)
{
struct v4l2_buffer buf;
struct v4l2_plane planes[VIDEO_MAX_PLANES];
int i;
memset(&buf, 0, sizeof buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
buf.memory = V4L2_MEMORY_DMABUF;
buf.index = index;
buf.m.planes = planes;
buf.length = n_planes;
memset(&planes, 0, sizeof planes);
for (i = 0; i < n_planes; ++i)
buf.m.planes[i].m.fd = dmafd[i];
if (ioctl(v4lfd, VIDIOC_QBUF, &buf) == -1) {
perror("VIDIOC_QBUF");
return -1;
}
return 0;
}
Dequeue, blocking과 stream 수명주기
121-156Capture 또는 display가 끝난 buffer는 `VIDIOC_DQBUF`로 dequeue합니다. Driver는 DMA 완료 시점과 DQBUF 사이 언제든 buffer lock을 해제할 수 있습니다. `VIDIOC_STREAMOFF`, `VIDIOC_REQBUFS` 호출 또는 device close 때도 memory가 unlock됩니다.
Capture application은 보통 empty buffer 여러 개를 enqueue하고 capture를 시작한 뒤 read loop에 들어갑니다. Filled buffer를 dequeue할 수 있을 때까지 기다리고 data가 더는 필요하지 않으면 그 buffer를 다시 enqueue합니다.
Output application은 buffer를 채워 enqueue하고 충분히 쌓이면 output을 시작합니다. Write loop에서 free buffer가 떨어지면 empty buffer가 dequeue되어 재사용 가능해질 때까지 기다립니다.
기본적으로 outgoing queue에 buffer가 없으면 `VIDIOC_DQBUF`가 block합니다. `open()`에 `O_NONBLOCK`을 주었다면 사용할 buffer가 없을 때 즉시 `EAGAIN`을 반환합니다. `select()`와 `poll()`은 항상 사용할 수 있습니다.
Capture 또는 display 시작·중지는 `VIDIOC_STREAMON`과 `VIDIOC_STREAMOFF`를 사용합니다. STREAMOFF는 양쪽 queue에서 모든 buffer를 제거하고 전부 unlock하는 부수 효과가 있습니다.
Multitasking system에는 정확한 'now' 개념이 없으므로 다른 event와 동기화해야 하면 capture/output `v4l2_buffer.timestamp`를 검사해야 합니다.
Importer가 DMA-BUF를 다시 사용할 수 있게 되는 조건입니다.
Empty buffer를 공급하고 filled buffer를 소비합니다.
Filled buffer를 공급하고 재사용할 empty buffer를 회수합니다.
Captured or displayed buffers are dequeued with the
:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl. The driver can unlock the
buffer at any time between the completion of the DMA and this ioctl. The
memory is also unlocked when
:ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` is called,
:ref:`VIDIOC_REQBUFS <VIDIOC_REQBUFS>`, or when the device is closed.
For capturing applications it is customary to enqueue a number of empty
buffers, to start capturing and enter the read loop. Here the
application waits until a filled buffer can be dequeued, and re-enqueues
the buffer when the data is no longer needed. Output applications fill
and enqueue buffers, when enough buffers are stacked up output is
started. In the write loop, when the application runs out of free
buffers it must wait until an empty buffer can be dequeued and reused.
Two methods exist to suspend execution of the application until one or
more buffers can be dequeued. By default :ref:`VIDIOC_DQBUF
<VIDIOC_QBUF>` blocks when no buffer is in the outgoing queue. When the
``O_NONBLOCK`` flag was given to the :c:func:`open()` function,
:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` returns immediately with an ``EAGAIN``
error code when no buffer is available. The
:c:func:`select()` and :c:func:`poll()`
functions are always available.
To start and stop capturing or displaying applications call the
:ref:`VIDIOC_STREAMON <VIDIOC_STREAMON>` and
:ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` ioctls.
.. note::
:ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` removes all buffers from
both queues and unlocks all buffers as a side effect. Since there is no
notion of doing anything "now" on a multitasking system, if an
application needs to synchronize with another event it should examine
the struct :c:type:`v4l2_buffer` ``timestamp`` of captured or
outputted buffers.
Importer driver 필수 operation
157-162DMA-BUF importing I/O driver는 `VIDIOC_REQBUFS`, `VIDIOC_QBUF`, `VIDIOC_DQBUF`, `VIDIOC_STREAMON`, `VIDIOC_STREAMOFF` ioctl과 `select()`, `poll()` function을 모두 지원해야 합니다.
Driver 구현에 요구되는 operation입니다.
Drivers implementing DMABUF importing I/O must support the
:ref:`VIDIOC_REQBUFS <VIDIOC_REQBUFS>`, :ref:`VIDIOC_QBUF <VIDIOC_QBUF>`,
:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>`, :ref:`VIDIOC_STREAMON
<VIDIOC_STREAMON>` and :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` ioctls,
and the :c:func:`select()` and :c:func:`poll()`
functions.
요약·해설
dmabuf.rst:1-162DMA-BUF importer는 pixel data를 복사하지 않고 다른 장치가 export한 file descriptor를 QBUF 때 연결합니다. REQBUFS의 memory type을 V4L2_MEMORY_DMABUF로 설정해야 이 mode와 지원 여부가 확정됩니다.
Single-plane은 v4l2_buffer.m.fd, multi-plane은 각 v4l2_plane.m.fd를 사용합니다. STREAMOFF는 양쪽 queue를 비우고 모든 imported buffer를 unlock하므로 timestamp 기반 동기화와 수명주기 관리가 중요합니다.