xref: /openbmc/linux/tools/testing/selftests/alsa/mixer-test.c (revision 3f48b137d88e710b67b2bcc01aa3d77b4db610c4)
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 
196*3f48b137SMark Brown bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val,
197*3f48b137SMark Brown 			   int index)
198*3f48b137SMark Brown {
199*3f48b137SMark Brown 	long int_val;
200*3f48b137SMark Brown 	long long int64_val;
201*3f48b137SMark Brown 
202*3f48b137SMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
203*3f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_NONE:
204*3f48b137SMark Brown 		ksft_print_msg("%s.%d Invalid control type NONE\n",
205*3f48b137SMark Brown 			       ctl->name, index);
206*3f48b137SMark Brown 		return false;
207*3f48b137SMark Brown 
208*3f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
209*3f48b137SMark Brown 		int_val = snd_ctl_elem_value_get_boolean(val, index);
210*3f48b137SMark Brown 		switch (int_val) {
211*3f48b137SMark Brown 		case 0:
212*3f48b137SMark Brown 		case 1:
213*3f48b137SMark Brown 			break;
214*3f48b137SMark Brown 		default:
215*3f48b137SMark Brown 			ksft_print_msg("%s.%d Invalid boolean value %ld\n",
216*3f48b137SMark Brown 				       ctl->name, index, int_val);
217*3f48b137SMark Brown 			return false;
218*3f48b137SMark Brown 		}
219*3f48b137SMark Brown 		break;
220*3f48b137SMark Brown 
221*3f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
222*3f48b137SMark Brown 		int_val = snd_ctl_elem_value_get_integer(val, index);
223*3f48b137SMark Brown 
224*3f48b137SMark Brown 		if (int_val < snd_ctl_elem_info_get_min(ctl->info)) {
225*3f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld less than minimum %ld\n",
226*3f48b137SMark Brown 				       ctl->name, index, int_val,
227*3f48b137SMark Brown 				       snd_ctl_elem_info_get_min(ctl->info));
228*3f48b137SMark Brown 			return false;
229*3f48b137SMark Brown 		}
230*3f48b137SMark Brown 
231*3f48b137SMark Brown 		if (int_val > snd_ctl_elem_info_get_max(ctl->info)) {
232*3f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld more than maximum %ld\n",
233*3f48b137SMark Brown 				       ctl->name, index, int_val,
234*3f48b137SMark Brown 				       snd_ctl_elem_info_get_max(ctl->info));
235*3f48b137SMark Brown 			return false;
236*3f48b137SMark Brown 		}
237*3f48b137SMark Brown 
238*3f48b137SMark Brown 		/* Only check step size if there is one and we're in bounds */
239*3f48b137SMark Brown 		if (snd_ctl_elem_info_get_step(ctl->info) &&
240*3f48b137SMark Brown 		    (int_val - snd_ctl_elem_info_get_min(ctl->info) %
241*3f48b137SMark Brown 		     snd_ctl_elem_info_get_step(ctl->info))) {
242*3f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld invalid for step %ld minimum %ld\n",
243*3f48b137SMark Brown 				       ctl->name, index, int_val,
244*3f48b137SMark Brown 				       snd_ctl_elem_info_get_step(ctl->info),
245*3f48b137SMark Brown 				       snd_ctl_elem_info_get_min(ctl->info));
246*3f48b137SMark Brown 			return false;
247*3f48b137SMark Brown 		}
248*3f48b137SMark Brown 		break;
249*3f48b137SMark Brown 
250*3f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
251*3f48b137SMark Brown 		int64_val = snd_ctl_elem_value_get_integer64(val, index);
252*3f48b137SMark Brown 
253*3f48b137SMark Brown 		if (int64_val < snd_ctl_elem_info_get_min64(ctl->info)) {
254*3f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld less than minimum %lld\n",
255*3f48b137SMark Brown 				       ctl->name, index, int64_val,
256*3f48b137SMark Brown 				       snd_ctl_elem_info_get_min64(ctl->info));
257*3f48b137SMark Brown 			return false;
258*3f48b137SMark Brown 		}
259*3f48b137SMark Brown 
260*3f48b137SMark Brown 		if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) {
261*3f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld more than maximum %lld\n",
262*3f48b137SMark Brown 				       ctl->name, index, int64_val,
263*3f48b137SMark Brown 				       snd_ctl_elem_info_get_max(ctl->info));
264*3f48b137SMark Brown 			return false;
265*3f48b137SMark Brown 		}
266*3f48b137SMark Brown 
267*3f48b137SMark Brown 		/* Only check step size if there is one and we're in bounds */
268*3f48b137SMark Brown 		if (snd_ctl_elem_info_get_step64(ctl->info) &&
269*3f48b137SMark Brown 		    (int64_val - snd_ctl_elem_info_get_min64(ctl->info)) %
270*3f48b137SMark Brown 		    snd_ctl_elem_info_get_step64(ctl->info)) {
271*3f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld invalid for step %lld minimum %lld\n",
272*3f48b137SMark Brown 				       ctl->name, index, int64_val,
273*3f48b137SMark Brown 				       snd_ctl_elem_info_get_step64(ctl->info),
274*3f48b137SMark Brown 				       snd_ctl_elem_info_get_min64(ctl->info));
275*3f48b137SMark Brown 			return false;
276*3f48b137SMark Brown 		}
277*3f48b137SMark Brown 		break;
278*3f48b137SMark Brown 
279*3f48b137SMark Brown 	default:
280*3f48b137SMark Brown 		/* No tests for other types */
281*3f48b137SMark Brown 		break;
282*3f48b137SMark Brown 	}
283*3f48b137SMark Brown 
284*3f48b137SMark Brown 	return true;
285*3f48b137SMark Brown }
286*3f48b137SMark Brown 
287*3f48b137SMark Brown /*
288*3f48b137SMark Brown  * Check that the provided value meets the constraints for the
289*3f48b137SMark Brown  * provided control.
290*3f48b137SMark Brown  */
291*3f48b137SMark Brown bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val)
292*3f48b137SMark Brown {
293*3f48b137SMark Brown 	int i;
294*3f48b137SMark Brown 	bool valid = true;
295*3f48b137SMark Brown 
296*3f48b137SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++)
297*3f48b137SMark Brown 		if (!ctl_value_index_valid(ctl, val, i))
298*3f48b137SMark Brown 			valid = false;
299*3f48b137SMark Brown 
300*3f48b137SMark Brown 	return valid;
301*3f48b137SMark Brown }
302*3f48b137SMark Brown 
3035aaf9effSMark Brown /*
3045aaf9effSMark Brown  * Check that we can read the default value and it is valid. Write
3055aaf9effSMark Brown  * tests use the read value to restore the default.
3065aaf9effSMark Brown  */
3075aaf9effSMark Brown void test_ctl_get_value(struct ctl_data *ctl)
3085aaf9effSMark Brown {
3095aaf9effSMark Brown 	int err;
3105aaf9effSMark Brown 
3115aaf9effSMark Brown 	/* If the control is turned off let's be polite */
3125aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
3135aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
3145aaf9effSMark Brown 		ksft_test_result_skip("get_value.%d.%d\n",
3155aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
3165aaf9effSMark Brown 		return;
3175aaf9effSMark Brown 	}
3185aaf9effSMark Brown 
3195aaf9effSMark Brown 	/* Can't test reading on an unreadable control */
3205aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info)) {
3215aaf9effSMark Brown 		ksft_print_msg("%s is not readable\n", ctl->name);
3225aaf9effSMark Brown 		ksft_test_result_skip("get_value.%d.%d\n",
3235aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
3245aaf9effSMark Brown 		return;
3255aaf9effSMark Brown 	}
3265aaf9effSMark Brown 
3275aaf9effSMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val);
3285aaf9effSMark Brown 	if (err < 0) {
3295aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
3305aaf9effSMark Brown 			       snd_strerror(err));
3315aaf9effSMark Brown 		goto out;
3325aaf9effSMark Brown 	}
3335aaf9effSMark Brown 
334*3f48b137SMark Brown 	if (!ctl_value_valid(ctl, ctl->def_val))
335*3f48b137SMark Brown 		err = -EINVAL;
3365aaf9effSMark Brown 
3375aaf9effSMark Brown out:
3385aaf9effSMark Brown 	ksft_test_result(err >= 0, "get_value.%d.%d\n",
3395aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
3405aaf9effSMark Brown }
3415aaf9effSMark Brown 
3425aaf9effSMark Brown bool show_mismatch(struct ctl_data *ctl, int index,
3435aaf9effSMark Brown 		   snd_ctl_elem_value_t *read_val,
3445aaf9effSMark Brown 		   snd_ctl_elem_value_t *expected_val)
3455aaf9effSMark Brown {
3465aaf9effSMark Brown 	long long expected_int, read_int;
3475aaf9effSMark Brown 
3485aaf9effSMark Brown 	/*
3495aaf9effSMark Brown 	 * We factor out the code to compare values representable as
3505aaf9effSMark Brown 	 * integers, ensure that check doesn't log otherwise.
3515aaf9effSMark Brown 	 */
3525aaf9effSMark Brown 	expected_int = 0;
3535aaf9effSMark Brown 	read_int = 0;
3545aaf9effSMark Brown 
3555aaf9effSMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
3565aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
3575aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_boolean(expected_val,
3585aaf9effSMark Brown 							      index);
3595aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_boolean(read_val, index);
3605aaf9effSMark Brown 		break;
3615aaf9effSMark Brown 
3625aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
3635aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_integer(expected_val,
3645aaf9effSMark Brown 							      index);
3655aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_integer(read_val, index);
3665aaf9effSMark Brown 		break;
3675aaf9effSMark Brown 
3685aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
3695aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_integer64(expected_val,
3705aaf9effSMark Brown 								index);
3715aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_integer64(read_val,
3725aaf9effSMark Brown 							    index);
3735aaf9effSMark Brown 		break;
3745aaf9effSMark Brown 
3755aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
3765aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_enumerated(expected_val,
3775aaf9effSMark Brown 								 index);
3785aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_enumerated(read_val,
3795aaf9effSMark Brown 							     index);
3805aaf9effSMark Brown 		break;
3815aaf9effSMark Brown 
3825aaf9effSMark Brown 	default:
3835aaf9effSMark Brown 		break;
3845aaf9effSMark Brown 	}
3855aaf9effSMark Brown 
3865aaf9effSMark Brown 	if (expected_int != read_int) {
3877cc994f2STakashi Sakamoto 		/*
3887cc994f2STakashi Sakamoto 		 * NOTE: The volatile attribute means that the hardware
3897cc994f2STakashi Sakamoto 		 * can voluntarily change the state of control element
3907cc994f2STakashi Sakamoto 		 * independent of any operation by software.
3917cc994f2STakashi Sakamoto 		 */
3927cc994f2STakashi Sakamoto 		bool is_volatile = snd_ctl_elem_info_is_volatile(ctl->info);
3937cc994f2STakashi Sakamoto 		ksft_print_msg("%s.%d expected %lld but read %lld, is_volatile %d\n",
3947cc994f2STakashi Sakamoto 			       ctl->name, index, expected_int, read_int, is_volatile);
3957cc994f2STakashi Sakamoto 		return !is_volatile;
3965aaf9effSMark Brown 	} else {
3975aaf9effSMark Brown 		return false;
3985aaf9effSMark Brown 	}
3995aaf9effSMark Brown }
4005aaf9effSMark Brown 
4015aaf9effSMark Brown /*
4025aaf9effSMark Brown  * Write a value then if possible verify that we get the expected
4035aaf9effSMark Brown  * result.  An optional expected value can be provided if we expect
4045aaf9effSMark Brown  * the write to fail, for verifying that invalid writes don't corrupt
4055aaf9effSMark Brown  * anything.
4065aaf9effSMark Brown  */
4075aaf9effSMark Brown int write_and_verify(struct ctl_data *ctl,
4085aaf9effSMark Brown 		     snd_ctl_elem_value_t *write_val,
4095aaf9effSMark Brown 		     snd_ctl_elem_value_t *expected_val)
4105aaf9effSMark Brown {
4115aaf9effSMark Brown 	int err, i;
4125aaf9effSMark Brown 	bool error_expected, mismatch_shown;
4135aaf9effSMark Brown 	snd_ctl_elem_value_t *read_val, *w_val;
4145aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&read_val);
4155aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&w_val);
4165aaf9effSMark Brown 
4175aaf9effSMark Brown 	/*
4185aaf9effSMark Brown 	 * We need to copy the write value since writing can modify
4195aaf9effSMark Brown 	 * the value which causes surprises, and allocate an expected
4205aaf9effSMark Brown 	 * value if we expect to read back what we wrote.
4215aaf9effSMark Brown 	 */
4225aaf9effSMark Brown 	snd_ctl_elem_value_copy(w_val, write_val);
4235aaf9effSMark Brown 	if (expected_val) {
4245aaf9effSMark Brown 		error_expected = true;
4255aaf9effSMark Brown 	} else {
4265aaf9effSMark Brown 		error_expected = false;
4275aaf9effSMark Brown 		snd_ctl_elem_value_alloca(&expected_val);
4285aaf9effSMark Brown 		snd_ctl_elem_value_copy(expected_val, write_val);
4295aaf9effSMark Brown 	}
4305aaf9effSMark Brown 
4315aaf9effSMark Brown 	/*
4325aaf9effSMark Brown 	 * Do the write, if we have an expected value ignore the error
4335aaf9effSMark Brown 	 * and carry on to validate the expected value.
4345aaf9effSMark Brown 	 */
4355aaf9effSMark Brown 	err = snd_ctl_elem_write(ctl->card->handle, w_val);
4365aaf9effSMark Brown 	if (err < 0 && !error_expected) {
4375aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_write() failed: %s\n",
4385aaf9effSMark Brown 			       snd_strerror(err));
4395aaf9effSMark Brown 		return err;
4405aaf9effSMark Brown 	}
4415aaf9effSMark Brown 
4425aaf9effSMark Brown 	/* Can we do the verification part? */
4435aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info))
4445aaf9effSMark Brown 		return err;
4455aaf9effSMark Brown 
4465aaf9effSMark Brown 	snd_ctl_elem_value_set_id(read_val, ctl->id);
4475aaf9effSMark Brown 
4485aaf9effSMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, read_val);
4495aaf9effSMark Brown 	if (err < 0) {
4505aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
4515aaf9effSMark Brown 			       snd_strerror(err));
4525aaf9effSMark Brown 		return err;
4535aaf9effSMark Brown 	}
4545aaf9effSMark Brown 
4555aaf9effSMark Brown 	/*
4565aaf9effSMark Brown 	 * Use the libray to compare values, if there's a mismatch
4575aaf9effSMark Brown 	 * carry on and try to provide a more useful diagnostic than
4585aaf9effSMark Brown 	 * just "mismatch".
4595aaf9effSMark Brown 	 */
4605aaf9effSMark Brown 	if (!snd_ctl_elem_value_compare(expected_val, read_val))
4615aaf9effSMark Brown 		return 0;
4625aaf9effSMark Brown 
4635aaf9effSMark Brown 	mismatch_shown = false;
4645aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++)
4655aaf9effSMark Brown 		if (show_mismatch(ctl, i, read_val, expected_val))
4665aaf9effSMark Brown 			mismatch_shown = true;
4675aaf9effSMark Brown 
4685aaf9effSMark Brown 	if (!mismatch_shown)
4695aaf9effSMark Brown 		ksft_print_msg("%s read and written values differ\n",
4705aaf9effSMark Brown 			       ctl->name);
4715aaf9effSMark Brown 
4725aaf9effSMark Brown 	return -1;
4735aaf9effSMark Brown }
4745aaf9effSMark Brown 
4755aaf9effSMark Brown /*
4765aaf9effSMark Brown  * Make sure we can write the default value back to the control, this
4775aaf9effSMark Brown  * should validate that at least some write works.
4785aaf9effSMark Brown  */
4795aaf9effSMark Brown void test_ctl_write_default(struct ctl_data *ctl)
4805aaf9effSMark Brown {
4815aaf9effSMark Brown 	int err;
4825aaf9effSMark Brown 
4835aaf9effSMark Brown 	/* If the control is turned off let's be polite */
4845aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
4855aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
4865aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
4875aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
4885aaf9effSMark Brown 		return;
4895aaf9effSMark Brown 	}
4905aaf9effSMark Brown 
4915aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
4925aaf9effSMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
4935aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
4945aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
4955aaf9effSMark Brown 		return;
4965aaf9effSMark Brown 	}
4975aaf9effSMark Brown 
4985aaf9effSMark Brown 	/* No idea what the default was for unreadable controls */
4995aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info)) {
5005aaf9effSMark Brown 		ksft_print_msg("%s couldn't read default\n", ctl->name);
5015aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
5025aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
5035aaf9effSMark Brown 		return;
5045aaf9effSMark Brown 	}
5055aaf9effSMark Brown 
5065aaf9effSMark Brown 	err = write_and_verify(ctl, ctl->def_val, NULL);
5075aaf9effSMark Brown 
5085aaf9effSMark Brown 	ksft_test_result(err >= 0, "write_default.%d.%d\n",
5095aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
5105aaf9effSMark Brown }
5115aaf9effSMark Brown 
5125aaf9effSMark Brown bool test_ctl_write_valid_boolean(struct ctl_data *ctl)
5135aaf9effSMark Brown {
5145aaf9effSMark Brown 	int err, i, j;
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 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
5225aaf9effSMark Brown 		for (j = 0; j < 2; j++) {
5235aaf9effSMark Brown 			snd_ctl_elem_value_set_boolean(val, i, j);
5245aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
5255aaf9effSMark Brown 			if (err != 0)
5265aaf9effSMark Brown 				fail = true;
5275aaf9effSMark Brown 		}
5285aaf9effSMark Brown 	}
5295aaf9effSMark Brown 
5305aaf9effSMark Brown 	return !fail;
5315aaf9effSMark Brown }
5325aaf9effSMark Brown 
5335aaf9effSMark Brown bool test_ctl_write_valid_integer(struct ctl_data *ctl)
5345aaf9effSMark Brown {
5355aaf9effSMark Brown 	int err;
5365aaf9effSMark Brown 	int i;
5375aaf9effSMark Brown 	long j, step;
5385aaf9effSMark Brown 	bool fail = false;
5395aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
5405aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
5415aaf9effSMark Brown 
5425aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
5435aaf9effSMark Brown 
5445aaf9effSMark Brown 	step = snd_ctl_elem_info_get_step(ctl->info);
5455aaf9effSMark Brown 	if (!step)
5465aaf9effSMark Brown 		step = 1;
5475aaf9effSMark Brown 
5485aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
5495aaf9effSMark Brown 		for (j = snd_ctl_elem_info_get_min(ctl->info);
5505aaf9effSMark Brown 		     j <= snd_ctl_elem_info_get_max(ctl->info); j += step) {
5515aaf9effSMark Brown 
5525aaf9effSMark Brown 			snd_ctl_elem_value_set_integer(val, i, j);
5535aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
5545aaf9effSMark Brown 			if (err != 0)
5555aaf9effSMark Brown 				fail = true;
5565aaf9effSMark Brown 		}
5575aaf9effSMark Brown 	}
5585aaf9effSMark Brown 
5595aaf9effSMark Brown 
5605aaf9effSMark Brown 	return !fail;
5615aaf9effSMark Brown }
5625aaf9effSMark Brown 
5635aaf9effSMark Brown bool test_ctl_write_valid_integer64(struct ctl_data *ctl)
5645aaf9effSMark Brown {
5655aaf9effSMark Brown 	int err, i;
5665aaf9effSMark Brown 	long long j, step;
5675aaf9effSMark Brown 	bool fail = false;
5685aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
5695aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
5705aaf9effSMark Brown 
5715aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
5725aaf9effSMark Brown 
5735aaf9effSMark Brown 	step = snd_ctl_elem_info_get_step64(ctl->info);
5745aaf9effSMark Brown 	if (!step)
5755aaf9effSMark Brown 		step = 1;
5765aaf9effSMark Brown 
5775aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
5785aaf9effSMark Brown 		for (j = snd_ctl_elem_info_get_min64(ctl->info);
5795aaf9effSMark Brown 		     j <= snd_ctl_elem_info_get_max64(ctl->info); j += step) {
5805aaf9effSMark Brown 
5815aaf9effSMark Brown 			snd_ctl_elem_value_set_integer64(val, i, j);
5825aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
5835aaf9effSMark Brown 			if (err != 0)
5845aaf9effSMark Brown 				fail = true;
5855aaf9effSMark Brown 		}
5865aaf9effSMark Brown 	}
5875aaf9effSMark Brown 
5885aaf9effSMark Brown 	return !fail;
5895aaf9effSMark Brown }
5905aaf9effSMark Brown 
5915aaf9effSMark Brown bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
5925aaf9effSMark Brown {
5935aaf9effSMark Brown 	int err, i, j;
5945aaf9effSMark Brown 	bool fail = false;
5955aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
5965aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
5975aaf9effSMark Brown 
5985aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
5995aaf9effSMark Brown 
6005aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
6015aaf9effSMark Brown 		for (j = 0; j < snd_ctl_elem_info_get_items(ctl->info); j++) {
6025aaf9effSMark Brown 			snd_ctl_elem_value_set_enumerated(val, i, j);
6035aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
6045aaf9effSMark Brown 			if (err != 0)
6055aaf9effSMark Brown 				fail = true;
6065aaf9effSMark Brown 		}
6075aaf9effSMark Brown 	}
6085aaf9effSMark Brown 
6095aaf9effSMark Brown 	return !fail;
6105aaf9effSMark Brown }
6115aaf9effSMark Brown 
6125aaf9effSMark Brown void test_ctl_write_valid(struct ctl_data *ctl)
6135aaf9effSMark Brown {
6145aaf9effSMark Brown 	bool pass;
6155aaf9effSMark Brown 	int err;
6165aaf9effSMark Brown 
6175aaf9effSMark Brown 	/* If the control is turned off let's be polite */
6185aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
6195aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
6205aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
6215aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
6225aaf9effSMark Brown 		return;
6235aaf9effSMark Brown 	}
6245aaf9effSMark Brown 
6255aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
6265aaf9effSMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
6275aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
6285aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
6295aaf9effSMark Brown 		return;
6305aaf9effSMark Brown 	}
6315aaf9effSMark Brown 
6325aaf9effSMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
6335aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
6345aaf9effSMark Brown 		pass = test_ctl_write_valid_boolean(ctl);
6355aaf9effSMark Brown 		break;
6365aaf9effSMark Brown 
6375aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
6385aaf9effSMark Brown 		pass = test_ctl_write_valid_integer(ctl);
6395aaf9effSMark Brown 		break;
6405aaf9effSMark Brown 
6415aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
6425aaf9effSMark Brown 		pass = test_ctl_write_valid_integer64(ctl);
6435aaf9effSMark Brown 		break;
6445aaf9effSMark Brown 
6455aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
6465aaf9effSMark Brown 		pass = test_ctl_write_valid_enumerated(ctl);
6475aaf9effSMark Brown 		break;
6485aaf9effSMark Brown 
6495aaf9effSMark Brown 	default:
6505aaf9effSMark Brown 		/* No tests for this yet */
6515aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
6525aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
6535aaf9effSMark Brown 		return;
6545aaf9effSMark Brown 	}
6555aaf9effSMark Brown 
6565aaf9effSMark Brown 	/* Restore the default value to minimise disruption */
6575aaf9effSMark Brown 	err = write_and_verify(ctl, ctl->def_val, NULL);
6585aaf9effSMark Brown 	if (err < 0)
6595aaf9effSMark Brown 		pass = false;
6605aaf9effSMark Brown 
6615aaf9effSMark Brown 	ksft_test_result(pass, "write_valid.%d.%d\n",
6625aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
6635aaf9effSMark Brown }
6645aaf9effSMark Brown 
6655aaf9effSMark Brown int main(void)
6665aaf9effSMark Brown {
6675aaf9effSMark Brown 	struct ctl_data *ctl;
6685aaf9effSMark Brown 
6695aaf9effSMark Brown 	ksft_print_header();
6705aaf9effSMark Brown 
6715aaf9effSMark Brown 	find_controls();
6725aaf9effSMark Brown 
6735aaf9effSMark Brown 	ksft_set_plan(num_controls * TESTS_PER_CONTROL);
6745aaf9effSMark Brown 
6755aaf9effSMark Brown 	for (ctl = ctl_list; ctl != NULL; ctl = ctl->next) {
6765aaf9effSMark Brown 		/*
6775aaf9effSMark Brown 		 * Must test get_value() before we write anything, the
6785aaf9effSMark Brown 		 * test stores the default value for later cleanup.
6795aaf9effSMark Brown 		 */
6805aaf9effSMark Brown 		test_ctl_get_value(ctl);
6815aaf9effSMark Brown 		test_ctl_write_default(ctl);
6825aaf9effSMark Brown 		test_ctl_write_valid(ctl);
6835aaf9effSMark Brown 	}
6845aaf9effSMark Brown 
6855aaf9effSMark Brown 	ksft_exit_pass();
6865aaf9effSMark Brown 
6875aaf9effSMark Brown 	return 0;
6885aaf9effSMark Brown }
689