]> git.alsa-project.org Git - alsa-utils.git/commitdiff
topology: Add support for pre-processing Topology2.0 syntax
authorRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Fri, 26 Mar 2021 21:44:13 +0000 (14:44 -0700)
committerJaroslav Kysela <perex@perex.cz>
Tue, 25 May 2021 16:26:51 +0000 (18:26 +0200)
This patch adds support for pre-processing the Topology2.0.
The '-p' switch add pre-processing support during compilation
and the '-P' switch is for converting the Topology2.0
configuration file into the existing syntax.

Topology2.0 is a high level keyword extension on top of the existing ALSA
conf topology format designed to:

1) Simplify the ALSA conf topology definitions by providing high level
   "classes" so topology designers need to write less config for common
   object definitions.

2) Allow simple reuse of objects. Define once and reuse (like M4) with
   the ability to alter objects configuration attributes from defaults.

3) Allow data type and value verification. This is not done today and
   frequently crops up in FW bug reports.

Common Topology Classes
-----------------------

Topology today has some common classes that are often reused throughout
with slightly altered configurations. i.e. widgets (components),
pipelines, dais and controls.

Topology2.0 introduces the high level concept of reusable "class" like
definition for that can be used to create topology objects.

Common Topology Attributes
--------------------------
Topology defines a lot of attributes per object with different types
and constraints. Today there is no easy way to validate type or
constraints and this can lead to many hard to find problems in FW at
runtime.

A new keyword "DefineAttribute" has been added to define attribute
constraints such as min value, max value, enum_values etc. This
then allows alsatplg to validate each topology object attribute.

Topology Classes define the list of attributes that they use and
whether the attribute is mandatory, can be overridden by parent users
or is immutable. This also helps alsatplg emit the appropriate errors
for attribute misuse.

Class constructor attributes
----------------------------
Some attributes in the class definition are declared as constructor
attributes and these will be used to construct the name of the object.
For ex: for the host widget, the index and direction are constructor
attributes and the name for the widget is derived as follows:
host.1.playback or host.2.capture etc.

Attribute Inheritance:
----------------------
One of the key features of Topology2.0 is how the attribute values are
propagated from a parent object to a child object. For ex: a pipeline
object can pass down the pipeline_id attribute to all its widgets.
Inheritance is implicit when an object and its embedded child objects
have matching names for a attribute/argument. Attribute values
set explicitly in an object instance always has precedence over
the values inherited from the parent object.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
1

1

1

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
topology/Makefile.am
topology/pre-processor.c [new file with mode: 0644]
topology/topology.c
topology/topology.h [new file with mode: 0644]

index a56c109c10e0bb44f7b4ad2fc759d8f62c66cfa2..47f32f05a11216d6c0c12f945631e91f990d5e06 100644 (file)
@@ -8,7 +8,9 @@ endif
 %.1: %.rst
        rst2man $< > $@
 
-alsatplg_SOURCES = topology.c
+alsatplg_SOURCES = topology.c pre-processor.c
+
+noinst_HEADERS = topology.h
 
 AM_CPPFLAGS = \
          -Wall -I$(top_srcdir)/include
