From 2c30fa4582c5d6c659e059e719c5f6163f7ef1e3 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Wed, 14 Apr 2010 14:53:05 -0700 Subject: [PATCH] 6939934 boomer needs engine scheduling overhaul 6939935 PSARC 2010/096 Public Audio DDI --- usr/src/uts/common/io/audio/ac97/ac97_cmi.c | 5 +- .../uts/common/io/audio/drv/audiocmi/audiocmi.c | 5 +- usr/src/uts/common/io/audio/drv/audiohd/audiohd.c | 5 +- usr/src/uts/common/io/audio/drv/audiols/audiols.c | 6 +- usr/src/uts/common/io/audio/impl/audio_client.c | 52 +--- usr/src/uts/common/io/audio/impl/audio_client.h | 5 +- usr/src/uts/common/io/audio/impl/audio_ctrl.c | 6 +- usr/src/uts/common/io/audio/impl/audio_engine.c | 323 +++++++++++++++------ usr/src/uts/common/io/audio/impl/audio_format.c | 194 +++++++------ usr/src/uts/common/io/audio/impl/audio_impl.h | 20 +- usr/src/uts/common/io/audio/impl/audio_input.c | 5 +- usr/src/uts/common/io/audio/impl/audio_oss.c | 7 +- usr/src/uts/common/io/audio/impl/audio_output.c | 5 +- usr/src/uts/common/io/audio/impl/audio_sun.c | 7 +- usr/src/uts/common/sys/audio/audio_driver.h | 7 +- 15 files changed, 394 insertions(+), 258 deletions(-) diff --git a/usr/src/uts/common/io/audio/ac97/ac97_cmi.c b/usr/src/uts/common/io/audio/ac97/ac97_cmi.c index 7cd0240f1f..e50d914be0 100644 --- a/usr/src/uts/common/io/audio/ac97/ac97_cmi.c +++ b/usr/src/uts/common/io/audio/ac97/ac97_cmi.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -290,7 +289,7 @@ cmi_setup_volume(ac97_t *ac) * override or fine tune AC'97 controls (i.e. creative cards) * use these C-Media codecs. */ - (void) audio_dev_add_soft_volume(ac_get_dev(ac)); + audio_dev_add_soft_volume(ac_get_dev(ac)); } void diff --git a/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.c b/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.c index 4e72d88e25..8aa989b03c 100644 --- a/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.c +++ b/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Purpose: Driver for CMEDIA CM8738 PCI audio controller. @@ -585,7 +584,7 @@ static void cmpci_add_controls(cmpci_dev_t *dev) { if (dev->softvol) { - (void) audio_dev_add_soft_volume(dev->adev); + audio_dev_add_soft_volume(dev->adev); } else { cmpci_alloc_ctrl(dev, CTL_VOLUME, 75); } diff --git a/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c b/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c index 252c3696d0..b5432e9228 100644 --- a/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c +++ b/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ #include @@ -1774,7 +1773,7 @@ audiohd_create_controls(audiohd_state_t *statep) /* * We always use soft volume control to adjust PCM volume. */ - (void) audio_dev_add_soft_volume(statep->adev); + audio_dev_add_soft_volume(statep->adev); /* Allocate other controls */ for (i = 0; i < statep->pathnum; i++) { diff --git a/usr/src/uts/common/io/audio/drv/audiols/audiols.c b/usr/src/uts/common/io/audio/drv/audiols/audiols.c index 44143a5d5e..ad5bd088c4 100644 --- a/usr/src/uts/common/io/audio/drv/audiols/audiols.c +++ b/usr/src/uts/common/io/audio/drv/audiols/audiols.c @@ -20,15 +20,13 @@ */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Purpose: Driver for the Creative Audigy LS sound card */ /* - * * Copyright (C) 4Front Technologies 1996-2009. */ @@ -1082,7 +1080,7 @@ audigyls_alloc_ctrl(audigyls_dev_t *dev, uint32_t num, uint64_t val) static void audigyls_add_controls(audigyls_dev_t *dev) { - (void) audio_dev_add_soft_volume(dev->adev); + audio_dev_add_soft_volume(dev->adev); audigyls_alloc_ctrl(dev, CTL_FRONT, 75 | (75 << 8)); audigyls_alloc_ctrl(dev, CTL_SURROUND, 75 | (75 << 8)); diff --git a/usr/src/uts/common/io/audio/impl/audio_client.c b/usr/src/uts/common/io/audio/impl/audio_client.c index d95a4faa97..69d7a36995 100644 --- a/usr/src/uts/common/io/audio/impl/audio_client.c +++ b/usr/src/uts/common/io/audio/impl/audio_client.c @@ -21,8 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #include @@ -93,13 +92,10 @@ auclnt_set_rate(audio_stream_t *sp, int rate) if ((rate < 5000) || (rate > 192000)) { return (EINVAL); } - mutex_enter(&sp->s_lock); - parms = *sp->s_user_parms; - if (rate != parms.p_rate) { + if (rate != sp->s_user_parms->p_rate) { parms.p_rate = rate; - rv = auimpl_format_setup(sp, &parms); + rv = auimpl_engine_setup(sp, 0, &parms, FORMAT_MSK_RATE); } - mutex_exit(&sp->s_lock); return (rv); } @@ -599,16 +595,13 @@ auclnt_set_format(audio_stream_t *sp, int fmt) } - mutex_enter(&sp->s_lock); - parms = *sp->s_user_parms; - /* * Optimization. Some personalities send us the same format * over and over again. (Sun personality does this * repeatedly.) setup_src is potentially expensive, so we * avoid doing it unless we really need to. */ - if (fmt != parms.p_format) { + if (fmt != sp->s_user_parms->p_format) { /* * Note that setting the format doesn't check that the * audio streams have been paused. As a result, any @@ -618,9 +611,8 @@ auclnt_set_format(audio_stream_t *sp, int fmt) * formats. */ parms.p_format = fmt; - rv = auimpl_format_setup(sp, &parms); + rv = auimpl_engine_setup(sp, 0, &parms, FORMAT_MSK_FMT); } - mutex_exit(&sp->s_lock); return (rv); } @@ -654,13 +646,10 @@ auclnt_set_channels(audio_stream_t *sp, int nchan) return (EINVAL); } - mutex_enter(&sp->s_lock); - parms = *sp->s_user_parms; - if (nchan != parms.p_nchan) { + if (nchan != sp->s_user_parms->p_nchan) { parms.p_nchan = nchan; - rv = auimpl_format_setup(sp, &parms); + rv = auimpl_engine_setup(sp, 0, &parms, FORMAT_MSK_CHAN); } - mutex_exit(&sp->s_lock); return (rv); } @@ -1295,13 +1284,12 @@ restart: int -auclnt_open(audio_client_t *c, uint_t fmts, int oflag) +auclnt_open(audio_client_t *c, int oflag) { audio_stream_t *sp; audio_dev_t *d = c->c_dev; int rv = 0; int flags; - audio_parms_t parms; flags = 0; if (oflag & FNDELAY) @@ -1309,34 +1297,14 @@ auclnt_open(audio_client_t *c, uint_t fmts, int oflag) if (oflag & FWRITE) { sp = &c->c_ostream; - rv = auimpl_engine_open(d, fmts, flags | ENGINE_OUTPUT, sp); - - if (rv != 0) { - goto done; - } - mutex_enter(&sp->s_lock); - parms = *sp->s_user_parms; - rv = auimpl_format_setup(sp, &parms); - mutex_exit(&sp->s_lock); - if (rv != 0) { + if ((rv = auimpl_engine_open(sp, flags | ENGINE_OUTPUT)) != 0) goto done; - } } if (oflag & FREAD) { sp = &c->c_istream; - rv = auimpl_engine_open(d, fmts, flags | ENGINE_INPUT, sp); - - if (rv != 0) { + if ((rv = auimpl_engine_open(sp, flags | ENGINE_INPUT)) != 0) goto done; - } - mutex_enter(&sp->s_lock); - parms = *sp->s_user_parms; - rv = auimpl_format_setup(sp, &parms); - mutex_exit(&sp->s_lock); - if (rv != 0) { - goto done; - } } done: diff --git a/usr/src/uts/common/io/audio/impl/audio_client.h b/usr/src/uts/common/io/audio/impl/audio_client.h index 2b3c650512..4641970ea2 100644 --- a/usr/src/uts/common/io/audio/impl/audio_client.h +++ b/usr/src/uts/common/io/audio/impl/audio_client.h @@ -20,8 +20,7 @@ */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _AUDIO_CLIENT_H @@ -113,7 +112,7 @@ audio_stream_t *auclnt_output_stream(audio_client_t *); int auclnt_get_oflag(audio_client_t *); -int auclnt_open(audio_client_t *, uint_t, int); +int auclnt_open(audio_client_t *, int); void auclnt_close(audio_client_t *); void auclnt_register_ops(minor_t, audio_client_ops_t *); diff --git a/usr/src/uts/common/io/audio/impl/audio_ctrl.c b/usr/src/uts/common/io/audio/impl/audio_ctrl.c index 4199ade9cb..2991f2e9df 100644 --- a/usr/src/uts/common/io/audio/impl/audio_ctrl.c +++ b/usr/src/uts/common/io/audio/impl/audio_ctrl.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #include @@ -235,7 +234,7 @@ audio_dev_del_control(audio_ctrl_t *ctrl) kmem_free(ctrl, sizeof (*ctrl)); } -int +void audio_dev_add_soft_volume(audio_dev_t *d) { audio_ctrl_desc_t desc; @@ -252,7 +251,6 @@ audio_dev_add_soft_volume(audio_dev_t *d) auimpl_get_pcmvol, auimpl_set_pcmvol, d); d->d_pcmvol = 75; } - return (0); } /* diff --git a/usr/src/uts/common/io/audio/impl/audio_engine.c b/usr/src/uts/common/io/audio/impl/audio_engine.c index 2f9954db5e..b62d4ecd3e 100644 --- a/usr/src/uts/common/io/audio/impl/audio_engine.c +++ b/usr/src/uts/common/io/audio/impl/audio_engine.c @@ -21,8 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #include @@ -360,46 +359,63 @@ auimpl_choose_format(int fmts) } int -auimpl_engine_open(audio_dev_t *d, int fmts, int flags, audio_stream_t *sp) +auimpl_engine_open(audio_stream_t *sp, int flags) { + return (auimpl_engine_setup(sp, flags, NULL, FORMAT_MSK_NONE)); +} + + +int +auimpl_engine_setup(audio_stream_t *sp, int flags, audio_parms_t *parms, + uint_t mask) +{ + audio_dev_t *d = sp->s_client->c_dev; audio_engine_t *e = NULL; + audio_parms_t uparms; list_t *list; - uint_t caps; + uint_t cap; int priority = 0; int rv = ENODEV; int sampsz; int i; int fragfr; + int fmts; - /* - * Engine selection: - * - * We try hard to avoid consuming an engine that can be used - * for another purpose. - * - */ + + mutex_enter(&d->d_lock); + + uparms = *sp->s_user_parms; + if (mask & FORMAT_MSK_FMT) + uparms.p_format = parms->p_format; + if (mask & FORMAT_MSK_RATE) + uparms.p_rate = parms->p_rate; + if (mask & FORMAT_MSK_CHAN) + uparms.p_nchan = parms->p_nchan; /* - * Which direction are we opening. (We must open exactly + * Which direction are we opening? (We must open exactly * one direction, otherwise the open is meaningless.) */ - if (flags & ENGINE_OUTPUT) - caps = ENGINE_OUTPUT_CAP; - else if (flags & ENGINE_INPUT) - caps = ENGINE_INPUT_CAP; - else - return (EINVAL); - list = &d->d_engines; + if (sp == &sp->s_client->c_ostream) { + cap = ENGINE_OUTPUT_CAP; + flags |= ENGINE_OUTPUT; + } else { + cap = ENGINE_INPUT_CAP; + flags |= ENGINE_INPUT; + } - mutex_enter(&d->d_lock); + if (uparms.p_format == AUDIO_FORMAT_AC3) { + fmts = AUDIO_FORMAT_AC3; + flags |= ENGINE_EXCLUSIVE; + } else { + fmts = AUDIO_FORMAT_PCM; + } - /* - * First we want to know if we already have "default" input - * and output engines. - */ + list = &d->d_engines; - /* if engine suspended, wait for it not to be */ + + /* If the device is suspended, wait for it to resume. */ while (d->d_suspended) { cv_wait(&d->d_ctrl_cv, &d->d_lock); } @@ -407,36 +423,72 @@ auimpl_engine_open(audio_dev_t *d, int fmts, int flags, audio_stream_t *sp) again: for (audio_engine_t *t = list_head(list); t; t = list_next(list, t)) { - int mypri; + int mypri; + int r; - /* make sure the engine can do what we want it to */ + /* Make sure the engine can do what we want it to. */ mutex_enter(&t->e_lock); - if ((((t->e_flags & caps) & caps) == 0) || - ((ENG_FORMAT(t) & fmts) == 0)) { + if ((t->e_flags & cap) == 0) { + mutex_exit(&t->e_lock); + continue; + } + + /* + * Open the engine early, as the inquiries to rate and format + * may not be accurate until this is done. + */ + if (list_is_empty(&t->e_streams)) { + if (ENG_OPEN(t, flags, &t->e_nframes, &t->e_data)) { + mutex_exit(&t->e_lock); + rv = EIO; + continue; + } + } + + if ((ENG_FORMAT(t) & fmts) == 0) { + if (list_is_empty(&t->e_streams)) + ENG_CLOSE(t); mutex_exit(&t->e_lock); continue; } - /* if in failed state, don't assign a new stream here */ + + /* If it is in failed state, don't use this engine. */ if (t->e_failed) { + if (list_is_empty(&t->e_streams)) + ENG_CLOSE(t); mutex_exit(&t->e_lock); - rv = EIO; + rv = rv ? EIO : 0; continue; } - /* if engine is in exclusive use, can't do it */ - if (t->e_flags & ENGINE_EXCLUSIVE) { + /* + * If the engine is in exclusive use, we can't use it. + * This is intended for use with AC3 or digital + * streams that cannot tolerate mixing. + */ + if ((t->e_flags & ENGINE_EXCLUSIVE) && (t != sp->s_engine)) { + if (list_is_empty(&t->e_streams)) + ENG_CLOSE(t); mutex_exit(&t->e_lock); - rv = EBUSY; + rv = rv ? EBUSY : 0; continue; } - /* if engine is in incompatible use, can't do it */ + /* + * If the engine is in use incompatibly, we can't use + * it. This should only happen for half-duplex audio + * devices. I've not seen any of these that are + * recent enough to be supported by Solaris. + */ if (((flags & ENGINE_INPUT) && (t->e_flags & ENGINE_OUTPUT)) || ((flags & ENGINE_OUTPUT) && (t->e_flags & ENGINE_INPUT))) { + if (list_is_empty(&t->e_streams)) + ENG_CLOSE(t); mutex_exit(&t->e_lock); - rv = EBUSY; + /* Only override the ENODEV or EIO. */ + rv = rv ? EBUSY : 0; continue; } @@ -454,26 +506,99 @@ again: * results in denying service to any client. */ + /* + * This engine *can* support us, so we should no longer + * have a failure mode. + */ rv = 0; - mypri = 2000; + mypri = (1U << 0); - /* try not to pick on idle engines */ - if (list_is_empty(&t->e_streams)) { - mypri -= 1000; + + /* + * Mixing is cheap, so try not to pick on idle + * engines. This avoids burning bus bandwidth (which + * may be precious for certain classes of traffic). + * Note that idleness is given a low priority compared + * to the other considerations. + * + * We also use this opportunity open the engine, if + * not already done so, so that our parameter + * inquiries will be valid. + */ + if (!list_is_empty(&t->e_streams)) + mypri |= (1U << 1); + + /* + * Slight preference is given to reuse an engine that + * we might already be using. + */ + if (t == sp->s_engine) + mypri |= (1U << 2); + + + /* + * Sample rate conversion avoidance. Upsampling + * requires multiplications and is moderately + * expensive. Downsampling requires division and is + * quite expensive, and hence to be avoided if at all + * possible. + */ + r = ENG_RATE(t); + if (uparms.p_rate == r) { + /* + * No conversion needed at all. This is ideal. + */ + mypri |= (1U << 4) | (1U << 3); + } else { + int src, dst; + + if (flags & ENGINE_INPUT) { + src = r; + dst = uparms.p_rate; + } else { + src = uparms.p_rate; + dst = r; + } + if ((src < dst) && ((dst % src) == 0)) { + /* + * Pure upsampling only. This + * penalizes any engine which requires + * downsampling. + */ + mypri |= (1U << 3); + } } - /* try not to pick on duplex engines first */ - if ((t->e_flags & ENGINE_CAPS) != caps) { - mypri -= 100; + /* + * Try not to pick on duplex engines. This way we + * leave engines that can be used for recording or + * playback available as such. All modern drivers + * use separate unidirectional engines for playback + * and record. + */ + if ((t->e_flags & ENGINE_CAPS) == cap) { + mypri |= (1U << 5); } - /* try not to pick on engines that can do other formats */ - if (t->e_format & ~fmts) { - mypri -= 10; + /* + * Try not to pick on engines that can do other + * formats. This will generally be false, but if it + * happens we pretty strongly avoid using a limited + * resource. + */ + if ((t->e_format & ~fmts) == 0) { + mypri |= (1U << 6); } if (mypri > priority) { if (e != NULL) { + /* + * If we opened this for our own use + * and we are no longer using it, then + * close it back down. + */ + if (list_is_empty(&e->e_streams)) + ENG_CLOSE(e); mutex_exit(&e->e_lock); } e = t; @@ -481,6 +606,11 @@ again: } else { mutex_exit(&t->e_lock); } + + /* + * Locking: at this point, if we have an engine, "e", it is + * locked. No other engines should have a lock held. + */ } if ((rv == EBUSY) && ((flags & ENGINE_NDELAY) == 0)) { @@ -501,13 +631,52 @@ again: ASSERT(e != NULL); ASSERT(mutex_owned(&e->e_lock)); - /* - * If the engine is already open, there is no need for further - * work. The first open will be relatively expensive, but - * subsequent opens should be as cheap as possible. - */ - if (!list_is_empty(&e->e_streams)) { - rv = 0; + if (sp->s_engine && (sp->s_engine != e)) { + /* + * If this represents a potential engine change, then + * we close off everything, and start anew. This turns + * out to be vastly simpler than trying to close all + * the races associated with a true hand off. This + * ought to be relatively uncommon (changing engines). + */ + + /* Drop the new reference. */ + if (list_is_empty(&e->e_streams)) + ENG_CLOSE(e); + mutex_exit(&e->e_lock); + mutex_exit(&d->d_lock); + + auimpl_engine_close(sp); + + /* Try again. */ + return (auimpl_engine_setup(sp, flags, parms, mask)); + } + + if (sp->s_engine == NULL) { + /* + * Add a reference to this engine if we don't already + * have one. + */ + sp->s_engine = e; + + if (!list_is_empty(&e->e_streams)) { + /* + * If the engine is already open, there is no + * need for further work. The first open will + * be relatively expensive, but subsequent + * opens should be as cheap as possible. + */ + list_insert_tail(&e->e_streams, sp); + goto ok; + } + list_insert_tail(&e->e_streams, sp); + + } else { + ASSERT(sp->s_engine == e); + /* + * No change in engine... hence don't reprogram the + * engine, and don't change references. + */ goto ok; } @@ -515,7 +684,7 @@ again: e->e_nchan = ENG_CHANNELS(e); e->e_rate = ENG_RATE(e); - /* Find out the "best" sample format supported by the device */ + /* Select format converters for the engine. */ switch (e->e_format) { case AUDIO_FORMAT_S24_NE: e->e_export = auimpl_export_24ne; @@ -566,7 +735,7 @@ again: goto done; } - /* sanity test a few values */ + /* Sanity test a few values. */ if ((e->e_nchan < 0) || (e->e_nchan > AUDIO_MAX_CHANNELS) || (e->e_rate < 5000) || (e->e_rate > 192000)) { audio_dev_warn(d, "bad engine channels or rate"); @@ -574,11 +743,6 @@ again: goto done; } - rv = ENG_OPEN(e, &e->e_nframes, &e->e_data); - if (rv != 0) { - audio_dev_warn(d, "unable to open engine"); - goto done; - } if ((e->e_nframes <= (fragfr * 2)) || (e->e_data == NULL)) { audio_dev_warn(d, "improper engine configuration"); rv = EINVAL; @@ -586,7 +750,6 @@ again: } e->e_framesz = e->e_nchan * sampsz; - e->e_intrs = audio_intrhz; e->e_fragfr = fragfr; e->e_head = 0; e->e_tail = 0; @@ -624,16 +787,7 @@ again: } } - e->e_flags |= (ENGINE_OPEN | (flags & (ENGINE_OUTPUT | ENGINE_INPUT))); - - /* - * Start the output callback to populate the engine on - * startup. This avoids a false underrun when we're first - * starting up. - */ - if (flags & ENGINE_OUTPUT) { - auimpl_output_preload(e); - } + e->e_flags |= flags; /* * Arrange for the engine to be started. We defer this to the @@ -646,7 +800,14 @@ again: */ e->e_need_start = B_TRUE; - if (e->e_flags & ENGINE_OUTPUT) { + if (flags & ENGINE_OUTPUT) { + /* + * Start the output callback to populate the engine on + * startup. This avoids a false underrun when we're + * first starting up. + */ + auimpl_output_preload(e); + e->e_periodic = ddi_periodic_add(auimpl_output_callback, e, NANOSEC / audio_intrhz, audio_priority); } else { @@ -658,12 +819,15 @@ ok: sp->s_phys_parms->p_rate = e->e_rate; sp->s_phys_parms->p_nchan = e->e_nchan; - list_insert_tail(&e->e_streams, sp); - sp->s_engine = e; + /* Configure the engine. */ + mutex_enter(&sp->s_lock); + rv = auimpl_format_setup(sp, parms, mask); + mutex_exit(&sp->s_lock); done: mutex_exit(&e->e_lock); mutex_exit(&d->d_lock); + return (rv); } @@ -672,7 +836,6 @@ auimpl_engine_close(audio_stream_t *sp) { audio_engine_t *e = sp->s_engine; audio_dev_t *d; - ddi_periodic_t p = 0; if (e == NULL) return; @@ -683,21 +846,21 @@ auimpl_engine_close(audio_stream_t *sp) while (d->d_suspended) { cv_wait(&d->d_ctrl_cv, &d->d_lock); } + mutex_enter(&e->e_lock); sp->s_engine = NULL; list_remove(&e->e_streams, sp); if (list_is_empty(&e->e_streams)) { ENG_STOP(e); - p = e->e_periodic; + ddi_periodic_delete(e->e_periodic); + e->e_periodic = 0; e->e_flags &= ENGINE_DRIVER_FLAGS; ENG_CLOSE(e); } mutex_exit(&e->e_lock); + cv_broadcast(&d->d_cv); mutex_exit(&d->d_lock); - if (p != 0) { - ddi_periodic_delete(p); - } } int @@ -813,7 +976,6 @@ auimpl_engine_ksupdate(kstat_t *ksp, int rw) st->st_format.value.ui32 = e->e_format; st->st_nchan.value.ui32 = e->e_nchan; st->st_rate.value.ui32 = e->e_rate; - st->st_intrs.value.ui32 = e->e_intrs; st->st_errors.value.ui32 = e->e_errors; st->st_engine_underruns.value.ui32 = e->e_underruns; st->st_engine_overruns.value.ui32 = e->e_overruns; @@ -859,7 +1021,6 @@ auimpl_engine_ksinit(audio_dev_t *d, audio_engine_t *e) kstat_named_init(&st->st_format, "format", KSTAT_DATA_UINT32); kstat_named_init(&st->st_nchan, "channels", KSTAT_DATA_UINT32); kstat_named_init(&st->st_rate, "rate", KSTAT_DATA_UINT32); - kstat_named_init(&st->st_intrs, "intrhz", KSTAT_DATA_UINT32); kstat_named_init(&st->st_errors, "errors", KSTAT_DATA_UINT32); kstat_named_init(&st->st_engine_overruns, "engine_overruns", KSTAT_DATA_UINT32); diff --git a/usr/src/uts/common/io/audio/impl/audio_format.c b/usr/src/uts/common/io/audio/impl/audio_format.c index 81a5f80c4d..9656cbad3f 100644 --- a/usr/src/uts/common/io/audio/impl/audio_format.c +++ b/usr/src/uts/common/io/audio/impl/audio_format.c @@ -21,8 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -37,6 +36,7 @@ #include "audio_impl.h" #include "audio_grc3.h" +extern uint_t audio_intrhz; /* * Note: In the function below, the division by the number of channels is @@ -690,34 +690,59 @@ static const struct audio_format_info { }; int -auimpl_format_setup(audio_stream_t *sp, audio_parms_t *uparms) +auimpl_format_setup(audio_stream_t *sp, audio_parms_t *parms, uint_t mask) { - audio_parms_t *source = &sp->s_cnv_src_parms; - audio_parms_t *target = &sp->s_cnv_dst_parms; + audio_parms_t source; + audio_parms_t target; + audio_parms_t *uparms; audio_cnv_func_t converter = NULL; const struct audio_format_info *info; int expand = AUDIO_UNIT_EXPAND; unsigned cnv_sampsz = sizeof (uint32_t); unsigned cnv_max; + boolean_t needsrc = B_FALSE; + + uint_t framesz; + uint_t fragfr; + uint_t fragbytes; + uint_t nfrags; + + ASSERT(mutex_owned(&sp->s_lock)); + + source = sp->s_cnv_src_parms; + target = sp->s_cnv_dst_parms; if (sp == &sp->s_client->c_ostream) { - source = uparms; + if (mask & FORMAT_MSK_FMT) + source.p_format = parms->p_format; + if (mask & FORMAT_MSK_RATE) + source.p_rate = parms->p_rate; + if (mask & FORMAT_MSK_CHAN) + source.p_nchan = parms->p_nchan; + uparms = &source; } else { - target = uparms; + if (mask & FORMAT_MSK_FMT) + target.p_format = parms->p_format; + if (mask & FORMAT_MSK_RATE) + target.p_rate = parms->p_rate; + if (mask & FORMAT_MSK_CHAN) + target.p_nchan = parms->p_nchan; + uparms = ⌖ } - ASSERT(mutex_owned(&sp->s_lock)); - /* * At least one of the source or target are S24_NE. * * If we have a signed/native endian format, then pick an * optimized converter. While at it, ensure that a valid * format is selected. + * + * After this function executes, "info" will point to the + * format information for the user parameters. */ - if (source->p_format != AUDIO_FORMAT_S24_NE) { + if (source.p_format != AUDIO_FORMAT_S24_NE) { for (info = &audio_format_info[0]; info->sampsize; info++) { - if (source->p_format == info->format) { + if (source.p_format == info->format) { converter = info->from; expand *= sizeof (int32_t); expand /= info->sampsize; @@ -726,61 +751,55 @@ auimpl_format_setup(audio_stream_t *sp, audio_parms_t *uparms) break; } } - if (info->format == AUDIO_FORMAT_NONE) { - audio_dev_warn(sp->s_client->c_dev, - "invalid source format selected"); - return (EINVAL); - } - - } else if (target->p_format != AUDIO_FORMAT_S24_NE) { + } else { + /* + * Target format. Note that this case is also taken + * if we're operating on S24_NE data. In that case + * the converter will be NULL and expand will not be + * altered. + */ for (info = &audio_format_info[0]; info->sampsize; info++) { - if (target->p_format == info->format) { + if (target.p_format == info->format) { converter = info->to; expand *= info->sampsize; expand /= sizeof (int32_t); break; } } - if (info->format == AUDIO_FORMAT_NONE) { - audio_dev_warn(sp->s_client->c_dev, - "invalid target format selected"); - return (EINVAL); - } } + if (info->format == AUDIO_FORMAT_NONE) { + audio_dev_warn(sp->s_client->c_dev, "invalid format selected"); + return (EINVAL); + } + + + ASSERT(info->sampsize); - if (source->p_nchan != target->p_nchan) { + if (source.p_nchan != target.p_nchan) { /* * if channels need conversion, then we must use the * default. */ converter = cnv_default; - expand *= target->p_nchan; - expand /= source->p_nchan; + expand *= target.p_nchan; + expand /= source.p_nchan; } - if (source->p_rate != target->p_rate) { - /* - * We need SRC; if we can avoid data conversion, do so. - */ - setup_src(sp, source->p_rate, target->p_rate, - source->p_nchan, target->p_nchan); - + if (source.p_rate != target.p_rate) { + needsrc = B_TRUE; converter = (converter == NULL) ? cnv_srconly : cnv_default; - expand *= target->p_rate; - expand /= source->p_rate; + expand *= target.p_rate; + expand /= source.p_rate; } - ASSERT(sp->s_engine); - ASSERT(sp->s_engine->e_intrs); - /* * Figure out the size of the conversion buffer we need. We * assume room for two full source fragments, which ought to * be enough, even with rounding errors. */ - cnv_max = 2 * (source->p_rate / sp->s_engine->e_intrs) * - cnv_sampsz * source->p_nchan; + cnv_max = 2 * (source.p_rate / audio_intrhz) * + cnv_sampsz * source.p_nchan; /* * If the conversion will cause us to expand fragments, then @@ -793,8 +812,45 @@ auimpl_format_setup(audio_stream_t *sp, audio_parms_t *uparms) cnv_max /= AUDIO_UNIT_EXPAND; } + framesz = info->sampsize * uparms->p_nchan; + fragfr = (uparms->p_rate / audio_intrhz); + fragbytes = fragfr * framesz; + + /* + * We need to "tune" the buffer and fragment counts for some + * uses... OSS applications may like to configure a low + * latency, and they rely upon write() to block to prevent too + * much data from being queued up. + */ + if (sp->s_hintsz) { + nfrags = sp->s_hintsz / fragbytes; + } else if (sp->s_hintfrags) { + nfrags = sp->s_hintfrags; + } else { + nfrags = sp->s_allocsz / fragbytes; + } + + /* + * Now make sure that the hint works -- we need at least 2 fragments, + * and we need to fit within the room allocated to us. + */ + if (nfrags < 2) { + nfrags = 2; + } + while ((nfrags * fragbytes) > sp->s_allocsz) { + nfrags--; + } + /* if the resulting configuration is invalid, note it */ + if (nfrags < 2) { + return (EINVAL); + } + /* * Now we need to allocate space. + * + * NB: Once the allocation succeeds, we must not fail. We are + * modifying the the stream settings and these changes must be + * made atomically. */ if (sp->s_cnv_max < cnv_max) { uint32_t *buf0, *buf1; @@ -822,55 +878,19 @@ auimpl_format_setup(audio_stream_t *sp, audio_parms_t *uparms) sp->s_cnv_max = cnv_max; } - /* - * NB: From here on, we must not fail. - */ - - /* - * Configure default fragment setup. - */ - for (info = &audio_format_info[0]; info->sampsize; info++) { - if (uparms->p_format == info->format) { - break; - } + /* Set up the SRC state if we will be using SRC. */ + if (needsrc) { + setup_src(sp, source.p_rate, target.p_rate, + source.p_nchan, target.p_nchan); } - ASSERT(info->sampsize); - - sp->s_framesz = info->sampsize * uparms->p_nchan; - sp->s_fragfr = (uparms->p_rate / sp->s_engine->e_intrs); - sp->s_fragbytes = sp->s_fragfr * sp->s_framesz; - /* - * We need to "tune" the buffer and fragment counts for some - * uses... OSS applications may like to configure a low - * latency, and they rely upon write() to block to prevent too - * much data from being queued up. - */ - if (sp->s_hintsz) { - sp->s_nfrags = sp->s_hintsz / sp->s_fragbytes; - } else if (sp->s_hintfrags) { - sp->s_nfrags = sp->s_hintfrags; - } else { - sp->s_nfrags = sp->s_allocsz / sp->s_fragbytes; - } - - /* - * Now make sure that the hint works -- we need at least 2 fragments, - * and we need to fit within the room allocated to us. - */ - if (sp->s_nfrags < 2) { - sp->s_nfrags = 2; - } - while ((sp->s_nfrags * sp->s_fragbytes) > sp->s_allocsz) { - sp->s_nfrags--; - } - /* if the resulting configuration is invalid, note it */ - if (sp->s_nfrags < 2) { - return (EINVAL); - } - sp->s_nframes = sp->s_nfrags * sp->s_fragfr; - sp->s_nbytes = sp->s_nframes * sp->s_framesz; + sp->s_framesz = framesz; + sp->s_fragfr = fragfr; + sp->s_fragbytes = fragbytes; + sp->s_nfrags = nfrags; + sp->s_nframes = nfrags * fragfr; + sp->s_nbytes = sp->s_nframes * framesz; *sp->s_user_parms = *uparms; sp->s_converter = converter; diff --git a/usr/src/uts/common/io/audio/impl/audio_impl.h b/usr/src/uts/common/io/audio/impl/audio_impl.h index 67f4b27e6c..1152d4242f 100644 --- a/usr/src/uts/common/io/audio/impl/audio_impl.h +++ b/usr/src/uts/common/io/audio/impl/audio_impl.h @@ -21,8 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _AUDIO_IMPL_H @@ -237,7 +236,6 @@ struct audio_stats { kstat_named_t st_format; kstat_named_t st_nchan; kstat_named_t st_rate; - kstat_named_t st_intrs; kstat_named_t st_errors; kstat_named_t st_engine_underruns; kstat_named_t st_engine_overruns; @@ -286,7 +284,6 @@ struct audio_engine { uint_t e_fragfr; uint_t e_playahead; - int e_intrs; int e_errors; int e_overruns; int e_underruns; @@ -323,8 +320,7 @@ struct audio_engine { * List of of streams attached to this engine. */ list_t e_streams; - int e_nrunning; - int e_suspended; + boolean_t e_suspended; boolean_t e_failed; boolean_t e_need_start; @@ -433,7 +429,12 @@ struct audio_ctrl { /* audio_format.c */ int auimpl_format_alloc(audio_stream_t *); void auimpl_format_free(audio_stream_t *); -int auimpl_format_setup(audio_stream_t *, audio_parms_t *); +int auimpl_format_setup(audio_stream_t *, audio_parms_t *, uint_t); +#define FORMAT_MSK_NONE (0x0) +#define FORMAT_MSK_FMT (0x1) +#define FORMAT_MSK_RATE (0x2) +#define FORMAT_MSK_CHAN (0x4) +#define FOMMAT_MSK_ALL (0x7) /* audio_output.c */ void auimpl_export_16ne(audio_engine_t *, uint_t, uint_t); @@ -481,7 +482,8 @@ audio_dev_t *auimpl_dev_hold_by_index(int); void auimpl_dev_release(audio_dev_t *); int auimpl_choose_format(int); -int auimpl_engine_open(audio_dev_t *, int, int, audio_stream_t *); +int auimpl_engine_open(audio_stream_t *, int); +int auimpl_engine_setup(audio_stream_t *, int, audio_parms_t *, uint_t); void auimpl_engine_close(audio_stream_t *); void auimpl_dev_walk_engines(audio_dev_t *, @@ -502,7 +504,7 @@ void auimpl_dev_vwarn(audio_dev_t *, const char *, va_list); #define ENG_QLEN(e) E_OP(e, qlen)(E_PRV(e)) #define ENG_PLAYAHEAD(e) E_OP(e, playahead)(E_PRV(e)) #define ENG_CLOSE(e) E_OP(e, close)(E_PRV(e)) -#define ENG_OPEN(e, nf, d) E_OP(e, open)(E_PRV(e), e->e_flags, nf, d) +#define ENG_OPEN(e, flg, nf, d) E_OP(e, open)(E_PRV(e), flg, nf, d) #define ENG_CHINFO(e, c, o, i) E_OP(e, chinfo(E_PRV(e), c, o, i)) /* audio_sun.c */ diff --git a/usr/src/uts/common/io/audio/impl/audio_input.c b/usr/src/uts/common/io/audio/impl/audio_input.c index ffc743e799..5d1e191057 100644 --- a/usr/src/uts/common/io/audio/impl/audio_input.c +++ b/usr/src/uts/common/io/audio/impl/audio_input.c @@ -21,8 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -149,7 +148,7 @@ auimpl_input_callback(void *arg) mutex_enter(&e->e_lock); - if (e->e_suspended || e->e_failed) { + if (e->e_suspended || e->e_failed || !e->e_periodic) { mutex_exit(&e->e_lock); return; } diff --git a/usr/src/uts/common/io/audio/impl/audio_oss.c b/usr/src/uts/common/io/audio/impl/audio_oss.c index b9c79da4f4..3729cc6f04 100644 --- a/usr/src/uts/common/io/audio/impl/audio_oss.c +++ b/usr/src/uts/common/io/audio/impl/audio_oss.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #include @@ -484,7 +483,7 @@ oss_open(audio_client_t *c, int oflag) osp = auclnt_output_stream(c); /* note that OSS always uses nonblocking open() semantics */ - if ((rv = auclnt_open(c, AUDIO_FORMAT_PCM, oflag | FNDELAY)) != 0) { + if ((rv = auclnt_open(c, oflag | FNDELAY)) != 0) { return (rv); } @@ -1927,7 +1926,7 @@ ossmix_open(audio_client_t *c, int oflag) _NOTE(ARGUNUSED(oflag)); - if ((rv = auclnt_open(c, AUDIO_FORMAT_NONE, 0)) != 0) { + if ((rv = auclnt_open(c, 0)) != 0) { return (rv); } diff --git a/usr/src/uts/common/io/audio/impl/audio_output.c b/usr/src/uts/common/io/audio/impl/audio_output.c index 1b59c13a5d..7b8f769abe 100644 --- a/usr/src/uts/common/io/audio/impl/audio_output.c +++ b/usr/src/uts/common/io/audio/impl/audio_output.c @@ -21,8 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -462,7 +461,7 @@ auimpl_output_callback(void *arg) mutex_enter(&e->e_lock); - if (e->e_suspended || e->e_failed) { + if (e->e_suspended || e->e_failed || !e->e_periodic) { mutex_exit(&e->e_lock); return; } diff --git a/usr/src/uts/common/io/audio/impl/audio_sun.c b/usr/src/uts/common/io/audio/impl/audio_sun.c index 0758d1afa4..ec6e8aa5d9 100644 --- a/usr/src/uts/common/io/audio/impl/audio_sun.c +++ b/usr/src/uts/common/io/audio/impl/audio_sun.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -1069,7 +1068,7 @@ devaudio_open(audio_client_t *c, int oflag) { int rv; - if ((rv = auclnt_open(c, AUDIO_FORMAT_PCM, oflag)) != 0) { + if ((rv = auclnt_open(c, oflag)) != 0) { return (rv); } @@ -1095,7 +1094,7 @@ devaudioctl_open(audio_client_t *c, int oflag) oflag &= ~(FWRITE | FREAD); - if ((rv = auclnt_open(c, AUDIO_FORMAT_NONE, 0)) != 0) { + if ((rv = auclnt_open(c, 0)) != 0) { return (rv); } diff --git a/usr/src/uts/common/sys/audio/audio_driver.h b/usr/src/uts/common/sys/audio/audio_driver.h index c78ea9357e..3b124b88c6 100644 --- a/usr/src/uts/common/sys/audio/audio_driver.h +++ b/usr/src/uts/common/sys/audio/audio_driver.h @@ -21,8 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SYS_AUDIO_AUDIO_DRIVER_H @@ -165,8 +164,6 @@ void audio_dump_dwords(const uint32_t *w, int dcount); #define ENGINE_OUTPUT (1U << 16) /* fields not for driver use */ #define ENGINE_INPUT (1U << 17) -#define ENGINE_OPEN (1U << 18) -#define ENGINE_RUNNING (1U << 19) #define ENGINE_EXCLUSIVE (1U << 20) /* exclusive use, e.g. AC3 */ #define ENGINE_NDELAY (1U << 21) /* non-blocking open */ @@ -209,7 +206,7 @@ audio_ctrl_t *audio_dev_add_control(audio_dev_t *, * result in loss range. The control is implemented using * AUDIO_CTRL_ID_VOLUME. */ -int audio_dev_add_soft_volume(audio_dev_t *); +void audio_dev_add_soft_volume(audio_dev_t *); /* * This will remove a control from an audio device. -- 2.11.4.GIT