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