Linux Audio

Check our new training course

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