Blame src/pcm/pcm_plug.c

Packit Service db8eaa
/*
Packit Service db8eaa
 * \file pcm/pcm_plug.c
Packit Service db8eaa
 * \ingroup PCM_Plugins
Packit Service db8eaa
 * \brief PCM Route & Volume Plugin Interface
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 - Plug
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
#include "pcm_local.h"
Packit Service db8eaa
#include "pcm_plugin.h"
Packit Service db8eaa
Packit Service db8eaa
#ifndef PIC
Packit Service db8eaa
/* entry for static linking */
Packit Service db8eaa
const char *_snd_module_pcm_plug = "";
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
#ifndef DOC_HIDDEN
Packit Service db8eaa
Packit Service db8eaa
enum snd_pcm_plug_route_policy {
Packit Service db8eaa
	PLUG_ROUTE_POLICY_NONE,
Packit Service db8eaa
	PLUG_ROUTE_POLICY_DEFAULT,
Packit Service db8eaa
	PLUG_ROUTE_POLICY_COPY,
Packit Service db8eaa
	PLUG_ROUTE_POLICY_AVERAGE,
Packit Service db8eaa
	PLUG_ROUTE_POLICY_DUP,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
typedef struct {
Packit Service db8eaa
	snd_pcm_generic_t gen;
Packit Service db8eaa
	snd_pcm_t *req_slave;
Packit Service db8eaa
	snd_pcm_format_t sformat;
Packit Service db8eaa
	int schannels;
Packit Service db8eaa
	int srate;
Packit Service db8eaa
	snd_config_t *rate_converter;
Packit Service db8eaa
	enum snd_pcm_plug_route_policy route_policy;
Packit Service db8eaa
	snd_pcm_route_ttable_entry_t *ttable;
Packit Service db8eaa
	int ttable_ok;
Packit Service db8eaa
	unsigned int tt_ssize, tt_cused, tt_sused;
Packit Service db8eaa
} snd_pcm_plug_t;
Packit Service db8eaa
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_plug_close(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	int err, result = 0;
Packit Service db8eaa
	free(plug->ttable);
Packit Service db8eaa
	if (plug->rate_converter) {
Packit Service db8eaa
		snd_config_delete(plug->rate_converter);
Packit Service db8eaa
		plug->rate_converter = NULL;
Packit Service db8eaa
	}
Packit Service db8eaa
	assert(plug->gen.slave == plug->req_slave);
Packit Service db8eaa
	if (plug->gen.close_slave) {
Packit Service db8eaa
		snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
Packit Service db8eaa
		snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
Packit Service db8eaa
		err = snd_pcm_close(plug->req_slave);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			result = err;
Packit Service db8eaa
	}
Packit Service db8eaa
	free(plug);
Packit Service db8eaa
	return result;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave = plug->req_slave;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	
Packit Service db8eaa
	if ((err = snd_pcm_info(slave, info)) < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static const snd_pcm_format_t linear_preferred_formats[] = {
Packit Service db8eaa
#ifdef SND_LITTLE_ENDIAN
Packit Service db8eaa
	SND_PCM_FORMAT_S16_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U16_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_S16_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U16_BE,
Packit Service db8eaa
#else
Packit Service db8eaa
	SND_PCM_FORMAT_S16_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U16_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_S16_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U16_LE,
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef SND_LITTLE_ENDIAN
Packit Service db8eaa
	SND_PCM_FORMAT_S32_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U32_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_S32_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U32_BE,
Packit Service db8eaa
#else
Packit Service db8eaa
	SND_PCM_FORMAT_S32_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U32_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_S32_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U32_LE,
Packit Service db8eaa
#endif
Packit Service db8eaa
	SND_PCM_FORMAT_S8,
Packit Service db8eaa
	SND_PCM_FORMAT_U8,
Packit Service db8eaa
#ifdef SND_LITTLE_ENDIAN
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT64_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT64_BE,
Packit Service db8eaa
#else
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT64_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT64_LE,
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef SND_LITTLE_ENDIAN
Packit Service db8eaa
	SND_PCM_FORMAT_S24_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U24_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_S24_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U24_BE,
Packit Service db8eaa
#else
Packit Service db8eaa
	SND_PCM_FORMAT_S24_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U24_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_S24_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U24_LE,
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef SND_LITTLE_ENDIAN
Packit Service db8eaa
	SND_PCM_FORMAT_S20_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U20_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_S20_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U20_BE,
Packit Service db8eaa
#else
Packit Service db8eaa
	SND_PCM_FORMAT_S20_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U20_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_S20_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U20_LE,
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef SND_LITTLE_ENDIAN
Packit Service db8eaa
	SND_PCM_FORMAT_S24_3LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U24_3LE,
Packit Service db8eaa
	SND_PCM_FORMAT_S24_3BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U24_3BE,
Packit Service db8eaa
#else
Packit Service db8eaa
	SND_PCM_FORMAT_S24_3BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U24_3BE,
Packit Service db8eaa
	SND_PCM_FORMAT_S24_3LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U24_3LE,
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef SND_LITTLE_ENDIAN
Packit Service db8eaa
	SND_PCM_FORMAT_S20_3LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U20_3LE,
Packit Service db8eaa
	SND_PCM_FORMAT_S20_3BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U20_3BE,
Packit Service db8eaa
#else
Packit Service db8eaa
	SND_PCM_FORMAT_S20_3BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U20_3BE,
Packit Service db8eaa
	SND_PCM_FORMAT_S20_3LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U20_3LE,
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef SND_LITTLE_ENDIAN
Packit Service db8eaa
	SND_PCM_FORMAT_S18_3LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U18_3LE,
Packit Service db8eaa
	SND_PCM_FORMAT_S18_3BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U18_3BE,
Packit Service db8eaa
#else
Packit Service db8eaa
	SND_PCM_FORMAT_S18_3BE,
Packit Service db8eaa
	SND_PCM_FORMAT_U18_3BE,
Packit Service db8eaa
	SND_PCM_FORMAT_S18_3LE,
Packit Service db8eaa
	SND_PCM_FORMAT_U18_3LE,
Packit Service db8eaa
#endif
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
#if defined(BUILD_PCM_PLUGIN_MULAW) || \
Packit Service db8eaa
	defined(BUILD_PCM_PLUGIN_ALAW) || \
Packit Service db8eaa
	defined(BUILD_PCM_PLUGIN_ADPCM)
Packit Service db8eaa
#define BUILD_PCM_NONLINEAR
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
#ifdef BUILD_PCM_NONLINEAR
Packit Service db8eaa
static const snd_pcm_format_t nonlinear_preferred_formats[] = {
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_MULAW
Packit Service db8eaa
	SND_PCM_FORMAT_MU_LAW,
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_ALAW
Packit Service db8eaa
	SND_PCM_FORMAT_A_LAW,
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_ADPCM
Packit Service db8eaa
	SND_PCM_FORMAT_IMA_ADPCM,
Packit Service db8eaa
#endif
Packit Service db8eaa
};
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_LFLOAT
Packit Service db8eaa
static const snd_pcm_format_t float_preferred_formats[] = {
Packit Service db8eaa
#ifdef SND_LITTLE_ENDIAN
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT64_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT64_BE,
Packit Service db8eaa
#else
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT64_BE,
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT_LE,
Packit Service db8eaa
	SND_PCM_FORMAT_FLOAT64_LE,
Packit Service db8eaa
#endif
Packit Service db8eaa
};
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
static const char linear_format_widths[32] = {
Packit Service db8eaa
	0, 0, 0, 0, 0, 0, 0, 1,
Packit Service db8eaa
	0, 0, 0, 0, 0, 0, 0, 1,
Packit Service db8eaa
	0, 1, 0, 1, 0, 0, 0, 1,
Packit Service db8eaa
	0, 0, 0, 0, 0, 0, 0, 1,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
static int check_linear_format(const snd_pcm_format_mask_t *format_mask, int wid, int sgn, int ed)
Packit Service db8eaa
{
Packit Service db8eaa
	int e, s;
Packit Service db8eaa
	if (! linear_format_widths[wid - 1])
Packit Service db8eaa
		return SND_PCM_FORMAT_UNKNOWN;
Packit Service db8eaa
	for (e = 0; e < 2; e++) {
Packit Service db8eaa
		for (s = 0; s < 2; s++) {
Packit Service db8eaa
			int pw = ((wid + 7) / 8) * 8;
Packit Service db8eaa
			for (; pw <= 32; pw += 8) {
Packit Service db8eaa
				snd_pcm_format_t f;
Packit Service db8eaa
				f = snd_pcm_build_linear_format(wid, pw, sgn, ed);
Packit Service db8eaa
				if (f != SND_PCM_FORMAT_UNKNOWN &&
Packit Service db8eaa
				    snd_pcm_format_mask_test(format_mask, f))
Packit Service db8eaa
					return f;
Packit Service db8eaa
			}
Packit Service db8eaa
			sgn = !sgn;
Packit Service db8eaa
		}
Packit Service db8eaa
		ed = !ed;
Packit Service db8eaa
	}
Packit Service db8eaa
	return SND_PCM_FORMAT_UNKNOWN;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, const snd_pcm_format_mask_t *format_mask)
Packit Service db8eaa
{
Packit Service db8eaa
	int w, w1, u, e;
Packit Service db8eaa
	snd_pcm_format_t f;
Packit Service db8eaa
	snd_pcm_format_mask_t lin = { SND_PCM_FMTBIT_LINEAR };
Packit Service db8eaa
	snd_pcm_format_mask_t fl = {
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_LFLOAT
Packit Service db8eaa
		SND_PCM_FMTBIT_FLOAT
Packit Service db8eaa
#else
Packit Service db8eaa
		{ 0 }
Packit Service db8eaa
#endif
Packit Service db8eaa
	};
Packit Service db8eaa
	if (snd_pcm_format_mask_test(format_mask, format))
Packit Service db8eaa
		return format;
Packit Service db8eaa
	if (!snd_pcm_format_mask_test(&lin, format) &&
Packit Service db8eaa
	    !snd_pcm_format_mask_test(&fl, format)) {
Packit Service db8eaa
		unsigned int i;
Packit Service db8eaa
		switch (format) {
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_MULAW
Packit Service db8eaa
		case SND_PCM_FORMAT_MU_LAW:
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_ALAW
Packit Service db8eaa
		case SND_PCM_FORMAT_A_LAW:
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_ADPCM
Packit Service db8eaa
		case SND_PCM_FORMAT_IMA_ADPCM:
Packit Service db8eaa
#endif
Packit Service db8eaa
			for (i = 0; i < sizeof(linear_preferred_formats) / sizeof(linear_preferred_formats[0]); ++i) {
Packit Service db8eaa
				snd_pcm_format_t f = linear_preferred_formats[i];
Packit Service db8eaa
				if (snd_pcm_format_mask_test(format_mask, f))
Packit Service db8eaa
					return f;
Packit Service db8eaa
			}
Packit Service db8eaa
			/* Fall through */
Packit Service db8eaa
		default:
Packit Service db8eaa
			return SND_PCM_FORMAT_UNKNOWN;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
	}
Packit Service db8eaa
	snd_mask_intersect(&lin, format_mask);
Packit Service db8eaa
	snd_mask_intersect(&fl, format_mask);
Packit Service db8eaa
	if (snd_mask_empty(&lin) && snd_mask_empty(&fl)) {
Packit Service db8eaa
#ifdef BUILD_PCM_NONLINEAR
Packit Service db8eaa
		unsigned int i;
Packit Service db8eaa
		for (i = 0; i < sizeof(nonlinear_preferred_formats) / sizeof(nonlinear_preferred_formats[0]); ++i) {
Packit Service db8eaa
			snd_pcm_format_t f = nonlinear_preferred_formats[i];
Packit Service db8eaa
			if (snd_pcm_format_mask_test(format_mask, f))
Packit Service db8eaa
				return f;
Packit Service db8eaa
		}
Packit Service db8eaa
#endif
Packit Service db8eaa
		return SND_PCM_FORMAT_UNKNOWN;
Packit Service db8eaa
	}
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_LFLOAT
Packit Service db8eaa
	if (snd_pcm_format_float(format)) {
Packit Service db8eaa
		if (snd_pcm_format_mask_test(&fl, format)) {
Packit Service db8eaa
			unsigned int i;
Packit Service db8eaa
			for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
Packit Service db8eaa
				snd_pcm_format_t f = float_preferred_formats[i];
Packit Service db8eaa
				if (snd_pcm_format_mask_test(format_mask, f))
Packit Service db8eaa
					return f;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
		w = 32;
Packit Service db8eaa
		u = 0;
Packit Service db8eaa
		e = snd_pcm_format_big_endian(format);
Packit Service db8eaa
	} else
Packit Service db8eaa
#endif
Packit Service db8eaa
	if (snd_mask_empty(&lin)) {
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_LFLOAT
Packit Service db8eaa
		unsigned int i;
Packit Service db8eaa
		for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
Packit Service db8eaa
			snd_pcm_format_t f = float_preferred_formats[i];
Packit Service db8eaa
			if (snd_pcm_format_mask_test(format_mask, f))
Packit Service db8eaa
				return f;
Packit Service db8eaa
		}
Packit Service db8eaa
#endif
Packit Service db8eaa
		return SND_PCM_FORMAT_UNKNOWN;
Packit Service db8eaa
	} else {
Packit Service db8eaa
		w = snd_pcm_format_width(format);
Packit Service db8eaa
		u = snd_pcm_format_unsigned(format);
Packit Service db8eaa
		e = snd_pcm_format_big_endian(format);
Packit Service db8eaa
	}
Packit Service db8eaa
	for (w1 = w; w1 <= 32; w1++) {
Packit Service db8eaa
		f = check_linear_format(format_mask, w1, u, e);
Packit Service db8eaa
		if (f != SND_PCM_FORMAT_UNKNOWN)
Packit Service db8eaa
			return f;
Packit Service db8eaa
	}
Packit Service db8eaa
	for (w1 = w - 1; w1 > 0; w1--) {
Packit Service db8eaa
		f = check_linear_format(format_mask, w1, u, e);
Packit Service db8eaa
		if (f != SND_PCM_FORMAT_UNKNOWN)
Packit Service db8eaa
			return f;
Packit Service db8eaa
	}
Packit Service db8eaa
	return SND_PCM_FORMAT_UNKNOWN;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void snd_pcm_plug_clear(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave = plug->req_slave;
Packit Service db8eaa
	/* Clear old plugins */
Packit Service db8eaa
	if (plug->gen.slave != slave) {
Packit Service db8eaa
		snd_pcm_unlink_hw_ptr(pcm, plug->gen.slave);
Packit Service db8eaa
		snd_pcm_unlink_appl_ptr(pcm, plug->gen.slave);
Packit Service db8eaa
		snd_pcm_close(plug->gen.slave);
Packit Service db8eaa
		plug->gen.slave = slave;
Packit Service db8eaa
		pcm->fast_ops = slave->fast_ops;
Packit Service db8eaa
		pcm->fast_op_arg = slave->fast_op_arg;
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
#ifndef DOC_HIDDEN
Packit Service db8eaa
typedef struct {
Packit Service db8eaa
	snd_pcm_access_t access;
Packit Service db8eaa
	snd_pcm_format_t format;
Packit Service db8eaa
	unsigned int channels;
Packit Service db8eaa
	unsigned int rate;
Packit Service db8eaa
} snd_pcm_plug_params_t;
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_RATE
Packit Service db8eaa
static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	if (clt->rate == slv->rate)
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	assert(snd_pcm_format_linear(slv->format));
Packit Service db8eaa
	err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->rate_converter,
Packit Service db8eaa
				plug->gen.slave, plug->gen.slave != plug->req_slave);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	slv->access = clt->access;
Packit Service db8eaa
	slv->rate = clt->rate;
Packit Service db8eaa
	if (snd_pcm_format_linear(clt->format))
Packit Service db8eaa
		slv->format = clt->format;
Packit Service db8eaa
	return 1;
Packit Service db8eaa
}
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_ROUTE
Packit Service db8eaa
static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	unsigned int tt_ssize, tt_cused, tt_sused;
Packit Service db8eaa
	snd_pcm_route_ttable_entry_t *ttable;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	if (clt->channels == slv->channels &&
Packit Service db8eaa
	    (!plug->ttable || plug->ttable_ok))
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	if (clt->rate != slv->rate &&
Packit Service db8eaa
	    clt->channels > slv->channels)
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	assert(snd_pcm_format_linear(slv->format));
Packit Service db8eaa
	tt_ssize = slv->channels;
Packit Service db8eaa
	tt_cused = clt->channels;
Packit Service db8eaa
	tt_sused = slv->channels;
Packit Service db8eaa
	ttable = alloca(tt_cused * tt_sused * sizeof(*ttable));
Packit Service db8eaa
	if (plug->ttable) {	/* expand or shrink table */
Packit Service db8eaa
		unsigned int c = 0, s = 0;
Packit Service db8eaa
		for (c = 0; c < tt_cused; c++) {
Packit Service db8eaa
			for (s = 0; s < tt_sused; s++) {
Packit Service db8eaa
				snd_pcm_route_ttable_entry_t v;
Packit Service db8eaa
				if (c >= plug->tt_cused)
Packit Service db8eaa
					v = 0;
Packit Service db8eaa
				else if (s >= plug->tt_sused)
Packit Service db8eaa
					v = 0;
Packit Service db8eaa
				else
Packit Service db8eaa
					v = plug->ttable[c * plug->tt_ssize + s];
Packit Service db8eaa
				ttable[c * tt_ssize + s] = v;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
		plug->ttable_ok = 1;
Packit Service db8eaa
	} else {
Packit Service db8eaa
		unsigned int k;
Packit Service db8eaa
		unsigned int c = 0, s = 0;
Packit Service db8eaa
		enum snd_pcm_plug_route_policy rpolicy = plug->route_policy;
Packit Service db8eaa
		int n;
Packit Service db8eaa
		for (k = 0; k < tt_cused * tt_sused; ++k)
Packit Service db8eaa
			ttable[k] = 0;
Packit Service db8eaa
		if (rpolicy == PLUG_ROUTE_POLICY_DEFAULT) {
Packit Service db8eaa
			rpolicy = PLUG_ROUTE_POLICY_COPY;
Packit Service db8eaa
			/* it's hack for mono conversion */
Packit Service db8eaa
			if (clt->channels == 1 || slv->channels == 1)
Packit Service db8eaa
				rpolicy = PLUG_ROUTE_POLICY_AVERAGE;
Packit Service db8eaa
		}
Packit Service db8eaa
		switch (rpolicy) {
Packit Service db8eaa
		case PLUG_ROUTE_POLICY_AVERAGE:
Packit Service db8eaa
		case PLUG_ROUTE_POLICY_DUP:
Packit Service db8eaa
			if (clt->channels > slv->channels) {
Packit Service db8eaa
				n = clt->channels;
Packit Service db8eaa
			} else {
Packit Service db8eaa
				n = slv->channels;
Packit Service db8eaa
			}
Packit Service db8eaa
			while (n-- > 0) {
Packit Service db8eaa
				snd_pcm_route_ttable_entry_t v = SND_PCM_PLUGIN_ROUTE_FULL;
Packit Service db8eaa
				if (rpolicy == PLUG_ROUTE_POLICY_AVERAGE) {
Packit Service db8eaa
					if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
Packit Service db8eaa
					    clt->channels > slv->channels) {
Packit Service db8eaa
						int srcs = clt->channels / slv->channels;
Packit Service db8eaa
						if (s < clt->channels % slv->channels)
Packit Service db8eaa
							srcs++;
Packit Service db8eaa
						v /= srcs;
Packit Service db8eaa
					} else if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
Packit Service db8eaa
						   slv->channels > clt->channels) {
Packit Service db8eaa
							int srcs = slv->channels / clt->channels;
Packit Service db8eaa
						if (s < slv->channels % clt->channels)
Packit Service db8eaa
							srcs++;
Packit Service db8eaa
						v /= srcs;
Packit Service db8eaa
					}
Packit Service db8eaa
				}
Packit Service db8eaa
				ttable[c * tt_ssize + s] = v;
Packit Service db8eaa
				if (++c == clt->channels)
Packit Service db8eaa
					c = 0;
Packit Service db8eaa
				if (++s == slv->channels)
Packit Service db8eaa
					s = 0;
Packit Service db8eaa
			}
Packit Service db8eaa
			break;
Packit Service db8eaa
		case PLUG_ROUTE_POLICY_COPY:
Packit Service db8eaa
			if (clt->channels < slv->channels) {
Packit Service db8eaa
				n = clt->channels;
Packit Service db8eaa
			} else {
Packit Service db8eaa
				n = slv->channels;
Packit Service db8eaa
			}
Packit Service db8eaa
			for (c = 0; (int)c < n; c++)
Packit Service db8eaa
				ttable[c * tt_ssize + c] = SND_PCM_PLUGIN_ROUTE_FULL;
Packit Service db8eaa
			break;
Packit Service db8eaa
		default:
Packit Service db8eaa
			SNDERR("Invalid route policy");
Packit Service db8eaa
			break;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	err = snd_pcm_route_open(new, NULL, slv->format, (int) slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->gen.slave, plug->gen.slave != plug->req_slave);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	slv->channels = clt->channels;
Packit Service db8eaa
	slv->access = clt->access;
Packit Service db8eaa
	if (snd_pcm_format_linear(clt->format))
Packit Service db8eaa
		slv->format = clt->format;
Packit Service db8eaa
	return 1;
Packit Service db8eaa
}
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	snd_pcm_format_t cfmt;
Packit Service db8eaa
	int (*f)(snd_pcm_t **_pcm, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave);
Packit Service db8eaa
Packit Service db8eaa
	/* No conversion is needed */
Packit Service db8eaa
	if (clt->format == slv->format &&
Packit Service db8eaa
	    clt->rate == slv->rate &&
Packit Service db8eaa
	    clt->channels == slv->channels &&
Packit Service db8eaa
	    (!plug->ttable || plug->ttable_ok))
Packit Service db8eaa
		return 0;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_pcm_format_linear(slv->format)) {
Packit Service db8eaa
		/* Conversion is done in another plugin */
Packit Service db8eaa
		if (clt->rate != slv->rate ||
Packit Service db8eaa
		    clt->channels != slv->channels ||
Packit Service db8eaa
		    (plug->ttable && !plug->ttable_ok))
Packit Service db8eaa
			return 0;
Packit Service db8eaa
		cfmt = clt->format;
Packit Service db8eaa
		switch (clt->format) {
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_MULAW
Packit Service db8eaa
		case SND_PCM_FORMAT_MU_LAW:
Packit Service db8eaa
			f = snd_pcm_mulaw_open;
Packit Service db8eaa
			break;
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_ALAW
Packit Service db8eaa
		case SND_PCM_FORMAT_A_LAW:
Packit Service db8eaa
			f = snd_pcm_alaw_open;
Packit Service db8eaa
			break;
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_ADPCM
Packit Service db8eaa
		case SND_PCM_FORMAT_IMA_ADPCM:
Packit Service db8eaa
			f = snd_pcm_adpcm_open;
Packit Service db8eaa
			break;
Packit Service db8eaa
#endif
Packit Service db8eaa
		default:
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_LFLOAT
Packit Service db8eaa
			if (snd_pcm_format_float(clt->format))
Packit Service db8eaa
				f = snd_pcm_lfloat_open;
Packit Service db8eaa
Packit Service db8eaa
			else
Packit Service db8eaa
#endif
Packit Service db8eaa
				f = snd_pcm_linear_open;
Packit Service db8eaa
			break;
Packit Service db8eaa
		}
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_LFLOAT
Packit Service db8eaa
	} else if (snd_pcm_format_float(slv->format)) {
Packit Service db8eaa
		if (snd_pcm_format_linear(clt->format)) {
Packit Service db8eaa
			cfmt = clt->format;
Packit Service db8eaa
			f = snd_pcm_lfloat_open;
Packit Service db8eaa
		} else if (clt->rate != slv->rate || clt->channels != slv->channels ||
Packit Service db8eaa
			   (plug->ttable && !plug->ttable_ok)) {
Packit Service db8eaa
			cfmt = SND_PCM_FORMAT_S16;
Packit Service db8eaa
			f = snd_pcm_lfloat_open;
Packit Service db8eaa
		} else
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef BUILD_PCM_NONLINEAR
Packit Service db8eaa
	} else {
Packit Service db8eaa
		switch (slv->format) {
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_MULAW
Packit Service db8eaa
		case SND_PCM_FORMAT_MU_LAW:
Packit Service db8eaa
			f = snd_pcm_mulaw_open;
Packit Service db8eaa
			break;
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_ALAW
Packit Service db8eaa
		case SND_PCM_FORMAT_A_LAW:
Packit Service db8eaa
			f = snd_pcm_alaw_open;
Packit Service db8eaa
			break;
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_ADPCM
Packit Service db8eaa
		case SND_PCM_FORMAT_IMA_ADPCM:
Packit Service db8eaa
			f = snd_pcm_adpcm_open;
Packit Service db8eaa
			break;
Packit Service db8eaa
#endif
Packit Service db8eaa
		default:
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
		}
Packit Service db8eaa
		if (snd_pcm_format_linear(clt->format))
Packit Service db8eaa
			cfmt = clt->format;
Packit Service db8eaa
		else
Packit Service db8eaa
			cfmt = SND_PCM_FORMAT_S16;
Packit Service db8eaa
#endif /* NONLINEAR */
Packit Service db8eaa
	}
Packit Service db8eaa
	err = f(new, NULL, slv->format, plug->gen.slave, plug->gen.slave != plug->req_slave);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	slv->format = cfmt;
Packit Service db8eaa
	slv->access = clt->access;
Packit Service db8eaa
	return 1;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_plug_change_access(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	if (clt->access == slv->access)
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	err = snd_pcm_copy_open(new, NULL, plug->gen.slave, plug->gen.slave != plug->req_slave);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	slv->access = clt->access;
Packit Service db8eaa
	return 1;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
Packit Service db8eaa
static int snd_pcm_plug_change_mmap(snd_pcm_t *pcm, snd_pcm_t **new,
Packit Service db8eaa
				    snd_pcm_plug_params_t *clt,
Packit Service db8eaa
				    snd_pcm_plug_params_t *slv)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (clt->access == slv->access)
Packit Service db8eaa
		return 0;
Packit Service db8eaa
Packit Service db8eaa
	switch (slv->access) {
Packit Service db8eaa
	case SND_PCM_ACCESS_MMAP_INTERLEAVED:
Packit Service db8eaa
	case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
Packit Service db8eaa
	case SND_PCM_ACCESS_MMAP_COMPLEX:
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	default:
Packit Service db8eaa
		break;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	err = __snd_pcm_mmap_emul_open(new, NULL, plug->gen.slave,
Packit Service db8eaa
				       plug->gen.slave != plug->req_slave);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	switch (slv->access) {
Packit Service db8eaa
	case SND_PCM_ACCESS_RW_INTERLEAVED:
Packit Service db8eaa
		slv->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_ACCESS_RW_NONINTERLEAVED:
Packit Service db8eaa
		slv->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
Packit Service db8eaa
		break;
Packit Service db8eaa
	default:
Packit Service db8eaa
		break;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 1;
Packit Service db8eaa
}
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm,
Packit Service db8eaa
				       snd_pcm_plug_params_t *client,
Packit Service db8eaa
				       snd_pcm_plug_params_t *slave)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	static int (*const funcs[])(snd_pcm_t *_pcm, snd_pcm_t **new, snd_pcm_plug_params_t *s, snd_pcm_plug_params_t *d) = {
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
Packit Service db8eaa
		snd_pcm_plug_change_mmap,
Packit Service db8eaa
#endif
Packit Service db8eaa
		snd_pcm_plug_change_format,
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_ROUTE
Packit Service db8eaa
		snd_pcm_plug_change_channels,
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_RATE
Packit Service db8eaa
		snd_pcm_plug_change_rate,
Packit Service db8eaa
#endif
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_ROUTE
Packit Service db8eaa
		snd_pcm_plug_change_channels,
Packit Service db8eaa
#endif
Packit Service db8eaa
		snd_pcm_plug_change_format,
Packit Service db8eaa
		snd_pcm_plug_change_access
Packit Service db8eaa
	};
Packit Service db8eaa
	snd_pcm_plug_params_t p = *slave;
Packit Service db8eaa
	unsigned int k = 0;
Packit Service db8eaa
	plug->ttable_ok = 0;
Packit Service db8eaa
	while (client->format != p.format ||
Packit Service db8eaa
	       client->channels != p.channels ||
Packit Service db8eaa
	       client->rate != p.rate ||
Packit Service db8eaa
	       client->access != p.access ||
Packit Service db8eaa
	       (plug->ttable && !plug->ttable_ok)) {
Packit Service db8eaa
		snd_pcm_t *new;
Packit Service db8eaa
		int err;
Packit Service db8eaa
		if (k >= sizeof(funcs)/sizeof(*funcs)) {
Packit Service db8eaa
			snd_pcm_plug_clear(pcm);
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
		}
Packit Service db8eaa
		err = funcs[k](pcm, &new, client, &p);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			snd_pcm_plug_clear(pcm);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
		if (err) {
Packit Service db8eaa
			plug->gen.slave = new;
Packit Service db8eaa
		}
Packit Service db8eaa
		k++;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_plug_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
Packit Service db8eaa
{
Packit Service db8eaa
	unsigned int rate_min, channels_max;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	/* HACK: to avoid overflow in PARTBIT_RATE code */
Packit Service db8eaa
	err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, NULL);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	if (rate_min < 4000) {
Packit Service db8eaa
		_snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, 4000, 0);
Packit Service db8eaa
		if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_RATE))
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	/* HACK: to avoid overflow in PERIOD_SIZE code */
Packit Service db8eaa
	err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_CHANNELS, &channels_max, NULL);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	if (channels_max > 10000) {
Packit Service db8eaa
		_snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_CHANNELS, 10000, 0);
Packit Service db8eaa
		if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_CHANNELS))
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	
Packit Service db8eaa
	_snd_pcm_hw_params_any(sparams);
Packit Service db8eaa
	if (plug->sformat >= 0) {
Packit Service db8eaa
		_snd_pcm_hw_params_set_format(sparams, plug->sformat);
Packit Service db8eaa
		_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
Packit Service db8eaa
	}
Packit Service db8eaa
	if (plug->schannels > 0)
Packit Service db8eaa
		_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
Packit Service db8eaa
				      plug->schannels, 0);
Packit Service db8eaa
	if (plug->srate > 0)
Packit Service db8eaa
		_snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
Packit Service db8eaa
					      plug->srate, 0, plug->srate + 1, -1);
Packit Service db8eaa
	/* reduce the available configurations */
Packit Service db8eaa
	err = snd_pcm_hw_refine(plug->req_slave, sparams);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int check_access_change(snd_pcm_hw_params_t *cparams,
Packit Service db8eaa
			       snd_pcm_hw_params_t *sparams)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_access_mask_t *smask;
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
Packit Service db8eaa
	const snd_pcm_access_mask_t *cmask;
Packit Service db8eaa
	snd_pcm_access_mask_t mask;
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
	smask = (snd_pcm_access_mask_t *)
Packit Service db8eaa
		snd_pcm_hw_param_get_mask(sparams,
Packit Service db8eaa
					  SND_PCM_HW_PARAM_ACCESS);
Packit Service db8eaa
	if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_INTERLEAVED) ||
Packit Service db8eaa
	    snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) ||
