| 
									
										
										
										
											2016-07-02 10:03:15 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * simple multi-thread stress test for PCM | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The main thread simply feeds or reads the sample data with the | 
					
						
							|  |  |  |  * random size continuously.  Meanwhile, the worker threads call some | 
					
						
							|  |  |  |  * update function depending on the given mode, and show the thread | 
					
						
							|  |  |  |  * number of the read value. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The function for the worker thread is specified via -m option. | 
					
						
							|  |  |  |  * When the random mode ('r') is set, the update function is chosen | 
					
						
							|  |  |  |  * randomly in the loop. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * When the -v option is passed, this tries to show some obtained value | 
					
						
							|  |  |  |  * from the function.  Without -v, as default, it shows the thread number | 
					
						
							|  |  |  |  * (0-9).  In addition, it puts the mode suffix ('a' for avail, 'd' for | 
					
						
							|  |  |  |  * delay, etc) for the random mode, as well as the suffix '!' indicating | 
					
						
							|  |  |  |  * the error from the called function. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <pthread.h>
 | 
					
						
							|  |  |  | #include <getopt.h>
 | 
					
						
							|  |  |  | #include "../include/asoundlib.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define MAX_THREADS	10
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum { | 
					
						
							|  |  |  | 	MODE_AVAIL_UPDATE, | 
					
						
							|  |  |  | 	MODE_STATUS, | 
					
						
							|  |  |  | 	MODE_HWSYNC, | 
					
						
							|  |  |  | 	MODE_TIMESTAMP, | 
					
						
							|  |  |  | 	MODE_DELAY, | 
					
						
							|  |  |  | 	MODE_RANDOM | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static char mode_suffix[] = { | 
					
						
							|  |  |  | 	'a', 's', 'h', 't', 'd', 'r' | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-02 16:00:45 +09:00
										 |  |  | static const char *pcmdev = "default"; | 
					
						
							| 
									
										
										
										
											2016-07-02 10:03:15 +02:00
										 |  |  | static int stream = SND_PCM_STREAM_PLAYBACK; | 
					
						
							|  |  |  | static int num_threads = 1; | 
					
						
							|  |  |  | static int periodsize = 16 * 1024; | 
					
						
							|  |  |  | static int bufsize = 16 * 1024 * 4; | 
					
						
							|  |  |  | static int channels = 2; | 
					
						
							|  |  |  | static int rate = 48000; | 
					
						
							|  |  |  | static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int running_mode = MODE_AVAIL_UPDATE; | 
					
						
							|  |  |  | static int show_value = 0; | 
					
						
							|  |  |  | static int quiet = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static pthread_t peeper_threads[MAX_THREADS]; | 
					
						
							|  |  |  | static int running = 1; | 
					
						
							|  |  |  | static snd_pcm_t *pcm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *peeper(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int thread_no = (long)data; | 
					
						
							|  |  |  | 	snd_pcm_sframes_t val; | 
					
						
							|  |  |  | 	snd_pcm_status_t *stat; | 
					
						
							|  |  |  | 	snd_htimestamp_t tstamp; | 
					
						
							|  |  |  | 	int mode = running_mode, err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	snd_pcm_status_alloca(&stat); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (running) { | 
					
						
							|  |  |  | 		if (running_mode == MODE_RANDOM) | 
					
						
							|  |  |  | 			mode = rand() % MODE_RANDOM; | 
					
						
							|  |  |  | 		switch (mode) { | 
					
						
							|  |  |  | 		case MODE_AVAIL_UPDATE: | 
					
						
							|  |  |  | 			val = snd_pcm_avail_update(pcm); | 
					
						
							|  |  |  | 			err = 0; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case MODE_STATUS: | 
					
						
							|  |  |  | 			err = snd_pcm_status(pcm, stat); | 
					
						
							|  |  |  | 			val = snd_pcm_status_get_avail(stat); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case MODE_HWSYNC: | 
					
						
							|  |  |  | 			err = snd_pcm_hwsync(pcm); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case MODE_TIMESTAMP: | 
					
						
							|  |  |  | 			err = snd_pcm_htimestamp(pcm, (snd_pcm_uframes_t *)&val, | 
					
						
							|  |  |  | 						 &tstamp); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			err = snd_pcm_delay(pcm, &val); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (quiet) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		if (running_mode == MODE_RANDOM) { | 
					
						
							|  |  |  | 			fprintf(stderr, "%d%c%s", thread_no, mode_suffix[mode], | 
					
						
							|  |  |  | 				err ? "!" : ""); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			if (show_value && mode != MODE_HWSYNC) | 
					
						
							|  |  |  | 				fprintf(stderr, "\r%d     ", (int)val); | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				fprintf(stderr, "%d%s", thread_no, | 
					
						
							|  |  |  | 					err ? "!" : ""); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void usage(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	fprintf(stderr, "usage: multi-thread [-options]\n"); | 
					
						
							|  |  |  | 	fprintf(stderr, "  -D str  Set device name\n"); | 
					
						
							|  |  |  | 	fprintf(stderr, "  -r val  Set sample rate\n"); | 
					
						
							|  |  |  | 	fprintf(stderr, "  -p val  Set period size (in frame)\n"); | 
					
						
							|  |  |  | 	fprintf(stderr, "  -b val  Set buffer size (in frame)\n"); | 
					
						
							|  |  |  | 	fprintf(stderr, "  -c val  Set number of channels\n"); | 
					
						
							|  |  |  | 	fprintf(stderr, "  -f str  Set PCM format\n"); | 
					
						
							|  |  |  | 	fprintf(stderr, "  -s str  Set stream direction (playback or capture)\n"); | 
					
						
							|  |  |  | 	fprintf(stderr, "  -t val  Set number of threads\n"); | 
					
						
							|  |  |  | 	fprintf(stderr, "  -m str  Running mode (avail, status, hwsync, timestamp, delay, random)\n"); | 
					
						
							|  |  |  | 	fprintf(stderr, "  -v      Show value\n"); | 
					
						
							|  |  |  | 	fprintf(stderr, "  -q      Quiet mode\n"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int parse_options(int argc, char **argv) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int c, i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while ((c = getopt(argc, argv, "D:r:f:p:b:s:t:m:vq")) >= 0) { | 
					
						
							|  |  |  | 		switch (c) { | 
					
						
							|  |  |  | 		case 'D': | 
					
						
							| 
									
										
										
										
											2023-02-02 16:00:45 +09:00
										 |  |  | 			pcmdev = optarg; | 
					
						
							| 
									
										
										
										
											2016-07-02 10:03:15 +02:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		case 'r': | 
					
						
							|  |  |  | 			rate = atoi(optarg); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 'p': | 
					
						
							|  |  |  | 			periodsize = atoi(optarg); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 'b': | 
					
						
							|  |  |  | 			bufsize = atoi(optarg); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 'c': | 
					
						
							|  |  |  | 			channels = atoi(optarg); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 'f': | 
					
						
							|  |  |  | 			format = snd_pcm_format_value(optarg); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 's': | 
					
						
							|  |  |  | 			if (*optarg == 'p' || *optarg == 'P') | 
					
						
							|  |  |  | 				stream = SND_PCM_STREAM_PLAYBACK; | 
					
						
							|  |  |  | 			else if (*optarg == 'c' || *optarg == 'C') | 
					
						
							|  |  |  | 				stream = SND_PCM_STREAM_CAPTURE; | 
					
						
							|  |  |  | 			else { | 
					
						
							|  |  |  | 				fprintf(stderr, "invalid stream direction\n"); | 
					
						
							|  |  |  | 				return 1; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 't': | 
					
						
							|  |  |  | 			num_threads = atoi(optarg); | 
					
						
							|  |  |  | 			if (num_threads < 1 || num_threads > MAX_THREADS) { | 
					
						
							|  |  |  | 				fprintf(stderr, "invalid number of threads\n"); | 
					
						
							|  |  |  | 				return 1; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 'm': | 
					
						
							|  |  |  | 			for (i = 0; i <= MODE_RANDOM; i++) | 
					
						
							|  |  |  | 				if (mode_suffix[i] == *optarg) | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 			if (i > MODE_RANDOM) { | 
					
						
							|  |  |  | 				fprintf(stderr, "invalid mode type\n"); | 
					
						
							|  |  |  | 				return 1; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			running_mode = i; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 'v': | 
					
						
							|  |  |  | 			show_value = 1; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 'q': | 
					
						
							|  |  |  | 			quiet = 1; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			usage(); | 
					
						
							|  |  |  | 			return 1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int setup_params(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_hw_params_t *hw; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* FIXME: more finer error checks */ | 
					
						
							|  |  |  | 	snd_pcm_hw_params_alloca(&hw); | 
					
						
							|  |  |  | 	snd_pcm_hw_params_any(pcm, hw); | 
					
						
							|  |  |  | 	snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED); | 
					
						
							|  |  |  | 	snd_pcm_hw_params_set_format(pcm, hw, format); | 
					
						
							|  |  |  | 	snd_pcm_hw_params_set_channels(pcm, hw, channels); | 
					
						
							|  |  |  | 	snd_pcm_hw_params_set_rate(pcm, hw, rate, 0); | 
					
						
							|  |  |  | 	snd_pcm_hw_params_set_period_size(pcm, hw, periodsize, 0); | 
					
						
							|  |  |  | 	snd_pcm_hw_params_set_buffer_size(pcm, hw, bufsize); | 
					
						
							|  |  |  | 	if (snd_pcm_hw_params(pcm, hw) < 0) { | 
					
						
							|  |  |  | 		fprintf(stderr, "snd_pcm_hw_params error\n"); | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int main(int argc, char **argv) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char *buf; | 
					
						
							|  |  |  | 	int i, err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (parse_options(argc, argv)) | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-02 16:00:45 +09:00
										 |  |  | 	err = snd_pcm_open(&pcm, pcmdev, stream, 0); | 
					
						
							| 
									
										
										
										
											2016-07-02 10:03:15 +02:00
										 |  |  | 	if (err < 0) { | 
					
						
							| 
									
										
										
										
											2023-02-02 16:00:45 +09:00
										 |  |  | 		fprintf(stderr, "cannot open pcm %s\n", pcmdev); | 
					
						
							| 
									
										
										
										
											2016-07-02 10:03:15 +02:00
										 |  |  | 		return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (setup_params()) | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	buf = calloc(1, snd_pcm_format_size(format, bufsize) * channels); | 
					
						
							|  |  |  | 	if (!buf) { | 
					
						
							|  |  |  | 		fprintf(stderr, "cannot alloc buffer\n"); | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < num_threads; i++) { | 
					
						
							|  |  |  | 		if (pthread_create(&peeper_threads[i], NULL, peeper, (void *)(long)i)) { | 
					
						
							|  |  |  | 			fprintf(stderr, "pthread_create error\n"); | 
					
						
							|  |  |  | 			return 1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (stream == SND_PCM_STREAM_CAPTURE) | 
					
						
							|  |  |  | 		snd_pcm_start(pcm); | 
					
						
							|  |  |  | 	for (;;) { | 
					
						
							|  |  |  | 		int size = rand() % (bufsize / 2); | 
					
						
							|  |  |  | 		if (stream == SND_PCM_STREAM_PLAYBACK) | 
					
						
							|  |  |  | 			err = snd_pcm_writei(pcm, buf, size); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			err = snd_pcm_readi(pcm, buf, size); | 
					
						
							|  |  |  | 		if (err < 0) { | 
					
						
							|  |  |  | 			fprintf(stderr, "read/write error %d\n", err); | 
					
						
							|  |  |  | 			err = snd_pcm_recover(pcm, err, 0); | 
					
						
							|  |  |  | 			if (err < 0) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			if (stream == SND_PCM_STREAM_CAPTURE) | 
					
						
							|  |  |  | 				snd_pcm_start(pcm); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	running = 0; | 
					
						
							|  |  |  | 	for (i = 0; i < num_threads; i++) | 
					
						
							|  |  |  | 		pthread_cancel(peeper_threads[i]); | 
					
						
							|  |  |  | 	for (i = 0; i < num_threads; i++) | 
					
						
							|  |  |  | 		pthread_join(peeper_threads[i], NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 1; | 
					
						
							|  |  |  | } |