xref: /illumos-gate/usr/src/uts/common/io/audio/impl/audio_client.c (revision 0d166b18feda26f6f45f5be1c0c8c5e539b90e6c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (C) 4Front Technologies 1996-2008.
23  *
24  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #include <sys/types.h>
29 #include <sys/sysmacros.h>
30 #include <sys/list.h>
31 #include <sys/file.h>
32 #include <sys/open.h>
33 #include <sys/stat.h>
34 #include <sys/errno.h>
35 #include <sys/atomic.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 
39 #include "audio_impl.h"
40 
41 /*
42  * Audio Client implementation.
43  */
44 
45 /*
46  * Attenuation table for dB->linear conversion. Indexed in steps of
47  * 0.5 dB.  Table size is 25 dB (first entry is handled as mute).
48  *
49  * Notably, the last item in table is taken as 0 dB (i.e. maximum volume).
50  *
51  * Table contents can be calculated as follows (requires sunmath library):
52  *
53  * scale = AUDIO_VOL_SCALE;
54  * for (i = -50; i <= 0; i++) {
55  *     x = exp10(0.05 * i);
56  *     printf("%d: %f %.0f\n", i,  x, trunc(x * scale));
57  * }
58  *
59  */
60 
61 static const uint16_t auimpl_db_table[AUDIO_DB_SIZE + 1] = {
62 	0,   0,   1,   1,   1,   1,   1,   1,   2,   2,
63 	2,   2,   3,   3,   4,   4,   5,   5,   6,   7,
64 	8,   9,   10,  11,  12,  14,  16,  18,  20,  22,
65 	25,  28,  32,  36,  40,  45,  51,  57,  64,  72,
66 	80,  90,  101, 114, 128, 143, 161, 181, 203, 228,
67 	256
68 };
69 
70 static list_t			auimpl_clients;
71 static krwlock_t		auimpl_client_lock;
72 static audio_client_ops_t	*audio_client_ops[AUDIO_MN_TYPE_MASK + 1];
73 
74 void *
75 auclnt_get_private(audio_client_t *c)
76 {
77 	return (c->c_private);
78 }
79 
80 void
81 auclnt_set_private(audio_client_t *c, void *private)
82 {
83 	c->c_private = private;
84 }
85 
86 int
87 auclnt_set_rate(audio_stream_t *sp, int rate)
88 {
89 	audio_parms_t	parms;
90 	int		rv = 0;
91 
92 	/* basic sanity checks! */
93 	if ((rate < 5000) || (rate > 192000)) {
94 		return (EINVAL);
95 	}
96 	mutex_enter(&sp->s_lock);
97 	parms = *sp->s_user_parms;
98 	if (rate != parms.p_rate) {
99 		parms.p_rate = rate;
100 		rv = auimpl_format_setup(sp, &parms);
101 	}
102 	mutex_exit(&sp->s_lock);
103 	return (rv);
104 }
105 
106 int
107 auclnt_get_rate(audio_stream_t *sp)
108 {
109 	return (sp->s_user_parms->p_rate);
110 }
111 
112 unsigned
113 auclnt_get_fragsz(audio_stream_t *sp)
114 {
115 	return (sp->s_fragbytes);
116 }
117 
118 unsigned
119 auclnt_get_framesz(audio_stream_t *sp)
120 {
121 	return (sp->s_framesz);
122 }
123 
124 unsigned
125 auclnt_get_nfrags(audio_stream_t *sp)
126 {
127 	return (sp->s_nfrags);
128 }
129 
130 unsigned
131 auclnt_get_nframes(audio_stream_t *sp)
132 {
133 	return (sp->s_nframes);
134 }
135 
136 void
137 auclnt_set_latency(audio_stream_t *sp, unsigned frags, unsigned bytes)
138 {
139 	mutex_enter(&sp->s_lock);
140 	sp->s_hintfrags = (uint16_t)frags;
141 	sp->s_hintsz = bytes;
142 	mutex_exit(&sp->s_lock);
143 }
144 
145 uint64_t
146 auclnt_get_head(audio_stream_t *sp)
147 {
148 	return (sp->s_head);
149 }
150 
151 uint64_t
152 auclnt_get_tail(audio_stream_t *sp)
153 {
154 	return (sp->s_tail);
155 }
156 
157 unsigned
158 auclnt_get_hidx(audio_stream_t *sp)
159 {
160 	return (sp->s_hidx);
161 }
162 
163 unsigned
164 auclnt_get_tidx(audio_stream_t *sp)
165 {
166 	return (sp->s_tidx);
167 }
168 
169 audio_stream_t *
170 auclnt_input_stream(audio_client_t *c)
171 {
172 	return (&c->c_istream);
173 }
174 
175 audio_stream_t *
176 auclnt_output_stream(audio_client_t *c)
177 {
178 	return (&c->c_ostream);
179 }
180 
181 unsigned
182 auclnt_get_count(audio_stream_t *sp)
183 {
184 	unsigned	count;
185 
186 	mutex_enter(&sp->s_lock);
187 	ASSERT((sp->s_head - sp->s_tail) <= sp->s_nframes);
188 	count = (unsigned)(sp->s_head - sp->s_tail);
189 	mutex_exit(&sp->s_lock);
190 
191 	return (count);
192 }
193 
194 unsigned
195 auclnt_consume(audio_stream_t *sp, unsigned n)
196 {
197 	mutex_enter(&sp->s_lock);
198 
199 	ASSERT(sp == &sp->s_client->c_istream);
200 	n = max(n, sp->s_head - sp->s_tail);
201 	sp->s_tail += n;
202 	sp->s_tidx += n;
203 	if (sp->s_tidx >= sp->s_nframes) {
204 		sp->s_tidx -= sp->s_nframes;
205 	}
206 
207 	ASSERT(sp->s_tail <= sp->s_head);
208 	ASSERT(sp->s_hidx < sp->s_nframes);
209 
210 	mutex_exit(&sp->s_lock);
211 
212 	return (n);
213 }
214 
215 unsigned
216 auclnt_consume_data(audio_stream_t *sp, caddr_t dst, unsigned n)
217 {
218 	unsigned nframes;
219 	unsigned framesz;
220 	unsigned cnt;
221 	caddr_t	data;
222 
223 	mutex_enter(&sp->s_lock);
224 
225 	nframes = sp->s_nframes;
226 	framesz = sp->s_framesz;
227 
228 	ASSERT(sp == &sp->s_client->c_istream);
229 	ASSERT(sp->s_head >= sp->s_tail);
230 	ASSERT(sp->s_tidx < nframes);
231 	ASSERT(sp->s_hidx < nframes);
232 
233 	cnt = n = min(n, sp->s_head - sp->s_tail);
234 	data = sp->s_data + (sp->s_tidx * framesz);
235 	do {
236 		unsigned nf, nb;
237 
238 		nf = min(nframes - sp->s_tidx, n);
239 		nb = nf * framesz;
240 
241 		bcopy(data, dst, nb);
242 		dst += nb;
243 		data += nb;
244 
245 		n -= nf;
246 		sp->s_tail += nf;
247 		sp->s_tidx += nf;
248 		if (sp->s_tidx == nframes) {
249 			sp->s_tidx = 0;
250 			data = sp->s_data;
251 		}
252 	} while (n);
253 
254 	ASSERT(sp->s_tail <= sp->s_head);
255 	ASSERT(sp->s_tidx < nframes);
256 
257 	mutex_exit(&sp->s_lock);
258 
259 	return (cnt);
260 }
261 
262 unsigned
263 auclnt_produce(audio_stream_t *sp, unsigned n)
264 {
265 	mutex_enter(&sp->s_lock);
266 
267 	ASSERT(sp == &sp->s_client->c_ostream);
268 	n = max(n, sp->s_nframes - (sp->s_head - sp->s_tail));
269 	sp->s_head += n;
270 	sp->s_hidx += n;
271 	if (sp->s_hidx >= sp->s_nframes) {
272 		sp->s_hidx -= sp->s_nframes;
273 	}
274 
275 	ASSERT(sp->s_tail <= sp->s_head);
276 	ASSERT(sp->s_hidx < sp->s_nframes);
277 
278 	mutex_exit(&sp->s_lock);
279 
280 	return (n);
281 }
282 
283 unsigned
284 auclnt_produce_data(audio_stream_t *sp, caddr_t src, unsigned n)
285 {
286 	unsigned nframes;
287 	unsigned framesz;
288 	unsigned cnt;
289 	caddr_t data;
290 
291 	mutex_enter(&sp->s_lock);
292 
293 	nframes = sp->s_nframes;
294 	framesz = sp->s_framesz;
295 
296 	ASSERT(sp == &sp->s_client->c_ostream);
297 	ASSERT(sp->s_head >= sp->s_tail);
298 	ASSERT(sp->s_tidx < nframes);
299 	ASSERT(sp->s_hidx < nframes);
300 
301 	cnt = n = min(n, nframes - (sp->s_head - sp->s_tail));
302 	data = sp->s_data + (sp->s_hidx * framesz);
303 	do {
304 		unsigned nf, nb;
305 
306 		nf = min(nframes - sp->s_hidx, n);
307 		nb = nf * framesz;
308 
309 		bcopy(src, data, nb);
310 
311 		src += nb;
312 		data += nb;
313 
314 		n -= nf;
315 		sp->s_head += nf;
316 		sp->s_hidx += nf;
317 		if (sp->s_hidx == nframes) {
318 			sp->s_hidx = 0;
319 			data = sp->s_data;
320 		}
321 	} while (n);
322 
323 	ASSERT(sp->s_tail <= sp->s_head);
324 	ASSERT(sp->s_hidx < nframes);
325 
326 	mutex_exit(&sp->s_lock);
327 
328 	return (cnt);
329 }
330 
331 int
332 auclnt_read(audio_client_t *c, struct uio *uio)
333 {
334 	audio_stream_t	*sp = &c->c_istream;
335 	unsigned	cnt;
336 	int		rv = 0;
337 	offset_t	loff;
338 	int		eagain;
339 
340 	loff = uio->uio_loffset;
341 	eagain = EAGAIN;
342 
343 	mutex_enter(&sp->s_lock);
344 
345 	if ((!sp->s_paused) && (!sp->s_running)) {
346 		mutex_exit(&sp->s_lock);
347 		auclnt_start(sp);
348 		mutex_enter(&sp->s_lock);
349 	}
350 
351 	ASSERT(sp->s_head >= sp->s_tail);
352 	ASSERT(sp->s_tidx < sp->s_nframes);
353 	ASSERT(sp->s_hidx < sp->s_nframes);
354 
355 	while (uio->uio_resid >= sp->s_framesz) {
356 
357 		while ((cnt = (sp->s_head - sp->s_tail)) == 0) {
358 			if (uio->uio_fmode & (FNONBLOCK|FNDELAY)) {
359 				mutex_exit(&sp->s_lock);
360 				return (eagain);
361 			}
362 			if (cv_wait_sig(&sp->s_cv, &sp->s_lock) == 0) {
363 				mutex_exit(&sp->s_lock);
364 				return (EINTR);
365 			}
366 		}
367 
368 		cnt = min(cnt, sp->s_nframes - sp->s_tidx);
369 		cnt = min(cnt, (uio->uio_resid / sp->s_framesz));
370 
371 		rv = uiomove(sp->s_data + (sp->s_tidx * sp->s_framesz),
372 		    cnt * sp->s_framesz, UIO_READ, uio);
373 		uio->uio_loffset = loff;
374 		eagain = 0;
375 
376 		if (rv != 0) {
377 			mutex_exit(&sp->s_lock);
378 			return (rv);
379 		}
380 
381 		sp->s_tail += cnt;
382 		sp->s_tidx += cnt;
383 		if (sp->s_tidx == sp->s_nframes) {
384 			sp->s_tidx = 0;
385 		}
386 	}
387 
388 	ASSERT(sp->s_tail <= sp->s_head);
389 	ASSERT(sp->s_tidx < sp->s_nframes);
390 
391 	/* round off any remaining partial bits */
392 	uio->uio_resid = 0;
393 
394 	mutex_exit(&sp->s_lock);
395 
396 	return (rv);
397 }
398 
399 int
400 auclnt_write(audio_client_t *c, struct uio *uio)
401 {
402 	audio_stream_t *sp = &c->c_ostream;
403 	unsigned	cnt;
404 	int		rv = 0;
405 	offset_t	loff;
406 	int		eagain;
407 
408 	loff = uio->uio_loffset;
409 	eagain = EAGAIN;
410 
411 	mutex_enter(&sp->s_lock);
412 
413 	ASSERT(sp->s_head >= sp->s_tail);
414 	ASSERT(sp->s_tidx < sp->s_nframes);
415 	ASSERT(sp->s_hidx < sp->s_nframes);
416 
417 	while (uio->uio_resid >= sp->s_framesz) {
418 
419 		while ((cnt = sp->s_nframes - (sp->s_head - sp->s_tail)) == 0) {
420 			if (uio->uio_fmode & (FNONBLOCK|FNDELAY)) {
421 				mutex_exit(&sp->s_lock);
422 				return (eagain);
423 			}
424 			if (cv_wait_sig(&sp->s_cv, &sp->s_lock) == 0) {
425 				mutex_exit(&sp->s_lock);
426 				return (EINTR);
427 			}
428 		}
429 
430 		cnt = min(cnt, sp->s_nframes - sp->s_hidx);
431 		cnt = min(cnt, (uio->uio_resid / sp->s_framesz));
432 
433 		rv = uiomove(sp->s_data + (sp->s_hidx * sp->s_framesz),
434 		    cnt * sp->s_framesz, UIO_WRITE, uio);
435 		uio->uio_loffset = loff;
436 		eagain = 0;
437 
438 		if (rv != 0) {
439 			mutex_exit(&sp->s_lock);
440 			return (rv);
441 		}
442 
443 		sp->s_head += cnt;
444 		sp->s_hidx += cnt;
445 		if (sp->s_hidx == sp->s_nframes) {
446 			sp->s_hidx = 0;
447 		}
448 
449 		if ((!sp->s_paused) && (!sp->s_running) &&
450 		    ((sp->s_head - sp->s_tail) > sp->s_fragfr)) {
451 			mutex_exit(&sp->s_lock);
452 			auclnt_start(sp);
453 			mutex_enter(&sp->s_lock);
454 		}
455 	}
456 
457 	ASSERT(sp->s_tail <= sp->s_head);
458 	ASSERT(sp->s_hidx < sp->s_nframes);
459 
460 	/* round off any remaining partial bits */
461 	uio->uio_resid = 0;
462 
463 	mutex_exit(&sp->s_lock);
464 
465 	return (rv);
466 }
467 
468 int
469 auclnt_chpoll(audio_client_t *c, short events, int anyyet, short *reventsp,
470     struct pollhead **phpp)
471 {
472 	audio_stream_t	*sp;
473 	short nev = 0;
474 
475 	if (events & (POLLIN | POLLRDNORM)) {
476 		sp = &c->c_istream;
477 		mutex_enter(&sp->s_lock);
478 		if ((sp->s_head - sp->s_tail) > sp->s_fragfr) {
479 			nev = POLLIN | POLLRDNORM;
480 		}
481 		mutex_exit(&sp->s_lock);
482 	}
483 
484 	if (events & POLLOUT) {
485 		sp = &c->c_ostream;
486 		mutex_enter(&sp->s_lock);
487 		if ((sp->s_nframes - (sp->s_head - sp->s_tail)) >
488 		    sp->s_fragfr) {
489 			nev = POLLOUT;
490 		}
491 		mutex_exit(&sp->s_lock);
492 	}
493 
494 	if (nev) {
495 		*reventsp = nev & events;
496 	} else {
497 		*reventsp = 0;
498 		if (!anyyet) {
499 			*phpp = &c->c_pollhead;
500 		}
501 	}
502 	return (0);
503 }
504 
505 void
506 auclnt_pollwakeup(audio_client_t *c, short events)
507 {
508 	pollwakeup(&c->c_pollhead, events);
509 }
510 
511 void
512 auclnt_get_output_qlen(audio_client_t *c, unsigned *slen, unsigned *flen)
513 {
514 	audio_stream_t	*sp = &c->c_ostream;
515 	audio_engine_t	*e = sp->s_engine;
516 	uint64_t	el, sl;
517 	unsigned	cnt, er, sr;
518 
519 	if (e == NULL) {
520 		/* if no output engine, can't do it! */
521 		*slen = 0;
522 		*flen = 0;
523 		return;
524 	}
525 
526 	mutex_enter(&e->e_lock);
527 	mutex_enter(&sp->s_lock);
528 	if (e->e_ops.audio_engine_qlen != NULL) {
529 		el = ENG_QLEN(e) + (e->e_head - e->e_tail);
530 	} else {
531 		el = (e->e_head - e->e_tail);
532 	}
533 	er = e->e_rate;
534 	sl = sp->s_cnv_cnt;
535 	sr = sp->s_user_parms->p_rate;
536 	cnt = (unsigned)(sp->s_head - sp->s_tail);
537 	mutex_exit(&sp->s_lock);
538 	mutex_exit(&e->e_lock);
539 
540 	/* engine frames converted to stream rate, plus stream frames */
541 	*slen = cnt;
542 	*flen = ((unsigned)(((el * sr) / er) + sl));
543 }
544 
545 int
546 auclnt_set_format(audio_stream_t *sp, int fmt)
547 {
548 	audio_parms_t	parms;
549 	int		rv = 0;
550 
551 	/*
552 	 * AC3: If we select an AC3 format, then we have to allocate
553 	 * another engine.  Normally this will be an output only
554 	 * engine.  However, for now we aren't supporting AC3
555 	 * passthru.
556 	 */
557 
558 	switch (fmt) {
559 	case AUDIO_FORMAT_U8:
560 	case AUDIO_FORMAT_ULAW:
561 	case AUDIO_FORMAT_ALAW:
562 	case AUDIO_FORMAT_S8:
563 	case AUDIO_FORMAT_S16_LE:
564 	case AUDIO_FORMAT_S16_BE:
565 	case AUDIO_FORMAT_U16_LE:
566 	case AUDIO_FORMAT_U16_BE:
567 	case AUDIO_FORMAT_S24_LE:
568 	case AUDIO_FORMAT_S24_BE:
569 	case AUDIO_FORMAT_S32_LE:
570 	case AUDIO_FORMAT_S32_BE:
571 	case AUDIO_FORMAT_S24_PACKED:
572 		break;
573 
574 	case AUDIO_FORMAT_AC3:		/* AC3: PASSTHRU */
575 	default:
576 		return (ENOTSUP);
577 	}
578 
579 
580 	mutex_enter(&sp->s_lock);
581 	parms = *sp->s_user_parms;
582 
583 	/*
584 	 * Optimization.  Some personalities send us the same format
585 	 * over and over again.  (Sun personality does this
586 	 * repeatedly.)  setup_src is potentially expensive, so we
587 	 * avoid doing it unless we really need to.
588 	 */
589 	if (fmt != parms.p_format) {
590 		/*
591 		 * Note that setting the format doesn't check that the
592 		 * audio streams have been paused.  As a result, any
593 		 * data still playing or recording will probably get
594 		 * misinterpreted.  It would be smart if the client
595 		 * application paused/stopped playback before changing
596 		 * formats.
597 		 */
598 		parms.p_format = fmt;
599 		rv = auimpl_format_setup(sp, &parms);
600 	}
601 	mutex_exit(&sp->s_lock);
602 
603 	return (rv);
604 }
605 
606 int
607 auclnt_get_format(audio_stream_t *sp)
608 {
609 	return (sp->s_user_parms->p_format);
610 }
611 
612 int
613 auclnt_get_output_format(audio_client_t *c)
614 {
615 	return (c->c_ostream.s_user_parms->p_format);
616 }
617 
618 int
619 auclnt_get_input_format(audio_client_t *c)
620 {
621 	return (c->c_istream.s_user_parms->p_format);
622 }
623 
624 int
625 auclnt_set_channels(audio_stream_t *sp, int nchan)
626 {
627 	audio_parms_t	parms;
628 	int		rv = 0;
629 
630 	/* Validate setting */
631 	if ((nchan > AUDIO_MAX_CHANNELS) || (nchan < 1)) {
632 		return (EINVAL);
633 	}
634 
635 	mutex_enter(&sp->s_lock);
636 	parms = *sp->s_user_parms;
637 	if (nchan != parms.p_nchan) {
638 		parms.p_nchan = nchan;
639 		rv = auimpl_format_setup(sp, &parms);
640 	}
641 	mutex_exit(&sp->s_lock);
642 
643 	return (rv);
644 }
645 
646 int
647 auclnt_get_channels(audio_stream_t *sp)
648 {
649 	return (sp->s_user_parms->p_nchan);
650 }
651 
652 
653 static void
654 auimpl_set_gain_master(audio_stream_t *sp, uint8_t gain)
655 {
656 	uint32_t	scaled;
657 
658 	if (gain > 100) {
659 		gain = 0;
660 	}
661 
662 	mutex_enter(&sp->s_lock);
663 	if (sp->s_gain_master == gain) {
664 		mutex_exit(&sp->s_lock);
665 		return;
666 	}
667 
668 	/*
669 	 * calculate the scaled values.  Done now to avoid calculations
670 	 * later.
671 	 */
672 	scaled = (gain * sp->s_gain_pct * AUDIO_DB_SIZE) / (100 * 100);
673 
674 	sp->s_gain_master = gain;
675 	sp->s_gain_scaled = auimpl_db_table[scaled];
676 
677 	if (!sp->s_muted) {
678 		sp->s_gain_eff = sp->s_gain_scaled;
679 	}
680 	mutex_exit(&sp->s_lock);
681 }
682 
683 int
684 auimpl_set_pcmvol(void *arg, uint64_t val)
685 {
686 	audio_dev_t	*d = arg;
687 	list_t		*l = &d->d_clients;
688 	audio_client_t	*c;
689 
690 	if (val > 100) {
691 		return (EINVAL);
692 	}
693 	rw_enter(&auimpl_client_lock, RW_WRITER);
694 	d->d_pcmvol = val & 0xff;
695 	rw_downgrade(&auimpl_client_lock);
696 
697 	for (c = list_head(l); c; c = list_next(l, c)) {
698 		/* don't need to check is_active here, its safe */
699 		auimpl_set_gain_master(&c->c_ostream, (uint8_t)val);
700 	}
701 	rw_exit(&auimpl_client_lock);
702 
703 	return (0);
704 }
705 
706 int
707 auimpl_get_pcmvol(void *arg, uint64_t *val)
708 {
709 	audio_dev_t	*d = arg;
710 
711 	*val = d->d_pcmvol;
712 	return (0);
713 }
714 
715 void
716 auclnt_set_gain(audio_stream_t *sp, uint8_t gain)
717 {
718 	uint32_t	scaled;
719 
720 	if (gain > 100) {
721 		gain = 0;
722 	}
723 
724 	mutex_enter(&sp->s_lock);
725 
726 	/* if no change, don't bother doing updates */
727 	if (sp->s_gain_pct == gain) {
728 		mutex_exit(&sp->s_lock);
729 		return;
730 	}
731 
732 	/*
733 	 * calculate the scaled values.  Done now to avoid calculations
734 	 * later.
735 	 */
736 	scaled = (gain * sp->s_gain_master * AUDIO_DB_SIZE) / (100 * 100);
737 
738 	sp->s_gain_pct = gain;
739 	sp->s_gain_scaled = auimpl_db_table[scaled];
740 
741 	if (!sp->s_muted) {
742 		sp->s_gain_eff = sp->s_gain_scaled;
743 	}
744 	mutex_exit(&sp->s_lock);
745 
746 	atomic_inc_uint(&sp->s_client->c_dev->d_serial);
747 }
748 
749 uint8_t
750 auclnt_get_gain(audio_stream_t *sp)
751 {
752 	return (sp->s_gain_pct);
753 }
754 
755 void
756 auclnt_set_muted(audio_stream_t *sp, boolean_t muted)
757 {
758 	mutex_enter(&sp->s_lock);
759 
760 	/* if no work change, don't bother doing updates */
761 	if (sp->s_muted == muted) {
762 		mutex_exit(&sp->s_lock);
763 		return;
764 	}
765 
766 	sp->s_muted = muted;
767 	if (muted) {
768 		sp->s_gain_eff = 0;
769 	} else {
770 		sp->s_gain_eff = sp->s_gain_scaled;
771 	}
772 	mutex_exit(&sp->s_lock);
773 
774 	atomic_inc_uint(&sp->s_client->c_dev->d_serial);
775 }
776 
777 boolean_t
778 auclnt_get_muted(audio_stream_t *sp)
779 {
780 	return (sp->s_muted);
781 }
782 
783 boolean_t
784 auclnt_is_running(audio_stream_t *sp)
785 {
786 	return (sp->s_running);
787 }
788 
789 void
790 auclnt_start(audio_stream_t *sp)
791 {
792 	mutex_enter(&sp->s_lock);
793 	sp->s_running = B_TRUE;
794 	mutex_exit(&sp->s_lock);
795 }
796 
797 void
798 auclnt_stop(audio_stream_t *sp)
799 {
800 	mutex_enter(&sp->s_lock);
801 	/* if running, then stop it */
802 	if (sp->s_running) {
803 		sp->s_running = B_FALSE;
804 		/*
805 		 * if we stopped the engine, we might need to wake up
806 		 * a thread that is waiting for drain to complete.
807 		 */
808 		cv_broadcast(&sp->s_cv);
809 	}
810 	mutex_exit(&sp->s_lock);
811 }
812 
813 /*
814  * When pausing, no new data will be played after the most recently
815  * mixed samples have played.  However, the audio engine will continue
816  * to play (possibly just silence).
817  *
818  * Note that we don't reference count the device, or release/close the
819  * engine here.  Once fired up, the engine continues running unil it
820  * is closed.
821  */
822 void
823 auclnt_set_paused(audio_stream_t *sp)
824 {
825 	mutex_enter(&sp->s_lock);
826 	if (sp->s_paused) {
827 		mutex_exit(&sp->s_lock);
828 		return;
829 	}
830 	sp->s_paused = B_TRUE;
831 	mutex_exit(&sp->s_lock);
832 
833 	auclnt_stop(sp);
834 
835 	atomic_inc_uint(&sp->s_client->c_dev->d_serial);
836 }
837 
838 void
839 auclnt_clear_paused(audio_stream_t *sp)
840 {
841 	mutex_enter(&sp->s_lock);
842 	if (!sp->s_paused) {
843 		mutex_exit(&sp->s_lock);
844 		return;
845 	}
846 	sp->s_paused = B_FALSE;
847 	mutex_exit(&sp->s_lock);
848 }
849 
850 boolean_t
851 auclnt_is_paused(audio_stream_t *sp)
852 {
853 	return (sp->s_paused);
854 }
855 
856 void
857 auclnt_flush(audio_stream_t *sp)
858 {
859 	mutex_enter(&sp->s_lock);
860 	if (sp == &sp->s_client->c_ostream) {
861 		sp->s_tail = sp->s_head;
862 		sp->s_tidx = sp->s_hidx;
863 	} else {
864 		sp->s_head = sp->s_tail;
865 		sp->s_hidx = sp->s_tidx;
866 	}
867 	sp->s_cnv_cnt = 0;
868 	mutex_exit(&sp->s_lock);
869 }
870 
871 int
872 auclnt_get_oflag(audio_client_t *c)
873 {
874 	return (c->c_omode);
875 }
876 
877 /*
878  * These routines should not be accessed by client "personality"
879  * implementations, but are for private framework use only.
880  */
881 
882 void
883 auimpl_client_init(void)
884 {
885 	rw_init(&auimpl_client_lock, NULL, RW_DRIVER, NULL);
886 	list_create(&auimpl_clients, sizeof (struct audio_client),
887 	    offsetof(struct audio_client, c_global_linkage));
888 }
889 
890 void
891 auimpl_client_fini(void)
892 {
893 	rw_destroy(&auimpl_client_lock);
894 	list_destroy(&auimpl_clients);
895 }
896 
897 static int
898 auimpl_stream_init(audio_stream_t *sp, audio_client_t *c)
899 {
900 	mutex_init(&sp->s_lock, NULL, MUTEX_DRIVER, NULL);
901 	cv_init(&sp->s_cv, NULL, CV_DRIVER, NULL);
902 	sp->s_client = c;
903 
904 	if (sp == &c->c_ostream) {
905 		sp->s_user_parms = &sp->s_cnv_src_parms;
906 		sp->s_phys_parms = &sp->s_cnv_dst_parms;
907 		sp->s_engcap = ENGINE_OUTPUT_CAP;
908 	} else {
909 		ASSERT(sp == &c->c_istream);
910 		sp->s_user_parms = &sp->s_cnv_dst_parms;
911 		sp->s_phys_parms = &sp->s_cnv_src_parms;
912 		sp->s_engcap = ENGINE_INPUT_CAP;
913 	}
914 
915 	/* for now initialize conversion parameters */
916 	sp->s_src_quality = 3;	/* reasonable compromise for now */
917 	sp->s_cnv_dst_nchan = 2;
918 	sp->s_cnv_dst_format = AUDIO_FORMAT_S24_NE;
919 	sp->s_cnv_dst_rate = 48000;
920 	sp->s_cnv_src_nchan = 2;
921 	sp->s_cnv_src_format = AUDIO_FORMAT_S24_NE;
922 	sp->s_cnv_src_rate = 48000;
923 
924 	/* set volume/gain all the way up */
925 	sp->s_muted = B_FALSE;
926 	sp->s_gain_pct = 0;
927 	sp->s_gain_scaled = AUDIO_VOL_SCALE;
928 	sp->s_gain_eff = AUDIO_VOL_SCALE;
929 
930 	/*
931 	 * We have to start off with a reasonable buffer and
932 	 * interrupt configuration.
933 	 */
934 	sp->s_allocsz = 65536;
935 	sp->s_data = ddi_umem_alloc(sp->s_allocsz, DDI_UMEM_NOSLEEP,
936 	    &sp->s_cookie);
937 	if (sp->s_data == NULL) {
938 		sp->s_allocsz = 0;
939 		audio_dev_warn(c->c_dev, "ddi_umem_alloc failed");
940 		return (ENOMEM);
941 	}
942 	/* make sure no stale data left in stream */
943 	bzero(sp->s_data, sp->s_allocsz);
944 
945 	/*
946 	 * Allocate SRC and data conversion state.
947 	 */
948 	mutex_enter(&sp->s_lock);
949 	if (auimpl_format_alloc(sp) != 0) {
950 		mutex_exit(&sp->s_lock);
951 		return (ENOMEM);
952 	}
953 
954 	mutex_exit(&sp->s_lock);
955 
956 	return (0);
957 }
958 
959 
960 static void
961 audio_stream_fini(audio_stream_t *sp)
962 {
963 	auimpl_format_free(sp);
964 	if (sp->s_cnv_buf0)
965 		kmem_free(sp->s_cnv_buf0, sp->s_cnv_max);
966 	if (sp->s_cnv_buf1)
967 		kmem_free(sp->s_cnv_buf1, sp->s_cnv_max);
968 	mutex_destroy(&sp->s_lock);
969 	cv_destroy(&sp->s_cv);
970 	if (sp->s_data != NULL) {
971 		ddi_umem_free(sp->s_cookie);
972 		sp->s_data = NULL;
973 	}
974 }
975 
976 int
977 auclnt_start_drain(audio_client_t *c)
978 {
979 	audio_stream_t	*sp;
980 	int		rv;
981 
982 	sp = &c->c_ostream;
983 
984 	/* start an asynchronous drain operation. */
985 	mutex_enter(&sp->s_lock);
986 	if (sp->s_paused || !sp->s_running) {
987 		rv = EALREADY;
988 	} else {
989 		sp->s_draining = B_TRUE;
990 		rv = 0;
991 	}
992 	mutex_exit(&sp->s_lock);
993 	return (rv);
994 }
995 
996 int
997 auclnt_drain(audio_client_t *c)
998 {
999 	audio_stream_t	*sp;
1000 
1001 	sp = &c->c_ostream;
1002 
1003 	/*
1004 	 * Note: Drain logic will automatically "stop" the stream when
1005 	 * the drain threshold has been reached.  So all we have to do
1006 	 * is wait for the stream to stop.
1007 	 */
1008 	mutex_enter(&sp->s_lock);
1009 	sp->s_draining = B_TRUE;
1010 	while (sp->s_draining && sp->s_running && !sp->s_paused) {
1011 		if (cv_wait_sig(&sp->s_cv, &sp->s_lock) == 0) {
1012 			mutex_exit(&sp->s_lock);
1013 			return (EINTR);
1014 		}
1015 	}
1016 	mutex_exit(&sp->s_lock);
1017 	return (0);
1018 }
1019 
1020 audio_client_t *
1021 auimpl_client_create(dev_t dev)
1022 {
1023 	audio_client_ops_t	*ops;
1024 	audio_client_t		*c;
1025 	audio_client_t		*next;
1026 	list_t			*list = &auimpl_clients;
1027 	minor_t			minor;
1028 	audio_dev_t		*d;
1029 
1030 	/* validate minor number */
1031 	minor = getminor(dev) & AUDIO_MN_TYPE_MASK;
1032 	if ((ops = audio_client_ops[minor]) == NULL) {
1033 		return (NULL);
1034 	}
1035 
1036 	/* lookup device instance */
1037 	if ((d = auimpl_dev_hold_by_devt(dev)) == NULL) {
1038 		audio_dev_warn(NULL, "no audio_dev for dev_t %d,%d",
1039 		    getmajor(dev), getminor(dev));
1040 		return (NULL);
1041 	}
1042 
1043 	if ((c = kmem_zalloc(sizeof (*c), KM_NOSLEEP)) == NULL) {
1044 		audio_dev_warn(d, "unable to allocate client structure");
1045 		auimpl_dev_release(d);
1046 		return (NULL);
1047 	}
1048 	c->c_dev = d;
1049 
1050 	mutex_init(&c->c_lock, NULL, MUTEX_DRIVER, NULL);
1051 	cv_init(&c->c_cv, NULL, CV_DRIVER, NULL);
1052 
1053 	if ((auimpl_stream_init(&c->c_ostream, c) != 0) ||
1054 	    (auimpl_stream_init(&c->c_istream, c) != 0)) {
1055 		goto failed;
1056 	}
1057 
1058 	c->c_major =		getmajor(dev);
1059 	c->c_origminor =	getminor(dev);
1060 	c->c_ops =		*ops;
1061 
1062 	/*
1063 	 * We hold the client lock here.
1064 	 */
1065 	rw_enter(&auimpl_client_lock, RW_WRITER);
1066 
1067 	minor = AUDIO_MN_CLONE_MASK;
1068 	for (next = list_head(list); next; next = list_next(list, next)) {
1069 		if (next->c_minor > minor) {
1070 			break;
1071 		}
1072 		minor++;
1073 	}
1074 	if (minor >= MAXMIN32) {
1075 		rw_exit(&auimpl_client_lock);
1076 		goto failed;
1077 	}
1078 	c->c_minor = minor;
1079 	list_insert_before(list, next, c);
1080 
1081 	rw_exit(&auimpl_client_lock);
1082 
1083 
1084 	return (c);
1085 
1086 failed:
1087 	auimpl_dev_release(d);
1088 	audio_stream_fini(&c->c_ostream);
1089 	audio_stream_fini(&c->c_istream);
1090 	mutex_destroy(&c->c_lock);
1091 	cv_destroy(&c->c_cv);
1092 	kmem_free(c, sizeof (*c));
1093 	return (NULL);
1094 }
1095 
1096 void
1097 auimpl_client_destroy(audio_client_t *c)
1098 {
1099 	/* remove us from the global list */
1100 	rw_enter(&auimpl_client_lock, RW_WRITER);
1101 	list_remove(&auimpl_clients, c);
1102 	rw_exit(&auimpl_client_lock);
1103 
1104 	ASSERT(!c->c_istream.s_running);
1105 	ASSERT(!c->c_istream.s_running);
1106 
1107 	/* release the device reference count */
1108 	auimpl_dev_release(c->c_dev);
1109 	c->c_dev = NULL;
1110 
1111 	mutex_destroy(&c->c_lock);
1112 	cv_destroy(&c->c_cv);
1113 
1114 	audio_stream_fini(&c->c_istream);
1115 	audio_stream_fini(&c->c_ostream);
1116 	kmem_free(c, sizeof (*c));
1117 }
1118 
1119 void
1120 auimpl_client_activate(audio_client_t *c)
1121 {
1122 	rw_enter(&auimpl_client_lock, RW_WRITER);
1123 	c->c_is_active = B_TRUE;
1124 	rw_exit(&auimpl_client_lock);
1125 }
1126 
1127 void
1128 auimpl_client_deactivate(audio_client_t *c)
1129 {
1130 	rw_enter(&auimpl_client_lock, RW_WRITER);
1131 	c->c_is_active = B_FALSE;
1132 	rw_exit(&auimpl_client_lock);
1133 }
1134 
1135 void
1136 auclnt_close(audio_client_t *c)
1137 {
1138 	audio_dev_t	*d = c->c_dev;
1139 
1140 	/* stop the engines if they are running */
1141 	auclnt_stop(&c->c_istream);
1142 	auclnt_stop(&c->c_ostream);
1143 
1144 	rw_enter(&auimpl_client_lock, RW_WRITER);
1145 	list_remove(&d->d_clients, c);
1146 	rw_exit(&auimpl_client_lock);
1147 
1148 	mutex_enter(&c->c_lock);
1149 	/* if in transition need to wait for other thread to release */
1150 	while (c->c_refcnt) {
1151 		cv_wait(&c->c_cv, &c->c_lock);
1152 	}
1153 	mutex_exit(&c->c_lock);
1154 
1155 	/* release any engines that we were holding */
1156 	auimpl_engine_close(&c->c_ostream);
1157 	auimpl_engine_close(&c->c_istream);
1158 }
1159 
1160 audio_dev_t *
1161 auclnt_hold_dev_by_index(int index)
1162 {
1163 	return (auimpl_dev_hold_by_index(index));
1164 }
1165 
1166 void
1167 auclnt_release_dev(audio_dev_t *dev)
1168 {
1169 	auimpl_dev_release(dev);
1170 }
1171 
1172 audio_client_t *
1173 auclnt_hold_by_devt(dev_t dev)
1174 {
1175 	minor_t	mn = getminor(dev);
1176 	major_t mj = getmajor(dev);
1177 	list_t *list;
1178 	audio_client_t *c;
1179 
1180 	list = &auimpl_clients;
1181 	/* linked list search is kind of inefficient, but it works */
1182 	rw_enter(&auimpl_client_lock, RW_READER);
1183 	for (c = list_head(list); c != NULL; c = list_next(list, c)) {
1184 		if ((c->c_major == mj) && (c->c_minor == mn)) {
1185 			mutex_enter(&c->c_lock);
1186 			if (c->c_is_active) {
1187 				c->c_refcnt++;
1188 				mutex_exit(&c->c_lock);
1189 			} else {
1190 				mutex_exit(&c->c_lock);
1191 				c = NULL;
1192 			}
1193 			break;
1194 		}
1195 	}
1196 	rw_exit(&auimpl_client_lock);
1197 	return (c);
1198 }
1199 
1200 void
1201 auclnt_release(audio_client_t *c)
1202 {
1203 	mutex_enter(&c->c_lock);
1204 	ASSERT(c->c_refcnt > 0);
1205 	c->c_refcnt--;
1206 	if (c->c_refcnt == 0)
1207 		cv_broadcast(&c->c_cv);
1208 	mutex_exit(&c->c_lock);
1209 }
1210 
1211 unsigned
1212 auclnt_dev_get_serial(audio_dev_t *d)
1213 {
1214 	return (d->d_serial);
1215 }
1216 
1217 void
1218 auclnt_dev_walk_clients(audio_dev_t *d,
1219     int (*walker)(audio_client_t *, void *),
1220     void *arg)
1221 {
1222 	list_t		*l = &d->d_clients;
1223 	audio_client_t	*c;
1224 	int		rv;
1225 
1226 	rw_enter(&auimpl_client_lock, RW_READER);
1227 restart:
1228 	for (c = list_head(l); c != NULL; c = list_next(l, c)) {
1229 		if (!c->c_is_active)
1230 			continue;
1231 		rv = (walker(c, arg));
1232 		if (rv == AUDIO_WALK_STOP) {
1233 			break;
1234 		} else if (rv == AUDIO_WALK_RESTART) {
1235 			goto restart;
1236 		}
1237 	}
1238 	rw_exit(&auimpl_client_lock);
1239 }
1240 
1241 
1242 int
1243 auclnt_open(audio_client_t *c, unsigned fmts, int oflag)
1244 {
1245 	audio_stream_t	*sp;
1246 	audio_dev_t	*d = c->c_dev;
1247 	int		rv = 0;
1248 	int		flags;
1249 	audio_parms_t	parms;
1250 
1251 	flags = 0;
1252 	if (oflag & FNDELAY)
1253 		flags |= ENGINE_NDELAY;
1254 
1255 	if (oflag & FWRITE) {
1256 		sp = &c->c_ostream;
1257 		rv = auimpl_engine_open(d, fmts, flags | ENGINE_OUTPUT, sp);
1258 
1259 		if (rv != 0) {
1260 			goto done;
1261 		}
1262 		mutex_enter(&sp->s_lock);
1263 		parms = *sp->s_user_parms;
1264 		rv = auimpl_format_setup(sp, &parms);
1265 		mutex_exit(&sp->s_lock);
1266 		if (rv != 0) {
1267 			goto done;
1268 		}
1269 	}
1270 
1271 	if (oflag & FREAD) {
1272 		sp = &c->c_istream;
1273 		rv = auimpl_engine_open(d, fmts, flags | ENGINE_INPUT, sp);
1274 
1275 		if (rv != 0) {
1276 			goto done;
1277 		}
1278 		mutex_enter(&sp->s_lock);
1279 		parms = *sp->s_user_parms;
1280 		rv = auimpl_format_setup(sp, &parms);
1281 		mutex_exit(&sp->s_lock);
1282 		if (rv != 0) {
1283 			goto done;
1284 		}
1285 	}
1286 
1287 done:
1288 	if (rv != 0) {
1289 		/* close any engines that we opened */
1290 		auimpl_engine_close(&c->c_ostream);
1291 		auimpl_engine_close(&c->c_istream);
1292 	} else {
1293 		rw_enter(&auimpl_client_lock, RW_WRITER);
1294 		list_insert_tail(&d->d_clients, c);
1295 		c->c_ostream.s_gain_master = d->d_pcmvol;
1296 		c->c_istream.s_gain_master = 100;
1297 		rw_exit(&auimpl_client_lock);
1298 		auclnt_set_gain(&c->c_ostream, 100);
1299 		auclnt_set_gain(&c->c_istream, 100);
1300 	}
1301 
1302 	return (rv);
1303 }
1304 
1305 minor_t
1306 auclnt_get_minor(audio_client_t *c)
1307 {
1308 	return (c->c_minor);
1309 }
1310 
1311 minor_t
1312 auclnt_get_original_minor(audio_client_t *c)
1313 {
1314 	return (c->c_origminor);
1315 }
1316 
1317 minor_t
1318 auclnt_get_minor_type(audio_client_t *c)
1319 {
1320 	return (c->c_origminor & AUDIO_MN_TYPE_MASK);
1321 }
1322 
1323 queue_t *
1324 auclnt_get_rq(audio_client_t *c)
1325 {
1326 	return (c->c_rq);
1327 }
1328 
1329 queue_t *
1330 auclnt_get_wq(audio_client_t *c)
1331 {
1332 	return (c->c_wq);
1333 }
1334 
1335 pid_t
1336 auclnt_get_pid(audio_client_t *c)
1337 {
1338 	return (c->c_pid);
1339 }
1340 
1341 cred_t *
1342 auclnt_get_cred(audio_client_t *c)
1343 {
1344 	return (c->c_cred);
1345 }
1346 
1347 audio_dev_t *
1348 auclnt_get_dev(audio_client_t *c)
1349 {
1350 	return (c->c_dev);
1351 }
1352 
1353 int
1354 auclnt_get_dev_number(audio_dev_t *dev)
1355 {
1356 	return (dev->d_number);
1357 }
1358 
1359 int
1360 auclnt_get_dev_index(audio_dev_t *dev)
1361 {
1362 	return (dev->d_index);
1363 }
1364 
1365 const char *
1366 auclnt_get_dev_name(audio_dev_t *dev)
1367 {
1368 	return (dev->d_name);
1369 }
1370 
1371 const char *
1372 auclnt_get_dev_driver(audio_dev_t *dev)
1373 {
1374 	return (ddi_driver_name(dev->d_dip));
1375 }
1376 
1377 dev_info_t *
1378 auclnt_get_dev_devinfo(audio_dev_t *dev)
1379 {
1380 	return (dev->d_dip);
1381 }
1382 
1383 const char *
1384 auclnt_get_dev_hw_info(audio_dev_t *dev, void **iter)
1385 {
1386 	struct audio_infostr *isp = *iter;
1387 	if (isp == NULL) {
1388 		isp = list_head(&dev->d_hwinfo);
1389 	} else {
1390 		isp = list_next(&dev->d_hwinfo, isp);
1391 	}
1392 
1393 	*iter = isp;
1394 	return (isp ? isp->i_line : NULL);
1395 }
1396 
1397 int
1398 auclnt_get_dev_instance(audio_dev_t *dev)
1399 {
1400 	return (dev->d_instance);
1401 }
1402 
1403 const char *
1404 auclnt_get_dev_description(audio_dev_t *dev)
1405 {
1406 	return (dev->d_desc);
1407 }
1408 
1409 const char *
1410 auclnt_get_dev_version(audio_dev_t *dev)
1411 {
1412 	return (dev->d_vers);
1413 }
1414 
1415 unsigned
1416 auclnt_get_dev_capab(audio_dev_t *dev)
1417 {
1418 	uint32_t	flags;
1419 	unsigned	caps = 0;
1420 
1421 	flags = dev->d_flags;
1422 
1423 	if (flags & DEV_OUTPUT_CAP)
1424 		caps |= AUDIO_CLIENT_CAP_PLAY;
1425 	if (flags & DEV_INPUT_CAP)
1426 		caps |= AUDIO_CLIENT_CAP_RECORD;
1427 	if (flags & DEV_DUPLEX_CAP)
1428 		caps |= AUDIO_CLIENT_CAP_DUPLEX;
1429 
1430 	/* AC3: deal with formats that don't support mixing */
1431 
1432 	return (caps);
1433 }
1434 
1435 uint64_t
1436 auclnt_get_samples(audio_stream_t *sp)
1437 {
1438 	uint64_t	n;
1439 
1440 	mutex_enter(&sp->s_lock);
1441 	n = sp->s_samples;
1442 	mutex_exit(&sp->s_lock);
1443 	return (n);
1444 }
1445 
1446 void
1447 auclnt_set_samples(audio_stream_t *sp, uint64_t n)
1448 {
1449 	mutex_enter(&sp->s_lock);
1450 	sp->s_samples = n;
1451 	mutex_exit(&sp->s_lock);
1452 }
1453 
1454 uint64_t
1455 auclnt_get_errors(audio_stream_t *sp)
1456 {
1457 	uint64_t	n;
1458 	mutex_enter(&sp->s_lock);
1459 	n = sp->s_errors;
1460 	mutex_exit(&sp->s_lock);
1461 	return (n);
1462 }
1463 
1464 void
1465 auclnt_set_errors(audio_stream_t *sp, uint64_t n)
1466 {
1467 	mutex_enter(&sp->s_lock);
1468 	sp->s_errors = n;
1469 	mutex_exit(&sp->s_lock);
1470 }
1471 
1472 void
1473 auclnt_register_ops(minor_t minor, audio_client_ops_t *ops)
1474 {
1475 	/* we control minor number allocations, no need for runtime checks */
1476 	ASSERT(minor <= AUDIO_MN_TYPE_MASK);
1477 
1478 	audio_client_ops[minor] = ops;
1479 }
1480 
1481 int
1482 auimpl_create_minors(audio_dev_t *d)
1483 {
1484 	char			path[MAXPATHLEN];
1485 	int			rv = 0;
1486 	minor_t			minor;
1487 	audio_client_ops_t	*ops;
1488 	char			*nt;
1489 
1490 	for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) {
1491 
1492 		if ((ops = audio_client_ops[i]) == NULL)
1493 			continue;
1494 
1495 		if (ops->aco_dev_init != NULL)
1496 			d->d_minor_data[i] = ops->aco_dev_init(d);
1497 
1498 		switch (i) {
1499 		case AUDIO_MINOR_SNDSTAT:
1500 			if (!(d->d_flags & DEV_SNDSTAT_CAP)) {
1501 				continue;
1502 			}
1503 			nt = DDI_PSEUDO;
1504 			break;
1505 
1506 		default:
1507 			if (!(d->d_flags & (DEV_INPUT_CAP| DEV_OUTPUT_CAP))) {
1508 				continue;
1509 			}
1510 			nt = DDI_NT_AUDIO;
1511 			break;
1512 		}
1513 
1514 		if (ops->aco_minor_prefix != NULL) {
1515 
1516 			minor = AUDIO_MKMN(d->d_instance, i);
1517 			(void) snprintf(path, sizeof (path),
1518 			    "%s%d", ops->aco_minor_prefix, d->d_instance);
1519 
1520 			rv = ddi_create_minor_node(d->d_dip, path, S_IFCHR,
1521 			    minor, nt, 0);
1522 
1523 			if (rv != 0)
1524 				break;
1525 		}
1526 	}
1527 	return (rv);
1528 }
1529 
1530 void
1531 auimpl_remove_minors(audio_dev_t *d)
1532 {
1533 	char			path[MAXPATHLEN];
1534 	audio_client_ops_t	*ops;
1535 
1536 	for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) {
1537 		if ((ops = audio_client_ops[i]) == NULL)
1538 			continue;
1539 		if (ops->aco_minor_prefix != NULL) {
1540 			(void) snprintf(path, sizeof (path), "%s%d",
1541 			    ops->aco_minor_prefix, d->d_instance);
1542 			(void) ddi_remove_minor_node(d->d_dip, path);
1543 		}
1544 
1545 		if (ops->aco_dev_fini != NULL)
1546 			ops->aco_dev_fini(d->d_minor_data[i]);
1547 	}
1548 }
1549 
1550 void *
1551 auclnt_get_dev_minor_data(audio_dev_t *d, minor_t mn)
1552 {
1553 	ASSERT(mn < (1U << AUDIO_MN_TYPE_NBITS));
1554 	return (d->d_minor_data[mn]);
1555 }
1556 
1557 void *
1558 auclnt_get_minor_data(audio_client_t *c, minor_t mn)
1559 {
1560 	ASSERT(mn < (1U << AUDIO_MN_TYPE_NBITS));
1561 	return (c->c_dev->d_minor_data[mn]);
1562 }
1563 
1564 /*
1565  * This will walk all controls registered to a clients device and callback
1566  * to walker for each one with its audio_ctrl. Note this data
1567  * must be considered read only by walker.
1568  *
1569  * Note that walk_func may return values to continue (AUDIO_WALK_CONTINUE)
1570  * or stop walk (AUDIO_WALK_STOP).
1571  *
1572  */
1573 void
1574 auclnt_walk_controls(audio_dev_t *d,
1575     int (*walker)(audio_ctrl_t *, void *),
1576     void *arg)
1577 {
1578 	audio_ctrl_t *ctrl;
1579 
1580 	rw_enter(&d->d_ctrl_lock, RW_READER);
1581 	for (ctrl = list_head(&d->d_controls); ctrl;
1582 	    ctrl = list_next(&d->d_controls, ctrl)) {
1583 		if (walker(ctrl, arg) == AUDIO_WALK_STOP)
1584 			break;
1585 	}
1586 	rw_exit(&d->d_ctrl_lock);
1587 }
1588 
1589 /*
1590  * This will search all controls attached to an
1591  * audio device for a control with the desired name.
1592  *
1593  * d    - the audio device to look on
1594  * name - name of the control being looked for.
1595  *
1596  * On successful return a ctrl handle will be returned. On
1597  * failure NULL is returned.
1598  */
1599 audio_ctrl_t *
1600 auclnt_find_control(audio_dev_t *d, const char *name)
1601 {
1602 	audio_ctrl_t *ctrl;
1603 
1604 	/* Verify argument */
1605 	ASSERT(d);
1606 
1607 	rw_enter(&d->d_ctrl_lock, RW_READER);
1608 	for (ctrl = list_head(&d->d_controls); ctrl;
1609 	    ctrl = list_next(&d->d_controls, ctrl)) {
1610 		if (strcmp(ctrl->ctrl_name, name) == 0) {
1611 			rw_exit(&d->d_ctrl_lock);
1612 			return (ctrl);
1613 		}
1614 	}
1615 	rw_exit(&d->d_ctrl_lock);
1616 	return (NULL);
1617 }
1618 
1619 /*
1620  * Given a known control, get its attributes.
1621  *
1622  * The caller must supply a audio_ctrl_desc_t structure.  Also the
1623  * values in the structure are ignored when making the call and filled
1624  * in by this function. All data pointed to by elements of desc should
1625  * be assumed read only.
1626  *
1627  * If an error occurs then a non-zero is returned.
1628  *
1629  */
1630 int
1631 auclnt_control_describe(audio_ctrl_t *ctrl, audio_ctrl_desc_t *desc)
1632 {
1633 	ASSERT(ctrl);
1634 	ASSERT(desc);
1635 
1636 	bcopy(&ctrl->ctrl_des, desc, sizeof (*desc));
1637 	return (0);
1638 }
1639 
1640 int
1641 auclnt_control_read(audio_ctrl_t *ctrl, uint64_t *value)
1642 {
1643 	return (audio_control_read(ctrl, value));
1644 }
1645 
1646 int
1647 auclnt_control_write(audio_ctrl_t *ctrl, uint64_t value)
1648 {
1649 	return (audio_control_write(ctrl, value));
1650 }
1651 
1652 void
1653 auclnt_warn(audio_client_t *c, const char *fmt, ...)
1654 {
1655 	va_list va;
1656 
1657 	va_start(va, fmt);
1658 	auimpl_dev_vwarn(c ? c->c_dev : NULL, fmt, va);
1659 	va_end(va);
1660 }
1661