Packit Service db8eaa
	    snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_MMAP_COMPLEX))
Packit Service db8eaa
		return 0; /* OK, we have mmap support */
Packit Service db8eaa
#ifdef BUILD_PCM_PLUGIN_MMAP_EMUL
Packit Service db8eaa
	/* no mmap support - we need mmap emulation */
Packit Service db8eaa
Packit Service db8eaa
	if (!snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
Packit Service db8eaa
	    !snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) 
Packit Service db8eaa
		return -EINVAL; /* even no RW access?  no way! */
Packit Service db8eaa
Packit Service db8eaa
	cmask = (const snd_pcm_access_mask_t *)
Packit Service db8eaa
		snd_pcm_hw_param_get_mask(cparams,
Packit Service db8eaa
					  SND_PCM_HW_PARAM_ACCESS);
Packit Service db8eaa
	snd_mask_none(&mask);
Packit Service db8eaa
	if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_INTERLEAVED) ||
Packit Service db8eaa
	    snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
Packit Service db8eaa
		if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_INTERLEAVED))
Packit Service db8eaa
			snd_pcm_access_mask_set(&mask,
Packit Service db8eaa
						SND_PCM_ACCESS_RW_INTERLEAVED);
Packit Service db8eaa
	}
Packit Service db8eaa
	if (snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_RW_NONINTERLEAVED) ||
