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