Blame src/pcm/pcm_mmap.c

Packit 4a16fb
/*
Packit 4a16fb
 *  PCM Interface - mmap
Packit 4a16fb
 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 *
Packit 4a16fb
 *   This library is free software; you can redistribute it and/or modify
Packit 4a16fb
 *   it under the terms of the GNU Lesser General Public License as
Packit 4a16fb
 *   published by the Free Software Foundation; either version 2.1 of
Packit 4a16fb
 *   the License, or (at your option) any later version.
Packit 4a16fb
 *
Packit 4a16fb
 *   This program is distributed in the hope that it will be useful,
Packit 4a16fb
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4a16fb
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 4a16fb
 *   GNU Lesser General Public License for more details.
Packit 4a16fb
 *
Packit 4a16fb
 *   You should have received a copy of the GNU Lesser General Public
Packit 4a16fb
 *   License along with this library; if not, write to the Free Software
Packit 4a16fb
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 4a16fb
 *
Packit 4a16fb
 */
Packit 4a16fb
Packit 4a16fb
#include "config.h"
Packit 4a16fb
#include <stdio.h>
Packit 4a16fb
#include <malloc.h>
Packit 4a16fb
#include <string.h>
Packit 4a16fb
#include <poll.h>
Packit 4a16fb
#include <sys/mman.h>
Packit 4a16fb
#ifdef HAVE_SYS_SHM_H
Packit 4a16fb
#include <sys/shm.h>
Packit 4a16fb
#endif
Packit 4a16fb
#include "pcm_local.h"
Packit 4a16fb
Packit 4a16fb
void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_sframes_t appl_ptr = *pcm->appl.ptr;
Packit 4a16fb
	appl_ptr -= frames;
Packit 4a16fb
	if (appl_ptr < 0)
Packit 4a16fb
		appl_ptr += pcm->boundary;
Packit 4a16fb
	*pcm->appl.ptr = appl_ptr;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_uframes_t appl_ptr = *pcm->appl.ptr;
Packit 4a16fb
	appl_ptr += frames;
Packit 4a16fb
	if (appl_ptr >= pcm->boundary)
Packit 4a16fb
		appl_ptr -= pcm->boundary;
Packit 4a16fb
	*pcm->appl.ptr = appl_ptr;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
void snd_pcm_mmap_hw_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_sframes_t hw_ptr = *pcm->hw.ptr;
Packit 4a16fb
	hw_ptr -= frames;
Packit 4a16fb
	if (hw_ptr < 0)
Packit 4a16fb
		hw_ptr += pcm->boundary;
Packit 4a16fb
	*pcm->hw.ptr = hw_ptr;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_uframes_t hw_ptr = *pcm->hw.ptr;
Packit 4a16fb
	hw_ptr += frames;
Packit 4a16fb
	if (hw_ptr >= pcm->boundary)
Packit 4a16fb
		hw_ptr -= pcm->boundary;
