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