xref: /openbmc/linux/tools/testing/selftests/alsa/mixer-test.c (revision 1188f7f111c61394ec56beb8e30322305a8220b6)
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>
6b1446bdaSMark 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"
29b310092eSJaroslav Kysela #include "alsa-local.h"
305aaf9effSMark Brown 
31c92b576aSMark Brown #define TESTS_PER_CONTROL 7
325aaf9effSMark Brown 
335aaf9effSMark Brown struct card_data {
345aaf9effSMark Brown 	snd_ctl_t *handle;
355aaf9effSMark Brown 	int card;
36b1446bdaSMark Brown 	struct pollfd pollfd;
375aaf9effSMark Brown 	int num_ctls;
385aaf9effSMark Brown 	snd_ctl_elem_list_t *ctls;
395aaf9effSMark Brown 	struct card_data *next;
405aaf9effSMark Brown };
415aaf9effSMark Brown 
425aaf9effSMark Brown struct ctl_data {
435aaf9effSMark Brown 	const char *name;
445aaf9effSMark Brown 	snd_ctl_elem_id_t *id;
455aaf9effSMark Brown 	snd_ctl_elem_info_t *info;
465aaf9effSMark Brown 	snd_ctl_elem_value_t *def_val;
475aaf9effSMark Brown 	int elem;
48b1446bdaSMark Brown 	int event_missing;
49b1446bdaSMark Brown 	int event_spurious;
505aaf9effSMark Brown 	struct card_data *card;
515aaf9effSMark Brown 	struct ctl_data *next;
525aaf9effSMark Brown };
535aaf9effSMark Brown 
545aaf9effSMark Brown int num_cards = 0;
555aaf9effSMark Brown int num_controls = 0;
565aaf9effSMark Brown struct card_data *card_list = NULL;
575aaf9effSMark Brown struct ctl_data *ctl_list = NULL;
585aaf9effSMark Brown 
find_controls(void)599d73d192SMark Brown static void find_controls(void)
605aaf9effSMark Brown {
615aaf9effSMark Brown 	char name[32];
625aaf9effSMark Brown 	int card, ctl, err;
635aaf9effSMark Brown 	struct card_data *card_data;
645aaf9effSMark Brown 	struct ctl_data *ctl_data;
65b73dad80SJaroslav Kysela 	snd_config_t *config;
661a0cc052SMark Brown 	char *card_name, *card_longname;
675aaf9effSMark Brown 
685aaf9effSMark Brown 	card = -1;
695aaf9effSMark Brown 	if (snd_card_next(&card) < 0 || card < 0)
705aaf9effSMark Brown 		return;
715aaf9effSMark Brown 
72b310092eSJaroslav Kysela 	config = get_alsalib_config();
73b73dad80SJaroslav Kysela 
745aaf9effSMark Brown 	while (card >= 0) {
755aaf9effSMark Brown 		sprintf(name, "hw:%d", card);
765aaf9effSMark Brown 
775aaf9effSMark Brown 		card_data = malloc(sizeof(*card_data));
785aaf9effSMark Brown 		if (!card_data)
795aaf9effSMark Brown 			ksft_exit_fail_msg("Out of memory\n");
805aaf9effSMark Brown 
81b73dad80SJaroslav Kysela 		err = snd_ctl_open_lconf(&card_data->handle, name, 0, config);
825aaf9effSMark Brown 		if (err < 0) {
835aaf9effSMark Brown 			ksft_print_msg("Failed to get hctl for card %d: %s\n",
845aaf9effSMark Brown 				       card, snd_strerror(err));
855aaf9effSMark Brown 			goto next_card;
865aaf9effSMark Brown 		}
875aaf9effSMark Brown 
881a0cc052SMark Brown 		err = snd_card_get_name(card, &card_name);
891a0cc052SMark Brown 		if (err != 0)
901a0cc052SMark Brown 			card_name = "Unknown";
911a0cc052SMark Brown 		err = snd_card_get_longname(card, &card_longname);
921a0cc052SMark Brown 		if (err != 0)
931a0cc052SMark Brown 			card_longname = "Unknown";
941a0cc052SMark Brown 		ksft_print_msg("Card %d - %s (%s)\n", card,
951a0cc052SMark Brown 			       card_name, card_longname);
961a0cc052SMark Brown 
975aaf9effSMark Brown 		/* Count controls */
985aaf9effSMark Brown 		snd_ctl_elem_list_malloc(&card_data->ctls);
995aaf9effSMark Brown 		snd_ctl_elem_list(card_data->handle, card_data->ctls);
1005aaf9effSMark Brown 		card_data->num_ctls = snd_ctl_elem_list_get_count(card_data->ctls);
1015aaf9effSMark Brown 
1025aaf9effSMark Brown 		/* Enumerate control information */
1035aaf9effSMark Brown 		snd_ctl_elem_list_alloc_space(card_data->ctls, card_data->num_ctls);
1045aaf9effSMark Brown 		snd_ctl_elem_list(card_data->handle, card_data->ctls);
1055aaf9effSMark Brown 
1065aaf9effSMark Brown 		card_data->card = num_cards++;
1075aaf9effSMark Brown 		card_data->next = card_list;
1085aaf9effSMark Brown 		card_list = card_data;
1095aaf9effSMark Brown 
1105aaf9effSMark Brown 		num_controls += card_data->num_ctls;
1115aaf9effSMark Brown 
1125aaf9effSMark Brown 		for (ctl = 0; ctl < card_data->num_ctls; ctl++) {
1135aaf9effSMark Brown 			ctl_data = malloc(sizeof(*ctl_data));
1145aaf9effSMark Brown 			if (!ctl_data)
1155aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1165aaf9effSMark Brown 
117b1446bdaSMark Brown 			memset(ctl_data, 0, sizeof(*ctl_data));
1185aaf9effSMark Brown 			ctl_data->card = card_data;
1195aaf9effSMark Brown 			ctl_data->elem = ctl;
1205aaf9effSMark Brown 			ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls,
1215aaf9effSMark Brown 								    ctl);
1225aaf9effSMark Brown 
1235aaf9effSMark Brown 			err = snd_ctl_elem_id_malloc(&ctl_data->id);
1245aaf9effSMark Brown 			if (err < 0)
1255aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1265aaf9effSMark Brown 
1275aaf9effSMark Brown 			err = snd_ctl_elem_info_malloc(&ctl_data->info);
1285aaf9effSMark Brown 			if (err < 0)
1295aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1305aaf9effSMark Brown 
1315aaf9effSMark Brown 			err = snd_ctl_elem_value_malloc(&ctl_data->def_val);
1325aaf9effSMark Brown 			if (err < 0)
1335aaf9effSMark Brown 				ksft_exit_fail_msg("Out of memory\n");
1345aaf9effSMark Brown 
1355aaf9effSMark Brown 			snd_ctl_elem_list_get_id(card_data->ctls, ctl,
1365aaf9effSMark Brown 						 ctl_data->id);
1375aaf9effSMark Brown 			snd_ctl_elem_info_set_id(ctl_data->info, ctl_data->id);
1385aaf9effSMark Brown 			err = snd_ctl_elem_info(card_data->handle,
1395aaf9effSMark Brown 						ctl_data->info);
1405aaf9effSMark Brown 			if (err < 0) {
1413e0e4a90SGhanshyam Agrawal 				ksft_print_msg("%s getting info for %s\n",
1425aaf9effSMark Brown 					       snd_strerror(err),
1435aaf9effSMark Brown 					       ctl_data->name);
1445aaf9effSMark Brown 			}
1455aaf9effSMark Brown 
1465aaf9effSMark Brown 			snd_ctl_elem_value_set_id(ctl_data->def_val,
1475aaf9effSMark Brown 						  ctl_data->id);
1485aaf9effSMark Brown 
1495aaf9effSMark Brown 			ctl_data->next = ctl_list;
1505aaf9effSMark Brown 			ctl_list = ctl_data;
1515aaf9effSMark Brown 		}
1525aaf9effSMark Brown 
153b1446bdaSMark Brown 		/* Set up for events */
154b1446bdaSMark Brown 		err = snd_ctl_subscribe_events(card_data->handle, true);
155b1446bdaSMark Brown 		if (err < 0) {
156b1446bdaSMark Brown 			ksft_exit_fail_msg("snd_ctl_subscribe_events() failed for card %d: %d\n",
157b1446bdaSMark Brown 					   card, err);
158b1446bdaSMark Brown 		}
159b1446bdaSMark Brown 
160b1446bdaSMark Brown 		err = snd_ctl_poll_descriptors_count(card_data->handle);
161b1446bdaSMark Brown 		if (err != 1) {
1628f85b4daSColin Ian King 			ksft_exit_fail_msg("Unexpected descriptor count %d for card %d\n",
163b1446bdaSMark Brown 					   err, card);
164b1446bdaSMark Brown 		}
165b1446bdaSMark Brown 
166b1446bdaSMark Brown 		err = snd_ctl_poll_descriptors(card_data->handle,
167b1446bdaSMark Brown 					       &card_data->pollfd, 1);
168b1446bdaSMark Brown 		if (err != 1) {
16945cbaa25SMirsad Todorovac 			ksft_exit_fail_msg("snd_ctl_poll_descriptors() failed for card %d: %d\n",
170b1446bdaSMark Brown 				       card, err);
171b1446bdaSMark Brown 		}
172b1446bdaSMark Brown 
1735aaf9effSMark Brown 	next_card:
1745aaf9effSMark Brown 		if (snd_card_next(&card) < 0) {
1755aaf9effSMark Brown 			ksft_print_msg("snd_card_next");
1765aaf9effSMark Brown 			break;
1775aaf9effSMark Brown 		}
1785aaf9effSMark Brown 	}
179b73dad80SJaroslav Kysela 
180b73dad80SJaroslav Kysela 	snd_config_delete(config);
1815aaf9effSMark Brown }
1825aaf9effSMark Brown 
183b1446bdaSMark Brown /*
184b1446bdaSMark Brown  * Block for up to timeout ms for an event, returns a negative value
185b1446bdaSMark Brown  * on error, 0 for no event and 1 for an event.
186b1446bdaSMark Brown  */
wait_for_event(struct ctl_data * ctl,int timeout)1879d73d192SMark Brown static int wait_for_event(struct ctl_data *ctl, int timeout)
188b1446bdaSMark Brown {
189b1446bdaSMark Brown 	unsigned short revents;
190b1446bdaSMark Brown 	snd_ctl_event_t *event;
19161ba93b4SDing Xiang 	int err;
192b1446bdaSMark Brown 	unsigned int mask = 0;
193b1446bdaSMark Brown 	unsigned int ev_id;
194b1446bdaSMark Brown 
195b1446bdaSMark Brown 	snd_ctl_event_alloca(&event);
196b1446bdaSMark Brown 
197b1446bdaSMark Brown 	do {
198b1446bdaSMark Brown 		err = poll(&(ctl->card->pollfd), 1, timeout);
199b1446bdaSMark Brown 		if (err < 0) {
200b1446bdaSMark Brown 			ksft_print_msg("poll() failed for %s: %s (%d)\n",
201b1446bdaSMark Brown 				       ctl->name, strerror(errno), errno);
202b1446bdaSMark Brown 			return -1;
203b1446bdaSMark Brown 		}
204b1446bdaSMark Brown 		/* Timeout */
205b1446bdaSMark Brown 		if (err == 0)
206b1446bdaSMark Brown 			return 0;
207b1446bdaSMark Brown 
208b1446bdaSMark Brown 		err = snd_ctl_poll_descriptors_revents(ctl->card->handle,
209b1446bdaSMark Brown 						       &(ctl->card->pollfd),
210b1446bdaSMark Brown 						       1, &revents);
211b1446bdaSMark Brown 		if (err < 0) {
2128f85b4daSColin Ian King 			ksft_print_msg("snd_ctl_poll_descriptors_revents() failed for %s: %d\n",
213b1446bdaSMark Brown 				       ctl->name, err);
214b1446bdaSMark Brown 			return err;
215b1446bdaSMark Brown 		}
216b1446bdaSMark Brown 		if (revents & POLLERR) {
2178f85b4daSColin Ian King 			ksft_print_msg("snd_ctl_poll_descriptors_revents() reported POLLERR for %s\n",
218b1446bdaSMark Brown 				       ctl->name);
219b1446bdaSMark Brown 			return -1;
220b1446bdaSMark Brown 		}
221b1446bdaSMark Brown 		/* No read events */
222b1446bdaSMark Brown 		if (!(revents & POLLIN)) {
223b1446bdaSMark Brown 			ksft_print_msg("No POLLIN\n");
224b1446bdaSMark Brown 			continue;
225b1446bdaSMark Brown 		}
226b1446bdaSMark Brown 
227b1446bdaSMark Brown 		err = snd_ctl_read(ctl->card->handle, event);
228b1446bdaSMark Brown 		if (err < 0) {
229b1446bdaSMark Brown 			ksft_print_msg("snd_ctl_read() failed for %s: %d\n",
230b1446bdaSMark Brown 			       ctl->name, err);
231b1446bdaSMark Brown 			return err;
232b1446bdaSMark Brown 		}
233b1446bdaSMark Brown 
234b1446bdaSMark Brown 		if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM)
235b1446bdaSMark Brown 			continue;
236b1446bdaSMark Brown 
237b1446bdaSMark Brown 		/* The ID returned from the event is 1 less than numid */
238b1446bdaSMark Brown 		mask = snd_ctl_event_elem_get_mask(event);
239b1446bdaSMark Brown 		ev_id = snd_ctl_event_elem_get_numid(event);
240b1446bdaSMark Brown 		if (ev_id != snd_ctl_elem_info_get_numid(ctl->info)) {
241b1446bdaSMark Brown 			ksft_print_msg("Event for unexpected ctl %s\n",
242b1446bdaSMark Brown 				       snd_ctl_event_elem_get_name(event));
243b1446bdaSMark Brown 			continue;
244b1446bdaSMark Brown 		}
245b1446bdaSMark Brown 
246b1446bdaSMark Brown 		if ((mask & SND_CTL_EVENT_MASK_REMOVE) == SND_CTL_EVENT_MASK_REMOVE) {
247b1446bdaSMark Brown 			ksft_print_msg("Removal event for %s\n",
248b1446bdaSMark Brown 				       ctl->name);
249b1446bdaSMark Brown 			return -1;
250b1446bdaSMark Brown 		}
251b1446bdaSMark Brown 	} while ((mask & SND_CTL_EVENT_MASK_VALUE) != SND_CTL_EVENT_MASK_VALUE);
252b1446bdaSMark Brown 
253b1446bdaSMark Brown 	return 1;
254b1446bdaSMark Brown }
255b1446bdaSMark Brown 
ctl_value_index_valid(struct ctl_data * ctl,snd_ctl_elem_value_t * val,int index)2569d73d192SMark Brown static bool ctl_value_index_valid(struct ctl_data *ctl,
2579d73d192SMark Brown 				  snd_ctl_elem_value_t *val,
2583f48b137SMark Brown 				  int index)
2593f48b137SMark Brown {
2603f48b137SMark Brown 	long int_val;
2613f48b137SMark Brown 	long long int64_val;
2623f48b137SMark Brown 
2633f48b137SMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
2643f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_NONE:
2653f48b137SMark Brown 		ksft_print_msg("%s.%d Invalid control type NONE\n",
2663f48b137SMark Brown 			       ctl->name, index);
2673f48b137SMark Brown 		return false;
2683f48b137SMark Brown 
2693f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
2703f48b137SMark Brown 		int_val = snd_ctl_elem_value_get_boolean(val, index);
2713f48b137SMark Brown 		switch (int_val) {
2723f48b137SMark Brown 		case 0:
2733f48b137SMark Brown 		case 1:
2743f48b137SMark Brown 			break;
2753f48b137SMark Brown 		default:
2763f48b137SMark Brown 			ksft_print_msg("%s.%d Invalid boolean value %ld\n",
2773f48b137SMark Brown 				       ctl->name, index, int_val);
2783f48b137SMark Brown 			return false;
2793f48b137SMark Brown 		}
2803f48b137SMark Brown 		break;
2813f48b137SMark Brown 
2823f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
2833f48b137SMark Brown 		int_val = snd_ctl_elem_value_get_integer(val, index);
2843f48b137SMark Brown 
2853f48b137SMark Brown 		if (int_val < snd_ctl_elem_info_get_min(ctl->info)) {
2863f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld less than minimum %ld\n",
2873f48b137SMark Brown 				       ctl->name, index, int_val,
2883f48b137SMark Brown 				       snd_ctl_elem_info_get_min(ctl->info));
2893f48b137SMark Brown 			return false;
2903f48b137SMark Brown 		}
2913f48b137SMark Brown 
2923f48b137SMark Brown 		if (int_val > snd_ctl_elem_info_get_max(ctl->info)) {
2933f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld more than maximum %ld\n",
2943f48b137SMark Brown 				       ctl->name, index, int_val,
2953f48b137SMark Brown 				       snd_ctl_elem_info_get_max(ctl->info));
2963f48b137SMark Brown 			return false;
2973f48b137SMark Brown 		}
2983f48b137SMark Brown 
2993f48b137SMark Brown 		/* Only check step size if there is one and we're in bounds */
3003f48b137SMark Brown 		if (snd_ctl_elem_info_get_step(ctl->info) &&
3013f48b137SMark Brown 		    (int_val - snd_ctl_elem_info_get_min(ctl->info) %
3023f48b137SMark Brown 		     snd_ctl_elem_info_get_step(ctl->info))) {
3033f48b137SMark Brown 			ksft_print_msg("%s.%d value %ld invalid for step %ld minimum %ld\n",
3043f48b137SMark Brown 				       ctl->name, index, int_val,
3053f48b137SMark Brown 				       snd_ctl_elem_info_get_step(ctl->info),
3063f48b137SMark Brown 				       snd_ctl_elem_info_get_min(ctl->info));
3073f48b137SMark Brown 			return false;
3083f48b137SMark Brown 		}
3093f48b137SMark Brown 		break;
3103f48b137SMark Brown 
3113f48b137SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
3123f48b137SMark Brown 		int64_val = snd_ctl_elem_value_get_integer64(val, index);
3133f48b137SMark Brown 
3143f48b137SMark Brown 		if (int64_val < snd_ctl_elem_info_get_min64(ctl->info)) {
3153f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld less than minimum %lld\n",
3163f48b137SMark Brown 				       ctl->name, index, int64_val,
3173f48b137SMark Brown 				       snd_ctl_elem_info_get_min64(ctl->info));
3183f48b137SMark Brown 			return false;
3193f48b137SMark Brown 		}
3203f48b137SMark Brown 
3213f48b137SMark Brown 		if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) {
322*efd7d5e1SMirsad Todorovac 			ksft_print_msg("%s.%d value %lld more than maximum %ld\n",
3233f48b137SMark Brown 				       ctl->name, index, int64_val,
3243f48b137SMark Brown 				       snd_ctl_elem_info_get_max(ctl->info));
3253f48b137SMark Brown 			return false;
3263f48b137SMark Brown 		}
3273f48b137SMark Brown 
3283f48b137SMark Brown 		/* Only check step size if there is one and we're in bounds */
3293f48b137SMark Brown 		if (snd_ctl_elem_info_get_step64(ctl->info) &&
3303f48b137SMark Brown 		    (int64_val - snd_ctl_elem_info_get_min64(ctl->info)) %
3313f48b137SMark Brown 		    snd_ctl_elem_info_get_step64(ctl->info)) {
3323f48b137SMark Brown 			ksft_print_msg("%s.%d value %lld invalid for step %lld minimum %lld\n",
3333f48b137SMark Brown 				       ctl->name, index, int64_val,
3343f48b137SMark Brown 				       snd_ctl_elem_info_get_step64(ctl->info),
3353f48b137SMark Brown 				       snd_ctl_elem_info_get_min64(ctl->info));
3363f48b137SMark Brown 			return false;
3373f48b137SMark Brown 		}
3383f48b137SMark Brown 		break;
3393f48b137SMark Brown 
34010f2f194SMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
34110f2f194SMark Brown 		int_val = snd_ctl_elem_value_get_enumerated(val, index);
34210f2f194SMark Brown 
34310f2f194SMark Brown 		if (int_val < 0) {
34410f2f194SMark Brown 			ksft_print_msg("%s.%d negative value %ld for enumeration\n",
34510f2f194SMark Brown 				       ctl->name, index, int_val);
34610f2f194SMark Brown 			return false;
34710f2f194SMark Brown 		}
34810f2f194SMark Brown 
34910f2f194SMark Brown 		if (int_val >= snd_ctl_elem_info_get_items(ctl->info)) {
35010f2f194SMark Brown 			ksft_print_msg("%s.%d value %ld more than item count %ld\n",
35110f2f194SMark Brown 				       ctl->name, index, int_val,
35210f2f194SMark Brown 				       snd_ctl_elem_info_get_items(ctl->info));
35310f2f194SMark Brown 			return false;
35410f2f194SMark Brown 		}
35510f2f194SMark Brown 		break;
35610f2f194SMark Brown 
3573f48b137SMark Brown 	default:
3583f48b137SMark Brown 		/* No tests for other types */
3593f48b137SMark Brown 		break;
3603f48b137SMark Brown 	}
3613f48b137SMark Brown 
3623f48b137SMark Brown 	return true;
3633f48b137SMark Brown }
3643f48b137SMark Brown 
3653f48b137SMark Brown /*
3663f48b137SMark Brown  * Check that the provided value meets the constraints for the
3673f48b137SMark Brown  * provided control.
3683f48b137SMark Brown  */
ctl_value_valid(struct ctl_data * ctl,snd_ctl_elem_value_t * val)3699d73d192SMark Brown static bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val)
3703f48b137SMark Brown {
3713f48b137SMark Brown 	int i;
3723f48b137SMark Brown 	bool valid = true;
3733f48b137SMark Brown 
3743f48b137SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++)
3753f48b137SMark Brown 		if (!ctl_value_index_valid(ctl, val, i))
3763f48b137SMark Brown 			valid = false;
3773f48b137SMark Brown 
3783f48b137SMark Brown 	return valid;
3793f48b137SMark Brown }
3803f48b137SMark Brown 
3815aaf9effSMark Brown /*
3825aaf9effSMark Brown  * Check that we can read the default value and it is valid. Write
3835aaf9effSMark Brown  * tests use the read value to restore the default.
3845aaf9effSMark Brown  */
test_ctl_get_value(struct ctl_data * ctl)3859d73d192SMark Brown static void test_ctl_get_value(struct ctl_data *ctl)
3865aaf9effSMark Brown {
3875aaf9effSMark Brown 	int err;
3885aaf9effSMark Brown 
3895aaf9effSMark Brown 	/* If the control is turned off let's be polite */
3905aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
3915aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
3925aaf9effSMark Brown 		ksft_test_result_skip("get_value.%d.%d\n",
3935aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
3945aaf9effSMark Brown 		return;
3955aaf9effSMark Brown 	}
3965aaf9effSMark Brown 
3975aaf9effSMark Brown 	/* Can't test reading on an unreadable control */
3985aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info)) {
3995aaf9effSMark Brown 		ksft_print_msg("%s is not readable\n", ctl->name);
4005aaf9effSMark Brown 		ksft_test_result_skip("get_value.%d.%d\n",
4015aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
4025aaf9effSMark Brown 		return;
4035aaf9effSMark Brown 	}
4045aaf9effSMark Brown 
4055aaf9effSMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val);
4065aaf9effSMark Brown 	if (err < 0) {
4075aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
4085aaf9effSMark Brown 			       snd_strerror(err));
4095aaf9effSMark Brown 		goto out;
4105aaf9effSMark Brown 	}
4115aaf9effSMark Brown 
4123f48b137SMark Brown 	if (!ctl_value_valid(ctl, ctl->def_val))
4133f48b137SMark Brown 		err = -EINVAL;
4145aaf9effSMark Brown 
4155aaf9effSMark Brown out:
4165aaf9effSMark Brown 	ksft_test_result(err >= 0, "get_value.%d.%d\n",
4175aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
4185aaf9effSMark Brown }
4195aaf9effSMark Brown 
strend(const char * haystack,const char * needle)420c92b576aSMark Brown static bool strend(const char *haystack, const char *needle)
421c92b576aSMark Brown {
422c92b576aSMark Brown 	size_t haystack_len = strlen(haystack);
423c92b576aSMark Brown 	size_t needle_len = strlen(needle);
424c92b576aSMark Brown 
425c92b576aSMark Brown 	if (needle_len > haystack_len)
426c92b576aSMark Brown 		return false;
427c92b576aSMark Brown 	return strcmp(haystack + haystack_len - needle_len, needle) == 0;
428c92b576aSMark Brown }
429c92b576aSMark Brown 
test_ctl_name(struct ctl_data * ctl)430c92b576aSMark Brown static void test_ctl_name(struct ctl_data *ctl)
431c92b576aSMark Brown {
432c92b576aSMark Brown 	bool name_ok = true;
433c92b576aSMark Brown 
434eae87251SMark Brown 	ksft_print_msg("%d.%d %s\n", ctl->card->card, ctl->elem,
435eae87251SMark Brown 		       ctl->name);
436eae87251SMark Brown 
437c92b576aSMark Brown 	/* Only boolean controls should end in Switch */
438c92b576aSMark Brown 	if (strend(ctl->name, " Switch")) {
439c92b576aSMark Brown 		if (snd_ctl_elem_info_get_type(ctl->info) != SND_CTL_ELEM_TYPE_BOOLEAN) {
440c92b576aSMark Brown 			ksft_print_msg("%d.%d %s ends in Switch but is not boolean\n",
441c92b576aSMark Brown 				       ctl->card->card, ctl->elem, ctl->name);
442c92b576aSMark Brown 			name_ok = false;
443c92b576aSMark Brown 		}
444c92b576aSMark Brown 	}
445c92b576aSMark Brown 
446c92b576aSMark Brown 	/* Writeable boolean controls should end in Switch */
447c92b576aSMark Brown 	if (snd_ctl_elem_info_get_type(ctl->info) == SND_CTL_ELEM_TYPE_BOOLEAN &&
448c92b576aSMark Brown 	    snd_ctl_elem_info_is_writable(ctl->info)) {
449c92b576aSMark Brown 		if (!strend(ctl->name, " Switch")) {
450c92b576aSMark Brown 			ksft_print_msg("%d.%d %s is a writeable boolean but not a Switch\n",
451c92b576aSMark Brown 				       ctl->card->card, ctl->elem, ctl->name);
452c92b576aSMark Brown 			name_ok = false;
453c92b576aSMark Brown 		}
454c92b576aSMark Brown 	}
455c92b576aSMark Brown 
456c92b576aSMark Brown 	ksft_test_result(name_ok, "name.%d.%d\n",
457c92b576aSMark Brown 			 ctl->card->card, ctl->elem);
458c92b576aSMark Brown }
459c92b576aSMark Brown 
show_values(struct ctl_data * ctl,snd_ctl_elem_value_t * orig_val,snd_ctl_elem_value_t * read_val)46005a2cdfeSMark Brown static void show_values(struct ctl_data *ctl, snd_ctl_elem_value_t *orig_val,
46105a2cdfeSMark Brown 			snd_ctl_elem_value_t *read_val)
46205a2cdfeSMark Brown {
46305a2cdfeSMark Brown 	long long orig_int, read_int;
46405a2cdfeSMark Brown 	int i;
46505a2cdfeSMark Brown 
46605a2cdfeSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
46705a2cdfeSMark Brown 		switch (snd_ctl_elem_info_get_type(ctl->info)) {
46805a2cdfeSMark Brown 		case SND_CTL_ELEM_TYPE_BOOLEAN:
46905a2cdfeSMark Brown 			orig_int = snd_ctl_elem_value_get_boolean(orig_val, i);
47005a2cdfeSMark Brown 			read_int = snd_ctl_elem_value_get_boolean(read_val, i);
47105a2cdfeSMark Brown 			break;
47205a2cdfeSMark Brown 
47305a2cdfeSMark Brown 		case SND_CTL_ELEM_TYPE_INTEGER:
47405a2cdfeSMark Brown 			orig_int = snd_ctl_elem_value_get_integer(orig_val, i);
47505a2cdfeSMark Brown 			read_int = snd_ctl_elem_value_get_integer(read_val, i);
47605a2cdfeSMark Brown 			break;
47705a2cdfeSMark Brown 
47805a2cdfeSMark Brown 		case SND_CTL_ELEM_TYPE_INTEGER64:
47905a2cdfeSMark Brown 			orig_int = snd_ctl_elem_value_get_integer64(orig_val,
48005a2cdfeSMark Brown 								    i);
48105a2cdfeSMark Brown 			read_int = snd_ctl_elem_value_get_integer64(read_val,
48205a2cdfeSMark Brown 								    i);
48305a2cdfeSMark Brown 			break;
48405a2cdfeSMark Brown 
48505a2cdfeSMark Brown 		case SND_CTL_ELEM_TYPE_ENUMERATED:
48605a2cdfeSMark Brown 			orig_int = snd_ctl_elem_value_get_enumerated(orig_val,
48705a2cdfeSMark Brown 								     i);
48805a2cdfeSMark Brown 			read_int = snd_ctl_elem_value_get_enumerated(read_val,
48905a2cdfeSMark Brown 								     i);
49005a2cdfeSMark Brown 			break;
49105a2cdfeSMark Brown 
49205a2cdfeSMark Brown 		default:
49305a2cdfeSMark Brown 			return;
49405a2cdfeSMark Brown 		}
49505a2cdfeSMark Brown 
49605a2cdfeSMark Brown 		ksft_print_msg("%s.%d orig %lld read %lld, is_volatile %d\n",
49705a2cdfeSMark Brown 			       ctl->name, i, orig_int, read_int,
49805a2cdfeSMark Brown 			       snd_ctl_elem_info_is_volatile(ctl->info));
49905a2cdfeSMark Brown 	}
50005a2cdfeSMark Brown }
50105a2cdfeSMark Brown 
show_mismatch(struct ctl_data * ctl,int index,snd_ctl_elem_value_t * read_val,snd_ctl_elem_value_t * expected_val)5029d73d192SMark Brown static bool show_mismatch(struct ctl_data *ctl, int index,
5035aaf9effSMark Brown 			  snd_ctl_elem_value_t *read_val,
5045aaf9effSMark Brown 			  snd_ctl_elem_value_t *expected_val)
5055aaf9effSMark Brown {
5065aaf9effSMark Brown 	long long expected_int, read_int;
5075aaf9effSMark Brown 
5085aaf9effSMark Brown 	/*
5095aaf9effSMark Brown 	 * We factor out the code to compare values representable as
5105aaf9effSMark Brown 	 * integers, ensure that check doesn't log otherwise.
5115aaf9effSMark Brown 	 */
5125aaf9effSMark Brown 	expected_int = 0;
5135aaf9effSMark Brown 	read_int = 0;
5145aaf9effSMark Brown 
5155aaf9effSMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
5165aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
5175aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_boolean(expected_val,
5185aaf9effSMark Brown 							      index);
5195aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_boolean(read_val, index);
5205aaf9effSMark Brown 		break;
5215aaf9effSMark Brown 
5225aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
5235aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_integer(expected_val,
5245aaf9effSMark Brown 							      index);
5255aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_integer(read_val, index);
5265aaf9effSMark Brown 		break;
5275aaf9effSMark Brown 
5285aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
5295aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_integer64(expected_val,
5305aaf9effSMark Brown 								index);
5315aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_integer64(read_val,
5325aaf9effSMark Brown 							    index);
5335aaf9effSMark Brown 		break;
5345aaf9effSMark Brown 
5355aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
5365aaf9effSMark Brown 		expected_int = snd_ctl_elem_value_get_enumerated(expected_val,
5375aaf9effSMark Brown 								 index);
5385aaf9effSMark Brown 		read_int = snd_ctl_elem_value_get_enumerated(read_val,
5395aaf9effSMark Brown 							     index);
5405aaf9effSMark Brown 		break;
5415aaf9effSMark Brown 
5425aaf9effSMark Brown 	default:
5435aaf9effSMark Brown 		break;
5445aaf9effSMark Brown 	}
5455aaf9effSMark Brown 
5465aaf9effSMark Brown 	if (expected_int != read_int) {
5477cc994f2STakashi Sakamoto 		/*
5487cc994f2STakashi Sakamoto 		 * NOTE: The volatile attribute means that the hardware
5497cc994f2STakashi Sakamoto 		 * can voluntarily change the state of control element
5507cc994f2STakashi Sakamoto 		 * independent of any operation by software.
5517cc994f2STakashi Sakamoto 		 */
5527cc994f2STakashi Sakamoto 		bool is_volatile = snd_ctl_elem_info_is_volatile(ctl->info);
5537cc994f2STakashi Sakamoto 		ksft_print_msg("%s.%d expected %lld but read %lld, is_volatile %d\n",
5547cc994f2STakashi Sakamoto 			       ctl->name, index, expected_int, read_int, is_volatile);
5557cc994f2STakashi Sakamoto 		return !is_volatile;
5565aaf9effSMark Brown 	} else {
5575aaf9effSMark Brown 		return false;
5585aaf9effSMark Brown 	}
5595aaf9effSMark Brown }
5605aaf9effSMark Brown 
5615aaf9effSMark Brown /*
5625aaf9effSMark Brown  * Write a value then if possible verify that we get the expected
5635aaf9effSMark Brown  * result.  An optional expected value can be provided if we expect
5645aaf9effSMark Brown  * the write to fail, for verifying that invalid writes don't corrupt
5655aaf9effSMark Brown  * anything.
5665aaf9effSMark Brown  */
write_and_verify(struct ctl_data * ctl,snd_ctl_elem_value_t * write_val,snd_ctl_elem_value_t * expected_val)5679d73d192SMark Brown static int write_and_verify(struct ctl_data *ctl,
5685aaf9effSMark Brown 			    snd_ctl_elem_value_t *write_val,
5695aaf9effSMark Brown 			    snd_ctl_elem_value_t *expected_val)
5705aaf9effSMark Brown {
5715aaf9effSMark Brown 	int err, i;
5725aaf9effSMark Brown 	bool error_expected, mismatch_shown;
573b1446bdaSMark Brown 	snd_ctl_elem_value_t *initial_val, *read_val, *w_val;
574b1446bdaSMark Brown 	snd_ctl_elem_value_alloca(&initial_val);
5755aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&read_val);
5765aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&w_val);
5775aaf9effSMark Brown 
5785aaf9effSMark Brown 	/*
5795aaf9effSMark Brown 	 * We need to copy the write value since writing can modify
5805aaf9effSMark Brown 	 * the value which causes surprises, and allocate an expected
5815aaf9effSMark Brown 	 * value if we expect to read back what we wrote.
5825aaf9effSMark Brown 	 */
5835aaf9effSMark Brown 	snd_ctl_elem_value_copy(w_val, write_val);
5845aaf9effSMark Brown 	if (expected_val) {
5855aaf9effSMark Brown 		error_expected = true;
5865aaf9effSMark Brown 	} else {
5875aaf9effSMark Brown 		error_expected = false;
5885aaf9effSMark Brown 		snd_ctl_elem_value_alloca(&expected_val);
5895aaf9effSMark Brown 		snd_ctl_elem_value_copy(expected_val, write_val);
5905aaf9effSMark Brown 	}
5915aaf9effSMark Brown 
592b1446bdaSMark Brown 	/* Store the value before we write */
593b1446bdaSMark Brown 	if (snd_ctl_elem_info_is_readable(ctl->info)) {
594b1446bdaSMark Brown 		snd_ctl_elem_value_set_id(initial_val, ctl->id);
595b1446bdaSMark Brown 
596b1446bdaSMark Brown 		err = snd_ctl_elem_read(ctl->card->handle, initial_val);
597b1446bdaSMark Brown 		if (err < 0) {
598b1446bdaSMark Brown 			ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
599b1446bdaSMark Brown 				       snd_strerror(err));
600b1446bdaSMark Brown 			return err;
601b1446bdaSMark Brown 		}
602b1446bdaSMark Brown 	}
603b1446bdaSMark Brown 
6045aaf9effSMark Brown 	/*
6055aaf9effSMark Brown 	 * Do the write, if we have an expected value ignore the error
6065aaf9effSMark Brown 	 * and carry on to validate the expected value.
6075aaf9effSMark Brown 	 */
6085aaf9effSMark Brown 	err = snd_ctl_elem_write(ctl->card->handle, w_val);
6095aaf9effSMark Brown 	if (err < 0 && !error_expected) {
6105aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_write() failed: %s\n",
6115aaf9effSMark Brown 			       snd_strerror(err));
6125aaf9effSMark Brown 		return err;
6135aaf9effSMark Brown 	}
6145aaf9effSMark Brown 
6155aaf9effSMark Brown 	/* Can we do the verification part? */
6165aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info))
6175aaf9effSMark Brown 		return err;
6185aaf9effSMark Brown 
6195aaf9effSMark Brown 	snd_ctl_elem_value_set_id(read_val, ctl->id);
6205aaf9effSMark Brown 
6215aaf9effSMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, read_val);
6225aaf9effSMark Brown 	if (err < 0) {
6235aaf9effSMark Brown 		ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
6245aaf9effSMark Brown 			       snd_strerror(err));
6255aaf9effSMark Brown 		return err;
6265aaf9effSMark Brown 	}
6275aaf9effSMark Brown 
6285aaf9effSMark Brown 	/*
629b1446bdaSMark Brown 	 * Check for an event if the value changed, or confirm that
630b1446bdaSMark Brown 	 * there was none if it didn't.  We rely on the kernel
631b1446bdaSMark Brown 	 * generating the notification before it returns from the
632b1446bdaSMark Brown 	 * write, this is currently true, should that ever change this
633b1446bdaSMark Brown 	 * will most likely break and need updating.
634b1446bdaSMark Brown 	 */
635b1446bdaSMark Brown 	if (!snd_ctl_elem_info_is_volatile(ctl->info)) {
636b1446bdaSMark Brown 		err = wait_for_event(ctl, 0);
637b1446bdaSMark Brown 		if (snd_ctl_elem_value_compare(initial_val, read_val)) {
638b1446bdaSMark Brown 			if (err < 1) {
639b1446bdaSMark Brown 				ksft_print_msg("No event generated for %s\n",
640b1446bdaSMark Brown 					       ctl->name);
64105a2cdfeSMark Brown 				show_values(ctl, initial_val, read_val);
642b1446bdaSMark Brown 				ctl->event_missing++;
643b1446bdaSMark Brown 			}
644b1446bdaSMark Brown 		} else {
645b1446bdaSMark Brown 			if (err != 0) {
646b1446bdaSMark Brown 				ksft_print_msg("Spurious event generated for %s\n",
647b1446bdaSMark Brown 					       ctl->name);
64805a2cdfeSMark Brown 				show_values(ctl, initial_val, read_val);
649b1446bdaSMark Brown 				ctl->event_spurious++;
650b1446bdaSMark Brown 			}
651b1446bdaSMark Brown 		}
652b1446bdaSMark Brown 	}
653b1446bdaSMark Brown 
654b1446bdaSMark Brown 	/*
6555aaf9effSMark Brown 	 * Use the libray to compare values, if there's a mismatch
6565aaf9effSMark Brown 	 * carry on and try to provide a more useful diagnostic than
6575aaf9effSMark Brown 	 * just "mismatch".
6585aaf9effSMark Brown 	 */
6595aaf9effSMark Brown 	if (!snd_ctl_elem_value_compare(expected_val, read_val))
6605aaf9effSMark Brown 		return 0;
6615aaf9effSMark Brown 
6625aaf9effSMark Brown 	mismatch_shown = false;
6635aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++)
6645aaf9effSMark Brown 		if (show_mismatch(ctl, i, read_val, expected_val))
6655aaf9effSMark Brown 			mismatch_shown = true;
6665aaf9effSMark Brown 
6675aaf9effSMark Brown 	if (!mismatch_shown)
6685aaf9effSMark Brown 		ksft_print_msg("%s read and written values differ\n",
6695aaf9effSMark Brown 			       ctl->name);
6705aaf9effSMark Brown 
6715aaf9effSMark Brown 	return -1;
6725aaf9effSMark Brown }
6735aaf9effSMark Brown 
6745aaf9effSMark Brown /*
6755aaf9effSMark Brown  * Make sure we can write the default value back to the control, this
6765aaf9effSMark Brown  * should validate that at least some write works.
6775aaf9effSMark Brown  */
test_ctl_write_default(struct ctl_data * ctl)6789d73d192SMark Brown static void test_ctl_write_default(struct ctl_data *ctl)
6795aaf9effSMark Brown {
6805aaf9effSMark Brown 	int err;
6815aaf9effSMark Brown 
6825aaf9effSMark Brown 	/* If the control is turned off let's be polite */
6835aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
6845aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
6855aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
6865aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
6875aaf9effSMark Brown 		return;
6885aaf9effSMark Brown 	}
6895aaf9effSMark Brown 
6905aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
6915aaf9effSMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
6925aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
6935aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
6945aaf9effSMark Brown 		return;
6955aaf9effSMark Brown 	}
6965aaf9effSMark Brown 
6975aaf9effSMark Brown 	/* No idea what the default was for unreadable controls */
6985aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_readable(ctl->info)) {
6995aaf9effSMark Brown 		ksft_print_msg("%s couldn't read default\n", ctl->name);
7005aaf9effSMark Brown 		ksft_test_result_skip("write_default.%d.%d\n",
7015aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
7025aaf9effSMark Brown 		return;
7035aaf9effSMark Brown 	}
7045aaf9effSMark Brown 
7055aaf9effSMark Brown 	err = write_and_verify(ctl, ctl->def_val, NULL);
7065aaf9effSMark Brown 
7075aaf9effSMark Brown 	ksft_test_result(err >= 0, "write_default.%d.%d\n",
7085aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
7095aaf9effSMark Brown }
7105aaf9effSMark Brown 
test_ctl_write_valid_boolean(struct ctl_data * ctl)7119d73d192SMark Brown static bool test_ctl_write_valid_boolean(struct ctl_data *ctl)
7125aaf9effSMark Brown {
7135aaf9effSMark Brown 	int err, i, j;
7145aaf9effSMark Brown 	bool fail = false;
7155aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
7165aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
7175aaf9effSMark Brown 
7185aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
7195aaf9effSMark Brown 
7205aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
7215aaf9effSMark Brown 		for (j = 0; j < 2; j++) {
7225aaf9effSMark Brown 			snd_ctl_elem_value_set_boolean(val, i, j);
7235aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
7245aaf9effSMark Brown 			if (err != 0)
7255aaf9effSMark Brown 				fail = true;
7265aaf9effSMark Brown 		}
7275aaf9effSMark Brown 	}
7285aaf9effSMark Brown 
7295aaf9effSMark Brown 	return !fail;
7305aaf9effSMark Brown }
7315aaf9effSMark Brown 
test_ctl_write_valid_integer(struct ctl_data * ctl)7329d73d192SMark Brown static bool test_ctl_write_valid_integer(struct ctl_data *ctl)
7335aaf9effSMark Brown {
7345aaf9effSMark Brown 	int err;
7355aaf9effSMark Brown 	int i;
7365aaf9effSMark Brown 	long j, step;
7375aaf9effSMark Brown 	bool fail = false;
7385aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
7395aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
7405aaf9effSMark Brown 
7415aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
7425aaf9effSMark Brown 
7435aaf9effSMark Brown 	step = snd_ctl_elem_info_get_step(ctl->info);
7445aaf9effSMark Brown 	if (!step)
7455aaf9effSMark Brown 		step = 1;
7465aaf9effSMark Brown 
7475aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
7485aaf9effSMark Brown 		for (j = snd_ctl_elem_info_get_min(ctl->info);
7495aaf9effSMark Brown 		     j <= snd_ctl_elem_info_get_max(ctl->info); j += step) {
7505aaf9effSMark Brown 
7515aaf9effSMark Brown 			snd_ctl_elem_value_set_integer(val, i, j);
7525aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
7535aaf9effSMark Brown 			if (err != 0)
7545aaf9effSMark Brown 				fail = true;
7555aaf9effSMark Brown 		}
7565aaf9effSMark Brown 	}
7575aaf9effSMark Brown 
7585aaf9effSMark Brown 
7595aaf9effSMark Brown 	return !fail;
7605aaf9effSMark Brown }
7615aaf9effSMark Brown 
test_ctl_write_valid_integer64(struct ctl_data * ctl)7629d73d192SMark Brown static bool test_ctl_write_valid_integer64(struct ctl_data *ctl)
7635aaf9effSMark Brown {
7645aaf9effSMark Brown 	int err, i;
7655aaf9effSMark Brown 	long long j, step;
7665aaf9effSMark Brown 	bool fail = false;
7675aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
7685aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
7695aaf9effSMark Brown 
7705aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
7715aaf9effSMark Brown 
7725aaf9effSMark Brown 	step = snd_ctl_elem_info_get_step64(ctl->info);
7735aaf9effSMark Brown 	if (!step)
7745aaf9effSMark Brown 		step = 1;
7755aaf9effSMark Brown 
7765aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
7775aaf9effSMark Brown 		for (j = snd_ctl_elem_info_get_min64(ctl->info);
7785aaf9effSMark Brown 		     j <= snd_ctl_elem_info_get_max64(ctl->info); j += step) {
7795aaf9effSMark Brown 
7805aaf9effSMark Brown 			snd_ctl_elem_value_set_integer64(val, i, j);
7815aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
7825aaf9effSMark Brown 			if (err != 0)
7835aaf9effSMark Brown 				fail = true;
7845aaf9effSMark Brown 		}
7855aaf9effSMark Brown 	}
7865aaf9effSMark Brown 
7875aaf9effSMark Brown 	return !fail;
7885aaf9effSMark Brown }
7895aaf9effSMark Brown 
test_ctl_write_valid_enumerated(struct ctl_data * ctl)7909d73d192SMark Brown static bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
7915aaf9effSMark Brown {
7925aaf9effSMark Brown 	int err, i, j;
7935aaf9effSMark Brown 	bool fail = false;
7945aaf9effSMark Brown 	snd_ctl_elem_value_t *val;
7955aaf9effSMark Brown 	snd_ctl_elem_value_alloca(&val);
7965aaf9effSMark Brown 
7975aaf9effSMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
7985aaf9effSMark Brown 
7995aaf9effSMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
8005aaf9effSMark Brown 		for (j = 0; j < snd_ctl_elem_info_get_items(ctl->info); j++) {
8015aaf9effSMark Brown 			snd_ctl_elem_value_set_enumerated(val, i, j);
8025aaf9effSMark Brown 			err = write_and_verify(ctl, val, NULL);
8035aaf9effSMark Brown 			if (err != 0)
8045aaf9effSMark Brown 				fail = true;
8055aaf9effSMark Brown 		}
8065aaf9effSMark Brown 	}
8075aaf9effSMark Brown 
8085aaf9effSMark Brown 	return !fail;
8095aaf9effSMark Brown }
8105aaf9effSMark Brown 
test_ctl_write_valid(struct ctl_data * ctl)8119d73d192SMark Brown static void test_ctl_write_valid(struct ctl_data *ctl)
8125aaf9effSMark Brown {
8135aaf9effSMark Brown 	bool pass;
8145aaf9effSMark Brown 
8155aaf9effSMark Brown 	/* If the control is turned off let's be polite */
8165aaf9effSMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
8175aaf9effSMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
8185aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
8195aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
8205aaf9effSMark Brown 		return;
8215aaf9effSMark Brown 	}
8225aaf9effSMark Brown 
8235aaf9effSMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
8245aaf9effSMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
8255aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
8265aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
8275aaf9effSMark Brown 		return;
8285aaf9effSMark Brown 	}
8295aaf9effSMark Brown 
8305aaf9effSMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
8315aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
8325aaf9effSMark Brown 		pass = test_ctl_write_valid_boolean(ctl);
8335aaf9effSMark Brown 		break;
8345aaf9effSMark Brown 
8355aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
8365aaf9effSMark Brown 		pass = test_ctl_write_valid_integer(ctl);
8375aaf9effSMark Brown 		break;
8385aaf9effSMark Brown 
8395aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
8405aaf9effSMark Brown 		pass = test_ctl_write_valid_integer64(ctl);
8415aaf9effSMark Brown 		break;
8425aaf9effSMark Brown 
8435aaf9effSMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
8445aaf9effSMark Brown 		pass = test_ctl_write_valid_enumerated(ctl);
8455aaf9effSMark Brown 		break;
8465aaf9effSMark Brown 
8475aaf9effSMark Brown 	default:
8485aaf9effSMark Brown 		/* No tests for this yet */
8495aaf9effSMark Brown 		ksft_test_result_skip("write_valid.%d.%d\n",
8505aaf9effSMark Brown 				      ctl->card->card, ctl->elem);
8515aaf9effSMark Brown 		return;
8525aaf9effSMark Brown 	}
8535aaf9effSMark Brown 
8545aaf9effSMark Brown 	/* Restore the default value to minimise disruption */
8557c79b10bSMark Brown 	write_and_verify(ctl, ctl->def_val, NULL);
8565aaf9effSMark Brown 
8575aaf9effSMark Brown 	ksft_test_result(pass, "write_valid.%d.%d\n",
8585aaf9effSMark Brown 			 ctl->card->card, ctl->elem);
8595aaf9effSMark Brown }
8605aaf9effSMark Brown 
test_ctl_write_invalid_value(struct ctl_data * ctl,snd_ctl_elem_value_t * val)8619d73d192SMark Brown static bool test_ctl_write_invalid_value(struct ctl_data *ctl,
86288b61322SMark Brown 					 snd_ctl_elem_value_t *val)
86388b61322SMark Brown {
86488b61322SMark Brown 	int err;
86588b61322SMark Brown 
86688b61322SMark Brown 	/* Ideally this will fail... */
86788b61322SMark Brown 	err = snd_ctl_elem_write(ctl->card->handle, val);
86888b61322SMark Brown 	if (err < 0)
86988b61322SMark Brown 		return false;
87088b61322SMark Brown 
87188b61322SMark Brown 	/* ...but some devices will clamp to an in range value */
87288b61322SMark Brown 	err = snd_ctl_elem_read(ctl->card->handle, val);
87388b61322SMark Brown 	if (err < 0) {
87488b61322SMark Brown 		ksft_print_msg("%s failed to read: %s\n",
87588b61322SMark Brown 			       ctl->name, snd_strerror(err));
87688b61322SMark Brown 		return true;
87788b61322SMark Brown 	}
87888b61322SMark Brown 
87988b61322SMark Brown 	return !ctl_value_valid(ctl, val);
88088b61322SMark Brown }
88188b61322SMark Brown 
test_ctl_write_invalid_boolean(struct ctl_data * ctl)8829d73d192SMark Brown static bool test_ctl_write_invalid_boolean(struct ctl_data *ctl)
88388b61322SMark Brown {
88461ba93b4SDing Xiang 	int i;
88588b61322SMark Brown 	bool fail = false;
88688b61322SMark Brown 	snd_ctl_elem_value_t *val;
88788b61322SMark Brown 	snd_ctl_elem_value_alloca(&val);
88888b61322SMark Brown 
88988b61322SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
89088b61322SMark Brown 		snd_ctl_elem_value_copy(val, ctl->def_val);
89188b61322SMark Brown 		snd_ctl_elem_value_set_boolean(val, i, 2);
89288b61322SMark Brown 
89388b61322SMark Brown 		if (test_ctl_write_invalid_value(ctl, val))
89488b61322SMark Brown 			fail = true;
89588b61322SMark Brown 	}
89688b61322SMark Brown 
89788b61322SMark Brown 	return !fail;
89888b61322SMark Brown }
89988b61322SMark Brown 
test_ctl_write_invalid_integer(struct ctl_data * ctl)9009d73d192SMark Brown static bool test_ctl_write_invalid_integer(struct ctl_data *ctl)
90188b61322SMark Brown {
90288b61322SMark Brown 	int i;
90388b61322SMark Brown 	bool fail = false;
90488b61322SMark Brown 	snd_ctl_elem_value_t *val;
90588b61322SMark Brown 	snd_ctl_elem_value_alloca(&val);
90688b61322SMark Brown 
90788b61322SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
90888b61322SMark Brown 		if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) {
90988b61322SMark Brown 			/* Just under range */
91088b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
91188b61322SMark Brown 			snd_ctl_elem_value_set_integer(val, i,
91288b61322SMark Brown 			       snd_ctl_elem_info_get_min(ctl->info) - 1);
91388b61322SMark Brown 
91488b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
91588b61322SMark Brown 				fail = true;
91688b61322SMark Brown 
91788b61322SMark Brown 			/* Minimum representable value */
91888b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
91988b61322SMark Brown 			snd_ctl_elem_value_set_integer(val, i, LONG_MIN);
92088b61322SMark Brown 
92188b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
92288b61322SMark Brown 				fail = true;
92388b61322SMark Brown 		}
92488b61322SMark Brown 
92588b61322SMark Brown 		if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) {
92688b61322SMark Brown 			/* Just over range */
92788b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
92888b61322SMark Brown 			snd_ctl_elem_value_set_integer(val, i,
92988b61322SMark Brown 			       snd_ctl_elem_info_get_max(ctl->info) + 1);
93088b61322SMark Brown 
93188b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
93288b61322SMark Brown 				fail = true;
93388b61322SMark Brown 
93488b61322SMark Brown 			/* Maximum representable value */
93588b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
93688b61322SMark Brown 			snd_ctl_elem_value_set_integer(val, i, LONG_MAX);
93788b61322SMark Brown 
93888b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
93988b61322SMark Brown 				fail = true;
94088b61322SMark Brown 		}
94188b61322SMark Brown 	}
94288b61322SMark Brown 
94388b61322SMark Brown 	return !fail;
94488b61322SMark Brown }
94588b61322SMark Brown 
test_ctl_write_invalid_integer64(struct ctl_data * ctl)9469d73d192SMark Brown static bool test_ctl_write_invalid_integer64(struct ctl_data *ctl)
94788b61322SMark Brown {
94888b61322SMark Brown 	int i;
94988b61322SMark Brown 	bool fail = false;
95088b61322SMark Brown 	snd_ctl_elem_value_t *val;
95188b61322SMark Brown 	snd_ctl_elem_value_alloca(&val);
95288b61322SMark Brown 
95388b61322SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
95488b61322SMark Brown 		if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) {
95588b61322SMark Brown 			/* Just under range */
95688b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
95788b61322SMark Brown 			snd_ctl_elem_value_set_integer64(val, i,
95888b61322SMark Brown 				snd_ctl_elem_info_get_min64(ctl->info) - 1);
95988b61322SMark Brown 
96088b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
96188b61322SMark Brown 				fail = true;
96288b61322SMark Brown 
96388b61322SMark Brown 			/* Minimum representable value */
96488b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
96588b61322SMark Brown 			snd_ctl_elem_value_set_integer64(val, i, LLONG_MIN);
96688b61322SMark Brown 
96788b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
96888b61322SMark Brown 				fail = true;
96988b61322SMark Brown 		}
97088b61322SMark Brown 
97188b61322SMark Brown 		if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) {
97288b61322SMark Brown 			/* Just over range */
97388b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
97488b61322SMark Brown 			snd_ctl_elem_value_set_integer64(val, i,
97588b61322SMark Brown 				snd_ctl_elem_info_get_max64(ctl->info) + 1);
97688b61322SMark Brown 
97788b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
97888b61322SMark Brown 				fail = true;
97988b61322SMark Brown 
98088b61322SMark Brown 			/* Maximum representable value */
98188b61322SMark Brown 			snd_ctl_elem_value_copy(val, ctl->def_val);
98288b61322SMark Brown 			snd_ctl_elem_value_set_integer64(val, i, LLONG_MAX);
98388b61322SMark Brown 
98488b61322SMark Brown 			if (test_ctl_write_invalid_value(ctl, val))
98588b61322SMark Brown 				fail = true;
98688b61322SMark Brown 		}
98788b61322SMark Brown 	}
98888b61322SMark Brown 
98988b61322SMark Brown 	return !fail;
99088b61322SMark Brown }
99188b61322SMark Brown 
test_ctl_write_invalid_enumerated(struct ctl_data * ctl)9929d73d192SMark Brown static bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl)
99388b61322SMark Brown {
99461ba93b4SDing Xiang 	int i;
99588b61322SMark Brown 	bool fail = false;
99688b61322SMark Brown 	snd_ctl_elem_value_t *val;
99788b61322SMark Brown 	snd_ctl_elem_value_alloca(&val);
99888b61322SMark Brown 
99988b61322SMark Brown 	snd_ctl_elem_value_set_id(val, ctl->id);
100088b61322SMark Brown 
100188b61322SMark Brown 	for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
100288b61322SMark Brown 		/* One beyond maximum */
100388b61322SMark Brown 		snd_ctl_elem_value_copy(val, ctl->def_val);
100488b61322SMark Brown 		snd_ctl_elem_value_set_enumerated(val, i,
100588b61322SMark Brown 				  snd_ctl_elem_info_get_items(ctl->info));
100688b61322SMark Brown 
100788b61322SMark Brown 		if (test_ctl_write_invalid_value(ctl, val))
100888b61322SMark Brown 			fail = true;
100988b61322SMark Brown 
101088b61322SMark Brown 		/* Maximum representable value */
101188b61322SMark Brown 		snd_ctl_elem_value_copy(val, ctl->def_val);
101288b61322SMark Brown 		snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX);
101388b61322SMark Brown 
101488b61322SMark Brown 		if (test_ctl_write_invalid_value(ctl, val))
101588b61322SMark Brown 			fail = true;
101688b61322SMark Brown 
101788b61322SMark Brown 	}
101888b61322SMark Brown 
101988b61322SMark Brown 	return !fail;
102088b61322SMark Brown }
102188b61322SMark Brown 
102288b61322SMark Brown 
test_ctl_write_invalid(struct ctl_data * ctl)10239d73d192SMark Brown static void test_ctl_write_invalid(struct ctl_data *ctl)
102488b61322SMark Brown {
102588b61322SMark Brown 	bool pass;
102688b61322SMark Brown 
102788b61322SMark Brown 	/* If the control is turned off let's be polite */
102888b61322SMark Brown 	if (snd_ctl_elem_info_is_inactive(ctl->info)) {
102988b61322SMark Brown 		ksft_print_msg("%s is inactive\n", ctl->name);
103088b61322SMark Brown 		ksft_test_result_skip("write_invalid.%d.%d\n",
103188b61322SMark Brown 				      ctl->card->card, ctl->elem);
103288b61322SMark Brown 		return;
103388b61322SMark Brown 	}
103488b61322SMark Brown 
103588b61322SMark Brown 	if (!snd_ctl_elem_info_is_writable(ctl->info)) {
103688b61322SMark Brown 		ksft_print_msg("%s is not writeable\n", ctl->name);
103788b61322SMark Brown 		ksft_test_result_skip("write_invalid.%d.%d\n",
103888b61322SMark Brown 				      ctl->card->card, ctl->elem);
103988b61322SMark Brown 		return;
104088b61322SMark Brown 	}
104188b61322SMark Brown 
104288b61322SMark Brown 	switch (snd_ctl_elem_info_get_type(ctl->info)) {
104388b61322SMark Brown 	case SND_CTL_ELEM_TYPE_BOOLEAN:
104488b61322SMark Brown 		pass = test_ctl_write_invalid_boolean(ctl);
104588b61322SMark Brown 		break;
104688b61322SMark Brown 
104788b61322SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER:
104888b61322SMark Brown 		pass = test_ctl_write_invalid_integer(ctl);
104988b61322SMark Brown 		break;
105088b61322SMark Brown 
105188b61322SMark Brown 	case SND_CTL_ELEM_TYPE_INTEGER64:
105288b61322SMark Brown 		pass = test_ctl_write_invalid_integer64(ctl);
105388b61322SMark Brown 		break;
105488b61322SMark Brown 
105588b61322SMark Brown 	case SND_CTL_ELEM_TYPE_ENUMERATED:
105688b61322SMark Brown 		pass = test_ctl_write_invalid_enumerated(ctl);
105788b61322SMark Brown 		break;
105888b61322SMark Brown 
105988b61322SMark Brown 	default:
106088b61322SMark Brown 		/* No tests for this yet */
106188b61322SMark Brown 		ksft_test_result_skip("write_invalid.%d.%d\n",
106288b61322SMark Brown 				      ctl->card->card, ctl->elem);
106388b61322SMark Brown 		return;
106488b61322SMark Brown 	}
106588b61322SMark Brown 
106688b61322SMark Brown 	/* Restore the default value to minimise disruption */
10677c79b10bSMark Brown 	write_and_verify(ctl, ctl->def_val, NULL);
106888b61322SMark Brown 
106988b61322SMark Brown 	ksft_test_result(pass, "write_invalid.%d.%d\n",
107088b61322SMark Brown 			 ctl->card->card, ctl->elem);
107188b61322SMark Brown }
107288b61322SMark Brown 
test_ctl_event_missing(struct ctl_data * ctl)10739d73d192SMark Brown static void test_ctl_event_missing(struct ctl_data *ctl)
1074b1446bdaSMark Brown {
1075b1446bdaSMark Brown 	ksft_test_result(!ctl->event_missing, "event_missing.%d.%d\n",
1076b1446bdaSMark Brown 			 ctl->card->card, ctl->elem);
1077b1446bdaSMark Brown }
1078b1446bdaSMark Brown 
test_ctl_event_spurious(struct ctl_data * ctl)10799d73d192SMark Brown static void test_ctl_event_spurious(struct ctl_data *ctl)
1080b1446bdaSMark Brown {
1081b1446bdaSMark Brown 	ksft_test_result(!ctl->event_spurious, "event_spurious.%d.%d\n",
1082b1446bdaSMark Brown 			 ctl->card->card, ctl->elem);
1083b1446bdaSMark Brown }
1084b1446bdaSMark Brown 
main(void)10855aaf9effSMark Brown int main(void)
10865aaf9effSMark Brown {
10875aaf9effSMark Brown 	struct ctl_data *ctl;
10885aaf9effSMark Brown 
10895aaf9effSMark Brown 	ksft_print_header();
10905aaf9effSMark Brown 
10915aaf9effSMark Brown 	find_controls();
10925aaf9effSMark Brown 
10935aaf9effSMark Brown 	ksft_set_plan(num_controls * TESTS_PER_CONTROL);
10945aaf9effSMark Brown 
10955aaf9effSMark Brown 	for (ctl = ctl_list; ctl != NULL; ctl = ctl->next) {
10965aaf9effSMark Brown 		/*
10975aaf9effSMark Brown 		 * Must test get_value() before we write anything, the
10985aaf9effSMark Brown 		 * test stores the default value for later cleanup.
10995aaf9effSMark Brown 		 */
11005aaf9effSMark Brown 		test_ctl_get_value(ctl);
1101c92b576aSMark Brown 		test_ctl_name(ctl);
11025aaf9effSMark Brown 		test_ctl_write_default(ctl);
11035aaf9effSMark Brown 		test_ctl_write_valid(ctl);
110488b61322SMark Brown 		test_ctl_write_invalid(ctl);
1105b1446bdaSMark Brown 		test_ctl_event_missing(ctl);
1106b1446bdaSMark Brown 		test_ctl_event_spurious(ctl);
11075aaf9effSMark Brown 	}
11085aaf9effSMark Brown 
11095aaf9effSMark Brown 	ksft_exit_pass();
11105aaf9effSMark Brown 
11115aaf9effSMark Brown 	return 0;
11125aaf9effSMark Brown }
1113