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>
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.
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>
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.
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.
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
34 /*! \page hcontrol High level control interface
36 <P> High level control interface is designed to access preloaded and sorted primitive controls.
38 \section hcontrol_general_overview General overview
40 <P> High level control interface caches the accesses to primitive controls
41 to reduce overhead accessing the real controls in kernel drivers.
50 #include <sys/ioctl.h>
51 #include "control_local.h"
52 #ifdef HAVE_LIBPTHREAD
57 #define NOT_FOUND 1000000000
60 static int snd_hctl_compare_default(const snd_hctl_elem_t *c1,
61 const snd_hctl_elem_t *c2);
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
70 int snd_hctl_open(snd_hctl_t **hctlp, const char *name, int mode)
75 if ((err = snd_ctl_open(&ctl, name, mode)) < 0)
77 err = snd_hctl_open_ctl(hctlp, ctl);
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
89 int snd_hctl_open_ctl(snd_hctl_t **hctlp, snd_ctl_t *ctl)
95 if ((hctl = (snd_hctl_t *)calloc(1, sizeof(snd_hctl_t))) == NULL)
97 INIT_LIST_HEAD(&hctl->elems);
104 * \brief close HCTL handle
105 * \param hctl HCTL handle
106 * \return 0 on success otherwise a negative error code
108 * Closes the specified HCTL handle and frees all associated
111 int snd_hctl_close(snd_hctl_t *hctl)
116 err = snd_ctl_close(hctl->ctl);
123 * \brief get identifier of HCTL handle
124 * \param hctl HCTL handle
125 * \return ascii identifier of HCTL handle
127 * Returns the ASCII identifier of given HCTL handle. It's the same
128 * identifier specified in snd_hctl_open().
130 const char *snd_hctl_name(snd_hctl_t *hctl)
133 return snd_ctl_name(hctl->ctl);
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
142 int snd_hctl_nonblock(snd_hctl_t *hctl, int nonblock)
145 return snd_ctl_nonblock(hctl->ctl, nonblock);
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
155 * A signal is raised when a change happens.
157 int snd_hctl_async(snd_hctl_t *hctl, int sig, pid_t pid)
160 return snd_ctl_async(hctl->ctl, sig, pid);
164 * \brief get count of poll descriptors for HCTL handle
165 * \param hctl HCTL handle
166 * \return count of poll descriptors
168 int snd_hctl_poll_descriptors_count(snd_hctl_t *hctl)
171 return snd_ctl_poll_descriptors_count(hctl->ctl);
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
181 int snd_hctl_poll_descriptors(snd_hctl_t *hctl, struct pollfd *pfds, unsigned int space)
184 return snd_ctl_poll_descriptors(hctl->ctl, pfds, space);
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
195 int snd_hctl_poll_descriptors_revents(snd_hctl_t *hctl, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
198 return snd_ctl_poll_descriptors_revents(hctl->ctl, pfds, nfds, revents);
201 static int snd_hctl_throw_event(snd_hctl_t *hctl, unsigned int mask,
202 snd_hctl_elem_t *elem)
205 return hctl->callback(hctl, mask, elem);
209 static int snd_hctl_elem_throw_event(snd_hctl_elem_t *elem,
213 return elem->callback(elem, mask);
217 static int snd_hctl_compare_mixer_priority_lookup(const char **name, const char * const *names, int coef)
221 for (res = 0; *names; names++, res += coef) {
222 if (!strncmp(*name, *names, strlen(*names))) {
223 *name += strlen(*names);
232 static int get_compare_weight(const snd_ctl_elem_id_t *id)
234 static const char *const names[] = {
270 static const char *const names1[] = {
284 static const char *const names2[] = {
295 const char *name = (char *)id->name, *name1;
298 if ((res = snd_hctl_compare_mixer_priority_lookup((const char **)&name, names, 1000000)) == NOT_FOUND)
302 for (name1 = name; *name1 != '\0'; name1++);
303 for (name1--; name1 != name && *name1 != ' '; name1--);
304 while (name1 != name && *name1 == ' ')
307 for (; name1 != name && *name1 != ' '; name1--);
309 if ((res1 = snd_hctl_compare_mixer_priority_lookup((const char **)&name, names1, 1000)) == NOT_FOUND)
315 if ((res1 = snd_hctl_compare_mixer_priority_lookup((const char **)&name, names2, 1)) == NOT_FOUND)
320 static int _snd_hctl_find_elem(snd_hctl_t *hctl, const snd_ctl_elem_id_t *id, int *dir)
327 assert(hctl->compare);
329 el.compare_weight = get_compare_weight(id);
334 c = hctl->compare(&el, hctl->pelems[idx]);
346 static int snd_hctl_elem_add(snd_hctl_t *hctl, snd_hctl_elem_t *elem)
350 elem->compare_weight = get_compare_weight(&elem->id);
351 if (hctl->count == hctl->alloc) {
354 h = realloc(hctl->pelems, sizeof(*h) * hctl->alloc);
361 if (hctl->count == 0) {
362 list_add_tail(&elem->list, &hctl->elems);
363 hctl->pelems[0] = elem;
365 idx = _snd_hctl_find_elem(hctl, &elem->id, &dir);
368 list_add(&elem->list, &hctl->pelems[idx]->list);
371 list_add_tail(&elem->list, &hctl->pelems[idx]->list);
373 memmove(hctl->pelems + idx + 1,
375 (hctl->count - idx) * sizeof(snd_hctl_elem_t *));
376 hctl->pelems[idx] = elem;
379 return snd_hctl_throw_event(hctl, SNDRV_CTL_EVENT_MASK_ADD, elem);
382 static void snd_hctl_elem_remove(snd_hctl_t *hctl, unsigned int idx)
384 snd_hctl_elem_t *elem = hctl->pelems[idx];
386 snd_hctl_elem_throw_event(elem, SNDRV_CTL_EVENT_MASK_REMOVE);
387 list_del(&elem->list);
390 m = hctl->count - idx;
392 memmove(hctl->pelems + idx,
393 hctl->pelems + idx + 1,
394 m * sizeof(snd_hctl_elem_t *));
398 * \brief free HCTL loaded elements
399 * \param hctl HCTL handle
400 * \return 0 on success otherwise a negative error code
402 int snd_hctl_free(snd_hctl_t *hctl)
404 while (hctl->count > 0)
405 snd_hctl_elem_remove(hctl, hctl->count - 1);
409 INIT_LIST_HEAD(&hctl->elems);
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);
419 static void snd_hctl_sort(snd_hctl_t *hctl)
422 #ifdef HAVE_LIBPTHREAD
423 static pthread_mutex_t sync_lock = PTHREAD_MUTEX_INITIALIZER;
427 assert(hctl->compare);
428 INIT_LIST_HEAD(&hctl->elems);
430 #ifdef HAVE_LIBPTHREAD
431 pthread_mutex_lock(&sync_lock);
434 qsort(hctl->pelems, hctl->count, sizeof(*hctl->pelems), hctl_compare);
435 #ifdef HAVE_LIBPTHREAD
436 pthread_mutex_unlock(&sync_lock);
438 for (k = 0; k < hctl->count; k++)
439 list_add_tail(&hctl->pelems[k]->list, &hctl->elems);
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
448 int snd_hctl_set_compare(snd_hctl_t *hctl, snd_hctl_compare_t compare)
451 hctl->compare = compare == NULL ? snd_hctl_compare_default : compare;
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
462 int snd_hctl_compare_fast(const snd_hctl_elem_t *c1,
463 const snd_hctl_elem_t *c2)
465 return c1->id.numid - c2->id.numid;
468 static int snd_hctl_compare_default(const snd_hctl_elem_t *c1,
469 const snd_hctl_elem_t *c2)
473 d = c1->id.iface - c2->id.iface;
476 if (c1->id.iface == SNDRV_CTL_ELEM_IFACE_MIXER) {
477 d = c1->compare_weight - c2->compare_weight;
481 d = c1->id.device - c2->id.device;
484 d = c1->id.subdevice - c2->id.subdevice;
487 res = strcmp((const char *)c1->id.name, (const char *)c2->id.name);
490 return c1->id.index - c2->id.index;
494 * \brief get first element for an HCTL
495 * \param hctl HCTL handle
496 * \return pointer to first element
498 snd_hctl_elem_t *snd_hctl_first_elem(snd_hctl_t *hctl)
501 if (list_empty(&hctl->elems))
503 return list_entry(hctl->elems.next, snd_hctl_elem_t, list);
507 * \brief get last element for an HCTL
508 * \param hctl HCTL handle
509 * \return pointer to last element
511 snd_hctl_elem_t *snd_hctl_last_elem(snd_hctl_t *hctl)
514 if (list_empty(&hctl->elems))
516 return list_entry(hctl->elems.prev, snd_hctl_elem_t, list);
520 * \brief get next HCTL element
521 * \param elem HCTL element
522 * \return pointer to next element
524 snd_hctl_elem_t *snd_hctl_elem_next(snd_hctl_elem_t *elem)
527 if (elem->list.next == &elem->hctl->elems)
529 return list_entry(elem->list.next, snd_hctl_elem_t, list);
533 * \brief get previous HCTL element
534 * \param elem HCTL element
535 * \return pointer to previous element
537 snd_hctl_elem_t *snd_hctl_elem_prev(snd_hctl_elem_t *elem)
540 if (elem->list.prev == &elem->hctl->elems)
542 return list_entry(elem->list.prev, snd_hctl_elem_t, list);
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
551 snd_hctl_elem_t *snd_hctl_find_elem(snd_hctl_t *hctl, const snd_ctl_elem_id_t *id)
554 int res = _snd_hctl_find_elem(hctl, id, &dir);
555 if (res < 0 || dir != 0)
557 return hctl->pelems[res];
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
565 int snd_hctl_load(snd_hctl_t *hctl)
567 snd_ctl_elem_list_t list;
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)
578 while (list.count != list.used) {
579 err = snd_ctl_elem_list_alloc_space(&list, list.count);
582 if ((err = snd_ctl_elem_list(hctl->ctl, &list)) < 0)
585 if (hctl->alloc < list.count) {
586 hctl->alloc = list.count;
588 hctl->pelems = malloc(hctl->alloc * sizeof(*hctl->pelems));
594 for (idx = 0; idx < list.count; idx++) {
595 snd_hctl_elem_t *elem;
596 elem = calloc(1, sizeof(snd_hctl_elem_t));
602 elem->id = list.pids[idx];
604 elem->compare_weight = get_compare_weight(&elem->id);
605 hctl->pelems[idx] = elem;
606 list_add_tail(&elem->list, &hctl->elems);
610 hctl->compare = snd_hctl_compare_default;
612 for (idx = 0; idx < hctl->count; idx++) {
613 int res = snd_hctl_throw_event(hctl, SNDRV_CTL_EVENT_MASK_ADD,
618 err = snd_ctl_subscribe_events(hctl->ctl, 1);
625 * \brief Set callback function for an HCTL
626 * \param hctl HCTL handle
627 * \param callback callback function
629 void snd_hctl_set_callback(snd_hctl_t *hctl, snd_hctl_callback_t callback)
632 hctl->callback = callback;
636 * \brief Set callback private value for an HCTL
637 * \param hctl HCTL handle
638 * \param callback_private callback private value
640 void snd_hctl_set_callback_private(snd_hctl_t *hctl, void *callback_private)
643 hctl->callback_private = callback_private;
647 * \brief Get callback private value for an HCTL
648 * \param hctl HCTL handle
649 * \return callback private value
651 void *snd_hctl_get_callback_private(snd_hctl_t *hctl)
654 return hctl->callback_private;
658 * \brief Get number of loaded elements for an HCTL
659 * \param hctl HCTL handle
660 * \return elements count
662 unsigned int snd_hctl_get_count(snd_hctl_t *hctl)
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
675 int snd_hctl_wait(snd_hctl_t *hctl, int timeout)
678 unsigned short *revents;
679 int i, npfds, pollio, err, err_poll;
681 npfds = snd_hctl_poll_descriptors_count(hctl);
682 if (npfds <= 0 || npfds >= 16) {
683 SNDERR("Invalid poll_fds %d\n", npfds);
686 pfd = alloca(sizeof(*pfd) * npfds);
687 revents = alloca(sizeof(*revents) * npfds);
688 err = snd_hctl_poll_descriptors(hctl, pfd, npfds);
692 SNDMSG("invalid poll descriptors %d\n", err);
697 err_poll = poll(pfd, npfds, timeout);
705 err = snd_hctl_poll_descriptors_revents(hctl, pfd, npfds, revents);
708 for (i = 0; i < npfds; i++) {
709 if (revents[i] & (POLLERR | POLLNVAL))
711 if ((revents[i] & (POLLIN | POLLOUT)) == 0)
716 return err_poll > 0 ? 1 : 0;
720 * \brief Get a ctl handle associated to the given hctl handle
721 * \param hctl HCTL handle
722 * \return a ctl handle otherwise NULL
724 snd_ctl_t *snd_hctl_ctl(snd_hctl_t *hctl)
729 static int snd_hctl_handle_event(snd_hctl_t *hctl, snd_ctl_event_t *event)
731 snd_hctl_elem_t *elem;
736 switch (event->type) {
737 case SND_CTL_EVENT_ELEM:
742 if (event->data.elem.mask == SNDRV_CTL_EVENT_MASK_REMOVE) {
744 res = _snd_hctl_find_elem(hctl, &event->data.elem.id, &dir);
745 assert(res >= 0 && dir == 0);
746 if (res < 0 || dir != 0)
748 snd_hctl_elem_remove(hctl, (unsigned int) res);
751 if (event->data.elem.mask & SNDRV_CTL_EVENT_MASK_ADD) {
752 elem = calloc(1, sizeof(snd_hctl_elem_t));
755 elem->id = event->data.elem.id;
757 res = snd_hctl_elem_add(hctl, elem);
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);
767 res = snd_hctl_elem_throw_event(elem, event->data.elem.mask &
768 (SNDRV_CTL_EVENT_MASK_VALUE |
769 SNDRV_CTL_EVENT_MASK_INFO));
777 * \brief Handle pending HCTL events invoking callbacks
778 * \param hctl HCTL handle
779 * \return 0 otherwise a negative error code on failure
781 int snd_hctl_handle_events(snd_hctl_t *hctl)
783 snd_ctl_event_t event;
785 unsigned int count = 0;
789 while ((res = snd_ctl_read(hctl->ctl, &event)) != 0 &&
793 res = snd_hctl_handle_event(hctl, &event);
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
807 int snd_hctl_elem_info(snd_hctl_elem_t *elem, snd_ctl_elem_info_t *info)
813 return snd_ctl_elem_info(elem->hctl->ctl, info);
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
822 int snd_hctl_elem_read(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value)
827 value->id = elem->id;
828 return snd_ctl_elem_read(elem->hctl->ctl, value);
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
839 int snd_hctl_elem_write(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value)
844 value->id = elem->id;
845 return snd_ctl_elem_write(elem->hctl->ctl, value);
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
855 int snd_hctl_elem_tlv_read(snd_hctl_elem_t *elem, unsigned int *tlv, unsigned int tlv_size)
859 assert(tlv_size >= 12);
860 return snd_ctl_elem_tlv_read(elem->hctl->ctl, &elem->id, tlv, tlv_size);
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
871 int snd_hctl_elem_tlv_write(snd_hctl_elem_t *elem, const unsigned int *tlv)
876 return snd_ctl_elem_tlv_write(elem->hctl->ctl, &elem->id, tlv);
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
887 int snd_hctl_elem_tlv_command(snd_hctl_elem_t *elem, const unsigned int *tlv)
892 return snd_ctl_elem_tlv_command(elem->hctl->ctl, &elem->id, tlv);
896 * \brief Get HCTL handle for an HCTL element
897 * \param elem HCTL element
898 * \return HCTL handle
900 snd_hctl_t *snd_hctl_elem_get_hctl(snd_hctl_elem_t *elem)
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
911 void snd_hctl_elem_get_id(const snd_hctl_elem_t *obj, snd_ctl_elem_id_t *ptr)
918 * \brief Get element numeric identifier of a CTL element id/value
919 * \param obj CTL element id/value
920 * \return element numeric identifier
922 unsigned int snd_hctl_elem_get_numid(const snd_hctl_elem_t *obj)
925 return obj->id.numid;
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
933 snd_ctl_elem_iface_t snd_hctl_elem_get_interface(const snd_hctl_elem_t *obj)
936 return obj->id.iface;
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
944 unsigned int snd_hctl_elem_get_device(const snd_hctl_elem_t *obj)
947 return obj->id.device;
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
955 unsigned int snd_hctl_elem_get_subdevice(const snd_hctl_elem_t *obj)
958 return obj->id.subdevice;
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
966 const char *snd_hctl_elem_get_name(const snd_hctl_elem_t *obj)
969 return (const char *)obj->id.name;
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
977 unsigned int snd_hctl_elem_get_index(const snd_hctl_elem_t *obj)
980 return obj->id.index;
984 * \brief Set callback function for an HCTL element
985 * \param obj HCTL element
986 * \param val callback function
988 void snd_hctl_elem_set_callback(snd_hctl_elem_t *obj, snd_hctl_elem_callback_t val)
995 * \brief Set callback private value for an HCTL element
996 * \param obj HCTL element
997 * \param val callback private value
999 void snd_hctl_elem_set_callback_private(snd_hctl_elem_t *obj, void * val)
1002 obj->callback_private = val;
1006 * \brief Get callback private value for an HCTL element
1007 * \param obj HCTL element
1008 * \return callback private value
1010 void * snd_hctl_elem_get_callback_private(const snd_hctl_elem_t *obj)
1013 return obj->callback_private;