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