13ccb1966SGarrett D'Amore /*
23ccb1966SGarrett D'Amore  * CDDL HEADER START
33ccb1966SGarrett D'Amore  *
43ccb1966SGarrett D'Amore  * The contents of this file are subject to the terms of the
53ccb1966SGarrett D'Amore  * Common Development and Distribution License (the "License").
63ccb1966SGarrett D'Amore  * You may not use this file except in compliance with the License.
73ccb1966SGarrett D'Amore  *
83ccb1966SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93ccb1966SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
103ccb1966SGarrett D'Amore  * See the License for the specific language governing permissions
113ccb1966SGarrett D'Amore  * and limitations under the License.
123ccb1966SGarrett D'Amore  *
133ccb1966SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
143ccb1966SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153ccb1966SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
163ccb1966SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
173ccb1966SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
183ccb1966SGarrett D'Amore  *
193ccb1966SGarrett D'Amore  * CDDL HEADER END
203ccb1966SGarrett D'Amore  */
213ccb1966SGarrett D'Amore /*
22*99bddabbSGarrett D'Amore  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
233ccb1966SGarrett D'Amore  * Use is subject to license terms.
243ccb1966SGarrett D'Amore  */
253ccb1966SGarrett D'Amore 
263ccb1966SGarrett D'Amore #include <stdio.h>
273ccb1966SGarrett D'Amore #include <stdlib.h>
283ccb1966SGarrett D'Amore #include <errno.h>
293ccb1966SGarrett D'Amore #include <string.h>
303ccb1966SGarrett D'Amore #include <strings.h>
313ccb1966SGarrett D'Amore #include <locale.h>
323ccb1966SGarrett D'Amore #include <libintl.h>
333ccb1966SGarrett D'Amore #include <stdarg.h>
343ccb1966SGarrett D'Amore #include <stddef.h>
353ccb1966SGarrett D'Amore #include <sys/types.h>
363ccb1966SGarrett D'Amore #include <sys/stat.h>
373ccb1966SGarrett D'Amore #include <sys/mkdev.h>
383ccb1966SGarrett D'Amore #include <fcntl.h>
393ccb1966SGarrett D'Amore #include <unistd.h>
403ccb1966SGarrett D'Amore #include <ctype.h>
413ccb1966SGarrett D'Amore #include <sys/param.h>
423ccb1966SGarrett D'Amore #include <sys/soundcard.h>
43aef5f291SGarrett D'Amore #include <libdevinfo.h>
443ccb1966SGarrett D'Amore 
453ccb1966SGarrett D'Amore #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
463ccb1966SGarrett D'Amore #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
473ccb1966SGarrett D'Amore #endif
483ccb1966SGarrett D'Amore 
493ccb1966SGarrett D'Amore #define	_(s)	gettext(s)
503ccb1966SGarrett D'Amore 
513ccb1966SGarrett D'Amore #define	MAXLINE	1024
523ccb1966SGarrett D'Amore 
533ccb1966SGarrett D'Amore #define	AUDIO_CTRL_STEREO_LEFT(v)	((uint8_t)((v) & 0xff))
543ccb1966SGarrett D'Amore #define	AUDIO_CTRL_STEREO_RIGHT(v)	((uint8_t)(((v) >> 8) & 0xff))
553ccb1966SGarrett D'Amore #define	AUDIO_CTRL_STEREO_VAL(l, r)	(((l) & 0xff) | (((r) & 0xff) << 8))
563ccb1966SGarrett D'Amore 
573ccb1966SGarrett D'Amore /*
583ccb1966SGarrett D'Amore  * These are borrowed from sys/audio/audio_common.h, where the values
593ccb1966SGarrett D'Amore  * are protected by _KERNEL.
603ccb1966SGarrett D'Amore  */
613ccb1966SGarrett D'Amore #define	AUDIO_MN_TYPE_NBITS	(4)
623ccb1966SGarrett D'Amore #define	AUDIO_MN_TYPE_MASK	((1U << AUDIO_MN_TYPE_NBITS) - 1)
633ccb1966SGarrett D'Amore #define	AUDIO_MINOR_MIXER	(0)
643ccb1966SGarrett D'Amore 
653ccb1966SGarrett D'Amore 
663ccb1966SGarrett D'Amore /*
673ccb1966SGarrett D'Amore  * Column display information
683ccb1966SGarrett D'Amore  * All are related to the types enumerated in col_t and any change should be
693ccb1966SGarrett D'Amore  * reflected in the corresponding indices and offsets for all the variables
703ccb1966SGarrett D'Amore  * accordingly.  Most tweaks to the display can be done by adjusting the
713ccb1966SGarrett D'Amore  * values here.
723ccb1966SGarrett D'Amore  */
733ccb1966SGarrett D'Amore 
743ccb1966SGarrett D'Amore /* types of columns displayed */
753ccb1966SGarrett D'Amore typedef enum { COL_DV = 0, COL_NM, COL_VAL, COL_SEL} col_t;
763ccb1966SGarrett D'Amore 
773ccb1966SGarrett D'Amore /* corresponding sizes of columns; does not include trailing null */
783ccb1966SGarrett D'Amore #define	COL_DV_SZ	16
793ccb1966SGarrett D'Amore #define	COL_NM_SZ	24
803ccb1966SGarrett D'Amore #define	COL_VAL_SZ	10
813ccb1966SGarrett D'Amore #define	COL_SEL_SZ	20
823ccb1966SGarrett D'Amore #define	COL_MAX_SZ	64
833ccb1966SGarrett D'Amore 
843ccb1966SGarrett D'Amore /* corresponding sizes of columns, indexed by col_t value */
853ccb1966SGarrett D'Amore static int col_sz[] = {
863ccb1966SGarrett D'Amore 	COL_DV_SZ, COL_NM_SZ, COL_VAL_SZ, COL_SEL_SZ
873ccb1966SGarrett D'Amore };
883ccb1966SGarrett D'Amore 
893ccb1966SGarrett D'Amore /* used by callers of the printing function */
903ccb1966SGarrett D'Amore typedef struct col_prt {
913ccb1966SGarrett D'Amore 	char *col_dv;
923ccb1966SGarrett D'Amore 	char *col_nm;
933ccb1966SGarrett D'Amore 	char *col_val;
943ccb1966SGarrett D'Amore 	char *col_sel;
953ccb1966SGarrett D'Amore } col_prt_t;
963ccb1966SGarrett D'Amore 
973ccb1966SGarrett D'Amore /* columns displayed in order with vopt = 0 */
983ccb1966SGarrett D'Amore static int col_dpy[] = {COL_NM, COL_VAL};
993ccb1966SGarrett D'Amore static int col_dpy_len = sizeof (col_dpy) / sizeof (*col_dpy);
1003ccb1966SGarrett D'Amore 
1013ccb1966SGarrett D'Amore /* tells the printing function what members to use; follows col_dpy[] */
1023ccb1966SGarrett D'Amore static size_t col_dpy_prt[] = {
1033ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_nm),
1043ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_val),
1053ccb1966SGarrett D'Amore };
1063ccb1966SGarrett D'Amore 
1073ccb1966SGarrett D'Amore /* columns displayed in order with vopt = 1 */
1083ccb1966SGarrett D'Amore static int col_dpy_vopt[] = { COL_DV, COL_NM, COL_VAL, COL_SEL};
1093ccb1966SGarrett D'Amore static int col_dpy_vopt_len = sizeof (col_dpy_vopt) / sizeof (*col_dpy_vopt);
1103ccb1966SGarrett D'Amore 
1113ccb1966SGarrett D'Amore /* tells the printing function what members to use; follows col_dpy_vopt[] */
1123ccb1966SGarrett D'Amore static size_t col_dpy_prt_vopt[] = {
1133ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_dv),
1143ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_nm),
1153ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_val),
1163ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_sel)
1173ccb1966SGarrett D'Amore };
1183ccb1966SGarrett D'Amore 
1193ccb1966SGarrett D'Amore /* columns displayed in order with tofile = 1 */
1203ccb1966SGarrett D'Amore static int col_dpy_tofile[] = { COL_NM, COL_VAL};
1213ccb1966SGarrett D'Amore static int col_dpy_tofile_len = sizeof (col_dpy_tofile) /
1223ccb1966SGarrett D'Amore     sizeof (*col_dpy_tofile);
1233ccb1966SGarrett D'Amore 
1243ccb1966SGarrett D'Amore /* tells the printing function what members to use; follows col_dpy_tofile[] */
1253ccb1966SGarrett D'Amore static size_t col_dpy_prt_tofile[] = {
1263ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_nm),
1273ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_val)
1283ccb1966SGarrett D'Amore };
1293ccb1966SGarrett D'Amore 
1303ccb1966SGarrett D'Amore 
1313ccb1966SGarrett D'Amore /*
1323ccb1966SGarrett D'Amore  * mixer and control accounting
1333ccb1966SGarrett D'Amore  */
1343ccb1966SGarrett D'Amore 
1353ccb1966SGarrett D'Amore typedef struct cinfo {
1363ccb1966SGarrett D'Amore 	oss_mixext ci;
1373ccb1966SGarrett D'Amore 	oss_mixer_enuminfo *enump;
1383ccb1966SGarrett D'Amore } cinfo_t;
1393ccb1966SGarrett D'Amore 
1403ccb1966SGarrett D'Amore typedef struct device {
1413ccb1966SGarrett D'Amore 	oss_card_info	card;
1423ccb1966SGarrett D'Amore 	oss_mixerinfo	mixer;
1433ccb1966SGarrett D'Amore 
1443ccb1966SGarrett D'Amore 	int		cmax;
1453ccb1966SGarrett D'Amore 	cinfo_t		*controls;
1463ccb1966SGarrett D'Amore 
1473ccb1966SGarrett D'Amore 	int		mfd;
1483ccb1966SGarrett D'Amore 	dev_t		devt;
1493ccb1966SGarrett D'Amore 
1503ccb1966SGarrett D'Amore 	struct device	*nextp;
1513ccb1966SGarrett D'Amore } device_t;
1523ccb1966SGarrett D'Amore 
1533ccb1966SGarrett D'Amore static device_t	*devices = NULL;
1543ccb1966SGarrett D'Amore 
1553ccb1966SGarrett D'Amore /*PRINTFLIKE1*/
1563ccb1966SGarrett D'Amore static void
msg(char * fmt,...)1573ccb1966SGarrett D'Amore msg(char *fmt, ...)
1583ccb1966SGarrett D'Amore {
1593ccb1966SGarrett D'Amore 	va_list ap;
1603ccb1966SGarrett D'Amore 
1613ccb1966SGarrett D'Amore 	va_start(ap, fmt);
1623ccb1966SGarrett D'Amore 	(void) vprintf(fmt, ap);
1633ccb1966SGarrett D'Amore 	va_end(ap);
1643ccb1966SGarrett D'Amore }
1653ccb1966SGarrett D'Amore 
1663ccb1966SGarrett D'Amore /*PRINTFLIKE1*/
1673ccb1966SGarrett D'Amore static void
warn(char * fmt,...)1683ccb1966SGarrett D'Amore warn(char *fmt, ...)
1693ccb1966SGarrett D'Amore {
1703ccb1966SGarrett D'Amore 	va_list ap;
1713ccb1966SGarrett D'Amore 
1723ccb1966SGarrett D'Amore 	va_start(ap, fmt);
1733ccb1966SGarrett D'Amore 	(void) vfprintf(stderr, fmt, ap);
1743ccb1966SGarrett D'Amore 	va_end(ap);
1753ccb1966SGarrett D'Amore }
1763ccb1966SGarrett D'Amore 
1773ccb1966SGarrett D'Amore static void
free_device(device_t * d)1783ccb1966SGarrett D'Amore free_device(device_t *d)
1793ccb1966SGarrett D'Amore {
1803ccb1966SGarrett D'Amore 	int		i;
1813ccb1966SGarrett D'Amore 	device_t	**dpp;
1823ccb1966SGarrett D'Amore 
1833ccb1966SGarrett D'Amore 	dpp = &devices;
1843ccb1966SGarrett D'Amore 	while ((*dpp) && ((*dpp) != d)) {
1853ccb1966SGarrett D'Amore 		dpp = &((*dpp)->nextp);
1863ccb1966SGarrett D'Amore 	}
1873ccb1966SGarrett D'Amore 	if (*dpp) {
1883ccb1966SGarrett D'Amore 		*dpp = d->nextp;
1893ccb1966SGarrett D'Amore 	}
1903ccb1966SGarrett D'Amore 	for (i = 0; i < d->cmax; i++) {
1913ccb1966SGarrett D'Amore 		if (d->controls[i].enump != NULL)
1923ccb1966SGarrett D'Amore 			free(d->controls[i].enump);
1933ccb1966SGarrett D'Amore 	}
1943ccb1966SGarrett D'Amore 
1953ccb1966SGarrett D'Amore 	if (d->mfd >= 0)
1963ccb1966SGarrett D'Amore 		(void) close(d->mfd);
1973ccb1966SGarrett D'Amore 
1983ccb1966SGarrett D'Amore 	free(d);
1993ccb1966SGarrett D'Amore }
2003ccb1966SGarrett D'Amore 
2013ccb1966SGarrett D'Amore static void
free_devices(void)2023ccb1966SGarrett D'Amore free_devices(void)
2033ccb1966SGarrett D'Amore {
2043ccb1966SGarrett D'Amore 	device_t *d = devices;
2053ccb1966SGarrett D'Amore 
2063ccb1966SGarrett D'Amore 	while ((d = devices) != NULL) {
2073ccb1966SGarrett D'Amore 		free_device(d);
2083ccb1966SGarrett D'Amore 	}
2093ccb1966SGarrett D'Amore 
2103ccb1966SGarrett D'Amore 	devices = NULL;
2113ccb1966SGarrett D'Amore }
2123ccb1966SGarrett D'Amore 
2133ccb1966SGarrett D'Amore 
2143ccb1966SGarrett D'Amore /*
2153ccb1966SGarrett D'Amore  * adds to the end of global devices and returns a pointer to the new entry
2163ccb1966SGarrett D'Amore  */
2173ccb1966SGarrett D'Amore static device_t *
alloc_device(void)2183ccb1966SGarrett D'Amore alloc_device(void)
2193ccb1966SGarrett D'Amore {
2203ccb1966SGarrett D'Amore 	device_t *p;
2213ccb1966SGarrett D'Amore 	device_t *d = calloc(1, sizeof (*d));
2223ccb1966SGarrett D'Amore 
2233ccb1966SGarrett D'Amore 	d->card.card = -1;
2243ccb1966SGarrett D'Amore 	d->mixer.dev = -1;
2253ccb1966SGarrett D'Amore 	d->mfd = -1;
2263ccb1966SGarrett D'Amore 
2273ccb1966SGarrett D'Amore 	if (devices == NULL) {
2283ccb1966SGarrett D'Amore 		devices = d;
2293ccb1966SGarrett D'Amore 	} else {
2303ccb1966SGarrett D'Amore 		for (p = devices; p->nextp != NULL; p = p->nextp) {}
2313ccb1966SGarrett D'Amore 
2323ccb1966SGarrett D'Amore 		p->nextp = d;
2333ccb1966SGarrett D'Amore 	}
2343ccb1966SGarrett D'Amore 	return (d);
2353ccb1966SGarrett D'Amore }
2363ccb1966SGarrett D'Amore 
2373ccb1966SGarrett D'Amore 
2383ccb1966SGarrett D'Amore /*
2393ccb1966SGarrett D'Amore  * cinfop->enump needs to be present
2403ccb1966SGarrett D'Amore  * idx should be: >= 0 to < cinfop->ci.maxvalue
2413ccb1966SGarrett D'Amore  */
2423ccb1966SGarrett D'Amore static char *
get_enum_str(cinfo_t * cinfop,int idx)2433ccb1966SGarrett D'Amore get_enum_str(cinfo_t *cinfop, int idx)
2443ccb1966SGarrett D'Amore {
2453ccb1966SGarrett D'Amore 	int sz = sizeof (*cinfop->ci.enum_present) * 8;
2463ccb1966SGarrett D'Amore 
2473ccb1966SGarrett D'Amore 	if (cinfop->ci.enum_present[idx / sz] & (1 << (idx % sz)))
2483ccb1966SGarrett D'Amore 		return (cinfop->enump->strings + cinfop->enump->strindex[idx]);
2493ccb1966SGarrett D'Amore 
2503ccb1966SGarrett D'Amore 	return (NULL);
2513ccb1966SGarrett D'Amore }
2523ccb1966SGarrett D'Amore 
2533ccb1966SGarrett D'Amore 
2543ccb1966SGarrett D'Amore /*
2553ccb1966SGarrett D'Amore  * caller fills in d->mixer.devnode; func fills in the rest
2563ccb1966SGarrett D'Amore  */
2573ccb1966SGarrett D'Amore static int
get_device_info(device_t * d)2583ccb1966SGarrett D'Amore get_device_info(device_t *d)
2593ccb1966SGarrett D'Amore {
2603ccb1966SGarrett D'Amore 	int fd = -1;
2613ccb1966SGarrett D'Amore 	int i;
2623ccb1966SGarrett D'Amore 	cinfo_t *ci;
2633ccb1966SGarrett D'Amore 
2643ccb1966SGarrett D'Amore 	if ((fd = open(d->mixer.devnode, O_RDWR)) < 0) {
2653ccb1966SGarrett D'Amore 		perror(_("Error opening device"));
2663ccb1966SGarrett D'Amore 		return (errno);
2673ccb1966SGarrett D'Amore 	}
2683ccb1966SGarrett D'Amore 	d->mfd = fd;
2693ccb1966SGarrett D'Amore 
2703ccb1966SGarrett D'Amore 	d->cmax = -1;
2713ccb1966SGarrett D'Amore 	if (ioctl(fd, SNDCTL_MIX_NREXT, &d->cmax) < 0) {
2723ccb1966SGarrett D'Amore 		perror(_("Error getting control count"));
2733ccb1966SGarrett D'Amore 		return (errno);
2743ccb1966SGarrett D'Amore 	}
2753ccb1966SGarrett D'Amore 
2763ccb1966SGarrett D'Amore 	d->controls = calloc(d->cmax, sizeof (*d->controls));
2773ccb1966SGarrett D'Amore 
2783ccb1966SGarrett D'Amore 	for (i = 0; i < d->cmax; i++) {
2793ccb1966SGarrett D'Amore 		ci = &d->controls[i];
2803ccb1966SGarrett D'Amore 
2813ccb1966SGarrett D'Amore 		ci->ci.dev = -1;
2823ccb1966SGarrett D'Amore 		ci->ci.ctrl = i;
2833ccb1966SGarrett D'Amore 
2843ccb1966SGarrett D'Amore 		if (ioctl(fd, SNDCTL_MIX_EXTINFO, &ci->ci) < 0) {
2853ccb1966SGarrett D'Amore 			perror(_("Error getting control info"));
2863ccb1966SGarrett D'Amore 			return (errno);
2873ccb1966SGarrett D'Amore 		}
2883ccb1966SGarrett D'Amore 
2893ccb1966SGarrett D'Amore 		if (ci->ci.type == MIXT_ENUM) {
2903ccb1966SGarrett D'Amore 			ci->enump = calloc(1, sizeof (*ci->enump));
2913ccb1966SGarrett D'Amore 			ci->enump->dev = -1;
2923ccb1966SGarrett D'Amore 			ci->enump->ctrl = ci->ci.ctrl;
2933ccb1966SGarrett D'Amore 
2943ccb1966SGarrett D'Amore 			if (ioctl(fd, SNDCTL_MIX_ENUMINFO, ci->enump) < 0) {
2953ccb1966SGarrett D'Amore 				perror(_("Error getting enum info"));
2963ccb1966SGarrett D'Amore 				return (errno);
2973ccb1966SGarrett D'Amore 			}
2983ccb1966SGarrett D'Amore 		}
2993ccb1966SGarrett D'Amore 	}
3003ccb1966SGarrett D'Amore 
3013ccb1966SGarrett D'Amore 	return (0);
3023ccb1966SGarrett D'Amore }
3033ccb1966SGarrett D'Amore 
3043ccb1966SGarrett D'Amore 
3053ccb1966SGarrett D'Amore static int
load_devices(void)3063ccb1966SGarrett D'Amore load_devices(void)
3073ccb1966SGarrett D'Amore {
3083ccb1966SGarrett D'Amore 	int rv = -1;
3093ccb1966SGarrett D'Amore 	int fd = -1;
3103ccb1966SGarrett D'Amore 	int i;
3113ccb1966SGarrett D'Amore 	oss_sysinfo si;
3123ccb1966SGarrett D'Amore 	device_t *d;
3133ccb1966SGarrett D'Amore 
314aef5f291SGarrett D'Amore 	if (devices != NULL) {
315aef5f291SGarrett D'Amore 		/* already loaded */
316aef5f291SGarrett D'Amore 		return (0);
317aef5f291SGarrett D'Amore 	}
318aef5f291SGarrett D'Amore 
3193ccb1966SGarrett D'Amore 	if ((fd = open("/dev/mixer", O_RDWR)) < 0) {
3203ccb1966SGarrett D'Amore 		rv = errno;
3213ccb1966SGarrett D'Amore 		warn(_("Error opening mixer\n"));
3223ccb1966SGarrett D'Amore 		goto OUT;
3233ccb1966SGarrett D'Amore 	}
3243ccb1966SGarrett D'Amore 
3253ccb1966SGarrett D'Amore 	if (ioctl(fd, SNDCTL_SYSINFO, &si) < 0) {
3263ccb1966SGarrett D'Amore 		rv = errno;
3273ccb1966SGarrett D'Amore 		perror(_("Error getting system information"));
3283ccb1966SGarrett D'Amore 		goto OUT;
3293ccb1966SGarrett D'Amore 	}
3303ccb1966SGarrett D'Amore 
3313ccb1966SGarrett D'Amore 	for (i = 0; i < si.nummixers; i++) {
3323ccb1966SGarrett D'Amore 
3333ccb1966SGarrett D'Amore 		struct stat sbuf;
3343ccb1966SGarrett D'Amore 
3353ccb1966SGarrett D'Amore 		d = alloc_device();
3363ccb1966SGarrett D'Amore 		d->mixer.dev = i;
3373ccb1966SGarrett D'Amore 
3383ccb1966SGarrett D'Amore 		if (ioctl(fd, SNDCTL_MIXERINFO, &d->mixer) != 0) {
3393ccb1966SGarrett D'Amore 			continue;
3403ccb1966SGarrett D'Amore 		}
3413ccb1966SGarrett D'Amore 
3423ccb1966SGarrett D'Amore 		d->card.card = d->mixer.card_number;
3433ccb1966SGarrett D'Amore 
3443ccb1966SGarrett D'Amore 		if ((ioctl(fd, SNDCTL_CARDINFO, &d->card) != 0) ||
3453ccb1966SGarrett D'Amore 		    (stat(d->mixer.devnode, &sbuf) != 0) ||
3463ccb1966SGarrett D'Amore 		    ((sbuf.st_mode & S_IFCHR) == 0)) {
3473ccb1966SGarrett D'Amore 			warn(_("Device present: %s\n"), d->mixer.devnode);
3483ccb1966SGarrett D'Amore 			free_device(d);
3493ccb1966SGarrett D'Amore 			continue;
3503ccb1966SGarrett D'Amore 		}
3513ccb1966SGarrett D'Amore 		d->devt = makedev(major(sbuf.st_rdev),
3523ccb1966SGarrett D'Amore 		    minor(sbuf.st_rdev) & ~(AUDIO_MN_TYPE_MASK));
3533ccb1966SGarrett D'Amore 
3543ccb1966SGarrett D'Amore 		if ((rv = get_device_info(d)) != 0) {
3553ccb1966SGarrett D'Amore 			free_device(d);
3563ccb1966SGarrett D'Amore 			goto OUT;
3573ccb1966SGarrett D'Amore 		}
3583ccb1966SGarrett D'Amore 	}
3593ccb1966SGarrett D'Amore 
3603ccb1966SGarrett D'Amore 	rv = 0;
3613ccb1966SGarrett D'Amore 
3623ccb1966SGarrett D'Amore OUT:
3633ccb1966SGarrett D'Amore 	if (fd >= 0)
3643ccb1966SGarrett D'Amore 		(void) close(fd);
3653ccb1966SGarrett D'Amore 	return (rv);
3663ccb1966SGarrett D'Amore }
3673ccb1966SGarrett D'Amore 
3683ccb1966SGarrett D'Amore 
3693ccb1966SGarrett D'Amore static int
ctype_valid(int type)3703ccb1966SGarrett D'Amore ctype_valid(int type)
3713ccb1966SGarrett D'Amore {
3723ccb1966SGarrett D'Amore 	switch (type) {
3733ccb1966SGarrett D'Amore 	case MIXT_ONOFF:
3743ccb1966SGarrett D'Amore 	case MIXT_ENUM:
3753ccb1966SGarrett D'Amore 	case MIXT_MONOSLIDER:
3763ccb1966SGarrett D'Amore 	case MIXT_STEREOSLIDER:
3773ccb1966SGarrett D'Amore 		return (1);
3783ccb1966SGarrett D'Amore 	default:
3793ccb1966SGarrett D'Amore 		return (0);
3803ccb1966SGarrett D'Amore 	}
3813ccb1966SGarrett D'Amore }
3823ccb1966SGarrett D'Amore 
3833ccb1966SGarrett D'Amore 
3843ccb1966SGarrett D'Amore static void
print_control_line(FILE * sfp,col_prt_t * colp,int vopt)3853ccb1966SGarrett D'Amore print_control_line(FILE *sfp, col_prt_t *colp, int vopt)
3863ccb1966SGarrett D'Amore {
3873ccb1966SGarrett D'Amore 	int i;
3883ccb1966SGarrett D'Amore 	size_t *col_prtp;
3893ccb1966SGarrett D'Amore 	int *col_dpyp;
3903ccb1966SGarrett D'Amore 	int col_cnt;
3913ccb1966SGarrett D'Amore 	int col_type;
3923ccb1966SGarrett D'Amore 	int width;
3933ccb1966SGarrett D'Amore 	char *colstr;
3943ccb1966SGarrett D'Amore 	char cbuf[COL_MAX_SZ + 1];
3953ccb1966SGarrett D'Amore 	char line[128];
3963ccb1966SGarrett D'Amore 	char *colsep =  " ";
3973ccb1966SGarrett D'Amore 
3983ccb1966SGarrett D'Amore 	if (sfp != NULL) {
3993ccb1966SGarrett D'Amore 		col_prtp = col_dpy_prt_tofile;
4003ccb1966SGarrett D'Amore 		col_dpyp = col_dpy_tofile;
4013ccb1966SGarrett D'Amore 		col_cnt = col_dpy_tofile_len;
4023ccb1966SGarrett D'Amore 	} else if (vopt) {
4033ccb1966SGarrett D'Amore 		col_prtp = col_dpy_prt_vopt;
4043ccb1966SGarrett D'Amore 		col_dpyp = col_dpy_vopt;
4053ccb1966SGarrett D'Amore 		col_cnt = col_dpy_vopt_len;
4063ccb1966SGarrett D'Amore 	} else {
4073ccb1966SGarrett D'Amore 		col_prtp = col_dpy_prt;
4083ccb1966SGarrett D'Amore 		col_dpyp = col_dpy;
4093ccb1966SGarrett D'Amore 		col_cnt = col_dpy_len;
4103ccb1966SGarrett D'Amore 	}
4113ccb1966SGarrett D'Amore 
4123ccb1966SGarrett D'Amore 	line[0] = '\0';
4133ccb1966SGarrett D'Amore 
4143ccb1966SGarrett D'Amore 	for (i = 0; i < col_cnt; i++) {
4153ccb1966SGarrett D'Amore 		col_type = col_dpyp[i];
4163ccb1966SGarrett D'Amore 		width = col_sz[col_type];
4173ccb1966SGarrett D'Amore 		colstr = *(char **)(((size_t)colp) + col_prtp[i]);
4183ccb1966SGarrett D'Amore 
4193ccb1966SGarrett D'Amore 		(void) snprintf(cbuf, sizeof (cbuf), "%- *s",
4203ccb1966SGarrett D'Amore 		    width > 0 ? width : 1,
4213ccb1966SGarrett D'Amore 		    (colstr == NULL) ? "" : colstr);
4223ccb1966SGarrett D'Amore 
4233ccb1966SGarrett D'Amore 		(void) strlcat(line, cbuf, sizeof (line));
4243ccb1966SGarrett D'Amore 		if (i < col_cnt - 1)
4253ccb1966SGarrett D'Amore 			(void) strlcat(line, colsep, sizeof (line));
4263ccb1966SGarrett D'Amore 	}
4273ccb1966SGarrett D'Amore 
4283ccb1966SGarrett D'Amore 	(void) fprintf(sfp ? sfp : stdout, "%s\n", line);
4293ccb1966SGarrett D'Amore }
4303ccb1966SGarrett D'Amore 
4313ccb1966SGarrett D'Amore 
4323ccb1966SGarrett D'Amore static void
print_header(FILE * sfp,int vopt)4333ccb1966SGarrett D'Amore print_header(FILE *sfp, int vopt)
4343ccb1966SGarrett D'Amore {
4353ccb1966SGarrett D'Amore 	col_prt_t col;
4363ccb1966SGarrett D'Amore 
4373ccb1966SGarrett D'Amore 	if (sfp) {
4383ccb1966SGarrett D'Amore 		col.col_nm = _("#CONTROL");
4393ccb1966SGarrett D'Amore 		col.col_val = _("VALUE");
4403ccb1966SGarrett D'Amore 	} else {
4413ccb1966SGarrett D'Amore 		col.col_dv = _("DEVICE");
4423ccb1966SGarrett D'Amore 		col.col_nm = _("CONTROL");
4433ccb1966SGarrett D'Amore 		col.col_val = _("VALUE");
4443ccb1966SGarrett D'Amore 		col.col_sel = _("POSSIBLE");
4453ccb1966SGarrett D'Amore 	}
4463ccb1966SGarrett D'Amore 	print_control_line(sfp, &col, vopt);
4473ccb1966SGarrett D'Amore }
4483ccb1966SGarrett D'Amore 
4493ccb1966SGarrett D'Amore 
4503ccb1966SGarrett D'Amore static int
print_control(FILE * sfp,device_t * d,cinfo_t * cinfop,int vopt)4513ccb1966SGarrett D'Amore print_control(FILE *sfp, device_t *d, cinfo_t *cinfop, int vopt)
4523ccb1966SGarrett D'Amore {
4533ccb1966SGarrett D'Amore 	int mfd = d->mfd;
4543ccb1966SGarrett D'Amore 	char *devnm = d->card.shortname;
4553ccb1966SGarrett D'Amore 	oss_mixer_value cval;
4563ccb1966SGarrett D'Amore 	char *str;
4573ccb1966SGarrett D'Amore 	int i;
4583ccb1966SGarrett D'Amore 	int idx = -1;
4593ccb1966SGarrett D'Amore 	int rv = -1;
4603ccb1966SGarrett D'Amore 	char valbuf[COL_VAL_SZ + 1];
4613ccb1966SGarrett D'Amore 	char selbuf[COL_SEL_SZ + 1];
4623ccb1966SGarrett D'Amore 	col_prt_t col;
4633ccb1966SGarrett D'Amore 
4643ccb1966SGarrett D'Amore 	cval.dev = -1;
4653ccb1966SGarrett D'Amore 	cval.ctrl = cinfop->ci.ctrl;
4663ccb1966SGarrett D'Amore 
4673ccb1966SGarrett D'Amore 	if (ctype_valid(cinfop->ci.type)) {
4683ccb1966SGarrett D'Amore 		if (ioctl(mfd, SNDCTL_MIX_READ, &cval) < 0) {
4693ccb1966SGarrett D'Amore 			rv = errno;
4703ccb1966SGarrett D'Amore 			perror(_("Error reading control\n"));
4713ccb1966SGarrett D'Amore 			return (rv);
4723ccb1966SGarrett D'Amore 		}
4733ccb1966SGarrett D'Amore 	} else {
4743ccb1966SGarrett D'Amore 		return (0);
4753ccb1966SGarrett D'Amore 	}
4763ccb1966SGarrett D'Amore 
4773ccb1966SGarrett D'Amore 	/*
4783ccb1966SGarrett D'Amore 	 * convert the control value into a string
4793ccb1966SGarrett D'Amore 	 */
4803ccb1966SGarrett D'Amore 	switch (cinfop->ci.type) {
4813ccb1966SGarrett D'Amore 	case MIXT_ONOFF:
4823ccb1966SGarrett D'Amore 		(void) snprintf(valbuf, sizeof (valbuf), "%s",
4833ccb1966SGarrett D'Amore 		    cval.value ? _("on") : _("off"));
4843ccb1966SGarrett D'Amore 		break;
4853ccb1966SGarrett D'Amore 
4863ccb1966SGarrett D'Amore 	case MIXT_MONOSLIDER:
4873ccb1966SGarrett D'Amore 		(void) snprintf(valbuf, sizeof (valbuf), "%d",
4883ccb1966SGarrett D'Amore 		    cval.value & 0xff);
4893ccb1966SGarrett D'Amore 		break;
4903ccb1966SGarrett D'Amore 
4913ccb1966SGarrett D'Amore 	case MIXT_STEREOSLIDER:
4923ccb1966SGarrett D'Amore 		(void) snprintf(valbuf, sizeof (valbuf), "%d:%d",
4933ccb1966SGarrett D'Amore 		    (int)AUDIO_CTRL_STEREO_LEFT(cval.value),
4943ccb1966SGarrett D'Amore 		    (int)AUDIO_CTRL_STEREO_RIGHT(cval.value));
4953ccb1966SGarrett D'Amore 		break;
4963ccb1966SGarrett D'Amore 
4973ccb1966SGarrett D'Amore 	case MIXT_ENUM:
4983ccb1966SGarrett D'Amore 		str = get_enum_str(cinfop, cval.value);
4993ccb1966SGarrett D'Amore 		if (str == NULL) {
5003ccb1966SGarrett D'Amore 			warn(_("Bad enum index %d for control '%s'\n"),
5013ccb1966SGarrett D'Amore 			    cval.value, cinfop->ci.extname);
5023ccb1966SGarrett D'Amore 			return (EINVAL);
5033ccb1966SGarrett D'Amore 		}
5043ccb1966SGarrett D'Amore 
5053ccb1966SGarrett D'Amore 		(void) snprintf(valbuf, sizeof (valbuf), "%s", str);
5063ccb1966SGarrett D'Amore 		break;
5073ccb1966SGarrett D'Amore 
5083ccb1966SGarrett D'Amore 	default:
5093ccb1966SGarrett D'Amore 		return (0);
5103ccb1966SGarrett D'Amore 	}
5113ccb1966SGarrett D'Amore 
5123ccb1966SGarrett D'Amore 	/*
5133ccb1966SGarrett D'Amore 	 * possible control values (range/selection)
5143ccb1966SGarrett D'Amore 	 */
5153ccb1966SGarrett D'Amore 	switch (cinfop->ci.type) {
5163ccb1966SGarrett D'Amore 	case MIXT_ONOFF:
5173ccb1966SGarrett D'Amore 		(void) snprintf(selbuf, sizeof (selbuf), _("on,off"));
5183ccb1966SGarrett D'Amore 		break;
5193ccb1966SGarrett D'Amore 
5203ccb1966SGarrett D'Amore 	case MIXT_MONOSLIDER:
5213ccb1966SGarrett D'Amore 		(void) snprintf(selbuf, sizeof (selbuf), "%d-%d",
5223ccb1966SGarrett D'Amore 		    cinfop->ci.minvalue, cinfop->ci.maxvalue);
5233ccb1966SGarrett D'Amore 		break;
5243ccb1966SGarrett D'Amore 	case MIXT_STEREOSLIDER:
5253ccb1966SGarrett D'Amore 		(void) snprintf(selbuf, sizeof (selbuf), "%d-%d:%d-%d",
5263ccb1966SGarrett D'Amore 		    cinfop->ci.minvalue, cinfop->ci.maxvalue,
5273ccb1966SGarrett D'Amore 		    cinfop->ci.minvalue, cinfop->ci.maxvalue);
5283ccb1966SGarrett D'Amore 		break;
5293ccb1966SGarrett D'Amore 
5303ccb1966SGarrett D'Amore 	case MIXT_ENUM:
5313ccb1966SGarrett D'Amore 		/*
5323ccb1966SGarrett D'Amore 		 * display the first choice on the same line, then display
5333ccb1966SGarrett D'Amore 		 * the rest on multiple lines
5343ccb1966SGarrett D'Amore 		 */
5353ccb1966SGarrett D'Amore 		selbuf[0] = 0;
5363ccb1966SGarrett D'Amore 		for (i = 0; i < cinfop->ci.maxvalue; i++) {
5373ccb1966SGarrett D'Amore 			str = get_enum_str(cinfop, i);
5383ccb1966SGarrett D'Amore 			if (str == NULL)
5393ccb1966SGarrett D'Amore 				continue;
5403ccb1966SGarrett D'Amore 
5413ccb1966SGarrett D'Amore 			if ((strlen(str) + 1 + strlen(selbuf)) >=
5423ccb1966SGarrett D'Amore 			    sizeof (selbuf)) {
5433ccb1966SGarrett D'Amore 				break;
5443ccb1966SGarrett D'Amore 			}
5453ccb1966SGarrett D'Amore 			if (strlen(selbuf)) {
5463ccb1966SGarrett D'Amore 				(void) strlcat(selbuf, ",", sizeof (selbuf));
5473ccb1966SGarrett D'Amore 			}
5483ccb1966SGarrett D'Amore 
5493ccb1966SGarrett D'Amore 			(void) strlcat(selbuf, str, sizeof (selbuf));
5503ccb1966SGarrett D'Amore 		}
5513ccb1966SGarrett D'Amore 		idx = i;
5523ccb1966SGarrett D'Amore 		break;
5533ccb1966SGarrett D'Amore 
5543ccb1966SGarrett D'Amore 	default:
5553ccb1966SGarrett D'Amore 		(void) snprintf(selbuf, sizeof (selbuf), "-");
5563ccb1966SGarrett D'Amore 	}
5573ccb1966SGarrett D'Amore 
5583ccb1966SGarrett D'Amore 	col.col_dv = devnm;
5593ccb1966SGarrett D'Amore 	col.col_nm = strlen(cinfop->ci.extname) ?
5603ccb1966SGarrett D'Amore 	    cinfop->ci.extname : cinfop->ci.id;
5613ccb1966SGarrett D'Amore 	while (strchr(col.col_nm, '_') != NULL) {
5623ccb1966SGarrett D'Amore 		col.col_nm = strchr(col.col_nm, '_') + 1;
5633ccb1966SGarrett D'Amore 	}
5643ccb1966SGarrett D'Amore 	col.col_val = valbuf;
5653ccb1966SGarrett D'Amore 	col.col_sel = selbuf;
5663ccb1966SGarrett D'Amore 	print_control_line(sfp, &col, vopt);
5673ccb1966SGarrett D'Amore 
568*99bddabbSGarrett D'Amore 	/* non-verbose mode prints don't display the enum values */
569*99bddabbSGarrett D'Amore 	if ((!vopt) || (sfp != NULL)) {
570*99bddabbSGarrett D'Amore 		return (0);
571*99bddabbSGarrett D'Amore 	}
572*99bddabbSGarrett D'Amore 
5733ccb1966SGarrett D'Amore 	/* print leftover enum value selections */
574*99bddabbSGarrett D'Amore 	while ((idx >= 0) && (idx < cinfop->ci.maxvalue)) {
5753ccb1966SGarrett D'Amore 		selbuf[0] = 0;
5763ccb1966SGarrett D'Amore 		for (i = idx; i < cinfop->ci.maxvalue; i++) {
5773ccb1966SGarrett D'Amore 			str = get_enum_str(cinfop, i);
5783ccb1966SGarrett D'Amore 			if (str == NULL)
5793ccb1966SGarrett D'Amore 				continue;
5803ccb1966SGarrett D'Amore 
5813ccb1966SGarrett D'Amore 			if ((strlen(str) + 1 + strlen(selbuf)) >=
5823ccb1966SGarrett D'Amore 			    sizeof (selbuf)) {
5833ccb1966SGarrett D'Amore 				break;
5843ccb1966SGarrett D'Amore 			}
5853ccb1966SGarrett D'Amore 			if (strlen(selbuf)) {
5863ccb1966SGarrett D'Amore 				(void) strlcat(selbuf, ",", sizeof (selbuf));
5873ccb1966SGarrett D'Amore 			}
5883ccb1966SGarrett D'Amore 
5893ccb1966SGarrett D'Amore 			(void) strlcat(selbuf, str, sizeof (selbuf));
5903ccb1966SGarrett D'Amore 		}
5913ccb1966SGarrett D'Amore 		idx = i;
5923ccb1966SGarrett D'Amore 		col.col_dv = NULL;
5933ccb1966SGarrett D'Amore 		col.col_nm = NULL;
5943ccb1966SGarrett D'Amore 		col.col_val = NULL;
5953ccb1966SGarrett D'Amore 		col.col_sel = selbuf;
5963ccb1966SGarrett D'Amore 		print_control_line(sfp, &col, vopt);
5973ccb1966SGarrett D'Amore 	}
5983ccb1966SGarrett D'Amore 
5993ccb1966SGarrett D'Amore 	return (0);
6003ccb1966SGarrett D'Amore }
6013ccb1966SGarrett D'Amore 
6023ccb1966SGarrett D'Amore 
6033ccb1966SGarrett D'Amore static int
set_device_control(device_t * d,cinfo_t * cinfop,char * wstr,int vopt)6043ccb1966SGarrett D'Amore set_device_control(device_t *d, cinfo_t *cinfop, char *wstr, int vopt)
6053ccb1966SGarrett D'Amore {
6063ccb1966SGarrett D'Amore 	int mfd = d->mfd;
6073ccb1966SGarrett D'Amore 	oss_mixer_value cval;
6083ccb1966SGarrett D'Amore 	int wlen = strlen(wstr);
6093ccb1966SGarrett D'Amore 	int lval, rval;
6103ccb1966SGarrett D'Amore 	char *lstr, *rstr;
6113ccb1966SGarrett D'Amore 	char *str;
6123ccb1966SGarrett D'Amore 	int i;
6133ccb1966SGarrett D'Amore 	int rv = -1;
6143ccb1966SGarrett D'Amore 
6153ccb1966SGarrett D'Amore 	cval.dev = -1;
6163ccb1966SGarrett D'Amore 	cval.ctrl = cinfop->ci.ctrl;
6173ccb1966SGarrett D'Amore 	cval.value = 0;
6183ccb1966SGarrett D'Amore 
6193ccb1966SGarrett D'Amore 	switch (cinfop->ci.type) {
6203ccb1966SGarrett D'Amore 	case MIXT_ONOFF:
6213ccb1966SGarrett D'Amore 		cval.value = (strncmp(_("on"), wstr, wlen) == 0) ? 1 : 0;
6223ccb1966SGarrett D'Amore 		break;
6233ccb1966SGarrett D'Amore 
6243ccb1966SGarrett D'Amore 	case MIXT_MONOSLIDER:
6253ccb1966SGarrett D'Amore 		cval.value = atoi(wstr);
6263ccb1966SGarrett D'Amore 		break;
6273ccb1966SGarrett D'Amore 
6283ccb1966SGarrett D'Amore 	case MIXT_STEREOSLIDER:
6293ccb1966SGarrett D'Amore 		lstr = wstr;
6303ccb1966SGarrett D'Amore 		rstr = strchr(wstr, ':');
6313ccb1966SGarrett D'Amore 		if (rstr != NULL) {
6323ccb1966SGarrett D'Amore 			*rstr = '\0';
6333ccb1966SGarrett D'Amore 			rstr++;
6343ccb1966SGarrett D'Amore 
6353ccb1966SGarrett D'Amore 			rval = atoi(rstr);
6363ccb1966SGarrett D'Amore 			lval = atoi(lstr);
6373ccb1966SGarrett D'Amore 
6383ccb1966SGarrett D'Amore 			rstr--;
6393ccb1966SGarrett D'Amore 			*rstr = ':';
6403ccb1966SGarrett D'Amore 		} else {
6413ccb1966SGarrett D'Amore 			lval = atoi(lstr);
6423ccb1966SGarrett D'Amore 			rval = lval;
6433ccb1966SGarrett D'Amore 		}
6443ccb1966SGarrett D'Amore 
6453ccb1966SGarrett D'Amore 		cval.value = AUDIO_CTRL_STEREO_VAL(lval, rval);
6463ccb1966SGarrett D'Amore 		break;
6473ccb1966SGarrett D'Amore 
6483ccb1966SGarrett D'Amore 	case MIXT_ENUM:
6493ccb1966SGarrett D'Amore 		for (i = 0; i < cinfop->ci.maxvalue; i++) {
6503ccb1966SGarrett D'Amore 			str = get_enum_str(cinfop, i);
6513ccb1966SGarrett D'Amore 			if (str == NULL)
6523ccb1966SGarrett D'Amore 				continue;
6533ccb1966SGarrett D'Amore 
6543ccb1966SGarrett D'Amore 			if (strncmp(wstr, str, wlen) == 0) {
6553ccb1966SGarrett D'Amore 				cval.value = i;
6563ccb1966SGarrett D'Amore 				break;
6573ccb1966SGarrett D'Amore 			}
6583ccb1966SGarrett D'Amore 		}
6593ccb1966SGarrett D'Amore 
6603ccb1966SGarrett D'Amore 		if (i >= cinfop->ci.maxvalue) {
6613ccb1966SGarrett D'Amore 			warn(_("Invalid enumeration value\n"));
6623ccb1966SGarrett D'Amore 			return (EINVAL);
6633ccb1966SGarrett D'Amore 		}
6643ccb1966SGarrett D'Amore 		break;
6653ccb1966SGarrett D'Amore 
6663ccb1966SGarrett D'Amore 	default:
6673ccb1966SGarrett D'Amore 		warn(_("Unsupported control type: %d\n"), cinfop->ci.type);
6683ccb1966SGarrett D'Amore 		return (EINVAL);
6693ccb1966SGarrett D'Amore 	}
6703ccb1966SGarrett D'Amore 
6713ccb1966SGarrett D'Amore 	if (vopt) {
6723ccb1966SGarrett D'Amore 		msg(_("%s: '%s' set to '%s'\n"), d->card.shortname,
6733ccb1966SGarrett D'Amore 		    cinfop->ci.extname, wstr);
6743ccb1966SGarrett D'Amore 	}
6753ccb1966SGarrett D'Amore 
6763ccb1966SGarrett D'Amore 	if (ioctl(mfd, SNDCTL_MIX_WRITE, &cval) < 0) {
6773ccb1966SGarrett D'Amore 		rv = errno;
6783ccb1966SGarrett D'Amore 		perror(_("Error writing control"));
6793ccb1966SGarrett D'Amore 		return (rv);
6803ccb1966SGarrett D'Amore 	}
6813ccb1966SGarrett D'Amore 
6823ccb1966SGarrett D'Amore 	rv = 0;
6833ccb1966SGarrett D'Amore 	return (rv);
6843ccb1966SGarrett D'Amore }
6853ccb1966SGarrett D'Amore 
6863ccb1966SGarrett D'Amore 
6873ccb1966SGarrett D'Amore static void
help(void)6883ccb1966SGarrett D'Amore help(void)
6893ccb1966SGarrett D'Amore {
6903ccb1966SGarrett D'Amore #define	HELP_STR	_(						\
6913ccb1966SGarrett D'Amore "audioctl list-devices\n"						\
6923ccb1966SGarrett D'Amore "	list all audio devices\n"					\
6933ccb1966SGarrett D'Amore "\n"									\
6943ccb1966SGarrett D'Amore "audioctl show-device [ -v ] [ -d <device> ]\n"				\
6953ccb1966SGarrett D'Amore "	display information about an audio device\n"			\
6963ccb1966SGarrett D'Amore "\n"									\
6973ccb1966SGarrett D'Amore "audioctl show-control [ -v ] [ -d <device> ] [ <control> ... ]\n"	\
6983ccb1966SGarrett D'Amore "	get the value of a specific control (all if not specified)\n"	\
6993ccb1966SGarrett D'Amore "\n"									\
7003ccb1966SGarrett D'Amore "audioctl set-control [ -v ] [ -d <device> ] <control> <value>\n"	\
7013ccb1966SGarrett D'Amore "	set the value of a specific control\n"				\
7023ccb1966SGarrett D'Amore "\n"									\
7033ccb1966SGarrett D'Amore "audioctl save-controls [ -d <device> ] [ -f ] <file>\n"		\
7043ccb1966SGarrett D'Amore "	save all control settings for the device to a file\n"		\
7053ccb1966SGarrett D'Amore "\n"									\
7063ccb1966SGarrett D'Amore "audioctl load-controls [ -d <device> ] <file>\n"			\
7073ccb1966SGarrett D'Amore "	restore previously saved control settings to device\n"		\
7083ccb1966SGarrett D'Amore "\n"									\
7093ccb1966SGarrett D'Amore "audioctl help\n"							\
7103ccb1966SGarrett D'Amore "	show this message.\n")
7113ccb1966SGarrett D'Amore 
7123ccb1966SGarrett D'Amore 	(void) fprintf(stderr, HELP_STR);
7133ccb1966SGarrett D'Amore }
7143ccb1966SGarrett D'Amore 
7153ccb1966SGarrett D'Amore dev_t
device_devt(char * name)7163ccb1966SGarrett D'Amore device_devt(char *name)
7173ccb1966SGarrett D'Amore {
7183ccb1966SGarrett D'Amore 	struct stat	sbuf;
7193ccb1966SGarrett D'Amore 
7203ccb1966SGarrett D'Amore 	if ((stat(name, &sbuf) != 0) ||
7213ccb1966SGarrett D'Amore 	    ((sbuf.st_mode & S_IFCHR) == 0)) {
7223ccb1966SGarrett D'Amore 		/* Not a device node! */
7233ccb1966SGarrett D'Amore 		return (0);
7243ccb1966SGarrett D'Amore 	}
7253ccb1966SGarrett D'Amore 
7263ccb1966SGarrett D'Amore 	return (makedev(major(sbuf.st_rdev),
7273ccb1966SGarrett D'Amore 	    minor(sbuf.st_rdev) & ~(AUDIO_MN_TYPE_MASK)));
7283ccb1966SGarrett D'Amore }
7293ccb1966SGarrett D'Amore 
7303ccb1966SGarrett D'Amore static device_t *
find_device(char * name)7313ccb1966SGarrett D'Amore find_device(char *name)
7323ccb1966SGarrett D'Amore {
7333ccb1966SGarrett D'Amore 	dev_t		devt;
7343ccb1966SGarrett D'Amore 	device_t	*d;
7353ccb1966SGarrett D'Amore 
7363ccb1966SGarrett D'Amore 	/*
7373ccb1966SGarrett D'Amore 	 * User may have specified:
7383ccb1966SGarrett D'Amore 	 *
7393ccb1966SGarrett D'Amore 	 * /dev/dsp[<num>]
7403ccb1966SGarrett D'Amore 	 * /dev/mixer[<num>]
7413ccb1966SGarrett D'Amore 	 * /dev/audio[<num>9]
7423ccb1966SGarrett D'Amore 	 * /dev/audioctl[<num>]
7433ccb1966SGarrett D'Amore 	 * /dev/sound/<num>{,ctl,dsp,mixer}
7443ccb1966SGarrett D'Amore 	 * /dev/sound/<driver>:<num>{,ctl,dsp,mixer}
7453ccb1966SGarrett D'Amore 	 *
7463ccb1966SGarrett D'Amore 	 * We can canonicalize these by looking at the dev_t though.
7473ccb1966SGarrett D'Amore 	 */
7483ccb1966SGarrett D'Amore 
749aef5f291SGarrett D'Amore 	if (load_devices() != 0) {
750aef5f291SGarrett D'Amore 		return (NULL);
751aef5f291SGarrett D'Amore 	}
752aef5f291SGarrett D'Amore 
7533ccb1966SGarrett D'Amore 	if (name == NULL)
7543ccb1966SGarrett D'Amore 		name = getenv("AUDIODEV");
7553ccb1966SGarrett D'Amore 
7563ccb1966SGarrett D'Amore 	if ((name == NULL) ||
7573ccb1966SGarrett D'Amore 	    (strcmp(name, "/dev/mixer") == 0)) {
7583ccb1966SGarrett D'Amore 		/* /dev/mixer node doesn't point to real hw */
7593ccb1966SGarrett D'Amore 		name = "/dev/dsp";
7603ccb1966SGarrett D'Amore 	}
7613ccb1966SGarrett D'Amore 
7623ccb1966SGarrett D'Amore 	if (*name == '/') {
7633ccb1966SGarrett D'Amore 		/* if we have a full path, convert to the devt */
7643ccb1966SGarrett D'Amore 		if ((devt = device_devt(name)) == 0) {
7653ccb1966SGarrett D'Amore 			warn(_("No such audio device.\n"));
7663ccb1966SGarrett D'Amore 			return (NULL);
7673ccb1966SGarrett D'Amore 		}
7683ccb1966SGarrett D'Amore 		name = NULL;
7693ccb1966SGarrett D'Amore 	}
7703ccb1966SGarrett D'Amore 
7713ccb1966SGarrett D'Amore 	for (d = devices; d != NULL; d = d->nextp) {
7723ccb1966SGarrett D'Amore 		oss_card_info *card = &d->card;
7733ccb1966SGarrett D'Amore 
7743ccb1966SGarrett D'Amore 		if ((name) && (strcmp(name, card->shortname) == 0)) {
7753ccb1966SGarrett D'Amore 			return (d);
7763ccb1966SGarrett D'Amore 		}
7773ccb1966SGarrett D'Amore 		if (devt == d->devt) {
7783ccb1966SGarrett D'Amore 			return (d);
7793ccb1966SGarrett D'Amore 		}
7803ccb1966SGarrett D'Amore 	}
7813ccb1966SGarrett D'Amore 
7823ccb1966SGarrett D'Amore 	warn(_("No such audio device.\n"));
7833ccb1966SGarrett D'Amore 	return (NULL);
7843ccb1966SGarrett D'Amore }
7853ccb1966SGarrett D'Amore 
7863ccb1966SGarrett D'Amore int
do_list_devices(int argc,char ** argv)7873ccb1966SGarrett D'Amore do_list_devices(int argc, char **argv)
7883ccb1966SGarrett D'Amore {
7893ccb1966SGarrett D'Amore 	int		optc;
7903ccb1966SGarrett D'Amore 	int		verbose = 0;
7913ccb1966SGarrett D'Amore 	device_t	*d;
7923ccb1966SGarrett D'Amore 
7933ccb1966SGarrett D'Amore 	while ((optc = getopt(argc, argv, "v")) != EOF) {
7943ccb1966SGarrett D'Amore 		switch (optc) {
7953ccb1966SGarrett D'Amore 		case 'v':
7963ccb1966SGarrett D'Amore 			verbose++;
7973ccb1966SGarrett D'Amore 			break;
7983ccb1966SGarrett D'Amore 		default:
7993ccb1966SGarrett D'Amore 			help();
8003ccb1966SGarrett D'Amore 			return (-1);
8013ccb1966SGarrett D'Amore 		}
8023ccb1966SGarrett D'Amore 	}
8033ccb1966SGarrett D'Amore 	argc -= optind;
8043ccb1966SGarrett D'Amore 	argv += optind;
8053ccb1966SGarrett D'Amore 	if (argc != 0) {
8063ccb1966SGarrett D'Amore 		help();
8073ccb1966SGarrett D'Amore 		return (-1);
8083ccb1966SGarrett D'Amore 	}
8093ccb1966SGarrett D'Amore 
810aef5f291SGarrett D'Amore 	if (load_devices() != 0) {
811aef5f291SGarrett D'Amore 		return (-1);
812aef5f291SGarrett D'Amore 	}
813aef5f291SGarrett D'Amore 
8143ccb1966SGarrett D'Amore 	for (d = devices; d != NULL; d = d->nextp) {
8153ccb1966SGarrett D'Amore 
8163ccb1966SGarrett D'Amore 		if ((d->mixer.enabled == 0) && (!verbose))
8173ccb1966SGarrett D'Amore 			continue;
8183ccb1966SGarrett D'Amore 
8193ccb1966SGarrett D'Amore 		if (verbose) {
8203ccb1966SGarrett D'Amore 			msg(_("%s (%s)\n"), d->card.shortname,
8213ccb1966SGarrett D'Amore 			    d->mixer.devnode);
8223ccb1966SGarrett D'Amore 		} else {
8233ccb1966SGarrett D'Amore 			msg(_("%s\n"), d->card.shortname);
8243ccb1966SGarrett D'Amore 		}
8253ccb1966SGarrett D'Amore 	}
8263ccb1966SGarrett D'Amore 
8273ccb1966SGarrett D'Amore 	return (0);
8283ccb1966SGarrett D'Amore }
8293ccb1966SGarrett D'Amore 
8303ccb1966SGarrett D'Amore int
do_show_device(int argc,char ** argv)8313ccb1966SGarrett D'Amore do_show_device(int argc, char **argv)
8323ccb1966SGarrett D'Amore {
8333ccb1966SGarrett D'Amore 	int		optc;
8343ccb1966SGarrett D'Amore 	char		*devname = NULL;
8353ccb1966SGarrett D'Amore 	device_t	*d;
8363ccb1966SGarrett D'Amore 
8373ccb1966SGarrett D'Amore 	while ((optc = getopt(argc, argv, "d:v")) != EOF) {
8383ccb1966SGarrett D'Amore 		switch (optc) {
8393ccb1966SGarrett D'Amore 		case 'd':
8403ccb1966SGarrett D'Amore 			devname = optarg;
8413ccb1966SGarrett D'Amore 			break;
8423ccb1966SGarrett D'Amore 		case 'v':
8433ccb1966SGarrett D'Amore 			break;
8443ccb1966SGarrett D'Amore 		default:
8453ccb1966SGarrett D'Amore 			help();
8463ccb1966SGarrett D'Amore 			return (-1);
8473ccb1966SGarrett D'Amore 		}
8483ccb1966SGarrett D'Amore 	}
8493ccb1966SGarrett D'Amore 	argc -= optind;
8503ccb1966SGarrett D'Amore 	argv += optind;
8513ccb1966SGarrett D'Amore 	if (argc != 0) {
8523ccb1966SGarrett D'Amore 		help();
8533ccb1966SGarrett D'Amore 		return (-1);
8543ccb1966SGarrett D'Amore 	}
8553ccb1966SGarrett D'Amore 
8563ccb1966SGarrett D'Amore 	if ((d = find_device(devname)) == NULL) {
8573ccb1966SGarrett D'Amore 		return (ENODEV);
8583ccb1966SGarrett D'Amore 	}
8593ccb1966SGarrett D'Amore 
8603ccb1966SGarrett D'Amore 	msg(_("Device: %s\n"), d->mixer.devnode);
8613ccb1966SGarrett D'Amore 	msg(_("  Name    = %s\n"), d->card.shortname);
8623ccb1966SGarrett D'Amore 	msg(_("  Config  = %s\n"), d->card.longname);
8633ccb1966SGarrett D'Amore 
8643ccb1966SGarrett D'Amore 	if (strlen(d->card.hw_info)) {
8653ccb1966SGarrett D'Amore 		msg(_("  HW Info = %s"), d->card.hw_info);
8663ccb1966SGarrett D'Amore 	}
8673ccb1966SGarrett D'Amore 
8683ccb1966SGarrett D'Amore 	return (0);
8693ccb1966SGarrett D'Amore }
8703ccb1966SGarrett D'Amore 
8713ccb1966SGarrett D'Amore int
do_show_control(int argc,char ** argv)8723ccb1966SGarrett D'Amore do_show_control(int argc, char **argv)
8733ccb1966SGarrett D'Amore {
8743ccb1966SGarrett D'Amore 	int		optc;
8753ccb1966SGarrett D'Amore 	int		rval = 0;
8763ccb1966SGarrett D'Amore 	int		verbose = 0;
8773ccb1966SGarrett D'Amore 	device_t	*d;
8783ccb1966SGarrett D'Amore 	char		*devname = NULL;
8793ccb1966SGarrett D'Amore 	int		i;
8803ccb1966SGarrett D'Amore 	int		j;
8813ccb1966SGarrett D'Amore 	int		rv;
8823ccb1966SGarrett D'Amore 	char		*n;
8833ccb1966SGarrett D'Amore 	cinfo_t		*cinfop;
8843ccb1966SGarrett D'Amore 
8853ccb1966SGarrett D'Amore 	while ((optc = getopt(argc, argv, "d:v")) != EOF) {
8863ccb1966SGarrett D'Amore 		switch (optc) {
8873ccb1966SGarrett D'Amore 		case 'd':
8883ccb1966SGarrett D'Amore 			devname = optarg;
8893ccb1966SGarrett D'Amore 			break;
8903ccb1966SGarrett D'Amore 		case 'v':
8913ccb1966SGarrett D'Amore 			verbose++;
8923ccb1966SGarrett D'Amore 			break;
8933ccb1966SGarrett D'Amore 		default:
8943ccb1966SGarrett D'Amore 			help();
8953ccb1966SGarrett D'Amore 			return (-1);
8963ccb1966SGarrett D'Amore 		}
8973ccb1966SGarrett D'Amore 	}
8983ccb1966SGarrett D'Amore 	argc -= optind;
8993ccb1966SGarrett D'Amore 	argv += optind;
9003ccb1966SGarrett D'Amore 
9013ccb1966SGarrett D'Amore 	if ((d = find_device(devname)) == NULL) {
9023ccb1966SGarrett D'Amore 		return (ENODEV);
9033ccb1966SGarrett D'Amore 	}
9043ccb1966SGarrett D'Amore 
9053ccb1966SGarrett D'Amore 	print_header(NULL, verbose);
9063ccb1966SGarrett D'Amore 	if (argc == 0) {
9073ccb1966SGarrett D'Amore 		/* do them all! */
9083ccb1966SGarrett D'Amore 		for (i = 0; i < d->cmax; i++) {
9093ccb1966SGarrett D'Amore 
9103ccb1966SGarrett D'Amore 			cinfop = &d->controls[i];
9113ccb1966SGarrett D'Amore 			rv = print_control(NULL, d, cinfop, verbose);
9123ccb1966SGarrett D'Amore 			rval = rval ? rval : rv;
9133ccb1966SGarrett D'Amore 		}
9143ccb1966SGarrett D'Amore 		return (rval);
9153ccb1966SGarrett D'Amore 	}
9163ccb1966SGarrett D'Amore 
9173ccb1966SGarrett D'Amore 	for (i = 0; i < argc; i++) {
9183ccb1966SGarrett D'Amore 		for (j = 0; j < d->cmax; j++) {
9193ccb1966SGarrett D'Amore 			cinfop = &d->controls[j];
9203ccb1966SGarrett D'Amore 			n = strrchr(cinfop->ci.extname, '_');
9213ccb1966SGarrett D'Amore 			n = n ? n + 1 : cinfop->ci.extname;
9223ccb1966SGarrett D'Amore 			if (strcmp(argv[i], n) == 0) {
9233ccb1966SGarrett D'Amore 				rv = print_control(NULL, d, cinfop, verbose);
9243ccb1966SGarrett D'Amore 				rval = rval ? rval : rv;
9253ccb1966SGarrett D'Amore 				break;
9263ccb1966SGarrett D'Amore 			}
9273ccb1966SGarrett D'Amore 		}
9283ccb1966SGarrett D'Amore 		/* Didn't find requested control */
9293ccb1966SGarrett D'Amore 		if (j == d->cmax) {
9303ccb1966SGarrett D'Amore 			warn(_("No such control: %s\n"), argv[i]);
9313ccb1966SGarrett D'Amore 			rval = rval ? rval : ENODEV;
9323ccb1966SGarrett D'Amore 		}
9333ccb1966SGarrett D'Amore 	}
9343ccb1966SGarrett D'Amore 
9353ccb1966SGarrett D'Amore 	return (rval);
9363ccb1966SGarrett D'Amore }
9373ccb1966SGarrett D'Amore 
9383ccb1966SGarrett D'Amore int
do_set_control(int argc,char ** argv)9393ccb1966SGarrett D'Amore do_set_control(int argc, char **argv)
9403ccb1966SGarrett D'Amore {
9413ccb1966SGarrett D'Amore 	int		optc;
9423ccb1966SGarrett D'Amore 	int		rval = 0;
9433ccb1966SGarrett D'Amore 	int		verbose = 0;
9443ccb1966SGarrett D'Amore 	device_t	*d;
9453ccb1966SGarrett D'Amore 	char		*devname = NULL;
9463ccb1966SGarrett D'Amore 	char		*cname;
9473ccb1966SGarrett D'Amore 	char		*value;
9483ccb1966SGarrett D'Amore 	int		i;
9493ccb1966SGarrett D'Amore 	int		found;
9503ccb1966SGarrett D'Amore 	int		rv;
9513ccb1966SGarrett D'Amore 	char		*n;
9523ccb1966SGarrett D'Amore 	cinfo_t		*cinfop;
9533ccb1966SGarrett D'Amore 
9543ccb1966SGarrett D'Amore 	while ((optc = getopt(argc, argv, "d:v")) != EOF) {
9553ccb1966SGarrett D'Amore 		switch (optc) {
9563ccb1966SGarrett D'Amore 		case 'd':
9573ccb1966SGarrett D'Amore 			devname = optarg;
9583ccb1966SGarrett D'Amore 			break;
9593ccb1966SGarrett D'Amore 		case 'v':
9603ccb1966SGarrett D'Amore 			verbose = 1;
9613ccb1966SGarrett D'Amore 			break;
9623ccb1966SGarrett D'Amore 		default:
9633ccb1966SGarrett D'Amore 			help();
9643ccb1966SGarrett D'Amore 			return (-1);
9653ccb1966SGarrett D'Amore 		}
9663ccb1966SGarrett D'Amore 	}
9673ccb1966SGarrett D'Amore 	argc -= optind;
9683ccb1966SGarrett D'Amore 	argv += optind;
9693ccb1966SGarrett D'Amore 
9703ccb1966SGarrett D'Amore 	if (argc != 2) {
9713ccb1966SGarrett D'Amore 		help();
9723ccb1966SGarrett D'Amore 		return (-1);
9733ccb1966SGarrett D'Amore 	}
9743ccb1966SGarrett D'Amore 	cname = argv[0];
9753ccb1966SGarrett D'Amore 	value = argv[1];
9763ccb1966SGarrett D'Amore 
9773ccb1966SGarrett D'Amore 	if ((d = find_device(devname)) == NULL) {
9783ccb1966SGarrett D'Amore 		return (ENODEV);
9793ccb1966SGarrett D'Amore 	}
9803ccb1966SGarrett D'Amore 
9813ccb1966SGarrett D'Amore 	for (i = 0, found = 0; i < d->cmax; i++) {
9823ccb1966SGarrett D'Amore 		cinfop = &d->controls[i];
9833ccb1966SGarrett D'Amore 		n = strrchr(cinfop->ci.extname, '_');
9843ccb1966SGarrett D'Amore 		n = n ? n + 1 : cinfop->ci.extname;
9853ccb1966SGarrett D'Amore 		if (strcmp(cname, n) != 0) {
9863ccb1966SGarrett D'Amore 			continue;
9873ccb1966SGarrett D'Amore 		}
9883ccb1966SGarrett D'Amore 		found = 1;
9893ccb1966SGarrett D'Amore 		rv = set_device_control(d, cinfop, value, verbose);
9903ccb1966SGarrett D'Amore 		rval = rval ? rval : rv;
9913ccb1966SGarrett D'Amore 	}
9923ccb1966SGarrett D'Amore 	if (!found) {
9933ccb1966SGarrett D'Amore 		warn(_("No such control: %s\n"), cname);
9943ccb1966SGarrett D'Amore 	}
9953ccb1966SGarrett D'Amore 
9963ccb1966SGarrett D'Amore 	return (rval);
9973ccb1966SGarrett D'Amore }
9983ccb1966SGarrett D'Amore 
9993ccb1966SGarrett D'Amore int
do_save_controls(int argc,char ** argv)10003ccb1966SGarrett D'Amore do_save_controls(int argc, char **argv)
10013ccb1966SGarrett D'Amore {
10023ccb1966SGarrett D'Amore 	int		optc;
10033ccb1966SGarrett D'Amore 	int		rval = 0;
10043ccb1966SGarrett D'Amore 	device_t	*d;
10053ccb1966SGarrett D'Amore 	char		*devname = NULL;
10063ccb1966SGarrett D'Amore 	char		*fname;
10073ccb1966SGarrett D'Amore 	int		i;
10083ccb1966S