1/*
2 * simple multi-thread stress test for PCM
3 *
4 * The main thread simply feeds or reads the sample data with the
5 * random size continuously.  Meanwhile, the worker threads call some
6 * update function depending on the given mode, and show the thread
7 * number of the read value.
8 *
9 * The function for the worker thread is specified via -m option.
10 * When the random mode ('r') is set, the update function is chosen
11 * randomly in the loop.
12 *
13 * When the -v option is passed, this tries to show some obtained value
14 * from the function.  Without -v, as default, it shows the thread number
15 * (0-9).  In addition, it puts the mode suffix ('a' for avail, 'd' for
16 * delay, etc) for the random mode, as well as the suffix '!' indicating
17 * the error from the called function.
18 */
19
20#include <stdio.h>
21#include <pthread.h>
22#include <getopt.h>
23#include "../include/asoundlib.h"
24
25#define MAX_THREADS	10
26
27enum {
28	MODE_AVAIL_UPDATE,
29	MODE_STATUS,
30	MODE_HWSYNC,
31	MODE_TIMESTAMP,
32	MODE_DELAY,
33	MODE_RANDOM
34};
35
36static char mode_suffix[] = {
37	'a', 's', 'h', 't', 'd', 'r'
38};
39
40static const char *pcmdev = "default";
41static int stream = SND_PCM_STREAM_PLAYBACK;
42static int num_threads = 1;
43static int periodsize = 16 * 1024;
44static int bufsize = 16 * 1024 * 4;
45static int channels = 2;
46static int rate = 48000;
47static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
48
49static int running_mode = MODE_AVAIL_UPDATE;
50static int show_value = 0;
51static int quiet = 0;
52
53static pthread_t peeper_threads[MAX_THREADS];
54static int running = 1;
55static snd_pcm_t *pcm;
56
57static void *peeper(void *data)
58{
59	int thread_no = (long)data;
60	snd_pcm_sframes_t val;
61	snd_pcm_status_t *stat;
62	snd_htimestamp_t tstamp;
63	int mode = running_mode, err;
64
65	snd_pcm_status_alloca(&stat);
66
67	while (running) {
68		if (running_mode == MODE_RANDOM)
69			mode = rand() % MODE_RANDOM;
70		switch (mode) {
71		case MODE_AVAIL_UPDATE:
72			val = snd_pcm_avail_update(pcm);
73			err = 0;
74			break;
75		case MODE_STATUS:
76			err = snd_pcm_status(pcm, stat);
77			val = snd_pcm_status_get_avail(stat);
78			break;
79		case MODE_HWSYNC:
80			err = snd_pcm_hwsync(pcm);
81			break;
82		case MODE_TIMESTAMP:
83			err = snd_pcm_htimestamp(pcm, (snd_pcm_uframes_t *)&val,
84						 &tstamp);
85			break;
86		default:
87			err = snd_pcm_delay(pcm, &val);
88			break;
89		}
90
91		if (quiet)
92			continue;
93		if (running_mode == MODE_RANDOM) {
94			fprintf(stderr, "%d%c%s", thread_no, mode_suffix[mode],
95				err ? "!" : "");
96		} else {
97			if (show_value && mode != MODE_HWSYNC)
98				fprintf(stderr, "\r%d     ", (int)val);
99			else
100				fprintf(stderr, "%d%s", thread_no,
101					err ? "!" : "");
102		}
103	}
104	return NULL;
105}
106
107static void usage(void)
108{
109	fprintf(stderr, "usage: multi-thread [-options]\n");
110	fprintf(stderr, "  -D str  Set device name\n");
111	fprintf(stderr, "  -r val  Set sample rate\n");
112	fprintf(stderr, "  -p val  Set period size (in frame)\n");
113	fprintf(stderr, "  -b val  Set buffer size (in frame)\n");
114	fprintf(stderr, "  -c val  Set number of channels\n");
115	fprintf(stderr, "  -f str  Set PCM format\n");
116	fprintf(stderr, "  -s str  Set stream direction (playback or capture)\n");
117	fprintf(stderr, "  -t val  Set number of threads\n");
118	fprintf(stderr, "  -m str  Running mode (avail, status, hwsync, timestamp, delay, random)\n");
119	fprintf(stderr, "  -v      Show value\n");
120	fprintf(stderr, "  -q      Quiet mode\n");
121}
122
123static int parse_options(int argc, char **argv)
124{
125	int c, i;
126
127	while ((c = getopt(argc, argv, "D:r:f:p:b:s:t:m:vq")) >= 0) {
128		switch (c) {
129		case 'D':
130			pcmdev = optarg;
131			break;
132		case 'r':
133			rate = atoi(optarg);
134			break;
135		case 'p':
136			periodsize = atoi(optarg);
137			break;
138		case 'b':
139			bufsize = atoi(optarg);
140			break;
141		case 'c':
142			channels = atoi(optarg);
143			break;
144		case 'f':
145			format = snd_pcm_format_value(optarg);
146			break;
147		case 's':
148			if (*optarg == 'p' || *optarg == 'P')
149				stream = SND_PCM_STREAM_PLAYBACK;
150			else if (*optarg == 'c' || *optarg == 'C')
151				stream = SND_PCM_STREAM_CAPTURE;
152			else {
153				fprintf(stderr, "invalid stream direction\n");
154				return 1;
155			}
156			break;
157		case 't':
158			num_threads = atoi(optarg);
159			if (num_threads < 1 || num_threads > MAX_THREADS) {
160				fprintf(stderr, "invalid number of threads\n");
161				return 1;
162			}
163			break;
164		case 'm':
165			for (i = 0; i <= MODE_RANDOM; i++)
166				if (mode_suffix[i] == *optarg)
167					break;
168			if (i > MODE_RANDOM) {
169				fprintf(stderr, "invalid mode type\n");
170				return 1;
171			}
172			running_mode = i;
173			break;
174		case 'v':
175			show_value = 1;
176			break;
177		case 'q':
178			quiet = 1;
179			break;
180		default:
181			usage();
182			return 1;
183		}
184	}
185	return 0;
186}
187
188static int setup_params(void)
189{
190	snd_pcm_hw_params_t *hw;
191
192	/* FIXME: more finer error checks */
193	snd_pcm_hw_params_alloca(&hw);
194	snd_pcm_hw_params_any(pcm, hw);
195	snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED);
196	snd_pcm_hw_params_set_format(pcm, hw, format);
197	snd_pcm_hw_params_set_channels(pcm, hw, channels);
198	snd_pcm_hw_params_set_rate(pcm, hw, rate, 0);
199	snd_pcm_hw_params_set_period_size(pcm, hw, periodsize, 0);
200	snd_pcm_hw_params_set_buffer_size(pcm, hw, bufsize);
201	if (snd_pcm_hw_params(pcm, hw) < 0) {
202		fprintf(stderr, "snd_pcm_hw_params error\n");
203		return 1;
204	}
205	return 0;
206}
207
208int main(int argc, char **argv)
209{
210	char *buf;
211	int i, err;
212
213	if (parse_options(argc, argv))
214		return 1;
215
216	err = snd_pcm_open(&pcm, pcmdev, stream, 0);
217	if (err < 0) {
218		fprintf(stderr, "cannot open pcm %s\n", pcmdev);
219		return 1;
220	}
221
222	if (setup_params())
223		return 1;
224
225	buf = calloc(1, snd_pcm_format_size(format, bufsize) * channels);
226	if (!buf) {
227		fprintf(stderr, "cannot alloc buffer\n");
228		return 1;
229	}
230
231	for (i = 0; i < num_threads; i++) {
232		if (pthread_create(&peeper_threads[i], NULL, peeper, (void *)(long)i)) {
233			fprintf(stderr, "pthread_create error\n");
234			return 1;
235		}
236	}
237
238	if (stream == SND_PCM_STREAM_CAPTURE)
239		snd_pcm_start(pcm);
240	for (;;) {
241		int size = rand() % (bufsize / 2);
242		if (stream == SND_PCM_STREAM_PLAYBACK)
243			err = snd_pcm_writei(pcm, buf, size);
244		else
245			err = snd_pcm_readi(pcm, buf, size);
246		if (err < 0) {
247			fprintf(stderr, "read/write error %d\n", err);
248			err = snd_pcm_recover(pcm, err, 0);
249			if (err < 0)
250				break;
251			if (stream == SND_PCM_STREAM_CAPTURE)
252				snd_pcm_start(pcm);
253		}
254	}
255
256	running = 0;
257	for (i = 0; i < num_threads; i++)
258		pthread_cancel(peeper_threads[i]);
259	for (i = 0; i < num_threads; i++)
260		pthread_join(peeper_threads[i], NULL);
261
262	return 1;
263}
264