utimer-test.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * This test covers the functionality of userspace-driven ALSA timers. Such timers
  4. * are purely virtual (so they don't directly depend on the hardware), and they could be
  5. * created and triggered by userspace applications.
  6. *
  7. * Author: Ivan Orlov <ivan.orlov0322@gmail.com>
  8. */
  9. #include "kselftest_harness.h"
  10. #include <sound/asound.h>
  11. #include <unistd.h>
  12. #include <fcntl.h>
  13. #include <limits.h>
  14. #include <sys/ioctl.h>
  15. #include <stdlib.h>
  16. #include <pthread.h>
  17. #include <string.h>
  18. #define FRAME_RATE 8000
  19. #define PERIOD_SIZE 4410
  20. #define UTIMER_DEFAULT_ID -1
  21. #define UTIMER_DEFAULT_FD -1
  22. #define NANO 1000000000ULL
  23. #define TICKS_COUNT 10
  24. #define TICKS_RECORDING_DELTA 5
  25. #define TIMER_OUTPUT_BUF_LEN 1024
  26. #define TIMER_FREQ_SEC 1
  27. #define RESULT_PREFIX_LEN strlen("Total ticks count: ")
  28. enum timer_app_event {
  29. TIMER_APP_STARTED,
  30. TIMER_APP_RESULT,
  31. TIMER_NO_EVENT,
  32. };
  33. FIXTURE(timer_f) {
  34. struct snd_timer_uinfo *utimer_info;
  35. };
  36. FIXTURE_SETUP(timer_f) {
  37. int timer_dev_fd;
  38. if (geteuid())
  39. SKIP(return, "This test needs root to run!");
  40. self->utimer_info = calloc(1, sizeof(*self->utimer_info));
  41. ASSERT_NE(NULL, self->utimer_info);
  42. /* Resolution is the time the period of frames takes in nanoseconds */
  43. self->utimer_info->resolution = (NANO / FRAME_RATE * PERIOD_SIZE);
  44. timer_dev_fd = open("/dev/snd/timer", O_RDONLY);
  45. ASSERT_GE(timer_dev_fd, 0);
  46. ASSERT_EQ(ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, self->utimer_info), 0);
  47. ASSERT_GE(self->utimer_info->fd, 0);
  48. close(timer_dev_fd);
  49. }
  50. FIXTURE_TEARDOWN(timer_f) {
  51. close(self->utimer_info->fd);
  52. free(self->utimer_info);
  53. }
  54. static void *ticking_func(void *data)
  55. {
  56. int i;
  57. int *fd = (int *)data;
  58. for (i = 0; i < TICKS_COUNT; i++) {
  59. /* Well, trigger the timer! */
  60. ioctl(*fd, SNDRV_TIMER_IOCTL_TRIGGER, NULL);
  61. sleep(TIMER_FREQ_SEC);
  62. }
  63. return NULL;
  64. }
  65. static enum timer_app_event parse_timer_output(const char *s)
  66. {
  67. if (strstr(s, "Timer has started"))
  68. return TIMER_APP_STARTED;
  69. if (strstr(s, "Total ticks count"))
  70. return TIMER_APP_RESULT;
  71. return TIMER_NO_EVENT;
  72. }
  73. static int parse_timer_result(const char *s)
  74. {
  75. char *end;
  76. long d;
  77. d = strtol(s + RESULT_PREFIX_LEN, &end, 10);
  78. if (end == s + RESULT_PREFIX_LEN)
  79. return -1;
  80. return d;
  81. }
  82. /*
  83. * This test triggers the timer and counts ticks at the same time. The amount
  84. * of the timer trigger calls should be equal to the amount of ticks received.
  85. */
  86. TEST_F(timer_f, utimer) {
  87. char command[64];
  88. pthread_t ticking_thread;
  89. int total_ticks = 0;
  90. FILE *rfp;
  91. char *buf = malloc(TIMER_OUTPUT_BUF_LEN);
  92. ASSERT_NE(buf, NULL);
  93. /* The timeout should be the ticks interval * count of ticks + some delta */
  94. sprintf(command, "./global-timer %d %d %d", SNDRV_TIMER_GLOBAL_UDRIVEN,
  95. self->utimer_info->id, TICKS_COUNT * TIMER_FREQ_SEC + TICKS_RECORDING_DELTA);
  96. rfp = popen(command, "r");
  97. while (fgets(buf, TIMER_OUTPUT_BUF_LEN, rfp)) {
  98. buf[TIMER_OUTPUT_BUF_LEN - 1] = 0;
  99. switch (parse_timer_output(buf)) {
  100. case TIMER_APP_STARTED:
  101. /* global-timer waits for timer to trigger, so start the ticking thread */
  102. pthread_create(&ticking_thread, NULL, ticking_func,
  103. &self->utimer_info->fd);
  104. break;
  105. case TIMER_APP_RESULT:
  106. total_ticks = parse_timer_result(buf);
  107. break;
  108. case TIMER_NO_EVENT:
  109. break;
  110. }
  111. }
  112. pthread_join(ticking_thread, NULL);
  113. ASSERT_EQ(total_ticks, TICKS_COUNT);
  114. pclose(rfp);
  115. free(buf);
  116. }
  117. TEST(wrong_timers_test) {
  118. int timer_dev_fd;
  119. int utimer_fd;
  120. struct snd_timer_uinfo wrong_timer = {
  121. .resolution = 0,
  122. .id = UTIMER_DEFAULT_ID,
  123. .fd = UTIMER_DEFAULT_FD,
  124. };
  125. timer_dev_fd = open("/dev/snd/timer", O_RDONLY);
  126. ASSERT_GE(timer_dev_fd, 0);
  127. utimer_fd = ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, &wrong_timer);
  128. ASSERT_LT(utimer_fd, 0);
  129. /* Check that id was not updated */
  130. ASSERT_EQ(wrong_timer.id, UTIMER_DEFAULT_ID);
  131. /* Test the NULL as an argument is processed correctly */
  132. ASSERT_LT(ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, NULL), 0);
  133. close(timer_dev_fd);
  134. }
  135. TEST_HARNESS_MAIN