pcm-test.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. // SPDX-License-Identifier: GPL-2.0
  2. //
  3. // kselftest for the ALSA PCM API
  4. //
  5. // Original author: Jaroslav Kysela <perex@perex.cz>
  6. // Copyright (c) 2022 Red Hat Inc.
  7. // This test will iterate over all cards detected in the system, exercising
  8. // every PCM device it can find. This may conflict with other system
  9. // software if there is audio activity so is best run on a system with a
  10. // minimal active userspace.
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <stdbool.h>
  14. #include <errno.h>
  15. #include <assert.h>
  16. #include <pthread.h>
  17. #include "kselftest.h"
  18. #include "alsa-local.h"
  19. typedef struct timespec timestamp_t;
  20. struct card_data {
  21. int card;
  22. snd_ctl_card_info_t *info;
  23. const char *name;
  24. pthread_t thread;
  25. struct card_data *next;
  26. };
  27. struct card_data *card_list;
  28. struct pcm_data {
  29. snd_pcm_t *handle;
  30. int card;
  31. int device;
  32. int subdevice;
  33. const char *card_name;
  34. snd_pcm_stream_t stream;
  35. snd_config_t *pcm_config;
  36. struct pcm_data *next;
  37. };
  38. struct pcm_data *pcm_list;
  39. int num_missing;
  40. struct pcm_data *pcm_missing;
  41. snd_config_t *default_pcm_config;
  42. /* Lock while reporting results since kselftest doesn't */
  43. pthread_mutex_t results_lock = PTHREAD_MUTEX_INITIALIZER;
  44. enum test_class {
  45. TEST_CLASS_DEFAULT,
  46. TEST_CLASS_SYSTEM,
  47. };
  48. void timestamp_now(timestamp_t *tstamp)
  49. {
  50. if (clock_gettime(CLOCK_MONOTONIC_RAW, tstamp))
  51. ksft_exit_fail_msg("clock_get_time\n");
  52. }
  53. long long timestamp_diff_ms(timestamp_t *tstamp)
  54. {
  55. timestamp_t now, diff;
  56. timestamp_now(&now);
  57. if (tstamp->tv_nsec > now.tv_nsec) {
  58. diff.tv_sec = now.tv_sec - tstamp->tv_sec - 1;
  59. diff.tv_nsec = (now.tv_nsec + 1000000000L) - tstamp->tv_nsec;
  60. } else {
  61. diff.tv_sec = now.tv_sec - tstamp->tv_sec;
  62. diff.tv_nsec = now.tv_nsec - tstamp->tv_nsec;
  63. }
  64. return (diff.tv_sec * 1000) + ((diff.tv_nsec + 500000L) / 1000000L);
  65. }
  66. static long device_from_id(snd_config_t *node)
  67. {
  68. const char *id;
  69. char *end;
  70. long v;
  71. if (snd_config_get_id(node, &id))
  72. ksft_exit_fail_msg("snd_config_get_id\n");
  73. errno = 0;
  74. v = strtol(id, &end, 10);
  75. if (errno || *end)
  76. return -1;
  77. return v;
  78. }
  79. static void missing_device(int card, int device, int subdevice, snd_pcm_stream_t stream)
  80. {
  81. struct pcm_data *pcm_data;
  82. for (pcm_data = pcm_list; pcm_data != NULL; pcm_data = pcm_data->next) {
  83. if (pcm_data->card != card)
  84. continue;
  85. if (pcm_data->device != device)
  86. continue;
  87. if (pcm_data->subdevice != subdevice)
  88. continue;
  89. if (pcm_data->stream != stream)
  90. continue;
  91. return;
  92. }
  93. pcm_data = calloc(1, sizeof(*pcm_data));
  94. if (!pcm_data)
  95. ksft_exit_fail_msg("Out of memory\n");
  96. pcm_data->card = card;
  97. pcm_data->device = device;
  98. pcm_data->subdevice = subdevice;
  99. pcm_data->stream = stream;
  100. pcm_data->next = pcm_missing;
  101. pcm_missing = pcm_data;
  102. num_missing++;
  103. }
  104. static void missing_devices(int card, snd_config_t *card_config)
  105. {
  106. snd_config_t *pcm_config, *node1, *node2;
  107. snd_config_iterator_t i1, i2, next1, next2;
  108. int device, subdevice;
  109. pcm_config = conf_get_subtree(card_config, "pcm", NULL);
  110. if (!pcm_config)
  111. return;
  112. snd_config_for_each(i1, next1, pcm_config) {
  113. node1 = snd_config_iterator_entry(i1);
  114. device = device_from_id(node1);
  115. if (device < 0)
  116. continue;
  117. if (snd_config_get_type(node1) != SND_CONFIG_TYPE_COMPOUND)
  118. continue;
  119. snd_config_for_each(i2, next2, node1) {
  120. node2 = snd_config_iterator_entry(i2);
  121. subdevice = device_from_id(node2);
  122. if (subdevice < 0)
  123. continue;
  124. if (conf_get_subtree(node2, "PLAYBACK", NULL))
  125. missing_device(card, device, subdevice, SND_PCM_STREAM_PLAYBACK);
  126. if (conf_get_subtree(node2, "CAPTURE", NULL))
  127. missing_device(card, device, subdevice, SND_PCM_STREAM_CAPTURE);
  128. }
  129. }
  130. }
  131. static void find_pcms(void)
  132. {
  133. char name[32], key[64];
  134. char *card_name, *card_longname;
  135. int card, dev, subdev, count, direction, err;
  136. snd_pcm_stream_t stream;
  137. struct pcm_data *pcm_data;
  138. snd_ctl_t *handle;
  139. snd_pcm_info_t *pcm_info;
  140. snd_config_t *config, *card_config, *pcm_config;
  141. struct card_data *card_data;
  142. snd_pcm_info_alloca(&pcm_info);
  143. card = -1;
  144. if (snd_card_next(&card) < 0 || card < 0)
  145. return;
  146. config = get_alsalib_config();
  147. while (card >= 0) {
  148. card_data = calloc(1, sizeof(*card_data));
  149. if (!card_data)
  150. ksft_exit_fail_msg("Out of memory\n");
  151. sprintf(name, "hw:%d", card);
  152. err = snd_ctl_open_lconf(&handle, name, 0, config);
  153. if (err < 0) {
  154. ksft_print_msg("Failed to get hctl for card %d: %s\n",
  155. card, snd_strerror(err));
  156. goto next_card;
  157. }
  158. err = snd_card_get_name(card, &card_name);
  159. if (err != 0)
  160. card_name = "Unknown";
  161. err = snd_card_get_longname(card, &card_longname);
  162. if (err != 0)
  163. card_longname = "Unknown";
  164. err = snd_ctl_card_info_malloc(&card_data->info);
  165. if (err != 0)
  166. ksft_exit_fail_msg("Failed to allocate card info: %d\n",
  167. err);
  168. err = snd_ctl_card_info(handle, card_data->info);
  169. if (err == 0) {
  170. card_data->name = snd_ctl_card_info_get_id(card_data->info);
  171. if (!card_data->name)
  172. ksft_print_msg("Failed to get card ID\n");
  173. } else {
  174. ksft_print_msg("Failed to get card info: %d\n", err);
  175. }
  176. if (!card_data->name)
  177. card_data->name = "Unknown";
  178. ksft_print_msg("Card %d/%s - %s (%s)\n", card,
  179. card_data->name, card_name, card_longname);
  180. card_config = conf_by_card(card);
  181. card_data->card = card;
  182. card_data->next = card_list;
  183. card_list = card_data;
  184. dev = -1;
  185. while (1) {
  186. if (snd_ctl_pcm_next_device(handle, &dev) < 0)
  187. ksft_exit_fail_msg("snd_ctl_pcm_next_device\n");
  188. if (dev < 0)
  189. break;
  190. for (direction = 0; direction < 2; direction++) {
  191. stream = direction ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK;
  192. sprintf(key, "pcm.%d.%s", dev, snd_pcm_stream_name(stream));
  193. pcm_config = conf_get_subtree(card_config, key, NULL);
  194. if (conf_get_bool(card_config, key, "skip", false)) {
  195. ksft_print_msg("skipping pcm %d.%d.%s\n", card, dev, snd_pcm_stream_name(stream));
  196. continue;
  197. }
  198. snd_pcm_info_set_device(pcm_info, dev);
  199. snd_pcm_info_set_subdevice(pcm_info, 0);
  200. snd_pcm_info_set_stream(pcm_info, stream);
  201. err = snd_ctl_pcm_info(handle, pcm_info);
  202. if (err == -ENOENT)
  203. continue;
  204. if (err < 0)
  205. ksft_exit_fail_msg("snd_ctl_pcm_info: %d:%d:%d\n",
  206. dev, 0, stream);
  207. ksft_print_msg("%s.0 - %s\n", card_data->name,
  208. snd_pcm_info_get_id(pcm_info));
  209. count = snd_pcm_info_get_subdevices_count(pcm_info);
  210. for (subdev = 0; subdev < count; subdev++) {
  211. sprintf(key, "pcm.%d.%d.%s", dev, subdev, snd_pcm_stream_name(stream));
  212. if (conf_get_bool(card_config, key, "skip", false)) {
  213. ksft_print_msg("skipping pcm %d.%d.%d.%s\n", card, dev,
  214. subdev, snd_pcm_stream_name(stream));
  215. continue;
  216. }
  217. pcm_data = calloc(1, sizeof(*pcm_data));
  218. if (!pcm_data)
  219. ksft_exit_fail_msg("Out of memory\n");
  220. pcm_data->card = card;
  221. pcm_data->device = dev;
  222. pcm_data->subdevice = subdev;
  223. pcm_data->card_name = card_data->name;
  224. pcm_data->stream = stream;
  225. pcm_data->pcm_config = conf_get_subtree(card_config, key, NULL);
  226. pcm_data->next = pcm_list;
  227. pcm_list = pcm_data;
  228. }
  229. }
  230. }
  231. /* check for missing devices */
  232. missing_devices(card, card_config);
  233. next_card:
  234. snd_ctl_close(handle);
  235. if (snd_card_next(&card) < 0) {
  236. ksft_print_msg("snd_card_next");
  237. break;
  238. }
  239. }
  240. snd_config_delete(config);
  241. }
  242. static void test_pcm_time(struct pcm_data *data, enum test_class class,
  243. const char *test_name, snd_config_t *pcm_cfg)
  244. {
  245. char name[64], msg[256];
  246. const int duration_s = 2, margin_ms = 100;
  247. const int duration_ms = duration_s * 1000;
  248. const char *cs;
  249. int i, err;
  250. snd_pcm_t *handle = NULL;
  251. snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED;
  252. snd_pcm_format_t format, old_format;
  253. const char *alt_formats[8];
  254. unsigned char *samples = NULL;
  255. snd_pcm_sframes_t frames;
  256. long long ms;
  257. long rate, channels, period_size, buffer_size;
  258. unsigned int rrate;
  259. snd_pcm_uframes_t rperiod_size, rbuffer_size, start_threshold;
  260. timestamp_t tstamp;
  261. bool pass = false;
  262. snd_pcm_hw_params_t *hw_params;
  263. snd_pcm_sw_params_t *sw_params;
  264. const char *test_class_name;
  265. bool skip = true;
  266. const char *desc;
  267. switch (class) {
  268. case TEST_CLASS_DEFAULT:
  269. test_class_name = "default";
  270. break;
  271. case TEST_CLASS_SYSTEM:
  272. test_class_name = "system";
  273. break;
  274. default:
  275. ksft_exit_fail_msg("Unknown test class %d\n", class);
  276. break;
  277. }
  278. desc = conf_get_string(pcm_cfg, "description", NULL, NULL);
  279. if (desc)
  280. ksft_print_msg("%s.%s.%s.%d.%d.%s - %s\n",
  281. test_class_name, test_name,
  282. data->card_name, data->device, data->subdevice,
  283. snd_pcm_stream_name(data->stream),
  284. desc);
  285. snd_pcm_hw_params_alloca(&hw_params);
  286. snd_pcm_sw_params_alloca(&sw_params);
  287. cs = conf_get_string(pcm_cfg, "format", NULL, "S16_LE");
  288. format = snd_pcm_format_value(cs);
  289. if (format == SND_PCM_FORMAT_UNKNOWN)
  290. ksft_exit_fail_msg("Wrong format '%s'\n", cs);
  291. conf_get_string_array(pcm_cfg, "alt_formats", NULL,
  292. alt_formats, ARRAY_SIZE(alt_formats), NULL);
  293. rate = conf_get_long(pcm_cfg, "rate", NULL, 48000);
  294. channels = conf_get_long(pcm_cfg, "channels", NULL, 2);
  295. period_size = conf_get_long(pcm_cfg, "period_size", NULL, 4096);
  296. buffer_size = conf_get_long(pcm_cfg, "buffer_size", NULL, 16384);
  297. samples = malloc((rate * channels * snd_pcm_format_physical_width(format)) / 8);
  298. if (!samples)
  299. ksft_exit_fail_msg("Out of memory\n");
  300. snd_pcm_format_set_silence(format, samples, rate * channels);
  301. sprintf(name, "hw:%d,%d,%d", data->card, data->device, data->subdevice);
  302. err = snd_pcm_open(&handle, name, data->stream, 0);
  303. if (err < 0) {
  304. snprintf(msg, sizeof(msg), "Failed to get pcm handle: %s", snd_strerror(err));
  305. goto __close;
  306. }
  307. err = snd_pcm_hw_params_any(handle, hw_params);
  308. if (err < 0) {
  309. snprintf(msg, sizeof(msg), "snd_pcm_hw_params_any: %s", snd_strerror(err));
  310. goto __close;
  311. }
  312. err = snd_pcm_hw_params_set_rate_resample(handle, hw_params, 0);
  313. if (err < 0) {
  314. snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_rate_resample: %s", snd_strerror(err));
  315. goto __close;
  316. }
  317. err = snd_pcm_hw_params_set_access(handle, hw_params, access);
  318. if (err < 0) {
  319. snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_access %s: %s",
  320. snd_pcm_access_name(access), snd_strerror(err));
  321. goto __close;
  322. }
  323. i = -1;
  324. __format:
  325. err = snd_pcm_hw_params_set_format(handle, hw_params, format);
  326. if (err < 0) {
  327. i++;
  328. if (i < ARRAY_SIZE(alt_formats) && alt_formats[i]) {
  329. old_format = format;
  330. format = snd_pcm_format_value(alt_formats[i]);
  331. if (format != SND_PCM_FORMAT_UNKNOWN) {
  332. ksft_print_msg("%s.%s.%d.%d.%s.%s format %s -> %s\n",
  333. test_name,
  334. data->card_name, data->device, data->subdevice,
  335. snd_pcm_stream_name(data->stream),
  336. snd_pcm_access_name(access),
  337. snd_pcm_format_name(old_format),
  338. snd_pcm_format_name(format));
  339. samples = realloc(samples, (rate * channels *
  340. snd_pcm_format_physical_width(format)) / 8);
  341. if (!samples)
  342. ksft_exit_fail_msg("Out of memory\n");
  343. snd_pcm_format_set_silence(format, samples, rate * channels);
  344. goto __format;
  345. }
  346. }
  347. snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_format %s: %s",
  348. snd_pcm_format_name(format), snd_strerror(err));
  349. goto __close;
  350. }
  351. err = snd_pcm_hw_params_set_channels(handle, hw_params, channels);
  352. if (err < 0) {
  353. snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_channels %ld: %s", channels, snd_strerror(err));
  354. goto __close;
  355. }
  356. rrate = rate;
  357. err = snd_pcm_hw_params_set_rate_near(handle, hw_params, &rrate, 0);
  358. if (err < 0) {
  359. snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_rate %ld: %s", rate, snd_strerror(err));
  360. goto __close;
  361. }
  362. if (rrate != rate) {
  363. snprintf(msg, sizeof(msg), "rate mismatch %ld != %u", rate, rrate);
  364. goto __close;
  365. }
  366. rperiod_size = period_size;
  367. err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &rperiod_size, 0);
  368. if (err < 0) {
  369. snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_period_size %ld: %s", period_size, snd_strerror(err));
  370. goto __close;
  371. }
  372. rbuffer_size = buffer_size;
  373. err = snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &rbuffer_size);
  374. if (err < 0) {
  375. snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_buffer_size %ld: %s", buffer_size, snd_strerror(err));
  376. goto __close;
  377. }
  378. err = snd_pcm_hw_params(handle, hw_params);
  379. if (err < 0) {
  380. snprintf(msg, sizeof(msg), "snd_pcm_hw_params: %s", snd_strerror(err));
  381. goto __close;
  382. }
  383. err = snd_pcm_sw_params_current(handle, sw_params);
  384. if (err < 0) {
  385. snprintf(msg, sizeof(msg), "snd_pcm_sw_params_current: %s", snd_strerror(err));
  386. goto __close;
  387. }
  388. if (data->stream == SND_PCM_STREAM_PLAYBACK) {
  389. start_threshold = (rbuffer_size / rperiod_size) * rperiod_size;
  390. } else {
  391. start_threshold = rperiod_size;
  392. }
  393. err = snd_pcm_sw_params_set_start_threshold(handle, sw_params, start_threshold);
  394. if (err < 0) {
  395. snprintf(msg, sizeof(msg), "snd_pcm_sw_params_set_start_threshold %ld: %s", (long)start_threshold, snd_strerror(err));
  396. goto __close;
  397. }
  398. err = snd_pcm_sw_params_set_avail_min(handle, sw_params, rperiod_size);
  399. if (err < 0) {
  400. snprintf(msg, sizeof(msg), "snd_pcm_sw_params_set_avail_min %ld: %s", (long)rperiod_size, snd_strerror(err));
  401. goto __close;
  402. }
  403. err = snd_pcm_sw_params(handle, sw_params);
  404. if (err < 0) {
  405. snprintf(msg, sizeof(msg), "snd_pcm_sw_params: %s", snd_strerror(err));
  406. goto __close;
  407. }
  408. ksft_print_msg("%s.%s.%s.%d.%d.%s hw_params.%s.%s.%ld.%ld.%ld.%ld sw_params.%ld\n",
  409. test_class_name, test_name,
  410. data->card_name, data->device, data->subdevice,
  411. snd_pcm_stream_name(data->stream),
  412. snd_pcm_access_name(access),
  413. snd_pcm_format_name(format),
  414. (long)rate, (long)channels,
  415. (long)rperiod_size, (long)rbuffer_size,
  416. (long)start_threshold);
  417. /* Set all the params, actually run the test */
  418. skip = false;
  419. timestamp_now(&tstamp);
  420. for (i = 0; i < duration_s; i++) {
  421. if (data->stream == SND_PCM_STREAM_PLAYBACK) {
  422. frames = snd_pcm_writei(handle, samples, rate);
  423. if (frames < 0) {
  424. snprintf(msg, sizeof(msg),
  425. "Write failed: expected %ld, wrote %li", rate, frames);
  426. goto __close;
  427. }
  428. if (frames < rate) {
  429. snprintf(msg, sizeof(msg),
  430. "expected %ld, wrote %li", rate, frames);
  431. goto __close;
  432. }
  433. } else {
  434. frames = snd_pcm_readi(handle, samples, rate);
  435. if (frames < 0) {
  436. snprintf(msg, sizeof(msg),
  437. "expected %ld, wrote %li", rate, frames);
  438. goto __close;
  439. }
  440. if (frames < rate) {
  441. snprintf(msg, sizeof(msg),
  442. "expected %ld, wrote %li", rate, frames);
  443. goto __close;
  444. }
  445. }
  446. }
  447. snd_pcm_drain(handle);
  448. ms = timestamp_diff_ms(&tstamp);
  449. if (ms < duration_ms - margin_ms || ms > duration_ms + margin_ms) {
  450. snprintf(msg, sizeof(msg), "time mismatch: expected %dms got %lld", duration_ms, ms);
  451. goto __close;
  452. }
  453. msg[0] = '\0';
  454. pass = true;
  455. __close:
  456. pthread_mutex_lock(&results_lock);
  457. switch (class) {
  458. case TEST_CLASS_SYSTEM:
  459. test_class_name = "system";
  460. /*
  461. * Anything specified as specific to this system
  462. * should always be supported.
  463. */
  464. ksft_test_result(!skip, "%s.%s.%s.%d.%d.%s.params\n",
  465. test_class_name, test_name,
  466. data->card_name, data->device,
  467. data->subdevice,
  468. snd_pcm_stream_name(data->stream));
  469. break;
  470. default:
  471. break;
  472. }
  473. if (!skip)
  474. ksft_test_result(pass, "%s.%s.%s.%d.%d.%s\n",
  475. test_class_name, test_name,
  476. data->card_name, data->device,
  477. data->subdevice,
  478. snd_pcm_stream_name(data->stream));
  479. else
  480. ksft_test_result_skip("%s.%s.%s.%d.%d.%s\n",
  481. test_class_name, test_name,
  482. data->card_name, data->device,
  483. data->subdevice,
  484. snd_pcm_stream_name(data->stream));
  485. if (msg[0])
  486. ksft_print_msg("%s\n", msg);
  487. pthread_mutex_unlock(&results_lock);
  488. free(samples);
  489. if (handle)
  490. snd_pcm_close(handle);
  491. }
  492. void run_time_tests(struct pcm_data *pcm, enum test_class class,
  493. snd_config_t *cfg)
  494. {
  495. const char *test_name, *test_type;
  496. snd_config_t *pcm_cfg;
  497. snd_config_iterator_t i, next;
  498. if (!cfg)
  499. return;
  500. cfg = conf_get_subtree(cfg, "test", NULL);
  501. if (cfg == NULL)
  502. return;
  503. snd_config_for_each(i, next, cfg) {
  504. pcm_cfg = snd_config_iterator_entry(i);
  505. if (snd_config_get_id(pcm_cfg, &test_name) < 0)
  506. ksft_exit_fail_msg("snd_config_get_id\n");
  507. test_type = conf_get_string(pcm_cfg, "type", NULL, "time");
  508. if (strcmp(test_type, "time") == 0)
  509. test_pcm_time(pcm, class, test_name, pcm_cfg);
  510. else
  511. ksft_exit_fail_msg("unknown test type '%s'\n", test_type);
  512. }
  513. }
  514. void *card_thread(void *data)
  515. {
  516. struct card_data *card = data;
  517. struct pcm_data *pcm;
  518. for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) {
  519. if (pcm->card != card->card)
  520. continue;
  521. run_time_tests(pcm, TEST_CLASS_DEFAULT, default_pcm_config);
  522. run_time_tests(pcm, TEST_CLASS_SYSTEM, pcm->pcm_config);
  523. }
  524. return 0;
  525. }
  526. int main(void)
  527. {
  528. struct card_data *card;
  529. struct card_cfg_data *conf;
  530. struct pcm_data *pcm;
  531. snd_config_t *global_config, *cfg;
  532. int num_pcm_tests = 0, num_tests, num_std_pcm_tests;
  533. int ret;
  534. void *thread_ret;
  535. ksft_print_header();
  536. global_config = conf_load_from_file("pcm-test.conf");
  537. default_pcm_config = conf_get_subtree(global_config, "pcm", NULL);
  538. if (default_pcm_config == NULL)
  539. ksft_exit_fail_msg("default pcm test configuration (pcm compound) is missing\n");
  540. conf_load();
  541. find_pcms();
  542. for (conf = conf_cards; conf; conf = conf->next)
  543. if (conf->card < 0)
  544. num_missing++;
  545. num_std_pcm_tests = conf_get_count(default_pcm_config, "test", NULL);
  546. for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) {
  547. num_pcm_tests += num_std_pcm_tests;
  548. cfg = pcm->pcm_config;
  549. if (cfg == NULL)
  550. continue;
  551. /* Setting params is reported as a separate test */
  552. num_tests = conf_get_count(cfg, "test", NULL) * 2;
  553. if (num_tests > 0)
  554. num_pcm_tests += num_tests;
  555. }
  556. ksft_set_plan(num_missing + num_pcm_tests);
  557. for (conf = conf_cards; conf; conf = conf->next)
  558. if (conf->card < 0)
  559. ksft_test_result_fail("test.missing.%s.%s\n",
  560. conf->filename, conf->config_id);
  561. for (pcm = pcm_missing; pcm != NULL; pcm = pcm->next) {
  562. ksft_test_result(false, "test.missing.%s.%d.%d.%s\n",
  563. pcm->card_name, pcm->device, pcm->subdevice,
  564. snd_pcm_stream_name(pcm->stream));
  565. }
  566. for (card = card_list; card != NULL; card = card->next) {
  567. ret = pthread_create(&card->thread, NULL, card_thread, card);
  568. if (ret != 0) {
  569. ksft_exit_fail_msg("Failed to create card %d thread: %d (%s)\n",
  570. card->card, ret,
  571. strerror(errno));
  572. }
  573. }
  574. for (card = card_list; card != NULL; card = card->next) {
  575. ret = pthread_join(card->thread, &thread_ret);
  576. if (ret != 0) {
  577. ksft_exit_fail_msg("Failed to join card %d thread: %d (%s)\n",
  578. card->card, ret,
  579. strerror(errno));
  580. }
  581. }
  582. snd_config_delete(global_config);
  583. conf_free();
  584. ksft_exit_pass();
  585. return 0;
  586. }