Packit 4a16fb
	*pcm->hw.ptr = hw_ptr;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_mmap_write_areas(snd_pcm_t *pcm,
Packit 4a16fb
						  const snd_pcm_channel_area_t *areas,
Packit 4a16fb
						  snd_pcm_uframes_t offset,
Packit 4a16fb
						  snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_uframes_t xfer = 0;
Packit 4a16fb
Packit 4a16fb
	if (snd_pcm_mmap_playback_avail(pcm) < size) {
Packit 4a16fb
		SNDMSG("too short avail %ld to size %ld", snd_pcm_mmap_playback_avail(pcm), size);
Packit 4a16fb
		return -EPIPE;
Packit 4a16fb
	}
Packit 4a16fb
	while (size > 0) {
Packit 4a16fb
		const snd_pcm_channel_area_t *pcm_areas;
Packit 4a16fb
		snd_pcm_uframes_t pcm_offset;
Packit 4a16fb
		snd_pcm_uframes_t frames = size;
Packit 4a16fb
		snd_pcm_sframes_t result;
Packit 4a16fb
Packit 4a16fb
		__snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
Packit 4a16fb
		snd_pcm_areas_copy(pcm_areas, pcm_offset,
Packit 4a16fb
				   areas, offset, 
Packit 4a16fb
				   pcm->channels, 
Packit 4a16fb
				   frames, pcm->format);
Packit 4a16fb
		result = __snd_pcm_mmap_commit(pcm, pcm_offset, frames);
Packit 4a16fb
		if (result < 0)
Packit 4a16fb
			return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
Packit 4a16fb
		offset += result;
Packit 4a16fb
		xfer += result;
Packit 4a16fb
		size -= result;
Packit 4a16fb
	}
Packit 4a16fb
	return (snd_pcm_sframes_t)xfer;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_mmap_read_areas(snd_pcm_t *pcm,
Packit 4a16fb
						 const snd_pcm_channel_area_t *areas,
Packit 4a16fb
						 snd_pcm_uframes_t offset,
Packit 4a16fb
						 snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_uframes_t xfer = 0;
Packit 4a16fb
Packit 4a16fb
	if (snd_pcm_mmap_capture_avail(pcm) < size) {
Packit 4a16fb
		SNDMSG("too short avail %ld to size %ld", snd_pcm_mmap_capture_avail(pcm), size);
Packit 4a16fb
		return -EPIPE;
Packit 4a16fb
	}
Packit 4a16fb
	while (size > 0) {
Packit 4a16fb
		const snd_pcm_channel_area_t *pcm_areas;
Packit 4a16fb
		snd_pcm_uframes_t pcm_offset;
Packit 4a16fb
		snd_pcm_uframes_t frames = size;
Packit 4a16fb
		snd_pcm_sframes_t result;
Packit 4a16fb
		
Packit 4a16fb
		__snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
Packit 4a16fb
		snd_pcm_areas_copy(areas, offset,
Packit 4a16fb
				   pcm_areas, pcm_offset,
Packit 4a16fb
				   pcm->channels, 
Packit 4a16fb
				   frames, pcm->format);
Packit 4a16fb
		result = __snd_pcm_mmap_commit(pcm, pcm_offset, frames);
Packit 4a16fb
		if (result < 0)
Packit 4a16fb
			return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
Packit 4a16fb
		offset += result;
Packit 4a16fb
		xfer += result;
Packit 4a16fb
		size -= result;
Packit 4a16fb
	}
Packit 4a16fb
	return (snd_pcm_sframes_t)xfer;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \brief Write interleaved frames to a PCM using direct buffer (mmap)
Packit 4a16fb
 * \param pcm PCM handle
Packit 4a16fb
 * \param buffer frames containing buffer
Packit 4a16fb
 * \param size frames to be written
Packit 4a16fb
 * \return a positive number of frames actually written otherwise a
Packit 4a16fb
 * negative error code
Packit 4a16fb
 * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING)
Packit 4a16fb
 * \retval -EPIPE an underrun occurred
Packit 4a16fb
 * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)
Packit 4a16fb
 *
Packit 4a16fb
 * If the blocking behaviour is selected, then routine waits until
Packit 4a16fb
 * all requested bytes are played or put to the playback ring buffer.
Packit 4a16fb
 * The count of bytes can be less only if a signal or underrun occurred.
Packit 4a16fb
 *
Packit 4a16fb
 * If the non-blocking behaviour is selected, then routine doesn't wait at all.
Packit 4a16fb
 */
Packit 4a16fb
snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_channel_area_t areas[pcm->channels];
Packit 4a16fb
	snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
Packit 4a16fb
	return snd_pcm_write_areas(pcm, areas, 0, size,
Packit 4a16fb
				   snd_pcm_mmap_write_areas);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \brief Write non interleaved frames to a PCM using direct buffer (mmap)
Packit 4a16fb
 * \param pcm PCM handle
Packit 4a16fb
 * \param bufs frames containing buffers (one for each channel)
Packit 4a16fb
 * \param size frames to be written
Packit 4a16fb
 * \return a positive number of frames actually written otherwise a
Packit 4a16fb
 * negative error code
Packit 4a16fb
 * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING)
Packit 4a16fb
 * \retval -EPIPE an underrun occurred
