xref: /illumos-gate/usr/src/uts/common/io/audio/impl/audio_oss.c (revision b31b5de1357c915fe7dab4d9646d9d84f9fe69bc)
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 	}
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 	}
520 
521 	return (0);
522 
523 failed:
524 	auclnt_close(c);
525 	return (rv);
526 }
527 
528 static void
529 oss_close(audio_client_t *c)
530 {
531 	ossclient_t	*sc;
532 
533 	sc = auclnt_get_private(c);
534 
535 	if (ddi_can_receive_sig() || (ddi_get_pid() == 0)) {
536 		(void) auclnt_drain(c);
537 	}
538 
539 	kmem_free(sc, sizeof (*sc));
540 
541 	auclnt_close(c);
542 }
543 
544 /*
545  * This is used to generate an array of names for an enumeration
546  */
547 static ushort_t
548 oss_set_enum(oss_mixer_enuminfo *ei, ushort_t nxt, const char *name)
549 {
550 	uint32_t	n;
551 
552 	/* Get current entry to fill in */
553 	n = ei->nvalues;
554 	(void) snprintf(&ei->strings[nxt], ((sizeof (ei->strings) - nxt) - 1),
555 	    "%s", name);
556 	ei->strindex[n] = nxt;
557 
558 	/* Adjust everything for next entry */
559 	nxt += strnlen(name, ((sizeof (ei->strings) - nxt) - 1));
560 	ei->strings[nxt++] = '\0';
561 
562 	ei->nvalues++;
563 	return (nxt);
564 }
565 
566 /*
567  * The following two functions are used to count the number of devices
568  * in under the boomer framework.
569  *
570  * We actually report the highest "index", and then if an audio device
571  * is not found, we report a bogus removed device for it in the actual
572  * ioctls.  This goofiness is required to make the OSS API happy.
573  */
574 int
575 oss_dev_walker(audio_dev_t *d, void *arg)
576 {
577 	int		*pcnt = arg;
578 	int		cnt;
579 	int		index;
580 
581 	cnt = *pcnt;
582 	index = auclnt_get_dev_index(d);
583 	if ((index + 1) > cnt) {
584 		cnt = index + 1;
585 		*pcnt = cnt;
586 	}
587 
588 	return (AUDIO_WALK_CONTINUE);
589 }
590 
591 static int
592 oss_cnt_devs(void)
593 {
594 	int cnt = 0;
595 
596 	auclnt_walk_devs(oss_dev_walker, &cnt);
597 	return (cnt);
598 }
599 
600 static int
601 sndctl_dsp_speed(audio_client_t *c, int *ratep)
602 {
603 	int		rv;
604 	int		rate;
605 	int		oflag;
606 
607 	rate = *ratep;
608 
609 	oflag = auclnt_get_oflag(c);
610 	if (oflag & FREAD) {
611 		if ((rv = auclnt_set_rate(auclnt_input_stream(c), rate)) != 0)
612 			return (rv);
613 	}
614 
615 	if (oflag & FWRITE) {
616 		if ((rv = auclnt_set_rate(auclnt_output_stream(c), rate)) != 0)
617 			return (rv);
618 	}
619 
620 	return (0);
621 }
622 
623 static int
624 sndctl_dsp_setfmt(audio_client_t *c, int *fmtp)
625 {
626 	int		rv;
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 ENOTSUP */
643 			return (ENOTSUP);
644 		}
645 
646 		if (oflag & FWRITE) {
647 			rv = auclnt_set_format(auclnt_output_stream(c), fmt);
648 			if (rv != 0)
649 				return (rv);
650 		}
651 
652 		if (oflag & FREAD) {
653 			rv = auclnt_set_format(auclnt_input_stream(c), fmt);
654 			if (rv != 0)
655 				return (rv);
656 		}
657 	}
658 
659 	if (oflag & FWRITE) {
660 		fmt = auclnt_get_format(auclnt_output_stream(c));
661 	} else if (oflag & FREAD) {
662 		fmt = auclnt_get_format(auclnt_input_stream(c));
663 	}
664 
665 	/* convert back to OSS */
666 	*(int *)fmtp = AFMT_QUERY;
667 	for (i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
668 		if (oss_formats[i].fmt == fmt) {
669 			*(int *)fmtp = oss_formats[i].oss;
670 		}
671 	}
672 
673 	return (0);
674 }
675 
676 static int
677 sndctl_dsp_getfmts(audio_client_t *c, int *fmtsp)
678 {
679 	_NOTE(ARGUNUSED(c));
680 
681 	/*
682 	 * For now, we support all the standard ones.  Later we might
683 	 * add in conditional support for AC3.
684 	 */
685 	*fmtsp = (AFMT_MU_LAW | AFMT_A_LAW |
686 	    AFMT_U8 | AFMT_S8 |
687 	    AFMT_S16_LE |AFMT_S16_BE |
688 	    AFMT_S24_LE | AFMT_S24_BE |
689 	    AFMT_S32_LE | AFMT_S32_BE |
690 	    AFMT_S24_PACKED);
691 
692 	return (0);
693 }
694 
695 static int
696 sndctl_dsp_channels(audio_client_t *c, int *chanp)
697 {
698 	int		rv;
699 	int		nchan;
700 	int		oflag;
701 
702 	oflag = auclnt_get_oflag(c);
703 
704 	nchan = *chanp;
705 	if (nchan != 0) {
706 		if (oflag & FWRITE) {
707 			rv = auclnt_set_channels(auclnt_output_stream(c),
708 			    nchan);
709 			if (rv != 0)
710 				return (rv);
711 		}
712 
713 		if (oflag & FREAD) {
714 			rv = auclnt_set_channels(auclnt_input_stream(c), nchan);
715 			if (rv != 0)
716 				return (rv);
717 		}
718 	}
719 
720 	if (oflag & FWRITE) {
721 		nchan = auclnt_get_channels(auclnt_output_stream(c));
722 	} else if (oflag & FREAD) {
723 		nchan = auclnt_get_channels(auclnt_input_stream(c));
724 	}
725 	*chanp = nchan;
726 	return (0);
727 }
728 
729 static int
730 sndctl_dsp_stereo(audio_client_t *c, int *onoff)
731 {
732 	int	nchan;
733 
734 	switch (*onoff) {
735 	case 0:
736 		nchan = 1;
737 		break;
738 	case 1:
739 		nchan = 2;
740 		break;
741 	default:
742 		return (EINVAL);
743 	}
744 
745 	return (sndctl_dsp_channels(c, &nchan));
746 }
747 
748 static int
749 sndctl_dsp_post(audio_client_t *c)
750 {
751 	if (auclnt_get_oflag(c) & FWRITE) {
752 		audio_stream_t	*sp = auclnt_output_stream(c);
753 		auclnt_flush(sp);
754 		auclnt_clear_paused(sp);
755 	}
756 	return (0);
757 }
758 
759 static int
760 sndctl_dsp_getcaps(audio_client_t *c, int *capsp)
761 {
762 	int		ncaps;
763 	int		osscaps = 0;
764 
765 	ncaps = auclnt_get_dev_capab(auclnt_get_dev(c));
766 
767 	if (ncaps & AUDIO_CLIENT_CAP_PLAY)
768 		osscaps |= PCM_CAP_OUTPUT;
769 	if (ncaps & AUDIO_CLIENT_CAP_RECORD)
770 		osscaps |= PCM_CAP_INPUT;
771 	if (ncaps & AUDIO_CLIENT_CAP_DUPLEX)
772 		osscaps |= PCM_CAP_DUPLEX;
773 
774 	*capsp = osscaps;
775 	return (0);
776 }
777 
778 static int
779 sndctl_dsp_gettrigger(audio_client_t *c, int *trigp)
780 {
781 	int		triggers = 0;
782 	int		oflag;
783 
784 	oflag = auclnt_get_oflag(c);
785 
786 	if (oflag & FWRITE) {
787 		if (!auclnt_is_paused(auclnt_output_stream(c))) {
788 			triggers |= PCM_ENABLE_OUTPUT;
789 		}
790 	}
791 
792 	if (oflag & FREAD) {
793 		if (!auclnt_is_paused(auclnt_input_stream(c))) {
794 			triggers |= PCM_ENABLE_INPUT;
795 		}
796 	}
797 	*trigp = triggers;
798 
799 	return (0);
800 }
801 
802 static int
803 sndctl_dsp_settrigger(audio_client_t *c, int *trigp)
804 {
805 	int		triggers;
806 	int		oflag;
807 	audio_stream_t	*sp;
808 
809 	oflag = auclnt_get_oflag(c);
810 	triggers = *trigp;
811 
812 	if ((oflag & FWRITE) && (triggers & PCM_ENABLE_OUTPUT)) {
813 		sp = auclnt_output_stream(c);
814 		auclnt_clear_paused(sp);
815 		auclnt_start(sp);
816 	}
817 
818 	if ((oflag & FREAD) && (triggers & PCM_ENABLE_INPUT)) {
819 		sp = auclnt_input_stream(c);
820 		auclnt_clear_paused(sp);
821 		auclnt_start(sp);
822 	}
823 
824 	return (0);
825 }
826 
827 struct oss_legacy_volume {
828 	pid_t		pid;
829 	uint8_t		ogain;
830 	uint8_t		igain;
831 };
832 
833 static int
834 oss_legacy_volume_walker(audio_client_t *c, void *arg)
835 {
836 	struct oss_legacy_volume	 *olv = arg;
837 
838 	if (auclnt_get_pid(c) == olv->pid) {
839 		if (olv->ogain <= 100) {
840 			auclnt_set_gain(auclnt_output_stream(c), olv->ogain);
841 		}
842 		if (olv->igain <= 100) {
843 			auclnt_set_gain(auclnt_input_stream(c), olv->igain);
844 		}
845 	}
846 	return (AUDIO_WALK_CONTINUE);
847 }
848 
849 static void
850 oss_set_legacy_volume(audio_client_t *c, uint8_t ogain, uint8_t igain)
851 {
852 	struct oss_legacy_volume olv;
853 
854 	olv.pid = auclnt_get_pid(c);
855 	olv.ogain = ogain;
856 	olv.igain = igain;
857 	auclnt_dev_walk_clients(auclnt_get_dev(c),
858 	    oss_legacy_volume_walker, &olv);
859 }
860 
861 static int
862 sndctl_dsp_getplayvol(audio_client_t *c, int *volp)
863 {
864 	int	vol;
865 
866 	/* convert monophonic soft value to OSS stereo value */
867 	vol = auclnt_get_gain(auclnt_output_stream(c));
868 	*volp = vol | (vol << 8);
869 	return (0);
870 }
871 
872 static int
873 sndctl_dsp_setplayvol(audio_client_t *c, int *volp)
874 {
875 	uint8_t		vol;
876 
877 	vol = *volp & 0xff;
878 	if (vol > 100) {
879 		return (EINVAL);
880 	}
881 
882 	auclnt_set_gain(auclnt_output_stream(c), vol);
883 	*volp = (vol | (vol << 8));
884 
885 	return (0);
886 }
887 
888 static int
889 sndctl_dsp_getrecvol(audio_client_t *c, int *volp)
890 {
891 	int	vol;
892 
893 	vol = auclnt_get_gain(auclnt_input_stream(c));
894 	*volp = (vol | (vol << 8));
895 	return (0);
896 }
897 
898 static int
899 sndctl_dsp_setrecvol(audio_client_t *c, int *volp)
900 {
901 	uint8_t		vol;
902 
903 	vol = *volp & 0xff;
904 	if (vol > 100) {
905 		return (EINVAL);
906 	}
907 
908 	auclnt_set_gain(auclnt_input_stream(c), vol);
909 	*volp = (vol | (vol << 8));
910 
911 	return (0);
912 }
913 
914 static int
915 sound_mixer_write_ogain(audio_client_t *c, int *volp)
916 {
917 	uint8_t		vol;
918 
919 	vol = *volp & 0xff;
920 	if (vol > 100) {
921 		return (EINVAL);
922 	}
923 	oss_set_legacy_volume(c, vol, 255);
924 	*volp = (vol | (vol << 8));
925 	return (0);
926 }
927 
928 static int
929 sound_mixer_write_igain(audio_client_t *c, int *volp)
930 {
931 	uint8_t		vol;
932 
933 	vol = *volp & 0xff;
934 	if (vol > 100) {
935 		return (EINVAL);
936 	}
937 	oss_set_legacy_volume(c, 255, vol);
938 	*volp = (vol | (vol << 8));
939 	return (0);
940 }
941 
942 static int
943 sndctl_dsp_readctl(audio_client_t *c, oss_digital_control *ctl)
944 {
945 	/* SPDIF: need to add support with spdif */
946 	_NOTE(ARGUNUSED(c));
947 	_NOTE(ARGUNUSED(ctl));
948 	return (ENOTSUP);
949 }
950 
951 static int
952 sndctl_dsp_writectl(audio_client_t *c, oss_digital_control *ctl)
953 {
954 	/* SPDIF: need to add support with spdif */
955 	_NOTE(ARGUNUSED(c));
956 	_NOTE(ARGUNUSED(ctl));
957 	return (ENOTSUP);
958 }
959 
960 static int
961 sndctl_dsp_cookedmode(audio_client_t *c, int *rvp)
962 {
963 	_NOTE(ARGUNUSED(c));
964 
965 	/* We are *always* in cooked mode -- at least until we have AC3. */
966 	if (*rvp == 0) {
967 		return (ENOTSUP);
968 	} else {
969 		return (0);
970 	}
971 }
972 
973 static int
974 sndctl_dsp_silence(audio_client_t *c)
975 {
976 	if (auclnt_get_oflag(c) & FWRITE) {
977 		audio_stream_t	*sp = auclnt_output_stream(c);
978 		auclnt_set_paused(sp);
979 		auclnt_flush(sp);
980 	}
981 	return (0);
982 }
983 
984 static int
985 sndctl_dsp_skip(audio_client_t *c)
986 {
987 	if (auclnt_get_oflag(c) & FWRITE) {
988 		audio_stream_t	*sp = auclnt_output_stream(c);
989 		auclnt_set_paused(sp);
990 		auclnt_flush(sp);
991 		auclnt_clear_paused(sp);
992 	}
993 	return (0);
994 }
995 
996 static int
997 sndctl_dsp_halt_input(audio_client_t *c)
998 {
999 	if (auclnt_get_oflag(c) & FREAD) {
1000 		audio_stream_t	*sp = auclnt_input_stream(c);
1001 		auclnt_set_paused(sp);
1002 		auclnt_flush(sp);
1003 	}
1004 	return (0);
1005 }
1006 
1007 static int
1008 sndctl_dsp_halt_output(audio_client_t *c)
1009 {
1010 	if (auclnt_get_oflag(c) & FWRITE) {
1011 		audio_stream_t	*sp = auclnt_output_stream(c);
1012 		auclnt_set_paused(sp);
1013 		auclnt_flush(sp);
1014 	}
1015 	return (0);
1016 }
1017 
1018 static int
1019 sndctl_dsp_halt(audio_client_t *c)
1020 {
1021 	(void) sndctl_dsp_halt_input(c);
1022 	(void) sndctl_dsp_halt_output(c);
1023 	return (0);
1024 }
1025 
1026 static int
1027 sndctl_dsp_sync(audio_client_t *c)
1028 {
1029 	return (auclnt_drain(c));
1030 }
1031 
1032 static int
1033 sndctl_dsp_setfragment(audio_client_t *c, int *fragp)
1034 {
1035 	_NOTE(ARGUNUSED(c));
1036 	_NOTE(ARGUNUSED(fragp));
1037 	/*
1038 	 * We don't really implement this "properly" at this time.
1039 	 * The problems with this ioctl are various: the API insists
1040 	 * that fragment sizes be a power of two -- and we can't cope
1041 	 * with accurately reporting fragment sizes in the face of
1042 	 * mixing and format conversion.
1043 	 *
1044 	 * Well behaved applications should really not use this API.
1045 	 *
1046 	 * According to the OSS API documentation, the values provided
1047 	 * are nothing more than a "hint" and not to be relied upon
1048 	 * anyway.  And we aren't obligated to report the actual
1049 	 * values back!
1050 	 */
1051 	return (0);
1052 }
1053 
1054 /*
1055  * A word about recsrc, and playtgt ioctls: We don't allow ordinary DSP
1056  * applications to change port configurations, because these could have a
1057  * bad effect for other applications.  Instead, these settings have to
1058  * be changed using the master mixer panel.  In order to make applications
1059  * happy, we just present a single "default" source/target.
1060  */
1061 static int
1062 sndctl_dsp_get_recsrc_names(audio_client_t *c, oss_mixer_enuminfo *ei)
1063 {
1064 	_NOTE(ARGUNUSED(c));
1065 
1066 	ei->nvalues = 1;
1067 	(void) snprintf(ei->strings, sizeof (ei->strings), "default");
1068 	ei->strindex[0] = 0;
1069 
1070 	return (0);
1071 }
1072 
1073 static int
1074 sndctl_dsp_get_recsrc(audio_client_t *c, int *srcp)
1075 {
1076 	_NOTE(ARGUNUSED(c));
1077 	*srcp = 0;
1078 	return (0);
1079 }
1080 
1081 static int
1082 sndctl_dsp_set_recsrc(audio_client_t *c, int *srcp)
1083 {
1084 	_NOTE(ARGUNUSED(c));
1085 	*srcp = 0;
1086 	return (0);
1087 }
1088 
1089 static int
1090 sndctl_dsp_get_playtgt_names(audio_client_t *c, oss_mixer_enuminfo *ei)
1091 {
1092 	_NOTE(ARGUNUSED(c));
1093 
1094 	ei->nvalues = 1;
1095 	(void) snprintf(ei->strings, sizeof (ei->strings), "default");
1096 	ei->strindex[0] = 0;
1097 
1098 	return (0);
1099 }
1100 
1101 static int
1102 sndctl_dsp_get_playtgt(audio_client_t *c, int *tgtp)
1103 {
1104 	_NOTE(ARGUNUSED(c));
1105 	*tgtp = 0;
1106 	return (0);
1107 }
1108 
1109 static int
1110 sndctl_dsp_set_playtgt(audio_client_t *c, int *tgtp)
1111 {
1112 	_NOTE(ARGUNUSED(c));
1113 	*tgtp = 0;
1114 	return (0);
1115 }
1116 
1117 static int
1118 sndctl_sysinfo(oss_sysinfo *si)
1119 {
1120 	bzero(si, sizeof (*si));
1121 	(void) snprintf(si->product, sizeof (si->product), "SunOS Audio");
1122 	(void) snprintf(si->version, sizeof (si->version), "4.0");
1123 	si->versionnum = OSS_VERSION;
1124 	si->numcards = oss_cnt_devs();
1125 	si->nummixers = si->numcards - 1;
1126 	si->numaudios = si->numcards - 1;
1127 	si->numaudioengines = si->numaudios;
1128 	(void) snprintf(si->license, sizeof (si->license), "CDDL");
1129 	return (0);
1130 }
1131 
1132 static int
1133 sndctl_cardinfo(audio_client_t *c, oss_card_info *ci)
1134 {
1135 	audio_dev_t	*d;
1136 	void		*iter;
1137 	const char 	*info;
1138 	int		n;
1139 	boolean_t	release;
1140 
1141 	if ((n = ci->card) == -1) {
1142 		release = B_FALSE;
1143 		d = auclnt_get_dev(c);
1144 		n = auclnt_get_dev_index(d);
1145 	} else {
1146 		release = B_TRUE;
1147 		d = auclnt_hold_dev_by_index(n);
1148 	}
1149 
1150 	bzero(ci, sizeof (*ci));
1151 	ci->card = n;
1152 
1153 	if (d == NULL) {
1154 		/*
1155 		 * If device removed (e.g. for DR), then
1156 		 * report a bogus removed entry.
1157 		 */
1158 		(void) snprintf(ci->shortname, sizeof (ci->shortname),
1159 		    "<removed>");
1160 		(void) snprintf(ci->longname, sizeof (ci->longname),
1161 		    "<removed>");
1162 		return (0);
1163 	}
1164 
1165 	(void) snprintf(ci->shortname, sizeof (ci->shortname),
1166 	    "%s", auclnt_get_dev_name(d));
1167 	(void) snprintf(ci->longname, sizeof (ci->longname),
1168 	    "%s (%s)", auclnt_get_dev_description(d),
1169 	    auclnt_get_dev_version(d));
1170 
1171 	iter = NULL;
1172 	while ((info = auclnt_get_dev_hw_info(d, &iter)) != NULL) {
1173 		(void) strlcat(ci->hw_info, info, sizeof (ci->hw_info));
1174 		(void) strlcat(ci->hw_info, "\n", sizeof (ci->hw_info));
1175 	}
1176 
1177 	/*
1178 	 * We don't report interrupt counts, ack counts (which are
1179 	 * just "read" interrupts, not spurious), or any other flags.
1180 	 * Nothing should be using any of this data anyway ... these
1181 	 * values were intended for 4Front's debugging purposes.  In
1182 	 * Solaris, drivers should use interrupt kstats to report
1183 	 * interrupt related statistics.
1184 	 */
1185 	if (release)
1186 		auclnt_release_dev(d);
1187 	return (0);
1188 }
1189 
1190 static int
1191 audioinfo_walker(audio_engine_t *e, void *a)
1192 {
1193 	oss_audioinfo *si = a;
1194 	int fmt, nchan, rate, cap;
1195 
1196 	fmt = auclnt_engine_get_format(e);
1197 	nchan = auclnt_engine_get_channels(e);
1198 	rate = auclnt_engine_get_rate(e);
1199 	cap = auclnt_engine_get_capab(e);
1200 
1201 	for (int i = 0; oss_formats[i].fmt != AUDIO_FORMAT_NONE; i++) {
1202 		if (fmt == oss_formats[i].fmt) {
1203 			if (cap & AUDIO_CLIENT_CAP_PLAY) {
1204 				si->oformats |= oss_formats[i].oss;
1205 			}
1206 			if (cap & AUDIO_CLIENT_CAP_RECORD) {
1207 				si->iformats |= oss_formats[i].oss;
1208 			}
1209 			break;
1210 		}
1211 	}
1212 	si->max_channels = max(nchan, si->max_channels);
1213 	si->max_rate = max(rate, si->max_rate);
1214 
1215 	return (AUDIO_WALK_CONTINUE);
1216 }
1217 
1218 static int
1219 sndctl_audioinfo(audio_client_t *c, oss_audioinfo *si)
1220 {
1221 	audio_dev_t		*d;
1222 	const char		*name;
1223 	int			n;
1224 	boolean_t		release;
1225 	unsigned		cap;
1226 
1227 	if ((n = si->dev) == -1) {
1228 		release = B_FALSE;
1229 		d = auclnt_get_dev(c);
1230 		n = auclnt_get_dev_index(d);
1231 	} else {
1232 		release = B_TRUE;
1233 		n++;	/* skip pseudo device */
1234 		d = auclnt_hold_dev_by_index(n);
1235 	}
1236 
1237 	bzero(si, sizeof (*si));
1238 	si->dev = n - 1;
1239 
1240 	if (d == NULL) {
1241 		/* if device not present, forge a false entry */
1242 		si->card_number = n;
1243 		si->mixer_dev = n - 1;
1244 		si->legacy_device = -1;
1245 		si->enabled = 0;
1246 		(void) snprintf(si->name, sizeof (si->name), "<removed>");
1247 		return (0);
1248 	}
1249 
1250 	name = auclnt_get_dev_name(d);
1251 	(void) snprintf(si->name, sizeof (si->name), "%s", name);
1252 
1253 	si->legacy_device = auclnt_get_dev_number(d);
1254 	si->caps = 0;
1255 
1256 	auclnt_dev_walk_engines(d, audioinfo_walker, si);
1257 
1258 	cap = auclnt_get_dev_capab(d);
1259 
1260 	if (cap	& AUDIO_CLIENT_CAP_DUPLEX) {
1261 		si->caps |= PCM_CAP_DUPLEX;
1262 	}
1263 	if (cap & AUDIO_CLIENT_CAP_PLAY) {
1264 		si->caps |= PCM_CAP_OUTPUT;
1265 	}
1266 	if (cap & AUDIO_CLIENT_CAP_RECORD) {
1267 		si->caps |= PCM_CAP_INPUT;
1268 	}
1269 
1270 	if (si->caps != 0) {
1271 		/* AC3: PCM_CAP_MULTI would be wrong for an AC3 only device */
1272 		si->caps |= PCM_CAP_BATCH | PCM_CAP_TRIGGER | PCM_CAP_MULTI;
1273 		/* MMAP: we add PCM_CAP_MMAP when we we support it */
1274 		si->enabled = 1;
1275 		si->rate_source = si->dev;
1276 
1277 		/* we can convert PCM formats */
1278 		if ((si->iformats | si->oformats) &
1279 		    AUDIO_FORMAT_PCM) {
1280 			si->min_channels = min(2, si->max_channels);
1281 			si->min_rate = min(5000, si->max_rate);
1282 			si->caps |= PCM_CAP_FREERATE;
1283 		}
1284 		(void) snprintf(si->devnode, sizeof (si->devnode),
1285 		    "/dev/sound/%s:%ddsp",
1286 		    auclnt_get_dev_driver(d), auclnt_get_dev_instance(d));
1287 	} else {
1288 		si->enabled = 0;	/* stops apps from using us directly */
1289 		si->caps = PCM_CAP_VIRTUAL;
1290 		(void) snprintf(si->devnode, sizeof (si->devnode),
1291 		    "/dev/sndstat");
1292 	}
1293 
1294 	si->pid = -1;
1295 	(void) snprintf(si->handle, sizeof (si->handle), "%s", name);
1296 	(void) snprintf(si->label, sizeof (si->label), "%s", name);
1297 	si->latency = -1;
1298 	si->card_number = n;
1299 	si->mixer_dev = n - 1;
1300 
1301 	if (release)
1302 		auclnt_release_dev(d);
1303 
1304 	return (0);
1305 }
1306 
1307 static int
1308 sound_mixer_info(audio_client_t *c, mixer_info *mi)
1309 {
1310 	audio_dev_t	*d;
1311 	ossdev_t	*odev;
1312 	ossclient_t	*sc;
1313 	const char	*name;
1314 
1315 	sc = auclnt_get_private(c);
1316 	odev = sc->o_ossdev;
1317 
1318 	d = auclnt_get_dev(c);
1319 
1320 	name = auclnt_get_dev_name(d);
1321 	(void) snprintf(mi->id, sizeof (mi->id), "%s", name);
1322 	(void) snprintf(mi->name, sizeof (mi->name), "%s", name);
1323 	(void) snprintf(mi->handle, sizeof (mi->handle), "%s", name);
1324 	mi->modify_counter = odev->d_modify_cnt;
1325 	mi->card_number = auclnt_get_dev_index(d);
1326 	mi->port_number = 0;
1327 	return (0);
1328 }
1329 
1330 static int
1331 sound_mixer_read_devmask(audio_client_t *c, int *devmask)
1332 {
1333 	_NOTE(ARGUNUSED(c));
1334 	*devmask = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_IGAIN;
1335 	return (0);
1336 }
1337 
1338 static int
1339 sound_mixer_read_recmask(audio_client_t *c, int *recmask)
1340 {
1341 	_NOTE(ARGUNUSED(c));
1342 	*recmask = 0;
1343 	return (0);
1344 }
1345 
1346 static int
1347 sound_mixer_read_recsrc(audio_client_t *c, int *recsrc)
1348 {
1349 	_NOTE(ARGUNUSED(c));
1350 	*recsrc = 0;
1351 	return (0);
1352 }
1353 
1354 static int
1355 sound_mixer_read_caps(audio_client_t *c, int *caps)
1356 {
1357 	_NOTE(ARGUNUSED(c));
1358 	/* single recording source... sort of */
1359 	*caps = SOUND_CAP_EXCL_INPUT;
1360 	return (0);
1361 }
1362 
1363 static int
1364 sndctl_mixerinfo(audio_client_t *c, oss_mixerinfo *mi)
1365 {
1366 	audio_dev_t		*d;
1367 	ossdev_t 		*odev;
1368 	const char		*name;
1369 	int			n;
1370 	boolean_t		release = B_FALSE;
1371 
1372 	if ((n = mi->dev) == -1) {
1373 		release = B_FALSE;
1374 		d = auclnt_get_dev(c);
1375 		n = auclnt_get_dev_index(d);
1376 	} else {
1377 		release = B_TRUE;
1378 		n++;
1379 		d = auclnt_hold_dev_by_index(n);
1380 	}
1381 
1382 	bzero(mi, sizeof (*mi));
1383 	mi->dev = n - 1;
1384 
1385 	if (d == NULL) {
1386 		mi->card_number = n;
1387 		mi->enabled = 0;
1388 		mi->legacy_device = -1;
1389 		(void) snprintf(mi->name, sizeof (mi->name), "<removed>");
1390 		(void) snprintf(mi->id, sizeof (mi->id), "<removed>");
1391 		return (0);
1392 	}
1393 
1394 	if ((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) {
1395 		if (release)
1396 			auclnt_release_dev(d);
1397 		return (EINVAL);
1398 	}
1399 
1400 	name = auclnt_get_dev_name(d);
1401 	(void) snprintf(mi->name, sizeof (mi->name), "%s", name);
1402 	(void) snprintf(mi->id, sizeof (mi->id), "%s", name);
1403 	(void) snprintf(mi->handle, sizeof (mi->handle), "%s", name);
1404 	mi->modify_counter = odev->d_modify_cnt;
1405 	mi->card_number = auclnt_get_dev_index(d);
1406 	mi->legacy_device = auclnt_get_dev_number(d);
1407 	if (mi->legacy_device >= 0) {
1408 		(void) snprintf(mi->devnode, sizeof (mi->devnode),
1409 		    "/dev/sound/%s:%dmixer",
1410 		    auclnt_get_dev_driver(d), auclnt_get_dev_instance(d));
1411 		mi->enabled = 1;
1412 	} else {
1413 		/* special nodes use generic sndstat node */
1414 		(void) snprintf(mi->devnode, sizeof (mi->devnode),
1415 		    "/dev/sndstat");
1416 		mi->enabled = 0;
1417 	}
1418 	mi->nrext = odev->d_nctrl;
1419 
1420 	if (release)
1421 		auclnt_release_dev(d);
1422 
1423 	return (0);
1424 }
1425 
1426 static int
1427 sndctl_dsp_getblksize(audio_client_t *c, int *fragsz)
1428 {
1429 	int	oflag = auclnt_get_oflag(c);
1430 
1431 	if (oflag & FWRITE)
1432 		*fragsz  = auclnt_get_fragsz(auclnt_output_stream(c));
1433 	else if (oflag & FREAD)
1434 		*fragsz  = auclnt_get_fragsz(auclnt_input_stream(c));
1435 
1436 	return (0);
1437 }
1438 
1439 static int
1440 sndctl_dsp_getospace(audio_client_t *c, audio_buf_info *bi)
1441 {
1442 	audio_stream_t	*sp;
1443 	unsigned	n;
1444 
1445 	if ((auclnt_get_oflag(c) & FWRITE) == 0) {
1446 		return (EACCES);
1447 	}
1448 
1449 	sp = auclnt_output_stream(c);
1450 	n = auclnt_get_nframes(sp) - auclnt_get_count(sp);
1451 
1452 	bi->fragsize  = auclnt_get_fragsz(sp);
1453 	bi->fragstotal = auclnt_get_nfrags(sp);
1454 	bi->bytes = (n * auclnt_get_framesz(sp));
1455 	bi->fragments = bi->bytes / bi->fragsize;
1456 
1457 	return (0);
1458 }
1459 
1460 static int
1461 sndctl_dsp_getispace(audio_client_t *c, audio_buf_info *bi)
1462 {
1463 	audio_stream_t	*sp;
1464 	unsigned	n;
1465 
1466 	if ((auclnt_get_oflag(c) & FREAD) == 0) {
1467 		return (EACCES);
1468 	}
1469 
1470 	sp = auclnt_input_stream(c);
1471 	n = auclnt_get_count(sp);
1472 
1473 	bi->fragsize  = auclnt_get_fragsz(sp);
1474 	bi->fragstotal = auclnt_get_nfrags(sp);
1475 	bi->bytes = (n * auclnt_get_framesz(sp));
1476 	bi->fragments = bi->bytes / bi->fragsize;
1477 
1478 	return (0);
1479 }
1480 
1481 static int
1482 sndctl_dsp_getodelay(audio_client_t *c, int *bytes)
1483 {
1484 	unsigned	framesz;
1485 	unsigned	slen, flen;
1486 
1487 	if (auclnt_get_oflag(c) & FWRITE) {
1488 		audio_stream_t	*sp = auclnt_output_stream(c);
1489 		framesz = auclnt_get_framesz(sp);
1490 		auclnt_get_output_qlen(c, &slen, &flen);
1491 		*bytes = (slen + flen) * framesz;
1492 	} else {
1493 		*bytes = 0;
1494 	}
1495 	return (0);
1496 }
1497 
1498 static int
1499 sndctl_dsp_current_iptr(audio_client_t *c, oss_count_t *count)
1500 {
1501 	if (auclnt_get_oflag(c) & FREAD) {
1502 		count->samples = auclnt_get_samples(auclnt_input_stream(c));
1503 		count->fifo_samples = 0;	/* not quite accurate */
1504 	} else {
1505 		count->samples = 0;
1506 		count->fifo_samples = 0;
1507 	}
1508 	return (0);
1509 }
1510 
1511 static int
1512 sndctl_dsp_current_optr(audio_client_t *c, oss_count_t *count)
1513 {
1514 	unsigned samples, fifo;
1515 
1516 	if (auclnt_get_oflag(c) & FWRITE) {
1517 		auclnt_get_output_qlen(c, &samples, &fifo);
1518 		count->samples = samples;
1519 		count->fifo_samples = fifo;
1520 	} else {
1521 		count->samples = 0;
1522 		count->fifo_samples = 0;
1523 	}
1524 	return (0);
1525 }
1526 
1527 static int
1528 sndctl_dsp_getoptr(audio_client_t *c, count_info *ci)
1529 {
1530 	audio_stream_t	*sp;
1531 	unsigned	framesz;
1532 	unsigned	fragsz;
1533 
1534 	bzero(ci, sizeof (*ci));
1535 	if ((auclnt_get_oflag(c) & FWRITE) == 0) {
1536 		return (0);
1537 	}
1538 	sp = auclnt_output_stream(c);
1539 	framesz = auclnt_get_framesz(sp);
1540 	fragsz = auclnt_get_fragsz(sp);
1541 	ci->blocks = auclnt_get_samples(sp) * framesz / fragsz;
1542 	auclnt_set_samples(sp, 0);
1543 	ci->bytes = auclnt_get_tail(sp) * framesz;
1544 	ci->ptr = auclnt_get_tidx(sp) * framesz;
1545 	return (0);
1546 }
1547 
1548 static int
1549 sndctl_dsp_getiptr(audio_client_t *c, count_info *ci)
1550 {
1551 	audio_stream_t	*sp;
1552 	unsigned	framesz;
1553 	unsigned	fragsz;
1554 
1555 	bzero(ci, sizeof (*ci));
1556 	if ((auclnt_get_oflag(c) & FREAD) == 0) {
1557 		return (0);
1558 	}
1559 	sp = auclnt_input_stream(c);
1560 	framesz = auclnt_get_framesz(sp);
1561 	fragsz = auclnt_get_fragsz(sp);
1562 	ci->blocks = auclnt_get_samples(sp) * framesz / fragsz;
1563 	auclnt_set_samples(sp, 0);
1564 	ci->bytes = auclnt_get_head(sp) * framesz;
1565 	ci->ptr = auclnt_get_hidx(sp) * framesz;
1566 	return (0);
1567 }
1568 
1569 static int
1570 sndctl_dsp_geterror(audio_client_t *c, audio_errinfo *bi)
1571 {
1572 	audio_stream_t	*sp;
1573 	unsigned	fragsz;
1574 	/*
1575 	 * Note: The use of this structure is unsafe... different
1576 	 * meanings for error codes are used by different implementations,
1577 	 * according to the spec.  (Even different versions of the same
1578 	 * implementation could have different values.)
1579 	 *
1580 	 * Rather than try to come up with a reliable solution here, we
1581 	 * don't use it.  If you want to report errors, or see the result
1582 	 * of errors, use syslog.
1583 	 */
1584 	bzero(bi, sizeof (*bi));
1585 
1586 	sp = auclnt_output_stream(c);
1587 	fragsz = max(auclnt_get_fragsz(sp), 1);
1588 	bi->play_underruns = (int)((auclnt_get_errors(sp) + (fragsz - 1)) /
1589 	    fragsz);
1590 	auclnt_set_errors(sp, 0);
1591 
1592 	sp = auclnt_input_stream(c);
1593 	fragsz = max(auclnt_get_fragsz(sp), 1);
1594 	bi->rec_overruns = (int)((auclnt_get_errors(sp) + (fragsz - 1)) /
1595 	    fragsz);
1596 	auclnt_set_errors(sp, 0);
1597 
1598 	return (0);
1599 }
1600 
1601 static int
1602 sndctl_sun_send_number(audio_client_t *c, int *num, cred_t *cr)
1603 {
1604 	audio_dev_t	*dev;
1605 	int		rv;
1606 
1607 	if ((rv = drv_priv(cr)) != 0) {
1608 		return (rv);
1609 	}
1610 
1611 	dev = auclnt_get_dev(c);
1612 	auclnt_set_dev_number(dev, *num);
1613 	return (0);
1614 }
1615 
1616 static int
1617 oss_getversion(int *versp)
1618 {
1619 	*versp = OSS_VERSION;
1620 	return (0);
1621 }
1622 
1623 static int
1624 oss_ioctl(audio_client_t *c, int cmd, intptr_t arg, int mode, cred_t *credp,
1625     int *rvalp)
1626 {
1627 	int	sz;
1628 	void	*data;
1629 	int	rv = 0;
1630 
1631 	_NOTE(ARGUNUSED(credp));
1632 
1633 	sz = OSSIOC_GETSZ(cmd);
1634 
1635 	if ((cmd & (OSSIOC_IN | OSSIOC_OUT)) && sz) {
1636 		if ((data = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
1637 			return (ENOMEM);
1638 		}
1639 	} else {
1640 		sz = 0;
1641 	}
1642 
1643 	if (cmd & OSSIOC_IN) {
1644 		if ((rv = ddi_copyin((void *)arg, data, sz, mode)) != 0) {
1645 			goto done;
1646 		}
1647 	}
1648 
1649 	switch (cmd) {
1650 		/*
1651 		 * DSP specific ioctls
1652 		 */
1653 	case SNDCTL_DSP_HALT:
1654 		rv = sndctl_dsp_halt(c);
1655 		break;
1656 
1657 	case SNDCTL_DSP_SYNC:
1658 		rv = sndctl_dsp_sync(c);
1659 		break;
1660 
1661 	case SNDCTL_DSP_SPEED:
1662 		rv = sndctl_dsp_speed(c, (int *)data);
1663 		break;
1664 	case SNDCTL_DSP_SETFMT:
1665 		rv = sndctl_dsp_setfmt(c, (int *)data);
1666 		break;
1667 	case SNDCTL_DSP_GETFMTS:
1668 		rv = sndctl_dsp_getfmts(c, (int *)data);
1669 		break;
1670 	case SNDCTL_DSP_STEREO:
1671 		rv = sndctl_dsp_stereo(c, (int *)data);
1672 		break;
1673 	case SNDCTL_DSP_CHANNELS:
1674 		rv = sndctl_dsp_channels(c, (int *)data);
1675 		break;
1676 	case SNDCTL_DSP_POST:
1677 		rv = sndctl_dsp_post(c);
1678 		break;
1679 	case SNDCTL_DSP_GETCAPS:
1680 		rv = sndctl_dsp_getcaps(c, (int *)data);
1681 		break;
1682 	case SNDCTL_DSP_GETTRIGGER:
1683 		rv = sndctl_dsp_gettrigger(c, (int *)data);
1684 		break;
1685 	case SNDCTL_DSP_SETTRIGGER:
1686 		rv = sndctl_dsp_settrigger(c, (int *)data);
1687 		break;
1688 	case SNDCTL_DSP_GETPLAYVOL:
1689 	case SOUND_MIXER_READ_VOLUME:	/* legacy mixer on dsp */
1690 	case SOUND_MIXER_READ_PCM:	/* legacy mixer on dsp */
1691 	case SOUND_MIXER_READ_OGAIN:	/* legacy mixer on dsp */
1692 		rv = sndctl_dsp_getplayvol(c, (int *)data);
1693 		break;
1694 	case SOUND_MIXER_WRITE_VOLUME:	/* legacy mixer on dsp */
1695 	case SOUND_MIXER_WRITE_PCM:	/* legacy mixer on dsp */
1696 	case SOUND_MIXER_WRITE_OGAIN:	/* legacy mixer on dsp */
1697 		rv = sound_mixer_write_ogain(c, (int *)data);
1698 		break;
1699 	case SNDCTL_DSP_SETPLAYVOL:
1700 		rv = sndctl_dsp_setplayvol(c, (int *)data);
1701 		break;
1702 	case SNDCTL_DSP_READCTL:
1703 		rv = sndctl_dsp_readctl(c, (oss_digital_control *)data);
1704 		break;
1705 	case SNDCTL_DSP_WRITECTL:
1706 		rv = sndctl_dsp_writectl(c, (oss_digital_control *)data);
1707 		break;
1708 	case SNDCTL_DSP_COOKEDMODE:
1709 		rv = sndctl_dsp_cookedmode(c, (int *)data);
1710 		break;
1711 	case SNDCTL_DSP_SILENCE:
1712 		rv = sndctl_dsp_silence(c);
1713 		break;
1714 	case SNDCTL_DSP_SKIP:
1715 		rv = sndctl_dsp_skip(c);
1716 		break;
1717 	case SNDCTL_DSP_HALT_INPUT:
1718 		rv = sndctl_dsp_halt_input(c);
1719 		break;
1720 	case SNDCTL_DSP_HALT_OUTPUT:
1721 		rv = sndctl_dsp_halt_output(c);
1722 		break;
1723 	case SNDCTL_DSP_GET_RECSRC_NAMES:
1724 		rv = sndctl_dsp_get_recsrc_names(c, (oss_mixer_enuminfo *)data);
1725 		break;
1726 	case SNDCTL_DSP_SUBDIVIDE:
1727 	case SNDCTL_DSP_SETFRAGMENT:
1728 		rv = sndctl_dsp_setfragment(c, (int *)data);
1729 		break;
1730 	case SNDCTL_DSP_GET_RECSRC:
1731 		rv = sndctl_dsp_get_recsrc(c, (int *)data);
1732 		break;
1733 	case SNDCTL_DSP_SET_RECSRC:
1734 		rv = sndctl_dsp_set_recsrc(c, (int *)data);
1735 		break;
1736 	case SNDCTL_DSP_GET_PLAYTGT_NAMES:
1737 		rv = sndctl_dsp_get_playtgt_names(c,
1738 		    (oss_mixer_enuminfo *)data);
1739 		break;
1740 	case SNDCTL_DSP_GET_PLAYTGT:
1741 		rv = sndctl_dsp_get_playtgt(c, (int *)data);
1742 		break;
1743 	case SNDCTL_DSP_SET_PLAYTGT:
1744 		rv = sndctl_dsp_set_playtgt(c, (int *)data);
1745 		break;
1746 	case SNDCTL_DSP_GETRECVOL:
1747 	case SOUND_MIXER_READ_RECGAIN:	/* legacy mixer on dsp */
1748 	case SOUND_MIXER_READ_RECLEV:	/* legacy mixer on dsp */
1749 	case SOUND_MIXER_READ_IGAIN:	/* legacy mixer on dsp */
1750 		rv = sndctl_dsp_getrecvol(c, (int *)data);
1751 		break;
1752 	case SOUND_MIXER_WRITE_RECGAIN:	/* legacy mixer on dsp */
1753 	case SOUND_MIXER_WRITE_RECLEV:	/* legacy mixer on dsp */
1754 	case SOUND_MIXER_WRITE_IGAIN:	/* legacy mixer on dsp */
1755 		rv = sound_mixer_write_igain(c, (int *)data);
1756 		break;
1757 	case SNDCTL_DSP_SETRECVOL:
1758 		rv = sndctl_dsp_setrecvol(c, (int *)data);
1759 		break;
1760 	case SNDCTL_DSP_SETDUPLEX:	/* Ignored */
1761 	case SNDCTL_DSP_LOW_WATER:	/* Ignored */
1762 	case SNDCTL_DSP_POLICY:		/* Ignored */
1763 	case SNDCTL_DSP_PROFILE:	/* Ignored */
1764 		rv = 0;
1765 		break;
1766 	case SNDCTL_DSP_GETBLKSIZE:
1767 		rv = sndctl_dsp_getblksize(c, (int *)data);
1768 		break;
1769 	case SNDCTL_DSP_GETOSPACE:
1770 		rv = sndctl_dsp_getospace(c, (audio_buf_info *)data);
1771 		break;
1772 	case SNDCTL_DSP_GETISPACE:
1773 		rv = sndctl_dsp_getispace(c, (audio_buf_info *)data);
1774 		break;
1775 	case SNDCTL_DSP_GETODELAY:
1776 		rv = sndctl_dsp_getodelay(c, (int *)data);
1777 		break;
1778 	case SNDCTL_DSP_GETOPTR:
1779 		rv = sndctl_dsp_getoptr(c, (count_info *)data);
1780 		break;
1781 	case SNDCTL_DSP_GETIPTR:
1782 		rv = sndctl_dsp_getiptr(c, (count_info *)data);
1783 		break;
1784 	case SNDCTL_DSP_GETERROR:
1785 		rv = sndctl_dsp_geterror(c, (audio_errinfo *)data);
1786 		break;
1787 	case SNDCTL_DSP_CURRENT_IPTR:
1788 		rv = sndctl_dsp_current_iptr(c, (oss_count_t *)data);
1789 		break;
1790 	case SNDCTL_DSP_CURRENT_OPTR:
1791 		rv = sndctl_dsp_current_optr(c, (oss_count_t *)data);
1792 		break;
1793 
1794 		/*
1795 		 * Shared ioctls with /dev/mixer.
1796 		 */
1797 	case OSS_GETVERSION:
1798 		rv = oss_getversion((int *)data);
1799 		break;
1800 	case SNDCTL_CARDINFO:
1801 		rv = sndctl_cardinfo(c, (oss_card_info *)data);
1802 		break;
1803 	case SNDCTL_ENGINEINFO:
1804 	case SNDCTL_AUDIOINFO:
1805 	case SNDCTL_AUDIOINFO_EX:
1806 		rv = sndctl_audioinfo(c, (oss_audioinfo *)data);
1807 		break;
1808 	case SNDCTL_SYSINFO:
1809 		rv = sndctl_sysinfo((oss_sysinfo *)data);
1810 		break;
1811 	case SNDCTL_MIXERINFO:
1812 		rv = sndctl_mixerinfo(c, (oss_mixerinfo *)data);
1813 		break;
1814 	case SOUND_MIXER_INFO:
1815 		rv = sound_mixer_info(c, (mixer_info *)data);
1816 		break;
1817 
1818 		/*
1819 		 * These are mixer ioctls that are virtualized for the DSP
1820 		 * device.  They are accessible via either /dev/mixer or
1821 		 * /dev/dsp.
1822 		 */
1823 	case SOUND_MIXER_READ_RECSRC:
1824 	case SOUND_MIXER_WRITE_RECSRC:
1825 		rv = sound_mixer_read_recsrc(c, (int *)data);
1826 		break;
1827 
1828 	case SOUND_MIXER_READ_DEVMASK:
1829 	case SOUND_MIXER_READ_STEREODEVS:
1830 		rv = sound_mixer_read_devmask(c, (int *)data);
1831 		break;
1832 
1833 	case SOUND_MIXER_READ_RECMASK:
1834 		rv = sound_mixer_read_recmask(c, (int *)data);
1835 		break;
1836 
1837 	case SOUND_MIXER_READ_CAPS:
1838 		rv = sound_mixer_read_caps(c, (int *)data);
1839 		break;
1840 
1841 		/*
1842 		 * Ioctls we have chosen not to support for now.  Some
1843 		 * of these are of legacy interest only.
1844 		 */
1845 	case SNDCTL_SETSONG:
1846 	case SNDCTL_GETSONG:
1847 	case SNDCTL_DSP_SYNCGROUP:
1848 	case SNDCTL_DSP_SYNCSTART:
1849 	case SNDCTL_DSP_GET_CHNORDER:
1850 	case SNDCTL_DSP_SET_CHNORDER:
1851 	case SNDCTL_DSP_GETIPEAKS:
1852 	case SNDCTL_DSP_GETOPEAKS:
1853 	case SNDCTL_DSP_GETCHANNELMASK:
1854 	case SNDCTL_DSP_BIND_CHANNEL:
1855 	case SNDCTL_DSP_SETSYNCRO:
1856 	default:
1857 		rv = EINVAL;
1858 		break;
1859 	}
1860 
1861 	if ((rv == 0) && (cmd & OSSIOC_OUT)) {
1862 		rv = ddi_copyout(data, (void *)arg, sz, mode);
1863 	}
1864 	if (rv == 0) {
1865 		*rvalp = 0;
1866 	}
1867 
1868 done:
1869 	if (sz) {
1870 		kmem_free(data, sz);
1871 	}
1872 	return (rv);
1873 }
1874 
1875 static void
1876 oss_output(audio_client_t *c)
1877 {
1878 	auclnt_pollwakeup(c, POLLOUT);
1879 }
1880 
1881 static void
1882 oss_input(audio_client_t *c)
1883 {
1884 	auclnt_pollwakeup(c, POLLIN | POLLRDNORM);
1885 }
1886 
1887 static void
1888 oss_notify(audio_client_t *c)
1889 {
1890 	audio_dev_t	*d;
1891 	ossdev_t	*odev;
1892 
1893 	d = auclnt_get_dev(c);
1894 	if ((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) {
1895 		return;
1896 	}
1897 	odev->d_modify_cnt++;
1898 }
1899 
1900 static int
1901 ossmix_open(audio_client_t *c, int oflag)
1902 {
1903 	int		rv;
1904 	ossclient_t	*sc;
1905 	ossdev_t	*odev;
1906 
1907 	_NOTE(ARGUNUSED(oflag));
1908 
1909 	if ((rv = auclnt_open(c, AUDIO_FORMAT_NONE, 0)) != 0) {
1910 		return (rv);
1911 	}
1912 
1913 	if ((sc = kmem_zalloc(sizeof (*sc), KM_NOSLEEP)) == NULL) {
1914 		return (ENOMEM);
1915 	}
1916 	sc->o_ss_sz = 8192;
1917 	if ((sc->o_ss_buf = kmem_zalloc(sc->o_ss_sz, KM_NOSLEEP)) == NULL) {
1918 		kmem_free(sc, sizeof (*sc));
1919 		return (ENOMEM);
1920 	}
1921 	auclnt_set_private(c, sc);
1922 
1923 	odev = auclnt_get_minor_data(c, AUDIO_MINOR_DSP);
1924 
1925 	/* set a couple of common fields */
1926 	sc->o_client = c;
1927 	sc->o_ossdev = odev;
1928 
1929 	return (rv);
1930 }
1931 
1932 static void
1933 ossmix_close(audio_client_t *c)
1934 {
1935 	ossclient_t	*sc;
1936 
1937 	sc = auclnt_get_private(c);
1938 
1939 	kmem_free(sc->o_ss_buf, sc->o_ss_sz);
1940 	kmem_free(sc, sizeof (*sc));
1941 
1942 	auclnt_close(c);
1943 }
1944 
1945 static int
1946 sndctl_mix_nrext(audio_client_t *c, int *ncp)
1947 {
1948 	audio_dev_t	*d;
1949 	ossdev_t	*odev;
1950 
1951 	d = auclnt_get_dev(c);
1952 
1953 	if ((*ncp != -1) && (*ncp != (auclnt_get_dev_index(d) - 1))) {
1954 		return (ENXIO);
1955 	}
1956 
1957 	if ((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) {
1958 		return (EINVAL);
1959 	}
1960 
1961 	*ncp = odev->d_nctrl;
1962 
1963 	return (0);
1964 }
1965 
1966 static int
1967 sndctl_mix_extinfo(audio_client_t *c, oss_mixext *pext)
1968 {
1969 	audio_dev_t		*d;
1970 	ossdev_t		*odev;
1971 	int			rv = 0;
1972 	int			dev;
1973 
1974 	d = auclnt_get_dev(c);
1975 
1976 	if (((dev = pext->dev) != -1) && (dev != (auclnt_get_dev_index(d) - 1)))
1977 		return (ENXIO);
1978 
1979 	if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
1980 	    (pext->ctrl >= odev->d_nctrl)) {
1981 		return (EINVAL);
1982 	}
1983 
1984 	bcopy(&odev->d_exts[pext->ctrl], pext, sizeof (*pext));
1985 	pext->enumbit = 0;
1986 	pext->dev = dev;
1987 
1988 	return (rv);
1989 }
1990 
1991 static int
1992 sndctl_mix_enuminfo(audio_client_t *c, oss_mixer_enuminfo *ei)
1993 {
1994 	audio_dev_t		*d;
1995 	audio_ctrl_desc_t	desc;
1996 	audio_ctrl_t		*ctrl;
1997 	ossdev_t		*odev;
1998 	uint64_t		mask;
1999 	int			bit;
2000 	ushort_t		nxt;
2001 
2002 	d = auclnt_get_dev(c);
2003 
2004 	if ((ei->dev != -1) && (ei->dev != (auclnt_get_dev_index(d) - 1)))
2005 		return (ENXIO);
2006 
2007 	if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
2008 	    (ei->ctrl >= odev->d_nctrl) ||
2009 	    (odev->d_exts[ei->ctrl].type != MIXT_ENUM) ||
2010 	    ((ctrl = odev->d_ctrls[ei->ctrl]) == NULL) ||
2011 	    (auclnt_control_describe(ctrl, &desc) != 0)) {
2012 		return (EINVAL);
2013 	}
2014 
2015 	mask = desc.acd_maxvalue;
2016 	bit = 0;
2017 	nxt = 0;
2018 	ei->nvalues = 0;
2019 	bzero(ei->strings, sizeof (ei->strings));
2020 	bzero(ei->strindex, sizeof (ei->strindex));
2021 
2022 	while (mask) {
2023 		const char *name = desc.acd_enum[bit];
2024 		nxt = oss_set_enum(ei, nxt, name ? name : "");
2025 		mask >>= 1;
2026 		bit++;
2027 	}
2028 
2029 	return (0);
2030 }
2031 
2032 static int
2033 sndctl_mix_read(audio_client_t *c, oss_mixer_value *vr)
2034 {
2035 	int			rv;
2036 	uint64_t		v;
2037 	audio_dev_t		*d;
2038 	audio_ctrl_t		*ctrl;
2039 	ossdev_t		*odev;
2040 
2041 	d = auclnt_get_dev(c);
2042 
2043 	if ((vr->dev != -1) && (vr->dev != (auclnt_get_dev_index(d) - 1)))
2044 		return (ENXIO);
2045 
2046 	if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
2047 	    (vr->ctrl >= odev->d_nctrl) ||
2048 	    ((ctrl = odev->d_ctrls[vr->ctrl]) == NULL)) {
2049 		return (EINVAL);
2050 	}
2051 	if ((rv = auclnt_control_read(ctrl, &v)) == 0) {
2052 		switch (odev->d_exts[vr->ctrl].type) {
2053 		case MIXT_ENUM:
2054 			/* translate this from an enum style bit mask */
2055 			vr->value = ddi_ffs((unsigned long)v) - 1;
2056 			break;
2057 		case MIXT_STEREOSLIDER:
2058 			vr->value = (int)ddi_swap16(v & 0xffff);
2059 			break;
2060 		case MIXT_MONOSLIDER:
2061 			vr->value = (int)(v | (v << 8));
2062 			break;
2063 		case MIXT_ONOFF:
2064 			/* this could be simple, or could be part of a multi */
2065 			if (odev->d_exts[vr->ctrl].enumbit >= 0) {
2066 				uint64_t mask;
2067 				mask = 1;
2068 				mask <<= (odev->d_exts[vr->ctrl].enumbit);
2069 				vr->value = (v & mask) ? 1 : 0;
2070 			} else {
2071 				vr->value = v ? 1 : 0;
2072 			}
2073 			break;
2074 
2075 		default:
2076 			vr->value = (int)v;
2077 			break;
2078 		}
2079 	}
2080 
2081 	return (rv);
2082 }
2083 
2084 static int
2085 sndctl_mix_write(audio_client_t *c, oss_mixer_value *vr)
2086 {
2087 	int			rv;
2088 	uint64_t		v;
2089 	audio_dev_t		*d;
2090 	audio_ctrl_t		*ctrl;
2091 	ossdev_t		*odev;
2092 
2093 	d = auclnt_get_dev(c);
2094 
2095 	if ((vr->dev != -1) && (vr->dev != (auclnt_get_dev_index(d) - 1)))
2096 		return (ENXIO);
2097 
2098 	if (((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) ||
2099 	    (vr->ctrl >= odev->d_nctrl) ||
2100 	    ((ctrl = odev->d_ctrls[vr->ctrl]) == NULL)) {
2101 		return (EINVAL);
2102 	}
2103 
2104 	switch (odev->d_exts[vr->ctrl].type) {
2105 	case MIXT_ONOFF:
2106 		/* this could be standalone, or it could be part of a multi */
2107 		if (odev->d_exts[vr->ctrl].enumbit >= 0) {
2108 			uint64_t mask;
2109 			if ((rv = auclnt_control_read(ctrl, &v)) != 0) {
2110 				return (EINVAL);
2111 			}
2112 			mask = 1;
2113 			mask <<= (odev->d_exts[vr->ctrl].enumbit);
2114 			if (vr->value) {
2115 				v |= mask;
2116 			} else {
2117 				v &= ~mask;
2118 			}
2119 		} else {
2120 			v = vr->value;
2121 		}
2122 		break;
2123 	case MIXT_ENUM:
2124 		/* translate this to an enum style bit mask */
2125 		v = 1U << vr->value;
2126 		break;
2127 	case MIXT_MONOSLIDER:
2128 		/* mask off high order bits */
2129 		v = vr->value & 0xff;
2130 		break;
2131 	case MIXT_STEREOSLIDER:
2132 		/* OSS uses reverse byte ordering */
2133 		v = vr->value;
2134 		v = ddi_swap16(vr->value & 0xffff);
2135 		break;
2136 	default:
2137 		v = vr->value;
2138 	}
2139 	rv = auclnt_control_write(ctrl, v);
2140 
2141 	return (rv);
2142 }
2143 
2144 static int
2145 sndctl_mix_nrmix(audio_client_t *c, int *nmixp)
2146 {
2147 	_NOTE(ARGUNUSED(c));
2148 	*nmixp = oss_cnt_devs() - 1;
2149 	return (0);
2150 }
2151 
2152 static int
2153 ossmix_ioctl(audio_client_t *c, int cmd, intptr_t arg, int mode, cred_t *credp,
2154     int *rvalp)
2155 {
2156 	int	sz;
2157 	void	*data;
2158 	int	rv = 0;
2159 
2160 	sz = OSSIOC_GETSZ(cmd);
2161 
2162 	if ((cmd & (OSSIOC_IN | OSSIOC_OUT)) && sz) {
2163 		if ((data = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
2164 			return (ENOMEM);
2165 		}
2166 	} else {
2167 		sz = 0;
2168 	}
2169 
2170 	if (cmd & OSSIOC_IN) {
2171 		if ((rv = ddi_copyin((void *)arg, data, sz, mode)) != 0) {
2172 			goto done;
2173 		}
2174 	}
2175 
2176 	switch (cmd) {
2177 		/*
2178 		 * Mixer specific ioctls
2179 		 */
2180 	case SNDCTL_MIX_NREXT:
2181 		rv = sndctl_mix_nrext(c, (int *)data);
2182 		break;
2183 	case SNDCTL_MIX_EXTINFO:
2184 		rv = sndctl_mix_extinfo(c, (oss_mixext *)data);
2185 		break;
2186 	case SNDCTL_MIX_ENUMINFO:
2187 		rv = sndctl_mix_enuminfo(c, (oss_mixer_enuminfo *)data);
2188 		break;
2189 	case SNDCTL_MIX_READ:
2190 		rv = sndctl_mix_read(c, (oss_mixer_value *)data);
2191 		break;
2192 	case SNDCTL_MIX_WRITE:
2193 		rv = sndctl_mix_write(c, (oss_mixer_value *)data);
2194 		break;
2195 	case SNDCTL_MIX_NRMIX:
2196 		rv = sndctl_mix_nrmix(c, (int *)data);
2197 		break;
2198 
2199 		/*
2200 		 * Legacy ioctls.  These are treated as soft values only,
2201 		 * and do not affect global hardware state.  For use by
2202 		 * legacy DSP applications.
2203 		 */
2204 	case SOUND_MIXER_READ_VOLUME:
2205 	case SOUND_MIXER_READ_PCM:
2206 	case SOUND_MIXER_READ_OGAIN:
2207 		rv = sndctl_dsp_getplayvol(c, (int *)data);
2208 		break;
2209 
2210 	case SOUND_MIXER_WRITE_VOLUME:
2211 	case SOUND_MIXER_WRITE_PCM:
2212 	case SOUND_MIXER_WRITE_OGAIN:
2213 		rv = sound_mixer_write_ogain(c, (int *)data);
2214 		break;
2215 
2216 	case SOUND_MIXER_READ_RECGAIN:
2217 	case SOUND_MIXER_READ_RECLEV:
2218 	case SOUND_MIXER_READ_IGAIN:
2219 		rv = sndctl_dsp_getrecvol(c, (int *)data);
2220 		break;
2221 
2222 	case SOUND_MIXER_WRITE_RECGAIN:
2223 	case SOUND_MIXER_WRITE_RECLEV:
2224 	case SOUND_MIXER_WRITE_IGAIN:
2225 		rv = sound_mixer_write_igain(c, (int *)data);
2226 		break;
2227 
2228 	case SOUND_MIXER_READ_RECSRC:
2229 	case SOUND_MIXER_WRITE_RECSRC:
2230 		rv = sound_mixer_read_recsrc(c, (int *)data);
2231 		break;
2232 
2233 	case SOUND_MIXER_READ_DEVMASK:
2234 	case SOUND_MIXER_READ_STEREODEVS:
2235 		rv = sound_mixer_read_devmask(c, (int *)data);
2236 		break;
2237 
2238 	case SOUND_MIXER_READ_RECMASK:
2239 		rv = sound_mixer_read_recmask(c, (int *)data);
2240 		break;
2241 
2242 		/*
2243 		 * Common ioctls shared with DSP
2244 		 */
2245 	case OSS_GETVERSION:
2246 		rv = oss_getversion((int *)data);
2247 		break;
2248 
2249 	case SNDCTL_CARDINFO:
2250 		rv = sndctl_cardinfo(c, (oss_card_info *)data);
2251 		break;
2252 
2253 	case SNDCTL_ENGINEINFO:
2254 	case SNDCTL_AUDIOINFO:
2255 	case SNDCTL_AUDIOINFO_EX:
2256 		rv = sndctl_audioinfo(c, (oss_audioinfo *)data);
2257 		break;
2258 
2259 	case SNDCTL_SYSINFO:
2260 		rv = sndctl_sysinfo((oss_sysinfo *)data);
2261 		break;
2262 
2263 	case SNDCTL_MIXERINFO:
2264 		rv = sndctl_mixerinfo(c, (oss_mixerinfo *)data);
2265 		break;
2266 
2267 	case SOUND_MIXER_INFO:
2268 		rv = sound_mixer_info(c, (mixer_info *)data);
2269 		break;
2270 
2271 	case SNDCTL_MIX_DESCRIPTION:	/* NOT SUPPORTED: tooltip */
2272 		rv = EIO;	/* OSS returns EIO for this one */
2273 		break;
2274 
2275 		/*
2276 		 * Special implementation-private ioctls.
2277 		 */
2278 	case SNDCTL_SUN_SEND_NUMBER:
2279 		rv = sndctl_sun_send_number(c, (int *)data, credp);
2280 		break;
2281 
2282 		/*
2283 		 * Legacy ioctls we don't support.
2284 		 */
2285 	case SOUND_MIXER_WRITE_MONGAIN:
2286 	case SOUND_MIXER_READ_MONGAIN:
2287 	case SOUND_MIXER_READ_BASS:
2288 	case SOUND_MIXER_READ_TREBLE:
2289 	case SOUND_MIXER_READ_SPEAKER:
2290 	case SOUND_MIXER_READ_LINE:
2291 	case SOUND_MIXER_READ_MIC:
2292 	case SOUND_MIXER_READ_CD:
2293 	case SOUND_MIXER_READ_IMIX:
2294 	case SOUND_MIXER_READ_ALTPCM:
2295 	case SOUND_MIXER_READ_SYNTH:
2296 	case SOUND_MIXER_READ_LINE1:
2297 	case SOUND_MIXER_READ_LINE2:
2298 	case SOUND_MIXER_READ_LINE3:
2299 	case SOUND_MIXER_WRITE_BASS:
2300 	case SOUND_MIXER_WRITE_TREBLE:
2301 	case SOUND_MIXER_WRITE_SPEAKER:
2302 	case SOUND_MIXER_WRITE_LINE:
2303 	case SOUND_MIXER_WRITE_MIC:
2304 	case SOUND_MIXER_WRITE_CD:
2305 	case SOUND_MIXER_WRITE_IMIX:
2306 	case SOUND_MIXER_WRITE_ALTPCM:
2307 	case SOUND_MIXER_WRITE_SYNTH:
2308 	case SOUND_MIXER_WRITE_LINE1:
2309 	case SOUND_MIXER_WRITE_LINE2:
2310 	case SOUND_MIXER_WRITE_LINE3:
2311 		/*
2312 		 * Additional ioctls we *could* support, but don't.
2313 		 */
2314 	case SNDCTL_SETSONG:
2315 	case SNDCTL_SETLABEL:
2316 	case SNDCTL_GETSONG:
2317 	case SNDCTL_GETLABEL:
2318 	case SNDCTL_MIDIINFO:
2319 	case SNDCTL_SETNAME:
2320 	default:
2321 		rv = EINVAL;
2322 		break;
2323 	}
2324 
2325 	if ((rv == 0) && (cmd & OSSIOC_OUT)) {
2326 		rv = ddi_copyout(data, (void *)arg, sz, mode);
2327 	}
2328 	if (rv == 0) {
2329 		*rvalp = 0;
2330 	}
2331 
2332 done:
2333 	if (sz) {
2334 		kmem_free(data, sz);
2335 	}
2336 	return (rv);
2337 }
2338 
2339 static void *
2340 oss_dev_init(audio_dev_t *dev)
2341 {
2342 	ossdev_t	*odev;
2343 
2344 	odev = kmem_zalloc(sizeof (*odev), KM_SLEEP);
2345 	odev->d_dev = dev;
2346 
2347 	mutex_init(&odev->d_mx, NULL, MUTEX_DRIVER, NULL);
2348 	cv_init(&odev->d_cv, NULL, CV_DRIVER, NULL);
2349 	oss_alloc_controls(odev);
2350 
2351 	return (odev);
2352 }
2353 
2354 static void
2355 oss_dev_fini(void *arg)
2356 {
2357 	ossdev_t	*odev = arg;
2358 
2359 	if (odev != NULL) {
2360 		oss_free_controls(odev);
2361 		mutex_destroy(&odev->d_mx);
2362 		cv_destroy(&odev->d_cv);
2363 		kmem_free(odev, sizeof (*odev));
2364 	}
2365 }
2366 
2367 static void
2368 sndstat_printf(ossclient_t *oc, const char *fmt, ...)
2369 {
2370 	va_list	va;
2371 
2372 	va_start(va, fmt);
2373 	(void) vsnprintf(oc->o_ss_buf + oc->o_ss_len,
2374 	    oc->o_ss_sz - oc->o_ss_len, fmt, va);
2375 	va_end(va);
2376 	oc->o_ss_len = strlen(oc->o_ss_buf);
2377 }
2378 
2379 static int
2380 sndstat_dev_walker(audio_dev_t *d, void *arg)
2381 {
2382 	ossclient_t	*oc = arg;
2383 	const char	*capstr;
2384 	unsigned	cap;
2385 
2386 	cap = auclnt_get_dev_capab(d);
2387 
2388 	if (cap	& AUDIO_CLIENT_CAP_DUPLEX) {
2389 		capstr = "DUPLEX";
2390 	} else if ((cap & AUDIO_CLIENT_CAP_PLAY) &&
2391 	    (cap & AUDIO_CLIENT_CAP_RECORD)) {
2392 		capstr = "INPUT,OUTPUT";
2393 	} else if (cap & AUDIO_CLIENT_CAP_PLAY) {
2394 		capstr = "OUTPUT";
2395 	} else if (cap & AUDIO_CLIENT_CAP_RECORD) {
2396 		capstr = "INPUT";
2397 	} else {
2398 		capstr = NULL;
2399 	}
2400 
2401 	if (capstr == NULL)
2402 		return (AUDIO_WALK_CONTINUE);
2403 
2404 	sndstat_printf(oc, "%d: %s %s, %s (%s)\n",
2405 	    auclnt_get_dev_number(d), auclnt_get_dev_name(d),
2406 	    auclnt_get_dev_description(d), auclnt_get_dev_version(d), capstr);
2407 
2408 	return (AUDIO_WALK_CONTINUE);
2409 }
2410 
2411 static int
2412 sndstat_mixer_walker(audio_dev_t *d, void *arg)
2413 {
2414 	ossclient_t	*oc = arg;
2415 	unsigned	cap;
2416 	void		*iter;
2417 	const char	*info;
2418 
2419 	cap = auclnt_get_dev_capab(d);
2420 
2421 	if ((cap & (AUDIO_CLIENT_CAP_PLAY|AUDIO_CLIENT_CAP_RECORD)) == 0)
2422 		return (AUDIO_WALK_CONTINUE);
2423 
2424 	sndstat_printf(oc, "%d: %s %s, %s\n",
2425 	    auclnt_get_dev_number(d), auclnt_get_dev_name(d),
2426 	    auclnt_get_dev_description(d), auclnt_get_dev_version(d));
2427 	iter = NULL;
2428 	while ((info = auclnt_get_dev_hw_info(d, &iter)) != NULL) {
2429 		sndstat_printf(oc, "\t%s\n", info);
2430 	}
2431 	return (AUDIO_WALK_CONTINUE);
2432 }
2433 
2434 static int
2435 ossmix_write(audio_client_t *c, struct uio *uio, cred_t *cr)
2436 {
2437 	/* write on sndstat is a no-op */
2438 	_NOTE(ARGUNUSED(c));
2439 	_NOTE(ARGUNUSED(uio));
2440 	_NOTE(ARGUNUSED(cr));
2441 
2442 	return (0);
2443 }
2444 
2445 static int
2446 ossmix_read(audio_client_t *c, struct uio *uio, cred_t *cr)
2447 {
2448 	ossclient_t	*oc;
2449 	unsigned	n;
2450 	int		rv;
2451 
2452 	_NOTE(ARGUNUSED(cr));
2453 
2454 	if (uio->uio_resid == 0) {
2455 		return (0);
2456 	}
2457 
2458 	oc = auclnt_get_private(c);
2459 
2460 	mutex_enter(&oc->o_ss_lock);
2461 
2462 	if (oc->o_ss_off == 0) {
2463 
2464 		sndstat_printf(oc, "SunOS Audio Framework\n");
2465 
2466 		sndstat_printf(oc, "\nAudio Devices:\n");
2467 		auclnt_walk_devs_by_number(sndstat_dev_walker, oc);
2468 
2469 		sndstat_printf(oc, "\nMixers:\n");
2470 		auclnt_walk_devs_by_number(sndstat_mixer_walker, oc);
2471 	}
2472 
2473 	/*
2474 	 * For simplicity's sake, we implement a non-seekable device.  We could
2475 	 * support seekability, but offsets would be rather meaningless between
2476 	 * changes.
2477 	 */
2478 	n = min(uio->uio_resid, (oc->o_ss_len - oc->o_ss_off));
2479 
2480 	rv = uiomove(oc->o_ss_buf + oc->o_ss_off, n, UIO_READ, uio);
2481 	if (rv != 0) {
2482 		n = 0;
2483 	}
2484 	oc->o_ss_off += n;
2485 
2486 	if (n == 0) {
2487 		/*
2488 		 * end-of-file reached... clear the sndstat buffer so that
2489 		 * subsequent reads will get the latest data.
2490 		 */
2491 		oc->o_ss_off = oc->o_ss_len = 0;
2492 	}
2493 	mutex_exit(&oc->o_ss_lock);
2494 	return (rv);
2495 }
2496 
2497 int
2498 oss_read(audio_client_t *c, struct uio *uio, cred_t *cr)
2499 {
2500 	_NOTE(ARGUNUSED(cr));
2501 
2502 	auclnt_clear_paused(auclnt_input_stream(c));
2503 
2504 	return (auclnt_read(c, uio));
2505 }
2506 
2507 int
2508 oss_write(audio_client_t *c, struct uio *uio, cred_t *cr)
2509 {
2510 	_NOTE(ARGUNUSED(cr));
2511 
2512 	auclnt_clear_paused(auclnt_output_stream(c));
2513 
2514 	return (auclnt_write(c, uio));
2515 }
2516 
2517 int
2518 oss_chpoll(audio_client_t *c, short events, int anyyet, short *reventsp,
2519     struct pollhead **phpp)
2520 {
2521 	return (auclnt_chpoll(c, events, anyyet, reventsp, phpp));
2522 }
2523 
2524 static struct audio_client_ops oss_ops = {
2525 	"sound,dsp",
2526 	oss_dev_init,
2527 	oss_dev_fini,
2528 	oss_open,
2529 	oss_close,
2530 	oss_read,
2531 	oss_write,
2532 	oss_ioctl,
2533 	oss_chpoll,
2534 	NULL,		/* mmap */
2535 	oss_input,
2536 	oss_output,
2537 	NULL,		/* notify */
2538 	NULL,		/* drain */
2539 };
2540 
2541 static struct audio_client_ops ossmix_ops = {
2542 	"sound,mixer",
2543 	NULL,
2544 	NULL,
2545 	ossmix_open,
2546 	ossmix_close,
2547 	ossmix_read,
2548 	ossmix_write,
2549 	ossmix_ioctl,
2550 	NULL,	/* chpoll */
2551 	NULL,   /* mmap */
2552 	NULL,	/* input */
2553 	NULL,   /* output */
2554 	oss_notify,
2555 	NULL,	/* drain */
2556 };
2557 
2558 /* nearly the same as ossxmix; different minor name helps devfsadm */
2559 static struct audio_client_ops sndstat_ops = {
2560 	"sound,sndstat",
2561 	NULL,	/* dev_init */
2562 	NULL,	/* dev_fini */
2563 	ossmix_open,
2564 	ossmix_close,
2565 	ossmix_read,
2566 	ossmix_write,
2567 	ossmix_ioctl,
2568 	NULL,	/* chpoll */
2569 	NULL,	/* mmap */
2570 	NULL,	/* input */
2571 	NULL,	/* output */
2572 	NULL,	/* notify */
2573 	NULL,	/* drain */
2574 };
2575 
2576 void
2577 auimpl_oss_init(void)
2578 {
2579 	auclnt_register_ops(AUDIO_MINOR_DSP, &oss_ops);
2580 	auclnt_register_ops(AUDIO_MINOR_MIXER, &ossmix_ops);
2581 	auclnt_register_ops(AUDIO_MINOR_SNDSTAT, &sndstat_ops);
2582 }
2583