← Documents Documentation/sound/utimers.rst GitHub 원문 ↗

Linux 6.18.37 · Sound / ALSA

사용자 공간 구동 타이머

사용자 공간 응용 프로그램이 `/dev/snd/timer`와 `SNDRV_TIMER_IOCTL_CREATE`·`TRIGGER`로 가상 ALSA 타이머를 생성하고 tick시키는 방법을 설명합니다. `snd_timer_uinfo`의 해상도·fd·id, 128개 제한, 생성 프로세스의 trigger 권한과 `snd-aloop timer_source=-1.4.<utimer_id>` 연동 및 period 해상도 계산식을 원문 코드와 함께 정리합니다.

Source pathDocumentation/sound/utimers.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

utimers.rst:1-126

사용자 공간 응용 프로그램이 `/dev/snd/timer`와 `SNDRV_TIMER_IOCTL_CREATE`·`TRIGGER`로 가상 ALSA 타이머를 생성하고 tick시키는 방법을 설명합니다. `snd_timer_uinfo`의 해상도·fd·id, 128개 제한, 생성 프로세스의 trigger 권한과 `snd-aloop timer_source=-1.4.<utimer_id>` 연동 및 period 해상도 계산식을 원문 코드와 함께 정리합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 =======================
4 Userspace-driven timers
5 =======================
6
7 :Author: Ivan Orlov <[email protected]>
8
9 Preface
10 =======
11
12 This document describes the userspace-driven timers: virtual ALSA timers
13 which could be created and controlled by userspace applications using
14 IOCTL calls. Such timers could be useful when synchronizing audio
15 stream with timer sources which we don't have ALSA timers exported for
16 (e.g. PTP clocks), and when synchronizing the audio stream going through
17 two virtual sound devices using ``snd-aloop`` (for instance, when
18 we have a network application sending frames to one snd-aloop device,
19 and another sound application listening on the other end of snd-aloop).
20
21 Enabling userspace-driven timers
22 ================================
23
24 The userspace-driven timers could be enabled in the kernel using the
25 ``CONFIG_SND_UTIMER`` configuration option. It depends on the
26 ``CONFIG_SND_TIMER`` option, so it also should be enabled.
27
28 Userspace-driven timers API
29 ===========================
30
31 Userspace application can create a userspace-driven ALSA timer by
32 executing the ``SNDRV_TIMER_IOCTL_CREATE`` ioctl call on the
33 ``/dev/snd/timer`` device file descriptor. The ``snd_timer_uinfo``
34 structure should be passed as an ioctl argument:
35
36 ::
37
38 struct snd_timer_uinfo {
39 __u64 resolution;
40 int fd;
41 unsigned int id;
42 unsigned char reserved[16];
43 }
44
45 The ``resolution`` field sets the desired resolution in nanoseconds for
46 the virtual timer. ``resolution`` field simply provides an information
47 about the virtual timer, but does not affect the timing itself. ``id``
48 field gets overwritten by the ioctl, and the identifier you get in this
49 field after the call can be used as a timer subdevice number when
50 passing the timer to ``snd-aloop`` kernel module or other userspace
51 applications. There could be up to 128 userspace-driven timers in the
52 system at one moment of time, thus the id value ranges from 0 to 127.
53
54 Besides from overwriting the ``snd_timer_uinfo`` struct, ioctl stores
55 a timer file descriptor, which can be used to trigger the timer, in the
56 ``fd`` field of the ``snd_timer_uinfo`` struct. Allocation of a file
57 descriptor for the timer guarantees that the timer can only be triggered
58 by the process which created it. The timer then can be triggered with
59 ``SNDRV_TIMER_IOCTL_TRIGGER`` ioctl call on the timer file descriptor.
60
61 So, the example code for creating and triggering the timer would be:
62
63 ::
64
65 static struct snd_timer_uinfo utimer_info = {
66 /* Timer is going to tick (presumably) every 1000000 ns */
67 .resolution = 1000000ULL,
68 .id = -1,
69 };
70
71 int timer_device_fd = open("/dev/snd/timer", O_RDWR | O_CLOEXEC);
72
73 if (ioctl(timer_device_fd, SNDRV_TIMER_IOCTL_CREATE, &utimer_info)) {
74 perror("Failed to create the timer");
75 return -1;
76 }
77
78 ...
79
80 /*
81 * Now we want to trigger the timer. Callbacks of all of the
82 * timer instances binded to this timer will be executed after
83 * this call.
84 */
85 ioctl(utimer_info.fd, SNDRV_TIMER_IOCTL_TRIGGER, NULL);
86
87 ...
88
89 /* Now, destroy the timer */
90 close(timer_info.fd);
91
92
93 More detailed example of creating and ticking the timer could be found
94 in the utimer ALSA selftest.
95
96 Userspace-driven timers and snd-aloop
97 -------------------------------------
98
99 Userspace-driven timers could be easily used with ``snd-aloop`` module
100 when synchronizing two sound applications on both ends of the virtual
101 sound loopback. For instance, if one of the applications receives sound
102 frames from network and sends them to snd-aloop pcm device, and another
103 application listens for frames on the other snd-aloop pcm device, it
104 makes sense that the ALSA middle layer should initiate a data
105 transaction when the new period of data is received through network, but
106 not when the certain amount of jiffies elapses. Userspace-driven ALSA
107 timers could be used to achieve this.
108
109 To use userspace-driven ALSA timer as a timer source of snd-aloop, pass
110 the following string as the snd-aloop ``timer_source`` parameter:
111
112 ::
113
114 # modprobe snd-aloop timer_source="-1.4.<utimer_id>"
115
116 Where ``utimer_id`` is the id of the timer you created with
117 ``SNDRV_TIMER_IOCTL_CREATE``, and ``4`` is the number of
118 userspace-driven timers device (``SNDRV_TIMER_GLOBAL_UDRIVEN``).
119
120 ``resolution`` for the userspace-driven ALSA timer used with snd-aloop
121 should be calculated as ``1000000000ULL / frame_rate * period_size`` as
122 the timer is going to tick every time a new period of frames is ready.
123
124 After that, each time you trigger the timer with
125 ``SNDRV_TIMER_IOCTL_TRIGGER`` the new period of data will be transferred
126 from one snd-aloop device to another.
127

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

