Blame src/pcm/pcm_plugin.c

Packit 4a16fb
/**
Packit 4a16fb
 * \file pcm/pcm_plugin.c
Packit 4a16fb
 * \ingroup PCM
Packit 4a16fb
 * \brief PCM Interface
Packit 4a16fb
 * \author Jaroslav Kysela <perex@perex.cz>
Packit 4a16fb
 * \author Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 * \date 2000-2001
Packit 4a16fb
 */
Packit 4a16fb
/*
Packit 4a16fb
 *  PCM - Common plugin code
Packit 4a16fb
 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 *
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
/*!
Packit 4a16fb
Packit 4a16fb
\page pcm_plugins PCM (digital audio) plugins
Packit 4a16fb
Packit 4a16fb
PCM plugins extends functionality and features of PCM devices.
Packit 4a16fb
The plugins take care about various sample conversions, sample
Packit 4a16fb
copying among channels and so on.
Packit 4a16fb
Packit 4a16fb
\section pcm_plugins_slave Slave definition
Packit 4a16fb
Packit 4a16fb
The slave plugin can be specified directly with a string or the definition
Packit 4a16fb
can be entered inside a compound configuration node. Some restrictions can
Packit 4a16fb
be also specified (like static rate or count of channels).
Packit 4a16fb
Packit 4a16fb
\code
Packit 4a16fb
pcm_slave.NAME {
Packit 4a16fb
	pcm STR		# PCM name
Packit 4a16fb
	# or
Packit 4a16fb
	pcm { }		# PCM definition
Packit 4a16fb
	format STR	# Format or "unchanged"
Packit 4a16fb
	channels INT	# Count of channels or "unchanged" string
Packit 4a16fb
	rate INT	# Rate in Hz or "unchanged" string
Packit 4a16fb
	period_time INT	# Period time in us or "unchanged" string
Packit 4a16fb
	buffer_time INT # Buffer time in us or "unchanged" string
Packit 4a16fb
}
Packit 4a16fb
\endcode
Packit 4a16fb
Packit 4a16fb
Example:
Packit 4a16fb
Packit 4a16fb
\code
Packit 4a16fb
pcm_slave.slave_rate44100Hz {
Packit 4a16fb
	pcm "hw:0,0"
Packit 4a16fb
	rate 44100
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
pcm.rate44100Hz {
Packit 4a16fb
	type plug
Packit 4a16fb
	slave slave_rate44100Hz
Packit 4a16fb
}
Packit 4a16fb
\endcode
Packit 4a16fb
Packit 4a16fb
The equivalent configuration (in one compound):
Packit 4a16fb
Packit 4a16fb
\code
Packit 4a16fb
pcm.rate44100Hz {
Packit 4a16fb
	type plug
Packit 4a16fb
	slave {
Packit 4a16fb
		pcm "hw:0,0"
Packit 4a16fb
		rate 44100
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
\endcode
Packit 4a16fb
Packit 4a16fb
*/
Packit 4a16fb
  
