xref: /openbmc/linux/tools/testing/selftests/alsa/mixer-test.c (revision b1446bda56456887f8d439938163164fe916bc0d)
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>
6*b1446bdaSMark Brown // Copyright (c) 2021-2 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>
1688b61322SMark 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*b1446bdaSMark Brown #define TESTS_PER_CONTROL 6
315aaf9effSMark Brown 
325aaf9effSMark Brown struct card_data {
335aaf9effSMark Brown 	snd_ctl_t *handle;
345aaf9effSMark Brown 	int card;
35*b1446bdaSMark Brown 	struct pollfd pollfd;
365aaf9effSMark Brown 	int num_ctls;
375aaf9effSMark Brown 	snd_ctl_elem_list_t *ctls;
385aaf9effSMark Brown 	struct card_data *next;
395aaf9effSMark Brown };
405aaf9effSMark Brown 
415aaf9effSMark Brown struct ctl_data {
425aaf9effSMark Brown 	const char *name;
435aaf9effSMark Brown 	snd_ctl_elem_id_t *id;
445aaf9effSMark Brown 	snd_ctl_elem_info_t *info;
455aaf9effSMark Brown 	snd_ctl_elem_value_t *def_val;
465aaf9effSMark Brown 	int elem;
47*b1446bdaSMark Brown 	int event_missing;
48*b1446bdaSMark Brown 	int event_spurious;
495aaf9effSMark Brown 	struct card_data *card;
505aaf9effSMark Brown 	struct ctl_data *next;
515aaf9effSMark Brown };
525aaf9effSMark Brown 
53b73dad80SJaroslav Kysela static const char *alsa_config =
54b73dad80SJaroslav Kysela "ctl.hw {\n"
55b73dad80SJaroslav Kysela "	@args [ CARD ]\n"
56b73dad80SJaroslav Kysela "	@args.CARD.type string\n"
57b73dad80SJaroslav Kysela "	type hw\n"
58b73dad80SJaroslav Kysela "	card $CARD\n"
59b73dad80SJaroslav Kysela "}\n"
60b73dad80SJaroslav Kysela ;
61b73dad80SJaroslav Kysela 
625aaf9effSMark Brown int num_cards = 0;
635aaf9effSMark Brown int num_controls = 0;
645aaf9effSMark Brown struct card_data *card_list = NULL;
655aaf9effSMark Brown struct ctl_data *ctl_list = NULL;
665aaf9effSMark Brown 
67b73dad80SJaroslav Kysela #ifdef SND_LIB_VER
68b73dad80SJaroslav Kysela #if SND_LIB_VERSION >= SND_LIB_VER(1, 2, 6)
69b73dad80SJaroslav Kysela #define LIB_HAS_LOAD_STRING
70b73dad80SJaroslav Kysela #endif
71b73dad80SJaroslav Kysela #endif
72b73dad80SJaroslav Kysela 
73b73dad80SJaroslav Kysela #ifndef LIB_HAS_LOAD_STRING
74b73dad80SJaroslav Kysela int snd_config_load_string(snd_config_t **config, const char *s, size_t size)
75b73dad80SJaroslav Kysela {
76b73dad80SJaroslav Kysela 	snd_input_t *input;
77b73dad80SJaroslav Kysela 	snd_config_t *dst;
78b73dad80SJaroslav Kysela 	int err;
79b73dad80SJaroslav Kysela 
80b73dad80SJaroslav Kysela 	assert(config && s);
81b73dad80SJaroslav Kysela 	if (size == 0)
82b73dad80SJaroslav Kysela 		size = strlen(s);
83b73dad80SJaroslav Kysela 	err = snd_input_buffer_open(&input, s, size);
84b73dad80SJaroslav Kysela 	if (err < 0)
85b73dad80SJaroslav Kysela 		return err;
86b73dad80SJaroslav Kysela 	err = snd_config_top(&dst);
87b73dad80SJaroslav Kysela 	if (err < 0) {
88b73dad80SJaroslav Kysela 		snd_input_close(input);
89b73dad80SJaroslav Kysela 		return err;
90b73dad80SJaroslav Kysela 	}
91b73dad80SJaroslav Kysela 	err = snd_config_load(dst, input);
92b73dad80SJaroslav Kysela 	snd_input_close(input);
93b73dad80SJaroslav Kysela 	if (err < 0) {
94b73dad80SJaroslav Kysela 		snd_config_delete(dst);
95b73dad80SJaroslav Kysela 		return err;
96b73dad80SJaroslav Kysela 	}
97b73dad80SJaroslav Kysela 	*config = dst;
98b73dad80SJaroslav Kysela 	return 0;
99b73dad80SJaroslav Kysela }
100b73dad80SJaroslav Kysela #endif
101b73dad80SJaroslav Kysela 
1025aaf9effSMark Brown void find_controls(void)
1035aaf9effSMark Brown {
1045aaf9effSMark Brown 	char name[32];
1055aaf9effSMark Brown 	int card, ctl, err;
1065aaf9effSMark Brown 	struct card_data *card_data;
1075aaf9effSMark Brown 	struct ctl_data *ctl_data;
108b73dad80SJaroslav Kysela 	snd_config_t *config;
1095aaf9effSMark Brown 
1105aaf9effSMark Brown 	card = -1;
1115aaf9effSMark Brown 	if (snd_card_next(&card) < 0 || card < 0)
1125aaf9effSMark Brown 		return;
1135aaf9effSMark Brown 
114b73dad80SJaroslav Kysela 	err = snd_config_load_string(&config, alsa_config, strlen(alsa_config));
115b73dad80SJaroslav Kysela 	if (err < 0) {
116b73dad80SJaroslav Kysela 		ksft_print_msg("Unable to parse custom alsa-lib configuration: %s\n",
117b73dad80SJaroslav Kysela 			       snd_strerror(err));
118b73dad80SJaroslav Kysela 		ksft_exit_fail();
119b73dad80SJaroslav Kysela 	}
120b73dad80SJaroslav Kysela 
1215aaf9effSMark Brown 	while (card >= 0) {
1225aaf9effSMark Brown 		sprintf(name, "hw:%d", card);
1235aaf9effSMark Brown 
1245aaf9effSMark Brown 		card_data = malloc(sizeof(*card_data));
1255aaf9effSMark Brown 		if (!card_data)
1265aaf9effSMark Brown 			ksft_exit_fail_msg("Out of memory\n");
1275aaf9effSMark Brown 
128b73dad80SJaroslav Kysela 		err = snd_ctl_open_lconf(&card_data->handle, name, 0, config);
1295aaf9effSMark Brown 		if (err < 0) {
1305aaf9effSMark Brown 			ksft_print_msg("Failed to get hctl for card %d: %s\n",
1315aaf9effSMark Brown 				       card, snd_strerror(err));
1325aaf9effSMark Brown 			goto next_card;
1335aaf9effSMark Brown 		}
1345aaf9effSMark Brown 
1355aaf9effSMark Brown 		/* Count controls */
1365aaf9effSMark Brown 		snd_ctl_elem_list_malloc(&card_data->ctls);
1375aaf9effSMark Brown 		snd_ctl_elem_list(card_data->handle, card_data->ctls);
1385aaf9effSMark Brown 		card_data->num_ctls = snd_ctl_elem_list_get_count(card_data->ctls);
1395aaf9effSMark Brown 
1405aaf9effSMark Brown 		/* Enumerate control information */
1415aaf9effSMark Brown 		snd_ctl_elem_list_alloc_space(card_data->ctls, card_data->num_ctls);
1425aaf9effSMark Brown 		snd_ctl_elem_list(card_data->handle, card_data->ctls);
1435aaf9effSMark Brown 
1445aaf9effSMark Brown 		card_data->card = num_cards++;
1455aaf9effSMark Brown 		card_data->next = card_list;
1465aaf9effSMark Brown 		card_list = card_data;
1475aaf9effSMark Brown 
1485aaf9effSMark Brown 		num_controls += card_data->num_ctls;
1495aaf9effSMark Brown 
1505aaf9effSMark Brown 		for (ctl = 0; ctl < card_data->num_ctls; ctl++) {
1515aaf9effSMark Brown 			ctl_data = malloc(sizeof(*ctl_data));
1525aaf9effSMark Brown 			if (!ctl_data)
1535aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1545aaf9effSMark Brown 
155*b1446bdaSMark Brown 			memset(ctl_data, 0, sizeof(*ctl_data));
1565aaf9effSMark Brown 			ctl_data->card = card_data;
1575aaf9effSMark Brown 			ctl_data->elem = ctl;
1585aaf9effSMark Brown 			ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls,
1595aaf9effSMark Brown 								    ctl);
1605aaf9effSMark Brown 
1615aaf9effSMark Brown 			err = snd_ctl_elem_id_malloc(&ctl_data->id);
1625aaf9effSMark Brown 			if (err < 0)
1635aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1645aaf9effSMark Brown 
1655aaf9effSMark Brown 			err = snd_ctl_elem_info_malloc(&ctl_data->info);
1665aaf9effSMark Brown 			if (err < 0)
1675aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1685aaf9effSMark Brown 
1695aaf9effSMark Brown 			err = snd_ctl_elem_value_malloc(&ctl_data->def_val);
1705aaf9effSMark Brown 			if (err < 0)
1715aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1725aaf9effSMark Brown 
1735aaf9effSMark Brown 			snd_ctl_elem_list_get_id(card_data->ctls, ctl,
1745aaf9effSMark Brown 						 ctl_data->id);
1755aaf9effSMark Brown 			snd_ctl_elem_info_set_id(ctl_data->info, ctl_data->id);
1765aaf9effSMark Brown 			err = snd_ctl_elem_info(card_data->handle,
1775aaf9effSMark Brown 						ctl_data->info);
1785aaf9effSMark Brown 			if (err < 0) {
1795aaf9effSMark Brown 				ksft_print_msg("%s getting info for %d\n",
1805aaf9effSMark Brown 					       snd_strerror(err),
1815aaf9effSMark Brown 					       ctl_data->name);
1825aaf9effSMark Brown 			}
1835aaf9effSMark Brown 
1845aaf9effSMark Brown 			snd_ctl_elem_value_set_id(ctl_data->def_val,
1855aaf9effSMark Brown 						  ctl_data->id);
1865aaf9effSMark Brown 
1875aaf9effSMark Brown 			ctl_data->next = ctl_list;
1885aaf9effSMark Brown 			ctl_list = ctl_data;
1895aaf9effSMark Brown 		}
1905aaf9effSMark Brown 
191*b1446bdaSMark Brown 		/* Set up for events */
192*b1446bdaSMark Brown 		err = snd_ctl_subscribe_events(card_data->handle, true);
193*b1446bdaSMark Brown 		if (err < 0) {
194*b1446bdaSMark Brown 			ksft_exit_fail_msg("snd_ctl_subscribe_events() failed for card %d: %d\n",
195*b1446bdaSMark Brown 					   card, err);
196*b1446bdaSMark Brown 		}
197*b1446bdaSMark Brown 
198*b1446bdaSMark Brown 		err = snd_ctl_poll_descriptors_count(card_data->handle);
199*b1446bdaSMark Brown 		if (err != 1) {
200*b1446bdaSMark Brown 			ksft_exit_fail_msg("Unexpected desciptor count %d for card %d\n",
201*b1446bdaSMark Brown 					   err, card);
202*b1446bdaSMark Brown 		}
203*b1446bdaSMark Brown 
204*b1446bdaSMark Brown 		err = snd_ctl_poll_descriptors(card_data->handle,
205*b1446bdaSMark Brown 					       &card_data->pollfd, 1);
206*b1446bdaSMark Brown 		if (err != 1) {
207*b1446bdaSMark Brown 			ksft_exit_fail_msg("snd_ctl_poll_descriptors() failed for %d\n",
208*b1446bdaSMark Brown 				       card, err);
209*b1446bdaSMark Brown 		}
210*b1446bdaSMark Brown 
2115aaf9effSMark Brown 	next_card:
2125aaf9effSMark Brown 		if (snd_card_next(&card) < 0) {
2135aaf9effSMark Brown 			ksft_print_msg("snd_card_next");
2145aaf9effSMark Brown 			break;
2155aaf9effSMark Brown 		}
2165aaf9effSMark Brown 	}
217b73dad80SJaroslav Kysela 
218b73dad80SJaroslav Kysela 	snd_config_delete(config);
2195aaf9effSMark Brown }
2205aaf9effSMark Brown 
221*b1446bdaSMark Brown /*
222*b1446bdaSMark Brown  * Block for up to timeout ms for an event, returns a negative value
223*b1446bdaSMark Brown  * on error, 0 for no event and 1 for an event.
224*b1446bdaSMark Brown  */
225*b1446bdaSMark Brown int wait_for_event(struct ctl_data *ctl, int timeout)
226*b1446bdaSMark Brown {
227*b1446bdaSMark Brown 	unsigned short revents;
228*b1446bdaSMark Brown 	snd_ctl_event_t *event;
229*b1446bdaSMark Brown 	int count, err;
230*b1446bdaSMark Brown 	unsigned int mask = 0;
231*b1446bdaSMark Brown 	unsigned int ev_id;
232*b1446bdaSMark Brown 
233*b1446bdaSMark Brown 	snd_ctl_event_alloca(&event);
234*b1446bdaSMark Brown 
235*b1446bdaSMark Brown 	do {
236*b1446bdaSMark Brown 		err = poll(&(ctl->card->pollfd), 1, timeout);
237*b1446bdaSMark Brown 		if (err < 0) {
238*b1446bdaSMark Brown 			ksft_print_msg("poll() failed for %s: %s (%d)\n",
239*b1446bdaSMark Brown 				       ctl->name, strerror(errno), errno);
240*b1446bdaSMark Brown 			return -1;
241*b1446bdaSMark Brown 		}
242*b1446bdaSMark Brown 		/* Timeout */
243*b1446bdaSMark Brown 		if (err == 0)
244*b1446bdaSMark Brown 			return 0;
245*b1446bdaSMark Brown 
246*b1446bdaSMark Brown 		err = snd_ctl_poll_descriptors_revents(ctl->card->handle,
247*b1446bdaSMark Brown 						       &(ctl->card->pollfd),
248*b1446bdaSMark Brown 						       1, &revents);
249*b1446bdaSMark Brown 		if (err < 0) {
250*b1446bdaSMark Brown 			ksft_print_msg("snd_ctl_poll_desciptors_revents() failed for %s: %d\n",
251*b1446bdaSMark Brown 				       ctl->name, err);
252*b1446bdaSMark Brown 			return err;
253*b1446bdaSMark Brown 		}
254*b1446bdaSMark Brown 		if (revents & POLLERR) {
255*b1446bdaSMark Brown 			ksft_print_msg("snd_ctl_poll_desciptors_revents() reported POLLERR for %s\n",
256*b1446bdaSMark Brown 				       ctl->name);
257*b1446bdaSMark Brown 			return -1;
258*b1446bdaSMark Brown 		}
259*b1446bdaSMark Brown 		/* No read events */
260*b1446bdaSMark Brown 		if (!(revents & POLLIN)) {
261*b1446bdaSMark Brown 			ksft_print_msg("No POLLIN\n");
262*b1446bdaSMark Brown 			continue;
263*b1446bdaSMark Brown 		}
264*b1446bdaSMark Brown 
265*b1446bdaSMark Brown 		err = snd_ctl_read(ctl->card->handle, event);
266*b1446bdaSMark Brown 		if (err < 0) {
267*b1446bdaSMark Brown 			ksft_print_msg("snd_ctl_read() failed for %s: %d\n",
268*b1446bdaSMark Brown 			       ctl->name, err);
269*b1446bdaSMark Brown 			return err;
270*b1446bdaSMark Brown 		}
271*b1446bdaSMark Brown 
272*b1446bdaSMark Brown 		if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM)
273*b1446bdaSMark Brown 			continue;
274*b1446bdaSMark Brown 
275*b1446bdaSMark Brown 		/* The ID returned from the event is 1 less than numid */
276*b1446bdaSMark Brown 		mask = snd_ctl_event_elem_get_mask(event);
277*b1446bdaSMark Brown 		ev_id = snd_ctl_event_elem_get_numid(event);
278*b1446bdaSMark Brown 		if (ev_id != snd_ctl_elem_info_get_numid(ctl->info)) {
279*b1446bdaSMark Brown 			ksft_print_msg("Event for unexpected ctl %s\n",
280*b1446bdaSMark Brown 				       snd_ctl_event_elem_get_name(event));
281*b1446bdaSMark Brown 			continue;
282*b1446bdaSMark Brown 		}
283*b1446bdaSMark Brown 
284*b1446bdaSMark Brown 		if ((mask & SND_CTL_EVENT_MASK_REMOVE) == SND_CTL_EVENT_MASK_REMOVE) {
285*b1446bdaSMark Brown 			ksft_print_msg("Removal event for %s\n",
286*b1446bdaSMark Brown 				       ctl->name);
287*b1446bdaSMark Brown 			return -1;
288*b1446bdaSMark Brown 		}
289*b1446bdaSMark Brown 	} while ((mask & SND_CTL_EVENT_MASK_VALUE) != SND_CTL_EVENT_MASK_VALUE);
290*b1446bdaSMark Brown 
291*b1446bdaSMark Brown 	return 1;
292*b1446bdaSMark Brown }
293*b1446bdaSMark Brown 
2943f48b137SMark Brown bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val,
2953f48b137SMark Brown 			   int index)
2963f48b137SMark Brown {
2973f48b137SMark Brown 	long int_val;
2983f48b137SMark Brown 	long long int64_val;
2993f48b137SMark Brown 
3003f48b137SMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
3013f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_NONE:
3023f48b137SMark Brown 		ksft_print_msg("%s.%d Invalid control type NONE\n",
3033f48b137SMark Brown 			       ctl->name, index);
3043f48b137SMark Brown 		return false;
3053f48b137SMark Brown 
3063f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
3073f48b137SMark Brown 		int_val = snd_ctl_elem_value_get_boolean(val, index);
3083f48b137SMark Brown 		switch (int_val) {
3093f48b137SMark Brown 		case 0:
3103f48b137SMark Brown 		case 1:
3113f48b137SMark Brown 			break;
3123f48b137SMark Brown 		default:
3133f48b137SMark Brown 			ksft_print_msg("%s.%d Invalid boolean value %ld\n",
3143f48b137SMark Brown 				       ctl->name, index, int_val);
3153f48b137SMark Brown 			return false;
3163f48b137SMark Brown 		}
3173f48b137SMark Brown 		break;
3183f48b137SMark Brown 
3193f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
3203f48b137SMark Brown 		int_val = snd_ctl_elem_value_get_integer(val, index);
3213f48b137SMark Brown 
3223f48b137SMark Brown 		if (int_val < snd_ctl_elem_info_get_min(ctl->info)) {
3233f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld less than minimum %ld\n",
3243f48b137SMark Brown 				       ctl->name, index, int_val,
3253f48b137SMark Brown 				       snd_ctl_elem_info_get_min(ctl->info));
3263f48b137SMark Brown 			return false;
3273f48b137SMark Brown 		}
3283f48b137SMark Brown 
3293f48b137SMark Brown 		if (int_val > snd_ctl_elem_info_get_max(ctl->info)) {
3303f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld more than maximum %ld\n",
3313f48b137SMark Brown 				       ctl->name, index, int_val,
3323f48b137SMark Brown 				       snd_ctl_elem_info_get_max(ctl->info));
3333f48b137SMark Brown 			return false;
3343f48b137SMark Brown 		}
3353f48b137SMark Brown 
3363f48b137SMark Brown 		/* Only check step size if there is one and we're in bounds */
3373f48b137SMark Brown 		if (snd_ctl_elem_info_get_step(ctl->info) &&
3383f48b137SMark Brown 		    (int_val - snd_ctl_elem_info_get_min(ctl->info) %
3393f48b137SMark Brown 		     snd_ctl_elem_info_get_step(ctl->info))) {
3403f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld invalid for step %ld minimum %ld\n",
3413f48b137SMark Brown 				       ctl->name, index, int_val,
3423f48b137SMark Brown 				       snd_ctl_elem_info_get_step(ctl->info),
3433f48b137SMark Brown 				       snd_ctl_elem_info_get_min(ctl->info));
3443f48b137SMark Brown 			return false;
3453f48b137SMark Brown 		}
3463f48b137SMark Brown 		break;
3473f48b137SMark Brown 
3483f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
3493f48b137SMark Brown 		int64_val = snd_ctl_elem_value_get_integer64(val, index);
3503f48b137SMark Brown 
3513f48b137SMark Brown 		if (int64_val < snd_ctl_elem_info_get_min64(ctl->info)) {
3523f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld less than minimum %lld\n",
3533f48b137SMark Brown 				       ctl->name, index, int64_val,
3543f48b137SMark Brown 				       snd_ctl_elem_info_get_min64(ctl->info));
3553f48b137SMark Brown 			return false;
3563f48b137SMark Brown 		}
3573f48b137SMark Brown 
3583f48b137SMark Brown 		if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) {
3593f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld more than maximum %lld\n",
3603f48b137SMark Brown 				       ctl->name, index, int64_val,
3613f48b137SMark Brown 				       snd_ctl_elem_info_get_max(ctl->info));
3623f48b137SMark Brown 			return false;
3633f48b137SMark Brown 		}
3643f48b137SMark Brown 
3653f48b137SMark Brown 		/* Only check step size if there is one and we're in bounds */
3663f48b137SMark Brown 		if (snd_ctl_elem_info_get_step64(ctl->info) &&
3673f48b137SMark Brown 		    (int64_val - snd_ctl_elem_info_get_min64(ctl->info)) %
3683f48b137SMark Brown 		    snd_ctl_elem_info_get_step64(ctl->info)) {
3693f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld invalid for step %lld minimum %lld\n",
3703f48b137SMark Brown 				       ctl->name, index, int64_val,
3713f48b137SMark Brown 				       snd_ctl_elem_info_get_step64(ctl->info),
3723f48b137SMark Brown 				       snd_ctl_elem_info_get_min64(ctl->info));
3733f48b137SMark Brown 			return false;
3743f48b137SMark Brown 		}
3753f48b137SMark Brown 		break;
3763f48b137SMark Brown 
37710f2f194SMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
37810f2f194SMark Brown 		int_val = snd_ctl_elem_value_get_enumerated(val, index);
37910f2f194SMark Brown 
38010f2f194SMark Brown 		if (int_val < 0) {
38110f2f194SMark Brown 			ksft_print_msg("%s.%d negative value %ld for enumeration\n",
38210f2f194SMark Brown 				       ctl->name, index, int_val);
38310f2f194SMark Brown 			return false;
38410f2f194SMark Brown 		}
38510f2f194SMark Brown 
38610f2f194SMark Brown 		if (int_val >= snd_ctl_elem_info_get_items(ctl->info)) {
38710f2f194SMark Brown 			ksft_print_msg("%s.%d value %ld more than item count %ld\n",
38810f2f194SMark Brown 				       ctl->name, index, int_val,
38910f2f194SMark Brown 				       snd_ctl_elem_info_get_items(ctl->info));
39010f2f194SMark Brown 			return false;
39110f2f194SMark Brown 		}
39210f2f194SMark Brown 		break;
39310f2f194SMark Brown 
3943f48b137SMark Brown 	default:
3953f48b137SMark Brown 		/* No tests for other types */
3963f48b137SMark Brown 		break;
3973f48b137SMark Brown 	}
3983f48b137SMark Brown 
3993f48b137SMark Brown 	return true;
4003f48b137SMark Brown }
4013f48b137SMark Brown 
4023f48b137SMark Brown /*
4033f48b137SMark Brown  * Check that the provided value meets the constraints for the
4043f48b137SMark Brown  * provided control.
4053f48b137SMark Brown  */
4063f48b137SMark Brown bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val)
4073f48b137SMark Brown {
4083f48b137SMark Brown 	int i;
4093f48b137SMark Brown 	bool valid = true;
4103f48b137SMark Brown 
4113f48b137SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++)
4123f48b137SMark Brown 		if (!ctl_value_index_valid(ctl, val, i))
4133f48b137SMark Brown 			valid = false;
4143f48b137SMark Brown 
4153f48b137SMark Brown 	return valid;
4163f48b137SMark Brown }
4173f48b137SMark Brown 
4185aaf9effSMark Brown /*
4195aaf9effSMark Brown  * Check that we can read the default value and it is valid. Write
4205aaf9effSMark Brown  * tests use the read value to restore the default.
4215aaf9effSMark Brown  */
4225aaf9effSMark Brown void test_ctl_get_value(struct ctl_data *ctl)
4235aaf9effSMark Brown {
4245aaf9effSMark Brown 	int err;
4255aaf9effSMark Brown 
4265aaf9effSMark Brown 	/* If the control is turned off let's be polite */
4275aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
4285aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
4295aaf9effSMark Brown 		ksft_test_result_skip("get_value.%d.%d\n",
4305aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
4315aaf9effSMark Brown 		return;
4325aaf9effSMark Brown 	}
4335aaf9effSMark Brown 
4345aaf9effSMark Brown 	/* Can't test reading on an unreadable control */
4355aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info)) {
4365aaf9effSMark Brown 		ksft_print_msg("%s is not readable\n", ctl->name);
4375aaf9effSMark Brown 		ksft_test_result_skip("get_value.%d.%d\n",
4385aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
4395aaf9effSMark Brown 		return;
4405aaf9effSMark Brown 	}
4415aaf9effSMark Brown 
4425aaf9effSMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val);
4435aaf9effSMark Brown 	if (err < 0) {
4445aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
4455aaf9effSMark Brown 			       snd_strerror(err));
4465aaf9effSMark Brown 		goto out;
4475aaf9effSMark Brown 	}
4485aaf9effSMark Brown 
4493f48b137SMark Brown 	if (!ctl_value_valid(ctl, ctl->def_val))
4503f48b137SMark Brown 		err = -EINVAL;
4515aaf9effSMark Brown 
4525aaf9effSMark Brown out:
4535aaf9effSMark Brown 	ksft_test_result(err >= 0, "get_value.%d.%d\n",
4545aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
4555aaf9effSMark Brown }
4565aaf9effSMark Brown 
4575aaf9effSMark Brown bool show_mismatch(struct ctl_data *ctl, int index,
4585aaf9effSMark Brown 		   snd_ctl_elem_value_t *read_val,
4595aaf9effSMark Brown 		   snd_ctl_elem_value_t *expected_val)
4605aaf9effSMark Brown {
4615aaf9effSMark Brown 	long long expected_int, read_int;
4625aaf9effSMark Brown 
4635aaf9effSMark Brown 	/*
4645aaf9effSMark Brown 	 * We factor out the code to compare values representable as
4655aaf9effSMark Brown 	 * integers, ensure that check doesn't log otherwise.
4665aaf9effSMark Brown 	 */
4675aaf9effSMark Brown 	expected_int = 0;
4685aaf9effSMark Brown 	read_int = 0;
4695aaf9effSMark Brown 
4705aaf9effSMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
4715aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
4725aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_boolean(expected_val,
4735aaf9effSMark Brown 							      index);
4745aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_boolean(read_val, index);
4755aaf9effSMark Brown 		break;
4765aaf9effSMark Brown 
4775aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
4785aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_integer(expected_val,
4795aaf9effSMark Brown 							      index);
4805aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_integer(read_val, index);
4815aaf9effSMark Brown 		break;
4825aaf9effSMark Brown 
4835aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
4845aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_integer64(expected_val,
4855aaf9effSMark Brown 								index);
4865aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_integer64(read_val,
4875aaf9effSMark Brown 							    index);
4885aaf9effSMark Brown 		break;
4895aaf9effSMark Brown 
4905aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
4915aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_enumerated(expected_val,
4925aaf9effSMark Brown 								 index);
4935aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_enumerated(read_val,
4945aaf9effSMark Brown 							     index);
4955aaf9effSMark Brown 		break;
4965aaf9effSMark Brown 
4975aaf9effSMark Brown 	default:
4985aaf9effSMark Brown 		break;
4995aaf9effSMark Brown 	}
5005aaf9effSMark Brown 
5015aaf9effSMark Brown 	if (expected_int != read_int) {
5027cc994f2STakashi Sakamoto 		/*
5037cc994f2STakashi Sakamoto 		 * NOTE: The volatile attribute means that the hardware
5047cc994f2STakashi Sakamoto 		 * can voluntarily change the state of control element
5057cc994f2STakashi Sakamoto 		 * independent of any operation by software.
5067cc994f2STakashi Sakamoto 		 */
5077cc994f2STakashi Sakamoto 		bool is_volatile = snd_ctl_elem_info_is_volatile(ctl->info);
5087cc994f2STakashi Sakamoto 		ksft_print_msg("%s.%d expected %lld but read %lld, is_volatile %d\n",
5097cc994f2STakashi Sakamoto 			       ctl->name, index, expected_int, read_int, is_volatile);
5107cc994f2STakashi Sakamoto 		return !is_volatile;
5115aaf9effSMark Brown 	} else {
5125aaf9effSMark Brown 		return false;
5135aaf9effSMark Brown 	}
5145aaf9effSMark Brown }
5155aaf9effSMark Brown 
5165aaf9effSMark Brown /*
5175aaf9effSMark Brown  * Write a value then if possible verify that we get the expected
5185aaf9effSMark Brown  * result.  An optional expected value can be provided if we expect
5195aaf9effSMark Brown  * the write to fail, for verifying that invalid writes don't corrupt
5205aaf9effSMark Brown  * anything.
5215aaf9effSMark Brown  */
5225aaf9effSMark Brown int write_and_verify(struct ctl_data *ctl,
5235aaf9effSMark Brown 		     snd_ctl_elem_value_t *write_val,
5245aaf9effSMark Brown 		     snd_ctl_elem_value_t *expected_val)
5255aaf9effSMark Brown {
5265aaf9effSMark Brown 	int err, i;
5275aaf9effSMark Brown 	bool error_expected, mismatch_shown;
528*b1446bdaSMark Brown 	snd_ctl_elem_value_t *initial_val, *read_val, *w_val;
529*b1446bdaSMark Brown 	snd_ctl_elem_value_alloca(&initial_val);
5305aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&read_val);
5315aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&w_val);
5325aaf9effSMark Brown 
5335aaf9effSMark Brown 	/*
5345aaf9effSMark Brown 	 * We need to copy the write value since writing can modify
5355aaf9effSMark Brown 	 * the value which causes surprises, and allocate an expected
5365aaf9effSMark Brown 	 * value if we expect to read back what we wrote.
5375aaf9effSMark Brown 	 */
5385aaf9effSMark Brown 	snd_ctl_elem_value_copy(w_val, write_val);
5395aaf9effSMark Brown 	if (expected_val) {
5405aaf9effSMark Brown 		error_expected = true;
5415aaf9effSMark Brown 	} else {
5425aaf9effSMark Brown 		error_expected = false;
5435aaf9effSMark Brown 		snd_ctl_elem_value_alloca(&expected_val);
5445aaf9effSMark Brown 		snd_ctl_elem_value_copy(expected_val, write_val);
5455aaf9effSMark Brown 	}
5465aaf9effSMark Brown 
547*b1446bdaSMark Brown 	/* Store the value before we write */
548*b1446bdaSMark Brown 	if (snd_ctl_elem_info_is_readable(ctl->info)) {
549*b1446bdaSMark Brown 		snd_ctl_elem_value_set_id(initial_val, ctl->id);
550*b1446bdaSMark Brown 
551*b1446bdaSMark Brown 		err = snd_ctl_elem_read(ctl->card->handle, initial_val);
552*b1446bdaSMark Brown 		if (err < 0) {
553*b1446bdaSMark Brown 			ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
554*b1446bdaSMark Brown 				       snd_strerror(err));
555*b1446bdaSMark Brown 			return err;
556*b1446bdaSMark Brown 		}
557*b1446bdaSMark Brown 	}
558*b1446bdaSMark Brown 
5595aaf9effSMark Brown 	/*
5605aaf9effSMark Brown 	 * Do the write, if we have an expected value ignore the error
5615aaf9effSMark Brown 	 * and carry on to validate the expected value.
5625aaf9effSMark Brown 	 */
5635aaf9effSMark Brown 	err = snd_ctl_elem_write(ctl->card->handle, w_val);
5645aaf9effSMark Brown 	if (err < 0 && !error_expected) {
5655aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_write() failed: %s\n",
5665aaf9effSMark Brown 			       snd_strerror(err));
5675aaf9effSMark Brown 		return err;
5685aaf9effSMark Brown 	}
5695aaf9effSMark Brown 
5705aaf9effSMark Brown 	/* Can we do the verification part? */
5715aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info))
5725aaf9effSMark Brown 		return err;
5735aaf9effSMark Brown 
5745aaf9effSMark Brown 	snd_ctl_elem_value_set_id(read_val, ctl->id);
5755aaf9effSMark Brown 
5765aaf9effSMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, read_val);
5775aaf9effSMark Brown 	if (err < 0) {
5785aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
5795aaf9effSMark Brown 			       snd_strerror(err));
5805aaf9effSMark Brown 		return err;
5815aaf9effSMark Brown 	}
5825aaf9effSMark Brown 
5835aaf9effSMark Brown 	/*
584*b1446bdaSMark Brown 	 * Check for an event if the value changed, or confirm that
585*b1446bdaSMark Brown 	 * there was none if it didn't.  We rely on the kernel
586*b1446bdaSMark Brown 	 * generating the notification before it returns from the
587*b1446bdaSMark Brown 	 * write, this is currently true, should that ever change this
588*b1446bdaSMark Brown 	 * will most likely break and need updating.
589*b1446bdaSMark Brown 	 */
590*b1446bdaSMark Brown 	if (!snd_ctl_elem_info_is_volatile(ctl->info)) {
591*b1446bdaSMark Brown 		err = wait_for_event(ctl, 0);
592*b1446bdaSMark Brown 		if (snd_ctl_elem_value_compare(initial_val, read_val)) {
593*b1446bdaSMark Brown 			if (err < 1) {
594*b1446bdaSMark Brown 				ksft_print_msg("No event generated for %s\n",
595*b1446bdaSMark Brown 					       ctl->name);
596*b1446bdaSMark Brown 				ctl->event_missing++;
597*b1446bdaSMark Brown 			}
598*b1446bdaSMark Brown 		} else {
599*b1446bdaSMark Brown 			if (err != 0) {
600*b1446bdaSMark Brown 				ksft_print_msg("Spurious event generated for %s\n",
601*b1446bdaSMark Brown 					       ctl->name);
602*b1446bdaSMark Brown 				ctl->event_spurious++;
603*b1446bdaSMark Brown 			}
604*b1446bdaSMark Brown 		}
605*b1446bdaSMark Brown 	}
606*b1446bdaSMark Brown 
607*b1446bdaSMark Brown 	/*
6085aaf9effSMark Brown 	 * Use the libray to compare values, if there's a mismatch
6095aaf9effSMark Brown 	 * carry on and try to provide a more useful diagnostic than
6105aaf9effSMark Brown 	 * just "mismatch".
6115aaf9effSMark Brown 	 */
6125aaf9effSMark Brown 	if (!snd_ctl_elem_value_compare(expected_val, read_val))
6135aaf9effSMark Brown 		return 0;
6145aaf9effSMark Brown 
6155aaf9effSMark Brown 	mismatch_shown = false;
6165aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++)
6175aaf9effSMark Brown 		if (show_mismatch(ctl, i, read_val, expected_val))
6185aaf9effSMark Brown 			mismatch_shown = true;
6195aaf9effSMark Brown 
6205aaf9effSMark Brown 	if (!mismatch_shown)
6215aaf9effSMark Brown 		ksft_print_msg("%s read and written values differ\n",
6225aaf9effSMark Brown 			       ctl->name);
6235aaf9effSMark Brown 
6245aaf9effSMark Brown 	return -1;
6255aaf9effSMark Brown }
6265aaf9effSMark Brown 
6275aaf9effSMark Brown /*
6285aaf9effSMark Brown  * Make sure we can write the default value back to the control, this
6295aaf9effSMark Brown  * should validate that at least some write works.
6305aaf9effSMark Brown  */
6315aaf9effSMark Brown void test_ctl_write_default(struct ctl_data *ctl)
6325aaf9effSMark Brown {
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_default.%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_default.%d.%d\n",
6465aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
6475aaf9effSMark Brown 		return;
6485aaf9effSMark Brown 	}
6495aaf9effSMark Brown 
6505aaf9effSMark Brown 	/* No idea what the default was for unreadable controls */
6515aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info)) {
6525aaf9effSMark Brown 		ksft_print_msg("%s couldn't read default\n", ctl->name);
6535aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
6545aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
6555aaf9effSMark Brown 		return;
6565aaf9effSMark Brown 	}
6575aaf9effSMark Brown 
6585aaf9effSMark Brown 	err = write_and_verify(ctl, ctl->def_val, NULL);
6595aaf9effSMark Brown 
6605aaf9effSMark Brown 	ksft_test_result(err >= 0, "write_default.%d.%d\n",
6615aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
6625aaf9effSMark Brown }
6635aaf9effSMark Brown 
6645aaf9effSMark Brown bool test_ctl_write_valid_boolean(struct ctl_data *ctl)
6655aaf9effSMark Brown {
6665aaf9effSMark Brown 	int err, i, j;
6675aaf9effSMark Brown 	bool fail = false;
6685aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
6695aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
6705aaf9effSMark Brown 
6715aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
6725aaf9effSMark Brown 
6735aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
6745aaf9effSMark Brown 		for (j = 0; j < 2; j++) {
6755aaf9effSMark Brown 			snd_ctl_elem_value_set_boolean(val, i, j);
6765aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
6775aaf9effSMark Brown 			if (err != 0)
6785aaf9effSMark Brown 				fail = true;
6795aaf9effSMark Brown 		}
6805aaf9effSMark Brown 	}
6815aaf9effSMark Brown 
6825aaf9effSMark Brown 	return !fail;
6835aaf9effSMark Brown }
6845aaf9effSMark Brown 
6855aaf9effSMark Brown bool test_ctl_write_valid_integer(struct ctl_data *ctl)
6865aaf9effSMark Brown {
6875aaf9effSMark Brown 	int err;
6885aaf9effSMark Brown 	int i;
6895aaf9effSMark Brown 	long j, step;
6905aaf9effSMark Brown 	bool fail = false;
6915aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
6925aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
6935aaf9effSMark Brown 
6945aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
6955aaf9effSMark Brown 
6965aaf9effSMark Brown 	step = snd_ctl_elem_info_get_step(ctl->info);
6975aaf9effSMark Brown 	if (!step)
6985aaf9effSMark Brown 		step = 1;
6995aaf9effSMark Brown 
7005aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
7015aaf9effSMark Brown 		for (j = snd_ctl_elem_info_get_min(ctl->info);
7025aaf9effSMark Brown 		     j <= snd_ctl_elem_info_get_max(ctl->info); j += step) {
7035aaf9effSMark Brown 
7045aaf9effSMark Brown 			snd_ctl_elem_value_set_integer(val, i, j);
7055aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
7065aaf9effSMark Brown 			if (err != 0)
7075aaf9effSMark Brown 				fail = true;
7085aaf9effSMark Brown 		}
7095aaf9effSMark Brown 	}
7105aaf9effSMark Brown 
7115aaf9effSMark Brown 
7125aaf9effSMark Brown 	return !fail;
7135aaf9effSMark Brown }
7145aaf9effSMark Brown 
7155aaf9effSMark Brown bool test_ctl_write_valid_integer64(struct ctl_data *ctl)
7165aaf9effSMark Brown {
7175aaf9effSMark Brown 	int err, i;
7185aaf9effSMark Brown 	long long j, step;
7195aaf9effSMark Brown 	bool fail = false;
7205aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
7215aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
7225aaf9effSMark Brown 
7235aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
7245aaf9effSMark Brown 
7255aaf9effSMark Brown 	step = snd_ctl_elem_info_get_step64(ctl->info);
7265aaf9effSMark Brown 	if (!step)
7275aaf9effSMark Brown 		step = 1;
7285aaf9effSMark Brown 
7295aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
7305aaf9effSMark Brown 		for (j = snd_ctl_elem_info_get_min64(ctl->info);
7315aaf9effSMark Brown 		     j <= snd_ctl_elem_info_get_max64(ctl->info); j += step) {
7325aaf9effSMark Brown 
7335aaf9effSMark Brown 			snd_ctl_elem_value_set_integer64(val, i, j);
7345aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
7355aaf9effSMark Brown 			if (err != 0)
7365aaf9effSMark Brown 				fail = true;
7375aaf9effSMark Brown 		}
7385aaf9effSMark Brown 	}
7395aaf9effSMark Brown 
7405aaf9effSMark Brown 	return !fail;
7415aaf9effSMark Brown }
7425aaf9effSMark Brown 
7435aaf9effSMark Brown bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
7445aaf9effSMark Brown {
7455aaf9effSMark Brown 	int err, i, j;
7465aaf9effSMark Brown 	bool fail = false;
7475aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
7485aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
7495aaf9effSMark Brown 
7505aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
7515aaf9effSMark Brown 
7525aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
7535aaf9effSMark Brown 		for (j = 0; j < snd_ctl_elem_info_get_items(ctl->info); j++) {
7545aaf9effSMark Brown 			snd_ctl_elem_value_set_enumerated(val, i, j);
7555aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
7565aaf9effSMark Brown 			if (err != 0)
7575aaf9effSMark Brown 				fail = true;
7585aaf9effSMark Brown 		}
7595aaf9effSMark Brown 	}
7605aaf9effSMark Brown 
7615aaf9effSMark Brown 	return !fail;
7625aaf9effSMark Brown }
7635aaf9effSMark Brown 
7645aaf9effSMark Brown void test_ctl_write_valid(struct ctl_data *ctl)
7655aaf9effSMark Brown {
7665aaf9effSMark Brown 	bool pass;
7675aaf9effSMark Brown 	int err;
7685aaf9effSMark Brown 
7695aaf9effSMark Brown 	/* If the control is turned off let's be polite */
7705aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
7715aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
7725aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
7735aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
7745aaf9effSMark Brown 		return;
7755aaf9effSMark Brown 	}
7765aaf9effSMark Brown 
7775aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
7785aaf9effSMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
7795aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
7805aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
7815aaf9effSMark Brown 		return;
7825aaf9effSMark Brown 	}
7835aaf9effSMark Brown 
7845aaf9effSMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
7855aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
7865aaf9effSMark Brown 		pass = test_ctl_write_valid_boolean(ctl);
7875aaf9effSMark Brown 		break;
7885aaf9effSMark Brown 
7895aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
7905aaf9effSMark Brown 		pass = test_ctl_write_valid_integer(ctl);
7915aaf9effSMark Brown 		break;
7925aaf9effSMark Brown 
7935aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
7945aaf9effSMark Brown 		pass = test_ctl_write_valid_integer64(ctl);
7955aaf9effSMark Brown 		break;
7965aaf9effSMark Brown 
7975aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
7985aaf9effSMark Brown 		pass = test_ctl_write_valid_enumerated(ctl);
7995aaf9effSMark Brown 		break;
8005aaf9effSMark Brown 
8015aaf9effSMark Brown 	default:
8025aaf9effSMark Brown 		/* No tests for this yet */
8035aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
8045aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
8055aaf9effSMark Brown 		return;
8065aaf9effSMark Brown 	}
8075aaf9effSMark Brown 
8085aaf9effSMark Brown 	/* Restore the default value to minimise disruption */
8095aaf9effSMark Brown 	err = write_and_verify(ctl, ctl->def_val, NULL);
8105aaf9effSMark Brown 	if (err < 0)
8115aaf9effSMark Brown 		pass = false;
8125aaf9effSMark Brown 
8135aaf9effSMark Brown 	ksft_test_result(pass, "write_valid.%d.%d\n",
8145aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
8155aaf9effSMark Brown }
8165aaf9effSMark Brown 
81788b61322SMark Brown bool test_ctl_write_invalid_value(struct ctl_data *ctl,
81888b61322SMark Brown 				  snd_ctl_elem_value_t *val)
81988b61322SMark Brown {
82088b61322SMark Brown 	int err;
82188b61322SMark Brown 	long val_read;
82288b61322SMark Brown 
82388b61322SMark Brown 	/* Ideally this will fail... */
82488b61322SMark Brown 	err = snd_ctl_elem_write(ctl->card->handle, val);
82588b61322SMark Brown 	if (err < 0)
82688b61322SMark Brown 		return false;
82788b61322SMark Brown 
82888b61322SMark Brown 	/* ...but some devices will clamp to an in range value */
82988b61322SMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, val);
83088b61322SMark Brown 	if (err < 0) {
83188b61322SMark Brown 		ksft_print_msg("%s failed to read: %s\n",
83288b61322SMark Brown 			       ctl->name, snd_strerror(err));
83388b61322SMark Brown 		return true;
83488b61322SMark Brown 	}
83588b61322SMark Brown 
83688b61322SMark Brown 	return !ctl_value_valid(ctl, val);
83788b61322SMark Brown }
83888b61322SMark Brown 
83988b61322SMark Brown bool test_ctl_write_invalid_boolean(struct ctl_data *ctl)
84088b61322SMark Brown {
84188b61322SMark Brown 	int err, i;
84288b61322SMark Brown 	long val_read;
84388b61322SMark Brown 	bool fail = false;
84488b61322SMark Brown 	snd_ctl_elem_value_t *val;
84588b61322SMark Brown 	snd_ctl_elem_value_alloca(&val);
84688b61322SMark Brown 
84788b61322SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
84888b61322SMark Brown 		snd_ctl_elem_value_copy(val, ctl->def_val);
84988b61322SMark Brown 		snd_ctl_elem_value_set_boolean(val, i, 2);
85088b61322SMark Brown 
85188b61322SMark Brown 		if (test_ctl_write_invalid_value(ctl, val))
85288b61322SMark Brown 			fail = true;
85388b61322SMark Brown 	}
85488b61322SMark Brown 
85588b61322SMark Brown 	return !fail;
85688b61322SMark Brown }
85788b61322SMark Brown 
85888b61322SMark Brown bool test_ctl_write_invalid_integer(struct ctl_data *ctl)
85988b61322SMark Brown {
86088b61322SMark Brown 	int i;
86188b61322SMark Brown 	bool fail = false;
86288b61322SMark Brown 	snd_ctl_elem_value_t *val;
86388b61322SMark Brown 	snd_ctl_elem_value_alloca(&val);
86488b61322SMark Brown 
86588b61322SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
86688b61322SMark Brown 		if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) {
86788b61322SMark Brown 			/* Just under range */
86888b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
86988b61322SMark Brown 			snd_ctl_elem_value_set_integer(val, i,
87088b61322SMark Brown 			       snd_ctl_elem_info_get_min(ctl->info) - 1);
87188b61322SMark Brown 
87288b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
87388b61322SMark Brown 				fail = true;
87488b61322SMark Brown 
87588b61322SMark Brown 			/* Minimum representable value */
87688b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
87788b61322SMark Brown 			snd_ctl_elem_value_set_integer(val, i, LONG_MIN);
87888b61322SMark Brown 
87988b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
88088b61322SMark Brown 				fail = true;
88188b61322SMark Brown 		}
88288b61322SMark Brown 
88388b61322SMark Brown 		if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) {
88488b61322SMark Brown 			/* Just over range */
88588b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
88688b61322SMark Brown 			snd_ctl_elem_value_set_integer(val, i,
88788b61322SMark Brown 			       snd_ctl_elem_info_get_max(ctl->info) + 1);
88888b61322SMark Brown 
88988b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
89088b61322SMark Brown 				fail = true;
89188b61322SMark Brown 
89288b61322SMark Brown 			/* Maximum representable value */
89388b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
89488b61322SMark Brown 			snd_ctl_elem_value_set_integer(val, i, LONG_MAX);
89588b61322SMark Brown 
89688b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
89788b61322SMark Brown 				fail = true;
89888b61322SMark Brown 		}
89988b61322SMark Brown 	}
90088b61322SMark Brown 
90188b61322SMark Brown 	return !fail;
90288b61322SMark Brown }
90388b61322SMark Brown 
90488b61322SMark Brown bool test_ctl_write_invalid_integer64(struct ctl_data *ctl)
90588b61322SMark Brown {
90688b61322SMark Brown 	int i;
90788b61322SMark Brown 	bool fail = false;
90888b61322SMark Brown 	snd_ctl_elem_value_t *val;
90988b61322SMark Brown 	snd_ctl_elem_value_alloca(&val);
91088b61322SMark Brown 
91188b61322SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
91288b61322SMark Brown 		if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) {
91388b61322SMark Brown 			/* Just under range */
91488b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
91588b61322SMark Brown 			snd_ctl_elem_value_set_integer64(val, i,
91688b61322SMark Brown 				snd_ctl_elem_info_get_min64(ctl->info) - 1);
91788b61322SMark Brown 
91888b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
91988b61322SMark Brown 				fail = true;
92088b61322SMark Brown 
92188b61322SMark Brown 			/* Minimum representable value */
92288b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
92388b61322SMark Brown 			snd_ctl_elem_value_set_integer64(val, i, LLONG_MIN);
92488b61322SMark Brown 
92588b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
92688b61322SMark Brown 				fail = true;
92788b61322SMark Brown 		}
92888b61322SMark Brown 
92988b61322SMark Brown 		if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) {
93088b61322SMark Brown 			/* Just over range */
93188b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
93288b61322SMark Brown 			snd_ctl_elem_value_set_integer64(val, i,
93388b61322SMark Brown 				snd_ctl_elem_info_get_max64(ctl->info) + 1);
93488b61322SMark Brown 
93588b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
93688b61322SMark Brown 				fail = true;
93788b61322SMark Brown 
93888b61322SMark Brown 			/* Maximum representable value */
93988b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
94088b61322SMark Brown 			snd_ctl_elem_value_set_integer64(val, i, LLONG_MAX);
94188b61322SMark Brown 
94288b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
94388b61322SMark Brown 				fail = true;
94488b61322SMark Brown 		}
94588b61322SMark Brown 	}
94688b61322SMark Brown 
94788b61322SMark Brown 	return !fail;
94888b61322SMark Brown }
94988b61322SMark Brown 
95088b61322SMark Brown bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl)
95188b61322SMark Brown {
95288b61322SMark Brown 	int err, i;
95388b61322SMark Brown 	unsigned int val_read;
95488b61322SMark Brown 	bool fail = false;
95588b61322SMark Brown 	snd_ctl_elem_value_t *val;
95688b61322SMark Brown 	snd_ctl_elem_value_alloca(&val);
95788b61322SMark Brown 
95888b61322SMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
95988b61322SMark Brown 
96088b61322SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
96188b61322SMark Brown 		/* One beyond maximum */
96288b61322SMark Brown 		snd_ctl_elem_value_copy(val, ctl->def_val);
96388b61322SMark Brown 		snd_ctl_elem_value_set_enumerated(val, i,
96488b61322SMark Brown 				  snd_ctl_elem_info_get_items(ctl->info));
96588b61322SMark Brown 
96688b61322SMark Brown 		if (test_ctl_write_invalid_value(ctl, val))
96788b61322SMark Brown 			fail = true;
96888b61322SMark Brown 
96988b61322SMark Brown 		/* Maximum representable value */
97088b61322SMark Brown 		snd_ctl_elem_value_copy(val, ctl->def_val);
97188b61322SMark Brown 		snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX);
97288b61322SMark Brown 
97388b61322SMark Brown 		if (test_ctl_write_invalid_value(ctl, val))
97488b61322SMark Brown 			fail = true;
97588b61322SMark Brown 
97688b61322SMark Brown 	}
97788b61322SMark Brown 
97888b61322SMark Brown 	return !fail;
97988b61322SMark Brown }
98088b61322SMark Brown 
98188b61322SMark Brown 
98288b61322SMark Brown void test_ctl_write_invalid(struct ctl_data *ctl)
98388b61322SMark Brown {
98488b61322SMark Brown 	bool pass;
98588b61322SMark Brown 	int err;
98688b61322SMark Brown 
98788b61322SMark Brown 	/* If the control is turned off let's be polite */
98888b61322SMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
98988b61322SMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
99088b61322SMark Brown 		ksft_test_result_skip("write_invalid.%d.%d\n",
99188b61322SMark Brown 				      ctl->card->card, ctl->elem);
99288b61322SMark Brown 		return;
99388b61322SMark Brown 	}
99488b61322SMark Brown 
99588b61322SMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
99688b61322SMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
99788b61322SMark Brown 		ksft_test_result_skip("write_invalid.%d.%d\n",
99888b61322SMark Brown 				      ctl->card->card, ctl->elem);
99988b61322SMark Brown 		return;
100088b61322SMark Brown 	}
100188b61322SMark Brown 
100288b61322SMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
100388b61322SMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
100488b61322SMark Brown 		pass = test_ctl_write_invalid_boolean(ctl);
100588b61322SMark Brown 		break;
100688b61322SMark Brown 
100788b61322SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
100888b61322SMark Brown 		pass = test_ctl_write_invalid_integer(ctl);
100988b61322SMark Brown 		break;
101088b61322SMark Brown 
101188b61322SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
101288b61322SMark Brown 		pass = test_ctl_write_invalid_integer64(ctl);
101388b61322SMark Brown 		break;
101488b61322SMark Brown 
101588b61322SMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
101688b61322SMark Brown 		pass = test_ctl_write_invalid_enumerated(ctl);
101788b61322SMark Brown 		break;
101888b61322SMark Brown 
101988b61322SMark Brown 	default:
102088b61322SMark Brown 		/* No tests for this yet */
102188b61322SMark Brown 		ksft_test_result_skip("write_invalid.%d.%d\n",
102288b61322SMark Brown 				      ctl->card->card, ctl->elem);
102388b61322SMark Brown 		return;
102488b61322SMark Brown 	}
102588b61322SMark Brown 
102688b61322SMark Brown 	/* Restore the default value to minimise disruption */
102788b61322SMark Brown 	err = write_and_verify(ctl, ctl->def_val, NULL);
102888b61322SMark Brown 	if (err < 0)
102988b61322SMark Brown 		pass = false;
103088b61322SMark Brown 
103188b61322SMark Brown 	ksft_test_result(pass, "write_invalid.%d.%d\n",
103288b61322SMark Brown 			 ctl->card->card, ctl->elem);
103388b61322SMark Brown }
103488b61322SMark Brown 
1035*b1446bdaSMark Brown void test_ctl_event_missing(struct ctl_data *ctl)
1036*b1446bdaSMark Brown {
1037*b1446bdaSMark Brown 	ksft_test_result(!ctl->event_missing, "event_missing.%d.%d\n",
1038*b1446bdaSMark Brown 			 ctl->card->card, ctl->elem);
1039*b1446bdaSMark Brown }
1040*b1446bdaSMark Brown 
1041*b1446bdaSMark Brown void test_ctl_event_spurious(struct ctl_data *ctl)
1042*b1446bdaSMark Brown {
1043*b1446bdaSMark Brown 	ksft_test_result(!ctl->event_spurious, "event_spurious.%d.%d\n",
1044*b1446bdaSMark Brown 			 ctl->card->card, ctl->elem);
1045*b1446bdaSMark Brown }
1046*b1446bdaSMark Brown 
10475aaf9effSMark Brown int main(void)
10485aaf9effSMark Brown {
10495aaf9effSMark Brown 	struct ctl_data *ctl;
10505aaf9effSMark Brown 
10515aaf9effSMark Brown 	ksft_print_header();
10525aaf9effSMark Brown 
10535aaf9effSMark Brown 	find_controls();
10545aaf9effSMark Brown 
10555aaf9effSMark Brown 	ksft_set_plan(num_controls * TESTS_PER_CONTROL);
10565aaf9effSMark Brown 
10575aaf9effSMark Brown 	for (ctl = ctl_list; ctl != NULL; ctl = ctl->next) {
10585aaf9effSMark Brown 		/*
10595aaf9effSMark Brown 		 * Must test get_value() before we write anything, the
10605aaf9effSMark Brown 		 * test stores the default value for later cleanup.
10615aaf9effSMark Brown 		 */
10625aaf9effSMark Brown 		test_ctl_get_value(ctl);
10635aaf9effSMark Brown 		test_ctl_write_default(ctl);
10645aaf9effSMark Brown 		test_ctl_write_valid(ctl);
106588b61322SMark Brown 		test_ctl_write_invalid(ctl);
1066*b1446bdaSMark Brown 		test_ctl_event_missing(ctl);
1067*b1446bdaSMark Brown 		test_ctl_event_spurious(ctl);
10685aaf9effSMark Brown 	}
10695aaf9effSMark Brown 
10705aaf9effSMark Brown 	ksft_exit_pass();
10715aaf9effSMark Brown 
10725aaf9effSMark Brown 	return 0;
10735aaf9effSMark Brown }
1074