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