static int snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event) { snd_ctl_hw_t *hw = handle->private_data; ssize_t res = read(hw->fd, event, sizeof(*event)); if (res <= 0) return -errno; if (CHECK_SANITY(res != sizeof(*event))) { SNDMSG("snd_ctl_hw_read: read size error (req:%d, got:%d)\n", sizeof(*event), res); return -EINVAL; } return 1; }
int snd_pcm_munmap(snd_pcm_t *pcm) { int err; unsigned int c; assert(pcm); if (CHECK_SANITY(! pcm->mmap_channels)) { SNDMSG("Not mmapped"); return -ENXIO; } if (pcm->mmap_shadow) return pcm->ops->munmap(pcm); 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 - 1) + pcm->sample_bits; 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 - 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: err = munmap(i->addr, size); if (err < 0) { SYSERR("mmap failed"); return -errno; } errno = 0; break; case SND_PCM_AREA_SHM: if (i->u.shm.area) { snd_shm_area_destroy(i->u.shm.area); i->u.shm.area = NULL; 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.area) { snd_shm_area_destroy(i1->u.shm.area); i1->u.shm.area = NULL; } } } } break; case SND_PCM_AREA_LOCAL: free(i->addr); break; default: assert(0); } i->addr = NULL; } err = pcm->ops->munmap(pcm); if (err < 0) return err; free(pcm->mmap_channels); free(pcm->running_areas); pcm->mmap_channels = NULL; pcm->running_areas = NULL; return 0; }
int snd_pcm_mmap(snd_pcm_t *pcm) { int err; unsigned int c; assert(pcm); if (CHECK_SANITY(! pcm->setup)) { SNDMSG("PCM not set up"); return -EIO; } if (CHECK_SANITY(pcm->mmap_channels || pcm->running_areas)) { SNDMSG("Already mmapped"); return -EBUSY; } err = pcm->ops->mmap(pcm); if (err < 0) return err; if (pcm->mmap_shadow) return 0; pcm->mmap_channels = calloc(pcm->channels, sizeof(pcm->mmap_channels[0])); if (!pcm->mmap_channels) return -ENOMEM; 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]; char *ptr; size_t size; unsigned int c1; if (i->addr) { a->addr = i->addr; a->first = i->first; a->step = i->step; continue; } size = i->first + i->step * (pcm->buffer_size - 1) + pcm->sample_bits; 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; 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: 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; /* 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->first = i->first; a->step = i->step; } return 0; }
int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode) { int fd, ver; char filename[sizeof(SNDRV_FILE_CONTROL) + 10]; int fmode; snd_ctl_t *ctl; snd_ctl_hw_t *hw; int err; *handle = NULL; if (CHECK_SANITY(card < 0 || card >= 32)) { SNDMSG("Invalid card index %d", card); return -EINVAL; } sprintf(filename, SNDRV_FILE_CONTROL, card); if (mode & SND_CTL_READONLY) fmode = O_RDONLY; else fmode = O_RDWR; if (mode & SND_CTL_NONBLOCK) fmode |= O_NONBLOCK; if (mode & SND_CTL_ASYNC) fmode |= O_ASYNC; fd = snd_open_device(filename, fmode); if (fd < 0) { snd_card_load(card); fd = snd_open_device(filename, fmode); if (fd < 0) return -errno; } if (ioctl(fd, SNDRV_CTL_IOCTL_PVERSION, &ver) < 0) { err = -errno; close(fd); return err; } if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_CTL_VERSION_MAX)) { close(fd); return -SND_ERROR_INCOMPATIBLE_VERSION; } hw = calloc(1, sizeof(snd_ctl_hw_t)); if (hw == NULL) { close(fd); return -ENOMEM; } hw->card = card; hw->fd = fd; hw->protocol = ver; err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name); if (err < 0) { close(fd); free(hw); return err; } ctl->ops = &snd_ctl_hw_ops; ctl->private_data = hw; ctl->poll_fd = fd; *handle = ctl; return 0; }