xref: /titanic_44/usr/src/cmd/audio/audiotest/audiotest.c (revision 68c47f65208790c466e5e484f2293d3baed71c6a)
188447a05SGarrett D'Amore /*
288447a05SGarrett D'Amore  * CDDL HEADER START
388447a05SGarrett D'Amore  *
488447a05SGarrett D'Amore  * The contents of this file are subject to the terms of the
588447a05SGarrett D'Amore  * Common Development and Distribution License (the "License").
688447a05SGarrett D'Amore  * You may not use this file except in compliance with the License.
788447a05SGarrett D'Amore  *
888447a05SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988447a05SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
1088447a05SGarrett D'Amore  * See the License for the specific language governing permissions
1188447a05SGarrett D'Amore  * and limitations under the License.
1288447a05SGarrett D'Amore  *
1388447a05SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
1488447a05SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588447a05SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
1688447a05SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
1788447a05SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
1888447a05SGarrett D'Amore  *
1988447a05SGarrett D'Amore  * CDDL HEADER END
2088447a05SGarrett D'Amore  */
2188447a05SGarrett D'Amore /*
2288447a05SGarrett D'Amore  * Copyright (C) 4Front Technologies 1996-2008.
2388447a05SGarrett D'Amore  *
24*68c47f65SGarrett D'Amore  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2588447a05SGarrett D'Amore  * Use is subject to license terms.
2688447a05SGarrett D'Amore  */
2788447a05SGarrett D'Amore /*
2888447a05SGarrett D'Amore  * This program is a general purpose test facility for audio output.
2988447a05SGarrett D'Amore  * It does not test record.
3088447a05SGarrett D'Amore  *
3188447a05SGarrett D'Amore  * The wavedata.c and wavedata.h files contain the actual samples compressed
3288447a05SGarrett D'Amore  * using the MS ADPCM algorithm.
3388447a05SGarrett D'Amore  */
3488447a05SGarrett D'Amore 
3588447a05SGarrett D'Amore #include <stdio.h>
3688447a05SGarrett D'Amore #include <stdlib.h>
3788447a05SGarrett D'Amore #include <unistd.h>
3888447a05SGarrett D'Amore #include <fcntl.h>
3988447a05SGarrett D'Amore #include <string.h>
4088447a05SGarrett D'Amore #include <errno.h>
4188447a05SGarrett D'Amore #include <unistd.h>
4288447a05SGarrett D'Amore #include <sys/time.h>
4388447a05SGarrett D'Amore #include <sys/ioctl.h>
4488447a05SGarrett D'Amore #include <sys/utsname.h>
4588447a05SGarrett D'Amore #include <sys/soundcard.h>
4688447a05SGarrett D'Amore #include <inttypes.h>
4788447a05SGarrett D'Amore #include <locale.h>
4888447a05SGarrett D'Amore 
4988447a05SGarrett D'Amore #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
5088447a05SGarrett D'Amore #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
5188447a05SGarrett D'Amore #endif
5288447a05SGarrett D'Amore 
5388447a05SGarrett D'Amore #define	_(s)	gettext(s)
5488447a05SGarrett D'Amore 
5588447a05SGarrett D'Amore /*
5688447a05SGarrett D'Amore  * Channel selectors
5788447a05SGarrett D'Amore  */
5888447a05SGarrett D'Amore #define	CH_LEFT		(1 << 0)
5988447a05SGarrett D'Amore #define	CH_RIGHT	(1 << 1)
6088447a05SGarrett D'Amore #define	CH_LREAR4	(1 << 2)	/* quadraphonic */
6188447a05SGarrett D'Amore #define	CH_RREAR4	(1 << 3)	/* quadraphonic */
6288447a05SGarrett D'Amore #define	CH_CENTER	(1 << 2)
6388447a05SGarrett D'Amore #define	CH_LFE		(1 << 3)
6488447a05SGarrett D'Amore #define	CH_LSURR	(1 << 4)
6588447a05SGarrett D'Amore #define	CH_RSURR	(1 << 5)
6688447a05SGarrett D'Amore #define	CH_LREAR	(1 << 6)
6788447a05SGarrett D'Amore #define	CH_RREAR	(1 << 7)
6888447a05SGarrett D'Amore #define	CH_STEREO	(CH_LEFT|CH_RIGHT)
6988447a05SGarrett D'Amore #define	CH_4		(CH_STEREO | CH_LREAR4 | CH_RREAR4)
7088447a05SGarrett D'Amore #define	CH_5		(CH_STEREO | CH_CENTER | CH_LSURR | CH_RSURR)
7188447a05SGarrett D'Amore #define	CH_7		(CH_5 | CH_LREAR | CH_RREAR)
7288447a05SGarrett D'Amore 
7388447a05SGarrett D'Amore typedef struct chancfg {
7488447a05SGarrett D'Amore 	int		mask;
7588447a05SGarrett D'Amore 	const char	*name;
7688447a05SGarrett D'Amore 	unsigned	flags;
7788447a05SGarrett D'Amore 	int16_t		*data;
7888447a05SGarrett D'Amore 	int		len;
7988447a05SGarrett D'Amore } chancfg_t;
8088447a05SGarrett D'Amore 
8188447a05SGarrett D'Amore typedef struct testcfg {
8288447a05SGarrett D'Amore 	int		nchan;
8388447a05SGarrett D'Amore 	chancfg_t	*tests[16];
8488447a05SGarrett D'Amore } testcfg_t;
8588447a05SGarrett D'Amore 
8688447a05SGarrett D'Amore #define	CFLAG_LFE	0x1	/* lfe channel - not full range */
8788447a05SGarrett D'Amore 
8888447a05SGarrett D'Amore /*
8988447a05SGarrett D'Amore  * TRANSLATION_NOTE : The following strings are displayed during progress.
9088447a05SGarrett D'Amore  * Its important for alignment that they have the same displayed length.
9188447a05SGarrett D'Amore  */
9288447a05SGarrett D'Amore #define	NM_LEFT		"\t<left> ................"
9388447a05SGarrett D'Amore #define	NM_RIGHT	"\t<right> ..............."
9488447a05SGarrett D'Amore #define	NM_LREAR	"\t<left rear> ..........."
9588447a05SGarrett D'Amore #define	NM_RREAR	"\t<right rear> .........."
9688447a05SGarrett D'Amore #define	NM_LSIDE	"\t<left side> ..........."
9788447a05SGarrett D'Amore #define	NM_RSIDE	"\t<right side> .........."
9888447a05SGarrett D'Amore #define	NM_CENTER	"\t<center> .............."
9988447a05SGarrett D'Amore #define	NM_LFE		"\t<lfe> ................."
10088447a05SGarrett D'Amore #define	NM_STEREO	"\t<stereo> .............."
10188447a05SGarrett D'Amore #define	NM_40		"\t<4.0 surround> ........"
10288447a05SGarrett D'Amore #define	NM_50		"\t<5.0 surround> ........"
10388447a05SGarrett D'Amore #define	NM_70		"\t<7.0 surround> ........"
10488447a05SGarrett D'Amore 
10588447a05SGarrett D'Amore chancfg_t ch_left = { CH_LEFT, NM_LEFT, 0 };
10688447a05SGarrett D'Amore chancfg_t ch_right = { CH_RIGHT, NM_RIGHT, 0 };
10788447a05SGarrett D'Amore chancfg_t ch_stereo = { CH_STEREO, NM_STEREO, 0 };
10888447a05SGarrett D'Amore 
10988447a05SGarrett D'Amore chancfg_t ch_center = { CH_CENTER, NM_CENTER, 0 };
11088447a05SGarrett D'Amore chancfg_t ch_lfe = { CH_LFE, NM_LFE, CFLAG_LFE };
11188447a05SGarrett D'Amore 
11288447a05SGarrett D'Amore chancfg_t ch_lsurr_4 = { (1 << 2), NM_LREAR, 0 };
11388447a05SGarrett D'Amore chancfg_t ch_rsurr_4 = { (1 << 3), NM_RREAR, 0 };
11488447a05SGarrett D'Amore chancfg_t ch_4 = { CH_4, NM_40, 0 };
11588447a05SGarrett D'Amore 
11688447a05SGarrett D'Amore chancfg_t ch_lsurr_5 = { CH_LSURR, NM_LREAR, 0 };
11788447a05SGarrett D'Amore chancfg_t ch_rsurr_5 = { CH_RSURR, NM_RREAR, 0 };
11888447a05SGarrett D'Amore chancfg_t ch_5 = { CH_5, NM_50, 0 };
11988447a05SGarrett D'Amore 
12088447a05SGarrett D'Amore chancfg_t ch_lsurr_7 = { CH_LSURR, NM_LSIDE, 0 };
12188447a05SGarrett D'Amore chancfg_t ch_rsurr_7 = { CH_RSURR, NM_RSIDE, 0 };
12288447a05SGarrett D'Amore chancfg_t ch_lrear_7 = { CH_LREAR, NM_LREAR, 0 };
12388447a05SGarrett D'Amore chancfg_t ch_rrear_7 = { CH_RREAR, NM_RREAR, 0 };
12488447a05SGarrett D'Amore chancfg_t ch_7 = { CH_7, NM_70, 0 };
12588447a05SGarrett D'Amore 
12688447a05SGarrett D'Amore testcfg_t test_stereo = {
12788447a05SGarrett D'Amore 	2, { &ch_left, &ch_right, &ch_stereo, NULL }
12888447a05SGarrett D'Amore };
12988447a05SGarrett D'Amore 
13088447a05SGarrett D'Amore testcfg_t test_quad = {
13188447a05SGarrett D'Amore 	4, { &ch_left, &ch_right, &ch_stereo,
13288447a05SGarrett D'Amore 	&ch_lsurr_4, &ch_rsurr_4, &ch_4, NULL }
13388447a05SGarrett D'Amore };
13488447a05SGarrett D'Amore 
13588447a05SGarrett D'Amore testcfg_t test_51 = {
13688447a05SGarrett D'Amore 	6, { &ch_left, &ch_right, &ch_stereo,
13788447a05SGarrett D'Amore 	&ch_lsurr_5, &ch_rsurr_5, &ch_center, &ch_lfe, &ch_5, NULL }
13888447a05SGarrett D'Amore };
13988447a05SGarrett D'Amore 
14088447a05SGarrett D'Amore testcfg_t test_71 = {
14188447a05SGarrett D'Amore 	8, { &ch_left, &ch_right, &ch_stereo,
14288447a05SGarrett D'Amore 	&ch_lsurr_7, &ch_rsurr_7, &ch_lrear_7, &ch_rrear_7,
14388447a05SGarrett D'Amore 	&ch_center, &ch_lfe, &ch_7, NULL }
14488447a05SGarrett D'Amore };
14588447a05SGarrett D'Amore 
14688447a05SGarrett D'Amore /*
14788447a05SGarrett D'Amore  * uncompress_wave() is defined in wavedata.c. It expands the audio
14888447a05SGarrett D'Amore  * samples stored in wavedata.h and returns the lenghth of the
14988447a05SGarrett D'Amore  * uncompressed version in bytes.
15088447a05SGarrett D'Amore  *
15188447a05SGarrett D'Amore  * The uncompressed wave data format is 16 bit (native) stereo
15288447a05SGarrett D'Amore  * recorded at 48000 Hz.
15388447a05SGarrett D'Amore  */
15488447a05SGarrett D'Amore extern int uncompress_wave(short *outbuf);
15588447a05SGarrett D'Amore 
15688447a05SGarrett D'Amore static int data_len;
15788447a05SGarrett D'Amore 
15888447a05SGarrett D'Amore #define	MAXDEVICE   64
15988447a05SGarrett D'Amore extern void describe_error(int);
16088447a05SGarrett D'Amore 
16188447a05SGarrett D'Amore #define	SAMPLE_RATE 48000
16288447a05SGarrett D'Amore 
16388447a05SGarrett D'Amore /*
16488447a05SGarrett D'Amore  * Operating mode flags (set from the command line).
16588447a05SGarrett D'Amore  */
16688447a05SGarrett D'Amore #define	TF_LOOP		0x00000010	/* Loop until interrupted */
16788447a05SGarrett D'Amore 
16888447a05SGarrett D'Amore static int mixerfd;
16988447a05SGarrett D'Amore static int num_devices_tested = 0;
17088447a05SGarrett D'Amore 
17188447a05SGarrett D'Amore static short *sample_buf;
17288447a05SGarrett D'Amore 
17388447a05SGarrett D'Amore void
17488447a05SGarrett D'Amore prepare(testcfg_t *tcfg)
17588447a05SGarrett D'Amore {
17688447a05SGarrett D'Amore 	int	nsamples;
17788447a05SGarrett D'Amore 	int	i;
17888447a05SGarrett D'Amore 	chancfg_t	*ccfg;
17988447a05SGarrett D'Amore 	if ((sample_buf = malloc(2000000)) == NULL) {
18088447a05SGarrett D'Amore 		perror("malloc");
18188447a05SGarrett D'Amore 		exit(-1);
18288447a05SGarrett D'Amore 	}
18388447a05SGarrett D'Amore 
18488447a05SGarrett D'Amore 	data_len = uncompress_wave(sample_buf);
18588447a05SGarrett D'Amore 	nsamples = (data_len / sizeof (int16_t)) / 2;
18688447a05SGarrett D'Amore 
18788447a05SGarrett D'Amore 	for (i = 0; (ccfg = tcfg->tests[i]) != NULL; i++) {
18888447a05SGarrett D'Amore 		int16_t		*src, *dst;
18988447a05SGarrett D'Amore 		int		ch;
19088447a05SGarrett D'Amore 		int		samp;
19188447a05SGarrett D'Amore 
19288447a05SGarrett D'Amore 		src = sample_buf;
19388447a05SGarrett D'Amore 
19488447a05SGarrett D'Amore 		if (ccfg->flags != CFLAG_LFE) {
19588447a05SGarrett D'Amore 			ccfg->len = nsamples * tcfg->nchan * sizeof (int16_t);
19688447a05SGarrett D'Amore 			ccfg->data = malloc(ccfg->len);
19788447a05SGarrett D'Amore 			if ((dst = ccfg->data) == NULL) {
19888447a05SGarrett D'Amore 				perror("malloc");
19988447a05SGarrett D'Amore 				exit(-1);
20088447a05SGarrett D'Amore 			}
20188447a05SGarrett D'Amore 			for (samp = 0; samp < nsamples; samp++) {
20288447a05SGarrett D'Amore 				for (ch = 0; ch < tcfg->nchan; ch++) {
20388447a05SGarrett D'Amore 					*dst = ((1U << ch) & ccfg->mask) ?
20488447a05SGarrett D'Amore 					    *src : 0;
20588447a05SGarrett D'Amore 					dst++;
20688447a05SGarrett D'Amore 				}
20788447a05SGarrett D'Amore 				src += 2;
20888447a05SGarrett D'Amore 			}
20988447a05SGarrett D'Amore 		} else {
21088447a05SGarrett D'Amore 			/* Skip LFE for now */
21188447a05SGarrett D'Amore 			ccfg->len = 0;
21288447a05SGarrett D'Amore 		}
21388447a05SGarrett D'Amore 	}
21488447a05SGarrett D'Amore }
21588447a05SGarrett D'Amore 
21688447a05SGarrett D'Amore /*
21788447a05SGarrett D'Amore  * The testdsp() routine checks the capabilities of a given audio device number
21888447a05SGarrett D'Amore  * (parameter n) and decides if the test sound needs to be played.
21988447a05SGarrett D'Amore  */
22088447a05SGarrett D'Amore 
22188447a05SGarrett D'Amore /*ARGSUSED*/
22288447a05SGarrett D'Amore int
22388447a05SGarrett D'Amore testdsp(int hd, int flags, testcfg_t *tcfg)
22488447a05SGarrett D'Amore {
22588447a05SGarrett D'Amore 	float ratio;
22688447a05SGarrett D'Amore 	struct timeval t1, t2;
22788447a05SGarrett D'Amore 	unsigned long t;
22888447a05SGarrett D'Amore 	int sample_rate;
22988447a05SGarrett D'Amore 	int delay;
23088447a05SGarrett D'Amore 	long long total_bytes = 0;
23188447a05SGarrett D'Amore 	unsigned int tmp, caps;
23288447a05SGarrett D'Amore 	int i;
23388447a05SGarrett D'Amore 	chancfg_t *ccfg;
23488447a05SGarrett D'Amore 
23588447a05SGarrett D'Amore 	caps = 0;
23688447a05SGarrett D'Amore 	if (ioctl(hd, SNDCTL_DSP_GETCAPS, &caps) == -1) {
23788447a05SGarrett D'Amore 		perror("SNDCTL_DSP_GETCAPS");
23888447a05SGarrett D'Amore 		return (-1);
23988447a05SGarrett D'Amore 	}
24088447a05SGarrett D'Amore 
24188447a05SGarrett D'Amore 	/*
24288447a05SGarrett D'Amore 	 * Setup the sample format. Since OSS will support AFMT_S16_NE
24388447a05SGarrett D'Amore 	 * regardless of the device we do not need to support any
24488447a05SGarrett D'Amore 	 * other formats.
24588447a05SGarrett D'Amore 	 */
24688447a05SGarrett D'Amore 
24788447a05SGarrett D'Amore 	tmp = AFMT_S16_NE;
24888447a05SGarrett D'Amore 	if (ioctl(hd, SNDCTL_DSP_SETFMT, &tmp) == -1 || tmp != AFMT_S16_NE) {
24988447a05SGarrett D'Amore 		(void) printf(_("Device doesn't support native 16-bit PCM\n"));
25088447a05SGarrett D'Amore 		return (-1);
25188447a05SGarrett D'Amore 	}
25288447a05SGarrett D'Amore 
25388447a05SGarrett D'Amore 	/*
25488447a05SGarrett D'Amore 	 * Setup the device for channels. Once again we can simply
25588447a05SGarrett D'Amore 	 * assume that stereo will always work before OSS takes care
25688447a05SGarrett D'Amore 	 * of this by emulation if necessary.
25788447a05SGarrett D'Amore 	 */
25888447a05SGarrett D'Amore 	tmp = tcfg->nchan;
25988447a05SGarrett D'Amore 	if (ioctl(hd, SNDCTL_DSP_CHANNELS, &tmp) == -1 || tmp != tcfg->nchan) {
26088447a05SGarrett D'Amore 		(void) printf(_("The device doesn't support %d channels\n"),
26188447a05SGarrett D'Amore 		    tcfg->nchan);
26288447a05SGarrett D'Amore 		return (-2);
26388447a05SGarrett D'Amore 	}
26488447a05SGarrett D'Amore 
26588447a05SGarrett D'Amore 	/*
26688447a05SGarrett D'Amore 	 * Set up the sample rate.
26788447a05SGarrett D'Amore 	 */
26888447a05SGarrett D'Amore 
26988447a05SGarrett D'Amore 	tmp = SAMPLE_RATE;
27088447a05SGarrett D'Amore 	if (ioctl(hd, SNDCTL_DSP_SPEED, &tmp) == -1) {
27188447a05SGarrett D'Amore 		perror("SNDCTL_DSP_SPEED");
27288447a05SGarrett D'Amore 		return (-3);
27388447a05SGarrett D'Amore 	}
27488447a05SGarrett D'Amore 
27588447a05SGarrett D'Amore 	sample_rate = tmp;
27688447a05SGarrett D'Amore 	if (sample_rate != SAMPLE_RATE) {
27788447a05SGarrett D'Amore 		(void) printf(_("The device doesn't support %d Hz\n"),
27888447a05SGarrett D'Amore 		    SAMPLE_RATE);
27988447a05SGarrett D'Amore 		return (-3);
28088447a05SGarrett D'Amore 	}
28188447a05SGarrett D'Amore 	(void) printf("\n");
28288447a05SGarrett D'Amore 
28388447a05SGarrett D'Amore 	/*
28488447a05SGarrett D'Amore 	 * This program will measure the real sampling rate by
28588447a05SGarrett D'Amore 	 * computing the total time required to play the sample.
28688447a05SGarrett D'Amore 	 *
28788447a05SGarrett D'Amore 	 * This is not terribly presice with short test sounds but it
28888447a05SGarrett D'Amore 	 * can be used to detect if the sampling rate badly
28988447a05SGarrett D'Amore 	 * wrong. Errors of few percents is more likely to be caused
29088447a05SGarrett D'Amore 	 * by poor accuracy of the system clock rather than problems
29188447a05SGarrett D'Amore 	 * with the sampling rate.
29288447a05SGarrett D'Amore 	 */
29388447a05SGarrett D'Amore 	(void) gettimeofday(&t1, NULL);
29488447a05SGarrett D'Amore 
29588447a05SGarrett D'Amore 	for (i = 0; (ccfg = tcfg->tests[i]) != NULL; i++) {
29688447a05SGarrett D'Amore 		(void) fputs(_(ccfg->name), stdout);
29788447a05SGarrett D'Amore 		(void) fflush(stdout);
29888447a05SGarrett D'Amore 		if (ccfg->flags & CFLAG_LFE) {
29988447a05SGarrett D'Amore 			(void) printf(_("SKIPPED\n"));
30088447a05SGarrett D'Amore 			continue;
30188447a05SGarrett D'Amore 		}
30288447a05SGarrett D'Amore 
30388447a05SGarrett D'Amore 		if (write(hd, ccfg->data, ccfg->len) < 0) {
30488447a05SGarrett D'Amore 			(void) printf(_("ERROR: %s\n"),
30588447a05SGarrett D'Amore 			    strerror(errno));
30688447a05SGarrett D'Amore 			return (-3);
30788447a05SGarrett D'Amore 		}
30888447a05SGarrett D'Amore 		(void) printf(_("OK\n"));
30988447a05SGarrett D'Amore 		total_bytes += ccfg->len;
31088447a05SGarrett D'Amore 	}
31188447a05SGarrett D'Amore 
31288447a05SGarrett D'Amore 	(void) gettimeofday(&t2, NULL);
31388447a05SGarrett D'Amore 	delay = 0;
31488447a05SGarrett D'Amore 	(void) ioctl(hd, SNDCTL_DSP_GETODELAY, &delay);	/* Ignore errors */
31588447a05SGarrett D'Amore 
31688447a05SGarrett D'Amore 	/*
31788447a05SGarrett D'Amore 	 * Perform the time computations using milliseconds.
31888447a05SGarrett D'Amore 	 */
31988447a05SGarrett D'Amore 
32088447a05SGarrett D'Amore 	t = t2.tv_sec - t1.tv_sec;
32188447a05SGarrett D'Amore 	t *= 1000;
32288447a05SGarrett D'Amore 
32388447a05SGarrett D'Amore 	t += t2.tv_usec / 1000;
32488447a05SGarrett D'Amore 	t -= t1.tv_usec / 1000;
32588447a05SGarrett D'Amore 
32688447a05SGarrett D'Amore 	total_bytes -= delay;
32788447a05SGarrett D'Amore 	total_bytes *= 1000;
32888447a05SGarrett D'Amore 
32988447a05SGarrett D'Amore 	total_bytes /= t;
33088447a05SGarrett D'Amore 	total_bytes /= (tcfg->nchan * sizeof (int16_t));
33188447a05SGarrett D'Amore 
33288447a05SGarrett D'Amore 	ratio = ((float)total_bytes / (float)sample_rate) * 100.0;
33388447a05SGarrett D'Amore 	(void) printf(_("\t<measured sample rate %8.2f Hz (%4.2f%%)>\n"),
33488447a05SGarrett D'Amore 	    (float)sample_rate * ratio / 100.0, ratio - 100.0);
33588447a05SGarrett D'Amore 	num_devices_tested++;
33688447a05SGarrett D'Amore 
33788447a05SGarrett D'Amore 	return (1);
33888447a05SGarrett D'Amore }
33988447a05SGarrett D'Amore 
34088447a05SGarrett D'Amore static int
34188447a05SGarrett D'Amore find_num_devices(void)
34288447a05SGarrett D'Amore {
34388447a05SGarrett D'Amore 	oss_sysinfo info;
34488447a05SGarrett D'Amore 	struct utsname un;
34588447a05SGarrett D'Amore 	/*
34688447a05SGarrett D'Amore 	 * Find out the number of available audio devices by calling
34788447a05SGarrett D'Amore 	 * SNDCTL_SYSINFO.
34888447a05SGarrett D'Amore 	 */
34988447a05SGarrett D'Amore 
35088447a05SGarrett D'Amore 	if (ioctl(mixerfd, SNDCTL_SYSINFO, &info) == -1) {
35188447a05SGarrett D'Amore 		if (errno == ENXIO) {
35288447a05SGarrett D'Amore 			(void) fprintf(stderr,
35388447a05SGarrett D'Amore 			    _("No supported sound hardware detected.\n"));
35488447a05SGarrett D'Amore 			exit(-1);
35588447a05SGarrett D'Amore 		} else {
35688447a05SGarrett D'Amore 			perror("SNDCTL_SYSINFO");
35788447a05SGarrett D'Amore 			(void) printf(_("Cannot get system information.\n"));
35888447a05SGarrett D'Amore 			exit(-1);
35988447a05SGarrett D'Amore 		}
36088447a05SGarrett D'Amore 	}
36188447a05SGarrett D'Amore 	(void) printf(_("Sound subsystem and version: %s %s (0x%08X)\n"),
36288447a05SGarrett D'Amore 	    info.product, info.version, info.versionnum);
36388447a05SGarrett D'Amore 
36488447a05SGarrett D'Amore 	if (uname(&un) != -1)
36588447a05SGarrett D'Amore 		(void) printf(_("Platform: %s %s %s %s\n"),
36688447a05SGarrett D'Amore 		    un.sysname, un.release, un.version, un.machine);
36788447a05SGarrett D'Amore 
36888447a05SGarrett D'Amore 	return (info.numaudios);
36988447a05SGarrett D'Amore }
37088447a05SGarrett D'Amore 
37188447a05SGarrett D'Amore /*
37288447a05SGarrett D'Amore  * The test_device() routine checks certain information about the device
37388447a05SGarrett D'Amore  * and calls testdsp() to play the test sound.
37488447a05SGarrett D'Amore  */
37588447a05SGarrett D'Amore 
37688447a05SGarrett D'Amore int
37788447a05SGarrett D'Amore test_device(char *dn, int flags, testcfg_t *tcfg)
37888447a05SGarrett D'Amore {
37988447a05SGarrett D'Amore 	oss_audioinfo ainfo;
38088447a05SGarrett D'Amore 	int code;
38188447a05SGarrett D'Amore 	int fd;
38288447a05SGarrett D'Amore 
38388447a05SGarrett D'Amore 	fd = open(dn, O_WRONLY, 0);
38488447a05SGarrett D'Amore 	if (fd == -1) {
38588447a05SGarrett D'Amore 		int err = errno;
38688447a05SGarrett D'Amore 		perror(dn);
38788447a05SGarrett D'Amore 		errno = err;
38888447a05SGarrett D'Amore 		describe_error(errno);
389*68c47f65SGarrett D'Amore 		return (-1);
39088447a05SGarrett D'Amore 	}
39188447a05SGarrett D'Amore 
39288447a05SGarrett D'Amore 	ainfo.dev = -1;
39388447a05SGarrett D'Amore 	if (ioctl(fd, SNDCTL_AUDIOINFO, &ainfo) == -1) {
39488447a05SGarrett D'Amore 		perror("SNDCTL_AUDIOINFO");
39588447a05SGarrett D'Amore 		(void) close(fd);
396*68c47f65SGarrett D'Amore 		return (-1);
39788447a05SGarrett D'Amore 	}
39888447a05SGarrett D'Amore 
39988447a05SGarrett D'Amore 	(void) printf(_("\n*** Scanning sound adapter #%d ***\n"),
40088447a05SGarrett D'Amore 	    ainfo.card_number);
40188447a05SGarrett D'Amore 
40288447a05SGarrett D'Amore 	(void) printf(_("%s (audio engine %d): %s\n"), ainfo.devnode, ainfo.dev,
40388447a05SGarrett D'Amore 	    ainfo.name);
40488447a05SGarrett D'Amore 
40588447a05SGarrett D'Amore 	if (!ainfo.enabled) {
40688447a05SGarrett D'Amore 		(void) printf(_("  - Device not present - Skipping\n"));
40788447a05SGarrett D'Amore 		(void) close(fd);
408*68c47f65SGarrett D'Amore 		return (0);
40988447a05SGarrett D'Amore 	}
41088447a05SGarrett D'Amore 
41188447a05SGarrett D'Amore 	if (!(ainfo.caps & PCM_CAP_OUTPUT)) {
41288447a05SGarrett D'Amore 		(void) printf(_("  - Skipping input only device\n"));
41388447a05SGarrett D'Amore 		(void) close(fd);
414*68c47f65SGarrett D'Amore 		return (0);
41588447a05SGarrett D'Amore 	}
41688447a05SGarrett D'Amore 
41788447a05SGarrett D'Amore 	(void) printf(_("  - Performing audio playback test... "));
41888447a05SGarrett D'Amore 	(void) fflush(stdout);
41988447a05SGarrett D'Amore 
42088447a05SGarrett D'Amore 	code = testdsp(fd, flags, tcfg);
42188447a05SGarrett D'Amore 	(void) close(fd);
422*68c47f65SGarrett D'Amore 	if (code < 0) {
423*68c47f65SGarrett D'Amore 		return (code);
424*68c47f65SGarrett D'Amore 	}
42588447a05SGarrett D'Amore 
42688447a05SGarrett D'Amore 	return (code == 1);
42788447a05SGarrett D'Amore }
42888447a05SGarrett D'Amore 
42988447a05SGarrett D'Amore void
43088447a05SGarrett D'Amore describe_error(int err)
43188447a05SGarrett D'Amore {
43288447a05SGarrett D'Amore 	switch (err) {
43388447a05SGarrett D'Amore 	case ENODEV:
43488447a05SGarrett D'Amore 		(void) fprintf(stderr,
43588447a05SGarrett D'Amore 		    _("The device file was found in /dev but\n"
43688447a05SGarrett D'Amore 		    "the driver was not loaded.\n"));
43788447a05SGarrett D'Amore 		break;
43888447a05SGarrett D'Amore 
43988447a05SGarrett D'Amore 	case ENXIO:
44088447a05SGarrett D'Amore 		(void) fprintf(stderr,
44188447a05SGarrett D'Amore 		    _("There are no sound devices available.\n"
44288447a05SGarrett D'Amore 		    "The most likely reason is that the device you have\n"
44388447a05SGarrett D'Amore 		    "is malfunctioning or it's not supported.\n"
44488447a05SGarrett D'Amore 		    "It's also possible that you are trying to use the wrong "
44588447a05SGarrett D'Amore 		    "device file.\n"));
44688447a05SGarrett D'Amore 		break;
44788447a05SGarrett D'Amore 
44888447a05SGarrett D'Amore 	case ENOSPC:
44988447a05SGarrett D'Amore 		(void) fprintf(stderr,
45088447a05SGarrett D'Amore 		    _("Your system cannot allocate memory for the device\n"
45188447a05SGarrett D'Amore 		    "buffers. Reboot your machine and try again.\n"));
45288447a05SGarrett D'Amore 		break;
45388447a05SGarrett D'Amore 
45488447a05SGarrett D'Amore 	case ENOENT:
45588447a05SGarrett D'Amore 		(void) fprintf(stderr,
45688447a05SGarrett D'Amore 		    _("The device file is missing from /dev.\n"));
45788447a05SGarrett D'Amore 		break;
45888447a05SGarrett D'Amore 
45988447a05SGarrett D'Amore 
46088447a05SGarrett D'Amore 	case EBUSY:
46188447a05SGarrett D'Amore 		(void) fprintf(stderr,
46288447a05SGarrett D'Amore 		    _("The device is busy. There is some other application\n"
46388447a05SGarrett D'Amore 		    "using it.\n"));
46488447a05SGarrett D'Amore 		break;
46588447a05SGarrett D'Amore 
46688447a05SGarrett D'Amore 	default:
46788447a05SGarrett D'Amore 		break;
46888447a05SGarrett D'Amore 	}
46988447a05SGarrett D'Amore }
47088447a05SGarrett D'Amore 
47188447a05SGarrett D'Amore int
47288447a05SGarrett D'Amore main(int argc, char *argv[])
47388447a05SGarrett D'Amore {
47488447a05SGarrett D'Amore 	int t, i;
47588447a05SGarrett D'Amore 	int maxdev;
47688447a05SGarrett D'Amore 	int flags = 0;
47788447a05SGarrett D'Amore 	int status = 0;
478*68c47f65SGarrett D'Amore 	int errors = 0;
47988447a05SGarrett D'Amore 	int numdev;
48088447a05SGarrett D'Amore 	extern int optind;
48188447a05SGarrett D'Amore 	testcfg_t	*tcfg;
48288447a05SGarrett D'Amore 
48388447a05SGarrett D'Amore 	(void) setlocale(LC_ALL, "");
48488447a05SGarrett D'Amore 	(void) textdomain(TEXT_DOMAIN);
48588447a05SGarrett D'Amore 
48688447a05SGarrett D'Amore 	tcfg = &test_stereo;
48788447a05SGarrett D'Amore 
48888447a05SGarrett D'Amore 	/*
48988447a05SGarrett D'Amore 	 * Simple command line switch handling.
49088447a05SGarrett D'Amore 	 */
49188447a05SGarrett D'Amore 
49288447a05SGarrett D'Amore 	while ((i = getopt(argc, argv, "l2457")) != EOF) {
49388447a05SGarrett D'Amore 		switch (i) {
49488447a05SGarrett D'Amore 		case 'l':
49588447a05SGarrett D'Amore 			flags |= TF_LOOP;
49688447a05SGarrett D'Amore 			break;
49788447a05SGarrett D'Amore 		case '2':
49888447a05SGarrett D'Amore 			tcfg = &test_stereo;
49988447a05SGarrett D'Amore 			break;
50088447a05SGarrett D'Amore 		case '4':
50188447a05SGarrett D'Amore 			tcfg = &test_quad;
50288447a05SGarrett D'Amore 			break;
50388447a05SGarrett D'Amore 		case '5':
50488447a05SGarrett D'Amore 			tcfg = &test_51;
50588447a05SGarrett D'Amore 			break;
50688447a05SGarrett D'Amore 		case '7':
50788447a05SGarrett D'Amore 			tcfg = &test_71;
50888447a05SGarrett D'Amore 			break;
50988447a05SGarrett D'Amore 		default:
51088447a05SGarrett D'Amore 			(void) printf(_("Usage: %s [options...] [device]\n"
51188447a05SGarrett D'Amore 			    "	-2	Stereo test\n"
51288447a05SGarrett D'Amore 			    "	-4	Quadraphonic 4.0 test\n"
51388447a05SGarrett D'Amore 			    "	-5	Surround 5.1 test\n"
51488447a05SGarrett D'Amore 			    "	-7	Surround 7.1 test\n"
51588447a05SGarrett D'Amore 			    "	-l	Loop test\n"), argv[0]);
51688447a05SGarrett D'Amore 			exit(-1);
51788447a05SGarrett D'Amore 		}
51888447a05SGarrett D'Amore 	}
51988447a05SGarrett D'Amore 
52088447a05SGarrett D'Amore 	/*
52188447a05SGarrett D'Amore 	 * Open the mixer device used for calling SNDCTL_SYSINFO and
52288447a05SGarrett D'Amore 	 * SNDCTL_AUDIOINFO.
52388447a05SGarrett D'Amore 	 */
52488447a05SGarrett D'Amore 	if ((mixerfd = open("/dev/mixer", O_RDWR, 0)) == -1) {
52588447a05SGarrett D'Amore 		int err = errno;
52688447a05SGarrett D'Amore 		perror("/dev/mixer");
52788447a05SGarrett D'Amore 		errno = err;
52888447a05SGarrett D'Amore 		describe_error(errno);
52988447a05SGarrett D'Amore 		exit(-1);
53088447a05SGarrett D'Amore 	}
53188447a05SGarrett D'Amore 
53288447a05SGarrett D'Amore 	prepare(tcfg);			/* Prepare the wave data */
53388447a05SGarrett D'Amore 
53488447a05SGarrett D'Amore 	/*
53588447a05SGarrett D'Amore 	 * Enumerate all devices and play the test sounds.
53688447a05SGarrett D'Amore 	 */
53788447a05SGarrett D'Amore 	maxdev = find_num_devices();
53888447a05SGarrett D'Amore 	if (maxdev < 1) {
53988447a05SGarrett D'Amore 		(void) printf(_("\n*** No audio hardware available ***\n"));
54088447a05SGarrett D'Amore 		exit(-1);
54188447a05SGarrett D'Amore 	}
54288447a05SGarrett D'Amore 
54388447a05SGarrett D'Amore 	numdev = (argc - optind);
54488447a05SGarrett D'Amore 	do {
54588447a05SGarrett D'Amore 		char *dn;
54688447a05SGarrett D'Amore 		oss_audioinfo	ainfo;
547*68c47f65SGarrett D'Amore 		int rv;
54888447a05SGarrett D'Amore 
549*68c47f65SGarrett D'Amore 		status = 0;
55088447a05SGarrett D'Amore 		if (numdev > 0) {
55188447a05SGarrett D'Amore 			for (t = 0; t < numdev; t++) {
55288447a05SGarrett D'Amore 				dn = argv[optind + t];
553*68c47f65SGarrett D'Amore 				rv = test_device(dn, flags, tcfg);
554*68c47f65SGarrett D'Amore 				if (rv < 0) {
555*68c47f65SGarrett D'Amore 					errors++;
556*68c47f65SGarrett D'Amore 				} else if (rv) {
55788447a05SGarrett D'Amore 					status++;
55888447a05SGarrett D'Amore 				}
559*68c47f65SGarrett D'Amore 			}
56088447a05SGarrett D'Amore 		} else {
56188447a05SGarrett D'Amore 			for (t = 0; t < maxdev; t++) {
56288447a05SGarrett D'Amore 				ainfo.dev = t;
56388447a05SGarrett D'Amore 				if (ioctl(mixerfd, SNDCTL_AUDIOINFO,
56488447a05SGarrett D'Amore 				    &ainfo) == -1) {
56588447a05SGarrett D'Amore 					perror("SNDCTL_AUDIOINFO");
56688447a05SGarrett D'Amore 					status++;
56788447a05SGarrett D'Amore 					continue;
56888447a05SGarrett D'Amore 				}
56988447a05SGarrett D'Amore 				dn = ainfo.devnode;
570*68c47f65SGarrett D'Amore 				rv = test_device(dn, flags, tcfg);
571*68c47f65SGarrett D'Amore 				if (rv < 0) {
572*68c47f65SGarrett D'Amore 					errors++;
573*68c47f65SGarrett D'Amore 				} else if (rv) {
57488447a05SGarrett D'Amore 					status++;
57588447a05SGarrett D'Amore 				}
57688447a05SGarrett D'Amore 			}
577*68c47f65SGarrett D'Amore 		}
57888447a05SGarrett D'Amore 
579*68c47f65SGarrett D'Amore 		if (errors == 0)
58088447a05SGarrett D'Amore 			(void) printf(_("\n*** All tests completed OK ***\n"));
58188447a05SGarrett D'Amore 		else
58288447a05SGarrett D'Amore 			(void) printf(_("\n*** Errors were detected ***\n"));
58388447a05SGarrett D'Amore 
584*68c47f65SGarrett D'Amore 	} while (status && (flags & TF_LOOP));
58588447a05SGarrett D'Amore 
58688447a05SGarrett D'Amore 	(void) close(mixerfd);
58788447a05SGarrett D'Amore 
58888447a05SGarrett D'Amore 	return (status);
58988447a05SGarrett D'Amore }
590