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