1*3ccb1966SGarrett D'Amore /*
2*3ccb1966SGarrett D'Amore  * CDDL HEADER START
3*3ccb1966SGarrett D'Amore  *
4*3ccb1966SGarrett D'Amore  * The contents of this file are subject to the terms of the
5*3ccb1966SGarrett D'Amore  * Common Development and Distribution License (the "License").
6*3ccb1966SGarrett D'Amore  * You may not use this file except in compliance with the License.
7*3ccb1966SGarrett D'Amore  *
8*3ccb1966SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*3ccb1966SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
10*3ccb1966SGarrett D'Amore  * See the License for the specific language governing permissions
11*3ccb1966SGarrett D'Amore  * and limitations under the License.
12*3ccb1966SGarrett D'Amore  *
13*3ccb1966SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
14*3ccb1966SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*3ccb1966SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
16*3ccb1966SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
17*3ccb1966SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
18*3ccb1966SGarrett D'Amore  *
19*3ccb1966SGarrett D'Amore  * CDDL HEADER END
20*3ccb1966SGarrett D'Amore  */
21*3ccb1966SGarrett D'Amore /*
22*3ccb1966SGarrett D'Amore  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23*3ccb1966SGarrett D'Amore  * Use is subject to license terms.
24*3ccb1966SGarrett D'Amore  */
25*3ccb1966SGarrett D'Amore 
26*3ccb1966SGarrett D'Amore #include <stdio.h>
27*3ccb1966SGarrett D'Amore #include <stdlib.h>
28*3ccb1966SGarrett D'Amore #include <errno.h>
29*3ccb1966SGarrett D'Amore #include <string.h>
30*3ccb1966SGarrett D'Amore #include <strings.h>
31*3ccb1966SGarrett D'Amore #include <locale.h>
32*3ccb1966SGarrett D'Amore #include <libintl.h>
33*3ccb1966SGarrett D'Amore #include <stdarg.h>
34*3ccb1966SGarrett D'Amore #include <stddef.h>
35*3ccb1966SGarrett D'Amore #include <sys/types.h>
36*3ccb1966SGarrett D'Amore #include <sys/stat.h>
37*3ccb1966SGarrett D'Amore #include <sys/mkdev.h>
38*3ccb1966SGarrett D'Amore #include <fcntl.h>
39*3ccb1966SGarrett D'Amore #include <unistd.h>
40*3ccb1966SGarrett D'Amore #include <ctype.h>
41*3ccb1966SGarrett D'Amore #include <sys/param.h>
42*3ccb1966SGarrett D'Amore #include <sys/soundcard.h>
43*3ccb1966SGarrett D'Amore 
44*3ccb1966SGarrett D'Amore #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
45*3ccb1966SGarrett D'Amore #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
46*3ccb1966SGarrett D'Amore #endif
47*3ccb1966SGarrett D'Amore 
48*3ccb1966SGarrett D'Amore #define	_(s)	gettext(s)
49*3ccb1966SGarrett D'Amore 
50*3ccb1966SGarrett D'Amore #define	MAXLINE	1024
51*3ccb1966SGarrett D'Amore 
52*3ccb1966SGarrett D'Amore #define	AUDIO_CTRL_STEREO_LEFT(v)	((uint8_t)((v) & 0xff))
53*3ccb1966SGarrett D'Amore #define	AUDIO_CTRL_STEREO_RIGHT(v)	((uint8_t)(((v) >> 8) & 0xff))
54*3ccb1966SGarrett D'Amore #define	AUDIO_CTRL_STEREO_VAL(l, r)	(((l) & 0xff) | (((r) & 0xff) << 8))
55*3ccb1966SGarrett D'Amore 
56*3ccb1966SGarrett D'Amore /*
57*3ccb1966SGarrett D'Amore  * These are borrowed from sys/audio/audio_common.h, where the values
58*3ccb1966SGarrett D'Amore  * are protected by _KERNEL.
59*3ccb1966SGarrett D'Amore  */
60*3ccb1966SGarrett D'Amore #define	AUDIO_MN_TYPE_NBITS	(4)
61*3ccb1966SGarrett D'Amore #define	AUDIO_MN_TYPE_MASK	((1U << AUDIO_MN_TYPE_NBITS) - 1)
62*3ccb1966SGarrett D'Amore #define	AUDIO_MINOR_MIXER	(0)
63*3ccb1966SGarrett D'Amore 
64*3ccb1966SGarrett D'Amore 
65*3ccb1966SGarrett D'Amore /*
66*3ccb1966SGarrett D'Amore  * Column display information
67*3ccb1966SGarrett D'Amore  * All are related to the types enumerated in col_t and any change should be
68*3ccb1966SGarrett D'Amore  * reflected in the corresponding indices and offsets for all the variables
69*3ccb1966SGarrett D'Amore  * accordingly.  Most tweaks to the display can be done by adjusting the
70*3ccb1966SGarrett D'Amore  * values here.
71*3ccb1966SGarrett D'Amore  */
72*3ccb1966SGarrett D'Amore 
73*3ccb1966SGarrett D'Amore /* types of columns displayed */
74*3ccb1966SGarrett D'Amore typedef enum { COL_DV = 0, COL_NM, COL_VAL, COL_SEL} col_t;
75*3ccb1966SGarrett D'Amore 
76*3ccb1966SGarrett D'Amore /* corresponding sizes of columns; does not include trailing null */
77*3ccb1966SGarrett D'Amore #define	COL_DV_SZ	16
78*3ccb1966SGarrett D'Amore #define	COL_NM_SZ	24
79*3ccb1966SGarrett D'Amore #define	COL_VAL_SZ	10
80*3ccb1966SGarrett D'Amore #define	COL_SEL_SZ	20
81*3ccb1966SGarrett D'Amore #define	COL_MAX_SZ	64
82*3ccb1966SGarrett D'Amore 
83*3ccb1966SGarrett D'Amore /* corresponding sizes of columns, indexed by col_t value */
84*3ccb1966SGarrett D'Amore static int col_sz[] = {
85*3ccb1966SGarrett D'Amore 	COL_DV_SZ, COL_NM_SZ, COL_VAL_SZ, COL_SEL_SZ
86*3ccb1966SGarrett D'Amore };
87*3ccb1966SGarrett D'Amore 
88*3ccb1966SGarrett D'Amore /* used by callers of the printing function */
89*3ccb1966SGarrett D'Amore typedef struct col_prt {
90*3ccb1966SGarrett D'Amore 	char *col_dv;
91*3ccb1966SGarrett D'Amore 	char *col_nm;
92*3ccb1966SGarrett D'Amore 	char *col_val;
93*3ccb1966SGarrett D'Amore 	char *col_sel;
94*3ccb1966SGarrett D'Amore } col_prt_t;
95*3ccb1966SGarrett D'Amore 
96*3ccb1966SGarrett D'Amore /* columns displayed in order with vopt = 0 */
97*3ccb1966SGarrett D'Amore static int col_dpy[] = {COL_NM, COL_VAL};
98*3ccb1966SGarrett D'Amore static int col_dpy_len = sizeof (col_dpy) / sizeof (*col_dpy);
99*3ccb1966SGarrett D'Amore 
100*3ccb1966SGarrett D'Amore /* tells the printing function what members to use; follows col_dpy[] */
101*3ccb1966SGarrett D'Amore static size_t col_dpy_prt[] = {
102*3ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_nm),
103*3ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_val),
104*3ccb1966SGarrett D'Amore };
105*3ccb1966SGarrett D'Amore 
106*3ccb1966SGarrett D'Amore /* columns displayed in order with vopt = 1 */
107*3ccb1966SGarrett D'Amore static int col_dpy_vopt[] = { COL_DV, COL_NM, COL_VAL, COL_SEL};
108*3ccb1966SGarrett D'Amore static int col_dpy_vopt_len = sizeof (col_dpy_vopt) / sizeof (*col_dpy_vopt);
109*3ccb1966SGarrett D'Amore 
110*3ccb1966SGarrett D'Amore /* tells the printing function what members to use; follows col_dpy_vopt[] */
111*3ccb1966SGarrett D'Amore static size_t col_dpy_prt_vopt[] = {
112*3ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_dv),
113*3ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_nm),
114*3ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_val),
115*3ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_sel)
116*3ccb1966SGarrett D'Amore };
117*3ccb1966SGarrett D'Amore 
118*3ccb1966SGarrett D'Amore /* columns displayed in order with tofile = 1 */
119*3ccb1966SGarrett D'Amore static int col_dpy_tofile[] = { COL_NM, COL_VAL};
120*3ccb1966SGarrett D'Amore static int col_dpy_tofile_len = sizeof (col_dpy_tofile) /
121*3ccb1966SGarrett D'Amore     sizeof (*col_dpy_tofile);
122*3ccb1966SGarrett D'Amore 
123*3ccb1966SGarrett D'Amore /* tells the printing function what members to use; follows col_dpy_tofile[] */
124*3ccb1966SGarrett D'Amore static size_t col_dpy_prt_tofile[] = {
125*3ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_nm),
126*3ccb1966SGarrett D'Amore 	offsetof(col_prt_t, col_val)
127*3ccb1966SGarrett D'Amore };
128*3ccb1966SGarrett D'Amore 
129*3ccb1966SGarrett D'Amore 
130*3ccb1966SGarrett D'Amore /*
131*3ccb1966SGarrett D'Amore  * mixer and control accounting
132*3ccb1966SGarrett D'Amore  */
133*3ccb1966SGarrett D'Amore 
134*3ccb1966SGarrett D'Amore typedef struct cinfo {
135*3ccb1966SGarrett D'Amore 	oss_mixext ci;
136*3ccb1966SGarrett D'Amore 	oss_mixer_enuminfo *enump;
137*3ccb1966SGarrett D'Amore } cinfo_t;
138*3ccb1966SGarrett D'Amore 
139*3ccb1966SGarrett D'Amore typedef struct device {
140*3ccb1966SGarrett D'Amore 	oss_card_info	card;
141*3ccb1966SGarrett D'Amore 	oss_mixerinfo	mixer;
142*3ccb1966SGarrett D'Amore 
143*3ccb1966SGarrett D'Amore 	int		cmax;
144*3ccb1966SGarrett D'Amore 	cinfo_t		*controls;
145*3ccb1966SGarrett D'Amore 
146*3ccb1966SGarrett D'Amore 	int		mfd;
147*3ccb1966SGarrett D'Amore 	dev_t		devt;
148*3ccb1966SGarrett D'Amore 
149*3ccb1966SGarrett D'Amore 	struct device	*nextp;
150*3ccb1966SGarrett D'Amore } device_t;
151*3ccb1966SGarrett D'Amore 
152*3ccb1966SGarrett D'Amore static device_t	*devices = NULL;
153*3ccb1966SGarrett D'Amore 
154*3ccb1966SGarrett D'Amore /*PRINTFLIKE1*/
155*3ccb1966SGarrett D'Amore static void
156*3ccb1966SGarrett D'Amore msg(char *fmt, ...)
157*3ccb1966SGarrett D'Amore {
158*3ccb1966SGarrett D'Amore 	va_list ap;
159*3ccb1966SGarrett D'Amore 
160*3ccb1966SGarrett D'Amore 	va_start(ap, fmt);
161*3ccb1966SGarrett D'Amore 	(void) vprintf(fmt, ap);
162*3ccb1966SGarrett D'Amore 	va_end(ap);
163*3ccb1966SGarrett D'Amore }
164*3ccb1966SGarrett D'Amore 
165*3ccb1966SGarrett D'Amore /*PRINTFLIKE1*/
166*3ccb1966SGarrett D'Amore static void
167*3ccb1966SGarrett D'Amore warn(char *fmt, ...)
168*3ccb1966SGarrett D'Amore {
169*3ccb1966SGarrett D'Amore 	va_list ap;
170*3ccb1966SGarrett D'Amore 
171*3ccb1966SGarrett D'Amore 	va_start(ap, fmt);
172*3ccb1966SGarrett D'Amore 	(void) vfprintf(stderr, fmt, ap);
173*3ccb1966SGarrett D'Amore 	va_end(ap);
174*3ccb1966SGarrett D'Amore }
175*3ccb1966SGarrett D'Amore 
176*3ccb1966SGarrett D'Amore static void
177*3ccb1966SGarrett D'Amore free_device(device_t *d)
178*3ccb1966SGarrett D'Amore {
179*3ccb1966SGarrett D'Amore 	int		i;
180*3ccb1966SGarrett D'Amore 	device_t	**dpp;
181*3ccb1966SGarrett D'Amore 
182*3ccb1966SGarrett D'Amore 	dpp = &devices;
183*3ccb1966SGarrett D'Amore 	while ((*dpp) && ((*dpp) != d)) {
184*3ccb1966SGarrett D'Amore 		dpp = &((*dpp)->nextp);
185*3ccb1966SGarrett D'Amore 	}
186*3ccb1966SGarrett D'Amore 	if (*dpp) {
187*3ccb1966SGarrett D'Amore 		*dpp = d->nextp;
188*3ccb1966SGarrett D'Amore 	}
189*3ccb1966SGarrett D'Amore 	for (i = 0; i < d->cmax; i++) {
190*3ccb1966SGarrett D'Amore 		if (d->controls[i].enump != NULL)
191*3ccb1966SGarrett D'Amore 			free(d->controls[i].enump);
192*3ccb1966SGarrett D'Amore 	}
193*3ccb1966SGarrett D'Amore 
194*3ccb1966SGarrett D'Amore 	if (d->mfd >= 0)
195*3ccb1966SGarrett D'Amore 		(void) close(d->mfd);
196*3ccb1966SGarrett D'Amore 
197*3ccb1966SGarrett D'Amore 	free(d);
198*3ccb1966SGarrett D'Amore }
199*3ccb1966SGarrett D'Amore 
200*3ccb1966SGarrett D'Amore static void
201*3ccb1966SGarrett D'Amore free_devices(void)
202*3ccb1966SGarrett D'Amore {
203*3ccb1966SGarrett D'Amore 	device_t *d = devices;
204*3ccb1966SGarrett D'Amore 
205*3ccb1966SGarrett D'Amore 	while ((d = devices) != NULL) {
206*3ccb1966SGarrett D'Amore 		free_device(d);
207*3ccb1966SGarrett D'Amore 	}
208*3ccb1966SGarrett D'Amore 
209*3ccb1966SGarrett D'Amore 	devices = NULL;
210*3ccb1966SGarrett D'Amore }
211*3ccb1966SGarrett D'Amore 
212*3ccb1966SGarrett D'Amore 
213*3ccb1966SGarrett D'Amore /*
214*3ccb1966SGarrett D'Amore  * adds to the end of global devices and returns a pointer to the new entry
215*3ccb1966SGarrett D'Amore  */
216*3ccb1966SGarrett D'Amore static device_t *
217*3ccb1966SGarrett D'Amore alloc_device(void)
218*3ccb1966SGarrett D'Amore {
219*3ccb1966SGarrett D'Amore 	device_t *p;
220*3ccb1966SGarrett D'Amore 	device_t *d = calloc(1, sizeof (*d));
221*3ccb1966SGarrett D'Amore 
222*3ccb1966SGarrett D'Amore 	d->card.card = -1;
223*3ccb1966SGarrett D'Amore 	d->mixer.dev = -1;
224*3ccb1966SGarrett D'Amore 	d->mfd = -1;
225*3ccb1966SGarrett D'Amore 
226*3ccb1966SGarrett D'Amore 	if (devices == NULL) {
227*3ccb1966SGarrett D'Amore 		devices = d;
228*3ccb1966SGarrett D'Amore 	} else {
229*3ccb1966SGarrett D'Amore 		for (p = devices; p->nextp != NULL; p = p->nextp) {}
230*3ccb1966SGarrett D'Amore 
231*3ccb1966SGarrett D'Amore 		p->nextp = d;
232*3ccb1966SGarrett D'Amore 	}
233*3ccb1966SGarrett D'Amore 	return (d);
234*3ccb1966SGarrett D'Amore }
235*3ccb1966SGarrett D'Amore 
236*3ccb1966SGarrett D'Amore 
237*3ccb1966SGarrett D'Amore /*
238*3ccb1966SGarrett D'Amore  * cinfop->enump needs to be present
239*3ccb1966SGarrett D'Amore  * idx should be: >= 0 to < cinfop->ci.maxvalue
240*3ccb1966SGarrett D'Amore  */
241*3ccb1966SGarrett D'Amore static char *
242*3ccb1966SGarrett D'Amore get_enum_str(cinfo_t *cinfop, int idx)
243*3ccb1966SGarrett D'Amore {
244*3ccb1966SGarrett D'Amore 	int sz = sizeof (*cinfop->ci.enum_present) * 8;
245*3ccb1966SGarrett D'Amore 
246*3ccb1966SGarrett D'Amore 	if (cinfop->ci.enum_present[idx / sz] & (1 << (idx % sz)))
247*3ccb1966SGarrett D'Amore 		return (cinfop->enump->strings + cinfop->enump->strindex[idx]);
248*3ccb1966SGarrett D'Amore 
249*3ccb1966SGarrett D'Amore 	return (NULL);
250*3ccb1966SGarrett D'Amore }
251*3ccb1966SGarrett D'Amore 
252*3ccb1966SGarrett D'Amore 
253*3ccb1966SGarrett D'Amore /*
254*3ccb1966SGarrett D'Amore  * caller fills in d->mixer.devnode; func fills in the rest
255*3ccb1966SGarrett D'Amore  */
256*3ccb1966SGarrett D'Amore static int
257*3ccb1966SGarrett D'Amore get_device_info(device_t *d)
258*3ccb1966SGarrett D'Amore {
259*3ccb1966SGarrett D'Amore 	int fd = -1;
260*3ccb1966SGarrett D'Amore 	int i;
261*3ccb1966SGarrett D'Amore 	cinfo_t *ci;
262*3ccb1966SGarrett D'Amore 
263*3ccb1966SGarrett D'Amore 	if ((fd = open(d->mixer.devnode, O_RDWR)) < 0) {
264*3ccb1966SGarrett D'Amore 		perror(_("Error opening device"));
265*3ccb1966SGarrett D'Amore 		return (errno);
266*3ccb1966SGarrett D'Amore 	}
267*3ccb1966SGarrett D'Amore 	d->mfd = fd;
268*3ccb1966SGarrett D'Amore 
269*3ccb1966SGarrett D'Amore 	d->cmax = -1;
270*3ccb1966SGarrett D'Amore 	if (ioctl(fd, SNDCTL_MIX_NREXT, &d->cmax) < 0) {
271*3ccb1966SGarrett D'Amore 		perror(_("Error getting control count"));
272*3ccb1966SGarrett D'Amore 		return (errno);
273*3ccb1966SGarrett D'Amore 	}
274*3ccb1966SGarrett D'Amore 
275*3ccb1966SGarrett D'Amore 	d->controls = calloc(d->cmax, sizeof (*d->controls));
276*3ccb1966SGarrett D'Amore 
277*3ccb1966SGarrett D'Amore 	for (i = 0; i < d->cmax; i++) {
278*3ccb1966SGarrett D'Amore 		ci = &d->controls[i];
279*3ccb1966SGarrett D'Amore 
280*3ccb1966SGarrett D'Amore 		ci->ci.dev = -1;
281*3ccb1966SGarrett D'Amore 		ci->ci.ctrl = i;
282*3ccb1966SGarrett D'Amore 
283*3ccb1966SGarrett D'Amore 		if (ioctl(fd, SNDCTL_MIX_EXTINFO, &ci->ci) < 0) {
284*3ccb1966SGarrett D'Amore 			perror(_("Error getting control info"));
285*3ccb1966SGarrett D'Amore 			return (errno);
286*3ccb1966SGarrett D'Amore 		}
287*3ccb1966SGarrett D'Amore 
288*3ccb1966SGarrett D'Amore 		if (ci->ci.type == MIXT_ENUM) {
289*3ccb1966SGarrett D'Amore 			ci->enump = calloc(1, sizeof (*ci->enump));
290*3ccb1966SGarrett D'Amore 			ci->enump->dev = -1;
291*3ccb1966SGarrett D'Amore 			ci->enump->ctrl = ci->ci.ctrl;
292*3ccb1966SGarrett D'Amore 
293*3ccb1966SGarrett D'Amore 			if (ioctl(fd, SNDCTL_MIX_ENUMINFO, ci->enump) < 0) {
294*3ccb1966SGarrett D'Amore 				perror(_("Error getting enum info"));
295*3ccb1966SGarrett D'Amore 				return (errno);
296*3ccb1966SGarrett D'Amore 			}
297*3ccb1966SGarrett D'Amore 		}
298*3ccb1966SGarrett D'Amore 	}
299*3ccb1966SGarrett D'Amore 
300*3ccb1966SGarrett D'Amore 	return (0);
301*3ccb1966SGarrett D'Amore }
302*3ccb1966SGarrett D'Amore 
303*3ccb1966SGarrett D'Amore 
304*3ccb1966SGarrett D'Amore static int
305*3ccb1966SGarrett D'Amore load_devices(void)
306*3ccb1966SGarrett D'Amore {
307*3ccb1966SGarrett D'Amore 	int rv = -1;
308*3ccb1966SGarrett D'Amore 	int fd = -1;
309*3ccb1966SGarrett D'Amore 	int i;
310*3ccb1966SGarrett D'Amore 	oss_sysinfo si;
311*3ccb1966SGarrett D'Amore 	device_t *d;
312*3ccb1966SGarrett D'Amore 
313*3ccb1966SGarrett D'Amore 	if ((fd = open("/dev/mixer", O_RDWR)) < 0) {
314*3ccb1966SGarrett D'Amore 		rv = errno;
315*3ccb1966SGarrett D'Amore 		warn(_("Error opening mixer\n"));
316*3ccb1966SGarrett D'Amore 		goto OUT;
317*3ccb1966SGarrett D'Amore 	}
318*3ccb1966SGarrett D'Amore 
319*3ccb1966SGarrett D'Amore 	if (ioctl(fd, SNDCTL_SYSINFO, &si) < 0) {
320*3ccb1966SGarrett D'Amore 		rv = errno;
321*3ccb1966SGarrett D'Amore 		perror(_("Error getting system information"));
322*3ccb1966SGarrett D'Amore 		goto OUT;
323*3ccb1966SGarrett D'Amore 	}
324*3ccb1966SGarrett D'Amore 
325*3ccb1966SGarrett D'Amore 	for (i = 0; i < si.nummixers; i++) {
326*3ccb1966SGarrett D'Amore 
327*3ccb1966SGarrett D'Amore 		struct stat sbuf;
328*3ccb1966SGarrett D'Amore 
329*3ccb1966SGarrett D'Amore 		d = alloc_device();
330*3ccb1966SGarrett D'Amore 		d->mixer.dev = i;
331*3ccb1966SGarrett D'Amore 
332*3ccb1966SGarrett D'Amore 		if (ioctl(fd, SNDCTL_MIXERINFO, &d->mixer) != 0) {
333*3ccb1966SGarrett D'Amore 			continue;
334*3ccb1966SGarrett D'Amore 		}
335*3ccb1966SGarrett D'Amore 
336*3ccb1966SGarrett D'Amore 		d->card.card = d->mixer.card_number;
337*3ccb1966SGarrett D'Amore 
338*3ccb1966SGarrett D'Amore 		if ((ioctl(fd, SNDCTL_CARDINFO, &d->card) != 0) ||
339*3ccb1966SGarrett D'Amore 		    (stat(d->mixer.devnode, &sbuf) != 0) ||
340*3ccb1966SGarrett D'Amore 		    ((sbuf.st_mode & S_IFCHR) == 0)) {
341*3ccb1966SGarrett D'Amore 			warn(_("Device present: %s\n"), d->mixer.devnode);
342*3ccb1966SGarrett D'Amore 			free_device(d);
343*3ccb1966SGarrett D'Amore 			continue;
344*3ccb1966SGarrett D'Amore 		}
345*3ccb1966SGarrett D'Amore 		d->devt = makedev(major(sbuf.st_rdev),
346*3ccb1966SGarrett D'Amore 		    minor(sbuf.st_rdev) & ~(AUDIO_MN_TYPE_MASK));
347*3ccb1966SGarrett D'Amore 
348*3ccb1966SGarrett D'Amore 		if ((rv = get_device_info(d)) != 0) {
349*3ccb1966SGarrett D'Amore 			free_device(d);
350*3ccb1966SGarrett D'Amore 			goto OUT;
351*3ccb1966SGarrett D'Amore 		}
352*3ccb1966SGarrett D'Amore 	}
353*3ccb1966SGarrett D'Amore 
354*3ccb1966SGarrett D'Amore 	rv = 0;
355*3ccb1966SGarrett D'Amore 
356*3ccb1966SGarrett D'Amore OUT:
357*3ccb1966SGarrett D'Amore 	if (fd >= 0)
358*3ccb1966SGarrett D'Amore 		(void) close(fd);
359*3ccb1966SGarrett D'Amore 	return (rv);
360*3ccb1966SGarrett D'Amore }
361*3ccb1966SGarrett D'Amore 
362*3ccb1966SGarrett D'Amore 
363*3ccb1966SGarrett D'Amore static int
364*3ccb1966SGarrett D'Amore ctype_valid(int type)
365*3ccb1966SGarrett D'Amore {
366*3ccb1966SGarrett D'Amore 	switch (type) {
367*3ccb1966SGarrett D'Amore 	case MIXT_ONOFF:
368*3ccb1966SGarrett D'Amore 	case MIXT_ENUM:
369*3ccb1966SGarrett D'Amore 	case MIXT_MONOSLIDER:
370*3ccb1966SGarrett D'Amore 	case MIXT_STEREOSLIDER:
371*3ccb1966SGarrett D'Amore 		return (1);
372*3ccb1966SGarrett D'Amore 	default:
373*3ccb1966SGarrett D'Amore 		return (0);
374*3ccb1966SGarrett D'Amore 	}
375*3ccb1966SGarrett D'Amore }
376*3ccb1966SGarrett D'Amore 
377*3ccb1966SGarrett D'Amore 
378*3ccb1966SGarrett D'Amore static void
379*3ccb1966SGarrett D'Amore print_control_line(FILE *sfp, col_prt_t *colp, int vopt)
380*3ccb1966SGarrett D'Amore {
381*3ccb1966SGarrett D'Amore 	int i;
382*3ccb1966SGarrett D'Amore 	size_t *col_prtp;
383*3ccb1966SGarrett D'Amore 	int *col_dpyp;
384*3ccb1966SGarrett D'Amore 	int col_cnt;
385*3ccb1966SGarrett D'Amore 	int col_type;
386*3ccb1966SGarrett D'Amore 	int width;
387*3ccb1966SGarrett D'Amore 	char *colstr;
388*3ccb1966SGarrett D'Amore 	char cbuf[COL_MAX_SZ + 1];
389*3ccb1966SGarrett D'Amore 	char line[128];
390*3ccb1966SGarrett D'Amore 	char *colsep =  " ";
391*3ccb1966SGarrett D'Amore 
392*3ccb1966SGarrett D'Amore 	if (sfp != NULL) {
393*3ccb1966SGarrett D'Amore 		col_prtp = col_dpy_prt_tofile;
394*3ccb1966SGarrett D'Amore 		col_dpyp = col_dpy_tofile;
395*3ccb1966SGarrett D'Amore 		col_cnt = col_dpy_tofile_len;
396*3ccb1966SGarrett D'Amore 	} else if (vopt) {
397*3ccb1966SGarrett D'Amore 		col_prtp = col_dpy_prt_vopt;
398*3ccb1966SGarrett D'Amore 		col_dpyp = col_dpy_vopt;
399*3ccb1966SGarrett D'Amore 		col_cnt = col_dpy_vopt_len;
400*3ccb1966SGarrett D'Amore 	} else {
401*3ccb1966SGarrett D'Amore 		col_prtp = col_dpy_prt;
402*3ccb1966SGarrett D'Amore 		col_dpyp = col_dpy;
403*3ccb1966SGarrett D'Amore 		col_cnt = col_dpy_len;
404*3ccb1966SGarrett D'Amore 	}
405*3ccb1966SGarrett D'Amore 
406*3ccb1966SGarrett D'Amore 	line[0] = '\0';
407*3ccb1966SGarrett D'Amore 
408*3ccb1966SGarrett D'Amore 	for (i = 0; i < col_cnt; i++) {
409*3ccb1966SGarrett D'Amore 		col_type = col_dpyp[i];
410*3ccb1966SGarrett D'Amore 		width = col_sz[col_type];
411*3ccb1966SGarrett D'Amore 		colstr = *(char **)(((size_t)colp) + col_prtp[i]);
412*3ccb1966SGarrett D'Amore 
413*3ccb1966SGarrett D'Amore 		(void) snprintf(cbuf, sizeof (cbuf), "%- *s",
414*3ccb1966SGarrett D'Amore 		    width > 0 ? width : 1,
415*3ccb1966SGarrett D'Amore 		    (colstr == NULL) ? "" : colstr);
416*3ccb1966SGarrett D'Amore 
417*3ccb1966SGarrett D'Amore 		(void) strlcat(line, cbuf, sizeof (line));
418*3ccb1966SGarrett D'Amore 		if (i < col_cnt - 1)
419*3ccb1966SGarrett D'Amore 			(void) strlcat(line, colsep, sizeof (line));
420*3ccb1966SGarrett D'Amore 	}
421*3ccb1966SGarrett D'Amore 
422*3ccb1966SGarrett D'Amore 	(void) fprintf(sfp ? sfp : stdout, "%s\n", line);
423*3ccb1966SGarrett D'Amore }
424*3ccb1966SGarrett D'Amore 
425*3ccb1966SGarrett D'Amore 
426*3ccb1966SGarrett D'Amore static void
427*3ccb1966SGarrett D'Amore print_header(FILE *sfp, int vopt)
428*3ccb1966SGarrett D'Amore {
429*3ccb1966SGarrett D'Amore 	col_prt_t col;
430*3ccb1966SGarrett D'Amore 
431*3ccb1966SGarrett D'Amore 	if (sfp) {
432*3ccb1966SGarrett D'Amore 		col.col_nm = _("#CONTROL");
433*3ccb1966SGarrett D'Amore 		col.col_val = _("VALUE");
434*3ccb1966SGarrett D'Amore 	} else {
435*3ccb1966SGarrett D'Amore 		col.col_dv = _("DEVICE");
436*3ccb1966SGarrett D'Amore 		col.col_nm = _("CONTROL");
437*3ccb1966SGarrett D'Amore 		col.col_val = _("VALUE");
438*3ccb1966SGarrett D'Amore 		col.col_sel = _("POSSIBLE");
439*3ccb1966SGarrett D'Amore 	}
440*3ccb1966SGarrett D'Amore 	print_control_line(sfp, &col, vopt);
441*3ccb1966SGarrett D'Amore }
442*3ccb1966SGarrett D'Amore 
443*3ccb1966SGarrett D'Amore 
444*3ccb1966SGarrett D'Amore static int
445*3ccb1966SGarrett D'Amore print_control(FILE *sfp, device_t *d, cinfo_t *cinfop, int vopt)
446*3ccb1966SGarrett D'Amore {
447*3ccb1966SGarrett D'Amore 	int mfd = d->mfd;
448*3ccb1966SGarrett D'Amore 	char *devnm = d->card.shortname;
449*3ccb1966SGarrett D'Amore 	oss_mixer_value cval;
450*3ccb1966SGarrett D'Amore 	char *str;
451*3ccb1966SGarrett D'Amore 	int i;
452*3ccb1966SGarrett D'Amore 	int idx = -1;
453*3ccb1966SGarrett D'Amore 	int rv = -1;
454*3ccb1966SGarrett D'Amore 	char valbuf[COL_VAL_SZ + 1];
455*3ccb1966SGarrett D'Amore 	char selbuf[COL_SEL_SZ + 1];
456*3ccb1966SGarrett D'Amore 	col_prt_t col;
457*3ccb1966SGarrett D'Amore 
458*3ccb1966SGarrett D'Amore 	cval.dev = -1;
459*3ccb1966SGarrett D'Amore 	cval.ctrl = cinfop->ci.ctrl;
460*3ccb1966SGarrett D'Amore 
461*3ccb1966SGarrett D'Amore 	if (ctype_valid(cinfop->ci.type)) {
462*3ccb1966SGarrett D'Amore 		if (ioctl(mfd, SNDCTL_MIX_READ, &cval) < 0) {
463*3ccb1966SGarrett D'Amore 			rv = errno;
464*3ccb1966SGarrett D'Amore 			perror(_("Error reading control\n"));
465*3ccb1966SGarrett D'Amore 			return (rv);
466*3ccb1966SGarrett D'Amore 		}
467*3ccb1966SGarrett D'Amore 	} else {
468*3ccb1966SGarrett D'Amore 		return (0);
469*3ccb1966SGarrett D'Amore 	}
470*3ccb1966SGarrett D'Amore 
471*3ccb1966SGarrett D'Amore 	/*
472*3ccb1966SGarrett D'Amore 	 * convert the control value into a string
473*3ccb1966SGarrett D'Amore 	 */
474*3ccb1966SGarrett D'Amore 	switch (cinfop->ci.type) {
475*3ccb1966SGarrett D'Amore 	case MIXT_ONOFF:
476*3ccb1966SGarrett D'Amore 		(void) snprintf(valbuf, sizeof (valbuf), "%s",
477*3ccb1966SGarrett D'Amore 		    cval.value ? _("on") : _("off"));
478*3ccb1966SGarrett D'Amore 		break;
479*3ccb1966SGarrett D'Amore 
480*3ccb1966SGarrett D'Amore 	case MIXT_MONOSLIDER:
481*3ccb1966SGarrett D'Amore 		(void) snprintf(valbuf, sizeof (valbuf), "%d",
482*3ccb1966SGarrett D'Amore 		    cval.value & 0xff);
483*3ccb1966SGarrett D'Amore 		break;
484*3ccb1966SGarrett D'Amore 
485*3ccb1966SGarrett D'Amore 	case MIXT_STEREOSLIDER:
486*3ccb1966SGarrett D'Amore 		(void) snprintf(valbuf, sizeof (valbuf), "%d:%d",
487*3ccb1966SGarrett D'Amore 		    (int)AUDIO_CTRL_STEREO_LEFT(cval.value),
488*3ccb1966SGarrett D'Amore 		    (int)AUDIO_CTRL_STEREO_RIGHT(cval.value));
489*3ccb1966SGarrett D'Amore 		break;
490*3ccb1966SGarrett D'Amore 
491*3ccb1966SGarrett D'Amore 	case MIXT_ENUM:
492*3ccb1966SGarrett D'Amore 		str = get_enum_str(cinfop, cval.value);
493*3ccb1966SGarrett D'Amore 		if (str == NULL) {
494*3ccb1966SGarrett D'Amore 			warn(_("Bad enum index %d for control '%s'\n"),
495*3ccb1966SGarrett D'Amore 			    cval.value, cinfop->ci.extname);
496*3ccb1966SGarrett D'Amore 			return (EINVAL);
497*3ccb1966SGarrett D'Amore 		}
498*3ccb1966SGarrett D'Amore 
499*3ccb1966SGarrett D'Amore 		(void) snprintf(valbuf, sizeof (valbuf), "%s", str);
500*3ccb1966SGarrett D'Amore 		break;
501*3ccb1966SGarrett D'Amore 
502*3ccb1966SGarrett D'Amore 	default:
503*3ccb1966SGarrett D'Amore 		return (0);
504*3ccb1966SGarrett D'Amore 	}
505*3ccb1966SGarrett D'Amore 
506*3ccb1966SGarrett D'Amore 	/*
507*3ccb1966SGarrett D'Amore 	 * possible control values (range/selection)
508*3ccb1966SGarrett D'Amore 	 */
509*3ccb1966SGarrett D'Amore 	switch (cinfop->ci.type) {
510*3ccb1966SGarrett D'Amore 	case MIXT_ONOFF:
511*3ccb1966SGarrett D'Amore 		(void) snprintf(selbuf, sizeof (selbuf), _("on,off"));
512*3ccb1966SGarrett D'Amore 		break;
513*3ccb1966SGarrett D'Amore 
514*3ccb1966SGarrett D'Amore 	case MIXT_MONOSLIDER:
515*3ccb1966SGarrett D'Amore 		(void) snprintf(selbuf, sizeof (selbuf), "%d-%d",
516*3ccb1966SGarrett D'Amore 		    cinfop->ci.minvalue, cinfop->ci.maxvalue);
517*3ccb1966SGarrett D'Amore 		break;
518*3ccb1966SGarrett D'Amore 	case MIXT_STEREOSLIDER:
519*3ccb1966SGarrett D'Amore 		(void) snprintf(selbuf, sizeof (selbuf), "%d-%d:%d-%d",
520*3ccb1966SGarrett D'Amore 		    cinfop->ci.minvalue, cinfop->ci.maxvalue,
521*3ccb1966SGarrett D'Amore 		    cinfop->ci.minvalue, cinfop->ci.maxvalue);
522*3ccb1966SGarrett D'Amore 		break;
523*3ccb1966SGarrett D'Amore 
524*3ccb1966SGarrett D'Amore 	case MIXT_ENUM:
525*3ccb1966SGarrett D'Amore 		/*
526*3ccb1966SGarrett D'Amore 		 * display the first choice on the same line, then display
527*3ccb1966SGarrett D'Amore 		 * the rest on multiple lines
528*3ccb1966SGarrett D'Amore 		 */
529*3ccb1966SGarrett D'Amore 		selbuf[0] = 0;
530*3ccb1966SGarrett D'Amore 		for (i = 0; i < cinfop->ci.maxvalue; i++) {
531*3ccb1966SGarrett D'Amore 			str = get_enum_str(cinfop, i);
532*3ccb1966SGarrett D'Amore 			if (str == NULL)
533*3ccb1966SGarrett D'Amore 				continue;
534*3ccb1966SGarrett D'Amore 
535*3ccb1966SGarrett D'Amore 			if ((strlen(str) + 1 + strlen(selbuf)) >=
536*3ccb1966SGarrett D'Amore 			    sizeof (selbuf)) {
537*3ccb1966SGarrett D'Amore 				break;
538*3ccb1966SGarrett D'Amore 			}
539*3ccb1966SGarrett D'Amore 			if (strlen(selbuf)) {
540*3ccb1966SGarrett D'Amore 				(void) strlcat(selbuf, ",", sizeof (selbuf));
541*3ccb1966SGarrett D'Amore 			}
542*3ccb1966SGarrett D'Amore 
543*3ccb1966SGarrett D'Amore 			(void) strlcat(selbuf, str, sizeof (selbuf));
544*3ccb1966SGarrett D'Amore 		}
545*3ccb1966SGarrett D'Amore 		idx = i;
546*3ccb1966SGarrett D'Amore 		break;
547*3ccb1966SGarrett D'Amore 
548*3ccb1966SGarrett D'Amore 	default:
549*3ccb1966SGarrett D'Amore 		(void) snprintf(selbuf, sizeof (selbuf), "-");
550*3ccb1966SGarrett D'Amore 	}
551*3ccb1966SGarrett D'Amore 
552*3ccb1966SGarrett D'Amore 	col.col_dv = devnm;
553*3ccb1966SGarrett D'Amore 	col.col_nm = strlen(cinfop->ci.extname) ?
554*3ccb1966SGarrett D'Amore 	    cinfop->ci.extname : cinfop->ci.id;
555*3ccb1966SGarrett D'Amore 	while (strchr(col.col_nm, '_') != NULL) {
556*3ccb1966SGarrett D'Amore 		col.col_nm = strchr(col.col_nm, '_') + 1;
557*3ccb1966SGarrett D'Amore 	}
558*3ccb1966SGarrett D'Amore 	col.col_val = valbuf;
559*3ccb1966SGarrett D'Amore 	col.col_sel = selbuf;
560*3ccb1966SGarrett D'Amore 	print_control_line(sfp, &col, vopt);
561*3ccb1966SGarrett D'Amore 
562*3ccb1966SGarrett D'Amore 	/* print leftover enum value selections */
563*3ccb1966SGarrett D'Amore 	while ((sfp == NULL) && (idx >= 0) && (idx < cinfop->ci.maxvalue)) {
564*3ccb1966SGarrett D'Amore 		selbuf[0] = 0;
565*3ccb1966SGarrett D'Amore 		for (i = idx; i < cinfop->ci.maxvalue; i++) {
566*3ccb1966SGarrett D'Amore 			str = get_enum_str(cinfop, i);
567*3ccb1966SGarrett D'Amore 			if (str == NULL)
568*3ccb1966SGarrett D'Amore 				continue;
569*3ccb1966SGarrett D'Amore 
570*3ccb1966SGarrett D'Amore 			if ((strlen(str) + 1 + strlen(selbuf)) >=
571*3ccb1966SGarrett D'Amore 			    sizeof (selbuf)) {
572*3ccb1966SGarrett D'Amore 				break;
573*3ccb1966SGarrett D'Amore 			}
574*3ccb1966SGarrett D'Amore 			if (strlen(selbuf)) {
575*3ccb1966SGarrett D'Amore 				(void) strlcat(selbuf, ",", sizeof (selbuf));
576*3ccb1966SGarrett D'Amore 			}
577*3ccb1966SGarrett D'Amore 
578*3ccb1966SGarrett D'Amore 			(void) strlcat(selbuf, str, sizeof (selbuf));
579*3ccb1966SGarrett D'Amore 		}
580*3ccb1966SGarrett D'Amore 		idx = i;
581*3ccb1966SGarrett D'Amore 		col.col_dv = NULL;
582*3ccb1966SGarrett D'Amore 		col.col_nm = NULL;
583*3ccb1966SGarrett D'Amore 		col.col_val = NULL;
584*3ccb1966SGarrett D'Amore 		col.col_sel = selbuf;
585*3ccb1966SGarrett D'Amore 		print_control_line(sfp, &col, vopt);
586*3ccb1966SGarrett D'Amore 	}
587*3ccb1966SGarrett D'Amore 
588*3ccb1966SGarrett D'Amore 	return (0);
589*3ccb1966SGarrett D'Amore }
590*3ccb1966SGarrett D'Amore 
591*3ccb1966SGarrett D'Amore 
592*3ccb1966SGarrett D'Amore static int
593*3ccb1966SGarrett D'Amore set_device_control(device_t *d, cinfo_t *cinfop, char *wstr, int vopt)
594*3ccb1966SGarrett D'Amore {
595*3ccb1966SGarrett D'Amore 	int mfd = d->mfd;
596*3ccb1966SGarrett D'Amore 	oss_mixer_value cval;
597*3ccb1966SGarrett D'Amore 	int wlen = strlen(wstr);
598*3ccb1966SGarrett D'Amore 	int lval, rval;
599*3ccb1966SGarrett D'Amore 	char *lstr, *rstr;
600*3ccb1966SGarrett D'Amore 	char *str;
601*3ccb1966SGarrett D'Amore 	int i;
602*3ccb1966SGarrett D'Amore 	int rv = -1;
603*3ccb1966SGarrett D'Amore 
604*3ccb1966SGarrett D'Amore 	cval.dev = -1;
605*3ccb1966SGarrett D'Amore 	cval.ctrl = cinfop->ci.ctrl;
606*3ccb1966SGarrett D'Amore 	cval.value = 0;
607*3ccb1966SGarrett D'Amore 
608*3ccb1966SGarrett D'Amore 	switch (cinfop->ci.type) {
609*3ccb1966SGarrett D'Amore 	case MIXT_ONOFF:
610*3ccb1966SGarrett D'Amore 		cval.value = (strncmp(_("on"), wstr, wlen) == 0) ? 1 : 0;
611*3ccb1966SGarrett D'Amore 		break;
612*3ccb1966SGarrett D'Amore 
613*3ccb1966SGarrett D'Amore 	case MIXT_MONOSLIDER:
614*3ccb1966SGarrett D'Amore 		cval.value = atoi(wstr);
615*3ccb1966SGarrett D'Amore 		break;
616*3ccb1966SGarrett D'Amore 
617*3ccb1966SGarrett D'Amore 	case MIXT_STEREOSLIDER:
618*3ccb1966SGarrett D'Amore 		lstr = wstr;
619*3ccb1966SGarrett D'Amore 		rstr = strchr(wstr, ':');
620*3ccb1966SGarrett D'Amore 		if (rstr != NULL) {
621*3ccb1966SGarrett D'Amore 			*rstr = '\0';
622*3ccb1966SGarrett D'Amore 			rstr++;
623*3ccb1966SGarrett D'Amore 
624*3ccb1966SGarrett D'Amore 			rval = atoi(rstr);
625*3ccb1966SGarrett D'Amore 			lval = atoi(lstr);
626*3ccb1966SGarrett D'Amore 
627*3ccb1966SGarrett D'Amore 			rstr--;
628*3ccb1966SGarrett D'Amore 			*rstr = ':';
629*3ccb1966SGarrett D'Amore 		} else {
630*3ccb1966SGarrett D'Amore 			lval = atoi(lstr);
631*3ccb1966SGarrett D'Amore 			rval = lval;
632*3ccb1966SGarrett D'Amore 		}
633*3ccb1966SGarrett D'Amore 
634*3ccb1966SGarrett D'Amore 		cval.value = AUDIO_CTRL_STEREO_VAL(lval, rval);
635*3ccb1966SGarrett D'Amore 		break;
636*3ccb1966SGarrett D'Amore 
637*3ccb1966SGarrett D'Amore 	case MIXT_ENUM:
638*3ccb1966SGarrett D'Amore 		for (i = 0; i < cinfop->ci.maxvalue; i++) {
639*3ccb1966SGarrett D'Amore 			str = get_enum_str(cinfop, i);
640*3ccb1966SGarrett D'Amore 			if (str == NULL)
641*3ccb1966SGarrett D'Amore 				continue;
642*3ccb1966SGarrett D'Amore 
643*3ccb1966SGarrett D'Amore 			if (strncmp(wstr, str, wlen) == 0) {
644*3ccb1966SGarrett D'Amore 				cval.value = i;
645*3ccb1966SGarrett D'Amore 				break;
646*3ccb1966SGarrett D'Amore 			}
647*3ccb1966SGarrett D'Amore 		}
648*3ccb1966SGarrett D'Amore 
649*3ccb1966SGarrett D'Amore 		if (i >= cinfop->ci.maxvalue) {
650*3ccb1966SGarrett D'Amore 			warn(_("Invalid enumeration value\n"));
651*3ccb1966SGarrett D'Amore 			return (EINVAL);
652*3ccb1966SGarrett D'Amore 		}
653*3ccb1966SGarrett D'Amore 		break;
654*3ccb1966SGarrett D'Amore 
655*3ccb1966SGarrett D'Amore 	default:
656*3ccb1966SGarrett D'Amore 		warn(_("Unsupported control type: %d\n"), cinfop->ci.type);
657*3ccb1966SGarrett D'Amore 		return (EINVAL);
658*3ccb1966SGarrett D'Amore 	}
659*3ccb1966SGarrett D'Amore 
660*3ccb1966SGarrett D'Amore 	if (vopt) {
661*3ccb1966SGarrett D'Amore 		msg(_("%s: '%s' set to '%s'\n"), d->card.shortname,
662*3ccb1966SGarrett D'Amore 		    cinfop->ci.extname, wstr);
663*3ccb1966SGarrett D'Amore 	}
664*3ccb1966SGarrett D'Amore 
665*3ccb1966SGarrett D'Amore 	if (ioctl(mfd, SNDCTL_MIX_WRITE, &cval) < 0) {
666*3ccb1966SGarrett D'Amore 		rv = errno;
667*3ccb1966SGarrett D'Amore 		perror(_("Error writing control"));
668*3ccb1966SGarrett D'Amore 		return (rv);
669*3ccb1966SGarrett D'Amore 	}
670*3ccb1966SGarrett D'Amore 
671*3ccb1966SGarrett D'Amore 	rv = 0;
672*3ccb1966SGarrett D'Amore 	return (rv);
673*3ccb1966SGarrett D'Amore }
674*3ccb1966SGarrett D'Amore 
675*3ccb1966SGarrett D'Amore 
676*3ccb1966SGarrett D'Amore static void
677*3ccb1966SGarrett D'Amore help(void)
678*3ccb1966SGarrett D'Amore {
679*3ccb1966SGarrett D'Amore #define	HELP_STR	_(						\
680*3ccb1966SGarrett D'Amore "audioctl list-devices\n"						\
681*3ccb1966SGarrett D'Amore "	list all audio devices\n"					\
682*3ccb1966SGarrett D'Amore "\n"									\
683*3ccb1966SGarrett D'Amore "audioctl show-device [ -v ] [ -d <device> ]\n"				\
684*3ccb1966SGarrett D'Amore "	display information about an audio device\n"			\
685*3ccb1966SGarrett D'Amore "\n"									\
686*3ccb1966SGarrett D'Amore "audioctl show-control [ -v ] [ -d <device> ] [ <control> ... ]\n"	\
687*3ccb1966SGarrett D'Amore "	get the value of a specific control (all if not specified)\n"	\
688*3ccb1966SGarrett D'Amore "\n"									\
689*3ccb1966SGarrett D'Amore "audioctl set-control [ -v ] [ -d <device> ] <control> <value>\n"	\
690*3ccb1966SGarrett D'Amore "	set the value of a specific control\n"				\
691*3ccb1966SGarrett D'Amore "\n"									\
692*3ccb1966SGarrett D'Amore "audioctl save-controls [ -d <device> ] [ -f ] <file>\n"		\
693*3ccb1966SGarrett D'Amore "	save all control settings for the device to a file\n"		\
694*3ccb1966SGarrett D'Amore "\n"									\
695*3ccb1966SGarrett D'Amore "audioctl load-controls [ -d <device> ] <file>\n"			\
696*3ccb1966SGarrett D'Amore "	restore previously saved control settings to device\n"		\
697*3ccb1966SGarrett D'Amore "\n"									\
698*3ccb1966SGarrett D'Amore "audioctl help\n"							\
699*3ccb1966SGarrett D'Amore "	show this message.\n")
700*3ccb1966SGarrett D'Amore 
701*3ccb1966SGarrett D'Amore 	(void) fprintf(stderr, HELP_STR);
702*3ccb1966SGarrett D'Amore }
703*3ccb1966SGarrett D'Amore 
704*3ccb1966SGarrett D'Amore dev_t
705*3ccb1966SGarrett D'Amore device_devt(char *name)
706*3ccb1966SGarrett D'Amore {
707*3ccb1966SGarrett D'Amore 	struct stat	sbuf;
708*3ccb1966SGarrett D'Amore 
709*3ccb1966SGarrett D'Amore 	if ((stat(name, &sbuf) != 0) ||
710*3ccb1966SGarrett D'Amore 	    ((sbuf.st_mode & S_IFCHR) == 0)) {
711*3ccb1966SGarrett D'Amore 		/* Not a device node! */
712*3ccb1966SGarrett D'Amore 		return (0);
713*3ccb1966SGarrett D'Amore 	}
714*3ccb1966SGarrett D'Amore 
715*3ccb1966SGarrett D'Amore 	return (makedev(major(sbuf.st_rdev),
716*3ccb1966SGarrett D'Amore 	    minor(sbuf.st_rdev) & ~(AUDIO_MN_TYPE_MASK)));
717*3ccb1966SGarrett D'Amore }
718*3ccb1966SGarrett D'Amore 
719*3ccb1966SGarrett D'Amore static device_t *
720*3ccb1966SGarrett D'Amore find_device(char *name)
721*3ccb1966SGarrett D'Amore {
722*3ccb1966SGarrett D'Amore 	dev_t		devt;
723*3ccb1966SGarrett D'Amore 	device_t	*d;
724*3ccb1966SGarrett D'Amore 
725*3ccb1966SGarrett D'Amore 	/*
726*3ccb1966SGarrett D'Amore 	 * User may have specified:
727*3ccb1966SGarrett D'Amore 	 *
728*3ccb1966SGarrett D'Amore 	 * /dev/dsp[<num>]
729*3ccb1966SGarrett D'Amore 	 * /dev/mixer[<num>]
730*3ccb1966SGarrett D'Amore 	 * /dev/audio[<num>9]
731*3ccb1966SGarrett D'Amore 	 * /dev/audioctl[<num>]
732*3ccb1966SGarrett D'Amore 	 * /dev/sound/<num>{,ctl,dsp,mixer}
733*3ccb1966SGarrett D'Amore 	 * /dev/sound/<driver>:<num>{,ctl,dsp,mixer}
734*3ccb1966SGarrett D'Amore 	 *
735*3ccb1966SGarrett D'Amore 	 * We can canonicalize these by looking at the dev_t though.
736*3ccb1966SGarrett D'Amore 	 */
737*3ccb1966SGarrett D'Amore 
738*3ccb1966SGarrett D'Amore 	if (name == NULL)
739*3ccb1966SGarrett D'Amore 		name = getenv("AUDIODEV");
740*3ccb1966SGarrett D'Amore 
741*3ccb1966SGarrett D'Amore 	if ((name == NULL) ||
742*3ccb1966SGarrett D'Amore 	    (strcmp(name, "/dev/mixer") == 0)) {
743*3ccb1966SGarrett D'Amore 		/* /dev/mixer node doesn't point to real hw */
744*3ccb1966SGarrett D'Amore 		name = "/dev/dsp";
745*3ccb1966SGarrett D'Amore 	}
746*3ccb1966SGarrett D'Amore 
747*3ccb1966SGarrett D'Amore 	if (*name == '/') {
748*3ccb1966SGarrett D'Amore 		/* if we have a full path, convert to the devt */
749*3ccb1966SGarrett D'Amore 		if ((devt = device_devt(name)) == 0) {
750*3ccb1966SGarrett D'Amore 			warn(_("No such audio device.\n"));
751*3ccb1966SGarrett D'Amore 			return (NULL);
752*3ccb1966SGarrett D'Amore 		}
753*3ccb1966SGarrett D'Amore 		name = NULL;
754*3ccb1966SGarrett D'Amore 	}
755*3ccb1966SGarrett D'Amore 
756*3ccb1966SGarrett D'Amore 	for (d = devices; d != NULL; d = d->nextp) {
757*3ccb1966SGarrett D'Amore 		oss_card_info *card = &d->card;
758*3ccb1966SGarrett D'Amore 
759*3ccb1966SGarrett D'Amore 		if ((name) && (strcmp(name, card->shortname) == 0)) {
760*3ccb1966SGarrett D'Amore 			return (d);
761*3ccb1966SGarrett D'Amore 		}
762*3ccb1966SGarrett D'Amore 		if (devt == d->devt) {
763*3ccb1966SGarrett D'Amore 			return (d);
764*3ccb1966SGarrett D'Amore 		}
765*3ccb1966SGarrett D'Amore 	}
766*3ccb1966SGarrett D'Amore 
767*3ccb1966SGarrett D'Amore 	warn(_("No such audio device.\n"));
768*3ccb1966SGarrett D'Amore 	return (NULL);
769*3ccb1966SGarrett D'Amore }
770*3ccb1966SGarrett D'Amore 
771*3ccb1966SGarrett D'Amore int
772*3ccb1966SGarrett D'Amore do_list_devices(int argc, char **argv)
773*3ccb1966SGarrett D'Amore {
774*3ccb1966SGarrett D'Amore 	int		optc;
775*3ccb1966SGarrett D'Amore 	int		verbose = 0;
776*3ccb1966SGarrett D'Amore 	device_t	*d;
777*3ccb1966SGarrett D'Amore 
778*3ccb1966SGarrett D'Amore 	while ((optc = getopt(argc, argv, "v")) != EOF) {
779*3ccb1966SGarrett D'Amore 		switch (optc) {
780*3ccb1966SGarrett D'Amore 		case 'v':
781*3ccb1966SGarrett D'Amore 			verbose++;
782*3ccb1966SGarrett D'Amore 			break;
783*3ccb1966SGarrett D'Amore 		default:
784*3ccb1966SGarrett D'Amore 			help();
785*3ccb1966SGarrett D'Amore 			return (-1);
786*3ccb1966SGarrett D'Amore 		}
787*3ccb1966SGarrett D'Amore 	}
788*3ccb1966SGarrett D'Amore 	argc -= optind;
789*3ccb1966SGarrett D'Amore 	argv += optind;
790*3ccb1966SGarrett D'Amore 	if (argc != 0) {
791*3ccb1966SGarrett D'Amore 		help();
792*3ccb1966SGarrett D'Amore 		return (-1);
793*3ccb1966SGarrett D'Amore 	}
794*3ccb1966SGarrett D'Amore 
795*3ccb1966SGarrett D'Amore 	for (d = devices; d != NULL; d = d->nextp) {
796*3ccb1966SGarrett D'Amore 
797*3ccb1966SGarrett D'Amore 		if ((d->mixer.enabled == 0) && (!verbose))
798*3ccb1966SGarrett D'Amore 			continue;
799*3ccb1966SGarrett D'Amore 
800*3ccb1966SGarrett D'Amore 		if (verbose) {
801*3ccb1966SGarrett D'Amore 			msg(_("%s (%s)\n"), d->card.shortname,
802*3ccb1966SGarrett D'Amore 			    d->mixer.devnode);
803*3ccb1966SGarrett D'Amore 		} else {
804*3ccb1966SGarrett D'Amore 			msg(_("%s\n"), d->card.shortname);
805*3ccb1966SGarrett D'Amore 		}
806*3ccb1966SGarrett D'Amore 	}
807*3ccb1966SGarrett D'Amore 
808*3ccb1966SGarrett D'Amore 	return (0);
809*3ccb1966SGarrett D'Amore }
810*3ccb1966SGarrett D'Amore 
811*3ccb1966SGarrett D'Amore int
812*3ccb1966SGarrett D'Amore do_show_device(int argc, char **argv)
813*3ccb1966SGarrett D'Amore {
814*3ccb1966SGarrett D'Amore 	int		optc;
815*3ccb1966SGarrett D'Amore 	char		*devname = NULL;
816*3ccb1966SGarrett D'Amore 	device_t	*d;
817*3ccb1966SGarrett D'Amore 
818*3ccb1966SGarrett D'Amore 	while ((optc = getopt(argc, argv, "d:v")) != EOF) {
819*3ccb1966SGarrett D'Amore 		switch (optc) {
820*3ccb1966SGarrett D'Amore 		case 'd':
821*3ccb1966SGarrett D'Amore 			devname = optarg;
822*3ccb1966SGarrett D'Amore 			break;
823*3ccb1966SGarrett D'Amore 		case 'v':
824*3ccb1966SGarrett D'Amore 			break;
825*3ccb1966SGarrett D'Amore 		default:
826*3ccb1966SGarrett D'Amore 			help();
827*3ccb1966SGarrett D'Amore 			return (-1);
828*3ccb1966SGarrett D'Amore 		}
829*3ccb1966SGarrett D'Amore 	}
830*3ccb1966SGarrett D'Amore 	argc -= optind;
831*3ccb1966SGarrett D'Amore 	argv += optind;
832*3ccb1966SGarrett D'Amore 	if (argc != 0) {
833*3ccb1966SGarrett D'Amore 		help();
834*3ccb1966SGarrett D'Amore 		return (-1);
835*3ccb1966SGarrett D'Amore 	}
836*3ccb1966SGarrett D'Amore 
837*3ccb1966SGarrett D'Amore 	if ((d = find_device(devname)) == NULL) {
838*3ccb1966SGarrett D'Amore 		return (ENODEV);
839*3ccb1966SGarrett D'Amore 	}
840*3ccb1966SGarrett D'Amore 
841*3ccb1966SGarrett D'Amore 	msg(_("Device: %s\n"), d->mixer.devnode);
842*3ccb1966SGarrett D'Amore 	msg(_("  Name    = %s\n"), d->card.shortname);
843*3ccb1966SGarrett D'Amore 	msg(_("  Config  = %s\n"), d->card.longname);
844*3ccb1966SGarrett D'Amore 
845*3ccb1966SGarrett D'Amore 	if (strlen(d->card.hw_info)) {
846*3ccb1966SGarrett D'Amore 		msg(_("  HW Info = %s"), d->card.hw_info);
847*3ccb1966SGarrett D'Amore 	}
848*3ccb1966SGarrett D'Amore 
849*3ccb1966SGarrett D'Amore 	return (0);
850*3ccb1966SGarrett D'Amore }
851*3ccb1966SGarrett D'Amore 
852*3ccb1966SGarrett D'Amore int
853*3ccb1966SGarrett D'Amore do_show_control(int argc, char **argv)
854*3ccb1966SGarrett D'Amore {
855*3ccb1966SGarrett D'Amore 	int		optc;
856*3ccb1966SGarrett D'Amore 	int		rval = 0;
857*3ccb1966SGarrett D'Amore 	int		verbose = 0;
858*3ccb1966SGarrett D'Amore 	device_t	*d;
859*3ccb1966SGarrett D'Amore 	char		*devname = NULL;
860*3ccb1966SGarrett D'Amore 	int		i;
861*3ccb1966SGarrett D'Amore 	int		j;
862*3ccb1966SGarrett D'Amore 	int		rv;
863*3ccb1966SGarrett D'Amore 	char		*n;
864*3ccb1966SGarrett D'Amore 	cinfo_t		*cinfop;
865*3ccb1966SGarrett D'Amore 
866*3ccb1966SGarrett D'Amore 	while ((optc = getopt(argc, argv, "d:v")) != EOF) {
867*3ccb1966SGarrett D'Amore 		switch (optc) {
868*3ccb1966SGarrett D'Amore 		case 'd':
869*3ccb1966SGarrett D'Amore 			devname = optarg;
870*3ccb1966SGarrett D'Amore 			break;
871*3ccb1966SGarrett D'Amore 		case 'v':
872*3ccb1966SGarrett D'Amore 			verbose++;
873*3ccb1966SGarrett D'Amore 			break;
874*3ccb1966SGarrett D'Amore 		default:
875*3ccb1966SGarrett D'Amore 			help();
876*3ccb1966SGarrett D'Amore 			return (-1);
877*3ccb1966SGarrett D'Amore 		}
878*3ccb1966SGarrett D'Amore 	}
879*3ccb1966SGarrett D'Amore 	argc -= optind;
880*3ccb1966SGarrett D'Amore 	argv += optind;
881*3ccb1966SGarrett D'Amore 
882*3ccb1966SGarrett D'Amore 	if ((d = find_device(devname)) == NULL) {
883*3ccb1966SGarrett D'Amore 		return (ENODEV);
884*3ccb1966SGarrett D'Amore 	}
885*3ccb1966SGarrett D'Amore 
886*3ccb1966SGarrett D'Amore 	print_header(NULL, verbose);
887*3ccb1966SGarrett D'Amore 	if (argc == 0) {
888*3ccb1966SGarrett D'Amore 		/* do them all! */
889*3ccb1966SGarrett D'Amore 		for (i = 0; i < d->cmax; i++) {
890*3ccb1966SGarrett D'Amore 
891*3ccb1966SGarrett D'Amore 			cinfop = &d->controls[i];
892*3ccb1966SGarrett D'Amore 			rv = print_control(NULL, d, cinfop, verbose);
893*3ccb1966SGarrett D'Amore 			rval = rval ? rval : rv;
894*3ccb1966SGarrett D'Amore 		}
895*3ccb1966SGarrett D'Amore 		return (rval);
896*3ccb1966SGarrett D'Amore 	}
897*3ccb1966SGarrett D'Amore 
898*3ccb1966SGarrett D'Amore 	for (i = 0; i < argc; i++) {
899*3ccb1966SGarrett D'Amore 		for (j = 0; j < d->cmax; j++) {
900*3ccb1966SGarrett D'Amore 			cinfop = &d->controls[j];
901*3ccb1966SGarrett D'Amore 			n = strrchr(cinfop->ci.extname, '_');
902*3ccb1966SGarrett D'Amore 			n = n ? n + 1 : cinfop->ci.extname;
903*3ccb1966SGarrett D'Amore 			if (strcmp(argv[i], n) == 0) {
904*3ccb1966SGarrett D'Amore 				rv = print_control(NULL, d, cinfop, verbose);
905*3ccb1966SGarrett D'Amore 				rval = rval ? rval : rv;
906*3ccb1966SGarrett D'Amore 				break;
907*3ccb1966SGarrett D'Amore 			}
908*3ccb1966SGarrett D'Amore 		}
909*3ccb1966SGarrett D'Amore 		/* Didn't find requested control */
910*3ccb1966SGarrett D'Amore 		if (j == d->cmax) {
911*3ccb1966SGarrett D'Amore 			warn(_("No such control: %s\n"), argv[i]);
912*3ccb1966SGarrett D'Amore 			rval = rval ? rval : ENODEV;
913*3ccb1966SGarrett D'Amore 		}
914*3ccb1966SGarrett D'Amore 	}
915*3ccb1966SGarrett D'Amore 
916*3ccb1966SGarrett D'Amore 	return (rval);
917*3ccb1966SGarrett D'Amore }
918*3ccb1966SGarrett D'Amore 
919*3ccb1966SGarrett D'Amore int
920*3ccb1966SGarrett D'Amore do_set_control(int argc, char **argv)
921*3ccb1966SGarrett D'Amore {
922*3ccb1966SGarrett D'Amore 	int		optc;
923*3ccb1966SGarrett D'Amore 	int		rval = 0;
924*3ccb1966SGarrett D'Amore 	int		verbose = 0;
925*3ccb1966SGarrett D'Amore 	device_t	*d;
926*3ccb1966SGarrett D'Amore 	char		*devname = NULL;
927*3ccb1966SGarrett D'Amore 	char		*cname;
928*3ccb1966SGarrett D'Amore 	char		*value;
929*3ccb1966SGarrett D'Amore 	int		i;
930*3ccb1966SGarrett D'Amore 	int		found;
931*3ccb1966SGarrett D'Amore 	int		rv;
932*3ccb1966SGarrett D'Amore 	char		*n;
933*3ccb1966SGarrett D'Amore 	cinfo_t		*cinfop;
934*3ccb1966SGarrett D'Amore 
935*3ccb1966SGarrett D'Amore 	while ((optc = getopt(argc, argv, "d:v")) != EOF) {
936*3ccb1966SGarrett D'Amore 		switch (optc) {
937*3ccb1966SGarrett D'Amore 		case 'd':
938*3ccb1966SGarrett D'Amore 			devname = optarg;
939*3ccb1966SGarrett D'Amore 			break;
940*3ccb1966SGarrett D'Amore 		case 'v':
941*3ccb1966SGarrett D'Amore 			verbose = 1;
942*3ccb1966SGarrett D'Amore 			break;
943*3ccb1966SGarrett D'Amore 		default:
944*3ccb1966SGarrett D'Amore 			help();
945*3ccb1966SGarrett D'Amore 			return (-1);
946*3ccb1966SGarrett D'Amore 		}
947*3ccb1966SGarrett D'Amore 	}
948*3ccb1966SGarrett D'Amore 	argc -= optind;
949*3ccb1966SGarrett D'Amore 	argv += optind;
950*3ccb1966SGarrett D'Amore 
951*3ccb1966SGarrett D'Amore 	if (argc != 2) {
952*3ccb1966SGarrett D'Amore 		help();
953*3ccb1966SGarrett D'Amore 		return (-1);
954*3ccb1966SGarrett D'Amore 	}
955*3ccb1966SGarrett D'Amore 	cname = argv[0];
956*3ccb1966SGarrett D'Amore 	value = argv[1];
957*3ccb1966SGarrett D'Amore 
958*3ccb1966SGarrett D'Amore 	if ((d = find_device(devname)) == NULL) {
959*3ccb1966SGarrett D'Amore 		return (ENODEV);
960*3ccb1966SGarrett D'Amore 	}
961*3ccb1966SGarrett D'Amore 
962*3ccb1966SGarrett D'Amore 	for (i = 0, found = 0; i < d->cmax; i++) {
963*3ccb1966SGarrett D'Amore 		cinfop = &d->controls[i];
964*3ccb1966SGarrett D'Amore 		n = strrchr(cinfop->ci.extname, '_');
965*3ccb1966SGarrett D'Amore 		n = n ? n + 1 : cinfop->ci.extname;
966*3ccb1966SGarrett D'Amore 		if (strcmp(cname, n) != 0) {
967*3ccb1966SGarrett D'Amore 			continue;
968*3ccb1966SGarrett D'Amore 		}
969*3ccb1966SGarrett D'Amore 		found = 1;
970*3ccb1966SGarrett D'Amore 		rv = set_device_control(d, cinfop, value, verbose);
971*3ccb1966SGarrett D'Amore 		rval = rval ? rval : rv;
972*3ccb1966SGarrett D'Amore 	}
973*3ccb1966SGarrett D'Amore 	if (!found) {
974*3ccb1966SGarrett D'Amore 		warn(_("No such control: %s\n"), cname);
975*3ccb1966SGarrett D'Amore 	}
976*3ccb1966SGarrett D'Amore 
977*3ccb1966SGarrett D'Amore 	return (rval);
978*3ccb1966SGarrett D'Amore }
979*3ccb1966SGarrett D'Amore 
980*3ccb1966SGarrett D'Amore int
981*3ccb1966SGarrett D'Amore do_save_controls(int argc, char **argv)
982*3ccb1966SGarrett D'Amore {
983*3ccb1966SGarrett D'Amore 	int		optc;
984*3ccb1966SGarrett D'Amore 	int		rval = 0;
985*3ccb1966SGarrett D'Amore 	device_t	*d;
986*3ccb1966SGarrett D'Amore 	char		*devname = NULL;
987*3ccb1966SGarrett D'Amore 	char		*fname;
988*3ccb1966SGarrett D'Amore 	int		i;
989*3ccb1966SGarrett D'Amore 	int		rv;
990*3ccb1966SGarrett D'Amore 	cinfo_t		*cinfop;
991*3ccb1966SGarrett D'Amore 	FILE		*fp;
992*3ccb1966SGarrett D'Amore 	int		fd;
993*3ccb1966SGarrett D'Amore 	int		mode;
994*3ccb1966SGarrett D'Amore 
995*3ccb1966SGarrett D'Amore 	mode = O_WRONLY | O_CREAT | O_EXCL;
996*3ccb1966SGarrett D'Amore 
997*3ccb1966SGarrett D'Amore 	while ((optc = getopt(argc, argv, "d:f")) != EOF) {
998*3ccb1966SGarrett D'Amore 		switch (optc) {
999*3ccb1966SGarrett D'Amore 		case 'd':
1000*3ccb1966SGarrett D'Amore 			devname = optarg;
1001*3ccb1966SGarrett D'Amore 			break;
1002*3ccb1966SGarrett D'Amore 		case 'f':
1003*3ccb1966SGarrett D'Amore 			mode &= ~O_EXCL;
1004*3ccb1966SGarrett D'Amore 			mode |= O_TRUNC;
1005*3ccb1966SGarrett D'Amore 			break;
1006*3ccb1966SGarrett D'Amore 		default:
1007*3ccb1966SGarrett D'Amore 			help();
1008*3ccb1966SGarrett D'Amore 			return (-1);
1009*3ccb1966SGarrett D'Amore 		}
1010*3ccb1966SGarrett D'Amore 	}
1011*3ccb1966SGarrett D'Amore 	argc -= optind;
1012*3ccb1966SGarrett D'Amore 	argv += optind;
1013*3ccb1966SGarrett D'Amore 
1014*3ccb1966SGarrett D'Amore 	if (argc != 1) {
1015*3ccb1966SGarrett D'Amore 		help();
1016*3ccb1966SGarrett D'Amore 		return (-1);
1017*3ccb1966SGarrett D'Amore 	}
1018*3ccb1966SGarrett D'Amore 	fname = argv[0];
1019*3ccb1966SGarrett D'Amore 
1020*3ccb1966SGarrett D'Amore 	if ((d = find_device(devname)) == NULL) {
1021*3ccb1966SGarrett D'Amore 		return (ENODEV);
1022*3ccb1966SGarrett D'Amore 	}
1023*3ccb1966SGarrett D'Amore 
1024*3ccb1966SGarrett D'Amore 	if ((fd = open(fname, mode, 0666)) < 0) {
1025*3ccb1966SGarrett D'Amore 		perror(_("Failed to create file"));
1026*3ccb1966SGarrett D'Amore 		return (errno);
1027*3ccb1966SGarrett D'Amore 	}
1028*3ccb1966SGarrett D'Amore 
1029*3ccb1966SGarrett D'Amore 	if ((fp = fdopen(fd, "w")) == NULL) {
1030*3ccb1966SGarrett D'Amore 		perror(_("Unable to open file\n"));
1031*3ccb1966SGarrett D'Amore 		(void) close(fd);
1032*3ccb1966SGarrett D'Amore 		(void) unlink(fname);
1033*3ccb1966SGarrett D'Amore 		return (errno);
1034*3ccb1966SGarrett D'Amore 	}
1035*3ccb1966SGarrett D'Amore 
1036*3ccb1966SGarrett D'Amore 	(void) fprintf(fp, "# Device: %s\n", d->mixer.devnode);
1037*3ccb1966SGarrett D'Amore 	(void) fprintf(fp, "# Name    = %s\n", d->card.shortname);
1038*3ccb1966SGarrett D'Amore 	(void) fprintf(fp, "# Config  = %s\n", d->card.longname);
1039*3ccb1966SGarrett D'Amore 
1040*3ccb1966SGarrett D'Amore 	if (strlen(d->card.hw_info)) {
1041*3ccb1966SGarrett D'Amore 		(void) fprintf(fp, "# HW Info = %s", d->card.hw_info);
1042*3ccb1966SGarrett D'Amore 	}
1043*3ccb1966SGarrett D'Amore 	(void) fprintf(fp, "#\n");
1044*3ccb1966SGarrett D'Amore 
1045*3ccb1966SGarrett D'Amore 	print_header(fp, 0);
1046*3ccb1966SGarrett D'Amore 
1047*3ccb1966SGarrett D'Amore 	for (i = 0; i < d->cmax; i++) {
1048*3ccb1966SGarrett D'Amore 		cinfop = &d->controls[i];
1049*3ccb1966SGarrett D'Amore 		rv = print_control(fp, d, cinfop, 0);
1050*3ccb1966SGarrett D'Amore 		rval = rval ? rval : rv;
1051*3ccb1966SGarrett D'Amore 	}
1052*3ccb1966SGarrett D'Amore 
1053*3ccb1966SGarrett D'Amore 	(void) fclose(fp);
1054*3ccb1966SGarrett D'Amore 
1055*3ccb1966SGarrett D'Amore 	return (rval);
1056*3ccb1966SGarrett D'Amore }
1057*3ccb1966SGarrett D'Amore 
1058*3ccb1966SGarrett D'Amore int
1059*3ccb1966SGarrett D'Amore do_load_controls(int argc, char **argv)
1060*3ccb1966SGarrett D'Amore {
1061*3ccb1966SGarrett D'Amore 	int	optc;
1062*3ccb1966SGarrett D'Amore 	int	rval = 0;
1063*3ccb1966SGarrett D'Amore 	device_t	*d;
1064*3ccb1966SGarrett D'Amore 	char		*devname = NULL;
1065*3ccb1966SGarrett D'Amore 	char	*fname;
1066*3ccb1966SGarrett D'Amore 	char	*cname;
1067*3ccb1966SGarrett D'Amore 	char	*value;
1068*3ccb1966SGarrett D'Amore 	int	i;
1069*3ccb1966SGarrett D'Amore 	int	rv;
1070*3ccb1966SGarrett D'Amore 	cinfo_t	*cinfop;
1071*3ccb1966SGarrett D'Amore 	FILE	*fp;
1072*3ccb1966SGarrett D'Amore 	char	linebuf[MAXLINE];
1073*3ccb1966SGarrett D'Amore 	int	lineno = 0;
1074*3ccb1966SGarrett D'Amore 	int	found;
1075*3ccb1966SGarrett D'Amore 
1076*3ccb1966SGarrett D'Amore 	while ((optc = getopt(argc, argv, "d:")) != EOF) {
1077*3ccb1966SGarrett D'Amore 		switch (optc) {
1078*3ccb1966SGarrett D'Amore 		case 'd':
1079*3ccb1966SGarrett D'Amore 			devname = optarg;
1080*3ccb1966SGarrett D'Amore 			break;
1081*3ccb1966SGarrett D'Amore 		default:
1082*3ccb1966SGarrett D'Amore 			help();
1083*3ccb1966SGarrett D'Amore 			return (-1);
1084*3ccb1966SGarrett D'Amore 		}
1085*3ccb1966SGarrett D'Amore 	}
1086*3ccb1966SGarrett D'Amore 	argc -= optind;
1087*3ccb1966SGarrett D'Amore 	argv += optind;
1088*3ccb1966SGarrett D'Amore 
1089*3ccb1966SGarrett D'Amore 	if (argc != 1) {
1090*3ccb1966SGarrett D'Amore 		help();
1091*3ccb1966SGarrett D'Amore 		return (-1);
1092*3ccb1966SGarrett D'Amore 	}
1093*3ccb1966SGarrett D'Amore 	fname = argv[0];
1094*3ccb1966SGarrett D'Amore 
1095*3ccb1966SGarrett D'Amore 	if ((d = find_device(devname)) == NULL) {
1096*3ccb1966SGarrett D'Amore 		return (ENODEV);
1097*3ccb1966SGarrett D'Amore 	}
1098*3ccb1966SGarrett D'Amore 
1099*3ccb1966SGarrett D'Amore 	if ((fp = fopen(fname, "r")) == NULL) {
1100*3ccb1966SGarrett D'Amore 		perror(_("Unable to open file"));
1101*3ccb1966SGarrett D'Amore 		return (errno);
1102*3ccb1966SGarrett D'Amore 	}
1103*3ccb1966SGarrett D'Amore 
1104*3ccb1966SGarrett D'Amore 	while (fgets(linebuf, sizeof (linebuf), fp) != NULL) {
1105*3ccb1966SGarrett D'Amore 		lineno++;
1106*3ccb1966SGarrett D'Amore 		if (linebuf[strlen(linebuf) - 1] != '\n') {
1107*3ccb1966SGarrett D'Amore 			warn(_("Warning: line too long at line %d\n"), lineno);
1108*3ccb1966SGarrett D'Amore 			/* read in the rest of the line and discard it */
1109*3ccb1966SGarrett D'Amore 			while (fgets(linebuf, sizeof (linebuf), fp) != NULL &&
1110*3ccb1966SGarrett D'Amore 			    (linebuf[strlen(linebuf) - 1] != '\n')) {
1111*3ccb1966SGarrett D'Amore 				continue;
1112*3ccb1966SGarrett D'Amore 			}
1113*3ccb1966SGarrett D'Amore 			continue;
1114*3ccb1966SGarrett D'Amore 		}
1115*3ccb1966SGarrett D'Amore 
1116*3ccb1966SGarrett D'Amore 		/* we have a good line ... */
1117*3ccb1966SGarrett D'Amore 		cname = strtok(linebuf, " \t\n");
1118*3ccb1966SGarrett D'Amore 		/* skip comments and blank lines */
1119*3ccb1966SGarrett D'Amore 		if ((cname == NULL) || (cname[0] == '#')) {
1120*3ccb1966SGarrett D'Amore 			continue;
1121*3ccb1966SGarrett D'Amore 		}
1122*3ccb1966SGarrett D'Amore 		value = strtok(NULL, " \t\n");
1123*3ccb1966SGarrett D'Amore 		if ((value == NULL) || (*cname == 0)) {
1124*3ccb1966SGarrett D'Amore 			warn(_("Warning: missing value at line %d\n"), lineno);
1125*3ccb1966SGarrett D'Amore 			continue;
1126*3ccb1966SGarrett D'Amore 		}
1127*3ccb1966SGarrett D'Amore 
1128*3ccb1966SGarrett D'Amore 		for (i = 0, found = 0; i < d->cmax; i++) {
1129*3ccb1966SGarrett D'Amore 			/* save and restore requires an exact match */
1130*3ccb1966SGarrett D'Amore 			cinfop = &d->controls[i];
1131*3ccb1966SGarrett D'Amore 			if (strcmp(cinfop->ci.extname, cname) != 0) {
1132*3ccb1966SGarrett D'Amore 				continue;
1133*3ccb1966SGarrett D'Amore 			}
1134*3ccb1966SGarrett D'Amore 			found = 1;
1135*3ccb1966SGarrett D'Amore 			rv = set_device_control(d, cinfop, value, 0);
1136*3ccb1966SGarrett D'Amore 			rval = rval ? rval : rv;
1137*3ccb1966SGarrett D'Amore 		}
1138*3ccb1966SGarrett D'Amore 		if (!found) {
1139*3ccb1966SGarrett D'Amore 			warn(_("No such control: %s\n"), cname);
1140*3ccb1966SGarrett D'Amore 		}
1141*3ccb1966SGarrett D'Amore 	}
1142*3ccb1966SGarrett D'Amore 	(void) fclose(fp);
1143*3ccb1966SGarrett D'Amore 
1144*3ccb1966SGarrett D'Amore 	return (rval);
1145*3ccb1966SGarrett D'Amore }
1146*3ccb1966SGarrett D'Amore 
1147*3ccb1966SGarrett D'Amore int
1148*3ccb1966SGarrett D'Amore main(int argc, char **argv)
1149*3ccb1966SGarrett D'Amore {
1150*3ccb1966SGarrett D'Amore 	int rv = 0;
1151*3ccb1966SGarrett D'Amore 	int opt;
1152*3ccb1966SGarrett D'Amore 
1153*3ccb1966SGarrett D'Amore 	(void) setlocale(LC_ALL, "");
1154*3ccb1966SGarrett D'Amore 	(void) textdomain(TEXT_DOMAIN);
1155*3ccb1966SGarrett D'Amore 
1156*3ccb1966SGarrett D'Amore 	while ((opt = getopt(argc, argv, "h")) != EOF) {
1157*3ccb1966SGarrett D'Amore 		switch (opt) {
1158*3ccb1966SGarrett D'Amore 		case 'h':
1159*3ccb1966SGarrett D'Amore 			help();
1160*3ccb1966SGarrett D'Amore 			rv = 0;
1161*3ccb1966SGarrett D'Amore 			goto OUT;
1162*3ccb1966SGarrett D'Amore 		default:
1163*3ccb1966SGarrett D'Amore 			rv = EINVAL;
1164*3ccb1966SGarrett D'Amore 			break;
1165*3ccb1966SGarrett D'Amore 		}
1166*3ccb1966SGarrett D'Amore 	}
1167*3ccb1966SGarrett D'Amore 
1168*3ccb1966SGarrett D'Amore 	if (rv) {
1169*3ccb1966SGarrett D'Amore 		goto OUT;
1170*3ccb1966SGarrett D'Amore 	}
1171*3ccb1966SGarrett D'Amore 
1172*3ccb1966SGarrett D'Amore 	argc -= optind;
1173*3ccb1966SGarrett D'Amore 	argv += optind;
1174*3ccb1966SGarrett D'Amore 
1175*3ccb1966SGarrett D'Amore 	rv = load_devices();
1176*3ccb1966SGarrett D'Amore 	if (rv != 0) {
1177*3ccb1966SGarrett D'Amore 		goto OUT;
1178*3ccb1966SGarrett D'Amore 	}
1179*3ccb1966SGarrett D'Amore 
1180*3ccb1966SGarrett D'Amore 	if (argc < 1) {
1181*3ccb1966SGarrett D'Amore 		help();
1182*3ccb1966SGarrett D'Amore 		rv = EINVAL;
1183*3ccb1966SGarrett D'Amore 	} else if (strcmp(argv[0], "help") == 0) {
1184*3ccb1966SGarrett D'Amore 		help();
1185*3ccb1966SGarrett D'Amore 		rv = 0;
1186*3ccb1966SGarrett D'Amore 	} else if (strcmp(argv[0], "list-devices") == 0) {
1187*3ccb1966SGarrett D'Amore 		rv = do_list_devices(argc, argv);
1188*3ccb1966SGarrett D'Amore 	} else if (strcmp(argv[0], "show-device") == 0) {
1189*3ccb1966SGarrett D'Amore 		rv = do_show_device(argc, argv);
1190*3ccb1966SGarrett D'Amore 	} else if (strcmp(argv[0], "show-control") == 0) {
1191*3ccb1966SGarrett D'Amore 		rv = do_show_control(argc, argv);
1192*3ccb1966SGarrett D'Amore 	} else if (strcmp(argv[0], "set-control") == 0) {
1193*3ccb1966SGarrett D'Amore 		rv = do_set_control(argc, argv);
1194*3ccb1966SGarrett D'Amore 	} else if (strcmp(argv[0], "load-controls") == 0) {
1195*3ccb1966SGarrett D'Amore 		rv = do_load_controls(argc, argv);
1196*3ccb1966SGarrett D'Amore 	} else if (strcmp(argv[0], "save-controls") == 0) {
1197*3ccb1966SGarrett D'Amore 		rv = do_save_controls(argc, argv);
1198*3ccb1966SGarrett D'Amore 	} else {
1199*3ccb1966SGarrett D'Amore 		help();
1200*3ccb1966SGarrett D'Amore 		rv = EINVAL;
1201*3ccb1966SGarrett D'Amore 	}
1202*3ccb1966SGarrett D'Amore 
1203*3ccb1966SGarrett D'Amore OUT:
1204*3ccb1966SGarrett D'Amore 	free_devices();
1205*3ccb1966SGarrett D'Amore 	return (rv);
1206*3ccb1966SGarrett D'Amore }
1207