개요와 사용 사례

1-20

이 문서는 사용자 공간 응용 프로그램이 IOCTL 호출로 만들고 제어할 수 있는 가상 ALSA 타이머인 userspace-driven timer를 설명한다. 저자는 Ivan Orlov이다.

이 타이머는 ALSA 타이머로 내보내지 않은 PTP clock 같은 타이머 소스에 오디오 스트림을 동기화할 때 유용하다.

또한 `snd-aloop`을 사용하는 두 가상 사운드 장치를 통과하는 오디오 스트림을 동기화할 수 있다. 예를 들어 네트워크 응용 프로그램이 한 snd-aloop 장치에 프레임을 보내고 다른 사운드 응용 프로그램이 snd-aloop 반대편에서 수신하는 구성을 맞출 수 있다.

사용자 공간 구동 타이머의 용도
사용 사례동기화 대상효과
외부 clockPTP clock 등 ALSA timer로 노출되지 않은 소스오디오 스트림을 외부 시간 기준에 맞춤
snd-alooploopback 양 끝의 두 사운드 응용 프로그램새 프레임이 준비된 시점에 데이터 전달

ALSA가 직접 제공하지 않는 외부 시점과 가상 오디오 경로를 연결한다.

네트워크와 snd-aloop 동기화
Network application receives framesUserspace timer triggersnd-aloop inputsnd-aloop outputListening sound application

네트워크 수신 시점이 가상 loopback 전송의 tick을 결정한다.

.. SPDX-License-Identifier: GPL-2.0

