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 <errno.h>
21#include <stdio.h>
22#include <alsa/asoundlib.h>
23#include "topology.h"
24#include "pre-processor.h"
25
26int tplg_build_base_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
27			   snd_config_t *parent, bool skip_name)
28{
29	snd_config_t *top, *parent_obj, *cfg, *dest;
30	const char *parent_name;
31
32	/* find parent section config */
33	top = tplg_object_get_section(tplg_pp, parent);
34	if (!top)
35		return -EINVAL;
36
37	parent_obj = tplg_object_get_instance_config(tplg_pp, parent);
38
39	/* get parent name */
40	parent_name = tplg_object_get_name(tplg_pp, parent_obj);
41	if (!parent_name)
42		return 0;
43
44	/* find parent config with name */
45	dest = tplg_find_config(top, parent_name);
46	if (!dest) {
47		SNDERR("Cannot find parent config %s\n", parent_name);
48		return -EINVAL;
49	}
50
51	/* build config from template and add to parent */
52	return tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, dest, skip_name);
53}
54
55int tplg_build_scale_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
56			      snd_config_t *parent)
57{
58	return tplg_build_base_object(tplg_pp, obj_cfg, parent, true);
59}
60
61int tplg_build_ops_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
62			      snd_config_t *parent)
63{
64	return tplg_build_base_object(tplg_pp, obj_cfg, parent, false);
65}
66
67int tplg_build_channel_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
68			      snd_config_t *parent)
69{
70	return tplg_build_base_object(tplg_pp, obj_cfg, parent, false);
71}
72
73int tplg_build_text_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
74			   snd_config_t *parent)
75{
76	snd_config_t *cfg;
77	const char *name;
78	int ret;
79
80	cfg = tplg_object_get_instance_config(tplg_pp, obj_cfg);
81
82	name = tplg_object_get_name(tplg_pp, cfg);
83	if (!name)
84		return -EINVAL;
85
86	ret = tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, NULL, false);
87	if (ret < 0)
88		return ret;
89
90	return tplg_parent_update(tplg_pp, parent, "texts", name);
91}
92
93int tplg_build_tlv_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
94			      snd_config_t *parent)
95{
96	snd_config_t *cfg;
97	const char *name;
98	int ret;
99
100	cfg = tplg_object_get_instance_config(tplg_pp, obj_cfg);
101
102	name = tplg_object_get_name(tplg_pp, cfg);
103	if (!name)
104		return -EINVAL;
105
106	ret = tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, NULL, false);
107	if (ret < 0)
108		return ret;
109
110	return tplg_parent_update(tplg_pp, parent, "tlv", name);
111}
112
113static int tplg_build_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
114			      snd_config_t *parent, char *type)
115{
116	snd_config_t *cfg, *obj;
117	const char *name;
118	int ret;
119
120	obj = tplg_object_get_instance_config(tplg_pp, obj_cfg);
121
122	/* get control name */
123	ret = snd_config_search(obj, "name", &cfg);
124	if (ret < 0)
125		return 0;
126
127	ret = snd_config_get_string(cfg, &name);
128	if (ret < 0)
129		return ret;
130
131	ret = tplg_build_object_from_template(tplg_pp, obj_cfg, &cfg, NULL, false);
132	if (ret < 0)
133		return ret;
134
135	ret = tplg_add_object_data(tplg_pp, obj_cfg, cfg, NULL);
136	if (ret < 0)
137		SNDERR("Failed to add data section for %s\n", name);
138
139	return tplg_parent_update(tplg_pp, parent, type, name);
140}
141
142int tplg_build_mixer_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
143			      snd_config_t *parent)
144{
145	return tplg_build_control(tplg_pp, obj_cfg, parent, "mixer");
146}
147
148int tplg_build_bytes_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
149			      snd_config_t *parent)
150{
151	return tplg_build_control(tplg_pp, obj_cfg, parent, "bytes");
152}
153
154int tplg_build_enum_control(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
155			     snd_config_t *parent)
156{
157	return tplg_build_control(tplg_pp, obj_cfg, parent, "enum");
158}
159
160/*
161 * Widget names for pipeline endpoints can be of the following type:
162 * "class.<constructor args separated by .> ex: pga.0.1, buffer.1.1 etc
163 * Optionally, the index argument for a widget can be omitted and will be substituted with
164 * the index from the route: ex: pga..0, host..playback etc
165 */
166static int tplg_pp_get_widget_name(struct tplg_pre_processor *tplg_pp,
167				      const char *string, long index, char **widget)
168{
169	snd_config_iterator_t i, next;
170	snd_config_t *temp_cfg, *child, *class_cfg, *n;
171	char *class_name, *args, *widget_name;
172	int ret;
173
174	/* get class name */
175	args = strchr(string, '.');
176	if (!args) {
177		SNDERR("Error getting class name for %s\n", string);
178		return -EINVAL;
179	}
180
181	class_name = calloc(1, strlen(string) - strlen(args) + 1);
182	if (!class_name)
183		return -ENOMEM;
184
185	snprintf(class_name, strlen(string) - strlen(args) + 1, "%s", string);
186
187	/* create config with Widget class type */
188	ret = snd_config_make(&temp_cfg, "Widget", SND_CONFIG_TYPE_COMPOUND);
189	if (ret < 0) {
190		free(class_name);
191		return ret;
192	}
193
194	/* create config with class name and add it to the Widget config */
195	ret = tplg_config_make_add(&child, class_name, SND_CONFIG_TYPE_COMPOUND, temp_cfg);
196	if (ret < 0) {
197		free(class_name);
198		return ret;
199	}
200
201	/* get class definition for widget */
202	class_cfg = tplg_class_lookup(tplg_pp, temp_cfg);
203	snd_config_delete(temp_cfg);
204	if (!class_cfg) {
205		free(class_name);
206		return -EINVAL;
207	}
208
209	/* get constructor for class */
210	ret = snd_config_search(class_cfg, "attributes.constructor", &temp_cfg);
211	if (ret < 0) {
212		SNDERR("No arguments in class for widget %s\n", string);
213		free(class_name);
214		return ret;
215	}
216
217	widget_name = strdup(class_name);
218	free(class_name);
219	if (!widget_name)
220		return -ENOMEM;
221
222	/* construct widget name using the constructor argument values */
223	snd_config_for_each(i, next, temp_cfg) {
224		const char *id;
225		char *arg, *remaining, *temp;
226
227		n = snd_config_iterator_entry(i);
228		if (snd_config_get_string(n, &id) < 0)
229			continue;
230
231		if (!args) {
232			SNDERR("insufficient arugments for widget %s\n", string);
233			ret = -EINVAL;
234			goto err;
235		}
236
237		remaining = strchr(args + 1, '.');
238		if (remaining) {
239			arg = calloc(1, strlen(args + 1) - strlen(remaining) + 1);
240			if (!arg) {
241				ret = -ENOMEM;
242				goto err;
243			}
244			snprintf(arg, strlen(args + 1) - strlen(remaining) + 1, "%s", args + 1);
245		} else {
246			arg = calloc(1, strlen(args + 1) + 1);
247			if (!arg) {
248				ret = -ENOMEM;
249				goto err;
250			}
251
252			snprintf(arg, strlen(args + 1) + 1, "%s", args + 1);
253		}
254
255		/* if no index provided, substitue with route index */
256		if (!strcmp(arg, "") && !strcmp(id, "index")) {
257			free(arg);
258			arg = tplg_snprintf("%ld", index);
259			if (!arg) {
260				ret = -ENOMEM;
261				free(arg);
262				goto err;
263			}
264		}
265
266		temp = tplg_snprintf("%s.%s", widget_name, arg);
267		if (!temp) {
268			ret = -ENOMEM;
269			free(arg);
270			goto err;
271		}
272
273		free(widget_name);
274		widget_name = temp;
275		free(arg);
276		if (remaining)
277			args = remaining;
278		else
279			args = NULL;
280	}
281
282	*widget = widget_name;
283	return 0;
284
285err:
286	free(widget_name);
287	return ret;
288}
289
290int tplg_build_dapm_route_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
291			      snd_config_t *parent)
292{
293	snd_config_t *top, *obj, *cfg, *route, *child, *parent_obj;
294	const char *name, *wname;
295	const char *parent_name = "Endpoint";
296	char *src_widget_name, *sink_widget_name, *line_str, *route_name;
297	const char *control = "";
298	long index = 0;
299	int ret;
300
301	obj = tplg_object_get_instance_config(tplg_pp, obj_cfg);
302
303	ret = snd_config_get_id(obj, &name);
304	if (ret < 0)
305		return -EINVAL;
306
307	/* endpoint connections at the top-level conf have no parent */
308	if (parent) {
309		parent_obj = tplg_object_get_instance_config(tplg_pp, parent);
310
311		ret = snd_config_get_id(parent_obj, &parent_name);
312		if (ret < 0)
313			return -EINVAL;
314	}
315
316	tplg_pp_debug("Building DAPM route object: '%s' ...", name);
317
318	ret = snd_config_search(tplg_pp->output_cfg, "SectionGraph", &top);
319	if (ret < 0) {
320		ret = tplg_config_make_add(&top, "SectionGraph",
321					  SND_CONFIG_TYPE_COMPOUND, tplg_pp->output_cfg);
322		if (ret < 0) {
323			SNDERR("Error creating 'SectionGraph' config\n");
324			return ret;
325		}
326	}
327
328	/* get route index */
329	ret = snd_config_search(obj, "index", &cfg);
330	if (ret >= 0) {
331		ret = snd_config_get_integer(cfg, &index);
332		if (ret < 0) {
333			SNDERR("Invalid index route %s\n", name);
334			return ret;
335		}
336	}
337
338	/* get source widget name */
339	ret = snd_config_search(obj, "source", &cfg);
340	if (ret < 0) {
341		SNDERR("No source for route %s\n", name);
342		return ret;
343	}
344
345	ret = snd_config_get_string(cfg, &wname);
346	if (ret < 0) {
347		SNDERR("Invalid name for source in route %s\n", name);
348		return ret;
349	}
350
351	ret = tplg_pp_get_widget_name(tplg_pp, wname, index, &src_widget_name);
352	if (ret < 0) {
353		SNDERR("error getting widget name for %s\n", wname);
354		return ret;
355	}
356
357	/* get sink widget name */
358	ret = snd_config_search(obj, "sink", &cfg);
359	if (ret < 0) {
360		SNDERR("No sink for route %s\n", name);
361		free(src_widget_name);
362		return ret;
363	}
364
365	ret = snd_config_get_string(cfg, &wname);
366	if (ret < 0) {
367		SNDERR("Invalid name for sink in route %s\n", name);
368		free(src_widget_name);
369		return ret;
370	}
371
372	ret = tplg_pp_get_widget_name(tplg_pp, wname, index, &sink_widget_name);
373	if (ret < 0) {
374		SNDERR("error getting widget name for %s\n", wname);
375		free(src_widget_name);
376		return ret;
377	}
378
379	/* get control name */
380	ret = snd_config_search(obj, "control", &cfg);
381	if (ret >= 0) {
382		ret = snd_config_get_string(cfg, &control);
383		if (ret < 0) {
384			SNDERR("Invalid control name for route %s\n", name);
385			goto err;
386		}
387	}
388
389	/* add route */
390	route_name = tplg_snprintf("%s.%s", parent_name, name);
391	if (!route_name) {
392		ret = -ENOMEM;
393		goto err;
394	}
395
396	ret = snd_config_make(&route, route_name, SND_CONFIG_TYPE_COMPOUND);
397	free(route_name);
398	if (ret < 0) {
399		SNDERR("Error creating route config for %s %d\n", name, ret);
400		goto err;
401	}
402
403	ret = snd_config_add(top, route);
404	if (ret < 0) {
405		SNDERR("Error adding route config for %s %d\n", name, ret);
406		goto err;
407	}
408
409	/* add index */
410	ret = tplg_config_make_add(&child, "index", SND_CONFIG_TYPE_INTEGER, route);
411	if (ret < 0) {
412		SNDERR("Error creating index config for %s\n", name);
413		goto err;
414	}
415
416	ret = snd_config_set_integer(child, index);
417	if (ret < 0) {
418		SNDERR("Error setting index config for %s\n", name);
419		goto err;
420	}
421
422	/* add lines */
423	ret = tplg_config_make_add(&cfg, "lines", SND_CONFIG_TYPE_COMPOUND, route);
424	if (ret < 0) {
425		SNDERR("Error creating lines config for %s\n", name);
426		goto err;
427	}
428
429	/* add route string */
430	ret = tplg_config_make_add(&child, "0", SND_CONFIG_TYPE_STRING, cfg);
431	if (ret < 0) {
432		SNDERR("Error creating lines config for %s\n", name);
433		goto err;
434	}
435
436	line_str = tplg_snprintf("%s, %s, %s", sink_widget_name, control, src_widget_name);
437	if (!line_str) {
438		ret = -ENOMEM;
439		goto err;
440	}
441
442	/* set the line string */
443	ret = snd_config_set_string(child, line_str);
444	free(line_str);
445	if (ret < 0)
446		SNDERR("Error creating lines config for %s\n", name);
447err:
448	free(src_widget_name);
449	free(sink_widget_name);
450	return ret;
451}
452