File Index
Files and directories
Sign in
FILE

utimer-test.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/alsa/utimer-test.c C
  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
 30enum timer_app_event {
 31	TIMER_APP_STARTED,
 32	TIMER_APP_RESULT,
 33	TIMER_NO_EVENT,
 34};
 35
 36FIXTURE(timer_f) {
 37	struct snd_timer_uinfo *utimer_info;
 38};
 39
 40FIXTURE_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
 61FIXTURE_TEARDOWN(timer_f) {
 62	close(self->utimer_info->fd);
 63	free(self->utimer_info);
 64}
 65
 66static 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
 80static 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
 90static 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 */
106TEST_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	free(buf);
139}
140
141TEST(wrong_timers_test) {
142	int timer_dev_fd;
143	int utimer_fd;
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
164TEST_HARNESS_MAIN
~/ Read-only file preview.