1 /**
2 * \file control/hcontrol.c
3 * \brief HCTL Interface - High Level CTL
4 * \author Jaroslav Kysela <perex@perex.cz>
5 * \author Abramo Bagnara <abramo@alsa-project.org>
6 * \date 2000
7 *
8 * HCTL interface is designed to access preloaded and sorted primitive controls.
9 * Callbacks may be used for event handling.
10 * See \ref hcontrol page for more details.
11 */
12 /*
13 * Control Interface - high level API
14 * Copyright (c) 2000 by Jaroslav Kysela <perex@perex.cz>
15 * Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
16 *
17 *
18 * This library is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU Lesser General Public License as
20 * published by the Free Software Foundation; either version 2.1 of
21 * the License, or (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU Lesser General Public License for more details.
27 *
28 * You should have received a copy of the GNU Lesser General Public
29 * License along with this library; if not, write to the Free Software
30 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
31 *
32 */
33
34 /*! \page hcontrol High level control interface
35
36 <P> High level control interface is designed to access preloaded and sorted primitive controls.
37
38 \section hcontrol_general_overview General overview
39
40 <P> High level control interface caches the accesses to primitive controls
41 to reduce overhead accessing the real controls in kernel drivers.
42
43 */
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <fcntl.h>
50 #include <sys/ioctl.h>
51 #include "control_local.h"
52 #ifdef HAVE_LIBPTHREAD
53 #include <pthread.h>
54 #endif
55
56 #ifndef DOC_HIDDEN
57 #define NOT_FOUND 1000000000
58 #endif
59
60 static int snd_hctl_compare_default(const snd_hctl_elem_t *c1,
61 const snd_hctl_elem_t *c2);
62
63 /**
64 * \brief Opens an HCTL
65 * \param hctlp Returned HCTL handle
66 * \param name ASCII identifier of the underlying CTL handle
67 * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC)
68 * \return 0 on success otherwise a negative error code
69 */
snd_hctl_open(snd_hctl_t ** hctlp,const char * name,int mode)70 int snd_hctl_open(snd_hctl_t **hctlp, const char *name, int mode)
71 {
72 snd_ctl_t *ctl;
73 int err;
74
75 if ((err = snd_ctl_open(&ctl, name, mode)) < 0)
76 return err;
77 err = snd_hctl_open_ctl(hctlp, ctl);
78 if (err < 0)
79 snd_ctl_close(ctl);
80 return err;
81 }
82
83 /**
84 * \brief Opens an HCTL
85 * \param hctlp Returned HCTL handle
86 * \param ctl underlying CTL handle
87 * \return 0 on success otherwise a negative error code
88 */
snd_hctl_open_ctl(snd_hctl_t ** hctlp,snd_ctl_t * ctl)89 int snd_hctl_open_ctl(snd_hctl_t **hctlp, snd_ctl_t *ctl)
90 {
91 snd_hctl_t *hctl;
92
93 assert(hctlp);
94 *hctlp = NULL;
95 if ((hctl = (snd_hctl_t *)calloc(1, sizeof(snd_hctl_t))) == NULL)
96 return -ENOMEM;
97 INIT_LIST_HEAD(&hctl->elems);
98 hctl->ctl = ctl;
99 *hctlp = hctl;
100 return 0;
101 }
102
103 /**
104 * \brief close HCTL handle
105 * \param hctl HCTL handle
106 * \return 0 on success otherwise a negative error code
107 *
108 * Closes the specified HCTL handle and frees all associated
109 * resources.
110 */
snd_hctl_close(snd_hctl_t * hctl)111 int snd_hctl_close(snd_hctl_t *hctl)
112 {
113 int err;
114
115 assert(hctl);
116 err = snd_ctl_close(hctl->ctl);
117 snd_hctl_free(hctl);
118 free(hctl);
119 return err;
120 }
121
122 /**
123 * \brief get identifier of HCTL handle
124 * \param hctl HCTL handle
125 * \return ascii identifier of HCTL handle
126 *
127 * Returns the ASCII identifier of given HCTL handle. It's the same
128 * identifier specified in snd_hctl_open().
129 */
snd_hctl_name(snd_hctl_t * hctl)130 const char *snd_hctl_name(snd_hctl_t *hctl)
131 {
132 assert(hctl);
133 return snd_ctl_name(hctl->ctl);
134 }
135
136 /**
137 * \brief set nonblock mode
138 * \param hctl HCTL handle
139 * \param nonblock 0 = block, 1 = nonblock mode
140 * \return 0 on success otherwise a negative error code
141 */
snd_hctl_nonblock(snd_hctl_t * hctl,int nonblock)142 int snd_hctl_nonblock(snd_hctl_t *hctl, int nonblock)
143 {
144 assert(hctl);
145 return snd_ctl_nonblock(hctl->ctl, nonblock);
146 }
147
148 /**
149 * \brief set async mode
150 * \param hctl HCTL handle
151 * \param sig Signal to raise: < 0 disable, 0 default (SIGIO)
152 * \param pid Process ID to signal: 0 current
153 * \return 0 on success otherwise a negative error code
154 *
155 * A signal is raised when a change happens.
156 */
snd_hctl_async(snd_hctl_t * hctl,int sig,pid_t pid)157 int snd_hctl_async(snd_hctl_t *hctl, int sig, pid_t pid)
158 {
159 assert(hctl);
160 return snd_ctl_async(hctl->ctl, sig, pid);
161 }
162
163 /**
164 * \brief get count of poll descriptors for HCTL handle
165 * \param hctl HCTL handle
166 * \return count of poll descriptors
167 */
snd_hctl_poll_descriptors_count(snd_hctl_t * hctl)168 int snd_hctl_poll_descriptors_count(snd_hctl_t *hctl)
169 {
170 assert(hctl);
171 return snd_ctl_poll_descriptors_count(hctl->ctl);
172 }
173
174 /**
175 * \brief get poll descriptors
176 * \param hctl HCTL handle
177 * \param pfds array of poll descriptors
178 * \param space space in the poll descriptor array
179 * \return count of filled descriptors
180 */
snd_hctl_poll_descriptors(snd_hctl_t * hctl,struct pollfd * pfds,unsigned int space)181 int snd_hctl_poll_descriptors(snd_hctl_t *hctl, struct pollfd *pfds, unsigned int space)
182 {
183 assert(hctl);
184 return snd_ctl_poll_descriptors(hctl->ctl, pfds, space);
185 }
186
187 /**
188 * \brief get returned events from poll descriptors
189 * \param hctl HCTL handle
190 * \param pfds array of poll descriptors
191 * \param nfds count of poll descriptors
192 * \param revents returned events
193 * \return zero if success, otherwise a negative error code
194 */
snd_hctl_poll_descriptors_revents(snd_hctl_t * hctl,struct pollfd * pfds,unsigned int nfds,unsigned short * revents)195 int snd_hctl_poll_descriptors_revents(snd_hctl_t *hctl, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
196 {
197 assert(hctl);
198 return snd_ctl_poll_descriptors_revents(hctl->ctl, pfds, nfds, revents);
199 }
200
snd_hctl_throw_event(snd_hctl_t * hctl,unsigned int mask,snd_hctl_elem_t * elem)201 static int snd_hctl_throw_event(snd_hctl_t *hctl, unsigned int mask,
202 snd_hctl_elem_t *elem)
203 {
204 if (hctl->callback)
205 return hctl->callback(hctl, mask, elem);
206 return 0;
207 }
208
snd_hctl_elem_throw_event(snd_hctl_elem_t * elem,unsigned int mask)209 static int snd_hctl_elem_throw_event(snd_hctl_elem_t *elem,
210 unsigned int mask)
211 {
212 if (elem->callback)
213 return elem->callback(elem, mask);
214 return 0;
215 }
216
snd_hctl_compare_mixer_priority_lookup(const char ** name,const char * const * names,int coef)217 static int snd_hctl_compare_mixer_priority_lookup(const char **name, const char * const *names, int coef)
218 {
219 int res;
220
221 for (res = 0; *names; names++, res += coef) {
222 if (!strncmp(*name, *names, strlen(*names))) {
223 *name += strlen(*names);
224 if (**name == ' ')
225 (*name)++;
226 return res+1;
227 }
228 }
229 return NOT_FOUND;
230 }
231
get_compare_weight(const snd_ctl_elem_id_t * id)232 static int get_compare_weight(const snd_ctl_elem_id_t *id)
233 {
234 static const char *const names[] = {
235 "Master",
236 "Hardware Master",
237 "Headphone",
238 "Tone Control",
239 "3D Control",
240 "PCM",
241 "Front",
242 "Surround",
243 "Center",
244 "LFE",
245 "Synth",
246 "FM",
247 "Wave",
248 "Music",
249 "DSP",
250 "Line",
251 "CD",
252 "Mic",
253 "Phone",
254 "Video",
255 "Zoom Video",
256 "PC Speaker",
257 "Aux",
258 "Mono",
259 "ADC",
260 "Capture Source",
261 "Capture",
262 "Playback",
263 "Loopback",
264 "Analog Loopback",
265 "Digital Loopback",
266 "I2S",
267 "IEC958",
268 NULL
269 };
270 static const char *const names1[] = {
271 "Switch",
272 "Volume",
273 "Playback",
274 "Capture",
275 "Bypass",
276 "Mono",
277 "Front",
278 "Rear",
279 "Pan",
280 "Output",
281 "-",
282 NULL
283 };
284 static const char *const names2[] = {
285 "Switch",
286 "Volume",
287 "Bypass",
288 "Depth",
289 "Wide",
290 "Space",
291 "Level",
292 "Center",
293 NULL
294 };
295 const char *name = (char *)id->name, *name1;
296 int res, res1;
297
298 if ((res = snd_hctl_compare_mixer_priority_lookup((const char **)&name, names, 1000000)) == NOT_FOUND)
299 return NOT_FOUND;
300 if (*name == '\0')
301 return res;
302 for (name1 = name; *name1 != '\0'; name1++);
303 for (name1--; name1 != name && *name1 != ' '; name1--);
304 while (name1 != name && *name1 == ' ')
305 name1--;
306 if (name1 != name) {
307 for (; name1 != name && *name1 != ' '; name1--);
308 name = name1;
309 if ((res1 = snd_hctl_compare_mixer_priority_lookup((const char **)&name, names1, 1000)) == NOT_FOUND)
310 return res;
311 res += res1;
312 } else {
313 name = name1;
314 }
315 if ((res1 = snd_hctl_compare_mixer_priority_lookup((const char **)&name, names2, 1)) == NOT_FOUND)
316 return res;
317 return res + res1;
318 }
319
_snd_hctl_find_elem(snd_hctl_t * hctl,const snd_ctl_elem_id_t * id,int * dir)320 static int _snd_hctl_find_elem(snd_hctl_t *hctl, const snd_ctl_elem_id_t *id, int *dir)
321 {
322 unsigned int l, u;
323 snd_hctl_elem_t el;
324 int c = 0;
325 int idx = -1;
326 assert(hctl && id);
327 assert(hctl->compare);
328 el.id = *id;
329 el.compare_weight = get_compare_weight(id);
330 l = 0;
331 u = hctl->count;
332 while (l < u) {
333 idx = (l + u) / 2;
334 c = hctl->compare(&el, hctl->pelems[idx]);
335 if (c < 0)
336 u = idx;
337 else if (c > 0)
338 l = idx + 1;
339 else
340 break;
341 }
342 *dir = c;
343 return idx;
344 }
345
snd_hctl_elem_add(snd_hctl_t * hctl,snd_hctl_elem_t * elem)346 static int snd_hctl_elem_add(snd_hctl_t *hctl, snd_hctl_elem_t *elem)
347 {
348 int dir;
349 int idx;
350 elem->compare_weight = get_compare_weight(&elem->id);
351 if (hctl->count == hctl->alloc) {
352 snd_hctl_elem_t **h;
353 hctl->alloc += 32;
354 h = realloc(hctl->pelems, sizeof(*h) * hctl->alloc);
355 if (!h) {
356 hctl->alloc -= 32;
357 return -ENOMEM;
358 }
359 hctl->pelems = h;
360 }
361 if (hctl->count == 0) {
362 list_add_tail(&elem->list, &hctl->elems);
363 hctl->pelems[0] = elem;
364 } else {
365 idx = _snd_hctl_find_elem(hctl, &elem->id, &dir);
366 assert(dir != 0);
367 if (dir > 0) {
368 list_add(&elem->list, &hctl->pelems[idx]->list);
369 idx++;
370 } else {
371 list_add_tail(&elem->list, &hctl->pelems[idx]->list);
372 }
373 memmove(hctl->pelems + idx + 1,
374 hctl->pelems + idx,
375 (hctl->count - idx) * sizeof(snd_hctl_elem_t *));
376 hctl->pelems[idx] = elem;
377 }
378 hctl->count++;
379 return snd_hctl_throw_event(hctl, SNDRV_CTL_EVENT_MASK_ADD, elem);
380 }
381
snd_hctl_elem_remove(snd_hctl_t * hctl,unsigned int idx)382 static void snd_hctl_elem_remove(snd_hctl_t *hctl, unsigned int idx)
383 {
384 snd_hctl_elem_t *elem = hctl->pelems[idx];
385 unsigned int m;
386 snd_hctl_elem_throw_event(elem, SNDRV_CTL_EVENT_MASK_REMOVE);
387 list_del(&elem->list);
388 free(elem);
389 hctl->count--;
390 m = hctl->count - idx;
391 if (m > 0)
392 memmove(hctl->pelems + idx,
393 hctl->pelems + idx + 1,
394 m * sizeof(snd_hctl_elem_t *));
395 }
396
397 /**
398 * \brief free HCTL loaded elements
399 * \param hctl HCTL handle
400 * \return 0 on success otherwise a negative error code
401 */
snd_hctl_free(snd_hctl_t * hctl)402 int snd_hctl_free(snd_hctl_t *hctl)
403 {
404 while (hctl->count > 0)
405 snd_hctl_elem_remove(hctl, hctl->count - 1);
406 free(hctl->pelems);
407 hctl->pelems = 0;
408 hctl->alloc = 0;
409 INIT_LIST_HEAD(&hctl->elems);
410 return 0;
411 }
412
413 static snd_hctl_t *compare_hctl;
hctl_compare(const void * a,const void * b)414 static int hctl_compare(const void *a, const void *b) {
415 return compare_hctl->compare(*(const snd_hctl_elem_t * const *) a,
416 *(const snd_hctl_elem_t * const *) b);
417 }
418
snd_hctl_sort(snd_hctl_t * hctl)419 static void snd_hctl_sort(snd_hctl_t *hctl)
420 {
421 unsigned int k;
422 #ifdef HAVE_LIBPTHREAD
423 static pthread_mutex_t sync_lock = PTHREAD_MUTEX_INITIALIZER;
424 #endif
425
426 assert(hctl);
427 assert(hctl->compare);
428 INIT_LIST_HEAD(&hctl->elems);
429
430 #ifdef HAVE_LIBPTHREAD
431 pthread_mutex_lock(&sync_lock);
432 #endif
433 compare_hctl = hctl;
434 qsort(hctl->pelems, hctl->count, sizeof(*hctl->pelems), hctl_compare);
435 #ifdef HAVE_LIBPTHREAD
436 pthread_mutex_unlock(&sync_lock);
437 #endif
438 for (k = 0; k < hctl->count; k++)
439 list_add_tail(&hctl->pelems[k]->list, &hctl->elems);
440 }
441
442 /**
443 * \brief Change HCTL compare function and reorder elements
444 * \param hctl HCTL handle
445 * \param compare Element compare function
446 * \return 0 on success otherwise a negative error code
447 */
snd_hctl_set_compare(snd_hctl_t * hctl,snd_hctl_compare_t compare)448 int snd_hctl_set_compare(snd_hctl_t *hctl, snd_hctl_compare_t compare)
449 {
450 assert(hctl);
451 hctl->compare = compare == NULL ? snd_hctl_compare_default : compare;
452 snd_hctl_sort(hctl);
453 return 0;
454 }
455
456 /**
457 * \brief A "don't care" fast compare functions that may be used with #snd_hctl_set_compare
458 * \param c1 First HCTL element
459 * \param c2 Second HCTL element
460 * \return -1 if c1 < c2, 0 if c1 == c2, 1 if c1 > c2
461 */
snd_hctl_compare_fast(const snd_hctl_elem_t * c1,const snd_hctl_elem_t * c2)462 int snd_hctl_compare_fast(const snd_hctl_elem_t *c1,
463 const snd_hctl_elem_t *c2)
464 {
465 return c1->id.numid - c2->id.numid;
466 }
467
snd_hctl_compare_default(const snd_hctl_elem_t * c1,const snd_hctl_elem_t * c2)468 static int snd_hctl_compare_default(const snd_hctl_elem_t *c1,
469 const snd_hctl_elem_t *c2)
470 {
471 int res, d;
472
473 d = c1->id.iface - c2->id.iface;
474 if (d != 0)
475 return d;
476 if (c1->id.iface == SNDRV_CTL_ELEM_IFACE_MIXER) {
477 d = c1->compare_weight - c2->compare_weight;
478 if (d != 0)
479 return d;
480 }
481 d = c1->id.device - c2->id.device;
482 if (d != 0)
483 return d;
484 d = c1->id.subdevice - c2->id.subdevice;
485 if (d != 0)
486 return d;
487 res = strcmp((const char *)c1->id.name, (const char *)c2->id.name);
488 if (res != 0)
489 return res;
490 return c1->id.index - c2->id.index;
491 }
492
493 /**
494 * \brief get first element for an HCTL
495 * \param hctl HCTL handle
496 * \return pointer to first element
497 */
snd_hctl_first_elem(snd_hctl_t * hctl)498 snd_hctl_elem_t *snd_hctl_first_elem(snd_hctl_t *hctl)
499 {
500 assert(hctl);
501 if (list_empty(&hctl->elems))
502 return NULL;
503 return list_entry(hctl->elems.next, snd_hctl_elem_t, list);
504 }
505
506 /**
507 * \brief get last element for an HCTL
508 * \param hctl HCTL handle
509 * \return pointer to last element
510 */
snd_hctl_last_elem(snd_hctl_t * hctl)511 snd_hctl_elem_t *snd_hctl_last_elem(snd_hctl_t *hctl)
512 {
513 assert(hctl);
514 if (list_empty(&hctl->elems))
515 return NULL;
516 return list_entry(hctl->elems.prev, snd_hctl_elem_t, list);
517 }
518
519 /**
520 * \brief get next HCTL element
521 * \param elem HCTL element
522 * \return pointer to next element
523 */
snd_hctl_elem_next(snd_hctl_elem_t * elem)524 snd_hctl_elem_t *snd_hctl_elem_next(snd_hctl_elem_t *elem)
525 {
526 assert(elem);
527 if (elem->list.next == &elem->hctl->elems)
528 return NULL;
529 return list_entry(elem->list.next, snd_hctl_elem_t, list);
530 }
531
532 /**
533 * \brief get previous HCTL element
534 * \param elem HCTL element
535 * \return pointer to previous element
536 */
snd_hctl_elem_prev(snd_hctl_elem_t * elem)537 snd_hctl_elem_t *snd_hctl_elem_prev(snd_hctl_elem_t *elem)
538 {
539 assert(elem);
540 if (elem->list.prev == &elem->hctl->elems)
541 return NULL;
542 return list_entry(elem->list.prev, snd_hctl_elem_t, list);
543 }
544
545 /**
546 * \brief Search an HCTL element
547 * \param hctl HCTL handle
548 * \param id Element identifier
549 * \return pointer to found HCTL element or NULL if it does not exists
550 */
snd_hctl_find_elem(snd_hctl_t * hctl,const snd_ctl_elem_id_t * id)551 snd_hctl_elem_t *snd_hctl_find_elem(snd_hctl_t *hctl, const snd_ctl_elem_id_t *id)
552 {
553 int dir;
554 int res = _snd_hctl_find_elem(hctl, id, &dir);
555 if (res < 0 || dir != 0)
556 return NULL;
557 return hctl->pelems[res];
558 }
559
560 /**
561 * \brief Load an HCTL with all elements and sort them
562 * \param hctl HCTL handle
563 * \return 0 on success otherwise a negative error code
564 */
snd_hctl_load(snd_hctl_t * hctl)565 int snd_hctl_load(snd_hctl_t *hctl)
566 {
567 snd_ctl_elem_list_t list;
568 int err = 0;
569 unsigned int idx;
570
571 assert(hctl);
572 assert(hctl->ctl);
573 assert(hctl->count == 0);
574 assert(list_empty(&hctl->elems));
575 memset(&list, 0, sizeof(list));
576 if ((err = snd_ctl_elem_list(hctl->ctl, &list)) < 0)
577 goto _end;
578 while (list.count != list.used) {
579 err = snd_ctl_elem_list_alloc_space(&list, list.count);
580 if (err < 0)
581 goto _end;
582 if ((err = snd_ctl_elem_list(hctl->ctl, &list)) < 0)
583 goto _end;
584 }
585 if (hctl->alloc < list.count) {
586 hctl->alloc = list.count;
587 free(hctl->pelems);
588 hctl->pelems = malloc(hctl->alloc * sizeof(*hctl->pelems));
589 if (!hctl->pelems) {
590 err = -ENOMEM;
591 goto _end;
592 }
593 }
594 for (idx = 0; idx < list.count; idx++) {
595 snd_hctl_elem_t *elem;
596 elem = calloc(1, sizeof(snd_hctl_elem_t));
597 if (elem == NULL) {
598 snd_hctl_free(hctl);
599 err = -ENOMEM;
600 goto _end;
601 }
602 elem->id = list.pids[idx];
603 elem->hctl = hctl;
604 elem->compare_weight = get_compare_weight(&elem->id);
605 hctl->pelems[idx] = elem;
606 list_add_tail(&elem->list, &hctl->elems);
607 hctl->count++;
608 }
609 if (!hctl->compare)
610 hctl->compare = snd_hctl_compare_default;
611 snd_hctl_sort(hctl);
612 for (idx = 0; idx < hctl->count; idx++) {
613 int res = snd_hctl_throw_event(hctl, SNDRV_CTL_EVENT_MASK_ADD,
614 hctl->pelems[idx]);
615 if (res < 0)
616 return res;
617 }
618 err = snd_ctl_subscribe_events(hctl->ctl, 1);
619 _end:
620 free(list.pids);
621 return err;
622 }
623
624 /**
625 * \brief Set callback function for an HCTL
626 * \param hctl HCTL handle
627 * \param callback callback function
628 */
snd_hctl_set_callback(snd_hctl_t * hctl,snd_hctl_callback_t callback)629 void snd_hctl_set_callback(snd_hctl_t *hctl, snd_hctl_callback_t callback)
630 {
631 assert(hctl);
632 hctl->callback = callback;
633 }
634
635 /**
636 * \brief Set callback private value for an HCTL
637 * \param hctl HCTL handle
638 * \param callback_private callback private value
639 */
snd_hctl_set_callback_private(snd_hctl_t * hctl,void * callback_private)640 void snd_hctl_set_callback_private(snd_hctl_t *hctl, void *callback_private)
641 {
642 assert(hctl);
643 hctl->callback_private = callback_private;
644 }
645
646 /**
647 * \brief Get callback private value for an HCTL
648 * \param hctl HCTL handle
649 * \return callback private value
650 */
snd_hctl_get_callback_private(snd_hctl_t * hctl)651 void *snd_hctl_get_callback_private(snd_hctl_t *hctl)
652 {
653 assert(hctl);
654 return hctl->callback_private;
655 }
656
657 /**
658 * \brief Get number of loaded elements for an HCTL
659 * \param hctl HCTL handle
660 * \return elements count
661 */
snd_hctl_get_count(snd_hctl_t * hctl)662 unsigned int snd_hctl_get_count(snd_hctl_t *hctl)
663 {
664 return hctl->count;
665 }
666
667 /**
668 * \brief Wait for a HCTL to become ready (i.e. at least one event pending)
669 * \param hctl HCTL handle
670 * \param timeout maximum time in milliseconds to wait
671 * \return a positive value on success otherwise a negative error code
672 * \retval 0 timeout occurred
673 * \retval 1 an event is pending
674 */
snd_hctl_wait(snd_hctl_t * hctl,int timeout)675 int snd_hctl_wait(snd_hctl_t *hctl, int timeout)
676 {
677 struct pollfd *pfd;
678 unsigned short *revents;
679 int i, npfds, pollio, err, err_poll;
680
681 npfds = snd_hctl_poll_descriptors_count(hctl);
682 if (npfds <= 0 || npfds >= 16) {
683 SNDERR("Invalid poll_fds %d\n", npfds);
684 return -EIO;
685 }
686 pfd = alloca(sizeof(*pfd) * npfds);
687 revents = alloca(sizeof(*revents) * npfds);
688 err = snd_hctl_poll_descriptors(hctl, pfd, npfds);
689 if (err < 0)
690 return err;
691 if (err != npfds) {
692 SNDMSG("invalid poll descriptors %d\n", err);
693 return -EIO;
694 }
695 do {
696 pollio = 0;
697 err_poll = poll(pfd, npfds, timeout);
698 if (err_poll < 0) {
699 if (errno == EINTR && !CTLINABORT(hctl->ctl))
700 continue;
701 return -errno;
702 }
703 if (! err_poll)
704 break;
705 err = snd_hctl_poll_descriptors_revents(hctl, pfd, npfds, revents);
706 if (err < 0)
707 return err;
708 for (i = 0; i < npfds; i++) {
709 if (revents[i] & (POLLERR | POLLNVAL))
710 return -EIO;
711 if ((revents[i] & (POLLIN | POLLOUT)) == 0)
712 continue;
713 pollio++;
714 }
715 } while (! pollio);
716 return err_poll > 0 ? 1 : 0;
717 }
718
719 /**
720 * \brief Get a ctl handle associated to the given hctl handle
721 * \param hctl HCTL handle
722 * \return a ctl handle otherwise NULL
723 */
snd_hctl_ctl(snd_hctl_t * hctl)724 snd_ctl_t *snd_hctl_ctl(snd_hctl_t *hctl)
725 {
726 return hctl->ctl;
727 }
728
snd_hctl_handle_event(snd_hctl_t * hctl,snd_ctl_event_t * event)729 static int snd_hctl_handle_event(snd_hctl_t *hctl, snd_ctl_event_t *event)
730 {
731 snd_hctl_elem_t *elem;
732 int res;
733
734 assert(hctl);
735 assert(hctl->ctl);
736 switch (event->type) {
737 case SND_CTL_EVENT_ELEM:
738 break;
739 default:
740 return 0;
741 }
742 if (event->data.elem.mask == SNDRV_CTL_EVENT_MASK_REMOVE) {
743 int dir;
744 res = _snd_hctl_find_elem(hctl, &event->data.elem.id, &dir);
745 if (res < 0 || dir != 0)
746 return -ENOENT;
747 snd_hctl_elem_remove(hctl, (unsigned int) res);
748 return 0;
749 }
750 if (event->data.elem.mask & SNDRV_CTL_EVENT_MASK_ADD) {
751 elem = calloc(1, sizeof(snd_hctl_elem_t));
752 if (elem == NULL)
753 return -ENOMEM;
754 elem->id = event->data.elem.id;
755 elem->hctl = hctl;
756 res = snd_hctl_elem_add(hctl, elem);
757 if (res < 0)
758 return res;
759 }
760 if (event->data.elem.mask & (SNDRV_CTL_EVENT_MASK_VALUE |
761 SNDRV_CTL_EVENT_MASK_INFO)) {
762 elem = snd_hctl_find_elem(hctl, &event->data.elem.id);
763 if (!elem)
764 return -ENOENT;
765 res = snd_hctl_elem_throw_event(elem, event->data.elem.mask &
766 (SNDRV_CTL_EVENT_MASK_VALUE |
767 SNDRV_CTL_EVENT_MASK_INFO));
768 if (res < 0)
769 return res;
770 }
771 return 0;
772 }
773
774 /**
775 * \brief Handle pending HCTL events invoking callbacks
776 * \param hctl HCTL handle
777 * \return 0 otherwise a negative error code on failure
778 */
snd_hctl_handle_events(snd_hctl_t * hctl)779 int snd_hctl_handle_events(snd_hctl_t *hctl)
780 {
781 snd_ctl_event_t event;
782 int res;
783 unsigned int count = 0;
784
785 assert(hctl);
786 assert(hctl->ctl);
787 while ((res = snd_ctl_read(hctl->ctl, &event)) != 0 &&
788 res != -EAGAIN) {
789 if (res < 0)
790 return res;
791 res = snd_hctl_handle_event(hctl, &event);
792 if (res < 0)
793 return res;
794 count++;
795 }
796 return count;
797 }
798
799 /**
800 * \brief Get information for an HCTL element
801 * \param elem HCTL element
802 * \param info HCTL element information
803 * \return 0 otherwise a negative error code on failure
804 */
snd_hctl_elem_info(snd_hctl_elem_t * elem,snd_ctl_elem_info_t * info)805 int snd_hctl_elem_info(snd_hctl_elem_t *elem, snd_ctl_elem_info_t *info)
806 {
807 assert(elem);
808 assert(elem->hctl);
809 assert(info);
810 info->id = elem->id;
811 return snd_ctl_elem_info(elem->hctl->ctl, info);
812 }
813
814 /**
815 * \brief Get value for an HCTL element
816 * \param elem HCTL element
817 * \param value HCTL element value
818 * \return 0 otherwise a negative error code on failure
819 */
snd_hctl_elem_read(snd_hctl_elem_t * elem,snd_ctl_elem_value_t * value)820 int snd_hctl_elem_read(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value)
821 {
822 assert(elem);
823 assert(elem->hctl);
824 assert(value);
825 value->id = elem->id;
826 return snd_ctl_elem_read(elem->hctl->ctl, value);
827 }
828
829 /**
830 * \brief Set value for an HCTL element
831 * \param elem HCTL element
832 * \param value HCTL element value
833 * \retval 0 on success
834 * \retval >1 on success when value was changed
835 * \retval <0 a negative error code on failure
836 */
snd_hctl_elem_write(snd_hctl_elem_t * elem,snd_ctl_elem_value_t * value)837 int snd_hctl_elem_write(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value)
838 {
839 assert(elem);
840 assert(elem->hctl);
841 assert(value);
842 value->id = elem->id;
843 return snd_ctl_elem_write(elem->hctl->ctl, value);
844 }
845
846 /**
847 * \brief Get TLV value for an HCTL element
848 * \param elem HCTL element
849 * \param tlv TLV array for value
850 * \param tlv_size size of TLV array in bytes
851 * \return 0 otherwise a negative error code on failure
852 */
snd_hctl_elem_tlv_read(snd_hctl_elem_t * elem,unsigned int * tlv,unsigned int tlv_size)853 int snd_hctl_elem_tlv_read(snd_hctl_elem_t *elem, unsigned int *tlv, unsigned int tlv_size)
854 {
855 assert(elem);
856 assert(tlv);
857 assert(tlv_size >= 12);
858 return snd_ctl_elem_tlv_read(elem->hctl->ctl, &elem->id, tlv, tlv_size);
859 }
860
861 /**
862 * \brief Set TLV value for an HCTL element
863 * \param elem HCTL element
864 * \param tlv TLV array for value
865 * \retval 0 on success
866 * \retval >1 on success when value was changed
867 * \retval <0 a negative error code on failure
868 */
snd_hctl_elem_tlv_write(snd_hctl_elem_t * elem,const unsigned int * tlv)869 int snd_hctl_elem_tlv_write(snd_hctl_elem_t *elem, const unsigned int *tlv)
870 {
871 assert(elem);
872 assert(tlv);
873 assert(tlv[SNDRV_CTL_TLVO_LEN] >= 4);
874 return snd_ctl_elem_tlv_write(elem->hctl->ctl, &elem->id, tlv);
875 }
876
877 /**
878 * \brief Set TLV value for an HCTL element
879 * \param elem HCTL element
880 * \param tlv TLV array for value
881 * \retval 0 on success
882 * \retval >1 on success when value was changed
883 * \retval <0 a negative error code on failure
884 */
snd_hctl_elem_tlv_command(snd_hctl_elem_t * elem,const unsigned int * tlv)885 int snd_hctl_elem_tlv_command(snd_hctl_elem_t *elem, const unsigned int *tlv)
886 {
887 assert(elem);
888 assert(tlv);
889 assert(tlv[SNDRV_CTL_TLVO_LEN] >= 4);
890 return snd_ctl_elem_tlv_command(elem->hctl->ctl, &elem->id, tlv);
891 }
892
893 /**
894 * \brief Get HCTL handle for an HCTL element
895 * \param elem HCTL element
896 * \return HCTL handle
897 */
snd_hctl_elem_get_hctl(snd_hctl_elem_t * elem)898 snd_hctl_t *snd_hctl_elem_get_hctl(snd_hctl_elem_t *elem)
899 {
900 assert(elem);
901 return elem->hctl;
902 }
903
904 /**
905 * \brief Get CTL element identifier of a CTL element id/value
906 * \param obj CTL element id/value
907 * \param ptr Pointer to returned CTL element identifier
908 */
snd_hctl_elem_get_id(const snd_hctl_elem_t * obj,snd_ctl_elem_id_t * ptr)909 void snd_hctl_elem_get_id(const snd_hctl_elem_t *obj, snd_ctl_elem_id_t *ptr)
910 {
911 assert(obj && ptr);
912 *ptr = obj->id;
913 }
914
915 /**
916 * \brief Get element numeric identifier of a CTL element id/value
917 * \param obj CTL element id/value
918 * \return element numeric identifier
919 */
snd_hctl_elem_get_numid(const snd_hctl_elem_t * obj)920 unsigned int snd_hctl_elem_get_numid(const snd_hctl_elem_t *obj)
921 {
922 assert(obj);
923 return obj->id.numid;
924 }
925
926 /**
927 * \brief Get interface part of CTL element identifier of a CTL element id/value
928 * \param obj CTL element id/value
929 * \return interface part of element identifier
930 */
snd_hctl_elem_get_interface(const snd_hctl_elem_t * obj)931 snd_ctl_elem_iface_t snd_hctl_elem_get_interface(const snd_hctl_elem_t *obj)
932 {
933 assert(obj);
934 return obj->id.iface;
935 }
936
937 /**
938 * \brief Get device part of CTL element identifier of a CTL element id/value
939 * \param obj CTL element id/value
940 * \return device part of element identifier
941 */
snd_hctl_elem_get_device(const snd_hctl_elem_t * obj)942 unsigned int snd_hctl_elem_get_device(const snd_hctl_elem_t *obj)
943 {
944 assert(obj);
945 return obj->id.device;
946 }
947
948 /**
949 * \brief Get subdevice part of CTL element identifier of a CTL element id/value
950 * \param obj CTL element id/value
951 * \return subdevice part of element identifier
952 */
snd_hctl_elem_get_subdevice(const snd_hctl_elem_t * obj)953 unsigned int snd_hctl_elem_get_subdevice(const snd_hctl_elem_t *obj)
954 {
955 assert(obj);
956 return obj->id.subdevice;
957 }
958
959 /**
960 * \brief Get name part of CTL element identifier of a CTL element id/value
961 * \param obj CTL element id/value
962 * \return name part of element identifier
963 */
snd_hctl_elem_get_name(const snd_hctl_elem_t * obj)964 const char *snd_hctl_elem_get_name(const snd_hctl_elem_t *obj)
965 {
966 assert(obj);
967 return (const char *)obj->id.name;
968 }
969
970 /**
971 * \brief Get index part of CTL element identifier of a CTL element id/value
972 * \param obj CTL element id/value
973 * \return index part of element identifier
974 */
snd_hctl_elem_get_index(const snd_hctl_elem_t * obj)975 unsigned int snd_hctl_elem_get_index(const snd_hctl_elem_t *obj)
976 {
977 assert(obj);
978 return obj->id.index;
979 }
980
981 /**
982 * \brief Set callback function for an HCTL element
983 * \param obj HCTL element
984 * \param val callback function
985 */
snd_hctl_elem_set_callback(snd_hctl_elem_t * obj,snd_hctl_elem_callback_t val)986 void snd_hctl_elem_set_callback(snd_hctl_elem_t *obj, snd_hctl_elem_callback_t val)
987 {
988 assert(obj);
989 obj->callback = val;
990 }
991
992 /**
993 * \brief Set callback private value for an HCTL element
994 * \param obj HCTL element
995 * \param val callback private value
996 */
snd_hctl_elem_set_callback_private(snd_hctl_elem_t * obj,void * val)997 void snd_hctl_elem_set_callback_private(snd_hctl_elem_t *obj, void * val)
998 {
999 assert(obj);
1000 obj->callback_private = val;
1001 }
1002
1003 /**
1004 * \brief Get callback private value for an HCTL element
1005 * \param obj HCTL element
1006 * \return callback private value
1007 */
snd_hctl_elem_get_callback_private(const snd_hctl_elem_t * obj)1008 void * snd_hctl_elem_get_callback_private(const snd_hctl_elem_t *obj)
1009 {
1010 assert(obj);
1011 return obj->callback_private;
1012 }
1013
1014