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
15struct 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
36struct 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. */
57static 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
64static 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
76static 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. */
94static 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
102static 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
115static 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
127static 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. */
169static const char *const labels[] = {
170	"trusty",
171	"utopic",
172	"vivid",
173	"willy",
174	"xenial",
175};
176
177static 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
186static 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
213static 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. */
226static 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
233static 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
245static 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. */
264static 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
282static 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. */
297static 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
305static 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
318static 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
330static 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. */
370static 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
390static 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
444static 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;
510end:
511	snd_ctl_elem_list_free_space(list);
512
513	return err;
514}
515
516static 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
612static 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
651static 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;
717end:
718	free(tlv);
719	free(curr);
720	return 0;
721}
722
723int 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}
900