Blame src/pcm/pcm_dmix.c

Packit Service db8eaa
/**
Packit Service db8eaa
 * \file pcm/pcm_dmix.c
Packit Service db8eaa
 * \ingroup PCM_Plugins
Packit Service db8eaa
 * \brief PCM Direct Stream Mixing (dmix) Plugin Interface
Packit Service db8eaa
 * \author Jaroslav Kysela <perex@perex.cz>
Packit Service db8eaa
 * \date 2003
Packit Service db8eaa
 */
Packit Service db8eaa
/*
Packit Service db8eaa
 *  PCM - Direct Stream Mixing
Packit Service db8eaa
 *  Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
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 <stddef.h>
Packit Service db8eaa
#include <unistd.h>
Packit Service db8eaa
#include <signal.h>
Packit Service db8eaa
#include <string.h>
Packit Service db8eaa
#include <fcntl.h>
Packit Service db8eaa
#include <ctype.h>
Packit Service db8eaa
#include <grp.h>
Packit Service db8eaa
#include <sys/ioctl.h>
Packit Service db8eaa
#include <sys/mman.h>
Packit Service db8eaa
#include <sys/shm.h>
Packit Service db8eaa
#include <sys/sem.h>
Packit Service db8eaa
#include <sys/wait.h>
Packit Service db8eaa
#include <sys/socket.h>
Packit Service db8eaa
#include <sys/un.h>
Packit Service db8eaa
#include <sys/mman.h>
Packit Service db8eaa
#include "pcm_direct.h"
Packit Service db8eaa
Packit Service db8eaa
#ifndef PIC
Packit Service db8eaa
/* entry for static linking */
Packit Service db8eaa
const char *_snd_module_pcm_dmix = "";
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
#ifndef DOC_HIDDEN
Packit Service db8eaa
/* start is pending - this state happens when rate plugin does a delayed commit */
Packit Service db8eaa
#define STATE_RUN_PENDING	1024
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 *
Packit Service db8eaa
 */
Packit Service db8eaa
Packit Service db8eaa
static int shm_sum_discard(snd_pcm_direct_t *dmix);
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 *  sum ring buffer shared memory area 
Packit Service db8eaa
 */
