[alsa-devel] [PATCH 0/2] topology: Support widgets' stream name and file inclusion for text conf file
From: Mengdong Lin mengdong.lin@linux.intel.com
This series doesn't require or cause any ABI updates. It doesn't need the kernel patches that trying to udpate ABI from v4 or v5.
It just fix a missing feature and enhance text conf file to support including files to control the increasing size of text conf files.
Mengdong Lin (2): topology: Fix missing stream name of widgets in text conf file topology: Add support for included files
include/topology.h | 38 +++++ src/topology/dapm.c | 10 ++ src/topology/parser.c | 374 +++++++++++++++++++++++++++++++++++++++++++++- src/topology/tplg_local.h | 17 +++ 4 files changed, 434 insertions(+), 5 deletions(-)
From: Mengdong Lin mengdong.lin@linux.intel.com
User can define the stream name of an input/output widget in the text conf file, by setting "stream_name" of a SectionWidget.
Topology C API and kernel already have support for configuring a widget's stream name. This patch just adds the missing part of the text conf file.
Signed-off-by: Mengdong Lin mengdong.lin@linux.intel.com
diff --git a/include/topology.h b/include/topology.h index 0675b52..a0d018e 100644 --- a/include/topology.h +++ b/include/topology.h @@ -483,6 +483,7 @@ extern "C" { * index "1" # Index number * * type "aif_in" # Widget type - detailed above + * stream_name "name" # Stream name * * no_pm "true" # No PM control bit. * reg "20" # PM bit register offset diff --git a/src/topology/dapm.c b/src/topology/dapm.c index e3c90d8..9fa0aac 100644 --- a/src/topology/dapm.c +++ b/src/topology/dapm.c @@ -507,6 +507,16 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, continue; }
+ if (strcmp(id, "stream_name") == 0) { + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; + + elem_copy_text(widget->sname, val, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + tplg_dbg("\t%s: %s\n", id, val); + continue; + } + if (strcmp(id, "no_pm") == 0) { if (snd_config_get_string(n, &val) < 0) return -EINVAL;
From: Mengdong Lin mengdong.lin@linux.intel.com
Users can include a list of files by SectionInclude in the text config file. This allows user to define common info in separate files (e.g. vendor tokens, tuples) and share them for different platforms, by including them. This can save the total size of files. Users can also specifiy a list of directories relative to "usr/share/alsa/topology" to search the included files.
The topology library will search and open an included file in the following order of priority: 1. directly open the file by its name; 2. search for the file name in the directory containing current file; 3. search for the file name in "usr/share/alsa/topology"; 4. search for the file name in user specified subdirectories under "usr/share/alsa/topology".
The order of the included files need not to be same as their dependencies, because the toplogy library will load all of them before parsing their dependencies.
Signed-off-by: Mengdong Lin mengdong.lin@linux.intel.com
diff --git a/include/topology.h b/include/topology.h index a0d018e..324f123 100644 --- a/include/topology.h +++ b/include/topology.h @@ -624,6 +624,43 @@ extern "C" { * data "name" # optional private data * } * </pre> + * + * <h4>Include other files</h4> + * Users can include a list of files by SectionInclude in the text config + * file. This allows user to define common info in separate files (e.g. + * vendor tokens, tuples) and share them for different platforms, by + * including them. This can save the total size of files. <br><br> + * Users can also specifiy a list of subdirectories under + * "usr/share/alsa/topology", the installation directory of toplogy + * configuration files, to search the included files. <br><br> + * + * The topology library will search and open an included file in the + * following order of priority: + * 1. directly open the file by its name; + * 2. search for the file name in the directory containing current file; + * 3. search for the file name in "usr/share/alsa/topology"; + * 4. search for the file name in user specified subdirectories under + * "usr/share/alsa/topology". + * + * The order of the included files need not to be same as their + * dependencies, since the tool will load them all before parsing their + * dependencies. + * + * <pre> + * SectionInclude."name" { + * path [ # Optional subdirectories to search included files + * "1st subdirectory" + * "2nd subdirectory" + * ... + * ] + * + * include [ # Name of included files, use relative path + * "name of 1st included file" + * "name of 2nd included file" + * ... + * ] + * } + * </pre> */
/** Maximum number of channels supported in one control */ diff --git a/src/topology/parser.c b/src/topology/parser.c index 3ab64f4..f0004d2 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -17,9 +17,19 @@ */
#include <sys/stat.h> +#include <libgen.h> #include "list.h" #include "tplg_local.h"
+/* installation directory of topology configuration files */ +const char install_dir[] = "/usr/share/alsa/topology/"; + +static int tplg_load_config(const char *file, const char *base_dir, + struct list_head *include_paths, + snd_config_t **cfg); +static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg, + const char *base_dir); + /* * Parse compound */ @@ -58,7 +68,248 @@ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg, return err; }
-static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) + +/** + * add_include_path - Add a diretory to the paths to search included files + * @tplg: Topology context. + * @dir: Name of directory to add. + * + * The direcotry will be taken as a subdiretory of topology installation + * directory "/usr/share/alsa/topology". + */ +static int add_include_path(snd_tplg_t *tplg, const char *dir) +{ + struct tplg_path *path; + + path = calloc(1, sizeof(*path)); + if (!path) + return -ENOMEM; + + path->dir = calloc(1, PATH_MAX + 1); + if (!path->dir) + return -ENOMEM; + + strcpy(path->dir, install_dir); + strncat(path->dir, dir, PATH_MAX - sizeof(install_dir)); + + tplg_dbg("Include path: %s\n", path->dir); + list_add_tail(&path->list, &tplg->include_paths); + return 0; +} + +/* Free all include paths in the list */ +static void free_include_paths(snd_tplg_t *tplg) +{ + struct list_head *pos, *npos, *base; + struct tplg_path *path; + + base = &tplg->include_paths; + list_for_each_safe(pos, npos, base) { + path = list_entry(pos, struct tplg_path, list); + list_del(&path->list); + if (path->dir) + free(path->dir); + free(path); + } +} + +/* Parse the include paths + */ +static int parse_include_path(snd_tplg_t *tplg, snd_config_t *cfg) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *dir; + int err; + + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + if (snd_config_get_string(n, &dir) < 0) + continue; + + if (!strlen(dir)) + continue; + + err = add_include_path(tplg, dir); + if (err < 0) { + SNDERR("error: failed to add include path %s\n", + dir); + return err; + } + } + + return 0; +} + +/* Add a child config to topology context, to free it finally */ +static int add_child_config(snd_tplg_t *tplg, snd_config_t *cfg) +{ + struct tplg_config *child; + + child = calloc(1, sizeof(*child)); + if (!child) + return -ENOMEM; + + child->cfg = cfg; + list_add_tail(&child->list, &tplg->child_cfg_list); + + return 0; +} + + +/* Free all child configs of the topology context */ +static void free_child_configs(snd_tplg_t *tplg) +{ + struct list_head *pos, *npos, *base; + struct tplg_config *child; + + base = &tplg->child_cfg_list; + list_for_each_safe(pos, npos, base) { + child = list_entry(pos, struct tplg_config, list); + list_del(&child->list); + snd_config_delete(child->cfg); + free(child); + } +} + +/** + * parse_included_file - Parse included files. + * @ tplg: topology context + * @ cfg: config that contains a list of included files + * @ base_dir: direcotry of the parent config file, to search + * the included files. + */ +static int parse_included_file(snd_tplg_t *tplg, snd_config_t *cfg, + const char *base_dir) +{ + snd_config_iterator_t i, next; + snd_config_t *n, *child; + const char *file; + char *full_path = NULL, *child_dir = NULL; + int err = 0, pos; + + full_path = calloc(1, PATH_MAX + 1); + if (!full_path) + return -ENOMEM; + + /* Current base directory will be used to calculate base directory + * of each included file (child) that may further include other files. + */ + strncpy(full_path, base_dir, PATH_MAX); + strcat(full_path, "/"); + pos = strlen(full_path); + + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + if (snd_config_get_string(n, &file) < 0) + continue; + + /* load config from the included file */ + tplg_dbg("Include file: %s\n", file); + err = tplg_load_config(file, base_dir, + &tplg->include_paths, + &child); + if (err < 0) { + SNDERR("error: failed to load topology file %s\n", + full_path); + goto out; + } + + /* store the config as a child */ + err = add_child_config(tplg, child); + if (err < 0) { + SNDERR("error: failed to add child config of file %s\n", + full_path); + goto out; + } + + /* Get the base directory for the child config, which may + * further include other files. Duplicate the path since + * dirname() may modify the input. + */ + full_path[pos] = 0; + strcat(full_path, file); + child_dir = strdup(full_path); + if (!child_dir) { + err = -ENOMEM; + goto out; + } + + /* parse topology items in the child confg */ + err = tplg_parse_config(tplg, child, dirname(child_dir)); + free(child_dir); + if (err < 0) { + SNDERR("error: failed to parse config of file %s\n", + full_path); + goto out; + } + } + +out: + if (full_path) + free(full_path); + + return err; +} + +/** + * tplg_parse_include - Parse included files and additional directories + * for searching them. + * @ tplg: topology context + * @ cfg: config that contains the included files and directories + * @ private: Base direcotry, which contains current config file, + * for searching included files as well. + */ +static int tplg_parse_include(snd_tplg_t *tplg, snd_config_t *cfg, + void *private) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + int err; + const char *base_dir, *inc_id; + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("error: compound is expected for include definition\n"); + return -EINVAL; + } + + /* directory of the included files */ + base_dir = (const char *)private; + snd_config_get_id(cfg, &inc_id); + + snd_config_for_each(i, next, cfg) { + const char *id; + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + if (strcmp(id, "path") == 0) { + err = parse_include_path(tplg, n); + if (err < 0) { + SNDERR("error: failed to parse path %s\n", + inc_id); + return err; + } + } + + if (strcmp(id, "include") == 0) { + err = parse_included_file(tplg, n, base_dir); + if (err < 0) { + SNDERR("error: failed to parse include %s\n", + inc_id); + return err; + } + } + } + + return 0; +} + +static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg, + const char *base_dir) { snd_config_iterator_t i, next; snd_config_t *n; @@ -198,19 +449,111 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) continue; }
+ if (strcmp(id, "SectionInclude") == 0) { + err = tplg_parse_compound(tplg, n, + tplg_parse_include, + (void *)base_dir); + if (err < 0) + return err; + continue; + } + SNDERR("error: unknown section %s\n", id); } return 0; }
-static int tplg_load_config(const char *file, snd_config_t **cfg) +/** + * open_tplg_file - Search and open a topology configuration file. + * @file: Name of the configuration file. + * @base_dir: Optional, directory of the parent config file of this file. + * @include_paths: Optional, addtional directories to search the file. + * + * This function will search and open the file in the following order + * of priority: + * 1. directly open the file by its name; + * 2. search for the file name in the directory of its parent file; + * 3. search for the file name in "usr/share/alsa/topology"; + * 4. search for the file name in user specified additional directories, + * which are subdirectories of "usr/share/alsa/topology". + */ +static FILE *open_tplg_file(const char *file, const char *base_dir, + struct list_head *include_paths) +{ + FILE *fp; + struct list_head *pos, *npos, *base; + struct tplg_path *path; + char *full_path = NULL; + + fp = fopen(file, "r"); + if (fp) + goto out; + + full_path = calloc(1, PATH_MAX + 1); + if (!full_path) + return NULL; + + /* search file in base directory */ + if (base_dir) { + strncpy(full_path, base_dir, PATH_MAX); + strcat(full_path, "/"); + strcat(full_path, file); + fp = fopen(full_path, "r"); + if (fp) + goto out; + } + + /* search file in top installation directory */ + strcpy(full_path, install_dir); + strcat(full_path, file); + fp = fopen(full_path, "r"); + if (fp) + goto out; + + /* search file in user specified include paths */ + if (include_paths) { + base = include_paths; + list_for_each_safe(pos, npos, base) { + path = list_entry(pos, struct tplg_path, list); + if (!path->dir) + continue; + + strncpy(full_path, path->dir, PATH_MAX); + strcat(full_path, "/"); + strcat(full_path, file); + fp = fopen(full_path, "r"); + if (fp) + goto out; + } + } + +out: + if (full_path) + free(full_path); + + return fp; +} + + +/** + * tplg_load_config - Load the config from a file. + * @file: Name of the config file to open and load. + * @base_dir: Optional, directory of the parent config file, to search + * this file. + * @include_paths: Optional, addtional directories to search the file. + * @cfg: output config loaded from this file. + * + */ +static int tplg_load_config(const char *file, const char *base_dir, + struct list_head *include_paths, + snd_config_t **cfg) { FILE *fp; snd_input_t *in; snd_config_t *top; int ret;
- fp = fopen(file, "r"); + fp = open_tplg_file(file, base_dir, include_paths); if (fp == NULL) { SNDERR("error: could not open configuration file %s", file); @@ -290,6 +633,7 @@ int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile, const char *outfile) { snd_config_t *cfg = NULL; + char *base_dir, *infile_bak = NULL; int err = 0;
tplg->out_fd = @@ -300,14 +644,26 @@ int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile, return -errno; }
- err = tplg_load_config(infile, &cfg); + err = tplg_load_config(infile, NULL, NULL, &cfg); if (err < 0) { SNDERR("error: failed to load topology file %s\n", infile); goto out_close; }
- err = tplg_parse_config(tplg, cfg); + /* Set the base directory from the config file path. Name of + * included topology config files can be relative to this base. + * Get dirname on the duplicated path because dirname() can + * modify input. + */ + infile_bak = strndup(infile, PATH_MAX); + if (!infile_bak) { + err = -ENOMEM; + goto out; + } + base_dir = dirname(infile_bak); + + err = tplg_parse_config(tplg, cfg, base_dir); if (err < 0) { SNDERR("error: failed to parse topology\n"); goto out; @@ -327,6 +683,9 @@ int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile,
out: snd_config_delete(cfg); + if (infile_bak) + free(infile_bak); + out_close: close(tplg->out_fd); return err; @@ -435,6 +794,8 @@ snd_tplg_t *snd_tplg_new(void) if (!tplg) return NULL;
+ INIT_LIST_HEAD(&tplg->include_paths); + INIT_LIST_HEAD(&tplg->child_cfg_list); tplg->manifest.size = sizeof(struct snd_soc_tplg_manifest);
INIT_LIST_HEAD(&tplg->tlv_list); @@ -479,5 +840,8 @@ void snd_tplg_free(snd_tplg_t *tplg) tplg_elem_free_list(&tplg->token_list); tplg_elem_free_list(&tplg->tuple_list);
+ free_include_paths(tplg); + free_child_configs(tplg); + free(tplg); } diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index cfde4cc..ce8f4b3 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -48,6 +48,11 @@ struct snd_tplg { /* out file */ int out_fd;
+ /* user specified paths to search included files */ + struct list_head include_paths; + /* child configs of included files */ + struct list_head child_cfg_list; + int verbose; unsigned int version;
@@ -169,6 +174,18 @@ struct tplg_elem { void (*free)(void *obj); };
+/* child topology config from an included file */ +struct tplg_config { + snd_config_t *cfg; + struct list_head list; /* list of all child configs */ +}; + +/* path to search included files */ +struct tplg_path { + char *dir; + struct list_head list; /* list of all include paths */ +}; + struct map_elem { const char *name; int id;
On Mon, 17 Oct 2016 07:55:21 +0200, mengdong.lin@linux.intel.com wrote:
From: Mengdong Lin mengdong.lin@linux.intel.com
This series doesn't require or cause any ABI updates. It doesn't need the kernel patches that trying to udpate ABI from v4 or v5.
It just fix a missing feature and enhance text conf file to support including files to control the increasing size of text conf files.
The inclusion of a file can be done in alsaconf syntax via <xxx>, too. We may extend it if needed instead?
The patches themselves look OK, so I'm not strongly against merging them, but just to make sure our standpoint.
thanks,
Takashi
Mengdong Lin (2): topology: Fix missing stream name of widgets in text conf file topology: Add support for included files
include/topology.h | 38 +++++ src/topology/dapm.c | 10 ++ src/topology/parser.c | 374 +++++++++++++++++++++++++++++++++++++++++++++- src/topology/tplg_local.h | 17 +++ 4 files changed, 434 insertions(+), 5 deletions(-)
-- 2.5.0
On Mon, Oct 17, 2016 at 02:49:42PM +0200, Takashi Iwai wrote:
On Mon, 17 Oct 2016 07:55:21 +0200, mengdong.lin@linux.intel.com wrote:
From: Mengdong Lin mengdong.lin@linux.intel.com
This series doesn't require or cause any ABI updates. It doesn't need the kernel patches that trying to udpate ABI from v4 or v5.
It just fix a missing feature and enhance text conf file to support including files to control the increasing size of text conf files.
The inclusion of a file can be done in alsaconf syntax via <xxx>, too. We may extend it if needed instead?
Ah that's interesting... Am fine either way.
The patches themselves look OK, so I'm not strongly against merging them, but just to make sure our standpoint.
The last SKL conf became a bit bigger, so for readability we though we can have different files, one for module instances, one for tuples and then finally one including these two which has the graph..
On 10/19/2016 11:45 AM, Vinod Koul wrote:
On Mon, Oct 17, 2016 at 02:49:42PM +0200, Takashi Iwai wrote:
On Mon, 17 Oct 2016 07:55:21 +0200, mengdong.lin@linux.intel.com wrote:
From: Mengdong Lin mengdong.lin@linux.intel.com
This series doesn't require or cause any ABI updates. It doesn't need the kernel patches that trying to udpate ABI from v4 or v5.
It just fix a missing feature and enhance text conf file to support including files to control the increasing size of text conf files.
The inclusion of a file can be done in alsaconf syntax via <xxx>, too. We may extend it if needed instead?
Ah that's interesting... Am fine either way.
That saves a lot of code as well :-)
I sent out the v2 series which extends alsaconf and now topology does not need any code change. Please review.
Thanks Mengdong
The patches themselves look OK, so I'm not strongly against merging them, but just to make sure our standpoint.
The last SKL conf became a bit bigger, so for readability we though we can have different files, one for module instances, one for tuples and then finally one including these two which has the graph..
On 10/17/2016 08:49 PM, Takashi Iwai wrote:
On Mon, 17 Oct 2016 07:55:21 +0200, mengdong.lin@linux.intel.com wrote:
From: Mengdong Lin mengdong.lin@linux.intel.com
This series doesn't require or cause any ABI updates. It doesn't need the kernel patches that trying to udpate ABI from v4 or v5.
It just fix a missing feature and enhance text conf file to support including files to control the increasing size of text conf files.
The inclusion of a file can be done in alsaconf syntax via <xxx>, too. We may extend it if needed instead?
Thanks for your review and advice!
It seems etter to extend and reuse alsaconf syntax, instead of adding topology specific code. This can also benefit UCM if users want to split common codec or SOC settings to other files.
alsaconf already support using absolute path to include a file. But it seems we cannot give a relative path (e.g. just a file name) and a configuration directory by confdir:xxx. I'll try to extend alsaconf.
The other patch is a bug fixing, if it's okay, could it be merged first?
Thanks Mengdong
The patches themselves look OK, so I'm not strongly against merging them, but just to make sure our standpoint.
thanks,
Takashi
Mengdong Lin (2): topology: Fix missing stream name of widgets in text conf file topology: Add support for included files
include/topology.h | 38 +++++ src/topology/dapm.c | 10 ++ src/topology/parser.c | 374 +++++++++++++++++++++++++++++++++++++++++++++- src/topology/tplg_local.h | 17 +++ 4 files changed, 434 insertions(+), 5 deletions(-)
-- 2.5.0
participants (4)
-
Mengdong Lin
-
mengdong.lin@linux.intel.com
-
Takashi Iwai
-
Vinod Koul