~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/alsa/conf.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 //
  3 // kselftest configuration helpers for the hw specific configuration
  4 //
  5 // Original author: Jaroslav Kysela <perex@perex.cz>
  6 // Copyright (c) 2022 Red Hat Inc.
  7 
  8 #include <stdio.h>
  9 #include <stdlib.h>
 10 #include <stdbool.h>
 11 #include <errno.h>
 12 #include <assert.h>
 13 #include <dirent.h>
 14 #include <regex.h>
 15 #include <sys/stat.h>
 16 
 17 #include "../kselftest.h"
 18 #include "alsa-local.h"
 19 
 20 #define SYSFS_ROOT "/sys"
 21 
 22 struct card_cfg_data *conf_cards;
 23 
 24 static const char *alsa_config =
 25 "ctl.hw {\n"
 26 "       @args [ CARD ]\n"
 27 "       @args.CARD.type string\n"
 28 "       type hw\n"
 29 "       card $CARD\n"
 30 "}\n"
 31 "pcm.hw {\n"
 32 "       @args [ CARD DEV SUBDEV ]\n"
 33 "       @args.CARD.type string\n"
 34 "       @args.DEV.type integer\n"
 35 "       @args.SUBDEV.type integer\n"
 36 "       type hw\n"
 37 "       card $CARD\n"
 38 "       device $DEV\n"
 39 "       subdevice $SUBDEV\n"
 40 "}\n"
 41 ;
 42 
 43 #ifdef SND_LIB_VER
 44 #if SND_LIB_VERSION >= SND_LIB_VER(1, 2, 6)
 45 #define LIB_HAS_LOAD_STRING
 46 #endif
 47 #endif
 48 
 49 #ifndef LIB_HAS_LOAD_STRING
 50 static int snd_config_load_string(snd_config_t **config, const char *s,
 51                                   size_t size)
 52 {
 53         snd_input_t *input;
 54         snd_config_t *dst;
 55         int err;
 56 
 57         assert(config && s);
 58         if (size == 0)
 59                 size = strlen(s);
 60         err = snd_input_buffer_open(&input, s, size);
 61         if (err < 0)
 62                 return err;
 63         err = snd_config_top(&dst);
 64         if (err < 0) {
 65                 snd_input_close(input);
 66                 return err;
 67         }
 68         err = snd_config_load(dst, input);
 69         snd_input_close(input);
 70         if (err < 0) {
 71                 snd_config_delete(dst);
 72                 return err;
 73         }
 74         *config = dst;
 75         return 0;
 76 }
 77 #endif
 78 
 79 snd_config_t *get_alsalib_config(void)
 80 {
 81         snd_config_t *config;
 82         int err;
 83 
 84         err = snd_config_load_string(&config, alsa_config, strlen(alsa_config));
 85         if (err < 0) {
 86                 ksft_print_msg("Unable to parse custom alsa-lib configuration: %s\n",
 87                                snd_strerror(err));
 88                 ksft_exit_fail();
 89         }
 90         return config;
 91 }
 92 
 93 static struct card_cfg_data *conf_data_by_card(int card, bool msg)
 94 {
 95         struct card_cfg_data *conf;
 96 
 97         for (conf = conf_cards; conf; conf = conf->next) {
 98                 if (conf->card == card) {
 99                         if (msg)
100                                 ksft_print_msg("using hw card config %s for card %d\n",
101                                                conf->filename, card);
102                         return conf;
103                 }
104         }
105         return NULL;
106 }
107 
108 static void dump_config_tree(snd_config_t *top)
109 {
110         snd_output_t *out;
111         int err;
112 
113         err = snd_output_stdio_attach(&out, stdout, 0);
114         if (err < 0)
115                 ksft_exit_fail_msg("stdout attach\n");
116         if (snd_config_save(top, out))
117                 ksft_exit_fail_msg("config save\n");
118         snd_output_close(out);
119 }
120 
121 snd_config_t *conf_load_from_file(const char *filename)
122 {
123         snd_config_t *dst;
124         snd_input_t *input;
125         int err;
126 
127         err = snd_input_stdio_open(&input, filename, "r");
128         if (err < 0)
129                 ksft_exit_fail_msg("Unable to parse filename %s\n", filename);
130         err = snd_config_top(&dst);
131         if (err < 0)
132                 ksft_exit_fail_msg("Out of memory\n");
133         err = snd_config_load(dst, input);
134         snd_input_close(input);
135         if (err < 0)
136                 ksft_exit_fail_msg("Unable to parse filename %s\n", filename);
137         return dst;
138 }
139 
140 static char *sysfs_get(const char *sysfs_root, const char *id)
141 {
142         char path[PATH_MAX], link[PATH_MAX + 1];
143         struct stat sb;
144         ssize_t len;
145         char *e;
146         int fd;
147 
148         if (id[0] == '/')
149                 id++;
150         snprintf(path, sizeof(path), "%s/%s", sysfs_root, id);
151         if (lstat(path, &sb) != 0)
152                 return NULL;
153         if (S_ISLNK(sb.st_mode)) {
154                 len = readlink(path, link, sizeof(link) - 1);
155                 if (len <= 0) {
156                         ksft_exit_fail_msg("sysfs: cannot read link '%s': %s\n",
157                                            path, strerror(errno));
158                         return NULL;
159                 }
160                 link[len] = '\0';
161                 e = strrchr(link, '/');
162                 if (e)
163                         return strdup(e + 1);
164                 return NULL;
165         }
166         if (S_ISDIR(sb.st_mode))
167                 return NULL;
168         if ((sb.st_mode & S_IRUSR) == 0)
169                 return NULL;
170 
171         fd = open(path, O_RDONLY);
172         if (fd < 0) {
173                 if (errno == ENOENT)
174                         return NULL;
175                 ksft_exit_fail_msg("sysfs: open failed for '%s': %s\n",
176                                    path, strerror(errno));
177         }
178         len = read(fd, path, sizeof(path)-1);
179         close(fd);
180         if (len < 0)
181                 ksft_exit_fail_msg("sysfs: unable to read value '%s': %s\n",
182                                    path, strerror(errno));
183         while (len > 0 && path[len-1] == '\n')
184                 len--;
185         path[len] = '\0';
186         e = strdup(path);
187         if (e == NULL)
188                 ksft_exit_fail_msg("Out of memory\n");
189         return e;
190 }
191 
192 static bool sysfs_match(const char *sysfs_root, snd_config_t *config)
193 {
194         snd_config_t *node, *path_config, *regex_config;
195         snd_config_iterator_t i, next;
196         const char *path_string, *regex_string, *v;
197         regex_t re;
198         regmatch_t match[1];
199         int iter = 0, ret;
200 
201         snd_config_for_each(i, next, config) {
202                 node = snd_config_iterator_entry(i);
203                 if (snd_config_search(node, "path", &path_config))
204                         ksft_exit_fail_msg("Missing path field in the sysfs block\n");
205                 if (snd_config_search(node, "regex", &regex_config))
206                         ksft_exit_fail_msg("Missing regex field in the sysfs block\n");
207                 if (snd_config_get_string(path_config, &path_string))
208                         ksft_exit_fail_msg("Path field in the sysfs block is not a string\n");
209                 if (snd_config_get_string(regex_config, &regex_string))
210                         ksft_exit_fail_msg("Regex field in the sysfs block is not a string\n");
211                 iter++;
212                 v = sysfs_get(sysfs_root, path_string);
213                 if (!v)
214                         return false;
215                 if (regcomp(&re, regex_string, REG_EXTENDED))
216                         ksft_exit_fail_msg("Wrong regex '%s'\n", regex_string);
217                 ret = regexec(&re, v, 1, match, 0);
218                 regfree(&re);
219                 if (ret)
220                         return false;
221         }
222         return iter > 0;
223 }
224 
225 static void assign_card_config(int card, const char *sysfs_card_root)
226 {
227         struct card_cfg_data *data;
228         snd_config_t *sysfs_card_config;
229 
230         for (data = conf_cards; data; data = data->next) {
231                 snd_config_search(data->config, "sysfs", &sysfs_card_config);
232                 if (!sysfs_match(sysfs_card_root, sysfs_card_config))
233                         continue;
234 
235                 data->card = card;
236                 break;
237         }
238 }
239 
240 static void assign_card_configs(void)
241 {
242         char fn[128];
243         int card;
244 
245         for (card = 0; card < 32; card++) {
246                 snprintf(fn, sizeof(fn), "%s/class/sound/card%d", SYSFS_ROOT, card);
247                 if (access(fn, R_OK) == 0)
248                         assign_card_config(card, fn);
249         }
250 }
251 
252 static int filename_filter(const struct dirent *dirent)
253 {
254         size_t flen;
255 
256         if (dirent == NULL)
257                 return 0;
258         if (dirent->d_type == DT_DIR)
259                 return 0;
260         flen = strlen(dirent->d_name);
261         if (flen <= 5)
262                 return 0;
263         if (strncmp(&dirent->d_name[flen-5], ".conf", 5) == 0)
264                 return 1;
265         return 0;
266 }
267 
268 static bool match_config(const char *filename)
269 {
270         struct card_cfg_data *data;
271         snd_config_t *config, *sysfs_config, *card_config, *sysfs_card_config, *node;
272         snd_config_iterator_t i, next;
273 
274         config = conf_load_from_file(filename);
275         if (snd_config_search(config, "sysfs", &sysfs_config) ||
276             snd_config_get_type(sysfs_config) != SND_CONFIG_TYPE_COMPOUND)
277                 ksft_exit_fail_msg("Missing global sysfs block in filename %s\n", filename);
278         if (snd_config_search(config, "card", &card_config) ||
279             snd_config_get_type(card_config) != SND_CONFIG_TYPE_COMPOUND)
280                 ksft_exit_fail_msg("Missing global card block in filename %s\n", filename);
281         if (!sysfs_match(SYSFS_ROOT, sysfs_config))
282                 return false;
283         snd_config_for_each(i, next, card_config) {
284                 node = snd_config_iterator_entry(i);
285                 if (snd_config_search(node, "sysfs", &sysfs_card_config) ||
286                     snd_config_get_type(sysfs_card_config) != SND_CONFIG_TYPE_COMPOUND)
287                         ksft_exit_fail_msg("Missing card sysfs block in filename %s\n", filename);
288 
289                 data = malloc(sizeof(*data));
290                 if (!data)
291                         ksft_exit_fail_msg("Out of memory\n");
292                 data->filename = filename;
293                 data->config = node;
294                 data->card = -1;
295                 if (snd_config_get_id(node, &data->config_id))
296                         ksft_exit_fail_msg("snd_config_get_id failed for card\n");
297                 data->next = conf_cards;
298                 conf_cards = data;
299         }
300         return true;
301 }
302 
303 void conf_load(void)
304 {
305         const char *fn = "conf.d";
306         struct dirent **namelist;
307         int n, j;
308 
309         n = scandir(fn, &namelist, filename_filter, alphasort);
310         if (n < 0)
311                 ksft_exit_fail_msg("scandir: %s\n", strerror(errno));
312         for (j = 0; j < n; j++) {
313                 size_t sl = strlen(fn) + strlen(namelist[j]->d_name) + 2;
314                 char *filename = malloc(sl);
315                 if (filename == NULL)
316                         ksft_exit_fail_msg("Out of memory\n");
317                 sprintf(filename, "%s/%s", fn, namelist[j]->d_name);
318                 if (match_config(filename))
319                         filename = NULL;
320                 free(filename);
321                 free(namelist[j]);
322         }
323         free(namelist);
324 
325         assign_card_configs();
326 }
327 
328 void conf_free(void)
329 {
330         struct card_cfg_data *conf;
331 
332         while (conf_cards) {
333                 conf = conf_cards;
334                 conf_cards = conf->next;
335                 snd_config_delete(conf->config);
336         }
337 }
338 
339 snd_config_t *conf_by_card(int card)
340 {
341         struct card_cfg_data *conf;
342 
343         conf = conf_data_by_card(card, true);
344         if (conf)
345                 return conf->config;
346         return NULL;
347 }
348 
349 static int conf_get_by_keys(snd_config_t *root, const char *key1,
350                             const char *key2, snd_config_t **result)
351 {
352         int ret;
353 
354         if (key1) {
355                 ret = snd_config_search(root, key1, &root);
356                 if (ret != -ENOENT && ret < 0)
357                         return ret;
358         }
359         if (key2)
360                 ret = snd_config_search(root, key2, &root);
361         if (ret >= 0)
362                 *result = root;
363         return ret;
364 }
365 
366 snd_config_t *conf_get_subtree(snd_config_t *root, const char *key1, const char *key2)
367 {
368         int ret;
369 
370         if (!root)
371                 return NULL;
372         ret = conf_get_by_keys(root, key1, key2, &root);
373         if (ret == -ENOENT)
374                 return NULL;
375         if (ret < 0)
376                 ksft_exit_fail_msg("key '%s'.'%s' search error: %s\n", key1, key2, snd_strerror(ret));
377         return root;
378 }
379 
380 int conf_get_count(snd_config_t *root, const char *key1, const char *key2)
381 {
382         snd_config_t *cfg;
383         snd_config_iterator_t i, next;
384         int count, ret;
385 
386         if (!root)
387                 return -1;
388         ret = conf_get_by_keys(root, key1, key2, &cfg);
389         if (ret == -ENOENT)
390                 return -1;
391         if (ret < 0)
392                 ksft_exit_fail_msg("key '%s'.'%s' search error: %s\n", key1, key2, snd_strerror(ret));
393         if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND)
394                 ksft_exit_fail_msg("key '%s'.'%s' is not a compound\n", key1, key2);
395         count = 0;
396         snd_config_for_each(i, next, cfg)
397                 count++;
398         return count;
399 }
400 
401 const char *conf_get_string(snd_config_t *root, const char *key1, const char *key2, const char *def)
402 {
403         snd_config_t *cfg;
404         const char *s;
405         int ret;
406 
407         if (!root)
408                 return def;
409         ret = conf_get_by_keys(root, key1, key2, &cfg);
410         if (ret == -ENOENT)
411                 return def;
412         if (ret < 0)
413                 ksft_exit_fail_msg("key '%s'.'%s' search error: %s\n", key1, key2, snd_strerror(ret));
414         if (snd_config_get_string(cfg, &s))
415                 ksft_exit_fail_msg("key '%s'.'%s' is not a string\n", key1, key2);
416         return s;
417 }
418 
419 long conf_get_long(snd_config_t *root, const char *key1, const char *key2, long def)
420 {
421         snd_config_t *cfg;
422         long l;
423         int ret;
424 
425         if (!root)
426                 return def;
427         ret = conf_get_by_keys(root, key1, key2, &cfg);
428         if (ret == -ENOENT)
429                 return def;
430         if (ret < 0)
431                 ksft_exit_fail_msg("key '%s'.'%s' search error: %s\n", key1, key2, snd_strerror(ret));
432         if (snd_config_get_integer(cfg, &l))
433                 ksft_exit_fail_msg("key '%s'.'%s' is not an integer\n", key1, key2);
434         return l;
435 }
436 
437 int conf_get_bool(snd_config_t *root, const char *key1, const char *key2, int def)
438 {
439         snd_config_t *cfg;
440         int ret;
441 
442         if (!root)
443                 return def;
444         ret = conf_get_by_keys(root, key1, key2, &cfg);
445         if (ret == -ENOENT)
446                 return def;
447         if (ret < 0)
448                 ksft_exit_fail_msg("key '%s'.'%s' search error: %s\n", key1, key2, snd_strerror(ret));
449         ret = snd_config_get_bool(cfg);
450         if (ret < 0)
451                 ksft_exit_fail_msg("key '%s'.'%s' is not an bool\n", key1, key2);
452         return !!ret;
453 }
454 
455 void conf_get_string_array(snd_config_t *root, const char *key1, const char *key2,
456                            const char **array, int array_size, const char *def)
457 {
458         snd_config_t *cfg;
459         char buf[16];
460         int ret, index;
461 
462         ret = conf_get_by_keys(root, key1, key2, &cfg);
463         if (ret == -ENOENT)
464                 cfg = NULL;
465         else if (ret < 0)
466                 ksft_exit_fail_msg("key '%s'.'%s' search error: %s\n", key1, key2, snd_strerror(ret));
467         for (index = 0; index < array_size; index++) {
468                 if (cfg == NULL) {
469                         array[index] = def;
470                 } else {
471                         sprintf(buf, "%i", index);
472                         array[index] = conf_get_string(cfg, buf, NULL, def);
473                 }
474         }
475 }
476 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php