summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett D'Amore <gdamore@opensolaris.org>2010-04-14 14:53:05 -0700
committerGarrett D'Amore <gdamore@opensolaris.org>2010-04-14 14:53:05 -0700
commit2c30fa4582c5d6c659e059e719c5f6163f7ef1e3 (patch)
tree88e5f5bd9bf2837dc1d7eb1fbe6784c239230cf2
parent27dd1e87cd3d939264769dd4af7e6a529cde001f (diff)
downloadillumos-joyent-2c30fa4582c5d6c659e059e719c5f6163f7ef1e3.tar.gz
6939934 boomer needs engine scheduling overhaul
6939935 PSARC 2010/096 Public Audio DDI
-rw-r--r--usr/src/uts/common/io/audio/ac97/ac97_cmi.c5
-rw-r--r--usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.c5
-rw-r--r--usr/src/uts/common/io/audio/drv/audiohd/audiohd.c5
-rw-r--r--usr/src/uts/common/io/audio/drv/audiols/audiols.c6
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_client.c52
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_client.h5
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_ctrl.c6
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_engine.c323
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_format.c194
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_impl.h20
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_input.c5
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_oss.c7
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_output.c5
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_sun.c7
-rw-r--r--usr/src/uts/common/sys/audio/audio_driver.h7
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 <sys/audio/audio_driver.h>
@@ -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 <sys/types.h>
@@ -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 <sys/types.h>
@@ -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 <sys/types.h>
@@ -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 = &target;
}
- 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 <sys/types.h>
@@ -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.