1/**
2 * \file pcm/pcm_dmix.c
3 * \ingroup PCM_Plugins
4 * \brief PCM Direct Stream Mixing (dmix) Plugin Interface
5 * \author Jaroslav Kysela <perex@perex.cz>
6 * \date 2003
7 */
8/*
9 *  PCM - Direct Stream Mixing
10 *  Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
11 *
12 *
13 *   This library is free software; you can redistribute it and/or modify
14 *   it under the terms of the GNU Lesser General Public License as
15 *   published by the Free Software Foundation; either version 2.1 of
16 *   the License, or (at your option) any later version.
17 *
18 *   This program is distributed in the hope that it will be useful,
19 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 *   GNU Lesser General Public License for more details.
22 *
23 *   You should have received a copy of the GNU Lesser General Public
24 *   License along with this library; if not, write to the Free Software
25 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
26 *
27 */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <stddef.h>
32#include <unistd.h>
33#include <signal.h>
34#include <string.h>
35#include <fcntl.h>
36#include <ctype.h>
37#include <grp.h>
38#include <sys/ioctl.h>
39#include <sys/mman.h>
40#include <sys/shm.h>
41#include <sys/sem.h>
42#include <sys/wait.h>
43#include <sys/socket.h>
44#include <sys/un.h>
45#include <sys/mman.h>
46#include "pcm_direct.h"
47
48#ifndef PIC
49/* entry for static linking */
50const char *_snd_module_pcm_dmix = "";
51#endif
52
53#ifndef DOC_HIDDEN
54/* start is pending - this state happens when rate plugin does a delayed commit */
55#define STATE_RUN_PENDING	1024
56#endif
57
58/*
59 *
60 */
61
62static int shm_sum_discard(snd_pcm_direct_t *dmix);
63
64/*
65 *  sum ring buffer shared memory area
66 */
67static int shm_sum_create_or_connect(snd_pcm_direct_t *dmix)
68{
69	struct shmid_ds buf;
70	int tmpid, err;
71	size_t size;
72
73	size = dmix->shmptr->s.channels *
74	       dmix->shmptr->s.buffer_size *
75	       sizeof(signed int);
76retryshm:
77	dmix->u.dmix.shmid_sum = shmget(dmix->ipc_key + 1, size,
78					IPC_CREAT | dmix->ipc_perm);
79	err = -errno;
80	if (dmix->u.dmix.shmid_sum < 0) {
81		if (errno == EINVAL)
82		if ((tmpid = shmget(dmix->ipc_key + 1, 0, dmix->ipc_perm)) != -1)
83		if (!shmctl(tmpid, IPC_STAT, &buf))
84	    	if (!buf.shm_nattch)
85		/* no users so destroy the segment */
86		if (!shmctl(tmpid, IPC_RMID, NULL))
87		    goto retryshm;
88		return err;
89	}
90	if (shmctl(dmix->u.dmix.shmid_sum, IPC_STAT, &buf) < 0) {
91		err = -errno;
92		shm_sum_discard(dmix);
93		return err;
94	}
95	if (dmix->ipc_gid >= 0) {
96		buf.shm_perm.gid = dmix->ipc_gid;
97		shmctl(dmix->u.dmix.shmid_sum, IPC_SET, &buf);
98	}
99	dmix->u.dmix.sum_buffer = shmat(dmix->u.dmix.shmid_sum, 0, 0);
100	if (dmix->u.dmix.sum_buffer == (void *) -1) {
101		err = -errno;
102		shm_sum_discard(dmix);
103		return err;
104	}
105	mlock(dmix->u.dmix.sum_buffer, size);
106	return 0;
107}
108
109static int shm_sum_discard(snd_pcm_direct_t *dmix)
110{
111	struct shmid_ds buf;
112	int ret = 0;
113
114	if (dmix->u.dmix.shmid_sum < 0)
115		return -EINVAL;
116	if (dmix->u.dmix.sum_buffer != (void *) -1 && shmdt(dmix->u.dmix.sum_buffer) < 0)
117		return -errno;
118	dmix->u.dmix.sum_buffer = (void *) -1;
119	if (shmctl(dmix->u.dmix.shmid_sum, IPC_STAT, &buf) < 0)
120		return -errno;
121	if (buf.shm_nattch == 0) {	/* we're the last user, destroy the segment */
122		if (shmctl(dmix->u.dmix.shmid_sum, IPC_RMID, NULL) < 0)
123			return -errno;
124		ret = 1;
125	}
126	dmix->u.dmix.shmid_sum = -1;
127	return ret;
128}
129
130static void dmix_server_free(snd_pcm_direct_t *dmix)
131{
132	/* remove the memory region */
133	shm_sum_create_or_connect(dmix);
134	shm_sum_discard(dmix);
135}
136
137/*
138 *  the main function of this plugin: mixing
139 *  FIXME: optimize it for different architectures
140 */
141
142#include "pcm_dmix_generic.c"
143#if defined(__i386__)
144#include "pcm_dmix_i386.c"
145#elif defined(__x86_64__)
146#include "pcm_dmix_x86_64.c"
147#else
148#ifndef DOC_HIDDEN
149#define mix_select_callbacks(x)	generic_mix_select_callbacks(x)
150#define dmix_supported_format generic_dmix_supported_format
151#endif
152#endif
153
154static void mix_areas(snd_pcm_direct_t *dmix,
155		      const snd_pcm_channel_area_t *src_areas,
156		      const snd_pcm_channel_area_t *dst_areas,
157		      snd_pcm_uframes_t src_ofs,
158		      snd_pcm_uframes_t dst_ofs,
159		      snd_pcm_uframes_t size)
160{
161	unsigned int src_step, dst_step;
162	unsigned int chn, dchn, channels, sample_size;
163	mix_areas_t *do_mix_areas;
164
165	channels = dmix->channels;
166	switch (dmix->shmptr->s.format) {
167	case SND_PCM_FORMAT_S16_LE:
168	case SND_PCM_FORMAT_S16_BE:
169		sample_size = 2;
170		do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_16;
171		break;
172	case SND_PCM_FORMAT_S32_LE:
173	case SND_PCM_FORMAT_S32_BE:
174		sample_size = 4;
175		do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_32;
176		break;
177	case SND_PCM_FORMAT_S24_LE:
178		sample_size = 4;
179		do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_24;
180		break;
181	case SND_PCM_FORMAT_S24_3LE:
182		sample_size = 3;
183		do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_24;
184		break;
185	case SND_PCM_FORMAT_U8:
186		sample_size = 1;
187		do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_u8;
188		break;
189	default:
190		return;
191	}
192	if (dmix->interleaved) {
193		/*
194		 * process all areas in one loop
195		 * it optimizes the memory accesses for this case
196		 */
197		do_mix_areas(size * channels,
198			     (unsigned char *)dst_areas[0].addr + sample_size * dst_ofs * channels,
199			     (unsigned char *)src_areas[0].addr + sample_size * src_ofs * channels,
200			     dmix->u.dmix.sum_buffer + dst_ofs * channels,
201			     sample_size,
202			     sample_size,
203			     sizeof(signed int));
204		return;
205	}
206	for (chn = 0; chn < channels; chn++) {
207		dchn = dmix->bindings ? dmix->bindings[chn] : chn;
208		if (dchn >= dmix->shmptr->s.channels)
209			continue;
210		src_step = src_areas[chn].step / 8;
211		dst_step = dst_areas[dchn].step / 8;
212		do_mix_areas(size,
213			     ((unsigned char *)dst_areas[dchn].addr + dst_areas[dchn].first / 8) + dst_ofs * dst_step,
214			     ((unsigned char *)src_areas[chn].addr + src_areas[chn].first / 8) + src_ofs * src_step,
215			     dmix->u.dmix.sum_buffer + channels * dst_ofs + chn,
216			     dst_step,
217			     src_step,
218			     channels * sizeof(signed int));
219	}
220}
221
222static void remix_areas(snd_pcm_direct_t *dmix,
223			const snd_pcm_channel_area_t *src_areas,
224			const snd_pcm_channel_area_t *dst_areas,
225			snd_pcm_uframes_t src_ofs,
226			snd_pcm_uframes_t dst_ofs,
227			snd_pcm_uframes_t size)
228{
229	unsigned int src_step, dst_step;
230	unsigned int chn, dchn, channels, sample_size;
231	mix_areas_t *do_remix_areas;
232
233	channels = dmix->channels;
234	switch (dmix->shmptr->s.format) {
235	case SND_PCM_FORMAT_S16_LE:
236	case SND_PCM_FORMAT_S16_BE:
237		sample_size = 2;
238		do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_16;
239		break;
240	case SND_PCM_FORMAT_S32_LE:
241	case SND_PCM_FORMAT_S32_BE:
242		sample_size = 4;
243		do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_32;
244		break;
245	case SND_PCM_FORMAT_S24_LE:
246		sample_size = 4;
247		do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_24;
248		break;
249	case SND_PCM_FORMAT_S24_3LE:
250		sample_size = 3;
251		do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_24;
252		break;
253	case SND_PCM_FORMAT_U8:
254		sample_size = 1;
255		do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_u8;
256		break;
257	default:
258		return;
259	}
260	if (dmix->interleaved) {
261		/*
262		 * process all areas in one loop
263		 * it optimizes the memory accesses for this case
264		 */
265		do_remix_areas(size * channels,
266			       (unsigned char *)dst_areas[0].addr + sample_size * dst_ofs * channels,
267			       (unsigned char *)src_areas[0].addr + sample_size * src_ofs * channels,
268			       dmix->u.dmix.sum_buffer + dst_ofs * channels,
269			       sample_size,
270			       sample_size,
271			       sizeof(signed int));
272		return;
273	}
274	for (chn = 0; chn < channels; chn++) {
275		dchn = dmix->bindings ? dmix->bindings[chn] : chn;
276		if (dchn >= dmix->shmptr->s.channels)
277			continue;
278		src_step = src_areas[chn].step / 8;
279		dst_step = dst_areas[dchn].step / 8;
280		do_remix_areas(size,
281			       ((unsigned char *)dst_areas[dchn].addr + dst_areas[dchn].first / 8) + dst_ofs * dst_step,
282			       ((unsigned char *)src_areas[chn].addr + src_areas[chn].first / 8) + src_ofs * src_step,
283			       dmix->u.dmix.sum_buffer + channels * dst_ofs + chn,
284			       dst_step,
285			       src_step,
286			       channels * sizeof(signed int));
287	}
288}
289
290/*
291 * if no concurrent access is allowed in the mixing routines, we need to protect
292 * the area via semaphore
293 */
294#ifndef DOC_HIDDEN
295#ifdef NO_CONCURRENT_ACCESS
296#define dmix_down_sem(dmix) snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT)
297#define dmix_up_sem(dmix) snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT)
298#else
299#define dmix_down_sem(dmix)
300#define dmix_up_sem(dmix)
301#endif
302#endif
303
304/*
305 *  synchronize shm ring buffer with hardware
306 */
307static void snd_pcm_dmix_sync_area(snd_pcm_t *pcm)
308{
309	snd_pcm_direct_t *dmix = pcm->private_data;
310	snd_pcm_uframes_t slave_hw_ptr, slave_appl_ptr, slave_size;
311	snd_pcm_uframes_t appl_ptr, size, transfer;
312	const snd_pcm_channel_area_t *src_areas, *dst_areas;
313
314	/* calculate the size to transfer */
315	/* check the available size in the local buffer
316	 * last_appl_ptr keeps the last updated position
317	 */
318	size = dmix->appl_ptr - dmix->last_appl_ptr;
319	if (! size)
320		return;
321	if (size >= pcm->boundary / 2)
322		size = pcm->boundary - size;
323
324	/* the slave_app_ptr can be far behind the slave_hw_ptr */
325	/* reduce mixing and errors here - just skip not catched writes */
326	if (dmix->slave_hw_ptr <= dmix->slave_appl_ptr)
327		slave_size = dmix->slave_appl_ptr - dmix->slave_hw_ptr;
328	else
329		slave_size = dmix->slave_appl_ptr + (dmix->slave_boundary - dmix->slave_hw_ptr);
330	if (slave_size > dmix->slave_buffer_size) {
331		transfer = dmix->slave_buffer_size - slave_size;
332		if (transfer > size)
333			transfer = size;
334		dmix->last_appl_ptr += transfer;
335		dmix->last_appl_ptr %= pcm->boundary;
336		dmix->slave_appl_ptr += transfer;
337		dmix->slave_appl_ptr %= dmix->slave_boundary;
338		size = dmix->appl_ptr - dmix->last_appl_ptr;
339		if (! size)
340			return;
341		if (size >= pcm->boundary / 2)
342			size = pcm->boundary - size;
343	}
344
345	/* check the available size in the slave PCM buffer */
346	slave_hw_ptr = dmix->slave_hw_ptr;
347	/* don't write on the last active period - this area may be cleared
348	 * by the driver during mix operation...
349	 */
350	slave_hw_ptr -= slave_hw_ptr % dmix->slave_period_size;
351	slave_hw_ptr += dmix->slave_buffer_size;
352	if (slave_hw_ptr >= dmix->slave_boundary)
353		slave_hw_ptr -= dmix->slave_boundary;
354	if (slave_hw_ptr < dmix->slave_appl_ptr)
355		slave_size = slave_hw_ptr + (dmix->slave_boundary - dmix->slave_appl_ptr);
356	else
357		slave_size = slave_hw_ptr - dmix->slave_appl_ptr;
358	if (slave_size < size)
359		size = slave_size;
360	if (! size)
361		return;
362
363	/* add sample areas here */
364	src_areas = snd_pcm_mmap_areas(pcm);
365	dst_areas = snd_pcm_mmap_areas(dmix->spcm);
366	appl_ptr = dmix->last_appl_ptr % pcm->buffer_size;
367	dmix->last_appl_ptr += size;
368	dmix->last_appl_ptr %= pcm->boundary;
369	slave_appl_ptr = dmix->slave_appl_ptr % dmix->slave_buffer_size;
370	dmix->slave_appl_ptr += size;
371	dmix->slave_appl_ptr %= dmix->slave_boundary;
372	dmix_down_sem(dmix);
373	for (;;) {
374		transfer = size;
375		if (appl_ptr + transfer > pcm->buffer_size)
376			transfer = pcm->buffer_size - appl_ptr;
377		if (slave_appl_ptr + transfer > dmix->slave_buffer_size)
378			transfer = dmix->slave_buffer_size - slave_appl_ptr;
379		mix_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer);
380		size -= transfer;
381		if (! size)
382			break;
383		slave_appl_ptr += transfer;
384		slave_appl_ptr %= dmix->slave_buffer_size;
385		appl_ptr += transfer;
386		appl_ptr %= pcm->buffer_size;
387	}
388	dmix_up_sem(dmix);
389}
390
391/*
392 *  synchronize hardware pointer (hw_ptr) with ours
393 */
394static int snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm)
395{
396	snd_pcm_direct_t *dmix = pcm->private_data;
397	snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail;
398	snd_pcm_sframes_t diff;
399
400	switch (snd_pcm_state(dmix->spcm)) {
401	case SND_PCM_STATE_DISCONNECTED:
402		dmix->state = SND_PCM_STATE_DISCONNECTED;
403		return -ENODEV;
404	default:
405		break;
406	}
407	if (dmix->slowptr)
408		snd_pcm_hwsync(dmix->spcm);
409	old_slave_hw_ptr = dmix->slave_hw_ptr;
410	slave_hw_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
411	diff = slave_hw_ptr - old_slave_hw_ptr;
412	if (diff == 0)		/* fast path */
413		return 0;
414	if (dmix->state != SND_PCM_STATE_RUNNING &&
415	    dmix->state != SND_PCM_STATE_DRAINING)
416		/* not really started yet - don't update hw_ptr */
417		return 0;
418	if (diff < 0) {
419		slave_hw_ptr += dmix->slave_boundary;
420		diff = slave_hw_ptr - old_slave_hw_ptr;
421	}
422	dmix->hw_ptr += diff;
423	dmix->hw_ptr %= pcm->boundary;
424	if (pcm->stop_threshold >= pcm->boundary)	/* don't care */
425		return 0;
426	avail = snd_pcm_mmap_playback_avail(pcm);
427	if (avail > dmix->avail_max)
428		dmix->avail_max = avail;
429	if (avail >= pcm->stop_threshold) {
430		snd_timer_stop(dmix->timer);
431		gettimestamp(&dmix->trigger_tstamp, pcm->monotonic);
432		if (dmix->state == SND_PCM_STATE_RUNNING) {
433			dmix->state = SND_PCM_STATE_XRUN;
434			return -EPIPE;
435		}
436		dmix->state = SND_PCM_STATE_SETUP;
437		/* clear queue to remove pending poll events */
438		snd_pcm_direct_clear_timer_queue(dmix);
439	}
440	return 0;
441}
442
443/*
444 *  plugin implementation
445 */
446
447static snd_pcm_state_t snd_pcm_dmix_state(snd_pcm_t *pcm)
448{
449	snd_pcm_direct_t *dmix = pcm->private_data;
450	snd_pcm_state_t state;
451	state = snd_pcm_state(dmix->spcm);
452	switch (state) {
453	case SND_PCM_STATE_SUSPENDED:
454		return state;
455	case SND_PCM_STATE_DISCONNECTED:
456		return state;
457	default:
458		break;
459	}
460	if (dmix->state == STATE_RUN_PENDING)
461		return SNDRV_PCM_STATE_RUNNING;
462	return dmix->state;
463}
464
465static int snd_pcm_dmix_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
466{
467	snd_pcm_direct_t *dmix = pcm->private_data;
468
469	switch (dmix->state) {
470	case SNDRV_PCM_STATE_DRAINING:
471	case SNDRV_PCM_STATE_RUNNING:
472		snd_pcm_dmix_sync_ptr(pcm);
473		break;
474	default:
475		break;
476	}
477	memset(status, 0, sizeof(*status));
478	status->state = snd_pcm_dmix_state(pcm);
479	status->trigger_tstamp = dmix->trigger_tstamp;
480	gettimestamp(&status->tstamp, pcm->monotonic);
481	status->avail = snd_pcm_mmap_playback_avail(pcm);
482	status->avail_max = status->avail > dmix->avail_max ? status->avail : dmix->avail_max;
483	dmix->avail_max = 0;
484	return 0;
485}
486
487static int snd_pcm_dmix_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
488{
489	snd_pcm_direct_t *dmix = pcm->private_data;
490	int err;
491
492	switch(dmix->state) {
493	case SNDRV_PCM_STATE_DRAINING:
494	case SNDRV_PCM_STATE_RUNNING:
495		err = snd_pcm_dmix_sync_ptr(pcm);
496		if (err < 0)
497			return err;
498		/* fallthru */
499	case SNDRV_PCM_STATE_PREPARED:
500	case SNDRV_PCM_STATE_SUSPENDED:
501	case STATE_RUN_PENDING:
502		*delayp = snd_pcm_mmap_playback_hw_avail(pcm);
503		return 0;
504	case SNDRV_PCM_STATE_XRUN:
505		return -EPIPE;
506	case SNDRV_PCM_STATE_DISCONNECTED:
507		return -ENODEV;
508	default:
509		return -EBADFD;
510	}
511}
512
513static int snd_pcm_dmix_hwsync(snd_pcm_t *pcm)
514{
515	snd_pcm_direct_t *dmix = pcm->private_data;
516
517	switch(dmix->state) {
518	case SNDRV_PCM_STATE_DRAINING:
519	case SNDRV_PCM_STATE_RUNNING:
520		/* sync slave PCM */
521		return snd_pcm_dmix_sync_ptr(pcm);
522	case SNDRV_PCM_STATE_PREPARED:
523	case SNDRV_PCM_STATE_SUSPENDED:
524	case STATE_RUN_PENDING:
525		return 0;
526	case SNDRV_PCM_STATE_XRUN:
527		return -EPIPE;
528	case SNDRV_PCM_STATE_DISCONNECTED:
529		return -ENODEV;
530	default:
531		return -EBADFD;
532	}
533}
534
535static int snd_pcm_dmix_prepare(snd_pcm_t *pcm)
536{
537	snd_pcm_direct_t *dmix = pcm->private_data;
538
539	snd_pcm_direct_check_interleave(dmix, pcm);
540	dmix->state = SND_PCM_STATE_PREPARED;
541	dmix->appl_ptr = dmix->last_appl_ptr = 0;
542	dmix->hw_ptr = 0;
543	return snd_pcm_direct_set_timer_params(dmix);
544}
545
546static void reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix)
547{
548	dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
549	if (pcm->buffer_size > pcm->period_size * 2)
550		return;
551	/* If we have too litte periods, better to align the start position
552	 * to the period boundary so that the interrupt can be handled properly
553	 * at the right time.
554	 */
555	dmix->slave_appl_ptr = ((dmix->slave_appl_ptr + dmix->slave_period_size - 1)
556				/ dmix->slave_period_size) * dmix->slave_period_size;
557}
558
559static int snd_pcm_dmix_reset(snd_pcm_t *pcm)
560{
561	snd_pcm_direct_t *dmix = pcm->private_data;
562	dmix->hw_ptr %= pcm->period_size;
563	dmix->appl_ptr = dmix->last_appl_ptr = dmix->hw_ptr;
564	reset_slave_ptr(pcm, dmix);
565	return 0;
566}
567
568static int snd_pcm_dmix_start_timer(snd_pcm_t *pcm, snd_pcm_direct_t *dmix)
569{
570	int err;
571
572	snd_pcm_hwsync(dmix->spcm);
573	reset_slave_ptr(pcm, dmix);
574	err = snd_timer_start(dmix->timer);
575	if (err < 0)
576		return err;
577	dmix->state = SND_PCM_STATE_RUNNING;
578	return 0;
579}
580
581static int snd_pcm_dmix_start(snd_pcm_t *pcm)
582{
583	snd_pcm_direct_t *dmix = pcm->private_data;
584	snd_pcm_sframes_t avail;
585	int err;
586
587	if (dmix->state != SND_PCM_STATE_PREPARED)
588		return -EBADFD;
589	avail = snd_pcm_mmap_playback_hw_avail(pcm);
590	if (avail == 0)
591		dmix->state = STATE_RUN_PENDING;
592	else if (avail < 0)
593		return 0;
594	else {
595		if ((err = snd_pcm_dmix_start_timer(pcm, dmix)) < 0)
596			return err;
597		snd_pcm_dmix_sync_area(pcm);
598	}
599	gettimestamp(&dmix->trigger_tstamp, pcm->monotonic);
600	return 0;
601}
602
603static int snd_pcm_dmix_drop(snd_pcm_t *pcm)
604{
605	snd_pcm_direct_t *dmix = pcm->private_data;
606	if (dmix->state == SND_PCM_STATE_OPEN)
607		return -EBADFD;
608	dmix->state = SND_PCM_STATE_SETUP;
609	snd_pcm_direct_timer_stop(dmix);
610	return 0;
611}
612
613static int snd_pcm_dmix_drain(snd_pcm_t *pcm)
614{
615	snd_pcm_direct_t *dmix = pcm->private_data;
616	snd_pcm_uframes_t stop_threshold;
617	int err;
618
619	if (dmix->state == SND_PCM_STATE_OPEN)
620		return -EBADFD;
621	if (pcm->mode & SND_PCM_NONBLOCK)
622		return -EAGAIN;
623	if (dmix->state == SND_PCM_STATE_PREPARED) {
624		if (snd_pcm_mmap_playback_hw_avail(pcm) > 0)
625			snd_pcm_dmix_start(pcm);
626		else {
627			snd_pcm_dmix_drop(pcm);
628			return 0;
629		}
630	}
631
632	if (dmix->state == SND_PCM_STATE_XRUN) {
633		snd_pcm_dmix_drop(pcm);
634		return 0;
635	}
636
637	stop_threshold = pcm->stop_threshold;
638	if (pcm->stop_threshold > pcm->buffer_size)
639		pcm->stop_threshold = pcm->buffer_size;
640	dmix->state = SND_PCM_STATE_DRAINING;
641	do {
642		err = snd_pcm_dmix_sync_ptr(pcm);
643		if (err < 0) {
644			snd_pcm_dmix_drop(pcm);
645			return err;
646		}
647		if (dmix->state == SND_PCM_STATE_DRAINING) {
648			snd_pcm_dmix_sync_area(pcm);
649			snd_pcm_wait_nocheck(pcm, -1);
650			snd_pcm_direct_clear_timer_queue(dmix); /* force poll to wait */
651		}
652	} while (dmix->state == SND_PCM_STATE_DRAINING);
653	pcm->stop_threshold = stop_threshold;
654	return 0;
655}
656
657static int snd_pcm_dmix_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
658{
659	return -EIO;
660}
661
662static snd_pcm_sframes_t snd_pcm_dmix_rewindable(snd_pcm_t *pcm)
663{
664	return snd_pcm_mmap_hw_avail(pcm);
665}
666
667static snd_pcm_sframes_t snd_pcm_dmix_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
668{
669	snd_pcm_direct_t *dmix = pcm->private_data;
670	snd_pcm_uframes_t slave_appl_ptr, slave_size;
671	snd_pcm_uframes_t appl_ptr, size, transfer, result;
672	const snd_pcm_channel_area_t *src_areas, *dst_areas;
673
674	if (dmix->state == SND_PCM_STATE_RUNNING ||
675	    dmix->state == SND_PCM_STATE_DRAINING)
676	    	return snd_pcm_dmix_hwsync(pcm);
677
678	if (dmix->last_appl_ptr < dmix->appl_ptr)
679		size = dmix->appl_ptr - dmix->last_appl_ptr;
680	else
681		size = dmix->appl_ptr + (pcm->boundary - dmix->last_appl_ptr);
682	if (frames < size)
683		size = frames;
684	snd_pcm_mmap_appl_backward(pcm, size);
685	frames -= size;
686	if (!frames)
687		return size;
688	result = size;
689
690	if (dmix->hw_ptr < dmix->appl_ptr)
691		size = dmix->appl_ptr - dmix->hw_ptr;
692	else
693		size = dmix->appl_ptr + (pcm->boundary - dmix->hw_ptr);
694	if (size > frames)
695		size = frames;
696	if (dmix->slave_hw_ptr < dmix->slave_appl_ptr)
697		slave_size = dmix->slave_appl_ptr - dmix->slave_hw_ptr;
698	else
699		slave_size = dmix->slave_appl_ptr + (pcm->boundary - dmix->slave_hw_ptr);
700	if (slave_size < size)
701		size = slave_size;
702	frames -= size;
703	result += size;
704
705	/* add sample areas here */
706	src_areas = snd_pcm_mmap_areas(pcm);
707	dst_areas = snd_pcm_mmap_areas(dmix->spcm);
708	dmix->last_appl_ptr -= size;
709	dmix->last_appl_ptr %= pcm->boundary;
710	appl_ptr = dmix->last_appl_ptr % pcm->buffer_size;
711	dmix->slave_appl_ptr -= size;
712	dmix->slave_appl_ptr %= dmix->slave_boundary;
713	slave_appl_ptr = dmix->slave_appl_ptr % dmix->slave_buffer_size;
714	dmix_down_sem(dmix);
715	for (;;) {
716		transfer = size;
717		if (appl_ptr + transfer > pcm->buffer_size)
718			transfer = pcm->buffer_size - appl_ptr;
719		if (slave_appl_ptr + transfer > dmix->slave_buffer_size)
720			transfer = dmix->slave_buffer_size - slave_appl_ptr;
721		remix_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer);
722		size -= transfer;
723		if (! size)
724			break;
725		slave_appl_ptr += transfer;
726		slave_appl_ptr %= dmix->slave_buffer_size;
727		appl_ptr += transfer;
728		appl_ptr %= pcm->buffer_size;
729	}
730	dmix->last_appl_ptr -= frames;
731	dmix->last_appl_ptr %= pcm->boundary;
732	dmix->slave_appl_ptr -= frames;
733	dmix->slave_appl_ptr %= dmix->slave_boundary;
734	dmix_up_sem(dmix);
735
736	snd_pcm_mmap_appl_backward(pcm, frames);
737
738	return result + frames;
739}
740
741static snd_pcm_sframes_t snd_pcm_dmix_forwardable(snd_pcm_t *pcm)
742{
743	return snd_pcm_mmap_avail(pcm);
744}
745
746static snd_pcm_sframes_t snd_pcm_dmix_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
747{
748	snd_pcm_sframes_t avail;
749
750	avail = snd_pcm_mmap_playback_avail(pcm);
751	if (avail < 0)
752		return 0;
753	if (frames > (snd_pcm_uframes_t)avail)
754		frames = avail;
755	snd_pcm_mmap_appl_forward(pcm, frames);
756	return frames;
757}
758
759static 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)
760{
761	return -ENODEV;
762}
763
764static 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)
765{
766	return -ENODEV;
767}
768
769static int snd_pcm_dmix_close(snd_pcm_t *pcm)
770{
771	snd_pcm_direct_t *dmix = pcm->private_data;
772
773	if (dmix->timer)
774		snd_timer_close(dmix->timer);
775	snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
776	snd_pcm_close(dmix->spcm);
777 	if (dmix->server)
778 		snd_pcm_direct_server_discard(dmix);
779 	if (dmix->client)
780 		snd_pcm_direct_client_discard(dmix);
781 	shm_sum_discard(dmix);
782	if (snd_pcm_direct_shm_discard(dmix)) {
783		if (snd_pcm_direct_semaphore_discard(dmix))
784			snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
785	} else
786		snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
787	free(dmix->bindings);
788	pcm->private_data = NULL;
789	free(dmix);
790	return 0;
791}
792
793static snd_pcm_sframes_t snd_pcm_dmix_mmap_commit(snd_pcm_t *pcm,
794						  snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
795						  snd_pcm_uframes_t size)
796{
797	snd_pcm_direct_t *dmix = pcm->private_data;
798	int err;
799
800	switch (snd_pcm_state(dmix->spcm)) {
801	case SND_PCM_STATE_XRUN:
802		return -EPIPE;
803	case SND_PCM_STATE_SUSPENDED:
804		return -ESTRPIPE;
805	default:
806		break;
807	}
808	if (! size)
809		return 0;
810	snd_pcm_mmap_appl_forward(pcm, size);
811	if (dmix->state == STATE_RUN_PENDING) {
812		if ((err = snd_pcm_dmix_start_timer(pcm, dmix)) < 0)
813			return err;
814	} else if (dmix->state == SND_PCM_STATE_RUNNING ||
815		   dmix->state == SND_PCM_STATE_DRAINING)
816		snd_pcm_dmix_sync_ptr(pcm);
817	if (dmix->state == SND_PCM_STATE_RUNNING ||
818	    dmix->state == SND_PCM_STATE_DRAINING) {
819		/* ok, we commit the changes after the validation of area */
820		/* it's intended, although the result might be crappy */
821		snd_pcm_dmix_sync_area(pcm);
822		/* clear timer queue to avoid a bogus return from poll */
823		if (snd_pcm_mmap_playback_avail(pcm) < pcm->avail_min)
824			snd_pcm_direct_clear_timer_queue(dmix);
825	}
826	return size;
827}
828
829static snd_pcm_sframes_t snd_pcm_dmix_avail_update(snd_pcm_t *pcm)
830{
831	snd_pcm_direct_t *dmix = pcm->private_data;
832
833	if (dmix->state == SND_PCM_STATE_RUNNING ||
834	    dmix->state == SND_PCM_STATE_DRAINING)
835		snd_pcm_dmix_sync_ptr(pcm);
836	return snd_pcm_mmap_playback_avail(pcm);
837}
838
839static int snd_pcm_dmix_htimestamp(snd_pcm_t *pcm,
840				   snd_pcm_uframes_t *avail,
841				   snd_htimestamp_t *tstamp)
842{
843	snd_pcm_direct_t *dmix = pcm->private_data;
844	snd_pcm_uframes_t avail1;
845	int ok = 0;
846
847	while (1) {
848		if (dmix->state == SND_PCM_STATE_RUNNING ||
849		    dmix->state == SND_PCM_STATE_DRAINING)
850			snd_pcm_dmix_sync_ptr(pcm);
851		avail1 = snd_pcm_mmap_playback_avail(pcm);
852		if (ok && *avail == avail1)
853			break;
854		*avail = avail1;
855		*tstamp = snd_pcm_hw_fast_tstamp(dmix->spcm);
856	}
857	return 0;
858}
859
860static int snd_pcm_dmix_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
861{
862	snd_pcm_direct_t *dmix = pcm->private_data;
863	if (dmix->state == SND_PCM_STATE_RUNNING)
864		snd_pcm_dmix_sync_area(pcm);
865	return snd_pcm_direct_poll_revents(pcm, pfds, nfds, revents);
866}
867
868
869static void snd_pcm_dmix_dump(snd_pcm_t *pcm, snd_output_t *out)
870{
871	snd_pcm_direct_t *dmix = pcm->private_data;
872
873	snd_output_printf(out, "Direct Stream Mixing PCM\n");
874	if (pcm->setup) {
875		snd_output_printf(out, "Its setup is:\n");
876		snd_pcm_dump_setup(pcm, out);
877	}
878	if (dmix->spcm)
879		snd_pcm_dump(dmix->spcm, out);
880}
881
882static const snd_pcm_ops_t snd_pcm_dmix_ops = {
883	.close = snd_pcm_dmix_close,
884	.info = snd_pcm_direct_info,
885	.hw_refine = snd_pcm_direct_hw_refine,
886	.hw_params = snd_pcm_direct_hw_params,
887	.hw_free = snd_pcm_direct_hw_free,
888	.sw_params = snd_pcm_direct_sw_params,
889	.channel_info = snd_pcm_direct_channel_info,
890	.dump = snd_pcm_dmix_dump,
891	.nonblock = snd_pcm_direct_nonblock,
892	.async = snd_pcm_direct_async,
893	.mmap = snd_pcm_direct_mmap,
894	.munmap = snd_pcm_direct_munmap,
895};
896
897static const snd_pcm_fast_ops_t snd_pcm_dmix_fast_ops = {
898	.status = snd_pcm_dmix_status,
899	.state = snd_pcm_dmix_state,
900	.hwsync = snd_pcm_dmix_hwsync,
901	.delay = snd_pcm_dmix_delay,
902	.prepare = snd_pcm_dmix_prepare,
903	.reset = snd_pcm_dmix_reset,
904	.start = snd_pcm_dmix_start,
905	.drop = snd_pcm_dmix_drop,
906	.drain = snd_pcm_dmix_drain,
907	.pause = snd_pcm_dmix_pause,
908	.rewindable = snd_pcm_dmix_rewindable,
909	.rewind = snd_pcm_dmix_rewind,
910	.forwardable = snd_pcm_dmix_forwardable,
911	.forward = snd_pcm_dmix_forward,
912	.resume = snd_pcm_direct_resume,
913	.link = NULL,
914	.link_slaves = NULL,
915	.unlink = NULL,
916	.writei = snd_pcm_mmap_writei,
917	.writen = snd_pcm_mmap_writen,
918	.readi = snd_pcm_dmix_readi,
919	.readn = snd_pcm_dmix_readn,
920	.avail_update = snd_pcm_dmix_avail_update,
921	.mmap_commit = snd_pcm_dmix_mmap_commit,
922	.htimestamp = snd_pcm_dmix_htimestamp,
923	.poll_descriptors = NULL,
924	.poll_descriptors_count = NULL,
925	.poll_revents = snd_pcm_dmix_poll_revents,
926};
927
928/**
929 * \brief Creates a new dmix PCM
930 * \param pcmp Returns created PCM handle
931 * \param name Name of PCM
932 * \param opts Direct PCM configurations
933 * \param params Parameters for slave
934 * \param root Configuration root
935 * \param sconf Slave configuration
936 * \param stream PCM Direction (stream)
937 * \param mode PCM Mode
938 * \retval zero on success otherwise a negative error code
939 * \warning Using of this function might be dangerous in the sense
940 *          of compatibility reasons. The prototype might be freely
941 *          changed in future.
942 */
943int snd_pcm_dmix_open(snd_pcm_t **pcmp, const char *name,
944		      struct snd_pcm_direct_open_conf *opts,
945		      struct slave_params *params,
946		      snd_config_t *root, snd_config_t *sconf,
947		      snd_pcm_stream_t stream, int mode)
948{
949	snd_pcm_t *pcm = NULL, *spcm = NULL;
950	snd_pcm_direct_t *dmix = NULL;
951	int ret, first_instance;
952	int fail_sem_loop = 10;
953
954	assert(pcmp);
955
956	if (stream != SND_PCM_STREAM_PLAYBACK) {
957		SNDERR("The dmix plugin supports only playback stream");
958		return -EINVAL;
959	}
960
961	dmix = calloc(1, sizeof(snd_pcm_direct_t));
962	if (!dmix) {
963		ret = -ENOMEM;
964		goto _err_nosem;
965	}
966
967	ret = snd_pcm_direct_parse_bindings(dmix, params, opts->bindings);
968	if (ret < 0)
969		goto _err_nosem;
970
971	dmix->ipc_key = opts->ipc_key;
972	dmix->ipc_perm = opts->ipc_perm;
973	dmix->ipc_gid = opts->ipc_gid;
974	dmix->semid = -1;
975	dmix->shmid = -1;
976
977	ret = snd_pcm_new(&pcm, dmix->type = SND_PCM_TYPE_DMIX, name, stream, mode);
978	if (ret < 0)
979		goto _err;
980
981
982	while (1) {
983		ret = snd_pcm_direct_semaphore_create_or_connect(dmix);
984		if (ret < 0) {
985			SNDERR("unable to create IPC semaphore");
986			goto _err_nosem;
987		}
988		ret = snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
989		if (ret < 0) {
990			snd_pcm_direct_semaphore_discard(dmix);
991			if (--fail_sem_loop <= 0)
992				goto _err_nosem;
993			continue;
994		}
995		break;
996	}
997
998	first_instance = ret = snd_pcm_direct_shm_create_or_connect(dmix);
999	if (ret < 0) {
1000		SNDERR("unable to create IPC shm instance");
1001		goto _err;
1002	}
1003
1004	pcm->ops = &snd_pcm_dmix_ops;
1005	pcm->fast_ops = &snd_pcm_dmix_fast_ops;
1006	pcm->private_data = dmix;
1007	dmix->state = SND_PCM_STATE_OPEN;
1008	dmix->slowptr = opts->slowptr;
1009	dmix->max_periods = opts->max_periods;
1010	dmix->sync_ptr = snd_pcm_dmix_sync_ptr;
1011
1012	if (first_instance) {
1013		/* recursion is already checked in
1014		   snd_pcm_direct_get_slave_ipc_offset() */
1015		ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
1016					 mode | SND_PCM_NONBLOCK, NULL);
1017		if (ret < 0) {
1018			SNDERR("unable to open slave");
1019			goto _err;
1020		}
1021
1022		if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
1023			SNDERR("dmix plugin can be only connected to hw plugin");
1024			ret = -EINVAL;
1025			goto _err;
1026		}
1027
1028		ret = snd_pcm_direct_initialize_slave(dmix, spcm, params);
1029		if (ret < 0) {
1030			SNDERR("unable to initialize slave");
1031			goto _err;
1032		}
1033
1034		dmix->spcm = spcm;
1035
1036		if (dmix->shmptr->use_server) {
1037			dmix->server_free = dmix_server_free;
1038
1039			ret = snd_pcm_direct_server_create(dmix);
1040			if (ret < 0) {
1041				SNDERR("unable to create server");
1042				goto _err;
1043			}
1044		}
1045
1046		dmix->shmptr->type = spcm->type;
1047	} else {
1048		if (dmix->shmptr->use_server) {
1049			/* up semaphore to avoid deadlock */
1050			snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
1051			ret = snd_pcm_direct_client_connect(dmix);
1052			if (ret < 0) {
1053				SNDERR("unable to connect client");
1054				goto _err_nosem;
1055			}
1056
1057			snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
1058			ret = snd_pcm_direct_open_secondary_client(&spcm, dmix, "dmix_client");
1059			if (ret < 0)
1060				goto _err;
1061		} else {
1062
1063			ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
1064						 mode | SND_PCM_NONBLOCK |
1065						 SND_PCM_APPEND,
1066						 NULL);
1067			if (ret < 0) {
1068				SNDERR("unable to open slave");
1069				goto _err;
1070			}
1071			if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
1072				SNDERR("dmix plugin can be only connected to hw plugin");
1073				ret = -EINVAL;
1074				goto _err;
1075			}
1076
1077			ret = snd_pcm_direct_initialize_secondary_slave(dmix, spcm, params);
1078			if (ret < 0) {
1079				SNDERR("unable to initialize slave");
1080				goto _err;
1081			}
1082		}
1083
1084		dmix->spcm = spcm;
1085	}
1086
1087	ret = shm_sum_create_or_connect(dmix);
1088	if (ret < 0) {
1089		SNDERR("unable to initialize sum ring buffer");
1090		goto _err;
1091	}
1092
1093	ret = snd_pcm_direct_initialize_poll_fd(dmix);
1094	if (ret < 0) {
1095		SNDERR("unable to initialize poll_fd");
1096		goto _err;
1097	}
1098
1099	mix_select_callbacks(dmix);
1100
1101	pcm->poll_fd = dmix->poll_fd;
1102	pcm->poll_events = POLLIN;	/* it's different than other plugins */
1103
1104	pcm->mmap_rw = 1;
1105	snd_pcm_set_hw_ptr(pcm, &dmix->hw_ptr, -1, 0);
1106	snd_pcm_set_appl_ptr(pcm, &dmix->appl_ptr, -1, 0);
1107
1108	if (dmix->channels == UINT_MAX)
1109		dmix->channels = dmix->shmptr->s.channels;
1110
1111	snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
1112
1113	*pcmp = pcm;
1114	return 0;
1115
1116 _err:
1117	if (dmix->timer)
1118		snd_timer_close(dmix->timer);
1119	if (dmix->server)
1120		snd_pcm_direct_server_discard(dmix);
1121	if (dmix->client)
1122		snd_pcm_direct_client_discard(dmix);
1123	if (spcm)
1124		snd_pcm_close(spcm);
1125	if (dmix->u.dmix.shmid_sum >= 0)
1126		shm_sum_discard(dmix);
1127	if (dmix->shmid >= 0)
1128		snd_pcm_direct_shm_discard(dmix);
1129	if (snd_pcm_direct_semaphore_discard(dmix) < 0)
1130		snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
1131 _err_nosem:
1132	if (dmix) {
1133		free(dmix->bindings);
1134		free(dmix);
1135	}
1136	if (pcm)
1137		snd_pcm_free(pcm);
1138	return ret;
1139}
1140
1141/*! \page pcm_plugins
1142
1143\section pcm_plugins_dmix Plugin: dmix
1144
1145This plugin provides direct mixing of multiple streams. The resolution
1146for 32-bit mixing is only 24-bit. The low significant byte is filled with
1147zeros. The extra 8 bits are used for the saturation.
1148
1149\code
1150pcm.name {
1151	type dmix		# Direct mix
1152	ipc_key INT		# unique IPC key
1153	ipc_key_add_uid BOOL	# add current uid to unique IPC key
1154	ipc_perm INT		# IPC permissions (octal, default 0600)
1155	slave STR
1156	# or
1157	slave {			# Slave definition
1158		pcm STR		# slave PCM name
1159		# or
1160		pcm { }		# slave PCM definition
1161		format STR	# format definition
1162		rate INT	# rate definition
1163		channels INT
1164		period_time INT	# in usec
1165		# or
1166		period_size INT	# in bytes
1167		buffer_time INT	# in usec
1168		# or
1169		buffer_size INT # in bytes
1170		periods INT	# when buffer_size or buffer_time is not specified
1171	}
1172	bindings {		# note: this is client independent!!!
1173		N INT		# maps slave channel to client channel N
1174	}
1175	slowptr BOOL		# slow but more precise pointer updates
1176}
1177\endcode
1178
1179<code>ipc_key</code> specfies the unique IPC key in integer.
1180This number must be unique for each different dmix definition,
1181since the shared memory is created with this key number.
1182When <code>ipc_key_add_uid</code> is set true, the uid value is
1183added to the value set in <code>ipc_key</code>.  This will
1184avoid the confliction of the same IPC key with different users
1185concurrently.
1186
1187Note that the dmix plugin itself supports only a single configuration.
1188That is, it supports only the fixed rate (default 48000), format
1189(\c S16), channels (2), and period_time (125000).
1190For using other configuration, you have to set the value explicitly
1191in the slave PCM definition.  The rate, format and channels can be
1192covered by an additional \ref pcm_plugins_dmix "plug plugin",
1193but there is only one base configuration, anyway.
1194
1195An example configuration for setting 44100 Hz, \c S32_LE format
1196as the slave PCM of "hw:0" is like below:
1197\code
1198pcm.dmix_44 {
1199	type dmix
1200	ipc_key 321456	# any unique value
1201	ipc_key_add_uid true
1202	slave {
1203		pcm "hw:0"
1204		format S32_LE
1205		rate 44100
1206	}
1207}
1208\endcode
1209You can hear 48000 Hz samples still using this dmix pcm via plug plugin
1210like:
1211\code
1212% aplay -Dplug:dmix_44 foo_48k.wav
1213\endcode
1214
1215For using the dmix plugin for OSS emulation device, you have to set
1216the period and the buffer sizes in power of two.  For example,
1217\code
1218pcm.dmixoss {
1219	type dmix
1220	ipc_key 321456	# any unique value
1221	ipc_key_add_uid true
1222	slave {
1223		pcm "hw:0"
1224		period_time 0
1225		period_size 1024  # must be power of 2
1226		buffer_size 8192  # ditto
1227	}
1228}
1229\endcode
1230<code>period_time 0</code> must be set, too, for resetting the
1231default value.  In the case of soundcards with multi-channel IO,
1232adding the bindings would help
1233\code
1234pcm.dmixoss {
1235	...
1236	bindings {
1237		0 0   # map from 0 to 0
1238		1 1   # map from 1 to 1
1239	}
1240}
1241\endcode
1242so that only the first two channels are used by dmix.
1243Also, note that ICE1712 have the limited buffer size, 5513 frames
1244(corresponding to 640 kB).  In this case, reduce the buffer_size
1245to 4096.
1246
1247\subsection pcm_plugins_dmix_funcref Function reference
1248
1249<UL>
1250  <LI>snd_pcm_dmix_open()
1251  <LI>_snd_pcm_dmix_open()
1252</UL>
1253
1254*/
1255
1256/**
1257 * \brief Creates a new dmix PCM
1258 * \param pcmp Returns created PCM handle
1259 * \param name Name of PCM
1260 * \param root Root configuration node
1261 * \param conf Configuration node with dmix PCM description
1262 * \param stream PCM Stream
1263 * \param mode PCM Mode
1264 * \warning Using of this function might be dangerous in the sense
1265 *          of compatibility reasons. The prototype might be freely
1266 *          changed in future.
1267 */
1268int _snd_pcm_dmix_open(snd_pcm_t **pcmp, const char *name,
1269		       snd_config_t *root, snd_config_t *conf,
1270		       snd_pcm_stream_t stream, int mode)
1271{
1272	snd_config_t *sconf;
1273	struct slave_params params;
1274	struct snd_pcm_direct_open_conf dopen;
1275	int bsize, psize;
1276	int err;
1277
1278	err = snd_pcm_direct_parse_open_conf(root, conf, stream, &dopen);
1279	if (err < 0)
1280		return err;
1281
1282	/* the default settings, it might be invalid for some hardware */
1283	params.format = SND_PCM_FORMAT_S16;
1284	params.rate = 48000;
1285	params.channels = 2;
1286	params.period_time = -1;
1287	params.buffer_time = -1;
1288	bsize = psize = -1;
1289	params.periods = 3;
1290
1291	err = snd_pcm_slave_conf(root, dopen.slave, &sconf, 8,
1292				 SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &params.format,
1293				 SND_PCM_HW_PARAM_RATE, 0, &params.rate,
1294				 SND_PCM_HW_PARAM_CHANNELS, 0, &params.channels,
1295				 SND_PCM_HW_PARAM_PERIOD_TIME, 0, &params.period_time,
1296				 SND_PCM_HW_PARAM_BUFFER_TIME, 0, &params.buffer_time,
1297				 SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &psize,
1298				 SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &bsize,
1299				 SND_PCM_HW_PARAM_PERIODS, 0, &params.periods);
1300	if (err < 0)
1301		return err;
1302
1303	/* set a reasonable default */
1304	if (psize == -1 && params.period_time == -1)
1305		params.period_time = 125000;    /* 0.125 seconds */
1306
1307	if (params.format == -2)
1308		params.format = SND_PCM_FORMAT_UNKNOWN;
1309	else if (!(dmix_supported_format & (1ULL << params.format))) {
1310		/* sorry, limited features */
1311		SNDERR("Unsupported format");
1312		snd_config_delete(sconf);
1313		return -EINVAL;
1314	}
1315
1316	params.period_size = psize;
1317	params.buffer_size = bsize;
1318
1319	err = snd_pcm_dmix_open(pcmp, name, &dopen, &params,
1320				root, sconf, stream, mode);
1321	snd_config_delete(sconf);
1322	return err;
1323}
1324#ifndef DOC_HIDDEN
1325SND_DLSYM_BUILD_VERSION(_snd_pcm_dmix_open, SND_PCM_DLSYM_VERSION);
1326#endif
1327