1 // SPDX-License-Identifier: GPL-2.0
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.
7 * Author: Ivan Orlov <ivan.orlov0322@gmail.com>
9 #include "../kselftest_harness.h"
10 #include <sound/asound.h>
14 #include <sys/ioctl.h>
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: ")
30 enum timer_app_event
{
37 struct snd_timer_uinfo
*utimer_info
;
40 FIXTURE_SETUP(timer_f
) {
44 SKIP(return, "This test needs root to run!");
46 self
->utimer_info
= calloc(1, sizeof(*self
->utimer_info
));
47 ASSERT_NE(NULL
, self
->utimer_info
);
49 /* Resolution is the time the period of frames takes in nanoseconds */
50 self
->utimer_info
->resolution
= (NANO
/ FRAME_RATE
* PERIOD_SIZE
);
52 timer_dev_fd
= open("/dev/snd/timer", O_RDONLY
);
53 ASSERT_GE(timer_dev_fd
, 0);
55 ASSERT_EQ(ioctl(timer_dev_fd
, SNDRV_TIMER_IOCTL_CREATE
, self
->utimer_info
), 0);
56 ASSERT_GE(self
->utimer_info
->fd
, 0);
61 FIXTURE_TEARDOWN(timer_f
) {
62 close(self
->utimer_info
->fd
);
63 free(self
->utimer_info
);
66 static void *ticking_func(void *data
)
69 int *fd
= (int *)data
;
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
);
80 static enum timer_app_event
parse_timer_output(const char *s
)
82 if (strstr(s
, "Timer has started"))
83 return TIMER_APP_STARTED
;
84 if (strstr(s
, "Total ticks count"))
85 return TIMER_APP_RESULT
;
87 return TIMER_NO_EVENT
;
90 static int parse_timer_result(const char *s
)
95 d
= strtol(s
+ RESULT_PREFIX_LEN
, &end
, 10);
96 if (end
== s
+ RESULT_PREFIX_LEN
)
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.
106 TEST_F(timer_f
, utimer
) {
108 pthread_t ticking_thread
;
111 char *buf
= malloc(TIMER_OUTPUT_BUF_LEN
);
113 ASSERT_NE(buf
, NULL
);
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
);
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
);
128 case TIMER_APP_RESULT
:
129 total_ticks
= parse_timer_result(buf
);
135 pthread_join(ticking_thread
, NULL
);
136 ASSERT_EQ(total_ticks
, TICKS_COUNT
);
140 TEST(wrong_timers_test
) {
144 struct snd_timer_uinfo wrong_timer
= {
146 .id
= UTIMER_DEFAULT_ID
,
147 .fd
= UTIMER_DEFAULT_FD
,
150 timer_dev_fd
= open("/dev/snd/timer", O_RDONLY
);
151 ASSERT_GE(timer_dev_fd
, 0);
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
);
158 /* Test the NULL as an argument is processed correctly */
159 ASSERT_LT(ioctl(timer_dev_fd
, SNDRV_TIMER_IOCTL_CREATE
, NULL
), 0);