165 lines
3.9 KiB
C
165 lines
3.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* This test covers the functionality of userspace-driven ALSA timers. Such timers
|
|
* are purely virtual (so they don't directly depend on the hardware), and they could be
|
|
* created and triggered by userspace applications.
|
|
*
|
|
* Author: Ivan Orlov <ivan.orlov0322@gmail.com>
|
|
*/
|
|
#include "../kselftest_harness.h"
|
|
#include <sound/asound.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <sys/ioctl.h>
|
|
#include <stdlib.h>
|
|
#include <pthread.h>
|
|
#include <string.h>
|
|
|
|
#define FRAME_RATE 8000
|
|
#define PERIOD_SIZE 4410
|
|
#define UTIMER_DEFAULT_ID -1
|
|
#define UTIMER_DEFAULT_FD -1
|
|
#define NANO 1000000000ULL
|
|
#define TICKS_COUNT 10
|
|
#define TICKS_RECORDING_DELTA 5
|
|
#define TIMER_OUTPUT_BUF_LEN 1024
|
|
#define TIMER_FREQ_SEC 1
|
|
#define RESULT_PREFIX_LEN strlen("Total ticks count: ")
|
|
|
|
enum timer_app_event {
|
|
TIMER_APP_STARTED,
|
|
TIMER_APP_RESULT,
|
|
TIMER_NO_EVENT,
|
|
};
|
|
|
|
FIXTURE(timer_f) {
|
|
struct snd_timer_uinfo *utimer_info;
|
|
};
|
|
|
|
FIXTURE_SETUP(timer_f) {
|
|
int timer_dev_fd;
|
|
|
|
if (geteuid())
|
|
SKIP(return, "This test needs root to run!");
|
|
|
|
self->utimer_info = calloc(1, sizeof(*self->utimer_info));
|
|
ASSERT_NE(NULL, self->utimer_info);
|
|
|
|
/* Resolution is the time the period of frames takes in nanoseconds */
|
|
self->utimer_info->resolution = (NANO / FRAME_RATE * PERIOD_SIZE);
|
|
|
|
timer_dev_fd = open("/dev/snd/timer", O_RDONLY);
|
|
ASSERT_GE(timer_dev_fd, 0);
|
|
|
|
ASSERT_EQ(ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, self->utimer_info), 0);
|
|
ASSERT_GE(self->utimer_info->fd, 0);
|
|
|
|
close(timer_dev_fd);
|
|
}
|
|
|
|
FIXTURE_TEARDOWN(timer_f) {
|
|
close(self->utimer_info->fd);
|
|
free(self->utimer_info);
|
|
}
|
|
|
|
static void *ticking_func(void *data)
|
|
{
|
|
int i;
|
|
int *fd = (int *)data;
|
|
|
|
for (i = 0; i < TICKS_COUNT; i++) {
|
|
/* Well, trigger the timer! */
|
|
ioctl(*fd, SNDRV_TIMER_IOCTL_TRIGGER, NULL);
|
|
sleep(TIMER_FREQ_SEC);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static enum timer_app_event parse_timer_output(const char *s)
|
|
{
|
|
if (strstr(s, "Timer has started"))
|
|
return TIMER_APP_STARTED;
|
|
if (strstr(s, "Total ticks count"))
|
|
return TIMER_APP_RESULT;
|
|
|
|
return TIMER_NO_EVENT;
|
|
}
|
|
|
|
static int parse_timer_result(const char *s)
|
|
{
|
|
char *end;
|
|
long d;
|
|
|
|
d = strtol(s + RESULT_PREFIX_LEN, &end, 10);
|
|
if (end == s + RESULT_PREFIX_LEN)
|
|
return -1;
|
|
|
|
return d;
|
|
}
|
|
|
|
/*
|
|
* This test triggers the timer and counts ticks at the same time. The amount
|
|
* of the timer trigger calls should be equal to the amount of ticks received.
|
|
*/
|
|
TEST_F(timer_f, utimer) {
|
|
char command[64];
|
|
pthread_t ticking_thread;
|
|
int total_ticks = 0;
|
|
FILE *rfp;
|
|
char *buf = malloc(TIMER_OUTPUT_BUF_LEN);
|
|
|
|
ASSERT_NE(buf, NULL);
|
|
|
|
/* The timeout should be the ticks interval * count of ticks + some delta */
|
|
sprintf(command, "./global-timer %d %d %d", SNDRV_TIMER_GLOBAL_UDRIVEN,
|
|
self->utimer_info->id, TICKS_COUNT * TIMER_FREQ_SEC + TICKS_RECORDING_DELTA);
|
|
|
|
rfp = popen(command, "r");
|
|
while (fgets(buf, TIMER_OUTPUT_BUF_LEN, rfp)) {
|
|
buf[TIMER_OUTPUT_BUF_LEN - 1] = 0;
|
|
switch (parse_timer_output(buf)) {
|
|
case TIMER_APP_STARTED:
|
|
/* global-timer waits for timer to trigger, so start the ticking thread */
|
|
pthread_create(&ticking_thread, NULL, ticking_func,
|
|
&self->utimer_info->fd);
|
|
break;
|
|
case TIMER_APP_RESULT:
|
|
total_ticks = parse_timer_result(buf);
|
|
break;
|
|
case TIMER_NO_EVENT:
|
|
break;
|
|
}
|
|
}
|
|
pthread_join(ticking_thread, NULL);
|
|
ASSERT_EQ(total_ticks, TICKS_COUNT);
|
|
pclose(rfp);
|
|
}
|
|
|
|
TEST(wrong_timers_test) {
|
|
int timer_dev_fd;
|
|
int utimer_fd;
|
|
size_t i;
|
|
struct snd_timer_uinfo wrong_timer = {
|
|
.resolution = 0,
|
|
.id = UTIMER_DEFAULT_ID,
|
|
.fd = UTIMER_DEFAULT_FD,
|
|
};
|
|
|
|
timer_dev_fd = open("/dev/snd/timer", O_RDONLY);
|
|
ASSERT_GE(timer_dev_fd, 0);
|
|
|
|
utimer_fd = ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, &wrong_timer);
|
|
ASSERT_LT(utimer_fd, 0);
|
|
/* Check that id was not updated */
|
|
ASSERT_EQ(wrong_timer.id, UTIMER_DEFAULT_ID);
|
|
|
|
/* Test the NULL as an argument is processed correctly */
|
|
ASSERT_LT(ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, NULL), 0);
|
|
|
|
close(timer_dev_fd);
|
|
}
|
|
|
|
TEST_HARNESS_MAIN
|