xref: /openbmc/linux/tools/testing/selftests/alsa/pcm-test.c (revision c48cafc241bff09d7ea682982ff96d532e64e2ed)
1aba51cd0SJaroslav Kysela // SPDX-License-Identifier: GPL-2.0
2aba51cd0SJaroslav Kysela //
3aba51cd0SJaroslav Kysela // kselftest for the ALSA PCM API
4aba51cd0SJaroslav Kysela //
5aba51cd0SJaroslav Kysela // Original author: Jaroslav Kysela <perex@perex.cz>
6aba51cd0SJaroslav Kysela // Copyright (c) 2022 Red Hat Inc.
7aba51cd0SJaroslav Kysela 
8aba51cd0SJaroslav Kysela // This test will iterate over all cards detected in the system, exercising
9aba51cd0SJaroslav Kysela // every PCM device it can find.  This may conflict with other system
10aba51cd0SJaroslav Kysela // software if there is audio activity so is best run on a system with a
11aba51cd0SJaroslav Kysela // minimal active userspace.
12aba51cd0SJaroslav Kysela 
13aba51cd0SJaroslav Kysela #include <stdio.h>
14aba51cd0SJaroslav Kysela #include <stdlib.h>
15aba51cd0SJaroslav Kysela #include <stdbool.h>
16aba51cd0SJaroslav Kysela #include <errno.h>
17aba51cd0SJaroslav Kysela #include <assert.h>
18aba51cd0SJaroslav Kysela 
19aba51cd0SJaroslav Kysela #include "../kselftest.h"
20aba51cd0SJaroslav Kysela #include "alsa-local.h"
21aba51cd0SJaroslav Kysela 
22aba51cd0SJaroslav Kysela typedef struct timespec timestamp_t;
23aba51cd0SJaroslav Kysela 
24aba51cd0SJaroslav Kysela struct pcm_data {
25aba51cd0SJaroslav Kysela 	snd_pcm_t *handle;
26aba51cd0SJaroslav Kysela 	int card;
27aba51cd0SJaroslav Kysela 	int device;
28aba51cd0SJaroslav Kysela 	int subdevice;
29aba51cd0SJaroslav Kysela 	snd_pcm_stream_t stream;
30aba51cd0SJaroslav Kysela 	snd_config_t *pcm_config;
31aba51cd0SJaroslav Kysela 	struct pcm_data *next;
32aba51cd0SJaroslav Kysela };
33aba51cd0SJaroslav Kysela 
34aba51cd0SJaroslav Kysela int num_pcms = 0;
35aba51cd0SJaroslav Kysela struct pcm_data *pcm_list = NULL;
36aba51cd0SJaroslav Kysela 
37aba51cd0SJaroslav Kysela int num_missing = 0;
38aba51cd0SJaroslav Kysela struct pcm_data *pcm_missing = NULL;
39aba51cd0SJaroslav Kysela 
40aba51cd0SJaroslav Kysela void timestamp_now(timestamp_t *tstamp)
41aba51cd0SJaroslav Kysela {
42aba51cd0SJaroslav Kysela 	if (clock_gettime(CLOCK_MONOTONIC_RAW, tstamp))
43aba51cd0SJaroslav Kysela 		ksft_exit_fail_msg("clock_get_time\n");
44aba51cd0SJaroslav Kysela }
45aba51cd0SJaroslav Kysela 
46aba51cd0SJaroslav Kysela long long timestamp_diff_ms(timestamp_t *tstamp)
47aba51cd0SJaroslav Kysela {
48aba51cd0SJaroslav Kysela 	timestamp_t now, diff;
49aba51cd0SJaroslav Kysela 	timestamp_now(&now);
50aba51cd0SJaroslav Kysela 	if (tstamp->tv_nsec > now.tv_nsec) {
51aba51cd0SJaroslav Kysela 		diff.tv_sec = now.tv_sec - tstamp->tv_sec - 1;
52aba51cd0SJaroslav Kysela 		diff.tv_nsec = (now.tv_nsec + 1000000000L) - tstamp->tv_nsec;
53aba51cd0SJaroslav Kysela 	} else {
54aba51cd0SJaroslav Kysela 		diff.tv_sec = now.tv_sec - tstamp->tv_sec;
55aba51cd0SJaroslav Kysela 		diff.tv_nsec = now.tv_nsec - tstamp->tv_nsec;
56aba51cd0SJaroslav Kysela 	}
57aba51cd0SJaroslav Kysela 	return (diff.tv_sec * 1000) + ((diff.tv_nsec + 500000L) / 1000000L);
58aba51cd0SJaroslav Kysela }
59aba51cd0SJaroslav Kysela 
60aba51cd0SJaroslav Kysela static long device_from_id(snd_config_t *node)
61aba51cd0SJaroslav Kysela {
62aba51cd0SJaroslav Kysela 	const char *id;
63aba51cd0SJaroslav Kysela 	char *end;
64aba51cd0SJaroslav Kysela 	long v;
65aba51cd0SJaroslav Kysela 
66aba51cd0SJaroslav Kysela 	if (snd_config_get_id(node, &id))
67aba51cd0SJaroslav Kysela 		ksft_exit_fail_msg("snd_config_get_id\n");
68aba51cd0SJaroslav Kysela 	errno = 0;
69aba51cd0SJaroslav Kysela 	v = strtol(id, &end, 10);
70aba51cd0SJaroslav Kysela 	if (errno || *end)
71aba51cd0SJaroslav Kysela 		return -1;
72aba51cd0SJaroslav Kysela 	return v;
73aba51cd0SJaroslav Kysela }
74aba51cd0SJaroslav Kysela 
75aba51cd0SJaroslav Kysela static void missing_device(int card, int device, int subdevice, snd_pcm_stream_t stream)
76aba51cd0SJaroslav Kysela {
77aba51cd0SJaroslav Kysela 	struct pcm_data *pcm_data;
78aba51cd0SJaroslav Kysela 
79aba51cd0SJaroslav Kysela 	for (pcm_data = pcm_list; pcm_data != NULL; pcm_data = pcm_data->next) {
80aba51cd0SJaroslav Kysela 		if (pcm_data->card != card)
81aba51cd0SJaroslav Kysela 			continue;
82aba51cd0SJaroslav Kysela 		if (pcm_data->device != device)
83aba51cd0SJaroslav Kysela 			continue;
84aba51cd0SJaroslav Kysela 		if (pcm_data->subdevice != subdevice)
85aba51cd0SJaroslav Kysela 			continue;
86aba51cd0SJaroslav Kysela 		if (pcm_data->stream != stream)
87aba51cd0SJaroslav Kysela 			continue;
88aba51cd0SJaroslav Kysela 		return;
89aba51cd0SJaroslav Kysela 	}
90aba51cd0SJaroslav Kysela 	pcm_data = calloc(1, sizeof(*pcm_data));
91aba51cd0SJaroslav Kysela 	if (!pcm_data)
92aba51cd0SJaroslav Kysela 		ksft_exit_fail_msg("Out of memory\n");
93aba51cd0SJaroslav Kysela 	pcm_data->card = card;
94aba51cd0SJaroslav Kysela 	pcm_data->device = device;
95aba51cd0SJaroslav Kysela 	pcm_data->subdevice = subdevice;
96aba51cd0SJaroslav Kysela 	pcm_data->stream = stream;
97aba51cd0SJaroslav Kysela 	pcm_data->next = pcm_missing;
98aba51cd0SJaroslav Kysela 	pcm_missing = pcm_data;
99aba51cd0SJaroslav Kysela 	num_missing++;
100aba51cd0SJaroslav Kysela }
101aba51cd0SJaroslav Kysela 
102aba51cd0SJaroslav Kysela static void missing_devices(int card, snd_config_t *card_config)
103aba51cd0SJaroslav Kysela {
104aba51cd0SJaroslav Kysela 	snd_config_t *pcm_config, *node1, *node2;
105aba51cd0SJaroslav Kysela 	snd_config_iterator_t i1, i2, next1, next2;
106aba51cd0SJaroslav Kysela 	int device, subdevice;
107aba51cd0SJaroslav Kysela 
108aba51cd0SJaroslav Kysela 	pcm_config = conf_get_subtree(card_config, "pcm", NULL);
109aba51cd0SJaroslav Kysela 	if (!pcm_config)
110aba51cd0SJaroslav Kysela 		return;
111aba51cd0SJaroslav Kysela 	snd_config_for_each(i1, next1, pcm_config) {
112aba51cd0SJaroslav Kysela 		node1 = snd_config_iterator_entry(i1);
113aba51cd0SJaroslav Kysela 		device = device_from_id(node1);
114aba51cd0SJaroslav Kysela 		if (device < 0)
115aba51cd0SJaroslav Kysela 			continue;
116aba51cd0SJaroslav Kysela 		if (snd_config_get_type(node1) != SND_CONFIG_TYPE_COMPOUND)
117aba51cd0SJaroslav Kysela 			continue;
118aba51cd0SJaroslav Kysela 		snd_config_for_each(i2, next2, node1) {
119aba51cd0SJaroslav Kysela 			node2 = snd_config_iterator_entry(i2);
120aba51cd0SJaroslav Kysela 			subdevice = device_from_id(node2);
121aba51cd0SJaroslav Kysela 			if (subdevice < 0)
122aba51cd0SJaroslav Kysela 				continue;
123aba51cd0SJaroslav Kysela 			if (conf_get_subtree(node2, "PLAYBACK", NULL))
124aba51cd0SJaroslav Kysela 				missing_device(card, device, subdevice, SND_PCM_STREAM_PLAYBACK);
125aba51cd0SJaroslav Kysela 			if (conf_get_subtree(node2, "CAPTURE", NULL))
126aba51cd0SJaroslav Kysela 				missing_device(card, device, subdevice, SND_PCM_STREAM_CAPTURE);
127aba51cd0SJaroslav Kysela 		}
128aba51cd0SJaroslav Kysela 	}
129aba51cd0SJaroslav Kysela }
130aba51cd0SJaroslav Kysela 
131aba51cd0SJaroslav Kysela static void find_pcms(void)
132aba51cd0SJaroslav Kysela {
133aba51cd0SJaroslav Kysela 	char name[32], key[64];
134aba51cd0SJaroslav Kysela 	int card, dev, subdev, count, direction, err;
135aba51cd0SJaroslav Kysela 	snd_pcm_stream_t stream;
136aba51cd0SJaroslav Kysela 	struct pcm_data *pcm_data;
137aba51cd0SJaroslav Kysela 	snd_ctl_t *handle;
138aba51cd0SJaroslav Kysela 	snd_pcm_info_t *pcm_info;
139aba51cd0SJaroslav Kysela 	snd_config_t *config, *card_config, *pcm_config;
140aba51cd0SJaroslav Kysela 
141aba51cd0SJaroslav Kysela 	snd_pcm_info_alloca(&pcm_info);
142aba51cd0SJaroslav Kysela 
143aba51cd0SJaroslav Kysela 	card = -1;
144aba51cd0SJaroslav Kysela 	if (snd_card_next(&card) < 0 || card < 0)
145aba51cd0SJaroslav Kysela 		return;
146aba51cd0SJaroslav Kysela 
147aba51cd0SJaroslav Kysela 	config = get_alsalib_config();
148aba51cd0SJaroslav Kysela 
149aba51cd0SJaroslav Kysela 	while (card >= 0) {
150aba51cd0SJaroslav Kysela 		sprintf(name, "hw:%d", card);
151aba51cd0SJaroslav Kysela 
152aba51cd0SJaroslav Kysela 		err = snd_ctl_open_lconf(&handle, name, 0, config);
153aba51cd0SJaroslav Kysela 		if (err < 0) {
154aba51cd0SJaroslav Kysela 			ksft_print_msg("Failed to get hctl for card %d: %s\n",
155aba51cd0SJaroslav Kysela 				       card, snd_strerror(err));
156aba51cd0SJaroslav Kysela 			goto next_card;
157aba51cd0SJaroslav Kysela 		}
158aba51cd0SJaroslav Kysela 
159aba51cd0SJaroslav Kysela 		card_config = conf_by_card(card);
160aba51cd0SJaroslav Kysela 
161aba51cd0SJaroslav Kysela 		dev = -1;
162aba51cd0SJaroslav Kysela 		while (1) {
163aba51cd0SJaroslav Kysela 			if (snd_ctl_pcm_next_device(handle, &dev) < 0)
164aba51cd0SJaroslav Kysela 				ksft_exit_fail_msg("snd_ctl_pcm_next_device\n");
165aba51cd0SJaroslav Kysela 			if (dev < 0)
166aba51cd0SJaroslav Kysela 				break;
167aba51cd0SJaroslav Kysela 
168aba51cd0SJaroslav Kysela 			for (direction = 0; direction < 2; direction++) {
169aba51cd0SJaroslav Kysela 				stream = direction ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK;
170aba51cd0SJaroslav Kysela 				sprintf(key, "pcm.%d.%s", dev, snd_pcm_stream_name(stream));
171aba51cd0SJaroslav Kysela 				pcm_config = conf_get_subtree(card_config, key, NULL);
172aba51cd0SJaroslav Kysela 				if (conf_get_bool(card_config, key, "skip", false)) {
173aba51cd0SJaroslav Kysela 					ksft_print_msg("skipping pcm %d.%d.%s\n", card, dev, snd_pcm_stream_name(stream));
174aba51cd0SJaroslav Kysela 					continue;
175aba51cd0SJaroslav Kysela 				}
176aba51cd0SJaroslav Kysela 				snd_pcm_info_set_device(pcm_info, dev);
177aba51cd0SJaroslav Kysela 				snd_pcm_info_set_subdevice(pcm_info, 0);
178aba51cd0SJaroslav Kysela 				snd_pcm_info_set_stream(pcm_info, stream);
179aba51cd0SJaroslav Kysela 				err = snd_ctl_pcm_info(handle, pcm_info);
180aba51cd0SJaroslav Kysela 				if (err == -ENOENT)
181aba51cd0SJaroslav Kysela 					continue;
182aba51cd0SJaroslav Kysela 				if (err < 0)
183aba51cd0SJaroslav Kysela 					ksft_exit_fail_msg("snd_ctl_pcm_info: %d:%d:%d\n",
184aba51cd0SJaroslav Kysela 							   dev, 0, stream);
185aba51cd0SJaroslav Kysela 				count = snd_pcm_info_get_subdevices_count(pcm_info);
186aba51cd0SJaroslav Kysela 				for (subdev = 0; subdev < count; subdev++) {
187aba51cd0SJaroslav Kysela 					sprintf(key, "pcm.%d.%d.%s", dev, subdev, snd_pcm_stream_name(stream));
188aba51cd0SJaroslav Kysela 					if (conf_get_bool(card_config, key, "skip", false)) {
189aba51cd0SJaroslav Kysela 						ksft_print_msg("skipping pcm %d.%d.%d.%s\n", card, dev,
190aba51cd0SJaroslav Kysela 							       subdev, snd_pcm_stream_name(stream));
191aba51cd0SJaroslav Kysela 						continue;
192aba51cd0SJaroslav Kysela 					}
193aba51cd0SJaroslav Kysela 					pcm_data = calloc(1, sizeof(*pcm_data));
194aba51cd0SJaroslav Kysela 					if (!pcm_data)
195aba51cd0SJaroslav Kysela 						ksft_exit_fail_msg("Out of memory\n");
196aba51cd0SJaroslav Kysela 					pcm_data->card = card;
197aba51cd0SJaroslav Kysela 					pcm_data->device = dev;
198aba51cd0SJaroslav Kysela 					pcm_data->subdevice = subdev;
199aba51cd0SJaroslav Kysela 					pcm_data->stream = stream;
200aba51cd0SJaroslav Kysela 					pcm_data->pcm_config = conf_get_subtree(card_config, key, NULL);
201aba51cd0SJaroslav Kysela 					pcm_data->next = pcm_list;
202aba51cd0SJaroslav Kysela 					pcm_list = pcm_data;
203aba51cd0SJaroslav Kysela 					num_pcms++;
204aba51cd0SJaroslav Kysela 				}
205aba51cd0SJaroslav Kysela 			}
206aba51cd0SJaroslav Kysela 		}
207aba51cd0SJaroslav Kysela 
208aba51cd0SJaroslav Kysela 		/* check for missing devices */
209aba51cd0SJaroslav Kysela 		missing_devices(card, card_config);
210aba51cd0SJaroslav Kysela 
211aba51cd0SJaroslav Kysela 	next_card:
212aba51cd0SJaroslav Kysela 		snd_ctl_close(handle);
213aba51cd0SJaroslav Kysela 		if (snd_card_next(&card) < 0) {
214aba51cd0SJaroslav Kysela 			ksft_print_msg("snd_card_next");
215aba51cd0SJaroslav Kysela 			break;
216aba51cd0SJaroslav Kysela 		}
217aba51cd0SJaroslav Kysela 	}
218aba51cd0SJaroslav Kysela 
219aba51cd0SJaroslav Kysela 	snd_config_delete(config);
220aba51cd0SJaroslav Kysela }
221aba51cd0SJaroslav Kysela 
222aba51cd0SJaroslav Kysela static void test_pcm_time1(struct pcm_data *data,
223*c48cafc2SMark Brown 			   const char *cfg_prefix, const char *sformat,
224*c48cafc2SMark Brown 			   long srate, long schannels,
225*c48cafc2SMark Brown 			   long speriod_size, long sbuffer_size)
226aba51cd0SJaroslav Kysela {
227aba51cd0SJaroslav Kysela 	char name[64], key[128], msg[256];
228aba51cd0SJaroslav Kysela 	const char *cs;
229aba51cd0SJaroslav Kysela 	int i, err;
230aba51cd0SJaroslav Kysela 	snd_pcm_t *handle = NULL;
231aba51cd0SJaroslav Kysela 	snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED;
232aba51cd0SJaroslav Kysela 	snd_pcm_format_t format;
233aba51cd0SJaroslav Kysela 	unsigned char *samples = NULL;
234aba51cd0SJaroslav Kysela 	snd_pcm_sframes_t frames;
235aba51cd0SJaroslav Kysela 	long long ms;
236aba51cd0SJaroslav Kysela 	long rate, channels, period_size, buffer_size;
237aba51cd0SJaroslav Kysela 	unsigned int rrate;
238aba51cd0SJaroslav Kysela 	snd_pcm_uframes_t rperiod_size, rbuffer_size, start_threshold;
239aba51cd0SJaroslav Kysela 	timestamp_t tstamp;
240aba51cd0SJaroslav Kysela 	bool pass = false, automatic = true;
241aba51cd0SJaroslav Kysela 	snd_pcm_hw_params_t *hw_params;
242aba51cd0SJaroslav Kysela 	snd_pcm_sw_params_t *sw_params;
243aba51cd0SJaroslav Kysela 
244aba51cd0SJaroslav Kysela 	snd_pcm_hw_params_alloca(&hw_params);
245aba51cd0SJaroslav Kysela 	snd_pcm_sw_params_alloca(&sw_params);
246aba51cd0SJaroslav Kysela 
247*c48cafc2SMark Brown 	cs = conf_get_string(data->pcm_config, cfg_prefix, "format", sformat);
248aba51cd0SJaroslav Kysela 	format = snd_pcm_format_value(cs);
249aba51cd0SJaroslav Kysela 	if (format == SND_PCM_FORMAT_UNKNOWN)
250aba51cd0SJaroslav Kysela 		ksft_exit_fail_msg("Wrong format '%s'\n", cs);
251*c48cafc2SMark Brown 	rate = conf_get_long(data->pcm_config, cfg_prefix, "rate", srate);
252*c48cafc2SMark Brown 	channels = conf_get_long(data->pcm_config, cfg_prefix, "channels", schannels);
253*c48cafc2SMark Brown 	period_size = conf_get_long(data->pcm_config, cfg_prefix, "period_size", speriod_size);
254*c48cafc2SMark Brown 	buffer_size = conf_get_long(data->pcm_config, cfg_prefix, "buffer_size", sbuffer_size);
255aba51cd0SJaroslav Kysela 
256*c48cafc2SMark Brown 	automatic = strcmp(sformat, snd_pcm_format_name(format)) == 0 &&
257*c48cafc2SMark Brown 			srate == rate &&
258*c48cafc2SMark Brown 			schannels == channels &&
259*c48cafc2SMark Brown 			speriod_size == period_size &&
260*c48cafc2SMark Brown 			sbuffer_size == buffer_size;
261aba51cd0SJaroslav Kysela 
262aba51cd0SJaroslav Kysela 	samples = malloc((rate * channels * snd_pcm_format_physical_width(format)) / 8);
263aba51cd0SJaroslav Kysela 	if (!samples)
264aba51cd0SJaroslav Kysela 		ksft_exit_fail_msg("Out of memory\n");
265aba51cd0SJaroslav Kysela 	snd_pcm_format_set_silence(format, samples, rate * channels);
266aba51cd0SJaroslav Kysela 
267aba51cd0SJaroslav Kysela 	sprintf(name, "hw:%d,%d,%d", data->card, data->device, data->subdevice);
268aba51cd0SJaroslav Kysela 	err = snd_pcm_open(&handle, name, data->stream, 0);
269aba51cd0SJaroslav Kysela 	if (err < 0) {
270aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "Failed to get pcm handle: %s", snd_strerror(err));
271aba51cd0SJaroslav Kysela 		goto __close;
272aba51cd0SJaroslav Kysela 	}
273aba51cd0SJaroslav Kysela 
274aba51cd0SJaroslav Kysela 	err = snd_pcm_hw_params_any(handle, hw_params);
275aba51cd0SJaroslav Kysela 	if (err < 0) {
276aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "snd_pcm_hw_params_any: %s", snd_strerror(err));
277aba51cd0SJaroslav Kysela 		goto __close;
278aba51cd0SJaroslav Kysela 	}
279aba51cd0SJaroslav Kysela 	err = snd_pcm_hw_params_set_rate_resample(handle, hw_params, 0);
280aba51cd0SJaroslav Kysela 	if (err < 0) {
281aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_rate_resample: %s", snd_strerror(err));
282aba51cd0SJaroslav Kysela 		goto __close;
283aba51cd0SJaroslav Kysela 	}
284aba51cd0SJaroslav Kysela 	err = snd_pcm_hw_params_set_access(handle, hw_params, access);
285aba51cd0SJaroslav Kysela 	if (err < 0) {
286aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_access %s: %s",
287aba51cd0SJaroslav Kysela 					   snd_pcm_access_name(access), snd_strerror(err));
288aba51cd0SJaroslav Kysela 		goto __close;
289aba51cd0SJaroslav Kysela 	}
290aba51cd0SJaroslav Kysela __format:
291aba51cd0SJaroslav Kysela 	err = snd_pcm_hw_params_set_format(handle, hw_params, format);
292aba51cd0SJaroslav Kysela 	if (err < 0) {
293aba51cd0SJaroslav Kysela 		if (automatic && format == SND_PCM_FORMAT_S16_LE) {
294aba51cd0SJaroslav Kysela 			format = SND_PCM_FORMAT_S32_LE;
295aba51cd0SJaroslav Kysela 			ksft_print_msg("%s.%d.%d.%d.%s.%s format S16_LE -> S32_LE\n",
296*c48cafc2SMark Brown 					 cfg_prefix,
297aba51cd0SJaroslav Kysela 					 data->card, data->device, data->subdevice,
298aba51cd0SJaroslav Kysela 					 snd_pcm_stream_name(data->stream),
299aba51cd0SJaroslav Kysela 					 snd_pcm_access_name(access));
300aba51cd0SJaroslav Kysela 		}
301aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_format %s: %s",
302aba51cd0SJaroslav Kysela 					   snd_pcm_format_name(format), snd_strerror(err));
303aba51cd0SJaroslav Kysela 		goto __close;
304aba51cd0SJaroslav Kysela 	}
305*c48cafc2SMark Brown 	err = snd_pcm_hw_params_set_channels(handle, hw_params, channels);
306aba51cd0SJaroslav Kysela 	if (err < 0) {
307aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_channels %ld: %s", channels, snd_strerror(err));
308aba51cd0SJaroslav Kysela 		goto __close;
309aba51cd0SJaroslav Kysela 	}
310aba51cd0SJaroslav Kysela 	rrate = rate;
311aba51cd0SJaroslav Kysela 	err = snd_pcm_hw_params_set_rate_near(handle, hw_params, &rrate, 0);
312aba51cd0SJaroslav Kysela 	if (err < 0) {
313aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_rate %ld: %s", rate, snd_strerror(err));
314aba51cd0SJaroslav Kysela 		goto __close;
315aba51cd0SJaroslav Kysela 	}
316aba51cd0SJaroslav Kysela 	if (rrate != rate) {
317*c48cafc2SMark Brown 		snprintf(msg, sizeof(msg), "rate mismatch %ld != %ld", rate, rrate);
318aba51cd0SJaroslav Kysela 		goto __close;
319aba51cd0SJaroslav Kysela 	}
320aba51cd0SJaroslav Kysela 	rperiod_size = period_size;
321aba51cd0SJaroslav Kysela 	err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &rperiod_size, 0);
322aba51cd0SJaroslav Kysela 	if (err < 0) {
323aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_period_size %ld: %s", period_size, snd_strerror(err));
324aba51cd0SJaroslav Kysela 		goto __close;
325aba51cd0SJaroslav Kysela 	}
326aba51cd0SJaroslav Kysela 	rbuffer_size = buffer_size;
327aba51cd0SJaroslav Kysela 	err = snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &rbuffer_size);
328aba51cd0SJaroslav Kysela 	if (err < 0) {
329aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_buffer_size %ld: %s", buffer_size, snd_strerror(err));
330aba51cd0SJaroslav Kysela 		goto __close;
331aba51cd0SJaroslav Kysela 	}
332aba51cd0SJaroslav Kysela 	err = snd_pcm_hw_params(handle, hw_params);
333aba51cd0SJaroslav Kysela 	if (err < 0) {
334aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "snd_pcm_hw_params: %s", snd_strerror(err));
335aba51cd0SJaroslav Kysela 		goto __close;
336aba51cd0SJaroslav Kysela 	}
337aba51cd0SJaroslav Kysela 
338aba51cd0SJaroslav Kysela 	err = snd_pcm_sw_params_current(handle, sw_params);
339aba51cd0SJaroslav Kysela 	if (err < 0) {
340aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "snd_pcm_sw_params_current: %s", snd_strerror(err));
341aba51cd0SJaroslav Kysela 		goto __close;
342aba51cd0SJaroslav Kysela 	}
343aba51cd0SJaroslav Kysela 	if (data->stream == SND_PCM_STREAM_PLAYBACK) {
344aba51cd0SJaroslav Kysela 		start_threshold = (rbuffer_size / rperiod_size) * rperiod_size;
345aba51cd0SJaroslav Kysela 	} else {
346aba51cd0SJaroslav Kysela 		start_threshold = rperiod_size;
347aba51cd0SJaroslav Kysela 	}
348aba51cd0SJaroslav Kysela 	err = snd_pcm_sw_params_set_start_threshold(handle, sw_params, start_threshold);
349aba51cd0SJaroslav Kysela 	if (err < 0) {
350aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "snd_pcm_sw_params_set_start_threshold %ld: %s", (long)start_threshold, snd_strerror(err));
351aba51cd0SJaroslav Kysela 		goto __close;
352aba51cd0SJaroslav Kysela 	}
353aba51cd0SJaroslav Kysela 	err = snd_pcm_sw_params_set_avail_min(handle, sw_params, rperiod_size);
354aba51cd0SJaroslav Kysela 	if (err < 0) {
355aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "snd_pcm_sw_params_set_avail_min %ld: %s", (long)rperiod_size, snd_strerror(err));
356aba51cd0SJaroslav Kysela 		goto __close;
357aba51cd0SJaroslav Kysela 	}
358aba51cd0SJaroslav Kysela 	err = snd_pcm_sw_params(handle, sw_params);
359aba51cd0SJaroslav Kysela 	if (err < 0) {
360aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "snd_pcm_sw_params: %s", snd_strerror(err));
361aba51cd0SJaroslav Kysela 		goto __close;
362aba51cd0SJaroslav Kysela 	}
363aba51cd0SJaroslav Kysela 
364aba51cd0SJaroslav Kysela 	ksft_print_msg("%s.%d.%d.%d.%s hw_params.%s.%s.%ld.%ld.%ld.%ld sw_params.%ld\n",
365*c48cafc2SMark Brown 			 cfg_prefix,
366aba51cd0SJaroslav Kysela 			 data->card, data->device, data->subdevice,
367aba51cd0SJaroslav Kysela 			 snd_pcm_stream_name(data->stream),
368aba51cd0SJaroslav Kysela 			 snd_pcm_access_name(access),
369aba51cd0SJaroslav Kysela 			 snd_pcm_format_name(format),
370aba51cd0SJaroslav Kysela 			 (long)rate, (long)channels,
371aba51cd0SJaroslav Kysela 			 (long)rperiod_size, (long)rbuffer_size,
372aba51cd0SJaroslav Kysela 			 (long)start_threshold);
373aba51cd0SJaroslav Kysela 
374aba51cd0SJaroslav Kysela 	timestamp_now(&tstamp);
375aba51cd0SJaroslav Kysela 	for (i = 0; i < 4; i++) {
376aba51cd0SJaroslav Kysela 		if (data->stream == SND_PCM_STREAM_PLAYBACK) {
377aba51cd0SJaroslav Kysela 			frames = snd_pcm_writei(handle, samples, rate);
378aba51cd0SJaroslav Kysela 			if (frames < 0) {
379aba51cd0SJaroslav Kysela 				snprintf(msg, sizeof(msg),
380aba51cd0SJaroslav Kysela 					 "Write failed: expected %d, wrote %li", rate, frames);
381aba51cd0SJaroslav Kysela 				goto __close;
382aba51cd0SJaroslav Kysela 			}
383aba51cd0SJaroslav Kysela 			if (frames < rate) {
384aba51cd0SJaroslav Kysela 				snprintf(msg, sizeof(msg),
385aba51cd0SJaroslav Kysela 					 "expected %d, wrote %li", rate, frames);
386aba51cd0SJaroslav Kysela 				goto __close;
387aba51cd0SJaroslav Kysela 			}
388aba51cd0SJaroslav Kysela 		} else {
389aba51cd0SJaroslav Kysela 			frames = snd_pcm_readi(handle, samples, rate);
390aba51cd0SJaroslav Kysela 			if (frames < 0) {
391aba51cd0SJaroslav Kysela 				snprintf(msg, sizeof(msg),
392aba51cd0SJaroslav Kysela 					 "expected %d, wrote %li", rate, frames);
393aba51cd0SJaroslav Kysela 				goto __close;
394aba51cd0SJaroslav Kysela 			}
395aba51cd0SJaroslav Kysela 			if (frames < rate) {
396aba51cd0SJaroslav Kysela 				snprintf(msg, sizeof(msg),
397aba51cd0SJaroslav Kysela 					 "expected %d, wrote %li", rate, frames);
398aba51cd0SJaroslav Kysela 				goto __close;
399aba51cd0SJaroslav Kysela 			}
400aba51cd0SJaroslav Kysela 		}
401aba51cd0SJaroslav Kysela 	}
402aba51cd0SJaroslav Kysela 
403aba51cd0SJaroslav Kysela 	snd_pcm_drain(handle);
404aba51cd0SJaroslav Kysela 	ms = timestamp_diff_ms(&tstamp);
405aba51cd0SJaroslav Kysela 	if (ms < 3900 || ms > 4100) {
406aba51cd0SJaroslav Kysela 		snprintf(msg, sizeof(msg), "time mismatch: expected 4000ms got %lld", ms);
407aba51cd0SJaroslav Kysela 		goto __close;
408aba51cd0SJaroslav Kysela 	}
409aba51cd0SJaroslav Kysela 
410aba51cd0SJaroslav Kysela 	msg[0] = '\0';
411aba51cd0SJaroslav Kysela 	pass = true;
412aba51cd0SJaroslav Kysela __close:
413aba51cd0SJaroslav Kysela 	ksft_test_result(pass, "%s.%d.%d.%d.%s%s%s\n",
414*c48cafc2SMark Brown 			 cfg_prefix,
415aba51cd0SJaroslav Kysela 			 data->card, data->device, data->subdevice,
416aba51cd0SJaroslav Kysela 			 snd_pcm_stream_name(data->stream),
417aba51cd0SJaroslav Kysela 			 msg[0] ? " " : "", msg);
418aba51cd0SJaroslav Kysela 	free(samples);
419aba51cd0SJaroslav Kysela 	if (handle)
420aba51cd0SJaroslav Kysela 		snd_pcm_close(handle);
421aba51cd0SJaroslav Kysela }
422aba51cd0SJaroslav Kysela 
423*c48cafc2SMark Brown #define TESTS_PER_PCM 2
424aba51cd0SJaroslav Kysela 
425aba51cd0SJaroslav Kysela int main(void)
426aba51cd0SJaroslav Kysela {
427aba51cd0SJaroslav Kysela 	struct pcm_data *pcm;
428aba51cd0SJaroslav Kysela 
429aba51cd0SJaroslav Kysela 	ksft_print_header();
430aba51cd0SJaroslav Kysela 
431aba51cd0SJaroslav Kysela 	conf_load();
432aba51cd0SJaroslav Kysela 
433aba51cd0SJaroslav Kysela 	find_pcms();
434aba51cd0SJaroslav Kysela 
435*c48cafc2SMark Brown 	ksft_set_plan(num_missing + num_pcms * TESTS_PER_PCM);
436aba51cd0SJaroslav Kysela 
437aba51cd0SJaroslav Kysela 	for (pcm = pcm_missing; pcm != NULL; pcm = pcm->next) {
438aba51cd0SJaroslav Kysela 		ksft_test_result(false, "test.missing.%d.%d.%d.%s\n",
439aba51cd0SJaroslav Kysela 				 pcm->card, pcm->device, pcm->subdevice,
440aba51cd0SJaroslav Kysela 				 snd_pcm_stream_name(pcm->stream));
441aba51cd0SJaroslav Kysela 	}
442aba51cd0SJaroslav Kysela 
443aba51cd0SJaroslav Kysela 	for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) {
444*c48cafc2SMark Brown 		test_pcm_time1(pcm, "test.time1", "S16_LE", 48000, 2, 512, 4096);
445*c48cafc2SMark Brown 		test_pcm_time1(pcm, "test.time2", "S16_LE", 48000, 2, 24000, 192000);
446aba51cd0SJaroslav Kysela 	}
447aba51cd0SJaroslav Kysela 
448aba51cd0SJaroslav Kysela 	conf_free();
449aba51cd0SJaroslav Kysela 
450aba51cd0SJaroslav Kysela 	ksft_exit_pass();
451aba51cd0SJaroslav Kysela 
452aba51cd0SJaroslav Kysela 	return 0;
453aba51cd0SJaroslav Kysela }
454