utimers.rst 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. .. SPDX-License-Identifier: GPL-2.0
  2. =======================
  3. Userspace-driven timers
  4. =======================
  5. :Author: Ivan Orlov <ivan.orlov0322@gmail.com>
  6. Preface
  7. =======
  8. This document describes the userspace-driven timers: virtual ALSA timers
  9. which could be created and controlled by userspace applications using
  10. IOCTL calls. Such timers could be useful when synchronizing audio
  11. stream with timer sources which we don't have ALSA timers exported for
  12. (e.g. PTP clocks), and when synchronizing the audio stream going through
  13. two virtual sound devices using ``snd-aloop`` (for instance, when
  14. we have a network application sending frames to one snd-aloop device,
  15. and another sound application listening on the other end of snd-aloop).
  16. Enabling userspace-driven timers
  17. ================================
  18. The userspace-driven timers could be enabled in the kernel using the
  19. ``CONFIG_SND_UTIMER`` configuration option. It depends on the
  20. ``CONFIG_SND_TIMER`` option, so it also should be enabled.
  21. Userspace-driven timers API
  22. ===========================
  23. Userspace application can create a userspace-driven ALSA timer by
  24. executing the ``SNDRV_TIMER_IOCTL_CREATE`` ioctl call on the
  25. ``/dev/snd/timer`` device file descriptor. The ``snd_timer_uinfo``
  26. structure should be passed as an ioctl argument:
  27. ::
  28. struct snd_timer_uinfo {
  29. __u64 resolution;
  30. int fd;
  31. unsigned int id;
  32. unsigned char reserved[16];
  33. }
  34. The ``resolution`` field sets the desired resolution in nanoseconds for
  35. the virtual timer. ``resolution`` field simply provides an information
  36. about the virtual timer, but does not affect the timing itself. ``id``
  37. field gets overwritten by the ioctl, and the identifier you get in this
  38. field after the call can be used as a timer subdevice number when
  39. passing the timer to ``snd-aloop`` kernel module or other userspace
  40. applications. There could be up to 128 userspace-driven timers in the
  41. system at one moment of time, thus the id value ranges from 0 to 127.
  42. Besides from overwriting the ``snd_timer_uinfo`` struct, ioctl stores
  43. a timer file descriptor, which can be used to trigger the timer, in the
  44. ``fd`` field of the ``snd_timer_uinfo`` struct. Allocation of a file
  45. descriptor for the timer guarantees that the timer can only be triggered
  46. by the process which created it. The timer then can be triggered with
  47. ``SNDRV_TIMER_IOCTL_TRIGGER`` ioctl call on the timer file descriptor.
  48. So, the example code for creating and triggering the timer would be:
  49. ::
  50. static struct snd_timer_uinfo utimer_info = {
  51. /* Timer is going to tick (presumably) every 1000000 ns */
  52. .resolution = 1000000ULL,
  53. .id = -1,
  54. };
  55. int timer_device_fd = open("/dev/snd/timer", O_RDWR | O_CLOEXEC);
  56. if (ioctl(timer_device_fd, SNDRV_TIMER_IOCTL_CREATE, &utimer_info)) {
  57. perror("Failed to create the timer");
  58. return -1;
  59. }
  60. ...
  61. /*
  62. * Now we want to trigger the timer. Callbacks of all of the
  63. * timer instances binded to this timer will be executed after
  64. * this call.
  65. */
  66. ioctl(utimer_info.fd, SNDRV_TIMER_IOCTL_TRIGGER, NULL);
  67. ...
  68. /* Now, destroy the timer */
  69. close(timer_info.fd);
  70. More detailed example of creating and ticking the timer could be found
  71. in the utimer ALSA selftest.
  72. Userspace-driven timers and snd-aloop
  73. -------------------------------------
  74. Userspace-driven timers could be easily used with ``snd-aloop`` module
  75. when synchronizing two sound applications on both ends of the virtual
  76. sound loopback. For instance, if one of the applications receives sound
  77. frames from network and sends them to snd-aloop pcm device, and another
  78. application listens for frames on the other snd-aloop pcm device, it
  79. makes sense that the ALSA middle layer should initiate a data
  80. transaction when the new period of data is received through network, but
  81. not when the certain amount of jiffies elapses. Userspace-driven ALSA
  82. timers could be used to achieve this.
  83. To use userspace-driven ALSA timer as a timer source of snd-aloop, pass
  84. the following string as the snd-aloop ``timer_source`` parameter:
  85. ::
  86. # modprobe snd-aloop timer_source="-1.4.<utimer_id>"
  87. Where ``utimer_id`` is the id of the timer you created with
  88. ``SNDRV_TIMER_IOCTL_CREATE``, and ``4`` is the number of
  89. userspace-driven timers device (``SNDRV_TIMER_GLOBAL_UDRIVEN``).
  90. ``resolution`` for the userspace-driven ALSA timer used with snd-aloop
  91. should be calculated as ``1000000000ULL / frame_rate * period_size`` as
  92. the timer is going to tick every time a new period of frames is ready.
  93. After that, each time you trigger the timer with
  94. ``SNDRV_TIMER_IOCTL_TRIGGER`` the new period of data will be transferred
  95. from one snd-aloop device to another.