Docs/sound: Add documentation for userspace-driven ALSA timers
Add the documentation which describes the new userspace-driven timers API introduced in this patch series. The documentation contains: - Description of userspace-driven ALSA timers, what they are for - Description of the timers API - Example of how the timers can be created and triggered - How the timers can be used as a timer sources for snd-aloop module Suggested-by: Axel Holzinger <aholzinger@gmx.de> Signed-off-by: Ivan Orlov <ivan.orlov0322@gmail.com> Signed-off-by: Takashi Iwai <tiwai@suse.de> Link: https://patch.msgid.link/20240813120701.171743-3-ivan.orlov0322@gmail.com
This commit is contained in:
parent
e949df0b02
commit
8fad71b677
@ -13,6 +13,7 @@ Sound Subsystem Documentation
|
||||
alsa-configuration
|
||||
hd-audio/index
|
||||
cards/index
|
||||
utimers
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
|
126
Documentation/sound/utimers.rst
Normal file
126
Documentation/sound/utimers.rst
Normal file
@ -0,0 +1,126 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=======================
|
||||
Userspace-driven timers
|
||||
=======================
|
||||
|
||||
:Author: Ivan Orlov <ivan.orlov0322@gmail.com>
|
||||
|
||||
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).
|
||||
|
||||
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
|
||||
===========================
|
||||
|
||||
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
|
||||
-------------------------------------
|
||||
|
||||
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.
|
Loading…
Reference in New Issue
Block a user