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