1/*
2  Copyright(c) 2021 Intel Corporation
3  All rights reserved.
4
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of version 2 of the GNU General Public License as
7  published by the Free Software Foundation.
8
9  This program is distributed in the hope that it will be useful, but
10  WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  General Public License for more details.
13
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17  The full GNU General Public License is included in this distribution
18  in the file called LICENSE.GPL.
19*/
20#include "aconfig.h"
21#include <assert.h>
22#include <errno.h>
23#include <limits.h>
24#include <stdio.h>
25#include <alsa/asoundlib.h>
26#include "topology.h"
27#include "pre-processor.h"
28
29bool tplg_class_is_attribute_check(const char *attr, snd_config_t *class_cfg, char *category)
30{
31	snd_config_iterator_t i, next;
32	snd_config_t *cfg, *n;
33	int ret;
34
35	ret = snd_config_search(class_cfg, category, &cfg);
36	if (ret < 0)
37		return false;
38
39	snd_config_for_each(i, next, cfg) {
40		const char *id, *s;
41
42		n = snd_config_iterator_entry(i);
43		if (snd_config_get_id(n, &id) < 0)
44			continue;
45
46		if (snd_config_get_string(n, &s) < 0)
47			continue;
48
49		if (!strcmp(attr, s))
50			return true;
51	}
52
53	return false;
54}
55
56/* check if attribute is mandatory */
57bool tplg_class_is_attribute_mandatory(const char *attr, snd_config_t *class_cfg)
58{
59	return tplg_class_is_attribute_check(attr, class_cfg, "attributes.mandatory");
60}
61
62/* check if attribute is immutable */
63bool tplg_class_is_attribute_immutable(const char *attr, snd_config_t *class_cfg)
64{
65	return tplg_class_is_attribute_check(attr, class_cfg, "attributes.immutable");
66}
67
68/* check if attribute is unique */
69bool tplg_class_is_attribute_unique(const char *attr, snd_config_t *class_cfg)
70{
71	snd_config_t *unique;
72	const char *s;
73	int ret;
74
75	ret = snd_config_search(class_cfg, "attributes.unique", &unique);
76	if (ret < 0)
77		return false;
78
79	if (snd_config_get_string(unique, &s) < 0)
80		return false;
81
82	if (!strcmp(attr, s))
83		return true;
84
85	return false;
86}
87
88/*
89 * Helper function to look up class definition from the Object config.
90 * ex: For an object declaration, Object.Widget.pga.0{}, return the config correspdonding to
91 * Class.Widget.pga{}. Note that input config , "cfg" does not include the "Object" node.
92 */
93snd_config_t *tplg_class_lookup(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg)
94{
95	snd_config_iterator_t first, end;
96	snd_config_t *class, *class_cfg = NULL;
97	const char *class_type, *class_name;
98	char *class_config_id;
99	int ret;
100
101	if (snd_config_get_id(cfg, &class_type) < 0)
102		return NULL;
103
104	first = snd_config_iterator_first(cfg);
105	end = snd_config_iterator_end(cfg);
106
107	if (first == end) {
108		SNDERR("No class name provided for object type: %s\n", class_type);
109		return NULL;
110	}
111
112	class = snd_config_iterator_entry(first);
113
114	if (snd_config_get_id(class, &class_name) < 0)
115		return NULL;
116
117	class_config_id = tplg_snprintf("Class.%s.%s", class_type, class_name);
118	if (!class_config_id)
119		return NULL;
120
121	ret = snd_config_search(tplg_pp->input_cfg, class_config_id, &class_cfg);
122	if (ret < 0)
123		SNDERR("No Class definition found for %s\n", class_config_id);
124
125	free(class_config_id);
126	return class_cfg;
127}
128
129/* find the attribute config by name in the class definition */
130snd_config_t *tplg_class_find_attribute_by_name(struct tplg_pre_processor *tplg_p ATTRIBUTE_UNUSED,
131						snd_config_t *class, const char *name)
132{
133	snd_config_t *attr = NULL;
134	const char *class_id;
135	char *attr_str;
136	int ret;
137
138	if (snd_config_get_id(class, &class_id) < 0)
139		return NULL;
140
141	attr_str = tplg_snprintf("DefineAttribute.%s", name);
142	if (!attr_str)
143		return NULL;
144
145	ret = snd_config_search(class, attr_str, &attr);
146	if (ret < 0)
147		SNDERR("No definition for attribute '%s' in class '%s'\n",
148			name, class_id);
149
150	free(attr_str);
151	return attr;
152}
153
154/* get the name of the attribute that must have a unique value in the object instance */
155const char *tplg_class_get_unique_attribute_name(struct tplg_pre_processor *tplg_pp ATTRIBUTE_UNUSED,
156						 snd_config_t *class)
157{
158	snd_config_t *unique;
159	const char *unique_name, *class_id;
160	int ret;
161
162	if (snd_config_get_id(class, &class_id) < 0)
163		return NULL;
164
165	ret = snd_config_search(class, "attributes.unique", &unique);
166	if (ret < 0) {
167		SNDERR("No unique attribute in class '%s'\n", class_id);
168		return NULL;
169	}
170
171	if (snd_config_get_string(unique, &unique_name) < 0) {
172		SNDERR("Invalid name for unique attribute in class '%s'\n", class_id);
173		return NULL;
174	}
175
176	return unique_name;
177}
178
179/* get attribute type from the definition */
180snd_config_type_t tplg_class_get_attribute_type(struct tplg_pre_processor *tplg_pp ATTRIBUTE_UNUSED,
181						snd_config_t *attr)
182{
183	snd_config_t *type;
184	const char *s;
185	int ret;
186
187	/* default to integer if no type is given */
188	ret = snd_config_search(attr, "type", &type);
189	if (ret < 0)
190		return SND_CONFIG_TYPE_INTEGER;
191
192	ret = snd_config_get_string(type, &s);
193	assert(ret >= 0);
194
195	if (!strcmp(s, "string"))
196		return SND_CONFIG_TYPE_STRING;
197
198	if (!strcmp(s, "compound"))
199		return SND_CONFIG_TYPE_COMPOUND;
200
201	if (!strcmp(s, "real"))
202		return SND_CONFIG_TYPE_REAL;
203
204	if (!strcmp(s, "integer64"))
205		return SND_CONFIG_TYPE_INTEGER64;
206
207	return SND_CONFIG_TYPE_INTEGER;
208}
209
210/* get token_ref for attribute with name attr_name in the class */
211const char *tplg_class_get_attribute_token_ref(struct tplg_pre_processor *tplg_pp ATTRIBUTE_UNUSED,
212					       snd_config_t *class, const char *attr_name)
213{
214	snd_config_t *attributes, *attr, *token_ref;
215	const char *token;
216	int ret;
217
218	ret = snd_config_search(class, "DefineAttribute", &attributes);
219	if (ret < 0)
220		return NULL;
221
222	ret = snd_config_search(attributes, attr_name, &attr);
223	if (ret < 0)
224		return NULL;
225
226	ret = snd_config_search(attr, "token_ref", &token_ref);
227	if (ret < 0)
228		return NULL;
229
230	ret = snd_config_get_string(token_ref, &token);
231	if (ret < 0)
232		return NULL;
233
234	return token;
235}
236
237/* convert a valid attribute string value to the corresponding tuple value */
238long tplg_class_attribute_valid_tuple_value(struct tplg_pre_processor *tplg_pp ATTRIBUTE_UNUSED,
239					    snd_config_t *class, snd_config_t *attr)
240{
241
242	snd_config_t *attributes, *cfg, *valid, *tuples, *n;
243	snd_config_iterator_t i, next;
244	const char *attr_name, *attr_value;
245	int ret;
246
247	ret = snd_config_get_id(attr, &attr_name);
248	if (ret < 0)
249		return -EINVAL;
250
251	ret = snd_config_get_string(attr, &attr_value);
252	if (ret < 0)
253		return -EINVAL;
254
255	/* find attribute definition in class */
256	ret = snd_config_search(class, "DefineAttribute", &attributes);
257	if (ret < 0)
258		return -EINVAL;
259
260
261	ret = snd_config_search(attributes, attr_name, &cfg);
262	if (ret < 0)
263		return -EINVAL;
264
265	/* check if it has valid values */
266	ret = snd_config_search(cfg, "constraints.valid_values", &valid);
267	if (ret < 0)
268		return -EINVAL;
269
270	ret = snd_config_search(cfg, "constraints.tuple_values", &tuples);
271	if (ret < 0)
272		return -EINVAL;
273
274	/* find and return the tuple value matching the attribute value id */
275	snd_config_for_each(i, next, valid) {
276		const char *s, *id;
277
278		n = snd_config_iterator_entry(i);
279		if (snd_config_get_string(n, &s) < 0)
280			continue;
281		if (snd_config_get_id(n, &id) < 0)
282			continue;
283
284		if (!strcmp(attr_value, s)) {
285			snd_config_t *tuple;
286			long tuple_value;
287
288			ret = snd_config_search(tuples, id, &tuple);
289			if (ret < 0)
290				return -EINVAL;
291
292			ret = snd_config_get_integer(tuple, &tuple_value);
293			if (ret < 0)
294				return ret;
295
296			return tuple_value;
297		}
298	}
299
300	return -EINVAL;
301}
302