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