1 // SPDX-License-Identifier: GPL-2.0
3 // kselftest for the ALSA PCM API
5 // Original author: Jaroslav Kysela <perex@perex.cz>
6 // Copyright (c) 2022 Red Hat Inc.
8 // This test will iterate over all cards detected in the system, exercising
9 // every PCM device it can find. This may conflict with other system
10 // software if there is audio activity so is best run on a system with a
11 // minimal active userspace.
20 #include "../kselftest.h"
21 #include "alsa-local.h"
23 typedef struct timespec timestamp_t
;
27 snd_ctl_card_info_t
*info
;
30 struct card_data
*next
;
33 struct card_data
*card_list
= NULL
;
40 const char *card_name
;
41 snd_pcm_stream_t stream
;
42 snd_config_t
*pcm_config
;
43 struct pcm_data
*next
;
46 struct pcm_data
*pcm_list
= NULL
;
49 struct pcm_data
*pcm_missing
= NULL
;
51 snd_config_t
*default_pcm_config
;
53 /* Lock while reporting results since kselftest doesn't */
54 pthread_mutex_t results_lock
= PTHREAD_MUTEX_INITIALIZER
;
61 void timestamp_now(timestamp_t
*tstamp
)
63 if (clock_gettime(CLOCK_MONOTONIC_RAW
, tstamp
))
64 ksft_exit_fail_msg("clock_get_time\n");
67 long long timestamp_diff_ms(timestamp_t
*tstamp
)
69 timestamp_t now
, diff
;
71 if (tstamp
->tv_nsec
> now
.tv_nsec
) {
72 diff
.tv_sec
= now
.tv_sec
- tstamp
->tv_sec
- 1;
73 diff
.tv_nsec
= (now
.tv_nsec
+ 1000000000L) - tstamp
->tv_nsec
;
75 diff
.tv_sec
= now
.tv_sec
- tstamp
->tv_sec
;
76 diff
.tv_nsec
= now
.tv_nsec
- tstamp
->tv_nsec
;
78 return (diff
.tv_sec
* 1000) + ((diff
.tv_nsec
+ 500000L) / 1000000L);
81 static long device_from_id(snd_config_t
*node
)
87 if (snd_config_get_id(node
, &id
))
88 ksft_exit_fail_msg("snd_config_get_id\n");
90 v
= strtol(id
, &end
, 10);
96 static void missing_device(int card
, int device
, int subdevice
, snd_pcm_stream_t stream
)
98 struct pcm_data
*pcm_data
;
100 for (pcm_data
= pcm_list
; pcm_data
!= NULL
; pcm_data
= pcm_data
->next
) {
101 if (pcm_data
->card
!= card
)
103 if (pcm_data
->device
!= device
)
105 if (pcm_data
->subdevice
!= subdevice
)
107 if (pcm_data
->stream
!= stream
)
111 pcm_data
= calloc(1, sizeof(*pcm_data
));
113 ksft_exit_fail_msg("Out of memory\n");
114 pcm_data
->card
= card
;
115 pcm_data
->device
= device
;
116 pcm_data
->subdevice
= subdevice
;
117 pcm_data
->stream
= stream
;
118 pcm_data
->next
= pcm_missing
;
119 pcm_missing
= pcm_data
;
123 static void missing_devices(int card
, snd_config_t
*card_config
)
125 snd_config_t
*pcm_config
, *node1
, *node2
;
126 snd_config_iterator_t i1
, i2
, next1
, next2
;
127 int device
, subdevice
;
129 pcm_config
= conf_get_subtree(card_config
, "pcm", NULL
);
132 snd_config_for_each(i1
, next1
, pcm_config
) {
133 node1
= snd_config_iterator_entry(i1
);
134 device
= device_from_id(node1
);
137 if (snd_config_get_type(node1
) != SND_CONFIG_TYPE_COMPOUND
)
139 snd_config_for_each(i2
, next2
, node1
) {
140 node2
= snd_config_iterator_entry(i2
);
141 subdevice
= device_from_id(node2
);
144 if (conf_get_subtree(node2
, "PLAYBACK", NULL
))
145 missing_device(card
, device
, subdevice
, SND_PCM_STREAM_PLAYBACK
);
146 if (conf_get_subtree(node2
, "CAPTURE", NULL
))
147 missing_device(card
, device
, subdevice
, SND_PCM_STREAM_CAPTURE
);
152 static void find_pcms(void)
154 char name
[32], key
[64];
155 char *card_name
, *card_longname
;
156 int card
, dev
, subdev
, count
, direction
, err
;
157 snd_pcm_stream_t stream
;
158 struct pcm_data
*pcm_data
;
160 snd_pcm_info_t
*pcm_info
;
161 snd_config_t
*config
, *card_config
, *pcm_config
;
162 struct card_data
*card_data
;
164 snd_pcm_info_alloca(&pcm_info
);
167 if (snd_card_next(&card
) < 0 || card
< 0)
170 config
= get_alsalib_config();
173 card_data
= calloc(1, sizeof(*card_data
));
175 ksft_exit_fail_msg("Out of memory\n");
177 sprintf(name
, "hw:%d", card
);
179 err
= snd_ctl_open_lconf(&handle
, name
, 0, config
);
181 ksft_print_msg("Failed to get hctl for card %d: %s\n",
182 card
, snd_strerror(err
));
186 err
= snd_card_get_name(card
, &card_name
);
188 card_name
= "Unknown";
189 err
= snd_card_get_longname(card
, &card_longname
);
191 card_longname
= "Unknown";
193 err
= snd_ctl_card_info_malloc(&card_data
->info
);
195 ksft_exit_fail_msg("Failed to allocate card info: %d\n",
198 err
= snd_ctl_card_info(handle
, card_data
->info
);
200 card_data
->name
= snd_ctl_card_info_get_id(card_data
->info
);
201 if (!card_data
->name
)
202 ksft_print_msg("Failed to get card ID\n");
204 ksft_print_msg("Failed to get card info: %d\n", err
);
207 if (!card_data
->name
)
208 card_data
->name
= "Unknown";
210 ksft_print_msg("Card %d/%s - %s (%s)\n", card
,
211 card_data
->name
, card_name
, card_longname
);
213 card_config
= conf_by_card(card
);
215 card_data
->card
= card
;
216 card_data
->next
= card_list
;
217 card_list
= card_data
;
221 if (snd_ctl_pcm_next_device(handle
, &dev
) < 0)
222 ksft_exit_fail_msg("snd_ctl_pcm_next_device\n");
226 for (direction
= 0; direction
< 2; direction
++) {
227 stream
= direction
? SND_PCM_STREAM_CAPTURE
: SND_PCM_STREAM_PLAYBACK
;
228 sprintf(key
, "pcm.%d.%s", dev
, snd_pcm_stream_name(stream
));
229 pcm_config
= conf_get_subtree(card_config
, key
, NULL
);
230 if (conf_get_bool(card_config
, key
, "skip", false)) {
231 ksft_print_msg("skipping pcm %d.%d.%s\n", card
, dev
, snd_pcm_stream_name(stream
));
234 snd_pcm_info_set_device(pcm_info
, dev
);
235 snd_pcm_info_set_subdevice(pcm_info
, 0);
236 snd_pcm_info_set_stream(pcm_info
, stream
);
237 err
= snd_ctl_pcm_info(handle
, pcm_info
);
241 ksft_exit_fail_msg("snd_ctl_pcm_info: %d:%d:%d\n",
244 ksft_print_msg("%s.0 - %s\n", card_data
->name
,
245 snd_pcm_info_get_id(pcm_info
));
247 count
= snd_pcm_info_get_subdevices_count(pcm_info
);
248 for (subdev
= 0; subdev
< count
; subdev
++) {
249 sprintf(key
, "pcm.%d.%d.%s", dev
, subdev
, snd_pcm_stream_name(stream
));
250 if (conf_get_bool(card_config
, key
, "skip", false)) {
251 ksft_print_msg("skipping pcm %d.%d.%d.%s\n", card
, dev
,
252 subdev
, snd_pcm_stream_name(stream
));
255 pcm_data
= calloc(1, sizeof(*pcm_data
));
257 ksft_exit_fail_msg("Out of memory\n");
258 pcm_data
->card
= card
;
259 pcm_data
->device
= dev
;
260 pcm_data
->subdevice
= subdev
;
261 pcm_data
->card_name
= card_data
->name
;
262 pcm_data
->stream
= stream
;
263 pcm_data
->pcm_config
= conf_get_subtree(card_config
, key
, NULL
);
264 pcm_data
->next
= pcm_list
;
270 /* check for missing devices */
271 missing_devices(card
, card_config
);
274 snd_ctl_close(handle
);
275 if (snd_card_next(&card
) < 0) {
276 ksft_print_msg("snd_card_next");
281 snd_config_delete(config
);
284 static void test_pcm_time(struct pcm_data
*data
, enum test_class
class,
285 const char *test_name
, snd_config_t
*pcm_cfg
)
287 char name
[64], msg
[256];
288 const int duration_s
= 2, margin_ms
= 100;
289 const int duration_ms
= duration_s
* 1000;
292 snd_pcm_t
*handle
= NULL
;
293 snd_pcm_access_t access
= SND_PCM_ACCESS_RW_INTERLEAVED
;
294 snd_pcm_format_t format
, old_format
;
295 const char *alt_formats
[8];
296 unsigned char *samples
= NULL
;
297 snd_pcm_sframes_t frames
;
299 long rate
, channels
, period_size
, buffer_size
;
301 snd_pcm_uframes_t rperiod_size
, rbuffer_size
, start_threshold
;
304 snd_pcm_hw_params_t
*hw_params
;
305 snd_pcm_sw_params_t
*sw_params
;
306 const char *test_class_name
;
311 case TEST_CLASS_DEFAULT
:
312 test_class_name
= "default";
314 case TEST_CLASS_SYSTEM
:
315 test_class_name
= "system";
318 ksft_exit_fail_msg("Unknown test class %d\n", class);
322 desc
= conf_get_string(pcm_cfg
, "description", NULL
, NULL
);
324 ksft_print_msg("%s.%s.%s.%d.%d.%s - %s\n",
325 test_class_name
, test_name
,
326 data
->card_name
, data
->device
, data
->subdevice
,
327 snd_pcm_stream_name(data
->stream
),
331 snd_pcm_hw_params_alloca(&hw_params
);
332 snd_pcm_sw_params_alloca(&sw_params
);
334 cs
= conf_get_string(pcm_cfg
, "format", NULL
, "S16_LE");
335 format
= snd_pcm_format_value(cs
);
336 if (format
== SND_PCM_FORMAT_UNKNOWN
)
337 ksft_exit_fail_msg("Wrong format '%s'\n", cs
);
338 conf_get_string_array(pcm_cfg
, "alt_formats", NULL
,
339 alt_formats
, ARRAY_SIZE(alt_formats
), NULL
);
340 rate
= conf_get_long(pcm_cfg
, "rate", NULL
, 48000);
341 channels
= conf_get_long(pcm_cfg
, "channels", NULL
, 2);
342 period_size
= conf_get_long(pcm_cfg
, "period_size", NULL
, 4096);
343 buffer_size
= conf_get_long(pcm_cfg
, "buffer_size", NULL
, 16384);
345 samples
= malloc((rate
* channels
* snd_pcm_format_physical_width(format
)) / 8);
347 ksft_exit_fail_msg("Out of memory\n");
348 snd_pcm_format_set_silence(format
, samples
, rate
* channels
);
350 sprintf(name
, "hw:%d,%d,%d", data
->card
, data
->device
, data
->subdevice
);
351 err
= snd_pcm_open(&handle
, name
, data
->stream
, 0);
353 snprintf(msg
, sizeof(msg
), "Failed to get pcm handle: %s", snd_strerror(err
));
357 err
= snd_pcm_hw_params_any(handle
, hw_params
);
359 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_any: %s", snd_strerror(err
));
362 err
= snd_pcm_hw_params_set_rate_resample(handle
, hw_params
, 0);
364 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_rate_resample: %s", snd_strerror(err
));
367 err
= snd_pcm_hw_params_set_access(handle
, hw_params
, access
);
369 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_access %s: %s",
370 snd_pcm_access_name(access
), snd_strerror(err
));
375 err
= snd_pcm_hw_params_set_format(handle
, hw_params
, format
);
378 if (i
< ARRAY_SIZE(alt_formats
) && alt_formats
[i
]) {
380 format
= snd_pcm_format_value(alt_formats
[i
]);
381 if (format
!= SND_PCM_FORMAT_UNKNOWN
) {
382 ksft_print_msg("%s.%s.%d.%d.%s.%s format %s -> %s\n",
384 data
->card_name
, data
->device
, data
->subdevice
,
385 snd_pcm_stream_name(data
->stream
),
386 snd_pcm_access_name(access
),
387 snd_pcm_format_name(old_format
),
388 snd_pcm_format_name(format
));
389 samples
= realloc(samples
, (rate
* channels
*
390 snd_pcm_format_physical_width(format
)) / 8);
392 ksft_exit_fail_msg("Out of memory\n");
393 snd_pcm_format_set_silence(format
, samples
, rate
* channels
);
397 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_format %s: %s",
398 snd_pcm_format_name(format
), snd_strerror(err
));
401 err
= snd_pcm_hw_params_set_channels(handle
, hw_params
, channels
);
403 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_channels %ld: %s", channels
, snd_strerror(err
));
407 err
= snd_pcm_hw_params_set_rate_near(handle
, hw_params
, &rrate
, 0);
409 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_rate %ld: %s", rate
, snd_strerror(err
));
413 snprintf(msg
, sizeof(msg
), "rate mismatch %ld != %u", rate
, rrate
);
416 rperiod_size
= period_size
;
417 err
= snd_pcm_hw_params_set_period_size_near(handle
, hw_params
, &rperiod_size
, 0);
419 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_period_size %ld: %s", period_size
, snd_strerror(err
));
422 rbuffer_size
= buffer_size
;
423 err
= snd_pcm_hw_params_set_buffer_size_near(handle
, hw_params
, &rbuffer_size
);
425 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_buffer_size %ld: %s", buffer_size
, snd_strerror(err
));
428 err
= snd_pcm_hw_params(handle
, hw_params
);
430 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params: %s", snd_strerror(err
));
434 err
= snd_pcm_sw_params_current(handle
, sw_params
);
436 snprintf(msg
, sizeof(msg
), "snd_pcm_sw_params_current: %s", snd_strerror(err
));
439 if (data
->stream
== SND_PCM_STREAM_PLAYBACK
) {
440 start_threshold
= (rbuffer_size
/ rperiod_size
) * rperiod_size
;
442 start_threshold
= rperiod_size
;
444 err
= snd_pcm_sw_params_set_start_threshold(handle
, sw_params
, start_threshold
);
446 snprintf(msg
, sizeof(msg
), "snd_pcm_sw_params_set_start_threshold %ld: %s", (long)start_threshold
, snd_strerror(err
));
449 err
= snd_pcm_sw_params_set_avail_min(handle
, sw_params
, rperiod_size
);
451 snprintf(msg
, sizeof(msg
), "snd_pcm_sw_params_set_avail_min %ld: %s", (long)rperiod_size
, snd_strerror(err
));
454 err
= snd_pcm_sw_params(handle
, sw_params
);
456 snprintf(msg
, sizeof(msg
), "snd_pcm_sw_params: %s", snd_strerror(err
));
460 ksft_print_msg("%s.%s.%s.%d.%d.%s hw_params.%s.%s.%ld.%ld.%ld.%ld sw_params.%ld\n",
461 test_class_name
, test_name
,
462 data
->card_name
, data
->device
, data
->subdevice
,
463 snd_pcm_stream_name(data
->stream
),
464 snd_pcm_access_name(access
),
465 snd_pcm_format_name(format
),
466 (long)rate
, (long)channels
,
467 (long)rperiod_size
, (long)rbuffer_size
,
468 (long)start_threshold
);
470 /* Set all the params, actually run the test */
473 timestamp_now(&tstamp
);
474 for (i
= 0; i
< duration_s
; i
++) {
475 if (data
->stream
== SND_PCM_STREAM_PLAYBACK
) {
476 frames
= snd_pcm_writei(handle
, samples
, rate
);
478 snprintf(msg
, sizeof(msg
),
479 "Write failed: expected %ld, wrote %li", rate
, frames
);
483 snprintf(msg
, sizeof(msg
),
484 "expected %ld, wrote %li", rate
, frames
);
488 frames
= snd_pcm_readi(handle
, samples
, rate
);
490 snprintf(msg
, sizeof(msg
),
491 "expected %ld, wrote %li", rate
, frames
);
495 snprintf(msg
, sizeof(msg
),
496 "expected %ld, wrote %li", rate
, frames
);
502 snd_pcm_drain(handle
);
503 ms
= timestamp_diff_ms(&tstamp
);
504 if (ms
< duration_ms
- margin_ms
|| ms
> duration_ms
+ margin_ms
) {
505 snprintf(msg
, sizeof(msg
), "time mismatch: expected %dms got %lld", duration_ms
, ms
);
512 pthread_mutex_lock(&results_lock
);
515 case TEST_CLASS_SYSTEM
:
516 test_class_name
= "system";
518 * Anything specified as specific to this system
519 * should always be supported.
521 ksft_test_result(!skip
, "%s.%s.%s.%d.%d.%s.params\n",
522 test_class_name
, test_name
,
523 data
->card_name
, data
->device
,
525 snd_pcm_stream_name(data
->stream
));
532 ksft_test_result(pass
, "%s.%s.%s.%d.%d.%s\n",
533 test_class_name
, test_name
,
534 data
->card_name
, data
->device
,
536 snd_pcm_stream_name(data
->stream
));
538 ksft_test_result_skip("%s.%s.%s.%d.%d.%s\n",
539 test_class_name
, test_name
,
540 data
->card_name
, data
->device
,
542 snd_pcm_stream_name(data
->stream
));
545 ksft_print_msg("%s\n", msg
);
547 pthread_mutex_unlock(&results_lock
);
551 snd_pcm_close(handle
);
554 void run_time_tests(struct pcm_data
*pcm
, enum test_class
class,
557 const char *test_name
, *test_type
;
558 snd_config_t
*pcm_cfg
;
559 snd_config_iterator_t i
, next
;
564 cfg
= conf_get_subtree(cfg
, "test", NULL
);
568 snd_config_for_each(i
, next
, cfg
) {
569 pcm_cfg
= snd_config_iterator_entry(i
);
570 if (snd_config_get_id(pcm_cfg
, &test_name
) < 0)
571 ksft_exit_fail_msg("snd_config_get_id\n");
572 test_type
= conf_get_string(pcm_cfg
, "type", NULL
, "time");
573 if (strcmp(test_type
, "time") == 0)
574 test_pcm_time(pcm
, class, test_name
, pcm_cfg
);
576 ksft_exit_fail_msg("unknown test type '%s'\n", test_type
);
580 void *card_thread(void *data
)
582 struct card_data
*card
= data
;
583 struct pcm_data
*pcm
;
585 for (pcm
= pcm_list
; pcm
!= NULL
; pcm
= pcm
->next
) {
586 if (pcm
->card
!= card
->card
)
589 run_time_tests(pcm
, TEST_CLASS_DEFAULT
, default_pcm_config
);
590 run_time_tests(pcm
, TEST_CLASS_SYSTEM
, pcm
->pcm_config
);
598 struct card_data
*card
;
599 struct card_cfg_data
*conf
;
600 struct pcm_data
*pcm
;
601 snd_config_t
*global_config
, *cfg
;
602 int num_pcm_tests
= 0, num_tests
, num_std_pcm_tests
;
608 global_config
= conf_load_from_file("pcm-test.conf");
609 default_pcm_config
= conf_get_subtree(global_config
, "pcm", NULL
);
610 if (default_pcm_config
== NULL
)
611 ksft_exit_fail_msg("default pcm test configuration (pcm compound) is missing\n");
617 for (conf
= conf_cards
; conf
; conf
= conf
->next
)
621 num_std_pcm_tests
= conf_get_count(default_pcm_config
, "test", NULL
);
623 for (pcm
= pcm_list
; pcm
!= NULL
; pcm
= pcm
->next
) {
624 num_pcm_tests
+= num_std_pcm_tests
;
625 cfg
= pcm
->pcm_config
;
628 /* Setting params is reported as a separate test */
629 num_tests
= conf_get_count(cfg
, "test", NULL
) * 2;
631 num_pcm_tests
+= num_tests
;
634 ksft_set_plan(num_missing
+ num_pcm_tests
);
636 for (conf
= conf_cards
; conf
; conf
= conf
->next
)
638 ksft_test_result_fail("test.missing.%s.%s\n",
639 conf
->filename
, conf
->config_id
);
641 for (pcm
= pcm_missing
; pcm
!= NULL
; pcm
= pcm
->next
) {
642 ksft_test_result(false, "test.missing.%s.%d.%d.%s\n",
643 pcm
->card_name
, pcm
->device
, pcm
->subdevice
,
644 snd_pcm_stream_name(pcm
->stream
));
647 for (card
= card_list
; card
!= NULL
; card
= card
->next
) {
648 ret
= pthread_create(&card
->thread
, NULL
, card_thread
, card
);
650 ksft_exit_fail_msg("Failed to create card %d thread: %d (%s)\n",
656 for (card
= card_list
; card
!= NULL
; card
= card
->next
) {
657 ret
= pthread_join(card
->thread
, &thread_ret
);
659 ksft_exit_fail_msg("Failed to join card %d thread: %d (%s)\n",
665 snd_config_delete(global_config
);