/* called in pcm lock */ snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t size) { snd_pcm_uframes_t xfer = 0; snd_pcm_sframes_t err = 0; if (! size) return 0; while (xfer < size) { snd_pcm_uframes_t frames = size - xfer; snd_pcm_uframes_t cont = pcm->buffer_size - 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, offset); snd_pcm_unlock(pcm); /* to avoid deadlock */ err = _snd_pcm_readi(pcm, buf, frames); snd_pcm_lock(pcm); 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, offset); } snd_pcm_unlock(pcm); /* to avoid deadlock */ err = _snd_pcm_readn(pcm->fast_op_arg, bufs, frames); snd_pcm_lock(pcm); if (err >= 0) frames = err; break; } default: SNDMSG("invalid access type %d", pcm->access); return -EINVAL; } if (err < 0) break; xfer += frames; offset = (offset + frames) % pcm->buffer_size; } if (xfer > 0) return xfer; return err; }
/* * synchronize shm ring buffer with hardware */ static void snd_pcm_dshare_sync_area(snd_pcm_t *pcm) { snd_pcm_direct_t *dshare = pcm->private_data; snd_pcm_uframes_t slave_hw_ptr, slave_appl_ptr, slave_size; snd_pcm_uframes_t appl_ptr, size; const snd_pcm_channel_area_t *src_areas, *dst_areas; /* calculate the size to transfer */ size = dshare->appl_ptr - dshare->last_appl_ptr; if (! size) return; slave_hw_ptr = dshare->slave_hw_ptr; /* don't write on the last active period - this area may be cleared * by the driver during write operation... */ slave_hw_ptr -= slave_hw_ptr % dshare->slave_period_size; slave_hw_ptr += dshare->slave_buffer_size; if (dshare->slave_hw_ptr > dshare->slave_boundary) slave_hw_ptr -= dshare->slave_boundary; if (slave_hw_ptr < dshare->slave_appl_ptr) slave_size = slave_hw_ptr + (dshare->slave_boundary - dshare->slave_appl_ptr); else slave_size = slave_hw_ptr - dshare->slave_appl_ptr; if (slave_size < size) size = slave_size; if (! size) return; /* add sample areas here */ src_areas = snd_pcm_mmap_areas(pcm); dst_areas = snd_pcm_mmap_areas(dshare->spcm); appl_ptr = dshare->last_appl_ptr % pcm->buffer_size; dshare->last_appl_ptr += size; dshare->last_appl_ptr %= pcm->boundary; slave_appl_ptr = dshare->slave_appl_ptr % dshare->slave_buffer_size; dshare->slave_appl_ptr += size; dshare->slave_appl_ptr %= dshare->slave_boundary; for (;;) { snd_pcm_uframes_t transfer = size; if (appl_ptr + transfer > pcm->buffer_size) transfer = pcm->buffer_size - appl_ptr; if (slave_appl_ptr + transfer > dshare->slave_buffer_size) transfer = dshare->slave_buffer_size - slave_appl_ptr; share_areas(dshare, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer); size -= transfer; if (! size) break; slave_appl_ptr += transfer; slave_appl_ptr %= dshare->slave_buffer_size; appl_ptr += transfer; appl_ptr %= pcm->buffer_size; } }
/* * synchronize shm ring buffer with hardware */ static void snd_pcm_dmix_sync_area(snd_pcm_t *pcm) { snd_pcm_direct_t *dmix = pcm->private_data; snd_pcm_uframes_t appl_ptr, slave_appl_ptr, slave_bsize; snd_pcm_uframes_t size, slave_hw_ptr; const snd_pcm_channel_area_t *src_areas, *dst_areas; /* calculate the size to transfer */ size = dmix->appl_ptr - dmix->last_appl_ptr; if (! size) return; slave_bsize = dmix->shmptr->s.buffer_size; slave_hw_ptr = dmix->slave_hw_ptr; /* don't write on the last active period - this area may be cleared * by the driver during mix operation... */ slave_hw_ptr -= slave_hw_ptr % dmix->shmptr->s.period_size; slave_hw_ptr += slave_bsize; if (dmix->slave_hw_ptr > dmix->slave_appl_ptr) slave_hw_ptr -= dmix->shmptr->s.boundary; if (dmix->slave_appl_ptr + size >= slave_hw_ptr) size = slave_hw_ptr - dmix->slave_appl_ptr; if (! size) return; /* add sample areas here */ src_areas = snd_pcm_mmap_areas(pcm); dst_areas = snd_pcm_mmap_areas(dmix->spcm); appl_ptr = dmix->last_appl_ptr % pcm->buffer_size; dmix->last_appl_ptr += size; dmix->last_appl_ptr %= pcm->boundary; slave_appl_ptr = dmix->slave_appl_ptr % slave_bsize; dmix->slave_appl_ptr += size; dmix->slave_appl_ptr %= dmix->shmptr->s.boundary; dmix_down_sem(dmix); for (;;) { snd_pcm_uframes_t transfer = size; if (appl_ptr + transfer > pcm->buffer_size) transfer = pcm->buffer_size - appl_ptr; if (slave_appl_ptr + transfer > slave_bsize) transfer = slave_bsize - slave_appl_ptr; mix_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer); size -= transfer; if (! size) break; slave_appl_ptr += transfer; slave_appl_ptr %= slave_bsize; appl_ptr += transfer; appl_ptr %= pcm->buffer_size; } dmix_up_sem(dmix); }
/* * synchronize shm ring buffer with hardware */ static void snd_pcm_dsnoop_sync_area(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr, snd_pcm_uframes_t size) { snd_pcm_direct_t *dsnoop = pcm->private_data; snd_pcm_uframes_t hw_ptr = dsnoop->hw_ptr; snd_pcm_uframes_t transfer; const snd_pcm_channel_area_t *src_areas, *dst_areas; /* add sample areas here */ dst_areas = snd_pcm_mmap_areas(pcm); src_areas = snd_pcm_mmap_areas(dsnoop->spcm); hw_ptr %= pcm->buffer_size; slave_hw_ptr %= dsnoop->slave_buffer_size; while (size > 0) { transfer = hw_ptr + size > pcm->buffer_size ? pcm->buffer_size - hw_ptr : size; transfer = slave_hw_ptr + transfer > dsnoop->slave_buffer_size ? dsnoop->slave_buffer_size - slave_hw_ptr : transfer; size -= transfer; snoop_areas(dsnoop, src_areas, dst_areas, slave_hw_ptr, hw_ptr, transfer); slave_hw_ptr += transfer; slave_hw_ptr %= dsnoop->slave_buffer_size; hw_ptr += transfer; hw_ptr %= pcm->buffer_size; } }
static void do_silence(snd_pcm_t *pcm) { snd_pcm_direct_t *dshare = pcm->private_data; const snd_pcm_channel_area_t *dst_areas; unsigned int chn, dchn, channels; snd_pcm_format_t format; dst_areas = snd_pcm_mmap_areas(dshare->spcm); channels = dshare->channels; format = dshare->shmptr->s.format; for (chn = 0; chn < channels; chn++) { dchn = dshare->bindings ? dshare->bindings[chn] : chn; snd_pcm_area_silence(&dst_areas[dchn], 0, dshare->shmptr->s.buffer_size, format); } }
/* * synchronize shm ring buffer with hardware */ static void snd_pcm_dmix_sync_area(snd_pcm_t *pcm) { snd_pcm_direct_t *dmix = pcm->private_data; snd_pcm_uframes_t slave_hw_ptr, slave_appl_ptr, slave_size; snd_pcm_uframes_t appl_ptr, size, transfer; const snd_pcm_channel_area_t *src_areas, *dst_areas; /* calculate the size to transfer */ /* check the available size in the local buffer * last_appl_ptr keeps the last updated position */ size = dmix->appl_ptr - dmix->last_appl_ptr; if (! size) return; if (size >= pcm->boundary / 2) size = pcm->boundary - size; /* the slave_app_ptr can be far behind the slave_hw_ptr */ /* reduce mixing and errors here - just skip not catched writes */ if (dmix->slave_hw_ptr <= dmix->slave_appl_ptr) slave_size = dmix->slave_appl_ptr - dmix->slave_hw_ptr; else slave_size = dmix->slave_appl_ptr + (dmix->slave_boundary - dmix->slave_hw_ptr); if (slave_size > dmix->slave_buffer_size) { transfer = dmix->slave_buffer_size - slave_size; if (transfer > size) transfer = size; dmix->last_appl_ptr += transfer; dmix->last_appl_ptr %= pcm->boundary; dmix->slave_appl_ptr += transfer; dmix->slave_appl_ptr %= dmix->slave_boundary; size = dmix->appl_ptr - dmix->last_appl_ptr; if (! size) return; if (size >= pcm->boundary / 2) size = pcm->boundary - size; } /* check the available size in the slave PCM buffer */ slave_hw_ptr = dmix->slave_hw_ptr; /* don't write on the last active period - this area may be cleared * by the driver during mix operation... */ slave_hw_ptr -= slave_hw_ptr % dmix->slave_period_size; slave_hw_ptr += dmix->slave_buffer_size; if (slave_hw_ptr >= dmix->slave_boundary) slave_hw_ptr -= dmix->slave_boundary; if (slave_hw_ptr < dmix->slave_appl_ptr) slave_size = slave_hw_ptr + (dmix->slave_boundary - dmix->slave_appl_ptr); else slave_size = slave_hw_ptr - dmix->slave_appl_ptr; if (slave_size < size) size = slave_size; if (! size) return; /* add sample areas here */ src_areas = snd_pcm_mmap_areas(pcm); dst_areas = snd_pcm_mmap_areas(dmix->spcm); appl_ptr = dmix->last_appl_ptr % pcm->buffer_size; dmix->last_appl_ptr += size; dmix->last_appl_ptr %= pcm->boundary; slave_appl_ptr = dmix->slave_appl_ptr % dmix->slave_buffer_size; dmix->slave_appl_ptr += size; dmix->slave_appl_ptr %= dmix->slave_boundary; dmix_down_sem(dmix); for (;;) { transfer = size; if (appl_ptr + transfer > pcm->buffer_size) transfer = pcm->buffer_size - appl_ptr; if (slave_appl_ptr + transfer > dmix->slave_buffer_size) transfer = dmix->slave_buffer_size - slave_appl_ptr; mix_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer); size -= transfer; if (! size) break; slave_appl_ptr += transfer; slave_appl_ptr %= dmix->slave_buffer_size; appl_ptr += transfer; appl_ptr %= pcm->buffer_size; } dmix_up_sem(dmix); }