/* * Control Interface - highlevel API * Copyright (c) 2000 by Jaroslav Kysela * * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include #include #define __USE_GNU #include #include "asoundlib.h" #include "control_local.h" static void snd_ctl_hfree1(snd_hcontrol_t *hcontrol); int snd_ctl_hbuild(snd_ctl_t *handle, snd_ctl_hsort_t *hsort) { snd_control_list_t list; snd_hcontrol_t *hcontrol, *prev; int err; unsigned int idx; assert(handle != NULL); if ((err = snd_ctl_hfree(handle)) < 0) return err; if (hsort == NULL) hsort = snd_ctl_hsort; memset(&list, 0, sizeof(list)); do { if (list.pids != NULL) free(list.pids); list.controls_offset = 0; list.controls_request = 0; list.controls_count = 0; if ((err = snd_ctl_clist(handle, &list)) < 0) return err; if (list.controls == 0) break; list.pids = (snd_control_id_t *)calloc(list.controls, sizeof(snd_control_id_t)); if (list.pids == NULL) return -ENOMEM; list.controls_request = list.controls; if ((err = snd_ctl_clist(handle, &list)) < 0) return err; } while (list.controls != list.controls_count); for (idx = 0, prev = NULL; idx < list.controls_count; idx++) { hcontrol = (snd_hcontrol_t *)calloc(1, sizeof(snd_hcontrol_t)); if (hcontrol == NULL) goto __nomem; hcontrol->id = list.pids[idx]; hcontrol->handle = handle; if (tsearch(hcontrol, &handle->hroot, (__compar_fn_t)hsort) == NULL) { __nomem: if (handle->hroot != NULL) { tdestroy(handle->hroot, (__free_fn_t)snd_ctl_hfree1); handle->hroot = NULL; } handle->hroot = NULL; if (hcontrol != NULL) free(hcontrol); free(list.pids); return -ENOMEM; } list_add_tail(&hcontrol->list, &handle->hlist); handle->hcount++; } if (list.pids != NULL) free(list.pids); if ((err = snd_ctl_hresort(handle, hsort)) < 0) { tdestroy(handle->hroot, (__free_fn_t)snd_ctl_hfree1); handle->hroot = NULL; } return 0; } static void snd_ctl_hfree1(snd_hcontrol_t *hcontrol) { snd_ctl_t *handle; assert(hcontrol != NULL); handle = hcontrol->handle; assert(handle != NULL); assert(handle->hcount > 0); if (hcontrol->event_remove) hcontrol->event_remove(handle, hcontrol); if (hcontrol->private_free) hcontrol->private_free(hcontrol->private_data); list_del(&hcontrol->list); free(hcontrol); handle->hcount--; } int snd_ctl_hfree(snd_ctl_t *handle) { handle->hsort = NULL; handle->herr = 0; if (handle->hroot != NULL) { tdestroy(handle->hroot, (__free_fn_t)snd_ctl_hfree1); handle->hroot = NULL; } assert(list_empty(&handle->hlist)); assert(handle->hcount == 0); return 0; } int snd_ctl_hsort(const snd_hcontrol_t *c1, const snd_hcontrol_t *c2) { int res; if (c1->id.iface < c2->id.iface) return -1; if (c1->id.iface > c2->id.iface) return 1; if ((res = strcmp(c1->id.name, c2->id.name)) != 0) return res; if (c1->id.index < c2->id.index) return -1; if (c1->id.index > c2->id.index) return 1; return 0; } static void snd_ctl_hresort_free(snd_hcontrol_t *hcontrol ATTRIBUTE_UNUSED) { /* nothing */ } int snd_ctl_hresort(snd_ctl_t *handle, snd_ctl_hsort_t *hsort) { struct list_head *list; snd_hcontrol_t *hcontrol; snd_control_id_t *ids, *pids; int idx; assert(handle != NULL && hsort != NULL); if (handle->hcount == 0) return 0; if (handle->herr < 0) return handle->herr; assert(handle->hroot_new == NULL); ids = pids = (snd_control_id_t *)malloc(sizeof(snd_control_id_t) * handle->hcount); if (ids == NULL) return -ENOMEM; /* first step - update search engine */ list_for_each(list, &handle->hlist) { hcontrol = list_entry(list, snd_hcontrol_t, list); *pids++ = hcontrol->id; if (tsearch(hcontrol, &handle->hroot_new, (__compar_fn_t)hsort) == NULL) { if (handle->hroot_new != NULL) tdestroy(handle->hroot_new, (__free_fn_t)snd_ctl_hresort_free); handle->hroot_new = NULL; free(ids); return -ENOMEM; } } if (handle->hroot != NULL) tdestroy(handle->hroot, (__free_fn_t)snd_ctl_hresort_free); handle->hsort = hsort; handle->hroot = handle->hroot_new; handle->hroot_new = NULL; /* second step - perform qsort and save results */ qsort(ids, handle->hcount, sizeof(snd_control_id_t), (int (*)(const void *, const void *))hsort); INIT_LIST_HEAD(&handle->hlist); for (idx = 0; idx < handle->hcount; idx++) { hcontrol = snd_ctl_hfind(handle, ids + idx); list_add_tail(&hcontrol->list, &handle->hlist); } free(ids); return 0; } snd_hcontrol_t *snd_ctl_hfirst(snd_ctl_t *handle) { assert(handle != NULL); if (list_empty(&handle->hlist)) return NULL; return (snd_hcontrol_t *)list_entry(handle->hlist.next, snd_hcontrol_t, list); } snd_hcontrol_t *snd_ctl_hlast(snd_ctl_t *handle) { assert(handle != NULL); if (list_empty(&handle->hlist)) return NULL; return (snd_hcontrol_t *)list_entry(handle->hlist.prev, snd_hcontrol_t, list); } snd_hcontrol_t *snd_ctl_hnext(snd_ctl_t *handle, snd_hcontrol_t *hcontrol) { assert(handle != NULL && hcontrol != NULL); if (hcontrol->list.next == &handle->hlist) return NULL; return (snd_hcontrol_t *)list_entry(hcontrol->list.next, snd_hcontrol_t, list); } snd_hcontrol_t *snd_ctl_hprev(snd_ctl_t *handle, snd_hcontrol_t *hcontrol) { assert(handle != NULL && hcontrol != NULL); if (hcontrol->list.prev == &handle->hlist) return NULL; return (snd_hcontrol_t *)list_entry(hcontrol->list.prev, snd_hcontrol_t, list); } int snd_ctl_hcount(snd_ctl_t *handle) { assert(handle != NULL); return handle->hcount; } snd_hcontrol_t *snd_ctl_hfind(snd_ctl_t *handle, snd_control_id_t *id) { void *res; assert(handle != NULL); if (handle->hroot == NULL) return NULL; res = tfind(id, &handle->hroot, (__compar_fn_t)handle->hsort); return res == NULL ? NULL : *(snd_hcontrol_t **)res; } int snd_ctl_hlist(snd_ctl_t *handle, snd_hcontrol_list_t *hlist) { struct list_head *list; snd_hcontrol_t *hcontrol; unsigned int idx; assert(hlist != NULL); if (hlist->controls_offset >= (unsigned int)handle->hcount) return -EINVAL; hlist->controls_count = 0; hlist->controls = handle->hcount; if (hlist->controls_request > 0) { if (hlist->pids == NULL) return -EINVAL; idx = 0; list_for_each(list, &handle->hlist) { hcontrol = list_entry(list, snd_hcontrol_t, list); if (idx >= hlist->controls_offset + hlist->controls_request) break; if (idx >= hlist->controls_offset) { hlist->pids[idx] = hcontrol->id; hlist->controls_count++; } idx++; } } return 0; } int snd_ctl_hcallback_rebuild(snd_ctl_t *handle, snd_ctl_hcallback_rebuild_t *callback, void *private_data) { assert(handle != NULL); handle->callback_rebuild = callback; handle->callback_rebuild_private_data = private_data; return 0; } int snd_ctl_hcallback_add(snd_ctl_t *handle, snd_ctl_hcallback_add_t *callback, void *private_data) { assert(handle != NULL); handle->callback_add = callback; handle->callback_add_private_data = private_data; return 0; } static void callback_rebuild(snd_ctl_t *handle, void *private_data ATTRIBUTE_UNUSED) { handle->herr = snd_ctl_hbuild(handle, handle->hsort); if (handle->herr >= 0 && handle->callback_rebuild) handle->callback_rebuild(handle, handle->callback_rebuild_private_data); } static void callback_change(snd_ctl_t *handle, void *private_data ATTRIBUTE_UNUSED, snd_control_id_t *id) { snd_hcontrol_t *hcontrol; if (handle->herr < 0) return; hcontrol = snd_ctl_hfind(handle, id); if (hcontrol == NULL) { handle->herr = -ENOENT; return; } hcontrol->change = 1; } static void callback_value(snd_ctl_t *handle, void *private_data ATTRIBUTE_UNUSED, snd_control_id_t *id) { snd_hcontrol_t *hcontrol; if (handle->herr < 0) return; hcontrol = snd_ctl_hfind(handle, id); if (hcontrol == NULL) { handle->herr = -ENOENT; return; } hcontrol->value = 1; } static void callback_add(snd_ctl_t *handle, void *private_data ATTRIBUTE_UNUSED, snd_control_id_t *id) { snd_hcontrol_t *hcontrol, *icontrol; if (handle->herr < 0) return; hcontrol = (snd_hcontrol_t *)calloc(1, sizeof(snd_hcontrol_t)); if (hcontrol == NULL) { handle->herr = -ENOMEM; return; } hcontrol->id = *id; hcontrol->handle = handle; icontrol = tsearch(hcontrol, &handle->hroot, (__compar_fn_t)handle->hsort); if (icontrol == NULL) { free(hcontrol); handle->herr = -ENOMEM; return; } if (icontrol != hcontrol) { /* double hit */ free(hcontrol); return; } list_add_tail(&hcontrol->list, &handle->hlist); if (handle->callback_add) handle->callback_add(handle, handle->callback_add_private_data, hcontrol); } static void callback_remove(snd_ctl_t *handle, void *private_data ATTRIBUTE_UNUSED, snd_control_id_t *id) { snd_hcontrol_t *hcontrol; if (handle->herr < 0) return; hcontrol = snd_ctl_hfind(handle, id); if (hcontrol == NULL) { handle->herr = -ENOENT; return; } if (tdelete(hcontrol, &handle->hroot, (__compar_fn_t)handle->hsort) != NULL) snd_ctl_hfree1(hcontrol); } int snd_ctl_hevent(snd_ctl_t *handle) { static snd_ctl_callbacks_t callbacks = { rebuild: callback_rebuild, value: callback_value, change: callback_change, add: callback_add, remove: callback_remove, private_data: NULL, reserved: { NULL, } }; struct list_head *list; snd_hcontrol_t *hcontrol; int res; assert(handle != NULL); handle->herr = 0; res = snd_ctl_read(handle, &callbacks); if (res < 0) return res; if (handle->herr < 0) return handle->herr; list_for_each(list, &handle->hlist) { hcontrol = list_entry(list, snd_hcontrol_t, list); if (hcontrol->change && hcontrol->event_change) { hcontrol->event_change(hcontrol->handle, hcontrol); hcontrol->change = 0; } if (hcontrol->value && hcontrol->event_value) { hcontrol->event_value(hcontrol->handle, hcontrol); hcontrol->value = 0; } } return res; }