15aaf9effSMark Brown // SPDX-License-Identifier: GPL-2.0 25aaf9effSMark Brown // 35aaf9effSMark Brown // kselftest for the ALSA mixer API 45aaf9effSMark Brown // 55aaf9effSMark Brown // Original author: Mark Brown <broonie@kernel.org> 65aaf9effSMark Brown // Copyright (c) 2021 Arm Limited 75aaf9effSMark Brown 85aaf9effSMark Brown // This test will iterate over all cards detected in the system, exercising 95aaf9effSMark Brown // every mixer control it can find. This may conflict with other system 105aaf9effSMark Brown // software if there is audio activity so is best run on a system with a 115aaf9effSMark Brown // minimal active userspace. 125aaf9effSMark Brown 135aaf9effSMark Brown #include <stdio.h> 145aaf9effSMark Brown #include <stdlib.h> 155aaf9effSMark Brown #include <stdbool.h> 165aaf9effSMark Brown #include <string.h> 175aaf9effSMark Brown #include <getopt.h> 185aaf9effSMark Brown #include <stdarg.h> 195aaf9effSMark Brown #include <ctype.h> 205aaf9effSMark Brown #include <math.h> 215aaf9effSMark Brown #include <errno.h> 225aaf9effSMark Brown #include <assert.h> 235aaf9effSMark Brown #include <alsa/asoundlib.h> 245aaf9effSMark Brown #include <poll.h> 255aaf9effSMark Brown #include <stdint.h> 265aaf9effSMark Brown 275aaf9effSMark Brown #include "../kselftest.h" 285aaf9effSMark Brown 295aaf9effSMark Brown #define TESTS_PER_CONTROL 3 305aaf9effSMark Brown 315aaf9effSMark Brown struct card_data { 325aaf9effSMark Brown snd_ctl_t *handle; 335aaf9effSMark Brown int card; 345aaf9effSMark Brown int num_ctls; 355aaf9effSMark Brown snd_ctl_elem_list_t *ctls; 365aaf9effSMark Brown struct card_data *next; 375aaf9effSMark Brown }; 385aaf9effSMark Brown 395aaf9effSMark Brown struct ctl_data { 405aaf9effSMark Brown const char *name; 415aaf9effSMark Brown snd_ctl_elem_id_t *id; 425aaf9effSMark Brown snd_ctl_elem_info_t *info; 435aaf9effSMark Brown snd_ctl_elem_value_t *def_val; 445aaf9effSMark Brown int elem; 455aaf9effSMark Brown struct card_data *card; 465aaf9effSMark Brown struct ctl_data *next; 475aaf9effSMark Brown }; 485aaf9effSMark Brown 49*b73dad80SJaroslav Kysela static const char *alsa_config = 50*b73dad80SJaroslav Kysela "ctl.hw {\n" 51*b73dad80SJaroslav Kysela " @args [ CARD ]\n" 52*b73dad80SJaroslav Kysela " @args.CARD.type string\n" 53*b73dad80SJaroslav Kysela " type hw\n" 54*b73dad80SJaroslav Kysela " card $CARD\n" 55*b73dad80SJaroslav Kysela "}\n" 56*b73dad80SJaroslav Kysela ; 57*b73dad80SJaroslav Kysela 585aaf9effSMark Brown int num_cards = 0; 595aaf9effSMark Brown int num_controls = 0; 605aaf9effSMark Brown struct card_data *card_list = NULL; 615aaf9effSMark Brown struct ctl_data *ctl_list = NULL; 625aaf9effSMark Brown 63*b73dad80SJaroslav Kysela #ifdef SND_LIB_VER 64*b73dad80SJaroslav Kysela #if SND_LIB_VERSION >= SND_LIB_VER(1, 2, 6) 65*b73dad80SJaroslav Kysela #define LIB_HAS_LOAD_STRING 66*b73dad80SJaroslav Kysela #endif 67*b73dad80SJaroslav Kysela #endif 68*b73dad80SJaroslav Kysela 69*b73dad80SJaroslav Kysela #ifndef LIB_HAS_LOAD_STRING 70*b73dad80SJaroslav Kysela int snd_config_load_string(snd_config_t **config, const char *s, size_t size) 71*b73dad80SJaroslav Kysela { 72*b73dad80SJaroslav Kysela snd_input_t *input; 73*b73dad80SJaroslav Kysela snd_config_t *dst; 74*b73dad80SJaroslav Kysela int err; 75*b73dad80SJaroslav Kysela 76*b73dad80SJaroslav Kysela assert(config && s); 77*b73dad80SJaroslav Kysela if (size == 0) 78*b73dad80SJaroslav Kysela size = strlen(s); 79*b73dad80SJaroslav Kysela err = snd_input_buffer_open(&input, s, size); 80*b73dad80SJaroslav Kysela if (err < 0) 81*b73dad80SJaroslav Kysela return err; 82*b73dad80SJaroslav Kysela err = snd_config_top(&dst); 83*b73dad80SJaroslav Kysela if (err < 0) { 84*b73dad80SJaroslav Kysela snd_input_close(input); 85*b73dad80SJaroslav Kysela return err; 86*b73dad80SJaroslav Kysela } 87*b73dad80SJaroslav Kysela err = snd_config_load(dst, input); 88*b73dad80SJaroslav Kysela snd_input_close(input); 89*b73dad80SJaroslav Kysela if (err < 0) { 90*b73dad80SJaroslav Kysela snd_config_delete(dst); 91*b73dad80SJaroslav Kysela return err; 92*b73dad80SJaroslav Kysela } 93*b73dad80SJaroslav Kysela *config = dst; 94*b73dad80SJaroslav Kysela return 0; 95*b73dad80SJaroslav Kysela } 96*b73dad80SJaroslav Kysela #endif 97*b73dad80SJaroslav Kysela 985aaf9effSMark Brown void find_controls(void) 995aaf9effSMark Brown { 1005aaf9effSMark Brown char name[32]; 1015aaf9effSMark Brown int card, ctl, err; 1025aaf9effSMark Brown struct card_data *card_data; 1035aaf9effSMark Brown struct ctl_data *ctl_data; 104*b73dad80SJaroslav Kysela snd_config_t *config; 1055aaf9effSMark Brown 1065aaf9effSMark Brown card = -1; 1075aaf9effSMark Brown if (snd_card_next(&card) < 0 || card < 0) 1085aaf9effSMark Brown return; 1095aaf9effSMark Brown 110*b73dad80SJaroslav Kysela err = snd_config_load_string(&config, alsa_config, strlen(alsa_config)); 111*b73dad80SJaroslav Kysela if (err < 0) { 112*b73dad80SJaroslav Kysela ksft_print_msg("Unable to parse custom alsa-lib configuration: %s\n", 113*b73dad80SJaroslav Kysela snd_strerror(err)); 114*b73dad80SJaroslav Kysela ksft_exit_fail(); 115*b73dad80SJaroslav Kysela } 116*b73dad80SJaroslav Kysela 1175aaf9effSMark Brown while (card >= 0) { 1185aaf9effSMark Brown sprintf(name, "hw:%d", card); 1195aaf9effSMark Brown 1205aaf9effSMark Brown card_data = malloc(sizeof(*card_data)); 1215aaf9effSMark Brown if (!card_data) 1225aaf9effSMark Brown ksft_exit_fail_msg("Out of memory\n"); 1235aaf9effSMark Brown 124*b73dad80SJaroslav Kysela err = snd_ctl_open_lconf(&card_data->handle, name, 0, config); 1255aaf9effSMark Brown if (err < 0) { 1265aaf9effSMark Brown ksft_print_msg("Failed to get hctl for card %d: %s\n", 1275aaf9effSMark Brown card, snd_strerror(err)); 1285aaf9effSMark Brown goto next_card; 1295aaf9effSMark Brown } 1305aaf9effSMark Brown 1315aaf9effSMark Brown /* Count controls */ 1325aaf9effSMark Brown snd_ctl_elem_list_malloc(&card_data->ctls); 1335aaf9effSMark Brown snd_ctl_elem_list(card_data->handle, card_data->ctls); 1345aaf9effSMark Brown card_data->num_ctls = snd_ctl_elem_list_get_count(card_data->ctls); 1355aaf9effSMark Brown 1365aaf9effSMark Brown /* Enumerate control information */ 1375aaf9effSMark Brown snd_ctl_elem_list_alloc_space(card_data->ctls, card_data->num_ctls); 1385aaf9effSMark Brown snd_ctl_elem_list(card_data->handle, card_data->ctls); 1395aaf9effSMark Brown 1405aaf9effSMark Brown card_data->card = num_cards++; 1415aaf9effSMark Brown card_data->next = card_list; 1425aaf9effSMark Brown card_list = card_data; 1435aaf9effSMark Brown 1445aaf9effSMark Brown num_controls += card_data->num_ctls; 1455aaf9effSMark Brown 1465aaf9effSMark Brown for (ctl = 0; ctl < card_data->num_ctls; ctl++) { 1475aaf9effSMark Brown ctl_data = malloc(sizeof(*ctl_data)); 1485aaf9effSMark Brown if (!ctl_data) 1495aaf9effSMark Brown ksft_exit_fail_msg("Out of memory\n"); 1505aaf9effSMark Brown 1515aaf9effSMark Brown ctl_data->card = card_data; 1525aaf9effSMark Brown ctl_data->elem = ctl; 1535aaf9effSMark Brown ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls, 1545aaf9effSMark Brown ctl); 1555aaf9effSMark Brown 1565aaf9effSMark Brown err = snd_ctl_elem_id_malloc(&ctl_data->id); 1575aaf9effSMark Brown if (err < 0) 1585aaf9effSMark Brown ksft_exit_fail_msg("Out of memory\n"); 1595aaf9effSMark Brown 1605aaf9effSMark Brown err = snd_ctl_elem_info_malloc(&ctl_data->info); 1615aaf9effSMark Brown if (err < 0) 1625aaf9effSMark Brown ksft_exit_fail_msg("Out of memory\n"); 1635aaf9effSMark Brown 1645aaf9effSMark Brown err = snd_ctl_elem_value_malloc(&ctl_data->def_val); 1655aaf9effSMark Brown if (err < 0) 1665aaf9effSMark Brown ksft_exit_fail_msg("Out of memory\n"); 1675aaf9effSMark Brown 1685aaf9effSMark Brown snd_ctl_elem_list_get_id(card_data->ctls, ctl, 1695aaf9effSMark Brown ctl_data->id); 1705aaf9effSMark Brown snd_ctl_elem_info_set_id(ctl_data->info, ctl_data->id); 1715aaf9effSMark Brown err = snd_ctl_elem_info(card_data->handle, 1725aaf9effSMark Brown ctl_data->info); 1735aaf9effSMark Brown if (err < 0) { 1745aaf9effSMark Brown ksft_print_msg("%s getting info for %d\n", 1755aaf9effSMark Brown snd_strerror(err), 1765aaf9effSMark Brown ctl_data->name); 1775aaf9effSMark Brown } 1785aaf9effSMark Brown 1795aaf9effSMark Brown snd_ctl_elem_value_set_id(ctl_data->def_val, 1805aaf9effSMark Brown ctl_data->id); 1815aaf9effSMark Brown 1825aaf9effSMark Brown ctl_data->next = ctl_list; 1835aaf9effSMark Brown ctl_list = ctl_data; 1845aaf9effSMark Brown } 1855aaf9effSMark Brown 1865aaf9effSMark Brown next_card: 1875aaf9effSMark Brown if (snd_card_next(&card) < 0) { 1885aaf9effSMark Brown ksft_print_msg("snd_card_next"); 1895aaf9effSMark Brown break; 1905aaf9effSMark Brown } 1915aaf9effSMark Brown } 192*b73dad80SJaroslav Kysela 193*b73dad80SJaroslav Kysela snd_config_delete(config); 1945aaf9effSMark Brown } 1955aaf9effSMark Brown 1965aaf9effSMark Brown /* 1975aaf9effSMark Brown * Check that we can read the default value and it is valid. Write 1985aaf9effSMark Brown * tests use the read value to restore the default. 1995aaf9effSMark Brown */ 2005aaf9effSMark Brown void test_ctl_get_value(struct ctl_data *ctl) 2015aaf9effSMark Brown { 2025aaf9effSMark Brown int err; 2035aaf9effSMark Brown long int_val; 2045aaf9effSMark Brown long long int64_val; 2055aaf9effSMark Brown 2065aaf9effSMark Brown /* If the control is turned off let's be polite */ 2075aaf9effSMark Brown if (snd_ctl_elem_info_is_inactive(ctl->info)) { 2085aaf9effSMark Brown ksft_print_msg("%s is inactive\n", ctl->name); 2095aaf9effSMark Brown ksft_test_result_skip("get_value.%d.%d\n", 2105aaf9effSMark Brown ctl->card->card, ctl->elem); 2115aaf9effSMark Brown return; 2125aaf9effSMark Brown } 2135aaf9effSMark Brown 2145aaf9effSMark Brown /* Can't test reading on an unreadable control */ 2155aaf9effSMark Brown if (!snd_ctl_elem_info_is_readable(ctl->info)) { 2165aaf9effSMark Brown ksft_print_msg("%s is not readable\n", ctl->name); 2175aaf9effSMark Brown ksft_test_result_skip("get_value.%d.%d\n", 2185aaf9effSMark Brown ctl->card->card, ctl->elem); 2195aaf9effSMark Brown return; 2205aaf9effSMark Brown } 2215aaf9effSMark Brown 2225aaf9effSMark Brown err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val); 2235aaf9effSMark Brown if (err < 0) { 2245aaf9effSMark Brown ksft_print_msg("snd_ctl_elem_read() failed: %s\n", 2255aaf9effSMark Brown snd_strerror(err)); 2265aaf9effSMark Brown goto out; 2275aaf9effSMark Brown } 2285aaf9effSMark Brown 2295aaf9effSMark Brown switch (snd_ctl_elem_info_get_type(ctl->info)) { 2305aaf9effSMark Brown case SND_CTL_ELEM_TYPE_NONE: 2315aaf9effSMark Brown ksft_print_msg("%s Invalid control type NONE\n", ctl->name); 2325aaf9effSMark Brown err = -1; 2335aaf9effSMark Brown break; 2345aaf9effSMark Brown 2355aaf9effSMark Brown case SND_CTL_ELEM_TYPE_BOOLEAN: 2365aaf9effSMark Brown int_val = snd_ctl_elem_value_get_boolean(ctl->def_val, 0); 2375aaf9effSMark Brown switch (int_val) { 2385aaf9effSMark Brown case 0: 2395aaf9effSMark Brown case 1: 2405aaf9effSMark Brown break; 2415aaf9effSMark Brown default: 2425aaf9effSMark Brown ksft_print_msg("%s Invalid boolean value %ld\n", 2435aaf9effSMark Brown ctl->name, int_val); 2445aaf9effSMark Brown err = -1; 2455aaf9effSMark Brown break; 2465aaf9effSMark Brown } 2475aaf9effSMark Brown break; 2485aaf9effSMark Brown 2495aaf9effSMark Brown case SND_CTL_ELEM_TYPE_INTEGER: 2505aaf9effSMark Brown int_val = snd_ctl_elem_value_get_integer(ctl->def_val, 0); 2515aaf9effSMark Brown 2525aaf9effSMark Brown if (int_val < snd_ctl_elem_info_get_min(ctl->info)) { 2535aaf9effSMark Brown ksft_print_msg("%s value %ld less than minimum %ld\n", 2545aaf9effSMark Brown ctl->name, int_val, 2555aaf9effSMark Brown snd_ctl_elem_info_get_min(ctl->info)); 2565aaf9effSMark Brown err = -1; 2575aaf9effSMark Brown } 2585aaf9effSMark Brown 2595aaf9effSMark Brown if (int_val > snd_ctl_elem_info_get_max(ctl->info)) { 2605aaf9effSMark Brown ksft_print_msg("%s value %ld more than maximum %ld\n", 2615aaf9effSMark Brown ctl->name, int_val, 2625aaf9effSMark Brown snd_ctl_elem_info_get_max(ctl->info)); 2635aaf9effSMark Brown err = -1; 2645aaf9effSMark Brown } 2655aaf9effSMark Brown 2665aaf9effSMark Brown /* Only check step size if there is one and we're in bounds */ 2675aaf9effSMark Brown if (err >= 0 && snd_ctl_elem_info_get_step(ctl->info) && 2685aaf9effSMark Brown (int_val - snd_ctl_elem_info_get_min(ctl->info) % 2695aaf9effSMark Brown snd_ctl_elem_info_get_step(ctl->info))) { 2705aaf9effSMark Brown ksft_print_msg("%s value %ld invalid for step %ld minimum %ld\n", 2715aaf9effSMark Brown ctl->name, int_val, 2725aaf9effSMark Brown snd_ctl_elem_info_get_step(ctl->info), 2735aaf9effSMark Brown snd_ctl_elem_info_get_min(ctl->info)); 2745aaf9effSMark Brown err = -1; 2755aaf9effSMark Brown } 2765aaf9effSMark Brown break; 2775aaf9effSMark Brown 2785aaf9effSMark Brown case SND_CTL_ELEM_TYPE_INTEGER64: 2795aaf9effSMark Brown int64_val = snd_ctl_elem_value_get_integer64(ctl->def_val, 0); 2805aaf9effSMark Brown 2815aaf9effSMark Brown if (int64_val < snd_ctl_elem_info_get_min64(ctl->info)) { 2825aaf9effSMark Brown ksft_print_msg("%s value %lld less than minimum %lld\n", 2835aaf9effSMark Brown ctl->name, int64_val, 2845aaf9effSMark Brown snd_ctl_elem_info_get_min64(ctl->info)); 2855aaf9effSMark Brown err = -1; 2865aaf9effSMark Brown } 2875aaf9effSMark Brown 2885aaf9effSMark Brown if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) { 2895aaf9effSMark Brown ksft_print_msg("%s value %lld more than maximum %lld\n", 2905aaf9effSMark Brown ctl->name, int64_val, 2915aaf9effSMark Brown snd_ctl_elem_info_get_max(ctl->info)); 2925aaf9effSMark Brown err = -1; 2935aaf9effSMark Brown } 2945aaf9effSMark Brown 2955aaf9effSMark Brown /* Only check step size if there is one and we're in bounds */ 2965aaf9effSMark Brown if (err >= 0 && snd_ctl_elem_info_get_step64(ctl->info) && 2975aaf9effSMark Brown (int64_val - snd_ctl_elem_info_get_min64(ctl->info)) % 2985aaf9effSMark Brown snd_ctl_elem_info_get_step64(ctl->info)) { 2995aaf9effSMark Brown ksft_print_msg("%s value %lld invalid for step %lld minimum %lld\n", 3005aaf9effSMark Brown ctl->name, int64_val, 3015aaf9effSMark Brown snd_ctl_elem_info_get_step64(ctl->info), 3025aaf9effSMark Brown snd_ctl_elem_info_get_min64(ctl->info)); 3035aaf9effSMark Brown err = -1; 3045aaf9effSMark Brown } 3055aaf9effSMark Brown break; 3065aaf9effSMark Brown 3075aaf9effSMark Brown default: 3085aaf9effSMark Brown /* No tests for other types */ 3095aaf9effSMark Brown ksft_test_result_skip("get_value.%d.%d\n", 3105aaf9effSMark Brown ctl->card->card, ctl->elem); 3115aaf9effSMark Brown return; 3125aaf9effSMark Brown } 3135aaf9effSMark Brown 3145aaf9effSMark Brown out: 3155aaf9effSMark Brown ksft_test_result(err >= 0, "get_value.%d.%d\n", 3165aaf9effSMark Brown ctl->card->card, ctl->elem); 3175aaf9effSMark Brown } 3185aaf9effSMark Brown 3195aaf9effSMark Brown bool show_mismatch(struct ctl_data *ctl, int index, 3205aaf9effSMark Brown snd_ctl_elem_value_t *read_val, 3215aaf9effSMark Brown snd_ctl_elem_value_t *expected_val) 3225aaf9effSMark Brown { 3235aaf9effSMark Brown long long expected_int, read_int; 3245aaf9effSMark Brown 3255aaf9effSMark Brown /* 3265aaf9effSMark Brown * We factor out the code to compare values representable as 3275aaf9effSMark Brown * integers, ensure that check doesn't log otherwise. 3285aaf9effSMark Brown */ 3295aaf9effSMark Brown expected_int = 0; 3305aaf9effSMark Brown read_int = 0; 3315aaf9effSMark Brown 3325aaf9effSMark Brown switch (snd_ctl_elem_info_get_type(ctl->info)) { 3335aaf9effSMark Brown case SND_CTL_ELEM_TYPE_BOOLEAN: 3345aaf9effSMark Brown expected_int = snd_ctl_elem_value_get_boolean(expected_val, 3355aaf9effSMark Brown index); 3365aaf9effSMark Brown read_int = snd_ctl_elem_value_get_boolean(read_val, index); 3375aaf9effSMark Brown break; 3385aaf9effSMark Brown 3395aaf9effSMark Brown case SND_CTL_ELEM_TYPE_INTEGER: 3405aaf9effSMark Brown expected_int = snd_ctl_elem_value_get_integer(expected_val, 3415aaf9effSMark Brown index); 3425aaf9effSMark Brown read_int = snd_ctl_elem_value_get_integer(read_val, index); 3435aaf9effSMark Brown break; 3445aaf9effSMark Brown 3455aaf9effSMark Brown case SND_CTL_ELEM_TYPE_INTEGER64: 3465aaf9effSMark Brown expected_int = snd_ctl_elem_value_get_integer64(expected_val, 3475aaf9effSMark Brown index); 3485aaf9effSMark Brown read_int = snd_ctl_elem_value_get_integer64(read_val, 3495aaf9effSMark Brown index); 3505aaf9effSMark Brown break; 3515aaf9effSMark Brown 3525aaf9effSMark Brown case SND_CTL_ELEM_TYPE_ENUMERATED: 3535aaf9effSMark Brown expected_int = snd_ctl_elem_value_get_enumerated(expected_val, 3545aaf9effSMark Brown index); 3555aaf9effSMark Brown read_int = snd_ctl_elem_value_get_enumerated(read_val, 3565aaf9effSMark Brown index); 3575aaf9effSMark Brown break; 3585aaf9effSMark Brown 3595aaf9effSMark Brown default: 3605aaf9effSMark Brown break; 3615aaf9effSMark Brown } 3625aaf9effSMark Brown 3635aaf9effSMark Brown if (expected_int != read_int) { 3647cc994f2STakashi Sakamoto /* 3657cc994f2STakashi Sakamoto * NOTE: The volatile attribute means that the hardware 3667cc994f2STakashi Sakamoto * can voluntarily change the state of control element 3677cc994f2STakashi Sakamoto * independent of any operation by software. 3687cc994f2STakashi Sakamoto */ 3697cc994f2STakashi Sakamoto bool is_volatile = snd_ctl_elem_info_is_volatile(ctl->info); 3707cc994f2STakashi Sakamoto ksft_print_msg("%s.%d expected %lld but read %lld, is_volatile %d\n", 3717cc994f2STakashi Sakamoto ctl->name, index, expected_int, read_int, is_volatile); 3727cc994f2STakashi Sakamoto return !is_volatile; 3735aaf9effSMark Brown } else { 3745aaf9effSMark Brown return false; 3755aaf9effSMark Brown } 3765aaf9effSMark Brown } 3775aaf9effSMark Brown 3785aaf9effSMark Brown /* 3795aaf9effSMark Brown * Write a value then if possible verify that we get the expected 3805aaf9effSMark Brown * result. An optional expected value can be provided if we expect 3815aaf9effSMark Brown * the write to fail, for verifying that invalid writes don't corrupt 3825aaf9effSMark Brown * anything. 3835aaf9effSMark Brown */ 3845aaf9effSMark Brown int write_and_verify(struct ctl_data *ctl, 3855aaf9effSMark Brown snd_ctl_elem_value_t *write_val, 3865aaf9effSMark Brown snd_ctl_elem_value_t *expected_val) 3875aaf9effSMark Brown { 3885aaf9effSMark Brown int err, i; 3895aaf9effSMark Brown bool error_expected, mismatch_shown; 3905aaf9effSMark Brown snd_ctl_elem_value_t *read_val, *w_val; 3915aaf9effSMark Brown snd_ctl_elem_value_alloca(&read_val); 3925aaf9effSMark Brown snd_ctl_elem_value_alloca(&w_val); 3935aaf9effSMark Brown 3945aaf9effSMark Brown /* 3955aaf9effSMark Brown * We need to copy the write value since writing can modify 3965aaf9effSMark Brown * the value which causes surprises, and allocate an expected 3975aaf9effSMark Brown * value if we expect to read back what we wrote. 3985aaf9effSMark Brown */ 3995aaf9effSMark Brown snd_ctl_elem_value_copy(w_val, write_val); 4005aaf9effSMark Brown if (expected_val) { 4015aaf9effSMark Brown error_expected = true; 4025aaf9effSMark Brown } else { 4035aaf9effSMark Brown error_expected = false; 4045aaf9effSMark Brown snd_ctl_elem_value_alloca(&expected_val); 4055aaf9effSMark Brown snd_ctl_elem_value_copy(expected_val, write_val); 4065aaf9effSMark Brown } 4075aaf9effSMark Brown 4085aaf9effSMark Brown /* 4095aaf9effSMark Brown * Do the write, if we have an expected value ignore the error 4105aaf9effSMark Brown * and carry on to validate the expected value. 4115aaf9effSMark Brown */ 4125aaf9effSMark Brown err = snd_ctl_elem_write(ctl->card->handle, w_val); 4135aaf9effSMark Brown if (err < 0 && !error_expected) { 4145aaf9effSMark Brown ksft_print_msg("snd_ctl_elem_write() failed: %s\n", 4155aaf9effSMark Brown snd_strerror(err)); 4165aaf9effSMark Brown return err; 4175aaf9effSMark Brown } 4185aaf9effSMark Brown 4195aaf9effSMark Brown /* Can we do the verification part? */ 4205aaf9effSMark Brown if (!snd_ctl_elem_info_is_readable(ctl->info)) 4215aaf9effSMark Brown return err; 4225aaf9effSMark Brown 4235aaf9effSMark Brown snd_ctl_elem_value_set_id(read_val, ctl->id); 4245aaf9effSMark Brown 4255aaf9effSMark Brown err = snd_ctl_elem_read(ctl->card->handle, read_val); 4265aaf9effSMark Brown if (err < 0) { 4275aaf9effSMark Brown ksft_print_msg("snd_ctl_elem_read() failed: %s\n", 4285aaf9effSMark Brown snd_strerror(err)); 4295aaf9effSMark Brown return err; 4305aaf9effSMark Brown } 4315aaf9effSMark Brown 4325aaf9effSMark Brown /* 4335aaf9effSMark Brown * Use the libray to compare values, if there's a mismatch 4345aaf9effSMark Brown * carry on and try to provide a more useful diagnostic than 4355aaf9effSMark Brown * just "mismatch". 4365aaf9effSMark Brown */ 4375aaf9effSMark Brown if (!snd_ctl_elem_value_compare(expected_val, read_val)) 4385aaf9effSMark Brown return 0; 4395aaf9effSMark Brown 4405aaf9effSMark Brown mismatch_shown = false; 4415aaf9effSMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) 4425aaf9effSMark Brown if (show_mismatch(ctl, i, read_val, expected_val)) 4435aaf9effSMark Brown mismatch_shown = true; 4445aaf9effSMark Brown 4455aaf9effSMark Brown if (!mismatch_shown) 4465aaf9effSMark Brown ksft_print_msg("%s read and written values differ\n", 4475aaf9effSMark Brown ctl->name); 4485aaf9effSMark Brown 4495aaf9effSMark Brown return -1; 4505aaf9effSMark Brown } 4515aaf9effSMark Brown 4525aaf9effSMark Brown /* 4535aaf9effSMark Brown * Make sure we can write the default value back to the control, this 4545aaf9effSMark Brown * should validate that at least some write works. 4555aaf9effSMark Brown */ 4565aaf9effSMark Brown void test_ctl_write_default(struct ctl_data *ctl) 4575aaf9effSMark Brown { 4585aaf9effSMark Brown int err; 4595aaf9effSMark Brown 4605aaf9effSMark Brown /* If the control is turned off let's be polite */ 4615aaf9effSMark Brown if (snd_ctl_elem_info_is_inactive(ctl->info)) { 4625aaf9effSMark Brown ksft_print_msg("%s is inactive\n", ctl->name); 4635aaf9effSMark Brown ksft_test_result_skip("write_default.%d.%d\n", 4645aaf9effSMark Brown ctl->card->card, ctl->elem); 4655aaf9effSMark Brown return; 4665aaf9effSMark Brown } 4675aaf9effSMark Brown 4685aaf9effSMark Brown if (!snd_ctl_elem_info_is_writable(ctl->info)) { 4695aaf9effSMark Brown ksft_print_msg("%s is not writeable\n", ctl->name); 4705aaf9effSMark Brown ksft_test_result_skip("write_default.%d.%d\n", 4715aaf9effSMark Brown ctl->card->card, ctl->elem); 4725aaf9effSMark Brown return; 4735aaf9effSMark Brown } 4745aaf9effSMark Brown 4755aaf9effSMark Brown /* No idea what the default was for unreadable controls */ 4765aaf9effSMark Brown if (!snd_ctl_elem_info_is_readable(ctl->info)) { 4775aaf9effSMark Brown ksft_print_msg("%s couldn't read default\n", ctl->name); 4785aaf9effSMark Brown ksft_test_result_skip("write_default.%d.%d\n", 4795aaf9effSMark Brown ctl->card->card, ctl->elem); 4805aaf9effSMark Brown return; 4815aaf9effSMark Brown } 4825aaf9effSMark Brown 4835aaf9effSMark Brown err = write_and_verify(ctl, ctl->def_val, NULL); 4845aaf9effSMark Brown 4855aaf9effSMark Brown ksft_test_result(err >= 0, "write_default.%d.%d\n", 4865aaf9effSMark Brown ctl->card->card, ctl->elem); 4875aaf9effSMark Brown } 4885aaf9effSMark Brown 4895aaf9effSMark Brown bool test_ctl_write_valid_boolean(struct ctl_data *ctl) 4905aaf9effSMark Brown { 4915aaf9effSMark Brown int err, i, j; 4925aaf9effSMark Brown bool fail = false; 4935aaf9effSMark Brown snd_ctl_elem_value_t *val; 4945aaf9effSMark Brown snd_ctl_elem_value_alloca(&val); 4955aaf9effSMark Brown 4965aaf9effSMark Brown snd_ctl_elem_value_set_id(val, ctl->id); 4975aaf9effSMark Brown 4985aaf9effSMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 4995aaf9effSMark Brown for (j = 0; j < 2; j++) { 5005aaf9effSMark Brown snd_ctl_elem_value_set_boolean(val, i, j); 5015aaf9effSMark Brown err = write_and_verify(ctl, val, NULL); 5025aaf9effSMark Brown if (err != 0) 5035aaf9effSMark Brown fail = true; 5045aaf9effSMark Brown } 5055aaf9effSMark Brown } 5065aaf9effSMark Brown 5075aaf9effSMark Brown return !fail; 5085aaf9effSMark Brown } 5095aaf9effSMark Brown 5105aaf9effSMark Brown bool test_ctl_write_valid_integer(struct ctl_data *ctl) 5115aaf9effSMark Brown { 5125aaf9effSMark Brown int err; 5135aaf9effSMark Brown int i; 5145aaf9effSMark Brown long j, step; 5155aaf9effSMark Brown bool fail = false; 5165aaf9effSMark Brown snd_ctl_elem_value_t *val; 5175aaf9effSMark Brown snd_ctl_elem_value_alloca(&val); 5185aaf9effSMark Brown 5195aaf9effSMark Brown snd_ctl_elem_value_set_id(val, ctl->id); 5205aaf9effSMark Brown 5215aaf9effSMark Brown step = snd_ctl_elem_info_get_step(ctl->info); 5225aaf9effSMark Brown if (!step) 5235aaf9effSMark Brown step = 1; 5245aaf9effSMark Brown 5255aaf9effSMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 5265aaf9effSMark Brown for (j = snd_ctl_elem_info_get_min(ctl->info); 5275aaf9effSMark Brown j <= snd_ctl_elem_info_get_max(ctl->info); j += step) { 5285aaf9effSMark Brown 5295aaf9effSMark Brown snd_ctl_elem_value_set_integer(val, i, j); 5305aaf9effSMark Brown err = write_and_verify(ctl, val, NULL); 5315aaf9effSMark Brown if (err != 0) 5325aaf9effSMark Brown fail = true; 5335aaf9effSMark Brown } 5345aaf9effSMark Brown } 5355aaf9effSMark Brown 5365aaf9effSMark Brown 5375aaf9effSMark Brown return !fail; 5385aaf9effSMark Brown } 5395aaf9effSMark Brown 5405aaf9effSMark Brown bool test_ctl_write_valid_integer64(struct ctl_data *ctl) 5415aaf9effSMark Brown { 5425aaf9effSMark Brown int err, i; 5435aaf9effSMark Brown long long j, step; 5445aaf9effSMark Brown bool fail = false; 5455aaf9effSMark Brown snd_ctl_elem_value_t *val; 5465aaf9effSMark Brown snd_ctl_elem_value_alloca(&val); 5475aaf9effSMark Brown 5485aaf9effSMark Brown snd_ctl_elem_value_set_id(val, ctl->id); 5495aaf9effSMark Brown 5505aaf9effSMark Brown step = snd_ctl_elem_info_get_step64(ctl->info); 5515aaf9effSMark Brown if (!step) 5525aaf9effSMark Brown step = 1; 5535aaf9effSMark Brown 5545aaf9effSMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 5555aaf9effSMark Brown for (j = snd_ctl_elem_info_get_min64(ctl->info); 5565aaf9effSMark Brown j <= snd_ctl_elem_info_get_max64(ctl->info); j += step) { 5575aaf9effSMark Brown 5585aaf9effSMark Brown snd_ctl_elem_value_set_integer64(val, i, j); 5595aaf9effSMark Brown err = write_and_verify(ctl, val, NULL); 5605aaf9effSMark Brown if (err != 0) 5615aaf9effSMark Brown fail = true; 5625aaf9effSMark Brown } 5635aaf9effSMark Brown } 5645aaf9effSMark Brown 5655aaf9effSMark Brown return !fail; 5665aaf9effSMark Brown } 5675aaf9effSMark Brown 5685aaf9effSMark Brown bool test_ctl_write_valid_enumerated(struct ctl_data *ctl) 5695aaf9effSMark Brown { 5705aaf9effSMark Brown int err, i, j; 5715aaf9effSMark Brown bool fail = false; 5725aaf9effSMark Brown snd_ctl_elem_value_t *val; 5735aaf9effSMark Brown snd_ctl_elem_value_alloca(&val); 5745aaf9effSMark Brown 5755aaf9effSMark Brown snd_ctl_elem_value_set_id(val, ctl->id); 5765aaf9effSMark Brown 5775aaf9effSMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 5785aaf9effSMark Brown for (j = 0; j < snd_ctl_elem_info_get_items(ctl->info); j++) { 5795aaf9effSMark Brown snd_ctl_elem_value_set_enumerated(val, i, j); 5805aaf9effSMark Brown err = write_and_verify(ctl, val, NULL); 5815aaf9effSMark Brown if (err != 0) 5825aaf9effSMark Brown fail = true; 5835aaf9effSMark Brown } 5845aaf9effSMark Brown } 5855aaf9effSMark Brown 5865aaf9effSMark Brown return !fail; 5875aaf9effSMark Brown } 5885aaf9effSMark Brown 5895aaf9effSMark Brown void test_ctl_write_valid(struct ctl_data *ctl) 5905aaf9effSMark Brown { 5915aaf9effSMark Brown bool pass; 5925aaf9effSMark Brown int err; 5935aaf9effSMark Brown 5945aaf9effSMark Brown /* If the control is turned off let's be polite */ 5955aaf9effSMark Brown if (snd_ctl_elem_info_is_inactive(ctl->info)) { 5965aaf9effSMark Brown ksft_print_msg("%s is inactive\n", ctl->name); 5975aaf9effSMark Brown ksft_test_result_skip("write_valid.%d.%d\n", 5985aaf9effSMark Brown ctl->card->card, ctl->elem); 5995aaf9effSMark Brown return; 6005aaf9effSMark Brown } 6015aaf9effSMark Brown 6025aaf9effSMark Brown if (!snd_ctl_elem_info_is_writable(ctl->info)) { 6035aaf9effSMark Brown ksft_print_msg("%s is not writeable\n", ctl->name); 6045aaf9effSMark Brown ksft_test_result_skip("write_valid.%d.%d\n", 6055aaf9effSMark Brown ctl->card->card, ctl->elem); 6065aaf9effSMark Brown return; 6075aaf9effSMark Brown } 6085aaf9effSMark Brown 6095aaf9effSMark Brown switch (snd_ctl_elem_info_get_type(ctl->info)) { 6105aaf9effSMark Brown case SND_CTL_ELEM_TYPE_BOOLEAN: 6115aaf9effSMark Brown pass = test_ctl_write_valid_boolean(ctl); 6125aaf9effSMark Brown break; 6135aaf9effSMark Brown 6145aaf9effSMark Brown case SND_CTL_ELEM_TYPE_INTEGER: 6155aaf9effSMark Brown pass = test_ctl_write_valid_integer(ctl); 6165aaf9effSMark Brown break; 6175aaf9effSMark Brown 6185aaf9effSMark Brown case SND_CTL_ELEM_TYPE_INTEGER64: 6195aaf9effSMark Brown pass = test_ctl_write_valid_integer64(ctl); 6205aaf9effSMark Brown break; 6215aaf9effSMark Brown 6225aaf9effSMark Brown case SND_CTL_ELEM_TYPE_ENUMERATED: 6235aaf9effSMark Brown pass = test_ctl_write_valid_enumerated(ctl); 6245aaf9effSMark Brown break; 6255aaf9effSMark Brown 6265aaf9effSMark Brown default: 6275aaf9effSMark Brown /* No tests for this yet */ 6285aaf9effSMark Brown ksft_test_result_skip("write_valid.%d.%d\n", 6295aaf9effSMark Brown ctl->card->card, ctl->elem); 6305aaf9effSMark Brown return; 6315aaf9effSMark Brown } 6325aaf9effSMark Brown 6335aaf9effSMark Brown /* Restore the default value to minimise disruption */ 6345aaf9effSMark Brown err = write_and_verify(ctl, ctl->def_val, NULL); 6355aaf9effSMark Brown if (err < 0) 6365aaf9effSMark Brown pass = false; 6375aaf9effSMark Brown 6385aaf9effSMark Brown ksft_test_result(pass, "write_valid.%d.%d\n", 6395aaf9effSMark Brown ctl->card->card, ctl->elem); 6405aaf9effSMark Brown } 6415aaf9effSMark Brown 6425aaf9effSMark Brown int main(void) 6435aaf9effSMark Brown { 6445aaf9effSMark Brown struct ctl_data *ctl; 6455aaf9effSMark Brown 6465aaf9effSMark Brown ksft_print_header(); 6475aaf9effSMark Brown 6485aaf9effSMark Brown find_controls(); 6495aaf9effSMark Brown 6505aaf9effSMark Brown ksft_set_plan(num_controls * TESTS_PER_CONTROL); 6515aaf9effSMark Brown 6525aaf9effSMark Brown for (ctl = ctl_list; ctl != NULL; ctl = ctl->next) { 6535aaf9effSMark Brown /* 6545aaf9effSMark Brown * Must test get_value() before we write anything, the 6555aaf9effSMark Brown * test stores the default value for later cleanup. 6565aaf9effSMark Brown */ 6575aaf9effSMark Brown test_ctl_get_value(ctl); 6585aaf9effSMark Brown test_ctl_write_default(ctl); 6595aaf9effSMark Brown test_ctl_write_valid(ctl); 6605aaf9effSMark Brown } 6615aaf9effSMark Brown 6625aaf9effSMark Brown ksft_exit_pass(); 6635aaf9effSMark Brown 6645aaf9effSMark Brown return 0; 6655aaf9effSMark Brown } 666