xref: /third_party/alsa-lib/src/pcm/pcm_multi.c (revision d5ac70f0)
1/**
2 * \file pcm/pcm_multi.c
3 * \ingroup PCM_Plugins
4 * \brief PCM Multi Streams to One Conversion Plugin Interface
5 * \author Abramo Bagnara <abramo@alsa-project.org>
6 * \date 2000-2001
7 */
8/*
9 *  PCM - Multi
10 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26 *
27 */
28
29#include "pcm_local.h"
30#include "pcm_generic.h"
31#include <stdio.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include <string.h>
35#include <math.h>
36
37#ifndef PIC
38/* entry for static linking */
39const char *_snd_module_pcm_multi = "";
40#endif
41
42#ifndef DOC_HIDDEN
43
44typedef struct {
45	snd_pcm_t *pcm;
46	unsigned int channels_count;
47	int close_slave;
48	snd_pcm_t *linked;
49} snd_pcm_multi_slave_t;
50
51typedef struct {
52	int slave_idx;
53	unsigned int slave_channel;
54} snd_pcm_multi_channel_t;
55
56typedef struct {
57	snd_pcm_uframes_t appl_ptr, hw_ptr;
58	unsigned int slaves_count;
59	unsigned int master_slave;
60	snd_pcm_multi_slave_t *slaves;
61	unsigned int channels_count;
62	snd_pcm_multi_channel_t *channels;
63} snd_pcm_multi_t;
64
65#endif
66
67static int snd_pcm_multi_close(snd_pcm_t *pcm)
68{
69	snd_pcm_multi_t *multi = pcm->private_data;
70	unsigned int i;
71	int ret = 0;
72	for (i = 0; i < multi->slaves_count; ++i) {
73		snd_pcm_multi_slave_t *slave = &multi->slaves[i];
74		if (slave->close_slave) {
75			int err = snd_pcm_close(slave->pcm);
76			if (err < 0)
77				ret = err;
78		}
79	}
80	free(multi->slaves);
81	free(multi->channels);
82	free(multi);
83	return ret;
84}
85
86static int snd_pcm_multi_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
87{
88	return 0;
89}
90
91static int snd_pcm_multi_async(snd_pcm_t *pcm, int sig, pid_t pid)
92{
93	snd_pcm_multi_t *multi = pcm->private_data;
94	snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
95	return snd_pcm_async(slave_0, sig, pid);
96}
97
98static int snd_pcm_multi_poll_descriptors_count(snd_pcm_t *pcm)
99{
100	snd_pcm_multi_t *multi = pcm->private_data;
101	snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
102	return snd_pcm_poll_descriptors_count(slave_0);
103}
104
105static int snd_pcm_multi_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
106{
107	snd_pcm_multi_t *multi = pcm->private_data;
108	snd_pcm_t *slave;
109	snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
110	int err;
111	unsigned int i;
112
113	for (i = 0; i < multi->slaves_count; ++i) {
114		slave = multi->slaves[i].pcm;
115		if (slave == slave_0)
116			continue;
117		err = snd_pcm_poll_descriptors(slave, pfds, space);
118		if (err < 0)
119			return err;
120	}
121	/* finally overwrite with master's pfds */
122	return snd_pcm_poll_descriptors(slave_0, pfds, space);
123}
124
125static int snd_pcm_multi_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
126{
127	snd_pcm_multi_t *multi = pcm->private_data;
128	snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
129	return snd_pcm_poll_descriptors_revents(slave_0, pfds, nfds, revents);
130}
131
132static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
133{
134	snd_pcm_multi_t *multi = pcm->private_data;
135	int err, n;
136	assert(info->subdevice < multi->slaves_count);
137	n = info->subdevice;
138	info->subdevice = 0;
139	err = snd_pcm_info(multi->slaves[n].pcm, info);
140	if (err < 0)
141		return err;
142	info->subdevices_count = multi->slaves_count;
143	return 0;
144}
145
146static int snd_pcm_multi_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
147{
148	snd_pcm_multi_t *multi = pcm->private_data;
149	snd_pcm_access_mask_t access_mask;
150	int err;
151	snd_pcm_access_mask_any(&access_mask);
152	snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
153	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
154					 &access_mask);
155	if (err < 0)
156		return err;
157	err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_CHANNELS,
158				    multi->channels_count, 0);
159	if (err < 0)
160		return err;
161	params->info = ~0U;
162	return 0;
163}
164
165static int snd_pcm_multi_hw_refine_sprepare(snd_pcm_t *pcm, unsigned int slave_idx,
166					    snd_pcm_hw_params_t *sparams)
167{
168	snd_pcm_multi_t *multi = pcm->private_data;
169	snd_pcm_multi_slave_t *slave = &multi->slaves[slave_idx];
170	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
171	_snd_pcm_hw_params_any(sparams);
172	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
173				   &saccess_mask);
174	_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
175			      slave->channels_count, 0);
176	return 0;
177}
178
179static int snd_pcm_multi_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
180					   unsigned int slave_idx ATTRIBUTE_UNUSED,
181					   snd_pcm_hw_params_t *params,
182					   snd_pcm_hw_params_t *sparams)
183{
184	int err;
185	unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
186			      SND_PCM_HW_PARBIT_SUBFORMAT |
187			      SND_PCM_HW_PARBIT_RATE |
188			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
189			      SND_PCM_HW_PARBIT_PERIOD_TIME |
190			      SND_PCM_HW_PARBIT_PERIODS |
191			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
192			      SND_PCM_HW_PARBIT_BUFFER_TIME |
193			      SND_PCM_HW_PARBIT_TICK_TIME);
194	const snd_pcm_access_mask_t *access_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
195	if (!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
196	    !snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) &&
197	    !snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
198		snd_pcm_access_mask_t saccess_mask;
199		snd_pcm_access_mask_any(&saccess_mask);
200		snd_pcm_access_mask_reset(&saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
201		err = _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
202						 &saccess_mask);
203		if (err < 0)
204			return err;
205	}
206	err = _snd_pcm_hw_params_refine(sparams, links, params);
207	if (err < 0)
208		return err;
209	return 0;
210}
211
212static int snd_pcm_multi_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
213					   unsigned int slave_idx ATTRIBUTE_UNUSED,
214					   snd_pcm_hw_params_t *params,
215					   snd_pcm_hw_params_t *sparams)
216{
217	int err;
218	unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
219			      SND_PCM_HW_PARBIT_SUBFORMAT |
220			      SND_PCM_HW_PARBIT_RATE |
221			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
222			      SND_PCM_HW_PARBIT_PERIOD_TIME |
223			      SND_PCM_HW_PARBIT_PERIODS |
224			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
225			      SND_PCM_HW_PARBIT_BUFFER_TIME |
226			      SND_PCM_HW_PARBIT_TICK_TIME);
227	snd_pcm_access_mask_t access_mask;
228	const snd_pcm_access_mask_t *saccess_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS);
229	snd_pcm_access_mask_any(&access_mask);
230	snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
231	if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
232		snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
233	if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_COMPLEX) &&
234	    !snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED))
235		snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_COMPLEX);
236	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
237					 &access_mask);
238	if (err < 0)
239		return err;
240	err = _snd_pcm_hw_params_refine(params, links, sparams);
241	if (err < 0)
242		return err;
243	params->info &= sparams->info;
244	return 0;
245}
246
247static int snd_pcm_multi_hw_refine_slave(snd_pcm_t *pcm,
248					 unsigned int slave_idx,
249					 snd_pcm_hw_params_t *sparams)
250{
251	snd_pcm_multi_t *multi = pcm->private_data;
252	snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
253	return snd_pcm_hw_refine(slave, sparams);
254}
255
256static int snd_pcm_multi_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
257{
258	snd_pcm_multi_t *multi = pcm->private_data;
259	unsigned int k;
260	snd_pcm_hw_params_t sparams[multi->slaves_count];
261	int err;
262	unsigned int cmask, changed;
263	err = snd_pcm_multi_hw_refine_cprepare(pcm, params);
264	if (err < 0)
265		return err;
266	for (k = 0; k < multi->slaves_count; ++k) {
267		err = snd_pcm_multi_hw_refine_sprepare(pcm, k, &sparams[k]);
268		if (err < 0) {
269			SNDERR("Slave PCM #%d not usable", k);
270			return err;
271		}
272	}
273	do {
274		cmask = params->cmask;
275		params->cmask = 0;
276		for (k = 0; k < multi->slaves_count; ++k) {
277			err = snd_pcm_multi_hw_refine_schange(pcm, k, params, &sparams[k]);
278			if (err >= 0)
279				err = snd_pcm_multi_hw_refine_slave(pcm, k, &sparams[k]);
280			if (err < 0) {
281				snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
282				return err;
283			}
284			err = snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
285			if (err < 0)
286				return err;
287		}
288		err = snd_pcm_hw_refine_soft(pcm, params);
289		changed = params->cmask;
290		params->cmask |= cmask;
291		if (err < 0)
292			return err;
293	} while (changed);
294	return 0;
295}
296
297static int snd_pcm_multi_hw_params_slave(snd_pcm_t *pcm,
298					 unsigned int slave_idx,
299					 snd_pcm_hw_params_t *sparams)
300{
301	snd_pcm_multi_t *multi = pcm->private_data;
302	snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
303	int err = snd_pcm_hw_params(slave, sparams);
304	if (err < 0)
305		return err;
306	err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format);
307	if (err < 0)
308		return err;
309	if (slave->stopped_areas) {
310		err = snd_pcm_areas_silence(slave->stopped_areas, 0, slave->channels, slave->buffer_size, slave->format);
311		if (err < 0)
312			return err;
313	}
314	return 0;
315}
316
317/* reset links to the normal state
318 * slave #0 = trigger master
319 * slave #1-(N-1) = trigger slaves, linked is set to #0
320 */
321static void reset_links(snd_pcm_multi_t *multi)
322{
323	unsigned int i;
324
325	for (i = 0; i < multi->slaves_count; ++i) {
326		if (multi->slaves[i].linked)
327			snd_pcm_unlink(multi->slaves[i].linked);
328		multi->slaves[0].linked = NULL;
329		if (! i)
330			continue;
331		if (snd_pcm_link(multi->slaves[0].pcm, multi->slaves[i].pcm) >= 0)
332			multi->slaves[i].linked = multi->slaves[0].pcm;
333	}
334}
335
336static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
337{
338	snd_pcm_multi_t *multi = pcm->private_data;
339	unsigned int i;
340	snd_pcm_hw_params_t sparams[multi->slaves_count];
341	int err;
342	for (i = 0; i < multi->slaves_count; ++i) {
343		err = snd_pcm_multi_hw_refine_sprepare(pcm, i, &sparams[i]);
344		assert(err >= 0);
345		err = snd_pcm_multi_hw_refine_schange(pcm, i, params, &sparams[i]);
346		assert(err >= 0);
347		err = snd_pcm_multi_hw_params_slave(pcm, i, &sparams[i]);
348		if (err < 0) {
349			snd_pcm_multi_hw_refine_cchange(pcm, i, params, &sparams[i]);
350			return err;
351		}
352	}
353	reset_links(multi);
354	return 0;
355}
356
357static int snd_pcm_multi_hw_free(snd_pcm_t *pcm)
358{
359	snd_pcm_multi_t *multi = pcm->private_data;
360	unsigned int i;
361	int err = 0;
362	for (i = 0; i < multi->slaves_count; ++i) {
363		snd_pcm_t *slave = multi->slaves[i].pcm;
364		int e = snd_pcm_hw_free(slave);
365		if (e < 0)
366			err = e;
367		if (!multi->slaves[i].linked)
368			continue;
369		e = snd_pcm_unlink(slave);
370		if (e < 0)
371			err = e;
372		multi->slaves[i].linked = NULL;
373	}
374	return err;
375}
376
377static int snd_pcm_multi_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
378{
379	snd_pcm_multi_t *multi = pcm->private_data;
380	unsigned int i;
381	int err;
382	for (i = 0; i < multi->slaves_count; ++i) {
383		snd_pcm_t *slave = multi->slaves[i].pcm;
384		err = snd_pcm_sw_params(slave, params);
385		if (err < 0)
386			return err;
387	}
388	return 0;
389}
390
391static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm);
392static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
393{
394	snd_pcm_multi_t *multi = pcm->private_data;
395	snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
396
397	int err = snd_pcm_status(slave, status);
398	if (err < 0)
399		return err;
400	snd_pcm_sframes_t avail = snd_pcm_multi_avail_update(pcm);
401	if (avail < 0)
402		return avail;
403	status->hw_ptr = *pcm->hw.ptr;
404	status->avail = avail;
405	return 0;
406}
407
408static snd_pcm_state_t snd_pcm_multi_state(snd_pcm_t *pcm)
409{
410	snd_pcm_multi_t *multi = pcm->private_data;
411	snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
412	return snd_pcm_state(slave);
413}
414
415static void snd_pcm_multi_hwptr_update(snd_pcm_t *pcm)
416{
417	snd_pcm_multi_t *multi = pcm->private_data;
418	snd_pcm_uframes_t hw_ptr = 0, slave_hw_ptr, avail, last_avail;
419	unsigned int i;
420	/* the logic is really simple, choose the lowest hw_ptr from slaves */
421	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
422		last_avail = 0;
423		for (i = 0; i < multi->slaves_count; ++i) {
424			slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr;
425			avail = __snd_pcm_playback_avail(pcm, multi->hw_ptr, slave_hw_ptr);
426			if (avail > last_avail) {
427				hw_ptr = slave_hw_ptr;
428				last_avail = avail;
429			}
430		}
431	} else {
432		last_avail = LONG_MAX;
433		for (i = 0; i < multi->slaves_count; ++i) {
434			slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr;
435			avail = __snd_pcm_capture_avail(pcm, multi->hw_ptr, slave_hw_ptr);
436			if (avail < last_avail) {
437				hw_ptr = slave_hw_ptr;
438				last_avail = avail;
439			}
440		}
441	}
442	multi->hw_ptr = hw_ptr;
443}
444
445static int snd_pcm_multi_hwsync(snd_pcm_t *pcm)
446{
447	snd_pcm_multi_t *multi = pcm->private_data;
448	unsigned int i;
449	int err;
450	for (i = 0; i < multi->slaves_count; ++i) {
451		err = snd_pcm_hwsync(multi->slaves[i].pcm);
452		if (err < 0)
453			return err;
454	}
455	snd_pcm_multi_hwptr_update(pcm);
456	return 0;
457}
458
459static int snd_pcm_multi_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
460{
461	snd_pcm_multi_t *multi = pcm->private_data;
462	snd_pcm_sframes_t d, dr = 0;
463	unsigned int i;
464	int err;
465	for (i = 0; i < multi->slaves_count; ++i) {
466		err = snd_pcm_delay(multi->slaves[i].pcm, &d);
467		if (err < 0)
468			return err;
469		if (dr < d)
470			dr = d;
471	}
472	*delayp = dr;
473	return 0;
474}
475
476static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm)
477{
478	snd_pcm_multi_t *multi = pcm->private_data;
479	snd_pcm_sframes_t ret = LONG_MAX;
480	unsigned int i;
481	for (i = 0; i < multi->slaves_count; ++i) {
482		snd_pcm_sframes_t avail;
483		avail = snd_pcm_avail_update(multi->slaves[i].pcm);
484		if (avail < 0)
485			return avail;
486		if (ret > avail)
487			ret = avail;
488	}
489	snd_pcm_multi_hwptr_update(pcm);
490	return ret;
491}
492
493static int snd_pcm_multi_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail,
494				    snd_htimestamp_t *tstamp)
495{
496	snd_pcm_multi_t *multi = pcm->private_data;
497	snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
498	return snd_pcm_htimestamp(slave, avail, tstamp);
499}
500
501static int snd_pcm_multi_prepare(snd_pcm_t *pcm)
502{
503	snd_pcm_multi_t *multi = pcm->private_data;
504	int result = 0, err;
505	unsigned int i;
506	for (i = 0; i < multi->slaves_count; ++i) {
507		/* We call prepare to each slave even if it's linked.
508		 * This is to make sure to sync non-mmaped control/status.
509		 */
510		err = snd_pcm_prepare(multi->slaves[i].pcm);
511		if (err < 0)
512			result = err;
513	}
514	multi->hw_ptr = multi->appl_ptr = 0;
515	return result;
516}
517
518static int snd_pcm_multi_reset(snd_pcm_t *pcm)
519{
520	snd_pcm_multi_t *multi = pcm->private_data;
521	int result = 0, err;
522	unsigned int i;
523	for (i = 0; i < multi->slaves_count; ++i) {
524		/* Reset each slave, as well as in prepare */
525		err = snd_pcm_reset(multi->slaves[i].pcm);
526		if (err < 0)
527			result = err;
528	}
529	multi->hw_ptr = multi->appl_ptr = 0;
530	return result;
531}
532
533/* when the first slave PCM is linked, it means that the whole multi
534 * plugin instance is linked manually to another PCM.  in this case,
535 * we need to trigger the master.
536 */
537static int snd_pcm_multi_start(snd_pcm_t *pcm)
538{
539	snd_pcm_multi_t *multi = pcm->private_data;
540	int err = 0;
541	unsigned int i;
542	if (multi->slaves[0].linked)
543		return snd_pcm_start(multi->slaves[0].linked);
544	for (i = 0; i < multi->slaves_count; ++i) {
545		if (multi->slaves[i].linked)
546			continue;
547		err = snd_pcm_start(multi->slaves[i].pcm);
548		if (err < 0)
549			return err;
550	}
551	return err;
552}
553
554static int snd_pcm_multi_drop(snd_pcm_t *pcm)
555{
556	snd_pcm_multi_t *multi = pcm->private_data;
557	int err = 0;
558	unsigned int i;
559	if (multi->slaves[0].linked)
560		return snd_pcm_drop(multi->slaves[0].linked);
561	for (i = 0; i < multi->slaves_count; ++i) {
562		if (multi->slaves[i].linked)
563			continue;
564		err = snd_pcm_drop(multi->slaves[i].pcm);
565		if (err < 0)
566			return err;
567	}
568	return err;
569}
570
571static int snd_pcm_multi_drain(snd_pcm_t *pcm)
572{
573	snd_pcm_multi_t *multi = pcm->private_data;
574	int err = 0;
575	unsigned int i;
576	if (multi->slaves[0].linked)
577		return snd_pcm_drain(multi->slaves[0].linked);
578	for (i = 0; i < multi->slaves_count; ++i) {
579		if (multi->slaves[i].linked)
580			continue;
581		err = snd_pcm_drain(multi->slaves[i].pcm);
582		if (err < 0)
583			return err;
584	}
585	return err;
586}
587
588static int snd_pcm_multi_pause(snd_pcm_t *pcm, int enable)
589{
590	snd_pcm_multi_t *multi = pcm->private_data;
591	int err = 0;
592	unsigned int i;
593	if (multi->slaves[0].linked)
594		return snd_pcm_pause(multi->slaves[0].linked, enable);
595	for (i = 0; i < multi->slaves_count; ++i) {
596		if (multi->slaves[i].linked)
597			continue;
598		err = snd_pcm_pause(multi->slaves[i].pcm, enable);
599		if (err < 0)
600			return err;
601	}
602	return err;
603}
604
605static int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
606{
607	snd_pcm_multi_t *multi = pcm->private_data;
608	unsigned int channel = info->channel;
609	snd_pcm_multi_channel_t *c = &multi->channels[channel];
610	int err;
611	if (c->slave_idx < 0)
612		return -ENXIO;
613	info->channel = c->slave_channel;
614	err = snd_pcm_channel_info(multi->slaves[c->slave_idx].pcm, info);
615	info->channel = channel;
616	return err;
617}
618
619static snd_pcm_sframes_t snd_pcm_multi_rewindable(snd_pcm_t *pcm)
620{
621	snd_pcm_multi_t *multi = pcm->private_data;
622	unsigned int i;
623	snd_pcm_sframes_t frames = LONG_MAX;
624
625	for (i = 0; i < multi->slaves_count; ++i) {
626		snd_pcm_sframes_t f = snd_pcm_rewindable(multi->slaves[i].pcm);
627		if (f <= 0)
628			return f;
629		if (f < frames)
630			frames = f;
631	}
632
633	return frames;
634
635}
636
637static snd_pcm_sframes_t snd_pcm_multi_forwardable(snd_pcm_t *pcm)
638{
639	snd_pcm_multi_t *multi = pcm->private_data;
640	unsigned int i;
641	snd_pcm_sframes_t frames = LONG_MAX;
642
643	for (i = 0; i < multi->slaves_count; ++i) {
644		snd_pcm_sframes_t f = snd_pcm_forwardable(multi->slaves[i].pcm);
645		if (f <= 0)
646			return f;
647		if (f < frames)
648			frames = f;
649	}
650
651	return frames;
652
653}
654
655static snd_pcm_sframes_t snd_pcm_multi_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
656{
657	snd_pcm_multi_t *multi = pcm->private_data;
658	unsigned int i;
659	snd_pcm_uframes_t pos[multi->slaves_count];
660	memset(pos, 0, sizeof(pos));
661	for (i = 0; i < multi->slaves_count; ++i) {
662		snd_pcm_t *slave_i = multi->slaves[i].pcm;
663		snd_pcm_sframes_t f = snd_pcm_rewind(slave_i, frames);
664		if (f < 0)
665			return f;
666		pos[i] = f;
667		frames = f;
668	}
669	/* Realign the pointers */
670	for (i = 0; i < multi->slaves_count; ++i) {
671		snd_pcm_t *slave_i = multi->slaves[i].pcm;
672		snd_pcm_uframes_t f = pos[i] - frames;
673		snd_pcm_sframes_t result;
674		if (f > 0) {
675			result = INTERNAL(snd_pcm_forward)(slave_i, f);
676			if (result < 0)
677				return result;
678			if ((snd_pcm_uframes_t)result != f)
679				return -EIO;
680		}
681	}
682	snd_pcm_mmap_appl_backward(pcm, frames);
683	return frames;
684}
685
686static snd_pcm_sframes_t snd_pcm_multi_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
687{
688	snd_pcm_multi_t *multi = pcm->private_data;
689	unsigned int i;
690	snd_pcm_uframes_t pos[multi->slaves_count];
691	memset(pos, 0, sizeof(pos));
692	for (i = 0; i < multi->slaves_count; ++i) {
693		snd_pcm_t *slave_i = multi->slaves[i].pcm;
694		snd_pcm_sframes_t f = INTERNAL(snd_pcm_forward)(slave_i, frames);
695		if (f < 0)
696			return f;
697		pos[i] = f;
698		frames = f;
699	}
700	/* Realign the pointers */
701	for (i = 0; i < multi->slaves_count; ++i) {
702		snd_pcm_t *slave_i = multi->slaves[i].pcm;
703		snd_pcm_uframes_t f = pos[i] - frames;
704		snd_pcm_sframes_t result;
705		if (f > 0) {
706			result = snd_pcm_rewind(slave_i, f);
707			if (result < 0)
708				return result;
709			if ((snd_pcm_uframes_t)result != f)
710				return -EIO;
711		}
712	}
713	snd_pcm_mmap_appl_forward(pcm, frames);
714	return frames;
715}
716
717static int snd_pcm_multi_resume(snd_pcm_t *pcm)
718{
719	snd_pcm_multi_t *multi = pcm->private_data;
720	int err = 0;
721	unsigned int i;
722	if (multi->slaves[0].linked)
723		return snd_pcm_resume(multi->slaves[0].linked);
724	for (i = 0; i < multi->slaves_count; ++i) {
725		if (multi->slaves[i].linked)
726			continue;
727		err = snd_pcm_resume(multi->slaves[i].pcm);
728		if (err < 0)
729			return err;
730	}
731	return err;
732}
733
734/* if a multi plugin instance is linked as slaves, every slave PCMs
735 * including the first one has to be relinked to the given master.
736 */
737static int snd_pcm_multi_link_slaves(snd_pcm_t *pcm, snd_pcm_t *master)
738{
739	snd_pcm_multi_t *multi = pcm->private_data;
740	unsigned int i;
741	int err;
742
743	for (i = 0; i < multi->slaves_count; ++i) {
744		snd_pcm_unlink(multi->slaves[i].pcm);
745		multi->slaves[i].linked = NULL;
746		err = snd_pcm_link(master, multi->slaves[i].pcm);
747		if (err < 0) {
748			reset_links(multi);
749			return err;
750		}
751		multi->slaves[i].linked = master;
752	}
753	return 0;
754}
755
756/* linking to a multi as a master is easy - simply link to the first
757 * slave element as its own slaves are already linked.
758 */
759static int snd_pcm_multi_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
760{
761	snd_pcm_multi_t *multi = pcm1->private_data;
762	snd_pcm_t *main_pcm = multi->slaves[0].pcm;
763	if (main_pcm->fast_ops->link)
764		return main_pcm->fast_ops->link(main_pcm->fast_op_arg, pcm2);
765	return -ENOSYS;
766}
767
768static int snd_pcm_multi_unlink(snd_pcm_t *pcm)
769{
770	snd_pcm_multi_t *multi = pcm->private_data;
771	unsigned int i;
772
773	for (i = 0; i < multi->slaves_count; ++i) {
774		if (multi->slaves[i].linked)
775			snd_pcm_unlink(multi->slaves[i].linked);
776		multi->slaves[0].linked = NULL;
777	}
778	return 0;
779}
780
781static snd_pcm_sframes_t snd_pcm_multi_mmap_commit(snd_pcm_t *pcm,
782						   snd_pcm_uframes_t offset,
783						   snd_pcm_uframes_t size)
784{
785	snd_pcm_multi_t *multi = pcm->private_data;
786	snd_pcm_t *slave;
787	unsigned int i;
788	snd_pcm_sframes_t result;
789
790	for (i = 0; i < multi->slaves_count; ++i) {
791		slave = multi->slaves[i].pcm;
792		result = snd_pcm_mmap_commit(slave, offset, size);
793		if (result < 0)
794			return result;
795		if ((snd_pcm_uframes_t)result != size)
796			return -EIO;
797	}
798	snd_pcm_mmap_appl_forward(pcm, size);
799	return size;
800}
801
802static int snd_pcm_multi_munmap(snd_pcm_t *pcm)
803{
804	free(pcm->mmap_channels);
805	free(pcm->running_areas);
806	pcm->mmap_channels = NULL;
807	pcm->running_areas = NULL;
808	return 0;
809}
810
811static int snd_pcm_multi_mmap(snd_pcm_t *pcm)
812{
813	snd_pcm_multi_t *multi = pcm->private_data;
814	unsigned int c;
815
816	pcm->mmap_channels = calloc(pcm->channels,
817				    sizeof(pcm->mmap_channels[0]));
818	pcm->running_areas = calloc(pcm->channels,
819				    sizeof(pcm->running_areas[0]));
820	if (!pcm->mmap_channels || !pcm->running_areas) {
821		snd_pcm_multi_munmap(pcm);
822		return -ENOMEM;
823	}
824
825	/* Copy the slave mmapped buffer data */
826	for (c = 0; c < pcm->channels; c++) {
827		snd_pcm_multi_channel_t *chan = &multi->channels[c];
828		snd_pcm_t *slave;
829		if (chan->slave_idx < 0) {
830			snd_pcm_multi_munmap(pcm);
831			return -ENXIO;
832		}
833		slave = multi->slaves[chan->slave_idx].pcm;
834		pcm->mmap_channels[c] =
835			slave->mmap_channels[chan->slave_channel];
836		pcm->mmap_channels[c].channel = c;
837		pcm->running_areas[c] =
838			slave->running_areas[chan->slave_channel];
839	}
840	return 0;
841}
842
843static int snd_pcm_multi_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail)
844{
845	snd_pcm_multi_t *multi = pcm->private_data;
846	unsigned int i;
847	for (i = 0; i < multi->slaves_count; ++i) {
848		if (snd_pcm_may_wait_for_avail_min(multi->slaves[i].pcm, avail))
849			return 1;
850	}
851	return 0;
852}
853
854static snd_pcm_chmap_query_t **snd_pcm_multi_query_chmaps(snd_pcm_t *pcm)
855{
856	snd_pcm_multi_t *multi = pcm->private_data;
857	snd_pcm_chmap_query_t **slave_maps[multi->slaves_count];
858	snd_pcm_chmap_query_t **maps;
859	unsigned int i;
860	int err = -ENOMEM;
861
862	memset(slave_maps, 0, sizeof(slave_maps));
863	maps = calloc(2, sizeof(*maps));
864	if (!maps)
865		return NULL;
866	maps[0] = calloc(multi->channels_count + 2, sizeof(int *));
867	if (!maps[0])
868		goto error;
869	maps[0]->type = SND_CHMAP_TYPE_FIXED;
870	maps[0]->map.channels = multi->channels_count;
871
872	for (i = 0; i < multi->slaves_count; i++) {
873		slave_maps[i] = snd_pcm_query_chmaps(multi->slaves[i].pcm);
874		if (!slave_maps[i])
875			goto error;
876	}
877
878	for (i = 0; i < multi->channels_count; i++) {
879		snd_pcm_multi_channel_t *bind = &multi->channels[i];
880		unsigned int slave_channels =
881			multi->slaves[bind->slave_idx].channels_count;
882		snd_pcm_chmap_query_t **p;
883
884		for (p = slave_maps[bind->slave_idx]; *p; p++) {
885			if ((*p)->map.channels == slave_channels) {
886				maps[0]->map.pos[i] =
887					(*p)->map.pos[bind->slave_channel];
888				break;
889			}
890		}
891	}
892	err = 0;
893
894 error:
895	for (i = 0; i < multi->slaves_count; i++) {
896		if (slave_maps[i])
897			snd_pcm_free_chmaps(slave_maps[i]);
898	}
899
900	if (err) {
901		snd_pcm_free_chmaps(maps);
902		return NULL;
903	}
904
905	return maps;
906}
907
908static snd_pcm_chmap_t *snd_pcm_multi_get_chmap(snd_pcm_t *pcm)
909{
910	snd_pcm_multi_t *multi = pcm->private_data;
911	snd_pcm_chmap_t *map;
912	snd_pcm_chmap_t *slave_maps[multi->slaves_count];
913	unsigned int i;
914	int err = -ENOMEM;
915
916	memset(slave_maps, 0, sizeof(slave_maps));
917	map = calloc(multi->channels_count + 1, sizeof(int));
918	if (!map)
919		return NULL;
920
921	for (i = 0; i < multi->slaves_count; i++) {
922		slave_maps[i] = snd_pcm_get_chmap(multi->slaves[i].pcm);
923		if (!slave_maps[i])
924			goto error;
925	}
926
927	map->channels = multi->channels_count;
928	for (i = 0; i < multi->channels_count; i++) {
929		snd_pcm_multi_channel_t *bind = &multi->channels[i];
930		map->pos[i] = slave_maps[bind->slave_idx]->pos[bind->slave_channel];
931	}
932	err = 0;
933
934 error:
935	for (i = 0; i < multi->slaves_count; i++)
936		free(slave_maps[i]);
937
938	if (err) {
939		free(map);
940		return NULL;
941	}
942
943	return map;
944}
945
946static int snd_pcm_multi_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
947{
948	snd_pcm_multi_t *multi = pcm->private_data;
949	snd_pcm_chmap_t *slave_maps[multi->slaves_count];
950	unsigned int i;
951	int err = 0;
952
953	if (map->channels != multi->channels_count)
954		return -EINVAL;
955
956	for (i = 0; i < multi->slaves_count; i++) {
957		slave_maps[i] = calloc(multi->slaves[i].channels_count + 1,
958				       sizeof(int));
959		if (!slave_maps[i]) {
960			for (i++; i < multi->slaves_count; i++)
961				slave_maps[i] = NULL;
962			err = -ENOMEM;
963			goto error;
964		}
965	}
966
967	for (i = 0; i < multi->channels_count; i++) {
968		snd_pcm_multi_channel_t *bind = &multi->channels[i];
969		slave_maps[bind->slave_idx]->pos[bind->slave_channel] =
970			map->pos[i];
971	}
972
973	for (i = 0; i < multi->slaves_count; i++) {
974		err = snd_pcm_set_chmap(multi->slaves[i].pcm, slave_maps[i]);
975		if (err < 0)
976			goto error;
977	}
978
979 error:
980	for (i = 0; i < multi->slaves_count; i++)
981		free(slave_maps[i]);
982
983	return err;
984}
985
986static void snd_pcm_multi_dump(snd_pcm_t *pcm, snd_output_t *out)
987{
988	snd_pcm_multi_t *multi = pcm->private_data;
989	unsigned int k;
990	snd_output_printf(out, "Multi PCM\n");
991	snd_output_printf(out, "  Channel bindings:\n");
992	for (k = 0; k < multi->channels_count; ++k) {
993		snd_pcm_multi_channel_t *c = &multi->channels[k];
994		if (c->slave_idx < 0)
995			continue;
996		snd_output_printf(out, "    %d: slave %d, channel %d\n",
997			k, c->slave_idx, c->slave_channel);
998	}
999	if (pcm->setup) {
1000		snd_output_printf(out, "Its setup is:\n");
1001		snd_pcm_dump_setup(pcm, out);
1002	}
1003	for (k = 0; k < multi->slaves_count; ++k) {
1004		snd_output_printf(out, "Slave #%d: ", k);
1005		snd_pcm_dump(multi->slaves[k].pcm, out);
1006	}
1007}
1008
1009static const snd_pcm_ops_t snd_pcm_multi_ops = {
1010	.close = snd_pcm_multi_close,
1011	.info = snd_pcm_multi_info,
1012	.hw_refine = snd_pcm_multi_hw_refine,
1013	.hw_params = snd_pcm_multi_hw_params,
1014	.hw_free = snd_pcm_multi_hw_free,
1015	.sw_params = snd_pcm_multi_sw_params,
1016	.channel_info = snd_pcm_multi_channel_info,
1017	.dump = snd_pcm_multi_dump,
1018	.nonblock = snd_pcm_multi_nonblock,
1019	.async = snd_pcm_multi_async,
1020	.mmap = snd_pcm_multi_mmap,
1021	.munmap = snd_pcm_multi_munmap,
1022	.query_chmaps = snd_pcm_multi_query_chmaps,
1023	.get_chmap = snd_pcm_multi_get_chmap,
1024	.set_chmap = snd_pcm_multi_set_chmap,
1025};
1026
1027static const snd_pcm_fast_ops_t snd_pcm_multi_fast_ops = {
1028	.status = snd_pcm_multi_status,
1029	.state = snd_pcm_multi_state,
1030	.hwsync = snd_pcm_multi_hwsync,
1031	.delay = snd_pcm_multi_delay,
1032	.prepare = snd_pcm_multi_prepare,
1033	.reset = snd_pcm_multi_reset,
1034	.start = snd_pcm_multi_start,
1035	.drop = snd_pcm_multi_drop,
1036	.drain = snd_pcm_multi_drain,
1037	.pause = snd_pcm_multi_pause,
1038	.writei = snd_pcm_mmap_writei,
1039	.writen = snd_pcm_mmap_writen,
1040	.readi = snd_pcm_mmap_readi,
1041	.readn = snd_pcm_mmap_readn,
1042	.rewindable = snd_pcm_multi_rewindable,
1043	.rewind = snd_pcm_multi_rewind,
1044	.forwardable = snd_pcm_multi_forwardable,
1045	.forward = snd_pcm_multi_forward,
1046	.resume = snd_pcm_multi_resume,
1047	.link = snd_pcm_multi_link,
1048	.link_slaves = snd_pcm_multi_link_slaves,
1049	.unlink = snd_pcm_multi_unlink,
1050	.avail_update = snd_pcm_multi_avail_update,
1051	.mmap_commit = snd_pcm_multi_mmap_commit,
1052	.htimestamp = snd_pcm_multi_htimestamp,
1053	.poll_descriptors_count = snd_pcm_multi_poll_descriptors_count,
1054	.poll_descriptors = snd_pcm_multi_poll_descriptors,
1055	.poll_revents = snd_pcm_multi_poll_revents,
1056	.may_wait_for_avail_min = snd_pcm_multi_may_wait_for_avail_min,
1057};
1058
1059/**
1060 * \brief Creates a new Multi PCM
1061 * \param pcmp Returns created PCM handle
1062 * \param name Name of PCM
1063 * \param slaves_count Count of slaves
1064 * \param master_slave Master slave number
1065 * \param slaves_pcm Array with slave PCMs
1066 * \param schannels_count Array with slave channel counts
1067 * \param channels_count Count of channels
1068 * \param sidxs Array with channels indexes to slaves
1069 * \param schannels Array with slave channels
1070 * \param close_slaves When set, the slave PCM handle is closed
1071 * \retval zero on success otherwise a negative error code
1072 * \warning Using of this function might be dangerous in the sense
1073 *          of compatibility reasons. The prototype might be freely
1074 *          changed in future.
1075 */
1076int snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
1077		       unsigned int slaves_count, unsigned int master_slave,
1078		       snd_pcm_t **slaves_pcm, unsigned int *schannels_count,
1079		       unsigned int channels_count,
1080		       int *sidxs, unsigned int *schannels,
1081		       int close_slaves)
1082{
1083	snd_pcm_t *pcm;
1084	snd_pcm_multi_t *multi;
1085	unsigned int i;
1086	snd_pcm_stream_t stream;
1087	int err;
1088
1089	assert(pcmp);
1090	assert(slaves_count > 0 && slaves_pcm && schannels_count);
1091	assert(channels_count > 0 && sidxs && schannels);
1092	assert(master_slave < slaves_count);
1093
1094	multi = calloc(1, sizeof(snd_pcm_multi_t));
1095	if (!multi) {
1096		return -ENOMEM;
1097	}
1098
1099	stream = slaves_pcm[0]->stream;
1100
1101	multi->slaves_count = slaves_count;
1102	multi->master_slave = master_slave;
1103	multi->slaves = calloc(slaves_count, sizeof(*multi->slaves));
1104	if (!multi->slaves) {
1105		free(multi);
1106		return -ENOMEM;
1107	}
1108	multi->channels_count = channels_count;
1109	multi->channels = calloc(channels_count, sizeof(*multi->channels));
1110	if (!multi->channels) {
1111		free(multi->slaves);
1112		free(multi);
1113		return -ENOMEM;
1114	}
1115	for (i = 0; i < slaves_count; ++i) {
1116		snd_pcm_multi_slave_t *slave = &multi->slaves[i];
1117		assert(slaves_pcm[i]->stream == stream);
1118		slave->pcm = slaves_pcm[i];
1119		slave->channels_count = schannels_count[i];
1120		slave->close_slave = close_slaves;
1121	}
1122	for (i = 0; i < channels_count; ++i) {
1123		snd_pcm_multi_channel_t *bind = &multi->channels[i];
1124		assert(sidxs[i] < (int)slaves_count);
1125		assert(schannels[i] < schannels_count[sidxs[i]]);
1126		bind->slave_idx = sidxs[i];
1127		bind->slave_channel = schannels[i];
1128		if (sidxs[i] < 0)
1129			continue;
1130	}
1131	multi->channels_count = channels_count;
1132
1133	err = snd_pcm_new(&pcm, SND_PCM_TYPE_MULTI, name, stream,
1134			  multi->slaves[0].pcm->mode);
1135	if (err < 0) {
1136		free(multi->slaves);
1137		free(multi->channels);
1138		free(multi);
1139		return err;
1140	}
1141	pcm->mmap_rw = 1;
1142	pcm->mmap_shadow = 1; /* has own mmap method */
1143	pcm->ops = &snd_pcm_multi_ops;
1144	pcm->fast_ops = &snd_pcm_multi_fast_ops;
1145	pcm->private_data = multi;
1146	pcm->poll_fd = multi->slaves[master_slave].pcm->poll_fd;
1147	pcm->poll_events = multi->slaves[master_slave].pcm->poll_events;
1148	pcm->tstamp_type = multi->slaves[master_slave].pcm->tstamp_type;
1149	snd_pcm_set_hw_ptr(pcm, &multi->hw_ptr, -1, 0);
1150	snd_pcm_set_appl_ptr(pcm, &multi->appl_ptr, -1, 0);
1151	*pcmp = pcm;
1152	return 0;
1153}
1154
1155/*! \page pcm_plugins
1156
1157\section pcm_plugins_multi Plugin: Multiple streams to One
1158
1159This plugin converts multiple streams to one.
1160
1161\code
1162pcm.name {
1163        type multi              # Multiple streams conversion PCM
1164        slaves {		# Slaves definition
1165		ID STR		# Slave PCM name
1166		# or
1167		ID {
1168			pcm STR		# Slave PCM name
1169			# or
1170			pcm { } 	# Slave PCM definition
1171			channels INT	# Slave channels
1172		}
1173        }
1174	bindings {		# Bindings table
1175		N {
1176			slave STR	# Slave key
1177			channel INT	# Slave channel
1178		}
1179	}
1180	[master INT]		# Define the master slave
1181}
1182\endcode
1183
1184For example, to bind two PCM streams with two-channel stereo (hw:0,0 and
1185hw:0,1) as one 4-channel stereo PCM stream, define like this:
1186\code
1187pcm.quad {
1188	type multi
1189
1190	slaves.a.pcm "hw:0,0"
1191	slaves.a.channels 2
1192	slaves.b.pcm "hw:0,1"
1193	slaves.b.channels 2
1194
1195	bindings.0.slave a
1196	bindings.0.channel 0
1197	bindings.1.slave a
1198	bindings.1.channel 1
1199	bindings.2.slave b
1200	bindings.2.channel 0
1201	bindings.3.slave b
1202	bindings.3.channel 1
1203}
1204\endcode
1205Note that the resultant pcm "quad" is not in the interleaved format
1206but in the "complex" format.  Hence, it's not accessible by applications
1207which can handle only the interleaved (or the non-interleaved) format.
1208In such a case, wrap this PCM with \ref pcm_plugins_route "route" or
1209\ref pcm_plugins_plug "plug" plugin.
1210\code
1211pcm.quad2 {
1212	type route
1213	slave.pcm "quad"
1214	ttable.0.0 1
1215	ttable.1.1 1
1216	ttable.2.2 1
1217	ttable.3.3 1
1218}
1219\endcode
1220
1221\subsection pcm_plugins_multi_funcref Function reference
1222
1223<UL>
1224  <LI>snd_pcm_multi_open()
1225  <LI>_snd_pcm_multi_open()
1226</UL>
1227
1228*/
1229
1230/**
1231 * \brief Creates a new Multi PCM
1232 * \param pcmp Returns created PCM handle
1233 * \param name Name of PCM
1234 * \param root Root configuration node
1235 * \param conf Configuration node with Multi PCM description
1236 * \param stream Stream type
1237 * \param mode Stream mode
1238 * \retval zero on success otherwise a negative error code
1239 * \warning Using of this function might be dangerous in the sense
1240 *          of compatibility reasons. The prototype might be freely
1241 *          changed in future.
1242 */
1243int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
1244			snd_config_t *root, snd_config_t *conf,
1245			snd_pcm_stream_t stream, int mode)
1246{
1247	snd_config_iterator_t i, inext, j, jnext;
1248	snd_config_t *slaves = NULL;
1249	snd_config_t *bindings = NULL;
1250	int err;
1251	unsigned int idx;
1252	const char **slaves_id = NULL;
1253	snd_config_t **slaves_conf = NULL;
1254	snd_pcm_t **slaves_pcm = NULL;
1255	unsigned int *slaves_channels = NULL;
1256	int *channels_sidx = NULL;
1257	unsigned int *channels_schannel = NULL;
1258	unsigned int slaves_count = 0;
1259	long master_slave = 0;
1260	unsigned int channels_count = 0;
1261	snd_config_for_each(i, inext, conf) {
1262		snd_config_t *n = snd_config_iterator_entry(i);
1263		const char *id;
1264		if (snd_config_get_id(n, &id) < 0)
1265			continue;
1266		if (snd_pcm_conf_generic_id(id))
1267			continue;
1268		if (strcmp(id, "slaves") == 0) {
1269			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1270				SNDERR("Invalid type for %s", id);
1271				return -EINVAL;
1272			}
1273			slaves = n;
1274			continue;
1275		}
1276		if (strcmp(id, "bindings") == 0) {
1277			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1278				SNDERR("Invalid type for %s", id);
1279				return -EINVAL;
1280			}
1281			bindings = n;
1282			continue;
1283		}
1284		if (strcmp(id, "master") == 0) {
1285			if (snd_config_get_integer(n, &master_slave) < 0) {
1286				SNDERR("Invalid type for %s", id);
1287				return -EINVAL;
1288			}
1289			continue;
1290		}
1291		SNDERR("Unknown field %s", id);
1292		return -EINVAL;
1293	}
1294	if (!slaves) {
1295		SNDERR("slaves is not defined");
1296		return -EINVAL;
1297	}
1298	if (!bindings) {
1299		SNDERR("bindings is not defined");
1300		return -EINVAL;
1301	}
1302	snd_config_for_each(i, inext, slaves) {
1303		++slaves_count;
1304	}
1305	if (master_slave < 0 || master_slave >= (long)slaves_count) {
1306		SNDERR("Master slave is out of range (0-%u)", slaves_count-1);
1307		return -EINVAL;
1308	}
1309	snd_config_for_each(i, inext, bindings) {
1310		long cchannel;
1311		snd_config_t *m = snd_config_iterator_entry(i);
1312		const char *id;
1313		if (snd_config_get_id(m, &id) < 0)
1314			continue;
1315		err = safe_strtol(id, &cchannel);
1316		if (err < 0 || cchannel < 0) {
1317			SNDERR("Invalid channel number: %s", id);
1318			return -EINVAL;
1319		}
1320		if ((unsigned long)cchannel >= channels_count)
1321			channels_count = cchannel + 1;
1322	}
1323	if (channels_count == 0) {
1324		SNDERR("No channels defined");
1325		return -EINVAL;
1326	}
1327	slaves_id = calloc(slaves_count, sizeof(*slaves_id));
1328	slaves_conf = calloc(slaves_count, sizeof(*slaves_conf));
1329	slaves_pcm = calloc(slaves_count, sizeof(*slaves_pcm));
1330	slaves_channels = calloc(slaves_count, sizeof(*slaves_channels));
1331	channels_sidx = calloc(channels_count, sizeof(*channels_sidx));
1332	channels_schannel = calloc(channels_count, sizeof(*channels_schannel));
1333	if (!slaves_id || !slaves_conf || !slaves_pcm || !slaves_channels ||
1334	    !channels_sidx || !channels_schannel) {
1335		err = -ENOMEM;
1336		goto _free;
1337	}
1338	for (idx = 0; idx < channels_count; ++idx)
1339		channels_sidx[idx] = -1;
1340	idx = 0;
1341	snd_config_for_each(i, inext, slaves) {
1342		snd_config_t *m = snd_config_iterator_entry(i);
1343		const char *id;
1344		int channels;
1345		if (snd_config_get_id(m, &id) < 0)
1346			continue;
1347		slaves_id[idx] = id;
1348		err = snd_pcm_slave_conf(root, m, &slaves_conf[idx], 1,
1349					 SND_PCM_HW_PARAM_CHANNELS, SCONF_MANDATORY, &channels);
1350		if (err < 0)
1351			goto _free;
1352		slaves_channels[idx] = channels;
1353		++idx;
1354	}
1355
1356	snd_config_for_each(i, inext, bindings) {
1357		snd_config_t *m = snd_config_iterator_entry(i);
1358		long cchannel = -1;
1359		long schannel = -1;
1360		int slave = -1;
1361		long val;
1362		const char *str;
1363		const char *id;
1364		if (snd_config_get_id(m, &id) < 0)
1365			continue;
1366		err = safe_strtol(id, &cchannel);
1367		if (err < 0 || cchannel < 0) {
1368			SNDERR("Invalid channel number: %s", id);
1369			err = -EINVAL;
1370			goto _free;
1371		}
1372		snd_config_for_each(j, jnext, m) {
1373			snd_config_t *n = snd_config_iterator_entry(j);
1374			const char *id;
1375			if (snd_config_get_id(n, &id) < 0)
1376				continue;
1377			if (strcmp(id, "comment") == 0)
1378				continue;
1379			if (strcmp(id, "slave") == 0) {
1380				char buf[32];
1381				unsigned int k;
1382				err = snd_config_get_string(n, &str);
1383				if (err < 0) {
1384					err = snd_config_get_integer(n, &val);
1385					if (err < 0) {
1386						SNDERR("Invalid value for %s", id);
1387						goto _free;
1388					}
1389					sprintf(buf, "%ld", val);
1390					str = buf;
1391				}
1392				for (k = 0; k < slaves_count; ++k) {
1393					if (strcmp(slaves_id[k], str) == 0)
1394						slave = k;
1395				}
1396				continue;
1397			}
1398			if (strcmp(id, "channel") == 0) {
1399				err = snd_config_get_integer(n, &schannel);
1400				if (err < 0) {
1401					SNDERR("Invalid type for %s", id);
1402					goto _free;
1403				}
1404				continue;
1405			}
1406			SNDERR("Unknown field %s", id);
1407			err = -EINVAL;
1408			goto _free;
1409		}
1410		if (slave < 0 || (unsigned int)slave >= slaves_count) {
1411			SNDERR("Invalid or missing sidx for channel %s", id);
1412			err = -EINVAL;
1413			goto _free;
1414		}
1415		if (schannel < 0 ||
1416		    (unsigned int) schannel >= slaves_channels[slave]) {
1417			SNDERR("Invalid or missing schannel for channel %s", id);
1418			err = -EINVAL;
1419			goto _free;
1420		}
1421		channels_sidx[cchannel] = slave;
1422		channels_schannel[cchannel] = schannel;
1423	}
1424
1425	for (idx = 0; idx < slaves_count; ++idx) {
1426		err = snd_pcm_open_slave(&slaves_pcm[idx], root,
1427					 slaves_conf[idx], stream, mode,
1428					 conf);
1429		if (err < 0)
1430			goto _free;
1431		snd_config_delete(slaves_conf[idx]);
1432		slaves_conf[idx] = NULL;
1433	}
1434	err = snd_pcm_multi_open(pcmp, name, slaves_count, master_slave,
1435				 slaves_pcm, slaves_channels,
1436				 channels_count,
1437				 channels_sidx, channels_schannel,
1438				 1);
1439_free:
1440	if (err < 0) {
1441		for (idx = 0; idx < slaves_count; ++idx) {
1442			if (slaves_pcm[idx])
1443				snd_pcm_close(slaves_pcm[idx]);
1444		}
1445	}
1446	if (slaves_conf) {
1447		for (idx = 0; idx < slaves_count; ++idx) {
1448			if (slaves_conf[idx])
1449				snd_config_delete(slaves_conf[idx]);
1450		}
1451		free(slaves_conf);
1452	}
1453	free(slaves_pcm);
1454	free(slaves_channels);
1455	free(channels_sidx);
1456	free(channels_schannel);
1457	free(slaves_id);
1458	return err;
1459}
1460#ifndef DOC_HIDDEN
1461SND_DLSYM_BUILD_VERSION(_snd_pcm_multi_open, SND_PCM_DLSYM_VERSION);
1462#endif
1463