xref: /openbmc/linux/tools/testing/selftests/alsa/mixer-test.c (revision 10f2f194663af178f32aeb4086fc3f6687d25056)
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 
49b73dad80SJaroslav Kysela static const char *alsa_config =
50b73dad80SJaroslav Kysela "ctl.hw {\n"
51b73dad80SJaroslav Kysela "	@args [ CARD ]\n"
52b73dad80SJaroslav Kysela "	@args.CARD.type string\n"
53b73dad80SJaroslav Kysela "	type hw\n"
54b73dad80SJaroslav Kysela "	card $CARD\n"
55b73dad80SJaroslav Kysela "}\n"
56b73dad80SJaroslav Kysela ;
57b73dad80SJaroslav 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 
63b73dad80SJaroslav Kysela #ifdef SND_LIB_VER
64b73dad80SJaroslav Kysela #if SND_LIB_VERSION >= SND_LIB_VER(1, 2, 6)
65b73dad80SJaroslav Kysela #define LIB_HAS_LOAD_STRING
66b73dad80SJaroslav Kysela #endif
67b73dad80SJaroslav Kysela #endif
68b73dad80SJaroslav Kysela 
69b73dad80SJaroslav Kysela #ifndef LIB_HAS_LOAD_STRING
70b73dad80SJaroslav Kysela int snd_config_load_string(snd_config_t **config, const char *s, size_t size)
71b73dad80SJaroslav Kysela {
72b73dad80SJaroslav Kysela 	snd_input_t *input;
73b73dad80SJaroslav Kysela 	snd_config_t *dst;
74b73dad80SJaroslav Kysela 	int err;
75b73dad80SJaroslav Kysela 
76b73dad80SJaroslav Kysela 	assert(config && s);
77b73dad80SJaroslav Kysela 	if (size == 0)
78b73dad80SJaroslav Kysela 		size = strlen(s);
79b73dad80SJaroslav Kysela 	err = snd_input_buffer_open(&input, s, size);
80b73dad80SJaroslav Kysela 	if (err < 0)
81b73dad80SJaroslav Kysela 		return err;
82b73dad80SJaroslav Kysela 	err = snd_config_top(&dst);
83b73dad80SJaroslav Kysela 	if (err < 0) {
84b73dad80SJaroslav Kysela 		snd_input_close(input);
85b73dad80SJaroslav Kysela 		return err;
86b73dad80SJaroslav Kysela 	}
87b73dad80SJaroslav Kysela 	err = snd_config_load(dst, input);
88b73dad80SJaroslav Kysela 	snd_input_close(input);
89b73dad80SJaroslav Kysela 	if (err < 0) {
90b73dad80SJaroslav Kysela 		snd_config_delete(dst);
91b73dad80SJaroslav Kysela 		return err;
92b73dad80SJaroslav Kysela 	}
93b73dad80SJaroslav Kysela 	*config = dst;
94b73dad80SJaroslav Kysela 	return 0;
95b73dad80SJaroslav Kysela }
96b73dad80SJaroslav Kysela #endif
97b73dad80SJaroslav 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;
104b73dad80SJaroslav 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 
110b73dad80SJaroslav Kysela 	err = snd_config_load_string(&config, alsa_config, strlen(alsa_config));
111b73dad80SJaroslav Kysela 	if (err < 0) {
112b73dad80SJaroslav Kysela 		ksft_print_msg("Unable to parse custom alsa-lib configuration: %s\n",
113b73dad80SJaroslav Kysela 			       snd_strerror(err));
114b73dad80SJaroslav Kysela 		ksft_exit_fail();
115b73dad80SJaroslav Kysela 	}
116b73dad80SJaroslav 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 
124b73dad80SJaroslav 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 	}
192b73dad80SJaroslav Kysela 
193b73dad80SJaroslav Kysela 	snd_config_delete(config);
1945aaf9effSMark Brown }
1955aaf9effSMark Brown 
1963f48b137SMark Brown bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val,
1973f48b137SMark Brown 			   int index)
1983f48b137SMark Brown {
1993f48b137SMark Brown 	long int_val;
2003f48b137SMark Brown 	long long int64_val;
2013f48b137SMark Brown 
2023f48b137SMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
2033f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_NONE:
2043f48b137SMark Brown 		ksft_print_msg("%s.%d Invalid control type NONE\n",
2053f48b137SMark Brown 			       ctl->name, index);
2063f48b137SMark Brown 		return false;
2073f48b137SMark Brown 
2083f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
2093f48b137SMark Brown 		int_val = snd_ctl_elem_value_get_boolean(val, index);
2103f48b137SMark Brown 		switch (int_val) {
2113f48b137SMark Brown 		case 0:
2123f48b137SMark Brown 		case 1:
2133f48b137SMark Brown 			break;
2143f48b137SMark Brown 		default:
2153f48b137SMark Brown 			ksft_print_msg("%s.%d Invalid boolean value %ld\n",
2163f48b137SMark Brown 				       ctl->name, index, int_val);
2173f48b137SMark Brown 			return false;
2183f48b137SMark Brown 		}
2193f48b137SMark Brown 		break;
2203f48b137SMark Brown 
2213f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
2223f48b137SMark Brown 		int_val = snd_ctl_elem_value_get_integer(val, index);
2233f48b137SMark Brown 
2243f48b137SMark Brown 		if (int_val < snd_ctl_elem_info_get_min(ctl->info)) {
2253f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld less than minimum %ld\n",
2263f48b137SMark Brown 				       ctl->name, index, int_val,
2273f48b137SMark Brown 				       snd_ctl_elem_info_get_min(ctl->info));
2283f48b137SMark Brown 			return false;
2293f48b137SMark Brown 		}
2303f48b137SMark Brown 
2313f48b137SMark Brown 		if (int_val > snd_ctl_elem_info_get_max(ctl->info)) {
2323f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld more than maximum %ld\n",
2333f48b137SMark Brown 				       ctl->name, index, int_val,
2343f48b137SMark Brown 				       snd_ctl_elem_info_get_max(ctl->info));
2353f48b137SMark Brown 			return false;
2363f48b137SMark Brown 		}
2373f48b137SMark Brown 
2383f48b137SMark Brown 		/* Only check step size if there is one and we're in bounds */
2393f48b137SMark Brown 		if (snd_ctl_elem_info_get_step(ctl->info) &&
2403f48b137SMark Brown 		    (int_val - snd_ctl_elem_info_get_min(ctl->info) %
2413f48b137SMark Brown 		     snd_ctl_elem_info_get_step(ctl->info))) {
2423f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld invalid for step %ld minimum %ld\n",
2433f48b137SMark Brown 				       ctl->name, index, int_val,
2443f48b137SMark Brown 				       snd_ctl_elem_info_get_step(ctl->info),
2453f48b137SMark Brown 				       snd_ctl_elem_info_get_min(ctl->info));
2463f48b137SMark Brown 			return false;
2473f48b137SMark Brown 		}
2483f48b137SMark Brown 		break;
2493f48b137SMark Brown 
2503f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
2513f48b137SMark Brown 		int64_val = snd_ctl_elem_value_get_integer64(val, index);
2523f48b137SMark Brown 
2533f48b137SMark Brown 		if (int64_val < snd_ctl_elem_info_get_min64(ctl->info)) {
2543f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld less than minimum %lld\n",
2553f48b137SMark Brown 				       ctl->name, index, int64_val,
2563f48b137SMark Brown 				       snd_ctl_elem_info_get_min64(ctl->info));
2573f48b137SMark Brown 			return false;
2583f48b137SMark Brown 		}
2593f48b137SMark Brown 
2603f48b137SMark Brown 		if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) {
2613f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld more than maximum %lld\n",
2623f48b137SMark Brown 				       ctl->name, index, int64_val,
2633f48b137SMark Brown 				       snd_ctl_elem_info_get_max(ctl->info));
2643f48b137SMark Brown 			return false;
2653f48b137SMark Brown 		}
2663f48b137SMark Brown 
2673f48b137SMark Brown 		/* Only check step size if there is one and we're in bounds */
2683f48b137SMark Brown 		if (snd_ctl_elem_info_get_step64(ctl->info) &&
2693f48b137SMark Brown 		    (int64_val - snd_ctl_elem_info_get_min64(ctl->info)) %
2703f48b137SMark Brown 		    snd_ctl_elem_info_get_step64(ctl->info)) {
2713f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld invalid for step %lld minimum %lld\n",
2723f48b137SMark Brown 				       ctl->name, index, int64_val,
2733f48b137SMark Brown 				       snd_ctl_elem_info_get_step64(ctl->info),
2743f48b137SMark Brown 				       snd_ctl_elem_info_get_min64(ctl->info));
2753f48b137SMark Brown 			return false;
2763f48b137SMark Brown 		}
2773f48b137SMark Brown 		break;
2783f48b137SMark Brown 
279*10f2f194SMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
280*10f2f194SMark Brown 		int_val = snd_ctl_elem_value_get_enumerated(val, index);
281*10f2f194SMark Brown 
282*10f2f194SMark Brown 		if (int_val < 0) {
283*10f2f194SMark Brown 			ksft_print_msg("%s.%d negative value %ld for enumeration\n",
284*10f2f194SMark Brown 				       ctl->name, index, int_val);
285*10f2f194SMark Brown 			return false;
286*10f2f194SMark Brown 		}
287*10f2f194SMark Brown 
288*10f2f194SMark Brown 		if (int_val >= snd_ctl_elem_info_get_items(ctl->info)) {
289*10f2f194SMark Brown 			ksft_print_msg("%s.%d value %ld more than item count %ld\n",
290*10f2f194SMark Brown 				       ctl->name, index, int_val,
291*10f2f194SMark Brown 				       snd_ctl_elem_info_get_items(ctl->info));
292*10f2f194SMark Brown 			return false;
293*10f2f194SMark Brown 		}
294*10f2f194SMark Brown 		break;
295*10f2f194SMark Brown 
2963f48b137SMark Brown 	default:
2973f48b137SMark Brown 		/* No tests for other types */
2983f48b137SMark Brown 		break;
2993f48b137SMark Brown 	}
3003f48b137SMark Brown 
3013f48b137SMark Brown 	return true;
3023f48b137SMark Brown }
3033f48b137SMark Brown 
3043f48b137SMark Brown /*
3053f48b137SMark Brown  * Check that the provided value meets the constraints for the
3063f48b137SMark Brown  * provided control.
3073f48b137SMark Brown  */
3083f48b137SMark Brown bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val)
3093f48b137SMark Brown {
3103f48b137SMark Brown 	int i;
3113f48b137SMark Brown 	bool valid = true;
3123f48b137SMark Brown 
3133f48b137SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++)
3143f48b137SMark Brown 		if (!ctl_value_index_valid(ctl, val, i))
3153f48b137SMark Brown 			valid = false;
3163f48b137SMark Brown 
3173f48b137SMark Brown 	return valid;
3183f48b137SMark Brown }
3193f48b137SMark Brown 
3205aaf9effSMark Brown /*
3215aaf9effSMark Brown  * Check that we can read the default value and it is valid. Write
3225aaf9effSMark Brown  * tests use the read value to restore the default.
3235aaf9effSMark Brown  */
3245aaf9effSMark Brown void test_ctl_get_value(struct ctl_data *ctl)
3255aaf9effSMark Brown {
3265aaf9effSMark Brown 	int err;
3275aaf9effSMark Brown 
3285aaf9effSMark Brown 	/* If the control is turned off let's be polite */
3295aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
3305aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
3315aaf9effSMark Brown 		ksft_test_result_skip("get_value.%d.%d\n",
3325aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
3335aaf9effSMark Brown 		return;
3345aaf9effSMark Brown 	}
3355aaf9effSMark Brown 
3365aaf9effSMark Brown 	/* Can't test reading on an unreadable control */
3375aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info)) {
3385aaf9effSMark Brown 		ksft_print_msg("%s is not readable\n", ctl->name);
3395aaf9effSMark Brown 		ksft_test_result_skip("get_value.%d.%d\n",
3405aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
3415aaf9effSMark Brown 		return;
3425aaf9effSMark Brown 	}
3435aaf9effSMark Brown 
3445aaf9effSMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val);
3455aaf9effSMark Brown 	if (err < 0) {
3465aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
3475aaf9effSMark Brown 			       snd_strerror(err));
3485aaf9effSMark Brown 		goto out;
3495aaf9effSMark Brown 	}
3505aaf9effSMark Brown 
3513f48b137SMark Brown 	if (!ctl_value_valid(ctl, ctl->def_val))
3523f48b137SMark Brown 		err = -EINVAL;
3535aaf9effSMark Brown 
3545aaf9effSMark Brown out:
3555aaf9effSMark Brown 	ksft_test_result(err >= 0, "get_value.%d.%d\n",
3565aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
3575aaf9effSMark Brown }
3585aaf9effSMark Brown 
3595aaf9effSMark Brown bool show_mismatch(struct ctl_data *ctl, int index,
3605aaf9effSMark Brown 		   snd_ctl_elem_value_t *read_val,
3615aaf9effSMark Brown 		   snd_ctl_elem_value_t *expected_val)
3625aaf9effSMark Brown {
3635aaf9effSMark Brown 	long long expected_int, read_int;
3645aaf9effSMark Brown 
3655aaf9effSMark Brown 	/*
3665aaf9effSMark Brown 	 * We factor out the code to compare values representable as
3675aaf9effSMark Brown 	 * integers, ensure that check doesn't log otherwise.
3685aaf9effSMark Brown 	 */
3695aaf9effSMark Brown 	expected_int = 0;
3705aaf9effSMark Brown 	read_int = 0;
3715aaf9effSMark Brown 
3725aaf9effSMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
3735aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
3745aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_boolean(expected_val,
3755aaf9effSMark Brown 							      index);
3765aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_boolean(read_val, index);
3775aaf9effSMark Brown 		break;
3785aaf9effSMark Brown 
3795aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
3805aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_integer(expected_val,
3815aaf9effSMark Brown 							      index);
3825aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_integer(read_val, index);
3835aaf9effSMark Brown 		break;
3845aaf9effSMark Brown 
3855aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
3865aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_integer64(expected_val,
3875aaf9effSMark Brown 								index);
3885aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_integer64(read_val,
3895aaf9effSMark Brown 							    index);
3905aaf9effSMark Brown 		break;
3915aaf9effSMark Brown 
3925aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
3935aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_enumerated(expected_val,
3945aaf9effSMark Brown 								 index);
3955aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_enumerated(read_val,
3965aaf9effSMark Brown 							     index);
3975aaf9effSMark Brown 		break;
3985aaf9effSMark Brown 
3995aaf9effSMark Brown 	default:
4005aaf9effSMark Brown 		break;
4015aaf9effSMark Brown 	}
4025aaf9effSMark Brown 
4035aaf9effSMark Brown 	if (expected_int != read_int) {
4047cc994f2STakashi Sakamoto 		/*
4057cc994f2STakashi Sakamoto 		 * NOTE: The volatile attribute means that the hardware
4067cc994f2STakashi Sakamoto 		 * can voluntarily change the state of control element
4077cc994f2STakashi Sakamoto 		 * independent of any operation by software.
4087cc994f2STakashi Sakamoto 		 */
4097cc994f2STakashi Sakamoto 		bool is_volatile = snd_ctl_elem_info_is_volatile(ctl->info);
4107cc994f2STakashi Sakamoto 		ksft_print_msg("%s.%d expected %lld but read %lld, is_volatile %d\n",
4117cc994f2STakashi Sakamoto 			       ctl->name, index, expected_int, read_int, is_volatile);
4127cc994f2STakashi Sakamoto 		return !is_volatile;
4135aaf9effSMark Brown 	} else {
4145aaf9effSMark Brown 		return false;
4155aaf9effSMark Brown 	}
4165aaf9effSMark Brown }
4175aaf9effSMark Brown 
4185aaf9effSMark Brown /*
4195aaf9effSMark Brown  * Write a value then if possible verify that we get the expected
4205aaf9effSMark Brown  * result.  An optional expected value can be provided if we expect
4215aaf9effSMark Brown  * the write to fail, for verifying that invalid writes don't corrupt
4225aaf9effSMark Brown  * anything.
4235aaf9effSMark Brown  */
4245aaf9effSMark Brown int write_and_verify(struct ctl_data *ctl,
4255aaf9effSMark Brown 		     snd_ctl_elem_value_t *write_val,
4265aaf9effSMark Brown 		     snd_ctl_elem_value_t *expected_val)
4275aaf9effSMark Brown {
4285aaf9effSMark Brown 	int err, i;
4295aaf9effSMark Brown 	bool error_expected, mismatch_shown;
4305aaf9effSMark Brown 	snd_ctl_elem_value_t *read_val, *w_val;
4315aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&read_val);
4325aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&w_val);
4335aaf9effSMark Brown 
4345aaf9effSMark Brown 	/*
4355aaf9effSMark Brown 	 * We need to copy the write value since writing can modify
4365aaf9effSMark Brown 	 * the value which causes surprises, and allocate an expected
4375aaf9effSMark Brown 	 * value if we expect to read back what we wrote.
4385aaf9effSMark Brown 	 */
4395aaf9effSMark Brown 	snd_ctl_elem_value_copy(w_val, write_val);
4405aaf9effSMark Brown 	if (expected_val) {
4415aaf9effSMark Brown 		error_expected = true;
4425aaf9effSMark Brown 	} else {
4435aaf9effSMark Brown 		error_expected = false;
4445aaf9effSMark Brown 		snd_ctl_elem_value_alloca(&expected_val);
4455aaf9effSMark Brown 		snd_ctl_elem_value_copy(expected_val, write_val);
4465aaf9effSMark Brown 	}
4475aaf9effSMark Brown 
4485aaf9effSMark Brown 	/*
4495aaf9effSMark Brown 	 * Do the write, if we have an expected value ignore the error
4505aaf9effSMark Brown 	 * and carry on to validate the expected value.
4515aaf9effSMark Brown 	 */
4525aaf9effSMark Brown 	err = snd_ctl_elem_write(ctl->card->handle, w_val);
4535aaf9effSMark Brown 	if (err < 0 && !error_expected) {
4545aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_write() failed: %s\n",
4555aaf9effSMark Brown 			       snd_strerror(err));
4565aaf9effSMark Brown 		return err;
4575aaf9effSMark Brown 	}
4585aaf9effSMark Brown 
4595aaf9effSMark Brown 	/* Can we do the verification part? */
4605aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info))
4615aaf9effSMark Brown 		return err;
4625aaf9effSMark Brown 
4635aaf9effSMark Brown 	snd_ctl_elem_value_set_id(read_val, ctl->id);
4645aaf9effSMark Brown 
4655aaf9effSMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, read_val);
4665aaf9effSMark Brown 	if (err < 0) {
4675aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
4685aaf9effSMark Brown 			       snd_strerror(err));
4695aaf9effSMark Brown 		return err;
4705aaf9effSMark Brown 	}
4715aaf9effSMark Brown 
4725aaf9effSMark Brown 	/*
4735aaf9effSMark Brown 	 * Use the libray to compare values, if there's a mismatch
4745aaf9effSMark Brown 	 * carry on and try to provide a more useful diagnostic than
4755aaf9effSMark Brown 	 * just "mismatch".
4765aaf9effSMark Brown 	 */
4775aaf9effSMark Brown 	if (!snd_ctl_elem_value_compare(expected_val, read_val))
4785aaf9effSMark Brown 		return 0;
4795aaf9effSMark Brown 
4805aaf9effSMark Brown 	mismatch_shown = false;
4815aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++)
4825aaf9effSMark Brown 		if (show_mismatch(ctl, i, read_val, expected_val))
4835aaf9effSMark Brown 			mismatch_shown = true;
4845aaf9effSMark Brown 
4855aaf9effSMark Brown 	if (!mismatch_shown)
4865aaf9effSMark Brown 		ksft_print_msg("%s read and written values differ\n",
4875aaf9effSMark Brown 			       ctl->name);
4885aaf9effSMark Brown 
4895aaf9effSMark Brown 	return -1;
4905aaf9effSMark Brown }
4915aaf9effSMark Brown 
4925aaf9effSMark Brown /*
4935aaf9effSMark Brown  * Make sure we can write the default value back to the control, this
4945aaf9effSMark Brown  * should validate that at least some write works.
4955aaf9effSMark Brown  */
4965aaf9effSMark Brown void test_ctl_write_default(struct ctl_data *ctl)
4975aaf9effSMark Brown {
4985aaf9effSMark Brown 	int err;
4995aaf9effSMark Brown 
5005aaf9effSMark Brown 	/* If the control is turned off let's be polite */
5015aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
5025aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
5035aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
5045aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
5055aaf9effSMark Brown 		return;
5065aaf9effSMark Brown 	}
5075aaf9effSMark Brown 
5085aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
5095aaf9effSMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
5105aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
5115aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
5125aaf9effSMark Brown 		return;
5135aaf9effSMark Brown 	}
5145aaf9effSMark Brown 
5155aaf9effSMark Brown 	/* No idea what the default was for unreadable controls */
5165aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info)) {
5175aaf9effSMark Brown 		ksft_print_msg("%s couldn't read default\n", ctl->name);
5185aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
5195aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
5205aaf9effSMark Brown 		return;
5215aaf9effSMark Brown 	}
5225aaf9effSMark Brown 
5235aaf9effSMark Brown 	err = write_and_verify(ctl, ctl->def_val, NULL);
5245aaf9effSMark Brown 
5255aaf9effSMark Brown 	ksft_test_result(err >= 0, "write_default.%d.%d\n",
5265aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
5275aaf9effSMark Brown }
5285aaf9effSMark Brown 
5295aaf9effSMark Brown bool test_ctl_write_valid_boolean(struct ctl_data *ctl)
5305aaf9effSMark Brown {
5315aaf9effSMark Brown 	int err, i, j;
5325aaf9effSMark Brown 	bool fail = false;
5335aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
5345aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
5355aaf9effSMark Brown 
5365aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
5375aaf9effSMark Brown 
5385aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
5395aaf9effSMark Brown 		for (j = 0; j < 2; j++) {
5405aaf9effSMark Brown 			snd_ctl_elem_value_set_boolean(val, i, j);
5415aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
5425aaf9effSMark Brown 			if (err != 0)
5435aaf9effSMark Brown 				fail = true;
5445aaf9effSMark Brown 		}
5455aaf9effSMark Brown 	}
5465aaf9effSMark Brown 
5475aaf9effSMark Brown 	return !fail;
5485aaf9effSMark Brown }
5495aaf9effSMark Brown 
5505aaf9effSMark Brown bool test_ctl_write_valid_integer(struct ctl_data *ctl)
5515aaf9effSMark Brown {
5525aaf9effSMark Brown 	int err;
5535aaf9effSMark Brown 	int i;
5545aaf9effSMark Brown 	long j, step;
5555aaf9effSMark Brown 	bool fail = false;
5565aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
5575aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
5585aaf9effSMark Brown 
5595aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
5605aaf9effSMark Brown 
5615aaf9effSMark Brown 	step = snd_ctl_elem_info_get_step(ctl->info);
5625aaf9effSMark Brown 	if (!step)
5635aaf9effSMark Brown 		step = 1;
5645aaf9effSMark Brown 
5655aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
5665aaf9effSMark Brown 		for (j = snd_ctl_elem_info_get_min(ctl->info);
5675aaf9effSMark Brown 		     j <= snd_ctl_elem_info_get_max(ctl->info); j += step) {
5685aaf9effSMark Brown 
5695aaf9effSMark Brown 			snd_ctl_elem_value_set_integer(val, i, j);
5705aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
5715aaf9effSMark Brown 			if (err != 0)
5725aaf9effSMark Brown 				fail = true;
5735aaf9effSMark Brown 		}
5745aaf9effSMark Brown 	}
5755aaf9effSMark Brown 
5765aaf9effSMark Brown 
5775aaf9effSMark Brown 	return !fail;
5785aaf9effSMark Brown }
5795aaf9effSMark Brown 
5805aaf9effSMark Brown bool test_ctl_write_valid_integer64(struct ctl_data *ctl)
5815aaf9effSMark Brown {
5825aaf9effSMark Brown 	int err, i;
5835aaf9effSMark Brown 	long long j, step;
5845aaf9effSMark Brown 	bool fail = false;
5855aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
5865aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
5875aaf9effSMark Brown 
5885aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
5895aaf9effSMark Brown 
5905aaf9effSMark Brown 	step = snd_ctl_elem_info_get_step64(ctl->info);
5915aaf9effSMark Brown 	if (!step)
5925aaf9effSMark Brown 		step = 1;
5935aaf9effSMark Brown 
5945aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
5955aaf9effSMark Brown 		for (j = snd_ctl_elem_info_get_min64(ctl->info);
5965aaf9effSMark Brown 		     j <= snd_ctl_elem_info_get_max64(ctl->info); j += step) {
5975aaf9effSMark Brown 
5985aaf9effSMark Brown 			snd_ctl_elem_value_set_integer64(val, i, j);
5995aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
6005aaf9effSMark Brown 			if (err != 0)
6015aaf9effSMark Brown 				fail = true;
6025aaf9effSMark Brown 		}
6035aaf9effSMark Brown 	}
6045aaf9effSMark Brown 
6055aaf9effSMark Brown 	return !fail;
6065aaf9effSMark Brown }
6075aaf9effSMark Brown 
6085aaf9effSMark Brown bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
6095aaf9effSMark Brown {
6105aaf9effSMark Brown 	int err, i, j;
6115aaf9effSMark Brown 	bool fail = false;
6125aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
6135aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
6145aaf9effSMark Brown 
6155aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
6165aaf9effSMark Brown 
6175aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
6185aaf9effSMark Brown 		for (j = 0; j < snd_ctl_elem_info_get_items(ctl->info); j++) {
6195aaf9effSMark Brown 			snd_ctl_elem_value_set_enumerated(val, i, j);
6205aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
6215aaf9effSMark Brown 			if (err != 0)
6225aaf9effSMark Brown 				fail = true;
6235aaf9effSMark Brown 		}
6245aaf9effSMark Brown 	}
6255aaf9effSMark Brown 
6265aaf9effSMark Brown 	return !fail;
6275aaf9effSMark Brown }
6285aaf9effSMark Brown 
6295aaf9effSMark Brown void test_ctl_write_valid(struct ctl_data *ctl)
6305aaf9effSMark Brown {
6315aaf9effSMark Brown 	bool pass;
6325aaf9effSMark Brown 	int err;
6335aaf9effSMark Brown 
6345aaf9effSMark Brown 	/* If the control is turned off let's be polite */
6355aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
6365aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
6375aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
6385aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
6395aaf9effSMark Brown 		return;
6405aaf9effSMark Brown 	}
6415aaf9effSMark Brown 
6425aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
6435aaf9effSMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
6445aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
6455aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
6465aaf9effSMark Brown 		return;
6475aaf9effSMark Brown 	}
6485aaf9effSMark Brown 
6495aaf9effSMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
6505aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
6515aaf9effSMark Brown 		pass = test_ctl_write_valid_boolean(ctl);
6525aaf9effSMark Brown 		break;
6535aaf9effSMark Brown 
6545aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
6555aaf9effSMark Brown 		pass = test_ctl_write_valid_integer(ctl);
6565aaf9effSMark Brown 		break;
6575aaf9effSMark Brown 
6585aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
6595aaf9effSMark Brown 		pass = test_ctl_write_valid_integer64(ctl);
6605aaf9effSMark Brown 		break;
6615aaf9effSMark Brown 
6625aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
6635aaf9effSMark Brown 		pass = test_ctl_write_valid_enumerated(ctl);
6645aaf9effSMark Brown 		break;
6655aaf9effSMark Brown 
6665aaf9effSMark Brown 	default:
6675aaf9effSMark Brown 		/* No tests for this yet */
6685aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
6695aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
6705aaf9effSMark Brown 		return;
6715aaf9effSMark Brown 	}
6725aaf9effSMark Brown 
6735aaf9effSMark Brown 	/* Restore the default value to minimise disruption */
6745aaf9effSMark Brown 	err = write_and_verify(ctl, ctl->def_val, NULL);
6755aaf9effSMark Brown 	if (err < 0)
6765aaf9effSMark Brown 		pass = false;
6775aaf9effSMark Brown 
6785aaf9effSMark Brown 	ksft_test_result(pass, "write_valid.%d.%d\n",
6795aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
6805aaf9effSMark Brown }
6815aaf9effSMark Brown 
6825aaf9effSMark Brown int main(void)
6835aaf9effSMark Brown {
6845aaf9effSMark Brown 	struct ctl_data *ctl;
6855aaf9effSMark Brown 
6865aaf9effSMark Brown 	ksft_print_header();
6875aaf9effSMark Brown 
6885aaf9effSMark Brown 	find_controls();
6895aaf9effSMark Brown 
6905aaf9effSMark Brown 	ksft_set_plan(num_controls * TESTS_PER_CONTROL);
6915aaf9effSMark Brown 
6925aaf9effSMark Brown 	for (ctl = ctl_list; ctl != NULL; ctl = ctl->next) {
6935aaf9effSMark Brown 		/*
6945aaf9effSMark Brown 		 * Must test get_value() before we write anything, the
6955aaf9effSMark Brown 		 * test stores the default value for later cleanup.
6965aaf9effSMark Brown 		 */
6975aaf9effSMark Brown 		test_ctl_get_value(ctl);
6985aaf9effSMark Brown 		test_ctl_write_default(ctl);
6995aaf9effSMark Brown 		test_ctl_write_valid(ctl);
7005aaf9effSMark Brown 	}
7015aaf9effSMark Brown 
7025aaf9effSMark Brown 	ksft_exit_pass();
7035aaf9effSMark Brown 
7045aaf9effSMark Brown 	return 0;
7055aaf9effSMark Brown }
706