1 /**
2  * \file control/setup.c
3  * \brief Routines to setup control primitives from configuration
4  * \author Abramo Bagnara <abramo@alsa-project.org>
5  * \author Jaroslav Kysela <perex@perex.cz>
6  * \date 2001
7  *
8  * Routines to setup control primitives from configuration
9  */
10 /*
11  *  Control Interface - routines for setup from configuration
12  *  Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
13  *			  Jaroslav Kysela <perex@perex.cz>
14  *
15  *
16  *   This library is free software; you can redistribute it and/or modify
17  *   it under the terms of the GNU Lesser General Public License as
18  *   published by the Free Software Foundation; either version 2.1 of
19  *   the License, or (at your option) any later version.
20  *
21  *   This program is distributed in the hope that it will be useful,
22  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
23  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  *   GNU Lesser General Public License for more details.
25  *
26  *   You should have received a copy of the GNU Lesser General Public
27  *   License along with this library; if not, write to the Free Software
28  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
29  *
30  */
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include "local.h"
39 
40 #ifndef DOC_HIDDEN
41 typedef struct {
42 	unsigned int lock: 1;
43 	unsigned int preserve: 1;
44 	snd_ctl_elem_id_t *id;
45 	snd_ctl_elem_info_t *info;
46 	snd_ctl_elem_value_t *val;
47 	snd_ctl_elem_value_t *mask;
48 	snd_ctl_elem_value_t *old;
49 	struct list_head list;
50 } snd_sctl_elem_t;
51 
52 struct _snd_sctl {
53 	int mode;
54 	snd_ctl_t *ctl;
55 	struct list_head elems;
56 };
57 #endif /* DOC_HIDDEN */
58 
free_elems(snd_sctl_t * h)59 static int free_elems(snd_sctl_t *h)
60 {
61 	int err = 0;
62 	while (!list_empty(&h->elems)) {
63 		snd_sctl_elem_t *elem = list_entry(h->elems.next, snd_sctl_elem_t, list);
64 		snd_ctl_elem_id_free(elem->id);
65 		snd_ctl_elem_info_free(elem->info);
66 		snd_ctl_elem_value_free(elem->val);
67 		snd_ctl_elem_value_free(elem->mask);
68 		snd_ctl_elem_value_free(elem->old);
69 		list_del(&elem->list);
70 		free(elem);
71 	}
72 	if ((h->mode & SND_SCTL_NOFREE) == 0)
73 		err = snd_ctl_close(h->ctl);
74 	free(h);
75 	return err;
76 }
77 
78 /**
79  * \brief Install given values to control elements
80  * \param h Setup control handle
81  * \result zero if success, otherwise a negative error code
82  */
snd_sctl_install(snd_sctl_t * h)83 int snd_sctl_install(snd_sctl_t *h)
84 {
85 	struct list_head *pos;
86 	int err;
87 	unsigned int k;
88 	assert(h);
89 	list_for_each(pos, &h->elems) {
90 		snd_sctl_elem_t *elem = list_entry(pos, snd_sctl_elem_t, list);
91 		unsigned int count;
92 		snd_ctl_elem_type_t type;
93 		if (elem->lock) {
94 			err = snd_ctl_elem_lock(h->ctl, elem->id);
95 			if (err < 0) {
96 				SNDERR("Cannot lock ctl elem");
97 				return err;
98 			}
99 		}
100 		err = snd_ctl_elem_read(h->ctl, elem->old);
101 		if (err < 0) {
102 			SNDERR("Cannot read ctl elem");
103 			return err;
104 		}
105 		count = snd_ctl_elem_info_get_count(elem->info);
106 		type = snd_ctl_elem_info_get_type(elem->info);
107 		switch (type) {
108 		case SND_CTL_ELEM_TYPE_BOOLEAN:
109 			for (k = 0; k < count; ++k) {
110 				int old, val, mask;
111 				old = snd_ctl_elem_value_get_boolean(elem->old, k);
112 				mask = snd_ctl_elem_value_get_boolean(elem->mask, k);
113 				old &= ~mask;
114 				if (old) {
115 					val = snd_ctl_elem_value_get_boolean(elem->val, k);
116 					val |= old;
117 					snd_ctl_elem_value_set_boolean(elem->val, k, val);
118 				}
119 			}
120 			break;
121 		case SND_CTL_ELEM_TYPE_INTEGER:
122 			for (k = 0; k < count; ++k) {
123 				long old, val, mask;
124 				old = snd_ctl_elem_value_get_integer(elem->old, k);
125 				mask = snd_ctl_elem_value_get_integer(elem->mask, k);
126 				old &= ~mask;
127 				if (old) {
128 					val = snd_ctl_elem_value_get_integer(elem->val, k);
129 					val |= old;
130 					snd_ctl_elem_value_set_integer(elem->val, k, val);
131 				}
132 			}
133 			break;
134 		case SND_CTL_ELEM_TYPE_ENUMERATED:
135 			for (k = 0; k < count; ++k) {
136 				unsigned int old, val, mask;
137 				old = snd_ctl_elem_value_get_enumerated(elem->old, k);
138 				mask = snd_ctl_elem_value_get_enumerated(elem->mask, k);
139 				old &= ~mask;
140 				if (old) {
141 					val = snd_ctl_elem_value_get_enumerated(elem->val, k);
142 					val |= old;
143 					snd_ctl_elem_value_set_enumerated(elem->val, k, val);
144 				}
145 			}
146 			break;
147 		case SND_CTL_ELEM_TYPE_IEC958:
148 			count = sizeof(snd_aes_iec958_t);
149 			/* Fall through */
150 		case SND_CTL_ELEM_TYPE_BYTES:
151 			for (k = 0; k < count; ++k) {
152 				unsigned char old, val, mask;
153 				old = snd_ctl_elem_value_get_byte(elem->old, k);
154 				mask = snd_ctl_elem_value_get_byte(elem->mask, k);
155 				old &= ~mask;
156 				if (old) {
157 					val = snd_ctl_elem_value_get_byte(elem->val, k);
158 					val |= old;
159 					snd_ctl_elem_value_set_byte(elem->val, k, val);
160 				}
161 			}
162 			break;
163 		default:
164 			assert(0);
165 			break;
166 		}
167 		err = snd_ctl_elem_write(h->ctl, elem->val);
168 		if (err < 0) {
169 			SNDERR("Cannot write ctl elem");
170 			return err;
171 		}
172 	}
173 	return 0;
174 }
175 
176 /**
177  * \brief Remove (restore) previous values from control elements
178  * \param h Setup control handle
179  * \result zero if success, otherwise a negative error code
180  */
snd_sctl_remove(snd_sctl_t * h)181 int snd_sctl_remove(snd_sctl_t *h)
182 {
183 	struct list_head *pos;
184 	int err;
185 	assert(h);
186 	list_for_each(pos, &h->elems) {
187 		snd_sctl_elem_t *elem = list_entry(pos, snd_sctl_elem_t, list);
188 		if (elem->lock) {
189 			err = snd_ctl_elem_unlock(h->ctl, elem->id);
190 			if (err < 0) {
191 				SNDERR("Cannot unlock ctl elem");
192 				return err;
193 			}
194 		}
195 		/* Only restore the old value if it differs from the requested
196 		 * value, because if it has changed restoring the old value
197 		 * overrides the change.  Take for example, a voice modem with
198 		 * a .conf that sets preserve off-hook.  Start playback (on-hook
199 		 * to off-hook), start record (off-hook to off-hook), stop
200 		 * playback (off-hook to restore on-hook), stop record (on-hook
201 		 * to restore off-hook), Clearly you don't want to leave the
202 		 * modem "on the phone" now that there isn't any playback or
203 		 * recording active.
204 		 */
205 		if (elem->preserve && snd_ctl_elem_value_compare(elem->val, elem->old)) {
206 			err = snd_ctl_elem_write(h->ctl, elem->old);
207 			if (err < 0) {
208 				SNDERR("Cannot restore ctl elem");
209 				return err;
210 			}
211 		}
212 	}
213 	return 0;
214 }
215 
snd_config_get_ctl_elem_enumerated(snd_config_t * n,snd_ctl_t * ctl,snd_ctl_elem_info_t * info)216 static int snd_config_get_ctl_elem_enumerated(snd_config_t *n, snd_ctl_t *ctl,
217 					      snd_ctl_elem_info_t *info)
218 {
219 	const char *str;
220 	long val;
221 	unsigned int idx, items;
222 	switch (snd_config_get_type(n)) {
223 	case SND_CONFIG_TYPE_INTEGER:
224 		snd_config_get_integer(n, &val);
225 		return val;
226 	case SND_CONFIG_TYPE_STRING:
227 		snd_config_get_string(n, &str);
228 		break;
229 	default:
230 		return -1;
231 	}
232 	items = snd_ctl_elem_info_get_items(info);
233 	for (idx = 0; idx < items; idx++) {
234 		int err;
235 		snd_ctl_elem_info_set_item(info, idx);
236 		err = snd_ctl_elem_info(ctl, info);
237 		if (err < 0) {
238 			SNDERR("Cannot obtain info for CTL elem");
239 			return err;
240 		}
241 		if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0)
242 			return idx;
243 	}
244 	return -1;
245 }
246 
snd_config_get_ctl_elem_value(snd_config_t * conf,snd_ctl_t * ctl,snd_ctl_elem_value_t * val,snd_ctl_elem_value_t * mask,snd_ctl_elem_info_t * info)247 static int snd_config_get_ctl_elem_value(snd_config_t *conf,
248 					 snd_ctl_t *ctl,
249 					 snd_ctl_elem_value_t *val,
250 					 snd_ctl_elem_value_t *mask,
251 					 snd_ctl_elem_info_t *info)
252 {
253 	int err;
254 	snd_config_iterator_t i, next;
255 	snd_ctl_elem_id_t id = {0};
256 	snd_ctl_elem_type_t type;
257 	unsigned int count;
258 	long v;
259 	long idx;
260 	snd_ctl_elem_value_get_id(val, &id);
261 	count = snd_ctl_elem_info_get_count(info);
262 	type = snd_ctl_elem_info_get_type(info);
263 	if (count == 1) {
264 		switch (type) {
265 		case SND_CTL_ELEM_TYPE_BOOLEAN:
266 			v = snd_config_get_bool(conf);
267 			if (v >= 0) {
268 				snd_ctl_elem_value_set_boolean(val, 0, v);
269 				if (mask)
270 					snd_ctl_elem_value_set_boolean(mask, 0, 1);
271 				return 0;
272 			}
273 			break;
274 		case SND_CTL_ELEM_TYPE_INTEGER:
275 			err = snd_config_get_integer(conf, &v);
276 			if (err == 0) {
277 				snd_ctl_elem_value_set_integer(val, 0, v);
278 				if (mask)
279 					snd_ctl_elem_value_set_integer(mask, 0, ~0L);
280 				return 0;
281 			}
282 			break;
283 		case SND_CTL_ELEM_TYPE_ENUMERATED:
284 			v = snd_config_get_ctl_elem_enumerated(conf, ctl, info);
285 			if (v >= 0) {
286 				snd_ctl_elem_value_set_enumerated(val, 0, v);
287 				if (mask)
288 					snd_ctl_elem_value_set_enumerated(mask, 0, ~0);
289 				return 0;
290 			}
291 			break;
292 		case SND_CTL_ELEM_TYPE_BYTES:
293 		case SND_CTL_ELEM_TYPE_IEC958:
294 			break;
295 		default:
296 			SNDERR("Unknown control type: %d", type);
297 			return -EINVAL;
298 		}
299 	}
300 	switch (type) {
301 	case SND_CTL_ELEM_TYPE_IEC958:
302 		count = sizeof(snd_aes_iec958_t);
303 		/* Fall through */
304 	case SND_CTL_ELEM_TYPE_BYTES:
305 	{
306 		const char *buf;
307 		err = snd_config_get_string(conf, &buf);
308 		if (err >= 0) {
309 			int c1 = 0;
310 			unsigned int len = strlen(buf);
311 			unsigned int idx = 0;
312 			if (len % 2 != 0 || len > count * 2) {
313 			_bad_content:
314 				SNDERR("bad value content\n");
315 				return -EINVAL;
316 			}
317 			while (*buf) {
318 				int c = *buf++;
319 				if (c >= '0' && c <= '9')
320 					c -= '0';
321 				else if (c >= 'a' && c <= 'f')
322 					c = c - 'a' + 10;
323 				else if (c >= 'A' && c <= 'F')
324 					c = c - 'A' + 10;
325 				else {
326 					goto _bad_content;
327 				}
328 				if (idx % 2 == 1) {
329 					snd_ctl_elem_value_set_byte(val, idx / 2, c1 << 4 | c);
330 					if (mask)
331 						snd_ctl_elem_value_set_byte(mask, idx / 2, 0xff);
332 				} else
333 					c1 = c;
334 				idx++;
335 			}
336 			return 0;
337 		}
338 	}
339 	default:
340 		break;
341 	}
342 	if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
343 		SNDERR("bad value type");
344 		return -EINVAL;
345 	}
346 
347 	snd_config_for_each(i, next, conf) {
348 		snd_config_t *n = snd_config_iterator_entry(i);
349 		const char *id;
350 		if (snd_config_get_id(n, &id) < 0)
351 			continue;
352 		err = safe_strtol(id, &idx);
353 		if (err < 0 || idx < 0 || (unsigned int) idx >= count) {
354 			SNDERR("bad value index");
355 			return -EINVAL;
356 		}
357 		switch (type) {
358 		case SND_CTL_ELEM_TYPE_BOOLEAN:
359 			v = snd_config_get_bool(n);
360 			if (v < 0)
361 				goto _bad_content;
362 			snd_ctl_elem_value_set_boolean(val, idx, v);
363 			if (mask)
364 				snd_ctl_elem_value_set_boolean(mask, idx, 1);
365 			break;
366 		case SND_CTL_ELEM_TYPE_INTEGER:
367 			err = snd_config_get_integer(n, &v);
368 			if (err < 0)
369 				goto _bad_content;
370 			snd_ctl_elem_value_set_integer(val, idx, v);
371 			if (mask)
372 				snd_ctl_elem_value_set_integer(mask, idx, ~0L);
373 			break;
374 		case SND_CTL_ELEM_TYPE_ENUMERATED:
375 			v = snd_config_get_ctl_elem_enumerated(n, ctl, info);
376 			if (v < 0)
377 				goto _bad_content;
378 			snd_ctl_elem_value_set_enumerated(val, idx, v);
379 			if (mask)
380 				snd_ctl_elem_value_set_enumerated(mask, idx, ~0);
381 			break;
382 		case SND_CTL_ELEM_TYPE_BYTES:
383 		case SND_CTL_ELEM_TYPE_IEC958:
384 			err = snd_config_get_integer(n, &v);
385 			if (err < 0 || v < 0 || v > 255)
386 				goto _bad_content;
387 			snd_ctl_elem_value_set_byte(val, idx, v);
388 			if (mask)
389 				snd_ctl_elem_value_set_byte(mask, idx, 0xff);
390 			break;
391 		default:
392 			break;
393 		}
394 	}
395 	return 0;
396 }
397 
add_elem(snd_sctl_t * h,snd_config_t * _conf,snd_config_t * private_data,int * quit)398 static int add_elem(snd_sctl_t *h, snd_config_t *_conf, snd_config_t *private_data, int *quit)
399 {
400 	snd_config_t *conf;
401 	snd_config_iterator_t i, next;
402 	int iface = SND_CTL_ELEM_IFACE_MIXER;
403 	const char *name = NULL;
404 	long index = 0;
405 	long device = -1;
406 	long subdevice = -1;
407 	int lock = 0;
408 	int preserve = 0;
409 	int optional = 0;
410 	int skip_rest = 0;
411 	snd_config_t *value = NULL, *mask = NULL;
412 	snd_sctl_elem_t *elem = NULL;
413 	int err;
414 	err = snd_config_expand(_conf, _conf, NULL, private_data, &conf);
415 	if (err < 0)
416 		return err;
417 	snd_config_for_each(i, next, conf) {
418 		snd_config_t *n = snd_config_iterator_entry(i);
419 		const char *id;
420 		if (snd_config_get_id(n, &id) < 0)
421 			continue;
422 		if (strcmp(id, "comment") == 0)
423 			continue;
424 		if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
425 			const char *ptr;
426 			if ((err = snd_config_get_string(n, &ptr)) < 0) {
427 				SNDERR("field %s is not a string", id);
428 				goto _err;
429 			}
430 			if ((err = snd_config_get_ctl_iface_ascii(ptr)) < 0) {
431 				SNDERR("Invalid value for '%s'", id);
432 				goto _err;
433 			}
434 			iface = err;
435 			continue;
436 		}
437 		if (strcmp(id, "name") == 0) {
438 			if ((err = snd_config_get_string(n, &name)) < 0) {
439 				SNDERR("field %s is not a string", id);
440 				goto _err;
441 			}
442 			continue;
443 		}
444 		if (strcmp(id, "index") == 0) {
445 			if ((err = snd_config_get_integer(n, &index)) < 0) {
446 				SNDERR("field %s is not an integer", id);
447 				goto _err;
448 			}
449 			continue;
450 		}
451 		if (strcmp(id, "device") == 0) {
452 			if ((err = snd_config_get_integer(n, &device)) < 0) {
453 				SNDERR("field %s is not an integer", id);
454 				goto _err;
455 			}
456 			continue;
457 		}
458 		if (strcmp(id, "subdevice") == 0) {
459 			if ((err = snd_config_get_integer(n, &subdevice)) < 0) {
460 				SNDERR("field %s is not an integer", id);
461 				goto _err;
462 			}
463 			continue;
464 		}
465 		if (strcmp(id, "lock") == 0) {
466 			err = snd_config_get_bool(n);
467 			if (err < 0)
468 				goto _err;
469 			lock = err;
470 			continue;
471 		}
472 		if (strcmp(id, "preserve") == 0) {
473 			err = snd_config_get_bool(n);
474 			if (err < 0)
475 				goto _err;
476 			preserve = err;
477 			continue;
478 		}
479 		if (strcmp(id, "value") == 0) {
480 			value = n;
481 			continue;
482 		}
483 		if (strcmp(id, "mask") == 0) {
484 			mask = n;
485 			continue;
486 		}
487 		if (strcmp(id, "optional") == 0) {
488 			err = snd_config_get_bool(n);
489 			if (err < 0)
490 				goto _err;
491 			optional = err;
492 			continue;
493 		}
494 		if (strcmp(id, "skip_rest") == 0) {
495 			err = snd_config_get_bool(n);
496 			if (err < 0)
497 				goto _err;
498 			skip_rest = err;
499 			continue;
500 		}
501 		SNDERR("Unknown field %s", id);
502 		return -EINVAL;
503 	}
504 	if (name == NULL) {
505 		SNDERR("Missing control name");
506 		err = -EINVAL;
507 		goto _err;
508 	}
509 	if (value == NULL) {
510 		SNDERR("Missing control value");
511 		err = -EINVAL;
512 		goto _err;
513 	}
514 	if (device < 0)
515 		device = 0;
516 	if (subdevice < 0)
517 		subdevice = 0;
518 	elem = calloc(1, sizeof(*elem));
519 	if (!elem)
520 		return -ENOMEM;
521 	err = snd_ctl_elem_id_malloc(&elem->id);
522 	if (err < 0)
523 		goto _err;
524 	err = snd_ctl_elem_info_malloc(&elem->info);
525 	if (err < 0)
526 		goto _err;
527 	err = snd_ctl_elem_value_malloc(&elem->val);
528 	if (err < 0)
529 		goto _err;
530 	err = snd_ctl_elem_value_malloc(&elem->mask);
531 	if (err < 0)
532 		goto _err;
533 	err = snd_ctl_elem_value_malloc(&elem->old);
534 	if (err < 0)
535 		goto _err;
536 	elem->lock = lock;
537 	elem->preserve = preserve;
538 	snd_ctl_elem_id_set_interface(elem->id, iface);
539 	snd_ctl_elem_id_set_name(elem->id, name);
540 	snd_ctl_elem_id_set_index(elem->id, index);
541 	snd_ctl_elem_id_set_device(elem->id, device);
542 	snd_ctl_elem_id_set_subdevice(elem->id, subdevice);
543 	snd_ctl_elem_info_set_id(elem->info, elem->id);
544 	err = snd_ctl_elem_info(h->ctl, elem->info);
545 	if (err < 0) {
546 		if (! optional)
547 			SNDERR("Cannot obtain info for CTL elem (%s,'%s',%li,%li,%li): %s", snd_ctl_elem_iface_name(iface), name, index, device, subdevice, snd_strerror(err));
548 		goto _err;
549 	} else {
550 		if (skip_rest)
551 			*quit = 1;
552 	}
553 	snd_ctl_elem_value_set_id(elem->val, elem->id);
554 	snd_ctl_elem_value_set_id(elem->old, elem->id);
555 	if (mask) {
556 		err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, NULL, elem->info);
557 		if (err < 0)
558 			goto _err;
559 		err = snd_config_get_ctl_elem_value(mask, h->ctl, elem->mask, NULL, elem->info);
560 		if (err < 0)
561 			goto _err;
562 	} else {
563 		err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info);
564 		if (err < 0)
565 			goto _err;
566 	}
567 
568 	err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info);
569 	if (err < 0)
570 		goto _err;
571 	list_add_tail(&elem->list, &h->elems);
572 
573  _err:
574  	if (err < 0 && elem) {
575 		if (elem->id)
576 			snd_ctl_elem_id_free(elem->id);
577 		if (elem->info)
578 			snd_ctl_elem_info_free(elem->info);
579 		if (elem->val)
580 			snd_ctl_elem_value_free(elem->val);
581 		if (elem->mask)
582 			snd_ctl_elem_value_free(elem->mask);
583 		if (elem->old)
584 			snd_ctl_elem_value_free(elem->old);
585 		free(elem);
586 		if (err != -ENOMEM && optional)
587 			err = 0; /* ignore the error */
588 	}
589 	if (conf)
590 		snd_config_delete(conf);
591 	return err;
592 }
593 
594 /**
595  * \brief Build setup control handle
596  * \param sctl Result - setup control handle
597  * \param handle Master control handle
598  * \param conf Setup configuration
599  * \param private_data Private data for runtime evaluation
600  * \param mode Build mode - SND_SCTL_xxxx
601  * \result zero if success, otherwise a negative error code
602  */
snd_sctl_build(snd_sctl_t ** sctl,snd_ctl_t * handle,snd_config_t * conf,snd_config_t * private_data,int mode)603 int snd_sctl_build(snd_sctl_t **sctl, snd_ctl_t *handle, snd_config_t *conf, snd_config_t *private_data, int mode)
604 {
605 	snd_sctl_t *h;
606 	snd_config_iterator_t i, next;
607 	int err, quit = 0;
608 
609 	assert(sctl);
610 	assert(handle);
611 	assert(conf);
612 	*sctl = NULL;
613 	if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND)
614 		return -EINVAL;
615 	h = calloc(1, sizeof(*h));
616 	if (!h) {
617 		if (mode & SND_SCTL_NOFREE)
618 			return -ENOMEM;
619 		snd_ctl_close(handle);
620 		return -ENOMEM;
621 	}
622 	h->mode = mode;
623 	h->ctl = handle;
624 	INIT_LIST_HEAD(&h->elems);
625 	snd_config_for_each(i, next, conf) {
626 		snd_config_t *n = snd_config_iterator_entry(i);
627 		err = add_elem(h, n, private_data, &quit);
628 		if (err < 0) {
629 			free_elems(h);
630 			return err;
631 		}
632 		if (quit)
633 			break;
634 	}
635 	*sctl = h;
636 	return 0;
637 }
638 
639 /**
640  * \brief Free setup control handle
641  * \param sctl Setup control handle
642  * \result zero if success, otherwise a negative error code
643  */
snd_sctl_free(snd_sctl_t * sctl)644 int snd_sctl_free(snd_sctl_t *sctl)
645 {
646 	assert(sctl);
647 	return free_elems(sctl);
648 }
649