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 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) { 1415aaf9effSMark Brown ksft_print_msg("%s getting info for %d\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) { 169b1446bdaSMark Brown ksft_exit_fail_msg("snd_ctl_poll_descriptors() failed for %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 */ 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; 191b1446bdaSMark Brown int count, 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 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)) { 3223f48b137SMark Brown ksft_print_msg("%s.%d value %lld more than maximum %lld\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 */ 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 */ 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 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 430c92b576aSMark Brown static void test_ctl_name(struct ctl_data *ctl) 431c92b576aSMark Brown { 432c92b576aSMark Brown bool name_ok = true; 433c92b576aSMark Brown bool check; 434c92b576aSMark Brown 435eae87251SMark Brown ksft_print_msg("%d.%d %s\n", ctl->card->card, ctl->elem, 436eae87251SMark Brown ctl->name); 437eae87251SMark Brown 438c92b576aSMark Brown /* Only boolean controls should end in Switch */ 439c92b576aSMark Brown if (strend(ctl->name, " Switch")) { 440c92b576aSMark Brown if (snd_ctl_elem_info_get_type(ctl->info) != SND_CTL_ELEM_TYPE_BOOLEAN) { 441c92b576aSMark Brown ksft_print_msg("%d.%d %s ends in Switch but is not boolean\n", 442c92b576aSMark Brown ctl->card->card, ctl->elem, ctl->name); 443c92b576aSMark Brown name_ok = false; 444c92b576aSMark Brown } 445c92b576aSMark Brown } 446c92b576aSMark Brown 447c92b576aSMark Brown /* Writeable boolean controls should end in Switch */ 448c92b576aSMark Brown if (snd_ctl_elem_info_get_type(ctl->info) == SND_CTL_ELEM_TYPE_BOOLEAN && 449c92b576aSMark Brown snd_ctl_elem_info_is_writable(ctl->info)) { 450c92b576aSMark Brown if (!strend(ctl->name, " Switch")) { 451c92b576aSMark Brown ksft_print_msg("%d.%d %s is a writeable boolean but not a Switch\n", 452c92b576aSMark Brown ctl->card->card, ctl->elem, ctl->name); 453c92b576aSMark Brown name_ok = false; 454c92b576aSMark Brown } 455c92b576aSMark Brown } 456c92b576aSMark Brown 457c92b576aSMark Brown ksft_test_result(name_ok, "name.%d.%d\n", 458c92b576aSMark Brown ctl->card->card, ctl->elem); 459c92b576aSMark Brown } 460c92b576aSMark Brown 461*05a2cdfeSMark Brown static void show_values(struct ctl_data *ctl, snd_ctl_elem_value_t *orig_val, 462*05a2cdfeSMark Brown snd_ctl_elem_value_t *read_val) 463*05a2cdfeSMark Brown { 464*05a2cdfeSMark Brown long long orig_int, read_int; 465*05a2cdfeSMark Brown int i; 466*05a2cdfeSMark Brown 467*05a2cdfeSMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 468*05a2cdfeSMark Brown switch (snd_ctl_elem_info_get_type(ctl->info)) { 469*05a2cdfeSMark Brown case SND_CTL_ELEM_TYPE_BOOLEAN: 470*05a2cdfeSMark Brown orig_int = snd_ctl_elem_value_get_boolean(orig_val, i); 471*05a2cdfeSMark Brown read_int = snd_ctl_elem_value_get_boolean(read_val, i); 472*05a2cdfeSMark Brown break; 473*05a2cdfeSMark Brown 474*05a2cdfeSMark Brown case SND_CTL_ELEM_TYPE_INTEGER: 475*05a2cdfeSMark Brown orig_int = snd_ctl_elem_value_get_integer(orig_val, i); 476*05a2cdfeSMark Brown read_int = snd_ctl_elem_value_get_integer(read_val, i); 477*05a2cdfeSMark Brown break; 478*05a2cdfeSMark Brown 479*05a2cdfeSMark Brown case SND_CTL_ELEM_TYPE_INTEGER64: 480*05a2cdfeSMark Brown orig_int = snd_ctl_elem_value_get_integer64(orig_val, 481*05a2cdfeSMark Brown i); 482*05a2cdfeSMark Brown read_int = snd_ctl_elem_value_get_integer64(read_val, 483*05a2cdfeSMark Brown i); 484*05a2cdfeSMark Brown break; 485*05a2cdfeSMark Brown 486*05a2cdfeSMark Brown case SND_CTL_ELEM_TYPE_ENUMERATED: 487*05a2cdfeSMark Brown orig_int = snd_ctl_elem_value_get_enumerated(orig_val, 488*05a2cdfeSMark Brown i); 489*05a2cdfeSMark Brown read_int = snd_ctl_elem_value_get_enumerated(read_val, 490*05a2cdfeSMark Brown i); 491*05a2cdfeSMark Brown break; 492*05a2cdfeSMark Brown 493*05a2cdfeSMark Brown default: 494*05a2cdfeSMark Brown return; 495*05a2cdfeSMark Brown } 496*05a2cdfeSMark Brown 497*05a2cdfeSMark Brown ksft_print_msg("%s.%d orig %lld read %lld, is_volatile %d\n", 498*05a2cdfeSMark Brown ctl->name, i, orig_int, read_int, 499*05a2cdfeSMark Brown snd_ctl_elem_info_is_volatile(ctl->info)); 500*05a2cdfeSMark Brown } 501*05a2cdfeSMark Brown } 502*05a2cdfeSMark Brown 5039d73d192SMark Brown static bool show_mismatch(struct ctl_data *ctl, int index, 5045aaf9effSMark Brown snd_ctl_elem_value_t *read_val, 5055aaf9effSMark Brown snd_ctl_elem_value_t *expected_val) 5065aaf9effSMark Brown { 5075aaf9effSMark Brown long long expected_int, read_int; 5085aaf9effSMark Brown 5095aaf9effSMark Brown /* 5105aaf9effSMark Brown * We factor out the code to compare values representable as 5115aaf9effSMark Brown * integers, ensure that check doesn't log otherwise. 5125aaf9effSMark Brown */ 5135aaf9effSMark Brown expected_int = 0; 5145aaf9effSMark Brown read_int = 0; 5155aaf9effSMark Brown 5165aaf9effSMark Brown switch (snd_ctl_elem_info_get_type(ctl->info)) { 5175aaf9effSMark Brown case SND_CTL_ELEM_TYPE_BOOLEAN: 5185aaf9effSMark Brown expected_int = snd_ctl_elem_value_get_boolean(expected_val, 5195aaf9effSMark Brown index); 5205aaf9effSMark Brown read_int = snd_ctl_elem_value_get_boolean(read_val, index); 5215aaf9effSMark Brown break; 5225aaf9effSMark Brown 5235aaf9effSMark Brown case SND_CTL_ELEM_TYPE_INTEGER: 5245aaf9effSMark Brown expected_int = snd_ctl_elem_value_get_integer(expected_val, 5255aaf9effSMark Brown index); 5265aaf9effSMark Brown read_int = snd_ctl_elem_value_get_integer(read_val, index); 5275aaf9effSMark Brown break; 5285aaf9effSMark Brown 5295aaf9effSMark Brown case SND_CTL_ELEM_TYPE_INTEGER64: 5305aaf9effSMark Brown expected_int = snd_ctl_elem_value_get_integer64(expected_val, 5315aaf9effSMark Brown index); 5325aaf9effSMark Brown read_int = snd_ctl_elem_value_get_integer64(read_val, 5335aaf9effSMark Brown index); 5345aaf9effSMark Brown break; 5355aaf9effSMark Brown 5365aaf9effSMark Brown case SND_CTL_ELEM_TYPE_ENUMERATED: 5375aaf9effSMark Brown expected_int = snd_ctl_elem_value_get_enumerated(expected_val, 5385aaf9effSMark Brown index); 5395aaf9effSMark Brown read_int = snd_ctl_elem_value_get_enumerated(read_val, 5405aaf9effSMark Brown index); 5415aaf9effSMark Brown break; 5425aaf9effSMark Brown 5435aaf9effSMark Brown default: 5445aaf9effSMark Brown break; 5455aaf9effSMark Brown } 5465aaf9effSMark Brown 5475aaf9effSMark Brown if (expected_int != read_int) { 5487cc994f2STakashi Sakamoto /* 5497cc994f2STakashi Sakamoto * NOTE: The volatile attribute means that the hardware 5507cc994f2STakashi Sakamoto * can voluntarily change the state of control element 5517cc994f2STakashi Sakamoto * independent of any operation by software. 5527cc994f2STakashi Sakamoto */ 5537cc994f2STakashi Sakamoto bool is_volatile = snd_ctl_elem_info_is_volatile(ctl->info); 5547cc994f2STakashi Sakamoto ksft_print_msg("%s.%d expected %lld but read %lld, is_volatile %d\n", 5557cc994f2STakashi Sakamoto ctl->name, index, expected_int, read_int, is_volatile); 5567cc994f2STakashi Sakamoto return !is_volatile; 5575aaf9effSMark Brown } else { 5585aaf9effSMark Brown return false; 5595aaf9effSMark Brown } 5605aaf9effSMark Brown } 5615aaf9effSMark Brown 5625aaf9effSMark Brown /* 5635aaf9effSMark Brown * Write a value then if possible verify that we get the expected 5645aaf9effSMark Brown * result. An optional expected value can be provided if we expect 5655aaf9effSMark Brown * the write to fail, for verifying that invalid writes don't corrupt 5665aaf9effSMark Brown * anything. 5675aaf9effSMark Brown */ 5689d73d192SMark Brown static int write_and_verify(struct ctl_data *ctl, 5695aaf9effSMark Brown snd_ctl_elem_value_t *write_val, 5705aaf9effSMark Brown snd_ctl_elem_value_t *expected_val) 5715aaf9effSMark Brown { 5725aaf9effSMark Brown int err, i; 5735aaf9effSMark Brown bool error_expected, mismatch_shown; 574b1446bdaSMark Brown snd_ctl_elem_value_t *initial_val, *read_val, *w_val; 575b1446bdaSMark Brown snd_ctl_elem_value_alloca(&initial_val); 5765aaf9effSMark Brown snd_ctl_elem_value_alloca(&read_val); 5775aaf9effSMark Brown snd_ctl_elem_value_alloca(&w_val); 5785aaf9effSMark Brown 5795aaf9effSMark Brown /* 5805aaf9effSMark Brown * We need to copy the write value since writing can modify 5815aaf9effSMark Brown * the value which causes surprises, and allocate an expected 5825aaf9effSMark Brown * value if we expect to read back what we wrote. 5835aaf9effSMark Brown */ 5845aaf9effSMark Brown snd_ctl_elem_value_copy(w_val, write_val); 5855aaf9effSMark Brown if (expected_val) { 5865aaf9effSMark Brown error_expected = true; 5875aaf9effSMark Brown } else { 5885aaf9effSMark Brown error_expected = false; 5895aaf9effSMark Brown snd_ctl_elem_value_alloca(&expected_val); 5905aaf9effSMark Brown snd_ctl_elem_value_copy(expected_val, write_val); 5915aaf9effSMark Brown } 5925aaf9effSMark Brown 593b1446bdaSMark Brown /* Store the value before we write */ 594b1446bdaSMark Brown if (snd_ctl_elem_info_is_readable(ctl->info)) { 595b1446bdaSMark Brown snd_ctl_elem_value_set_id(initial_val, ctl->id); 596b1446bdaSMark Brown 597b1446bdaSMark Brown err = snd_ctl_elem_read(ctl->card->handle, initial_val); 598b1446bdaSMark Brown if (err < 0) { 599b1446bdaSMark Brown ksft_print_msg("snd_ctl_elem_read() failed: %s\n", 600b1446bdaSMark Brown snd_strerror(err)); 601b1446bdaSMark Brown return err; 602b1446bdaSMark Brown } 603b1446bdaSMark Brown } 604b1446bdaSMark Brown 6055aaf9effSMark Brown /* 6065aaf9effSMark Brown * Do the write, if we have an expected value ignore the error 6075aaf9effSMark Brown * and carry on to validate the expected value. 6085aaf9effSMark Brown */ 6095aaf9effSMark Brown err = snd_ctl_elem_write(ctl->card->handle, w_val); 6105aaf9effSMark Brown if (err < 0 && !error_expected) { 6115aaf9effSMark Brown ksft_print_msg("snd_ctl_elem_write() failed: %s\n", 6125aaf9effSMark Brown snd_strerror(err)); 6135aaf9effSMark Brown return err; 6145aaf9effSMark Brown } 6155aaf9effSMark Brown 6165aaf9effSMark Brown /* Can we do the verification part? */ 6175aaf9effSMark Brown if (!snd_ctl_elem_info_is_readable(ctl->info)) 6185aaf9effSMark Brown return err; 6195aaf9effSMark Brown 6205aaf9effSMark Brown snd_ctl_elem_value_set_id(read_val, ctl->id); 6215aaf9effSMark Brown 6225aaf9effSMark Brown err = snd_ctl_elem_read(ctl->card->handle, read_val); 6235aaf9effSMark Brown if (err < 0) { 6245aaf9effSMark Brown ksft_print_msg("snd_ctl_elem_read() failed: %s\n", 6255aaf9effSMark Brown snd_strerror(err)); 6265aaf9effSMark Brown return err; 6275aaf9effSMark Brown } 6285aaf9effSMark Brown 6295aaf9effSMark Brown /* 630b1446bdaSMark Brown * Check for an event if the value changed, or confirm that 631b1446bdaSMark Brown * there was none if it didn't. We rely on the kernel 632b1446bdaSMark Brown * generating the notification before it returns from the 633b1446bdaSMark Brown * write, this is currently true, should that ever change this 634b1446bdaSMark Brown * will most likely break and need updating. 635b1446bdaSMark Brown */ 636b1446bdaSMark Brown if (!snd_ctl_elem_info_is_volatile(ctl->info)) { 637b1446bdaSMark Brown err = wait_for_event(ctl, 0); 638b1446bdaSMark Brown if (snd_ctl_elem_value_compare(initial_val, read_val)) { 639b1446bdaSMark Brown if (err < 1) { 640b1446bdaSMark Brown ksft_print_msg("No event generated for %s\n", 641b1446bdaSMark Brown ctl->name); 642*05a2cdfeSMark Brown show_values(ctl, initial_val, read_val); 643b1446bdaSMark Brown ctl->event_missing++; 644b1446bdaSMark Brown } 645b1446bdaSMark Brown } else { 646b1446bdaSMark Brown if (err != 0) { 647b1446bdaSMark Brown ksft_print_msg("Spurious event generated for %s\n", 648b1446bdaSMark Brown ctl->name); 649*05a2cdfeSMark Brown show_values(ctl, initial_val, read_val); 650b1446bdaSMark Brown ctl->event_spurious++; 651b1446bdaSMark Brown } 652b1446bdaSMark Brown } 653b1446bdaSMark Brown } 654b1446bdaSMark Brown 655b1446bdaSMark Brown /* 6565aaf9effSMark Brown * Use the libray to compare values, if there's a mismatch 6575aaf9effSMark Brown * carry on and try to provide a more useful diagnostic than 6585aaf9effSMark Brown * just "mismatch". 6595aaf9effSMark Brown */ 6605aaf9effSMark Brown if (!snd_ctl_elem_value_compare(expected_val, read_val)) 6615aaf9effSMark Brown return 0; 6625aaf9effSMark Brown 6635aaf9effSMark Brown mismatch_shown = false; 6645aaf9effSMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) 6655aaf9effSMark Brown if (show_mismatch(ctl, i, read_val, expected_val)) 6665aaf9effSMark Brown mismatch_shown = true; 6675aaf9effSMark Brown 6685aaf9effSMark Brown if (!mismatch_shown) 6695aaf9effSMark Brown ksft_print_msg("%s read and written values differ\n", 6705aaf9effSMark Brown ctl->name); 6715aaf9effSMark Brown 6725aaf9effSMark Brown return -1; 6735aaf9effSMark Brown } 6745aaf9effSMark Brown 6755aaf9effSMark Brown /* 6765aaf9effSMark Brown * Make sure we can write the default value back to the control, this 6775aaf9effSMark Brown * should validate that at least some write works. 6785aaf9effSMark Brown */ 6799d73d192SMark Brown static void test_ctl_write_default(struct ctl_data *ctl) 6805aaf9effSMark Brown { 6815aaf9effSMark Brown int err; 6825aaf9effSMark Brown 6835aaf9effSMark Brown /* If the control is turned off let's be polite */ 6845aaf9effSMark Brown if (snd_ctl_elem_info_is_inactive(ctl->info)) { 6855aaf9effSMark Brown ksft_print_msg("%s is inactive\n", ctl->name); 6865aaf9effSMark Brown ksft_test_result_skip("write_default.%d.%d\n", 6875aaf9effSMark Brown ctl->card->card, ctl->elem); 6885aaf9effSMark Brown return; 6895aaf9effSMark Brown } 6905aaf9effSMark Brown 6915aaf9effSMark Brown if (!snd_ctl_elem_info_is_writable(ctl->info)) { 6925aaf9effSMark Brown ksft_print_msg("%s is not writeable\n", ctl->name); 6935aaf9effSMark Brown ksft_test_result_skip("write_default.%d.%d\n", 6945aaf9effSMark Brown ctl->card->card, ctl->elem); 6955aaf9effSMark Brown return; 6965aaf9effSMark Brown } 6975aaf9effSMark Brown 6985aaf9effSMark Brown /* No idea what the default was for unreadable controls */ 6995aaf9effSMark Brown if (!snd_ctl_elem_info_is_readable(ctl->info)) { 7005aaf9effSMark Brown ksft_print_msg("%s couldn't read default\n", ctl->name); 7015aaf9effSMark Brown ksft_test_result_skip("write_default.%d.%d\n", 7025aaf9effSMark Brown ctl->card->card, ctl->elem); 7035aaf9effSMark Brown return; 7045aaf9effSMark Brown } 7055aaf9effSMark Brown 7065aaf9effSMark Brown err = write_and_verify(ctl, ctl->def_val, NULL); 7075aaf9effSMark Brown 7085aaf9effSMark Brown ksft_test_result(err >= 0, "write_default.%d.%d\n", 7095aaf9effSMark Brown ctl->card->card, ctl->elem); 7105aaf9effSMark Brown } 7115aaf9effSMark Brown 7129d73d192SMark Brown static bool test_ctl_write_valid_boolean(struct ctl_data *ctl) 7135aaf9effSMark Brown { 7145aaf9effSMark Brown int err, i, j; 7155aaf9effSMark Brown bool fail = false; 7165aaf9effSMark Brown snd_ctl_elem_value_t *val; 7175aaf9effSMark Brown snd_ctl_elem_value_alloca(&val); 7185aaf9effSMark Brown 7195aaf9effSMark Brown snd_ctl_elem_value_set_id(val, ctl->id); 7205aaf9effSMark Brown 7215aaf9effSMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 7225aaf9effSMark Brown for (j = 0; j < 2; j++) { 7235aaf9effSMark Brown snd_ctl_elem_value_set_boolean(val, i, j); 7245aaf9effSMark Brown err = write_and_verify(ctl, val, NULL); 7255aaf9effSMark Brown if (err != 0) 7265aaf9effSMark Brown fail = true; 7275aaf9effSMark Brown } 7285aaf9effSMark Brown } 7295aaf9effSMark Brown 7305aaf9effSMark Brown return !fail; 7315aaf9effSMark Brown } 7325aaf9effSMark Brown 7339d73d192SMark Brown static bool test_ctl_write_valid_integer(struct ctl_data *ctl) 7345aaf9effSMark Brown { 7355aaf9effSMark Brown int err; 7365aaf9effSMark Brown int i; 7375aaf9effSMark Brown long j, step; 7385aaf9effSMark Brown bool fail = false; 7395aaf9effSMark Brown snd_ctl_elem_value_t *val; 7405aaf9effSMark Brown snd_ctl_elem_value_alloca(&val); 7415aaf9effSMark Brown 7425aaf9effSMark Brown snd_ctl_elem_value_set_id(val, ctl->id); 7435aaf9effSMark Brown 7445aaf9effSMark Brown step = snd_ctl_elem_info_get_step(ctl->info); 7455aaf9effSMark Brown if (!step) 7465aaf9effSMark Brown step = 1; 7475aaf9effSMark Brown 7485aaf9effSMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 7495aaf9effSMark Brown for (j = snd_ctl_elem_info_get_min(ctl->info); 7505aaf9effSMark Brown j <= snd_ctl_elem_info_get_max(ctl->info); j += step) { 7515aaf9effSMark Brown 7525aaf9effSMark Brown snd_ctl_elem_value_set_integer(val, i, j); 7535aaf9effSMark Brown err = write_and_verify(ctl, val, NULL); 7545aaf9effSMark Brown if (err != 0) 7555aaf9effSMark Brown fail = true; 7565aaf9effSMark Brown } 7575aaf9effSMark Brown } 7585aaf9effSMark Brown 7595aaf9effSMark Brown 7605aaf9effSMark Brown return !fail; 7615aaf9effSMark Brown } 7625aaf9effSMark Brown 7639d73d192SMark Brown static bool test_ctl_write_valid_integer64(struct ctl_data *ctl) 7645aaf9effSMark Brown { 7655aaf9effSMark Brown int err, i; 7665aaf9effSMark Brown long long j, step; 7675aaf9effSMark Brown bool fail = false; 7685aaf9effSMark Brown snd_ctl_elem_value_t *val; 7695aaf9effSMark Brown snd_ctl_elem_value_alloca(&val); 7705aaf9effSMark Brown 7715aaf9effSMark Brown snd_ctl_elem_value_set_id(val, ctl->id); 7725aaf9effSMark Brown 7735aaf9effSMark Brown step = snd_ctl_elem_info_get_step64(ctl->info); 7745aaf9effSMark Brown if (!step) 7755aaf9effSMark Brown step = 1; 7765aaf9effSMark Brown 7775aaf9effSMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 7785aaf9effSMark Brown for (j = snd_ctl_elem_info_get_min64(ctl->info); 7795aaf9effSMark Brown j <= snd_ctl_elem_info_get_max64(ctl->info); j += step) { 7805aaf9effSMark Brown 7815aaf9effSMark Brown snd_ctl_elem_value_set_integer64(val, i, j); 7825aaf9effSMark Brown err = write_and_verify(ctl, val, NULL); 7835aaf9effSMark Brown if (err != 0) 7845aaf9effSMark Brown fail = true; 7855aaf9effSMark Brown } 7865aaf9effSMark Brown } 7875aaf9effSMark Brown 7885aaf9effSMark Brown return !fail; 7895aaf9effSMark Brown } 7905aaf9effSMark Brown 7919d73d192SMark Brown static bool test_ctl_write_valid_enumerated(struct ctl_data *ctl) 7925aaf9effSMark Brown { 7935aaf9effSMark Brown int err, i, j; 7945aaf9effSMark Brown bool fail = false; 7955aaf9effSMark Brown snd_ctl_elem_value_t *val; 7965aaf9effSMark Brown snd_ctl_elem_value_alloca(&val); 7975aaf9effSMark Brown 7985aaf9effSMark Brown snd_ctl_elem_value_set_id(val, ctl->id); 7995aaf9effSMark Brown 8005aaf9effSMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 8015aaf9effSMark Brown for (j = 0; j < snd_ctl_elem_info_get_items(ctl->info); j++) { 8025aaf9effSMark Brown snd_ctl_elem_value_set_enumerated(val, i, j); 8035aaf9effSMark Brown err = write_and_verify(ctl, val, NULL); 8045aaf9effSMark Brown if (err != 0) 8055aaf9effSMark Brown fail = true; 8065aaf9effSMark Brown } 8075aaf9effSMark Brown } 8085aaf9effSMark Brown 8095aaf9effSMark Brown return !fail; 8105aaf9effSMark Brown } 8115aaf9effSMark Brown 8129d73d192SMark Brown static void test_ctl_write_valid(struct ctl_data *ctl) 8135aaf9effSMark Brown { 8145aaf9effSMark Brown bool pass; 8155aaf9effSMark Brown 8165aaf9effSMark Brown /* If the control is turned off let's be polite */ 8175aaf9effSMark Brown if (snd_ctl_elem_info_is_inactive(ctl->info)) { 8185aaf9effSMark Brown ksft_print_msg("%s is inactive\n", ctl->name); 8195aaf9effSMark Brown ksft_test_result_skip("write_valid.%d.%d\n", 8205aaf9effSMark Brown ctl->card->card, ctl->elem); 8215aaf9effSMark Brown return; 8225aaf9effSMark Brown } 8235aaf9effSMark Brown 8245aaf9effSMark Brown if (!snd_ctl_elem_info_is_writable(ctl->info)) { 8255aaf9effSMark Brown ksft_print_msg("%s is not writeable\n", ctl->name); 8265aaf9effSMark Brown ksft_test_result_skip("write_valid.%d.%d\n", 8275aaf9effSMark Brown ctl->card->card, ctl->elem); 8285aaf9effSMark Brown return; 8295aaf9effSMark Brown } 8305aaf9effSMark Brown 8315aaf9effSMark Brown switch (snd_ctl_elem_info_get_type(ctl->info)) { 8325aaf9effSMark Brown case SND_CTL_ELEM_TYPE_BOOLEAN: 8335aaf9effSMark Brown pass = test_ctl_write_valid_boolean(ctl); 8345aaf9effSMark Brown break; 8355aaf9effSMark Brown 8365aaf9effSMark Brown case SND_CTL_ELEM_TYPE_INTEGER: 8375aaf9effSMark Brown pass = test_ctl_write_valid_integer(ctl); 8385aaf9effSMark Brown break; 8395aaf9effSMark Brown 8405aaf9effSMark Brown case SND_CTL_ELEM_TYPE_INTEGER64: 8415aaf9effSMark Brown pass = test_ctl_write_valid_integer64(ctl); 8425aaf9effSMark Brown break; 8435aaf9effSMark Brown 8445aaf9effSMark Brown case SND_CTL_ELEM_TYPE_ENUMERATED: 8455aaf9effSMark Brown pass = test_ctl_write_valid_enumerated(ctl); 8465aaf9effSMark Brown break; 8475aaf9effSMark Brown 8485aaf9effSMark Brown default: 8495aaf9effSMark Brown /* No tests for this yet */ 8505aaf9effSMark Brown ksft_test_result_skip("write_valid.%d.%d\n", 8515aaf9effSMark Brown ctl->card->card, ctl->elem); 8525aaf9effSMark Brown return; 8535aaf9effSMark Brown } 8545aaf9effSMark Brown 8555aaf9effSMark Brown /* Restore the default value to minimise disruption */ 8567c79b10bSMark Brown write_and_verify(ctl, ctl->def_val, NULL); 8575aaf9effSMark Brown 8585aaf9effSMark Brown ksft_test_result(pass, "write_valid.%d.%d\n", 8595aaf9effSMark Brown ctl->card->card, ctl->elem); 8605aaf9effSMark Brown } 8615aaf9effSMark Brown 8629d73d192SMark Brown static bool test_ctl_write_invalid_value(struct ctl_data *ctl, 86388b61322SMark Brown snd_ctl_elem_value_t *val) 86488b61322SMark Brown { 86588b61322SMark Brown int err; 86688b61322SMark Brown long val_read; 86788b61322SMark Brown 86888b61322SMark Brown /* Ideally this will fail... */ 86988b61322SMark Brown err = snd_ctl_elem_write(ctl->card->handle, val); 87088b61322SMark Brown if (err < 0) 87188b61322SMark Brown return false; 87288b61322SMark Brown 87388b61322SMark Brown /* ...but some devices will clamp to an in range value */ 87488b61322SMark Brown err = snd_ctl_elem_read(ctl->card->handle, val); 87588b61322SMark Brown if (err < 0) { 87688b61322SMark Brown ksft_print_msg("%s failed to read: %s\n", 87788b61322SMark Brown ctl->name, snd_strerror(err)); 87888b61322SMark Brown return true; 87988b61322SMark Brown } 88088b61322SMark Brown 88188b61322SMark Brown return !ctl_value_valid(ctl, val); 88288b61322SMark Brown } 88388b61322SMark Brown 8849d73d192SMark Brown static bool test_ctl_write_invalid_boolean(struct ctl_data *ctl) 88588b61322SMark Brown { 88688b61322SMark Brown int err, i; 88788b61322SMark Brown long val_read; 88888b61322SMark Brown bool fail = false; 88988b61322SMark Brown snd_ctl_elem_value_t *val; 89088b61322SMark Brown snd_ctl_elem_value_alloca(&val); 89188b61322SMark Brown 89288b61322SMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 89388b61322SMark Brown snd_ctl_elem_value_copy(val, ctl->def_val); 89488b61322SMark Brown snd_ctl_elem_value_set_boolean(val, i, 2); 89588b61322SMark Brown 89688b61322SMark Brown if (test_ctl_write_invalid_value(ctl, val)) 89788b61322SMark Brown fail = true; 89888b61322SMark Brown } 89988b61322SMark Brown 90088b61322SMark Brown return !fail; 90188b61322SMark Brown } 90288b61322SMark Brown 9039d73d192SMark Brown static bool test_ctl_write_invalid_integer(struct ctl_data *ctl) 90488b61322SMark Brown { 90588b61322SMark Brown int i; 90688b61322SMark Brown bool fail = false; 90788b61322SMark Brown snd_ctl_elem_value_t *val; 90888b61322SMark Brown snd_ctl_elem_value_alloca(&val); 90988b61322SMark Brown 91088b61322SMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 91188b61322SMark Brown if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) { 91288b61322SMark Brown /* Just under range */ 91388b61322SMark Brown snd_ctl_elem_value_copy(val, ctl->def_val); 91488b61322SMark Brown snd_ctl_elem_value_set_integer(val, i, 91588b61322SMark Brown snd_ctl_elem_info_get_min(ctl->info) - 1); 91688b61322SMark Brown 91788b61322SMark Brown if (test_ctl_write_invalid_value(ctl, val)) 91888b61322SMark Brown fail = true; 91988b61322SMark Brown 92088b61322SMark Brown /* Minimum representable value */ 92188b61322SMark Brown snd_ctl_elem_value_copy(val, ctl->def_val); 92288b61322SMark Brown snd_ctl_elem_value_set_integer(val, i, LONG_MIN); 92388b61322SMark Brown 92488b61322SMark Brown if (test_ctl_write_invalid_value(ctl, val)) 92588b61322SMark Brown fail = true; 92688b61322SMark Brown } 92788b61322SMark Brown 92888b61322SMark Brown if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) { 92988b61322SMark Brown /* Just over range */ 93088b61322SMark Brown snd_ctl_elem_value_copy(val, ctl->def_val); 93188b61322SMark Brown snd_ctl_elem_value_set_integer(val, i, 93288b61322SMark Brown snd_ctl_elem_info_get_max(ctl->info) + 1); 93388b61322SMark Brown 93488b61322SMark Brown if (test_ctl_write_invalid_value(ctl, val)) 93588b61322SMark Brown fail = true; 93688b61322SMark Brown 93788b61322SMark Brown /* Maximum representable value */ 93888b61322SMark Brown snd_ctl_elem_value_copy(val, ctl->def_val); 93988b61322SMark Brown snd_ctl_elem_value_set_integer(val, i, LONG_MAX); 94088b61322SMark Brown 94188b61322SMark Brown if (test_ctl_write_invalid_value(ctl, val)) 94288b61322SMark Brown fail = true; 94388b61322SMark Brown } 94488b61322SMark Brown } 94588b61322SMark Brown 94688b61322SMark Brown return !fail; 94788b61322SMark Brown } 94888b61322SMark Brown 9499d73d192SMark Brown static bool test_ctl_write_invalid_integer64(struct ctl_data *ctl) 95088b61322SMark Brown { 95188b61322SMark Brown int i; 95288b61322SMark Brown bool fail = false; 95388b61322SMark Brown snd_ctl_elem_value_t *val; 95488b61322SMark Brown snd_ctl_elem_value_alloca(&val); 95588b61322SMark Brown 95688b61322SMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 95788b61322SMark Brown if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) { 95888b61322SMark Brown /* Just under range */ 95988b61322SMark Brown snd_ctl_elem_value_copy(val, ctl->def_val); 96088b61322SMark Brown snd_ctl_elem_value_set_integer64(val, i, 96188b61322SMark Brown snd_ctl_elem_info_get_min64(ctl->info) - 1); 96288b61322SMark Brown 96388b61322SMark Brown if (test_ctl_write_invalid_value(ctl, val)) 96488b61322SMark Brown fail = true; 96588b61322SMark Brown 96688b61322SMark Brown /* Minimum representable value */ 96788b61322SMark Brown snd_ctl_elem_value_copy(val, ctl->def_val); 96888b61322SMark Brown snd_ctl_elem_value_set_integer64(val, i, LLONG_MIN); 96988b61322SMark Brown 97088b61322SMark Brown if (test_ctl_write_invalid_value(ctl, val)) 97188b61322SMark Brown fail = true; 97288b61322SMark Brown } 97388b61322SMark Brown 97488b61322SMark Brown if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) { 97588b61322SMark Brown /* Just over range */ 97688b61322SMark Brown snd_ctl_elem_value_copy(val, ctl->def_val); 97788b61322SMark Brown snd_ctl_elem_value_set_integer64(val, i, 97888b61322SMark Brown snd_ctl_elem_info_get_max64(ctl->info) + 1); 97988b61322SMark Brown 98088b61322SMark Brown if (test_ctl_write_invalid_value(ctl, val)) 98188b61322SMark Brown fail = true; 98288b61322SMark Brown 98388b61322SMark Brown /* Maximum representable value */ 98488b61322SMark Brown snd_ctl_elem_value_copy(val, ctl->def_val); 98588b61322SMark Brown snd_ctl_elem_value_set_integer64(val, i, LLONG_MAX); 98688b61322SMark Brown 98788b61322SMark Brown if (test_ctl_write_invalid_value(ctl, val)) 98888b61322SMark Brown fail = true; 98988b61322SMark Brown } 99088b61322SMark Brown } 99188b61322SMark Brown 99288b61322SMark Brown return !fail; 99388b61322SMark Brown } 99488b61322SMark Brown 9959d73d192SMark Brown static bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl) 99688b61322SMark Brown { 99788b61322SMark Brown int err, i; 99888b61322SMark Brown unsigned int val_read; 99988b61322SMark Brown bool fail = false; 100088b61322SMark Brown snd_ctl_elem_value_t *val; 100188b61322SMark Brown snd_ctl_elem_value_alloca(&val); 100288b61322SMark Brown 100388b61322SMark Brown snd_ctl_elem_value_set_id(val, ctl->id); 100488b61322SMark Brown 100588b61322SMark Brown for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 100688b61322SMark Brown /* One beyond maximum */ 100788b61322SMark Brown snd_ctl_elem_value_copy(val, ctl->def_val); 100888b61322SMark Brown snd_ctl_elem_value_set_enumerated(val, i, 100988b61322SMark Brown snd_ctl_elem_info_get_items(ctl->info)); 101088b61322SMark Brown 101188b61322SMark Brown if (test_ctl_write_invalid_value(ctl, val)) 101288b61322SMark Brown fail = true; 101388b61322SMark Brown 101488b61322SMark Brown /* Maximum representable value */ 101588b61322SMark Brown snd_ctl_elem_value_copy(val, ctl->def_val); 101688b61322SMark Brown snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX); 101788b61322SMark Brown 101888b61322SMark Brown if (test_ctl_write_invalid_value(ctl, val)) 101988b61322SMark Brown fail = true; 102088b61322SMark Brown 102188b61322SMark Brown } 102288b61322SMark Brown 102388b61322SMark Brown return !fail; 102488b61322SMark Brown } 102588b61322SMark Brown 102688b61322SMark Brown 10279d73d192SMark Brown static void test_ctl_write_invalid(struct ctl_data *ctl) 102888b61322SMark Brown { 102988b61322SMark Brown bool pass; 103088b61322SMark Brown int err; 103188b61322SMark Brown 103288b61322SMark Brown /* If the control is turned off let's be polite */ 103388b61322SMark Brown if (snd_ctl_elem_info_is_inactive(ctl->info)) { 103488b61322SMark Brown ksft_print_msg("%s is inactive\n", ctl->name); 103588b61322SMark Brown ksft_test_result_skip("write_invalid.%d.%d\n", 103688b61322SMark Brown ctl->card->card, ctl->elem); 103788b61322SMark Brown return; 103888b61322SMark Brown } 103988b61322SMark Brown 104088b61322SMark Brown if (!snd_ctl_elem_info_is_writable(ctl->info)) { 104188b61322SMark Brown ksft_print_msg("%s is not writeable\n", ctl->name); 104288b61322SMark Brown ksft_test_result_skip("write_invalid.%d.%d\n", 104388b61322SMark Brown ctl->card->card, ctl->elem); 104488b61322SMark Brown return; 104588b61322SMark Brown } 104688b61322SMark Brown 104788b61322SMark Brown switch (snd_ctl_elem_info_get_type(ctl->info)) { 104888b61322SMark Brown case SND_CTL_ELEM_TYPE_BOOLEAN: 104988b61322SMark Brown pass = test_ctl_write_invalid_boolean(ctl); 105088b61322SMark Brown break; 105188b61322SMark Brown 105288b61322SMark Brown case SND_CTL_ELEM_TYPE_INTEGER: 105388b61322SMark Brown pass = test_ctl_write_invalid_integer(ctl); 105488b61322SMark Brown break; 105588b61322SMark Brown 105688b61322SMark Brown case SND_CTL_ELEM_TYPE_INTEGER64: 105788b61322SMark Brown pass = test_ctl_write_invalid_integer64(ctl); 105888b61322SMark Brown break; 105988b61322SMark Brown 106088b61322SMark Brown case SND_CTL_ELEM_TYPE_ENUMERATED: 106188b61322SMark Brown pass = test_ctl_write_invalid_enumerated(ctl); 106288b61322SMark Brown break; 106388b61322SMark Brown 106488b61322SMark Brown default: 106588b61322SMark Brown /* No tests for this yet */ 106688b61322SMark Brown ksft_test_result_skip("write_invalid.%d.%d\n", 106788b61322SMark Brown ctl->card->card, ctl->elem); 106888b61322SMark Brown return; 106988b61322SMark Brown } 107088b61322SMark Brown 107188b61322SMark Brown /* Restore the default value to minimise disruption */ 10727c79b10bSMark Brown write_and_verify(ctl, ctl->def_val, NULL); 107388b61322SMark Brown 107488b61322SMark Brown ksft_test_result(pass, "write_invalid.%d.%d\n", 107588b61322SMark Brown ctl->card->card, ctl->elem); 107688b61322SMark Brown } 107788b61322SMark Brown 10789d73d192SMark Brown static void test_ctl_event_missing(struct ctl_data *ctl) 1079b1446bdaSMark Brown { 1080b1446bdaSMark Brown ksft_test_result(!ctl->event_missing, "event_missing.%d.%d\n", 1081b1446bdaSMark Brown ctl->card->card, ctl->elem); 1082b1446bdaSMark Brown } 1083b1446bdaSMark Brown 10849d73d192SMark Brown static void test_ctl_event_spurious(struct ctl_data *ctl) 1085b1446bdaSMark Brown { 1086b1446bdaSMark Brown ksft_test_result(!ctl->event_spurious, "event_spurious.%d.%d\n", 1087b1446bdaSMark Brown ctl->card->card, ctl->elem); 1088b1446bdaSMark Brown } 1089b1446bdaSMark Brown 10905aaf9effSMark Brown int main(void) 10915aaf9effSMark Brown { 10925aaf9effSMark Brown struct ctl_data *ctl; 10935aaf9effSMark Brown 10945aaf9effSMark Brown ksft_print_header(); 10955aaf9effSMark Brown 10965aaf9effSMark Brown find_controls(); 10975aaf9effSMark Brown 10985aaf9effSMark Brown ksft_set_plan(num_controls * TESTS_PER_CONTROL); 10995aaf9effSMark Brown 11005aaf9effSMark Brown for (ctl = ctl_list; ctl != NULL; ctl = ctl->next) { 11015aaf9effSMark Brown /* 11025aaf9effSMark Brown * Must test get_value() before we write anything, the 11035aaf9effSMark Brown * test stores the default value for later cleanup. 11045aaf9effSMark Brown */ 11055aaf9effSMark Brown test_ctl_get_value(ctl); 1106c92b576aSMark Brown test_ctl_name(ctl); 11075aaf9effSMark Brown test_ctl_write_default(ctl); 11085aaf9effSMark Brown test_ctl_write_valid(ctl); 110988b61322SMark Brown test_ctl_write_invalid(ctl); 1110b1446bdaSMark Brown test_ctl_event_missing(ctl); 1111b1446bdaSMark Brown test_ctl_event_spurious(ctl); 11125aaf9effSMark Brown } 11135aaf9effSMark Brown 11145aaf9effSMark Brown ksft_exit_pass(); 11155aaf9effSMark Brown 11165aaf9effSMark Brown return 0; 11175aaf9effSMark Brown } 1118