=======================
Userspace-driven timers
=======================

:Author: Ivan Orlov <[email protected]>

Preface
=======

This document describes the userspace-driven timers: virtual ALSA timers
which could be created and controlled by userspace applications using
IOCTL calls. Such timers could be useful when synchronizing audio
stream with timer sources which we don't have ALSA timers exported for
(e.g. PTP clocks), and when synchronizing the audio stream going through
two virtual sound devices using ``snd-aloop`` (for instance, when
we have a network application sending frames to one snd-aloop device,
and another sound application listening on the other end of snd-aloop).

커널 설정에서 활성화

21-28

사용자 공간 구동 타이머는 커널의 `CONFIG_SND_UTIMER` 설정 옵션으로 활성화한다. 이 옵션은 `CONFIG_SND_TIMER`에 의존하므로 두 옵션을 모두 켜야 한다.

설정 의존성
CONFIG_SND_TIMERCONFIG_SND_UTIMERUserspace-driven ALSA timers

기본 ALSA timer 지원 위에 userspace-driven timer 기능을 활성화한다.

Enabling userspace-driven timers
================================

The userspace-driven timers could be enabled in the kernel using the
``CONFIG_SND_UTIMER`` configuration option. It depends on the
``CONFIG_SND_TIMER`` option, so it also should be enabled.

Userspace-driven timers API

생성·트리거·파괴 API

29-98

사용자 공간 응용 프로그램은 `/dev/snd/timer` 장치의 파일 디스크립터에 `SNDRV_TIMER_IOCTL_CREATE` ioctl을 실행해 사용자 공간 구동 ALSA 타이머를 만든다. ioctl 인수로 `struct snd_timer_uinfo`를 전달한다.

`snd_timer_uinfo`에는 `__u64 resolution`, `int fd`, `unsigned int id`, `unsigned char reserved[16]` 필드가 있다.

struct snd_timer_uinfo 필드
필드입력·출력의미
resolution입력가상 타이머의 원하는 해상도, 나노초 단위
fd출력생성된 타이머를 trigger하는 파일 디스크립터
id출력0~127 범위의 타이머 식별자·subdevice 번호
reserved[16]예약향후 확장을 위한 예약 바이트

타이머 생성 요청과 ioctl 반환 정보를 함께 담는다.

`resolution`은 가상 타이머에 관한 정보만 제공하며 실제 타이밍 자체에는 영향을 주지 않는다. `id`는 ioctl이 덮어쓰며, 호출 뒤 받은 값은 타이머를 `snd-aloop` 커널 모듈이나 다른 사용자 공간 응용 프로그램에 전달할 때 timer subdevice 번호로 사용할 수 있다.

한 시점에 시스템에는 최대 128개의 사용자 공간 구동 타이머가 존재할 수 있으므로 `id` 값의 범위는 0부터 127까지다.

ioctl은 `snd_timer_uinfo`를 갱신할 뿐 아니라 타이머를 trigger하는 데 쓸 파일 디스크립터를 구조체의 `fd` 필드에 저장한다. 타이머용 파일 디스크립터를 할당함으로써 타이머를 만든 프로세스만 이를 trigger할 수 있음을 보장한다.

생성된 타이머는 그 타이머 파일 디스크립터에 `SNDRV_TIMER_IOCTL_TRIGGER` ioctl을 실행해 tick시킨다.

타이머 생성과 제어
open /dev/snd/timerSNDRV_TIMER_IOCTL_CREATE + snd_timer_uinfoReceive utimer_info.fd and idSNDRV_TIMER_IOCTL_TRIGGER on fdBound timer instance callbacks

장치 파일을 연 뒤 CREATE가 전용 fd와 id를 반환하고 TRIGGER가 callback을 실행한다.

