[alsa-devel] [PATCH 0/7] topology: Add support for vendor tuples
From: Mengdong Lin mengdong.lin@linux.intel.com
This series addes support for vendor tuples to topology, to avoid importing binary data blob from other files.
Backward compatibility of ABI is not impacted. A kernel patch is also submitted "ASoC: topology: ABI - Define types for vendor tuples".
The 1st patch is small code cleanup. The 2nd patch is a preparation, since tuples will need the type-specific free handler.
Mengdong Lin (7): topology: Use the generic pointer to free an element's object topology: Define a free handler for the element topology: Add doc for vendor tuples topology: ABI - Define types for vendor tuples topology: Add support for vendor tokens topology: Add support for parsing vendor tuples topology: Build data objects with tuples
include/sound/asoc.h | 42 ++++- include/topology.h | 79 +++++++- src/topology/data.c | 470 +++++++++++++++++++++++++++++++++++++++++++++- src/topology/elem.c | 15 +- src/topology/parser.c | 24 +++ src/topology/tplg_local.h | 47 +++++ 6 files changed, 669 insertions(+), 8 deletions(-)
From: Mengdong Lin mengdong.lin@linux.intel.com
The element is a wrapper for different types of objects.So use the generic pointer 'obj' instead of the type-specific pointer to free the object.
Signed-off-by: Mengdong Lin mengdong.lin@linux.intel.com
diff --git a/src/topology/elem.c b/src/topology/elem.c index 12d6a72..00f9eea 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -83,8 +83,8 @@ void tplg_elem_free(struct tplg_elem *elem) /* free struct snd_tplg_ object, * the union pointers share the same address */ - if (elem->mixer_ctrl) - free(elem->mixer_ctrl); + if (elem->obj) + free(elem->obj);
free(elem); }
From: Mengdong Lin mengdong.lin@linux.intel.com
This handler is defined for type-specific destruction of an element.
Signed-off-by: Mengdong Lin mengdong.lin@linux.intel.com
diff --git a/src/topology/elem.c b/src/topology/elem.c index 00f9eea..f2afaaf 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -83,8 +83,12 @@ void tplg_elem_free(struct tplg_elem *elem) /* free struct snd_tplg_ object, * the union pointers share the same address */ - if (elem->obj) + if (elem->obj) { + if (elem->free) + elem->free(elem->obj); + free(elem->obj); + }
free(elem); } diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 4915b1a..7368a86 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -127,6 +127,8 @@ struct tplg_elem { */ struct list_head ref_list; struct list_head list; /* list of all elements with same type */ + + void (*free)(void *obj); };
struct map_elem {
From: Mengdong Lin mengdong.lin@linux.intel.com
Describe how to define vendor tokens and tuples in the text conf file.
Signed-off-by: Mengdong Lin mengdong.lin@linux.intel.com
diff --git a/include/topology.h b/include/topology.h index 011f6ae..51d282f 100644 --- a/include/topology.h +++ b/include/topology.h @@ -203,12 +203,77 @@ extern "C" { * bytes "0x12,0x34,0x56,0x78" * shorts "0x1122,0x3344,0x5566,0x7788" * words "0xaabbccdd,0x11223344,0x66aa77bb,0xefef1234" + * tuples "section id of the vendor tuples" * }; * </pre> - * The file, bytes, shorts and words keywords are all mutually exclusive as - * the private data should only be taken from one source. The private data can - * either be read from a separate file or defined in the topology file using - * the bytes, shorts or words keywords. + * The file, bytes, shorts, words and tuples keywords are all mutually + * exclusive as the private data should only be taken from one source. + * The private data can either be read from a separate file or defined in + * the topology file using the bytes, shorts, words or tuples keywords. + * The keyword tuples is to define vendor specific tuples. Please refer to + * section Vendor Tokens and Vendor tuples. + * + * <h6>Vendor Tokens</h6> + * A vendor token list is defined as a new section. Each token element is + * a pair of string ID and integer value. And both the ID and value are + * vendor-specific. + * + * <pre> + * SectionVendorTokens."id of the vendor tokens" { + * comment "optional comments" + * VENDOR_TOKEN_ID1 "1" + * VENDOR_TOKEN_ID2 "2" + * VENDOR_TOKEN_ID3 "3" + * ... + * } + * </pre> + * + * <h6>Vendor Tuples</h6> + * Vendor tuples are defined as a new section. It contains a reference to + * a vendor token list and several tuple arrays. + * All arrays share a vendor token list, defined by the tokens keyword. + * Each tuple array is for a specific type, defined by the string following + * the tuples keyword. Supported types are: string, uuid, bool, byte, + * short and word. + * + * <pre> + * SectionVendorTuples."id of the vendor tuples" { + * tokens "id of the vendor tokens" + * + * tuples."string" { + * VENDOR_TOKEN_ID1 "character string" + * ... + * } + * + * tuples."uuid" { + * VENDOR_TOKEN_ID2 "16 character uuid" + * ... + * } + * + * tuples."bool" { + * VENDOR_TOKEN_ID3 "true/false" + * ... + * } + * + * tuples."byte" { + * VENDOR_TOKEN_ID4 "0x11" + * VENDOR_TOKEN_ID5 "0x22" + * ... + * } + * + * tuples."short" { + * VENDOR_TOKEN_ID6 "0x1122" + * VENDOR_TOKEN_ID7 "0x3344" + * ... + * } + * + * tuples."word" { + * VENDOR_TOKEN_ID8 "0x11223344" + * VENDOR_TOKEN_ID9 "0x55667788" + * ... + * } + * } + * </pre> * * <h5>Mixer Controls</h5> * A mixer control is defined as a new section that can include channel mapping, @@ -389,6 +454,10 @@ extern "C" { * fields are the same for widgets as they are for controls whilst the other * fields map on very closely to the driver widget fields. * + * <h5>Widget Private Data</h5> + * Widget can have private data. For the format of the private data, please + * refer to section Control Private Data. + * * <h4>PCM Capabilities</h4> * Topology can also define the capabilities of FE and BE PCMs. Capabilities * can be defined with the following section :-
From: Mengdong Lin mengdong.lin@linux.intel.com
Tuples, a pair of token and value, can be used to define vendor specific data, for controls and widgets. This can avoid importing binary data blob from other files.
Vendor specific tuple arrays will be embeded in the private data buffer of a control or widget object. To be backward compatible, union is used to define the tuple arrays in the existing private data ABI object 'struct snd_soc_tplg_private'.
Vendors need to make sure the token values defined by the topology conf file match those defined by their driver.
Now supported tuple types are uuid, string, bool, byte, short and word.
Signed-off-by: Mengdong Lin mengdong.lin@linux.intel.com
diff --git a/include/sound/asoc.h b/include/sound/asoc.h index a29c05c..920c9e0 100644 --- a/include/sound/asoc.h +++ b/include/sound/asoc.h @@ -107,6 +107,14 @@ #define SND_SOC_TPLG_STREAM_PLAYBACK 0 #define SND_SOC_TPLG_STREAM_CAPTURE 1
+/* vendor tuple types */ +#define SND_SOC_TPLG_TUPLE_TYPE_UUID 0 +#define SND_SOC_TPLG_TUPLE_TYPE_STRING 1 +#define SND_SOC_TPLG_TUPLE_TYPE_BOOL 2 +#define SND_SOC_TPLG_TUPLE_TYPE_BYTE 3 +#define SND_SOC_TPLG_TUPLE_TYPE_WORD 4 +#define SND_SOC_TPLG_TUPLE_TYPE_SHORT 5 + /* * Block Header. * This header precedes all object and object arrays below. @@ -123,6 +131,35 @@ struct snd_soc_tplg_hdr { __le32 count; /* number of elements in block */ } __attribute__((packed));
+/* vendor tuple for uuid */ +struct snd_soc_tplg_vendor_uuid_elem { + __le32 token; + char uuid[16]; +} __attribute__((packed)); + +/* vendor tuple for a bool/byte/short/word value */ +struct snd_soc_tplg_vendor_value_elem { + __le32 token; + __le32 value; +} __attribute__((packed)); + +/* vendor tuple for string */ +struct snd_soc_tplg_vendor_string_elem { + __le32 token; + char string[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; +} __attribute__((packed)); + +struct snd_soc_tplg_vendor_array { + __le32 size; /* size in bytes of the array, including all elements */ + __le32 type; /* SND_SOC_TPLG_TUPLE_TYPE_ */ + __le32 num_elems; /* number of elements in array */ + union { + struct snd_soc_tplg_vendor_uuid_elem uuid[0]; + struct snd_soc_tplg_vendor_value_elem value[0]; + struct snd_soc_tplg_vendor_string_elem string[0]; + }; +} __attribute__((packed)); + /* * Private data. * All topology objects may have private data that can be used by the driver or @@ -130,7 +167,10 @@ struct snd_soc_tplg_hdr { */ struct snd_soc_tplg_private { __le32 size; /* in bytes of private data */ - char data[0]; + union { + char data[0]; + struct snd_soc_tplg_vendor_array array[0]; + }; } __attribute__((packed));
/*
From: Mengdong Lin mengdong.lin@linux.intel.com
Vendor can define a token list in SectionVendorTokens. Each token element is a pair of string ID and integer value. And both the ID and value are vendor-specific.
Signed-off-by: Mengdong Lin mengdong.lin@linux.intel.com
diff --git a/include/topology.h b/include/topology.h index 51d282f..0df112c 100644 --- a/include/topology.h +++ b/include/topology.h @@ -578,6 +578,7 @@ enum snd_tplg_type { SND_TPLG_TYPE_BE, /*!< BE DAI link */ SND_TPLG_TYPE_CC, /*!< Hostless codec <-> codec link */ SND_TPLG_TYPE_MANIFEST, /*!< Topology manifest */ + SND_TPLG_TYPE_TOKEN, /*!< Vendor tokens */ };
/** diff --git a/src/topology/data.c b/src/topology/data.c index 370c0fa..8455c15 100644 --- a/src/topology/data.c +++ b/src/topology/data.c @@ -253,6 +253,56 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, return ret; }
+/* Parse vendor tokens + */ +int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id, *value; + struct tplg_elem *elem; + struct tplg_vendor_tokens *tokens; + int num_tokens = 0; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_TOKEN); + if (!elem) + return -ENOMEM; + + snd_config_for_each(i, next, cfg) { + num_tokens++; + } + + if (!num_tokens) + return 0; + + tplg_dbg(" Vendor tokens: %s, %d tokens\n", elem->id, num_tokens); + + tokens = calloc(1, sizeof(*tokens) + + num_tokens * sizeof(struct tplg_token)); + if (!tokens) + return -ENOMEM; + elem->tokens = tokens; + + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + if (snd_config_get_string(n, &value) < 0) + continue; + + elem_copy_text(tokens->token[tokens->num_tokens].id, id, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + tokens->token[tokens->num_tokens].value = atoi(value); + tplg_dbg("\t\t %s : %d\n", tokens->token[tokens->num_tokens].id, + tokens->token[tokens->num_tokens].value); + tokens->num_tokens++; + } + + return 0; +}
/* Parse Private data. * diff --git a/src/topology/elem.c b/src/topology/elem.c index f2afaaf..95e3fd4 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -193,6 +193,9 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, list_add_tail(&elem->list, &tplg->cc_list); obj_size = sizeof(struct snd_soc_tplg_link_config); break; + case SND_TPLG_TYPE_TOKEN: + list_add_tail(&elem->list, &tplg->token_list); + break; default: free(elem); return NULL; diff --git a/src/topology/parser.c b/src/topology/parser.c index 4546257..264abc8 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -173,6 +173,14 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) continue; }
+ if (strcmp(id, "SectionVendorTokens") == 0) { + err = tplg_parse_compound(tplg, n, tplg_parse_tokens, + NULL); + if (err < 0) + return err; + continue; + } + SNDERR("error: unknown section %s\n", id); } return 0; @@ -407,6 +415,7 @@ snd_tplg_t *snd_tplg_new(void) INIT_LIST_HEAD(&tplg->mixer_list); INIT_LIST_HEAD(&tplg->enum_list); INIT_LIST_HEAD(&tplg->bytes_ext_list); + INIT_LIST_HEAD(&tplg->token_list);
return tplg; } @@ -426,6 +435,7 @@ void snd_tplg_free(snd_tplg_t *tplg) tplg_elem_free_list(&tplg->mixer_list); tplg_elem_free_list(&tplg->enum_list); tplg_elem_free_list(&tplg->bytes_ext_list); + tplg_elem_free_list(&tplg->token_list);
free(tplg); } diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 7368a86..679bfff 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -69,6 +69,7 @@ struct snd_tplg { struct list_head route_list; struct list_head text_list; struct list_head pdata_list; + struct list_head token_list; struct list_head pcm_config_list; struct list_head pcm_caps_list;
@@ -86,6 +87,16 @@ struct tplg_ref { struct list_head list; };
+/* element for vendor tokens */ +struct tplg_token { + char id[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + unsigned int value; +}; + +struct tplg_vendor_tokens { + unsigned int num_tokens; + struct tplg_token token[0]; +}; /* topology element */ struct tplg_elem {
@@ -118,6 +129,7 @@ struct tplg_elem { /* these do not map to UAPI structs but are internal only */ struct snd_soc_tplg_ctl_tlv *tlv; struct snd_soc_tplg_private *data; + struct tplg_vendor_tokens *tokens; };
/* an element may refer to other elements: @@ -151,6 +163,9 @@ int tplg_parse_text(snd_tplg_t *tplg, snd_config_t *cfg, int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED); + int tplg_parse_control_bytes(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
From: Mengdong Lin mengdong.lin@linux.intel.com
Vendor can define several tuple arrays in 'SectionVendorTuples', as well as the reference to a vendor token list object.
A later patche will copy vendor tuples in ABI format to the private buffer of its parent data object in the building phase.
Signed-off-by: Mengdong Lin mengdong.lin@linux.intel.com
diff --git a/include/topology.h b/include/topology.h index 0df112c..b47f422 100644 --- a/include/topology.h +++ b/include/topology.h @@ -579,6 +579,7 @@ enum snd_tplg_type { SND_TPLG_TYPE_CC, /*!< Hostless codec <-> codec link */ SND_TPLG_TYPE_MANIFEST, /*!< Topology manifest */ SND_TPLG_TYPE_TOKEN, /*!< Vendor tokens */ + SND_TPLG_TYPE_TUPLE, /*!< Vendor tuples */ };
/** diff --git a/src/topology/data.c b/src/topology/data.c index 8455c15..0d07842 100644 --- a/src/topology/data.c +++ b/src/topology/data.c @@ -253,6 +253,144 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, return ret; }
+static int parse_tuple_set(snd_tplg_t *tplg, snd_config_t *cfg, + struct tplg_tuple_set **s) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id, *value; + struct tplg_tuple_set *set; + unsigned int type, num_tuples = 0; + struct tplg_tuple *tuple; + + snd_config_get_id(cfg, &id); + + if (strcmp(id, "uuid") == 0) + type = SND_SOC_TPLG_TUPLE_TYPE_UUID; + else if (strcmp(id, "string") == 0) + type = SND_SOC_TPLG_TUPLE_TYPE_STRING; + else if (strcmp(id, "bool") == 0) + type = SND_SOC_TPLG_TUPLE_TYPE_BOOL; + else if (strcmp(id, "byte") == 0) + type = SND_SOC_TPLG_TUPLE_TYPE_BYTE; + else if (strcmp(id, "short") == 0) + type = SND_SOC_TPLG_TUPLE_TYPE_SHORT; + else if (strcmp(id, "word") == 0) + type = SND_SOC_TPLG_TUPLE_TYPE_WORD; + else { + SNDERR("error: invalid tuple type '%s'\n", id); + return -EINVAL; + } + + snd_config_for_each(i, next, cfg) + num_tuples++; + if (!num_tuples) + return 0; + + tplg_dbg("\t %d %s tuples:\n", num_tuples, id); + set = calloc(1, sizeof(*set) + num_tuples * sizeof(struct tplg_tuple)); + if (!set) + return -ENOMEM; + + set->type = type; + + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + + /* get id */ + if (snd_config_get_id(n, &id) < 0) + continue; + + /* get value */ + if (snd_config_get_string(n, &value) < 0) + continue; + + tuple = &set->tuple[set->num_tuples]; + elem_copy_text(tuple->token, id, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + switch (type) { + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + memcpy(tuple->uuid, value, 16); + tplg_dbg("\t\t%s = %s\n", tuple->token, tuple->uuid); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + elem_copy_text(tuple->string, value, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + tplg_dbg("\t\t%s = %s\n", tuple->token, tuple->string); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_BOOL: + if (strcmp(value, "true") == 0) + tuple->value = 1; + tplg_dbg("\t\t%s = %d\n", tuple->token, tuple->value); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_BYTE: + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + case SND_SOC_TPLG_TUPLE_TYPE_WORD: + tuple->value = atoi(value); + tplg_dbg("\t\t%s = %d\n", tuple->token, tuple->value); + break; + + default: + break; + } + + set->num_tuples++; + } + + *s = set; + return 0; +} + +static int parse_tuple_sets(snd_tplg_t *tplg, snd_config_t *cfg, + struct tplg_vendor_tuples *tuples) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id; + unsigned int num_tuple_sets = 0; + int err; + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("error: compound type expected for %s", id); + return -EINVAL; + } + + snd_config_for_each(i, next, cfg) { + num_tuple_sets++; + } + + if (!num_tuple_sets) + return 0; + + tuples->set = calloc(1, num_tuple_sets * sizeof(void *)); + if (!tuples->set) + return -ENOMEM; + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("error: compound type expected for %s, is %d", + id, snd_config_get_type(n)); + return -EINVAL; + } + + err = parse_tuple_set(tplg, n, &tuples->set[tuples->num_sets]); + if (err < 0) + return err; + + /* overlook empty tuple sets */ + if (tuples->set[tuples->num_sets]) + tuples->num_sets++; + } + + return 0; +} + /* Parse vendor tokens */ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, @@ -304,10 +442,69 @@ int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, return 0; }
+/* Parse vendor tuples. + */ +int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id, *value; + struct tplg_elem *elem; + struct tplg_vendor_tuples *tuples; + int err; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_TUPLE); + if (!elem) + return -ENOMEM; + + tplg_dbg(" Vendor Tuples: %s\n", elem->id); + + tuples = calloc(1, sizeof(*tuples)); + if (!tuples) + return -ENOMEM; + elem->tuples = tuples; + + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + if (strcmp(id, "tokens") == 0) { + if (snd_config_get_string(n, &value) < 0) + return -EINVAL; + tplg_ref_add(elem, SND_TPLG_TYPE_TOKEN, value); + tplg_dbg("\t refer to vendor tokens: %s\n", value); + } + + if (strcmp(id, "tuples") == 0) { + err = parse_tuple_sets(tplg, n, tuples); + if (err < 0) + return err; + } + } + + return 0; +} + +/* Free handler of tuples */ +void tplg_free_tuples(void *obj) +{ + struct tplg_vendor_tuples *tuples = (struct tplg_vendor_tuples *)obj; + int i; + + if (!tuples) + return; + + for (i = 0; i < tuples->num_sets; i++) + free(tuples->set[i]); +} + /* Parse Private data. * * Object private data can either be from file or defined as bytes, shorts, - * words. + * words, tuples. */ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) @@ -365,6 +562,14 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, continue; }
+ if (strcmp(id, "tuples") == 0) { + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; + tplg_dbg(" Data: %s\n", val); + tplg_ref_add(elem, SND_TPLG_TYPE_TUPLE, val); + continue; + } + if (strcmp(id, "index") == 0) { if (snd_config_get_string(n, &val) < 0) return -EINVAL; diff --git a/src/topology/elem.c b/src/topology/elem.c index 95e3fd4..50414f0 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -196,6 +196,10 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, case SND_TPLG_TYPE_TOKEN: list_add_tail(&elem->list, &tplg->token_list); break; + case SND_TPLG_TYPE_TUPLE: + list_add_tail(&elem->list, &tplg->tuple_list); + elem->free = tplg_free_tuples; + break; default: free(elem); return NULL; diff --git a/src/topology/parser.c b/src/topology/parser.c index 264abc8..0d967b4 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -181,6 +181,14 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) continue; }
+ if (strcmp(id, "SectionVendorTuples") == 0) { + err = tplg_parse_compound(tplg, n, tplg_parse_tuples, + NULL); + if (err < 0) + return err; + continue; + } + SNDERR("error: unknown section %s\n", id); } return 0; @@ -416,6 +424,7 @@ snd_tplg_t *snd_tplg_new(void) INIT_LIST_HEAD(&tplg->enum_list); INIT_LIST_HEAD(&tplg->bytes_ext_list); INIT_LIST_HEAD(&tplg->token_list); + INIT_LIST_HEAD(&tplg->tuple_list);
return tplg; } @@ -436,6 +445,7 @@ void snd_tplg_free(snd_tplg_t *tplg) tplg_elem_free_list(&tplg->enum_list); tplg_elem_free_list(&tplg->bytes_ext_list); tplg_elem_free_list(&tplg->token_list); + tplg_elem_free_list(&tplg->tuple_list);
free(tplg); } diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 679bfff..4d59a1f 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -70,6 +70,7 @@ struct snd_tplg { struct list_head text_list; struct list_head pdata_list; struct list_head token_list; + struct list_head tuple_list; struct list_head pcm_config_list; struct list_head pcm_caps_list;
@@ -97,6 +98,28 @@ struct tplg_vendor_tokens { unsigned int num_tokens; struct tplg_token token[0]; }; + +/* element for vendor tuples */ +struct tplg_tuple { + char token[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + union { + char string[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + char uuid[16]; + unsigned int value; + }; +}; + +struct tplg_tuple_set { + unsigned int type; /* uuid, bool, byte, short, word, string*/ + unsigned int num_tuples; + struct tplg_tuple tuple[0]; +}; + +struct tplg_vendor_tuples { + unsigned int num_sets; + struct tplg_tuple_set **set; +}; + /* topology element */ struct tplg_elem {
@@ -130,6 +153,7 @@ struct tplg_elem { struct snd_soc_tplg_ctl_tlv *tlv; struct snd_soc_tplg_private *data; struct tplg_vendor_tokens *tokens; + struct tplg_vendor_tuples *tuples; };
/* an element may refer to other elements: @@ -166,6 +190,11 @@ int tplg_parse_data(snd_tplg_t *tplg, snd_config_t *cfg, int tplg_parse_tokens(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+int tplg_parse_tuples(snd_tplg_t *tplg, snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED); + +void tplg_free_tuples(void *obj); + int tplg_parse_control_bytes(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
On Thu, 24 Mar 2016 04:07:36 +0100, mengdong.lin@linux.intel.com wrote:
+static int parse_tuple_set(snd_tplg_t *tplg, snd_config_t *cfg,
- struct tplg_tuple_set **s)
....
switch (type) {
case SND_SOC_TPLG_TUPLE_TYPE_UUID:
memcpy(tuple->uuid, value, 16);
This may become out-of-bound access. Check the size of value string beforehand.
tplg_dbg("\t\t%s = %s\n", tuple->token, tuple->uuid);
break;
case SND_SOC_TPLG_TUPLE_TYPE_STRING:
elem_copy_text(tuple->string, value,
SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
tplg_dbg("\t\t%s = %s\n", tuple->token, tuple->string);
break;
case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
if (strcmp(value, "true") == 0)
tuple->value = 1;
tplg_dbg("\t\t%s = %d\n", tuple->token, tuple->value);
break;
case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
case SND_SOC_TPLG_TUPLE_TYPE_WORD:
tuple->value = atoi(value);
atoi() isn't good enough. It can't handle a hex number, for example, and can't give an error.
+/* Free handler of tuples */ +void tplg_free_tuples(void *obj) +{
- struct tplg_vendor_tuples *tuples = (struct tplg_vendor_tuples *)obj;
- int i;
- if (!tuples)
return;
- for (i = 0; i < tuples->num_sets; i++)
free(tuples->set[i]);
+}
tuples->set itself isn't freed?
Takashi
On 03/29/2016 10:13 PM, Takashi Iwai wrote:
On Thu, 24 Mar 2016 04:07:36 +0100, mengdong.lin@linux.intel.com wrote:
+static int parse_tuple_set(snd_tplg_t *tplg, snd_config_t *cfg,
- struct tplg_tuple_set **s)
....
switch (type) {
case SND_SOC_TPLG_TUPLE_TYPE_UUID:
memcpy(tuple->uuid, value, 16);
This may become out-of-bound access. Check the size of value string beforehand.
I'll fix this in v2.
tplg_dbg("\t\t%s = %s\n", tuple->token, tuple->uuid);
break;
case SND_SOC_TPLG_TUPLE_TYPE_STRING:
elem_copy_text(tuple->string, value,
SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
tplg_dbg("\t\t%s = %s\n", tuple->token, tuple->string);
break;
case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
if (strcmp(value, "true") == 0)
tuple->value = 1;
tplg_dbg("\t\t%s = %d\n", tuple->token, tuple->value);
break;
case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
case SND_SOC_TPLG_TUPLE_TYPE_WORD:
tuple->value = atoi(value);
atoi() isn't good enough. It can't handle a hex number, for example, and can't give an error.
Sorry. I missed this. I'll use strtol() to handle the data and check on the return value.
+/* Free handler of tuples */ +void tplg_free_tuples(void *obj) +{
- struct tplg_vendor_tuples *tuples = (struct tplg_vendor_tuples *)obj;
- int i;
- if (!tuples)
return;
- for (i = 0; i < tuples->num_sets; i++)
free(tuples->set[i]);
+}
tuples->set itself isn't freed?
This a memory leak. I'll fix this.
Thanks again for your review. I've sent out v2 with the fixes.
Regards Mengdong
From: Mengdong Lin mengdong.lin@linux.intel.com
For data objects with tuples, the parser will bind the vendor tuples and tokens, copy the tuples to the private buffer of its parent data object. Then later the builder will export the vendor tuples as private binary data for the control or widgets objects.
Signed-off-by: Mengdong Lin mengdong.lin@linux.intel.com
diff --git a/src/topology/data.c b/src/topology/data.c index 0d07842..589d48d 100644 --- a/src/topology/data.c +++ b/src/topology/data.c @@ -253,6 +253,198 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, return ret; }
+/* get the token integer value from its id */ +static int get_token_value(const char *token_id, + struct tplg_vendor_tokens *tokens) +{ + int i; + + for (i = 0; i < tokens->num_tokens; i++) { + if (strcmp(token_id, tokens->token[i].id) == 0) + return tokens->token[i].value; + } + + SNDERR("error: cannot find token id '%s'\n", token_id); + return -1; +} + +/* get the vendor tokens referred by the vendor tuples */ +static struct tplg_elem *get_tokens(snd_tplg_t *tplg, struct tplg_elem *elem) +{ + struct tplg_ref *ref; + struct list_head *base, *pos; + int err = 0; + + base = &elem->ref_list; + list_for_each(pos, base) { + + ref = list_entry(pos, struct tplg_ref, list); + + if (!ref->id || ref->type != SND_TPLG_TYPE_TOKEN) + continue; + + if (!ref->elem) { + ref->elem = tplg_elem_lookup(&tplg->token_list, + ref->id, SND_TPLG_TYPE_TOKEN); + } + + return ref->elem; + } + + return NULL; +} + +/* check if a data element has tuples */ +static bool has_tuples(struct tplg_elem *elem) +{ + struct tplg_ref *ref; + struct list_head *base, *pos; + int err = 0; + + base = &elem->ref_list; + list_for_each(pos, base) { + + ref = list_entry(pos, struct tplg_ref, list); + if (ref->id && ref->type == SND_TPLG_TYPE_TUPLE) + return true; + } + + return false; +} + +/* get size of a tuple element from its type */ +static unsigned int get_tuple_size(int type) +{ + switch (type) { + + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + return sizeof(struct snd_soc_tplg_vendor_uuid_elem); + + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + return sizeof(struct snd_soc_tplg_vendor_string_elem); + + default: + return sizeof(struct snd_soc_tplg_vendor_value_elem); + } +} + +/* fill a data element's private buffer with its tuples */ +static int copy_tuples(struct tplg_elem *elem, + struct tplg_vendor_tuples *tuples, struct tplg_vendor_tokens *tokens) +{ + struct snd_soc_tplg_private *priv = elem->data; + struct tplg_tuple_set *tuple_set; + struct tplg_tuple *tuple; + struct snd_soc_tplg_vendor_array *array; + struct snd_soc_tplg_vendor_uuid_elem *uuid; + struct snd_soc_tplg_vendor_string_elem *string; + struct snd_soc_tplg_vendor_value_elem *value; + int set_size, size, off; + int i, j, token_val; + + if (priv) { + SNDERR("error: %s has more data than tuples\n", elem->id); + return -EINVAL; + } + + size = 0; + for (i = 0; i < tuples->num_sets ; i++) { + tuple_set = tuples->set[i]; + set_size = sizeof(struct snd_soc_tplg_vendor_array) + + get_tuple_size(tuple_set->type) + * tuple_set->num_tuples; + size += set_size; + if (size > TPLG_MAX_PRIV_SIZE) { + SNDERR("error: data too big %d\n", size); + return -EINVAL; + } + + if (priv != NULL) + priv = realloc(priv, sizeof(*priv) + size); + else + priv = calloc(1, sizeof(*priv) + size); + if (!priv) + return -ENOMEM; + + off = priv->size; + priv->size = size; + + array = (struct snd_soc_tplg_vendor_array *)(priv->data + off); + array->size = set_size; + array->type = tuple_set->type; + array->num_elems = tuple_set->num_tuples; + + /* fill the private data buffer */ + for (j = 0; j < tuple_set->num_tuples; j++) { + tuple = &tuple_set->tuple[j]; + token_val = get_token_value(tuple->token, tokens); + if (token_val < 0) + return -EINVAL; + + switch (tuple_set->type) { + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + uuid = &array->uuid[j]; + uuid->token = token_val; + memcpy(uuid->uuid, tuple->uuid, 16); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + string = &array->string[j]; + string->token = token_val; + elem_copy_text(string->string, tuple->string, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + break; + + default: + value = &array->value[j]; + value->token = token_val; + value->value = tuple->value; + break; + } + } + } + + elem->data = priv; + return 0; +} + +/* build a data element from its tuples */ +static int build_tuples(snd_tplg_t *tplg, struct tplg_elem *elem) +{ + struct tplg_ref *ref; + struct list_head *base, *pos; + struct tplg_elem *tuples, *tokens; + + base = &elem->ref_list; + list_for_each(pos, base) { + + ref = list_entry(pos, struct tplg_ref, list); + + if (!ref->id || ref->type != SND_TPLG_TYPE_TUPLE) + continue; + + tplg_dbg("look up tuples %s\n", ref->id); + + if (!ref->elem) + ref->elem = tplg_elem_lookup(&tplg->tuple_list, + ref->id, SND_TPLG_TYPE_TUPLE); + tuples = ref->elem; + if (!tuples) + return -EINVAL; + + tplg_dbg("found tuples %s\n", tuples->id); + tokens = get_tokens(tplg, tuples); + if (!tokens) + return -EINVAL; + + tplg_dbg("found tokens %s\n", tokens->id); + /* a data object can only have one tuples object */ + return copy_tuples(elem, tuples->tuples, tokens->tokens); + } + + return 0; +} + static int parse_tuple_set(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_tuple_set **s) { @@ -650,3 +842,24 @@ int tplg_copy_data(struct tplg_elem *elem, struct tplg_elem *ref) memcpy(priv->data, ref->data->data, priv_data_size); return 0; } + +/* check data objects and build those with tuples */ +int tplg_build_data(snd_tplg_t *tplg) +{ + struct list_head *base, *pos; + struct tplg_elem *elem; + int err = 0; + + base = &tplg->pdata_list; + list_for_each(pos, base) { + + elem = list_entry(pos, struct tplg_elem, list); + if (has_tuples(elem)) { + err = build_tuples(tplg, elem); + if (err < 0) + return err; + } + } + + return 0; +} diff --git a/src/topology/parser.c b/src/topology/parser.c index 0d967b4..30d91f9 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -242,6 +242,10 @@ static int tplg_build_integ(snd_tplg_t *tplg) { int err;
+ err = tplg_build_data(tplg); + if (err < 0) + return err; + err = tplg_build_controls(tplg); if (err < 0) return err; diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 4d59a1f..4c601d4 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -222,6 +222,7 @@ int tplg_parse_be(snd_tplg_t *tplg, int tplg_parse_cc(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED);
+int tplg_build_data(snd_tplg_t *tplg); int tplg_build_controls(snd_tplg_t *tplg); int tplg_build_widgets(snd_tplg_t *tplg); int tplg_build_routes(snd_tplg_t *tplg);
On Thu, Mar 24, 2016 at 11:05:29AM +0800, mengdong.lin@linux.intel.com wrote:
From: Mengdong Lin mengdong.lin@linux.intel.com
This series addes support for vendor tuples to topology, to avoid importing binary data blob from other files.
Backward compatibility of ABI is not impacted. A kernel patch is also submitted "ASoC: topology: ABI - Define types for vendor tuples".
It's now been three kernel releases since the original code was merged and we've still not seen any effort to make the ABI actually available. Where are we with that? I've mentioned it a few times previously but don't really recall any response.
On 03/24/2016 07:20 PM, Mark Brown wrote:
On Thu, Mar 24, 2016 at 11:05:29AM +0800, mengdong.lin@linux.intel.com wrote:
From: Mengdong Lin mengdong.lin@linux.intel.com
This series addes support for vendor tuples to topology, to avoid importing binary data blob from other files.
Backward compatibility of ABI is not impacted. A kernel patch is also submitted "ASoC: topology: ABI - Define types for vendor tuples".
It's now been three kernel releases since the original code was merged and we've still not seen any effort to make the ABI actually available. Where are we with that? I've mentioned it a few times previously but don't really recall any response.
We've been working on this. I'm sorry we're still not fast enough.
This ABI update is introduced by previous failure to upstream the topology configuration file for Skylake, which imports vendor-specific binary data from another file. It was refused because of the license of the binary data is not obvious. So we turn to use vendor tuples, because the vendor tuples can show the layout and meaning of binary data, which can help to check if the data is safe to open source. This ABI change has no impact on backward compatibility.
Also, the audio device driver for new platforms are under development now, which uses topology for DAI and DAI links. We're checking if current ABI can meet all requirements for the new platforms. This may still need a few weeks.
Thanks Mengdong
Hi Takashi and Mark,
Despite the ABI update, could you help to review if this is a doable solution to define vendor-specific data by tuples?
Previously there was a patch for Skylake topology conf file, but this file try to import vendor specific config data as a binary blob from other files. The patch was refused for on license of the binary blob and we were suggested to expose the layout of the data. This patch series is to meet this request, providing a way for vendors to define their structured private data.
Here is the link of the refused patch: [alsa-devel] [PATCH] conf: topology: Add topolgy for skylake i2s configuration
http://mailman.alsa-project.org/pipermail/alsa-devel/2016-February/104099.ht...
Thanks Mengdong
On 03/25/2016 01:39 PM, Mengdong Lin wrote:
http://mailman.alsa-project.org/pipermail/alsa-devel/2016-February/104099.ht... On 03/24/2016 07:20 PM, Mark Brown wrote:
On Thu, Mar 24, 2016 at 11:05:29AM +0800, mengdong.lin@linux.intel.com wrote:
From: Mengdong Lin mengdong.lin@linux.intel.com
This series addes support for vendor tuples to topology, to avoid importing binary data blob from other files.
Backward compatibility of ABI is not impacted. A kernel patch is also submitted "ASoC: topology: ABI - Define types for vendor tuples".
It's now been three kernel releases since the original code was merged and we've still not seen any effort to make the ABI actually available. Where are we with that? I've mentioned it a few times previously but don't really recall any response.
We've been working on this. I'm sorry we're still not fast enough.
This ABI update is introduced by previous failure to upstream the topology configuration file for Skylake, which imports vendor-specific binary data from another file. It was refused because of the license of the binary data is not obvious. So we turn to use vendor tuples, because the vendor tuples can show the layout and meaning of binary data, which can help to check if the data is safe to open source. This ABI change has no impact on backward compatibility.
Also, the audio device driver for new platforms are under development now, which uses topology for DAI and DAI links. We're checking if current ABI can meet all requirements for the new platforms. This may still need a few weeks.
Thanks Mengdong
On Mon, 28 Mar 2016 09:49:36 +0200, Mengdong Lin wrote:
Hi Takashi and Mark,
Despite the ABI update, could you help to review if this is a doable solution to define vendor-specific data by tuples?
I'll check it tomorrow, as now it's still in Easter holidays.
Previously there was a patch for Skylake topology conf file, but this file try to import vendor specific config data as a binary blob from other files. The patch was refused for on license of the binary blob and we were suggested to expose the layout of the data. This patch series is to meet this request, providing a way for vendors to define their structured private data.
Here is the link of the refused patch: [alsa-devel] [PATCH] conf: topology: Add topolgy for skylake i2s configuration
http://mailman.alsa-project.org/pipermail/alsa-devel/2016-February/104099.ht...
The changes are good if they are mandatory. But too late for 4.6, in anyway. And, the missing piece is the "grand plan". When this API/ABI is supposed to be stabilized and released?
thanks,
Takashi
Thanks Mengdong
On 03/25/2016 01:39 PM, Mengdong Lin wrote:
http://mailman.alsa-project.org/pipermail/alsa-devel/2016-February/104099.ht... On 03/24/2016 07:20 PM, Mark Brown wrote:
On Thu, Mar 24, 2016 at 11:05:29AM +0800, mengdong.lin@linux.intel.com wrote:
From: Mengdong Lin mengdong.lin@linux.intel.com
This series addes support for vendor tuples to topology, to avoid importing binary data blob from other files.
Backward compatibility of ABI is not impacted. A kernel patch is also submitted "ASoC: topology: ABI - Define types for vendor tuples".
It's now been three kernel releases since the original code was merged and we've still not seen any effort to make the ABI actually available. Where are we with that? I've mentioned it a few times previously but don't really recall any response.
We've been working on this. I'm sorry we're still not fast enough.
This ABI update is introduced by previous failure to upstream the topology configuration file for Skylake, which imports vendor-specific binary data from another file. It was refused because of the license of the binary data is not obvious. So we turn to use vendor tuples, because the vendor tuples can show the layout and meaning of binary data, which can help to check if the data is safe to open source. This ABI change has no impact on backward compatibility.
Also, the audio device driver for new platforms are under development now, which uses topology for DAI and DAI links. We're checking if current ABI can meet all requirements for the new platforms. This may still need a few weeks.
Thanks Mengdong
On 03/28/2016 03:57 PM, Takashi Iwai wrote:
On Mon, 28 Mar 2016 09:49:36 +0200, Mengdong Lin wrote:
Hi Takashi and Mark,
Despite the ABI update, could you help to review if this is a doable solution to define vendor-specific data by tuples?
I'll check it tomorrow, as now it's still in Easter holidays.
Thank you very much!
Previously there was a patch for Skylake topology conf file, but this file try to import vendor specific config data as a binary blob from other files. The patch was refused for on license of the binary blob and we were suggested to expose the layout of the data. This patch series is to meet this request, providing a way for vendors to define their structured private data.
Here is the link of the refused patch: [alsa-devel] [PATCH] conf: topology: Add topolgy for skylake i2s configuration
http://mailman.alsa-project.org/pipermail/alsa-devel/2016-February/104099.ht...
The changes are good if they are mandatory. But too late for 4.6, in anyway. And, the missing piece is the "grand plan". When this API/ABI is supposed to be stabilized and released?
We're checking topology feature requests for platforms later than Skylake. We expect to finish the feature collection this or next week. Then we can give a more accurate schedule.
Now the unstable ABI is mainly for ASoC DAI and DAI links. Platforms later than Skylake need them. We're checking the gap between existing ABI and new requests.
Thanks Mengdong
participants (4)
-
Mark Brown
-
Mengdong Lin
-
mengdong.lin@linux.intel.com
-
Takashi Iwai