Blame src/pcm/pcm_plugin.c

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