From: Jaroslav Kysela Date: Tue, 2 Feb 2021 18:26:24 +0000 (+0100) Subject: ucm: add ${find-card} and ${find-device} substitutions X-Git-Tag: v1.2.5~109 X-Git-Url: https://git.alsa-project.org/?a=commitdiff_plain;h=296b067fc75b5da26062d4e64e7a2b4fdf86c75c;p=alsa-lib.git ucm: add ${find-card} and ${find-device} substitutions It may be useful to find a correct card or device through control API information fields. Increase the syntax version to 4. Examples: ${find-card:field=name,regex='HDA Intel'} ${find-device:type=pcm,field=id,regex='HDMI 1$'} ${find-device:ctl=hw:acp,type=pcm,field=id,regex=DMIC} Signed-off-by: Jaroslav Kysela --- diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index e564e839..f99cd5db 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -40,7 +40,7 @@ #include #include "use-case.h" -#define SYNTAX_VERSION_MAX 3 +#define SYNTAX_VERSION_MAX 4 #define MAX_CARD_SHORT_NAME 32 #define MAX_CARD_LONG_NAME 80 @@ -289,6 +289,7 @@ int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr, int slave); struct ctl_list *uc_mgr_get_master_ctl(snd_use_case_mgr_t *uc_mgr); +struct ctl_list *uc_mgr_get_ctl_by_card(snd_use_case_mgr_t *uc_mgr, int card); struct ctl_list *uc_mgr_get_ctl_by_name(snd_use_case_mgr_t *uc_mgr, const char *name, int idx); snd_ctl_t *uc_mgr_get_ctl(snd_use_case_mgr_t *uc_mgr); diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c index df6d736f..7e745bca 100644 --- a/src/ucm/ucm_subs.c +++ b/src/ucm/ucm_subs.c @@ -28,6 +28,7 @@ #include #include #include +#include static char *rval_open_name(snd_use_case_mgr_t *uc_mgr) { @@ -161,6 +162,8 @@ static char *rval_card_number_by_name(snd_use_case_mgr_t *uc_mgr, const char *id return NULL; } + uc_error("${CardNumberByName} substitution is obsolete - use ${find-card}!"); + return get_card_number(get_ctl_list_by_name(uc_mgr, id)); } @@ -173,12 +176,323 @@ static char *rval_card_id_by_name(snd_use_case_mgr_t *uc_mgr, const char *id) return NULL; } + uc_error("${CardIdByName} substitution is obsolete - use ${find-card}!"); + ctl_list = get_ctl_list_by_name(uc_mgr, id); if (ctl_list == NULL) return NULL; return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info)); } +typedef struct lookup_iterate *(*lookup_iter_fcn_t) + (snd_use_case_mgr_t *uc_mgr, struct lookup_iterate *iter); +typedef const char *(*lookup_fcn_t)(void *); + +struct lookup_fcn { + char *name; + const char *(*fcn)(void *opaque); +}; + +struct lookup_iterate { + int (*init)(snd_use_case_mgr_t *uc_mgr, struct lookup_iterate *iter, + snd_config_t *config); + void (*done)(struct lookup_iterate *iter); + lookup_iter_fcn_t first; + lookup_iter_fcn_t next; + char *(*retfcn)(struct lookup_iterate *iter, snd_config_t *config); + struct lookup_fcn *fcns; + lookup_fcn_t fcn; + struct ctl_list *ctl_list; + void *info; +}; + +static snd_config_t *parse_lookup_query(const char *query) +{ + snd_input_t *input; + snd_config_t *config; + int err; + + err = snd_input_buffer_open(&input, query, strlen(query)); + if (err < 0) { + uc_error("unable to create memory input buffer"); + return NULL; + } + snd_config_top(&config); + err = snd_config_load(config, input); + snd_input_close(input); + if (err < 0) { + snd_config_delete(config); + uc_error("wrong arguments '%s'", query); + return NULL; + } + return config; +} + +static char *rval_lookup_main(snd_use_case_mgr_t *uc_mgr, + const char *query, + struct lookup_iterate *iter) +{ + snd_config_t *config, *d; + struct lookup_fcn *fcn; + struct lookup_iterate *curr; + const char *s; + char *result; + regmatch_t match[1]; + regex_t re; + int err; + + if (uc_mgr->conf_format < 4) { + uc_error("Lookups are supported in v4+ syntax"); + return NULL; + } + + config = parse_lookup_query(query); + if (config == NULL) + return NULL; + if (iter->init && iter->init(uc_mgr, iter, config)) + goto null; + if (snd_config_search(config, "field", &d)) { + uc_error("Lookups require field!"); + goto null; + } + if (snd_config_get_string(d, &s)) + goto null; + for (fcn = iter->fcns ; fcn; fcn++) { + if (strcasecmp(fcn->name, s) == 0) { + iter->fcn = fcn->fcn; + break; + } + } + if (iter->fcn == NULL) { + uc_error("Unknown field value '%s'", s); + goto null; + } + if (snd_config_search(config, "regex", &d)) { + uc_error("Lookups require regex!"); + goto null; + } + if (snd_config_get_string(d, &s)) + goto null; + err = regcomp(&re, s, REG_EXTENDED | REG_ICASE); + if (err) { + uc_error("Regex '%s' compilation failed (code %d)", s, err); + goto null; + } + + result = NULL; + for (curr = iter->first(uc_mgr, iter); curr; curr = iter->next(uc_mgr, iter)) { + s = curr->fcn(iter->info); + if (s == NULL) + continue; + if (regexec(&re, s, ARRAY_SIZE(match), match, 0) == 0) { + result = curr->retfcn(iter, config); + break; + } + } + snd_config_delete(config); + regfree(&re); + if (iter->done) + iter->done(iter); + return result; +null: + if (iter->done) + iter->done(iter); + snd_config_delete(config); + return NULL; +} + +static struct lookup_iterate *rval_card_lookup1(snd_use_case_mgr_t *uc_mgr, + struct lookup_iterate *iter, + int card) +{ + if (snd_card_next(&card) < 0 || card < 0) + return NULL; + iter->ctl_list = uc_mgr_get_ctl_by_card(uc_mgr, card); + if (iter->ctl_list == NULL) + return NULL; + iter->info = iter->ctl_list->ctl_info; + return iter; +} + +static struct lookup_iterate *rval_card_lookup_first(snd_use_case_mgr_t *uc_mgr, + struct lookup_iterate *iter) +{ + return rval_card_lookup1(uc_mgr, iter, -1); +} + +static struct lookup_iterate *rval_card_lookup_next(snd_use_case_mgr_t *uc_mgr, + struct lookup_iterate *iter) +{ + return rval_card_lookup1(uc_mgr, iter, snd_ctl_card_info_get_card(iter->info)); +} + +static char *rval_card_lookup_return(struct lookup_iterate *iter, snd_config_t *config) +{ + snd_config_t *d; + const char *s; + + if (snd_config_search(config, "return", &d)) + return strdup(snd_ctl_card_info_get_id(iter->info)); + else if (snd_config_get_string(d, &s)) + return NULL; + else if (strcasecmp(s, "id") == 0) + return strdup(snd_ctl_card_info_get_id(iter->info)); + else if (strcasecmp(s, "number") == 0) { + char num[16]; + snprintf(num, sizeof(num), "%d", snd_ctl_card_info_get_card(iter->info)); + return strdup(num); + } else { + uc_error("Unknown return type '%s'", s); + return NULL; + } +} + +static char *rval_card_lookup(snd_use_case_mgr_t *uc_mgr, const char *query) +{ + static struct lookup_fcn fcns[] = { + { .name = "id", (lookup_fcn_t)snd_ctl_card_info_get_id }, + { .name = "driver", (lookup_fcn_t)snd_ctl_card_info_get_driver }, + { .name = "name", (lookup_fcn_t)snd_ctl_card_info_get_name }, + { .name = "longname", (lookup_fcn_t)snd_ctl_card_info_get_longname }, + { .name = "mixername", (lookup_fcn_t)snd_ctl_card_info_get_mixername }, + { .name = "components", (lookup_fcn_t)snd_ctl_card_info_get_components }, + { 0 }, + }; + struct lookup_iterate iter = { + .first = rval_card_lookup_first, + .next = rval_card_lookup_next, + .retfcn = rval_card_lookup_return, + .fcns = fcns, + }; + return rval_lookup_main(uc_mgr, query, &iter); +} + +static struct lookup_iterate *rval_pcm_lookup1(struct lookup_iterate *iter, + int device) +{ + snd_pcm_info_t *pcminfo; + snd_ctl_t *ctl = iter->ctl_list->ctl; + int err; + + if (snd_ctl_pcm_next_device(ctl, &device) < 0 || device < 0) + return NULL; + pcminfo = iter->info; + snd_pcm_info_set_device(pcminfo, device); + err = snd_ctl_pcm_info(ctl, pcminfo); + if (err < 0) { + uc_error("Unable to obtain PCM info (device %d)", device); + return NULL; + } + return iter; +} + +static struct lookup_iterate *rval_pcm_lookup_first(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, + struct lookup_iterate *iter) +{ + return rval_pcm_lookup1(iter, -1); +} + +static struct lookup_iterate *rval_pcm_lookup_next(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, + struct lookup_iterate *iter) +{ + return rval_pcm_lookup1(iter, snd_pcm_info_get_device(iter->info)); +} + +static char *rval_pcm_lookup_return(struct lookup_iterate *iter, + snd_config_t *config ATTRIBUTE_UNUSED) +{ + char num[16]; + snprintf(num, sizeof(num), "%d", snd_pcm_info_get_device(iter->info)); + return strdup(num); +} + +static int rval_pcm_lookup_init(struct lookup_iterate *iter, + snd_config_t *config) +{ + static struct lookup_fcn pcm_fcns[] = { + { .name = "id", (lookup_fcn_t)snd_pcm_info_get_id }, + { .name = "name", (lookup_fcn_t)snd_pcm_info_get_name }, + { .name = "subname", (lookup_fcn_t)snd_pcm_info_get_subdevice_name }, + { 0 }, + }; + snd_config_t *d; + const char *s; + snd_pcm_info_t *pcminfo; + snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; + + if (snd_config_search(config, "stream", &d) == 0 && + snd_config_get_string(d, &s) == 0) { + if (strcasecmp(s, "playback") == 0) + stream = SND_PCM_STREAM_PLAYBACK; + else if (strcasecmp(s, "capture") == 0) + stream = SND_PCM_STREAM_CAPTURE; + else { + uc_error("Unknown stream type '%s'", s); + return -EINVAL; + } + } + if (snd_pcm_info_malloc(&pcminfo)) + return -ENOMEM; + snd_pcm_info_set_device(pcminfo, 0); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + iter->first = rval_pcm_lookup_first; + iter->next = rval_pcm_lookup_next; + iter->retfcn = rval_pcm_lookup_return; + iter->fcns = pcm_fcns; + iter->info = pcminfo; + return 0; +} + +static int rval_device_lookup_init(snd_use_case_mgr_t *uc_mgr, + struct lookup_iterate *iter, + snd_config_t *config) +{ + static struct { + const char *name; + int (*init)(struct lookup_iterate *iter, snd_config_t *config); + } *t, types[] = { + { .name = "pcm", .init = rval_pcm_lookup_init }, + { 0 } + }; + snd_config_t *d; + const char *s; + int err; + + if (snd_config_search(config, "ctl", &d) || snd_config_get_string(d, &s)) { + iter->ctl_list = uc_mgr_get_master_ctl(uc_mgr); + } else { + err = uc_mgr_open_ctl(uc_mgr, &iter->ctl_list, s, 1); + if (err < 0) { + uc_error("Control device '%s' not found", s); + return -EINVAL; + } + } + if (snd_config_search(config, "type", &d) || snd_config_get_string(d, &s)) { + uc_error("Missing device type!"); + return -EINVAL; + } + for (t = types; t; t++) + if (strcasecmp(t->name, s) == 0) + return t->init(iter, config); + uc_error("Device type '%s' is invalid", s); + return -EINVAL; +} + +static void rval_device_lookup_done(struct lookup_iterate *iter) +{ + free(iter->info); +} + +static char *rval_device_lookup(snd_use_case_mgr_t *uc_mgr, const char *query) +{ + struct lookup_iterate iter = { + .init = rval_device_lookup_init, + .done = rval_device_lookup_done, + }; + return rval_lookup_main(uc_mgr, query, &iter); +} + static char *rval_env(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id) { char *e; @@ -317,6 +631,8 @@ __std: MATCH_VARIABLE2(value, "${env:", rval_env, false); MATCH_VARIABLE2(value, "${sys:", rval_sysfs, false); MATCH_VARIABLE2(value, "${var:", rval_var, true); + MATCH_VARIABLE2(value, "${find-card:", rval_card_lookup, false); + MATCH_VARIABLE2(value, "${find-device:", rval_device_lookup, false); MATCH_VARIABLE2(value, "${CardNumberByName:", rval_card_number_by_name, false); MATCH_VARIABLE2(value, "${CardIdByName:", rval_card_id_by_name, false); __merr: diff --git a/src/ucm/utils.c b/src/ucm/utils.c index df6fd4de..f1fd8e92 100644 --- a/src/ucm/utils.c +++ b/src/ucm/utils.c @@ -67,13 +67,25 @@ struct ctl_list *uc_mgr_get_master_ctl(snd_use_case_mgr_t *uc_mgr) return ctl_list; } +struct ctl_list *uc_mgr_get_ctl_by_card(snd_use_case_mgr_t *uc_mgr, int card) +{ + struct ctl_list *ctl_list; + char cname[32]; + int err; + + sprintf(cname, "hw:%d", card); + err = uc_mgr_open_ctl(uc_mgr, &ctl_list, cname, 1); + if (err < 0) + return NULL; + return ctl_list; +} + struct ctl_list *uc_mgr_get_ctl_by_name(snd_use_case_mgr_t *uc_mgr, const char *name, int idx) { struct list_head *pos; - struct ctl_list *ctl_list = NULL; + struct ctl_list *ctl_list; const char *s; - char cname[32]; - int idx2, card, err; + int idx2, card; idx2 = idx; list_for_each(pos, &uc_mgr->ctl_list) { @@ -94,9 +106,8 @@ struct ctl_list *uc_mgr_get_ctl_by_name(snd_use_case_mgr_t *uc_mgr, const char * return NULL; while (card >= 0) { - sprintf(cname, "hw:%d", card); - err = uc_mgr_open_ctl(uc_mgr, &ctl_list, cname, 1); - if (err < 0) + ctl_list = uc_mgr_get_ctl_by_card(uc_mgr, card); + if (ctl_list == NULL) continue; /* really? */ s = snd_ctl_card_info_get_name(ctl_list->ctl_info); if (s && strcmp(s, name) == 0) {