Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / tools / testing / selftests / alsa / conf.c
blobe2b3a5810f474dd30efd29323542ea68a24bd776
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.
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>
17 #include "../kselftest.h"
18 #include "alsa-local.h"
20 #define SYSFS_ROOT "/sys"
22 struct card_cfg_data *conf_cards;
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"
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
49 #ifndef LIB_HAS_LOAD_STRING
50 static int snd_config_load_string(snd_config_t **config, const char *s,
51 size_t size)
53 snd_input_t *input;
54 snd_config_t *dst;
55 int err;
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;
68 err = snd_config_load(dst, input);
69 snd_input_close(input);
70 if (err < 0) {
71 snd_config_delete(dst);
72 return err;
74 *config = dst;
75 return 0;
77 #endif
79 snd_config_t *get_alsalib_config(void)
81 snd_config_t *config;
82 int err;
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();
90 return config;
93 static struct card_cfg_data *conf_data_by_card(int card, bool msg)
95 struct card_cfg_data *conf;
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;
105 return NULL;
108 static void dump_config_tree(snd_config_t *top)
110 snd_output_t *out;
111 int err;
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);
121 snd_config_t *conf_load_from_file(const char *filename)
123 snd_config_t *dst;
124 snd_input_t *input;
125 int err;
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;
140 static char *sysfs_get(const char *sysfs_root, const char *id)
142 char path[PATH_MAX], link[PATH_MAX + 1];
143 struct stat sb;
144 ssize_t len;
145 char *e;
146 int fd;
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;
160 link[len] = '\0';
161 e = strrchr(link, '/');
162 if (e)
163 return strdup(e + 1);
164 return NULL;
166 if (S_ISDIR(sb.st_mode))
167 return NULL;
168 if ((sb.st_mode & S_IRUSR) == 0)
169 return NULL;
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));
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;
192 static bool sysfs_match(const char *sysfs_root, snd_config_t *config)
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;
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;
222 return iter > 0;
225 static void assign_card_config(int card, const char *sysfs_card_root)
227 struct card_cfg_data *data;
228 snd_config_t *sysfs_card_config;
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;
235 data->card = card;
236 break;
240 static void assign_card_configs(void)
242 char fn[128];
243 int card;
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);
252 static int filename_filter(const struct dirent *dirent)
254 size_t flen;
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;
268 static bool match_config(const char *filename)
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;
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);
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;
300 return true;
303 void conf_load(void)
305 const char *fn = "conf.d";
306 struct dirent **namelist;
307 int n, j;
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]);
323 free(namelist);
325 assign_card_configs();
328 void conf_free(void)
330 struct card_cfg_data *conf;
332 while (conf_cards) {
333 conf = conf_cards;
334 conf_cards = conf->next;
335 snd_config_delete(conf->config);
339 snd_config_t *conf_by_card(int card)
341 struct card_cfg_data *conf;
343 conf = conf_data_by_card(card, true);
344 if (conf)
345 return conf->config;
346 return NULL;
349 static int conf_get_by_keys(snd_config_t *root, const char *key1,
350 const char *key2, snd_config_t **result)
352 int ret;
354 if (key1) {
355 ret = snd_config_search(root, key1, &root);
356 if (ret != -ENOENT && ret < 0)
357 return ret;
359 if (key2)
360 ret = snd_config_search(root, key2, &root);
361 if (ret >= 0)
362 *result = root;
363 return ret;
366 snd_config_t *conf_get_subtree(snd_config_t *root, const char *key1, const char *key2)
368 int ret;
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;
380 int conf_get_count(snd_config_t *root, const char *key1, const char *key2)
382 snd_config_t *cfg;
383 snd_config_iterator_t i, next;
384 int count, ret;
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;
401 const char *conf_get_string(snd_config_t *root, const char *key1, const char *key2, const char *def)
403 snd_config_t *cfg;
404 const char *s;
405 int ret;
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;
419 long conf_get_long(snd_config_t *root, const char *key1, const char *key2, long def)
421 snd_config_t *cfg;
422 long l;
423 int ret;
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;
437 int conf_get_bool(snd_config_t *root, const char *key1, const char *key2, int def)
439 snd_config_t *cfg;
440 int ret;
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;
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)
458 snd_config_t *cfg;
459 char buf[16];
460 int ret, index;
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);