188447a05SGarrett D'Amore /*
288447a05SGarrett D'Amore  * CDDL HEADER START
388447a05SGarrett D'Amore  *
488447a05SGarrett D'Amore  * The contents of this file are subject to the terms of the
588447a05SGarrett D'Amore  * Common Development and Distribution License (the "License").
688447a05SGarrett D'Amore  * You may not use this file except in compliance with the License.
788447a05SGarrett D'Amore  *
888447a05SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988447a05SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
1088447a05SGarrett D'Amore  * See the License for the specific language governing permissions
1188447a05SGarrett D'Amore  * and limitations under the License.
1288447a05SGarrett D'Amore  *
1388447a05SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
1488447a05SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588447a05SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
1688447a05SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
1788447a05SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
1888447a05SGarrett D'Amore  *
1988447a05SGarrett D'Amore  * CDDL HEADER END
2088447a05SGarrett D'Amore  */
2188447a05SGarrett D'Amore /*
2288447a05SGarrett D'Amore  * Copyright (C) 4Front Technologies 1996-2008.
2388447a05SGarrett D'Amore  *
242c30fa45SGarrett D'Amore  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2588447a05SGarrett D'Amore  */
2688447a05SGarrett D'Amore 
2788447a05SGarrett D'Amore #include <sys/types.h>
2888447a05SGarrett D'Amore #include <sys/sysmacros.h>
2988447a05SGarrett D'Amore #include <sys/list.h>
3088447a05SGarrett D'Amore #include <sys/file.h>
3188447a05SGarrett D'Amore #include <sys/open.h>
3288447a05SGarrett D'Amore #include <sys/stat.h>
3388447a05SGarrett D'Amore #include <sys/errno.h>
3488447a05SGarrett D'Amore #include <sys/atomic.h>
3588447a05SGarrett D'Amore #include <sys/ddi.h>
3688447a05SGarrett D'Amore #include <sys/sunddi.h>
3788447a05SGarrett D'Amore 
3888447a05SGarrett D'Amore #include "audio_impl.h"
3988447a05SGarrett D'Amore 
4088447a05SGarrett D'Amore /*
4188447a05SGarrett D'Amore  * Audio Client implementation.
4288447a05SGarrett D'Amore  */
4388447a05SGarrett D'Amore 
4488447a05SGarrett D'Amore /*
4588447a05SGarrett D'Amore  * Attenuation table for dB->linear conversion. Indexed in steps of
4688447a05SGarrett D'Amore  * 0.5 dB.  Table size is 25 dB (first entry is handled as mute).
4788447a05SGarrett D'Amore  *
4888447a05SGarrett D'Amore  * Notably, the last item in table is taken as 0 dB (i.e. maximum volume).
4988447a05SGarrett D'Amore  *
5088447a05SGarrett D'Amore  * Table contents can be calculated as follows (requires sunmath library):
5188447a05SGarrett D'Amore  *
5288447a05SGarrett D'Amore  * scale = AUDIO_VOL_SCALE;
5388447a05SGarrett D'Amore  * for (i = -50; i <= 0; i++) {
5488447a05SGarrett D'Amore  *     x = exp10(0.05 * i);
5588447a05SGarrett D'Amore  *     printf("%d: %f %.0f\n", i,  x, trunc(x * scale));
5688447a05SGarrett D'Amore  * }
5788447a05SGarrett D'Amore  *
5888447a05SGarrett D'Amore  */
5988447a05SGarrett D'Amore 
6053a539a7SGarrett D'Amore static const uint16_t auimpl_db_table[AUDIO_DB_SIZE + 1] = {
6188447a05SGarrett D'Amore 	0,   0,   1,   1,   1,   1,   1,   1,   2,   2,
6288447a05SGarrett D'Amore 	2,   2,   3,   3,   4,   4,   5,   5,   6,   7,
6388447a05SGarrett D'Amore 	8,   9,   10,  11,  12,  14,  16,  18,  20,  22,
6488447a05SGarrett D'Amore 	25,  28,  32,  36,  40,  45,  51,  57,  64,  72,
6588447a05SGarrett D'Amore 	80,  90,  101, 114, 128, 143, 161, 181, 203, 228,
6688447a05SGarrett D'Amore 	256
6788447a05SGarrett D'Amore };
6888447a05SGarrett D'Amore 
6953a539a7SGarrett D'Amore static list_t			auimpl_clients;
7053a539a7SGarrett D'Amore static krwlock_t		auimpl_client_lock;
7153a539a7SGarrett D'Amore static audio_client_ops_t	*audio_client_ops[AUDIO_MN_TYPE_MASK + 1];
7253a539a7SGarrett D'Amore 
7388447a05SGarrett D'Amore void *
auclnt_get_private(audio_client_t * c)7488447a05SGarrett D'Amore auclnt_get_private(audio_client_t *c)
7588447a05SGarrett D'Amore {
7688447a05SGarrett D'Amore 	return (c->c_private);
7788447a05SGarrett D'Amore }
7888447a05SGarrett D'Amore 
7988447a05SGarrett D'Amore void
auclnt_set_private(audio_client_t * c,void * private)8088447a05SGarrett D'Amore auclnt_set_private(audio_client_t *c, void *private)
8188447a05SGarrett D'Amore {
8288447a05SGarrett D'Amore 	c->c_private = private;
8388447a05SGarrett D'Amore }
8488447a05SGarrett D'Amore 
8588447a05SGarrett D'Amore int
auclnt_set_rate(audio_stream_t * sp,int rate)8688447a05SGarrett D'Amore auclnt_set_rate(audio_stream_t *sp, int rate)
8788447a05SGarrett D'Amore {
8888447a05SGarrett D'Amore 	audio_parms_t	parms;
8988447a05SGarrett D'Amore 	int		rv = 0;
9088447a05SGarrett D'Amore 
9188447a05SGarrett D'Amore 	/* basic sanity checks! */
9288447a05SGarrett D'Amore 	if ((rate < 5000) || (rate > 192000)) {
9388447a05SGarrett D'Amore 		return (EINVAL);
9488447a05SGarrett D'Amore 	}
952c30fa45SGarrett D'Amore 	if (rate != sp->s_user_parms->p_rate) {
9688447a05SGarrett D'Amore 		parms.p_rate = rate;
972c30fa45SGarrett D'Amore 		rv = auimpl_engine_setup(sp, 0, &parms, FORMAT_MSK_RATE);
9888447a05SGarrett D'Amore 	}
9988447a05SGarrett D'Amore 	return (rv);
10088447a05SGarrett D'Amore }
10188447a05SGarrett D'Amore 
10288447a05SGarrett D'Amore int
auclnt_get_rate(audio_stream_t * sp)10388447a05SGarrett D'Amore auclnt_get_rate(audio_stream_t *sp)
10488447a05SGarrett D'Amore {
10588447a05SGarrett D'Amore 	return (sp->s_user_parms->p_rate);
10688447a05SGarrett D'Amore }
10788447a05SGarrett D'Amore 
10868c47f65SGarrett D'Amore uint_t
auclnt_get_fragsz(audio_stream_t * sp)10988447a05SGarrett D'Amore auclnt_get_fragsz(audio_stream_t *sp)
11088447a05SGarrett D'Amore {
11188447a05SGarrett D'Amore 	return (sp->s_fragbytes);
11288447a05SGarrett D'Amore }
11388447a05SGarrett D'Amore 
11468c47f65SGarrett D'Amore uint_t
auclnt_get_framesz(audio_stream_t * sp)11588447a05SGarrett D'Amore auclnt_get_framesz(audio_stream_t *sp)
11688447a05SGarrett D'Amore {
11788447a05SGarrett D'Amore 	return (sp->s_framesz);
11888447a05SGarrett D'Amore }
11988447a05SGarrett D'Amore 
12068c47f65SGarrett D'Amore uint_t
auclnt_get_nfrags(audio_stream_t * sp)12188447a05SGarrett D'Amore auclnt_get_nfrags(audio_stream_t *sp)
12288447a05SGarrett D'Amore {
12388447a05SGarrett D'Amore 	return (sp->s_nfrags);
12488447a05SGarrett D'Amore }
12588447a05SGarrett D'Amore 
12668c47f65SGarrett D'Amore uint_t
auclnt_get_nframes(audio_stream_t * sp)12788447a05SGarrett D'Amore auclnt_get_nframes(audio_stream_t *sp)
12888447a05SGarrett D'Amore {
12988447a05SGarrett D'Amore 	return (sp->s_nframes);
13088447a05SGarrett D'Amore }
13188447a05SGarrett D'Amore 
132a702341cSGarrett D'Amore void
auclnt_set_latency(audio_stream_t * sp,uint_t frags,uint_t bytes)13368c47f65SGarrett D'Amore auclnt_set_latency(audio_stream_t *sp, uint_t frags, uint_t bytes)
134a702341cSGarrett D'Amore {
135a702341cSGarrett D'Amore 	mutex_enter(&sp->s_lock);
136a702341cSGarrett D'Amore 	sp->s_hintfrags = (uint16_t)frags;
137a702341cSGarrett D'Amore 	sp->s_hintsz = bytes;
138a702341cSGarrett D'Amore 	mutex_exit(&sp->s_lock);
139a702341cSGarrett D'Amore }
140a702341cSGarrett D'Amore 
14188447a05SGarrett D'Amore uint64_t
auclnt_get_head(audio_stream_t * sp)14288447a05SGarrett D'Amore auclnt_get_head(audio_stream_t *sp)
14388447a05SGarrett D'Amore {
14488447a05SGarrett D'Amore 	return (sp->s_head);
14588447a05SGarrett D'Amore }
14688447a05SGarrett D'Amore 
14788447a05SGarrett D'Amore uint64_t
auclnt_get_tail(audio_stream_t * sp)14888447a05SGarrett D'Amore auclnt_get_tail(audio_stream_t *sp)
14988447a05SGarrett D'Amore {
15088447a05SGarrett D'Amore 	return (sp->s_tail);
15188447a05SGarrett D'Amore }
15288447a05SGarrett D'Amore 
15368c47f65SGarrett D'Amore uint_t
auclnt_get_hidx(audio_stream_t * sp)15488447a05SGarrett D'Amore auclnt_get_hidx(audio_stream_t *sp)
15588447a05SGarrett D'Amore {
15688447a05SGarrett D'Amore 	return (sp->s_hidx);
15788447a05SGarrett D'Amore }
15888447a05SGarrett D'Amore 
15968c47f65SGarrett D'Amore uint_t
auclnt_get_tidx(audio_stream_t * sp)16088447a05SGarrett D'Amore auclnt_get_tidx(audio_stream_t *sp)
16188447a05SGarrett D'Amore {
16288447a05SGarrett D'Amore 	return (sp->s_tidx);
16388447a05SGarrett D'Amore }
16488447a05SGarrett D'Amore 
16588447a05SGarrett D'Amore audio_stream_t *
auclnt_input_stream(audio_client_t * c)16688447a05SGarrett D'Amore auclnt_input_stream(audio_client_t *c)
16788447a05SGarrett D'Amore {
16888447a05SGarrett D'Amore 	return (&c->c_istream);
16988447a05SGarrett D'Amore }
17088447a05SGarrett D'Amore 
17188447a05SGarrett D'Amore audio_stream_t *
auclnt_output_stream(audio_client_t * c)17288447a05SGarrett D'Amore auclnt_output_stream(audio_client_t *c)
17388447a05SGarrett D'Amore {
17488447a05SGarrett D'Amore 	return (&c->c_ostream);
17588447a05SGarrett D'Amore }
17688447a05SGarrett D'Amore 
17768c47f65SGarrett D'Amore uint_t
auclnt_get_count(audio_stream_t * sp)17888447a05SGarrett D'Amore auclnt_get_count(audio_stream_t *sp)
17988447a05SGarrett D'Amore {
18068c47f65SGarrett D'Amore 	uint_t	count;
18188447a05SGarrett D'Amore 
18288447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
18388447a05SGarrett D'Amore 	ASSERT((sp->s_head - sp->s_tail) <= sp->s_nframes);
18468c47f65SGarrett D'Amore 	count = (uint_t)(sp->s_head - sp->s_tail);
18588447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
18688447a05SGarrett D'Amore 
18788447a05SGarrett D'Amore 	return (count);
18888447a05SGarrett D'Amore }
18988447a05SGarrett D'Amore 
19068c47f65SGarrett D'Amore uint_t
auclnt_consume(audio_stream_t * sp,uint_t n)19168c47f65SGarrett D'Amore auclnt_consume(audio_stream_t *sp, uint_t n)
19288447a05SGarrett D'Amore {
19388447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
19488447a05SGarrett D'Amore 
19588447a05SGarrett D'Amore 	ASSERT(sp == &sp->s_client->c_istream);
19688447a05SGarrett D'Amore 	n = max(n, sp->s_head - sp->s_tail);
19788447a05SGarrett D'Amore 	sp->s_tail += n;
19888447a05SGarrett D'Amore 	sp->s_tidx += n;
19988447a05SGarrett D'Amore 	if (sp->s_tidx >= sp->s_nframes) {
20088447a05SGarrett D'Amore 		sp->s_tidx -= sp->s_nframes;
20188447a05SGarrett D'Amore 	}
20288447a05SGarrett D'Amore 
20388447a05SGarrett D'Amore 	ASSERT(sp->s_tail <= sp->s_head);
20488447a05SGarrett D'Amore 	ASSERT(sp->s_hidx < sp->s_nframes);
20588447a05SGarrett D'Amore 
20688447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
20788447a05SGarrett D'Amore 
20888447a05SGarrett D'Amore 	return (n);
20988447a05SGarrett D'Amore }
21088447a05SGarrett D'Amore 
21168c47f65SGarrett D'Amore uint_t
auclnt_consume_data(audio_stream_t * sp,caddr_t dst,uint_t n)21268c47f65SGarrett D'Amore auclnt_consume_data(audio_stream_t *sp, caddr_t dst, uint_t n)
21388447a05SGarrett D'Amore {
21468c47f65SGarrett D'Amore 	uint_t nframes;
21568c47f65SGarrett D'Amore 	uint_t framesz;
21668c47f65SGarrett D'Amore 	uint_t cnt;
21788447a05SGarrett D'Amore 	caddr_t	data;
21888447a05SGarrett D'Amore 
21988447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
22088447a05SGarrett D'Amore 
22188447a05SGarrett D'Amore 	nframes = sp->s_nframes;
22288447a05SGarrett D'Amore 	framesz = sp->s_framesz;
22388447a05SGarrett D'Amore 
22488447a05SGarrett D'Amore 	ASSERT(sp == &sp->s_client->c_istream);
22588447a05SGarrett D'Amore 	ASSERT(sp->s_head >= sp->s_tail);
22688447a05SGarrett D'Amore 	ASSERT(sp->s_tidx < nframes);
22788447a05SGarrett D'Amore 	ASSERT(sp->s_hidx < nframes);
22888447a05SGarrett D'Amore 
22988447a05SGarrett D'Amore 	cnt = n = min(n, sp->s_head - sp->s_tail);
23088447a05SGarrett D'Amore 	data = sp->s_data + (sp->s_tidx * framesz);
23188447a05SGarrett D'Amore 	do {
23268c47f65SGarrett D'Amore 		uint_t nf, nb;
23388447a05SGarrett D'Amore 
23488447a05SGarrett D'Amore 		nf = min(nframes - sp->s_tidx, n);
23588447a05SGarrett D'Amore 		nb = nf * framesz;
23688447a05SGarrett D'Amore 
23788447a05SGarrett D'Amore 		bcopy(data, dst, nb);
23888447a05SGarrett D'Amore 		dst += nb;
23988447a05SGarrett D'Amore 		data += nb;
24088447a05SGarrett D'Amore 
24188447a05SGarrett D'Amore 		n -= nf;
24288447a05SGarrett D'Amore 		sp->s_tail += nf;
24388447a05SGarrett D'Amore 		sp->s_tidx += nf;
24488447a05SGarrett D'Amore 		if (sp->s_tidx == nframes) {
24588447a05SGarrett D'Amore 			sp->s_tidx = 0;
24688447a05SGarrett D'Amore 			data = sp->s_data;
24788447a05SGarrett D'Amore 		}
24888447a05SGarrett D'Amore 	} while (n);
24988447a05SGarrett D'Amore 
25088447a05SGarrett D'Amore 	ASSERT(sp->s_tail <= sp->s_head);
25188447a05SGarrett D'Amore 	ASSERT(sp->s_tidx < nframes);
25288447a05SGarrett D'Amore 
25388447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
25488447a05SGarrett D'Amore 
25588447a05SGarrett D'Amore 	return (cnt);
25688447a05SGarrett D'Amore }
25788447a05SGarrett D'Amore 
25868c47f65SGarrett D'Amore uint_t
auclnt_produce(audio_stream_t * sp,uint_t n)25968c47f65SGarrett D'Amore auclnt_produce(audio_stream_t *sp, uint_t n)
26088447a05SGarrett D'Amore {
26188447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
26288447a05SGarrett D'Amore 
26388447a05SGarrett D'Amore 	ASSERT(sp == &sp->s_client->c_ostream);
26488447a05SGarrett D'Amore 	n = max(n, sp->s_nframes - (sp->s_head - sp->s_tail));
26588447a05SGarrett D'Amore 	sp->s_head += n;
26688447a05SGarrett D'Amore 	sp->s_hidx += n;
26788447a05SGarrett D'Amore 	if (sp->s_hidx >= sp->s_nframes) {
26888447a05SGarrett D'Amore 		sp->s_hidx -= sp->s_nframes;
26988447a05SGarrett D'Amore 	}
27088447a05SGarrett D'Amore 
27188447a05SGarrett D'Amore 	ASSERT(sp->s_tail <= sp->s_head);
27288447a05SGarrett D'Amore 	ASSERT(sp->s_hidx < sp->s_nframes);
27388447a05SGarrett D'Amore 
27488447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
27588447a05SGarrett D'Amore 
27688447a05SGarrett D'Amore 	return (n);
27788447a05SGarrett D'Amore }
27888447a05SGarrett D'Amore 
27968c47f65SGarrett D'Amore uint_t
auclnt_produce_data(audio_stream_t * sp,caddr_t src,uint_t n)28068c47f65SGarrett D'Amore auclnt_produce_data(audio_stream_t *sp, caddr_t src, uint_t n)
28188447a05SGarrett D'Amore {
28268c47f65SGarrett D'Amore 	uint_t nframes;
28368c47f65SGarrett D'Amore 	uint_t framesz;
28468c47f65SGarrett D'Amore 	uint_t cnt;
28588447a05SGarrett D'Amore 	caddr_t data;
28688447a05SGarrett D'Amore 
28788447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
28888447a05SGarrett D'Amore 
28988447a05SGarrett D'Amore 	nframes = sp->s_nframes;
29088447a05SGarrett D'Amore 	framesz = sp->s_framesz;
29188447a05SGarrett D'Amore 
29288447a05SGarrett D'Amore 	ASSERT(sp == &sp->s_client->c_ostream);
29388447a05SGarrett D'Amore 	ASSERT(sp->s_head >= sp->s_tail);
29488447a05SGarrett D'Amore 	ASSERT(sp->s_tidx < nframes);
29588447a05SGarrett D'Amore 	ASSERT(sp->s_hidx < nframes);
29688447a05SGarrett D'Amore 
29788447a05SGarrett D'Amore 	cnt = n = min(n, nframes - (sp->s_head - sp->s_tail));
29888447a05SGarrett D'Amore 	data = sp->s_data + (sp->s_hidx * framesz);
29988447a05SGarrett D'Amore 	do {
30068c47f65SGarrett D'Amore 		uint_t nf, nb;
30188447a05SGarrett D'Amore 
30288447a05SGarrett D'Amore 		nf = min(nframes - sp->s_hidx, n);
30388447a05SGarrett D'Amore 		nb = nf * framesz;
30488447a05SGarrett D'Amore 
30588447a05SGarrett D'Amore 		bcopy(src, data, nb);
30688447a05SGarrett D'Amore 
30788447a05SGarrett D'Amore 		src += nb;
30888447a05SGarrett D'Amore 		data += nb;
30988447a05SGarrett D'Amore 
31088447a05SGarrett D'Amore 		n -= nf;
31188447a05SGarrett D'Amore 		sp->s_head += nf;
31288447a05SGarrett D'Amore 		sp->s_hidx += nf;
31388447a05SGarrett D'Amore 		if (sp->s_hidx == nframes) {
31488447a05SGarrett D'Amore 			sp->s_hidx = 0;
31588447a05SGarrett D'Amore 			data = sp->s_data;
31688447a05SGarrett D'Amore 		}
31788447a05SGarrett D'Amore 	} while (n);
31888447a05SGarrett D'Amore 
31988447a05SGarrett D'Amore 	ASSERT(sp->s_tail <= sp->s_head);
32088447a05SGarrett D'Amore 	ASSERT(sp->s_hidx < nframes);
32188447a05SGarrett D'Amore 
32288447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
32388447a05SGarrett D'Amore 
32488447a05SGarrett D'Amore 	return (cnt);
32588447a05SGarrett D'Amore }
32688447a05SGarrett D'Amore 
32788447a05SGarrett D'Amore int
auclnt_read(audio_client_t * c,struct uio * uio)32888447a05SGarrett D'Amore auclnt_read(audio_client_t *c, struct uio *uio)
32988447a05SGarrett D'Amore {
33088447a05SGarrett D'Amore 	audio_stream_t	*sp = &c->c_istream;
33168c47f65SGarrett D'Amore 	uint_t		cnt;
33288447a05SGarrett D'Amore 	int		rv = 0;
33388447a05SGarrett D'Amore 	offset_t	loff;
33488447a05SGarrett D'Amore 	int		eagain;
33568c47f65SGarrett D'Amore 	uint_t		tidx;
33668c47f65SGarrett D'Amore 	uint_t		framesz;
33788447a05SGarrett D'Amore 
33888447a05SGarrett D'Amore 	loff = uio->uio_loffset;
33988447a05SGarrett D'Amore 	eagain = EAGAIN;
34088447a05SGarrett D'Amore 
34188447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
34288447a05SGarrett D'Amore 
34388447a05SGarrett D'Amore 	if ((!sp->s_paused) && (!sp->s_running)) {
34488447a05SGarrett D'Amore 		mutex_exit(&sp->s_lock);
34588447a05SGarrett D'Amore 		auclnt_start(sp);
34688447a05SGarrett D'Amore 		mutex_enter(&sp->s_lock);
34788447a05SGarrett D'Amore 	}
34888447a05SGarrett D'Amore 
34968c47f65SGarrett D'Amore 
35068c47f65SGarrett D'Amore 	framesz = sp->s_framesz;
35168c47f65SGarrett D'Amore 
35288447a05SGarrett D'Amore 	ASSERT(sp->s_head >= sp->s_tail);
35388447a05SGarrett D'Amore 	ASSERT(sp->s_tidx < sp->s_nframes);
35488447a05SGarrett D'Amore 
35568c47f65SGarrett D'Amore 	while (uio->uio_resid >= framesz) {
35688447a05SGarrett D'Amore 
35788447a05SGarrett D'Amore 		while ((cnt = (sp->s_head - sp->s_tail)) == 0) {
35888447a05SGarrett D'Amore 			if (uio->uio_fmode & (FNONBLOCK|FNDELAY)) {
35988447a05SGarrett D'Amore 				mutex_exit(&sp->s_lock);
36088447a05SGarrett D'Amore 				return (eagain);
36188447a05SGarrett D'Amore 			}
36288447a05SGarrett D'Amore 			if (cv_wait_sig(&sp->s_cv, &sp->s_lock) == 0) {
36388447a05SGarrett D'Amore 				mutex_exit(&sp->s_lock);
36488447a05SGarrett D'Amore 				return (EINTR);
36588447a05SGarrett D'Amore 			}
36688447a05SGarrett D'Amore 		}
36788447a05SGarrett D'Amore 
36868c47f65SGarrett D'Amore 		tidx = sp->s_tidx;
36968c47f65SGarrett D'Amore 		cnt = min(cnt, sp->s_nframes - tidx);
37068c47f65SGarrett D'Amore 		cnt = min(cnt, (uio->uio_resid / framesz));
37168c47f65SGarrett D'Amore 
37268c47f65SGarrett D'Amore 		mutex_exit(&sp->s_lock);
37368c47f65SGarrett D'Amore 		rv = uiomove(sp->s_data + (tidx * framesz),
37468c47f65SGarrett D'Amore 		    cnt * framesz, UIO_READ, uio);
37588447a05SGarrett D'Amore 
37688447a05SGarrett D'Amore 		uio->uio_loffset = loff;
37788447a05SGarrett D'Amore 		eagain = 0;
37888447a05SGarrett D'Amore 
37988447a05SGarrett D'Amore 		if (rv != 0) {
38088447a05SGarrett D'Amore 			return (rv);
38188447a05SGarrett D'Amore 		}
38288447a05SGarrett D'Amore 
38368c47f65SGarrett D'Amore 		mutex_enter(&sp->s_lock);
38488447a05SGarrett D'Amore 		sp->s_tail += cnt;
38588447a05SGarrett D'Amore 		sp->s_tidx += cnt;
38688447a05SGarrett D'Amore 		if (sp->s_tidx == sp->s_nframes) {
38788447a05SGarrett D'Amore 			sp->s_tidx = 0;
38888447a05SGarrett D'Amore 		}
38988447a05SGarrett D'Amore 	}
39088447a05SGarrett D'Amore 
39188447a05SGarrett D'Amore 	ASSERT(sp->s_tail <= sp->s_head);
39288447a05SGarrett D'Amore 	ASSERT(sp->s_tidx < sp->s_nframes);
39388447a05SGarrett D'Amore 
39488447a05SGarrett D'Amore 	/* round off any remaining partial bits */
39588447a05SGarrett D'Amore 	uio->uio_resid = 0;
39688447a05SGarrett D'Amore 
39788447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
39888447a05SGarrett D'Amore 
39988447a05SGarrett D'Amore 	return (rv);
40088447a05SGarrett D'Amore }
40188447a05SGarrett D'Amore 
40288447a05SGarrett D'Amore int
auclnt_write(audio_client_t * c,struct uio * uio)40388447a05SGarrett D'Amore auclnt_write(audio_client_t *c, struct uio *uio)
40488447a05SGarrett D'Amore {
40588447a05SGarrett D'Amore 	audio_stream_t *sp = &c->c_ostream;
40668c47f65SGarrett D'Amore 	uint_t		cnt;
40788447a05SGarrett D'Amore 	int		rv = 0;
40888447a05SGarrett D'Amore 	offset_t	loff;
40988447a05SGarrett D'Amore 	int		eagain;
41068c47f65SGarrett D'Amore 	uint_t		framesz;
41168c47f65SGarrett D'Amore 	uint_t		hidx;
41288447a05SGarrett D'Amore 
41388447a05SGarrett D'Amore 	loff = uio->uio_loffset;
41488447a05SGarrett D'Amore 	eagain = EAGAIN;
41588447a05SGarrett D'Amore 
41688447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
41788447a05SGarrett D'Amore 
41868c47f65SGarrett D'Amore 	framesz = sp->s_framesz;
41968c47f65SGarrett D'Amore 
42088447a05SGarrett D'Amore 	ASSERT(sp->s_head >= sp->s_tail);
42188447a05SGarrett D'Amore 	ASSERT(sp->s_hidx < sp->s_nframes);
42288447a05SGarrett D'Amore 
42368c47f65SGarrett D'Amore 	while (uio->uio_resid >= framesz) {
42488447a05SGarrett D'Amore 
42588447a05SGarrett D'Amore 		while ((cnt = sp->s_nframes - (sp->s_head - sp->s_tail)) == 0) {
42688447a05SGarrett D'Amore 			if (uio->uio_fmode & (FNONBLOCK|FNDELAY)) {
42788447a05SGarrett D'Amore 				mutex_exit(&sp->s_lock);
42888447a05SGarrett D'Amore 				return (eagain);
42988447a05SGarrett D'Amore 			}
43088447a05SGarrett D'Amore 			if (cv_wait_sig(&sp->s_cv, &sp->s_lock) == 0) {
43188447a05SGarrett D'Amore 				mutex_exit(&sp->s_lock);
43288447a05SGarrett D'Amore 				return (EINTR);
43388447a05SGarrett D'Amore 			}
43488447a05SGarrett D'Amore 		}
43588447a05SGarrett D'Amore 
43668c47f65SGarrett D'Amore 		hidx = sp->s_hidx;
43768c47f65SGarrett D'Amore 		cnt = min(cnt, sp->s_nframes - hidx);
43868c47f65SGarrett D'Amore 		cnt = min(cnt, (uio->uio_resid / framesz));
43968c47f65SGarrett D'Amore 
44068c47f65SGarrett D'Amore 		/*
44168c47f65SGarrett D'Amore 		 * We have to drop the stream lock, because the
44268c47f65SGarrett D'Amore 		 * uiomove might require doing a page in, which could
44368c47f65SGarrett D'Amore 		 * get blocked behind the PIL of the audio processing
44468c47f65SGarrett D'Amore 		 * thread which also grabs the s_lock.  (Hence, there
44568c47f65SGarrett D'Amore 		 * is a risk of deadlock due to priority inversion.)
44668c47f65SGarrett D'Amore 		 */
44768c47f65SGarrett D'Amore 		mutex_exit(&sp->s_lock);
44868c47f65SGarrett D'Amore 
44968c47f65SGarrett D'Amore 		rv = uiomove(sp->s_data + (hidx * framesz),
45068c47f65SGarrett D'Amore 		    cnt * framesz, UIO_WRITE, uio);
45188447a05SGarrett D'Amore 
45288447a05SGarrett D'Amore 		uio->uio_loffset = loff;
45388447a05SGarrett D'Amore 		eagain = 0;
45488447a05SGarrett D'Amore 
45588447a05SGarrett D'Amore 		if (rv != 0) {
45688447a05SGarrett D'Amore 			return (rv);
45788447a05SGarrett D'Amore 		}
45888447a05SGarrett D'Amore 
45968c47f65SGarrett D'Amore 		mutex_enter(&sp->s_lock);
46068c47f65SGarrett D'Amore 
46188447a05SGarrett D'Amore 		sp->s_head += cnt;
46288447a05SGarrett D'Amore 		sp->s_hidx += cnt;
46388447a05SGarrett D'Amore 		if (sp->s_hidx == sp->s_nframes) {
46488447a05SGarrett D'Amore 			sp->s_hidx = 0;
46588447a05SGarrett D'Amore 		}
46688447a05SGarrett D'Amore 
46788447a05SGarrett D'Amore 		if ((!sp->s_paused) && (!sp->s_running) &&
46888447a05SGarrett D'Amore 		    ((sp->s_head - sp->s_tail) > sp->s_fragfr)) {
46988447a05SGarrett D'Amore 			mutex_exit(&sp->s_lock);
47088447a05SGarrett D'Amore 			auclnt_start(sp);
47188447a05SGarrett D'Amore 			mutex_enter(&sp->s_lock);
47288447a05SGarrett D'Amore 		}
47388447a05SGarrett D'Amore 	}
47488447a05SGarrett D'Amore 
47588447a05SGarrett D'Amore 	ASSERT(sp->s_tail <= sp->s_head);
47688447a05SGarrett D'Amore 	ASSERT(sp->s_hidx < sp->s_nframes);
47788447a05SGarrett D'Amore 
47888447a05SGarrett D'Amore 	/* round off any remaining partial bits */
47988447a05SGarrett D'Amore 	uio->uio_resid = 0;
48088447a05SGarrett D'Amore 
48188447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
48288447a05SGarrett D'Amore 
48388447a05SGarrett D'Amore 	return (rv);
48488447a05SGarrett D'Amore }
48588447a05SGarrett D'Amore 
48688447a05SGarrett D'Amore int
auclnt_chpoll(audio_client_t * c,short events,int anyyet,short * reventsp,struct pollhead ** phpp)48788447a05SGarrett D'Amore auclnt_chpoll(audio_client_t *c, short events, int anyyet, short *reventsp,
48888447a05SGarrett D'Amore     struct pollhead **phpp)
48988447a05SGarrett D'Amore {
49088447a05SGarrett D'Amore 	audio_stream_t	*sp;
49188447a05SGarrett D'Amore 	short nev = 0;
49288447a05SGarrett D'Amore 
49388447a05SGarrett D'Amore 	if (events & (POLLIN | POLLRDNORM)) {
49488447a05SGarrett D'Amore 		sp = &c->c_istream;
49588447a05SGarrett D'Amore 		mutex_enter(&sp->s_lock);
49688447a05SGarrett D'Amore 		if ((sp->s_head - sp->s_tail) > sp->s_fragfr) {
49788447a05SGarrett D'Amore 			nev = POLLIN | POLLRDNORM;
49888447a05SGarrett D'Amore 		}
49988447a05SGarrett D'Amore 		mutex_exit(&sp->s_lock);
50088447a05SGarrett D'Amore 	}
50188447a05SGarrett D'Amore 
50288447a05SGarrett D'Amore 	if (events & POLLOUT) {
50388447a05SGarrett D'Amore 		sp = &c->c_ostream;
50488447a05SGarrett D'Amore 		mutex_enter(&sp->s_lock);
50588447a05SGarrett D'Amore 		if ((sp->s_nframes - (sp->s_head - sp->s_tail)) >
50688447a05SGarrett D'Amore 		    sp->s_fragfr) {
50788447a05SGarrett D'Amore 			nev = POLLOUT;
50888447a05SGarrett D'Amore 		}
50988447a05SGarrett D'Amore 		mutex_exit(&sp->s_lock);
51088447a05SGarrett D'Amore 	}
51188447a05SGarrett D'Amore 
51288447a05SGarrett D'Amore 	if (nev) {
51388447a05SGarrett D'Amore 		*reventsp = nev & events;
51488447a05SGarrett D'Amore 	} else {
51588447a05SGarrett D'Amore 		*reventsp = 0;
51688447a05SGarrett D'Amore 		if (!anyyet) {
51788447a05SGarrett D'Amore 			*phpp = &c->c_pollhead;
51888447a05SGarrett D'Amore 		}
51988447a05SGarrett D'Amore 	}
52088447a05SGarrett D'Amore 	return (0);
52188447a05SGarrett D'Amore }
52288447a05SGarrett D'Amore 
52388447a05SGarrett D'Amore void
auclnt_pollwakeup(audio_client_t * c,short events)52488447a05SGarrett D'Amore auclnt_pollwakeup(audio_client_t *c, short events)
52588447a05SGarrett D'Amore {
52688447a05SGarrett D'Amore 	pollwakeup(&c->c_pollhead, events);
52788447a05SGarrett D'Amore }
52888447a05SGarrett D'Amore 
52988447a05SGarrett D'Amore void
auclnt_get_output_qlen(audio_client_t * c,uint_t * slen,uint_t * flen)53068c47f65SGarrett D'Amore auclnt_get_output_qlen(audio_client_t *c, uint_t *slen, uint_t *flen)
53188447a05SGarrett D'Amore {
53288447a05SGarrett D'Amore 	audio_stream_t	*sp = &c->c_ostream;
53388447a05SGarrett D'Amore 	audio_engine_t	*e = sp->s_engine;
53488447a05SGarrett D'Amore 	uint64_t	el, sl;
53568c47f65SGarrett D'Amore 	uint_t		cnt, er, sr;
53688447a05SGarrett D'Amore 
53788447a05SGarrett D'Amore 	if (e == NULL) {
53888447a05SGarrett D'Amore 		/* if no output engine, can't do it! */
53988447a05SGarrett D'Amore 		*slen = 0;
54088447a05SGarrett D'Amore 		*flen = 0;
54188447a05SGarrett D'Amore 		return;
54288447a05SGarrett D'Amore 	}
54388447a05SGarrett D'Amore 
54488447a05SGarrett D'Amore 	mutex_enter(&e->e_lock);
54588447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
546f9ead4a5SGarrett D'Amore 	if (e->e_ops.audio_engine_qlen != NULL) {
547f9ead4a5SGarrett D'Amore 		el = ENG_QLEN(e) + (e->e_head - e->e_tail);
548f9ead4a5SGarrett D'Amore 	} else {
549f9ead4a5SGarrett D'Amore 		el = (e->e_head - e->e_tail);
550f9ead4a5SGarrett D'Amore 	}
55188447a05SGarrett D'Amore 	er = e->e_rate;
55288447a05SGarrett D'Amore 	sl = sp->s_cnv_cnt;
55388447a05SGarrett D'Amore 	sr = sp->s_user_parms->p_rate;
55468c47f65SGarrett D'Amore 	cnt = (uint_t)(sp->s_head - sp->s_tail);
55588447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
55688447a05SGarrett D'Amore 	mutex_exit(&e->e_lock);
55788447a05SGarrett D'Amore 
55888447a05SGarrett D'Amore 	/* engine frames converted to stream rate, plus stream frames */
55988447a05SGarrett D'Amore 	*slen = cnt;
56068c47f65SGarrett D'Amore 	*flen = ((uint_t)(((el * sr) / er) + sl));
56188447a05SGarrett D'Amore }
56288447a05SGarrett D'Amore 
56388447a05SGarrett D'Amore int
auclnt_set_format(audio_stream_t * sp,int fmt)56488447a05SGarrett D'Amore auclnt_set_format(audio_stream_t *sp, int fmt)
56588447a05SGarrett D'Amore {
56688447a05SGarrett D'Amore 	audio_parms_t	parms;
56788447a05SGarrett D'Amore 	int		rv = 0;
56888447a05SGarrett D'Amore 
56988447a05SGarrett D'Amore 	/*
57088447a05SGarrett D'Amore 	 * AC3: If we select an AC3 format, then we have to allocate
57188447a05SGarrett D'Amore 	 * another engine.  Normally this will be an output only
57288447a05SGarrett D'Amore 	 * engine.  However, for now we aren't supporting AC3
57388447a05SGarrett D'Amore 	 * passthru.
57488447a05SGarrett D'Amore 	 */
57588447a05SGarrett D'Amore 
57688447a05SGarrett D'Amore 	switch (fmt) {
57788447a05SGarrett D'Amore 	case AUDIO_FORMAT_U8:
57888447a05SGarrett D'Amore 	case AUDIO_FORMAT_ULAW:
57988447a05SGarrett D'Amore 	case AUDIO_FORMAT_ALAW:
58088447a05SGarrett D'Amore 	case AUDIO_FORMAT_S8:
58188447a05SGarrett D'Amore 	case AUDIO_FORMAT_S16_LE:
58288447a05SGarrett D'Amore 	case AUDIO_FORMAT_S16_BE:
58388447a05SGarrett D'Amore 	case AUDIO_FORMAT_U16_LE:
58488447a05SGarrett D'Amore 	case AUDIO_FORMAT_U16_BE:
58588447a05SGarrett D'Amore 	case AUDIO_FORMAT_S24_LE:
58688447a05SGarrett D'Amore 	case AUDIO_FORMAT_S24_BE:
58788447a05SGarrett D'Amore 	case AUDIO_FORMAT_S32_LE:
58888447a05SGarrett D'Amore 	case AUDIO_FORMAT_S32_BE:
58988447a05SGarrett D'Amore 	case AUDIO_FORMAT_S24_PACKED:
59088447a05SGarrett D'Amore 		break;
59188447a05SGarrett D'Amore 
59288447a05SGarrett D'Amore 	case AUDIO_FORMAT_AC3:		/* AC3: PASSTHRU */
59388447a05SGarrett D'Amore 	default:
59488447a05SGarrett D'Amore 		return (ENOTSUP);
59588447a05SGarrett D'Amore 	}
59688447a05SGarrett D'Amore 
59788447a05SGarrett D'Amore 
59888447a05SGarrett D'Amore 	/*
59988447a05SGarrett D'Amore 	 * Optimization.  Some personalities send us the same format
60088447a05SGarrett D'Amore 	 * over and over again.  (Sun personality does this
60188447a05SGarrett D'Amore 	 * repeatedly.)  setup_src is potentially expensive, so we
60288447a05SGarrett D'Amore 	 * avoid doing it unless we really need to.
60388447a05SGarrett D'Amore 	 */
6042c30fa45SGarrett D'Amore 	if (fmt != sp->s_user_parms->p_format) {
60588447a05SGarrett D'Amore 		/*
60688447a05SGarrett D'Amore 		 * Note that setting the format doesn't check that the
60788447a05SGarrett D'Amore 		 * audio streams have been paused.  As a result, any
60888447a05SGarrett D'Amore 		 * data still playing or recording will probably get
60988447a05SGarrett D'Amore 		 * misinterpreted.  It would be smart if the client
61088447a05SGarrett D'Amore 		 * application paused/stopped playback before changing
61188447a05SGarrett D'Amore 		 * formats.
61288447a05SGarrett D'Amore 		 */
61388447a05SGarrett D'Amore 		parms.p_format = fmt;
6142c30fa45SGarrett D'Amore 		rv = auimpl_engine_setup(sp, 0, &parms, FORMAT_MSK_FMT);
61588447a05SGarrett D'Amore 	}
61688447a05SGarrett D'Amore 
61788447a05SGarrett D'Amore 	return (rv);
61888447a05SGarrett D'Amore }
61988447a05SGarrett D'Amore 
62088447a05SGarrett D'Amore int
auclnt_get_format(audio_stream_t * sp)62188447a05SGarrett D'Amore auclnt_get_format(audio_stream_t *sp)
62288447a05SGarrett D'Amore {
62388447a05SGarrett D'Amore 	return (sp->s_user_parms->p_format);
62488447a05SGarrett D'Amore }
62588447a05SGarrett D'Amore 
62688447a05SGarrett D'Amore int
auclnt_get_output_format(audio_client_t * c)62788447a05SGarrett D'Amore auclnt_get_output_format(audio_client_t *c)
62888447a05SGarrett D'Amore {
62988447a05SGarrett D'Amore 	return (c->c_ostream.s_user_parms->p_format);
63088447a05SGarrett D'Amore }
63188447a05SGarrett D'Amore 
63288447a05SGarrett D'Amore int
auclnt_get_input_format(audio_client_t * c)63388447a05SGarrett D'Amore auclnt_get_input_format(audio_client_t *c)
63488447a05SGarrett D'Amore {
63588447a05SGarrett D'Amore 	return (c->c_istream.s_user_parms->p_format);
63688447a05SGarrett D'Amore }
63788447a05SGarrett D'Amore 
63888447a05SGarrett D'Amore int
auclnt_set_channels(audio_stream_t * sp,int nchan)63988447a05SGarrett D'Amore auclnt_set_channels(audio_stream_t *sp, int nchan)
64088447a05SGarrett D'Amore {
64188447a05SGarrett D'Amore 	audio_parms_t	parms;
64288447a05SGarrett D'Amore 	int		rv = 0;
64388447a05SGarrett D'Amore 
64488447a05SGarrett D'Amore 	/* Validate setting */
64588447a05SGarrett D'Amore 	if ((nchan > AUDIO_MAX_CHANNELS) || (nchan < 1)) {
64688447a05SGarrett D'Amore 		return (EINVAL);
64788447a05SGarrett D'Amore 	}
64888447a05SGarrett D'Amore 
6492c30fa45SGarrett D'Amore 	if (nchan != sp->s_user_parms->p_nchan) {
65088447a05SGarrett D'Amore 		parms.p_nchan = nchan;
6512c30fa45SGarrett D'Amore 		rv = auimpl_engine_setup(sp, 0, &parms, FORMAT_MSK_CHAN);
65288447a05SGarrett D'Amore 	}
65388447a05SGarrett D'Amore 
65488447a05SGarrett D'Amore 	return (rv);
65588447a05SGarrett D'Amore }
65688447a05SGarrett D'Amore 
65788447a05SGarrett D'Amore int
auclnt_get_channels(audio_stream_t * sp)65888447a05SGarrett D'Amore auclnt_get_channels(audio_stream_t *sp)
65988447a05SGarrett D'Amore {
66088447a05SGarrett D'Amore 	return (sp->s_user_parms->p_nchan);
66188447a05SGarrett D'Amore }
66288447a05SGarrett D'Amore 
66353a539a7SGarrett D'Amore 
66453a539a7SGarrett D'Amore static void
auimpl_set_gain_master(audio_stream_t * sp,uint8_t gain)66588447a05SGarrett D'Amore auimpl_set_gain_master(audio_stream_t *sp, uint8_t gain)
66688447a05SGarrett D'Amore {
66788447a05SGarrett D'Amore 	uint32_t	scaled;
66888447a05SGarrett D'Amore 
66988447a05SGarrett D'Amore 	if (gain > 100) {
67088447a05SGarrett D'Amore 		gain = 0;
67188447a05SGarrett D'Amore 	}
67288447a05SGarrett D'Amore 
67388447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
67488447a05SGarrett D'Amore 	if (sp->s_gain_master == gain) {
67588447a05SGarrett D'Amore 		mutex_exit(&sp->s_lock);
67688447a05SGarrett D'Amore 		return;
67788447a05SGarrett D'Amore 	}
67888447a05SGarrett D'Amore 
67988447a05SGarrett D'Amore 	/*
68088447a05SGarrett D'Amore 	 * calculate the scaled values.  Done now to avoid calculations
68188447a05SGarrett D'Amore 	 * later.
68288447a05SGarrett D'Amore 	 */
68388447a05SGarrett D'Amore 	scaled = (gain * sp->s_gain_pct * AUDIO_DB_SIZE) / (100 * 100);
68488447a05SGarrett D'Amore 
68588447a05SGarrett D'Amore 	sp->s_gain_master = gain;
68688447a05SGarrett D'Amore 	sp->s_gain_scaled = auimpl_db_table[scaled];
68788447a05SGarrett D'Amore 
68888447a05SGarrett D'Amore 	if (!sp->s_muted) {
68988447a05SGarrett D'Amore 		sp->s_gain_eff = sp->s_gain_scaled;
69088447a05SGarrett D'Amore 	}
69188447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
69288447a05SGarrett D'Amore }
69388447a05SGarrett D'Amore 
69453a539a7SGarrett D'Amore int
auimpl_set_pcmvol(void * arg,uint64_t val)69553a539a7SGarrett D'Amore auimpl_set_pcmvol(void *arg, uint64_t val)
69653a539a7SGarrett D'Amore {
69753a539a7SGarrett D'Amore 	audio_dev_t	*d = arg;
69853a539a7SGarrett D'Amore 	list_t		*l = &d->d_clients;
69953a539a7SGarrett D'Amore 	audio_client_t	*c;
70053a539a7SGarrett D'Amore 
70153a539a7SGarrett D'Amore 	if (val > 100) {
70253a539a7SGarrett D'Amore 		return (EINVAL);
70353a539a7SGarrett D'Amore 	}
70453a539a7SGarrett D'Amore 	rw_enter(&auimpl_client_lock, RW_WRITER);
70553a539a7SGarrett D'Amore 	d->d_pcmvol = val & 0xff;
70653a539a7SGarrett D'Amore 	rw_downgrade(&auimpl_client_lock);
70753a539a7SGarrett D'Amore 
70853a539a7SGarrett D'Amore 	for (c = list_head(l); c; c = list_next(l, c)) {
70953a539a7SGarrett D'Amore 		/* don't need to check is_active here, its safe */
71053a539a7SGarrett D'Amore 		auimpl_set_gain_master(&c->c_ostream, (uint8_t)val);
71153a539a7SGarrett D'Amore 	}
71253a539a7SGarrett D'Amore 	rw_exit(&auimpl_client_lock);
71353a539a7SGarrett D'Amore 
71453a539a7SGarrett D'Amore 	return (0);
71553a539a7SGarrett D'Amore }
71653a539a7SGarrett D'Amore 
71753a539a7SGarrett D'Amore int
auimpl_get_pcmvol(void * arg,uint64_t * val)71853a539a7SGarrett D'Amore auimpl_get_pcmvol(void *arg, uint64_t *val)
71953a539a7SGarrett D'Amore {
72053a539a7SGarrett D'Amore 	audio_dev_t	*d = arg;
72153a539a7SGarrett D'Amore 
72253a539a7SGarrett D'Amore 	*val = d->d_pcmvol;
72353a539a7SGarrett D'Amore 	return (0);
72453a539a7SGarrett D'Amore }
72553a539a7SGarrett D'Amore 
72688447a05SGarrett D'Amore void
auclnt_set_gain(audio_stream_t * sp,uint8_t gain)72788447a05SGarrett D'Amore auclnt_set_gain(audio_stream_t *sp, uint8_t gain)
72888447a05SGarrett D'Amore {
72988447a05SGarrett D'Amore 	uint32_t	scaled;
73088447a05SGarrett D'Amore 
73188447a05SGarrett D'Amore 	if (gain > 100) {
73288447a05SGarrett D'Amore 		gain = 0;
73388447a05SGarrett D'Amore 	}
73488447a05SGarrett D'Amore 
73588447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
73688447a05SGarrett D'Amore 
73788447a05SGarrett D'Amore 	/* if no change, don't bother doing updates */
73888447a05SGarrett D'Amore 	if (sp->s_gain_pct == gain) {
73988447a05SGarrett D'Amore 		mutex_exit(&sp->s_lock);
74088447a05SGarrett D'Amore 		return;
74188447a05SGarrett D'Amore 	}
74288447a05SGarrett D'Amore 
74388447a05SGarrett D'Amore 	/*
74488447a05SGarrett D'Amore 	 * calculate the scaled values.  Done now to avoid calculations
74588447a05SGarrett D'Amore 	 * later.
74688447a05SGarrett D'Amore 	 */
74788447a05SGarrett D'Amore 	scaled = (gain * sp->s_gain_master * AUDIO_DB_SIZE) / (100 * 100);
74888447a05SGarrett D'Amore 
74988447a05SGarrett D'Amore 	sp->s_gain_pct = gain;
75088447a05SGarrett D'Amore 	sp->s_gain_scaled = auimpl_db_table[scaled];
75188447a05SGarrett D'Amore 
75288447a05SGarrett D'Amore 	if (!sp->s_muted) {
75388447a05SGarrett D'Amore 		sp->s_gain_eff = sp->s_gain_scaled;
75488447a05SGarrett D'Amore 	}
75588447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
75688447a05SGarrett D'Amore 
757682cb104SGarrett D'Amore 	atomic_inc_uint(&sp->s_client->c_dev->d_serial);
75888447a05SGarrett D'Amore }
75988447a05SGarrett D'Amore 
76088447a05SGarrett D'Amore uint8_t
auclnt_get_gain(audio_stream_t * sp)76188447a05SGarrett D'Amore auclnt_get_gain(audio_stream_t *sp)
76288447a05SGarrett D'Amore {
76388447a05SGarrett D'Amore 	return (sp->s_gain_pct);
76488447a05SGarrett D'Amore }
76588447a05SGarrett D'Amore 
76688447a05SGarrett D'Amore void
auclnt_set_muted(audio_stream_t * sp,boolean_t muted)76788447a05SGarrett D'Amore auclnt_set_muted(audio_stream_t *sp, boolean_t muted)
76888447a05SGarrett D'Amore {
76988447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
77088447a05SGarrett D'Amore 
77188447a05SGarrett D'Amore 	/* if no work change, don't bother doing updates */
77288447a05SGarrett D'Amore 	if (sp->s_muted == muted) {
77388447a05SGarrett D'Amore 		mutex_exit(&sp->s_lock);
77488447a05SGarrett D'Amore 		return;
77588447a05SGarrett D'Amore 	}
77688447a05SGarrett D'Amore 
77788447a05SGarrett D'Amore 	sp->s_muted = muted;
77888447a05SGarrett D'Amore 	if (muted) {
77988447a05SGarrett D'Amore 		sp->s_gain_eff = 0;
78088447a05SGarrett D'Amore 	} else {
78188447a05SGarrett D'Amore 		sp->s_gain_eff = sp->s_gain_scaled;
78288447a05SGarrett D'Amore 	}
78388447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
78488447a05SGarrett D'Amore 
785682cb104SGarrett D'Amore 	atomic_inc_uint(&sp->s_client->c_dev->d_serial);
78688447a05SGarrett D'Amore }
78788447a05SGarrett D'Amore 
78888447a05SGarrett D'Amore boolean_t
auclnt_get_muted(audio_stream_t * sp)78988447a05SGarrett D'Amore auclnt_get_muted(audio_stream_t *sp)
79088447a05SGarrett D'Amore {
79188447a05SGarrett D'Amore 	return (sp->s_muted);
79288447a05SGarrett D'Amore }
79388447a05SGarrett D'Amore 
794622a30bfSGarrett D'Amore boolean_t
auclnt_is_running(audio_stream_t * sp)795622a30bfSGarrett D'Amore auclnt_is_running(audio_stream_t *sp)
796622a30bfSGarrett D'Amore {
797622a30bfSGarrett D'Amore 	return (sp->s_running);
798622a30bfSGarrett D'Amore }
799622a30bfSGarrett D'Amore 
80088447a05SGarrett D'Amore void
auclnt_start(audio_stream_t * sp)80188447a05SGarrett D'Amore auclnt_start(audio_stream_t *sp)
80288447a05SGarrett D'Amore {
80388447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
80488447a05SGarrett D'Amore 	sp->s_running = B_TRUE;
80588447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
80688447a05SGarrett D'Amore }
80788447a05SGarrett D'Amore 
80888447a05SGarrett D'Amore void
auclnt_stop(audio_stream_t * sp)80988447a05SGarrett D'Amore auclnt_stop(audio_stream_t *sp)
81088447a05SGarrett D'Amore {
81188447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
81288447a05SGarrett D'Amore 	/* if running, then stop it */
81388447a05SGarrett D'Amore 	if (sp->s_running) {
81488447a05SGarrett D'Amore 		sp->s_running = B_FALSE;
81588447a05SGarrett D'Amore 		/*
81688447a05SGarrett D'Amore 		 * if we stopped the engine, we might need to wake up
81788447a05SGarrett D'Amore 		 * a thread that is waiting for drain to complete.
81888447a05SGarrett D'Amore 		 */
81988447a05SGarrett D'Amore 		cv_broadcast(&sp->s_cv);
82088447a05SGarrett D'Amore 	}
82188447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
82288447a05SGarrett D'Amore }
82388447a05SGarrett D'Amore 
82488447a05SGarrett D'Amore /*
82588447a05SGarrett D'Amore  * When pausing, no new data will be played after the most recently
82688447a05SGarrett D'Amore  * mixed samples have played.  However, the audio engine will continue
82788447a05SGarrett D'Amore  * to play (possibly just silence).
82888447a05SGarrett D'Amore  *
82988447a05SGarrett D'Amore  * Note that we don't reference count the device, or release/close the
83088447a05SGarrett D'Amore  * engine here.  Once fired up, the engine continues running unil it
83188447a05SGarrett D'Amore  * is closed.
83288447a05SGarrett D'Amore  */
83388447a05SGarrett D'Amore void
auclnt_set_paused(audio_stream_t * sp)83488447a05SGarrett D'Amore auclnt_set_paused(audio_stream_t *sp)
83588447a05SGarrett D'Amore {
83688447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
83788447a05SGarrett D'Amore 	if (sp->s_paused) {
83888447a05SGarrett D'Amore 		mutex_exit(&sp->s_lock);
83988447a05SGarrett D'Amore 		return;
84088447a05SGarrett D'Amore 	}
84188447a05SGarrett D'Amore 	sp->s_paused = B_TRUE;
84288447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
84388447a05SGarrett D'Amore 
84488447a05SGarrett D'Amore 	auclnt_stop(sp);
84588447a05SGarrett D'Amore 
846682cb104SGarrett D'Amore 	atomic_inc_uint(&sp->s_client->c_dev->d_serial);
84788447a05SGarrett D'Amore }
84888447a05SGarrett D'Amore 
84988447a05SGarrett D'Amore void
auclnt_clear_paused(audio_stream_t * sp)85088447a05SGarrett D'Amore auclnt_clear_paused(audio_stream_t *sp)
85188447a05SGarrett D'Amore {
85288447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
85388447a05SGarrett D'Amore 	if (!sp->s_paused) {
85488447a05SGarrett D'Amore 		mutex_exit(&sp->s_lock);
85588447a05SGarrett D'Amore 		return;
85688447a05SGarrett D'Amore 	}
85788447a05SGarrett D'Amore 	sp->s_paused = B_FALSE;
85888447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
85988447a05SGarrett D'Amore }
86088447a05SGarrett D'Amore 
86188447a05SGarrett D'Amore boolean_t
auclnt_is_paused(audio_stream_t * sp)86288447a05SGarrett D'Amore auclnt_is_paused(audio_stream_t *sp)
86388447a05SGarrett D'Amore {
86488447a05SGarrett D'Amore 	return (sp->s_paused);
86588447a05SGarrett D'Amore }
86688447a05SGarrett D'Amore 
86788447a05SGarrett D'Amore void
auclnt_flush(audio_stream_t * sp)86888447a05SGarrett D'Amore auclnt_flush(audio_stream_t *sp)
86988447a05SGarrett D'Amore {
87088447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
87188447a05SGarrett D'Amore 	if (sp == &sp->s_client->c_ostream) {
87288447a05SGarrett D'Amore 		sp->s_tail = sp->s_head;
87388447a05SGarrett D'Amore 		sp->s_tidx = sp->s_hidx;
87488447a05SGarrett D'Amore 	} else {
87588447a05SGarrett D'Amore 		sp->s_head = sp->s_tail;
87688447a05SGarrett D'Amore 		sp->s_hidx = sp->s_tidx;
87788447a05SGarrett D'Amore 	}
87888447a05SGarrett D'Amore 	sp->s_cnv_cnt = 0;
87988447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
88088447a05SGarrett D'Amore }
88188447a05SGarrett D'Amore 
88288447a05SGarrett D'Amore int
auclnt_get_oflag(audio_client_t * c)88388447a05SGarrett D'Amore auclnt_get_oflag(audio_client_t *c)
88488447a05SGarrett D'Amore {
88588447a05SGarrett D'Amore 	return (c->c_omode);
88688447a05SGarrett D'Amore }
88788447a05SGarrett D'Amore 
88888447a05SGarrett D'Amore /*
88988447a05SGarrett D'Amore  * These routines should not be accessed by client "personality"
89088447a05SGarrett D'Amore  * implementations, but are for private framework use only.
89188447a05SGarrett D'Amore  */
89288447a05SGarrett D'Amore 
89388447a05SGarrett D'Amore void
auimpl_client_init(void)89488447a05SGarrett D'Amore auimpl_client_init(void)
89588447a05SGarrett D'Amore {
89688447a05SGarrett D'Amore 	rw_init(&auimpl_client_lock, NULL, RW_DRIVER, NULL);
89788447a05SGarrett D'Amore 	list_create(&auimpl_clients, sizeof (struct audio_client),
89888447a05SGarrett D'Amore 	    offsetof(struct audio_client, c_global_linkage));
89988447a05SGarrett D'Amore }
90088447a05SGarrett D'Amore 
90188447a05SGarrett D'Amore void
auimpl_client_fini(void)90288447a05SGarrett D'Amore auimpl_client_fini(void)
90388447a05SGarrett D'Amore {
90488447a05SGarrett D'Amore 	rw_destroy(&auimpl_client_lock);
90588447a05SGarrett D'Amore 	list_destroy(&auimpl_clients);
90688447a05SGarrett D'Amore }
90788447a05SGarrett D'Amore 
90888447a05SGarrett D'Amore static int
auimpl_stream_init(audio_stream_t * sp,audio_client_t * c)90988447a05SGarrett D'Amore auimpl_stream_init(audio_stream_t *sp, audio_client_t *c)
91088447a05SGarrett D'Amore {
91188447a05SGarrett D'Amore 	mutex_init(&sp->s_lock, NULL, MUTEX_DRIVER, NULL);
91288447a05SGarrett D'Amore 	cv_init(&sp->s_cv, NULL, CV_DRIVER, NULL);
91388447a05SGarrett D'Amore 	sp->s_client = c;
91488447a05SGarrett D'Amore 
91588447a05SGarrett D'Amore 	if (sp == &c->c_ostream) {
91688447a05SGarrett D'Amore 		sp->s_user_parms = &sp->s_cnv_src_parms;
91788447a05SGarrett D'Amore 		sp->s_phys_parms = &sp->s_cnv_dst_parms;
91888447a05SGarrett D'Amore 		sp->s_engcap = ENGINE_OUTPUT_CAP;
91988447a05SGarrett D'Amore 	} else {
92088447a05SGarrett D'Amore 		ASSERT(sp == &c->c_istream);
92188447a05SGarrett D'Amore 		sp->s_user_parms = &sp->s_cnv_dst_parms;
92288447a05SGarrett D'Amore 		sp->s_phys_parms = &sp->s_cnv_src_parms;
92388447a05SGarrett D'Amore 		sp->s_engcap = ENGINE_INPUT_CAP;
92488447a05SGarrett D'Amore 	}
92588447a05SGarrett D'Amore 
92688447a05SGarrett D'Amore 	/* for now initialize conversion parameters */
92788447a05SGarrett D'Amore 	sp->s_src_quality = 3;	/* reasonable compromise for now */
92888447a05SGarrett D'Amore 	sp->s_cnv_dst_nchan = 2;
92988447a05SGarrett D'Amore 	sp->s_cnv_dst_format = AUDIO_FORMAT_S24_NE;
93088447a05SGarrett D'Amore 	sp->s_cnv_dst_rate = 48000;
93188447a05SGarrett D'Amore 	sp->s_cnv_src_nchan = 2;
93288447a05SGarrett D'Amore 	sp->s_cnv_src_format = AUDIO_FORMAT_S24_NE;
93388447a05SGarrett D'Amore 	sp->s_cnv_src_rate = 48000;
93488447a05SGarrett D'Amore 
93588447a05SGarrett D'Amore 	/* set volume/gain all the way up */
93688447a05SGarrett D'Amore 	sp->s_muted = B_FALSE;
93788447a05SGarrett D'Amore 	sp->s_gain_pct = 0;
93888447a05SGarrett D'Amore 	sp->s_gain_scaled = AUDIO_VOL_SCALE;
93988447a05SGarrett D'Amore 	sp->s_gain_eff = AUDIO_VOL_SCALE;
94088447a05SGarrett D'Amore 
94188447a05SGarrett D'Amore 	/*
94288447a05SGarrett D'Amore 	 * We have to start off with a reasonable buffer and
94388447a05SGarrett D'Amore 	 * interrupt configuration.
94488447a05SGarrett D'Amore 	 */
94588447a05SGarrett D'Amore 	sp->s_allocsz = 65536;
94688447a05SGarrett D'Amore 	sp->s_data = ddi_umem_alloc(sp->s_allocsz, DDI_UMEM_NOSLEEP,
94788447a05SGarrett D'Amore 	    &sp->s_cookie);
94888447a05SGarrett D'Amore 	if (sp->s_data == NULL) {
94988447a05SGarrett D'Amore 		sp->s_allocsz = 0;
95088447a05SGarrett D'Amore 		audio_dev_warn(c->c_dev, "ddi_umem_alloc failed");
95188447a05SGarrett D'Amore 		return (ENOMEM);
95288447a05SGarrett D'Amore 	}
95388447a05SGarrett D'Amore 	/* make sure no stale data left in stream */
95488447a05SGarrett D'Amore 	bzero(sp->s_data, sp->s_allocsz);
95588447a05SGarrett D'Amore 
95688447a05SGarrett D'Amore 	/*
95788447a05SGarrett D'Amore 	 * Allocate SRC and data conversion state.
95888447a05SGarrett D'Amore 	 */
95988447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
96088447a05SGarrett D'Amore 	if (auimpl_format_alloc(sp) != 0) {
96188447a05SGarrett D'Amore 		mutex_exit(&sp->s_lock);
96288447a05SGarrett D'Amore 		return (ENOMEM);
96388447a05SGarrett D'Amore 	}
96488447a05SGarrett D'Amore 
96588447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
96688447a05SGarrett D'Amore 
96788447a05SGarrett D'Amore 	return (0);
96888447a05SGarrett D'Amore }
96988447a05SGarrett D'Amore 
97088447a05SGarrett D'Amore 
97188447a05SGarrett D'Amore static void
audio_stream_fini(audio_stream_t * sp)97288447a05SGarrett D'Amore audio_stream_fini(audio_stream_t *sp)
97388447a05SGarrett D'Amore {
97488447a05SGarrett D'Amore 	auimpl_format_free(sp);
97588447a05SGarrett D'Amore 	if (sp->s_cnv_buf0)
97688447a05SGarrett D'Amore 		kmem_free(sp->s_cnv_buf0, sp->s_cnv_max);
97788447a05SGarrett D'Amore 	if (sp->s_cnv_buf1)
97888447a05SGarrett D'Amore 		kmem_free(sp->s_cnv_buf1, sp->s_cnv_max);
97988447a05SGarrett D'Amore 	mutex_destroy(&sp->s_lock);
98088447a05SGarrett D'Amore 	cv_destroy(&sp->s_cv);
98188447a05SGarrett D'Amore 	if (sp->s_data != NULL) {
98288447a05SGarrett D'Amore 		ddi_umem_free(sp->s_cookie);
98388447a05SGarrett D'Amore 		sp->s_data = NULL;
98488447a05SGarrett D'Amore 	}
98588447a05SGarrett D'Amore }
98688447a05SGarrett D'Amore 
98788447a05SGarrett D'Amore int
auclnt_start_drain(audio_client_t * c)98888447a05SGarrett D'Amore auclnt_start_drain(audio_client_t *c)
98988447a05SGarrett D'Amore {
99088447a05SGarrett D'Amore 	audio_stream_t	*sp;
99188447a05SGarrett D'Amore 	int		rv;
99288447a05SGarrett D'Amore 
99388447a05SGarrett D'Amore 	sp = &c->c_ostream;
99488447a05SGarrett D'Amore 
99588447a05SGarrett D'Amore 	/* start an asynchronous drain operation. */
99688447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
99788447a05SGarrett D'Amore 	if (sp->s_paused || !sp->s_running) {
99888447a05SGarrett D'Amore 		rv = EALREADY;
99988447a05SGarrett D'Amore 	} else {
100088447a05SGarrett D'Amore 		sp->s_draining = B_TRUE;
100188447a05SGarrett D'Amore 		rv = 0;
100288447a05SGarrett D'Amore 	}
100388447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
100488447a05SGarrett D'Amore 	return (rv);
100588447a05SGarrett D'Amore }
100688447a05SGarrett D'Amore 
100788447a05SGarrett D'Amore int
auclnt_drain(audio_client_t * c)100888447a05SGarrett D'Amore auclnt_drain(audio_client_t *c)
100988447a05SGarrett D'Amore {
101088447a05SGarrett D'Amore 	audio_stream_t	*sp;
101188447a05SGarrett D'Amore 
101288447a05SGarrett D'Amore 	sp = &c->c_ostream;
101388447a05SGarrett D'Amore 
101488447a05SGarrett D'Amore 	/*
101588447a05SGarrett D'Amore 	 * Note: Drain logic will automatically "stop" the stream when
101688447a05SGarrett D'Amore 	 * the drain threshold has been reached.  So all we have to do
101788447a05SGarrett D'Amore 	 * is wait for the stream to stop.
101888447a05SGarrett D'Amore 	 */
101988447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
102088447a05SGarrett D'Amore 	sp->s_draining = B_TRUE;
102188447a05SGarrett D'Amore 	while (sp->s_draining && sp->s_running && !sp->s_paused) {
102288447a05SGarrett D'Amore 		if (cv_wait_sig(&sp->s_cv, &sp->s_lock) == 0) {
102388447a05SGarrett D'Amore 			mutex_exit(&sp->s_lock);
102488447a05SGarrett D'Amore 			return (EINTR);
102588447a05SGarrett D'Amore 		}
102688447a05SGarrett D'Amore 	}
102788447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
102888447a05SGarrett D'Amore 	return (0);
102988447a05SGarrett D'Amore }
103088447a05SGarrett D'Amore 
103188447a05SGarrett D'Amore audio_client_t *
auimpl_client_create(dev_t dev)103288447a05SGarrett D'Amore auimpl_client_create(dev_t dev)
103388447a05SGarrett D'Amore {
103488447a05SGarrett D'Amore 	audio_client_ops_t	*ops;
103588447a05SGarrett D'Amore 	audio_client_t		*c;
103688447a05SGarrett D'Amore 	audio_client_t		*next;
103788447a05SGarrett D'Amore 	list_t			*list = &auimpl_clients;
103888447a05SGarrett D'Amore 	minor_t			minor;
103988447a05SGarrett D'Amore 	audio_dev_t		*d;
104088447a05SGarrett D'Amore 
104188447a05SGarrett D'Amore 	/* validate minor number */
104288447a05SGarrett D'Amore 	minor = getminor(dev) & AUDIO_MN_TYPE_MASK;
104388447a05SGarrett D'Amore 	if ((ops = audio_client_ops[minor]) == NULL) {
104488447a05SGarrett D'Amore 		return (NULL);
104588447a05SGarrett D'Amore 	}
104688447a05SGarrett D'Amore 
104788447a05SGarrett D'Amore 	/* lookup device instance */
104888447a05SGarrett D'Amore 	if ((d = auimpl_dev_hold_by_devt(dev)) == NULL) {
104988447a05SGarrett D'Amore 		audio_dev_warn(NULL, "no audio_dev for dev_t %d,%d",
105088447a05SGarrett D'Amore 		    getmajor(dev), getminor(dev));
105188447a05SGarrett D'Amore 		return (NULL);
105288447a05SGarrett D'Amore 	}
105388447a05SGarrett D'Amore 
105488447a05SGarrett D'Amore 	if ((c = kmem_zalloc(sizeof (*c), KM_NOSLEEP)) == NULL) {
105588447a05SGarrett D'Amore 		audio_dev_warn(d, "unable to allocate client structure");
105688447a05SGarrett D'Amore 		auimpl_dev_release(d);
105788447a05SGarrett D'Amore 		return (NULL);
105888447a05SGarrett D'Amore 	}
105988447a05SGarrett D'Amore 	c->c_dev = d;
106088447a05SGarrett D'Amore 
106188447a05SGarrett D'Amore 	mutex_init(&c->c_lock, NULL, MUTEX_DRIVER, NULL);
106288447a05SGarrett D'Amore 	cv_init(&c->c_cv, NULL, CV_DRIVER, NULL);
106388447a05SGarrett D'Amore 
106488447a05SGarrett D'Amore 	if ((auimpl_stream_init(&c->c_ostream, c) != 0) ||
106588447a05SGarrett D'Amore 	    (auimpl_stream_init(&c->c_istream, c) != 0)) {
106688447a05SGarrett D'Amore 		goto failed;
106788447a05SGarrett D'Amore 	}
106888447a05SGarrett D'Amore 
106988447a05SGarrett D'Amore 	c->c_major =		getmajor(dev);
107088447a05SGarrett D'Amore 	c->c_origminor =	getminor(dev);
107188447a05SGarrett D'Amore 	c->c_ops =		*ops;
107288447a05SGarrett D'Amore 
107388447a05SGarrett D'Amore 	/*
107488447a05SGarrett D'Amore 	 * We hold the client lock here.
107588447a05SGarrett D'Amore 	 */
107688447a05SGarrett D'Amore 	rw_enter(&auimpl_client_lock, RW_WRITER);
107788447a05SGarrett D'Amore 
107888447a05SGarrett D'Amore 	minor = AUDIO_MN_CLONE_MASK;
107988447a05SGarrett D'Amore 	for (next = list_head(list); next; next = list_next(list, next)) {
108088447a05SGarrett D'Amore 		if (next->c_minor > minor) {
108188447a05SGarrett D'Amore 			break;
108288447a05SGarrett D'Amore 		}
108388447a05SGarrett D'Amore 		minor++;
108488447a05SGarrett D'Amore 	}
108588447a05SGarrett D'Amore 	if (minor >= MAXMIN32) {
108688447a05SGarrett D'Amore 		rw_exit(&auimpl_client_lock);
108788447a05SGarrett D'Amore 		goto failed;
108888447a05SGarrett D'Amore 	}
108988447a05SGarrett D'Amore 	c->c_minor = minor;
109088447a05SGarrett D'Amore 	list_insert_before(list, next, c);
109188447a05SGarrett D'Amore 
109288447a05SGarrett D'Amore 	rw_exit(&auimpl_client_lock);
109388447a05SGarrett D'Amore 
109488447a05SGarrett D'Amore 
109588447a05SGarrett D'Amore 	return (c);
109688447a05SGarrett D'Amore 
109788447a05SGarrett D'Amore failed:
109888447a05SGarrett D'Amore 	auimpl_dev_release(d);
109988447a05SGarrett D'Amore 	audio_stream_fini(&c->c_ostream);
110088447a05SGarrett D'Amore 	audio_stream_fini(&c->c_istream);
110188447a05SGarrett D'Amore 	mutex_destroy(&c->c_lock);
110288447a05SGarrett D'Amore 	cv_destroy(&c->c_cv);
110388447a05SGarrett D'Amore 	kmem_free(c, sizeof (*c));
110488447a05SGarrett D'Amore 	return (NULL);
110588447a05SGarrett D'Amore }
110688447a05SGarrett D'Amore 
110788447a05SGarrett D'Amore void
auimpl_client_destroy(audio_client_t * c)110888447a05SGarrett D'Amore auimpl_client_destroy(audio_client_t *c)
110988447a05SGarrett D'Amore {
111088447a05SGarrett D'Amore 	/* remove us from the global list */
111188447a05SGarrett D'Amore 	rw_enter(&auimpl_client_lock, RW_WRITER);
111288447a05SGarrett D'Amore 	list_remove(&auimpl_clients, c);
111388447a05SGarrett D'Amore 	rw_exit(&auimpl_client_lock);
111488447a05SGarrett D'Amore 
111588447a05SGarrett D'Amore 	ASSERT(!c->c_istream.s_running);
1116*4a2cf368SZhao Edgar Liu - Sun Microsystems 	ASSERT(!c->c_ostream.s_running);
111788447a05SGarrett D'Amore 
111888447a05SGarrett D'Amore 	/* release the device reference count */
111988447a05SGarrett D'Amore 	auimpl_dev_release(c->c_dev);
112088447a05SGarrett D'Amore 	c->c_dev = NULL;
112188447a05SGarrett D'Amore 
112288447a05SGarrett D'Amore 	mutex_destroy(&c->c_lock);
112388447a05SGarrett D'Amore 	cv_destroy(&c->c_cv);
112488447a05SGarrett D'Amore 
112588447a05SGarrett D'Amore 	audio_stream_fini(&c->c_istream);
112688447a05SGarrett D'Amore 	audio_stream_fini(&c->c_ostream);
112788447a05SGarrett D'Amore 	kmem_free(c, sizeof (*c));
112888447a05SGarrett D'Amore }
112988447a05SGarrett D'Amore 
113053a539a7SGarrett D'Amore void
auimpl_client_activate(audio_client_t * c)113153a539a7SGarrett D'Amore auimpl_client_activate(audio_client_t *c)
113253a539a7SGarrett D'Amore {
113353a539a7SGarrett D'Amore 	rw_enter(&auimpl_client_lock, RW_WRITER);
113453a539a7SGarrett D'Amore 	c->c_is_active = B_TRUE;
113553a539a7SGarrett D'Amore 	rw_exit(&auimpl_client_lock);
113653a539a7SGarrett D'Amore }
113753a539a7SGarrett D'Amore 
113853a539a7SGarrett D'Amore void
auimpl_client_deactivate(audio_client_t * c)113953a539a7SGarrett D'Amore auimpl_client_deactivate(audio_client_t *c)
114053a539a7SGarrett D'Amore {
114153a539a7SGarrett D'Amore 	rw_enter(&auimpl_client_lock, RW_WRITER);
114253a539a7SGarrett D'Amore 	c->c_is_active = B_FALSE;
114353a539a7SGarrett D'Amore 	rw_exit(&auimpl_client_lock);
114453a539a7SGarrett D'Amore }
114553a539a7SGarrett D'Amore 
114688447a05SGarrett D'Amore void
auclnt_close(audio_client_t * c)114788447a05SGarrett D'Amore auclnt_close(audio_client_t *c)
114888447a05SGarrett D'Amore {
114988447a05SGarrett D'Amore 	audio_dev_t	*d = c->c_dev;
115088447a05SGarrett D'Amore 
115188447a05SGarrett D'Amore 	/* stop the engines if they are running */
115288447a05SGarrett D'Amore 	auclnt_stop(&c->c_istream);
115388447a05SGarrett D'Amore 	auclnt_stop(&c->c_ostream);
115488447a05SGarrett D'Amore 
115553a539a7SGarrett D'Amore 	rw_enter(&auimpl_client_lock, RW_WRITER);
115688447a05SGarrett D'Amore 	list_remove(&d->d_clients, c);
115753a539a7SGarrett D'Amore 	rw_exit(&auimpl_client_lock);
115888447a05SGarrett D'Amore 
115988447a05SGarrett D'Amore 	mutex_enter(&c->c_lock);
116088447a05SGarrett D'Amore 	/* if in transition need to wait for other thread to release */
116188447a05SGarrett D'Amore 	while (c->c_refcnt) {
116288447a05SGarrett D'Amore 		cv_wait(&c->c_cv, &c->c_lock);
116388447a05SGarrett D'Amore 	}
116488447a05SGarrett D'Amore 	mutex_exit(&c->c_lock);
116588447a05SGarrett D'Amore 
116688447a05SGarrett D'Amore 	/* release any engines that we were holding */
116788447a05SGarrett D'Amore 	auimpl_engine_close(&c->c_ostream);
116888447a05SGarrett D'Amore 	auimpl_engine_close(&c->c_istream);
116988447a05SGarrett D'Amore }
117088447a05SGarrett D'Amore 
117188447a05SGarrett D'Amore audio_dev_t *
auclnt_hold_dev_by_index(int index)117288447a05SGarrett D'Amore auclnt_hold_dev_by_index(int index)
117388447a05SGarrett D'Amore {
117488447a05SGarrett D'Amore 	return (auimpl_dev_hold_by_index(index));
117588447a05SGarrett D'Amore }
117688447a05SGarrett D'Amore 
117788447a05SGarrett D'Amore void
auclnt_release_dev(audio_dev_t * dev)117888447a05SGarrett D'Amore auclnt_release_dev(audio_dev_t *dev)
117988447a05SGarrett D'Amore {
118088447a05SGarrett D'Amore 	auimpl_dev_release(dev);
118188447a05SGarrett D'Amore }
118288447a05SGarrett D'Amore 
118388447a05SGarrett D'Amore audio_client_t *
auclnt_hold_by_devt(dev_t dev)118488447a05SGarrett D'Amore auclnt_hold_by_devt(dev_t dev)
118588447a05SGarrett D'Amore {
118688447a05SGarrett D'Amore 	minor_t	mn = getminor(dev);
118788447a05SGarrett D'Amore 	major_t mj = getmajor(dev);
118888447a05SGarrett D'Amore 	list_t *list;
118988447a05SGarrett D'Amore 	audio_client_t *c;
119088447a05SGarrett D'Amore 
119188447a05SGarrett D'Amore 	list = &auimpl_clients;
119288447a05SGarrett D'Amore 	/* linked list search is kind of inefficient, but it works */
119388447a05SGarrett D'Amore 	rw_enter(&auimpl_client_lock, RW_READER);
119488447a05SGarrett D'Amore 	for (c = list_head(list); c != NULL; c = list_next(list, c)) {
119588447a05SGarrett D'Amore 		if ((c->c_major == mj) && (c->c_minor == mn)) {
119688447a05SGarrett D'Amore 			mutex_enter(&c->c_lock);
119753a539a7SGarrett D'Amore 			if (c->c_is_active) {
119888447a05SGarrett D'Amore 				c->c_refcnt++;
119988447a05SGarrett D'Amore 				mutex_exit(&c->c_lock);
120088447a05SGarrett D'Amore 			} else {
120188447a05SGarrett D'Amore 				mutex_exit(&c->c_lock);
120288447a05SGarrett D'Amore 				c = NULL;
120388447a05SGarrett D'Amore 			}
120488447a05SGarrett D'Amore 			break;
120588447a05SGarrett D'Amore 		}
120688447a05SGarrett D'Amore 	}
120788447a05SGarrett D'Amore 	rw_exit(&auimpl_client_lock);
120888447a05SGarrett D'Amore 	return (c);
120988447a05SGarrett D'Amore }
121088447a05SGarrett D'Amore 
121168c47f65SGarrett D'Amore int
auclnt_serialize(audio_client_t * c)121268c47f65SGarrett D'Amore auclnt_serialize(audio_client_t *c)
121368c47f65SGarrett D'Amore {
121468c47f65SGarrett D'Amore 	mutex_enter(&c->c_lock);
121568c47f65SGarrett D'Amore 	while (c->c_serialize) {
121668c47f65SGarrett D'Amore 		if (cv_wait_sig(&c->c_cv, &c->c_lock) == 0) {
121768c47f65SGarrett D'Amore 			mutex_exit(&c->c_lock);
121868c47f65SGarrett D'Amore 			return (EINTR);
121968c47f65SGarrett D'Amore 		}
122068c47f65SGarrett D'Amore 	}
122168c47f65SGarrett D'Amore 	c->c_serialize = B_TRUE;
122268c47f65SGarrett D'Amore 	mutex_exit(&c->c_lock);
122368c47f65SGarrett D'Amore 	return (0);
122468c47f65SGarrett D'Amore }
122568c47f65SGarrett D'Amore 
122668c47f65SGarrett D'Amore void
auclnt_unserialize(audio_client_t * c)122768c47f65SGarrett D'Amore auclnt_unserialize(audio_client_t *c)
122868c47f65SGarrett D'Amore {
122968c47f65SGarrett D'Amore 	mutex_enter(&c->c_lock);
123068c47f65SGarrett D'Amore 	ASSERT(c->c_serialize);
123168c47f65SGarrett D'Amore 	c->c_serialize = B_FALSE;
123268c47f65SGarrett D'Amore 	cv_broadcast(&c->c_cv);
123368c47f65SGarrett D'Amore 	mutex_exit(&c->c_lock);
123468c47f65SGarrett D'Amore }
123568c47f65SGarrett D'Amore 
123668c47f65SGarrett D'Amore void
auclnt_hold(audio_client_t * c)123768c47f65SGarrett D'Amore auclnt_hold(audio_client_t *c)
123868c47f65SGarrett D'Amore {
123968c47f65SGarrett D'Amore 	mutex_enter(&c->c_lock);
124068c47f65SGarrett D'Amore 	c->c_refcnt++;
124168c47f65SGarrett D'Amore 	mutex_exit(&c->c_lock);
124268c47f65SGarrett D'Amore }
124368c47f65SGarrett D'Amore 
124488447a05SGarrett D'Amore void
auclnt_release(audio_client_t * c)124588447a05SGarrett D'Amore auclnt_release(audio_client_t *c)
124688447a05SGarrett D'Amore {
124788447a05SGarrett D'Amore 	mutex_enter(&c->c_lock);
1248a19bb1faSGarrett D'Amore 	ASSERT(c->c_refcnt > 0);
124988447a05SGarrett D'Amore 	c->c_refcnt--;
125088447a05SGarrett D'Amore 	if (c->c_refcnt == 0)
125188447a05SGarrett D'Amore 		cv_broadcast(&c->c_cv);
125288447a05SGarrett D'Amore 	mutex_exit(&c->c_lock);
125388447a05SGarrett D'Amore }
125488447a05SGarrett D'Amore 
125568c47f65SGarrett D'Amore uint_t
auclnt_dev_get_serial(audio_dev_t * d)1256682cb104SGarrett D'Amore auclnt_dev_get_serial(audio_dev_t *d)
1257682cb104SGarrett D'Amore {
1258682cb104SGarrett D'Amore 	return (d->d_serial);
1259682cb104SGarrett D'Amore }
1260682cb104SGarrett D'Amore 
126188447a05SGarrett D'Amore void
auclnt_dev_walk_clients(audio_dev_t * d,int (* walker)(audio_client_t *,void *),void * arg)126288447a05SGarrett D'Amore auclnt_dev_walk_clients(audio_dev_t *d,
126388447a05SGarrett D'Amore     int (*walker)(audio_client_t *, void *),
126488447a05SGarrett D'Amore     void *arg)
126588447a05SGarrett D'Amore {
126688447a05SGarrett D'Amore 	list_t		*l = &d->d_clients;
126788447a05SGarrett D'Amore 	audio_client_t	*c;
126888447a05SGarrett D'Amore 	int		rv;
126988447a05SGarrett D'Amore 
127053a539a7SGarrett D'Amore 	rw_enter(&auimpl_client_lock, RW_READER);
127188447a05SGarrett D'Amore restart:
127288447a05SGarrett D'Amore 	for (c = list_head(l); c != NULL; c = list_next(l, c)) {
127353a539a7SGarrett D'Amore 		if (!c->c_is_active)
127453a539a7SGarrett D'Amore 			continue;
127588447a05SGarrett D'Amore 		rv = (walker(c, arg));
127688447a05SGarrett D'Amore 		if (rv == AUDIO_WALK_STOP) {
127788447a05SGarrett D'Amore 			break;
127888447a05SGarrett D'Amore 		} else if (rv == AUDIO_WALK_RESTART) {
127988447a05SGarrett D'Amore 			goto restart;
128088447a05SGarrett D'Amore 		}
128188447a05SGarrett D'Amore 	}
128253a539a7SGarrett D'Amore 	rw_exit(&auimpl_client_lock);
128388447a05SGarrett D'Amore }
128488447a05SGarrett D'Amore 
128588447a05SGarrett D'Amore 
128688447a05SGarrett D'Amore int
auclnt_open(audio_client_t * c,int oflag)12872c30fa45SGarrett D'Amore auclnt_open(audio_client_t *c, int oflag)
128888447a05SGarrett D'Amore {
128988447a05SGarrett D'Amore 	audio_stream_t	*sp;
129088447a05SGarrett D'Amore 	audio_dev_t	*d = c->c_dev;
129188447a05SGarrett D'Amore 	int		rv = 0;
129288447a05SGarrett D'Amore 	int		flags;
129388447a05SGarrett D'Amore 
129488447a05SGarrett D'Amore 	flags = 0;
129588447a05SGarrett D'Amore 	if (oflag & FNDELAY)
129688447a05SGarrett D'Amore 		flags |= ENGINE_NDELAY;
129788447a05SGarrett D'Amore 
129888447a05SGarrett D'Amore 	if (oflag & FWRITE) {
129988447a05SGarrett D'Amore 		sp = &c->c_ostream;
13002c30fa45SGarrett D'Amore 		if ((rv = auimpl_engine_open(sp, flags | ENGINE_OUTPUT)) != 0)
130188447a05SGarrett D'Amore 			goto done;
130288447a05SGarrett D'Amore 	}
130388447a05SGarrett D'Amore 
130488447a05SGarrett D'Amore 	if (oflag & FREAD) {
130588447a05SGarrett D'Amore 		sp = &c->c_istream;
13062c30fa45SGarrett D'Amore 		if ((rv = auimpl_engine_open(sp, flags | ENGINE_INPUT)) != 0)
130788447a05SGarrett D'Amore 			goto done;
130888447a05SGarrett D'Amore 	}
130988447a05SGarrett D'Amore 
131088447a05SGarrett D'Amore done:
131188447a05SGarrett D'Amore 	if (rv != 0) {
131288447a05SGarrett D'Amore 		/* close any engines that we opened */
131388447a05SGarrett D'Amore 		auimpl_engine_close(&c->c_ostream);
131488447a05SGarrett D'Amore 		auimpl_engine_close(&c->c_istream);
131588447a05SGarrett D'Amore 	} else {
131653a539a7SGarrett D'Amore 		rw_enter(&auimpl_client_lock, RW_WRITER);
131788447a05SGarrett D'Amore 		list_insert_tail(&d->d_clients, c);
131888447a05SGarrett D'Amore 		c->c_ostream.s_gain_master = d->d_pcmvol;
131988447a05SGarrett D'Amore 		c->c_istream.s_gain_master = 100;
132053a539a7SGarrett D'Amore 		rw_exit(&auimpl_client_lock);
132188447a05SGarrett D'Amore 		auclnt_set_gain(&c->c_ostream, 100);
132288447a05SGarrett D'Amore 		auclnt_set_gain(&c->c_istream, 100);
132388447a05SGarrett D'Amore 	}
132488447a05SGarrett D'Amore 
132588447a05SGarrett D'Amore 	return (rv);
132688447a05SGarrett D'Amore }
132788447a05SGarrett D'Amore 
132888447a05SGarrett D'Amore minor_t
auclnt_get_minor(audio_client_t * c)132988447a05SGarrett D'Amore auclnt_get_minor(audio_client_t *c)
133088447a05SGarrett D'Amore {
133188447a05SGarrett D'Amore 	return (c->c_minor);
133288447a05SGarrett D'Amore }
133388447a05SGarrett D'Amore 
133488447a05SGarrett D'Amore minor_t
auclnt_get_original_minor(audio_client_t * c)133588447a05SGarrett D'Amore auclnt_get_original_minor(audio_client_t *c)
133688447a05SGarrett D'Amore {
133788447a05SGarrett D'Amore 	return (c->c_origminor);
133888447a05SGarrett D'Amore }
133988447a05SGarrett D'Amore 
134088447a05SGarrett D'Amore minor_t
auclnt_get_minor_type(audio_client_t * c)134188447a05SGarrett D'Amore auclnt_get_minor_type(audio_client_t *c)
134288447a05SGarrett D'Amore {
134388447a05SGarrett D'Amore 	return (c->c_origminor & AUDIO_MN_TYPE_MASK);
134488447a05SGarrett D'Amore }
134588447a05SGarrett D'Amore 
1346a19bb1faSGarrett D'Amore queue_t *
auclnt_get_rq(audio_client_t * c)1347a19bb1faSGarrett D'Amore auclnt_get_rq(audio_client_t *c)
1348a19bb1faSGarrett D'Amore {
1349a19bb1faSGarrett D'Amore 	return (c->c_rq);
1350a19bb1faSGarrett D'Amore }
1351a19bb1faSGarrett D'Amore 
1352a19bb1faSGarrett D'Amore queue_t *
auclnt_get_wq(audio_client_t * c)1353a19bb1faSGarrett D'Amore auclnt_get_wq(audio_client_t *c)
1354a19bb1faSGarrett D'Amore {
1355a19bb1faSGarrett D'Amore 	return (c->c_wq);
1356a19bb1faSGarrett D'Amore }
1357a19bb1faSGarrett D'Amore 
135888447a05SGarrett D'Amore pid_t
auclnt_get_pid(audio_client_t * c)135988447a05SGarrett D'Amore auclnt_get_pid(audio_client_t *c)
136088447a05SGarrett D'Amore {
136188447a05SGarrett D'Amore 	return (c->c_pid);
136288447a05SGarrett D'Amore }
136388447a05SGarrett D'Amore 
136488447a05SGarrett D'Amore cred_t *
auclnt_get_cred(audio_client_t * c)136588447a05SGarrett D'Amore auclnt_get_cred(audio_client_t *c)
136688447a05SGarrett D'Amore {
136788447a05SGarrett D'Amore 	return (c->c_cred);
136888447a05SGarrett D'Amore }
136988447a05SGarrett D'Amore 
137088447a05SGarrett D'Amore audio_dev_t *
auclnt_get_dev(audio_client_t * c)137188447a05SGarrett D'Amore auclnt_get_dev(audio_client_t *c)
137288447a05SGarrett D'Amore {
137388447a05SGarrett D'Amore 	return (c->c_dev);
137488447a05SGarrett D'Amore }
137588447a05SGarrett D'Amore 
137688447a05SGarrett D'Amore int
auclnt_get_dev_number(audio_dev_t * dev)137788447a05SGarrett D'Amore auclnt_get_dev_number(audio_dev_t *dev)
137888447a05SGarrett D'Amore {
137988447a05SGarrett D'Amore 	return (dev->d_number);
138088447a05SGarrett D'Amore }
138188447a05SGarrett D'Amore 
138288447a05SGarrett D'Amore int
auclnt_get_dev_index(audio_dev_t * dev)138388447a05SGarrett D'Amore auclnt_get_dev_index(audio_dev_t *dev)
138488447a05SGarrett D'Amore {
138588447a05SGarrett D'Amore 	return (dev->d_index);
138688447a05SGarrett D'Amore }
138788447a05SGarrett D'Amore 
138888447a05SGarrett D'Amore const char *
auclnt_get_dev_name(audio_dev_t * dev)138988447a05SGarrett D'Amore auclnt_get_dev_name(audio_dev_t *dev)
139088447a05SGarrett D'Amore {
139188447a05SGarrett D'Amore 	return (dev->d_name);
139288447a05SGarrett D'Amore }
139388447a05SGarrett D'Amore 
139488447a05SGarrett D'Amore const char *
auclnt_get_dev_driver(audio_dev_t * dev)139588447a05SGarrett D'Amore auclnt_get_dev_driver(audio_dev_t *dev)
139688447a05SGarrett D'Amore {
139788447a05SGarrett D'Amore 	return (ddi_driver_name(dev->d_dip));
139888447a05SGarrett D'Amore }
139988447a05SGarrett D'Amore 
140088447a05SGarrett D'Amore dev_info_t *
auclnt_get_dev_devinfo(audio_dev_t * dev)140188447a05SGarrett D'Amore auclnt_get_dev_devinfo(audio_dev_t *dev)
140288447a05SGarrett D'Amore {
140388447a05SGarrett D'Amore 	return (dev->d_dip);
140488447a05SGarrett D'Amore }
140588447a05SGarrett D'Amore 
140688447a05SGarrett D'Amore const char *
auclnt_get_dev_hw_info(audio_dev_t * dev,void ** iter)140788447a05SGarrett D'Amore auclnt_get_dev_hw_info(audio_dev_t *dev, void **iter)
140888447a05SGarrett D'Amore {
140988447a05SGarrett D'Amore 	struct audio_infostr *isp = *iter;
141088447a05SGarrett D'Amore 	if (isp == NULL) {
141188447a05SGarrett D'Amore 		isp = list_head(&dev->d_hwinfo);
141288447a05SGarrett D'Amore 	} else {
141388447a05SGarrett D'Amore 		isp = list_next(&dev->d_hwinfo, isp);
141488447a05SGarrett D'Amore 	}
141588447a05SGarrett D'Amore 
141688447a05SGarrett D'Amore 	*iter = isp;
141788447a05SGarrett D'Amore 	return (isp ? isp->i_line : NULL);
141888447a05SGarrett D'Amore }
141988447a05SGarrett D'Amore 
142088447a05SGarrett D'Amore int
auclnt_get_dev_instance(audio_dev_t * dev)142188447a05SGarrett D'Amore auclnt_get_dev_instance(audio_dev_t *dev)
142288447a05SGarrett D'Amore {
142388447a05SGarrett D'Amore 	return (dev->d_instance);
142488447a05SGarrett D'Amore }
142588447a05SGarrett D'Amore 
142688447a05SGarrett D'Amore const char *
auclnt_get_dev_description(audio_dev_t * dev)142788447a05SGarrett D'Amore auclnt_get_dev_description(audio_dev_t *dev)
142888447a05SGarrett D'Amore {
142988447a05SGarrett D'Amore 	return (dev->d_desc);
143088447a05SGarrett D'Amore }
143188447a05SGarrett D'Amore 
143288447a05SGarrett D'Amore const char *
auclnt_get_dev_version(audio_dev_t * dev)143388447a05SGarrett D'Amore auclnt_get_dev_version(audio_dev_t *dev)
143488447a05SGarrett D'Amore {
143588447a05SGarrett D'Amore 	return (dev->d_vers);
143688447a05SGarrett D'Amore }
143788447a05SGarrett D'Amore 
143868c47f65SGarrett D'Amore uint_t
auclnt_get_dev_capab(audio_dev_t * dev)143988447a05SGarrett D'Amore auclnt_get_dev_capab(audio_dev_t *dev)
144088447a05SGarrett D'Amore {
144188447a05SGarrett D'Amore 	uint32_t	flags;
144268c47f65SGarrett D'Amore 	uint_t		caps = 0;
144388447a05SGarrett D'Amore 
144488447a05SGarrett D'Amore 	flags = dev->d_flags;
144588447a05SGarrett D'Amore 
144688447a05SGarrett D'Amore 	if (flags & DEV_OUTPUT_CAP)
144788447a05SGarrett D'Amore 		caps |= AUDIO_CLIENT_CAP_PLAY;
144888447a05SGarrett D'Amore 	if (flags & DEV_INPUT_CAP)
144988447a05SGarrett D'Amore 		caps |= AUDIO_CLIENT_CAP_RECORD;
145088447a05SGarrett D'Amore 	if (flags & DEV_DUPLEX_CAP)
145188447a05SGarrett D'Amore 		caps |= AUDIO_CLIENT_CAP_DUPLEX;
145288447a05SGarrett D'Amore 
145388447a05SGarrett D'Amore 	/* AC3: deal with formats that don't support mixing */
145488447a05SGarrett D'Amore 
145588447a05SGarrett D'Amore 	return (caps);
145688447a05SGarrett D'Amore }
145788447a05SGarrett D'Amore 
145888447a05SGarrett D'Amore uint64_t
auclnt_get_samples(audio_stream_t * sp)145988447a05SGarrett D'Amore auclnt_get_samples(audio_stream_t *sp)
146088447a05SGarrett D'Amore {
146188447a05SGarrett D'Amore 	uint64_t	n;
146288447a05SGarrett D'Amore 
146388447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
146488447a05SGarrett D'Amore 	n = sp->s_samples;
146588447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
146688447a05SGarrett D'Amore 	return (n);
146788447a05SGarrett D'Amore }
146888447a05SGarrett D'Amore 
146988447a05SGarrett D'Amore void
auclnt_set_samples(audio_stream_t * sp,uint64_t n)147088447a05SGarrett D'Amore auclnt_set_samples(audio_stream_t *sp, uint64_t n)
147188447a05SGarrett D'Amore {
147288447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
147388447a05SGarrett D'Amore 	sp->s_samples = n;
147488447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
147588447a05SGarrett D'Amore }
147688447a05SGarrett D'Amore 
147788447a05SGarrett D'Amore uint64_t
auclnt_get_errors(audio_stream_t * sp)147888447a05SGarrett D'Amore auclnt_get_errors(audio_stream_t *sp)
147988447a05SGarrett D'Amore {
148088447a05SGarrett D'Amore 	uint64_t	n;
148188447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
148288447a05SGarrett D'Amore 	n = sp->s_errors;
148388447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
148488447a05SGarrett D'Amore 	return (n);
148588447a05SGarrett D'Amore }
148688447a05SGarrett D'Amore 
148788447a05SGarrett D'Amore void
auclnt_set_errors(audio_stream_t * sp,uint64_t n)148888447a05SGarrett D'Amore auclnt_set_errors(audio_stream_t *sp, uint64_t n)
148988447a05SGarrett D'Amore {
149088447a05SGarrett D'Amore 	mutex_enter(&sp->s_lock);
149188447a05SGarrett D'Amore 	sp->s_errors = n;
149288447a05SGarrett D'Amore 	mutex_exit(&sp->s_lock);
149388447a05SGarrett D'Amore }
149488447a05SGarrett D'Amore 
149588447a05SGarrett D'Amore void
auclnt_register_ops(minor_t minor,audio_client_ops_t * ops)149688447a05SGarrett D'Amore auclnt_register_ops(minor_t minor, audio_client_ops_t *ops)
149788447a05SGarrett D'Amore {
149888447a05SGarrett D'Amore 	/* we control minor number allocations, no need for runtime checks */
149988447a05SGarrett D'Amore 	ASSERT(minor <= AUDIO_MN_TYPE_MASK);
150088447a05SGarrett D'Amore 
150188447a05SGarrett D'Amore 	audio_client_ops[minor] = ops;
150288447a05SGarrett D'Amore }
150388447a05SGarrett D'Amore 
150488447a05SGarrett D'Amore int
auimpl_create_minors(audio_dev_t * d)150588447a05SGarrett D'Amore auimpl_create_minors(audio_dev_t *d)
150688447a05SGarrett D'Amore {
150788447a05SGarrett D'Amore 	char			path[MAXPATHLEN];
150888447a05SGarrett D'Amore 	int			rv = 0;
150988447a05SGarrett D'Amore 	minor_t			minor;
151088447a05SGarrett D'Amore 	audio_client_ops_t	*ops;
151188447a05SGarrett D'Amore 	char			*nt;
151288447a05SGarrett D'Amore 
151388447a05SGarrett D'Amore 	for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) {
151488447a05SGarrett D'Amore 
151588447a05SGarrett D'Amore 		if ((ops = audio_client_ops[i]) == NULL)
151688447a05SGarrett D'Amore 			continue;
151788447a05SGarrett D'Amore 
151888447a05SGarrett D'Amore 		if (ops->aco_dev_init != NULL)
151988447a05SGarrett D'Amore 			d->d_minor_data[i] = ops->aco_dev_init(d);
152088447a05SGarrett D'Amore 
152188447a05SGarrett D'Amore 		switch (i) {
152288447a05SGarrett D'Amore 		case AUDIO_MINOR_SNDSTAT:
152388447a05SGarrett D'Amore 			if (!(d->d_flags & DEV_SNDSTAT_CAP)) {
152488447a05SGarrett D'Amore 				continue;
152588447a05SGarrett D'Amore 			}
152688447a05SGarrett D'Amore 			nt = DDI_PSEUDO;
152788447a05SGarrett D'Amore 			break;
152888447a05SGarrett D'Amore 
152988447a05SGarrett D'Amore 		default:
153088447a05SGarrett D'Amore 			if (!(d->d_flags & (DEV_INPUT_CAP| DEV_OUTPUT_CAP))) {
153188447a05SGarrett D'Amore 				continue;
153288447a05SGarrett D'Amore 			}
153388447a05SGarrett D'Amore 			nt = DDI_NT_AUDIO;
153488447a05SGarrett D'Amore 			break;
153588447a05SGarrett D'Amore 		}
153688447a05SGarrett D'Amore 
153788447a05SGarrett D'Amore 		if (ops->aco_minor_prefix != NULL) {
153888447a05SGarrett D'Amore 
153988447a05SGarrett D'Amore 			minor = AUDIO_MKMN(d->d_instance, i);
154088447a05SGarrett D'Amore 			(void) snprintf(path, sizeof (path),
154188447a05SGarrett D'Amore 			    "%s%d", ops->aco_minor_prefix, d->d_instance);
154288447a05SGarrett D'Amore 
154388447a05SGarrett D'Amore 			rv = ddi_create_minor_node(d->d_dip, path, S_IFCHR,
154488447a05SGarrett D'Amore 			    minor, nt, 0);
154588447a05SGarrett D'Amore 
154688447a05SGarrett D'Amore 			if (rv != 0)
154788447a05SGarrett D'Amore 				break;
154888447a05SGarrett D'Amore 		}
154988447a05SGarrett D'Amore 	}
155088447a05SGarrett D'Amore 	return (rv);
155188447a05SGarrett D'Amore }
155288447a05SGarrett D'Amore 
155388447a05SGarrett D'Amore void
auimpl_remove_minors(audio_dev_t * d)155488447a05SGarrett D'Amore auimpl_remove_minors(audio_dev_t *d)
155588447a05SGarrett D'Amore {
155688447a05SGarrett D'Amore 	char			path[MAXPATHLEN];
155788447a05SGarrett D'Amore 	audio_client_ops_t	*ops;
155888447a05SGarrett D'Amore 
155988447a05SGarrett D'Amore 	for (int i = 0; i <= AUDIO_MN_TYPE_MASK; i++) {
156088447a05SGarrett D'Amore 		if ((ops = audio_client_ops[i]) == NULL)
156188447a05SGarrett D'Amore 			continue;
156288447a05SGarrett D'Amore 		if (ops->aco_minor_prefix != NULL) {
156388447a05SGarrett D'Amore 			(void) snprintf(path, sizeof (path), "%s%d",
156488447a05SGarrett D'Amore 			    ops->aco_minor_prefix, d->d_instance);
156588447a05SGarrett D'Amore 			(void) ddi_remove_minor_node(d->d_dip, path);
156688447a05SGarrett D'Amore 		}
156788447a05SGarrett D'Amore 
156888447a05SGarrett D'Amore 		if (ops->aco_dev_fini != NULL)
156988447a05SGarrett D'Amore 			ops->aco_dev_fini(d->d_minor_data[i]);
157088447a05SGarrett D'Amore 	}
157188447a05SGarrett D'Amore }
157288447a05SGarrett D'Amore 
157388447a05SGarrett D'Amore void *
auclnt_get_dev_minor_data(audio_dev_t * d,minor_t mn)157488447a05SGarrett D'Amore auclnt_get_dev_minor_data(audio_dev_t *d, minor_t mn)
157588447a05SGarrett D'Amore {
157688447a05SGarrett D'Amore 	ASSERT(mn < (1U << AUDIO_MN_TYPE_NBITS));
157788447a05SGarrett D'Amore 	return (d->d_minor_data[mn]);
157888447a05SGarrett D'Amore }
157988447a05SGarrett D'Amore 
158088447a05SGarrett D'Amore void *
auclnt_get_minor_data(audio_client_t * c,minor_t mn)158188447a05SGarrett D'Amore auclnt_get_minor_data(audio_client_t *c, minor_t mn)
158288447a05SGarrett D'Amore {
158388447a05SGarrett D'Amore 	ASSERT(mn < (1U << AUDIO_MN_TYPE_NBITS));
158488447a05SGarrett D'Amore 	return (c->c_dev->d_minor_data[mn]);
158588447a05SGarrett D'Amore }
158688447a05SGarrett D'Amore 
158788447a05SGarrett D'Amore /*
158888447a05SGarrett D'Amore  * This will walk all controls registered to a clients device and callback
158988447a05SGarrett D'Amore  * to walker for each one with its audio_ctrl. Note this data
159088447a05SGarrett D'Amore  * must be considered read only by walker.
159188447a05SGarrett D'Amore  *
159288447a05SGarrett D'Amore  * Note that walk_func may return values to continue (AUDIO_WALK_CONTINUE)
159388447a05SGarrett D'Amore  * or stop walk (AUDIO_WALK_STOP).
159488447a05SGarrett D'Amore  *
159588447a05SGarrett D'Amore  */
159688447a05SGarrett D'Amore void
auclnt_walk_controls(audio_dev_t * d,int (* walker)(audio_ctrl_t *,void *),void * arg)159788447a05SGarrett D'Amore auclnt_walk_controls(audio_dev_t *d,
159888447a05SGarrett D'Amore     int (*walker)(audio_ctrl_t *, void *),
159988447a05SGarrett D'Amore     void *arg)
160088447a05SGarrett D'Amore {
160188447a05SGarrett D'Amore 	audio_ctrl_t *ctrl;
160288447a05SGarrett D'Amore 
160368c47f65SGarrett D'Amore 	mutex_enter(&d->d_ctrl_lock);
160488447a05SGarrett D'Amore 	for (ctrl = list_head(&d->d_controls); ctrl;
160588447a05SGarrett D'Amore 	    ctrl = list_next(&d->d_controls, ctrl)) {
160688447a05SGarrett D'Amore 		if (walker(ctrl, arg) == AUDIO_WALK_STOP)
160788447a05SGarrett D'Amore 			break;
160888447a05SGarrett D'Amore 	}
160968c47f65SGarrett D'Amore 	mutex_exit(&d->d_ctrl_lock);
161088447a05SGarrett D'Amore }
161188447a05SGarrett D'Amore 
161288447a05SGarrett D'Amore /*
161388447a05SGarrett D'Amore  * This will search all controls attached to an
161488447a05SGarrett D'Amore  * audio device for a control with the desired name.
161588447a05SGarrett D'Amore  *
161688447a05SGarrett D'Amore  * d    - the audio device to look on
161788447a05SGarrett D'Amore  * name - name of the control being looked for.
161888447a05SGarrett D'Amore  *
161988447a05SGarrett D'Amore  * On successful return a ctrl handle will be returned. On
162088447a05SGarrett D'Amore  * failure NULL is returned.
162188447a05SGarrett D'Amore  */
162288447a05SGarrett D'Amore audio_ctrl_t *
auclnt_find_control(audio_dev_t * d,const char * name)162388447a05SGarrett D'Amore auclnt_find_control(audio_dev_t *d, const char *name)
162488447a05SGarrett D'Amore {
162588447a05SGarrett D'Amore 	audio_ctrl_t *ctrl;
162688447a05SGarrett D'Amore 
162788447a05SGarrett D'Amore 	/* Verify argument */
162888447a05SGarrett D'Amore 	ASSERT(d);
162988447a05SGarrett D'Amore 
163068c47f65SGarrett D'Amore 	mutex_enter(&d->d_ctrl_lock);
163188447a05SGarrett D'Amore 	for (ctrl = list_head(&d->d_controls); ctrl;
163288447a05SGarrett D'Amore 	    ctrl = list_next(&d->d_controls, ctrl)) {
163388447a05SGarrett D'Amore 		if (strcmp(ctrl->ctrl_name, name) == 0) {
163468c47f65SGarrett D'Amore 			mutex_exit(&d->d_ctrl_lock);
163588447a05SGarrett D'Amore 			return (ctrl);
163688447a05SGarrett D'Amore 		}
163788447a05SGarrett D'Amore 	}
163868c47f65SGarrett D'Amore 	mutex_exit(&d->d_ctrl_lock);
163988447a05SGarrett D'Amore 	return (NULL);
164088447a05SGarrett D'Amore }
164188447a05SGarrett D'Amore 
164288447a05SGarrett D'Amore /*
164388447a05SGarrett D'Amore  * Given a known control, get its attributes.
164488447a05SGarrett D'Amore  *
164588447a05SGarrett D'Amore  * The caller must supply a audio_ctrl_desc_t structure.  Also the
164688447a05SGarrett D'Amore  * values in the structure are ignored when making the call and filled
164788447a05SGarrett D'Amore  * in by this function. All data pointed to by elements of desc should
164888447a05SGarrett D'Amore  * be assumed read only.
164988447a05SGarrett D'Amore  *
165088447a05SGarrett D'Amore  * If an error occurs then a non-zero is returned.
165188447a05SGarrett D'Amore  *
165288447a05SGarrett D'Amore  */
165388447a05SGarrett D'Amore int
auclnt_control_describe(audio_ctrl_t * ctrl,audio_ctrl_desc_t * desc)165488447a05SGarrett D'Amore auclnt_control_describe(audio_ctrl_t *ctrl, audio_ctrl_desc_t *desc)
165588447a05SGarrett D'Amore {
165688447a05SGarrett D'Amore 	ASSERT(ctrl);
165788447a05SGarrett D'Amore 	ASSERT(desc);
165888447a05SGarrett D'Amore 
165988447a05SGarrett D'Amore 	bcopy(&ctrl->ctrl_des, desc, sizeof (*desc));
166088447a05SGarrett D'Amore 	return (0);
166188447a05SGarrett D'Amore }
166288447a05SGarrett D'Amore 
166388447a05SGarrett D'Amore int
auclnt_control_read(audio_ctrl_t * ctrl,uint64_t * value)166488447a05SGarrett D'Amore auclnt_control_read(audio_ctrl_t *ctrl, uint64_t *value)
166588447a05SGarrett D'Amore {
166688447a05SGarrett D'Amore 	return (audio_control_read(ctrl, value));
166788447a05SGarrett D'Amore }
166888447a05SGarrett D'Amore 
166988447a05SGarrett D'Amore int
auclnt_control_write(audio_ctrl_t * ctrl,uint64_t value)167088447a05SGarrett D'Amore auclnt_control_write(audio_ctrl_t *ctrl, uint64_t value)
167188447a05SGarrett D'Amore {
167288447a05SGarrett D'Amore 	return (audio_control_write(ctrl, value));
167388447a05SGarrett D'Amore }
167488447a05SGarrett D'Amore 
167588447a05SGarrett D'Amore void
auclnt_warn(audio_client_t * c,const char * fmt,...)167688447a05SGarrett D'Amore auclnt_warn(audio_client_t *c, const char *fmt, ...)
167788447a05SGarrett D'Amore {
167888447a05SGarrett D'Amore 	va_list va;
167988447a05SGarrett D'Amore 
168088447a05SGarrett D'Amore 	va_start(va, fmt);
168188447a05SGarrett D'Amore 	auimpl_dev_vwarn(c ? c->c_dev : NULL, fmt, va);
168288447a05SGarrett D'Amore 	va_end(va);
168388447a05SGarrett D'Amore }
1684