Home | History | Annotate | Download | only in hal
      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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    274 static const char *plugin_title(struct plugin *plugin)
    275 {
    276 	if (plugin == NULL)
    277 		return "(null)";
    278 	return plugin->title;
    279 }
    280