Imported Upstream version 1.0.25
[platform/upstream/alsa-lib.git] / src / control / hcontrol.c
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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
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
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
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
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
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
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
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  */
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;
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
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  */
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  */
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
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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)
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  */
724 snd_ctl_t *snd_hctl_ctl(snd_hctl_t *hctl)
725 {
726         return hctl->ctl;
727 }
728
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                 assert(res >= 0 && dir == 0);
746                 if (res < 0 || dir != 0)
747                         return -ENOENT;
748                 snd_hctl_elem_remove(hctl, (unsigned int) res);
749                 return 0;
750         }
751         if (event->data.elem.mask & SNDRV_CTL_EVENT_MASK_ADD) {
752                 elem = calloc(1, sizeof(snd_hctl_elem_t));
753                 if (elem == NULL)
754                         return -ENOMEM;
755                 elem->id = event->data.elem.id;
756                 elem->hctl = hctl;
757                 res = snd_hctl_elem_add(hctl, elem);
758                 if (res < 0)
759                         return res;
760         }
761         if (event->data.elem.mask & (SNDRV_CTL_EVENT_MASK_VALUE |
762                                      SNDRV_CTL_EVENT_MASK_INFO)) {
763                 elem = snd_hctl_find_elem(hctl, &event->data.elem.id);
764                 assert(elem);
765                 if (!elem)
766                         return -ENOENT;
767                 res = snd_hctl_elem_throw_event(elem, event->data.elem.mask &
768                                                 (SNDRV_CTL_EVENT_MASK_VALUE |
769                                                  SNDRV_CTL_EVENT_MASK_INFO));
770                 if (res < 0)
771                         return res;
772         }
773         return 0;
774 }
775
776 /**
777  * \brief Handle pending HCTL events invoking callbacks
778  * \param hctl HCTL handle
779  * \return 0 otherwise a negative error code on failure
780  */
781 int snd_hctl_handle_events(snd_hctl_t *hctl)
782 {
783         snd_ctl_event_t event;
784         int res;
785         unsigned int count = 0;
786         
787         assert(hctl);
788         assert(hctl->ctl);
789         while ((res = snd_ctl_read(hctl->ctl, &event)) != 0 &&
790                res != -EAGAIN) {
791                 if (res < 0)
792                         return res;
793                 res = snd_hctl_handle_event(hctl, &event);
794                 if (res < 0)
795                         return res;
796                 count++;
797         }
798         return count;
799 }
800
801 /**
802  * \brief Get information for an HCTL element
803  * \param elem HCTL element
804  * \param info HCTL element information
805  * \return 0 otherwise a negative error code on failure
806  */
807 int snd_hctl_elem_info(snd_hctl_elem_t *elem, snd_ctl_elem_info_t *info)
808 {
809         assert(elem);
810         assert(elem->hctl);
811         assert(info);
812         info->id = elem->id;
813         return snd_ctl_elem_info(elem->hctl->ctl, info);
814 }
815
816 /**
817  * \brief Get value for an HCTL element
818  * \param elem HCTL element
819  * \param value HCTL element value
820  * \return 0 otherwise a negative error code on failure
821  */
822 int snd_hctl_elem_read(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value)
823 {
824         assert(elem);
825         assert(elem->hctl);
826         assert(value);
827         value->id = elem->id;
828         return snd_ctl_elem_read(elem->hctl->ctl, value);
829 }
830
831 /**
832  * \brief Set value for an HCTL element
833  * \param elem HCTL element
834  * \param value HCTL element value
835  * \retval 0 on success
836  * \retval >1 on success when value was changed
837  * \retval <0 a negative error code on failure
838  */
839 int snd_hctl_elem_write(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value)
840 {
841         assert(elem);
842         assert(elem->hctl);
843         assert(value);
844         value->id = elem->id;
845         return snd_ctl_elem_write(elem->hctl->ctl, value);
846 }
847
848 /**
849  * \brief Get TLV value for an HCTL element
850  * \param elem HCTL element
851  * \param tlv TLV array for value
852  * \param tlv_size size of TLV array in bytes
853  * \return 0 otherwise a negative error code on failure
854  */
855 int snd_hctl_elem_tlv_read(snd_hctl_elem_t *elem, unsigned int *tlv, unsigned int tlv_size)
856 {
857         assert(elem);
858         assert(tlv);
859         assert(tlv_size >= 12);
860         return snd_ctl_elem_tlv_read(elem->hctl->ctl, &elem->id, tlv, tlv_size);
861 }
862
863 /**
864  * \brief Set TLV value for an HCTL element
865  * \param elem HCTL element
866  * \param tlv TLV array for value
867  * \retval 0 on success
868  * \retval >1 on success when value was changed
869  * \retval <0 a negative error code on failure
870  */
871 int snd_hctl_elem_tlv_write(snd_hctl_elem_t *elem, const unsigned int *tlv)
872 {
873         assert(elem);
874         assert(tlv);
875         assert(tlv[1] >= 4);
876         return snd_ctl_elem_tlv_write(elem->hctl->ctl, &elem->id, tlv);
877 }
878
879 /**
880  * \brief Set TLV value for an HCTL element
881  * \param elem HCTL element
882  * \param tlv TLV array for value
883  * \retval 0 on success
884  * \retval >1 on success when value was changed
885  * \retval <0 a negative error code on failure
886  */
887 int snd_hctl_elem_tlv_command(snd_hctl_elem_t *elem, const unsigned int *tlv)
888 {
889         assert(elem);
890         assert(tlv);
891         assert(tlv[1] >= 4);
892         return snd_ctl_elem_tlv_command(elem->hctl->ctl, &elem->id, tlv);
893 }
894
895 /**
896  * \brief Get HCTL handle for an HCTL element
897  * \param elem HCTL element
898  * \return HCTL handle
899  */
900 snd_hctl_t *snd_hctl_elem_get_hctl(snd_hctl_elem_t *elem)
901 {
902         assert(elem);
903         return elem->hctl;
904 }
905
906 /**
907  * \brief Get CTL element identifier of a CTL element id/value
908  * \param obj CTL element id/value
909  * \param ptr Pointer to returned CTL element identifier
910  */
911 void snd_hctl_elem_get_id(const snd_hctl_elem_t *obj, snd_ctl_elem_id_t *ptr)
912 {
913         assert(obj && ptr);
914         *ptr = obj->id;
915 }
916
917 /**
918  * \brief Get element numeric identifier of a CTL element id/value
919  * \param obj CTL element id/value
920  * \return element numeric identifier
921  */
922 unsigned int snd_hctl_elem_get_numid(const snd_hctl_elem_t *obj)
923 {
924         assert(obj);
925         return obj->id.numid;
926 }
927
928 /**
929  * \brief Get interface part of CTL element identifier of a CTL element id/value
930  * \param obj CTL element id/value
931  * \return interface part of element identifier
932  */
933 snd_ctl_elem_iface_t snd_hctl_elem_get_interface(const snd_hctl_elem_t *obj)
934 {
935         assert(obj);
936         return obj->id.iface;
937 }
938
939 /**
940  * \brief Get device part of CTL element identifier of a CTL element id/value
941  * \param obj CTL element id/value
942  * \return device part of element identifier
943  */
944 unsigned int snd_hctl_elem_get_device(const snd_hctl_elem_t *obj)
945 {
946         assert(obj);
947         return obj->id.device;
948 }
949
950 /**
951  * \brief Get subdevice part of CTL element identifier of a CTL element id/value
952  * \param obj CTL element id/value
953  * \return subdevice part of element identifier
954  */
955 unsigned int snd_hctl_elem_get_subdevice(const snd_hctl_elem_t *obj)
956 {
957         assert(obj);
958         return obj->id.subdevice;
959 }
960
961 /**
962  * \brief Get name part of CTL element identifier of a CTL element id/value
963  * \param obj CTL element id/value
964  * \return name part of element identifier
965  */
966 const char *snd_hctl_elem_get_name(const snd_hctl_elem_t *obj)
967 {
968         assert(obj);
969         return (const char *)obj->id.name;
970 }
971
972 /**
973  * \brief Get index part of CTL element identifier of a CTL element id/value
974  * \param obj CTL element id/value
975  * \return index part of element identifier
976  */
977 unsigned int snd_hctl_elem_get_index(const snd_hctl_elem_t *obj)
978 {
979         assert(obj);
980         return obj->id.index;
981 }
982
983 /**
984  * \brief Set callback function for an HCTL element
985  * \param obj HCTL element
986  * \param val callback function
987  */
988 void snd_hctl_elem_set_callback(snd_hctl_elem_t *obj, snd_hctl_elem_callback_t val)
989 {
990         assert(obj);
991         obj->callback = val;
992 }
993
994 /**
995  * \brief Set callback private value for an HCTL element
996  * \param obj HCTL element
997  * \param val callback private value
998  */
999 void snd_hctl_elem_set_callback_private(snd_hctl_elem_t *obj, void * val)
1000 {
1001         assert(obj);
1002         obj->callback_private = val;
1003 }
1004
1005 /**
1006  * \brief Get callback private value for an HCTL element
1007  * \param obj HCTL element
1008  * \return callback private value
1009  */
1010 void * snd_hctl_elem_get_callback_private(const snd_hctl_elem_t *obj)
1011 {
1012         assert(obj);
1013         return obj->callback_private;
1014 }
1015