xref: /openbmc/linux/tools/testing/selftests/alsa/mixer-test.c (revision 88b6132248940954b958cd4e6d0432c640a9abd2)
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>
16*88b61322SMark Brown #include <limits.h>
175aaf9effSMark Brown #include <string.h>
185aaf9effSMark Brown #include <getopt.h>
195aaf9effSMark Brown #include <stdarg.h>
205aaf9effSMark Brown #include <ctype.h>
215aaf9effSMark Brown #include <math.h>
225aaf9effSMark Brown #include <errno.h>
235aaf9effSMark Brown #include <assert.h>
245aaf9effSMark Brown #include <alsa/asoundlib.h>
255aaf9effSMark Brown #include <poll.h>
265aaf9effSMark Brown #include <stdint.h>
275aaf9effSMark Brown 
285aaf9effSMark Brown #include "../kselftest.h"
295aaf9effSMark Brown 
30*88b61322SMark Brown #define TESTS_PER_CONTROL 4
315aaf9effSMark Brown 
325aaf9effSMark Brown struct card_data {
335aaf9effSMark Brown 	snd_ctl_t *handle;
345aaf9effSMark Brown 	int card;
355aaf9effSMark Brown 	int num_ctls;
365aaf9effSMark Brown 	snd_ctl_elem_list_t *ctls;
375aaf9effSMark Brown 	struct card_data *next;
385aaf9effSMark Brown };
395aaf9effSMark Brown 
405aaf9effSMark Brown struct ctl_data {
415aaf9effSMark Brown 	const char *name;
425aaf9effSMark Brown 	snd_ctl_elem_id_t *id;
435aaf9effSMark Brown 	snd_ctl_elem_info_t *info;
445aaf9effSMark Brown 	snd_ctl_elem_value_t *def_val;
455aaf9effSMark Brown 	int elem;
465aaf9effSMark Brown 	struct card_data *card;
475aaf9effSMark Brown 	struct ctl_data *next;
485aaf9effSMark Brown };
495aaf9effSMark Brown 
50b73dad80SJaroslav Kysela static const char *alsa_config =
51b73dad80SJaroslav Kysela "ctl.hw {\n"
52b73dad80SJaroslav Kysela "	@args [ CARD ]\n"
53b73dad80SJaroslav Kysela "	@args.CARD.type string\n"
54b73dad80SJaroslav Kysela "	type hw\n"
55b73dad80SJaroslav Kysela "	card $CARD\n"
56b73dad80SJaroslav Kysela "}\n"
57b73dad80SJaroslav Kysela ;
58b73dad80SJaroslav Kysela 
595aaf9effSMark Brown int num_cards = 0;
605aaf9effSMark Brown int num_controls = 0;
615aaf9effSMark Brown struct card_data *card_list = NULL;
625aaf9effSMark Brown struct ctl_data *ctl_list = NULL;
635aaf9effSMark Brown 
64b73dad80SJaroslav Kysela #ifdef SND_LIB_VER
65b73dad80SJaroslav Kysela #if SND_LIB_VERSION >= SND_LIB_VER(1, 2, 6)
66b73dad80SJaroslav Kysela #define LIB_HAS_LOAD_STRING
67b73dad80SJaroslav Kysela #endif
68b73dad80SJaroslav Kysela #endif
69b73dad80SJaroslav Kysela 
70b73dad80SJaroslav Kysela #ifndef LIB_HAS_LOAD_STRING
71b73dad80SJaroslav Kysela int snd_config_load_string(snd_config_t **config, const char *s, size_t size)
72b73dad80SJaroslav Kysela {
73b73dad80SJaroslav Kysela 	snd_input_t *input;
74b73dad80SJaroslav Kysela 	snd_config_t *dst;
75b73dad80SJaroslav Kysela 	int err;
76b73dad80SJaroslav Kysela 
77b73dad80SJaroslav Kysela 	assert(config && s);
78b73dad80SJaroslav Kysela 	if (size == 0)
79b73dad80SJaroslav Kysela 		size = strlen(s);
80b73dad80SJaroslav Kysela 	err = snd_input_buffer_open(&input, s, size);
81b73dad80SJaroslav Kysela 	if (err < 0)
82b73dad80SJaroslav Kysela 		return err;
83b73dad80SJaroslav Kysela 	err = snd_config_top(&dst);
84b73dad80SJaroslav Kysela 	if (err < 0) {
85b73dad80SJaroslav Kysela 		snd_input_close(input);
86b73dad80SJaroslav Kysela 		return err;
87b73dad80SJaroslav Kysela 	}
88b73dad80SJaroslav Kysela 	err = snd_config_load(dst, input);
89b73dad80SJaroslav Kysela 	snd_input_close(input);
90b73dad80SJaroslav Kysela 	if (err < 0) {
91b73dad80SJaroslav Kysela 		snd_config_delete(dst);
92b73dad80SJaroslav Kysela 		return err;
93b73dad80SJaroslav Kysela 	}
94b73dad80SJaroslav Kysela 	*config = dst;
95b73dad80SJaroslav Kysela 	return 0;
96b73dad80SJaroslav Kysela }
97b73dad80SJaroslav Kysela #endif
98b73dad80SJaroslav Kysela 
995aaf9effSMark Brown void find_controls(void)
1005aaf9effSMark Brown {
1015aaf9effSMark Brown 	char name[32];
1025aaf9effSMark Brown 	int card, ctl, err;
1035aaf9effSMark Brown 	struct card_data *card_data;
1045aaf9effSMark Brown 	struct ctl_data *ctl_data;
105b73dad80SJaroslav Kysela 	snd_config_t *config;
1065aaf9effSMark Brown 
1075aaf9effSMark Brown 	card = -1;
1085aaf9effSMark Brown 	if (snd_card_next(&card) < 0 || card < 0)
1095aaf9effSMark Brown 		return;
1105aaf9effSMark Brown 
111b73dad80SJaroslav Kysela 	err = snd_config_load_string(&config, alsa_config, strlen(alsa_config));
112b73dad80SJaroslav Kysela 	if (err < 0) {
113b73dad80SJaroslav Kysela 		ksft_print_msg("Unable to parse custom alsa-lib configuration: %s\n",
114b73dad80SJaroslav Kysela 			       snd_strerror(err));
115b73dad80SJaroslav Kysela 		ksft_exit_fail();
116b73dad80SJaroslav Kysela 	}
117b73dad80SJaroslav Kysela 
1185aaf9effSMark Brown 	while (card >= 0) {
1195aaf9effSMark Brown 		sprintf(name, "hw:%d", card);
1205aaf9effSMark Brown 
1215aaf9effSMark Brown 		card_data = malloc(sizeof(*card_data));
1225aaf9effSMark Brown 		if (!card_data)
1235aaf9effSMark Brown 			ksft_exit_fail_msg("Out of memory\n");
1245aaf9effSMark Brown 
125b73dad80SJaroslav Kysela 		err = snd_ctl_open_lconf(&card_data->handle, name, 0, config);
1265aaf9effSMark Brown 		if (err < 0) {
1275aaf9effSMark Brown 			ksft_print_msg("Failed to get hctl for card %d: %s\n",
1285aaf9effSMark Brown 				       card, snd_strerror(err));
1295aaf9effSMark Brown 			goto next_card;
1305aaf9effSMark Brown 		}
1315aaf9effSMark Brown 
1325aaf9effSMark Brown 		/* Count controls */
1335aaf9effSMark Brown 		snd_ctl_elem_list_malloc(&card_data->ctls);
1345aaf9effSMark Brown 		snd_ctl_elem_list(card_data->handle, card_data->ctls);
1355aaf9effSMark Brown 		card_data->num_ctls = snd_ctl_elem_list_get_count(card_data->ctls);
1365aaf9effSMark Brown 
1375aaf9effSMark Brown 		/* Enumerate control information */
1385aaf9effSMark Brown 		snd_ctl_elem_list_alloc_space(card_data->ctls, card_data->num_ctls);
1395aaf9effSMark Brown 		snd_ctl_elem_list(card_data->handle, card_data->ctls);
1405aaf9effSMark Brown 
1415aaf9effSMark Brown 		card_data->card = num_cards++;
1425aaf9effSMark Brown 		card_data->next = card_list;
1435aaf9effSMark Brown 		card_list = card_data;
1445aaf9effSMark Brown 
1455aaf9effSMark Brown 		num_controls += card_data->num_ctls;
1465aaf9effSMark Brown 
1475aaf9effSMark Brown 		for (ctl = 0; ctl < card_data->num_ctls; ctl++) {
1485aaf9effSMark Brown 			ctl_data = malloc(sizeof(*ctl_data));
1495aaf9effSMark Brown 			if (!ctl_data)
1505aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1515aaf9effSMark Brown 
1525aaf9effSMark Brown 			ctl_data->card = card_data;
1535aaf9effSMark Brown 			ctl_data->elem = ctl;
1545aaf9effSMark Brown 			ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls,
1555aaf9effSMark Brown 								    ctl);
1565aaf9effSMark Brown 
1575aaf9effSMark Brown 			err = snd_ctl_elem_id_malloc(&ctl_data->id);
1585aaf9effSMark Brown 			if (err < 0)
1595aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1605aaf9effSMark Brown 
1615aaf9effSMark Brown 			err = snd_ctl_elem_info_malloc(&ctl_data->info);
1625aaf9effSMark Brown 			if (err < 0)
1635aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1645aaf9effSMark Brown 
1655aaf9effSMark Brown 			err = snd_ctl_elem_value_malloc(&ctl_data->def_val);
1665aaf9effSMark Brown 			if (err < 0)
1675aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1685aaf9effSMark Brown 
1695aaf9effSMark Brown 			snd_ctl_elem_list_get_id(card_data->ctls, ctl,
1705aaf9effSMark Brown 						 ctl_data->id);
1715aaf9effSMark Brown 			snd_ctl_elem_info_set_id(ctl_data->info, ctl_data->id);
1725aaf9effSMark Brown 			err = snd_ctl_elem_info(card_data->handle,
1735aaf9effSMark Brown 						ctl_data->info);
1745aaf9effSMark Brown 			if (err < 0) {
1755aaf9effSMark Brown 				ksft_print_msg("%s getting info for %d\n",
1765aaf9effSMark Brown 					       snd_strerror(err),
1775aaf9effSMark Brown 					       ctl_data->name);
1785aaf9effSMark Brown 			}
1795aaf9effSMark Brown 
1805aaf9effSMark Brown 			snd_ctl_elem_value_set_id(ctl_data->def_val,
1815aaf9effSMark Brown 						  ctl_data->id);
1825aaf9effSMark Brown 
1835aaf9effSMark Brown 			ctl_data->next = ctl_list;
1845aaf9effSMark Brown 			ctl_list = ctl_data;
1855aaf9effSMark Brown 		}
1865aaf9effSMark Brown 
1875aaf9effSMark Brown 	next_card:
1885aaf9effSMark Brown 		if (snd_card_next(&card) < 0) {
1895aaf9effSMark Brown 			ksft_print_msg("snd_card_next");
1905aaf9effSMark Brown 			break;
1915aaf9effSMark Brown 		}
1925aaf9effSMark Brown 	}
193b73dad80SJaroslav Kysela 
194b73dad80SJaroslav Kysela 	snd_config_delete(config);
1955aaf9effSMark Brown }
1965aaf9effSMark Brown 
1973f48b137SMark Brown bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val,
1983f48b137SMark Brown 			   int index)
1993f48b137SMark Brown {
2003f48b137SMark Brown 	long int_val;
2013f48b137SMark Brown 	long long int64_val;
2023f48b137SMark Brown 
2033f48b137SMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
2043f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_NONE:
2053f48b137SMark Brown 		ksft_print_msg("%s.%d Invalid control type NONE\n",
2063f48b137SMark Brown 			       ctl->name, index);
2073f48b137SMark Brown 		return false;
2083f48b137SMark Brown 
2093f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
2103f48b137SMark Brown 		int_val = snd_ctl_elem_value_get_boolean(val, index);
2113f48b137SMark Brown 		switch (int_val) {
2123f48b137SMark Brown 		case 0:
2133f48b137SMark Brown 		case 1:
2143f48b137SMark Brown 			break;
2153f48b137SMark Brown 		default:
2163f48b137SMark Brown 			ksft_print_msg("%s.%d Invalid boolean value %ld\n",
2173f48b137SMark Brown 				       ctl->name, index, int_val);
2183f48b137SMark Brown 			return false;
2193f48b137SMark Brown 		}
2203f48b137SMark Brown 		break;
2213f48b137SMark Brown 
2223f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
2233f48b137SMark Brown 		int_val = snd_ctl_elem_value_get_integer(val, index);
2243f48b137SMark Brown 
2253f48b137SMark Brown 		if (int_val < snd_ctl_elem_info_get_min(ctl->info)) {
2263f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld less than minimum %ld\n",
2273f48b137SMark Brown 				       ctl->name, index, int_val,
2283f48b137SMark Brown 				       snd_ctl_elem_info_get_min(ctl->info));
2293f48b137SMark Brown 			return false;
2303f48b137SMark Brown 		}
2313f48b137SMark Brown 
2323f48b137SMark Brown 		if (int_val > snd_ctl_elem_info_get_max(ctl->info)) {
2333f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld more than maximum %ld\n",
2343f48b137SMark Brown 				       ctl->name, index, int_val,
2353f48b137SMark Brown 				       snd_ctl_elem_info_get_max(ctl->info));
2363f48b137SMark Brown 			return false;
2373f48b137SMark Brown 		}
2383f48b137SMark Brown 
2393f48b137SMark Brown 		/* Only check step size if there is one and we're in bounds */
2403f48b137SMark Brown 		if (snd_ctl_elem_info_get_step(ctl->info) &&
2413f48b137SMark Brown 		    (int_val - snd_ctl_elem_info_get_min(ctl->info) %
2423f48b137SMark Brown 		     snd_ctl_elem_info_get_step(ctl->info))) {
2433f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld invalid for step %ld minimum %ld\n",
2443f48b137SMark Brown 				       ctl->name, index, int_val,
2453f48b137SMark Brown 				       snd_ctl_elem_info_get_step(ctl->info),
2463f48b137SMark Brown 				       snd_ctl_elem_info_get_min(ctl->info));
2473f48b137SMark Brown 			return false;
2483f48b137SMark Brown 		}
2493f48b137SMark Brown 		break;
2503f48b137SMark Brown 
2513f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
2523f48b137SMark Brown 		int64_val = snd_ctl_elem_value_get_integer64(val, index);
2533f48b137SMark Brown 
2543f48b137SMark Brown 		if (int64_val < snd_ctl_elem_info_get_min64(ctl->info)) {
2553f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld less than minimum %lld\n",
2563f48b137SMark Brown 				       ctl->name, index, int64_val,
2573f48b137SMark Brown 				       snd_ctl_elem_info_get_min64(ctl->info));
2583f48b137SMark Brown 			return false;
2593f48b137SMark Brown 		}
2603f48b137SMark Brown 
2613f48b137SMark Brown 		if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) {
2623f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld more than maximum %lld\n",
2633f48b137SMark Brown 				       ctl->name, index, int64_val,
2643f48b137SMark Brown 				       snd_ctl_elem_info_get_max(ctl->info));
2653f48b137SMark Brown 			return false;
2663f48b137SMark Brown 		}
2673f48b137SMark Brown 
2683f48b137SMark Brown 		/* Only check step size if there is one and we're in bounds */
2693f48b137SMark Brown 		if (snd_ctl_elem_info_get_step64(ctl->info) &&
2703f48b137SMark Brown 		    (int64_val - snd_ctl_elem_info_get_min64(ctl->info)) %
2713f48b137SMark Brown 		    snd_ctl_elem_info_get_step64(ctl->info)) {
2723f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld invalid for step %lld minimum %lld\n",
2733f48b137SMark Brown 				       ctl->name, index, int64_val,
2743f48b137SMark Brown 				       snd_ctl_elem_info_get_step64(ctl->info),
2753f48b137SMark Brown 				       snd_ctl_elem_info_get_min64(ctl->info));
2763f48b137SMark Brown 			return false;
2773f48b137SMark Brown 		}
2783f48b137SMark Brown 		break;
2793f48b137SMark Brown 
28010f2f194SMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
28110f2f194SMark Brown 		int_val = snd_ctl_elem_value_get_enumerated(val, index);
28210f2f194SMark Brown 
28310f2f194SMark Brown 		if (int_val < 0) {
28410f2f194SMark Brown 			ksft_print_msg("%s.%d negative value %ld for enumeration\n",
28510f2f194SMark Brown 				       ctl->name, index, int_val);
28610f2f194SMark Brown 			return false;
28710f2f194SMark Brown 		}
28810f2f194SMark Brown 
28910f2f194SMark Brown 		if (int_val >= snd_ctl_elem_info_get_items(ctl->info)) {
29010f2f194SMark Brown 			ksft_print_msg("%s.%d value %ld more than item count %ld\n",
29110f2f194SMark Brown 				       ctl->name, index, int_val,
29210f2f194SMark Brown 				       snd_ctl_elem_info_get_items(ctl->info));
29310f2f194SMark Brown 			return false;
29410f2f194SMark Brown 		}
29510f2f194SMark Brown 		break;
29610f2f194SMark Brown 
2973f48b137SMark Brown 	default:
2983f48b137SMark Brown 		/* No tests for other types */
2993f48b137SMark Brown 		break;
3003f48b137SMark Brown 	}
3013f48b137SMark Brown 
3023f48b137SMark Brown 	return true;
3033f48b137SMark Brown }
3043f48b137SMark Brown 
3053f48b137SMark Brown /*
3063f48b137SMark Brown  * Check that the provided value meets the constraints for the
3073f48b137SMark Brown  * provided control.
3083f48b137SMark Brown  */
3093f48b137SMark Brown bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val)
3103f48b137SMark Brown {
3113f48b137SMark Brown 	int i;
3123f48b137SMark Brown 	bool valid = true;
3133f48b137SMark Brown 
3143f48b137SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++)
3153f48b137SMark Brown 		if (!ctl_value_index_valid(ctl, val, i))
3163f48b137SMark Brown 			valid = false;
3173f48b137SMark Brown 
3183f48b137SMark Brown 	return valid;
3193f48b137SMark Brown }
3203f48b137SMark Brown 
3215aaf9effSMark Brown /*
3225aaf9effSMark Brown  * Check that we can read the default value and it is valid. Write
3235aaf9effSMark Brown  * tests use the read value to restore the default.
3245aaf9effSMark Brown  */
3255aaf9effSMark Brown void test_ctl_get_value(struct ctl_data *ctl)
3265aaf9effSMark Brown {
3275aaf9effSMark Brown 	int err;
3285aaf9effSMark Brown 
3295aaf9effSMark Brown 	/* If the control is turned off let's be polite */
3305aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
3315aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
3325aaf9effSMark Brown 		ksft_test_result_skip("get_value.%d.%d\n",
3335aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
3345aaf9effSMark Brown 		return;
3355aaf9effSMark Brown 	}
3365aaf9effSMark Brown 
3375aaf9effSMark Brown 	/* Can't test reading on an unreadable control */
3385aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info)) {
3395aaf9effSMark Brown 		ksft_print_msg("%s is not readable\n", ctl->name);
3405aaf9effSMark Brown 		ksft_test_result_skip("get_value.%d.%d\n",
3415aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
3425aaf9effSMark Brown 		return;
3435aaf9effSMark Brown 	}
3445aaf9effSMark Brown 
3455aaf9effSMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val);
3465aaf9effSMark Brown 	if (err < 0) {
3475aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
3485aaf9effSMark Brown 			       snd_strerror(err));
3495aaf9effSMark Brown 		goto out;
3505aaf9effSMark Brown 	}
3515aaf9effSMark Brown 
3523f48b137SMark Brown 	if (!ctl_value_valid(ctl, ctl->def_val))
3533f48b137SMark Brown 		err = -EINVAL;
3545aaf9effSMark Brown 
3555aaf9effSMark Brown out:
3565aaf9effSMark Brown 	ksft_test_result(err >= 0, "get_value.%d.%d\n",
3575aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
3585aaf9effSMark Brown }
3595aaf9effSMark Brown 
3605aaf9effSMark Brown bool show_mismatch(struct ctl_data *ctl, int index,
3615aaf9effSMark Brown 		   snd_ctl_elem_value_t *read_val,
3625aaf9effSMark Brown 		   snd_ctl_elem_value_t *expected_val)
3635aaf9effSMark Brown {
3645aaf9effSMark Brown 	long long expected_int, read_int;
3655aaf9effSMark Brown 
3665aaf9effSMark Brown 	/*
3675aaf9effSMark Brown 	 * We factor out the code to compare values representable as
3685aaf9effSMark Brown 	 * integers, ensure that check doesn't log otherwise.
3695aaf9effSMark Brown 	 */
3705aaf9effSMark Brown 	expected_int = 0;
3715aaf9effSMark Brown 	read_int = 0;
3725aaf9effSMark Brown 
3735aaf9effSMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
3745aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
3755aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_boolean(expected_val,
3765aaf9effSMark Brown 							      index);
3775aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_boolean(read_val, index);
3785aaf9effSMark Brown 		break;
3795aaf9effSMark Brown 
3805aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
3815aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_integer(expected_val,
3825aaf9effSMark Brown 							      index);
3835aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_integer(read_val, index);
3845aaf9effSMark Brown 		break;
3855aaf9effSMark Brown 
3865aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
3875aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_integer64(expected_val,
3885aaf9effSMark Brown 								index);
3895aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_integer64(read_val,
3905aaf9effSMark Brown 							    index);
3915aaf9effSMark Brown 		break;
3925aaf9effSMark Brown 
3935aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
3945aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_enumerated(expected_val,
3955aaf9effSMark Brown 								 index);
3965aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_enumerated(read_val,
3975aaf9effSMark Brown 							     index);
3985aaf9effSMark Brown 		break;
3995aaf9effSMark Brown 
4005aaf9effSMark Brown 	default:
4015aaf9effSMark Brown 		break;
4025aaf9effSMark Brown 	}
4035aaf9effSMark Brown 
4045aaf9effSMark Brown 	if (expected_int != read_int) {
4057cc994f2STakashi Sakamoto 		/*
4067cc994f2STakashi Sakamoto 		 * NOTE: The volatile attribute means that the hardware
4077cc994f2STakashi Sakamoto 		 * can voluntarily change the state of control element
4087cc994f2STakashi Sakamoto 		 * independent of any operation by software.
4097cc994f2STakashi Sakamoto 		 */
4107cc994f2STakashi Sakamoto 		bool is_volatile = snd_ctl_elem_info_is_volatile(ctl->info);
4117cc994f2STakashi Sakamoto 		ksft_print_msg("%s.%d expected %lld but read %lld, is_volatile %d\n",
4127cc994f2STakashi Sakamoto 			       ctl->name, index, expected_int, read_int, is_volatile);
4137cc994f2STakashi Sakamoto 		return !is_volatile;
4145aaf9effSMark Brown 	} else {
4155aaf9effSMark Brown 		return false;
4165aaf9effSMark Brown 	}
4175aaf9effSMark Brown }
4185aaf9effSMark Brown 
4195aaf9effSMark Brown /*
4205aaf9effSMark Brown  * Write a value then if possible verify that we get the expected
4215aaf9effSMark Brown  * result.  An optional expected value can be provided if we expect
4225aaf9effSMark Brown  * the write to fail, for verifying that invalid writes don't corrupt
4235aaf9effSMark Brown  * anything.
4245aaf9effSMark Brown  */
4255aaf9effSMark Brown int write_and_verify(struct ctl_data *ctl,
4265aaf9effSMark Brown 		     snd_ctl_elem_value_t *write_val,
4275aaf9effSMark Brown 		     snd_ctl_elem_value_t *expected_val)
4285aaf9effSMark Brown {
4295aaf9effSMark Brown 	int err, i;
4305aaf9effSMark Brown 	bool error_expected, mismatch_shown;
4315aaf9effSMark Brown 	snd_ctl_elem_value_t *read_val, *w_val;
4325aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&read_val);
4335aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&w_val);
4345aaf9effSMark Brown 
4355aaf9effSMark Brown 	/*
4365aaf9effSMark Brown 	 * We need to copy the write value since writing can modify
4375aaf9effSMark Brown 	 * the value which causes surprises, and allocate an expected
4385aaf9effSMark Brown 	 * value if we expect to read back what we wrote.
4395aaf9effSMark Brown 	 */
4405aaf9effSMark Brown 	snd_ctl_elem_value_copy(w_val, write_val);
4415aaf9effSMark Brown 	if (expected_val) {
4425aaf9effSMark Brown 		error_expected = true;
4435aaf9effSMark Brown 	} else {
4445aaf9effSMark Brown 		error_expected = false;
4455aaf9effSMark Brown 		snd_ctl_elem_value_alloca(&expected_val);
4465aaf9effSMark Brown 		snd_ctl_elem_value_copy(expected_val, write_val);
4475aaf9effSMark Brown 	}
4485aaf9effSMark Brown 
4495aaf9effSMark Brown 	/*
4505aaf9effSMark Brown 	 * Do the write, if we have an expected value ignore the error
4515aaf9effSMark Brown 	 * and carry on to validate the expected value.
4525aaf9effSMark Brown 	 */
4535aaf9effSMark Brown 	err = snd_ctl_elem_write(ctl->card->handle, w_val);
4545aaf9effSMark Brown 	if (err < 0 && !error_expected) {
4555aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_write() failed: %s\n",
4565aaf9effSMark Brown 			       snd_strerror(err));
4575aaf9effSMark Brown 		return err;
4585aaf9effSMark Brown 	}
4595aaf9effSMark Brown 
4605aaf9effSMark Brown 	/* Can we do the verification part? */
4615aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info))
4625aaf9effSMark Brown 		return err;
4635aaf9effSMark Brown 
4645aaf9effSMark Brown 	snd_ctl_elem_value_set_id(read_val, ctl->id);
4655aaf9effSMark Brown 
4665aaf9effSMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, read_val);
4675aaf9effSMark Brown 	if (err < 0) {
4685aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
4695aaf9effSMark Brown 			       snd_strerror(err));
4705aaf9effSMark Brown 		return err;
4715aaf9effSMark Brown 	}
4725aaf9effSMark Brown 
4735aaf9effSMark Brown 	/*
4745aaf9effSMark Brown 	 * Use the libray to compare values, if there's a mismatch
4755aaf9effSMark Brown 	 * carry on and try to provide a more useful diagnostic than
4765aaf9effSMark Brown 	 * just "mismatch".
4775aaf9effSMark Brown 	 */
4785aaf9effSMark Brown 	if (!snd_ctl_elem_value_compare(expected_val, read_val))
4795aaf9effSMark Brown 		return 0;
4805aaf9effSMark Brown 
4815aaf9effSMark Brown 	mismatch_shown = false;
4825aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++)
4835aaf9effSMark Brown 		if (show_mismatch(ctl, i, read_val, expected_val))
4845aaf9effSMark Brown 			mismatch_shown = true;
4855aaf9effSMark Brown 
4865aaf9effSMark Brown 	if (!mismatch_shown)
4875aaf9effSMark Brown 		ksft_print_msg("%s read and written values differ\n",
4885aaf9effSMark Brown 			       ctl->name);
4895aaf9effSMark Brown 
4905aaf9effSMark Brown 	return -1;
4915aaf9effSMark Brown }
4925aaf9effSMark Brown 
4935aaf9effSMark Brown /*
4945aaf9effSMark Brown  * Make sure we can write the default value back to the control, this
4955aaf9effSMark Brown  * should validate that at least some write works.
4965aaf9effSMark Brown  */
4975aaf9effSMark Brown void test_ctl_write_default(struct ctl_data *ctl)
4985aaf9effSMark Brown {
4995aaf9effSMark Brown 	int err;
5005aaf9effSMark Brown 
5015aaf9effSMark Brown 	/* If the control is turned off let's be polite */
5025aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
5035aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
5045aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
5055aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
5065aaf9effSMark Brown 		return;
5075aaf9effSMark Brown 	}
5085aaf9effSMark Brown 
5095aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
5105aaf9effSMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
5115aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
5125aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
5135aaf9effSMark Brown 		return;
5145aaf9effSMark Brown 	}
5155aaf9effSMark Brown 
5165aaf9effSMark Brown 	/* No idea what the default was for unreadable controls */
5175aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info)) {
5185aaf9effSMark Brown 		ksft_print_msg("%s couldn't read default\n", ctl->name);
5195aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
5205aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
5215aaf9effSMark Brown 		return;
5225aaf9effSMark Brown 	}
5235aaf9effSMark Brown 
5245aaf9effSMark Brown 	err = write_and_verify(ctl, ctl->def_val, NULL);
5255aaf9effSMark Brown 
5265aaf9effSMark Brown 	ksft_test_result(err >= 0, "write_default.%d.%d\n",
5275aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
5285aaf9effSMark Brown }
5295aaf9effSMark Brown 
5305aaf9effSMark Brown bool test_ctl_write_valid_boolean(struct ctl_data *ctl)
5315aaf9effSMark Brown {
5325aaf9effSMark Brown 	int err, i, j;
5335aaf9effSMark Brown 	bool fail = false;
5345aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
5355aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
5365aaf9effSMark Brown 
5375aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
5385aaf9effSMark Brown 
5395aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
5405aaf9effSMark Brown 		for (j = 0; j < 2; j++) {
5415aaf9effSMark Brown 			snd_ctl_elem_value_set_boolean(val, i, j);
5425aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
5435aaf9effSMark Brown 			if (err != 0)
5445aaf9effSMark Brown 				fail = true;
5455aaf9effSMark Brown 		}
5465aaf9effSMark Brown 	}
5475aaf9effSMark Brown 
5485aaf9effSMark Brown 	return !fail;
5495aaf9effSMark Brown }
5505aaf9effSMark Brown 
5515aaf9effSMark Brown bool test_ctl_write_valid_integer(struct ctl_data *ctl)
5525aaf9effSMark Brown {
5535aaf9effSMark Brown 	int err;
5545aaf9effSMark Brown 	int i;
5555aaf9effSMark Brown 	long j, step;
5565aaf9effSMark Brown 	bool fail = false;
5575aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
5585aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
5595aaf9effSMark Brown 
5605aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
5615aaf9effSMark Brown 
5625aaf9effSMark Brown 	step = snd_ctl_elem_info_get_step(ctl->info);
5635aaf9effSMark Brown 	if (!step)
5645aaf9effSMark Brown 		step = 1;
5655aaf9effSMark Brown 
5665aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
5675aaf9effSMark Brown 		for (j = snd_ctl_elem_info_get_min(ctl->info);
5685aaf9effSMark Brown 		     j <= snd_ctl_elem_info_get_max(ctl->info); j += step) {
5695aaf9effSMark Brown 
5705aaf9effSMark Brown 			snd_ctl_elem_value_set_integer(val, i, j);
5715aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
5725aaf9effSMark Brown 			if (err != 0)
5735aaf9effSMark Brown 				fail = true;
5745aaf9effSMark Brown 		}
5755aaf9effSMark Brown 	}
5765aaf9effSMark Brown 
5775aaf9effSMark Brown 
5785aaf9effSMark Brown 	return !fail;
5795aaf9effSMark Brown }
5805aaf9effSMark Brown 
5815aaf9effSMark Brown bool test_ctl_write_valid_integer64(struct ctl_data *ctl)
5825aaf9effSMark Brown {
5835aaf9effSMark Brown 	int err, i;
5845aaf9effSMark Brown 	long long j, step;
5855aaf9effSMark Brown 	bool fail = false;
5865aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
5875aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
5885aaf9effSMark Brown 
5895aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
5905aaf9effSMark Brown 
5915aaf9effSMark Brown 	step = snd_ctl_elem_info_get_step64(ctl->info);
5925aaf9effSMark Brown 	if (!step)
5935aaf9effSMark Brown 		step = 1;
5945aaf9effSMark Brown 
5955aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
5965aaf9effSMark Brown 		for (j = snd_ctl_elem_info_get_min64(ctl->info);
5975aaf9effSMark Brown 		     j <= snd_ctl_elem_info_get_max64(ctl->info); j += step) {
5985aaf9effSMark Brown 
5995aaf9effSMark Brown 			snd_ctl_elem_value_set_integer64(val, i, j);
6005aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
6015aaf9effSMark Brown 			if (err != 0)
6025aaf9effSMark Brown 				fail = true;
6035aaf9effSMark Brown 		}
6045aaf9effSMark Brown 	}
6055aaf9effSMark Brown 
6065aaf9effSMark Brown 	return !fail;
6075aaf9effSMark Brown }
6085aaf9effSMark Brown 
6095aaf9effSMark Brown bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
6105aaf9effSMark Brown {
6115aaf9effSMark Brown 	int err, i, j;
6125aaf9effSMark Brown 	bool fail = false;
6135aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
6145aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
6155aaf9effSMark Brown 
6165aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
6175aaf9effSMark Brown 
6185aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
6195aaf9effSMark Brown 		for (j = 0; j < snd_ctl_elem_info_get_items(ctl->info); j++) {
6205aaf9effSMark Brown 			snd_ctl_elem_value_set_enumerated(val, i, j);
6215aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
6225aaf9effSMark Brown 			if (err != 0)
6235aaf9effSMark Brown 				fail = true;
6245aaf9effSMark Brown 		}
6255aaf9effSMark Brown 	}
6265aaf9effSMark Brown 
6275aaf9effSMark Brown 	return !fail;
6285aaf9effSMark Brown }
6295aaf9effSMark Brown 
6305aaf9effSMark Brown void test_ctl_write_valid(struct ctl_data *ctl)
6315aaf9effSMark Brown {
6325aaf9effSMark Brown 	bool pass;
6335aaf9effSMark Brown 	int err;
6345aaf9effSMark Brown 
6355aaf9effSMark Brown 	/* If the control is turned off let's be polite */
6365aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
6375aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
6385aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
6395aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
6405aaf9effSMark Brown 		return;
6415aaf9effSMark Brown 	}
6425aaf9effSMark Brown 
6435aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
6445aaf9effSMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
6455aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
6465aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
6475aaf9effSMark Brown 		return;
6485aaf9effSMark Brown 	}
6495aaf9effSMark Brown 
6505aaf9effSMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
6515aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
6525aaf9effSMark Brown 		pass = test_ctl_write_valid_boolean(ctl);
6535aaf9effSMark Brown 		break;
6545aaf9effSMark Brown 
6555aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
6565aaf9effSMark Brown 		pass = test_ctl_write_valid_integer(ctl);
6575aaf9effSMark Brown 		break;
6585aaf9effSMark Brown 
6595aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
6605aaf9effSMark Brown 		pass = test_ctl_write_valid_integer64(ctl);
6615aaf9effSMark Brown 		break;
6625aaf9effSMark Brown 
6635aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
6645aaf9effSMark Brown 		pass = test_ctl_write_valid_enumerated(ctl);
6655aaf9effSMark Brown 		break;
6665aaf9effSMark Brown 
6675aaf9effSMark Brown 	default:
6685aaf9effSMark Brown 		/* No tests for this yet */
6695aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
6705aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
6715aaf9effSMark Brown 		return;
6725aaf9effSMark Brown 	}
6735aaf9effSMark Brown 
6745aaf9effSMark Brown 	/* Restore the default value to minimise disruption */
6755aaf9effSMark Brown 	err = write_and_verify(ctl, ctl->def_val, NULL);
6765aaf9effSMark Brown 	if (err < 0)
6775aaf9effSMark Brown 		pass = false;
6785aaf9effSMark Brown 
6795aaf9effSMark Brown 	ksft_test_result(pass, "write_valid.%d.%d\n",
6805aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
6815aaf9effSMark Brown }
6825aaf9effSMark Brown 
683*88b61322SMark Brown bool test_ctl_write_invalid_value(struct ctl_data *ctl,
684*88b61322SMark Brown 				  snd_ctl_elem_value_t *val)
685*88b61322SMark Brown {
686*88b61322SMark Brown 	int err;
687*88b61322SMark Brown 	long val_read;
688*88b61322SMark Brown 
689*88b61322SMark Brown 	/* Ideally this will fail... */
690*88b61322SMark Brown 	err = snd_ctl_elem_write(ctl->card->handle, val);
691*88b61322SMark Brown 	if (err < 0)
692*88b61322SMark Brown 		return false;
693*88b61322SMark Brown 
694*88b61322SMark Brown 	/* ...but some devices will clamp to an in range value */
695*88b61322SMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, val);
696*88b61322SMark Brown 	if (err < 0) {
697*88b61322SMark Brown 		ksft_print_msg("%s failed to read: %s\n",
698*88b61322SMark Brown 			       ctl->name, snd_strerror(err));
699*88b61322SMark Brown 		return true;
700*88b61322SMark Brown 	}
701*88b61322SMark Brown 
702*88b61322SMark Brown 	return !ctl_value_valid(ctl, val);
703*88b61322SMark Brown }
704*88b61322SMark Brown 
705*88b61322SMark Brown bool test_ctl_write_invalid_boolean(struct ctl_data *ctl)
706*88b61322SMark Brown {
707*88b61322SMark Brown 	int err, i;
708*88b61322SMark Brown 	long val_read;
709*88b61322SMark Brown 	bool fail = false;
710*88b61322SMark Brown 	snd_ctl_elem_value_t *val;
711*88b61322SMark Brown 	snd_ctl_elem_value_alloca(&val);
712*88b61322SMark Brown 
713*88b61322SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
714*88b61322SMark Brown 		snd_ctl_elem_value_copy(val, ctl->def_val);
715*88b61322SMark Brown 		snd_ctl_elem_value_set_boolean(val, i, 2);
716*88b61322SMark Brown 
717*88b61322SMark Brown 		if (test_ctl_write_invalid_value(ctl, val))
718*88b61322SMark Brown 			fail = true;
719*88b61322SMark Brown 	}
720*88b61322SMark Brown 
721*88b61322SMark Brown 	return !fail;
722*88b61322SMark Brown }
723*88b61322SMark Brown 
724*88b61322SMark Brown bool test_ctl_write_invalid_integer(struct ctl_data *ctl)
725*88b61322SMark Brown {
726*88b61322SMark Brown 	int i;
727*88b61322SMark Brown 	bool fail = false;
728*88b61322SMark Brown 	snd_ctl_elem_value_t *val;
729*88b61322SMark Brown 	snd_ctl_elem_value_alloca(&val);
730*88b61322SMark Brown 
731*88b61322SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
732*88b61322SMark Brown 		if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) {
733*88b61322SMark Brown 			/* Just under range */
734*88b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
735*88b61322SMark Brown 			snd_ctl_elem_value_set_integer(val, i,
736*88b61322SMark Brown 			       snd_ctl_elem_info_get_min(ctl->info) - 1);
737*88b61322SMark Brown 
738*88b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
739*88b61322SMark Brown 				fail = true;
740*88b61322SMark Brown 
741*88b61322SMark Brown 			/* Minimum representable value */
742*88b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
743*88b61322SMark Brown 			snd_ctl_elem_value_set_integer(val, i, LONG_MIN);
744*88b61322SMark Brown 
745*88b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
746*88b61322SMark Brown 				fail = true;
747*88b61322SMark Brown 		}
748*88b61322SMark Brown 
749*88b61322SMark Brown 		if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) {
750*88b61322SMark Brown 			/* Just over range */
751*88b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
752*88b61322SMark Brown 			snd_ctl_elem_value_set_integer(val, i,
753*88b61322SMark Brown 			       snd_ctl_elem_info_get_max(ctl->info) + 1);
754*88b61322SMark Brown 
755*88b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
756*88b61322SMark Brown 				fail = true;
757*88b61322SMark Brown 
758*88b61322SMark Brown 			/* Maximum representable value */
759*88b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
760*88b61322SMark Brown 			snd_ctl_elem_value_set_integer(val, i, LONG_MAX);
761*88b61322SMark Brown 
762*88b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
763*88b61322SMark Brown 				fail = true;
764*88b61322SMark Brown 		}
765*88b61322SMark Brown 	}
766*88b61322SMark Brown 
767*88b61322SMark Brown 	return !fail;
768*88b61322SMark Brown }
769*88b61322SMark Brown 
770*88b61322SMark Brown bool test_ctl_write_invalid_integer64(struct ctl_data *ctl)
771*88b61322SMark Brown {
772*88b61322SMark Brown 	int i;
773*88b61322SMark Brown 	bool fail = false;
774*88b61322SMark Brown 	snd_ctl_elem_value_t *val;
775*88b61322SMark Brown 	snd_ctl_elem_value_alloca(&val);
776*88b61322SMark Brown 
777*88b61322SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
778*88b61322SMark Brown 		if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) {
779*88b61322SMark Brown 			/* Just under range */
780*88b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
781*88b61322SMark Brown 			snd_ctl_elem_value_set_integer64(val, i,
782*88b61322SMark Brown 				snd_ctl_elem_info_get_min64(ctl->info) - 1);
783*88b61322SMark Brown 
784*88b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
785*88b61322SMark Brown 				fail = true;
786*88b61322SMark Brown 
787*88b61322SMark Brown 			/* Minimum representable value */
788*88b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
789*88b61322SMark Brown 			snd_ctl_elem_value_set_integer64(val, i, LLONG_MIN);
790*88b61322SMark Brown 
791*88b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
792*88b61322SMark Brown 				fail = true;
793*88b61322SMark Brown 		}
794*88b61322SMark Brown 
795*88b61322SMark Brown 		if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) {
796*88b61322SMark Brown 			/* Just over range */
797*88b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
798*88b61322SMark Brown 			snd_ctl_elem_value_set_integer64(val, i,
799*88b61322SMark Brown 				snd_ctl_elem_info_get_max64(ctl->info) + 1);
800*88b61322SMark Brown 
801*88b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
802*88b61322SMark Brown 				fail = true;
803*88b61322SMark Brown 
804*88b61322SMark Brown 			/* Maximum representable value */
805*88b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
806*88b61322SMark Brown 			snd_ctl_elem_value_set_integer64(val, i, LLONG_MAX);
807*88b61322SMark Brown 
808*88b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
809*88b61322SMark Brown 				fail = true;
810*88b61322SMark Brown 		}
811*88b61322SMark Brown 	}
812*88b61322SMark Brown 
813*88b61322SMark Brown 	return !fail;
814*88b61322SMark Brown }
815*88b61322SMark Brown 
816*88b61322SMark Brown bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl)
817*88b61322SMark Brown {
818*88b61322SMark Brown 	int err, i;
819*88b61322SMark Brown 	unsigned int val_read;
820*88b61322SMark Brown 	bool fail = false;
821*88b61322SMark Brown 	snd_ctl_elem_value_t *val;
822*88b61322SMark Brown 	snd_ctl_elem_value_alloca(&val);
823*88b61322SMark Brown 
824*88b61322SMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
825*88b61322SMark Brown 
826*88b61322SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
827*88b61322SMark Brown 		/* One beyond maximum */
828*88b61322SMark Brown 		snd_ctl_elem_value_copy(val, ctl->def_val);
829*88b61322SMark Brown 		snd_ctl_elem_value_set_enumerated(val, i,
830*88b61322SMark Brown 				  snd_ctl_elem_info_get_items(ctl->info));
831*88b61322SMark Brown 
832*88b61322SMark Brown 		if (test_ctl_write_invalid_value(ctl, val))
833*88b61322SMark Brown 			fail = true;
834*88b61322SMark Brown 
835*88b61322SMark Brown 		/* Maximum representable value */
836*88b61322SMark Brown 		snd_ctl_elem_value_copy(val, ctl->def_val);
837*88b61322SMark Brown 		snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX);
838*88b61322SMark Brown 
839*88b61322SMark Brown 		if (test_ctl_write_invalid_value(ctl, val))
840*88b61322SMark Brown 			fail = true;
841*88b61322SMark Brown 
842*88b61322SMark Brown 	}
843*88b61322SMark Brown 
844*88b61322SMark Brown 	return !fail;
845*88b61322SMark Brown }
846*88b61322SMark Brown 
847*88b61322SMark Brown 
848*88b61322SMark Brown void test_ctl_write_invalid(struct ctl_data *ctl)
849*88b61322SMark Brown {
850*88b61322SMark Brown 	bool pass;
851*88b61322SMark Brown 	int err;
852*88b61322SMark Brown 
853*88b61322SMark Brown 	/* If the control is turned off let's be polite */
854*88b61322SMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
855*88b61322SMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
856*88b61322SMark Brown 		ksft_test_result_skip("write_invalid.%d.%d\n",
857*88b61322SMark Brown 				      ctl->card->card, ctl->elem);
858*88b61322SMark Brown 		return;
859*88b61322SMark Brown 	}
860*88b61322SMark Brown 
861*88b61322SMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
862*88b61322SMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
863*88b61322SMark Brown 		ksft_test_result_skip("write_invalid.%d.%d\n",
864*88b61322SMark Brown 				      ctl->card->card, ctl->elem);
865*88b61322SMark Brown 		return;
866*88b61322SMark Brown 	}
867*88b61322SMark Brown 
868*88b61322SMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
869*88b61322SMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
870*88b61322SMark Brown 		pass = test_ctl_write_invalid_boolean(ctl);
871*88b61322SMark Brown 		break;
872*88b61322SMark Brown 
873*88b61322SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
874*88b61322SMark Brown 		pass = test_ctl_write_invalid_integer(ctl);
875*88b61322SMark Brown 		break;
876*88b61322SMark Brown 
877*88b61322SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
878*88b61322SMark Brown 		pass = test_ctl_write_invalid_integer64(ctl);
879*88b61322SMark Brown 		break;
880*88b61322SMark Brown 
881*88b61322SMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
882*88b61322SMark Brown 		pass = test_ctl_write_invalid_enumerated(ctl);
883*88b61322SMark Brown 		break;
884*88b61322SMark Brown 
885*88b61322SMark Brown 	default:
886*88b61322SMark Brown 		/* No tests for this yet */
887*88b61322SMark Brown 		ksft_test_result_skip("write_invalid.%d.%d\n",
888*88b61322SMark Brown 				      ctl->card->card, ctl->elem);
889*88b61322SMark Brown 		return;
890*88b61322SMark Brown 	}
891*88b61322SMark Brown 
892*88b61322SMark Brown 	/* Restore the default value to minimise disruption */
893*88b61322SMark Brown 	err = write_and_verify(ctl, ctl->def_val, NULL);
894*88b61322SMark Brown 	if (err < 0)
895*88b61322SMark Brown 		pass = false;
896*88b61322SMark Brown 
897*88b61322SMark Brown 	ksft_test_result(pass, "write_invalid.%d.%d\n",
898*88b61322SMark Brown 			 ctl->card->card, ctl->elem);
899*88b61322SMark Brown }
900*88b61322SMark Brown 
9015aaf9effSMark Brown int main(void)
9025aaf9effSMark Brown {
9035aaf9effSMark Brown 	struct ctl_data *ctl;
9045aaf9effSMark Brown 
9055aaf9effSMark Brown 	ksft_print_header();
9065aaf9effSMark Brown 
9075aaf9effSMark Brown 	find_controls();
9085aaf9effSMark Brown 
9095aaf9effSMark Brown 	ksft_set_plan(num_controls * TESTS_PER_CONTROL);
9105aaf9effSMark Brown 
9115aaf9effSMark Brown 	for (ctl = ctl_list; ctl != NULL; ctl = ctl->next) {
9125aaf9effSMark Brown 		/*
9135aaf9effSMark Brown 		 * Must test get_value() before we write anything, the
9145aaf9effSMark Brown 		 * test stores the default value for later cleanup.
9155aaf9effSMark Brown 		 */
9165aaf9effSMark Brown 		test_ctl_get_value(ctl);
9175aaf9effSMark Brown 		test_ctl_write_default(ctl);
9185aaf9effSMark Brown 		test_ctl_write_valid(ctl);
919*88b61322SMark Brown 		test_ctl_write_invalid(ctl);
9205aaf9effSMark Brown 	}
9215aaf9effSMark Brown 
9225aaf9effSMark Brown 	ksft_exit_pass();
9235aaf9effSMark Brown 
9245aaf9effSMark Brown 	return 0;
9255aaf9effSMark Brown }
926