xref: /linux/Documentation/sound/utimers.rst (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
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