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