OSDN Git Service

fixed dmix crashes by Clemens Ladisch <clemens@ladisch.de>:
[android-x86/external-alsa-lib.git] / src / pcm / pcm_plug.c
1 /*
2  * \file pcm/pcm_plug.c
3  * \ingroup PCM_Plugins
4  * \brief PCM Route & Volume Plugin Interface
5  * \author Abramo Bagnara <abramo@alsa-project.org>
6  * \date 2000-2001
7  */
8 /*
9  *  PCM - Plug
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 "pcm_local.h"
30 #include "pcm_plugin.h"
31
32 #ifndef PIC
33 /* entry for static linking */
34 const char *_snd_module_pcm_plug = "";
35 #endif
36
37 #ifndef DOC_HIDDEN
38
39 enum snd_pcm_plug_route_policy {
40         PLUG_ROUTE_POLICY_NONE,
41         PLUG_ROUTE_POLICY_DEFAULT,
42         PLUG_ROUTE_POLICY_COPY,
43         PLUG_ROUTE_POLICY_AVERAGE,
44         PLUG_ROUTE_POLICY_DUP,
45 };
46
47 typedef struct {
48         snd_pcm_t *req_slave;
49         int close_slave;
50         snd_pcm_t *slave;
51         snd_pcm_format_t sformat;
52         int schannels;
53         int srate;
54         enum snd_pcm_plug_route_policy route_policy;
55         snd_pcm_route_ttable_entry_t *ttable;
56         int ttable_ok;
57         unsigned int tt_ssize, tt_cused, tt_sused;
58 } snd_pcm_plug_t;
59
60 #endif
61
62 static int snd_pcm_plug_close(snd_pcm_t *pcm)
63 {
64         snd_pcm_plug_t *plug = pcm->private_data;
65         int err, result = 0;
66         if (plug->ttable)
67                 free(plug->ttable);
68         if (plug->slave != plug->req_slave) {
69                 SNDERR("plug slaves mismatch");
70                 return -EINVAL;
71         }
72         if (plug->close_slave) {
73                 snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
74                 snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
75                 err = snd_pcm_close(plug->req_slave);
76                 if (err < 0)
77                         result = err;
78         }
79         free(plug);
80         return result;
81 }
82
83 static int snd_pcm_plug_nonblock(snd_pcm_t *pcm, int nonblock)
84 {
85         snd_pcm_plug_t *plug = pcm->private_data;
86         return snd_pcm_nonblock(plug->slave, nonblock);
87 }
88
89 static int snd_pcm_plug_async(snd_pcm_t *pcm, int sig, pid_t pid)
90 {
91         snd_pcm_plug_t *plug = pcm->private_data;
92         return snd_pcm_async(plug->slave, sig, pid);
93 }
94
95 static int snd_pcm_plug_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
96 {
97         snd_pcm_plug_t *plug = pcm->private_data;
98         return snd_pcm_poll_descriptors_revents(plug->slave, pfds, nfds, revents);
99 }
100
101 static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
102 {
103         snd_pcm_plug_t *plug = pcm->private_data;
104         snd_pcm_t *slave = plug->req_slave;
105         int err;
106         
107         if ((err = snd_pcm_info(slave, info)) < 0)
108                 return err;
109         return 0;
110 }
111
112 static snd_pcm_format_t linear_preferred_formats[] = {
113 #ifdef SND_LITTLE_ENDIAN
114         SND_PCM_FORMAT_S16_LE,
115         SND_PCM_FORMAT_U16_LE,
116         SND_PCM_FORMAT_S16_BE,
117         SND_PCM_FORMAT_U16_BE,
118 #else
119         SND_PCM_FORMAT_S16_BE,
120         SND_PCM_FORMAT_U16_BE,
121         SND_PCM_FORMAT_S16_LE,
122         SND_PCM_FORMAT_U16_LE,
123 #endif
124 #ifdef SND_LITTLE_ENDIAN
125         SND_PCM_FORMAT_S32_LE,
126         SND_PCM_FORMAT_U32_LE,
127         SND_PCM_FORMAT_S32_BE,
128         SND_PCM_FORMAT_U32_BE,
129 #else
130         SND_PCM_FORMAT_S32_BE,
131         SND_PCM_FORMAT_U32_BE,
132         SND_PCM_FORMAT_S32_LE,
133         SND_PCM_FORMAT_U32_LE,
134 #endif
135         SND_PCM_FORMAT_S8,
136         SND_PCM_FORMAT_U8,
137 #ifdef SND_LITTLE_ENDIAN
138         SND_PCM_FORMAT_FLOAT_LE,
139         SND_PCM_FORMAT_FLOAT64_LE,
140         SND_PCM_FORMAT_FLOAT_BE,
141         SND_PCM_FORMAT_FLOAT64_BE,
142 #else
143         SND_PCM_FORMAT_FLOAT_BE,
144         SND_PCM_FORMAT_FLOAT64_BE,
145         SND_PCM_FORMAT_FLOAT_LE,
146         SND_PCM_FORMAT_FLOAT64_LE,
147 #endif
148 #ifdef SND_LITTLE_ENDIAN
149         SND_PCM_FORMAT_S24_LE,
150         SND_PCM_FORMAT_U24_LE,
151         SND_PCM_FORMAT_S24_BE,
152         SND_PCM_FORMAT_U24_BE,
153 #else
154         SND_PCM_FORMAT_S24_BE,
155         SND_PCM_FORMAT_U24_BE,
156         SND_PCM_FORMAT_S24_LE,
157         SND_PCM_FORMAT_U24_LE,
158 #endif
159 #ifdef SND_LITTLE_ENDIAN
160         SND_PCM_FORMAT_S24_3LE,
161         SND_PCM_FORMAT_U24_3LE,
162         SND_PCM_FORMAT_S24_3BE,
163         SND_PCM_FORMAT_U24_3BE,
164 #else
165         SND_PCM_FORMAT_S24_3BE,
166         SND_PCM_FORMAT_U24_3BE,
167         SND_PCM_FORMAT_S24_3LE,
168         SND_PCM_FORMAT_U24_3LE,
169 #endif
170 #ifdef SND_LITTLE_ENDIAN
171         SND_PCM_FORMAT_S20_3LE,
172         SND_PCM_FORMAT_U20_3LE,
173         SND_PCM_FORMAT_S20_3BE,
174         SND_PCM_FORMAT_U20_3BE,
175 #else
176         SND_PCM_FORMAT_S20_3BE,
177         SND_PCM_FORMAT_U20_3BE,
178         SND_PCM_FORMAT_S20_3LE,
179         SND_PCM_FORMAT_U20_3LE,
180 #endif
181 #ifdef SND_LITTLE_ENDIAN
182         SND_PCM_FORMAT_S18_3LE,
183         SND_PCM_FORMAT_U18_3LE,
184         SND_PCM_FORMAT_S18_3BE,
185         SND_PCM_FORMAT_U18_3BE,
186 #else
187         SND_PCM_FORMAT_S18_3BE,
188         SND_PCM_FORMAT_U18_3BE,
189         SND_PCM_FORMAT_S18_3LE,
190         SND_PCM_FORMAT_U18_3LE,
191 #endif
192 };
193
194 static snd_pcm_format_t nonlinear_preferred_formats[] = {
195         SND_PCM_FORMAT_MU_LAW,
196         SND_PCM_FORMAT_A_LAW,
197         SND_PCM_FORMAT_IMA_ADPCM,
198 };
199
200 static snd_pcm_format_t float_preferred_formats[] = {
201 #ifdef SND_LITTLE_ENDIAN
202         SND_PCM_FORMAT_FLOAT_LE,
203         SND_PCM_FORMAT_FLOAT64_LE,
204         SND_PCM_FORMAT_FLOAT_BE,
205         SND_PCM_FORMAT_FLOAT64_BE,
206 #else
207         SND_PCM_FORMAT_FLOAT_BE,
208         SND_PCM_FORMAT_FLOAT64_BE,
209         SND_PCM_FORMAT_FLOAT_LE,
210         SND_PCM_FORMAT_FLOAT64_LE,
211 #endif
212 };
213
214 static char linear_format_widths[32] = {
215         0, 0, 0, 0, 0, 0, 0, 1,
216         0, 0, 0, 0, 0, 0, 0, 1,
217         0, 1, 0, 1, 0, 0, 0, 1,
218         0, 0, 0, 0, 0, 0, 0, 1,
219 };
220
221 static int check_linear_format(const snd_pcm_format_mask_t *format_mask, int wid, int sgn, int ed)
222 {
223         int e, s;
224         if (! linear_format_widths[wid - 1])
225                 return SND_PCM_FORMAT_UNKNOWN;
226         for (e = 0; e < 2; e++) {
227                 for (s = 0; s < 2; s++) {
228                         int pw = ((wid + 7) / 8) * 8;
229                         for (; pw <= 32; pw += 8) {
230                                 snd_pcm_format_t f;
231                                 f = snd_pcm_build_linear_format(wid, pw, sgn, ed);
232                                 if (f != SND_PCM_FORMAT_UNKNOWN &&
233                                     snd_pcm_format_mask_test(format_mask, f))
234                                         return f;
235                         }
236                         sgn = !sgn;
237                 }
238                 ed = !ed;
239         }
240         return SND_PCM_FORMAT_UNKNOWN;
241 }
242
243 static snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, const snd_pcm_format_mask_t *format_mask)
244 {
245         int w, w1, u, e;
246         snd_pcm_format_t f;
247         snd_pcm_format_mask_t lin = { SND_PCM_FMTBIT_LINEAR };
248         snd_pcm_format_mask_t fl = { SND_PCM_FMTBIT_FLOAT };
249         if (snd_pcm_format_mask_test(format_mask, format))
250                 return format;
251         if (!snd_pcm_format_mask_test(&lin, format) &&
252             !snd_pcm_format_mask_test(&fl, format)) {
253                 unsigned int i;
254                 switch (format) {
255                 case SND_PCM_FORMAT_MU_LAW:
256                 case SND_PCM_FORMAT_A_LAW:
257                 case SND_PCM_FORMAT_IMA_ADPCM:
258                         for (i = 0; i < sizeof(linear_preferred_formats) / sizeof(linear_preferred_formats[0]); ++i) {
259                                 snd_pcm_format_t f = linear_preferred_formats[i];
260                                 if (snd_pcm_format_mask_test(format_mask, f))
261                                         return f;
262                         }
263                         /* Fall through */
264                 default:
265                         return SND_PCM_FORMAT_UNKNOWN;
266                 }
267
268         }
269         snd_mask_intersect(&lin, format_mask);
270         snd_mask_intersect(&fl, format_mask);
271         if (snd_mask_empty(&lin) && snd_mask_empty(&fl)) {
272                 unsigned int i;
273                 for (i = 0; i < sizeof(nonlinear_preferred_formats) / sizeof(nonlinear_preferred_formats[0]); ++i) {
274                         snd_pcm_format_t f = nonlinear_preferred_formats[i];
275                         if (snd_pcm_format_mask_test(format_mask, f))
276                                 return f;
277                 }
278                 return SND_PCM_FORMAT_UNKNOWN;
279         }
280         if (snd_pcm_format_float(format)) {
281                 if (snd_pcm_format_mask_test(&fl, format)) {
282                         unsigned int i;
283                         for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
284                                 snd_pcm_format_t f = float_preferred_formats[i];
285                                 if (snd_pcm_format_mask_test(format_mask, f))
286                                         return f;
287                         }
288                 }
289                 w = 32;
290                 u = 0;
291                 e = snd_pcm_format_big_endian(format);
292         } else if (snd_mask_empty(&lin)) {
293                 unsigned int i;
294                 for (i = 0; i < sizeof(float_preferred_formats) / sizeof(float_preferred_formats[0]); ++i) {
295                         snd_pcm_format_t f = float_preferred_formats[i];
296                         if (snd_pcm_format_mask_test(format_mask, f))
297                                 return f;
298                 }
299                 return SND_PCM_FORMAT_UNKNOWN;
300         } else {
301                 w = snd_pcm_format_width(format);
302                 u = snd_pcm_format_unsigned(format);
303                 e = snd_pcm_format_big_endian(format);
304         }
305         for (w1 = w; w1 <= 32; w1++) {
306                 f = check_linear_format(format_mask, w1, u, e);
307                 if (f != SND_PCM_FORMAT_UNKNOWN)
308                         return f;
309         }
310         for (w1 = w - 1; w1 > 0; w1--) {
311                 f = check_linear_format(format_mask, w1, u, e);
312                 if (f != SND_PCM_FORMAT_UNKNOWN)
313                         return f;
314         }
315         return SND_PCM_FORMAT_UNKNOWN;
316 }
317
318 static void snd_pcm_plug_clear(snd_pcm_t *pcm)
319 {
320         snd_pcm_plug_t *plug = pcm->private_data;
321         snd_pcm_t *slave = plug->req_slave;
322         /* Clear old plugins */
323         if (plug->slave != slave) {
324                 snd_pcm_unlink_hw_ptr(pcm, plug->slave);
325                 snd_pcm_unlink_appl_ptr(pcm, plug->slave);
326                 snd_pcm_close(plug->slave);
327                 plug->slave = slave;
328                 pcm->fast_ops = slave->fast_ops;
329                 pcm->fast_op_arg = slave->fast_op_arg;
330         }
331 }
332
333 typedef struct {
334         snd_pcm_access_t access;
335         snd_pcm_format_t format;
336         unsigned int channels;
337         unsigned int rate;
338 } snd_pcm_plug_params_t;
339
340 static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
341 {
342         snd_pcm_plug_t *plug = pcm->private_data;
343         int err;
344         assert(snd_pcm_format_linear(slv->format));
345         if (clt->rate == slv->rate)
346                 return 0;
347         err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->slave, plug->slave != plug->req_slave);
348         if (err < 0)
349                 return err;
350         slv->access = clt->access;
351         slv->rate = clt->rate;
352         if (snd_pcm_format_linear(clt->format))
353                 slv->format = clt->format;
354         return 1;
355 }
356
357 static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
358 {
359         snd_pcm_plug_t *plug = pcm->private_data;
360         unsigned int tt_ssize, tt_cused, tt_sused;
361         snd_pcm_route_ttable_entry_t *ttable;
362         int err;
363         assert(snd_pcm_format_linear(slv->format));
364         if (clt->channels == slv->channels && !plug->ttable)
365                 return 0;
366         if (clt->rate != slv->rate &&
367             clt->channels > slv->channels)
368                 return 0;
369         tt_ssize = slv->channels;
370         tt_cused = clt->channels;
371         tt_sused = slv->channels;
372         ttable = alloca(tt_cused * tt_sused * sizeof(*ttable));
373         if (plug->ttable) {     /* expand or shrink table */
374                 unsigned int c = 0, s = 0;
375                 for (c = 0; c < tt_cused; c++) {
376                         for (s = 0; s < tt_sused; s++) {
377                                 snd_pcm_route_ttable_entry_t v;
378                                 if (c >= plug->tt_cused)
379                                         v = 0;
380                                 else if (s >= plug->tt_sused)
381                                         v = 0;
382                                 else
383                                         v = plug->ttable[c * plug->tt_ssize + s];
384                                 ttable[c * tt_ssize + s] = v;
385                         }
386                 }
387                 plug->ttable_ok = 1;
388         } else {
389                 unsigned int k;
390                 unsigned int c = 0, s = 0;
391                 enum snd_pcm_plug_route_policy rpolicy = plug->route_policy;
392                 int n;
393                 for (k = 0; k < tt_cused * tt_sused; ++k)
394                         ttable[k] = 0;
395                 if (rpolicy == PLUG_ROUTE_POLICY_DEFAULT) {
396                         rpolicy = PLUG_ROUTE_POLICY_COPY;
397                         /* it's hack for mono conversion */
398                         if (clt->channels == 1 || slv->channels == 1)
399                                 rpolicy = PLUG_ROUTE_POLICY_AVERAGE;
400                 }
401                 switch (rpolicy) {
402                 case PLUG_ROUTE_POLICY_AVERAGE:
403                 case PLUG_ROUTE_POLICY_DUP:
404                         if (clt->channels > slv->channels) {
405                                 n = clt->channels;
406                         } else {
407                                 n = slv->channels;
408                         }
409                         while (n-- > 0) {
410                                 snd_pcm_route_ttable_entry_t v = SND_PCM_PLUGIN_ROUTE_FULL;
411                                 if (rpolicy == PLUG_ROUTE_POLICY_AVERAGE) {
412                                         if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
413                                             clt->channels > slv->channels) {
414                                                 int srcs = clt->channels / slv->channels;
415                                                 if (s < clt->channels % slv->channels)
416                                                         srcs++;
417                                                 v /= srcs;
418                                         } else if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
419                                                    slv->channels > clt->channels) {
420                                                         int srcs = slv->channels / clt->channels;
421                                                 if (s < slv->channels % clt->channels)
422                                                         srcs++;
423                                                 v /= srcs;
424                                         }
425                                 }
426                                 ttable[c * tt_ssize + s] = v;
427                                 if (++c == clt->channels)
428                                         c = 0;
429                                 if (++s == slv->channels)
430                                         s = 0;
431                         }
432                         break;
433                 case PLUG_ROUTE_POLICY_COPY:
434                         if (clt->channels < slv->channels) {
435                                 n = clt->channels;
436                         } else {
437                                 n = slv->channels;
438                         }
439                         for (c = 0; (int)c < n; c++)
440                                 ttable[c * tt_ssize + c] = SND_PCM_PLUGIN_ROUTE_FULL;
441                         break;
442                 default:
443                         SNDERR("Invalid route policy");
444                         break;
445                 }
446         }
447         err = snd_pcm_route_open(new, NULL, slv->format, (int) slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->slave, plug->slave != plug->req_slave);
448         if (err < 0)
449                 return err;
450         slv->channels = clt->channels;
451         slv->access = clt->access;
452         if (snd_pcm_format_linear(clt->format))
453                 slv->format = clt->format;
454         return 1;
455 }
456
457 static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
458 {
459         snd_pcm_plug_t *plug = pcm->private_data;
460         int err;
461         snd_pcm_format_t cfmt;
462         int (*f)(snd_pcm_t **_pcm, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave);
463         if (snd_pcm_format_linear(slv->format)) {
464                 /* Conversion is done in another plugin */
465                 if (clt->format == slv->format ||
466                     clt->rate != slv->rate ||
467                     clt->channels != slv->channels)
468                         return 0;
469                 cfmt = clt->format;
470                 switch (clt->format) {
471                 case SND_PCM_FORMAT_MU_LAW:
472                         f = snd_pcm_mulaw_open;
473                         break;
474                 case SND_PCM_FORMAT_A_LAW:
475                         f = snd_pcm_alaw_open;
476                         break;
477                 case SND_PCM_FORMAT_IMA_ADPCM:
478                         f = snd_pcm_adpcm_open;
479                         break;
480                 default:
481                         if (snd_pcm_format_float(clt->format)) {
482                                 f = snd_pcm_lfloat_open;
483                         } else {
484                                 assert(snd_pcm_format_linear(clt->format));
485                                 f = snd_pcm_linear_open;
486                         }
487                         break;
488                 }
489         } else if (snd_pcm_format_float(slv->format)) {
490                 /* Conversion is done in another plugin */
491                 if (clt->format == slv->format &&
492                     clt->rate == slv->rate &&
493                     clt->channels == slv->channels)
494                         return 0;
495                 cfmt = clt->format;
496                 if (snd_pcm_format_linear(clt->format))
497                         f = snd_pcm_lfloat_open;
498                 else {
499                         assert(0);      /* TODO */
500                         return -EINVAL;
501                 }
502         } else {
503                 /* No conversion is needed */
504                 if (clt->format == slv->format &&
505                     clt->rate == slv->rate &&
506                     clt->channels == clt->channels)
507                         return 0;
508                 switch (slv->format) {
509                 case SND_PCM_FORMAT_MU_LAW:
510                         f = snd_pcm_mulaw_open;
511                         break;
512                 case SND_PCM_FORMAT_A_LAW:
513                         f = snd_pcm_alaw_open;
514                         break;
515                 case SND_PCM_FORMAT_IMA_ADPCM:
516                         f = snd_pcm_adpcm_open;
517                         break;
518                 default:
519                         assert(0);
520                         return -EINVAL;
521                 }
522                 if (snd_pcm_format_linear(clt->format))
523                         cfmt = clt->format;
524                 else
525                         cfmt = SND_PCM_FORMAT_S16;
526         }
527         err = f(new, NULL, slv->format, plug->slave, plug->slave != plug->req_slave);
528         if (err < 0)
529                 return err;
530         slv->format = cfmt;
531         slv->access = clt->access;
532         return 1;
533 }
534
535 static int snd_pcm_plug_change_access(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
536 {
537         snd_pcm_plug_t *plug = pcm->private_data;
538         int err;
539         if (clt->access == slv->access)
540                 return 0;
541         err = snd_pcm_copy_open(new, NULL, plug->slave, plug->slave != plug->req_slave);
542         if (err < 0)
543                 return err;
544         slv->access = clt->access;
545         return 1;
546 }
547
548 static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm,
549                                        snd_pcm_plug_params_t *client,
550                                        snd_pcm_plug_params_t *slave)
551 {
552         snd_pcm_plug_t *plug = pcm->private_data;
553         int (*funcs[])(snd_pcm_t *_pcm, snd_pcm_t **new, snd_pcm_plug_params_t *s, snd_pcm_plug_params_t *d) = {
554                 snd_pcm_plug_change_format,
555                 snd_pcm_plug_change_channels,
556                 snd_pcm_plug_change_rate,
557                 snd_pcm_plug_change_channels,
558                 snd_pcm_plug_change_format,
559                 snd_pcm_plug_change_access
560         };
561         snd_pcm_plug_params_t p = *slave;
562         unsigned int k = 0;
563         plug->ttable_ok = 0;
564         while (client->format != p.format ||
565                client->channels != p.channels ||
566                client->rate != p.rate ||
567                client->access != p.access) {
568                 snd_pcm_t *new;
569                 int err;
570                 assert(k < sizeof(funcs)/sizeof(*funcs));
571                 err = funcs[k](pcm, &new, client, &p);
572                 if (err < 0) {
573                         snd_pcm_plug_clear(pcm);
574                         return err;
575                 }
576                 if (err) {
577                         plug->slave = new;
578                         pcm->fast_ops = new->fast_ops;
579                         pcm->fast_op_arg = new->fast_op_arg;
580                 }
581                 k++;
582         }
583         /* it's exception, user specified ttable, but no reduction/expand */
584         if (plug->ttable && !plug->ttable_ok) {
585                 snd_pcm_t *new;
586                 int err;
587                 err = snd_pcm_plug_change_channels(pcm, &new, client, &p);
588                 if (err < 0) {
589                         snd_pcm_plug_clear(pcm);
590                         return err;
591                 }
592                 assert(err);
593                 assert(plug->ttable_ok);
594                 plug->slave = new;
595                 pcm->fast_ops = new->fast_ops;
596                 pcm->fast_op_arg = new->fast_op_arg;
597         }
598         return 0;
599 }
600
601 static int snd_pcm_plug_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
602 {
603         unsigned int rate_min, channels_max;
604         int err;
605
606         /* HACK: to avoid overflow in PARTBIT_RATE code */
607         err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, NULL);
608         if (err < 0)
609                 return err;
610         if (rate_min < 4000) {
611                 _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, 4000, 0);
612                 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_RATE))
613                         return -EINVAL;
614         }
615         /* HACK: to avoid overflow in PERIOD_SIZE code */
616         err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_CHANNELS, &channels_max, NULL);
617         if (err < 0)
618                 return err;
619         if (channels_max > 10000) {
620                 _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_CHANNELS, 10000, 0);
621                 if (snd_pcm_hw_param_empty(params, SND_PCM_HW_PARAM_CHANNELS))
622                         return -EINVAL;
623         }
624         return 0;
625 }
626
627 static int snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
628 {
629         snd_pcm_plug_t *plug = pcm->private_data;
630         int err;
631         
632         _snd_pcm_hw_params_any(sparams);
633         if (plug->sformat >= 0) {
634                 _snd_pcm_hw_params_set_format(sparams, plug->sformat);
635                 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
636         }
637         if (plug->schannels > 0)
638                 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
639                                       plug->schannels, 0);
640         if (plug->srate > 0)
641                 _snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
642                                               plug->srate, 0, plug->srate + 1, -1);
643         /* reduce the available configurations */
644         err = snd_pcm_hw_refine(plug->req_slave, sparams);
645         if (err < 0)
646                 return err;
647         return 0;
648 }
649
650 static int snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
651                                           snd_pcm_hw_params_t *sparams)
652 {
653         snd_pcm_plug_t *plug = pcm->private_data;
654         snd_pcm_t *slave = plug->req_slave;
655         unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
656                               SND_PCM_HW_PARBIT_TICK_TIME);
657         const snd_pcm_format_mask_t *format_mask, *sformat_mask;
658         snd_pcm_format_mask_t sfmt_mask;
659         int err;
660         snd_pcm_format_t format;
661         snd_interval_t t, buffer_size;
662         const snd_interval_t *srate, *crate;
663
664         if (plug->srate == -2)
665                 links |= SND_PCM_HW_PARBIT_RATE;
666         else {
667                 err = snd_pcm_hw_param_refine_multiple(slave, sparams, SND_PCM_HW_PARAM_RATE, params);
668                 if (err < 0)
669                         return err;
670         }
671         
672         if (plug->schannels == -2)
673                 links |= SND_PCM_HW_PARBIT_CHANNELS;
674         else {
675                 err = snd_pcm_hw_param_refine_near(slave, sparams, SND_PCM_HW_PARAM_CHANNELS, params);
676                 if (err < 0)
677                         return err;
678         }
679         if (plug->sformat == -2)
680                 links |= SND_PCM_HW_PARBIT_FORMAT;
681         else {
682                 format_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_FORMAT);
683                 sformat_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_FORMAT);
684                 snd_mask_none(&sfmt_mask);
685                 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
686                         snd_pcm_format_t f;
687                         if (!snd_pcm_format_mask_test(format_mask, format))
688                                 continue;
689                         if (snd_pcm_format_mask_test(sformat_mask, format))
690                                 f = format;
691                         else {
692                                 f = snd_pcm_plug_slave_format(format, sformat_mask);
693                                 if (f == SND_PCM_FORMAT_UNKNOWN)
694                                         continue;
695                         }
696                         snd_pcm_format_mask_set(&sfmt_mask, f);
697                 }
698
699                 if (snd_pcm_format_mask_empty(&sfmt_mask)) {
700                         SNDERR("Unable to find an usable slave format for '%s'", pcm->name);
701                         for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
702                                 if (!snd_pcm_format_mask_test(format_mask, format))
703                                         continue;
704                                 SNDERR("Format: %s", snd_pcm_format_name(format));
705                         }
706                         for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
707                                 if (!snd_pcm_format_mask_test(sformat_mask, format))
708                                         continue;
709                                 SNDERR("Slave format: %s", snd_pcm_format_name(format));
710                         }
711                         return -EINVAL;
712                 }
713                 err = snd_pcm_hw_param_set_mask(slave, sparams, SND_CHANGE,
714                                                 SND_PCM_HW_PARAM_FORMAT, &sfmt_mask);
715                 if (err < 0)
716                         return -EINVAL;
717         }
718
719         if (snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_FORMAT, sparams) ||
720             snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_CHANNELS, sparams) ||
721             snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_RATE, sparams) ||
722             snd_pcm_hw_param_never_eq(params, SND_PCM_HW_PARAM_ACCESS, sparams)) {
723                 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_MMAP };
724                 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
725                                            &access_mask);
726                 if (snd_pcm_access_mask_empty(snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS))) {
727                         SNDERR("Unable to find an usable access for '%s'", pcm->name);
728                         return -EINVAL;
729                 }
730         }
731         if ((links & SND_PCM_HW_PARBIT_RATE) ||
732             snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
733                 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
734                           SND_PCM_HW_PARBIT_BUFFER_SIZE);
735         else {
736                 snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
737                 snd_interval_unfloor(&buffer_size);
738                 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
739                 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
740                 snd_interval_muldiv(&buffer_size, srate, crate, &t);
741                 err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
742                 if (err < 0)
743                         return err;
744         }
745         err = _snd_pcm_hw_params_refine(sparams, links, params);
746         if (err < 0)
747                 return err;
748         return 0;
749 }
750         
751 static int snd_pcm_plug_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
752                                           snd_pcm_hw_params_t *params,
753                                           snd_pcm_hw_params_t *sparams)
754 {
755         snd_pcm_plug_t *plug = pcm->private_data;
756         unsigned int links = (SND_PCM_HW_PARBIT_PERIOD_TIME |
757                               SND_PCM_HW_PARBIT_TICK_TIME);
758         const snd_pcm_format_mask_t *format_mask, *sformat_mask;
759         snd_pcm_format_mask_t fmt_mask;
760         int err;
761         snd_pcm_format_t format;
762         snd_interval_t t;
763         const snd_interval_t *sbuffer_size;
764         const snd_interval_t *srate, *crate;
765
766         if (plug->schannels == -2)
767                 links |= SND_PCM_HW_PARBIT_CHANNELS;
768
769         if (plug->sformat == -2)
770                 links |= SND_PCM_HW_PARBIT_FORMAT;
771         else {
772                 format_mask = snd_pcm_hw_param_get_mask(params,
773                                                         SND_PCM_HW_PARAM_FORMAT);
774                 sformat_mask = snd_pcm_hw_param_get_mask(sparams,
775                                                          SND_PCM_HW_PARAM_FORMAT);
776                 snd_mask_none(&fmt_mask);
777                 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
778                         snd_pcm_format_t f;
779                         if (!snd_pcm_format_mask_test(format_mask, format))
780                                 continue;
781                         if (snd_pcm_format_mask_test(sformat_mask, format))
782                                 f = format;
783                         else {
784                                 f = snd_pcm_plug_slave_format(format, sformat_mask);
785                                 if (f == SND_PCM_FORMAT_UNKNOWN)
786                                         continue;
787                         }
788                         snd_pcm_format_mask_set(&fmt_mask, format);
789                 }
790
791                 if (snd_pcm_format_mask_empty(&fmt_mask)) {
792                         SNDERR("Unable to find an usable client format");
793                         for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
794                                 if (!snd_pcm_format_mask_test(format_mask, format))
795                                         continue;
796                                 SNDERR("Format: %s", snd_pcm_format_name(format));
797                         }
798                         for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
799                                 if (!snd_pcm_format_mask_test(sformat_mask, format))
800                                         continue;
801                                 SNDERR("Slave format: %s", snd_pcm_format_name(format));
802                         }
803                         return -EINVAL;
804                 }
805                 
806                 err = _snd_pcm_hw_param_set_mask(params, 
807                                                  SND_PCM_HW_PARAM_FORMAT, &fmt_mask);
808                 if (err < 0)
809                         return err;
810         }
811
812         if (plug->srate == -2)
813                 links |= SND_PCM_HW_PARBIT_RATE;
814         else {
815                 unsigned int rate_min, srate_min;
816                 int rate_mindir, srate_mindir;
817                 
818                 /* This is a temporary hack, waiting for a better solution */
819                 err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_RATE, &rate_min, &rate_mindir);
820                 if (err < 0)
821                         return err;
822                 err = snd_pcm_hw_param_get_min(sparams, SND_PCM_HW_PARAM_RATE, &srate_min, &srate_mindir);
823                 if (err < 0)
824                         return err;
825                 if (rate_min == srate_min && srate_mindir > rate_mindir) {
826                         err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, srate_min, srate_mindir);
827                         if (err < 0)
828                                 return err;
829                 }
830         }
831         if ((links & SND_PCM_HW_PARBIT_RATE) ||
832             snd_pcm_hw_param_always_eq(params, SND_PCM_HW_PARAM_RATE, sparams))
833                 links |= (SND_PCM_HW_PARBIT_PERIOD_SIZE |
834                           SND_PCM_HW_PARBIT_BUFFER_SIZE);
835         else {
836                 sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
837                 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
838                 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
839                 snd_interval_muldiv(sbuffer_size, crate, srate, &t);
840                 snd_interval_floor(&t);
841                 if (snd_interval_empty(&t))
842                         return -EINVAL;
843                 err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
844                 if (err < 0)
845                         return err;
846         }
847         err = _snd_pcm_hw_params_refine(params, links, sparams);
848         if (err < 0)
849                 return err;
850         /* FIXME */
851         params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
852         return 0;
853 }
854
855 static int snd_pcm_plug_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
856 {
857         snd_pcm_plug_t *plug = pcm->private_data;
858         return snd_pcm_hw_refine(plug->req_slave, params);
859 }
860
861 static int snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
862 {
863         return snd_pcm_hw_refine_slave(pcm, params,
864                                        snd_pcm_plug_hw_refine_cprepare,
865                                        snd_pcm_plug_hw_refine_cchange,
866                                        snd_pcm_plug_hw_refine_sprepare,
867                                        snd_pcm_plug_hw_refine_schange,
868                                        snd_pcm_plug_hw_refine_slave);
869 }
870
871 static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
872 {
873         snd_pcm_plug_t *plug = pcm->private_data;
874         snd_pcm_t *slave = plug->req_slave;
875         snd_pcm_plug_params_t clt_params, slv_params;
876         snd_pcm_hw_params_t sparams;
877         int err;
878
879         err = snd_pcm_plug_hw_refine_sprepare(pcm, &sparams);
880         assert(err >= 0);
881         err = snd_pcm_plug_hw_refine_schange(pcm, params, &sparams);
882         assert(err >= 0);
883         err = snd_pcm_hw_refine_soft(slave, &sparams);
884         assert(err >= 0);
885
886         INTERNAL(snd_pcm_hw_params_get_access)(params, &clt_params.access);
887         INTERNAL(snd_pcm_hw_params_get_format)(params, &clt_params.format);
888         INTERNAL(snd_pcm_hw_params_get_channels)(params, &clt_params.channels);
889         INTERNAL(snd_pcm_hw_params_get_rate)(params, &clt_params.rate, 0);
890
891         INTERNAL(snd_pcm_hw_params_get_format)(&sparams, &slv_params.format);
892         INTERNAL(snd_pcm_hw_params_get_channels)(&sparams, &slv_params.channels);
893         INTERNAL(snd_pcm_hw_params_get_rate)(&sparams, &slv_params.rate, 0);
894         snd_pcm_plug_clear(pcm);
895         if (!(clt_params.format == slv_params.format &&
896               clt_params.channels == slv_params.channels &&
897               clt_params.rate == slv_params.rate &&
898               !plug->ttable &&
899               snd_pcm_hw_params_test_access(slave, &sparams,
900                                             clt_params.access) >= 0)) {
901                 INTERNAL(snd_pcm_hw_params_set_access_first)(slave, &sparams, &slv_params.access);
902                 err = snd_pcm_plug_insert_plugins(pcm, &clt_params, &slv_params);
903                 if (err < 0)
904                         return err;
905         }
906         slave = plug->slave;
907         err = _snd_pcm_hw_params(slave, params);
908         if (err < 0) {
909                 snd_pcm_plug_clear(pcm);
910                 return err;
911         }
912         snd_pcm_unlink_hw_ptr(pcm, plug->req_slave);
913         snd_pcm_unlink_appl_ptr(pcm, plug->req_slave);
914         snd_pcm_link_hw_ptr(pcm, slave);
915         snd_pcm_link_appl_ptr(pcm, slave);
916         return 0;
917 }
918
919 static int snd_pcm_plug_hw_free(snd_pcm_t *pcm)
920 {
921         snd_pcm_plug_t *plug = pcm->private_data;
922         snd_pcm_t *slave = plug->slave;
923         int err = snd_pcm_hw_free(slave);
924         snd_pcm_plug_clear(pcm);
925         return err;
926 }
927
928 static int snd_pcm_plug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
929 {
930         snd_pcm_plug_t *plug = pcm->private_data;
931         return snd_pcm_sw_params(plug->slave, params);
932 }
933
934 static int snd_pcm_plug_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
935 {
936         snd_pcm_plug_t *plug = pcm->private_data;
937         return snd_pcm_channel_info(plug->slave, info);
938 }
939
940 static int snd_pcm_plug_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
941 {
942         return 0;
943 }
944
945 static int snd_pcm_plug_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
946 {
947         return 0;
948 }
949
950 static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)
951 {
952         snd_pcm_plug_t *plug = pcm->private_data;
953         snd_output_printf(out, "Plug PCM: ");
954         snd_pcm_dump(plug->slave, out);
955 }
956
957 static snd_pcm_ops_t snd_pcm_plug_ops = {
958         .close = snd_pcm_plug_close,
959         .info = snd_pcm_plug_info,
960         .hw_refine = snd_pcm_plug_hw_refine,
961         .hw_params = snd_pcm_plug_hw_params,
962         .hw_free = snd_pcm_plug_hw_free,
963         .sw_params = snd_pcm_plug_sw_params,
964         .channel_info = snd_pcm_plug_channel_info,
965         .dump = snd_pcm_plug_dump,
966         .nonblock = snd_pcm_plug_nonblock,
967         .async = snd_pcm_plug_async,
968         .poll_revents = snd_pcm_plug_poll_revents,
969         .mmap = snd_pcm_plug_mmap,
970         .munmap = snd_pcm_plug_munmap,
971 };
972
973 /**
974  * \brief Creates a new Plug PCM
975  * \param pcmp Returns created PCM handle
976  * \param name Name of PCM
977  * \param sformat Slave (destination) format
978  * \param slave Slave PCM handle
979  * \param close_slave When set, the slave PCM handle is closed with copy PCM
980  * \retval zero on success otherwise a negative error code
981  * \warning Using of this function might be dangerous in the sense
982  *          of compatibility reasons. The prototype might be freely
983  *          changed in future.
984  */
985 int snd_pcm_plug_open(snd_pcm_t **pcmp,
986                       const char *name,
987                       snd_pcm_format_t sformat, int schannels, int srate,
988                       enum snd_pcm_plug_route_policy route_policy,
989                       snd_pcm_route_ttable_entry_t *ttable,
990                       unsigned int tt_ssize,
991                       unsigned int tt_cused, unsigned int tt_sused,
992                       snd_pcm_t *slave, int close_slave)
993 {
994         snd_pcm_t *pcm;
995         snd_pcm_plug_t *plug;
996         int err;
997         assert(pcmp && slave);
998
999         plug = calloc(1, sizeof(snd_pcm_plug_t));
1000         if (!plug)
1001                 return -ENOMEM;
1002         plug->sformat = sformat;
1003         plug->schannels = schannels;
1004         plug->srate = srate;
1005         plug->slave = plug->req_slave = slave;
1006         plug->close_slave = close_slave;
1007         plug->route_policy = route_policy;
1008         plug->ttable = ttable;
1009         plug->tt_ssize = tt_ssize;
1010         plug->tt_cused = tt_cused;
1011         plug->tt_sused = tt_sused;
1012         
1013         err = snd_pcm_new(&pcm, SND_PCM_TYPE_PLUG, name, slave->stream, slave->mode);
1014         if (err < 0) {
1015                 free(plug);
1016                 return err;
1017         }
1018         pcm->ops = &snd_pcm_plug_ops;
1019         pcm->fast_ops = slave->fast_ops;
1020         pcm->fast_op_arg = slave->fast_op_arg;
1021         pcm->private_data = plug;
1022         pcm->poll_fd = slave->poll_fd;
1023         pcm->poll_events = slave->poll_events;
1024         snd_pcm_link_hw_ptr(pcm, slave);
1025         snd_pcm_link_appl_ptr(pcm, slave);
1026         *pcmp = pcm;
1027
1028         return 0;
1029 }
1030
1031 /*! \page pcm_plugins
1032
1033 \section pcm_plugins_plug Automatic conversion plugin
1034
1035 This plugin converts channels, rate and format on request.
1036
1037 \code
1038 pcm.name {
1039         type plug               # Automatic conversion PCM
1040         slave STR               # Slave name
1041         # or
1042         slave {                 # Slave definition
1043                 pcm STR         # Slave PCM name
1044                 # or
1045                 pcm { }         # Slave PCM definition
1046                 [format STR]    # Slave format (default nearest) or "unchanged"
1047                 [channels INT]  # Slave channels (default nearest) or "unchanged"
1048                 [rate INT]      # Slave rate (default nearest) or "unchanged"
1049         }
1050         route_policy STR        # route policy for automatic ttable generation
1051                                 # STR can be 'default', 'average', 'copy', 'duplicate'
1052                                 # average: result is average of input channels
1053                                 # copy: only first channels are copied to destination
1054                                 # duplicate: duplicate first set of channels
1055                                 # default: copy policy, except for mono capture - sum
1056         ttable {                # Transfer table (bi-dimensional compound of cchannels * schannels numbers)
1057                 CCHANNEL {
1058                         SCHANNEL REAL   # route value (0.0 - 1.0)
1059                 }
1060         }
1061 }
1062 \endcode
1063
1064 \subsection pcm_plugins_plug_funcref Function reference
1065
1066 <UL>
1067   <LI>snd_pcm_plug_open()
1068   <LI>_snd_pcm_plug_open()
1069 </UL>
1070
1071 */
1072
1073 /**
1074  * \brief Creates a new Plug PCM
1075  * \param pcmp Returns created PCM handle
1076  * \param name Name of PCM
1077  * \param root Root configuration node
1078  * \param conf Configuration node with Plug PCM description
1079  * \param stream Stream type
1080  * \param mode Stream mode
1081  * \retval zero on success otherwise a negative error code
1082  * \warning Using of this function might be dangerous in the sense
1083  *          of compatibility reasons. The prototype might be freely
1084  *          changed in future.
1085  */
1086 int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
1087                        snd_config_t *root, snd_config_t *conf, 
1088                        snd_pcm_stream_t stream, int mode)
1089 {
1090         snd_config_iterator_t i, next;
1091         int err;
1092         snd_pcm_t *spcm;
1093         snd_config_t *slave = NULL, *sconf;
1094         snd_config_t *tt = NULL;
1095         enum snd_pcm_plug_route_policy route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1096         snd_pcm_route_ttable_entry_t *ttable = NULL;
1097         unsigned int csize, ssize;
1098         unsigned int cused, sused;
1099         snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1100         int schannels = -1, srate = -1;
1101         snd_config_for_each(i, next, conf) {
1102                 snd_config_t *n = snd_config_iterator_entry(i);
1103                 const char *id;
1104                 if (snd_config_get_id(n, &id) < 0)
1105                         continue;
1106                 if (snd_pcm_conf_generic_id(id))
1107                         continue;
1108                 if (strcmp(id, "slave") == 0) {
1109                         slave = n;
1110                         continue;
1111                 }
1112                 if (strcmp(id, "ttable") == 0) {
1113                         route_policy = PLUG_ROUTE_POLICY_NONE;
1114                         if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1115                                 SNDERR("Invalid type for %s", id);
1116                                 return -EINVAL;
1117                         }
1118                         tt = n;
1119                         continue;
1120                 }
1121                 if (strcmp(id, "route_policy") == 0) {
1122                         const char *str;
1123                         if ((err = snd_config_get_string(n, &str)) < 0) {
1124                                 SNDERR("Invalid type for %s", id);
1125                                 return -EINVAL;
1126                         }
1127                         if (tt != NULL)
1128                                 SNDERR("Table is defined, route policy is ignored");
1129                         if (!strcmp(str, "default"))
1130                                 route_policy = PLUG_ROUTE_POLICY_DEFAULT;
1131                         else if (!strcmp(str, "average"))
1132                                 route_policy = PLUG_ROUTE_POLICY_AVERAGE;
1133                         else if (!strcmp(str, "copy"))
1134                                 route_policy = PLUG_ROUTE_POLICY_COPY;
1135                         else if (!strcmp(str, "duplicate"))
1136                                 route_policy = PLUG_ROUTE_POLICY_DUP;
1137                         continue;
1138                 }
1139                 SNDERR("Unknown field %s", id);
1140                 return -EINVAL;
1141         }
1142         if (!slave) {
1143                 SNDERR("slave is not defined");
1144                 return -EINVAL;
1145         }
1146         err = snd_pcm_slave_conf(root, slave, &sconf, 3,
1147                                  SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &sformat,
1148                                  SND_PCM_HW_PARAM_CHANNELS, SCONF_UNCHANGED, &schannels,
1149                                  SND_PCM_HW_PARAM_RATE, SCONF_UNCHANGED, &srate);
1150         if (err < 0)
1151                 return err;
1152         if (tt) {
1153                 err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);
1154                 if (err < 0) {
1155                         snd_config_delete(sconf);
1156                         return err;
1157                 }
1158                 ttable = malloc(csize * ssize * sizeof(*ttable));
1159                 if (ttable == NULL) {
1160                         snd_config_delete(sconf);
1161                         return err;
1162                 }
1163                 err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, -1);
1164                 if (err < 0) {
1165                         snd_config_delete(sconf);
1166                         return err;
1167                 }
1168         }
1169         
1170         err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode);
1171         snd_config_delete(sconf);
1172         if (err < 0)
1173                 return err;
1174         err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate,
1175                                 route_policy, ttable, ssize, cused, sused, spcm, 1);
1176         if (err < 0)
1177                 snd_pcm_close(spcm);
1178         return err;
1179 }
1180 #ifndef DOC_HIDDEN
1181 SND_DLSYM_BUILD_VERSION(_snd_pcm_plug_open, SND_PCM_DLSYM_VERSION);
1182 #endif