xref: /illumos-gate/usr/src/uts/common/io/audio/impl/audio_client.c (revision 682cb1044237d21ad6810702564bec833b8c410c)
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 	el = ENG_QLEN(e) + (e->e_head - e->e_tail);
529 	er = e->e_rate;
530 	sl = sp->s_cnv_cnt;
531 	sr = sp->s_user_parms->p_rate;
532 	cnt = (unsigned)(sp->s_head - sp->s_tail);
533 	mutex_exit(&sp->s_lock);
534 	mutex_exit(&e->e_lock);
535 
536 	/* engine frames converted to stream rate, plus stream frames */
537 	*slen = cnt;
538 	*flen = ((unsigned)(((el * sr) / er) + sl));
539 }
540 
541 int
542 auclnt_set_format(audio_stream_t *sp, int fmt)
543 {
544 	audio_parms_t	parms;
545 	int		rv = 0;
546 
547 	/*
548 	 * AC3: If we select an AC3 format, then we have to allocate
549 	 * another engine.  Normally this will be an output only
550 	 * engine.  However, for now we aren't supporting AC3
551 	 * passthru.
552 	 */
553 
554 	switch (fmt) {
555 	case AUDIO_FORMAT_U8:
556 	case AUDIO_FORMAT_ULAW:
557 	case AUDIO_FORMAT_ALAW:
558 	case AUDIO_FORMAT_S8:
559 	case AUDIO_FORMAT_S16_LE:
560 	case AUDIO_FORMAT_S16_BE:
561 	case AUDIO_FORMAT_U16_LE:
562 	case AUDIO_FORMAT_U16_BE:
563 	case AUDIO_FORMAT_S24_LE:
564 	case AUDIO_FORMAT_S24_BE:
565 	case AUDIO_FORMAT_S32_LE:
566 	case AUDIO_FORMAT_S32_BE:
567 	case AUDIO_FORMAT_S24_PACKED:
568 		break;
569 
570 	case AUDIO_FORMAT_AC3:		/* AC3: PASSTHRU */
571 	default:
572 		return (ENOTSUP);
573 	}
574 
575 
576 	mutex_enter(&sp->s_lock);
577 	parms = *sp->s_user_parms;
578 
579 	/*
580 	 * Optimization.  Some personalities send us the same format
581 	 * over and over again.  (Sun personality does this
582 	 * repeatedly.)  setup_src is potentially expensive, so we
583 	 * avoid doing it unless we really need to.
584 	 */
585 	if (fmt != parms.p_format) {
586 		/*
587 		 * Note that setting the format doesn't check that the
588 		 * audio streams have been paused.  As a result, any
589 		 * data still playing or recording will probably get
590 		 * misinterpreted.  It would be smart if the client
591 		 * application paused/stopped playback before changing
592 		 * formats.
593 		 */
594 		parms.p_format = fmt;
595 		rv = auimpl_format_setup(sp, &parms);
596 	}
597 	mutex_exit(&sp->s_lock);
598 
599 	return (rv);
600 }
601 
602 int
603 auclnt_get_format(audio_stream_t *sp)
604 {
605 	return (sp->s_user_parms->p_format);
606 }
607 
608 int
609 auclnt_get_output_format(audio_client_t *c)
610 {
611 	return (c->c_ostream.s_user_parms->p_format);
612 }
613 
614 int
615 auclnt_get_input_format(audio_client_t *c)
616 {
617 	return (c->c_istream.s_user_parms->p_format);
618 }
619 
620 int
621 auclnt_set_channels(audio_stream_t *sp, int nchan)
622 {
623 	audio_parms_t	parms;
624 	int		rv = 0;
625 
626 	/* Validate setting */
627 	if ((nchan > AUDIO_MAX_CHANNELS) || (nchan < 1)) {
628 		return (EINVAL);
629 	}
630 
631 	mutex_enter(&sp->s_lock);
632 	parms = *sp->s_user_parms;
633 	if (nchan != parms.p_nchan) {
634 		parms.p_nchan = nchan;
635 		rv = auimpl_format_setup(sp, &parms);
636 	}
637 	mutex_exit(&sp->s_lock);
638 
639 	return (rv);
640 }
641 
642 int
643 auclnt_get_channels(audio_stream_t *sp)
644 {
645 	return (sp->s_user_parms->p_nchan);
646 }
647 
648 
649 static void
650 auimpl_set_gain_master(audio_stream_t *sp, uint8_t gain)
651 {
652 	uint32_t	scaled;
653 
654 	if (gain > 100) {
655 		gain = 0;
656 	}
657 
658 	mutex_enter(&sp->s_lock);
659 	if (sp->s_gain_master == gain) {
660 		mutex_exit(&sp->s_lock);
661 		return;
662 	}
663 
664 	/*
665 	 * calculate the scaled values.  Done now to avoid calculations
666 	 * later.
667 	 */
668 	scaled = (gain * sp->s_gain_pct * AUDIO_DB_SIZE) / (100 * 100);
669 
670 	sp->s_gain_master = gain;
671 	sp->s_gain_scaled = auimpl_db_table[scaled];
672 
673 	if (!sp->s_muted) {
674 		sp->s_gain_eff = sp->s_gain_scaled;
675 	}
676 	mutex_exit(&sp->s_lock);
677 }
678 
679 int
680 auimpl_set_pcmvol(void *arg, uint64_t val)
681 {
682 	audio_dev_t	*d = arg;
683 	list_t		*l = &d->d_clients;
684 	audio_client_t	*c;
685 
686 	if (val > 100) {
687 		return (EINVAL);
688 	}
689 	rw_enter(&auimpl_client_lock, RW_WRITER);
690 	d->d_pcmvol = val & 0xff;
691 	rw_downgrade(&auimpl_client_lock);
692 
693 	for (c = list_head(l); c; c = list_next(l, c)) {
694 		/* don't need to check is_active here, its safe */
695 		auimpl_set_gain_master(&c->c_ostream, (uint8_t)val);
696 	}
697 	rw_exit(&auimpl_client_lock);
698 
699 	return (0);
700 }
701 
702 int
703 auimpl_get_pcmvol(void *arg, uint64_t *val)
704 {
705 	audio_dev_t	*d = arg;
706 
707 	*val = d->d_pcmvol;
708 	return (0);
709 }
710 
711 void
712 auclnt_set_gain(audio_stream_t *sp, uint8_t gain)
713 {
714 	uint32_t	scaled;
715 
716 	if (gain > 100) {
717 		gain = 0;
718 	}
719 
720 	mutex_enter(&sp->s_lock);
721 
722 	/* if no change, don't bother doing updates */
723 	if (sp->s_gain_pct == gain) {
724 		mutex_exit(&sp->s_lock);
725 		return;
726 	}
727 
728 	/*
729 	 * calculate the scaled values.  Done now to avoid calculations
730 	 * later.
731 	 */
732 	scaled = (gain * sp->s_gain_master * AUDIO_DB_SIZE) / (100 * 100);
733 
734 	sp->s_gain_pct = gain;
735 	sp->s_gain_scaled = auimpl_db_table[scaled];
736 
737 	if (!sp->s_muted) {
738 		sp->s_gain_eff = sp->s_gain_scaled;
739 	}
740 	mutex_exit(&sp->s_lock);
741 
742 	atomic_inc_uint(&sp->s_client->c_dev->d_serial);
743 }
744 
745 uint8_t
746 auclnt_get_gain(audio_stream_t *sp)
747 {
748 	return (sp->s_gain_pct);
749 }
750 
751 void
752 auclnt_set_muted(audio_stream_t *sp, boolean_t muted)
753 {
754 	mutex_enter(&sp->s_lock);
755 
756 	/* if no work change, don't bother doing updates */
757 	if (sp->s_muted == muted) {
758 		mutex_exit(&sp->s_lock);
759 		return;
760 	}
761 
762 	sp->s_muted = muted;
763 	if (muted) {
764 		sp->s_gain_eff = 0;
765 	} else {
766 		sp->s_gain_eff = sp->s_gain_scaled;
767 	}
768 	mutex_exit(&sp->s_lock);
769 
770 	atomic_inc_uint(&sp->s_client->c_dev->d_serial);
771 }
772 
773 boolean_t
774 auclnt_get_muted(audio_stream_t *sp)
775 {
776 	return (sp->s_muted);
777 }
778 
779 boolean_t
780 auclnt_is_running(audio_stream_t *sp)
781 {
782 	return (sp->s_running);
783 }
784 
785 void
786 auclnt_start(audio_stream_t *sp)
787 {
788 	mutex_enter(&sp->s_lock);
789 	sp->s_running = B_TRUE;
790 	mutex_exit(&sp->s_lock);
791 }
792 
793 void
794 auclnt_stop(audio_stream_t *sp)
795 {
796 	mutex_enter(&sp->s_lock);
797 	/* if running, then stop it */
798 	if (sp->s_running) {
799 		sp->s_running = B_FALSE;
800 		/*
801 		 * if we stopped the engine, we might need to wake up
802 		 * a thread that is waiting for drain to complete.
803 		 */
804 		cv_broadcast(&sp->s_cv);
805 	}
806 	mutex_exit(&sp->s_lock);
807 }
808 
809 /*
810  * When pausing, no new data will be played after the most recently
811  * mixed samples have played.  However, the audio engine will continue
812  * to play (possibly just silence).
813  *
814  * Note that we don't reference count the device, or release/close the
815  * engine here.  Once fired up, the engine continues running unil it
816  * is closed.
817  */
818 void
819 auclnt_set_paused(audio_stream_t *sp)
820 {
821 	mutex_enter(&sp->s_lock);
822 	if (sp->s_paused) {
823 		mutex_exit(&sp->s_lock);
824 		return;
825 	}
826 	sp->s_paused = B_TRUE;
827 	mutex_exit(&sp->s_lock);
828 
829 	auclnt_stop(sp);
830 
831 	atomic_inc_uint(&sp->s_client->c_dev->d_serial);
832 }
833 
834 void
835 auclnt_clear_paused(audio_stream_t *sp)
836 {
837 	mutex_enter(&sp->s_lock);
838 	if (!sp->s_paused) {
839 		mutex_exit(&sp->s_lock);
840 		return;
841 	}
842 	sp->s_paused = B_FALSE;
843 	mutex_exit(&sp->s_lock);
844 }
845 
846 boolean_t
847 auclnt_is_paused(audio_stream_t *sp)
848 {
849 	return (sp->s_paused);
850 }
851 
852 void
853 auclnt_flush(audio_stream_t *sp)
854 {
855 	mutex_enter(&sp->s_lock);
856 	if (sp == &sp->s_client->c_ostream) {
857 		sp->s_tail = sp->s_head;
858 		sp->s_tidx = sp->s_hidx;
859 	} else {
860 		sp->s_head = sp->s_tail;
861 		sp->s_hidx = sp->s_tidx;
862 	}
863 	sp->s_cnv_cnt = 0;
864 	mutex_exit(&sp->s_lock);
865 }
866 
867 int
868 auclnt_get_oflag(audio_client_t *c)
869 {
870 	return (c->c_omode);
871 }
872 
873 /*
874  * These routines should not be accessed by client "personality"
875  * implementations, but are for private framework use only.
876  */
877 
878 void
879 auimpl_client_init(void)
880 {
881 	rw_init(&auimpl_client_lock, NULL, RW_DRIVER, NULL);
882 	list_create(&auimpl_clients, sizeof (struct audio_client),
883 	    offsetof(struct audio_client, c_global_linkage));
884 }
885 
886 void
887 auimpl_client_fini(void)
888 {
889 	rw_destroy(&auimpl_client_lock);
890 	list_destroy(&auimpl_clients);
891 }
892 
893 static int
894 auimpl_stream_init(audio_stream_t *sp, audio_client_t *c)
895 {
896 	mutex_init(&sp->s_lock, NULL, MUTEX_DRIVER, NULL);
897 	cv_init(&sp->s_cv, NULL, CV_DRIVER, NULL);
898 	sp->s_client = c;
899 
900 	if (sp == &c->c_ostream) {
901 		sp->s_user_parms = &sp->s_cnv_src_parms;
902 		sp->s_phys_parms = &sp->s_cnv_dst_parms;
903 		sp->s_engcap = ENGINE_OUTPUT_CAP;
904 	} else {
905 		ASSERT(sp == &c->c_istream);
906 		sp->s_user_parms = &sp->s_cnv_dst_parms;
907 		sp->s_phys_parms = &sp->s_cnv_src_parms;
908 		sp->s_engcap = ENGINE_INPUT_CAP;
909 	}
910 
911 	/* for now initialize conversion parameters */
912 	sp->s_src_quality = 3;	/* reasonable compromise for now */
913 	sp->s_cnv_dst_nchan = 2;
914 	sp->s_cnv_dst_format = AUDIO_FORMAT_S24_NE;
915 	sp->s_cnv_dst_rate = 48000;
916 	sp->s_cnv_src_nchan = 2;
917 	sp->s_cnv_src_format = AUDIO_FORMAT_S24_NE;
918 	sp->s_cnv_src_rate = 48000;
919 
920 	/* set volume/gain all the way up */
921 	sp->s_muted = B_FALSE;
922 	sp->s_gain_pct = 0;
923 	sp->s_gain_scaled = AUDIO_VOL_SCALE;
924 	sp->s_gain_eff = AUDIO_VOL_SCALE;
925 
926 	/*
927 	 * We have to start off with a reasonable buffer and
928 	 * interrupt configuration.
929 	 */
930 	sp->s_allocsz = 65536;
931 	sp->s_data = ddi_umem_alloc(sp->s_allocsz, DDI_UMEM_NOSLEEP,
932 	    &sp->s_cookie);
933 	if (sp->s_data == NULL) {
934 		sp->s_allocsz = 0;
935 		audio_dev_warn(c->c_dev, "ddi_umem_alloc failed");
936 		return (ENOMEM);
937 	}
938 	/* make sure no stale data left in stream */
939 	bzero(sp->s_data, sp->s_allocsz);
940 
941 	/*
942 	 * Allocate SRC and data conversion state.
943 	 */
944 	mutex_enter(&sp->s_lock);
945 	if (auimpl_format_alloc(sp) != 0) {
946 		mutex_exit(&sp->s_lock);
947 		return (ENOMEM);
948 	}
949 
950 	mutex_exit(&sp->s_lock);
951 
952 	return (0);
953 }
954 
955 
956 static void
957 audio_stream_fini(audio_stream_t *sp)
958 {
959 	auimpl_format_free(sp);
960 	if (sp->s_cnv_buf0)
961 		kmem_free(sp->s_cnv_buf0, sp->s_cnv_max);
962 	if (sp->s_cnv_buf1)
963 		kmem_free(sp->s_cnv_buf1, sp->s_cnv_max);
964 	mutex_destroy(&sp->s_lock);
965 	cv_destroy(&sp->s_cv);
966 	if (sp->s_data != NULL) {
967 		ddi_umem_free(sp->s_cookie);
968 		sp->s_data = NULL;
969 	}
970 }
971 
972 int
973 auclnt_start_drain(audio_client_t *c)
974 {
975 	audio_stream_t	*sp;
976 	int		rv;
977 
978 	sp = &c->c_ostream;
979 
980 	/* start an asynchronous drain operation. */
981 	mutex_enter(&sp->s_lock);
982 	if (sp->s_paused || !sp->s_running) {
983 		rv = EALREADY;
984 	} else {
985 		sp->s_draining = B_TRUE;
986 		rv = 0;
987 	}
988 	mutex_exit(&sp->s_lock);
989 	return (rv);
990 }
991 
992 int
993 auclnt_drain(audio_client_t *c)
994 {
995 	audio_stream_t	*sp;
996 
997 	sp = &c->c_ostream;
998 
999 	/*
1000 	 * Note: Drain logic will automatically "stop" the stream when
1001 	 * the drain threshold has been reached.  So all we have to do
1002 	 * is wait for the stream to stop.
1003 	 */
1004 	mutex_enter(&sp->s_lock);
1005 	sp->s_draining = B_TRUE;
1006 	while (sp->s_draining && sp->s_running && !sp->s_paused) {
1007 		if (cv_wait_sig(&sp->s_cv, &sp->s_lock) == 0) {
1008 			mutex_exit(&sp->s_lock);
1009 			return (EINTR);
1010 		}
1011 	}
1012 	mutex_exit(&sp->s_lock);
1013 	return (0);
1014 }
1015 
1016 audio_client_t *
1017 auimpl_client_create(dev_t dev)
1018 {
1019 	audio_client_ops_t	*ops;
1020 	audio_client_t		*c;
1021 	audio_client_t		*next;
1022 	list_t			*list = &auimpl_clients;
1023 	minor_t			minor;
1024 	audio_dev_t		*d;
1025 
1026 	/* validate minor number */
1027 	minor = getminor(dev) & AUDIO_MN_TYPE_MASK;
1028 	if ((ops = audio_client_ops[minor]) == NULL) {
1029 		return (NULL);
1030 	}
1031 
1032 	/* lookup device instance */
1033 	if ((d = auimpl_dev_hold_by_devt(dev)) == NULL) {
1034 		audio_dev_warn(NULL, "no audio_dev for dev_t %d,%d",
1035 		    getmajor(dev), getminor(dev));
1036 		return (NULL);
1037 	}
1038 
1039 	if ((c = kmem_zalloc(sizeof (*c), KM_NOSLEEP)) == NULL) {
1040 		audio_dev_warn(d, "unable to allocate client structure");
1041 		auimpl_dev_release(d);
1042 		return (NULL);
1043 	}
1044 	c->c_dev = d;
1045 
1046 	mutex_init(&c->c_lock, NULL, MUTEX_DRIVER, NULL);
1047 	cv_init(&c->c_cv, NULL, CV_DRIVER, NULL);
1048 
1049 	if ((auimpl_stream_init(&c->c_ostream, c) != 0) ||
1050 	    (auimpl_stream_init(&c->c_istream, c) != 0)) {
1051 		goto failed;
1052 	}
1053 
1054 	c->c_major =		getmajor(dev);
1055 	c->c_origminor =	getminor(dev);
1056 	c->c_ops =		*ops;
1057 
1058 	/*
1059 	 * We hold the client lock here.
1060 	 */
1061 	rw_enter(&auimpl_client_lock, RW_WRITER);
1062 
1063 	minor = AUDIO_MN_CLONE_MASK;
1064 	for (next = list_head(list); next; next = list_next(list, next)) {
1065 		if (next->c_minor > minor) {
1066 			break;
1067 		}
1068 		minor++;
1069 	}
1070 	if (minor >= MAXMIN32) {
1071 		rw_exit(&auimpl_client_lock);
1072 		goto failed;
1073 	}
1074 	c->c_minor = minor;
1075 	list_insert_before(list, next, c);
1076 
1077 	rw_exit(&auimpl_client_lock);
1078 
1079 
1080 	return (c);
1081 
1082 failed:
1083 	auimpl_dev_release(d);
1084 	audio_stream_fini(&c->c_ostream);
1085 	audio_stream_fini(&c->c_istream);
1086 	mutex_destroy(&c->c_lock);
1087 	cv_destroy(&c->c_cv);
1088 	kmem_free(c, sizeof (*c));
1089 	return (NULL);
1090 }
1091 
1092 void
1093 auimpl_client_destroy(audio_client_t *c)
1094 {
1095 	/* remove us from the global list */
1096 	rw_enter(&auimpl_client_lock, RW_WRITER);
1097 	list_remove(&auimpl_clients, c);
1098 	rw_exit(&auimpl_client_lock);
1099 
1100 	ASSERT(!c->c_istream.s_running);
1101 	ASSERT(!c->c_istream.s_running);
1102 
1103 	/* release the device reference count */
1104 	auimpl_dev_release(c->c_dev);
1105 	c->c_dev = NULL;
1106 
1107 	mutex_destroy(&c->c_lock);
1108 	cv_destroy(&c->c_cv);
1109 
1110 	audio_stream_fini(&c->c_istream);
1111 	audio_stream_fini(&c->c_ostream);
1112 	kmem_free(c, sizeof (*c));
1113 }
1114 
1115 void
1116 auimpl_client_activate(audio_client_t *c)
1117 {
1118 	rw_enter(&auimpl_client_lock, RW_WRITER);
1119 	c->c_is_active = B_TRUE;
1120 	rw_exit(&auimpl_client_lock);
1121 }
1122 
1123 void
1124 auimpl_client_deactivate(audio_client_t *c)
1125 {
1126 	rw_enter(&auimpl_client_lock, RW_WRITER);
1127 	c->c_is_active = B_FALSE;
1128 	rw_exit(&auimpl_client_lock);
1129 }
1130 
1131 void
1132 auclnt_close(audio_client_t *c)
1133 {
1134 	audio_dev_t	*d = c->c_dev;
1135 
1136 	/* stop the engines if they are running */
1137 	auclnt_stop(&c->c_istream);
1138 	auclnt_stop(&c->c_ostream);
1139 
1140 	rw_enter(&auimpl_client_lock, RW_WRITER);
1141 	list_remove(&d->d_clients, c);
1142 	rw_exit(&auimpl_client_lock);
1143 
1144 	mutex_enter(&c->c_lock);
1145 	/* if in transition need to wait for other thread to release */
1146 	while (c->c_refcnt) {
1147 		cv_wait(&c->c_cv, &c->c_lock);
1148 	}
1149 	mutex_exit(&c->c_lock);
1150 
1151 	/* release any engines that we were holding */
1152 	auimpl_engine_close(&c->c_ostream);
1153 	auimpl_engine_close(&c->c_istream);
1154 }
1155 
1156 audio_dev_t *
1157 auclnt_hold_dev_by_index(int index)
1158 {
1159 	return (auimpl_dev_hold_by_index(index));
1160 }
1161 
1162 void
1163 auclnt_release_dev(audio_dev_t *dev)
1164 {
1165 	auimpl_dev_release(dev);
1166 }
1167 
1168 audio_client_t *
1169 auclnt_hold_by_devt(dev_t dev)
1170 {
1171 	minor_t	mn = getminor(dev);
1172 	major_t mj = getmajor(dev);
1173 	list_t *list;
1174 	audio_client_t *c;
1175 
1176 	list = &auimpl_clients;
1177 	/* linked list search is kind of inefficient, but it works */
1178 	rw_enter(&auimpl_client_lock, RW_READER);
1179 	for (c = list_head(list); c != NULL; c = list_next(list, c)) {
1180 		if ((c->c_major == mj) && (c->c_minor == mn)) {
1181 			mutex_enter(&c->c_lock);
1182 			if (c->c_is_active) {
1183 				c->c_refcnt++;
1184 				mutex_exit(&c->c_lock);
1185 			} else {
1186 				mutex_exit(&c->c_lock);
1187 				c = NULL;
1188 			}
1189 			break;
1190 		}
1191 	}
1192 	rw_exit(&auimpl_client_lock);
1193 	return (c);
1194 }
1195 
1196 void
1197 auclnt_release(audio_client_t *c)
1198 {
1199 	mutex_enter(&c->c_lock);
1200 	ASSERT(c->c_refcnt > 0);
1201 	c->c_refcnt--;
1202 	if (c->c_refcnt == 0)
1203 		cv_broadcast(&c->c_cv);
1204 	mutex_exit(&c->c_lock);
1205 }
1206 
1207 unsigned
1208 auclnt_dev_get_serial(audio_dev_t *d)
1209 {
1210 	return (d->d_serial);
1211 }
1212 
1213 void
1214 auclnt_dev_walk_clients(audio_dev_t *d,
1215     int (*walker)(audio_client_t *, void *),
1216     void *arg)
1217 {
1218 	list_t		*l = &d->d_clients;
1219 	audio_client_t	*c;
1220 	int		rv;
1221 
1222 	rw_enter(&auimpl_client_lock, RW_READER);
1223 restart:
1224 	for (c = list_head(l); c != NULL; c = list_next(l, c)) {
1225 		if (!c->c_is_active)
1226 			continue;
1227 		rv = (walker(c, arg));
1228 		if (rv == AUDIO_WALK_STOP) {
1229 			break;
1230 		} else if (rv == AUDIO_WALK_RESTART) {
1231 			goto restart;
1232 		}
1233 	}
1234 	rw_exit(&auimpl_client_lock);
1235 }
1236 
1237 
1238 int
1239 auclnt_open(audio_client_t *c, unsigned fmts, int oflag)
1240 {
1241 	audio_stream_t	*sp;
1242 	audio_dev_t	*d = c->c_dev;
1243 	int		rv = 0;
1244 	int		flags;
1245 	audio_parms_t	parms;
1246 
1247 	flags = 0;
1248 	if (oflag & FNDELAY)
1249 		flags |= ENGINE_NDELAY;
1250 
1251 	if (oflag & FWRITE) {
1252 		sp = &c->c_ostream;
1253 		rv = auimpl_engine_open(d, fmts, flags | ENGINE_OUTPUT, sp);
1254 
1255 		if (rv != 0) {
1256 			goto done;
1257 		}
1258 		mutex_enter(&sp->s_lock);
1259 		parms = *sp->s_user_parms;
1260 		rv = auimpl_format_setup(sp, &parms);
1261 		mutex_exit(&sp->s_lock);
1262 		if (rv != 0) {
1263 			goto done;
1264 		}
1265 	}
1266 
1267 	if (oflag & FREAD) {
1268 		sp = &c->c_istream;
1269 		rv = auimpl_engine_open(d, fmts, flags | ENGINE_INPUT, sp);
1270 
1271 		if (rv != 0) {
1272 			goto done;
1273 		}
1274 		mutex_enter(&sp->s_lock);
1275 		parms = *sp->s_user_parms;
1276 		rv = auimpl_format_setup(sp, &parms);
1277 		mutex_exit(&sp->s_lock);
1278 		if (rv != 0) {
1279 			goto done;
1280 		}
1281 	}
1282 
1283 done:
1284 	if (rv != 0) {
1285 		/* close any engines that we opened */
1286 		auimpl_engine_close(&c->c_ostream);
1287 		auimpl_engine_close(&c->c_istream);
1288 	} else {
1289 		rw_enter(&auimpl_client_lock, RW_WRITER);
1290 		list_insert_tail(&d->d_clients, c);
1291 		c->c_ostream.s_gain_master = d->d_pcmvol;
1292 		c->c_istream.s_gain_master = 100;
1293 		rw_exit(&auimpl_client_lock);
1294 		auclnt_set_gain(&c->c_ostream, 100);
1295 		auclnt_set_gain(&c->c_istream, 100);
1296 	}
1297 
1298 	return (rv);
1299 }
1300 
1301 minor_t
1302 auclnt_get_minor(audio_client_t *c)
1303 {
1304 	return (c->c_minor);
1305 }
1306 
1307 minor_t
1308 auclnt_get_original_minor(audio_client_t *c)
1309 {
1310 	return (c->c_origminor);
1311 }
1312 
1313 minor_t
1314 auclnt_get_minor_type(audio_client_t *c)
1315 {
1316 	return (c->c_origminor & AUDIO_MN_TYPE_MASK);
1317 }
1318 
1319 queue_t *
1320 auclnt_get_rq(audio_client_t *c)
1321 {
1322 	return (c->c_rq);
1323 }
1324 
1325 queue_t *
1326 auclnt_get_wq(audio_client_t *c)
1327 {
1328 	return (c->c_wq);
1329 }
1330 
1331 pid_t
1332 auclnt_get_pid(audio_client_t *c)
1333 {
1334 	return (c->c_pid);
1335 }
1336 
1337 cred_t *
1338 auclnt_get_cred(audio_client_t *c)
1339 {
1340 	return (c->c_cred);
1341 }
1342 
1343 audio_dev_t *
1344 auclnt_get_dev(audio_client_t *c)
1345 {
1346 	return (c->c_dev);
1347 }
1348 
1349 int
1350 auclnt_get_dev_number(audio_dev_t *dev)
1351 {
1352 	return (dev->d_number);
1353 }
1354 
1355 int
1356 auclnt_get_dev_index(audio_dev_t *dev)
1357 {
1358 	return (dev->d_index);
1359 }
1360 
1361 const char *
1362 auclnt_get_dev_name(audio_dev_t *dev)
1363 {
1364 	return (dev->d_name);
1365 }
1366 
1367 const char *
1368 auclnt_get_dev_driver(audio_dev_t *dev)
1369 {
1370 	return (ddi_driver_name(dev->d_dip));
1371 }
1372 
1373 dev_info_t *
1374 auclnt_get_dev_devinfo(audio_dev_t *dev)
1375 {
1376 	return (dev->d_dip);
1377 }
1378 
1379 const char *
1380 auclnt_get_dev_hw_info(audio_dev_t *dev, void **iter)
1381 {
1382 	struct audio_infostr *isp = *iter;
1383 	if (isp == NULL) {
1384 		isp = list_head(&dev->d_hwinfo);
1385 	} else {
1386 		isp = list_next(&dev->d_hwinfo, isp);
1387 	}
1388 
1389 	*iter = isp;
1390 	return (isp ? isp->i_line : NULL);
1391 }
1392 
1393 int
1394 auclnt_get_dev_instance(audio_dev_t *dev)
1395 {
1396 	return (dev->d_instance);
1397 }
1398 
1399 const char *
1400 auclnt_get_dev_description(audio_dev_t *dev)
1401 {
1402 	return (dev->d_desc);
1403 }
1404 
1405 const char *
1406 auclnt_get_dev_version(audio_dev_t *dev)
1407 {
1408 	return (dev->d_vers);
1409 }
1410 
1411 unsigned
1412 auclnt_get_dev_capab(audio_dev_t *dev)
1413 {
1414 	uint32_t	flags;
1415 	unsigned	caps = 0;
1416 
1417 	flags = dev->d_flags;
1418 
1419 	if (flags & DEV_OUTPUT_CAP)
1420 		caps |= AUDIO_CLIENT_CAP_PLAY;
1421 	if (flags & DEV_INPUT_CAP)
1422 		caps |= AUDIO_CLIENT_CAP_RECORD;
1423 	if (flags & DEV_DUPLEX_CAP)
1424 		caps |= AUDIO_CLIENT_CAP_DUPLEX;
1425 
1426 	/* AC3: deal with formats that don't support mixing */
1427 
1428 	return (caps);
1429 }
1430 
1431 uint64_t
1432 auclnt_get_samples(audio_stream_t *sp)
1433 {
1434 	uint64_t	n;
1435 
1436 	mutex_enter(&sp->s_lock);
1437 	n = sp->s_samples;
1438 	mutex_exit(&sp->s_lock);
1439 	return (n);
1440 }
1441 
1442 void
1443 auclnt_set_samples(audio_stream_t *sp, uint64_t n)
1444 {
1445 	mutex_enter(&sp->s_lock);
1446 	sp->s_samples = n;
1447 	mutex_exit(&sp->s_lock);
1448 }
1449 
1450 uint64_t
1451 auclnt_get_errors(audio_stream_t *sp)
1452 {
1453 	uint64_t	n;
1454 	mutex_enter(&sp->s_lock);
1455 	n = sp->s_errors;
1456 	mutex_exit(&sp->s_lock);
1457 	return (n);
1458 }
1459 
1460 void
1461 auclnt_set_errors(audio_stream_t *sp, uint64_t n)
1462 {
1463 	mutex_enter(&sp->s_lock);
1464 	sp->s_errors = n;
1465 	mutex_exit(&sp->s_lock);
1466 }
1467 
1468 void
1469 auclnt_register_ops(minor_t minor, audio_client_ops_t *ops)
1470 {
1471 	/* we control minor number allocations, no need for runtime checks */
1472 	ASSERT(minor <= AUDIO_MN_TYPE_MASK);
1473 
1474 	audio_client_ops[minor] = ops;
1475 }
1476 
1477 int
1478 auimpl_create_minors(audio_dev_t *d)
1479 {
1480 	char			path[MAXPATHLEN];
1481 	int			rv = 0;
1482 	minor_t			minor;
1483 	audio_client_ops_t	*ops;
1484 	char			*nt;
1485 
1486 	for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) {
1487 
1488 		if ((ops = audio_client_ops[i]) == NULL)
1489 			continue;
1490 
1491 		if (ops->aco_dev_init != NULL)
1492 			d->d_minor_data[i] = ops->aco_dev_init(d);
1493 
1494 		switch (i) {
1495 		case AUDIO_MINOR_SNDSTAT:
1496 			if (!(d->d_flags & DEV_SNDSTAT_CAP)) {
1497 				continue;
1498 			}
1499 			nt = DDI_PSEUDO;
1500 			break;
1501 
1502 		default:
1503 			if (!(d->d_flags & (DEV_INPUT_CAP| DEV_OUTPUT_CAP))) {
1504 				continue;
1505 			}
1506 			nt = DDI_NT_AUDIO;
1507 			break;
1508 		}
1509 
1510 		if (ops->aco_minor_prefix != NULL) {
1511 
1512 			minor = AUDIO_MKMN(d->d_instance, i);
1513 			(void) snprintf(path, sizeof (path),
1514 			    "%s%d", ops->aco_minor_prefix, d->d_instance);
1515 
1516 			rv = ddi_create_minor_node(d->d_dip, path, S_IFCHR,
1517 			    minor, nt, 0);
1518 
1519 			if (rv != 0)
1520 				break;
1521 		}
1522 	}
1523 	return (rv);
1524 }
1525 
1526 void
1527 auimpl_remove_minors(audio_dev_t *d)
1528 {
1529 	char			path[MAXPATHLEN];
1530 	audio_client_ops_t	*ops;
1531 
1532 	for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) {
1533 		if ((ops = audio_client_ops[i]) == NULL)
1534 			continue;
1535 		if (ops->aco_minor_prefix != NULL) {
1536 			(void) snprintf(path, sizeof (path), "%s%d",
1537 			    ops->aco_minor_prefix, d->d_instance);
1538 			(void) ddi_remove_minor_node(d->d_dip, path);
1539 		}
1540 
1541 		if (ops->aco_dev_fini != NULL)
1542 			ops->aco_dev_fini(d->d_minor_data[i]);
1543 	}
1544 }
1545 
1546 void *
1547 auclnt_get_dev_minor_data(audio_dev_t *d, minor_t mn)
1548 {
1549 	ASSERT(mn < (1U << AUDIO_MN_TYPE_NBITS));
1550 	return (d->d_minor_data[mn]);
1551 }
1552 
1553 void *
1554 auclnt_get_minor_data(audio_client_t *c, minor_t mn)
1555 {
1556 	ASSERT(mn < (1U << AUDIO_MN_TYPE_NBITS));
1557 	return (c->c_dev->d_minor_data[mn]);
1558 }
1559 
1560 /*
1561  * This will walk all controls registered to a clients device and callback
1562  * to walker for each one with its audio_ctrl. Note this data
1563  * must be considered read only by walker.
1564  *
1565  * Note that walk_func may return values to continue (AUDIO_WALK_CONTINUE)
1566  * or stop walk (AUDIO_WALK_STOP).
1567  *
1568  */
1569 void
1570 auclnt_walk_controls(audio_dev_t *d,
1571     int (*walker)(audio_ctrl_t *, void *),
1572     void *arg)
1573 {
1574 	audio_ctrl_t *ctrl;
1575 
1576 	rw_enter(&d->d_ctrl_lock, RW_READER);
1577 	for (ctrl = list_head(&d->d_controls); ctrl;
1578 	    ctrl = list_next(&d->d_controls, ctrl)) {
1579 		if (walker(ctrl, arg) == AUDIO_WALK_STOP)
1580 			break;
1581 	}
1582 	rw_exit(&d->d_ctrl_lock);
1583 }
1584 
1585 /*
1586  * This will search all controls attached to an
1587  * audio device for a control with the desired name.
1588  *
1589  * d    - the audio device to look on
1590  * name - name of the control being looked for.
1591  *
1592  * On successful return a ctrl handle will be returned. On
1593  * failure NULL is returned.
1594  */
1595 audio_ctrl_t *
1596 auclnt_find_control(audio_dev_t *d, const char *name)
1597 {
1598 	audio_ctrl_t *ctrl;
1599 
1600 	/* Verify argument */
1601 	ASSERT(d);
1602 
1603 	rw_enter(&d->d_ctrl_lock, RW_READER);
1604 	for (ctrl = list_head(&d->d_controls); ctrl;
1605 	    ctrl = list_next(&d->d_controls, ctrl)) {
1606 		if (strcmp(ctrl->ctrl_name, name) == 0) {
1607 			rw_exit(&d->d_ctrl_lock);
1608 			return (ctrl);
1609 		}
1610 	}
1611 	rw_exit(&d->d_ctrl_lock);
1612 	return (NULL);
1613 }
1614 
1615 /*
1616  * Given a known control, get its attributes.
1617  *
1618  * The caller must supply a audio_ctrl_desc_t structure.  Also the
1619  * values in the structure are ignored when making the call and filled
1620  * in by this function. All data pointed to by elements of desc should
1621  * be assumed read only.
1622  *
1623  * If an error occurs then a non-zero is returned.
1624  *
1625  */
1626 int
1627 auclnt_control_describe(audio_ctrl_t *ctrl, audio_ctrl_desc_t *desc)
1628 {
1629 	ASSERT(ctrl);
1630 	ASSERT(desc);
1631 
1632 	bcopy(&ctrl->ctrl_des, desc, sizeof (*desc));
1633 	return (0);
1634 }
1635 
1636 int
1637 auclnt_control_read(audio_ctrl_t *ctrl, uint64_t *value)
1638 {
1639 	return (audio_control_read(ctrl, value));
1640 }
1641 
1642 int
1643 auclnt_control_write(audio_ctrl_t *ctrl, uint64_t value)
1644 {
1645 	return (audio_control_write(ctrl, value));
1646 }
1647 
1648 void
1649 auclnt_warn(audio_client_t *c, const char *fmt, ...)
1650 {
1651 	va_list va;
1652 
1653 	va_start(va, fmt);
1654 	auimpl_dev_vwarn(c ? c->c_dev : NULL, fmt, va);
1655 	va_end(va);
1656 }
1657