Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
31enum timer_app_event {
32 TIMER_APP_STARTED,
33 TIMER_APP_RESULT,
34 TIMER_NO_EVENT,
35};
36
37FIXTURE(timer_f) {
38 struct snd_timer_uinfo *utimer_info;
39};
40
41FIXTURE_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
69FIXTURE_TEARDOWN(timer_f) {
70 close(self->utimer_info->fd);
71 free(self->utimer_info);
72}
73
74static 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
88static 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
98static 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 */
114TEST_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
149TEST(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
172TEST_HARNESS_MAIN