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