mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-08-21 16:31:14 +02:00

Add a test for the new functionality of userspace-driven timers and the tool which allows us to count timer ticks in a certain time period. The test: 1. Creates a userspace-driven timer with ioctl to /dev/snd/timer 2. Starts the `global-timer` application to count the ticks of the timer from step 1. 3. Asynchronously triggers the timer multiple times with some interval 4. Compares the amount of caught ticks with the amount of trigger calls. Since we can't include <alsa/asoundlib.h> and <sound/asound.h> in one file due to overlapping declarations, I have to split the test into two applications: one of them counts the amount of timer ticks in the defined time period, and another one is the actual test which creates the timer, triggers it periodically and starts the first app to count the amount of ticks in a separate thread. Besides from testing the functionality itself, the test represents a sample application showing userspace-driven ALSA timers API. Also, the timer test includes a test case which tries to create a timer with invalid resolution (=0), and NULL as a timer info structure. Signed-off-by: Ivan Orlov <ivan.orlov0322@gmail.com> Signed-off-by: Takashi Iwai <tiwai@suse.de> Link: https://patch.msgid.link/20240813120701.171743-5-ivan.orlov0322@gmail.com
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
|