Packit 4a16fb
 * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)
Packit 4a16fb
 *
Packit 4a16fb
 * If the blocking behaviour is selected, then routine waits until
Packit 4a16fb
 * all requested bytes are played or put to the playback ring buffer.
Packit 4a16fb
 * The count of bytes can be less only if a signal or underrun occurred.
Packit 4a16fb
 *
Packit 4a16fb
 * If the non-blocking behaviour is selected, then routine doesn't wait at all.
Packit 4a16fb
 */
Packit 4a16fb
snd_pcm_sframes_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_channel_area_t areas[pcm->channels];
Packit 4a16fb
	snd_pcm_areas_from_bufs(pcm, areas, bufs);
Packit 4a16fb
	return snd_pcm_write_areas(pcm, areas, 0, size,
Packit 4a16fb
				   snd_pcm_mmap_write_areas);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \brief Read interleaved frames from a PCM using direct buffer (mmap)
Packit 4a16fb
 * \param pcm PCM handle
Packit 4a16fb
 * \param buffer frames containing buffer
Packit 4a16fb
 * \param size frames to be written
Packit 4a16fb
 * \return a positive number of frames actually read otherwise a
Packit 4a16fb
 * negative error code
Packit 4a16fb
 * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING)
Packit 4a16fb
 * \retval -EPIPE an overrun occurred
Packit 4a16fb
 * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)
Packit 4a16fb
 *
Packit 4a16fb
 * If the blocking behaviour was selected, then routine waits until
Packit 4a16fb
 * all requested bytes are filled. The count of bytes can be less only
Packit 4a16fb
 * if a signal or underrun occurred.
Packit 4a16fb
 *
Packit 4a16fb
 * If the non-blocking behaviour is selected, then routine doesn't wait at all.
Packit 4a16fb
 */
Packit 4a16fb
snd_pcm_sframes_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_channel_area_t areas[pcm->channels];
Packit 4a16fb
	snd_pcm_areas_from_buf(pcm, areas, buffer);
Packit 4a16fb
	return snd_pcm_read_areas(pcm, areas, 0, size,
Packit 4a16fb
				  snd_pcm_mmap_read_areas);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \brief Read non interleaved frames to a PCM using direct buffer (mmap)
Packit 4a16fb
 * \param pcm PCM handle
Packit 4a16fb
 * \param bufs frames containing buffers (one for each channel)
Packit 4a16fb
 * \param size frames to be written
Packit 4a16fb
 * \return a positive number of frames actually read otherwise a
Packit 4a16fb
 * negative error code
Packit 4a16fb
 * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING)
Packit 4a16fb
 * \retval -EPIPE an overrun occurred
Packit 4a16fb
 * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)
Packit 4a16fb
 *
Packit 4a16fb
 * If the blocking behaviour was selected, then routine waits until
Packit 4a16fb
 * all requested bytes are filled. The count of bytes can be less only
Packit 4a16fb
 * if a signal or underrun occurred.
Packit 4a16fb
 *
Packit 4a16fb
 * If the non-blocking behaviour is selected, then routine doesn't wait at all.
Packit 4a16fb
 */
Packit 4a16fb
snd_pcm_sframes_t snd_pcm_mmap_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_channel_area_t areas[pcm->channels];
Packit 4a16fb
	snd_pcm_areas_from_bufs(pcm, areas, bufs);
Packit 4a16fb
	return snd_pcm_read_areas(pcm, areas, 0, size,
Packit 4a16fb
				  snd_pcm_mmap_read_areas);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
