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