Packit 4a16fb
#include <limits.h>
Packit 4a16fb
#include "pcm_local.h"
Packit 4a16fb
#include "pcm_plugin.h"
Packit 4a16fb
Packit 4a16fb
#ifndef DOC_HIDDEN
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t
Packit 4a16fb
snd_pcm_plugin_undo_read(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
Packit 4a16fb
			 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
Packit 4a16fb
			 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
Packit 4a16fb
			 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
Packit 4a16fb
			 snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
Packit 4a16fb
{
Packit 4a16fb
	return -EIO;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t
Packit 4a16fb
snd_pcm_plugin_undo_write(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
Packit 4a16fb
			  const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
Packit 4a16fb
			  snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
Packit 4a16fb
			  snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
Packit 4a16fb
			  snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
Packit 4a16fb
{
Packit 4a16fb
	return -EIO;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
snd_pcm_sframes_t
Packit 4a16fb
snd_pcm_plugin_undo_read_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
Packit 4a16fb
				 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
Packit 4a16fb
				 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
Packit 4a16fb
				 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
Packit 4a16fb
				 snd_pcm_uframes_t slave_undo_size)
Packit 4a16fb
{
Packit 4a16fb
	return slave_undo_size;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
snd_pcm_sframes_t
Packit 4a16fb
snd_pcm_plugin_undo_write_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
Packit 4a16fb
				  const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
Packit 4a16fb
				  snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
Packit 4a16fb
				  snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
Packit 4a16fb
				  snd_pcm_uframes_t slave_undo_size)
Packit 4a16fb
{
Packit 4a16fb
	return slave_undo_size;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
void snd_pcm_plugin_init(snd_pcm_plugin_t *plugin)
Packit 4a16fb
{
Packit 4a16fb
	memset(plugin, 0, sizeof(snd_pcm_plugin_t));
Packit 4a16fb
	plugin->undo_read = snd_pcm_plugin_undo_read;
Packit 4a16fb
	plugin->undo_write = snd_pcm_plugin_undo_write;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plugin_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plugin_t *plugin = pcm->private_data;
Packit 4a16fb
	snd_pcm_sframes_t sd;
Packit 4a16fb
	int err = snd_pcm_delay(plugin->gen.slave, &sd);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
        if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
Packit 4a16fb
	    pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
Packit 4a16fb
	    pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
Packit 4a16fb
                sd += snd_pcm_mmap_capture_avail(pcm);
Packit 4a16fb
        }        
Packit 4a16fb
Packit 4a16fb
	*delayp = sd;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plugin_prepare(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plugin_t *plugin = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	err = snd_pcm_prepare(plugin->gen.slave);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	*pcm->hw.ptr = 0;
Packit 4a16fb
	*pcm->appl.ptr = 0;
Packit 4a16fb
	if (plugin->init) {
Packit 4a16fb
		err = plugin->init(pcm);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plugin_reset(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plugin_t *plugin = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	err = snd_pcm_reset(plugin->gen.slave);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	*pcm->hw.ptr = 0;
Packit 4a16fb
	*pcm->appl.ptr = 0;
Packit 4a16fb
	if (plugin->init) {
Packit 4a16fb
		err = plugin->init(pcm);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_plugin_rewindable(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	return snd_pcm_mmap_hw_rewindable(pcm);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
snd_pcm_sframes_t snd_pcm_plugin_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plugin_t *plugin = pcm->private_data;
Packit 4a16fb
	snd_pcm_sframes_t n = snd_pcm_plugin_rewindable(pcm);
Packit 4a16fb
	snd_pcm_sframes_t sframes;
Packit 4a16fb
Packit 4a16fb
	if ((snd_pcm_uframes_t)n < frames)
Packit 4a16fb
		frames = n;
Packit 4a16fb
	if (frames == 0)
Packit 4a16fb
		return 0;
Packit 4a16fb
	
Packit 4a16fb
        sframes = frames;
Packit 4a16fb
	sframes = snd_pcm_rewind(plugin->gen.slave, sframes);
Packit 4a16fb
	if (sframes < 0)
Packit 4a16fb
		return sframes;
Packit 4a16fb
	snd_pcm_mmap_appl_backward(pcm, (snd_pcm_uframes_t) sframes);
Packit 4a16fb
	return (snd_pcm_sframes_t) sframes;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_plugin_forwardable(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	return snd_pcm_mmap_avail(pcm);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
snd_pcm_sframes_t snd_pcm_plugin_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plugin_t *plugin = pcm->private_data;
Packit 4a16fb
	snd_pcm_sframes_t n = snd_pcm_plugin_forwardable(pcm);
Packit 4a16fb
	snd_pcm_sframes_t sframes;
Packit 4a16fb
Packit 4a16fb
	if ((snd_pcm_uframes_t)n < frames)
Packit 4a16fb
		frames = n;
Packit 4a16fb
	if (frames == 0)
Packit 4a16fb
		return 0;
Packit 4a16fb
	
Packit 4a16fb
        sframes = frames;
Packit 4a16fb
	sframes = INTERNAL(snd_pcm_forward)(plugin->gen.slave, sframes);
Packit 4a16fb
	if (sframes < 0)
Packit 4a16fb
		return sframes;
Packit 4a16fb
	snd_pcm_mmap_appl_forward(pcm, (snd_pcm_uframes_t) frames);
Packit 4a16fb
	return (snd_pcm_sframes_t) frames;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_plugin_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_plugin_t *plugin = pcm->private_data;
Packit 4a16fb
	snd_pcm_t *slave = plugin->gen.slave;
Packit 4a16fb
	snd_pcm_uframes_t xfer = 0;
Packit 4a16fb
	snd_pcm_sframes_t result;
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	while (size > 0) {
Packit 4a16fb
		snd_pcm_uframes_t frames = size;
Packit 4a16fb
		const snd_pcm_channel_area_t *slave_areas;
Packit 4a16fb
		snd_pcm_uframes_t slave_offset;
Packit 4a16fb
		snd_pcm_uframes_t slave_frames = ULONG_MAX;
Packit 4a16fb
		
Packit 4a16fb
		result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
Packit 4a16fb
		if (result < 0) {
Packit 4a16fb
			err = result;
Packit 4a16fb
			goto error;
Packit 4a16fb
		}
Packit 4a16fb
		if (slave_frames == 0)
Packit 4a16fb
			break;
Packit 4a16fb
		frames = plugin->write(pcm, areas, offset, frames,
Packit 4a16fb
				       slave_areas, slave_offset, &slave_frames);
Packit 4a16fb
		if (CHECK_SANITY(slave_frames > snd_pcm_mmap_playback_avail(slave))) {
Packit 4a16fb
			SNDMSG("write overflow %ld > %ld", slave_frames,
Packit 4a16fb
			       snd_pcm_mmap_playback_avail(slave));
Packit 4a16fb
			err = -EPIPE;
Packit 4a16fb
			goto error;
Packit 4a16fb
		}
Packit 4a16fb
		result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
Packit 4a16fb
		if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
Packit 4a16fb
			snd_pcm_sframes_t res;
Packit 4a16fb
			res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
Packit 4a16fb
			if (res < 0) {
Packit 4a16fb
				err = res;
Packit 4a16fb
				goto error;
Packit 4a16fb
			}
Packit 4a16fb
			frames -= res;
Packit 4a16fb
		}
Packit 4a16fb
		if (result <= 0) {
Packit 4a16fb
			err = result;
Packit 4a16fb
			goto error;
Packit 4a16fb
		}
Packit 4a16fb
		snd_pcm_mmap_appl_forward(pcm, frames);
Packit 4a16fb
		offset += frames;
Packit 4a16fb
		xfer += frames;
Packit 4a16fb
		size -= frames;
Packit 4a16fb
	}
Packit 4a16fb
	return (snd_pcm_sframes_t)xfer;
Packit 4a16fb
Packit 4a16fb
 error:
Packit 4a16fb
	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_plugin_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_plugin_t *plugin = pcm->private_data;
Packit 4a16fb
	snd_pcm_t *slave = plugin->gen.slave;
Packit 4a16fb
	snd_pcm_uframes_t xfer = 0;
Packit 4a16fb
	snd_pcm_sframes_t result;
Packit 4a16fb
	int err;
Packit 4a16fb
	
Packit 4a16fb
	while (size > 0) {
Packit 4a16fb
		snd_pcm_uframes_t frames = size;
Packit 4a16fb
		const snd_pcm_channel_area_t *slave_areas;
Packit 4a16fb
		snd_pcm_uframes_t slave_offset;
Packit 4a16fb
		snd_pcm_uframes_t slave_frames = ULONG_MAX;
Packit 4a16fb
		
Packit 4a16fb
		result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
Packit 4a16fb
		if (result < 0) {
Packit 4a16fb
			err = result;
Packit 4a16fb
			goto error;
Packit 4a16fb
		}
Packit 4a16fb
		if (slave_frames == 0)
Packit 4a16fb
			break;
Packit 4a16fb
		frames = (plugin->read)(pcm, areas, offset, frames,
Packit 4a16fb
				      slave_areas, slave_offset, &slave_frames);
Packit 4a16fb
		if (CHECK_SANITY(slave_frames > snd_pcm_mmap_capture_avail(slave))) {
Packit 4a16fb
			SNDMSG("read overflow %ld > %ld", slave_frames,
Packit 4a16fb
			       snd_pcm_mmap_playback_avail(slave));
Packit 4a16fb
			err = -EPIPE;
Packit 4a16fb
			goto error;
Packit 4a16fb
		}
Packit 4a16fb
		result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
Packit 4a16fb
		if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
Packit 4a16fb
			snd_pcm_sframes_t res;
Packit 4a16fb
			
Packit 4a16fb
			res = plugin->undo_read(slave, areas, offset, frames, slave_frames - result);
Packit 4a16fb
			if (res < 0) {
Packit 4a16fb
				err = res;
Packit 4a16fb
				goto error;
Packit 4a16fb
			}
Packit 4a16fb
			frames -= res;
Packit 4a16fb
		}
Packit 4a16fb
		if (result <= 0) {
Packit 4a16fb
			err = result;
Packit 4a16fb
			goto error;
Packit 4a16fb
		}
Packit 4a16fb
		snd_pcm_mmap_appl_forward(pcm, frames);
Packit 4a16fb
		offset += frames;
Packit 4a16fb
		xfer += frames;
Packit 4a16fb
		size -= frames;
Packit 4a16fb
	}
Packit 4a16fb
	return (snd_pcm_sframes_t)xfer;
Packit 4a16fb
Packit 4a16fb
 error:
Packit 4a16fb
	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t
Packit 4a16fb
snd_pcm_plugin_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_plugin_write_areas);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t
Packit 4a16fb
snd_pcm_plugin_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_plugin_write_areas);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t
Packit 4a16fb
snd_pcm_plugin_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_plugin_read_areas);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t
Packit 4a16fb
snd_pcm_plugin_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_plugin_read_areas);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t
Packit 4a16fb
snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm,
Packit 4a16fb
			   snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
Packit 4a16fb
			   snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plugin_t *plugin = pcm->private_data;
Packit 4a16fb
	snd_pcm_t *slave = plugin->gen.slave;
Packit 4a16fb
	const snd_pcm_channel_area_t *areas;
Packit 4a16fb
	snd_pcm_uframes_t appl_offset;
Packit 4a16fb
	snd_pcm_sframes_t slave_size;
Packit 4a16fb
	snd_pcm_sframes_t xfer;
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
Packit 4a16fb
		snd_pcm_mmap_appl_forward(pcm, size);
Packit 4a16fb
		return size;
Packit 4a16fb
	}
Packit 4a16fb
	slave_size = snd_pcm_avail_update(slave);
Packit 4a16fb
	if (slave_size < 0)
Packit 4a16fb
		return slave_size;
Packit 4a16fb
	areas = snd_pcm_mmap_areas(pcm);
Packit 4a16fb
	appl_offset = snd_pcm_mmap_offset(pcm);
Packit 4a16fb
	xfer = 0;
Packit 4a16fb
	while (size > 0 && slave_size > 0) {
Packit 4a16fb
		snd_pcm_uframes_t frames = size;
Packit 4a16fb
		snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
Packit 4a16fb
		const snd_pcm_channel_area_t *slave_areas;
Packit 4a16fb
		snd_pcm_uframes_t slave_offset;
Packit 4a16fb
		snd_pcm_uframes_t slave_frames = ULONG_MAX;
Packit 4a16fb
		snd_pcm_sframes_t result;
Packit 4a16fb
Packit 4a16fb
		result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
Packit 4a16fb
		if (result < 0) {
Packit 4a16fb
			err = result;
Packit 4a16fb
			goto error;
Packit 4a16fb
		}
Packit 4a16fb
		if (frames > cont)
Packit 4a16fb
			frames = cont;
Packit 4a16fb
		frames = plugin->write(pcm, areas, appl_offset, frames,
Packit 4a16fb
				       slave_areas, slave_offset, &slave_frames);
Packit 4a16fb
		result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
Packit 4a16fb
		if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
Packit 4a16fb
			snd_pcm_sframes_t res;
Packit 4a16fb
			
Packit 4a16fb
			res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
Packit 4a16fb
			if (res < 0) {
Packit 4a16fb
				err = res;
Packit 4a16fb
				goto error;
Packit 4a16fb
			}
Packit 4a16fb
			frames -= res;
Packit 4a16fb
		}
Packit 4a16fb
		if (result <= 0) {
Packit 4a16fb
			err = result;
Packit 4a16fb
			goto error;
Packit 4a16fb
		}
Packit 4a16fb
		snd_pcm_mmap_appl_forward(pcm, frames);
Packit 4a16fb
		if (frames == cont)
Packit 4a16fb
			appl_offset = 0;
Packit 4a16fb
		else
Packit 4a16fb
			appl_offset += result;
Packit 4a16fb
		size -= frames;
Packit 4a16fb
		slave_size -= frames;
Packit 4a16fb
		xfer += frames;
Packit 4a16fb
	}
Packit 4a16fb
	if (CHECK_SANITY(size)) {
Packit 4a16fb
		SNDMSG("short commit: %ld", size);
Packit 4a16fb
		return -EPIPE;
Packit 4a16fb
	}
Packit 4a16fb
	return xfer;
Packit 4a16fb
Packit 4a16fb
 error:
Packit 4a16fb
	return xfer > 0 ? xfer : err;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plugin_t *plugin = pcm->private_data;
Packit 4a16fb
	snd_pcm_t *slave = plugin->gen.slave;
Packit 4a16fb
	snd_pcm_sframes_t slave_size;
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	slave_size = snd_pcm_avail_update(slave);
Packit 4a16fb
	if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
Packit 4a16fb
	    pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
Packit 4a16fb
	    pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED)
Packit 4a16fb
		goto _capture;
Packit 4a16fb
        *pcm->hw.ptr = *slave->hw.ptr;
Packit 4a16fb
        return slave_size;
Packit 4a16fb
 _capture:
Packit 4a16fb
 	{
Packit 4a16fb
		const snd_pcm_channel_area_t *areas;
Packit 4a16fb
		snd_pcm_uframes_t xfer, hw_offset, size;
Packit 4a16fb
		
Packit 4a16fb
		xfer = snd_pcm_mmap_capture_avail(pcm);
Packit 4a16fb
		size = pcm->buffer_size - xfer;
Packit 4a16fb
		areas = snd_pcm_mmap_areas(pcm);
Packit 4a16fb
		hw_offset = snd_pcm_mmap_hw_offset(pcm);
Packit 4a16fb
		while (size > 0 && slave_size > 0) {
Packit 4a16fb
			snd_pcm_uframes_t frames = size;
Packit 4a16fb
			snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
Packit 4a16fb
			const snd_pcm_channel_area_t *slave_areas;
Packit 4a16fb
			snd_pcm_uframes_t slave_offset;
Packit 4a16fb
			snd_pcm_uframes_t slave_frames = ULONG_MAX;
Packit 4a16fb
			snd_pcm_sframes_t result;
Packit 4a16fb
			/* As mentioned in the ALSA API (see pcm/pcm.c:942):
Packit 4a16fb
			 * The function #snd_pcm_avail_update()
Packit 4a16fb
			 * have to be called before any mmap begin+commit operation.
Packit 4a16fb
			 * Otherwise the snd_pcm_areas_copy will not called a second time.
Packit 4a16fb
			 * But this is needed, if the ring buffer wrap is reached and
Packit 4a16fb
			 * there is more data available.
Packit 4a16fb
			 */
Packit 4a16fb
			slave_size = snd_pcm_avail_update(slave);
Packit 4a16fb
			result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
Packit 4a16fb
			if (result < 0) {
Packit 4a16fb
				err = result;
Packit 4a16fb
				goto error;
Packit 4a16fb
			}
Packit 4a16fb
			if (frames > cont)
Packit 4a16fb
				frames = cont;
Packit 4a16fb
			frames = (plugin->read)(pcm, areas, hw_offset, frames,
Packit 4a16fb
					      slave_areas, slave_offset, &slave_frames);
Packit 4a16fb
			result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
Packit 4a16fb
			if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
Packit 4a16fb
				snd_pcm_sframes_t res;
Packit 4a16fb
				
Packit 4a16fb
				res = plugin->undo_read(slave, areas, hw_offset, frames, slave_frames - result);
Packit 4a16fb
				if (res < 0) {
Packit 4a16fb
					err = res;
Packit 4a16fb
					goto error;
Packit 4a16fb
				}
Packit 4a16fb
				frames -= res;
Packit 4a16fb
			}
Packit 4a16fb
			if (result <= 0) {
Packit 4a16fb
				err = result;
Packit 4a16fb
				goto error;
Packit 4a16fb
			}
Packit 4a16fb
			snd_pcm_mmap_hw_forward(pcm, frames);
Packit 4a16fb
			if (frames == cont)
Packit 4a16fb
				hw_offset = 0;
Packit 4a16fb
			else
Packit 4a16fb
				hw_offset += frames;
Packit 4a16fb
			size -= frames;
Packit 4a16fb
			slave_size -= slave_frames;
Packit 4a16fb
			xfer += frames;
Packit 4a16fb
		}
Packit 4a16fb
		return (snd_pcm_sframes_t)xfer;
Packit 4a16fb
Packit 4a16fb
	error:
Packit 4a16fb
		return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_plugin_t *plugin = pcm->private_data;
Packit 4a16fb
	snd_pcm_sframes_t err;
Packit 4a16fb
Packit 4a16fb
	/* sync with the latest hw and appl ptrs */
Packit 4a16fb
	snd_pcm_plugin_avail_update(pcm);
Packit 4a16fb
Packit 4a16fb
	err = snd_pcm_status(plugin->gen.slave, status);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	status->appl_ptr = *pcm->appl.ptr;
Packit 4a16fb
	status->hw_ptr = *pcm->hw.ptr;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
int snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t *pcm,
Packit 4a16fb
					  snd_pcm_uframes_t avail)
