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