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