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 	defined(BUILD_PCM_PLUGIN_IEC958)
191 #define BUILD_PCM_NONLINEAR
192 #endif
193 
194 #ifdef BUILD_PCM_NONLINEAR
195 static 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
213 static 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 
228 static 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 
check_linear_format(const snd_pcm_format_mask_t *format_mask, int wid, int sgn, int ed)235 static 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 
snd_pcm_plug_slave_format(snd_pcm_format_t format, const snd_pcm_format_mask_t *format_mask)257 static 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 
snd_pcm_plug_clear(snd_pcm_t *pcm)351 static 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
367 typedef 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
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)376 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)
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
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)396 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)
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
iec958_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)499 static 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 
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)510 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)
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 
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)616 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)
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
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)630 static 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 
snd_pcm_plug_insert_plugins(snd_pcm_t *pcm, snd_pcm_plug_params_t *client, snd_pcm_plug_params_t *slave)667 static 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 
snd_pcm_plug_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)716 static 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 
snd_pcm_plug_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)742 static 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 
check_access_change(snd_pcm_hw_params_t *cparams, snd_pcm_hw_params_t *sparams)765 static 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 
snd_pcm_plug_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_params_t *sparams)812 static 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 
snd_pcm_plug_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params, snd_pcm_hw_params_t *sparams)912 static 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 
snd_pcm_plug_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)1018 static 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 
snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)1024 static 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 
snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)1034 static 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 
snd_pcm_plug_hw_free(snd_pcm_t *pcm)1088 static 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 
snd_pcm_plug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)1097 static 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 
snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)1109 static 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 
1116 static 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  */
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)1146 int 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 
1208 This plugin converts channels, rate and format on request.
1209 
1210 \code
1211 pcm.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  */
_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)1264 int _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
1376 SND_DLSYM_BUILD_VERSION(_snd_pcm_plug_open, SND_PCM_DLSYM_VERSION);
1377 #endif
1378