xref: /onnv-gate/usr/src/cmd/audio/audiotest/audiotest.c (revision 12389:d0eed068d40c)
19484Sgarrett.damore@Sun.COM /*
29484Sgarrett.damore@Sun.COM  * CDDL HEADER START
39484Sgarrett.damore@Sun.COM  *
49484Sgarrett.damore@Sun.COM  * The contents of this file are subject to the terms of the
59484Sgarrett.damore@Sun.COM  * Common Development and Distribution License (the "License").
69484Sgarrett.damore@Sun.COM  * You may not use this file except in compliance with the License.
79484Sgarrett.damore@Sun.COM  *
89484Sgarrett.damore@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99484Sgarrett.damore@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109484Sgarrett.damore@Sun.COM  * See the License for the specific language governing permissions
119484Sgarrett.damore@Sun.COM  * and limitations under the License.
129484Sgarrett.damore@Sun.COM  *
139484Sgarrett.damore@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149484Sgarrett.damore@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159484Sgarrett.damore@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169484Sgarrett.damore@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179484Sgarrett.damore@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189484Sgarrett.damore@Sun.COM  *
199484Sgarrett.damore@Sun.COM  * CDDL HEADER END
209484Sgarrett.damore@Sun.COM  */
219484Sgarrett.damore@Sun.COM /*
229484Sgarrett.damore@Sun.COM  * Copyright (C) 4Front Technologies 1996-2008.
239484Sgarrett.damore@Sun.COM  *
24*12389SEdgar.Liu@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
259484Sgarrett.damore@Sun.COM  */
269484Sgarrett.damore@Sun.COM /*
279484Sgarrett.damore@Sun.COM  * This program is a general purpose test facility for audio output.
289484Sgarrett.damore@Sun.COM  * It does not test record.
299484Sgarrett.damore@Sun.COM  *
309484Sgarrett.damore@Sun.COM  * The wavedata.c and wavedata.h files contain the actual samples compressed
319484Sgarrett.damore@Sun.COM  * using the MS ADPCM algorithm.
329484Sgarrett.damore@Sun.COM  */
339484Sgarrett.damore@Sun.COM 
349484Sgarrett.damore@Sun.COM #include <stdio.h>
359484Sgarrett.damore@Sun.COM #include <stdlib.h>
369484Sgarrett.damore@Sun.COM #include <unistd.h>
379484Sgarrett.damore@Sun.COM #include <fcntl.h>
389484Sgarrett.damore@Sun.COM #include <string.h>
399484Sgarrett.damore@Sun.COM #include <errno.h>
409484Sgarrett.damore@Sun.COM #include <unistd.h>
419484Sgarrett.damore@Sun.COM #include <sys/time.h>
429484Sgarrett.damore@Sun.COM #include <sys/ioctl.h>
439484Sgarrett.damore@Sun.COM #include <sys/utsname.h>
449484Sgarrett.damore@Sun.COM #include <sys/soundcard.h>
459484Sgarrett.damore@Sun.COM #include <inttypes.h>
469484Sgarrett.damore@Sun.COM #include <locale.h>
479484Sgarrett.damore@Sun.COM 
489484Sgarrett.damore@Sun.COM #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
499484Sgarrett.damore@Sun.COM #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
509484Sgarrett.damore@Sun.COM #endif
519484Sgarrett.damore@Sun.COM 
529484Sgarrett.damore@Sun.COM #define	_(s)	gettext(s)
539484Sgarrett.damore@Sun.COM 
549484Sgarrett.damore@Sun.COM /*
559484Sgarrett.damore@Sun.COM  * Channel selectors
569484Sgarrett.damore@Sun.COM  */
579484Sgarrett.damore@Sun.COM #define	CH_LEFT		(1 << 0)
589484Sgarrett.damore@Sun.COM #define	CH_RIGHT	(1 << 1)
599484Sgarrett.damore@Sun.COM #define	CH_LREAR4	(1 << 2)	/* quadraphonic */
609484Sgarrett.damore@Sun.COM #define	CH_RREAR4	(1 << 3)	/* quadraphonic */
619484Sgarrett.damore@Sun.COM #define	CH_CENTER	(1 << 2)
629484Sgarrett.damore@Sun.COM #define	CH_LFE		(1 << 3)
639484Sgarrett.damore@Sun.COM #define	CH_LSURR	(1 << 4)
649484Sgarrett.damore@Sun.COM #define	CH_RSURR	(1 << 5)
659484Sgarrett.damore@Sun.COM #define	CH_LREAR	(1 << 6)
669484Sgarrett.damore@Sun.COM #define	CH_RREAR	(1 << 7)
679484Sgarrett.damore@Sun.COM #define	CH_STEREO	(CH_LEFT|CH_RIGHT)
689484Sgarrett.damore@Sun.COM #define	CH_4		(CH_STEREO | CH_LREAR4 | CH_RREAR4)
699484Sgarrett.damore@Sun.COM #define	CH_5		(CH_STEREO | CH_CENTER | CH_LSURR | CH_RSURR)
709484Sgarrett.damore@Sun.COM #define	CH_7		(CH_5 | CH_LREAR | CH_RREAR)
719484Sgarrett.damore@Sun.COM 
729484Sgarrett.damore@Sun.COM typedef struct chancfg {
739484Sgarrett.damore@Sun.COM 	int		mask;
749484Sgarrett.damore@Sun.COM 	const char	*name;
759484Sgarrett.damore@Sun.COM 	unsigned	flags;
769484Sgarrett.damore@Sun.COM 	int16_t		*data;
779484Sgarrett.damore@Sun.COM 	int		len;
789484Sgarrett.damore@Sun.COM } chancfg_t;
799484Sgarrett.damore@Sun.COM 
809484Sgarrett.damore@Sun.COM typedef struct testcfg {
819484Sgarrett.damore@Sun.COM 	int		nchan;
82*12389SEdgar.Liu@Sun.COM 	uint32_t	rate;
839484Sgarrett.damore@Sun.COM 	chancfg_t	*tests[16];
849484Sgarrett.damore@Sun.COM } testcfg_t;
859484Sgarrett.damore@Sun.COM 
869484Sgarrett.damore@Sun.COM #define	CFLAG_LFE	0x1	/* lfe channel - not full range */
879484Sgarrett.damore@Sun.COM 
889484Sgarrett.damore@Sun.COM /*
899484Sgarrett.damore@Sun.COM  * TRANSLATION_NOTE : The following strings are displayed during progress.
909484Sgarrett.damore@Sun.COM  * Its important for alignment that they have the same displayed length.
919484Sgarrett.damore@Sun.COM  */
929484Sgarrett.damore@Sun.COM #define	NM_LEFT		"\t<left> ................"
939484Sgarrett.damore@Sun.COM #define	NM_RIGHT	"\t<right> ..............."
949484Sgarrett.damore@Sun.COM #define	NM_LREAR	"\t<left rear> ..........."
959484Sgarrett.damore@Sun.COM #define	NM_RREAR	"\t<right rear> .........."
969484Sgarrett.damore@Sun.COM #define	NM_LSIDE	"\t<left side> ..........."
979484Sgarrett.damore@Sun.COM #define	NM_RSIDE	"\t<right side> .........."
989484Sgarrett.damore@Sun.COM #define	NM_CENTER	"\t<center> .............."
999484Sgarrett.damore@Sun.COM #define	NM_LFE		"\t<lfe> ................."
1009484Sgarrett.damore@Sun.COM #define	NM_STEREO	"\t<stereo> .............."
1019484Sgarrett.damore@Sun.COM #define	NM_40		"\t<4.0 surround> ........"
1029484Sgarrett.damore@Sun.COM #define	NM_50		"\t<5.0 surround> ........"
1039484Sgarrett.damore@Sun.COM #define	NM_70		"\t<7.0 surround> ........"
1049484Sgarrett.damore@Sun.COM 
1059484Sgarrett.damore@Sun.COM chancfg_t ch_left = { CH_LEFT, NM_LEFT, 0 };
1069484Sgarrett.damore@Sun.COM chancfg_t ch_right = { CH_RIGHT, NM_RIGHT, 0 };
1079484Sgarrett.damore@Sun.COM chancfg_t ch_stereo = { CH_STEREO, NM_STEREO, 0 };
1089484Sgarrett.damore@Sun.COM 
1099484Sgarrett.damore@Sun.COM chancfg_t ch_center = { CH_CENTER, NM_CENTER, 0 };
1109484Sgarrett.damore@Sun.COM chancfg_t ch_lfe = { CH_LFE, NM_LFE, CFLAG_LFE };
1119484Sgarrett.damore@Sun.COM 
1129484Sgarrett.damore@Sun.COM chancfg_t ch_lsurr_4 = { (1 << 2), NM_LREAR, 0 };
1139484Sgarrett.damore@Sun.COM chancfg_t ch_rsurr_4 = { (1 << 3), NM_RREAR, 0 };
1149484Sgarrett.damore@Sun.COM chancfg_t ch_4 = { CH_4, NM_40, 0 };
1159484Sgarrett.damore@Sun.COM 
1169484Sgarrett.damore@Sun.COM chancfg_t ch_lsurr_5 = { CH_LSURR, NM_LREAR, 0 };
1179484Sgarrett.damore@Sun.COM chancfg_t ch_rsurr_5 = { CH_RSURR, NM_RREAR, 0 };
1189484Sgarrett.damore@Sun.COM chancfg_t ch_5 = { CH_5, NM_50, 0 };
1199484Sgarrett.damore@Sun.COM 
1209484Sgarrett.damore@Sun.COM chancfg_t ch_lsurr_7 = { CH_LSURR, NM_LSIDE, 0 };
1219484Sgarrett.damore@Sun.COM chancfg_t ch_rsurr_7 = { CH_RSURR, NM_RSIDE, 0 };
1229484Sgarrett.damore@Sun.COM chancfg_t ch_lrear_7 = { CH_LREAR, NM_LREAR, 0 };
1239484Sgarrett.damore@Sun.COM chancfg_t ch_rrear_7 = { CH_RREAR, NM_RREAR, 0 };
1249484Sgarrett.damore@Sun.COM chancfg_t ch_7 = { CH_7, NM_70, 0 };
1259484Sgarrett.damore@Sun.COM 
1269484Sgarrett.damore@Sun.COM testcfg_t test_stereo = {
127*12389SEdgar.Liu@Sun.COM 	2, 48000, { &ch_left, &ch_right, &ch_stereo, NULL }
1289484Sgarrett.damore@Sun.COM };
1299484Sgarrett.damore@Sun.COM 
1309484Sgarrett.damore@Sun.COM testcfg_t test_quad = {
131*12389SEdgar.Liu@Sun.COM 	4, 48000, { &ch_left, &ch_right, &ch_stereo,
1329484Sgarrett.damore@Sun.COM 	&ch_lsurr_4, &ch_rsurr_4, &ch_4, NULL }
1339484Sgarrett.damore@Sun.COM };
1349484Sgarrett.damore@Sun.COM 
1359484Sgarrett.damore@Sun.COM testcfg_t test_51 = {
136*12389SEdgar.Liu@Sun.COM 	6, 48000, { &ch_left, &ch_right, &ch_stereo,
1379484Sgarrett.damore@Sun.COM 	&ch_lsurr_5, &ch_rsurr_5, &ch_center, &ch_lfe, &ch_5, NULL }
1389484Sgarrett.damore@Sun.COM };
1399484Sgarrett.damore@Sun.COM 
1409484Sgarrett.damore@Sun.COM testcfg_t test_71 = {
141*12389SEdgar.Liu@Sun.COM 	8, 48000, { &ch_left, &ch_right, &ch_stereo,
1429484Sgarrett.damore@Sun.COM 	&ch_lsurr_7, &ch_rsurr_7, &ch_lrear_7, &ch_rrear_7,
1439484Sgarrett.damore@Sun.COM 	&ch_center, &ch_lfe, &ch_7, NULL }
1449484Sgarrett.damore@Sun.COM };
1459484Sgarrett.damore@Sun.COM 
1469484Sgarrett.damore@Sun.COM /*
1479484Sgarrett.damore@Sun.COM  * uncompress_wave() is defined in wavedata.c. It expands the audio
1489484Sgarrett.damore@Sun.COM  * samples stored in wavedata.h and returns the lenghth of the
1499484Sgarrett.damore@Sun.COM  * uncompressed version in bytes.
1509484Sgarrett.damore@Sun.COM  *
1519484Sgarrett.damore@Sun.COM  * The uncompressed wave data format is 16 bit (native) stereo
1529484Sgarrett.damore@Sun.COM  * recorded at 48000 Hz.
1539484Sgarrett.damore@Sun.COM  */
1549484Sgarrett.damore@Sun.COM extern int uncompress_wave(short *outbuf);
1559484Sgarrett.damore@Sun.COM 
1569484Sgarrett.damore@Sun.COM static int data_len;
1579484Sgarrett.damore@Sun.COM 
1589484Sgarrett.damore@Sun.COM #define	MAXDEVICE   64
1599484Sgarrett.damore@Sun.COM extern void describe_error(int);
1609484Sgarrett.damore@Sun.COM 
1619484Sgarrett.damore@Sun.COM #define	SAMPLE_RATE 48000
1629484Sgarrett.damore@Sun.COM 
1639484Sgarrett.damore@Sun.COM /*
1649484Sgarrett.damore@Sun.COM  * Operating mode flags (set from the command line).
1659484Sgarrett.damore@Sun.COM  */
1669484Sgarrett.damore@Sun.COM #define	TF_LOOP		0x00000010	/* Loop until interrupted */
1679484Sgarrett.damore@Sun.COM 
1689484Sgarrett.damore@Sun.COM static int mixerfd;
1699484Sgarrett.damore@Sun.COM static int num_devices_tested = 0;
1709484Sgarrett.damore@Sun.COM 
1719484Sgarrett.damore@Sun.COM static short *sample_buf;
1729484Sgarrett.damore@Sun.COM 
1739484Sgarrett.damore@Sun.COM void
prepare(testcfg_t * tcfg)1749484Sgarrett.damore@Sun.COM prepare(testcfg_t *tcfg)
1759484Sgarrett.damore@Sun.COM {
1769484Sgarrett.damore@Sun.COM 	int	nsamples;
177*12389SEdgar.Liu@Sun.COM 	int	i, j;
1789484Sgarrett.damore@Sun.COM 	chancfg_t	*ccfg;
1799484Sgarrett.damore@Sun.COM 	if ((sample_buf = malloc(2000000)) == NULL) {
1809484Sgarrett.damore@Sun.COM 		perror("malloc");
1819484Sgarrett.damore@Sun.COM 		exit(-1);
1829484Sgarrett.damore@Sun.COM 	}
1839484Sgarrett.damore@Sun.COM 
1849484Sgarrett.damore@Sun.COM 	data_len = uncompress_wave(sample_buf);
1859484Sgarrett.damore@Sun.COM 	nsamples = (data_len / sizeof (int16_t)) / 2;
1869484Sgarrett.damore@Sun.COM 
1879484Sgarrett.damore@Sun.COM 	for (i = 0; (ccfg = tcfg->tests[i]) != NULL; i++) {
1889484Sgarrett.damore@Sun.COM 		int16_t		*src, *dst;
1899484Sgarrett.damore@Sun.COM 		int		ch;
1909484Sgarrett.damore@Sun.COM 		int		samp;
191*12389SEdgar.Liu@Sun.COM 		int		rate_multiple;
1929484Sgarrett.damore@Sun.COM 
1939484Sgarrett.damore@Sun.COM 		src = sample_buf;
194*12389SEdgar.Liu@Sun.COM 		rate_multiple = tcfg->rate / 48000;
1959484Sgarrett.damore@Sun.COM 
1969484Sgarrett.damore@Sun.COM 		if (ccfg->flags != CFLAG_LFE) {
197*12389SEdgar.Liu@Sun.COM 			ccfg->len = nsamples * tcfg->nchan *
198*12389SEdgar.Liu@Sun.COM 			    sizeof (int16_t) * rate_multiple;
1999484Sgarrett.damore@Sun.COM 			ccfg->data = malloc(ccfg->len);
2009484Sgarrett.damore@Sun.COM 			if ((dst = ccfg->data) == NULL) {
2019484Sgarrett.damore@Sun.COM 				perror("malloc");
2029484Sgarrett.damore@Sun.COM 				exit(-1);
2039484Sgarrett.damore@Sun.COM 			}
2049484Sgarrett.damore@Sun.COM 			for (samp = 0; samp < nsamples; samp++) {
2059484Sgarrett.damore@Sun.COM 				for (ch = 0; ch < tcfg->nchan; ch++) {
206*12389SEdgar.Liu@Sun.COM 					for (j = 0; j < rate_multiple; j++) {
207*12389SEdgar.Liu@Sun.COM 						*dst = ((1U << ch) & ccfg->mask)
208*12389SEdgar.Liu@Sun.COM 						    ? *src : 0;
209*12389SEdgar.Liu@Sun.COM 						dst++;
210*12389SEdgar.Liu@Sun.COM 					}
2119484Sgarrett.damore@Sun.COM 				}
2129484Sgarrett.damore@Sun.COM 				src += 2;
2139484Sgarrett.damore@Sun.COM 			}
2149484Sgarrett.damore@Sun.COM 		} else {
2159484Sgarrett.damore@Sun.COM 			/* Skip LFE for now */
2169484Sgarrett.damore@Sun.COM 			ccfg->len = 0;
2179484Sgarrett.damore@Sun.COM 		}
2189484Sgarrett.damore@Sun.COM 	}
2199484Sgarrett.damore@Sun.COM }
2209484Sgarrett.damore@Sun.COM 
2219484Sgarrett.damore@Sun.COM /*
2229484Sgarrett.damore@Sun.COM  * The testdsp() routine checks the capabilities of a given audio device number
2239484Sgarrett.damore@Sun.COM  * (parameter n) and decides if the test sound needs to be played.
2249484Sgarrett.damore@Sun.COM  */
2259484Sgarrett.damore@Sun.COM 
2269484Sgarrett.damore@Sun.COM /*ARGSUSED*/
2279484Sgarrett.damore@Sun.COM int
testdsp(int hd,int flags,testcfg_t * tcfg)2289484Sgarrett.damore@Sun.COM testdsp(int hd, int flags, testcfg_t *tcfg)
2299484Sgarrett.damore@Sun.COM {
2309484Sgarrett.damore@Sun.COM 	float ratio;
2319484Sgarrett.damore@Sun.COM 	struct timeval t1, t2;
2329484Sgarrett.damore@Sun.COM 	unsigned long t;
2339484Sgarrett.damore@Sun.COM 	int sample_rate;
2349484Sgarrett.damore@Sun.COM 	int delay;
2359484Sgarrett.damore@Sun.COM 	long long total_bytes = 0;
2369484Sgarrett.damore@Sun.COM 	unsigned int tmp, caps;
2379484Sgarrett.damore@Sun.COM 	int i;
2389484Sgarrett.damore@Sun.COM 	chancfg_t *ccfg;
2399484Sgarrett.damore@Sun.COM 
2409484Sgarrett.damore@Sun.COM 	caps = 0;
2419484Sgarrett.damore@Sun.COM 	if (ioctl(hd, SNDCTL_DSP_GETCAPS, &caps) == -1) {
2429484Sgarrett.damore@Sun.COM 		perror("SNDCTL_DSP_GETCAPS");
2439484Sgarrett.damore@Sun.COM 		return (-1);
2449484Sgarrett.damore@Sun.COM 	}
2459484Sgarrett.damore@Sun.COM 
2469484Sgarrett.damore@Sun.COM 	/*
2479484Sgarrett.damore@Sun.COM 	 * Setup the sample format. Since OSS will support AFMT_S16_NE
2489484Sgarrett.damore@Sun.COM 	 * regardless of the device we do not need to support any
2499484Sgarrett.damore@Sun.COM 	 * other formats.
2509484Sgarrett.damore@Sun.COM 	 */
2519484Sgarrett.damore@Sun.COM 	tmp = AFMT_S16_NE;
2529484Sgarrett.damore@Sun.COM 	if (ioctl(hd, SNDCTL_DSP_SETFMT, &tmp) == -1 || tmp != AFMT_S16_NE) {
2539484Sgarrett.damore@Sun.COM 		(void) printf(_("Device doesn't support native 16-bit PCM\n"));
2549484Sgarrett.damore@Sun.COM 		return (-1);
2559484Sgarrett.damore@Sun.COM 	}
2569484Sgarrett.damore@Sun.COM 
2579484Sgarrett.damore@Sun.COM 	/*
2589484Sgarrett.damore@Sun.COM 	 * Setup the device for channels. Once again we can simply
2599484Sgarrett.damore@Sun.COM 	 * assume that stereo will always work before OSS takes care
2609484Sgarrett.damore@Sun.COM 	 * of this by emulation if necessary.
2619484Sgarrett.damore@Sun.COM 	 */
2629484Sgarrett.damore@Sun.COM 	tmp = tcfg->nchan;
2639484Sgarrett.damore@Sun.COM 	if (ioctl(hd, SNDCTL_DSP_CHANNELS, &tmp) == -1 || tmp != tcfg->nchan) {
2649484Sgarrett.damore@Sun.COM 		(void) printf(_("The device doesn't support %d channels\n"),
2659484Sgarrett.damore@Sun.COM 		    tcfg->nchan);
2669484Sgarrett.damore@Sun.COM 		return (-2);
2679484Sgarrett.damore@Sun.COM 	}
2689484Sgarrett.damore@Sun.COM 
2699484Sgarrett.damore@Sun.COM 	/*
2709484Sgarrett.damore@Sun.COM 	 * Set up the sample rate.
2719484Sgarrett.damore@Sun.COM 	 */
272*12389SEdgar.Liu@Sun.COM 	tmp = tcfg->rate;
2739484Sgarrett.damore@Sun.COM 	if (ioctl(hd, SNDCTL_DSP_SPEED, &tmp) == -1) {
2749484Sgarrett.damore@Sun.COM 		perror("SNDCTL_DSP_SPEED");
2759484Sgarrett.damore@Sun.COM 		return (-3);
2769484Sgarrett.damore@Sun.COM 	}
2779484Sgarrett.damore@Sun.COM 
2789484Sgarrett.damore@Sun.COM 	sample_rate = tmp;
279*12389SEdgar.Liu@Sun.COM 	if (sample_rate != tcfg->rate) {
2809484Sgarrett.damore@Sun.COM 		(void) printf(_("The device doesn't support %d Hz\n"),
281*12389SEdgar.Liu@Sun.COM 		    tcfg->rate);
2829484Sgarrett.damore@Sun.COM 		return (-3);
2839484Sgarrett.damore@Sun.COM 	}
2849484Sgarrett.damore@Sun.COM 	(void) printf("\n");
2859484Sgarrett.damore@Sun.COM 
2869484Sgarrett.damore@Sun.COM 	/*
2879484Sgarrett.damore@Sun.COM 	 * This program will measure the real sampling rate by
2889484Sgarrett.damore@Sun.COM 	 * computing the total time required to play the sample.
2899484Sgarrett.damore@Sun.COM 	 *
2909484Sgarrett.damore@Sun.COM 	 * This is not terribly presice with short test sounds but it
2919484Sgarrett.damore@Sun.COM 	 * can be used to detect if the sampling rate badly
2929484Sgarrett.damore@Sun.COM 	 * wrong. Errors of few percents is more likely to be caused
2939484Sgarrett.damore@Sun.COM 	 * by poor accuracy of the system clock rather than problems
2949484Sgarrett.damore@Sun.COM 	 * with the sampling rate.
2959484Sgarrett.damore@Sun.COM 	 */
2969484Sgarrett.damore@Sun.COM 	(void) gettimeofday(&t1, NULL);
2979484Sgarrett.damore@Sun.COM 
2989484Sgarrett.damore@Sun.COM 	for (i = 0; (ccfg = tcfg->tests[i]) != NULL; i++) {
2999484Sgarrett.damore@Sun.COM 		(void) fputs(_(ccfg->name), stdout);
3009484Sgarrett.damore@Sun.COM 		(void) fflush(stdout);
3019484Sgarrett.damore@Sun.COM 		if (ccfg->flags & CFLAG_LFE) {
3029484Sgarrett.damore@Sun.COM 			(void) printf(_("SKIPPED\n"));
3039484Sgarrett.damore@Sun.COM 			continue;
3049484Sgarrett.damore@Sun.COM 		}
3059484Sgarrett.damore@Sun.COM 
3069484Sgarrett.damore@Sun.COM 		if (write(hd, ccfg->data, ccfg->len) < 0) {
3079484Sgarrett.damore@Sun.COM 			(void) printf(_("ERROR: %s\n"),
3089484Sgarrett.damore@Sun.COM 			    strerror(errno));
3099484Sgarrett.damore@Sun.COM 			return (-3);
3109484Sgarrett.damore@Sun.COM 		}
3119484Sgarrett.damore@Sun.COM 		(void) printf(_("OK\n"));
3129484Sgarrett.damore@Sun.COM 		total_bytes += ccfg->len;
3139484Sgarrett.damore@Sun.COM 	}
3149484Sgarrett.damore@Sun.COM 
3159484Sgarrett.damore@Sun.COM 	(void) gettimeofday(&t2, NULL);
3169484Sgarrett.damore@Sun.COM 	delay = 0;
3179484Sgarrett.damore@Sun.COM 	(void) ioctl(hd, SNDCTL_DSP_GETODELAY, &delay);	/* Ignore errors */
3189484Sgarrett.damore@Sun.COM 
3199484Sgarrett.damore@Sun.COM 	/*
3209484Sgarrett.damore@Sun.COM 	 * Perform the time computations using milliseconds.
3219484Sgarrett.damore@Sun.COM 	 */
3229484Sgarrett.damore@Sun.COM 
3239484Sgarrett.damore@Sun.COM 	t = t2.tv_sec - t1.tv_sec;
3249484Sgarrett.damore@Sun.COM 	t *= 1000;
3259484Sgarrett.damore@Sun.COM 
3269484Sgarrett.damore@Sun.COM 	t += t2.tv_usec / 1000;
3279484Sgarrett.damore@Sun.COM 	t -= t1.tv_usec / 1000;
3289484Sgarrett.damore@Sun.COM 
3299484Sgarrett.damore@Sun.COM 	total_bytes -= delay;
3309484Sgarrett.damore@Sun.COM 	total_bytes *= 1000;
3319484Sgarrett.damore@Sun.COM 
3329484Sgarrett.damore@Sun.COM 	total_bytes /= t;
3339484Sgarrett.damore@Sun.COM 	total_bytes /= (tcfg->nchan * sizeof (int16_t));
3349484Sgarrett.damore@Sun.COM 
3359484Sgarrett.damore@Sun.COM 	ratio = ((float)total_bytes / (float)sample_rate) * 100.0;
3369484Sgarrett.damore@Sun.COM 	(void) printf(_("\t<measured sample rate %8.2f Hz (%4.2f%%)>\n"),
3379484Sgarrett.damore@Sun.COM 	    (float)sample_rate * ratio / 100.0, ratio - 100.0);
3389484Sgarrett.damore@Sun.COM 	num_devices_tested++;
3399484Sgarrett.damore@Sun.COM 
3409484Sgarrett.damore@Sun.COM 	return (1);
3419484Sgarrett.damore@Sun.COM }
3429484Sgarrett.damore@Sun.COM 
3439484Sgarrett.damore@Sun.COM static int
find_num_devices(void)3449484Sgarrett.damore@Sun.COM find_num_devices(void)
3459484Sgarrett.damore@Sun.COM {
3469484Sgarrett.damore@Sun.COM 	oss_sysinfo info;
3479484Sgarrett.damore@Sun.COM 	struct utsname un;
3489484Sgarrett.damore@Sun.COM 	/*
3499484Sgarrett.damore@Sun.COM 	 * Find out the number of available audio devices by calling
3509484Sgarrett.damore@Sun.COM 	 * SNDCTL_SYSINFO.
3519484Sgarrett.damore@Sun.COM 	 */
3529484Sgarrett.damore@Sun.COM 
3539484Sgarrett.damore@Sun.COM 	if (ioctl(mixerfd, SNDCTL_SYSINFO, &info) == -1) {
3549484Sgarrett.damore@Sun.COM 		if (errno == ENXIO) {
3559484Sgarrett.damore@Sun.COM 			(void) fprintf(stderr,
3569484Sgarrett.damore@Sun.COM 			    _("No supported sound hardware detected.\n"));
3579484Sgarrett.damore@Sun.COM 			exit(-1);
3589484Sgarrett.damore@Sun.COM 		} else {
3599484Sgarrett.damore@Sun.COM 			perror("SNDCTL_SYSINFO");
3609484Sgarrett.damore@Sun.COM 			(void) printf(_("Cannot get system information.\n"));
3619484Sgarrett.damore@Sun.COM 			exit(-1);
3629484Sgarrett.damore@Sun.COM 		}
3639484Sgarrett.damore@Sun.COM 	}
3649484Sgarrett.damore@Sun.COM 	(void) printf(_("Sound subsystem and version: %s %s (0x%08X)\n"),
3659484Sgarrett.damore@Sun.COM 	    info.product, info.version, info.versionnum);
3669484Sgarrett.damore@Sun.COM 
3679484Sgarrett.damore@Sun.COM 	if (uname(&un) != -1)
3689484Sgarrett.damore@Sun.COM 		(void) printf(_("Platform: %s %s %s %s\n"),
3699484Sgarrett.damore@Sun.COM 		    un.sysname, un.release, un.version, un.machine);
3709484Sgarrett.damore@Sun.COM 
3719484Sgarrett.damore@Sun.COM 	return (info.numaudios);
3729484Sgarrett.damore@Sun.COM }
3739484Sgarrett.damore@Sun.COM 
3749484Sgarrett.damore@Sun.COM /*
3759484Sgarrett.damore@Sun.COM  * The test_device() routine checks certain information about the device
3769484Sgarrett.damore@Sun.COM  * and calls testdsp() to play the test sound.
3779484Sgarrett.damore@Sun.COM  */
3789484Sgarrett.damore@Sun.COM 
3799484Sgarrett.damore@Sun.COM int
test_device(char * dn,int flags,testcfg_t * tcfg)3809484Sgarrett.damore@Sun.COM test_device(char *dn, int flags, testcfg_t *tcfg)
3819484Sgarrett.damore@Sun.COM {
3829484Sgarrett.damore@Sun.COM 	oss_audioinfo ainfo;
3839484Sgarrett.damore@Sun.COM 	int code;
3849484Sgarrett.damore@Sun.COM 	int fd;
3859484Sgarrett.damore@Sun.COM 
3869484Sgarrett.damore@Sun.COM 	fd = open(dn, O_WRONLY, 0);
3879484Sgarrett.damore@Sun.COM 	if (fd == -1) {
3889484Sgarrett.damore@Sun.COM 		int err = errno;
3899484Sgarrett.damore@Sun.COM 		perror(dn);
3909484Sgarrett.damore@Sun.COM 		errno = err;
3919484Sgarrett.damore@Sun.COM 		describe_error(errno);
39211936Sgdamore@opensolaris.org 		return (-1);
3939484Sgarrett.damore@Sun.COM 	}
3949484Sgarrett.damore@Sun.COM 
3959484Sgarrett.damore@Sun.COM 	ainfo.dev = -1;
3969484Sgarrett.damore@Sun.COM 	if (ioctl(fd, SNDCTL_AUDIOINFO, &ainfo) == -1) {
3979484Sgarrett.damore@Sun.COM 		perror("SNDCTL_AUDIOINFO");
3989484Sgarrett.damore@Sun.COM 		(void) close(fd);
39911936Sgdamore@opensolaris.org 		return (-1);
4009484Sgarrett.damore@Sun.COM 	}
4019484Sgarrett.damore@Sun.COM 
4029484Sgarrett.damore@Sun.COM 	(void) printf(_("\n*** Scanning sound adapter #%d ***\n"),
4039484Sgarrett.damore@Sun.COM 	    ainfo.card_number);
4049484Sgarrett.damore@Sun.COM 
4059484Sgarrett.damore@Sun.COM 	(void) printf(_("%s (audio engine %d): %s\n"), ainfo.devnode, ainfo.dev,
4069484Sgarrett.damore@Sun.COM 	    ainfo.name);
4079484Sgarrett.damore@Sun.COM 
4089484Sgarrett.damore@Sun.COM 	if (!ainfo.enabled) {
4099484Sgarrett.damore@Sun.COM 		(void) printf(_("  - Device not present - Skipping\n"));
4109484Sgarrett.damore@Sun.COM 		(void) close(fd);
41111936Sgdamore@opensolaris.org 		return (0);
4129484Sgarrett.damore@Sun.COM 	}
4139484Sgarrett.damore@Sun.COM 
4149484Sgarrett.damore@Sun.COM 	if (!(ainfo.caps & PCM_CAP_OUTPUT)) {
4159484Sgarrett.damore@Sun.COM 		(void) printf(_("  - Skipping input only device\n"));
4169484Sgarrett.damore@Sun.COM 		(void) close(fd);
41711936Sgdamore@opensolaris.org 		return (0);
4189484Sgarrett.damore@Sun.COM 	}
4199484Sgarrett.damore@Sun.COM 
4209484Sgarrett.damore@Sun.COM 	(void) printf(_("  - Performing audio playback test... "));
4219484Sgarrett.damore@Sun.COM 	(void) fflush(stdout);
4229484Sgarrett.damore@Sun.COM 
4239484Sgarrett.damore@Sun.COM 	code = testdsp(fd, flags, tcfg);
4249484Sgarrett.damore@Sun.COM 	(void) close(fd);
42511936Sgdamore@opensolaris.org 	if (code < 0) {
42611936Sgdamore@opensolaris.org 		return (code);
42711936Sgdamore@opensolaris.org 	}
4289484Sgarrett.damore@Sun.COM 
4299484Sgarrett.damore@Sun.COM 	return (code == 1);
4309484Sgarrett.damore@Sun.COM }
4319484Sgarrett.damore@Sun.COM 
4329484Sgarrett.damore@Sun.COM void
describe_error(int err)4339484Sgarrett.damore@Sun.COM describe_error(int err)
4349484Sgarrett.damore@Sun.COM {
4359484Sgarrett.damore@Sun.COM 	switch (err) {
4369484Sgarrett.damore@Sun.COM 	case ENODEV:
4379484Sgarrett.damore@Sun.COM 		(void) fprintf(stderr,
4389484Sgarrett.damore@Sun.COM 		    _("The device file was found in /dev but\n"
4399484Sgarrett.damore@Sun.COM 		    "the driver was not loaded.\n"));
4409484Sgarrett.damore@Sun.COM 		break;
4419484Sgarrett.damore@Sun.COM 
4429484Sgarrett.damore@Sun.COM 	case ENXIO:
4439484Sgarrett.damore@Sun.COM 		(void) fprintf(stderr,
4449484Sgarrett.damore@Sun.COM 		    _("There are no sound devices available.\n"
4459484Sgarrett.damore@Sun.COM 		    "The most likely reason is that the device you have\n"
4469484Sgarrett.damore@Sun.COM 		    "is malfunctioning or it's not supported.\n"
4479484Sgarrett.damore@Sun.COM 		    "It's also possible that you are trying to use the wrong "
4489484Sgarrett.damore@Sun.COM 		    "device file.\n"));
4499484Sgarrett.damore@Sun.COM 		break;
4509484Sgarrett.damore@Sun.COM 
4519484Sgarrett.damore@Sun.COM 	case ENOSPC:
4529484Sgarrett.damore@Sun.COM 		(void) fprintf(stderr,
4539484Sgarrett.damore@Sun.COM 		    _("Your system cannot allocate memory for the device\n"
4549484Sgarrett.damore@Sun.COM 		    "buffers. Reboot your machine and try again.\n"));
4559484Sgarrett.damore@Sun.COM 		break;
4569484Sgarrett.damore@Sun.COM 
4579484Sgarrett.damore@Sun.COM 	case ENOENT:
4589484Sgarrett.damore@Sun.COM 		(void) fprintf(stderr,
4599484Sgarrett.damore@Sun.COM 		    _("The device file is missing from /dev.\n"));
4609484Sgarrett.damore@Sun.COM 		break;
4619484Sgarrett.damore@Sun.COM 
4629484Sgarrett.damore@Sun.COM 
4639484Sgarrett.damore@Sun.COM 	case EBUSY:
4649484Sgarrett.damore@Sun.COM 		(void) fprintf(stderr,
4659484Sgarrett.damore@Sun.COM 		    _("The device is busy. There is some other application\n"
4669484Sgarrett.damore@Sun.COM 		    "using it.\n"));
4679484Sgarrett.damore@Sun.COM 		break;
4689484Sgarrett.damore@Sun.COM 
4699484Sgarrett.damore@Sun.COM 	default:
4709484Sgarrett.damore@Sun.COM 		break;
4719484Sgarrett.damore@Sun.COM 	}
4729484Sgarrett.damore@Sun.COM }
4739484Sgarrett.damore@Sun.COM 
4749484Sgarrett.damore@Sun.COM int
main(int argc,char * argv[])4759484Sgarrett.damore@Sun.COM main(int argc, char *argv[])
4769484Sgarrett.damore@Sun.COM {
4779484Sgarrett.damore@Sun.COM 	int t, i;
4789484Sgarrett.damore@Sun.COM 	int maxdev;
4799484Sgarrett.damore@Sun.COM 	int flags = 0;
4809484Sgarrett.damore@Sun.COM 	int status = 0;
48111936Sgdamore@opensolaris.org 	int errors = 0;
4829484Sgarrett.damore@Sun.COM 	int numdev;
4839484Sgarrett.damore@Sun.COM 	extern int optind;
4849484Sgarrett.damore@Sun.COM 	testcfg_t	*tcfg;
4859484Sgarrett.damore@Sun.COM 
4869484Sgarrett.damore@Sun.COM 	(void) setlocale(LC_ALL, "");
4879484Sgarrett.damore@Sun.COM 	(void) textdomain(TEXT_DOMAIN);
4889484Sgarrett.damore@Sun.COM 
4899484Sgarrett.damore@Sun.COM 	tcfg = &test_stereo;
4909484Sgarrett.damore@Sun.COM 
4919484Sgarrett.damore@Sun.COM 	/*
4929484Sgarrett.damore@Sun.COM 	 * Simple command line switch handling.
4939484Sgarrett.damore@Sun.COM 	 */
4949484Sgarrett.damore@Sun.COM 
495*12389SEdgar.Liu@Sun.COM 	while ((i = getopt(argc, argv, "l2457r:")) != EOF) {
4969484Sgarrett.damore@Sun.COM 		switch (i) {
4979484Sgarrett.damore@Sun.COM 		case 'l':
4989484Sgarrett.damore@Sun.COM 			flags |= TF_LOOP;
4999484Sgarrett.damore@Sun.COM 			break;
5009484Sgarrett.damore@Sun.COM 		case '2':
5019484Sgarrett.damore@Sun.COM 			tcfg = &test_stereo;
5029484Sgarrett.damore@Sun.COM 			break;
5039484Sgarrett.damore@Sun.COM 		case '4':
5049484Sgarrett.damore@Sun.COM 			tcfg = &test_quad;
5059484Sgarrett.damore@Sun.COM 			break;
5069484Sgarrett.damore@Sun.COM 		case '5':
5079484Sgarrett.damore@Sun.COM 			tcfg = &test_51;
5089484Sgarrett.damore@Sun.COM 			break;
5099484Sgarrett.damore@Sun.COM 		case '7':
5109484Sgarrett.damore@Sun.COM 			tcfg = &test_71;
5119484Sgarrett.damore@Sun.COM 			break;
512*12389SEdgar.Liu@Sun.COM 		case 'r':
513*12389SEdgar.Liu@Sun.COM 			tcfg->rate = atoi(optarg);
514*12389SEdgar.Liu@Sun.COM 			break;
5159484Sgarrett.damore@Sun.COM 		default:
5169484Sgarrett.damore@Sun.COM 			(void) printf(_("Usage: %s [options...] [device]\n"
5179484Sgarrett.damore@Sun.COM 			    "	-2	Stereo test\n"
5189484Sgarrett.damore@Sun.COM 			    "	-4	Quadraphonic 4.0 test\n"
5199484Sgarrett.damore@Sun.COM 			    "	-5	Surround 5.1 test\n"
5209484Sgarrett.damore@Sun.COM 			    "	-7	Surround 7.1 test\n"
521*12389SEdgar.Liu@Sun.COM 			    "	-r	Sample Rate (48000|96000|192000)\n"
5229484Sgarrett.damore@Sun.COM 			    "	-l	Loop test\n"), argv[0]);
5239484Sgarrett.damore@Sun.COM 			exit(-1);
5249484Sgarrett.damore@Sun.COM 		}
5259484Sgarrett.damore@Sun.COM 	}
5269484Sgarrett.damore@Sun.COM 
5279484Sgarrett.damore@Sun.COM 	/*
5289484Sgarrett.damore@Sun.COM 	 * Open the mixer device used for calling SNDCTL_SYSINFO and
5299484Sgarrett.damore@Sun.COM 	 * SNDCTL_AUDIOINFO.
5309484Sgarrett.damore@Sun.COM 	 */
5319484Sgarrett.damore@Sun.COM 	if ((mixerfd = open("/dev/mixer", O_RDWR, 0)) == -1) {
5329484Sgarrett.damore@Sun.COM 		int err = errno;
5339484Sgarrett.damore@Sun.COM 		perror("/dev/mixer");
5349484Sgarrett.damore@Sun.COM 		errno = err;
5359484Sgarrett.damore@Sun.COM 		describe_error(errno);
5369484Sgarrett.damore@Sun.COM 		exit(-1);
5379484Sgarrett.damore@Sun.COM 	}
5389484Sgarrett.damore@Sun.COM 
5399484Sgarrett.damore@Sun.COM 	prepare(tcfg);			/* Prepare the wave data */
5409484Sgarrett.damore@Sun.COM 
5419484Sgarrett.damore@Sun.COM 	/*
5429484Sgarrett.damore@Sun.COM 	 * Enumerate all devices and play the test sounds.
5439484Sgarrett.damore@Sun.COM 	 */
5449484Sgarrett.damore@Sun.COM 	maxdev = find_num_devices();
5459484Sgarrett.damore@Sun.COM 	if (maxdev < 1) {
5469484Sgarrett.damore@Sun.COM 		(void) printf(_("\n*** No audio hardware available ***\n"));
5479484Sgarrett.damore@Sun.COM 		exit(-1);
5489484Sgarrett.damore@Sun.COM 	}
5499484Sgarrett.damore@Sun.COM 
5509484Sgarrett.damore@Sun.COM 	numdev = (argc - optind);
5519484Sgarrett.damore@Sun.COM 	do {
5529484Sgarrett.damore@Sun.COM 		char *dn;
5539484Sgarrett.damore@Sun.COM 		oss_audioinfo	ainfo;
55411936Sgdamore@opensolaris.org 		int rv;
5559484Sgarrett.damore@Sun.COM 
55611936Sgdamore@opensolaris.org 		status = 0;
5579484Sgarrett.damore@Sun.COM 		if (numdev > 0) {
5589484Sgarrett.damore@Sun.COM 			for (t = 0; t < numdev; t++) {
5599484Sgarrett.damore@Sun.COM 				dn = argv[optind + t];
56011936Sgdamore@opensolaris.org 				rv = test_device(dn, flags, tcfg);
56111936Sgdamore@opensolaris.org 				if (rv < 0) {
56211936Sgdamore@opensolaris.org 					errors++;
56311936Sgdamore@opensolaris.org 				} else if (rv) {
5649484Sgarrett.damore@Sun.COM 					status++;
56511936Sgdamore@opensolaris.org 				}
5669484Sgarrett.damore@Sun.COM 			}
5679484Sgarrett.damore@Sun.COM 		} else {
5689484Sgarrett.damore@Sun.COM 			for (t = 0; t < maxdev; t++) {
5699484Sgarrett.damore@Sun.COM 				ainfo.dev = t;
5709484Sgarrett.damore@Sun.COM 				if (ioctl(mixerfd, SNDCTL_AUDIOINFO,
5719484Sgarrett.damore@Sun.COM 				    &ainfo) == -1) {
5729484Sgarrett.damore@Sun.COM 					perror("SNDCTL_AUDIOINFO");
5739484Sgarrett.damore@Sun.COM 					status++;
5749484Sgarrett.damore@Sun.COM 					continue;
5759484Sgarrett.damore@Sun.COM 				}
5769484Sgarrett.damore@Sun.COM 				dn = ainfo.devnode;
57711936Sgdamore@opensolaris.org 				rv = test_device(dn, flags, tcfg);
57811936Sgdamore@opensolaris.org 				if (rv < 0) {
57911936Sgdamore@opensolaris.org 					errors++;
58011936Sgdamore@opensolaris.org 				} else if (rv) {
5819484Sgarrett.damore@Sun.COM 					status++;
58211936Sgdamore@opensolaris.org 				}
5839484Sgarrett.damore@Sun.COM 			}
5849484Sgarrett.damore@Sun.COM 		}
5859484Sgarrett.damore@Sun.COM 
58611936Sgdamore@opensolaris.org 		if (errors == 0)
5879484Sgarrett.damore@Sun.COM 			(void) printf(_("\n*** All tests completed OK ***\n"));
5889484Sgarrett.damore@Sun.COM 		else
5899484Sgarrett.damore@Sun.COM 			(void) printf(_("\n*** Errors were detected ***\n"));
5909484Sgarrett.damore@Sun.COM 
59111936Sgdamore@opensolaris.org 	} while (status && (flags & TF_LOOP));
5929484Sgarrett.damore@Sun.COM 
5939484Sgarrett.damore@Sun.COM 	(void) close(mixerfd);
5949484Sgarrett.damore@Sun.COM 
5959484Sgarrett.damore@Sun.COM 	return (status);
5969484Sgarrett.damore@Sun.COM }
597