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