1 // SPDX-License-Identifier: GPL-2.0
3 // kselftest configuration helpers for the hw specific configuration
5 // Original author: Jaroslav Kysela <perex@perex.cz>
6 // Copyright (c) 2022 Red Hat Inc.
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
=
27 " @args.CARD.type string\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"
39 " subdevice $SUBDEV\n"
44 #if SND_LIB_VERSION >= SND_LIB_VER(1, 2, 6)
45 #define LIB_HAS_LOAD_STRING
49 #ifndef LIB_HAS_LOAD_STRING
50 static int snd_config_load_string(snd_config_t
**config
, const char *s
,
60 err
= snd_input_buffer_open(&input
, s
, size
);
63 err
= snd_config_top(&dst
);
65 snd_input_close(input
);
68 err
= snd_config_load(dst
, input
);
69 snd_input_close(input
);
71 snd_config_delete(dst
);
79 snd_config_t
*get_alsalib_config(void)
84 err
= snd_config_load_string(&config
, alsa_config
, strlen(alsa_config
));
86 ksft_print_msg("Unable to parse custom alsa-lib configuration: %s\n",
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
) {
100 ksft_print_msg("using hw card config %s for card %d\n",
101 conf
->filename
, card
);
108 static void dump_config_tree(snd_config_t
*top
)
113 err
= snd_output_stdio_attach(&out
, stdout
, 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
)
127 err
= snd_input_stdio_open(&input
, filename
, "r");
129 ksft_exit_fail_msg("Unable to parse filename %s\n", filename
);
130 err
= snd_config_top(&dst
);
132 ksft_exit_fail_msg("Out of memory\n");
133 err
= snd_config_load(dst
, input
);
134 snd_input_close(input
);
136 ksft_exit_fail_msg("Unable to parse filename %s\n", filename
);
140 static char *sysfs_get(const char *sysfs_root
, const char *id
)
142 char path
[PATH_MAX
], link
[PATH_MAX
+ 1];
150 snprintf(path
, sizeof(path
), "%s/%s", sysfs_root
, id
);
151 if (lstat(path
, &sb
) != 0)
153 if (S_ISLNK(sb
.st_mode
)) {
154 len
= readlink(path
, link
, sizeof(link
) - 1);
156 ksft_exit_fail_msg("sysfs: cannot read link '%s': %s\n",
157 path
, strerror(errno
));
161 e
= strrchr(link
, '/');
163 return strdup(e
+ 1);
166 if (S_ISDIR(sb
.st_mode
))
168 if ((sb
.st_mode
& S_IRUSR
) == 0)
171 fd
= open(path
, O_RDONLY
);
175 ksft_exit_fail_msg("sysfs: open failed for '%s': %s\n",
176 path
, strerror(errno
));
178 len
= read(fd
, path
, sizeof(path
)-1);
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')
188 ksft_exit_fail_msg("Out of memory\n");
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
;
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", ®ex_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
, ®ex_string
))
210 ksft_exit_fail_msg("Regex field in the sysfs block is not a string\n");
212 v
= sysfs_get(sysfs_root
, path_string
);
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);
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
))
240 static void assign_card_configs(void)
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
)
258 if (dirent
->d_type
== DT_DIR
)
260 flen
= strlen(dirent
->d_name
);
263 if (strncmp(&dirent
->d_name
[flen
-5], ".conf", 5) == 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
))
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
));
291 ksft_exit_fail_msg("Out of memory\n");
292 data
->filename
= filename
;
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
;
305 const char *fn
= "conf.d";
306 struct dirent
**namelist
;
309 n
= scandir(fn
, &namelist
, filename_filter
, alphasort
);
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
))
325 assign_card_configs();
330 struct card_cfg_data
*conf
;
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);
349 static int conf_get_by_keys(snd_config_t
*root
, const char *key1
,
350 const char *key2
, snd_config_t
**result
)
355 ret
= snd_config_search(root
, key1
, &root
);
356 if (ret
!= -ENOENT
&& ret
< 0)
360 ret
= snd_config_search(root
, key2
, &root
);
366 snd_config_t
*conf_get_subtree(snd_config_t
*root
, const char *key1
, const char *key2
)
372 ret
= conf_get_by_keys(root
, key1
, key2
, &root
);
376 ksft_exit_fail_msg("key '%s'.'%s' search error: %s\n", key1
, key2
, snd_strerror(ret
));
380 int conf_get_count(snd_config_t
*root
, const char *key1
, const char *key2
)
383 snd_config_iterator_t i
, next
;
388 ret
= conf_get_by_keys(root
, key1
, key2
, &cfg
);
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
);
396 snd_config_for_each(i
, next
, cfg
)
401 const char *conf_get_string(snd_config_t
*root
, const char *key1
, const char *key2
, const char *def
)
409 ret
= conf_get_by_keys(root
, key1
, key2
, &cfg
);
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
);
419 long conf_get_long(snd_config_t
*root
, const char *key1
, const char *key2
, long def
)
427 ret
= conf_get_by_keys(root
, key1
, key2
, &cfg
);
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
);
437 int conf_get_bool(snd_config_t
*root
, const char *key1
, const char *key2
, int def
)
444 ret
= conf_get_by_keys(root
, key1
, key2
, &cfg
);
448 ksft_exit_fail_msg("key '%s'.'%s' search error: %s\n", key1
, key2
, snd_strerror(ret
));
449 ret
= snd_config_get_bool(cfg
);
451 ksft_exit_fail_msg("key '%s'.'%s' is not an bool\n", key1
, key2
);
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
)
462 ret
= conf_get_by_keys(root
, key1
, key2
, &cfg
);
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
++) {
471 sprintf(buf
, "%i", index
);
472 array
[index
] = conf_get_string(cfg
, buf
, NULL
, def
);