162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// kselftest for the ALSA mixer API 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Original author: Mark Brown <broonie@kernel.org> 662306a36Sopenharmony_ci// Copyright (c) 2021-2 Arm Limited 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci// This test will iterate over all cards detected in the system, exercising 962306a36Sopenharmony_ci// every mixer control it can find. This may conflict with other system 1062306a36Sopenharmony_ci// software if there is audio activity so is best run on a system with a 1162306a36Sopenharmony_ci// minimal active userspace. 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <stdio.h> 1462306a36Sopenharmony_ci#include <stdlib.h> 1562306a36Sopenharmony_ci#include <stdbool.h> 1662306a36Sopenharmony_ci#include <limits.h> 1762306a36Sopenharmony_ci#include <string.h> 1862306a36Sopenharmony_ci#include <getopt.h> 1962306a36Sopenharmony_ci#include <stdarg.h> 2062306a36Sopenharmony_ci#include <ctype.h> 2162306a36Sopenharmony_ci#include <math.h> 2262306a36Sopenharmony_ci#include <errno.h> 2362306a36Sopenharmony_ci#include <assert.h> 2462306a36Sopenharmony_ci#include <alsa/asoundlib.h> 2562306a36Sopenharmony_ci#include <poll.h> 2662306a36Sopenharmony_ci#include <stdint.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "../kselftest.h" 2962306a36Sopenharmony_ci#include "alsa-local.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define TESTS_PER_CONTROL 7 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct card_data { 3462306a36Sopenharmony_ci snd_ctl_t *handle; 3562306a36Sopenharmony_ci int card; 3662306a36Sopenharmony_ci struct pollfd pollfd; 3762306a36Sopenharmony_ci int num_ctls; 3862306a36Sopenharmony_ci snd_ctl_elem_list_t *ctls; 3962306a36Sopenharmony_ci struct card_data *next; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct ctl_data { 4362306a36Sopenharmony_ci const char *name; 4462306a36Sopenharmony_ci snd_ctl_elem_id_t *id; 4562306a36Sopenharmony_ci snd_ctl_elem_info_t *info; 4662306a36Sopenharmony_ci snd_ctl_elem_value_t *def_val; 4762306a36Sopenharmony_ci int elem; 4862306a36Sopenharmony_ci int event_missing; 4962306a36Sopenharmony_ci int event_spurious; 5062306a36Sopenharmony_ci struct card_data *card; 5162306a36Sopenharmony_ci struct ctl_data *next; 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ciint num_cards = 0; 5562306a36Sopenharmony_ciint num_controls = 0; 5662306a36Sopenharmony_cistruct card_data *card_list = NULL; 5762306a36Sopenharmony_cistruct ctl_data *ctl_list = NULL; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void find_controls(void) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci char name[32]; 6262306a36Sopenharmony_ci int card, ctl, err; 6362306a36Sopenharmony_ci struct card_data *card_data; 6462306a36Sopenharmony_ci struct ctl_data *ctl_data; 6562306a36Sopenharmony_ci snd_config_t *config; 6662306a36Sopenharmony_ci char *card_name, *card_longname; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci card = -1; 6962306a36Sopenharmony_ci if (snd_card_next(&card) < 0 || card < 0) 7062306a36Sopenharmony_ci return; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci config = get_alsalib_config(); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci while (card >= 0) { 7562306a36Sopenharmony_ci sprintf(name, "hw:%d", card); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci card_data = malloc(sizeof(*card_data)); 7862306a36Sopenharmony_ci if (!card_data) 7962306a36Sopenharmony_ci ksft_exit_fail_msg("Out of memory\n"); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci err = snd_ctl_open_lconf(&card_data->handle, name, 0, config); 8262306a36Sopenharmony_ci if (err < 0) { 8362306a36Sopenharmony_ci ksft_print_msg("Failed to get hctl for card %d: %s\n", 8462306a36Sopenharmony_ci card, snd_strerror(err)); 8562306a36Sopenharmony_ci goto next_card; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci err = snd_card_get_name(card, &card_name); 8962306a36Sopenharmony_ci if (err != 0) 9062306a36Sopenharmony_ci card_name = "Unknown"; 9162306a36Sopenharmony_ci err = snd_card_get_longname(card, &card_longname); 9262306a36Sopenharmony_ci if (err != 0) 9362306a36Sopenharmony_ci card_longname = "Unknown"; 9462306a36Sopenharmony_ci ksft_print_msg("Card %d - %s (%s)\n", card, 9562306a36Sopenharmony_ci card_name, card_longname); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* Count controls */ 9862306a36Sopenharmony_ci snd_ctl_elem_list_malloc(&card_data->ctls); 9962306a36Sopenharmony_ci snd_ctl_elem_list(card_data->handle, card_data->ctls); 10062306a36Sopenharmony_ci card_data->num_ctls = snd_ctl_elem_list_get_count(card_data->ctls); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* Enumerate control information */ 10362306a36Sopenharmony_ci snd_ctl_elem_list_alloc_space(card_data->ctls, card_data->num_ctls); 10462306a36Sopenharmony_ci snd_ctl_elem_list(card_data->handle, card_data->ctls); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci card_data->card = num_cards++; 10762306a36Sopenharmony_ci card_data->next = card_list; 10862306a36Sopenharmony_ci card_list = card_data; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci num_controls += card_data->num_ctls; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci for (ctl = 0; ctl < card_data->num_ctls; ctl++) { 11362306a36Sopenharmony_ci ctl_data = malloc(sizeof(*ctl_data)); 11462306a36Sopenharmony_ci if (!ctl_data) 11562306a36Sopenharmony_ci ksft_exit_fail_msg("Out of memory\n"); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci memset(ctl_data, 0, sizeof(*ctl_data)); 11862306a36Sopenharmony_ci ctl_data->card = card_data; 11962306a36Sopenharmony_ci ctl_data->elem = ctl; 12062306a36Sopenharmony_ci ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls, 12162306a36Sopenharmony_ci ctl); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci err = snd_ctl_elem_id_malloc(&ctl_data->id); 12462306a36Sopenharmony_ci if (err < 0) 12562306a36Sopenharmony_ci ksft_exit_fail_msg("Out of memory\n"); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci err = snd_ctl_elem_info_malloc(&ctl_data->info); 12862306a36Sopenharmony_ci if (err < 0) 12962306a36Sopenharmony_ci ksft_exit_fail_msg("Out of memory\n"); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci err = snd_ctl_elem_value_malloc(&ctl_data->def_val); 13262306a36Sopenharmony_ci if (err < 0) 13362306a36Sopenharmony_ci ksft_exit_fail_msg("Out of memory\n"); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci snd_ctl_elem_list_get_id(card_data->ctls, ctl, 13662306a36Sopenharmony_ci ctl_data->id); 13762306a36Sopenharmony_ci snd_ctl_elem_info_set_id(ctl_data->info, ctl_data->id); 13862306a36Sopenharmony_ci err = snd_ctl_elem_info(card_data->handle, 13962306a36Sopenharmony_ci ctl_data->info); 14062306a36Sopenharmony_ci if (err < 0) { 14162306a36Sopenharmony_ci ksft_print_msg("%s getting info for %s\n", 14262306a36Sopenharmony_ci snd_strerror(err), 14362306a36Sopenharmony_ci ctl_data->name); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci snd_ctl_elem_value_set_id(ctl_data->def_val, 14762306a36Sopenharmony_ci ctl_data->id); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci ctl_data->next = ctl_list; 15062306a36Sopenharmony_ci ctl_list = ctl_data; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* Set up for events */ 15462306a36Sopenharmony_ci err = snd_ctl_subscribe_events(card_data->handle, true); 15562306a36Sopenharmony_ci if (err < 0) { 15662306a36Sopenharmony_ci ksft_exit_fail_msg("snd_ctl_subscribe_events() failed for card %d: %d\n", 15762306a36Sopenharmony_ci card, err); 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci err = snd_ctl_poll_descriptors_count(card_data->handle); 16162306a36Sopenharmony_ci if (err != 1) { 16262306a36Sopenharmony_ci ksft_exit_fail_msg("Unexpected descriptor count %d for card %d\n", 16362306a36Sopenharmony_ci err, card); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci err = snd_ctl_poll_descriptors(card_data->handle, 16762306a36Sopenharmony_ci &card_data->pollfd, 1); 16862306a36Sopenharmony_ci if (err != 1) { 16962306a36Sopenharmony_ci ksft_exit_fail_msg("snd_ctl_poll_descriptors() failed for card %d: %d\n", 17062306a36Sopenharmony_ci card, err); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci next_card: 17462306a36Sopenharmony_ci if (snd_card_next(&card) < 0) { 17562306a36Sopenharmony_ci ksft_print_msg("snd_card_next"); 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci snd_config_delete(config); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* 18462306a36Sopenharmony_ci * Block for up to timeout ms for an event, returns a negative value 18562306a36Sopenharmony_ci * on error, 0 for no event and 1 for an event. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_cistatic int wait_for_event(struct ctl_data *ctl, int timeout) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci unsigned short revents; 19062306a36Sopenharmony_ci snd_ctl_event_t *event; 19162306a36Sopenharmony_ci int err; 19262306a36Sopenharmony_ci unsigned int mask = 0; 19362306a36Sopenharmony_ci unsigned int ev_id; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci snd_ctl_event_alloca(&event); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci do { 19862306a36Sopenharmony_ci err = poll(&(ctl->card->pollfd), 1, timeout); 19962306a36Sopenharmony_ci if (err < 0) { 20062306a36Sopenharmony_ci ksft_print_msg("poll() failed for %s: %s (%d)\n", 20162306a36Sopenharmony_ci ctl->name, strerror(errno), errno); 20262306a36Sopenharmony_ci return -1; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci /* Timeout */ 20562306a36Sopenharmony_ci if (err == 0) 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci err = snd_ctl_poll_descriptors_revents(ctl->card->handle, 20962306a36Sopenharmony_ci &(ctl->card->pollfd), 21062306a36Sopenharmony_ci 1, &revents); 21162306a36Sopenharmony_ci if (err < 0) { 21262306a36Sopenharmony_ci ksft_print_msg("snd_ctl_poll_descriptors_revents() failed for %s: %d\n", 21362306a36Sopenharmony_ci ctl->name, err); 21462306a36Sopenharmony_ci return err; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci if (revents & POLLERR) { 21762306a36Sopenharmony_ci ksft_print_msg("snd_ctl_poll_descriptors_revents() reported POLLERR for %s\n", 21862306a36Sopenharmony_ci ctl->name); 21962306a36Sopenharmony_ci return -1; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci /* No read events */ 22262306a36Sopenharmony_ci if (!(revents & POLLIN)) { 22362306a36Sopenharmony_ci ksft_print_msg("No POLLIN\n"); 22462306a36Sopenharmony_ci continue; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci err = snd_ctl_read(ctl->card->handle, event); 22862306a36Sopenharmony_ci if (err < 0) { 22962306a36Sopenharmony_ci ksft_print_msg("snd_ctl_read() failed for %s: %d\n", 23062306a36Sopenharmony_ci ctl->name, err); 23162306a36Sopenharmony_ci return err; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM) 23562306a36Sopenharmony_ci continue; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* The ID returned from the event is 1 less than numid */ 23862306a36Sopenharmony_ci mask = snd_ctl_event_elem_get_mask(event); 23962306a36Sopenharmony_ci ev_id = snd_ctl_event_elem_get_numid(event); 24062306a36Sopenharmony_ci if (ev_id != snd_ctl_elem_info_get_numid(ctl->info)) { 24162306a36Sopenharmony_ci ksft_print_msg("Event for unexpected ctl %s\n", 24262306a36Sopenharmony_ci snd_ctl_event_elem_get_name(event)); 24362306a36Sopenharmony_ci continue; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if ((mask & SND_CTL_EVENT_MASK_REMOVE) == SND_CTL_EVENT_MASK_REMOVE) { 24762306a36Sopenharmony_ci ksft_print_msg("Removal event for %s\n", 24862306a36Sopenharmony_ci ctl->name); 24962306a36Sopenharmony_ci return -1; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci } while ((mask & SND_CTL_EVENT_MASK_VALUE) != SND_CTL_EVENT_MASK_VALUE); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return 1; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic bool ctl_value_index_valid(struct ctl_data *ctl, 25762306a36Sopenharmony_ci snd_ctl_elem_value_t *val, 25862306a36Sopenharmony_ci int index) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci long int_val; 26162306a36Sopenharmony_ci long long int64_val; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci switch (snd_ctl_elem_info_get_type(ctl->info)) { 26462306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_NONE: 26562306a36Sopenharmony_ci ksft_print_msg("%s.%d Invalid control type NONE\n", 26662306a36Sopenharmony_ci ctl->name, index); 26762306a36Sopenharmony_ci return false; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_BOOLEAN: 27062306a36Sopenharmony_ci int_val = snd_ctl_elem_value_get_boolean(val, index); 27162306a36Sopenharmony_ci switch (int_val) { 27262306a36Sopenharmony_ci case 0: 27362306a36Sopenharmony_ci case 1: 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci default: 27662306a36Sopenharmony_ci ksft_print_msg("%s.%d Invalid boolean value %ld\n", 27762306a36Sopenharmony_ci ctl->name, index, int_val); 27862306a36Sopenharmony_ci return false; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_INTEGER: 28362306a36Sopenharmony_ci int_val = snd_ctl_elem_value_get_integer(val, index); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (int_val < snd_ctl_elem_info_get_min(ctl->info)) { 28662306a36Sopenharmony_ci ksft_print_msg("%s.%d value %ld less than minimum %ld\n", 28762306a36Sopenharmony_ci ctl->name, index, int_val, 28862306a36Sopenharmony_ci snd_ctl_elem_info_get_min(ctl->info)); 28962306a36Sopenharmony_ci return false; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (int_val > snd_ctl_elem_info_get_max(ctl->info)) { 29362306a36Sopenharmony_ci ksft_print_msg("%s.%d value %ld more than maximum %ld\n", 29462306a36Sopenharmony_ci ctl->name, index, int_val, 29562306a36Sopenharmony_ci snd_ctl_elem_info_get_max(ctl->info)); 29662306a36Sopenharmony_ci return false; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* Only check step size if there is one and we're in bounds */ 30062306a36Sopenharmony_ci if (snd_ctl_elem_info_get_step(ctl->info) && 30162306a36Sopenharmony_ci (int_val - snd_ctl_elem_info_get_min(ctl->info) % 30262306a36Sopenharmony_ci snd_ctl_elem_info_get_step(ctl->info))) { 30362306a36Sopenharmony_ci ksft_print_msg("%s.%d value %ld invalid for step %ld minimum %ld\n", 30462306a36Sopenharmony_ci ctl->name, index, int_val, 30562306a36Sopenharmony_ci snd_ctl_elem_info_get_step(ctl->info), 30662306a36Sopenharmony_ci snd_ctl_elem_info_get_min(ctl->info)); 30762306a36Sopenharmony_ci return false; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_INTEGER64: 31262306a36Sopenharmony_ci int64_val = snd_ctl_elem_value_get_integer64(val, index); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (int64_val < snd_ctl_elem_info_get_min64(ctl->info)) { 31562306a36Sopenharmony_ci ksft_print_msg("%s.%d value %lld less than minimum %lld\n", 31662306a36Sopenharmony_ci ctl->name, index, int64_val, 31762306a36Sopenharmony_ci snd_ctl_elem_info_get_min64(ctl->info)); 31862306a36Sopenharmony_ci return false; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) { 32262306a36Sopenharmony_ci ksft_print_msg("%s.%d value %lld more than maximum %ld\n", 32362306a36Sopenharmony_ci ctl->name, index, int64_val, 32462306a36Sopenharmony_ci snd_ctl_elem_info_get_max(ctl->info)); 32562306a36Sopenharmony_ci return false; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Only check step size if there is one and we're in bounds */ 32962306a36Sopenharmony_ci if (snd_ctl_elem_info_get_step64(ctl->info) && 33062306a36Sopenharmony_ci (int64_val - snd_ctl_elem_info_get_min64(ctl->info)) % 33162306a36Sopenharmony_ci snd_ctl_elem_info_get_step64(ctl->info)) { 33262306a36Sopenharmony_ci ksft_print_msg("%s.%d value %lld invalid for step %lld minimum %lld\n", 33362306a36Sopenharmony_ci ctl->name, index, int64_val, 33462306a36Sopenharmony_ci snd_ctl_elem_info_get_step64(ctl->info), 33562306a36Sopenharmony_ci snd_ctl_elem_info_get_min64(ctl->info)); 33662306a36Sopenharmony_ci return false; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_ENUMERATED: 34162306a36Sopenharmony_ci int_val = snd_ctl_elem_value_get_enumerated(val, index); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (int_val < 0) { 34462306a36Sopenharmony_ci ksft_print_msg("%s.%d negative value %ld for enumeration\n", 34562306a36Sopenharmony_ci ctl->name, index, int_val); 34662306a36Sopenharmony_ci return false; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (int_val >= snd_ctl_elem_info_get_items(ctl->info)) { 35062306a36Sopenharmony_ci ksft_print_msg("%s.%d value %ld more than item count %ld\n", 35162306a36Sopenharmony_ci ctl->name, index, int_val, 35262306a36Sopenharmony_ci snd_ctl_elem_info_get_items(ctl->info)); 35362306a36Sopenharmony_ci return false; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci default: 35862306a36Sopenharmony_ci /* No tests for other types */ 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return true; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci/* 36662306a36Sopenharmony_ci * Check that the provided value meets the constraints for the 36762306a36Sopenharmony_ci * provided control. 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_cistatic bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci int i; 37262306a36Sopenharmony_ci bool valid = true; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) 37562306a36Sopenharmony_ci if (!ctl_value_index_valid(ctl, val, i)) 37662306a36Sopenharmony_ci valid = false; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return valid; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/* 38262306a36Sopenharmony_ci * Check that we can read the default value and it is valid. Write 38362306a36Sopenharmony_ci * tests use the read value to restore the default. 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_cistatic void test_ctl_get_value(struct ctl_data *ctl) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci int err; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* If the control is turned off let's be polite */ 39062306a36Sopenharmony_ci if (snd_ctl_elem_info_is_inactive(ctl->info)) { 39162306a36Sopenharmony_ci ksft_print_msg("%s is inactive\n", ctl->name); 39262306a36Sopenharmony_ci ksft_test_result_skip("get_value.%d.%d\n", 39362306a36Sopenharmony_ci ctl->card->card, ctl->elem); 39462306a36Sopenharmony_ci return; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* Can't test reading on an unreadable control */ 39862306a36Sopenharmony_ci if (!snd_ctl_elem_info_is_readable(ctl->info)) { 39962306a36Sopenharmony_ci ksft_print_msg("%s is not readable\n", ctl->name); 40062306a36Sopenharmony_ci ksft_test_result_skip("get_value.%d.%d\n", 40162306a36Sopenharmony_ci ctl->card->card, ctl->elem); 40262306a36Sopenharmony_ci return; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val); 40662306a36Sopenharmony_ci if (err < 0) { 40762306a36Sopenharmony_ci ksft_print_msg("snd_ctl_elem_read() failed: %s\n", 40862306a36Sopenharmony_ci snd_strerror(err)); 40962306a36Sopenharmony_ci goto out; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (!ctl_value_valid(ctl, ctl->def_val)) 41362306a36Sopenharmony_ci err = -EINVAL; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ciout: 41662306a36Sopenharmony_ci ksft_test_result(err >= 0, "get_value.%d.%d\n", 41762306a36Sopenharmony_ci ctl->card->card, ctl->elem); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic bool strend(const char *haystack, const char *needle) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci size_t haystack_len = strlen(haystack); 42362306a36Sopenharmony_ci size_t needle_len = strlen(needle); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (needle_len > haystack_len) 42662306a36Sopenharmony_ci return false; 42762306a36Sopenharmony_ci return strcmp(haystack + haystack_len - needle_len, needle) == 0; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic void test_ctl_name(struct ctl_data *ctl) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci bool name_ok = true; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci ksft_print_msg("%d.%d %s\n", ctl->card->card, ctl->elem, 43562306a36Sopenharmony_ci ctl->name); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* Only boolean controls should end in Switch */ 43862306a36Sopenharmony_ci if (strend(ctl->name, " Switch")) { 43962306a36Sopenharmony_ci if (snd_ctl_elem_info_get_type(ctl->info) != SND_CTL_ELEM_TYPE_BOOLEAN) { 44062306a36Sopenharmony_ci ksft_print_msg("%d.%d %s ends in Switch but is not boolean\n", 44162306a36Sopenharmony_ci ctl->card->card, ctl->elem, ctl->name); 44262306a36Sopenharmony_ci name_ok = false; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* Writeable boolean controls should end in Switch */ 44762306a36Sopenharmony_ci if (snd_ctl_elem_info_get_type(ctl->info) == SND_CTL_ELEM_TYPE_BOOLEAN && 44862306a36Sopenharmony_ci snd_ctl_elem_info_is_writable(ctl->info)) { 44962306a36Sopenharmony_ci if (!strend(ctl->name, " Switch")) { 45062306a36Sopenharmony_ci ksft_print_msg("%d.%d %s is a writeable boolean but not a Switch\n", 45162306a36Sopenharmony_ci ctl->card->card, ctl->elem, ctl->name); 45262306a36Sopenharmony_ci name_ok = false; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci ksft_test_result(name_ok, "name.%d.%d\n", 45762306a36Sopenharmony_ci ctl->card->card, ctl->elem); 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic void show_values(struct ctl_data *ctl, snd_ctl_elem_value_t *orig_val, 46162306a36Sopenharmony_ci snd_ctl_elem_value_t *read_val) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci long long orig_int, read_int; 46462306a36Sopenharmony_ci int i; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 46762306a36Sopenharmony_ci switch (snd_ctl_elem_info_get_type(ctl->info)) { 46862306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_BOOLEAN: 46962306a36Sopenharmony_ci orig_int = snd_ctl_elem_value_get_boolean(orig_val, i); 47062306a36Sopenharmony_ci read_int = snd_ctl_elem_value_get_boolean(read_val, i); 47162306a36Sopenharmony_ci break; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_INTEGER: 47462306a36Sopenharmony_ci orig_int = snd_ctl_elem_value_get_integer(orig_val, i); 47562306a36Sopenharmony_ci read_int = snd_ctl_elem_value_get_integer(read_val, i); 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_INTEGER64: 47962306a36Sopenharmony_ci orig_int = snd_ctl_elem_value_get_integer64(orig_val, 48062306a36Sopenharmony_ci i); 48162306a36Sopenharmony_ci read_int = snd_ctl_elem_value_get_integer64(read_val, 48262306a36Sopenharmony_ci i); 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_ENUMERATED: 48662306a36Sopenharmony_ci orig_int = snd_ctl_elem_value_get_enumerated(orig_val, 48762306a36Sopenharmony_ci i); 48862306a36Sopenharmony_ci read_int = snd_ctl_elem_value_get_enumerated(read_val, 48962306a36Sopenharmony_ci i); 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci default: 49362306a36Sopenharmony_ci return; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ksft_print_msg("%s.%d orig %lld read %lld, is_volatile %d\n", 49762306a36Sopenharmony_ci ctl->name, i, orig_int, read_int, 49862306a36Sopenharmony_ci snd_ctl_elem_info_is_volatile(ctl->info)); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic bool show_mismatch(struct ctl_data *ctl, int index, 50362306a36Sopenharmony_ci snd_ctl_elem_value_t *read_val, 50462306a36Sopenharmony_ci snd_ctl_elem_value_t *expected_val) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci long long expected_int, read_int; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* 50962306a36Sopenharmony_ci * We factor out the code to compare values representable as 51062306a36Sopenharmony_ci * integers, ensure that check doesn't log otherwise. 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ci expected_int = 0; 51362306a36Sopenharmony_ci read_int = 0; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci switch (snd_ctl_elem_info_get_type(ctl->info)) { 51662306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_BOOLEAN: 51762306a36Sopenharmony_ci expected_int = snd_ctl_elem_value_get_boolean(expected_val, 51862306a36Sopenharmony_ci index); 51962306a36Sopenharmony_ci read_int = snd_ctl_elem_value_get_boolean(read_val, index); 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_INTEGER: 52362306a36Sopenharmony_ci expected_int = snd_ctl_elem_value_get_integer(expected_val, 52462306a36Sopenharmony_ci index); 52562306a36Sopenharmony_ci read_int = snd_ctl_elem_value_get_integer(read_val, index); 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_INTEGER64: 52962306a36Sopenharmony_ci expected_int = snd_ctl_elem_value_get_integer64(expected_val, 53062306a36Sopenharmony_ci index); 53162306a36Sopenharmony_ci read_int = snd_ctl_elem_value_get_integer64(read_val, 53262306a36Sopenharmony_ci index); 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_ENUMERATED: 53662306a36Sopenharmony_ci expected_int = snd_ctl_elem_value_get_enumerated(expected_val, 53762306a36Sopenharmony_ci index); 53862306a36Sopenharmony_ci read_int = snd_ctl_elem_value_get_enumerated(read_val, 53962306a36Sopenharmony_ci index); 54062306a36Sopenharmony_ci break; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci default: 54362306a36Sopenharmony_ci break; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (expected_int != read_int) { 54762306a36Sopenharmony_ci /* 54862306a36Sopenharmony_ci * NOTE: The volatile attribute means that the hardware 54962306a36Sopenharmony_ci * can voluntarily change the state of control element 55062306a36Sopenharmony_ci * independent of any operation by software. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_ci bool is_volatile = snd_ctl_elem_info_is_volatile(ctl->info); 55362306a36Sopenharmony_ci ksft_print_msg("%s.%d expected %lld but read %lld, is_volatile %d\n", 55462306a36Sopenharmony_ci ctl->name, index, expected_int, read_int, is_volatile); 55562306a36Sopenharmony_ci return !is_volatile; 55662306a36Sopenharmony_ci } else { 55762306a36Sopenharmony_ci return false; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/* 56262306a36Sopenharmony_ci * Write a value then if possible verify that we get the expected 56362306a36Sopenharmony_ci * result. An optional expected value can be provided if we expect 56462306a36Sopenharmony_ci * the write to fail, for verifying that invalid writes don't corrupt 56562306a36Sopenharmony_ci * anything. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_cistatic int write_and_verify(struct ctl_data *ctl, 56862306a36Sopenharmony_ci snd_ctl_elem_value_t *write_val, 56962306a36Sopenharmony_ci snd_ctl_elem_value_t *expected_val) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci int err, i; 57262306a36Sopenharmony_ci bool error_expected, mismatch_shown; 57362306a36Sopenharmony_ci snd_ctl_elem_value_t *initial_val, *read_val, *w_val; 57462306a36Sopenharmony_ci snd_ctl_elem_value_alloca(&initial_val); 57562306a36Sopenharmony_ci snd_ctl_elem_value_alloca(&read_val); 57662306a36Sopenharmony_ci snd_ctl_elem_value_alloca(&w_val); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* 57962306a36Sopenharmony_ci * We need to copy the write value since writing can modify 58062306a36Sopenharmony_ci * the value which causes surprises, and allocate an expected 58162306a36Sopenharmony_ci * value if we expect to read back what we wrote. 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci snd_ctl_elem_value_copy(w_val, write_val); 58462306a36Sopenharmony_ci if (expected_val) { 58562306a36Sopenharmony_ci error_expected = true; 58662306a36Sopenharmony_ci } else { 58762306a36Sopenharmony_ci error_expected = false; 58862306a36Sopenharmony_ci snd_ctl_elem_value_alloca(&expected_val); 58962306a36Sopenharmony_ci snd_ctl_elem_value_copy(expected_val, write_val); 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Store the value before we write */ 59362306a36Sopenharmony_ci if (snd_ctl_elem_info_is_readable(ctl->info)) { 59462306a36Sopenharmony_ci snd_ctl_elem_value_set_id(initial_val, ctl->id); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci err = snd_ctl_elem_read(ctl->card->handle, initial_val); 59762306a36Sopenharmony_ci if (err < 0) { 59862306a36Sopenharmony_ci ksft_print_msg("snd_ctl_elem_read() failed: %s\n", 59962306a36Sopenharmony_ci snd_strerror(err)); 60062306a36Sopenharmony_ci return err; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* 60562306a36Sopenharmony_ci * Do the write, if we have an expected value ignore the error 60662306a36Sopenharmony_ci * and carry on to validate the expected value. 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_ci err = snd_ctl_elem_write(ctl->card->handle, w_val); 60962306a36Sopenharmony_ci if (err < 0 && !error_expected) { 61062306a36Sopenharmony_ci ksft_print_msg("snd_ctl_elem_write() failed: %s\n", 61162306a36Sopenharmony_ci snd_strerror(err)); 61262306a36Sopenharmony_ci return err; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* Can we do the verification part? */ 61662306a36Sopenharmony_ci if (!snd_ctl_elem_info_is_readable(ctl->info)) 61762306a36Sopenharmony_ci return err; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci snd_ctl_elem_value_set_id(read_val, ctl->id); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci err = snd_ctl_elem_read(ctl->card->handle, read_val); 62262306a36Sopenharmony_ci if (err < 0) { 62362306a36Sopenharmony_ci ksft_print_msg("snd_ctl_elem_read() failed: %s\n", 62462306a36Sopenharmony_ci snd_strerror(err)); 62562306a36Sopenharmony_ci return err; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* 62962306a36Sopenharmony_ci * Check for an event if the value changed, or confirm that 63062306a36Sopenharmony_ci * there was none if it didn't. We rely on the kernel 63162306a36Sopenharmony_ci * generating the notification before it returns from the 63262306a36Sopenharmony_ci * write, this is currently true, should that ever change this 63362306a36Sopenharmony_ci * will most likely break and need updating. 63462306a36Sopenharmony_ci */ 63562306a36Sopenharmony_ci if (!snd_ctl_elem_info_is_volatile(ctl->info)) { 63662306a36Sopenharmony_ci err = wait_for_event(ctl, 0); 63762306a36Sopenharmony_ci if (snd_ctl_elem_value_compare(initial_val, read_val)) { 63862306a36Sopenharmony_ci if (err < 1) { 63962306a36Sopenharmony_ci ksft_print_msg("No event generated for %s\n", 64062306a36Sopenharmony_ci ctl->name); 64162306a36Sopenharmony_ci show_values(ctl, initial_val, read_val); 64262306a36Sopenharmony_ci ctl->event_missing++; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci } else { 64562306a36Sopenharmony_ci if (err != 0) { 64662306a36Sopenharmony_ci ksft_print_msg("Spurious event generated for %s\n", 64762306a36Sopenharmony_ci ctl->name); 64862306a36Sopenharmony_ci show_values(ctl, initial_val, read_val); 64962306a36Sopenharmony_ci ctl->event_spurious++; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* 65562306a36Sopenharmony_ci * Use the libray to compare values, if there's a mismatch 65662306a36Sopenharmony_ci * carry on and try to provide a more useful diagnostic than 65762306a36Sopenharmony_ci * just "mismatch". 65862306a36Sopenharmony_ci */ 65962306a36Sopenharmony_ci if (!snd_ctl_elem_value_compare(expected_val, read_val)) 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci mismatch_shown = false; 66362306a36Sopenharmony_ci for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) 66462306a36Sopenharmony_ci if (show_mismatch(ctl, i, read_val, expected_val)) 66562306a36Sopenharmony_ci mismatch_shown = true; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (!mismatch_shown) 66862306a36Sopenharmony_ci ksft_print_msg("%s read and written values differ\n", 66962306a36Sopenharmony_ci ctl->name); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci return -1; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci/* 67562306a36Sopenharmony_ci * Make sure we can write the default value back to the control, this 67662306a36Sopenharmony_ci * should validate that at least some write works. 67762306a36Sopenharmony_ci */ 67862306a36Sopenharmony_cistatic void test_ctl_write_default(struct ctl_data *ctl) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci int err; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* If the control is turned off let's be polite */ 68362306a36Sopenharmony_ci if (snd_ctl_elem_info_is_inactive(ctl->info)) { 68462306a36Sopenharmony_ci ksft_print_msg("%s is inactive\n", ctl->name); 68562306a36Sopenharmony_ci ksft_test_result_skip("write_default.%d.%d\n", 68662306a36Sopenharmony_ci ctl->card->card, ctl->elem); 68762306a36Sopenharmony_ci return; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (!snd_ctl_elem_info_is_writable(ctl->info)) { 69162306a36Sopenharmony_ci ksft_print_msg("%s is not writeable\n", ctl->name); 69262306a36Sopenharmony_ci ksft_test_result_skip("write_default.%d.%d\n", 69362306a36Sopenharmony_ci ctl->card->card, ctl->elem); 69462306a36Sopenharmony_ci return; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* No idea what the default was for unreadable controls */ 69862306a36Sopenharmony_ci if (!snd_ctl_elem_info_is_readable(ctl->info)) { 69962306a36Sopenharmony_ci ksft_print_msg("%s couldn't read default\n", ctl->name); 70062306a36Sopenharmony_ci ksft_test_result_skip("write_default.%d.%d\n", 70162306a36Sopenharmony_ci ctl->card->card, ctl->elem); 70262306a36Sopenharmony_ci return; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci err = write_and_verify(ctl, ctl->def_val, NULL); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci ksft_test_result(err >= 0, "write_default.%d.%d\n", 70862306a36Sopenharmony_ci ctl->card->card, ctl->elem); 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic bool test_ctl_write_valid_boolean(struct ctl_data *ctl) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci int err, i, j; 71462306a36Sopenharmony_ci bool fail = false; 71562306a36Sopenharmony_ci snd_ctl_elem_value_t *val; 71662306a36Sopenharmony_ci snd_ctl_elem_value_alloca(&val); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci snd_ctl_elem_value_set_id(val, ctl->id); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 72162306a36Sopenharmony_ci for (j = 0; j < 2; j++) { 72262306a36Sopenharmony_ci snd_ctl_elem_value_set_boolean(val, i, j); 72362306a36Sopenharmony_ci err = write_and_verify(ctl, val, NULL); 72462306a36Sopenharmony_ci if (err != 0) 72562306a36Sopenharmony_ci fail = true; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci return !fail; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic bool test_ctl_write_valid_integer(struct ctl_data *ctl) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci int err; 73562306a36Sopenharmony_ci int i; 73662306a36Sopenharmony_ci long j, step; 73762306a36Sopenharmony_ci bool fail = false; 73862306a36Sopenharmony_ci snd_ctl_elem_value_t *val; 73962306a36Sopenharmony_ci snd_ctl_elem_value_alloca(&val); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci snd_ctl_elem_value_set_id(val, ctl->id); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci step = snd_ctl_elem_info_get_step(ctl->info); 74462306a36Sopenharmony_ci if (!step) 74562306a36Sopenharmony_ci step = 1; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 74862306a36Sopenharmony_ci for (j = snd_ctl_elem_info_get_min(ctl->info); 74962306a36Sopenharmony_ci j <= snd_ctl_elem_info_get_max(ctl->info); j += step) { 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci snd_ctl_elem_value_set_integer(val, i, j); 75262306a36Sopenharmony_ci err = write_and_verify(ctl, val, NULL); 75362306a36Sopenharmony_ci if (err != 0) 75462306a36Sopenharmony_ci fail = true; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return !fail; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic bool test_ctl_write_valid_integer64(struct ctl_data *ctl) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci int err, i; 76562306a36Sopenharmony_ci long long j, step; 76662306a36Sopenharmony_ci bool fail = false; 76762306a36Sopenharmony_ci snd_ctl_elem_value_t *val; 76862306a36Sopenharmony_ci snd_ctl_elem_value_alloca(&val); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci snd_ctl_elem_value_set_id(val, ctl->id); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci step = snd_ctl_elem_info_get_step64(ctl->info); 77362306a36Sopenharmony_ci if (!step) 77462306a36Sopenharmony_ci step = 1; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 77762306a36Sopenharmony_ci for (j = snd_ctl_elem_info_get_min64(ctl->info); 77862306a36Sopenharmony_ci j <= snd_ctl_elem_info_get_max64(ctl->info); j += step) { 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci snd_ctl_elem_value_set_integer64(val, i, j); 78162306a36Sopenharmony_ci err = write_and_verify(ctl, val, NULL); 78262306a36Sopenharmony_ci if (err != 0) 78362306a36Sopenharmony_ci fail = true; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci return !fail; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic bool test_ctl_write_valid_enumerated(struct ctl_data *ctl) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci int err, i, j; 79362306a36Sopenharmony_ci bool fail = false; 79462306a36Sopenharmony_ci snd_ctl_elem_value_t *val; 79562306a36Sopenharmony_ci snd_ctl_elem_value_alloca(&val); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci snd_ctl_elem_value_set_id(val, ctl->id); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 80062306a36Sopenharmony_ci for (j = 0; j < snd_ctl_elem_info_get_items(ctl->info); j++) { 80162306a36Sopenharmony_ci snd_ctl_elem_value_set_enumerated(val, i, j); 80262306a36Sopenharmony_ci err = write_and_verify(ctl, val, NULL); 80362306a36Sopenharmony_ci if (err != 0) 80462306a36Sopenharmony_ci fail = true; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return !fail; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic void test_ctl_write_valid(struct ctl_data *ctl) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci bool pass; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* If the control is turned off let's be polite */ 81662306a36Sopenharmony_ci if (snd_ctl_elem_info_is_inactive(ctl->info)) { 81762306a36Sopenharmony_ci ksft_print_msg("%s is inactive\n", ctl->name); 81862306a36Sopenharmony_ci ksft_test_result_skip("write_valid.%d.%d\n", 81962306a36Sopenharmony_ci ctl->card->card, ctl->elem); 82062306a36Sopenharmony_ci return; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (!snd_ctl_elem_info_is_writable(ctl->info)) { 82462306a36Sopenharmony_ci ksft_print_msg("%s is not writeable\n", ctl->name); 82562306a36Sopenharmony_ci ksft_test_result_skip("write_valid.%d.%d\n", 82662306a36Sopenharmony_ci ctl->card->card, ctl->elem); 82762306a36Sopenharmony_ci return; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci switch (snd_ctl_elem_info_get_type(ctl->info)) { 83162306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_BOOLEAN: 83262306a36Sopenharmony_ci pass = test_ctl_write_valid_boolean(ctl); 83362306a36Sopenharmony_ci break; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_INTEGER: 83662306a36Sopenharmony_ci pass = test_ctl_write_valid_integer(ctl); 83762306a36Sopenharmony_ci break; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_INTEGER64: 84062306a36Sopenharmony_ci pass = test_ctl_write_valid_integer64(ctl); 84162306a36Sopenharmony_ci break; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_ENUMERATED: 84462306a36Sopenharmony_ci pass = test_ctl_write_valid_enumerated(ctl); 84562306a36Sopenharmony_ci break; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci default: 84862306a36Sopenharmony_ci /* No tests for this yet */ 84962306a36Sopenharmony_ci ksft_test_result_skip("write_valid.%d.%d\n", 85062306a36Sopenharmony_ci ctl->card->card, ctl->elem); 85162306a36Sopenharmony_ci return; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci /* Restore the default value to minimise disruption */ 85562306a36Sopenharmony_ci write_and_verify(ctl, ctl->def_val, NULL); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci ksft_test_result(pass, "write_valid.%d.%d\n", 85862306a36Sopenharmony_ci ctl->card->card, ctl->elem); 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic bool test_ctl_write_invalid_value(struct ctl_data *ctl, 86262306a36Sopenharmony_ci snd_ctl_elem_value_t *val) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci int err; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci /* Ideally this will fail... */ 86762306a36Sopenharmony_ci err = snd_ctl_elem_write(ctl->card->handle, val); 86862306a36Sopenharmony_ci if (err < 0) 86962306a36Sopenharmony_ci return false; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* ...but some devices will clamp to an in range value */ 87262306a36Sopenharmony_ci err = snd_ctl_elem_read(ctl->card->handle, val); 87362306a36Sopenharmony_ci if (err < 0) { 87462306a36Sopenharmony_ci ksft_print_msg("%s failed to read: %s\n", 87562306a36Sopenharmony_ci ctl->name, snd_strerror(err)); 87662306a36Sopenharmony_ci return true; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci return !ctl_value_valid(ctl, val); 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic bool test_ctl_write_invalid_boolean(struct ctl_data *ctl) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci int i; 88562306a36Sopenharmony_ci bool fail = false; 88662306a36Sopenharmony_ci snd_ctl_elem_value_t *val; 88762306a36Sopenharmony_ci snd_ctl_elem_value_alloca(&val); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 89062306a36Sopenharmony_ci snd_ctl_elem_value_copy(val, ctl->def_val); 89162306a36Sopenharmony_ci snd_ctl_elem_value_set_boolean(val, i, 2); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (test_ctl_write_invalid_value(ctl, val)) 89462306a36Sopenharmony_ci fail = true; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci return !fail; 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic bool test_ctl_write_invalid_integer(struct ctl_data *ctl) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci int i; 90362306a36Sopenharmony_ci bool fail = false; 90462306a36Sopenharmony_ci snd_ctl_elem_value_t *val; 90562306a36Sopenharmony_ci snd_ctl_elem_value_alloca(&val); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 90862306a36Sopenharmony_ci if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) { 90962306a36Sopenharmony_ci /* Just under range */ 91062306a36Sopenharmony_ci snd_ctl_elem_value_copy(val, ctl->def_val); 91162306a36Sopenharmony_ci snd_ctl_elem_value_set_integer(val, i, 91262306a36Sopenharmony_ci snd_ctl_elem_info_get_min(ctl->info) - 1); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (test_ctl_write_invalid_value(ctl, val)) 91562306a36Sopenharmony_ci fail = true; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci /* Minimum representable value */ 91862306a36Sopenharmony_ci snd_ctl_elem_value_copy(val, ctl->def_val); 91962306a36Sopenharmony_ci snd_ctl_elem_value_set_integer(val, i, LONG_MIN); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (test_ctl_write_invalid_value(ctl, val)) 92262306a36Sopenharmony_ci fail = true; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) { 92662306a36Sopenharmony_ci /* Just over range */ 92762306a36Sopenharmony_ci snd_ctl_elem_value_copy(val, ctl->def_val); 92862306a36Sopenharmony_ci snd_ctl_elem_value_set_integer(val, i, 92962306a36Sopenharmony_ci snd_ctl_elem_info_get_max(ctl->info) + 1); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (test_ctl_write_invalid_value(ctl, val)) 93262306a36Sopenharmony_ci fail = true; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci /* Maximum representable value */ 93562306a36Sopenharmony_ci snd_ctl_elem_value_copy(val, ctl->def_val); 93662306a36Sopenharmony_ci snd_ctl_elem_value_set_integer(val, i, LONG_MAX); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci if (test_ctl_write_invalid_value(ctl, val)) 93962306a36Sopenharmony_ci fail = true; 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci return !fail; 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic bool test_ctl_write_invalid_integer64(struct ctl_data *ctl) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci int i; 94962306a36Sopenharmony_ci bool fail = false; 95062306a36Sopenharmony_ci snd_ctl_elem_value_t *val; 95162306a36Sopenharmony_ci snd_ctl_elem_value_alloca(&val); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 95462306a36Sopenharmony_ci if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) { 95562306a36Sopenharmony_ci /* Just under range */ 95662306a36Sopenharmony_ci snd_ctl_elem_value_copy(val, ctl->def_val); 95762306a36Sopenharmony_ci snd_ctl_elem_value_set_integer64(val, i, 95862306a36Sopenharmony_ci snd_ctl_elem_info_get_min64(ctl->info) - 1); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (test_ctl_write_invalid_value(ctl, val)) 96162306a36Sopenharmony_ci fail = true; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* Minimum representable value */ 96462306a36Sopenharmony_ci snd_ctl_elem_value_copy(val, ctl->def_val); 96562306a36Sopenharmony_ci snd_ctl_elem_value_set_integer64(val, i, LLONG_MIN); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (test_ctl_write_invalid_value(ctl, val)) 96862306a36Sopenharmony_ci fail = true; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) { 97262306a36Sopenharmony_ci /* Just over range */ 97362306a36Sopenharmony_ci snd_ctl_elem_value_copy(val, ctl->def_val); 97462306a36Sopenharmony_ci snd_ctl_elem_value_set_integer64(val, i, 97562306a36Sopenharmony_ci snd_ctl_elem_info_get_max64(ctl->info) + 1); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci if (test_ctl_write_invalid_value(ctl, val)) 97862306a36Sopenharmony_ci fail = true; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* Maximum representable value */ 98162306a36Sopenharmony_ci snd_ctl_elem_value_copy(val, ctl->def_val); 98262306a36Sopenharmony_ci snd_ctl_elem_value_set_integer64(val, i, LLONG_MAX); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (test_ctl_write_invalid_value(ctl, val)) 98562306a36Sopenharmony_ci fail = true; 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci return !fail; 99062306a36Sopenharmony_ci} 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cistatic bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl) 99362306a36Sopenharmony_ci{ 99462306a36Sopenharmony_ci int i; 99562306a36Sopenharmony_ci bool fail = false; 99662306a36Sopenharmony_ci snd_ctl_elem_value_t *val; 99762306a36Sopenharmony_ci snd_ctl_elem_value_alloca(&val); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci snd_ctl_elem_value_set_id(val, ctl->id); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { 100262306a36Sopenharmony_ci /* One beyond maximum */ 100362306a36Sopenharmony_ci snd_ctl_elem_value_copy(val, ctl->def_val); 100462306a36Sopenharmony_ci snd_ctl_elem_value_set_enumerated(val, i, 100562306a36Sopenharmony_ci snd_ctl_elem_info_get_items(ctl->info)); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (test_ctl_write_invalid_value(ctl, val)) 100862306a36Sopenharmony_ci fail = true; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci /* Maximum representable value */ 101162306a36Sopenharmony_ci snd_ctl_elem_value_copy(val, ctl->def_val); 101262306a36Sopenharmony_ci snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci if (test_ctl_write_invalid_value(ctl, val)) 101562306a36Sopenharmony_ci fail = true; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci return !fail; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic void test_ctl_write_invalid(struct ctl_data *ctl) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci bool pass; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* If the control is turned off let's be polite */ 102862306a36Sopenharmony_ci if (snd_ctl_elem_info_is_inactive(ctl->info)) { 102962306a36Sopenharmony_ci ksft_print_msg("%s is inactive\n", ctl->name); 103062306a36Sopenharmony_ci ksft_test_result_skip("write_invalid.%d.%d\n", 103162306a36Sopenharmony_ci ctl->card->card, ctl->elem); 103262306a36Sopenharmony_ci return; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (!snd_ctl_elem_info_is_writable(ctl->info)) { 103662306a36Sopenharmony_ci ksft_print_msg("%s is not writeable\n", ctl->name); 103762306a36Sopenharmony_ci ksft_test_result_skip("write_invalid.%d.%d\n", 103862306a36Sopenharmony_ci ctl->card->card, ctl->elem); 103962306a36Sopenharmony_ci return; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci switch (snd_ctl_elem_info_get_type(ctl->info)) { 104362306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_BOOLEAN: 104462306a36Sopenharmony_ci pass = test_ctl_write_invalid_boolean(ctl); 104562306a36Sopenharmony_ci break; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_INTEGER: 104862306a36Sopenharmony_ci pass = test_ctl_write_invalid_integer(ctl); 104962306a36Sopenharmony_ci break; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_INTEGER64: 105262306a36Sopenharmony_ci pass = test_ctl_write_invalid_integer64(ctl); 105362306a36Sopenharmony_ci break; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci case SND_CTL_ELEM_TYPE_ENUMERATED: 105662306a36Sopenharmony_ci pass = test_ctl_write_invalid_enumerated(ctl); 105762306a36Sopenharmony_ci break; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci default: 106062306a36Sopenharmony_ci /* No tests for this yet */ 106162306a36Sopenharmony_ci ksft_test_result_skip("write_invalid.%d.%d\n", 106262306a36Sopenharmony_ci ctl->card->card, ctl->elem); 106362306a36Sopenharmony_ci return; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci /* Restore the default value to minimise disruption */ 106762306a36Sopenharmony_ci write_and_verify(ctl, ctl->def_val, NULL); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci ksft_test_result(pass, "write_invalid.%d.%d\n", 107062306a36Sopenharmony_ci ctl->card->card, ctl->elem); 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_cistatic void test_ctl_event_missing(struct ctl_data *ctl) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci ksft_test_result(!ctl->event_missing, "event_missing.%d.%d\n", 107662306a36Sopenharmony_ci ctl->card->card, ctl->elem); 107762306a36Sopenharmony_ci} 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cistatic void test_ctl_event_spurious(struct ctl_data *ctl) 108062306a36Sopenharmony_ci{ 108162306a36Sopenharmony_ci ksft_test_result(!ctl->event_spurious, "event_spurious.%d.%d\n", 108262306a36Sopenharmony_ci ctl->card->card, ctl->elem); 108362306a36Sopenharmony_ci} 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ciint main(void) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci struct ctl_data *ctl; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci ksft_print_header(); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci find_controls(); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci ksft_set_plan(num_controls * TESTS_PER_CONTROL); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci for (ctl = ctl_list; ctl != NULL; ctl = ctl->next) { 109662306a36Sopenharmony_ci /* 109762306a36Sopenharmony_ci * Must test get_value() before we write anything, the 109862306a36Sopenharmony_ci * test stores the default value for later cleanup. 109962306a36Sopenharmony_ci */ 110062306a36Sopenharmony_ci test_ctl_get_value(ctl); 110162306a36Sopenharmony_ci test_ctl_name(ctl); 110262306a36Sopenharmony_ci test_ctl_write_default(ctl); 110362306a36Sopenharmony_ci test_ctl_write_valid(ctl); 110462306a36Sopenharmony_ci test_ctl_write_invalid(ctl); 110562306a36Sopenharmony_ci test_ctl_event_missing(ctl); 110662306a36Sopenharmony_ci test_ctl_event_spurious(ctl); 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci ksft_exit_pass(); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci return 0; 111262306a36Sopenharmony_ci} 1113