pcm - fix the buffer allocation for NONINTERLEAVED mmap access

The previous code did not allocated a separate buffer for all channels
when a NONINTERLEAVED access was used. The result was that only one
"shared" buffer was incorrectly allocated.

Also, the code was a bit cleaned (cosmetic change only).
This commit is contained in:
Jaroslav Kysela 2006-01-02 12:16:59 +00:00
parent 2f65643c1c
commit b08e01ca9e

View file

@ -326,122 +326,130 @@ int snd_pcm_mmap(snd_pcm_t *pcm)
for (c = 0; c < pcm->channels; ++c) { for (c = 0; c < pcm->channels; ++c) {
snd_pcm_channel_info_t *i = &pcm->mmap_channels[c]; snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
snd_pcm_channel_area_t *a = &pcm->running_areas[c]; snd_pcm_channel_area_t *a = &pcm->running_areas[c];
char *ptr;
size_t size;
unsigned int c1; unsigned int c1;
if (!i->addr) { if (i->addr) {
char *ptr; a->addr = i->addr;
size_t size = i->first + i->step * (pcm->buffer_size - 1) + pcm->sample_bits; a->first = i->first;
for (c1 = c + 1; c1 < pcm->channels; ++c1) { a->step = i->step;
snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1]; continue;
size_t s; }
if (i1->type != i->type) size = i->first + i->step * (pcm->buffer_size - 1) + pcm->sample_bits;
continue; for (c1 = c + 1; c1 < pcm->channels; ++c1) {
switch (i1->type) { snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
case SND_PCM_AREA_MMAP: size_t s;
if (i1->u.mmap.fd != i->u.mmap.fd || if (i1->type != i->type)
i1->u.mmap.offset != i->u.mmap.offset) continue;
continue; switch (i1->type) {
break;
case SND_PCM_AREA_SHM:
if (i1->u.shm.shmid != i->u.shm.shmid)
continue;
break;
case SND_PCM_AREA_LOCAL:
break;
default:
assert(0);
}
s = i1->first + i1->step * (pcm->buffer_size - 1) + pcm->sample_bits;
if (s > size)
size = s;
}
size = (size + 7) / 8;
size = page_align(size);
switch (i->type) {
case SND_PCM_AREA_MMAP: 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 (i1->u.mmap.fd != i->u.mmap.fd ||
if (ptr == MAP_FAILED) { i1->u.mmap.offset != i->u.mmap.offset)
SYSERR("mmap failed"); continue;
return -errno;
}
i->addr = ptr;
break; break;
case SND_PCM_AREA_SHM: case SND_PCM_AREA_SHM:
if (i->u.shm.shmid < 0) { if (i1->u.shm.shmid != i->u.shm.shmid)
int id; continue;
/* FIXME: safer permission? */
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;
}
/* automatically remove segment if not used */
if (shmctl(id, IPC_RMID, NULL) < 0){
SYSERR("shmctl mark remove failed");
return -errno;
}
i->u.shm.area = snd_shm_area_create(id, ptr);
if (i->u.shm.area == NULL) {
SYSERR("snd_shm_area_create failed");
return -ENOMEM;
}
if (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
unsigned int c1;
for (c1 = c + 1; c1 < pcm->channels; c1++) {
snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
if (i1->u.shm.shmid < 0) {
i1->u.shm.shmid = id;
i1->u.shm.area = snd_shm_area_share(i->u.shm.area);
}
}
}
} else {
ptr = shmat(i->u.shm.shmid, 0, 0);
if (ptr == (void*) -1) {
SYSERR("shmat failed");
return -errno;
}
}
i->addr = ptr;
break; break;
case SND_PCM_AREA_LOCAL: case SND_PCM_AREA_LOCAL:
ptr = malloc(size);
if (ptr == NULL) {
SYSERR("malloc failed");
return -errno;
}
i->addr = ptr;
break; break;
default: default:
assert(0); assert(0);
} }
for (c1 = c + 1; c1 < pcm->channels; ++c1) { s = i1->first + i1->step * (pcm->buffer_size - 1) + pcm->sample_bits;
snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1]; if (s > size)
if (i1->type != i->type) size = s;
continue; }
switch (i1->type) { size = (size + 7) / 8;
case SND_PCM_AREA_MMAP: size = page_align(size);
if (i1->u.mmap.fd != i->u.mmap.fd || switch (i->type) {
i1->u.mmap.offset != i->u.mmap.offset) case SND_PCM_AREA_MMAP:
continue; ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, i->u.mmap.fd, i->u.mmap.offset);
break; if (ptr == MAP_FAILED) {
case SND_PCM_AREA_SHM: SYSERR("mmap failed");
if (i1->u.shm.shmid != i->u.shm.shmid) return -errno;
continue;
break;
case SND_PCM_AREA_LOCAL:
break;
default:
assert(0);
}
i1->addr = i->addr;
} }
i->addr = ptr;
break;
case SND_PCM_AREA_SHM:
if (i->u.shm.shmid < 0) {
int id;
/* FIXME: safer permission? */
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;
}
/* automatically remove segment if not used */
if (shmctl(id, IPC_RMID, NULL) < 0){
SYSERR("shmctl mark remove failed");
return -errno;
}
i->u.shm.area = snd_shm_area_create(id, ptr);
if (i->u.shm.area == NULL) {
SYSERR("snd_shm_area_create failed");
return -ENOMEM;
}
if (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
unsigned int c1;
for (c1 = c + 1; c1 < pcm->channels; c1++) {
snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
if (i1->u.shm.shmid < 0) {
i1->u.shm.shmid = id;
i1->u.shm.area = snd_shm_area_share(i->u.shm.area);
}
}
}
} else {
ptr = shmat(i->u.shm.shmid, 0, 0);
if (ptr == (void*) -1) {
SYSERR("shmat failed");
return -errno;
}
}
i->addr = ptr;
break;
case SND_PCM_AREA_LOCAL:
ptr = malloc(size);
if (ptr == NULL) {
SYSERR("malloc 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;
/* follow thru */
case SND_PCM_AREA_LOCAL:
if (pcm->access != SND_PCM_ACCESS_MMAP_INTERLEAVED &&
pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED)
continue;
break;
default:
assert(0);
}
i1->addr = i->addr;
} }
a->addr = i->addr; a->addr = i->addr;
a->first = i->first; a->first = i->first;