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