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
;
28 struct card_data
*next
;
31 struct card_data
*card_list
= NULL
;
38 snd_pcm_stream_t stream
;
39 snd_config_t
*pcm_config
;
40 struct pcm_data
*next
;
43 struct pcm_data
*pcm_list
= NULL
;
46 struct pcm_data
*pcm_missing
= NULL
;
48 snd_config_t
*default_pcm_config
;
50 /* Lock while reporting results since kselftest doesn't */
51 pthread_mutex_t results_lock
= PTHREAD_MUTEX_INITIALIZER
;
58 void timestamp_now(timestamp_t
*tstamp
)
60 if (clock_gettime(CLOCK_MONOTONIC_RAW
, tstamp
))
61 ksft_exit_fail_msg("clock_get_time\n");
64 long long timestamp_diff_ms(timestamp_t
*tstamp
)
66 timestamp_t now
, diff
;
68 if (tstamp
->tv_nsec
> now
.tv_nsec
) {
69 diff
.tv_sec
= now
.tv_sec
- tstamp
->tv_sec
- 1;
70 diff
.tv_nsec
= (now
.tv_nsec
+ 1000000000L) - tstamp
->tv_nsec
;
72 diff
.tv_sec
= now
.tv_sec
- tstamp
->tv_sec
;
73 diff
.tv_nsec
= now
.tv_nsec
- tstamp
->tv_nsec
;
75 return (diff
.tv_sec
* 1000) + ((diff
.tv_nsec
+ 500000L) / 1000000L);
78 static long device_from_id(snd_config_t
*node
)
84 if (snd_config_get_id(node
, &id
))
85 ksft_exit_fail_msg("snd_config_get_id\n");
87 v
= strtol(id
, &end
, 10);
93 static void missing_device(int card
, int device
, int subdevice
, snd_pcm_stream_t stream
)
95 struct pcm_data
*pcm_data
;
97 for (pcm_data
= pcm_list
; pcm_data
!= NULL
; pcm_data
= pcm_data
->next
) {
98 if (pcm_data
->card
!= card
)
100 if (pcm_data
->device
!= device
)
102 if (pcm_data
->subdevice
!= subdevice
)
104 if (pcm_data
->stream
!= stream
)
108 pcm_data
= calloc(1, sizeof(*pcm_data
));
110 ksft_exit_fail_msg("Out of memory\n");
111 pcm_data
->card
= card
;
112 pcm_data
->device
= device
;
113 pcm_data
->subdevice
= subdevice
;
114 pcm_data
->stream
= stream
;
115 pcm_data
->next
= pcm_missing
;
116 pcm_missing
= pcm_data
;
120 static void missing_devices(int card
, snd_config_t
*card_config
)
122 snd_config_t
*pcm_config
, *node1
, *node2
;
123 snd_config_iterator_t i1
, i2
, next1
, next2
;
124 int device
, subdevice
;
126 pcm_config
= conf_get_subtree(card_config
, "pcm", NULL
);
129 snd_config_for_each(i1
, next1
, pcm_config
) {
130 node1
= snd_config_iterator_entry(i1
);
131 device
= device_from_id(node1
);
134 if (snd_config_get_type(node1
) != SND_CONFIG_TYPE_COMPOUND
)
136 snd_config_for_each(i2
, next2
, node1
) {
137 node2
= snd_config_iterator_entry(i2
);
138 subdevice
= device_from_id(node2
);
141 if (conf_get_subtree(node2
, "PLAYBACK", NULL
))
142 missing_device(card
, device
, subdevice
, SND_PCM_STREAM_PLAYBACK
);
143 if (conf_get_subtree(node2
, "CAPTURE", NULL
))
144 missing_device(card
, device
, subdevice
, SND_PCM_STREAM_CAPTURE
);
149 static void find_pcms(void)
151 char name
[32], key
[64];
152 char *card_name
, *card_longname
;
153 int card
, dev
, subdev
, count
, direction
, err
;
154 snd_pcm_stream_t stream
;
155 struct pcm_data
*pcm_data
;
157 snd_pcm_info_t
*pcm_info
;
158 snd_config_t
*config
, *card_config
, *pcm_config
;
159 struct card_data
*card_data
;
161 snd_pcm_info_alloca(&pcm_info
);
164 if (snd_card_next(&card
) < 0 || card
< 0)
167 config
= get_alsalib_config();
170 sprintf(name
, "hw:%d", card
);
172 err
= snd_ctl_open_lconf(&handle
, name
, 0, config
);
174 ksft_print_msg("Failed to get hctl for card %d: %s\n",
175 card
, snd_strerror(err
));
179 err
= snd_card_get_name(card
, &card_name
);
181 card_name
= "Unknown";
182 err
= snd_card_get_longname(card
, &card_longname
);
184 card_longname
= "Unknown";
185 ksft_print_msg("Card %d - %s (%s)\n", card
,
186 card_name
, card_longname
);
188 card_config
= conf_by_card(card
);
190 card_data
= calloc(1, sizeof(*card_data
));
192 ksft_exit_fail_msg("Out of memory\n");
193 card_data
->card
= card
;
194 card_data
->next
= card_list
;
195 card_list
= card_data
;
199 if (snd_ctl_pcm_next_device(handle
, &dev
) < 0)
200 ksft_exit_fail_msg("snd_ctl_pcm_next_device\n");
204 for (direction
= 0; direction
< 2; direction
++) {
205 stream
= direction
? SND_PCM_STREAM_CAPTURE
: SND_PCM_STREAM_PLAYBACK
;
206 sprintf(key
, "pcm.%d.%s", dev
, snd_pcm_stream_name(stream
));
207 pcm_config
= conf_get_subtree(card_config
, key
, NULL
);
208 if (conf_get_bool(card_config
, key
, "skip", false)) {
209 ksft_print_msg("skipping pcm %d.%d.%s\n", card
, dev
, snd_pcm_stream_name(stream
));
212 snd_pcm_info_set_device(pcm_info
, dev
);
213 snd_pcm_info_set_subdevice(pcm_info
, 0);
214 snd_pcm_info_set_stream(pcm_info
, stream
);
215 err
= snd_ctl_pcm_info(handle
, pcm_info
);
219 ksft_exit_fail_msg("snd_ctl_pcm_info: %d:%d:%d\n",
221 count
= snd_pcm_info_get_subdevices_count(pcm_info
);
222 for (subdev
= 0; subdev
< count
; subdev
++) {
223 sprintf(key
, "pcm.%d.%d.%s", dev
, subdev
, snd_pcm_stream_name(stream
));
224 if (conf_get_bool(card_config
, key
, "skip", false)) {
225 ksft_print_msg("skipping pcm %d.%d.%d.%s\n", card
, dev
,
226 subdev
, snd_pcm_stream_name(stream
));
229 pcm_data
= calloc(1, sizeof(*pcm_data
));
231 ksft_exit_fail_msg("Out of memory\n");
232 pcm_data
->card
= card
;
233 pcm_data
->device
= dev
;
234 pcm_data
->subdevice
= subdev
;
235 pcm_data
->stream
= stream
;
236 pcm_data
->pcm_config
= conf_get_subtree(card_config
, key
, NULL
);
237 pcm_data
->next
= pcm_list
;
243 /* check for missing devices */
244 missing_devices(card
, card_config
);
247 snd_ctl_close(handle
);
248 if (snd_card_next(&card
) < 0) {
249 ksft_print_msg("snd_card_next");
254 snd_config_delete(config
);
257 static void test_pcm_time(struct pcm_data
*data
, enum test_class
class,
258 const char *test_name
, snd_config_t
*pcm_cfg
)
260 char name
[64], msg
[256];
261 const int duration_s
= 2, margin_ms
= 100;
262 const int duration_ms
= duration_s
* 1000;
265 snd_pcm_t
*handle
= NULL
;
266 snd_pcm_access_t access
= SND_PCM_ACCESS_RW_INTERLEAVED
;
267 snd_pcm_format_t format
, old_format
;
268 const char *alt_formats
[8];
269 unsigned char *samples
= NULL
;
270 snd_pcm_sframes_t frames
;
272 long rate
, channels
, period_size
, buffer_size
;
274 snd_pcm_uframes_t rperiod_size
, rbuffer_size
, start_threshold
;
277 snd_pcm_hw_params_t
*hw_params
;
278 snd_pcm_sw_params_t
*sw_params
;
279 const char *test_class_name
;
284 case TEST_CLASS_DEFAULT
:
285 test_class_name
= "default";
287 case TEST_CLASS_SYSTEM
:
288 test_class_name
= "system";
291 ksft_exit_fail_msg("Unknown test class %d\n", class);
295 desc
= conf_get_string(pcm_cfg
, "description", NULL
, NULL
);
297 ksft_print_msg("%s.%s.%d.%d.%d.%s - %s\n",
298 test_class_name
, test_name
,
299 data
->card
, data
->device
, data
->subdevice
,
300 snd_pcm_stream_name(data
->stream
),
304 snd_pcm_hw_params_alloca(&hw_params
);
305 snd_pcm_sw_params_alloca(&sw_params
);
307 cs
= conf_get_string(pcm_cfg
, "format", NULL
, "S16_LE");
308 format
= snd_pcm_format_value(cs
);
309 if (format
== SND_PCM_FORMAT_UNKNOWN
)
310 ksft_exit_fail_msg("Wrong format '%s'\n", cs
);
311 conf_get_string_array(pcm_cfg
, "alt_formats", NULL
,
312 alt_formats
, ARRAY_SIZE(alt_formats
), NULL
);
313 rate
= conf_get_long(pcm_cfg
, "rate", NULL
, 48000);
314 channels
= conf_get_long(pcm_cfg
, "channels", NULL
, 2);
315 period_size
= conf_get_long(pcm_cfg
, "period_size", NULL
, 4096);
316 buffer_size
= conf_get_long(pcm_cfg
, "buffer_size", NULL
, 16384);
318 samples
= malloc((rate
* channels
* snd_pcm_format_physical_width(format
)) / 8);
320 ksft_exit_fail_msg("Out of memory\n");
321 snd_pcm_format_set_silence(format
, samples
, rate
* channels
);
323 sprintf(name
, "hw:%d,%d,%d", data
->card
, data
->device
, data
->subdevice
);
324 err
= snd_pcm_open(&handle
, name
, data
->stream
, 0);
326 snprintf(msg
, sizeof(msg
), "Failed to get pcm handle: %s", snd_strerror(err
));
330 err
= snd_pcm_hw_params_any(handle
, hw_params
);
332 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_any: %s", snd_strerror(err
));
335 err
= snd_pcm_hw_params_set_rate_resample(handle
, hw_params
, 0);
337 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_rate_resample: %s", snd_strerror(err
));
340 err
= snd_pcm_hw_params_set_access(handle
, hw_params
, access
);
342 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_access %s: %s",
343 snd_pcm_access_name(access
), snd_strerror(err
));
348 err
= snd_pcm_hw_params_set_format(handle
, hw_params
, format
);
351 if (i
< ARRAY_SIZE(alt_formats
) && alt_formats
[i
]) {
353 format
= snd_pcm_format_value(alt_formats
[i
]);
354 if (format
!= SND_PCM_FORMAT_UNKNOWN
) {
355 ksft_print_msg("%s.%d.%d.%d.%s.%s format %s -> %s\n",
357 data
->card
, data
->device
, data
->subdevice
,
358 snd_pcm_stream_name(data
->stream
),
359 snd_pcm_access_name(access
),
360 snd_pcm_format_name(old_format
),
361 snd_pcm_format_name(format
));
362 samples
= realloc(samples
, (rate
* channels
*
363 snd_pcm_format_physical_width(format
)) / 8);
365 ksft_exit_fail_msg("Out of memory\n");
366 snd_pcm_format_set_silence(format
, samples
, rate
* channels
);
370 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_format %s: %s",
371 snd_pcm_format_name(format
), snd_strerror(err
));
374 err
= snd_pcm_hw_params_set_channels(handle
, hw_params
, channels
);
376 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_channels %ld: %s", channels
, snd_strerror(err
));
380 err
= snd_pcm_hw_params_set_rate_near(handle
, hw_params
, &rrate
, 0);
382 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_rate %ld: %s", rate
, snd_strerror(err
));
386 snprintf(msg
, sizeof(msg
), "rate mismatch %ld != %d", rate
, rrate
);
389 rperiod_size
= period_size
;
390 err
= snd_pcm_hw_params_set_period_size_near(handle
, hw_params
, &rperiod_size
, 0);
392 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_period_size %ld: %s", period_size
, snd_strerror(err
));
395 rbuffer_size
= buffer_size
;
396 err
= snd_pcm_hw_params_set_buffer_size_near(handle
, hw_params
, &rbuffer_size
);
398 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params_set_buffer_size %ld: %s", buffer_size
, snd_strerror(err
));
401 err
= snd_pcm_hw_params(handle
, hw_params
);
403 snprintf(msg
, sizeof(msg
), "snd_pcm_hw_params: %s", snd_strerror(err
));
407 err
= snd_pcm_sw_params_current(handle
, sw_params
);
409 snprintf(msg
, sizeof(msg
), "snd_pcm_sw_params_current: %s", snd_strerror(err
));
412 if (data
->stream
== SND_PCM_STREAM_PLAYBACK
) {
413 start_threshold
= (rbuffer_size
/ rperiod_size
) * rperiod_size
;
415 start_threshold
= rperiod_size
;
417 err
= snd_pcm_sw_params_set_start_threshold(handle
, sw_params
, start_threshold
);
419 snprintf(msg
, sizeof(msg
), "snd_pcm_sw_params_set_start_threshold %ld: %s", (long)start_threshold
, snd_strerror(err
));
422 err
= snd_pcm_sw_params_set_avail_min(handle
, sw_params
, rperiod_size
);
424 snprintf(msg
, sizeof(msg
), "snd_pcm_sw_params_set_avail_min %ld: %s", (long)rperiod_size
, snd_strerror(err
));
427 err
= snd_pcm_sw_params(handle
, sw_params
);
429 snprintf(msg
, sizeof(msg
), "snd_pcm_sw_params: %s", snd_strerror(err
));
433 ksft_print_msg("%s.%s.%d.%d.%d.%s hw_params.%s.%s.%ld.%ld.%ld.%ld sw_params.%ld\n",
434 test_class_name
, test_name
,
435 data
->card
, data
->device
, data
->subdevice
,
436 snd_pcm_stream_name(data
->stream
),
437 snd_pcm_access_name(access
),
438 snd_pcm_format_name(format
),
439 (long)rate
, (long)channels
,
440 (long)rperiod_size
, (long)rbuffer_size
,
441 (long)start_threshold
);
443 /* Set all the params, actually run the test */
446 timestamp_now(&tstamp
);
447 for (i
= 0; i
< duration_s
; i
++) {
448 if (data
->stream
== SND_PCM_STREAM_PLAYBACK
) {
449 frames
= snd_pcm_writei(handle
, samples
, rate
);
451 snprintf(msg
, sizeof(msg
),
452 "Write failed: expected %ld, wrote %li", rate
, frames
);
456 snprintf(msg
, sizeof(msg
),
457 "expected %ld, wrote %li", rate
, frames
);
461 frames
= snd_pcm_readi(handle
, samples
, rate
);
463 snprintf(msg
, sizeof(msg
),
464 "expected %ld, wrote %li", rate
, frames
);
468 snprintf(msg
, sizeof(msg
),
469 "expected %ld, wrote %li", rate
, frames
);
475 snd_pcm_drain(handle
);
476 ms
= timestamp_diff_ms(&tstamp
);
477 if (ms
< duration_ms
- margin_ms
|| ms
> duration_ms
+ margin_ms
) {
478 snprintf(msg
, sizeof(msg
), "time mismatch: expected %dms got %lld", duration_ms
, ms
);
485 pthread_mutex_lock(&results_lock
);
488 case TEST_CLASS_SYSTEM
:
489 test_class_name
= "system";
491 * Anything specified as specific to this system
492 * should always be supported.
494 ksft_test_result(!skip
, "%s.%s.%d.%d.%d.%s.params\n",
495 test_class_name
, test_name
,
496 data
->card
, data
->device
, data
->subdevice
,
497 snd_pcm_stream_name(data
->stream
));
504 ksft_test_result(pass
, "%s.%s.%d.%d.%d.%s\n",
505 test_class_name
, test_name
,
506 data
->card
, data
->device
, data
->subdevice
,
507 snd_pcm_stream_name(data
->stream
));
509 ksft_test_result_skip("%s.%s.%d.%d.%d.%s\n",
510 test_class_name
, test_name
,
511 data
->card
, data
->device
, data
->subdevice
,
512 snd_pcm_stream_name(data
->stream
));
515 ksft_print_msg("%s\n", msg
);
517 pthread_mutex_unlock(&results_lock
);
521 snd_pcm_close(handle
);
524 void run_time_tests(struct pcm_data
*pcm
, enum test_class
class,
527 const char *test_name
, *test_type
;
528 snd_config_t
*pcm_cfg
;
529 snd_config_iterator_t i
, next
;
534 cfg
= conf_get_subtree(cfg
, "test", NULL
);
538 snd_config_for_each(i
, next
, cfg
) {
539 pcm_cfg
= snd_config_iterator_entry(i
);
540 if (snd_config_get_id(pcm_cfg
, &test_name
) < 0)
541 ksft_exit_fail_msg("snd_config_get_id\n");
542 test_type
= conf_get_string(pcm_cfg
, "type", NULL
, "time");
543 if (strcmp(test_type
, "time") == 0)
544 test_pcm_time(pcm
, class, test_name
, pcm_cfg
);
546 ksft_exit_fail_msg("unknown test type '%s'\n", test_type
);
550 void *card_thread(void *data
)
552 struct card_data
*card
= data
;
553 struct pcm_data
*pcm
;
555 for (pcm
= pcm_list
; pcm
!= NULL
; pcm
= pcm
->next
) {
556 if (pcm
->card
!= card
->card
)
559 run_time_tests(pcm
, TEST_CLASS_DEFAULT
, default_pcm_config
);
560 run_time_tests(pcm
, TEST_CLASS_SYSTEM
, pcm
->pcm_config
);
568 struct card_data
*card
;
569 struct pcm_data
*pcm
;
570 snd_config_t
*global_config
, *cfg
;
571 int num_pcm_tests
= 0, num_tests
, num_std_pcm_tests
;
577 global_config
= conf_load_from_file("pcm-test.conf");
578 default_pcm_config
= conf_get_subtree(global_config
, "pcm", NULL
);
579 if (default_pcm_config
== NULL
)
580 ksft_exit_fail_msg("default pcm test configuration (pcm compound) is missing\n");
586 num_std_pcm_tests
= conf_get_count(default_pcm_config
, "test", NULL
);
588 for (pcm
= pcm_list
; pcm
!= NULL
; pcm
= pcm
->next
) {
589 num_pcm_tests
+= num_std_pcm_tests
;
590 cfg
= pcm
->pcm_config
;
593 /* Setting params is reported as a separate test */
594 num_tests
= conf_get_count(cfg
, "test", NULL
) * 2;
596 num_pcm_tests
+= num_tests
;
599 ksft_set_plan(num_missing
+ num_pcm_tests
);
601 for (pcm
= pcm_missing
; pcm
!= NULL
; pcm
= pcm
->next
) {
602 ksft_test_result(false, "test.missing.%d.%d.%d.%s\n",
603 pcm
->card
, pcm
->device
, pcm
->subdevice
,
604 snd_pcm_stream_name(pcm
->stream
));
607 for (card
= card_list
; card
!= NULL
; card
= card
->next
) {
608 ret
= pthread_create(&card
->thread
, NULL
, card_thread
, card
);
610 ksft_exit_fail_msg("Failed to create card %d thread: %d (%s)\n",
616 for (card
= card_list
; card
!= NULL
; card
= card
->next
) {
617 ret
= pthread_join(card
->thread
, &thread_ret
);
619 ksft_exit_fail_msg("Failed to join card %d thread: %d (%s)\n",
625 snd_config_delete(global_config
);