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