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