xref: /illumos-gate/usr/src/uts/common/io/audio/impl/audio_client.c (revision e44e85a7f9935f0428e188393e3da61b17e83884)
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 void
747 auclnt_start(audio_stream_t *sp)
748 {
749 	mutex_enter(&sp->s_lock);
750 	sp->s_running = B_TRUE;
751 	mutex_exit(&sp->s_lock);
752 }
753 
754 void
755 auclnt_stop(audio_stream_t *sp)
756 {
757 	mutex_enter(&sp->s_lock);
758 	/* if running, then stop it */
759 	if (sp->s_running) {
760 		sp->s_running = B_FALSE;
761 		/*
762 		 * if we stopped the engine, we might need to wake up
763 		 * a thread that is waiting for drain to complete.
764 		 */
765 		cv_broadcast(&sp->s_cv);
766 	}
767 	mutex_exit(&sp->s_lock);
768 }
769 
770 /*
771  * When pausing, no new data will be played after the most recently
772  * mixed samples have played.  However, the audio engine will continue
773  * to play (possibly just silence).
774  *
775  * Note that we don't reference count the device, or release/close the
776  * engine here.  Once fired up, the engine continues running unil it
777  * is closed.
778  */
779 void
780 auclnt_set_paused(audio_stream_t *sp)
781 {
782 	mutex_enter(&sp->s_lock);
783 	if (sp->s_paused) {
784 		mutex_exit(&sp->s_lock);
785 		return;
786 	}
787 	sp->s_paused = B_TRUE;
788 	mutex_exit(&sp->s_lock);
789 
790 	auclnt_stop(sp);
791 
792 	auclnt_notify_dev(sp->s_client->c_dev);
793 }
794 
795 void
796 auclnt_clear_paused(audio_stream_t *sp)
797 {
798 	mutex_enter(&sp->s_lock);
799 	if (!sp->s_paused) {
800 		mutex_exit(&sp->s_lock);
801 		return;
802 	}
803 	sp->s_paused = B_FALSE;
804 	mutex_exit(&sp->s_lock);
805 }
806 
807 boolean_t
808 auclnt_is_paused(audio_stream_t *sp)
809 {
810 	return (sp->s_paused);
811 }
812 
813 void
814 auclnt_flush(audio_stream_t *sp)
815 {
816 	mutex_enter(&sp->s_lock);
817 	if (sp == &sp->s_client->c_ostream) {
818 		sp->s_tail = sp->s_head;
819 		sp->s_tidx = sp->s_hidx;
820 	} else {
821 		sp->s_head = sp->s_tail;
822 		sp->s_hidx = sp->s_tidx;
823 	}
824 	sp->s_cnv_cnt = 0;
825 	mutex_exit(&sp->s_lock);
826 }
827 
828 int
829 auclnt_get_oflag(audio_client_t *c)
830 {
831 	return (c->c_omode);
832 }
833 
834 /*
835  * These routines should not be accessed by client "personality"
836  * implementations, but are for private framework use only.
837  */
838 
839 static list_t			auimpl_clients;
840 static krwlock_t		auimpl_client_lock;
841 static audio_client_ops_t	*audio_client_ops[AUDIO_MN_TYPE_MASK + 1];
842 
843 void
844 auimpl_client_init(void)
845 {
846 	rw_init(&auimpl_client_lock, NULL, RW_DRIVER, NULL);
847 	list_create(&auimpl_clients, sizeof (struct audio_client),
848 	    offsetof(struct audio_client, c_global_linkage));
849 }
850 
851 void
852 auimpl_client_fini(void)
853 {
854 	rw_destroy(&auimpl_client_lock);
855 	list_destroy(&auimpl_clients);
856 }
857 
858 static int
859 auimpl_stream_init(audio_stream_t *sp, audio_client_t *c)
860 {
861 	mutex_init(&sp->s_lock, NULL, MUTEX_DRIVER, NULL);
862 	cv_init(&sp->s_cv, NULL, CV_DRIVER, NULL);
863 	sp->s_client = c;
864 
865 	if (sp == &c->c_ostream) {
866 		sp->s_user_parms = &sp->s_cnv_src_parms;
867 		sp->s_phys_parms = &sp->s_cnv_dst_parms;
868 		sp->s_engcap = ENGINE_OUTPUT_CAP;
869 	} else {
870 		ASSERT(sp == &c->c_istream);
871 		sp->s_user_parms = &sp->s_cnv_dst_parms;
872 		sp->s_phys_parms = &sp->s_cnv_src_parms;
873 		sp->s_engcap = ENGINE_INPUT_CAP;
874 	}
875 
876 	/* for now initialize conversion parameters */
877 	sp->s_src_quality = 3;	/* reasonable compromise for now */
878 	sp->s_cnv_dst_nchan = 2;
879 	sp->s_cnv_dst_format = AUDIO_FORMAT_S24_NE;
880 	sp->s_cnv_dst_rate = 48000;
881 	sp->s_cnv_src_nchan = 2;
882 	sp->s_cnv_src_format = AUDIO_FORMAT_S24_NE;
883 	sp->s_cnv_src_rate = 48000;
884 
885 	/* set volume/gain all the way up */
886 	sp->s_muted = B_FALSE;
887 	sp->s_gain_pct = 0;
888 	sp->s_gain_scaled = AUDIO_VOL_SCALE;
889 	sp->s_gain_eff = AUDIO_VOL_SCALE;
890 
891 	/*
892 	 * We have to start off with a reasonable buffer and
893 	 * interrupt configuration.
894 	 */
895 	sp->s_allocsz = 65536;
896 	sp->s_data = ddi_umem_alloc(sp->s_allocsz, DDI_UMEM_NOSLEEP,
897 	    &sp->s_cookie);
898 	if (sp->s_data == NULL) {
899 		sp->s_allocsz = 0;
900 		audio_dev_warn(c->c_dev, "ddi_umem_alloc failed");
901 		return (ENOMEM);
902 	}
903 	/* make sure no stale data left in stream */
904 	bzero(sp->s_data, sp->s_allocsz);
905 
906 	/*
907 	 * Allocate SRC and data conversion state.
908 	 */
909 	mutex_enter(&sp->s_lock);
910 	if (auimpl_format_alloc(sp) != 0) {
911 		mutex_exit(&sp->s_lock);
912 		return (ENOMEM);
913 	}
914 
915 	mutex_exit(&sp->s_lock);
916 
917 	return (0);
918 }
919 
920 
921 static void
922 audio_stream_fini(audio_stream_t *sp)
923 {
924 	auimpl_format_free(sp);
925 	if (sp->s_cnv_buf0)
926 		kmem_free(sp->s_cnv_buf0, sp->s_cnv_max);
927 	if (sp->s_cnv_buf1)
928 		kmem_free(sp->s_cnv_buf1, sp->s_cnv_max);
929 	mutex_destroy(&sp->s_lock);
930 	cv_destroy(&sp->s_cv);
931 	if (sp->s_data != NULL) {
932 		ddi_umem_free(sp->s_cookie);
933 		sp->s_data = NULL;
934 	}
935 }
936 
937 void
938 auimpl_client_task(void *arg)
939 {
940 	audio_client_t		*c = arg;
941 
942 	mutex_enter(&c->c_lock);
943 
944 	for (;;) {
945 		if (c->c_closing) {
946 			break;
947 		}
948 
949 		if (c->c_do_output) {
950 			c->c_do_output = B_FALSE;
951 
952 			mutex_exit(&c->c_lock);
953 			if (c->c_output != NULL)
954 				c->c_output(c);
955 			mutex_enter(&c->c_lock);
956 			continue;
957 		}
958 
959 		if (c->c_do_input) {
960 			c->c_do_input = B_FALSE;
961 
962 			mutex_exit(&c->c_lock);
963 			if (c->c_input != NULL)
964 				c->c_input(c);
965 			mutex_enter(&c->c_lock);
966 			continue;
967 		}
968 
969 		if (c->c_do_notify) {
970 			c->c_do_notify = B_FALSE;
971 
972 			mutex_exit(&c->c_lock);
973 			if (c->c_notify != NULL)
974 				c->c_notify(c);
975 			mutex_enter(&c->c_lock);
976 			continue;
977 		}
978 
979 		if (c->c_do_drain) {
980 			c->c_do_drain = B_FALSE;
981 
982 			mutex_exit(&c->c_lock);
983 			if (c->c_drain != NULL)
984 				c->c_drain(c);
985 			mutex_enter(&c->c_lock);
986 			continue;
987 		}
988 
989 		/* if we got here, we had no work to do */
990 		cv_wait(&c->c_cv, &c->c_lock);
991 	}
992 	mutex_exit(&c->c_lock);
993 }
994 
995 int
996 auclnt_start_drain(audio_client_t *c)
997 {
998 	audio_stream_t	*sp;
999 	int		rv;
1000 
1001 	sp = &c->c_ostream;
1002 
1003 	/* start an asynchronous drain operation. */
1004 	mutex_enter(&sp->s_lock);
1005 	if (sp->s_paused || !sp->s_running) {
1006 		rv = EALREADY;
1007 	} else {
1008 		sp->s_draining = B_TRUE;
1009 		rv = 0;
1010 	}
1011 	mutex_exit(&sp->s_lock);
1012 	return (rv);
1013 }
1014 
1015 int
1016 auclnt_drain(audio_client_t *c)
1017 {
1018 	audio_stream_t	*sp;
1019 
1020 	sp = &c->c_ostream;
1021 
1022 	/*
1023 	 * Note: Drain logic will automatically "stop" the stream when
1024 	 * the drain threshold has been reached.  So all we have to do
1025 	 * is wait for the stream to stop.
1026 	 */
1027 	mutex_enter(&sp->s_lock);
1028 	sp->s_draining = B_TRUE;
1029 	while (sp->s_draining && sp->s_running && !sp->s_paused) {
1030 		if (cv_wait_sig(&sp->s_cv, &sp->s_lock) == 0) {
1031 			mutex_exit(&sp->s_lock);
1032 			return (EINTR);
1033 		}
1034 	}
1035 	mutex_exit(&sp->s_lock);
1036 	return (0);
1037 }
1038 
1039 audio_client_t *
1040 auimpl_client_create(dev_t dev)
1041 {
1042 	audio_client_ops_t	*ops;
1043 	audio_client_t		*c;
1044 	audio_client_t		*next;
1045 	list_t			*list = &auimpl_clients;
1046 	minor_t			minor;
1047 	audio_dev_t		*d;
1048 	char			scratch[80];
1049 	static uint64_t		unique = 0;
1050 
1051 	/* validate minor number */
1052 	minor = getminor(dev) & AUDIO_MN_TYPE_MASK;
1053 	if ((ops = audio_client_ops[minor]) == NULL) {
1054 		return (NULL);
1055 	}
1056 
1057 	/* lookup device instance */
1058 	if ((d = auimpl_dev_hold_by_devt(dev)) == NULL) {
1059 		audio_dev_warn(NULL, "no audio_dev for dev_t %d,%d",
1060 		    getmajor(dev), getminor(dev));
1061 		return (NULL);
1062 	}
1063 
1064 	if ((c = kmem_zalloc(sizeof (*c), KM_NOSLEEP)) == NULL) {
1065 		audio_dev_warn(d, "unable to allocate client structure");
1066 		auimpl_dev_release(d);
1067 		return (NULL);
1068 	}
1069 	c->c_dev = d;
1070 
1071 	mutex_init(&c->c_lock, NULL, MUTEX_DRIVER, NULL);
1072 	cv_init(&c->c_cv, NULL, CV_DRIVER, NULL);
1073 
1074 	if ((auimpl_stream_init(&c->c_ostream, c) != 0) ||
1075 	    (auimpl_stream_init(&c->c_istream, c) != 0)) {
1076 		goto failed;
1077 	}
1078 
1079 	c->c_major =		getmajor(dev);
1080 	c->c_origminor =	getminor(dev);
1081 	c->c_ops =		*ops;
1082 
1083 	(void) snprintf(scratch, sizeof (scratch), "auclnt%" PRIx64,
1084 	    atomic_inc_64_nv(&unique));
1085 	c->c_tq = ddi_taskq_create(NULL, scratch, 1, TASKQ_DEFAULTPRI, 0);
1086 	if (c->c_tq == NULL) {
1087 		audio_dev_warn(d, "client taskq_create failed");
1088 		goto failed;
1089 	}
1090 
1091 	/*
1092 	 * We hold the client lock here.
1093 	 */
1094 	rw_enter(&auimpl_client_lock, RW_WRITER);
1095 
1096 	minor = AUDIO_MN_CLONE_MASK;
1097 	for (next = list_head(list); next; next = list_next(list, next)) {
1098 		if (next->c_minor > minor) {
1099 			break;
1100 		}
1101 		minor++;
1102 	}
1103 	if (minor >= MAXMIN32) {
1104 		rw_exit(&auimpl_client_lock);
1105 		goto failed;
1106 	}
1107 	c->c_minor = minor;
1108 	list_insert_before(list, next, c);
1109 
1110 	rw_exit(&auimpl_client_lock);
1111 
1112 
1113 	return (c);
1114 
1115 failed:
1116 	auimpl_dev_release(d);
1117 	if (c->c_tq != NULL) {
1118 		ddi_taskq_destroy(c->c_tq);
1119 	}
1120 	audio_stream_fini(&c->c_ostream);
1121 	audio_stream_fini(&c->c_istream);
1122 	mutex_destroy(&c->c_lock);
1123 	cv_destroy(&c->c_cv);
1124 	kmem_free(c, sizeof (*c));
1125 	return (NULL);
1126 }
1127 
1128 void
1129 auimpl_client_destroy(audio_client_t *c)
1130 {
1131 	/* remove us from the global list */
1132 	rw_enter(&auimpl_client_lock, RW_WRITER);
1133 	list_remove(&auimpl_clients, c);
1134 	rw_exit(&auimpl_client_lock);
1135 
1136 	ASSERT(!c->c_istream.s_running);
1137 	ASSERT(!c->c_istream.s_running);
1138 
1139 	/* release the device reference count */
1140 	auimpl_dev_release(c->c_dev);
1141 	c->c_dev = NULL;
1142 
1143 	ddi_taskq_destroy(c->c_tq);
1144 
1145 	mutex_destroy(&c->c_lock);
1146 	cv_destroy(&c->c_cv);
1147 
1148 	audio_stream_fini(&c->c_istream);
1149 	audio_stream_fini(&c->c_ostream);
1150 	kmem_free(c, sizeof (*c));
1151 }
1152 
1153 void
1154 auclnt_close(audio_client_t *c)
1155 {
1156 	audio_dev_t	*d = c->c_dev;
1157 
1158 	/* stop the engines if they are running */
1159 	auclnt_stop(&c->c_istream);
1160 	auclnt_stop(&c->c_ostream);
1161 
1162 	rw_enter(&d->d_clnt_lock, RW_WRITER);
1163 	list_remove(&d->d_clients, c);
1164 	rw_exit(&d->d_clnt_lock);
1165 
1166 	mutex_enter(&c->c_lock);
1167 	/* if in transition need to wait for other thread to release */
1168 	while (c->c_refcnt) {
1169 		cv_wait(&c->c_cv, &c->c_lock);
1170 	}
1171 	c->c_closing = B_TRUE;
1172 	cv_broadcast(&c->c_cv);
1173 	mutex_exit(&c->c_lock);
1174 
1175 	/* make sure taskq has drained */
1176 	ddi_taskq_wait(c->c_tq);
1177 
1178 	/* release any engines that we were holding */
1179 	auimpl_engine_close(&c->c_ostream);
1180 	auimpl_engine_close(&c->c_istream);
1181 }
1182 
1183 audio_dev_t *
1184 auclnt_hold_dev_by_index(int index)
1185 {
1186 	return (auimpl_dev_hold_by_index(index));
1187 }
1188 
1189 void
1190 auclnt_release_dev(audio_dev_t *dev)
1191 {
1192 	auimpl_dev_release(dev);
1193 }
1194 
1195 audio_client_t *
1196 auclnt_hold_by_devt(dev_t dev)
1197 {
1198 	minor_t	mn = getminor(dev);
1199 	major_t mj = getmajor(dev);
1200 	list_t *list;
1201 	audio_client_t *c;
1202 
1203 	list = &auimpl_clients;
1204 	/* linked list search is kind of inefficient, but it works */
1205 	rw_enter(&auimpl_client_lock, RW_READER);
1206 	for (c = list_head(list); c != NULL; c = list_next(list, c)) {
1207 		if ((c->c_major == mj) && (c->c_minor == mn)) {
1208 			mutex_enter(&c->c_lock);
1209 			if (c->c_is_open) {
1210 				c->c_refcnt++;
1211 				mutex_exit(&c->c_lock);
1212 			} else {
1213 				mutex_exit(&c->c_lock);
1214 				c = NULL;
1215 			}
1216 			break;
1217 		}
1218 	}
1219 	rw_exit(&auimpl_client_lock);
1220 	return (c);
1221 }
1222 
1223 void
1224 auclnt_release(audio_client_t *c)
1225 {
1226 	mutex_enter(&c->c_lock);
1227 	c->c_refcnt--;
1228 	if (c->c_refcnt == 0)
1229 		cv_broadcast(&c->c_cv);
1230 	mutex_exit(&c->c_lock);
1231 }
1232 
1233 void
1234 auclnt_dev_walk_clients(audio_dev_t *d,
1235     int (*walker)(audio_client_t *, void *),
1236     void *arg)
1237 {
1238 	list_t		*l = &d->d_clients;
1239 	audio_client_t	*c;
1240 	int		rv;
1241 
1242 	rw_enter(&d->d_clnt_lock, RW_READER);
1243 restart:
1244 	for (c = list_head(l); c != NULL; c = list_next(l, c)) {
1245 		rv = (walker(c, arg));
1246 		if (rv == AUDIO_WALK_STOP) {
1247 			break;
1248 		} else if (rv == AUDIO_WALK_RESTART) {
1249 			goto restart;
1250 		}
1251 	}
1252 	rw_exit(&d->d_clnt_lock);
1253 }
1254 
1255 
1256 int
1257 auclnt_open(audio_client_t *c, unsigned fmts, int oflag)
1258 {
1259 	audio_stream_t	*sp;
1260 	audio_dev_t	*d = c->c_dev;
1261 	int		rv = 0;
1262 	int		flags;
1263 	audio_parms_t	parms;
1264 
1265 	flags = 0;
1266 	if (oflag & FNDELAY)
1267 		flags |= ENGINE_NDELAY;
1268 
1269 	if (oflag & FWRITE) {
1270 		sp = &c->c_ostream;
1271 		rv = auimpl_engine_open(d, fmts, flags | ENGINE_OUTPUT, sp);
1272 
1273 		if (rv != 0) {
1274 			goto done;
1275 		}
1276 		mutex_enter(&sp->s_lock);
1277 		parms = *sp->s_user_parms;
1278 		rv = auimpl_format_setup(sp, &parms);
1279 		mutex_exit(&sp->s_lock);
1280 		if (rv != 0) {
1281 			goto done;
1282 		}
1283 	}
1284 
1285 	if (oflag & FREAD) {
1286 		sp = &c->c_istream;
1287 		rv = auimpl_engine_open(d, fmts, flags | ENGINE_INPUT, sp);
1288 
1289 		if (rv != 0) {
1290 			goto done;
1291 		}
1292 		mutex_enter(&sp->s_lock);
1293 		parms = *sp->s_user_parms;
1294 		rv = auimpl_format_setup(sp, &parms);
1295 		mutex_exit(&sp->s_lock);
1296 		if (rv != 0) {
1297 			goto done;
1298 		}
1299 	}
1300 
1301 	if (ddi_taskq_dispatch(c->c_tq, auimpl_client_task, c, DDI_NOSLEEP) !=
1302 	    DDI_SUCCESS) {
1303 		audio_dev_warn(d, "unable to start client taskq");
1304 		rv = ENOMEM;
1305 	}
1306 
1307 done:
1308 	if (rv != 0) {
1309 		/* close any engines that we opened */
1310 		auimpl_engine_close(&c->c_ostream);
1311 		auimpl_engine_close(&c->c_istream);
1312 	} else {
1313 		rw_enter(&d->d_clnt_lock, RW_WRITER);
1314 		list_insert_tail(&d->d_clients, c);
1315 		c->c_ostream.s_gain_master = d->d_pcmvol;
1316 		c->c_istream.s_gain_master = 100;
1317 		rw_exit(&d->d_clnt_lock);
1318 		auclnt_set_gain(&c->c_ostream, 100);
1319 		auclnt_set_gain(&c->c_istream, 100);
1320 	}
1321 
1322 	return (rv);
1323 }
1324 
1325 minor_t
1326 auclnt_get_minor(audio_client_t *c)
1327 {
1328 	return (c->c_minor);
1329 }
1330 
1331 minor_t
1332 auclnt_get_original_minor(audio_client_t *c)
1333 {
1334 	return (c->c_origminor);
1335 }
1336 
1337 minor_t
1338 auclnt_get_minor_type(audio_client_t *c)
1339 {
1340 	return (c->c_origminor & AUDIO_MN_TYPE_MASK);
1341 }
1342 
1343 pid_t
1344 auclnt_get_pid(audio_client_t *c)
1345 {
1346 	return (c->c_pid);
1347 }
1348 
1349 cred_t *
1350 auclnt_get_cred(audio_client_t *c)
1351 {
1352 	return (c->c_cred);
1353 }
1354 
1355 audio_dev_t *
1356 auclnt_get_dev(audio_client_t *c)
1357 {
1358 	return (c->c_dev);
1359 }
1360 
1361 int
1362 auclnt_get_dev_number(audio_dev_t *dev)
1363 {
1364 	return (dev->d_number);
1365 }
1366 
1367 int
1368 auclnt_get_dev_index(audio_dev_t *dev)
1369 {
1370 	return (dev->d_index);
1371 }
1372 
1373 const char *
1374 auclnt_get_dev_name(audio_dev_t *dev)
1375 {
1376 	return (dev->d_name);
1377 }
1378 
1379 const char *
1380 auclnt_get_dev_driver(audio_dev_t *dev)
1381 {
1382 	return (ddi_driver_name(dev->d_dip));
1383 }
1384 
1385 dev_info_t *
1386 auclnt_get_dev_devinfo(audio_dev_t *dev)
1387 {
1388 	return (dev->d_dip);
1389 }
1390 
1391 const char *
1392 auclnt_get_dev_hw_info(audio_dev_t *dev, void **iter)
1393 {
1394 	struct audio_infostr *isp = *iter;
1395 	if (isp == NULL) {
1396 		isp = list_head(&dev->d_hwinfo);
1397 	} else {
1398 		isp = list_next(&dev->d_hwinfo, isp);
1399 	}
1400 
1401 	*iter = isp;
1402 	return (isp ? isp->i_line : NULL);
1403 }
1404 
1405 int
1406 auclnt_get_dev_instance(audio_dev_t *dev)
1407 {
1408 	return (dev->d_instance);
1409 }
1410 
1411 const char *
1412 auclnt_get_dev_description(audio_dev_t *dev)
1413 {
1414 	return (dev->d_desc);
1415 }
1416 
1417 const char *
1418 auclnt_get_dev_version(audio_dev_t *dev)
1419 {
1420 	return (dev->d_vers);
1421 }
1422 
1423 unsigned
1424 auclnt_get_dev_capab(audio_dev_t *dev)
1425 {
1426 	uint32_t	flags;
1427 	unsigned	caps = 0;
1428 
1429 	flags = dev->d_flags;
1430 
1431 	if (flags & DEV_OUTPUT_CAP)
1432 		caps |= AUDIO_CLIENT_CAP_PLAY;
1433 	if (flags & DEV_INPUT_CAP)
1434 		caps |= AUDIO_CLIENT_CAP_RECORD;
1435 	if (flags & DEV_DUPLEX_CAP)
1436 		caps |= AUDIO_CLIENT_CAP_DUPLEX;
1437 
1438 	/* AC3: deal with formats that don't support mixing */
1439 
1440 	return (caps);
1441 }
1442 
1443 void
1444 auclnt_notify_dev(audio_dev_t *dev)
1445 {
1446 	list_t *l = &dev->d_clients;
1447 	audio_client_t *c;
1448 
1449 	rw_enter(&dev->d_clnt_lock, RW_READER);
1450 	for (c = list_head(l); c != NULL; c = list_next(l, c)) {
1451 		mutex_enter(&c->c_lock);
1452 		c->c_do_notify = B_TRUE;
1453 		cv_broadcast(&c->c_cv);
1454 		mutex_exit(&c->c_lock);
1455 	}
1456 	rw_exit(&dev->d_clnt_lock);
1457 }
1458 
1459 uint64_t
1460 auclnt_get_samples(audio_stream_t *sp)
1461 {
1462 	uint64_t	n;
1463 
1464 	mutex_enter(&sp->s_lock);
1465 	n = sp->s_samples;
1466 	mutex_exit(&sp->s_lock);
1467 	return (n);
1468 }
1469 
1470 void
1471 auclnt_set_samples(audio_stream_t *sp, uint64_t n)
1472 {
1473 	mutex_enter(&sp->s_lock);
1474 	sp->s_samples = n;
1475 	mutex_exit(&sp->s_lock);
1476 }
1477 
1478 uint64_t
1479 auclnt_get_errors(audio_stream_t *sp)
1480 {
1481 	uint64_t	n;
1482 	mutex_enter(&sp->s_lock);
1483 	n = sp->s_errors;
1484 	mutex_exit(&sp->s_lock);
1485 	return (n);
1486 }
1487 
1488 void
1489 auclnt_set_errors(audio_stream_t *sp, uint64_t n)
1490 {
1491 	mutex_enter(&sp->s_lock);
1492 	sp->s_errors = n;
1493 	mutex_exit(&sp->s_lock);
1494 }
1495 
1496 void
1497 auclnt_register_ops(minor_t minor, audio_client_ops_t *ops)
1498 {
1499 	/* we control minor number allocations, no need for runtime checks */
1500 	ASSERT(minor <= AUDIO_MN_TYPE_MASK);
1501 
1502 	audio_client_ops[minor] = ops;
1503 }
1504 
1505 int
1506 auimpl_create_minors(audio_dev_t *d)
1507 {
1508 	char			path[MAXPATHLEN];
1509 	int			rv = 0;
1510 	minor_t			minor;
1511 	audio_client_ops_t	*ops;
1512 	char			*nt;
1513 
1514 	for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) {
1515 
1516 		if ((ops = audio_client_ops[i]) == NULL)
1517 			continue;
1518 
1519 		if (ops->aco_dev_init != NULL)
1520 			d->d_minor_data[i] = ops->aco_dev_init(d);
1521 
1522 		switch (i) {
1523 		case AUDIO_MINOR_SNDSTAT:
1524 			if (!(d->d_flags & DEV_SNDSTAT_CAP)) {
1525 				continue;
1526 			}
1527 			nt = DDI_PSEUDO;
1528 			break;
1529 
1530 		default:
1531 			if (!(d->d_flags & (DEV_INPUT_CAP| DEV_OUTPUT_CAP))) {
1532 				continue;
1533 			}
1534 			nt = DDI_NT_AUDIO;
1535 			break;
1536 		}
1537 
1538 		if (ops->aco_minor_prefix != NULL) {
1539 
1540 			minor = AUDIO_MKMN(d->d_instance, i);
1541 			(void) snprintf(path, sizeof (path),
1542 			    "%s%d", ops->aco_minor_prefix, d->d_instance);
1543 
1544 			rv = ddi_create_minor_node(d->d_dip, path, S_IFCHR,
1545 			    minor, nt, 0);
1546 
1547 			if (rv != 0)
1548 				break;
1549 		}
1550 	}
1551 	return (rv);
1552 }
1553 
1554 void
1555 auimpl_remove_minors(audio_dev_t *d)
1556 {
1557 	char			path[MAXPATHLEN];
1558 	audio_client_ops_t	*ops;
1559 
1560 	for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) {
1561 		if ((ops = audio_client_ops[i]) == NULL)
1562 			continue;
1563 		if (ops->aco_minor_prefix != NULL) {
1564 			(void) snprintf(path, sizeof (path), "%s%d",
1565 			    ops->aco_minor_prefix, d->d_instance);
1566 			(void) ddi_remove_minor_node(d->d_dip, path);
1567 		}
1568 
1569 		if (ops->aco_dev_fini != NULL)
1570 			ops->aco_dev_fini(d->d_minor_data[i]);
1571 	}
1572 }
1573 
1574 void *
1575 auclnt_get_dev_minor_data(audio_dev_t *d, minor_t mn)
1576 {
1577 	ASSERT(mn < (1U << AUDIO_MN_TYPE_NBITS));
1578 	return (d->d_minor_data[mn]);
1579 }
1580 
1581 void *
1582 auclnt_get_minor_data(audio_client_t *c, minor_t mn)
1583 {
1584 	ASSERT(mn < (1U << AUDIO_MN_TYPE_NBITS));
1585 	return (c->c_dev->d_minor_data[mn]);
1586 }
1587 
1588 /*
1589  * This will walk all controls registered to a clients device and callback
1590  * to walker for each one with its audio_ctrl. Note this data
1591  * must be considered read only by walker.
1592  *
1593  * Note that walk_func may return values to continue (AUDIO_WALK_CONTINUE)
1594  * or stop walk (AUDIO_WALK_STOP).
1595  *
1596  */
1597 void
1598 auclnt_walk_controls(audio_dev_t *d,
1599     int (*walker)(audio_ctrl_t *, void *),
1600     void *arg)
1601 {
1602 	audio_ctrl_t *ctrl;
1603 
1604 	rw_enter(&d->d_ctrl_lock, RW_READER);
1605 	for (ctrl = list_head(&d->d_controls); ctrl;
1606 	    ctrl = list_next(&d->d_controls, ctrl)) {
1607 		if (walker(ctrl, arg) == AUDIO_WALK_STOP)
1608 			break;
1609 	}
1610 	rw_exit(&d->d_ctrl_lock);
1611 }
1612 
1613 /*
1614  * This will search all controls attached to an
1615  * audio device for a control with the desired name.
1616  *
1617  * d    - the audio device to look on
1618  * name - name of the control being looked for.
1619  *
1620  * On successful return a ctrl handle will be returned. On
1621  * failure NULL is returned.
1622  */
1623 audio_ctrl_t *
1624 auclnt_find_control(audio_dev_t *d, const char *name)
1625 {
1626 	audio_ctrl_t *ctrl;
1627 
1628 	/* Verify argument */
1629 	ASSERT(d);
1630 
1631 	rw_enter(&d->d_ctrl_lock, RW_READER);
1632 	for (ctrl = list_head(&d->d_controls); ctrl;
1633 	    ctrl = list_next(&d->d_controls, ctrl)) {
1634 		if (strcmp(ctrl->ctrl_name, name) == 0) {
1635 			rw_exit(&d->d_ctrl_lock);
1636 			return (ctrl);
1637 		}
1638 	}
1639 	rw_exit(&d->d_ctrl_lock);
1640 	return (NULL);
1641 }
1642 
1643 /*
1644  * Given a known control, get its attributes.
1645  *
1646  * The caller must supply a audio_ctrl_desc_t structure.  Also the
1647  * values in the structure are ignored when making the call and filled
1648  * in by this function. All data pointed to by elements of desc should
1649  * be assumed read only.
1650  *
1651  * If an error occurs then a non-zero is returned.
1652  *
1653  */
1654 int
1655 auclnt_control_describe(audio_ctrl_t *ctrl, audio_ctrl_desc_t *desc)
1656 {
1657 	ASSERT(ctrl);
1658 	ASSERT(desc);
1659 
1660 	bcopy(&ctrl->ctrl_des, desc, sizeof (*desc));
1661 	return (0);
1662 }
1663 
1664 int
1665 auclnt_control_read(audio_ctrl_t *ctrl, uint64_t *value)
1666 {
1667 	return (audio_control_read(ctrl, value));
1668 }
1669 
1670 int
1671 auclnt_control_write(audio_ctrl_t *ctrl, uint64_t value)
1672 {
1673 	return (audio_control_write(ctrl, value));
1674 }
1675 
1676 void
1677 auclnt_warn(audio_client_t *c, const char *fmt, ...)
1678 {
1679 	va_list va;
1680 
1681 	va_start(va, fmt);
1682 	auimpl_dev_vwarn(c ? c->c_dev : NULL, fmt, va);
1683 	va_end(va);
1684 }
1685