xref: /illumos-gate/usr/src/uts/common/io/audio/impl/audio_oss.c (revision c7402f0767d7a0360fabd0bd449c6baf9b282074)
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		rv;
608 	int		rate;
609 	int		oflag;
610 
611 	rate = *ratep;
612 
613 	oflag = auclnt_get_oflag(c);
614 	if (oflag & FREAD) {
615 		if ((rv = auclnt_set_rate(auclnt_input_stream(c), rate)) != 0)
616 			return (rv);
617 	}
618 
619 	if (oflag & FWRITE) {
620 		if ((rv = auclnt_set_rate(auclnt_output_stream(c), rate)) != 0)
621 			return (rv);
622 	}
623 
624 	return (0);
625 }
626 
627 static int
628 sndctl_dsp_setfmt(audio_client_t *c, int *fmtp)
629 {
630 	int		rv;
631 	int		fmt;
632 	int		i;
633 	int		oflag;
634 
635 	oflag = auclnt_get_oflag(c);
636 
637 	if (*fmtp != AFMT_QUERY) {
638 		/* convert from OSS */
639 		for (i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
640 			if (oss_formats[i].oss == *fmtp) {
641 				fmt = oss_formats[i].fmt;
642 				break;
643 			}
644 		}
645 		if (fmt == AUDIO_FORMAT_NONE) {
646 			/* if format not known, return ENOTSUP */
647 			return (ENOTSUP);
648 		}
649 
650 		if (oflag & FWRITE) {
651 			rv = auclnt_set_format(auclnt_output_stream(c), fmt);
652 			if (rv != 0)
653 				return (rv);
654 		}
655 
656 		if (oflag & FREAD) {
657 			rv = auclnt_set_format(auclnt_input_stream(c), fmt);
658 			if (rv != 0)
659 				return (rv);
660 		}
661 	}
662 
663 	if (oflag & FWRITE) {
664 		fmt = auclnt_get_format(auclnt_output_stream(c));
665 	} else if (oflag & FREAD) {
666 		fmt = auclnt_get_format(auclnt_input_stream(c));
667 	}
668 
669 	/* convert back to OSS */
670 	*(int *)fmtp = AFMT_QUERY;
671 	for (i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
672 		if (oss_formats[i].fmt == fmt) {
673 			*(int *)fmtp = oss_formats[i].oss;
674 		}
675 	}
676 
677 	return (0);
678 }
679 
680 static int
681 sndctl_dsp_getfmts(audio_client_t *c, int *fmtsp)
682 {
683 	_NOTE(ARGUNUSED(c));
684 
685 	/*
686 	 * For now, we support all the standard ones.  Later we might
687 	 * add in conditional support for AC3.
688 	 */
689 	*fmtsp = (AFMT_MU_LAW | AFMT_A_LAW |
690 	    AFMT_U8 | AFMT_S8 |
691 	    AFMT_S16_LE |AFMT_S16_BE |
692 	    AFMT_S24_LE | AFMT_S24_BE |
693 	    AFMT_S32_LE | AFMT_S32_BE |
694 	    AFMT_S24_PACKED);
695 
696 	return (0);
697 }
698 
699 static int
700 sndctl_dsp_channels(audio_client_t *c, int *chanp)
701 {
702 	int		rv;
703 	int		nchan;
704 	int		oflag;
705 
706 	oflag = auclnt_get_oflag(c);
707 
708 	nchan = *chanp;
709 	if (nchan != 0) {
710 		if (oflag & FWRITE) {
711 			rv = auclnt_set_channels(auclnt_output_stream(c),
712 			    nchan);
713 			if (rv != 0)
714 				return (rv);
715 		}
716 
717 		if (oflag & FREAD) {
718 			rv = auclnt_set_channels(auclnt_input_stream(c), nchan);
719 			if (rv != 0)
720 				return (rv);
721 		}
722 	}
723 
724 	if (oflag & FWRITE) {
725 		nchan = auclnt_get_channels(auclnt_output_stream(c));
726 	} else if (oflag & FREAD) {
727 		nchan = auclnt_get_channels(auclnt_input_stream(c));
728 	}
729 	*chanp = nchan;
730 	return (0);
731 }
732 
733 static int
734 sndctl_dsp_stereo(audio_client_t *c, int *onoff)
735 {
736 	int	nchan;
737 
738 	switch (*onoff) {
739 	case 0:
740 		nchan = 1;
741 		break;
742 	case 1:
743 		nchan = 2;
744 		break;
745 	default:
746 		return (EINVAL);
747 	}
748 
749 	return (sndctl_dsp_channels(c, &nchan));
750 }
751 
752 static int
753 sndctl_dsp_post(audio_client_t *c)
754 {
755 	if (auclnt_get_oflag(c) & FWRITE) {
756 		audio_stream_t	*sp = auclnt_output_stream(c);
757 		auclnt_flush(sp);
758 		auclnt_clear_paused(sp);
759 	}
760 	return (0);
761 }
762 
763 static int
764 sndctl_dsp_getcaps(audio_client_t *c, int *capsp)
765 {
766 	int		ncaps;
767 	int		osscaps = 0;
768 
769 	ncaps = auclnt_get_dev_capab(auclnt_get_dev(c));
770 
771 	if (ncaps & AUDIO_CLIENT_CAP_PLAY)
772 		osscaps |= PCM_CAP_OUTPUT;
773 	if (ncaps & AUDIO_CLIENT_CAP_RECORD)
774 		osscaps |= PCM_CAP_INPUT;
775 	if (ncaps & AUDIO_CLIENT_CAP_DUPLEX)
776 		osscaps |= PCM_CAP_DUPLEX;
777 
778 	*capsp = osscaps;
779 	return (0);
780 }
781 
782 static int
783 sndctl_dsp_gettrigger(audio_client_t *c, int *trigp)
784 {
785 	int		triggers = 0;
786 	int		oflag;
787 
788 	oflag = auclnt_get_oflag(c);
789 
790 	if (oflag & FWRITE) {
791 		if (!auclnt_is_paused(auclnt_output_stream(c))) {
792 			triggers |= PCM_ENABLE_OUTPUT;
793 		}
794 	}
795 
796 	if (oflag & FREAD) {
797 		if (!auclnt_is_paused(auclnt_input_stream(c))) {
798 			triggers |= PCM_ENABLE_INPUT;
799 		}
800 	}
801 	*trigp = triggers;
802 
803 	return (0);
804 }
805 
806 static int
807 sndctl_dsp_settrigger(audio_client_t *c, int *trigp)
808 {
809 	int		triggers;
810 	int		oflag;
811 	audio_stream_t	*sp;
812 
813 	oflag = auclnt_get_oflag(c);
814 	triggers = *trigp;
815 
816 	if ((oflag & FWRITE) && (triggers & PCM_ENABLE_OUTPUT)) {
817 		sp = auclnt_output_stream(c);
818 		auclnt_clear_paused(sp);
819 		auclnt_start(sp);
820 	}
821 
822 	if ((oflag & FREAD) && (triggers & PCM_ENABLE_INPUT)) {
823 		sp = auclnt_input_stream(c);
824 		auclnt_clear_paused(sp);
825 		auclnt_start(sp);
826 	}
827 
828 	return (0);
829 }
830 
831 struct oss_legacy_volume {
832 	pid_t		pid;
833 	uint8_t		ogain;
834 	uint8_t		igain;
835 };
836 
837 static int
838 oss_legacy_volume_walker(audio_client_t *c, void *arg)
839 {
840 	struct oss_legacy_volume	 *olv = arg;
841 
842 	if (auclnt_get_pid(c) == olv->pid) {
843 		if (olv->ogain <= 100) {
844 			auclnt_set_gain(auclnt_output_stream(c), olv->ogain);
845 		}
846 		if (olv->igain <= 100) {
847 			auclnt_set_gain(auclnt_input_stream(c), olv->igain);
848 		}
849 	}
850 	return (AUDIO_WALK_CONTINUE);
851 }
852 
853 static void
854 oss_set_legacy_volume(audio_client_t *c, uint8_t ogain, uint8_t igain)
855 {
856 	struct oss_legacy_volume olv;
857 
858 	olv.pid = auclnt_get_pid(c);
859 	olv.ogain = ogain;
860 	olv.igain = igain;
861 	auclnt_dev_walk_clients(auclnt_get_dev(c),
862 	    oss_legacy_volume_walker, &olv);
863 }
864 
865 static int
866 sndctl_dsp_getplayvol(audio_client_t *c, int *volp)
867 {
868 	int	vol;
869 
870 	/* convert monophonic soft value to OSS stereo value */
871 	vol = auclnt_get_gain(auclnt_output_stream(c));
872 	*volp = vol | (vol << 8);
873 	return (0);
874 }
875 
876 static int
877 sndctl_dsp_setplayvol(audio_client_t *c, int *volp)
878 {
879 	uint8_t		vol;
880 
881 	vol = *volp & 0xff;
882 	if (vol > 100) {
883 		return (EINVAL);
884 	}
885 
886 	auclnt_set_gain(auclnt_output_stream(c), vol);
887 	*volp = (vol | (vol << 8));
888 
889 	return (0);
890 }
891 
892 static int
893 sndctl_dsp_getrecvol(audio_client_t *c, int *volp)
894 {
895 	int	vol;
896 
897 	vol = auclnt_get_gain(auclnt_input_stream(c));
898 	*volp = (vol | (vol << 8));
899 	return (0);
900 }
901 
902 static int
903 sndctl_dsp_setrecvol(audio_client_t *c, int *volp)
904 {
905 	uint8_t		vol;
906 
907 	vol = *volp & 0xff;
908 	if (vol > 100) {
909 		return (EINVAL);
910 	}
911 
912 	auclnt_set_gain(auclnt_input_stream(c), vol);
913 	*volp = (vol | (vol << 8));
914 
915 	return (0);
916 }
917 
918 static int
919 sound_mixer_write_ogain(audio_client_t *c, int *volp)
920 {
921 	uint8_t		vol;
922 
923 	vol = *volp & 0xff;
924 	if (vol > 100) {
925 		return (EINVAL);
926 	}
927 	oss_set_legacy_volume(c, vol, 255);
928 	*volp = (vol | (vol << 8));
929 	return (0);
930 }
931 
932 static int
933 sound_mixer_write_igain(audio_client_t *c, int *volp)
934 {
935 	uint8_t		vol;
936 
937 	vol = *volp & 0xff;
938 	if (vol > 100) {
939 		return (EINVAL);
940 	}
941 	oss_set_legacy_volume(c, 255, vol);
942 	*volp = (vol | (vol << 8));
943 	return (0);
944 }
945 
946 static int
947 sndctl_dsp_readctl(audio_client_t *c, oss_digital_control *ctl)
948 {
949 	/* SPDIF: need to add support with spdif */
950 	_NOTE(ARGUNUSED(c));
951 	_NOTE(ARGUNUSED(ctl));
952 	return (ENOTSUP);
953 }
954 
955 static int
956 sndctl_dsp_writectl(audio_client_t *c, oss_digital_control *ctl)
957 {
958 	/* SPDIF: need to add support with spdif */
959 	_NOTE(ARGUNUSED(c));
960 	_NOTE(ARGUNUSED(ctl));
961 	return (ENOTSUP);
962 }
963 
964 static int
965 sndctl_dsp_cookedmode(audio_client_t *c, int *rvp)
966 {
967 	_NOTE(ARGUNUSED(c));
968 
969 	/* We are *always* in cooked mode -- at least until we have AC3. */
970 	if (*rvp == 0) {
971 		return (ENOTSUP);
972 	} else {
973 		return (0);
974 	}
975 }
976 
977 static int
978 sndctl_dsp_silence(audio_client_t *c)
979 {
980 	if (auclnt_get_oflag(c) & FWRITE) {
981 		audio_stream_t	*sp = auclnt_output_stream(c);
982 		auclnt_set_paused(sp);
983 		auclnt_flush(sp);
984 	}
985 	return (0);
986 }
987 
988 static int
989 sndctl_dsp_skip(audio_client_t *c)
990 {
991 	if (auclnt_get_oflag(c) & FWRITE) {
992 		audio_stream_t	*sp = auclnt_output_stream(c);
993 		auclnt_set_paused(sp);
994 		auclnt_flush(sp);
995 		auclnt_clear_paused(sp);
996 	}
997 	return (0);
998 }
999 
1000 static int
1001 sndctl_dsp_halt_input(audio_client_t *c)
1002 {
1003 	if (auclnt_get_oflag(c) & FREAD) {
1004 		audio_stream_t	*sp = auclnt_input_stream(c);
1005 		auclnt_set_paused(sp);
1006 		auclnt_flush(sp);
1007 	}
1008 	return (0);
1009 }
1010 
1011 static int
1012 sndctl_dsp_halt_output(audio_client_t *c)
1013 {
1014 	if (auclnt_get_oflag(c) & FWRITE) {
1015 		audio_stream_t	*sp = auclnt_output_stream(c);
1016 		auclnt_set_paused(sp);
1017 		auclnt_flush(sp);
1018 	}
1019 	return (0);
1020 }
1021 
1022 static int
1023 sndctl_dsp_halt(audio_client_t *c)
1024 {
1025 	(void) sndctl_dsp_halt_input(c);
1026 	(void) sndctl_dsp_halt_output(c);
1027 	return (0);
1028 }
1029 
1030 static int
1031 sndctl_dsp_sync(audio_client_t *c)
1032 {
1033 	return (auclnt_drain(c));
1034 }
1035 
1036 static int
1037 sndctl_dsp_setfragment(audio_client_t *c, int *fragp)
1038 {
1039 	int	bufsz;
1040 	int	nfrags;
1041 	int	fragsz;
1042 
1043 	nfrags = (*fragp) >> 16;
1044 	if ((nfrags >= 0x7fffU) || (nfrags < 2)) {
1045 		/* use infinite setting... no change */
1046 		return (0);
1047 	}
1048 
1049 	fragsz = (*fragp) & 0xffff;
1050 	if (fragsz > 16) {
1051 		/* basically too big, so, no change */
1052 		return (0);
1053 	}
1054 	bufsz = (1U << fragsz) * nfrags;
1055 
1056 	/*
1057 	 * Now we have our desired buffer size, but we have to
1058 	 * make sure we have a whole number of fragments >= 2, and
1059 	 * less than the maximum.
1060 	 */
1061 	bufsz = ((*fragp) >> 16) * (1U << (*fragp));
1062 	if (bufsz >= 65536) {
1063 		return (0);
1064 	}
1065 
1066 	/*
1067 	 * We set the latency hints in terms of bytes, not fragments.
1068 	 */
1069 	auclnt_set_latency(auclnt_output_stream(c), 0, bufsz);
1070 	auclnt_set_latency(auclnt_input_stream(c), 0, bufsz);
1071 
1072 	/*
1073 	 * According to the OSS API documentation, the values provided
1074 	 * are nothing more than a "hint" and not to be relied upon
1075 	 * anyway.  And we aren't obligated to report the actual
1076 	 * values back!
1077 	 */
1078 	return (0);
1079 }
1080 
1081 static int
1082 sndctl_dsp_policy(audio_client_t *c, int *policy)
1083 {
1084 	int	hint = *policy;
1085 	if ((hint >= 2) && (hint <= 10)) {
1086 		auclnt_set_latency(auclnt_input_stream(c), hint, 0);
1087 		auclnt_set_latency(auclnt_output_stream(c), hint, 0);
1088 	}
1089 	return (0);
1090 }
1091 
1092 /*
1093  * A word about recsrc, and playtgt ioctls: We don't allow ordinary DSP
1094  * applications to change port configurations, because these could have a
1095  * bad effect for other applications.  Instead, these settings have to
1096  * be changed using the master mixer panel.  In order to make applications
1097  * happy, we just present a single "default" source/target.
1098  */
1099 static int
1100 sndctl_dsp_get_recsrc_names(audio_client_t *c, oss_mixer_enuminfo *ei)
1101 {
1102 	_NOTE(ARGUNUSED(c));
1103 
1104 	ei->nvalues = 1;
1105 	(void) snprintf(ei->strings, sizeof (ei->strings), "default");
1106 	ei->strindex[0] = 0;
1107 
1108 	return (0);
1109 }
1110 
1111 static int
1112 sndctl_dsp_get_recsrc(audio_client_t *c, int *srcp)
1113 {
1114 	_NOTE(ARGUNUSED(c));
1115 	*srcp = 0;
1116 	return (0);
1117 }
1118 
1119 static int
1120 sndctl_dsp_set_recsrc(audio_client_t *c, int *srcp)
1121 {
1122 	_NOTE(ARGUNUSED(c));
1123 	*srcp = 0;
1124 	return (0);
1125 }
1126 
1127 static int
1128 sndctl_dsp_get_playtgt_names(audio_client_t *c, oss_mixer_enuminfo *ei)
1129 {
1130 	_NOTE(ARGUNUSED(c));
1131 
1132 	ei->nvalues = 1;
1133 	(void) snprintf(ei->strings, sizeof (ei->strings), "default");
1134 	ei->strindex[0] = 0;
1135 
1136 	return (0);
1137 }
1138 
1139 static int
1140 sndctl_dsp_get_playtgt(audio_client_t *c, int *tgtp)
1141 {
1142 	_NOTE(ARGUNUSED(c));
1143 	*tgtp = 0;
1144 	return (0);
1145 }
1146 
1147 static int
1148 sndctl_dsp_set_playtgt(audio_client_t *c, int *tgtp)
1149 {
1150 	_NOTE(ARGUNUSED(c));
1151 	*tgtp = 0;
1152 	return (0);
1153 }
1154 
1155 static int
1156 sndctl_sysinfo(oss_sysinfo *si)
1157 {
1158 	bzero(si, sizeof (*si));
1159 	(void) snprintf(si->product, sizeof (si->product), "SunOS Audio");
1160 	(void) snprintf(si->version, sizeof (si->version), "4.0");
1161 	si->versionnum = OSS_VERSION;
1162 	si->numcards = oss_cnt_devs();
1163 	si->nummixers = si->numcards - 1;
1164 	si->numaudios = si->numcards - 1;
1165 	si->numaudioengines = si->numaudios;
1166 	(void) snprintf(si->license, sizeof (si->license), "CDDL");
1167 	return (0);
1168 }
1169 
1170 static int
1171 sndctl_cardinfo(audio_client_t *c, oss_card_info *ci)
1172 {
1173 	audio_dev_t	*d;
1174 	void		*iter;
1175 	const char 	*info;
1176 	int		n;
1177 	boolean_t	release;
1178 
1179 	if ((n = ci->card) == -1) {
1180 		release = B_FALSE;
1181 		d = auclnt_get_dev(c);
1182 		n = auclnt_get_dev_index(d);
1183 	} else {
1184 		release = B_TRUE;
1185 		d = auclnt_hold_dev_by_index(n);
1186 	}
1187 
1188 	bzero(ci, sizeof (*ci));
1189 	ci->card = n;
1190 
1191 	if (d == NULL) {
1192 		/*
1193 		 * If device removed (e.g. for DR), then
1194 		 * report a bogus removed entry.
1195 		 */
1196 		(void) snprintf(ci->shortname, sizeof (ci->shortname),
1197 		    "<removed>");
1198 		(void) snprintf(ci->longname, sizeof (ci->longname),
1199 		    "<removed>");
1200 		return (0);
1201 	}
1202 
1203 	(void) snprintf(ci->shortname, sizeof (ci->shortname),
1204 	    "%s", auclnt_get_dev_name(d));
1205 	(void) snprintf(ci->longname, sizeof (ci->longname),
1206 	    "%s (%s)", auclnt_get_dev_description(d),
1207 	    auclnt_get_dev_version(d));
1208 
1209 	iter = NULL;
1210 	while ((info = auclnt_get_dev_hw_info(d, &iter)) != NULL) {
1211 		(void) strlcat(ci->hw_info, info, sizeof (ci->hw_info));
1212 		(void) strlcat(ci->hw_info, "\n", sizeof (ci->hw_info));
1213 	}
1214 
1215 	/*
1216 	 * We don't report interrupt counts, ack counts (which are
1217 	 * just "read" interrupts, not spurious), or any other flags.
1218 	 * Nothing should be using any of this data anyway ... these
1219 	 * values were intended for 4Front's debugging purposes.  In
1220 	 * Solaris, drivers should use interrupt kstats to report
1221 	 * interrupt related statistics.
1222 	 */
1223 	if (release)
1224 		auclnt_release_dev(d);
1225 	return (0);
1226 }
1227 
1228 static int
1229 audioinfo_walker(audio_engine_t *e, void *a)
1230 {
1231 	oss_audioinfo *si = a;
1232 	int fmt, nchan, rate, cap;
1233 
1234 	fmt = auclnt_engine_get_format(e);
1235 	nchan = auclnt_engine_get_channels(e);
1236 	rate = auclnt_engine_get_rate(e);
1237 	cap = auclnt_engine_get_capab(e);
1238 
1239 	for (int i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
1240 		if (fmt == oss_formats[i].fmt) {
1241 			if (cap & AUDIO_CLIENT_CAP_PLAY) {
1242 				si->oformats |= oss_formats[i].oss;
1243 			}
1244 			if (cap & AUDIO_CLIENT_CAP_RECORD) {
1245 				si->iformats |= oss_formats[i].oss;
1246 			}
1247 			break;
1248 		}
1249 	}
1250 	si->max_channels = max(nchan, si->max_channels);
1251 	si->max_rate = max(rate, si->max_rate);
1252 
1253 	return (AUDIO_WALK_CONTINUE);
1254 }
1255 
1256 static int
1257 sndctl_audioinfo(audio_client_t *c, oss_audioinfo *si)
1258 {
1259 	audio_dev_t		*d;
1260 	const char		*name;
1261 	int			n;
1262 	boolean_t		release;
1263 	unsigned		cap;
1264 
1265 	if ((n = si->dev) == -1) {
1266 		release = B_FALSE;
1267 		d = auclnt_get_dev(c);
1268 		n = auclnt_get_dev_index(d);
1269 	} else {
1270 		release = B_TRUE;
1271 		n++;	/* skip pseudo device */
1272 		d = auclnt_hold_dev_by_index(n);
1273 	}
1274 
1275 	bzero(si, sizeof (*si));
1276 	si->dev = n - 1;
1277 
1278 	if (d == NULL) {
1279 		/* if device not present, forge a false entry */
1280 		si->card_number = n;
1281 		si->mixer_dev = n - 1;
1282 		si->legacy_device = -1;
1283 		si->enabled = 0;
1284 		(void) snprintf(si->name, sizeof (si->name), "<removed>");
1285 		return (0);
1286 	}
1287 
1288 	name = auclnt_get_dev_name(d);
1289 	(void) snprintf(si->name, sizeof (si->name), "%s", name);
1290 
1291 	si->legacy_device = auclnt_get_dev_number(d);
1292 	si->caps = 0;
1293 
1294 	auclnt_dev_walk_engines(d, audioinfo_walker, si);
1295 
1296 	cap = auclnt_get_dev_capab(d);
1297 
1298 	if (cap	& AUDIO_CLIENT_CAP_DUPLEX) {
1299 		si->caps |= PCM_CAP_DUPLEX;
1300 	}
1301 	if (cap & AUDIO_CLIENT_CAP_PLAY) {
1302 		si->caps |= PCM_CAP_OUTPUT;
1303 	}
1304 	if (cap & AUDIO_CLIENT_CAP_RECORD) {
1305 		si->caps |= PCM_CAP_INPUT;
1306 	}
1307 
1308 	if (si->caps != 0) {
1309 		/* AC3: PCM_CAP_MULTI would be wrong for an AC3 only device */
1310 		si->caps |= PCM_CAP_BATCH | PCM_CAP_TRIGGER | PCM_CAP_MULTI;
1311 		/* MMAP: we add PCM_CAP_MMAP when we we support it */
1312 		si->enabled = 1;
1313 		si->rate_source = si->dev;
1314 
1315 		/* we can convert PCM formats */
1316 		if ((si->iformats | si->oformats) &
1317 		    AUDIO_FORMAT_PCM) {
1318 			si->min_channels = min(2, si->max_channels);
1319 			si->min_rate = min(5000, si->max_rate);
1320 			si->caps |= PCM_CAP_FREERATE;
1321 		}
1322 		(void) snprintf(si->devnode, sizeof (si->devnode),
1323 		    "/dev/sound/%s:%ddsp",
1324 		    auclnt_get_dev_driver(d), auclnt_get_dev_instance(d));
1325 	} else {
1326 		si->enabled = 0;	/* stops apps from using us directly */
1327 		si->caps = PCM_CAP_VIRTUAL;
1328 		(void) snprintf(si->devnode, sizeof (si->devnode),
1329 		    "/dev/sndstat");
1330 	}
1331 
1332 	si->pid = -1;
1333 	(void) snprintf(si->handle, sizeof (si->handle), "%s", name);
1334 	(void) snprintf(si->label, sizeof (si->label), "%s", name);
1335 	si->latency = -1;
1336 	si->card_number = n;
1337 	si->mixer_dev = n - 1;
1338 
1339 	if (release)
1340 		auclnt_release_dev(d);
1341 
1342 	return (0);
1343 }
1344 
1345 static int
1346 sound_mixer_info(audio_client_t *c, mixer_info *mi)
1347 {
1348 	audio_dev_t	*d;
1349 	ossdev_t	*odev;
1350 	ossclient_t	*sc;
1351 	const char	*name;
1352 
1353 	sc = auclnt_get_private(c);
1354 	odev = sc->o_ossdev;
1355 
1356 	d = auclnt_get_dev(c);
1357 
1358 	name = auclnt_get_dev_name(d);
1359 	(void) snprintf(mi->id, sizeof (mi->id), "%s", name);
1360 	(void) snprintf(mi->name, sizeof (mi->name), "%s", name);
1361 	(void) snprintf(mi->handle, sizeof (mi->handle), "%s", name);
1362 	mi->modify_counter = odev->d_modify_cnt;
1363 	mi->card_number = auclnt_get_dev_index(d);
1364 	mi->port_number = 0;
1365 	return (0);
1366 }
1367 
1368 static int
1369 sound_mixer_read_devmask(audio_client_t *c, int *devmask)
1370 {
1371 	_NOTE(ARGUNUSED(c));
1372 	*devmask = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_IGAIN;
1373 	return (0);
1374 }
1375 
1376 static int
1377 sound_mixer_read_recmask(audio_client_t *c, int *recmask)
1378 {
1379 	_NOTE(ARGUNUSED(c));
1380 	*recmask = 0;
1381 	return (0);
1382 }
1383 
1384 static int
1385 sound_mixer_read_recsrc(audio_client_t *c, int *recsrc)
1386 {
1387 	_NOTE(ARGUNUSED(c));
1388 	*recsrc = 0;
1389 	return (0);
1390 }
1391 
1392 static int
1393 sound_mixer_read_caps(audio_client_t *c, int *caps)
1394 {
1395 	_NOTE(ARGUNUSED(c));
1396 	/* single recording source... sort of */
1397 	*caps = SOUND_CAP_EXCL_INPUT;
1398 	return (0);
1399 }
1400 
1401 static int
1402 sndctl_mixerinfo(audio_client_t *c, oss_mixerinfo *mi)
1403 {
1404 	audio_dev_t		*d;
1405 	ossdev_t 		*odev;
1406 	const char		*name;
1407 	int			n;
1408 	boolean_t		release = B_FALSE;
1409 
1410 	if ((n = mi->dev) == -1) {
1411 		release = B_FALSE;
1412 		d = auclnt_get_dev(c);
1413 		n = auclnt_get_dev_index(d);
1414 	} else {
1415 		release = B_TRUE;
1416 		n++;
1417 		d = auclnt_hold_dev_by_index(n);
1418 	}
1419 
1420 	bzero(mi, sizeof (*mi));
1421 	mi->dev = n - 1;
1422 
1423 	if (d == NULL) {
1424 		mi->card_number = n;
1425 		mi->enabled = 0;
1426 		mi->legacy_device = -1;
1427 		(void) snprintf(mi->name, sizeof (mi->name), "<removed>");
1428 		(void) snprintf(mi->id, sizeof (mi->id), "<removed>");
1429 		return (0);
1430 	}
1431 
1432 	if ((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) {
1433 		if (release)
1434 			auclnt_release_dev(d);
1435 		return (EINVAL);
1436 	}
1437 
1438 	name = auclnt_get_dev_name(d);
1439 	(void) snprintf(mi->name, sizeof (mi->name), "%s", name);
1440 	(void) snprintf(mi->id, sizeof (mi->id), "%s", name);
1441 	(void) snprintf(mi->handle, sizeof (mi->handle), "%s", name);
1442 	mi->modify_counter = odev->d_modify_cnt;
1443 	mi->card_number = auclnt_get_dev_index(d);
1444 	mi->legacy_device = auclnt_get_dev_number(d);
1445 	if (mi->legacy_device >= 0) {
1446 		(void) snprintf(mi->devnode, sizeof (mi->devnode),
1447 		    "/dev/sound/%s:%dmixer",
1448 		    auclnt_get_dev_driver(d), auclnt_get_dev_instance(d));
1449 		mi->enabled = 1;
1450 	} else {
1451 		/* special nodes use generic sndstat node */
1452 		(void) snprintf(mi->devnode, sizeof (mi->devnode),
1453 		    "/dev/sndstat");
1454 		mi->enabled = 0;
1455 	}
1456 	mi->nrext = odev->d_nctrl;
1457 
1458 	if (release)
1459 		auclnt_release_dev(d);
1460 
1461 	return (0);
1462 }
1463 
1464 static int
1465 sndctl_dsp_getblksize(audio_client_t *c, int *fragsz)
1466 {
1467 	int	oflag = auclnt_get_oflag(c);
1468 
1469 	if (oflag & FWRITE)
1470 		*fragsz  = auclnt_get_fragsz(auclnt_output_stream(c));
1471 	else if (oflag & FREAD)
1472 		*fragsz  = auclnt_get_fragsz(auclnt_input_stream(c));
1473 
1474 	return (0);
1475 }
1476 
1477 static int
1478 sndctl_dsp_getospace(audio_client_t *c, audio_buf_info *bi)
1479 {
1480 	audio_stream_t	*sp;
1481 	unsigned	n;
1482 
1483 	if ((auclnt_get_oflag(c) & FWRITE) == 0) {
1484 		return (EACCES);
1485 	}
1486 
1487 	sp = auclnt_output_stream(c);
1488 	n = auclnt_get_nframes(sp) - auclnt_get_count(sp);
1489 
1490 	bi->fragsize  = auclnt_get_fragsz(sp);
1491 	bi->fragstotal = auclnt_get_nfrags(sp);
1492 	bi->bytes = (n * auclnt_get_framesz(sp));
1493 	bi->fragments = bi->bytes / bi->fragsize;
1494 
1495 	return (0);
1496 }
1497 
1498 static int
1499 sndctl_dsp_getispace(audio_client_t *c, audio_buf_info *bi)
1500 {
1501 	audio_stream_t	*sp;
1502 	unsigned	n;
1503 
1504 	if ((auclnt_get_oflag(c) & FREAD) == 0) {
1505 		return (EACCES);
1506 	}
1507 
1508 	sp = auclnt_input_stream(c);
1509 	n = auclnt_get_count(sp);
1510 
1511 	bi->fragsize  = auclnt_get_fragsz(sp);
1512 	bi->fragstotal = auclnt_get_nfrags(sp);
1513 	bi->bytes = (n * auclnt_get_framesz(sp));
1514 	bi->fragments = bi->bytes / bi->fragsize;
1515 
1516 	return (0);
1517 }
1518 
1519 static int
1520 sndctl_dsp_getodelay(audio_client_t *c, int *bytes)
1521 {
1522 	unsigned	framesz;
1523 	unsigned	slen, flen;
1524 
1525 	if (auclnt_get_oflag(c) & FWRITE) {
1526 		audio_stream_t	*sp = auclnt_output_stream(c);
1527 		framesz = auclnt_get_framesz(sp);
1528 		auclnt_get_output_qlen(c, &slen, &flen);
1529 		*bytes = (slen + flen) * framesz;
1530 	} else {
1531 		*bytes = 0;
1532 	}
1533 	return (0);
1534 }
1535 
1536 static int
1537 sndctl_dsp_current_iptr(audio_client_t *c, oss_count_t *count)
1538 {
1539 	if (auclnt_get_oflag(c) & FREAD) {
1540 		count->samples = auclnt_get_samples(auclnt_input_stream(c));
1541 		count->fifo_samples = 0;	/* not quite accurate */
1542 	} else {
1543 		count->samples = 0;
1544 		count->fifo_samples = 0;
1545 	}
1546 	return (0);
1547 }
1548 
1549 static int
1550 sndctl_dsp_current_optr(audio_client_t *c, oss_count_t *count)
1551 {
1552 	unsigned samples, fifo;
1553 
1554 	if (auclnt_get_oflag(c) & FWRITE) {
1555 		auclnt_get_output_qlen(c, &samples, &fifo);
1556 		count->samples = samples;
1557 		count->fifo_samples = fifo;
1558 	} else {
1559 		count->samples = 0;
1560 		count->fifo_samples = 0;
1561 	}
1562 	return (0);
1563 }
1564 
1565 static int
1566 sndctl_dsp_getoptr(audio_client_t *c, count_info *ci)
1567 {
1568 	audio_stream_t	*sp;
1569 	unsigned	framesz;
1570 	unsigned	fragsz;
1571 
1572 	bzero(ci, sizeof (*ci));
1573 	if ((auclnt_get_oflag(c) & FWRITE) == 0) {
1574 		return (0);
1575 	}
1576 	sp = auclnt_output_stream(c);
1577 	framesz = auclnt_get_framesz(sp);
1578 	fragsz = auclnt_get_fragsz(sp);
1579 	ci->blocks = auclnt_get_samples(sp) * framesz / fragsz;
1580 	auclnt_set_samples(sp, 0);
1581 	ci->bytes = auclnt_get_tail(sp) * framesz;
1582 	ci->ptr = auclnt_get_tidx(sp) * framesz;
1583 	return (0);
1584 }
1585 
1586 static int
1587 sndctl_dsp_getiptr(audio_client_t *c, count_info *ci)
1588 {
1589 	audio_stream_t	*sp;
1590 	unsigned	framesz;
1591 	unsigned	fragsz;
1592 
1593 	bzero(ci, sizeof (*ci));
1594 	if ((auclnt_get_oflag(c) & FREAD) == 0) {
1595 		return (0);
1596 	}
1597 	sp = auclnt_input_stream(c);
1598 	framesz = auclnt_get_framesz(sp);
1599 	fragsz = auclnt_get_fragsz(sp);
1600 	ci->blocks = auclnt_get_samples(sp) * framesz / fragsz;
1601 	auclnt_set_samples(sp, 0);
1602 	ci->bytes = auclnt_get_head(sp) * framesz;
1603 	ci->ptr = auclnt_get_hidx(sp) * framesz;
1604 	return (0);
1605 }
1606 
1607 static int
1608 sndctl_dsp_geterror(audio_client_t *c, audio_errinfo *bi)
1609 {
1610 	audio_stream_t	*sp;
1611 	unsigned	fragsz;
1612 	/*
1613 	 * Note: The use of this structure is unsafe... different
1614 	 * meanings for error codes are used by different implementations,
1615 	 * according to the spec.  (Even different versions of the same
1616 	 * implementation could have different values.)
1617 	 *
1618 	 * Rather than try to come up with a reliable solution here, we
1619 	 * don't use it.  If you want to report errors, or see the result
1620 	 * of errors, use syslog.
1621 	 */
1622 	bzero(bi, sizeof (*bi));
1623 
1624 	sp = auclnt_output_stream(c);
1625 	fragsz = max(auclnt_get_fragsz(sp), 1);
1626 	bi->play_underruns = (int)((auclnt_get_errors(sp) + (fragsz - 1)) /
1627 	    fragsz);
1628 	auclnt_set_errors(sp, 0);
1629 
1630 	sp = auclnt_input_stream(c);
1631 	fragsz = max(auclnt_get_fragsz(sp), 1);
1632 	bi->rec_overruns = (int)((auclnt_get_errors(sp) + (fragsz - 1)) /
1633 	    fragsz);
1634 	auclnt_set_errors(sp, 0);
1635 
1636 	return (0);
1637 }
1638 
1639 static int
1640 sndctl_sun_send_number(audio_client_t *c, int *num, cred_t *cr)
1641 {
1642 	audio_dev_t	*dev;
1643 	int		rv;
1644 
1645 	if ((rv = drv_priv(cr)) != 0) {
1646 		return (rv);
1647 	}
1648 
1649 	dev = auclnt_get_dev(c);
1650 	auclnt_set_dev_number(dev, *num);
1651 	return (0);
1652 }
1653 
1654 static int
1655 oss_getversion(int *versp)
1656 {
1657 	*versp = OSS_VERSION;
1658 	return (0);
1659 }
1660 
1661 static int
1662 oss_ioctl(audio_client_t *c, int cmd, intptr_t arg, int mode, cred_t *credp,
1663     int *rvalp)
1664 {
1665 	int	sz;
1666 	void	*data;
1667 	int	rv = 0;
1668 
1669 	_NOTE(ARGUNUSED(credp));
1670 
1671 	sz = OSSIOC_GETSZ(cmd);
1672 
1673 	if ((cmd & (OSSIOC_IN | OSSIOC_OUT)) && sz) {
1674 		if ((data = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
1675 			return (ENOMEM);
1676 		}
1677 	} else {
1678 		sz = 0;
1679 	}
1680 
1681 	if (cmd & OSSIOC_IN) {
1682 		if ((rv = ddi_copyin((void *)arg, data, sz, mode)) != 0) {
1683 			goto done;
1684 		}
1685 	}
1686 
1687 	switch (cmd) {
1688 		/*
1689 		 * DSP specific ioctls
1690 		 */
1691 	case SNDCTL_DSP_HALT:
1692 		rv = sndctl_dsp_halt(c);
1693 		break;
1694 
1695 	case SNDCTL_DSP_SYNC:
1696 		rv = sndctl_dsp_sync(c);
1697 		break;
1698 
1699 	case SNDCTL_DSP_SPEED:
1700 		rv = sndctl_dsp_speed(c, (int *)data);
1701 		break;
1702 	case SNDCTL_DSP_SETFMT:
1703 		rv = sndctl_dsp_setfmt(c, (int *)data);
1704 		break;
1705 	case SNDCTL_DSP_GETFMTS:
1706 		rv = sndctl_dsp_getfmts(c, (int *)data);
1707 		break;
1708 	case SNDCTL_DSP_STEREO:
1709 		rv = sndctl_dsp_stereo(c, (int *)data);
1710 		break;
1711 	case SNDCTL_DSP_CHANNELS:
1712 		rv = sndctl_dsp_channels(c, (int *)data);
1713 		break;
1714 	case SNDCTL_DSP_POST:
1715 		rv = sndctl_dsp_post(c);
1716 		break;
1717 	case SNDCTL_DSP_GETCAPS:
1718 		rv = sndctl_dsp_getcaps(c, (int *)data);
1719 		break;
1720 	case SNDCTL_DSP_GETTRIGGER:
1721 		rv = sndctl_dsp_gettrigger(c, (int *)data);
1722 		break;
1723 	case SNDCTL_DSP_SETTRIGGER:
1724 		rv = sndctl_dsp_settrigger(c, (int *)data);
1725 		break;
1726 	case SNDCTL_DSP_GETPLAYVOL:
1727 	case SOUND_MIXER_READ_VOLUME:	/* legacy mixer on dsp */
1728 	case SOUND_MIXER_READ_PCM:	/* legacy mixer on dsp */
1729 	case SOUND_MIXER_READ_OGAIN:	/* legacy mixer on dsp */
1730 		rv = sndctl_dsp_getplayvol(c, (int *)data);
1731 		break;
1732 	case SOUND_MIXER_WRITE_VOLUME:	/* legacy mixer on dsp */
1733 	case SOUND_MIXER_WRITE_PCM:	/* legacy mixer on dsp */
1734 	case SOUND_MIXER_WRITE_OGAIN:	/* legacy mixer on dsp */
1735 		rv = sound_mixer_write_ogain(c, (int *)data);
1736 		break;
1737 	case SNDCTL_DSP_SETPLAYVOL:
1738 		rv = sndctl_dsp_setplayvol(c, (int *)data);
1739 		break;
1740 	case SNDCTL_DSP_READCTL:
1741 		rv = sndctl_dsp_readctl(c, (oss_digital_control *)data);
1742 		break;
1743 	case SNDCTL_DSP_WRITECTL:
1744 		rv = sndctl_dsp_writectl(c, (oss_digital_control *)data);
1745 		break;
1746 	case SNDCTL_DSP_COOKEDMODE:
1747 		rv = sndctl_dsp_cookedmode(c, (int *)data);
1748 		break;
1749 	case SNDCTL_DSP_SILENCE:
1750 		rv = sndctl_dsp_silence(c);
1751 		break;
1752 	case SNDCTL_DSP_SKIP:
1753 		rv = sndctl_dsp_skip(c);
1754 		break;
1755 	case SNDCTL_DSP_HALT_INPUT:
1756 		rv = sndctl_dsp_halt_input(c);
1757 		break;
1758 	case SNDCTL_DSP_HALT_OUTPUT:
1759 		rv = sndctl_dsp_halt_output(c);
1760 		break;
1761 	case SNDCTL_DSP_GET_RECSRC_NAMES:
1762 		rv = sndctl_dsp_get_recsrc_names(c, (oss_mixer_enuminfo *)data);
1763 		break;
1764 	case SNDCTL_DSP_SETFRAGMENT:
1765 		rv = sndctl_dsp_setfragment(c, (int *)data);
1766 		break;
1767 	case SNDCTL_DSP_GET_RECSRC:
1768 		rv = sndctl_dsp_get_recsrc(c, (int *)data);
1769 		break;
1770 	case SNDCTL_DSP_SET_RECSRC:
1771 		rv = sndctl_dsp_set_recsrc(c, (int *)data);
1772 		break;
1773 	case SNDCTL_DSP_GET_PLAYTGT_NAMES:
1774 		rv = sndctl_dsp_get_playtgt_names(c,
1775 		    (oss_mixer_enuminfo *)data);
1776 		break;
1777 	case SNDCTL_DSP_GET_PLAYTGT:
1778 		rv = sndctl_dsp_get_playtgt(c, (int *)data);
1779 		break;
1780 	case SNDCTL_DSP_SET_PLAYTGT:
1781 		rv = sndctl_dsp_set_playtgt(c, (int *)data);
1782 		break;
1783 	case SNDCTL_DSP_GETRECVOL:
1784 	case SOUND_MIXER_READ_RECGAIN:	/* legacy mixer on dsp */
1785 	case SOUND_MIXER_READ_RECLEV:	/* legacy mixer on dsp */
1786 	case SOUND_MIXER_READ_IGAIN:	/* legacy mixer on dsp */
1787 		rv = sndctl_dsp_getrecvol(c, (int *)data);
1788 		break;
1789 	case SOUND_MIXER_WRITE_RECGAIN:	/* legacy mixer on dsp */
1790 	case SOUND_MIXER_WRITE_RECLEV:	/* legacy mixer on dsp */
1791 	case SOUND_MIXER_WRITE_IGAIN:	/* legacy mixer on dsp */
1792 		rv = sound_mixer_write_igain(c, (int *)data);
1793 		break;
1794 	case SNDCTL_DSP_SETRECVOL:
1795 		rv = sndctl_dsp_setrecvol(c, (int *)data);
1796 		break;
1797 	case SNDCTL_DSP_SUBDIVIDE:	/* Ignored */
1798 	case SNDCTL_DSP_SETDUPLEX:	/* Ignored */
1799 	case SNDCTL_DSP_LOW_WATER:	/* Ignored */
1800 	case SNDCTL_DSP_PROFILE:	/* Ignored */
1801 		rv = 0;
1802 		break;
1803 	case SNDCTL_DSP_POLICY:
1804 		rv = sndctl_dsp_policy(c, (int *)data);
1805 		break;
1806 	case SNDCTL_DSP_GETBLKSIZE:
1807 		rv = sndctl_dsp_getblksize(c, (int *)data);
1808 		break;
1809 	case SNDCTL_DSP_GETOSPACE:
1810 		rv = sndctl_dsp_getospace(c, (audio_buf_info *)data);
1811 		break;
1812 	case SNDCTL_DSP_GETISPACE:
1813 		rv = sndctl_dsp_getispace(c, (audio_buf_info *)data);
1814 		break;
1815 	case SNDCTL_DSP_GETODELAY:
1816 		rv = sndctl_dsp_getodelay(c, (int *)data);
1817 		break;
1818 	case SNDCTL_DSP_GETOPTR:
1819 		rv = sndctl_dsp_getoptr(c, (count_info *)data);
1820 		break;
1821 	case SNDCTL_DSP_GETIPTR:
1822 		rv = sndctl_dsp_getiptr(c, (count_info *)data);
1823 		break;
1824 	case SNDCTL_DSP_GETERROR:
1825 		rv = sndctl_dsp_geterror(c, (audio_errinfo *)data);
1826 		break;
1827 	case SNDCTL_DSP_CURRENT_IPTR:
1828 		rv = sndctl_dsp_current_iptr(c, (oss_count_t *)data);
1829 		break;
1830 	case SNDCTL_DSP_CURRENT_OPTR:
1831 		rv = sndctl_dsp_current_optr(c, (oss_count_t *)data);
1832 		break;
1833 
1834 		/*
1835 		 * Shared ioctls with /dev/mixer.
1836 		 */
1837 	case OSS_GETVERSION:
1838 		rv = oss_getversion((int *)data);
1839 		break;
1840 	case SNDCTL_CARDINFO:
1841 		rv = sndctl_cardinfo(c, (oss_card_info *)data);
1842 		break;
1843 	case SNDCTL_ENGINEINFO:
1844 	case SNDCTL_AUDIOINFO:
1845 	case SNDCTL_AUDIOINFO_EX:
1846 		rv = sndctl_audioinfo(c, (oss_audioinfo *)data);
1847 		break;
1848 	case SNDCTL_SYSINFO:
1849 		rv = sndctl_sysinfo((oss_sysinfo *)data);
1850 		break;
1851 	case SNDCTL_MIXERINFO:
1852 		rv = sndctl_mixerinfo(c, (oss_mixerinfo *)data);
1853 		break;
1854 	case SOUND_MIXER_INFO:
1855 		rv = sound_mixer_info(c, (mixer_info *)data);
1856 		break;
1857 
1858 		/*
1859 		 * These are mixer ioctls that are virtualized for the DSP
1860 		 * device.  They are accessible via either /dev/mixer or
1861 		 * /dev/dsp.
1862 		 */
1863 	case SOUND_MIXER_READ_RECSRC:
1864 	case SOUND_MIXER_WRITE_RECSRC:
1865 		rv = sound_mixer_read_recsrc(c, (int *)data);
1866 		break;
1867 
1868 	case SOUND_MIXER_READ_DEVMASK:
1869 	case SOUND_MIXER_READ_STEREODEVS:
1870 		rv = sound_mixer_read_devmask(c, (int *)data);
1871 		break;
1872 
1873 	case SOUND_MIXER_READ_RECMASK:
1874 		rv = sound_mixer_read_recmask(c, (int *)data);
1875 		break;
1876 
1877 	case SOUND_MIXER_READ_CAPS:
1878 		rv = sound_mixer_read_caps(c, (int *)data);
1879 		break;
1880 
1881 		/*
1882 		 * Ioctls we have chosen not to support for now.  Some
1883 		 * of these are of legacy interest only.
1884 		 */
1885 	case SNDCTL_SETSONG:
1886 	case SNDCTL_GETSONG:
1887 	case SNDCTL_DSP_SYNCGROUP:
1888 	case SNDCTL_DSP_SYNCSTART:
1889 	case SNDCTL_DSP_GET_CHNORDER:
1890 	case SNDCTL_DSP_SET_CHNORDER:
1891 	case SNDCTL_DSP_GETIPEAKS:
1892 	case SNDCTL_DSP_GETOPEAKS:
1893 	case SNDCTL_DSP_GETCHANNELMASK:
1894 	case SNDCTL_DSP_BIND_CHANNEL:
1895 	case SNDCTL_DSP_SETSYNCRO:
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 		/*
2283 		 * Common ioctls shared with DSP
2284 		 */
2285 	case OSS_GETVERSION:
2286 		rv = oss_getversion((int *)data);
2287 		break;
2288 
2289 	case SNDCTL_CARDINFO:
2290 		rv = sndctl_cardinfo(c, (oss_card_info *)data);
2291 		break;
2292 
2293 	case SNDCTL_ENGINEINFO:
2294 	case SNDCTL_AUDIOINFO:
2295 	case SNDCTL_AUDIOINFO_EX:
2296 		rv = sndctl_audioinfo(c, (oss_audioinfo *)data);
2297 		break;
2298 
2299 	case SNDCTL_SYSINFO:
2300 		rv = sndctl_sysinfo((oss_sysinfo *)data);
2301 		break;
2302 
2303 	case SNDCTL_MIXERINFO:
2304 		rv = sndctl_mixerinfo(c, (oss_mixerinfo *)data);
2305 		break;
2306 
2307 	case SOUND_MIXER_INFO:
2308 		rv = sound_mixer_info(c, (mixer_info *)data);
2309 		break;
2310 
2311 	case SNDCTL_MIX_DESCRIPTION:	/* NOT SUPPORTED: tooltip */
2312 		rv = EIO;	/* OSS returns EIO for this one */
2313 		break;
2314 
2315 		/*
2316 		 * Special implementation-private ioctls.
2317 		 */
2318 	case SNDCTL_SUN_SEND_NUMBER:
2319 		rv = sndctl_sun_send_number(c, (int *)data, credp);
2320 		break;
2321 
2322 		/*
2323 		 * Legacy ioctls we don't support.
2324 		 */
2325 	case SOUND_MIXER_WRITE_MONGAIN:
2326 	case SOUND_MIXER_READ_MONGAIN:
2327 	case SOUND_MIXER_READ_BASS:
2328 	case SOUND_MIXER_READ_TREBLE:
2329 	case SOUND_MIXER_READ_SPEAKER:
2330 	case SOUND_MIXER_READ_LINE:
2331 	case SOUND_MIXER_READ_MIC:
2332 	case SOUND_MIXER_READ_CD:
2333 	case SOUND_MIXER_READ_IMIX:
2334 	case SOUND_MIXER_READ_ALTPCM:
2335 	case SOUND_MIXER_READ_SYNTH:
2336 	case SOUND_MIXER_READ_LINE1:
2337 	case SOUND_MIXER_READ_LINE2:
2338 	case SOUND_MIXER_READ_LINE3:
2339 	case SOUND_MIXER_WRITE_BASS:
2340 	case SOUND_MIXER_WRITE_TREBLE:
2341 	case SOUND_MIXER_WRITE_SPEAKER:
2342 	case SOUND_MIXER_WRITE_LINE:
2343 	case SOUND_MIXER_WRITE_MIC:
2344 	case SOUND_MIXER_WRITE_CD:
2345 	case SOUND_MIXER_WRITE_IMIX:
2346 	case SOUND_MIXER_WRITE_ALTPCM:
2347 	case SOUND_MIXER_WRITE_SYNTH:
2348 	case SOUND_MIXER_WRITE_LINE1:
2349 	case SOUND_MIXER_WRITE_LINE2:
2350 	case SOUND_MIXER_WRITE_LINE3:
2351 		/*
2352 		 * Additional ioctls we *could* support, but don't.
2353 		 */
2354 	case SNDCTL_SETSONG:
2355 	case SNDCTL_SETLABEL:
2356 	case SNDCTL_GETSONG:
2357 	case SNDCTL_GETLABEL:
2358 	case SNDCTL_MIDIINFO:
2359 	case SNDCTL_SETNAME:
2360 	default:
2361 		rv = EINVAL;
2362 		break;
2363 	}
2364 
2365 	if ((rv == 0) && (cmd & OSSIOC_OUT)) {
2366 		rv = ddi_copyout(data, (void *)arg, sz, mode);
2367 	}
2368 	if (rv == 0) {
2369 		*rvalp = 0;
2370 	}
2371 
2372 done:
2373 	if (sz) {
2374 		kmem_free(data, sz);
2375 	}
2376 	return (rv);
2377 }
2378 
2379 static void *
2380 oss_dev_init(audio_dev_t *dev)
2381 {
2382 	ossdev_t	*odev;
2383 
2384 	odev = kmem_zalloc(sizeof (*odev), KM_SLEEP);
2385 	odev->d_dev = dev;
2386 
2387 	mutex_init(&odev->d_mx, NULL, MUTEX_DRIVER, NULL);
2388 	cv_init(&odev->d_cv, NULL, CV_DRIVER, NULL);
2389 	oss_alloc_controls(odev);
2390 
2391 	return (odev);
2392 }
2393 
2394 static void
2395 oss_dev_fini(void *arg)
2396 {
2397 	ossdev_t	*odev = arg;
2398 
2399 	if (odev != NULL) {
2400 		oss_free_controls(odev);
2401 		mutex_destroy(&odev->d_mx);
2402 		cv_destroy(&odev->d_cv);
2403 		kmem_free(odev, sizeof (*odev));
2404 	}
2405 }
2406 
2407 static void
2408 sndstat_printf(ossclient_t *oc, const char *fmt, ...)
2409 {
2410 	va_list	va;
2411 
2412 	va_start(va, fmt);
2413 	(void) vsnprintf(oc->o_ss_buf + oc->o_ss_len,
2414 	    oc->o_ss_sz - oc->o_ss_len, fmt, va);
2415 	va_end(va);
2416 	oc->o_ss_len = strlen(oc->o_ss_buf);
2417 }
2418 
2419 static int
2420 sndstat_dev_walker(audio_dev_t *d, void *arg)
2421 {
2422 	ossclient_t	*oc = arg;
2423 	const char	*capstr;
2424 	unsigned	cap;
2425 
2426 	cap = auclnt_get_dev_capab(d);
2427 
2428 	if (cap	& AUDIO_CLIENT_CAP_DUPLEX) {
2429 		capstr = "DUPLEX";
2430 	} else if ((cap & AUDIO_CLIENT_CAP_PLAY) &&
2431 	    (cap & AUDIO_CLIENT_CAP_RECORD)) {
2432 		capstr = "INPUT,OUTPUT";
2433 	} else if (cap & AUDIO_CLIENT_CAP_PLAY) {
2434 		capstr = "OUTPUT";
2435 	} else if (cap & AUDIO_CLIENT_CAP_RECORD) {
2436 		capstr = "INPUT";
2437 	} else {
2438 		capstr = NULL;
2439 	}
2440 
2441 	if (capstr == NULL)
2442 		return (AUDIO_WALK_CONTINUE);
2443 
2444 	sndstat_printf(oc, "%d: %s %s, %s (%s)\n",
2445 	    auclnt_get_dev_number(d), auclnt_get_dev_name(d),
2446 	    auclnt_get_dev_description(d), auclnt_get_dev_version(d), capstr);
2447 
2448 	return (AUDIO_WALK_CONTINUE);
2449 }
2450 
2451 static int
2452 sndstat_mixer_walker(audio_dev_t *d, void *arg)
2453 {
2454 	ossclient_t	*oc = arg;
2455 	unsigned	cap;
2456 	void		*iter;
2457 	const char	*info;
2458 
2459 	cap = auclnt_get_dev_capab(d);
2460 
2461 	if ((cap & (AUDIO_CLIENT_CAP_PLAY|AUDIO_CLIENT_CAP_RECORD)) == 0)
2462 		return (AUDIO_WALK_CONTINUE);
2463 
2464 	sndstat_printf(oc, "%d: %s %s, %s\n",
2465 	    auclnt_get_dev_number(d), auclnt_get_dev_name(d),
2466 	    auclnt_get_dev_description(d), auclnt_get_dev_version(d));
2467 	iter = NULL;
2468 	while ((info = auclnt_get_dev_hw_info(d, &iter)) != NULL) {
2469 		sndstat_printf(oc, "\t%s\n", info);
2470 	}
2471 	return (AUDIO_WALK_CONTINUE);
2472 }
2473 
2474 static int
2475 ossmix_write(audio_client_t *c, struct uio *uio, cred_t *cr)
2476 {
2477 	/* write on sndstat is a no-op */
2478 	_NOTE(ARGUNUSED(c));
2479 	_NOTE(ARGUNUSED(uio));
2480 	_NOTE(ARGUNUSED(cr));
2481 
2482 	return (0);
2483 }
2484 
2485 static int
2486 ossmix_read(audio_client_t *c, struct uio *uio, cred_t *cr)
2487 {
2488 	ossclient_t	*oc;
2489 	unsigned	n;
2490 	int		rv;
2491 
2492 	_NOTE(ARGUNUSED(cr));
2493 
2494 	if (uio->uio_resid == 0) {
2495 		return (0);
2496 	}
2497 
2498 	oc = auclnt_get_private(c);
2499 
2500 	mutex_enter(&oc->o_ss_lock);
2501 
2502 	if (oc->o_ss_off == 0) {
2503 
2504 		sndstat_printf(oc, "SunOS Audio Framework\n");
2505 
2506 		sndstat_printf(oc, "\nAudio Devices:\n");
2507 		auclnt_walk_devs_by_number(sndstat_dev_walker, oc);
2508 
2509 		sndstat_printf(oc, "\nMixers:\n");
2510 		auclnt_walk_devs_by_number(sndstat_mixer_walker, oc);
2511 	}
2512 
2513 	/*
2514 	 * For simplicity's sake, we implement a non-seekable device.  We could
2515 	 * support seekability, but offsets would be rather meaningless between
2516 	 * changes.
2517 	 */
2518 	n = min(uio->uio_resid, (oc->o_ss_len - oc->o_ss_off));
2519 
2520 	rv = uiomove(oc->o_ss_buf + oc->o_ss_off, n, UIO_READ, uio);
2521 	if (rv != 0) {
2522 		n = 0;
2523 	}
2524 	oc->o_ss_off += n;
2525 
2526 	if (n == 0) {
2527 		/*
2528 		 * end-of-file reached... clear the sndstat buffer so that
2529 		 * subsequent reads will get the latest data.
2530 		 */
2531 		oc->o_ss_off = oc->o_ss_len = 0;
2532 	}
2533 	mutex_exit(&oc->o_ss_lock);
2534 	return (rv);
2535 }
2536 
2537 int
2538 oss_read(audio_client_t *c, struct uio *uio, cred_t *cr)
2539 {
2540 	_NOTE(ARGUNUSED(cr));
2541 
2542 	auclnt_clear_paused(auclnt_input_stream(c));
2543 
2544 	return (auclnt_read(c, uio));
2545 }
2546 
2547 int
2548 oss_write(audio_client_t *c, struct uio *uio, cred_t *cr)
2549 {
2550 	_NOTE(ARGUNUSED(cr));
2551 
2552 	auclnt_clear_paused(auclnt_output_stream(c));
2553 
2554 	return (auclnt_write(c, uio));
2555 }
2556 
2557 int
2558 oss_chpoll(audio_client_t *c, short events, int anyyet, short *reventsp,
2559     struct pollhead **phpp)
2560 {
2561 	return (auclnt_chpoll(c, events, anyyet, reventsp, phpp));
2562 }
2563 
2564 static struct audio_client_ops oss_ops = {
2565 	"sound,dsp",
2566 	oss_dev_init,
2567 	oss_dev_fini,
2568 	oss_open,
2569 	oss_close,
2570 	oss_read,
2571 	oss_write,
2572 	oss_ioctl,
2573 	oss_chpoll,
2574 	NULL,		/* mmap */
2575 	oss_input,
2576 	oss_output,
2577 	NULL,		/* notify */
2578 	NULL,		/* drain */
2579 };
2580 
2581 static struct audio_client_ops ossmix_ops = {
2582 	"sound,mixer",
2583 	NULL,
2584 	NULL,
2585 	ossmix_open,
2586 	ossmix_close,
2587 	ossmix_read,
2588 	ossmix_write,
2589 	ossmix_ioctl,
2590 	NULL,	/* chpoll */
2591 	NULL,   /* mmap */
2592 	NULL,	/* input */
2593 	NULL,   /* output */
2594 	oss_notify,
2595 	NULL,	/* drain */
2596 };
2597 
2598 /* nearly the same as ossxmix; different minor name helps devfsadm */
2599 static struct audio_client_ops sndstat_ops = {
2600 	"sound,sndstat",
2601 	NULL,	/* dev_init */
2602 	NULL,	/* dev_fini */
2603 	ossmix_open,
2604 	ossmix_close,
2605 	ossmix_read,
2606 	ossmix_write,
2607 	ossmix_ioctl,
2608 	NULL,	/* chpoll */
2609 	NULL,	/* mmap */
2610 	NULL,	/* input */
2611 	NULL,	/* output */
2612 	NULL,	/* notify */
2613 	NULL,	/* drain */
2614 };
2615 
2616 void
2617 auimpl_oss_init(void)
2618 {
2619 	auclnt_register_ops(AUDIO_MINOR_DSP, &oss_ops);
2620 	auclnt_register_ops(AUDIO_MINOR_MIXER, &ossmix_ops);
2621 	auclnt_register_ops(AUDIO_MINOR_SNDSTAT, &sndstat_ops);
2622 }
2623