Blame src/pcm/pcm_dmix.c

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