From aba2260ae7b528f721f2ed2bf490fd3f740ad2e6 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sat, 9 Nov 2019 11:53:32 +0100 Subject: [PATCH] ucm: switch to ucm2 directory and v2 format, keep backward compatibility Signed-off-by: Jaroslav Kysela --- src/ucm/parser.c | 149 ++++++++++++++++++++++++++++++++++---------- src/ucm/ucm_local.h | 8 ++- src/ucm/utils.c | 14 +++-- 3 files changed, 134 insertions(+), 37 deletions(-) diff --git a/src/ucm/parser.c b/src/ucm/parser.c index d0190b54..3e1fc24c 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -61,6 +61,65 @@ static int parse_sequence(snd_use_case_mgr_t *uc_mgr, struct list_head *base, snd_config_t *cfg); +/* + * compose configuration file + */ +static void configuration_filename2(char *fn, size_t fn_len, int format, + const char *dir, const char *file, + const char *suffix) +{ + snprintf(fn, fn_len, "%s/ucm%s/%s/%s%s", + snd_config_topdir(), format >= 2 ? "2" : "", + dir, file, suffix); + fn[fn_len-1] = '\0'; +} + +static void configuration_filename(snd_use_case_mgr_t *uc_mgr, + char *fn, size_t fn_len, + const char *file, const char *suffix) +{ + const char *env, *dir; + + if (uc_mgr->conf_format > 0) { + /* known format */ + env = getenv(uc_mgr->conf_format >= 2 ? ALSA_CONFIG_UCM2_VAR : + ALSA_CONFIG_UCM_VAR); + } else { + /* auto-detect */ + env = getenv(ALSA_CONFIG_UCM2_VAR); + if (env == NULL) { + env = getenv(ALSA_CONFIG_UCM_VAR); + } else { + uc_mgr->conf_format = 2; + } + } + if (env) { + snprintf(fn, fn_len, "%s/%s/%s%s", + env, uc_mgr->conf_file_name, file, suffix); + fn[fn_len-1] = '\0'; + return; + } + + dir = uc_mgr->conf_file_name; + if (uc_mgr->conf_format > 0) { +__format: + configuration_filename2(fn, fn_len, uc_mgr->conf_format, + dir, file, suffix); + return; + } + + configuration_filename2(fn, fn_len, 2, dir, file, suffix); + if (access(fn, R_OK) == 0) + return; + + configuration_filename2(fn, fn_len, 0, dir, file, suffix); + if (access(fn, R_OK)) { + /* make sure that the error message refers to the new path */ + uc_mgr->conf_format = 2; + goto __format; + } +} + /* * Parse string */ @@ -1052,7 +1111,6 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb; snd_config_t *cfg; char filename[MAX_FILE]; - char *env = getenv(ALSA_CONFIG_UCM_VAR); int err; /* allocate verb */ @@ -1080,15 +1138,8 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, } /* open Verb file for reading */ - if (env) - snprintf(filename, sizeof(filename), "%s/%s/%s", - env, uc_mgr->conf_file_name, file); - else - snprintf(filename, sizeof(filename), "%s/ucm/%s/%s", - snd_config_topdir(), uc_mgr->conf_file_name, file); - filename[sizeof(filename)-1] = '\0'; - - err = uc_mgr_config_load(filename, &cfg); + configuration_filename(uc_mgr, filename, sizeof(filename), file, ""); + err = uc_mgr_config_load(uc_mgr->conf_format, filename, &cfg); if (err < 0) { uc_error("error: failed to open verb file %s : %d", filename, -errno); @@ -1283,6 +1334,7 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) snd_config_iterator_t i, next; snd_config_t *n; const char *id; + long l; int err; if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { @@ -1290,6 +1342,23 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) return -EINVAL; } + if (uc_mgr->conf_format >= 2) { + err = snd_config_search(cfg, "Syntax", &n); + if (err < 0) { + uc_error("Syntax field not found in %s", uc_mgr->conf_file_name); + return -EINVAL; + } + err = snd_config_get_integer(n, &l); + if (err < 0) { + uc_error("Syntax field is invalid in %s", uc_mgr->conf_file_name); + return err; + } + if (l < 2 || l > SYNTAX_VERSION_MAX) { + uc_error("Incompatible syntax %d in %s", l, uc_mgr->conf_file_name); + return -EINVAL; + } + } + /* parse master config sections */ snd_config_for_each(i, next, cfg) { @@ -1297,6 +1366,9 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) if (snd_config_get_id(n, &id) < 0) continue; + if (uc_mgr->conf_format >= 2 && strcmp(id, "Syntax") == 0) + continue; + if (strcmp(id, "Comment") == 0) { err = parse_string(n, &uc_mgr->comment); if (err < 0) { @@ -1400,10 +1472,10 @@ next_card: return -1; } -static int load_master_config(const char *card_name, snd_config_t **cfg) +static int load_master_config(snd_use_case_mgr_t *uc_mgr, + const char *card_name, snd_config_t **cfg) { char filename[MAX_FILE]; - char *env = getenv(ALSA_CONFIG_UCM_VAR); int err; if (strnlen(card_name, MAX_CARD_LONG_NAME) == MAX_CARD_LONG_NAME) { @@ -1412,16 +1484,9 @@ static int load_master_config(const char *card_name, snd_config_t **cfg) return -EINVAL; } - if (env) - snprintf(filename, sizeof(filename)-1, - "%s/%s/%s.conf", env, card_name, card_name); - else - snprintf(filename, sizeof(filename)-1, - "%s/ucm/%s/%s.conf", snd_config_topdir(), - card_name, card_name); - filename[MAX_FILE-1] = '\0'; - - err = uc_mgr_config_load(filename, cfg); + configuration_filename(uc_mgr, filename, sizeof(filename), + card_name, ".conf"); + err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg); if (err < 0) { uc_error("error: could not parse configuration for card %s", card_name); @@ -1452,7 +1517,7 @@ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr) err = get_card_long_name(uc_mgr); if (err == 0) /* load file that maches the card long name */ - err = load_master_config(uc_mgr->card_long_name, &cfg); + err = load_master_config(uc_mgr, uc_mgr->card_long_name, &cfg); if (err == 0) { /* got device-specific file that matches the card long name */ @@ -1462,7 +1527,7 @@ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr) * either short name or long name (users may open a card by * its name or long name). */ - err = load_master_config(uc_mgr->card_name, &cfg); + err = load_master_config(uc_mgr, uc_mgr->card_name, &cfg); if (err < 0) return err; strncpy(uc_mgr->conf_file_name, uc_mgr->card_name, MAX_CARD_LONG_NAME); @@ -1512,26 +1577,27 @@ static int is_component_directory(const char *dir) * * Cards are defined by machines. Each card/machine installs its UCM * configuration files in a subdirectory with the same name as the sound - * card under /usr/share/alsa/ucm. This function will scan all the card + * card under /usr/share/alsa/ucm2. This function will scan all the card * directories and skip the component directories defined in the array * component_dir. */ int uc_mgr_scan_master_configs(const char **_list[]) { char filename[MAX_FILE], dfl[MAX_FILE]; - char *env = getenv(ALSA_CONFIG_UCM_VAR); - const char **list; + char *env = getenv(ALSA_CONFIG_UCM2_VAR); + const char **list, *d_name; snd_config_t *cfg, *c; int i, j, cnt, err; + long l; ssize_t ss; struct dirent **namelist; if (env) snprintf(filename, sizeof(filename)-1, "%s", env); else - snprintf(filename, sizeof(filename)-1, "%s/ucm", + snprintf(filename, sizeof(filename)-1, "%s/ucm2", snd_config_topdir()); - filename[MAX_FILE-1] = '\0'; + filename[sizeof(filename)-1] = '\0'; #if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__sun) && !defined(ANDROID) #define SORTFUNC versionsort @@ -1569,13 +1635,32 @@ int uc_mgr_scan_master_configs(const char **_list[]) for (i = j = 0; i < cnt; i++) { + d_name = namelist[i]->d_name; + /* Skip the directories for component devices */ - if (is_component_directory(namelist[i]->d_name)) + if (is_component_directory(d_name)) continue; - err = load_master_config(namelist[i]->d_name, &cfg); + + configuration_filename2(filename, sizeof(filename), 2, + d_name, d_name, ".conf"); + err = uc_mgr_config_load(2, filename, &cfg); if (err < 0) goto __err; + err = snd_config_search(cfg, "Syntax", &c); + if (err < 0) { + uc_error("Syntax field not found in %s", d_name); + continue; + } + err = snd_config_get_integer(c, &l); + if (err < 0) { + uc_error("Syntax field is invalid in %s", d_name); + goto __err; + } + if (l < 2 || l > SYNTAX_VERSION_MAX) { + uc_error("Incompatible syntax %d in %s", l, d_name); + goto __err; + } err = snd_config_search(cfg, "Comment", &c); if (err >= 0) { err = parse_string(c, (char **)&list[j+1]); @@ -1585,7 +1670,7 @@ int uc_mgr_scan_master_configs(const char **_list[]) } } snd_config_delete(cfg); - list[j] = strdup(namelist[i]->d_name); + list[j] = strdup(d_name); if (list[j] == NULL) { err = -ENOMEM; goto __err; diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index 8e7ac7ea..ee15d385 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -40,6 +40,8 @@ #include #include "use-case.h" +#define SYNTAX_VERSION_MAX 2 + #define MAX_FILE 256 #define MAX_CARD_LONG_NAME 80 @@ -193,6 +195,7 @@ struct snd_use_case_mgr { char card_long_name[MAX_CARD_LONG_NAME]; char conf_file_name[MAX_CARD_LONG_NAME]; char *comment; + int conf_format; /* use case verb, devices and modifier configs parsed from files */ struct list_head verb_list; @@ -235,7 +238,7 @@ struct snd_use_case_mgr { void uc_mgr_error(const char *fmt, ...); void uc_mgr_stdout(const char *fmt, ...); -int uc_mgr_config_load(const char *file, snd_config_t **cfg); +int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg); int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr); int uc_mgr_scan_master_configs(const char **_list[]); @@ -246,3 +249,6 @@ void uc_mgr_free(snd_use_case_mgr_t *uc_mgr); /** The name of the environment variable containing the UCM directory */ #define ALSA_CONFIG_UCM_VAR "ALSA_CONFIG_UCM" + +/** The name of the environment variable containing the UCM directory (new syntax) */ +#define ALSA_CONFIG_UCM2_VAR "ALSA_CONFIG_UCM2" diff --git a/src/ucm/utils.c b/src/ucm/utils.c index d6ed50a1..84ad5ac8 100644 --- a/src/ucm/utils.c +++ b/src/ucm/utils.c @@ -49,7 +49,7 @@ void uc_mgr_stdout(const char *fmt,...) va_end(va); } -int uc_mgr_config_load(const char *file, snd_config_t **cfg) +int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg) { FILE *fp; snd_input_t *in; @@ -71,9 +71,15 @@ int uc_mgr_config_load(const char *file, snd_config_t **cfg) if (err < 0) goto __err1; - path = getenv(ALSA_CONFIG_UCM_VAR); - if (!path || path[0] == '\0') - path = ALSA_CONFIG_DIR "/ucm"; + if (format >= 2) { + path = getenv(ALSA_CONFIG_UCM2_VAR); + if (!path || path[0] == '\0') + path = ALSA_CONFIG_DIR "/ucm2"; + } else { + path = getenv(ALSA_CONFIG_UCM_VAR); + if (!path || path[0] == '\0') + path = ALSA_CONFIG_DIR "/ucm"; + } default_paths[0] = path; default_paths[1] = NULL; -- 2.47.1