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