OSDN Git Service

6945dd2e835fc0fe418aaa02b73503213f0e27c4
[android-x86/external-alsa-lib.git] / src / ucm / ucm_include.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) 2020 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 include_eval_one(snd_use_case_mgr_t *uc_mgr,
41                             snd_config_t *inc,
42                             snd_config_t **result,
43                             snd_config_t **before,
44                             snd_config_t **after)
45 {
46         const char *file;
47         char *s;
48         int err;
49
50         *result = NULL;
51
52         if (snd_config_get_type(inc) != SND_CONFIG_TYPE_COMPOUND) {
53                 uc_error("compound type expected for Include.1");
54                 return -EINVAL;
55         }
56
57         err = get_string(inc, "File", &file);
58         if (err < 0) {
59                 uc_error("file expected (Include)");
60                 return -EINVAL;
61         }
62
63         err = snd_config_search(inc, "Before", before);
64         if (err < 0 && err != -ENOENT) {
65                 uc_error("before block identifier error");
66                 return -EINVAL;
67         }
68
69         err = snd_config_search(inc, "After", after);
70         if (err < 0 && err != -ENOENT) {
71                 uc_error("before block identifier error");
72                 return -EINVAL;
73         }
74
75         err = uc_mgr_get_substituted_value(uc_mgr, &s, file);
76         if (err < 0)
77                 return err;
78         err = uc_mgr_config_load_file(uc_mgr, s, result);
79         free(s);
80         return err;
81 }
82
83 #if 0
84 static void config_dump(snd_config_t *cfg)
85 {
86         snd_output_t *out;
87         snd_output_stdio_attach(&out, stderr, 0);
88         snd_output_printf(out, "-----\n");
89         snd_config_save(cfg, out);
90         snd_output_close(out);
91 }
92 #endif
93
94 static int find_position_node(snd_config_t **res, snd_config_t *dst,
95                               const char *id, snd_config_t *pos)
96 {
97         const char *s;
98         int err;
99
100         err = get_string(pos, id, &s);
101         if (err < 0 && err != -ENOENT)
102                 return err;
103         if (err == 0) {
104                 err = snd_config_search(dst, s, res);
105                 if (err < 0 && err != -ENOENT)
106                         return err;
107         }
108         return 0;
109 }
110
111 static int merge_it(snd_config_t *dst, snd_config_t *n)
112 {
113         snd_config_t *dn;
114         const char *id;
115         int err;
116
117         err = snd_config_get_id(n, &id);
118         if (err < 0)
119                 return err;
120         err = snd_config_search(dst, id, &dn);
121         if (err < 0)
122                 return err;
123         err = snd_config_merge(dn, n, 0); /* merge / append mode */
124         if (err < 0)
125                 snd_config_delete(n);
126         return err;
127 }
128
129 static int compound_merge(const char *id,
130                           snd_config_t *dst, snd_config_t *src,
131                           snd_config_t *before, snd_config_t *after)
132 {
133         snd_config_iterator_t i, next;
134         snd_config_t *n, *_before = NULL, *_after = NULL;
135         char tmpid[32];
136         int err, array, idx;
137
138         if (snd_config_get_type(src) != SND_CONFIG_TYPE_COMPOUND) {
139                 uc_error("compound type expected for the merged block");
140                 return -EINVAL;
141         }
142
143         if (before) {
144                 err = find_position_node(&_before, dst, id, before);
145                 if (err < 0)
146                         return err;
147         }
148         if (after) {
149                 err = find_position_node(&_after, dst, id, after);
150                 if (err < 0)
151                         return err;
152         }
153
154         /* direct merge? */
155         if (!_before && !_after)
156                 return snd_config_merge(dst, src, 0);   /* merge / append mode */
157
158         if (_before && _after) {
159                 uc_error("defined both before and after identifiers in the If or Include block");
160                 return -EINVAL;
161         }
162
163         array = snd_config_is_array(dst);
164         if (array < 0) {
165                 uc_error("destination configuration node is not a compound");
166                 return array;
167         }
168         if (array && snd_config_is_array(src) <= 0) {
169                 uc_error("source configuration node is not an array");
170                 return -EINVAL;
171         }
172
173         idx = 0;
174
175         /* for array, use a temporary non-clashing identifier */
176         if (array > 0) {
177                 snd_config_for_each(i, next, dst) {
178                         n = snd_config_iterator_entry(i);
179                         snprintf(tmpid, sizeof(tmpid), "_tmp_%d", idx++);
180                         err = snd_config_set_id(n, tmpid);
181                         if (err < 0)
182                                 return err;
183                 }
184         }
185
186         snd_config_for_each(i, next, src) {
187                 n = snd_config_iterator_entry(i);
188                 err = snd_config_remove(n);
189                 if (err < 0)
190                         return err;
191                 /* for array, use a temporary non-clashing identifier */
192                 if (array > 0) {
193                         snprintf(tmpid, sizeof(tmpid), "_tmp_%d", idx++);
194                         err = snd_config_set_id(n, tmpid);
195                         if (err < 0)
196                                 return err;
197                 }
198                 if (_before) {
199                         err = snd_config_add_before(_before, n);
200                         if (err == -EEXIST)
201                                 err = merge_it(dst, n);
202                         if (err < 0)
203                                 return err;
204                         _before = NULL;
205                         _after = n;
206                 } else if (_after) {
207                         err = snd_config_add_after(_after, n);
208                         if (err == -EEXIST)
209                                 err = merge_it(dst, n);
210                         if (err < 0)
211                                 return err;
212                         _after = n;
213                 }
214         }
215
216         /* set new indexes for the final array */
217         if (array > 0) {
218                 idx = 0;
219                 snd_config_for_each(i, next, dst) {
220                         n = snd_config_iterator_entry(i);
221                         snprintf(tmpid, sizeof(tmpid), "%d", idx++);
222                         err = snd_config_set_id(n, tmpid);
223                         if (err < 0)
224                                 return err;
225                 }
226         }
227
228         snd_config_delete(src);
229         return 0;
230 }
231
232 int uc_mgr_config_tree_merge(snd_use_case_mgr_t *uc_mgr,
233                              snd_config_t *parent, snd_config_t *new_ctx,
234                              snd_config_t *before, snd_config_t *after)
235 {
236         snd_config_iterator_t i, next;
237         snd_config_t *n, *parent2;
238         const char *id;
239         int err;
240
241         err = uc_mgr_substitute_tree(uc_mgr, new_ctx);
242         if (err < 0)
243                 return err;
244
245         snd_config_for_each(i, next, new_ctx) {
246                 n = snd_config_iterator_entry(i);
247                 err = snd_config_remove(n);
248                 if (err < 0)
249                         return err;
250                 err = snd_config_get_id(n, &id);
251                 if (err < 0) {
252 __add:
253                         err = snd_config_add(parent, n);
254                         if (err < 0)
255                                 return err;
256                 } else {
257                         err = snd_config_search(parent, id, &parent2);
258                         if (err == -ENOENT)
259                                 goto __add;
260                         err = compound_merge(id, parent2, n, before, after);
261                         if (err < 0) {
262                                 snd_config_delete(n);
263                                 return err;
264                         }
265                 }
266         }
267         return 0;
268 }
269
270 /*
271  * put back the included configuration to the parent
272  */
273 int uc_mgr_evaluate_include(snd_use_case_mgr_t *uc_mgr,
274                               snd_config_t *parent,
275                               snd_config_t *inc)
276 {
277         snd_config_iterator_t i, next;
278         snd_config_t *a, *n, *before, *after;
279         int err;
280
281         if (uc_mgr->conf_format < 3) {
282                 uc_error("in-place include is supported in v3+ syntax");
283                 return -EINVAL;
284         }
285
286         if (snd_config_get_type(inc) != SND_CONFIG_TYPE_COMPOUND) {
287                 uc_error("compound type expected for Include");
288                 return -EINVAL;
289         }
290
291         snd_config_for_each(i, next, inc) {
292                 n = snd_config_iterator_entry(i);
293                 before = after = NULL;
294                 err = include_eval_one(uc_mgr, n, &a, &before, &after);
295                 if (err < 0)
296                         return err;
297                 if (a == NULL)
298                         continue;
299                 err = uc_mgr_evaluate_inplace(uc_mgr, a);
300                 if (err < 0)
301                         return err;
302                 err = uc_mgr_config_tree_merge(uc_mgr, parent, a, before, after);
303                 if (err < 0)
304                         return err;
305                 snd_config_delete(a);
306                 
307         }
308         return 0;
309 }