Path: blob/master/tools/testing/selftests/alsa/utimer-test.c
26285 views
// SPDX-License-Identifier: GPL-2.01/*2* This test covers the functionality of userspace-driven ALSA timers. Such timers3* are purely virtual (so they don't directly depend on the hardware), and they could be4* created and triggered by userspace applications.5*6* Author: Ivan Orlov <[email protected]>7*/8#include "../kselftest_harness.h"9#include <sound/asound.h>10#include <unistd.h>11#include <fcntl.h>12#include <limits.h>13#include <sys/ioctl.h>14#include <stdlib.h>15#include <pthread.h>16#include <string.h>1718#define FRAME_RATE 800019#define PERIOD_SIZE 441020#define UTIMER_DEFAULT_ID -121#define UTIMER_DEFAULT_FD -122#define NANO 1000000000ULL23#define TICKS_COUNT 1024#define TICKS_RECORDING_DELTA 525#define TIMER_OUTPUT_BUF_LEN 102426#define TIMER_FREQ_SEC 127#define RESULT_PREFIX_LEN strlen("Total ticks count: ")2829enum timer_app_event {30TIMER_APP_STARTED,31TIMER_APP_RESULT,32TIMER_NO_EVENT,33};3435FIXTURE(timer_f) {36struct snd_timer_uinfo *utimer_info;37};3839FIXTURE_SETUP(timer_f) {40int timer_dev_fd;4142if (geteuid())43SKIP(return, "This test needs root to run!");4445self->utimer_info = calloc(1, sizeof(*self->utimer_info));46ASSERT_NE(NULL, self->utimer_info);4748/* Resolution is the time the period of frames takes in nanoseconds */49self->utimer_info->resolution = (NANO / FRAME_RATE * PERIOD_SIZE);5051timer_dev_fd = open("/dev/snd/timer", O_RDONLY);52ASSERT_GE(timer_dev_fd, 0);5354ASSERT_EQ(ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, self->utimer_info), 0);55ASSERT_GE(self->utimer_info->fd, 0);5657close(timer_dev_fd);58}5960FIXTURE_TEARDOWN(timer_f) {61close(self->utimer_info->fd);62free(self->utimer_info);63}6465static void *ticking_func(void *data)66{67int i;68int *fd = (int *)data;6970for (i = 0; i < TICKS_COUNT; i++) {71/* Well, trigger the timer! */72ioctl(*fd, SNDRV_TIMER_IOCTL_TRIGGER, NULL);73sleep(TIMER_FREQ_SEC);74}7576return NULL;77}7879static enum timer_app_event parse_timer_output(const char *s)80{81if (strstr(s, "Timer has started"))82return TIMER_APP_STARTED;83if (strstr(s, "Total ticks count"))84return TIMER_APP_RESULT;8586return TIMER_NO_EVENT;87}8889static int parse_timer_result(const char *s)90{91char *end;92long d;9394d = strtol(s + RESULT_PREFIX_LEN, &end, 10);95if (end == s + RESULT_PREFIX_LEN)96return -1;9798return d;99}100101/*102* This test triggers the timer and counts ticks at the same time. The amount103* of the timer trigger calls should be equal to the amount of ticks received.104*/105TEST_F(timer_f, utimer) {106char command[64];107pthread_t ticking_thread;108int total_ticks = 0;109FILE *rfp;110char *buf = malloc(TIMER_OUTPUT_BUF_LEN);111112ASSERT_NE(buf, NULL);113114/* The timeout should be the ticks interval * count of ticks + some delta */115sprintf(command, "./global-timer %d %d %d", SNDRV_TIMER_GLOBAL_UDRIVEN,116self->utimer_info->id, TICKS_COUNT * TIMER_FREQ_SEC + TICKS_RECORDING_DELTA);117118rfp = popen(command, "r");119while (fgets(buf, TIMER_OUTPUT_BUF_LEN, rfp)) {120buf[TIMER_OUTPUT_BUF_LEN - 1] = 0;121switch (parse_timer_output(buf)) {122case TIMER_APP_STARTED:123/* global-timer waits for timer to trigger, so start the ticking thread */124pthread_create(&ticking_thread, NULL, ticking_func,125&self->utimer_info->fd);126break;127case TIMER_APP_RESULT:128total_ticks = parse_timer_result(buf);129break;130case TIMER_NO_EVENT:131break;132}133}134pthread_join(ticking_thread, NULL);135ASSERT_EQ(total_ticks, TICKS_COUNT);136pclose(rfp);137free(buf);138}139140TEST(wrong_timers_test) {141int timer_dev_fd;142int utimer_fd;143size_t i;144struct snd_timer_uinfo wrong_timer = {145.resolution = 0,146.id = UTIMER_DEFAULT_ID,147.fd = UTIMER_DEFAULT_FD,148};149150timer_dev_fd = open("/dev/snd/timer", O_RDONLY);151ASSERT_GE(timer_dev_fd, 0);152153utimer_fd = ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, &wrong_timer);154ASSERT_LT(utimer_fd, 0);155/* Check that id was not updated */156ASSERT_EQ(wrong_timer.id, UTIMER_DEFAULT_ID);157158/* Test the NULL as an argument is processed correctly */159ASSERT_LT(ioctl(timer_dev_fd, SNDRV_TIMER_IOCTL_CREATE, NULL), 0);160161close(timer_dev_fd);162}163164TEST_HARNESS_MAIN165166167