conf.c 11 KB

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