xref: /illumos-gate/usr/src/uts/common/io/audio/impl/audio_oss.c (revision 635216b673cf196ac523ff2a7ab715717e553292)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/open.h>
28 #include <sys/errno.h>
29 #include <sys/ddi.h>
30 #include <sys/sunddi.h>
31 #include <sys/audio/audio_oss.h>
32 #include <sys/file.h>
33 #include <sys/note.h>
34 #include <sys/sysmacros.h>
35 #include <sys/list.h>
36 #include "audio_client.h"
37 
38 #define	OSS_FMT		AFMT_S16_LE
39 #define	OSS_RATE	48000
40 #define	OSS_CHANNELS	2
41 
42 typedef struct ossclient ossclient_t;
43 typedef struct ossdev ossdev_t;
44 
45 static const struct {
46 	int	oss;
47 	int	fmt;
48 } oss_formats[] = {
49 	{ AFMT_MU_LAW,		AUDIO_FORMAT_ULAW },
50 	{ AFMT_A_LAW,		AUDIO_FORMAT_ALAW },
51 	{ AFMT_U8,		AUDIO_FORMAT_U8 },
52 	{ AFMT_S8,		AUDIO_FORMAT_S8 },
53 	{ AFMT_S16_BE,		AUDIO_FORMAT_S16_BE },
54 	{ AFMT_S16_LE,		AUDIO_FORMAT_S16_LE },
55 	{ AFMT_U16_BE,		AUDIO_FORMAT_U16_BE },
56 	{ AFMT_U16_LE,		AUDIO_FORMAT_U16_LE },
57 	{ AFMT_S24_BE,		AUDIO_FORMAT_S24_BE },
58 	{ AFMT_S24_LE,		AUDIO_FORMAT_S24_LE },
59 	{ AFMT_S32_BE,		AUDIO_FORMAT_S32_BE },
60 	{ AFMT_S32_LE,		AUDIO_FORMAT_S32_LE },
61 	{ AFMT_S24_PACKED,	AUDIO_FORMAT_S24_PACKED },
62 	{ AFMT_AC3,		AUDIO_FORMAT_AC3 },
63 	{ AFMT_QUERY,		AUDIO_FORMAT_NONE }
64 };
65 
66 /* common structure shared between both mixer and dsp nodes */
67 struct ossclient {
68 	ossdev_t		*o_ossdev;
69 	audio_client_t		*o_client;
70 	/* sndstat */
71 	kmutex_t		o_ss_lock;
72 	char			*o_ss_buf;
73 	size_t			o_ss_len;
74 	size_t			o_ss_sz;
75 	size_t			o_ss_off;
76 };
77 
78 struct ossdev {
79 	audio_dev_t		*d_dev;
80 
81 	uint_t			d_nctrl;	/* num actual controls */
82 	uint_t			d_nalloc;	/* num allocated controls */
83 	audio_ctrl_t		**d_ctrls;	/* array of control handles */
84 	oss_mixext		*d_exts;	/* array of mixer descs */
85 
86 	int			d_play_grp;
87 	int			d_rec_grp;
88 	int			d_mon_grp;
89 	int			d_misc_grp;
90 
91 	kmutex_t		d_mx;
92 	kcondvar_t		d_cv;
93 };
94 
95 static int
96 oss_cnt_controls(audio_ctrl_t *ctrl, void *arg)
97 {
98 	int			*pint = (int *)arg;
99 	int			cnt;
100 	audio_ctrl_desc_t	desc;
101 
102 	cnt = *pint;
103 	cnt++;
104 	*pint = cnt;
105 
106 	if (auclnt_control_describe(ctrl, &desc) != 0)
107 		return (AUDIO_WALK_CONTINUE);
108 
109 	if (desc.acd_flags & AUDIO_CTRL_FLAG_MULTI) {
110 		for (uint64_t mask = desc.acd_maxvalue; mask; mask >>= 1) {
111 			if (mask & 1) {
112 				cnt++;
113 			}
114 		}
115 		*pint = cnt;
116 	}
117 
118 	return (AUDIO_WALK_CONTINUE);
119 }
120 
121 /*
122  * Add one entry to the OSS user control table to internal control
123  * helper table.
124  *
125  * This is used with auimpl_walk_controls. The table must be pre-
126  * allocated before it is walk'd. This includes the root and
127  * extended control markers!
128  */
129 static int
130 oss_add_control(audio_ctrl_t *ctrl, void *arg)
131 {
132 	ossdev_t		*odev = arg;
133 	audio_ctrl_desc_t	desc;
134 	oss_mixext		*ext;
135 	int			bit;
136 	uint64_t		mask;
137 	const char		*name;
138 	int			parent;
139 	int			flags;
140 	unsigned		scope;
141 
142 	if (auclnt_control_describe(ctrl, &desc))
143 		return (AUDIO_WALK_CONTINUE);
144 
145 	parent = 0;
146 
147 	/*
148 	 * Add appropriate group if not already done so.
149 	 */
150 	if (desc.acd_flags & AUDIO_CTRL_FLAG_PLAY) {
151 		if (!odev->d_play_grp) {
152 			ext = &odev->d_exts[odev->d_nctrl];
153 			ext->ctrl = odev->d_nctrl;
154 			ext->control_no = -1;
155 			ext->type = MIXT_GROUP;
156 			ext->desc = MIXEXT_SCOPE_OUTPUT;
157 			ext->timestamp = gethrtime();
158 			(void) snprintf(ext->id, sizeof (ext->id), "PLAYBACK");
159 			odev->d_play_grp = odev->d_nctrl;
160 			odev->d_nctrl++;
161 		}
162 		scope = MIXEXT_SCOPE_OUTPUT;
163 		parent = odev->d_play_grp;
164 	} else if (desc.acd_flags & AUDIO_CTRL_FLAG_REC) {
165 		if (!odev->d_rec_grp) {
166 			ext = &odev->d_exts[odev->d_nctrl];
167 			ext->ctrl = odev->d_nctrl;
168 			ext->control_no = -1;
169 			ext->type = MIXT_GROUP;
170 			ext->desc = MIXEXT_SCOPE_INPUT;
171 			ext->timestamp = gethrtime();
172 			(void) snprintf(ext->id, sizeof (ext->id), "RECORD");
173 			odev->d_rec_grp = odev->d_nctrl;
174 			odev->d_nctrl++;
175 		}
176 		scope = MIXEXT_SCOPE_INPUT;
177 		parent = odev->d_rec_grp;
178 	} else if (desc.acd_flags & AUDIO_CTRL_FLAG_MONITOR) {
179 		if (!odev->d_mon_grp) {
180 			ext = &odev->d_exts[odev->d_nctrl];
181 			ext->ctrl = odev->d_nctrl;
182 			ext->control_no = -1;
183 			ext->type = MIXT_GROUP;
184 			ext->desc = MIXEXT_SCOPE_MONITOR;
185 			ext->timestamp = gethrtime();
186 			(void) snprintf(ext->id, sizeof (ext->id), "MONITOR");
187 			odev->d_mon_grp = odev->d_nctrl;
188 			odev->d_nctrl++;
189 		}
190 		scope = MIXEXT_SCOPE_MONITOR;
191 		parent = odev->d_mon_grp;
192 	} else {
193 		if (!odev->d_misc_grp) {
194 			ext = &odev->d_exts[odev->d_nctrl];
195 			ext->ctrl = odev->d_nctrl;
196 			ext->control_no = -1;
197 			ext->type = MIXT_GROUP;
198 			ext->desc = MIXEXT_SCOPE_OTHER;
199 			ext->timestamp = gethrtime();
200 			(void) snprintf(ext->id, sizeof (ext->id), "MISC");
201 			odev->d_misc_grp = odev->d_nctrl;
202 			odev->d_nctrl++;
203 		}
204 		scope = MIXEXT_SCOPE_OTHER;
205 		parent = odev->d_misc_grp;
206 	}
207 
208 	name = desc.acd_name ? desc.acd_name : "";
209 
210 	if (desc.acd_flags & AUDIO_CTRL_FLAG_MULTI) {
211 		ext = &odev->d_exts[odev->d_nctrl];
212 		ext->ctrl = odev->d_nctrl;
213 		ext->control_no = -1;
214 		ext->type = MIXT_GROUP;
215 		ext->timestamp = gethrtime();
216 		ext->parent = parent;
217 		ext->desc = scope;
218 		(void) snprintf(ext->id, sizeof (ext->id), "%s", name);
219 		(void) snprintf(ext->extname, sizeof (ext->extname),
220 		    "%s", name);
221 		parent = odev->d_nctrl++;
222 	}
223 
224 	/* Next available open entry */
225 	ext = &odev->d_exts[odev->d_nctrl];
226 
227 	/* Record the underlying control handle */
228 	odev->d_ctrls[odev->d_nctrl] = ctrl;
229 
230 	/*
231 	 * Now setup the oss entry
232 	 */
233 
234 	ext->ctrl = odev->d_nctrl;
235 	ext->control_no = -1;
236 	ext->maxvalue = (int)desc.acd_maxvalue;
237 	ext->minvalue = (int)desc.acd_minvalue;
238 	ext->timestamp = gethrtime();
239 	ext->parent = parent;
240 	ext->desc = scope;
241 	/* all controls should be pollable for now */
242 	flags = MIXF_POLL;
243 
244 	/*
245 	 * The following flags are intended to help out applications
246 	 * which need to figure out where to place certain controls.
247 	 * A few further words of guidance:
248 	 *
249 	 * Apps that just want a single master volume control should
250 	 * adjust the control(s) that are labelled with MIXF_PCMVOL if
251 	 * present.  They can fall back to adjusting all MAINVOL
252 	 * levels instead, if no PCMVOL is present.
253 	 *
254 	 * Controls that are one type on a certain device might be a
255 	 * different type on another device.  For example,
256 	 * audiopci/ak4531 can adjust input gains for individual
257 	 * levels, but lacks a master record gain.  AC'97, on the
258 	 * other hand, has individual monitor gains for inputs, but
259 	 * only a single master recording gain.
260 	 */
261 	if (desc.acd_flags & AUDIO_CTRL_FLAG_READABLE)
262 		flags |= MIXF_READABLE;
263 	if (desc.acd_flags & AUDIO_CTRL_FLAG_WRITEABLE)
264 		flags |= MIXF_WRITEABLE;
265 	if (desc.acd_flags & AUDIO_CTRL_FLAG_CENTIBEL)
266 		flags |= MIXF_CENTIBEL;
267 	if (desc.acd_flags & AUDIO_CTRL_FLAG_DECIBEL)
268 		flags |= MIXF_DECIBEL;
269 	if (desc.acd_flags & AUDIO_CTRL_FLAG_MAINVOL)
270 		flags |= MIXF_MAINVOL;
271 	if (desc.acd_flags & AUDIO_CTRL_FLAG_PCMVOL)
272 		flags |= MIXF_PCMVOL;
273 	if (desc.acd_flags & AUDIO_CTRL_FLAG_RECVOL)
274 		flags |= MIXF_RECVOL;
275 	if (desc.acd_flags & AUDIO_CTRL_FLAG_MONVOL)
276 		flags |= MIXF_MONVOL;
277 	ext->flags = flags;
278 
279 	(void) snprintf(ext->id, sizeof (ext->id), "%s", name);
280 
281 	/*
282 	 * For now just use the same extname as the real name.
283 	 */
284 	(void) snprintf(ext->extname, sizeof (ext->extname), name);
285 
286 	/*
287 	 * Now we deal with various control types.
288 	 */
289 	switch (desc.acd_type) {
290 	case AUDIO_CTRL_TYPE_BOOLEAN:
291 		ext->type = MIXT_ONOFF;
292 		ext->enumbit = -1;
293 		break;
294 	case AUDIO_CTRL_TYPE_STEREO:
295 		ext->type = MIXT_STEREOSLIDER;
296 		break;
297 	case AUDIO_CTRL_TYPE_MONO:
298 		ext->type = MIXT_MONOSLIDER;
299 		break;
300 	case AUDIO_CTRL_TYPE_ENUM:
301 
302 		if (desc.acd_flags & AUDIO_CTRL_FLAG_MULTI) {
303 			/*
304 			 * We turn AUDIO_CTRL_FLAG_MULTI into a group
305 			 * of checkboxes, since OSS can't represent it
306 			 * natively.
307 			 */
308 			mask = desc.acd_maxvalue;
309 			bit = 0;
310 			while (mask) {
311 				if (mask & 1) {
312 					ext = &odev->d_exts[odev->d_nctrl];
313 					(void) snprintf(ext->extname,
314 					    sizeof (ext->extname), "%s.%s",
315 					    name, desc.acd_enum[bit]);
316 					(void) snprintf(ext->id,
317 					    sizeof (ext->id), "%s",
318 					    desc.acd_enum[bit]);
319 					ext->ctrl = odev->d_nctrl;
320 					ext->control_no = -1;
321 					ext->parent = parent;
322 					ext->timestamp = gethrtime();
323 					ext->type = MIXT_ONOFF;
324 					ext->minvalue = 0;
325 					ext->maxvalue = 1;
326 					ext->enumbit = bit;
327 					ext->flags = flags;
328 					odev->d_ctrls[odev->d_nctrl] = ctrl;
329 					odev->d_nctrl++;
330 				}
331 				bit++;
332 				mask >>= 1;
333 			}
334 			return (AUDIO_WALK_CONTINUE);
335 		} else {
336 			/*
337 			 * NB: This is sufficient only for controls
338 			 * with a single value.  It cannot express the
339 			 * richer bitmask capabilities.
340 			 */
341 			ext->type = MIXT_ENUM;
342 			ext->minvalue = 0;
343 
344 			/*
345 			 * For an enumaration, we need to figure out
346 			 * which values are present, and set the
347 			 * appropriate mask and max value.
348 			 */
349 			bzero(ext->enum_present, sizeof (ext->enum_present));
350 			mask = desc.acd_maxvalue;
351 			bit = 0;
352 			while (mask) {
353 				if (mask & 1) {
354 					ext->enum_present[bit / 8] |=
355 					    (1 << (bit % 8));
356 				}
357 				mask >>= 1;
358 				bit++;
359 			}
360 			ext->maxvalue = bit;
361 		}
362 		break;
363 
364 	case AUDIO_CTRL_TYPE_METER:
365 	default:
366 		/* Its an unknown or unsupported (for now) control, skip */
367 		return (AUDIO_WALK_CONTINUE);
368 	}
369 
370 	odev->d_nctrl++;
371 
372 	return (AUDIO_WALK_CONTINUE);
373 }
374 
375 /*
376  * Free up an OSS user land control to internal control,
377  * helper table.
378  */
379 static void
380 oss_free_controls(ossdev_t *odev)
381 {
382 	kmem_free(odev->d_ctrls, sizeof (audio_ctrl_t *) * odev->d_nalloc);
383 	kmem_free(odev->d_exts, sizeof (oss_mixext) * odev->d_nalloc);
384 	odev->d_nctrl = 0;
385 	odev->d_nalloc = 0;
386 }
387 
388 /*
389  * Allocate and fill in an OSS user land controls to internal controls
390  * helper table. This is done on one audio_dev device.
391  */
392 static void
393 oss_alloc_controls(ossdev_t *odev)
394 {
395 	audio_dev_t		*d = odev->d_dev;
396 	int			nctrl = 0;
397 	oss_mixext		*ext;
398 	oss_mixext_root		*root_data;
399 
400 	/* Find out who many entries we need */
401 	auclnt_walk_controls(d, oss_cnt_controls, &nctrl);
402 	nctrl++;		/* Needs space for the device root node */
403 	nctrl++;		/* Needs space for the device ext marker */
404 	nctrl++;		/* Needs space for the play group */
405 	nctrl++;		/* Needs space for the record group */
406 	nctrl++;		/* Needs space for the monitor group */
407 	nctrl++;		/* Needs space for the tone group */
408 	nctrl++;		/* Needs space for the 3D group */
409 	nctrl++;		/* Needs space for the misc group */
410 
411 	/* Allocate the OSS to boomer helper table */
412 	odev->d_nalloc = nctrl;
413 	odev->d_ctrls = kmem_zalloc(sizeof (audio_ctrl_t *) * nctrl, KM_SLEEP);
414 	odev->d_exts = kmem_zalloc(sizeof (oss_mixext) * nctrl, KM_SLEEP);
415 
416 	/*
417 	 * Setup special case outputs to output OSS routes helper tables
418 	 */
419 
420 	/*
421 	 * Root node is first, that way all others parent is this one
422 	 */
423 	ext = &odev->d_exts[odev->d_nctrl];
424 	ext->ctrl = 0;
425 	ext->parent = -1;
426 	ext->type = MIXT_DEVROOT;
427 	ext->timestamp = gethrtime();
428 	(void) snprintf(ext->id, sizeof (ext->id), "DEVROOT");
429 	/*
430 	 * Root data... nobody should be using this though.
431 	 */
432 	root_data = (oss_mixext_root *)&ext->data;
433 	(void) snprintf(root_data->name, sizeof (root_data->name), "%s",
434 	    auclnt_get_dev_name(d));
435 	(void) snprintf(root_data->id, sizeof (root_data->id), "%s",
436 	    auclnt_get_dev_name(d));
437 
438 	odev->d_nctrl++;
439 
440 	/*
441 	 * Insert an extra marker -- needed to keep layout apps hapy.
442 	 * This prevents some apps from assuming we are in "LEGACY" mode.
443 	 */
444 	ext = &odev->d_exts[odev->d_nctrl];
445 	ext->ctrl = odev->d_nctrl;
446 	ext->control_no = -1;
447 	ext->type = MIXT_MARKER;
448 	ext->timestamp = gethrtime();
449 	ext->parent = 0;
450 	odev->d_nctrl++;
451 
452 	/* Fill in the complete table now */
453 	auclnt_walk_controls(d, oss_add_control, odev);
454 
455 	/* Update the update_counter reference counter for groups */
456 	for (nctrl = 0; nctrl < odev->d_nctrl; nctrl++) {
457 		int i;
458 
459 		ext = &odev->d_exts[nctrl];
460 		i = ext->parent;
461 		while ((i >= 0) && (i < odev->d_nctrl)) {
462 
463 			ext = &odev->d_exts[i];
464 			ASSERT(ext->parent < i);
465 			ASSERT((ext->type == MIXT_GROUP) ||
466 			    (ext->type == MIXT_DEVROOT));
467 			ext->update_counter++;
468 			i = ext->parent;
469 		}
470 	}
471 
472 	ASSERT(odev->d_nctrl <= odev->d_nalloc);
473 }
474 
475 static int
476 oss_open(audio_client_t *c, int oflag)
477 {
478 	int		rv;
479 	ossdev_t	*odev;
480 	ossclient_t	*sc;
481 	audio_stream_t	*isp, *osp;
482 
483 	isp = auclnt_input_stream(c);
484 	osp = auclnt_output_stream(c);
485 
486 	/* note that OSS always uses nonblocking open() semantics */
487 	if ((rv = auclnt_open(c, AUDIO_FORMAT_PCM, oflag | FNDELAY)) != 0) {
488 		return (rv);
489 	}
490 
491 	if ((sc = kmem_zalloc(sizeof (*sc), KM_NOSLEEP)) == NULL) {
492 		auclnt_close(c);
493 		return (ENOMEM);
494 	}
495 	auclnt_set_private(c, sc);
496 
497 	odev = auclnt_get_minor_data(c, AUDIO_MINOR_DSP);
498 
499 	/* set a couple of common fields */
500 	sc->o_client = c;
501 	sc->o_ossdev = odev;
502 
503 	/* set all default parameters */
504 	if (oflag & FWRITE) {
505 		if (((rv = auclnt_set_format(osp, OSS_FMT)) != 0) ||
506 		    ((rv = auclnt_set_rate(osp, OSS_RATE)) != 0) ||
507 		    ((rv = auclnt_set_channels(osp, OSS_CHANNELS)) != 0)) {
508 			goto failed;
509 		}
510 		/* default to 5 fragments to provide reasonable latency */
511 		auclnt_set_latency(osp, 5, 0);
512 	}
513 
514 	if (oflag & FREAD) {
515 		if (((rv = auclnt_set_format(isp, OSS_FMT)) != 0) ||
516 		    ((rv = auclnt_set_rate(isp, OSS_RATE)) != 0) ||
517 		    ((rv = auclnt_set_channels(isp, OSS_CHANNELS)) != 0)) {
518 			goto failed;
519 		}
520 		/* default to 5 fragments to provide reasonable latency */
521 		auclnt_set_latency(isp, 5, 0);
522 	}
523 
524 	return (0);
525 
526 failed:
527 	auclnt_close(c);
528 	return (rv);
529 }
530 
531 static void
532 oss_close(audio_client_t *c)
533 {
534 	ossclient_t	*sc;
535 
536 	sc = auclnt_get_private(c);
537 
538 	if (ddi_can_receive_sig() || (ddi_get_pid() == 0)) {
539 		(void) auclnt_drain(c);
540 	}
541 
542 	kmem_free(sc, sizeof (*sc));
543 
544 	auclnt_close(c);
545 }
546 
547 /*
548  * This is used to generate an array of names for an enumeration
549  */
550 static ushort_t
551 oss_set_enum(oss_mixer_enuminfo *ei, ushort_t nxt, const char *name)
552 {
553 	uint32_t	n;
554 
555 	/* Get current entry to fill in */
556 	n = ei->nvalues;
557 	(void) snprintf(&ei->strings[nxt], ((sizeof (ei->strings) - nxt) - 1),
558 	    "%s", name);
559 	ei->strindex[n] = nxt;
560 
561 	/* Adjust everything for next entry */
562 	nxt += strnlen(name, ((sizeof (ei->strings) - nxt) - 1));
563 	ei->strings[nxt++] = '\0';
564 
565 	ei->nvalues++;
566 	return (nxt);
567 }
568 
569 /*
570  * The following two functions are used to count the number of devices
571  * in under the boomer framework.
572  *
573  * We actually report the highest "index", and then if an audio device
574  * is not found, we report a bogus removed device for it in the actual
575  * ioctls.  This goofiness is required to make the OSS API happy.
576  */
577 int
578 oss_dev_walker(audio_dev_t *d, void *arg)
579 {
580 	int		*pcnt = arg;
581 	int		cnt;
582 	int		index;
583 
584 	cnt = *pcnt;
585 	index = auclnt_get_dev_index(d);
586 	if ((index + 1) > cnt) {
587 		cnt = index + 1;
588 		*pcnt = cnt;
589 	}
590 
591 	return (AUDIO_WALK_CONTINUE);
592 }
593 
594 static int
595 oss_cnt_devs(void)
596 {
597 	int cnt = 0;
598 
599 	auclnt_walk_devs(oss_dev_walker, &cnt);
600 	return (cnt);
601 }
602 
603 static int
604 sndctl_dsp_speed(audio_client_t *c, int *ratep)
605 {
606 	int		rate;
607 	int		oflag;
608 
609 	rate = *ratep;
610 
611 	oflag = auclnt_get_oflag(c);
612 	if (oflag & FREAD) {
613 		(void) auclnt_set_rate(auclnt_input_stream(c), rate);
614 		*ratep = auclnt_get_rate(auclnt_input_stream(c));
615 	}
616 
617 	if (oflag & FWRITE) {
618 		(void) auclnt_set_rate(auclnt_output_stream(c), rate);
619 		*ratep = auclnt_get_rate(auclnt_output_stream(c));
620 	}
621 
622 	return (0);
623 }
624 
625 static int
626 sndctl_dsp_setfmt(audio_client_t *c, int *fmtp)
627 {
628 	int		fmt;
629 	int		i;
630 	int		oflag;
631 
632 	oflag = auclnt_get_oflag(c);
633 
634 	if (*fmtp != AFMT_QUERY) {
635 		/* convert from OSS */
636 		for (i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
637 			if (oss_formats[i].oss == *fmtp) {
638 				fmt = oss_formats[i].fmt;
639 				break;
640 			}
641 		}
642 		if (fmt == AUDIO_FORMAT_NONE) {
643 			/* if format not known, return */
644 			goto done;
645 		}
646 
647 		if (oflag & FWRITE) {
648 			(void) auclnt_set_format(auclnt_output_stream(c), fmt);
649 		}
650 
651 		if (oflag & FREAD) {
652 			(void) auclnt_set_format(auclnt_input_stream(c), fmt);
653 		}
654 	}
655 
656 done:
657 	if (oflag & FWRITE) {
658 		fmt = auclnt_get_format(auclnt_output_stream(c));
659 	} else if (oflag & FREAD) {
660 		fmt = auclnt_get_format(auclnt_input_stream(c));
661 	}
662 
663 	/* convert back to OSS */
664 	*(int *)fmtp = AFMT_QUERY;
665 	for (i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
666 		if (oss_formats[i].fmt == fmt) {
667 			*(int *)fmtp = oss_formats[i].oss;
668 		}
669 	}
670 
671 	return (0);
672 }
673 
674 static int
675 sndctl_dsp_getfmts(audio_client_t *c, int *fmtsp)
676 {
677 	_NOTE(ARGUNUSED(c));
678 
679 	/*
680 	 * For now, we support all the standard ones.  Later we might
681 	 * add in conditional support for AC3.
682 	 */
683 	*fmtsp = (AFMT_MU_LAW | AFMT_A_LAW |
684 	    AFMT_U8 | AFMT_S8 |
685 	    AFMT_S16_LE |AFMT_S16_BE |
686 	    AFMT_S24_LE | AFMT_S24_BE |
687 	    AFMT_S32_LE | AFMT_S32_BE |
688 	    AFMT_S24_PACKED);
689 
690 	return (0);
691 }
692 
693 static int
694 sndctl_dsp_channels(audio_client_t *c, int *chanp)
695 {
696 	int		nchan;
697 	int		oflag;
698 
699 	oflag = auclnt_get_oflag(c);
700 
701 	nchan = *chanp;
702 	if (nchan != 0) {
703 		if (oflag & FWRITE) {
704 			(void) auclnt_set_channels(auclnt_output_stream(c),
705 			    nchan);
706 		}
707 
708 		if (oflag & FREAD) {
709 			(void) auclnt_set_channels(auclnt_input_stream(c),
710 			    nchan);
711 		}
712 	}
713 
714 	if (oflag & FWRITE) {
715 		nchan = auclnt_get_channels(auclnt_output_stream(c));
716 	} else if (oflag & FREAD) {
717 		nchan = auclnt_get_channels(auclnt_input_stream(c));
718 	}
719 	*chanp = nchan;
720 	return (0);
721 }
722 
723 static int
724 sndctl_dsp_stereo(audio_client_t *c, int *onoff)
725 {
726 	int	nchan;
727 
728 	switch (*onoff) {
729 	case 0:
730 		nchan = 1;
731 		break;
732 	case 1:
733 		nchan = 2;
734 		break;
735 	default:
736 		return (EINVAL);
737 	}
738 
739 	return (sndctl_dsp_channels(c, &nchan));
740 }
741 
742 static int
743 sndctl_dsp_post(audio_client_t *c)
744 {
745 	if (auclnt_get_oflag(c) & FWRITE) {
746 		audio_stream_t	*sp = auclnt_output_stream(c);
747 		auclnt_flush(sp);
748 		auclnt_clear_paused(sp);
749 	}
750 	return (0);
751 }
752 
753 static int
754 sndctl_dsp_getcaps(audio_client_t *c, int *capsp)
755 {
756 	int		ncaps;
757 	int		osscaps = 0;
758 
759 	ncaps = auclnt_get_dev_capab(auclnt_get_dev(c));
760 
761 	if (ncaps & AUDIO_CLIENT_CAP_PLAY)
762 		osscaps |= PCM_CAP_OUTPUT;
763 	if (ncaps & AUDIO_CLIENT_CAP_RECORD)
764 		osscaps |= PCM_CAP_INPUT;
765 	if (ncaps & AUDIO_CLIENT_CAP_DUPLEX)
766 		osscaps |= PCM_CAP_DUPLEX;
767 
768 	if (osscaps != 0) {
769 		osscaps |= PCM_CAP_TRIGGER | PCM_CAP_BATCH;
770 		if (!(ncaps & AUDIO_CLIENT_CAP_OPAQUE)) {
771 			osscaps |= PCM_CAP_FREERATE | PCM_CAP_MULTI;
772 		}
773 	} else {
774 		/* This is the sndstat device! */
775 		osscaps = PCM_CAP_VIRTUAL;
776 	}
777 
778 	*capsp = osscaps;
779 	return (0);
780 }
781 
782 static int
783 sndctl_dsp_gettrigger(audio_client_t *c, int *trigp)
784 {
785 	int		triggers = 0;
786 	int		oflag;
787 
788 	oflag = auclnt_get_oflag(c);
789 
790 	if (oflag & FWRITE) {
791 		if (!auclnt_is_paused(auclnt_output_stream(c))) {
792 			triggers |= PCM_ENABLE_OUTPUT;
793 		}
794 	}
795 
796 	if (oflag & FREAD) {
797 		if (!auclnt_is_paused(auclnt_input_stream(c))) {
798 			triggers |= PCM_ENABLE_INPUT;
799 		}
800 	}
801 	*trigp = triggers;
802 
803 	return (0);
804 }
805 
806 static int
807 sndctl_dsp_settrigger(audio_client_t *c, int *trigp)
808 {
809 	int		triggers;
810 	int		oflag;
811 	audio_stream_t	*sp;
812 
813 	oflag = auclnt_get_oflag(c);
814 	triggers = *trigp;
815 
816 	if ((oflag & FWRITE) && (triggers & PCM_ENABLE_OUTPUT)) {
817 		sp = auclnt_output_stream(c);
818 		auclnt_clear_paused(sp);
819 		auclnt_start(sp);
820 	}
821 
822 	if ((oflag & FREAD) && (triggers & PCM_ENABLE_INPUT)) {
823 		sp = auclnt_input_stream(c);
824 		auclnt_clear_paused(sp);
825 		auclnt_start(sp);
826 	}
827 
828 	return (0);
829 }
830 
831 struct oss_legacy_volume {
832 	pid_t		pid;
833 	uint8_t		ogain;
834 	uint8_t		igain;
835 };
836 
837 static int
838 oss_legacy_volume_walker(audio_client_t *c, void *arg)
839 {
840 	struct oss_legacy_volume	 *olv = arg;
841 
842 	if (auclnt_get_pid(c) == olv->pid) {
843 		if (olv->ogain <= 100) {
844 			auclnt_set_gain(auclnt_output_stream(c), olv->ogain);
845 		}
846 		if (olv->igain <= 100) {
847 			auclnt_set_gain(auclnt_input_stream(c), olv->igain);
848 		}
849 	}
850 	return (AUDIO_WALK_CONTINUE);
851 }
852 
853 static void
854 oss_set_legacy_volume(audio_client_t *c, uint8_t ogain, uint8_t igain)
855 {
856 	struct oss_legacy_volume olv;
857 
858 	olv.pid = auclnt_get_pid(c);
859 	olv.ogain = ogain;
860 	olv.igain = igain;
861 	auclnt_dev_walk_clients(auclnt_get_dev(c),
862 	    oss_legacy_volume_walker, &olv);
863 }
864 
865 static int
866 sndctl_dsp_getplayvol(audio_client_t *c, int *volp)
867 {
868 	int	vol;
869 
870 	/* convert monophonic soft value to OSS stereo value */
871 	vol = auclnt_get_gain(auclnt_output_stream(c));
872 	*volp = vol | (vol << 8);
873 	return (0);
874 }
875 
876 static int
877 sndctl_dsp_setplayvol(audio_client_t *c, int *volp)
878 {
879 	uint8_t		vol;
880 
881 	vol = *volp & 0xff;
882 	if (vol > 100) {
883 		return (EINVAL);
884 	}
885 
886 	auclnt_set_gain(auclnt_output_stream(c), vol);
887 	*volp = (vol | (vol << 8));
888 
889 	return (0);
890 }
891 
892 static int
893 sndctl_dsp_getrecvol(audio_client_t *c, int *volp)
894 {
895 	int	vol;
896 
897 	vol = auclnt_get_gain(auclnt_input_stream(c));
898 	*volp = (vol | (vol << 8));
899 	return (0);
900 }
901 
902 static int
903 sndctl_dsp_setrecvol(audio_client_t *c, int *volp)
904 {
905 	uint8_t		vol;
906 
907 	vol = *volp & 0xff;
908 	if (vol > 100) {
909 		return (EINVAL);
910 	}
911 
912 	auclnt_set_gain(auclnt_input_stream(c), vol);
913 	*volp = (vol | (vol << 8));
914 
915 	return (0);
916 }
917 
918 static int
919 sound_mixer_write_ogain(audio_client_t *c, int *volp)
920 {
921 	uint8_t		vol;
922 
923 	vol = *volp & 0xff;
924 	if (vol > 100) {
925 		return (EINVAL);
926 	}
927 	oss_set_legacy_volume(c, vol, 255);
928 	*volp = (vol | (vol << 8));
929 	return (0);
930 }
931 
932 static int
933 sound_mixer_write_igain(audio_client_t *c, int *volp)
934 {
935 	uint8_t		vol;
936 
937 	vol = *volp & 0xff;
938 	if (vol > 100) {
939 		return (EINVAL);
940 	}
941 	oss_set_legacy_volume(c, 255, vol);
942 	*volp = (vol | (vol << 8));
943 	return (0);
944 }
945 
946 static int
947 sndctl_dsp_readctl(audio_client_t *c, oss_digital_control *ctl)
948 {
949 	/* SPDIF: need to add support with spdif */
950 	_NOTE(ARGUNUSED(c));
951 	_NOTE(ARGUNUSED(ctl));
952 	return (ENOTSUP);
953 }
954 
955 static int
956 sndctl_dsp_writectl(audio_client_t *c, oss_digital_control *ctl)
957 {
958 	/* SPDIF: need to add support with spdif */
959 	_NOTE(ARGUNUSED(c));
960 	_NOTE(ARGUNUSED(ctl));
961 	return (ENOTSUP);
962 }
963 
964 static int
965 sndctl_dsp_cookedmode(audio_client_t *c, int *rvp)
966 {
967 	_NOTE(ARGUNUSED(c));
968 
969 	/* We are *always* in cooked mode -- at least until we have AC3. */
970 	if (*rvp == 0) {
971 		return (ENOTSUP);
972 	} else {
973 		return (0);
974 	}
975 }
976 
977 static int
978 sndctl_dsp_silence(audio_client_t *c)
979 {
980 	if (auclnt_get_oflag(c) & FWRITE) {
981 		audio_stream_t	*sp = auclnt_output_stream(c);
982 		auclnt_set_paused(sp);
983 		auclnt_flush(sp);
984 	}
985 	return (0);
986 }
987 
988 static int
989 sndctl_dsp_skip(audio_client_t *c)
990 {
991 	if (auclnt_get_oflag(c) & FWRITE) {
992 		audio_stream_t	*sp = auclnt_output_stream(c);
993 		auclnt_set_paused(sp);
994 		auclnt_flush(sp);
995 		auclnt_clear_paused(sp);
996 	}
997 	return (0);
998 }
999 
1000 static int
1001 sndctl_dsp_halt_input(audio_client_t *c)
1002 {
1003 	if (auclnt_get_oflag(c) & FREAD) {
1004 		audio_stream_t	*sp = auclnt_input_stream(c);
1005 		auclnt_set_paused(sp);
1006 		auclnt_flush(sp);
1007 	}
1008 	return (0);
1009 }
1010 
1011 static int
1012 sndctl_dsp_halt_output(audio_client_t *c)
1013 {
1014 	if (auclnt_get_oflag(c) & FWRITE) {
1015 		audio_stream_t	*sp = auclnt_output_stream(c);
1016 		auclnt_set_paused(sp);
1017 		auclnt_flush(sp);
1018 	}
1019 	return (0);
1020 }
1021 
1022 static int
1023 sndctl_dsp_halt(audio_client_t *c)
1024 {
1025 	(void) sndctl_dsp_halt_input(c);
1026 	(void) sndctl_dsp_halt_output(c);
1027 	return (0);
1028 }
1029 
1030 static int
1031 sndctl_dsp_sync(audio_client_t *c)
1032 {
1033 	return (auclnt_drain(c));
1034 }
1035 
1036 static int
1037 sndctl_dsp_setfragment(audio_client_t *c, int *fragp)
1038 {
1039 	int	bufsz;
1040 	int	nfrags;
1041 	int	fragsz;
1042 
1043 	nfrags = (*fragp) >> 16;
1044 	if ((nfrags >= 0x7fffU) || (nfrags < 2)) {
1045 		/* use infinite setting... no change */
1046 		return (0);
1047 	}
1048 
1049 	fragsz = (*fragp) & 0xffff;
1050 	if (fragsz > 16) {
1051 		/* basically too big, so, no change */
1052 		return (0);
1053 	}
1054 	bufsz = (1U << fragsz) * nfrags;
1055 
1056 	/*
1057 	 * Now we have our desired buffer size, but we have to
1058 	 * make sure we have a whole number of fragments >= 2, and
1059 	 * less than the maximum.
1060 	 */
1061 	bufsz = ((*fragp) >> 16) * (1U << (*fragp));
1062 	if (bufsz >= 65536) {
1063 		return (0);
1064 	}
1065 
1066 	/*
1067 	 * We set the latency hints in terms of bytes, not fragments.
1068 	 */
1069 	auclnt_set_latency(auclnt_output_stream(c), 0, bufsz);
1070 	auclnt_set_latency(auclnt_input_stream(c), 0, bufsz);
1071 
1072 	/*
1073 	 * According to the OSS API documentation, the values provided
1074 	 * are nothing more than a "hint" and not to be relied upon
1075 	 * anyway.  And we aren't obligated to report the actual
1076 	 * values back!
1077 	 */
1078 	return (0);
1079 }
1080 
1081 static int
1082 sndctl_dsp_policy(audio_client_t *c, int *policy)
1083 {
1084 	int	hint = *policy;
1085 	if ((hint >= 2) && (hint <= 10)) {
1086 		auclnt_set_latency(auclnt_input_stream(c), hint, 0);
1087 		auclnt_set_latency(auclnt_output_stream(c), hint, 0);
1088 	}
1089 	return (0);
1090 }
1091 
1092 /*
1093  * A word about recsrc, and playtgt ioctls: We don't allow ordinary DSP
1094  * applications to change port configurations, because these could have a
1095  * bad effect for other applications.  Instead, these settings have to
1096  * be changed using the master mixer panel.  In order to make applications
1097  * happy, we just present a single "default" source/target.
1098  */
1099 static int
1100 sndctl_dsp_get_recsrc_names(audio_client_t *c, oss_mixer_enuminfo *ei)
1101 {
1102 	_NOTE(ARGUNUSED(c));
1103 
1104 	ei->nvalues = 1;
1105 	(void) snprintf(ei->strings, sizeof (ei->strings), "default");
1106 	ei->strindex[0] = 0;
1107 
1108 	return (0);
1109 }
1110 
1111 static int
1112 sndctl_dsp_get_recsrc(audio_client_t *c, int *srcp)
1113 {
1114 	_NOTE(ARGUNUSED(c));
1115 	*srcp = 0;
1116 	return (0);
1117 }
1118 
1119 static int
1120 sndctl_dsp_set_recsrc(audio_client_t *c, int *srcp)
1121 {
1122 	_NOTE(ARGUNUSED(c));
1123 	*srcp = 0;
1124 	return (0);
1125 }
1126 
1127 static int
1128 sndctl_dsp_get_playtgt_names(audio_client_t *c, oss_mixer_enuminfo *ei)
1129 {
1130 	_NOTE(ARGUNUSED(c));
1131 
1132 	ei->nvalues = 1;
1133 	(void) snprintf(ei->strings, sizeof (ei->strings), "default");
1134 	ei->strindex[0] = 0;
1135 
1136 	return (0);
1137 }
1138 
1139 static int
1140 sndctl_dsp_get_playtgt(audio_client_t *c, int *tgtp)
1141 {
1142 	_NOTE(ARGUNUSED(c));
1143 	*tgtp = 0;
1144 	return (0);
1145 }
1146 
1147 static int
1148 sndctl_dsp_set_playtgt(audio_client_t *c, int *tgtp)
1149 {
1150 	_NOTE(ARGUNUSED(c));
1151 	*tgtp = 0;
1152 	return (0);
1153 }
1154 
1155 static int
1156 sndctl_sysinfo(oss_sysinfo *si)
1157 {
1158 	bzero(si, sizeof (*si));
1159 	(void) snprintf(si->product, sizeof (si->product), "SunOS Audio");
1160 	(void) snprintf(si->version, sizeof (si->version), "4.0");
1161 	si->versionnum = OSS_VERSION;
1162 	si->numcards = oss_cnt_devs();
1163 	si->nummixers = si->numcards - 1;
1164 	si->numaudios = si->numcards - 1;
1165 	si->numaudioengines = si->numaudios;
1166 	(void) snprintf(si->license, sizeof (si->license), "CDDL");
1167 	return (0);
1168 }
1169 
1170 static int
1171 sndctl_cardinfo(audio_client_t *c, oss_card_info *ci)
1172 {
1173 	audio_dev_t	*d;
1174 	void		*iter;
1175 	const char 	*info;
1176 	int		n;
1177 	boolean_t	release;
1178 
1179 	if ((n = ci->card) == -1) {
1180 		release = B_FALSE;
1181 		d = auclnt_get_dev(c);
1182 		n = auclnt_get_dev_index(d);
1183 	} else {
1184 		release = B_TRUE;
1185 		d = auclnt_hold_dev_by_index(n);
1186 	}
1187 
1188 	bzero(ci, sizeof (*ci));
1189 	ci->card = n;
1190 
1191 	if (d == NULL) {
1192 		/*
1193 		 * If device removed (e.g. for DR), then
1194 		 * report a bogus removed entry.
1195 		 */
1196 		(void) snprintf(ci->shortname, sizeof (ci->shortname),
1197 		    "<removed>");
1198 		(void) snprintf(ci->longname, sizeof (ci->longname),
1199 		    "<removed>");
1200 		return (0);
1201 	}
1202 
1203 	(void) snprintf(ci->shortname, sizeof (ci->shortname),
1204 	    "%s", auclnt_get_dev_name(d));
1205 	(void) snprintf(ci->longname, sizeof (ci->longname),
1206 	    "%s (%s)", auclnt_get_dev_description(d),
1207 	    auclnt_get_dev_version(d));
1208 
1209 	iter = NULL;
1210 	while ((info = auclnt_get_dev_hw_info(d, &iter)) != NULL) {
1211 		(void) strlcat(ci->hw_info, info, sizeof (ci->hw_info));
1212 		(void) strlcat(ci->hw_info, "\n", sizeof (ci->hw_info));
1213 	}
1214 
1215 	/*
1216 	 * We don't report interrupt counts, ack counts (which are
1217 	 * just "read" interrupts, not spurious), or any other flags.
1218 	 * Nothing should be using any of this data anyway ... these
1219 	 * values were intended for 4Front's debugging purposes.  In
1220 	 * Solaris, drivers should use interrupt kstats to report
1221 	 * interrupt related statistics.
1222 	 */
1223 	if (release)
1224 		auclnt_release_dev(d);
1225 	return (0);
1226 }
1227 
1228 static int
1229 audioinfo_walker(audio_engine_t *e, void *a)
1230 {
1231 	oss_audioinfo *si = a;
1232 	int fmt, nchan, rate, cap;
1233 
1234 	fmt = auclnt_engine_get_format(e);
1235 	nchan = auclnt_engine_get_channels(e);
1236 	rate = auclnt_engine_get_rate(e);
1237 	cap = auclnt_engine_get_capab(e);
1238 
1239 	for (int i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
1240 		if (fmt == oss_formats[i].fmt) {
1241 			if (cap & AUDIO_CLIENT_CAP_PLAY) {
1242 				si->oformats |= oss_formats[i].oss;
1243 			}
1244 			if (cap & AUDIO_CLIENT_CAP_RECORD) {
1245 				si->iformats |= oss_formats[i].oss;
1246 			}
1247 			break;
1248 		}
1249 	}
1250 	si->max_channels = max(nchan, si->max_channels);
1251 	si->max_rate = max(rate, si->max_rate);
1252 
1253 	return (AUDIO_WALK_CONTINUE);
1254 }
1255 
1256 static int
1257 sndctl_audioinfo(audio_client_t *c, oss_audioinfo *si)
1258 {
1259 	audio_dev_t		*d;
1260 	const char		*name;
1261 	int			n;
1262 	boolean_t		release;
1263 	unsigned		cap;
1264 
1265 	if ((n = si->dev) == -1) {
1266 		release = B_FALSE;
1267 		d = auclnt_get_dev(c);
1268 		n = auclnt_get_dev_index(d);
1269 	} else {
1270 		release = B_TRUE;
1271 		n++;	/* skip pseudo device */
1272 		d = auclnt_hold_dev_by_index(n);
1273 	}
1274 
1275 	bzero(si, sizeof (*si));
1276 	si->dev = n - 1;
1277 
1278 	if (d == NULL) {
1279 		/* if device not present, forge a false entry */
1280 		si->card_number = n;
1281 		si->mixer_dev = n - 1;
1282 		si->legacy_device = -1;
1283 		si->enabled = 0;
1284 		(void) snprintf(si->name, sizeof (si->name), "<removed>");
1285 		return (0);
1286 	}
1287 
1288 	name = auclnt_get_dev_name(d);
1289 	(void) snprintf(si->name, sizeof (si->name), "%s", name);
1290 
1291 	si->legacy_device = auclnt_get_dev_number(d);
1292 	si->caps = 0;
1293 
1294 	auclnt_dev_walk_engines(d, audioinfo_walker, si);
1295 
1296 	cap = auclnt_get_dev_capab(d);
1297 
1298 	if (cap	& AUDIO_CLIENT_CAP_DUPLEX) {
1299 		si->caps |= PCM_CAP_DUPLEX;
1300 	}
1301 	if (cap & AUDIO_CLIENT_CAP_PLAY) {
1302 		si->caps |= PCM_CAP_OUTPUT;
1303 	}
1304 	if (cap & AUDIO_CLIENT_CAP_RECORD) {
1305 		si->caps |= PCM_CAP_INPUT;
1306 	}
1307 
1308 	if (si->caps != 0) {
1309 		/* MMAP: we add PCM_CAP_MMAP when we we support it */
1310 		si->caps |= PCM_CAP_TRIGGER | PCM_CAP_BATCH;
1311 		si->enabled = 1;
1312 		si->rate_source = si->dev;
1313 
1314 		/* we can convert and mix PCM formats */
1315 		if (!(cap & AUDIO_CLIENT_CAP_OPAQUE)) {
1316 			si->min_channels = min(2, si->max_channels);
1317 			si->min_rate = min(5000, si->max_rate);
1318 			si->caps |= PCM_CAP_FREERATE | PCM_CAP_MULTI;
1319 		}
1320 		(void) snprintf(si->devnode, sizeof (si->devnode),
1321 		    "/dev/sound/%s:%ddsp",
1322 		    auclnt_get_dev_driver(d), auclnt_get_dev_instance(d));
1323 	} else {
1324 		si->enabled = 0;	/* stops apps from using us directly */
1325 		si->caps = PCM_CAP_VIRTUAL;
1326 		(void) snprintf(si->devnode, sizeof (si->devnode),
1327 		    "/dev/sndstat");
1328 	}
1329 
1330 	si->pid = -1;
1331 	(void) snprintf(si->handle, sizeof (si->handle), "%s", name);
1332 	(void) snprintf(si->label, sizeof (si->label), "%s", name);
1333 	si->latency = -1;
1334 	si->card_number = n;
1335 	si->mixer_dev = n - 1;
1336 
1337 	if (release)
1338 		auclnt_release_dev(d);
1339 
1340 	return (0);
1341 }
1342 
1343 static int
1344 sound_mixer_info(audio_client_t *c, mixer_info *mi)
1345 {
1346 	audio_dev_t	*d;
1347 	const char	*name;
1348 
1349 	d = auclnt_get_dev(c);
1350 
1351 	name = auclnt_get_dev_name(d);
1352 	(void) snprintf(mi->id, sizeof (mi->id), "%s", name);
1353 	(void) snprintf(mi->name, sizeof (mi->name), "%s", name);
1354 	(void) snprintf(mi->handle, sizeof (mi->handle), "%s", name);
1355 	mi->modify_counter = (int)auclnt_dev_get_serial(d);
1356 	mi->card_number = auclnt_get_dev_index(d);
1357 	mi->port_number = 0;
1358 	return (0);
1359 }
1360 
1361 static int
1362 sound_mixer_read_devmask(audio_client_t *c, int *devmask)
1363 {
1364 	_NOTE(ARGUNUSED(c));
1365 	*devmask = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_IGAIN;
1366 	return (0);
1367 }
1368 
1369 static int
1370 sound_mixer_read_recmask(audio_client_t *c, int *recmask)
1371 {
1372 	_NOTE(ARGUNUSED(c));
1373 	*recmask = 0;
1374 	return (0);
1375 }
1376 
1377 static int
1378 sound_mixer_read_recsrc(audio_client_t *c, int *recsrc)
1379 {
1380 	_NOTE(ARGUNUSED(c));
1381 	*recsrc = 0;
1382 	return (0);
1383 }
1384 
1385 static int
1386 sound_mixer_read_caps(audio_client_t *c, int *caps)
1387 {
1388 	_NOTE(ARGUNUSED(c));
1389 	/* single recording source... sort of */
1390 	*caps = SOUND_CAP_EXCL_INPUT;
1391 	return (0);
1392 }
1393 
1394 static int
1395 sndctl_mixerinfo(audio_client_t *c, oss_mixerinfo *mi)
1396 {
1397 	audio_dev_t		*d;
1398 	ossdev_t 		*odev;
1399 	const char		*name;
1400 	int			n;
1401 	boolean_t		release = B_FALSE;
1402 
1403 	if ((n = mi->dev) == -1) {
1404 		release = B_FALSE;
1405 		d = auclnt_get_dev(c);
1406 		n = auclnt_get_dev_index(d);
1407 	} else {
1408 		release = B_TRUE;
1409 		n++;
1410 		d = auclnt_hold_dev_by_index(n);
1411 	}
1412 
1413 	bzero(mi, sizeof (*mi));
1414 	mi->dev = n - 1;
1415 
1416 	if (d == NULL) {
1417 		mi->card_number = n;
1418 		mi->enabled = 0;
1419 		mi->legacy_device = -1;
1420 		(void) snprintf(mi->name, sizeof (mi->name), "<removed>");
1421 		(void) snprintf(mi->id, sizeof (mi->id), "<removed>");
1422 		return (0);
1423 	}
1424 
1425 	if ((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) {
1426 		if (release)
1427 			auclnt_release_dev(d);
1428 		return (EINVAL);
1429 	}
1430 
1431 	name = auclnt_get_dev_name(d);
1432 	(void) snprintf(mi->name, sizeof (mi->name), "%s", name);
1433 	(void) snprintf(mi->id, sizeof (mi->id), "%s", name);
1434 	(void) snprintf(mi->handle, sizeof (mi->handle), "%s", name);
1435 	mi->modify_counter = (int)auclnt_dev_get_serial(d);
1436 	mi->card_number = auclnt_get_dev_index(d);
1437 	mi->legacy_device = auclnt_get_dev_number(d);
1438 	if (mi->legacy_device >= 0) {
1439 		(void) snprintf(mi->devnode, sizeof (mi->devnode),
1440 		    "/dev/sound/%s:%dmixer",
1441 		    auclnt_get_dev_driver(d), auclnt_get_dev_instance(d));
1442 		mi->enabled = 1;
1443 	} else {
1444 		/* special nodes use generic sndstat node */
1445 		(void) snprintf(mi->devnode, sizeof (mi->devnode),
1446 		    "/dev/sndstat");
1447 		mi->enabled = 0;
1448 	}
1449 	mi->nrext = odev->d_nctrl;
1450 
1451 	if (release)
1452 		auclnt_release_dev(d);
1453 
1454 	return (0);
1455 }
1456 
1457 static int
1458 sndctl_dsp_getblksize(audio_client_t *c, int *fragsz)
1459 {
1460 	int	oflag = auclnt_get_oflag(c);
1461 
1462 	if (oflag & FWRITE)
1463 		*fragsz  = auclnt_get_fragsz(auclnt_output_stream(c));
1464 	else if (oflag & FREAD)
1465 		*fragsz  = auclnt_get_fragsz(auclnt_input_stream(c));
1466 
1467 	return (0);
1468 }
1469 
1470 static int
1471 sndctl_dsp_getospace(audio_client_t *c, audio_buf_info *bi)
1472 {
1473 	audio_stream_t	*sp;
1474 	unsigned	n;
1475 
1476 	if ((auclnt_get_oflag(c) & FWRITE) == 0) {
1477 		return (EACCES);
1478 	}
1479 
1480 	sp = auclnt_output_stream(c);
1481 	n = auclnt_get_nframes(sp) - auclnt_get_count(sp);
1482 
1483 	bi->fragsize  = auclnt_get_fragsz(sp);
1484 	bi->fragstotal = auclnt_get_nfrags(sp);
1485 	bi->bytes = (n * auclnt_get_framesz(sp));
1486 	bi->fragments = bi->bytes / bi->fragsize;
1487 
1488 	return (0);
1489 }
1490 
1491 static int
1492 sndctl_dsp_getispace(audio_client_t *c, audio_buf_info *bi)
1493 {
1494 	audio_stream_t	*sp;
1495 	unsigned	n;
1496 
1497 	if ((auclnt_get_oflag(c) & FREAD) == 0) {
1498 		return (EACCES);
1499 	}
1500 
1501 	sp = auclnt_input_stream(c);
1502 	n = auclnt_get_count(sp);
1503 
1504 	bi->fragsize  = auclnt_get_fragsz(sp);
1505 	bi->fragstotal = auclnt_get_nfrags(sp);
1506 	bi->bytes = (n * auclnt_get_framesz(sp));
1507 	bi->fragments = bi->bytes / bi->fragsize;
1508 
1509 	return (0);
1510 }
1511 
1512 static int
1513 sndctl_dsp_getodelay(audio_client_t *c, int *bytes)
1514 {
1515 	unsigned	framesz;
1516 	unsigned	slen, flen;
1517 
1518 	if (auclnt_get_oflag(c) & FWRITE) {
1519 		audio_stream_t	*sp = auclnt_output_stream(c);
1520 		framesz = auclnt_get_framesz(sp);
1521 		auclnt_get_output_qlen(c, &slen, &flen);
1522 		*bytes = (slen + flen) * framesz;
1523 	} else {
1524 		*bytes = 0;
1525 	}
1526 	return (0);
1527 }
1528 
1529 static int
1530 sndctl_dsp_current_iptr(audio_client_t *c, oss_count_t *count)
1531 {
1532 	if (auclnt_get_oflag(c) & FREAD) {
1533 		count->samples = auclnt_get_samples(auclnt_input_stream(c));
1534 		count->fifo_samples = 0;	/* not quite accurate */
1535 	} else {
1536 		count->samples = 0;
1537 		count->fifo_samples = 0;
1538 	}
1539 	return (0);
1540 }
1541 
1542 static int
1543 sndctl_dsp_current_optr(audio_client_t *c, oss_count_t *count)
1544 {
1545 	unsigned samples, fifo;
1546 
1547 	if (auclnt_get_oflag(c) & FWRITE) {
1548 		auclnt_get_output_qlen(c, &samples, &fifo);
1549 		count->samples = samples;
1550 		count->fifo_samples = fifo;
1551 	} else {
1552 		count->samples = 0;
1553 		count->fifo_samples = 0;
1554 	}
1555 	return (0);
1556 }
1557 
1558 static int
1559 sndctl_dsp_getoptr(audio_client_t *c, count_info *ci)
1560 {
1561 	audio_stream_t	*sp;
1562 	unsigned	framesz;
1563 	unsigned	fragsz;
1564 
1565 	bzero(ci, sizeof (*ci));
1566 	if ((auclnt_get_oflag(c) & FWRITE) == 0) {
1567 		return (0);
1568 	}
1569 	sp = auclnt_output_stream(c);
1570 	framesz = auclnt_get_framesz(sp);
1571 	fragsz = auclnt_get_fragsz(sp);
1572 	ci->blocks = auclnt_get_samples(sp) * framesz / fragsz;
1573 	auclnt_set_samples(sp, 0);
1574 	ci->bytes = auclnt_get_tail(sp) * framesz;
1575 	ci->ptr = auclnt_get_tidx(sp) * framesz;
1576 	return (0);
1577 }
1578 
1579 static int
1580 sndctl_dsp_getiptr(audio_client_t *c, count_info *ci)
1581 {
1582 	audio_stream_t	*sp;
1583 	unsigned	framesz;
1584 	unsigned	fragsz;
1585 
1586 	bzero(ci, sizeof (*ci));
1587 	if ((auclnt_get_oflag(c) & FREAD) == 0) {
1588 		return (0);
1589 	}
1590 	sp = auclnt_input_stream(c);
1591 	framesz = auclnt_get_framesz(sp);
1592 	fragsz = auclnt_get_fragsz(sp);
1593 	ci->blocks = auclnt_get_samples(sp) * framesz / fragsz;
1594 	auclnt_set_samples(sp, 0);
1595 	ci->bytes = auclnt_get_head(sp) * framesz;
1596 	ci->ptr = auclnt_get_hidx(sp) * framesz;
1597 	return (0);
1598 }
1599 
1600 static int
1601 sndctl_dsp_geterror(audio_client_t *c, audio_errinfo *bi)
1602 {
1603 	audio_stream_t	*sp;
1604 	unsigned	fragsz;
1605 	/*
1606 	 * Note: The use of this structure is unsafe... different
1607 	 * meanings for error codes are used by different implementations,
1608 	 * according to the spec.  (Even different versions of the same
1609 	 * implementation could have different values.)
1610 	 *
1611 	 * Rather than try to come up with a reliable solution here, we
1612 	 * don't use it.  If you want to report errors, or see the result
1613 	 * of errors, use syslog.
1614 	 */
1615 	bzero(bi, sizeof (*bi));
1616 
1617 	sp = auclnt_output_stream(c);
1618 	fragsz = max(auclnt_get_fragsz(sp), 1);
1619 	bi->play_underruns = (int)((auclnt_get_errors(sp) + (fragsz - 1)) /
1620 	    fragsz);
1621 	auclnt_set_errors(sp, 0);
1622 
1623 	sp = auclnt_input_stream(c);
1624 	fragsz = max(auclnt_get_fragsz(sp), 1);
1625 	bi->rec_overruns = (int)((auclnt_get_errors(sp) + (fragsz - 1)) /
1626 	    fragsz);
1627 	auclnt_set_errors(sp, 0);
1628 
1629 	return (0);
1630 }
1631 
1632 static int
1633 sndctl_sun_send_number(audio_client_t *c, int *num, cred_t *cr)
1634 {
1635 	audio_dev_t	*dev;
1636 	int		rv;
1637 
1638 	if ((rv = drv_priv(cr)) != 0) {
1639 		return (rv);
1640 	}
1641 
1642 	dev = auclnt_get_dev(c);
1643 	auclnt_set_dev_number(dev, *num);
1644 	return (0);
1645 }
1646 
1647 static int
1648 oss_getversion(int *versp)
1649 {
1650 	*versp = OSS_VERSION;
1651 	return (0);
1652 }
1653 
1654 static int
1655 oss_ioctl(audio_client_t *c, int cmd, intptr_t arg, int mode, cred_t *credp,
1656     int *rvalp)
1657 {
1658 	int	sz;
1659 	void	*data;
1660 	int	rv = 0;
1661 
1662 	_NOTE(ARGUNUSED(credp));
1663 
1664 	sz = OSSIOC_GETSZ(cmd);
1665 
1666 	if ((cmd & (OSSIOC_IN | OSSIOC_OUT)) && sz) {
1667 		if ((data = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
1668 			return (ENOMEM);
1669 		}
1670 	} else {
1671 		sz = 0;
1672 	}
1673 
1674 	if (cmd & OSSIOC_IN) {
1675 		if ((rv = ddi_copyin((void *)arg, data, sz, mode)) != 0) {
1676 			goto done;
1677 		}
1678 	}
1679 
1680 	switch (cmd) {
1681 		/*
1682 		 * DSP specific ioctls
1683 		 */
1684 	case SNDCTL_DSP_HALT:
1685 		rv = sndctl_dsp_halt(c);
1686 		break;
1687 
1688 	case SNDCTL_DSP_SYNC:
1689 		rv = sndctl_dsp_sync(c);
1690 		break;
1691 
1692 	case SNDCTL_DSP_SPEED:
1693 		rv = sndctl_dsp_speed(c, (int *)data);
1694 		break;
1695 	case SNDCTL_DSP_SETFMT:
1696 		rv = sndctl_dsp_setfmt(c, (int *)data);
1697 		break;
1698 	case SNDCTL_DSP_GETFMTS:
1699 		rv = sndctl_dsp_getfmts(c, (int *)data);
1700 		break;
1701 	case SNDCTL_DSP_STEREO:
1702 		rv = sndctl_dsp_stereo(c, (int *)data);
1703 		break;
1704 	case SNDCTL_DSP_CHANNELS:
1705 		rv = sndctl_dsp_channels(c, (int *)data);
1706 		break;
1707 	case SNDCTL_DSP_POST:
1708 		rv = sndctl_dsp_post(c);
1709 		break;
1710 	case SNDCTL_DSP_GETCAPS:
1711 		rv = sndctl_dsp_getcaps(c, (int *)data);
1712 		break;
1713 	case SNDCTL_DSP_GETTRIGGER:
1714 		rv = sndctl_dsp_gettrigger(c, (int *)data);
1715 		break;
1716 	case SNDCTL_DSP_SETTRIGGER:
1717 		rv = sndctl_dsp_settrigger(c, (int *)data);
1718 		break;
1719 	case SNDCTL_DSP_GETPLAYVOL:
1720 	case SOUND_MIXER_READ_VOLUME:	/* legacy mixer on dsp */
1721 	case SOUND_MIXER_READ_PCM:	/* legacy mixer on dsp */
1722 	case SOUND_MIXER_READ_OGAIN:	/* legacy mixer on dsp */
1723 		rv = sndctl_dsp_getplayvol(c, (int *)data);
1724 		break;
1725 	case SOUND_MIXER_WRITE_VOLUME:	/* legacy mixer on dsp */
1726 	case SOUND_MIXER_WRITE_PCM:	/* legacy mixer on dsp */
1727 	case SOUND_MIXER_WRITE_OGAIN:	/* legacy mixer on dsp */
1728 		rv = sound_mixer_write_ogain(c, (int *)data);
1729 		break;
1730 	case SNDCTL_DSP_SETPLAYVOL:
1731 		rv = sndctl_dsp_setplayvol(c, (int *)data);
1732 		break;
1733 	case SNDCTL_DSP_READCTL:
1734 		rv = sndctl_dsp_readctl(c, (oss_digital_control *)data);
1735 		break;
1736 	case SNDCTL_DSP_WRITECTL:
1737 		rv = sndctl_dsp_writectl(c, (oss_digital_control *)data);
1738 		break;
1739 	case SNDCTL_DSP_COOKEDMODE:
1740 		rv = sndctl_dsp_cookedmode(c, (int *)data);
1741 		break;
1742 	case SNDCTL_DSP_SILENCE:
1743 		rv = sndctl_dsp_silence(c);
1744 		break;
1745 	case SNDCTL_DSP_SKIP:
1746 		rv = sndctl_dsp_skip(c);
1747 		break;
1748 	case SNDCTL_DSP_HALT_INPUT:
1749 		rv = sndctl_dsp_halt_input(c);
1750 		break;
1751 	case SNDCTL_DSP_HALT_OUTPUT:
1752 		rv = sndctl_dsp_halt_output(c);
1753 		break;
1754 	case SNDCTL_DSP_GET_RECSRC_NAMES:
1755 		rv = sndctl_dsp_get_recsrc_names(c, (oss_mixer_enuminfo *)data);
1756 		break;
1757 	case SNDCTL_DSP_SETFRAGMENT:
1758 		rv = sndctl_dsp_setfragment(c, (int *)data);
1759 		break;
1760 	case SNDCTL_DSP_GET_RECSRC:
1761 		rv = sndctl_dsp_get_recsrc(c, (int *)data);
1762 		break;
1763 	case SNDCTL_DSP_SET_RECSRC:
1764 		rv = sndctl_dsp_set_recsrc(c, (int *)data);
1765 		break;
1766 	case SNDCTL_DSP_GET_PLAYTGT_NAMES:
1767 		rv = sndctl_dsp_get_playtgt_names(c,
1768 		    (oss_mixer_enuminfo *)data);
1769 		break;
1770 	case SNDCTL_DSP_GET_PLAYTGT:
1771 		rv = sndctl_dsp_get_playtgt(c, (int *)data);
1772 		break;
1773 	case SNDCTL_DSP_SET_PLAYTGT:
1774 		rv = sndctl_dsp_set_playtgt(c, (int *)data);
1775 		break;
1776 	case SNDCTL_DSP_GETRECVOL:
1777 	case SOUND_MIXER_READ_RECGAIN:	/* legacy mixer on dsp */
1778 	case SOUND_MIXER_READ_RECLEV:	/* legacy mixer on dsp */
1779 	case SOUND_MIXER_READ_IGAIN:	/* legacy mixer on dsp */
1780 		rv = sndctl_dsp_getrecvol(c, (int *)data);
1781 		break;
1782 	case SOUND_MIXER_WRITE_RECGAIN:	/* legacy mixer on dsp */
1783 	case SOUND_MIXER_WRITE_RECLEV:	/* legacy mixer on dsp */
1784 	case SOUND_MIXER_WRITE_IGAIN:	/* legacy mixer on dsp */
1785 		rv = sound_mixer_write_igain(c, (int *)data);
1786 		break;
1787 	case SNDCTL_DSP_SETRECVOL:
1788 		rv = sndctl_dsp_setrecvol(c, (int *)data);
1789 		break;
1790 	case SNDCTL_DSP_SUBDIVIDE:	/* Ignored */
1791 	case SNDCTL_DSP_SETDUPLEX:	/* Ignored */
1792 	case SNDCTL_DSP_LOW_WATER:	/* Ignored */
1793 	case SNDCTL_DSP_PROFILE:	/* Ignored */
1794 		rv = 0;
1795 		break;
1796 	case SNDCTL_DSP_POLICY:
1797 		rv = sndctl_dsp_policy(c, (int *)data);
1798 		break;
1799 	case SNDCTL_DSP_GETBLKSIZE:
1800 		rv = sndctl_dsp_getblksize(c, (int *)data);
1801 		break;
1802 	case SNDCTL_DSP_GETOSPACE:
1803 		rv = sndctl_dsp_getospace(c, (audio_buf_info *)data);
1804 		break;
1805 	case SNDCTL_DSP_GETISPACE:
1806 		rv = sndctl_dsp_getispace(c, (audio_buf_info *)data);
1807 		break;
1808 	case SNDCTL_DSP_GETODELAY:
1809 		rv = sndctl_dsp_getodelay(c, (int *)data);
1810 		break;
1811 	case SNDCTL_DSP_GETOPTR:
1812 		rv = sndctl_dsp_getoptr(c, (count_info *)data);
1813 		break;
1814 	case SNDCTL_DSP_GETIPTR:
1815 		rv = sndctl_dsp_getiptr(c, (count_info *)data);
1816 		break;
1817 	case SNDCTL_DSP_GETERROR:
1818 		rv = sndctl_dsp_geterror(c, (audio_errinfo *)data);
1819 		break;
1820 	case SNDCTL_DSP_CURRENT_IPTR:
1821 		rv = sndctl_dsp_current_iptr(c, (oss_count_t *)data);
1822 		break;
1823 	case SNDCTL_DSP_CURRENT_OPTR:
1824 		rv = sndctl_dsp_current_optr(c, (oss_count_t *)data);
1825 		break;
1826 
1827 		/*
1828 		 * Shared ioctls with /dev/mixer.
1829 		 */
1830 	case OSS_GETVERSION:
1831 		rv = oss_getversion((int *)data);
1832 		break;
1833 	case SNDCTL_CARDINFO:
1834 		rv = sndctl_cardinfo(c, (oss_card_info *)data);
1835 		break;
1836 	case SNDCTL_ENGINEINFO:
1837 	case SNDCTL_AUDIOINFO:
1838 	case SNDCTL_AUDIOINFO_EX:
1839 		rv = sndctl_audioinfo(c, (oss_audioinfo *)data);
1840 		break;
1841 	case SNDCTL_SYSINFO:
1842 		rv = sndctl_sysinfo((oss_sysinfo *)data);
1843 		break;
1844 	case SNDCTL_MIXERINFO:
1845 		rv = sndctl_mixerinfo(c, (oss_mixerinfo *)data);
1846 		break;
1847 	case SOUND_MIXER_INFO:
1848 		rv = sound_mixer_info(c, (mixer_info *)data);
1849 		break;
1850 
1851 		/*
1852 		 * These are mixer ioctls that are virtualized for the DSP
1853 		 * device.  They are accessible via either /dev/mixer or
1854 		 * /dev/dsp.
1855 		 */
1856 	case SOUND_MIXER_READ_RECSRC:
1857 	case SOUND_MIXER_WRITE_RECSRC:
1858 		rv = sound_mixer_read_recsrc(c, (int *)data);
1859 		break;
1860 
1861 	case SOUND_MIXER_READ_DEVMASK:
1862 	case SOUND_MIXER_READ_STEREODEVS:
1863 		rv = sound_mixer_read_devmask(c, (int *)data);
1864 		break;
1865 
1866 	case SOUND_MIXER_READ_RECMASK:
1867 		rv = sound_mixer_read_recmask(c, (int *)data);
1868 		break;
1869 
1870 	case SOUND_MIXER_READ_CAPS:
1871 		rv = sound_mixer_read_caps(c, (int *)data);
1872 		break;
1873 
1874 		/*
1875 		 * Ioctls we have chosen not to support for now.  Some
1876 		 * of these are of legacy interest only.
1877 		 */
1878 	case SNDCTL_SETSONG:
1879 	case SNDCTL_GETSONG:
1880 	case SNDCTL_DSP_SYNCGROUP:
1881 	case SNDCTL_DSP_SYNCSTART:
1882 	case SNDCTL_DSP_GET_CHNORDER:
1883 	case SNDCTL_DSP_SET_CHNORDER:
1884 	case SNDCTL_DSP_GETIPEAKS:
1885 	case SNDCTL_DSP_GETOPEAKS:
1886 	case SNDCTL_DSP_GETCHANNELMASK:
1887 	case SNDCTL_DSP_BIND_CHANNEL:
1888 	case SNDCTL_DSP_SETSYNCRO:
1889 	case SNDCTL_DSP_NONBLOCK:
1890 	default:
1891 		rv = EINVAL;
1892 		break;
1893 	}
1894 
1895 	if ((rv == 0) && (cmd & OSSIOC_OUT)) {
1896 		rv = ddi_copyout(data, (void *)arg, sz, mode);
1897 	}
1898 	if (rv == 0) {
1899 		*rvalp = 0;
1900 	}
1901 
1902 done:
1903 	if (sz) {
1904 		kmem_free(data, sz);
1905 	}
1906 	return (rv);
1907 }
1908 
1909 static void
1910 oss_output(audio_client_t *c)
1911 {
1912 	auclnt_pollwakeup(c, POLLOUT);
1913 }
1914 
1915 static void
1916 oss_input(audio_client_t *c)
1917 {
1918 	auclnt_pollwakeup(c, POLLIN | POLLRDNORM);
1919 }
1920 
1921 static int
1922 ossmix_open(audio_client_t *c, int oflag)
1923 {
1924 	int		rv;
1925 	ossclient_t	*sc;
1926 	ossdev_t	*odev;
1927 
1928 	_NOTE(ARGUNUSED(oflag));
1929 
1930 	if ((rv = auclnt_open(c, AUDIO_FORMAT_NONE, 0)) != 0) {
1931 		return (rv);
1932 	}
1933 
1934 	if ((sc = kmem_zalloc(sizeof (*sc), KM_NOSLEEP)) == NULL) {
1935 		return (ENOMEM);
1936 	}
1937 	sc->o_ss_sz = 8192;
1938 	if ((sc->o_ss_buf = kmem_zalloc(sc->o_ss_sz, KM_NOSLEEP)) == NULL) {
1939 		kmem_free(sc, sizeof (*sc));
1940 		return (ENOMEM);
1941 	}
1942 	auclnt_set_private(c, sc);
1943 
1944 	odev = auclnt_get_minor_data(c, AUDIO_MINOR_DSP);
1945 
1946 	/* set a couple of common fields */
1947 	sc->o_client = c;
1948 	sc->o_ossdev = odev;
1949 
1950 	return (rv);
1951 }
1952 
1953 static void
1954 ossmix_close(audio_client_t *c)
1955 {
1956 	ossclient_t	*sc;
1957 
1958 	sc = auclnt_get_private(c);
1959 
1960 	kmem_free(sc->o_ss_buf, sc->o_ss_sz);
1961 	kmem_free(sc, sizeof (*sc));
1962 
1963 	auclnt_close(c);
1964 }
1965 
1966 static int
1967 sndctl_mix_nrext(audio_client_t *c, int *ncp)
1968 {
1969 	audio_dev_t	*d;
1970 	ossdev_t	*odev;
1971 
1972 	d = auclnt_get_dev(c);
1973 
1974 	if ((*ncp != -1) && (*ncp != (auclnt_get_dev_index(d) - 1))) {
1975 		return (ENXIO);
1976 	}
1977 
1978 	if ((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) {
1979 		return (EINVAL);
1980 	}
1981 
1982 	*ncp = odev->d_nctrl;
1983 
1984 	return (0);
1985 }
1986 
1987 static int
1988 sndctl_mix_extinfo(audio_client_t *c, oss_mixext *pext)
1989 {
1990 	audio_dev_t		*d;
1991 	ossdev_t		*odev;
1992 	int			rv = 0;
1993 	int			dev;
1994 
1995 	d = auclnt_get_dev(c);
1996 
1997 	if (((dev = pext->dev) != -1) && (dev != (auclnt_get_dev_index(d) - 1)))
1998 		return (ENXIO);
1999 
2000 	if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
2001 	    (pext->ctrl >= odev->d_nctrl)) {
2002 		return (EINVAL);
2003 	}
2004 
2005 	bcopy(&odev->d_exts[pext->ctrl], pext, sizeof (*pext));
2006 	pext->enumbit = 0;
2007 	pext->dev = dev;
2008 
2009 	return (rv);
2010 }
2011 
2012 static int
2013 sndctl_mix_enuminfo(audio_client_t *c, oss_mixer_enuminfo *ei)
2014 {
2015 	audio_dev_t		*d;
2016 	audio_ctrl_desc_t	desc;
2017 	audio_ctrl_t		*ctrl;
2018 	ossdev_t		*odev;
2019 	uint64_t		mask;
2020 	int			bit;
2021 	ushort_t		nxt;
2022 
2023 	d = auclnt_get_dev(c);
2024 
2025 	if ((ei->dev != -1) && (ei->dev != (auclnt_get_dev_index(d) - 1)))
2026 		return (ENXIO);
2027 
2028 	if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
2029 	    (ei->ctrl >= odev->d_nctrl) ||
2030 	    (odev->d_exts[ei->ctrl].type != MIXT_ENUM) ||
2031 	    ((ctrl = odev->d_ctrls[ei->ctrl]) == NULL) ||
2032 	    (auclnt_control_describe(ctrl, &desc) != 0)) {
2033 		return (EINVAL);
2034 	}
2035 
2036 	mask = desc.acd_maxvalue;
2037 	bit = 0;
2038 	nxt = 0;
2039 	ei->nvalues = 0;
2040 	bzero(ei->strings, sizeof (ei->strings));
2041 	bzero(ei->strindex, sizeof (ei->strindex));
2042 
2043 	while (mask) {
2044 		const char *name = desc.acd_enum[bit];
2045 		nxt = oss_set_enum(ei, nxt, name ? name : "");
2046 		mask >>= 1;
2047 		bit++;
2048 	}
2049 
2050 	return (0);
2051 }
2052 
2053 static int
2054 sndctl_mix_read(audio_client_t *c, oss_mixer_value *vr)
2055 {
2056 	int			rv;
2057 	uint64_t		v;
2058 	audio_dev_t		*d;
2059 	audio_ctrl_t		*ctrl;
2060 	ossdev_t		*odev;
2061 
2062 	d = auclnt_get_dev(c);
2063 
2064 	if ((vr->dev != -1) && (vr->dev != (auclnt_get_dev_index(d) - 1)))
2065 		return (ENXIO);
2066 
2067 	if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
2068 	    (vr->ctrl >= odev->d_nctrl) ||
2069 	    ((ctrl = odev->d_ctrls[vr->ctrl]) == NULL)) {
2070 		return (EINVAL);
2071 	}
2072 	if ((rv = auclnt_control_read(ctrl, &v)) == 0) {
2073 		switch (odev->d_exts[vr->ctrl].type) {
2074 		case MIXT_ENUM:
2075 			/* translate this from an enum style bit mask */
2076 			vr->value = ddi_ffs((unsigned long)v) - 1;
2077 			break;
2078 		case MIXT_STEREOSLIDER:
2079 			vr->value = (int)ddi_swap16(v & 0xffff);
2080 			break;
2081 		case MIXT_MONOSLIDER:
2082 			vr->value = (int)(v | (v << 8));
2083 			break;
2084 		case MIXT_ONOFF:
2085 			/* this could be simple, or could be part of a multi */
2086 			if (odev->d_exts[vr->ctrl].enumbit >= 0) {
2087 				uint64_t mask;
2088 				mask = 1;
2089 				mask <<= (odev->d_exts[vr->ctrl].enumbit);
2090 				vr->value = (v & mask) ? 1 : 0;
2091 			} else {
2092 				vr->value = v ? 1 : 0;
2093 			}
2094 			break;
2095 
2096 		default:
2097 			vr->value = (int)v;
2098 			break;
2099 		}
2100 	}
2101 
2102 	return (rv);
2103 }
2104 
2105 static int
2106 sndctl_mix_write(audio_client_t *c, oss_mixer_value *vr)
2107 {
2108 	int			rv;
2109 	uint64_t		v;
2110 	audio_dev_t		*d;
2111 	audio_ctrl_t		*ctrl;
2112 	ossdev_t		*odev;
2113 
2114 	d = auclnt_get_dev(c);
2115 
2116 	if ((vr->dev != -1) && (vr->dev != (auclnt_get_dev_index(d) - 1)))
2117 		return (ENXIO);
2118 
2119 	if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
2120 	    (vr->ctrl >= odev->d_nctrl) ||
2121 	    ((ctrl = odev->d_ctrls[vr->ctrl]) == NULL)) {
2122 		return (EINVAL);
2123 	}
2124 
2125 	switch (odev->d_exts[vr->ctrl].type) {
2126 	case MIXT_ONOFF:
2127 		/* this could be standalone, or it could be part of a multi */
2128 		if (odev->d_exts[vr->ctrl].enumbit >= 0) {
2129 			uint64_t mask;
2130 			if ((rv = auclnt_control_read(ctrl, &v)) != 0) {
2131 				return (EINVAL);
2132 			}
2133 			mask = 1;
2134 			mask <<= (odev->d_exts[vr->ctrl].enumbit);
2135 			if (vr->value) {
2136 				v |= mask;
2137 			} else {
2138 				v &= ~mask;
2139 			}
2140 		} else {
2141 			v = vr->value;
2142 		}
2143 		break;
2144 	case MIXT_ENUM:
2145 		/* translate this to an enum style bit mask */
2146 		v = 1U << vr->value;
2147 		break;
2148 	case MIXT_MONOSLIDER:
2149 		/* mask off high order bits */
2150 		v = vr->value & 0xff;
2151 		break;
2152 	case MIXT_STEREOSLIDER:
2153 		/* OSS uses reverse byte ordering */
2154 		v = vr->value;
2155 		v = ddi_swap16(vr->value & 0xffff);
2156 		break;
2157 	default:
2158 		v = vr->value;
2159 	}
2160 	rv = auclnt_control_write(ctrl, v);
2161 
2162 	return (rv);
2163 }
2164 
2165 static int
2166 sndctl_mix_nrmix(audio_client_t *c, int *nmixp)
2167 {
2168 	_NOTE(ARGUNUSED(c));
2169 	*nmixp = oss_cnt_devs() - 1;
2170 	return (0);
2171 }
2172 
2173 static int
2174 ossmix_ioctl(audio_client_t *c, int cmd, intptr_t arg, int mode, cred_t *credp,
2175     int *rvalp)
2176 {
2177 	int	sz;
2178 	void	*data;
2179 	int	rv = 0;
2180 
2181 	sz = OSSIOC_GETSZ(cmd);
2182 
2183 	if ((cmd & (OSSIOC_IN | OSSIOC_OUT)) && sz) {
2184 		if ((data = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
2185 			return (ENOMEM);
2186 		}
2187 	} else {
2188 		sz = 0;
2189 	}
2190 
2191 	if (cmd & OSSIOC_IN) {
2192 		if ((rv = ddi_copyin((void *)arg, data, sz, mode)) != 0) {
2193 			goto done;
2194 		}
2195 	}
2196 
2197 	switch (cmd) {
2198 		/*
2199 		 * Mixer specific ioctls
2200 		 */
2201 	case SNDCTL_MIX_NREXT:
2202 		rv = sndctl_mix_nrext(c, (int *)data);
2203 		break;
2204 	case SNDCTL_MIX_EXTINFO:
2205 		rv = sndctl_mix_extinfo(c, (oss_mixext *)data);
2206 		break;
2207 	case SNDCTL_MIX_ENUMINFO:
2208 		rv = sndctl_mix_enuminfo(c, (oss_mixer_enuminfo *)data);
2209 		break;
2210 	case SNDCTL_MIX_READ:
2211 		rv = sndctl_mix_read(c, (oss_mixer_value *)data);
2212 		break;
2213 	case SNDCTL_MIX_WRITE:
2214 		rv = sndctl_mix_write(c, (oss_mixer_value *)data);
2215 		break;
2216 	case SNDCTL_MIX_NRMIX:
2217 		rv = sndctl_mix_nrmix(c, (int *)data);
2218 		break;
2219 
2220 		/*
2221 		 * Legacy ioctls.  These are treated as soft values only,
2222 		 * and do not affect global hardware state.  For use by
2223 		 * legacy DSP applications.
2224 		 */
2225 	case SOUND_MIXER_READ_VOLUME:
2226 	case SOUND_MIXER_READ_PCM:
2227 	case SOUND_MIXER_READ_OGAIN:
2228 		rv = sndctl_dsp_getplayvol(c, (int *)data);
2229 		break;
2230 
2231 	case SOUND_MIXER_WRITE_VOLUME:
2232 	case SOUND_MIXER_WRITE_PCM:
2233 	case SOUND_MIXER_WRITE_OGAIN:
2234 		rv = sound_mixer_write_ogain(c, (int *)data);
2235 		break;
2236 
2237 	case SOUND_MIXER_READ_RECGAIN:
2238 	case SOUND_MIXER_READ_RECLEV:
2239 	case SOUND_MIXER_READ_IGAIN:
2240 		rv = sndctl_dsp_getrecvol(c, (int *)data);
2241 		break;
2242 
2243 	case SOUND_MIXER_WRITE_RECGAIN:
2244 	case SOUND_MIXER_WRITE_RECLEV:
2245 	case SOUND_MIXER_WRITE_IGAIN:
2246 		rv = sound_mixer_write_igain(c, (int *)data);
2247 		break;
2248 
2249 	case SOUND_MIXER_READ_RECSRC:
2250 	case SOUND_MIXER_WRITE_RECSRC:
2251 		rv = sound_mixer_read_recsrc(c, (int *)data);
2252 		break;
2253 
2254 	case SOUND_MIXER_READ_DEVMASK:
2255 	case SOUND_MIXER_READ_STEREODEVS:
2256 		rv = sound_mixer_read_devmask(c, (int *)data);
2257 		break;
2258 
2259 	case SOUND_MIXER_READ_RECMASK:
2260 		rv = sound_mixer_read_recmask(c, (int *)data);
2261 		break;
2262 
2263 	case SOUND_MIXER_READ_CAPS:
2264 		rv = sound_mixer_read_caps(c, (int *)data);
2265 		break;
2266 
2267 		/*
2268 		 * Common ioctls shared with DSP
2269 		 */
2270 	case OSS_GETVERSION:
2271 		rv = oss_getversion((int *)data);
2272 		break;
2273 
2274 	case SNDCTL_CARDINFO:
2275 		rv = sndctl_cardinfo(c, (oss_card_info *)data);
2276 		break;
2277 
2278 	case SNDCTL_ENGINEINFO:
2279 	case SNDCTL_AUDIOINFO:
2280 	case SNDCTL_AUDIOINFO_EX:
2281 		rv = sndctl_audioinfo(c, (oss_audioinfo *)data);
2282 		break;
2283 
2284 	case SNDCTL_SYSINFO:
2285 		rv = sndctl_sysinfo((oss_sysinfo *)data);
2286 		break;
2287 
2288 	case SNDCTL_MIXERINFO:
2289 		rv = sndctl_mixerinfo(c, (oss_mixerinfo *)data);
2290 		break;
2291 
2292 	case SOUND_MIXER_INFO:
2293 		rv = sound_mixer_info(c, (mixer_info *)data);
2294 		break;
2295 
2296 	case SNDCTL_MIX_DESCRIPTION:	/* NOT SUPPORTED: tooltip */
2297 		rv = EIO;	/* OSS returns EIO for this one */
2298 		break;
2299 
2300 		/*
2301 		 * Special implementation-private ioctls.
2302 		 */
2303 	case SNDCTL_SUN_SEND_NUMBER:
2304 		rv = sndctl_sun_send_number(c, (int *)data, credp);
2305 		break;
2306 
2307 		/*
2308 		 * Legacy ioctls we don't support.
2309 		 */
2310 	case SOUND_MIXER_WRITE_MONGAIN:
2311 	case SOUND_MIXER_READ_MONGAIN:
2312 	case SOUND_MIXER_READ_BASS:
2313 	case SOUND_MIXER_READ_TREBLE:
2314 	case SOUND_MIXER_READ_SPEAKER:
2315 	case SOUND_MIXER_READ_LINE:
2316 	case SOUND_MIXER_READ_MIC:
2317 	case SOUND_MIXER_READ_CD:
2318 	case SOUND_MIXER_READ_IMIX:
2319 	case SOUND_MIXER_READ_ALTPCM:
2320 	case SOUND_MIXER_READ_SYNTH:
2321 	case SOUND_MIXER_READ_LINE1:
2322 	case SOUND_MIXER_READ_LINE2:
2323 	case SOUND_MIXER_READ_LINE3:
2324 	case SOUND_MIXER_WRITE_BASS:
2325 	case SOUND_MIXER_WRITE_TREBLE:
2326 	case SOUND_MIXER_WRITE_SPEAKER:
2327 	case SOUND_MIXER_WRITE_LINE:
2328 	case SOUND_MIXER_WRITE_MIC:
2329 	case SOUND_MIXER_WRITE_CD:
2330 	case SOUND_MIXER_WRITE_IMIX:
2331 	case SOUND_MIXER_WRITE_ALTPCM:
2332 	case SOUND_MIXER_WRITE_SYNTH:
2333 	case SOUND_MIXER_WRITE_LINE1:
2334 	case SOUND_MIXER_WRITE_LINE2:
2335 	case SOUND_MIXER_WRITE_LINE3:
2336 		/*
2337 		 * Additional ioctls we *could* support, but don't.
2338 		 */
2339 	case SNDCTL_SETSONG:
2340 	case SNDCTL_SETLABEL:
2341 	case SNDCTL_GETSONG:
2342 	case SNDCTL_GETLABEL:
2343 	case SNDCTL_MIDIINFO:
2344 	case SNDCTL_SETNAME:
2345 	default:
2346 		rv = EINVAL;
2347 		break;
2348 	}
2349 
2350 	if ((rv == 0) && (cmd & OSSIOC_OUT)) {
2351 		rv = ddi_copyout(data, (void *)arg, sz, mode);
2352 	}
2353 	if (rv == 0) {
2354 		*rvalp = 0;
2355 	}
2356 
2357 done:
2358 	if (sz) {
2359 		kmem_free(data, sz);
2360 	}
2361 	return (rv);
2362 }
2363 
2364 static void *
2365 oss_dev_init(audio_dev_t *dev)
2366 {
2367 	ossdev_t	*odev;
2368 
2369 	odev = kmem_zalloc(sizeof (*odev), KM_SLEEP);
2370 	odev->d_dev = dev;
2371 
2372 	mutex_init(&odev->d_mx, NULL, MUTEX_DRIVER, NULL);
2373 	cv_init(&odev->d_cv, NULL, CV_DRIVER, NULL);
2374 	oss_alloc_controls(odev);
2375 
2376 	return (odev);
2377 }
2378 
2379 static void
2380 oss_dev_fini(void *arg)
2381 {
2382 	ossdev_t	*odev = arg;
2383 
2384 	if (odev != NULL) {
2385 		oss_free_controls(odev);
2386 		mutex_destroy(&odev->d_mx);
2387 		cv_destroy(&odev->d_cv);
2388 		kmem_free(odev, sizeof (*odev));
2389 	}
2390 }
2391 
2392 static void
2393 sndstat_printf(ossclient_t *oc, const char *fmt, ...)
2394 {
2395 	va_list	va;
2396 
2397 	va_start(va, fmt);
2398 	(void) vsnprintf(oc->o_ss_buf + oc->o_ss_len,
2399 	    oc->o_ss_sz - oc->o_ss_len, fmt, va);
2400 	va_end(va);
2401 	oc->o_ss_len = strlen(oc->o_ss_buf);
2402 }
2403 
2404 static int
2405 sndstat_dev_walker(audio_dev_t *d, void *arg)
2406 {
2407 	ossclient_t	*oc = arg;
2408 	const char	*capstr;
2409 	unsigned	cap;
2410 
2411 	cap = auclnt_get_dev_capab(d);
2412 
2413 	if (cap	& AUDIO_CLIENT_CAP_DUPLEX) {
2414 		capstr = "DUPLEX";
2415 	} else if ((cap & AUDIO_CLIENT_CAP_PLAY) &&
2416 	    (cap & AUDIO_CLIENT_CAP_RECORD)) {
2417 		capstr = "INPUT,OUTPUT";
2418 	} else if (cap & AUDIO_CLIENT_CAP_PLAY) {
2419 		capstr = "OUTPUT";
2420 	} else if (cap & AUDIO_CLIENT_CAP_RECORD) {
2421 		capstr = "INPUT";
2422 	} else {
2423 		capstr = NULL;
2424 	}
2425 
2426 	if (capstr == NULL)
2427 		return (AUDIO_WALK_CONTINUE);
2428 
2429 	sndstat_printf(oc, "%d: %s %s, %s (%s)\n",
2430 	    auclnt_get_dev_number(d), auclnt_get_dev_name(d),
2431 	    auclnt_get_dev_description(d), auclnt_get_dev_version(d), capstr);
2432 
2433 	return (AUDIO_WALK_CONTINUE);
2434 }
2435 
2436 static int
2437 sndstat_mixer_walker(audio_dev_t *d, void *arg)
2438 {
2439 	ossclient_t	*oc = arg;
2440 	unsigned	cap;
2441 	void		*iter;
2442 	const char	*info;
2443 
2444 	cap = auclnt_get_dev_capab(d);
2445 
2446 	if ((cap & (AUDIO_CLIENT_CAP_PLAY|AUDIO_CLIENT_CAP_RECORD)) == 0)
2447 		return (AUDIO_WALK_CONTINUE);
2448 
2449 	sndstat_printf(oc, "%d: %s %s, %s\n",
2450 	    auclnt_get_dev_number(d), auclnt_get_dev_name(d),
2451 	    auclnt_get_dev_description(d), auclnt_get_dev_version(d));
2452 	iter = NULL;
2453 	while ((info = auclnt_get_dev_hw_info(d, &iter)) != NULL) {
2454 		sndstat_printf(oc, "\t%s\n", info);
2455 	}
2456 	return (AUDIO_WALK_CONTINUE);
2457 }
2458 
2459 static int
2460 ossmix_write(audio_client_t *c, struct uio *uio, cred_t *cr)
2461 {
2462 	/* write on sndstat is a no-op */
2463 	_NOTE(ARGUNUSED(c));
2464 	_NOTE(ARGUNUSED(uio));
2465 	_NOTE(ARGUNUSED(cr));
2466 
2467 	return (0);
2468 }
2469 
2470 static int
2471 ossmix_read(audio_client_t *c, struct uio *uio, cred_t *cr)
2472 {
2473 	ossclient_t	*oc;
2474 	unsigned	n;
2475 	int		rv;
2476 
2477 	_NOTE(ARGUNUSED(cr));
2478 
2479 	if (uio->uio_resid == 0) {
2480 		return (0);
2481 	}
2482 
2483 	oc = auclnt_get_private(c);
2484 
2485 	mutex_enter(&oc->o_ss_lock);
2486 
2487 	if (oc->o_ss_off == 0) {
2488 
2489 		sndstat_printf(oc, "SunOS Audio Framework\n");
2490 
2491 		sndstat_printf(oc, "\nAudio Devices:\n");
2492 		auclnt_walk_devs_by_number(sndstat_dev_walker, oc);
2493 
2494 		sndstat_printf(oc, "\nMixers:\n");
2495 		auclnt_walk_devs_by_number(sndstat_mixer_walker, oc);
2496 	}
2497 
2498 	/*
2499 	 * For simplicity's sake, we implement a non-seekable device.  We could
2500 	 * support seekability, but offsets would be rather meaningless between
2501 	 * changes.
2502 	 */
2503 	n = min(uio->uio_resid, (oc->o_ss_len - oc->o_ss_off));
2504 
2505 	rv = uiomove(oc->o_ss_buf + oc->o_ss_off, n, UIO_READ, uio);
2506 	if (rv != 0) {
2507 		n = 0;
2508 	}
2509 	oc->o_ss_off += n;
2510 
2511 	if (n == 0) {
2512 		/*
2513 		 * end-of-file reached... clear the sndstat buffer so that
2514 		 * subsequent reads will get the latest data.
2515 		 */
2516 		oc->o_ss_off = oc->o_ss_len = 0;
2517 	}
2518 	mutex_exit(&oc->o_ss_lock);
2519 	return (rv);
2520 }
2521 
2522 int
2523 oss_read(audio_client_t *c, struct uio *uio, cred_t *cr)
2524 {
2525 	_NOTE(ARGUNUSED(cr));
2526 
2527 	auclnt_clear_paused(auclnt_input_stream(c));
2528 
2529 	return (auclnt_read(c, uio));
2530 }
2531 
2532 int
2533 oss_write(audio_client_t *c, struct uio *uio, cred_t *cr)
2534 {
2535 	_NOTE(ARGUNUSED(cr));
2536 
2537 	auclnt_clear_paused(auclnt_output_stream(c));
2538 
2539 	return (auclnt_write(c, uio));
2540 }
2541 
2542 int
2543 oss_chpoll(audio_client_t *c, short events, int anyyet, short *reventsp,
2544     struct pollhead **phpp)
2545 {
2546 	return (auclnt_chpoll(c, events, anyyet, reventsp, phpp));
2547 }
2548 
2549 static struct audio_client_ops oss_ops = {
2550 	"sound,dsp",
2551 	oss_dev_init,
2552 	oss_dev_fini,
2553 	oss_open,
2554 	oss_close,
2555 	oss_read,
2556 	oss_write,
2557 	oss_ioctl,
2558 	oss_chpoll,
2559 	NULL,		/* mmap */
2560 	oss_input,
2561 	oss_output,
2562 	NULL,		/* drain */
2563 };
2564 
2565 static struct audio_client_ops ossmix_ops = {
2566 	"sound,mixer",
2567 	NULL,
2568 	NULL,
2569 	ossmix_open,
2570 	ossmix_close,
2571 	ossmix_read,
2572 	ossmix_write,
2573 	ossmix_ioctl,
2574 	NULL,	/* chpoll */
2575 	NULL,   /* mmap */
2576 	NULL,	/* input */
2577 	NULL,   /* output */
2578 	NULL,	/* drain */
2579 	NULL,	/* wput */
2580 	NULL,	/* wsrv */
2581 };
2582 
2583 /* nearly the same as ossxmix; different minor name helps devfsadm */
2584 static struct audio_client_ops sndstat_ops = {
2585 	"sound,sndstat",
2586 	NULL,	/* dev_init */
2587 	NULL,	/* dev_fini */
2588 	ossmix_open,
2589 	ossmix_close,
2590 	ossmix_read,
2591 	ossmix_write,
2592 	ossmix_ioctl,
2593 	NULL,	/* chpoll */
2594 	NULL,	/* mmap */
2595 	NULL,	/* input */
2596 	NULL,	/* output */
2597 	NULL,	/* drain */
2598 	NULL,	/* wput */
2599 	NULL,	/* wsrv */
2600 };
2601 
2602 void
2603 auimpl_oss_init(void)
2604 {
2605 	auclnt_register_ops(AUDIO_MINOR_DSP, &oss_ops);
2606 	auclnt_register_ops(AUDIO_MINOR_MIXER, &ossmix_ops);
2607 	auclnt_register_ops(AUDIO_MINOR_SNDSTAT, &sndstat_ops);
2608 }
2609