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