1 /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5 
6 #include <stdlib.h>
7 #include <syslog.h>
8 #include "cras_dsp_ini.h"
9 
10 #define MAX_INI_KEY_LENGTH 64  /* names like "output_source:output_0" */
11 #define MAX_NR_PORT 128	/* the max number of ports for a plugin */
12 #define MAX_PORT_NAME_LENGTH 20 /* names like "output_32" */
13 
14 /* Format of the ini file (See dsp.ini.sample for an example).
15 
16 - Each section in the ini file specifies a plugin. The section name is
17   just an identifier. The "library" and "label" attributes in a
18   section must be defined. The "library" attribute is the name of the
19   shared library from which this plugin will be loaded, or a special
20   value "builtin" for built-in plugins. The "label" attribute specify
21   which plugin inside the shared library should be loaded.
22 
23 - Built-in plugins have an attribute "label" which has value "source"
24   or "sink". It defines where the audio data flows into and flows out
25   of the pipeline.  Built-in plugins also have a attribute "purpose"
26   which has the value "playback" or "capture". It defines which
27   pipeline these plugins belong to.
28 
29 - Each plugin can have an optional "disable expression", which defines
30   under which conditions the plugin is disabled.
31 
32 - Each plugin have some ports which specify the parameters for the
33   plugin or to specify connections to other plugins. The ports in each
34   plugin are numbered from 0. Each port is either an input port or an
35   output port, and each port is either an audio port or a control
36   port. The connections between two ports are expressed by giving the
37   same value to both ports. For audio ports, the value should be
38   "{identifier}". For control ports, the value shoule be
39   "<identifier>". For example, the following fragment
40 
41   [plugin1]
42   ...
43   output_4={audio_left}
44   output_5={audio_right}
45 
46   [plugin2]
47   ...
48   input_0={audio_left}
49 
50   [plugin3]
51   ...
52   input_2={audio_right}
53 
54   specifies these connections:
55   port 4 of plugin1 --> port 0 of plugin2
56   port 5 of plugin1 --> port 2 of plugin3
57 
58 */
59 
getstring(struct ini * ini,const char * sec_name,const char * key)60 static const char *getstring(struct ini *ini, const char *sec_name,
61 			     const char *key)
62 {
63 	char full_key[MAX_INI_KEY_LENGTH];
64 	snprintf(full_key, sizeof(full_key), "%s:%s", sec_name, key);
65 	return iniparser_getstring(ini->dict, full_key, NULL);
66 }
67 
lookup_flow(struct ini * ini,const char * name)68 static int lookup_flow(struct ini *ini, const char *name)
69 {
70 	int i;
71 	const struct flow *flow;
72 
73 	FOR_ARRAY_ELEMENT(&ini->flows, i, flow) {
74 		if (strcmp(flow->name, name) == 0)
75 			return i;
76 	}
77 
78 	return -1;
79 }
80 
lookup_or_add_flow(struct ini * ini,const char * name)81 static int lookup_or_add_flow(struct ini *ini, const char *name)
82 {
83 	struct flow *flow;
84 	int i = lookup_flow(ini, name);
85 	if (i != -1)
86 		return i;
87 	i = ARRAY_COUNT(&ini->flows);
88 	flow = ARRAY_APPEND_ZERO(&ini->flows);
89 	flow->name = name;
90 	return i;
91 }
92 
parse_ports(struct ini * ini,const char * sec_name,struct plugin * plugin)93 static int parse_ports(struct ini *ini, const char *sec_name,
94 		       struct plugin *plugin)
95 {
96 	char key[MAX_PORT_NAME_LENGTH];
97 	const char *str;
98 	int i;
99 	struct port *p;
100 	int direction;
101 
102 	for (i = 0; i < MAX_NR_PORT; i++) {
103 		direction = PORT_INPUT;
104 		snprintf(key, sizeof(key), "input_%d", i);
105 		str = getstring(ini, sec_name, key);
106 		if (str == NULL)  {
107 			direction = PORT_OUTPUT;
108 			snprintf(key, sizeof(key), "output_%d", i);
109 			str = getstring(ini, sec_name, key);
110 			if (str == NULL)
111 				break; /* no more ports */
112 		}
113 
114 		if (*str == '\0') {
115 			syslog(LOG_ERR, "empty value for %s:%s", sec_name, key);
116 			return -1;
117 		}
118 
119 		if (str[0] == '<' || str[0] == '{') {
120 			p = ARRAY_APPEND_ZERO(&plugin->ports);
121 			p->type = (str[0] == '<') ? PORT_CONTROL : PORT_AUDIO;
122 			p->flow_id = lookup_or_add_flow(ini, str);
123 			p->init_value = 0;
124 		} else {
125 			char *endptr;
126 			float init_value = strtof(str, &endptr);
127 			if (endptr == str) {
128 				syslog(LOG_ERR, "cannot parse number from '%s'",
129 				       str);
130 			}
131 			p = ARRAY_APPEND_ZERO(&plugin->ports);
132 			p->type = PORT_CONTROL;
133 			p->flow_id = INVALID_FLOW_ID;
134 			p->init_value = init_value;
135 		}
136 		p->direction = direction;
137 	}
138 
139 	return 0;
140 }
141 
parse_plugin_section(struct ini * ini,const char * sec_name,struct plugin * p)142 static int parse_plugin_section(struct ini *ini, const char *sec_name,
143 				struct plugin *p)
144 {
145 	p->title = sec_name;
146 	p->library = getstring(ini, sec_name, "library");
147 	p->label = getstring(ini, sec_name, "label");
148 	p->purpose = getstring(ini, sec_name, "purpose");
149 	p->disable_expr = cras_expr_expression_parse(
150 		getstring(ini, sec_name, "disable"));
151 
152 	if (p->library == NULL || p->label == NULL) {
153 		syslog(LOG_ERR, "A plugin must have library and label: %s",
154 		       sec_name);
155 		return -1;
156 	}
157 
158 	if (parse_ports(ini, sec_name, p) < 0) {
159 		syslog(LOG_ERR, "Failed to parse ports: %s", sec_name);
160 		return -1;
161 	}
162 
163 	return 0;
164 }
165 
fill_flow_info(struct ini * ini)166 static void fill_flow_info(struct ini *ini)
167 {
168 	int i, j;
169 	struct plugin *plugin;
170 	struct port *port;
171 	struct flow *flow;
172 	struct plugin **pplugin;
173 	int *pport;
174 
175 	FOR_ARRAY_ELEMENT(&ini->plugins, i, plugin) {
176 		FOR_ARRAY_ELEMENT(&plugin->ports, j, port) {
177 			int flow_id = port->flow_id;
178 			if (flow_id == INVALID_FLOW_ID)
179 				continue;
180 			flow = ARRAY_ELEMENT(&ini->flows, flow_id);
181 			flow->type = port->type;
182 			if (port->direction == PORT_INPUT) {
183 				pplugin = &flow->to;
184 				pport = &flow->to_port;
185 			} else {
186 				pplugin = &flow->from;
187 				pport = &flow->from_port;
188 			}
189 			*pplugin = plugin;
190 			*pport = j;
191 		}
192 	}
193 }
194 
195 
cras_dsp_ini_create(const char * ini_filename)196 struct ini *cras_dsp_ini_create(const char *ini_filename)
197 {
198 	struct ini *ini;
199 	dictionary *dict;
200 	int nsec, i;
201 	const char *sec_name;
202 	struct plugin *plugin;
203 
204 	ini = calloc(1, sizeof(struct ini));
205 	if (!ini) {
206 		syslog(LOG_ERR, "no memory for ini struct");
207 		return NULL;
208 	}
209 
210 	dict = iniparser_load((char *)ini_filename);
211 	if (!dict) {
212 		syslog(LOG_ERR, "no ini file %s", ini_filename);
213 		goto bail;
214 	}
215 	ini->dict = dict;
216 
217 	/* Parse the plugin sections */
218 	nsec = iniparser_getnsec(dict);
219 	for (i = 0; i < nsec; i++) {
220 		sec_name = iniparser_getsecname(dict, i);
221 		plugin = ARRAY_APPEND_ZERO(&ini->plugins);
222 		if (parse_plugin_section(ini, sec_name, plugin) < 0)
223 			goto bail;
224 	}
225 
226 	/* Fill flow info now because now the plugin array won't change */
227 	fill_flow_info(ini);
228 
229 	return ini;
230 bail:
231 	cras_dsp_ini_free(ini);
232 	return NULL;
233 }
234 
cras_dsp_ini_free(struct ini * ini)235 void cras_dsp_ini_free(struct ini *ini)
236 {
237 	struct plugin *p;
238 	int i;
239 
240 	/* free plugins */
241 	FOR_ARRAY_ELEMENT(&ini->plugins, i, p) {
242 		cras_expr_expression_free(p->disable_expr);
243 		ARRAY_FREE(&p->ports);
244 	}
245 	ARRAY_FREE(&ini->plugins);
246 	ARRAY_FREE(&ini->flows);
247 
248 	if (ini->dict) {
249 		iniparser_freedict(ini->dict);
250 		ini->dict = NULL;
251 	}
252 
253 	free(ini);
254 }
255 
port_direction_str(enum port_direction port_direction)256 static const char *port_direction_str(enum port_direction port_direction)
257 {
258 	switch (port_direction) {
259 	case PORT_INPUT: return "input";
260 	case PORT_OUTPUT: return "output";
261 	default: return "unknown";
262 	}
263 }
264 
port_type_str(enum port_type port_type)265 static const char *port_type_str(enum port_type port_type)
266 {
267 	switch (port_type) {
268 	case PORT_CONTROL: return "control";
269 	case PORT_AUDIO: return "audio";
270 	default: return "unknown";
271 	}
272 }
273 
plugin_title(struct plugin * plugin)274 static const char *plugin_title(struct plugin *plugin)
275 {
276 	if (plugin == NULL)
277 		return "(null)";
278 	return plugin->title;
279 }
280