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