예제는 `utimer_info.resolution`을 `1000000ULL`로 설정해 타이머가 대략 1,000,000ns마다 tick할 것이라는 정보를 제공하고, 생성 전 `id`를 -1로 초기화한다. `/dev/snd/timer`는 `O_RDWR | O_CLOEXEC`로 연다.

`SNDRV_TIMER_IOCTL_CREATE` 호출이 실패하면 오류를 출력하고 -1을 반환한다. 성공한 뒤 `ioctl(utimer_info.fd, SNDRV_TIMER_IOCTL_TRIGGER, NULL)`을 호출하면 이 타이머에 bind된 모든 timer instance의 callback이 실행된다.

사용을 마치면 원문 예제의 `close(timer_info.fd)` 호출로 타이머를 파괴한다. 타이머 생성과 tick의 더 자세한 예제는 utimer ALSA selftest에서 찾을 수 있다.

예제의 수명 주기
단계호출결과
장치 열기open("/dev/snd/timer", O_RDWR | O_CLOEXEC)timer device fd 확보
생성SNDRV_TIMER_IOCTL_CREATEutimer_info.id와 utimer_info.fd 반환
tickSNDRV_TIMER_IOCTL_TRIGGERbind된 timer instance callback 실행
파괴close(timer_info.fd)타이머 수명 종료

코드 블록의 주요 호출과 상태 변화를 정리한다.

파일 디스크립터 소유권
Creating processSNDRV_TIMER_IOCTL_CREATEAllocated timer fdOnly creator can trigger timer

CREATE가 반환한 전용 fd로 생성 프로세스의 trigger 권한을 제한한다.

===========================

Userspace application can create a userspace-driven ALSA timer by
executing the ``SNDRV_TIMER_IOCTL_CREATE`` ioctl call on the
``/dev/snd/timer`` device file descriptor. The ``snd_timer_uinfo``
structure should be passed as an ioctl argument:

::

    struct snd_timer_uinfo {
        __u64 resolution;
        int fd;
        unsigned int id;
        unsigned char reserved[16];
    }

The ``resolution`` field sets the desired resolution in nanoseconds for
the virtual timer. ``resolution`` field simply provides an information
about the virtual timer, but does not affect the timing itself. ``id``
field gets overwritten by the ioctl, and the identifier you get in this
field after the call can be used as a timer subdevice number when
passing the timer to ``snd-aloop`` kernel module or other userspace
applications. There could be up to 128 userspace-driven timers in the
system at one moment of time, thus the id value ranges from 0 to 127.

Besides from overwriting the ``snd_timer_uinfo`` struct, ioctl stores
a timer file descriptor, which can be used to trigger the timer, in the
``fd`` field of the ``snd_timer_uinfo`` struct. Allocation of a file
descriptor for the timer guarantees that the timer can only be triggered
by the process which created it. The timer then can be triggered with
``SNDRV_TIMER_IOCTL_TRIGGER`` ioctl call on the timer file descriptor.

So, the example code for creating and triggering the timer would be:

::

    static struct snd_timer_uinfo utimer_info = {
        /* Timer is going to tick (presumably) every 1000000 ns */
        .resolution = 1000000ULL,
        .id = -1,
    };

    int timer_device_fd = open("/dev/snd/timer",  O_RDWR | O_CLOEXEC);

    if (ioctl(timer_device_fd, SNDRV_TIMER_IOCTL_CREATE, &utimer_info)) {
        perror("Failed to create the timer");
        return -1;
    }

    ...

    /*
     * Now we want to trigger the timer. Callbacks of all of the
     * timer instances binded to this timer will be executed after
     * this call.
     */
    ioctl(utimer_info.fd, SNDRV_TIMER_IOCTL_TRIGGER, NULL);

    ...

    /* Now, destroy the timer */
    close(timer_info.fd);


More detailed example of creating and ticking the timer could be found
in the utimer ALSA selftest.

Userspace-driven timers and snd-aloop
-------------------------------------

snd-aloop의 타이머 소스로 사용

99-126

