xref: /onnv-gate/usr/src/uts/common/io/audio/impl/audio_engine.c (revision 12165:e481916a5729)
19484Sgarrett.damore@Sun.COM /*
29484Sgarrett.damore@Sun.COM  * CDDL HEADER START
39484Sgarrett.damore@Sun.COM  *
49484Sgarrett.damore@Sun.COM  * The contents of this file are subject to the terms of the
59484Sgarrett.damore@Sun.COM  * Common Development and Distribution License (the "License").
69484Sgarrett.damore@Sun.COM  * You may not use this file except in compliance with the License.
79484Sgarrett.damore@Sun.COM  *
89484Sgarrett.damore@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99484Sgarrett.damore@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109484Sgarrett.damore@Sun.COM  * See the License for the specific language governing permissions
119484Sgarrett.damore@Sun.COM  * and limitations under the License.
129484Sgarrett.damore@Sun.COM  *
139484Sgarrett.damore@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149484Sgarrett.damore@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159484Sgarrett.damore@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169484Sgarrett.damore@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179484Sgarrett.damore@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189484Sgarrett.damore@Sun.COM  *
199484Sgarrett.damore@Sun.COM  * CDDL HEADER END
209484Sgarrett.damore@Sun.COM  */
219484Sgarrett.damore@Sun.COM /*
229484Sgarrett.damore@Sun.COM  * Copyright (C) 4Front Technologies 1996-2008.
239484Sgarrett.damore@Sun.COM  *
24*12165Sgdamore@opensolaris.org  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
259484Sgarrett.damore@Sun.COM  */
269484Sgarrett.damore@Sun.COM 
279484Sgarrett.damore@Sun.COM #include <sys/types.h>
289484Sgarrett.damore@Sun.COM #include <sys/list.h>
299484Sgarrett.damore@Sun.COM #include <sys/sysmacros.h>
309484Sgarrett.damore@Sun.COM #include <sys/ddi.h>
319484Sgarrett.damore@Sun.COM #include <sys/sunddi.h>
329484Sgarrett.damore@Sun.COM #include <sys/callb.h>
339484Sgarrett.damore@Sun.COM #include <sys/kstat.h>
349484Sgarrett.damore@Sun.COM #include <sys/note.h>
359484Sgarrett.damore@Sun.COM 
369484Sgarrett.damore@Sun.COM #include "audio_impl.h"
379484Sgarrett.damore@Sun.COM 
389484Sgarrett.damore@Sun.COM /*
399484Sgarrett.damore@Sun.COM  * Audio Engine functions.
409484Sgarrett.damore@Sun.COM  */
419484Sgarrett.damore@Sun.COM 
4211936Sgdamore@opensolaris.org /*
4311936Sgdamore@opensolaris.org  * Globals
4411936Sgdamore@opensolaris.org  */
4511936Sgdamore@opensolaris.org uint_t		audio_intrhz = AUDIO_INTRHZ;
4611936Sgdamore@opensolaris.org /*
4711936Sgdamore@opensolaris.org  * We need to operate at fairly high interrupt priority to avoid
4811936Sgdamore@opensolaris.org  * underruns due to other less time sensitive processing.
4911936Sgdamore@opensolaris.org  */
5011936Sgdamore@opensolaris.org int		audio_priority = DDI_IPL_8;
5111936Sgdamore@opensolaris.org 
529484Sgarrett.damore@Sun.COM audio_dev_t *
audio_dev_alloc(dev_info_t * dip,int instance)539484Sgarrett.damore@Sun.COM audio_dev_alloc(dev_info_t *dip, int instance)
549484Sgarrett.damore@Sun.COM {
559484Sgarrett.damore@Sun.COM 	audio_dev_t *d;
569484Sgarrett.damore@Sun.COM 
579484Sgarrett.damore@Sun.COM 	/*
589484Sgarrett.damore@Sun.COM 	 * For a card with multiple independent audio ports on it, we
599484Sgarrett.damore@Sun.COM 	 * allow the driver to provide a different instance numbering
609484Sgarrett.damore@Sun.COM 	 * scheme than the standard DDI instance number.  (This is
619484Sgarrett.damore@Sun.COM 	 * sort of like the PPA numbering scheme used by NIC drivers
629484Sgarrett.damore@Sun.COM 	 * -- by default PPA == instance, but sometimes we need more
639484Sgarrett.damore@Sun.COM 	 * flexibility.)
649484Sgarrett.damore@Sun.COM 	 */
659484Sgarrett.damore@Sun.COM 	if (instance == 0) {
669484Sgarrett.damore@Sun.COM 		instance = ddi_get_instance(dip);
679484Sgarrett.damore@Sun.COM 	}
689484Sgarrett.damore@Sun.COM 	/* generally this shouldn't occur */
699484Sgarrett.damore@Sun.COM 	if (instance > AUDIO_MN_INST_MASK) {
709484Sgarrett.damore@Sun.COM 		audio_dev_warn(NULL, "bad instance number for %s (%d)",
719484Sgarrett.damore@Sun.COM 		    ddi_driver_name(dip), instance);
729484Sgarrett.damore@Sun.COM 		return (NULL);
739484Sgarrett.damore@Sun.COM 	}
749484Sgarrett.damore@Sun.COM 
759484Sgarrett.damore@Sun.COM 	if ((d = kmem_zalloc(sizeof (*d), KM_NOSLEEP)) == NULL) {
769484Sgarrett.damore@Sun.COM 		audio_dev_warn(NULL, "unable to allocate audio device struct");
779484Sgarrett.damore@Sun.COM 		return (NULL);
789484Sgarrett.damore@Sun.COM 	}
799484Sgarrett.damore@Sun.COM 	d->d_dip = dip;
809484Sgarrett.damore@Sun.COM 	d->d_number = -1;
819484Sgarrett.damore@Sun.COM 	d->d_major = ddi_driver_major(dip);
829484Sgarrett.damore@Sun.COM 	d->d_instance = instance;
839484Sgarrett.damore@Sun.COM 	d->d_pcmvol = 100;
849484Sgarrett.damore@Sun.COM 	mutex_init(&d->d_lock, NULL, MUTEX_DRIVER, NULL);
859484Sgarrett.damore@Sun.COM 	cv_init(&d->d_cv, NULL, CV_DRIVER, NULL);
8611936Sgdamore@opensolaris.org 	mutex_init(&d->d_ctrl_lock, NULL, MUTEX_DRIVER, NULL);
8711936Sgdamore@opensolaris.org 	cv_init(&d->d_ctrl_cv, NULL, CV_DRIVER, NULL);
889484Sgarrett.damore@Sun.COM 	list_create(&d->d_clients, sizeof (struct audio_client),
899484Sgarrett.damore@Sun.COM 	    offsetof(struct audio_client, c_dev_linkage));
909484Sgarrett.damore@Sun.COM 	list_create(&d->d_engines, sizeof (struct audio_engine),
919484Sgarrett.damore@Sun.COM 	    offsetof(struct audio_engine, e_dev_linkage));
929484Sgarrett.damore@Sun.COM 	list_create(&d->d_controls, sizeof (struct audio_ctrl),
939484Sgarrett.damore@Sun.COM 	    offsetof(struct audio_ctrl, ctrl_linkage));
949484Sgarrett.damore@Sun.COM 	list_create(&d->d_hwinfo, sizeof (struct audio_infostr),
959484Sgarrett.damore@Sun.COM 	    offsetof(struct audio_infostr, i_linkage));
969484Sgarrett.damore@Sun.COM 	(void) snprintf(d->d_name, sizeof (d->d_name), "%s#%d",
979484Sgarrett.damore@Sun.COM 	    ddi_driver_name(dip), instance);
989484Sgarrett.damore@Sun.COM 
999484Sgarrett.damore@Sun.COM 	return (d);
1009484Sgarrett.damore@Sun.COM }
1019484Sgarrett.damore@Sun.COM 
1029484Sgarrett.damore@Sun.COM void
audio_dev_free(audio_dev_t * d)1039484Sgarrett.damore@Sun.COM audio_dev_free(audio_dev_t *d)
1049484Sgarrett.damore@Sun.COM {
1059484Sgarrett.damore@Sun.COM 	struct audio_infostr *isp;
10611936Sgdamore@opensolaris.org 
1079484Sgarrett.damore@Sun.COM 	while ((isp = list_remove_head(&d->d_hwinfo)) != NULL) {
1089484Sgarrett.damore@Sun.COM 		kmem_free(isp, sizeof (*isp));
1099484Sgarrett.damore@Sun.COM 	}
1109484Sgarrett.damore@Sun.COM 	if (d->d_pcmvol_ctrl != NULL) {
1119484Sgarrett.damore@Sun.COM 		audio_dev_del_control(d->d_pcmvol_ctrl);
1129484Sgarrett.damore@Sun.COM 	}
1139484Sgarrett.damore@Sun.COM 	list_destroy(&d->d_hwinfo);
1149484Sgarrett.damore@Sun.COM 	list_destroy(&d->d_engines);
1159484Sgarrett.damore@Sun.COM 	list_destroy(&d->d_controls);
1169484Sgarrett.damore@Sun.COM 	list_destroy(&d->d_clients);
11711936Sgdamore@opensolaris.org 	mutex_destroy(&d->d_ctrl_lock);
1189484Sgarrett.damore@Sun.COM 	mutex_destroy(&d->d_lock);
1199484Sgarrett.damore@Sun.COM 	cv_destroy(&d->d_cv);
12011936Sgdamore@opensolaris.org 	cv_destroy(&d->d_ctrl_cv);
1219484Sgarrett.damore@Sun.COM 	kmem_free(d, sizeof (*d));
1229484Sgarrett.damore@Sun.COM }
1239484Sgarrett.damore@Sun.COM 
1249484Sgarrett.damore@Sun.COM void
audio_dev_set_description(audio_dev_t * d,const char * desc)1259484Sgarrett.damore@Sun.COM audio_dev_set_description(audio_dev_t *d, const char *desc)
1269484Sgarrett.damore@Sun.COM {
1279484Sgarrett.damore@Sun.COM 	(void) strlcpy(d->d_desc, desc, sizeof (d->d_desc));
1289484Sgarrett.damore@Sun.COM }
1299484Sgarrett.damore@Sun.COM 
1309484Sgarrett.damore@Sun.COM void
audio_dev_set_version(audio_dev_t * d,const char * vers)1319484Sgarrett.damore@Sun.COM audio_dev_set_version(audio_dev_t *d, const char *vers)
1329484Sgarrett.damore@Sun.COM {
1339484Sgarrett.damore@Sun.COM 	(void) strlcpy(d->d_vers, vers, sizeof (d->d_vers));
1349484Sgarrett.damore@Sun.COM }
1359484Sgarrett.damore@Sun.COM 
1369484Sgarrett.damore@Sun.COM void
audio_dev_add_info(audio_dev_t * d,const char * info)1379484Sgarrett.damore@Sun.COM audio_dev_add_info(audio_dev_t *d, const char *info)
1389484Sgarrett.damore@Sun.COM {
1399484Sgarrett.damore@Sun.COM 	struct audio_infostr *isp;
1409484Sgarrett.damore@Sun.COM 
1419484Sgarrett.damore@Sun.COM 	/* failure to add information structure is not critical */
1429484Sgarrett.damore@Sun.COM 	isp = kmem_zalloc(sizeof (*isp), KM_NOSLEEP);
1439484Sgarrett.damore@Sun.COM 	if (isp == NULL) {
1449484Sgarrett.damore@Sun.COM 		audio_dev_warn(d, "unable to allocate information structure");
1459484Sgarrett.damore@Sun.COM 	} else {
1469484Sgarrett.damore@Sun.COM 		(void) snprintf(isp->i_line, sizeof (isp->i_line), info);
1479484Sgarrett.damore@Sun.COM 		list_insert_tail(&d->d_hwinfo, isp);
1489484Sgarrett.damore@Sun.COM 	}
1499484Sgarrett.damore@Sun.COM }
1509484Sgarrett.damore@Sun.COM 
15111936Sgdamore@opensolaris.org static void
auimpl_engine_reset(audio_engine_t * e)15211936Sgdamore@opensolaris.org auimpl_engine_reset(audio_engine_t *e)
1539484Sgarrett.damore@Sun.COM {
1549484Sgarrett.damore@Sun.COM 	char	*buf;
1559484Sgarrett.damore@Sun.COM 	char	*ptr;
15611936Sgdamore@opensolaris.org 	int	nfr, resid, cnt;
15711936Sgdamore@opensolaris.org 	int	tidx;
1589484Sgarrett.damore@Sun.COM 
15911936Sgdamore@opensolaris.org 	tidx = e->e_tidx;
16011936Sgdamore@opensolaris.org 	nfr = min(e->e_head - e->e_tail, e->e_nframes);
16111936Sgdamore@opensolaris.org 	buf = kmem_alloc(nfr * e->e_framesz, KM_SLEEP);
16211936Sgdamore@opensolaris.org 	ptr = buf;
16311936Sgdamore@opensolaris.org 	cnt = 0;
16411936Sgdamore@opensolaris.org 
16511936Sgdamore@opensolaris.org 	ASSERT(e->e_nframes);
1669484Sgarrett.damore@Sun.COM 
16711936Sgdamore@opensolaris.org 	for (resid = nfr; resid; resid -= cnt) {
16811936Sgdamore@opensolaris.org 		int	nbytes;
16911936Sgdamore@opensolaris.org 
17011936Sgdamore@opensolaris.org 		cnt = min((e->e_nframes - tidx), resid);
17111936Sgdamore@opensolaris.org 		nbytes = cnt * e->e_framesz;
17211936Sgdamore@opensolaris.org 
17311936Sgdamore@opensolaris.org 		bcopy(e->e_data + (tidx * e->e_framesz), ptr, nbytes);
17411936Sgdamore@opensolaris.org 		ptr += nbytes;
17511936Sgdamore@opensolaris.org 		tidx += cnt;
17611936Sgdamore@opensolaris.org 		if (tidx == e->e_nframes) {
17711936Sgdamore@opensolaris.org 			tidx = 0;
17811936Sgdamore@opensolaris.org 		}
1799484Sgarrett.damore@Sun.COM 	}
1809484Sgarrett.damore@Sun.COM 
1819484Sgarrett.damore@Sun.COM 	if (e->e_flags & ENGINE_INPUT) {
1829484Sgarrett.damore@Sun.COM 		/* record */
1839484Sgarrett.damore@Sun.COM 		e->e_hidx = 0;
1849484Sgarrett.damore@Sun.COM 		e->e_tidx = (e->e_nframes - nfr) % e->e_nframes;
1859484Sgarrett.damore@Sun.COM 	} else {
1869484Sgarrett.damore@Sun.COM 		/* play */
1879484Sgarrett.damore@Sun.COM 		e->e_hidx = nfr % e->e_nframes;
1889484Sgarrett.damore@Sun.COM 		e->e_tidx = 0;
1899484Sgarrett.damore@Sun.COM 	}
1909484Sgarrett.damore@Sun.COM 
1919484Sgarrett.damore@Sun.COM 	/* relocate from scratch area to destination */
1929484Sgarrett.damore@Sun.COM 	bcopy(buf, e->e_data + (e->e_tidx * e->e_framesz), nfr * e->e_framesz);
19311936Sgdamore@opensolaris.org 	kmem_free(buf, nfr * e->e_framesz);
1949484Sgarrett.damore@Sun.COM }
1959484Sgarrett.damore@Sun.COM 
19611936Sgdamore@opensolaris.org static volatile uint_t auimpl_engno = 0;
19711936Sgdamore@opensolaris.org 
1989484Sgarrett.damore@Sun.COM audio_engine_t *
audio_engine_alloc(audio_engine_ops_t * ops,uint_t flags)19911936Sgdamore@opensolaris.org audio_engine_alloc(audio_engine_ops_t *ops, uint_t flags)
2009484Sgarrett.damore@Sun.COM {
2019484Sgarrett.damore@Sun.COM 	int i;
2029484Sgarrett.damore@Sun.COM 	audio_engine_t *e;
20311936Sgdamore@opensolaris.org 	char tname[32];
20411936Sgdamore@opensolaris.org 	int num;
2059484Sgarrett.damore@Sun.COM 
2069484Sgarrett.damore@Sun.COM 	if (ops->audio_engine_version != AUDIO_ENGINE_VERSION) {
2079484Sgarrett.damore@Sun.COM 		audio_dev_warn(NULL, "audio engine version mismatch: %d != %d",
2089484Sgarrett.damore@Sun.COM 		    ops->audio_engine_version, AUDIO_ENGINE_VERSION);
2099484Sgarrett.damore@Sun.COM 		return (NULL);
2109484Sgarrett.damore@Sun.COM 	}
2119484Sgarrett.damore@Sun.COM 
2129484Sgarrett.damore@Sun.COM 	/* NB: The ops vector must be held in persistent storage! */
2139484Sgarrett.damore@Sun.COM 	e = kmem_zalloc(sizeof (audio_engine_t), KM_NOSLEEP);
2149484Sgarrett.damore@Sun.COM 	if (e == NULL) {
2159484Sgarrett.damore@Sun.COM 		audio_dev_warn(NULL, "unable to allocate engine struct");
2169484Sgarrett.damore@Sun.COM 		return (NULL);
2179484Sgarrett.damore@Sun.COM 	}
2189484Sgarrett.damore@Sun.COM 	e->e_ops = *ops;
21911936Sgdamore@opensolaris.org 	mutex_init(&e->e_lock, NULL, MUTEX_DRIVER,
22011936Sgdamore@opensolaris.org 	    DDI_INTR_PRI(audio_priority));
22111936Sgdamore@opensolaris.org 	cv_init(&e->e_cv, NULL, CV_DRIVER, NULL);
2229484Sgarrett.damore@Sun.COM 	list_create(&e->e_streams, sizeof (struct audio_stream),
2239484Sgarrett.damore@Sun.COM 	    offsetof(struct audio_stream, s_eng_linkage));
2249484Sgarrett.damore@Sun.COM 
2259484Sgarrett.damore@Sun.COM 	for (i = 0; i < AUDIO_MAX_CHANNELS; i++) {
2269484Sgarrett.damore@Sun.COM 		e->e_chbufs[i] = kmem_zalloc(sizeof (int32_t) * AUDIO_CHBUFS,
2279484Sgarrett.damore@Sun.COM 		    KM_NOSLEEP);
2289484Sgarrett.damore@Sun.COM 		if (e->e_chbufs[i] == NULL) {
2299484Sgarrett.damore@Sun.COM 			audio_dev_warn(NULL, "unable to allocate channel buf");
2309484Sgarrett.damore@Sun.COM 			audio_engine_free(e);
2319484Sgarrett.damore@Sun.COM 			return (NULL);
2329484Sgarrett.damore@Sun.COM 		}
2339484Sgarrett.damore@Sun.COM 	}
2349484Sgarrett.damore@Sun.COM 
23511936Sgdamore@opensolaris.org 	num = atomic_inc_uint_nv(&auimpl_engno);
23611936Sgdamore@opensolaris.org 
23711936Sgdamore@opensolaris.org 	(void) snprintf(tname, sizeof (tname), "audio_engine_%d", num);
23811936Sgdamore@opensolaris.org 
2399484Sgarrett.damore@Sun.COM 	e->e_flags = flags & ENGINE_DRIVER_FLAGS;
2409484Sgarrett.damore@Sun.COM 	return (e);
2419484Sgarrett.damore@Sun.COM }
2429484Sgarrett.damore@Sun.COM 
2439484Sgarrett.damore@Sun.COM void
audio_engine_free(audio_engine_t * e)2449484Sgarrett.damore@Sun.COM audio_engine_free(audio_engine_t *e)
2459484Sgarrett.damore@Sun.COM {
2469484Sgarrett.damore@Sun.COM 	int i;
2479484Sgarrett.damore@Sun.COM 
2489484Sgarrett.damore@Sun.COM 	for (i = 0; i < AUDIO_MAX_CHANNELS; i++) {
2499484Sgarrett.damore@Sun.COM 		if (e->e_chbufs[i] != NULL) {
2509484Sgarrett.damore@Sun.COM 			kmem_free(e->e_chbufs[i],
2519484Sgarrett.damore@Sun.COM 			    sizeof (int32_t) * AUDIO_CHBUFS);
2529484Sgarrett.damore@Sun.COM 		}
2539484Sgarrett.damore@Sun.COM 	}
25411936Sgdamore@opensolaris.org 
2559484Sgarrett.damore@Sun.COM 	list_destroy(&e->e_streams);
2569484Sgarrett.damore@Sun.COM 	mutex_destroy(&e->e_lock);
25711936Sgdamore@opensolaris.org 	cv_destroy(&e->e_cv);
2589484Sgarrett.damore@Sun.COM 	kmem_free(e, sizeof (*e));
2599484Sgarrett.damore@Sun.COM }
2609484Sgarrett.damore@Sun.COM 
2619484Sgarrett.damore@Sun.COM static list_t auimpl_devs_by_index;
2629484Sgarrett.damore@Sun.COM static list_t auimpl_devs_by_number;
2639484Sgarrett.damore@Sun.COM static krwlock_t auimpl_dev_lock;
2649484Sgarrett.damore@Sun.COM 
2659484Sgarrett.damore@Sun.COM /*
2669484Sgarrett.damore@Sun.COM  * Not for public consumption: Private interfaces.
2679484Sgarrett.damore@Sun.COM  */
2689484Sgarrett.damore@Sun.COM void
auimpl_dev_hold(audio_dev_t * d)2699484Sgarrett.damore@Sun.COM auimpl_dev_hold(audio_dev_t *d)
2709484Sgarrett.damore@Sun.COM {
2719484Sgarrett.damore@Sun.COM 	/* bump the reference count */
2729484Sgarrett.damore@Sun.COM 	mutex_enter(&d->d_lock);
2739484Sgarrett.damore@Sun.COM 	d->d_refcnt++;
2749484Sgarrett.damore@Sun.COM 	mutex_exit(&d->d_lock);
2759484Sgarrett.damore@Sun.COM }
2769484Sgarrett.damore@Sun.COM 
2779484Sgarrett.damore@Sun.COM audio_dev_t *
auimpl_dev_hold_by_devt(dev_t dev)2789484Sgarrett.damore@Sun.COM auimpl_dev_hold_by_devt(dev_t dev)
2799484Sgarrett.damore@Sun.COM {
2809484Sgarrett.damore@Sun.COM 	audio_dev_t *d;
2819484Sgarrett.damore@Sun.COM 	major_t major;
2829484Sgarrett.damore@Sun.COM 	int instance;
2839484Sgarrett.damore@Sun.COM 	list_t *l = &auimpl_devs_by_index;
2849484Sgarrett.damore@Sun.COM 
2859484Sgarrett.damore@Sun.COM 	major = getmajor(dev);
2869484Sgarrett.damore@Sun.COM 	instance = (getminor(dev) >> AUDIO_MN_INST_SHIFT) & AUDIO_MN_INST_MASK;
2879484Sgarrett.damore@Sun.COM 
2889484Sgarrett.damore@Sun.COM 	rw_enter(&auimpl_dev_lock, RW_READER);
2899484Sgarrett.damore@Sun.COM 
2909484Sgarrett.damore@Sun.COM 	for (d = list_head(l); d; d = list_next(l, d)) {
2919484Sgarrett.damore@Sun.COM 		if ((d->d_major == major) && (d->d_instance == instance)) {
2929484Sgarrett.damore@Sun.COM 			auimpl_dev_hold(d);
2939484Sgarrett.damore@Sun.COM 			break;
2949484Sgarrett.damore@Sun.COM 		}
2959484Sgarrett.damore@Sun.COM 	}
2969484Sgarrett.damore@Sun.COM 
2979484Sgarrett.damore@Sun.COM 	rw_exit(&auimpl_dev_lock);
2989484Sgarrett.damore@Sun.COM 	return (d);
2999484Sgarrett.damore@Sun.COM }
3009484Sgarrett.damore@Sun.COM 
3019484Sgarrett.damore@Sun.COM audio_dev_t *
auimpl_dev_hold_by_index(int index)3029484Sgarrett.damore@Sun.COM auimpl_dev_hold_by_index(int index)
3039484Sgarrett.damore@Sun.COM {
3049484Sgarrett.damore@Sun.COM 	audio_dev_t *d;
3059484Sgarrett.damore@Sun.COM 	list_t *l = &auimpl_devs_by_index;
3069484Sgarrett.damore@Sun.COM 
3079484Sgarrett.damore@Sun.COM 	rw_enter(&auimpl_dev_lock, RW_READER);
3089484Sgarrett.damore@Sun.COM 
3099484Sgarrett.damore@Sun.COM 	for (d = list_head(l); d; d = list_next(l, d)) {
3109484Sgarrett.damore@Sun.COM 		if (d->d_index == index) {
3119484Sgarrett.damore@Sun.COM 			auimpl_dev_hold(d);
3129484Sgarrett.damore@Sun.COM 			break;
3139484Sgarrett.damore@Sun.COM 		}
3149484Sgarrett.damore@Sun.COM 	}
3159484Sgarrett.damore@Sun.COM 
3169484Sgarrett.damore@Sun.COM 	rw_exit(&auimpl_dev_lock);
3179484Sgarrett.damore@Sun.COM 	return (d);
3189484Sgarrett.damore@Sun.COM }
3199484Sgarrett.damore@Sun.COM 
3209484Sgarrett.damore@Sun.COM void
auimpl_dev_release(audio_dev_t * d)3219484Sgarrett.damore@Sun.COM auimpl_dev_release(audio_dev_t *d)
3229484Sgarrett.damore@Sun.COM {
3239484Sgarrett.damore@Sun.COM 	mutex_enter(&d->d_lock);
3249484Sgarrett.damore@Sun.COM 	d->d_refcnt--;
3259484Sgarrett.damore@Sun.COM 	mutex_exit(&d->d_lock);
3269484Sgarrett.damore@Sun.COM }
3279484Sgarrett.damore@Sun.COM 
3289484Sgarrett.damore@Sun.COM int
auimpl_choose_format(int fmts)3299484Sgarrett.damore@Sun.COM auimpl_choose_format(int fmts)
3309484Sgarrett.damore@Sun.COM {
3319484Sgarrett.damore@Sun.COM 	/*
3329484Sgarrett.damore@Sun.COM 	 * Choose the very best format we can.  We choose 24 bit in
3339484Sgarrett.damore@Sun.COM 	 * preference to 32 bit because we mix in 24 bit.  We do that
3349484Sgarrett.damore@Sun.COM 	 * to allow overflows to fit within 32-bits.  (Very few humans
3359484Sgarrett.damore@Sun.COM 	 * can tell a difference between 24 and 32 bit audio anyway.)
3369484Sgarrett.damore@Sun.COM 	 */
3379484Sgarrett.damore@Sun.COM 	if (fmts & AUDIO_FORMAT_S24_NE)
3389484Sgarrett.damore@Sun.COM 		return (AUDIO_FORMAT_S24_NE);
3399484Sgarrett.damore@Sun.COM 
3409484Sgarrett.damore@Sun.COM 	if (fmts & AUDIO_FORMAT_S32_NE)
3419484Sgarrett.damore@Sun.COM 		return (AUDIO_FORMAT_S32_NE);
3429484Sgarrett.damore@Sun.COM 
3439484Sgarrett.damore@Sun.COM 	if (fmts & AUDIO_FORMAT_S24_OE)
3449484Sgarrett.damore@Sun.COM 		return (AUDIO_FORMAT_S24_OE);
3459484Sgarrett.damore@Sun.COM 
3469484Sgarrett.damore@Sun.COM 	if (fmts & AUDIO_FORMAT_S32_OE)
3479484Sgarrett.damore@Sun.COM 		return (AUDIO_FORMAT_S32_OE);
3489484Sgarrett.damore@Sun.COM 
3499484Sgarrett.damore@Sun.COM 	if (fmts & AUDIO_FORMAT_S16_NE)
3509484Sgarrett.damore@Sun.COM 		return (AUDIO_FORMAT_S16_NE);
3519484Sgarrett.damore@Sun.COM 
3529484Sgarrett.damore@Sun.COM 	if (fmts & AUDIO_FORMAT_S16_OE)
3539484Sgarrett.damore@Sun.COM 		return (AUDIO_FORMAT_S16_OE);
3549484Sgarrett.damore@Sun.COM 
3559484Sgarrett.damore@Sun.COM 	if (fmts & AUDIO_FORMAT_AC3)
3569484Sgarrett.damore@Sun.COM 		return (AUDIO_FORMAT_AC3);
3579484Sgarrett.damore@Sun.COM 
3589484Sgarrett.damore@Sun.COM 	return (AUDIO_FORMAT_NONE);
3599484Sgarrett.damore@Sun.COM }
3609484Sgarrett.damore@Sun.COM 
3619484Sgarrett.damore@Sun.COM int
auimpl_engine_open(audio_stream_t * sp,int flags)362*12165Sgdamore@opensolaris.org auimpl_engine_open(audio_stream_t *sp, int flags)
3639484Sgarrett.damore@Sun.COM {
364*12165Sgdamore@opensolaris.org 	return (auimpl_engine_setup(sp, flags, NULL, FORMAT_MSK_NONE));
365*12165Sgdamore@opensolaris.org }
366*12165Sgdamore@opensolaris.org 
367*12165Sgdamore@opensolaris.org 
368*12165Sgdamore@opensolaris.org int
auimpl_engine_setup(audio_stream_t * sp,int flags,audio_parms_t * parms,uint_t mask)369*12165Sgdamore@opensolaris.org auimpl_engine_setup(audio_stream_t *sp, int flags, audio_parms_t *parms,
370*12165Sgdamore@opensolaris.org     uint_t mask)
371*12165Sgdamore@opensolaris.org {
372*12165Sgdamore@opensolaris.org 	audio_dev_t	*d = sp->s_client->c_dev;
3739484Sgarrett.damore@Sun.COM 	audio_engine_t	*e = NULL;
374*12165Sgdamore@opensolaris.org 	audio_parms_t	uparms;
3759484Sgarrett.damore@Sun.COM 	list_t		*list;
376*12165Sgdamore@opensolaris.org 	uint_t		cap;
3779484Sgarrett.damore@Sun.COM 	int		priority = 0;
3789484Sgarrett.damore@Sun.COM 	int		rv = ENODEV;
3799484Sgarrett.damore@Sun.COM 	int		sampsz;
3809484Sgarrett.damore@Sun.COM 	int		i;
38111936Sgdamore@opensolaris.org 	int		fragfr;
382*12165Sgdamore@opensolaris.org 	int		fmts;
3839484Sgarrett.damore@Sun.COM 
384*12165Sgdamore@opensolaris.org 
385*12165Sgdamore@opensolaris.org 	mutex_enter(&d->d_lock);
386*12165Sgdamore@opensolaris.org 
387*12165Sgdamore@opensolaris.org 	uparms = *sp->s_user_parms;
388*12165Sgdamore@opensolaris.org 	if (mask & FORMAT_MSK_FMT)
389*12165Sgdamore@opensolaris.org 		uparms.p_format = parms->p_format;
390*12165Sgdamore@opensolaris.org 	if (mask & FORMAT_MSK_RATE)
391*12165Sgdamore@opensolaris.org 		uparms.p_rate = parms->p_rate;
392*12165Sgdamore@opensolaris.org 	if (mask & FORMAT_MSK_CHAN)
393*12165Sgdamore@opensolaris.org 		uparms.p_nchan = parms->p_nchan;
3949484Sgarrett.damore@Sun.COM 
3959484Sgarrett.damore@Sun.COM 	/*
396*12165Sgdamore@opensolaris.org 	 * Which direction are we opening?  (We must open exactly
3979484Sgarrett.damore@Sun.COM 	 * one direction, otherwise the open is meaningless.)
3989484Sgarrett.damore@Sun.COM 	 */
399*12165Sgdamore@opensolaris.org 
400*12165Sgdamore@opensolaris.org 	if (sp == &sp->s_client->c_ostream) {
401*12165Sgdamore@opensolaris.org 		cap = ENGINE_OUTPUT_CAP;
402*12165Sgdamore@opensolaris.org 		flags |= ENGINE_OUTPUT;
403*12165Sgdamore@opensolaris.org 	} else {
404*12165Sgdamore@opensolaris.org 		cap = ENGINE_INPUT_CAP;
405*12165Sgdamore@opensolaris.org 		flags |= ENGINE_INPUT;
406*12165Sgdamore@opensolaris.org 	}
407*12165Sgdamore@opensolaris.org 
408*12165Sgdamore@opensolaris.org 	if (uparms.p_format == AUDIO_FORMAT_AC3) {
409*12165Sgdamore@opensolaris.org 		fmts = AUDIO_FORMAT_AC3;
410*12165Sgdamore@opensolaris.org 		flags |= ENGINE_EXCLUSIVE;
411*12165Sgdamore@opensolaris.org 	} else {
412*12165Sgdamore@opensolaris.org 		fmts = AUDIO_FORMAT_PCM;
413*12165Sgdamore@opensolaris.org 	}
4149484Sgarrett.damore@Sun.COM 
4159484Sgarrett.damore@Sun.COM 	list = &d->d_engines;
4169484Sgarrett.damore@Sun.COM 
4179484Sgarrett.damore@Sun.COM 
418*12165Sgdamore@opensolaris.org 	/* If the device is suspended, wait for it to resume. */
41911936Sgdamore@opensolaris.org 	while (d->d_suspended) {
42011936Sgdamore@opensolaris.org 		cv_wait(&d->d_ctrl_cv, &d->d_lock);
42111936Sgdamore@opensolaris.org 	}
42211936Sgdamore@opensolaris.org 
4239484Sgarrett.damore@Sun.COM again:
4249484Sgarrett.damore@Sun.COM 
4259484Sgarrett.damore@Sun.COM 	for (audio_engine_t *t = list_head(list); t; t = list_next(list, t)) {
426*12165Sgdamore@opensolaris.org 		int		mypri;
427*12165Sgdamore@opensolaris.org 		int		r;
4289484Sgarrett.damore@Sun.COM 
429*12165Sgdamore@opensolaris.org 		/* Make sure the engine can do what we want it to. */
4309484Sgarrett.damore@Sun.COM 		mutex_enter(&t->e_lock);
43111936Sgdamore@opensolaris.org 
432*12165Sgdamore@opensolaris.org 		if ((t->e_flags & cap) == 0) {
433*12165Sgdamore@opensolaris.org 			mutex_exit(&t->e_lock);
434*12165Sgdamore@opensolaris.org 			continue;
435*12165Sgdamore@opensolaris.org 		}
436*12165Sgdamore@opensolaris.org 
437*12165Sgdamore@opensolaris.org 		/*
438*12165Sgdamore@opensolaris.org 		 * Open the engine early, as the inquiries to rate and format
439*12165Sgdamore@opensolaris.org 		 * may not be accurate until this is done.
440*12165Sgdamore@opensolaris.org 		 */
441*12165Sgdamore@opensolaris.org 		if (list_is_empty(&t->e_streams)) {
442*12165Sgdamore@opensolaris.org 			if (ENG_OPEN(t, flags, &t->e_nframes, &t->e_data)) {
443*12165Sgdamore@opensolaris.org 				mutex_exit(&t->e_lock);
444*12165Sgdamore@opensolaris.org 				rv = EIO;
445*12165Sgdamore@opensolaris.org 				continue;
446*12165Sgdamore@opensolaris.org 			}
447*12165Sgdamore@opensolaris.org 		}
448*12165Sgdamore@opensolaris.org 
449*12165Sgdamore@opensolaris.org 		if ((ENG_FORMAT(t) & fmts) == 0) {
450*12165Sgdamore@opensolaris.org 			if (list_is_empty(&t->e_streams))
451*12165Sgdamore@opensolaris.org 				ENG_CLOSE(t);
4529484Sgarrett.damore@Sun.COM 			mutex_exit(&t->e_lock);
4539484Sgarrett.damore@Sun.COM 			continue;
4549484Sgarrett.damore@Sun.COM 		}
4559484Sgarrett.damore@Sun.COM 
456*12165Sgdamore@opensolaris.org 
457*12165Sgdamore@opensolaris.org 		/* If it is in failed state, don't use this engine. */
45811936Sgdamore@opensolaris.org 		if (t->e_failed) {
459*12165Sgdamore@opensolaris.org 			if (list_is_empty(&t->e_streams))
460*12165Sgdamore@opensolaris.org 				ENG_CLOSE(t);
46111936Sgdamore@opensolaris.org 			mutex_exit(&t->e_lock);
462*12165Sgdamore@opensolaris.org 			rv = rv ? EIO : 0;
46311936Sgdamore@opensolaris.org 			continue;
46411936Sgdamore@opensolaris.org 		}
46511936Sgdamore@opensolaris.org 
466*12165Sgdamore@opensolaris.org 		/*
467*12165Sgdamore@opensolaris.org 		 * If the engine is in exclusive use, we can't use it.
468*12165Sgdamore@opensolaris.org 		 * This is intended for use with AC3 or digital
469*12165Sgdamore@opensolaris.org 		 * streams that cannot tolerate mixing.
470*12165Sgdamore@opensolaris.org 		 */
471*12165Sgdamore@opensolaris.org 		if ((t->e_flags & ENGINE_EXCLUSIVE) && (t != sp->s_engine)) {
472*12165Sgdamore@opensolaris.org 			if (list_is_empty(&t->e_streams))
473*12165Sgdamore@opensolaris.org 				ENG_CLOSE(t);
4749484Sgarrett.damore@Sun.COM 			mutex_exit(&t->e_lock);
475*12165Sgdamore@opensolaris.org 			rv = rv ? EBUSY : 0;
4769484Sgarrett.damore@Sun.COM 			continue;
4779484Sgarrett.damore@Sun.COM 		}
4789484Sgarrett.damore@Sun.COM 
479*12165Sgdamore@opensolaris.org 		/*
480*12165Sgdamore@opensolaris.org 		 * If the engine is in use incompatibly, we can't use
481*12165Sgdamore@opensolaris.org 		 * it.  This should only happen for half-duplex audio
482*12165Sgdamore@opensolaris.org 		 * devices.  I've not seen any of these that are
483*12165Sgdamore@opensolaris.org 		 * recent enough to be supported by Solaris.
484*12165Sgdamore@opensolaris.org 		 */
4859484Sgarrett.damore@Sun.COM 		if (((flags & ENGINE_INPUT) && (t->e_flags & ENGINE_OUTPUT)) ||
4869484Sgarrett.damore@Sun.COM 		    ((flags & ENGINE_OUTPUT) && (t->e_flags & ENGINE_INPUT))) {
487*12165Sgdamore@opensolaris.org 			if (list_is_empty(&t->e_streams))
488*12165Sgdamore@opensolaris.org 				ENG_CLOSE(t);
4899484Sgarrett.damore@Sun.COM 			mutex_exit(&t->e_lock);
490*12165Sgdamore@opensolaris.org 			/* Only override the ENODEV or EIO. */
491*12165Sgdamore@opensolaris.org 			rv = rv ? EBUSY : 0;
4929484Sgarrett.damore@Sun.COM 			continue;
4939484Sgarrett.damore@Sun.COM 		}
4949484Sgarrett.damore@Sun.COM 
4959484Sgarrett.damore@Sun.COM 		/*
4969484Sgarrett.damore@Sun.COM 		 * In order to support as many different possible
4979484Sgarrett.damore@Sun.COM 		 * output streams (e.g. AC3 passthru or AC3 decode),
4989484Sgarrett.damore@Sun.COM 		 * or multiple exclusive outputs, we treat audio
4999484Sgarrett.damore@Sun.COM 		 * engines as *precious*.
5009484Sgarrett.damore@Sun.COM 		 *
5019484Sgarrett.damore@Sun.COM 		 * This means that we will try hard to reuse an
5029484Sgarrett.damore@Sun.COM 		 * existing allocated engine.  This may not be the
5039484Sgarrett.damore@Sun.COM 		 * optimal performance configuration (especially if we
5049484Sgarrett.damore@Sun.COM 		 * wanted to avoid rate conversion, for example), but
5059484Sgarrett.damore@Sun.COM 		 * it should have fewer cases where the configuration
5069484Sgarrett.damore@Sun.COM 		 * results in denying service to any client.
5079484Sgarrett.damore@Sun.COM 		 */
5089484Sgarrett.damore@Sun.COM 
509*12165Sgdamore@opensolaris.org 		/*
510*12165Sgdamore@opensolaris.org 		 * This engine *can* support us, so we should no longer
511*12165Sgdamore@opensolaris.org 		 * have a failure mode.
512*12165Sgdamore@opensolaris.org 		 */
5139484Sgarrett.damore@Sun.COM 		rv = 0;
514*12165Sgdamore@opensolaris.org 		mypri = (1U << 0);
515*12165Sgdamore@opensolaris.org 
516*12165Sgdamore@opensolaris.org 
517*12165Sgdamore@opensolaris.org 		/*
518*12165Sgdamore@opensolaris.org 		 * Mixing is cheap, so try not to pick on idle
519*12165Sgdamore@opensolaris.org 		 * engines.  This avoids burning bus bandwidth (which
520*12165Sgdamore@opensolaris.org 		 * may be precious for certain classes of traffic).
521*12165Sgdamore@opensolaris.org 		 * Note that idleness is given a low priority compared
522*12165Sgdamore@opensolaris.org 		 * to the other considerations.
523*12165Sgdamore@opensolaris.org 		 *
524*12165Sgdamore@opensolaris.org 		 * We also use this opportunity open the engine, if
525*12165Sgdamore@opensolaris.org 		 * not already done so, so that our parameter
526*12165Sgdamore@opensolaris.org 		 * inquiries will be valid.
527*12165Sgdamore@opensolaris.org 		 */
528*12165Sgdamore@opensolaris.org 		if (!list_is_empty(&t->e_streams))
529*12165Sgdamore@opensolaris.org 			mypri |= (1U << 1);
530*12165Sgdamore@opensolaris.org 
531*12165Sgdamore@opensolaris.org 		/*
532*12165Sgdamore@opensolaris.org 		 * Slight preference is given to reuse an engine that
533*12165Sgdamore@opensolaris.org 		 * we might already be using.
534*12165Sgdamore@opensolaris.org 		 */
535*12165Sgdamore@opensolaris.org 		if (t == sp->s_engine)
536*12165Sgdamore@opensolaris.org 			mypri |= (1U << 2);
537*12165Sgdamore@opensolaris.org 
5389484Sgarrett.damore@Sun.COM 
539*12165Sgdamore@opensolaris.org 		/*
540*12165Sgdamore@opensolaris.org 		 * Sample rate conversion avoidance.  Upsampling
541*12165Sgdamore@opensolaris.org 		 * requires multiplications and is moderately
542*12165Sgdamore@opensolaris.org 		 * expensive.  Downsampling requires division and is
543*12165Sgdamore@opensolaris.org 		 * quite expensive, and hence to be avoided if at all
544*12165Sgdamore@opensolaris.org 		 * possible.
545*12165Sgdamore@opensolaris.org 		 */
546*12165Sgdamore@opensolaris.org 		r = ENG_RATE(t);
547*12165Sgdamore@opensolaris.org 		if (uparms.p_rate == r) {
548*12165Sgdamore@opensolaris.org 			/*
549*12165Sgdamore@opensolaris.org 			 * No conversion needed at all.  This is ideal.
550*12165Sgdamore@opensolaris.org 			 */
551*12165Sgdamore@opensolaris.org 			mypri |= (1U << 4) | (1U << 3);
552*12165Sgdamore@opensolaris.org 		} else {
553*12165Sgdamore@opensolaris.org 			int src, dst;
554*12165Sgdamore@opensolaris.org 
555*12165Sgdamore@opensolaris.org 			if (flags & ENGINE_INPUT) {
556*12165Sgdamore@opensolaris.org 				src = r;
557*12165Sgdamore@opensolaris.org 				dst = uparms.p_rate;
558*12165Sgdamore@opensolaris.org 			} else {
559*12165Sgdamore@opensolaris.org 				src = uparms.p_rate;
560*12165Sgdamore@opensolaris.org 				dst = r;
561*12165Sgdamore@opensolaris.org 			}
562*12165Sgdamore@opensolaris.org 			if ((src < dst) && ((dst % src) == 0)) {
563*12165Sgdamore@opensolaris.org 				/*
564*12165Sgdamore@opensolaris.org 				 * Pure upsampling only. This
565*12165Sgdamore@opensolaris.org 				 * penalizes any engine which requires
566*12165Sgdamore@opensolaris.org 				 * downsampling.
567*12165Sgdamore@opensolaris.org 				 */
568*12165Sgdamore@opensolaris.org 				mypri |= (1U << 3);
569*12165Sgdamore@opensolaris.org 			}
5709484Sgarrett.damore@Sun.COM 		}
5719484Sgarrett.damore@Sun.COM 
572*12165Sgdamore@opensolaris.org 		/*
573*12165Sgdamore@opensolaris.org 		 * Try not to pick on duplex engines.  This way we
574*12165Sgdamore@opensolaris.org 		 * leave engines that can be used for recording or
575*12165Sgdamore@opensolaris.org 		 * playback available as such.  All modern drivers
576*12165Sgdamore@opensolaris.org 		 * use separate unidirectional engines for playback
577*12165Sgdamore@opensolaris.org 		 * and record.
578*12165Sgdamore@opensolaris.org 		 */
579*12165Sgdamore@opensolaris.org 		if ((t->e_flags & ENGINE_CAPS) == cap) {
580*12165Sgdamore@opensolaris.org 			mypri |= (1U << 5);
5819484Sgarrett.damore@Sun.COM 		}
5829484Sgarrett.damore@Sun.COM 
583*12165Sgdamore@opensolaris.org 		/*
584*12165Sgdamore@opensolaris.org 		 * Try not to pick on engines that can do other
585*12165Sgdamore@opensolaris.org 		 * formats.  This will generally be false, but if it
586*12165Sgdamore@opensolaris.org 		 * happens we pretty strongly avoid using a limited
587*12165Sgdamore@opensolaris.org 		 * resource.
588*12165Sgdamore@opensolaris.org 		 */
589*12165Sgdamore@opensolaris.org 		if ((t->e_format & ~fmts) == 0) {
590*12165Sgdamore@opensolaris.org 			mypri |= (1U << 6);
5919484Sgarrett.damore@Sun.COM 		}
5929484Sgarrett.damore@Sun.COM 
5939484Sgarrett.damore@Sun.COM 		if (mypri > priority) {
5949484Sgarrett.damore@Sun.COM 			if (e != NULL) {
595*12165Sgdamore@opensolaris.org 				/*
596*12165Sgdamore@opensolaris.org 				 * If we opened this for our own use
597*12165Sgdamore@opensolaris.org 				 * and we are no longer using it, then
598*12165Sgdamore@opensolaris.org 				 * close it back down.
599*12165Sgdamore@opensolaris.org 				 */
600*12165Sgdamore@opensolaris.org 				if (list_is_empty(&e->e_streams))
601*12165Sgdamore@opensolaris.org 					ENG_CLOSE(e);
6029484Sgarrett.damore@Sun.COM 				mutex_exit(&e->e_lock);
6039484Sgarrett.damore@Sun.COM 			}
6049484Sgarrett.damore@Sun.COM 			e = t;
6059484Sgarrett.damore@Sun.COM 			priority = mypri;
6069484Sgarrett.damore@Sun.COM 		} else {
6079484Sgarrett.damore@Sun.COM 			mutex_exit(&t->e_lock);
6089484Sgarrett.damore@Sun.COM 		}
609*12165Sgdamore@opensolaris.org 
610*12165Sgdamore@opensolaris.org 		/*
611*12165Sgdamore@opensolaris.org 		 * Locking: at this point, if we have an engine, "e", it is
612*12165Sgdamore@opensolaris.org 		 * locked.  No other engines should have a lock held.
613*12165Sgdamore@opensolaris.org 		 */
6149484Sgarrett.damore@Sun.COM 	}
6159484Sgarrett.damore@Sun.COM 
6169484Sgarrett.damore@Sun.COM 	if ((rv == EBUSY) && ((flags & ENGINE_NDELAY) == 0)) {
6179484Sgarrett.damore@Sun.COM 		ASSERT(e == NULL);
6189484Sgarrett.damore@Sun.COM 		if (cv_wait_sig(&d->d_cv, &d->d_lock) == 0) {
6199484Sgarrett.damore@Sun.COM 			mutex_exit(&d->d_lock);
6209484Sgarrett.damore@Sun.COM 			return (EINTR);
6219484Sgarrett.damore@Sun.COM 		}
6229484Sgarrett.damore@Sun.COM 		goto again;
6239484Sgarrett.damore@Sun.COM 	}
6249484Sgarrett.damore@Sun.COM 
6259484Sgarrett.damore@Sun.COM 	if (rv != 0) {
6269484Sgarrett.damore@Sun.COM 		ASSERT(e == NULL);
6279484Sgarrett.damore@Sun.COM 		mutex_exit(&d->d_lock);
6289484Sgarrett.damore@Sun.COM 		return (rv);
6299484Sgarrett.damore@Sun.COM 	}
6309484Sgarrett.damore@Sun.COM 
6319484Sgarrett.damore@Sun.COM 	ASSERT(e != NULL);
6329484Sgarrett.damore@Sun.COM 	ASSERT(mutex_owned(&e->e_lock));
6339484Sgarrett.damore@Sun.COM 
634*12165Sgdamore@opensolaris.org 	if (sp->s_engine && (sp->s_engine != e)) {
635*12165Sgdamore@opensolaris.org 		/*
636*12165Sgdamore@opensolaris.org 		 * If this represents a potential engine change, then
637*12165Sgdamore@opensolaris.org 		 * we close off everything, and start anew. This turns
638*12165Sgdamore@opensolaris.org 		 * out to be vastly simpler than trying to close all
639*12165Sgdamore@opensolaris.org 		 * the races associated with a true hand off.  This
640*12165Sgdamore@opensolaris.org 		 * ought to be relatively uncommon (changing engines).
641*12165Sgdamore@opensolaris.org 		 */
642*12165Sgdamore@opensolaris.org 
643*12165Sgdamore@opensolaris.org 		/* Drop the new reference. */
644*12165Sgdamore@opensolaris.org 		if (list_is_empty(&e->e_streams))
645*12165Sgdamore@opensolaris.org 			ENG_CLOSE(e);
646*12165Sgdamore@opensolaris.org 		mutex_exit(&e->e_lock);
647*12165Sgdamore@opensolaris.org 		mutex_exit(&d->d_lock);
648*12165Sgdamore@opensolaris.org 
649*12165Sgdamore@opensolaris.org 		auimpl_engine_close(sp);
650*12165Sgdamore@opensolaris.org 
651*12165Sgdamore@opensolaris.org 		/* Try again. */
652*12165Sgdamore@opensolaris.org 		return (auimpl_engine_setup(sp, flags, parms, mask));
653*12165Sgdamore@opensolaris.org 	}
654*12165Sgdamore@opensolaris.org 
655*12165Sgdamore@opensolaris.org 	if (sp->s_engine == NULL) {
656*12165Sgdamore@opensolaris.org 		/*
657*12165Sgdamore@opensolaris.org 		 * Add a reference to this engine if we don't already
658*12165Sgdamore@opensolaris.org 		 * have one.
659*12165Sgdamore@opensolaris.org 		 */
660*12165Sgdamore@opensolaris.org 		sp->s_engine = e;
661*12165Sgdamore@opensolaris.org 
662*12165Sgdamore@opensolaris.org 		if (!list_is_empty(&e->e_streams)) {
663*12165Sgdamore@opensolaris.org 			/*
664*12165Sgdamore@opensolaris.org 			 * If the engine is already open, there is no
665*12165Sgdamore@opensolaris.org 			 * need for further work.  The first open will
666*12165Sgdamore@opensolaris.org 			 * be relatively expensive, but subsequent
667*12165Sgdamore@opensolaris.org 			 * opens should be as cheap as possible.
668*12165Sgdamore@opensolaris.org 			 */
669*12165Sgdamore@opensolaris.org 			list_insert_tail(&e->e_streams, sp);
670*12165Sgdamore@opensolaris.org 			goto ok;
671*12165Sgdamore@opensolaris.org 		}
672*12165Sgdamore@opensolaris.org 		list_insert_tail(&e->e_streams, sp);
673*12165Sgdamore@opensolaris.org 
674*12165Sgdamore@opensolaris.org 	} else {
675*12165Sgdamore@opensolaris.org 		ASSERT(sp->s_engine == e);
676*12165Sgdamore@opensolaris.org 		/*
677*12165Sgdamore@opensolaris.org 		 * No change in engine... hence don't reprogram the
678*12165Sgdamore@opensolaris.org 		 * engine, and don't change references.
679*12165Sgdamore@opensolaris.org 		 */
6809484Sgarrett.damore@Sun.COM 		goto ok;
6819484Sgarrett.damore@Sun.COM 	}
6829484Sgarrett.damore@Sun.COM 
6839484Sgarrett.damore@Sun.COM 	e->e_format = ENG_FORMAT(e);
6849484Sgarrett.damore@Sun.COM 	e->e_nchan = ENG_CHANNELS(e);
6859484Sgarrett.damore@Sun.COM 	e->e_rate = ENG_RATE(e);
6869484Sgarrett.damore@Sun.COM 
687*12165Sgdamore@opensolaris.org 	/* Select format converters for the engine. */
6889484Sgarrett.damore@Sun.COM 	switch (e->e_format) {
6899484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S24_NE:
6909484Sgarrett.damore@Sun.COM 		e->e_export = auimpl_export_24ne;
6919484Sgarrett.damore@Sun.COM 		e->e_import = auimpl_import_24ne;
6929484Sgarrett.damore@Sun.COM 		sampsz = 4;
6939484Sgarrett.damore@Sun.COM 		break;
6949484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S32_NE:
6959484Sgarrett.damore@Sun.COM 		e->e_export = auimpl_export_32ne;
6969484Sgarrett.damore@Sun.COM 		e->e_import = auimpl_import_32ne;
6979484Sgarrett.damore@Sun.COM 		sampsz = 4;
6989484Sgarrett.damore@Sun.COM 		break;
6999484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S24_OE:
7009484Sgarrett.damore@Sun.COM 		e->e_export = auimpl_export_24oe;
7019484Sgarrett.damore@Sun.COM 		e->e_import = auimpl_import_24oe;
7029484Sgarrett.damore@Sun.COM 		sampsz = 4;
7039484Sgarrett.damore@Sun.COM 		break;
7049484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S32_OE:
7059484Sgarrett.damore@Sun.COM 		e->e_export = auimpl_export_32oe;
7069484Sgarrett.damore@Sun.COM 		e->e_import = auimpl_import_32oe;
7079484Sgarrett.damore@Sun.COM 		sampsz = 4;
7089484Sgarrett.damore@Sun.COM 		break;
7099484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S16_NE:
7109484Sgarrett.damore@Sun.COM 		e->e_export = auimpl_export_16ne;
7119484Sgarrett.damore@Sun.COM 		e->e_import = auimpl_import_16ne;
7129484Sgarrett.damore@Sun.COM 		sampsz = 2;
7139484Sgarrett.damore@Sun.COM 		break;
7149484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S16_OE:
7159484Sgarrett.damore@Sun.COM 		e->e_export = auimpl_export_16oe;
7169484Sgarrett.damore@Sun.COM 		e->e_import = auimpl_import_16oe;
7179484Sgarrett.damore@Sun.COM 		sampsz = 2;
7189484Sgarrett.damore@Sun.COM 		break;
7199484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_AC3:
7209484Sgarrett.damore@Sun.COM 		e->e_export = auimpl_export_24ne;
7219484Sgarrett.damore@Sun.COM 		e->e_import = auimpl_import_24ne;
7229484Sgarrett.damore@Sun.COM 		flags |= ENGINE_EXCLUSIVE;
7239484Sgarrett.damore@Sun.COM 		sampsz = 2;
7249484Sgarrett.damore@Sun.COM 		break;
7259484Sgarrett.damore@Sun.COM 	default:
7269484Sgarrett.damore@Sun.COM 		audio_dev_warn(d, "bad format");
7279484Sgarrett.damore@Sun.COM 		rv = ENOTSUP;
7289484Sgarrett.damore@Sun.COM 		goto done;
7299484Sgarrett.damore@Sun.COM 	}
7309484Sgarrett.damore@Sun.COM 
73111936Sgdamore@opensolaris.org 	fragfr = e->e_rate / audio_intrhz;
73211936Sgdamore@opensolaris.org 	if ((fragfr > AUDIO_CHBUFS) || (fragfr < 1)) {
73311936Sgdamore@opensolaris.org 		audio_dev_warn(d, "invalid fragment configration");
73411936Sgdamore@opensolaris.org 		rv = EINVAL;
73511936Sgdamore@opensolaris.org 		goto done;
73611936Sgdamore@opensolaris.org 	}
73711936Sgdamore@opensolaris.org 
738*12165Sgdamore@opensolaris.org 	/* Sanity test a few values. */
7399484Sgarrett.damore@Sun.COM 	if ((e->e_nchan < 0) || (e->e_nchan > AUDIO_MAX_CHANNELS) ||
7409484Sgarrett.damore@Sun.COM 	    (e->e_rate < 5000) || (e->e_rate > 192000)) {
7419484Sgarrett.damore@Sun.COM 		audio_dev_warn(d, "bad engine channels or rate");
7429484Sgarrett.damore@Sun.COM 		rv = EINVAL;
7439484Sgarrett.damore@Sun.COM 		goto done;
7449484Sgarrett.damore@Sun.COM 	}
7459484Sgarrett.damore@Sun.COM 
74611936Sgdamore@opensolaris.org 	if ((e->e_nframes <= (fragfr * 2)) || (e->e_data == NULL)) {
7479484Sgarrett.damore@Sun.COM 		audio_dev_warn(d, "improper engine configuration");
7489484Sgarrett.damore@Sun.COM 		rv = EINVAL;
7499484Sgarrett.damore@Sun.COM 		goto done;
7509484Sgarrett.damore@Sun.COM 	}
7519484Sgarrett.damore@Sun.COM 
7529484Sgarrett.damore@Sun.COM 	e->e_framesz = e->e_nchan * sampsz;
75311936Sgdamore@opensolaris.org 	e->e_fragfr = fragfr;
7549484Sgarrett.damore@Sun.COM 	e->e_head = 0;
7559484Sgarrett.damore@Sun.COM 	e->e_tail = 0;
7569484Sgarrett.damore@Sun.COM 	e->e_hidx = 0;
7579484Sgarrett.damore@Sun.COM 	e->e_tidx = 0;
7589484Sgarrett.damore@Sun.COM 	e->e_limiter_state = 0x10000;
75911936Sgdamore@opensolaris.org 	bzero(e->e_data, e->e_nframes * e->e_framesz);
7609484Sgarrett.damore@Sun.COM 
76111213Sgdamore@opensolaris.org 	if (e->e_ops.audio_engine_playahead == NULL) {
76211936Sgdamore@opensolaris.org 		e->e_playahead = (fragfr * 3) / 2;
76311213Sgdamore@opensolaris.org 	} else {
76411213Sgdamore@opensolaris.org 		e->e_playahead = ENG_PLAYAHEAD(e);
76511213Sgdamore@opensolaris.org 		/*
76611213Sgdamore@opensolaris.org 		 * Need to have at least a fragment plus some extra to
76711213Sgdamore@opensolaris.org 		 * avoid underruns.
76811213Sgdamore@opensolaris.org 		 */
76911936Sgdamore@opensolaris.org 		if (e->e_playahead < ((fragfr * 3) / 2)) {
77011936Sgdamore@opensolaris.org 			e->e_playahead = (fragfr * 3) / 2;
77111213Sgdamore@opensolaris.org 		}
77211213Sgdamore@opensolaris.org 
77311213Sgdamore@opensolaris.org 		/*
77411213Sgdamore@opensolaris.org 		 * Impossible to queue more frames than FIFO can hold.
77511213Sgdamore@opensolaris.org 		 */
77611213Sgdamore@opensolaris.org 		if (e->e_playahead > e->e_nframes) {
77711936Sgdamore@opensolaris.org 			e->e_playahead = (fragfr * 3) / 2;
77811213Sgdamore@opensolaris.org 		}
77911213Sgdamore@opensolaris.org 	}
78011213Sgdamore@opensolaris.org 
7819484Sgarrett.damore@Sun.COM 	for (i = 0; i < e->e_nchan; i++) {
7829484Sgarrett.damore@Sun.COM 		if (e->e_ops.audio_engine_chinfo == NULL) {
7839484Sgarrett.damore@Sun.COM 			e->e_choffs[i] = i;
7849484Sgarrett.damore@Sun.COM 			e->e_chincr[i] = e->e_nchan;
7859484Sgarrett.damore@Sun.COM 		} else {
7869484Sgarrett.damore@Sun.COM 			ENG_CHINFO(e, i, &e->e_choffs[i], &e->e_chincr[i]);
7879484Sgarrett.damore@Sun.COM 		}
7889484Sgarrett.damore@Sun.COM 	}
7899484Sgarrett.damore@Sun.COM 
790*12165Sgdamore@opensolaris.org 	e->e_flags |= flags;
7919626Sgdamore@opensolaris.org 
7929626Sgdamore@opensolaris.org 	/*
79311936Sgdamore@opensolaris.org 	 * Arrange for the engine to be started.  We defer this to the
79411936Sgdamore@opensolaris.org 	 * periodic callback, to ensure that the start happens near
79511936Sgdamore@opensolaris.org 	 * the edge of the periodic callback.  This is necessary to
79611936Sgdamore@opensolaris.org 	 * ensure that the first fragment processed is about the same
79711936Sgdamore@opensolaris.org 	 * size as the usual fragment size.  (Basically, the problem
79811936Sgdamore@opensolaris.org 	 * is that we have only 10 msec resolution with the periodic
79911936Sgdamore@opensolaris.org 	 * interface, whch is rather unfortunate.)
8009484Sgarrett.damore@Sun.COM 	 */
80111936Sgdamore@opensolaris.org 	e->e_need_start = B_TRUE;
80211936Sgdamore@opensolaris.org 
803*12165Sgdamore@opensolaris.org 	if (flags & ENGINE_OUTPUT) {
804*12165Sgdamore@opensolaris.org 		/*
805*12165Sgdamore@opensolaris.org 		 * Start the output callback to populate the engine on
806*12165Sgdamore@opensolaris.org 		 * startup.  This avoids a false underrun when we're
807*12165Sgdamore@opensolaris.org 		 * first starting up.
808*12165Sgdamore@opensolaris.org 		 */
809*12165Sgdamore@opensolaris.org 		auimpl_output_preload(e);
810*12165Sgdamore@opensolaris.org 
81111936Sgdamore@opensolaris.org 		e->e_periodic = ddi_periodic_add(auimpl_output_callback, e,
81211936Sgdamore@opensolaris.org 		    NANOSEC / audio_intrhz, audio_priority);
81311936Sgdamore@opensolaris.org 	} else {
81411936Sgdamore@opensolaris.org 		e->e_periodic = ddi_periodic_add(auimpl_input_callback, e,
81511936Sgdamore@opensolaris.org 		    NANOSEC / audio_intrhz, audio_priority);
8169484Sgarrett.damore@Sun.COM 	}
8179484Sgarrett.damore@Sun.COM 
8189484Sgarrett.damore@Sun.COM ok:
8199484Sgarrett.damore@Sun.COM 	sp->s_phys_parms->p_rate = e->e_rate;
8209484Sgarrett.damore@Sun.COM 	sp->s_phys_parms->p_nchan = e->e_nchan;
8219484Sgarrett.damore@Sun.COM 
822*12165Sgdamore@opensolaris.org 	/* Configure the engine. */
823*12165Sgdamore@opensolaris.org 	mutex_enter(&sp->s_lock);
824*12165Sgdamore@opensolaris.org 	rv = auimpl_format_setup(sp, parms, mask);
825*12165Sgdamore@opensolaris.org 	mutex_exit(&sp->s_lock);
8269484Sgarrett.damore@Sun.COM 
8279484Sgarrett.damore@Sun.COM done:
8289484Sgarrett.damore@Sun.COM 	mutex_exit(&e->e_lock);
8299484Sgarrett.damore@Sun.COM 	mutex_exit(&d->d_lock);
830*12165Sgdamore@opensolaris.org 
8319484Sgarrett.damore@Sun.COM 	return (rv);
8329484Sgarrett.damore@Sun.COM }
8339484Sgarrett.damore@Sun.COM 
8349484Sgarrett.damore@Sun.COM void
auimpl_engine_close(audio_stream_t * sp)8359484Sgarrett.damore@Sun.COM auimpl_engine_close(audio_stream_t *sp)
8369484Sgarrett.damore@Sun.COM {
8379484Sgarrett.damore@Sun.COM 	audio_engine_t	*e = sp->s_engine;
8389484Sgarrett.damore@Sun.COM 	audio_dev_t	*d;
8399484Sgarrett.damore@Sun.COM 
8409484Sgarrett.damore@Sun.COM 	if (e == NULL)
8419484Sgarrett.damore@Sun.COM 		return;
8429484Sgarrett.damore@Sun.COM 
8439484Sgarrett.damore@Sun.COM 	d = e->e_dev;
8449484Sgarrett.damore@Sun.COM 
8459484Sgarrett.damore@Sun.COM 	mutex_enter(&d->d_lock);
84611936Sgdamore@opensolaris.org 	while (d->d_suspended) {
84711936Sgdamore@opensolaris.org 		cv_wait(&d->d_ctrl_cv, &d->d_lock);
84811936Sgdamore@opensolaris.org 	}
849*12165Sgdamore@opensolaris.org 
8509484Sgarrett.damore@Sun.COM 	mutex_enter(&e->e_lock);
8519484Sgarrett.damore@Sun.COM 	sp->s_engine = NULL;
8529484Sgarrett.damore@Sun.COM 	list_remove(&e->e_streams, sp);
8539484Sgarrett.damore@Sun.COM 	if (list_is_empty(&e->e_streams)) {
85411936Sgdamore@opensolaris.org 		ENG_STOP(e);
855*12165Sgdamore@opensolaris.org 		ddi_periodic_delete(e->e_periodic);
856*12165Sgdamore@opensolaris.org 		e->e_periodic = 0;
8579484Sgarrett.damore@Sun.COM 		e->e_flags &= ENGINE_DRIVER_FLAGS;
8589484Sgarrett.damore@Sun.COM 		ENG_CLOSE(e);
8599484Sgarrett.damore@Sun.COM 	}
8609484Sgarrett.damore@Sun.COM 	mutex_exit(&e->e_lock);
861*12165Sgdamore@opensolaris.org 
8629484Sgarrett.damore@Sun.COM 	cv_broadcast(&d->d_cv);
8639484Sgarrett.damore@Sun.COM 	mutex_exit(&d->d_lock);
8649484Sgarrett.damore@Sun.COM }
8659484Sgarrett.damore@Sun.COM 
8669484Sgarrett.damore@Sun.COM int
audio_dev_register(audio_dev_t * d)8679484Sgarrett.damore@Sun.COM audio_dev_register(audio_dev_t *d)
8689484Sgarrett.damore@Sun.COM {
8699484Sgarrett.damore@Sun.COM 	list_t *l;
8709484Sgarrett.damore@Sun.COM 	audio_dev_t *srch;
8719484Sgarrett.damore@Sun.COM 	int start;
8729484Sgarrett.damore@Sun.COM 
8739484Sgarrett.damore@Sun.COM 	/*
8749484Sgarrett.damore@Sun.COM 	 * Make sure we don't automatically unload.  This prevents
8759484Sgarrett.damore@Sun.COM 	 * loss of hardware settings when no audio clients are
8769484Sgarrett.damore@Sun.COM 	 * running.
8779484Sgarrett.damore@Sun.COM 	 */
8789484Sgarrett.damore@Sun.COM 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, d->d_dip,
8799484Sgarrett.damore@Sun.COM 	    DDI_NO_AUTODETACH, 1);
8809484Sgarrett.damore@Sun.COM 
8819484Sgarrett.damore@Sun.COM 	/*
8829484Sgarrett.damore@Sun.COM 	 * This does an in-order insertion, finding the first available
8839484Sgarrett.damore@Sun.COM 	 * free index.  "Special" devices (ones without any actual engines)
8849484Sgarrett.damore@Sun.COM 	 * are all numbered 0.  There should only be one of them anyway.
8859484Sgarrett.damore@Sun.COM 	 * All others start at one.
8869484Sgarrett.damore@Sun.COM 	 */
8879484Sgarrett.damore@Sun.COM 	if (d->d_flags & DEV_SNDSTAT_CAP) {
8889484Sgarrett.damore@Sun.COM 		start = 0;
8899484Sgarrett.damore@Sun.COM 	} else {
8909484Sgarrett.damore@Sun.COM 		start = 1;
8919484Sgarrett.damore@Sun.COM 	}
8929484Sgarrett.damore@Sun.COM 	d->d_index = start;
89311936Sgdamore@opensolaris.org 
8949484Sgarrett.damore@Sun.COM 	rw_enter(&auimpl_dev_lock, RW_WRITER);
8959484Sgarrett.damore@Sun.COM 	l = &auimpl_devs_by_index;
8969484Sgarrett.damore@Sun.COM 	for (srch = list_head(l); srch; srch = list_next(l, srch)) {
8979484Sgarrett.damore@Sun.COM 		/* skip over special nodes */
8989484Sgarrett.damore@Sun.COM 		if (srch->d_index < start)
8999484Sgarrett.damore@Sun.COM 			continue;
9009484Sgarrett.damore@Sun.COM 		if (srch->d_index > d->d_index) {
9019484Sgarrett.damore@Sun.COM 			/* found a free spot! */
9029484Sgarrett.damore@Sun.COM 			break;
9039484Sgarrett.damore@Sun.COM 		}
9049484Sgarrett.damore@Sun.COM 		d->d_index++;
9059484Sgarrett.damore@Sun.COM 	}
9069484Sgarrett.damore@Sun.COM 	/*
9079484Sgarrett.damore@Sun.COM 	 * NB: If srch is NULL, then list_insert_before puts
9089484Sgarrett.damore@Sun.COM 	 * it on the tail of the list.  So if we didn't find a
9099484Sgarrett.damore@Sun.COM 	 * hole, then that's where we want it.
9109484Sgarrett.damore@Sun.COM 	 */
9119484Sgarrett.damore@Sun.COM 	list_insert_before(l, srch, d);
9129484Sgarrett.damore@Sun.COM 
9139484Sgarrett.damore@Sun.COM 	/* insert in order by number */
9149484Sgarrett.damore@Sun.COM 	l = &auimpl_devs_by_number;
9159484Sgarrett.damore@Sun.COM 	for (srch = list_head(l); srch; srch = list_next(l, srch)) {
9169484Sgarrett.damore@Sun.COM 		if (srch->d_number >= d->d_number) {
9179484Sgarrett.damore@Sun.COM 			break;
9189484Sgarrett.damore@Sun.COM 		}
9199484Sgarrett.damore@Sun.COM 	}
9209484Sgarrett.damore@Sun.COM 	list_insert_before(l, srch, d);
9219484Sgarrett.damore@Sun.COM 
9229484Sgarrett.damore@Sun.COM 	rw_exit(&auimpl_dev_lock);
9239484Sgarrett.damore@Sun.COM 
9249484Sgarrett.damore@Sun.COM 	if (auimpl_create_minors(d) != 0) {
9259484Sgarrett.damore@Sun.COM 		rw_enter(&auimpl_dev_lock, RW_WRITER);
9269484Sgarrett.damore@Sun.COM 		auimpl_remove_minors(d);
9279484Sgarrett.damore@Sun.COM 		list_remove(&auimpl_devs_by_index, d);
9289484Sgarrett.damore@Sun.COM 		list_remove(&auimpl_devs_by_number, d);
9299484Sgarrett.damore@Sun.COM 		rw_exit(&auimpl_dev_lock);
9309484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
9319484Sgarrett.damore@Sun.COM 	}
9329484Sgarrett.damore@Sun.COM 
9339484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
9349484Sgarrett.damore@Sun.COM }
9359484Sgarrett.damore@Sun.COM 
9369484Sgarrett.damore@Sun.COM int
audio_dev_unregister(audio_dev_t * d)9379484Sgarrett.damore@Sun.COM audio_dev_unregister(audio_dev_t *d)
9389484Sgarrett.damore@Sun.COM {
9399484Sgarrett.damore@Sun.COM 	rw_enter(&auimpl_dev_lock, RW_WRITER);
9409484Sgarrett.damore@Sun.COM 
9419484Sgarrett.damore@Sun.COM 	mutex_enter(&d->d_lock);
9429484Sgarrett.damore@Sun.COM 	/* if we are still in use, we can't unregister */
9439484Sgarrett.damore@Sun.COM 	if (d->d_refcnt) {
9449484Sgarrett.damore@Sun.COM 		mutex_exit(&d->d_lock);
9459484Sgarrett.damore@Sun.COM 		rw_exit(&auimpl_dev_lock);
9469484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
9479484Sgarrett.damore@Sun.COM 	}
9489484Sgarrett.damore@Sun.COM 	auimpl_remove_minors(d);
9499484Sgarrett.damore@Sun.COM 	list_remove(&auimpl_devs_by_index, d);
9509484Sgarrett.damore@Sun.COM 	list_remove(&auimpl_devs_by_number, d);
9519484Sgarrett.damore@Sun.COM 	mutex_exit(&d->d_lock);
9529484Sgarrett.damore@Sun.COM 
9539484Sgarrett.damore@Sun.COM 	rw_exit(&auimpl_dev_lock);
9549484Sgarrett.damore@Sun.COM 
9559484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
9569484Sgarrett.damore@Sun.COM }
9579484Sgarrett.damore@Sun.COM 
9589484Sgarrett.damore@Sun.COM static int
auimpl_engine_ksupdate(kstat_t * ksp,int rw)9599484Sgarrett.damore@Sun.COM auimpl_engine_ksupdate(kstat_t *ksp, int rw)
9609484Sgarrett.damore@Sun.COM {
9619484Sgarrett.damore@Sun.COM 	audio_engine_t *e = ksp->ks_private;
9629484Sgarrett.damore@Sun.COM 	struct audio_stats *st = &e->e_stats;
9639484Sgarrett.damore@Sun.COM 
9649484Sgarrett.damore@Sun.COM 	if (rw == KSTAT_WRITE) {
9659484Sgarrett.damore@Sun.COM 		return (EACCES);
9669484Sgarrett.damore@Sun.COM 	}
9679484Sgarrett.damore@Sun.COM 
9689484Sgarrett.damore@Sun.COM 	mutex_enter(&e->e_lock);
9699484Sgarrett.damore@Sun.COM 	st->st_head.value.ui64 = e->e_head;
9709484Sgarrett.damore@Sun.COM 	st->st_tail.value.ui64 = e->e_tail;
9719484Sgarrett.damore@Sun.COM 	st->st_flags.value.ui32 = e->e_flags;
97211936Sgdamore@opensolaris.org 	st->st_nbytes.value.ui32 = e->e_framesz * e->e_nframes;
9739484Sgarrett.damore@Sun.COM 	st->st_framesz.value.ui32 = e->e_framesz;
9749484Sgarrett.damore@Sun.COM 	st->st_hidx.value.ui32 = e->e_hidx;
9759484Sgarrett.damore@Sun.COM 	st->st_tidx.value.ui32 = e->e_tidx;
9769484Sgarrett.damore@Sun.COM 	st->st_format.value.ui32 = e->e_format;
9779484Sgarrett.damore@Sun.COM 	st->st_nchan.value.ui32 = e->e_nchan;
9789484Sgarrett.damore@Sun.COM 	st->st_rate.value.ui32 = e->e_rate;
9799484Sgarrett.damore@Sun.COM 	st->st_errors.value.ui32 = e->e_errors;
98010157Sgdamore@opensolaris.org 	st->st_engine_underruns.value.ui32 = e->e_underruns;
98110157Sgdamore@opensolaris.org 	st->st_engine_overruns.value.ui32 = e->e_overruns;
98210157Sgdamore@opensolaris.org 	st->st_stream_underruns.value.ui32 = e->e_stream_underruns;
98310157Sgdamore@opensolaris.org 	st->st_stream_overruns.value.ui32 = e->e_stream_overruns;
9849484Sgarrett.damore@Sun.COM 	st->st_suspended.value.ui32 = e->e_suspended;
98511936Sgdamore@opensolaris.org 	st->st_failed.value.ui32 = e->e_failed;
98611936Sgdamore@opensolaris.org 	st->st_playahead.value.ui32 = e->e_playahead;
9879484Sgarrett.damore@Sun.COM 	mutex_exit(&e->e_lock);
9889484Sgarrett.damore@Sun.COM 
9899484Sgarrett.damore@Sun.COM 	return (0);
9909484Sgarrett.damore@Sun.COM }
9919484Sgarrett.damore@Sun.COM 
9929484Sgarrett.damore@Sun.COM static void
auimpl_engine_ksinit(audio_dev_t * d,audio_engine_t * e)9939484Sgarrett.damore@Sun.COM auimpl_engine_ksinit(audio_dev_t *d, audio_engine_t *e)
9949484Sgarrett.damore@Sun.COM {
9959484Sgarrett.damore@Sun.COM 	char			name[32];
9969484Sgarrett.damore@Sun.COM 	struct audio_stats	*st;
9979484Sgarrett.damore@Sun.COM 
9989484Sgarrett.damore@Sun.COM 	(void) snprintf(name, sizeof (name), "engine_%d", e->e_num);
9999484Sgarrett.damore@Sun.COM 
10009484Sgarrett.damore@Sun.COM 	e->e_ksp = kstat_create(ddi_driver_name(d->d_dip), d->d_instance,
10019484Sgarrett.damore@Sun.COM 	    name, "misc", KSTAT_TYPE_NAMED,
10029484Sgarrett.damore@Sun.COM 	    sizeof (struct audio_stats) / sizeof (kstat_named_t), 0);
10039484Sgarrett.damore@Sun.COM 
10049484Sgarrett.damore@Sun.COM 	if (e->e_ksp == NULL) {
10059484Sgarrett.damore@Sun.COM 		audio_dev_warn(d, "unable to initialize kstats");
10069484Sgarrett.damore@Sun.COM 		return;
10079484Sgarrett.damore@Sun.COM 	}
10089484Sgarrett.damore@Sun.COM 
10099484Sgarrett.damore@Sun.COM 	st = &e->e_stats;
10109484Sgarrett.damore@Sun.COM 	e->e_ksp->ks_data = st;
10119484Sgarrett.damore@Sun.COM 	e->e_ksp->ks_private = e;
10129484Sgarrett.damore@Sun.COM 	e->e_ksp->ks_lock = NULL;
10139484Sgarrett.damore@Sun.COM 	e->e_ksp->ks_update = auimpl_engine_ksupdate;
10149484Sgarrett.damore@Sun.COM 	kstat_named_init(&st->st_head, "head", KSTAT_DATA_UINT64);
10159484Sgarrett.damore@Sun.COM 	kstat_named_init(&st->st_tail, "tail", KSTAT_DATA_UINT64);
10169484Sgarrett.damore@Sun.COM 	kstat_named_init(&st->st_flags, "flags", KSTAT_DATA_UINT32);
101711936Sgdamore@opensolaris.org 	kstat_named_init(&st->st_nbytes, "nbytes", KSTAT_DATA_UINT32);
10189484Sgarrett.damore@Sun.COM 	kstat_named_init(&st->st_framesz, "framesz", KSTAT_DATA_UINT32);
10199484Sgarrett.damore@Sun.COM 	kstat_named_init(&st->st_hidx, "hidx", KSTAT_DATA_UINT32);
10209484Sgarrett.damore@Sun.COM 	kstat_named_init(&st->st_tidx, "tidx", KSTAT_DATA_UINT32);
10219484Sgarrett.damore@Sun.COM 	kstat_named_init(&st->st_format, "format", KSTAT_DATA_UINT32);
10229484Sgarrett.damore@Sun.COM 	kstat_named_init(&st->st_nchan, "channels", KSTAT_DATA_UINT32);
10239484Sgarrett.damore@Sun.COM 	kstat_named_init(&st->st_rate, "rate", KSTAT_DATA_UINT32);
10249484Sgarrett.damore@Sun.COM 	kstat_named_init(&st->st_errors, "errors", KSTAT_DATA_UINT32);
102510157Sgdamore@opensolaris.org 	kstat_named_init(&st->st_engine_overruns, "engine_overruns",
102610157Sgdamore@opensolaris.org 	    KSTAT_DATA_UINT32);
102710157Sgdamore@opensolaris.org 	kstat_named_init(&st->st_engine_underruns, "engine_underruns",
102810157Sgdamore@opensolaris.org 	    KSTAT_DATA_UINT32);
102910157Sgdamore@opensolaris.org 	kstat_named_init(&st->st_stream_overruns, "stream_overruns",
103010157Sgdamore@opensolaris.org 	    KSTAT_DATA_UINT32);
103110157Sgdamore@opensolaris.org 	kstat_named_init(&st->st_stream_underruns, "stream_underruns",
103210157Sgdamore@opensolaris.org 	    KSTAT_DATA_UINT32);
103311936Sgdamore@opensolaris.org 	kstat_named_init(&st->st_playahead, "playahead", KSTAT_DATA_UINT32);
10349484Sgarrett.damore@Sun.COM 	kstat_named_init(&st->st_suspended, "suspended", KSTAT_DATA_UINT32);
103511936Sgdamore@opensolaris.org 	kstat_named_init(&st->st_failed, "failed", KSTAT_DATA_UINT32);
10369484Sgarrett.damore@Sun.COM 	kstat_install(e->e_ksp);
10379484Sgarrett.damore@Sun.COM }
10389484Sgarrett.damore@Sun.COM 
10399484Sgarrett.damore@Sun.COM void
audio_dev_add_engine(audio_dev_t * d,audio_engine_t * e)10409484Sgarrett.damore@Sun.COM audio_dev_add_engine(audio_dev_t *d, audio_engine_t *e)
10419484Sgarrett.damore@Sun.COM {
104211936Sgdamore@opensolaris.org 	mutex_enter(&d->d_lock);
10439484Sgarrett.damore@Sun.COM 
104411936Sgdamore@opensolaris.org 	e->e_num = d->d_engno++;
10459484Sgarrett.damore@Sun.COM 
10469484Sgarrett.damore@Sun.COM 	auimpl_engine_ksinit(d, e);
10479484Sgarrett.damore@Sun.COM 
10489484Sgarrett.damore@Sun.COM 	/* check for duplex */
10499484Sgarrett.damore@Sun.COM 	if ((e->e_flags & ENGINE_OUTPUT_CAP) && (d->d_flags & DEV_INPUT_CAP)) {
10509484Sgarrett.damore@Sun.COM 		d->d_flags |= DEV_DUPLEX_CAP;
10519484Sgarrett.damore@Sun.COM 	}
10529484Sgarrett.damore@Sun.COM 	if ((e->e_flags & ENGINE_INPUT_CAP) && (d->d_flags & DEV_OUTPUT_CAP)) {
10539484Sgarrett.damore@Sun.COM 		d->d_flags |= DEV_DUPLEX_CAP;
10549484Sgarrett.damore@Sun.COM 	}
10559484Sgarrett.damore@Sun.COM 	/* add in the direction caps -- must be done after duplex above */
10569484Sgarrett.damore@Sun.COM 	if (e->e_flags & ENGINE_OUTPUT_CAP) {
10579484Sgarrett.damore@Sun.COM 		d->d_flags |= DEV_OUTPUT_CAP;
10589484Sgarrett.damore@Sun.COM 	}
10599484Sgarrett.damore@Sun.COM 	if (e->e_flags & ENGINE_INPUT_CAP) {
10609484Sgarrett.damore@Sun.COM 		d->d_flags |= DEV_INPUT_CAP;
10619484Sgarrett.damore@Sun.COM 	}
10629484Sgarrett.damore@Sun.COM 
10639484Sgarrett.damore@Sun.COM 	list_insert_tail(&d->d_engines, e);
10649484Sgarrett.damore@Sun.COM 	e->e_dev = d;
10659484Sgarrett.damore@Sun.COM 	mutex_exit(&d->d_lock);
10669484Sgarrett.damore@Sun.COM }
10679484Sgarrett.damore@Sun.COM 
10689484Sgarrett.damore@Sun.COM void
audio_dev_remove_engine(audio_dev_t * d,audio_engine_t * e)10699484Sgarrett.damore@Sun.COM audio_dev_remove_engine(audio_dev_t *d, audio_engine_t *e)
10709484Sgarrett.damore@Sun.COM {
10719484Sgarrett.damore@Sun.COM 	mutex_enter(&d->d_lock);
10729484Sgarrett.damore@Sun.COM 	list_remove(&d->d_engines, e);
10739484Sgarrett.damore@Sun.COM 	e->e_dev = NULL;
10749484Sgarrett.damore@Sun.COM 	if (e->e_ksp)
10759484Sgarrett.damore@Sun.COM 		kstat_delete(e->e_ksp);
10769484Sgarrett.damore@Sun.COM 	e->e_ksp = NULL;
10779484Sgarrett.damore@Sun.COM 	mutex_exit(&d->d_lock);
10789484Sgarrett.damore@Sun.COM }
10799484Sgarrett.damore@Sun.COM 
10809484Sgarrett.damore@Sun.COM /*
10819484Sgarrett.damore@Sun.COM  * Change the number.
10829484Sgarrett.damore@Sun.COM  */
10839484Sgarrett.damore@Sun.COM void
auclnt_set_dev_number(audio_dev_t * d,int num)10849484Sgarrett.damore@Sun.COM auclnt_set_dev_number(audio_dev_t *d, int num)
10859484Sgarrett.damore@Sun.COM {
10869484Sgarrett.damore@Sun.COM 	list_t		*l = &auimpl_devs_by_number;
10879484Sgarrett.damore@Sun.COM 	audio_dev_t	*srch;
10889484Sgarrett.damore@Sun.COM 
10899484Sgarrett.damore@Sun.COM 	/* reorder our list */
10909484Sgarrett.damore@Sun.COM 	rw_enter(&auimpl_dev_lock, RW_WRITER);
10919484Sgarrett.damore@Sun.COM 	d->d_number = num;
10929484Sgarrett.damore@Sun.COM 	list_remove(l, d);
10939484Sgarrett.damore@Sun.COM 	for (srch = list_head(l); srch; srch = list_next(l, srch)) {
10949484Sgarrett.damore@Sun.COM 		if (srch->d_number >= d->d_number) {
10959484Sgarrett.damore@Sun.COM 			break;
10969484Sgarrett.damore@Sun.COM 		}
10979484Sgarrett.damore@Sun.COM 	}
10989484Sgarrett.damore@Sun.COM 	list_insert_before(l, srch, d);
10999484Sgarrett.damore@Sun.COM 
11009484Sgarrett.damore@Sun.COM 	rw_exit(&auimpl_dev_lock);
11019484Sgarrett.damore@Sun.COM }
11029484Sgarrett.damore@Sun.COM 
11039484Sgarrett.damore@Sun.COM void
auclnt_walk_devs(int (* walker)(audio_dev_t *,void *),void * arg)11049484Sgarrett.damore@Sun.COM auclnt_walk_devs(int (*walker)(audio_dev_t *, void *), void *arg)
11059484Sgarrett.damore@Sun.COM {
11069484Sgarrett.damore@Sun.COM 	audio_dev_t	*d;
11079484Sgarrett.damore@Sun.COM 	boolean_t	cont;
11089484Sgarrett.damore@Sun.COM 	list_t		*l;
11099484Sgarrett.damore@Sun.COM 
11109484Sgarrett.damore@Sun.COM 	l = &auimpl_devs_by_index;
11119484Sgarrett.damore@Sun.COM 	rw_enter(&auimpl_dev_lock, RW_READER);
11129484Sgarrett.damore@Sun.COM 	for (d = list_head(l); d; d = list_next(l, d)) {
11139484Sgarrett.damore@Sun.COM 		cont = walker(d, arg);
11149484Sgarrett.damore@Sun.COM 		if (cont == AUDIO_WALK_STOP)
11159484Sgarrett.damore@Sun.COM 			break;
11169484Sgarrett.damore@Sun.COM 	}
11179484Sgarrett.damore@Sun.COM 	rw_exit(&auimpl_dev_lock);
11189484Sgarrett.damore@Sun.COM }
11199484Sgarrett.damore@Sun.COM 
11209484Sgarrett.damore@Sun.COM void
auclnt_walk_devs_by_number(int (* walker)(audio_dev_t *,void *),void * arg)11219484Sgarrett.damore@Sun.COM auclnt_walk_devs_by_number(int (*walker)(audio_dev_t *, void *), void *arg)
11229484Sgarrett.damore@Sun.COM {
11239484Sgarrett.damore@Sun.COM 	audio_dev_t	*d;
11249484Sgarrett.damore@Sun.COM 	boolean_t	cont;
11259484Sgarrett.damore@Sun.COM 	list_t		*l;
11269484Sgarrett.damore@Sun.COM 
11279484Sgarrett.damore@Sun.COM 	l = &auimpl_devs_by_number;
11289484Sgarrett.damore@Sun.COM 	rw_enter(&auimpl_dev_lock, RW_READER);
11299484Sgarrett.damore@Sun.COM 	for (d = list_head(l); d; d = list_next(l, d)) {
11309484Sgarrett.damore@Sun.COM 		cont = walker(d, arg);
11319484Sgarrett.damore@Sun.COM 		if (cont == AUDIO_WALK_STOP)
11329484Sgarrett.damore@Sun.COM 			break;
11339484Sgarrett.damore@Sun.COM 	}
11349484Sgarrett.damore@Sun.COM 	rw_exit(&auimpl_dev_lock);
11359484Sgarrett.damore@Sun.COM }
11369484Sgarrett.damore@Sun.COM 
11379484Sgarrett.damore@Sun.COM void
auclnt_dev_walk_engines(audio_dev_t * d,int (* walker)(audio_engine_t *,void *),void * arg)11389484Sgarrett.damore@Sun.COM auclnt_dev_walk_engines(audio_dev_t *d,
11399484Sgarrett.damore@Sun.COM     int (*walker)(audio_engine_t *, void *),
11409484Sgarrett.damore@Sun.COM     void *arg)
11419484Sgarrett.damore@Sun.COM {
11429484Sgarrett.damore@Sun.COM 	audio_engine_t *e;
11439484Sgarrett.damore@Sun.COM 	list_t *l = &d->d_engines;
11449484Sgarrett.damore@Sun.COM 
11459484Sgarrett.damore@Sun.COM 	mutex_enter(&d->d_lock);
11469484Sgarrett.damore@Sun.COM 	for (e = list_head(l); e != NULL; e = list_next(l, e)) {
11479484Sgarrett.damore@Sun.COM 		if (walker(e, arg) == AUDIO_WALK_STOP) {
11489484Sgarrett.damore@Sun.COM 			break;
11499484Sgarrett.damore@Sun.COM 		}
11509484Sgarrett.damore@Sun.COM 	}
11519484Sgarrett.damore@Sun.COM 	mutex_exit(&d->d_lock);
11529484Sgarrett.damore@Sun.COM }
11539484Sgarrett.damore@Sun.COM 
11549484Sgarrett.damore@Sun.COM int
auclnt_engine_get_format(audio_engine_t * e)11559484Sgarrett.damore@Sun.COM auclnt_engine_get_format(audio_engine_t *e)
11569484Sgarrett.damore@Sun.COM {
11579484Sgarrett.damore@Sun.COM 	return (ENG_FORMAT(e));
11589484Sgarrett.damore@Sun.COM }
11599484Sgarrett.damore@Sun.COM 
11609484Sgarrett.damore@Sun.COM int
auclnt_engine_get_channels(audio_engine_t * e)11619484Sgarrett.damore@Sun.COM auclnt_engine_get_channels(audio_engine_t *e)
11629484Sgarrett.damore@Sun.COM {
11639484Sgarrett.damore@Sun.COM 	return (ENG_CHANNELS(e));
11649484Sgarrett.damore@Sun.COM }
11659484Sgarrett.damore@Sun.COM 
11669484Sgarrett.damore@Sun.COM int
auclnt_engine_get_rate(audio_engine_t * e)11679484Sgarrett.damore@Sun.COM auclnt_engine_get_rate(audio_engine_t *e)
11689484Sgarrett.damore@Sun.COM {
11699484Sgarrett.damore@Sun.COM 	return (ENG_RATE(e));
11709484Sgarrett.damore@Sun.COM }
11719484Sgarrett.damore@Sun.COM 
117211936Sgdamore@opensolaris.org uint_t
auclnt_engine_get_capab(audio_engine_t * e)11739484Sgarrett.damore@Sun.COM auclnt_engine_get_capab(audio_engine_t *e)
11749484Sgarrett.damore@Sun.COM {
117511936Sgdamore@opensolaris.org 	uint_t capab = 0;
11769484Sgarrett.damore@Sun.COM 
11779484Sgarrett.damore@Sun.COM 	if (e->e_flags & ENGINE_INPUT_CAP) {
11789484Sgarrett.damore@Sun.COM 		capab |= AUDIO_CLIENT_CAP_RECORD;
11799484Sgarrett.damore@Sun.COM 	}
11809484Sgarrett.damore@Sun.COM 	if (e->e_flags & ENGINE_OUTPUT_CAP) {
11819484Sgarrett.damore@Sun.COM 		capab |= AUDIO_CLIENT_CAP_PLAY;
11829484Sgarrett.damore@Sun.COM 	}
11839484Sgarrett.damore@Sun.COM 	return (capab);
11849484Sgarrett.damore@Sun.COM }
11859484Sgarrett.damore@Sun.COM 
11869484Sgarrett.damore@Sun.COM /*
11879484Sgarrett.damore@Sun.COM  * This function suspends an engine.  The intent is to pause the
11889484Sgarrett.damore@Sun.COM  * engine temporarily so that it does not underrun while user threads
11899484Sgarrett.damore@Sun.COM  * are suspended.  The driver is still responsible for actually doing
11909484Sgarrett.damore@Sun.COM  * the driver suspend work -- all this does is put the engine in a
11919484Sgarrett.damore@Sun.COM  * paused state.  It does not prevent, for example, threads from
11929484Sgarrett.damore@Sun.COM  * accessing the hardware.
11939484Sgarrett.damore@Sun.COM  *
11949484Sgarrett.damore@Sun.COM  * A properly implemented driver won't even be aware of the existence
11959484Sgarrett.damore@Sun.COM  * of this routine -- the driver will just handle the suspend &
11969484Sgarrett.damore@Sun.COM  * resume.  At the point of suspend & resume, the driver will see that
11979484Sgarrett.damore@Sun.COM  * the engines are not running (as if all threads had "paused" it).
11989484Sgarrett.damore@Sun.COM  *
11999484Sgarrett.damore@Sun.COM  * Failure to execute either of the routines below is not critical,
12009484Sgarrett.damore@Sun.COM  * but will probably lead to underruns and overflows as the kernel
12019484Sgarrett.damore@Sun.COM  * driver gets resumed well in advance of the time when user threads
12029484Sgarrett.damore@Sun.COM  * are ready to start operation.
12039484Sgarrett.damore@Sun.COM  */
120411936Sgdamore@opensolaris.org static void
auimpl_engine_suspend(audio_engine_t * e)120511936Sgdamore@opensolaris.org auimpl_engine_suspend(audio_engine_t *e)
120611936Sgdamore@opensolaris.org {
120711936Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&e->e_lock));
120811936Sgdamore@opensolaris.org 
120911936Sgdamore@opensolaris.org 	if (e->e_failed || e->e_suspended) {
121011936Sgdamore@opensolaris.org 		e->e_suspended = B_TRUE;
121111936Sgdamore@opensolaris.org 		return;
121211936Sgdamore@opensolaris.org 	}
121311936Sgdamore@opensolaris.org 	e->e_suspended = B_TRUE;
121411936Sgdamore@opensolaris.org 	if (e->e_flags & ENGINE_INPUT) {
121511936Sgdamore@opensolaris.org 		e->e_head = ENG_COUNT(e);
121611936Sgdamore@opensolaris.org 		ENG_STOP(e);
121711936Sgdamore@opensolaris.org 	}
121811936Sgdamore@opensolaris.org 	if (e->e_flags & ENGINE_OUTPUT) {
121911936Sgdamore@opensolaris.org 		e->e_tail = ENG_COUNT(e);
122011936Sgdamore@opensolaris.org 		ENG_STOP(e);
122111936Sgdamore@opensolaris.org 	}
122211936Sgdamore@opensolaris.org }
122311936Sgdamore@opensolaris.org 
122411936Sgdamore@opensolaris.org static void
auimpl_engine_resume(audio_engine_t * e)122511936Sgdamore@opensolaris.org auimpl_engine_resume(audio_engine_t *e)
122611936Sgdamore@opensolaris.org {
122711936Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&e->e_lock));
122811936Sgdamore@opensolaris.org 	ASSERT(e->e_suspended);
122911936Sgdamore@opensolaris.org 
123011936Sgdamore@opensolaris.org 	if (e->e_failed) {
123111936Sgdamore@opensolaris.org 		/* No longer suspended, but still failed! */
123211936Sgdamore@opensolaris.org 		e->e_suspended = B_FALSE;
123311936Sgdamore@opensolaris.org 		return;
123411936Sgdamore@opensolaris.org 	}
123511936Sgdamore@opensolaris.org 
123611936Sgdamore@opensolaris.org 	if (e->e_flags & (ENGINE_INPUT | ENGINE_OUTPUT)) {
123711936Sgdamore@opensolaris.org 
123811936Sgdamore@opensolaris.org 		auimpl_engine_reset(e);
123911936Sgdamore@opensolaris.org 
124011936Sgdamore@opensolaris.org 		if (e->e_flags & ENGINE_OUTPUT) {
124111936Sgdamore@opensolaris.org 			auimpl_output_preload(e);
124211936Sgdamore@opensolaris.org 		}
124311936Sgdamore@opensolaris.org 
124411936Sgdamore@opensolaris.org 		e->e_need_start = B_TRUE;
124511936Sgdamore@opensolaris.org 	}
124611936Sgdamore@opensolaris.org 	e->e_suspended = B_FALSE;
124711936Sgdamore@opensolaris.org 	cv_broadcast(&e->e_cv);
124811936Sgdamore@opensolaris.org }
124911936Sgdamore@opensolaris.org 
12509484Sgarrett.damore@Sun.COM static int
auimpl_dev_suspend(audio_dev_t * d,void * dontcare)125111936Sgdamore@opensolaris.org auimpl_dev_suspend(audio_dev_t *d, void *dontcare)
12529484Sgarrett.damore@Sun.COM {
125311936Sgdamore@opensolaris.org 	list_t		*l;
125411936Sgdamore@opensolaris.org 	audio_engine_t	*e;
125511936Sgdamore@opensolaris.org 
12569484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(dontcare));
12579484Sgarrett.damore@Sun.COM 
125811936Sgdamore@opensolaris.org 	mutex_enter(&d->d_lock);
125911936Sgdamore@opensolaris.org 	mutex_enter(&d->d_ctrl_lock);
126011936Sgdamore@opensolaris.org 	if (d->d_suspended) {
126111936Sgdamore@opensolaris.org 		d->d_suspended++;
126211936Sgdamore@opensolaris.org 		mutex_exit(&d->d_ctrl_lock);
126311936Sgdamore@opensolaris.org 		mutex_exit(&d->d_lock);
126411936Sgdamore@opensolaris.org 		return (AUDIO_WALK_CONTINUE);
126511936Sgdamore@opensolaris.org 	}
126611936Sgdamore@opensolaris.org 
126711936Sgdamore@opensolaris.org 	d->d_suspended++;
126811936Sgdamore@opensolaris.org 
126911936Sgdamore@opensolaris.org 	(void) auimpl_save_controls(d);
127011936Sgdamore@opensolaris.org 	mutex_exit(&d->d_ctrl_lock);
127111936Sgdamore@opensolaris.org 
127211936Sgdamore@opensolaris.org 	l = &d->d_engines;
127311936Sgdamore@opensolaris.org 	for (e = list_head(l); e != NULL; e = list_next(l, e)) {
127411936Sgdamore@opensolaris.org 		mutex_enter(&e->e_lock);
127511936Sgdamore@opensolaris.org 		auimpl_engine_suspend(e);
127611936Sgdamore@opensolaris.org 		mutex_exit(&e->e_lock);
127711936Sgdamore@opensolaris.org 	}
127811936Sgdamore@opensolaris.org 	mutex_exit(&d->d_lock);
12799484Sgarrett.damore@Sun.COM 
12809484Sgarrett.damore@Sun.COM 	return (AUDIO_WALK_CONTINUE);
12819484Sgarrett.damore@Sun.COM }
12829484Sgarrett.damore@Sun.COM 
12839484Sgarrett.damore@Sun.COM static int
auimpl_dev_resume(audio_dev_t * d,void * dontcare)128411936Sgdamore@opensolaris.org auimpl_dev_resume(audio_dev_t *d, void *dontcare)
12859484Sgarrett.damore@Sun.COM {
128611936Sgdamore@opensolaris.org 	list_t		*l;
128711936Sgdamore@opensolaris.org 	audio_engine_t	*e;
128811936Sgdamore@opensolaris.org 
12899484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(dontcare));
129011936Sgdamore@opensolaris.org 
129111936Sgdamore@opensolaris.org 	mutex_enter(&d->d_lock);
129211936Sgdamore@opensolaris.org 	mutex_enter(&d->d_ctrl_lock);
129311936Sgdamore@opensolaris.org 
129411936Sgdamore@opensolaris.org 	ASSERT(d->d_suspended);
129511936Sgdamore@opensolaris.org 	d->d_suspended--;
129611936Sgdamore@opensolaris.org 	if (d->d_suspended) {
129711936Sgdamore@opensolaris.org 		mutex_exit(&d->d_ctrl_lock);
129811936Sgdamore@opensolaris.org 		mutex_exit(&d->d_lock);
129911936Sgdamore@opensolaris.org 		return (AUDIO_WALK_CONTINUE);
130011936Sgdamore@opensolaris.org 	}
130111936Sgdamore@opensolaris.org 
130211936Sgdamore@opensolaris.org 	(void) auimpl_restore_controls(d);
130311936Sgdamore@opensolaris.org 	cv_broadcast(&d->d_ctrl_cv);
130411936Sgdamore@opensolaris.org 	mutex_exit(&d->d_ctrl_lock);
130511936Sgdamore@opensolaris.org 
130611936Sgdamore@opensolaris.org 	l = &d->d_engines;
130711936Sgdamore@opensolaris.org 	for (e = list_head(l); e != NULL; e = list_next(l, e)) {
130811936Sgdamore@opensolaris.org 		mutex_enter(&e->e_lock);
130911936Sgdamore@opensolaris.org 		auimpl_engine_resume(e);
131011936Sgdamore@opensolaris.org 		mutex_exit(&e->e_lock);
131111936Sgdamore@opensolaris.org 	}
131211936Sgdamore@opensolaris.org 	mutex_exit(&d->d_lock);
131311936Sgdamore@opensolaris.org 
13149484Sgarrett.damore@Sun.COM 	return (AUDIO_WALK_CONTINUE);
13159484Sgarrett.damore@Sun.COM }
13169484Sgarrett.damore@Sun.COM 
13179484Sgarrett.damore@Sun.COM boolean_t
auimpl_cpr(void * arg,int code)13189484Sgarrett.damore@Sun.COM auimpl_cpr(void *arg, int code)
13199484Sgarrett.damore@Sun.COM {
13209484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(arg));
13219484Sgarrett.damore@Sun.COM 
13229484Sgarrett.damore@Sun.COM 	switch (code) {
13239484Sgarrett.damore@Sun.COM 	case CB_CODE_CPR_CHKPT:
132411936Sgdamore@opensolaris.org 		auclnt_walk_devs(auimpl_dev_suspend, NULL);
13259484Sgarrett.damore@Sun.COM 		return (B_TRUE);
13269484Sgarrett.damore@Sun.COM 
13279484Sgarrett.damore@Sun.COM 	case CB_CODE_CPR_RESUME:
132811936Sgdamore@opensolaris.org 		auclnt_walk_devs(auimpl_dev_resume, NULL);
13299484Sgarrett.damore@Sun.COM 		return (B_TRUE);
13309484Sgarrett.damore@Sun.COM 
13319484Sgarrett.damore@Sun.COM 	default:
13329484Sgarrett.damore@Sun.COM 		return (B_FALSE);
13339484Sgarrett.damore@Sun.COM 	}
13349484Sgarrett.damore@Sun.COM }
13359484Sgarrett.damore@Sun.COM 
133611936Sgdamore@opensolaris.org void
audio_dev_suspend(audio_dev_t * d)133711936Sgdamore@opensolaris.org audio_dev_suspend(audio_dev_t *d)
133811936Sgdamore@opensolaris.org {
133911936Sgdamore@opensolaris.org 	(void) auimpl_dev_suspend(d, NULL);
134011936Sgdamore@opensolaris.org }
134111936Sgdamore@opensolaris.org 
134211936Sgdamore@opensolaris.org void
audio_dev_resume(audio_dev_t * d)134311936Sgdamore@opensolaris.org audio_dev_resume(audio_dev_t *d)
134411936Sgdamore@opensolaris.org {
134511936Sgdamore@opensolaris.org 	(void) auimpl_dev_resume(d, NULL);
134611936Sgdamore@opensolaris.org }
134711936Sgdamore@opensolaris.org 
13489484Sgarrett.damore@Sun.COM static callb_id_t	auimpl_cpr_id = 0;
13499484Sgarrett.damore@Sun.COM 
13509484Sgarrett.damore@Sun.COM void
auimpl_dev_init(void)13519484Sgarrett.damore@Sun.COM auimpl_dev_init(void)
13529484Sgarrett.damore@Sun.COM {
13539484Sgarrett.damore@Sun.COM 	rw_init(&auimpl_dev_lock, NULL, RW_DRIVER, NULL);
13549484Sgarrett.damore@Sun.COM 	list_create(&auimpl_devs_by_index, sizeof (struct audio_dev),
13559484Sgarrett.damore@Sun.COM 	    offsetof(struct audio_dev, d_by_index));
13569484Sgarrett.damore@Sun.COM 	list_create(&auimpl_devs_by_number, sizeof (struct audio_dev),
13579484Sgarrett.damore@Sun.COM 	    offsetof(struct audio_dev, d_by_number));
13589484Sgarrett.damore@Sun.COM 
13599484Sgarrett.damore@Sun.COM 	/*
13609484Sgarrett.damore@Sun.COM 	 * We "borrow" the CB_CL_CPR_PM class, which gets executed at
13619484Sgarrett.damore@Sun.COM 	 * about the right time for us.  It would be nice to have a
13629484Sgarrett.damore@Sun.COM 	 * new CB_CL_CPR_AUDIO class, but it isn't critical at this
13639484Sgarrett.damore@Sun.COM 	 * point.
13649484Sgarrett.damore@Sun.COM 	 *
13659484Sgarrett.damore@Sun.COM 	 * Note that we don't care about our thread id.
13669484Sgarrett.damore@Sun.COM 	 */
13679484Sgarrett.damore@Sun.COM 	auimpl_cpr_id = callb_add(auimpl_cpr, NULL, CB_CL_CPR_PM, "audio_cpr");
13689484Sgarrett.damore@Sun.COM }
13699484Sgarrett.damore@Sun.COM 
13709484Sgarrett.damore@Sun.COM void
auimpl_dev_fini(void)13719484Sgarrett.damore@Sun.COM auimpl_dev_fini(void)
13729484Sgarrett.damore@Sun.COM {
13739484Sgarrett.damore@Sun.COM 	(void) callb_delete(auimpl_cpr_id);
13749484Sgarrett.damore@Sun.COM 	list_destroy(&auimpl_devs_by_index);
13759484Sgarrett.damore@Sun.COM 	list_destroy(&auimpl_devs_by_number);
13769484Sgarrett.damore@Sun.COM 	rw_destroy(&auimpl_dev_lock);
13779484Sgarrett.damore@Sun.COM }
13789484Sgarrett.damore@Sun.COM 
13799484Sgarrett.damore@Sun.COM void
audio_engine_set_private(audio_engine_t * eng,void * prv)13809484Sgarrett.damore@Sun.COM audio_engine_set_private(audio_engine_t *eng, void *prv)
13819484Sgarrett.damore@Sun.COM {
13829484Sgarrett.damore@Sun.COM 	eng->e_private = prv;
13839484Sgarrett.damore@Sun.COM }
13849484Sgarrett.damore@Sun.COM 
13859484Sgarrett.damore@Sun.COM void *
audio_engine_get_private(audio_engine_t * eng)13869484Sgarrett.damore@Sun.COM audio_engine_get_private(audio_engine_t *eng)
13879484Sgarrett.damore@Sun.COM {
13889484Sgarrett.damore@Sun.COM 	return (eng->e_private);
13899484Sgarrett.damore@Sun.COM }
13909484Sgarrett.damore@Sun.COM 
13919484Sgarrett.damore@Sun.COM void
audio_dump_bytes(const uint8_t * w,int dcount)13929484Sgarrett.damore@Sun.COM audio_dump_bytes(const uint8_t *w, int dcount)
13939484Sgarrett.damore@Sun.COM {
13949484Sgarrett.damore@Sun.COM 	char		line[64];
13959484Sgarrett.damore@Sun.COM 	char		*s;
13969484Sgarrett.damore@Sun.COM 	int		i;
13979484Sgarrett.damore@Sun.COM 	const int	wrap = 16;
13989484Sgarrett.damore@Sun.COM 
13999484Sgarrett.damore@Sun.COM 	s = line;
14009484Sgarrett.damore@Sun.COM 	line[0] = 0;
14019484Sgarrett.damore@Sun.COM 
14029484Sgarrett.damore@Sun.COM 	cmn_err(CE_NOTE, "starting @ %p", (void *)w);
14039484Sgarrett.damore@Sun.COM 	for (i = 0; i < dcount; i++) {
14049484Sgarrett.damore@Sun.COM 
14059484Sgarrett.damore@Sun.COM 		(void) sprintf(s, " %02x", *w);
14069484Sgarrett.damore@Sun.COM 		s += strlen(s);
14079484Sgarrett.damore@Sun.COM 		w++;
14089484Sgarrett.damore@Sun.COM 
14099484Sgarrett.damore@Sun.COM 		if ((i % wrap) == (wrap - 1)) {
14109484Sgarrett.damore@Sun.COM 			cmn_err(CE_NOTE, "%08x:%s", i - (wrap - 1), line);
14119484Sgarrett.damore@Sun.COM 			line[0] = 0;
14129484Sgarrett.damore@Sun.COM 			s = line;
14139484Sgarrett.damore@Sun.COM 		}
14149484Sgarrett.damore@Sun.COM 	}
14159484Sgarrett.damore@Sun.COM 
14169484Sgarrett.damore@Sun.COM 	if ((i % wrap) != 0) {
14179484Sgarrett.damore@Sun.COM 		cmn_err(CE_NOTE, "%08x:%s", i - (i % wrap), line);
14189484Sgarrett.damore@Sun.COM 	}
14199484Sgarrett.damore@Sun.COM }
14209484Sgarrett.damore@Sun.COM 
14219484Sgarrett.damore@Sun.COM void
audio_dump_words(const uint16_t * w,int dcount)14229484Sgarrett.damore@Sun.COM audio_dump_words(const uint16_t *w, int dcount)
14239484Sgarrett.damore@Sun.COM {
14249484Sgarrett.damore@Sun.COM 	char		line[64];
14259484Sgarrett.damore@Sun.COM 	char		*s;
14269484Sgarrett.damore@Sun.COM 	int		i;
14279484Sgarrett.damore@Sun.COM 	const int	wrap = 8;
14289484Sgarrett.damore@Sun.COM 
14299484Sgarrett.damore@Sun.COM 	s = line;
14309484Sgarrett.damore@Sun.COM 	line[0] = 0;
14319484Sgarrett.damore@Sun.COM 
14329484Sgarrett.damore@Sun.COM 	cmn_err(CE_NOTE, "starting @ %p", (void *)w);
14339484Sgarrett.damore@Sun.COM 	for (i = 0; i < dcount; i++) {
14349484Sgarrett.damore@Sun.COM 
14359484Sgarrett.damore@Sun.COM 		(void) sprintf(s, " %04x", *w);
14369484Sgarrett.damore@Sun.COM 		s += strlen(s);
14379484Sgarrett.damore@Sun.COM 		w++;
14389484Sgarrett.damore@Sun.COM 
14399484Sgarrett.damore@Sun.COM 		if ((i % wrap) == (wrap - 1)) {
14409484Sgarrett.damore@Sun.COM 			cmn_err(CE_NOTE, "%08x:%s", i - (wrap - 1), line);
14419484Sgarrett.damore@Sun.COM 			line[0] = 0;
14429484Sgarrett.damore@Sun.COM 			s = line;
14439484Sgarrett.damore@Sun.COM 		}
14449484Sgarrett.damore@Sun.COM 	}
14459484Sgarrett.damore@Sun.COM 
14469484Sgarrett.damore@Sun.COM 	if ((i % wrap) != 0) {
14479484Sgarrett.damore@Sun.COM 		cmn_err(CE_NOTE, "%08x:%s", i - (i % wrap), line);
14489484Sgarrett.damore@Sun.COM 	}
14499484Sgarrett.damore@Sun.COM }
14509484Sgarrett.damore@Sun.COM 
14519484Sgarrett.damore@Sun.COM void
audio_dump_dwords(const uint32_t * w,int dcount)14529484Sgarrett.damore@Sun.COM audio_dump_dwords(const uint32_t *w, int dcount)
14539484Sgarrett.damore@Sun.COM {
14549484Sgarrett.damore@Sun.COM 	char		line[128];
14559484Sgarrett.damore@Sun.COM 	char		*s;
14569484Sgarrett.damore@Sun.COM 	int		i;
14579484Sgarrett.damore@Sun.COM 	const int	wrap = 4;
14589484Sgarrett.damore@Sun.COM 
14599484Sgarrett.damore@Sun.COM 	s = line;
14609484Sgarrett.damore@Sun.COM 	line[0] = 0;
14619484Sgarrett.damore@Sun.COM 
14629484Sgarrett.damore@Sun.COM 	cmn_err(CE_NOTE, "starting @ %p", (void *)w);
14639484Sgarrett.damore@Sun.COM 	for (i = 0; i < dcount; i++) {
14649484Sgarrett.damore@Sun.COM 
14659484Sgarrett.damore@Sun.COM 		(void) sprintf(s, " %08x", *w);
14669484Sgarrett.damore@Sun.COM 		s += strlen(s);
14679484Sgarrett.damore@Sun.COM 		w++;
14689484Sgarrett.damore@Sun.COM 
14699484Sgarrett.damore@Sun.COM 		if ((i % wrap) == (wrap - 1)) {
14709484Sgarrett.damore@Sun.COM 			cmn_err(CE_NOTE, "%08x:%s", i - (wrap - 1), line);
14719484Sgarrett.damore@Sun.COM 			line[0] = 0;
14729484Sgarrett.damore@Sun.COM 			s = line;
14739484Sgarrett.damore@Sun.COM 		}
14749484Sgarrett.damore@Sun.COM 	}
14759484Sgarrett.damore@Sun.COM 
14769484Sgarrett.damore@Sun.COM 	if ((i % wrap) != 0) {
14779484Sgarrett.damore@Sun.COM 		cmn_err(CE_NOTE, "%08x:%s", i - (i % wrap), line);
14789484Sgarrett.damore@Sun.COM 	}
14799484Sgarrett.damore@Sun.COM }
1480