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