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