xref: /third_party/alsa-lib/src/mixer/mixer.c (revision d5ac70f0)
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.
36This 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
51typedef struct _snd_mixer_slave {
52	snd_hctl_t *hctl;
53	struct list_head list;
54} snd_mixer_slave_t;
55
56#endif
57
58static 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 */
68int 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 */
96int 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 */
119int 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 */
138int snd_mixer_elem_empty(snd_mixer_elem_t *melem)
139{
140	return bag_empty(&melem->helems);
141}
142
143static 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
179static 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 */
210int 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 */
232int 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 */
262int 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 */
286int 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 */
308int 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
322static 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
331static 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
339static 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 */
369void *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 */
385int 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 */
411int 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 */
455int 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 */
488void 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 */
502int 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 */
514int 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 */
527int 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 */
557int 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 */
579int 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 */
597void 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 */
612int 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
639static 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
651static 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
659static 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 */
677int 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 */
697int 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 */
721int 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 */
751int 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 */
771int 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 */
799snd_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 */
812snd_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 */
825snd_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 */
838snd_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 */
851int 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 */
872void 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 */
883void 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 */
894void * 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 */
905unsigned 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 */
916void 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 */
927void 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 */
938void * 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 */
949snd_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 */
960size_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 */
970int 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 */
983void 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 */
995void 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 */
1006snd_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 */
1017snd_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 */
1028void *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 */
1040snd_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 */
1052int 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 */
1065int 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 */
1078int 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 */
1091int 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