xref: /openbmc/linux/tools/testing/selftests/alsa/mixer-test.c (revision 7cc994f27e84cc94ce612d201c78763f93eab2c4)
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 
495aaf9effSMark Brown int num_cards = 0;
505aaf9effSMark Brown int num_controls = 0;
515aaf9effSMark Brown struct card_data *card_list = NULL;
525aaf9effSMark Brown struct ctl_data *ctl_list = NULL;
535aaf9effSMark Brown 
545aaf9effSMark Brown void find_controls(void)
555aaf9effSMark Brown {
565aaf9effSMark Brown 	char name[32];
575aaf9effSMark Brown 	int card, ctl, err;
585aaf9effSMark Brown 	struct card_data *card_data;
595aaf9effSMark Brown 	struct ctl_data *ctl_data;
605aaf9effSMark Brown 
615aaf9effSMark Brown 	card = -1;
625aaf9effSMark Brown 	if (snd_card_next(&card) < 0 || card < 0)
635aaf9effSMark Brown 		return;
645aaf9effSMark Brown 
655aaf9effSMark Brown 	while (card >= 0) {
665aaf9effSMark Brown 		sprintf(name, "hw:%d", card);
675aaf9effSMark Brown 
685aaf9effSMark Brown 		card_data = malloc(sizeof(*card_data));
695aaf9effSMark Brown 		if (!card_data)
705aaf9effSMark Brown 			ksft_exit_fail_msg("Out of memory\n");
715aaf9effSMark Brown 
725aaf9effSMark Brown 		err = snd_ctl_open(&card_data->handle, name, 0);
735aaf9effSMark Brown 		if (err < 0) {
745aaf9effSMark Brown 			ksft_print_msg("Failed to get hctl for card %d: %s\n",
755aaf9effSMark Brown 				       card, snd_strerror(err));
765aaf9effSMark Brown 			goto next_card;
775aaf9effSMark Brown 		}
785aaf9effSMark Brown 
795aaf9effSMark Brown 		/* Count controls */
805aaf9effSMark Brown 		snd_ctl_elem_list_malloc(&card_data->ctls);
815aaf9effSMark Brown 		snd_ctl_elem_list(card_data->handle, card_data->ctls);
825aaf9effSMark Brown 		card_data->num_ctls = snd_ctl_elem_list_get_count(card_data->ctls);
835aaf9effSMark Brown 
845aaf9effSMark Brown 		/* Enumerate control information */
855aaf9effSMark Brown 		snd_ctl_elem_list_alloc_space(card_data->ctls, card_data->num_ctls);
865aaf9effSMark Brown 		snd_ctl_elem_list(card_data->handle, card_data->ctls);
875aaf9effSMark Brown 
885aaf9effSMark Brown 		card_data->card = num_cards++;
895aaf9effSMark Brown 		card_data->next = card_list;
905aaf9effSMark Brown 		card_list = card_data;
915aaf9effSMark Brown 
925aaf9effSMark Brown 		num_controls += card_data->num_ctls;
935aaf9effSMark Brown 
945aaf9effSMark Brown 		for (ctl = 0; ctl < card_data->num_ctls; ctl++) {
955aaf9effSMark Brown 			ctl_data = malloc(sizeof(*ctl_data));
965aaf9effSMark Brown 			if (!ctl_data)
975aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
985aaf9effSMark Brown 
995aaf9effSMark Brown 			ctl_data->card = card_data;
1005aaf9effSMark Brown 			ctl_data->elem = ctl;
1015aaf9effSMark Brown 			ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls,
1025aaf9effSMark Brown 								    ctl);
1035aaf9effSMark Brown 
1045aaf9effSMark Brown 			err = snd_ctl_elem_id_malloc(&ctl_data->id);
1055aaf9effSMark Brown 			if (err < 0)
1065aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1075aaf9effSMark Brown 
1085aaf9effSMark Brown 			err = snd_ctl_elem_info_malloc(&ctl_data->info);
1095aaf9effSMark Brown 			if (err < 0)
1105aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1115aaf9effSMark Brown 
1125aaf9effSMark Brown 			err = snd_ctl_elem_value_malloc(&ctl_data->def_val);
1135aaf9effSMark Brown 			if (err < 0)
1145aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1155aaf9effSMark Brown 
1165aaf9effSMark Brown 			snd_ctl_elem_list_get_id(card_data->ctls, ctl,
1175aaf9effSMark Brown 						 ctl_data->id);
1185aaf9effSMark Brown 			snd_ctl_elem_info_set_id(ctl_data->info, ctl_data->id);
1195aaf9effSMark Brown 			err = snd_ctl_elem_info(card_data->handle,
1205aaf9effSMark Brown 						ctl_data->info);
1215aaf9effSMark Brown 			if (err < 0) {
1225aaf9effSMark Brown 				ksft_print_msg("%s getting info for %d\n",
1235aaf9effSMark Brown 					       snd_strerror(err),
1245aaf9effSMark Brown 					       ctl_data->name);
1255aaf9effSMark Brown 			}
1265aaf9effSMark Brown 
1275aaf9effSMark Brown 			snd_ctl_elem_value_set_id(ctl_data->def_val,
1285aaf9effSMark Brown 						  ctl_data->id);
1295aaf9effSMark Brown 
1305aaf9effSMark Brown 			ctl_data->next = ctl_list;
1315aaf9effSMark Brown 			ctl_list = ctl_data;
1325aaf9effSMark Brown 		}
1335aaf9effSMark Brown 
1345aaf9effSMark Brown 	next_card:
1355aaf9effSMark Brown 		if (snd_card_next(&card) < 0) {
1365aaf9effSMark Brown 			ksft_print_msg("snd_card_next");
1375aaf9effSMark Brown 			break;
1385aaf9effSMark Brown 		}
1395aaf9effSMark Brown 	}
1405aaf9effSMark Brown }
1415aaf9effSMark Brown 
1425aaf9effSMark Brown /*
1435aaf9effSMark Brown  * Check that we can read the default value and it is valid. Write
1445aaf9effSMark Brown  * tests use the read value to restore the default.
1455aaf9effSMark Brown  */
1465aaf9effSMark Brown void test_ctl_get_value(struct ctl_data *ctl)
1475aaf9effSMark Brown {
1485aaf9effSMark Brown 	int err;
1495aaf9effSMark Brown 	long int_val;
1505aaf9effSMark Brown 	long long int64_val;
1515aaf9effSMark Brown 
1525aaf9effSMark Brown 	/* If the control is turned off let's be polite */
1535aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
1545aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
1555aaf9effSMark Brown 		ksft_test_result_skip("get_value.%d.%d\n",
1565aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
1575aaf9effSMark Brown 		return;
1585aaf9effSMark Brown 	}
1595aaf9effSMark Brown 
1605aaf9effSMark Brown 	/* Can't test reading on an unreadable control */
1615aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info)) {
1625aaf9effSMark Brown 		ksft_print_msg("%s is not readable\n", ctl->name);
1635aaf9effSMark Brown 		ksft_test_result_skip("get_value.%d.%d\n",
1645aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
1655aaf9effSMark Brown 		return;
1665aaf9effSMark Brown 	}
1675aaf9effSMark Brown 
1685aaf9effSMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val);
1695aaf9effSMark Brown 	if (err < 0) {
1705aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
1715aaf9effSMark Brown 			       snd_strerror(err));
1725aaf9effSMark Brown 		goto out;
1735aaf9effSMark Brown 	}
1745aaf9effSMark Brown 
1755aaf9effSMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
1765aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_NONE:
1775aaf9effSMark Brown 		ksft_print_msg("%s Invalid control type NONE\n", ctl->name);
1785aaf9effSMark Brown 		err = -1;
1795aaf9effSMark Brown 		break;
1805aaf9effSMark Brown 
1815aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
1825aaf9effSMark Brown 		int_val = snd_ctl_elem_value_get_boolean(ctl->def_val, 0);
1835aaf9effSMark Brown 		switch (int_val) {
1845aaf9effSMark Brown 		case 0:
1855aaf9effSMark Brown 		case 1:
1865aaf9effSMark Brown 			break;
1875aaf9effSMark Brown 		default:
1885aaf9effSMark Brown 			ksft_print_msg("%s Invalid boolean value %ld\n",
1895aaf9effSMark Brown 				       ctl->name, int_val);
1905aaf9effSMark Brown 			err = -1;
1915aaf9effSMark Brown 			break;
1925aaf9effSMark Brown 		}
1935aaf9effSMark Brown 		break;
1945aaf9effSMark Brown 
1955aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
1965aaf9effSMark Brown 		int_val = snd_ctl_elem_value_get_integer(ctl->def_val, 0);
1975aaf9effSMark Brown 
1985aaf9effSMark Brown 		if (int_val < snd_ctl_elem_info_get_min(ctl->info)) {
1995aaf9effSMark Brown 			ksft_print_msg("%s value %ld less than minimum %ld\n",
2005aaf9effSMark Brown 				       ctl->name, int_val,
2015aaf9effSMark Brown 				       snd_ctl_elem_info_get_min(ctl->info));
2025aaf9effSMark Brown 			err = -1;
2035aaf9effSMark Brown 		}
2045aaf9effSMark Brown 
2055aaf9effSMark Brown 		if (int_val > snd_ctl_elem_info_get_max(ctl->info)) {
2065aaf9effSMark Brown 			ksft_print_msg("%s value %ld more than maximum %ld\n",
2075aaf9effSMark Brown 				       ctl->name, int_val,
2085aaf9effSMark Brown 				       snd_ctl_elem_info_get_max(ctl->info));
2095aaf9effSMark Brown 			err = -1;
2105aaf9effSMark Brown 		}
2115aaf9effSMark Brown 
2125aaf9effSMark Brown 		/* Only check step size if there is one and we're in bounds */
2135aaf9effSMark Brown 		if (err >= 0 && snd_ctl_elem_info_get_step(ctl->info) &&
2145aaf9effSMark Brown 		    (int_val - snd_ctl_elem_info_get_min(ctl->info) %
2155aaf9effSMark Brown 		     snd_ctl_elem_info_get_step(ctl->info))) {
2165aaf9effSMark Brown 			ksft_print_msg("%s value %ld invalid for step %ld minimum %ld\n",
2175aaf9effSMark Brown 				       ctl->name, int_val,
2185aaf9effSMark Brown 				       snd_ctl_elem_info_get_step(ctl->info),
2195aaf9effSMark Brown 				       snd_ctl_elem_info_get_min(ctl->info));
2205aaf9effSMark Brown 			err = -1;
2215aaf9effSMark Brown 		}
2225aaf9effSMark Brown 		break;
2235aaf9effSMark Brown 
2245aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
2255aaf9effSMark Brown 		int64_val = snd_ctl_elem_value_get_integer64(ctl->def_val, 0);
2265aaf9effSMark Brown 
2275aaf9effSMark Brown 		if (int64_val < snd_ctl_elem_info_get_min64(ctl->info)) {
2285aaf9effSMark Brown 			ksft_print_msg("%s value %lld less than minimum %lld\n",
2295aaf9effSMark Brown 				       ctl->name, int64_val,
2305aaf9effSMark Brown 				       snd_ctl_elem_info_get_min64(ctl->info));
2315aaf9effSMark Brown 			err = -1;
2325aaf9effSMark Brown 		}
2335aaf9effSMark Brown 
2345aaf9effSMark Brown 		if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) {
2355aaf9effSMark Brown 			ksft_print_msg("%s value %lld more than maximum %lld\n",
2365aaf9effSMark Brown 				       ctl->name, int64_val,
2375aaf9effSMark Brown 				       snd_ctl_elem_info_get_max(ctl->info));
2385aaf9effSMark Brown 			err = -1;
2395aaf9effSMark Brown 		}
2405aaf9effSMark Brown 
2415aaf9effSMark Brown 		/* Only check step size if there is one and we're in bounds */
2425aaf9effSMark Brown 		if (err >= 0 && snd_ctl_elem_info_get_step64(ctl->info) &&
2435aaf9effSMark Brown 		    (int64_val - snd_ctl_elem_info_get_min64(ctl->info)) %
2445aaf9effSMark Brown 		    snd_ctl_elem_info_get_step64(ctl->info)) {
2455aaf9effSMark Brown 			ksft_print_msg("%s value %lld invalid for step %lld minimum %lld\n",
2465aaf9effSMark Brown 				       ctl->name, int64_val,
2475aaf9effSMark Brown 				       snd_ctl_elem_info_get_step64(ctl->info),
2485aaf9effSMark Brown 				       snd_ctl_elem_info_get_min64(ctl->info));
2495aaf9effSMark Brown 			err = -1;
2505aaf9effSMark Brown 		}
2515aaf9effSMark Brown 		break;
2525aaf9effSMark Brown 
2535aaf9effSMark Brown 	default:
2545aaf9effSMark Brown 		/* No tests for other types */
2555aaf9effSMark Brown 		ksft_test_result_skip("get_value.%d.%d\n",
2565aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
2575aaf9effSMark Brown 		return;
2585aaf9effSMark Brown 	}
2595aaf9effSMark Brown 
2605aaf9effSMark Brown out:
2615aaf9effSMark Brown 	ksft_test_result(err >= 0, "get_value.%d.%d\n",
2625aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
2635aaf9effSMark Brown }
2645aaf9effSMark Brown 
2655aaf9effSMark Brown bool show_mismatch(struct ctl_data *ctl, int index,
2665aaf9effSMark Brown 		   snd_ctl_elem_value_t *read_val,
2675aaf9effSMark Brown 		   snd_ctl_elem_value_t *expected_val)
2685aaf9effSMark Brown {
2695aaf9effSMark Brown 	long long expected_int, read_int;
2705aaf9effSMark Brown 
2715aaf9effSMark Brown 	/*
2725aaf9effSMark Brown 	 * We factor out the code to compare values representable as
2735aaf9effSMark Brown 	 * integers, ensure that check doesn't log otherwise.
2745aaf9effSMark Brown 	 */
2755aaf9effSMark Brown 	expected_int = 0;
2765aaf9effSMark Brown 	read_int = 0;
2775aaf9effSMark Brown 
2785aaf9effSMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
2795aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
2805aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_boolean(expected_val,
2815aaf9effSMark Brown 							      index);
2825aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_boolean(read_val, index);
2835aaf9effSMark Brown 		break;
2845aaf9effSMark Brown 
2855aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
2865aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_integer(expected_val,
2875aaf9effSMark Brown 							      index);
2885aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_integer(read_val, index);
2895aaf9effSMark Brown 		break;
2905aaf9effSMark Brown 
2915aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
2925aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_integer64(expected_val,
2935aaf9effSMark Brown 								index);
2945aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_integer64(read_val,
2955aaf9effSMark Brown 							    index);
2965aaf9effSMark Brown 		break;
2975aaf9effSMark Brown 
2985aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
2995aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_enumerated(expected_val,
3005aaf9effSMark Brown 								 index);
3015aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_enumerated(read_val,
3025aaf9effSMark Brown 							     index);
3035aaf9effSMark Brown 		break;
3045aaf9effSMark Brown 
3055aaf9effSMark Brown 	default:
3065aaf9effSMark Brown 		break;
3075aaf9effSMark Brown 	}
3085aaf9effSMark Brown 
3095aaf9effSMark Brown 	if (expected_int != read_int) {
310*7cc994f2STakashi Sakamoto 		/*
311*7cc994f2STakashi Sakamoto 		 * NOTE: The volatile attribute means that the hardware
312*7cc994f2STakashi Sakamoto 		 * can voluntarily change the state of control element
313*7cc994f2STakashi Sakamoto 		 * independent of any operation by software.
314*7cc994f2STakashi Sakamoto 		 */
315*7cc994f2STakashi Sakamoto 		bool is_volatile = snd_ctl_elem_info_is_volatile(ctl->info);
316*7cc994f2STakashi Sakamoto 		ksft_print_msg("%s.%d expected %lld but read %lld, is_volatile %d\n",
317*7cc994f2STakashi Sakamoto 			       ctl->name, index, expected_int, read_int, is_volatile);
318*7cc994f2STakashi Sakamoto 		return !is_volatile;
3195aaf9effSMark Brown 	} else {
3205aaf9effSMark Brown 		return false;
3215aaf9effSMark Brown 	}
3225aaf9effSMark Brown }
3235aaf9effSMark Brown 
3245aaf9effSMark Brown /*
3255aaf9effSMark Brown  * Write a value then if possible verify that we get the expected
3265aaf9effSMark Brown  * result.  An optional expected value can be provided if we expect
3275aaf9effSMark Brown  * the write to fail, for verifying that invalid writes don't corrupt
3285aaf9effSMark Brown  * anything.
3295aaf9effSMark Brown  */
3305aaf9effSMark Brown int write_and_verify(struct ctl_data *ctl,
3315aaf9effSMark Brown 		     snd_ctl_elem_value_t *write_val,
3325aaf9effSMark Brown 		     snd_ctl_elem_value_t *expected_val)
3335aaf9effSMark Brown {
3345aaf9effSMark Brown 	int err, i;
3355aaf9effSMark Brown 	bool error_expected, mismatch_shown;
3365aaf9effSMark Brown 	snd_ctl_elem_value_t *read_val, *w_val;
3375aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&read_val);
3385aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&w_val);
3395aaf9effSMark Brown 
3405aaf9effSMark Brown 	/*
3415aaf9effSMark Brown 	 * We need to copy the write value since writing can modify
3425aaf9effSMark Brown 	 * the value which causes surprises, and allocate an expected
3435aaf9effSMark Brown 	 * value if we expect to read back what we wrote.
3445aaf9effSMark Brown 	 */
3455aaf9effSMark Brown 	snd_ctl_elem_value_copy(w_val, write_val);
3465aaf9effSMark Brown 	if (expected_val) {
3475aaf9effSMark Brown 		error_expected = true;
3485aaf9effSMark Brown 	} else {
3495aaf9effSMark Brown 		error_expected = false;
3505aaf9effSMark Brown 		snd_ctl_elem_value_alloca(&expected_val);
3515aaf9effSMark Brown 		snd_ctl_elem_value_copy(expected_val, write_val);
3525aaf9effSMark Brown 	}
3535aaf9effSMark Brown 
3545aaf9effSMark Brown 	/*
3555aaf9effSMark Brown 	 * Do the write, if we have an expected value ignore the error
3565aaf9effSMark Brown 	 * and carry on to validate the expected value.
3575aaf9effSMark Brown 	 */
3585aaf9effSMark Brown 	err = snd_ctl_elem_write(ctl->card->handle, w_val);
3595aaf9effSMark Brown 	if (err < 0 && !error_expected) {
3605aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_write() failed: %s\n",
3615aaf9effSMark Brown 			       snd_strerror(err));
3625aaf9effSMark Brown 		return err;
3635aaf9effSMark Brown 	}
3645aaf9effSMark Brown 
3655aaf9effSMark Brown 	/* Can we do the verification part? */
3665aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info))
3675aaf9effSMark Brown 		return err;
3685aaf9effSMark Brown 
3695aaf9effSMark Brown 	snd_ctl_elem_value_set_id(read_val, ctl->id);
3705aaf9effSMark Brown 
3715aaf9effSMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, read_val);
3725aaf9effSMark Brown 	if (err < 0) {
3735aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
3745aaf9effSMark Brown 			       snd_strerror(err));
3755aaf9effSMark Brown 		return err;
3765aaf9effSMark Brown 	}
3775aaf9effSMark Brown 
3785aaf9effSMark Brown 	/*
3795aaf9effSMark Brown 	 * Use the libray to compare values, if there's a mismatch
3805aaf9effSMark Brown 	 * carry on and try to provide a more useful diagnostic than
3815aaf9effSMark Brown 	 * just "mismatch".
3825aaf9effSMark Brown 	 */
3835aaf9effSMark Brown 	if (!snd_ctl_elem_value_compare(expected_val, read_val))
3845aaf9effSMark Brown 		return 0;
3855aaf9effSMark Brown 
3865aaf9effSMark Brown 	mismatch_shown = false;
3875aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++)
3885aaf9effSMark Brown 		if (show_mismatch(ctl, i, read_val, expected_val))
3895aaf9effSMark Brown 			mismatch_shown = true;
3905aaf9effSMark Brown 
3915aaf9effSMark Brown 	if (!mismatch_shown)
3925aaf9effSMark Brown 		ksft_print_msg("%s read and written values differ\n",
3935aaf9effSMark Brown 			       ctl->name);
3945aaf9effSMark Brown 
3955aaf9effSMark Brown 	return -1;
3965aaf9effSMark Brown }
3975aaf9effSMark Brown 
3985aaf9effSMark Brown /*
3995aaf9effSMark Brown  * Make sure we can write the default value back to the control, this
4005aaf9effSMark Brown  * should validate that at least some write works.
4015aaf9effSMark Brown  */
4025aaf9effSMark Brown void test_ctl_write_default(struct ctl_data *ctl)
4035aaf9effSMark Brown {
4045aaf9effSMark Brown 	int err;
4055aaf9effSMark Brown 
4065aaf9effSMark Brown 	/* If the control is turned off let's be polite */
4075aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
4085aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
4095aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
4105aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
4115aaf9effSMark Brown 		return;
4125aaf9effSMark Brown 	}
4135aaf9effSMark Brown 
4145aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
4155aaf9effSMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
4165aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
4175aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
4185aaf9effSMark Brown 		return;
4195aaf9effSMark Brown 	}
4205aaf9effSMark Brown 
4215aaf9effSMark Brown 	/* No idea what the default was for unreadable controls */
4225aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info)) {
4235aaf9effSMark Brown 		ksft_print_msg("%s couldn't read default\n", ctl->name);
4245aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
4255aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
4265aaf9effSMark Brown 		return;
4275aaf9effSMark Brown 	}
4285aaf9effSMark Brown 
4295aaf9effSMark Brown 	err = write_and_verify(ctl, ctl->def_val, NULL);
4305aaf9effSMark Brown 
4315aaf9effSMark Brown 	ksft_test_result(err >= 0, "write_default.%d.%d\n",
4325aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
4335aaf9effSMark Brown }
4345aaf9effSMark Brown 
4355aaf9effSMark Brown bool test_ctl_write_valid_boolean(struct ctl_data *ctl)
4365aaf9effSMark Brown {
4375aaf9effSMark Brown 	int err, i, j;
4385aaf9effSMark Brown 	bool fail = false;
4395aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
4405aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
4415aaf9effSMark Brown 
4425aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
4435aaf9effSMark Brown 
4445aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
4455aaf9effSMark Brown 		for (j = 0; j < 2; j++) {
4465aaf9effSMark Brown 			snd_ctl_elem_value_set_boolean(val, i, j);
4475aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
4485aaf9effSMark Brown 			if (err != 0)
4495aaf9effSMark Brown 				fail = true;
4505aaf9effSMark Brown 		}
4515aaf9effSMark Brown 	}
4525aaf9effSMark Brown 
4535aaf9effSMark Brown 	return !fail;
4545aaf9effSMark Brown }
4555aaf9effSMark Brown 
4565aaf9effSMark Brown bool test_ctl_write_valid_integer(struct ctl_data *ctl)
4575aaf9effSMark Brown {
4585aaf9effSMark Brown 	int err;
4595aaf9effSMark Brown 	int i;
4605aaf9effSMark Brown 	long j, step;
4615aaf9effSMark Brown 	bool fail = false;
4625aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
4635aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
4645aaf9effSMark Brown 
4655aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
4665aaf9effSMark Brown 
4675aaf9effSMark Brown 	step = snd_ctl_elem_info_get_step(ctl->info);
4685aaf9effSMark Brown 	if (!step)
4695aaf9effSMark Brown 		step = 1;
4705aaf9effSMark Brown 
4715aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
4725aaf9effSMark Brown 		for (j = snd_ctl_elem_info_get_min(ctl->info);
4735aaf9effSMark Brown 		     j <= snd_ctl_elem_info_get_max(ctl->info); j += step) {
4745aaf9effSMark Brown 
4755aaf9effSMark Brown 			snd_ctl_elem_value_set_integer(val, i, j);
4765aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
4775aaf9effSMark Brown 			if (err != 0)
4785aaf9effSMark Brown 				fail = true;
4795aaf9effSMark Brown 		}
4805aaf9effSMark Brown 	}
4815aaf9effSMark Brown 
4825aaf9effSMark Brown 
4835aaf9effSMark Brown 	return !fail;
4845aaf9effSMark Brown }
4855aaf9effSMark Brown 
4865aaf9effSMark Brown bool test_ctl_write_valid_integer64(struct ctl_data *ctl)
4875aaf9effSMark Brown {
4885aaf9effSMark Brown 	int err, i;
4895aaf9effSMark Brown 	long long j, step;
4905aaf9effSMark Brown 	bool fail = false;
4915aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
4925aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
4935aaf9effSMark Brown 
4945aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
4955aaf9effSMark Brown 
4965aaf9effSMark Brown 	step = snd_ctl_elem_info_get_step64(ctl->info);
4975aaf9effSMark Brown 	if (!step)
4985aaf9effSMark Brown 		step = 1;
4995aaf9effSMark Brown 
5005aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
5015aaf9effSMark Brown 		for (j = snd_ctl_elem_info_get_min64(ctl->info);
5025aaf9effSMark Brown 		     j <= snd_ctl_elem_info_get_max64(ctl->info); j += step) {
5035aaf9effSMark Brown 
5045aaf9effSMark Brown 			snd_ctl_elem_value_set_integer64(val, i, j);
5055aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
5065aaf9effSMark Brown 			if (err != 0)
5075aaf9effSMark Brown 				fail = true;
5085aaf9effSMark Brown 		}
5095aaf9effSMark Brown 	}
5105aaf9effSMark Brown 
5115aaf9effSMark Brown 	return !fail;
5125aaf9effSMark Brown }
5135aaf9effSMark Brown 
5145aaf9effSMark Brown bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
5155aaf9effSMark Brown {
5165aaf9effSMark Brown 	int err, i, j;
5175aaf9effSMark Brown 	bool fail = false;
5185aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
5195aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
5205aaf9effSMark Brown 
5215aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
5225aaf9effSMark Brown 
5235aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
5245aaf9effSMark Brown 		for (j = 0; j < snd_ctl_elem_info_get_items(ctl->info); j++) {
5255aaf9effSMark Brown 			snd_ctl_elem_value_set_enumerated(val, i, j);
5265aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
5275aaf9effSMark Brown 			if (err != 0)
5285aaf9effSMark Brown 				fail = true;
5295aaf9effSMark Brown 		}
5305aaf9effSMark Brown 	}
5315aaf9effSMark Brown 
5325aaf9effSMark Brown 	return !fail;
5335aaf9effSMark Brown }
5345aaf9effSMark Brown 
5355aaf9effSMark Brown void test_ctl_write_valid(struct ctl_data *ctl)
5365aaf9effSMark Brown {
5375aaf9effSMark Brown 	bool pass;
5385aaf9effSMark Brown 	int err;
5395aaf9effSMark Brown 
5405aaf9effSMark Brown 	/* If the control is turned off let's be polite */
5415aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
5425aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
5435aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
5445aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
5455aaf9effSMark Brown 		return;
5465aaf9effSMark Brown 	}
5475aaf9effSMark Brown 
5485aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
5495aaf9effSMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
5505aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
5515aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
5525aaf9effSMark Brown 		return;
5535aaf9effSMark Brown 	}
5545aaf9effSMark Brown 
5555aaf9effSMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
5565aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
5575aaf9effSMark Brown 		pass = test_ctl_write_valid_boolean(ctl);
5585aaf9effSMark Brown 		break;
5595aaf9effSMark Brown 
5605aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
5615aaf9effSMark Brown 		pass = test_ctl_write_valid_integer(ctl);
5625aaf9effSMark Brown 		break;
5635aaf9effSMark Brown 
5645aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
5655aaf9effSMark Brown 		pass = test_ctl_write_valid_integer64(ctl);
5665aaf9effSMark Brown 		break;
5675aaf9effSMark Brown 
5685aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
5695aaf9effSMark Brown 		pass = test_ctl_write_valid_enumerated(ctl);
5705aaf9effSMark Brown 		break;
5715aaf9effSMark Brown 
5725aaf9effSMark Brown 	default:
5735aaf9effSMark Brown 		/* No tests for this yet */
5745aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
5755aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
5765aaf9effSMark Brown 		return;
5775aaf9effSMark Brown 	}
5785aaf9effSMark Brown 
5795aaf9effSMark Brown 	/* Restore the default value to minimise disruption */
5805aaf9effSMark Brown 	err = write_and_verify(ctl, ctl->def_val, NULL);
5815aaf9effSMark Brown 	if (err < 0)
5825aaf9effSMark Brown 		pass = false;
5835aaf9effSMark Brown 
5845aaf9effSMark Brown 	ksft_test_result(pass, "write_valid.%d.%d\n",
5855aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
5865aaf9effSMark Brown }
5875aaf9effSMark Brown 
5885aaf9effSMark Brown int main(void)
5895aaf9effSMark Brown {
5905aaf9effSMark Brown 	struct ctl_data *ctl;
5915aaf9effSMark Brown 
5925aaf9effSMark Brown 	ksft_print_header();
5935aaf9effSMark Brown 
5945aaf9effSMark Brown 	find_controls();
5955aaf9effSMark Brown 
5965aaf9effSMark Brown 	ksft_set_plan(num_controls * TESTS_PER_CONTROL);
5975aaf9effSMark Brown 
5985aaf9effSMark Brown 	for (ctl = ctl_list; ctl != NULL; ctl = ctl->next) {
5995aaf9effSMark Brown 		/*
6005aaf9effSMark Brown 		 * Must test get_value() before we write anything, the
6015aaf9effSMark Brown 		 * test stores the default value for later cleanup.
6025aaf9effSMark Brown 		 */
6035aaf9effSMark Brown 		test_ctl_get_value(ctl);
6045aaf9effSMark Brown 		test_ctl_write_default(ctl);
6055aaf9effSMark Brown 		test_ctl_write_valid(ctl);
6065aaf9effSMark Brown 	}
6075aaf9effSMark Brown 
6085aaf9effSMark Brown 	ksft_exit_pass();
6095aaf9effSMark Brown 
6105aaf9effSMark Brown 	return 0;
6115aaf9effSMark Brown }
612