Packit 4a16fb
{
Packit 4a16fb
	if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
Packit 4a16fb
	    pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
Packit 4a16fb
	    pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
Packit 4a16fb
		/* mmap access on capture device already consumes data from
Packit 4a16fb
		 * slave in avail_update operation. Entering snd_pcm_wait after
Packit 4a16fb
		 * having already consumed some fragments leads to waiting for
Packit 4a16fb
		 * too long time, as slave will unnecessarily wait for avail_min
Packit 4a16fb
		 * condition reached again. To avoid unnecessary wait times we
Packit 4a16fb
		 * adapt the avail_min threshold on slave dynamically. Just
Packit 4a16fb
		 * modifying slave->avail_min as a shortcut and lightweight
Packit 4a16fb
		 * solution does not work for all slave plugin types and in
Packit 4a16fb
		 * addition it will not propagate the change through all
Packit 4a16fb
		 * downstream plugins, so we have to use the sw_params API.
Packit 4a16fb
		 * note: reading fragmental parts from slave will only happen
Packit 4a16fb
		 * in case
Packit 4a16fb
		 * a) the slave can provide contineous hw_ptr between periods
Packit 4a16fb
		 * b) avail_min does not match one slave_period
Packit 4a16fb
		 */
Packit 4a16fb
		snd_pcm_plugin_t *plugin = pcm->private_data;
Packit 4a16fb
		snd_pcm_t *slave = plugin->gen.slave;
Packit 4a16fb
		snd_pcm_uframes_t needed_slave_avail_min;
Packit 4a16fb
		snd_pcm_sframes_t available;
Packit 4a16fb
Packit 4a16fb
		/* update, as it might have changed. This will also call
Packit 4a16fb
		 * avail_update on slave and also can return error
Packit 4a16fb
		 */
Packit 4a16fb
		available = snd_pcm_avail_update(pcm);
Packit 4a16fb
		if (available < 0)
Packit 4a16fb
			return 0;
Packit 4a16fb
Packit 4a16fb
		if ((snd_pcm_uframes_t)available >= pcm->avail_min)
Packit 4a16fb
			/* don't wait at all. As we can't configure avail_min
Packit 4a16fb
			 * of slave to 0 return here
Packit 4a16fb
			 */
Packit 4a16fb
			return 0;
Packit 4a16fb
Packit 4a16fb
		needed_slave_avail_min = pcm->avail_min - available;
Packit 4a16fb
		if (slave->avail_min != needed_slave_avail_min) {
Packit 4a16fb
			snd_pcm_sw_params_t *swparams;
Packit 4a16fb
			snd_pcm_sw_params_alloca(&swparams);
Packit 4a16fb
			/* pray that changing sw_params while running is
Packit 4a16fb
			 * properly implemented in all downstream plugins...
Packit 4a16fb
			 * it's legal but not commonly used.
Packit 4a16fb
			 */
Packit 4a16fb
			snd_pcm_sw_params_current(slave, swparams);
Packit 4a16fb
			/* snd_pcm_sw_params_set_avail_min() restricts setting
Packit 4a16fb
			 * to >= period size. This conflicts at least with our
Packit 4a16fb
			 * dshare patch which allows combining multiple periods
Packit 4a16fb
			 * or with slaves which return hw postions between
Packit 4a16fb
			 * periods -> set directly in sw_param structure
Packit 4a16fb
			 */
Packit 4a16fb
			swparams->avail_min = needed_slave_avail_min;
Packit 4a16fb
			snd_pcm_sw_params(slave, swparams);
Packit 4a16fb
		}
Packit 4a16fb
		avail = available;
Packit 4a16fb
	}
