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