188447a05SGarrett D'Amore /*
288447a05SGarrett D'Amore  * CDDL HEADER START
388447a05SGarrett D'Amore  *
488447a05SGarrett D'Amore  * The contents of this file are subject to the terms of the
588447a05SGarrett D'Amore  * Common Development and Distribution License (the "License").
688447a05SGarrett D'Amore  * You may not use this file except in compliance with the License.
788447a05SGarrett D'Amore  *
888447a05SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988447a05SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
1088447a05SGarrett D'Amore  * See the License for the specific language governing permissions
1188447a05SGarrett D'Amore  * and limitations under the License.
1288447a05SGarrett D'Amore  *
1388447a05SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
1488447a05SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588447a05SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
1688447a05SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
1788447a05SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
1888447a05SGarrett D'Amore  *
1988447a05SGarrett D'Amore  * CDDL HEADER END
2088447a05SGarrett D'Amore  */
2188447a05SGarrett D'Amore /*
22*2c30fa45SGarrett D'Amore  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2388447a05SGarrett D'Amore  */
2488447a05SGarrett D'Amore 
2588447a05SGarrett D'Amore #include <sys/types.h>
2688447a05SGarrett D'Amore #include <sys/open.h>
2788447a05SGarrett D'Amore #include <sys/errno.h>
2888447a05SGarrett D'Amore #include <sys/ddi.h>
2988447a05SGarrett D'Amore #include <sys/sunddi.h>
3088447a05SGarrett D'Amore #include <sys/audio/audio_oss.h>
3188447a05SGarrett D'Amore #include <sys/file.h>
3288447a05SGarrett D'Amore #include <sys/note.h>
3388447a05SGarrett D'Amore #include <sys/sysmacros.h>
3488447a05SGarrett D'Amore #include <sys/list.h>
3588447a05SGarrett D'Amore #include "audio_client.h"
3688447a05SGarrett D'Amore 
3788447a05SGarrett D'Amore #define	OSS_FMT		AFMT_S16_LE
3888447a05SGarrett D'Amore #define	OSS_RATE	48000
3988447a05SGarrett D'Amore #define	OSS_CHANNELS	2
4088447a05SGarrett D'Amore 
4188447a05SGarrett D'Amore typedef struct ossclient ossclient_t;
4288447a05SGarrett D'Amore typedef struct ossdev ossdev_t;
4388447a05SGarrett D'Amore 
4488447a05SGarrett D'Amore static const struct {
4588447a05SGarrett D'Amore 	int	oss;
4688447a05SGarrett D'Amore 	int	fmt;
4788447a05SGarrett D'Amore } oss_formats[] = {
4888447a05SGarrett D'Amore 	{ AFMT_MU_LAW,		AUDIO_FORMAT_ULAW },
4988447a05SGarrett D'Amore 	{ AFMT_A_LAW,		AUDIO_FORMAT_ALAW },
5088447a05SGarrett D'Amore 	{ AFMT_U8,		AUDIO_FORMAT_U8 },
5188447a05SGarrett D'Amore 	{ AFMT_S8,		AUDIO_FORMAT_S8 },
5288447a05SGarrett D'Amore 	{ AFMT_S16_BE,		AUDIO_FORMAT_S16_BE },
5388447a05SGarrett D'Amore 	{ AFMT_S16_LE,		AUDIO_FORMAT_S16_LE },
5488447a05SGarrett D'Amore 	{ AFMT_U16_BE,		AUDIO_FORMAT_U16_BE },
5588447a05SGarrett D'Amore 	{ AFMT_U16_LE,		AUDIO_FORMAT_U16_LE },
5688447a05SGarrett D'Amore 	{ AFMT_S24_BE,		AUDIO_FORMAT_S24_BE },
5788447a05SGarrett D'Amore 	{ AFMT_S24_LE,		AUDIO_FORMAT_S24_LE },
5888447a05SGarrett D'Amore 	{ AFMT_S32_BE,		AUDIO_FORMAT_S32_BE },
5988447a05SGarrett D'Amore 	{ AFMT_S32_LE,		AUDIO_FORMAT_S32_LE },
6088447a05SGarrett D'Amore 	{ AFMT_S24_PACKED,	AUDIO_FORMAT_S24_PACKED },
6188447a05SGarrett D'Amore 	{ AFMT_AC3,		AUDIO_FORMAT_AC3 },
6288447a05SGarrett D'Amore 	{ AFMT_QUERY,		AUDIO_FORMAT_NONE }
6388447a05SGarrett D'Amore };
6488447a05SGarrett D'Amore 
6588447a05SGarrett D'Amore /* common structure shared between both mixer and dsp nodes */
6688447a05SGarrett D'Amore struct ossclient {
6788447a05SGarrett D'Amore 	ossdev_t		*o_ossdev;
6888447a05SGarrett D'Amore 	audio_client_t		*o_client;
6988447a05SGarrett D'Amore 	/* sndstat */
7088447a05SGarrett D'Amore 	kmutex_t		o_ss_lock;
7188447a05SGarrett D'Amore 	char			*o_ss_buf;
7288447a05SGarrett D'Amore 	size_t			o_ss_len;
7388447a05SGarrett D'Amore 	size_t			o_ss_sz;
7488447a05SGarrett D'Amore 	size_t			o_ss_off;
7588447a05SGarrett D'Amore };
7688447a05SGarrett D'Amore 
7788447a05SGarrett D'Amore struct ossdev {
7888447a05SGarrett D'Amore 	audio_dev_t		*d_dev;
7988447a05SGarrett D'Amore 
8088447a05SGarrett D'Amore 	uint_t			d_nctrl;	/* num actual controls */
8188447a05SGarrett D'Amore 	uint_t			d_nalloc;	/* num allocated controls */
8288447a05SGarrett D'Amore 	audio_ctrl_t		**d_ctrls;	/* array of control handles */
8388447a05SGarrett D'Amore 	oss_mixext		*d_exts;	/* array of mixer descs */
8488447a05SGarrett D'Amore 
8588447a05SGarrett D'Amore 	int			d_play_grp;
8688447a05SGarrett D'Amore 	int			d_rec_grp;
8788447a05SGarrett D'Amore 	int			d_mon_grp;
8888447a05SGarrett D'Amore 	int			d_misc_grp;
8988447a05SGarrett D'Amore 
9088447a05SGarrett D'Amore 	kmutex_t		d_mx;
9188447a05SGarrett D'Amore 	kcondvar_t		d_cv;
9288447a05SGarrett D'Amore };
9388447a05SGarrett D'Amore 
9488447a05SGarrett D'Amore static int
oss_cnt_controls(audio_ctrl_t * ctrl,void * arg)9588447a05SGarrett D'Amore oss_cnt_controls(audio_ctrl_t *ctrl, void *arg)
9688447a05SGarrett D'Amore {
9788447a05SGarrett D'Amore 	int			*pint = (int *)arg;
9888447a05SGarrett D'Amore 	int			cnt;
9988447a05SGarrett D'Amore 	audio_ctrl_desc_t	desc;
10088447a05SGarrett D'Amore 
10188447a05SGarrett D'Amore 	cnt = *pint;
10288447a05SGarrett D'Amore 	cnt++;
10388447a05SGarrett D'Amore 	*pint = cnt;
10488447a05SGarrett D'Amore 
10588447a05SGarrett D'Amore 	if (auclnt_control_describe(ctrl, &desc) != 0)
10688447a05SGarrett D'Amore 		return (AUDIO_WALK_CONTINUE);
10788447a05SGarrett D'Amore 
10888447a05SGarrett D'Amore 	if (desc.acd_flags & AUDIO_CTRL_FLAG_MULTI) {
10988447a05SGarrett D'Amore 		for (uint64_t mask = desc.acd_maxvalue; mask; mask >>= 1) {
11088447a05SGarrett D'Amore 			if (mask & 1) {
11188447a05SGarrett D'Amore 				cnt++;
11288447a05SGarrett D'Amore 			}
11388447a05SGarrett D'Amore 		}
11488447a05SGarrett D'Amore 		*pint = cnt;
11588447a05SGarrett D'Amore 	}
11688447a05SGarrett D'Amore 
11788447a05SGarrett D'Amore 	return (AUDIO_WALK_CONTINUE);
11888447a05SGarrett D'Amore }
11988447a05SGarrett D'Amore 
12088447a05SGarrett D'Amore /*
12188447a05SGarrett D'Amore  * Add one entry to the OSS user control table to internal control
12288447a05SGarrett D'Amore  * helper table.
12388447a05SGarrett D'Amore  *
12488447a05SGarrett D'Amore  * This is used with auimpl_walk_controls. The table must be pre-
12588447a05SGarrett D'Amore  * allocated before it is walk'd. This includes the root and
12688447a05SGarrett D'Amore  * extended control markers!
12788447a05SGarrett D'Amore  */
12888447a05SGarrett D'Amore static int
oss_add_control(audio_ctrl_t * ctrl,void * arg)12988447a05SGarrett D'Amore oss_add_control(audio_ctrl_t *ctrl, void *arg)
13088447a05SGarrett D'Amore {
13188447a05SGarrett D'Amore 	ossdev_t		*odev = arg;
13288447a05SGarrett D'Amore 	audio_ctrl_desc_t	desc;
13388447a05SGarrett D'Amore 	oss_mixext		*ext;
13488447a05SGarrett D'Amore 	int			bit;
13588447a05SGarrett D'Amore 	uint64_t		mask;
13688447a05SGarrett D'Amore 	const char		*name;
13788447a05SGarrett D'Amore 	int			parent;
13888447a05SGarrett D'Amore 	int			flags;
13988447a05SGarrett D'Amore 	unsigned		scope;
14088447a05SGarrett D'Amore 
14188447a05SGarrett D'Amore 	if (auclnt_control_describe(ctrl, &desc))
14288447a05SGarrett D'Amore 		return (AUDIO_WALK_CONTINUE);
14388447a05SGarrett D'Amore 
14488447a05SGarrett D'Amore 	parent = 0;
14588447a05SGarrett D'Amore 
14688447a05SGarrett D'Amore 	/*
14788447a05SGarrett D'Amore 	 * Add appropriate group if not already done so.
14888447a05SGarrett D'Amore 	 */
14988447a05SGarrett D'Amore 	if (desc.acd_flags & AUDIO_CTRL_FLAG_PLAY) {
15088447a05SGarrett D'Amore 		if (!odev->d_play_grp) {
15188447a05SGarrett D'Amore 			ext = &odev->d_exts[odev->d_nctrl];
15288447a05SGarrett D'Amore 			ext->ctrl = odev->d_nctrl;
15388447a05SGarrett D'Amore 			ext->control_no = -1;
15488447a05SGarrett D'Amore 			ext->type = MIXT_GROUP;
15588447a05SGarrett D'Amore 			ext->desc = MIXEXT_SCOPE_OUTPUT;
15688447a05SGarrett D'Amore 			ext->timestamp = gethrtime();
15788447a05SGarrett D'Amore 			(void) snprintf(ext->id, sizeof (ext->id), "PLAYBACK");
15888447a05SGarrett D'Amore 			odev->d_play_grp = odev->d_nctrl;
15988447a05SGarrett D'Amore 			odev->d_nctrl++;
16088447a05SGarrett D'Amore 		}
16188447a05SGarrett D'Amore 		scope = MIXEXT_SCOPE_OUTPUT;
16288447a05SGarrett D'Amore 		parent = odev->d_play_grp;
16388447a05SGarrett D'Amore 	} else if (desc.acd_flags & AUDIO_CTRL_FLAG_REC) {
16488447a05SGarrett D'Amore 		if (!odev->d_rec_grp) {
16588447a05SGarrett D'Amore 			ext = &odev->d_exts[odev->d_nctrl];
16688447a05SGarrett D'Amore 			ext->ctrl = odev->d_nctrl;
16788447a05SGarrett D'Amore 			ext->control_no = -1;
16888447a05SGarrett D'Amore 			ext->type = MIXT_GROUP;
16988447a05SGarrett D'Amore 			ext->desc = MIXEXT_SCOPE_INPUT;
17088447a05SGarrett D'Amore 			ext->timestamp = gethrtime();
17188447a05SGarrett D'Amore 			(void) snprintf(ext->id, sizeof (ext->id), "RECORD");
17288447a05SGarrett D'Amore 			odev->d_rec_grp = odev->d_nctrl;
17388447a05SGarrett D'Amore 			odev->d_nctrl++;
17488447a05SGarrett D'Amore 		}
17588447a05SGarrett D'Amore 		scope = MIXEXT_SCOPE_INPUT;
17688447a05SGarrett D'Amore 		parent = odev->d_rec_grp;
17788447a05SGarrett D'Amore 	} else if (desc.acd_flags & AUDIO_CTRL_FLAG_MONITOR) {
17888447a05SGarrett D'Amore 		if (!odev->d_mon_grp) {
17988447a05SGarrett D'Amore 			ext = &odev->d_exts[odev->d_nctrl];
18088447a05SGarrett D'Amore 			ext->ctrl = odev->d_nctrl;
18188447a05SGarrett D'Amore 			ext->control_no = -1;
18288447a05SGarrett D'Amore 			ext->type = MIXT_GROUP;
18388447a05SGarrett D'Amore 			ext->desc = MIXEXT_SCOPE_MONITOR;
18488447a05SGarrett D'Amore 			ext->timestamp = gethrtime();
18588447a05SGarrett D'Amore 			(void) snprintf(ext->id, sizeof (ext->id), "MONITOR");
18688447a05SGarrett D'Amore 			odev->d_mon_grp = odev->d_nctrl;
18788447a05SGarrett D'Amore 			odev->d_nctrl++;
18888447a05SGarrett D'Amore 		}
18988447a05SGarrett D'Amore 		scope = MIXEXT_SCOPE_MONITOR;
19088447a05SGarrett D'Amore 		parent = odev->d_mon_grp;
19188447a05SGarrett D'Amore 	} else {
19288447a05SGarrett D'Amore 		if (!odev->d_misc_grp) {
19388447a05SGarrett D'Amore 			ext = &odev->d_exts[odev->d_nctrl];
19488447a05SGarrett D'Amore 			ext->ctrl = odev->d_nctrl;
19588447a05SGarrett D'Amore 			ext->control_no = -1;
19688447a05SGarrett D'Amore 			ext->type = MIXT_GROUP;
19788447a05SGarrett D'Amore 			ext->desc = MIXEXT_SCOPE_OTHER;
19888447a05SGarrett D'Amore 			ext->timestamp = gethrtime();
19988447a05SGarrett D'Amore 			(void) snprintf(ext->id, sizeof (ext->id), "MISC");
20088447a05SGarrett D'Amore 			odev->d_misc_grp = odev->d_nctrl;
20188447a05SGarrett D'Amore 			odev->d_nctrl++;
20288447a05SGarrett D'Amore 		}
20388447a05SGarrett D'Amore 		scope = MIXEXT_SCOPE_OTHER;
20488447a05SGarrett D'Amore 		parent = odev->d_misc_grp;
20588447a05SGarrett D'Amore 	}
20688447a05SGarrett D'Amore 
20788447a05SGarrett D'Amore 	name = desc.acd_name ? desc.acd_name : "";
20888447a05SGarrett D'Amore 
20988447a05SGarrett D'Amore 	if (desc.acd_flags & AUDIO_CTRL_FLAG_MULTI) {
21088447a05SGarrett D'Amore 		ext = &odev->d_exts[odev->d_nctrl];
21188447a05SGarrett D'Amore 		ext->ctrl = odev->d_nctrl;
21288447a05SGarrett D'Amore 		ext->control_no = -1;
21388447a05SGarrett D'Amore 		ext->type = MIXT_GROUP;
21488447a05SGarrett D'Amore 		ext->timestamp = gethrtime();
21588447a05SGarrett D'Amore 		ext->parent = parent;
21688447a05SGarrett D'Amore 		ext->desc = scope;
21788447a05SGarrett D'Amore 		(void) snprintf(ext->id, sizeof (ext->id), "%s", name);
21888447a05SGarrett D'Amore 		(void) snprintf(ext->extname, sizeof (ext->extname),
21988447a05SGarrett D'Amore 		    "%s", name);
22088447a05SGarrett D'Amore 		parent = odev->d_nctrl++;
22188447a05SGarrett D'Amore 	}
22288447a05SGarrett D'Amore 
22388447a05SGarrett D'Amore 	/* Next available open entry */
22488447a05SGarrett D'Amore 	ext = &odev->d_exts[odev->d_nctrl];
22588447a05SGarrett D'Amore 
22688447a05SGarrett D'Amore 	/* Record the underlying control handle */
22788447a05SGarrett D'Amore 	odev->d_ctrls[odev->d_nctrl] = ctrl;
22888447a05SGarrett D'Amore 
22988447a05SGarrett D'Amore 	/*
23088447a05SGarrett D'Amore 	 * Now setup the oss entry
23188447a05SGarrett D'Amore 	 */
23288447a05SGarrett D'Amore 
23388447a05SGarrett D'Amore 	ext->ctrl = odev->d_nctrl;
23488447a05SGarrett D'Amore 	ext->control_no = -1;
23588447a05SGarrett D'Amore 	ext->maxvalue = (int)desc.acd_maxvalue;
23688447a05SGarrett D'Amore 	ext->minvalue = (int)desc.acd_minvalue;
23788447a05SGarrett D'Amore 	ext->timestamp = gethrtime();
23888447a05SGarrett D'Amore 	ext->parent = parent;
23988447a05SGarrett D'Amore 	ext->desc = scope;
24088447a05SGarrett D'Amore 	/* all controls should be pollable for now */
24188447a05SGarrett D'Amore 	flags = MIXF_POLL;
24288447a05SGarrett D'Amore 
24388447a05SGarrett D'Amore 	/*
24488447a05SGarrett D'Amore 	 * The following flags are intended to help out applications
24588447a05SGarrett D'Amore 	 * which need to figure out where to place certain controls.
24688447a05SGarrett D'Amore 	 * A few further words of guidance:
24788447a05SGarrett D'Amore 	 *
24888447a05SGarrett D'Amore 	 * Apps that just want a single master volume control should
24988447a05SGarrett D'Amore 	 * adjust the control(s) that are labelled with MIXF_PCMVOL if
25088447a05SGarrett D'Amore 	 * present.  They can fall back to adjusting all MAINVOL
25188447a05SGarrett D'Amore 	 * levels instead, if no PCMVOL is present.
25288447a05SGarrett D'Amore 	 *
25388447a05SGarrett D'Amore 	 * Controls that are one type on a certain device might be a
25488447a05SGarrett D'Amore 	 * different type on another device.  For example,
25588447a05SGarrett D'Amore 	 * audiopci/ak4531 can adjust input gains for individual
25688447a05SGarrett D'Amore 	 * levels, but lacks a master record gain.  AC'97, on the
25788447a05SGarrett D'Amore 	 * other hand, has individual monitor gains for inputs, but
25888447a05SGarrett D'Amore 	 * only a single master recording gain.
25988447a05SGarrett D'Amore 	 */
26088447a05SGarrett D'Amore 	if (desc.acd_flags & AUDIO_CTRL_FLAG_READABLE)
26188447a05SGarrett D'Amore 		flags |= MIXF_READABLE;
26288447a05SGarrett D'Amore 	if (desc.acd_flags & AUDIO_CTRL_FLAG_WRITEABLE)
26388447a05SGarrett D'Amore 		flags |= MIXF_WRITEABLE;
26488447a05SGarrett D'Amore 	if (desc.acd_flags & AUDIO_CTRL_FLAG_CENTIBEL)
26588447a05SGarrett D'Amore 		flags |= MIXF_CENTIBEL;
26688447a05SGarrett D'Amore 	if (desc.acd_flags & AUDIO_CTRL_FLAG_DECIBEL)
26788447a05SGarrett D'Amore 		flags |= MIXF_DECIBEL;
26888447a05SGarrett D'Amore 	if (desc.acd_flags & AUDIO_CTRL_FLAG_MAINVOL)
26988447a05SGarrett D'Amore 		flags |= MIXF_MAINVOL;
27088447a05SGarrett D'Amore 	if (desc.acd_flags & AUDIO_CTRL_FLAG_PCMVOL)
27188447a05SGarrett D'Amore 		flags |= MIXF_PCMVOL;
27288447a05SGarrett D'Amore 	if (desc.acd_flags & AUDIO_CTRL_FLAG_RECVOL)
27388447a05SGarrett D'Amore 		flags |= MIXF_RECVOL;
27488447a05SGarrett D'Amore 	if (desc.acd_flags & AUDIO_CTRL_FLAG_MONVOL)
27588447a05SGarrett D'Amore 		flags |= MIXF_MONVOL;
27688447a05SGarrett D'Amore 	ext->flags = flags;
27788447a05SGarrett D'Amore 
27888447a05SGarrett D'Amore 	(void) snprintf(ext->id, sizeof (ext->id), "%s", name);
27988447a05SGarrett D'Amore 
28088447a05SGarrett D'Amore 	/*
28188447a05SGarrett D'Amore 	 * For now just use the same extname as the real name.
28288447a05SGarrett D'Amore 	 */
28388447a05SGarrett D'Amore 	(void) snprintf(ext->extname, sizeof (ext->extname), name);
28488447a05SGarrett D'Amore 
28588447a05SGarrett D'Amore 	/*
28688447a05SGarrett D'Amore 	 * Now we deal with various control types.
28788447a05SGarrett D'Amore 	 */
28888447a05SGarrett D'Amore 	switch (desc.acd_type) {
28988447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_BOOLEAN:
29088447a05SGarrett D'Amore 		ext->type = MIXT_ONOFF;
29188447a05SGarrett D'Amore 		ext->enumbit = -1;
29288447a05SGarrett D'Amore 		break;
29388447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_STEREO:
29488447a05SGarrett D'Amore 		ext->type = MIXT_STEREOSLIDER;
29588447a05SGarrett D'Amore 		break;
29688447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_MONO:
29788447a05SGarrett D'Amore 		ext->type = MIXT_MONOSLIDER;
29888447a05SGarrett D'Amore 		break;
29988447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_ENUM:
30088447a05SGarrett D'Amore 
30188447a05SGarrett D'Amore 		if (desc.acd_flags & AUDIO_CTRL_FLAG_MULTI) {
30288447a05SGarrett D'Amore 			/*
30388447a05SGarrett D'Amore 			 * We turn AUDIO_CTRL_FLAG_MULTI into a group
30488447a05SGarrett D'Amore 			 * of checkboxes, since OSS can't represent it
30588447a05SGarrett D'Amore 			 * natively.
30688447a05SGarrett D'Amore 			 */
30788447a05SGarrett D'Amore 			mask = desc.acd_maxvalue;
30888447a05SGarrett D'Amore 			bit = 0;
30988447a05SGarrett D'Amore 			while (mask) {
31088447a05SGarrett D'Amore 				if (mask & 1) {
31188447a05SGarrett D'Amore 					ext = &odev->d_exts[odev->d_nctrl];
31288447a05SGarrett D'Amore 					(void) snprintf(ext->extname,
31388447a05SGarrett D'Amore 					    sizeof (ext->extname), "%s.%s",
31488447a05SGarrett D'Amore 					    name, desc.acd_enum[bit]);
31588447a05SGarrett D'Amore 					(void) snprintf(ext->id,
31688447a05SGarrett D'Amore 					    sizeof (ext->id), "%s",
31788447a05SGarrett D'Amore 					    desc.acd_enum[bit]);
31888447a05SGarrett D'Amore 					ext->ctrl = odev->d_nctrl;
31988447a05SGarrett D'Amore 					ext->control_no = -1;
32088447a05SGarrett D'Amore 					ext->parent = parent;
32188447a05SGarrett D'Amore 					ext->timestamp = gethrtime();
32288447a05SGarrett D'Amore 					ext->type = MIXT_ONOFF;
32388447a05SGarrett D'Amore 					ext->minvalue = 0;
32488447a05SGarrett D'Amore 					ext->maxvalue = 1;
32588447a05SGarrett D'Amore 					ext->enumbit = bit;
32688447a05SGarrett D'Amore 					ext->flags = flags;
32788447a05SGarrett D'Amore 					odev->d_ctrls[odev->d_nctrl] = ctrl;
32888447a05SGarrett D'Amore 					odev->d_nctrl++;
32988447a05SGarrett D'Amore 				}
33088447a05SGarrett D'Amore 				bit++;
33188447a05SGarrett D'Amore 				mask >>= 1;
33288447a05SGarrett D'Amore 			}
33388447a05SGarrett D'Amore 			return (AUDIO_WALK_CONTINUE);
33488447a05SGarrett D'Amore 		} else {
33588447a05SGarrett D'Amore 			/*
33688447a05SGarrett D'Amore 			 * NB: This is sufficient only for controls
33788447a05SGarrett D'Amore 			 * with a single value.  It cannot express the
33888447a05SGarrett D'Amore 			 * richer bitmask capabilities.
33988447a05SGarrett D'Amore 			 */
34088447a05SGarrett D'Amore 			ext->type = MIXT_ENUM;
34188447a05SGarrett D'Amore 			ext->minvalue = 0;
34288447a05SGarrett D'Amore 
34388447a05SGarrett D'Amore 			/*
34488447a05SGarrett D'Amore 			 * For an enumaration, we need to figure out
34588447a05SGarrett D'Amore 			 * which values are present, and set the
34688447a05SGarrett D'Amore 			 * appropriate mask and max value.
34788447a05SGarrett D'Amore 			 */
34888447a05SGarrett D'Amore 			bzero(ext->enum_present, sizeof (ext->enum_present));
34988447a05SGarrett D'Amore 			mask = desc.acd_maxvalue;
35088447a05SGarrett D'Amore 			bit = 0;
35188447a05SGarrett D'Amore 			while (mask) {
35288447a05SGarrett D'Amore 				if (mask & 1) {
35388447a05SGarrett D'Amore 					ext->enum_present[bit / 8] |=
35488447a05SGarrett D'Amore 					    (1 << (bit % 8));
35588447a05SGarrett D'Amore 				}
35688447a05SGarrett D'Amore 				mask >>= 1;
35788447a05SGarrett D'Amore 				bit++;
35888447a05SGarrett D'Amore 			}
35988447a05SGarrett D'Amore 			ext->maxvalue = bit;
36088447a05SGarrett D'Amore 		}
36188447a05SGarrett D'Amore 		break;
36288447a05SGarrett D'Amore 
36388447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_METER:
36488447a05SGarrett D'Amore 	default:
36588447a05SGarrett D'Amore 		/* Its an unknown or unsupported (for now) control, skip */
36688447a05SGarrett D'Amore 		return (AUDIO_WALK_CONTINUE);
36788447a05SGarrett D'Amore 	}
36888447a05SGarrett D'Amore 
36988447a05SGarrett D'Amore 	odev->d_nctrl++;
37088447a05SGarrett D'Amore 
37188447a05SGarrett D'Amore 	return (AUDIO_WALK_CONTINUE);
37288447a05SGarrett D'Amore }
37388447a05SGarrett D'Amore 
37488447a05SGarrett D'Amore /*
37588447a05SGarrett D'Amore  * Free up an OSS user land control to internal control,
37688447a05SGarrett D'Amore  * helper table.
37788447a05SGarrett D'Amore  */
37888447a05SGarrett D'Amore static void
oss_free_controls(ossdev_t * odev)37988447a05SGarrett D'Amore oss_free_controls(ossdev_t *odev)
38088447a05SGarrett D'Amore {
38188447a05SGarrett D'Amore 	kmem_free(odev->d_ctrls, sizeof (audio_ctrl_t *) * odev->d_nalloc);
38288447a05SGarrett D'Amore 	kmem_free(odev->d_exts, sizeof (oss_mixext) * odev->d_nalloc);
38388447a05SGarrett D'Amore 	odev->d_nctrl = 0;
38488447a05SGarrett D'Amore 	odev->d_nalloc = 0;
38588447a05SGarrett D'Amore }
38688447a05SGarrett D'Amore 
38788447a05SGarrett D'Amore /*
38888447a05SGarrett D'Amore  * Allocate and fill in an OSS user land controls to internal controls
38988447a05SGarrett D'Amore  * helper table. This is done on one audio_dev device.
39088447a05SGarrett D'Amore  */
39188447a05SGarrett D'Amore static void
oss_alloc_controls(ossdev_t * odev)39288447a05SGarrett D'Amore oss_alloc_controls(ossdev_t *odev)
39388447a05SGarrett D'Amore {
39488447a05SGarrett D'Amore 	audio_dev_t		*d = odev->d_dev;
39588447a05SGarrett D'Amore 	int			nctrl = 0;
39688447a05SGarrett D'Amore 	oss_mixext		*ext;
39788447a05SGarrett D'Amore 	oss_mixext_root		*root_data;
39888447a05SGarrett D'Amore 
39988447a05SGarrett D'Amore 	/* Find out who many entries we need */
40088447a05SGarrett D'Amore 	auclnt_walk_controls(d, oss_cnt_controls, &nctrl);
40188447a05SGarrett D'Amore 	nctrl++;		/* Needs space for the device root node */
40288447a05SGarrett D'Amore 	nctrl++;		/* Needs space for the device ext marker */
40388447a05SGarrett D'Amore 	nctrl++;		/* Needs space for the play group */
40488447a05SGarrett D'Amore 	nctrl++;		/* Needs space for the record group */
40588447a05SGarrett D'Amore 	nctrl++;		/* Needs space for the monitor group */
40688447a05SGarrett D'Amore 	nctrl++;		/* Needs space for the tone group */
40788447a05SGarrett D'Amore 	nctrl++;		/* Needs space for the 3D group */
40888447a05SGarrett D'Amore 	nctrl++;		/* Needs space for the misc group */
40988447a05SGarrett D'Amore 
41088447a05SGarrett D'Amore 	/* Allocate the OSS to boomer helper table */
41188447a05SGarrett D'Amore 	odev->d_nalloc = nctrl;
41288447a05SGarrett D'Amore 	odev->d_ctrls = kmem_zalloc(sizeof (audio_ctrl_t *) * nctrl, KM_SLEEP);
41388447a05SGarrett D'Amore 	odev->d_exts = kmem_zalloc(sizeof (oss_mixext) * nctrl, KM_SLEEP);
41488447a05SGarrett D'Amore 
41588447a05SGarrett D'Amore 	/*
41688447a05SGarrett D'Amore 	 * Setup special case outputs to output OSS routes helper tables
41788447a05SGarrett D'Amore 	 */
41888447a05SGarrett D'Amore 
41988447a05SGarrett D'Amore 	/*
42088447a05SGarrett D'Amore 	 * Root node is first, that way all others parent is this one
42188447a05SGarrett D'Amore 	 */
42288447a05SGarrett D'Amore 	ext = &odev->d_exts[odev->d_nctrl];
42388447a05SGarrett D'Amore 	ext->ctrl = 0;
42488447a05SGarrett D'Amore 	ext->parent = -1;
42588447a05SGarrett D'Amore 	ext->type = MIXT_DEVROOT;
42688447a05SGarrett D'Amore 	ext->timestamp = gethrtime();
42788447a05SGarrett D'Amore 	(void) snprintf(ext->id, sizeof (ext->id), "DEVROOT");
42888447a05SGarrett D'Amore 	/*
42988447a05SGarrett D'Amore 	 * Root data... nobody should be using this though.
43088447a05SGarrett D'Amore 	 */
43188447a05SGarrett D'Amore 	root_data = (oss_mixext_root *)&ext->data;
43288447a05SGarrett D'Amore 	(void) snprintf(root_data->name, sizeof (root_data->name), "%s",
43388447a05SGarrett D'Amore 	    auclnt_get_dev_name(d));
43488447a05SGarrett D'Amore 	(void) snprintf(root_data->id, sizeof (root_data->id), "%s",
43588447a05SGarrett D'Amore 	    auclnt_get_dev_name(d));
43688447a05SGarrett D'Amore 
43788447a05SGarrett D'Amore 	odev->d_nctrl++;
43888447a05SGarrett D'Amore 
43988447a05SGarrett D'Amore 	/*
44088447a05SGarrett D'Amore 	 * Insert an extra marker -- needed to keep layout apps hapy.
44188447a05SGarrett D'Amore 	 * This prevents some apps from assuming we are in "LEGACY" mode.
44288447a05SGarrett D'Amore 	 */
44388447a05SGarrett D'Amore 	ext = &odev->d_exts[odev->d_nctrl];
44488447a05SGarrett D'Amore 	ext->ctrl = odev->d_nctrl;
44588447a05SGarrett D'Amore 	ext->control_no = -1;
44688447a05SGarrett D'Amore 	ext->type = MIXT_MARKER;
44788447a05SGarrett D'Amore 	ext->timestamp = gethrtime();
44888447a05SGarrett D'Amore 	ext->parent = 0;
44988447a05SGarrett D'Amore 	odev->d_nctrl++;
45088447a05SGarrett D'Amore 
45188447a05SGarrett D'Amore 	/* Fill in the complete table now */
45288447a05SGarrett D'Amore 	auclnt_walk_controls(d, oss_add_control, odev);
45388447a05SGarrett D'Amore 
45488447a05SGarrett D'Amore 	/* Update the update_counter reference counter for groups */
45588447a05SGarrett D'Amore 	for (nctrl = 0; nctrl < odev->d_nctrl; nctrl++) {
45688447a05SGarrett D'Amore 		int i;
45788447a05SGarrett D'Amore 
45888447a05SGarrett D'Amore 		ext = &odev->d_exts[nctrl];
45988447a05SGarrett D'Amore 		i = ext->parent;
46088447a05SGarrett D'Amore 		while ((i >= 0) && (i < odev->d_nctrl)) {
46188447a05SGarrett D'Amore 
46288447a05SGarrett D'Amore 			ext = &odev->d_exts[i];
46388447a05SGarrett D'Amore 			ASSERT(ext->parent < i);
46488447a05SGarrett D'Amore 			ASSERT((ext->type == MIXT_GROUP) ||
46588447a05SGarrett D'Amore 			    (ext->type == MIXT_DEVROOT));
46688447a05SGarrett D'Amore 			ext->update_counter++;
46788447a05SGarrett D'Amore 			i = ext->parent;
46888447a05SGarrett D'Amore 		}
46988447a05SGarrett D'Amore 	}
47088447a05SGarrett D'Amore 
47188447a05SGarrett D'Amore 	ASSERT(odev->d_nctrl <= odev->d_nalloc);
47288447a05SGarrett D'Amore }
47388447a05SGarrett D'Amore 
47488447a05SGarrett D'Amore static int
oss_open(audio_client_t * c,int oflag)47588447a05SGarrett D'Amore oss_open(audio_client_t *c, int oflag)
47688447a05SGarrett D'Amore {
47788447a05SGarrett D'Amore 	int		rv;
47888447a05SGarrett D'Amore 	ossdev_t	*odev;
47988447a05SGarrett D'Amore 	ossclient_t	*sc;
48088447a05SGarrett D'Amore 	audio_stream_t	*isp, *osp;
48188447a05SGarrett D'Amore 
48288447a05SGarrett D'Amore 	isp = auclnt_input_stream(c);
48388447a05SGarrett D'Amore 	osp = auclnt_output_stream(c);
48488447a05SGarrett D'Amore 
48588447a05SGarrett D'Amore 	/* note that OSS always uses nonblocking open() semantics */
486*2c30fa45SGarrett D'Amore 	if ((rv = auclnt_open(c, oflag | FNDELAY)) != 0) {
48788447a05SGarrett D'Amore 		return (rv);
48888447a05SGarrett D'Amore 	}
48988447a05SGarrett D'Amore 
49088447a05SGarrett D'Amore 	if ((sc = kmem_zalloc(sizeof (*sc), KM_NOSLEEP)) == NULL) {
49188447a05SGarrett D'Amore 		auclnt_close(c);
49288447a05SGarrett D'Amore 		return (ENOMEM);
49388447a05SGarrett D'Amore 	}
49488447a05SGarrett D'Amore 	auclnt_set_private(c, sc);
49588447a05SGarrett D'Amore 
49688447a05SGarrett D'Amore 	odev = auclnt_get_minor_data(c, AUDIO_MINOR_DSP);
49788447a05SGarrett D'Amore 
49888447a05SGarrett D'Amore 	/* set a couple of common fields */
49988447a05SGarrett D'Amore 	sc->o_client = c;
50088447a05SGarrett D'Amore 	sc->o_ossdev = odev;
50188447a05SGarrett D'Amore 
50288447a05SGarrett D'Amore 	/* set all default parameters */
50388447a05SGarrett D'Amore 	if (oflag & FWRITE) {
50488447a05SGarrett D'Amore 		if (((rv = auclnt_set_format(osp, OSS_FMT)) != 0) ||
50588447a05SGarrett D'Amore 		    ((rv = auclnt_set_rate(osp, OSS_RATE)) != 0) ||
50688447a05SGarrett D'Amore 		    ((rv = auclnt_set_channels(osp, OSS_CHANNELS)) != 0)) {
50788447a05SGarrett D'Amore 			goto failed;
50888447a05SGarrett D'Amore 		}
509a702341cSGarrett D'Amore 		/* default to 5 fragments to provide reasonable latency */
510a702341cSGarrett D'Amore 		auclnt_set_latency(osp, 5, 0);
51188447a05SGarrett D'Amore 	}
51288447a05SGarrett D'Amore 
51388447a05SGarrett D'Amore 	if (oflag & FREAD) {
51488447a05SGarrett D'Amore 		if (((rv = auclnt_set_format(isp, OSS_FMT)) != 0) ||
51588447a05SGarrett D'Amore 		    ((rv = auclnt_set_rate(isp, OSS_RATE)) != 0) ||
51688447a05SGarrett D'Amore 		    ((rv = auclnt_set_channels(isp, OSS_CHANNELS)) != 0)) {
51788447a05SGarrett D'Amore 			goto failed;
51888447a05SGarrett D'Amore 		}
519a702341cSGarrett D'Amore 		/* default to 5 fragments to provide reasonable latency */
520a702341cSGarrett D'Amore 		auclnt_set_latency(isp, 5, 0);
52188447a05SGarrett D'Amore 	}
52288447a05SGarrett D'Amore 
52388447a05SGarrett D'Amore 	return (0);
52488447a05SGarrett D'Amore 
52588447a05SGarrett D'Amore failed:
52688447a05SGarrett D'Amore 	auclnt_close(c);
52788447a05SGarrett D'Amore 	return (rv);
52888447a05SGarrett D'Amore }
52988447a05SGarrett D'Amore 
53088447a05SGarrett D'Amore static void
oss_close(audio_client_t * c)53188447a05SGarrett D'Amore oss_close(audio_client_t *c)
53288447a05SGarrett D'Amore {
53388447a05SGarrett D'Amore 	ossclient_t	*sc;
53488447a05SGarrett D'Amore 
53588447a05SGarrett D'Amore 	sc = auclnt_get_private(c);
53688447a05SGarrett D'Amore 
53788447a05SGarrett D'Amore 	if (ddi_can_receive_sig() || (ddi_get_pid() == 0)) {
53888447a05SGarrett D'Amore 		(void) auclnt_drain(c);
53988447a05SGarrett D'Amore 	}
54088447a05SGarrett D'Amore 
54188447a05SGarrett D'Amore 	kmem_free(sc, sizeof (*sc));
54288447a05SGarrett D'Amore 
54388447a05SGarrett D'Amore 	auclnt_close(c);
54488447a05SGarrett D'Amore }
54588447a05SGarrett D'Amore 
54688447a05SGarrett D'Amore /*
54788447a05SGarrett D'Amore  * This is used to generate an array of names for an enumeration
54888447a05SGarrett D'Amore  */
54988447a05SGarrett D'Amore static ushort_t
oss_set_enum(oss_mixer_enuminfo * ei,ushort_t nxt,const char * name)55088447a05SGarrett D'Amore oss_set_enum(oss_mixer_enuminfo *ei, ushort_t nxt, const char *name)
55188447a05SGarrett D'Amore {
55288447a05SGarrett D'Amore 	uint32_t	n;
55388447a05SGarrett D'Amore 
55488447a05SGarrett D'Amore 	/* Get current entry to fill in */
55588447a05SGarrett D'Amore 	n = ei->nvalues;
55688447a05SGarrett D'Amore 	(void) snprintf(&ei->strings[nxt], ((sizeof (ei->strings) - nxt) - 1),
55788447a05SGarrett D'Amore 	    "%s", name);
55888447a05SGarrett D'Amore 	ei->strindex[n] = nxt;
55988447a05SGarrett D'Amore 
56088447a05SGarrett D'Amore 	/* Adjust everything for next entry */
56188447a05SGarrett D'Amore 	nxt += strnlen(name, ((sizeof (ei->strings) - nxt) - 1));
56288447a05SGarrett D'Amore 	ei->strings[nxt++] = '\0';
56388447a05SGarrett D'Amore 
56488447a05SGarrett D'Amore 	ei->nvalues++;
56588447a05SGarrett D'Amore 	return (nxt);
56688447a05SGarrett D'Amore }
56788447a05SGarrett D'Amore 
56888447a05SGarrett D'Amore /*
56988447a05SGarrett D'Amore  * The following two functions are used to count the number of devices
57088447a05SGarrett D'Amore  * in under the boomer framework.
57188447a05SGarrett D'Amore  *
57288447a05SGarrett D'Amore  * We actually report the highest "index", and then if an audio device
57388447a05SGarrett D'Amore  * is not found, we report a bogus removed device for it in the actual
57488447a05SGarrett D'Amore  * ioctls.  This goofiness is required to make the OSS API happy.
57588447a05SGarrett D'Amore  */
57688447a05SGarrett D'Amore int
oss_dev_walker(audio_dev_t * d,void * arg)57788447a05SGarrett D'Amore oss_dev_walker(audio_dev_t *d, void *arg)
57888447a05SGarrett D'Amore {
57988447a05SGarrett D'Amore 	int		*pcnt = arg;
58088447a05SGarrett D'Amore 	int		cnt;
58188447a05SGarrett D'Amore 	int		index;
58288447a05SGarrett D'Amore 
58388447a05SGarrett D'Amore 	cnt = *pcnt;
58488447a05SGarrett D'Amore 	index = auclnt_get_dev_index(d);
58588447a05SGarrett D'Amore 	if ((index + 1) > cnt) {
58688447a05SGarrett D'Amore 		cnt = index + 1;
58788447a05SGarrett D'Amore 		*pcnt = cnt;
58888447a05SGarrett D'Amore 	}
58988447a05SGarrett D'Amore 
59088447a05SGarrett D'Amore 	return (AUDIO_WALK_CONTINUE);
59188447a05SGarrett D'Amore }
59288447a05SGarrett D'Amore 
59388447a05SGarrett D'Amore static int
oss_cnt_devs(void)59488447a05SGarrett D'Amore oss_cnt_devs(void)
59588447a05SGarrett D'Amore {
59688447a05SGarrett D'Amore 	int cnt = 0;
59788447a05SGarrett D'Amore 
59888447a05SGarrett D'Amore 	auclnt_walk_devs(oss_dev_walker, &cnt);
59988447a05SGarrett D'Amore 	return (cnt);
60088447a05SGarrett D'Amore }
60188447a05SGarrett D'Amore 
60288447a05SGarrett D'Amore static int
sndctl_dsp_speed(audio_client_t * c,int * ratep)60388447a05SGarrett D'Amore sndctl_dsp_speed(audio_client_t *c, int *ratep)
60488447a05SGarrett D'Amore {
60588447a05SGarrett D'Amore 	int		rate;
60688447a05SGarrett D'Amore 	int		oflag;
60788447a05SGarrett D'Amore 
60888447a05SGarrett D'Amore 	rate = *ratep;
60988447a05SGarrett D'Amore 
61088447a05SGarrett D'Amore 	oflag = auclnt_get_oflag(c);
61188447a05SGarrett D'Amore 	if (oflag & FREAD) {
61226025630SGarrett D'Amore 		(void) auclnt_set_rate(auclnt_input_stream(c), rate);
61326025630SGarrett D'Amore 		*ratep = auclnt_get_rate(auclnt_input_stream(c));
61488447a05SGarrett D'Amore 	}
61588447a05SGarrett D'Amore 
61688447a05SGarrett D'Amore 	if (oflag & FWRITE) {
61726025630SGarrett D'Amore 		(void) auclnt_set_rate(auclnt_output_stream(c), rate);
61826025630SGarrett D'Amore 		*ratep = auclnt_get_rate(auclnt_output_stream(c));
61988447a05SGarrett D'Amore 	}
62088447a05SGarrett D'Amore 
62188447a05SGarrett D'Amore 	return (0);
62288447a05SGarrett D'Amore }
62388447a05SGarrett D'Amore 
62488447a05SGarrett D'Amore static int
sndctl_dsp_setfmt(audio_client_t * c,int * fmtp)62588447a05SGarrett D'Amore sndctl_dsp_setfmt(audio_client_t *c, int *fmtp)
62688447a05SGarrett D'Amore {
62788447a05SGarrett D'Amore 	int		fmt;
62888447a05SGarrett D'Amore 	int		i;
62988447a05SGarrett D'Amore 	int		oflag;
63088447a05SGarrett D'Amore 
63188447a05SGarrett D'Amore 	oflag = auclnt_get_oflag(c);
63288447a05SGarrett D'Amore 
63388447a05SGarrett D'Amore 	if (*fmtp != AFMT_QUERY) {
63488447a05SGarrett D'Amore 		/* convert from OSS */
63588447a05SGarrett D'Amore 		for (i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
63688447a05SGarrett D'Amore 			if (oss_formats[i].oss == *fmtp) {
63788447a05SGarrett D'Amore 				fmt = oss_formats[i].fmt;
63888447a05SGarrett D'Amore 				break;
63988447a05SGarrett D'Amore 			}
64088447a05SGarrett D'Amore 		}
64188447a05SGarrett D'Amore 		if (fmt == AUDIO_FORMAT_NONE) {
64226025630SGarrett D'Amore 			/* if format not known, return */
64326025630SGarrett D'Amore 			goto done;
64488447a05SGarrett D'Amore 		}
64588447a05SGarrett D'Amore 
64688447a05SGarrett D'Amore 		if (oflag & FWRITE) {
64726025630SGarrett D'Amore 			(void) auclnt_set_format(auclnt_output_stream(c), fmt);
64888447a05SGarrett D'Amore 		}
64988447a05SGarrett D'Amore 
65088447a05SGarrett D'Amore 		if (oflag & FREAD) {
65126025630SGarrett D'Amore 			(void) auclnt_set_format(auclnt_input_stream(c), fmt);
65288447a05SGarrett D'Amore 		}
65388447a05SGarrett D'Amore 	}
65488447a05SGarrett D'Amore 
65526025630SGarrett D'Amore done:
65688447a05SGarrett D'Amore 	if (oflag & FWRITE) {
65788447a05SGarrett D'Amore 		fmt = auclnt_get_format(auclnt_output_stream(c));
65888447a05SGarrett D'Amore 	} else if (oflag & FREAD) {
65988447a05SGarrett D'Amore 		fmt = auclnt_get_format(auclnt_input_stream(c));
66088447a05SGarrett D'Amore 	}
66188447a05SGarrett D'Amore 
66288447a05SGarrett D'Amore 	/* convert back to OSS */
66388447a05SGarrett D'Amore 	*(int *)fmtp = AFMT_QUERY;
66488447a05SGarrett D'Amore 	for (i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
66588447a05SGarrett D'Amore 		if (oss_formats[i].fmt == fmt) {
66688447a05SGarrett D'Amore 			*(int *)fmtp = oss_formats[i].oss;
66788447a05SGarrett D'Amore 		}
66888447a05SGarrett D'Amore 	}
66988447a05SGarrett D'Amore 
67088447a05SGarrett D'Amore 	return (0);
67188447a05SGarrett D'Amore }
67288447a05SGarrett D'Amore 
67388447a05SGarrett D'Amore static int
sndctl_dsp_getfmts(audio_client_t * c,int * fmtsp)67488447a05SGarrett D'Amore sndctl_dsp_getfmts(audio_client_t *c, int *fmtsp)
67588447a05SGarrett D'Amore {
67688447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
67788447a05SGarrett D'Amore 
67888447a05SGarrett D'Amore 	/*
67988447a05SGarrett D'Amore 	 * For now, we support all the standard ones.  Later we might
68088447a05SGarrett D'Amore 	 * add in conditional support for AC3.
68188447a05SGarrett D'Amore 	 */
68288447a05SGarrett D'Amore 	*fmtsp = (AFMT_MU_LAW | AFMT_A_LAW |
68388447a05SGarrett D'Amore 	    AFMT_U8 | AFMT_S8 |
68488447a05SGarrett D'Amore 	    AFMT_S16_LE |AFMT_S16_BE |
68588447a05SGarrett D'Amore 	    AFMT_S24_LE | AFMT_S24_BE |
68688447a05SGarrett D'Amore 	    AFMT_S32_LE | AFMT_S32_BE |
68788447a05SGarrett D'Amore 	    AFMT_S24_PACKED);
68888447a05SGarrett D'Amore 
68988447a05SGarrett D'Amore 	return (0);
69088447a05SGarrett D'Amore }
69188447a05SGarrett D'Amore 
69288447a05SGarrett D'Amore static int
sndctl_dsp_channels(audio_client_t * c,int * chanp)69388447a05SGarrett D'Amore sndctl_dsp_channels(audio_client_t *c, int *chanp)
69488447a05SGarrett D'Amore {
69588447a05SGarrett D'Amore 	int		nchan;
69688447a05SGarrett D'Amore 	int		oflag;
69788447a05SGarrett D'Amore 
69888447a05SGarrett D'Amore 	oflag = auclnt_get_oflag(c);
69988447a05SGarrett D'Amore 
70088447a05SGarrett D'Amore 	nchan = *chanp;
70188447a05SGarrett D'Amore 	if (nchan != 0) {
70288447a05SGarrett D'Amore 		if (oflag & FWRITE) {
70326025630SGarrett D'Amore 			(void) auclnt_set_channels(auclnt_output_stream(c),
70488447a05SGarrett D'Amore 			    nchan);
70588447a05SGarrett D'Amore 		}
70688447a05SGarrett D'Amore 
70788447a05SGarrett D'Amore 		if (oflag & FREAD) {
70826025630SGarrett D'Amore 			(void) auclnt_set_channels(auclnt_input_stream(c),
70926025630SGarrett D'Amore 			    nchan);
71088447a05SGarrett D'Amore 		}
71188447a05SGarrett D'Amore 	}
71288447a05SGarrett D'Amore 
71388447a05SGarrett D'Amore 	if (oflag & FWRITE) {
71488447a05SGarrett D'Amore 		nchan = auclnt_get_channels(auclnt_output_stream(c));
71588447a05SGarrett D'Amore 	} else if (oflag & FREAD) {
71688447a05SGarrett D'Amore 		nchan = auclnt_get_channels(auclnt_input_stream(c));
71788447a05SGarrett D'Amore 	}
71888447a05SGarrett D'Amore 	*chanp = nchan;
71988447a05SGarrett D'Amore 	return (0);
72088447a05SGarrett D'Amore }
72188447a05SGarrett D'Amore 
72288447a05SGarrett D'Amore static int
sndctl_dsp_stereo(audio_client_t * c,int * onoff)72388447a05SGarrett D'Amore sndctl_dsp_stereo(audio_client_t *c, int *onoff)
72488447a05SGarrett D'Amore {
72588447a05SGarrett D'Amore 	int	nchan;
72688447a05SGarrett D'Amore 
72788447a05SGarrett D'Amore 	switch (*onoff) {
72888447a05SGarrett D'Amore 	case 0:
72988447a05SGarrett D'Amore 		nchan = 1;
73088447a05SGarrett D'Amore 		break;
73188447a05SGarrett D'Amore 	case 1:
73288447a05SGarrett D'Amore 		nchan = 2;
73388447a05SGarrett D'Amore 		break;
73488447a05SGarrett D'Amore 	default:
73588447a05SGarrett D'Amore 		return (EINVAL);
73688447a05SGarrett D'Amore 	}
73788447a05SGarrett D'Amore 
73888447a05SGarrett D'Amore 	return (sndctl_dsp_channels(c, &nchan));
73988447a05SGarrett D'Amore }
74088447a05SGarrett D'Amore 
74188447a05SGarrett D'Amore static int
sndctl_dsp_post(audio_client_t * c)74288447a05SGarrett D'Amore sndctl_dsp_post(audio_client_t *c)
74388447a05SGarrett D'Amore {
74488447a05SGarrett D'Amore 	if (auclnt_get_oflag(c) & FWRITE) {
74588447a05SGarrett D'Amore 		audio_stream_t	*sp = auclnt_output_stream(c);
74688447a05SGarrett D'Amore 		auclnt_flush(sp);
74788447a05SGarrett D'Amore 		auclnt_clear_paused(sp);
74888447a05SGarrett D'Amore 	}
74988447a05SGarrett D'Amore 	return (0);
75088447a05SGarrett D'Amore }
75188447a05SGarrett D'Amore 
75288447a05SGarrett D'Amore static int
sndctl_dsp_getcaps(audio_client_t * c,int * capsp)75388447a05SGarrett D'Amore sndctl_dsp_getcaps(audio_client_t *c, int *capsp)
75488447a05SGarrett D'Amore {
75588447a05SGarrett D'Amore 	int		ncaps;
75688447a05SGarrett D'Amore 	int		osscaps = 0;
75788447a05SGarrett D'Amore 
75888447a05SGarrett D'Amore 	ncaps = auclnt_get_dev_capab(auclnt_get_dev(c));
75988447a05SGarrett D'Amore 
76088447a05SGarrett D'Amore 	if (ncaps & AUDIO_CLIENT_CAP_PLAY)
76188447a05SGarrett D'Amore 		osscaps |= PCM_CAP_OUTPUT;
76288447a05SGarrett D'Amore 	if (ncaps & AUDIO_CLIENT_CAP_RECORD)
76388447a05SGarrett D'Amore 		osscaps |= PCM_CAP_INPUT;
76488447a05SGarrett D'Amore 	if (ncaps & AUDIO_CLIENT_CAP_DUPLEX)
76588447a05SGarrett D'Amore 		osscaps |= PCM_CAP_DUPLEX;
76688447a05SGarrett D'Amore 
76726025630SGarrett D'Amore 	if (osscaps != 0) {
76826025630SGarrett D'Amore 		osscaps |= PCM_CAP_TRIGGER | PCM_CAP_BATCH;
76926025630SGarrett D'Amore 		if (!(ncaps & AUDIO_CLIENT_CAP_OPAQUE)) {
77026025630SGarrett D'Amore 			osscaps |= PCM_CAP_FREERATE | PCM_CAP_MULTI;
77126025630SGarrett D'Amore 		}
77226025630SGarrett D'Amore 	} else {
77326025630SGarrett D'Amore 		/* This is the sndstat device! */
77426025630SGarrett D'Amore 		osscaps = PCM_CAP_VIRTUAL;
77526025630SGarrett D'Amore 	}
77626025630SGarrett D'Amore 
77788447a05SGarrett D'Amore 	*capsp = osscaps;
77888447a05SGarrett D'Amore 	return (0);
77988447a05SGarrett D'Amore }
78088447a05SGarrett D'Amore 
78188447a05SGarrett D'Amore static int
sndctl_dsp_gettrigger(audio_client_t * c,int * trigp)78288447a05SGarrett D'Amore sndctl_dsp_gettrigger(audio_client_t *c, int *trigp)
78388447a05SGarrett D'Amore {
78488447a05SGarrett D'Amore 	int		triggers = 0;
78588447a05SGarrett D'Amore 	int		oflag;
78688447a05SGarrett D'Amore 
78788447a05SGarrett D'Amore 	oflag = auclnt_get_oflag(c);
78888447a05SGarrett D'Amore 
78988447a05SGarrett D'Amore 	if (oflag & FWRITE) {
79088447a05SGarrett D'Amore 		if (!auclnt_is_paused(auclnt_output_stream(c))) {
79188447a05SGarrett D'Amore 			triggers |= PCM_ENABLE_OUTPUT;
79288447a05SGarrett D'Amore 		}
79388447a05SGarrett D'Amore 	}
79488447a05SGarrett D'Amore 
79588447a05SGarrett D'Amore 	if (oflag & FREAD) {
79688447a05SGarrett D'Amore 		if (!auclnt_is_paused(auclnt_input_stream(c))) {
79788447a05SGarrett D'Amore 			triggers |= PCM_ENABLE_INPUT;
79888447a05SGarrett D'Amore 		}
79988447a05SGarrett D'Amore 	}
80088447a05SGarrett D'Amore 	*trigp = triggers;
80188447a05SGarrett D'Amore 
80288447a05SGarrett D'Amore 	return (0);
80388447a05SGarrett D'Amore }
80488447a05SGarrett D'Amore 
80588447a05SGarrett D'Amore static int
sndctl_dsp_settrigger(audio_client_t * c,int * trigp)80688447a05SGarrett D'Amore sndctl_dsp_settrigger(audio_client_t *c, int *trigp)
80788447a05SGarrett D'Amore {
80888447a05SGarrett D'Amore 	int		triggers;
80988447a05SGarrett D'Amore 	int		oflag;
8109556c422SGarrett D'Amore 	audio_stream_t	*sp;
81188447a05SGarrett D'Amore 
81288447a05SGarrett D'Amore 	oflag = auclnt_get_oflag(c);
81388447a05SGarrett D'Amore 	triggers = *trigp;
81488447a05SGarrett D'Amore 
81588447a05SGarrett D'Amore 	if ((oflag & FWRITE) && (triggers & PCM_ENABLE_OUTPUT)) {
8169556c422SGarrett D'Amore 		sp = auclnt_output_stream(c);
8179556c422SGarrett D'Amore 		auclnt_clear_paused(sp);
8189556c422SGarrett D'Amore 		auclnt_start(sp);
81988447a05SGarrett D'Amore 	}
82088447a05SGarrett D'Amore 
82188447a05SGarrett D'Amore 	if ((oflag & FREAD) && (triggers & PCM_ENABLE_INPUT)) {
8229556c422SGarrett D'Amore 		sp = auclnt_input_stream(c);
8239556c422SGarrett D'Amore 		auclnt_clear_paused(sp);
8249556c422SGarrett D'Amore 		auclnt_start(sp);
82588447a05SGarrett D'Amore 	}
82688447a05SGarrett D'Amore 
82788447a05SGarrett D'Amore 	return (0);
82888447a05SGarrett D'Amore }
82988447a05SGarrett D'Amore 
83088447a05SGarrett D'Amore struct oss_legacy_volume {
83188447a05SGarrett D'Amore 	pid_t		pid;
83288447a05SGarrett D'Amore 	uint8_t		ogain;
83388447a05SGarrett D'Amore 	uint8_t		igain;
83488447a05SGarrett D'Amore };
83588447a05SGarrett D'Amore 
83688447a05SGarrett D'Amore static int
oss_legacy_volume_walker(audio_client_t * c,void * arg)83788447a05SGarrett D'Amore oss_legacy_volume_walker(audio_client_t *c, void *arg)
83888447a05SGarrett D'Amore {
83988447a05SGarrett D'Amore 	struct oss_legacy_volume	 *olv = arg;
84088447a05SGarrett D'Amore 
84188447a05SGarrett D'Amore 	if (auclnt_get_pid(c) == olv->pid) {
84288447a05SGarrett D'Amore 		if (olv->ogain <= 100) {
84388447a05SGarrett D'Amore 			auclnt_set_gain(auclnt_output_stream(c), olv->ogain);
84488447a05SGarrett D'Amore 		}
84588447a05SGarrett D'Amore 		if (olv->igain <= 100) {
84688447a05SGarrett D'Amore 			auclnt_set_gain(auclnt_input_stream(c), olv->igain);
84788447a05SGarrett D'Amore 		}
84888447a05SGarrett D'Amore 	}
84988447a05SGarrett D'Amore 	return (AUDIO_WALK_CONTINUE);
85088447a05SGarrett D'Amore }
85188447a05SGarrett D'Amore 
85288447a05SGarrett D'Amore static void
oss_set_legacy_volume(audio_client_t * c,uint8_t ogain,uint8_t igain)85388447a05SGarrett D'Amore oss_set_legacy_volume(audio_client_t *c, uint8_t ogain, uint8_t igain)
85488447a05SGarrett D'Amore {
85588447a05SGarrett D'Amore 	struct oss_legacy_volume olv;
85688447a05SGarrett D'Amore 
85788447a05SGarrett D'Amore 	olv.pid = auclnt_get_pid(c);
85888447a05SGarrett D'Amore 	olv.ogain = ogain;
85988447a05SGarrett D'Amore 	olv.igain = igain;
86088447a05SGarrett D'Amore 	auclnt_dev_walk_clients(auclnt_get_dev(c),
86188447a05SGarrett D'Amore 	    oss_legacy_volume_walker, &olv);
86288447a05SGarrett D'Amore }
86388447a05SGarrett D'Amore 
86488447a05SGarrett D'Amore static int
sndctl_dsp_getplayvol(audio_client_t * c,int * volp)86588447a05SGarrett D'Amore sndctl_dsp_getplayvol(audio_client_t *c, int *volp)
86688447a05SGarrett D'Amore {
86788447a05SGarrett D'Amore 	int	vol;
86888447a05SGarrett D'Amore 
86988447a05SGarrett D'Amore 	/* convert monophonic soft value to OSS stereo value */
87088447a05SGarrett D'Amore 	vol = auclnt_get_gain(auclnt_output_stream(c));
87188447a05SGarrett D'Amore 	*volp = vol | (vol << 8);
87288447a05SGarrett D'Amore 	return (0);
87388447a05SGarrett D'Amore }
87488447a05SGarrett D'Amore 
87588447a05SGarrett D'Amore static int
sndctl_dsp_setplayvol(audio_client_t * c,int * volp)87688447a05SGarrett D'Amore sndctl_dsp_setplayvol(audio_client_t *c, int *volp)
87788447a05SGarrett D'Amore {
87888447a05SGarrett D'Amore 	uint8_t		vol;
87988447a05SGarrett D'Amore 
88088447a05SGarrett D'Amore 	vol = *volp & 0xff;
88188447a05SGarrett D'Amore 	if (vol > 100) {
88288447a05SGarrett D'Amore 		return (EINVAL);
88388447a05SGarrett D'Amore 	}
88488447a05SGarrett D'Amore 
88588447a05SGarrett D'Amore 	auclnt_set_gain(auclnt_output_stream(c), vol);
88688447a05SGarrett D'Amore 	*volp = (vol | (vol << 8));
88788447a05SGarrett D'Amore 
88888447a05SGarrett D'Amore 	return (0);
88988447a05SGarrett D'Amore }
89088447a05SGarrett D'Amore 
89188447a05SGarrett D'Amore static int
sndctl_dsp_getrecvol(audio_client_t * c,int * volp)89288447a05SGarrett D'Amore sndctl_dsp_getrecvol(audio_client_t *c, int *volp)
89388447a05SGarrett D'Amore {
89488447a05SGarrett D'Amore 	int	vol;
89588447a05SGarrett D'Amore 
89688447a05SGarrett D'Amore 	vol = auclnt_get_gain(auclnt_input_stream(c));
89788447a05SGarrett D'Amore 	*volp = (vol | (vol << 8));
89888447a05SGarrett D'Amore 	return (0);
89988447a05SGarrett D'Amore }
90088447a05SGarrett D'Amore 
90188447a05SGarrett D'Amore static int
sndctl_dsp_setrecvol(audio_client_t * c,int * volp)90288447a05SGarrett D'Amore sndctl_dsp_setrecvol(audio_client_t *c, int *volp)
90388447a05SGarrett D'Amore {
90488447a05SGarrett D'Amore 	uint8_t		vol;
90588447a05SGarrett D'Amore 
90688447a05SGarrett D'Amore 	vol = *volp & 0xff;
90788447a05SGarrett D'Amore 	if (vol > 100) {
90888447a05SGarrett D'Amore 		return (EINVAL);
90988447a05SGarrett D'Amore 	}
91088447a05SGarrett D'Amore 
91188447a05SGarrett D'Amore 	auclnt_set_gain(auclnt_input_stream(c), vol);
91288447a05SGarrett D'Amore 	*volp = (vol | (vol << 8));
91388447a05SGarrett D'Amore 
91488447a05SGarrett D'Amore 	return (0);
91588447a05SGarrett D'Amore }
91688447a05SGarrett D'Amore 
91788447a05SGarrett D'Amore static int
sound_mixer_write_ogain(audio_client_t * c,int * volp)91888447a05SGarrett D'Amore sound_mixer_write_ogain(audio_client_t *c, int *volp)
91988447a05SGarrett D'Amore {
92088447a05SGarrett D'Amore 	uint8_t		vol;
92188447a05SGarrett D'Amore 
92288447a05SGarrett D'Amore 	vol = *volp & 0xff;
92388447a05SGarrett D'Amore 	if (vol > 100) {
92488447a05SGarrett D'Amore 		return (EINVAL);
92588447a05SGarrett D'Amore 	}
92688447a05SGarrett D'Amore 	oss_set_legacy_volume(c, vol, 255);
92788447a05SGarrett D'Amore 	*volp = (vol | (vol << 8));
92888447a05SGarrett D'Amore 	return (0);
92988447a05SGarrett D'Amore }
93088447a05SGarrett D'Amore 
93188447a05SGarrett D'Amore static int
sound_mixer_write_igain(audio_client_t * c,int * volp)93288447a05SGarrett D'Amore sound_mixer_write_igain(audio_client_t *c, int *volp)
93388447a05SGarrett D'Amore {
93488447a05SGarrett D'Amore 	uint8_t		vol;
93588447a05SGarrett D'Amore 
93688447a05SGarrett D'Amore 	vol = *volp & 0xff;
93788447a05SGarrett D'Amore 	if (vol > 100) {
93888447a05SGarrett D'Amore 		return (EINVAL);
93988447a05SGarrett D'Amore 	}
94088447a05SGarrett D'Amore 	oss_set_legacy_volume(c, 255, vol);
94188447a05SGarrett D'Amore 	*volp = (vol | (vol << 8));
94288447a05SGarrett D'Amore 	return (0);
94388447a05SGarrett D'Amore }
94488447a05SGarrett D'Amore 
94588447a05SGarrett D'Amore static int
sndctl_dsp_readctl(audio_client_t * c,oss_digital_control * ctl)94688447a05SGarrett D'Amore sndctl_dsp_readctl(audio_client_t *c, oss_digital_control *ctl)
94788447a05SGarrett D'Amore {
94888447a05SGarrett D'Amore 	/* SPDIF: need to add support with spdif */
94988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
95088447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(ctl));
95188447a05SGarrett D'Amore 	return (ENOTSUP);
95288447a05SGarrett D'Amore }
95388447a05SGarrett D'Amore 
95488447a05SGarrett D'Amore static int
sndctl_dsp_writectl(audio_client_t * c,oss_digital_control * ctl)95588447a05SGarrett D'Amore sndctl_dsp_writectl(audio_client_t *c, oss_digital_control *ctl)
95688447a05SGarrett D'Amore {
95788447a05SGarrett D'Amore 	/* SPDIF: need to add support with spdif */
95888447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
95988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(ctl));
96088447a05SGarrett D'Amore 	return (ENOTSUP);
96188447a05SGarrett D'Amore }
96288447a05SGarrett D'Amore 
96388447a05SGarrett D'Amore static int
sndctl_dsp_cookedmode(audio_client_t * c,int * rvp)96488447a05SGarrett D'Amore sndctl_dsp_cookedmode(audio_client_t *c, int *rvp)
96588447a05SGarrett D'Amore {
96688447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
96788447a05SGarrett D'Amore 
96888447a05SGarrett D'Amore 	/* We are *always* in cooked mode -- at least until we have AC3. */
96988447a05SGarrett D'Amore 	if (*rvp == 0) {
97088447a05SGarrett D'Amore 		return (ENOTSUP);
97188447a05SGarrett D'Amore 	} else {
97288447a05SGarrett D'Amore 		return (0);
97388447a05SGarrett D'Amore 	}
97488447a05SGarrett D'Amore }
97588447a05SGarrett D'Amore 
97688447a05SGarrett D'Amore static int
sndctl_dsp_silence(audio_client_t * c)97788447a05SGarrett D'Amore sndctl_dsp_silence(audio_client_t *c)
97888447a05SGarrett D'Amore {
97988447a05SGarrett D'Amore 	if (auclnt_get_oflag(c) & FWRITE) {
98088447a05SGarrett D'Amore 		audio_stream_t	*sp = auclnt_output_stream(c);
98188447a05SGarrett D'Amore 		auclnt_set_paused(sp);
98288447a05SGarrett D'Amore 		auclnt_flush(sp);
98388447a05SGarrett D'Amore 	}
98488447a05SGarrett D'Amore 	return (0);
98588447a05SGarrett D'Amore }
98688447a05SGarrett D'Amore 
98788447a05SGarrett D'Amore static int
sndctl_dsp_skip(audio_client_t * c)98888447a05SGarrett D'Amore sndctl_dsp_skip(audio_client_t *c)
98988447a05SGarrett D'Amore {
99088447a05SGarrett D'Amore 	if (auclnt_get_oflag(c) & FWRITE) {
99188447a05SGarrett D'Amore 		audio_stream_t	*sp = auclnt_output_stream(c);
99288447a05SGarrett D'Amore 		auclnt_set_paused(sp);
99388447a05SGarrett D'Amore 		auclnt_flush(sp);
99488447a05SGarrett D'Amore 		auclnt_clear_paused(sp);
99588447a05SGarrett D'Amore 	}
99688447a05SGarrett D'Amore 	return (0);
99788447a05SGarrett D'Amore }
99888447a05SGarrett D'Amore 
99988447a05SGarrett D'Amore static int
sndctl_dsp_halt_input(audio_client_t * c)100088447a05SGarrett D'Amore sndctl_dsp_halt_input(audio_client_t *c)
100188447a05SGarrett D'Amore {
100288447a05SGarrett D'Amore 	if (auclnt_get_oflag(c) & FREAD) {
100388447a05SGarrett D'Amore 		audio_stream_t	*sp = auclnt_input_stream(c);
100488447a05SGarrett D'Amore 		auclnt_set_paused(sp);
100588447a05SGarrett D'Amore 		auclnt_flush(sp);
100688447a05SGarrett D'Amore 	}
100788447a05SGarrett D'Amore 	return (0);
100888447a05SGarrett D'Amore }
100988447a05SGarrett D'Amore 
101088447a05SGarrett D'Amore static int
sndctl_dsp_halt_output(audio_client_t * c)101188447a05SGarrett D'Amore sndctl_dsp_halt_output(audio_client_t *c)
101288447a05SGarrett D'Amore {
101388447a05SGarrett D'Amore 	if (auclnt_get_oflag(c) & FWRITE) {
101488447a05SGarrett D'Amore 		audio_stream_t	*sp = auclnt_output_stream(c);
101588447a05SGarrett D'Amore 		auclnt_set_paused(sp);
101688447a05SGarrett D'Amore 		auclnt_flush(sp);
101788447a05SGarrett D'Amore 	}
101888447a05SGarrett D'Amore 	return (0);
101988447a05SGarrett D'Amore }
102088447a05SGarrett D'Amore 
102188447a05SGarrett D'Amore static int
sndctl_dsp_halt(audio_client_t * c)102288447a05SGarrett D'Amore sndctl_dsp_halt(audio_client_t *c)
102388447a05SGarrett D'Amore {
102488447a05SGarrett D'Amore 	(void) sndctl_dsp_halt_input(c);
102588447a05SGarrett D'Amore 	(void) sndctl_dsp_halt_output(c);
102688447a05SGarrett D'Amore 	return (0);
102788447a05SGarrett D'Amore }
102888447a05SGarrett D'Amore 
102988447a05SGarrett D'Amore static int
sndctl_dsp_sync(audio_client_t * c)103088447a05SGarrett D'Amore sndctl_dsp_sync(audio_client_t *c)
103188447a05SGarrett D'Amore {
103288447a05SGarrett D'Amore 	return (auclnt_drain(c));
103388447a05SGarrett D'Amore }
103488447a05SGarrett D'Amore 
103588447a05SGarrett D'Amore static int
sndctl_dsp_setfragment(audio_client_t * c,int * fragp)103688447a05SGarrett D'Amore sndctl_dsp_setfragment(audio_client_t *c, int *fragp)
103788447a05SGarrett D'Amore {
1038a702341cSGarrett D'Amore 	int	bufsz;
1039a702341cSGarrett D'Amore 	int	nfrags;
1040a702341cSGarrett D'Amore 	int	fragsz;
1041a702341cSGarrett D'Amore 
1042a702341cSGarrett D'Amore 	nfrags = (*fragp) >> 16;
1043a702341cSGarrett D'Amore 	if ((nfrags >= 0x7fffU) || (nfrags < 2)) {
1044a702341cSGarrett D'Amore 		/* use infinite setting... no change */
1045a702341cSGarrett D'Amore 		return (0);
1046a702341cSGarrett D'Amore 	}
1047a702341cSGarrett D'Amore 
1048a702341cSGarrett D'Amore 	fragsz = (*fragp) & 0xffff;
1049a702341cSGarrett D'Amore 	if (fragsz > 16) {
1050a702341cSGarrett D'Amore 		/* basically too big, so, no change */
1051a702341cSGarrett D'Amore 		return (0);
1052a702341cSGarrett D'Amore 	}
1053a702341cSGarrett D'Amore 	bufsz = (1U << fragsz) * nfrags;
1054a702341cSGarrett D'Amore 
1055a702341cSGarrett D'Amore 	/*
1056a702341cSGarrett D'Amore 	 * Now we have our desired buffer size, but we have to
1057a702341cSGarrett D'Amore 	 * make sure we have a whole number of fragments >= 2, and
1058a702341cSGarrett D'Amore 	 * less than the maximum.
1059a702341cSGarrett D'Amore 	 */
1060a702341cSGarrett D'Amore 	bufsz = ((*fragp) >> 16) * (1U << (*fragp));
1061a702341cSGarrett D'Amore 	if (bufsz >= 65536) {
1062a702341cSGarrett D'Amore 		return (0);
1063a702341cSGarrett D'Amore 	}
1064a702341cSGarrett D'Amore 
1065a702341cSGarrett D'Amore 	/*
1066a702341cSGarrett D'Amore 	 * We set the latency hints in terms of bytes, not fragments.
1067a702341cSGarrett D'Amore 	 */
1068a702341cSGarrett D'Amore 	auclnt_set_latency(auclnt_output_stream(c), 0, bufsz);
1069a702341cSGarrett D'Amore 	auclnt_set_latency(auclnt_input_stream(c), 0, bufsz);
1070a702341cSGarrett D'Amore 
107188447a05SGarrett D'Amore 	/*
107288447a05SGarrett D'Amore 	 * According to the OSS API documentation, the values provided
107388447a05SGarrett D'Amore 	 * are nothing more than a "hint" and not to be relied upon
107488447a05SGarrett D'Amore 	 * anyway.  And we aren't obligated to report the actual
107588447a05SGarrett D'Amore 	 * values back!
107688447a05SGarrett D'Amore 	 */
107788447a05SGarrett D'Amore 	return (0);
107888447a05SGarrett D'Amore }
107988447a05SGarrett D'Amore 
1080a702341cSGarrett D'Amore static int
sndctl_dsp_policy(audio_client_t * c,int * policy)1081a702341cSGarrett D'Amore sndctl_dsp_policy(audio_client_t *c, int *policy)
1082a702341cSGarrett D'Amore {
1083a702341cSGarrett D'Amore 	int	hint = *policy;
1084a702341cSGarrett D'Amore 	if ((hint >= 2) && (hint <= 10)) {
1085a702341cSGarrett D'Amore 		auclnt_set_latency(auclnt_input_stream(c), hint, 0);
1086a702341cSGarrett D'Amore 		auclnt_set_latency(auclnt_output_stream(c), hint, 0);
1087a702341cSGarrett D'Amore 	}
1088a702341cSGarrett D'Amore 	return (0);
1089a702341cSGarrett D'Amore }
1090a702341cSGarrett D'Amore 
109188447a05SGarrett D'Amore /*
109288447a05SGarrett D'Amore  * A word about recsrc, and playtgt ioctls: We don't allow ordinary DSP
109388447a05SGarrett D'Amore  * applications to change port configurations, because these could have a
109488447a05SGarrett D'Amore  * bad effect for other applications.  Instead, these settings have to
109588447a05SGarrett D'Amore  * be changed using the master mixer panel.  In order to make applications
109688447a05SGarrett D'Amore  * happy, we just present a single "default" source/target.
109788447a05SGarrett D'Amore  */
109888447a05SGarrett D'Amore static int
sndctl_dsp_get_recsrc_names(audio_client_t * c,oss_mixer_enuminfo * ei)109988447a05SGarrett D'Amore sndctl_dsp_get_recsrc_names(audio_client_t *c, oss_mixer_enuminfo *ei)
110088447a05SGarrett D'Amore {
110188447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
110288447a05SGarrett D'Amore 
110388447a05SGarrett D'Amore 	ei->nvalues = 1;
110488447a05SGarrett D'Amore 	(void) snprintf(ei->strings, sizeof (ei->strings), "default");
110588447a05SGarrett D'Amore 	ei->strindex[0] = 0;
110688447a05SGarrett D'Amore 
110788447a05SGarrett D'Amore 	return (0);
110888447a05SGarrett D'Amore }
110988447a05SGarrett D'Amore 
111088447a05SGarrett D'Amore static int
sndctl_dsp_get_recsrc(audio_client_t * c,int * srcp)111188447a05SGarrett D'Amore sndctl_dsp_get_recsrc(audio_client_t *c, int *srcp)
111288447a05SGarrett D'Amore {
111388447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
111488447a05SGarrett D'Amore 	*srcp = 0;
111588447a05SGarrett D'Amore 	return (0);
111688447a05SGarrett D'Amore }
111788447a05SGarrett D'Amore 
111888447a05SGarrett D'Amore static int
sndctl_dsp_set_recsrc(audio_client_t * c,int * srcp)111988447a05SGarrett D'Amore sndctl_dsp_set_recsrc(audio_client_t *c, int *srcp)
112088447a05SGarrett D'Amore {
112188447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
112288447a05SGarrett D'Amore 	*srcp = 0;
112388447a05SGarrett D'Amore 	return (0);
112488447a05SGarrett D'Amore }
112588447a05SGarrett D'Amore 
112688447a05SGarrett D'Amore static int
sndctl_dsp_get_playtgt_names(audio_client_t * c,oss_mixer_enuminfo * ei)112788447a05SGarrett D'Amore sndctl_dsp_get_playtgt_names(audio_client_t *c, oss_mixer_enuminfo *ei)
112888447a05SGarrett D'Amore {
112988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
113088447a05SGarrett D'Amore 
113188447a05SGarrett D'Amore 	ei->nvalues = 1;
113288447a05SGarrett D'Amore 	(void) snprintf(ei->strings, sizeof (ei->strings), "default");
113388447a05SGarrett D'Amore 	ei->strindex[0] = 0;
113488447a05SGarrett D'Amore 
113588447a05SGarrett D'Amore 	return (0);
113688447a05SGarrett D'Amore }
113788447a05SGarrett D'Amore 
113888447a05SGarrett D'Amore static int
sndctl_dsp_get_playtgt(audio_client_t * c,int * tgtp)113988447a05SGarrett D'Amore sndctl_dsp_get_playtgt(audio_client_t *c, int *tgtp)
114088447a05SGarrett D'Amore {
114188447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
114288447a05SGarrett D'Amore 	*tgtp = 0;
114388447a05SGarrett D'Amore 	return (0);
114488447a05SGarrett D'Amore }
114588447a05SGarrett D'Amore 
114688447a05SGarrett D'Amore static int
sndctl_dsp_set_playtgt(audio_client_t * c,int * tgtp)114788447a05SGarrett D'Amore sndctl_dsp_set_playtgt(audio_client_t *c, int *tgtp)
114888447a05SGarrett D'Amore {
114988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
115088447a05SGarrett D'Amore 	*tgtp = 0;
115188447a05SGarrett D'Amore 	return (0);
115288447a05SGarrett D'Amore }
115388447a05SGarrett D'Amore 
115488447a05SGarrett D'Amore static int
sndctl_sysinfo(oss_sysinfo * si)115588447a05SGarrett D'Amore sndctl_sysinfo(oss_sysinfo *si)
115688447a05SGarrett D'Amore {
115788447a05SGarrett D'Amore 	bzero(si, sizeof (*si));
115888447a05SGarrett D'Amore 	(void) snprintf(si->product, sizeof (si->product), "SunOS Audio");
115988447a05SGarrett D'Amore 	(void) snprintf(si->version, sizeof (si->version), "4.0");
116088447a05SGarrett D'Amore 	si->versionnum = OSS_VERSION;
116188447a05SGarrett D'Amore 	si->numcards = oss_cnt_devs();
116288447a05SGarrett D'Amore 	si->nummixers = si->numcards - 1;
116388447a05SGarrett D'Amore 	si->numaudios = si->numcards - 1;
116488447a05SGarrett D'Amore 	si->numaudioengines = si->numaudios;
116588447a05SGarrett D'Amore 	(void) snprintf(si->license, sizeof (si->license), "CDDL");
116688447a05SGarrett D'Amore 	return (0);
116788447a05SGarrett D'Amore }
116888447a05SGarrett D'Amore 
116988447a05SGarrett D'Amore static int
sndctl_cardinfo(audio_client_t * c,oss_card_info * ci)117088447a05SGarrett D'Amore sndctl_cardinfo(audio_client_t *c, oss_card_info *ci)
117188447a05SGarrett D'Amore {
117288447a05SGarrett D'Amore 	audio_dev_t	*d;
117388447a05SGarrett D'Amore 	void		*iter;
117488447a05SGarrett D'Amore 	const char 	*info;
117588447a05SGarrett D'Amore 	int		n;
117688447a05SGarrett D'Amore 	boolean_t	release;
117788447a05SGarrett D'Amore 
117888447a05SGarrett D'Amore 	if ((n = ci->card) == -1) {
117988447a05SGarrett D'Amore 		release = B_FALSE;
118088447a05SGarrett D'Amore 		d = auclnt_get_dev(c);
118188447a05SGarrett D'Amore 		n = auclnt_get_dev_index(d);
118288447a05SGarrett D'Amore 	} else {
118388447a05SGarrett D'Amore 		release = B_TRUE;
118488447a05SGarrett D'Amore 		d = auclnt_hold_dev_by_index(n);
118588447a05SGarrett D'Amore 	}
118688447a05SGarrett D'Amore 
118788447a05SGarrett D'Amore 	bzero(ci, sizeof (*ci));
118888447a05SGarrett D'Amore 	ci->card = n;
118988447a05SGarrett D'Amore 
119088447a05SGarrett D'Amore 	if (d == NULL) {
119188447a05SGarrett D'Amore 		/*
119288447a05SGarrett D'Amore 		 * If device removed (e.g. for DR), then
119388447a05SGarrett D'Amore 		 * report a bogus removed entry.
119488447a05SGarrett D'Amore 		 */
119588447a05SGarrett D'Amore 		(void) snprintf(ci->shortname, sizeof (ci->shortname),
119688447a05SGarrett D'Amore 		    "<removed>");
119788447a05SGarrett D'Amore 		(void) snprintf(ci->longname, sizeof (ci->longname),
119888447a05SGarrett D'Amore 		    "<removed>");
119988447a05SGarrett D'Amore 		return (0);
120088447a05SGarrett D'Amore 	}
120188447a05SGarrett D'Amore 
120288447a05SGarrett D'Amore 	(void) snprintf(ci->shortname, sizeof (ci->shortname),
120388447a05SGarrett D'Amore 	    "%s", auclnt_get_dev_name(d));
120488447a05SGarrett D'Amore 	(void) snprintf(ci->longname, sizeof (ci->longname),
120588447a05SGarrett D'Amore 	    "%s (%s)", auclnt_get_dev_description(d),
120688447a05SGarrett D'Amore 	    auclnt_get_dev_version(d));
120788447a05SGarrett D'Amore 
120888447a05SGarrett D'Amore 	iter = NULL;
120988447a05SGarrett D'Amore 	while ((info = auclnt_get_dev_hw_info(d, &iter)) != NULL) {
121088447a05SGarrett D'Amore 		(void) strlcat(ci->hw_info, info, sizeof (ci->hw_info));
121188447a05SGarrett D'Amore 		(void) strlcat(ci->hw_info, "\n", sizeof (ci->hw_info));
121288447a05SGarrett D'Amore 	}
121388447a05SGarrett D'Amore 
121488447a05SGarrett D'Amore 	/*
121588447a05SGarrett D'Amore 	 * We don't report interrupt counts, ack counts (which are
121688447a05SGarrett D'Amore 	 * just "read" interrupts, not spurious), or any other flags.
121788447a05SGarrett D'Amore 	 * Nothing should be using any of this data anyway ... these
121888447a05SGarrett D'Amore 	 * values were intended for 4Front's debugging purposes.  In
121988447a05SGarrett D'Amore 	 * Solaris, drivers should use interrupt kstats to report
122088447a05SGarrett D'Amore 	 * interrupt related statistics.
122188447a05SGarrett D'Amore 	 */
122288447a05SGarrett D'Amore 	if (release)
122388447a05SGarrett D'Amore 		auclnt_release_dev(d);
122488447a05SGarrett D'Amore 	return (0);
122588447a05SGarrett D'Amore }
122688447a05SGarrett D'Amore 
122788447a05SGarrett D'Amore static int
audioinfo_walker(audio_engine_t * e,void * a)122888447a05SGarrett D'Amore audioinfo_walker(audio_engine_t *e, void *a)
122988447a05SGarrett D'Amore {
123088447a05SGarrett D'Amore 	oss_audioinfo *si = a;
123188447a05SGarrett D'Amore 	int fmt, nchan, rate, cap;
123288447a05SGarrett D'Amore 
123388447a05SGarrett D'Amore 	fmt = auclnt_engine_get_format(e);
123488447a05SGarrett D'Amore 	nchan = auclnt_engine_get_channels(e);
123588447a05SGarrett D'Amore 	rate = auclnt_engine_get_rate(e);
123688447a05SGarrett D'Amore 	cap = auclnt_engine_get_capab(e);
123788447a05SGarrett D'Amore 
123888447a05SGarrett D'Amore 	for (int i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
123988447a05SGarrett D'Amore 		if (fmt == oss_formats[i].fmt) {
124088447a05SGarrett D'Amore 			if (cap & AUDIO_CLIENT_CAP_PLAY) {
124188447a05SGarrett D'Amore 				si->oformats |= oss_formats[i].oss;
124288447a05SGarrett D'Amore 			}
124388447a05SGarrett D'Amore 			if (cap & AUDIO_CLIENT_CAP_RECORD) {
124488447a05SGarrett D'Amore 				si->iformats |= oss_formats[i].oss;
124588447a05SGarrett D'Amore 			}
124688447a05SGarrett D'Amore 			break;
124788447a05SGarrett D'Amore 		}
124888447a05SGarrett D'Amore 	}
124988447a05SGarrett D'Amore 	si->max_channels = max(nchan, si->max_channels);
125088447a05SGarrett D'Amore 	si->max_rate = max(rate, si->max_rate);
125188447a05SGarrett D'Amore 
125288447a05SGarrett D'Amore 	return (AUDIO_WALK_CONTINUE);
125388447a05SGarrett D'Amore }
125488447a05SGarrett D'Amore 
125588447a05SGarrett D'Amore static int
sndctl_audioinfo(audio_client_t * c,oss_audioinfo * si)125688447a05SGarrett D'Amore sndctl_audioinfo(audio_client_t *c, oss_audioinfo *si)
125788447a05SGarrett D'Amore {
125888447a05SGarrett D'Amore 	audio_dev_t		*d;
125988447a05SGarrett D'Amore 	const char		*name;
126088447a05SGarrett D'Amore 	int			n;
126188447a05SGarrett D'Amore 	boolean_t		release;
126288447a05SGarrett D'Amore 	unsigned		cap;
126388447a05SGarrett D'Amore 
126488447a05SGarrett D'Amore 	if ((n = si->dev) == -1) {
126588447a05SGarrett D'Amore 		release = B_FALSE;
126688447a05SGarrett D'Amore 		d = auclnt_get_dev(c);
126788447a05SGarrett D'Amore 		n = auclnt_get_dev_index(d);
126888447a05SGarrett D'Amore 	} else {
126988447a05SGarrett D'Amore 		release = B_TRUE;
127088447a05SGarrett D'Amore 		n++;	/* skip pseudo device */
127188447a05SGarrett D'Amore 		d = auclnt_hold_dev_by_index(n);
127288447a05SGarrett D'Amore 	}
127388447a05SGarrett D'Amore 
127488447a05SGarrett D'Amore 	bzero(si, sizeof (*si));
127588447a05SGarrett D'Amore 	si->dev = n - 1;
127688447a05SGarrett D'Amore 
127788447a05SGarrett D'Amore 	if (d == NULL) {
127888447a05SGarrett D'Amore 		/* if device not present, forge a false entry */
127988447a05SGarrett D'Amore 		si->card_number = n;
128088447a05SGarrett D'Amore 		si->mixer_dev = n - 1;
128188447a05SGarrett D'Amore 		si->legacy_device = -1;
128288447a05SGarrett D'Amore 		si->enabled = 0;
128388447a05SGarrett D'Amore 		(void) snprintf(si->name, sizeof (si->name), "<removed>");
128488447a05SGarrett D'Amore 		return (0);
128588447a05SGarrett D'Amore 	}
128688447a05SGarrett D'Amore 
128788447a05SGarrett D'Amore 	name = auclnt_get_dev_name(d);
128888447a05SGarrett D'Amore 	(void) snprintf(si->name, sizeof (si->name), "%s", name);
128988447a05SGarrett D'Amore 
129088447a05SGarrett D'Amore 	si->legacy_device = auclnt_get_dev_number(d);
129188447a05SGarrett D'Amore 	si->caps = 0;
129288447a05SGarrett D'Amore 
129388447a05SGarrett D'Amore 	auclnt_dev_walk_engines(d, audioinfo_walker, si);
129488447a05SGarrett D'Amore 
129588447a05SGarrett D'Amore 	cap = auclnt_get_dev_capab(d);
129688447a05SGarrett D'Amore 
129788447a05SGarrett D'Amore 	if (cap	& AUDIO_CLIENT_CAP_DUPLEX) {
129888447a05SGarrett D'Amore 		si->caps |= PCM_CAP_DUPLEX;
129988447a05SGarrett D'Amore 	}
130088447a05SGarrett D'Amore 	if (cap & AUDIO_CLIENT_CAP_PLAY) {
130188447a05SGarrett D'Amore 		si->caps |= PCM_CAP_OUTPUT;
130288447a05SGarrett D'Amore 	}
130388447a05SGarrett D'Amore 	if (cap & AUDIO_CLIENT_CAP_RECORD) {
130488447a05SGarrett D'Amore 		si->caps |= PCM_CAP_INPUT;
130588447a05SGarrett D'Amore 	}
130688447a05SGarrett D'Amore 
130788447a05SGarrett D'Amore 	if (si->caps != 0) {
130888447a05SGarrett D'Amore 		/* MMAP: we add PCM_CAP_MMAP when we we support it */
130926025630SGarrett D'Amore 		si->caps |= PCM_CAP_TRIGGER | PCM_CAP_BATCH;
131088447a05SGarrett D'Amore 		si->enabled = 1;
131188447a05SGarrett D'Amore 		si->rate_source = si->dev;
131288447a05SGarrett D'Amore 
131326025630SGarrett D'Amore 		/* we can convert and mix PCM formats */
131426025630SGarrett D'Amore 		if (!(cap & AUDIO_CLIENT_CAP_OPAQUE)) {
131588447a05SGarrett D'Amore 			si->min_channels = min(2, si->max_channels);
131688447a05SGarrett D'Amore 			si->min_rate = min(5000, si->max_rate);
131726025630SGarrett D'Amore 			si->caps |= PCM_CAP_FREERATE | PCM_CAP_MULTI;
131888447a05SGarrett D'Amore 		}
131988447a05SGarrett D'Amore 		(void) snprintf(si->devnode, sizeof (si->devnode),
132088447a05SGarrett D'Amore 		    "/dev/sound/%s:%ddsp",
132188447a05SGarrett D'Amore 		    auclnt_get_dev_driver(d), auclnt_get_dev_instance(d));
132288447a05SGarrett D'Amore 	} else {
132388447a05SGarrett D'Amore 		si->enabled = 0;	/* stops apps from using us directly */
132488447a05SGarrett D'Amore 		si->caps = PCM_CAP_VIRTUAL;
132588447a05SGarrett D'Amore 		(void) snprintf(si->devnode, sizeof (si->devnode),
132688447a05SGarrett D'Amore 		    "/dev/sndstat");
132788447a05SGarrett D'Amore 	}
132888447a05SGarrett D'Amore 
132988447a05SGarrett D'Amore 	si->pid = -1;
133088447a05SGarrett D'Amore 	(void) snprintf(si->handle, sizeof (si->handle), "%s", name);
133188447a05SGarrett D'Amore 	(void) snprintf(si->label, sizeof (si->label), "%s", name);
133288447a05SGarrett D'Amore 	si->latency = -1;
133388447a05SGarrett D'Amore 	si->card_number = n;
133488447a05SGarrett D'Amore 	si->mixer_dev = n - 1;
133588447a05SGarrett D'Amore 
133688447a05SGarrett D'Amore 	if (release)
133788447a05SGarrett D'Amore 		auclnt_release_dev(d);
133888447a05SGarrett D'Amore 
133988447a05SGarrett D'Amore 	return (0);
134088447a05SGarrett D'Amore }
134188447a05SGarrett D'Amore 
134288447a05SGarrett D'Amore static int
sound_mixer_info(audio_client_t * c,mixer_info * mi)134388447a05SGarrett D'Amore sound_mixer_info(audio_client_t *c, mixer_info *mi)
134488447a05SGarrett D'Amore {
134588447a05SGarrett D'Amore 	audio_dev_t	*d;
134688447a05SGarrett D'Amore 	const char	*name;
134788447a05SGarrett D'Amore 
134888447a05SGarrett D'Amore 	d = auclnt_get_dev(c);
134988447a05SGarrett D'Amore 
135088447a05SGarrett D'Amore 	name = auclnt_get_dev_name(d);
135188447a05SGarrett D'Amore 	(void) snprintf(mi->id, sizeof (mi->id), "%s", name);
135288447a05SGarrett D'Amore 	(void) snprintf(mi->name, sizeof (mi->name), "%s", name);
135388447a05SGarrett D'Amore 	(void) snprintf(mi->handle, sizeof (mi->handle), "%s", name);
1354682cb104SGarrett D'Amore 	mi->modify_counter = (int)auclnt_dev_get_serial(d);
135588447a05SGarrett D'Amore 	mi->card_number = auclnt_get_dev_index(d);
135688447a05SGarrett D'Amore 	mi->port_number = 0;
135788447a05SGarrett D'Amore 	return (0);
135888447a05SGarrett D'Amore }
135988447a05SGarrett D'Amore 
136088447a05SGarrett D'Amore static int
sound_mixer_read_devmask(audio_client_t * c,int * devmask)136188447a05SGarrett D'Amore sound_mixer_read_devmask(audio_client_t *c, int *devmask)
136288447a05SGarrett D'Amore {
136388447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
136488447a05SGarrett D'Amore 	*devmask = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_IGAIN;
136588447a05SGarrett D'Amore 	return (0);
136688447a05SGarrett D'Amore }
136788447a05SGarrett D'Amore 
136888447a05SGarrett D'Amore static int
sound_mixer_read_recmask(audio_client_t * c,int * recmask)136988447a05SGarrett D'Amore sound_mixer_read_recmask(audio_client_t *c, int *recmask)
137088447a05SGarrett D'Amore {
137188447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
137288447a05SGarrett D'Amore 	*recmask = 0;
137388447a05SGarrett D'Amore 	return (0);
137488447a05SGarrett D'Amore }
137588447a05SGarrett D'Amore 
137688447a05SGarrett D'Amore static int
sound_mixer_read_recsrc(audio_client_t * c,int * recsrc)137788447a05SGarrett D'Amore sound_mixer_read_recsrc(audio_client_t *c, int *recsrc)
137888447a05SGarrett D'Amore {
137988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
138088447a05SGarrett D'Amore 	*recsrc = 0;
138188447a05SGarrett D'Amore 	return (0);
138288447a05SGarrett D'Amore }
138388447a05SGarrett D'Amore 
138488447a05SGarrett D'Amore static int
sound_mixer_read_caps(audio_client_t * c,int * caps)138588447a05SGarrett D'Amore sound_mixer_read_caps(audio_client_t *c, int *caps)
138688447a05SGarrett D'Amore {
138788447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
138888447a05SGarrett D'Amore 	/* single recording source... sort of */
138988447a05SGarrett D'Amore 	*caps = SOUND_CAP_EXCL_INPUT;
139088447a05SGarrett D'Amore 	return (0);
139188447a05SGarrett D'Amore }
139288447a05SGarrett D'Amore 
139388447a05SGarrett D'Amore static int
sndctl_mixerinfo(audio_client_t * c,oss_mixerinfo * mi)139488447a05SGarrett D'Amore sndctl_mixerinfo(audio_client_t *c, oss_mixerinfo *mi)
139588447a05SGarrett D'Amore {
139688447a05SGarrett D'Amore 	audio_dev_t		*d;
139788447a05SGarrett D'Amore 	ossdev_t 		*odev;
139888447a05SGarrett D'Amore 	const char		*name;
139988447a05SGarrett D'Amore 	int			n;
140088447a05SGarrett D'Amore 	boolean_t		release = B_FALSE;
140188447a05SGarrett D'Amore 
140288447a05SGarrett D'Amore 	if ((n = mi->dev) == -1) {
140388447a05SGarrett D'Amore 		release = B_FALSE;
140488447a05SGarrett D'Amore 		d = auclnt_get_dev(c);
140588447a05SGarrett D'Amore 		n = auclnt_get_dev_index(d);
140688447a05SGarrett D'Amore 	} else {
140788447a05SGarrett D'Amore 		release = B_TRUE;
140888447a05SGarrett D'Amore 		n++;
140988447a05SGarrett D'Amore 		d = auclnt_hold_dev_by_index(n);
141088447a05SGarrett D'Amore 	}
141188447a05SGarrett D'Amore 
141288447a05SGarrett D'Amore 	bzero(mi, sizeof (*mi));
141388447a05SGarrett D'Amore 	mi->dev = n - 1;
141488447a05SGarrett D'Amore 
141588447a05SGarrett D'Amore 	if (d == NULL) {
141688447a05SGarrett D'Amore 		mi->card_number = n;
141788447a05SGarrett D'Amore 		mi->enabled = 0;
141888447a05SGarrett D'Amore 		mi->legacy_device = -1;
141988447a05SGarrett D'Amore 		(void) snprintf(mi->name, sizeof (mi->name), "<removed>");
142088447a05SGarrett D'Amore 		(void) snprintf(mi->id, sizeof (mi->id), "<removed>");
142188447a05SGarrett D'Amore 		return (0);
142288447a05SGarrett D'Amore 	}
142388447a05SGarrett D'Amore 
142488447a05SGarrett D'Amore 	if ((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) {
142588447a05SGarrett D'Amore 		if (release)
142688447a05SGarrett D'Amore 			auclnt_release_dev(d);
142788447a05SGarrett D'Amore 		return (EINVAL);
142888447a05SGarrett D'Amore 	}
142988447a05SGarrett D'Amore 
143088447a05SGarrett D'Amore 	name = auclnt_get_dev_name(d);
143188447a05SGarrett D'Amore 	(void) snprintf(mi->name, sizeof (mi->name), "%s", name);
143288447a05SGarrett D'Amore 	(void) snprintf(mi->id, sizeof (mi->id), "%s", name);
143388447a05SGarrett D'Amore 	(void) snprintf(mi->handle, sizeof (mi->handle), "%s", name);
1434682cb104SGarrett D'Amore 	mi->modify_counter = (int)auclnt_dev_get_serial(d);
143588447a05SGarrett D'Amore 	mi->card_number = auclnt_get_dev_index(d);
143688447a05SGarrett D'Amore 	mi->legacy_device = auclnt_get_dev_number(d);
143788447a05SGarrett D'Amore 	if (mi->legacy_device >= 0) {
143888447a05SGarrett D'Amore 		(void) snprintf(mi->devnode, sizeof (mi->devnode),
143988447a05SGarrett D'Amore 		    "/dev/sound/%s:%dmixer",
144088447a05SGarrett D'Amore 		    auclnt_get_dev_driver(d), auclnt_get_dev_instance(d));
144188447a05SGarrett D'Amore 		mi->enabled = 1;
144288447a05SGarrett D'Amore 	} else {
144388447a05SGarrett D'Amore 		/* special nodes use generic sndstat node */
144488447a05SGarrett D'Amore 		(void) snprintf(mi->devnode, sizeof (mi->devnode),
144588447a05SGarrett D'Amore 		    "/dev/sndstat");
144688447a05SGarrett D'Amore 		mi->enabled = 0;
144788447a05SGarrett D'Amore 	}
144888447a05SGarrett D'Amore 	mi->nrext = odev->d_nctrl;
144988447a05SGarrett D'Amore 
145088447a05SGarrett D'Amore 	if (release)
145188447a05SGarrett D'Amore 		auclnt_release_dev(d);
145288447a05SGarrett D'Amore 
145388447a05SGarrett D'Amore 	return (0);
145488447a05SGarrett D'Amore }
145588447a05SGarrett D'Amore 
145688447a05SGarrett D'Amore static int
sndctl_dsp_getblksize(audio_client_t * c,int * fragsz)145788447a05SGarrett D'Amore sndctl_dsp_getblksize(audio_client_t *c, int *fragsz)
145888447a05SGarrett D'Amore {
145988447a05SGarrett D'Amore 	int	oflag = auclnt_get_oflag(c);
146088447a05SGarrett D'Amore 
146188447a05SGarrett D'Amore 	if (oflag & FWRITE)
146288447a05SGarrett D'Amore 		*fragsz  = auclnt_get_fragsz(auclnt_output_stream(c));
146388447a05SGarrett D'Amore 	else if (oflag & FREAD)
146488447a05SGarrett D'Amore 		*fragsz  = auclnt_get_fragsz(auclnt_input_stream(c));
146588447a05SGarrett D'Amore 
146688447a05SGarrett D'Amore 	return (0);
146788447a05SGarrett D'Amore }
146888447a05SGarrett D'Amore 
146988447a05SGarrett D'Amore static int
sndctl_dsp_getospace(audio_client_t * c,audio_buf_info * bi)147088447a05SGarrett D'Amore sndctl_dsp_getospace(audio_client_t *c, audio_buf_info *bi)
147188447a05SGarrett D'Amore {
147288447a05SGarrett D'Amore 	audio_stream_t	*sp;
147388447a05SGarrett D'Amore 	unsigned	n;
147488447a05SGarrett D'Amore 
147588447a05SGarrett D'Amore 	if ((auclnt_get_oflag(c) & FWRITE) == 0) {
147688447a05SGarrett D'Amore 		return (EACCES);
147788447a05SGarrett D'Amore 	}
147888447a05SGarrett D'Amore 
147988447a05SGarrett D'Amore 	sp = auclnt_output_stream(c);
148088447a05SGarrett D'Amore 	n = auclnt_get_nframes(sp) - auclnt_get_count(sp);
148188447a05SGarrett D'Amore 
148288447a05SGarrett D'Amore 	bi->fragsize  = auclnt_get_fragsz(sp);
148388447a05SGarrett D'Amore 	bi->fragstotal = auclnt_get_nfrags(sp);
148488447a05SGarrett D'Amore 	bi->bytes = (n * auclnt_get_framesz(sp));
148588447a05SGarrett D'Amore 	bi->fragments = bi->bytes / bi->fragsize;
148688447a05SGarrett D'Amore 
148788447a05SGarrett D'Amore 	return (0);
148888447a05SGarrett D'Amore }
148988447a05SGarrett D'Amore 
149088447a05SGarrett D'Amore static int
sndctl_dsp_getispace(audio_client_t * c,audio_buf_info * bi)149188447a05SGarrett D'Amore sndctl_dsp_getispace(audio_client_t *c, audio_buf_info *bi)
149288447a05SGarrett D'Amore {
149388447a05SGarrett D'Amore 	audio_stream_t	*sp;
149488447a05SGarrett D'Amore 	unsigned	n;
149588447a05SGarrett D'Amore 
149688447a05SGarrett D'Amore 	if ((auclnt_get_oflag(c) & FREAD) == 0) {
149788447a05SGarrett D'Amore 		return (EACCES);
149888447a05SGarrett D'Amore 	}
149988447a05SGarrett D'Amore 
150088447a05SGarrett D'Amore 	sp = auclnt_input_stream(c);
150188447a05SGarrett D'Amore 	n = auclnt_get_count(sp);
150288447a05SGarrett D'Amore 
150388447a05SGarrett D'Amore 	bi->fragsize  = auclnt_get_fragsz(sp);
150488447a05SGarrett D'Amore 	bi->fragstotal = auclnt_get_nfrags(sp);
150588447a05SGarrett D'Amore 	bi->bytes = (n * auclnt_get_framesz(sp));
150688447a05SGarrett D'Amore 	bi->fragments = bi->bytes / bi->fragsize;
150788447a05SGarrett D'Amore 
150888447a05SGarrett D'Amore 	return (0);
150988447a05SGarrett D'Amore }
151088447a05SGarrett D'Amore 
151188447a05SGarrett D'Amore static int
sndctl_dsp_getodelay(audio_client_t * c,int * bytes)151288447a05SGarrett D'Amore sndctl_dsp_getodelay(audio_client_t *c, int *bytes)
151388447a05SGarrett D'Amore {
151488447a05SGarrett D'Amore 	unsigned	framesz;
151588447a05SGarrett D'Amore 	unsigned	slen, flen;
151688447a05SGarrett D'Amore 
151788447a05SGarrett D'Amore 	if (auclnt_get_oflag(c) & FWRITE) {
151888447a05SGarrett D'Amore 		audio_stream_t	*sp = auclnt_output_stream(c);
151988447a05SGarrett D'Amore 		framesz = auclnt_get_framesz(sp);
152088447a05SGarrett D'Amore 		auclnt_get_output_qlen(c, &slen, &flen);
152188447a05SGarrett D'Amore 		*bytes = (slen + flen) * framesz;
152288447a05SGarrett D'Amore 	} else {
152388447a05SGarrett D'Amore 		*bytes = 0;
152488447a05SGarrett D'Amore 	}
152588447a05SGarrett D'Amore 	return (0);
152688447a05SGarrett D'Amore }
152788447a05SGarrett D'Amore 
152888447a05SGarrett D'Amore static int
sndctl_dsp_current_iptr(audio_client_t * c,oss_count_t * count)152988447a05SGarrett D'Amore sndctl_dsp_current_iptr(audio_client_t *c, oss_count_t *count)
153088447a05SGarrett D'Amore {
153188447a05SGarrett D'Amore 	if (auclnt_get_oflag(c) & FREAD) {
153288447a05SGarrett D'Amore 		count->samples = auclnt_get_samples(auclnt_input_stream(c));
153388447a05SGarrett D'Amore 		count->fifo_samples = 0;	/* not quite accurate */
153488447a05SGarrett D'Amore 	} else {
153588447a05SGarrett D'Amore 		count->samples = 0;
153688447a05SGarrett D'Amore 		count->fifo_samples = 0;
153788447a05SGarrett D'Amore 	}
153888447a05SGarrett D'Amore 	return (0);
153988447a05SGarrett D'Amore }
154088447a05SGarrett D'Amore 
154188447a05SGarrett D'Amore static int
sndctl_dsp_current_optr(audio_client_t * c,oss_count_t * count)154288447a05SGarrett D'Amore sndctl_dsp_current_optr(audio_client_t *c, oss_count_t *count)
154388447a05SGarrett D'Amore {
154488447a05SGarrett D'Amore 	unsigned samples, fifo;
154588447a05SGarrett D'Amore 
154688447a05SGarrett D'Amore 	if (auclnt_get_oflag(c) & FWRITE) {
154788447a05SGarrett D'Amore 		auclnt_get_output_qlen(c, &samples, &fifo);
154888447a05SGarrett D'Amore 		count->samples = samples;
154988447a05SGarrett D'Amore 		count->fifo_samples = fifo;
155088447a05SGarrett D'Amore 	} else {
155188447a05SGarrett D'Amore 		count->samples = 0;
155288447a05SGarrett D'Amore 		count->fifo_samples = 0;
155388447a05SGarrett D'Amore 	}
155488447a05SGarrett D'Amore 	return (0);
155588447a05SGarrett D'Amore }
155688447a05SGarrett D'Amore 
155788447a05SGarrett D'Amore static int
sndctl_dsp_getoptr(audio_client_t * c,count_info * ci)155888447a05SGarrett D'Amore sndctl_dsp_getoptr(audio_client_t *c, count_info *ci)
155988447a05SGarrett D'Amore {
156088447a05SGarrett D'Amore 	audio_stream_t	*sp;
156188447a05SGarrett D'Amore 	unsigned	framesz;
156288447a05SGarrett D'Amore 	unsigned	fragsz;
156388447a05SGarrett D'Amore 
156488447a05SGarrett D'Amore 	bzero(ci, sizeof (*ci));
156588447a05SGarrett D'Amore 	if ((auclnt_get_oflag(c) & FWRITE) == 0) {
156688447a05SGarrett D'Amore 		return (0);
156788447a05SGarrett D'Amore 	}
156888447a05SGarrett D'Amore 	sp = auclnt_output_stream(c);
156988447a05SGarrett D'Amore 	framesz = auclnt_get_framesz(sp);
157088447a05SGarrett D'Amore 	fragsz = auclnt_get_fragsz(sp);
157188447a05SGarrett D'Amore 	ci->blocks = auclnt_get_samples(sp) * framesz / fragsz;
157288447a05SGarrett D'Amore 	auclnt_set_samples(sp, 0);
157388447a05SGarrett D'Amore 	ci->bytes = auclnt_get_tail(sp) * framesz;
157488447a05SGarrett D'Amore 	ci->ptr = auclnt_get_tidx(sp) * framesz;
157588447a05SGarrett D'Amore 	return (0);
157688447a05SGarrett D'Amore }
157788447a05SGarrett D'Amore 
157888447a05SGarrett D'Amore static int
sndctl_dsp_getiptr(audio_client_t * c,count_info * ci)157988447a05SGarrett D'Amore sndctl_dsp_getiptr(audio_client_t *c, count_info *ci)
158088447a05SGarrett D'Amore {
158188447a05SGarrett D'Amore 	audio_stream_t	*sp;
158288447a05SGarrett D'Amore 	unsigned	framesz;
158388447a05SGarrett D'Amore 	unsigned	fragsz;
158488447a05SGarrett D'Amore 
158588447a05SGarrett D'Amore 	bzero(ci, sizeof (*ci));
158688447a05SGarrett D'Amore 	if ((auclnt_get_oflag(c) & FREAD) == 0) {
158788447a05SGarrett D'Amore 		return (0);
158888447a05SGarrett D'Amore 	}
158988447a05SGarrett D'Amore 	sp = auclnt_input_stream(c);
159088447a05SGarrett D'Amore 	framesz = auclnt_get_framesz(sp);
159188447a05SGarrett D'Amore 	fragsz = auclnt_get_fragsz(sp);
159288447a05SGarrett D'Amore 	ci->blocks = auclnt_get_samples(sp) * framesz / fragsz;
159388447a05SGarrett D'Amore 	auclnt_set_samples(sp, 0);
159488447a05SGarrett D'Amore 	ci->bytes = auclnt_get_head(sp) * framesz;
159588447a05SGarrett D'Amore 	ci->ptr = auclnt_get_hidx(sp) * framesz;
159688447a05SGarrett D'Amore 	return (0);
159788447a05SGarrett D'Amore }
159888447a05SGarrett D'Amore 
159988447a05SGarrett D'Amore static int
sndctl_dsp_geterror(audio_client_t * c,audio_errinfo * bi)160088447a05SGarrett D'Amore sndctl_dsp_geterror(audio_client_t *c, audio_errinfo *bi)
160188447a05SGarrett D'Amore {
160288447a05SGarrett D'Amore 	audio_stream_t	*sp;
160388447a05SGarrett D'Amore 	unsigned	fragsz;
160488447a05SGarrett D'Amore 	/*
160588447a05SGarrett D'Amore 	 * Note: The use of this structure is unsafe... different
160688447a05SGarrett D'Amore 	 * meanings for error codes are used by different implementations,
160788447a05SGarrett D'Amore 	 * according to the spec.  (Even different versions of the same
160888447a05SGarrett D'Amore 	 * implementation could have different values.)
160988447a05SGarrett D'Amore 	 *
161088447a05SGarrett D'Amore 	 * Rather than try to come up with a reliable solution here, we
161188447a05SGarrett D'Amore 	 * don't use it.  If you want to report errors, or see the result
161288447a05SGarrett D'Amore 	 * of errors, use syslog.
161388447a05SGarrett D'Amore 	 */
161488447a05SGarrett D'Amore 	bzero(bi, sizeof (*bi));
161588447a05SGarrett D'Amore 
161688447a05SGarrett D'Amore 	sp = auclnt_output_stream(c);
161788447a05SGarrett D'Amore 	fragsz = max(auclnt_get_fragsz(sp), 1);
161888447a05SGarrett D'Amore 	bi->play_underruns = (int)((auclnt_get_errors(sp) + (fragsz - 1)) /
161988447a05SGarrett D'Amore 	    fragsz);
162088447a05SGarrett D'Amore 	auclnt_set_errors(sp, 0);
162188447a05SGarrett D'Amore 
162288447a05SGarrett D'Amore 	sp = auclnt_input_stream(c);
162388447a05SGarrett D'Amore 	fragsz = max(auclnt_get_fragsz(sp), 1);
162488447a05SGarrett D'Amore 	bi->rec_overruns = (int)((auclnt_get_errors(sp) + (fragsz - 1)) /
162588447a05SGarrett D'Amore 	    fragsz);
162688447a05SGarrett D'Amore 	auclnt_set_errors(sp, 0);
162788447a05SGarrett D'Amore 
162888447a05SGarrett D'Amore 	return (0);
162988447a05SGarrett D'Amore }
163088447a05SGarrett D'Amore 
163188447a05SGarrett D'Amore static int
sndctl_sun_send_number(audio_client_t * c,int * num,cred_t * cr)163288447a05SGarrett D'Amore sndctl_sun_send_number(audio_client_t *c, int *num, cred_t *cr)
163388447a05SGarrett D'Amore {
163488447a05SGarrett D'Amore 	audio_dev_t	*dev;
163588447a05SGarrett D'Amore 	int		rv;
163688447a05SGarrett D'Amore 
163788447a05SGarrett D'Amore 	if ((rv = drv_priv(cr)) != 0) {
163888447a05SGarrett D'Amore 		return (rv);
163988447a05SGarrett D'Amore 	}
164088447a05SGarrett D'Amore 
164188447a05SGarrett D'Amore 	dev = auclnt_get_dev(c);
164288447a05SGarrett D'Amore 	auclnt_set_dev_number(dev, *num);
164388447a05SGarrett D'Amore 	return (0);
164488447a05SGarrett D'Amore }
164588447a05SGarrett D'Amore 
164688447a05SGarrett D'Amore static int
oss_getversion(int * versp)164788447a05SGarrett D'Amore oss_getversion(int *versp)
164888447a05SGarrett D'Amore {
164988447a05SGarrett D'Amore 	*versp = OSS_VERSION;
165088447a05SGarrett D'Amore 	return (0);
165188447a05SGarrett D'Amore }
165288447a05SGarrett D'Amore 
165388447a05SGarrett D'Amore static int
oss_ioctl(audio_client_t * c,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)165488447a05SGarrett D'Amore oss_ioctl(audio_client_t *c, int cmd, intptr_t arg, int mode, cred_t *credp,
165588447a05SGarrett D'Amore     int *rvalp)
165688447a05SGarrett D'Amore {
165788447a05SGarrett D'Amore 	int	sz;
165888447a05SGarrett D'Amore 	void	*data;
165988447a05SGarrett D'Amore 	int	rv = 0;
166088447a05SGarrett D'Amore 
166188447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(credp));
166288447a05SGarrett D'Amore 
166388447a05SGarrett D'Amore 	sz = OSSIOC_GETSZ(cmd);
166488447a05SGarrett D'Amore 
166588447a05SGarrett D'Amore 	if ((cmd & (OSSIOC_IN | OSSIOC_OUT)) && sz) {
166688447a05SGarrett D'Amore 		if ((data = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
166788447a05SGarrett D'Amore 			return (ENOMEM);
166888447a05SGarrett D'Amore 		}
166988447a05SGarrett D'Amore 	} else {
167088447a05SGarrett D'Amore 		sz = 0;
167188447a05SGarrett D'Amore 	}
167288447a05SGarrett D'Amore 
167388447a05SGarrett D'Amore 	if (cmd & OSSIOC_IN) {
167488447a05SGarrett D'Amore 		if ((rv = ddi_copyin((void *)arg, data, sz, mode)) != 0) {
167588447a05SGarrett D'Amore 			goto done;
167688447a05SGarrett D'Amore 		}
167788447a05SGarrett D'Amore 	}
167888447a05SGarrett D'Amore 
167988447a05SGarrett D'Amore 	switch (cmd) {
168088447a05SGarrett D'Amore 		/*
168188447a05SGarrett D'Amore 		 * DSP specific ioctls
168288447a05SGarrett D'Amore 		 */
168388447a05SGarrett D'Amore 	case SNDCTL_DSP_HALT:
168488447a05SGarrett D'Amore 		rv = sndctl_dsp_halt(c);
168588447a05SGarrett D'Amore 		break;
168688447a05SGarrett D'Amore 
168788447a05SGarrett D'Amore 	case SNDCTL_DSP_SYNC:
168888447a05SGarrett D'Amore 		rv = sndctl_dsp_sync(c);
168988447a05SGarrett D'Amore 		break;
169088447a05SGarrett D'Amore 
169188447a05SGarrett D'Amore 	case SNDCTL_DSP_SPEED:
169288447a05SGarrett D'Amore 		rv = sndctl_dsp_speed(c, (int *)data);
169388447a05SGarrett D'Amore 		break;
169488447a05SGarrett D'Amore 	case SNDCTL_DSP_SETFMT:
169588447a05SGarrett D'Amore 		rv = sndctl_dsp_setfmt(c, (int *)data);
169688447a05SGarrett D'Amore 		break;
169788447a05SGarrett D'Amore 	case SNDCTL_DSP_GETFMTS:
169888447a05SGarrett D'Amore 		rv = sndctl_dsp_getfmts(c, (int *)data);
169988447a05SGarrett D'Amore 		break;
170088447a05SGarrett D'Amore 	case SNDCTL_DSP_STEREO:
170188447a05SGarrett D'Amore 		rv = sndctl_dsp_stereo(c, (int *)data);
170288447a05SGarrett D'Amore 		break;
170388447a05SGarrett D'Amore 	case SNDCTL_DSP_CHANNELS:
170488447a05SGarrett D'Amore 		rv = sndctl_dsp_channels(c, (int *)data);
170588447a05SGarrett D'Amore 		break;
170688447a05SGarrett D'Amore 	case SNDCTL_DSP_POST:
170788447a05SGarrett D'Amore 		rv = sndctl_dsp_post(c);
170888447a05SGarrett D'Amore 		break;
170988447a05SGarrett D'Amore 	case SNDCTL_DSP_GETCAPS:
171088447a05SGarrett D'Amore 		rv = sndctl_dsp_getcaps(c, (int *)data);
171188447a05SGarrett D'Amore 		break;
171288447a05SGarrett D'Amore 	case SNDCTL_DSP_GETTRIGGER:
171388447a05SGarrett D'Amore 		rv = sndctl_dsp_gettrigger(c, (int *)data);
171488447a05SGarrett D'Amore 		break;
171588447a05SGarrett D'Amore 	case SNDCTL_DSP_SETTRIGGER:
171688447a05SGarrett D'Amore 		rv = sndctl_dsp_settrigger(c, (int *)data);
171788447a05SGarrett D'Amore 		break;
171888447a05SGarrett D'Amore 	case SNDCTL_DSP_GETPLAYVOL:
171988447a05SGarrett D'Amore 	case SOUND_MIXER_READ_VOLUME:	/* legacy mixer on dsp */
172088447a05SGarrett D'Amore 	case SOUND_MIXER_READ_PCM:	/* legacy mixer on dsp */
172188447a05SGarrett D'Amore 	case SOUND_MIXER_READ_OGAIN:	/* legacy mixer on dsp */
172288447a05SGarrett D'Amore 		rv = sndctl_dsp_getplayvol(c, (int *)data);
172388447a05SGarrett D'Amore 		break;
172488447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_VOLUME:	/* legacy mixer on dsp */
172588447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_PCM:	/* legacy mixer on dsp */
172688447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_OGAIN:	/* legacy mixer on dsp */
172788447a05SGarrett D'Amore 		rv = sound_mixer_write_ogain(c, (int *)data);
172888447a05SGarrett D'Amore 		break;
172988447a05SGarrett D'Amore 	case SNDCTL_DSP_SETPLAYVOL:
173088447a05SGarrett D'Amore 		rv = sndctl_dsp_setplayvol(c, (int *)data);
173188447a05SGarrett D'Amore 		break;
173288447a05SGarrett D'Amore 	case SNDCTL_DSP_READCTL:
173388447a05SGarrett D'Amore 		rv = sndctl_dsp_readctl(c, (oss_digital_control *)data);
173488447a05SGarrett D'Amore 		break;
173588447a05SGarrett D'Amore 	case SNDCTL_DSP_WRITECTL:
173688447a05SGarrett D'Amore 		rv = sndctl_dsp_writectl(c, (oss_digital_control *)data);
173788447a05SGarrett D'Amore 		break;
173888447a05SGarrett D'Amore 	case SNDCTL_DSP_COOKEDMODE:
173988447a05SGarrett D'Amore 		rv = sndctl_dsp_cookedmode(c, (int *)data);
174088447a05SGarrett D'Amore 		break;
174188447a05SGarrett D'Amore 	case SNDCTL_DSP_SILENCE:
174288447a05SGarrett D'Amore 		rv = sndctl_dsp_silence(c);
174388447a05SGarrett D'Amore 		break;
174488447a05SGarrett D'Amore 	case SNDCTL_DSP_SKIP:
174588447a05SGarrett D'Amore 		rv = sndctl_dsp_skip(c);
174688447a05SGarrett D'Amore 		break;
174788447a05SGarrett D'Amore 	case SNDCTL_DSP_HALT_INPUT:
174888447a05SGarrett D'Amore 		rv = sndctl_dsp_halt_input(c);
174988447a05SGarrett D'Amore 		break;
175088447a05SGarrett D'Amore 	case SNDCTL_DSP_HALT_OUTPUT:
175188447a05SGarrett D'Amore 		rv = sndctl_dsp_halt_output(c);
175288447a05SGarrett D'Amore 		break;
175388447a05SGarrett D'Amore 	case SNDCTL_DSP_GET_RECSRC_NAMES:
175488447a05SGarrett D'Amore 		rv = sndctl_dsp_get_recsrc_names(c, (oss_mixer_enuminfo *)data);
175588447a05SGarrett D'Amore 		break;
175688447a05SGarrett D'Amore 	case SNDCTL_DSP_SETFRAGMENT:
175788447a05SGarrett D'Amore 		rv = sndctl_dsp_setfragment(c, (int *)data);
175888447a05SGarrett D'Amore 		break;
175988447a05SGarrett D'Amore 	case SNDCTL_DSP_GET_RECSRC:
176088447a05SGarrett D'Amore 		rv = sndctl_dsp_get_recsrc(c, (int *)data);
176188447a05SGarrett D'Amore 		break;
176288447a05SGarrett D'Amore 	case SNDCTL_DSP_SET_RECSRC:
176388447a05SGarrett D'Amore 		rv = sndctl_dsp_set_recsrc(c, (int *)data);
176488447a05SGarrett D'Amore 		break;
176588447a05SGarrett D'Amore 	case SNDCTL_DSP_GET_PLAYTGT_NAMES:
176688447a05SGarrett D'Amore 		rv = sndctl_dsp_get_playtgt_names(c,
176788447a05SGarrett D'Amore 		    (oss_mixer_enuminfo *)data);
176888447a05SGarrett D'Amore 		break;
176988447a05SGarrett D'Amore 	case SNDCTL_DSP_GET_PLAYTGT:
177088447a05SGarrett D'Amore 		rv = sndctl_dsp_get_playtgt(c, (int *)data);
177188447a05SGarrett D'Amore 		break;
177288447a05SGarrett D'Amore 	case SNDCTL_DSP_SET_PLAYTGT:
177388447a05SGarrett D'Amore 		rv = sndctl_dsp_set_playtgt(c, (int *)data);
177488447a05SGarrett D'Amore 		break;
177588447a05SGarrett D'Amore 	case SNDCTL_DSP_GETRECVOL:
177688447a05SGarrett D'Amore 	case SOUND_MIXER_READ_RECGAIN:	/* legacy mixer on dsp */
177788447a05SGarrett D'Amore 	case SOUND_MIXER_READ_RECLEV:	/* legacy mixer on dsp */
177888447a05SGarrett D'Amore 	case SOUND_MIXER_READ_IGAIN:	/* legacy mixer on dsp */
177988447a05SGarrett D'Amore 		rv = sndctl_dsp_getrecvol(c, (int *)data);
178088447a05SGarrett D'Amore 		break;
178188447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_RECGAIN:	/* legacy mixer on dsp */
178288447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_RECLEV:	/* legacy mixer on dsp */
178388447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_IGAIN:	/* legacy mixer on dsp */
178488447a05SGarrett D'Amore 		rv = sound_mixer_write_igain(c, (int *)data);
178588447a05SGarrett D'Amore 		break;
178688447a05SGarrett D'Amore 	case SNDCTL_DSP_SETRECVOL:
178788447a05SGarrett D'Amore 		rv = sndctl_dsp_setrecvol(c, (int *)data);
178888447a05SGarrett D'Amore 		break;
1789a702341cSGarrett D'Amore 	case SNDCTL_DSP_SUBDIVIDE:	/* Ignored */
179088447a05SGarrett D'Amore 	case SNDCTL_DSP_SETDUPLEX:	/* Ignored */
179188447a05SGarrett D'Amore 	case SNDCTL_DSP_LOW_WATER:	/* Ignored */
179288447a05SGarrett D'Amore 	case SNDCTL_DSP_PROFILE:	/* Ignored */
179388447a05SGarrett D'Amore 		rv = 0;
179488447a05SGarrett D'Amore 		break;
1795a702341cSGarrett D'Amore 	case SNDCTL_DSP_POLICY:
1796a702341cSGarrett D'Amore 		rv = sndctl_dsp_policy(c, (int *)data);
1797a702341cSGarrett D'Amore 		break;
179888447a05SGarrett D'Amore 	case SNDCTL_DSP_GETBLKSIZE:
179988447a05SGarrett D'Amore 		rv = sndctl_dsp_getblksize(c, (int *)data);
180088447a05SGarrett D'Amore 		break;
180188447a05SGarrett D'Amore 	case SNDCTL_DSP_GETOSPACE:
180288447a05SGarrett D'Amore 		rv = sndctl_dsp_getospace(c, (audio_buf_info *)data);
180388447a05SGarrett D'Amore 		break;
180488447a05SGarrett D'Amore 	case SNDCTL_DSP_GETISPACE:
180588447a05SGarrett D'Amore 		rv = sndctl_dsp_getispace(c, (audio_buf_info *)data);
180688447a05SGarrett D'Amore 		break;
180788447a05SGarrett D'Amore 	case SNDCTL_DSP_GETODELAY:
180888447a05SGarrett D'Amore 		rv = sndctl_dsp_getodelay(c, (int *)data);
180988447a05SGarrett D'Amore 		break;
181088447a05SGarrett D'Amore 	case SNDCTL_DSP_GETOPTR:
181188447a05SGarrett D'Amore 		rv = sndctl_dsp_getoptr(c, (count_info *)data);
181288447a05SGarrett D'Amore 		break;
181388447a05SGarrett D'Amore 	case SNDCTL_DSP_GETIPTR:
181488447a05SGarrett D'Amore 		rv = sndctl_dsp_getiptr(c, (count_info *)data);
181588447a05SGarrett D'Amore 		break;
181688447a05SGarrett D'Amore 	case SNDCTL_DSP_GETERROR:
181788447a05SGarrett D'Amore 		rv = sndctl_dsp_geterror(c, (audio_errinfo *)data);
181888447a05SGarrett D'Amore 		break;
181988447a05SGarrett D'Amore 	case SNDCTL_DSP_CURRENT_IPTR:
182088447a05SGarrett D'Amore 		rv = sndctl_dsp_current_iptr(c, (oss_count_t *)data);
182188447a05SGarrett D'Amore 		break;
182288447a05SGarrett D'Amore 	case SNDCTL_DSP_CURRENT_OPTR:
182388447a05SGarrett D'Amore 		rv = sndctl_dsp_current_optr(c, (oss_count_t *)data);
182488447a05SGarrett D'Amore 		break;
182588447a05SGarrett D'Amore 
182688447a05SGarrett D'Amore 		/*
182788447a05SGarrett D'Amore 		 * Shared ioctls with /dev/mixer.
182888447a05SGarrett D'Amore 		 */
182988447a05SGarrett D'Amore 	case OSS_GETVERSION:
183088447a05SGarrett D'Amore 		rv = oss_getversion((int *)data);
183188447a05SGarrett D'Amore 		break;
183288447a05SGarrett D'Amore 	case SNDCTL_CARDINFO:
183388447a05SGarrett D'Amore 		rv = sndctl_cardinfo(c, (oss_card_info *)data);
183488447a05SGarrett D'Amore 		break;
183588447a05SGarrett D'Amore 	case SNDCTL_ENGINEINFO:
183688447a05SGarrett D'Amore 	case SNDCTL_AUDIOINFO:
183788447a05SGarrett D'Amore 	case SNDCTL_AUDIOINFO_EX:
183888447a05SGarrett D'Amore 		rv = sndctl_audioinfo(c, (oss_audioinfo *)data);
183988447a05SGarrett D'Amore 		break;
184088447a05SGarrett D'Amore 	case SNDCTL_SYSINFO:
184188447a05SGarrett D'Amore 		rv = sndctl_sysinfo((oss_sysinfo *)data);
184288447a05SGarrett D'Amore 		break;
184388447a05SGarrett D'Amore 	case SNDCTL_MIXERINFO:
184488447a05SGarrett D'Amore 		rv = sndctl_mixerinfo(c, (oss_mixerinfo *)data);
184588447a05SGarrett D'Amore 		break;
184688447a05SGarrett D'Amore 	case SOUND_MIXER_INFO:
184788447a05SGarrett D'Amore 		rv = sound_mixer_info(c, (mixer_info *)data);
184888447a05SGarrett D'Amore 		break;
184988447a05SGarrett D'Amore 
185088447a05SGarrett D'Amore 		/*
185188447a05SGarrett D'Amore 		 * These are mixer ioctls that are virtualized for the DSP
185288447a05SGarrett D'Amore 		 * device.  They are accessible via either /dev/mixer or
185388447a05SGarrett D'Amore 		 * /dev/dsp.
185488447a05SGarrett D'Amore 		 */
185588447a05SGarrett D'Amore 	case SOUND_MIXER_READ_RECSRC:
185688447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_RECSRC:
185788447a05SGarrett D'Amore 		rv = sound_mixer_read_recsrc(c, (int *)data);
185888447a05SGarrett D'Amore 		break;
185988447a05SGarrett D'Amore 
186088447a05SGarrett D'Amore 	case SOUND_MIXER_READ_DEVMASK:
186188447a05SGarrett D'Amore 	case SOUND_MIXER_READ_STEREODEVS:
186288447a05SGarrett D'Amore 		rv = sound_mixer_read_devmask(c, (int *)data);
186388447a05SGarrett D'Amore 		break;
186488447a05SGarrett D'Amore 
186588447a05SGarrett D'Amore 	case SOUND_MIXER_READ_RECMASK:
186688447a05SGarrett D'Amore 		rv = sound_mixer_read_recmask(c, (int *)data);
186788447a05SGarrett D'Amore 		break;
186888447a05SGarrett D'Amore 
186988447a05SGarrett D'Amore 	case SOUND_MIXER_READ_CAPS:
187088447a05SGarrett D'Amore 		rv = sound_mixer_read_caps(c, (int *)data);
187188447a05SGarrett D'Amore 		break;
187288447a05SGarrett D'Amore 
187388447a05SGarrett D'Amore 		/*
187488447a05SGarrett D'Amore 		 * Ioctls we have chosen not to support for now.  Some
187588447a05SGarrett D'Amore 		 * of these are of legacy interest only.
187688447a05SGarrett D'Amore 		 */
187788447a05SGarrett D'Amore 	case SNDCTL_SETSONG:
187888447a05SGarrett D'Amore 	case SNDCTL_GETSONG:
187988447a05SGarrett D'Amore 	case SNDCTL_DSP_SYNCGROUP:
188088447a05SGarrett D'Amore 	case SNDCTL_DSP_SYNCSTART:
188188447a05SGarrett D'Amore 	case SNDCTL_DSP_GET_CHNORDER:
188288447a05SGarrett D'Amore 	case SNDCTL_DSP_SET_CHNORDER:
188388447a05SGarrett D'Amore 	case SNDCTL_DSP_GETIPEAKS:
188488447a05SGarrett D'Amore 	case SNDCTL_DSP_GETOPEAKS:
188588447a05SGarrett D'Amore 	case SNDCTL_DSP_GETCHANNELMASK:
188688447a05SGarrett D'Amore 	case SNDCTL_DSP_BIND_CHANNEL:
188788447a05SGarrett D'Amore 	case SNDCTL_DSP_SETSYNCRO:
188826025630SGarrett D'Amore 	case SNDCTL_DSP_NONBLOCK:
188988447a05SGarrett D'Amore 	default:
189088447a05SGarrett D'Amore 		rv = EINVAL;
189188447a05SGarrett D'Amore 		break;
189288447a05SGarrett D'Amore 	}
189388447a05SGarrett D'Amore 
189488447a05SGarrett D'Amore 	if ((rv == 0) && (cmd & OSSIOC_OUT)) {
189588447a05SGarrett D'Amore 		rv = ddi_copyout(data, (void *)arg, sz, mode);
189688447a05SGarrett D'Amore 	}
189788447a05SGarrett D'Amore 	if (rv == 0) {
189888447a05SGarrett D'Amore 		*rvalp = 0;
189988447a05SGarrett D'Amore 	}
190088447a05SGarrett D'Amore 
190188447a05SGarrett D'Amore done:
190288447a05SGarrett D'Amore 	if (sz) {
190388447a05SGarrett D'Amore 		kmem_free(data, sz);
190488447a05SGarrett D'Amore 	}
190588447a05SGarrett D'Amore 	return (rv);
190688447a05SGarrett D'Amore }
190788447a05SGarrett D'Amore 
190888447a05SGarrett D'Amore static void
oss_output(audio_client_t * c)190988447a05SGarrett D'Amore oss_output(audio_client_t *c)
191088447a05SGarrett D'Amore {
191188447a05SGarrett D'Amore 	auclnt_pollwakeup(c, POLLOUT);
191288447a05SGarrett D'Amore }
191388447a05SGarrett D'Amore 
191488447a05SGarrett D'Amore static void
oss_input(audio_client_t * c)191588447a05SGarrett D'Amore oss_input(audio_client_t *c)
191688447a05SGarrett D'Amore {
191788447a05SGarrett D'Amore 	auclnt_pollwakeup(c, POLLIN | POLLRDNORM);
191888447a05SGarrett D'Amore }
191988447a05SGarrett D'Amore 
192088447a05SGarrett D'Amore static int
ossmix_open(audio_client_t * c,int oflag)192188447a05SGarrett D'Amore ossmix_open(audio_client_t *c, int oflag)
192288447a05SGarrett D'Amore {
192388447a05SGarrett D'Amore 	int		rv;
192488447a05SGarrett D'Amore 	ossclient_t	*sc;
192588447a05SGarrett D'Amore 	ossdev_t	*odev;
192688447a05SGarrett D'Amore 
192788447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(oflag));
192888447a05SGarrett D'Amore 
1929*2c30fa45SGarrett D'Amore 	if ((rv = auclnt_open(c, 0)) != 0) {
193088447a05SGarrett D'Amore 		return (rv);
193188447a05SGarrett D'Amore 	}
193288447a05SGarrett D'Amore 
193388447a05SGarrett D'Amore 	if ((sc = kmem_zalloc(sizeof (*sc), KM_NOSLEEP)) == NULL) {
193488447a05SGarrett D'Amore 		return (ENOMEM);
193588447a05SGarrett D'Amore 	}
193688447a05SGarrett D'Amore 	sc->o_ss_sz = 8192;
193788447a05SGarrett D'Amore 	if ((sc->o_ss_buf = kmem_zalloc(sc->o_ss_sz, KM_NOSLEEP)) == NULL) {
193888447a05SGarrett D'Amore 		kmem_free(sc, sizeof (*sc));
193988447a05SGarrett D'Amore 		return (ENOMEM);
194088447a05SGarrett D'Amore 	}
194188447a05SGarrett D'Amore 	auclnt_set_private(c, sc);
194288447a05SGarrett D'Amore 
194388447a05SGarrett D'Amore 	odev = auclnt_get_minor_data(c, AUDIO_MINOR_DSP);
194488447a05SGarrett D'Amore 
194588447a05SGarrett D'Amore 	/* set a couple of common fields */
194688447a05SGarrett D'Amore 	sc->o_client = c;
194788447a05SGarrett D'Amore 	sc->o_ossdev = odev;
194888447a05SGarrett D'Amore 
194988447a05SGarrett D'Amore 	return (rv);
195088447a05SGarrett D'Amore }
195188447a05SGarrett D'Amore 
195288447a05SGarrett D'Amore static void
ossmix_close(audio_client_t * c)195388447a05SGarrett D'Amore ossmix_close(audio_client_t *c)
195488447a05SGarrett D'Amore {
195588447a05SGarrett D'Amore 	ossclient_t	*sc;
195688447a05SGarrett D'Amore 
195788447a05SGarrett D'Amore 	sc = auclnt_get_private(c);
195888447a05SGarrett D'Amore 
195988447a05SGarrett D'Amore 	kmem_free(sc->o_ss_buf, sc->o_ss_sz);
196088447a05SGarrett D'Amore 	kmem_free(sc, sizeof (*sc));
196188447a05SGarrett D'Amore 
196288447a05SGarrett D'Amore 	auclnt_close(c);
196388447a05SGarrett D'Amore }
196488447a05SGarrett D'Amore 
196588447a05SGarrett D'Amore static int
sndctl_mix_nrext(audio_client_t * c,int * ncp)196688447a05SGarrett D'Amore sndctl_mix_nrext(audio_client_t *c, int *ncp)
196788447a05SGarrett D'Amore {
196888447a05SGarrett D'Amore 	audio_dev_t	*d;
196988447a05SGarrett D'Amore 	ossdev_t	*odev;
197088447a05SGarrett D'Amore 
197188447a05SGarrett D'Amore 	d = auclnt_get_dev(c);
197288447a05SGarrett D'Amore 
197388447a05SGarrett D'Amore 	if ((*ncp != -1) && (*ncp != (auclnt_get_dev_index(d) - 1))) {
197488447a05SGarrett D'Amore 		return (ENXIO);
197588447a05SGarrett D'Amore 	}
197688447a05SGarrett D'Amore 
197788447a05SGarrett D'Amore 	if ((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) {
197888447a05SGarrett D'Amore 		return (EINVAL);
197988447a05SGarrett D'Amore 	}
198088447a05SGarrett D'Amore 
198188447a05SGarrett D'Amore 	*ncp = odev->d_nctrl;
198288447a05SGarrett D'Amore 
198388447a05SGarrett D'Amore 	return (0);
198488447a05SGarrett D'Amore }
198588447a05SGarrett D'Amore 
198688447a05SGarrett D'Amore static int
sndctl_mix_extinfo(audio_client_t * c,oss_mixext * pext)198788447a05SGarrett D'Amore sndctl_mix_extinfo(audio_client_t *c, oss_mixext *pext)
198888447a05SGarrett D'Amore {
198988447a05SGarrett D'Amore 	audio_dev_t		*d;
199088447a05SGarrett D'Amore 	ossdev_t		*odev;
199188447a05SGarrett D'Amore 	int			rv = 0;
199288447a05SGarrett D'Amore 	int			dev;
199388447a05SGarrett D'Amore 
199488447a05SGarrett D'Amore 	d = auclnt_get_dev(c);
199588447a05SGarrett D'Amore 
199688447a05SGarrett D'Amore 	if (((dev = pext->dev) != -1) && (dev != (auclnt_get_dev_index(d) - 1)))
199788447a05SGarrett D'Amore 		return (ENXIO);
199888447a05SGarrett D'Amore 
199988447a05SGarrett D'Amore 	if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
200088447a05SGarrett D'Amore 	    (pext->ctrl >= odev->d_nctrl)) {
200188447a05SGarrett D'Amore 		return (EINVAL);
200288447a05SGarrett D'Amore 	}
200388447a05SGarrett D'Amore 
200488447a05SGarrett D'Amore 	bcopy(&odev->d_exts[pext->ctrl], pext, sizeof (*pext));
200588447a05SGarrett D'Amore 	pext->enumbit = 0;
200688447a05SGarrett D'Amore 	pext->dev = dev;
200788447a05SGarrett D'Amore 
200888447a05SGarrett D'Amore 	return (rv);
200988447a05SGarrett D'Amore }
201088447a05SGarrett D'Amore 
201188447a05SGarrett D'Amore static int
sndctl_mix_enuminfo(audio_client_t * c,oss_mixer_enuminfo * ei)201288447a05SGarrett D'Amore sndctl_mix_enuminfo(audio_client_t *c, oss_mixer_enuminfo *ei)
201388447a05SGarrett D'Amore {
201488447a05SGarrett D'Amore 	audio_dev_t		*d;
201588447a05SGarrett D'Amore 	audio_ctrl_desc_t	desc;
201688447a05SGarrett D'Amore 	audio_ctrl_t		*ctrl;
201788447a05SGarrett D'Amore 	ossdev_t		*odev;
201888447a05SGarrett D'Amore 	uint64_t		mask;
201988447a05SGarrett D'Amore 	int			bit;
202088447a05SGarrett D'Amore 	ushort_t		nxt;
202188447a05SGarrett D'Amore 
202288447a05SGarrett D'Amore 	d = auclnt_get_dev(c);
202388447a05SGarrett D'Amore 
202488447a05SGarrett D'Amore 	if ((ei->dev != -1) && (ei->dev != (auclnt_get_dev_index(d) - 1)))
202588447a05SGarrett D'Amore 		return (ENXIO);
202688447a05SGarrett D'Amore 
202788447a05SGarrett D'Amore 	if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
202888447a05SGarrett D'Amore 	    (ei->ctrl >= odev->d_nctrl) ||
202988447a05SGarrett D'Amore 	    (odev->d_exts[ei->ctrl].type != MIXT_ENUM) ||
203088447a05SGarrett D'Amore 	    ((ctrl = odev->d_ctrls[ei->ctrl]) == NULL) ||
203188447a05SGarrett D'Amore 	    (auclnt_control_describe(ctrl, &desc) != 0)) {
203288447a05SGarrett D'Amore 		return (EINVAL);
203388447a05SGarrett D'Amore 	}
203488447a05SGarrett D'Amore 
203588447a05SGarrett D'Amore 	mask = desc.acd_maxvalue;
203688447a05SGarrett D'Amore 	bit = 0;
203788447a05SGarrett D'Amore 	nxt = 0;
203888447a05SGarrett D'Amore 	ei->nvalues = 0;
203988447a05SGarrett D'Amore 	bzero(ei->strings, sizeof (ei->strings));
204088447a05SGarrett D'Amore 	bzero(ei->strindex, sizeof (ei->strindex));
204188447a05SGarrett D'Amore 
204288447a05SGarrett D'Amore 	while (mask) {
204388447a05SGarrett D'Amore 		const char *name = desc.acd_enum[bit];
204488447a05SGarrett D'Amore 		nxt = oss_set_enum(ei, nxt, name ? name : "");
204588447a05SGarrett D'Amore 		mask >>= 1;
204688447a05SGarrett D'Amore 		bit++;
204788447a05SGarrett D'Amore 	}
204888447a05SGarrett D'Amore 
204988447a05SGarrett D'Amore 	return (0);
205088447a05SGarrett D'Amore }
205188447a05SGarrett D'Amore 
205288447a05SGarrett D'Amore static int
sndctl_mix_read(audio_client_t * c,oss_mixer_value * vr)205388447a05SGarrett D'Amore sndctl_mix_read(audio_client_t *c, oss_mixer_value *vr)
205488447a05SGarrett D'Amore {
205588447a05SGarrett D'Amore 	int			rv;
205688447a05SGarrett D'Amore 	uint64_t		v;
205788447a05SGarrett D'Amore 	audio_dev_t		*d;
205888447a05SGarrett D'Amore 	audio_ctrl_t		*ctrl;
205988447a05SGarrett D'Amore 	ossdev_t		*odev;
206088447a05SGarrett D'Amore 
206188447a05SGarrett D'Amore 	d = auclnt_get_dev(c);
206288447a05SGarrett D'Amore 
206388447a05SGarrett D'Amore 	if ((vr->dev != -1) && (vr->dev != (auclnt_get_dev_index(d) - 1)))
206488447a05SGarrett D'Amore 		return (ENXIO);
206588447a05SGarrett D'Amore 
206688447a05SGarrett D'Amore 	if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
206788447a05SGarrett D'Amore 	    (vr->ctrl >= odev->d_nctrl) ||
206888447a05SGarrett D'Amore 	    ((ctrl = odev->d_ctrls[vr->ctrl]) == NULL)) {
206988447a05SGarrett D'Amore 		return (EINVAL);
207088447a05SGarrett D'Amore 	}
207188447a05SGarrett D'Amore 	if ((rv = auclnt_control_read(ctrl, &v)) == 0) {
207288447a05SGarrett D'Amore 		switch (odev->d_exts[vr->ctrl].type) {
207388447a05SGarrett D'Amore 		case MIXT_ENUM:
207488447a05SGarrett D'Amore 			/* translate this from an enum style bit mask */
207588447a05SGarrett D'Amore 			vr->value = ddi_ffs((unsigned long)v) - 1;
207688447a05SGarrett D'Amore 			break;
207788447a05SGarrett D'Amore 		case MIXT_STEREOSLIDER:
207888447a05SGarrett D'Amore 			vr->value = (int)ddi_swap16(v & 0xffff);
207988447a05SGarrett D'Amore 			break;
208088447a05SGarrett D'Amore 		case MIXT_MONOSLIDER:
208188447a05SGarrett D'Amore 			vr->value = (int)(v | (v << 8));
208288447a05SGarrett D'Amore 			break;
208388447a05SGarrett D'Amore 		case MIXT_ONOFF:
208488447a05SGarrett D'Amore 			/* this could be simple, or could be part of a multi */
208588447a05SGarrett D'Amore 			if (odev->d_exts[vr->ctrl].enumbit >= 0) {
208688447a05SGarrett D'Amore 				uint64_t mask;
208788447a05SGarrett D'Amore 				mask = 1;
208888447a05SGarrett D'Amore 				mask <<= (odev->d_exts[vr->ctrl].enumbit);
208988447a05SGarrett D'Amore 				vr->value = (v & mask) ? 1 : 0;
209088447a05SGarrett D'Amore 			} else {
209188447a05SGarrett D'Amore 				vr->value = v ? 1 : 0;
209288447a05SGarrett D'Amore 			}
209388447a05SGarrett D'Amore 			break;
209488447a05SGarrett D'Amore 
209588447a05SGarrett D'Amore 		default:
209688447a05SGarrett D'Amore 			vr->value = (int)v;
209788447a05SGarrett D'Amore 			break;
209888447a05SGarrett D'Amore 		}
209988447a05SGarrett D'Amore 	}
210088447a05SGarrett D'Amore 
210188447a05SGarrett D'Amore 	return (rv);
210288447a05SGarrett D'Amore }
210388447a05SGarrett D'Amore 
210488447a05SGarrett D'Amore static int
sndctl_mix_write(audio_client_t * c,oss_mixer_value * vr)210588447a05SGarrett D'Amore sndctl_mix_write(audio_client_t *c, oss_mixer_value *vr)
210688447a05SGarrett D'Amore {
210788447a05SGarrett D'Amore 	int			rv;
210888447a05SGarrett D'Amore 	uint64_t		v;
210988447a05SGarrett D'Amore 	audio_dev_t		*d;
211088447a05SGarrett D'Amore 	audio_ctrl_t		*ctrl;
211188447a05SGarrett D'Amore 	ossdev_t		*odev;
211288447a05SGarrett D'Amore 
211388447a05SGarrett D'Amore 	d = auclnt_get_dev(c);
211488447a05SGarrett D'Amore 
211588447a05SGarrett D'Amore 	if ((vr->dev != -1) && (vr->dev != (auclnt_get_dev_index(d) - 1)))
211688447a05SGarrett D'Amore 		return (ENXIO);
211788447a05SGarrett D'Amore 
211888447a05SGarrett D'Amore 	if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
211988447a05SGarrett D'Amore 	    (vr->ctrl >= odev->d_nctrl) ||
212088447a05SGarrett D'Amore 	    ((ctrl = odev->d_ctrls[vr->ctrl]) == NULL)) {
212188447a05SGarrett D'Amore 		return (EINVAL);
212288447a05SGarrett D'Amore 	}
212388447a05SGarrett D'Amore 
212488447a05SGarrett D'Amore 	switch (odev->d_exts[vr->ctrl].type) {
212588447a05SGarrett D'Amore 	case MIXT_ONOFF:
212688447a05SGarrett D'Amore 		/* this could be standalone, or it could be part of a multi */
212788447a05SGarrett D'Amore 		if (odev->d_exts[vr->ctrl].enumbit >= 0) {
212888447a05SGarrett D'Amore 			uint64_t mask;
212988447a05SGarrett D'Amore 			if ((rv = auclnt_control_read(ctrl, &v)) != 0) {
213088447a05SGarrett D'Amore 				return (EINVAL);
213188447a05SGarrett D'Amore 			}
213288447a05SGarrett D'Amore 			mask = 1;
213388447a05SGarrett D'Amore 			mask <<= (odev->d_exts[vr->ctrl].enumbit);
213488447a05SGarrett D'Amore 			if (vr->value) {
213588447a05SGarrett D'Amore 				v |= mask;
213688447a05SGarrett D'Amore 			} else {
213788447a05SGarrett D'Amore 				v &= ~mask;
213888447a05SGarrett D'Amore 			}
213988447a05SGarrett D'Amore 		} else {
214088447a05SGarrett D'Amore 			v = vr->value;
214188447a05SGarrett D'Amore 		}
214288447a05SGarrett D'Amore 		break;
214388447a05SGarrett D'Amore 	case MIXT_ENUM:
214488447a05SGarrett D'Amore 		/* translate this to an enum style bit mask */
214588447a05SGarrett D'Amore 		v = 1U << vr->value;
214688447a05SGarrett D'Amore 		break;
214788447a05SGarrett D'Amore 	case MIXT_MONOSLIDER:
214888447a05SGarrett D'Amore 		/* mask off high order bits */
214988447a05SGarrett D'Amore 		v = vr->value & 0xff;
215088447a05SGarrett D'Amore 		break;
215188447a05SGarrett D'Amore 	case MIXT_STEREOSLIDER:
215288447a05SGarrett D'Amore 		/* OSS uses reverse byte ordering */
215388447a05SGarrett D'Amore 		v = vr->value;
215488447a05SGarrett D'Amore 		v = ddi_swap16(vr->value & 0xffff);
215588447a05SGarrett D'Amore 		break;
215688447a05SGarrett D'Amore 	default:
215788447a05SGarrett D'Amore 		v = vr->value;
215888447a05SGarrett D'Amore 	}
215988447a05SGarrett D'Amore 	rv = auclnt_control_write(ctrl, v);
216088447a05SGarrett D'Amore 
216188447a05SGarrett D'Amore 	return (rv);
216288447a05SGarrett D'Amore }
216388447a05SGarrett D'Amore 
216488447a05SGarrett D'Amore static int
sndctl_mix_nrmix(audio_client_t * c,int * nmixp)216588447a05SGarrett D'Amore sndctl_mix_nrmix(audio_client_t *c, int *nmixp)
216688447a05SGarrett D'Amore {
216788447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
216888447a05SGarrett D'Amore 	*nmixp = oss_cnt_devs() - 1;
216988447a05SGarrett D'Amore 	return (0);
217088447a05SGarrett D'Amore }
217188447a05SGarrett D'Amore 
217288447a05SGarrett D'Amore static int
ossmix_ioctl(audio_client_t * c,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)217388447a05SGarrett D'Amore ossmix_ioctl(audio_client_t *c, int cmd, intptr_t arg, int mode, cred_t *credp,
217488447a05SGarrett D'Amore     int *rvalp)
217588447a05SGarrett D'Amore {
217688447a05SGarrett D'Amore 	int	sz;
217788447a05SGarrett D'Amore 	void	*data;
217888447a05SGarrett D'Amore 	int	rv = 0;
217988447a05SGarrett D'Amore 
218088447a05SGarrett D'Amore 	sz = OSSIOC_GETSZ(cmd);
218188447a05SGarrett D'Amore 
218288447a05SGarrett D'Amore 	if ((cmd & (OSSIOC_IN | OSSIOC_OUT)) && sz) {
218388447a05SGarrett D'Amore 		if ((data = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
218488447a05SGarrett D'Amore 			return (ENOMEM);
218588447a05SGarrett D'Amore 		}
218688447a05SGarrett D'Amore 	} else {
218788447a05SGarrett D'Amore 		sz = 0;
218888447a05SGarrett D'Amore 	}
218988447a05SGarrett D'Amore 
219088447a05SGarrett D'Amore 	if (cmd & OSSIOC_IN) {
219188447a05SGarrett D'Amore 		if ((rv = ddi_copyin((void *)arg, data, sz, mode)) != 0) {
219288447a05SGarrett D'Amore 			goto done;
219388447a05SGarrett D'Amore 		}
219488447a05SGarrett D'Amore 	}
219588447a05SGarrett D'Amore 
219688447a05SGarrett D'Amore 	switch (cmd) {
219788447a05SGarrett D'Amore 		/*
219888447a05SGarrett D'Amore 		 * Mixer specific ioctls
219988447a05SGarrett D'Amore 		 */
220088447a05SGarrett D'Amore 	case SNDCTL_MIX_NREXT:
220188447a05SGarrett D'Amore 		rv = sndctl_mix_nrext(c, (int *)data);
220288447a05SGarrett D'Amore 		break;
220388447a05SGarrett D'Amore 	case SNDCTL_MIX_EXTINFO:
220488447a05SGarrett D'Amore 		rv = sndctl_mix_extinfo(c, (oss_mixext *)data);
220588447a05SGarrett D'Amore 		break;
220688447a05SGarrett D'Amore 	case SNDCTL_MIX_ENUMINFO:
220788447a05SGarrett D'Amore 		rv = sndctl_mix_enuminfo(c, (oss_mixer_enuminfo *)data);
220888447a05SGarrett D'Amore 		break;
220988447a05SGarrett D'Amore 	case SNDCTL_MIX_READ:
221088447a05SGarrett D'Amore 		rv = sndctl_mix_read(c, (oss_mixer_value *)data);
221188447a05SGarrett D'Amore 		break;
221288447a05SGarrett D'Amore 	case SNDCTL_MIX_WRITE:
221388447a05SGarrett D'Amore 		rv = sndctl_mix_write(c, (oss_mixer_value *)data);
221488447a05SGarrett D'Amore 		break;
221588447a05SGarrett D'Amore 	case SNDCTL_MIX_NRMIX:
221688447a05SGarrett D'Amore 		rv = sndctl_mix_nrmix(c, (int *)data);
221788447a05SGarrett D'Amore 		break;
221888447a05SGarrett D'Amore 
221988447a05SGarrett D'Amore 		/*
222088447a05SGarrett D'Amore 		 * Legacy ioctls.  These are treated as soft values only,
222188447a05SGarrett D'Amore 		 * and do not affect global hardware state.  For use by
222288447a05SGarrett D'Amore 		 * legacy DSP applications.
222388447a05SGarrett D'Amore 		 */
222488447a05SGarrett D'Amore 	case SOUND_MIXER_READ_VOLUME:
222588447a05SGarrett D'Amore 	case SOUND_MIXER_READ_PCM:
222688447a05SGarrett D'Amore 	case SOUND_MIXER_READ_OGAIN:
222788447a05SGarrett D'Amore 		rv = sndctl_dsp_getplayvol(c, (int *)data);
222888447a05SGarrett D'Amore 		break;
222988447a05SGarrett D'Amore 
223088447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_VOLUME:
223188447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_PCM:
223288447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_OGAIN:
223388447a05SGarrett D'Amore 		rv = sound_mixer_write_ogain(c, (int *)data);
223488447a05SGarrett D'Amore 		break;
223588447a05SGarrett D'Amore 
223688447a05SGarrett D'Amore 	case SOUND_MIXER_READ_RECGAIN:
223788447a05SGarrett D'Amore 	case SOUND_MIXER_READ_RECLEV:
223888447a05SGarrett D'Amore 	case SOUND_MIXER_READ_IGAIN:
223988447a05SGarrett D'Amore 		rv = sndctl_dsp_getrecvol(c, (int *)data);
224088447a05SGarrett D'Amore 		break;
224188447a05SGarrett D'Amore 
224288447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_RECGAIN:
224388447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_RECLEV:
224488447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_IGAIN:
224588447a05SGarrett D'Amore 		rv = sound_mixer_write_igain(c, (int *)data);
224688447a05SGarrett D'Amore 		break;
224788447a05SGarrett D'Amore 
224888447a05SGarrett D'Amore 	case SOUND_MIXER_READ_RECSRC:
224988447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_RECSRC:
225088447a05SGarrett D'Amore 		rv = sound_mixer_read_recsrc(c, (int *)data);
225188447a05SGarrett D'Amore 		break;
225288447a05SGarrett D'Amore 
225388447a05SGarrett D'Amore 	case SOUND_MIXER_READ_DEVMASK:
225488447a05SGarrett D'Amore 	case SOUND_MIXER_READ_STEREODEVS:
225588447a05SGarrett D'Amore 		rv = sound_mixer_read_devmask(c, (int *)data);
225688447a05SGarrett D'Amore 		break;
225788447a05SGarrett D'Amore 
225888447a05SGarrett D'Amore 	case SOUND_MIXER_READ_RECMASK:
225988447a05SGarrett D'Amore 		rv = sound_mixer_read_recmask(c, (int *)data);
226088447a05SGarrett D'Amore 		break;
226188447a05SGarrett D'Amore 
22621069b515SGarrett D'Amore 	case SOUND_MIXER_READ_CAPS:
22631069b515SGarrett D'Amore 		rv = sound_mixer_read_caps(c, (int *)data);
22641069b515SGarrett D'Amore 		break;
22651069b515SGarrett D'Amore 
226688447a05SGarrett D'Amore 		/*
226788447a05SGarrett D'Amore 		 * Common ioctls shared with DSP
226888447a05SGarrett D'Amore 		 */
226988447a05SGarrett D'Amore 	case OSS_GETVERSION:
227088447a05SGarrett D'Amore 		rv = oss_getversion((int *)data);
227188447a05SGarrett D'Amore 		break;
227288447a05SGarrett D'Amore 
227388447a05SGarrett D'Amore 	case SNDCTL_CARDINFO:
227488447a05SGarrett D'Amore 		rv = sndctl_cardinfo(c, (oss_card_info *)data);
227588447a05SGarrett D'Amore 		break;
227688447a05SGarrett D'Amore 
227788447a05SGarrett D'Amore 	case SNDCTL_ENGINEINFO:
227888447a05SGarrett D'Amore 	case SNDCTL_AUDIOINFO:
227988447a05SGarrett D'Amore 	case SNDCTL_AUDIOINFO_EX:
228088447a05SGarrett D'Amore 		rv = sndctl_audioinfo(c, (oss_audioinfo *)data);
228188447a05SGarrett D'Amore 		break;
228288447a05SGarrett D'Amore 
228388447a05SGarrett D'Amore 	case SNDCTL_SYSINFO:
228488447a05SGarrett D'Amore 		rv = sndctl_sysinfo((oss_sysinfo *)data);
228588447a05SGarrett D'Amore 		break;
228688447a05SGarrett D'Amore 
228788447a05SGarrett D'Amore 	case SNDCTL_MIXERINFO:
228888447a05SGarrett D'Amore 		rv = sndctl_mixerinfo(c, (oss_mixerinfo *)data);
228988447a05SGarrett D'Amore 		break;
229088447a05SGarrett D'Amore 
229188447a05SGarrett D'Amore 	case SOUND_MIXER_INFO:
229288447a05SGarrett D'Amore 		rv = sound_mixer_info(c, (mixer_info *)data);
229388447a05SGarrett D'Amore 		break;
229488447a05SGarrett D'Amore 
229588447a05SGarrett D'Amore 	case SNDCTL_MIX_DESCRIPTION:	/* NOT SUPPORTED: tooltip */
229688447a05SGarrett D'Amore 		rv = EIO;	/* OSS returns EIO for this one */
229788447a05SGarrett D'Amore 		break;
229888447a05SGarrett D'Amore 
229988447a05SGarrett D'Amore 		/*
230088447a05SGarrett D'Amore 		 * Special implementation-private ioctls.
230188447a05SGarrett D'Amore 		 */
230288447a05SGarrett D'Amore 	case SNDCTL_SUN_SEND_NUMBER:
230388447a05SGarrett D'Amore 		rv = sndctl_sun_send_number(c, (int *)data, credp);
230488447a05SGarrett D'Amore 		break;
230588447a05SGarrett D'Amore 
230688447a05SGarrett D'Amore 		/*
230788447a05SGarrett D'Amore 		 * Legacy ioctls we don't support.
230888447a05SGarrett D'Amore 		 */
230988447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_MONGAIN:
231088447a05SGarrett D'Amore 	case SOUND_MIXER_READ_MONGAIN:
231188447a05SGarrett D'Amore 	case SOUND_MIXER_READ_BASS:
231288447a05SGarrett D'Amore 	case SOUND_MIXER_READ_TREBLE:
231388447a05SGarrett D'Amore 	case SOUND_MIXER_READ_SPEAKER:
231488447a05SGarrett D'Amore 	case SOUND_MIXER_READ_LINE:
231588447a05SGarrett D'Amore 	case SOUND_MIXER_READ_MIC:
231688447a05SGarrett D'Amore 	case SOUND_MIXER_READ_CD:
231788447a05SGarrett D'Amore 	case SOUND_MIXER_READ_IMIX:
231888447a05SGarrett D'Amore 	case SOUND_MIXER_READ_ALTPCM:
231988447a05SGarrett D'Amore 	case SOUND_MIXER_READ_SYNTH:
232088447a05SGarrett D'Amore 	case SOUND_MIXER_READ_LINE1:
232188447a05SGarrett D'Amore 	case SOUND_MIXER_READ_LINE2:
232288447a05SGarrett D'Amore 	case SOUND_MIXER_READ_LINE3:
232388447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_BASS:
232488447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_TREBLE:
232588447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_SPEAKER:
232688447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_LINE:
232788447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_MIC:
232888447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_CD:
232988447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_IMIX:
233088447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_ALTPCM:
233188447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_SYNTH:
233288447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_LINE1:
233388447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_LINE2:
233488447a05SGarrett D'Amore 	case SOUND_MIXER_WRITE_LINE3:
233588447a05SGarrett D'Amore 		/*
233688447a05SGarrett D'Amore 		 * Additional ioctls we *could* support, but don't.
233788447a05SGarrett D'Amore 		 */
233888447a05SGarrett D'Amore 	case SNDCTL_SETSONG:
233988447a05SGarrett D'Amore 	case SNDCTL_SETLABEL:
234088447a05SGarrett D'Amore 	case SNDCTL_GETSONG:
234188447a05SGarrett D'Amore 	case SNDCTL_GETLABEL:
234288447a05SGarrett D'Amore 	case SNDCTL_MIDIINFO:
234388447a05SGarrett D'Amore 	case SNDCTL_SETNAME:
234488447a05SGarrett D'Amore 	default:
234588447a05SGarrett D'Amore 		rv = EINVAL;
234688447a05SGarrett D'Amore 		break;
234788447a05SGarrett D'Amore 	}
234888447a05SGarrett D'Amore 
234988447a05SGarrett D'Amore 	if ((rv == 0) && (cmd & OSSIOC_OUT)) {
235088447a05SGarrett D'Amore 		rv = ddi_copyout(data, (void *)arg, sz, mode);
235188447a05SGarrett D'Amore 	}
235288447a05SGarrett D'Amore 	if (rv == 0) {
235388447a05SGarrett D'Amore 		*rvalp = 0;
235488447a05SGarrett D'Amore 	}
235588447a05SGarrett D'Amore 
235688447a05SGarrett D'Amore done:
235788447a05SGarrett D'Amore 	if (sz) {
235888447a05SGarrett D'Amore 		kmem_free(data, sz);
235988447a05SGarrett D'Amore 	}
236088447a05SGarrett D'Amore 	return (rv);
236188447a05SGarrett D'Amore }
236288447a05SGarrett D'Amore 
236388447a05SGarrett D'Amore static void *
oss_dev_init(audio_dev_t * dev)236488447a05SGarrett D'Amore oss_dev_init(audio_dev_t *dev)
236588447a05SGarrett D'Amore {
236688447a05SGarrett D'Amore 	ossdev_t	*odev;
236788447a05SGarrett D'Amore 
236888447a05SGarrett D'Amore 	odev = kmem_zalloc(sizeof (*odev), KM_SLEEP);
236988447a05SGarrett D'Amore 	odev->d_dev = dev;
237088447a05SGarrett D'Amore 
237188447a05SGarrett D'Amore 	mutex_init(&odev->d_mx, NULL, MUTEX_DRIVER, NULL);
237288447a05SGarrett D'Amore 	cv_init(&odev->d_cv, NULL, CV_DRIVER, NULL);
237388447a05SGarrett D'Amore 	oss_alloc_controls(odev);
237488447a05SGarrett D'Amore 
237588447a05SGarrett D'Amore 	return (odev);
237688447a05SGarrett D'Amore }
237788447a05SGarrett D'Amore 
237888447a05SGarrett D'Amore static void
oss_dev_fini(void * arg)237988447a05SGarrett D'Amore oss_dev_fini(void *arg)
238088447a05SGarrett D'Amore {
238188447a05SGarrett D'Amore 	ossdev_t	*odev = arg;
238288447a05SGarrett D'Amore 
238388447a05SGarrett D'Amore 	if (odev != NULL) {
238488447a05SGarrett D'Amore 		oss_free_controls(odev);
238588447a05SGarrett D'Amore 		mutex_destroy(&odev->d_mx);
238688447a05SGarrett D'Amore 		cv_destroy(&odev->d_cv);
238788447a05SGarrett D'Amore 		kmem_free(odev, sizeof (*odev));
238888447a05SGarrett D'Amore 	}
238988447a05SGarrett D'Amore }
239088447a05SGarrett D'Amore 
239188447a05SGarrett D'Amore static void
sndstat_printf(ossclient_t * oc,const char * fmt,...)239288447a05SGarrett D'Amore sndstat_printf(ossclient_t *oc, const char *fmt, ...)
239388447a05SGarrett D'Amore {
239488447a05SGarrett D'Amore 	va_list	va;
239588447a05SGarrett D'Amore 
239688447a05SGarrett D'Amore 	va_start(va, fmt);
239788447a05SGarrett D'Amore 	(void) vsnprintf(oc->o_ss_buf + oc->o_ss_len,
239888447a05SGarrett D'Amore 	    oc->o_ss_sz - oc->o_ss_len, fmt, va);
239988447a05SGarrett D'Amore 	va_end(va);
240088447a05SGarrett D'Amore 	oc->o_ss_len = strlen(oc->o_ss_buf);
240188447a05SGarrett D'Amore }
240288447a05SGarrett D'Amore 
240388447a05SGarrett D'Amore static int
sndstat_dev_walker(audio_dev_t * d,void * arg)240488447a05SGarrett D'Amore sndstat_dev_walker(audio_dev_t *d, void *arg)
240588447a05SGarrett D'Amore {
240688447a05SGarrett D'Amore 	ossclient_t	*oc = arg;
240788447a05SGarrett D'Amore 	const char	*capstr;
240888447a05SGarrett D'Amore 	unsigned	cap;
240988447a05SGarrett D'Amore 
241088447a05SGarrett D'Amore 	cap = auclnt_get_dev_capab(d);
241188447a05SGarrett D'Amore 
241288447a05SGarrett D'Amore 	if (cap	& AUDIO_CLIENT_CAP_DUPLEX) {
241388447a05SGarrett D'Amore 		capstr = "DUPLEX";
241488447a05SGarrett D'Amore 	} else if ((cap & AUDIO_CLIENT_CAP_PLAY) &&
241588447a05SGarrett D'Amore 	    (cap & AUDIO_CLIENT_CAP_RECORD)) {
241688447a05SGarrett D'Amore 		capstr = "INPUT,OUTPUT";
241788447a05SGarrett D'Amore 	} else if (cap & AUDIO_CLIENT_CAP_PLAY) {
241888447a05SGarrett D'Amore 		capstr = "OUTPUT";
241988447a05SGarrett D'Amore 	} else if (cap & AUDIO_CLIENT_CAP_RECORD) {
242088447a05SGarrett D'Amore 		capstr = "INPUT";
242188447a05SGarrett D'Amore 	} else {
242288447a05SGarrett D'Amore 		capstr = NULL;
242388447a05SGarrett D'Amore 	}
242488447a05SGarrett D'Amore 
242588447a05SGarrett D'Amore 	if (capstr == NULL)
242688447a05SGarrett D'Amore 		return (AUDIO_WALK_CONTINUE);
242788447a05SGarrett D'Amore 
242888447a05SGarrett D'Amore 	sndstat_printf(oc, "%d: %s %s, %s (%s)\n",
242988447a05SGarrett D'Amore 	    auclnt_get_dev_number(d), auclnt_get_dev_name(d),
243088447a05SGarrett D'Amore 	    auclnt_get_dev_description(d), auclnt_get_dev_version(d), capstr);
243188447a05SGarrett D'Amore 
243288447a05SGarrett D'Amore 	return (AUDIO_WALK_CONTINUE);
243388447a05SGarrett D'Amore }
243488447a05SGarrett D'Amore 
243588447a05SGarrett D'Amore static int
sndstat_mixer_walker(audio_dev_t * d,void * arg)243688447a05SGarrett D'Amore sndstat_mixer_walker(audio_dev_t *d, void *arg)
243788447a05SGarrett D'Amore {
243888447a05SGarrett D'Amore 	ossclient_t	*oc = arg;
243988447a05SGarrett D'Amore 	unsigned	cap;
244088447a05SGarrett D'Amore 	void		*iter;
244188447a05SGarrett D'Amore 	const char	*info;
244288447a05SGarrett D'Amore 
244388447a05SGarrett D'Amore 	cap = auclnt_get_dev_capab(d);
244488447a05SGarrett D'Amore 
244588447a05SGarrett D'Amore 	if ((cap & (AUDIO_CLIENT_CAP_PLAY|AUDIO_CLIENT_CAP_RECORD)) == 0)
244688447a05SGarrett D'Amore 		return (AUDIO_WALK_CONTINUE);
244788447a05SGarrett D'Amore 
244888447a05SGarrett D'Amore 	sndstat_printf(oc, "%d: %s %s, %s\n",
244988447a05SGarrett D'Amore 	    auclnt_get_dev_number(d), auclnt_get_dev_name(d),
245088447a05SGarrett D'Amore 	    auclnt_get_dev_description(d), auclnt_get_dev_version(d));
245188447a05SGarrett D'Amore 	iter = NULL;
245288447a05SGarrett D'Amore 	while ((info = auclnt_get_dev_hw_info(d, &iter)) != NULL) {
245388447a05SGarrett D'Amore 		sndstat_printf(oc, "\t%s\n", info);
245488447a05SGarrett D'Amore 	}
245588447a05SGarrett D'Amore 	return (AUDIO_WALK_CONTINUE);
245688447a05SGarrett D'Amore }
245788447a05SGarrett D'Amore 
245888447a05SGarrett D'Amore static int
ossmix_write(audio_client_t * c,struct uio * uio,cred_t * cr)245988447a05SGarrett D'Amore ossmix_write(audio_client_t *c, struct uio *uio, cred_t *cr)
246088447a05SGarrett D'Amore {
246188447a05SGarrett D'Amore 	/* write on sndstat is a no-op */
246288447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(c));
246388447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(uio));
246488447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(cr));
246588447a05SGarrett D'Amore 
246688447a05SGarrett D'Amore 	return (0);
246788447a05SGarrett D'Amore }
246888447a05SGarrett D'Amore 
246988447a05SGarrett D'Amore static int
ossmix_read(audio_client_t * c,struct uio * uio,cred_t * cr)247088447a05SGarrett D'Amore ossmix_read(audio_client_t *c, struct uio *uio, cred_t *cr)
247188447a05SGarrett D'Amore {
247288447a05SGarrett D'Amore 	ossclient_t	*oc;
247388447a05SGarrett D'Amore 	unsigned	n;
247488447a05SGarrett D'Amore 	int		rv;
247588447a05SGarrett D'Amore 
247688447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(cr));
247788447a05SGarrett D'Amore 
247888447a05SGarrett D'Amore 	if (uio->uio_resid == 0) {
247988447a05SGarrett D'Amore 		return (0);
248088447a05SGarrett D'Amore 	}
248188447a05SGarrett D'Amore 
248288447a05SGarrett D'Amore 	oc = auclnt_get_private(c);
248388447a05SGarrett D'Amore 
248488447a05SGarrett D'Amore 	mutex_enter(&oc->o_ss_lock);
248588447a05SGarrett D'Amore 
248688447a05SGarrett D'Amore 	if (oc->o_ss_off == 0) {
248788447a05SGarrett D'Amore 
248888447a05SGarrett D'Amore 		sndstat_printf(oc, "SunOS Audio Framework\n");
248988447a05SGarrett D'Amore 
249088447a05SGarrett D'Amore 		sndstat_printf(oc, "\nAudio Devices:\n");
249188447a05SGarrett D'Amore 		auclnt_walk_devs_by_number(sndstat_dev_walker, oc);
249288447a05SGarrett D'Amore 
249388447a05SGarrett D'Amore 		sndstat_printf(oc, "\nMixers:\n");
249488447a05SGarrett D'Amore 		auclnt_walk_devs_by_number(sndstat_mixer_walker, oc);
249588447a05SGarrett D'Amore 	}
249688447a05SGarrett D'Amore 
249788447a05SGarrett D'Amore 	/*
249888447a05SGarrett D'Amore 	 * For simplicity's sake, we implement a non-seekable device.  We could
249988447a05SGarrett D'Amore 	 * support seekability, but offsets would be rather meaningless between
250088447a05SGarrett D'Amore 	 * changes.
250188447a05SGarrett D'Amore 	 */
250288447a05SGarrett D'Amore 	n = min(uio->uio_resid, (oc->o_ss_len - oc->o_ss_off));
250388447a05SGarrett D'Amore 
250488447a05SGarrett D'Amore 	rv = uiomove(oc->o_ss_buf + oc->o_ss_off, n, UIO_READ, uio);
250588447a05SGarrett D'Amore 	if (rv != 0) {
250688447a05SGarrett D'Amore 		n = 0;
250788447a05SGarrett D'Amore 	}
250888447a05SGarrett D'Amore 	oc->o_ss_off += n;
250988447a05SGarrett D'Amore 
251088447a05SGarrett D'Amore 	if (n == 0) {
251188447a05SGarrett D'Amore 		/*
251288447a05SGarrett D'Amore 		 * end-of-file reached... clear the sndstat buffer so that
251388447a05SGarrett D'Amore 		 * subsequent reads will get the latest data.
251488447a05SGarrett D'Amore 		 */
251588447a05SGarrett D'Amore 		oc->o_ss_off = oc->o_ss_len = 0;
251688447a05SGarrett D'Amore 	}
251788447a05SGarrett D'Amore 	mutex_exit(&oc->o_ss_lock);
251888447a05SGarrett D'Amore 	return (rv);
251988447a05SGarrett D'Amore }
252088447a05SGarrett D'Amore 
252188447a05SGarrett D'Amore int
oss_read(audio_client_t * c,struct uio * uio,cred_t * cr)252288447a05SGarrett D'Amore oss_read(audio_client_t *c, struct uio *uio, cred_t *cr)
252388447a05SGarrett D'Amore {
252488447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(cr));
252588447a05SGarrett D'Amore 
252688447a05SGarrett D'Amore 	auclnt_clear_paused(auclnt_input_stream(c));
252788447a05SGarrett D'Amore 
252888447a05SGarrett D'Amore 	return (auclnt_read(c, uio));
252988447a05SGarrett D'Amore }
253088447a05SGarrett D'Amore 
253188447a05SGarrett D'Amore int
oss_write(audio_client_t * c,struct uio * uio,cred_t * cr)253288447a05SGarrett D'Amore oss_write(audio_client_t *c, struct uio *uio, cred_t *cr)
253388447a05SGarrett D'Amore {
253488447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(cr));
253588447a05SGarrett D'Amore 
253688447a05SGarrett D'Amore 	auclnt_clear_paused(auclnt_output_stream(c));
253788447a05SGarrett D'Amore 
253888447a05SGarrett D'Amore 	return (auclnt_write(c, uio));
253988447a05SGarrett D'Amore }
254088447a05SGarrett D'Amore 
254188447a05SGarrett D'Amore int
oss_chpoll(audio_client_t * c,short events,int anyyet,short * reventsp,struct pollhead ** phpp)254288447a05SGarrett D'Amore oss_chpoll(audio_client_t *c, short events, int anyyet, short *reventsp,
254388447a05SGarrett D'Amore     struct pollhead **phpp)
254488447a05SGarrett D'Amore {
254588447a05SGarrett D'Amore 	return (auclnt_chpoll(c, events, anyyet, reventsp, phpp));
254688447a05SGarrett D'Amore }
254788447a05SGarrett D'Amore 
254888447a05SGarrett D'Amore static struct audio_client_ops oss_ops = {
254988447a05SGarrett D'Amore 	"sound,dsp",
255088447a05SGarrett D'Amore 	oss_dev_init,
255188447a05SGarrett D'Amore 	oss_dev_fini,
255288447a05SGarrett D'Amore 	oss_open,
255388447a05SGarrett D'Amore 	oss_close,
255488447a05SGarrett D'Amore 	oss_read,
255588447a05SGarrett D'Amore 	oss_write,
255688447a05SGarrett D'Amore 	oss_ioctl,
255788447a05SGarrett D'Amore 	oss_chpoll,
255888447a05SGarrett D'Amore 	NULL,		/* mmap */
255988447a05SGarrett D'Amore 	oss_input,
256088447a05SGarrett D'Amore 	oss_output,
256188447a05SGarrett D'Amore 	NULL,		/* drain */
256288447a05SGarrett D'Amore };
256388447a05SGarrett D'Amore 
256488447a05SGarrett D'Amore static struct audio_client_ops ossmix_ops = {
256588447a05SGarrett D'Amore 	"sound,mixer",
256688447a05SGarrett D'Amore 	NULL,
256788447a05SGarrett D'Amore 	NULL,
256888447a05SGarrett D'Amore 	ossmix_open,
256988447a05SGarrett D'Amore 	ossmix_close,
257088447a05SGarrett D'Amore 	ossmix_read,
257188447a05SGarrett D'Amore 	ossmix_write,
257288447a05SGarrett D'Amore 	ossmix_ioctl,
257388447a05SGarrett D'Amore 	NULL,	/* chpoll */
257488447a05SGarrett D'Amore 	NULL,   /* mmap */
257588447a05SGarrett D'Amore 	NULL,	/* input */
257688447a05SGarrett D'Amore 	NULL,   /* output */
257788447a05SGarrett D'Amore 	NULL,	/* drain */
2578a19bb1faSGarrett D'Amore 	NULL,	/* wput */
2579a19bb1faSGarrett D'Amore 	NULL,	/* wsrv */
258088447a05SGarrett D'Amore };
258188447a05SGarrett D'Amore 
258288447a05SGarrett D'Amore /* nearly the same as ossxmix; different minor name helps devfsadm */
258388447a05SGarrett D'Amore static struct audio_client_ops sndstat_ops = {
258488447a05SGarrett D'Amore 	"sound,sndstat",
258588447a05SGarrett D'Amore 	NULL,	/* dev_init */
258688447a05SGarrett D'Amore 	NULL,	/* dev_fini */
258788447a05SGarrett D'Amore 	ossmix_open,
258888447a05SGarrett D'Amore 	ossmix_close,
258988447a05SGarrett D'Amore 	ossmix_read,
259088447a05SGarrett D'Amore 	ossmix_write,
259188447a05SGarrett D'Amore 	ossmix_ioctl,
259288447a05SGarrett D'Amore 	NULL,	/* chpoll */
259388447a05SGarrett D'Amore 	NULL,	/* mmap */
259488447a05SGarrett D'Amore 	NULL,	/* input */
259588447a05SGarrett D'Amore 	NULL,	/* output */
259688447a05SGarrett D'Amore 	NULL,	/* drain */
2597a19bb1faSGarrett D'Amore 	NULL,	/* wput */
2598a19bb1faSGarrett D'Amore 	NULL,	/* wsrv */
259988447a05SGarrett D'Amore };
260088447a05SGarrett D'Amore 
260188447a05SGarrett D'Amore void
auimpl_oss_init(void)260288447a05SGarrett D'Amore auimpl_oss_init(void)
260388447a05SGarrett D'Amore {
260488447a05SGarrett D'Amore 	auclnt_register_ops(AUDIO_MINOR_DSP, &oss_ops);
260588447a05SGarrett D'Amore 	auclnt_register_ops(AUDIO_MINOR_MIXER, &ossmix_ops);
260688447a05SGarrett D'Amore 	auclnt_register_ops(AUDIO_MINOR_SNDSTAT, &sndstat_ops);
260788447a05SGarrett D'Amore }
2608