summaryrefslogtreecommitdiff
path: root/usr
diff options
context:
space:
mode:
authorGarrett D'Amore <gdamore@opensolaris.org>2009-05-06 20:21:23 -0700
committerGarrett D'Amore <gdamore@opensolaris.org>2009-05-06 20:21:23 -0700
commita702341c8e6cc834a456108a8bf5d22f031da3be (patch)
tree90d0769fbdf4c39650d3f8858762bb5dcd6c0b71 /usr
parent0ca46ac5a61f0a757ca72f469fdbb1859c3fa490 (diff)
downloadillumos-gate-a702341c8e6cc834a456108a8bf5d22f031da3be.tar.gz
6835822 some OSS applications have unacceptably large audio latency
Diffstat (limited to 'usr')
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_client.c9
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_client.h2
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_format.c31
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_impl.h4
-rw-r--r--usr/src/uts/common/io/audio/impl/audio_oss.c64
5 files changed, 96 insertions, 14 deletions
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 f3514df49b..8ed13f1d41 100644
--- a/usr/src/uts/common/io/audio/impl/audio_client.c
+++ b/usr/src/uts/common/io/audio/impl/audio_client.c
@@ -129,6 +129,15 @@ auclnt_get_nframes(audio_stream_t *sp)
return (sp->s_nframes);
}
+void
+auclnt_set_latency(audio_stream_t *sp, unsigned frags, unsigned bytes)
+{
+ mutex_enter(&sp->s_lock);
+ sp->s_hintfrags = (uint16_t)frags;
+ sp->s_hintsz = bytes;
+ mutex_exit(&sp->s_lock);
+}
+
uint64_t
auclnt_get_head(audio_stream_t *sp)
{
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 d38b1485a8..197a14bdff 100644
--- a/usr/src/uts/common/io/audio/impl/audio_client.h
+++ b/usr/src/uts/common/io/audio/impl/audio_client.h
@@ -102,6 +102,8 @@ uint64_t auclnt_get_tail(audio_stream_t *);
unsigned auclnt_get_hidx(audio_stream_t *);
unsigned auclnt_get_tidx(audio_stream_t *);
+void auclnt_set_latency(audio_stream_t *, unsigned, unsigned);
+
audio_stream_t *auclnt_input_stream(audio_client_t *);
audio_stream_t *auclnt_output_stream(audio_client_t *);
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 b7385e336c..81a5f80c4d 100644
--- a/usr/src/uts/common/io/audio/impl/audio_format.c
+++ b/usr/src/uts/common/io/audio/impl/audio_format.c
@@ -839,7 +839,36 @@ auimpl_format_setup(audio_stream_t *sp, audio_parms_t *uparms)
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;
- sp->s_nfrags = sp->s_allocsz / sp->s_fragbytes;
+
+ /*
+ * 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_user_parms = *uparms;
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 7c8099ea88..562ad3eb9a 100644
--- a/usr/src/uts/common/io/audio/impl/audio_impl.h
+++ b/usr/src/uts/common/io/audio/impl/audio_impl.h
@@ -84,7 +84,9 @@ struct audio_stream {
#define s_tidx s_buf.b_tidx
#define s_hidx s_buf.b_hidx
ddi_umem_cookie_t s_cookie;
- size_t s_allocsz;
+ uint32_t s_allocsz;
+ uint32_t s_hintsz; /* latency hints */
+ uint16_t s_hintfrags;
/*
* Various counters.
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 cbd08cc407..37ff03fbc7 100644
--- a/usr/src/uts/common/io/audio/impl/audio_oss.c
+++ b/usr/src/uts/common/io/audio/impl/audio_oss.c
@@ -508,6 +508,8 @@ oss_open(audio_client_t *c, int oflag)
((rv = auclnt_set_channels(osp, OSS_CHANNELS)) != 0)) {
goto failed;
}
+ /* default to 5 fragments to provide reasonable latency */
+ auclnt_set_latency(osp, 5, 0);
}
if (oflag & FREAD) {
@@ -516,6 +518,8 @@ oss_open(audio_client_t *c, int oflag)
((rv = auclnt_set_channels(isp, OSS_CHANNELS)) != 0)) {
goto failed;
}
+ /* default to 5 fragments to provide reasonable latency */
+ auclnt_set_latency(isp, 5, 0);
}
return (0);
@@ -1032,17 +1036,40 @@ sndctl_dsp_sync(audio_client_t *c)
static int
sndctl_dsp_setfragment(audio_client_t *c, int *fragp)
{
- _NOTE(ARGUNUSED(c));
- _NOTE(ARGUNUSED(fragp));
+ int bufsz;
+ int nfrags;
+ int fragsz;
+
+ nfrags = (*fragp) >> 16;
+ if ((nfrags >= 0x7fffU) || (nfrags < 2)) {
+ /* use infinite setting... no change */
+ return (0);
+ }
+
+ fragsz = (*fragp) & 0xffff;
+ if (fragsz > 16) {
+ /* basically too big, so, no change */
+ return (0);
+ }
+ bufsz = (1U << fragsz) * nfrags;
+
+ /*
+ * Now we have our desired buffer size, but we have to
+ * make sure we have a whole number of fragments >= 2, and
+ * less than the maximum.
+ */
+ bufsz = ((*fragp) >> 16) * (1U << (*fragp));
+ if (bufsz >= 65536) {
+ return (0);
+ }
+
+ /*
+ * We set the latency hints in terms of bytes, not fragments.
+ */
+ auclnt_set_latency(auclnt_output_stream(c), 0, bufsz);
+ auclnt_set_latency(auclnt_input_stream(c), 0, bufsz);
+
/*
- * We don't really implement this "properly" at this time.
- * The problems with this ioctl are various: the API insists
- * that fragment sizes be a power of two -- and we can't cope
- * with accurately reporting fragment sizes in the face of
- * mixing and format conversion.
- *
- * Well behaved applications should really not use this API.
- *
* According to the OSS API documentation, the values provided
* are nothing more than a "hint" and not to be relied upon
* anyway. And we aren't obligated to report the actual
@@ -1051,6 +1078,17 @@ sndctl_dsp_setfragment(audio_client_t *c, int *fragp)
return (0);
}
+static int
+sndctl_dsp_policy(audio_client_t *c, int *policy)
+{
+ int hint = *policy;
+ if ((hint >= 2) && (hint <= 10)) {
+ auclnt_set_latency(auclnt_input_stream(c), hint, 0);
+ auclnt_set_latency(auclnt_output_stream(c), hint, 0);
+ }
+ return (0);
+}
+
/*
* A word about recsrc, and playtgt ioctls: We don't allow ordinary DSP
* applications to change port configurations, because these could have a
@@ -1723,7 +1761,6 @@ oss_ioctl(audio_client_t *c, int cmd, intptr_t arg, int mode, cred_t *credp,
case SNDCTL_DSP_GET_RECSRC_NAMES:
rv = sndctl_dsp_get_recsrc_names(c, (oss_mixer_enuminfo *)data);
break;
- case SNDCTL_DSP_SUBDIVIDE:
case SNDCTL_DSP_SETFRAGMENT:
rv = sndctl_dsp_setfragment(c, (int *)data);
break;
@@ -1757,12 +1794,15 @@ oss_ioctl(audio_client_t *c, int cmd, intptr_t arg, int mode, cred_t *credp,
case SNDCTL_DSP_SETRECVOL:
rv = sndctl_dsp_setrecvol(c, (int *)data);
break;
+ case SNDCTL_DSP_SUBDIVIDE: /* Ignored */
case SNDCTL_DSP_SETDUPLEX: /* Ignored */
case SNDCTL_DSP_LOW_WATER: /* Ignored */
- case SNDCTL_DSP_POLICY: /* Ignored */
case SNDCTL_DSP_PROFILE: /* Ignored */
rv = 0;
break;
+ case SNDCTL_DSP_POLICY:
+ rv = sndctl_dsp_policy(c, (int *)data);
+ break;
case SNDCTL_DSP_GETBLKSIZE:
rv = sndctl_dsp_getblksize(c, (int *)data);
break;