[alsa-devel] [PATCH]fix mmap emulation bug of recording doesn't work
Record doesn't work if enabling mmap emulation and rate conversion needed, this patch fix this bug.
diff -uNr alsa-lib-1.0.14a/src/pcm/pcm_hw.c alsa-lib-new/src/pcm/pcm_hw.c --- alsa-lib-1.0.14a/src/pcm/pcm_hw.c 2007-06-11 16:53:13.000000000 +0800 +++ alsa-lib-new/src/pcm/pcm_hw.c 2007-07-04 12:16:31.000000000 +0800 @@ -96,6 +96,7 @@ int shadow_appl_ptr: 1, avail_update_flag: 1, mmap_shm: 1; + snd_pcm_uframes_t hw_ptr; snd_pcm_uframes_t appl_ptr; /* restricted parameters */ snd_pcm_format_t format; @@ -373,7 +374,9 @@ if (pcm->stream == SND_PCM_STREAM_CAPTURE) { if (hw->mmap_shm) { hw->shadow_appl_ptr = 1; + hw->hw_ptr = 0; hw->appl_ptr = 0; + snd_pcm_set_hw_ptr(pcm, &hw->hw_ptr, -1, 0); snd_pcm_set_appl_ptr(pcm, &hw->appl_ptr, -1, 0); } else { hw->shadow_appl_ptr = 0; @@ -949,28 +952,91 @@ return size; }
+static inline snd_pcm_uframes_t snd_pcm_hw_capture_avail(snd_pcm_t *pcm) +{ + snd_pcm_sframes_t avail; + snd_pcm_hw_t *hw = pcm->private_data; + + avail = hw->mmap_status->hw_ptr - hw->mmap_control->appl_ptr; + if (avail < 0) + avail += pcm->boundary; + return avail; +} + +static snd_pcm_sframes_t snd_pcm_hw_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size) +{ + snd_pcm_uframes_t xfer = 0; + snd_pcm_sframes_t err = 0; + snd_pcm_hw_t *hw = pcm->private_data; + if (! size) + return 0; + while (xfer < size) { + snd_pcm_uframes_t frames = size - xfer; + snd_pcm_uframes_t appl_offset = hw->mmap_control->appl_ptr % pcm->buffer_size; + snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset; + if (cont < frames) + frames = cont; + switch (pcm->access) { + case SND_PCM_ACCESS_MMAP_INTERLEAVED: + { + const snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm); + char *buf = snd_pcm_channel_area_addr(a, appl_offset); + err = _snd_pcm_readi(pcm, buf, frames); + if (err >= 0) + frames = err; + break; + } + case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: + { + snd_pcm_uframes_t channels = pcm->channels; + unsigned int c; + void *bufs[channels]; + const snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm); + for (c = 0; c < channels; ++c) { + const snd_pcm_channel_area_t *a = &areas[c]; + bufs[c] = snd_pcm_channel_area_addr(a, appl_offset); + } + err = _snd_pcm_readn(pcm->fast_op_arg, bufs, frames); + if (err >= 0) + frames = err; + } + default: + SNDMSG("invalid access type %d", pcm->access); + return -EINVAL; + } + if (err < 0) + break; + xfer += frames; + } + if (xfer > 0) + return xfer; + return err; +} + static snd_pcm_sframes_t snd_pcm_hw_avail_update(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private_data; - snd_pcm_uframes_t avail; + snd_pcm_uframes_t avail, xfer_avail;
sync_ptr(hw, 0); if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { avail = snd_pcm_mmap_playback_avail(pcm); } else { avail = snd_pcm_mmap_capture_avail(pcm); - if (avail > 0 && hw->mmap_shm) { + if (avail < pcm->avail_min && hw->mmap_shm) { snd_pcm_sframes_t err; - snd_pcm_hw_t *hw = pcm->private_data; - hw->avail_update_flag = 1; - err = snd_pcm_read_mmap(pcm, avail); - hw->avail_update_flag = 0; - if (err < 0) - return err; - if ((snd_pcm_uframes_t)err != avail) - SNDMSG("short read %ld for avail %ld", err, avail); - return err; - } + xfer_avail = snd_pcm_hw_capture_avail(pcm); + xfer_avail -= xfer_avail % pcm->xfer_align; + if (xfer_avail > 0) { + hw->avail_update_flag = 1; + err = snd_pcm_hw_read_mmap(pcm, xfer_avail); + hw->avail_update_flag = 0; + if (err < 0) + return err; + hw->hw_ptr += err; + avail = snd_pcm_mmap_capture_avail(pcm); + } + } } switch (FAST_PCM_STATE(hw)) { case SNDRV_PCM_STATE_RUNNING:
Record doesn't work if enabling mmap emulation and rate conversion needed, this patch fix this bug.
diff -uNr alsa-lib-1.0.14a/src/pcm/pcm_hw.c alsa-lib-new/src/pcm/pcm_hw.c --- alsa-lib-1.0.14a/src/pcm/pcm_hw.c 2007-06-11 16:53:13.000000000 +0800 +++ alsa-lib-new/src/pcm/pcm_hw.c 2007-07-04 12:16:31.000000000 +0800 @@ -96,6 +96,7 @@ int shadow_appl_ptr: 1, avail_update_flag: 1, mmap_shm: 1; + snd_pcm_uframes_t hw_ptr; snd_pcm_uframes_t appl_ptr; /* restricted parameters */ snd_pcm_format_t format; @@ -373,7 +374,9 @@ if (pcm->stream == SND_PCM_STREAM_CAPTURE) { if (hw->mmap_shm) { hw->shadow_appl_ptr = 1; + hw->hw_ptr = 0; hw->appl_ptr = 0; + snd_pcm_set_hw_ptr(pcm, &hw->hw_ptr, -1, 0); snd_pcm_set_appl_ptr(pcm, &hw->appl_ptr, -1, 0); } else { hw->shadow_appl_ptr = 0; @@ -949,28 +952,91 @@ return size; }
+static inline snd_pcm_uframes_t snd_pcm_hw_capture_avail(snd_pcm_t *pcm) +{ + snd_pcm_sframes_t avail; + snd_pcm_hw_t *hw = pcm->private_data; + + avail = hw->mmap_status->hw_ptr - hw->mmap_control->appl_ptr; + if (avail < 0) + avail += pcm->boundary; + return avail; +} + +static snd_pcm_sframes_t snd_pcm_hw_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size) +{ + snd_pcm_uframes_t xfer = 0; + snd_pcm_sframes_t err = 0; + snd_pcm_hw_t *hw = pcm->private_data; + if (! size) + return 0; + while (xfer < size) { + snd_pcm_uframes_t frames = size - xfer; + snd_pcm_uframes_t appl_offset = hw->mmap_control->appl_ptr % pcm->buffer_size; + snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset; + if (cont < frames) + frames = cont; + switch (pcm->access) { + case SND_PCM_ACCESS_MMAP_INTERLEAVED: + { + const snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm); + char *buf = snd_pcm_channel_area_addr(a, appl_offset); + err = _snd_pcm_readi(pcm, buf, frames); + if (err >= 0) + frames = err; + break; + } + case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: + { + snd_pcm_uframes_t channels = pcm->channels; + unsigned int c; + void *bufs[channels]; + const snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm); + for (c = 0; c < channels; ++c) { + const snd_pcm_channel_area_t *a = &areas[c]; + bufs[c] = snd_pcm_channel_area_addr(a, appl_offset); + } + err = _snd_pcm_readn(pcm->fast_op_arg, bufs, frames); + if (err >= 0) + frames = err; + } + default: + SNDMSG("invalid access type %d", pcm->access); + return -EINVAL; + } + if (err < 0) + break; + xfer += frames; + } + if (xfer > 0) + return xfer; + return err; +} + static snd_pcm_sframes_t snd_pcm_hw_avail_update(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private_data; - snd_pcm_uframes_t avail; + snd_pcm_uframes_t avail, xfer_avail;
sync_ptr(hw, 0); if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { avail = snd_pcm_mmap_playback_avail(pcm); } else { avail = snd_pcm_mmap_capture_avail(pcm); - if (avail > 0 && hw->mmap_shm) { + if (avail < pcm->avail_min && hw->mmap_shm) { snd_pcm_sframes_t err; - snd_pcm_hw_t *hw = pcm->private_data; - hw->avail_update_flag = 1; - err = snd_pcm_read_mmap(pcm, avail); - hw->avail_update_flag = 0; - if (err < 0) - return err; - if ((snd_pcm_uframes_t)err != avail) - SNDMSG("short read %ld for avail %ld", err, avail); - return err; - } + xfer_avail = snd_pcm_hw_capture_avail(pcm); + xfer_avail -= xfer_avail % pcm->xfer_align; + if (xfer_avail > 0) { + hw->avail_update_flag = 1; + err = snd_pcm_hw_read_mmap(pcm, xfer_avail); + hw->avail_update_flag = 0; + if (err < 0) + return err; + hw->hw_ptr += err; + avail = snd_pcm_mmap_capture_avail(pcm); + } + } } switch (FAST_PCM_STATE(hw)) { case SNDRV_PCM_STATE_RUNNING:
At Wed, 4 Jul 2007 22:18:24 +0800, Roy Huang wrote:
Record doesn't work if enabling mmap emulation and rate conversion needed, this patch fix this bug.
Thanks for the patch. I need to check your changes in detail as it's not so obvious. What is the problem with rate plugin and mmap-emulation? From what I find in your patch, it's something to do with the hw_ptr update mismatch.
Takashi
diff -uNr alsa-lib-1.0.14a/src/pcm/pcm_hw.c alsa-lib-new/src/pcm/pcm_hw.c --- alsa-lib-1.0.14a/src/pcm/pcm_hw.c 2007-06-11 16:53:13.000000000 +0800 +++ alsa-lib-new/src/pcm/pcm_hw.c 2007-07-04 12:16:31.000000000 +0800 @@ -96,6 +96,7 @@ int shadow_appl_ptr: 1, avail_update_flag: 1, mmap_shm: 1;
snd_pcm_uframes_t hw_ptr; snd_pcm_uframes_t appl_ptr; /* restricted parameters */ snd_pcm_format_t format;
@@ -373,7 +374,9 @@ if (pcm->stream == SND_PCM_STREAM_CAPTURE) { if (hw->mmap_shm) { hw->shadow_appl_ptr = 1;
hw->hw_ptr = 0; hw->appl_ptr = 0;
snd_pcm_set_hw_ptr(pcm, &hw->hw_ptr, -1, 0); snd_pcm_set_appl_ptr(pcm, &hw->appl_ptr, -1, 0); } else { hw->shadow_appl_ptr = 0;
@@ -949,28 +952,91 @@ return size; }
+static inline snd_pcm_uframes_t snd_pcm_hw_capture_avail(snd_pcm_t *pcm) +{
snd_pcm_sframes_t avail;
snd_pcm_hw_t *hw = pcm->private_data;
avail = hw->mmap_status->hw_ptr - hw->mmap_control->appl_ptr;
if (avail < 0)
avail += pcm->boundary;
return avail;
+}
+static snd_pcm_sframes_t snd_pcm_hw_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size) +{
snd_pcm_uframes_t xfer = 0;
snd_pcm_sframes_t err = 0;
snd_pcm_hw_t *hw = pcm->private_data;
if (! size)
return 0;
while (xfer < size) {
snd_pcm_uframes_t frames = size - xfer;
snd_pcm_uframes_t appl_offset = hw->mmap_control->appl_ptr %
pcm->buffer_size;
snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
if (cont < frames)
frames = cont;
switch (pcm->access) {
case SND_PCM_ACCESS_MMAP_INTERLEAVED:
{
const snd_pcm_channel_area_t *a =
snd_pcm_mmap_areas(pcm);
char *buf = snd_pcm_channel_area_addr(a, appl_offset);
err = _snd_pcm_readi(pcm, buf, frames);
if (err >= 0)
frames = err;
break;
}
case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
{
snd_pcm_uframes_t channels = pcm->channels;
unsigned int c;
void *bufs[channels];
const snd_pcm_channel_area_t *areas =
snd_pcm_mmap_areas(pcm);
for (c = 0; c < channels; ++c) {
const snd_pcm_channel_area_t *a = &areas[c];
bufs[c] = snd_pcm_channel_area_addr(a,
appl_offset);
}
err = _snd_pcm_readn(pcm->fast_op_arg, bufs, frames);
if (err >= 0)
frames = err;
}
default:
SNDMSG("invalid access type %d", pcm->access);
return -EINVAL;
}
if (err < 0)
break;
xfer += frames;
}
if (xfer > 0)
return xfer;
return err;
+}
static snd_pcm_sframes_t snd_pcm_hw_avail_update(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private_data;
snd_pcm_uframes_t avail;
snd_pcm_uframes_t avail, xfer_avail; sync_ptr(hw, 0); if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { avail = snd_pcm_mmap_playback_avail(pcm); } else { avail = snd_pcm_mmap_capture_avail(pcm);
if (avail > 0 && hw->mmap_shm) {
if (avail < pcm->avail_min && hw->mmap_shm) { snd_pcm_sframes_t err;
snd_pcm_hw_t *hw = pcm->private_data;
hw->avail_update_flag = 1;
err = snd_pcm_read_mmap(pcm, avail);
hw->avail_update_flag = 0;
if (err < 0)
return err;
if ((snd_pcm_uframes_t)err != avail)
SNDMSG("short read %ld for avail %ld",
err, avail);
return err;
}
xfer_avail = snd_pcm_hw_capture_avail(pcm);
xfer_avail -= xfer_avail % pcm->xfer_align;
if (xfer_avail > 0) {
hw->avail_update_flag = 1;
err = snd_pcm_hw_read_mmap(pcm, xfer_avail);
hw->avail_update_flag = 0;
if (err < 0)
return err;
hw->hw_ptr += err;
avail = snd_pcm_mmap_capture_avail(pcm);
}
} } switch (FAST_PCM_STATE(hw)) { case SNDRV_PCM_STATE_RUNNING:
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Hi Takashi,
There is no problem with rate plugin. If no plugin is used, record can works. If any plugin is used, it will have problem.
If mmap emulation is enabled, data is copied from driver to hw layer's buffer, then to upper layer. In snd_pcm_hw_t, appl_ptr is used to indicate the position in hw layer's buffer where last byte is copied to upper layer. But there is no way to know the position in hw layer's buffer where the last byte is copied from driver.
In function snd_pcm_hw_avail_update, it calls snd_pcm_mmap_capture_avail to find how much data is available for upper layer, denote the amount of available data as s1. Part of the available data is already in hw layer's buffer, denote the amount of this part of data as s2. But snd_pcm_hw_avail_update calls snd_pcm_read_mmap to copy data as much as s1 from driver to hw layer's buffer. In fact only (s1-s2) is needed to be copied.
So in new function snd_pcm_hw_avail_update, only data in amout of (s1-s2) will be copied by snd_pcm_read_mmap.
Roy
On 7/5/07, Takashi Iwai tiwai@suse.de wrote:
At Wed, 4 Jul 2007 22:18:24 +0800, Roy Huang wrote:
Record doesn't work if enabling mmap emulation and rate conversion needed, this patch fix this bug.
Thanks for the patch. I need to check your changes in detail as it's not so obvious. What is the problem with rate plugin and mmap-emulation? From what I find in your patch, it's something to do with the hw_ptr update mismatch.
Takashi
At Thu, 5 Jul 2007 14:17:35 +0800, Roy Huang wrote:
Hi Takashi,
There is no problem with rate plugin. If no plugin is used, record can works. If any plugin is used, it will have problem.
If mmap emulation is enabled, data is copied from driver to hw layer's buffer, then to upper layer. In snd_pcm_hw_t, appl_ptr is used to indicate the position in hw layer's buffer where last byte is copied to upper layer. But there is no way to know the position in hw layer's buffer where the last byte is copied from driver.
In function snd_pcm_hw_avail_update, it calls snd_pcm_mmap_capture_avail to find how much data is available for upper layer, denote the amount of available data as s1. Part of the available data is already in hw layer's buffer, denote the amount of this part of data as s2. But snd_pcm_hw_avail_update calls snd_pcm_read_mmap to copy data as much as s1 from driver to hw layer's buffer. In fact only (s1-s2) is needed to be copied.
So in new function snd_pcm_hw_avail_update, only data in amout of (s1-s2) will be copied by snd_pcm_read_mmap.
Thanks for clarification. I merged your patch now to the HG tree. (BTW, the patch in your post was broken due to MUA. Could you fix it or use attachments at the next time?)
This fix reminds me that it might be cleaner to split mmap emulation as another plugin. The mix-up in hw layer gets messy now.
Takashi
Roy
On 7/5/07, Takashi Iwai tiwai@suse.de wrote:
At Wed, 4 Jul 2007 22:18:24 +0800, Roy Huang wrote:
Record doesn't work if enabling mmap emulation and rate conversion needed, this patch fix this bug.
Thanks for the patch. I need to check your changes in detail as it's not so obvious. What is the problem with rate plugin and mmap-emulation? From what I find in your patch, it's something to do with the hw_ptr update mismatch.
Takashi
participants (2)
-
Roy Huang
-
Takashi Iwai