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