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.
25*197c9523SMarcel Telka  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
2688447a05SGarrett D'Amore  */
2788447a05SGarrett D'Amore 
2888447a05SGarrett D'Amore #include <sys/types.h>
2988447a05SGarrett D'Amore #include <sys/list.h>
3088447a05SGarrett D'Amore #include <sys/sysmacros.h>
3188447a05SGarrett D'Amore #include <sys/ddi.h>
3288447a05SGarrett D'Amore #include <sys/sunddi.h>
3388447a05SGarrett D'Amore #include <sys/callb.h>
3488447a05SGarrett D'Amore #include <sys/kstat.h>
3588447a05SGarrett D'Amore #include <sys/note.h>
3688447a05SGarrett D'Amore 
3788447a05SGarrett D'Amore #include "audio_impl.h"
3888447a05SGarrett D'Amore 
3988447a05SGarrett D'Amore /*
4088447a05SGarrett D'Amore  * Audio Engine functions.
4188447a05SGarrett D'Amore  */
4288447a05SGarrett D'Amore 
4368c47f65SGarrett D'Amore /*
4468c47f65SGarrett D'Amore  * Globals
4568c47f65SGarrett D'Amore  */
4668c47f65SGarrett D'Amore uint_t		audio_intrhz = AUDIO_INTRHZ;
4768c47f65SGarrett D'Amore /*
4868c47f65SGarrett D'Amore  * We need to operate at fairly high interrupt priority to avoid
4968c47f65SGarrett D'Amore  * underruns due to other less time sensitive processing.
5068c47f65SGarrett D'Amore  */
5168c47f65SGarrett D'Amore int		audio_priority = DDI_IPL_8;
5268c47f65SGarrett D'Amore 
5388447a05SGarrett D'Amore audio_dev_t *
audio_dev_alloc(dev_info_t * dip,int instance)5488447a05SGarrett D'Amore audio_dev_alloc(dev_info_t *dip, int instance)
5588447a05SGarrett D'Amore {
5688447a05SGarrett D'Amore 	audio_dev_t *d;
5788447a05SGarrett D'Amore 
5888447a05SGarrett D'Amore 	/*
5988447a05SGarrett D'Amore 	 * For a card with multiple independent audio ports on it, we
6088447a05SGarrett D'Amore 	 * allow the driver to provide a different instance numbering
6188447a05SGarrett D'Amore 	 * scheme than the standard DDI instance number.  (This is
6288447a05SGarrett D'Amore 	 * sort of like the PPA numbering scheme used by NIC drivers
6388447a05SGarrett D'Amore 	 * -- by default PPA == instance, but sometimes we need more
6488447a05SGarrett D'Amore 	 * flexibility.)
6588447a05SGarrett D'Amore 	 */
6688447a05SGarrett D'Amore 	if (instance == 0) {
6788447a05SGarrett D'Amore 		instance = ddi_get_instance(dip);
6888447a05SGarrett D'Amore 	}
6988447a05SGarrett D'Amore 	/* generally this shouldn't occur */
7088447a05SGarrett D'Amore 	if (instance > AUDIO_MN_INST_MASK) {
7188447a05SGarrett D'Amore 		audio_dev_warn(NULL, "bad instance number for %s (%d)",
7288447a05SGarrett D'Amore 		    ddi_driver_name(dip), instance);
7388447a05SGarrett D'Amore 		return (NULL);
7488447a05SGarrett D'Amore 	}
7588447a05SGarrett D'Amore 
7688447a05SGarrett D'Amore 	if ((d = kmem_zalloc(sizeof (*d), KM_NOSLEEP)) == NULL) {
7788447a05SGarrett D'Amore 		audio_dev_warn(NULL, "unable to allocate audio device struct");
7888447a05SGarrett D'Amore 		return (NULL);
7988447a05SGarrett D'Amore 	}
8088447a05SGarrett D'Amore 	d->d_dip = dip;
8188447a05SGarrett D'Amore 	d->d_number = -1;
8288447a05SGarrett D'Amore 	d->d_major = ddi_driver_major(dip);
8388447a05SGarrett D'Amore 	d->d_instance = instance;
8488447a05SGarrett D'Amore 	d->d_pcmvol = 100;
8588447a05SGarrett D'Amore 	mutex_init(&d->d_lock, NULL, MUTEX_DRIVER, NULL);
8688447a05SGarrett D'Amore 	cv_init(&d->d_cv, NULL, CV_DRIVER, NULL);
8768c47f65SGarrett D'Amore 	mutex_init(&d->d_ctrl_lock, NULL, MUTEX_DRIVER, NULL);
8868c47f65SGarrett D'Amore 	cv_init(&d->d_ctrl_cv, NULL, CV_DRIVER, NULL);
8988447a05SGarrett D'Amore 	list_create(&d->d_clients, sizeof (struct audio_client),
9088447a05SGarrett D'Amore 	    offsetof(struct audio_client, c_dev_linkage));
9188447a05SGarrett D'Amore 	list_create(&d->d_engines, sizeof (struct audio_engine),
9288447a05SGarrett D'Amore 	    offsetof(struct audio_engine, e_dev_linkage));
9388447a05SGarrett D'Amore 	list_create(&d->d_controls, sizeof (struct audio_ctrl),
9488447a05SGarrett D'Amore 	    offsetof(struct audio_ctrl, ctrl_linkage));
9588447a05SGarrett D'Amore 	list_create(&d->d_hwinfo, sizeof (struct audio_infostr),
9688447a05SGarrett D'Amore 	    offsetof(struct audio_infostr, i_linkage));
9788447a05SGarrett D'Amore 	(void) snprintf(d->d_name, sizeof (d->d_name), "%s#%d",
9888447a05SGarrett D'Amore 	    ddi_driver_name(dip), instance);
9988447a05SGarrett D'Amore 
10088447a05SGarrett D'Amore 	return (d);
10188447a05SGarrett D'Amore }
10288447a05SGarrett D'Amore 
10388447a05SGarrett D'Amore void
audio_dev_free(audio_dev_t * d)10488447a05SGarrett D'Amore audio_dev_free(audio_dev_t *d)
10588447a05SGarrett D'Amore {
10688447a05SGarrett D'Amore 	struct audio_infostr *isp;
10768c47f65SGarrett D'Amore 
10888447a05SGarrett D'Amore 	while ((isp = list_remove_head(&d->d_hwinfo)) != NULL) {
10988447a05SGarrett D'Amore 		kmem_free(isp, sizeof (*isp));
11088447a05SGarrett D'Amore 	}
11188447a05SGarrett D'Amore 	if (d->d_pcmvol_ctrl != NULL) {
11288447a05SGarrett D'Amore 		audio_dev_del_control(d->d_pcmvol_ctrl);
11388447a05SGarrett D'Amore 	}
11488447a05SGarrett D'Amore 	list_destroy(&d->d_hwinfo);
11588447a05SGarrett D'Amore 	list_destroy(&d->d_engines);
11688447a05SGarrett D'Amore 	list_destroy(&d->d_controls);
11788447a05SGarrett D'Amore 	list_destroy(&d->d_clients);
11868c47f65SGarrett D'Amore 	mutex_destroy(&d->d_ctrl_lock);
11988447a05SGarrett D'Amore 	mutex_destroy(&d->d_lock);
12088447a05SGarrett D'Amore 	cv_destroy(&d->d_cv);
12168c47f65SGarrett D'Amore 	cv_destroy(&d->d_ctrl_cv);
12288447a05SGarrett D'Amore 	kmem_free(d, sizeof (*d));
12388447a05SGarrett D'Amore }
12488447a05SGarrett D'Amore 
12588447a05SGarrett D'Amore void
audio_dev_set_description(audio_dev_t * d,const char * desc)12688447a05SGarrett D'Amore audio_dev_set_description(audio_dev_t *d, const char *desc)
12788447a05SGarrett D'Amore {
12888447a05SGarrett D'Amore 	(void) strlcpy(d->d_desc, desc, sizeof (d->d_desc));
12988447a05SGarrett D'Amore }
13088447a05SGarrett D'Amore 
13188447a05SGarrett D'Amore void
audio_dev_set_version(audio_dev_t * d,const char * vers)13288447a05SGarrett D'Amore audio_dev_set_version(audio_dev_t *d, const char *vers)
13388447a05SGarrett D'Amore {
13488447a05SGarrett D'Amore 	(void) strlcpy(d->d_vers, vers, sizeof (d->d_vers));
13588447a05SGarrett D'Amore }
13688447a05SGarrett D'Amore 
13788447a05SGarrett D'Amore void
audio_dev_add_info(audio_dev_t * d,const char * info)13888447a05SGarrett D'Amore audio_dev_add_info(audio_dev_t *d, const char *info)
13988447a05SGarrett D'Amore {
14088447a05SGarrett D'Amore 	struct audio_infostr *isp;
14188447a05SGarrett D'Amore 
14288447a05SGarrett D'Amore 	/* failure to add information structure is not critical */
14388447a05SGarrett D'Amore 	isp = kmem_zalloc(sizeof (*isp), KM_NOSLEEP);
14488447a05SGarrett D'Amore 	if (isp == NULL) {
14588447a05SGarrett D'Amore 		audio_dev_warn(d, "unable to allocate information structure");
14688447a05SGarrett D'Amore 	} else {
14788447a05SGarrett D'Amore 		(void) snprintf(isp->i_line, sizeof (isp->i_line), info);
14888447a05SGarrett D'Amore 		list_insert_tail(&d->d_hwinfo, isp);
14988447a05SGarrett D'Amore 	}
15088447a05SGarrett D'Amore }
15188447a05SGarrett D'Amore 
15268c47f65SGarrett D'Amore static void
auimpl_engine_reset(audio_engine_t * e)15368c47f65SGarrett D'Amore auimpl_engine_reset(audio_engine_t *e)
15488447a05SGarrett D'Amore {
15588447a05SGarrett D'Amore 	char	*buf;
15688447a05SGarrett D'Amore 	char	*ptr;
15768c47f65SGarrett D'Amore 	int	nfr, resid, cnt;
15868c47f65SGarrett D'Amore 	int	tidx;
15988447a05SGarrett D'Amore 
16068c47f65SGarrett D'Amore 	tidx = e->e_tidx;
16168c47f65SGarrett D'Amore 	nfr = min(e->e_head - e->e_tail, e->e_nframes);
16268c47f65SGarrett D'Amore 	buf = kmem_alloc(nfr * e->e_framesz, KM_SLEEP);
16388447a05SGarrett D'Amore 	ptr = buf;
16468c47f65SGarrett D'Amore 	cnt = 0;
16588447a05SGarrett D'Amore 
16668c47f65SGarrett D'Amore 	ASSERT(e->e_nframes);
16788447a05SGarrett D'Amore 
16868c47f65SGarrett D'Amore 	for (resid = nfr; resid; resid -= cnt) {
16988447a05SGarrett D'Amore 		int	nbytes;
17088447a05SGarrett D'Amore 
17168c47f65SGarrett D'Amore 		cnt = min((e->e_nframes - tidx), resid);
17288447a05SGarrett D'Amore 		nbytes = cnt * e->e_framesz;
17388447a05SGarrett D'Amore 
17468c47f65SGarrett D'Amore 		bcopy(e->e_data + (tidx * e->e_framesz), ptr, nbytes);
17588447a05SGarrett D'Amore 		ptr += nbytes;
17668c47f65SGarrett D'Amore 		tidx += cnt;
17768c47f65SGarrett D'Amore 		if (tidx == e->e_nframes) {
17868c47f65SGarrett D'Amore 			tidx = 0;
17988447a05SGarrett D'Amore 		}
18088447a05SGarrett D'Amore 	}
18188447a05SGarrett D'Amore 
18288447a05SGarrett D'Amore 	if (e->e_flags & ENGINE_INPUT) {
18388447a05SGarrett D'Amore 		/* record */
18488447a05SGarrett D'Amore 		e->e_hidx = 0;
18588447a05SGarrett D'Amore 		e->e_tidx = (e->e_nframes - nfr) % e->e_nframes;
18688447a05SGarrett D'Amore 	} else {
18788447a05SGarrett D'Amore 		/* play */
18888447a05SGarrett D'Amore 		e->e_hidx = nfr % e->e_nframes;
18988447a05SGarrett D'Amore 		e->e_tidx = 0;
19088447a05SGarrett D'Amore 	}
19188447a05SGarrett D'Amore 
19288447a05SGarrett D'Amore 	/* relocate from scratch area to destination */
19388447a05SGarrett D'Amore 	bcopy(buf, e->e_data + (e->e_tidx * e->e_framesz), nfr * e->e_framesz);
19468c47f65SGarrett D'Amore 	kmem_free(buf, nfr * e->e_framesz);
19588447a05SGarrett D'Amore }
19688447a05SGarrett D'Amore 
19768c47f65SGarrett D'Amore static volatile uint_t auimpl_engno = 0;
19868c47f65SGarrett D'Amore 
19988447a05SGarrett D'Amore audio_engine_t *
audio_engine_alloc(audio_engine_ops_t * ops,uint_t flags)20068c47f65SGarrett D'Amore audio_engine_alloc(audio_engine_ops_t *ops, uint_t flags)
20188447a05SGarrett D'Amore {
20288447a05SGarrett D'Amore 	int i;
20388447a05SGarrett D'Amore 	audio_engine_t *e;
20468c47f65SGarrett D'Amore 	char tname[32];
20568c47f65SGarrett D'Amore 	int num;
20688447a05SGarrett D'Amore 
20788447a05SGarrett D'Amore 	if (ops->audio_engine_version != AUDIO_ENGINE_VERSION) {
20888447a05SGarrett D'Amore 		audio_dev_warn(NULL, "audio engine version mismatch: %d != %d",
20988447a05SGarrett D'Amore 		    ops->audio_engine_version, AUDIO_ENGINE_VERSION);
21088447a05SGarrett D'Amore 		return (NULL);
21188447a05SGarrett D'Amore 	}
21288447a05SGarrett D'Amore 
21388447a05SGarrett D'Amore 	/* NB: The ops vector must be held in persistent storage! */
21488447a05SGarrett D'Amore 	e = kmem_zalloc(sizeof (audio_engine_t), KM_NOSLEEP);
21588447a05SGarrett D'Amore 	if (e == NULL) {
21688447a05SGarrett D'Amore 		audio_dev_warn(NULL, "unable to allocate engine struct");
21788447a05SGarrett D'Amore 		return (NULL);
21888447a05SGarrett D'Amore 	}
21988447a05SGarrett D'Amore 	e->e_ops = *ops;
22068c47f65SGarrett D'Amore 	mutex_init(&e->e_lock, NULL, MUTEX_DRIVER,
22168c47f65SGarrett D'Amore 	    DDI_INTR_PRI(audio_priority));
22268c47f65SGarrett D'Amore 	cv_init(&e->e_cv, NULL, CV_DRIVER, NULL);
22388447a05SGarrett D'Amore 	list_create(&e->e_streams, sizeof (struct audio_stream),
22488447a05SGarrett D'Amore 	    offsetof(struct audio_stream, s_eng_linkage));
22588447a05SGarrett D'Amore 
22688447a05SGarrett D'Amore 	for (i = 0; i < AUDIO_MAX_CHANNELS; i++) {
22788447a05SGarrett D'Amore 		e->e_chbufs[i] = kmem_zalloc(sizeof (int32_t) * AUDIO_CHBUFS,
22888447a05SGarrett D'Amore 		    KM_NOSLEEP);
22988447a05SGarrett D'Amore 		if (e->e_chbufs[i] == NULL) {
23088447a05SGarrett D'Amore 			audio_dev_warn(NULL, "unable to allocate channel buf");
23188447a05SGarrett D'Amore 			audio_engine_free(e);
23288447a05SGarrett D'Amore 			return (NULL);
23388447a05SGarrett D'Amore 		}
23488447a05SGarrett D'Amore 	}
23588447a05SGarrett D'Amore 
23668c47f65SGarrett D'Amore 	num = atomic_inc_uint_nv(&auimpl_engno);
23768c47f65SGarrett D'Amore 
23868c47f65SGarrett D'Amore 	(void) snprintf(tname, sizeof (tname), "audio_engine_%d", num);
23968c47f65SGarrett D'Amore 
24088447a05SGarrett D'Amore 	e->e_flags = flags & ENGINE_DRIVER_FLAGS;
24188447a05SGarrett D'Amore 	return (e);
24288447a05SGarrett D'Amore }
24388447a05SGarrett D'Amore 
24488447a05SGarrett D'Amore void
audio_engine_free(audio_engine_t * e)24588447a05SGarrett D'Amore audio_engine_free(audio_engine_t *e)
24688447a05SGarrett D'Amore {
24788447a05SGarrett D'Amore 	int i;
24888447a05SGarrett D'Amore 
24988447a05SGarrett D'Amore 	for (i = 0; i < AUDIO_MAX_CHANNELS; i++) {
25088447a05SGarrett D'Amore 		if (e->e_chbufs[i] != NULL) {
25188447a05SGarrett D'Amore 			kmem_free(e->e_chbufs[i],
25288447a05SGarrett D'Amore 			    sizeof (int32_t) * AUDIO_CHBUFS);
25388447a05SGarrett D'Amore 		}
25488447a05SGarrett D'Amore 	}
25568c47f65SGarrett D'Amore 
25688447a05SGarrett D'Amore 	list_destroy(&e->e_streams);
25788447a05SGarrett D'Amore 	mutex_destroy(&e->e_lock);
25868c47f65SGarrett D'Amore 	cv_destroy(&e->e_cv);
25988447a05SGarrett D'Amore 	kmem_free(e, sizeof (*e));
26088447a05SGarrett D'Amore }
26188447a05SGarrett D'Amore 
26288447a05SGarrett D'Amore static list_t auimpl_devs_by_index;
26388447a05SGarrett D'Amore static list_t auimpl_devs_by_number;
26488447a05SGarrett D'Amore static krwlock_t auimpl_dev_lock;
26588447a05SGarrett D'Amore 
26688447a05SGarrett D'Amore /*
26788447a05SGarrett D'Amore  * Not for public consumption: Private interfaces.
26888447a05SGarrett D'Amore  */
26988447a05SGarrett D'Amore void
auimpl_dev_hold(audio_dev_t * d)27088447a05SGarrett D'Amore auimpl_dev_hold(audio_dev_t *d)
27188447a05SGarrett D'Amore {
27288447a05SGarrett D'Amore 	/* bump the reference count */
27388447a05SGarrett D'Amore 	mutex_enter(&d->d_lock);
27488447a05SGarrett D'Amore 	d->d_refcnt++;
27588447a05SGarrett D'Amore 	mutex_exit(&d->d_lock);
27688447a05SGarrett D'Amore }
27788447a05SGarrett D'Amore 
27888447a05SGarrett D'Amore audio_dev_t *
auimpl_dev_hold_by_devt(dev_t dev)27988447a05SGarrett D'Amore auimpl_dev_hold_by_devt(dev_t dev)
28088447a05SGarrett D'Amore {
28188447a05SGarrett D'Amore 	audio_dev_t *d;
28288447a05SGarrett D'Amore 	major_t major;
28388447a05SGarrett D'Amore 	int instance;
28488447a05SGarrett D'Amore 	list_t *l = &auimpl_devs_by_index;
28588447a05SGarrett D'Amore 
28688447a05SGarrett D'Amore 	major = getmajor(dev);
28788447a05SGarrett D'Amore 	instance = (getminor(dev) >> AUDIO_MN_INST_SHIFT) & AUDIO_MN_INST_MASK;
28888447a05SGarrett D'Amore 
28988447a05SGarrett D'Amore 	rw_enter(&auimpl_dev_lock, RW_READER);
29088447a05SGarrett D'Amore 
29188447a05SGarrett D'Amore 	for (d = list_head(l); d; d = list_next(l, d)) {
29288447a05SGarrett D'Amore 		if ((d->d_major == major) && (d->d_instance == instance)) {
29388447a05SGarrett D'Amore 			auimpl_dev_hold(d);
29488447a05SGarrett D'Amore 			break;
29588447a05SGarrett D'Amore 		}
29688447a05SGarrett D'Amore 	}
29788447a05SGarrett D'Amore 
29888447a05SGarrett D'Amore 	rw_exit(&auimpl_dev_lock);
29988447a05SGarrett D'Amore 	return (d);
30088447a05SGarrett D'Amore }
30188447a05SGarrett D'Amore 
30288447a05SGarrett D'Amore audio_dev_t *
auimpl_dev_hold_by_index(int index)30388447a05SGarrett D'Amore auimpl_dev_hold_by_index(int index)
30488447a05SGarrett D'Amore {
30588447a05SGarrett D'Amore 	audio_dev_t *d;
30688447a05SGarrett D'Amore 	list_t *l = &auimpl_devs_by_index;
30788447a05SGarrett D'Amore 
30888447a05SGarrett D'Amore 	rw_enter(&auimpl_dev_lock, RW_READER);
30988447a05SGarrett D'Amore 
31088447a05SGarrett D'Amore 	for (d = list_head(l); d; d = list_next(l, d)) {
31188447a05SGarrett D'Amore 		if (d->d_index == index) {
31288447a05SGarrett D'Amore 			auimpl_dev_hold(d);
31388447a05SGarrett D'Amore 			break;
31488447a05SGarrett D'Amore 		}
31588447a05SGarrett D'Amore 	}
31688447a05SGarrett D'Amore 
31788447a05SGarrett D'Amore 	rw_exit(&auimpl_dev_lock);
31888447a05SGarrett D'Amore 	return (d);
31988447a05SGarrett D'Amore }
32088447a05SGarrett D'Amore 
32188447a05SGarrett D'Amore void
auimpl_dev_release(audio_dev_t * d)32288447a05SGarrett D'Amore auimpl_dev_release(audio_dev_t *d)
32388447a05SGarrett D'Amore {
32488447a05SGarrett D'Amore 	mutex_enter(&d->d_lock);
32588447a05SGarrett D'Amore 	d->d_refcnt--;
32688447a05SGarrett D'Amore 	mutex_exit(&d->d_lock);
32788447a05SGarrett D'Amore }
32888447a05SGarrett D'Amore 
32988447a05SGarrett D'Amore int
auimpl_choose_format(int fmts)33088447a05SGarrett D'Amore auimpl_choose_format(int fmts)
33188447a05SGarrett D'Amore {
33288447a05SGarrett D'Amore 	/*
33388447a05SGarrett D'Amore 	 * Choose the very best format we can.  We choose 24 bit in
33488447a05SGarrett D'Amore 	 * preference to 32 bit because we mix in 24 bit.  We do that
33588447a05SGarrett D'Amore 	 * to allow overflows to fit within 32-bits.  (Very few humans
33688447a05SGarrett D'Amore 	 * can tell a difference between 24 and 32 bit audio anyway.)
33788447a05SGarrett D'Amore 	 */
33888447a05SGarrett D'Amore 	if (fmts & AUDIO_FORMAT_S24_NE)
33988447a05SGarrett D'Amore 		return (AUDIO_FORMAT_S24_NE);
34088447a05SGarrett D'Amore 
34188447a05SGarrett D'Amore 	if (fmts & AUDIO_FORMAT_S32_NE)
34288447a05SGarrett D'Amore 		return (AUDIO_FORMAT_S32_NE);
34388447a05SGarrett D'Amore 
34488447a05SGarrett D'Amore 	if (fmts & AUDIO_FORMAT_S24_OE)
34588447a05SGarrett D'Amore 		return (AUDIO_FORMAT_S24_OE);
34688447a05SGarrett D'Amore 
34788447a05SGarrett D'Amore 	if (fmts & AUDIO_FORMAT_S32_OE)
34888447a05SGarrett D'Amore 		return (AUDIO_FORMAT_S32_OE);
34988447a05SGarrett D'Amore 
35088447a05SGarrett D'Amore 	if (fmts & AUDIO_FORMAT_S16_NE)
35188447a05SGarrett D'Amore 		return (AUDIO_FORMAT_S16_NE);
35288447a05SGarrett D'Amore 
35388447a05SGarrett D'Amore 	if (fmts & AUDIO_FORMAT_S16_OE)
35488447a05SGarrett D'Amore 		return (AUDIO_FORMAT_S16_OE);
35588447a05SGarrett D'Amore 
35688447a05SGarrett D'Amore 	if (fmts & AUDIO_FORMAT_AC3)
35788447a05SGarrett D'Amore 		return (AUDIO_FORMAT_AC3);
35888447a05SGarrett D'Amore 
35988447a05SGarrett D'Amore 	return (AUDIO_FORMAT_NONE);
36088447a05SGarrett D'Amore }
36188447a05SGarrett D'Amore 
36288447a05SGarrett D'Amore int
auimpl_engine_open(audio_stream_t * sp,int flags)3632c30fa45SGarrett D'Amore auimpl_engine_open(audio_stream_t *sp, int flags)
36488447a05SGarrett D'Amore {
3652c30fa45SGarrett D'Amore 	return (auimpl_engine_setup(sp, flags, NULL, FORMAT_MSK_NONE));
3662c30fa45SGarrett D'Amore }
3672c30fa45SGarrett D'Amore 
3682c30fa45SGarrett D'Amore 
3692c30fa45SGarrett D'Amore int
auimpl_engine_setup(audio_stream_t * sp,int flags,audio_parms_t * parms,uint_t mask)3702c30fa45SGarrett D'Amore auimpl_engine_setup(audio_stream_t *sp, int flags, audio_parms_t *parms,
3712c30fa45SGarrett D'Amore     uint_t mask)
3722c30fa45SGarrett D'Amore {
3732c30fa45SGarrett D'Amore 	audio_dev_t	*d = sp->s_client->c_dev;
37488447a05SGarrett D'Amore 	audio_engine_t	*e = NULL;
3752c30fa45SGarrett D'Amore 	audio_parms_t	uparms;
37688447a05SGarrett D'Amore 	list_t		*list;
3772c30fa45SGarrett D'Amore 	uint_t		cap;
37888447a05SGarrett D'Amore 	int		priority = 0;
37988447a05SGarrett D'Amore 	int		rv = ENODEV;
38088447a05SGarrett D'Amore 	int		sampsz;
38188447a05SGarrett D'Amore 	int		i;
38268c47f65SGarrett D'Amore 	int		fragfr;
3832c30fa45SGarrett D'Amore 	int		fmts;
38488447a05SGarrett D'Amore 
3852c30fa45SGarrett D'Amore 
3862c30fa45SGarrett D'Amore 	mutex_enter(&d->d_lock);
3872c30fa45SGarrett D'Amore 
3882c30fa45SGarrett D'Amore 	uparms = *sp->s_user_parms;
3892c30fa45SGarrett D'Amore 	if (mask & FORMAT_MSK_FMT)
3902c30fa45SGarrett D'Amore 		uparms.p_format = parms->p_format;
3912c30fa45SGarrett D'Amore 	if (mask & FORMAT_MSK_RATE)
3922c30fa45SGarrett D'Amore 		uparms.p_rate = parms->p_rate;
3932c30fa45SGarrett D'Amore 	if (mask & FORMAT_MSK_CHAN)
3942c30fa45SGarrett D'Amore 		uparms.p_nchan = parms->p_nchan;
39588447a05SGarrett D'Amore 
39688447a05SGarrett D'Amore 	/*
3972c30fa45SGarrett D'Amore 	 * Which direction are we opening?  (We must open exactly
39888447a05SGarrett D'Amore 	 * one direction, otherwise the open is meaningless.)
39988447a05SGarrett D'Amore 	 */
40088447a05SGarrett D'Amore 
4012c30fa45SGarrett D'Amore 	if (sp == &sp->s_client->c_ostream) {
4022c30fa45SGarrett D'Amore 		cap = ENGINE_OUTPUT_CAP;
4032c30fa45SGarrett D'Amore 		flags |= ENGINE_OUTPUT;
4042c30fa45SGarrett D'Amore 	} else {
4052c30fa45SGarrett D'Amore 		cap = ENGINE_INPUT_CAP;
4062c30fa45SGarrett D'Amore 		flags |= ENGINE_INPUT;
4072c30fa45SGarrett D'Amore 	}
40888447a05SGarrett D'Amore 
4092c30fa45SGarrett D'Amore 	if (uparms.p_format == AUDIO_FORMAT_AC3) {
4102c30fa45SGarrett D'Amore 		fmts = AUDIO_FORMAT_AC3;
4112c30fa45SGarrett D'Amore 		flags |= ENGINE_EXCLUSIVE;
4122c30fa45SGarrett D'Amore 	} else {
4132c30fa45SGarrett D'Amore 		fmts = AUDIO_FORMAT_PCM;
4142c30fa45SGarrett D'Amore 	}
4152c30fa45SGarrett D'Amore 
4162c30fa45SGarrett D'Amore 	list = &d->d_engines;
41788447a05SGarrett D'Amore 
41888447a05SGarrett D'Amore 
4192c30fa45SGarrett D'Amore 	/* If the device is suspended, wait for it to resume. */
42068c47f65SGarrett D'Amore 	while (d->d_suspended) {
42168c47f65SGarrett D'Amore 		cv_wait(&d->d_ctrl_cv, &d->d_lock);
42268c47f65SGarrett D'Amore 	}
42368c47f65SGarrett D'Amore 
42488447a05SGarrett D'Amore again:
42588447a05SGarrett D'Amore 
42688447a05SGarrett D'Amore 	for (audio_engine_t *t = list_head(list); t; t = list_next(list, t)) {
4272c30fa45SGarrett D'Amore 		int		mypri;
4282c30fa45SGarrett D'Amore 		int		r;
42988447a05SGarrett D'Amore 
4302c30fa45SGarrett D'Amore 		/* Make sure the engine can do what we want it to. */
43188447a05SGarrett D'Amore 		mutex_enter(&t->e_lock);
43268c47f65SGarrett D'Amore 
4332c30fa45SGarrett D'Amore 		if ((t->e_flags & cap) == 0) {
4342c30fa45SGarrett D'Amore 			mutex_exit(&t->e_lock);
4352c30fa45SGarrett D'Amore 			continue;
4362c30fa45SGarrett D'Amore 		}
4372c30fa45SGarrett D'Amore 
4382c30fa45SGarrett D'Amore 		/*
4392c30fa45SGarrett D'Amore 		 * Open the engine early, as the inquiries to rate and format
4402c30fa45SGarrett D'Amore 		 * may not be accurate until this is done.
4412c30fa45SGarrett D'Amore 		 */
4422c30fa45SGarrett D'Amore 		if (list_is_empty(&t->e_streams)) {
4432c30fa45SGarrett D'Amore 			if (ENG_OPEN(t, flags, &t->e_nframes, &t->e_data)) {
4442c30fa45SGarrett D'Amore 				mutex_exit(&t->e_lock);
4452c30fa45SGarrett D'Amore 				rv = EIO;
4462c30fa45SGarrett D'Amore 				continue;
4472c30fa45SGarrett D'Amore 			}
4482c30fa45SGarrett D'Amore 		}
4492c30fa45SGarrett D'Amore 
4502c30fa45SGarrett D'Amore 		if ((ENG_FORMAT(t) & fmts) == 0) {
4512c30fa45SGarrett D'Amore 			if (list_is_empty(&t->e_streams))
4522c30fa45SGarrett D'Amore 				ENG_CLOSE(t);
45388447a05SGarrett D'Amore 			mutex_exit(&t->e_lock);
45488447a05SGarrett D'Amore 			continue;
45588447a05SGarrett D'Amore 		}
45688447a05SGarrett D'Amore 
4572c30fa45SGarrett D'Amore 
4582c30fa45SGarrett D'Amore 		/* If it is in failed state, don't use this engine. */
45968c47f65SGarrett D'Amore 		if (t->e_failed) {
4602c30fa45SGarrett D'Amore 			if (list_is_empty(&t->e_streams))
4612c30fa45SGarrett D'Amore 				ENG_CLOSE(t);
46268c47f65SGarrett D'Amore 			mutex_exit(&t->e_lock);
4632c30fa45SGarrett D'Amore 			rv = rv ? EIO : 0;
46468c47f65SGarrett D'Amore 			continue;
46568c47f65SGarrett D'Amore 		}
46668c47f65SGarrett D'Amore 
4672c30fa45SGarrett D'Amore 		/*
4682c30fa45SGarrett D'Amore 		 * If the engine is in exclusive use, we can't use it.
4692c30fa45SGarrett D'Amore 		 * This is intended for use with AC3 or digital
4702c30fa45SGarrett D'Amore 		 * streams that cannot tolerate mixing.
4712c30fa45SGarrett D'Amore 		 */
4722c30fa45SGarrett D'Amore 		if ((t->e_flags & ENGINE_EXCLUSIVE) && (t != sp->s_engine)) {
4732c30fa45SGarrett D'Amore 			if (list_is_empty(&t->e_streams))
4742c30fa45SGarrett D'Amore 				ENG_CLOSE(t);
47588447a05SGarrett D'Amore 			mutex_exit(&t->e_lock);
4762c30fa45SGarrett D'Amore 			rv = rv ? EBUSY : 0;
47788447a05SGarrett D'Amore 			continue;
47888447a05SGarrett D'Amore 		}
47988447a05SGarrett D'Amore 
4802c30fa45SGarrett D'Amore 		/*
4812c30fa45SGarrett D'Amore 		 * If the engine is in use incompatibly, we can't use
4822c30fa45SGarrett D'Amore 		 * it.  This should only happen for half-duplex audio
4832c30fa45SGarrett D'Amore 		 * devices.  I've not seen any of these that are
4842c30fa45SGarrett D'Amore 		 * recent enough to be supported by Solaris.
4852c30fa45SGarrett D'Amore 		 */
48688447a05SGarrett D'Amore 		if (((flags & ENGINE_INPUT) && (t->e_flags & ENGINE_OUTPUT)) ||
48788447a05SGarrett D'Amore 		    ((flags & ENGINE_OUTPUT) && (t->e_flags & ENGINE_INPUT))) {
4882c30fa45SGarrett D'Amore 			if (list_is_empty(&t->e_streams))
4892c30fa45SGarrett D'Amore 				ENG_CLOSE(t);
49088447a05SGarrett D'Amore 			mutex_exit(&t->e_lock);
4912c30fa45SGarrett D'Amore 			/* Only override the ENODEV or EIO. */
4922c30fa45SGarrett D'Amore 			rv = rv ? EBUSY : 0;
49388447a05SGarrett D'Amore 			continue;
49488447a05SGarrett D'Amore 		}
49588447a05SGarrett D'Amore 
49688447a05SGarrett D'Amore 		/*
49788447a05SGarrett D'Amore 		 * In order to support as many different possible
49888447a05SGarrett D'Amore 		 * output streams (e.g. AC3 passthru or AC3 decode),
49988447a05SGarrett D'Amore 		 * or multiple exclusive outputs, we treat audio
50088447a05SGarrett D'Amore 		 * engines as *precious*.
50188447a05SGarrett D'Amore 		 *
50288447a05SGarrett D'Amore 		 * This means that we will try hard to reuse an
50388447a05SGarrett D'Amore 		 * existing allocated engine.  This may not be the
50488447a05SGarrett D'Amore 		 * optimal performance configuration (especially if we
50588447a05SGarrett D'Amore 		 * wanted to avoid rate conversion, for example), but
50688447a05SGarrett D'Amore 		 * it should have fewer cases where the configuration
50788447a05SGarrett D'Amore 		 * results in denying service to any client.
50888447a05SGarrett D'Amore 		 */
50988447a05SGarrett D'Amore 
5102c30fa45SGarrett D'Amore 		/*
5112c30fa45SGarrett D'Amore 		 * This engine *can* support us, so we should no longer
5122c30fa45SGarrett D'Amore 		 * have a failure mode.
5132c30fa45SGarrett D'Amore 		 */
51488447a05SGarrett D'Amore 		rv = 0;
5152c30fa45SGarrett D'Amore 		mypri = (1U << 0);
51688447a05SGarrett D'Amore 
5172c30fa45SGarrett D'Amore 
5182c30fa45SGarrett D'Amore 		/*
5192c30fa45SGarrett D'Amore 		 * Mixing is cheap, so try not to pick on idle
5202c30fa45SGarrett D'Amore 		 * engines.  This avoids burning bus bandwidth (which
5212c30fa45SGarrett D'Amore 		 * may be precious for certain classes of traffic).
5222c30fa45SGarrett D'Amore 		 * Note that idleness is given a low priority compared
5232c30fa45SGarrett D'Amore 		 * to the other considerations.
5242c30fa45SGarrett D'Amore 		 *
5252c30fa45SGarrett D'Amore 		 * We also use this opportunity open the engine, if
5262c30fa45SGarrett D'Amore 		 * not already done so, so that our parameter
5272c30fa45SGarrett D'Amore 		 * inquiries will be valid.
5282c30fa45SGarrett D'Amore 		 */
5292c30fa45SGarrett D'Amore 		if (!list_is_empty(&t->e_streams))
5302c30fa45SGarrett D'Amore 			mypri |= (1U << 1);
5312c30fa45SGarrett D'Amore 
5322c30fa45SGarrett D'Amore 		/*
5332c30fa45SGarrett D'Amore 		 * Slight preference is given to reuse an engine that
5342c30fa45SGarrett D'Amore 		 * we might already be using.
5352c30fa45SGarrett D'Amore 		 */
5362c30fa45SGarrett D'Amore 		if (t == sp->s_engine)
5372c30fa45SGarrett D'Amore 			mypri |= (1U << 2);
5382c30fa45SGarrett D'Amore 
5392c30fa45SGarrett D'Amore 
5402c30fa45SGarrett D'Amore 		/*
5412c30fa45SGarrett D'Amore 		 * Sample rate conversion avoidance.  Upsampling
5422c30fa45SGarrett D'Amore 		 * requires multiplications and is moderately
5432c30fa45SGarrett D'Amore 		 * expensive.  Downsampling requires division and is
5442c30fa45SGarrett D'Amore 		 * quite expensive, and hence to be avoided if at all
5452c30fa45SGarrett D'Amore 		 * possible.
5462c30fa45SGarrett D'Amore 		 */
5472c30fa45SGarrett D'Amore 		r = ENG_RATE(t);
5482c30fa45SGarrett D'Amore 		if (uparms.p_rate == r) {
5492c30fa45SGarrett D'Amore 			/*
5502c30fa45SGarrett D'Amore 			 * No conversion needed at all.  This is ideal.
5512c30fa45SGarrett D'Amore 			 */
5522c30fa45SGarrett D'Amore 			mypri |= (1U << 4) | (1U << 3);
5532c30fa45SGarrett D'Amore 		} else {
5542c30fa45SGarrett D'Amore 			int src, dst;
5552c30fa45SGarrett D'Amore 
5562c30fa45SGarrett D'Amore 			if (flags & ENGINE_INPUT) {
5572c30fa45SGarrett D'Amore 				src = r;
5582c30fa45SGarrett D'Amore 				dst = uparms.p_rate;
5592c30fa45SGarrett D'Amore 			} else {
5602c30fa45SGarrett D'Amore 				src = uparms.p_rate;
5612c30fa45SGarrett D'Amore 				dst = r;
5622c30fa45SGarrett D'Amore 			}
5632c30fa45SGarrett D'Amore 			if ((src < dst) && ((dst % src) == 0)) {
5642c30fa45SGarrett D'Amore 				/*
5652c30fa45SGarrett D'Amore 				 * Pure upsampling only. This
5662c30fa45SGarrett D'Amore 				 * penalizes any engine which requires
5672c30fa45SGarrett D'Amore 				 * downsampling.
5682c30fa45SGarrett D'Amore 				 */
5692c30fa45SGarrett D'Amore 				mypri |= (1U << 3);
5702c30fa45SGarrett D'Amore 			}
57188447a05SGarrett D'Amore 		}
57288447a05SGarrett D'Amore 
5732c30fa45SGarrett D'Amore 		/*
5742c30fa45SGarrett D'Amore 		 * Try not to pick on duplex engines.  This way we
5752c30fa45SGarrett D'Amore 		 * leave engines that can be used for recording or
5762c30fa45SGarrett D'Amore 		 * playback available as such.  All modern drivers
5772c30fa45SGarrett D'Amore 		 * use separate unidirectional engines for playback
5782c30fa45SGarrett D'Amore 		 * and record.
5792c30fa45SGarrett D'Amore 		 */
5802c30fa45SGarrett D'Amore 		if ((t->e_flags & ENGINE_CAPS) == cap) {
5812c30fa45SGarrett D'Amore 			mypri |= (1U << 5);
58288447a05SGarrett D'Amore 		}
58388447a05SGarrett D'Amore 
5842c30fa45SGarrett D'Amore 		/*
5852c30fa45SGarrett D'Amore 		 * Try not to pick on engines that can do other
5862c30fa45SGarrett D'Amore 		 * formats.  This will generally be false, but if it
5872c30fa45SGarrett D'Amore 		 * happens we pretty strongly avoid using a limited
5882c30fa45SGarrett D'Amore 		 * resource.
5892c30fa45SGarrett D'Amore 		 */
5902c30fa45SGarrett D'Amore 		if ((t->e_format & ~fmts) == 0) {
5912c30fa45SGarrett D'Amore 			mypri |= (1U << 6);
59288447a05SGarrett D'Amore 		}
59388447a05SGarrett D'Amore 
59488447a05SGarrett D'Amore 		if (mypri > priority) {
59588447a05SGarrett D'Amore 			if (e != NULL) {
5962c30fa45SGarrett D'Amore 				/*
5972c30fa45SGarrett D'Amore 				 * If we opened this for our own use
5982c30fa45SGarrett D'Amore 				 * and we are no longer using it, then
5992c30fa45SGarrett D'Amore 				 * close it back down.
6002c30fa45SGarrett D'Amore 				 */
6012c30fa45SGarrett D'Amore 				if (list_is_empty(&e->e_streams))
6022c30fa45SGarrett D'Amore 					ENG_CLOSE(e);
60388447a05SGarrett D'Amore 				mutex_exit(&e->e_lock);
60488447a05SGarrett D'Amore 			}
60588447a05SGarrett D'Amore 			e = t;
60688447a05SGarrett D'Amore 			priority = mypri;
60788447a05SGarrett D'Amore 		} else {
60888447a05SGarrett D'Amore 			mutex_exit(&t->e_lock);
60988447a05SGarrett D'Amore 		}
6102c30fa45SGarrett D'Amore 
6112c30fa45SGarrett D'Amore 		/*
6122c30fa45SGarrett D'Amore 		 * Locking: at this point, if we have an engine, "e", it is
6132c30fa45SGarrett D'Amore 		 * locked.  No other engines should have a lock held.
6142c30fa45SGarrett D'Amore 		 */
61588447a05SGarrett D'Amore 	}
61688447a05SGarrett D'Amore 
61788447a05SGarrett D'Amore 	if ((rv == EBUSY) && ((flags & ENGINE_NDELAY) == 0)) {
61888447a05SGarrett D'Amore 		ASSERT(e == NULL);
61988447a05SGarrett D'Amore 		if (cv_wait_sig(&d->d_cv, &d->d_lock) == 0) {
62088447a05SGarrett D'Amore 			mutex_exit(&d->d_lock);
62188447a05SGarrett D'Amore 			return (EINTR);
62288447a05SGarrett D'Amore 		}
62388447a05SGarrett D'Amore 		goto again;
62488447a05SGarrett D'Amore 	}
62588447a05SGarrett D'Amore 
62688447a05SGarrett D'Amore 	if (rv != 0) {
62788447a05SGarrett D'Amore 		ASSERT(e == NULL);
62888447a05SGarrett D'Amore 		mutex_exit(&d->d_lock);
62988447a05SGarrett D'Amore 		return (rv);
63088447a05SGarrett D'Amore 	}
63188447a05SGarrett D'Amore 
63288447a05SGarrett D'Amore 	ASSERT(e != NULL);
63388447a05SGarrett D'Amore 	ASSERT(mutex_owned(&e->e_lock));
63488447a05SGarrett D'Amore 
6352c30fa45SGarrett D'Amore 	if (sp->s_engine && (sp->s_engine != e)) {
6362c30fa45SGarrett D'Amore 		/*
6372c30fa45SGarrett D'Amore 		 * If this represents a potential engine change, then
6382c30fa45SGarrett D'Amore 		 * we close off everything, and start anew. This turns
6392c30fa45SGarrett D'Amore 		 * out to be vastly simpler than trying to close all
6402c30fa45SGarrett D'Amore 		 * the races associated with a true hand off.  This
6412c30fa45SGarrett D'Amore 		 * ought to be relatively uncommon (changing engines).
6422c30fa45SGarrett D'Amore 		 */
6432c30fa45SGarrett D'Amore 
6442c30fa45SGarrett D'Amore 		/* Drop the new reference. */
6452c30fa45SGarrett D'Amore 		if (list_is_empty(&e->e_streams))
6462c30fa45SGarrett D'Amore 			ENG_CLOSE(e);
6472c30fa45SGarrett D'Amore 		mutex_exit(&e->e_lock);
6482c30fa45SGarrett D'Amore 		mutex_exit(&d->d_lock);
6492c30fa45SGarrett D'Amore 
6502c30fa45SGarrett D'Amore 		auimpl_engine_close(sp);
6512c30fa45SGarrett D'Amore 
6522c30fa45SGarrett D'Amore 		/* Try again. */
6532c30fa45SGarrett D'Amore 		return (auimpl_engine_setup(sp, flags, parms, mask));
6542c30fa45SGarrett D'Amore 	}
6552c30fa45SGarrett D'Amore 
6562c30fa45SGarrett D'Amore 	if (sp->s_engine == NULL) {
6572c30fa45SGarrett D'Amore 		/*
6582c30fa45SGarrett D'Amore 		 * Add a reference to this engine if we don't already
6592c30fa45SGarrett D'Amore 		 * have one.
6602c30fa45SGarrett D'Amore 		 */
6612c30fa45SGarrett D'Amore 		sp->s_engine = e;
6622c30fa45SGarrett D'Amore 
6632c30fa45SGarrett D'Amore 		if (!list_is_empty(&e->e_streams)) {
6642c30fa45SGarrett D'Amore 			/*
6652c30fa45SGarrett D'Amore 			 * If the engine is already open, there is no
6662c30fa45SGarrett D'Amore 			 * need for further work.  The first open will
6672c30fa45SGarrett D'Amore 			 * be relatively expensive, but subsequent
6682c30fa45SGarrett D'Amore 			 * opens should be as cheap as possible.
6692c30fa45SGarrett D'Amore 			 */
6702c30fa45SGarrett D'Amore 			list_insert_tail(&e->e_streams, sp);
6712c30fa45SGarrett D'Amore 			goto ok;
6722c30fa45SGarrett D'Amore 		}
6732c30fa45SGarrett D'Amore 		list_insert_tail(&e->e_streams, sp);
6742c30fa45SGarrett D'Amore 
6752c30fa45SGarrett D'Amore 	} else {
6762c30fa45SGarrett D'Amore 		ASSERT(sp->s_engine == e);
6772c30fa45SGarrett D'Amore 		/*
6782c30fa45SGarrett D'Amore 		 * No change in engine... hence don't reprogram the
6792c30fa45SGarrett D'Amore 		 * engine, and don't change references.
6802c30fa45SGarrett D'Amore 		 */
68188447a05SGarrett D'Amore 		goto ok;
68288447a05SGarrett D'Amore 	}
68388447a05SGarrett D'Amore 
68488447a05SGarrett D'Amore 	e->e_format = ENG_FORMAT(e);
68588447a05SGarrett D'Amore 	e->e_nchan = ENG_CHANNELS(e);
68688447a05SGarrett D'Amore 	e->e_rate = ENG_RATE(e);
68788447a05SGarrett D'Amore 
6882c30fa45SGarrett D'Amore 	/* Select format converters for the engine. */
68988447a05SGarrett D'Amore 	switch (e->e_format) {
69088447a05SGarrett D'Amore 	case AUDIO_FORMAT_S24_NE:
69188447a05SGarrett D'Amore 		e->e_export = auimpl_export_24ne;
69288447a05SGarrett D'Amore 		e->e_import = auimpl_import_24ne;
69388447a05SGarrett D'Amore 		sampsz = 4;
69488447a05SGarrett D'Amore 		break;
69588447a05SGarrett D'Amore 	case AUDIO_FORMAT_S32_NE:
69688447a05SGarrett D'Amore 		e->e_export = auimpl_export_32ne;
69788447a05SGarrett D'Amore 		e->e_import = auimpl_import_32ne;
69888447a05SGarrett D'Amore 		sampsz = 4;
69988447a05SGarrett D'Amore 		break;
70088447a05SGarrett D'Amore 	case AUDIO_FORMAT_S24_OE:
70188447a05SGarrett D'Amore 		e->e_export = auimpl_export_24oe;
70288447a05SGarrett D'Amore 		e->e_import = auimpl_import_24oe;
70388447a05SGarrett D'Amore 		sampsz = 4;
70488447a05SGarrett D'Amore 		break;
70588447a05SGarrett D'Amore 	case AUDIO_FORMAT_S32_OE:
70688447a05SGarrett D'Amore 		e->e_export = auimpl_export_32oe;
70788447a05SGarrett D'Amore 		e->e_import = auimpl_import_32oe;
70888447a05SGarrett D'Amore 		sampsz = 4;
70988447a05SGarrett D'Amore 		break;
71088447a05SGarrett D'Amore 	case AUDIO_FORMAT_S16_NE:
71188447a05SGarrett D'Amore 		e->e_export = auimpl_export_16ne;
71288447a05SGarrett D'Amore 		e->e_import = auimpl_import_16ne;
71388447a05SGarrett D'Amore 		sampsz = 2;
71488447a05SGarrett D'Amore 		break;
71588447a05SGarrett D'Amore 	case AUDIO_FORMAT_S16_OE:
71688447a05SGarrett D'Amore 		e->e_export = auimpl_export_16oe;
71788447a05SGarrett D'Amore 		e->e_import = auimpl_import_16oe;
71888447a05SGarrett D'Amore 		sampsz = 2;
71988447a05SGarrett D'Amore 		break;
72088447a05SGarrett D'Amore 	case AUDIO_FORMAT_AC3:
72188447a05SGarrett D'Amore 		e->e_export = auimpl_export_24ne;
72288447a05SGarrett D'Amore 		e->e_import = auimpl_import_24ne;
72388447a05SGarrett D'Amore 		flags |= ENGINE_EXCLUSIVE;
72488447a05SGarrett D'Amore 		sampsz = 2;
72588447a05SGarrett D'Amore 		break;
72688447a05SGarrett D'Amore 	default:
72788447a05SGarrett D'Amore 		audio_dev_warn(d, "bad format");
72888447a05SGarrett D'Amore 		rv = ENOTSUP;
72988447a05SGarrett D'Amore 		goto done;
73088447a05SGarrett D'Amore 	}
73188447a05SGarrett D'Amore 
73268c47f65SGarrett D'Amore 	fragfr = e->e_rate / audio_intrhz;
73368c47f65SGarrett D'Amore 	if ((fragfr > AUDIO_CHBUFS) || (fragfr < 1)) {
73468c47f65SGarrett D'Amore 		audio_dev_warn(d, "invalid fragment configration");
73568c47f65SGarrett D'Amore 		rv = EINVAL;
73668c47f65SGarrett D'Amore 		goto done;
73768c47f65SGarrett D'Amore 	}
73868c47f65SGarrett D'Amore 
7392c30fa45SGarrett D'Amore 	/* Sanity test a few values. */
74088447a05SGarrett D'Amore 	if ((e->e_nchan < 0) || (e->e_nchan > AUDIO_MAX_CHANNELS) ||
74188447a05SGarrett D'Amore 	    (e->e_rate < 5000) || (e->e_rate > 192000)) {
74288447a05SGarrett D'Amore 		audio_dev_warn(d, "bad engine channels or rate");
74388447a05SGarrett D'Amore 		rv = EINVAL;
74488447a05SGarrett D'Amore 		goto done;
74588447a05SGarrett D'Amore 	}
74688447a05SGarrett D'Amore 
74768c47f65SGarrett D'Amore 	if ((e->e_nframes <= (fragfr * 2)) || (e->e_data == NULL)) {
74888447a05SGarrett D'Amore 		audio_dev_warn(d, "improper engine configuration");
74988447a05SGarrett D'Amore 		rv = EINVAL;
75088447a05SGarrett D'Amore 		goto done;
75188447a05SGarrett D'Amore 	}
75288447a05SGarrett D'Amore 
75388447a05SGarrett D'Amore 	e->e_framesz = e->e_nchan * sampsz;
75468c47f65SGarrett D'Amore 	e->e_fragfr = fragfr;
75588447a05SGarrett D'Amore 	e->e_head = 0;
75688447a05SGarrett D'Amore 	e->e_tail = 0;
75788447a05SGarrett D'Amore 	e->e_hidx = 0;
75888447a05SGarrett D'Amore 	e->e_tidx = 0;
75988447a05SGarrett D'Amore 	e->e_limiter_state = 0x10000;
76068c47f65SGarrett D'Amore 	bzero(e->e_data, e->e_nframes * e->e_framesz);
76188447a05SGarrett D'Amore 
762f9ead4a5SGarrett D'Amore 	if (e->e_ops.audio_engine_playahead == NULL) {
76368c47f65SGarrett D'Amore 		e->e_playahead = (fragfr * 3) / 2;
764f9ead4a5SGarrett D'Amore 	} else {
765f9ead4a5SGarrett D'Amore 		e->e_playahead = ENG_PLAYAHEAD(e);
766f9ead4a5SGarrett D'Amore 		/*
767f9ead4a5SGarrett D'Amore 		 * Need to have at least a fragment plus some extra to
768f9ead4a5SGarrett D'Amore 		 * avoid underruns.
769f9ead4a5SGarrett D'Amore 		 */
77068c47f65SGarrett D'Amore 		if (e->e_playahead < ((fragfr * 3) / 2)) {
77168c47f65SGarrett D'Amore 			e->e_playahead = (fragfr * 3) / 2;
772f9ead4a5SGarrett D'Amore 		}
773f9ead4a5SGarrett D'Amore 
774f9ead4a5SGarrett D'Amore 		/*
775f9ead4a5SGarrett D'Amore 		 * Impossible to queue more frames than FIFO can hold.
776f9ead4a5SGarrett D'Amore 		 */
777f9ead4a5SGarrett D'Amore 		if (e->e_playahead > e->e_nframes) {
77868c47f65SGarrett D'Amore 			e->e_playahead = (fragfr * 3) / 2;
779f9ead4a5SGarrett D'Amore 		}
780f9ead4a5SGarrett D'Amore 	}
781f9ead4a5SGarrett D'Amore 
78288447a05SGarrett D'Amore 	for (i = 0; i < e->e_nchan; i++) {
78388447a05SGarrett D'Amore 		if (e->e_ops.audio_engine_chinfo == NULL) {
78488447a05SGarrett D'Amore 			e->e_choffs[i] = i;
78588447a05SGarrett D'Amore 			e->e_chincr[i] = e->e_nchan;
78688447a05SGarrett D'Amore 		} else {
78788447a05SGarrett D'Amore 			ENG_CHINFO(e, i, &e->e_choffs[i], &e->e_chincr[i]);
78888447a05SGarrett D'Amore 		}
78988447a05SGarrett D'Amore 	}
79088447a05SGarrett D'Amore 
7912c30fa45SGarrett D'Amore 	e->e_flags |= flags;
792c91826f1SGarrett D'Amore 
79388447a05SGarrett D'Amore 	/*
79468c47f65SGarrett D'Amore 	 * Arrange for the engine to be started.  We defer this to the
79568c47f65SGarrett D'Amore 	 * periodic callback, to ensure that the start happens near
79668c47f65SGarrett D'Amore 	 * the edge of the periodic callback.  This is necessary to
79768c47f65SGarrett D'Amore 	 * ensure that the first fragment processed is about the same
79868c47f65SGarrett D'Amore 	 * size as the usual fragment size.  (Basically, the problem
79968c47f65SGarrett D'Amore 	 * is that we have only 10 msec resolution with the periodic
80068c47f65SGarrett D'Amore 	 * interface, whch is rather unfortunate.)
80188447a05SGarrett D'Amore 	 */
80268c47f65SGarrett D'Amore 	e->e_need_start = B_TRUE;
80368c47f65SGarrett D'Amore 
8042c30fa45SGarrett D'Amore 	if (flags & ENGINE_OUTPUT) {
8052c30fa45SGarrett D'Amore 		/*
8062c30fa45SGarrett D'Amore 		 * Start the output callback to populate the engine on
8072c30fa45SGarrett D'Amore 		 * startup.  This avoids a false underrun when we're
8082c30fa45SGarrett D'Amore 		 * first starting up.
8092c30fa45SGarrett D'Amore 		 */
8102c30fa45SGarrett D'Amore 		auimpl_output_preload(e);
8112c30fa45SGarrett D'Amore 
81268c47f65SGarrett D'Amore 		e->e_periodic = ddi_periodic_add(auimpl_output_callback, e,
81368c47f65SGarrett D'Amore 		    NANOSEC / audio_intrhz, audio_priority);
81468c47f65SGarrett D'Amore 	} else {
81568c47f65SGarrett D'Amore 		e->e_periodic = ddi_periodic_add(auimpl_input_callback, e,
81668c47f65SGarrett D'Amore 		    NANOSEC / audio_intrhz, audio_priority);
81788447a05SGarrett D'Amore 	}
81888447a05SGarrett D'Amore 
81988447a05SGarrett D'Amore ok:
82088447a05SGarrett D'Amore 	sp->s_phys_parms->p_rate = e->e_rate;
82188447a05SGarrett D'Amore 	sp->s_phys_parms->p_nchan = e->e_nchan;
82288447a05SGarrett D'Amore 
8232c30fa45SGarrett D'Amore 	/* Configure the engine. */
8242c30fa45SGarrett D'Amore 	mutex_enter(&sp->s_lock);
8252c30fa45SGarrett D'Amore 	rv = auimpl_format_setup(sp, parms, mask);
8262c30fa45SGarrett D'Amore 	mutex_exit(&sp->s_lock);
82788447a05SGarrett D'Amore 
82888447a05SGarrett D'Amore done:
82988447a05SGarrett D'Amore 	mutex_exit(&e->e_lock);
83088447a05SGarrett D'Amore 	mutex_exit(&d->d_lock);
8312c30fa45SGarrett D'Amore 
83288447a05SGarrett D'Amore 	return (rv);
83388447a05SGarrett D'Amore }
83488447a05SGarrett D'Amore 
83588447a05SGarrett D'Amore void
auimpl_engine_close(audio_stream_t * sp)83688447a05SGarrett D'Amore auimpl_engine_close(audio_stream_t *sp)
83788447a05SGarrett D'Amore {
83888447a05SGarrett D'Amore 	audio_engine_t	*e = sp->s_engine;
83988447a05SGarrett D'Amore 	audio_dev_t	*d;
840*197c9523SMarcel Telka 	ddi_periodic_t	ep;
84188447a05SGarrett D'Amore 
84288447a05SGarrett D'Amore 	if (e == NULL)
84388447a05SGarrett D'Amore 		return;
84488447a05SGarrett D'Amore 
84588447a05SGarrett D'Amore 	d = e->e_dev;
846*197c9523SMarcel Telka 	ep = 0;
84788447a05SGarrett D'Amore 
84888447a05SGarrett D'Amore 	mutex_enter(&d->d_lock);
84968c47f65SGarrett D'Amore 	while (d->d_suspended) {
85068c47f65SGarrett D'Amore 		cv_wait(&d->d_ctrl_cv, &d->d_lock);
85168c47f65SGarrett D'Amore 	}
8522c30fa45SGarrett D'Amore 
85388447a05SGarrett D'Amore 	mutex_enter(&e->e_lock);
85488447a05SGarrett D'Amore 	sp->s_engine = NULL;
85588447a05SGarrett D'Amore 	list_remove(&e->e_streams, sp);
85688447a05SGarrett D'Amore 	if (list_is_empty(&e->e_streams)) {
85768c47f65SGarrett D'Amore 		ENG_STOP(e);
858*197c9523SMarcel Telka 		ep = e->e_periodic;
8592c30fa45SGarrett D'Amore 		e->e_periodic = 0;
86088447a05SGarrett D'Amore 		e->e_flags &= ENGINE_DRIVER_FLAGS;
86188447a05SGarrett D'Amore 		ENG_CLOSE(e);
86288447a05SGarrett D'Amore 	}
86388447a05SGarrett D'Amore 	mutex_exit(&e->e_lock);
8642c30fa45SGarrett D'Amore 
865*197c9523SMarcel Telka 	if (ep != 0)
866*197c9523SMarcel Telka 		ddi_periodic_delete(ep);
867*197c9523SMarcel Telka 
86888447a05SGarrett D'Amore 	cv_broadcast(&d->d_cv);
86988447a05SGarrett D'Amore 	mutex_exit(&d->d_lock);
87088447a05SGarrett D'Amore }
87188447a05SGarrett D'Amore 
87288447a05SGarrett D'Amore int
audio_dev_register(audio_dev_t * d)87388447a05SGarrett D'Amore audio_dev_register(audio_dev_t *d)
87488447a05SGarrett D'Amore {
87588447a05SGarrett D'Amore 	list_t *l;
87688447a05SGarrett D'Amore 	audio_dev_t *srch;
87788447a05SGarrett D'Amore 	int start;
87888447a05SGarrett D'Amore 
87988447a05SGarrett D'Amore 	/*
88088447a05SGarrett D'Amore 	 * Make sure we don't automatically unload.  This prevents
88188447a05SGarrett D'Amore 	 * loss of hardware settings when no audio clients are
88288447a05SGarrett D'Amore 	 * running.
88388447a05SGarrett D'Amore 	 */
88488447a05SGarrett D'Amore 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, d->d_dip,
88588447a05SGarrett D'Amore 	    DDI_NO_AUTODETACH, 1);
88688447a05SGarrett D'Amore 
88788447a05SGarrett D'Amore 	/*
88888447a05SGarrett D'Amore 	 * This does an in-order insertion, finding the first available
88988447a05SGarrett D'Amore 	 * free index.  "Special" devices (ones without any actual engines)
89088447a05SGarrett D'Amore 	 * are all numbered 0.  There should only be one of them anyway.
89188447a05SGarrett D'Amore 	 * All others start at one.
89288447a05SGarrett D'Amore 	 */
89388447a05SGarrett D'Amore 	if (d->d_flags & DEV_SNDSTAT_CAP) {
89488447a05SGarrett D'Amore 		start = 0;
89588447a05SGarrett D'Amore 	} else {
89688447a05SGarrett D'Amore 		start = 1;
89788447a05SGarrett D'Amore 	}
89888447a05SGarrett D'Amore 	d->d_index = start;
89968c47f65SGarrett D'Amore 
90088447a05SGarrett D'Amore 	rw_enter(&auimpl_dev_lock, RW_WRITER);
90188447a05SGarrett D'Amore 	l = &auimpl_devs_by_index;
90288447a05SGarrett D'Amore 	for (srch = list_head(l); srch; srch = list_next(l, srch)) {
90388447a05SGarrett D'Amore 		/* skip over special nodes */
90488447a05SGarrett D'Amore 		if (srch->d_index < start)
90588447a05SGarrett D'Amore 			continue;
90688447a05SGarrett D'Amore 		if (srch->d_index > d->d_index) {
90788447a05SGarrett D'Amore 			/* found a free spot! */
90888447a05SGarrett D'Amore 			break;
90988447a05SGarrett D'Amore 		}
91088447a05SGarrett D'Amore 		d->d_index++;
91188447a05SGarrett D'Amore 	}
91288447a05SGarrett D'Amore 	/*
91388447a05SGarrett D'Amore 	 * NB: If srch is NULL, then list_insert_before puts
91488447a05SGarrett D'Amore 	 * it on the tail of the list.  So if we didn't find a
91588447a05SGarrett D'Amore 	 * hole, then that's where we want it.
91688447a05SGarrett D'Amore 	 */
91788447a05SGarrett D'Amore 	list_insert_before(l, srch, d);
91888447a05SGarrett D'Amore 
91988447a05SGarrett D'Amore 	/* insert in order by number */
92088447a05SGarrett D'Amore 	l = &auimpl_devs_by_number;
92188447a05SGarrett D'Amore 	for (srch = list_head(l); srch; srch = list_next(l, srch)) {
92288447a05SGarrett D'Amore 		if (srch->d_number >= d->d_number) {
92388447a05SGarrett D'Amore 			break;
92488447a05SGarrett D'Amore 		}
92588447a05SGarrett D'Amore 	}
92688447a05SGarrett D'Amore 	list_insert_before(l, srch, d);
92788447a05SGarrett D'Amore 
92888447a05SGarrett D'Amore 	rw_exit(&auimpl_dev_lock);
92988447a05SGarrett D'Amore 
93088447a05SGarrett D'Amore 	if (auimpl_create_minors(d) != 0) {
93188447a05SGarrett D'Amore 		rw_enter(&auimpl_dev_lock, RW_WRITER);
93288447a05SGarrett D'Amore 		auimpl_remove_minors(d);
93388447a05SGarrett D'Amore 		list_remove(&auimpl_devs_by_index, d);
93488447a05SGarrett D'Amore 		list_remove(&auimpl_devs_by_number, d);
93588447a05SGarrett D'Amore 		rw_exit(&auimpl_dev_lock);
93688447a05SGarrett D'Amore 		return (DDI_FAILURE);
93788447a05SGarrett D'Amore 	}
93888447a05SGarrett D'Amore 
93988447a05SGarrett D'Amore 	return (DDI_SUCCESS);
94088447a05SGarrett D'Amore }
94188447a05SGarrett D'Amore 
94288447a05SGarrett D'Amore int
audio_dev_unregister(audio_dev_t * d)94388447a05SGarrett D'Amore audio_dev_unregister(audio_dev_t *d)
94488447a05SGarrett D'Amore {
94588447a05SGarrett D'Amore 	rw_enter(&auimpl_dev_lock, RW_WRITER);
94688447a05SGarrett D'Amore 
94788447a05SGarrett D'Amore 	mutex_enter(&d->d_lock);
94888447a05SGarrett D'Amore 	/* if we are still in use, we can't unregister */
94988447a05SGarrett D'Amore 	if (d->d_refcnt) {
95088447a05SGarrett D'Amore 		mutex_exit(&d->d_lock);
95188447a05SGarrett D'Amore 		rw_exit(&auimpl_dev_lock);
95288447a05SGarrett D'Amore 		return (DDI_FAILURE);
95388447a05SGarrett D'Amore 	}
95488447a05SGarrett D'Amore 	auimpl_remove_minors(d);
95588447a05SGarrett D'Amore 	list_remove(&auimpl_devs_by_index, d);
95688447a05SGarrett D'Amore 	list_remove(&auimpl_devs_by_number, d);
95788447a05SGarrett D'Amore 	mutex_exit(&d->d_lock);
95888447a05SGarrett D'Amore 
95988447a05SGarrett D'Amore 	rw_exit(&auimpl_dev_lock);
96088447a05SGarrett D'Amore 
96188447a05SGarrett D'Amore 	return (DDI_SUCCESS);
96288447a05SGarrett D'Amore }
96388447a05SGarrett D'Amore 
96488447a05SGarrett D'Amore static int
auimpl_engine_ksupdate(kstat_t * ksp,int rw)96588447a05SGarrett D'Amore auimpl_engine_ksupdate(kstat_t *ksp, int rw)
96688447a05SGarrett D'Amore {
96788447a05SGarrett D'Amore 	audio_engine_t *e = ksp->ks_private;
96888447a05SGarrett D'Amore 	struct audio_stats *st = &e->e_stats;
96988447a05SGarrett D'Amore 
97088447a05SGarrett D'Amore 	if (rw == KSTAT_WRITE) {
97188447a05SGarrett D'Amore 		return (EACCES);
97288447a05SGarrett D'Amore 	}
97388447a05SGarrett D'Amore 
97488447a05SGarrett D'Amore 	mutex_enter(&e->e_lock);
97588447a05SGarrett D'Amore 	st->st_head.value.ui64 = e->e_head;
97688447a05SGarrett D'Amore 	st->st_tail.value.ui64 = e->e_tail;
97788447a05SGarrett D'Amore 	st->st_flags.value.ui32 = e->e_flags;
97868c47f65SGarrett D'Amore 	st->st_nbytes.value.ui32 = e->e_framesz * e->e_nframes;
97988447a05SGarrett D'Amore 	st->st_framesz.value.ui32 = e->e_framesz;
98088447a05SGarrett D'Amore 	st->st_hidx.value.ui32 = e->e_hidx;
98188447a05SGarrett D'Amore 	st->st_tidx.value.ui32 = e->e_tidx;
98288447a05SGarrett D'Amore 	st->st_format.value.ui32 = e->e_format;
98388447a05SGarrett D'Amore 	st->st_nchan.value.ui32 = e->e_nchan;
98488447a05SGarrett D'Amore 	st->st_rate.value.ui32 = e->e_rate;
98588447a05SGarrett D'Amore 	st->st_errors.value.ui32 = e->e_errors;
986a19bb1faSGarrett D'Amore 	st->st_engine_underruns.value.ui32 = e->e_underruns;
987a19bb1faSGarrett D'Amore 	st->st_engine_overruns.value.ui32 = e->e_overruns;
988a19bb1faSGarrett D'Amore 	st->st_stream_underruns.value.ui32 = e->e_stream_underruns;
989a19bb1faSGarrett D'Amore 	st->st_stream_overruns.value.ui32 = e->e_stream_overruns;
99088447a05SGarrett D'Amore 	st->st_suspended.value.ui32 = e->e_suspended;
99168c47f65SGarrett D'Amore 	st->st_failed.value.ui32 = e->e_failed;
99268c47f65SGarrett D'Amore 	st->st_playahead.value.ui32 = e->e_playahead;
99388447a05SGarrett D'Amore 	mutex_exit(&e->e_lock);
99488447a05SGarrett D'Amore 
99588447a05SGarrett D'Amore 	return (0);
99688447a05SGarrett D'Amore }
99788447a05SGarrett D'Amore 
99888447a05SGarrett D'Amore static void
auimpl_engine_ksinit(audio_dev_t * d,audio_engine_t * e)99988447a05SGarrett D'Amore auimpl_engine_ksinit(audio_dev_t *d, audio_engine_t *e)
100088447a05SGarrett D'Amore {
100188447a05SGarrett D'Amore 	char			name[32];
100288447a05SGarrett D'Amore 	struct audio_stats	*st;
100388447a05SGarrett D'Amore 
100488447a05SGarrett D'Amore 	(void) snprintf(name, sizeof (name), "engine_%d", e->e_num);
100588447a05SGarrett D'Amore 
100688447a05SGarrett D'Amore 	e->e_ksp = kstat_create(ddi_driver_name(d->d_dip), d->d_instance,
100788447a05SGarrett D'Amore 	    name, "misc", KSTAT_TYPE_NAMED,
100888447a05SGarrett D'Amore 	    sizeof (struct audio_stats) / sizeof (kstat_named_t), 0);
100988447a05SGarrett D'Amore 
101088447a05SGarrett D'Amore 	if (e->e_ksp == NULL) {
101188447a05SGarrett D'Amore 		audio_dev_warn(d, "unable to initialize kstats");
101288447a05SGarrett D'Amore 		return;
101388447a05SGarrett D'Amore 	}
101488447a05SGarrett D'Amore 
101588447a05SGarrett D'Amore 	st = &e->e_stats;
101688447a05SGarrett D'Amore 	e->e_ksp->ks_data = st;
101788447a05SGarrett D'Amore 	e->e_ksp->ks_private = e;
101888447a05SGarrett D'Amore 	e->e_ksp->ks_lock = NULL;
101988447a05SGarrett D'Amore 	e->e_ksp->ks_update = auimpl_engine_ksupdate;
102088447a05SGarrett D'Amore 	kstat_named_init(&st->st_head, "head", KSTAT_DATA_UINT64);
102188447a05SGarrett D'Amore 	kstat_named_init(&st->st_tail, "tail", KSTAT_DATA_UINT64);
102288447a05SGarrett D'Amore 	kstat_named_init(&st->st_flags, "flags", KSTAT_DATA_UINT32);
102388447a05SGarrett D'Amore 	kstat_named_init(&st->st_nbytes, "nbytes", KSTAT_DATA_UINT32);
102468c47f65SGarrett D'Amore 	kstat_named_init(&st->st_framesz, "framesz", KSTAT_DATA_UINT32);
102588447a05SGarrett D'Amore 	kstat_named_init(&st->st_hidx, "hidx", KSTAT_DATA_UINT32);
102688447a05SGarrett D'Amore 	kstat_named_init(&st->st_tidx, "tidx", KSTAT_DATA_UINT32);
102788447a05SGarrett D'Amore 	kstat_named_init(&st->st_format, "format", KSTAT_DATA_UINT32);
102888447a05SGarrett D'Amore 	kstat_named_init(&st->st_nchan, "channels", KSTAT_DATA_UINT32);
102988447a05SGarrett D'Amore 	kstat_named_init(&st->st_rate, "rate", KSTAT_DATA_UINT32);
103088447a05SGarrett D'Amore 	kstat_named_init(&st->st_errors, "errors", KSTAT_DATA_UINT32);
1031a19bb1faSGarrett D'Amore 	kstat_named_init(&st->st_engine_overruns, "engine_overruns",
1032a19bb1faSGarrett D'Amore 	    KSTAT_DATA_UINT32);
1033a19bb1faSGarrett D'Amore 	kstat_named_init(&st->st_engine_underruns, "engine_underruns",
1034a19bb1faSGarrett D'Amore 	    KSTAT_DATA_UINT32);
1035a19bb1faSGarrett D'Amore 	kstat_named_init(&st->st_stream_overruns, "stream_overruns",
1036a19bb1faSGarrett D'Amore 	    KSTAT_DATA_UINT32);
1037a19bb1faSGarrett D'Amore 	kstat_named_init(&st->st_stream_underruns, "stream_underruns",
1038a19bb1faSGarrett D'Amore 	    KSTAT_DATA_UINT32);
103968c47f65SGarrett D'Amore 	kstat_named_init(&st->st_playahead, "playahead", KSTAT_DATA_UINT32);
104088447a05SGarrett D'Amore 	kstat_named_init(&st->st_suspended, "suspended", KSTAT_DATA_UINT32);
104168c47f65SGarrett D'Amore 	kstat_named_init(&st->st_failed, "failed", KSTAT_DATA_UINT32);
104288447a05SGarrett D'Amore 	kstat_install(e->e_ksp);
104388447a05SGarrett D'Amore }
104488447a05SGarrett D'Amore 
104588447a05SGarrett D'Amore void
audio_dev_add_engine(audio_dev_t * d,audio_engine_t * e)104688447a05SGarrett D'Amore audio_dev_add_engine(audio_dev_t *d, audio_engine_t *e)
104788447a05SGarrett D'Amore {
104888447a05SGarrett D'Amore 	mutex_enter(&d->d_lock);
104988447a05SGarrett D'Amore 
105068c47f65SGarrett D'Amore 	e->e_num = d->d_engno++;
105168c47f65SGarrett D'Amore 
105288447a05SGarrett D'Amore 	auimpl_engine_ksinit(d, e);
105388447a05SGarrett D'Amore 
105488447a05SGarrett D'Amore 	/* check for duplex */
105588447a05SGarrett D'Amore 	if ((e->e_flags & ENGINE_OUTPUT_CAP) && (d->d_flags & DEV_INPUT_CAP)) {
105688447a05SGarrett D'Amore 		d->d_flags |= DEV_DUPLEX_CAP;
105788447a05SGarrett D'Amore 	}
105888447a05SGarrett D'Amore 	if ((e->e_flags & ENGINE_INPUT_CAP) && (d->d_flags & DEV_OUTPUT_CAP)) {
105988447a05SGarrett D'Amore 		d->d_flags |= DEV_DUPLEX_CAP;
106088447a05SGarrett D'Amore 	}
106188447a05SGarrett D'Amore 	/* add in the direction caps -- must be done after duplex above */
106288447a05SGarrett D'Amore 	if (e->e_flags & ENGINE_OUTPUT_CAP) {
106388447a05SGarrett D'Amore 		d->d_flags |= DEV_OUTPUT_CAP;
106488447a05SGarrett D'Amore 	}
106588447a05SGarrett D'Amore 	if (e->e_flags & ENGINE_INPUT_CAP) {
106688447a05SGarrett D'Amore 		d->d_flags |= DEV_INPUT_CAP;
106788447a05SGarrett D'Amore 	}
106888447a05SGarrett D'Amore 
106988447a05SGarrett D'Amore 	list_insert_tail(&d->d_engines, e);
107088447a05SGarrett D'Amore 	e->e_dev = d;
107188447a05SGarrett D'Amore 	mutex_exit(&d->d_lock);
107288447a05SGarrett D'Amore }
107388447a05SGarrett D'Amore 
107488447a05SGarrett D'Amore void
audio_dev_remove_engine(audio_dev_t * d,audio_engine_t * e)107588447a05SGarrett D'Amore audio_dev_remove_engine(audio_dev_t *d, audio_engine_t *e)
107688447a05SGarrett D'Amore {
107788447a05SGarrett D'Amore 	mutex_enter(&d->d_lock);
107888447a05SGarrett D'Amore 	list_remove(&d->d_engines, e);
107988447a05SGarrett D'Amore 	e->e_dev = NULL;
108088447a05SGarrett D'Amore 	if (e->e_ksp)
108188447a05SGarrett D'Amore 		kstat_delete(e->e_ksp);
108288447a05SGarrett D'Amore 	e->e_ksp = NULL;
108388447a05SGarrett D'Amore 	mutex_exit(&d->d_lock);
108488447a05SGarrett D'Amore }
108588447a05SGarrett D'Amore 
108688447a05SGarrett D'Amore /*
108788447a05SGarrett D'Amore  * Change the number.
108888447a05SGarrett D'Amore  */
108988447a05SGarrett D'Amore void
auclnt_set_dev_number(audio_dev_t * d,int num)109088447a05SGarrett D'Amore auclnt_set_dev_number(audio_dev_t *d, int num)
109188447a05SGarrett D'Amore {
109288447a05SGarrett D'Amore 	list_t		*l = &auimpl_devs_by_number;
109388447a05SGarrett D'Amore 	audio_dev_t	*srch;
109488447a05SGarrett D'Amore 
109588447a05SGarrett D'Amore 	/* reorder our list */
109688447a05SGarrett D'Amore 	rw_enter(&auimpl_dev_lock, RW_WRITER);
109788447a05SGarrett D'Amore 	d->d_number = num;
109888447a05SGarrett D'Amore 	list_remove(l, d);
109988447a05SGarrett D'Amore 	for (srch = list_head(l); srch; srch = list_next(l, srch)) {
110088447a05SGarrett D'Amore 		if (srch->d_number >= d->d_number) {
110188447a05SGarrett D'Amore 			break;
110288447a05SGarrett D'Amore 		}
110388447a05SGarrett D'Amore 	}
110488447a05SGarrett D'Amore 	list_insert_before(l, srch, d);
110588447a05SGarrett D'Amore 
110688447a05SGarrett D'Amore 	rw_exit(&auimpl_dev_lock);
110788447a05SGarrett D'Amore }
110888447a05SGarrett D'Amore 
110988447a05SGarrett D'Amore void
auclnt_walk_devs(int (* walker)(audio_dev_t *,void *),void * arg)111088447a05SGarrett D'Amore auclnt_walk_devs(int (*walker)(audio_dev_t *, void *), void *arg)
111188447a05SGarrett D'Amore {
111288447a05SGarrett D'Amore 	audio_dev_t	*d;
111388447a05SGarrett D'Amore 	boolean_t	cont;
111488447a05SGarrett D'Amore 	list_t		*l;
111588447a05SGarrett D'Amore 
111688447a05SGarrett D'Amore 	l = &auimpl_devs_by_index;
111788447a05SGarrett D'Amore 	rw_enter(&auimpl_dev_lock, RW_READER);
111888447a05SGarrett D'Amore 	for (d = list_head(l); d; d = list_next(l, d)) {
111988447a05SGarrett D'Amore 		cont = walker(d, arg);
112088447a05SGarrett D'Amore 		if (cont == AUDIO_WALK_STOP)
112188447a05SGarrett D'Amore 			break;
112288447a05SGarrett D'Amore 	}
112388447a05SGarrett D'Amore 	rw_exit(&auimpl_dev_lock);
112488447a05SGarrett D'Amore }
112588447a05SGarrett D'Amore 
112688447a05SGarrett D'Amore void
auclnt_walk_devs_by_number(int (* walker)(audio_dev_t *,void *),void * arg)112788447a05SGarrett D'Amore auclnt_walk_devs_by_number(int (*walker)(audio_dev_t *, void *), void *arg)
112888447a05SGarrett D'Amore {
112988447a05SGarrett D'Amore 	audio_dev_t	*d;
113088447a05SGarrett D'Amore 	boolean_t	cont;
113188447a05SGarrett D'Amore 	list_t		*l;
113288447a05SGarrett D'Amore 
113388447a05SGarrett D'Amore 	l = &auimpl_devs_by_number;
113488447a05SGarrett D'Amore 	rw_enter(&auimpl_dev_lock, RW_READER);
113588447a05SGarrett D'Amore 	for (d = list_head(l); d; d = list_next(l, d)) {
113688447a05SGarrett D'Amore 		cont = walker(d, arg);
113788447a05SGarrett D'Amore 		if (cont == AUDIO_WALK_STOP)
113888447a05SGarrett D'Amore 			break;
113988447a05SGarrett D'Amore 	}
114088447a05SGarrett D'Amore 	rw_exit(&auimpl_dev_lock);
114188447a05SGarrett D'Amore }
114288447a05SGarrett D'Amore 
114388447a05SGarrett D'Amore void
auclnt_dev_walk_engines(audio_dev_t * d,int (* walker)(audio_engine_t *,void *),void * arg)114488447a05SGarrett D'Amore auclnt_dev_walk_engines(audio_dev_t *d,
114588447a05SGarrett D'Amore     int (*walker)(audio_engine_t *, void *),
114688447a05SGarrett D'Amore     void *arg)
114788447a05SGarrett D'Amore {
114888447a05SGarrett D'Amore 	audio_engine_t *e;
114988447a05SGarrett D'Amore 	list_t *l = &d->d_engines;
115088447a05SGarrett D'Amore 
115188447a05SGarrett D'Amore 	mutex_enter(&d->d_lock);
115288447a05SGarrett D'Amore 	for (e = list_head(l); e != NULL; e = list_next(l, e)) {
115388447a05SGarrett D'Amore 		if (walker(e, arg) == AUDIO_WALK_STOP) {
115488447a05SGarrett D'Amore 			break;
115588447a05SGarrett D'Amore 		}
115688447a05SGarrett D'Amore 	}
115788447a05SGarrett D'Amore 	mutex_exit(&d->d_lock);
115888447a05SGarrett D'Amore }
115988447a05SGarrett D'Amore 
116088447a05SGarrett D'Amore int
auclnt_engine_get_format(audio_engine_t * e)116188447a05SGarrett D'Amore auclnt_engine_get_format(audio_engine_t *e)
116288447a05SGarrett D'Amore {
116388447a05SGarrett D'Amore 	return (ENG_FORMAT(e));
116488447a05SGarrett D'Amore }
116588447a05SGarrett D'Amore 
116688447a05SGarrett D'Amore int
auclnt_engine_get_channels(audio_engine_t * e)116788447a05SGarrett D'Amore auclnt_engine_get_channels(audio_engine_t *e)
116888447a05SGarrett D'Amore {
116988447a05SGarrett D'Amore 	return (ENG_CHANNELS(e));
117088447a05SGarrett D'Amore }
117188447a05SGarrett D'Amore 
117288447a05SGarrett D'Amore int
auclnt_engine_get_rate(audio_engine_t * e)117388447a05SGarrett D'Amore auclnt_engine_get_rate(audio_engine_t *e)
117488447a05SGarrett D'Amore {
117588447a05SGarrett D'Amore 	return (ENG_RATE(e));
117688447a05SGarrett D'Amore }
117788447a05SGarrett D'Amore 
117868c47f65SGarrett D'Amore uint_t
auclnt_engine_get_capab(audio_engine_t * e)117988447a05SGarrett D'Amore auclnt_engine_get_capab(audio_engine_t *e)
118088447a05SGarrett D'Amore {
118168c47f65SGarrett D'Amore 	uint_t capab = 0;
118288447a05SGarrett D'Amore 
118388447a05SGarrett D'Amore 	if (e->e_flags & ENGINE_INPUT_CAP) {
118488447a05SGarrett D'Amore 		capab |= AUDIO_CLIENT_CAP_RECORD;
118588447a05SGarrett D'Amore 	}
118688447a05SGarrett D'Amore 	if (e->e_flags & ENGINE_OUTPUT_CAP) {
118788447a05SGarrett D'Amore 		capab |= AUDIO_CLIENT_CAP_PLAY;
118888447a05SGarrett D'Amore 	}
118988447a05SGarrett D'Amore 	return (capab);
119088447a05SGarrett D'Amore }
119188447a05SGarrett D'Amore 
119288447a05SGarrett D'Amore /*
119388447a05SGarrett D'Amore  * This function suspends an engine.  The intent is to pause the
119488447a05SGarrett D'Amore  * engine temporarily so that it does not underrun while user threads
119588447a05SGarrett D'Amore  * are suspended.  The driver is still responsible for actually doing
119688447a05SGarrett D'Amore  * the driver suspend work -- all this does is put the engine in a
119788447a05SGarrett D'Amore  * paused state.  It does not prevent, for example, threads from
119888447a05SGarrett D'Amore  * accessing the hardware.
119988447a05SGarrett D'Amore  *
120088447a05SGarrett D'Amore  * A properly implemented driver won't even be aware of the existence
120188447a05SGarrett D'Amore  * of this routine -- the driver will just handle the suspend &
120288447a05SGarrett D'Amore  * resume.  At the point of suspend & resume, the driver will see that
120388447a05SGarrett D'Amore  * the engines are not running (as if all threads had "paused" it).
120488447a05SGarrett D'Amore  *
120588447a05SGarrett D'Amore  * Failure to execute either of the routines below is not critical,
120688447a05SGarrett D'Amore  * but will probably lead to underruns and overflows as the kernel
120788447a05SGarrett D'Amore  * driver gets resumed well in advance of the time when user threads
120888447a05SGarrett D'Amore  * are ready to start operation.
120988447a05SGarrett D'Amore  */
121068c47f65SGarrett D'Amore static void
auimpl_engine_suspend(audio_engine_t * e)121168c47f65SGarrett D'Amore auimpl_engine_suspend(audio_engine_t *e)
121268c47f65SGarrett D'Amore {
121368c47f65SGarrett D'Amore 	ASSERT(mutex_owned(&e->e_lock));
121468c47f65SGarrett D'Amore 
121568c47f65SGarrett D'Amore 	if (e->e_failed || e->e_suspended) {
121668c47f65SGarrett D'Amore 		e->e_suspended = B_TRUE;
121768c47f65SGarrett D'Amore 		return;
121868c47f65SGarrett D'Amore 	}
121968c47f65SGarrett D'Amore 	e->e_suspended = B_TRUE;
122068c47f65SGarrett D'Amore 	if (e->e_flags & ENGINE_INPUT) {
122168c47f65SGarrett D'Amore 		e->e_head = ENG_COUNT(e);
122268c47f65SGarrett D'Amore 		ENG_STOP(e);
122368c47f65SGarrett D'Amore 	}
122468c47f65SGarrett D'Amore 	if (e->e_flags & ENGINE_OUTPUT) {
122568c47f65SGarrett D'Amore 		e->e_tail = ENG_COUNT(e);
122668c47f65SGarrett D'Amore 		ENG_STOP(e);
122768c47f65SGarrett D'Amore 	}
122868c47f65SGarrett D'Amore }
122968c47f65SGarrett D'Amore 
123068c47f65SGarrett D'Amore static void
auimpl_engine_resume(audio_engine_t * e)123168c47f65SGarrett D'Amore auimpl_engine_resume(audio_engine_t *e)
123268c47f65SGarrett D'Amore {
123368c47f65SGarrett D'Amore 	ASSERT(mutex_owned(&e->e_lock));
123468c47f65SGarrett D'Amore 	ASSERT(e->e_suspended);
123568c47f65SGarrett D'Amore 
123668c47f65SGarrett D'Amore 	if (e->e_failed) {
123768c47f65SGarrett D'Amore 		/* No longer suspended, but still failed! */
123868c47f65SGarrett D'Amore 		e->e_suspended = B_FALSE;
123968c47f65SGarrett D'Amore 		return;
124068c47f65SGarrett D'Amore 	}
124168c47f65SGarrett D'Amore 
124268c47f65SGarrett D'Amore 	if (e->e_flags & (ENGINE_INPUT | ENGINE_OUTPUT)) {
124368c47f65SGarrett D'Amore 
124468c47f65SGarrett D'Amore 		auimpl_engine_reset(e);
124568c47f65SGarrett D'Amore 
124668c47f65SGarrett D'Amore 		if (e->e_flags & ENGINE_OUTPUT) {
124768c47f65SGarrett D'Amore 			auimpl_output_preload(e);
124868c47f65SGarrett D'Amore 		}
124968c47f65SGarrett D'Amore 
125068c47f65SGarrett D'Amore 		e->e_need_start = B_TRUE;
125168c47f65SGarrett D'Amore 	}
125268c47f65SGarrett D'Amore 	e->e_suspended = B_FALSE;
125368c47f65SGarrett D'Amore 	cv_broadcast(&e->e_cv);
125468c47f65SGarrett D'Amore }
125568c47f65SGarrett D'Amore 
125688447a05SGarrett D'Amore static int
auimpl_dev_suspend(audio_dev_t * d,void * dontcare)125768c47f65SGarrett D'Amore auimpl_dev_suspend(audio_dev_t *d, void *dontcare)
125888447a05SGarrett D'Amore {
125968c47f65SGarrett D'Amore 	list_t		*l;
126068c47f65SGarrett D'Amore 	audio_engine_t	*e;
126168c47f65SGarrett D'Amore 
126288447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(dontcare));
126388447a05SGarrett D'Amore 
126468c47f65SGarrett D'Amore 	mutex_enter(&d->d_lock);
126568c47f65SGarrett D'Amore 	mutex_enter(&d->d_ctrl_lock);
126668c47f65SGarrett D'Amore 	if (d->d_suspended) {
126768c47f65SGarrett D'Amore 		d->d_suspended++;
126868c47f65SGarrett D'Amore 		mutex_exit(&d->d_ctrl_lock);
126968c47f65SGarrett D'Amore 		mutex_exit(&d->d_lock);
127068c47f65SGarrett D'Amore 		return (AUDIO_WALK_CONTINUE);
127168c47f65SGarrett D'Amore 	}
127268c47f65SGarrett D'Amore 
127368c47f65SGarrett D'Amore 	d->d_suspended++;
127468c47f65SGarrett D'Amore 
127568c47f65SGarrett D'Amore 	(void) auimpl_save_controls(d);
127668c47f65SGarrett D'Amore 	mutex_exit(&d->d_ctrl_lock);
127768c47f65SGarrett D'Amore 
127868c47f65SGarrett D'Amore 	l = &d->d_engines;
127968c47f65SGarrett D'Amore 	for (e = list_head(l); e != NULL; e = list_next(l, e)) {
128068c47f65SGarrett D'Amore 		mutex_enter(&e->e_lock);
128168c47f65SGarrett D'Amore 		auimpl_engine_suspend(e);
128268c47f65SGarrett D'Amore 		mutex_exit(&e->e_lock);
128368c47f65SGarrett D'Amore 	}
128468c47f65SGarrett D'Amore 	mutex_exit(&d->d_lock);
128588447a05SGarrett D'Amore 
128688447a05SGarrett D'Amore 	return (AUDIO_WALK_CONTINUE);
128788447a05SGarrett D'Amore }
128888447a05SGarrett D'Amore 
128988447a05SGarrett D'Amore static int
auimpl_dev_resume(audio_dev_t * d,void * dontcare)129068c47f65SGarrett D'Amore auimpl_dev_resume(audio_dev_t *d, void *dontcare)
129188447a05SGarrett D'Amore {
129268c47f65SGarrett D'Amore 	list_t		*l;
129368c47f65SGarrett D'Amore 	audio_engine_t	*e;
129468c47f65SGarrett D'Amore 
129588447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(dontcare));
129668c47f65SGarrett D'Amore 
129768c47f65SGarrett D'Amore 	mutex_enter(&d->d_lock);
129868c47f65SGarrett D'Amore 	mutex_enter(&d->d_ctrl_lock);
129968c47f65SGarrett D'Amore 
130068c47f65SGarrett D'Amore 	ASSERT(d->d_suspended);
130168c47f65SGarrett D'Amore 	d->d_suspended--;
130268c47f65SGarrett D'Amore 	if (d->d_suspended) {
130368c47f65SGarrett D'Amore 		mutex_exit(&d->d_ctrl_lock);
130468c47f65SGarrett D'Amore 		mutex_exit(&d->d_lock);
130568c47f65SGarrett D'Amore 		return (AUDIO_WALK_CONTINUE);
130668c47f65SGarrett D'Amore 	}
130768c47f65SGarrett D'Amore 
130868c47f65SGarrett D'Amore 	(void) auimpl_restore_controls(d);
130968c47f65SGarrett D'Amore 	cv_broadcast(&d->d_ctrl_cv);
131068c47f65SGarrett D'Amore 	mutex_exit(&d->d_ctrl_lock);
131168c47f65SGarrett D'Amore 
131268c47f65SGarrett D'Amore 	l = &d->d_engines;
131368c47f65SGarrett D'Amore 	for (e = list_head(l); e != NULL; e = list_next(l, e)) {
131468c47f65SGarrett D'Amore 		mutex_enter(&e->e_lock);
131568c47f65SGarrett D'Amore 		auimpl_engine_resume(e);
131668c47f65SGarrett D'Amore 		mutex_exit(&e->e_lock);
131768c47f65SGarrett D'Amore 	}
131868c47f65SGarrett D'Amore 	mutex_exit(&d->d_lock);
131968c47f65SGarrett D'Amore 
132088447a05SGarrett D'Amore 	return (AUDIO_WALK_CONTINUE);
132188447a05SGarrett D'Amore }
132288447a05SGarrett D'Amore 
132388447a05SGarrett D'Amore boolean_t
auimpl_cpr(void * arg,int code)132488447a05SGarrett D'Amore auimpl_cpr(void *arg, int code)
132588447a05SGarrett D'Amore {
132688447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
132788447a05SGarrett D'Amore 
132888447a05SGarrett D'Amore 	switch (code) {
132988447a05SGarrett D'Amore 	case CB_CODE_CPR_CHKPT:
133068c47f65SGarrett D'Amore 		auclnt_walk_devs(auimpl_dev_suspend, NULL);
133188447a05SGarrett D'Amore 		return (B_TRUE);
133288447a05SGarrett D'Amore 
133388447a05SGarrett D'Amore 	case CB_CODE_CPR_RESUME:
133468c47f65SGarrett D'Amore 		auclnt_walk_devs(auimpl_dev_resume, NULL);
133588447a05SGarrett D'Amore 		return (B_TRUE);
133688447a05SGarrett D'Amore 
133788447a05SGarrett D'Amore 	default:
133888447a05SGarrett D'Amore 		return (B_FALSE);
133988447a05SGarrett D'Amore 	}
134088447a05SGarrett D'Amore }
134188447a05SGarrett D'Amore 
134268c47f65SGarrett D'Amore void
audio_dev_suspend(audio_dev_t * d)134368c47f65SGarrett D'Amore audio_dev_suspend(audio_dev_t *d)
134468c47f65SGarrett D'Amore {
134568c47f65SGarrett D'Amore 	(void) auimpl_dev_suspend(d, NULL);
134668c47f65SGarrett D'Amore }
134768c47f65SGarrett D'Amore 
134868c47f65SGarrett D'Amore void
audio_dev_resume(audio_dev_t * d)134968c47f65SGarrett D'Amore audio_dev_resume(audio_dev_t *d)
135068c47f65SGarrett D'Amore {
135168c47f65SGarrett D'Amore 	(void) auimpl_dev_resume(d, NULL);
135268c47f65SGarrett D'Amore }
135368c47f65SGarrett D'Amore 
135488447a05SGarrett D'Amore static callb_id_t	auimpl_cpr_id = 0;
135588447a05SGarrett D'Amore 
135688447a05SGarrett D'Amore void
auimpl_dev_init(void)135788447a05SGarrett D'Amore auimpl_dev_init(void)
135888447a05SGarrett D'Amore {
135988447a05SGarrett D'Amore 	rw_init(&auimpl_dev_lock, NULL, RW_DRIVER, NULL);
136088447a05SGarrett D'Amore 	list_create(&auimpl_devs_by_index, sizeof (struct audio_dev),
136188447a05SGarrett D'Amore 	    offsetof(struct audio_dev, d_by_index));
136288447a05SGarrett D'Amore 	list_create(&auimpl_devs_by_number, sizeof (struct audio_dev),
136388447a05SGarrett D'Amore 	    offsetof(struct audio_dev, d_by_number));
136488447a05SGarrett D'Amore 
136588447a05SGarrett D'Amore 	/*
136688447a05SGarrett D'Amore 	 * We "borrow" the CB_CL_CPR_PM class, which gets executed at
136788447a05SGarrett D'Amore 	 * about the right time for us.  It would be nice to have a
136888447a05SGarrett D'Amore 	 * new CB_CL_CPR_AUDIO class, but it isn't critical at this
136988447a05SGarrett D'Amore 	 * point.
137088447a05SGarrett D'Amore 	 *
137188447a05SGarrett D'Amore 	 * Note that we don't care about our thread id.
137288447a05SGarrett D'Amore 	 */
137388447a05SGarrett D'Amore 	auimpl_cpr_id = callb_add(auimpl_cpr, NULL, CB_CL_CPR_PM, "audio_cpr");
137488447a05SGarrett D'Amore }
137588447a05SGarrett D'Amore 
137688447a05SGarrett D'Amore void
auimpl_dev_fini(void)137788447a05SGarrett D'Amore auimpl_dev_fini(void)
137888447a05SGarrett D'Amore {
137988447a05SGarrett D'Amore 	(void) callb_delete(auimpl_cpr_id);
138088447a05SGarrett D'Amore 	list_destroy(&auimpl_devs_by_index);
138188447a05SGarrett D'Amore 	list_destroy(&auimpl_devs_by_number);
138288447a05SGarrett D'Amore 	rw_destroy(&auimpl_dev_lock);
138388447a05SGarrett D'Amore }
138488447a05SGarrett D'Amore 
138588447a05SGarrett D'Amore void
audio_engine_set_private(audio_engine_t * eng,void * prv)138688447a05SGarrett D'Amore audio_engine_set_private(audio_engine_t *eng, void *prv)
138788447a05SGarrett D'Amore {
138888447a05SGarrett D'Amore 	eng->e_private = prv;
138988447a05SGarrett D'Amore }
139088447a05SGarrett D'Amore 
139188447a05SGarrett D'Amore void *
audio_engine_get_private(audio_engine_t * eng)139288447a05SGarrett D'Amore audio_engine_get_private(audio_engine_t *eng)
139388447a05SGarrett D'Amore {
139488447a05SGarrett D'Amore 	return (eng->e_private);
139588447a05SGarrett D'Amore }
139688447a05SGarrett D'Amore 
139788447a05SGarrett D'Amore void
audio_dump_bytes(const uint8_t * w,int dcount)139888447a05SGarrett D'Amore audio_dump_bytes(const uint8_t *w, int dcount)
139988447a05SGarrett D'Amore {
140088447a05SGarrett D'Amore 	char		line[64];
140188447a05SGarrett D'Amore 	char		*s;
140288447a05SGarrett D'Amore 	int		i;
140388447a05SGarrett D'Amore 	const int	wrap = 16;
140488447a05SGarrett D'Amore 
140588447a05SGarrett D'Amore 	s = line;
140688447a05SGarrett D'Amore 	line[0] = 0;
140788447a05SGarrett D'Amore 
140888447a05SGarrett D'Amore 	cmn_err(CE_NOTE, "starting @ %p", (void *)w);
140988447a05SGarrett D'Amore 	for (i = 0; i < dcount; i++) {
141088447a05SGarrett D'Amore 
141188447a05SGarrett D'Amore 		(void) sprintf(s, " %02x", *w);
141288447a05SGarrett D'Amore 		s += strlen(s);
141388447a05SGarrett D'Amore 		w++;
141488447a05SGarrett D'Amore 
141588447a05SGarrett D'Amore 		if ((i % wrap) == (wrap - 1)) {
141688447a05SGarrett D'Amore 			cmn_err(CE_NOTE, "%08x:%s", i - (wrap - 1), line);
141788447a05SGarrett D'Amore 			line[0] = 0;
141888447a05SGarrett D'Amore 			s = line;
141988447a05SGarrett D'Amore 		}
142088447a05SGarrett D'Amore 	}
142188447a05SGarrett D'Amore 
142288447a05SGarrett D'Amore 	if ((i % wrap) != 0) {
142388447a05SGarrett D'Amore 		cmn_err(CE_NOTE, "%08x:%s", i - (i % wrap), line);
142488447a05SGarrett D'Amore 	}
142588447a05SGarrett D'Amore }
142688447a05SGarrett D'Amore 
142788447a05SGarrett D'Amore void
audio_dump_words(const uint16_t * w,int dcount)142888447a05SGarrett D'Amore audio_dump_words(const uint16_t *w, int dcount)
142988447a05SGarrett D'Amore {
143088447a05SGarrett D'Amore 	char		line[64];
143188447a05SGarrett D'Amore 	char		*s;
143288447a05SGarrett D'Amore 	int		i;
143388447a05SGarrett D'Amore 	const int	wrap = 8;
143488447a05SGarrett D'Amore 
143588447a05SGarrett D'Amore 	s = line;
143688447a05SGarrett D'Amore 	line[0] = 0;
143788447a05SGarrett D'Amore 
143888447a05SGarrett D'Amore 	cmn_err(CE_NOTE, "starting @ %p", (void *)w);
143988447a05SGarrett D'Amore 	for (i = 0; i < dcount; i++) {
144088447a05SGarrett D'Amore 
144188447a05SGarrett D'Amore 		(void) sprintf(s, " %04x", *w);
144288447a05SGarrett D'Amore 		s += strlen(s);
144388447a05SGarrett D'Amore 		w++;
144488447a05SGarrett D'Amore 
144588447a05SGarrett D'Amore 		if ((i % wrap) == (wrap - 1)) {
144688447a05SGarrett D'Amore 			cmn_err(CE_NOTE, "%08x:%s", i - (wrap - 1), line);
144788447a05SGarrett D'Amore 			line[0] = 0;
144888447a05SGarrett D'Amore 			s = line;
144988447a05SGarrett D'Amore 		}
145088447a05SGarrett D'Amore 	}
145188447a05SGarrett D'Amore 
145288447a05SGarrett D'Amore 	if ((i % wrap) != 0) {
145388447a05SGarrett D'Amore 		cmn_err(CE_NOTE, "%08x:%s", i - (i % wrap), line);
145488447a05SGarrett D'Amore 	}
145588447a05SGarrett D'Amore }
145688447a05SGarrett D'Amore 
145788447a05SGarrett D'Amore void
audio_dump_dwords(const uint32_t * w,int dcount)145888447a05SGarrett D'Amore audio_dump_dwords(const uint32_t *w, int dcount)
145988447a05SGarrett D'Amore {
146088447a05SGarrett D'Amore 	char		line[128];
146188447a05SGarrett D'Amore 	char		*s;
146288447a05SGarrett D'Amore 	int		i;
146388447a05SGarrett D'Amore 	const int	wrap = 4;
146488447a05SGarrett D'Amore 
146588447a05SGarrett D'Amore 	s = line;
146688447a05SGarrett D'Amore 	line[0] = 0;
146788447a05SGarrett D'Amore 
146888447a05SGarrett D'Amore 	cmn_err(CE_NOTE, "starting @ %p", (void *)w);
146988447a05SGarrett D'Amore 	for (i = 0; i < dcount; i++) {
147088447a05SGarrett D'Amore 
147188447a05SGarrett D'Amore 		(void) sprintf(s, " %08x", *w);
147288447a05SGarrett D'Amore 		s += strlen(s);
147388447a05SGarrett D'Amore 		w++;
147488447a05SGarrett D'Amore 
147588447a05SGarrett D'Amore 		if ((i % wrap) == (wrap - 1)) {
147688447a05SGarrett D'Amore 			cmn_err(CE_NOTE, "%08x:%s", i - (wrap - 1), line);
147788447a05SGarrett D'Amore 			line[0] = 0;
147888447a05SGarrett D'Amore 			s = line;
147988447a05SGarrett D'Amore 		}
148088447a05SGarrett D'Amore 	}
148188447a05SGarrett D'Amore 
148288447a05SGarrett D'Amore 	if ((i % wrap) != 0) {
148388447a05SGarrett D'Amore 		cmn_err(CE_NOTE, "%08x:%s", i - (i % wrap), line);
148488447a05SGarrett D'Amore 	}
148588447a05SGarrett D'Amore }
1486