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