xref: /linux/tools/testing/selftests/alsa/utimer-test.c (revision 4b097a7b25a01a3732f0e7569921efc9ad22bc81)
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