2 * user-control-element-set.c - a program to test in-kernel implementation of
3 * user-defined control element set.
5 * Copyright (c) 2015-2016 Takashi Sakamoto
7 * Licensed under the terms of the GNU General Public License, version 2.
11 #include "../include/asoundlib.h"
12 #include <sound/tlv.h>
15 struct elem_set_trial {
18 snd_ctl_elem_type_t type;
19 unsigned int member_count;
20 unsigned int element_count;
22 snd_ctl_elem_id_t *id;
24 int (*add_elem_set)(struct elem_set_trial *trial,
25 snd_ctl_elem_info_t *info);
26 int (*check_elem_props)(struct elem_set_trial *trial,
27 snd_ctl_elem_info_t *info);
28 void (*change_elem_members)(struct elem_set_trial *trial,
29 snd_ctl_elem_value_t *elem_data);
30 int (*allocate_elem_set_tlv)(struct elem_set_trial *trial,
43 * History of TLV feature:
45 * 2016/09/15: 398fa4db6c69 ("ALSA: control: move layout of TLV payload to UAPI
47 * 2012/07/21: 2d3391ec0ecc ("ALSA: PCM: channel mapping API implementation")
48 * 2011/11/20: bf1d1c9b6179 ("ALSA: tlv: add DECLARE_TLV_DB_RANGE()")
49 * 2009/07/16: 085f30654175 ("ALSA: Add new TLV types for dBwith min/max")
50 * 2006/09/06: 55a29af5ed5d ("[ALSA] Add definition of TLV dB range compound")
51 * 2006/08/28: 063a40d9111c ("Add the definition of linear volume TLV")
52 * 2006/08/28: 42750b04c5ba ("[ALSA] Control API - TLV implementation for
53 * additional information like dB scale")
56 /* Operations for elements in an element set with boolean type. */
57 static int add_bool_elem_set(struct elem_set_trial *trial,
58 snd_ctl_elem_info_t *info)
60 return snd_ctl_add_boolean_elem_set(trial->handle, info,
61 trial->element_count, trial->member_count);
64 static void change_bool_elem_members(struct elem_set_trial *trial,
65 snd_ctl_elem_value_t *elem_data)
70 for (i = 0; i < trial->member_count; ++i) {
71 val = snd_ctl_elem_value_get_boolean(elem_data, i);
72 snd_ctl_elem_value_set_boolean(elem_data, i, !val);
76 static int allocate_bool_elem_set_tlv(struct elem_set_trial *trial,
80 * Performs like a toggle switch for attenuation, because they're bool
83 static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(range, -10000, 0);
85 *tlv = malloc(sizeof(range));
88 memcpy(*tlv, range, sizeof(range));
93 /* Operations for elements in an element set with integer type. */
94 static int add_int_elem_set(struct elem_set_trial *trial,
95 snd_ctl_elem_info_t *info)
97 return snd_ctl_add_integer_elem_set(trial->handle, info,
98 trial->element_count, trial->member_count,
102 static int check_int_elem_props(struct elem_set_trial *trial,
103 snd_ctl_elem_info_t *info)
105 if (snd_ctl_elem_info_get_min(info) != 0)
107 if (snd_ctl_elem_info_get_max(info) != 25)
109 if (snd_ctl_elem_info_get_step(info) != 1)
115 static void change_int_elem_members(struct elem_set_trial *trial,
116 snd_ctl_elem_value_t *elem_data)
121 for (i = 0; i < trial->member_count; ++i) {
122 val = snd_ctl_elem_value_get_integer(elem_data, i);
123 snd_ctl_elem_value_set_integer(elem_data, i, ++val);
127 static int allocate_int_elem_set_tlv(struct elem_set_trial *trial,
130 unsigned int count, pos;
132 struct chmap_entry *entry;
134 /* Calculate size of TLV packet for channel-mapping information. */
136 for (i = 1; i <= 25; ++i) {
137 count += 2; /* sizeof(struct chmap_entry). */
138 count += i; /* struct chmap_entry.maps. */
141 *tlv = malloc((2 + count) * sizeof(unsigned int));
146 * Emulate channel-mapping information in in-kernel implementation.
147 * Here, 25 entries are for each different channel.
149 (*tlv)[0] = SNDRV_CTL_TLVT_CONTAINER;
150 (*tlv)[1] = count * sizeof(unsigned int);
153 for (i = 1; i <= 25 && pos < count; ++i) {
154 entry = (struct chmap_entry *)&(*tlv)[pos];
156 entry->type = SNDRV_CTL_TLVT_CHMAP_FIXED;
157 entry->length = i * sizeof(unsigned int);
160 for (j = 0; j < i; ++j)
161 entry->maps[j] = SND_CHMAP_MONO + j;
168 /* Operations for elements in an element set with enumerated type. */
169 static const char *const labels[] = {
177 static int add_enum_elem_set(struct elem_set_trial *trial,
178 snd_ctl_elem_info_t *info)
180 return snd_ctl_add_enumerated_elem_set(trial->handle, info,
181 trial->element_count, trial->member_count,
182 sizeof(labels) / sizeof(labels[0]),
186 static int check_enum_elem_props(struct elem_set_trial *trial,
187 snd_ctl_elem_info_t *info)
194 items = snd_ctl_elem_info_get_items(info);
195 if (items != sizeof(labels) / sizeof(labels[0]))
198 /* Enumerate and validate all of labels registered to this element. */
199 for (i = 0; i < items; ++i) {
200 snd_ctl_elem_info_set_item(info, i);
201 err = snd_ctl_elem_info(trial->handle, info);
205 label = snd_ctl_elem_info_get_item_name(info);
206 if (strncmp(label, labels[i], strlen(labels[i])) != 0)
213 static void change_enum_elem_members(struct elem_set_trial *trial,
214 snd_ctl_elem_value_t *elem_data)
219 for (i = 0; i < trial->member_count; ++i) {
220 val = snd_ctl_elem_value_get_enumerated(elem_data, i);
221 snd_ctl_elem_value_set_enumerated(elem_data, i, ++val);
225 /* Operations for elements in an element set with bytes type. */
226 static int add_bytes_elem_set(struct elem_set_trial *trial,
227 snd_ctl_elem_info_t *info)
229 return snd_ctl_add_bytes_elem_set(trial->handle, info,
230 trial->element_count, trial->member_count);
233 static void change_bytes_elem_members(struct elem_set_trial *trial,
234 snd_ctl_elem_value_t *elem_data)
239 for (i = 0; i < trial->member_count; ++i) {
240 val = snd_ctl_elem_value_get_byte(elem_data, i);
241 snd_ctl_elem_value_set_byte(elem_data, i, ++val);
245 static int allocate_bytes_elem_set_tlv(struct elem_set_trial *trial,
250 * 20 * log10(x/255) (dB)
251 * Here, x is written value.
253 static const SNDRV_CTL_TLVD_DECLARE_DB_LINEAR(range, -4813, 0);
255 *tlv = malloc(sizeof(range));
258 memcpy(*tlv, range, sizeof(range));
263 /* Operations for elements in an element set with iec958 type. */
264 static int add_iec958_elem_set(struct elem_set_trial *trial,
265 snd_ctl_elem_info_t *info)
269 snd_ctl_elem_info_get_id(info, trial->id);
271 err = snd_ctl_elem_add_iec958(trial->handle, trial->id);
276 * In historical reason, the above API is not allowed to fill all of
277 * fields in identification data.
279 return snd_ctl_elem_info(trial->handle, info);
282 static void change_iec958_elem_members(struct elem_set_trial *trial,
283 snd_ctl_elem_value_t *elem_data)
285 snd_aes_iec958_t data;
287 /* To suppress GCC warnings. */
288 trial->element_count = 1;
290 snd_ctl_elem_value_get_iec958(elem_data, &data);
291 /* This is an arbitrary number. */
293 snd_ctl_elem_value_set_iec958(elem_data, &data);
296 /* Operations for elements in an element set with integer64 type. */
297 static int add_int64_elem_set(struct elem_set_trial *trial,
298 snd_ctl_elem_info_t *info)
300 return snd_ctl_add_integer64_elem_set(trial->handle, info,
301 trial->element_count, trial->member_count,
305 static int check_int64_elem_props(struct elem_set_trial *trial,
306 snd_ctl_elem_info_t *info)
308 if (snd_ctl_elem_info_get_min64(info) != 0)
310 if (snd_ctl_elem_info_get_max64(info) != 10000)
312 if (snd_ctl_elem_info_get_step64(info) != 1)
318 static void change_int64_elem_members(struct elem_set_trial *trial,
319 snd_ctl_elem_value_t *elem_data)
324 for (i = 0; i < trial->member_count; ++i) {
325 val = snd_ctl_elem_value_get_integer64(elem_data, i);
326 snd_ctl_elem_value_set_integer64(elem_data, i, ++val);
330 static int allocate_int64_elem_set_tlv(struct elem_set_trial *trial,
334 * Use this fomula between linear/dB value:
336 * Linear: dB range (coeff)
337 * 0<-> 4: -59.40<->-56.36 (44)
338 * 4<->22: -56.36<->-45.56 (60)
339 * 22<->33: -45.56<->-40.72 (76)
340 * 33<->37: -40.72<->-38.32 (44)
341 * 37<->48: -38.32<->-29.96 (76)
342 * 48<->66: -29.96<->-22.04 (60)
343 * 66<->84: -22.04<-> -8.36 (44)
344 * 84<->95: -8.36<-> -1.76 (60)
345 * 95<->99: -1.76<-> 0.00 (76)
348 static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(range,
349 0, 4, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-5940, 44, 1),
350 4, 22, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-5636, 60, 0),
351 22, 33, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-4556, 76, 0),
352 33, 37, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-4072, 44, 0),
353 37, 48, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-3832, 76, 0),
354 48, 66, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-2996, 60, 0),
355 66, 84, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-2204, 44, 0),
356 84, 95, SNDRV_CTL_TLVD_DB_SCALE_ITEM( -836, 60, 0),
357 95, 99, SNDRV_CTL_TLVD_DB_SCALE_ITEM( -176, 76, 0),
358 100, 10000, SNDRV_CTL_TLVD_DB_SCALE_ITEM(0, 0, 0),
361 *tlv = malloc(sizeof(range));
364 memcpy(*tlv, range, sizeof(range));
369 /* Common operations. */
370 static int add_elem_set(struct elem_set_trial *trial)
372 snd_ctl_elem_info_t *info;
376 snprintf(name, 64, "userspace-control-element-%s",
377 snd_ctl_elem_type_name(trial->type));
379 snd_ctl_elem_info_alloca(&info);
380 snd_ctl_elem_info_set_interface(info, SND_CTL_ELEM_IFACE_MIXER);
381 snd_ctl_elem_info_set_name(info, name);
383 err = trial->add_elem_set(trial, info);
385 snd_ctl_elem_info_get_id(info, trial->id);
390 static int check_event(struct elem_set_trial *trial, unsigned int mask,
391 unsigned int expected_count)
395 unsigned short revents;
396 snd_ctl_event_t *event;
399 snd_ctl_event_alloca(&event);
401 if (snd_ctl_poll_descriptors_count(trial->handle) != 1)
404 if (snd_ctl_poll_descriptors(trial->handle, &pfds, 1) != 1)
407 while (expected_count > 0) {
408 count = poll(&pfds, 1, 1000);
411 /* Some events are already supplied. */
415 err = snd_ctl_poll_descriptors_revents(trial->handle, &pfds,
419 if (revents & POLLERR)
421 if (!(revents & POLLIN))
424 err = snd_ctl_read(trial->handle, event);
427 if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM)
430 * I expect each event is generated separately to the same
431 * element or several events are generated at once.
433 if ((snd_ctl_event_elem_get_mask(event) & mask) != mask)
438 if (expected_count != 0)
444 static int check_elem_list(struct elem_set_trial *trial)
446 snd_ctl_elem_list_t *list;
447 snd_ctl_elem_id_t *id;
452 snd_ctl_elem_list_alloca(&list);
453 snd_ctl_elem_id_alloca(&id);
455 err = snd_ctl_elem_list(trial->handle, list);
459 /* Certainly some elements are already added. */
460 if (snd_ctl_elem_list_get_count(list) == 0)
463 err = snd_ctl_elem_list_alloc_space(list,
464 snd_ctl_elem_list_get_count(list));
468 err = snd_ctl_elem_list(trial->handle, list);
472 if (trial->element_count > snd_ctl_elem_list_get_count(list)) {
478 for (e = 0; e < snd_ctl_elem_list_get_count(list); ++e) {
479 snd_ctl_elem_list_get_id(list, e, id);
481 if (strcmp(snd_ctl_elem_id_get_name(id),
482 snd_ctl_elem_id_get_name(trial->id)) != 0)
484 if (snd_ctl_elem_id_get_interface(id) !=
485 snd_ctl_elem_id_get_interface(trial->id))
487 if (snd_ctl_elem_id_get_device(id) !=
488 snd_ctl_elem_id_get_device(trial->id))
490 if (snd_ctl_elem_id_get_subdevice(id) !=
491 snd_ctl_elem_id_get_subdevice(trial->id))
495 * Here, I expect the list includes element ID data in numerical
496 * order. Actually, it does.
498 if (snd_ctl_elem_id_get_numid(id) !=
499 snd_ctl_elem_id_get_numid(trial->id) + i)
501 if (snd_ctl_elem_id_get_index(id) !=
502 snd_ctl_elem_id_get_index(trial->id) + i)
508 if (i != trial->element_count)
511 snd_ctl_elem_list_free_space(list);
516 static int check_elem_set_props(struct elem_set_trial *trial)
518 snd_ctl_elem_id_t *id;
519 snd_ctl_elem_info_t *info;
526 snd_ctl_elem_id_alloca(&id);
527 snd_ctl_elem_info_alloca(&info);
529 snd_ctl_elem_info_set_id(info, trial->id);
530 numid = snd_ctl_elem_id_get_numid(trial->id);
531 index = snd_ctl_elem_id_get_index(trial->id);
533 for (i = 0; i < trial->element_count; ++i) {
534 snd_ctl_elem_info_set_index(info, index + i);
537 * In Linux 4.0 or former, ioctl(SNDRV_CTL_IOCTL_ELEM_ADD)
538 * doesn't fill all of fields for identification.
541 snd_ctl_elem_info_set_numid(info, numid + i);
543 err = snd_ctl_elem_info(trial->handle, info);
547 /* Check some common properties. */
548 if (snd_ctl_elem_info_get_type(info) != trial->type)
550 if (snd_ctl_elem_info_get_count(info) != trial->member_count)
554 * In a case of IPC, this is the others. But in this case,
557 if (snd_ctl_elem_info_get_owner(info) != getpid())
561 * Just adding an element set by userspace applications,
562 * included elements are initially locked.
564 if (!snd_ctl_elem_info_is_locked(info))
568 * In initial state, any application can register TLV data for
569 * user-defined element set except for IEC 958 type, thus
570 * elements in any user-defined set should allow any write
573 if (trial->type != SND_CTL_ELEM_TYPE_IEC958 &&
574 !snd_ctl_elem_info_is_tlv_writable(info))
577 /* Check type-specific properties. */
578 if (trial->check_elem_props != NULL) {
579 err = trial->check_elem_props(trial, info);
584 snd_ctl_elem_info_get_id(info, id);
585 err = snd_ctl_elem_unlock(trial->handle, id);
590 * Till kernel v4.14, ALSA control core allows elements in any
591 * user-defined set to have TLV_READ flag even if they have no
592 * TLV data in their initial state. In this case, any read
593 * operation for TLV data should return -ENXIO.
595 if (snd_ctl_elem_info_is_tlv_readable(info)) {
596 unsigned int data[32];
597 err = snd_ctl_elem_tlv_read(trial->handle, trial->id,
604 trial->tlv_readable = true;
612 static int check_elems(struct elem_set_trial *trial)
614 snd_ctl_elem_value_t *data;
620 snd_ctl_elem_value_alloca(&data);
622 snd_ctl_elem_value_set_id(data, trial->id);
623 numid = snd_ctl_elem_id_get_numid(trial->id);
624 index = snd_ctl_elem_id_get_index(trial->id);
626 for (i = 0; i < trial->element_count; ++i) {
627 snd_ctl_elem_value_set_index(data, index + i);
630 * In Linux 4.0 or former, ioctl(SNDRV_CTL_IOCTL_ELEM_ADD)
631 * doesn't fill all of fields for identification.
634 snd_ctl_elem_value_set_numid(data, numid + i);
636 err = snd_ctl_elem_read(trial->handle, data);
640 /* Change members of an element in this element set. */
641 trial->change_elem_members(trial, data);
643 err = snd_ctl_elem_write(trial->handle, data);
651 static int check_tlv(struct elem_set_trial *trial)
660 err = trial->allocate_elem_set_tlv(trial, &tlv);
664 len = tlv[SNDRV_CTL_TLVO_LEN] + sizeof(unsigned int) * 2;
672 * In in-kernel implementation, write and command operations are the
673 * same for an element set added by userspace applications. Here, I
676 err = snd_ctl_elem_tlv_write(trial->handle, trial->id,
677 (const unsigned int *)tlv);
682 * Since kernel v4.14, any write operation to an element in user-defined
683 * set can change state of the other elements in the same set. In this
684 * case, any TLV data is firstly available after the operation.
686 if (!trial->tlv_readable) {
687 mask = SND_CTL_EVENT_MASK_INFO | SND_CTL_EVENT_MASK_TLV;
688 count = trial->element_count;
690 mask = SND_CTL_EVENT_MASK_TLV;
693 err = check_event(trial, mask, count);
696 if (!trial->tlv_readable) {
697 snd_ctl_elem_info_t *info;
698 snd_ctl_elem_info_alloca(&info);
700 snd_ctl_elem_info_set_id(info, trial->id);
701 err = snd_ctl_elem_info(trial->handle, info);
704 if (!snd_ctl_elem_info_is_tlv_readable(info))
707 /* Now TLV data is available for this element set. */
708 trial->tlv_readable = true;
711 err = snd_ctl_elem_tlv_read(trial->handle, trial->id, curr, len);
715 if (memcmp(curr, tlv, len) != 0)
725 struct elem_set_trial trial = {0};
729 snd_ctl_elem_id_alloca(&trial.id);
731 err = snd_ctl_open(&trial.handle, "hw:0", 0);
735 err = snd_ctl_subscribe_events(trial.handle, 1);
739 /* Test all of types. */
740 for (i = 0; i < SND_CTL_ELEM_TYPE_LAST; ++i) {
743 /* Assign type-dependent operations. */
744 switch (trial.type) {
745 case SND_CTL_ELEM_TYPE_BOOLEAN:
746 trial.element_count = 900;
747 trial.member_count = 128;
748 trial.add_elem_set = add_bool_elem_set;
749 trial.check_elem_props = NULL;
750 trial.change_elem_members = change_bool_elem_members;
751 trial.allocate_elem_set_tlv =
752 allocate_bool_elem_set_tlv;
753 trial.tlv_readable = false;
755 case SND_CTL_ELEM_TYPE_INTEGER:
756 trial.element_count = 900;
757 trial.member_count = 128;
758 trial.add_elem_set = add_int_elem_set;
759 trial.check_elem_props = check_int_elem_props;
760 trial.change_elem_members = change_int_elem_members;
761 trial.allocate_elem_set_tlv =
762 allocate_int_elem_set_tlv;
763 trial.tlv_readable = false;
765 case SND_CTL_ELEM_TYPE_ENUMERATED:
766 trial.element_count = 900;
767 trial.member_count = 128;
768 trial.add_elem_set = add_enum_elem_set;
769 trial.check_elem_props = check_enum_elem_props;
770 trial.change_elem_members = change_enum_elem_members;
771 trial.allocate_elem_set_tlv = NULL;
772 trial.tlv_readable = false;
774 case SND_CTL_ELEM_TYPE_BYTES:
775 trial.element_count = 900;
776 trial.member_count = 512;
777 trial.add_elem_set = add_bytes_elem_set;
778 trial.check_elem_props = NULL;
779 trial.change_elem_members = change_bytes_elem_members;
780 trial.allocate_elem_set_tlv =
781 allocate_bytes_elem_set_tlv;
782 trial.tlv_readable = false;
784 case SND_CTL_ELEM_TYPE_IEC958:
785 trial.element_count = 1;
786 trial.member_count = 1;
787 trial.add_elem_set = add_iec958_elem_set;
788 trial.check_elem_props = NULL;
789 trial.change_elem_members = change_iec958_elem_members;
790 trial.allocate_elem_set_tlv = NULL;
791 trial.tlv_readable = false;
793 case SND_CTL_ELEM_TYPE_INTEGER64:
795 trial.element_count = 900;
796 trial.member_count = 64;
797 trial.add_elem_set = add_int64_elem_set;
798 trial.check_elem_props = check_int64_elem_props;
799 trial.change_elem_members = change_int64_elem_members;
800 trial.allocate_elem_set_tlv =
801 allocate_int64_elem_set_tlv;
802 trial.tlv_readable = false;
806 /* Test an operation to add an element set. */
807 err = add_elem_set(&trial);
809 printf("Fail to add an element set with %s type.\n",
810 snd_ctl_elem_type_name(trial.type));
813 err = check_event(&trial, SND_CTL_EVENT_MASK_ADD,
814 trial.element_count);
816 printf("Fail to check some events to add elements with "
818 snd_ctl_elem_type_name(trial.type));
822 /* Check added elements are retrieved in a list. */
823 err = check_elem_list(&trial);
825 printf("Fail to list each element with %s type.\n",
826 snd_ctl_elem_type_name(trial.type));
830 /* Check properties of each element in this element set. */
831 err = check_elem_set_props(&trial);
833 printf("Fail to check properties of each element with "
835 snd_ctl_elem_type_name(trial.type));
840 * Test operations to change the state of members in each
841 * element in the element set.
843 err = check_elems(&trial);
845 printf("Fail to change status of each element with %s "
847 snd_ctl_elem_type_name(trial.type));
850 err = check_event(&trial, SND_CTL_EVENT_MASK_VALUE,
851 trial.element_count);
853 printf("Fail to check some events to change status of "
854 "each elements with %s type.\n",
855 snd_ctl_elem_type_name(trial.type));
860 * Test an operation to change TLV data of this element set,
861 * except for enumerated and IEC958 type.
863 if (trial.allocate_elem_set_tlv != NULL) {
864 err = check_tlv(&trial);
866 printf("Fail to change TLV data of an element "
867 "set with %s type.\n",
868 snd_ctl_elem_type_name(trial.type));
873 /* Test an operation to remove elements in this element set. */
874 err = snd_ctl_elem_remove(trial.handle, trial.id);
876 printf("Fail to remove elements with %s type.\n",
877 snd_ctl_elem_type_name(trial.type));
880 err = check_event(&trial, SND_CTL_EVENT_MASK_REMOVE,
881 trial.element_count);
883 printf("Fail to check some events to remove each "
884 "element with %s type.\n",
885 snd_ctl_elem_type_name(trial.type));
891 printf("%s\n", snd_strerror(err));
894 snd_ctl_elem_remove(trial.handle, trial.id);