Packit 4a16fb
	return snd_pcm_generic_may_wait_for_avail_min(pcm, avail);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
const snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops = {
Packit 4a16fb
	.status = snd_pcm_plugin_status,
Packit 4a16fb
	.state = snd_pcm_generic_state,
Packit 4a16fb
	.hwsync = snd_pcm_generic_hwsync,
Packit 4a16fb
	.delay = snd_pcm_plugin_delay,
Packit 4a16fb
	.prepare = snd_pcm_plugin_prepare,
Packit 4a16fb
	.reset = snd_pcm_plugin_reset,
Packit 4a16fb
	.start = snd_pcm_generic_start,
Packit 4a16fb
	.drop = snd_pcm_generic_drop,
Packit 4a16fb
	.drain = snd_pcm_generic_drain,
Packit 4a16fb
	.pause = snd_pcm_generic_pause,
Packit 4a16fb
	.rewindable = snd_pcm_plugin_rewindable,
Packit 4a16fb
	.rewind = snd_pcm_plugin_rewind,
Packit 4a16fb
	.forwardable = snd_pcm_plugin_forwardable,
Packit 4a16fb
	.forward = snd_pcm_plugin_forward,
Packit 4a16fb
	.resume = snd_pcm_generic_resume,
Packit 4a16fb
	.link = snd_pcm_generic_link,
Packit 4a16fb
	.link_slaves = snd_pcm_generic_link_slaves,
Packit 4a16fb
	.unlink = snd_pcm_generic_unlink,
Packit 4a16fb
	.writei = snd_pcm_plugin_writei,
Packit 4a16fb
	.writen = snd_pcm_plugin_writen,
Packit 4a16fb
	.readi = snd_pcm_plugin_readi,
Packit 4a16fb
	.readn = snd_pcm_plugin_readn,
Packit 4a16fb
	.avail_update = snd_pcm_plugin_avail_update,
Packit 4a16fb
	.mmap_commit = snd_pcm_plugin_mmap_commit,
Packit 4a16fb
	.htimestamp = snd_pcm_generic_htimestamp,
Packit 4a16fb
	.poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
Packit 4a16fb
	.poll_descriptors = snd_pcm_generic_poll_descriptors,
Packit 4a16fb
	.poll_revents = snd_pcm_generic_poll_revents,
Packit 4a16fb
	.may_wait_for_avail_min = snd_pcm_plugin_may_wait_for_avail_min,
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
#endif