1.. SPDX-License-Identifier: GPL-2.0 2 3======================= 4Userspace-driven timers 5======================= 6 7:Author: Ivan Orlov <ivan.orlov0322@gmail.com> 8 9Preface 10======= 11 12This document describes the userspace-driven timers: virtual ALSA timers 13which could be created and controlled by userspace applications using 14IOCTL calls. Such timers could be useful when synchronizing audio 15stream 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 17two virtual sound devices using ``snd-aloop`` (for instance, when 18we have a network application sending frames to one snd-aloop device, 19and another sound application listening on the other end of snd-aloop). 20 21Enabling userspace-driven timers 22================================ 23 24The 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 28Userspace-driven timers API 29=========================== 30 31Userspace application can create a userspace-driven ALSA timer by 32executing the ``SNDRV_TIMER_IOCTL_CREATE`` ioctl call on the 33``/dev/snd/timer`` device file descriptor. The ``snd_timer_uinfo`` 34structure 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 45The ``resolution`` field sets the desired resolution in nanoseconds for 46the virtual timer. ``resolution`` field simply provides an information 47about the virtual timer, but does not affect the timing itself. ``id`` 48field gets overwritten by the ioctl, and the identifier you get in this 49field after the call can be used as a timer subdevice number when 50passing the timer to ``snd-aloop`` kernel module or other userspace 51applications. There could be up to 128 userspace-driven timers in the 52system at one moment of time, thus the id value ranges from 0 to 127. 53 54Besides from overwriting the ``snd_timer_uinfo`` struct, ioctl stores 55a 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 57descriptor for the timer guarantees that the timer can only be triggered 58by 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 61So, 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 93More detailed example of creating and ticking the timer could be found 94in the utimer ALSA selftest. 95 96Userspace-driven timers and snd-aloop 97------------------------------------- 98 99Userspace-driven timers could be easily used with ``snd-aloop`` module 100when synchronizing two sound applications on both ends of the virtual 101sound loopback. For instance, if one of the applications receives sound 102frames from network and sends them to snd-aloop pcm device, and another 103application listens for frames on the other snd-aloop pcm device, it 104makes sense that the ALSA middle layer should initiate a data 105transaction when the new period of data is received through network, but 106not when the certain amount of jiffies elapses. Userspace-driven ALSA 107timers could be used to achieve this. 108 109To use userspace-driven ALSA timer as a timer source of snd-aloop, pass 110the following string as the snd-aloop ``timer_source`` parameter: 111 112:: 113 114 # modprobe snd-aloop timer_source="-1.4.<utimer_id>" 115 116Where ``utimer_id`` is the id of the timer you created with 117``SNDRV_TIMER_IOCTL_CREATE``, and ``4`` is the number of 118userspace-driven timers device (``SNDRV_TIMER_GLOBAL_UDRIVEN``). 119 120``resolution`` for the userspace-driven ALSA timer used with snd-aloop 121should be calculated as ``1000000000ULL / frame_rate * period_size`` as 122the timer is going to tick every time a new period of frames is ready. 123 124After that, each time you trigger the timer with 125``SNDRV_TIMER_IOCTL_TRIGGER`` the new period of data will be transferred 126from one snd-aloop device to another. 127