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