mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-11-09 13:30:03 -05:00
topology: add snd_tplg_save()
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
4a0efdc873
commit
aa1bac2d04
14 changed files with 2165 additions and 260 deletions
|
|
@ -27,7 +27,8 @@ libatopology_la_SOURCES =\
|
|||
text.c \
|
||||
channel.c \
|
||||
ops.c \
|
||||
elem.c
|
||||
elem.c \
|
||||
save.c
|
||||
|
||||
noinst_HEADERS = tplg_local.h
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
unsigned int i;
|
||||
|
||||
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);
|
||||
for (i = 0; i < ARRAY_SIZE(widget_map); i++) {
|
||||
if ((unsigned int)widget_map[i].id == type)
|
||||
return widget_map[i].name;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
tplg_ref_add(elem, SND_TPLG_TYPE_BYTES, value);
|
||||
tplg_dbg("\t\t %s\n", value);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
/* 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;
|
||||
struct tplg_vendor_tuples *tuples = elem->tuples;
|
||||
unsigned int i;
|
||||
int err = 0;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
tplg_dbg("\tdata: %s\n", val);
|
||||
tplg_ref_add(elem, SND_TPLG_TYPE_DATA, val);
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/* save CC */
|
||||
int tplg_save_cc(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)
|
||||
err = tplg_save_printf(dst, pfx, "}\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
struct audio_hw_format {
|
||||
unsigned int type;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
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",
|
||||
},
|
||||
};
|
||||
|
||||
static int get_audio_hw_format(const char *val)
|
||||
{
|
||||
if (!strlen(val))
|
||||
unsigned int i;
|
||||
|
||||
if (val[0] == '\0')
|
||||
return -EINVAL;
|
||||
|
||||
if (!strcmp(val, "I2S"))
|
||||
return SND_SOC_DAI_FORMAT_I2S;
|
||||
|
||||
if (!strcmp(val, "RIGHT_J"))
|
||||
return SND_SOC_DAI_FORMAT_RIGHT_J;
|
||||
|
||||
if (!strcmp(val, "LEFT_J"))
|
||||
return SND_SOC_DAI_FORMAT_LEFT_J;
|
||||
|
||||
if (!strcmp(val, "DSP_A"))
|
||||
return SND_SOC_DAI_FORMAT_DSP_A;
|
||||
|
||||
if (!strcmp(val, "DSP_B"))
|
||||
return SND_SOC_DAI_FORMAT_DSP_B;
|
||||
|
||||
if (!strcmp(val, "AC97"))
|
||||
return SND_SOC_DAI_FORMAT_AC97;
|
||||
|
||||
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)
|
||||
|
|
|
|||
632
src/topology/save.c
Normal file
632
src/topology/save.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue