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