Blame src/pcm/pcm_multi.c

Packit Service db8eaa
/**
Packit Service db8eaa
 * \file pcm/pcm_multi.c
Packit Service db8eaa
 * \ingroup PCM_Plugins
Packit Service db8eaa
 * \brief PCM Multi Streams to One Conversion 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 - Multi
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 <stdio.h>
Packit Service db8eaa
#include <stdlib.h>
Packit Service db8eaa
#include <unistd.h>
Packit Service db8eaa
#include <string.h>
Packit Service db8eaa
#include <math.h>
Packit Service db8eaa
#include "pcm_local.h"
Packit Service db8eaa
#include "pcm_generic.h"
Packit Service db8eaa
Packit Service db8eaa
#ifndef PIC
Packit Service db8eaa
/* entry for static linking */
Packit Service db8eaa
const char *_snd_module_pcm_multi = "";
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
#ifndef DOC_HIDDEN
Packit Service db8eaa
Packit Service db8eaa
typedef struct {
Packit Service db8eaa
	snd_pcm_t *pcm;
Packit Service db8eaa
	unsigned int channels_count;
Packit Service db8eaa
	int close_slave;
Packit Service db8eaa
	snd_pcm_t *linked;
Packit Service db8eaa
} snd_pcm_multi_slave_t;
Packit Service db8eaa
Packit Service db8eaa
typedef struct {
Packit Service db8eaa
	int slave_idx;
Packit Service db8eaa
	unsigned int slave_channel;
Packit Service db8eaa
} snd_pcm_multi_channel_t;
Packit Service db8eaa
Packit Service db8eaa
typedef struct {
Packit Service db8eaa
	snd_pcm_uframes_t appl_ptr, hw_ptr;
Packit Service db8eaa
	unsigned int slaves_count;
Packit Service db8eaa
	unsigned int master_slave;
Packit Service db8eaa
	snd_pcm_multi_slave_t *slaves;
Packit Service db8eaa
	unsigned int channels_count;
Packit Service db8eaa
	snd_pcm_multi_channel_t *channels;
Packit Service db8eaa
} snd_pcm_multi_t;
Packit Service db8eaa
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_close(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	int ret = 0;
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		snd_pcm_multi_slave_t *slave = &multi->slaves[i];
Packit Service db8eaa
		if (slave->close_slave) {
Packit Service db8eaa
			int err = snd_pcm_close(slave->pcm);
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				ret = err;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	free(multi->slaves);
Packit Service db8eaa
	free(multi->channels);
Packit Service db8eaa
	free(multi);
Packit Service db8eaa
	return ret;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_async(snd_pcm_t *pcm, int sig, pid_t pid)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
Packit Service db8eaa
	return snd_pcm_async(slave_0, sig, pid);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_poll_descriptors_count(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
Packit Service db8eaa
	return snd_pcm_poll_descriptors_count(slave_0);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave;
Packit Service db8eaa
	snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		slave = multi->slaves[i].pcm;
Packit Service db8eaa
		if (slave == slave_0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		err = snd_pcm_poll_descriptors(slave, pfds, space);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	/* finally overwrite with master's pfds */
Packit Service db8eaa
	return snd_pcm_poll_descriptors(slave_0, pfds, space);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
Packit Service db8eaa
	return snd_pcm_poll_descriptors_revents(slave_0, pfds, nfds, revents);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	int err, n;
Packit Service db8eaa
	assert(info->subdevice < multi->slaves_count);
Packit Service db8eaa
	n = info->subdevice;
Packit Service db8eaa
	info->subdevice = 0;
Packit Service db8eaa
	err = snd_pcm_info(multi->slaves[n].pcm, info);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	info->subdevices_count = multi->slaves_count;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_access_mask_t access_mask;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	snd_pcm_access_mask_any(&access_mask);
Packit Service db8eaa
	snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
Packit Service db8eaa
	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
Packit Service db8eaa
					 &access_mask);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_CHANNELS,
Packit Service db8eaa
				    multi->channels_count, 0);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	params->info = ~0U;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_hw_refine_sprepare(snd_pcm_t *pcm, unsigned int slave_idx,
Packit Service db8eaa
					    snd_pcm_hw_params_t *sparams)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_multi_slave_t *slave = &multi->slaves[slave_idx];
Packit Service db8eaa
	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
Packit Service db8eaa
	_snd_pcm_hw_params_any(sparams);
Packit Service db8eaa
	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
Packit Service db8eaa
				   &saccess_mask);
Packit Service db8eaa
	_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
Packit Service db8eaa
			      slave->channels_count, 0);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
Packit Service db8eaa
					   unsigned int slave_idx 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
	int err;
Packit Service db8eaa
	unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_SUBFORMAT |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_RATE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIODS |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_BUFFER_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit Service db8eaa
	const snd_pcm_access_mask_t *access_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
Packit Service db8eaa
	if (!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
Packit Service db8eaa
	    !snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) &&
Packit Service db8eaa
	    !snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
Packit Service db8eaa
		snd_pcm_access_mask_t saccess_mask;
Packit Service db8eaa
		snd_pcm_access_mask_any(&saccess_mask);
Packit Service db8eaa
		snd_pcm_access_mask_reset(&saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
Packit Service db8eaa
		err = _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
Packit Service db8eaa
						 &saccess_mask);
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_multi_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
Packit Service db8eaa
					   unsigned int slave_idx 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
	int err;
Packit Service db8eaa
	unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_SUBFORMAT |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_RATE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_PERIODS |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_BUFFER_TIME |
Packit Service db8eaa
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit Service db8eaa
	snd_pcm_access_mask_t access_mask;
Packit Service db8eaa
	const snd_pcm_access_mask_t *saccess_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS);
Packit Service db8eaa
	snd_pcm_access_mask_any(&access_mask);
Packit Service db8eaa
	snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
Packit Service db8eaa
	if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
Packit Service db8eaa
		snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
Packit Service db8eaa
	if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_COMPLEX) &&
Packit Service db8eaa
	    !snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED))
Packit Service db8eaa
		snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_COMPLEX);
Packit Service db8eaa
	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
Packit Service db8eaa
					 &access_mask);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
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
	params->info &= sparams->info;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_hw_refine_slave(snd_pcm_t *pcm,
Packit Service db8eaa
					 unsigned int slave_idx,
Packit Service db8eaa
					 snd_pcm_hw_params_t *sparams)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
Packit Service db8eaa
	return snd_pcm_hw_refine(slave, sparams);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int k;
Packit Service db8eaa
	snd_pcm_hw_params_t sparams[multi->slaves_count];
Packit Service db8eaa
	int err;
Packit Service db8eaa
	unsigned int cmask, changed;
