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