OSDN Git Service

ucm: evaluate nested If blocks also in True/False blocks
[android-x86/external-alsa-lib.git] / src / ucm / ucm_cond.c
1 /*
2  *  This library is free software; you can redistribute it and/or
3  *  modify it under the terms of the GNU Lesser General Public
4  *  License as published by the Free Software Foundation; either
5  *  version 2 of the License, or (at your option) any later version.
6  *
7  *  This library is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  *  Lesser General Public License for more details.
11  *
12  *  You should have received a copy of the GNU Lesser General Public
13  *  License along with this library; if not, write to the Free Software
14  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
15  *
16  *  Support for the verb/device/modifier core logic and API,
17  *  command line tool and file parser was kindly sponsored by
18  *  Texas Instruments Inc.
19  *  Support for multiple active modifiers and devices,
20  *  transition sequences, multiple client access and user defined use
21  *  cases was kindly sponsored by Wolfson Microelectronics PLC.
22  *
23  *  Copyright (C) 2019 Red Hat Inc.
24  *  Authors: Jaroslav Kysela <perex@perex.cz>
25  */
26
27 #include "ucm_local.h"
28
29 static int get_string(snd_config_t *compound, const char *key, const char **str)
30 {
31         snd_config_t *node;
32         int err;
33
34         err = snd_config_search(compound, key, &node);
35         if (err < 0)
36                 return err;
37         return snd_config_get_string(node, str);
38 }
39
40 static int if_eval_string(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
41 {
42         const char *string1 = NULL, *string2 = NULL;
43         char *s1, *s2;
44         int err;
45
46         err = get_string(eval, "String1", &string1);
47         if (err < 0 && err != -ENOENT) {
48                 uc_error("String error (If.Condition.String1)");
49                 return -EINVAL;
50         }
51
52         err = get_string(eval, "String2", &string2);
53         if (err < 0 && err != -ENOENT) {
54                 uc_error("String error (If.Condition.String2)");
55                 return -EINVAL;
56         }
57
58         if (string1 || string2) {
59                 if (string1 == NULL) {
60                         uc_error("If.Condition.String1 not defined");
61                         return -EINVAL;
62                 }
63                 if (string2 == NULL) {
64                         uc_error("If.Condition.String2 not defined");
65                         return -EINVAL;
66                 }
67                 err = uc_mgr_get_substituted_value(uc_mgr, &s1, string1);
68                 if (err < 0)
69                         return err;
70                 err = uc_mgr_get_substituted_value(uc_mgr, &s2, string2);
71                 if (err < 0) {
72                         free(s1);
73                         return err;
74                 }
75                 err = strcasecmp(string1, string2) == 0;
76                 free(s2);
77                 free(s1);
78                 return err;
79         }
80
81         err = get_string(eval, "Haystack", &string1);
82         if (err < 0 && err != -ENOENT) {
83                 uc_error("String error (If.Condition.Haystack)");
84                 return -EINVAL;
85         }
86
87         err = get_string(eval, "Needle", &string2);
88         if (err < 0 && err != -ENOENT) {
89                 uc_error("String error (If.Condition.Needle)");
90                 return -EINVAL;
91         }
92
93         if (string1 || string2) {
94                 if (string1 == NULL) {
95                         uc_error("If.Condition.Haystack not defined");
96                         return -EINVAL;
97                 }
98                 if (string2 == NULL) {
99                         uc_error("If.Condition.Needle not defined");
100                         return -EINVAL;
101                 }
102                 err = uc_mgr_get_substituted_value(uc_mgr, &s1, string1);
103                 if (err < 0)
104                         return err;
105                 err = uc_mgr_get_substituted_value(uc_mgr, &s2, string2);
106                 if (err < 0) {
107                         free(s1);
108                         return err;
109                 }
110                 err = strstr(string1, string2) == 0;
111                 free(s2);
112                 free(s1);
113                 return err;
114         }
115
116         uc_error("Unknown String condition arguments");
117         return -EINVAL;
118 }
119
120 static int if_eval_control_exists(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
121 {
122         snd_ctl_t *ctl;
123         const char *device = NULL, *ctldef;
124         snd_ctl_elem_id_t *elem_id;
125         snd_ctl_elem_info_t *elem_info;
126         char *s;
127         int err;
128
129
130         snd_ctl_elem_id_alloca(&elem_id);
131         snd_ctl_elem_info_alloca(&elem_info);
132
133         err = get_string(eval, "Device", &device);
134         if (err < 0 && err != -ENOENT) {
135                 uc_error("ControlExists error (If.Condition.Device)");
136                 return -EINVAL;
137         }
138
139         err = get_string(eval, "Control", &ctldef);
140         if (err < 0) {
141                 uc_error("ControlExists error (If.Condition.Control)");
142                 return -EINVAL;
143         }
144
145         err = uc_mgr_get_substituted_value(uc_mgr, &s, ctldef);
146         if (err < 0)
147                 return err;
148         err = snd_ctl_ascii_elem_id_parse(elem_id, s);
149         free(s);
150         if (err < 0) {
151                 uc_error("unable to parse element identificator (%s)", ctldef);
152                 return -EINVAL;
153         }
154
155         if (device == NULL) {
156                 ctl = uc_mgr_get_ctl(uc_mgr);
157                 if (ctl == NULL) {
158                         uc_error("cannot determine control device");
159                         return -EINVAL;
160                 }
161         } else {
162                 err = uc_mgr_get_substituted_value(uc_mgr, &s, device);
163                 if (err < 0)
164                         return err;
165                 err = uc_mgr_open_ctl(uc_mgr, &ctl, s);
166                 free(s);
167                 if (err < 0)
168                         return err;
169         }
170
171         snd_ctl_elem_info_set_id(elem_info, elem_id);
172         err = snd_ctl_elem_info(ctl, elem_info);
173         if (err < 0)
174                 return 0;
175
176         return 1;
177 }
178
179 static int if_eval(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval)
180 {
181         const char *type;
182         int err;
183
184         if (snd_config_get_type(eval) != SND_CONFIG_TYPE_COMPOUND) {
185                 uc_error("compound type expected for If.Condition");
186                 return -EINVAL;
187         }
188
189         err = get_string(eval, "Type", &type);
190         if (err < 0) {
191                 uc_error("type block error (If.Condition)");
192                 return -EINVAL;
193         }
194
195         if (strcmp(type, "ControlExists") == 0)
196                 return if_eval_control_exists(uc_mgr, eval);
197
198         if (strcmp(type, "String") == 0)
199                 return if_eval_string(uc_mgr, eval);
200
201         uc_error("unknown If.Condition.Type");
202         return -EINVAL;
203 }
204
205 static int if_eval_one(snd_use_case_mgr_t *uc_mgr,
206                        snd_config_t *cond,
207                        snd_config_t **result)
208 {
209         snd_config_t *expr, *_true = NULL, *_false = NULL;
210         int err;
211
212         *result = NULL;
213
214         if (snd_config_get_type(cond) != SND_CONFIG_TYPE_COMPOUND) {
215                 uc_error("compound type expected for If.1");
216                 return -EINVAL;
217         }
218
219         if (snd_config_search(cond, "Condition", &expr) < 0) {
220                 uc_error("condition block expected (If)");
221                 return -EINVAL;
222         }
223
224         err = snd_config_search(cond, "True", &_true);
225         if (err < 0 && err != -ENOENT) {
226                 uc_error("true block error (If)");
227                 return -EINVAL;
228         }
229
230         err = snd_config_search(cond, "False", &_false);
231         if (err < 0 && err != -ENOENT) {
232                 uc_error("false block error (If)");
233                 return -EINVAL;
234         }
235
236         err = if_eval(uc_mgr, expr);
237         if (err > 0) {
238                 *result = _true;
239                 return 0;
240         } else if (err == 0) {
241                 *result = _false;
242                 return 0;
243         } else {
244                 return err;
245         }
246 }
247
248 #if 0
249 static void config_dump(snd_config_t *cfg)
250 {
251         snd_output_t *out;
252         snd_output_stdio_attach(&out, stderr, 0);
253         snd_output_printf(out, "-----\n");
254         snd_config_save(cfg, out);
255         snd_output_close(out);
256 }
257 #endif
258
259 static int compound_merge(snd_config_t *dst, snd_config_t *src)
260 {
261         snd_config_iterator_t i, next;
262         snd_config_t *n;
263         int err;
264
265         if (snd_config_get_type(src) != SND_CONFIG_TYPE_COMPOUND) {
266                 uc_error("compound type expected for If True/False block");
267                 return -EINVAL;
268         }
269
270         snd_config_for_each(i, next, src) {
271                 n = snd_config_iterator_entry(i);
272                 err = snd_config_remove(n);
273                 if (err < 0)
274                         return err;
275                 err = snd_config_add(dst, n);
276                 if (err < 0) {
277                         return err;
278                 }
279         }
280
281         return 0;
282 }
283
284 /*
285  * put back the result from all conditions to the parent
286  */
287 int uc_mgr_evaluate_condition(snd_use_case_mgr_t *uc_mgr,
288                               snd_config_t *parent,
289                               snd_config_t *cond)
290 {
291         snd_config_iterator_t i, i2, next, next2;
292         snd_config_t *a, *n, *n2, *parent2;
293         const char *id;
294         int err;
295
296         if (uc_mgr->conf_format < 2) {
297                 uc_error("conditions are not supported for v1 syntax");
298                 return -EINVAL;
299         }
300
301         if (snd_config_get_type(cond) != SND_CONFIG_TYPE_COMPOUND) {
302                 uc_error("compound type expected for If");
303                 return -EINVAL;
304         }
305
306         snd_config_for_each(i, next, cond) {
307                 n = snd_config_iterator_entry(i);
308                 err = if_eval_one(uc_mgr, n, &a);
309                 if (err < 0)
310                         return err;
311                 err = snd_config_search(a, "If", &n2);
312                 if (err < 0 && err != -ENOENT) {
313                         uc_error("If block error (If)");
314                         return -EINVAL;
315                 } else if (err == 0) {
316                         err = uc_mgr_evaluate_condition(uc_mgr, a, n2);
317                         if (err < 0)
318                                 return err;
319                         snd_config_delete(n2);
320                 }
321                 snd_config_for_each(i2, next2, a) {
322                         n2 = snd_config_iterator_entry(i2);
323                         err = snd_config_remove(n2);
324                         if (err < 0)
325                                 return err;
326                         err = snd_config_get_id(n2, &id);
327                         if (err < 0) {
328 __add:
329                                 err = snd_config_add(parent, n2);
330                                 if (err < 0)
331                                         return err;
332                                 continue;
333                         } else {
334                                 err = snd_config_search(parent, id, &parent2);
335                                 if (err == -ENOENT)
336                                         goto __add;
337                                 err = compound_merge(parent2, n2);
338                                 if (err < 0)
339                                         return err;
340                         }
341                         snd_config_delete(n2);
342                 }
343         }
344         return 0;
345 }