1d5ac70f0Sopenharmony_ci/* 2d5ac70f0Sopenharmony_ci * user-control-element-set.c - a program to test in-kernel implementation of 3d5ac70f0Sopenharmony_ci * user-defined control element set. 4d5ac70f0Sopenharmony_ci * 5d5ac70f0Sopenharmony_ci * Copyright (c) 2015-2016 Takashi Sakamoto 6d5ac70f0Sopenharmony_ci * 7d5ac70f0Sopenharmony_ci * Licensed under the terms of the GNU General Public License, version 2. 8d5ac70f0Sopenharmony_ci */ 9d5ac70f0Sopenharmony_ci 10d5ac70f0Sopenharmony_ci#include "config.h" 11d5ac70f0Sopenharmony_ci#include "../include/asoundlib.h" 12d5ac70f0Sopenharmony_ci#include <sound/tlv.h> 13d5ac70f0Sopenharmony_ci#include <stdbool.h> 14d5ac70f0Sopenharmony_ci 15d5ac70f0Sopenharmony_cistruct elem_set_trial { 16d5ac70f0Sopenharmony_ci snd_ctl_t *handle; 17d5ac70f0Sopenharmony_ci 18d5ac70f0Sopenharmony_ci snd_ctl_elem_type_t type; 19d5ac70f0Sopenharmony_ci unsigned int member_count; 20d5ac70f0Sopenharmony_ci unsigned int element_count; 21d5ac70f0Sopenharmony_ci 22d5ac70f0Sopenharmony_ci snd_ctl_elem_id_t *id; 23d5ac70f0Sopenharmony_ci 24d5ac70f0Sopenharmony_ci int (*add_elem_set)(struct elem_set_trial *trial, 25d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info); 26d5ac70f0Sopenharmony_ci int (*check_elem_props)(struct elem_set_trial *trial, 27d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info); 28d5ac70f0Sopenharmony_ci void (*change_elem_members)(struct elem_set_trial *trial, 29d5ac70f0Sopenharmony_ci snd_ctl_elem_value_t *elem_data); 30d5ac70f0Sopenharmony_ci int (*allocate_elem_set_tlv)(struct elem_set_trial *trial, 31d5ac70f0Sopenharmony_ci unsigned int **tlv); 32d5ac70f0Sopenharmony_ci 33d5ac70f0Sopenharmony_ci bool tlv_readable; 34d5ac70f0Sopenharmony_ci}; 35d5ac70f0Sopenharmony_ci 36d5ac70f0Sopenharmony_cistruct chmap_entry { 37d5ac70f0Sopenharmony_ci unsigned int type; 38d5ac70f0Sopenharmony_ci unsigned int length; 39d5ac70f0Sopenharmony_ci unsigned int maps[0]; 40d5ac70f0Sopenharmony_ci}; 41d5ac70f0Sopenharmony_ci 42d5ac70f0Sopenharmony_ci/* 43d5ac70f0Sopenharmony_ci * History of TLV feature: 44d5ac70f0Sopenharmony_ci * 45d5ac70f0Sopenharmony_ci * 2016/09/15: 398fa4db6c69 ("ALSA: control: move layout of TLV payload to UAPI 46d5ac70f0Sopenharmony_ci * header") 47d5ac70f0Sopenharmony_ci * 2012/07/21: 2d3391ec0ecc ("ALSA: PCM: channel mapping API implementation") 48d5ac70f0Sopenharmony_ci * 2011/11/20: bf1d1c9b6179 ("ALSA: tlv: add DECLARE_TLV_DB_RANGE()") 49d5ac70f0Sopenharmony_ci * 2009/07/16: 085f30654175 ("ALSA: Add new TLV types for dBwith min/max") 50d5ac70f0Sopenharmony_ci * 2006/09/06: 55a29af5ed5d ("[ALSA] Add definition of TLV dB range compound") 51d5ac70f0Sopenharmony_ci * 2006/08/28: 063a40d9111c ("Add the definition of linear volume TLV") 52d5ac70f0Sopenharmony_ci * 2006/08/28: 42750b04c5ba ("[ALSA] Control API - TLV implementation for 53d5ac70f0Sopenharmony_ci * additional information like dB scale") 54d5ac70f0Sopenharmony_ci */ 55d5ac70f0Sopenharmony_ci 56d5ac70f0Sopenharmony_ci/* Operations for elements in an element set with boolean type. */ 57d5ac70f0Sopenharmony_cistatic int add_bool_elem_set(struct elem_set_trial *trial, 58d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info) 59d5ac70f0Sopenharmony_ci{ 60d5ac70f0Sopenharmony_ci return snd_ctl_add_boolean_elem_set(trial->handle, info, 61d5ac70f0Sopenharmony_ci trial->element_count, trial->member_count); 62d5ac70f0Sopenharmony_ci} 63d5ac70f0Sopenharmony_ci 64d5ac70f0Sopenharmony_cistatic void change_bool_elem_members(struct elem_set_trial *trial, 65d5ac70f0Sopenharmony_ci snd_ctl_elem_value_t *elem_data) 66d5ac70f0Sopenharmony_ci{ 67d5ac70f0Sopenharmony_ci int val; 68d5ac70f0Sopenharmony_ci unsigned int i; 69d5ac70f0Sopenharmony_ci 70d5ac70f0Sopenharmony_ci for (i = 0; i < trial->member_count; ++i) { 71d5ac70f0Sopenharmony_ci val = snd_ctl_elem_value_get_boolean(elem_data, i); 72d5ac70f0Sopenharmony_ci snd_ctl_elem_value_set_boolean(elem_data, i, !val); 73d5ac70f0Sopenharmony_ci } 74d5ac70f0Sopenharmony_ci} 75d5ac70f0Sopenharmony_ci 76d5ac70f0Sopenharmony_cistatic int allocate_bool_elem_set_tlv(struct elem_set_trial *trial, 77d5ac70f0Sopenharmony_ci unsigned int **tlv) 78d5ac70f0Sopenharmony_ci{ 79d5ac70f0Sopenharmony_ci /* 80d5ac70f0Sopenharmony_ci * Performs like a toggle switch for attenuation, because they're bool 81d5ac70f0Sopenharmony_ci * elements. 82d5ac70f0Sopenharmony_ci */ 83d5ac70f0Sopenharmony_ci static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(range, -10000, 0); 84d5ac70f0Sopenharmony_ci 85d5ac70f0Sopenharmony_ci *tlv = malloc(sizeof(range)); 86d5ac70f0Sopenharmony_ci if (*tlv == NULL) 87d5ac70f0Sopenharmony_ci return -ENOMEM; 88d5ac70f0Sopenharmony_ci memcpy(*tlv, range, sizeof(range)); 89d5ac70f0Sopenharmony_ci 90d5ac70f0Sopenharmony_ci return 0; 91d5ac70f0Sopenharmony_ci} 92d5ac70f0Sopenharmony_ci 93d5ac70f0Sopenharmony_ci/* Operations for elements in an element set with integer type. */ 94d5ac70f0Sopenharmony_cistatic int add_int_elem_set(struct elem_set_trial *trial, 95d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info) 96d5ac70f0Sopenharmony_ci{ 97d5ac70f0Sopenharmony_ci return snd_ctl_add_integer_elem_set(trial->handle, info, 98d5ac70f0Sopenharmony_ci trial->element_count, trial->member_count, 99d5ac70f0Sopenharmony_ci 0, 25, 1); 100d5ac70f0Sopenharmony_ci} 101d5ac70f0Sopenharmony_ci 102d5ac70f0Sopenharmony_cistatic int check_int_elem_props(struct elem_set_trial *trial, 103d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info) 104d5ac70f0Sopenharmony_ci{ 105d5ac70f0Sopenharmony_ci if (snd_ctl_elem_info_get_min(info) != 0) 106d5ac70f0Sopenharmony_ci return -EIO; 107d5ac70f0Sopenharmony_ci if (snd_ctl_elem_info_get_max(info) != 25) 108d5ac70f0Sopenharmony_ci return -EIO; 109d5ac70f0Sopenharmony_ci if (snd_ctl_elem_info_get_step(info) != 1) 110d5ac70f0Sopenharmony_ci return -EIO; 111d5ac70f0Sopenharmony_ci 112d5ac70f0Sopenharmony_ci return 0; 113d5ac70f0Sopenharmony_ci} 114d5ac70f0Sopenharmony_ci 115d5ac70f0Sopenharmony_cistatic void change_int_elem_members(struct elem_set_trial *trial, 116d5ac70f0Sopenharmony_ci snd_ctl_elem_value_t *elem_data) 117d5ac70f0Sopenharmony_ci{ 118d5ac70f0Sopenharmony_ci long val; 119d5ac70f0Sopenharmony_ci unsigned int i; 120d5ac70f0Sopenharmony_ci 121d5ac70f0Sopenharmony_ci for (i = 0; i < trial->member_count; ++i) { 122d5ac70f0Sopenharmony_ci val = snd_ctl_elem_value_get_integer(elem_data, i); 123d5ac70f0Sopenharmony_ci snd_ctl_elem_value_set_integer(elem_data, i, ++val); 124d5ac70f0Sopenharmony_ci } 125d5ac70f0Sopenharmony_ci} 126d5ac70f0Sopenharmony_ci 127d5ac70f0Sopenharmony_cistatic int allocate_int_elem_set_tlv(struct elem_set_trial *trial, 128d5ac70f0Sopenharmony_ci unsigned int **tlv) 129d5ac70f0Sopenharmony_ci{ 130d5ac70f0Sopenharmony_ci unsigned int count, pos; 131d5ac70f0Sopenharmony_ci unsigned int i, j; 132d5ac70f0Sopenharmony_ci struct chmap_entry *entry; 133d5ac70f0Sopenharmony_ci 134d5ac70f0Sopenharmony_ci /* Calculate size of TLV packet for channel-mapping information. */ 135d5ac70f0Sopenharmony_ci count = 0; 136d5ac70f0Sopenharmony_ci for (i = 1; i <= 25; ++i) { 137d5ac70f0Sopenharmony_ci count += 2; /* sizeof(struct chmap_entry). */ 138d5ac70f0Sopenharmony_ci count += i; /* struct chmap_entry.maps. */ 139d5ac70f0Sopenharmony_ci } 140d5ac70f0Sopenharmony_ci 141d5ac70f0Sopenharmony_ci *tlv = malloc((2 + count) * sizeof(unsigned int)); 142d5ac70f0Sopenharmony_ci if (!*tlv) 143d5ac70f0Sopenharmony_ci return -ENOMEM; 144d5ac70f0Sopenharmony_ci 145d5ac70f0Sopenharmony_ci /* 146d5ac70f0Sopenharmony_ci * Emulate channel-mapping information in in-kernel implementation. 147d5ac70f0Sopenharmony_ci * Here, 25 entries are for each different channel. 148d5ac70f0Sopenharmony_ci */ 149d5ac70f0Sopenharmony_ci (*tlv)[0] = SNDRV_CTL_TLVT_CONTAINER; 150d5ac70f0Sopenharmony_ci (*tlv)[1] = count * sizeof(unsigned int); 151d5ac70f0Sopenharmony_ci pos = 2; 152d5ac70f0Sopenharmony_ci 153d5ac70f0Sopenharmony_ci for (i = 1; i <= 25 && pos < count; ++i) { 154d5ac70f0Sopenharmony_ci entry = (struct chmap_entry *)&(*tlv)[pos]; 155d5ac70f0Sopenharmony_ci 156d5ac70f0Sopenharmony_ci entry->type = SNDRV_CTL_TLVT_CHMAP_FIXED; 157d5ac70f0Sopenharmony_ci entry->length = i * sizeof(unsigned int); 158d5ac70f0Sopenharmony_ci pos += 2; 159d5ac70f0Sopenharmony_ci 160d5ac70f0Sopenharmony_ci for (j = 0; j < i; ++j) 161d5ac70f0Sopenharmony_ci entry->maps[j] = SND_CHMAP_MONO + j; 162d5ac70f0Sopenharmony_ci pos += i; 163d5ac70f0Sopenharmony_ci } 164d5ac70f0Sopenharmony_ci 165d5ac70f0Sopenharmony_ci return 0; 166d5ac70f0Sopenharmony_ci} 167d5ac70f0Sopenharmony_ci 168d5ac70f0Sopenharmony_ci/* Operations for elements in an element set with enumerated type. */ 169d5ac70f0Sopenharmony_cistatic const char *const labels[] = { 170d5ac70f0Sopenharmony_ci "trusty", 171d5ac70f0Sopenharmony_ci "utopic", 172d5ac70f0Sopenharmony_ci "vivid", 173d5ac70f0Sopenharmony_ci "willy", 174d5ac70f0Sopenharmony_ci "xenial", 175d5ac70f0Sopenharmony_ci}; 176d5ac70f0Sopenharmony_ci 177d5ac70f0Sopenharmony_cistatic int add_enum_elem_set(struct elem_set_trial *trial, 178d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info) 179d5ac70f0Sopenharmony_ci{ 180d5ac70f0Sopenharmony_ci return snd_ctl_add_enumerated_elem_set(trial->handle, info, 181d5ac70f0Sopenharmony_ci trial->element_count, trial->member_count, 182d5ac70f0Sopenharmony_ci sizeof(labels) / sizeof(labels[0]), 183d5ac70f0Sopenharmony_ci labels); 184d5ac70f0Sopenharmony_ci} 185d5ac70f0Sopenharmony_ci 186d5ac70f0Sopenharmony_cistatic int check_enum_elem_props(struct elem_set_trial *trial, 187d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info) 188d5ac70f0Sopenharmony_ci{ 189d5ac70f0Sopenharmony_ci unsigned int items; 190d5ac70f0Sopenharmony_ci unsigned int i; 191d5ac70f0Sopenharmony_ci const char *label; 192d5ac70f0Sopenharmony_ci int err; 193d5ac70f0Sopenharmony_ci 194d5ac70f0Sopenharmony_ci items = snd_ctl_elem_info_get_items(info); 195d5ac70f0Sopenharmony_ci if (items != sizeof(labels) / sizeof(labels[0])) 196d5ac70f0Sopenharmony_ci return -EIO; 197d5ac70f0Sopenharmony_ci 198d5ac70f0Sopenharmony_ci /* Enumerate and validate all of labels registered to this element. */ 199d5ac70f0Sopenharmony_ci for (i = 0; i < items; ++i) { 200d5ac70f0Sopenharmony_ci snd_ctl_elem_info_set_item(info, i); 201d5ac70f0Sopenharmony_ci err = snd_ctl_elem_info(trial->handle, info); 202d5ac70f0Sopenharmony_ci if (err < 0) 203d5ac70f0Sopenharmony_ci return err; 204d5ac70f0Sopenharmony_ci 205d5ac70f0Sopenharmony_ci label = snd_ctl_elem_info_get_item_name(info); 206d5ac70f0Sopenharmony_ci if (strncmp(label, labels[i], strlen(labels[i])) != 0) 207d5ac70f0Sopenharmony_ci return -EIO; 208d5ac70f0Sopenharmony_ci } 209d5ac70f0Sopenharmony_ci 210d5ac70f0Sopenharmony_ci return 0; 211d5ac70f0Sopenharmony_ci} 212d5ac70f0Sopenharmony_ci 213d5ac70f0Sopenharmony_cistatic void change_enum_elem_members(struct elem_set_trial *trial, 214d5ac70f0Sopenharmony_ci snd_ctl_elem_value_t *elem_data) 215d5ac70f0Sopenharmony_ci{ 216d5ac70f0Sopenharmony_ci unsigned int val; 217d5ac70f0Sopenharmony_ci unsigned int i; 218d5ac70f0Sopenharmony_ci 219d5ac70f0Sopenharmony_ci for (i = 0; i < trial->member_count; ++i) { 220d5ac70f0Sopenharmony_ci val = snd_ctl_elem_value_get_enumerated(elem_data, i); 221d5ac70f0Sopenharmony_ci snd_ctl_elem_value_set_enumerated(elem_data, i, ++val); 222d5ac70f0Sopenharmony_ci } 223d5ac70f0Sopenharmony_ci} 224d5ac70f0Sopenharmony_ci 225d5ac70f0Sopenharmony_ci/* Operations for elements in an element set with bytes type. */ 226d5ac70f0Sopenharmony_cistatic int add_bytes_elem_set(struct elem_set_trial *trial, 227d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info) 228d5ac70f0Sopenharmony_ci{ 229d5ac70f0Sopenharmony_ci return snd_ctl_add_bytes_elem_set(trial->handle, info, 230d5ac70f0Sopenharmony_ci trial->element_count, trial->member_count); 231d5ac70f0Sopenharmony_ci} 232d5ac70f0Sopenharmony_ci 233d5ac70f0Sopenharmony_cistatic void change_bytes_elem_members(struct elem_set_trial *trial, 234d5ac70f0Sopenharmony_ci snd_ctl_elem_value_t *elem_data) 235d5ac70f0Sopenharmony_ci{ 236d5ac70f0Sopenharmony_ci unsigned char val; 237d5ac70f0Sopenharmony_ci unsigned int i; 238d5ac70f0Sopenharmony_ci 239d5ac70f0Sopenharmony_ci for (i = 0; i < trial->member_count; ++i) { 240d5ac70f0Sopenharmony_ci val = snd_ctl_elem_value_get_byte(elem_data, i); 241d5ac70f0Sopenharmony_ci snd_ctl_elem_value_set_byte(elem_data, i, ++val); 242d5ac70f0Sopenharmony_ci } 243d5ac70f0Sopenharmony_ci} 244d5ac70f0Sopenharmony_ci 245d5ac70f0Sopenharmony_cistatic int allocate_bytes_elem_set_tlv(struct elem_set_trial *trial, 246d5ac70f0Sopenharmony_ci unsigned int **tlv) 247d5ac70f0Sopenharmony_ci{ 248d5ac70f0Sopenharmony_ci /* 249d5ac70f0Sopenharmony_ci * Emulate AK4396. 250d5ac70f0Sopenharmony_ci * 20 * log10(x/255) (dB) 251d5ac70f0Sopenharmony_ci * Here, x is written value. 252d5ac70f0Sopenharmony_ci */ 253d5ac70f0Sopenharmony_ci static const SNDRV_CTL_TLVD_DECLARE_DB_LINEAR(range, -4813, 0); 254d5ac70f0Sopenharmony_ci 255d5ac70f0Sopenharmony_ci *tlv = malloc(sizeof(range)); 256d5ac70f0Sopenharmony_ci if (*tlv == NULL) 257d5ac70f0Sopenharmony_ci return -ENOMEM; 258d5ac70f0Sopenharmony_ci memcpy(*tlv, range, sizeof(range)); 259d5ac70f0Sopenharmony_ci 260d5ac70f0Sopenharmony_ci return 0; 261d5ac70f0Sopenharmony_ci} 262d5ac70f0Sopenharmony_ci 263d5ac70f0Sopenharmony_ci/* Operations for elements in an element set with iec958 type. */ 264d5ac70f0Sopenharmony_cistatic int add_iec958_elem_set(struct elem_set_trial *trial, 265d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info) 266d5ac70f0Sopenharmony_ci{ 267d5ac70f0Sopenharmony_ci int err; 268d5ac70f0Sopenharmony_ci 269d5ac70f0Sopenharmony_ci snd_ctl_elem_info_get_id(info, trial->id); 270d5ac70f0Sopenharmony_ci 271d5ac70f0Sopenharmony_ci err = snd_ctl_elem_add_iec958(trial->handle, trial->id); 272d5ac70f0Sopenharmony_ci if (err < 0) 273d5ac70f0Sopenharmony_ci return err; 274d5ac70f0Sopenharmony_ci 275d5ac70f0Sopenharmony_ci /* 276d5ac70f0Sopenharmony_ci * In historical reason, the above API is not allowed to fill all of 277d5ac70f0Sopenharmony_ci * fields in identification data. 278d5ac70f0Sopenharmony_ci */ 279d5ac70f0Sopenharmony_ci return snd_ctl_elem_info(trial->handle, info); 280d5ac70f0Sopenharmony_ci} 281d5ac70f0Sopenharmony_ci 282d5ac70f0Sopenharmony_cistatic void change_iec958_elem_members(struct elem_set_trial *trial, 283d5ac70f0Sopenharmony_ci snd_ctl_elem_value_t *elem_data) 284d5ac70f0Sopenharmony_ci{ 285d5ac70f0Sopenharmony_ci snd_aes_iec958_t data; 286d5ac70f0Sopenharmony_ci 287d5ac70f0Sopenharmony_ci /* To suppress GCC warnings. */ 288d5ac70f0Sopenharmony_ci trial->element_count = 1; 289d5ac70f0Sopenharmony_ci 290d5ac70f0Sopenharmony_ci snd_ctl_elem_value_get_iec958(elem_data, &data); 291d5ac70f0Sopenharmony_ci /* This is an arbitrary number. */ 292d5ac70f0Sopenharmony_ci data.pad = 10; 293d5ac70f0Sopenharmony_ci snd_ctl_elem_value_set_iec958(elem_data, &data); 294d5ac70f0Sopenharmony_ci} 295d5ac70f0Sopenharmony_ci 296d5ac70f0Sopenharmony_ci/* Operations for elements in an element set with integer64 type. */ 297d5ac70f0Sopenharmony_cistatic int add_int64_elem_set(struct elem_set_trial *trial, 298d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info) 299d5ac70f0Sopenharmony_ci{ 300d5ac70f0Sopenharmony_ci return snd_ctl_add_integer64_elem_set(trial->handle, info, 301d5ac70f0Sopenharmony_ci trial->element_count, trial->member_count, 302d5ac70f0Sopenharmony_ci 0, 10000, 1); 303d5ac70f0Sopenharmony_ci} 304d5ac70f0Sopenharmony_ci 305d5ac70f0Sopenharmony_cistatic int check_int64_elem_props(struct elem_set_trial *trial, 306d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info) 307d5ac70f0Sopenharmony_ci{ 308d5ac70f0Sopenharmony_ci if (snd_ctl_elem_info_get_min64(info) != 0) 309d5ac70f0Sopenharmony_ci return -EIO; 310d5ac70f0Sopenharmony_ci if (snd_ctl_elem_info_get_max64(info) != 10000) 311d5ac70f0Sopenharmony_ci return -EIO; 312d5ac70f0Sopenharmony_ci if (snd_ctl_elem_info_get_step64(info) != 1) 313d5ac70f0Sopenharmony_ci return -EIO; 314d5ac70f0Sopenharmony_ci 315d5ac70f0Sopenharmony_ci return 0; 316d5ac70f0Sopenharmony_ci} 317d5ac70f0Sopenharmony_ci 318d5ac70f0Sopenharmony_cistatic void change_int64_elem_members(struct elem_set_trial *trial, 319d5ac70f0Sopenharmony_ci snd_ctl_elem_value_t *elem_data) 320d5ac70f0Sopenharmony_ci{ 321d5ac70f0Sopenharmony_ci long long val; 322d5ac70f0Sopenharmony_ci unsigned int i; 323d5ac70f0Sopenharmony_ci 324d5ac70f0Sopenharmony_ci for (i = 0; i < trial->member_count; ++i) { 325d5ac70f0Sopenharmony_ci val = snd_ctl_elem_value_get_integer64(elem_data, i); 326d5ac70f0Sopenharmony_ci snd_ctl_elem_value_set_integer64(elem_data, i, ++val); 327d5ac70f0Sopenharmony_ci } 328d5ac70f0Sopenharmony_ci} 329d5ac70f0Sopenharmony_ci 330d5ac70f0Sopenharmony_cistatic int allocate_int64_elem_set_tlv(struct elem_set_trial *trial, 331d5ac70f0Sopenharmony_ci unsigned int **tlv) 332d5ac70f0Sopenharmony_ci{ 333d5ac70f0Sopenharmony_ci /* 334d5ac70f0Sopenharmony_ci * Use this fomula between linear/dB value: 335d5ac70f0Sopenharmony_ci * 336d5ac70f0Sopenharmony_ci * Linear: dB range (coeff) 337d5ac70f0Sopenharmony_ci * 0<-> 4: -59.40<->-56.36 (44) 338d5ac70f0Sopenharmony_ci * 4<->22: -56.36<->-45.56 (60) 339d5ac70f0Sopenharmony_ci * 22<->33: -45.56<->-40.72 (76) 340d5ac70f0Sopenharmony_ci * 33<->37: -40.72<->-38.32 (44) 341d5ac70f0Sopenharmony_ci * 37<->48: -38.32<->-29.96 (76) 342d5ac70f0Sopenharmony_ci * 48<->66: -29.96<->-22.04 (60) 343d5ac70f0Sopenharmony_ci * 66<->84: -22.04<-> -8.36 (44) 344d5ac70f0Sopenharmony_ci * 84<->95: -8.36<-> -1.76 (60) 345d5ac70f0Sopenharmony_ci * 95<->99: -1.76<-> 0.00 (76) 346d5ac70f0Sopenharmony_ci * 100<->..: 0.0 347d5ac70f0Sopenharmony_ci */ 348d5ac70f0Sopenharmony_ci static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(range, 349d5ac70f0Sopenharmony_ci 0, 4, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-5940, 44, 1), 350d5ac70f0Sopenharmony_ci 4, 22, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-5636, 60, 0), 351d5ac70f0Sopenharmony_ci 22, 33, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-4556, 76, 0), 352d5ac70f0Sopenharmony_ci 33, 37, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-4072, 44, 0), 353d5ac70f0Sopenharmony_ci 37, 48, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-3832, 76, 0), 354d5ac70f0Sopenharmony_ci 48, 66, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-2996, 60, 0), 355d5ac70f0Sopenharmony_ci 66, 84, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-2204, 44, 0), 356d5ac70f0Sopenharmony_ci 84, 95, SNDRV_CTL_TLVD_DB_SCALE_ITEM( -836, 60, 0), 357d5ac70f0Sopenharmony_ci 95, 99, SNDRV_CTL_TLVD_DB_SCALE_ITEM( -176, 76, 0), 358d5ac70f0Sopenharmony_ci 100, 10000, SNDRV_CTL_TLVD_DB_SCALE_ITEM(0, 0, 0), 359d5ac70f0Sopenharmony_ci ); 360d5ac70f0Sopenharmony_ci 361d5ac70f0Sopenharmony_ci *tlv = malloc(sizeof(range)); 362d5ac70f0Sopenharmony_ci if (*tlv == NULL) 363d5ac70f0Sopenharmony_ci return -ENOMEM; 364d5ac70f0Sopenharmony_ci memcpy(*tlv, range, sizeof(range)); 365d5ac70f0Sopenharmony_ci 366d5ac70f0Sopenharmony_ci return 0; 367d5ac70f0Sopenharmony_ci} 368d5ac70f0Sopenharmony_ci 369d5ac70f0Sopenharmony_ci/* Common operations. */ 370d5ac70f0Sopenharmony_cistatic int add_elem_set(struct elem_set_trial *trial) 371d5ac70f0Sopenharmony_ci{ 372d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info; 373d5ac70f0Sopenharmony_ci char name[64] = {0}; 374d5ac70f0Sopenharmony_ci int err; 375d5ac70f0Sopenharmony_ci 376d5ac70f0Sopenharmony_ci snprintf(name, 64, "userspace-control-element-%s", 377d5ac70f0Sopenharmony_ci snd_ctl_elem_type_name(trial->type)); 378d5ac70f0Sopenharmony_ci 379d5ac70f0Sopenharmony_ci snd_ctl_elem_info_alloca(&info); 380d5ac70f0Sopenharmony_ci snd_ctl_elem_info_set_interface(info, SND_CTL_ELEM_IFACE_MIXER); 381d5ac70f0Sopenharmony_ci snd_ctl_elem_info_set_name(info, name); 382d5ac70f0Sopenharmony_ci 383d5ac70f0Sopenharmony_ci err = trial->add_elem_set(trial, info); 384d5ac70f0Sopenharmony_ci if (err >= 0) 385d5ac70f0Sopenharmony_ci snd_ctl_elem_info_get_id(info, trial->id); 386d5ac70f0Sopenharmony_ci 387d5ac70f0Sopenharmony_ci return err; 388d5ac70f0Sopenharmony_ci} 389d5ac70f0Sopenharmony_ci 390d5ac70f0Sopenharmony_cistatic int check_event(struct elem_set_trial *trial, unsigned int mask, 391d5ac70f0Sopenharmony_ci unsigned int expected_count) 392d5ac70f0Sopenharmony_ci{ 393d5ac70f0Sopenharmony_ci struct pollfd pfds; 394d5ac70f0Sopenharmony_ci int count; 395d5ac70f0Sopenharmony_ci unsigned short revents; 396d5ac70f0Sopenharmony_ci snd_ctl_event_t *event; 397d5ac70f0Sopenharmony_ci int err; 398d5ac70f0Sopenharmony_ci 399d5ac70f0Sopenharmony_ci snd_ctl_event_alloca(&event); 400d5ac70f0Sopenharmony_ci 401d5ac70f0Sopenharmony_ci if (snd_ctl_poll_descriptors_count(trial->handle) != 1) 402d5ac70f0Sopenharmony_ci return -ENXIO; 403d5ac70f0Sopenharmony_ci 404d5ac70f0Sopenharmony_ci if (snd_ctl_poll_descriptors(trial->handle, &pfds, 1) != 1) 405d5ac70f0Sopenharmony_ci return -ENXIO; 406d5ac70f0Sopenharmony_ci 407d5ac70f0Sopenharmony_ci while (expected_count > 0) { 408d5ac70f0Sopenharmony_ci count = poll(&pfds, 1, 1000); 409d5ac70f0Sopenharmony_ci if (count < 0) 410d5ac70f0Sopenharmony_ci return errno; 411d5ac70f0Sopenharmony_ci /* Some events are already supplied. */ 412d5ac70f0Sopenharmony_ci if (count == 0) 413d5ac70f0Sopenharmony_ci return -ETIMEDOUT; 414d5ac70f0Sopenharmony_ci 415d5ac70f0Sopenharmony_ci err = snd_ctl_poll_descriptors_revents(trial->handle, &pfds, 416d5ac70f0Sopenharmony_ci count, &revents); 417d5ac70f0Sopenharmony_ci if (err < 0) 418d5ac70f0Sopenharmony_ci return err; 419d5ac70f0Sopenharmony_ci if (revents & POLLERR) 420d5ac70f0Sopenharmony_ci return -EIO; 421d5ac70f0Sopenharmony_ci if (!(revents & POLLIN)) 422d5ac70f0Sopenharmony_ci continue; 423d5ac70f0Sopenharmony_ci 424d5ac70f0Sopenharmony_ci err = snd_ctl_read(trial->handle, event); 425d5ac70f0Sopenharmony_ci if (err < 0) 426d5ac70f0Sopenharmony_ci return err; 427d5ac70f0Sopenharmony_ci if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM) 428d5ac70f0Sopenharmony_ci continue; 429d5ac70f0Sopenharmony_ci /* 430d5ac70f0Sopenharmony_ci * I expect each event is generated separately to the same 431d5ac70f0Sopenharmony_ci * element or several events are generated at once. 432d5ac70f0Sopenharmony_ci */ 433d5ac70f0Sopenharmony_ci if ((snd_ctl_event_elem_get_mask(event) & mask) != mask) 434d5ac70f0Sopenharmony_ci continue; 435d5ac70f0Sopenharmony_ci --expected_count; 436d5ac70f0Sopenharmony_ci } 437d5ac70f0Sopenharmony_ci 438d5ac70f0Sopenharmony_ci if (expected_count != 0) 439d5ac70f0Sopenharmony_ci return -EIO; 440d5ac70f0Sopenharmony_ci 441d5ac70f0Sopenharmony_ci return 0; 442d5ac70f0Sopenharmony_ci} 443d5ac70f0Sopenharmony_ci 444d5ac70f0Sopenharmony_cistatic int check_elem_list(struct elem_set_trial *trial) 445d5ac70f0Sopenharmony_ci{ 446d5ac70f0Sopenharmony_ci snd_ctl_elem_list_t *list; 447d5ac70f0Sopenharmony_ci snd_ctl_elem_id_t *id; 448d5ac70f0Sopenharmony_ci int e; 449d5ac70f0Sopenharmony_ci unsigned int i; 450d5ac70f0Sopenharmony_ci int err; 451d5ac70f0Sopenharmony_ci 452d5ac70f0Sopenharmony_ci snd_ctl_elem_list_alloca(&list); 453d5ac70f0Sopenharmony_ci snd_ctl_elem_id_alloca(&id); 454d5ac70f0Sopenharmony_ci 455d5ac70f0Sopenharmony_ci err = snd_ctl_elem_list(trial->handle, list); 456d5ac70f0Sopenharmony_ci if (err < 0) 457d5ac70f0Sopenharmony_ci return err; 458d5ac70f0Sopenharmony_ci 459d5ac70f0Sopenharmony_ci /* Certainly some elements are already added. */ 460d5ac70f0Sopenharmony_ci if (snd_ctl_elem_list_get_count(list) == 0) 461d5ac70f0Sopenharmony_ci return -EIO; 462d5ac70f0Sopenharmony_ci 463d5ac70f0Sopenharmony_ci err = snd_ctl_elem_list_alloc_space(list, 464d5ac70f0Sopenharmony_ci snd_ctl_elem_list_get_count(list)); 465d5ac70f0Sopenharmony_ci if (err < 0) 466d5ac70f0Sopenharmony_ci return err; 467d5ac70f0Sopenharmony_ci 468d5ac70f0Sopenharmony_ci err = snd_ctl_elem_list(trial->handle, list); 469d5ac70f0Sopenharmony_ci if (err < 0) 470d5ac70f0Sopenharmony_ci goto end; 471d5ac70f0Sopenharmony_ci 472d5ac70f0Sopenharmony_ci if (trial->element_count > snd_ctl_elem_list_get_count(list)) { 473d5ac70f0Sopenharmony_ci err = -EIO; 474d5ac70f0Sopenharmony_ci goto end; 475d5ac70f0Sopenharmony_ci } 476d5ac70f0Sopenharmony_ci 477d5ac70f0Sopenharmony_ci i = 0; 478d5ac70f0Sopenharmony_ci for (e = 0; e < snd_ctl_elem_list_get_count(list); ++e) { 479d5ac70f0Sopenharmony_ci snd_ctl_elem_list_get_id(list, e, id); 480d5ac70f0Sopenharmony_ci 481d5ac70f0Sopenharmony_ci if (strcmp(snd_ctl_elem_id_get_name(id), 482d5ac70f0Sopenharmony_ci snd_ctl_elem_id_get_name(trial->id)) != 0) 483d5ac70f0Sopenharmony_ci continue; 484d5ac70f0Sopenharmony_ci if (snd_ctl_elem_id_get_interface(id) != 485d5ac70f0Sopenharmony_ci snd_ctl_elem_id_get_interface(trial->id)) 486d5ac70f0Sopenharmony_ci continue; 487d5ac70f0Sopenharmony_ci if (snd_ctl_elem_id_get_device(id) != 488d5ac70f0Sopenharmony_ci snd_ctl_elem_id_get_device(trial->id)) 489d5ac70f0Sopenharmony_ci continue; 490d5ac70f0Sopenharmony_ci if (snd_ctl_elem_id_get_subdevice(id) != 491d5ac70f0Sopenharmony_ci snd_ctl_elem_id_get_subdevice(trial->id)) 492d5ac70f0Sopenharmony_ci continue; 493d5ac70f0Sopenharmony_ci 494d5ac70f0Sopenharmony_ci /* 495d5ac70f0Sopenharmony_ci * Here, I expect the list includes element ID data in numerical 496d5ac70f0Sopenharmony_ci * order. Actually, it does. 497d5ac70f0Sopenharmony_ci */ 498d5ac70f0Sopenharmony_ci if (snd_ctl_elem_id_get_numid(id) != 499d5ac70f0Sopenharmony_ci snd_ctl_elem_id_get_numid(trial->id) + i) 500d5ac70f0Sopenharmony_ci continue; 501d5ac70f0Sopenharmony_ci if (snd_ctl_elem_id_get_index(id) != 502d5ac70f0Sopenharmony_ci snd_ctl_elem_id_get_index(trial->id) + i) 503d5ac70f0Sopenharmony_ci continue; 504d5ac70f0Sopenharmony_ci 505d5ac70f0Sopenharmony_ci ++i; 506d5ac70f0Sopenharmony_ci } 507d5ac70f0Sopenharmony_ci 508d5ac70f0Sopenharmony_ci if (i != trial->element_count) 509d5ac70f0Sopenharmony_ci err = -EIO; 510d5ac70f0Sopenharmony_ciend: 511d5ac70f0Sopenharmony_ci snd_ctl_elem_list_free_space(list); 512d5ac70f0Sopenharmony_ci 513d5ac70f0Sopenharmony_ci return err; 514d5ac70f0Sopenharmony_ci} 515d5ac70f0Sopenharmony_ci 516d5ac70f0Sopenharmony_cistatic int check_elem_set_props(struct elem_set_trial *trial) 517d5ac70f0Sopenharmony_ci{ 518d5ac70f0Sopenharmony_ci snd_ctl_elem_id_t *id; 519d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info; 520d5ac70f0Sopenharmony_ci unsigned int numid; 521d5ac70f0Sopenharmony_ci unsigned int index; 522d5ac70f0Sopenharmony_ci unsigned int i; 523d5ac70f0Sopenharmony_ci unsigned int j; 524d5ac70f0Sopenharmony_ci int err; 525d5ac70f0Sopenharmony_ci 526d5ac70f0Sopenharmony_ci snd_ctl_elem_id_alloca(&id); 527d5ac70f0Sopenharmony_ci snd_ctl_elem_info_alloca(&info); 528d5ac70f0Sopenharmony_ci 529d5ac70f0Sopenharmony_ci snd_ctl_elem_info_set_id(info, trial->id); 530d5ac70f0Sopenharmony_ci numid = snd_ctl_elem_id_get_numid(trial->id); 531d5ac70f0Sopenharmony_ci index = snd_ctl_elem_id_get_index(trial->id); 532d5ac70f0Sopenharmony_ci 533d5ac70f0Sopenharmony_ci for (i = 0; i < trial->element_count; ++i) { 534d5ac70f0Sopenharmony_ci snd_ctl_elem_info_set_index(info, index + i); 535d5ac70f0Sopenharmony_ci 536d5ac70f0Sopenharmony_ci /* 537d5ac70f0Sopenharmony_ci * In Linux 4.0 or former, ioctl(SNDRV_CTL_IOCTL_ELEM_ADD) 538d5ac70f0Sopenharmony_ci * doesn't fill all of fields for identification. 539d5ac70f0Sopenharmony_ci */ 540d5ac70f0Sopenharmony_ci if (numid > 0) 541d5ac70f0Sopenharmony_ci snd_ctl_elem_info_set_numid(info, numid + i); 542d5ac70f0Sopenharmony_ci 543d5ac70f0Sopenharmony_ci err = snd_ctl_elem_info(trial->handle, info); 544d5ac70f0Sopenharmony_ci if (err < 0) 545d5ac70f0Sopenharmony_ci return err; 546d5ac70f0Sopenharmony_ci 547d5ac70f0Sopenharmony_ci /* Check some common properties. */ 548d5ac70f0Sopenharmony_ci if (snd_ctl_elem_info_get_type(info) != trial->type) 549d5ac70f0Sopenharmony_ci return -EIO; 550d5ac70f0Sopenharmony_ci if (snd_ctl_elem_info_get_count(info) != trial->member_count) 551d5ac70f0Sopenharmony_ci return -EIO; 552d5ac70f0Sopenharmony_ci 553d5ac70f0Sopenharmony_ci /* 554d5ac70f0Sopenharmony_ci * In a case of IPC, this is the others. But in this case, 555d5ac70f0Sopenharmony_ci * it's myself. 556d5ac70f0Sopenharmony_ci */ 557d5ac70f0Sopenharmony_ci if (snd_ctl_elem_info_get_owner(info) != getpid()) 558d5ac70f0Sopenharmony_ci return -EIO; 559d5ac70f0Sopenharmony_ci 560d5ac70f0Sopenharmony_ci /* 561d5ac70f0Sopenharmony_ci * Just adding an element set by userspace applications, 562d5ac70f0Sopenharmony_ci * included elements are initially locked. 563d5ac70f0Sopenharmony_ci */ 564d5ac70f0Sopenharmony_ci if (!snd_ctl_elem_info_is_locked(info)) 565d5ac70f0Sopenharmony_ci return -EIO; 566d5ac70f0Sopenharmony_ci 567d5ac70f0Sopenharmony_ci /* 568d5ac70f0Sopenharmony_ci * In initial state, any application can register TLV data for 569d5ac70f0Sopenharmony_ci * user-defined element set except for IEC 958 type, thus 570d5ac70f0Sopenharmony_ci * elements in any user-defined set should allow any write 571d5ac70f0Sopenharmony_ci * operation. 572d5ac70f0Sopenharmony_ci */ 573d5ac70f0Sopenharmony_ci if (trial->type != SND_CTL_ELEM_TYPE_IEC958 && 574d5ac70f0Sopenharmony_ci !snd_ctl_elem_info_is_tlv_writable(info)) 575d5ac70f0Sopenharmony_ci return -EIO; 576d5ac70f0Sopenharmony_ci 577d5ac70f0Sopenharmony_ci /* Check type-specific properties. */ 578d5ac70f0Sopenharmony_ci if (trial->check_elem_props != NULL) { 579d5ac70f0Sopenharmony_ci err = trial->check_elem_props(trial, info); 580d5ac70f0Sopenharmony_ci if (err < 0) 581d5ac70f0Sopenharmony_ci return err; 582d5ac70f0Sopenharmony_ci } 583d5ac70f0Sopenharmony_ci 584d5ac70f0Sopenharmony_ci snd_ctl_elem_info_get_id(info, id); 585d5ac70f0Sopenharmony_ci err = snd_ctl_elem_unlock(trial->handle, id); 586d5ac70f0Sopenharmony_ci if (err < 0) 587d5ac70f0Sopenharmony_ci return err; 588d5ac70f0Sopenharmony_ci 589d5ac70f0Sopenharmony_ci /* 590d5ac70f0Sopenharmony_ci * Till kernel v4.14, ALSA control core allows elements in any 591d5ac70f0Sopenharmony_ci * user-defined set to have TLV_READ flag even if they have no 592d5ac70f0Sopenharmony_ci * TLV data in their initial state. In this case, any read 593d5ac70f0Sopenharmony_ci * operation for TLV data should return -ENXIO. 594d5ac70f0Sopenharmony_ci */ 595d5ac70f0Sopenharmony_ci if (snd_ctl_elem_info_is_tlv_readable(info)) { 596d5ac70f0Sopenharmony_ci unsigned int data[32]; 597d5ac70f0Sopenharmony_ci err = snd_ctl_elem_tlv_read(trial->handle, trial->id, 598d5ac70f0Sopenharmony_ci data, sizeof(data)); 599d5ac70f0Sopenharmony_ci if (err >= 0) 600d5ac70f0Sopenharmony_ci return -EIO; 601d5ac70f0Sopenharmony_ci if (err != -ENXIO) 602d5ac70f0Sopenharmony_ci return err; 603d5ac70f0Sopenharmony_ci 604d5ac70f0Sopenharmony_ci trial->tlv_readable = true; 605d5ac70f0Sopenharmony_ci } 606d5ac70f0Sopenharmony_ci 607d5ac70f0Sopenharmony_ci } 608d5ac70f0Sopenharmony_ci 609d5ac70f0Sopenharmony_ci return 0; 610d5ac70f0Sopenharmony_ci} 611d5ac70f0Sopenharmony_ci 612d5ac70f0Sopenharmony_cistatic int check_elems(struct elem_set_trial *trial) 613d5ac70f0Sopenharmony_ci{ 614d5ac70f0Sopenharmony_ci snd_ctl_elem_value_t *data; 615d5ac70f0Sopenharmony_ci unsigned int numid; 616d5ac70f0Sopenharmony_ci unsigned int index; 617d5ac70f0Sopenharmony_ci unsigned int i; 618d5ac70f0Sopenharmony_ci int err; 619d5ac70f0Sopenharmony_ci 620d5ac70f0Sopenharmony_ci snd_ctl_elem_value_alloca(&data); 621d5ac70f0Sopenharmony_ci 622d5ac70f0Sopenharmony_ci snd_ctl_elem_value_set_id(data, trial->id); 623d5ac70f0Sopenharmony_ci numid = snd_ctl_elem_id_get_numid(trial->id); 624d5ac70f0Sopenharmony_ci index = snd_ctl_elem_id_get_index(trial->id); 625d5ac70f0Sopenharmony_ci 626d5ac70f0Sopenharmony_ci for (i = 0; i < trial->element_count; ++i) { 627d5ac70f0Sopenharmony_ci snd_ctl_elem_value_set_index(data, index + i); 628d5ac70f0Sopenharmony_ci 629d5ac70f0Sopenharmony_ci /* 630d5ac70f0Sopenharmony_ci * In Linux 4.0 or former, ioctl(SNDRV_CTL_IOCTL_ELEM_ADD) 631d5ac70f0Sopenharmony_ci * doesn't fill all of fields for identification. 632d5ac70f0Sopenharmony_ci */ 633d5ac70f0Sopenharmony_ci if (numid > 0) 634d5ac70f0Sopenharmony_ci snd_ctl_elem_value_set_numid(data, numid + i); 635d5ac70f0Sopenharmony_ci 636d5ac70f0Sopenharmony_ci err = snd_ctl_elem_read(trial->handle, data); 637d5ac70f0Sopenharmony_ci if (err < 0) 638d5ac70f0Sopenharmony_ci return err; 639d5ac70f0Sopenharmony_ci 640d5ac70f0Sopenharmony_ci /* Change members of an element in this element set. */ 641d5ac70f0Sopenharmony_ci trial->change_elem_members(trial, data); 642d5ac70f0Sopenharmony_ci 643d5ac70f0Sopenharmony_ci err = snd_ctl_elem_write(trial->handle, data); 644d5ac70f0Sopenharmony_ci if (err < 0) 645d5ac70f0Sopenharmony_ci return err; 646d5ac70f0Sopenharmony_ci } 647d5ac70f0Sopenharmony_ci 648d5ac70f0Sopenharmony_ci return 0; 649d5ac70f0Sopenharmony_ci} 650d5ac70f0Sopenharmony_ci 651d5ac70f0Sopenharmony_cistatic int check_tlv(struct elem_set_trial *trial) 652d5ac70f0Sopenharmony_ci{ 653d5ac70f0Sopenharmony_ci unsigned int *tlv; 654d5ac70f0Sopenharmony_ci int mask; 655d5ac70f0Sopenharmony_ci unsigned int count; 656d5ac70f0Sopenharmony_ci unsigned int len; 657d5ac70f0Sopenharmony_ci unsigned int *curr; 658d5ac70f0Sopenharmony_ci int err; 659d5ac70f0Sopenharmony_ci 660d5ac70f0Sopenharmony_ci err = trial->allocate_elem_set_tlv(trial, &tlv); 661d5ac70f0Sopenharmony_ci if (err < 0) 662d5ac70f0Sopenharmony_ci return err; 663d5ac70f0Sopenharmony_ci 664d5ac70f0Sopenharmony_ci len = tlv[SNDRV_CTL_TLVO_LEN] + sizeof(unsigned int) * 2; 665d5ac70f0Sopenharmony_ci curr = malloc(len); 666d5ac70f0Sopenharmony_ci if (curr == NULL) { 667d5ac70f0Sopenharmony_ci free(tlv); 668d5ac70f0Sopenharmony_ci return -ENOMEM; 669d5ac70f0Sopenharmony_ci } 670d5ac70f0Sopenharmony_ci 671d5ac70f0Sopenharmony_ci /* 672d5ac70f0Sopenharmony_ci * In in-kernel implementation, write and command operations are the 673d5ac70f0Sopenharmony_ci * same for an element set added by userspace applications. Here, I 674d5ac70f0Sopenharmony_ci * use write. 675d5ac70f0Sopenharmony_ci */ 676d5ac70f0Sopenharmony_ci err = snd_ctl_elem_tlv_write(trial->handle, trial->id, 677d5ac70f0Sopenharmony_ci (const unsigned int *)tlv); 678d5ac70f0Sopenharmony_ci if (err < 0) 679d5ac70f0Sopenharmony_ci goto end; 680d5ac70f0Sopenharmony_ci 681d5ac70f0Sopenharmony_ci /* 682d5ac70f0Sopenharmony_ci * Since kernel v4.14, any write operation to an element in user-defined 683d5ac70f0Sopenharmony_ci * set can change state of the other elements in the same set. In this 684d5ac70f0Sopenharmony_ci * case, any TLV data is firstly available after the operation. 685d5ac70f0Sopenharmony_ci */ 686d5ac70f0Sopenharmony_ci if (!trial->tlv_readable) { 687d5ac70f0Sopenharmony_ci mask = SND_CTL_EVENT_MASK_INFO | SND_CTL_EVENT_MASK_TLV; 688d5ac70f0Sopenharmony_ci count = trial->element_count; 689d5ac70f0Sopenharmony_ci } else { 690d5ac70f0Sopenharmony_ci mask = SND_CTL_EVENT_MASK_TLV; 691d5ac70f0Sopenharmony_ci count = 1; 692d5ac70f0Sopenharmony_ci } 693d5ac70f0Sopenharmony_ci err = check_event(trial, mask, count); 694d5ac70f0Sopenharmony_ci if (err < 0) 695d5ac70f0Sopenharmony_ci goto end; 696d5ac70f0Sopenharmony_ci if (!trial->tlv_readable) { 697d5ac70f0Sopenharmony_ci snd_ctl_elem_info_t *info; 698d5ac70f0Sopenharmony_ci snd_ctl_elem_info_alloca(&info); 699d5ac70f0Sopenharmony_ci 700d5ac70f0Sopenharmony_ci snd_ctl_elem_info_set_id(info, trial->id); 701d5ac70f0Sopenharmony_ci err = snd_ctl_elem_info(trial->handle, info); 702d5ac70f0Sopenharmony_ci if (err < 0) 703d5ac70f0Sopenharmony_ci return err; 704d5ac70f0Sopenharmony_ci if (!snd_ctl_elem_info_is_tlv_readable(info)) 705d5ac70f0Sopenharmony_ci return -EIO; 706d5ac70f0Sopenharmony_ci 707d5ac70f0Sopenharmony_ci /* Now TLV data is available for this element set. */ 708d5ac70f0Sopenharmony_ci trial->tlv_readable = true; 709d5ac70f0Sopenharmony_ci } 710d5ac70f0Sopenharmony_ci 711d5ac70f0Sopenharmony_ci err = snd_ctl_elem_tlv_read(trial->handle, trial->id, curr, len); 712d5ac70f0Sopenharmony_ci if (err < 0) 713d5ac70f0Sopenharmony_ci goto end; 714d5ac70f0Sopenharmony_ci 715d5ac70f0Sopenharmony_ci if (memcmp(curr, tlv, len) != 0) 716d5ac70f0Sopenharmony_ci err = -EIO; 717d5ac70f0Sopenharmony_ciend: 718d5ac70f0Sopenharmony_ci free(tlv); 719d5ac70f0Sopenharmony_ci free(curr); 720d5ac70f0Sopenharmony_ci return 0; 721d5ac70f0Sopenharmony_ci} 722d5ac70f0Sopenharmony_ci 723d5ac70f0Sopenharmony_ciint main(void) 724d5ac70f0Sopenharmony_ci{ 725d5ac70f0Sopenharmony_ci struct elem_set_trial trial = {0}; 726d5ac70f0Sopenharmony_ci unsigned int i; 727d5ac70f0Sopenharmony_ci int err; 728d5ac70f0Sopenharmony_ci 729d5ac70f0Sopenharmony_ci snd_ctl_elem_id_alloca(&trial.id); 730d5ac70f0Sopenharmony_ci 731d5ac70f0Sopenharmony_ci err = snd_ctl_open(&trial.handle, "hw:0", 0); 732d5ac70f0Sopenharmony_ci if (err < 0) 733d5ac70f0Sopenharmony_ci return EXIT_FAILURE; 734d5ac70f0Sopenharmony_ci 735d5ac70f0Sopenharmony_ci err = snd_ctl_subscribe_events(trial.handle, 1); 736d5ac70f0Sopenharmony_ci if (err < 0) 737d5ac70f0Sopenharmony_ci return EXIT_FAILURE; 738d5ac70f0Sopenharmony_ci 739d5ac70f0Sopenharmony_ci /* Test all of types. */ 740d5ac70f0Sopenharmony_ci for (i = 0; i < SND_CTL_ELEM_TYPE_LAST; ++i) { 741d5ac70f0Sopenharmony_ci trial.type = i + 1; 742d5ac70f0Sopenharmony_ci 743d5ac70f0Sopenharmony_ci /* Assign type-dependent operations. */ 744d5ac70f0Sopenharmony_ci switch (trial.type) { 745d5ac70f0Sopenharmony_ci case SND_CTL_ELEM_TYPE_BOOLEAN: 746d5ac70f0Sopenharmony_ci trial.element_count = 900; 747d5ac70f0Sopenharmony_ci trial.member_count = 128; 748d5ac70f0Sopenharmony_ci trial.add_elem_set = add_bool_elem_set; 749d5ac70f0Sopenharmony_ci trial.check_elem_props = NULL; 750d5ac70f0Sopenharmony_ci trial.change_elem_members = change_bool_elem_members; 751d5ac70f0Sopenharmony_ci trial.allocate_elem_set_tlv = 752d5ac70f0Sopenharmony_ci allocate_bool_elem_set_tlv; 753d5ac70f0Sopenharmony_ci trial.tlv_readable = false; 754d5ac70f0Sopenharmony_ci break; 755d5ac70f0Sopenharmony_ci case SND_CTL_ELEM_TYPE_INTEGER: 756d5ac70f0Sopenharmony_ci trial.element_count = 900; 757d5ac70f0Sopenharmony_ci trial.member_count = 128; 758d5ac70f0Sopenharmony_ci trial.add_elem_set = add_int_elem_set; 759d5ac70f0Sopenharmony_ci trial.check_elem_props = check_int_elem_props; 760d5ac70f0Sopenharmony_ci trial.change_elem_members = change_int_elem_members; 761d5ac70f0Sopenharmony_ci trial.allocate_elem_set_tlv = 762d5ac70f0Sopenharmony_ci allocate_int_elem_set_tlv; 763d5ac70f0Sopenharmony_ci trial.tlv_readable = false; 764d5ac70f0Sopenharmony_ci break; 765d5ac70f0Sopenharmony_ci case SND_CTL_ELEM_TYPE_ENUMERATED: 766d5ac70f0Sopenharmony_ci trial.element_count = 900; 767d5ac70f0Sopenharmony_ci trial.member_count = 128; 768d5ac70f0Sopenharmony_ci trial.add_elem_set = add_enum_elem_set; 769d5ac70f0Sopenharmony_ci trial.check_elem_props = check_enum_elem_props; 770d5ac70f0Sopenharmony_ci trial.change_elem_members = change_enum_elem_members; 771d5ac70f0Sopenharmony_ci trial.allocate_elem_set_tlv = NULL; 772d5ac70f0Sopenharmony_ci trial.tlv_readable = false; 773d5ac70f0Sopenharmony_ci break; 774d5ac70f0Sopenharmony_ci case SND_CTL_ELEM_TYPE_BYTES: 775d5ac70f0Sopenharmony_ci trial.element_count = 900; 776d5ac70f0Sopenharmony_ci trial.member_count = 512; 777d5ac70f0Sopenharmony_ci trial.add_elem_set = add_bytes_elem_set; 778d5ac70f0Sopenharmony_ci trial.check_elem_props = NULL; 779d5ac70f0Sopenharmony_ci trial.change_elem_members = change_bytes_elem_members; 780d5ac70f0Sopenharmony_ci trial.allocate_elem_set_tlv = 781d5ac70f0Sopenharmony_ci allocate_bytes_elem_set_tlv; 782d5ac70f0Sopenharmony_ci trial.tlv_readable = false; 783d5ac70f0Sopenharmony_ci break; 784d5ac70f0Sopenharmony_ci case SND_CTL_ELEM_TYPE_IEC958: 785d5ac70f0Sopenharmony_ci trial.element_count = 1; 786d5ac70f0Sopenharmony_ci trial.member_count = 1; 787d5ac70f0Sopenharmony_ci trial.add_elem_set = add_iec958_elem_set; 788d5ac70f0Sopenharmony_ci trial.check_elem_props = NULL; 789d5ac70f0Sopenharmony_ci trial.change_elem_members = change_iec958_elem_members; 790d5ac70f0Sopenharmony_ci trial.allocate_elem_set_tlv = NULL; 791d5ac70f0Sopenharmony_ci trial.tlv_readable = false; 792d5ac70f0Sopenharmony_ci break; 793d5ac70f0Sopenharmony_ci case SND_CTL_ELEM_TYPE_INTEGER64: 794d5ac70f0Sopenharmony_ci default: 795d5ac70f0Sopenharmony_ci trial.element_count = 900; 796d5ac70f0Sopenharmony_ci trial.member_count = 64; 797d5ac70f0Sopenharmony_ci trial.add_elem_set = add_int64_elem_set; 798d5ac70f0Sopenharmony_ci trial.check_elem_props = check_int64_elem_props; 799d5ac70f0Sopenharmony_ci trial.change_elem_members = change_int64_elem_members; 800d5ac70f0Sopenharmony_ci trial.allocate_elem_set_tlv = 801d5ac70f0Sopenharmony_ci allocate_int64_elem_set_tlv; 802d5ac70f0Sopenharmony_ci trial.tlv_readable = false; 803d5ac70f0Sopenharmony_ci break; 804d5ac70f0Sopenharmony_ci } 805d5ac70f0Sopenharmony_ci 806d5ac70f0Sopenharmony_ci /* Test an operation to add an element set. */ 807d5ac70f0Sopenharmony_ci err = add_elem_set(&trial); 808d5ac70f0Sopenharmony_ci if (err < 0) { 809d5ac70f0Sopenharmony_ci printf("Fail to add an element set with %s type.\n", 810d5ac70f0Sopenharmony_ci snd_ctl_elem_type_name(trial.type)); 811d5ac70f0Sopenharmony_ci break; 812d5ac70f0Sopenharmony_ci } 813d5ac70f0Sopenharmony_ci err = check_event(&trial, SND_CTL_EVENT_MASK_ADD, 814d5ac70f0Sopenharmony_ci trial.element_count); 815d5ac70f0Sopenharmony_ci if (err < 0) { 816d5ac70f0Sopenharmony_ci printf("Fail to check some events to add elements with " 817d5ac70f0Sopenharmony_ci "%s type.\n", 818d5ac70f0Sopenharmony_ci snd_ctl_elem_type_name(trial.type)); 819d5ac70f0Sopenharmony_ci break; 820d5ac70f0Sopenharmony_ci } 821d5ac70f0Sopenharmony_ci 822d5ac70f0Sopenharmony_ci /* Check added elements are retrieved in a list. */ 823d5ac70f0Sopenharmony_ci err = check_elem_list(&trial); 824d5ac70f0Sopenharmony_ci if (err < 0) { 825d5ac70f0Sopenharmony_ci printf("Fail to list each element with %s type.\n", 826d5ac70f0Sopenharmony_ci snd_ctl_elem_type_name(trial.type)); 827d5ac70f0Sopenharmony_ci break; 828d5ac70f0Sopenharmony_ci } 829d5ac70f0Sopenharmony_ci 830d5ac70f0Sopenharmony_ci /* Check properties of each element in this element set. */ 831d5ac70f0Sopenharmony_ci err = check_elem_set_props(&trial); 832d5ac70f0Sopenharmony_ci if (err < 0) { 833d5ac70f0Sopenharmony_ci printf("Fail to check properties of each element with " 834d5ac70f0Sopenharmony_ci "%s type.\n", 835d5ac70f0Sopenharmony_ci snd_ctl_elem_type_name(trial.type)); 836d5ac70f0Sopenharmony_ci break; 837d5ac70f0Sopenharmony_ci } 838d5ac70f0Sopenharmony_ci 839d5ac70f0Sopenharmony_ci /* 840d5ac70f0Sopenharmony_ci * Test operations to change the state of members in each 841d5ac70f0Sopenharmony_ci * element in the element set. 842d5ac70f0Sopenharmony_ci */ 843d5ac70f0Sopenharmony_ci err = check_elems(&trial); 844d5ac70f0Sopenharmony_ci if (err < 0) { 845d5ac70f0Sopenharmony_ci printf("Fail to change status of each element with %s " 846d5ac70f0Sopenharmony_ci "type.\n", 847d5ac70f0Sopenharmony_ci snd_ctl_elem_type_name(trial.type)); 848d5ac70f0Sopenharmony_ci break; 849d5ac70f0Sopenharmony_ci } 850d5ac70f0Sopenharmony_ci err = check_event(&trial, SND_CTL_EVENT_MASK_VALUE, 851d5ac70f0Sopenharmony_ci trial.element_count); 852d5ac70f0Sopenharmony_ci if (err < 0) { 853d5ac70f0Sopenharmony_ci printf("Fail to check some events to change status of " 854d5ac70f0Sopenharmony_ci "each elements with %s type.\n", 855d5ac70f0Sopenharmony_ci snd_ctl_elem_type_name(trial.type)); 856d5ac70f0Sopenharmony_ci break; 857d5ac70f0Sopenharmony_ci } 858d5ac70f0Sopenharmony_ci 859d5ac70f0Sopenharmony_ci /* 860d5ac70f0Sopenharmony_ci * Test an operation to change TLV data of this element set, 861d5ac70f0Sopenharmony_ci * except for enumerated and IEC958 type. 862d5ac70f0Sopenharmony_ci */ 863d5ac70f0Sopenharmony_ci if (trial.allocate_elem_set_tlv != NULL) { 864d5ac70f0Sopenharmony_ci err = check_tlv(&trial); 865d5ac70f0Sopenharmony_ci if (err < 0) { 866d5ac70f0Sopenharmony_ci printf("Fail to change TLV data of an element " 867d5ac70f0Sopenharmony_ci "set with %s type.\n", 868d5ac70f0Sopenharmony_ci snd_ctl_elem_type_name(trial.type)); 869d5ac70f0Sopenharmony_ci break; 870d5ac70f0Sopenharmony_ci } 871d5ac70f0Sopenharmony_ci } 872d5ac70f0Sopenharmony_ci 873d5ac70f0Sopenharmony_ci /* Test an operation to remove elements in this element set. */ 874d5ac70f0Sopenharmony_ci err = snd_ctl_elem_remove(trial.handle, trial.id); 875d5ac70f0Sopenharmony_ci if (err < 0) { 876d5ac70f0Sopenharmony_ci printf("Fail to remove elements with %s type.\n", 877d5ac70f0Sopenharmony_ci snd_ctl_elem_type_name(trial.type)); 878d5ac70f0Sopenharmony_ci break; 879d5ac70f0Sopenharmony_ci } 880d5ac70f0Sopenharmony_ci err = check_event(&trial, SND_CTL_EVENT_MASK_REMOVE, 881d5ac70f0Sopenharmony_ci trial.element_count); 882d5ac70f0Sopenharmony_ci if (err < 0) { 883d5ac70f0Sopenharmony_ci printf("Fail to check some events to remove each " 884d5ac70f0Sopenharmony_ci "element with %s type.\n", 885d5ac70f0Sopenharmony_ci snd_ctl_elem_type_name(trial.type)); 886d5ac70f0Sopenharmony_ci break; 887d5ac70f0Sopenharmony_ci } 888d5ac70f0Sopenharmony_ci } 889d5ac70f0Sopenharmony_ci 890d5ac70f0Sopenharmony_ci if (err < 0) { 891d5ac70f0Sopenharmony_ci printf("%s\n", snd_strerror(err)); 892d5ac70f0Sopenharmony_ci 893d5ac70f0Sopenharmony_ci /* To ensure. */ 894d5ac70f0Sopenharmony_ci snd_ctl_elem_remove(trial.handle, trial.id); 895d5ac70f0Sopenharmony_ci return EXIT_FAILURE; 896d5ac70f0Sopenharmony_ci } 897d5ac70f0Sopenharmony_ci 898d5ac70f0Sopenharmony_ci return EXIT_SUCCESS; 899d5ac70f0Sopenharmony_ci} 900