Userspace-driven timers

Author:

Ivan Orlov <ivan.orlov0322@gmail.com>

Preface

This document describes the userspace-driven timers: virtual ALSA timerswhich could be created and controlled by userspace applications usingIOCTL calls. Such timers could be useful when synchronizing audiostream with timer sources which we don’t have ALSA timers exported for(e.g. PTP clocks), and when synchronizing the audio stream going throughtwo virtual sound devices usingsnd-aloop (for instance, whenwe 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 theCONFIG_SND_UTIMER configuration option. It depends on theCONFIG_SND_TIMER option, so it also should be enabled.

Userspace-driven timers API

Userspace application can create a userspace-driven ALSA timer byexecuting theSNDRV_TIMER_IOCTL_CREATE ioctl call on the/dev/snd/timer device file descriptor. Thesnd_timer_uinfostructure should be passed as an ioctl argument:

struct snd_timer_uinfo {    __u64 resolution;    int fd;    unsigned int id;    unsigned char reserved[16];}

Theresolution field sets the desired resolution in nanoseconds forthe virtual timer.resolution field simply provides an informationabout the virtual timer, but does not affect the timing itself.idfield gets overwritten by the ioctl, and the identifier you get in thisfield after the call can be used as a timer subdevice number whenpassing the timer tosnd-aloop kernel module or other userspaceapplications. There could be up to 128 userspace-driven timers in thesystem at one moment of time, thus the id value ranges from 0 to 127.

Besides from overwriting thesnd_timer_uinfo struct, ioctl storesa timer file descriptor, which can be used to trigger the timer, in thefd field of thesnd_timer_uinfo struct. Allocation of a filedescriptor for the timer guarantees that the timer can only be triggeredby the process which created it. The timer then can be triggered withSNDRV_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 foundin the utimer ALSA selftest.

Userspace-driven timers and snd-aloop

Userspace-driven timers could be easily used withsnd-aloop modulewhen synchronizing two sound applications on both ends of the virtualsound loopback. For instance, if one of the applications receives soundframes from network and sends them to snd-aloop pcm device, and anotherapplication listens for frames on the other snd-aloop pcm device, itmakes sense that the ALSA middle layer should initiate a datatransaction when the new period of data is received through network, butnot when the certain amount of jiffies elapses. Userspace-driven ALSAtimers could be used to achieve this.

To use userspace-driven ALSA timer as a timer source of snd-aloop, passthe following string as the snd-alooptimer_source parameter:

# modprobe snd-aloop timer_source="-1.4.<utimer_id>"

Whereutimer_id is the id of the timer you created withSNDRV_TIMER_IOCTL_CREATE, and4 is the number ofuserspace-driven timers device (SNDRV_TIMER_GLOBAL_UDRIVEN).

resolution for the userspace-driven ALSA timer used with snd-aloopshould be calculated as1000000000ULL/frame_rate*period_size asthe timer is going to tick every time a new period of frames is ready.

After that, each time you trigger the timer withSNDRV_TIMER_IOCTL_TRIGGER the new period of data will be transferredfrom one snd-aloop device to another.