diff options
author | Garrett D'Amore <gdamore@opensolaris.org> | 2009-05-06 20:21:23 -0700 |
---|---|---|
committer | Garrett D'Amore <gdamore@opensolaris.org> | 2009-05-06 20:21:23 -0700 |
commit | a702341c8e6cc834a456108a8bf5d22f031da3be (patch) | |
tree | 90d0769fbdf4c39650d3f8858762bb5dcd6c0b71 /usr | |
parent | 0ca46ac5a61f0a757ca72f469fdbb1859c3fa490 (diff) | |
download | illumos-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.c | 9 | ||||
-rw-r--r-- | usr/src/uts/common/io/audio/impl/audio_client.h | 2 | ||||
-rw-r--r-- | usr/src/uts/common/io/audio/impl/audio_format.c | 31 | ||||
-rw-r--r-- | usr/src/uts/common/io/audio/impl/audio_impl.h | 4 | ||||
-rw-r--r-- | usr/src/uts/common/io/audio/impl/audio_oss.c | 64 |
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; |