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 #include <errno.h> 19 20 #define FRAME_RATE 8000 21 #define PERIOD_SIZE 4410 22 #define UTIMER_DEFAULT_ID -1 23 #define UTIMER_DEFAULT_FD -1 24 #define NANO 1000000000ULL 25 #define TICKS_COUNT 10 26 #define TICKS_RECORDING_DELTA 5 27 #define TIMER_OUTPUT_BUF_LEN 1024 28 #define TIMER_FREQ_SEC 1 29 #define RESULT_PREFIX_LEN strlen("Total ticks count: ") 30 31 enum timer_app_event { 32 TIMER_APP_STARTED, 33 TIMER_APP_RESULT, 34 TIMER_NO_EVENT, 35 }; 36 37 FIXTURE(timer_f) { 38 struct snd_timer_uinfo *utimer_info; 39 }; 40 41 FIXTURE_SETUP(timer_f) { 42 int timer_dev_fd; 43 44 if (geteuid()) 45 SKIP(return, "This test needs root to run!"); 46 47 self->utimer_info = calloc(1, sizeof(*self->utimer_info)); 48 ASSERT_NE(NULL, self->utimer_info); 49 50 /* Resolution is the time the period of frames takes in nanoseconds */ 51 self->utimer_info->resolution = (NANO / FRAME_RATE * PERIOD_SIZE); 52 53 timer_dev_fd = open("/dev/snd/timer", O_RDONLY); 54 ASSERT_GE(timer_dev_fd, 0); 55 56 if (ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, self->utimer_info) < 0) { 57 int err = errno; 58 59 close(timer_dev_fd); 60 if (err == ENOTTY || err == ENXIO) 61 SKIP(return, "CONFIG_SND_UTIMER not enabled"); 62 ASSERT_EQ(err, 0); 63 } 64 ASSERT_GE(self->utimer_info->fd, 0); 65 66 close(timer_dev_fd); 67 } 68 69 FIXTURE_TEARDOWN(timer_f) { 70 close(self->utimer_info->fd); 71 free(self->utimer_info); 72 } 73 74 static void *ticking_func(void *data) 75 { 76 int i; 77 int *fd = (int *)data; 78 79 for (i = 0; i < TICKS_COUNT; i++) { 80 /* Well, trigger the timer! */ 81 ioctl(*fd, SNDRV_TIMER_IOCTL_TRIGGER, NULL); 82 sleep(TIMER_FREQ_SEC); 83 } 84 85 return NULL; 86 } 87 88 static enum timer_app_event parse_timer_output(const char *s) 89 { 90 if (strstr(s, "Timer has started")) 91 return TIMER_APP_STARTED; 92 if (strstr(s, "Total ticks count")) 93 return TIMER_APP_RESULT; 94 95 return TIMER_NO_EVENT; 96 } 97 98 static int parse_timer_result(const char *s) 99 { 100 char *end; 101 long d; 102 103 d = strtol(s + RESULT_PREFIX_LEN, &end, 10); 104 if (end == s + RESULT_PREFIX_LEN) 105 return -1; 106 107 return d; 108 } 109 110 /* 111 * This test triggers the timer and counts ticks at the same time. The amount 112 * of the timer trigger calls should be equal to the amount of ticks received. 113 */ 114 TEST_F(timer_f, utimer) { 115 char command[64]; 116 pthread_t ticking_thread; 117 int total_ticks = 0; 118 FILE *rfp; 119 char *buf = malloc(TIMER_OUTPUT_BUF_LEN); 120 121 ASSERT_NE(buf, NULL); 122 123 /* The timeout should be the ticks interval * count of ticks + some delta */ 124 sprintf(command, "./global-timer %d %d %d", SNDRV_TIMER_GLOBAL_UDRIVEN, 125 self->utimer_info->id, TICKS_COUNT * TIMER_FREQ_SEC + TICKS_RECORDING_DELTA); 126 127 rfp = popen(command, "r"); 128 while (fgets(buf, TIMER_OUTPUT_BUF_LEN, rfp)) { 129 buf[TIMER_OUTPUT_BUF_LEN - 1] = 0; 130 switch (parse_timer_output(buf)) { 131 case TIMER_APP_STARTED: 132 /* global-timer waits for timer to trigger, so start the ticking thread */ 133 pthread_create(&ticking_thread, NULL, ticking_func, 134 &self->utimer_info->fd); 135 break; 136 case TIMER_APP_RESULT: 137 total_ticks = parse_timer_result(buf); 138 break; 139 case TIMER_NO_EVENT: 140 break; 141 } 142 } 143 pthread_join(ticking_thread, NULL); 144 ASSERT_EQ(total_ticks, TICKS_COUNT); 145 pclose(rfp); 146 free(buf); 147 } 148 149 TEST(wrong_timers_test) { 150 int timer_dev_fd; 151 int utimer_fd; 152 struct snd_timer_uinfo wrong_timer = { 153 .resolution = 0, 154 .id = UTIMER_DEFAULT_ID, 155 .fd = UTIMER_DEFAULT_FD, 156 }; 157 158 timer_dev_fd = open("/dev/snd/timer", O_RDONLY); 159 ASSERT_GE(timer_dev_fd, 0); 160 161 utimer_fd = ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, &wrong_timer); 162 ASSERT_LT(utimer_fd, 0); 163 /* Check that id was not updated */ 164 ASSERT_EQ(wrong_timer.id, UTIMER_DEFAULT_ID); 165 166 /* Test the NULL as an argument is processed correctly */ 167 ASSERT_LT(ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, NULL), 0); 168 169 close(timer_dev_fd); 170 } 171 172 TEST_HARNESS_MAIN 173