diff --git a/topology/pre-processor.c b/topology/pre-processor.c
new file mode 100644 (file)
index 0000000..212adfe
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+  Copyright(c) 2021 Intel Corporation
+  All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of version 2 of the GNU General Public License as
+  published by the Free Software Foundation.
+
+  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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+  The full GNU General Public License is included in this distribution
+  in the file called LICENSE.GPL.
+*/
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+
+#include <alsa/input.h>
+#include <alsa/output.h>
+#include <alsa/conf.h>
+#include "gettext.h"
+#include "topology.h"
+
+static int pre_process_config(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg)
+{
+       snd_config_iterator_t i, next, i2, next2;
+       snd_config_t *n, *n2;
+       const char *id;
+
+       if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+               fprintf(stderr, "compound type expected at top level");
+               return -EINVAL;
+       }
+
+       /* parse topology objects */
+       snd_config_for_each(i, next, cfg) {
+               n = snd_config_iterator_entry(i);
+               if (snd_config_get_id(n, &id) < 0)
+                       continue;
+
+               if (strcmp(id, "Object"))
+                       continue;
+
+               if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
+                       fprintf(stderr, "compound type expected for %s", id);
+                       return -EINVAL;
+               }
+
+               snd_config_for_each(i2, next2, n) {
+                       n2 = snd_config_iterator_entry(i2);
+
+                       if (snd_config_get_id(n, &id) < 0)
+                               continue;
+
+                       if (snd_config_get_type(n2) != SND_CONFIG_TYPE_COMPOUND) {
+                               fprintf(stderr, "compound type expected for %s", id);
+                               return -EINVAL;
+                       }
+
+                       /* TODO: Add support for parsing objects */
+               }
+       }
+
+       return 0;
+}
+
+void free_pre_preprocessor(struct tplg_pre_processor *tplg_pp)
+{
+       snd_output_close(tplg_pp->output);
+       snd_output_close(tplg_pp->dbg_output);
+       snd_config_delete(tplg_pp->output_cfg);
+       free(tplg_pp);
+}
+
+int init_pre_precessor(struct tplg_pre_processor **tplg_pp, snd_output_type_t type,
+                      const char *output_file)
+{
+       struct tplg_pre_processor *_tplg_pp;
+       int ret;
+
+       _tplg_pp = calloc(1, sizeof(struct tplg_pre_processor));
+       if (!_tplg_pp)
+               ret = -ENOMEM;
+
+       *tplg_pp = _tplg_pp;
+
+       /* create output top-level config node */
+       ret = snd_config_top(&_tplg_pp->output_cfg);
+       if (ret < 0)
+               goto err;
+
+       /* open output based on type */
+       if (type == SND_OUTPUT_STDIO) {
+               ret = snd_output_stdio_open(&_tplg_pp->output, output_file, "w");
+               if (ret < 0) {
+                       fprintf(stderr, "failed to open file output\n");
+                       goto open_err;
+               }
+       } else {
+               ret = snd_output_buffer_open(&_tplg_pp->output);
+               if (ret < 0) {
+                       fprintf(stderr, "failed to open buffer output\n");
+                       goto open_err;
+               }
+       }
+
+       /* debug output */
+       ret = snd_output_stdio_attach(&_tplg_pp->dbg_output, stdout, 0);
+       if (ret < 0) {
+               fprintf(stderr, "failed to open stdout output\n");
+               goto out_close;
+       }
+
+       return 0;
+out_close:
+       snd_output_close(_tplg_pp->output);
+open_err:
+       snd_config_delete(_tplg_pp->output_cfg);
+err:
+       free(_tplg_pp);
+       return ret;
+}
+
+int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size)
+{
+       snd_input_t *in;
+       snd_config_t *top;
+       int err;
+
+       /* create input buffer */
+       err = snd_input_buffer_open(&in, config, config_size);
+       if (err < 0) {
+               fprintf(stderr, "Unable to open input buffer\n");
+               return err;
+       }
+
+       /* create top-level config node */
+       err = snd_config_top(&top);
+       if (err < 0)
+               goto input_close;
+
+       /* load config */
+       err = snd_config_load(top, in);
+       if (err < 0) {
+               fprintf(stderr, "Unable not load configuration\n");
+               goto err;
+       }
+
+       tplg_pp->input_cfg = top;
+
+       err = pre_process_config(tplg_pp, top);
+       if (err < 0) {
+               fprintf(stderr, "Unable to pre-process configuration\n");
+               goto err;
+       }
+
+       /* save config to output */
+       err = snd_config_save(tplg_pp->output_cfg, tplg_pp->output);
+       if (err < 0)
+               fprintf(stderr, "failed to save pre-processed output file\n");
+
+err:
+       snd_config_delete(top);
+input_close:
+       snd_input_close(in);
+
+       return err;
+}
index c2f0943242666f74a22bc97d75fdf995059b5f84..e0c7e7c88bff93db164781ebb1769bfe3465dc85 100644 (file)
@@ -20,6 +20,7 @@
   in the file called LICENSE.GPL.
 */
 
+#include <stdbool.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdint.h>
@@ -35,6 +36,9 @@
 #include <alsa/topology.h>
 #include "gettext.h"
 #include "version.h"
+#include "topology.h"
+
+bool pre_process_config = false;
 
 static snd_output_t *log;
 
