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