alsa/0043-topology-add-snd_tplg_save.patch
Takashi Iwai 6ab9f38ea9 Accepting request 766329 from home:tiwai:branches:multimedia:libs
- 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

OBS-URL: https://build.opensuse.org/request/show/766329
OBS-URL: https://build.opensuse.org/package/show/multimedia:libs/alsa?expand=0&rev=265
2020-01-22 14:27:43 +00:00

3142 lines
82 KiB
Diff

From aa1bac2d04bd1fb4ccae96a1136e60454298a710 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Mon, 16 Dec 2019 14:26:31 +0100
Subject: [PATCH 43/63] topology: add snd_tplg_save()
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
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 <ctype.h>
+#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 <perex@perex.cz>
+*/
+
+#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