| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- .. 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.
|