Packit Service db8eaa
	err = snd_pcm_multi_hw_refine_cprepare(pcm, params);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	for (k = 0; k < multi->slaves_count; ++k) {
Packit Service db8eaa
		err = snd_pcm_multi_hw_refine_sprepare(pcm, k, &sparams[k]);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			SNDERR("Slave PCM #%d not usable", k);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	do {
Packit Service db8eaa
		cmask = params->cmask;
Packit Service db8eaa
		params->cmask = 0;
Packit Service db8eaa
		for (k = 0; k < multi->slaves_count; ++k) {
Packit Service db8eaa
			err = snd_pcm_multi_hw_refine_schange(pcm, k, params, &sparams[k]);
Packit Service db8eaa
			if (err >= 0)
Packit Service db8eaa
				err = snd_pcm_multi_hw_refine_slave(pcm, k, &sparams[k]);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			err = snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				return err;
Packit Service db8eaa
		}
Packit Service db8eaa
		err = snd_pcm_hw_refine_soft(pcm, params);
Packit Service db8eaa
		changed = params->cmask;
Packit Service db8eaa
		params->cmask |= cmask;
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	} while (changed);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_hw_params_slave(snd_pcm_t *pcm,
Packit Service db8eaa
					 unsigned int slave_idx,
Packit Service db8eaa
					 snd_pcm_hw_params_t *sparams)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
Packit Service db8eaa
	int err = snd_pcm_hw_params(slave, sparams);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	if (slave->stopped_areas) {
Packit Service db8eaa
		err = snd_pcm_areas_silence(slave->stopped_areas, 0, slave->channels, slave->buffer_size, slave->format);
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
/* reset links to the normal state
Packit Service db8eaa
 * slave #0 = trigger master
Packit Service db8eaa
 * slave #1-(N-1) = trigger slaves, linked is set to #0
Packit Service db8eaa
 */
Packit Service db8eaa
static void reset_links(snd_pcm_multi_t *multi)
Packit Service db8eaa
{
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		if (multi->slaves[i].linked)
Packit Service db8eaa
			snd_pcm_unlink(multi->slaves[i].linked);
Packit Service db8eaa
		multi->slaves[0].linked = NULL;
Packit Service db8eaa
		if (! i)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		if (snd_pcm_link(multi->slaves[0].pcm, multi->slaves[i].pcm) >= 0)
Packit Service db8eaa
			multi->slaves[i].linked = multi->slaves[0].pcm;
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	snd_pcm_hw_params_t sparams[multi->slaves_count];
Packit Service db8eaa
	int err;
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		err = snd_pcm_multi_hw_refine_sprepare(pcm, i, &sparams[i]);
Packit Service db8eaa
		assert(err >= 0);
Packit Service db8eaa
		err = snd_pcm_multi_hw_refine_schange(pcm, i, params, &sparams[i]);
Packit Service db8eaa
		assert(err >= 0);
Packit Service db8eaa
		err = snd_pcm_multi_hw_params_slave(pcm, i, &sparams[i]);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			snd_pcm_multi_hw_refine_cchange(pcm, i, params, &sparams[i]);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	reset_links(multi);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_hw_free(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	int err = 0;
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		snd_pcm_t *slave = multi->slaves[i].pcm;
Packit Service db8eaa
		int e = snd_pcm_hw_free(slave);
Packit Service db8eaa
		if (e < 0)
Packit Service db8eaa
			err = e;
Packit Service db8eaa
		if (!multi->slaves[i].linked)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		e = snd_pcm_unlink(slave);
Packit Service db8eaa
		if (e < 0)
Packit Service db8eaa
			err = e;
Packit Service db8eaa
		multi->slaves[i].linked = NULL;
Packit Service db8eaa
	}
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		snd_pcm_t *slave = multi->slaves[i].pcm;
Packit Service db8eaa
		err = snd_pcm_sw_params(slave, params);
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_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
Packit Service db8eaa
	return snd_pcm_status(slave, status);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_state_t snd_pcm_multi_state(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
Packit Service db8eaa
	return snd_pcm_state(slave);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void snd_pcm_multi_hwptr_update(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_uframes_t hw_ptr = 0, slave_hw_ptr, avail, last_avail;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	/* the logic is really simple, choose the lowest hw_ptr from slaves */
Packit Service db8eaa
	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
Packit Service db8eaa
		last_avail = 0;
Packit Service db8eaa
		for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
			slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr;
Packit Service db8eaa
			avail = __snd_pcm_playback_avail(pcm, multi->hw_ptr, slave_hw_ptr);
Packit Service db8eaa
			if (avail > last_avail) {
Packit Service db8eaa
				hw_ptr = slave_hw_ptr;
Packit Service db8eaa
				last_avail = avail;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
	} else {
Packit Service db8eaa
		last_avail = LONG_MAX;
Packit Service db8eaa
		for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
			slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr;
Packit Service db8eaa
			avail = __snd_pcm_capture_avail(pcm, multi->hw_ptr, slave_hw_ptr);
Packit Service db8eaa
			if (avail < last_avail) {
Packit Service db8eaa
				hw_ptr = slave_hw_ptr;
Packit Service db8eaa
				last_avail = avail;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	multi->hw_ptr = hw_ptr;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_hwsync(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		err = snd_pcm_hwsync(multi->slaves[i].pcm);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	snd_pcm_multi_hwptr_update(pcm);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_sframes_t d, dr = 0;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		err = snd_pcm_delay(multi->slaves[i].pcm, &d);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		if (dr < d)
Packit Service db8eaa
			dr = d;
Packit Service db8eaa
	}
Packit Service db8eaa
	*delayp = dr;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_sframes_t ret = LONG_MAX;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		snd_pcm_sframes_t avail;
Packit Service db8eaa
		avail = snd_pcm_avail_update(multi->slaves[i].pcm);
Packit Service db8eaa
		if (avail < 0)
Packit Service db8eaa
			return avail;
Packit Service db8eaa
		if (ret > avail)
Packit Service db8eaa
			ret = avail;
Packit Service db8eaa
	}
Packit Service db8eaa
	snd_pcm_multi_hwptr_update(pcm);
Packit Service db8eaa
	return ret;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail,
Packit Service db8eaa
				    snd_htimestamp_t *tstamp)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
Packit Service db8eaa
	return snd_pcm_htimestamp(slave, avail, tstamp);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_prepare(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	int result = 0, err;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		/* We call prepare to each slave even if it's linked.
Packit Service db8eaa
		 * This is to make sure to sync non-mmaped control/status.
Packit Service db8eaa
		 */
Packit Service db8eaa
		err = snd_pcm_prepare(multi->slaves[i].pcm);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			result = err;
Packit Service db8eaa
	}
Packit Service db8eaa
	multi->hw_ptr = multi->appl_ptr = 0;
Packit Service db8eaa
	return result;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_reset(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	int result = 0, err;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		/* Reset each slave, as well as in prepare */
Packit Service db8eaa
		err = snd_pcm_reset(multi->slaves[i].pcm);
Packit Service db8eaa
		if (err < 0) 
Packit Service db8eaa
			result = err;
Packit Service db8eaa
	}
Packit Service db8eaa
	multi->hw_ptr = multi->appl_ptr = 0;
Packit Service db8eaa
	return result;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* when the first slave PCM is linked, it means that the whole multi
Packit Service db8eaa
 * plugin instance is linked manually to another PCM.  in this case,
Packit Service db8eaa
 * we need to trigger the master.
Packit Service db8eaa
 */
Packit Service db8eaa
static int snd_pcm_multi_start(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	int err = 0;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	if (multi->slaves[0].linked)
Packit Service db8eaa
		return snd_pcm_start(multi->slaves[0].linked);
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		if (multi->slaves[i].linked)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		err = snd_pcm_start(multi->slaves[i].pcm);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_drop(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	int err = 0;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	if (multi->slaves[0].linked)
Packit Service db8eaa
		return snd_pcm_drop(multi->slaves[0].linked);
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		if (multi->slaves[i].linked)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		err = snd_pcm_drop(multi->slaves[i].pcm);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_drain(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	int err = 0;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	if (multi->slaves[0].linked)
Packit Service db8eaa
		return snd_pcm_drain(multi->slaves[0].linked);
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		if (multi->slaves[i].linked)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		err = snd_pcm_drain(multi->slaves[i].pcm);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_pause(snd_pcm_t *pcm, int enable)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	int err = 0;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	if (multi->slaves[0].linked)
Packit Service db8eaa
		return snd_pcm_pause(multi->slaves[0].linked, enable);
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		if (multi->slaves[i].linked)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		err = snd_pcm_pause(multi->slaves[i].pcm, enable);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int channel = info->channel;
Packit Service db8eaa
	snd_pcm_multi_channel_t *c = &multi->channels[channel];
Packit Service db8eaa
	int err;
Packit Service db8eaa
	if (c->slave_idx < 0)
Packit Service db8eaa
		return -ENXIO;
Packit Service db8eaa
	info->channel = c->slave_channel;
Packit Service db8eaa
	err = snd_pcm_channel_info(multi->slaves[c->slave_idx].pcm, info);
Packit Service db8eaa
	info->channel = channel;
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_multi_rewindable(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	snd_pcm_sframes_t frames = LONG_MAX;
Packit Service db8eaa
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		snd_pcm_sframes_t f = snd_pcm_rewindable(multi->slaves[i].pcm);
Packit Service db8eaa
		if (f <= 0)
Packit Service db8eaa
			return f;
Packit Service db8eaa
		if (f < frames)
Packit Service db8eaa
			frames = f;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return frames;
Packit Service db8eaa
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_multi_forwardable(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	snd_pcm_sframes_t frames = LONG_MAX;
Packit Service db8eaa
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		snd_pcm_sframes_t f = snd_pcm_forwardable(multi->slaves[i].pcm);
Packit Service db8eaa
		if (f <= 0)
Packit Service db8eaa
			return f;
Packit Service db8eaa
		if (f < frames)
Packit Service db8eaa
			frames = f;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return frames;
Packit Service db8eaa
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_multi_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	snd_pcm_uframes_t pos[multi->slaves_count];
Packit Service db8eaa
	memset(pos, 0, sizeof(pos));
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		snd_pcm_t *slave_i = multi->slaves[i].pcm;
Packit Service db8eaa
		snd_pcm_sframes_t f = snd_pcm_rewind(slave_i, frames);
Packit Service db8eaa
		if (f < 0)
Packit Service db8eaa
			return f;
Packit Service db8eaa
		pos[i] = f;
Packit Service db8eaa
		frames = f;
Packit Service db8eaa
	}
Packit Service db8eaa
	/* Realign the pointers */
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		snd_pcm_t *slave_i = multi->slaves[i].pcm;
Packit Service db8eaa
		snd_pcm_uframes_t f = pos[i] - frames;
Packit Service db8eaa
		snd_pcm_sframes_t result;
Packit Service db8eaa
		if (f > 0) {
Packit Service db8eaa
			result = INTERNAL(snd_pcm_forward)(slave_i, f);
Packit Service db8eaa
			if (result < 0)
Packit Service db8eaa
				return result;
Packit Service db8eaa
			if ((snd_pcm_uframes_t)result != f)
Packit Service db8eaa
				return -EIO;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	return frames;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_multi_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	snd_pcm_uframes_t pos[multi->slaves_count];
Packit Service db8eaa
	memset(pos, 0, sizeof(pos));
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		snd_pcm_t *slave_i = multi->slaves[i].pcm;
Packit Service db8eaa
		snd_pcm_sframes_t f = INTERNAL(snd_pcm_forward)(slave_i, frames);
Packit Service db8eaa
		if (f < 0)
Packit Service db8eaa
			return f;
Packit Service db8eaa
		pos[i] = f;
Packit Service db8eaa
		frames = f;
Packit Service db8eaa
	}
Packit Service db8eaa
	/* Realign the pointers */
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		snd_pcm_t *slave_i = multi->slaves[i].pcm;
Packit Service db8eaa
		snd_pcm_uframes_t f = pos[i] - frames;
Packit Service db8eaa
		snd_pcm_sframes_t result;
Packit Service db8eaa
		if (f > 0) {
Packit Service db8eaa
			result = snd_pcm_rewind(slave_i, f);
Packit Service db8eaa
			if (result < 0)
Packit Service db8eaa
				return result;
Packit Service db8eaa
			if ((snd_pcm_uframes_t)result != f)
Packit Service db8eaa
				return -EIO;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	return frames;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_resume(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	int err = 0;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	if (multi->slaves[0].linked)
Packit Service db8eaa
		return snd_pcm_resume(multi->slaves[0].linked);
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		if (multi->slaves[i].linked)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		err = snd_pcm_resume(multi->slaves[i].pcm);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* if a multi plugin instance is linked as slaves, every slave PCMs
Packit Service db8eaa
 * including the first one has to be relinked to the given master.
Packit Service db8eaa
 */
Packit Service db8eaa
static int snd_pcm_multi_link_slaves(snd_pcm_t *pcm, snd_pcm_t *master)
Packit Service db8eaa
{ 
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		snd_pcm_unlink(multi->slaves[i].pcm);
Packit Service db8eaa
		multi->slaves[i].linked = NULL;
Packit Service db8eaa
		err = snd_pcm_link(master, multi->slaves[i].pcm);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			reset_links(multi);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
		multi->slaves[i].linked = master;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* linking to a multi as a master is easy - simply link to the first
Packit Service db8eaa
 * slave element as its own slaves are already linked.
Packit Service db8eaa
 */
Packit Service db8eaa
static int snd_pcm_multi_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm1->private_data;
Packit Service db8eaa
	if (multi->slaves[0].pcm->fast_ops->link)
Packit Service db8eaa
		return multi->slaves[0].pcm->fast_ops->link(multi->slaves[0].pcm, pcm2);
Packit Service db8eaa
	return -ENOSYS;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_unlink(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		if (multi->slaves[i].linked)
Packit Service db8eaa
			snd_pcm_unlink(multi->slaves[i].linked);
Packit Service db8eaa
		multi->slaves[0].linked = NULL;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_multi_mmap_commit(snd_pcm_t *pcm,
Packit Service db8eaa
						   snd_pcm_uframes_t offset,
Packit Service db8eaa
						   snd_pcm_uframes_t size)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_t *slave;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	snd_pcm_sframes_t result;
Packit Service db8eaa
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		slave = multi->slaves[i].pcm;
Packit Service db8eaa
		result = snd_pcm_mmap_commit(slave, offset, size);
Packit Service db8eaa
		if (result < 0)
Packit Service db8eaa
			return result;
Packit Service db8eaa
		if ((snd_pcm_uframes_t)result != size)
Packit Service db8eaa
			return -EIO;
Packit Service db8eaa
	}
Packit Service db8eaa
	multi->appl_ptr += size;
Packit Service db8eaa
	multi->appl_ptr %= pcm->boundary;
Packit Service db8eaa
	return size;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_munmap(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	free(pcm->mmap_channels);
Packit Service db8eaa
	free(pcm->running_areas);
Packit Service db8eaa
	pcm->mmap_channels = NULL;
Packit Service db8eaa
	pcm->running_areas = NULL;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_mmap(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int c;
Packit Service db8eaa
Packit Service db8eaa
	pcm->mmap_channels = calloc(pcm->channels,
Packit Service db8eaa
				    sizeof(pcm->mmap_channels[0]));
Packit Service db8eaa
	pcm->running_areas = calloc(pcm->channels,
Packit Service db8eaa
				    sizeof(pcm->running_areas[0]));
Packit Service db8eaa
	if (!pcm->mmap_channels || !pcm->running_areas) {
Packit Service db8eaa
		snd_pcm_multi_munmap(pcm);
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* Copy the slave mmapped buffer data */
Packit Service db8eaa
	for (c = 0; c < pcm->channels; c++) {
Packit Service db8eaa
		snd_pcm_multi_channel_t *chan = &multi->channels[c];
Packit Service db8eaa
		snd_pcm_t *slave;
Packit Service db8eaa
		if (chan->slave_idx < 0) {
Packit Service db8eaa
			snd_pcm_multi_munmap(pcm);
Packit Service db8eaa
			return -ENXIO;
Packit Service db8eaa
		}
Packit Service db8eaa
		slave = multi->slaves[chan->slave_idx].pcm;
Packit Service db8eaa
		pcm->mmap_channels[c] =
Packit Service db8eaa
			slave->mmap_channels[chan->slave_channel];
Packit Service db8eaa
		pcm->mmap_channels[c].channel = c;
Packit Service db8eaa
		pcm->running_areas[c] =
Packit Service db8eaa
			slave->running_areas[chan->slave_channel];
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; ++i) {
Packit Service db8eaa
		if (snd_pcm_may_wait_for_avail_min(multi->slaves[i].pcm, avail))
Packit Service db8eaa
			return 1;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_chmap_query_t **snd_pcm_multi_query_chmaps(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_chmap_query_t **slave_maps[multi->slaves_count];
Packit Service db8eaa
	snd_pcm_chmap_query_t **maps;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	int err = -ENOMEM;
Packit Service db8eaa
Packit Service db8eaa
	memset(slave_maps, 0, sizeof(slave_maps));
Packit Service db8eaa
	maps = calloc(2, sizeof(*maps));
Packit Service db8eaa
	if (!maps)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	maps[0] = calloc(multi->channels_count + 2, sizeof(int *));
Packit Service db8eaa
	if (!maps[0])
Packit Service db8eaa
		goto error;
Packit Service db8eaa
	maps[0]->type = SND_CHMAP_TYPE_FIXED;
Packit Service db8eaa
	maps[0]->map.channels = multi->channels_count;
Packit Service db8eaa
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; i++) {
Packit Service db8eaa
		slave_maps[i] = snd_pcm_query_chmaps(multi->slaves[i].pcm);
Packit Service db8eaa
		if (!slave_maps[i])
Packit Service db8eaa
			goto error;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	for (i = 0; i < multi->channels_count; i++) {
Packit Service db8eaa
		snd_pcm_multi_channel_t *bind = &multi->channels[i];
Packit Service db8eaa
		unsigned int slave_channels =
Packit Service db8eaa
			multi->slaves[bind->slave_idx].channels_count;
Packit Service db8eaa
		snd_pcm_chmap_query_t **p;
Packit Service db8eaa
Packit Service db8eaa
		for (p = slave_maps[bind->slave_idx]; *p; p++) {
Packit Service db8eaa
			if ((*p)->map.channels == slave_channels) {
Packit Service db8eaa
				maps[0]->map.pos[i] =
Packit Service db8eaa
					(*p)->map.pos[bind->slave_channel];
Packit Service db8eaa
				break;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	err = 0;
Packit Service db8eaa
Packit Service db8eaa
 error:
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; i++) {
Packit Service db8eaa
		if (slave_maps[i])
Packit Service db8eaa
			snd_pcm_free_chmaps(slave_maps[i]);
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if (err) {
Packit Service db8eaa
		snd_pcm_free_chmaps(maps);
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return maps;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_chmap_t *snd_pcm_multi_get_chmap(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_chmap_t *map;
Packit Service db8eaa
	snd_pcm_chmap_t *slave_maps[multi->slaves_count];
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	int err = -ENOMEM;
Packit Service db8eaa
Packit Service db8eaa
	memset(slave_maps, 0, sizeof(slave_maps));
Packit Service db8eaa
	map = calloc(multi->channels_count + 1, sizeof(int));
Packit Service db8eaa
	if (!map)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; i++) {
Packit Service db8eaa
		slave_maps[i] = snd_pcm_get_chmap(multi->slaves[i].pcm);
Packit Service db8eaa
		if (!slave_maps[i])
Packit Service db8eaa
			goto error;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	map->channels = multi->channels_count;
Packit Service db8eaa
	for (i = 0; i < multi->channels_count; i++) {
Packit Service db8eaa
		snd_pcm_multi_channel_t *bind = &multi->channels[i];
Packit Service db8eaa
		map->pos[i] = slave_maps[bind->slave_idx]->pos[bind->slave_channel];
Packit Service db8eaa
	}
Packit Service db8eaa
	err = 0;
Packit Service db8eaa
Packit Service db8eaa
 error:
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; i++)
Packit Service db8eaa
		free(slave_maps[i]);
Packit Service db8eaa
Packit Service db8eaa
	if (err) {
Packit Service db8eaa
		free(map);
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return map;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_multi_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	snd_pcm_chmap_t *slave_maps[multi->slaves_count];
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	int err = 0;
Packit Service db8eaa
Packit Service db8eaa
	if (map->channels != multi->channels_count)
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; i++) {
Packit Service db8eaa
		slave_maps[i] = calloc(multi->slaves[i].channels_count + 1,
Packit Service db8eaa
				       sizeof(int));
Packit Service db8eaa
		if (!slave_maps[i]) {
Packit Service db8eaa
			for (i++; i < multi->slaves_count; i++)
Packit Service db8eaa
				slave_maps[i] = NULL;
Packit Service db8eaa
			err = -ENOMEM;
Packit Service db8eaa
			goto error;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	for (i = 0; i < multi->channels_count; i++) {
Packit Service db8eaa
		snd_pcm_multi_channel_t *bind = &multi->channels[i];
Packit Service db8eaa
		slave_maps[bind->slave_idx]->pos[bind->slave_channel] =
Packit Service db8eaa
			map->pos[i];
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; i++) {
Packit Service db8eaa
		err = snd_pcm_set_chmap(multi->slaves[i].pcm, slave_maps[i]);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			goto error;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
 error:
Packit Service db8eaa
	for (i = 0; i < multi->slaves_count; i++)
Packit Service db8eaa
		free(slave_maps[i]);
Packit Service db8eaa
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void snd_pcm_multi_dump(snd_pcm_t *pcm, snd_output_t *out)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_multi_t *multi = pcm->private_data;
Packit Service db8eaa
	unsigned int k;
Packit Service db8eaa
	snd_output_printf(out, "Multi PCM\n");
Packit Service db8eaa
	snd_output_printf(out, "  Channel bindings:\n");
Packit Service db8eaa
	for (k = 0; k < multi->channels_count; ++k) {
Packit Service db8eaa
		snd_pcm_multi_channel_t *c = &multi->channels[k];
Packit Service db8eaa
		if (c->slave_idx < 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		snd_output_printf(out, "    %d: slave %d, channel %d\n", 
Packit Service db8eaa
			k, c->slave_idx, c->slave_channel);
Packit Service db8eaa
	}
Packit Service db8eaa
	if (pcm->setup) {
Packit Service db8eaa
		snd_output_printf(out, "Its setup is:\n");
Packit Service db8eaa
		snd_pcm_dump_setup(pcm, out);
Packit Service db8eaa
	}
Packit Service db8eaa
	for (k = 0; k < multi->slaves_count; ++k) {
Packit Service db8eaa
		snd_output_printf(out, "Slave #%d: ", k);
Packit Service db8eaa
		snd_pcm_dump(multi->slaves[k].pcm, out);
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static const snd_pcm_ops_t snd_pcm_multi_ops = {
Packit Service db8eaa
	.close = snd_pcm_multi_close,
Packit Service db8eaa
	.info = snd_pcm_multi_info,
Packit Service db8eaa
	.hw_refine = snd_pcm_multi_hw_refine,
Packit Service db8eaa
	.hw_params = snd_pcm_multi_hw_params,
Packit Service db8eaa
	.hw_free = snd_pcm_multi_hw_free,
Packit Service db8eaa
	.sw_params = snd_pcm_multi_sw_params,
Packit Service db8eaa
	.channel_info = snd_pcm_multi_channel_info,
Packit Service db8eaa
	.dump = snd_pcm_multi_dump,
Packit Service db8eaa
	.nonblock = snd_pcm_multi_nonblock,
Packit Service db8eaa
	.async = snd_pcm_multi_async,
Packit Service db8eaa
	.mmap = snd_pcm_multi_mmap,
Packit Service db8eaa
	.munmap = snd_pcm_multi_munmap,
Packit Service db8eaa
	.query_chmaps = snd_pcm_multi_query_chmaps,
Packit Service db8eaa
	.get_chmap = snd_pcm_multi_get_chmap,
Packit Service db8eaa
	.set_chmap = snd_pcm_multi_set_chmap,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
static const snd_pcm_fast_ops_t snd_pcm_multi_fast_ops = {
Packit Service db8eaa
	.status = snd_pcm_multi_status,
Packit Service db8eaa
	.state = snd_pcm_multi_state,
Packit Service db8eaa
	.hwsync = snd_pcm_multi_hwsync,
Packit Service db8eaa
	.delay = snd_pcm_multi_delay,
Packit Service db8eaa
	.prepare = snd_pcm_multi_prepare,
Packit Service db8eaa
	.reset = snd_pcm_multi_reset,
Packit Service db8eaa
	.start = snd_pcm_multi_start,
Packit Service db8eaa
	.drop = snd_pcm_multi_drop,
Packit Service db8eaa
	.drain = snd_pcm_multi_drain,
Packit Service db8eaa
	.pause = snd_pcm_multi_pause,
Packit Service db8eaa
	.writei = snd_pcm_mmap_writei,
Packit Service db8eaa
	.writen = snd_pcm_mmap_writen,
Packit Service db8eaa
	.readi = snd_pcm_mmap_readi,
Packit Service db8eaa
	.readn = snd_pcm_mmap_readn,
Packit Service db8eaa
	.rewindable = snd_pcm_multi_rewindable,
Packit Service db8eaa
	.rewind = snd_pcm_multi_rewind,
Packit Service db8eaa
	.forwardable = snd_pcm_multi_forwardable,
Packit Service db8eaa
	.forward = snd_pcm_multi_forward,
Packit Service db8eaa
	.resume = snd_pcm_multi_resume,
Packit Service db8eaa
	.link = snd_pcm_multi_link,
Packit Service db8eaa
	.link_slaves = snd_pcm_multi_link_slaves,
Packit Service db8eaa
	.unlink = snd_pcm_multi_unlink,
Packit Service db8eaa
	.avail_update = snd_pcm_multi_avail_update,
Packit Service db8eaa
	.mmap_commit = snd_pcm_multi_mmap_commit,
Packit Service db8eaa
	.htimestamp = snd_pcm_multi_htimestamp,
Packit Service db8eaa
	.poll_descriptors_count = snd_pcm_multi_poll_descriptors_count,
Packit Service db8eaa
	.poll_descriptors = snd_pcm_multi_poll_descriptors,
Packit Service db8eaa
	.poll_revents = snd_pcm_multi_poll_revents,
Packit Service db8eaa
	.may_wait_for_avail_min = snd_pcm_multi_may_wait_for_avail_min,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
/**
Packit Service db8eaa
 * \brief Creates a new Multi PCM
Packit Service db8eaa
 * \param pcmp Returns created PCM handle
Packit Service db8eaa
 * \param name Name of PCM
Packit Service db8eaa
 * \param slaves_count Count of slaves
Packit Service db8eaa
 * \param master_slave Master slave number
Packit Service db8eaa
 * \param slaves_pcm Array with slave PCMs
Packit Service db8eaa
 * \param schannels_count Array with slave channel counts
Packit Service db8eaa
 * \param channels_count Count of channels
Packit Service db8eaa
 * \param sidxs Array with channels indexes to slaves
Packit Service db8eaa
 * \param schannels Array with slave channels
Packit Service db8eaa
 * \param close_slaves When set, the slave PCM handle is closed
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_multi_open(snd_pcm_t **pcmp, const char *name,
Packit Service db8eaa
		       unsigned int slaves_count, unsigned int master_slave,
Packit Service db8eaa
		       snd_pcm_t **slaves_pcm, unsigned int *schannels_count,
Packit Service db8eaa
		       unsigned int channels_count,
Packit Service db8eaa
		       int *sidxs, unsigned int *schannels,
Packit Service db8eaa
		       int close_slaves)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_t *pcm;
Packit Service db8eaa
	snd_pcm_multi_t *multi;
Packit Service db8eaa
	unsigned int i;
Packit Service db8eaa
	snd_pcm_stream_t stream;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	assert(pcmp);
Packit Service db8eaa
	assert(slaves_count > 0 && slaves_pcm && schannels_count);
Packit Service db8eaa
	assert(channels_count > 0 && sidxs && schannels);
Packit Service db8eaa
	assert(master_slave < slaves_count);
Packit Service db8eaa
Packit Service db8eaa
	multi = calloc(1, sizeof(snd_pcm_multi_t));
Packit Service db8eaa
	if (!multi) {
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	stream = slaves_pcm[0]->stream;
Packit Service db8eaa
	
Packit Service db8eaa
	multi->slaves_count = slaves_count;
Packit Service db8eaa
	multi->master_slave = master_slave;
Packit Service db8eaa
	multi->slaves = calloc(slaves_count, sizeof(*multi->slaves));
Packit Service db8eaa
	if (!multi->slaves) {
Packit Service db8eaa
		free(multi);
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
	multi->channels_count = channels_count;
Packit Service db8eaa
	multi->channels = calloc(channels_count, sizeof(*multi->channels));
Packit Service db8eaa
	if (!multi->channels) {
Packit Service db8eaa
		free(multi->slaves);
Packit Service db8eaa
		free(multi);
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
	for (i = 0; i < slaves_count; ++i) {
Packit Service db8eaa
		snd_pcm_multi_slave_t *slave = &multi->slaves[i];
Packit Service db8eaa
		assert(slaves_pcm[i]->stream == stream);
Packit Service db8eaa
		slave->pcm = slaves_pcm[i];
Packit Service db8eaa
		slave->channels_count = schannels_count[i];
Packit Service db8eaa
		slave->close_slave = close_slaves;
Packit Service db8eaa
	}
Packit Service db8eaa
	for (i = 0; i < channels_count; ++i) {
Packit Service db8eaa
		snd_pcm_multi_channel_t *bind = &multi->channels[i];
Packit Service db8eaa
		assert(sidxs[i] < (int)slaves_count);
Packit Service db8eaa
		assert(schannels[i] < schannels_count[sidxs[i]]);
Packit Service db8eaa
		bind->slave_idx = sidxs[i];
Packit Service db8eaa
		bind->slave_channel = schannels[i];
Packit Service db8eaa
		if (sidxs[i] < 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
	}
Packit Service db8eaa
	multi->channels_count = channels_count;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_pcm_new(&pcm, SND_PCM_TYPE_MULTI, name, stream,
Packit Service db8eaa
			  multi->slaves[0].pcm->mode);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		free(multi->slaves);
Packit Service db8eaa
		free(multi->channels);
Packit Service db8eaa
		free(multi);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	pcm->mmap_rw = 1;
Packit Service db8eaa
	pcm->mmap_shadow = 1; /* has own mmap method */
Packit Service db8eaa
	pcm->ops = &snd_pcm_multi_ops;
Packit Service db8eaa
	pcm->fast_ops = &snd_pcm_multi_fast_ops;
Packit Service db8eaa
	pcm->private_data = multi;
Packit Service db8eaa
	pcm->poll_fd = multi->slaves[master_slave].pcm->poll_fd;
Packit Service db8eaa
	pcm->poll_events = multi->slaves[master_slave].pcm->poll_events;
Packit Service db8eaa
	pcm->tstamp_type = multi->slaves[master_slave].pcm->tstamp_type;
Packit Service db8eaa
	snd_pcm_set_hw_ptr(pcm, &multi->hw_ptr, -1, 0);
Packit Service db8eaa
	snd_pcm_set_appl_ptr(pcm, &multi->appl_ptr, -1, 0);
Packit Service db8eaa
	*pcmp = pcm;
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_multi Plugin: Multiple streams to One
Packit Service db8eaa
Packit Service db8eaa
This plugin converts multiple streams to one.
Packit Service db8eaa
Packit Service db8eaa
\code
Packit Service db8eaa
pcm.name {
Packit Service db8eaa
        type multi              # Multiple streams conversion PCM
Packit Service db8eaa
        slaves {		# Slaves definition
Packit Service db8eaa
		ID STR		# Slave PCM name
Packit Service db8eaa
		# or
Packit Service db8eaa
		ID {
Packit Service db8eaa
			pcm STR		# Slave PCM name
Packit Service db8eaa
			# or
Packit Service db8eaa
			pcm { } 	# Slave PCM definition
Packit Service db8eaa
			channels INT	# Slave channels
Packit Service db8eaa
		}
Packit Service db8eaa
        }
Packit Service db8eaa
	bindings {		# Bindings table
Packit Service db8eaa
		N {
Packit Service db8eaa
			slave STR	# Slave key
Packit Service db8eaa
			channel INT	# Slave channel
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	[master INT]		# Define the master slave
Packit Service db8eaa
}
Packit Service db8eaa
\endcode
Packit Service db8eaa
Packit Service db8eaa
For example, to bind two PCM streams with two-channel stereo (hw:0,0 and
Packit Service db8eaa
hw:0,1) as one 4-channel stereo PCM stream, define like this:
Packit Service db8eaa
\code
Packit Service db8eaa
pcm.quad {
Packit Service db8eaa
	type multi
Packit Service db8eaa
Packit Service db8eaa
	slaves.a.pcm "hw:0,0"
Packit Service db8eaa
	slaves.a.channels 2
Packit Service db8eaa
	slaves.b.pcm "hw:0,1"
Packit Service db8eaa
	slaves.b.channels 2
Packit Service db8eaa
Packit Service db8eaa
	bindings.0.slave a
Packit Service db8eaa
	bindings.0.channel 0
Packit Service db8eaa
	bindings.1.slave a
Packit Service db8eaa
	bindings.1.channel 1
Packit Service db8eaa
	bindings.2.slave b
Packit Service db8eaa
	bindings.2.channel 0
Packit Service db8eaa
	bindings.3.slave b
Packit Service db8eaa
	bindings.3.channel 1
Packit Service db8eaa
}
Packit Service db8eaa
\endcode
Packit Service db8eaa
Note that the resultant pcm "quad" is not in the interleaved format
Packit Service db8eaa
but in the "complex" format.  Hence, it's not accessible by applications
Packit Service db8eaa
which can handle only the interleaved (or the non-interleaved) format.
Packit Service db8eaa
In such a case, wrap this PCM with \ref pcm_plugins_route "route" or
Packit Service db8eaa
\ref pcm_plugins_plug "plug" plugin.
Packit Service db8eaa
\code
Packit Service db8eaa
pcm.quad2 {
Packit Service db8eaa
	type route
Packit Service db8eaa
	slave.pcm "quad"
Packit Service db8eaa
	ttable.0.0 1
Packit Service db8eaa
	ttable.1.1 1
Packit Service db8eaa
	ttable.2.2 1
Packit Service db8eaa
	ttable.3.3 1
Packit Service db8eaa
}
Packit Service db8eaa
\endcode
Packit Service db8eaa
Packit Service db8eaa
\subsection pcm_plugins_multi_funcref Function reference
Packit Service db8eaa
Packit Service db8eaa
    Packit Service db8eaa
      
  • snd_pcm_multi_open()
  • Packit Service db8eaa
      
  • _snd_pcm_multi_open()
  • Packit Service db8eaa
    Packit Service db8eaa
    Packit Service db8eaa
    */
    Packit Service db8eaa
    Packit Service db8eaa
    /**
    Packit Service db8eaa
     * \brief Creates a new Multi 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 Multi 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_multi_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, inext, j, jnext;
    Packit Service db8eaa
    	snd_config_t *slaves = NULL;
    Packit Service db8eaa
    	snd_config_t *bindings = NULL;
    Packit Service db8eaa
    	int err;
    Packit Service db8eaa
    	unsigned int idx;
    Packit Service db8eaa
    	const char **slaves_id = NULL;
    Packit Service db8eaa
    	snd_config_t **slaves_conf = NULL;
    Packit Service db8eaa
    	snd_pcm_t **slaves_pcm = NULL;
    Packit Service db8eaa
    	unsigned int *slaves_channels = NULL;
    Packit Service db8eaa
    	int *channels_sidx = NULL;
    Packit Service db8eaa
    	unsigned int *channels_schannel = NULL;
    Packit Service db8eaa
    	unsigned int slaves_count = 0;
    Packit Service db8eaa
    	long master_slave = 0;
    Packit Service db8eaa
    	unsigned int channels_count = 0;
    Packit Service db8eaa
    	snd_config_for_each(i, inext, 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, "slaves") == 0) {
    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
    			slaves = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "bindings") == 0) {
    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
    			bindings = n;
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (strcmp(id, "master") == 0) {
    Packit Service db8eaa
    			if (snd_config_get_integer(n, &master_slave) < 0) {
    Packit Service db8eaa
    				SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    				return -EINVAL;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		SNDERR("Unknown field %s", id);
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (!slaves) {
    Packit Service db8eaa
    		SNDERR("slaves is not defined");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (!bindings) {
    Packit Service db8eaa
    		SNDERR("bindings is not defined");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	snd_config_for_each(i, inext, slaves) {
    Packit Service db8eaa
    		++slaves_count;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (master_slave < 0 || master_slave >= (long)slaves_count) {
    Packit Service db8eaa
    		SNDERR("Master slave is out of range (0-%u)\n", slaves_count-1);
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	snd_config_for_each(i, inext, bindings) {
    Packit Service db8eaa
    		long cchannel;
    Packit Service db8eaa
    		snd_config_t *m = snd_config_iterator_entry(i);
    Packit Service db8eaa
    		const char *id;
    Packit Service db8eaa
    		if (snd_config_get_id(m, &id) < 0)
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		err = safe_strtol(id, &cchannel);
    Packit Service db8eaa
    		if (err < 0 || cchannel < 0) {
    Packit Service db8eaa
    			SNDERR("Invalid channel number: %s", id);
    Packit Service db8eaa
    			return -EINVAL;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if ((unsigned long)cchannel >= channels_count)
    Packit Service db8eaa
    			channels_count = cchannel + 1;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (channels_count == 0) {
    Packit Service db8eaa
    		SNDERR("No channels defined");
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	slaves_id = calloc(slaves_count, sizeof(*slaves_id));
    Packit Service db8eaa
    	slaves_conf = calloc(slaves_count, sizeof(*slaves_conf));
    Packit Service db8eaa
    	slaves_pcm = calloc(slaves_count, sizeof(*slaves_pcm));
    Packit Service db8eaa
    	slaves_channels = calloc(slaves_count, sizeof(*slaves_channels));
    Packit Service db8eaa
    	channels_sidx = calloc(channels_count, sizeof(*channels_sidx));
    Packit Service db8eaa
    	channels_schannel = calloc(channels_count, sizeof(*channels_schannel));
    Packit Service db8eaa
    	if (!slaves_id || !slaves_conf || !slaves_pcm || !slaves_channels ||
    Packit Service db8eaa
    	    !channels_sidx || !channels_schannel) {
    Packit Service db8eaa
    		err = -ENOMEM;
    Packit Service db8eaa
    		goto _free;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	for (idx = 0; idx < channels_count; ++idx)
    Packit Service db8eaa
    		channels_sidx[idx] = -1;
    Packit Service db8eaa
    	idx = 0;
    Packit Service db8eaa
    	snd_config_for_each(i, inext, slaves) {
    Packit Service db8eaa
    		snd_config_t *m = snd_config_iterator_entry(i);
    Packit Service db8eaa
    		const char *id;
    Packit Service db8eaa
    		int channels;
    Packit Service db8eaa
    		if (snd_config_get_id(m, &id) < 0)
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		slaves_id[idx] = id;
    Packit Service db8eaa
    		err = snd_pcm_slave_conf(root, m, &slaves_conf[idx], 1,
    Packit Service db8eaa
    					 SND_PCM_HW_PARAM_CHANNELS, SCONF_MANDATORY, &channels);
    Packit Service db8eaa
    		if (err < 0)
    Packit Service db8eaa
    			goto _free;
    Packit Service db8eaa
    		slaves_channels[idx] = channels;
    Packit Service db8eaa
    		++idx;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    Packit Service db8eaa
    	snd_config_for_each(i, inext, bindings) {
    Packit Service db8eaa
    		snd_config_t *m = snd_config_iterator_entry(i);
    Packit Service db8eaa
    		long cchannel = -1;
    Packit Service db8eaa
    		long schannel = -1;
    Packit Service db8eaa
    		int slave = -1;
    Packit Service db8eaa
    		long val;
    Packit Service db8eaa
    		const char *str;
    Packit Service db8eaa
    		const char *id;
    Packit Service db8eaa
    		if (snd_config_get_id(m, &id) < 0)
    Packit Service db8eaa
    			continue;
    Packit Service db8eaa
    		err = safe_strtol(id, &cchannel);
    Packit Service db8eaa
    		if (err < 0 || cchannel < 0) {
    Packit Service db8eaa
    			SNDERR("Invalid channel number: %s", id);
    Packit Service db8eaa
    			err = -EINVAL;
    Packit Service db8eaa
    			goto _free;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		snd_config_for_each(j, jnext, m) {
    Packit Service db8eaa
    			snd_config_t *n = snd_config_iterator_entry(j);
    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 (strcmp(id, "comment") == 0)
    Packit Service db8eaa
    				continue;
    Packit Service db8eaa
    			if (strcmp(id, "slave") == 0) {
    Packit Service db8eaa
    				char buf[32];
    Packit Service db8eaa
    				unsigned int k;
    Packit Service db8eaa
    				err = snd_config_get_string(n, &str);
    Packit Service db8eaa
    				if (err < 0) {
    Packit Service db8eaa
    					err = snd_config_get_integer(n, &val;;
    Packit Service db8eaa
    					if (err < 0) {
    Packit Service db8eaa
    						SNDERR("Invalid value for %s", id);
    Packit Service db8eaa
    						goto _free;
    Packit Service db8eaa
    					}
    Packit Service db8eaa
    					sprintf(buf, "%ld", val);
    Packit Service db8eaa
    					str = buf;
    Packit Service db8eaa
    				}
    Packit Service db8eaa
    				for (k = 0; k < slaves_count; ++k) {
    Packit Service db8eaa
    					if (strcmp(slaves_id[k], str) == 0)
    Packit Service db8eaa
    						slave = k;
    Packit Service db8eaa
    				}
    Packit Service db8eaa
    				continue;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			if (strcmp(id, "channel") == 0) {
    Packit Service db8eaa
    				err = snd_config_get_integer(n, &schannel);
    Packit Service db8eaa
    				if (err < 0) {
    Packit Service db8eaa
    					SNDERR("Invalid type for %s", id);
    Packit Service db8eaa
    					goto _free;
    Packit Service db8eaa
    				}
    Packit Service db8eaa
    				continue;
    Packit Service db8eaa
    			}
    Packit Service db8eaa
    			SNDERR("Unknown field %s", id);
    Packit Service db8eaa
    			err = -EINVAL;
    Packit Service db8eaa
    			goto _free;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (slave < 0 || (unsigned int)slave >= slaves_count) {
    Packit Service db8eaa
    			SNDERR("Invalid or missing sidx for channel %s", id);
    Packit Service db8eaa
    			err = -EINVAL;
    Packit Service db8eaa
    			goto _free;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		if (schannel < 0 || 
    Packit Service db8eaa
    		    (unsigned int) schannel >= slaves_channels[slave]) {
    Packit Service db8eaa
    			SNDERR("Invalid or missing schannel for channel %s", id);
    Packit Service db8eaa
    			err = -EINVAL;
    Packit Service db8eaa
    			goto _free;
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		channels_sidx[cchannel] = slave;
    Packit Service db8eaa
    		channels_schannel[cchannel] = schannel;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	
    Packit Service db8eaa
    	for (idx = 0; idx < slaves_count; ++idx) {
    Packit Service db8eaa
    		err = snd_pcm_open_slave(&slaves_pcm[idx], root,
    Packit Service db8eaa
    					 slaves_conf[idx], stream, mode,
    Packit Service db8eaa
    					 conf);
    Packit Service db8eaa
    		if (err < 0)
    Packit Service db8eaa
    			goto _free;
    Packit Service db8eaa
    		snd_config_delete(slaves_conf[idx]);
    Packit Service db8eaa
    		slaves_conf[idx] = NULL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	err = snd_pcm_multi_open(pcmp, name, slaves_count, master_slave,
    Packit Service db8eaa
    				 slaves_pcm, slaves_channels,
    Packit Service db8eaa
    				 channels_count,
    Packit Service db8eaa
    				 channels_sidx, channels_schannel,
    Packit Service db8eaa
    				 1);
    Packit Service db8eaa
    _free:
    Packit Service db8eaa
    	if (err < 0) {
    Packit Service db8eaa
    		for (idx = 0; idx < slaves_count; ++idx) {
    Packit Service db8eaa
    			if (slaves_pcm[idx])
    Packit Service db8eaa
    				snd_pcm_close(slaves_pcm[idx]);
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	if (slaves_conf) {
    Packit Service db8eaa
    		for (idx = 0; idx < slaves_count; ++idx) {
    Packit Service db8eaa
    			if (slaves_conf[idx])
    Packit Service db8eaa
    				snd_config_delete(slaves_conf[idx]);
    Packit Service db8eaa
    		}
    Packit Service db8eaa
    		free(slaves_conf);
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    	free(slaves_pcm);
    Packit Service db8eaa
    	free(slaves_channels);
    Packit Service db8eaa
    	free(channels_sidx);
    Packit Service db8eaa
    	free(channels_schannel);
    Packit Service db8eaa
    	free(slaves_id);
    Packit Service db8eaa
    	return err;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    #ifndef DOC_HIDDEN
    Packit Service db8eaa
    SND_DLSYM_BUILD_VERSION(_snd_pcm_multi_open, SND_PCM_DLSYM_VERSION);
    Packit Service db8eaa
    #endif