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