diff options
Diffstat (limited to 'src/pcm/pcm_mmap.c')
-rw-r--r-- | src/pcm/pcm_mmap.c | 318 |
1 files changed, 240 insertions, 78 deletions
diff --git a/src/pcm/pcm_mmap.c b/src/pcm/pcm_mmap.c index 4eb51d4f..1a754c72 100644 --- a/src/pcm/pcm_mmap.c +++ b/src/pcm/pcm_mmap.c @@ -23,15 +23,23 @@ #include <string.h> #include <errno.h> #include <sys/poll.h> +#include <sys/mman.h> +#include <sys/shm.h> +#include <asm/page.h> #include "pcm_local.h" + +#ifndef PAGE_ALIGN +#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) +#endif + + snd_pcm_channel_area_t *snd_pcm_mmap_areas(snd_pcm_t *pcm) { - int state = snd_pcm_state(pcm); - if (state == SND_PCM_STATE_RUNNING) - return pcm->running_areas; - else - return pcm->stopped_areas; + if (pcm->stopped_areas && + snd_pcm_state(pcm) != SND_PCM_STATE_RUNNING) + return pcm->stopped_areas; + return pcm->running_areas; } size_t snd_pcm_mmap_playback_xfer(snd_pcm_t *pcm, size_t frames) @@ -40,7 +48,7 @@ size_t snd_pcm_mmap_playback_xfer(snd_pcm_t *pcm, size_t frames) size_t avail = snd_pcm_mmap_playback_avail(pcm); if (avail < frames) frames = avail; - cont = pcm->setup.buffer_size - *pcm->appl_ptr % pcm->setup.buffer_size; + cont = pcm->buffer_size - *pcm->appl_ptr % pcm->buffer_size; if (cont < frames) frames = cont; return frames; @@ -52,7 +60,7 @@ size_t snd_pcm_mmap_capture_xfer(snd_pcm_t *pcm, size_t frames) size_t avail = snd_pcm_mmap_capture_avail(pcm); if (avail < frames) frames = avail; - cont = pcm->setup.buffer_size - *pcm->appl_ptr % pcm->setup.buffer_size; + cont = pcm->buffer_size - *pcm->appl_ptr % pcm->buffer_size; if (cont < frames) frames = cont; return frames; @@ -70,13 +78,13 @@ size_t snd_pcm_mmap_xfer(snd_pcm_t *pcm, size_t frames) size_t snd_pcm_mmap_offset(snd_pcm_t *pcm) { assert(pcm); - return *pcm->appl_ptr % pcm->setup.buffer_size; + return *pcm->appl_ptr % pcm->buffer_size; } size_t snd_pcm_mmap_hw_offset(snd_pcm_t *pcm) { assert(pcm); - return *pcm->hw_ptr % pcm->setup.buffer_size; + return *pcm->hw_ptr % pcm->buffer_size; } void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, size_t frames) @@ -84,7 +92,7 @@ void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, size_t frames) ssize_t appl_ptr = *pcm->appl_ptr; appl_ptr -= frames; if (appl_ptr < 0) - appl_ptr += pcm->setup.boundary; + appl_ptr += pcm->boundary; *pcm->appl_ptr = appl_ptr; } @@ -92,8 +100,8 @@ void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, size_t frames) { size_t appl_ptr = *pcm->appl_ptr; appl_ptr += frames; - if (appl_ptr >= pcm->setup.boundary) - appl_ptr -= pcm->setup.boundary; + if (appl_ptr >= pcm->boundary) + appl_ptr -= pcm->boundary; *pcm->appl_ptr = appl_ptr; } @@ -102,7 +110,7 @@ void snd_pcm_mmap_hw_backward(snd_pcm_t *pcm, size_t frames) ssize_t hw_ptr = *pcm->hw_ptr; hw_ptr -= frames; if (hw_ptr < 0) - hw_ptr += pcm->setup.boundary; + hw_ptr += pcm->boundary; *pcm->hw_ptr = hw_ptr; } @@ -110,8 +118,8 @@ void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, size_t frames) { size_t hw_ptr = *pcm->hw_ptr; hw_ptr += frames; - if (hw_ptr >= pcm->setup.boundary) - hw_ptr -= pcm->setup.boundary; + if (hw_ptr >= pcm->boundary) + hw_ptr -= pcm->boundary; *pcm->hw_ptr = hw_ptr; } @@ -127,11 +135,13 @@ ssize_t snd_pcm_mmap_write_areas(snd_pcm_t *pcm, xfer = 0; while (xfer < size) { size_t frames = snd_pcm_mmap_playback_xfer(pcm, size - xfer); + ssize_t err; snd_pcm_areas_copy(areas, offset, snd_pcm_mmap_areas(pcm), snd_pcm_mmap_offset(pcm), - pcm->setup.format.channels, - frames, pcm->setup.format.sfmt); - snd_pcm_mmap_forward(pcm, frames); + pcm->channels, + frames, pcm->format); + err = snd_pcm_mmap_forward(pcm, frames); + assert(err == (ssize_t)frames); offset += frames; xfer += frames; } @@ -152,11 +162,13 @@ ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *pcm, xfer = 0; while (xfer < size) { size_t frames = snd_pcm_mmap_capture_xfer(pcm, size - xfer); + ssize_t err; snd_pcm_areas_copy(snd_pcm_mmap_areas(pcm), snd_pcm_mmap_offset(pcm), areas, offset, - pcm->setup.format.channels, - frames, pcm->setup.format.sfmt); - snd_pcm_mmap_forward(pcm, frames); + pcm->channels, + frames, pcm->format); + err = snd_pcm_mmap_forward(pcm, frames); + assert(err == (ssize_t)frames); offset += frames; xfer += frames; } @@ -167,7 +179,7 @@ ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *pcm, ssize_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, size_t size) { - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; snd_pcm_areas_from_buf(pcm, areas, (void*)buffer); return snd_pcm_write_areas(pcm, areas, 0, size, snd_pcm_mmap_write_areas); @@ -175,7 +187,7 @@ ssize_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, size_t size) ssize_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, size_t size) { - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; snd_pcm_areas_from_bufs(pcm, areas, bufs); return snd_pcm_write_areas(pcm, areas, 0, size, snd_pcm_mmap_write_areas); @@ -183,7 +195,7 @@ ssize_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, size_t size) ssize_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, size_t size) { - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; snd_pcm_areas_from_buf(pcm, areas, buffer); return snd_pcm_read_areas(pcm, areas, 0, size, snd_pcm_mmap_read_areas); @@ -191,71 +203,204 @@ ssize_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, size_t size) ssize_t snd_pcm_mmap_readn(snd_pcm_t *pcm, void **bufs, size_t size) { - snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_channel_area_t areas[pcm->channels]; snd_pcm_areas_from_bufs(pcm, areas, bufs); return snd_pcm_read_areas(pcm, areas, 0, size, snd_pcm_mmap_read_areas); } -int snd_pcm_mmap_get_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *stopped_areas, snd_pcm_channel_area_t *running_areas) +int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) { - snd_pcm_channel_setup_t setup; - snd_pcm_channel_area_t *r, *rp, *s, *sp; - unsigned int channel; - int err; - assert(pcm); - assert(pcm->mmap_info); - if (!pcm->running_areas) { - r = calloc(pcm->setup.format.channels, sizeof(*r)); - s = calloc(pcm->setup.format.channels, sizeof(*s)); - for (channel = 0, rp = r, sp = s; channel < pcm->setup.format.channels; ++channel, ++rp, ++sp) { - setup.channel = channel; - err = snd_pcm_channel_setup(pcm, &setup); - if (err < 0) { - free(r); - free(s); - return err; - } - *rp = setup.running_area; - *sp = setup.stopped_area; - } - pcm->running_areas = r; - pcm->stopped_areas = s; + return pcm->ops->channel_info(pcm, info); +} + +int snd_pcm_channel_info_shm(snd_pcm_t *pcm, snd_pcm_channel_info_t *info, + int shmid) +{ + switch (pcm->access) { + case SND_PCM_ACCESS_MMAP_INTERLEAVED: + case SND_PCM_ACCESS_RW_INTERLEAVED: + info->first = info->channel * pcm->bits_per_sample; + info->step = pcm->bits_per_frame; + break; + case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: + case SND_PCM_ACCESS_RW_NONINTERLEAVED: + info->first = 0; + info->step = pcm->bits_per_sample; + break; + default: + assert(0); + break; } - if (running_areas) - memcpy(running_areas, pcm->running_areas, pcm->setup.format.channels * sizeof(*running_areas)); - if (stopped_areas) - memcpy(stopped_areas, pcm->stopped_areas, pcm->setup.format.channels * sizeof(*stopped_areas)); + info->addr = 0; + info->type = SND_PCM_AREA_SHM; + info->u.shm.shmid = shmid; return 0; -} +} int snd_pcm_mmap(snd_pcm_t *pcm) { int err; + unsigned int c; assert(pcm); - assert(pcm->valid_setup); - if (pcm->mmap_info) - return 0; - - if ((err = pcm->ops->mmap(pcm->op_arg)) < 0) - return err; - err = snd_pcm_mmap_get_areas(pcm, NULL, NULL); + assert(pcm->setup); + assert(!pcm->mmap_channels); + err = pcm->ops->mmap(pcm); if (err < 0) return err; + pcm->mmap_channels = calloc(pcm->channels, sizeof(pcm->mmap_channels[0])); + if (!pcm->mmap_channels) + return -ENOMEM; + assert(!pcm->running_areas); + pcm->running_areas = calloc(pcm->channels, sizeof(pcm->running_areas[0])); + if (!pcm->running_areas) { + free(pcm->mmap_channels); + pcm->mmap_channels = NULL; + return -ENOMEM; + } + for (c = 0; c < pcm->channels; ++c) { + snd_pcm_channel_info_t *i = &pcm->mmap_channels[c]; + i->channel = c; + err = snd_pcm_channel_info(pcm, i); + if (err < 0) + return err; + } + for (c = 0; c < pcm->channels; ++c) { + snd_pcm_channel_info_t *i = &pcm->mmap_channels[c]; + snd_pcm_channel_area_t *a = &pcm->running_areas[c]; + unsigned int c1; + if (!i->addr) { + char *ptr; + size_t size = i->first + i->step * pcm->buffer_size; + for (c1 = c + 1; c1 < pcm->channels; ++c1) { + snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1]; + size_t s; + if (i1->type != i->type) + continue; + switch (i1->type) { + case SND_PCM_AREA_MMAP: + if (i1->u.mmap.fd != i->u.mmap.fd || + i1->u.mmap.offset != i->u.mmap.offset) + continue; + break; + case SND_PCM_AREA_SHM: + if (i1->u.shm.shmid != i->u.shm.shmid) + continue; + break; + default: + assert(0); + } + s = i1->first + i1->step * pcm->buffer_size; + if (s > size) + size = s; + } + size = (size + 7) / 8; + size = PAGE_ALIGN(size); + switch (i->type) { + case SND_PCM_AREA_MMAP: + ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, i->u.mmap.fd, i->u.mmap.offset); + if (ptr == MAP_FAILED) { + SYSERR("mmap failed"); + return -errno; + } + i->addr = ptr; + break; + case SND_PCM_AREA_SHM: + if (i->u.shm.shmid < 0) { + int id; + id = shmget(IPC_PRIVATE, size, 0666); + if (id < 0) { + SYSERR("shmget failed"); + return -errno; + } + i->u.shm.shmid = id; + } + ptr = shmat(i->u.shm.shmid, 0, 0); + if (ptr == (void*) -1) { + SYSERR("shmat failed"); + return -errno; + } + i->addr = ptr; + break; + default: + assert(0); + } + } + for (c1 = c + 1; c1 < pcm->channels; ++c1) { + snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1]; + if (i1->type != i->type) + continue; + switch (i1->type) { + case SND_PCM_AREA_MMAP: + if (i1->u.mmap.fd != i->u.mmap.fd || + i1->u.mmap.offset != i->u.mmap.offset) + continue; + break; + case SND_PCM_AREA_SHM: + if (i1->u.shm.shmid != i->u.shm.shmid) + continue; + break; + default: + assert(0); + } + i1->addr = i->addr; + } + a->addr = i->addr; + a->first = i->first; + a->step = i->step; + } return 0; } int snd_pcm_munmap(snd_pcm_t *pcm) { int err; + unsigned int c; assert(pcm); - assert(pcm->mmap_info); - if ((err = pcm->ops->munmap(pcm->op_arg)) < 0) + assert(pcm->mmap_channels); + for (c = 0; c < pcm->channels; ++c) { + snd_pcm_channel_info_t *i = &pcm->mmap_channels[c]; + unsigned int c1; + size_t size = i->first + i->step * pcm->buffer_size; + if (!i->addr) + continue; + for (c1 = c + 1; c1 < pcm->channels; ++c1) { + snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1]; + size_t s; + if (i1->addr != i->addr) + continue; + i1->addr = NULL; + s = i1->first + i1->step * pcm->buffer_size; + if (s > size) + size = s; + } + size = (size + 7) / 8; + size = PAGE_ALIGN(size); + switch (i->type) { + case SND_PCM_AREA_MMAP: + err = munmap(i->addr, size); + if (err < 0) { + SYSERR("mmap failed"); + return -errno; + } + break; + case SND_PCM_AREA_SHM: + err = shmdt(i->addr); + if (err < 0) { + SYSERR("shmdt failed"); + return -errno; + } + break; + default: + assert(0); + } + i->addr = NULL; + } + err = pcm->ops->munmap(pcm); + if (err < 0) return err; - free(pcm->stopped_areas); - free(pcm->running_areas); - pcm->stopped_areas = 0; - pcm->running_areas = 0; + free(pcm->mmap_channels); + pcm->mmap_channels = 0; return 0; } @@ -267,25 +412,34 @@ ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size) while (xfer < size) { size_t frames = size - xfer; size_t offset = snd_pcm_mmap_hw_offset(pcm); - size_t cont = pcm->setup.buffer_size - offset; + size_t cont = pcm->buffer_size - offset; if (cont < frames) frames = cont; - if (pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED) { + switch (pcm->access) { + case SND_PCM_ACCESS_MMAP_INTERLEAVED: + { snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm); char *buf = snd_pcm_channel_area_addr(a, offset); - assert(pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED); err = _snd_pcm_writei(pcm, buf, size); - } else { - size_t channels = pcm->setup.format.channels; + break; + } + case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: + { + size_t channels = pcm->channels; unsigned int c; void *bufs[channels]; snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm); - assert(pcm->setup.mmap_shape == SND_PCM_MMAP_NONINTERLEAVED); for (c = 0; c < channels; ++c) { snd_pcm_channel_area_t *a = &areas[c]; bufs[c] = snd_pcm_channel_area_addr(a, offset); } err = _snd_pcm_writen(pcm, bufs, size); + break; + } + default: + assert(0); + err = -EINVAL; + break; } if (err < 0) break; @@ -304,26 +458,34 @@ ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size) while (xfer < size) { size_t frames = size - xfer; size_t offset = snd_pcm_mmap_hw_offset(pcm); - size_t cont = pcm->setup.buffer_size - offset; + size_t cont = pcm->buffer_size - offset; if (cont < frames) frames = cont; - if (pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED) { + switch (pcm->access) { + case SND_PCM_ACCESS_MMAP_INTERLEAVED: + { snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm); char *buf = snd_pcm_channel_area_addr(a, offset); - assert(pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED); err = _snd_pcm_readi(pcm, buf, size); - } else { - size_t channels = pcm->setup.format.channels; + break; + } + case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: + { + size_t channels = pcm->channels; unsigned int c; void *bufs[channels]; snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm); - assert(pcm->setup.mmap_shape == SND_PCM_MMAP_NONINTERLEAVED); for (c = 0; c < channels; ++c) { snd_pcm_channel_area_t *a = &areas[c]; bufs[c] = snd_pcm_channel_area_addr(a, offset); } err = _snd_pcm_readn(pcm->fast_op_arg, bufs, size); } + default: + assert(0); + err = -EINVAL; + break; + } if (err < 0) break; xfer += frames; |