summaryrefslogtreecommitdiff
path: root/src/topology/dapm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/topology/dapm.c')
-rw-r--r--src/topology/dapm.c253
1 files changed, 238 insertions, 15 deletions
diff --git a/src/topology/dapm.c b/src/topology/dapm.c
index 88bddca3..9fab2d92 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);
+}