Packit Service db8eaa
	    snd_pcm_access_mask_test(cmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
Packit Service db8eaa
		if (snd_pcm_access_mask_test(smask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
Packit Service db8eaa
			snd_pcm_access_mask_set(&mask,
Packit Service db8eaa
						SND_PCM_ACCESS_RW_NONINTERLEAVED);
Packit Service db8eaa
	}
Packit Service db8eaa
	if (!snd_mask_empty(&mask))
Packit Service db8eaa
		*smask = mask; /* prefer the straight conversion */
Packit Service db8eaa
	return 0;
Packit Service db8eaa
#else
Packit Service db8eaa
	return -EINVAL;
Packit Service db8eaa
#endif
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
Packit Service db8eaa
					  snd_pcm_hw_params_t *sparams)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave = plug->req_slave;
Packit Service db8eaa
	unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit Service db8eaa
	const snd_pcm_format_mask_t *format_mask, *sformat_mask;
Packit Service db8eaa
	snd_pcm_format_mask_t sfmt_mask;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	snd_pcm_format_t format;
Packit Service db8eaa
	snd_interval_t t, buffer_size;
Packit Service db8eaa
	const snd_interval_t *srate, *crate;
Packit Service db8eaa
Packit Service db8eaa
	if (plug->srate == -2 ||
Packit Service db8eaa
	    (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
Packit Service db8eaa
	    (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
Packit Service db8eaa
		links |= SND_PCM_HW_PARBIT_RATE;
Packit Service db8eaa
	else {
Packit Service db8eaa
		err = snd_pcm_hw_param_refine_multiple(slave, sparams, SND_PCM_HW_PARAM_RATE, params);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	
Packit Service db8eaa
	if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
Packit Service db8eaa
		links |= SND_PCM_HW_PARBIT_CHANNELS;
Packit Service db8eaa
	else {
Packit Service db8eaa
		err = snd_pcm_hw_param_refine_near(slave, sparams, SND_PCM_HW_PARAM_CHANNELS, params);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
Packit Service db8eaa
		links |= SND_PCM_HW_PARBIT_FORMAT;
Packit Service db8eaa
	else {
Packit Service db8eaa
		format_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_FORMAT);
Packit Service db8eaa
		sformat_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_FORMAT);
Packit Service db8eaa
		snd_mask_none(&sfmt_mask);
Packit Service db8eaa
		for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
Packit Service db8eaa
			snd_pcm_format_t f;
Packit Service db8eaa
			if (!snd_pcm_format_mask_test(format_mask, format))
Packit Service db8eaa
				continue;
Packit Service db8eaa
			if (snd_pcm_format_mask_test(sformat_mask, format))
Packit Service db8eaa
				f = format;
Packit Service db8eaa
			else {
Packit Service db8eaa
				f = snd_pcm_plug_slave_format(format, sformat_mask);
Packit Service db8eaa
				if (f == SND_PCM_FORMAT_UNKNOWN)
Packit Service db8eaa
					continue;
Packit Service db8eaa
			}
Packit Service db8eaa
			snd_pcm_format_mask_set(&sfmt_mask, f);
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (snd_pcm_format_mask_empty(&sfmt_mask)) {
Packit Service db8eaa
			SNDERR("Unable to find an usable slave format for '%s'", pcm->name);
Packit Service db8eaa
			for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
Packit Service db8eaa
				if (!snd_pcm_format_mask_test(format_mask, format))
Packit Service db8eaa
					continue;
Packit Service db8eaa
				SNDERR("Format: %s", snd_pcm_format_name(format));
Packit Service db8eaa
			}
Packit Service db8eaa
			for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
Packit Service db8eaa
				if (!snd_pcm_format_mask_test(sformat_mask, format))
Packit Service db8eaa
					continue;
Packit Service db8eaa
				SNDERR("Slave format: %s", snd_pcm_format_name(format));
Packit Service db8eaa
			}
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
		}
Packit Service db8eaa
		err = snd_pcm_hw_param_set_mask(slave, sparams, SND_CHANGE,
Packit Service db8eaa
						SND_PCM_HW_PARAM_FORMAT, &sfmt_mask);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if (snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_ACCESS, sparams)) {
Packit Service db8eaa
		err = check_access_change(params, sparams);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			SNDERR("Unable to find an usable access for '%s'",
Packit Service db8eaa
			       pcm->name);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if ((links & SND_PCM_HW_PARBIT_RATE) ||
Packit Service db8eaa
	    snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
Packit Service db8eaa
		links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit Service db8eaa
			  SND_PCM_HW_PARBIT_BUFFER_SIZE);
Packit Service db8eaa
	else {
Packit Service db8eaa
		snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
Packit Service db8eaa
		snd_interval_unfloor(&buffer_size);
Packit Service db8eaa
		crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
Packit Service db8eaa
		srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
Packit Service db8eaa
		snd_interval_muldiv(&buffer_size, srate, crate, &t);
Packit Service db8eaa
		err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	err = _snd_pcm_hw_params_refine(sparams, links, params);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
	
Packit Service db8eaa
static int snd_pcm_plug_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
Packit Service db8eaa
					  snd_pcm_hw_params_t *params,
Packit Service db8eaa
					  snd_pcm_hw_params_t *sparams)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit Service db8eaa
	const snd_pcm_format_mask_t *format_mask, *sformat_mask;
Packit Service db8eaa
	snd_pcm_format_mask_t fmt_mask;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	snd_pcm_format_t format;
Packit Service db8eaa
	snd_interval_t t;
Packit Service db8eaa
	const snd_interval_t *sbuffer_size;
Packit Service db8eaa
	const snd_interval_t *srate, *crate;
Packit Service db8eaa
Packit Service db8eaa
	if (plug->schannels == -2 || (pcm->mode & SND_PCM_NO_AUTO_CHANNELS))
Packit Service db8eaa
		links |= SND_PCM_HW_PARBIT_CHANNELS;
Packit Service db8eaa
Packit Service db8eaa
	if (plug->sformat == -2 || (pcm->mode & SND_PCM_NO_AUTO_FORMAT))
Packit Service db8eaa
		links |= SND_PCM_HW_PARBIT_FORMAT;
Packit Service db8eaa
	else {
Packit Service db8eaa
		format_mask = snd_pcm_hw_param_get_mask(params,
Packit Service db8eaa
							SND_PCM_HW_PARAM_FORMAT);
Packit Service db8eaa
		sformat_mask = snd_pcm_hw_param_get_mask(sparams,
Packit Service db8eaa
							 SND_PCM_HW_PARAM_FORMAT);
Packit Service db8eaa
		snd_mask_none(&fmt_mask);
Packit Service db8eaa
		for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
Packit Service db8eaa
			snd_pcm_format_t f;
Packit Service db8eaa
			if (!snd_pcm_format_mask_test(format_mask, format))
Packit Service db8eaa
				continue;
Packit Service db8eaa
			if (snd_pcm_format_mask_test(sformat_mask, format))
Packit Service db8eaa
				f = format;
Packit Service db8eaa
			else {
Packit Service db8eaa
				f = snd_pcm_plug_slave_format(format, sformat_mask);
Packit Service db8eaa
				if (f == SND_PCM_FORMAT_UNKNOWN)
Packit Service db8eaa
					continue;
Packit Service db8eaa
			}
Packit Service db8eaa
			snd_pcm_format_mask_set(&fmt_mask, format);
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (snd_pcm_format_mask_empty(&fmt_mask)) {
Packit Service db8eaa
			SNDERR("Unable to find an usable client format");
Packit Service db8eaa
			for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
Packit Service db8eaa
				if (!snd_pcm_format_mask_test(format_mask, format))
Packit Service db8eaa
					continue;
Packit Service db8eaa
				SNDERR("Format: %s", snd_pcm_format_name(format));
Packit Service db8eaa
			}
Packit Service db8eaa
			for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
Packit Service db8eaa
				if (!snd_pcm_format_mask_test(sformat_mask, format))
Packit Service db8eaa
					continue;
Packit Service db8eaa
				SNDERR("Slave format: %s", snd_pcm_format_name(format));
Packit Service db8eaa
			}
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
		}
Packit Service db8eaa
		
Packit Service db8eaa
		err = _snd_pcm_hw_param_set_mask(params, 
Packit Service db8eaa
						 SND_PCM_HW_PARAM_FORMAT, &fmt_mask);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if (plug->srate == -2 ||
Packit Service db8eaa
	    (pcm->mode & SND_PCM_NO_AUTO_RESAMPLE) ||
Packit Service db8eaa
	    (params->flags & SND_PCM_HW_PARAMS_NORESAMPLE))
Packit Service db8eaa
		links |= SND_PCM_HW_PARBIT_RATE;
Packit Service db8eaa
	else {
Packit Service db8eaa
		unsigned int rate_min, srate_min;
Packit Service db8eaa
		int rate_mindir, srate_mindir;
Packit Service db8eaa
		
Packit Service db8eaa
		/* This is a temporary hack, waiting for a better solution */
Packit Service db8eaa
		err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, &rate_mindir);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		err = snd_pcm_hw_param_get_min(sparams, SND_PCM_HW_PARAM_RATE, &srate_min, &srate_mindir);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		if (rate_min == srate_min && srate_mindir > rate_mindir) {
Packit Service db8eaa
			err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, srate_min, srate_mindir);
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				return err;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	if ((links & SND_PCM_HW_PARBIT_RATE) ||
Packit Service db8eaa
	    snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
Packit Service db8eaa
		links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit Service db8eaa
			  SND_PCM_HW_PARBIT_BUFFER_SIZE);
Packit Service db8eaa
	else {
Packit Service db8eaa
		sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
Packit Service db8eaa
		crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
Packit Service db8eaa
		srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
Packit Service db8eaa
		snd_interval_muldiv(sbuffer_size, crate, srate, &t);
Packit Service db8eaa
		snd_interval_floor(&t);
Packit Service db8eaa
		if (snd_interval_empty(&t))
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
		err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	err = _snd_pcm_hw_params_refine(params, links, sparams);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	/* FIXME */
Packit Service db8eaa
	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_plug_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	return snd_pcm_hw_refine(plug->req_slave, params);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit Service db8eaa
{
Packit Service db8eaa
	return snd_pcm_hw_refine_slave(pcm, params,
Packit Service db8eaa
				       snd_pcm_plug_hw_refine_cprepare,
Packit Service db8eaa
				       snd_pcm_plug_hw_refine_cchange,
Packit Service db8eaa
				       snd_pcm_plug_hw_refine_sprepare,
Packit Service db8eaa
				       snd_pcm_plug_hw_refine_schange,
Packit Service db8eaa
				       snd_pcm_plug_hw_refine_slave);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave = plug->req_slave;
Packit Service db8eaa
	snd_pcm_plug_params_t clt_params, slv_params;
Packit Service db8eaa
	snd_pcm_hw_params_t sparams;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_pcm_plug_hw_refine_sprepare(pcm, &sparams);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = snd_pcm_plug_hw_refine_schange(pcm, params, &sparams);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = snd_pcm_hw_refine_soft(slave, &sparams);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	INTERNAL(snd_pcm_hw_params_get_access)(params, &clt_params.access);
Packit Service db8eaa
	INTERNAL(snd_pcm_hw_params_get_format)(params, &clt_params.format);
Packit Service db8eaa
	INTERNAL(snd_pcm_hw_params_get_channels)(params, &clt_params.channels);
Packit Service db8eaa
	INTERNAL(snd_pcm_hw_params_get_rate)(params, &clt_params.rate, 0);
Packit Service db8eaa
Packit Service db8eaa
	INTERNAL(snd_pcm_hw_params_get_format)(&sparams, &slv_params.format);
Packit Service db8eaa
	INTERNAL(snd_pcm_hw_params_get_channels)(&sparams, &slv_params.channels);
Packit Service db8eaa
	INTERNAL(snd_pcm_hw_params_get_rate)(&sparams, &slv_params.rate, 0);
Packit Service db8eaa
	snd_pcm_plug_clear(pcm);
Packit Service db8eaa
	if (!(clt_params.format == slv_params.format &&
Packit Service db8eaa
	      clt_params.channels == slv_params.channels &&
Packit Service db8eaa
	      clt_params.rate == slv_params.rate &&
Packit Service db8eaa
	      !plug->ttable &&
Packit Service db8eaa
	      snd_pcm_hw_params_test_access(slave, &sparams,
Packit Service db8eaa
					    clt_params.access) >= 0)) {
Packit Service db8eaa
		INTERNAL(snd_pcm_hw_params_set_access_first)(slave, &sparams, &slv_params.access);
Packit Service db8eaa
		err = snd_pcm_plug_insert_plugins(pcm, &clt_params, &slv_params);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	slave = plug->gen.slave;
Packit Service db8eaa
	err = _snd_pcm_hw_params_internal(slave, params);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		snd_pcm_plug_clear(pcm);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
Packit Service db8eaa
	snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
Packit Service db8eaa
Packit Service db8eaa
	pcm->fast_ops = slave->fast_ops;
Packit Service db8eaa
	pcm->fast_op_arg = slave->fast_op_arg;
Packit Service db8eaa
	snd_pcm_link_hw_ptr(pcm, slave);
Packit Service db8eaa
	snd_pcm_link_appl_ptr(pcm, slave);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_plug_hw_free(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave = plug->gen.slave;
Packit Service db8eaa
	int err = snd_pcm_hw_free(slave);
Packit Service db8eaa
	snd_pcm_plug_clear(pcm);
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_plug_t *plug = pcm->private_data;
Packit Service db8eaa
	snd_output_printf(out, "Plug PCM: ");
Packit Service db8eaa
	snd_pcm_dump(plug->gen.slave, out);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static const snd_pcm_ops_t snd_pcm_plug_ops = {
Packit Service db8eaa
	.close = snd_pcm_plug_close,
Packit Service db8eaa
	.info = snd_pcm_plug_info,
Packit Service db8eaa
	.hw_refine = snd_pcm_plug_hw_refine,
Packit Service db8eaa
	.hw_params = snd_pcm_plug_hw_params,
Packit Service db8eaa
	.hw_free = snd_pcm_plug_hw_free,
Packit Service db8eaa
	.sw_params = snd_pcm_generic_sw_params,
Packit Service db8eaa
	.channel_info = snd_pcm_generic_channel_info,
Packit Service db8eaa
	.dump = snd_pcm_plug_dump,
Packit Service db8eaa
	.nonblock = snd_pcm_generic_nonblock,
Packit Service db8eaa
	.async = snd_pcm_generic_async,
Packit Service db8eaa
	.mmap = snd_pcm_generic_mmap,
Packit Service db8eaa
	.munmap = snd_pcm_generic_munmap,
Packit Service db8eaa
	.query_chmaps = snd_pcm_generic_query_chmaps,
Packit Service db8eaa
	.get_chmap = snd_pcm_generic_get_chmap,
Packit Service db8eaa
	.set_chmap = snd_pcm_generic_set_chmap,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
/**
Packit Service db8eaa
 * \brief Creates a new Plug PCM
Packit Service db8eaa
 * \param pcmp Returns created PCM handle
Packit Service db8eaa
 * \param name Name of PCM
Packit Service db8eaa
 * \param sformat Slave (destination) format
Packit Service db8eaa
 * \param slave Slave PCM handle
Packit Service db8eaa
 * \param close_slave When set, the slave PCM handle is closed with copy PCM
Packit Service db8eaa
 * \retval zero on success otherwise a negative error code
Packit Service db8eaa
 * \warning Using of this function might be dangerous in the sense
Packit Service db8eaa
 *          of compatibility reasons. The prototype might be freely
Packit Service db8eaa
 *          changed in future.
Packit Service db8eaa
 */
Packit Service db8eaa
int snd_pcm_plug_open(snd_pcm_t **pcmp,
Packit Service db8eaa
		      const char *name,
Packit Service db8eaa
		      snd_pcm_format_t sformat, int schannels, int srate,
Packit Service db8eaa
		      const snd_config_t *rate_converter,
Packit Service db8eaa
		      enum snd_pcm_plug_route_policy route_policy,
Packit Service db8eaa
		      snd_pcm_route_ttable_entry_t *ttable,
Packit Service db8eaa
		      unsigned int tt_ssize,
Packit Service db8eaa
		      unsigned int tt_cused, unsigned int tt_sused,
Packit Service db8eaa
		      snd_pcm_t *slave, int close_slave)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_t *pcm;
Packit Service db8eaa
	snd_pcm_plug_t *plug;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	assert(pcmp && slave);
Packit Service db8eaa
Packit Service db8eaa
	plug = calloc(1, sizeof(snd_pcm_plug_t));
Packit Service db8eaa
	if (!plug)
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	plug->sformat = sformat;
Packit Service db8eaa
	plug->schannels = schannels;
Packit Service db8eaa
	plug->srate = srate;
Packit Service db8eaa
	plug->gen.slave = plug->req_slave = slave;
Packit Service db8eaa
	plug->gen.close_slave = close_slave;
Packit Service db8eaa
	plug->route_policy = route_policy;
Packit Service db8eaa
	plug->ttable = ttable;
Packit Service db8eaa
	plug->tt_ssize = tt_ssize;
Packit Service db8eaa
	plug->tt_cused = tt_cused;
Packit Service db8eaa
	plug->tt_sused = tt_sused;
Packit Service db8eaa
	
Packit Service db8eaa
	err = snd_pcm_new(&pcm, SND_PCM_TYPE_PLUG, name, slave->stream, slave->mode);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		free(plug);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	pcm->ops = &snd_pcm_plug_ops;
Packit Service db8eaa
	pcm->fast_ops = slave->fast_ops;
Packit Service db8eaa
	pcm->fast_op_arg = slave->fast_op_arg;
Packit Service db8eaa
	if (rate_converter) {
Packit Service db8eaa
		err = snd_config_copy(&plug->rate_converter,
Packit Service db8eaa
				      (snd_config_t *)rate_converter);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			snd_pcm_free(pcm);
Packit Service db8eaa
			free(plug);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	pcm->private_data = plug;
Packit Service db8eaa
	pcm->poll_fd = slave->poll_fd;
Packit Service db8eaa
	pcm->poll_events = slave->poll_events;
Packit Service db8eaa
	pcm->mmap_shadow = 1;
Packit Service db8eaa
	pcm->tstamp_type = slave->tstamp_type;
Packit Service db8eaa
	snd_pcm_link_hw_ptr(pcm, slave);
Packit Service db8eaa
	snd_pcm_link_appl_ptr(pcm, slave);
Packit Service db8eaa
	*pcmp = pcm;
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*! \page pcm_plugins
Packit Service db8eaa
Packit Service db8eaa
\section pcm_plugins_plug Automatic conversion plugin
Packit Service db8eaa
Packit Service db8eaa
This plugin converts channels, rate and format on request.
Packit Service db8eaa
Packit Service db8eaa
\code
Packit Service db8eaa
pcm.name {
Packit Service db8eaa
        type plug               # Automatic conversion PCM
Packit Service db8eaa
        slave STR               # Slave name
Packit Service db8eaa
        # or
Packit Service db8eaa
        slave {                 # Slave definition
Packit Service db8eaa
                pcm STR         # Slave PCM name
Packit Service db8eaa
                # or
Packit Service db8eaa
                pcm { }         # Slave PCM definition
Packit Service db8eaa
		[format STR]	# Slave format (default nearest) or "unchanged"
Packit Service db8eaa
		[channels INT]	# Slave channels (default nearest) or "unchanged"
Packit Service db8eaa
		[rate INT]	# Slave rate (default nearest) or "unchanged"
Packit Service db8eaa
        }
Packit Service db8eaa
	route_policy STR	# route policy for automatic ttable generation
Packit Service db8eaa
				# STR can be 'default', 'average', 'copy', 'duplicate'
Packit Service db8eaa
				# average: result is average of input channels
Packit Service db8eaa
				# copy: only first channels are copied to destination
Packit Service db8eaa
				# duplicate: duplicate first set of channels
Packit Service db8eaa
				# default: copy policy, except for mono capture - sum
Packit Service db8eaa
	ttable {		# Transfer table (bi-dimensional compound of cchannels * schannels numbers)
Packit Service db8eaa
		CCHANNEL {
Packit Service db8eaa
			SCHANNEL REAL	# route value (0.0 - 1.0)
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	rate_converter STR	# type of rate converter
Packit Service db8eaa
	# or
Packit Service db8eaa
	rate_converter [ STR1 STR2 ... ]
Packit Service db8eaa
				# type of rate converter
Packit Service db8eaa
				# default value is taken from defaults.pcm.rate_converter
Packit Service db8eaa
}
Packit Service db8eaa
\endcode
Packit Service db8eaa
Packit Service db8eaa
\subsection pcm_plugins_plug_funcref Function reference
Packit Service db8eaa
Packit Service db8eaa
    Packit Service db8eaa
      
  • snd_pcm_plug_open()
  • Packit Service db8eaa
      
  • _snd_pcm_plug_open()
  • Packit Service db8eaa
    Packit Service db8eaa
    Packit Service db8eaa
    */
    Packit Service db8eaa
    Packit Service db8eaa
    /**
    Packit Service db8eaa
     * \brief Creates a new Plug PCM
    Packit Service db8eaa
     * \param pcmp Returns created PCM handle
    Packit Service db8eaa
     * \param name Name of PCM
    Packit Service db8eaa
     * \param root Root configuration node
    Packit Service db8eaa
     * \param conf Configuration node with Plug PCM description
    Packit Service db8eaa
     * \param stream Stream type
    Packit Service db8eaa
     * \param mode Stream mode
    Packit Service db8eaa
     * \retval zero on success otherwise a negative error code
    Packit Service db8eaa
     * \warning Using of this function might be dangerous in the sense
    Packit Service db8eaa
     *          of compatibility reasons. The prototype might be freely
    Packit Service db8eaa
     *          changed in future.
    Packit Service db8eaa
     */
    Packit Service db8eaa
    int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
    Packit Service db8eaa
    		       snd_config_t *root, snd_config_t *conf, 
    Packit Service db8eaa
    		       snd_pcm_stream_t stream, int mode)
    Packit Service db8eaa
    {
    Packit Service db8eaa
    	snd_config_iterator_t i, next;
    Packit Service db8eaa
    	int err;
    Packit Service db8eaa
    	snd_pcm_t *spcm;
    Packit Service db8eaa
    	snd_config_t *slave = NULL, *sconf;
    Packit Service db8eaa
    	snd_config_t *tt = NULL;
    Packit Service db8eaa
    	enum snd_pcm_plug_route_policy route_policy = PLUG_ROUTE_POLICY_DEFAULT;
    Packit Service db8eaa
    	snd_pcm_route_ttable_entry_t *ttable = NULL;
    Packit Service db8eaa
    	unsigned int csize, ssize;
    Packit Service db8eaa
    	unsigned int cused, sused;
    Packit Service db8eaa
    	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
    Packit Service db8eaa
    	int schannels = -1, srate = -1;
    Packit Service db8eaa
    	const snd_config_t *rate_converter = NULL;
    Packit Service db8eaa
    Packit Service db8eaa
    	snd_config_for_each(i, next, conf) {
    Packit Service db8eaa
    		snd_config_t *n = snd_config_iterator_entry(i);
    Packit Service db8eaa
    		const char *id;
    Packit Service db8eaa
    		if (snd_config_get_id(n, &id) < 0)
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		if (snd_pcm_conf_generic_id(id))
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		if (strcmp(id, "slave") == 0) {
    Packit Service db8eaa
    			slave = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    #ifdef BUILD_PCM_PLUGIN_ROUTE
    Packit Service db8eaa
    		if (strcmp(id, "ttable") == 0) {
    Packit Service db8eaa
    			route_policy = PLUG_ROUTE_POLICY_NONE;
    Packit Service db8eaa
    			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
    Packit Service db8eaa
    				SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    				return -EINVAL;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			tt = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "route_policy") == 0) {
    Packit Service db8eaa
    			const char *str;
    Packit Service db8eaa
    			if ((err = snd_config_get_string(n, &str)) < 0) {
    Packit Service db8eaa
    				SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    				return -EINVAL;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			if (tt != NULL)
    Packit Service db8eaa
    				SNDERR("Table is defined, route policy is ignored");
    Packit Service db8eaa
    			if (!strcmp(str, "default"))
    Packit Service db8eaa
    				route_policy = PLUG_ROUTE_POLICY_DEFAULT;
    Packit Service db8eaa
    			else if (!strcmp(str, "average"))
    Packit Service db8eaa
    				route_policy = PLUG_ROUTE_POLICY_AVERAGE;
    Packit Service db8eaa
    			else if (!strcmp(str, "copy"))
    Packit Service db8eaa
    				route_policy = PLUG_ROUTE_POLICY_COPY;
    Packit Service db8eaa
    			else if (!strcmp(str, "duplicate"))
    Packit Service db8eaa
    				route_policy = PLUG_ROUTE_POLICY_DUP;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    #endif
    Packit Service db8eaa
    #ifdef BUILD_PCM_PLUGIN_RATE
    Packit Service db8eaa
    		if (strcmp(id, "rate_converter") == 0) {
    Packit Service db8eaa
    			rate_converter = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    #endif
    Packit Service db8eaa
    		SNDERR("Unknown field %s", id);
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (!slave) {
    Packit Service db8eaa
    		SNDERR("slave is not defined");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	err = snd_pcm_slave_conf(root, slave, &sconf, 3,
    Packit Service db8eaa
    				 SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &sformat,
    Packit Service db8eaa
    				 SND_PCM_HW_PARAM_CHANNELS, SCONF_UNCHANGED, &schannels,
    Packit Service db8eaa
    				 SND_PCM_HW_PARAM_RATE, SCONF_UNCHANGED, &srate);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    #ifdef BUILD_PCM_PLUGIN_ROUTE
    Packit Service db8eaa
    	if (tt) {
    Packit Service db8eaa
    		err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
    Packit Service db8eaa
    		if (err < 0) {
    Packit Service db8eaa
    			snd_config_delete(sconf);
    Packit Service db8eaa
    			return err;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		ttable = malloc(csize * ssize * sizeof(*ttable));
    Packit Service db8eaa
    		if (ttable == NULL) {
    Packit Service db8eaa
    			snd_config_delete(sconf);
    Packit Service db8eaa
    			return err;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, -1);
    Packit Service db8eaa
    		if (err < 0) {
    Packit Service db8eaa
    			snd_config_delete(sconf);
    Packit Service db8eaa
    			return err;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    #endif
    Packit Service db8eaa
    	
    Packit Service db8eaa
    #ifdef BUILD_PCM_PLUGIN_RATE
    Packit Service db8eaa
    	if (! rate_converter)
    Packit Service db8eaa
    		rate_converter = snd_pcm_rate_get_default_converter(root);
    Packit Service db8eaa
    #endif
    Packit Service db8eaa
    Packit Service db8eaa
    	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
    Packit Service db8eaa
    	snd_config_delete(sconf);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    	err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter,
    Packit Service db8eaa
    				route_policy, ttable, ssize, cused, sused, spcm, 1);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		snd_pcm_close(spcm);
    Packit Service db8eaa
    	return err;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    #ifndef DOC_HIDDEN
    Packit Service db8eaa
    SND_DLSYM_BUILD_VERSION(_snd_pcm_plug_open, SND_PCM_DLSYM_VERSION);
    Packit Service db8eaa
    #endif