Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/alsa/conf.c
26285 views
1
// SPDX-License-Identifier: GPL-2.0
2
//
3
// kselftest configuration helpers for the hw specific configuration
4
//
5
// Original author: Jaroslav Kysela <[email protected]>
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
477