사용자 공간 구동 타이머는 가상 사운드 loopback 양 끝의 두 응용 프로그램을 동기화할 때 `snd-aloop` 모듈과 쉽게 함께 사용할 수 있다.

한 응용 프로그램이 네트워크에서 사운드 프레임을 받아 snd-aloop PCM 장치로 보내고 다른 응용 프로그램이 반대편 PCM 장치에서 프레임을 듣는다면, ALSA 중간 계층은 일정한 jiffies가 지난 때가 아니라 네트워크로 새 데이터 period를 받은 때 데이터 전송을 시작하는 것이 적절하다. 사용자 공간 구동 ALSA 타이머가 이 시점을 제공한다.

userspace-driven ALSA timer를 snd-aloop의 타이머 소스로 사용하려면 `snd-aloop`의 `timer_source` 매개변수에 `-1.4.<utimer_id>` 형식의 문자열을 전달한다. 예제 명령은 `modprobe snd-aloop timer_source="-1.4.<utimer_id>"`이다.

timer_source 구성 요소
부분의미
-1global timer를 지정하는 카드 위치
4userspace-driven timer 장치 번호 SNDRV_TIMER_GLOBAL_UDRIVEN
utimer_idSNDRV_TIMER_IOCTL_CREATE로 받은 타이머 id

-1.4.&lt;utimer_id&gt; 문자열의 의미를 분해한다.

`utimer_id`는 `SNDRV_TIMER_IOCTL_CREATE`로 만든 타이머의 id이며, `4`는 사용자 공간 구동 타이머 장치 `SNDRV_TIMER_GLOBAL_UDRIVEN`의 번호다.

snd-aloop과 함께 쓰는 userspace-driven ALSA timer의 `resolution`은 `1000000000ULL / frame_rate * period_size`로 계산해야 한다. 새 프레임 period가 준비될 때마다 타이머가 tick하기 때문이다.

snd-aloop 해상도 계산
단위와 의미
1000000000ULL1초의 나노초 수
/ frame_rate프레임 하나의 시간
* period_sizeperiod 하나의 전체 시간

한 period에 해당하는 나노초를 타이머 정보로 설정한다.

그 뒤 `SNDRV_TIMER_IOCTL_TRIGGER`로 타이머를 trigger할 때마다 새 데이터 period가 한 snd-aloop 장치에서 다른 장치로 전송된다.

snd-aloop period 전달
New network period readySNDRV_TIMER_IOCTL_TRIGGERUserspace-driven ALSA timer ticksnd-aloop transfers one periodPeer sound application receives frames

네트워크에서 period가 준비되는 순간을 명시적인 timer tick으로 바꾼다.

Userspace-driven timers could be easily used with ``snd-aloop`` module
when synchronizing two sound applications on both ends of the virtual
sound loopback. For instance, if one of the applications receives sound
frames from network and sends them to snd-aloop pcm device, and another
application listens for frames on the other snd-aloop pcm device, it
makes sense that the ALSA middle layer should initiate a data
transaction when the new period of data is received through network, but
not when the certain amount of jiffies elapses. Userspace-driven ALSA
timers could be used to achieve this.

To use userspace-driven ALSA timer as a timer source of snd-aloop, pass
the following string as the snd-aloop ``timer_source`` parameter:

::

  # modprobe snd-aloop timer_source="-1.4.<utimer_id>"

Where ``utimer_id`` is the id of the timer you created with
``SNDRV_TIMER_IOCTL_CREATE``, and ``4`` is the number of
userspace-driven timers device (``SNDRV_TIMER_GLOBAL_UDRIVEN``).

``resolution`` for the userspace-driven ALSA timer used with snd-aloop
should be calculated as ``1000000000ULL / frame_rate * period_size`` as
the timer is going to tick every time a new period of frames is ready.

After that, each time you trigger the timer with
``SNDRV_TIMER_IOCTL_TRIGGER`` the new period of data will be transferred
from one snd-aloop device to another.