diff --git a/.files b/.files index 4e5cd85..62fa09e 100644 Binary files a/.files and b/.files differ diff --git a/.rev b/.rev index bbf4836..d973fa6 100644 --- a/.rev +++ b/.rev @@ -1581,4 +1581,12 @@ 758568 + + 27de3982a69fe8c4175a40c495f09d45 + 1.2.1.2 + + dimstar_suse + + 766334 + diff --git a/0032-Update-the-attributes.m4-macro-file-from-xine.patch b/0032-Update-the-attributes.m4-macro-file-from-xine.patch new file mode 100644 index 0000000..73a916c --- /dev/null +++ b/0032-Update-the-attributes.m4-macro-file-from-xine.patch @@ -0,0 +1,128 @@ +From 9e2bbccfcc8069a676519149a280f20c1e05f0ac Mon Sep 17 00:00:00 2001 +From: David Ward +Date: Fri, 3 Jan 2020 13:05:51 -0500 +Subject: [PATCH 32/63] Update the attributes.m4 macro file from xine +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This file was imported from the xine project. Update it to the current +revision, which resolves the "no AC_LANG_SOURCE call detected in body" +warnings with Autoconf 2.68 or later. + +Cc: Diego Pettenò +Signed-off-by: David Ward +Signed-off-by: Jaroslav Kysela +--- + m4/attributes.m4 | 33 ++++++++++++++++++++------------- + 1 file changed, 20 insertions(+), 13 deletions(-) + +diff --git a/m4/attributes.m4 b/m4/attributes.m4 +index e86456a468e5..3d9c256a09a2 100644 +--- a/m4/attributes.m4 ++++ b/m4/attributes.m4 +@@ -1,6 +1,6 @@ + dnl Macros to check the presence of generic (non-typed) symbols. +-dnl Copyright (c) 2006-2007 Diego Pettenò +-dnl Copyright (c) 2006-2007 xine project ++dnl Copyright (c) 2006-2008 Diego Pettenò ++dnl Copyright (c) 2006-2008 xine project + dnl + dnl This program is free software; you can redistribute it and/or modify + dnl it under the terms of the GNU General Public License as published by +@@ -25,7 +25,7 @@ dnl License when using or distributing such scripts, even though portions + dnl of the text of the Macro appear in them. The GNU General Public + dnl License (GPL) does govern all other use of the material that + dnl constitutes the Autoconf Macro. +-dnl ++dnl + dnl This special exception to the GPL applies to versions of the + dnl Autoconf Macro released by this project. When you make and + dnl distribute a modified version of the Autoconf Macro, you may extend +@@ -39,7 +39,7 @@ AC_DEFUN([CC_CHECK_CFLAGS_SILENT], [ + AC_CACHE_VAL(AS_TR_SH([cc_cv_cflags_$1]), + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $1" +- AC_COMPILE_IFELSE([int a;], ++ AC_COMPILE_IFELSE([AC_LANG_SOURCE([int a;])], + [eval "AS_TR_SH([cc_cv_cflags_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_cflags_$1])='no'"]) + CFLAGS="$ac_save_CFLAGS" +@@ -71,7 +71,7 @@ AC_DEFUN([CC_CHECK_CFLAG_APPEND], [ + ) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], +- [CFLAGS="$CFLAGS $1"; $2], [$3]) ++ [CFLAGS="$CFLAGS $1"; DEBUG_CFLAGS="$DEBUG_CFLAGS $1"; $2], [$3]) + ]) + + dnl CC_CHECK_CFLAGS_APPEND([FLAG1 FLAG2], [action-if-found], [action-if-not]) +@@ -89,7 +89,7 @@ AC_DEFUN([CC_CHECK_LDFLAGS], [ + AS_TR_SH([cc_cv_ldflags_$1]), + [ac_save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $1" +- AC_LINK_IFELSE([int main() { return 1; }], ++ AC_LINK_IFELSE([AC_LANG_SOURCE([int main() { return 1; }])], + [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_ldflags_$1])="]) + LDFLAGS="$ac_save_LDFLAGS" +@@ -109,14 +109,21 @@ AC_DEFUN([CC_NOUNDEFINED], [ + dnl FreeBSD (et al.) does not complete linking for shared objects when pthreads + dnl are requested, as different implementations are present; to avoid problems + dnl use -Wl,-z,defs only for those platform not behaving this way. +- *-freebsd*) ;; ++ dnl ++ dnl MinGW platforms: for libraries required -no-undefined, ++ dnl use it only for libraries in mingw32-w64 ++ ++ *-freebsd* | *-openbsd*) ;; ++ *-mingw*) ++ LDFLAGS_NOUNDEFINED="-no-undefined" ++ ;; + *) + dnl First of all check for the --no-undefined variant of GNU ld. This allows + dnl for a much more readable commandline, so that people can understand what + dnl it does without going to look for what the heck -z defs does. +- for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do ++ for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do + CC_CHECK_LDFLAGS([$possible_flags], [LDFLAGS_NOUNDEFINED="$possible_flags"]) +- break ++ if test "x$LDFLAGS_NOUNDEFINED" = "x"; then break; fi + done + ;; + esac +@@ -147,7 +154,7 @@ AC_DEFUN([CC_CHECK_ATTRIBUTE], [ + AS_TR_SH([cc_cv_attribute_$1]), + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" +- AC_COMPILE_IFELSE([$3], ++ AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])], + [eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_attribute_$1])='no'"]) + CFLAGS="$ac_save_CFLAGS" +@@ -257,7 +264,7 @@ AC_DEFUN([CC_FLAG_VISIBILITY], [ + cc_cv_flag_visibility='yes', + cc_cv_flag_visibility='no') + CFLAGS="$cc_flag_visibility_save_CFLAGS"]) +- ++ + AS_IF([test "x$cc_cv_flag_visibility" = "xyes"], + [AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1, + [Define this if the compiler supports the -fvisibility flag]) +@@ -295,11 +302,11 @@ AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [ + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + for cc_attribute_align_try in 64 32 16 8 4 2; do +- AC_COMPILE_IFELSE([ ++ AC_COMPILE_IFELSE([AC_LANG_SOURCE([ + int main() { + static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0; + return c; +- }], [cc_cv_attribute_aligned=$cc_attribute_align_try; break]) ++ }])], [cc_cv_attribute_aligned=$cc_attribute_align_try; break]) + done + CFLAGS="$ac_save_CFLAGS" + ]) +-- +2.16.4 + diff --git a/0033-topology-avoid-to-use-the-atoi-directly-when-expecte.patch b/0033-topology-avoid-to-use-the-atoi-directly-when-expecte.patch new file mode 100644 index 0000000..493513b --- /dev/null +++ b/0033-topology-avoid-to-use-the-atoi-directly-when-expecte.patch @@ -0,0 +1,763 @@ +From f373bf1f6eea0f2037a7714e9b55aa65fa00b889 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sat, 14 Dec 2019 13:36:09 +0100 +Subject: [PATCH 33/63] topology: avoid to use the atoi() directly when + expected + +Signed-off-by: Jaroslav Kysela +--- + src/topology/channel.c | 10 ++-- + src/topology/ctl.c | 37 ++++++------ + src/topology/dapm.c | 32 +++++----- + src/topology/data.c | 14 ++--- + src/topology/elem.c | 9 ++- + src/topology/ops.c | 2 +- + src/topology/parser.c | 29 +++++++++ + src/topology/pcm.c | 148 ++++++++++++++++++++-------------------------- + src/topology/tplg_local.h | 2 + + 9 files changed, 150 insertions(+), 133 deletions(-) + +diff --git a/src/topology/channel.c b/src/topology/channel.c +index 60f7e219a674..4569eb31e0c5 100644 +--- a/src/topology/channel.c ++++ b/src/topology/channel.c +@@ -80,8 +80,8 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg, + snd_config_iterator_t i, next; + snd_config_t *n; + struct snd_soc_tplg_channel *channel = private; +- const char *id, *value; +- int channel_id; ++ const char *id; ++ int channel_id, value; + + if (tplg->channel_idx >= SND_SOC_TPLG_MAX_CHAN) + return -EINVAL; +@@ -109,13 +109,13 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg, + continue; + + /* get value */ +- if (snd_config_get_string(n, &value) < 0) ++ if (tplg_get_integer(n, &value, 0) < 0) + continue; + + if (strcmp(id, "reg") == 0) +- channel->reg = atoi(value); ++ channel->reg = value; + else if (strcmp(id, "shift") == 0) +- channel->shift = atoi(value); ++ channel->shift = value; + + tplg_dbg("\t\t%s = %s\n", id, value); + } +diff --git a/src/topology/ctl.c b/src/topology/ctl.c +index 1db0b16eac95..e1896f46c576 100644 +--- a/src/topology/ctl.c ++++ b/src/topology/ctl.c +@@ -286,7 +286,8 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem) + snd_config_t *n; + struct snd_soc_tplg_ctl_tlv *tplg_tlv; + struct snd_soc_tplg_tlv_dbscale *scale; +- const char *id = NULL, *value = NULL; ++ const char *id = NULL; ++ int val; + + tplg_dbg(" scale: %s\n", elem->id); + +@@ -310,18 +311,18 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem) + } + + /* get value */ +- if (snd_config_get_string(n, &value) < 0) ++ if (tplg_get_integer(n, &val, 0)) + continue; + +- tplg_dbg("\t%s = %s\n", id, value); ++ tplg_dbg("\t%s = %i\n", id, val); + + /* get TLV data */ + if (strcmp(id, "min") == 0) +- scale->min = atoi(value); ++ scale->min = val; + else if (strcmp(id, "step") == 0) +- scale->step = atoi(value); ++ scale->step = val; + else if (strcmp(id, "mute") == 0) +- scale->mute = atoi(value); ++ scale->mute = val; + else + SNDERR("error: unknown key %s\n", id); + } +@@ -372,7 +373,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg, + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id, *val = NULL; +- int err; ++ int err, ival; + bool access_set = false, tlv_set = false; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_BYTES); +@@ -398,37 +399,37 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg, + continue; + + if (strcmp(id, "base") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + +- be->base = atoi(val); ++ be->base = ival; + tplg_dbg("\t%s: %d\n", id, be->base); + continue; + } + + if (strcmp(id, "num_regs") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + +- be->num_regs = atoi(val); ++ be->num_regs = ival; + tplg_dbg("\t%s: %d\n", id, be->num_regs); + continue; + } + + if (strcmp(id, "max") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + +- be->max = atoi(val); ++ be->max = ival; + tplg_dbg("\t%s: %d\n", id, be->max); + continue; + } + + if (strcmp(id, "mask") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (tplg_get_integer(n, &ival, 16)) + return -EINVAL; + +- be->mask = strtol(val, NULL, 16); ++ be->mask = ival; + tplg_dbg("\t%s: %d\n", id, be->mask); + continue; + } +@@ -598,7 +599,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg, + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id, *val = NULL; +- int err, j; ++ int err, j, ival; + bool access_set = false, tlv_set = false; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_MIXER); +@@ -647,10 +648,10 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg, + } + + if (strcmp(id, "max") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + +- mc->max = atoi(val); ++ mc->max = ival; + tplg_dbg("\t%s: %d\n", id, mc->max); + continue; + } +diff --git a/src/topology/dapm.c b/src/topology/dapm.c +index ce46913192df..c6fd793d0d72 100644 +--- a/src/topology/dapm.c ++++ b/src/topology/dapm.c +@@ -426,7 +426,7 @@ int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg, + snd_config_iterator_t i, next; + snd_config_t *n; + int err; +- const char *graph_id, *val = NULL; ++ const char *graph_id; + int index = -1; + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { +@@ -445,9 +445,10 @@ int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg, + } + + if (strcmp(id, "index") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (tplg_get_integer(n, &index, 0)) ++ return -EINVAL; ++ if (index < 0) + return -EINVAL; +- index = atoi(val); + } + + if (strcmp(id, "lines") == 0) { +@@ -479,6 +480,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + snd_config_t *n; + const char *id, *val = NULL; + int widget_type, err; ++ int ival; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_DAPM_WIDGET); + if (!elem) +@@ -540,55 +542,55 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + } + + if (strcmp(id, "shift") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + +- widget->shift = atoi(val); ++ widget->shift = ival; + tplg_dbg("\t%s: %d\n", id, widget->shift); + continue; + } + + if (strcmp(id, "reg") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + +- widget->reg = atoi(val); ++ widget->reg = ival; + tplg_dbg("\t%s: %d\n", id, widget->reg); + continue; + } + + if (strcmp(id, "invert") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + +- widget->invert = atoi(val); ++ widget->invert = ival; + tplg_dbg("\t%s: %d\n", id, widget->invert); + continue; + } + + if (strcmp(id, "subseq") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + +- widget->subseq= atoi(val); ++ widget->subseq = ival; + tplg_dbg("\t%s: %d\n", id, widget->subseq); + continue; + } + + if (strcmp(id, "event_type") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + +- widget->event_type = atoi(val); ++ widget->event_type = ival; + tplg_dbg("\t%s: %d\n", id, widget->event_type); + continue; + } + + if (strcmp(id, "event_flags") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + +- widget->event_flags = atoi(val); ++ widget->event_flags = ival; + tplg_dbg("\t%s: %d\n", id, widget->event_flags); + continue; + } +diff --git a/src/topology/data.c b/src/topology/data.c +index 729ce1f4c0b6..0edfe54f3383 100644 +--- a/src/topology/data.c ++++ b/src/topology/data.c +@@ -800,10 +800,10 @@ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, + { + snd_config_iterator_t i, next; + snd_config_t *n; +- const char *id, *value; ++ const char *id; + struct tplg_elem *elem; + struct tplg_vendor_tokens *tokens; +- int num_tokens = 0; ++ int num_tokens = 0, value; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_TOKEN); + if (!elem) +@@ -830,12 +830,12 @@ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, + if (snd_config_get_id(n, &id) < 0) + continue; + +- if (snd_config_get_string(n, &value) < 0) ++ if (tplg_get_integer(n, &value, 0)) + continue; + + snd_strlcpy(tokens->token[tokens->num_tokens].id, id, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); +- tokens->token[tokens->num_tokens].value = atoi(value); ++ tokens->token[tokens->num_tokens].value = value; + tplg_dbg("\t\t %s : %d\n", tokens->token[tokens->num_tokens].id, + tokens->token[tokens->num_tokens].value); + tokens->num_tokens++; +@@ -1013,7 +1013,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id, *val = NULL; +- int err = 0; ++ int err = 0, ival; + struct tplg_elem *elem; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_DATA); +@@ -1071,10 +1071,10 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, + } + + if (strcmp(id, "type") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + +- elem->vendor_type = atoi(val); ++ elem->vendor_type = ival; + tplg_dbg("\t%s: %d\n", id, elem->index); + continue; + } +diff --git a/src/topology/elem.c b/src/topology/elem.c +index d8618cc9d983..f2076f7958aa 100644 +--- a/src/topology/elem.c ++++ b/src/topology/elem.c +@@ -153,7 +153,7 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, + enum snd_tplg_type type) + { + struct tplg_elem *elem; +- const char *id, *val = NULL; ++ const char *id; + int obj_size = 0; + void *obj; + snd_config_iterator_t i, next; +@@ -178,11 +178,14 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, + if (snd_config_get_id(n, &id)) + continue; + if (strcmp(id, "index") == 0) { +- if (snd_config_get_string(n, &val) < 0) { ++ if (tplg_get_integer(n, &elem->index, 0)) { ++ free(elem); ++ return NULL; ++ } ++ if (elem->index < 0) { + free(elem); + return NULL; + } +- elem->index = atoi(val); + } + } + } else if (name != NULL) +diff --git a/src/topology/ops.c b/src/topology/ops.c +index 0f4295a2e50c..073acdcb1453 100644 +--- a/src/topology/ops.c ++++ b/src/topology/ops.c +@@ -42,7 +42,7 @@ static int lookup_ops(const char *c) + } + + /* cant find string name in our table so we use its ID number */ +- return atoi(c); ++ return strtol(c, NULL, 0); + } + + /* Parse Control operations. Ops can come from standard names above or +diff --git a/src/topology/parser.c b/src/topology/parser.c +index 5940692da2e2..7e657809307d 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -21,6 +21,35 @@ + #include "list.h" + #include "tplg_local.h" + ++/* ++ * Get integer value ++ */ ++int tplg_get_integer(snd_config_t *n, int *val, int base) ++{ ++ const char *str; ++ long lval; ++ int err; ++ ++ switch (snd_config_get_type(n)) { ++ case SND_CONFIG_TYPE_INTEGER: ++ err = snd_config_get_integer(n, &lval); ++ if (err < 0) ++ return err; ++ if (lval < INT_MIN || lval > INT_MAX) ++ return -EINVAL; ++ *val = lval; ++ return err; ++ case SND_CONFIG_TYPE_STRING: ++ err = snd_config_get_string(n, &str); ++ if (err < 0) ++ return err; ++ *val = strtol(str, NULL, base); ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ + /* + * Parse compound + */ +diff --git a/src/topology/pcm.c b/src/topology/pcm.c +index 98a8df979e24..d6c52b4752ee 100644 +--- a/src/topology/pcm.c ++++ b/src/topology/pcm.c +@@ -368,6 +368,24 @@ static int split_rate(struct snd_soc_tplg_stream_caps *caps, char *str) + return 0; + } + ++static int parse_unsigned(snd_config_t *n, unsigned int *dst) ++{ ++ int ival; ++ ++ if (tplg_get_integer(n, &ival, 0) < 0) ++ return -EINVAL; ++ ++ *dst = ival; ++#if TPLG_DEBUG ++ { ++ const char *id; ++ if (snd_config_get_id(n, &id) >= 0) ++ tplg_dbg("\t\t%s: %d\n", id, *dst); ++ } ++#endif ++ return 0; ++} ++ + /* Parse pcm stream capabilities */ + int tplg_parse_stream_caps(snd_tplg_t *tplg, + snd_config_t *cfg, +@@ -402,10 +420,10 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg, + if (id[0] == '#') + continue; + +- if (snd_config_get_string(n, &val) < 0) +- return -EINVAL; +- + if (strcmp(id, "formats") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ + s = strdup(val); + if (s == NULL) + return -ENOMEM; +@@ -421,6 +439,9 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg, + } + + if (strcmp(id, "rates") == 0) { ++ if (snd_config_get_string(n, &val) < 0) ++ return -EINVAL; ++ + s = strdup(val); + if (!s) + return -ENOMEM; +@@ -436,68 +457,68 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg, + } + + if (strcmp(id, "rate_min") == 0) { +- sc->rate_min = atoi(val); +- tplg_dbg("\t\t%s: %d\n", id, sc->rate_min); ++ if (parse_unsigned(n, &sc->rate_min)) ++ return -EINVAL; + continue; + } + + if (strcmp(id, "rate_max") == 0) { +- sc->rate_max = atoi(val); +- tplg_dbg("\t\t%s: %d\n", id, sc->rate_max); ++ if (parse_unsigned(n, &sc->rate_max)) ++ return -EINVAL; + continue; + } + + if (strcmp(id, "channels_min") == 0) { +- sc->channels_min = atoi(val); +- tplg_dbg("\t\t%s: %d\n", id, sc->channels_min); ++ if (parse_unsigned(n, &sc->channels_min)) ++ return -EINVAL; + continue; + } + + if (strcmp(id, "channels_max") == 0) { +- sc->channels_max = atoi(val); +- tplg_dbg("\t\t%s: %d\n", id, sc->channels_max); ++ if (parse_unsigned(n, &sc->channels_max)) ++ return -EINVAL; + continue; + } + + if (strcmp(id, "periods_min") == 0) { +- sc->periods_min = atoi(val); +- tplg_dbg("\t\t%s: %d\n", id, sc->periods_min); ++ if (parse_unsigned(n, &sc->periods_min)) ++ return -EINVAL; + continue; + } + + if (strcmp(id, "periods_max") == 0) { +- sc->periods_max = atoi(val); +- tplg_dbg("\t\t%s: %d\n", id, sc->periods_max); ++ if (parse_unsigned(n, &sc->periods_max)) ++ return -EINVAL; + continue; + } + + if (strcmp(id, "period_size_min") == 0) { +- sc->period_size_min = atoi(val); +- tplg_dbg("\t\t%s: %d\n", id, sc->period_size_min); ++ if (parse_unsigned(n, &sc->period_size_min)) ++ return -EINVAL; + continue; + } + + if (strcmp(id, "period_size_max") == 0) { +- sc->period_size_max = atoi(val); +- tplg_dbg("\t\t%s: %d\n", id, sc->period_size_max); ++ if (parse_unsigned(n, &sc->period_size_max)) ++ return -EINVAL; + continue; + } + + if (strcmp(id, "buffer_size_min") == 0) { +- sc->buffer_size_min = atoi(val); +- tplg_dbg("\t\t%s: %d\n", id, sc->buffer_size_min); ++ if (parse_unsigned(n, &sc->buffer_size_min)) ++ return -EINVAL; + continue; + } + + if (strcmp(id, "buffer_size_max") == 0) { +- sc->buffer_size_max = atoi(val); +- tplg_dbg("\t\t%s: %d\n", id, sc->buffer_size_max); ++ if (parse_unsigned(n, &sc->buffer_size_max)) ++ return -EINVAL; + continue; + } + + if (strcmp(id, "sig_bits") == 0) { +- sc->sig_bits = atoi(val); +- tplg_dbg("\t\t%s: %d\n", id, sc->sig_bits); ++ if (parse_unsigned(n, &sc->sig_bits)) ++ return -EINVAL; + continue; + } + +@@ -674,11 +695,8 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, + continue; + + if (strcmp(id, "id") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &pcm->pcm_id)) + return -EINVAL; +- +- pcm->pcm_id = atoi(val); +- tplg_dbg("\t%s: %d\n", id, pcm->pcm_id); + continue; + } + +@@ -784,30 +802,21 @@ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg, + continue; + + if (strcmp(id, "id") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &dai->dai_id)) + return -EINVAL; +- +- dai->dai_id = atoi(val); +- tplg_dbg("\t%s: %d\n", id, dai->dai_id); + continue; + } + + if (strcmp(id, "playback") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &dai->playback)) + return -EINVAL; +- +- dai->playback = atoi(val); +- tplg_dbg("\t%s: %d\n", id, dai->playback); + continue; + } + + + if (strcmp(id, "capture") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &dai->capture)) + return -EINVAL; +- +- dai->capture = atoi(val); +- tplg_dbg("\t%s: %d\n", id, dai->capture); + continue; + } + +@@ -949,11 +958,8 @@ int tplg_parse_link(snd_tplg_t *tplg, + continue; + + if (strcmp(id, "id") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &link->id)) + return -EINVAL; +- +- link->id = atoi(val); +- tplg_dbg("\t%s: %d\n", id, link->id); + continue; + } + +@@ -975,10 +981,8 @@ int tplg_parse_link(snd_tplg_t *tplg, + } + + if (strcmp(id, "default_hw_conf_id") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &link->default_hw_config_id)) + return -EINVAL; +- +- link->default_hw_config_id = atoi(val); + continue; + } + +@@ -1030,7 +1034,7 @@ int tplg_parse_cc(snd_tplg_t *tplg, + struct tplg_elem *elem; + snd_config_iterator_t i, next; + snd_config_t *n; +- const char *id, *val = NULL; ++ const char *id; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_CC); + if (!elem) +@@ -1054,11 +1058,8 @@ int tplg_parse_cc(snd_tplg_t *tplg, + continue; + + if (strcmp(id, "id") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &link->id)) + return -EINVAL; +- +- link->id = atoi(val); +- tplg_dbg("\t%s: %d\n", id, link->id); + continue; + } + +@@ -1130,11 +1131,8 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + continue; + + if (strcmp(id, "id") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &hw_cfg->id)) + return -EINVAL; +- +- hw_cfg->id = atoi(val); +- tplg_dbg("\t%s: %d\n", id, hw_cfg->id); + continue; + } + +@@ -1173,10 +1171,8 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + + if (strcmp(id, "bclk_freq") == 0 || + strcmp(id, "bclk_rate") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &hw_cfg->bclk_rate)) + return -EINVAL; +- +- hw_cfg->bclk_rate = atoi(val); + continue; + } + +@@ -1223,19 +1219,15 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + + if (strcmp(id, "fsync_freq") == 0 || + strcmp(id, "fsync_rate") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &hw_cfg->fsync_rate)) + return -EINVAL; +- +- hw_cfg->fsync_rate = atoi(val); + continue; + } + + if (strcmp(id, "mclk_freq") == 0 || + strcmp(id, "mclk_rate") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &hw_cfg->mclk_rate)) + return -EINVAL; +- +- hw_cfg->mclk_rate = atoi(val); + continue; + } + +@@ -1275,50 +1267,38 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + } + + if (strcmp(id, "tdm_slots") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &hw_cfg->tdm_slots)) + return -EINVAL; +- +- hw_cfg->tdm_slots = atoi(val); + continue; + } + + if (strcmp(id, "tdm_slot_width") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &hw_cfg->tdm_slot_width)) + return -EINVAL; +- +- hw_cfg->tdm_slot_width = atoi(val); + continue; + } + + if (strcmp(id, "tx_slots") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &hw_cfg->tx_slots)) + return -EINVAL; +- +- hw_cfg->tx_slots = atoi(val); + continue; + } + + if (strcmp(id, "rx_slots") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &hw_cfg->rx_slots)) + return -EINVAL; +- +- hw_cfg->rx_slots = atoi(val); + continue; + } + + if (strcmp(id, "tx_channels") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &hw_cfg->tx_channels)) + return -EINVAL; +- +- hw_cfg->tx_channels = atoi(val); + continue; + } + + if (strcmp(id, "rx_channels") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ if (parse_unsigned(n, &hw_cfg->rx_channels)) + return -EINVAL; +- +- hw_cfg->rx_channels = atoi(val); + continue; + } + +diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h +index 101491a42e60..991e0b4121bd 100644 +--- a/src/topology/tplg_local.h ++++ b/src/topology/tplg_local.h +@@ -280,6 +280,8 @@ struct tplg_elem *tplg_elem_lookup(struct list_head *base, + struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, + snd_config_t *cfg, const char *name, enum snd_tplg_type type); + ++int tplg_get_integer(snd_config_t *n, int *val, int base); ++ + int tplg_parse_channel(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + snd_config_t *cfg, void *private); + +-- +2.16.4 + diff --git a/0034-topology-use-snd_config_get_bool-instead-own-impleme.patch b/0034-topology-use-snd_config_get_bool-instead-own-impleme.patch new file mode 100644 index 0000000..fe43544 --- /dev/null +++ b/0034-topology-use-snd_config_get_bool-instead-own-impleme.patch @@ -0,0 +1,207 @@ +From 5925a6d870331c631f85ed4e18a8c5e6459b3c36 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sat, 14 Dec 2019 13:50:04 +0100 +Subject: [PATCH 34/63] topology: use snd_config_get_bool() instead own + implementation + +Signed-off-by: Jaroslav Kysela +--- + src/topology/ctl.c | 9 +++------ + src/topology/dapm.c | 9 ++++----- + src/topology/data.c | 15 ++++++++++++--- + src/topology/pcm.c | 29 +++++++++++++++-------------- + 4 files changed, 34 insertions(+), 28 deletions(-) + +diff --git a/src/topology/ctl.c b/src/topology/ctl.c +index e1896f46c576..9190efefb575 100644 +--- a/src/topology/ctl.c ++++ b/src/topology/ctl.c +@@ -657,13 +657,10 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg, + } + + if (strcmp(id, "invert") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ ival = snd_config_get_bool(n); ++ if (ival < 0) + return -EINVAL; +- +- if (strcmp(val, "true") == 0) +- mc->invert = 1; +- else if (strcmp(val, "false") == 0) +- mc->invert = 0; ++ mc->invert = ival; + + tplg_dbg("\t%s: %d\n", id, mc->invert); + continue; +diff --git a/src/topology/dapm.c b/src/topology/dapm.c +index c6fd793d0d72..ad7092107896 100644 +--- a/src/topology/dapm.c ++++ b/src/topology/dapm.c +@@ -479,8 +479,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id, *val = NULL; +- int widget_type, err; +- int ival; ++ int widget_type, err, ival; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_DAPM_WIDGET); + if (!elem) +@@ -531,11 +530,11 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + } + + if (strcmp(id, "no_pm") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ ival = snd_config_get_bool(n); ++ if (ival < 0) + return -EINVAL; + +- if (strcmp(val, "true") == 0) +- widget->reg = -1; ++ widget->reg = ival ? -1 : 0; + + tplg_dbg("\t%s: %s\n", id, val); + continue; +diff --git a/src/topology/data.c b/src/topology/data.c +index 0edfe54f3383..6b1337b39bad 100644 +--- a/src/topology/data.c ++++ b/src/topology/data.c +@@ -557,6 +557,7 @@ static int parse_tuple_set(snd_config_t *cfg, + unsigned int type, num_tuples = 0; + struct tplg_tuple *tuple; + unsigned long int tuple_val; ++ int ival; + + snd_config_get_id(cfg, &id); + +@@ -607,25 +608,33 @@ static int parse_tuple_set(snd_config_t *cfg, + + switch (type) { + case SND_SOC_TPLG_TUPLE_TYPE_UUID: ++ if (snd_config_get_string(n, &value) < 0) ++ continue; + if (get_uuid(value, tuple->uuid) < 0) + goto err; + break; + + case SND_SOC_TPLG_TUPLE_TYPE_STRING: ++ if (snd_config_get_string(n, &value) < 0) ++ continue; + snd_strlcpy(tuple->string, value, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + tplg_dbg("\t\t%s = %s\n", tuple->token, tuple->string); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_BOOL: +- if (strcmp(value, "true") == 0) +- tuple->value = 1; ++ ival = snd_config_get_bool(n); ++ if (ival < 0) ++ continue; ++ tuple->value = ival; + tplg_dbg("\t\t%s = %d\n", tuple->token, tuple->value); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_BYTE: + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + case SND_SOC_TPLG_TUPLE_TYPE_WORD: ++ if (snd_config_get_string(n, &value) < 0) ++ continue; + errno = 0; + /* no support for negative value */ + tuple_val = strtoul(value, NULL, 0); +@@ -1012,7 +1021,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, + { + snd_config_iterator_t i, next; + snd_config_t *n; +- const char *id, *val = NULL; ++ const char *id; + int err = 0, ival; + struct tplg_elem *elem; + +diff --git a/src/topology/pcm.c b/src/topology/pcm.c +index d6c52b4752ee..6364e24f3c43 100644 +--- a/src/topology/pcm.c ++++ b/src/topology/pcm.c +@@ -669,8 +669,8 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, + struct tplg_elem *elem; + snd_config_iterator_t i, next; + snd_config_t *n; +- const char *id, *val = NULL; +- int err; ++ const char *id; ++ int err, ival; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_PCM); + if (!elem) +@@ -709,11 +709,11 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, + } + + if (strcmp(id, "compress") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ ival = snd_config_get_bool(n); ++ if (ival < 0) + return -EINVAL; + +- if (strcmp(val, "true") == 0) +- pcm->compress = 1; ++ pcm->compress = ival; + + tplg_dbg("\t%s: %s\n", id, val); + continue; +@@ -1107,7 +1107,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id, *val = NULL; +- int ret; ++ int ret, ival; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_HW_CONFIG); + if (!elem) +@@ -1178,11 +1178,11 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + + if (strcmp(id, "bclk_invert") == 0 || + strcmp(id, "invert_bclk") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ ival = snd_config_get_bool(n); ++ if (ival < 0) + return -EINVAL; + +- if (!strcmp(val, "true")) +- hw_cfg->invert_bclk = true; ++ hw_cfg->invert_bclk = ival; + continue; + } + +@@ -1209,11 +1209,11 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + + if (strcmp(id, "fsync_invert") == 0 || + strcmp(id, "invert_fsync") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ ival = snd_config_get_bool(n); ++ if (ival < 0) + return -EINVAL; + +- if (!strcmp(val, "true")) +- hw_cfg->invert_fsync = true; ++ hw_cfg->invert_fsync = ival; + continue; + } + +@@ -1254,10 +1254,11 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + + if (strcmp(id, "pm_gate_clocks") == 0 || + strcmp(id, "clock_gated") == 0) { +- if (snd_config_get_string(n, &val) < 0) ++ ival = snd_config_get_bool(n); ++ if (ival < 0) + return -EINVAL; + +- if (!strcmp(val, "true")) ++ if (ival) + hw_cfg->clock_gated = + SND_SOC_TPLG_DAI_CLK_GATE_GATED; + else +-- +2.16.4 + diff --git a/0035-topology-fix-tplg_get_integer-handle-errno-ERANGE.patch b/0035-topology-fix-tplg_get_integer-handle-errno-ERANGE.patch new file mode 100644 index 0000000..a41fe67 --- /dev/null +++ b/0035-topology-fix-tplg_get_integer-handle-errno-ERANGE.patch @@ -0,0 +1,39 @@ +From 1047a5f3c0d39a3b0579db027f52d7facdf44077 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sat, 14 Dec 2019 13:52:18 +0100 +Subject: [PATCH 35/63] topology: fix tplg_get_integer() - handle errno == + ERANGE + +Signed-off-by: Jaroslav Kysela +--- + src/topology/parser.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/src/topology/parser.c b/src/topology/parser.c +index 7e657809307d..667c8d45517b 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -36,14 +36,19 @@ int tplg_get_integer(snd_config_t *n, int *val, int base) + if (err < 0) + return err; + if (lval < INT_MIN || lval > INT_MAX) +- return -EINVAL; ++ return -ERANGE; + *val = lval; + return err; + case SND_CONFIG_TYPE_STRING: + err = snd_config_get_string(n, &str); + if (err < 0) + return err; ++ errno = 0; + *val = strtol(str, NULL, base); ++ if (errno == ERANGE) ++ return -ERANGE; ++ if (errno && *val == 0) ++ return -EINVAL; + return 0; + default: + return -EINVAL; +-- +2.16.4 + diff --git a/0036-topology-add-tplg_get_unsigned-function.patch b/0036-topology-add-tplg_get_unsigned-function.patch new file mode 100644 index 0000000..6703729 --- /dev/null +++ b/0036-topology-add-tplg_get_unsigned-function.patch @@ -0,0 +1,172 @@ +From 14e43a11873d14ec6f16967c83629237ef44ac38 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sat, 14 Dec 2019 14:05:49 +0100 +Subject: [PATCH 36/63] topology: add tplg_get_unsigned() function + +Signed-off-by: Jaroslav Kysela +--- + src/topology/data.c | 19 +++++-------------- + src/topology/parser.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ + src/topology/pcm.c | 13 ++----------- + src/topology/tplg_local.h | 1 + + 4 files changed, 55 insertions(+), 25 deletions(-) + +diff --git a/src/topology/data.c b/src/topology/data.c +index 6b1337b39bad..9807445e8c37 100644 +--- a/src/topology/data.c ++++ b/src/topology/data.c +@@ -556,7 +556,7 @@ static int parse_tuple_set(snd_config_t *cfg, + struct tplg_tuple_set *set; + unsigned int type, num_tuples = 0; + struct tplg_tuple *tuple; +- unsigned long int tuple_val; ++ unsigned int tuple_val; + int ival; + + snd_config_get_id(cfg, &id); +@@ -598,10 +598,6 @@ static int parse_tuple_set(snd_config_t *cfg, + if (snd_config_get_id(n, &id) < 0) + continue; + +- /* get value */ +- if (snd_config_get_string(n, &value) < 0) +- continue; +- + tuple = &set->tuple[set->num_tuples]; + snd_strlcpy(tuple->token, id, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); +@@ -633,14 +629,9 @@ static int parse_tuple_set(snd_config_t *cfg, + case SND_SOC_TPLG_TUPLE_TYPE_BYTE: + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + case SND_SOC_TPLG_TUPLE_TYPE_WORD: +- if (snd_config_get_string(n, &value) < 0) +- continue; +- errno = 0; +- /* no support for negative value */ +- tuple_val = strtoul(value, NULL, 0); +- if ((errno == ERANGE && tuple_val == ULONG_MAX) +- || (errno != 0 && tuple_val == 0)) { +- SNDERR("error: tuple %s:strtoul fail\n", id); ++ ival = tplg_get_unsigned(n, &tuple_val, 0); ++ if (ival < 0) { ++ SNDERR("error: tuple %s: %s\n", id, snd_strerror(ival)); + goto err; + } + +@@ -654,7 +645,7 @@ static int parse_tuple_set(snd_config_t *cfg, + goto err; + } + +- tuple->value = (unsigned int) tuple_val; ++ tuple->value = tuple_val; + tplg_dbg("\t\t%s = 0x%x\n", tuple->token, tuple->value); + break; + +diff --git a/src/topology/parser.c b/src/topology/parser.c +index 667c8d45517b..f56ad97e42b9 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -55,6 +55,53 @@ int tplg_get_integer(snd_config_t *n, int *val, int base) + } + } + ++/* ++ * Get unsigned integer value ++ */ ++int tplg_get_unsigned(snd_config_t *n, unsigned *val, int base) ++{ ++ const char *str; ++ long lval; ++ long long llval; ++ unsigned long uval; ++ int err; ++ ++ switch (snd_config_get_type(n)) { ++ case SND_CONFIG_TYPE_INTEGER: ++ err = snd_config_get_integer(n, &lval); ++ if (err < 0) ++ return err; ++ if (lval < 0 || lval > UINT_MAX) ++ return -ERANGE; ++ *val = lval; ++ return err; ++ case SND_CONFIG_TYPE_INTEGER64: ++ err = snd_config_get_integer64(n, &llval); ++ if (err < 0) ++ return err; ++ if (llval < 0 || llval > UINT_MAX) ++ return -ERANGE; ++ *val = llval; ++ return err; ++ case SND_CONFIG_TYPE_STRING: ++ err = snd_config_get_string(n, &str); ++ if (err < 0) ++ return err; ++ errno = 0; ++ uval = strtoul(str, NULL, base); ++ if (errno == ERANGE && uval == ULONG_MAX) ++ return -ERANGE; ++ if (errno && uval == 0) ++ return -EINVAL; ++ if (uval > UINT_MAX) ++ return -ERANGE; ++ *val = uval; ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ + /* + * Parse compound + */ +diff --git a/src/topology/pcm.c b/src/topology/pcm.c +index 6364e24f3c43..9b87549cabbd 100644 +--- a/src/topology/pcm.c ++++ b/src/topology/pcm.c +@@ -606,8 +606,7 @@ static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + struct snd_soc_tplg_pcm *pcm = elem->pcm; + snd_config_iterator_t i, next; + snd_config_t *n; +- const char *id, *value = NULL; +- unsigned long int id_val; ++ const char *id; + + snd_config_get_id(cfg, &id); + tplg_dbg("\t\tFE DAI %s:\n", id); +@@ -622,19 +621,11 @@ static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + continue; + + if (strcmp(id, "id") == 0) { +- if (snd_config_get_string(n, &value) < 0) +- continue; +- errno = 0; +- /* no support for negative value */ +- id_val = strtoul(value, NULL, 0); +- if ((errno == ERANGE && id_val == ULONG_MAX) +- || (errno != 0 && id_val == 0) +- || id_val > UINT_MAX) { ++ if (tplg_get_unsigned(n, &pcm->dai_id, 0)) { + SNDERR("error: invalid fe dai ID\n"); + return -EINVAL; + } + +- pcm->dai_id = (int) id_val; + tplg_dbg("\t\t\tindex: %d\n", pcm->dai_id); + } + } +diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h +index 991e0b4121bd..e16c78d49709 100644 +--- a/src/topology/tplg_local.h ++++ b/src/topology/tplg_local.h +@@ -281,6 +281,7 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, + snd_config_t *cfg, const char *name, enum snd_tplg_type type); + + int tplg_get_integer(snd_config_t *n, int *val, int base); ++int tplg_get_unsigned(snd_config_t *n, unsigned *val, int base); + + int tplg_parse_channel(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + snd_config_t *cfg, void *private); +-- +2.16.4 + diff --git a/0037-topology-convert-builder-to-use-the-mallocated-memor.patch b/0037-topology-convert-builder-to-use-the-mallocated-memor.patch new file mode 100644 index 0000000..7ee7750 --- /dev/null +++ b/0037-topology-convert-builder-to-use-the-mallocated-memor.patch @@ -0,0 +1,296 @@ +From 39fb37fef5bd3b3fa7a63e06a5f0a147197fddb9 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sat, 14 Dec 2019 19:13:53 +0100 +Subject: [PATCH 37/63] topology: convert builder to use the mallocated memory + +Signed-off-by: Jaroslav Kysela +--- + src/topology/builder.c | 88 +++++++++++++++++++++++++++++++++++++---------- + src/topology/parser.c | 74 ++++++++++++++++----------------------- + src/topology/tplg_local.h | 5 +-- + 3 files changed, 103 insertions(+), 64 deletions(-) + +diff --git a/src/topology/builder.c b/src/topology/builder.c +index 1a22a453695a..40943b56e4aa 100644 +--- a/src/topology/builder.c ++++ b/src/topology/builder.c +@@ -29,7 +29,7 @@ static void verbose(snd_tplg_t *tplg, const char *fmt, ...) + return; + + va_start(va, fmt); +- fprintf(stdout, "0x%6.6zx/%6.6zd - ", tplg->out_pos, tplg->out_pos); ++ fprintf(stdout, "0x%6.6zx/%6.6zd - ", tplg->bin_pos, tplg->bin_pos); + vfprintf(stdout, fmt, va); + va_end(va); + } +@@ -37,18 +37,11 @@ static void verbose(snd_tplg_t *tplg, const char *fmt, ...) + /* write a block, track the position */ + static ssize_t twrite(snd_tplg_t *tplg, void *data, size_t data_size) + { +- ssize_t r = write(tplg->out_fd, data, data_size); +- if (r != (ssize_t)data_size) { +- if (r < 0) { +- SNDERR("error: unable to write: %s", strerror(errno)); +- return -errno; +- } +- tplg->out_pos += r; +- SNDERR("error: unable to write (partial)"); ++ if (tplg->bin_pos + data_size > tplg->bin_size) + return -EIO; +- } +- tplg->out_pos += r; +- return r; ++ memcpy(tplg->bin + tplg->bin_pos, data, data_size); ++ tplg->bin_pos += data_size; ++ return data_size; + } + + /* write out block header to output file */ +@@ -71,12 +64,12 @@ static ssize_t write_block_header(snd_tplg_t *tplg, unsigned int type, + hdr.count = count; + + /* make sure file offset is aligned with the calculated HDR offset */ +- if (tplg->out_pos != tplg->next_hdr_pos) { ++ if (tplg->bin_pos != tplg->next_hdr_pos) { + SNDERR("error: New header is at offset 0x%zx but file" + " offset 0x%zx is %s by %ld bytes\n", +- tplg->next_hdr_pos, tplg->out_pos, +- tplg->out_pos > tplg->next_hdr_pos ? "ahead" : "behind", +- labs(tplg->out_pos - tplg->next_hdr_pos)); ++ tplg->next_hdr_pos, tplg->bin_pos, ++ tplg->bin_pos > tplg->next_hdr_pos ? "ahead" : "behind", ++ labs(tplg->bin_pos - tplg->next_hdr_pos)); + return -EINVAL; + } + +@@ -163,6 +156,41 @@ static int write_elem_block(snd_tplg_t *tplg, + return 0; + } + ++static size_t calc_manifest_size(snd_tplg_t *tplg) ++{ ++ return sizeof(struct snd_soc_tplg_hdr) + ++ sizeof(tplg->manifest) + ++ tplg->manifest.priv.size; ++} ++ ++static size_t calc_real_size(struct list_head *base) ++{ ++ struct list_head *pos; ++ struct tplg_elem *elem, *elem_next; ++ size_t size = 0; ++ ++ list_for_each(pos, base) { ++ ++ elem = list_entry(pos, struct tplg_elem, list); ++ ++ /* compound elems have already been copied to other elems */ ++ if (elem->compound_elem) ++ continue; ++ ++ if (elem->size <= 0) ++ continue; ++ ++ size += elem->size; ++ ++ elem_next = list_entry(pos->next, struct tplg_elem, list); ++ ++ if ((pos->next == base) || (elem_next->index != elem->index)) ++ size += sizeof(struct snd_soc_tplg_hdr); ++ } ++ ++ return size; ++} ++ + static size_t calc_block_size(struct list_head *base) + { + struct list_head *pos; +@@ -277,9 +305,27 @@ int tplg_write_data(snd_tplg_t *tplg) + }; + + ssize_t ret; +- size_t size; ++ size_t total_size, size; + unsigned int index; + ++ /* calculate total size */ ++ total_size = calc_manifest_size(tplg); ++ for (index = 0; index < ARRAY_SIZE(wtable); index++) { ++ wptr = &wtable[index]; ++ size = calc_real_size(wptr->list); ++ total_size += size; ++ } ++ ++ /* allocate new binary output */ ++ free(tplg->bin); ++ tplg->bin = malloc(total_size); ++ tplg->bin_pos = 0; ++ tplg->bin_size = total_size; ++ if (tplg->bin == NULL) { ++ tplg->bin_size = 0; ++ return -ENOMEM; ++ } ++ + /* write manifest */ + ret = write_manifest_data(tplg); + if (ret < 0) { +@@ -306,7 +352,13 @@ int tplg_write_data(snd_tplg_t *tplg) + } + } + +- verbose(tplg, "total size is 0x%zx/%zd\n", tplg->out_pos, tplg->out_pos); ++ verbose(tplg, "total size is 0x%zx/%zd\n", tplg->bin_pos, tplg->bin_pos); ++ ++ if (total_size != tplg->bin_pos) { ++ SNDERR("total size mismatch (%zd != %zd)\n", ++ total_size, tplg->bin_pos); ++ return -EINVAL; ++ } + + return 0; + } +diff --git a/src/topology/parser.c b/src/topology/parser.c +index f56ad97e42b9..98a9f9e9deac 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -393,50 +393,29 @@ static int tplg_build_integ(snd_tplg_t *tplg) + return err; + } + +-int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile, +- const char *outfile) ++int snd_tplg_build_file(snd_tplg_t *tplg, ++ const char *infile, ++ const char *outfile) + { + snd_config_t *cfg = NULL; + int err = 0; + +- tplg->out_fd = +- open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); +- if (tplg->out_fd < 0) { +- SNDERR("error: failed to open %s err %d\n", +- outfile, -errno); +- return -errno; +- } +- + err = tplg_load_config(infile, &cfg); + if (err < 0) { + SNDERR("error: failed to load topology file %s\n", + infile); +- goto out_close; ++ return err; + } + + err = tplg_parse_config(tplg, cfg); + if (err < 0) { + SNDERR("error: failed to parse topology\n"); +- goto out; +- } +- +- err = tplg_build_integ(tplg); +- if (err < 0) { +- SNDERR("error: failed to check topology integrity\n"); +- goto out; +- } +- +- err = tplg_write_data(tplg); +- if (err < 0) { +- SNDERR("error: failed to write data %d\n", err); +- goto out; ++ return err; + } + +-out: + snd_config_delete(cfg); +-out_close: +- close(tplg->out_fd); +- return err; ++ ++ return snd_tplg_build(tplg, outfile); + } + + int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +@@ -468,31 +447,38 @@ int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + + int snd_tplg_build(snd_tplg_t *tplg, const char *outfile) + { +- int err; +- +- tplg->out_fd = +- open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); +- if (tplg->out_fd < 0) { +- SNDERR("error: failed to open %s err %d\n", +- outfile, -errno); +- return -errno; +- } ++ int fd, err; ++ ssize_t r; + + err = tplg_build_integ(tplg); + if (err < 0) { + SNDERR("error: failed to check topology integrity\n"); +- goto out; ++ return err; + } + + err = tplg_write_data(tplg); + if (err < 0) { + SNDERR("error: failed to write data %d\n", err); +- goto out; ++ return err; + } + +-out: +- close(tplg->out_fd); +- return err; ++ fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); ++ if (fd < 0) { ++ SNDERR("error: failed to open %s err %d\n", outfile, -errno); ++ return -errno; ++ } ++ r = write(fd, tplg->bin, tplg->bin_size); ++ close(fd); ++ if (r < 0) { ++ err = -errno; ++ SNDERR("error: write error: %s\n", strerror(errno)); ++ return err; ++ } ++ if ((size_t)r != tplg->bin_size) { ++ SNDERR("error: partial write (%zd != %zd)\n", r, tplg->bin_size); ++ return -EIO; ++ } ++ return 0; + } + + int snd_tplg_set_manifest_data(snd_tplg_t *tplg, const void *data, int len) +@@ -571,8 +557,8 @@ snd_tplg_t *snd_tplg_new(void) + + void snd_tplg_free(snd_tplg_t *tplg) + { +- if (tplg->manifest_pdata) +- free(tplg->manifest_pdata); ++ free(tplg->bin); ++ free(tplg->manifest_pdata); + + tplg_elem_free_list(&tplg->tlv_list); + tplg_elem_free_list(&tplg->widget_list); +diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h +index e16c78d49709..87e6c9a517de 100644 +--- a/src/topology/tplg_local.h ++++ b/src/topology/tplg_local.h +@@ -60,8 +60,9 @@ typedef enum _snd_pcm_rates { + + struct snd_tplg { + /* out file */ +- int out_fd; +- size_t out_pos; ++ unsigned char *bin; ++ size_t bin_pos; ++ size_t bin_size; + + int verbose; + unsigned int version; +-- +2.16.4 + diff --git a/0038-topology-add-binary-output-from-the-builder.patch b/0038-topology-add-binary-output-from-the-builder.patch new file mode 100644 index 0000000..6c77496 --- /dev/null +++ b/0038-topology-add-binary-output-from-the-builder.patch @@ -0,0 +1,173 @@ +From bee8d4fcaa52d00950d035ec561513c2b9e7cac7 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sat, 14 Dec 2019 19:20:02 +0100 +Subject: [PATCH 38/63] topology: add binary output from the builder + +- snd_tplg_build_bin() +- snd_tplg_build_bin_file() + +Signed-off-by: Jaroslav Kysela +--- + include/topology.h | 22 +++++++++++++- + src/topology/parser.c | 79 ++++++++++++++++++++++++++++++++++++++++++--------- + 2 files changed, 87 insertions(+), 14 deletions(-) + +diff --git a/include/topology.h b/include/topology.h +index 27da7308dd62..c9ef554a610f 100644 +--- a/include/topology.h ++++ b/include/topology.h +@@ -791,7 +791,18 @@ void snd_tplg_free(snd_tplg_t *tplg); + * \return Zero on success, otherwise a negative error code + */ + int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile, +- const char *outfile); ++ const char *outfile); ++ ++/** ++ * \brief Parse and build topology text file into binary file. ++ * \param tplg Topology instance. ++ * \param infile Topology text input file to be parsed ++ * \param bin Binary topology output buffer (malloc). ++ * \param size Binary topology output buffer size in bytes. ++ * \return Zero on success, otherwise a negative error code ++ */ ++int snd_tplg_build_bin_file(snd_tplg_t *tplg, const char *infile, ++ void **bin, size_t *size); + + /** + * \brief Enable verbose reporting of binary file output +@@ -1089,6 +1100,15 @@ int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); + */ + int snd_tplg_build(snd_tplg_t *tplg, const char *outfile); + ++/** ++ * \brief Build all registered topology data into memory. ++ * \param tplg Topology instance. ++ * \param bin Binary topology output buffer (malloc). ++ * \param size Binary topology output buffer size in bytes. ++ * \return Zero on success, otherwise a negative error code ++ */ ++int snd_tplg_build_bin(snd_tplg_t *tplg, void **bin, size_t *size); ++ + /** + * \brief Attach private data to topology manifest. + * \param tplg Topology instance. +diff --git a/src/topology/parser.c b/src/topology/parser.c +index 98a9f9e9deac..861565b734ad 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -393,9 +393,7 @@ static int tplg_build_integ(snd_tplg_t *tplg) + return err; + } + +-int snd_tplg_build_file(snd_tplg_t *tplg, +- const char *infile, +- const char *outfile) ++static int tplg_load(snd_tplg_t *tplg, const char *infile) + { + snd_config_t *cfg = NULL; + int err = 0; +@@ -414,10 +412,53 @@ int snd_tplg_build_file(snd_tplg_t *tplg, + } + + snd_config_delete(cfg); ++ return 0; ++} ++ ++static int tplg_build(snd_tplg_t *tplg) ++{ ++ int err; ++ ++ err = tplg_build_integ(tplg); ++ if (err < 0) { ++ SNDERR("error: failed to check topology integrity\n"); ++ return err; ++ } ++ ++ err = tplg_write_data(tplg); ++ if (err < 0) { ++ SNDERR("error: failed to write data %d\n", err); ++ return err; ++ } ++ return 0; ++} ++ ++int snd_tplg_build_file(snd_tplg_t *tplg, ++ const char *infile, ++ const char *outfile) ++{ ++ int err; ++ ++ err = tplg_load(tplg, infile); ++ if (err < 0) ++ return err; + + return snd_tplg_build(tplg, outfile); + } + ++int snd_tplg_build_bin_file(snd_tplg_t *tplg, ++ const char *infile, ++ void **bin, size_t *size) ++{ ++ int err; ++ ++ err = tplg_load(tplg, infile); ++ if (err < 0) ++ return err; ++ ++ return snd_tplg_build_bin(tplg, bin, size); ++} ++ + int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + { + switch (t->type) { +@@ -450,17 +491,9 @@ int snd_tplg_build(snd_tplg_t *tplg, const char *outfile) + int fd, err; + ssize_t r; + +- err = tplg_build_integ(tplg); +- if (err < 0) { +- SNDERR("error: failed to check topology integrity\n"); +- return err; +- } +- +- err = tplg_write_data(tplg); +- if (err < 0) { +- SNDERR("error: failed to write data %d\n", err); ++ err = tplg_build(tplg); ++ if (err < 0) + return err; +- } + + fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { +@@ -481,6 +514,26 @@ int snd_tplg_build(snd_tplg_t *tplg, const char *outfile) + return 0; + } + ++int snd_tplg_build_bin(snd_tplg_t *tplg, ++ void **bin, size_t *size) ++{ ++ int err; ++ ++ err = tplg_build(tplg); ++ if (err < 0) ++ return err; ++ ++ err = tplg_build(tplg); ++ if (err < 0) ++ return err; ++ ++ *bin = tplg->bin; ++ *size = tplg->bin_size; ++ tplg->bin = NULL; ++ tplg->bin_size = tplg->bin_pos = 0; ++ return 0; ++} ++ + int snd_tplg_set_manifest_data(snd_tplg_t *tplg, const void *data, int len) + { + if (len <= 0) +-- +2.16.4 + diff --git a/0039-topology-parser-recode-tplg_parse_config.patch b/0039-topology-parser-recode-tplg_parse_config.patch new file mode 100644 index 0000000..1867a91 --- /dev/null +++ b/0039-topology-parser-recode-tplg_parse_config.patch @@ -0,0 +1,346 @@ +From 22b66731f3dc0eb5149a99ff547eeb84eaf8d54b Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sat, 14 Dec 2019 20:32:24 +0100 +Subject: [PATCH 39/63] topology: parser - recode tplg_parse_config() + +Signed-off-by: Jaroslav Kysela +--- + src/topology/parser.c | 225 +++++++++++++++++++--------------------------- + src/topology/tplg_local.h | 67 ++++---------- + 2 files changed, 108 insertions(+), 184 deletions(-) + +diff --git a/src/topology/parser.c b/src/topology/parser.c +index 861565b734ad..82af7cc518d8 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -142,9 +142,88 @@ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg, + + static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) + { ++ static struct _parser { ++ const char *id; ++ int (*parser)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++ } *p, parsers[] = { ++ { ++ .id = "SectionTLV", ++ .parser = tplg_parse_tlv ++ }, ++ { ++ .id = "SectionControlMixer", ++ .parser = tplg_parse_control_mixer ++ }, ++ { ++ .id = "SectionControlEnum", ++ .parser = tplg_parse_control_enum ++ }, ++ { ++ .id = "SectionControlBytes", ++ .parser = tplg_parse_control_bytes ++ }, ++ { ++ .id = "SectionWidget", ++ .parser = tplg_parse_dapm_widget ++ }, ++ { ++ .id = "SectionPCMCapabilities", ++ .parser = tplg_parse_stream_caps ++ }, ++ { ++ .id = "SectionPCM", ++ .parser = tplg_parse_pcm ++ }, ++ { ++ .id = "SectionDAI", ++ .parser = tplg_parse_dai ++ }, ++ { ++ .id = "SectionHWConfig", ++ .parser = tplg_parse_hw_config ++ }, ++ { ++ .id = "SectionLink", ++ .parser = tplg_parse_link ++ }, ++ { ++ .id = "SectionBE", ++ .parser = tplg_parse_link ++ }, ++ { ++ .id = "SectionCC", ++ .parser = tplg_parse_cc ++ }, ++ { ++ .id = "SectionGraph", ++ .parser = tplg_parse_dapm_graph ++ }, ++ { ++ .id = "SectionText", ++ .parser = tplg_parse_text ++ }, ++ { ++ .id = "SectionData", ++ .parser = tplg_parse_data ++ }, ++ { ++ .id = "SectionVendorTokens", ++ .parser = tplg_parse_tokens ++ }, ++ { ++ .id = "SectionVendorTuples", ++ .parser = tplg_parse_tuples ++ }, ++ { ++ .id = "SectionManifest", ++ .parser = tplg_parse_manifest_data ++ }, ++ }; ++ int (*parser)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id; ++ unsigned int idx; + int err; + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { +@@ -159,145 +238,23 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) + if (snd_config_get_id(n, &id) < 0) + continue; + +- if (strcmp(id, "SectionTLV") == 0) { +- err = tplg_parse_compound(tplg, n, tplg_parse_tlv, +- NULL); +- if (err < 0) +- return err; +- continue; ++ parser = NULL; ++ for (idx = 0; idx < ARRAY_SIZE(parsers); idx++) { ++ p = &parsers[idx]; ++ if (strcmp(id, p->id) == 0) { ++ parser = p->parser; ++ break; ++ } + } + +- if (strcmp(id, "SectionControlMixer") == 0) { +- err = tplg_parse_compound(tplg, n, +- tplg_parse_control_mixer, NULL); +- if (err < 0) +- return err; ++ if (parser == NULL) { ++ SNDERR("error: unknown section %s\n", id); + continue; + } + +- if (strcmp(id, "SectionControlEnum") == 0) { +- err = tplg_parse_compound(tplg, n, +- tplg_parse_control_enum, NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionControlBytes") == 0) { +- err = tplg_parse_compound(tplg, n, +- tplg_parse_control_bytes, NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionWidget") == 0) { +- err = tplg_parse_compound(tplg, n, +- tplg_parse_dapm_widget, NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionPCMCapabilities") == 0) { +- err = tplg_parse_compound(tplg, n, +- tplg_parse_stream_caps, NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionPCM") == 0) { +- err = tplg_parse_compound(tplg, n, +- tplg_parse_pcm, NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionDAI") == 0) { +- err = tplg_parse_compound(tplg, n, +- tplg_parse_dai, NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionHWConfig") == 0) { +- err = tplg_parse_compound(tplg, n, tplg_parse_hw_config, +- NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionLink") == 0 +- || strcmp(id, "SectionBE") == 0) { +- err = tplg_parse_compound(tplg, n, tplg_parse_link, +- NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionCC") == 0) { +- err = tplg_parse_compound(tplg, n, tplg_parse_cc, +- NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionGraph") == 0) { +- err = tplg_parse_compound(tplg, n, +- tplg_parse_dapm_graph, NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionText") == 0) { +- err = tplg_parse_compound(tplg, n, tplg_parse_text, +- NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionData") == 0) { +- err = tplg_parse_compound(tplg, n, tplg_parse_data, +- NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionVendorTokens") == 0) { +- err = tplg_parse_compound(tplg, n, tplg_parse_tokens, +- NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionVendorTuples") == 0) { +- err = tplg_parse_compound(tplg, n, tplg_parse_tuples, +- NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- if (strcmp(id, "SectionManifest") == 0) { +- err = tplg_parse_compound(tplg, n, +- tplg_parse_manifest_data, +- NULL); +- if (err < 0) +- return err; +- continue; +- } +- +- SNDERR("error: unknown section %s\n", id); ++ err = tplg_parse_compound(tplg, n, parser, NULL); ++ if (err < 0) ++ return err; + } + return 0; + } +diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h +index 87e6c9a517de..77a681897a85 100644 +--- a/src/topology/tplg_local.h ++++ b/src/topology/tplg_local.h +@@ -202,59 +202,26 @@ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg, + + int tplg_write_data(snd_tplg_t *tplg); + +-int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_text(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED); ++int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_text(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_control_bytes(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_control_mixer(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_dapm_widget(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_stream_caps(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_cc(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); + + void tplg_free_tuples(void *obj); + +-int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_control_bytes(snd_tplg_t *tplg, +- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_control_mixer(snd_tplg_t *tplg, +- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_dapm_widget(snd_tplg_t *tplg, +- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_stream_caps(snd_tplg_t *tplg, +- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_pcm(snd_tplg_t *tplg, +- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_link(snd_tplg_t *tplg, +- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_cc(snd_tplg_t *tplg, +- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); +- +-int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED); +- + int tplg_build_data(snd_tplg_t *tplg); + int tplg_build_manifest_data(snd_tplg_t *tplg); + int tplg_build_controls(snd_tplg_t *tplg); +-- +2.16.4 + diff --git a/0040-topology-add-snd_tplg_load-remove-snd_tplg_build_bin.patch b/0040-topology-add-snd_tplg_load-remove-snd_tplg_build_bin.patch new file mode 100644 index 0000000..1b18fe3 --- /dev/null +++ b/0040-topology-add-snd_tplg_load-remove-snd_tplg_build_bin.patch @@ -0,0 +1,208 @@ +From d52eaba63dfe1d845663a4cd1bf676fafc43874a Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sun, 15 Dec 2019 16:03:29 +0100 +Subject: [PATCH 40/63] topology: add snd_tplg_load() remove + snd_tplg_build_bin_file() + +Signed-off-by: Jaroslav Kysela +--- + include/topology.h | 16 ++++----- + src/topology/parser.c | 96 ++++++++++++++++++--------------------------------- + 2 files changed, 41 insertions(+), 71 deletions(-) + +diff --git a/include/topology.h b/include/topology.h +index c9ef554a610f..c9f4ffea27de 100644 +--- a/include/topology.h ++++ b/include/topology.h +@@ -784,25 +784,23 @@ snd_tplg_t *snd_tplg_new(void); + void snd_tplg_free(snd_tplg_t *tplg); + + /** +- * \brief Parse and build topology text file into binary file. ++ * \brief Load topology from the text buffer. + * \param tplg Topology instance. +- * \param infile Topology text input file to be parsed +- * \param outfile Binary topology output file. ++ * \param buf Text buffer. ++ * \param size Text buffer size in bytes. + * \return Zero on success, otherwise a negative error code + */ +-int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile, +- const char *outfile); ++int snd_tplg_load(snd_tplg_t *tplg, const char *buf, size_t size); + + /** + * \brief Parse and build topology text file into binary file. + * \param tplg Topology instance. + * \param infile Topology text input file to be parsed +- * \param bin Binary topology output buffer (malloc). +- * \param size Binary topology output buffer size in bytes. ++ * \param outfile Binary topology output file. + * \return Zero on success, otherwise a negative error code + */ +-int snd_tplg_build_bin_file(snd_tplg_t *tplg, const char *infile, +- void **bin, size_t *size); ++int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile, ++ const char *outfile); + + /** + * \brief Enable verbose reporting of binary file output +diff --git a/src/topology/parser.c b/src/topology/parser.c +index 82af7cc518d8..ed864d3223c4 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -259,52 +259,30 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) + return 0; + } + +-static int tplg_load_config(const char *file, snd_config_t **cfg) ++static int tplg_load_config(snd_tplg_t *tplg, snd_input_t *in) + { +- FILE *fp; +- snd_input_t *in; + snd_config_t *top; + int ret; + +- fp = fopen(file, "r"); +- if (fp == NULL) { +- SNDERR("error: could not open configuration file %s", +- file); +- return -errno; +- } +- +- ret = snd_input_stdio_attach(&in, fp, 1); +- if (ret < 0) { +- fclose(fp); +- SNDERR("error: could not attach stdio %s", file); +- return ret; +- } + ret = snd_config_top(&top); + if (ret < 0) +- goto err; ++ return ret; + + ret = snd_config_load(top, in); + if (ret < 0) { +- SNDERR("error: could not load configuration file %s", +- file); +- goto err_load; ++ SNDERR("error: could not load configuration"); ++ snd_config_delete(top); ++ return ret; + } + +- ret = snd_input_close(in); ++ ret = tplg_parse_config(tplg, top); ++ snd_config_delete(top); + if (ret < 0) { +- in = NULL; +- goto err_load; ++ SNDERR("error: failed to parse topology"); ++ return ret; + } + +- *cfg = top; + return 0; +- +-err_load: +- snd_config_delete(top); +-err: +- if (in) +- snd_input_close(in); +- return ret; + } + + static int tplg_build_integ(snd_tplg_t *tplg) +@@ -350,26 +328,20 @@ static int tplg_build_integ(snd_tplg_t *tplg) + return err; + } + +-static int tplg_load(snd_tplg_t *tplg, const char *infile) ++int snd_tplg_load(snd_tplg_t *tplg, const char *buf, size_t size) + { +- snd_config_t *cfg = NULL; +- int err = 0; +- +- err = tplg_load_config(infile, &cfg); +- if (err < 0) { +- SNDERR("error: failed to load topology file %s\n", +- infile); +- return err; +- } ++ snd_input_t *in; ++ int err; + +- err = tplg_parse_config(tplg, cfg); ++ err = snd_input_buffer_open(&in, buf, size); + if (err < 0) { +- SNDERR("error: failed to parse topology\n"); ++ SNDERR("error: could not create input buffer"); + return err; + } + +- snd_config_delete(cfg); +- return 0; ++ err = tplg_load_config(tplg, in); ++ snd_input_close(in); ++ return err; + } + + static int tplg_build(snd_tplg_t *tplg) +@@ -394,26 +366,30 @@ int snd_tplg_build_file(snd_tplg_t *tplg, + const char *infile, + const char *outfile) + { ++ FILE *fp; ++ snd_input_t *in; + int err; + +- err = tplg_load(tplg, infile); +- if (err < 0) +- return err; +- +- return snd_tplg_build(tplg, outfile); +-} ++ fp = fopen(infile, "r"); ++ if (fp == NULL) { ++ SNDERR("error: could not open configuration file %s", ++ infile); ++ return -errno; ++ } + +-int snd_tplg_build_bin_file(snd_tplg_t *tplg, +- const char *infile, +- void **bin, size_t *size) +-{ +- int err; ++ err = snd_input_stdio_attach(&in, fp, 1); ++ if (err < 0) { ++ fclose(fp); ++ SNDERR("error: could not attach stdio %s", infile); ++ return err; ++ } + +- err = tplg_load(tplg, infile); ++ err = tplg_load_config(tplg, in); ++ snd_input_close(in); + if (err < 0) + return err; + +- return snd_tplg_build_bin(tplg, bin, size); ++ return snd_tplg_build(tplg, outfile); + } + + int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +@@ -476,10 +452,6 @@ int snd_tplg_build_bin(snd_tplg_t *tplg, + { + int err; + +- err = tplg_build(tplg); +- if (err < 0) +- return err; +- + err = tplg_build(tplg); + if (err < 0) + return err; +-- +2.16.4 + diff --git a/0041-topology-move-the-topology-element-table-from-builde.patch b/0041-topology-move-the-topology-element-table-from-builde.patch new file mode 100644 index 0000000..721162e --- /dev/null +++ b/0041-topology-move-the-topology-element-table-from-builde.patch @@ -0,0 +1,464 @@ +From 4f076f5b69a873418ecb826a4198e4d95a3a2a6f Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sun, 15 Dec 2019 16:15:29 +0100 +Subject: [PATCH 41/63] topology: move the topology element table from builder + to elem + +- use offsetof() for the lists +- add other info to describe the elements +- use the table in the element constructor + +Signed-off-by: Jaroslav Kysela +--- + src/topology/builder.c | 98 ++++---------------- + src/topology/ctl.c | 7 +- + src/topology/elem.c | 232 ++++++++++++++++++++++++++++++++-------------- + src/topology/tplg_local.h | 15 +++ + 4 files changed, 199 insertions(+), 153 deletions(-) + +diff --git a/src/topology/builder.c b/src/topology/builder.c +index 40943b56e4aa..cadb55830809 100644 +--- a/src/topology/builder.c ++++ b/src/topology/builder.c +@@ -236,83 +236,20 @@ static ssize_t write_manifest_data(snd_tplg_t *tplg) + + int tplg_write_data(snd_tplg_t *tplg) + { +- struct wtable { +- const char *name; +- struct list_head *list; +- int type; +- int tsoc; +- } *wptr, wtable[] = { +- { +- .name = "control mixer", +- .list = &tplg->mixer_list, +- .type = SND_TPLG_TYPE_MIXER, +- .tsoc = SND_SOC_TPLG_TYPE_MIXER, +- }, +- { +- .name = "control enum", +- .list = &tplg->enum_list, +- .type = SND_TPLG_TYPE_ENUM, +- .tsoc = SND_SOC_TPLG_TYPE_ENUM, +- }, +- { +- .name = "control extended (bytes)", +- .list = &tplg->bytes_ext_list, +- .type = SND_TPLG_TYPE_BYTES, +- .tsoc = SND_SOC_TPLG_TYPE_BYTES, +- }, +- { +- .name = "dapm widget", +- .list = &tplg->widget_list, +- .type = SND_TPLG_TYPE_DAPM_WIDGET, +- .tsoc = SND_SOC_TPLG_TYPE_DAPM_WIDGET, +- }, +- { +- .name = "pcm", +- .list = &tplg->pcm_list, +- .type = SND_TPLG_TYPE_PCM, +- .tsoc = SND_SOC_TPLG_TYPE_PCM, +- }, +- { +- .name = "physical dai", +- .list = &tplg->dai_list, +- .type = SND_TPLG_TYPE_DAI, +- .tsoc = SND_SOC_TPLG_TYPE_DAI, +- }, +- { +- .name = "be", +- .list = &tplg->be_list, +- .type = SND_TPLG_TYPE_BE, +- .tsoc = SND_SOC_TPLG_TYPE_BACKEND_LINK, +- }, +- { +- .name = "cc", +- .list = &tplg->cc_list, +- .type = SND_TPLG_TYPE_CC, +- .tsoc = SND_SOC_TPLG_TYPE_CODEC_LINK, +- }, +- { +- .name = "route (dapm graph)", +- .list = &tplg->route_list, +- .type = SND_TPLG_TYPE_DAPM_GRAPH, +- .tsoc = SND_SOC_TPLG_TYPE_DAPM_GRAPH, +- }, +- { +- .name = "private data", +- .list = &tplg->pdata_list, +- .type = SND_TPLG_TYPE_DATA, +- .tsoc = SND_SOC_TPLG_TYPE_PDATA, +- }, +- }; +- ++ struct tplg_table *tptr; ++ struct list_head *list; + ssize_t ret; + size_t total_size, size; + unsigned int index; + + /* calculate total size */ + total_size = calc_manifest_size(tplg); +- for (index = 0; index < ARRAY_SIZE(wtable); index++) { +- wptr = &wtable[index]; +- size = calc_real_size(wptr->list); ++ for (index = 0; index < tplg_table_items; index++) { ++ tptr = &tplg_table[index]; ++ if (!tptr->build) ++ continue; ++ list = (struct list_head *)((void *)tplg + tptr->loff); ++ size = calc_real_size(list); + total_size += size; + } + +@@ -334,20 +271,23 @@ int tplg_write_data(snd_tplg_t *tplg) + } + + /* write all blocks */ +- for (index = 0; index < ARRAY_SIZE(wtable); index++) { +- wptr = &wtable[index]; ++ for (index = 0; index < tplg_table_items; index++) { ++ tptr = &tplg_table[index]; ++ if (!tptr->build) ++ continue; ++ list = (struct list_head *)((void *)tplg + tptr->loff); + /* calculate the block size in bytes for all elems in this list */ +- size = calc_block_size(wptr->list); ++ size = calc_block_size(list); + if (size == 0) + continue; + verbose(tplg, "block size for type %s (%d:%d) is 0x%zx/%zd\n", +- wptr->name, wptr->type, +- wptr->tsoc, size, size); +- ret = write_elem_block(tplg, wptr->list, size, +- wptr->tsoc, wptr->name); ++ tptr->name, tptr->type, ++ tptr->tsoc, size, size); ++ ret = write_elem_block(tplg, list, size, ++ tptr->tsoc, tptr->name); + if (ret < 0) { + SNDERR("failed to write %s elements: %s\n", +- wptr->name, snd_strerror(-ret)); ++ tptr->name, snd_strerror(-ret)); + return ret; + } + } +diff --git a/src/topology/ctl.c b/src/topology/ctl.c +index 9190efefb575..539329cd661f 100644 +--- a/src/topology/ctl.c ++++ b/src/topology/ctl.c +@@ -284,18 +284,13 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem) + { + snd_config_iterator_t i, next; + snd_config_t *n; +- struct snd_soc_tplg_ctl_tlv *tplg_tlv; ++ struct snd_soc_tplg_ctl_tlv *tplg_tlv = elem->tlv; + struct snd_soc_tplg_tlv_dbscale *scale; + const char *id = NULL; + int val; + + tplg_dbg(" scale: %s\n", elem->id); + +- tplg_tlv = calloc(1, sizeof(*tplg_tlv)); +- if (!tplg_tlv) +- return -ENOMEM; +- +- elem->tlv = tplg_tlv; + tplg_tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv); + tplg_tlv->type = SNDRV_CTL_TLVT_DB_SCALE; + scale = &tplg_tlv->scale; +diff --git a/src/topology/elem.c b/src/topology/elem.c +index f2076f7958aa..92ca7da4c4aa 100644 +--- a/src/topology/elem.c ++++ b/src/topology/elem.c +@@ -20,6 +20,154 @@ + #include "list.h" + #include "tplg_local.h" + ++struct tplg_table tplg_table[] = { ++ { ++ .name = "manifest", ++ .loff = offsetof(snd_tplg_t, manifest_list), ++ .type = SND_TPLG_TYPE_MANIFEST, ++ .tsoc = SND_SOC_TPLG_TYPE_MANIFEST, ++ .size = sizeof(struct snd_soc_tplg_manifest), ++ .enew = 1, ++ }, ++ { ++ .name = "control mixer", ++ .loff = offsetof(snd_tplg_t, mixer_list), ++ .type = SND_TPLG_TYPE_MIXER, ++ .tsoc = SND_SOC_TPLG_TYPE_MIXER, ++ .size = sizeof(struct snd_soc_tplg_mixer_control), ++ .build = 1, ++ .enew = 1, ++ }, ++ { ++ .name = "control enum", ++ .loff = offsetof(snd_tplg_t, enum_list), ++ .type = SND_TPLG_TYPE_ENUM, ++ .tsoc = SND_SOC_TPLG_TYPE_ENUM, ++ .size = sizeof(struct snd_soc_tplg_enum_control), ++ .build = 1, ++ .enew = 1, ++ }, ++ { ++ .name = "control extended (bytes)", ++ .loff = offsetof(snd_tplg_t, bytes_ext_list), ++ .type = SND_TPLG_TYPE_BYTES, ++ .tsoc = SND_SOC_TPLG_TYPE_BYTES, ++ .size = sizeof(struct snd_soc_tplg_bytes_control), ++ .build = 1, ++ .enew = 1, ++ }, ++ { ++ .name = "dapm widget", ++ .loff = offsetof(snd_tplg_t, widget_list), ++ .type = SND_TPLG_TYPE_DAPM_WIDGET, ++ .tsoc = SND_SOC_TPLG_TYPE_DAPM_WIDGET, ++ .size = sizeof(struct snd_soc_tplg_dapm_widget), ++ .build = 1, ++ .enew = 1, ++ }, ++ { ++ .name = "pcm", ++ .loff = offsetof(snd_tplg_t, pcm_list), ++ .type = SND_TPLG_TYPE_PCM, ++ .tsoc = SND_SOC_TPLG_TYPE_PCM, ++ .size = sizeof(struct snd_soc_tplg_pcm), ++ .build = 1, ++ .enew = 1, ++ }, ++ { ++ .name = "physical dai", ++ .loff = offsetof(snd_tplg_t, dai_list), ++ .type = SND_TPLG_TYPE_DAI, ++ .tsoc = SND_SOC_TPLG_TYPE_DAI, ++ .size = sizeof(struct snd_soc_tplg_dai), ++ .build = 1, ++ .enew = 1, ++ }, ++ { ++ .name = "be", ++ .loff = offsetof(snd_tplg_t, be_list), ++ .type = SND_TPLG_TYPE_BE, ++ .tsoc = SND_SOC_TPLG_TYPE_BACKEND_LINK, ++ .size = sizeof(struct snd_soc_tplg_link_config), ++ .build = 1, ++ .enew = 1, ++ }, ++ { ++ .name = "cc", ++ .loff = offsetof(snd_tplg_t, cc_list), ++ .type = SND_TPLG_TYPE_CC, ++ .tsoc = SND_SOC_TPLG_TYPE_CODEC_LINK, ++ .size = sizeof(struct snd_soc_tplg_link_config), ++ .build = 1, ++ .enew = 1, ++ }, ++ { ++ .name = "route (dapm graph)", ++ .loff = offsetof(snd_tplg_t, route_list), ++ .type = SND_TPLG_TYPE_DAPM_GRAPH, ++ .tsoc = SND_SOC_TPLG_TYPE_DAPM_GRAPH, ++ .build = 1, ++ }, ++ { ++ .name = "private data", ++ .loff = offsetof(snd_tplg_t, pdata_list), ++ .type = SND_TPLG_TYPE_DATA, ++ .tsoc = SND_SOC_TPLG_TYPE_PDATA, ++ .build = 1, ++ .enew = 1, ++ }, ++ { ++ .name = "text", ++ .loff = offsetof(snd_tplg_t, text_list), ++ .type = SND_TPLG_TYPE_TEXT, ++ .size = sizeof(struct tplg_texts), ++ .enew = 1, ++ }, ++ { ++ .name = "tlv", ++ .loff = offsetof(snd_tplg_t, tlv_list), ++ .type = SND_TPLG_TYPE_TLV, ++ .size = sizeof(struct snd_soc_tplg_ctl_tlv), ++ .enew = 1, ++ }, ++ { ++ .name = "stream config", ++ .loff = offsetof(snd_tplg_t, pcm_config_list), ++ .type = SND_TPLG_TYPE_STREAM_CONFIG, ++ .size = sizeof(struct snd_soc_tplg_stream), ++ .enew = 1, ++ }, ++ { ++ .name = "stream capabilities", ++ .loff = offsetof(snd_tplg_t, pcm_caps_list), ++ .type = SND_TPLG_TYPE_STREAM_CAPS, ++ .size = sizeof(struct snd_soc_tplg_stream_caps), ++ .enew = 1, ++ }, ++ { ++ .name = "token", ++ .loff = offsetof(snd_tplg_t, token_list), ++ .type = SND_TPLG_TYPE_TOKEN, ++ .enew = 1, ++ }, ++ { ++ .name = "tuple", ++ .loff = offsetof(snd_tplg_t, tuple_list), ++ .type = SND_TPLG_TYPE_TUPLE, ++ .free = tplg_free_tuples, ++ .enew = 1, ++ }, ++ { ++ .name = "hw config", ++ .loff = offsetof(snd_tplg_t, hw_cfg_list), ++ .type = SND_TPLG_TYPE_HW_CONFIG, ++ .size = sizeof(struct snd_soc_tplg_hw_config), ++ .enew = 1, ++ } ++}; ++ ++unsigned int tplg_table_items = ARRAY_SIZE(tplg_table); ++ + int tplg_ref_add(struct tplg_elem *elem, int type, const char* id) + { + struct tplg_ref *ref; +@@ -152,9 +300,12 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, + const char *name, + enum snd_tplg_type type) + { ++ struct tplg_table *tptr; + struct tplg_elem *elem; ++ struct list_head *list; + const char *id; + int obj_size = 0; ++ unsigned index; + void *obj; + snd_config_iterator_t i, next; + snd_config_t *n; +@@ -191,79 +342,24 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, + } else if (name != NULL) + snd_strlcpy(elem->id, name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + +- switch (type) { +- case SND_TPLG_TYPE_DATA: +- tplg_elem_insert(elem, &tplg->pdata_list); +- break; +- case SND_TPLG_TYPE_MANIFEST: +- tplg_elem_insert(elem, &tplg->manifest_list); +- obj_size = sizeof(struct snd_soc_tplg_manifest); +- break; +- case SND_TPLG_TYPE_TEXT: +- tplg_elem_insert(elem, &tplg->text_list); +- obj_size = sizeof(struct tplg_texts); +- break; +- case SND_TPLG_TYPE_TLV: +- tplg_elem_insert(elem, &tplg->tlv_list); +- elem->size = sizeof(struct snd_soc_tplg_ctl_tlv); +- break; +- case SND_TPLG_TYPE_BYTES: +- tplg_elem_insert(elem, &tplg->bytes_ext_list); +- obj_size = sizeof(struct snd_soc_tplg_bytes_control); +- break; +- case SND_TPLG_TYPE_ENUM: +- tplg_elem_insert(elem, &tplg->enum_list); +- obj_size = sizeof(struct snd_soc_tplg_enum_control); +- break; +- case SND_TPLG_TYPE_MIXER: +- tplg_elem_insert(elem, &tplg->mixer_list); +- obj_size = sizeof(struct snd_soc_tplg_mixer_control); +- break; +- case SND_TPLG_TYPE_DAPM_WIDGET: +- tplg_elem_insert(elem, &tplg->widget_list); +- obj_size = sizeof(struct snd_soc_tplg_dapm_widget); +- break; +- case SND_TPLG_TYPE_STREAM_CONFIG: +- tplg_elem_insert(elem, &tplg->pcm_config_list); +- obj_size = sizeof(struct snd_soc_tplg_stream); ++ for (index = 0; index < tplg_table_items; index++) { ++ tptr = &tplg_table[index]; ++ if (!tptr->enew) ++ continue; ++ if ((int)type != tptr->type) ++ continue; + break; +- case SND_TPLG_TYPE_STREAM_CAPS: +- tplg_elem_insert(elem, &tplg->pcm_caps_list); +- obj_size = sizeof(struct snd_soc_tplg_stream_caps); +- break; +- case SND_TPLG_TYPE_PCM: +- tplg_elem_insert(elem, &tplg->pcm_list); +- obj_size = sizeof(struct snd_soc_tplg_pcm); +- break; +- case SND_TPLG_TYPE_DAI: +- tplg_elem_insert(elem, &tplg->dai_list); +- obj_size = sizeof(struct snd_soc_tplg_dai); +- break; +- case SND_TPLG_TYPE_BE: +- case SND_TPLG_TYPE_LINK: +- tplg_elem_insert(elem, &tplg->be_list); +- obj_size = sizeof(struct snd_soc_tplg_link_config); +- break; +- case SND_TPLG_TYPE_CC: +- tplg_elem_insert(elem, &tplg->cc_list); +- obj_size = sizeof(struct snd_soc_tplg_link_config); +- break; +- case SND_TPLG_TYPE_TOKEN: +- tplg_elem_insert(elem, &tplg->token_list); +- break; +- case SND_TPLG_TYPE_TUPLE: +- tplg_elem_insert(elem, &tplg->tuple_list); +- elem->free = tplg_free_tuples; +- break; +- case SND_TPLG_TYPE_HW_CONFIG: +- tplg_elem_insert(elem, &tplg->hw_cfg_list); +- obj_size = sizeof(struct snd_soc_tplg_hw_config); +- break; +- default: ++ } ++ if (index >= tplg_table_items) { + free(elem); + return NULL; + } + ++ list = (struct list_head *)((void *)tplg + tptr->loff); ++ tplg_elem_insert(elem, list); ++ obj_size = tptr->size; ++ elem->free = tptr->free; ++ + /* create new object too if required */ + if (obj_size > 0) { + obj = calloc(1, obj_size); +diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h +index 77a681897a85..0987898f8336 100644 +--- a/src/topology/tplg_local.h ++++ b/src/topology/tplg_local.h +@@ -196,6 +196,21 @@ struct map_elem { + int id; + }; + ++/* mapping table */ ++struct tplg_table { ++ const char *name; ++ off_t loff; ++ size_t size; ++ int type; ++ int tsoc; ++ unsigned build: 1; ++ unsigned enew: 1; ++ void (*free)(void *); ++}; ++ ++extern struct tplg_table tplg_table[]; ++extern unsigned int tplg_table_items; ++ + int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg, + int (*fcn)(snd_tplg_t *, snd_config_t *, void *), + void *private); +-- +2.16.4 + diff --git a/0042-topology-add-parser-to-the-tplg_table.patch b/0042-topology-add-parser-to-the-tplg_table.patch new file mode 100644 index 0000000..2637593 --- /dev/null +++ b/0042-topology-add-parser-to-the-tplg_table.patch @@ -0,0 +1,331 @@ +From 4a0efdc87355d5789876e20b9530dc85224ad281 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sun, 15 Dec 2019 17:24:50 +0100 +Subject: [PATCH 42/63] topology: add parser to the tplg_table + +Signed-off-by: Jaroslav Kysela +--- + src/topology/elem.c | 35 ++++++++++++++++++ + src/topology/parser.c | 90 +++++------------------------------------------ + src/topology/tplg_local.h | 3 ++ + 3 files changed, 47 insertions(+), 81 deletions(-) + +diff --git a/src/topology/elem.c b/src/topology/elem.c +index 92ca7da4c4aa..e79a68b71a91 100644 +--- a/src/topology/elem.c ++++ b/src/topology/elem.c +@@ -23,112 +23,139 @@ + struct tplg_table tplg_table[] = { + { + .name = "manifest", ++ .id = "SectionManifest", + .loff = offsetof(snd_tplg_t, manifest_list), + .type = SND_TPLG_TYPE_MANIFEST, + .tsoc = SND_SOC_TPLG_TYPE_MANIFEST, + .size = sizeof(struct snd_soc_tplg_manifest), + .enew = 1, ++ .parse = tplg_parse_manifest_data, + }, + { + .name = "control mixer", ++ .id = "SectionControlMixer", + .loff = offsetof(snd_tplg_t, mixer_list), + .type = SND_TPLG_TYPE_MIXER, + .tsoc = SND_SOC_TPLG_TYPE_MIXER, + .size = sizeof(struct snd_soc_tplg_mixer_control), + .build = 1, + .enew = 1, ++ .parse = tplg_parse_control_mixer, + }, + { + .name = "control enum", ++ .id = "SectionControlEnum", + .loff = offsetof(snd_tplg_t, enum_list), + .type = SND_TPLG_TYPE_ENUM, + .tsoc = SND_SOC_TPLG_TYPE_ENUM, + .size = sizeof(struct snd_soc_tplg_enum_control), + .build = 1, + .enew = 1, ++ .parse = tplg_parse_control_enum, + }, + { + .name = "control extended (bytes)", ++ .id = "SectionControlBytes", + .loff = offsetof(snd_tplg_t, bytes_ext_list), + .type = SND_TPLG_TYPE_BYTES, + .tsoc = SND_SOC_TPLG_TYPE_BYTES, + .size = sizeof(struct snd_soc_tplg_bytes_control), + .build = 1, + .enew = 1, ++ .parse = tplg_parse_control_bytes, + }, + { + .name = "dapm widget", ++ .id = "SectionWidget", + .loff = offsetof(snd_tplg_t, widget_list), + .type = SND_TPLG_TYPE_DAPM_WIDGET, + .tsoc = SND_SOC_TPLG_TYPE_DAPM_WIDGET, + .size = sizeof(struct snd_soc_tplg_dapm_widget), + .build = 1, + .enew = 1, ++ .parse = tplg_parse_dapm_widget, + }, + { + .name = "pcm", ++ .id = "SectionPCM", + .loff = offsetof(snd_tplg_t, pcm_list), + .type = SND_TPLG_TYPE_PCM, + .tsoc = SND_SOC_TPLG_TYPE_PCM, + .size = sizeof(struct snd_soc_tplg_pcm), + .build = 1, + .enew = 1, ++ .parse = tplg_parse_pcm, + }, + { + .name = "physical dai", ++ .id = "SectionDAI", + .loff = offsetof(snd_tplg_t, dai_list), + .type = SND_TPLG_TYPE_DAI, + .tsoc = SND_SOC_TPLG_TYPE_DAI, + .size = sizeof(struct snd_soc_tplg_dai), + .build = 1, + .enew = 1, ++ .parse = tplg_parse_dai, + }, + { + .name = "be", ++ .id = "SectionBE", ++ .id2 = "SectionLink", + .loff = offsetof(snd_tplg_t, be_list), + .type = SND_TPLG_TYPE_BE, + .tsoc = SND_SOC_TPLG_TYPE_BACKEND_LINK, + .size = sizeof(struct snd_soc_tplg_link_config), + .build = 1, + .enew = 1, ++ .parse = tplg_parse_link, + }, + { + .name = "cc", ++ .id = "SectionCC", + .loff = offsetof(snd_tplg_t, cc_list), + .type = SND_TPLG_TYPE_CC, + .tsoc = SND_SOC_TPLG_TYPE_CODEC_LINK, + .size = sizeof(struct snd_soc_tplg_link_config), + .build = 1, + .enew = 1, ++ .parse = tplg_parse_cc, + }, + { + .name = "route (dapm graph)", ++ .id = "SectionGraph", + .loff = offsetof(snd_tplg_t, route_list), + .type = SND_TPLG_TYPE_DAPM_GRAPH, + .tsoc = SND_SOC_TPLG_TYPE_DAPM_GRAPH, + .build = 1, ++ .parse = tplg_parse_dapm_graph, + }, + { + .name = "private data", ++ .id = "SectionData", + .loff = offsetof(snd_tplg_t, pdata_list), + .type = SND_TPLG_TYPE_DATA, + .tsoc = SND_SOC_TPLG_TYPE_PDATA, + .build = 1, + .enew = 1, ++ .parse = tplg_parse_data, + }, + { + .name = "text", ++ .id = "SectionText", + .loff = offsetof(snd_tplg_t, text_list), + .type = SND_TPLG_TYPE_TEXT, + .size = sizeof(struct tplg_texts), + .enew = 1, ++ .parse = tplg_parse_text, + }, + { + .name = "tlv", ++ .id = "SectionTLV", + .loff = offsetof(snd_tplg_t, tlv_list), + .type = SND_TPLG_TYPE_TLV, + .size = sizeof(struct snd_soc_tplg_ctl_tlv), + .enew = 1, ++ .parse = tplg_parse_tlv, + }, + { + .name = "stream config", +@@ -139,30 +166,38 @@ struct tplg_table tplg_table[] = { + }, + { + .name = "stream capabilities", ++ .id = "SectionPCMCapabilities", + .loff = offsetof(snd_tplg_t, pcm_caps_list), + .type = SND_TPLG_TYPE_STREAM_CAPS, + .size = sizeof(struct snd_soc_tplg_stream_caps), + .enew = 1, ++ .parse = tplg_parse_stream_caps, + }, + { + .name = "token", ++ .id = "SectionVendorTokens", + .loff = offsetof(snd_tplg_t, token_list), + .type = SND_TPLG_TYPE_TOKEN, + .enew = 1, ++ .parse = tplg_parse_tokens, + }, + { + .name = "tuple", ++ .id = "SectionVendorTuples", + .loff = offsetof(snd_tplg_t, tuple_list), + .type = SND_TPLG_TYPE_TUPLE, + .free = tplg_free_tuples, + .enew = 1, ++ .parse = tplg_parse_tuples, + }, + { + .name = "hw config", ++ .id = "SectionHWConfig", + .loff = offsetof(snd_tplg_t, hw_cfg_list), + .type = SND_TPLG_TYPE_HW_CONFIG, + .size = sizeof(struct snd_soc_tplg_hw_config), + .enew = 1, ++ .parse = tplg_parse_hw_config, + } + }; + +diff --git a/src/topology/parser.c b/src/topology/parser.c +index ed864d3223c4..11202769391c 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -142,87 +142,11 @@ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg, + + static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) + { +- static struct _parser { +- const char *id; +- int (*parser)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); +- } *p, parsers[] = { +- { +- .id = "SectionTLV", +- .parser = tplg_parse_tlv +- }, +- { +- .id = "SectionControlMixer", +- .parser = tplg_parse_control_mixer +- }, +- { +- .id = "SectionControlEnum", +- .parser = tplg_parse_control_enum +- }, +- { +- .id = "SectionControlBytes", +- .parser = tplg_parse_control_bytes +- }, +- { +- .id = "SectionWidget", +- .parser = tplg_parse_dapm_widget +- }, +- { +- .id = "SectionPCMCapabilities", +- .parser = tplg_parse_stream_caps +- }, +- { +- .id = "SectionPCM", +- .parser = tplg_parse_pcm +- }, +- { +- .id = "SectionDAI", +- .parser = tplg_parse_dai +- }, +- { +- .id = "SectionHWConfig", +- .parser = tplg_parse_hw_config +- }, +- { +- .id = "SectionLink", +- .parser = tplg_parse_link +- }, +- { +- .id = "SectionBE", +- .parser = tplg_parse_link +- }, +- { +- .id = "SectionCC", +- .parser = tplg_parse_cc +- }, +- { +- .id = "SectionGraph", +- .parser = tplg_parse_dapm_graph +- }, +- { +- .id = "SectionText", +- .parser = tplg_parse_text +- }, +- { +- .id = "SectionData", +- .parser = tplg_parse_data +- }, +- { +- .id = "SectionVendorTokens", +- .parser = tplg_parse_tokens +- }, +- { +- .id = "SectionVendorTuples", +- .parser = tplg_parse_tuples +- }, +- { +- .id = "SectionManifest", +- .parser = tplg_parse_manifest_data +- }, +- }; + int (*parser)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id; ++ struct tplg_table *p; + unsigned int idx; + int err; + +@@ -239,10 +163,14 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) + continue; + + parser = NULL; +- for (idx = 0; idx < ARRAY_SIZE(parsers); idx++) { +- p = &parsers[idx]; +- if (strcmp(id, p->id) == 0) { +- parser = p->parser; ++ for (idx = 0; idx < tplg_table_items; idx++) { ++ p = &tplg_table[idx]; ++ if (p->id && strcmp(id, p->id) == 0) { ++ parser = p->parse; ++ break; ++ } ++ if (p->id2 && strcmp(id, p->id2) == 0) { ++ parser = p->parse; + break; + } + } +diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h +index 0987898f8336..bea88ba35608 100644 +--- a/src/topology/tplg_local.h ++++ b/src/topology/tplg_local.h +@@ -199,6 +199,8 @@ struct map_elem { + /* mapping table */ + struct tplg_table { + const char *name; ++ const char *id; ++ const char *id2; + off_t loff; + size_t size; + int type; +@@ -206,6 +208,7 @@ struct tplg_table { + unsigned build: 1; + unsigned enew: 1; + void (*free)(void *); ++ int (*parse)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); + }; + + extern struct tplg_table tplg_table[]; +-- +2.16.4 + diff --git a/0043-topology-add-snd_tplg_save.patch b/0043-topology-add-snd_tplg_save.patch new file mode 100644 index 0000000..5b8eeeb --- /dev/null +++ b/0043-topology-add-snd_tplg_save.patch @@ -0,0 +1,3141 @@ +From aa1bac2d04bd1fb4ccae96a1136e60454298a710 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Mon, 16 Dec 2019 14:26:31 +0100 +Subject: [PATCH 43/63] topology: add snd_tplg_save() + +Signed-off-by: Jaroslav Kysela +--- + include/topology.h | 15 ++ + src/conf.c | 17 ++ + src/topology/Makefile.am | 3 +- + src/topology/channel.c | 45 ++++ + src/topology/ctl.c | 201 ++++++++++++++- + src/topology/dapm.c | 209 ++++++++++----- + src/topology/data.c | 601 ++++++++++++++++++++++++++++++++++--------- + src/topology/elem.c | 18 ++ + src/topology/ops.c | 92 +++++++ + src/topology/parser.c | 4 + + src/topology/pcm.c | 505 +++++++++++++++++++++++++++++++----- + src/topology/save.c | 632 ++++++++++++++++++++++++++++++++++++++++++++++ + src/topology/text.c | 19 ++ + src/topology/tplg_local.h | 58 ++++- + 14 files changed, 2162 insertions(+), 257 deletions(-) + create mode 100644 src/topology/save.c + +diff --git a/include/topology.h b/include/topology.h +index c9f4ffea27de..69aa5ed733e3 100644 +--- a/include/topology.h ++++ b/include/topology.h +@@ -1124,6 +1124,21 @@ int snd_tplg_set_manifest_data(snd_tplg_t *tplg, const void *data, int len); + */ + int snd_tplg_set_version(snd_tplg_t *tplg, unsigned int version); + ++/* ++ * Flags for the snd_tplg_save() ++ */ ++#define SND_TPLG_SAVE_SORT (1<<0) /*!< sort identifiers */ ++#define SND_TPLG_SAVE_GROUPS (1<<1) /*!< create the structure by group index */ ++#define SND_TPLG_SAVE_NOCHECK (1<<16) /*!< unchecked output for debugging */ ++ ++/** ++ * \brief Save the topology to the text configuration string. ++ * \param tplg Topology instance. ++ * \param dst A pointer to string with result (malloc). ++ * \return Zero on success, otherwise a negative error code ++ */ ++int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags); ++ + /* \} */ + + #ifdef __cplusplus +diff --git a/src/conf.c b/src/conf.c +index 3e753b266b8d..c4db9f21a15e 100644 +--- a/src/conf.c ++++ b/src/conf.c +@@ -874,6 +874,21 @@ static int get_nonwhite(input_t *input) + } + } + ++static inline int get_hexachar(input_t *input) ++{ ++ int c, num = 0; ++ ++ c = get_char(input); ++ if (c >= '0' && c <= '9') num |= (c - '0') << 4; ++ else if (c >= 'a' && c <= 'f') num |= (c - 'a') << 4; ++ else if (c >= 'A' && c <= 'F') num |= (c - 'A') << 4; ++ c = get_char(input); ++ if (c >= '0' && c <= '9') num |= (c - '0') << 0; ++ else if (c >= 'a' && c <= 'f') num |= (c - 'a') << 0; ++ else if (c >= 'A' && c <= 'F') num |= (c - 'A') << 0; ++ return c; ++} ++ + static int get_quotedchar(input_t *input) + { + int c; +@@ -891,6 +906,8 @@ static int get_quotedchar(input_t *input) + return '\r'; + case 'f': + return '\f'; ++ case 'x': ++ return get_hexachar(input); + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + { +diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am +index 9dc472d62fac..a850ec4c195c 100644 +--- a/src/topology/Makefile.am ++++ b/src/topology/Makefile.am +@@ -27,7 +27,8 @@ libatopology_la_SOURCES =\ + text.c \ + channel.c \ + ops.c \ +- elem.c ++ elem.c \ ++ save.c + + noinst_HEADERS = tplg_local.h + +diff --git a/src/topology/channel.c b/src/topology/channel.c +index 4569eb31e0c5..b54a10c89379 100644 +--- a/src/topology/channel.c ++++ b/src/topology/channel.c +@@ -73,6 +73,18 @@ static int lookup_channel(const char *c) + return -EINVAL; + } + ++const char *tplg_channel_name(int type) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(channel_map); i++) { ++ if (channel_map[i].id == type) ++ return channel_map[i].name; ++ } ++ ++ return NULL; ++} ++ + /* Parse a channel mapping. */ + int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg, + void *private) +@@ -123,3 +135,36 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg, + tplg->channel_idx++; + return 0; + } ++ ++int tplg_save_channels(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct snd_soc_tplg_channel *channel, ++ unsigned int count, char **dst, const char *pfx) ++{ ++ struct snd_soc_tplg_channel *c; ++ const char *s; ++ unsigned int index; ++ int err; ++ ++ if (count == 0) ++ return 0; ++ err = tplg_save_printf(dst, pfx, "channel {\n"); ++ for (index = 0; err >= 0 && index < count; index++) { ++ c = channel + index; ++ s = tplg_channel_name(c->id); ++ if (s == NULL) ++ err = tplg_save_printf(dst, pfx, "\t%u", c->id); ++ else ++ err = tplg_save_printf(dst, pfx, "\t%s", s); ++ if (err >= 0) ++ err = tplg_save_printf(dst, NULL, " {\n"); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "\t\treg %d\n", c->reg); ++ if (err >= 0 && c->shift > 0) ++ err = tplg_save_printf(dst, pfx, "\t\tshift %u\n", c->shift); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "\t}\n"); ++ } ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} +diff --git a/src/topology/ctl.c b/src/topology/ctl.c +index 539329cd661f..979cc1b035f1 100644 +--- a/src/topology/ctl.c ++++ b/src/topology/ctl.c +@@ -28,15 +28,16 @@ struct ctl_access_elem { + }; + + /* CTL access strings and codes */ ++/* place the multi-bit values on top - like read_write - for save */ + static const struct ctl_access_elem ctl_access[] = { ++ {"read_write", SNDRV_CTL_ELEM_ACCESS_READWRITE}, ++ {"tlv_read_write", SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE}, + {"read", SNDRV_CTL_ELEM_ACCESS_READ}, + {"write", SNDRV_CTL_ELEM_ACCESS_WRITE}, +- {"read_write", SNDRV_CTL_ELEM_ACCESS_READWRITE}, + {"volatile", SNDRV_CTL_ELEM_ACCESS_VOLATILE}, + {"timestamp", SNDRV_CTL_ELEM_ACCESS_TIMESTAMP}, + {"tlv_read", SNDRV_CTL_ELEM_ACCESS_TLV_READ}, + {"tlv_write", SNDRV_CTL_ELEM_ACCESS_TLV_WRITE}, +- {"tlv_read_write", SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE}, + {"tlv_command", SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND}, + {"inactive", SNDRV_CTL_ELEM_ACCESS_INACTIVE}, + {"lock", SNDRV_CTL_ELEM_ACCESS_LOCK}, +@@ -103,6 +104,46 @@ int parse_access(snd_config_t *cfg, + return err; + } + ++/* Save Access */ ++static int tplg_save_access(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct snd_soc_tplg_ctl_hdr *hdr, char **dst, ++ const char *pfx) ++{ ++ const char *last; ++ unsigned int j, count, access, cval; ++ int err; ++ ++ if (hdr->access == 0) ++ return 0; ++ ++ access = hdr->access; ++ for (j = 0, count = 0, last = NULL; j < ARRAY_SIZE(ctl_access); j++) { ++ cval = ctl_access[j].value; ++ if ((access & cval) == cval) { ++ access &= ~cval; ++ last = ctl_access[j].name; ++ count++; ++ } ++ } ++ if (count == 1) ++ return tplg_save_printf(dst, pfx, "access.0 %s\n", last); ++ err = tplg_save_printf(dst, pfx, "access [\n"); ++ if (err < 0) ++ return err; ++ access = hdr->access; ++ for (j = 0; j < ARRAY_SIZE(ctl_access); j++) { ++ cval = ctl_access[j].value; ++ if ((access & cval) == cval) { ++ err = tplg_save_printf(dst, pfx, "\t%s\n", ++ ctl_access[j].name); ++ if (err < 0) ++ return err; ++ access &= ~cval; ++ } ++ } ++ return tplg_save_printf(dst, pfx, "]\n"); ++} ++ + /* copy referenced TLV to the mixer control */ + static int copy_tlv(struct tplg_elem *elem, struct tplg_elem *ref) + { +@@ -358,6 +399,37 @@ int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg, + return err; + } + ++/* save TLV data */ ++int tplg_save_tlv(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ struct snd_soc_tplg_ctl_tlv *tlv = elem->tlv; ++ struct snd_soc_tplg_tlv_dbscale *scale; ++ int err; ++ ++ if (tlv->type != SNDRV_CTL_TLVT_DB_SCALE) { ++ SNDERR("unknown TLV type"); ++ return -EINVAL; ++ } ++ ++ scale = &tlv->scale; ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "\tscale {\n"); ++ if (err >= 0 && scale->min) ++ err = tplg_save_printf(dst, pfx, "\t\tmin %i\n", scale->min); ++ if (err >= 0 && scale->step > 0) ++ err = tplg_save_printf(dst, pfx, "\t\tstep %i\n", scale->step); ++ if (err >= 0 && scale->mute > 0) ++ err = tplg_save_printf(dst, pfx, "\t\tmute %i\n", scale->mute); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "\t}\n"); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} ++ + /* Parse Control Bytes */ + int tplg_parse_control_bytes(snd_tplg_t *tplg, + snd_config_t *cfg, +@@ -430,7 +502,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg, + } + + if (strcmp(id, "data") == 0) { +- err = tplg_parse_data_refs(n, elem); ++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); + if (err < 0) + return err; + continue; +@@ -485,6 +557,49 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg, + return 0; + } + ++/* save control bytes */ ++int tplg_save_control_bytes(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ struct snd_soc_tplg_bytes_control *be = elem->bytes_ext; ++ char pfx2[16]; ++ int err; ++ ++ if (!be) ++ return 0; ++ ++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: ""); ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ if (err < 0) ++ return err; ++ if (err >= 0 && elem->index > 0) ++ err = tplg_save_printf(dst, pfx, "\tindex %u\n", elem->index); ++ if (err >= 0 && be->base > 0) ++ err = tplg_save_printf(dst, pfx, "\tbase %u\n", be->base); ++ if (err >= 0 && be->num_regs > 0) ++ err = tplg_save_printf(dst, pfx, "\tnum_regs %u\n", be->num_regs); ++ if (err >= 0 && be->max > 0) ++ err = tplg_save_printf(dst, pfx, "\tmax %u\n", be->max); ++ if (err >= 0 && be->mask > 0) ++ err = tplg_save_printf(dst, pfx, "\tmask %u\n", be->mask); ++ if (err >= 0) ++ err = tplg_save_ops(tplg, &be->hdr, dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_ext_ops(tplg, be, dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_access(tplg, &be->hdr, dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TLV, ++ "tlv", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA, ++ "data", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} ++ + /* Parse Control Enums. */ + int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED) +@@ -559,7 +674,7 @@ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg, + } + + if (strcmp(id, "data") == 0) { +- err = tplg_parse_data_refs(n, elem); ++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); + if (err < 0) + return err; + continue; +@@ -582,6 +697,42 @@ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg, + return 0; + } + ++/* save control eunm */ ++int tplg_save_control_enum(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ struct snd_soc_tplg_enum_control *ec = elem->enum_ctrl; ++ char pfx2[16]; ++ int err; ++ ++ if (!ec) ++ return 0; ++ ++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: ""); ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ if (err < 0) ++ return err; ++ if (err >= 0 && elem->index > 0) ++ err = tplg_save_printf(dst, pfx, "\tindex %u\n", elem->index); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TEXT, ++ "texts", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_channels(tplg, ec->channel, ec->num_channels, ++ dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_ops(tplg, &ec->hdr, dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_access(tplg, &ec->hdr, dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA, ++ "data", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} ++ + /* Parse Controls. + * + * Mixer control. Supports multiple channels. +@@ -683,7 +834,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg, + } + + if (strcmp(id, "data") == 0) { +- err = tplg_parse_data_refs(n, elem); ++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); + if (err < 0) + return err; + continue; +@@ -709,6 +860,46 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg, + return 0; + } + ++int tplg_save_control_mixer(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, char **dst, ++ const char *pfx) ++{ ++ struct snd_soc_tplg_mixer_control *mc = elem->mixer_ctrl; ++ char pfx2[16]; ++ int err; ++ ++ if (!mc) ++ return 0; ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ if (err < 0) ++ return err; ++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: ""); ++ if (err >= 0 && elem->index > 0) ++ err = tplg_save_printf(dst, pfx, "\tindex %u\n", elem->index); ++ if (err >= 0) ++ err = tplg_save_channels(tplg, mc->channel, mc->num_channels, ++ dst, pfx2); ++ if (err >= 0 && mc->max > 0) ++ err = tplg_save_printf(dst, pfx, "\tmax %u\n", mc->max); ++ if (err >= 0 && mc->invert > 0) ++ err = tplg_save_printf(dst, pfx, "\tinvert 1\n", mc->max); ++ if (err >= 0 && mc->invert > 0) ++ err = tplg_save_printf(dst, pfx, "\tinvert 1\n", mc->max); ++ if (err >= 0) ++ err = tplg_save_ops(tplg, &mc->hdr, dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_access(tplg, &mc->hdr, dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TLV, ++ "tlv", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA, ++ "data", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} ++ + static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr, + struct snd_tplg_ctl_template *t) + { +diff --git a/src/topology/dapm.c b/src/topology/dapm.c +index ad7092107896..2bdacedca125 100644 +--- a/src/topology/dapm.c ++++ b/src/topology/dapm.c +@@ -43,7 +43,6 @@ static const struct map_elem widget_map[] = { + {"effect", SND_SOC_TPLG_DAPM_EFFECT}, + {"siggen", SND_SOC_TPLG_DAPM_SIGGEN}, + {"src", SND_SOC_TPLG_DAPM_SRC}, +- {"asrc", SND_SOC_TPLG_DAPM_ASRC}, + {"encoder", SND_SOC_TPLG_DAPM_ENCODER}, + {"decoder", SND_SOC_TPLG_DAPM_DECODER}, + }; +@@ -60,70 +59,16 @@ static int lookup_widget(const char *w) + return -EINVAL; + } + +-static int tplg_parse_dapm_mixers(snd_config_t *cfg, struct tplg_elem *elem) ++static const char *get_widget_name(unsigned int type) + { +- snd_config_iterator_t i, next; +- snd_config_t *n; +- const char *value = NULL; +- +- tplg_dbg(" DAPM Mixer Controls: %s\n", elem->id); +- +- snd_config_for_each(i, next, cfg) { +- n = snd_config_iterator_entry(i); +- +- /* get value */ +- if (snd_config_get_string(n, &value) < 0) +- continue; +- +- tplg_ref_add(elem, SND_TPLG_TYPE_MIXER, value); +- tplg_dbg("\t\t %s\n", value); +- } +- +- return 0; +-} +- +-static int tplg_parse_dapm_enums(snd_config_t *cfg, struct tplg_elem *elem) +-{ +- snd_config_iterator_t i, next; +- snd_config_t *n; +- const char *value = NULL; +- +- tplg_dbg(" DAPM Enum Controls: %s\n", elem->id); +- +- snd_config_for_each(i, next, cfg) { +- n = snd_config_iterator_entry(i); +- +- /* get value */ +- if (snd_config_get_string(n, &value) < 0) +- continue; +- +- tplg_ref_add(elem, SND_TPLG_TYPE_ENUM, value); +- tplg_dbg("\t\t %s\n", value); +- } +- +- return 0; +-} +- +-static int tplg_parse_dapm_bytes(snd_config_t *cfg, struct tplg_elem *elem) +-{ +- snd_config_iterator_t i, next; +- snd_config_t *n; +- const char *value = NULL; +- +- tplg_dbg(" DAPM Bytes Controls: %s\n", elem->id); +- +- snd_config_for_each(i, next, cfg) { +- n = snd_config_iterator_entry(i); +- +- /* get value */ +- if (snd_config_get_string(n, &value) < 0) +- continue; ++ unsigned int i; + +- tplg_ref_add(elem, SND_TPLG_TYPE_BYTES, value); +- tplg_dbg("\t\t %s\n", value); ++ for (i = 0; i < ARRAY_SIZE(widget_map); i++) { ++ if ((unsigned int)widget_map[i].id == type) ++ return widget_map[i].name; + } + +- return 0; ++ return NULL; + } + + /* move referenced controls to the widget */ +@@ -340,7 +285,7 @@ struct tplg_elem *tplg_elem_new_route(snd_tplg_t *tplg, int index) + + #define LINE_SIZE 1024 + +-/* line is defined as '"source, control, sink"' */ ++/* line is defined as '"sink, control, source"' */ + static int tplg_parse_line(const char *text, + struct snd_soc_tplg_dapm_graph_elem *line) + { +@@ -470,6 +415,77 @@ int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg, + return 0; + } + ++/* save DAPM graph */ ++int tplg_save_dapm_graph(snd_tplg_t *tplg, int index, char **dst, const char *pfx) ++{ ++ struct snd_soc_tplg_dapm_graph_elem *route; ++ struct list_head *pos; ++ struct tplg_elem *elem; ++ int err, first = 1, old_index = -1; ++ unsigned block = -1, count = 0; ++ ++ list_for_each(pos, &tplg->route_list) { ++ elem = list_entry(pos, struct tplg_elem, list); ++ if (!elem->route || elem->type != SND_TPLG_TYPE_DAPM_GRAPH) ++ continue; ++ if (index >= 0 && elem->index != index) ++ continue; ++ count++; ++ } ++ if (count == 0) ++ return 0; ++ err = tplg_save_printf(dst, pfx, "SectionGraph {\n"); ++ list_for_each(pos, &tplg->route_list) { ++ elem = list_entry(pos, struct tplg_elem, list); ++ if (!elem->route || elem->type != SND_TPLG_TYPE_DAPM_GRAPH) ++ continue; ++ if (index >= 0 && elem->index != index) ++ continue; ++ if (old_index != elem->index) { ++ if (old_index >= 0) { ++ err = tplg_save_printf(dst, pfx, "\t\t]\n"); ++ if (err < 0) ++ return err; ++ err = tplg_save_printf(dst, pfx, "\t}\n"); ++ if (err < 0) ++ return err; ++ } ++ old_index = elem->index; ++ block++; ++ first = 1; ++ err = tplg_save_printf(dst, pfx, "\tset%u {\n", block); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "\t\tindex %u\n", ++ elem->index); ++ if (err < 0) ++ return err; ++ } ++ if (first) { ++ first = 0; ++ err = tplg_save_printf(dst, pfx, "\t\tlines [\n", elem->index); ++ if (err < 0) ++ return err; ++ } ++ route = elem->route; ++ err = tplg_save_printf(dst, pfx, "\t\t\t'%s, %s, %s'\n", ++ route->sink, route->control, ++ route->source); ++ if (err < 0) ++ return err; ++ } ++ ++ if (!first) { ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "\t\t]\n"); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "\t}\n"); ++ } ++ ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} ++ + /* DAPM Widget */ + int tplg_parse_dapm_widget(snd_tplg_t *tplg, + snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) +@@ -595,7 +611,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + } + + if (strcmp(id, "enum") == 0) { +- err = tplg_parse_dapm_enums(n, elem); ++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_ENUM); + if (err < 0) + return err; + +@@ -603,7 +619,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + } + + if (strcmp(id, "mixer") == 0) { +- err = tplg_parse_dapm_mixers(n, elem); ++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_MIXER); + if (err < 0) + return err; + +@@ -611,7 +627,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + } + + if (strcmp(id, "bytes") == 0) { +- err = tplg_parse_dapm_bytes(n, elem); ++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_BYTES); + if (err < 0) + return err; + +@@ -619,7 +635,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + } + + if (strcmp(id, "data") == 0) { +- err = tplg_parse_data_refs(n, elem); ++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); + if (err < 0) + return err; + continue; +@@ -629,6 +645,66 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + return 0; + } + ++/* save DAPM widget */ ++int tplg_save_dapm_widget(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ struct snd_soc_tplg_dapm_widget *widget = elem->widget; ++ const char *s; ++ char pfx2[16]; ++ int err; ++ ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ if (err >= 0 && elem->index) ++ err = tplg_save_printf(dst, pfx, "\tindex %u\n", ++ elem->index); ++ if (err >= 0) { ++ s = get_widget_name(widget->id); ++ if (s) ++ err = tplg_save_printf(dst, pfx, "\ttype %s\n", s); ++ else ++ err = tplg_save_printf(dst, pfx, "\ttype %u\n", ++ widget->id); ++ } ++ if (err >= 0 && widget->sname[0]) ++ err = tplg_save_printf(dst, pfx, "\tstream_name '%s'\n", ++ widget->sname); ++ if (err >= 0 && widget->reg) ++ err = tplg_save_printf(dst, pfx, "\tno_pm 1\n"); ++ if (err >= 0 && widget->shift) ++ err = tplg_save_printf(dst, pfx, "\tshift %u\n", ++ widget->shift); ++ if (err >= 0 && widget->invert) ++ err = tplg_save_printf(dst, pfx, "\tinvert %u\n", ++ widget->invert); ++ if (err >= 0 && widget->subseq) ++ err = tplg_save_printf(dst, pfx, "\tsubseq %u\n", ++ widget->subseq); ++ if (err >= 0 && widget->event_type) ++ err = tplg_save_printf(dst, pfx, "\tevent_type %u\n", ++ widget->event_type); ++ if (err >= 0 && widget->event_flags) ++ err = tplg_save_printf(dst, pfx, "\tevent_flags %u\n", ++ widget->event_flags); ++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: ""); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_ENUM, ++ "enum", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_MIXER, ++ "mixer", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_BYTES, ++ "bytes", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA, ++ "data", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} ++ + int tplg_add_route(snd_tplg_t *tplg, struct snd_tplg_graph_elem *t, int index) + { + struct tplg_elem *elem; +@@ -744,7 +820,6 @@ int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + default: + SNDERR("error: widget %s: invalid type %d for ctl %d\n", + wt->name, ct->type, i); +- ret = -EINVAL; + break; + } + +diff --git a/src/topology/data.c b/src/topology/data.c +index 9807445e8c37..11cd73f5ed6e 100644 +--- a/src/topology/data.c ++++ b/src/topology/data.c +@@ -21,6 +21,10 @@ + #include "tplg_local.h" + #include + ++#define UUID_FORMAT "\ ++0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, \ ++0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x" ++ + /* Get private data buffer of an element */ + struct snd_soc_tplg_private *get_priv_data(struct tplg_elem *elem) + { +@@ -64,6 +68,96 @@ struct snd_soc_tplg_private *get_priv_data(struct tplg_elem *elem) + return priv; + } + ++/* Parse references for the element, either a single data section ++ * or a list of data sections. ++ */ ++int tplg_parse_refs(snd_config_t *cfg, struct tplg_elem *elem, ++ unsigned int type) ++{ ++ snd_config_type_t cfg_type; ++ snd_config_iterator_t i, next; ++ snd_config_t *n; ++ const char *val = NULL; ++ int err, count; ++ ++ cfg_type = snd_config_get_type(cfg); ++ ++ /* refer to a single data section */ ++ if (cfg_type == SND_CONFIG_TYPE_STRING) { ++ if (snd_config_get_string(cfg, &val) < 0) ++ return -EINVAL; ++ ++ tplg_dbg("\tref data: %s\n", val); ++ err = tplg_ref_add(elem, type, val); ++ if (err < 0) ++ return err; ++ return 1; ++ } ++ ++ if (cfg_type != SND_CONFIG_TYPE_COMPOUND) { ++ SNDERR("error: compound type expected for %s", elem->id); ++ return -EINVAL; ++ } ++ ++ /* refer to a list of data sections */ ++ count = 0; ++ snd_config_for_each(i, next, cfg) { ++ const char *val; ++ ++ n = snd_config_iterator_entry(i); ++ if (snd_config_get_string(n, &val) < 0) ++ continue; ++ ++ tplg_dbg("\tref data: %s\n", val); ++ err = tplg_ref_add(elem, type, val); ++ if (err < 0) ++ return err; ++ count++; ++ } ++ ++ return count; ++} ++ ++/* save references */ ++int tplg_save_refs(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, unsigned int type, ++ const char *id, char **dst, const char *pfx) ++{ ++ struct tplg_ref *ref, *last; ++ struct list_head *pos; ++ int err, count; ++ ++ count = 0; ++ last = NULL; ++ list_for_each(pos, &elem->ref_list) { ++ ref = list_entry(pos, struct tplg_ref, list); ++ if (ref->type == type) { ++ last = ref; ++ count++; ++ } ++ } ++ ++ if (count == 0) ++ return 0; ++ ++ if (count == 1) ++ return tplg_save_printf(dst, pfx, "%s '%s'\n", id, last->id); ++ ++ err = tplg_save_printf(dst, pfx, "%s [\n", id); ++ if (err < 0) ++ return err; ++ list_for_each(pos, &elem->ref_list) { ++ ref = list_entry(pos, struct tplg_ref, list); ++ if (ref->type == type) { ++ err = tplg_save_printf(dst, pfx, "\t'%s'\n", ref->id); ++ if (err < 0) ++ return err; ++ } ++ } ++ ++ return tplg_save_printf(dst, pfx, "]\n"); ++} ++ + /* Get Private data from a file. */ + static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem) + { +@@ -140,58 +234,98 @@ err: + static void dump_priv_data(struct tplg_elem *elem) + { + struct snd_soc_tplg_private *priv = elem->data; +- unsigned int i, j = 0; ++ unsigned int i; + + tplg_dbg(" elem size = %d, priv data size = %d\n", + elem->size, priv->size); + + for (i = 0; i < priv->size; i++) { +- if (j++ % 8 == 0) ++ if (i > 0 && (i % 16) == 0) + tplg_dbg("\n"); + +- tplg_dbg(" 0x%x", *p++); ++ tplg_dbg(" %02x:", *p++); + } + + tplg_dbg("\n\n"); + } + ++static inline int check_nibble(unsigned char c) ++{ ++ return (c >= '0' && c <= '9') || ++ (c >= 'a' && c <= 'f') || ++ (c >= 'A' && c <= 'F'); ++} ++ + /* get number of hex value elements in CSV list */ + static int get_hex_num(const char *str) + { +- int commas = 0, values = 0, len = strlen(str); +- const char *end = str + len; ++ int delims, values, len = strlen(str); ++ const char *s, *end = str + len; ++ ++ /* check "aa:bb:00" syntax */ ++ s = str; ++ delims = values = 0; ++ while (s < end) { ++ /* skip white space */ ++ if (isspace(*s)) { ++ s++; ++ continue; ++ } ++ /* find delimeters */ ++ if (*s == ':') { ++ delims++; ++ s++; ++ continue; ++ } ++ /* check 00 hexadecimal value */ ++ if (s + 1 <= end) { ++ if (check_nibble(s[0]) && check_nibble(s[1])) { ++ values++; ++ } else { ++ goto format2; ++ } ++ s++; ++ } ++ s++; ++ } ++ goto end; + ++format2: + /* we expect "0x0, 0x0, 0x0" */ +- while (str < end) { ++ s = str; ++ delims = values = 0; ++ while (s < end) { + + /* skip white space */ +- if (isspace(*str)) { +- str++; ++ if (isspace(*s)) { ++ s++; + continue; + } + + /* find delimeters */ +- if (*str == ',') { +- commas++; +- str++; ++ if (*s == ',') { ++ delims++; ++ s++; + continue; + } + + /* find 0x[0-9] values */ +- if (*str == '0' && str + 2 <= end) { +- if (str[1] == 'x' && str[2] >= '0' && str[2] <= 'f') { ++ if (*s == '0' && s + 2 <= end) { ++ if (s[1] == 'x' && check_nibble(s[2])) { ++ if (check_nibble(s[3])) ++ s++; + values++; +- str += 3; +- } else { +- str++; ++ s += 2; + } ++ s++; + } + +- str++; ++ s++; + } + ++end: + /* there should always be one less comma than value */ +- if (values -1 != commas) ++ if (values - 1 != delims) + return -EINVAL; + + return values; +@@ -547,6 +681,71 @@ static int build_tuples(snd_tplg_t *tplg, struct tplg_elem *elem) + return 0; + } + ++struct tuple_type { ++ unsigned int type; ++ const char *name; ++ unsigned int size; ++}; ++ ++static struct tuple_type tuple_types[] = { ++ { ++ .type = SND_SOC_TPLG_TUPLE_TYPE_UUID, ++ .name = "uuid", ++ .size = 4, ++ }, ++ { ++ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING, ++ .name = "string", ++ .size = 6, ++ }, ++ { ++ .type = SND_SOC_TPLG_TUPLE_TYPE_BOOL, ++ .name = "bool", ++ .size = 4, ++ }, ++ { ++ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, ++ .name = "byte", ++ .size = 4, ++ }, ++ { ++ .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT, ++ .name = "short", ++ .size = 5, ++ }, ++ { ++ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, ++ .name = "word", ++ .size = 4 ++ }, ++}; ++ ++static int get_tuple_type(const char *name) ++{ ++ struct tuple_type *t; ++ unsigned int i; ++ ++ /* skip initial index for sorting */ ++ while ((*name >= '0' && *name <= '9') || *name == '_') ++ name++; ++ for (i = 0; i < ARRAY_SIZE(tuple_types); i++) { ++ t = &tuple_types[i]; ++ if (strncasecmp(t->name, name, t->size) == 0) ++ return t->type; ++ } ++ return -EINVAL; ++} ++ ++static const char *get_tuple_type_name(unsigned int type) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(tuple_types); i++) ++ if (tuple_types[i].type == type) ++ return tuple_types[i].name; ++ return NULL; ++} ++ + static int parse_tuple_set(snd_config_t *cfg, + struct tplg_tuple_set **s) + { +@@ -554,28 +753,17 @@ static int parse_tuple_set(snd_config_t *cfg, + snd_config_t *n; + const char *id, *value; + struct tplg_tuple_set *set; +- unsigned int type, num_tuples = 0; ++ unsigned int num_tuples = 0; + struct tplg_tuple *tuple; + unsigned int tuple_val; +- int ival; ++ int type, ival; + + snd_config_get_id(cfg, &id); + +- if (strncmp(id, "uuid", 4) == 0) +- type = SND_SOC_TPLG_TUPLE_TYPE_UUID; +- else if (strncmp(id, "string", 5) == 0) +- type = SND_SOC_TPLG_TUPLE_TYPE_STRING; +- else if (strncmp(id, "bool", 4) == 0) +- type = SND_SOC_TPLG_TUPLE_TYPE_BOOL; +- else if (strncmp(id, "byte", 4) == 0) +- type = SND_SOC_TPLG_TUPLE_TYPE_BYTE; +- else if (strncmp(id, "short", 5) == 0) +- type = SND_SOC_TPLG_TUPLE_TYPE_SHORT; +- else if (strncmp(id, "word", 4) == 0) +- type = SND_SOC_TPLG_TUPLE_TYPE_WORD; +- else { +- SNDERR("error: invalid tuple type '%s'\n", id); +- return -EINVAL; ++ type = get_tuple_type(id); ++ if (type < 0) { ++ SNDERR("error: invalid tuple type '%s'", id); ++ return type; + } + + snd_config_for_each(i, next, cfg) +@@ -664,6 +852,84 @@ err: + return -EINVAL; + } + ++/* save tuple set */ ++static int tplg_save_tuple_set(struct tplg_vendor_tuples *tuples, ++ unsigned int set_index, ++ char **dst, const char *pfx) ++{ ++ struct tplg_tuple_set *set; ++ struct tplg_tuple *tuple; ++ const char *s, *fmt; ++ char buf[32]; ++ unsigned int i; ++ int err; ++ ++ set = tuples->set[set_index]; ++ if (set->num_tuples == 0) ++ return 0; ++ s = get_tuple_type_name(set->type); ++ if (s == NULL) ++ return -EINVAL; ++ if (tuples->num_sets < 10) ++ fmt = "%u_"; ++ else if (tuples->num_sets < 100) ++ fmt = "%02u_"; ++ else if (tuples->num_sets < 1000) ++ fmt = "%03u_"; ++ else ++ return -EINVAL; ++ if (set->num_tuples > 1) { ++ snprintf(buf, sizeof(buf), "tuples.%s%%s {\n", fmt); ++ err = tplg_save_printf(dst, NULL, buf, set_index, s); ++ if (err < 0) ++ return err; ++ } ++ for (i = 0; i < set->num_tuples; i++) { ++ tuple = &set->tuple[i]; ++ if (set->num_tuples == 1) { ++ snprintf(buf, sizeof(buf), "tuples.%s%%s.'%%s' ", fmt); ++ err = tplg_save_printf(dst, NULL, buf, ++ set_index, s, tuple->token); ++ } else { ++ err = tplg_save_printf(dst, pfx, "\t'%s' ", ++ tuple->token); ++ } ++ switch (set->type) { ++ case SND_SOC_TPLG_TUPLE_TYPE_UUID: ++ err = tplg_save_printf(dst, NULL, "'" UUID_FORMAT "'\n", ++ tuple->uuid[0], tuple->uuid[1], ++ tuple->uuid[2], tuple->uuid[3], ++ tuple->uuid[4], tuple->uuid[5], ++ tuple->uuid[6], tuple->uuid[7], ++ tuple->uuid[8], tuple->uuid[9], ++ tuple->uuid[10], tuple->uuid[11], ++ tuple->uuid[12], tuple->uuid[13], ++ tuple->uuid[14], tuple->uuid[15]); ++ break; ++ case SND_SOC_TPLG_TUPLE_TYPE_STRING: ++ err = tplg_save_printf(dst, NULL, "'%s'\n", ++ tuple->string); ++ break; ++ case SND_SOC_TPLG_TUPLE_TYPE_BOOL: ++ case SND_SOC_TPLG_TUPLE_TYPE_BYTE: ++ case SND_SOC_TPLG_TUPLE_TYPE_SHORT: ++ err = tplg_save_printf(dst, NULL, "%u\n", tuple->value); ++ break; ++ case SND_SOC_TPLG_TUPLE_TYPE_WORD: ++ tplg_nice_value_format(buf, sizeof(buf), tuple->value); ++ err = tplg_save_printf(dst, NULL, "%s\n", buf); ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (err < 0) ++ return err; ++ } ++ if (set->num_tuples > 1) ++ return tplg_save_printf(dst, pfx, "}\n"); ++ return 0; ++} ++ + static int parse_tuple_sets(snd_config_t *cfg, + struct tplg_vendor_tuples *tuples) + { +@@ -710,87 +976,24 @@ static int parse_tuple_sets(snd_config_t *cfg, + return 0; + } + +-/* Parse tuples references for a data element, either a single tuples section +- * or a list of tuples sections. +- */ +-static int parse_tuples_refs(snd_config_t *cfg, +- struct tplg_elem *elem) +-{ +- snd_config_type_t type; +- snd_config_iterator_t i, next; +- snd_config_t *n; +- const char *val = NULL; +- +- type = snd_config_get_type(cfg); +- +- /* refer to a single tuples section */ +- if (type == SND_CONFIG_TYPE_STRING) { +- if (snd_config_get_string(cfg, &val) < 0) +- return -EINVAL; +- tplg_dbg("\ttuples: %s\n", val); +- return tplg_ref_add(elem, SND_TPLG_TYPE_TUPLE, val); +- } +- +- if (type != SND_CONFIG_TYPE_COMPOUND) { +- SNDERR("error: compound type expected for %s", elem->id); +- return -EINVAL; +- } +- +- /* refer to a list of data sections */ +- snd_config_for_each(i, next, cfg) { +- const char *val; +- +- n = snd_config_iterator_entry(i); +- if (snd_config_get_string(n, &val) < 0) +- continue; +- +- tplg_dbg("\ttuples: %s\n", val); +- tplg_ref_add(elem, SND_TPLG_TYPE_TUPLE, val); +- } +- +- return 0; +-} +- +-/* Parse private data references for the element, either a single data section +- * or a list of data sections. +- */ +-int tplg_parse_data_refs(snd_config_t *cfg, +- struct tplg_elem *elem) ++/* save tuple sets */ ++int tplg_save_tuple_sets(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) + { +- snd_config_type_t type; +- snd_config_iterator_t i, next; +- snd_config_t *n; +- const char *val = NULL; +- +- type = snd_config_get_type(cfg); +- +- /* refer to a single data section */ +- if (type == SND_CONFIG_TYPE_STRING) { +- if (snd_config_get_string(cfg, &val) < 0) +- return -EINVAL; +- +- tplg_dbg("\tdata: %s\n", val); +- return tplg_ref_add(elem, SND_TPLG_TYPE_DATA, val); +- } +- +- if (type != SND_CONFIG_TYPE_COMPOUND) { +- SNDERR("error: compound type expected for %s", elem->id); +- return -EINVAL; +- } +- +- /* refer to a list of data sections */ +- snd_config_for_each(i, next, cfg) { +- const char *val; +- +- n = snd_config_iterator_entry(i); +- if (snd_config_get_string(n, &val) < 0) +- continue; ++ struct tplg_vendor_tuples *tuples = elem->tuples; ++ unsigned int i; ++ int err = 0; + +- tplg_dbg("\tdata: %s\n", val); +- tplg_ref_add(elem, SND_TPLG_TYPE_DATA, val); ++ for (i = 0; i < tuples->num_sets; i++) { ++ err = tplg_save_printf(dst, pfx, ""); ++ if (err < 0) ++ break; ++ err = tplg_save_tuple_set(tuples, i, dst, pfx); ++ if (err < 0) ++ break; + } +- +- return 0; ++ return err; + } + + /* Parse vendor tokens +@@ -844,6 +1047,31 @@ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, + return 0; + } + ++/* save vendor tokens */ ++int tplg_save_tokens(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ struct tplg_vendor_tokens *tokens = elem->tokens; ++ unsigned int i; ++ int err; ++ ++ if (!tokens || tokens->num_tokens == 0) ++ return 0; ++ ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ if (err < 0) ++ return err; ++ for (i = 0; err >= 0 && i < tokens->num_tokens; i++) ++ err = tplg_save_printf(dst, pfx, "\t'%s' %u\n", ++ tokens->token[i].id, ++ tokens->token[i].value); ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ if (err < 0) ++ return err; ++ return 0; ++} ++ + /* Parse vendor tuples. + */ + int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg, +@@ -890,6 +1118,29 @@ int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg, + return 0; + } + ++/* save vendor tuples */ ++int tplg_save_tuples(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ char pfx2[16]; ++ int err; ++ ++ if (!elem->tuples) ++ return 0; ++ ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: ""); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TOKEN, ++ "tokens", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_tuple_sets(tplg, elem, dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return 0; ++} ++ + /* Free handler of tuples */ + void tplg_free_tuples(void *obj) + { +@@ -944,7 +1195,7 @@ int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg, + + + if (strcmp(id, "data") == 0) { +- err = tplg_parse_data_refs(n, elem); ++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); + if (err < 0) + return err; + continue; +@@ -954,6 +1205,51 @@ int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg, + return 0; + } + ++/* save manifest data */ ++int tplg_save_manifest_data(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, char **dst, ++ const char *pfx) ++{ ++ struct list_head *pos; ++ struct tplg_ref *ref; ++ int err, index, count; ++ ++ /* for each ref in this manifest elem */ ++ count = 0; ++ list_for_each(pos, &elem->ref_list) { ++ ref = list_entry(pos, struct tplg_ref, list); ++ if (ref->type != SND_TPLG_TYPE_DATA) ++ continue; ++ count++; ++ } ++ if (count > 1) { ++ err = tplg_save_printf(dst, NULL, "'%s'.data [\n", elem->id); ++ if (err < 0) ++ return err; ++ } ++ index = 0; ++ list_for_each(pos, &elem->ref_list) { ++ ref = list_entry(pos, struct tplg_ref, list); ++ if (ref->type != SND_TPLG_TYPE_DATA) ++ continue; ++ if (count == 1) { ++ err = tplg_save_printf(dst, NULL, "'%s'.data.%u '%s'\n", ++ elem->id, index, ref->id); ++ } else { ++ err = tplg_save_printf(dst, pfx, "\t'%s'\n", ref->id); ++ if (err < 0) ++ return err; ++ } ++ index++; ++ } ++ if (count > 1) { ++ err = tplg_save_printf(dst, pfx, "]\n"); ++ if (err < 0) ++ return err; ++ } ++ return 0; ++} ++ + /* merge private data of manifest */ + int tplg_build_manifest_data(snd_tplg_t *tplg) + { +@@ -1064,7 +1360,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, + } + + if (strcmp(id, "tuples") == 0) { +- err = parse_tuples_refs(n, elem); ++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_TUPLE); + if (err < 0) + return err; + continue; +@@ -1083,6 +1379,81 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, + return err; + } + ++/* save data element */ ++int tplg_save_data(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ struct snd_soc_tplg_private *priv = elem->data; ++ struct list_head *pos; ++ struct tplg_ref *ref; ++ char pfx2[16]; ++ unsigned int i, count; ++ int err; ++ ++ count = 0; ++ if (priv && priv->size > 0) ++ count++; ++ list_for_each(pos, &elem->ref_list) { ++ ref = list_entry(pos, struct tplg_ref, list); ++ if (ref->type == SND_TPLG_TYPE_TUPLE) ++ count++; ++ } ++ if (elem->vendor_type > 0) ++ count++; ++ ++ if (count > 1) { ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ if (err >= 0) ++ err = tplg_save_printf(dst, NULL, ""); ++ } else { ++ err = tplg_save_printf(dst, NULL, "'%s'.", elem->id); ++ } ++ if (err >= 0 && priv && priv->size > 0) { ++ if (count > 1) { ++ err = tplg_save_printf(dst, pfx, ""); ++ if (err < 0) ++ return err; ++ } ++ if (priv->size > 8) { ++ err = tplg_save_printf(dst, NULL, "bytes\n"); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "\t'"); ++ } else { ++ err = tplg_save_printf(dst, NULL, "bytes '"); ++ } ++ if (err < 0) ++ return err; ++ for (i = 0; i < priv->size; i++) { ++ if (i > 0 && (i % 8) == 0) { ++ err = tplg_save_printf(dst, NULL, ":\n"); ++ if (err < 0) ++ return err; ++ err = tplg_save_printf(dst, pfx, "\t "); ++ if (err < 0) ++ return err; ++ } ++ err = tplg_save_printf(dst, NULL, "%s%02x", ++ (i % 8) == 0 ? "" : ":", ++ (unsigned char)priv->data[i]); ++ if (err < 0) ++ return err; ++ } ++ err = tplg_save_printf(dst, NULL, "'\n"); ++ } ++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: ""); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_TUPLE, ++ "tuples", dst, ++ count > 1 ? pfx2 : NULL); ++ if (err >= 0 && elem->vendor_type > 0) ++ err = tplg_save_printf(dst, pfx, "type %u", ++ elem->vendor_type); ++ if (err >= 0 && count > 1) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} ++ + /* Find a referenced data element and copy its data to the parent + * element's private data buffer. + * An element can refer to multiple data sections. Data of these sections +diff --git a/src/topology/elem.c b/src/topology/elem.c +index e79a68b71a91..89aed1fc0449 100644 +--- a/src/topology/elem.c ++++ b/src/topology/elem.c +@@ -30,6 +30,7 @@ struct tplg_table tplg_table[] = { + .size = sizeof(struct snd_soc_tplg_manifest), + .enew = 1, + .parse = tplg_parse_manifest_data, ++ .save = tplg_save_manifest_data, + }, + { + .name = "control mixer", +@@ -41,6 +42,7 @@ struct tplg_table tplg_table[] = { + .build = 1, + .enew = 1, + .parse = tplg_parse_control_mixer, ++ .save = tplg_save_control_mixer, + }, + { + .name = "control enum", +@@ -52,6 +54,7 @@ struct tplg_table tplg_table[] = { + .build = 1, + .enew = 1, + .parse = tplg_parse_control_enum, ++ .save = tplg_save_control_enum, + }, + { + .name = "control extended (bytes)", +@@ -63,6 +66,7 @@ struct tplg_table tplg_table[] = { + .build = 1, + .enew = 1, + .parse = tplg_parse_control_bytes, ++ .save = tplg_save_control_bytes, + }, + { + .name = "dapm widget", +@@ -74,6 +78,7 @@ struct tplg_table tplg_table[] = { + .build = 1, + .enew = 1, + .parse = tplg_parse_dapm_widget, ++ .save = tplg_save_dapm_widget, + }, + { + .name = "pcm", +@@ -85,6 +90,7 @@ struct tplg_table tplg_table[] = { + .build = 1, + .enew = 1, + .parse = tplg_parse_pcm, ++ .save = tplg_save_pcm, + }, + { + .name = "physical dai", +@@ -96,6 +102,7 @@ struct tplg_table tplg_table[] = { + .build = 1, + .enew = 1, + .parse = tplg_parse_dai, ++ .save = tplg_save_dai, + }, + { + .name = "be", +@@ -108,6 +115,7 @@ struct tplg_table tplg_table[] = { + .build = 1, + .enew = 1, + .parse = tplg_parse_link, ++ .save = tplg_save_link, + }, + { + .name = "cc", +@@ -119,6 +127,7 @@ struct tplg_table tplg_table[] = { + .build = 1, + .enew = 1, + .parse = tplg_parse_cc, ++ .save = tplg_save_cc, + }, + { + .name = "route (dapm graph)", +@@ -128,6 +137,7 @@ struct tplg_table tplg_table[] = { + .tsoc = SND_SOC_TPLG_TYPE_DAPM_GRAPH, + .build = 1, + .parse = tplg_parse_dapm_graph, ++ .gsave = tplg_save_dapm_graph, + }, + { + .name = "private data", +@@ -138,6 +148,7 @@ struct tplg_table tplg_table[] = { + .build = 1, + .enew = 1, + .parse = tplg_parse_data, ++ .save = tplg_save_data, + }, + { + .name = "text", +@@ -147,6 +158,7 @@ struct tplg_table tplg_table[] = { + .size = sizeof(struct tplg_texts), + .enew = 1, + .parse = tplg_parse_text, ++ .save = tplg_save_text, + }, + { + .name = "tlv", +@@ -156,6 +168,7 @@ struct tplg_table tplg_table[] = { + .size = sizeof(struct snd_soc_tplg_ctl_tlv), + .enew = 1, + .parse = tplg_parse_tlv, ++ .save = tplg_save_tlv, + }, + { + .name = "stream config", +@@ -172,6 +185,7 @@ struct tplg_table tplg_table[] = { + .size = sizeof(struct snd_soc_tplg_stream_caps), + .enew = 1, + .parse = tplg_parse_stream_caps, ++ .save = tplg_save_stream_caps, + }, + { + .name = "token", +@@ -180,6 +194,7 @@ struct tplg_table tplg_table[] = { + .type = SND_TPLG_TYPE_TOKEN, + .enew = 1, + .parse = tplg_parse_tokens, ++ .save = tplg_save_tokens, + }, + { + .name = "tuple", +@@ -189,6 +204,7 @@ struct tplg_table tplg_table[] = { + .free = tplg_free_tuples, + .enew = 1, + .parse = tplg_parse_tuples, ++ .save = tplg_save_tuples, + }, + { + .name = "hw config", +@@ -198,6 +214,7 @@ struct tplg_table tplg_table[] = { + .size = sizeof(struct snd_soc_tplg_hw_config), + .enew = 1, + .parse = tplg_parse_hw_config, ++ .save = tplg_save_hw_config, + } + }; + +@@ -394,6 +411,7 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, + tplg_elem_insert(elem, list); + obj_size = tptr->size; + elem->free = tptr->free; ++ elem->table = tptr; + + /* create new object too if required */ + if (obj_size > 0) { +diff --git a/src/topology/ops.c b/src/topology/ops.c +index 073acdcb1453..ad72ef1b2cb6 100644 +--- a/src/topology/ops.c ++++ b/src/topology/ops.c +@@ -45,6 +45,18 @@ static int lookup_ops(const char *c) + return strtol(c, NULL, 0); + } + ++const char *tplg_ops_name(int type) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(control_map); i++) { ++ if (control_map[i].id == type) ++ return control_map[i].name; ++ } ++ ++ return NULL; ++} ++ + /* Parse Control operations. Ops can come from standard names above or + * bespoke driver controls with numbers >= 256 + */ +@@ -84,6 +96,46 @@ int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg, + return 0; + } + ++/* save control operations */ ++int tplg_save_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct snd_soc_tplg_ctl_hdr *hdr, char **dst, ++ const char *pfx) ++{ ++ const char *s; ++ int err; ++ ++ if (hdr->ops.info + hdr->ops.get + hdr->ops.put == 0) ++ return 0; ++ err = tplg_save_printf(dst, pfx, "ops.0 {\n"); ++ if (err >= 0 && hdr->ops.info > 0) { ++ s = tplg_ops_name(hdr->ops.info); ++ if (s == NULL) ++ err = tplg_save_printf(dst, pfx, "\tinfo %u\n", ++ hdr->ops.info); ++ else ++ err = tplg_save_printf(dst, pfx, "\tinfo %s\n", s); ++ } ++ if (err >= 0 && hdr->ops.get > 0) { ++ s = tplg_ops_name(hdr->ops.get); ++ if (s == NULL) ++ err = tplg_save_printf(dst, pfx, "\tget %u\n", ++ hdr->ops.get); ++ else ++ err = tplg_save_printf(dst, pfx, "\tget %s\n", s); ++ } ++ if (err >= 0 && hdr->ops.put > 0) { ++ s = tplg_ops_name(hdr->ops.put); ++ if (s == NULL) ++ err = tplg_save_printf(dst, pfx, "\tput %u\n", ++ hdr->ops.put); ++ else ++ err = tplg_save_printf(dst, pfx, "\tput %s\n", s); ++ } ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} ++ + /* Parse External Control operations. Ops can come from standard names above or + * bespoke driver controls with numbers >= 256 + */ +@@ -121,3 +173,43 @@ int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + + return 0; + } ++ ++/* save external control operations */ ++int tplg_save_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct snd_soc_tplg_bytes_control *be, ++ char **dst, const char *pfx) ++{ ++ const char *s; ++ int err; ++ ++ if (be->ext_ops.info + be->ext_ops.get + be->ext_ops.put == 0) ++ return 0; ++ err = tplg_save_printf(dst, pfx, "extops.0 {\n"); ++ if (err >= 0 && be->ext_ops.info > 0) { ++ s = tplg_ops_name(be->ext_ops.info); ++ if (s == NULL) ++ err = tplg_save_printf(dst, pfx, "\tinfo %u\n", ++ be->ext_ops.info); ++ else ++ err = tplg_save_printf(dst, pfx, "\tinfo %s\n", s); ++ } ++ if (err >= 0 && be->ext_ops.get > 0) { ++ s = tplg_ops_name(be->ext_ops.get); ++ if (s == NULL) ++ err = tplg_save_printf(dst, pfx, "\tget %u\n", ++ be->ext_ops.get); ++ else ++ err = tplg_save_printf(dst, pfx, "\tget %s\n", s); ++ } ++ if (err >= 0 && be->ext_ops.put > 0) { ++ s = tplg_ops_name(be->ext_ops.put); ++ if (s == NULL) ++ err = tplg_save_printf(dst, pfx, "\tput %u\n", ++ be->ext_ops.put); ++ else ++ err = tplg_save_printf(dst, pfx, "\tput %s\n", s); ++ } ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} +diff --git a/src/topology/parser.c b/src/topology/parser.c +index 11202769391c..de5edd1b6591 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -71,6 +71,8 @@ int tplg_get_unsigned(snd_config_t *n, unsigned *val, int base) + err = snd_config_get_integer(n, &lval); + if (err < 0) + return err; ++ if (lval < 0 && lval >= INT_MIN) ++ lval = UINT_MAX + lval + 1; + if (lval < 0 || lval > UINT_MAX) + return -ERANGE; + *val = lval; +@@ -79,6 +81,8 @@ int tplg_get_unsigned(snd_config_t *n, unsigned *val, int base) + err = snd_config_get_integer64(n, &llval); + if (err < 0) + return err; ++ if (llval < 0 && llval >= INT_MIN) ++ llval = UINT_MAX + llval + 1; + if (llval < 0 || llval > UINT_MAX) + return -ERANGE; + *val = llval; +diff --git a/src/topology/pcm.c b/src/topology/pcm.c +index 9b87549cabbd..d09fbe42f5da 100644 +--- a/src/topology/pcm.c ++++ b/src/topology/pcm.c +@@ -345,6 +345,13 @@ static int get_rate_value(const char* name) + return SND_PCM_RATE_UNKNOWN; + } + ++static const char *get_rate_name(int rate) ++{ ++ if (rate >= 0 && rate <= SND_PCM_RATE_LAST) ++ return snd_pcm_rate_names[rate]; ++ return NULL; ++} ++ + static int split_rate(struct snd_soc_tplg_stream_caps *caps, char *str) + { + char *s = NULL; +@@ -527,6 +534,80 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg, + return 0; + } + ++/* save stream caps */ ++int tplg_save_stream_caps(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ struct snd_soc_tplg_stream_caps *sc = elem->stream_caps; ++ const char *s; ++ unsigned int i; ++ int err, first; ++ ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ if (err >= 0 && sc->formats) { ++ err = tplg_save_printf(dst, pfx, "\tformats '"); ++ first = 1; ++ for (i = 0; err >= 0 && i < SND_PCM_FORMAT_LAST; i++) { ++ if (sc->formats & (1ULL << i)) { ++ s = snd_pcm_format_name(i); ++ err = tplg_save_printf(dst, NULL, "%s%s", ++ !first ? ", " : "", s); ++ first = 0; ++ } ++ } ++ if (err >= 0) ++ err = tplg_save_printf(dst, NULL, "'\n"); ++ } ++ if (err >= 0 && sc->rates) { ++ err = tplg_save_printf(dst, pfx, "\trates '"); ++ first = 1; ++ for (i = 0; err >= 0 && i < SND_PCM_RATE_LAST; i++) { ++ if (sc->rates & (1ULL << i)) { ++ s = get_rate_name(i); ++ err = tplg_save_printf(dst, NULL, "%s%s", ++ !first ? ", " : "", s); ++ first = 0; ++ } ++ } ++ if (err >= 0) ++ err = tplg_save_printf(dst, NULL, "'\n"); ++ } ++ if (err >= 0 && sc->rate_min) ++ err = tplg_save_printf(dst, pfx, "\trate_min %u\n", ++ sc->rate_min); ++ if (err >= 0 && sc->rate_max) ++ err = tplg_save_printf(dst, pfx, "\trate_max %u\n", ++ sc->rate_max); ++ if (err >= 0 && sc->channels_min) ++ err = tplg_save_printf(dst, pfx, "\tchannels_min %u\n", ++ sc->channels_min); ++ if (err >= 0 && sc->channels_max) ++ err = tplg_save_printf(dst, pfx, "\tchannels_max %u\n", ++ sc->channels_max); ++ if (err >= 0 && sc->periods_min) ++ err = tplg_save_printf(dst, pfx, "\tperiods_min %u\n", ++ sc->periods_min); ++ if (err >= 0 && sc->periods_max) ++ err = tplg_save_printf(dst, pfx, "\tperiods_max %u\n", ++ sc->periods_max); ++ if (err >= 0 && sc->period_size_min) ++ err = tplg_save_printf(dst, pfx, "\tperiod_size_min %u\n", ++ sc->period_size_min); ++ if (err >= 0 && sc->period_size_max) ++ err = tplg_save_printf(dst, pfx, "\tperiod_size_max %u\n", ++ sc->period_size_max); ++ if (err >= 0 && sc->buffer_size_min) ++ err = tplg_save_printf(dst, pfx, "\tbuffer_size_min %u\n", ++ sc->buffer_size_min); ++ if (err >= 0 && sc->buffer_size_max) ++ err = tplg_save_printf(dst, pfx, "\tbuffer_size_max %u\n", ++ sc->buffer_size_max); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} ++ + /* Parse the caps and config of a pcm stream */ + static int tplg_parse_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + snd_config_t *cfg, void *private) +@@ -598,6 +679,61 @@ static int tplg_parse_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + return 0; + } + ++/* Save the caps and config of a pcm stream */ ++int tplg_save_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ static const char *stream_ids[2] = { ++ "playback", ++ "capture" ++ }; ++ static unsigned int stream_types[2] = { ++ SND_SOC_TPLG_STREAM_PLAYBACK, ++ SND_SOC_TPLG_STREAM_CAPTURE ++ }; ++ struct snd_soc_tplg_stream_caps *caps; ++ unsigned int streams[2], stream; ++ const char *s; ++ int err; ++ ++ switch (elem->type) { ++ case SND_TPLG_TYPE_PCM: ++ streams[0] = elem->pcm->playback; ++ streams[1] = elem->pcm->capture; ++ caps = elem->pcm->caps; ++ break; ++ case SND_TPLG_TYPE_DAI: ++ streams[0] = elem->dai->playback; ++ streams[1] = elem->dai->capture; ++ caps = elem->dai->caps; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ for (stream = 0; stream < 2; stream++) { ++ if (streams[stream] == 0) ++ continue; ++ if (!caps) ++ continue; ++ s = caps[stream_types[stream]].name; ++ if (s[0] == '\0') ++ continue; ++ err = tplg_save_printf(dst, pfx, "pcm.%s {\n", stream_ids[stream]); ++ if (err < 0) ++ return err; ++ err = tplg_save_printf(dst, pfx, "\tcapabilities '%s'\n", s); ++ if (err < 0) ++ return err; ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ + /* Parse name and id of a front-end DAI (ie. cpu dai of a FE DAI link) */ + static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + snd_config_t *cfg, void *private) +@@ -633,6 +769,19 @@ static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + return 0; + } + ++/* Save the caps and config of a pcm stream */ ++int tplg_save_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ struct snd_soc_tplg_pcm *pcm = elem->pcm; ++ int err = 0; ++ ++ if (pcm->dai_id > 0) ++ err = tplg_save_printf(dst, pfx, "dai.0.id %u\n", pcm->dai_id); ++ return err; ++} ++ + /* parse a flag bit of the given mask */ + static int parse_flag(snd_config_t *n, unsigned int mask_in, + unsigned int *mask, unsigned int *flags) +@@ -652,6 +801,32 @@ static int parse_flag(snd_config_t *n, unsigned int mask_in, + return 0; + } + ++static int save_flags(unsigned int flags, unsigned int mask, ++ char **dst, const char *pfx) ++{ ++ static unsigned int flag_masks[3] = { ++ SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES, ++ SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS, ++ SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS, ++ }; ++ static const char *flag_ids[3] = { ++ "symmetric_rates", ++ "symmetric_channels", ++ "symmetric_sample_bits", ++ }; ++ unsigned int i; ++ int err = 0; ++ ++ for (i = 0; err >= 0 && i < ARRAY_SIZE(flag_masks); i++) { ++ if (mask & flag_masks[i]) { ++ unsigned int v = (flags & flag_masks[i]) ? 1 : 0; ++ err = tplg_save_printf(dst, pfx, "%s %u\n", ++ flag_ids[i], v); ++ } ++ } ++ return err; ++} ++ + /* Parse PCM (for front end DAI & DAI link) in text conf file */ + int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED) +@@ -748,7 +923,7 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, + + /* private data */ + if (strcmp(id, "data") == 0) { +- err = tplg_parse_data_refs(n, elem); ++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); + if (err < 0) + return err; + continue; +@@ -758,6 +933,40 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, + return 0; + } + ++/* save PCM */ ++int tplg_save_pcm(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ struct snd_soc_tplg_pcm *pcm = elem->pcm; ++ char pfx2[16]; ++ int err; ++ ++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: ""); ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ if (err >= 0 && elem->index) ++ err = tplg_save_printf(dst, pfx, "\tindex %u\n", ++ elem->index); ++ if (err >= 0 && pcm->pcm_id) ++ err = tplg_save_printf(dst, pfx, "\tid %u\n", ++ pcm->pcm_id); ++ if (err >= 0 && pcm->compress) ++ err = tplg_save_printf(dst, pfx, "\tcompress 1\n"); ++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: ""); ++ if (err >= 0) ++ err = tplg_save_fe_dai(tplg, elem, dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_streams(tplg, elem, dst, pfx2); ++ if (err >= 0) ++ err = save_flags(pcm->flags, pcm->flag_mask, dst, pfx); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA, ++ "data", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} ++ + /* Parse physical DAI */ + int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED) +@@ -766,7 +975,7 @@ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg, + struct tplg_elem *elem; + snd_config_iterator_t i, next; + snd_config_t *n; +- const char *id, *val = NULL; ++ const char *id; + int err; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_DAI); +@@ -851,11 +1060,9 @@ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg, + + /* private data */ + if (strcmp(id, "data") == 0) { +- if (snd_config_get_string(n, &val) < 0) +- return -EINVAL; +- +- tplg_ref_add(elem, SND_TPLG_TYPE_DATA, val); +- tplg_dbg("\t%s: %s\n", id, val); ++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); ++ if (err < 0) ++ return err; + continue; + } + } +@@ -863,55 +1070,55 @@ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg, + return 0; + } + ++/* save DAI */ ++int tplg_save_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ struct snd_soc_tplg_dai *dai = elem->dai; ++ char pfx2[16]; ++ int err; ++ ++ if (!dai) ++ return 0; ++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: ""); ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ if (err >= 0 && elem->index) ++ err = tplg_save_printf(dst, pfx, "\tindex %u\n", ++ elem->index); ++ if (err >= 0 && dai->dai_id) ++ err = tplg_save_printf(dst, pfx, "\tid %u\n", ++ dai->dai_id); ++ if (err >= 0 && dai->playback) ++ err = tplg_save_printf(dst, pfx, "\tplayback %u\n", ++ dai->playback); ++ if (err >= 0 && dai->capture) ++ err = tplg_save_printf(dst, pfx, "\tcapture %u\n", ++ dai->capture); ++ if (err >= 0) ++ err = tplg_save_streams(tplg, elem, dst, pfx2); ++ if (err >= 0) ++ err = save_flags(dai->flags, dai->flag_mask, dst, pfx); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA, ++ "data", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} ++ + /* parse physical link runtime supported HW configs in text conf file */ + static int parse_hw_config_refs(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + snd_config_t *cfg, + struct tplg_elem *elem) + { + struct snd_soc_tplg_link_config *link = elem->link; +- snd_config_type_t type; +- snd_config_iterator_t i, next; +- snd_config_t *n; +- const char *id, *val = NULL; +- +- if (snd_config_get_id(cfg, &id) < 0) +- return -EINVAL; +- type = snd_config_get_type(cfg); +- +- /* refer to a single HW config */ +- if (type == SND_CONFIG_TYPE_STRING) { +- if (snd_config_get_string(cfg, &val) < 0) +- return -EINVAL; +- +- link->num_hw_configs = 1; +- return tplg_ref_add(elem, SND_TPLG_TYPE_HW_CONFIG, val); +- } +- +- if (type != SND_CONFIG_TYPE_COMPOUND) { +- SNDERR("error: compound type expected for %s", id); +- return -EINVAL; +- } +- +- /* refer to a list of HW configs */ +- snd_config_for_each(i, next, cfg) { +- const char *val; +- int err; +- +- n = snd_config_iterator_entry(i); +- if (snd_config_get_string(n, &val) < 0) +- continue; +- +- if (link->num_hw_configs >= SND_SOC_TPLG_HW_CONFIG_MAX) { +- SNDERR("error: exceed max hw configs for link %s", id); +- return -EINVAL; +- } +- +- link->num_hw_configs++; +- err = tplg_ref_add(elem, SND_TPLG_TYPE_HW_CONFIG, val); +- if (err < 0) +- return err; +- } ++ int err; + ++ err = tplg_parse_refs(cfg, elem, SND_TPLG_TYPE_HW_CONFIG); ++ if (err < 0) ++ return err; ++ link->num_hw_configs = err; + return 0; + } + +@@ -1007,7 +1214,7 @@ int tplg_parse_link(snd_tplg_t *tplg, + + /* private data */ + if (strcmp(id, "data") == 0) { +- err = tplg_parse_data_refs(n, elem); ++ err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); + if (err < 0) + return err; + continue; +@@ -1017,6 +1224,44 @@ int tplg_parse_link(snd_tplg_t *tplg, + return 0; + } + ++/* save physical link */ ++int tplg_save_link(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ struct snd_soc_tplg_link_config *link = elem->link; ++ char pfx2[16]; ++ int err; ++ ++ if (!link) ++ return 0; ++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: ""); ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ if (err >= 0 && elem->index) ++ err = tplg_save_printf(dst, pfx, "\tindex %u\n", ++ elem->index); ++ if (err >= 0 && link->id) ++ err = tplg_save_printf(dst, pfx, "\tid %u\n", ++ link->id); ++ if (err >= 0 && link->stream_name[0]) ++ err = tplg_save_printf(dst, pfx, "\tstream_name '%s'\n", ++ link->stream_name); ++ if (err >= 0 && link->default_hw_config_id) ++ err = tplg_save_printf(dst, pfx, "\tdefault_hw_conf_id %u\n", ++ link->default_hw_config_id); ++ if (err >= 0) ++ err = save_flags(link->flags, link->flag_mask, dst, pfx); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_HW_CONFIG, ++ "hw_configs", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_refs(tplg, elem, SND_TPLG_TYPE_DATA, ++ "data", dst, pfx2); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} ++ + /* Parse cc */ + int tplg_parse_cc(snd_tplg_t *tplg, + snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) +@@ -1059,36 +1304,95 @@ int tplg_parse_cc(snd_tplg_t *tplg, + return 0; + } + +-static int get_audio_hw_format(const char *val) ++/* save CC */ ++int tplg_save_cc(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) + { +- if (!strlen(val)) +- return -EINVAL; +- +- if (!strcmp(val, "I2S")) +- return SND_SOC_DAI_FORMAT_I2S; ++ struct snd_soc_tplg_link_config *link = elem->link; ++ char pfx2[16]; ++ int err; + +- if (!strcmp(val, "RIGHT_J")) +- return SND_SOC_DAI_FORMAT_RIGHT_J; ++ if (!link) ++ return 0; ++ snprintf(pfx2, sizeof(pfx2), "%s\t", pfx ?: ""); ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ if (err >= 0 && elem->index) ++ err = tplg_save_printf(dst, pfx, "\tindex %u\n", ++ elem->index); ++ if (err >= 0 && link->id) ++ err = tplg_save_printf(dst, pfx, "\tid %u\n", ++ link->id); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} + +- if (!strcmp(val, "LEFT_J")) +- return SND_SOC_DAI_FORMAT_LEFT_J; ++struct audio_hw_format { ++ unsigned int type; ++ const char *name; ++}; + +- if (!strcmp(val, "DSP_A")) +- return SND_SOC_DAI_FORMAT_DSP_A; ++static struct audio_hw_format audio_hw_formats[] = { ++ { ++ .type = SND_SOC_DAI_FORMAT_I2S, ++ .name = "I2S", ++ }, ++ { ++ .type = SND_SOC_DAI_FORMAT_RIGHT_J, ++ .name = "RIGHT_J", ++ }, ++ { ++ .type = SND_SOC_DAI_FORMAT_LEFT_J, ++ .name = "LEFT_J", ++ }, ++ { ++ .type = SND_SOC_DAI_FORMAT_DSP_A, ++ .name = "DSP_A", ++ }, ++ { ++ .type = SND_SOC_DAI_FORMAT_DSP_B, ++ .name = "DSP_B", ++ }, ++ { ++ .type = SND_SOC_DAI_FORMAT_AC97, ++ .name = "AC97", ++ }, ++ { ++ .type = SND_SOC_DAI_FORMAT_AC97, ++ .name = "AC97", ++ }, ++ { ++ .type = SND_SOC_DAI_FORMAT_PDM, ++ .name = "PDM", ++ }, ++}; + +- if (!strcmp(val, "DSP_B")) +- return SND_SOC_DAI_FORMAT_DSP_B; ++static int get_audio_hw_format(const char *val) ++{ ++ unsigned int i; + +- if (!strcmp(val, "AC97")) +- return SND_SOC_DAI_FORMAT_AC97; ++ if (val[0] == '\0') ++ return -EINVAL; + +- if (!strcmp(val, "PDM")) +- return SND_SOC_DAI_FORMAT_PDM; ++ for (i = 0; i < ARRAY_SIZE(audio_hw_formats); i++) ++ if (strcasecmp(audio_hw_formats[i].name, val) == 0) ++ return audio_hw_formats[i].type; + + SNDERR("error: invalid audio HW format %s\n", val); + return -EINVAL; + } + ++static const char *get_audio_hw_format_name(unsigned int type) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(audio_hw_formats); i++) ++ if (audio_hw_formats[i].type == type) ++ return audio_hw_formats[i].name; ++ return NULL; ++} ++ + int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED) + { +@@ -1299,6 +1603,71 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + return 0; + } + ++/* save hw config */ ++int tplg_save_hw_config(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ struct snd_soc_tplg_hw_config *hc = elem->hw_cfg; ++ int err; ++ ++ err = tplg_save_printf(dst, NULL, "'%s' {\n", elem->id); ++ if (err >= 0 && hc->id) ++ err = tplg_save_printf(dst, pfx, "\tid %u\n", ++ hc->id); ++ if (err >= 0 && hc->fmt) ++ err = tplg_save_printf(dst, pfx, "\tformat '%s'\n", ++ get_audio_hw_format_name(hc->fmt)); ++ if (err >= 0 && hc->bclk_master) ++ err = tplg_save_printf(dst, pfx, "\tbclk '%s'\n", ++ hc->bclk_master == SND_SOC_TPLG_BCLK_CS ? ++ "codec_slave" : "codec_master"); ++ if (err >= 0 && hc->bclk_rate) ++ err = tplg_save_printf(dst, pfx, "\tbclk_freq %u\n", ++ hc->bclk_rate); ++ if (err >= 0 && hc->invert_bclk) ++ err = tplg_save_printf(dst, pfx, "\tbclk_invert 1\n"); ++ if (err >= 0 && hc->fsync_master) ++ err = tplg_save_printf(dst, pfx, "\tfsync_master '%s'\n", ++ hc->fsync_master == SND_SOC_TPLG_FSYNC_CS ? ++ "codec_slave" : "codec_master"); ++ if (err >= 0 && hc->fsync_rate) ++ err = tplg_save_printf(dst, pfx, "\tfsync_freq %u\n", ++ hc->fsync_rate); ++ if (err >= 0 && hc->invert_fsync) ++ err = tplg_save_printf(dst, pfx, "\tfsync_invert 1\n"); ++ if (err >= 0 && hc->mclk_rate) ++ err = tplg_save_printf(dst, pfx, "\tmclk_freq %u\n", ++ hc->mclk_rate); ++ if (err >= 0 && hc->mclk_direction) ++ err = tplg_save_printf(dst, pfx, "\tmclk '%s'\n", ++ hc->mclk_direction == SND_SOC_TPLG_MCLK_CI ? ++ "codec_mclk_in" : "codec_mclk_out"); ++ if (err >= 0 && hc->clock_gated) ++ err = tplg_save_printf(dst, pfx, "\tpm_gate_clocks 1\n"); ++ if (err >= 0 && hc->tdm_slots) ++ err = tplg_save_printf(dst, pfx, "\ttdm_slots %u\n", ++ hc->tdm_slots); ++ if (err >= 0 && hc->tdm_slot_width) ++ err = tplg_save_printf(dst, pfx, "\ttdm_slot_width %u\n", ++ hc->tdm_slot_width); ++ if (err >= 0 && hc->tx_slots) ++ err = tplg_save_printf(dst, pfx, "\ttx_slots %u\n", ++ hc->tx_slots); ++ if (err >= 0 && hc->rx_slots) ++ err = tplg_save_printf(dst, pfx, "\trx_slots %u\n", ++ hc->rx_slots); ++ if (err >= 0 && hc->tx_channels) ++ err = tplg_save_printf(dst, pfx, "\ttx_channels %u\n", ++ hc->tx_channels); ++ if (err >= 0 && hc->rx_channels) ++ err = tplg_save_printf(dst, pfx, "\trx_channels %u\n", ++ hc->rx_channels); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "}\n"); ++ return err; ++} ++ + /* copy stream object */ + static void tplg_add_stream_object(struct snd_soc_tplg_stream *strm, + struct snd_tplg_stream_template *strm_tpl) +diff --git a/src/topology/save.c b/src/topology/save.c +new file mode 100644 +index 000000000000..0498911f67d5 +--- /dev/null ++++ b/src/topology/save.c +@@ -0,0 +1,632 @@ ++/* ++ Copyright(c) 2019 Red Hat Inc. ++ All rights reserved. ++ ++ 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.1 of ++ the License, or (at your option) any later version. ++ ++ 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 Lesser General Public License for more details. ++ ++ Authors: Jaroslav Kysela ++*/ ++ ++#include "list.h" ++#include "tplg_local.h" ++ ++#define SAVE_ALLOC_SHIFT (13) /* 8192 bytes */ ++ ++int tplg_save_printf(char **dst, const char *pfx, const char *fmt, ...) ++{ ++ va_list va; ++ char buf[1024], *s; ++ size_t n, l, t, pl; ++ ++ if (pfx == NULL) ++ pfx = ""; ++ ++ va_start(va, fmt); ++ n = vsnprintf(buf, sizeof(buf), fmt, va); ++ va_end(va); ++ ++ if (n >= sizeof(buf)) ++ return -EOVERFLOW; ++ ++ pl = strlen(pfx); ++ l = *dst ? strlen(*dst) : 0; ++ t = l + pl + n + 1; ++ /* allocate chunks */ ++ if (*dst == NULL || ++ (l >> SAVE_ALLOC_SHIFT) != (t >> SAVE_ALLOC_SHIFT)) { ++ s = realloc(*dst, ((t >> SAVE_ALLOC_SHIFT) + 1) << ++ SAVE_ALLOC_SHIFT); ++ if (s == NULL) { ++ free(*dst); ++ *dst = NULL; ++ return -ENOMEM; ++ } ++ } else { ++ s = *dst; ++ } ++ ++ if (pl > 0) ++ strcpy(s + l, pfx); ++ strcpy(s + l + pl, buf); ++ *dst = s; ++ return 0; ++} ++ ++int tplg_nice_value_format(char *dst, size_t dst_size, unsigned int value) ++{ ++ if ((value % 1000) != 0) { ++ if (value > 0xfffffff0) ++ return snprintf(dst, dst_size, "%d", (int)value); ++ if (value >= 0xffff0000) ++ return snprintf(dst, dst_size, "0x%x", value); ++ } ++ return snprintf(dst, dst_size, "%u", value); ++} ++ ++static int tplg_pprint_integer(snd_config_t *n, char **ret) ++{ ++ long lval; ++ int err, type; ++ char buf[16]; ++ ++ type = snd_config_get_type(n); ++ if (type == SND_CONFIG_TYPE_INTEGER) { ++ err = snd_config_get_integer(n, &lval); ++ if (err < 0) ++ return err; ++ if (lval < INT_MIN || lval > UINT_MAX) ++ return snd_config_get_ascii(n, ret); ++ } else if (type == SND_CONFIG_TYPE_INTEGER64) { ++ long long llval; ++ err = snd_config_get_integer64(n, &llval); ++ if (err < 0) ++ return err; ++ if (llval < INT_MIN || llval > UINT_MAX) ++ return snd_config_get_ascii(n, ret); ++ lval = llval; ++ } ++ err = tplg_nice_value_format(buf, sizeof(buf), (unsigned int)lval); ++ if (err < 0) ++ return err; ++ *ret = strdup(buf); ++ if (*ret == NULL) ++ return -ENOMEM; ++ return 0; ++} ++ ++static int tplg_check_array_item(const char *id, int index) ++{ ++ const char *p; ++ long int val; ++ ++ for (p = id; *p; p++) { ++ if (*p < '0' || *p > '9') ++ return 0; ++ } ++ ++ errno = 0; ++ val = strtol(id, NULL, 10); ++ return errno == 0 && val == index; ++} ++ ++static int _compar(const void *a, const void *b) ++{ ++ const snd_config_t *c1 = *(snd_config_t **)a; ++ const snd_config_t *c2 = *(snd_config_t **)b; ++ const char *id1, *id2; ++ if (snd_config_get_id(c1, &id1)) return 0; ++ if (snd_config_get_id(c2, &id2)) return 0; ++ return strcmp(id1, id2); ++} ++ ++static snd_config_t *sort_config(const char *id, snd_config_t *src) ++{ ++ snd_config_t *dst, **a; ++ snd_config_iterator_t i, next; ++ int index, array, count; ++ ++ if (snd_config_get_type(src) != SND_CONFIG_TYPE_COMPOUND) { ++ ++ if (snd_config_copy(&dst, src) >= 0) ++ return dst; ++ return NULL; ++ } ++ count = 0; ++ snd_config_for_each(i, next, src) ++ count++; ++ a = malloc(sizeof(dst) * count); ++ if (a == NULL) ++ return NULL; ++ index = array = 0; ++ snd_config_for_each(i, next, src) { ++ snd_config_t *s = snd_config_iterator_entry(i); ++ const char *id2; ++ a[index++] = s; ++ if (array < 0) ++ continue; ++ if (snd_config_get_id(s, &id2)) { ++ free(a); ++ return NULL; ++ } ++ if (array >= 0 && tplg_check_array_item(id2, array)) ++ array++; ++ else ++ array = -1; ++ } ++ if (array < 0) ++ qsort(a, count, sizeof(a[0]), _compar); ++ if (snd_config_make_compound(&dst, id, count == 1)) { ++ free(a); ++ return NULL; ++ } ++ for (index = 0; index < count; index++) { ++ snd_config_t *s = a[index]; ++ const char *id2; ++ if (snd_config_get_id(s, &id2)) { ++ snd_config_delete(dst); ++ free(a); ++ return NULL; ++ } ++ s = sort_config(id2, s); ++ if (s == NULL || snd_config_add(dst, s)) { ++ if (s) ++ snd_config_delete(s); ++ snd_config_delete(dst); ++ free(a); ++ return NULL; ++ } ++ } ++ free(a); ++ return dst; ++} ++ ++static int tplg_check_quoted(const unsigned char *p) ++{ ++ for ( ; *p != '\0'; p++) { ++ switch (*p) { ++ case ' ': ++ case '=': ++ case ';': ++ case ',': ++ case '.': ++ case '{': ++ case '}': ++ case '\'': ++ case '"': ++ return 1; ++ default: ++ if (*p <= 31 || *p >= 127) ++ return 1; ++ ++ } ++ } ++ return 0; ++} ++ ++static int tplg_save_quoted(char **dst, const char *str) ++{ ++ static const char nibble[16] = "0123456789abcdef"; ++ unsigned char *p, *d, *t; ++ int c; ++ ++ d = t = alloca(strlen(str) * 5 + 1 + 1); ++ for (p = (unsigned char *)str; *p != '\0'; p++) { ++ c = *p; ++ switch (c) { ++ case '\n': ++ *t++ = '\\'; ++ *t++ = 'n'; ++ break; ++ case '\t': ++ *t++ = '\\'; ++ *t++ = 't'; ++ break; ++ case '\v': ++ *t++ = '\\'; ++ *t++ = 'v'; ++ break; ++ case '\b': ++ *t++ = '\\'; ++ *t++ = 'b'; ++ break; ++ case '\r': ++ *t++ = '\\'; ++ *t++ = 'r'; ++ break; ++ case '\f': ++ *t++ = '\\'; ++ *t++ = 'f'; ++ break; ++ case '\'': ++ *t++ = '\\'; ++ *t++ = c; ++ break; ++ default: ++ if (c >= 32 && c <= 126) { ++ *t++ = c; ++ } else { ++ *t++ = '\\'; ++ *t++ = 'x'; ++ *t++ = nibble[(c >> 4) & 0x0f]; ++ *t++ = nibble[(c >> 0) & 0x0f]; ++ } ++ break; ++ } ++ } ++ *t = '\0'; ++ return tplg_save_printf(dst, NULL, "'%s'", d); ++} ++ ++static int tplg_save_string(char **dst, const char *str, int id) ++{ ++ const unsigned char *p = (const unsigned char *)str; ++ ++ if (!p || !*p) ++ return tplg_save_printf(dst, NULL, "''"); ++ ++ if (!id && ((*p >= '0' && *p <= '9') || *p == '-')) ++ return tplg_save_quoted(dst, str); ++ ++ if (tplg_check_quoted(p)) ++ return tplg_save_quoted(dst, str); ++ ++ return tplg_save_printf(dst, NULL, "%s", str); ++} ++ ++static int save_config(char **dst, int level, const char *delim, snd_config_t *src) ++{ ++ snd_config_iterator_t i, next; ++ snd_config_t *s; ++ const char *id; ++ char *pfx; ++ unsigned int count; ++ int type, err, quoted, array; ++ ++ if (delim == NULL) ++ delim = ""; ++ ++ type = snd_config_get_type(src); ++ if (type != SND_CONFIG_TYPE_COMPOUND) { ++ char *val; ++ if (type == SND_CONFIG_TYPE_INTEGER || ++ type == SND_CONFIG_TYPE_INTEGER64) { ++ err = tplg_pprint_integer(src, &val); ++ } else { ++ err = snd_config_get_ascii(src, &val); ++ } ++ if (err < 0) ++ return err; ++ if (type == SND_CONFIG_TYPE_STRING) { ++ /* hexa array pretty print */ ++ id = strchr(val, '\n'); ++ if (id) { ++ err = tplg_save_printf(dst, NULL, "\n"); ++ if (err < 0) ++ goto retval; ++ for (id++; *id == '\t'; id++) { ++ err = tplg_save_printf(dst, NULL, "\t"); ++ if (err < 0) ++ goto retval; ++ } ++ delim = ""; ++ } ++ err = tplg_save_printf(dst, NULL, "%s'%s'\n", delim, val); ++ } else { ++ err = tplg_save_printf(dst, NULL, "%s%s\n", delim, val); ++ } ++retval: ++ free(val); ++ return err; ++ } ++ ++ count = 0; ++ quoted = 0; ++ array = 0; ++ s = NULL; ++ snd_config_for_each(i, next, src) { ++ s = snd_config_iterator_entry(i); ++ err = snd_config_get_id(s, &id); ++ if (err < 0) ++ return err; ++ if (!quoted && tplg_check_quoted((unsigned char *)id)) ++ quoted = 1; ++ if (array >= 0 && tplg_check_array_item(id, array)) ++ array++; ++ else ++ array = -1; ++ count++; ++ } ++ if (count == 0) ++ return 0; ++ ++ if (count == 1) { ++ err = snd_config_get_id(s, &id); ++ if (err >= 0 && level > 0) ++ err = tplg_save_printf(dst, NULL, "."); ++ if (err >= 0) ++ err = tplg_save_string(dst, id, 1); ++ if (err >= 0) ++ err = save_config(dst, level, " ", s); ++ return err; ++ } ++ ++ pfx = alloca(level + 1); ++ memset(pfx, '\t', level); ++ pfx[level] = '\0'; ++ ++ if (level > 0) { ++ err = tplg_save_printf(dst, NULL, "%s%s\n", delim, ++ array >= 0 ? "[" : "{"); ++ if (err < 0) ++ return err; ++ } ++ ++ snd_config_for_each(i, next, src) { ++ s = snd_config_iterator_entry(i); ++ const char *id; ++ err = snd_config_get_id(s, &id); ++ if (err < 0) ++ return err; ++ err = tplg_save_printf(dst, pfx, ""); ++ if (err < 0) ++ return err; ++ if (array < 0) { ++ delim = " "; ++ if (quoted) { ++ err = tplg_save_quoted(dst, id); ++ } else { ++ err = tplg_save_string(dst, id, 1); ++ if (err < 0) ++ return err; ++ } ++ } else { ++ delim = ""; ++ } ++ err = save_config(dst, level + 1, delim, s); ++ if (err < 0) ++ return err; ++ } ++ ++ if (level > 0) { ++ pfx[level - 1] = '\0'; ++ err = tplg_save_printf(dst, pfx, "%s\n", ++ array >= 0 ? "]" : "}"); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int tplg_save(snd_tplg_t *tplg, char **dst, int gindex, const char *prefix) ++{ ++ struct tplg_table *tptr; ++ struct tplg_elem *elem; ++ struct list_head *list, *pos; ++ char pfx2[16]; ++ unsigned int index; ++ int err, count; ++ ++ snprintf(pfx2, sizeof(pfx2), "%s\t", prefix ?: ""); ++ ++ /* write all blocks */ ++ for (index = 0; index < tplg_table_items; index++) { ++ tptr = &tplg_table[index]; ++ list = (struct list_head *)((void *)tplg + tptr->loff); ++ ++ /* count elements */ ++ count = 0; ++ list_for_each(pos, list) { ++ elem = list_entry(pos, struct tplg_elem, list); ++ if (gindex >= 0 && elem->index != gindex) ++ continue; ++ if (tptr->save == NULL && tptr->gsave == NULL) { ++ SNDERR("unable to create %s block (no callback)", ++ tptr->id); ++ err = -ENXIO; ++ goto _err; ++ } ++ if (tptr->save) ++ count++; ++ } ++ ++ if (count == 0) ++ continue; ++ ++ if (count > 1) { ++ err = tplg_save_printf(dst, prefix, "%s {\n", ++ elem->table ? ++ elem->table->id : "_NOID_"); ++ } else { ++ err = tplg_save_printf(dst, prefix, "%s.", ++ elem->table ? ++ elem->table->id : "_NOID_"); ++ } ++ ++ if (err < 0) ++ goto _err; ++ ++ list_for_each(pos, list) { ++ elem = list_entry(pos, struct tplg_elem, list); ++ if (gindex >= 0 && elem->index != gindex) ++ continue; ++ if (count > 1) { ++ err = tplg_save_printf(dst, pfx2, ""); ++ if (err < 0) ++ goto _err; ++ } ++ err = tptr->save(tplg, elem, dst, count > 1 ? pfx2 : prefix); ++ if (err < 0) { ++ SNDERR("failed to save %s elements: %s", ++ tptr->id, snd_strerror(-err)); ++ goto _err; ++ } ++ } ++ if (count > 1) { ++ err = tplg_save_printf(dst, prefix, "}\n"); ++ if (err < 0) ++ goto _err; ++ } ++ } ++ ++ /* save globals */ ++ for (index = 0; index < tplg_table_items; index++) { ++ tptr = &tplg_table[index]; ++ if (tptr->gsave) { ++ err = tptr->gsave(tplg, gindex, dst, prefix); ++ if (err < 0) ++ goto _err; ++ } ++ } ++ ++ return 0; ++ ++_err: ++ free(*dst); ++ *dst = NULL; ++ return err; ++} ++ ++static int tplg_index_compar(const void *a, const void *b) ++{ ++ const int *a1 = a, *b1 = b; ++ return *a1 - *b1; ++} ++ ++static int tplg_index_groups(snd_tplg_t *tplg, int **indexes) ++{ ++ struct tplg_table *tptr; ++ struct tplg_elem *elem; ++ struct list_head *list, *pos; ++ unsigned int index, j, count, size; ++ int *a, *b; ++ ++ count = 0; ++ size = 16; ++ a = malloc(size * sizeof(a[0])); ++ ++ for (index = 0; index < tplg_table_items; index++) { ++ tptr = &tplg_table[index]; ++ list = (struct list_head *)((void *)tplg + tptr->loff); ++ list_for_each(pos, list) { ++ elem = list_entry(pos, struct tplg_elem, list); ++ for (j = 0; j < count; j++) { ++ if (a[j] == elem->index) ++ break; ++ } ++ if (j < count) ++ continue; ++ if (count + 1 >= size) { ++ size += 8; ++ b = realloc(a, size * sizeof(a[0])); ++ if (b == NULL) { ++ free(a); ++ return -ENOMEM; ++ } ++ a = b; ++ } ++ a[count++] = elem->index; ++ } ++ } ++ a[count] = -1; ++ ++ qsort(a, count, sizeof(a[0]), tplg_index_compar); ++ ++ *indexes = a; ++ return 0; ++} ++ ++int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags) ++{ ++ snd_input_t *in; ++ snd_config_t *top, *top2; ++ char *dst2; ++ int *indexes, *a; ++ int err; ++ ++ assert(tplg); ++ assert(dst); ++ *dst = NULL; ++ ++ if (flags & SND_TPLG_SAVE_GROUPS) { ++ err = tplg_index_groups(tplg, &indexes); ++ if (err < 0) ++ return err; ++ for (a = indexes; err >= 0 && *a >= 0; a++) { ++ err = tplg_save_printf(dst, NULL, ++ "IndexGroup.%d {\n", ++ *a); ++ if (err >= 0) ++ err = tplg_save(tplg, dst, *a, "\t"); ++ if (err >= 0) ++ err = tplg_save_printf(dst, NULL, "}\n"); ++ } ++ free(indexes); ++ } else { ++ err = tplg_save(tplg, dst, -1, NULL); ++ } ++ ++ if (err < 0) ++ goto _err; ++ ++ if (flags & SND_TPLG_SAVE_NOCHECK) ++ return 0; ++ ++ /* always load configuration - check */ ++ err = snd_input_buffer_open(&in, *dst, strlen(*dst)); ++ if (err < 0) { ++ SNDERR("could not create input buffer"); ++ goto _err; ++ } ++ ++ err = snd_config_top(&top); ++ if (err < 0) { ++ snd_input_close(in); ++ goto _err; ++ } ++ ++ err = snd_config_load(top, in); ++ snd_input_close(in); ++ if (err < 0) { ++ SNDERR("could not load configuration"); ++ snd_config_delete(top); ++ goto _err; ++ } ++ ++ if (flags & SND_TPLG_SAVE_SORT) { ++ top2 = sort_config(NULL, top); ++ if (top2 == NULL) { ++ SNDERR("could not sort configuration"); ++ snd_config_delete(top); ++ err = -EINVAL; ++ goto _err; ++ } ++ snd_config_delete(top); ++ top = top2; ++ } ++ ++ dst2 = NULL; ++ err = save_config(&dst2, 0, NULL, top); ++ snd_config_delete(top); ++ if (err < 0) { ++ SNDERR("could not save configuration"); ++ goto _err; ++ } ++ ++ free(*dst); ++ *dst = dst2; ++ return 0; ++ ++_err: ++ free(*dst); ++ *dst = NULL; ++ return err; ++} +diff --git a/src/topology/text.c b/src/topology/text.c +index f301a4ded727..e9386e7df492 100644 +--- a/src/topology/text.c ++++ b/src/topology/text.c +@@ -89,3 +89,22 @@ int tplg_parse_text(snd_tplg_t *tplg, snd_config_t *cfg, + + return err; + } ++ ++/* save text data */ ++int tplg_save_text(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) ++{ ++ struct tplg_texts *texts = elem->texts; ++ unsigned int i; ++ int err; ++ ++ if (!texts || texts->num_items == 0) ++ return 0; ++ err = tplg_save_printf(dst, pfx, "'%s'.values [\n", elem->id); ++ for (i = 0; err >= 0 && i < texts->num_items; i++) ++ err = tplg_save_printf(dst, pfx, "\t'%s'\n", texts->items[i][0]); ++ if (err >= 0) ++ err = tplg_save_printf(dst, pfx, "]\n"); ++ return err; ++} +diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h +index bea88ba35608..42a3aa96ba0e 100644 +--- a/src/topology/tplg_local.h ++++ b/src/topology/tplg_local.h +@@ -37,6 +37,7 @@ + + struct tplg_ref; + struct tplg_elem; ++struct tplg_table; + + typedef enum _snd_pcm_rates { + SND_PCM_RATE_UNKNOWN = -1, +@@ -147,6 +148,8 @@ struct tplg_vendor_tuples { + /* topology element */ + struct tplg_elem { + ++ struct tplg_table *table; ++ + char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + + int index; +@@ -209,6 +212,10 @@ struct tplg_table { + unsigned enew: 1; + void (*free)(void *); + int (*parse)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); ++ int (*save)(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *prefix); ++ int (*gsave)(snd_tplg_t *tplg, int index, ++ char **dst, const char *prefix); + }; + + extern struct tplg_table tplg_table[]; +@@ -250,7 +257,8 @@ int tplg_build_pcm_dai(snd_tplg_t *tplg, unsigned int type); + int tplg_copy_data(snd_tplg_t *tplg, struct tplg_elem *elem, + struct tplg_ref *ref); + +-int tplg_parse_data_refs(snd_config_t *cfg, struct tplg_elem *elem); ++int tplg_parse_refs(snd_config_t *cfg, struct tplg_elem *elem, ++ unsigned int type); + + int tplg_ref_add(struct tplg_elem *elem, int type, const char* id); + int tplg_ref_add_elem(struct tplg_elem *elem, struct tplg_elem *elem_ref); +@@ -269,9 +277,11 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, + int tplg_get_integer(snd_config_t *n, int *val, int base); + int tplg_get_unsigned(snd_config_t *n, unsigned *val, int base); + ++const char *tplg_channel_name(int type); + int tplg_parse_channel(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + snd_config_t *cfg, void *private); + ++const char *tplg_ops_name(int type); + int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + snd_config_t *cfg, void *private); + int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, +@@ -299,3 +309,49 @@ int tplg_build_links(snd_tplg_t *tplg, unsigned int type); + int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); + int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); + int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); ++ ++int tplg_nice_value_format(char *dst, size_t dst_size, unsigned int value); ++ ++int tplg_save_printf(char **dst, const char *prefix, const char *fmt, ...); ++int tplg_save_refs(snd_tplg_t *tplg, struct tplg_elem *elem, unsigned int type, ++ const char *id, char **dst, const char *pfx); ++int tplg_save_channels(snd_tplg_t *tplg, struct snd_soc_tplg_channel *channel, ++ unsigned int channel_count, char **dst, const char *pfx); ++int tplg_save_ops(snd_tplg_t *tplg, struct snd_soc_tplg_ctl_hdr *hdr, ++ char **dst, const char *pfx); ++int tplg_save_ext_ops(snd_tplg_t *tplg, struct snd_soc_tplg_bytes_control *be, ++ char **dst, const char *pfx); ++int tplg_save_manifest_data(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_control_mixer(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_control_enum(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_control_bytes(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_tlv(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_data(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_text(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_tokens(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_tuples(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_dapm_graph(snd_tplg_t *tplg, int index, ++ char **dst, const char *pfx); ++int tplg_save_dapm_widget(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_link(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_cc(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_pcm(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_hw_config(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_stream_caps(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); ++int tplg_save_dai(snd_tplg_t *tplg, struct tplg_elem *elem, ++ char **dst, const char *pfx); +-- +2.16.4 + diff --git a/0044-topology-add-snd_tplg_create-with-flags.patch b/0044-topology-add-snd_tplg_create-with-flags.patch new file mode 100644 index 0000000..f200579 --- /dev/null +++ b/0044-topology-add-snd_tplg_create-with-flags.patch @@ -0,0 +1,109 @@ +From b336aea507b80493cdae439f09f710eec4bcd4ae Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Fri, 20 Dec 2019 14:59:00 +0100 +Subject: [PATCH 44/63] topology: add snd_tplg_create() with flags + +Add SND_TPLG_CREATE_VERBOSE and SND_TPLG_CREATE_DAPM_NOSORT +flags for the special operations. + +Signed-off-by: Jaroslav Kysela +--- + include/topology.h | 10 ++++++++++ + src/topology/dapm.c | 5 ++++- + src/topology/parser.c | 10 +++++++++- + src/topology/tplg_local.h | 1 + + 4 files changed, 24 insertions(+), 2 deletions(-) + +diff --git a/include/topology.h b/include/topology.h +index 69aa5ed733e3..63c13a9885e9 100644 +--- a/include/topology.h ++++ b/include/topology.h +@@ -771,12 +771,22 @@ enum snd_tplg_type { + /** Fit for all user cases */ + #define SND_TPLG_INDEX_ALL 0 + ++/** Flags for the snd_tplg_create */ ++#define SND_TPLG_CREATE_VERBOSE (1<<0) /*!< Verbose output */ ++#define SND_TPLG_CREATE_DAPM_NOSORT (1<<1) /*!< Do not sort DAPM objects by index */ ++ + /** + * \brief Create a new topology parser instance. + * \return New topology parser instance + */ + snd_tplg_t *snd_tplg_new(void); + ++/** ++ * \brief Create a new topology parser instance. ++ * \return New topology parser instance ++ */ ++snd_tplg_t *snd_tplg_create(int flags); ++ + /** + * \brief Free a topology parser instance. + * \param tplg Topology parser instance +diff --git a/src/topology/dapm.c b/src/topology/dapm.c +index 2bdacedca125..d6c15fc1dfaa 100644 +--- a/src/topology/dapm.c ++++ b/src/topology/dapm.c +@@ -268,7 +268,10 @@ struct tplg_elem *tplg_elem_new_route(snd_tplg_t *tplg, int index) + return NULL; + + elem->index = index; +- tplg_elem_insert(elem, &tplg->route_list); ++ if (tplg->dapm_sort) ++ tplg_elem_insert(elem, &tplg->route_list); ++ else ++ list_add_tail(&elem->list, &tplg->route_list); + strcpy(elem->id, "line"); + elem->type = SND_TPLG_TYPE_DAPM_GRAPH; + elem->size = sizeof(*line); +diff --git a/src/topology/parser.c b/src/topology/parser.c +index de5edd1b6591..8f810f751533 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -432,7 +432,7 @@ static bool is_little_endian(void) + return false; + } + +-snd_tplg_t *snd_tplg_new(void) ++snd_tplg_t *snd_tplg_create(int flags) + { + snd_tplg_t *tplg; + +@@ -445,6 +445,9 @@ snd_tplg_t *snd_tplg_new(void) + if (!tplg) + return NULL; + ++ tplg->verbose = !!(flags & SND_TPLG_CREATE_VERBOSE); ++ tplg->dapm_sort = (flags & SND_TPLG_CREATE_DAPM_NOSORT) == 0; ++ + tplg->manifest.size = sizeof(struct snd_soc_tplg_manifest); + + INIT_LIST_HEAD(&tplg->tlv_list); +@@ -469,6 +472,11 @@ snd_tplg_t *snd_tplg_new(void) + return tplg; + } + ++snd_tplg_t *snd_tplg_new(void) ++{ ++ return snd_tplg_create(0); ++} ++ + void snd_tplg_free(snd_tplg_t *tplg) + { + free(tplg->bin); +diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h +index 42a3aa96ba0e..74b3a55cada4 100644 +--- a/src/topology/tplg_local.h ++++ b/src/topology/tplg_local.h +@@ -66,6 +66,7 @@ struct snd_tplg { + size_t bin_size; + + int verbose; ++ unsigned int dapm_sort: 1; + unsigned int version; + + /* runtime state */ +-- +2.16.4 + diff --git a/0045-topology-add-snd_tplg_version-function.patch b/0045-topology-add-snd_tplg_version-function.patch new file mode 100644 index 0000000..d8621b4 --- /dev/null +++ b/0045-topology-add-snd_tplg_version-function.patch @@ -0,0 +1,44 @@ +From 0793ef064a97afd0b1335af0d187ede227b90582 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Fri, 20 Dec 2019 21:28:30 +0100 +Subject: [PATCH 45/63] topology: add snd_tplg_version() function + +Signed-off-by: Jaroslav Kysela +--- + include/topology.h | 6 ++++++ + src/topology/parser.c | 5 +++++ + 2 files changed, 11 insertions(+) + +diff --git a/include/topology.h b/include/topology.h +index 63c13a9885e9..37bced1a4434 100644 +--- a/include/topology.h ++++ b/include/topology.h +@@ -775,6 +775,12 @@ enum snd_tplg_type { + #define SND_TPLG_CREATE_VERBOSE (1<<0) /*!< Verbose output */ + #define SND_TPLG_CREATE_DAPM_NOSORT (1<<1) /*!< Do not sort DAPM objects by index */ + ++/** ++ * \brief Return the version of the topology library. ++ * \return A static string with the version number. ++ */ ++const char *snd_tplg_version(void); ++ + /** + * \brief Create a new topology parser instance. + * \return New topology parser instance +diff --git a/src/topology/parser.c b/src/topology/parser.c +index 8f810f751533..1eaa24bd42f5 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -503,3 +503,8 @@ void snd_tplg_free(snd_tplg_t *tplg) + + free(tplg); + } ++ ++const char *snd_tplg_version(void) ++{ ++ return SND_LIB_VERSION_STR; ++} +-- +2.16.4 + diff --git a/0046-topology-cleanup-the-SNDERR-calls.patch b/0046-topology-cleanup-the-SNDERR-calls.patch new file mode 100644 index 0000000..495b09e --- /dev/null +++ b/0046-topology-cleanup-the-SNDERR-calls.patch @@ -0,0 +1,876 @@ +From f42b2c088a23e1c6156c0d5710efd7689b8c15be Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Fri, 20 Dec 2019 23:48:40 +0100 +Subject: [PATCH 46/63] topology: cleanup the SNDERR() calls + +- remove the wrong new lines +- remove error/warning prefixes (error is error) + +Signed-off-by: Jaroslav Kysela +--- + src/topology/builder.c | 16 +++++++-------- + src/topology/channel.c | 2 +- + src/topology/ctl.c | 36 +++++++++++++++------------------- + src/topology/dapm.c | 40 +++++++++++++++++-------------------- + src/topology/data.c | 53 +++++++++++++++++++++++++------------------------- + src/topology/parser.c | 33 +++++++++++++++---------------- + src/topology/pcm.c | 29 +++++++++++++-------------- + src/topology/text.c | 2 +- + 8 files changed, 99 insertions(+), 112 deletions(-) + +diff --git a/src/topology/builder.c b/src/topology/builder.c +index cadb55830809..74c44405e653 100644 +--- a/src/topology/builder.c ++++ b/src/topology/builder.c +@@ -65,8 +65,8 @@ static ssize_t write_block_header(snd_tplg_t *tplg, unsigned int type, + + /* make sure file offset is aligned with the calculated HDR offset */ + if (tplg->bin_pos != tplg->next_hdr_pos) { +- SNDERR("error: New header is at offset 0x%zx but file" +- " offset 0x%zx is %s by %ld bytes\n", ++ SNDERR("New header is at offset 0x%zx but file" ++ " offset 0x%zx is %s by %ld bytes", + tplg->next_hdr_pos, tplg->bin_pos, + tplg->bin_pos > tplg->next_hdr_pos ? "ahead" : "behind", + labs(tplg->bin_pos - tplg->next_hdr_pos)); +@@ -108,7 +108,7 @@ static int write_elem_block(snd_tplg_t *tplg, + ret = write_block_header(tplg, tplg_type, elem->vendor_type, + tplg->version, elem->index, block_size, count); + if (ret < 0) { +- SNDERR("error: failed to write %s block %d\n", ++ SNDERR("failed to write %s block %d", + obj_name, ret); + return ret; + } +@@ -148,7 +148,7 @@ static int write_elem_block(snd_tplg_t *tplg, + + /* make sure we have written the correct size */ + if (total_size != size) { +- SNDERR("error: size mismatch. Expected %zu wrote %zu\n", ++ SNDERR("size mismatch. Expected %zu wrote %zu", + size, total_size); + return -EIO; + } +@@ -221,7 +221,7 @@ static ssize_t write_manifest_data(snd_tplg_t *tplg) + tplg->version, 0, + sizeof(tplg->manifest) + tplg->manifest.priv.size, 1); + if (ret < 0) { +- SNDERR("error: failed to write manifest block\n"); ++ SNDERR("failed to write manifest block"); + return ret; + } + +@@ -266,7 +266,7 @@ int tplg_write_data(snd_tplg_t *tplg) + /* write manifest */ + ret = write_manifest_data(tplg); + if (ret < 0) { +- SNDERR("failed to write manifest %d\n", ret); ++ SNDERR("failed to write manifest %d", ret); + return ret; + } + +@@ -286,7 +286,7 @@ int tplg_write_data(snd_tplg_t *tplg) + ret = write_elem_block(tplg, list, size, + tptr->tsoc, tptr->name); + if (ret < 0) { +- SNDERR("failed to write %s elements: %s\n", ++ SNDERR("failed to write %s elements: %s", + tptr->name, snd_strerror(-ret)); + return ret; + } +@@ -295,7 +295,7 @@ int tplg_write_data(snd_tplg_t *tplg) + verbose(tplg, "total size is 0x%zx/%zd\n", tplg->bin_pos, tplg->bin_pos); + + if (total_size != tplg->bin_pos) { +- SNDERR("total size mismatch (%zd != %zd)\n", ++ SNDERR("total size mismatch (%zd != %zd)", + total_size, tplg->bin_pos); + return -EINVAL; + } +diff --git a/src/topology/channel.c b/src/topology/channel.c +index b54a10c89379..110775a8053d 100644 +--- a/src/topology/channel.c ++++ b/src/topology/channel.c +@@ -104,7 +104,7 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg, + + channel_id = lookup_channel(id); + if (channel_id < 0) { +- SNDERR("error: invalid channel %s\n", id); ++ SNDERR("invalid channel %s", id); + return -EINVAL; + } + +diff --git a/src/topology/ctl.c b/src/topology/ctl.c +index 979cc1b035f1..03874b27c76f 100644 +--- a/src/topology/ctl.c ++++ b/src/topology/ctl.c +@@ -94,7 +94,7 @@ int parse_access(snd_config_t *cfg, + if (strcmp(id, "access") == 0) { + err = parse_access_values(n, hdr); + if (err < 0) { +- SNDERR("error: failed to parse access"); ++ SNDERR("failed to parse access"); + return err; + } + continue; +@@ -187,8 +187,8 @@ static int tplg_build_mixer_control(snd_tplg_t *tplg, + } + + if (!ref->elem) { +- SNDERR("error: cannot find '%s' referenced by" +- " control '%s'\n", ref->id, elem->id); ++ SNDERR("cannot find '%s' referenced by" ++ " control '%s'", ref->id, elem->id); + return -EINVAL; + } else if (err < 0) + return err; +@@ -236,8 +236,8 @@ static int tplg_build_enum_control(snd_tplg_t *tplg, + return err; + } + if (!ref->elem) { +- SNDERR("error: cannot find '%s' referenced by" +- " control '%s'\n", ref->id, elem->id); ++ SNDERR("cannot find '%s' referenced by" ++ " control '%s'", ref->id, elem->id); + return -EINVAL; + } + } +@@ -341,10 +341,8 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem) + n = snd_config_iterator_entry(i); + + /* get ID */ +- if (snd_config_get_id(n, &id) < 0) { +- SNDERR("error: cant get ID\n"); ++ if (snd_config_get_id(n, &id) < 0) + return -EINVAL; +- } + + /* get value */ + if (tplg_get_integer(n, &val, 0)) +@@ -360,7 +358,7 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem) + else if (strcmp(id, "mute") == 0) + scale->mute = val; + else +- SNDERR("error: unknown key %s\n", id); ++ SNDERR("unknown id '%s'", id); + } + + return 0; +@@ -389,7 +387,7 @@ int tplg_parse_tlv(snd_tplg_t *tplg, snd_config_t *cfg, + if (strcmp(id, "scale") == 0) { + err = tplg_parse_tlv_dbscale(n, elem); + if (err < 0) { +- SNDERR("error: failed to DBScale"); ++ SNDERR("failed to DBScale"); + return err; + } + continue; +@@ -651,8 +649,7 @@ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg, + + if (strcmp(id, "channel") == 0) { + if (ec->num_channels >= SND_SOC_TPLG_MAX_CHAN) { +- SNDERR("error: too many channels %s\n", +- elem->id); ++ SNDERR("too many channels %s", elem->id); + return -EINVAL; + } + +@@ -779,8 +776,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg, + + if (strcmp(id, "channel") == 0) { + if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) { +- SNDERR("error: too many channels %s\n", +- elem->id); ++ SNDERR("too many channels %s", elem->id); + return -EINVAL; + } + +@@ -933,7 +929,7 @@ static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr, + struct snd_soc_tplg_tlv_dbscale *scale; + + if (!tlvt) { +- SNDERR("error: missing TLV data\n"); ++ SNDERR("missing TLV data"); + return -EINVAL; + } + +@@ -952,7 +948,7 @@ static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr, + + /* TODO: add support for other TLV types */ + default: +- SNDERR("error: unsupported TLV type %d\n", tlv->type); ++ SNDERR("unsupported TLV type %d", tlv->type); + break; + } + } +@@ -971,7 +967,7 @@ int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer, + tplg_dbg(" Control Mixer: %s\n", mixer->hdr.name); + + if (mixer->hdr.type != SND_SOC_TPLG_TYPE_MIXER) { +- SNDERR("error: invalid mixer type %d\n", mixer->hdr.type); ++ SNDERR("invalid mixer type %d", mixer->hdr.type); + return -EINVAL; + } + +@@ -1039,7 +1035,7 @@ int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl, + tplg_dbg(" Control Enum: %s\n", enum_ctl->hdr.name); + + if (enum_ctl->hdr.type != SND_SOC_TPLG_TYPE_ENUM) { +- SNDERR("error: invalid enum type %d\n", enum_ctl->hdr.type); ++ SNDERR("invalid enum type %d", enum_ctl->hdr.type); + return -EINVAL; + } + +@@ -1113,7 +1109,7 @@ int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl, + tplg_dbg(" Control Bytes: %s\n", bytes_ctl->hdr.name); + + if (bytes_ctl->hdr.type != SND_SOC_TPLG_TYPE_BYTES) { +- SNDERR("error: invalid bytes type %d\n", bytes_ctl->hdr.type); ++ SNDERR("invalid bytes type %d", bytes_ctl->hdr.type); + return -EINVAL; + } + +@@ -1157,7 +1153,7 @@ int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl, + if (be->hdr.access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + if ((be->hdr.access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) + != SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) { +- SNDERR("error: Invalid TLV bytes control access 0x%x\n", ++ SNDERR("Invalid TLV bytes control access 0x%x", + be->hdr.access); + tplg_elem_free(elem); + return -EINVAL; +diff --git a/src/topology/dapm.c b/src/topology/dapm.c +index d6c15fc1dfaa..f61fe07157ac 100644 +--- a/src/topology/dapm.c ++++ b/src/topology/dapm.c +@@ -154,8 +154,7 @@ static int tplg_build_widget(snd_tplg_t *tplg, struct tplg_elem *elem) + } + + if (!ref->elem) { +- SNDERR("error: cannot find '%s'" +- " referenced by widget '%s'\n", ++ SNDERR("cannot find '%s' referenced by widget '%s'", + ref->id, elem->id); + return -EINVAL; + } +@@ -179,8 +178,7 @@ int tplg_build_widgets(snd_tplg_t *tplg) + + elem = list_entry(pos, struct tplg_elem, list); + if (!elem->widget || elem->type != SND_TPLG_TYPE_DAPM_WIDGET) { +- SNDERR("error: invalid widget '%s'\n", +- elem->id); ++ SNDERR("invalid widget '%s'", elem->id); + return -EINVAL; + } + +@@ -207,8 +205,7 @@ int tplg_build_routes(snd_tplg_t *tplg) + elem = list_entry(pos, struct tplg_elem, list); + + if (!elem->route || elem->type != SND_TPLG_TYPE_DAPM_GRAPH) { +- SNDERR("error: invalid route '%s'\n", +- elem->id); ++ SNDERR("invalid route '%s'", elem->id); + return -EINVAL; + } + +@@ -218,14 +215,13 @@ int tplg_build_routes(snd_tplg_t *tplg) + + /* validate sink */ + if (strlen(route->sink) <= 0) { +- SNDERR("error: no sink\n"); ++ SNDERR("no sink"); + return -EINVAL; + + } + if (!tplg_elem_lookup(&tplg->widget_list, route->sink, + SND_TPLG_TYPE_DAPM_WIDGET, SND_TPLG_INDEX_ALL)) { +- SNDERR("warning: undefined sink widget/stream '%s'\n", +- route->sink); ++ SNDERR("undefined sink widget/stream '%s'", route->sink); + } + + /* validate control name */ +@@ -234,21 +230,21 @@ int tplg_build_routes(snd_tplg_t *tplg) + SND_TPLG_TYPE_MIXER, elem->index) && + !tplg_elem_lookup(&tplg->enum_list, route->control, + SND_TPLG_TYPE_ENUM, elem->index)) { +- SNDERR("warning: Undefined mixer/enum control '%s'\n", +- route->control); ++ SNDERR("Undefined mixer/enum control '%s'", ++ route->control); + } + } + + /* validate source */ + if (strlen(route->source) <= 0) { +- SNDERR("error: no source\n"); ++ SNDERR("no source"); + return -EINVAL; + + } + if (!tplg_elem_lookup(&tplg->widget_list, route->source, + SND_TPLG_TYPE_DAPM_WIDGET, SND_TPLG_INDEX_ALL)) { +- SNDERR("warning: Undefined source widget/stream '%s'\n", +- route->source); ++ SNDERR("Undefined source widget/stream '%s'", ++ route->source); + } + + /* add graph to manifest */ +@@ -300,7 +296,7 @@ static int tplg_parse_line(const char *text, + + len = strlen(buf); + if (len <= 2) { +- SNDERR("error: invalid route \"%s\"\n", buf); ++ SNDERR("invalid route \"%s\"", buf); + return -EINVAL; + } + +@@ -309,7 +305,7 @@ static int tplg_parse_line(const char *text, + if (buf[i] == ',') + goto second; + } +- SNDERR("error: invalid route \"%s\"\n", buf); ++ SNDERR("invalid route \"%s\"", buf); + return -EINVAL; + + second: +@@ -323,7 +319,7 @@ second: + goto done; + } + +- SNDERR("error: invalid route \"%s\"\n", buf); ++ SNDERR("invalid route \"%s\"", buf); + return -EINVAL; + + done: +@@ -378,7 +374,7 @@ int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg, + int index = -1; + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { +- SNDERR("error: compound is expected for dapm graph definition\n"); ++ SNDERR("compound is expected for dapm graph definition"); + return -EINVAL; + } + +@@ -401,13 +397,13 @@ int tplg_parse_dapm_graph(snd_tplg_t *tplg, snd_config_t *cfg, + + if (strcmp(id, "lines") == 0) { + if (index < 0) { +- SNDERR("error: failed to parse dapm graph %s, missing index\n", ++ SNDERR("failed to parse dapm graph %s, missing index", + graph_id); + return -EINVAL; + } + err = tplg_parse_routes(tplg, n, index); + if (err < 0) { +- SNDERR("error: failed to parse dapm graph %s\n", ++ SNDERR("failed to parse dapm graph %s", + graph_id); + return err; + } +@@ -528,7 +524,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + + widget_type = lookup_widget(val); + if (widget_type < 0){ +- SNDERR("Widget '%s': Unsupported widget type %s\n", ++ SNDERR("widget '%s': Unsupported widget type %s", + elem->id, val); + return -EINVAL; + } +@@ -821,7 +817,7 @@ int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + break; + + default: +- SNDERR("error: widget %s: invalid type %d for ctl %d\n", ++ SNDERR("widget %s: invalid type %d for ctl %d", + wt->name, ct->type, i); + break; + } +diff --git a/src/topology/data.c b/src/topology/data.c +index 11cd73f5ed6e..f00ed3011869 100644 +--- a/src/topology/data.c ++++ b/src/topology/data.c +@@ -61,7 +61,7 @@ struct snd_soc_tplg_private *get_priv_data(struct tplg_elem *elem) + priv = &elem->pcm->priv; + break; + default: +- SNDERR("error: '%s': no support for private data for type %d\n", ++ SNDERR("'%s': no support for private data for type %d", + elem->id, elem->type); + } + +@@ -95,7 +95,7 @@ int tplg_parse_refs(snd_config_t *cfg, struct tplg_elem *elem, + } + + if (cfg_type != SND_CONFIG_TYPE_COMPOUND) { +- SNDERR("error: compound type expected for %s", elem->id); ++ SNDERR("compound type expected for %s", elem->id); + return -EINVAL; + } + +@@ -183,8 +183,7 @@ static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem) + + fp = fopen(filename, "r"); + if (fp == NULL) { +- SNDERR("error: invalid data file path '%s'\n", +- filename); ++ SNDERR("invalid data file path '%s'", filename); + return -errno; + } + +@@ -192,12 +191,12 @@ static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem) + size = ftell(fp); + fseek(fp, 0L, SEEK_SET); + if (size <= 0) { +- SNDERR("error: invalid data file size %zu\n", size); ++ SNDERR("invalid data file size %zu", size); + ret = -EINVAL; + goto err; + } + if (size > TPLG_MAX_PRIV_SIZE) { +- SNDERR("error: data file too big %zu\n", size); ++ SNDERR("data file too big %zu", size); + ret = -EINVAL; + goto err; + } +@@ -350,7 +349,7 @@ static int get_uuid(const char *str, unsigned char *uuid_le) + if ((errno == ERANGE && val == ULONG_MAX) + || (errno != 0 && val == 0) + || (val > UCHAR_MAX)) { +- SNDERR("error: invalid value for uuid\n"); ++ SNDERR("invalid value for uuid"); + ret = -EINVAL; + goto out; + } +@@ -365,7 +364,7 @@ static int get_uuid(const char *str, unsigned char *uuid_le) + } + + if (values < 16) { +- SNDERR("error: less than 16 integers for uuid\n"); ++ SNDERR("less than 16 integers for uuid"); + ret = -EINVAL; + } + +@@ -446,7 +445,7 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, + + num = get_hex_num(value); + if (num <= 0) { +- SNDERR("error: malformed hex variable list %s\n", value); ++ SNDERR("malformed hex variable list %s", value); + return -EINVAL; + } + +@@ -454,7 +453,7 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, + priv = elem->data; + + if (size > TPLG_MAX_PRIV_SIZE) { +- SNDERR("error: data too big %d\n", size); ++ SNDERR("data too big %d", size); + return -EINVAL; + } + +@@ -492,7 +491,7 @@ static int get_token_value(const char *token_id, + return tokens->token[i].value; + } + +- SNDERR("error: cannot find token id '%s'\n", token_id); ++ SNDERR("cannot find token id '%s'", token_id); + return -1; + } + +@@ -579,7 +578,7 @@ static int copy_tuples(struct tplg_elem *elem, + * tuple_set->num_tuples; + size += set_size; + if (size > TPLG_MAX_PRIV_SIZE) { +- SNDERR("error: data too big %d\n", size); ++ SNDERR("data too big %d", size); + return -EINVAL; + } + +@@ -662,13 +661,13 @@ static int build_tuples(snd_tplg_t *tplg, struct tplg_elem *elem) + ref->id, SND_TPLG_TYPE_TUPLE, elem->index); + tuples = ref->elem; + if (!tuples) { +- SNDERR("error: cannot find tuples %s\n", ref->id); ++ SNDERR("cannot find tuples %s", ref->id); + return -EINVAL; + } + + tokens = get_tokens(tplg, tuples); + if (!tokens) { +- SNDERR("error: cannot find token for %s\n", ref->id); ++ SNDERR("cannot find token for %s", ref->id); + return -EINVAL; + } + +@@ -762,7 +761,7 @@ static int parse_tuple_set(snd_config_t *cfg, + + type = get_tuple_type(id); + if (type < 0) { +- SNDERR("error: invalid tuple type '%s'", id); ++ SNDERR("invalid tuple type '%s'", id); + return type; + } + +@@ -819,7 +818,7 @@ static int parse_tuple_set(snd_config_t *cfg, + case SND_SOC_TPLG_TUPLE_TYPE_WORD: + ival = tplg_get_unsigned(n, &tuple_val, 0); + if (ival < 0) { +- SNDERR("error: tuple %s: %s\n", id, snd_strerror(ival)); ++ SNDERR("tuple %s: %s", id, snd_strerror(ival)); + goto err; + } + +@@ -829,7 +828,7 @@ static int parse_tuple_set(snd_config_t *cfg, + && tuple_val > USHRT_MAX) + || (type == SND_SOC_TPLG_TUPLE_TYPE_BYTE + && tuple_val > UCHAR_MAX)) { +- SNDERR("error: tuple %s: invalid value\n", id); ++ SNDERR("tuple %s: invalid value", id); + goto err; + } + +@@ -941,7 +940,7 @@ static int parse_tuple_sets(snd_config_t *cfg, + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + if (snd_config_get_id(cfg, &id) >= 0) +- SNDERR("error: compound type expected for %s", id); ++ SNDERR("compound type expected for %s", id); + return -EINVAL; + } + +@@ -959,8 +958,8 @@ static int parse_tuple_sets(snd_config_t *cfg, + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { +- SNDERR("error: compound type expected for %s, is %d", +- id, snd_config_get_type(n)); ++ SNDERR("compound type expected for %s, is %d", ++ id, snd_config_get_type(n)); + return -EINVAL; + } + +@@ -1169,7 +1168,7 @@ int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg, + int err; + + if (!list_empty(&tplg->manifest_list)) { +- SNDERR("error: already has manifest data\n"); ++ SNDERR("already has manifest data"); + return -EINVAL; + } + +@@ -1326,7 +1325,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, + if (strcmp(id, "file") == 0) { + err = tplg_parse_data_file(n, elem); + if (err < 0) { +- SNDERR("error: failed to parse data file\n"); ++ SNDERR("failed to parse data file"); + return err; + } + continue; +@@ -1335,7 +1334,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, + if (strcmp(id, "bytes") == 0) { + err = tplg_parse_data_hex(n, elem, 1); + if (err < 0) { +- SNDERR("error: failed to parse data bytes\n"); ++ SNDERR("failed to parse data bytes"); + return err; + } + continue; +@@ -1344,7 +1343,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, + if (strcmp(id, "shorts") == 0) { + err = tplg_parse_data_hex(n, elem, 2); + if (err < 0) { +- SNDERR("error: failed to parse data shorts\n"); ++ SNDERR("failed to parse data shorts"); + return err; + } + continue; +@@ -1353,7 +1352,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, + if (strcmp(id, "words") == 0) { + err = tplg_parse_data_hex(n, elem, 4); + if (err < 0) { +- SNDERR("error: failed to parse data words\n"); ++ SNDERR("failed to parse data words"); + return err; + } + continue; +@@ -1470,8 +1469,8 @@ int tplg_copy_data(snd_tplg_t *tplg, struct tplg_elem *elem, + ref_elem = tplg_elem_lookup(&tplg->pdata_list, + ref->id, SND_TPLG_TYPE_DATA, elem->index); + if (!ref_elem) { +- SNDERR("error: cannot find data '%s' referenced by" +- " element '%s'\n", ref->id, elem->id); ++ SNDERR("cannot find data '%s' referenced by" ++ " element '%s'", ref->id, elem->id); + return -EINVAL; + } + +diff --git a/src/topology/parser.c b/src/topology/parser.c +index 1eaa24bd42f5..5a5dd14f2bdd 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -122,7 +122,7 @@ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg, + return -EINVAL; + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { +- SNDERR("error: compound type expected for %s", id); ++ SNDERR("compound type expected for %s", id); + return -EINVAL; + } + +@@ -131,7 +131,7 @@ int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg, + n = snd_config_iterator_entry(i); + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { +- SNDERR("error: compound type expected for %s, is %d", ++ SNDERR("compound type expected for %s, is %d", + id, snd_config_get_type(cfg)); + return -EINVAL; + } +@@ -155,7 +155,7 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) + int err; + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { +- SNDERR("error: compound type expected at top level"); ++ SNDERR("compound type expected at top level"); + return -EINVAL; + } + +@@ -180,7 +180,7 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) + } + + if (parser == NULL) { +- SNDERR("error: unknown section %s\n", id); ++ SNDERR("unknown section %s", id); + continue; + } + +@@ -202,7 +202,7 @@ static int tplg_load_config(snd_tplg_t *tplg, snd_input_t *in) + + ret = snd_config_load(top, in); + if (ret < 0) { +- SNDERR("error: could not load configuration"); ++ SNDERR("could not load configuration"); + snd_config_delete(top); + return ret; + } +@@ -210,7 +210,7 @@ static int tplg_load_config(snd_tplg_t *tplg, snd_input_t *in) + ret = tplg_parse_config(tplg, top); + snd_config_delete(top); + if (ret < 0) { +- SNDERR("error: failed to parse topology"); ++ SNDERR("failed to parse topology"); + return ret; + } + +@@ -267,7 +267,7 @@ int snd_tplg_load(snd_tplg_t *tplg, const char *buf, size_t size) + + err = snd_input_buffer_open(&in, buf, size); + if (err < 0) { +- SNDERR("error: could not create input buffer"); ++ SNDERR("could not create input buffer"); + return err; + } + +@@ -282,13 +282,13 @@ static int tplg_build(snd_tplg_t *tplg) + + err = tplg_build_integ(tplg); + if (err < 0) { +- SNDERR("error: failed to check topology integrity\n"); ++ SNDERR("failed to check topology integrity"); + return err; + } + + err = tplg_write_data(tplg); + if (err < 0) { +- SNDERR("error: failed to write data %d\n", err); ++ SNDERR("failed to write data %d", err); + return err; + } + return 0; +@@ -304,15 +304,14 @@ int snd_tplg_build_file(snd_tplg_t *tplg, + + fp = fopen(infile, "r"); + if (fp == NULL) { +- SNDERR("error: could not open configuration file %s", +- infile); ++ SNDERR("could not open configuration file %s", infile); + return -errno; + } + + err = snd_input_stdio_attach(&in, fp, 1); + if (err < 0) { + fclose(fp); +- SNDERR("error: could not attach stdio %s", infile); ++ SNDERR("could not attach stdio %s", infile); + return err; + } + +@@ -346,7 +345,7 @@ int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + case SND_TPLG_TYPE_CC: + return tplg_add_link_object(tplg, t); + default: +- SNDERR("error: invalid object type %d\n", t->type); ++ SNDERR("invalid object type %d", t->type); + return -EINVAL; + }; + } +@@ -362,18 +361,18 @@ int snd_tplg_build(snd_tplg_t *tplg, const char *outfile) + + fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { +- SNDERR("error: failed to open %s err %d\n", outfile, -errno); ++ SNDERR("failed to open %s err %d", outfile, -errno); + return -errno; + } + r = write(fd, tplg->bin, tplg->bin_size); + close(fd); + if (r < 0) { + err = -errno; +- SNDERR("error: write error: %s\n", strerror(errno)); ++ SNDERR("write error: %s", strerror(errno)); + return err; + } + if ((size_t)r != tplg->bin_size) { +- SNDERR("error: partial write (%zd != %zd)\n", r, tplg->bin_size); ++ SNDERR("partial write (%zd != %zd)", r, tplg->bin_size); + return -EIO; + } + return 0; +@@ -437,7 +436,7 @@ snd_tplg_t *snd_tplg_create(int flags) + snd_tplg_t *tplg; + + if (!is_little_endian()) { +- SNDERR("error: cannot support big-endian machines\n"); ++ SNDERR("cannot support big-endian machines"); + return NULL; + } + +diff --git a/src/topology/pcm.c b/src/topology/pcm.c +index d09fbe42f5da..bc3070816867 100644 +--- a/src/topology/pcm.c ++++ b/src/topology/pcm.c +@@ -116,8 +116,8 @@ static int build_pcm(snd_tplg_t *tplg, struct tplg_elem *elem) + return err; + } + if (!ref->elem) { +- SNDERR("error: cannot find '%s' referenced by" +- " PCM '%s'\n", ref->id, elem->id); ++ SNDERR("cannot find '%s' referenced by" ++ " PCM '%s'", ref->id, elem->id); + return -EINVAL; + } + } +@@ -137,7 +137,7 @@ int tplg_build_pcms(snd_tplg_t *tplg, unsigned int type) + + elem = list_entry(pos, struct tplg_elem, list); + if (elem->type != type) { +- SNDERR("error: invalid elem '%s'\n", elem->id); ++ SNDERR("invalid elem '%s'", elem->id); + return -EINVAL; + } + +@@ -196,7 +196,7 @@ int tplg_build_dais(snd_tplg_t *tplg, unsigned int type) + + elem = list_entry(pos, struct tplg_elem, list); + if (elem->type != type) { +- SNDERR("error: invalid elem '%s'\n", elem->id); ++ SNDERR("invalid elem '%s'", elem->id); + return -EINVAL; + } + +@@ -251,8 +251,8 @@ static int build_link(snd_tplg_t *tplg, struct tplg_elem *elem) + ref->elem = tplg_elem_lookup(&tplg->hw_cfg_list, + ref->id, SND_TPLG_TYPE_HW_CONFIG, elem->index); + if (!ref->elem) { +- SNDERR("error: cannot find HW config '%s'" +- " referenced by link '%s'\n", ++ SNDERR("cannot find HW config '%s'" ++ " referenced by link '%s'", + ref->id, elem->id); + return -EINVAL; + } +@@ -320,7 +320,7 @@ static int split_format(struct snd_soc_tplg_stream_caps *caps, char *str) + while ((s != NULL) && (i < SND_SOC_TPLG_MAX_FORMATS)) { + format = snd_pcm_format_value(s); + if (format == SND_PCM_FORMAT_UNKNOWN) { +- SNDERR("error: unsupported stream format %s\n", s); ++ SNDERR("unsupported stream format %s", s); + return -EINVAL; + } + +@@ -363,7 +363,7 @@ static int split_rate(struct snd_soc_tplg_stream_caps *caps, char *str) + rate = get_rate_value(s); + + if (rate == SND_PCM_RATE_UNKNOWN) { +- SNDERR("error: unsupported stream rate %s\n", s); ++ SNDERR("unsupported stream rate %s", s); + return -EINVAL; + } + +@@ -758,7 +758,7 @@ static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + + if (strcmp(id, "id") == 0) { + if (tplg_get_unsigned(n, &pcm->dai_id, 0)) { +- SNDERR("error: invalid fe dai ID\n"); ++ SNDERR("invalid fe dai ID"); + return -EINVAL; + } + +@@ -1379,7 +1379,7 @@ static int get_audio_hw_format(const char *val) + if (strcasecmp(audio_hw_formats[i].name, val) == 0) + return audio_hw_formats[i].type; + +- SNDERR("error: invalid audio HW format %s\n", val); ++ SNDERR("invalid audio HW format %s", val); + return -EINVAL; + } + +@@ -1452,8 +1452,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + /* For backwards capability, + * "master" == "codec is slave" + */ +- SNDERR("warning: deprecated bclk value '%s'\n", +- val); ++ SNDERR("deprecated bclk value '%s'", val); + + hw_cfg->bclk_master = SND_SOC_TPLG_BCLK_CS; + } else if (!strcmp(val, "codec_slave")) { +@@ -1490,8 +1489,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + /* For backwards capability, + * "master" == "codec is slave" + */ +- SNDERR("warning: deprecated fsync value '%s'\n", +- val); ++ SNDERR("deprecated fsync value '%s'", val); + + hw_cfg->fsync_master = SND_SOC_TPLG_FSYNC_CS; + } else if (!strcmp(val, "codec_slave")) { +@@ -1535,8 +1533,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + /* For backwards capability, + * "master" == "for codec, mclk is input" + */ +- SNDERR("warning: deprecated mclk value '%s'\n", +- val); ++ SNDERR("deprecated mclk value '%s'", val); + + hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CI; + } else if (!strcmp(val, "codec_mclk_in")) { +diff --git a/src/topology/text.c b/src/topology/text.c +index e9386e7df492..6dbf22305fd3 100644 +--- a/src/topology/text.c ++++ b/src/topology/text.c +@@ -38,7 +38,7 @@ static int parse_text_values(snd_config_t *cfg, struct tplg_elem *elem) + n = snd_config_iterator_entry(i); + + if (j == SND_SOC_TPLG_NUM_TEXTS) { +- tplg_dbg("error: text string number exceeds %d\n", j); ++ tplg_dbg("text string number exceeds %d\n", j); + return -ENOMEM; + } + +-- +2.16.4 + diff --git a/0047-topology-dapm-fix-the-SNDERR-Undefined.patch b/0047-topology-dapm-fix-the-SNDERR-Undefined.patch new file mode 100644 index 0000000..ac3658e --- /dev/null +++ b/0047-topology-dapm-fix-the-SNDERR-Undefined.patch @@ -0,0 +1,35 @@ +From 712fbacf160f899582a9adc3f30f297211b063bb Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Fri, 27 Dec 2019 19:32:03 +0100 +Subject: [PATCH 47/63] topology: dapm - fix the SNDERR() - Undefined + +Signed-off-by: Jaroslav Kysela +--- + src/topology/dapm.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/topology/dapm.c b/src/topology/dapm.c +index f61fe07157ac..88bddca3c08e 100644 +--- a/src/topology/dapm.c ++++ b/src/topology/dapm.c +@@ -230,7 +230,7 @@ int tplg_build_routes(snd_tplg_t *tplg) + SND_TPLG_TYPE_MIXER, elem->index) && + !tplg_elem_lookup(&tplg->enum_list, route->control, + SND_TPLG_TYPE_ENUM, elem->index)) { +- SNDERR("Undefined mixer/enum control '%s'", ++ SNDERR("undefined mixer/enum control '%s'", + route->control); + } + } +@@ -243,7 +243,7 @@ int tplg_build_routes(snd_tplg_t *tplg) + } + if (!tplg_elem_lookup(&tplg->widget_list, route->source, + SND_TPLG_TYPE_DAPM_WIDGET, SND_TPLG_INDEX_ALL)) { +- SNDERR("Undefined source widget/stream '%s'", ++ SNDERR("undefined source widget/stream '%s'", + route->source); + } + +-- +2.16.4 + diff --git a/0048-topology-fix-the-unitialized-tuples.patch b/0048-topology-fix-the-unitialized-tuples.patch new file mode 100644 index 0000000..616e297 --- /dev/null +++ b/0048-topology-fix-the-unitialized-tuples.patch @@ -0,0 +1,25 @@ +From 07d779143bfd24448034cd55945b46c46407247c Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sat, 28 Dec 2019 10:18:34 +0100 +Subject: [PATCH 48/63] topology: fix the unitialized tuples + +Signed-off-by: Jaroslav Kysela +--- + src/topology/data.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/topology/data.c b/src/topology/data.c +index f00ed3011869..4e43fcc9d5a7 100644 +--- a/src/topology/data.c ++++ b/src/topology/data.c +@@ -601,6 +601,7 @@ static int copy_tuples(struct tplg_elem *elem, + elem->data = priv; + + array = (struct snd_soc_tplg_vendor_array *)(priv->data + off); ++ memset(array, 0, set_size); + array->size = set_size; + array->type = tuple_set->type; + array->num_elems = tuple_set->num_tuples; +-- +2.16.4 + diff --git a/0049-topology-implement-shorter-hexa-uuid-00-00-parser.patch b/0049-topology-implement-shorter-hexa-uuid-00-00-parser.patch new file mode 100644 index 0000000..579fe94 --- /dev/null +++ b/0049-topology-implement-shorter-hexa-uuid-00-00-parser.patch @@ -0,0 +1,96 @@ +From 2947d83c1322bcdb31c1da180acb0f779a63dcdd Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sat, 28 Dec 2019 21:44:03 +0100 +Subject: [PATCH 49/63] topology: implement shorter hexa uuid 00:00 parser + +Signed-off-by: Jaroslav Kysela +--- + src/topology/data.c | 37 +++++++++++++++++++++++++++++++------ + 1 file changed, 31 insertions(+), 6 deletions(-) + +diff --git a/src/topology/data.c b/src/topology/data.c +index 4e43fcc9d5a7..1ddd3c509e64 100644 +--- a/src/topology/data.c ++++ b/src/topology/data.c +@@ -22,8 +22,8 @@ + #include + + #define UUID_FORMAT "\ +-0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, \ +-0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x" ++%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:\ ++%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x" + + /* Get private data buffer of an element */ + struct snd_soc_tplg_private *get_priv_data(struct tplg_elem *elem) +@@ -316,7 +316,6 @@ format2: + values++; + s += 2; + } +- s++; + } + + s++; +@@ -341,6 +340,32 @@ static int get_uuid(const char *str, unsigned char *uuid_le) + if (tmp == NULL) + return -ENOMEM; + ++ if (strchr(tmp, ':') == NULL) ++ goto data2; ++ ++ s = strtok(tmp, ":"); ++ while (s != NULL) { ++ errno = 0; ++ val = strtoul(s, NULL, 16); ++ if ((errno == ERANGE && val == ULONG_MAX) ++ || (errno != 0 && val == 0) ++ || (val > UCHAR_MAX)) { ++ SNDERR("invalid value for uuid"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ *(uuid_le + values) = (unsigned char)val; ++ ++ values++; ++ if (values >= 16) ++ break; ++ ++ s = strtok(NULL, ":"); ++ } ++ goto out; ++ ++data2: + s = strtok(tmp, ","); + + while (s != NULL) { +@@ -354,7 +379,7 @@ static int get_uuid(const char *str, unsigned char *uuid_le) + goto out; + } + +- *(uuid_le + values) = (unsigned char)val; ++ *(uuid_le + values) = (unsigned char)val; + + values++; + if (values >= 16) +@@ -413,7 +438,7 @@ static int copy_data_hex(char *data, int off, const char *str, int width) + return -ENOMEM; + + p += off; +- s = strtok(tmp, ","); ++ s = strtok(tmp, ",:"); + + while (s != NULL) { + ret = write_hex(p, s, width); +@@ -422,7 +447,7 @@ static int copy_data_hex(char *data, int off, const char *str, int width) + return ret; + } + +- s = strtok(NULL, ","); ++ s = strtok(NULL, ",:"); + p += width; + } + +-- +2.16.4 + diff --git a/0050-topology-fix-the-TPLG_DEBUG-compilation.patch b/0050-topology-fix-the-TPLG_DEBUG-compilation.patch new file mode 100644 index 0000000..b5f54b7 --- /dev/null +++ b/0050-topology-fix-the-TPLG_DEBUG-compilation.patch @@ -0,0 +1,66 @@ +From 1482d1f254fe42d83d904e52c911df8693b62653 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sun, 29 Dec 2019 20:05:14 +0100 +Subject: [PATCH 50/63] topology: fix the TPLG_DEBUG compilation + +Signed-off-by: Jaroslav Kysela +--- + src/topology/channel.c | 2 +- + src/topology/data.c | 5 ++++- + src/topology/pcm.c | 2 +- + 3 files changed, 6 insertions(+), 3 deletions(-) + +diff --git a/src/topology/channel.c b/src/topology/channel.c +index 110775a8053d..390c3f164e97 100644 +--- a/src/topology/channel.c ++++ b/src/topology/channel.c +@@ -129,7 +129,7 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg, + else if (strcmp(id, "shift") == 0) + channel->shift = value; + +- tplg_dbg("\t\t%s = %s\n", id, value); ++ tplg_dbg("\t\t%s = %d\n", id, value); + } + + tplg->channel_idx++; +diff --git a/src/topology/data.c b/src/topology/data.c +index 1ddd3c509e64..7b4bdccde602 100644 +--- a/src/topology/data.c ++++ b/src/topology/data.c +@@ -230,9 +230,11 @@ err: + return ret; + } + +-static void dump_priv_data(struct tplg_elem *elem) ++static void dump_priv_data(struct tplg_elem *elem ATTRIBUTE_UNUSED) + { ++#ifdef TPLG_DEBUG + struct snd_soc_tplg_private *priv = elem->data; ++ unsigned char *p = (unsigned char *)priv->data; + unsigned int i; + + tplg_dbg(" elem size = %d, priv data size = %d\n", +@@ -246,6 +248,7 @@ static void dump_priv_data(struct tplg_elem *elem) + } + + tplg_dbg("\n\n"); ++#endif + } + + static inline int check_nibble(unsigned char c) +diff --git a/src/topology/pcm.c b/src/topology/pcm.c +index bc3070816867..8e5afbe6ea72 100644 +--- a/src/topology/pcm.c ++++ b/src/topology/pcm.c +@@ -881,7 +881,7 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, + + pcm->compress = ival; + +- tplg_dbg("\t%s: %s\n", id, val); ++ tplg_dbg("\t%s: %d\n", id, ival); + continue; + } + +-- +2.16.4 + diff --git a/0051-topology-fix-the-ops-parser-accept-integer-hexa-valu.patch b/0051-topology-fix-the-ops-parser-accept-integer-hexa-valu.patch new file mode 100644 index 0000000..c6eea1e --- /dev/null +++ b/0051-topology-fix-the-ops-parser-accept-integer-hexa-valu.patch @@ -0,0 +1,91 @@ +From c765615bce7903a0f3e3d5e7826483708398c184 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Tue, 31 Dec 2019 15:27:58 +0100 +Subject: [PATCH 51/63] topology: fix the ops parser (accept integer/hexa + values) + +Signed-off-by: Jaroslav Kysela +--- + src/topology/ops.c | 36 +++++++++++++++++++++++++----------- + 1 file changed, 25 insertions(+), 11 deletions(-) + +diff --git a/src/topology/ops.c b/src/topology/ops.c +index ad72ef1b2cb6..2885c7814604 100644 +--- a/src/topology/ops.c ++++ b/src/topology/ops.c +@@ -67,6 +67,7 @@ int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg, + snd_config_t *n; + struct snd_soc_tplg_ctl_hdr *hdr = private; + const char *id, *value; ++ int ival; + + tplg_dbg("\tOps\n"); + hdr->size = sizeof(*hdr); +@@ -80,17 +81,23 @@ int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg, + continue; + + /* get value - try strings then ints */ +- if (snd_config_get_string(n, &value) < 0) +- continue; ++ if (snd_config_get_type(n) == SND_CONFIG_TYPE_STRING) { ++ if (snd_config_get_string(n, &value) < 0) ++ continue; ++ ival = lookup_ops(value); ++ } else { ++ if (tplg_get_integer(n, &ival, 0)) ++ continue; ++ } + + if (strcmp(id, "info") == 0) +- hdr->ops.info = lookup_ops(value); ++ hdr->ops.info = ival; + else if (strcmp(id, "put") == 0) +- hdr->ops.put = lookup_ops(value); ++ hdr->ops.put = ival; + else if (strcmp(id, "get") == 0) +- hdr->ops.get = lookup_ops(value); ++ hdr->ops.get = ival; + +- tplg_dbg("\t\t%s = %s\n", id, value); ++ tplg_dbg("\t\t%s = %d\n", id, ival); + } + + return 0; +@@ -146,6 +153,7 @@ int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + snd_config_t *n; + struct snd_soc_tplg_bytes_control *be = private; + const char *id, *value; ++ int ival; + + tplg_dbg("\tExt Ops\n"); + +@@ -158,15 +166,21 @@ int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + continue; + + /* get value - try strings then ints */ +- if (snd_config_get_string(n, &value) < 0) +- continue; ++ if (snd_config_get_type(n) == SND_CONFIG_TYPE_STRING) { ++ if (snd_config_get_string(n, &value) < 0) ++ continue; ++ ival = lookup_ops(value); ++ } else { ++ if (tplg_get_integer(n, &ival, 0)) ++ continue; ++ } + + if (strcmp(id, "info") == 0) +- be->ext_ops.info = lookup_ops(value); ++ be->ext_ops.info = ival; + else if (strcmp(id, "put") == 0) +- be->ext_ops.put = lookup_ops(value); ++ be->ext_ops.put = ival; + else if (strcmp(id, "get") == 0) +- be->ext_ops.get = lookup_ops(value); ++ be->ext_ops.get = ival; + + tplg_dbg("\t\t%s = %s\n", id, value); + } +-- +2.16.4 + diff --git a/0052-topology-fix-the-wrong-memory-access-object-realloc.patch b/0052-topology-fix-the-wrong-memory-access-object-realloc.patch new file mode 100644 index 0000000..f867fb3 --- /dev/null +++ b/0052-topology-fix-the-wrong-memory-access-object-realloc.patch @@ -0,0 +1,36 @@ +From 0ba4d6d9c0ae4576f35724d2a5735990f09ceeb0 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Wed, 1 Jan 2020 19:10:20 +0100 +Subject: [PATCH 52/63] topology: fix the wrong memory access (object realloc) + +Signed-off-by: Jaroslav Kysela +--- + src/topology/pcm.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/topology/pcm.c b/src/topology/pcm.c +index 8e5afbe6ea72..bd72895971f5 100644 +--- a/src/topology/pcm.c ++++ b/src/topology/pcm.c +@@ -252,8 +252,8 @@ static int build_link(snd_tplg_t *tplg, struct tplg_elem *elem) + ref->id, SND_TPLG_TYPE_HW_CONFIG, elem->index); + if (!ref->elem) { + SNDERR("cannot find HW config '%s'" +- " referenced by link '%s'", +- ref->id, elem->id); ++ " referenced by link '%s'", ++ ref->id, elem->id); + return -EINVAL; + } + +@@ -267,6 +267,7 @@ static int build_link(snd_tplg_t *tplg, struct tplg_elem *elem) + err = tplg_copy_data(tplg, elem, ref); + if (err < 0) + return err; ++ link = elem->link; /* realloc */ + break; + + default: +-- +2.16.4 + diff --git a/0053-topology-implement-snd_tplg_decode.patch b/0053-topology-implement-snd_tplg_decode.patch new file mode 100644 index 0000000..d55690e --- /dev/null +++ b/0053-topology-implement-snd_tplg_decode.patch @@ -0,0 +1,2375 @@ +From b6c9afb4f59bb678dc834028680d579f47dc273b Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Sun, 15 Dec 2019 15:24:57 +0100 +Subject: [PATCH 53/63] topology: implement snd_tplg_decode + +Signed-off-by: Jaroslav Kysela +--- + include/topology.h | 14 +- + src/topology/Makefile.am | 3 +- + src/topology/ctl.c | 450 ++++++++++++++++++++++++++++++++++++++++------ + src/topology/dapm.c | 253 ++++++++++++++++++++++++-- + src/topology/data.c | 440 ++++++++++++++++++++++++++++++++++++++++++++- + src/topology/decoder.c | 136 ++++++++++++++ + src/topology/elem.c | 80 +++++++++ + src/topology/parser.c | 20 ++- + src/topology/pcm.c | 396 ++++++++++++++++++++++++++++++++++------ + src/topology/save.c | 3 + + src/topology/tplg_local.h | 71 +++++++- + 11 files changed, 1727 insertions(+), 139 deletions(-) + create mode 100644 src/topology/decoder.c + +diff --git a/include/topology.h b/include/topology.h +index 37bced1a4434..1f52e66ea7b0 100644 +--- a/include/topology.h ++++ b/include/topology.h +@@ -885,7 +885,10 @@ struct snd_tplg_ctl_template { + const char *name; /*!< Control name */ + int access; /*!< Control access */ + struct snd_tplg_io_ops_template ops; /*!< operations */ +- struct snd_tplg_tlv_template *tlv; /*!< non NULL means we have TLV data */ ++ union { ++ struct snd_tplg_tlv_template *tlv; /*!< non NULL means we have TLV data */ ++ struct snd_tplg_tlv_dbscale_template *tlv_scale; /*!< scale TLV data */ ++ }; + }; + + /** \struct snd_tplg_mixer_template +@@ -1155,6 +1158,15 @@ int snd_tplg_set_version(snd_tplg_t *tplg, unsigned int version); + */ + int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags); + ++/** ++ * \brief Decode the binary topology contents. ++ * \param tplg Topology instance. ++ * \param bin Binary topology input buffer. ++ * \param size Binary topology input buffer size. ++ * \return Zero on success, otherwise a negative error code ++ */ ++int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags); ++ + /* \} */ + + #ifdef __cplusplus +diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am +index a850ec4c195c..12d1d4455883 100644 +--- a/src/topology/Makefile.am ++++ b/src/topology/Makefile.am +@@ -28,7 +28,8 @@ libatopology_la_SOURCES =\ + channel.c \ + ops.c \ + elem.c \ +- save.c ++ save.c \ ++ decoder.c + + noinst_HEADERS = tplg_local.h + +diff --git a/src/topology/ctl.c b/src/topology/ctl.c +index 03874b27c76f..24d437aa4a2e 100644 +--- a/src/topology/ctl.c ++++ b/src/topology/ctl.c +@@ -621,8 +621,9 @@ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg, + tplg->channel_idx = 0; + + /* set channel reg to default state */ +- for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++) ++ for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++) { + ec->channel[j].reg = -1; ++ } + + tplg_dbg(" Control Enum: %s\n", elem->id); + +@@ -896,9 +897,14 @@ int tplg_save_control_mixer(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + return err; + } + +-static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr, +- struct snd_tplg_ctl_template *t) ++static int init_ctl_hdr(snd_tplg_t *tplg, ++ struct tplg_elem *parent, ++ struct snd_soc_tplg_ctl_hdr *hdr, ++ struct snd_tplg_ctl_template *t) + { ++ struct tplg_elem *elem; ++ int err; ++ + hdr->size = sizeof(struct snd_soc_tplg_ctl_hdr); + hdr->type = t->type; + +@@ -924,7 +930,7 @@ static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr, + && !(hdr->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) { + + struct snd_tplg_tlv_template *tlvt = t->tlv; +- struct snd_soc_tplg_ctl_tlv *tlv = &hdr->tlv; ++ struct snd_soc_tplg_ctl_tlv *tlv; + struct snd_tplg_tlv_dbscale_template *scalet; + struct snd_soc_tplg_tlv_dbscale *scale; + +@@ -933,6 +939,17 @@ static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr, + return -EINVAL; + } + ++ elem = tplg_elem_new_common(tplg, NULL, parent->id, ++ SND_TPLG_TYPE_TLV); ++ if (!elem) ++ return -ENOMEM; ++ ++ tlv = elem->tlv; ++ ++ err = tplg_ref_add(parent, SND_TPLG_TYPE_TLV, parent->id); ++ if (err < 0) ++ return err; ++ + tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv); + tlv->type = tlvt->type; + +@@ -957,10 +974,10 @@ static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr, + } + + int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer, +- struct tplg_elem **e) ++ struct tplg_elem **e) + { +- struct snd_soc_tplg_private *priv = mixer->priv; + struct snd_soc_tplg_mixer_control *mc; ++ struct snd_soc_tplg_private *priv; + struct tplg_elem *elem; + int ret, i, num_channels; + +@@ -979,7 +996,7 @@ int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer, + /* init new mixer */ + mc = elem->mixer_ctrl; + mc->size = elem->size; +- ret = init_ctl_hdr(&mc->hdr, &mixer->hdr); ++ ret = init_ctl_hdr(tplg, elem, &mc->hdr, &mixer->hdr); + if (ret < 0) { + tplg_elem_free(elem); + return ret; +@@ -1000,25 +1017,20 @@ int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer, + for (i = 0; i < num_channels; i++) { + struct snd_tplg_channel_elem *channel = &mixer->map->channel[i]; + +- mc->channel[i].size = channel->size; ++ mc->channel[i].size = sizeof(mc->channel[0]); + mc->channel[i].reg = channel->reg; + mc->channel[i].shift = channel->shift; + mc->channel[i].id = channel->id; + } + + /* priv data */ +- if (priv) { +- mc = realloc(mc, elem->size + priv->size); +- if (!mc) { +- tplg_elem_free(elem); +- return -ENOMEM; +- } +- +- elem->mixer_ctrl = mc; +- elem->size += priv->size; +- mc->priv.size = priv->size; +- memcpy(mc->priv.data, priv->data, priv->size); +- } ++ priv = mixer->priv; ++ if (priv && priv->size > 0) { ++ ret = tplg_add_data(tplg, elem, priv, ++ sizeof(*priv) + priv->size); ++ if (ret < 0) ++ return ret; ++ } + + if (e) + *e = elem; +@@ -1026,11 +1038,12 @@ int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer, + } + + int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl, +- struct tplg_elem **e) ++ struct tplg_elem **e) + { + struct snd_soc_tplg_enum_control *ec; ++ struct snd_soc_tplg_private *priv; + struct tplg_elem *elem; +- int ret, i, num_items; ++ int ret, i, num_items, num_channels; + + tplg_dbg(" Control Enum: %s\n", enum_ctl->hdr.name); + +@@ -1046,7 +1059,7 @@ int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl, + + ec = elem->enum_ctrl; + ec->size = elem->size; +- ret = init_ctl_hdr(&ec->hdr, &enum_ctl->hdr); ++ ret = init_ctl_hdr(tplg, elem, &ec->hdr, &enum_ctl->hdr); + if (ret < 0) { + tplg_elem_free(elem); + return ret; +@@ -1058,6 +1071,22 @@ int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl, + ec->mask = enum_ctl->mask; + ec->count = enum_ctl->items; + ++ /* set channel reg to default state */ ++ for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) ++ ec->channel[i].reg = -1; ++ ++ num_channels = enum_ctl->map ? enum_ctl->map->num_channels : 0; ++ ec->num_channels = num_channels; ++ ++ for (i = 0; i < num_channels; i++) { ++ struct snd_tplg_channel_elem *channel = &enum_ctl->map->channel[i]; ++ ++ ec->channel[i].size = sizeof(ec->channel[0]); ++ ec->channel[i].reg = channel->reg; ++ ec->channel[i].shift = channel->shift; ++ ec->channel[i].id = channel->id; ++ } ++ + if (enum_ctl->texts != NULL) { + for (i = 0; i < num_items; i++) { + if (enum_ctl->texts[i] != NULL) +@@ -1077,21 +1106,13 @@ int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl, + } + } + +- if (enum_ctl->priv != NULL) { +- ec = realloc(ec, +- elem->size + enum_ctl->priv->size); +- if (!ec) { +- tplg_elem_free(elem); +- return -ENOMEM; +- } +- +- elem->enum_ctrl = ec; +- elem->size += enum_ctl->priv->size; +- +- memcpy(ec->priv.data, enum_ctl->priv->data, +- enum_ctl->priv->size); +- +- ec->priv.size = enum_ctl->priv->size; ++ /* priv data */ ++ priv = enum_ctl->priv; ++ if (priv && priv->size > 0) { ++ ret = tplg_add_data(tplg, elem, priv, ++ sizeof(*priv) + priv->size); ++ if (ret < 0) ++ return ret; + } + + if (e) +@@ -1100,9 +1121,10 @@ int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl, + } + + int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl, +- struct tplg_elem **e) ++ struct tplg_elem **e) + { + struct snd_soc_tplg_bytes_control *be; ++ struct snd_soc_tplg_private *priv; + struct tplg_elem *elem; + int ret; + +@@ -1120,7 +1142,7 @@ int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl, + + be = elem->bytes_ext; + be->size = elem->size; +- ret = init_ctl_hdr(&be->hdr, &bytes_ctl->hdr); ++ ret = init_ctl_hdr(tplg, elem, &be->hdr, &bytes_ctl->hdr); + if (ret < 0) { + tplg_elem_free(elem); + return ret; +@@ -1133,20 +1155,13 @@ int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl, + be->ext_ops.put = bytes_ctl->ext_ops.put; + be->ext_ops.get = bytes_ctl->ext_ops.get; + +- if (bytes_ctl->priv != NULL) { +- be = realloc(be, +- elem->size + bytes_ctl->priv->size); +- if (!be) { +- tplg_elem_free(elem); +- return -ENOMEM; +- } +- elem->bytes_ext = be; +- elem->size += bytes_ctl->priv->size; +- +- memcpy(be->priv.data, bytes_ctl->priv->data, +- bytes_ctl->priv->size); +- +- be->priv.size = bytes_ctl->priv->size; ++ /* priv data */ ++ priv = bytes_ctl->priv; ++ if (priv && priv->size > 0) { ++ ret = tplg_add_data(tplg, elem, priv, ++ sizeof(*priv) + priv->size); ++ if (ret < 0) ++ return ret; + } + + /* check on TLV bytes control */ +@@ -1184,3 +1199,330 @@ int tplg_add_bytes_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + { + return tplg_add_bytes(tplg, t->bytes_ctl, NULL); + } ++ ++int tplg_decode_control_mixer1(snd_tplg_t *tplg, ++ struct list_head *heap, ++ struct snd_tplg_mixer_template *mt, ++ size_t pos, ++ void *bin, size_t size) ++{ ++ struct snd_soc_tplg_mixer_control *mc = bin; ++ struct snd_tplg_channel_map_template *map; ++ struct snd_tplg_tlv_dbscale_template *db; ++ int i; ++ ++ if (size < sizeof(*mc)) { ++ SNDERR("mixer: small size %d", size); ++ return -EINVAL; ++ } ++ ++ tplg_dv(tplg, pos, "mixer: size %d TLV size %d private size %d", ++ mc->size, mc->hdr.tlv.size, mc->priv.size); ++ if (size != mc->size + mc->priv.size) { ++ SNDERR("mixer: unexpected element size %d", size); ++ return -EINVAL; ++ } ++ ++ memset(mt, 0, sizeof(*mt)); ++ mt->hdr.type = mc->hdr.type; ++ mt->hdr.name = mc->hdr.name; ++ mt->hdr.access = mc->hdr.access; ++ mt->hdr.ops.get = mc->hdr.ops.get; ++ mt->hdr.ops.put = mc->hdr.ops.put; ++ mt->hdr.ops.info = mc->hdr.ops.info; ++ mt->min = mc->min; ++ mt->max = mc->max; ++ mt->platform_max = mc->platform_max; ++ tplg_dv(tplg, pos, "mixer: name '%s' access 0x%x", ++ mt->hdr.name, mt->hdr.access); ++ if (mc->num_channels > 0) { ++ map = tplg_calloc(heap, sizeof(*map)); ++ map->num_channels = mc->num_channels; ++ for (i = 0; i < map->num_channels; i++) { ++ map->channel[i].reg = mc->channel[i].reg; ++ map->channel[i].shift = mc->channel[i].shift; ++ map->channel[i].id = mc->channel[i].id; ++ } ++ mt->map = map; ++ } ++ if (mc->hdr.tlv.size == 0) { ++ /* nothing */ ++ } else if (mc->hdr.tlv.size == sizeof(struct snd_soc_tplg_ctl_tlv)) { ++ if (mc->hdr.tlv.type != SNDRV_CTL_TLVT_DB_SCALE) { ++ SNDERR("mixer: unknown TLV type %d", ++ mc->hdr.tlv.type); ++ return -EINVAL; ++ } ++ db = tplg_calloc(heap, sizeof(*db)); ++ if (db == NULL) ++ return -ENOMEM; ++ mt->hdr.tlv_scale = db; ++ db->hdr.type = mc->hdr.tlv.type; ++ db->min = mc->hdr.tlv.scale.min; ++ db->step = mc->hdr.tlv.scale.step; ++ db->mute = mc->hdr.tlv.scale.mute; ++ tplg_dv(tplg, pos, "mixer: dB scale TLV: min %d step %d mute %d", ++ db->min, db->step, db->mute); ++ } else { ++ SNDERR("mixer: wrong TLV size %d", mc->hdr.tlv.size); ++ return -EINVAL; ++ } ++ ++ mt->priv = &mc->priv; ++ tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_mixer_control, priv), ++ "mixer: private start"); ++ return 0; ++} ++ ++int tplg_decode_control_mixer(snd_tplg_t *tplg, ++ size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size) ++{ ++ struct list_head heap; ++ snd_tplg_obj_template_t t; ++ struct snd_tplg_mixer_template mt; ++ struct snd_soc_tplg_mixer_control *mc; ++ size_t size2; ++ int err; ++ ++ err = tplg_decode_template(tplg, pos, hdr, &t); ++ if (err < 0) ++ return err; ++ ++next: ++ if (size < sizeof(*mc)) { ++ SNDERR("mixer: small size %d", size); ++ return -EINVAL; ++ } ++ INIT_LIST_HEAD(&heap); ++ mc = bin; ++ size2 = mc->size + mc->priv.size; ++ if (size2 > size) { ++ SNDERR("mixer: wrong element size (%d, priv %d)", ++ mc->size, mc->priv.size); ++ return -EINVAL; ++ } ++ ++ err = tplg_decode_control_mixer1(tplg, &heap, &mt, pos, bin, size2); ++ if (err >= 0) { ++ t.mixer = &mt; ++ err = snd_tplg_add_object(tplg, &t); ++ } ++ tplg_free(&heap); ++ if (err < 0) ++ return err; ++ ++ bin += size2; ++ size -= size2; ++ pos += size2; ++ ++ if (size > 0) ++ goto next; ++ ++ return 0; ++} ++ ++int tplg_decode_control_enum1(snd_tplg_t *tplg, ++ struct list_head *heap, ++ struct snd_tplg_enum_template *et, ++ size_t pos, ++ void *bin, size_t size) ++{ ++ struct snd_soc_tplg_enum_control *ec = bin; ++ struct snd_tplg_channel_map_template cmt; ++ int i; ++ ++ if (size < sizeof(*ec)) { ++ SNDERR("enum: small size %d", size); ++ return -EINVAL; ++ } ++ ++ tplg_dv(tplg, pos, "enum: size %d private size %d", ++ ec->size, ec->priv.size); ++ if (size != ec->size + ec->priv.size) { ++ SNDERR("enum: unexpected element size %d", size); ++ return -EINVAL; ++ } ++ if (ec->num_channels > SND_TPLG_MAX_CHAN || ++ ec->num_channels > SND_SOC_TPLG_MAX_CHAN) { ++ SNDERR("enum: unexpected channel count %d", ec->num_channels); ++ return -EINVAL; ++ } ++ if (ec->items > SND_SOC_TPLG_NUM_TEXTS) { ++ SNDERR("enum: unexpected texts count %d", ec->items); ++ return -EINVAL; ++ } ++ ++ memset(et, 0, sizeof(*et)); ++ et->hdr.type = ec->hdr.type; ++ et->hdr.name = ec->hdr.name; ++ et->hdr.access = ec->hdr.access; ++ et->hdr.ops.get = ec->hdr.ops.get; ++ et->hdr.ops.put = ec->hdr.ops.put; ++ et->hdr.ops.info = ec->hdr.ops.info; ++ et->mask = ec->mask; ++ ++ if (ec->items > 0) { ++ et->items = ec->items; ++ et->texts = tplg_calloc(heap, sizeof(char *) * ec->items); ++ if (!et->texts) ++ return -ENOMEM; ++ for (i = 0; ec->items; i++) { ++ unsigned int j = i * sizeof(int) * ENUM_VAL_SIZE; ++ et->texts[i] = ec->texts[i]; ++ et->values[i] = (int *)&ec->values[j]; ++ } ++ } ++ ++ et->map = &cmt; ++ memset(&cmt, 0, sizeof(cmt)); ++ cmt.num_channels = ec->num_channels; ++ for (i = 0; i < cmt.num_channels; i++) { ++ struct snd_tplg_channel_elem *channel = &cmt.channel[i]; ++ tplg_dv(tplg, pos + ((void *)&ec->channel[i] - (void *)ec), ++ "enum: channel size %d", ec->channel[i].size); ++ channel->reg = ec->channel[i].reg; ++ channel->shift = ec->channel[i].shift; ++ channel->id = ec->channel[i].id; ++ } ++ ++ et->priv = &ec->priv; ++ return 0; ++} ++ ++int tplg_decode_control_enum(snd_tplg_t *tplg, ++ size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size) ++{ ++ struct list_head heap; ++ snd_tplg_obj_template_t t; ++ struct snd_tplg_enum_template et; ++ struct snd_soc_tplg_enum_control *ec; ++ size_t size2; ++ int err; ++ ++ err = tplg_decode_template(tplg, pos, hdr, &t); ++ if (err < 0) ++ return err; ++ ++next: ++ if (size < sizeof(*ec)) { ++ SNDERR("enum: small size %d", size); ++ return -EINVAL; ++ } ++ INIT_LIST_HEAD(&heap); ++ ec = bin; ++ size2 = ec->size + ec->priv.size; ++ if (size2 > size) { ++ SNDERR("enum: wrong element size (%d, priv %d)", ++ ec->size, ec->priv.size); ++ return -EINVAL; ++ } ++ ++ err = tplg_decode_control_enum1(tplg, &heap, &et, pos, bin, size); ++ if (err >= 0) { ++ t.enum_ctl = &et; ++ err = snd_tplg_add_object(tplg, &t); ++ } ++ tplg_free(&heap); ++ if (err < 0) ++ return err; ++ ++ bin += size2; ++ size -= size2; ++ pos += size2; ++ ++ if (size > 0) ++ goto next; ++ ++ return 0; ++} ++ ++int tplg_decode_control_bytes1(snd_tplg_t *tplg, ++ struct snd_tplg_bytes_template *bt, ++ size_t pos, ++ void *bin, size_t size) ++{ ++ struct snd_soc_tplg_bytes_control *bc = bin; ++ ++ if (size < sizeof(*bc)) { ++ SNDERR("bytes: small size %d", size); ++ return -EINVAL; ++ } ++ ++ tplg_dv(tplg, pos, "control bytes: size %d private size %d", ++ bc->size, bc->priv.size); ++ if (size != bc->size + bc->priv.size) { ++ SNDERR("bytes: unexpected element size %d", size); ++ return -EINVAL; ++ } ++ ++ memset(bt, 0, sizeof(*bt)); ++ bt->hdr.type = bc->hdr.type; ++ bt->hdr.name = bc->hdr.name; ++ bt->hdr.access = bc->hdr.access; ++ bt->hdr.ops.get = bc->hdr.ops.get; ++ bt->hdr.ops.put = bc->hdr.ops.put; ++ bt->hdr.ops.info = bc->hdr.ops.info; ++ bt->max = bc->max; ++ bt->mask = bc->mask; ++ bt->base = bc->base; ++ bt->num_regs = bc->num_regs; ++ bt->ext_ops.get = bc->ext_ops.get; ++ bt->ext_ops.put = bc->ext_ops.put; ++ bt->ext_ops.info = bc->ext_ops.info; ++ tplg_dv(tplg, pos, "control bytes: name '%s' access 0x%x", ++ bt->hdr.name, bt->hdr.access); ++ ++ bt->priv = &bc->priv; ++ return 0; ++} ++ ++int tplg_decode_control_bytes(snd_tplg_t *tplg, ++ size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size) ++{ ++ snd_tplg_obj_template_t t; ++ struct snd_tplg_bytes_template bt; ++ struct snd_soc_tplg_bytes_control *bc; ++ size_t size2; ++ int err; ++ ++ err = tplg_decode_template(tplg, pos, hdr, &t); ++ if (err < 0) ++ return err; ++ ++next: ++ if (size < sizeof(*bc)) { ++ SNDERR("bytes: small size %d", size); ++ return -EINVAL; ++ } ++ bc = bin; ++ size2 = bc->size + bc->priv.size; ++ if (size2 > size) { ++ SNDERR("bytes: wrong element size (%d, priv %d)", ++ bc->size, bc->priv.size); ++ return -EINVAL; ++ } ++ ++ err = tplg_decode_control_bytes1(tplg, &bt, pos, bin, size); ++ if (err < 0) ++ return err; ++ ++ t.bytes_ctl = &bt; ++ err = snd_tplg_add_object(tplg, &t); ++ if (err < 0) ++ return err; ++ ++ bin += size2; ++ size -= size2; ++ pos += size2; ++ ++ if (size > 0) ++ goto next; ++ ++ return 0; ++} +diff --git a/src/topology/dapm.c b/src/topology/dapm.c +index 88bddca3c08e..9fab2d92c150 100644 +--- a/src/topology/dapm.c ++++ b/src/topology/dapm.c +@@ -420,19 +420,39 @@ int tplg_save_dapm_graph(snd_tplg_t *tplg, int index, char **dst, const char *pf + struct snd_soc_tplg_dapm_graph_elem *route; + struct list_head *pos; + struct tplg_elem *elem; +- int err, first = 1, old_index = -1; +- unsigned block = -1, count = 0; ++ int err, first, old_index; ++ unsigned block, count; ++ const char *fmt; + ++ old_index = -1; ++ block = 0; ++ count = 0; + list_for_each(pos, &tplg->route_list) { + elem = list_entry(pos, struct tplg_elem, list); + if (!elem->route || elem->type != SND_TPLG_TYPE_DAPM_GRAPH) + continue; + if (index >= 0 && elem->index != index) + continue; ++ if (old_index != elem->index) { ++ block++; ++ old_index = elem->index; ++ } + count++; + } + if (count == 0) + return 0; ++ if (block < 10) { ++ fmt = "\tset%u {\n"; ++ } else if (block < 100) { ++ fmt = "\tset%02u {\n"; ++ } else if (block < 1000) { ++ fmt = "\tset%03u {\n"; ++ } else { ++ return -EINVAL; ++ } ++ old_index = -1; ++ block = -1; ++ first = 1; + err = tplg_save_printf(dst, pfx, "SectionGraph {\n"); + list_for_each(pos, &tplg->route_list) { + elem = list_entry(pos, struct tplg_elem, list); +@@ -452,7 +472,7 @@ int tplg_save_dapm_graph(snd_tplg_t *tplg, int index, char **dst, const char *pf + old_index = elem->index; + block++; + first = 1; +- err = tplg_save_printf(dst, pfx, "\tset%u {\n", block); ++ err = tplg_save_printf(dst, pfx, fmt, block); + if (err >= 0) + err = tplg_save_printf(dst, pfx, "\t\tindex %u\n", + elem->index); +@@ -771,20 +791,14 @@ int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + w->event_flags = wt->event_flags; + w->event_type = wt->event_type; + +- if (wt->priv != NULL) { +- w = realloc(w, +- elem->size + wt->priv->size); +- if (!w) { ++ /* add private data */ ++ if (wt->priv != NULL && wt->priv->size > 0) { ++ ret = tplg_add_data(tplg, elem, wt->priv, ++ sizeof(*wt->priv) + wt->priv->size); ++ if (ret < 0) { + tplg_elem_free(elem); +- return -ENOMEM; ++ return ret; + } +- +- elem->widget = w; +- elem->size += wt->priv->size; +- +- memcpy(w->priv.data, wt->priv->data, +- wt->priv->size); +- w->priv.size = wt->priv->size; + } + + /* add controls to the widget's reference list */ +@@ -836,3 +850,212 @@ int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + + return 0; + } ++ ++/* decode dapm widget from the binary input */ ++int tplg_decode_dapm_widget(snd_tplg_t *tplg, ++ size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size) ++{ ++ struct list_head heap; ++ struct snd_soc_tplg_dapm_widget *w; ++ snd_tplg_obj_template_t t; ++ struct snd_tplg_widget_template *wt; ++ struct snd_tplg_mixer_template *mt; ++ struct snd_tplg_enum_template *et; ++ struct snd_tplg_bytes_template *bt; ++ struct snd_soc_tplg_ctl_hdr *chdr; ++ struct snd_soc_tplg_mixer_control *mc; ++ struct snd_soc_tplg_enum_control *ec; ++ struct snd_soc_tplg_bytes_control *bc; ++ size_t size2; ++ unsigned int index; ++ int err; ++ ++ err = tplg_decode_template(tplg, pos, hdr, &t); ++ if (err < 0) ++ return err; ++ ++next: ++ INIT_LIST_HEAD(&heap); ++ w = bin; ++ ++ if (size < sizeof(*w)) { ++ SNDERR("dapm widget: small size %d", size); ++ return -EINVAL; ++ } ++ if (sizeof(*w) != w->size) { ++ SNDERR("dapm widget: unknown element size %d (expected %zd)", ++ w->size, sizeof(*w)); ++ return -EINVAL; ++ } ++ if (w->num_kcontrols > 16) { ++ SNDERR("dapm widget: too many kcontrols %d", ++ w->num_kcontrols); ++ return -EINVAL; ++ } ++ ++ tplg_dv(tplg, pos, "dapm widget: size %d private size %d kcontrols %d", ++ w->size, w->priv.size, w->num_kcontrols); ++ ++ wt = tplg_calloc(&heap, sizeof(*wt) + sizeof(void *) * w->num_kcontrols); ++ if (wt == NULL) ++ return -ENOMEM; ++ wt->id = w->id; ++ wt->name = w->name; ++ wt->sname = w->sname; ++ wt->reg = w->reg; ++ wt->shift = w->shift; ++ wt->mask = w->mask; ++ wt->subseq = w->subseq; ++ wt->invert = w->invert; ++ wt->ignore_suspend = w->ignore_suspend; ++ wt->event_flags = w->event_flags; ++ wt->event_type = w->event_type; ++ ++ tplg_dv(tplg, pos, "dapm widget: name '%s' sname '%s'", wt->name, wt->sname); ++ ++ if (sizeof(*w) + w->priv.size > size) { ++ SNDERR("dapm widget: wrong private data size %d", ++ w->priv.size); ++ return -EINVAL; ++ } ++ ++ tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_dapm_widget, priv), ++ "dapm widget: private start"); ++ ++ wt->priv = &w->priv; ++ bin += sizeof(*w) + w->priv.size; ++ size -= sizeof(*w) + w->priv.size; ++ pos += sizeof(*w) + w->priv.size; ++ ++ for (index = 0; index < w->num_kcontrols; index++) { ++ chdr = bin; ++ switch (chdr->type) { ++ case SND_SOC_TPLG_TYPE_MIXER: ++ mt = tplg_calloc(&heap, sizeof(*mt)); ++ if (mt == NULL) { ++ err = -ENOMEM; ++ goto retval; ++ } ++ wt->ctl[index] = (void *)mt; ++ wt->num_ctls++; ++ mc = bin; ++ size2 = mc->size + mc->priv.size; ++ tplg_dv(tplg, pos, "kcontrol mixer size %zd", size2); ++ if (size2 > size) { ++ SNDERR("dapm widget: small mixer size %d", ++ size2); ++ err = -EINVAL; ++ goto retval; ++ } ++ err = tplg_decode_control_mixer1(tplg, &heap, mt, pos, ++ bin, size2); ++ break; ++ case SND_SOC_TPLG_TYPE_ENUM: ++ et = tplg_calloc(&heap, sizeof(*mt)); ++ if (et == NULL) { ++ err = -ENOMEM; ++ goto retval; ++ } ++ wt->ctl[index] = (void *)et; ++ wt->num_ctls++; ++ ec = bin; ++ size2 = ec->size + ec->priv.size; ++ tplg_dv(tplg, pos, "kcontrol enum size %zd", size2); ++ if (size2 > size) { ++ SNDERR("dapm widget: small enum size %d", ++ size2); ++ err = -EINVAL; ++ goto retval; ++ } ++ err = tplg_decode_control_enum1(tplg, &heap, et, pos, ++ bin, size2); ++ break; ++ case SND_SOC_TPLG_TYPE_BYTES: ++ bt = tplg_calloc(&heap, sizeof(*bt)); ++ if (bt == NULL) { ++ err = -ENOMEM; ++ goto retval; ++ } ++ wt->ctl[index] = (void *)bt; ++ wt->num_ctls++; ++ bc = bin; ++ size2 = bc->size + bc->priv.size; ++ tplg_dv(tplg, pos, "kcontrol bytes size %zd", size2); ++ if (size2 > size) { ++ SNDERR("dapm widget: small bytes size %d", ++ size2); ++ err = -EINVAL; ++ goto retval; ++ } ++ err = tplg_decode_control_bytes1(tplg, bt, pos, ++ bin, size2); ++ break; ++ default: ++ SNDERR("dapm widget: wrong control type %d", ++ chdr->type); ++ err = -EINVAL; ++ goto retval; ++ } ++ if (err < 0) ++ goto retval; ++ bin += size2; ++ size -= size2; ++ pos += size2; ++ } ++ ++ t.widget = wt; ++ err = snd_tplg_add_object(tplg, &t); ++ tplg_free(&heap); ++ if (err < 0) ++ return err; ++ if (size > 0) ++ goto next; ++ return 0; ++ ++retval: ++ tplg_free(&heap); ++ return err; ++} ++ ++/* decode dapm link from the binary input */ ++int tplg_decode_dapm_graph(snd_tplg_t *tplg, ++ size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size) ++{ ++ struct snd_soc_tplg_dapm_graph_elem *g; ++ snd_tplg_obj_template_t t; ++ struct snd_tplg_graph_template *gt; ++ struct snd_tplg_graph_elem *ge; ++ size_t asize; ++ int err; ++ ++ err = tplg_decode_template(tplg, pos, hdr, &t); ++ if (err < 0) ++ return err; ++ ++ asize = sizeof(*gt) + (size / sizeof(*g)) * sizeof(*ge); ++ gt = alloca(asize); ++ memset(gt, 0, asize); ++ for (ge = gt->elem; size > 0; ge++) { ++ g = bin; ++ if (size < sizeof(*g)) { ++ SNDERR("dapm graph: small size %d", size); ++ return -EINVAL; ++ } ++ ge->src = g->source; ++ ge->ctl = g->control; ++ ge->sink = g->sink; ++ gt->count++; ++ tplg_dv(tplg, pos, "dapm graph: src='%s' ctl='%s' sink='%s'", ++ ge->src, ge->ctl, ge->sink); ++ bin += sizeof(*g); ++ size -= sizeof(*g); ++ pos += sizeof(*g); ++ } ++ ++ t.graph = gt; ++ return snd_tplg_add_object(tplg, &t); ++} +diff --git a/src/topology/data.c b/src/topology/data.c +index 7b4bdccde602..64563920a4e2 100644 +--- a/src/topology/data.c ++++ b/src/topology/data.c +@@ -566,7 +566,7 @@ static bool has_tuples(struct tplg_elem *elem) + } + + /* get size of a tuple element from its type */ +-static unsigned int get_tuple_size(int type) ++unsigned int tplg_get_tuple_size(int type) + { + switch (type) { + +@@ -602,7 +602,7 @@ static int copy_tuples(struct tplg_elem *elem, + for (i = 0; i < tuples->num_sets ; i++) { + tuple_set = tuples->set[i]; + set_size = sizeof(struct snd_soc_tplg_vendor_array) +- + get_tuple_size(tuple_set->type) ++ + tplg_get_tuple_size(tuple_set->type) + * tuple_set->num_tuples; + size += set_size; + if (size > TPLG_MAX_PRIV_SIZE) { +@@ -1250,6 +1250,9 @@ int tplg_save_manifest_data(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + continue; + count++; + } ++ if (count == 0) ++ return tplg_save_printf(dst, NULL, ++ "'%s'.comment 'empty'\n", elem->id); + if (count > 1) { + err = tplg_save_printf(dst, NULL, "'%s'.data [\n", elem->id); + if (err < 0) +@@ -1557,3 +1560,436 @@ int tplg_build_data(snd_tplg_t *tplg) + + return 0; + } ++ ++/* decode manifest data */ ++int tplg_decode_manifest_data(snd_tplg_t *tplg, ++ size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size) ++{ ++ struct snd_soc_tplg_manifest *m = bin; ++ struct tplg_elem *elem; ++ size_t off; ++ ++ if (hdr->index != 0) { ++ SNDERR("manifest - wrong index %d", hdr->index); ++ return -EINVAL; ++ } ++ ++ if (sizeof(*m) > size) { ++ SNDERR("manifest - wrong size %zd (minimal %zd)", ++ size, sizeof(*m)); ++ return -EINVAL; ++ } ++ ++ if (m->size != sizeof(*m)) { ++ SNDERR("manifest - wrong sructure size %d", m->size); ++ return -EINVAL; ++ } ++ ++ off = offsetof(struct snd_soc_tplg_manifest, priv); ++ if (off + m->priv.size > size) { ++ SNDERR("manifest - wrong private size %d", m->priv.size); ++ return -EINVAL; ++ } ++ ++ tplg->manifest = *m; ++ ++ bin += off; ++ size -= off; ++ pos += off; ++ ++ elem = tplg_elem_new_common(tplg, NULL, "manifest", ++ SND_TPLG_TYPE_MANIFEST); ++ if (!elem) ++ return -ENOMEM; ++ ++ tplg_dv(tplg, pos, "manifest: private size %d", size); ++ return tplg_add_data(tplg, elem, bin, size); ++} ++ ++int tplg_add_token(snd_tplg_t *tplg, struct tplg_elem *parent, ++ unsigned int token, ++ char str_ref[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]) ++{ ++ struct tplg_elem *elem; ++ struct tplg_token *t; ++ struct tplg_vendor_tokens *tokens; ++ unsigned int i; ++ size_t size; ++ ++ elem = tplg_elem_lookup(&tplg->token_list, parent->id, ++ SND_TPLG_TYPE_TOKEN, parent->index); ++ if (elem == NULL) { ++ elem = tplg_elem_new_common(tplg, NULL, parent->id, ++ SND_TPLG_TYPE_TOKEN); ++ if (!elem) ++ return -ENOMEM; ++ } ++ ++ tokens = elem->tokens; ++ if (tokens) { ++ for (i = 0; i < tokens->num_tokens; i++) { ++ t = &tokens->token[i]; ++ if (t->value == token) ++ goto found; ++ } ++ size = sizeof(*tokens) + ++ (tokens->num_tokens + 1) * sizeof(struct tplg_token); ++ tokens = realloc(tokens, size); ++ } else { ++ size = sizeof(*tokens) + 1 * sizeof(struct tplg_token); ++ tokens = calloc(1, size); ++ } ++ ++ if (!tokens) ++ return -ENOMEM; ++ ++ memset(&tokens->token[tokens->num_tokens], 0, sizeof(struct tplg_token)); ++ elem->tokens = tokens; ++ t = &tokens->token[tokens->num_tokens]; ++ tokens->num_tokens++; ++ snprintf(t->id, sizeof(t->id), "token%u", token); ++ t->value = token; ++found: ++ snd_strlcpy(str_ref, t->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); ++ return 0; ++} ++ ++static int tplg_verify_tuple_set(snd_tplg_t *tplg, size_t pos, ++ const void *bin, size_t size) ++{ ++ const struct snd_soc_tplg_vendor_array *va; ++ unsigned int j; ++ ++ va = bin; ++ if (size < sizeof(*va) || size < va->size) { ++ tplg_dv(tplg, pos, "tuple set verify: wrong size %d", size); ++ return -EINVAL; ++ } ++ ++ switch (va->type) { ++ case SND_SOC_TPLG_TUPLE_TYPE_UUID: ++ case SND_SOC_TPLG_TUPLE_TYPE_STRING: ++ case SND_SOC_TPLG_TUPLE_TYPE_BOOL: ++ case SND_SOC_TPLG_TUPLE_TYPE_BYTE: ++ case SND_SOC_TPLG_TUPLE_TYPE_WORD: ++ case SND_SOC_TPLG_TUPLE_TYPE_SHORT: ++ break; ++ default: ++ tplg_dv(tplg, pos, "tuple set verify: unknown array type %d", va->type); ++ return -EINVAL; ++ } ++ ++ j = tplg_get_tuple_size(va->type) * va->num_elems; ++ if (j + sizeof(*va) != va->size) { ++ tplg_dv(tplg, pos, "tuple set verify: wrong vendor array size %d " ++ "(expected %d for %d count %d)", ++ va->size, j + sizeof(*va), va->type, va->num_elems); ++ return -EINVAL; ++ } ++ ++ if (va->num_elems > 4096) { ++ tplg_dv(tplg, pos, "tuple set verify: tuples overflow %d", va->num_elems); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int tplg_decode_tuple_set(snd_tplg_t *tplg, ++ size_t pos, ++ struct tplg_elem *parent, ++ struct tplg_tuple_set **_set, ++ const void *bin, size_t size) ++{ ++ const struct snd_soc_tplg_vendor_array *va; ++ struct tplg_tuple_set *set; ++ struct tplg_tuple *tuple; ++ unsigned int j; ++ int err; ++ ++ va = bin; ++ if (size < sizeof(*va) || size < va->size) { ++ SNDERR("tuples: wrong size %d", size); ++ return -EINVAL; ++ } ++ ++ switch (va->type) { ++ case SND_SOC_TPLG_TUPLE_TYPE_UUID: ++ case SND_SOC_TPLG_TUPLE_TYPE_STRING: ++ case SND_SOC_TPLG_TUPLE_TYPE_BOOL: ++ case SND_SOC_TPLG_TUPLE_TYPE_BYTE: ++ case SND_SOC_TPLG_TUPLE_TYPE_WORD: ++ case SND_SOC_TPLG_TUPLE_TYPE_SHORT: ++ break; ++ default: ++ SNDERR("tuples: unknown array type %d", va->type); ++ return -EINVAL; ++ } ++ ++ j = tplg_get_tuple_size(va->type) * va->num_elems; ++ if (j + sizeof(*va) != va->size) { ++ SNDERR("tuples: wrong vendor array size %d " ++ "(expected %d for %d count %d)", ++ va->size, j + sizeof(*va), va->type, va->num_elems); ++ return -EINVAL; ++ } ++ ++ if (va->num_elems > 4096) { ++ SNDERR("tuples: tuples overflow %d", va->num_elems); ++ return -EINVAL; ++ } ++ ++ set = calloc(1, sizeof(*set) + va->num_elems * sizeof(struct tplg_tuple)); ++ if (!set) ++ return -ENOMEM; ++ ++ set->type = va->type; ++ set->num_tuples = va->num_elems; ++ ++ tplg_dv(tplg, pos, "tuple set: type %d (%s) tuples %d size %d", set->type, ++ get_tuple_type_name(set->type), set->num_tuples, va->size); ++ for (j = 0; j < set->num_tuples; j++) { ++ tuple = &set->tuple[j]; ++ switch (va->type) { ++ case SND_SOC_TPLG_TUPLE_TYPE_UUID: ++ err = tplg_add_token(tplg, parent, va->uuid[j].token, ++ tuple->token); ++ if (err < 0) ++ goto retval; ++ memcpy(tuple->uuid, va->uuid[j].uuid, ++ sizeof(va->uuid[j].uuid)); ++ break; ++ case SND_SOC_TPLG_TUPLE_TYPE_STRING: ++ err = tplg_add_token(tplg, parent, va->string[j].token, ++ tuple->token); ++ if (err < 0) ++ goto retval; ++ snd_strlcpy(tuple->string, va->string[j].string, ++ sizeof(tuple->string)); ++ break; ++ case SND_SOC_TPLG_TUPLE_TYPE_BOOL: ++ case SND_SOC_TPLG_TUPLE_TYPE_BYTE: ++ case SND_SOC_TPLG_TUPLE_TYPE_WORD: ++ case SND_SOC_TPLG_TUPLE_TYPE_SHORT: ++ err = tplg_add_token(tplg, parent, va->value[j].token, ++ tuple->token); ++ if (err < 0) ++ goto retval; ++ tuple->value = va->value[j].value; ++ break; ++ } ++ } ++ ++ *_set = set; ++ return 0; ++ ++retval: ++ free(set); ++ return err; ++} ++ ++/* verify tuples from the binary input */ ++static int tplg_verify_tuples(snd_tplg_t *tplg, size_t pos, ++ const void *bin, size_t size) ++{ ++ const struct snd_soc_tplg_vendor_array *va; ++ int err; ++ ++ if (size < sizeof(*va)) { ++ tplg_dv(tplg, pos, "tuples: small size %d", size); ++ return -EINVAL; ++ } ++ ++next: ++ va = bin; ++ if (size < sizeof(*va)) { ++ tplg_dv(tplg, pos, "tuples: unexpected vendor arry size %d", size); ++ return -EINVAL; ++ } ++ ++ err = tplg_verify_tuple_set(tplg, pos, va, va->size); ++ if (err < 0) ++ return err; ++ ++ bin += va->size; ++ size -= va->size; ++ pos += va->size; ++ if (size > 0) ++ goto next; ++ ++ return 0; ++} ++ ++/* add tuples from the binary input */ ++static int tplg_decode_tuples(snd_tplg_t *tplg, ++ size_t pos, ++ struct tplg_elem *parent, ++ struct tplg_vendor_tuples *tuples, ++ const void *bin, size_t size) ++{ ++ const struct snd_soc_tplg_vendor_array *va; ++ struct tplg_tuple_set *set; ++ int err; ++ ++ if (size < sizeof(*va)) { ++ SNDERR("tuples: small size %d", size); ++ return -EINVAL; ++ } ++ ++next: ++ va = bin; ++ if (size < sizeof(*va)) { ++ SNDERR("tuples: unexpected vendor arry size %d", size); ++ return -EINVAL; ++ } ++ ++ if (tuples->num_sets >= tuples->alloc_sets) { ++ SNDERR("tuples: index overflow (%d)", tuples->num_sets); ++ return -EINVAL; ++ } ++ ++ err = tplg_decode_tuple_set(tplg, pos, parent, &set, va, va->size); ++ if (err < 0) ++ return err; ++ tuples->set[tuples->num_sets++] = set; ++ ++ bin += va->size; ++ size -= va->size; ++ pos += va->size; ++ if (size > 0) ++ goto next; ++ ++ return 0; ++} ++ ++/* decode private data */ ++int tplg_add_data(snd_tplg_t *tplg, ++ struct tplg_elem *parent, ++ const void *bin, size_t size) ++{ ++ const struct snd_soc_tplg_private *tp; ++ const struct snd_soc_tplg_vendor_array *va; ++ struct tplg_elem *elem = NULL, *elem2 = NULL; ++ struct tplg_vendor_tuples *tuples = NULL; ++ char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; ++ char suffix[16]; ++ size_t pos = 0, off; ++ int err, num_tuples = 0, block = 0; ++ ++ if (size == 0) ++ return 0; ++ ++ off = offsetof(struct snd_soc_tplg_private, array); ++ ++next: ++ tp = bin; ++ if (off + size < tp->size) { ++ SNDERR("data: unexpected element size %d", size); ++ return -EINVAL; ++ } ++ ++ if (tplg_verify_tuples(tplg, pos, tp->array, tp->size) < 0) { ++ if (tuples) { ++ err = tplg_ref_add(elem, SND_TPLG_TYPE_TOKEN, parent->id); ++ if (err < 0) ++ return err; ++ err = tplg_ref_add(elem2, SND_TPLG_TYPE_TUPLE, id); ++ if (err < 0) ++ return err; ++ err = tplg_ref_add(parent, SND_TPLG_TYPE_DATA, id); ++ if (err < 0) ++ return err; ++ tuples = NULL; ++ } ++ tplg_dv(tplg, pos, "add bytes: size %d", tp->size); ++ snprintf(suffix, sizeof(suffix), "data%u", block++); ++ err = tplg_add_data_bytes(tplg, parent, suffix, tp->array, tp->size); ++ } else { ++ if (!tuples) { ++ snprintf(id, sizeof(id), "%.30s:tuple%d", parent->id, (block++) & 0xffff); ++ elem = tplg_elem_new_common(tplg, NULL, id, SND_TPLG_TYPE_TUPLE); ++ if (!elem) ++ return -ENOMEM; ++ ++ elem2 = tplg_elem_new_common(tplg, NULL, id, SND_TPLG_TYPE_DATA); ++ if (!elem2) ++ return -ENOMEM; ++ ++ tuples = calloc(1, sizeof(*tuples)); ++ if (!tuples) ++ return -ENOMEM; ++ elem->tuples = tuples; ++ ++ tuples->alloc_sets = (size / sizeof(*va)) + 1; ++ tuples->set = calloc(1, tuples->alloc_sets * sizeof(void *)); ++ if (!tuples->set) { ++ tuples->alloc_sets = 0; ++ return -ENOMEM; ++ } ++ } ++ tplg_dv(tplg, pos, "decode tuples: size %d", tp->size); ++ err = tplg_decode_tuples(tplg, pos, parent, tuples, tp->array, tp->size); ++ num_tuples++; ++ } ++ if (err < 0) ++ return err; ++ ++ bin += off + tp->size; ++ size -= off + tp->size; ++ pos += off + tp->size; ++ if (size > 0) ++ goto next; ++ ++ if (tuples && elem && elem2) { ++ err = tplg_ref_add(elem, SND_TPLG_TYPE_TOKEN, parent->id); ++ if (err < 0) ++ return err; ++ err = tplg_ref_add(elem2, SND_TPLG_TYPE_TUPLE, id); ++ if (err < 0) ++ return err; ++ err = tplg_ref_add(parent, SND_TPLG_TYPE_DATA, id); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/* add private data - bytes */ ++int tplg_add_data_bytes(snd_tplg_t *tplg, struct tplg_elem *parent, ++ const char *suffix, const void *bin, size_t size) ++{ ++ struct snd_soc_tplg_private *priv; ++ struct tplg_elem *elem; ++ char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; ++ ++ if (suffix) ++ snprintf(id, sizeof(id), "%.30s:%.12s", parent->id, suffix); ++ else ++ snd_strlcpy(id, parent->id, sizeof(id)); ++ elem = tplg_elem_new_common(tplg, NULL, id, SND_TPLG_TYPE_DATA); ++ if (!elem) ++ return -ENOMEM; ++ ++ priv = malloc(sizeof(*priv) + size); ++ if (!priv) ++ return -ENOMEM; ++ memcpy(priv->data, bin, size); ++ priv->size = size; ++ elem->data = priv; ++ ++ return tplg_ref_add(parent, SND_TPLG_TYPE_DATA, id); ++} ++ ++/* decode data from the binary input */ ++int tplg_decode_data(snd_tplg_t *tplg ATTRIBUTE_UNUSED, ++ size_t pos ATTRIBUTE_UNUSED, ++ struct snd_soc_tplg_hdr *hdr ATTRIBUTE_UNUSED, ++ void *bin ATTRIBUTE_UNUSED, ++ size_t size ATTRIBUTE_UNUSED) ++{ ++ SNDERR("data type not expected"); ++ return -EINVAL; ++} +diff --git a/src/topology/decoder.c b/src/topology/decoder.c +new file mode 100644 +index 000000000000..2d6a8969756a +--- /dev/null ++++ b/src/topology/decoder.c +@@ -0,0 +1,136 @@ ++/* ++ Copyright (c) 2019 Red Hat Inc. ++ All rights reserved. ++ ++ 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.1 of ++ the License, or (at your option) any later version. ++ ++ 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 Lesser General Public License for more details. ++ ++ Authors: Jaroslav Kysela ++*/ ++ ++#include "list.h" ++#include "tplg_local.h" ++ ++/* verbose output detailing each object size and file position */ ++void tplg_dv(snd_tplg_t *tplg, size_t pos, const char *fmt, ...) ++{ ++ va_list va; ++ ++ if (!tplg->verbose) ++ return; ++ ++ va_start(va, fmt); ++ fprintf(stdout, "D0x%6.6zx/%6.6zd - ", pos, pos); ++ vfprintf(stdout, fmt, va); ++ va_end(va); ++ putc('\n', stdout); ++} ++ ++int tplg_decode_template(snd_tplg_t *tplg, ++ size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ snd_tplg_obj_template_t *t) ++{ ++ int type; ++ ++ type = tplg_get_type(hdr->type); ++ tplg_dv(tplg, pos, "template: asoc type %d library type %d", hdr->type, type); ++ if (type < 0) ++ return type; ++ ++ memset(t, 0, sizeof(*t)); ++ t->type = type; ++ t->index = hdr->index; ++ t->version = hdr->version; ++ t->vendor_type = hdr->vendor_type; ++ tplg_dv(tplg, pos, "template: index %d version %d vendor_type %d", ++ hdr->index, hdr->version, hdr->vendor_type); ++ return 0; ++} ++ ++int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags) ++{ ++ struct snd_soc_tplg_hdr *hdr; ++ struct tplg_table *tptr; ++ size_t pos; ++ void *b = bin; ++ unsigned int index; ++ int err; ++ ++ if (dflags != 0) ++ return -EINVAL; ++ if (tplg == NULL || bin == NULL) ++ return -EINVAL; ++ while (1) { ++ pos = b - bin; ++ if (size == pos) { ++ tplg_dv(tplg, pos, "block: success (total %zd)", size); ++ return 0; ++ } ++ if (size - pos < sizeof(*hdr)) { ++ tplg_dv(tplg, pos, "block: small size"); ++ SNDERR("incomplete header data to decode"); ++ return -EINVAL; ++ } ++ hdr = b; ++ if (hdr->magic != SND_SOC_TPLG_MAGIC) { ++ SNDERR("bad block magic %08x", hdr->magic); ++ return -EINVAL; ++ } ++ ++ tplg_dv(tplg, pos, "block: abi %d size %d payload size %d", ++ hdr->abi, hdr->size, hdr->payload_size); ++ if (hdr->abi != SND_SOC_TPLG_ABI_VERSION) { ++ SNDERR("unsupported ABI version %d", hdr->abi); ++ return -EINVAL; ++ } ++ if (hdr->size != sizeof(*hdr)) { ++ SNDERR("header size mismatch"); ++ return -EINVAL; ++ } ++ ++ if (size - pos < hdr->size + hdr->payload_size) { ++ SNDERR("incomplete payload data to decode"); ++ return -EINVAL; ++ } ++ ++ if (hdr->payload_size < 8) { ++ SNDERR("wrong payload size %d", hdr->payload_size); ++ return -EINVAL; ++ } ++ ++ /* first block must be manifest */ ++ if (b == bin) { ++ if (hdr->type != SND_SOC_TPLG_TYPE_MANIFEST) { ++ SNDERR("first block must be manifest (value %d)", hdr->type); ++ return -EINVAL; ++ } ++ err = snd_tplg_set_version(tplg, hdr->version); ++ if (err < 0) ++ return err; ++ } ++ ++ pos += hdr->size; ++ for (index = 0; index < tplg_table_items; index++) { ++ tptr = &tplg_table[index]; ++ if (tptr->tsoc == (int)hdr->type) ++ break; ++ } ++ if (index >= tplg_table_items || tptr->decod == NULL) { ++ SNDERR("unknown block type %d", hdr->type); ++ return -EINVAL; ++ } ++ tplg_dv(tplg, pos, "block: type %d - %s", hdr->type, tptr->name); ++ err = tptr->decod(tplg, pos, hdr, b + hdr->size, hdr->payload_size); ++ if (err < 0) ++ return err; ++ b += hdr->size + hdr->payload_size; ++ } ++} +diff --git a/src/topology/elem.c b/src/topology/elem.c +index 89aed1fc0449..ed5b5f13054f 100644 +--- a/src/topology/elem.c ++++ b/src/topology/elem.c +@@ -31,6 +31,7 @@ struct tplg_table tplg_table[] = { + .enew = 1, + .parse = tplg_parse_manifest_data, + .save = tplg_save_manifest_data, ++ .decod = tplg_decode_manifest_data, + }, + { + .name = "control mixer", +@@ -43,6 +44,7 @@ struct tplg_table tplg_table[] = { + .enew = 1, + .parse = tplg_parse_control_mixer, + .save = tplg_save_control_mixer, ++ .decod = tplg_decode_control_mixer, + }, + { + .name = "control enum", +@@ -55,6 +57,7 @@ struct tplg_table tplg_table[] = { + .enew = 1, + .parse = tplg_parse_control_enum, + .save = tplg_save_control_enum, ++ .decod = tplg_decode_control_enum, + }, + { + .name = "control extended (bytes)", +@@ -67,6 +70,7 @@ struct tplg_table tplg_table[] = { + .enew = 1, + .parse = tplg_parse_control_bytes, + .save = tplg_save_control_bytes, ++ .decod = tplg_decode_control_bytes, + }, + { + .name = "dapm widget", +@@ -79,6 +83,7 @@ struct tplg_table tplg_table[] = { + .enew = 1, + .parse = tplg_parse_dapm_widget, + .save = tplg_save_dapm_widget, ++ .decod = tplg_decode_dapm_widget, + }, + { + .name = "pcm", +@@ -91,6 +96,7 @@ struct tplg_table tplg_table[] = { + .enew = 1, + .parse = tplg_parse_pcm, + .save = tplg_save_pcm, ++ .decod = tplg_decode_pcm, + }, + { + .name = "physical dai", +@@ -103,6 +109,7 @@ struct tplg_table tplg_table[] = { + .enew = 1, + .parse = tplg_parse_dai, + .save = tplg_save_dai, ++ .decod = tplg_decode_dai, + }, + { + .name = "be", +@@ -116,6 +123,7 @@ struct tplg_table tplg_table[] = { + .enew = 1, + .parse = tplg_parse_link, + .save = tplg_save_link, ++ .decod = tplg_decode_link, + }, + { + .name = "cc", +@@ -128,6 +136,7 @@ struct tplg_table tplg_table[] = { + .enew = 1, + .parse = tplg_parse_cc, + .save = tplg_save_cc, ++ .decod = tplg_decode_cc, + }, + { + .name = "route (dapm graph)", +@@ -138,6 +147,7 @@ struct tplg_table tplg_table[] = { + .build = 1, + .parse = tplg_parse_dapm_graph, + .gsave = tplg_save_dapm_graph, ++ .decod = tplg_decode_dapm_graph, + }, + { + .name = "private data", +@@ -149,6 +159,7 @@ struct tplg_table tplg_table[] = { + .enew = 1, + .parse = tplg_parse_data, + .save = tplg_save_data, ++ .decod = tplg_decode_data, + }, + { + .name = "text", +@@ -220,6 +231,17 @@ struct tplg_table tplg_table[] = { + + unsigned int tplg_table_items = ARRAY_SIZE(tplg_table); + ++int tplg_get_type(int asoc_type) ++{ ++ unsigned int index; ++ ++ for (index = 0; index < tplg_table_items; index++) ++ if (tplg_table[index].tsoc == asoc_type) ++ return tplg_table[index].type; ++ SNDERR("uknown asoc type %d", asoc_type); ++ return -EINVAL; ++} ++ + int tplg_ref_add(struct tplg_elem *elem, int type, const char* id) + { + struct tplg_ref *ref; +@@ -331,6 +353,36 @@ struct tplg_elem *tplg_elem_lookup(struct list_head *base, const char* id, + return NULL; + } + ++/* find an element by type */ ++struct tplg_elem *tplg_elem_type_lookup(snd_tplg_t *tplg, ++ enum snd_tplg_type type) ++{ ++ struct tplg_table *tptr; ++ struct list_head *pos, *list; ++ struct tplg_elem *elem; ++ unsigned int index; ++ ++ for (index = 0; index < tplg_table_items; index++) { ++ tptr = &tplg_table[index]; ++ if (!tptr->enew) ++ continue; ++ if ((int)type != tptr->type) ++ continue; ++ break; ++ } ++ if (index >= tplg_table_items) ++ return NULL; ++ ++ list = (struct list_head *)((void *)tplg + tptr->loff); ++ ++ /* return only first element */ ++ list_for_each(pos, list) { ++ elem = list_entry(pos, struct tplg_elem, list); ++ return elem; ++ } ++ return NULL; ++} ++ + /* insert a new element into list in the ascending order of index value */ + void tplg_elem_insert(struct tplg_elem *elem_p, struct list_head *list) + { +@@ -428,3 +480,31 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, + elem->type = type; + return elem; + } ++ ++struct tplg_alloc { ++ struct list_head list; ++ void *data[0]; ++}; ++ ++void *tplg_calloc(struct list_head *heap, size_t size) ++{ ++ struct tplg_alloc *a; ++ ++ a = calloc(1, sizeof(*a) + size); ++ if (a == NULL) ++ return NULL; ++ list_add_tail(&a->list, heap); ++ return a->data; ++} ++ ++void tplg_free(struct list_head *heap) ++{ ++ struct list_head *pos, *npos; ++ struct tplg_alloc *a; ++ ++ list_for_each_safe(pos, npos, heap) { ++ a = list_entry(pos, struct tplg_alloc, list); ++ list_del(&a->list); ++ free(a); ++ } ++} +diff --git a/src/topology/parser.c b/src/topology/parser.c +index 5a5dd14f2bdd..d7783a93ab6b 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -396,17 +396,21 @@ int snd_tplg_build_bin(snd_tplg_t *tplg, + + int snd_tplg_set_manifest_data(snd_tplg_t *tplg, const void *data, int len) + { ++ struct tplg_elem *elem; ++ ++ elem = tplg_elem_type_lookup(tplg, SND_TPLG_TYPE_MANIFEST); ++ if (elem == NULL) { ++ elem = tplg_elem_new_common(tplg, NULL, "manifest", ++ SND_TPLG_TYPE_MANIFEST); ++ if (!elem) ++ return -ENOMEM; ++ tplg->manifest.size = elem->size; ++ } ++ + if (len <= 0) + return 0; + +- tplg->manifest.priv.size = len; +- +- tplg->manifest_pdata = malloc(len); +- if (!tplg->manifest_pdata) +- return -ENOMEM; +- +- memcpy(tplg->manifest_pdata, data, len); +- return 0; ++ return tplg_add_data_bytes(tplg, elem, NULL, data, len); + } + + int snd_tplg_set_version(snd_tplg_t *tplg, unsigned int version) +diff --git a/src/topology/pcm.c b/src/topology/pcm.c +index bd72895971f5..4e04a6bc3d78 100644 +--- a/src/topology/pcm.c ++++ b/src/topology/pcm.c +@@ -1679,11 +1679,20 @@ static void tplg_add_stream_object(struct snd_soc_tplg_stream *strm, + strm->channels = strm_tpl->channels; + } + +-static void tplg_add_stream_caps(struct snd_soc_tplg_stream_caps *caps, +- struct snd_tplg_stream_caps_template *caps_tpl) ++static int tplg_add_stream_caps(snd_tplg_t *tplg, ++ struct snd_tplg_stream_caps_template *caps_tpl) + { +- snd_strlcpy(caps->name, caps_tpl->name, +- SNDRV_CTL_ELEM_ID_NAME_MAXLEN); ++ struct snd_soc_tplg_stream_caps *caps; ++ struct tplg_elem *elem; ++ ++ elem = tplg_elem_new_common(tplg, NULL, caps_tpl->name, ++ SND_TPLG_TYPE_STREAM_CAPS); ++ if (!elem) ++ return -ENOMEM; ++ ++ caps = elem->stream_caps; ++ ++ snd_strlcpy(caps->name, caps_tpl->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + caps->formats = caps_tpl->formats; + caps->rates = caps_tpl->rates; +@@ -1698,15 +1707,17 @@ static void tplg_add_stream_caps(struct snd_soc_tplg_stream_caps *caps, + caps->buffer_size_min = caps_tpl->buffer_size_min; + caps->buffer_size_max = caps_tpl->buffer_size_max; + caps->sig_bits = caps_tpl->sig_bits; ++ return 0; + } + + /* Add a PCM element (FE DAI & DAI link) from C API */ + int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + { + struct snd_tplg_pcm_template *pcm_tpl = t->pcm; +- struct snd_soc_tplg_pcm *pcm, *_pcm; ++ struct snd_soc_tplg_private *priv; ++ struct snd_soc_tplg_pcm *pcm; + struct tplg_elem *elem; +- int i; ++ int ret, i; + + tplg_dbg("PCM: %s, DAI %s\n", pcm_tpl->pcm_name, pcm_tpl->dai_name); + +@@ -1732,8 +1743,13 @@ int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + pcm->compress = pcm_tpl->compress; + + for (i = 0; i < 2; i++) { +- if (pcm_tpl->caps[i]) +- tplg_add_stream_caps(&pcm->caps[i], pcm_tpl->caps[i]); ++ if (!pcm_tpl->caps[i] || !pcm_tpl->caps[i]->name) ++ continue; ++ ret = tplg_add_stream_caps(tplg, pcm_tpl->caps[i]); ++ if (ret < 0) ++ return ret; ++ snd_strlcpy(pcm->caps[i].name, pcm_tpl->caps[i]->name, ++ sizeof(pcm->caps[i].name)); + } + + pcm->flag_mask = pcm_tpl->flag_mask; +@@ -1744,22 +1760,12 @@ int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + tplg_add_stream_object(&pcm->stream[i], &pcm_tpl->stream[i]); + + /* private data */ +- if (pcm_tpl->priv != NULL && pcm_tpl->priv->size) { +- tplg_dbg("\t priv data size %d\n", pcm_tpl->priv->size); +- _pcm = realloc(pcm, +- elem->size + pcm_tpl->priv->size); +- if (!_pcm) { +- tplg_elem_free(elem); +- return -ENOMEM; +- } +- +- pcm = _pcm; +- elem->pcm = pcm; +- elem->size += pcm_tpl->priv->size; +- +- memcpy(pcm->priv.data, pcm_tpl->priv->data, +- pcm_tpl->priv->size); +- pcm->priv.size = pcm_tpl->priv->size; ++ priv = pcm_tpl->priv; ++ if (priv && priv->size > 0) { ++ ret = tplg_add_data(tplg, elem, priv, ++ sizeof(*priv) + priv->size); ++ if (ret < 0) ++ return ret; + } + + return 0; +@@ -1810,9 +1816,11 @@ static int set_link_hw_config(struct snd_soc_tplg_hw_config *cfg, + int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + { + struct snd_tplg_link_template *link_tpl = t->link; +- struct snd_soc_tplg_link_config *link, *_link; ++ struct snd_soc_tplg_link_config *link; ++ struct snd_soc_tplg_private *priv; + struct tplg_elem *elem; + unsigned int i; ++ int ret; + + if (t->type != SND_TPLG_TYPE_LINK && t->type != SND_TPLG_TYPE_BE + && t->type != SND_TPLG_TYPE_CC) +@@ -1854,21 +1862,12 @@ int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + link->flags = link_tpl->flags; + + /* private data */ +- if (link_tpl->priv != NULL && link_tpl->priv->size) { +- _link = realloc(link, +- elem->size + link_tpl->priv->size); +- if (!_link) { +- tplg_elem_free(elem); +- return -ENOMEM; +- } +- +- link = _link; +- elem->link = link; +- elem->size += link_tpl->priv->size; +- +- memcpy(link->priv.data, link_tpl->priv->data, +- link_tpl->priv->size); +- link->priv.size = link_tpl->priv->size; ++ priv = link_tpl->priv; ++ if (priv && priv->size > 0) { ++ ret = tplg_add_data(tplg, elem, priv, ++ sizeof(*priv) + priv->size); ++ if (ret < 0) ++ return ret; + } + + return 0; +@@ -1877,14 +1876,15 @@ int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + { + struct snd_tplg_dai_template *dai_tpl = t->dai; +- struct snd_soc_tplg_dai *dai, *_dai; ++ struct snd_soc_tplg_dai *dai; ++ struct snd_soc_tplg_private *priv; + struct tplg_elem *elem; +- int i; ++ int ret, i; + + tplg_dbg("DAI %s\n", dai_tpl->dai_name); + + elem = tplg_elem_new_common(tplg, NULL, dai_tpl->dai_name, +- SND_TPLG_TYPE_DAI); ++ SND_TPLG_TYPE_DAI); + if (!elem) + return -ENOMEM; + +@@ -1900,8 +1900,13 @@ int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + dai->capture = dai_tpl->capture; + + for (i = 0; i < 2; i++) { +- if (dai_tpl->caps[i]) +- tplg_add_stream_caps(&dai->caps[i], dai_tpl->caps[i]); ++ if (!dai_tpl->caps[i] || !dai_tpl->caps[i]->name) ++ continue; ++ ret = tplg_add_stream_caps(tplg, dai_tpl->caps[i]); ++ if (ret < 0) ++ return ret; ++ snd_strlcpy(dai->caps[i].name, dai_tpl->caps[i]->name, ++ sizeof(dai->caps[i].name)); + } + + /* flags */ +@@ -1909,22 +1914,299 @@ int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + dai->flags = dai_tpl->flags; + + /* private data */ +- if (dai_tpl->priv != NULL) { +- _dai = realloc(dai, +- elem->size + dai_tpl->priv->size); +- if (!_dai) { +- tplg_elem_free(elem); +- return -ENOMEM; ++ priv = dai_tpl->priv; ++ if (priv && priv->size > 0) { ++ ret = tplg_add_data(tplg, elem, priv, ++ sizeof(*priv) + priv->size); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* decode pcm from the binary input */ ++int tplg_decode_pcm(snd_tplg_t *tplg, ++ size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size) ++{ ++ struct snd_soc_tplg_pcm *pcm; ++ snd_tplg_obj_template_t t; ++ struct snd_tplg_pcm_template *pt; ++ struct snd_tplg_stream_caps_template caps[2], *cap; ++ struct snd_tplg_stream_template *stream; ++ unsigned int i; ++ size_t asize; ++ int err; ++ ++ err = tplg_decode_template(tplg, pos, hdr, &t); ++ if (err < 0) ++ return err; ++ ++ asize = sizeof(*pt) + SND_SOC_TPLG_STREAM_CONFIG_MAX * sizeof(*stream); ++ pt = alloca(asize); ++ ++next: ++ memset(pt, 0, asize); ++ pcm = bin; ++ ++ if (size < sizeof(*pcm)) { ++ SNDERR("pcm: small size %d", size); ++ return -EINVAL; ++ } ++ if (sizeof(*pcm) != pcm->size) { ++ SNDERR("pcm: unknown element size %d (expected %zd)", ++ pcm->size, sizeof(*pcm)); ++ return -EINVAL; ++ } ++ if (pcm->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX) { ++ SNDERR("pcm: wrong number of streams %d", pcm->num_streams); ++ return -EINVAL; ++ } ++ if (sizeof(*pcm) + pcm->priv.size > size) { ++ SNDERR("pcm: wrong private data size %d", pcm->priv.size); ++ return -EINVAL; ++ } ++ ++ tplg_dv(tplg, pos, "pcm: size %d private size %d streams %d", ++ pcm->size, pcm->priv.size, pcm->num_streams); ++ ++ pt->pcm_name = pcm->pcm_name; ++ tplg_dv(tplg, pos, "pcm: pcm_name '%s'", pt->pcm_name); ++ pt->dai_name = pcm->dai_name; ++ tplg_dv(tplg, pos, "pcm: dai_name '%s'", pt->dai_name); ++ pt->pcm_id = pcm->pcm_id; ++ pt->dai_id = pcm->dai_id; ++ tplg_dv(tplg, pos, "pcm: pcm_id %d dai_id %d", pt->pcm_id, pt->dai_id); ++ pt->playback = pcm->playback; ++ pt->capture = pcm->capture; ++ pt->compress = pcm->compress; ++ tplg_dv(tplg, pos, "pcm: playback %d capture %d compress", ++ pt->playback, pt->capture, pt->compress); ++ pt->num_streams = pcm->num_streams; ++ pt->flag_mask = pcm->flag_mask; ++ pt->flags = pcm->flags; ++ for (i = 0; i < pcm->num_streams; i++) { ++ stream = &pt->stream[i]; ++ if (pcm->stream[i].size != sizeof(pcm->stream[0])) { ++ SNDERR("pcm: unknown stream structure size %d", ++ pcm->stream[i].size); ++ return -EINVAL; ++ } ++ stream->name = pcm->stream[i].name; ++ tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, stream[i]), ++ "stream %d: '%s'", i, stream->name); ++ stream->format = pcm->stream[i].format; ++ stream->rate = pcm->stream[i].rate; ++ stream->period_bytes = pcm->stream[i].period_bytes; ++ stream->buffer_bytes = pcm->stream[i].buffer_bytes; ++ stream->channels = pcm->stream[i].channels; ++ } ++ for (i = 0; i < 2; i++) { ++ if (i == 0 && !pcm->playback) ++ continue; ++ if (i == 1 && !pcm->capture) ++ continue; ++ cap = &caps[i]; ++ pt->caps[i] = cap; ++ if (pcm->caps[i].size != sizeof(pcm->caps[0])) { ++ SNDERR("pcm: unknown caps structure size %d", ++ pcm->caps[i].size); ++ return -EINVAL; + } ++ cap->name = pcm->caps[i].name; ++ tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, caps[i]), ++ "caps %d: '%s'", i, cap->name); ++ cap->formats = pcm->caps[i].formats; ++ cap->rates = pcm->caps[i].rates; ++ cap->rate_min = pcm->caps[i].rate_min; ++ cap->rate_max = pcm->caps[i].rate_max; ++ cap->channels_min = pcm->caps[i].channels_min; ++ cap->channels_max = pcm->caps[i].channels_max; ++ cap->periods_min = pcm->caps[i].periods_min; ++ cap->periods_max = pcm->caps[i].periods_max; ++ cap->period_size_min = pcm->caps[i].period_size_min; ++ cap->period_size_max = pcm->caps[i].period_size_max; ++ cap->buffer_size_min = pcm->caps[i].buffer_size_min; ++ cap->buffer_size_max = pcm->caps[i].buffer_size_max; ++ cap->sig_bits = pcm->caps[i].sig_bits; ++ } ++ ++ tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, priv), ++ "pcm: private start"); ++ pt->priv = &pcm->priv; ++ ++ bin += sizeof(*pcm) + pcm->priv.size; ++ size -= sizeof(*pcm) + pcm->priv.size; ++ pos += sizeof(*pcm) + pcm->priv.size; ++ ++ t.pcm = pt; ++ err = snd_tplg_add_object(tplg, &t); ++ if (err < 0) ++ return err; ++ ++ if (size > 0) ++ goto next; ++ ++ return 0; ++} ++ ++/* decode dai from the binary input */ ++int tplg_decode_dai(snd_tplg_t *tplg, ++ size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size) ++{ ++ SNDERR("not implemented"); ++ return -ENXIO; ++} ++ ++/* decode cc from the binary input */ ++int tplg_decode_cc(snd_tplg_t *tplg, ++ size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size) ++{ ++ SNDERR("not implemented"); ++ return -ENXIO; ++} ++ ++/* decode link from the binary input */ ++int tplg_decode_link(snd_tplg_t *tplg, ++ size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size) ++{ ++ struct snd_soc_tplg_link_config *link; ++ snd_tplg_obj_template_t t; ++ struct snd_tplg_link_template lt; ++ struct snd_tplg_stream_template streams[SND_SOC_TPLG_STREAM_CONFIG_MAX]; ++ struct snd_tplg_stream_template *stream; ++ struct snd_tplg_hw_config_template hws[SND_SOC_TPLG_HW_CONFIG_MAX]; ++ struct snd_tplg_hw_config_template *hw; ++ unsigned int i, j; ++ int err; ++ ++ err = tplg_decode_template(tplg, pos, hdr, &t); ++ if (err < 0) ++ return err; ++ ++next: ++ memset(<, 0, sizeof(lt)); ++ memset(streams, 0, sizeof(streams)); ++ memset(hws, 0, sizeof(hws)); ++ link = bin; + +- dai = _dai; +- dai->priv.size = dai_tpl->priv->size; ++ if (size < sizeof(*link)) { ++ SNDERR("link: small size %d", size); ++ return -EINVAL; ++ } ++ if (sizeof(*link) != link->size) { ++ SNDERR("link: unknown element size %d (expected %zd)", ++ link->size, sizeof(*link)); ++ return -EINVAL; ++ } ++ if (link->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX) { ++ SNDERR("link: wrong number of streams %d", link->num_streams); ++ return -EINVAL; ++ } ++ if (link->num_hw_configs > SND_SOC_TPLG_HW_CONFIG_MAX) { ++ SNDERR("link: wrong number of streams %d", link->num_streams); ++ return -EINVAL; ++ } ++ if (sizeof(*link) + link->priv.size > size) { ++ SNDERR("link: wrong private data size %d", link->priv.size); ++ return -EINVAL; ++ } + +- elem->dai = dai; +- elem->size += dai->priv.size; +- memcpy(dai->priv.data, dai_tpl->priv->data, +- dai->priv.size); ++ tplg_dv(tplg, pos, "link: size %d private size %d streams %d " ++ "hw_configs %d", ++ link->size, link->priv.size, link->num_streams, ++ link->num_hw_configs); ++ ++ lt.id = link->id; ++ lt.name = link->name; ++ tplg_dv(tplg, pos, "link: name '%s'", lt.name); ++ lt.stream_name = link->stream_name; ++ tplg_dv(tplg, pos, "link: stream_name '%s'", lt.stream_name); ++ lt.num_streams = link->num_streams; ++ lt.num_hw_configs = link->num_hw_configs; ++ lt.default_hw_config_id = link->default_hw_config_id; ++ lt.flag_mask = link->flag_mask; ++ lt.flags = link->flags; ++ for (i = 0; i < link->num_streams; i++) { ++ stream = &streams[i]; ++ if (link->stream[i].size != sizeof(link->stream[0])) { ++ SNDERR("link: unknown stream structure size %d", ++ link->stream[i].size); ++ return -EINVAL; ++ } ++ stream->name = link->stream[i].name; ++ tplg_dv(tplg, ++ pos + offsetof(struct snd_soc_tplg_link_config, stream[i]), ++ "stream %d: '%s'", i, stream->name); ++ stream->format = link->stream[i].format; ++ stream->rate = link->stream[i].rate; ++ stream->period_bytes = link->stream[i].period_bytes; ++ stream->buffer_bytes = link->stream[i].buffer_bytes; ++ stream->channels = link->stream[i].channels; + } ++ lt.stream = streams; ++ for (i = 0; i < link->num_hw_configs; i++) { ++ hw = &hws[i]; ++ if (link->hw_config[i].size != sizeof(link->hw_config[0])) { ++ SNDERR("link: unknown hw_config structure size %d", ++ link->hw_config[i].size); ++ return -EINVAL; ++ } ++ hw->id = link->hw_config[i].id; ++ hw->fmt = link->hw_config[i].fmt; ++ hw->clock_gated = link->hw_config[i].clock_gated; ++ hw->invert_bclk = link->hw_config[i].invert_bclk; ++ hw->invert_fsync = link->hw_config[i].invert_fsync; ++ hw->bclk_master = link->hw_config[i].bclk_master; ++ hw->fsync_master = link->hw_config[i].fsync_master; ++ hw->mclk_direction = link->hw_config[i].mclk_direction; ++ hw->mclk_rate = link->hw_config[i].mclk_rate; ++ hw->bclk_rate = link->hw_config[i].bclk_rate; ++ hw->fsync_rate = link->hw_config[i].fsync_rate; ++ hw->tdm_slots = link->hw_config[i].tdm_slots; ++ hw->tdm_slot_width = link->hw_config[i].tdm_slot_width; ++ hw->tx_slots = link->hw_config[i].tx_slots; ++ hw->rx_slots = link->hw_config[i].rx_slots; ++ hw->tx_channels = link->hw_config[i].tx_channels; ++ if (hw->tx_channels > SND_SOC_TPLG_MAX_CHAN) { ++ SNDERR("link: wrong tx channels %d", hw->tx_channels); ++ return -EINVAL; ++ } ++ for (j = 0; j < hw->tx_channels; j++) ++ hw->tx_chanmap[j] = link->hw_config[i].tx_chanmap[j]; ++ hw->rx_channels = link->hw_config[i].rx_channels; ++ if (hw->rx_channels > SND_SOC_TPLG_MAX_CHAN) { ++ SNDERR("link: wrong rx channels %d", hw->tx_channels); ++ return -EINVAL; ++ } ++ for (j = 0; j < hw->rx_channels; j++) ++ hw->rx_chanmap[j] = link->hw_config[i].rx_chanmap[j]; ++ } ++ lt.hw_config = hws; ++ ++ tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, priv), ++ "link: private start"); ++ lt.priv = &link->priv; ++ ++ bin += sizeof(*link) + link->priv.size; ++ size -= sizeof(*link) + link->priv.size; ++ pos += sizeof(*link) + link->priv.size; ++ ++ t.link = < ++ err = snd_tplg_add_object(tplg, &t); ++ if (err < 0) ++ return err; ++ ++ if (size > 0) ++ goto next; + + return 0; + } +diff --git a/src/topology/save.c b/src/topology/save.c +index 0498911f67d5..c6eabc49a7d5 100644 +--- a/src/topology/save.c ++++ b/src/topology/save.c +@@ -577,6 +577,9 @@ int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags) + if (err < 0) + goto _err; + ++ if (*dst == NULL) ++ return -EINVAL; ++ + if (flags & SND_TPLG_SAVE_NOCHECK) + return 0; + +diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h +index 74b3a55cada4..22fc5fba48b9 100644 +--- a/src/topology/tplg_local.h ++++ b/src/topology/tplg_local.h +@@ -142,7 +142,8 @@ struct tplg_tuple_set { + }; + + struct tplg_vendor_tuples { +- unsigned int num_sets; ++ unsigned int num_sets; ++ unsigned int alloc_sets; + struct tplg_tuple_set **set; + }; + +@@ -217,11 +218,19 @@ struct tplg_table { + char **dst, const char *prefix); + int (*gsave)(snd_tplg_t *tplg, int index, + char **dst, const char *prefix); ++ int (*decod)(snd_tplg_t *tplg, size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size); + }; + + extern struct tplg_table tplg_table[]; + extern unsigned int tplg_table_items; + ++void *tplg_calloc(struct list_head *heap, size_t size); ++void tplg_free(struct list_head *heap); ++ ++int tplg_get_type(int asoc_type); ++ + int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg, + int (*fcn)(snd_tplg_t *, snd_config_t *, void *), + void *private); +@@ -246,6 +255,7 @@ int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); + int tplg_parse_cc(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); + int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); + ++unsigned int tplg_get_tuple_size(int type); + void tplg_free_tuples(void *obj); + + int tplg_build_data(snd_tplg_t *tplg); +@@ -272,6 +282,8 @@ struct tplg_elem *tplg_elem_lookup(struct list_head *base, + const char* id, + unsigned int type, + int index); ++struct tplg_elem *tplg_elem_type_lookup(snd_tplg_t *tplg, ++ enum snd_tplg_type type); + struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, + snd_config_t *cfg, const char *name, enum snd_tplg_type type); + +@@ -291,6 +303,10 @@ int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base, + const char* id); + ++int tplg_add_data(snd_tplg_t *tplg, struct tplg_elem *parent, ++ const void *bin, size_t size); ++int tplg_add_data_bytes(snd_tplg_t *tplg, struct tplg_elem *parent, ++ const char *suffix, const void *bin, size_t size); + int tplg_add_mixer_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); + int tplg_add_enum_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); + int tplg_add_bytes_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); +@@ -356,3 +372,56 @@ int tplg_save_stream_caps(snd_tplg_t *tplg, struct tplg_elem *elem, + char **dst, const char *pfx); + int tplg_save_dai(snd_tplg_t *tplg, struct tplg_elem *elem, + char **dst, const char *pfx); ++ ++void tplg_dv(snd_tplg_t *tplg, size_t pos, const char *fmt, ...); ++int tplg_decode_template(snd_tplg_t *tplg, ++ size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ snd_tplg_obj_template_t *t); ++int tplg_decode_manifest_data(snd_tplg_t *tplg, size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size); ++int tplg_decode_control_mixer1(snd_tplg_t *tplg, ++ struct list_head *heap, ++ struct snd_tplg_mixer_template *mt, ++ size_t pos, ++ void *bin, size_t size); ++int tplg_decode_control_mixer(snd_tplg_t *tplg, size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size); ++int tplg_decode_control_enum1(snd_tplg_t *tplg, ++ struct list_head *heap, ++ struct snd_tplg_enum_template *et, ++ size_t pos, ++ void *bin, size_t size); ++int tplg_decode_control_enum(snd_tplg_t *tplg, size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size); ++int tplg_decode_control_bytes1(snd_tplg_t *tplg, ++ struct snd_tplg_bytes_template *bt, ++ size_t pos, ++ void *bin, size_t size); ++int tplg_decode_control_bytes(snd_tplg_t *tplg, size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size); ++int tplg_decode_data(snd_tplg_t *tplg, size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size); ++int tplg_decode_dapm_graph(snd_tplg_t *tplg, size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size); ++int tplg_decode_dapm_widget(snd_tplg_t *tplg, size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size); ++int tplg_decode_link(snd_tplg_t *tplg, size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size); ++int tplg_decode_cc(snd_tplg_t *tplg, size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size); ++int tplg_decode_pcm(snd_tplg_t *tplg, size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size); ++int tplg_decode_dai(snd_tplg_t *tplg, size_t pos, ++ struct snd_soc_tplg_hdr *hdr, ++ void *bin, size_t size); +-- +2.16.4 + diff --git a/0054-topology-move-the-elem-list-delete-to-tplg_elem_free.patch b/0054-topology-move-the-elem-list-delete-to-tplg_elem_free.patch new file mode 100644 index 0000000..7c58e09 --- /dev/null +++ b/0054-topology-move-the-elem-list-delete-to-tplg_elem_free.patch @@ -0,0 +1,39 @@ +From cb88813ff71226af5d16f8853d186ff7c572dbe0 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Fri, 3 Jan 2020 22:07:11 +0100 +Subject: [PATCH 54/63] topology: move the elem->list delete to + tplg_elem_free() + +The tplg_elem_free() is called in the error path from many places +and it is expected that the element object will be unregistered +from the tplg structure, too. + +Signed-off-by: Jaroslav Kysela +--- + src/topology/elem.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/topology/elem.c b/src/topology/elem.c +index ed5b5f13054f..cbd7f4b636c1 100644 +--- a/src/topology/elem.c ++++ b/src/topology/elem.c +@@ -301,6 +301,8 @@ struct tplg_elem *tplg_elem_new(void) + + void tplg_elem_free(struct tplg_elem *elem) + { ++ list_del(&elem->list); ++ + tplg_ref_free_list(&elem->ref_list); + + /* free struct snd_tplg_ object, +@@ -323,7 +325,6 @@ void tplg_elem_free_list(struct list_head *base) + + list_for_each_safe(pos, npos, base) { + elem = list_entry(pos, struct tplg_elem, list); +- list_del(&elem->list); + tplg_elem_free(elem); + } + } +-- +2.16.4 + diff --git a/0055-topology-unify-the-log-mechanism.patch b/0055-topology-unify-the-log-mechanism.patch new file mode 100644 index 0000000..155957c --- /dev/null +++ b/0055-topology-unify-the-log-mechanism.patch @@ -0,0 +1,654 @@ +From ae6522e10621839a15f5a439091af7542d84d3e5 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Fri, 3 Jan 2020 22:31:27 +0100 +Subject: [PATCH 55/63] topology: unify the log mechanism + +Signed-off-by: Jaroslav Kysela +--- + src/topology/Makefile.am | 3 ++- + src/topology/builder.c | 54 +++++++++++++++++++++-------------------------- + src/topology/ctl.c | 30 +++++++++++++------------- + src/topology/dapm.c | 19 +++++++++-------- + src/topology/data.c | 26 +++++++++++------------ + src/topology/decoder.c | 32 ++++++++-------------------- + src/topology/log.c | 34 +++++++++++++++++++++++++++++ + src/topology/pcm.c | 48 ++++++++++++++++++++--------------------- + src/topology/tplg_local.h | 8 ++++++- + 9 files changed, 138 insertions(+), 116 deletions(-) + create mode 100644 src/topology/log.c + +diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am +index 12d1d4455883..9f48891f5c28 100644 +--- a/src/topology/Makefile.am ++++ b/src/topology/Makefile.am +@@ -29,7 +29,8 @@ libatopology_la_SOURCES =\ + ops.c \ + elem.c \ + save.c \ +- decoder.c ++ decoder.c \ ++ log.c + + noinst_HEADERS = tplg_local.h + +diff --git a/src/topology/builder.c b/src/topology/builder.c +index 74c44405e653..7af5de084d84 100644 +--- a/src/topology/builder.c ++++ b/src/topology/builder.c +@@ -20,20 +20,6 @@ + #include "list.h" + #include "tplg_local.h" + +-/* verbose output detailing each object size and file position */ +-static void verbose(snd_tplg_t *tplg, const char *fmt, ...) +-{ +- va_list va; +- +- if (!tplg->verbose) +- return; +- +- va_start(va, fmt); +- fprintf(stdout, "0x%6.6zx/%6.6zd - ", tplg->bin_pos, tplg->bin_pos); +- vfprintf(stdout, fmt, va); +- va_end(va); +-} +- + /* write a block, track the position */ + static ssize_t twrite(snd_tplg_t *tplg, void *data, size_t data_size) + { +@@ -73,10 +59,11 @@ static ssize_t write_block_header(snd_tplg_t *tplg, unsigned int type, + return -EINVAL; + } + +- verbose(tplg, "header index %d type %d count %d size 0x%lx/%ld vendor %d " +- "version %d\n", index, type, count, +- (long unsigned int)payload_size, (long int)payload_size, +- vendor_type, version); ++ tplg_log(tplg, 'B', tplg->bin_pos, ++ "header index %d type %d count %d size 0x%lx/%ld vendor %d " ++ "version %d", index, type, count, ++ (long unsigned int)payload_size, (long int)payload_size, ++ vendor_type, version); + + tplg->next_hdr_pos += hdr.payload_size + sizeof(hdr); + +@@ -121,13 +108,15 @@ static int write_elem_block(snd_tplg_t *tplg, + continue; + + if (elem->type != SND_TPLG_TYPE_DAPM_GRAPH) +- verbose(tplg, "%s '%s': write %d bytes\n", +- obj_name, elem->id, elem->size); ++ tplg_log(tplg, 'B', tplg->bin_pos, ++ "%s '%s': write %d bytes", ++ obj_name, elem->id, elem->size); + else +- verbose(tplg, "%s '%s -> %s -> %s': write %d bytes\n", +- obj_name, elem->route->source, +- elem->route->control, +- elem->route->sink, elem->size); ++ tplg_log(tplg, 'B', tplg->bin_pos, ++ "%s '%s -> %s -> %s': write %d bytes", ++ obj_name, elem->route->source, ++ elem->route->control, ++ elem->route->sink, elem->size); + + wsize = twrite(tplg, elem->obj, elem->size); + if (wsize < 0) +@@ -225,10 +214,13 @@ static ssize_t write_manifest_data(snd_tplg_t *tplg) + return ret; + } + +- verbose(tplg, "manifest: write %d bytes\n", sizeof(tplg->manifest)); ++ tplg_log(tplg, 'B', tplg->bin_pos, "manifest: write %d bytes", ++ sizeof(tplg->manifest)); + ret = twrite(tplg, &tplg->manifest, sizeof(tplg->manifest)); + if (ret >= 0) { +- verbose(tplg, "manifest: write %d priv bytes\n", tplg->manifest.priv.size); ++ tplg_log(tplg, 'B', tplg->bin_pos, ++ "manifest: write %d priv bytes", ++ tplg->manifest.priv.size); + ret = twrite(tplg, tplg->manifest_pdata, tplg->manifest.priv.size); + } + return ret; +@@ -280,9 +272,10 @@ int tplg_write_data(snd_tplg_t *tplg) + size = calc_block_size(list); + if (size == 0) + continue; +- verbose(tplg, "block size for type %s (%d:%d) is 0x%zx/%zd\n", +- tptr->name, tptr->type, +- tptr->tsoc, size, size); ++ tplg_log(tplg, 'B', tplg->bin_pos, ++ "block size for type %s (%d:%d) is 0x%zx/%zd", ++ tptr->name, tptr->type, ++ tptr->tsoc, size, size); + ret = write_elem_block(tplg, list, size, + tptr->tsoc, tptr->name); + if (ret < 0) { +@@ -292,7 +285,8 @@ int tplg_write_data(snd_tplg_t *tplg) + } + } + +- verbose(tplg, "total size is 0x%zx/%zd\n", tplg->bin_pos, tplg->bin_pos); ++ tplg_log(tplg, 'B', tplg->bin_pos, "total size is 0x%zx/%zd", ++ tplg->bin_pos, tplg->bin_pos); + + if (total_size != tplg->bin_pos) { + SNDERR("total size mismatch (%zd != %zd)", +diff --git a/src/topology/ctl.c b/src/topology/ctl.c +index 24d437aa4a2e..a5a81148d1eb 100644 +--- a/src/topology/ctl.c ++++ b/src/topology/ctl.c +@@ -1216,8 +1216,8 @@ int tplg_decode_control_mixer1(snd_tplg_t *tplg, + return -EINVAL; + } + +- tplg_dv(tplg, pos, "mixer: size %d TLV size %d private size %d", +- mc->size, mc->hdr.tlv.size, mc->priv.size); ++ tplg_log(tplg, 'D', pos, "mixer: size %d TLV size %d private size %d", ++ mc->size, mc->hdr.tlv.size, mc->priv.size); + if (size != mc->size + mc->priv.size) { + SNDERR("mixer: unexpected element size %d", size); + return -EINVAL; +@@ -1233,7 +1233,7 @@ int tplg_decode_control_mixer1(snd_tplg_t *tplg, + mt->min = mc->min; + mt->max = mc->max; + mt->platform_max = mc->platform_max; +- tplg_dv(tplg, pos, "mixer: name '%s' access 0x%x", ++ tplg_log(tplg, 'D', pos, "mixer: name '%s' access 0x%x", + mt->hdr.name, mt->hdr.access); + if (mc->num_channels > 0) { + map = tplg_calloc(heap, sizeof(*map)); +@@ -1261,16 +1261,16 @@ int tplg_decode_control_mixer1(snd_tplg_t *tplg, + db->min = mc->hdr.tlv.scale.min; + db->step = mc->hdr.tlv.scale.step; + db->mute = mc->hdr.tlv.scale.mute; +- tplg_dv(tplg, pos, "mixer: dB scale TLV: min %d step %d mute %d", +- db->min, db->step, db->mute); ++ tplg_log(tplg, 'D', pos, "mixer: dB scale TLV: min %d step %d mute %d", ++ db->min, db->step, db->mute); + } else { + SNDERR("mixer: wrong TLV size %d", mc->hdr.tlv.size); + return -EINVAL; + } + + mt->priv = &mc->priv; +- tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_mixer_control, priv), +- "mixer: private start"); ++ tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_mixer_control, priv), ++ "mixer: private start"); + return 0; + } + +@@ -1338,8 +1338,8 @@ int tplg_decode_control_enum1(snd_tplg_t *tplg, + return -EINVAL; + } + +- tplg_dv(tplg, pos, "enum: size %d private size %d", +- ec->size, ec->priv.size); ++ tplg_log(tplg, 'D', pos, "enum: size %d private size %d", ++ ec->size, ec->priv.size); + if (size != ec->size + ec->priv.size) { + SNDERR("enum: unexpected element size %d", size); + return -EINVAL; +@@ -1380,8 +1380,8 @@ int tplg_decode_control_enum1(snd_tplg_t *tplg, + cmt.num_channels = ec->num_channels; + for (i = 0; i < cmt.num_channels; i++) { + struct snd_tplg_channel_elem *channel = &cmt.channel[i]; +- tplg_dv(tplg, pos + ((void *)&ec->channel[i] - (void *)ec), +- "enum: channel size %d", ec->channel[i].size); ++ tplg_log(tplg, 'D', pos + ((void *)&ec->channel[i] - (void *)ec), ++ "enum: channel size %d", ec->channel[i].size); + channel->reg = ec->channel[i].reg; + channel->shift = ec->channel[i].shift; + channel->id = ec->channel[i].id; +@@ -1452,8 +1452,8 @@ int tplg_decode_control_bytes1(snd_tplg_t *tplg, + return -EINVAL; + } + +- tplg_dv(tplg, pos, "control bytes: size %d private size %d", +- bc->size, bc->priv.size); ++ tplg_log(tplg, 'D', pos, "control bytes: size %d private size %d", ++ bc->size, bc->priv.size); + if (size != bc->size + bc->priv.size) { + SNDERR("bytes: unexpected element size %d", size); + return -EINVAL; +@@ -1473,8 +1473,8 @@ int tplg_decode_control_bytes1(snd_tplg_t *tplg, + bt->ext_ops.get = bc->ext_ops.get; + bt->ext_ops.put = bc->ext_ops.put; + bt->ext_ops.info = bc->ext_ops.info; +- tplg_dv(tplg, pos, "control bytes: name '%s' access 0x%x", +- bt->hdr.name, bt->hdr.access); ++ tplg_log(tplg, 'D', pos, "control bytes: name '%s' access 0x%x", ++ bt->hdr.name, bt->hdr.access); + + bt->priv = &bc->priv; + return 0; +diff --git a/src/topology/dapm.c b/src/topology/dapm.c +index 9fab2d92c150..0bf64833c11f 100644 +--- a/src/topology/dapm.c ++++ b/src/topology/dapm.c +@@ -895,8 +895,8 @@ next: + return -EINVAL; + } + +- tplg_dv(tplg, pos, "dapm widget: size %d private size %d kcontrols %d", +- w->size, w->priv.size, w->num_kcontrols); ++ tplg_log(tplg, 'D', pos, "dapm widget: size %d private size %d kcontrols %d", ++ w->size, w->priv.size, w->num_kcontrols); + + wt = tplg_calloc(&heap, sizeof(*wt) + sizeof(void *) * w->num_kcontrols); + if (wt == NULL) +@@ -913,7 +913,8 @@ next: + wt->event_flags = w->event_flags; + wt->event_type = w->event_type; + +- tplg_dv(tplg, pos, "dapm widget: name '%s' sname '%s'", wt->name, wt->sname); ++ tplg_log(tplg, 'D', pos, "dapm widget: name '%s' sname '%s'", ++ wt->name, wt->sname); + + if (sizeof(*w) + w->priv.size > size) { + SNDERR("dapm widget: wrong private data size %d", +@@ -921,8 +922,8 @@ next: + return -EINVAL; + } + +- tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_dapm_widget, priv), +- "dapm widget: private start"); ++ tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_dapm_widget, priv), ++ "dapm widget: private start"); + + wt->priv = &w->priv; + bin += sizeof(*w) + w->priv.size; +@@ -942,7 +943,7 @@ next: + wt->num_ctls++; + mc = bin; + size2 = mc->size + mc->priv.size; +- tplg_dv(tplg, pos, "kcontrol mixer size %zd", size2); ++ tplg_log(tplg, 'D', pos, "kcontrol mixer size %zd", size2); + if (size2 > size) { + SNDERR("dapm widget: small mixer size %d", + size2); +@@ -962,7 +963,7 @@ next: + wt->num_ctls++; + ec = bin; + size2 = ec->size + ec->priv.size; +- tplg_dv(tplg, pos, "kcontrol enum size %zd", size2); ++ tplg_log(tplg, 'D', pos, "kcontrol enum size %zd", size2); + if (size2 > size) { + SNDERR("dapm widget: small enum size %d", + size2); +@@ -982,7 +983,7 @@ next: + wt->num_ctls++; + bc = bin; + size2 = bc->size + bc->priv.size; +- tplg_dv(tplg, pos, "kcontrol bytes size %zd", size2); ++ tplg_log(tplg, 'D', pos, "kcontrol bytes size %zd", size2); + if (size2 > size) { + SNDERR("dapm widget: small bytes size %d", + size2); +@@ -1049,7 +1050,7 @@ int tplg_decode_dapm_graph(snd_tplg_t *tplg, + ge->ctl = g->control; + ge->sink = g->sink; + gt->count++; +- tplg_dv(tplg, pos, "dapm graph: src='%s' ctl='%s' sink='%s'", ++ tplg_log(tplg, 'D', pos, "dapm graph: src='%s' ctl='%s' sink='%s'", + ge->src, ge->ctl, ge->sink); + bin += sizeof(*g); + size -= sizeof(*g); +diff --git a/src/topology/data.c b/src/topology/data.c +index 64563920a4e2..0b513428da80 100644 +--- a/src/topology/data.c ++++ b/src/topology/data.c +@@ -1604,7 +1604,7 @@ int tplg_decode_manifest_data(snd_tplg_t *tplg, + if (!elem) + return -ENOMEM; + +- tplg_dv(tplg, pos, "manifest: private size %d", size); ++ tplg_log(tplg, 'D', pos, "manifest: private size %d", size); + return tplg_add_data(tplg, elem, bin, size); + } + +@@ -1664,7 +1664,7 @@ static int tplg_verify_tuple_set(snd_tplg_t *tplg, size_t pos, + + va = bin; + if (size < sizeof(*va) || size < va->size) { +- tplg_dv(tplg, pos, "tuple set verify: wrong size %d", size); ++ tplg_log(tplg, 'A', pos, "tuple set verify: wrong size %d", size); + return -EINVAL; + } + +@@ -1677,20 +1677,20 @@ static int tplg_verify_tuple_set(snd_tplg_t *tplg, size_t pos, + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + break; + default: +- tplg_dv(tplg, pos, "tuple set verify: unknown array type %d", va->type); ++ tplg_log(tplg, 'A', pos, "tuple set verify: unknown array type %d", va->type); + return -EINVAL; + } + + j = tplg_get_tuple_size(va->type) * va->num_elems; + if (j + sizeof(*va) != va->size) { +- tplg_dv(tplg, pos, "tuple set verify: wrong vendor array size %d " +- "(expected %d for %d count %d)", +- va->size, j + sizeof(*va), va->type, va->num_elems); ++ tplg_log(tplg, 'A', pos, "tuple set verify: wrong vendor array size %d " ++ "(expected %d for %d count %d)", ++ va->size, j + sizeof(*va), va->type, va->num_elems); + return -EINVAL; + } + + if (va->num_elems > 4096) { +- tplg_dv(tplg, pos, "tuple set verify: tuples overflow %d", va->num_elems); ++ tplg_log(tplg, 'A', pos, "tuple set verify: tuples overflow %d", va->num_elems); + return -EINVAL; + } + +@@ -1748,8 +1748,8 @@ static int tplg_decode_tuple_set(snd_tplg_t *tplg, + set->type = va->type; + set->num_tuples = va->num_elems; + +- tplg_dv(tplg, pos, "tuple set: type %d (%s) tuples %d size %d", set->type, +- get_tuple_type_name(set->type), set->num_tuples, va->size); ++ tplg_log(tplg, 'A', pos, "tuple set: type %d (%s) tuples %d size %d", set->type, ++ get_tuple_type_name(set->type), set->num_tuples, va->size); + for (j = 0; j < set->num_tuples; j++) { + tuple = &set->tuple[j]; + switch (va->type) { +@@ -1798,14 +1798,14 @@ static int tplg_verify_tuples(snd_tplg_t *tplg, size_t pos, + int err; + + if (size < sizeof(*va)) { +- tplg_dv(tplg, pos, "tuples: small size %d", size); ++ tplg_log(tplg, 'A', pos, "tuples: small size %d", size); + return -EINVAL; + } + + next: + va = bin; + if (size < sizeof(*va)) { +- tplg_dv(tplg, pos, "tuples: unexpected vendor arry size %d", size); ++ tplg_log(tplg, 'A', pos, "tuples: unexpected vendor arry size %d", size); + return -EINVAL; + } + +@@ -1903,7 +1903,7 @@ next: + return err; + tuples = NULL; + } +- tplg_dv(tplg, pos, "add bytes: size %d", tp->size); ++ tplg_log(tplg, 'A', pos, "add bytes: size %d", tp->size); + snprintf(suffix, sizeof(suffix), "data%u", block++); + err = tplg_add_data_bytes(tplg, parent, suffix, tp->array, tp->size); + } else { +@@ -1929,7 +1929,7 @@ next: + return -ENOMEM; + } + } +- tplg_dv(tplg, pos, "decode tuples: size %d", tp->size); ++ tplg_log(tplg, 'A', pos, "decode tuples: size %d", tp->size); + err = tplg_decode_tuples(tplg, pos, parent, tuples, tp->array, tp->size); + num_tuples++; + } +diff --git a/src/topology/decoder.c b/src/topology/decoder.c +index 2d6a8969756a..66ebe5d8a668 100644 +--- a/src/topology/decoder.c ++++ b/src/topology/decoder.c +@@ -18,21 +18,6 @@ + #include "list.h" + #include "tplg_local.h" + +-/* verbose output detailing each object size and file position */ +-void tplg_dv(snd_tplg_t *tplg, size_t pos, const char *fmt, ...) +-{ +- va_list va; +- +- if (!tplg->verbose) +- return; +- +- va_start(va, fmt); +- fprintf(stdout, "D0x%6.6zx/%6.6zd - ", pos, pos); +- vfprintf(stdout, fmt, va); +- va_end(va); +- putc('\n', stdout); +-} +- + int tplg_decode_template(snd_tplg_t *tplg, + size_t pos, + struct snd_soc_tplg_hdr *hdr, +@@ -41,7 +26,8 @@ int tplg_decode_template(snd_tplg_t *tplg, + int type; + + type = tplg_get_type(hdr->type); +- tplg_dv(tplg, pos, "template: asoc type %d library type %d", hdr->type, type); ++ tplg_log(tplg, 'D', pos, "template: asoc type %d library type %d", ++ hdr->type, type); + if (type < 0) + return type; + +@@ -50,8 +36,8 @@ int tplg_decode_template(snd_tplg_t *tplg, + t->index = hdr->index; + t->version = hdr->version; + t->vendor_type = hdr->vendor_type; +- tplg_dv(tplg, pos, "template: index %d version %d vendor_type %d", +- hdr->index, hdr->version, hdr->vendor_type); ++ tplg_log(tplg, 'D', pos, "template: index %d version %d vendor_type %d", ++ hdr->index, hdr->version, hdr->vendor_type); + return 0; + } + +@@ -71,11 +57,11 @@ int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags) + while (1) { + pos = b - bin; + if (size == pos) { +- tplg_dv(tplg, pos, "block: success (total %zd)", size); ++ tplg_log(tplg, 'D', pos, "block: success (total %zd)", size); + return 0; + } + if (size - pos < sizeof(*hdr)) { +- tplg_dv(tplg, pos, "block: small size"); ++ tplg_log(tplg, 'D', pos, "block: small size"); + SNDERR("incomplete header data to decode"); + return -EINVAL; + } +@@ -85,8 +71,8 @@ int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags) + return -EINVAL; + } + +- tplg_dv(tplg, pos, "block: abi %d size %d payload size %d", +- hdr->abi, hdr->size, hdr->payload_size); ++ tplg_log(tplg, 'D', pos, "block: abi %d size %d payload size %d", ++ hdr->abi, hdr->size, hdr->payload_size); + if (hdr->abi != SND_SOC_TPLG_ABI_VERSION) { + SNDERR("unsupported ABI version %d", hdr->abi); + return -EINVAL; +@@ -127,7 +113,7 @@ int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags) + SNDERR("unknown block type %d", hdr->type); + return -EINVAL; + } +- tplg_dv(tplg, pos, "block: type %d - %s", hdr->type, tptr->name); ++ tplg_log(tplg, 'D', pos, "block: type %d - %s", hdr->type, tptr->name); + err = tptr->decod(tplg, pos, hdr, b + hdr->size, hdr->payload_size); + if (err < 0) + return err; +diff --git a/src/topology/log.c b/src/topology/log.c +new file mode 100644 +index 000000000000..1ca365287c22 +--- /dev/null ++++ b/src/topology/log.c +@@ -0,0 +1,34 @@ ++/* ++ Copyright (c) 2019 Red Hat Inc. ++ All rights reserved. ++ ++ 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.1 of ++ the License, or (at your option) any later version. ++ ++ 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 Lesser General Public License for more details. ++ ++ Authors: Jaroslav Kysela ++*/ ++ ++#include "list.h" ++#include "tplg_local.h" ++ ++/* verbose output detailing each object size and file position */ ++void tplg_log_(snd_tplg_t *tplg, char type, size_t pos, const char *fmt, ...) ++{ ++ va_list va; ++ ++ if (!tplg->verbose) ++ return; ++ ++ va_start(va, fmt); ++ fprintf(stdout, "%c0x%6.6zx/%6.6zd - ", type, pos, pos); ++ vfprintf(stdout, fmt, va); ++ va_end(va); ++ putc('\n', stdout); ++} +diff --git a/src/topology/pcm.c b/src/topology/pcm.c +index 4e04a6bc3d78..918e3e9a957f 100644 +--- a/src/topology/pcm.c ++++ b/src/topology/pcm.c +@@ -1969,21 +1969,21 @@ next: + return -EINVAL; + } + +- tplg_dv(tplg, pos, "pcm: size %d private size %d streams %d", +- pcm->size, pcm->priv.size, pcm->num_streams); ++ tplg_log(tplg, 'D', pos, "pcm: size %d private size %d streams %d", ++ pcm->size, pcm->priv.size, pcm->num_streams); + + pt->pcm_name = pcm->pcm_name; +- tplg_dv(tplg, pos, "pcm: pcm_name '%s'", pt->pcm_name); ++ tplg_log(tplg, 'D', pos, "pcm: pcm_name '%s'", pt->pcm_name); + pt->dai_name = pcm->dai_name; +- tplg_dv(tplg, pos, "pcm: dai_name '%s'", pt->dai_name); ++ tplg_log(tplg, 'D', pos, "pcm: dai_name '%s'", pt->dai_name); + pt->pcm_id = pcm->pcm_id; + pt->dai_id = pcm->dai_id; +- tplg_dv(tplg, pos, "pcm: pcm_id %d dai_id %d", pt->pcm_id, pt->dai_id); ++ tplg_log(tplg, 'D', pos, "pcm: pcm_id %d dai_id %d", pt->pcm_id, pt->dai_id); + pt->playback = pcm->playback; + pt->capture = pcm->capture; + pt->compress = pcm->compress; +- tplg_dv(tplg, pos, "pcm: playback %d capture %d compress", +- pt->playback, pt->capture, pt->compress); ++ tplg_log(tplg, 'D', pos, "pcm: playback %d capture %d compress", ++ pt->playback, pt->capture, pt->compress); + pt->num_streams = pcm->num_streams; + pt->flag_mask = pcm->flag_mask; + pt->flags = pcm->flags; +@@ -1995,8 +1995,8 @@ next: + return -EINVAL; + } + stream->name = pcm->stream[i].name; +- tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, stream[i]), +- "stream %d: '%s'", i, stream->name); ++ tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, stream[i]), ++ "stream %d: '%s'", i, stream->name); + stream->format = pcm->stream[i].format; + stream->rate = pcm->stream[i].rate; + stream->period_bytes = pcm->stream[i].period_bytes; +@@ -2016,8 +2016,8 @@ next: + return -EINVAL; + } + cap->name = pcm->caps[i].name; +- tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, caps[i]), +- "caps %d: '%s'", i, cap->name); ++ tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, caps[i]), ++ "caps %d: '%s'", i, cap->name); + cap->formats = pcm->caps[i].formats; + cap->rates = pcm->caps[i].rates; + cap->rate_min = pcm->caps[i].rate_min; +@@ -2033,8 +2033,8 @@ next: + cap->sig_bits = pcm->caps[i].sig_bits; + } + +- tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, priv), +- "pcm: private start"); ++ tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, priv), ++ "pcm: private start"); + pt->priv = &pcm->priv; + + bin += sizeof(*pcm) + pcm->priv.size; +@@ -2120,16 +2120,16 @@ next: + return -EINVAL; + } + +- tplg_dv(tplg, pos, "link: size %d private size %d streams %d " +- "hw_configs %d", +- link->size, link->priv.size, link->num_streams, +- link->num_hw_configs); ++ tplg_log(tplg, 'D', pos, "link: size %d private size %d streams %d " ++ "hw_configs %d", ++ link->size, link->priv.size, link->num_streams, ++ link->num_hw_configs); + + lt.id = link->id; + lt.name = link->name; +- tplg_dv(tplg, pos, "link: name '%s'", lt.name); ++ tplg_log(tplg, 'D', pos, "link: name '%s'", lt.name); + lt.stream_name = link->stream_name; +- tplg_dv(tplg, pos, "link: stream_name '%s'", lt.stream_name); ++ tplg_log(tplg, 'D', pos, "link: stream_name '%s'", lt.stream_name); + lt.num_streams = link->num_streams; + lt.num_hw_configs = link->num_hw_configs; + lt.default_hw_config_id = link->default_hw_config_id; +@@ -2143,9 +2143,9 @@ next: + return -EINVAL; + } + stream->name = link->stream[i].name; +- tplg_dv(tplg, +- pos + offsetof(struct snd_soc_tplg_link_config, stream[i]), +- "stream %d: '%s'", i, stream->name); ++ tplg_log(tplg, 'D', ++ pos + offsetof(struct snd_soc_tplg_link_config, stream[i]), ++ "stream %d: '%s'", i, stream->name); + stream->format = link->stream[i].format; + stream->rate = link->stream[i].rate; + stream->period_bytes = link->stream[i].period_bytes; +@@ -2192,8 +2192,8 @@ next: + } + lt.hw_config = hws; + +- tplg_dv(tplg, pos + offsetof(struct snd_soc_tplg_pcm, priv), +- "link: private start"); ++ tplg_log(tplg, 'D', pos + offsetof(struct snd_soc_tplg_pcm, priv), ++ "link: private start"); + lt.priv = &link->priv; + + bin += sizeof(*link) + link->priv.size; +diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h +index 22fc5fba48b9..e061db755ed2 100644 +--- a/src/topology/tplg_local.h ++++ b/src/topology/tplg_local.h +@@ -226,6 +226,13 @@ struct tplg_table { + extern struct tplg_table tplg_table[]; + extern unsigned int tplg_table_items; + ++#define tplg_log(tplg, type, pos, fmt, args...) do { \ ++ if ((tplg)->verbose) \ ++ tplg_log_((tplg), (type), (pos), (fmt), ##args); \ ++} while (0) ++ ++void tplg_log_(snd_tplg_t *tplg, char type, size_t pos, const char *fmt, ...); ++ + void *tplg_calloc(struct list_head *heap, size_t size); + void tplg_free(struct list_head *heap); + +@@ -373,7 +380,6 @@ int tplg_save_stream_caps(snd_tplg_t *tplg, struct tplg_elem *elem, + int tplg_save_dai(snd_tplg_t *tplg, struct tplg_elem *elem, + char **dst, const char *pfx); + +-void tplg_dv(snd_tplg_t *tplg, size_t pos, const char *fmt, ...); + int tplg_decode_template(snd_tplg_t *tplg, + size_t pos, + struct snd_soc_tplg_hdr *hdr, +-- +2.16.4 + diff --git a/0056-topology-tplg_dbg-cleanups.patch b/0056-topology-tplg_dbg-cleanups.patch new file mode 100644 index 0000000..ffcad02 --- /dev/null +++ b/0056-topology-tplg_dbg-cleanups.patch @@ -0,0 +1,784 @@ +From d768da27e7f9f56328c61ed5d37c17ce075cbc6e Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Fri, 3 Jan 2020 22:44:15 +0100 +Subject: [PATCH 56/63] topology: tplg_dbg() cleanups + +- remove newline at the end + +Signed-off-by: Jaroslav Kysela +--- + src/topology/channel.c | 6 +++--- + src/topology/ctl.c | 40 ++++++++++++++++++++-------------------- + src/topology/dapm.c | 32 ++++++++++++++++---------------- + src/topology/data.c | 48 +++++++++++++++++++++++++++--------------------- + src/topology/ops.c | 8 ++++---- + src/topology/pcm.c | 36 ++++++++++++++++++------------------ + src/topology/text.c | 6 +++--- + 7 files changed, 91 insertions(+), 85 deletions(-) + +diff --git a/src/topology/channel.c b/src/topology/channel.c +index 390c3f164e97..47d5ea4c419a 100644 +--- a/src/topology/channel.c ++++ b/src/topology/channel.c +@@ -100,7 +100,7 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg, + + channel += tplg->channel_idx; + snd_config_get_id(cfg, &id); +- tplg_dbg("\tChannel %s at index %d\n", id, tplg->channel_idx); ++ tplg_dbg("\tChannel %s at index %d", id, tplg->channel_idx); + + channel_id = lookup_channel(id); + if (channel_id < 0) { +@@ -110,7 +110,7 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg, + + channel->id = channel_id; + channel->size = sizeof(*channel); +- tplg_dbg("\tChan %s = %d\n", id, channel->id); ++ tplg_dbg("\tChan %s = %d", id, channel->id); + + snd_config_for_each(i, next, cfg) { + +@@ -129,7 +129,7 @@ int tplg_parse_channel(snd_tplg_t *tplg, snd_config_t *cfg, + else if (strcmp(id, "shift") == 0) + channel->shift = value; + +- tplg_dbg("\t\t%s = %d\n", id, value); ++ tplg_dbg("\t\t%s = %d", id, value); + } + + tplg->channel_idx++; +diff --git a/src/topology/ctl.c b/src/topology/ctl.c +index a5a81148d1eb..41dc2ddbb826 100644 +--- a/src/topology/ctl.c ++++ b/src/topology/ctl.c +@@ -54,7 +54,7 @@ static int parse_access_values(snd_config_t *cfg, + const char *value = NULL; + unsigned int j; + +- tplg_dbg(" Access:\n"); ++ tplg_dbg(" Access:"); + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); +@@ -67,7 +67,7 @@ static int parse_access_values(snd_config_t *cfg, + for (j = 0; j < ARRAY_SIZE(ctl_access); j++) { + if (strcmp(value, ctl_access[j].name) == 0) { + hdr->access |= ctl_access[j].value; +- tplg_dbg("\t%s\n", value); ++ tplg_dbg("\t%s", value); + break; + } + } +@@ -150,7 +150,7 @@ static int copy_tlv(struct tplg_elem *elem, struct tplg_elem *ref) + struct snd_soc_tplg_mixer_control *mixer_ctrl = elem->mixer_ctrl; + struct snd_soc_tplg_ctl_tlv *tlv = ref->tlv; + +- tplg_dbg("TLV '%s' used by '%s\n", ref->id, elem->id); ++ tplg_dbg("TLV '%s' used by '%s", ref->id, elem->id); + + /* TLV has a fixed size */ + mixer_ctrl->hdr.tlv = *tlv; +@@ -330,7 +330,7 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem) + const char *id = NULL; + int val; + +- tplg_dbg(" scale: %s\n", elem->id); ++ tplg_dbg(" scale: %s", elem->id); + + tplg_tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv); + tplg_tlv->type = SNDRV_CTL_TLVT_DB_SCALE; +@@ -348,7 +348,7 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem) + if (tplg_get_integer(n, &val, 0)) + continue; + +- tplg_dbg("\t%s = %i\n", id, val); ++ tplg_dbg("\t%s = %i", id, val); + + /* get TLV data */ + if (strcmp(id, "min") == 0) +@@ -450,7 +450,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg, + snd_strlcpy(be->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + be->hdr.type = SND_SOC_TPLG_TYPE_BYTES; + +- tplg_dbg(" Control Bytes: %s\n", elem->id); ++ tplg_dbg(" Control Bytes: %s", elem->id); + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); +@@ -468,7 +468,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg, + return -EINVAL; + + be->base = ival; +- tplg_dbg("\t%s: %d\n", id, be->base); ++ tplg_dbg("\t%s: %d", id, be->base); + continue; + } + +@@ -477,7 +477,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg, + return -EINVAL; + + be->num_regs = ival; +- tplg_dbg("\t%s: %d\n", id, be->num_regs); ++ tplg_dbg("\t%s: %d", id, be->num_regs); + continue; + } + +@@ -486,7 +486,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg, + return -EINVAL; + + be->max = ival; +- tplg_dbg("\t%s: %d\n", id, be->max); ++ tplg_dbg("\t%s: %d", id, be->max); + continue; + } + +@@ -495,7 +495,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg, + return -EINVAL; + + be->mask = ival; +- tplg_dbg("\t%s: %d\n", id, be->mask); ++ tplg_dbg("\t%s: %d", id, be->mask); + continue; + } + +@@ -515,7 +515,7 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg, + return err; + + tlv_set = true; +- tplg_dbg("\t%s: %s\n", id, val); ++ tplg_dbg("\t%s: %s", id, val); + continue; + } + +@@ -625,7 +625,7 @@ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg, + ec->channel[j].reg = -1; + } + +- tplg_dbg(" Control Enum: %s\n", elem->id); ++ tplg_dbg(" Control Enum: %s", elem->id); + + snd_config_for_each(i, next, cfg) { + +@@ -644,7 +644,7 @@ int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg, + return -EINVAL; + + tplg_ref_add(elem, SND_TPLG_TYPE_TEXT, val); +- tplg_dbg("\t%s: %s\n", id, val); ++ tplg_dbg("\t%s: %s", id, val); + continue; + } + +@@ -761,7 +761,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg, + for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++) + mc->channel[j].reg = -1; + +- tplg_dbg(" Control Mixer: %s\n", elem->id); ++ tplg_dbg(" Control Mixer: %s", elem->id); + + /* giterate trough each mixer elment */ + snd_config_for_each(i, next, cfg) { +@@ -795,7 +795,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg, + return -EINVAL; + + mc->max = ival; +- tplg_dbg("\t%s: %d\n", id, mc->max); ++ tplg_dbg("\t%s: %d", id, mc->max); + continue; + } + +@@ -805,7 +805,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg, + return -EINVAL; + mc->invert = ival; + +- tplg_dbg("\t%s: %d\n", id, mc->invert); ++ tplg_dbg("\t%s: %d", id, mc->invert); + continue; + } + +@@ -826,7 +826,7 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg, + return err; + + tlv_set = true; +- tplg_dbg("\t%s: %s\n", id, val); ++ tplg_dbg("\t%s: %s", id, val); + continue; + } + +@@ -981,7 +981,7 @@ int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer, + struct tplg_elem *elem; + int ret, i, num_channels; + +- tplg_dbg(" Control Mixer: %s\n", mixer->hdr.name); ++ tplg_dbg(" Control Mixer: %s", mixer->hdr.name); + + if (mixer->hdr.type != SND_SOC_TPLG_TYPE_MIXER) { + SNDERR("invalid mixer type %d", mixer->hdr.type); +@@ -1045,7 +1045,7 @@ int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl, + struct tplg_elem *elem; + int ret, i, num_items, num_channels; + +- tplg_dbg(" Control Enum: %s\n", enum_ctl->hdr.name); ++ tplg_dbg(" Control Enum: %s", enum_ctl->hdr.name); + + if (enum_ctl->hdr.type != SND_SOC_TPLG_TYPE_ENUM) { + SNDERR("invalid enum type %d", enum_ctl->hdr.type); +@@ -1128,7 +1128,7 @@ int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl, + struct tplg_elem *elem; + int ret; + +- tplg_dbg(" Control Bytes: %s\n", bytes_ctl->hdr.name); ++ tplg_dbg(" Control Bytes: %s", bytes_ctl->hdr.name); + + if (bytes_ctl->hdr.type != SND_SOC_TPLG_TYPE_BYTES) { + SNDERR("invalid bytes type %d", bytes_ctl->hdr.type); +diff --git a/src/topology/dapm.c b/src/topology/dapm.c +index 0bf64833c11f..cb85e66734f6 100644 +--- a/src/topology/dapm.c ++++ b/src/topology/dapm.c +@@ -76,8 +76,8 @@ static int copy_dapm_control(struct tplg_elem *elem, struct tplg_elem *ref) + { + struct snd_soc_tplg_dapm_widget *widget = elem->widget; + +- tplg_dbg("Control '%s' used by '%s'\n", ref->id, elem->id); +- tplg_dbg("\tparent size: %d + %d -> %d, priv size -> %d\n", ++ tplg_dbg("Control '%s' used by '%s'", ref->id, elem->id); ++ tplg_dbg("\tparent size: %d + %d -> %d, priv size -> %d", + elem->size, ref->size, elem->size + ref->size, + widget->priv.size); + +@@ -210,8 +210,8 @@ int tplg_build_routes(snd_tplg_t *tplg) + } + + route = elem->route; +- tplg_dbg("\nCheck route: sink '%s', control '%s', source '%s'\n", +- route->sink, route->control, route->source); ++ tplg_dbg("Check route: sink '%s', control '%s', source '%s'", ++ route->sink, route->control, route->source); + + /* validate sink */ + if (strlen(route->sink) <= 0) { +@@ -357,7 +357,7 @@ static int tplg_parse_routes(snd_tplg_t *tplg, snd_config_t *cfg, int index) + if (err < 0) + return err; + +- tplg_dbg("route: sink '%s', control '%s', source '%s'\n", ++ tplg_dbg("route: sink '%s', control '%s', source '%s'", + line->sink, line->control, line->source); + } + +@@ -520,7 +520,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + if (!elem) + return -ENOMEM; + +- tplg_dbg(" Widget: %s\n", elem->id); ++ tplg_dbg(" Widget: %s", elem->id); + + widget = elem->widget; + snd_strlcpy(widget->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); +@@ -550,7 +550,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + } + + widget->id = widget_type; +- tplg_dbg("\t%s: %s\n", id, val); ++ tplg_dbg("\t%s: %s", id, val); + continue; + } + +@@ -560,7 +560,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + + snd_strlcpy(widget->sname, val, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); +- tplg_dbg("\t%s: %s\n", id, val); ++ tplg_dbg("\t%s: %s", id, val); + continue; + } + +@@ -571,7 +571,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + + widget->reg = ival ? -1 : 0; + +- tplg_dbg("\t%s: %s\n", id, val); ++ tplg_dbg("\t%s: %s", id, val); + continue; + } + +@@ -580,7 +580,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + return -EINVAL; + + widget->shift = ival; +- tplg_dbg("\t%s: %d\n", id, widget->shift); ++ tplg_dbg("\t%s: %d", id, widget->shift); + continue; + } + +@@ -589,7 +589,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + return -EINVAL; + + widget->reg = ival; +- tplg_dbg("\t%s: %d\n", id, widget->reg); ++ tplg_dbg("\t%s: %d", id, widget->reg); + continue; + } + +@@ -598,7 +598,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + return -EINVAL; + + widget->invert = ival; +- tplg_dbg("\t%s: %d\n", id, widget->invert); ++ tplg_dbg("\t%s: %d", id, widget->invert); + continue; + } + +@@ -607,7 +607,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + return -EINVAL; + + widget->subseq = ival; +- tplg_dbg("\t%s: %d\n", id, widget->subseq); ++ tplg_dbg("\t%s: %d", id, widget->subseq); + continue; + } + +@@ -616,7 +616,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + return -EINVAL; + + widget->event_type = ival; +- tplg_dbg("\t%s: %d\n", id, widget->event_type); ++ tplg_dbg("\t%s: %d", id, widget->event_type); + continue; + } + +@@ -625,7 +625,7 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, + return -EINVAL; + + widget->event_flags = ival; +- tplg_dbg("\t%s: %d\n", id, widget->event_flags); ++ tplg_dbg("\t%s: %d", id, widget->event_flags); + continue; + } + +@@ -767,7 +767,7 @@ int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + struct tplg_elem *elem; + int i, ret = 0; + +- tplg_dbg("Widget: %s\n", wt->name); ++ tplg_dbg("Widget: %s", wt->name); + + elem = tplg_elem_new_common(tplg, NULL, wt->name, + SND_TPLG_TYPE_DAPM_WIDGET); +diff --git a/src/topology/data.c b/src/topology/data.c +index 0b513428da80..37c4591987db 100644 +--- a/src/topology/data.c ++++ b/src/topology/data.c +@@ -87,7 +87,7 @@ int tplg_parse_refs(snd_config_t *cfg, struct tplg_elem *elem, + if (snd_config_get_string(cfg, &val) < 0) + return -EINVAL; + +- tplg_dbg("\tref data: %s\n", val); ++ tplg_dbg("\tref data: %s", val); + err = tplg_ref_add(elem, type, val); + if (err < 0) + return err; +@@ -108,7 +108,7 @@ int tplg_parse_refs(snd_config_t *cfg, struct tplg_elem *elem, + if (snd_config_get_string(n, &val) < 0) + continue; + +- tplg_dbg("\tref data: %s\n", val); ++ tplg_dbg("\tref data: %s", val); + err = tplg_ref_add(elem, type, val); + if (err < 0) + return err; +@@ -169,7 +169,7 @@ static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem) + size_t size, bytes_read; + int ret = 0; + +- tplg_dbg("data DataFile: %s\n", elem->id); ++ tplg_dbg("data DataFile: %s", elem->id); + + if (snd_config_get_string(cfg, &value) < 0) + return -EINVAL; +@@ -235,19 +235,25 @@ static void dump_priv_data(struct tplg_elem *elem ATTRIBUTE_UNUSED) + #ifdef TPLG_DEBUG + struct snd_soc_tplg_private *priv = elem->data; + unsigned char *p = (unsigned char *)priv->data; ++ char buf[128], buf2[8]; + unsigned int i; + +- tplg_dbg(" elem size = %d, priv data size = %d\n", ++ tplg_dbg(" elem size = %d, priv data size = %d", + elem->size, priv->size); + ++ buf[0] = '\0'; + for (i = 0; i < priv->size; i++) { +- if (i > 0 && (i % 16) == 0) +- tplg_dbg("\n"); ++ if (i > 0 && (i % 16) == 0) { ++ tplg_dbg("%s", buf); ++ buf[0] = '\0'; ++ } + +- tplg_dbg(" %02x:", *p++); ++ snprintf(buf2, sizeof(buf2), " %02x", *p++); ++ strcat(buf, buf2); + } + +- tplg_dbg("\n\n"); ++ if (buf[0]) ++ tplg_dbg("%s", buf); + #endif + } + +@@ -466,7 +472,7 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, + int size, esize, off, num; + int ret; + +- tplg_dbg(" data: %s\n", elem->id); ++ tplg_dbg(" data: %s", elem->id); + + if (snd_config_get_string(cfg, &value) < 0) + return -EINVAL; +@@ -683,7 +689,7 @@ static int build_tuples(snd_tplg_t *tplg, struct tplg_elem *elem) + if (ref->type != SND_TPLG_TYPE_TUPLE) + continue; + +- tplg_dbg("tuples '%s' used by data '%s'\n", ref->id, elem->id); ++ tplg_dbg("tuples '%s' used by data '%s'", ref->id, elem->id); + + if (!ref->elem) + ref->elem = tplg_elem_lookup(&tplg->tuple_list, +@@ -799,7 +805,7 @@ static int parse_tuple_set(snd_config_t *cfg, + if (!num_tuples) + return 0; + +- tplg_dbg("\t %d %s tuples:\n", num_tuples, id); ++ tplg_dbg("\t %d %s tuples:", num_tuples, id); + set = calloc(1, sizeof(*set) + num_tuples * sizeof(struct tplg_tuple)); + if (!set) + return -ENOMEM; +@@ -831,7 +837,7 @@ static int parse_tuple_set(snd_config_t *cfg, + continue; + snd_strlcpy(tuple->string, value, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); +- tplg_dbg("\t\t%s = %s\n", tuple->token, tuple->string); ++ tplg_dbg("\t\t%s = %s", tuple->token, tuple->string); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_BOOL: +@@ -839,7 +845,7 @@ static int parse_tuple_set(snd_config_t *cfg, + if (ival < 0) + continue; + tuple->value = ival; +- tplg_dbg("\t\t%s = %d\n", tuple->token, tuple->value); ++ tplg_dbg("\t\t%s = %d", tuple->token, tuple->value); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_BYTE: +@@ -862,7 +868,7 @@ static int parse_tuple_set(snd_config_t *cfg, + } + + tuple->value = tuple_val; +- tplg_dbg("\t\t%s = 0x%x\n", tuple->token, tuple->value); ++ tplg_dbg("\t\t%s = 0x%x", tuple->token, tuple->value); + break; + + default: +@@ -1047,7 +1053,7 @@ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, + if (!num_tokens) + return 0; + +- tplg_dbg(" Vendor tokens: %s, %d tokens\n", elem->id, num_tokens); ++ tplg_dbg(" Vendor tokens: %s, %d tokens", elem->id, num_tokens); + + tokens = calloc(1, sizeof(*tokens) + + num_tokens * sizeof(struct tplg_token)); +@@ -1067,7 +1073,7 @@ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, + snd_strlcpy(tokens->token[tokens->num_tokens].id, id, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + tokens->token[tokens->num_tokens].value = value; +- tplg_dbg("\t\t %s : %d\n", tokens->token[tokens->num_tokens].id, ++ tplg_dbg("\t\t %s : %d", tokens->token[tokens->num_tokens].id, + tokens->token[tokens->num_tokens].value); + tokens->num_tokens++; + } +@@ -1116,7 +1122,7 @@ int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg, + if (!elem) + return -ENOMEM; + +- tplg_dbg(" Vendor Tuples: %s\n", elem->id); ++ tplg_dbg(" Vendor Tuples: %s", elem->id); + + tuples = calloc(1, sizeof(*tuples)); + if (!tuples) +@@ -1133,7 +1139,7 @@ int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg, + if (snd_config_get_string(n, &value) < 0) + return -EINVAL; + tplg_ref_add(elem, SND_TPLG_TYPE_TOKEN, value); +- tplg_dbg("\t refer to vendor tokens: %s\n", value); ++ tplg_dbg("\t refer to vendor tokens: %s", value); + } + + if (strcmp(id, "tuples") == 0) { +@@ -1208,7 +1214,7 @@ int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg, + manifest = elem->manifest; + manifest->size = elem->size; + +- tplg_dbg(" Manifest: %s\n", elem->id); ++ tplg_dbg(" Manifest: %s", elem->id); + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); +@@ -1402,7 +1408,7 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, + return -EINVAL; + + elem->vendor_type = ival; +- tplg_dbg("\t%s: %d\n", id, elem->index); ++ tplg_dbg("\t%s: %d", id, elem->index); + continue; + } + } +@@ -1506,7 +1512,7 @@ int tplg_copy_data(snd_tplg_t *tplg, struct tplg_elem *elem, + return -EINVAL; + } + +- tplg_dbg("Data '%s' used by '%s'\n", ref->id, elem->id); ++ tplg_dbg("Data '%s' used by '%s'", ref->id, elem->id); + /* overlook empty private data */ + if (!ref_elem->data || !ref_elem->data->size) { + ref->elem = ref_elem; +diff --git a/src/topology/ops.c b/src/topology/ops.c +index 2885c7814604..110eef58851d 100644 +--- a/src/topology/ops.c ++++ b/src/topology/ops.c +@@ -69,7 +69,7 @@ int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg, + const char *id, *value; + int ival; + +- tplg_dbg("\tOps\n"); ++ tplg_dbg("\tOps"); + hdr->size = sizeof(*hdr); + + snd_config_for_each(i, next, cfg) { +@@ -97,7 +97,7 @@ int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg, + else if (strcmp(id, "get") == 0) + hdr->ops.get = ival; + +- tplg_dbg("\t\t%s = %d\n", id, ival); ++ tplg_dbg("\t\t%s = %d", id, ival); + } + + return 0; +@@ -155,7 +155,7 @@ int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + const char *id, *value; + int ival; + +- tplg_dbg("\tExt Ops\n"); ++ tplg_dbg("\tExt Ops"); + + snd_config_for_each(i, next, cfg) { + +@@ -182,7 +182,7 @@ int tplg_parse_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + else if (strcmp(id, "get") == 0) + be->ext_ops.get = ival; + +- tplg_dbg("\t\t%s = %s\n", id, value); ++ tplg_dbg("\t\t%s = %s", id, value); + } + + return 0; +diff --git a/src/topology/pcm.c b/src/topology/pcm.c +index 918e3e9a957f..da88783f2b09 100644 +--- a/src/topology/pcm.c ++++ b/src/topology/pcm.c +@@ -68,7 +68,7 @@ static void copy_stream_caps(const char *id ATTRIBUTE_UNUSED, + { + struct snd_soc_tplg_stream_caps *ref_caps = ref_elem->stream_caps; + +- tplg_dbg("Copy pcm caps (%ld bytes) from '%s' to '%s' \n", ++ tplg_dbg("Copy pcm caps (%ld bytes) from '%s' to '%s'", + sizeof(*caps), ref_elem->id, id); + + *caps = *ref_caps; +@@ -388,7 +388,7 @@ static int parse_unsigned(snd_config_t *n, unsigned int *dst) + { + const char *id; + if (snd_config_get_id(n, &id) >= 0) +- tplg_dbg("\t\t%s: %d\n", id, *dst); ++ tplg_dbg("\t\t%s: %d", id, *dst); + } + #endif + return 0; +@@ -415,7 +415,7 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg, + sc->size = elem->size; + snd_strlcpy(sc->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + +- tplg_dbg(" PCM Capabilities: %s\n", elem->id); ++ tplg_dbg(" PCM Capabilities: %s", elem->id); + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); +@@ -442,7 +442,7 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg, + if (err < 0) + return err; + +- tplg_dbg("\t\t%s: %s\n", id, val); ++ tplg_dbg("\t\t%s: %s", id, val); + continue; + } + +@@ -460,7 +460,7 @@ int tplg_parse_stream_caps(snd_tplg_t *tplg, + if (err < 0) + return err; + +- tplg_dbg("\t\t%s: %s\n", id, val); ++ tplg_dbg("\t\t%s: %s", id, val); + continue; + } + +@@ -625,7 +625,7 @@ static int tplg_parse_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + + snd_config_get_id(cfg, &id); + +- tplg_dbg("\t%s:\n", id); ++ tplg_dbg("\t%s:", id); + + switch (elem->type) { + case SND_TPLG_TYPE_PCM: +@@ -672,7 +672,7 @@ static int tplg_parse_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + snd_strlcpy(caps[stream].name, value, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + +- tplg_dbg("\t\t%s\n\t\t\t%s\n", id, value); ++ tplg_dbg("\t\t%s\n\t\t\t%s", id, value); + continue; + } + } +@@ -746,7 +746,7 @@ static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + const char *id; + + snd_config_get_id(cfg, &id); +- tplg_dbg("\t\tFE DAI %s:\n", id); ++ tplg_dbg("\t\tFE DAI %s:", id); + snd_strlcpy(pcm->dai_name, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + snd_config_for_each(i, next, cfg) { +@@ -763,7 +763,7 @@ static int tplg_parse_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + return -EINVAL; + } + +- tplg_dbg("\t\t\tindex: %d\n", pcm->dai_id); ++ tplg_dbg("\t\t\tindex: %d", pcm->dai_id); + } + } + +@@ -847,7 +847,7 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, + pcm->size = elem->size; + snd_strlcpy(pcm->pcm_name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + +- tplg_dbg(" PCM: %s\n", elem->id); ++ tplg_dbg(" PCM: %s", elem->id); + + snd_config_for_each(i, next, cfg) { + +@@ -882,7 +882,7 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, + + pcm->compress = ival; + +- tplg_dbg("\t%s: %d\n", id, ival); ++ tplg_dbg("\t%s: %d", id, ival); + continue; + } + +@@ -988,7 +988,7 @@ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg, + snd_strlcpy(dai->dai_name, elem->id, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + +- tplg_dbg(" DAI: %s\n", elem->id); ++ tplg_dbg(" DAI: %s", elem->id); + + snd_config_for_each(i, next, cfg) { + +@@ -1142,7 +1142,7 @@ int tplg_parse_link(snd_tplg_t *tplg, + link->size = elem->size; + snd_strlcpy(link->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + +- tplg_dbg(" Link: %s\n", elem->id); ++ tplg_dbg(" Link: %s", elem->id); + + snd_config_for_each(i, next, cfg) { + +@@ -1168,7 +1168,7 @@ int tplg_parse_link(snd_tplg_t *tplg, + + snd_strlcpy(link->stream_name, val, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); +- tplg_dbg("\t%s: %s\n", id, val); ++ tplg_dbg("\t%s: %s", id, val); + continue; + } + +@@ -1280,7 +1280,7 @@ int tplg_parse_cc(snd_tplg_t *tplg, + link = elem->link; + link->size = elem->size; + +- tplg_dbg(" CC: %s\n", elem->id); ++ tplg_dbg(" CC: %s", elem->id); + + snd_config_for_each(i, next, cfg) { + +@@ -1412,7 +1412,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + hw_cfg = elem->hw_cfg; + hw_cfg->size = elem->size; + +- tplg_dbg(" Link HW config: %s\n", elem->id); ++ tplg_dbg(" Link HW config: %s", elem->id); + + snd_config_for_each(i, next, cfg) { + +@@ -1719,7 +1719,7 @@ int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + struct tplg_elem *elem; + int ret, i; + +- tplg_dbg("PCM: %s, DAI %s\n", pcm_tpl->pcm_name, pcm_tpl->dai_name); ++ tplg_dbg("PCM: %s, DAI %s", pcm_tpl->pcm_name, pcm_tpl->dai_name); + + if (pcm_tpl->num_streams > SND_SOC_TPLG_STREAM_CONFIG_MAX) + return -EINVAL; +@@ -1881,7 +1881,7 @@ int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + struct tplg_elem *elem; + int ret, i; + +- tplg_dbg("DAI %s\n", dai_tpl->dai_name); ++ tplg_dbg("DAI %s", dai_tpl->dai_name); + + elem = tplg_elem_new_common(tplg, NULL, dai_tpl->dai_name, + SND_TPLG_TYPE_DAI); +diff --git a/src/topology/text.c b/src/topology/text.c +index 6dbf22305fd3..507c5450ab14 100644 +--- a/src/topology/text.c ++++ b/src/topology/text.c +@@ -32,13 +32,13 @@ static int parse_text_values(snd_config_t *cfg, struct tplg_elem *elem) + const char *value = NULL; + int j = 0; + +- tplg_dbg(" Text Values: %s\n", elem->id); ++ tplg_dbg(" Text Values: %s", elem->id); + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + + if (j == SND_SOC_TPLG_NUM_TEXTS) { +- tplg_dbg("text string number exceeds %d\n", j); ++ tplg_dbg("text string number exceeds %d", j); + return -ENOMEM; + } + +@@ -48,7 +48,7 @@ static int parse_text_values(snd_config_t *cfg, struct tplg_elem *elem) + + snd_strlcpy(&texts->items[j][0], value, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); +- tplg_dbg("\t%s\n", &texts->items[j][0]); ++ tplg_dbg("\t%s", &texts->items[j][0]); + + j++; + } +-- +2.16.4 + diff --git a/0057-topology-cosmetic-changes-functions.patch b/0057-topology-cosmetic-changes-functions.patch new file mode 100644 index 0000000..ff1376a --- /dev/null +++ b/0057-topology-cosmetic-changes-functions.patch @@ -0,0 +1,201 @@ +From b20b400e2f598c86abaf697d66396cecd49b3e14 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Fri, 3 Jan 2020 22:56:48 +0100 +Subject: [PATCH 57/63] topology: cosmetic changes (functions) + +Signed-off-by: Jaroslav Kysela +--- + src/topology/builder.c | 3 ++- + src/topology/ctl.c | 5 +++-- + src/topology/data.c | 17 +++++++++-------- + src/topology/parser.c | 4 ++-- + src/topology/pcm.c | 16 ++++++++-------- + 5 files changed, 24 insertions(+), 21 deletions(-) + +diff --git a/src/topology/builder.c b/src/topology/builder.c +index 7af5de084d84..15757668d7d4 100644 +--- a/src/topology/builder.c ++++ b/src/topology/builder.c +@@ -71,7 +71,8 @@ static ssize_t write_block_header(snd_tplg_t *tplg, unsigned int type, + } + + static int write_elem_block(snd_tplg_t *tplg, +- struct list_head *base, size_t size, int tplg_type, const char *obj_name) ++ struct list_head *base, size_t size, ++ int tplg_type, const char *obj_name) + { + struct list_head *pos, *sub_pos, *sub_base; + struct tplg_elem *elem, *elem_next; +diff --git a/src/topology/ctl.c b/src/topology/ctl.c +index 41dc2ddbb826..b78f1c54be05 100644 +--- a/src/topology/ctl.c ++++ b/src/topology/ctl.c +@@ -600,7 +600,7 @@ int tplg_save_control_bytes(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + + /* Parse Control Enums. */ + int tplg_parse_control_enum(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED) ++ void *private ATTRIBUTE_UNUSED) + { + struct snd_soc_tplg_enum_control *ec; + struct tplg_elem *elem; +@@ -736,7 +736,8 @@ int tplg_save_control_enum(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + * Mixer control. Supports multiple channels. + */ + int tplg_parse_control_mixer(snd_tplg_t *tplg, +- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) ++ snd_config_t *cfg, ++ void *private ATTRIBUTE_UNUSED) + { + struct snd_soc_tplg_mixer_control *mc; + struct tplg_elem *elem; +diff --git a/src/topology/data.c b/src/topology/data.c +index 37c4591987db..b63e98cd7282 100644 +--- a/src/topology/data.c ++++ b/src/topology/data.c +@@ -516,7 +516,7 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, + + /* get the token integer value from its id */ + static int get_token_value(const char *token_id, +- struct tplg_vendor_tokens *tokens) ++ struct tplg_vendor_tokens *tokens) + { + unsigned int i; + +@@ -589,7 +589,8 @@ unsigned int tplg_get_tuple_size(int type) + + /* Add a tuples object to the private buffer of its parent data element */ + static int copy_tuples(struct tplg_elem *elem, +- struct tplg_vendor_tuples *tuples, struct tplg_vendor_tokens *tokens) ++ struct tplg_vendor_tuples *tuples, ++ struct tplg_vendor_tokens *tokens) + { + struct snd_soc_tplg_private *priv = elem->data, *priv2; + struct tplg_tuple_set *tuple_set; +@@ -781,7 +782,7 @@ static const char *get_tuple_type_name(unsigned int type) + } + + static int parse_tuple_set(snd_config_t *cfg, +- struct tplg_tuple_set **s) ++ struct tplg_tuple_set **s) + { + snd_config_iterator_t i, next; + snd_config_t *n; +@@ -965,7 +966,7 @@ static int tplg_save_tuple_set(struct tplg_vendor_tuples *tuples, + } + + static int parse_tuple_sets(snd_config_t *cfg, +- struct tplg_vendor_tuples *tuples) ++ struct tplg_vendor_tuples *tuples) + { + snd_config_iterator_t i, next; + snd_config_t *n; +@@ -1033,7 +1034,7 @@ int tplg_save_tuple_sets(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + /* Parse vendor tokens + */ + int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED) ++ void *private ATTRIBUTE_UNUSED) + { + snd_config_iterator_t i, next; + snd_config_t *n; +@@ -1109,7 +1110,7 @@ int tplg_save_tokens(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + /* Parse vendor tuples. + */ + int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED) ++ void *private ATTRIBUTE_UNUSED) + { + snd_config_iterator_t i, next; + snd_config_t *n; +@@ -1193,7 +1194,7 @@ void tplg_free_tuples(void *obj) + /* Parse manifest's data references + */ + int tplg_parse_manifest_data(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED) ++ void *private ATTRIBUTE_UNUSED) + { + struct snd_soc_tplg_manifest *manifest; + struct tplg_elem *elem; +@@ -1341,7 +1342,7 @@ int tplg_build_manifest_data(snd_tplg_t *tplg) + * words, tuples. + */ + int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, +- void *private ATTRIBUTE_UNUSED) ++ void *private ATTRIBUTE_UNUSED) + { + snd_config_iterator_t i, next; + snd_config_t *n; +diff --git a/src/topology/parser.c b/src/topology/parser.c +index d7783a93ab6b..436e48416a43 100644 +--- a/src/topology/parser.c ++++ b/src/topology/parser.c +@@ -110,8 +110,8 @@ int tplg_get_unsigned(snd_config_t *n, unsigned *val, int base) + * Parse compound + */ + int tplg_parse_compound(snd_tplg_t *tplg, snd_config_t *cfg, +- int (*fcn)(snd_tplg_t *, snd_config_t *, void *), +- void *private) ++ int (*fcn)(snd_tplg_t *, snd_config_t *, void *), ++ void *private) + { + const char *id; + snd_config_iterator_t i, next; +diff --git a/src/topology/pcm.c b/src/topology/pcm.c +index da88783f2b09..61159d33357c 100644 +--- a/src/topology/pcm.c ++++ b/src/topology/pcm.c +@@ -1124,8 +1124,8 @@ static int parse_hw_config_refs(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + } + + /* Parse a physical link element in text conf file */ +-int tplg_parse_link(snd_tplg_t *tplg, +- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) ++int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg, ++ void *private ATTRIBUTE_UNUSED) + { + struct snd_soc_tplg_link_config *link; + struct tplg_elem *elem; +@@ -1264,8 +1264,8 @@ int tplg_save_link(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + } + + /* Parse cc */ +-int tplg_parse_cc(snd_tplg_t *tplg, +- snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) ++int tplg_parse_cc(snd_tplg_t *tplg, snd_config_t *cfg, ++ void *private ATTRIBUTE_UNUSED) + { + struct snd_soc_tplg_link_config *link; + struct tplg_elem *elem; +@@ -1307,8 +1307,8 @@ int tplg_parse_cc(snd_tplg_t *tplg, + + /* save CC */ + int tplg_save_cc(snd_tplg_t *tplg ATTRIBUTE_UNUSED, +- struct tplg_elem *elem, +- char **dst, const char *pfx) ++ struct tplg_elem *elem, ++ char **dst, const char *pfx) + { + struct snd_soc_tplg_link_config *link = elem->link; + char pfx2[16]; +@@ -1668,7 +1668,7 @@ int tplg_save_hw_config(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + + /* copy stream object */ + static void tplg_add_stream_object(struct snd_soc_tplg_stream *strm, +- struct snd_tplg_stream_template *strm_tpl) ++ struct snd_tplg_stream_template *strm_tpl) + { + snd_strlcpy(strm->name, strm_tpl->name, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); +@@ -1773,7 +1773,7 @@ int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) + + /* Set link HW config from C API template */ + static int set_link_hw_config(struct snd_soc_tplg_hw_config *cfg, +- struct snd_tplg_hw_config_template *tpl) ++ struct snd_tplg_hw_config_template *tpl) + { + unsigned int i; + +-- +2.16.4 + diff --git a/0058-mixer-Fix-memory-leak-for-more-than-16-file-descript.patch b/0058-mixer-Fix-memory-leak-for-more-than-16-file-descript.patch new file mode 100644 index 0000000..3aed692 --- /dev/null +++ b/0058-mixer-Fix-memory-leak-for-more-than-16-file-descript.patch @@ -0,0 +1,29 @@ +From 14ad963e192f101246e9b559d3b3fd41268330fb Mon Sep 17 00:00:00 2001 +From: David Fries +Date: Tue, 7 Jan 2020 09:18:10 -0600 +Subject: [PATCH 58/63] mixer: Fix memory leak for more than 16 file descriptor + case + +Signed-off-by: David Fries +Reviewed-by: Takashi Iwai +Signed-off-by: Takashi Iwai +--- + src/mixer/mixer.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c +index 8205647449d2..b1af99456c8d 100644 +--- a/src/mixer/mixer.c ++++ b/src/mixer/mixer.c +@@ -767,7 +767,7 @@ int snd_mixer_wait(snd_mixer_t *mixer, int timeout) + if (count < 0) + return count; + if ((unsigned int) count > sizeof(spfds) / sizeof(spfds[0])) { +- pfds = malloc(count * sizeof(*pfds)); ++ pfds = alloca(count * sizeof(*pfds)); + if (!pfds) + return -ENOMEM; + err = snd_mixer_poll_descriptors(mixer, pfds, +-- +2.16.4 + diff --git a/0059-Quote-strings-containing-or-when-saving-an-alsa-conf.patch b/0059-Quote-strings-containing-or-when-saving-an-alsa-conf.patch new file mode 100644 index 0000000..b5845ef --- /dev/null +++ b/0059-Quote-strings-containing-or-when-saving-an-alsa-conf.patch @@ -0,0 +1,28 @@ +From a78dd1bd1e9b47c2aee9b1e066de515b6854695a Mon Sep 17 00:00:00 2001 +From: Bertware +Date: Mon, 13 Jan 2020 10:21:33 +0100 +Subject: [PATCH 59/63] Quote strings containing [ or ] when saving an alsa + config + +Signed-off-by: Bert Marcelis +Signed-off-by: Jaroslav Kysela +--- + src/conf.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/conf.c b/src/conf.c +index c4db9f21a15e..50d0403452a0 100644 +--- a/src/conf.c ++++ b/src/conf.c +@@ -1531,6 +1531,8 @@ static void string_print(char *str, int id, snd_output_t *out) + case '.': + case '{': + case '}': ++ case '[': ++ case ']': + case '\'': + case '"': + goto quoted; +-- +2.16.4 + diff --git a/0060-ucm-fix-the-configuration-directory-longname-for-ucm.patch b/0060-ucm-fix-the-configuration-directory-longname-for-ucm.patch new file mode 100644 index 0000000..44c91a3 --- /dev/null +++ b/0060-ucm-fix-the-configuration-directory-longname-for-ucm.patch @@ -0,0 +1,32 @@ +From b34715004f4a1aabb85c5b9f03ba9f200638ee97 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Wed, 15 Jan 2020 09:41:05 +0100 +Subject: [PATCH 60/63] ucm: fix the configuration directory (longname) for + ucm2 + +The new ucm2 expects that the longname directory is shared with +the driver directory. Fix that for 'Syntax 2'. + +Signed-off-by: Jaroslav Kysela +--- + src/ucm/parser.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/ucm/parser.c b/src/ucm/parser.c +index d61124a343e1..b3970a13bf73 100644 +--- a/src/ucm/parser.c ++++ b/src/ucm/parser.c +@@ -1649,7 +1649,9 @@ __longname: + + if (err == 0) { + /* got device-specific file that matches the card long name */ +- snd_strlcpy(uc_mgr->conf_file_name, longname, sizeof(uc_mgr->conf_file_name)); ++ if (uc_mgr->conf_format < 2) ++ snd_strlcpy(uc_mgr->conf_file_name, longname, ++ sizeof(uc_mgr->conf_file_name)); + goto __parse; + } + } +-- +2.16.4 + diff --git a/0061-ucm-split-conf_file_name-and-conf_dir_name.patch b/0061-ucm-split-conf_file_name-and-conf_dir_name.patch new file mode 100644 index 0000000..53cc941 --- /dev/null +++ b/0061-ucm-split-conf_file_name-and-conf_dir_name.patch @@ -0,0 +1,209 @@ +From fe6425af751a768a2ba6cf7d430a85553d3b84f6 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Wed, 15 Jan 2020 10:31:56 +0100 +Subject: [PATCH 61/63] ucm: split conf_file_name and conf_dir_name + +With ucm2, the file name might differ from the directory +name. Also, allocate those fields. + +Signed-off-by: Jaroslav Kysela +--- + src/ucm/main.c | 7 ++++--- + src/ucm/parser.c | 37 ++++++++++++++++++++++++++----------- + src/ucm/ucm_local.h | 4 ++-- + src/ucm/ucm_subs.c | 2 +- + src/ucm/utils.c | 4 ++++ + 5 files changed, 37 insertions(+), 17 deletions(-) + +diff --git a/src/ucm/main.c b/src/ucm/main.c +index 61922f10b7c7..23e15bd8dacb 100644 +--- a/src/ucm/main.c ++++ b/src/ucm/main.c +@@ -1700,12 +1700,13 @@ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, + err = 0; + } else if (strcmp(identifier, "_file") == 0) { + /* get the conf file name of the opened card */ +- if ((uc_mgr->card_name == NULL) +- || (uc_mgr->conf_file_name[0] == '\0')) { ++ if ((uc_mgr->card_name == NULL) || ++ (uc_mgr->conf_file_name == NULL) || ++ (uc_mgr->conf_file_name[0] == '\0')) { + err = -ENOENT; + goto __end; + } +- *value = strndup(uc_mgr->conf_file_name, MAX_FILE); ++ *value = strdup(uc_mgr->conf_file_name); + if (*value == NULL) { + err = -ENOMEM; + goto __end; +diff --git a/src/ucm/parser.c b/src/ucm/parser.c +index b3970a13bf73..1bfde0be327d 100644 +--- a/src/ucm/parser.c ++++ b/src/ucm/parser.c +@@ -124,6 +124,16 @@ static void configuration_filename(snd_use_case_mgr_t *uc_mgr, + configuration_filename2(fn, fn_len, 2, dir, file, suffix); + } + ++/* ++ * Replace mallocated string ++ */ ++static char *replace_string(char **dst, const char *value) ++{ ++ free(*dst); ++ *dst = strdup(value); ++ return *dst; ++} ++ + /* + * Parse string + */ +@@ -1186,7 +1196,7 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, + + /* open Verb file for reading */ + configuration_filename(uc_mgr, filename, sizeof(filename), +- uc_mgr->conf_file_name, file, ""); ++ uc_mgr->conf_dir_name, 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", +@@ -1404,16 +1414,16 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) + 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); ++ uc_error("Syntax field not found in %s", uc_mgr->conf_dir_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); ++ uc_error("Syntax field is invalid in %s", uc_mgr->conf_dir_name); + return err; + } + if (l < 2 || l > SYNTAX_VERSION_MAX) { +- uc_error("Incompatible syntax %d in %s", l, uc_mgr->conf_file_name); ++ uc_error("Incompatible syntax %d in %s", l, uc_mgr->conf_dir_name); + return -EINVAL; + } + /* delete this field to avoid strcmp() call in the loop */ +@@ -1561,8 +1571,9 @@ static int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name, char *long + return err; + + _name = snd_ctl_card_info_get_name(info); ++ if (replace_string(&mgr->conf_dir_name, _name) == NULL) ++ return -ENOMEM; + _long_name = snd_ctl_card_info_get_longname(info); +- snd_strlcpy(mgr->conf_file_name, _name, sizeof(mgr->conf_file_name)); + snd_strlcpy(longname, _long_name, MAX_CARD_LONG_NAME); + + return 0; +@@ -1585,7 +1596,7 @@ static int load_master_config(snd_use_case_mgr_t *uc_mgr, + if (getenv(ALSA_CONFIG_UCM2_VAR) || !getenv(ALSA_CONFIG_UCM_VAR)) { + uc_mgr->conf_format = 2; + configuration_filename(uc_mgr, filename, sizeof(filename), +- uc_mgr->conf_file_name, card_name, ".conf"); ++ uc_mgr->conf_dir_name, card_name, ".conf"); + if (access(filename, R_OK) == 0) + goto __load; + } +@@ -1608,6 +1619,9 @@ __load: + return err; + } + ++ if (replace_string(&uc_mgr->conf_file_name, card_name) == NULL) ++ return -ENOMEM; ++ + return 0; + } + +@@ -1632,7 +1646,8 @@ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr) + char longname[MAX_CARD_LONG_NAME]; + int err; + +- snd_strlcpy(uc_mgr->conf_file_name, uc_mgr->card_name, sizeof(uc_mgr->conf_file_name)); ++ if (replace_string(&uc_mgr->conf_dir_name, uc_mgr->card_name) == NULL) ++ return -ENOMEM; + + if (strncmp(name, "hw:", 3) == 0) { + err = get_by_card(uc_mgr, name, longname); +@@ -1650,14 +1665,14 @@ __longname: + if (err == 0) { + /* got device-specific file that matches the card long name */ + if (uc_mgr->conf_format < 2) +- snd_strlcpy(uc_mgr->conf_file_name, longname, +- sizeof(uc_mgr->conf_file_name)); ++ snd_strlcpy(uc_mgr->conf_dir_name, longname, ++ sizeof(uc_mgr->conf_dir_name)); + goto __parse; + } + } + + /* standard path */ +- err = load_master_config(uc_mgr, uc_mgr->conf_file_name, &cfg, 0); ++ err = load_master_config(uc_mgr, uc_mgr->conf_dir_name, &cfg, 0); + if (err < 0) + goto __error; + +@@ -1673,7 +1688,7 @@ __parse: + + __error: + uc_mgr_free_ctl_list(uc_mgr); +- uc_mgr->conf_file_name[0] = '\0'; ++ uc_mgr->conf_dir_name[0] = '\0'; + return err; + } + +diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h +index ba9615078ef8..63e0f3bf1697 100644 +--- a/src/ucm/ucm_local.h ++++ b/src/ucm/ucm_local.h +@@ -187,7 +187,6 @@ struct use_case_verb { + /* verb transition list */ + struct list_head transition_list; + +- /* hardware devices that can be used with this use case */ + struct list_head device_list; + + /* component device list */ +@@ -205,7 +204,8 @@ struct use_case_verb { + */ + struct snd_use_case_mgr { + char *card_name; +- char conf_file_name[MAX_CARD_LONG_NAME]; ++ char *conf_file_name; ++ char *conf_dir_name; + char *comment; + int conf_format; + +diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c +index 90e395f0ab6f..d931f603ddd7 100644 +--- a/src/ucm/ucm_subs.c ++++ b/src/ucm/ucm_subs.c +@@ -31,7 +31,7 @@ + + static char *rval_conf_name(snd_use_case_mgr_t *uc_mgr) + { +- if (uc_mgr->conf_file_name[0]) ++ if (uc_mgr->conf_file_name && uc_mgr->conf_file_name[0]) + return strdup(uc_mgr->conf_file_name); + return NULL; + } +diff --git a/src/ucm/utils.c b/src/ucm/utils.c +index cde1d672e7b4..daa568c16a30 100644 +--- a/src/ucm/utils.c ++++ b/src/ucm/utils.c +@@ -441,7 +441,11 @@ void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr) + uc_mgr_free_sequence(&uc_mgr->default_list); + uc_mgr_free_value(&uc_mgr->value_list); + free(uc_mgr->comment); ++ free(uc_mgr->conf_dir_name); ++ free(uc_mgr->conf_file_name); + uc_mgr->comment = NULL; ++ uc_mgr->conf_dir_name = NULL; ++ uc_mgr->conf_file_name = NULL; + uc_mgr->active_verb = NULL; + INIT_LIST_HEAD(&uc_mgr->active_devices); + INIT_LIST_HEAD(&uc_mgr->active_modifiers); +-- +2.16.4 + diff --git a/0062-ucm-remove-MAX_FILE-definition-and-use-correct-PATH_.patch b/0062-ucm-remove-MAX_FILE-definition-and-use-correct-PATH_.patch new file mode 100644 index 0000000..08ef9ba --- /dev/null +++ b/0062-ucm-remove-MAX_FILE-definition-and-use-correct-PATH_.patch @@ -0,0 +1,58 @@ +From ebf5213cd61824b10bcaf67c570919e2a9ea0e1f Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Wed, 15 Jan 2020 10:40:01 +0100 +Subject: [PATCH 62/63] ucm: remove MAX_FILE definition and use correct + PATH_MAX + +Signed-off-by: Jaroslav Kysela +--- + src/ucm/parser.c | 6 +++--- + src/ucm/ucm_local.h | 1 - + 2 files changed, 3 insertions(+), 4 deletions(-) + +diff --git a/src/ucm/parser.c b/src/ucm/parser.c +index 1bfde0be327d..6c13fafdabbf 100644 +--- a/src/ucm/parser.c ++++ b/src/ucm/parser.c +@@ -1167,7 +1167,7 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, + snd_config_t *n; + struct use_case_verb *verb; + snd_config_t *cfg; +- char filename[MAX_FILE]; ++ char filename[PATH_MAX]; + int err; + + /* allocate verb */ +@@ -1582,7 +1582,7 @@ static int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name, char *long + static int load_master_config(snd_use_case_mgr_t *uc_mgr, + const char *card_name, snd_config_t **cfg, int longname) + { +- char filename[MAX_FILE]; ++ char filename[PATH_MAX]; + int err; + + if (strnlen(card_name, MAX_CARD_LONG_NAME) == MAX_CARD_LONG_NAME) { +@@ -1733,7 +1733,7 @@ static int is_component_directory(const char *dir) + */ + int uc_mgr_scan_master_configs(const char **_list[]) + { +- char filename[MAX_FILE], dfl[MAX_FILE]; ++ char filename[PATH_MAX], dfl[PATH_MAX]; + char *env = getenv(ALSA_CONFIG_UCM2_VAR); + const char **list, *d_name; + snd_config_t *cfg, *c; +diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h +index 63e0f3bf1697..fa9fc16661bb 100644 +--- a/src/ucm/ucm_local.h ++++ b/src/ucm/ucm_local.h +@@ -42,7 +42,6 @@ + + #define SYNTAX_VERSION_MAX 2 + +-#define MAX_FILE 256 + #define MAX_CARD_SHORT_NAME 32 + #define MAX_CARD_LONG_NAME 80 + +-- +2.16.4 + diff --git a/0063-topology-remove-MAX_FILE-definition-and-use-correct-.patch b/0063-topology-remove-MAX_FILE-definition-and-use-correct-.patch new file mode 100644 index 0000000..9475da6 --- /dev/null +++ b/0063-topology-remove-MAX_FILE-definition-and-use-correct-.patch @@ -0,0 +1,40 @@ +From fbe1ac4a09933f527aff860aad035586367f21b4 Mon Sep 17 00:00:00 2001 +From: Jaroslav Kysela +Date: Wed, 15 Jan 2020 10:41:35 +0100 +Subject: [PATCH 63/63] topology: remove MAX_FILE definition and use correct + PATH_MAX + +Signed-off-by: Jaroslav Kysela +--- + src/topology/data.c | 2 +- + src/topology/tplg_local.h | 1 - + 2 files changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/topology/data.c b/src/topology/data.c +index b63e98cd7282..5742b35773f6 100644 +--- a/src/topology/data.c ++++ b/src/topology/data.c +@@ -163,7 +163,7 @@ static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem) + { + struct snd_soc_tplg_private *priv = NULL; + const char *value = NULL; +- char filename[MAX_FILE]; ++ char filename[PATH_MAX]; + char *env = getenv(ALSA_CONFIG_TPLG_VAR); + FILE *fp; + size_t size, bytes_read; +diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h +index e061db755ed2..5ace0d1919e1 100644 +--- a/src/topology/tplg_local.h ++++ b/src/topology/tplg_local.h +@@ -29,7 +29,6 @@ + #define tplg_dbg(fmt, arg...) do { } while (0) + #endif + +-#define MAX_FILE 256 + #define TPLG_MAX_PRIV_SIZE (1024 * 128) + + /** The name of the environment variable containing the tplg directory */ +-- +2.16.4 + diff --git a/alsa.changes b/alsa.changes index 7628da3..56bd2df 100644 --- a/alsa.changes +++ b/alsa.changes @@ -1,4 +1,44 @@ ------------------------------------------------------------------- +Tue Jan 21 15:49:49 CET 2020 - tiwai@suse.de + +- Backport upstream fixes: + more topology fixes, a memory leak fix in mixer API, alsactl + string handling fix, UCM config fixes: + 0032-Update-the-attributes.m4-macro-file-from-xine.patch + 0033-topology-avoid-to-use-the-atoi-directly-when-expecte.patch + 0034-topology-use-snd_config_get_bool-instead-own-impleme.patch + 0035-topology-fix-tplg_get_integer-handle-errno-ERANGE.patch + 0036-topology-add-tplg_get_unsigned-function.patch + 0037-topology-convert-builder-to-use-the-mallocated-memor.patch + 0038-topology-add-binary-output-from-the-builder.patch + 0039-topology-parser-recode-tplg_parse_config.patch + 0040-topology-add-snd_tplg_load-remove-snd_tplg_build_bin.patch + 0041-topology-move-the-topology-element-table-from-builde.patch + 0042-topology-add-parser-to-the-tplg_table.patch + 0043-topology-add-snd_tplg_save.patch + 0044-topology-add-snd_tplg_create-with-flags.patch + 0045-topology-add-snd_tplg_version-function.patch + 0046-topology-cleanup-the-SNDERR-calls.patch + 0047-topology-dapm-fix-the-SNDERR-Undefined.patch + 0048-topology-fix-the-unitialized-tuples.patch + 0049-topology-implement-shorter-hexa-uuid-00-00-parser.patch + 0050-topology-fix-the-TPLG_DEBUG-compilation.patch + 0051-topology-fix-the-ops-parser-accept-integer-hexa-valu.patch + 0052-topology-fix-the-wrong-memory-access-object-realloc.patch + 0053-topology-implement-snd_tplg_decode.patch + 0054-topology-move-the-elem-list-delete-to-tplg_elem_free.patch + 0055-topology-unify-the-log-mechanism.patch + 0056-topology-tplg_dbg-cleanups.patch + 0057-topology-cosmetic-changes-functions.patch + 0058-mixer-Fix-memory-leak-for-more-than-16-file-descript.patch + 0059-Quote-strings-containing-or-when-saving-an-alsa-conf.patch + 0060-ucm-fix-the-configuration-directory-longname-for-ucm.patch + 0061-ucm-split-conf_file_name-and-conf_dir_name.patch + 0062-ucm-remove-MAX_FILE-definition-and-use-correct-PATH_.patch + 0063-topology-remove-MAX_FILE-definition-and-use-correct-.patch +- Remove INSTALL document, add NOTES instead + +------------------------------------------------------------------- Fri Dec 20 16:54:25 CET 2019 - tiwai@suse.de - Upstream fixes, including the alsa-tools build breakage: diff --git a/alsa.spec b/alsa.spec index d26a8d8..51ed618 100644 --- a/alsa.spec +++ b/alsa.spec @@ -1,7 +1,7 @@ # # spec file for package alsa # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -79,6 +79,38 @@ Patch28: 0028-topology-more-coding-fixes.patch Patch29: 0029-Fix-alsa-sound-.h-for-external-programs.patch Patch30: 0030-type_compat-Add-missing-__s64-and-__u64-definitions-.patch Patch31: 0031-uapi-Move-typedefs-from-uapi-to-sound.patch +Patch32: 0032-Update-the-attributes.m4-macro-file-from-xine.patch +Patch33: 0033-topology-avoid-to-use-the-atoi-directly-when-expecte.patch +Patch34: 0034-topology-use-snd_config_get_bool-instead-own-impleme.patch +Patch35: 0035-topology-fix-tplg_get_integer-handle-errno-ERANGE.patch +Patch36: 0036-topology-add-tplg_get_unsigned-function.patch +Patch37: 0037-topology-convert-builder-to-use-the-mallocated-memor.patch +Patch38: 0038-topology-add-binary-output-from-the-builder.patch +Patch39: 0039-topology-parser-recode-tplg_parse_config.patch +Patch40: 0040-topology-add-snd_tplg_load-remove-snd_tplg_build_bin.patch +Patch41: 0041-topology-move-the-topology-element-table-from-builde.patch +Patch42: 0042-topology-add-parser-to-the-tplg_table.patch +Patch43: 0043-topology-add-snd_tplg_save.patch +Patch44: 0044-topology-add-snd_tplg_create-with-flags.patch +Patch45: 0045-topology-add-snd_tplg_version-function.patch +Patch46: 0046-topology-cleanup-the-SNDERR-calls.patch +Patch47: 0047-topology-dapm-fix-the-SNDERR-Undefined.patch +Patch48: 0048-topology-fix-the-unitialized-tuples.patch +Patch49: 0049-topology-implement-shorter-hexa-uuid-00-00-parser.patch +Patch50: 0050-topology-fix-the-TPLG_DEBUG-compilation.patch +Patch51: 0051-topology-fix-the-ops-parser-accept-integer-hexa-valu.patch +Patch52: 0052-topology-fix-the-wrong-memory-access-object-realloc.patch +Patch53: 0053-topology-implement-snd_tplg_decode.patch +Patch54: 0054-topology-move-the-elem-list-delete-to-tplg_elem_free.patch +Patch55: 0055-topology-unify-the-log-mechanism.patch +Patch56: 0056-topology-tplg_dbg-cleanups.patch +Patch57: 0057-topology-cosmetic-changes-functions.patch +Patch58: 0058-mixer-Fix-memory-leak-for-more-than-16-file-descript.patch +Patch59: 0059-Quote-strings-containing-or-when-saving-an-alsa-conf.patch +Patch60: 0060-ucm-fix-the-configuration-directory-longname-for-ucm.patch +Patch61: 0061-ucm-split-conf_file_name-and-conf_dir_name.patch +Patch62: 0062-ucm-remove-MAX_FILE-definition-and-use-correct-PATH_.patch +Patch63: 0063-topology-remove-MAX_FILE-definition-and-use-correct-.patch # rest suse fixes Patch101: alsa-lib-ignore-non-accessible-ALSA_CONFIG_PATH.patch BuildRequires: doxygen @@ -194,6 +226,38 @@ This package contains the library for ALSA topology support. %patch29 -p1 %patch30 -p1 %patch31 -p1 +%patch32 -p1 +%patch33 -p1 +%patch34 -p1 +%patch35 -p1 +%patch36 -p1 +%patch37 -p1 +%patch38 -p1 +%patch39 -p1 +%patch40 -p1 +%patch41 -p1 +%patch42 -p1 +%patch43 -p1 +%patch44 -p1 +%patch45 -p1 +%patch46 -p1 +%patch47 -p1 +%patch48 -p1 +%patch49 -p1 +%patch50 -p1 +%patch51 -p1 +%patch52 -p1 +%patch53 -p1 +%patch54 -p1 +%patch55 -p1 +%patch56 -p1 +%patch57 -p1 +%patch58 -p1 +%patch59 -p1 +%patch60 -p1 +%patch61 -p1 +%patch62 -p1 +%patch63 -p1 %patch101 -p1 %build @@ -279,7 +343,7 @@ cp %{_sourcedir}/README* %{buildroot}%{_docdir}/%{name} cp COPYING %{buildroot}%{_docdir}/%{name} %endif mkdir -p %{buildroot}%{_docdir}/%{name}/alsa-lib -cp ChangeLog INSTALL TODO MEMORY-LEAK %{buildroot}%{_docdir}/%{name}/alsa-lib +cp ChangeLog TODO MEMORY-LEAK NOTES %{buildroot}%{_docdir}/%{name}/alsa-lib cp doc/asoundrc.txt %{buildroot}%{_docdir}/%{name}/alsa-lib %post