1 /**
2  * \file mixer/mixer.c
3  * \brief Mixer Interface
4  * \author Jaroslav Kysela <perex@perex.cz>
5  * \author Abramo Bagnara <abramo@alsa-project.org>
6  * \date 2001
7  *
8  * Mixer interface is designed to access mixer elements.
9  * Callbacks may be used for event handling.
10  */
11 /*
12  *  Mixer Interface - main file
13  *  Copyright (c) 1998/1999/2000 by Jaroslav Kysela <perex@perex.cz>
14  *  Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
15  *
16  *
17  *   This library is free software; you can redistribute it and/or modify
18  *   it under the terms of the GNU Lesser General Public License as
19  *   published by the Free Software Foundation; either version 2.1 of
20  *   the License, or (at your option) any later version.
21  *
22  *   This program is distributed in the hope that it will be useful,
23  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
24  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  *   GNU Lesser General Public License for more details.
26  *
27  *   You should have received a copy of the GNU Lesser General Public
28  *   License along with this library; if not, write to the Free Software
29  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
30  *
31  */
32 
33 /*! \page mixer Mixer interface
34 
35 <P>Mixer interface is designed to access the abstracted mixer controls.
36 This is an abstraction layer over the hcontrol layer.
37 
38 \section mixer_general_overview General overview
39 
40 */
41 
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <fcntl.h>
47 #include <sys/ioctl.h>
48 #include "mixer_local.h"
49 
50 #ifndef DOC_HIDDEN
51 typedef struct _snd_mixer_slave {
52 	snd_hctl_t *hctl;
53 	struct list_head list;
54 } snd_mixer_slave_t;
55 
56 #endif
57 
58 static int snd_mixer_compare_default(const snd_mixer_elem_t *c1,
59 				     const snd_mixer_elem_t *c2);
60 
61 
62 /**
63  * \brief Opens an empty mixer
64  * \param mixerp Returned mixer handle
65  * \param mode Open mode
66  * \return 0 on success otherwise a negative error code
67  */
snd_mixer_open(snd_mixer_t ** mixerp,int mode ATTRIBUTE_UNUSED)68 int snd_mixer_open(snd_mixer_t **mixerp, int mode ATTRIBUTE_UNUSED)
69 {
70 	snd_mixer_t *mixer;
71 	assert(mixerp);
72 	mixer = calloc(1, sizeof(*mixer));
73 	if (mixer == NULL)
74 		return -ENOMEM;
75 	INIT_LIST_HEAD(&mixer->slaves);
76 	INIT_LIST_HEAD(&mixer->classes);
77 	INIT_LIST_HEAD(&mixer->elems);
78 	mixer->compare = snd_mixer_compare_default;
79 	*mixerp = mixer;
80 	return 0;
81 }
82 
83 /**
84  * \brief Attach an HCTL element to a mixer element
85  * \param melem Mixer element
86  * \param helem HCTL element
87  * \return 0 on success otherwise a negative error code
88  *
89  * For use by mixer element class specific code.
90  */
snd_mixer_elem_attach(snd_mixer_elem_t * melem,snd_hctl_elem_t * helem)91 int snd_mixer_elem_attach(snd_mixer_elem_t *melem,
92 			  snd_hctl_elem_t *helem)
93 {
94 	bag_t *bag = snd_hctl_elem_get_callback_private(helem);
95 	int err;
96 	err = bag_add(bag, melem);
97 	if (err < 0)
98 		return err;
99 	return bag_add(&melem->helems, helem);
100 }
101 
102 /**
103  * \brief Detach an HCTL element from a mixer element
104  * \param melem Mixer element
105  * \param helem HCTL element
106  * \return 0 on success otherwise a negative error code
107  *
108  * For use by mixer element class specific code.
109  */
snd_mixer_elem_detach(snd_mixer_elem_t * melem,snd_hctl_elem_t * helem)110 int snd_mixer_elem_detach(snd_mixer_elem_t *melem,
111 			  snd_hctl_elem_t *helem)
112 {
113 	bag_t *bag = snd_hctl_elem_get_callback_private(helem);
114 	int err;
115 	err = bag_del(bag, melem);
116 	assert(err >= 0);
117 	err = bag_del(&melem->helems, helem);
118 	assert(err >= 0);
119 	return 0;
120 }
121 
122 /**
123  * \brief Return true if a mixer element does not contain any HCTL elements
124  * \param melem Mixer element
125  * \return 0 if not empty, 1 if empty
126  *
127  * For use by mixer element class specific code.
128  */
snd_mixer_elem_empty(snd_mixer_elem_t * melem)129 int snd_mixer_elem_empty(snd_mixer_elem_t *melem)
130 {
131 	return bag_empty(&melem->helems);
132 }
133 
hctl_elem_event_handler(snd_hctl_elem_t * helem,unsigned int mask)134 static int hctl_elem_event_handler(snd_hctl_elem_t *helem,
135 				   unsigned int mask)
136 {
137 	bag_t *bag = snd_hctl_elem_get_callback_private(helem);
138 	if (mask == SND_CTL_EVENT_MASK_REMOVE) {
139 		int res = 0;
140 		int err;
141 		bag_iterator_t i, n;
142 		bag_for_each_safe(i, n, bag) {
143 			snd_mixer_elem_t *melem = bag_iterator_entry(i);
144 			snd_mixer_class_t *class = melem->class;
145 			err = class->event(class, mask, helem, melem);
146 			if (err < 0)
147 				res = err;
148 		}
149 		assert(bag_empty(bag));
150 		bag_free(bag);
151 		return res;
152 	}
153 	if (mask & (SND_CTL_EVENT_MASK_VALUE | SND_CTL_EVENT_MASK_INFO)) {
154 		int err = 0;
155 		bag_iterator_t i, n;
156 		bag_for_each_safe(i, n, bag) {
157 			snd_mixer_elem_t *melem = bag_iterator_entry(i);
158 			snd_mixer_class_t *class = melem->class;
159 			err = class->event(class, mask, helem, melem);
160 			if (err < 0)
161 				return err;
162 		}
163 	}
164 	return 0;
165 }
166 
hctl_event_handler(snd_hctl_t * hctl,unsigned int mask,snd_hctl_elem_t * elem)167 static int hctl_event_handler(snd_hctl_t *hctl, unsigned int mask,
168 			      snd_hctl_elem_t *elem)
169 {
170 	snd_mixer_t *mixer = snd_hctl_get_callback_private(hctl);
171 	int res = 0;
172 	if (mask & SND_CTL_EVENT_MASK_ADD) {
173 		struct list_head *pos;
174 		bag_t *bag;
175 		int err = bag_new(&bag);
176 		if (err < 0)
177 			return err;
178 		snd_hctl_elem_set_callback(elem, hctl_elem_event_handler);
179 		snd_hctl_elem_set_callback_private(elem, bag);
180 		list_for_each(pos, &mixer->classes) {
181 			snd_mixer_class_t *c;
182 			c = list_entry(pos, snd_mixer_class_t, list);
183 			err = c->event(c, mask, elem, NULL);
184 			if (err < 0)
185 				res = err;
186 		}
187 	}
188 	return res;
189 }
190 
191 
192 /**
193  * \brief Attach an HCTL specified with the CTL device name to an opened mixer
194  * \param mixer Mixer handle
195  * \param name HCTL name (see #snd_hctl_open)
196  * \return 0 on success otherwise a negative error code
197  */
snd_mixer_attach(snd_mixer_t * mixer,const char * name)198 int snd_mixer_attach(snd_mixer_t *mixer, const char *name)
199 {
200 	snd_hctl_t *hctl;
201 	int err;
202 
203 	err = snd_hctl_open(&hctl, name, 0);
204 	if (err < 0)
205 		return err;
206 	err = snd_mixer_attach_hctl(mixer, hctl);
207 	if (err < 0)
208 		return err;
209 	return 0;
210 }
211 
212 /**
213  * \brief Attach an HCTL to an opened mixer
214  * \param mixer Mixer handle
215  * \param hctl the HCTL to be attached
216  * \return 0 on success otherwise a negative error code
217  *
218  * Upon error, this function closes the given hctl handle automatically.
219  */
snd_mixer_attach_hctl(snd_mixer_t * mixer,snd_hctl_t * hctl)220 int snd_mixer_attach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl)
221 {
222 	snd_mixer_slave_t *slave;
223 	int err;
224 
225 	assert(hctl);
226 	slave = calloc(1, sizeof(*slave));
227 	if (slave == NULL) {
228 		snd_hctl_close(hctl);
229 		return -ENOMEM;
230 	}
231 	err = snd_hctl_nonblock(hctl, 1);
232 	if (err < 0) {
233 		snd_hctl_close(hctl);
234 		free(slave);
235 		return err;
236 	}
237 	snd_hctl_set_callback(hctl, hctl_event_handler);
238 	snd_hctl_set_callback_private(hctl, mixer);
239 	slave->hctl = hctl;
240 	list_add_tail(&slave->list, &mixer->slaves);
241 	return 0;
242 }
243 
244 /**
245  * \brief Detach a previously attached HCTL to an opened mixer freeing all related resources
246  * \param mixer Mixer handle
247  * \param name HCTL previously attached
248  * \return 0 on success otherwise a negative error code
249  */
snd_mixer_detach(snd_mixer_t * mixer,const char * name)250 int snd_mixer_detach(snd_mixer_t *mixer, const char *name)
251 {
252 	struct list_head *pos;
253 	list_for_each(pos, &mixer->slaves) {
254 		snd_mixer_slave_t *s;
255 		s = list_entry(pos, snd_mixer_slave_t, list);
256 		if (strcmp(name, snd_hctl_name(s->hctl)) == 0) {
257 			snd_hctl_close(s->hctl);
258 			list_del(pos);
259 			free(s);
260 			return 0;
261 		}
262 	}
263 	return -ENOENT;
264 }
265 
266 /**
267  * \brief Detach a previously attached HCTL to an opened mixer freeing all related resources
268  * \param mixer Mixer handle
269  * \param hctl HCTL previously attached
270  * \return 0 on success otherwise a negative error code
271  *
272  * Note: The hctl handle is not closed!
273  */
snd_mixer_detach_hctl(snd_mixer_t * mixer,snd_hctl_t * hctl)274 int snd_mixer_detach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl)
275 {
276 	struct list_head *pos;
277 	list_for_each(pos, &mixer->slaves) {
278 		snd_mixer_slave_t *s;
279 		s = list_entry(pos, snd_mixer_slave_t, list);
280 		if (hctl == s->hctl) {
281 			list_del(pos);
282 			free(s);
283 			return 0;
284 		}
285 	}
286 	return -ENOENT;
287 }
288 
289 /**
290  * \brief Obtain a HCTL pointer associated to given name
291  * \param mixer Mixer handle
292  * \param name HCTL previously attached
293  * \param hctl HCTL pointer
294  * \return 0 on success otherwise a negative error code
295  */
snd_mixer_get_hctl(snd_mixer_t * mixer,const char * name,snd_hctl_t ** hctl)296 int snd_mixer_get_hctl(snd_mixer_t *mixer, const char *name, snd_hctl_t **hctl)
297 {
298 	struct list_head *pos;
299 	list_for_each(pos, &mixer->slaves) {
300 		snd_mixer_slave_t *s;
301 		s = list_entry(pos, snd_mixer_slave_t, list);
302 		if (strcmp(name, snd_hctl_name(s->hctl)) == 0) {
303 			*hctl = s->hctl;
304 			return 0;
305 		}
306 	}
307 	return -ENOENT;
308 }
309 
snd_mixer_throw_event(snd_mixer_t * mixer,unsigned int mask,snd_mixer_elem_t * elem)310 static int snd_mixer_throw_event(snd_mixer_t *mixer, unsigned int mask,
311 			  snd_mixer_elem_t *elem)
312 {
313 	mixer->events++;
314 	if (mixer->callback)
315 		return mixer->callback(mixer, mask, elem);
316 	return 0;
317 }
318 
snd_mixer_elem_throw_event(snd_mixer_elem_t * elem,unsigned int mask)319 static int snd_mixer_elem_throw_event(snd_mixer_elem_t *elem, unsigned int mask)
320 {
321 	elem->class->mixer->events++;
322 	if (elem->callback)
323 		return elem->callback(elem, mask);
324 	return 0;
325 }
326 
_snd_mixer_find_elem(snd_mixer_t * mixer,snd_mixer_elem_t * elem,int * dir)327 static int _snd_mixer_find_elem(snd_mixer_t *mixer, snd_mixer_elem_t *elem, int *dir)
328 {
329 	unsigned int l, u;
330 	int c = 0;
331 	int idx = -1;
332 	assert(mixer && elem);
333 	assert(mixer->compare);
334 	l = 0;
335 	u = mixer->count;
336 	while (l < u) {
337 		idx = (l + u) / 2;
338 		c = mixer->compare(elem, mixer->pelems[idx]);
339 		if (c < 0)
340 			u = idx;
341 		else if (c > 0)
342 			l = idx + 1;
343 		else
344 			break;
345 	}
346 	*dir = c;
347 	return idx;
348 }
349 
350 /**
351  * \brief Get private data associated to give mixer element
352  * \param elem Mixer element
353  * \return private data
354  *
355  * For use by mixer element class specific code.
356  */
snd_mixer_elem_get_private(const snd_mixer_elem_t * elem)357 void *snd_mixer_elem_get_private(const snd_mixer_elem_t *elem)
358 {
359 	return elem->private_data;
360 }
361 
362 /**
363  * \brief Allocate a new mixer element
364  * \param elem Returned mixer element
365  * \param type Mixer element type
366  * \param compare_weight Mixer element compare weight
367  * \param private_data Private data
368  * \param private_free Private data free callback
369  * \return 0 on success otherwise a negative error code
370  *
371  * For use by mixer element class specific code.
372  */
snd_mixer_elem_new(snd_mixer_elem_t ** elem,snd_mixer_elem_type_t type,int compare_weight,void * private_data,void (* private_free)(snd_mixer_elem_t * elem))373 int snd_mixer_elem_new(snd_mixer_elem_t **elem,
374 		       snd_mixer_elem_type_t type,
375 		       int compare_weight,
376 		       void *private_data,
377 		       void (*private_free)(snd_mixer_elem_t *elem))
378 {
379 	snd_mixer_elem_t *melem = calloc(1, sizeof(*melem));
380 	if (melem == NULL)
381 		return -ENOMEM;
382 	melem->type = type;
383 	melem->compare_weight = compare_weight;
384 	melem->private_data = private_data;
385 	melem->private_free = private_free;
386 	INIT_LIST_HEAD(&melem->helems);
387 	*elem = melem;
388 	return 0;
389 }
390 
391 /**
392  * \brief Add an element for a registered mixer element class
393  * \param elem Mixer element
394  * \param class Mixer element class
395  * \return 0 on success otherwise a negative error code
396  *
397  * For use by mixer element class specific code.
398  */
snd_mixer_elem_add(snd_mixer_elem_t * elem,snd_mixer_class_t * class)399 int snd_mixer_elem_add(snd_mixer_elem_t *elem, snd_mixer_class_t *class)
400 {
401 	int dir, idx;
402 	snd_mixer_t *mixer = class->mixer;
403 	elem->class = class;
404 
405 	if (mixer->count == mixer->alloc) {
406 		snd_mixer_elem_t **m;
407 		mixer->alloc += 32;
408 		m = realloc(mixer->pelems, sizeof(*m) * mixer->alloc);
409 		if (!m) {
410 			mixer->alloc -= 32;
411 			return -ENOMEM;
412 		}
413 		mixer->pelems = m;
414 	}
415 	if (mixer->count == 0) {
416 		list_add_tail(&elem->list, &mixer->elems);
417 		mixer->pelems[0] = elem;
418 	} else {
419 		idx = _snd_mixer_find_elem(mixer, elem, &dir);
420 		assert(dir != 0);
421 		if (dir > 0) {
422 			list_add(&elem->list, &mixer->pelems[idx]->list);
423 			idx++;
424 		} else {
425 			list_add_tail(&elem->list, &mixer->pelems[idx]->list);
426 		}
427 		memmove(mixer->pelems + idx + 1,
428 			mixer->pelems + idx,
429 			(mixer->count - idx) * sizeof(snd_mixer_elem_t *));
430 		mixer->pelems[idx] = elem;
431 	}
432 	mixer->count++;
433 	return snd_mixer_throw_event(mixer, SND_CTL_EVENT_MASK_ADD, elem);
434 }
435 
436 /**
437  * \brief Remove a mixer element
438  * \param elem Mixer element
439  * \return 0 on success otherwise a negative error code
440  *
441  * For use by mixer element class specific code.
442  */
snd_mixer_elem_remove(snd_mixer_elem_t * elem)443 int snd_mixer_elem_remove(snd_mixer_elem_t *elem)
444 {
445 	snd_mixer_t *mixer = elem->class->mixer;
446 	bag_iterator_t i, n;
447 	int err, idx, dir;
448 	unsigned int m;
449 	assert(elem);
450 	assert(mixer->count);
451 	idx = _snd_mixer_find_elem(mixer, elem, &dir);
452 	if (dir != 0)
453 		return -EINVAL;
454 	bag_for_each_safe(i, n, &elem->helems) {
455 		snd_hctl_elem_t *helem = bag_iterator_entry(i);
456 		snd_mixer_elem_detach(elem, helem);
457 	}
458 	err = snd_mixer_elem_throw_event(elem, SND_CTL_EVENT_MASK_REMOVE);
459 	list_del(&elem->list);
460 	snd_mixer_elem_free(elem);
461 	mixer->count--;
462 	m = mixer->count - idx;
463 	if (m > 0)
464 		memmove(mixer->pelems + idx,
465 			mixer->pelems + idx + 1,
466 			m * sizeof(snd_mixer_elem_t *));
467 	return err;
468 }
469 
470 /**
471  * \brief Free a mixer element
472  * \param elem Mixer element
473  * \return 0 on success otherwise a negative error code
474  *
475  * For use by mixer element class specific code.
476  */
snd_mixer_elem_free(snd_mixer_elem_t * elem)477 void snd_mixer_elem_free(snd_mixer_elem_t *elem)
478 {
479 	if (elem->private_free)
480 		elem->private_free(elem);
481 	free(elem);
482 }
483 
484 /**
485  * \brief Mixer element informations are changed
486  * \param elem Mixer element
487  * \return 0 on success otherwise a negative error code
488  *
489  * For use by mixer element class specific code.
490  */
snd_mixer_elem_info(snd_mixer_elem_t * elem)491 int snd_mixer_elem_info(snd_mixer_elem_t *elem)
492 {
493 	return snd_mixer_elem_throw_event(elem, SND_CTL_EVENT_MASK_INFO);
494 }
495 
496 /**
497  * \brief Mixer element values is changed
498  * \param elem Mixer element
499  * \return 0 on success otherwise a negative error code
500  *
501  * For use by mixer element class specific code.
502  */
snd_mixer_elem_value(snd_mixer_elem_t * elem)503 int snd_mixer_elem_value(snd_mixer_elem_t *elem)
504 {
505 	return snd_mixer_elem_throw_event(elem, SND_CTL_EVENT_MASK_VALUE);
506 }
507 
508 /**
509  * \brief Register mixer element class
510  * \param class Mixer element class
511  * \param mixer Mixer handle
512  * \return 0 on success otherwise a negative error code
513  *
514  * For use by mixer element class specific code.
515  */
snd_mixer_class_register(snd_mixer_class_t * class,snd_mixer_t * mixer)516 int snd_mixer_class_register(snd_mixer_class_t *class, snd_mixer_t *mixer)
517 {
518 	struct list_head *pos;
519 	class->mixer = mixer;
520 	list_add_tail(&class->list, &mixer->classes);
521 	if (!class->event)
522 		return 0;
523 	list_for_each(pos, &mixer->slaves) {
524 		int err;
525 		snd_mixer_slave_t *slave;
526 		snd_hctl_elem_t *elem;
527 		slave = list_entry(pos, snd_mixer_slave_t, list);
528 		elem = snd_hctl_first_elem(slave->hctl);
529 		while (elem) {
530 			err = class->event(class, SND_CTL_EVENT_MASK_ADD, elem, NULL);
531 			if (err < 0)
532 				return err;
533 			elem = snd_hctl_elem_next(elem);
534 		}
535 	}
536 	return 0;
537 }
538 
539 /**
540  * \brief Unregister mixer element class and remove all its elements
541  * \param class Mixer element class
542  * \return 0 on success otherwise a negative error code
543  *
544  * Note that the class structure is also deallocated!
545  */
snd_mixer_class_unregister(snd_mixer_class_t * class)546 int snd_mixer_class_unregister(snd_mixer_class_t *class)
547 {
548 	unsigned int k;
549 	snd_mixer_elem_t *e;
550 	snd_mixer_t *mixer = class->mixer;
551 	for (k = mixer->count; k > 0; k--) {
552 		e = mixer->pelems[k-1];
553 		if (e->class == class)
554 			snd_mixer_elem_remove(e);
555 	}
556 	if (class->private_free)
557 		class->private_free(class);
558 	list_del(&class->list);
559 	free(class);
560 	return 0;
561 }
562 
563 /**
564  * \brief Load a mixer elements
565  * \param mixer Mixer handle
566  * \return 0 on success otherwise a negative error code
567  */
snd_mixer_load(snd_mixer_t * mixer)568 int snd_mixer_load(snd_mixer_t *mixer)
569 {
570 	struct list_head *pos;
571 	list_for_each(pos, &mixer->slaves) {
572 		int err;
573 		snd_mixer_slave_t *s;
574 		s = list_entry(pos, snd_mixer_slave_t, list);
575 		err = snd_hctl_load(s->hctl);
576 		if (err < 0)
577 			return err;
578 	}
579 	return 0;
580 }
581 
582 /**
583  * \brief Unload all mixer elements and free all related resources
584  * \param mixer Mixer handle
585  */
snd_mixer_free(snd_mixer_t * mixer)586 void snd_mixer_free(snd_mixer_t *mixer)
587 {
588 	struct list_head *pos;
589 	list_for_each(pos, &mixer->slaves) {
590 		snd_mixer_slave_t *s;
591 		s = list_entry(pos, snd_mixer_slave_t, list);
592 		snd_hctl_free(s->hctl);
593 	}
594 }
595 
596 /**
597  * \brief Close a mixer and free all related resources
598  * \param mixer Mixer handle
599  * \return 0 on success otherwise a negative error code
600  */
snd_mixer_close(snd_mixer_t * mixer)601 int snd_mixer_close(snd_mixer_t *mixer)
602 {
603 	int res = 0;
604 	assert(mixer);
605 	while (!list_empty(&mixer->classes)) {
606 		snd_mixer_class_t *c;
607 		c = list_entry(mixer->classes.next, snd_mixer_class_t, list);
608 		snd_mixer_class_unregister(c);
609 	}
610 	assert(list_empty(&mixer->elems));
611 	assert(mixer->count == 0);
612 	free(mixer->pelems);
613 	mixer->pelems = NULL;
614 	while (!list_empty(&mixer->slaves)) {
615 		int err;
616 		snd_mixer_slave_t *s;
617 		s = list_entry(mixer->slaves.next, snd_mixer_slave_t, list);
618 		err = snd_hctl_close(s->hctl);
619 		if (err < 0)
620 			res = err;
621 		list_del(&s->list);
622 		free(s);
623 	}
624 	free(mixer);
625 	return res;
626 }
627 
snd_mixer_compare_default(const snd_mixer_elem_t * c1,const snd_mixer_elem_t * c2)628 static int snd_mixer_compare_default(const snd_mixer_elem_t *c1,
629 				     const snd_mixer_elem_t *c2)
630 {
631 	int d = c1->compare_weight - c2->compare_weight;
632 	if (d)
633 		return d;
634 	assert(c1->class && c1->class->compare);
635 	assert(c2->class && c2->class->compare);
636 	assert(c1->class == c2->class);
637 	return c1->class->compare(c1, c2);
638 }
639 
mixer_compare(const void * a,const void * b)640 static int mixer_compare(const void *a, const void *b)
641 {
642 	snd_mixer_t *mixer;
643 
644 	mixer = (*((const snd_mixer_elem_t * const *)a))->class->mixer;
645 	return mixer->compare(*(const snd_mixer_elem_t * const *)a, *(const snd_mixer_elem_t * const *)b);
646 }
647 
snd_mixer_sort(snd_mixer_t * mixer)648 static int snd_mixer_sort(snd_mixer_t *mixer)
649 {
650 	unsigned int k;
651 	assert(mixer);
652 	assert(mixer->compare);
653 	INIT_LIST_HEAD(&mixer->elems);
654 	qsort(mixer->pelems, mixer->count, sizeof(snd_mixer_elem_t *), mixer_compare);
655 	for (k = 0; k < mixer->count; k++)
656 		list_add_tail(&mixer->pelems[k]->list, &mixer->elems);
657 	return 0;
658 }
659 
660 /**
661  * \brief Change mixer compare function and reorder elements
662  * \param mixer Mixer handle
663  * \param compare Element compare function
664  * \return 0 on success otherwise a negative error code
665  */
snd_mixer_set_compare(snd_mixer_t * mixer,snd_mixer_compare_t compare)666 int snd_mixer_set_compare(snd_mixer_t *mixer, snd_mixer_compare_t compare)
667 {
668 	snd_mixer_compare_t compare_old;
669 	int err;
670 
671 	assert(mixer);
672 	compare_old = mixer->compare;
673 	mixer->compare = compare == NULL ? snd_mixer_compare_default : compare;
674 	if ((err = snd_mixer_sort(mixer)) < 0) {
675 		mixer->compare = compare_old;
676 		return err;
677 	}
678 	return 0;
679 }
680 
681 /**
682  * \brief get count of poll descriptors for mixer handle
683  * \param mixer Mixer handle
684  * \return count of poll descriptors
685  */
snd_mixer_poll_descriptors_count(snd_mixer_t * mixer)686 int snd_mixer_poll_descriptors_count(snd_mixer_t *mixer)
687 {
688 	struct list_head *pos;
689 	unsigned int c = 0;
690 	assert(mixer);
691 	list_for_each(pos, &mixer->slaves) {
692 		snd_mixer_slave_t *s;
693 		int n;
694 		s = list_entry(pos, snd_mixer_slave_t, list);
695 		n = snd_hctl_poll_descriptors_count(s->hctl);
696 		if (n < 0)
697 			return n;
698 		c += n;
699 	}
700 	return c;
701 }
702 
703 /**
704  * \brief get poll descriptors
705  * \param mixer Mixer handle
706  * \param pfds array of poll descriptors
707  * \param space space in the poll descriptor array
708  * \return count of filled descriptors
709  */
snd_mixer_poll_descriptors(snd_mixer_t * mixer,struct pollfd * pfds,unsigned int space)710 int snd_mixer_poll_descriptors(snd_mixer_t *mixer, struct pollfd *pfds, unsigned int space)
711 {
712 	struct list_head *pos;
713 	unsigned int count = 0;
714 	assert(mixer);
715 	list_for_each(pos, &mixer->slaves) {
716 		snd_mixer_slave_t *s;
717 		int n;
718 		s = list_entry(pos, snd_mixer_slave_t, list);
719 		n = snd_hctl_poll_descriptors(s->hctl, pfds, space);
720 		if (n < 0)
721 			return n;
722 		if (space >= (unsigned int) n) {
723 			count += n;
724 			space -= n;
725 			pfds += n;
726 		} else
727 			space = 0;
728 	}
729 	return count;
730 }
731 
732 /**
733  * \brief get returned events from poll descriptors
734  * \param mixer Mixer handle
735  * \param pfds array of poll descriptors
736  * \param nfds count of poll descriptors
737  * \param revents returned events
738  * \return zero if success, otherwise a negative error code
739  */
snd_mixer_poll_descriptors_revents(snd_mixer_t * mixer,struct pollfd * pfds,unsigned int nfds,unsigned short * revents)740 int snd_mixer_poll_descriptors_revents(snd_mixer_t *mixer, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
741 {
742 	unsigned int idx;
743 	unsigned short res;
744         assert(mixer && pfds && revents);
745 	if (nfds == 0)
746 		return -EINVAL;
747 	res = 0;
748 	for (idx = 0; idx < nfds; idx++, pfds++)
749 		res |= pfds->revents & (POLLIN|POLLERR|POLLNVAL);
750 	*revents = res;
751 	return 0;
752 }
753 
754 /**
755  * \brief Wait for a mixer to become ready (i.e. at least one event pending)
756  * \param mixer Mixer handle
757  * \param timeout maximum time in milliseconds to wait
758  * \return 0 otherwise a negative error code on failure
759  */
snd_mixer_wait(snd_mixer_t * mixer,int timeout)760 int snd_mixer_wait(snd_mixer_t *mixer, int timeout)
761 {
762 	struct pollfd spfds[16];
763 	struct pollfd *pfds = spfds;
764 	int err;
765 	int count;
766 	count = snd_mixer_poll_descriptors(mixer, pfds, sizeof(spfds) / sizeof(spfds[0]));
767 	if (count < 0)
768 		return count;
769 	if ((unsigned int) count > sizeof(spfds) / sizeof(spfds[0])) {
770 		pfds = alloca(count * sizeof(*pfds));
771 		if (!pfds)
772 			return -ENOMEM;
773 		err = snd_mixer_poll_descriptors(mixer, pfds,
774 						 (unsigned int) count);
775 		assert(err == count);
776 	}
777 	err = poll(pfds, (unsigned int) count, timeout);
778 	if (err < 0)
779 		return -errno;
780 	return 0;
781 }
782 
783 /**
784  * \brief get first element for a mixer
785  * \param mixer Mixer handle
786  * \return pointer to first element
787  */
snd_mixer_first_elem(snd_mixer_t * mixer)788 snd_mixer_elem_t *snd_mixer_first_elem(snd_mixer_t *mixer)
789 {
790 	assert(mixer);
791 	if (list_empty(&mixer->elems))
792 		return NULL;
793 	return list_entry(mixer->elems.next, snd_mixer_elem_t, list);
794 }
795 
796 /**
797  * \brief get last element for a mixer
798  * \param mixer Mixer handle
799  * \return pointer to last element
800  */
snd_mixer_last_elem(snd_mixer_t * mixer)801 snd_mixer_elem_t *snd_mixer_last_elem(snd_mixer_t *mixer)
802 {
803 	assert(mixer);
804 	if (list_empty(&mixer->elems))
805 		return NULL;
806 	return list_entry(mixer->elems.prev, snd_mixer_elem_t, list);
807 }
808 
809 /**
810  * \brief get next mixer element
811  * \param elem mixer element
812  * \return pointer to next element
813  */
snd_mixer_elem_next(snd_mixer_elem_t * elem)814 snd_mixer_elem_t *snd_mixer_elem_next(snd_mixer_elem_t *elem)
815 {
816 	assert(elem);
817 	if (elem->list.next == &elem->class->mixer->elems)
818 		return NULL;
819 	return list_entry(elem->list.next, snd_mixer_elem_t, list);
820 }
821 
822 /**
823  * \brief get previous mixer element
824  * \param elem mixer element
825  * \return pointer to previous element
826  */
snd_mixer_elem_prev(snd_mixer_elem_t * elem)827 snd_mixer_elem_t *snd_mixer_elem_prev(snd_mixer_elem_t *elem)
828 {
829 	assert(elem);
830 	if (elem->list.prev == &elem->class->mixer->elems)
831 		return NULL;
832 	return list_entry(elem->list.prev, snd_mixer_elem_t, list);
833 }
834 
835 /**
836  * \brief Handle pending mixer events invoking callbacks
837  * \param mixer Mixer handle
838  * \return Number of events that occured on success, otherwise a negative error code on failure
839  */
snd_mixer_handle_events(snd_mixer_t * mixer)840 int snd_mixer_handle_events(snd_mixer_t *mixer)
841 {
842 	struct list_head *pos;
843 	assert(mixer);
844 	mixer->events = 0;
845 	list_for_each(pos, &mixer->slaves) {
846 		int err;
847 		snd_mixer_slave_t *s;
848 		s = list_entry(pos, snd_mixer_slave_t, list);
849 		err = snd_hctl_handle_events(s->hctl);
850 		if (err < 0)
851 			return err;
852 	}
853 	return mixer->events;
854 }
855 
856 /**
857  * \brief Set callback function for a mixer
858  * \param obj mixer handle
859  * \param val callback function
860  */
snd_mixer_set_callback(snd_mixer_t * obj,snd_mixer_callback_t val)861 void snd_mixer_set_callback(snd_mixer_t *obj, snd_mixer_callback_t val)
862 {
863 	assert(obj);
864 	obj->callback = val;
865 }
866 
867 /**
868  * \brief Set callback private value for a mixer
869  * \param mixer mixer handle
870  * \param val callback private value
871  */
snd_mixer_set_callback_private(snd_mixer_t * mixer,void * val)872 void snd_mixer_set_callback_private(snd_mixer_t *mixer, void * val)
873 {
874 	assert(mixer);
875 	mixer->callback_private = val;
876 }
877 
878 /**
879  * \brief Get callback private value for a mixer
880  * \param mixer mixer handle
881  * \return callback private value
882  */
snd_mixer_get_callback_private(const snd_mixer_t * mixer)883 void * snd_mixer_get_callback_private(const snd_mixer_t *mixer)
884 {
885 	assert(mixer);
886 	return mixer->callback_private;
887 }
888 
889 /**
890  * \brief Get elements count for a mixer
891  * \param mixer mixer handle
892  * \return elements count
893  */
snd_mixer_get_count(const snd_mixer_t * mixer)894 unsigned int snd_mixer_get_count(const snd_mixer_t *mixer)
895 {
896 	assert(mixer);
897 	return mixer->count;
898 }
899 
900 /**
901  * \brief Set callback function for a mixer element
902  * \param mixer mixer element
903  * \param val callback function
904  */
snd_mixer_elem_set_callback(snd_mixer_elem_t * mixer,snd_mixer_elem_callback_t val)905 void snd_mixer_elem_set_callback(snd_mixer_elem_t *mixer, snd_mixer_elem_callback_t val)
906 {
907 	assert(mixer);
908 	mixer->callback = val;
909 }
910 
911 /**
912  * \brief Set callback private value for a mixer element
913  * \param mixer mixer element
914  * \param val callback private value
915  */
snd_mixer_elem_set_callback_private(snd_mixer_elem_t * mixer,void * val)916 void snd_mixer_elem_set_callback_private(snd_mixer_elem_t *mixer, void * val)
917 {
918 	assert(mixer);
919 	mixer->callback_private = val;
920 }
921 
922 /**
923  * \brief Get callback private value for a mixer element
924  * \param mixer mixer element
925  * \return callback private value
926  */
snd_mixer_elem_get_callback_private(const snd_mixer_elem_t * mixer)927 void * snd_mixer_elem_get_callback_private(const snd_mixer_elem_t *mixer)
928 {
929 	assert(mixer);
930 	return mixer->callback_private;
931 }
932 
933 /**
934  * \brief Get type for a mixer element
935  * \param mixer mixer element
936  * \return mixer element type
937  */
snd_mixer_elem_get_type(const snd_mixer_elem_t * mixer)938 snd_mixer_elem_type_t snd_mixer_elem_get_type(const snd_mixer_elem_t *mixer)
939 {
940 	assert(mixer);
941 	return mixer->type;
942 }
943 
944 
945 /**
946  * \brief get size of #snd_mixer_class_t
947  * \return size in bytes
948  */
snd_mixer_class_sizeof()949 size_t snd_mixer_class_sizeof()
950 {
951 	return sizeof(snd_mixer_class_t);
952 }
953 
954 /**
955  * \brief allocate an invalid #snd_mixer_class_t using standard malloc
956  * \param ptr returned pointer
957  * \return 0 on success otherwise negative error code
958  */
snd_mixer_class_malloc(snd_mixer_class_t ** ptr)959 int snd_mixer_class_malloc(snd_mixer_class_t **ptr)
960 {
961 	assert(ptr);
962 	*ptr = calloc(1, sizeof(snd_mixer_class_t));
963 	if (!*ptr)
964 		return -ENOMEM;
965 	return 0;
966 }
967 
968 /**
969  * \brief frees a previously allocated #snd_mixer_class_t
970  * \param obj pointer to object to free
971  */
snd_mixer_class_free(snd_mixer_class_t * obj)972 void snd_mixer_class_free(snd_mixer_class_t *obj)
973 {
974 	if (obj->private_free)
975 		obj->private_free(obj);
976 	free(obj);
977 }
978 
979 /**
980  * \brief copy one #snd_mixer_class_t to another
981  * \param dst pointer to destination
982  * \param src pointer to source
983  */
snd_mixer_class_copy(snd_mixer_class_t * dst,const snd_mixer_class_t * src)984 void snd_mixer_class_copy(snd_mixer_class_t *dst, const snd_mixer_class_t *src)
985 {
986 	assert(dst && src);
987 	*dst = *src;
988 }
989 
990 /**
991  * \brief Get a mixer associated to given mixer class
992  * \param obj Mixer simple class identifier
993  * \return mixer pointer
994  */
snd_mixer_class_get_mixer(const snd_mixer_class_t * obj)995 snd_mixer_t *snd_mixer_class_get_mixer(const snd_mixer_class_t *obj)
996 {
997 	assert(obj);
998 	return obj->mixer;
999 }
1000 
1001 /**
1002  * \brief Get mixer event callback associated to given mixer class
1003  * \param obj Mixer simple class identifier
1004  * \return event callback pointer
1005  */
snd_mixer_class_get_event(const snd_mixer_class_t * obj)1006 snd_mixer_event_t snd_mixer_class_get_event(const snd_mixer_class_t *obj)
1007 {
1008 	assert(obj);
1009 	return obj->event;
1010 }
1011 
1012 /**
1013  * \brief Get mixer private data associated to given mixer class
1014  * \param obj Mixer simple class identifier
1015  * \return event callback pointer
1016  */
snd_mixer_class_get_private(const snd_mixer_class_t * obj)1017 void *snd_mixer_class_get_private(const snd_mixer_class_t *obj)
1018 {
1019 	assert(obj);
1020 	return obj->private_data;
1021 }
1022 
1023 
1024 /**
1025  * \brief Get mixer compare callback associated to given mixer class
1026  * \param obj Mixer simple class identifier
1027  * \return event callback pointer
1028  */
snd_mixer_class_get_compare(const snd_mixer_class_t * obj)1029 snd_mixer_compare_t snd_mixer_class_get_compare(const snd_mixer_class_t *obj)
1030 {
1031 	assert(obj);
1032 	return obj->compare;
1033 }
1034 
1035 /**
1036  * \brief Set mixer event callback to given mixer class
1037  * \param obj Mixer simple class identifier
1038  * \param event Event callback
1039  * \return zero if success, otherwise a negative error code
1040  */
snd_mixer_class_set_event(snd_mixer_class_t * obj,snd_mixer_event_t event)1041 int snd_mixer_class_set_event(snd_mixer_class_t *obj, snd_mixer_event_t event)
1042 {
1043 	assert(obj);
1044 	obj->event = event;
1045 	return 0;
1046 }
1047 
1048 /**
1049  * \brief Set mixer private data to given mixer class
1050  * \param obj Mixer simple class identifier
1051  * \param private_data class private data
1052  * \return zero if success, otherwise a negative error code
1053  */
snd_mixer_class_set_private(snd_mixer_class_t * obj,void * private_data)1054 int snd_mixer_class_set_private(snd_mixer_class_t *obj, void *private_data)
1055 {
1056 	assert(obj);
1057 	obj->private_data = private_data;
1058 	return 0;
1059 }
1060 
1061 /**
1062  * \brief Set mixer private data free callback to given mixer class
1063  * \param obj Mixer simple class identifier
1064  * \param private_free Mixer class private data free callback
1065  * \return zero if success, otherwise a negative error code
1066  */
snd_mixer_class_set_private_free(snd_mixer_class_t * obj,void (* private_free)(snd_mixer_class_t *))1067 int snd_mixer_class_set_private_free(snd_mixer_class_t *obj, void (*private_free)(snd_mixer_class_t *))
1068 {
1069 	assert(obj);
1070 	obj->private_free = private_free;
1071 	return 0;
1072 }
1073 
1074 /**
1075  * \brief Set mixer compare callback to given mixer class
1076  * \param obj Mixer simple class identifier
1077  * \param compare the compare callback to be used
1078  * \return zero if success, otherwise a negative error code
1079  */
snd_mixer_class_set_compare(snd_mixer_class_t * obj,snd_mixer_compare_t compare)1080 int snd_mixer_class_set_compare(snd_mixer_class_t *obj, snd_mixer_compare_t compare)
1081 {
1082 	assert(obj);
1083 	obj->compare = compare;
1084 	return 0;
1085 }
1086