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