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