Packit Service db8eaa
static int shm_sum_create_or_connect(snd_pcm_direct_t *dmix)
Packit Service db8eaa
{
Packit Service db8eaa
	struct shmid_ds buf;
Packit Service db8eaa
	int tmpid, err;
Packit Service db8eaa
	size_t size;
Packit Service db8eaa
Packit Service db8eaa
	size = dmix->shmptr->s.channels *
Packit Service db8eaa
	       dmix->shmptr->s.buffer_size *
Packit Service db8eaa
	       sizeof(signed int);	
Packit Service db8eaa
retryshm:
Packit Service db8eaa
	dmix->u.dmix.shmid_sum = shmget(dmix->ipc_key + 1, size,
Packit Service db8eaa
					IPC_CREAT | dmix->ipc_perm);
Packit Service db8eaa
	err = -errno;
Packit Service db8eaa
	if (dmix->u.dmix.shmid_sum < 0) {
Packit Service db8eaa
		if (errno == EINVAL)
Packit Service db8eaa
		if ((tmpid = shmget(dmix->ipc_key + 1, 0, dmix->ipc_perm)) != -1)
Packit Service db8eaa
		if (!shmctl(tmpid, IPC_STAT, &buf))
Packit Service db8eaa
	    	if (!buf.shm_nattch) 
Packit Service db8eaa
		/* no users so destroy the segment */
Packit Service db8eaa
		if (!shmctl(tmpid, IPC_RMID, NULL))
Packit Service db8eaa
		    goto retryshm;
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (shmctl(dmix->u.dmix.shmid_sum, IPC_STAT, &buf) < 0) {
Packit Service db8eaa
		err = -errno;
Packit Service db8eaa
		shm_sum_discard(dmix);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (dmix->ipc_gid >= 0) {
Packit Service db8eaa
		buf.shm_perm.gid = dmix->ipc_gid;
Packit Service db8eaa
		shmctl(dmix->u.dmix.shmid_sum, IPC_SET, &buf;; 
Packit Service db8eaa
	}
Packit Service db8eaa
	dmix->u.dmix.sum_buffer = shmat(dmix->u.dmix.shmid_sum, 0, 0);
Packit Service db8eaa
	if (dmix->u.dmix.sum_buffer == (void *) -1) {
Packit Service db8eaa
		err = -errno;
Packit Service db8eaa
		shm_sum_discard(dmix);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	mlock(dmix->u.dmix.sum_buffer, size);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int shm_sum_discard(snd_pcm_direct_t *dmix)
Packit Service db8eaa
{
Packit Service db8eaa
	struct shmid_ds buf;
Packit Service db8eaa
	int ret = 0;
Packit Service db8eaa
Packit Service db8eaa
	if (dmix->u.dmix.shmid_sum < 0)
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	if (dmix->u.dmix.sum_buffer != (void *) -1 && shmdt(dmix->u.dmix.sum_buffer) < 0)
Packit Service db8eaa
		return -errno;
Packit Service db8eaa
	dmix->u.dmix.sum_buffer = (void *) -1;
Packit Service db8eaa
	if (shmctl(dmix->u.dmix.shmid_sum, IPC_STAT, &buf) < 0)
Packit Service db8eaa
		return -errno;
Packit Service db8eaa
	if (buf.shm_nattch == 0) {	/* we're the last user, destroy the segment */
Packit Service db8eaa
		if (shmctl(dmix->u.dmix.shmid_sum, IPC_RMID, NULL) < 0)
Packit Service db8eaa
			return -errno;
Packit Service db8eaa
		ret = 1;
Packit Service db8eaa
	}
Packit Service db8eaa
	dmix->u.dmix.shmid_sum = -1;
Packit Service db8eaa
	return ret;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void dmix_server_free(snd_pcm_direct_t *dmix)
Packit Service db8eaa
{
Packit Service db8eaa
	/* remove the memory region */
Packit Service db8eaa
	shm_sum_create_or_connect(dmix);
Packit Service db8eaa
	shm_sum_discard(dmix);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 *  the main function of this plugin: mixing
Packit Service db8eaa
 *  FIXME: optimize it for different architectures
Packit Service db8eaa
 */
Packit Service db8eaa
Packit Service db8eaa
#include "pcm_dmix_generic.c"
Packit Service db8eaa
#if defined(__i386__)
Packit Service db8eaa
#include "pcm_dmix_i386.c"
Packit Service db8eaa
#elif defined(__x86_64__)
Packit Service db8eaa
#include "pcm_dmix_x86_64.c"
Packit Service db8eaa
#else
Packit Service db8eaa
#ifndef DOC_HIDDEN
Packit Service db8eaa
#define mix_select_callbacks(x)	generic_mix_select_callbacks(x)
Packit Service db8eaa
#define dmix_supported_format generic_dmix_supported_format
Packit Service db8eaa
#endif
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
static void mix_areas(snd_pcm_direct_t *dmix,
Packit Service db8eaa
		      const snd_pcm_channel_area_t *src_areas,
Packit Service db8eaa
		      const snd_pcm_channel_area_t *dst_areas,
Packit Service db8eaa
		      snd_pcm_uframes_t src_ofs,
Packit Service db8eaa
		      snd_pcm_uframes_t dst_ofs,
Packit Service db8eaa
		      snd_pcm_uframes_t size)
Packit Service db8eaa
{
Packit Service db8eaa
	unsigned int src_step, dst_step;
Packit Service db8eaa
	unsigned int chn, dchn, channels, sample_size;
Packit Service db8eaa
	mix_areas_t *do_mix_areas;
Packit Service db8eaa
	
Packit Service db8eaa
	channels = dmix->channels;
Packit Service db8eaa
	switch (dmix->shmptr->s.format) {
Packit Service db8eaa
	case SND_PCM_FORMAT_S16_LE:
Packit Service db8eaa
	case SND_PCM_FORMAT_S16_BE:
Packit Service db8eaa
		sample_size = 2;
Packit Service db8eaa
		do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_16;
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_S32_LE:
Packit Service db8eaa
	case SND_PCM_FORMAT_S32_BE:
Packit Service db8eaa
		sample_size = 4;
Packit Service db8eaa
		do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_32;
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_S24_LE:
Packit Service db8eaa
		sample_size = 4;
Packit Service db8eaa
		do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_24;
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_S24_3LE:
Packit Service db8eaa
		sample_size = 3;
Packit Service db8eaa
		do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_24;
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_U8:
Packit Service db8eaa
		sample_size = 1;
Packit Service db8eaa
		do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_u8;
Packit Service db8eaa
		break;
Packit Service db8eaa
	default:
Packit Service db8eaa
		return;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (dmix->interleaved) {
Packit Service db8eaa
		/*
Packit Service db8eaa
		 * process all areas in one loop
Packit Service db8eaa
		 * it optimizes the memory accesses for this case
Packit Service db8eaa
		 */
Packit Service db8eaa
		do_mix_areas(size * channels,
Packit Service db8eaa
			     (unsigned char *)dst_areas[0].addr + sample_size * dst_ofs * channels,
Packit Service db8eaa
			     (unsigned char *)src_areas[0].addr + sample_size * src_ofs * channels,
Packit Service db8eaa
			     dmix->u.dmix.sum_buffer + dst_ofs * channels,
Packit Service db8eaa
			     sample_size,
Packit Service db8eaa
			     sample_size,
Packit Service db8eaa
			     sizeof(signed int));
Packit Service db8eaa
		return;
Packit Service db8eaa
	}
Packit Service db8eaa
	for (chn = 0; chn < channels; chn++) {
Packit Service db8eaa
		dchn = dmix->bindings ? dmix->bindings[chn] : chn;
Packit Service db8eaa
		if (dchn >= dmix->shmptr->s.channels)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		src_step = src_areas[chn].step / 8;
Packit Service db8eaa
		dst_step = dst_areas[dchn].step / 8;
Packit Service db8eaa
		do_mix_areas(size,
Packit Service db8eaa
			     ((unsigned char *)dst_areas[dchn].addr + dst_areas[dchn].first / 8) + dst_ofs * dst_step,
Packit Service db8eaa
			     ((unsigned char *)src_areas[chn].addr + src_areas[chn].first / 8) + src_ofs * src_step,
Packit Service db8eaa
			     dmix->u.dmix.sum_buffer + dmix->shmptr->s.channels * dst_ofs + dchn,
Packit Service db8eaa
			     dst_step,
Packit Service db8eaa
			     src_step,
Packit Service db8eaa
			     dmix->shmptr->s.channels * sizeof(signed int));
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void remix_areas(snd_pcm_direct_t *dmix,
Packit Service db8eaa
			const snd_pcm_channel_area_t *src_areas,
Packit Service db8eaa
			const snd_pcm_channel_area_t *dst_areas,
Packit Service db8eaa
			snd_pcm_uframes_t src_ofs,
Packit Service db8eaa
			snd_pcm_uframes_t dst_ofs,
Packit Service db8eaa
			snd_pcm_uframes_t size)
Packit Service db8eaa
{
Packit Service db8eaa
	unsigned int src_step, dst_step;
Packit Service db8eaa
	unsigned int chn, dchn, channels, sample_size;
Packit Service db8eaa
	mix_areas_t *do_remix_areas;
Packit Service db8eaa
	
Packit Service db8eaa
	channels = dmix->channels;
Packit Service db8eaa
	switch (dmix->shmptr->s.format) {
Packit Service db8eaa
	case SND_PCM_FORMAT_S16_LE:
Packit Service db8eaa
	case SND_PCM_FORMAT_S16_BE:
Packit Service db8eaa
		sample_size = 2;
Packit Service db8eaa
		do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_16;
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_S32_LE:
Packit Service db8eaa
	case SND_PCM_FORMAT_S32_BE:
Packit Service db8eaa
		sample_size = 4;
Packit Service db8eaa
		do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_32;
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_S24_LE:
Packit Service db8eaa
		sample_size = 4;
Packit Service db8eaa
		do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_24;
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_S24_3LE:
Packit Service db8eaa
		sample_size = 3;
Packit Service db8eaa
		do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_24;
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_FORMAT_U8:
Packit Service db8eaa
		sample_size = 1;
Packit Service db8eaa
		do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_u8;
Packit Service db8eaa
		break;
Packit Service db8eaa
	default:
Packit Service db8eaa
		return;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (dmix->interleaved) {
Packit Service db8eaa
		/*
Packit Service db8eaa
		 * process all areas in one loop
Packit Service db8eaa
		 * it optimizes the memory accesses for this case
Packit Service db8eaa
		 */
Packit Service db8eaa
		do_remix_areas(size * channels,
Packit Service db8eaa
			       (unsigned char *)dst_areas[0].addr + sample_size * dst_ofs * channels,
Packit Service db8eaa
			       (unsigned char *)src_areas[0].addr + sample_size * src_ofs * channels,
Packit Service db8eaa
			       dmix->u.dmix.sum_buffer + dst_ofs * channels,
Packit Service db8eaa
			       sample_size,
Packit Service db8eaa
			       sample_size,
Packit Service db8eaa
			       sizeof(signed int));
Packit Service db8eaa
		return;
Packit Service db8eaa
	}
Packit Service db8eaa
	for (chn = 0; chn < channels; chn++) {
Packit Service db8eaa
		dchn = dmix->bindings ? dmix->bindings[chn] : chn;
Packit Service db8eaa
		if (dchn >= dmix->shmptr->s.channels)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		src_step = src_areas[chn].step / 8;
Packit Service db8eaa
		dst_step = dst_areas[dchn].step / 8;
Packit Service db8eaa
		do_remix_areas(size,
Packit Service db8eaa
			       ((unsigned char *)dst_areas[dchn].addr + dst_areas[dchn].first / 8) + dst_ofs * dst_step,
Packit Service db8eaa
			       ((unsigned char *)src_areas[chn].addr + src_areas[chn].first / 8) + src_ofs * src_step,
Packit Service db8eaa
			       dmix->u.dmix.sum_buffer + dmix->shmptr->s.channels * dst_ofs + dchn,
Packit Service db8eaa
			       dst_step,
Packit Service db8eaa
			       src_step,
Packit Service db8eaa
			       dmix->shmptr->s.channels * sizeof(signed int));
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * if no concurrent access is allowed in the mixing routines, we need to protect
Packit Service db8eaa
 * the area via semaphore
Packit Service db8eaa
 */
Packit Service db8eaa
#ifndef DOC_HIDDEN
Packit Service db8eaa
static void dmix_down_sem(snd_pcm_direct_t *dmix)
Packit Service db8eaa
{
Packit Service db8eaa
	if (dmix->u.dmix.use_sem)
Packit Service db8eaa
		snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static void dmix_up_sem(snd_pcm_direct_t *dmix)
Packit Service db8eaa
{
Packit Service db8eaa
	if (dmix->u.dmix.use_sem)
Packit Service db8eaa
		snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
Packit Service db8eaa
}
Packit Service db8eaa
#endif
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 *  synchronize shm ring buffer with hardware
Packit Service db8eaa
 */
Packit Service db8eaa
static void snd_pcm_dmix_sync_area(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	snd_pcm_uframes_t slave_hw_ptr, slave_appl_ptr, slave_size;
Packit Service db8eaa
	snd_pcm_uframes_t appl_ptr, size, transfer;
Packit Service db8eaa
	const snd_pcm_channel_area_t *src_areas, *dst_areas;
Packit Service db8eaa
	
Packit Service db8eaa
	/* calculate the size to transfer */
Packit Service db8eaa
	/* check the available size in the local buffer
Packit Service db8eaa
	 * last_appl_ptr keeps the last updated position
Packit Service db8eaa
	 */
Packit Service db8eaa
	size = pcm_frame_diff2(dmix->appl_ptr, dmix->last_appl_ptr, pcm->boundary);
Packit Service db8eaa
	if (! size)
Packit Service db8eaa
		return;
Packit Service db8eaa
Packit Service db8eaa
	/* the slave_app_ptr can be far behind the slave_hw_ptr */
Packit Service db8eaa
	/* reduce mixing and errors here - just skip not catched writes */
Packit Service db8eaa
	slave_size = pcm_frame_diff(dmix->slave_appl_ptr, dmix->slave_hw_ptr, dmix->slave_boundary);
Packit Service db8eaa
	if (slave_size > dmix->slave_buffer_size) {
Packit Service db8eaa
		transfer = dmix->slave_buffer_size - slave_size;
Packit Service db8eaa
		if (transfer > size)
Packit Service db8eaa
			transfer = size;
Packit Service db8eaa
		dmix->last_appl_ptr += transfer;
Packit Service db8eaa
		dmix->last_appl_ptr %= pcm->boundary;
Packit Service db8eaa
		dmix->slave_appl_ptr += transfer;
Packit Service db8eaa
		dmix->slave_appl_ptr %= dmix->slave_boundary;
Packit Service db8eaa
		size = pcm_frame_diff2(dmix->appl_ptr, dmix->last_appl_ptr, pcm->boundary);
Packit Service db8eaa
		if (! size)
Packit Service db8eaa
			return;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* check the available size in the slave PCM buffer */
Packit Service db8eaa
	slave_hw_ptr = dmix->slave_hw_ptr;
Packit Service db8eaa
	/* don't write on the last active period - this area may be cleared
Packit Service db8eaa
	 * by the driver during mix operation...
Packit Service db8eaa
	 */
Packit Service db8eaa
	slave_hw_ptr -= slave_hw_ptr % dmix->slave_period_size;
Packit Service db8eaa
	slave_hw_ptr += dmix->slave_buffer_size;
Packit Service db8eaa
	if (slave_hw_ptr >= dmix->slave_boundary)
Packit Service db8eaa
		slave_hw_ptr -= dmix->slave_boundary;
Packit Service db8eaa
	slave_size = pcm_frame_diff(slave_hw_ptr, dmix->slave_appl_ptr, dmix->slave_boundary);
Packit Service db8eaa
	if (slave_size < size)
Packit Service db8eaa
		size = slave_size;
Packit Service db8eaa
	if (! size)
Packit Service db8eaa
		return;
Packit Service db8eaa
Packit Service db8eaa
	/* add sample areas here */
Packit Service db8eaa
	src_areas = snd_pcm_mmap_areas(pcm);
Packit Service db8eaa
	dst_areas = snd_pcm_mmap_areas(dmix->spcm);
Packit Service db8eaa
	appl_ptr = dmix->last_appl_ptr % pcm->buffer_size;
Packit Service db8eaa
	dmix->last_appl_ptr += size;
Packit Service db8eaa
	dmix->last_appl_ptr %= pcm->boundary;
Packit Service db8eaa
	slave_appl_ptr = dmix->slave_appl_ptr % dmix->slave_buffer_size;
Packit Service db8eaa
	dmix->slave_appl_ptr += size;
Packit Service db8eaa
	dmix->slave_appl_ptr %= dmix->slave_boundary;
Packit Service db8eaa
	dmix_down_sem(dmix);
Packit Service db8eaa
	for (;;) {
Packit Service db8eaa
		transfer = size;
Packit Service db8eaa
		if (appl_ptr + transfer > pcm->buffer_size)
Packit Service db8eaa
			transfer = pcm->buffer_size - appl_ptr;
Packit Service db8eaa
		if (slave_appl_ptr + transfer > dmix->slave_buffer_size)
Packit Service db8eaa
			transfer = dmix->slave_buffer_size - slave_appl_ptr;
Packit Service db8eaa
		mix_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer);
Packit Service db8eaa
		size -= transfer;
Packit Service db8eaa
		if (! size)
Packit Service db8eaa
			break;
Packit Service db8eaa
		slave_appl_ptr += transfer;
Packit Service db8eaa
		slave_appl_ptr %= dmix->slave_buffer_size;
Packit Service db8eaa
		appl_ptr += transfer;
Packit Service db8eaa
		appl_ptr %= pcm->buffer_size;
Packit Service db8eaa
	}
Packit Service db8eaa
	dmix_up_sem(dmix);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 *  synchronize hardware pointer (hw_ptr) with ours
Packit Service db8eaa
 */
Packit Service db8eaa
static int snd_pcm_dmix_sync_ptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	snd_pcm_uframes_t old_slave_hw_ptr, avail;
Packit Service db8eaa
	snd_pcm_sframes_t diff;
Packit Service db8eaa
	
Packit Service db8eaa
	old_slave_hw_ptr = dmix->slave_hw_ptr;
Packit Service db8eaa
	dmix->slave_hw_ptr = slave_hw_ptr;
Packit Service db8eaa
	diff = slave_hw_ptr - old_slave_hw_ptr;
Packit Service db8eaa
	if (diff == 0)		/* fast path */
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	if (dmix->state != SND_PCM_STATE_RUNNING &&
Packit Service db8eaa
	    dmix->state != SND_PCM_STATE_DRAINING)
Packit Service db8eaa
		/* not really started yet - don't update hw_ptr */
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	if (diff < 0) {
Packit Service db8eaa
		slave_hw_ptr += dmix->slave_boundary;
Packit Service db8eaa
		diff = slave_hw_ptr - old_slave_hw_ptr;
Packit Service db8eaa
	}
Packit Service db8eaa
	dmix->hw_ptr += diff;
Packit Service db8eaa
	dmix->hw_ptr %= pcm->boundary;
Packit Service db8eaa
	if (pcm->stop_threshold >= pcm->boundary)	/* don't care */
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	avail = snd_pcm_mmap_playback_avail(pcm);
Packit Service db8eaa
	if (avail > dmix->avail_max)
Packit Service db8eaa
		dmix->avail_max = avail;
Packit Service db8eaa
	if (avail >= pcm->stop_threshold) {
Packit Service db8eaa
		snd_timer_stop(dmix->timer);
Packit Service db8eaa
		gettimestamp(&dmix->trigger_tstamp, pcm->tstamp_type);
Packit Service db8eaa
		if (dmix->state == SND_PCM_STATE_RUNNING) {
Packit Service db8eaa
			dmix->state = SND_PCM_STATE_XRUN;
Packit Service db8eaa
			return -EPIPE;
Packit Service db8eaa
		}
Packit Service db8eaa
		dmix->state = SND_PCM_STATE_SETUP;
Packit Service db8eaa
		/* clear queue to remove pending poll events */
Packit Service db8eaa
		snd_pcm_direct_clear_timer_queue(dmix);
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	switch (snd_pcm_state(dmix->spcm)) {
Packit Service db8eaa
	case SND_PCM_STATE_DISCONNECTED:
Packit Service db8eaa
		dmix->state = SND_PCM_STATE_DISCONNECTED;
Packit Service db8eaa
		return -ENODEV;
Packit Service db8eaa
	case SND_PCM_STATE_XRUN:
Packit Service db8eaa
		if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		break;
Packit Service db8eaa
	default:
Packit Service db8eaa
		break;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (snd_pcm_direct_client_chk_xrun(dmix, pcm))
Packit Service db8eaa
		return -EPIPE;
Packit Service db8eaa
	if (dmix->slowptr)
Packit Service db8eaa
		snd_pcm_hwsync(dmix->spcm);
Packit Service db8eaa
Packit Service db8eaa
	return snd_pcm_dmix_sync_ptr0(pcm, *dmix->spcm->hw.ptr);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 *  plugin implementation
Packit Service db8eaa
 */
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_state_t snd_pcm_dmix_state(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	snd_pcm_state_t state;
Packit Service db8eaa
	state = snd_pcm_state(dmix->spcm);
Packit Service db8eaa
	switch (state) {
Packit Service db8eaa
	case SND_PCM_STATE_SUSPENDED:
Packit Service db8eaa
	case SND_PCM_STATE_DISCONNECTED:
Packit Service db8eaa
		dmix->state = state;
Packit Service db8eaa
		return state;
Packit Service db8eaa
	case SND_PCM_STATE_XRUN:
Packit Service db8eaa
		if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		break;
Packit Service db8eaa
	default:
Packit Service db8eaa
		break;
Packit Service db8eaa
	}
Packit Service db8eaa
	snd_pcm_direct_client_chk_xrun(dmix, pcm);
Packit Service db8eaa
	if (dmix->state == STATE_RUN_PENDING)
Packit Service db8eaa
		return SNDRV_PCM_STATE_RUNNING;
Packit Service db8eaa
	return dmix->state;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_dmix_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
Packit Service db8eaa
	memset(status, 0, sizeof(*status));
Packit Service db8eaa
	snd_pcm_status(dmix->spcm, status);
Packit Service db8eaa
Packit Service db8eaa
	switch (dmix->state) {
Packit Service db8eaa
	case SNDRV_PCM_STATE_DRAINING:
Packit Service db8eaa
	case SNDRV_PCM_STATE_RUNNING:
Packit Service db8eaa
		snd_pcm_dmix_sync_ptr0(pcm, status->hw_ptr);
Packit Service 01c0b7
		status->delay = snd_pcm_mmap_playback_delay(pcm);
Packit Service db8eaa
		break;
Packit Service db8eaa
	default:
Packit Service db8eaa
		break;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	status->state = snd_pcm_dmix_state(pcm);
Packit Service 01c0b7
	status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */
Packit Service db8eaa
	status->trigger_tstamp = dmix->trigger_tstamp;
Packit Service db8eaa
	status->avail = snd_pcm_mmap_playback_avail(pcm);
Packit Service db8eaa
	status->avail_max = status->avail > dmix->avail_max ? status->avail : dmix->avail_max;
Packit Service db8eaa
	dmix->avail_max = 0;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_dmix_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	
Packit Service db8eaa
	switch(dmix->state) {
Packit Service db8eaa
	case SNDRV_PCM_STATE_DRAINING:
Packit Service db8eaa
	case SNDRV_PCM_STATE_RUNNING:
Packit Service db8eaa
		err = snd_pcm_dmix_sync_ptr(pcm);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		/* fallthru */
Packit Service db8eaa
	case SNDRV_PCM_STATE_PREPARED:
Packit Service db8eaa
	case SNDRV_PCM_STATE_SUSPENDED:
Packit Service db8eaa
	case STATE_RUN_PENDING:
Packit Service 01c0b7
		*delayp = snd_pcm_mmap_playback_delay(pcm);
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	case SNDRV_PCM_STATE_XRUN:
Packit Service db8eaa
		return -EPIPE;
Packit Service db8eaa
	case SNDRV_PCM_STATE_DISCONNECTED:
Packit Service db8eaa
		return -ENODEV;
Packit Service db8eaa
	default:
Packit Service db8eaa
		return -EBADFD;
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_dmix_hwsync(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
Packit Service db8eaa
	switch(dmix->state) {
Packit Service db8eaa
	case SNDRV_PCM_STATE_DRAINING:
Packit Service db8eaa
	case SNDRV_PCM_STATE_RUNNING:
Packit Service db8eaa
		/* sync slave PCM */
Packit Service db8eaa
		return snd_pcm_dmix_sync_ptr(pcm);
Packit Service db8eaa
	case SNDRV_PCM_STATE_PREPARED:
Packit Service db8eaa
	case SNDRV_PCM_STATE_SUSPENDED:
Packit Service db8eaa
	case STATE_RUN_PENDING:
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	case SNDRV_PCM_STATE_XRUN:
Packit Service db8eaa
		return -EPIPE;
Packit Service db8eaa
	case SNDRV_PCM_STATE_DISCONNECTED:
Packit Service db8eaa
		return -ENODEV;
Packit Service db8eaa
	default:
Packit Service db8eaa
		return -EBADFD;
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_dmix_reset(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	dmix->hw_ptr %= pcm->period_size;
Packit Service db8eaa
	dmix->appl_ptr = dmix->last_appl_ptr = dmix->hw_ptr;
Packit Service db8eaa
	dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
Packit Service db8eaa
	snd_pcm_direct_reset_slave_ptr(pcm, dmix);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_dmix_start_timer(snd_pcm_t *pcm, snd_pcm_direct_t *dmix)
Packit Service db8eaa
{
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	snd_pcm_hwsync(dmix->spcm);
Packit Service db8eaa
	dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
Packit Service db8eaa
	snd_pcm_direct_reset_slave_ptr(pcm, dmix);
Packit Service db8eaa
	err = snd_timer_start(dmix->timer);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	dmix->state = SND_PCM_STATE_RUNNING;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_dmix_start(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	snd_pcm_sframes_t avail;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	
Packit Service db8eaa
	if (dmix->state != SND_PCM_STATE_PREPARED)
Packit Service db8eaa
		return -EBADFD;
Packit Service db8eaa
	avail = snd_pcm_mmap_playback_hw_avail(pcm);
Packit Service db8eaa
	if (avail == 0)
Packit Service db8eaa
		dmix->state = STATE_RUN_PENDING;
Packit Service db8eaa
	else if (avail < 0)
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	else {
Packit Service db8eaa
		if ((err = snd_pcm_dmix_start_timer(pcm, dmix)) < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		snd_pcm_dmix_sync_area(pcm);
Packit Service db8eaa
	}
Packit Service db8eaa
	gettimestamp(&dmix->trigger_tstamp, pcm->tstamp_type);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_dmix_drop(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	if (dmix->state == SND_PCM_STATE_OPEN)
Packit Service db8eaa
		return -EBADFD;
Packit Service db8eaa
	dmix->state = SND_PCM_STATE_SETUP;
Packit Service db8eaa
	snd_pcm_direct_timer_stop(dmix);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* locked version */
Packit Service db8eaa
static int __snd_pcm_dmix_drain(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	snd_pcm_uframes_t stop_threshold;
Packit Service db8eaa
	int err = 0;
Packit Service db8eaa
Packit Service db8eaa
	switch (snd_pcm_state(dmix->spcm)) {
Packit Service db8eaa
	case SND_PCM_STATE_SUSPENDED:
Packit Service db8eaa
		return -ESTRPIPE;
Packit Service db8eaa
	default:
Packit Service db8eaa
		break;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if (dmix->state == SND_PCM_STATE_OPEN)
Packit Service db8eaa
		return -EBADFD;
Packit Service db8eaa
	if (dmix->state == SND_PCM_STATE_PREPARED) {
Packit Service db8eaa
		if (snd_pcm_mmap_playback_hw_avail(pcm) > 0)
Packit Service db8eaa
			snd_pcm_dmix_start(pcm);
Packit Service db8eaa
		else {
Packit Service db8eaa
			snd_pcm_dmix_drop(pcm);
Packit Service db8eaa
			return 0;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if (dmix->state == SND_PCM_STATE_XRUN) {
Packit Service db8eaa
		snd_pcm_dmix_drop(pcm);
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	stop_threshold = pcm->stop_threshold;
Packit Service db8eaa
	if (pcm->stop_threshold > pcm->buffer_size)
Packit Service db8eaa
		pcm->stop_threshold = pcm->buffer_size;
Packit Service db8eaa
	dmix->state = SND_PCM_STATE_DRAINING;
Packit Service db8eaa
	do {
Packit Service db8eaa
		err = snd_pcm_dmix_sync_ptr(pcm);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			snd_pcm_dmix_drop(pcm);
Packit Service db8eaa
			goto done;
Packit Service db8eaa
		}
Packit Service db8eaa
		if (dmix->state == SND_PCM_STATE_DRAINING) {
Packit Service db8eaa
			snd_pcm_dmix_sync_area(pcm);
Packit Service db8eaa
			if ((pcm->mode & SND_PCM_NONBLOCK) == 0) {
Packit Service db8eaa
				snd_pcm_wait_nocheck(pcm, -1);
Packit Service db8eaa
				snd_pcm_direct_clear_timer_queue(dmix); /* force poll to wait */
Packit Service db8eaa
			}
Packit Service db8eaa
Packit Service db8eaa
			switch (snd_pcm_state(dmix->spcm)) {
Packit Service db8eaa
			case SND_PCM_STATE_SUSPENDED:
Packit Service db8eaa
				err = -ESTRPIPE;
Packit Service db8eaa
				goto done;
Packit Service db8eaa
			default:
Packit Service db8eaa
				break;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
		if (pcm->mode & SND_PCM_NONBLOCK) {
Packit Service db8eaa
			if (dmix->state == SND_PCM_STATE_DRAINING) {
Packit Service db8eaa
				err = -EAGAIN;
Packit Service db8eaa
				goto done;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
	} while (dmix->state == SND_PCM_STATE_DRAINING);
Packit Service db8eaa
done:
Packit Service db8eaa
	pcm->stop_threshold = stop_threshold;
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_dmix_drain(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	snd_pcm_lock(pcm);
Packit Service db8eaa
	err = __snd_pcm_dmix_drain(pcm);
Packit Service db8eaa
	snd_pcm_unlock(pcm);
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_dmix_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -EIO;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_dmix_rewindable(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	return snd_pcm_mmap_playback_hw_rewindable(pcm);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_dmix_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	snd_pcm_uframes_t slave_appl_ptr, slave_size;
Packit Service db8eaa
	snd_pcm_uframes_t appl_ptr, size, transfer, result, frames_to_remix;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	const snd_pcm_channel_area_t *src_areas, *dst_areas;
Packit Service db8eaa
Packit Service db8eaa
	if (dmix->state == SND_PCM_STATE_RUNNING ||
Packit Service db8eaa
	    dmix->state == SND_PCM_STATE_DRAINING) {
Packit Service db8eaa
		err = snd_pcm_dmix_hwsync(pcm);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* (appl_ptr - last_appl_ptr) indicates the frames which are not
Packit Service db8eaa
	 * already mixed
Packit Service db8eaa
	 * (last_appl_ptr - hw_ptr)  indicates the frames which are already
Packit Service db8eaa
	 * mixed but not played yet.
Packit Service db8eaa
	 * So they can be remixed.
Packit Service db8eaa
	 */
Packit Service db8eaa
Packit Service db8eaa
	size = pcm_frame_diff(dmix->last_appl_ptr, dmix->appl_ptr, pcm->boundary);
Packit Service db8eaa
	if (frames < size)
Packit Service db8eaa
		size = frames;
Packit Service db8eaa
	snd_pcm_mmap_appl_backward(pcm, size);
Packit Service db8eaa
	frames -= size;
Packit Service db8eaa
	if (!frames)
Packit Service db8eaa
		return size;
Packit Service db8eaa
	result = size;
Packit Service db8eaa
Packit Service db8eaa
	/* Always at this point last_appl_ptr == appl_ptr
Packit Service db8eaa
	 * So (appl_ptr - hw_ptr) indicates the frames which can be remixed
Packit Service db8eaa
	 */
Packit Service db8eaa
	size = pcm_frame_diff(dmix->appl_ptr, dmix->hw_ptr, pcm->boundary);
Packit Service db8eaa
	if (size > frames)
Packit Service db8eaa
		size = frames;
Packit Service db8eaa
	slave_size = pcm_frame_diff(dmix->slave_appl_ptr, dmix->slave_hw_ptr, pcm->boundary);
Packit Service db8eaa
	if (slave_size < size)
Packit Service db8eaa
		size = slave_size;
Packit Service db8eaa
Packit Service db8eaa
	/* frames which should be remixed will be saved
Packit Service db8eaa
	 * to also backward the appl pointer on success
Packit Service db8eaa
	 */
Packit Service db8eaa
	frames_to_remix = size;
Packit Service db8eaa
Packit Service db8eaa
	/* add sample areas here */
Packit Service db8eaa
	src_areas = snd_pcm_mmap_areas(pcm);
Packit Service db8eaa
	dst_areas = snd_pcm_mmap_areas(dmix->spcm);
Packit Service db8eaa
	dmix->last_appl_ptr -= size;
Packit Service db8eaa
	dmix->last_appl_ptr %= pcm->boundary;
Packit Service db8eaa
	appl_ptr = dmix->last_appl_ptr % pcm->buffer_size;
Packit Service db8eaa
	dmix->slave_appl_ptr -= size;
Packit Service db8eaa
	dmix->slave_appl_ptr %= dmix->slave_boundary;
Packit Service db8eaa
	slave_appl_ptr = dmix->slave_appl_ptr % dmix->slave_buffer_size;
Packit Service db8eaa
	dmix_down_sem(dmix);
Packit Service db8eaa
	for (;;) {
Packit Service db8eaa
		transfer = size;
Packit Service db8eaa
		if (appl_ptr + transfer > pcm->buffer_size)
Packit Service db8eaa
			transfer = pcm->buffer_size - appl_ptr;
Packit Service db8eaa
		if (slave_appl_ptr + transfer > dmix->slave_buffer_size)
Packit Service db8eaa
			transfer = dmix->slave_buffer_size - slave_appl_ptr;
Packit Service db8eaa
		remix_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer);
Packit Service db8eaa
		size -= transfer;
Packit Service db8eaa
		if (! size)
Packit Service db8eaa
			break;
Packit Service db8eaa
		slave_appl_ptr += transfer;
Packit Service db8eaa
		slave_appl_ptr %= dmix->slave_buffer_size;
Packit Service db8eaa
		appl_ptr += transfer;
Packit Service db8eaa
		appl_ptr %= pcm->buffer_size;
Packit Service db8eaa
	}
Packit Service db8eaa
	dmix_up_sem(dmix);
Packit Service db8eaa
Packit Service db8eaa
	snd_pcm_mmap_appl_backward(pcm, frames_to_remix);
Packit Service db8eaa
	result += frames_to_remix;
Packit Service db8eaa
	/* At this point last_appl_ptr and appl_ptr has to indicate the
Packit Service db8eaa
	 * position of the first not mixed frame
Packit Service db8eaa
	 */
Packit Service db8eaa
Packit Service db8eaa
	return result;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_dmix_forwardable(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	return snd_pcm_mmap_avail(pcm);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_dmix_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_sframes_t avail;
Packit Service db8eaa
Packit Service db8eaa
	avail = snd_pcm_dmix_forwardable(pcm);
Packit Service db8eaa
	if (frames > (snd_pcm_uframes_t)avail)
Packit Service db8eaa
		frames = avail;
Packit Service db8eaa
	snd_pcm_mmap_appl_forward(pcm, frames);
Packit Service db8eaa
	return frames;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_dmix_readi(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -ENODEV;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_dmix_readn(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return -ENODEV;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_dmix_close(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
Packit Service db8eaa
	if (dmix->timer)
Packit Service db8eaa
		snd_timer_close(dmix->timer);
Packit Service db8eaa
	snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
Packit Service db8eaa
	snd_pcm_close(dmix->spcm);
Packit Service db8eaa
 	if (dmix->server)
Packit Service db8eaa
 		snd_pcm_direct_server_discard(dmix);
Packit Service db8eaa
 	if (dmix->client)
Packit Service db8eaa
 		snd_pcm_direct_client_discard(dmix);
Packit Service db8eaa
 	shm_sum_discard(dmix);
Packit Service db8eaa
	if (snd_pcm_direct_shm_discard(dmix)) {
Packit Service db8eaa
		if (snd_pcm_direct_semaphore_discard(dmix))
Packit Service db8eaa
			snd_pcm_direct_semaphore_final(dmix, DIRECT_IPC_SEM_CLIENT);
Packit Service db8eaa
	} else
Packit Service db8eaa
		snd_pcm_direct_semaphore_final(dmix, DIRECT_IPC_SEM_CLIENT);
Packit Service db8eaa
	free(dmix->bindings);
Packit Service db8eaa
	pcm->private_data = NULL;
Packit Service db8eaa
	free(dmix);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_dmix_mmap_commit(snd_pcm_t *pcm,
Packit Service db8eaa
						  snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
Packit Service db8eaa
						  snd_pcm_uframes_t size)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	switch (snd_pcm_state(dmix->spcm)) {
Packit Service db8eaa
	case SND_PCM_STATE_XRUN:
Packit Service db8eaa
		if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_PCM_STATE_SUSPENDED:
Packit Service db8eaa
		return -ESTRPIPE;
Packit Service db8eaa
	default:
Packit Service db8eaa
		break;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (snd_pcm_direct_client_chk_xrun(dmix, pcm))
Packit Service db8eaa
		return -EPIPE;
Packit Service db8eaa
	if (! size)
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	snd_pcm_mmap_appl_forward(pcm, size);
Packit Service db8eaa
	if (dmix->state == STATE_RUN_PENDING) {
Packit Service db8eaa
		if ((err = snd_pcm_dmix_start_timer(pcm, dmix)) < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	} else if (dmix->state == SND_PCM_STATE_RUNNING ||
Packit Service db8eaa
		   dmix->state == SND_PCM_STATE_DRAINING) {
Packit Service db8eaa
		if ((err = snd_pcm_dmix_sync_ptr(pcm)) < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (dmix->state == SND_PCM_STATE_RUNNING ||
Packit Service db8eaa
	    dmix->state == SND_PCM_STATE_DRAINING) {
Packit Service db8eaa
		/* ok, we commit the changes after the validation of area */
Packit Service db8eaa
		/* it's intended, although the result might be crappy */
Packit Service db8eaa
		snd_pcm_dmix_sync_area(pcm);
Packit Service db8eaa
		/* clear timer queue to avoid a bogus return from poll */
Packit Service db8eaa
		if (snd_pcm_mmap_playback_avail(pcm) < pcm->avail_min)
Packit Service db8eaa
			snd_pcm_direct_clear_timer_queue(dmix);
Packit Service db8eaa
	}
Packit Service db8eaa
	return size;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static snd_pcm_sframes_t snd_pcm_dmix_avail_update(snd_pcm_t *pcm)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	
Packit Service db8eaa
	if (dmix->state == SND_PCM_STATE_RUNNING ||
Packit Service db8eaa
	    dmix->state == SND_PCM_STATE_DRAINING) {
Packit Service db8eaa
		if ((err = snd_pcm_dmix_sync_ptr(pcm)) < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (dmix->state == SND_PCM_STATE_XRUN)
Packit Service db8eaa
		return -EPIPE;
Packit Service db8eaa
Packit Service db8eaa
	return snd_pcm_mmap_playback_avail(pcm);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_dmix_htimestamp(snd_pcm_t *pcm,
Packit Service db8eaa
				   snd_pcm_uframes_t *avail,
Packit Service db8eaa
				   snd_htimestamp_t *tstamp)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	snd_pcm_uframes_t avail1;
Packit Service db8eaa
	int ok = 0;
Packit Service db8eaa
	
Packit Service db8eaa
	while (1) {
Packit Service db8eaa
		if (dmix->state == SND_PCM_STATE_RUNNING ||
Packit Service db8eaa
		    dmix->state == SND_PCM_STATE_DRAINING)
Packit Service db8eaa
			snd_pcm_dmix_sync_ptr(pcm);
Packit Service db8eaa
		avail1 = snd_pcm_mmap_playback_avail(pcm);
Packit Service db8eaa
		if (ok && *avail == avail1)
Packit Service db8eaa
			break;
Packit Service db8eaa
		*avail = avail1;
Packit Service db8eaa
		*tstamp = snd_pcm_hw_fast_tstamp(dmix->spcm);
Packit Service db8eaa
		ok = 1;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int snd_pcm_dmix_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
	if (dmix->state == SND_PCM_STATE_RUNNING)
Packit Service db8eaa
		snd_pcm_dmix_sync_area(pcm);
Packit Service db8eaa
	return snd_pcm_direct_poll_revents(pcm, pfds, nfds, revents);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
Packit Service db8eaa
static void snd_pcm_dmix_dump(snd_pcm_t *pcm, snd_output_t *out)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_direct_t *dmix = pcm->private_data;
Packit Service db8eaa
Packit Service db8eaa
	snd_output_printf(out, "Direct Stream Mixing PCM\n");
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
	if (dmix->spcm)
Packit Service db8eaa
		snd_pcm_dump(dmix->spcm, out);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static const snd_pcm_ops_t snd_pcm_dmix_ops = {
Packit Service db8eaa
	.close = snd_pcm_dmix_close,
Packit Service db8eaa
	.info = snd_pcm_direct_info,
Packit Service db8eaa
	.hw_refine = snd_pcm_direct_hw_refine,
Packit Service db8eaa
	.hw_params = snd_pcm_direct_hw_params,
Packit Service db8eaa
	.hw_free = snd_pcm_direct_hw_free,
Packit Service db8eaa
	.sw_params = snd_pcm_direct_sw_params,
Packit Service db8eaa
	.channel_info = snd_pcm_direct_channel_info,
Packit Service db8eaa
	.dump = snd_pcm_dmix_dump,
Packit Service db8eaa
	.nonblock = snd_pcm_direct_nonblock,
Packit Service db8eaa
	.async = snd_pcm_direct_async,
Packit Service db8eaa
	.mmap = snd_pcm_direct_mmap,
Packit Service db8eaa
	.munmap = snd_pcm_direct_munmap,
Packit Service db8eaa
	.query_chmaps = snd_pcm_direct_query_chmaps,
Packit Service db8eaa
	.get_chmap = snd_pcm_direct_get_chmap,
Packit Service db8eaa
	.set_chmap = snd_pcm_direct_set_chmap,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
static const snd_pcm_fast_ops_t snd_pcm_dmix_fast_ops = {
Packit Service db8eaa
	.status = snd_pcm_dmix_status,
Packit Service db8eaa
	.state = snd_pcm_dmix_state,
Packit Service db8eaa
	.hwsync = snd_pcm_dmix_hwsync,
Packit Service db8eaa
	.delay = snd_pcm_dmix_delay,
Packit Service db8eaa
	.prepare = snd_pcm_direct_prepare,
Packit Service db8eaa
	.reset = snd_pcm_dmix_reset,
Packit Service db8eaa
	.start = snd_pcm_dmix_start,
Packit Service db8eaa
	.drop = snd_pcm_dmix_drop,
Packit Service db8eaa
	.drain = snd_pcm_dmix_drain,
Packit Service db8eaa
	.pause = snd_pcm_dmix_pause,
Packit Service db8eaa
	.rewindable = snd_pcm_dmix_rewindable,
Packit Service db8eaa
	.rewind = snd_pcm_dmix_rewind,
Packit Service db8eaa
	.forwardable = snd_pcm_dmix_forwardable,
Packit Service db8eaa
	.forward = snd_pcm_dmix_forward,
Packit Service db8eaa
	.resume = snd_pcm_direct_resume,
Packit Service db8eaa
	.link = NULL,
Packit Service db8eaa
	.link_slaves = NULL,
Packit Service db8eaa
	.unlink = NULL,
Packit Service db8eaa
	.writei = snd_pcm_mmap_writei,
Packit Service db8eaa
	.writen = snd_pcm_mmap_writen,
Packit Service db8eaa
	.readi = snd_pcm_dmix_readi,
Packit Service db8eaa
	.readn = snd_pcm_dmix_readn,
Packit Service db8eaa
	.avail_update = snd_pcm_dmix_avail_update,
Packit Service db8eaa
	.mmap_commit = snd_pcm_dmix_mmap_commit,
Packit Service db8eaa
	.htimestamp = snd_pcm_dmix_htimestamp,
Packit Service db8eaa
	.poll_descriptors = snd_pcm_direct_poll_descriptors,
Packit Service db8eaa
	.poll_descriptors_count = NULL,
Packit Service db8eaa
	.poll_revents = snd_pcm_dmix_poll_revents,
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
/**
Packit Service db8eaa
 * \brief Creates a new dmix PCM
Packit Service db8eaa
 * \param pcmp Returns created PCM handle
Packit Service db8eaa
 * \param name Name of PCM
Packit Service db8eaa
 * \param opts Direct PCM configurations
Packit Service db8eaa
 * \param params Parameters for slave
Packit Service db8eaa
 * \param root Configuration root
Packit Service db8eaa
 * \param sconf Slave configuration
Packit Service db8eaa
 * \param stream PCM Direction (stream)
Packit Service db8eaa
 * \param mode PCM 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_dmix_open(snd_pcm_t **pcmp, const char *name,
Packit Service db8eaa
		      struct snd_pcm_direct_open_conf *opts,
Packit Service db8eaa
		      struct slave_params *params,
Packit Service db8eaa
		      snd_config_t *root, snd_config_t *sconf,
Packit Service db8eaa
		      snd_pcm_stream_t stream, int mode)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_pcm_t *pcm = NULL, *spcm = NULL;
Packit Service db8eaa
	snd_pcm_direct_t *dmix = NULL;
Packit Service db8eaa
	int ret, first_instance;
Packit Service db8eaa
	int fail_sem_loop = 10;
Packit Service db8eaa
Packit Service db8eaa
	assert(pcmp);
Packit Service db8eaa
Packit Service db8eaa
	if (stream != SND_PCM_STREAM_PLAYBACK) {
Packit Service db8eaa
		SNDERR("The dmix plugin supports only playback stream");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	dmix = calloc(1, sizeof(snd_pcm_direct_t));
Packit Service db8eaa
	if (!dmix) {
Packit Service db8eaa
		ret = -ENOMEM;
Packit Service db8eaa
		goto _err_nosem;
Packit Service db8eaa
	}
Packit Service db8eaa
	
Packit Service db8eaa
	ret = snd_pcm_direct_parse_bindings(dmix, params, opts->bindings);
Packit Service db8eaa
	if (ret < 0)
Packit Service db8eaa
		goto _err_nosem;
Packit Service db8eaa
	
Packit Service db8eaa
	dmix->ipc_key = opts->ipc_key;
Packit Service db8eaa
	dmix->ipc_perm = opts->ipc_perm;
Packit Service db8eaa
	dmix->ipc_gid = opts->ipc_gid;
Packit Service db8eaa
	dmix->tstamp_type = opts->tstamp_type;
Packit Service db8eaa
	dmix->semid = -1;
Packit Service db8eaa
	dmix->shmid = -1;
Packit Service db8eaa
Packit Service db8eaa
	ret = snd_pcm_new(&pcm, dmix->type = SND_PCM_TYPE_DMIX, name, stream, mode);
Packit Service db8eaa
	if (ret < 0)
Packit Service db8eaa
		goto _err;
Packit Service db8eaa
Packit Service db8eaa
	
Packit Service db8eaa
	while (1) {
Packit Service db8eaa
		ret = snd_pcm_direct_semaphore_create_or_connect(dmix);
Packit Service db8eaa
		if (ret < 0) {
Packit Service db8eaa
			SNDERR("unable to create IPC semaphore");
Packit Service db8eaa
			goto _err_nosem;
Packit Service db8eaa
		}
Packit Service db8eaa
		ret = snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
Packit Service db8eaa
		if (ret < 0) {
Packit Service db8eaa
			snd_pcm_direct_semaphore_discard(dmix);
Packit Service db8eaa
			if (--fail_sem_loop <= 0)
Packit Service db8eaa
				goto _err_nosem;
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
		break;
Packit Service db8eaa
	}
Packit Service db8eaa
		
Packit Service db8eaa
	first_instance = ret = snd_pcm_direct_shm_create_or_connect(dmix);
Packit Service db8eaa
	if (ret < 0) {
Packit Service db8eaa
		SNDERR("unable to create IPC shm instance");
Packit Service db8eaa
		goto _err;
Packit Service db8eaa
	}
Packit Service db8eaa
		
Packit Service db8eaa
	pcm->ops = &snd_pcm_dmix_ops;
Packit Service db8eaa
	pcm->fast_ops = &snd_pcm_dmix_fast_ops;
Packit Service db8eaa
	pcm->private_data = dmix;
Packit Service db8eaa
	dmix->state = SND_PCM_STATE_OPEN;
Packit Service db8eaa
	dmix->slowptr = opts->slowptr;
Packit Service db8eaa
	dmix->max_periods = opts->max_periods;
Packit Service db8eaa
	dmix->var_periodsize = opts->var_periodsize;
Packit Service db8eaa
	dmix->hw_ptr_alignment = opts->hw_ptr_alignment;
Packit Service db8eaa
	dmix->sync_ptr = snd_pcm_dmix_sync_ptr;
Packit Service db8eaa
	dmix->direct_memory_access = opts->direct_memory_access;
Packit Service db8eaa
Packit Service db8eaa
 retry:
Packit Service db8eaa
	if (first_instance) {
Packit Service db8eaa
		/* recursion is already checked in
Packit Service db8eaa
		   snd_pcm_direct_get_slave_ipc_offset() */
Packit Service db8eaa
		ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
Packit Service db8eaa
					 mode | SND_PCM_NONBLOCK, NULL);
Packit Service db8eaa
		if (ret < 0) {
Packit Service db8eaa
			SNDERR("unable to open slave");
Packit Service db8eaa
			goto _err;
Packit Service db8eaa
		}
Packit Service db8eaa
	
Packit Service db8eaa
		if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
Packit Service db8eaa
			SNDERR("dmix plugin can be only connected to hw plugin");
Packit Service db8eaa
			ret = -EINVAL;
Packit Service db8eaa
			goto _err;
Packit Service db8eaa
		}
Packit Service db8eaa
		
Packit Service db8eaa
		ret = snd_pcm_direct_initialize_slave(dmix, spcm, params);
Packit Service db8eaa
		if (ret < 0) {
Packit Service db8eaa
			SNDERR("unable to initialize slave");
Packit Service db8eaa
			goto _err;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		dmix->spcm = spcm;
Packit Service db8eaa
Packit Service db8eaa
		if (dmix->shmptr->use_server) {
Packit Service db8eaa
			dmix->server_free = dmix_server_free;
Packit Service db8eaa
		
Packit Service db8eaa
			ret = snd_pcm_direct_server_create(dmix);
Packit Service db8eaa
			if (ret < 0) {
Packit Service db8eaa
				SNDERR("unable to create server");
Packit Service db8eaa
				goto _err;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		dmix->shmptr->type = spcm->type;
Packit Service db8eaa
	} else {
Packit Service db8eaa
		if (dmix->shmptr->use_server) {
Packit Service db8eaa
			/* up semaphore to avoid deadlock */
Packit Service db8eaa
			snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
Packit Service db8eaa
			ret = snd_pcm_direct_client_connect(dmix);
Packit Service db8eaa
			if (ret < 0) {
Packit Service db8eaa
				SNDERR("unable to connect client");
Packit Service db8eaa
				goto _err_nosem;
Packit Service db8eaa
			}
Packit Service db8eaa
			
Packit Service db8eaa
			snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
Packit Service db8eaa
			ret = snd_pcm_direct_open_secondary_client(&spcm, dmix, "dmix_client");
Packit Service db8eaa
			if (ret < 0)
Packit Service db8eaa
				goto _err;
Packit Service db8eaa
		} else {
Packit Service db8eaa
Packit Service db8eaa
			ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
Packit Service db8eaa
						 mode | SND_PCM_NONBLOCK |
Packit Service db8eaa
						 SND_PCM_APPEND,
Packit Service db8eaa
						 NULL);
Packit Service db8eaa
			if (ret < 0) {
Packit Service db8eaa
				/* all other streams have been closed;
Packit Service db8eaa
				 * retry as the first instance
Packit Service db8eaa
				 */
Packit Service db8eaa
				if (ret == -EBADFD) {
Packit Service db8eaa
					first_instance = 1;
Packit Service db8eaa
					goto retry;
Packit Service db8eaa
				}
Packit Service db8eaa
				SNDERR("unable to open slave");
Packit Service db8eaa
				goto _err;
Packit Service db8eaa
			}
Packit Service db8eaa
			if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
Packit Service db8eaa
				SNDERR("dmix plugin can be only connected to hw plugin");
Packit Service db8eaa
				ret = -EINVAL;
Packit Service db8eaa
				goto _err;
Packit Service db8eaa
			}
Packit Service db8eaa
		
Packit Service db8eaa
			ret = snd_pcm_direct_initialize_secondary_slave(dmix, spcm, params);
Packit Service db8eaa
			if (ret < 0) {
Packit Service db8eaa
				SNDERR("unable to initialize slave");
Packit Service db8eaa
				goto _err;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		dmix->spcm = spcm;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	ret = shm_sum_create_or_connect(dmix);
Packit Service db8eaa
	if (ret < 0) {
Packit Service db8eaa
		SNDERR("unable to initialize sum ring buffer");
Packit Service db8eaa
		goto _err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	ret = snd_pcm_direct_initialize_poll_fd(dmix);
Packit Service db8eaa
	if (ret < 0) {
Packit Service db8eaa
		SNDERR("unable to initialize poll_fd");
Packit Service db8eaa
		goto _err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	mix_select_callbacks(dmix);
Packit Service db8eaa
		
Packit Service db8eaa
	pcm->poll_fd = dmix->poll_fd;
Packit Service db8eaa
	pcm->poll_events = POLLIN;	/* it's different than other plugins */
Packit Service db8eaa
	pcm->tstamp_type = spcm->tstamp_type;
Packit Service db8eaa
	pcm->mmap_rw = 1;
Packit Service db8eaa
	snd_pcm_set_hw_ptr(pcm, &dmix->hw_ptr, -1, 0);
Packit Service db8eaa
	snd_pcm_set_appl_ptr(pcm, &dmix->appl_ptr, -1, 0);
Packit Service db8eaa
	
Packit Service db8eaa
	if (dmix->channels == UINT_MAX)
Packit Service db8eaa
		dmix->channels = dmix->shmptr->s.channels;
Packit Service db8eaa
Packit Service db8eaa
	snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
Packit Service db8eaa
Packit Service db8eaa
	*pcmp = pcm;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
	
Packit Service db8eaa
 _err:
Packit Service db8eaa
	if (dmix->timer)
Packit Service db8eaa
		snd_timer_close(dmix->timer);
Packit Service db8eaa
	if (dmix->server)
Packit Service db8eaa
		snd_pcm_direct_server_discard(dmix);
Packit Service db8eaa
	if (dmix->client)
Packit Service db8eaa
		snd_pcm_direct_client_discard(dmix);
Packit Service db8eaa
	if (spcm)
Packit Service db8eaa
		snd_pcm_close(spcm);
Packit Service db8eaa
	if (dmix->u.dmix.shmid_sum >= 0)
Packit Service db8eaa
		shm_sum_discard(dmix);
Packit Service db8eaa
	if ((dmix->shmid >= 0) && (snd_pcm_direct_shm_discard(dmix))) {
Packit Service db8eaa
		if (snd_pcm_direct_semaphore_discard(dmix))
Packit Service db8eaa
			snd_pcm_direct_semaphore_final(dmix, DIRECT_IPC_SEM_CLIENT);
Packit Service db8eaa
	} else
Packit Service db8eaa
		snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
Packit Service db8eaa
 _err_nosem:
Packit Service db8eaa
	if (dmix) {
Packit Service db8eaa
		free(dmix->bindings);
Packit Service db8eaa
		free(dmix);
Packit Service db8eaa
	}
Packit Service db8eaa
	if (pcm)
Packit Service db8eaa
		snd_pcm_free(pcm);
Packit Service db8eaa
	return ret;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*! \page pcm_plugins
Packit Service db8eaa
Packit Service db8eaa
\section pcm_plugins_dmix Plugin: dmix
Packit Service db8eaa
Packit Service db8eaa
This plugin provides direct mixing of multiple streams. The resolution
Packit Service db8eaa
for 32-bit mixing is only 24-bit. The low significant byte is filled with
Packit Service db8eaa
zeros. The extra 8 bits are used for the saturation.
Packit Service db8eaa
Packit Service db8eaa
\code
Packit Service db8eaa
pcm.name {
Packit Service db8eaa
	type dmix		# Direct mix
Packit Service db8eaa
	ipc_key INT		# unique IPC key
Packit Service db8eaa
	ipc_key_add_uid BOOL	# add current uid to unique IPC key
Packit Service db8eaa
	ipc_perm INT		# IPC permissions (octal, default 0600)
Packit Service db8eaa
	hw_ptr_alignment STR	# Slave application and hw pointer alignment type
Packit Service db8eaa
				# STR can be one of the below strings :
Packit Service db8eaa
				# no
Packit Service db8eaa
				# roundup
Packit Service db8eaa
				# rounddown
Packit Service db8eaa
				# auto (default)
Packit Service db8eaa
	tstamp_type STR		# timestamp type
Packit Service db8eaa
				# STR can be one of the below strings :
Packit Service db8eaa
				# default, gettimeofday, monotonic, monotonic_raw
Packit Service db8eaa
	slave STR
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	# format definition
Packit Service db8eaa
		rate INT	# rate definition
Packit Service db8eaa
		channels INT
Packit Service db8eaa
		period_time INT	# in usec
Packit Service db8eaa
		# or
Packit Service db8eaa
		period_size INT	# in frames
Packit Service db8eaa
		buffer_time INT	# in usec
Packit Service db8eaa
		# or
Packit Service db8eaa
		buffer_size INT # in frames
Packit Service db8eaa
		periods INT	# when buffer_size or buffer_time is not specified
Packit Service db8eaa
	}
Packit Service db8eaa
	bindings {		# note: this is client independent!!!
Packit Service db8eaa
		N INT		# maps slave channel to client channel N
Packit Service db8eaa
	}
Packit Service db8eaa
	slowptr BOOL		# slow but more precise pointer updates
Packit Service db8eaa
}
Packit Service db8eaa
\endcode
Packit Service db8eaa
Packit Service db8eaa
ipc_key specfies the unique IPC key in integer.
Packit Service db8eaa
This number must be unique for each different dmix definition,
Packit Service db8eaa
since the shared memory is created with this key number.
Packit Service db8eaa
When ipc_key_add_uid is set true, the uid value is
Packit Service db8eaa
added to the value set in ipc_key.  This will
Packit Service db8eaa
avoid the confliction of the same IPC key with different users
Packit Service db8eaa
concurrently.
Packit Service db8eaa
Packit Service db8eaa
hw_ptr_alignment specifies slave application and hw
Packit Service db8eaa
pointer alignment type. By default hw_ptr_alignment is auto. Below are
Packit Service db8eaa
the possible configurations:
Packit Service db8eaa
- no: minimal latency with minimal frames dropped at startup. But
Packit Service db8eaa
  wakeup of application (return from snd_pcm_wait() or poll()) can
Packit Service db8eaa
  take up to 2 * period.
Packit Service db8eaa
- roundup: It is guaranteed that all frames will be played at
Packit Service db8eaa
  startup. But the latency will increase upto period-1 frames.
Packit Service db8eaa
- rounddown: It is guaranteed that a wakeup will happen for each
Packit Service db8eaa
  period and frames can be written from application. But on startup
Packit Service db8eaa
  upto period-1 frames will be dropped.
Packit Service db8eaa
- auto: Selects the best approach depending on the used period and
Packit Service db8eaa
  buffer size.
Packit Service db8eaa
  If the application buffer size is < 2 * application period,
Packit Service db8eaa
  "roundup" will be selected to avoid under runs. If the slave_period
Packit Service db8eaa
  is < 10ms we could expect that there are low latency
Packit Service db8eaa
  requirements. Therefore "rounddown" will be chosen to avoid long
Packit Service db8eaa
  wakeup times. Such wakeup delay could otherwise end up with Xruns in
Packit Service db8eaa
  case of a dependency to another sound device (e.g. forwarding of
Packit Service db8eaa
  microphone to speaker). Else "no" will be chosen.
Packit Service db8eaa
Packit Service db8eaa
Note that the dmix plugin itself supports only a single configuration.
Packit Service db8eaa
That is, it supports only the fixed rate (default 48000), format
Packit Service db8eaa
(\c S16), channels (2), and period_time (125000).
Packit Service db8eaa
For using other configuration, you have to set the value explicitly
Packit Service db8eaa
in the slave PCM definition.  The rate, format and channels can be
Packit Service db8eaa
covered by an additional \ref pcm_plugins_dmix "plug plugin",
Packit Service db8eaa
but there is only one base configuration, anyway.
Packit Service db8eaa
Packit Service db8eaa
An example configuration for setting 44100 Hz, \c S32_LE format
Packit Service db8eaa
as the slave PCM of "hw:0" is like below:
Packit Service db8eaa
\code
Packit Service db8eaa
pcm.dmix_44 {
Packit Service db8eaa
	type dmix
Packit Service db8eaa
	ipc_key 321456	# any unique value
Packit Service db8eaa
	ipc_key_add_uid true
Packit Service db8eaa
	slave {
Packit Service db8eaa
		pcm "hw:0"
Packit Service db8eaa
		format S32_LE
Packit Service db8eaa
		rate 44100
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
\endcode
Packit Service db8eaa
You can hear 48000 Hz samples still using this dmix pcm via plug plugin
Packit Service db8eaa
like:
Packit Service db8eaa
\code
Packit Service db8eaa
% aplay -Dplug:dmix_44 foo_48k.wav
Packit Service db8eaa
\endcode
Packit Service db8eaa
Packit Service db8eaa
For using the dmix plugin for OSS emulation device, you have to set
Packit Service db8eaa
the period and the buffer sizes in power of two.  For example,
Packit Service db8eaa
\code
Packit Service db8eaa
pcm.dmixoss {
Packit Service db8eaa
	type dmix
Packit Service db8eaa
	ipc_key 321456	# any unique value
Packit Service db8eaa
	ipc_key_add_uid true
Packit Service db8eaa
	slave {
Packit Service db8eaa
		pcm "hw:0"
Packit Service db8eaa
		period_time 0
Packit Service db8eaa
		period_size 1024  # must be power of 2
Packit Service db8eaa
		buffer_size 8192  # ditto
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
\endcode
Packit Service db8eaa
period_time 0 must be set, too, for resetting the
Packit Service db8eaa
default value.  In the case of soundcards with multi-channel IO,
Packit Service db8eaa
adding the bindings would help
Packit Service db8eaa
\code
Packit Service db8eaa
pcm.dmixoss {
Packit Service db8eaa
	...
Packit Service db8eaa
	bindings {
Packit Service db8eaa
		0 0   # map from 0 to 0
Packit Service db8eaa
		1 1   # map from 1 to 1
Packit Service db8eaa
	}
Packit Service db8eaa
}
Packit Service db8eaa
\endcode
Packit Service db8eaa
so that only the first two channels are used by dmix.
Packit Service db8eaa
Also, note that ICE1712 have the limited buffer size, 5513 frames
Packit Service db8eaa
(corresponding to 640 kB).  In this case, reduce the buffer_size
Packit Service db8eaa
to 4096.
Packit Service db8eaa
Packit Service db8eaa
\subsection pcm_plugins_dmix_funcref Function reference
Packit Service db8eaa
Packit Service db8eaa
    Packit Service db8eaa
      
  • snd_pcm_dmix_open()
  • Packit Service db8eaa
      
  • _snd_pcm_dmix_open()
  • Packit Service db8eaa
    Packit Service db8eaa
    Packit Service db8eaa
    */
    Packit Service db8eaa
    Packit Service db8eaa
    /**
    Packit Service db8eaa
     * \brief Creates a new dmix 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 dmix PCM description
    Packit Service db8eaa
     * \param stream PCM Stream
    Packit Service db8eaa
     * \param mode PCM Mode
    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_dmix_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_t *sconf;
    Packit Service db8eaa
    	struct slave_params params;
    Packit Service db8eaa
    	struct snd_pcm_direct_open_conf dopen;
    Packit Service db8eaa
    	int bsize, psize;
    Packit Service db8eaa
    	int err;
    Packit Service db8eaa
    Packit Service db8eaa
    	err = snd_pcm_direct_parse_open_conf(root, conf, stream, &dopen);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    Packit Service db8eaa
    	/* the default settings, it might be invalid for some hardware */
    Packit Service db8eaa
    	params.format = SND_PCM_FORMAT_S16;
    Packit Service db8eaa
    	params.rate = 48000;
    Packit Service db8eaa
    	params.channels = 2;
    Packit Service db8eaa
    	params.period_time = -1;
    Packit Service db8eaa
    	params.buffer_time = -1;
    Packit Service db8eaa
    	bsize = psize = -1;
    Packit Service db8eaa
    	params.periods = 3;
    Packit Service db8eaa
    Packit Service db8eaa
    	err = snd_pcm_slave_conf(root, dopen.slave, &sconf, 8,
    Packit Service db8eaa
    				 SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &params.format,
    Packit Service db8eaa
    				 SND_PCM_HW_PARAM_RATE, 0, &params.rate,
    Packit Service db8eaa
    				 SND_PCM_HW_PARAM_CHANNELS, 0, &params.channels,
    Packit Service db8eaa
    				 SND_PCM_HW_PARAM_PERIOD_TIME, 0, &params.period_time,
    Packit Service db8eaa
    				 SND_PCM_HW_PARAM_BUFFER_TIME, 0, &params.buffer_time,
    Packit Service db8eaa
    				 SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &psize,
    Packit Service db8eaa
    				 SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &bsize,
    Packit Service db8eaa
    				 SND_PCM_HW_PARAM_PERIODS, 0, &params.periods);
    Packit Service db8eaa
    	if (err < 0)
    Packit Service db8eaa
    		return err;
    Packit Service db8eaa
    Packit Service db8eaa
    	/* set a reasonable default */  
    Packit Service db8eaa
    	if (psize == -1 && params.period_time == -1)
    Packit Service db8eaa
    		params.period_time = 125000;    /* 0.125 seconds */
    Packit Service db8eaa
    Packit Service db8eaa
    	if (params.format == -2)
    Packit Service db8eaa
    		params.format = SND_PCM_FORMAT_UNKNOWN;
    Packit Service db8eaa
    	else if (!(dmix_supported_format & (1ULL << params.format))) {
    Packit Service db8eaa
    		/* sorry, limited features */
    Packit Service db8eaa
    		SNDERR("Unsupported format");
    Packit Service db8eaa
    		snd_config_delete(sconf);
    Packit Service db8eaa
    		return -EINVAL;
    Packit Service db8eaa
    	}
    Packit Service db8eaa
    Packit Service db8eaa
    	params.period_size = psize;
    Packit Service db8eaa
    	params.buffer_size = bsize;
    Packit Service db8eaa
    Packit Service db8eaa
    	err = snd_pcm_dmix_open(pcmp, name, &dopen, &params,
    Packit Service db8eaa
    				root, sconf, stream, mode);
    Packit Service db8eaa
    	snd_config_delete(sconf);
    Packit Service db8eaa
    	return err;
    Packit Service db8eaa
    }
    Packit Service db8eaa
    #ifndef DOC_HIDDEN
    Packit Service db8eaa
    SND_DLSYM_BUILD_VERSION(_snd_pcm_dmix_open, SND_PCM_DLSYM_VERSION);
    Packit Service db8eaa
    #endif