2000-07-28 20:21:12 +00:00
|
|
|
/*
|
|
|
|
|
* Control Interface - highlevel API
|
|
|
|
|
* Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz>
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* 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 <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
#include <assert.h>
|
2000-08-04 10:24:12 +00:00
|
|
|
#define __USE_GNU
|
|
|
|
|
#include <search.h>
|
2000-07-28 20:21:12 +00:00
|
|
|
#include "asoundlib.h"
|
|
|
|
|
#include "control_local.h"
|
|
|
|
|
|
2000-08-04 10:24:12 +00:00
|
|
|
static void snd_ctl_cfree1(snd_hcontrol_t *hcontrol);
|
2000-07-28 20:21:12 +00:00
|
|
|
|
|
|
|
|
int snd_ctl_cbuild(snd_ctl_t *handle, snd_ctl_csort_t *csort)
|
|
|
|
|
{
|
|
|
|
|
snd_control_list_t list;
|
|
|
|
|
snd_hcontrol_t *hcontrol, *prev;
|
2000-08-04 10:24:12 +00:00
|
|
|
int err;
|
|
|
|
|
unsigned int idx;
|
2000-07-28 20:21:12 +00:00
|
|
|
|
2000-08-04 10:24:12 +00:00
|
|
|
printf("cbuild - start\n");
|
2000-07-28 20:21:12 +00:00
|
|
|
assert(handle != NULL);
|
|
|
|
|
if ((err = snd_ctl_cfree(handle)) < 0)
|
|
|
|
|
return err;
|
2000-08-04 10:24:12 +00:00
|
|
|
if (csort == NULL)
|
|
|
|
|
csort = snd_ctl_csort;
|
2000-07-28 20:21:12 +00:00
|
|
|
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) {
|
|
|
|
|
snd_ctl_cfree(handle);
|
|
|
|
|
free(list.pids);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
hcontrol->id = list.pids[idx];
|
2000-08-04 10:24:12 +00:00
|
|
|
hcontrol->handle = handle;
|
|
|
|
|
if (tsearch(hcontrol, &handle->croot, (__compar_fn_t)csort) == NULL) {
|
|
|
|
|
tdestroy(&handle->croot, (__free_fn_t)snd_ctl_cfree1);
|
|
|
|
|
handle->croot = NULL;
|
2000-07-28 20:21:12 +00:00
|
|
|
}
|
2000-08-04 10:24:12 +00:00
|
|
|
handle->ccount++;
|
2000-07-28 20:21:12 +00:00
|
|
|
}
|
|
|
|
|
if (list.pids != NULL)
|
|
|
|
|
free(list.pids);
|
2000-08-04 10:24:12 +00:00
|
|
|
handle->csort = csort;
|
2000-07-28 20:21:12 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-08-04 10:24:12 +00:00
|
|
|
static void snd_ctl_cfree1(snd_hcontrol_t *hcontrol)
|
2000-07-28 20:21:12 +00:00
|
|
|
{
|
2000-08-04 10:24:12 +00:00
|
|
|
snd_ctl_t *handle;
|
|
|
|
|
|
|
|
|
|
assert(hcontrol != NULL);
|
|
|
|
|
handle = hcontrol->handle;
|
2000-07-28 20:21:12 +00:00
|
|
|
assert(handle != NULL);
|
2000-08-04 10:24:12 +00:00
|
|
|
assert(handle->ccount > 0);
|
2000-07-28 20:21:12 +00:00
|
|
|
if (hcontrol->event_remove)
|
|
|
|
|
hcontrol->event_remove(handle, hcontrol);
|
|
|
|
|
if (hcontrol->private_free)
|
|
|
|
|
hcontrol->private_free(hcontrol->private_data);
|
|
|
|
|
free(hcontrol);
|
2000-08-04 10:24:12 +00:00
|
|
|
handle->ccount--;
|
2000-07-28 20:21:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_ctl_cfree(snd_ctl_t *handle)
|
|
|
|
|
{
|
|
|
|
|
handle->csort = NULL;
|
2000-08-04 10:24:12 +00:00
|
|
|
handle->cerr = 0;
|
|
|
|
|
if (handle->croot != NULL) {
|
|
|
|
|
tdestroy(handle->croot, (__free_fn_t)snd_ctl_cfree1);
|
|
|
|
|
handle->croot = NULL;
|
|
|
|
|
}
|
2000-07-28 20:21:12 +00:00
|
|
|
assert(handle->ccount == 0);
|
2000-08-04 10:24:12 +00:00
|
|
|
return 0;
|
2000-07-28 20:21:12 +00:00
|
|
|
}
|
|
|
|
|
|
2000-08-04 10:24:12 +00:00
|
|
|
int snd_ctl_csort(const snd_hcontrol_t *c1, const snd_hcontrol_t *c2)
|
2000-07-28 20:21:12 +00:00
|
|
|
{
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
res = strcmp(c1->id.name, c2->id.name);
|
|
|
|
|
if (res == 0) {
|
|
|
|
|
if (c1->id.index < c2->id.index)
|
|
|
|
|
return -1;
|
|
|
|
|
if (c1->id.index > c2->id.index)
|
|
|
|
|
return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2000-08-04 10:24:12 +00:00
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void snd_ctl_cresort_action(snd_hcontrol_t *hcontrol, VISIT which, int level)
|
|
|
|
|
{
|
|
|
|
|
snd_ctl_t *handle;
|
|
|
|
|
|
|
|
|
|
level = 0; /* to keep GCC happy */
|
|
|
|
|
assert(hcontrol != NULL);
|
|
|
|
|
handle = hcontrol->handle;
|
|
|
|
|
assert(handle != NULL);
|
|
|
|
|
if (handle->cerr < 0)
|
|
|
|
|
return;
|
|
|
|
|
switch (which) {
|
|
|
|
|
case preorder: break;
|
|
|
|
|
case postorder: break;
|
|
|
|
|
case endorder:
|
|
|
|
|
case leaf:
|
|
|
|
|
if (tsearch(hcontrol, &handle->croot, (__compar_fn_t)handle->csort) == NULL)
|
|
|
|
|
handle->cerr = -ENOMEM;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void snd_ctl_cresort_free(snd_hcontrol_t *hcontrol)
|
|
|
|
|
{
|
|
|
|
|
hcontrol = NULL; /* to keep GCC happy */
|
|
|
|
|
/* nothing */
|
2000-07-28 20:21:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_ctl_cresort(snd_ctl_t *handle, snd_ctl_csort_t *csort)
|
|
|
|
|
{
|
2000-08-04 10:24:12 +00:00
|
|
|
int result;
|
|
|
|
|
snd_ctl_csort_t *csort_old;
|
2000-07-28 20:21:12 +00:00
|
|
|
|
|
|
|
|
assert(handle != NULL && csort != NULL);
|
|
|
|
|
if (handle->ccount == 0)
|
|
|
|
|
return 0;
|
2000-08-04 10:24:12 +00:00
|
|
|
if (handle->cerr < 0)
|
|
|
|
|
return handle->cerr;
|
|
|
|
|
assert(handle->croot_new == NULL);
|
|
|
|
|
csort_old = handle->csort;
|
2000-07-28 20:21:12 +00:00
|
|
|
handle->csort = csort;
|
2000-08-04 10:24:12 +00:00
|
|
|
twalk(handle->croot, (__action_fn_t)snd_ctl_cresort_action);
|
|
|
|
|
if (handle->cerr < 0) {
|
|
|
|
|
result = handle->cerr;
|
|
|
|
|
handle->cerr = 0;
|
|
|
|
|
handle->csort = csort_old;
|
|
|
|
|
tdestroy(handle->croot_new, (__free_fn_t)snd_ctl_cresort_free);
|
|
|
|
|
handle->croot_new = NULL;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
tdestroy(handle->croot, (__free_fn_t)snd_ctl_cresort_free);
|
|
|
|
|
handle->croot = handle->croot_new;
|
|
|
|
|
handle->croot_new = NULL;
|
2000-07-28 20:21:12 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snd_hcontrol_t *snd_ctl_cfind(snd_ctl_t *handle, snd_control_id_t *id)
|
|
|
|
|
{
|
|
|
|
|
assert(handle != NULL);
|
2000-08-04 10:24:12 +00:00
|
|
|
if (handle->croot == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
return (snd_hcontrol_t *)tfind(id, &handle->croot, (__compar_fn_t)handle->csort);
|
2000-07-28 20:21:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_ctl_ccallback_rebuild(snd_ctl_t *handle, snd_ctl_ccallback_rebuild_t *callback, void *private_data)
|
|
|
|
|
{
|
|
|
|
|
assert(handle != NULL);
|
|
|
|
|
handle->callback_rebuild = callback;
|
|
|
|
|
handle->callback_rebuild_private_data = private_data;
|
2000-08-04 10:24:12 +00:00
|
|
|
return 0;
|
2000-07-28 20:21:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_ctl_ccallback_add(snd_ctl_t *handle, snd_ctl_ccallback_add_t *callback, void *private_data)
|
|
|
|
|
{
|
|
|
|
|
assert(handle != NULL);
|
|
|
|
|
handle->callback_add = callback;
|
|
|
|
|
handle->callback_add_private_data = private_data;
|
2000-08-04 10:24:12 +00:00
|
|
|
return 0;
|
2000-07-28 20:21:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void callback_rebuild(snd_ctl_t *handle, void *private_data)
|
|
|
|
|
{
|
2000-08-04 10:24:12 +00:00
|
|
|
private_data = NULL; /* to keep GCC happy */
|
2000-07-28 20:21:12 +00:00
|
|
|
handle->cerr = snd_ctl_cbuild(handle, handle->csort);
|
|
|
|
|
if (handle->cerr >= 0 && handle->callback_rebuild)
|
|
|
|
|
handle->callback_rebuild(handle, handle->callback_rebuild_private_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void callback_change(snd_ctl_t *handle, void *private_data, snd_control_id_t *id)
|
|
|
|
|
{
|
|
|
|
|
snd_hcontrol_t *hcontrol;
|
|
|
|
|
|
2000-08-04 10:24:12 +00:00
|
|
|
private_data = NULL; /* to keep GCC happy */
|
2000-07-28 20:21:12 +00:00
|
|
|
if (handle->cerr < 0)
|
|
|
|
|
return;
|
|
|
|
|
hcontrol = snd_ctl_cfind(handle, id);
|
|
|
|
|
if (hcontrol == NULL) {
|
|
|
|
|
handle->cerr = -ENOENT;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
hcontrol->change = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void callback_value(snd_ctl_t *handle, void *private_data, snd_control_id_t *id)
|
|
|
|
|
{
|
|
|
|
|
snd_hcontrol_t *hcontrol;
|
|
|
|
|
|
2000-08-04 10:24:12 +00:00
|
|
|
private_data = NULL; /* to keep GCC happy */
|
2000-07-28 20:21:12 +00:00
|
|
|
if (handle->cerr < 0)
|
|
|
|
|
return;
|
|
|
|
|
hcontrol = snd_ctl_cfind(handle, id);
|
|
|
|
|
if (hcontrol == NULL) {
|
|
|
|
|
handle->cerr = -ENOENT;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
hcontrol->value = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void callback_add(snd_ctl_t *handle, void *private_data, snd_control_id_t *id)
|
|
|
|
|
{
|
|
|
|
|
snd_hcontrol_t *hcontrol, *icontrol;
|
|
|
|
|
|
2000-08-04 10:24:12 +00:00
|
|
|
private_data = NULL; /* to keep GCC happy */
|
2000-07-28 20:21:12 +00:00
|
|
|
if (handle->cerr < 0)
|
|
|
|
|
return;
|
|
|
|
|
hcontrol = (snd_hcontrol_t *)calloc(1, sizeof(snd_hcontrol_t));
|
|
|
|
|
if (hcontrol == NULL) {
|
|
|
|
|
handle->cerr = -ENOMEM;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
hcontrol->id = *id;
|
2000-08-04 10:24:12 +00:00
|
|
|
hcontrol->handle = handle;
|
|
|
|
|
icontrol = tsearch(hcontrol, &handle->croot, (__compar_fn_t)handle->csort);
|
|
|
|
|
if (icontrol == NULL) {
|
|
|
|
|
free(hcontrol);
|
|
|
|
|
handle->cerr = -ENOMEM;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (icontrol != hcontrol) { /* double hit */
|
|
|
|
|
free(hcontrol);
|
|
|
|
|
return;
|
2000-07-28 20:21:12 +00:00
|
|
|
}
|
|
|
|
|
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, snd_control_id_t *id)
|
|
|
|
|
{
|
|
|
|
|
snd_hcontrol_t *hcontrol;
|
|
|
|
|
|
2000-08-04 10:24:12 +00:00
|
|
|
private_data = NULL; /* to keep GCC happy */
|
2000-07-28 20:21:12 +00:00
|
|
|
if (handle->cerr < 0)
|
|
|
|
|
return;
|
|
|
|
|
hcontrol = snd_ctl_cfind(handle, id);
|
|
|
|
|
if (hcontrol == NULL) {
|
|
|
|
|
handle->cerr = -ENOENT;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2000-08-04 10:24:12 +00:00
|
|
|
if (tdelete(hcontrol, &handle->croot, (__compar_fn_t)handle->csort) != NULL)
|
|
|
|
|
snd_ctl_cfree1(hcontrol);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void snd_ctl_cevent_walk1(snd_hcontrol_t *hcontrol, VISIT which, int level)
|
|
|
|
|
{
|
|
|
|
|
level = 0; /* to keep GCC happy */
|
|
|
|
|
assert(hcontrol != NULL);
|
|
|
|
|
switch (which) {
|
|
|
|
|
case preorder: break;
|
|
|
|
|
case postorder: break;
|
|
|
|
|
case endorder:
|
|
|
|
|
case leaf:
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2000-07-28 20:21:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_ctl_cevent(snd_ctl_t *handle)
|
|
|
|
|
{
|
2000-08-04 10:24:12 +00:00
|
|
|
static snd_ctl_callbacks_t callbacks = {
|
2000-07-28 20:21:12 +00:00
|
|
|
rebuild: callback_rebuild,
|
|
|
|
|
value: callback_value,
|
|
|
|
|
change: callback_change,
|
|
|
|
|
add: callback_add,
|
2000-08-04 10:24:12 +00:00
|
|
|
remove: callback_remove,
|
|
|
|
|
private_data: NULL,
|
|
|
|
|
reserved: { NULL, }
|
2000-07-28 20:21:12 +00:00
|
|
|
};
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
assert(handle != NULL);
|
|
|
|
|
handle->cerr = 0;
|
|
|
|
|
res = snd_ctl_read(handle, &callbacks);
|
|
|
|
|
if (res < 0)
|
|
|
|
|
return res;
|
|
|
|
|
if (handle->cerr < 0)
|
|
|
|
|
return handle->cerr;
|
2000-08-04 10:24:12 +00:00
|
|
|
twalk(handle->croot, (__action_fn_t)snd_ctl_cevent_walk1);
|
2000-07-28 20:21:12 +00:00
|
|
|
return res;
|
|
|
|
|
}
|