@@ -45,6 +49,8 @@ _("Usage: %s [OPTIONS]...\n"
 "\n"
 "-h, --help              help\n"
 "-c, --compile=FILE      compile configuration file\n"
+"-p, --pre-process       pre-process Topology2.0 configuration file before compilation\n"
+"-P, --pre-process=FILE  pre-process Topology2.0 configuration file\n"
 "-d, --decode=FILE       decode binary topology file\n"
 "-n, --normalize=FILE    normalize configuration file\n"
 "-u, --dump=FILE         dump (reparse) configuration file\n"
@@ -227,8 +233,38 @@ static int dump(const char *source_file, const char *output_file, int cflags, in
        return err;
 }
 
+/* Convert Topology2.0 conf to the existing conf syntax */
+static int pre_process_conf(const char *source_file, const char *output_file)
+{
+       struct tplg_pre_processor *tplg_pp;
+       size_t config_size;
+       char *config;
+       int err;
+
+       err = load(source_file, (void **)&config, &config_size);
+       if (err)
+               return err;
+
+       /* init pre-processor */
+       err = init_pre_precessor(&tplg_pp, SND_OUTPUT_STDIO, output_file);
+       if (err < 0) {
+               fprintf(stderr, _("failed to init pre-processor for Topology2.0\n"));
+               free(config);
+               return err;
+       }
+
+       /* pre-process conf file */
+       err = pre_process(tplg_pp, config, config_size);
+
+       /* free pre-processor */
+       free_pre_preprocessor(tplg_pp);
+       free(config);
+       return err;
+}
+
 static int compile(const char *source_file, const char *output_file, int cflags)
 {
+       struct tplg_pre_processor *tplg_pp = NULL;
        snd_tplg_t *tplg;
        char *config;
        void *bin;
@@ -238,7 +274,32 @@ static int compile(const char *source_file, const char *output_file, int cflags)
        err = load(source_file, (void **)&config, &config_size);
        if (err)
                return err;
-       err = load_topology(&tplg, config, config_size, cflags);
+
+       /* pre-process before compiling */
+       if (pre_process_config) {
+               char *pconfig;
+               size_t size;
+
+               /* init pre-processor */
+               init_pre_precessor(&tplg_pp, SND_OUTPUT_BUFFER, NULL);
+
+               /* pre-process conf file */
+               err = pre_process(tplg_pp, config, config_size);
+               if (err) {
+                       free_pre_preprocessor(tplg_pp);
+                       free(config);
+                       return err;
+               }
+
+               /* load topology */
+               size = snd_output_buffer_string(tplg_pp->output, &pconfig);
+               err = load_topology(&tplg, pconfig, size, cflags);
+
+               /* free pre-processor */
+               free_pre_preprocessor(tplg_pp);
+       } else {
+               err = load_topology(&tplg, config, config_size, cflags);
+       }
        free(config);
        if (err)
                return err;
@@ -292,11 +353,12 @@ static int decode(const char *source_file, const char *output_file,
 
 int main(int argc, char *argv[])
 {
-       static const char short_options[] = "hc:d:n:u:v:o:sgxzV";
+       static const char short_options[] = "hc:d:n:u:v:o:pP:sgxzV";
        static const struct option long_options[] = {
                {"help", 0, NULL, 'h'},
                {"verbose", 1, NULL, 'v'},
                {"compile", 1, NULL, 'c'},
+               {"pre-process", 1, NULL, 'p'},
                {"decode", 1, NULL, 'd'},
                {"normalize", 1, NULL, 'n'},
                {"dump", 1, NULL, 'u'},
@@ -336,7 +398,7 @@ int main(int argc, char *argv[])
                case 'n':
                case 'u':
                        if (source_file) {
-                               fprintf(stderr, _("Cannot combine operations (compile, normalize, dump)\n"));
+                               fprintf(stderr, _("Cannot combine operations (compile, normalize, pre-process, dump)\n"));
                                return 1;
                        }
                        source_file = optarg;
@@ -348,6 +410,13 @@ int main(int argc, char *argv[])
                case 's':
                        sflags |= SND_TPLG_SAVE_SORT;
                        break;
+               case 'P':
+                       op = 'P';
+                       source_file = optarg;
+                       break;
+               case 'p':
+                       pre_process_config = true;
+                       break;
                case 'g':
                        sflags |= SND_TPLG_SAVE_GROUPS;
                        break;
@@ -384,6 +453,9 @@ int main(int argc, char *argv[])
        case 'd':
                err = decode(source_file, output_file, cflags, dflags, sflags);
                break;
+       case 'P':
+               err = pre_process_conf(source_file, output_file);
+               break;
        default:
                err = dump(source_file, output_file, cflags, sflags);
                break;
diff --git a/topology/topology.h b/topology/topology.h
new file mode 100644 (file)
index 0000000..494612b
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __TOPOLOGY_H
+#define __TOPOLOGY_H
+
+#include <stdlib.h>
+
+/* pre_processor */
+struct tplg_pre_processor {
+       snd_config_t *input_cfg;
+       snd_config_t *output_cfg;
+       snd_output_t *output;
+       snd_output_t *dbg_output;
+};
+
+int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size);
+int init_pre_precessor(struct tplg_pre_processor **tplg_pp, snd_output_type_t type,
+                      const char *output_file);
+void free_pre_preprocessor(struct tplg_pre_processor *tplg_pp);
+#endif