int snd_pcm_channel_info_shm(snd_pcm_t *pcm, snd_pcm_channel_info_t *info, int shmid)
Packit 4a16fb
{
Packit 4a16fb
	switch (pcm->access) {
Packit 4a16fb
	case SND_PCM_ACCESS_MMAP_INTERLEAVED:
Packit 4a16fb
	case SND_PCM_ACCESS_RW_INTERLEAVED:
Packit 4a16fb
		info->first = info->channel * pcm->sample_bits;
Packit 4a16fb
		info->step = pcm->frame_bits;
Packit 4a16fb
		break;
Packit 4a16fb
	case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
Packit 4a16fb
	case SND_PCM_ACCESS_RW_NONINTERLEAVED:
Packit 4a16fb
		info->first = 0;
Packit 4a16fb
		info->step = pcm->sample_bits;
Packit 4a16fb
		break;
Packit 4a16fb
	default:
Packit 4a16fb
		SNDMSG("invalid access type %d", pcm->access);
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
	info->addr = 0;
Packit 4a16fb
	if (pcm->hw_flags & SND_PCM_HW_PARAMS_EXPORT_BUFFER) {
Packit 4a16fb
		info->type = SND_PCM_AREA_SHM;
Packit 4a16fb
		info->u.shm.shmid = shmid;
Packit 4a16fb
		info->u.shm.area = NULL;
Packit 4a16fb
	} else
Packit 4a16fb
		info->type = SND_PCM_AREA_LOCAL;
Packit 4a16fb
	return 0;
Packit 4a16fb
}	
Packit 4a16fb
Packit 4a16fb
int snd_pcm_mmap(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	unsigned int c;
Packit 4a16fb
	assert(pcm);
Packit 4a16fb
	if (CHECK_SANITY(! pcm->setup)) {
Packit 4a16fb
		SNDMSG("PCM not set up");
Packit 4a16fb
		return -EIO;
Packit 4a16fb
	}
Packit 4a16fb
	if (CHECK_SANITY(pcm->mmap_channels || pcm->running_areas)) {
Packit 4a16fb
		SNDMSG("Already mmapped");
Packit 4a16fb
		return -EBUSY;
Packit 4a16fb
	}
Packit 4a16fb
	if (pcm->ops->mmap)
Packit 4a16fb
		err = pcm->ops->mmap(pcm);
Packit 4a16fb
	else
Packit 4a16fb
		err = -ENOSYS;
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	if (pcm->mmap_shadow)
Packit 4a16fb
		return 0;
Packit 4a16fb
	pcm->mmap_channels = calloc(pcm->channels, sizeof(pcm->mmap_channels[0]));
Packit 4a16fb
	if (!pcm->mmap_channels)
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	pcm->running_areas = calloc(pcm->channels, sizeof(pcm->running_areas[0]));
Packit 4a16fb
	if (!pcm->running_areas) {
Packit 4a16fb
		free(pcm->mmap_channels);
Packit 4a16fb
		pcm->mmap_channels = NULL;
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	}
Packit 4a16fb
	for (c = 0; c < pcm->channels; ++c) {
Packit 4a16fb
		snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
Packit 4a16fb
		i->channel = c;
Packit 4a16fb
		err = snd_pcm_channel_info(pcm, i);
Packit 4a16fb
		if (err < 0) {
Packit 4a16fb
			free(pcm->mmap_channels);
Packit 4a16fb
			free(pcm->running_areas);
Packit 4a16fb
			pcm->mmap_channels = NULL;
Packit 4a16fb
			pcm->running_areas = NULL;
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	for (c = 0; c < pcm->channels; ++c) {
Packit 4a16fb
		snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
Packit 4a16fb
		snd_pcm_channel_area_t *a = &pcm->running_areas[c];
Packit 4a16fb
		char *ptr;
Packit 4a16fb
		size_t size;
Packit 4a16fb
		unsigned int c1;
Packit 4a16fb
		if (i->addr) {
Packit 4a16fb
        		a->addr = i->addr;
Packit 4a16fb
        		a->first = i->first;
Packit 4a16fb
        		a->step = i->step;
Packit 4a16fb
		        continue;
Packit 4a16fb
                }
Packit 4a16fb
                size = i->first + i->step * (pcm->buffer_size - 1) + pcm->sample_bits;
Packit 4a16fb
		for (c1 = c + 1; c1 < pcm->channels; ++c1) {
Packit 4a16fb
			snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
Packit 4a16fb
			size_t s;
Packit 4a16fb
			if (i1->type != i->type)
Packit 4a16fb
				continue;
Packit 4a16fb
			switch (i1->type) {
Packit 4a16fb
			case SND_PCM_AREA_MMAP:
Packit 4a16fb
				if (i1->u.mmap.fd != i->u.mmap.fd ||
Packit 4a16fb
				    i1->u.mmap.offset != i->u.mmap.offset)
Packit 4a16fb
					continue;
Packit 4a16fb
				break;
Packit 4a16fb
			case SND_PCM_AREA_SHM:
Packit 4a16fb
				if (i1->u.shm.shmid != i->u.shm.shmid)
Packit 4a16fb
					continue;
Packit 4a16fb
				break;
Packit 4a16fb
			case SND_PCM_AREA_LOCAL:
Packit 4a16fb
				break;
Packit 4a16fb
			default:
Packit 4a16fb
				assert(0);
Packit 4a16fb
			}
Packit 4a16fb
			s = i1->first + i1->step * (pcm->buffer_size - 1) + pcm->sample_bits;
Packit 4a16fb
			if (s > size)
Packit 4a16fb
				size = s;
Packit 4a16fb
		}
Packit 4a16fb
		size = (size + 7) / 8;
Packit 4a16fb
		size = page_align(size);
Packit 4a16fb
		switch (i->type) {
Packit 4a16fb
		case SND_PCM_AREA_MMAP:
Packit 4a16fb
			ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, i->u.mmap.fd, i->u.mmap.offset);
Packit 4a16fb
			if (ptr == MAP_FAILED) {
Packit 4a16fb
				SYSERR("mmap failed");
Packit 4a16fb
				return -errno;
Packit 4a16fb
			}
Packit 4a16fb
			i->addr = ptr;
Packit 4a16fb
			break;
Packit 4a16fb
		case SND_PCM_AREA_SHM:
Packit 4a16fb
#ifdef HAVE_SYS_SHM_H
Packit 4a16fb
			if (i->u.shm.shmid < 0) {
Packit 4a16fb
				int id;
Packit 4a16fb
				/* FIXME: safer permission? */
Packit 4a16fb
				id = shmget(IPC_PRIVATE, size, 0666);
Packit 4a16fb
				if (id < 0) {
Packit 4a16fb
					SYSERR("shmget failed");
Packit 4a16fb
					return -errno;
Packit 4a16fb
				}
Packit 4a16fb
				i->u.shm.shmid = id;
Packit 4a16fb
				ptr = shmat(i->u.shm.shmid, 0, 0);
Packit 4a16fb
				if (ptr == (void *) -1) {
Packit 4a16fb
					SYSERR("shmat failed");
Packit 4a16fb
					return -errno;
Packit 4a16fb
				}
Packit 4a16fb
				/* automatically remove segment if not used */
Packit 4a16fb
				if (shmctl(id, IPC_RMID, NULL) < 0){
Packit 4a16fb
					SYSERR("shmctl mark remove failed");
Packit 4a16fb
					return -errno;
Packit 4a16fb
				}
Packit 4a16fb
				i->u.shm.area = snd_shm_area_create(id, ptr);
Packit 4a16fb
				if (i->u.shm.area == NULL) {
Packit 4a16fb
					SYSERR("snd_shm_area_create failed");
Packit 4a16fb
					return -ENOMEM;
Packit 4a16fb
				}
Packit 4a16fb
				if (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
Packit 4a16fb
				    pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
Packit 4a16fb
					unsigned int c1;
Packit 4a16fb
					for (c1 = c + 1; c1 < pcm->channels; c1++) {
Packit 4a16fb
						snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
Packit 4a16fb
						if (i1->u.shm.shmid < 0) {
Packit 4a16fb
							i1->u.shm.shmid = id;
Packit 4a16fb
							i1->u.shm.area = snd_shm_area_share(i->u.shm.area);
Packit 4a16fb
						}
Packit 4a16fb
					}
Packit 4a16fb
				}
Packit 4a16fb
			} else {
Packit 4a16fb
				ptr = shmat(i->u.shm.shmid, 0, 0);
Packit 4a16fb
				if (ptr == (void*) -1) {
Packit 4a16fb
					SYSERR("shmat failed");
Packit 4a16fb
					return -errno;
Packit 4a16fb
				}
Packit 4a16fb
			}
Packit 4a16fb
			i->addr = ptr;
Packit 4a16fb
			break;
Packit 4a16fb
#else
Packit 4a16fb
			SYSERR("shm support not available");
Packit 4a16fb
			return -ENOSYS;
Packit 4a16fb
#endif
Packit 4a16fb
		case SND_PCM_AREA_LOCAL:
Packit 4a16fb
			ptr = malloc(size);
Packit 4a16fb
			if (ptr == NULL) {
Packit 4a16fb
				SYSERR("malloc failed");
Packit 4a16fb
				return -errno;
Packit 4a16fb
			}
Packit 4a16fb
			i->addr = ptr;
Packit 4a16fb
			break;
Packit 4a16fb
		default:
Packit 4a16fb
			assert(0);
Packit 4a16fb
		}
Packit 4a16fb
		for (c1 = c + 1; c1 < pcm->channels; ++c1) {
Packit 4a16fb
			snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
Packit 4a16fb
			if (i1->type != i->type)
Packit 4a16fb
				continue;
Packit 4a16fb
			switch (i1->type) {
Packit 4a16fb
			case SND_PCM_AREA_MMAP:
Packit 4a16fb
				if (i1->u.mmap.fd != i->u.mmap.fd ||
Packit 4a16fb
                                    i1->u.mmap.offset != i->u.mmap.offset)
Packit 4a16fb
					continue;
Packit 4a16fb
				break;
Packit 4a16fb
			case SND_PCM_AREA_SHM:
Packit 4a16fb
				if (i1->u.shm.shmid != i->u.shm.shmid)
Packit 4a16fb
					continue;
Packit 4a16fb
				/* fall through */
Packit 4a16fb
			case SND_PCM_AREA_LOCAL:
Packit 4a16fb
				if (pcm->access != SND_PCM_ACCESS_MMAP_INTERLEAVED &&
Packit 4a16fb
				    pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED)
Packit 4a16fb
				        continue;
Packit 4a16fb
				break;
Packit 4a16fb
			default:
Packit 4a16fb
				assert(0);
Packit 4a16fb
			}
Packit 4a16fb
			i1->addr = i->addr;
Packit 4a16fb
		}
Packit 4a16fb
		a->addr = i->addr;
Packit 4a16fb
		a->first = i->first;
Packit 4a16fb
		a->step = i->step;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
int snd_pcm_munmap(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	unsigned int c;
Packit 4a16fb
	assert(pcm);
Packit 4a16fb
	if (CHECK_SANITY(! pcm->mmap_channels)) {
Packit 4a16fb
		SNDMSG("Not mmapped");
Packit 4a16fb
		return -ENXIO;
Packit 4a16fb
	}
Packit 4a16fb
	if (pcm->mmap_shadow) {
Packit 4a16fb
		if (pcm->ops->munmap)
Packit 4a16fb
			return pcm->ops->munmap(pcm);
Packit 4a16fb
		else
Packit 4a16fb
			return -ENOSYS;
Packit 4a16fb
	}
Packit 4a16fb
	for (c = 0; c < pcm->channels; ++c) {
Packit 4a16fb
		snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
Packit 4a16fb
		unsigned int c1;
Packit 4a16fb
		size_t size = i->first + i->step * (pcm->buffer_size - 1) + pcm->sample_bits;
Packit 4a16fb
		if (!i->addr)
Packit 4a16fb
			continue;
Packit 4a16fb
		for (c1 = c + 1; c1 < pcm->channels; ++c1) {
Packit 4a16fb
			snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
Packit 4a16fb
			size_t s;
Packit 4a16fb
			if (i1->addr != i->addr)
Packit 4a16fb
				continue;
Packit 4a16fb
			i1->addr = NULL;
Packit 4a16fb
			s = i1->first + i1->step * (pcm->buffer_size - 1) + pcm->sample_bits;
Packit 4a16fb
			if (s > size)
Packit 4a16fb
				size = s;
Packit 4a16fb
		}
Packit 4a16fb
		size = (size + 7) / 8;
Packit 4a16fb
		size = page_align(size);
Packit 4a16fb
		switch (i->type) {
Packit 4a16fb
		case SND_PCM_AREA_MMAP:
Packit 4a16fb
			err = munmap(i->addr, size);
Packit 4a16fb
			if (err < 0) {
Packit 4a16fb
				SYSERR("mmap failed");
Packit 4a16fb
				return -errno;
Packit 4a16fb
			}
Packit 4a16fb
			errno = 0;
Packit 4a16fb
			break;
Packit 4a16fb
		case SND_PCM_AREA_SHM:
Packit 4a16fb
#ifdef HAVE_SYS_SHM_H
Packit 4a16fb
			if (i->u.shm.area) {
Packit 4a16fb
				snd_shm_area_destroy(i->u.shm.area);
Packit 4a16fb
				i->u.shm.area = NULL;
Packit 4a16fb
				if (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
Packit 4a16fb
				    pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
Packit 4a16fb
					unsigned int c1;
Packit 4a16fb
					for (c1 = c + 1; c1 < pcm->channels; c1++) {
Packit 4a16fb
						snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
Packit 4a16fb
						if (i1->u.shm.area) {
Packit 4a16fb
							snd_shm_area_destroy(i1->u.shm.area);
Packit 4a16fb
							i1->u.shm.area = NULL;
Packit 4a16fb
						}
Packit 4a16fb
					}
Packit 4a16fb
				}
Packit 4a16fb
			}
Packit 4a16fb
			break;
Packit 4a16fb
#else
Packit 4a16fb
			SYSERR("shm support not available");
Packit 4a16fb
			return -ENOSYS;
Packit 4a16fb
#endif
Packit 4a16fb
		case SND_PCM_AREA_LOCAL:
Packit 4a16fb
			free(i->addr);
Packit 4a16fb
			break;
Packit 4a16fb
		default:
Packit 4a16fb
			assert(0);
Packit 4a16fb
		}
Packit 4a16fb
		i->addr = NULL;
Packit 4a16fb
	}
Packit 4a16fb
	if (pcm->ops->munmap)
Packit 4a16fb
		err = pcm->ops->munmap(pcm);
Packit 4a16fb
	else
Packit 4a16fb
		err = -ENOSYS;
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	free(pcm->mmap_channels);
Packit 4a16fb
	free(pcm->running_areas);
Packit 4a16fb
	pcm->mmap_channels = NULL;
Packit 4a16fb
	pcm->running_areas = NULL;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/* called in pcm lock */
Packit 4a16fb
snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset,
Packit 4a16fb
				     snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_uframes_t xfer = 0;
Packit 4a16fb
	snd_pcm_sframes_t err = 0;
Packit 4a16fb
	if (! size)
Packit 4a16fb
		return 0;
Packit 4a16fb
	while (xfer < size) {
Packit 4a16fb
		snd_pcm_uframes_t frames = size - xfer;
Packit 4a16fb
		snd_pcm_uframes_t cont = pcm->buffer_size - offset;
Packit 4a16fb
		if (cont < frames)
Packit 4a16fb
			frames = cont;
Packit 4a16fb
		switch (pcm->access) {
Packit 4a16fb
		case SND_PCM_ACCESS_MMAP_INTERLEAVED:
Packit 4a16fb
		{
Packit 4a16fb
			const snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm);
Packit 4a16fb
			const char *buf = snd_pcm_channel_area_addr(a, offset);
Packit 4a16fb
			snd_pcm_unlock(pcm); /* to avoid deadlock */
Packit 4a16fb
			err = _snd_pcm_writei(pcm, buf, frames);
Packit 4a16fb
			snd_pcm_lock(pcm);
Packit 4a16fb
			if (err >= 0)
Packit 4a16fb
				frames = err;
Packit 4a16fb
			break;
Packit 4a16fb
		}
Packit 4a16fb
		case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
Packit 4a16fb
		{
Packit 4a16fb
			unsigned int channels = pcm->channels;
Packit 4a16fb
			unsigned int c;
Packit 4a16fb
			void *bufs[channels];
Packit 4a16fb
			const snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm);
Packit 4a16fb
			for (c = 0; c < channels; ++c) {
Packit 4a16fb
				const snd_pcm_channel_area_t *a = &areas[c];
Packit 4a16fb
				bufs[c] = snd_pcm_channel_area_addr(a, offset);
Packit 4a16fb
			}
Packit 4a16fb
			snd_pcm_unlock(pcm); /* to avoid deadlock */
Packit 4a16fb
			err = _snd_pcm_writen(pcm, bufs, frames);
Packit 4a16fb
			snd_pcm_lock(pcm);
Packit 4a16fb
			if (err >= 0)
Packit 4a16fb
				frames = err;
Packit 4a16fb
			break;
Packit 4a16fb
		}
Packit 4a16fb
		default:
Packit 4a16fb
			SNDMSG("invalid access type %d", pcm->access);
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
		}
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			break;
Packit 4a16fb
		xfer += frames;
Packit 4a16fb
		offset = (offset + frames) % pcm->buffer_size;
Packit 4a16fb
	}
Packit 4a16fb
	if (xfer > 0)
Packit 4a16fb
		return xfer;
Packit 4a16fb
	return err;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/* called in pcm lock */
Packit 4a16fb
snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset,
Packit 4a16fb
				    snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_uframes_t xfer = 0;
Packit 4a16fb
	snd_pcm_sframes_t err = 0;
Packit 4a16fb
	if (! size)
Packit 4a16fb
		return 0;
Packit 4a16fb
	while (xfer < size) {
Packit 4a16fb
		snd_pcm_uframes_t frames = size - xfer;
Packit 4a16fb
		snd_pcm_uframes_t cont = pcm->buffer_size - offset;
Packit 4a16fb
		if (cont < frames)
Packit 4a16fb
			frames = cont;
Packit 4a16fb
		switch (pcm->access) {
Packit 4a16fb
		case SND_PCM_ACCESS_MMAP_INTERLEAVED:
Packit 4a16fb
		{
Packit 4a16fb
			const snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm);
Packit 4a16fb
			char *buf = snd_pcm_channel_area_addr(a, offset);
Packit 4a16fb
			snd_pcm_unlock(pcm); /* to avoid deadlock */
Packit 4a16fb
			err = _snd_pcm_readi(pcm, buf, frames);
Packit 4a16fb
			snd_pcm_lock(pcm);
Packit 4a16fb
			if (err >= 0)
Packit 4a16fb
				frames = err;
Packit 4a16fb
			break;
Packit 4a16fb
		}
Packit 4a16fb
		case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
Packit 4a16fb
		{
Packit 4a16fb
			snd_pcm_uframes_t channels = pcm->channels;
Packit 4a16fb
			unsigned int c;
Packit 4a16fb
			void *bufs[channels];
Packit 4a16fb
			const snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm);
Packit 4a16fb
			for (c = 0; c < channels; ++c) {
Packit 4a16fb
				const snd_pcm_channel_area_t *a = &areas[c];
Packit 4a16fb
				bufs[c] = snd_pcm_channel_area_addr(a, offset);
Packit 4a16fb
			}
Packit 4a16fb
			snd_pcm_unlock(pcm); /* to avoid deadlock */
Packit 4a16fb
			err = _snd_pcm_readn(pcm->fast_op_arg, bufs, frames);
Packit 4a16fb
			snd_pcm_lock(pcm);
Packit 4a16fb
			if (err >= 0)
Packit 4a16fb
				frames = err;
Packit 4a16fb
			break;
Packit 4a16fb
		}
Packit 4a16fb
		default:
Packit 4a16fb
			SNDMSG("invalid access type %d", pcm->access);
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
		}
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			break;
Packit 4a16fb
		xfer += frames;
Packit 4a16fb
		offset = (offset + frames) % pcm->buffer_size;
Packit 4a16fb
	}
Packit 4a16fb
	if (xfer > 0)
Packit 4a16fb
		return xfer;
Packit 4a16fb
	return err;
Packit 4a16fb
}