From 31e403ce9ded4b3f197901324448ae5f078cbcdc Mon Sep 17 00:00:00 2001 From: Abramo Bagnara Date: Wed, 6 Jun 2001 11:49:52 +0000 Subject: [PATCH] More work on pcm_hooks --- doc/asoundrc.doc | 9 +- include/pcm.h | 8 +- src/conf.c | 2 +- src/pcm/pcm_hooks.c | 721 ++++++++++++++++++++++++++++++++++++++++++-- src/pcm/pcm_local.h | 1 + src/pcm/pcm_multi.c | 31 +- src/pcm/pcm_route.c | 13 +- src/pcm/pcm_share.c | 8 +- 8 files changed, 728 insertions(+), 65 deletions(-) diff --git a/doc/asoundrc.doc b/doc/asoundrc.doc index e2f8c293..3cc0afaf 100644 --- a/doc/asoundrc.doc +++ b/doc/asoundrc.doc @@ -42,10 +42,15 @@ hook_args.NAME { ... # Arbitrary arguments } -# PCM hook definition -pcm_hook.NAME { +# PCM hook type +pcm_hook_type.NAME { [lib STR] # Library file (default libasound.so) [install STR] # Install function (default _snd_pcm_hook_NAME_install) +} + +# PCM hook definition +pcm_hook.NAME { + type STR # PCM Hook type (see pcm_hook_type) [args STR] # Arguments for install function (see hook_args) # or [args { }] # Arguments for install function diff --git a/include/pcm.h b/include/pcm.h index 2f537f22..a54dc473 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -721,10 +721,10 @@ void snd_pcm_info_set_subdevice(snd_pcm_info_t *obj, unsigned int val); void snd_pcm_info_set_stream(snd_pcm_info_t *obj, snd_pcm_stream_t val); typedef enum _snd_pcm_hook_type { - SND_PCM_HOOK_HW_PARAMS, - SND_PCM_HOOK_HW_FREE, - SND_PCM_HOOK_CLOSE, - SND_PCM_HOOK_LAST = SND_PCM_HOOK_CLOSE, + SND_PCM_HOOK_TYPE_HW_PARAMS, + SND_PCM_HOOK_TYPE_HW_FREE, + SND_PCM_HOOK_TYPE_CLOSE, + SND_PCM_HOOK_TYPE_LAST = SND_PCM_HOOK_TYPE_CLOSE, } snd_pcm_hook_type_t; typedef struct _snd_pcm_hook snd_pcm_hook_t; diff --git a/src/conf.c b/src/conf.c index 17f3304e..b09a7f71 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1610,7 +1610,7 @@ static int load_defaults(snd_config_t *subs, snd_config_t *defs) return 0; } -static int safe_strtol(const char *str, long *val) +int safe_strtol(const char *str, long *val) { char *end; long v; diff --git a/src/pcm/pcm_hooks.c b/src/pcm/pcm_hooks.c index 0fd19651..20526e8f 100644 --- a/src/pcm/pcm_hooks.c +++ b/src/pcm/pcm_hooks.c @@ -33,7 +33,7 @@ struct _snd_pcm_hook { typedef struct { snd_pcm_t *slave; int close_slave; - struct list_head hooks[SND_PCM_HOOK_LAST + 1]; + struct list_head hooks[SND_PCM_HOOK_TYPE_LAST + 1]; } snd_pcm_hooks_t; static int snd_pcm_hooks_close(snd_pcm_t *pcm) @@ -47,13 +47,13 @@ static int snd_pcm_hooks_close(snd_pcm_t *pcm) if (err < 0) return err; } - list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_CLOSE]) { + list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_TYPE_CLOSE]) { snd_pcm_hook_t *hook = list_entry(pos, snd_pcm_hook_t, list); err = hook->func(hook); if (err < 0) return err; } - for (k = 0; k <= SND_PCM_HOOK_LAST; ++k) { + for (k = 0; k <= SND_PCM_HOOK_TYPE_LAST; ++k) { struct list_head *hooks = &h->hooks[k]; while (!list_empty(hooks)) { snd_pcm_hook_t *hook; @@ -201,7 +201,7 @@ static int snd_pcm_hooks_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) int err = snd_pcm_hw_params(h->slave, params); if (err < 0) return err; - list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_HW_PARAMS]) { + list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_TYPE_HW_PARAMS]) { snd_pcm_hook_t *hook = list_entry(pos, snd_pcm_hook_t, list); err = hook->func(hook); if (err < 0) @@ -217,7 +217,7 @@ static int snd_pcm_hooks_hw_free(snd_pcm_t *pcm) int err = snd_pcm_hw_free(h->slave); if (err < 0) return err; - list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_HW_FREE]) { + list_for_each_safe(pos, next, &h->hooks[SND_PCM_HOOK_TYPE_HW_FREE]) { snd_pcm_hook_t *hook = list_entry(pos, snd_pcm_hook_t, list); err = hook->func(hook); if (err < 0) @@ -299,7 +299,7 @@ int snd_pcm_hooks_open(snd_pcm_t **pcmp, const char *name, snd_pcm_t *slave, int return -ENOMEM; h->slave = slave; h->close_slave = close_slave; - for (k = 0; k <= SND_PCM_HOOK_LAST; ++k) { + for (k = 0; k <= SND_PCM_HOOK_TYPE_LAST; ++k) { INIT_LIST_HEAD(&h->hooks[k]); } pcm = calloc(1, sizeof(snd_pcm_t)); @@ -331,7 +331,7 @@ int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *conf) char buf[256]; const char *str; const char *lib = NULL, *install = NULL; - snd_config_t *args = NULL; + snd_config_t *type = NULL, *args = NULL; snd_config_iterator_t i, next; int (*install_func)(snd_pcm_t *pcm, snd_config_t *args); void *h; @@ -351,20 +351,8 @@ int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *conf) const char *id = snd_config_get_id(n); if (strcmp(id, "comment") == 0) continue; - if (strcmp(id, "lib") == 0) { - err = snd_config_get_string(n, &lib); - if (err < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - continue; - } - if (strcmp(id, "install") == 0) { - err = snd_config_get_string(n, &install); - if (err < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } + if (strcmp(id, "type") == 0) { + type = n; continue; } if (strcmp(id, "args") == 0) { @@ -374,20 +362,59 @@ int snd_pcm_hook_add_conf(snd_pcm_t *pcm, snd_config_t *conf) SNDERR("Unknown field %s", id); return -EINVAL; } - if (args && snd_config_get_string(args, &str) >= 0) { - err = snd_config_search_alias(snd_config, "hook_args", str, &args); - if (err < 0) { - SNDERR("unknown hook_args %s", str); - return err; + if (!type) { + SNDERR("type is not defined"); + return -EINVAL; + } + err = snd_config_get_string(type, &str); + if (err < 0) { + SNDERR("Invalid type for %s", snd_config_get_id(type)); + return err; + } + err = snd_config_search_alias(snd_config, "pcm_hook_type", str, &type); + if (err >= 0) { + if (snd_config_get_type(type) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("Invalid type for PCM type %s definition", str); + return -EINVAL; + } + snd_config_for_each(i, next, type) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id = snd_config_get_id(n); + if (strcmp(id, "comment") == 0) + continue; + if (strcmp(id, "lib") == 0) { + err = snd_config_get_string(n, &lib); + if (err < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + continue; + } + if (strcmp(id, "install") == 0) { + err = snd_config_get_string(n, &install); + if (err < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + continue; + } + SNDERR("Unknown field %s", id); + return -EINVAL; } } if (!install) { install = buf; - snprintf(buf, sizeof(buf), "_snd_pcm_hook_%s_install", - snd_config_get_id(conf)); + snprintf(buf, sizeof(buf), "_snd_pcm_hook_%s_install", str); } if (!lib) lib = ALSA_LIB; + if (args && snd_config_get_string(args, &str) >= 0) { + err = snd_config_search_alias(snd_config, "hook_args", str, &args); + if (err < 0) { + SNDERR("unknown hook_args %s", str); + return err; + } + } h = dlopen(lib, RTLD_NOW); if (!h) { SNDERR("Cannot open shared library %s", lib); @@ -534,3 +561,641 @@ int snd_pcm_hook_remove(snd_pcm_hook_t *hook) return 0; } +typedef struct { + unsigned int lock: 1; + unsigned int preserve: 1; + snd_ctl_elem_id_t *id; + snd_ctl_elem_info_t *info; + snd_ctl_elem_value_t *val; + snd_ctl_elem_value_t *mask; + snd_ctl_elem_value_t *old; + struct list_head list; +} snd_pcm_hook_ctl_elem_t; + +typedef struct { + snd_ctl_t *ctl; + struct list_head elems; +} snd_pcm_hook_ctl_elems_t; + +static int free_elems(snd_pcm_hook_ctl_elems_t *h) +{ + int err; + while (!list_empty(&h->elems)) { + snd_pcm_hook_ctl_elem_t *elem = list_entry(h->elems.next, snd_pcm_hook_ctl_elem_t, list); + snd_ctl_elem_id_free(elem->id); + snd_ctl_elem_info_free(elem->info); + snd_ctl_elem_value_free(elem->val); + snd_ctl_elem_value_free(elem->mask); + snd_ctl_elem_value_free(elem->old); + list_del(&elem->list); + free(elem); + } + err = snd_ctl_close(h->ctl); + if (err < 0) + return err; + free(h); + return 0; +} + +static int snd_pcm_hook_ctl_elems_hw_params(snd_pcm_hook_t *hook) +{ + snd_pcm_hook_ctl_elems_t *h = snd_pcm_hook_get_private(hook); + struct list_head *pos; + int err; + unsigned int k; + list_for_each(pos, &h->elems) { + snd_pcm_hook_ctl_elem_t *elem = list_entry(pos, snd_pcm_hook_ctl_elem_t, list); + unsigned int count; + snd_ctl_elem_type_t type; + if (elem->lock) { + err = snd_ctl_elem_lock(h->ctl, elem->id); + if (err < 0) { + SNDERR("Cannot lock ctl elem"); + return err; + } + } + err = snd_ctl_elem_read(h->ctl, elem->old); + if (err < 0) { + SNDERR("Cannot read ctl elem"); + return err; + } + count = snd_ctl_elem_info_get_count(elem->info); + type = snd_ctl_elem_info_get_type(elem->info); + switch (type) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + for (k = 0; k < count; ++k) { + int old, val, mask; + old = snd_ctl_elem_value_get_boolean(elem->old, k); + mask = snd_ctl_elem_value_get_boolean(elem->mask, k); + old &= ~mask; + if (old) { + val = snd_ctl_elem_value_get_boolean(elem->val, k); + val |= old; + snd_ctl_elem_value_set_boolean(elem->val, k, val); + } + } + break; + case SND_CTL_ELEM_TYPE_INTEGER: + for (k = 0; k < count; ++k) { + long old, val, mask; + old = snd_ctl_elem_value_get_integer(elem->old, k); + mask = snd_ctl_elem_value_get_integer(elem->mask, k); + old &= ~mask; + if (old) { + val = snd_ctl_elem_value_get_integer(elem->val, k); + val |= old; + snd_ctl_elem_value_set_integer(elem->val, k, val); + } + } + break; + case SND_CTL_ELEM_TYPE_ENUMERATED: + for (k = 0; k < count; ++k) { + unsigned int old, val, mask; + old = snd_ctl_elem_value_get_enumerated(elem->old, k); + mask = snd_ctl_elem_value_get_enumerated(elem->mask, k); + old &= ~mask; + if (old) { + val = snd_ctl_elem_value_get_enumerated(elem->val, k); + val |= old; + snd_ctl_elem_value_set_enumerated(elem->val, k, val); + } + } + break; + case SND_CTL_ELEM_TYPE_IEC958: + count = sizeof(snd_aes_iec958_t); + /* Fall through */ + case SND_CTL_ELEM_TYPE_BYTES: + for (k = 0; k < count; ++k) { + unsigned char old, val, mask; + old = snd_ctl_elem_value_get_byte(elem->old, k); + mask = snd_ctl_elem_value_get_byte(elem->mask, k); + old &= ~mask; + if (old) { + val = snd_ctl_elem_value_get_byte(elem->val, k); + val |= old; + snd_ctl_elem_value_set_byte(elem->val, k, val); + } + } + break; + default: + assert(0); + break; + } + err = snd_ctl_elem_write(h->ctl, elem->val); + if (err < 0) { + SNDERR("Cannot write ctl elem"); + return err; + } + } + return 0; +} + +static int snd_pcm_hook_ctl_elems_hw_free(snd_pcm_hook_t *hook) +{ + snd_pcm_hook_ctl_elems_t *h = snd_pcm_hook_get_private(hook); + struct list_head *pos; + int err; + list_for_each(pos, &h->elems) { + snd_pcm_hook_ctl_elem_t *elem = list_entry(pos, snd_pcm_hook_ctl_elem_t, list); + if (elem->lock) { + err = snd_ctl_elem_unlock(h->ctl, elem->id); + if (err < 0) { + SNDERR("Cannot unlock ctl elem"); + return err; + } + } + if (elem->preserve) { + err = snd_ctl_elem_write(h->ctl, elem->old); + if (err < 0) { + SNDERR("Cannot restore ctl elem"); + return err; + } + } + } + return 0; +} + +static int snd_pcm_hook_ctl_elems_close(snd_pcm_hook_t *hook) +{ + snd_pcm_hook_ctl_elems_t *h = snd_pcm_hook_get_private(hook); + return free_elems(h); +} + +int snd_config_get_iface(snd_config_t *conf) +{ + long v; + const char *str; + int err; + snd_ctl_elem_iface_t idx; + err = snd_config_get_integer(conf, &v); + if (err >= 0) { + if (v < 0 || v > SND_CTL_ELEM_IFACE_LAST) { + _invalid_value: + SNDERR("Invalid value for %s", snd_config_get_id(conf)); + return -EINVAL; + } + return v; + } + err = snd_config_get_string(conf, &str); + if (err < 0) { + SNDERR("Invalid type for %s", snd_config_get_id(conf)); + return -EINVAL; + } + for (idx = 0; idx <= SND_CTL_ELEM_IFACE_LAST; idx++) { + if (strcasecmp(snd_ctl_elem_iface_name(idx), str) == 0) + return idx; + } + goto _invalid_value; +} + +int snd_config_get_bool(snd_config_t *conf) +{ + long v; + const char *str; + int err; + unsigned int k; + static struct { + const char *str; + int val; + } b[] = { + { "false", 0 }, + { "true", 1 }, + { "no", 0 }, + { "yes", 1 }, + { "off", 0 }, + { "on", 1 }, + }; + err = snd_config_get_integer(conf, &v); + if (err >= 0) { + if (v < 0 || v > 1) { + _invalid_value: + SNDERR("Invalid value for %s", snd_config_get_id(conf)); + return -EINVAL; + } + return v; + } + err = snd_config_get_string(conf, &str); + if (err < 0) { + SNDERR("Invalid type for %s", snd_config_get_id(conf)); + return -EINVAL; + } + for (k = 0; k < sizeof(b) / sizeof(*b); k++) { + if (strcasecmp(b[k].str, str) == 0) + return b[k].val; + } + goto _invalid_value; +} + +int snd_config_get_ctl_elem_enumerated(snd_config_t *n, snd_ctl_t *ctl, + snd_ctl_elem_info_t *info) +{ + const char *str; + long val; + unsigned int idx, items; + switch (snd_enum_to_int(snd_config_get_type(n))) { + case SND_CONFIG_TYPE_INTEGER: + snd_config_get_integer(n, &val); + return val; + case SND_CONFIG_TYPE_STRING: + snd_config_get_string(n, &str); + break; + default: + return -1; + } + items = snd_ctl_elem_info_get_items(info); + for (idx = 0; idx < items; idx++) { + int err; + snd_ctl_elem_info_set_item(info, idx); + err = snd_ctl_elem_info(ctl, info); + if (err < 0) { + SNDERR("Cannot obtain info for CTL elem"); + return err; + } + if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0) + return idx; + } + return -1; +} + +int snd_config_get_ctl_elem_value(snd_config_t *conf, + snd_ctl_t *ctl, + snd_ctl_elem_value_t *val, + snd_ctl_elem_value_t *mask, + snd_ctl_elem_info_t *info) +{ + int err; + snd_config_iterator_t i, next; + snd_ctl_elem_id_t *id; + snd_ctl_elem_type_t type; + unsigned int count; + long v; + long idx; + snd_ctl_elem_id_alloca(&id); + snd_ctl_elem_value_get_id(val, id); + count = snd_ctl_elem_info_get_count(info); + type = snd_ctl_elem_info_get_type(info); + if (count == 1) { + switch (snd_enum_to_int(type)) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + v = snd_config_get_bool(conf); + if (v >= 0) { + snd_ctl_elem_value_set_boolean(val, 0, v); + if (mask) + snd_ctl_elem_value_set_boolean(mask, 0, 1); + return 0; + } + break; + case SND_CTL_ELEM_TYPE_INTEGER: + err = snd_config_get_integer(conf, &v); + if (err == 0) { + snd_ctl_elem_value_set_integer(val, 0, v); + if (mask) + snd_ctl_elem_value_set_integer(mask, 0, ~0L); + return 0; + } + break; + case SND_CTL_ELEM_TYPE_ENUMERATED: + v = snd_config_get_ctl_elem_enumerated(conf, ctl, info); + if (v >= 0) { + snd_ctl_elem_value_set_enumerated(val, 0, v); + if (mask) + snd_ctl_elem_value_set_enumerated(mask, 0, ~0); + return 0; + } + break; + case SND_CTL_ELEM_TYPE_BYTES: + case SND_CTL_ELEM_TYPE_IEC958: + break; + default: + SNDERR("Unknow control type: %d", snd_enum_to_int(type)); + return -EINVAL; + } + } + switch (snd_enum_to_int(type)) { + case SND_CTL_ELEM_TYPE_IEC958: + count = sizeof(snd_aes_iec958_t); + /* Fall through */ + case SND_CTL_ELEM_TYPE_BYTES: + { + const char *buf; + err = snd_config_get_string(conf, &buf); + if (err >= 0) { + int c1 = 0; + unsigned int len = strlen(buf); + unsigned int idx = 0; + if (len % 2 != 0 || len > count * 2) { + _bad_content: + SNDERR("bad value content\n"); + return -EINVAL; + } + while (*buf) { + int c = *buf++; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'a' && c <= 'f') + c = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + c = c - 'A' + 10; + else { + goto _bad_content; + } + if (idx % 2 == 1) { + snd_ctl_elem_value_set_byte(val, idx / 2, c1 << 4 | c); + if (mask) + snd_ctl_elem_value_set_byte(mask, idx / 2, 0xff); + } else + c1 = c; + idx++; + } + return 0; + } + } + default: + break; + } + if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("bad value type"); + return -EINVAL; + } + + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + err = safe_strtol(snd_config_get_id(n), &idx); + if (err < 0 || idx < 0 || (unsigned int) idx >= count) { + SNDERR("bad value index"); + return -EINVAL; + } + switch (snd_enum_to_int(type)) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + v = snd_config_get_bool(n); + if (v < 0) + goto _bad_content; + snd_ctl_elem_value_set_boolean(val, idx, v); + if (mask) + snd_ctl_elem_value_set_boolean(mask, idx, 1); + break; + case SND_CTL_ELEM_TYPE_INTEGER: + err = snd_config_get_integer(n, &v); + if (err < 0) + goto _bad_content; + snd_ctl_elem_value_set_integer(val, idx, v); + if (mask) + snd_ctl_elem_value_set_integer(mask, idx, ~0L); + break; + case SND_CTL_ELEM_TYPE_ENUMERATED: + v = snd_config_get_ctl_elem_enumerated(n, ctl, info); + if (v < 0) + goto _bad_content; + snd_ctl_elem_value_set_enumerated(val, idx, v); + if (mask) + snd_ctl_elem_value_set_enumerated(mask, idx, ~0); + break; + case SND_CTL_ELEM_TYPE_BYTES: + case SND_CTL_ELEM_TYPE_IEC958: + err = snd_config_get_integer(n, &v); + if (err < 0 || v < 0 || v > 255) + goto _bad_content; + snd_ctl_elem_value_set_byte(val, idx, v); + if (mask) + snd_ctl_elem_value_set_byte(mask, idx, 0xff); + break; + default: + break; + } + } + return 0; +} + +static int add_elem(snd_pcm_hook_ctl_elems_t *h, snd_config_t *conf, snd_pcm_info_t *info) +{ + snd_config_iterator_t i, next; + int iface = SND_CTL_ELEM_IFACE_PCM; + const char *name = NULL; + long index = 0; + long device = -1; + long subdevice = -1; + int lock = 0; + int preserve = 0; + snd_config_t *value = NULL, *mask = NULL; + snd_pcm_hook_ctl_elem_t *elem; + int err; + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id = snd_config_get_id(n); + if (strcmp(id, "comment") == 0) + continue; + if (strcmp(id, "iface") == 0) { + iface = snd_config_get_iface(n); + if (iface < 0) + return iface; + continue; + } + if (strcmp(id, "name") == 0) { + err = snd_config_get_string(n, &name); + if (err < 0) { + _invalid_type: + SNDERR("Invalid type for %s", id); + return err; + } + continue; + } + if (strcmp(id, "index") == 0) { + err = snd_config_get_integer(n, &index); + if (err < 0) + goto _invalid_type; + continue; + } + if (strcmp(id, "device") == 0) { + err = snd_config_get_integer(n, &device); + if (err < 0) + goto _invalid_type; + continue; + } + if (strcmp(id, "subdevice") == 0) { + err = snd_config_get_integer(n, &subdevice); + if (err < 0) + goto _invalid_type; + continue; + } + if (strcmp(id, "lock") == 0) { + lock = snd_config_get_bool(n); + if (lock < 0) { + err = lock; + _invalid_value: + SNDERR("Invalid value for %s", id); + return err; + } + continue; + } + if (strcmp(id, "preserve") == 0) { + preserve = snd_config_get_bool(n); + if (preserve < 0) { + err = preserve; + goto _invalid_value; + } + continue; + } + if (strcmp(id, "value") == 0) { + value = n; + continue; + } + if (strcmp(id, "mask") == 0) { + mask = n; + continue; + } + SNDERR("Unknown field %s", id); + return -EINVAL; + } + if (!name) { + SNDERR("Missing control name"); + return -EINVAL; + } + if (!value) { + SNDERR("Missing control value"); + return -EINVAL; + } + if (device < 0) { + if (iface == SND_CTL_ELEM_IFACE_PCM) + device = snd_pcm_info_get_device(info); + else + device = 0; + } + if (subdevice < 0) { + if (iface == SND_CTL_ELEM_IFACE_PCM) + subdevice = snd_pcm_info_get_subdevice(info); + else + subdevice = 0; + } + elem = calloc(1, sizeof(*elem)); + if (!elem) + return -ENOMEM; + err = snd_ctl_elem_id_malloc(&elem->id); + if (err < 0) + goto _err; + err = snd_ctl_elem_info_malloc(&elem->info); + if (err < 0) + goto _err; + err = snd_ctl_elem_value_malloc(&elem->val); + if (err < 0) + goto _err; + err = snd_ctl_elem_value_malloc(&elem->mask); + if (err < 0) + goto _err; + err = snd_ctl_elem_value_malloc(&elem->old); + if (err < 0) + goto _err; + elem->lock = lock; + elem->preserve = preserve; + snd_ctl_elem_id_set_interface(elem->id, iface); + snd_ctl_elem_id_set_name(elem->id, name); + snd_ctl_elem_id_set_index(elem->id, index); + snd_ctl_elem_id_set_device(elem->id, device); + snd_ctl_elem_id_set_subdevice(elem->id, subdevice); + snd_ctl_elem_info_set_id(elem->info, elem->id); + err = snd_ctl_elem_info(h->ctl, elem->info); + if (err < 0) { + SNDERR("Cannot obtain info for CTL elem"); + goto _err; + } + snd_ctl_elem_value_set_id(elem->val, elem->id); + snd_ctl_elem_value_set_id(elem->old, elem->id); + if (mask) { + err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, NULL, elem->info); + if (err < 0) + goto _err; + err = snd_config_get_ctl_elem_value(mask, h->ctl, elem->mask, NULL, elem->info); + if (err < 0) + goto _err; + } else { + err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info); + if (err < 0) + goto _err; + } + + err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info); + if (err < 0) + goto _err; + list_add_tail(&elem->list, &h->elems); + return 0; + + _err: + if (elem->id) + snd_ctl_elem_id_free(elem->id); + if (elem->info) + snd_ctl_elem_info_free(elem->info); + if (elem->val) + snd_ctl_elem_value_free(elem->val); + if (elem->mask) + snd_ctl_elem_value_free(elem->mask); + if (elem->old) + snd_ctl_elem_value_free(elem->old); + free(elem); + return err; +} + +int _snd_pcm_hook_ctl_elems_install(snd_pcm_t *pcm, snd_config_t *conf) +{ + int err; + int card; + snd_pcm_info_t *info; + char ctl_name[16]; + snd_ctl_t *ctl; + snd_pcm_hook_ctl_elems_t *h; + snd_config_iterator_t i, next; + snd_pcm_hook_t *h_hw_params = NULL, *h_hw_free = NULL, *h_close = NULL; + assert(conf); + assert(snd_config_get_type(conf) == SND_CONFIG_TYPE_COMPOUND); + snd_pcm_info_alloca(&info); + err = snd_pcm_info(pcm, info); + if (err < 0) + return err; + card = snd_pcm_info_get_card(info); + if (card < 0) { + SNDERR("No card for this PCM"); + return -EINVAL; + } + sprintf(ctl_name, "hw:%d", card); + err = snd_ctl_open(&ctl, ctl_name, 0); + if (err < 0) { + SNDERR("Cannot open CTL %s", ctl_name); + return err; + } + h = calloc(1, sizeof(*h)); + if (!h) { + snd_ctl_close(ctl); + return -ENOMEM; + } + h->ctl = ctl; + INIT_LIST_HEAD(&h->elems); + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + err = add_elem(h, n, info); + if (err < 0) { + free_elems(h); + return err; + } + } + err = snd_pcm_hook_add(&h_hw_params, pcm, SND_PCM_HOOK_TYPE_HW_PARAMS, + snd_pcm_hook_ctl_elems_hw_params, h); + if (err < 0) + goto _err; + err = snd_pcm_hook_add(&h_hw_free, pcm, SND_PCM_HOOK_TYPE_HW_FREE, + snd_pcm_hook_ctl_elems_hw_free, h); + if (err < 0) + goto _err; + err = snd_pcm_hook_add(&h_close, pcm, SND_PCM_HOOK_TYPE_CLOSE, + snd_pcm_hook_ctl_elems_close, h); + if (err < 0) + goto _err; + return 0; + _err: + if (h_hw_params) + snd_pcm_hook_remove(h_hw_params); + if (h_hw_free) + snd_pcm_hook_remove(h_hw_free); + if (h_close) + snd_pcm_hook_remove(h_close); + free_elems(h); + return err; +} + diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index a890708e..5e672f42 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -576,3 +576,4 @@ int snd_pcm_conf_generic_id(const char *id); (1U << SND_PCM_FORMAT_U32_LE) | \ (1U << SND_PCM_FORMAT_U32_BE)) +int safe_strtol(const char *str, long *val); diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index e8e7359e..036dd326 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -79,15 +79,14 @@ static int snd_pcm_multi_async(snd_pcm_t *pcm, int sig, pid_t pid) static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info) { snd_pcm_multi_t *multi = pcm->private_data; - if (multi->slaves_count == 1) - return snd_pcm_info(multi->slaves[0].pcm, info); - memset(info, 0, sizeof(*info)); - info->stream = snd_enum_to_int(pcm->stream); - info->card = -1; - strncpy(info->id, pcm->name, sizeof(info->id)); - strncpy(info->name, pcm->name, sizeof(info->name)); - strncpy(info->subname, pcm->name, sizeof(info->subname)); - info->subdevices_count = 1; + int err, n; + assert(info->subdevice < multi->slaves_count); + n = info->subdevice; + info->subdevice = 0; + err = snd_pcm_info(multi->slaves[n].pcm, info); + if (err < 0) + return err; + info->subdevices_count = multi->slaves_count; return 0; } @@ -698,17 +697,15 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name, snd_config_t *conf, ++slaves_count; } snd_config_for_each(i, inext, bindings) { - int cchannel = -1; - char *p; + long cchannel; snd_config_t *m = snd_config_iterator_entry(i); const char *id = snd_config_get_id(m); - errno = 0; - cchannel = strtol(id, &p, 10); - if (errno || *p || cchannel < 0) { + err = safe_strtol(id, &cchannel); + if (err < 0 || cchannel < 0) { SNDERR("Invalid channel number: %s", id); return -EINVAL; } - if ((unsigned)cchannel >= channels_count) + if ((unsigned long)cchannel >= channels_count) channels_count = cchannel + 1; } if (channels_count == 0) { @@ -745,8 +742,8 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name, snd_config_t *conf, long val; const char *str; const char *id = snd_config_get_id(m); - cchannel = strtol(id, 0, 10); - if (cchannel < 0) { + err = safe_strtol(id, &cchannel); + if (err < 0 || cchannel < 0) { SNDERR("Invalid channel number: %s", id); err = -EINVAL; goto _free; diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c index b4c6efc8..c4aaf5f7 100644 --- a/src/pcm/pcm_route.c +++ b/src/pcm/pcm_route.c @@ -784,16 +784,15 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt int sused = -1; snd_config_iterator_t i, inext; unsigned int k; + int err; for (k = 0; k < tt_csize * tt_ssize; ++k) ttable[k] = 0.0; snd_config_for_each(i, inext, tt) { snd_config_t *in = snd_config_iterator_entry(i); snd_config_iterator_t j, jnext; - char *p; long cchannel; - errno = 0; - cchannel = strtol(snd_config_get_id(in), &p, 10); - if (errno || *p || + err = safe_strtol(snd_config_get_id(in), &cchannel); + if (err < 0 || cchannel < 0 || (unsigned int) cchannel > tt_csize) { SNDERR("Invalid client channel: %s", snd_config_get_id(in)); return -EINVAL; @@ -804,11 +803,9 @@ int snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_entry_t *tt snd_config_t *jnode = snd_config_iterator_entry(j); double value; long schannel; - int err; const char *id = snd_config_get_id(jnode); - errno = 0; - schannel = strtol(id, &p, 10); - if (errno || *p || + err = safe_strtol(id, &schannel); + if (err < 0 || schannel < 0 || (unsigned int) schannel > tt_ssize || (schannels > 0 && schannel >= schannels)) { SNDERR("Invalid slave channel: %s", id); diff --git a/src/pcm/pcm_share.c b/src/pcm/pcm_share.c index ad133f38..d6a1a1b7 100644 --- a/src/pcm/pcm_share.c +++ b/src/pcm/pcm_share.c @@ -1435,12 +1435,10 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, const char *name, snd_config_t *conf, } snd_config_for_each(i, next, bindings) { int cchannel = -1; - char *p; snd_config_t *n = snd_config_iterator_entry(i); const char *id = snd_config_get_id(n); - errno = 0; - cchannel = strtol(id, &p, 10); - if (errno || *p || cchannel < 0) { + err = safe_strtol(id, &cchannel); + if (err < 0 || cchannel < 0) { SNDERR("Invalid client channel in binding: %s", id); return -EINVAL; } @@ -1458,7 +1456,7 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, const char *name, snd_config_t *conf, const char *id = snd_config_get_id(n); long cchannel; long schannel = -1; - cchannel = strtol(id, 0, 10); + cchannel = atoi(id); err = snd_config_get_integer(n, &schannel); if (err < 0) goto _free; -- 2.47.1