xref: /third_party/alsa-lib/src/pcm/pcm_rate.c (revision d5ac70f0)
1/**
2 * \file pcm/pcm_rate.c
3 * \ingroup PCM_Plugins
4 * \brief PCM Rate Plugin Interface
5 * \author Abramo Bagnara <abramo@alsa-project.org>
6 * \author Jaroslav Kysela <perex@perex.cz>
7 * \date 2000-2004
8 */
9/*
10 *  PCM - Rate conversion
11 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
12 *                2004 by Jaroslav Kysela <perex@perex.cz>
13 *
14 *
15 *   This library is free software; you can redistribute it and/or modify
16 *   it under the terms of the GNU Lesser General Public License as
17 *   published by the Free Software Foundation; either version 2.1 of
18 *   the License, or (at your option) any later version.
19 *
20 *   This program is distributed in the hope that it will be useful,
21 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
22 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 *   GNU Lesser General Public License for more details.
24 *
25 *   You should have received a copy of the GNU Lesser General Public
26 *   License along with this library; if not, write to the Free Software
27 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
28 *
29 */
30#include "pcm_local.h"
31#include "pcm_plugin.h"
32#include "pcm_rate.h"
33#include "plugin_ops.h"
34#include "bswap.h"
35#include <inttypes.h>
36
37#if 0
38#define DEBUG_REFINE
39#endif
40
41#ifndef PIC
42/* entry for static linking */
43const char *_snd_module_pcm_rate = "";
44#endif
45
46#ifndef DOC_HIDDEN
47
48typedef struct _snd_pcm_rate snd_pcm_rate_t;
49
50struct _snd_pcm_rate {
51	snd_pcm_generic_t gen;
52	snd_pcm_uframes_t appl_ptr, hw_ptr, last_slave_hw_ptr;
53	snd_pcm_uframes_t last_commit_ptr;
54	snd_pcm_uframes_t orig_avail_min;
55	snd_pcm_sw_params_t sw_params;
56	snd_pcm_format_t sformat;
57	unsigned int srate;
58	snd_pcm_channel_area_t *pareas;	/* areas for splitted period (rate pcm) */
59	snd_pcm_channel_area_t *sareas;	/* areas for splitted period (slave pcm) */
60	snd_pcm_rate_info_t info;
61	void *open_func;
62	void *obj;
63	snd_pcm_rate_ops_t ops;
64	unsigned int src_conv_idx;
65	unsigned int dst_conv_idx;
66	snd_pcm_channel_area_t *src_buf;
67	snd_pcm_channel_area_t *dst_buf;
68	int start_pending; /* start is triggered but not commited to slave */
69	snd_htimestamp_t trigger_tstamp;
70	unsigned int plugin_version;
71	unsigned int rate_min, rate_max;
72	snd_pcm_format_t orig_in_format;
73	snd_pcm_format_t orig_out_format;
74	uint64_t in_formats;
75	uint64_t out_formats;
76	unsigned int format_flags;
77};
78
79#define SND_PCM_RATE_PLUGIN_VERSION_OLD	0x010001	/* old rate plugin */
80#endif /* DOC_HIDDEN */
81
82/* allocate a channel area and a temporary buffer for the given size */
83static snd_pcm_channel_area_t *
84rate_alloc_tmp_buf(snd_pcm_format_t format,
85		   unsigned int channels, unsigned int frames)
86{
87	snd_pcm_channel_area_t *ap;
88	int width = snd_pcm_format_physical_width(format);
89	unsigned int i;
90
91	ap = malloc(sizeof(*ap) * channels);
92	if (!ap)
93		return NULL;
94	ap->addr = malloc(frames * channels * width / 8);
95	if (!ap->addr) {
96		free(ap);
97		return NULL;
98	}
99
100	/* set up in interleaved format */
101	for (i = 0; i < channels; i++) {
102		ap[i].addr = ap[0].addr + (i * width) / 8;
103		ap[i].first = 0;
104		ap[i].step = width * channels;
105	}
106
107	return ap;
108}
109
110static void rate_free_tmp_buf(snd_pcm_channel_area_t **ptr)
111{
112	snd_pcm_channel_area_t *c = *ptr;
113
114	if (c) {
115		free(c->addr);
116		free(c);
117		*ptr = NULL;
118	}
119}
120
121static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
122{
123	snd_pcm_rate_t *rate = pcm->private_data;
124	int err;
125	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
126	snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
127	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
128					 &access_mask);
129	if (err < 0)
130		return err;
131	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
132					 &format_mask);
133	if (err < 0)
134		return err;
135	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
136	if (err < 0)
137		return err;
138	if (rate->rate_min) {
139		err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE,
140						rate->rate_min, 0);
141		if (err < 0)
142			return err;
143	}
144	if (rate->rate_max) {
145		err = _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_RATE,
146						rate->rate_max, 0);
147		if (err < 0)
148			return err;
149	}
150	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
151	return 0;
152}
153
154static int snd_pcm_rate_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
155{
156	snd_pcm_rate_t *rate = pcm->private_data;
157	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
158	_snd_pcm_hw_params_any(sparams);
159	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
160				   &saccess_mask);
161	if (rate->sformat != SND_PCM_FORMAT_UNKNOWN) {
162		_snd_pcm_hw_params_set_format(sparams, rate->sformat);
163		_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
164	}
165	_snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
166				     rate->srate, 0, rate->srate + 1, -1);
167	return 0;
168}
169
170static int snd_pcm_rate_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
171					  snd_pcm_hw_params_t *sparams)
172{
173	snd_pcm_rate_t *rate = pcm->private_data;
174	snd_interval_t t, buffer_size;
175	const snd_interval_t *srate, *crate;
176	int err;
177	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
178			      SND_PCM_HW_PARBIT_PERIOD_TIME |
179			      SND_PCM_HW_PARBIT_TICK_TIME);
180	if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
181		links |= (SND_PCM_HW_PARBIT_FORMAT |
182			  SND_PCM_HW_PARBIT_SUBFORMAT |
183			  SND_PCM_HW_PARBIT_SAMPLE_BITS |
184			  SND_PCM_HW_PARBIT_FRAME_BITS);
185	snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
186	snd_interval_unfloor(&buffer_size);
187	crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
188	srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
189	snd_interval_muldiv(&buffer_size, srate, crate, &t);
190	err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
191	if (err < 0)
192		return err;
193	err = _snd_pcm_hw_params_refine(sparams, links, params);
194	if (err < 0)
195		return err;
196	return 0;
197}
198
199static int snd_pcm_rate_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
200					  snd_pcm_hw_params_t *sparams)
201{
202	snd_pcm_rate_t *rate = pcm->private_data;
203	snd_interval_t t;
204#ifdef DEBUG_REFINE
205	snd_output_t *out;
206#endif
207	const snd_interval_t *sbuffer_size, *buffer_size;
208	const snd_interval_t *srate, *crate;
209	int err;
210	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
211			      SND_PCM_HW_PARBIT_PERIOD_TIME |
212			      SND_PCM_HW_PARBIT_TICK_TIME);
213	if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
214		links |= (SND_PCM_HW_PARBIT_FORMAT |
215			  SND_PCM_HW_PARBIT_SUBFORMAT |
216			  SND_PCM_HW_PARBIT_SAMPLE_BITS |
217			  SND_PCM_HW_PARBIT_FRAME_BITS);
218	sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
219	crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
220	srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
221	snd_interval_muldiv(sbuffer_size, crate, srate, &t);
222	snd_interval_floor(&t);
223	err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
224	if (err < 0)
225		return err;
226	buffer_size = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE);
227	/*
228	 * this condition probably needs more work:
229	 *   in case when the buffer_size is known and we are looking
230	 *   for best period_size, we should prefer situation when
231	 *   (buffer_size / period_size) * period_size == buffer_size
232	 */
233	if (snd_interval_single(buffer_size) && buffer_size->integer) {
234		snd_interval_t *period_size;
235		period_size = (snd_interval_t *)snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE);
236		if (!snd_interval_checkempty(period_size) &&
237		    period_size->openmin && period_size->openmax &&
238		    period_size->min + 1 == period_size->max) {
239			if (period_size->min > 0 && (buffer_size->min / period_size->min) * period_size->min == buffer_size->min) {
240		    		snd_interval_set_value(period_size, period_size->min);
241		    	} else if ((buffer_size->max / period_size->max) * period_size->max == buffer_size->max) {
242		    		snd_interval_set_value(period_size, period_size->max);
243		    	}
244		}
245	}
246#ifdef DEBUG_REFINE
247	snd_output_stdio_attach(&out, stderr, 0);
248	snd_output_printf(out, "REFINE (params):\n");
249	snd_pcm_hw_params_dump(params, out);
250	snd_output_printf(out, "REFINE (slave params):\n");
251	snd_pcm_hw_params_dump(sparams, out);
252	snd_output_close(out);
253#endif
254	err = _snd_pcm_hw_params_refine(params, links, sparams);
255#ifdef DEBUG_REFINE
256	snd_output_stdio_attach(&out, stderr, 0);
257	snd_output_printf(out, "********************\n");
258	snd_output_printf(out, "REFINE (params) (%i):\n", err);
259	snd_pcm_hw_params_dump(params, out);
260	snd_output_printf(out, "REFINE (slave params):\n");
261	snd_pcm_hw_params_dump(sparams, out);
262	snd_output_close(out);
263#endif
264	if (err < 0)
265		return err;
266	return 0;
267}
268
269static int snd_pcm_rate_hw_refine(snd_pcm_t *pcm,
270				  snd_pcm_hw_params_t *params)
271{
272	return snd_pcm_hw_refine_slave(pcm, params,
273				       snd_pcm_rate_hw_refine_cprepare,
274				       snd_pcm_rate_hw_refine_cchange,
275				       snd_pcm_rate_hw_refine_sprepare,
276				       snd_pcm_rate_hw_refine_schange,
277				       snd_pcm_generic_hw_refine);
278}
279
280/* evaluate the best matching available format to the given format */
281static int get_best_format(uint64_t mask, snd_pcm_format_t orig)
282{
283	int pwidth = snd_pcm_format_physical_width(orig);
284	int width = snd_pcm_format_width(orig);
285	int signd = snd_pcm_format_signed(orig);
286	int best_score = -1;
287	int match = -1;
288	int f, score;
289
290	for (f = 0; f <= SND_PCM_FORMAT_LAST; f++) {
291		if (!(mask & (1ULL << f)))
292			continue;
293		score = 0;
294		if (snd_pcm_format_linear(f)) {
295			if (snd_pcm_format_physical_width(f) == pwidth)
296				score++;
297			if (snd_pcm_format_physical_width(f) >= pwidth)
298				score++;
299			if (snd_pcm_format_width(f) == width)
300				score++;
301			if (snd_pcm_format_signed(f) == signd)
302				score++;
303		}
304		if (score > best_score) {
305			match = f;
306			best_score = score;
307		}
308	}
309
310	return match;
311}
312
313/* set up the input and output formats from the available lists */
314static int choose_preferred_format(snd_pcm_rate_t *rate)
315{
316	uint64_t in_mask = rate->in_formats;
317	uint64_t out_mask = rate->out_formats;
318	int in, out;
319
320	if (!in_mask || !out_mask)
321		return 0;
322
323	if (rate->orig_in_format == rate->orig_out_format)
324		if (in_mask & out_mask & (1ULL << rate->orig_in_format))
325			return 0; /* nothing changed */
326
327 repeat:
328	in = get_best_format(in_mask, rate->orig_in_format);
329	out = get_best_format(out_mask, rate->orig_out_format);
330	if (in < 0 || out < 0)
331		return -ENOENT;
332
333	if ((rate->format_flags & SND_PCM_RATE_FLAG_SYNC_FORMATS) &&
334	    in != out) {
335		if (out_mask & (1ULL << in))
336			out = in;
337		else if (in_mask & (1ULL << out))
338			in = out;
339		else {
340			in_mask &= ~(1ULL << in);
341			out_mask &= ~(1ULL << out);
342			goto repeat;
343		}
344	}
345
346	rate->info.in.format = in;
347	rate->info.out.format = out;
348	return 0;
349}
350
351static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
352{
353	snd_pcm_rate_t *rate = pcm->private_data;
354	snd_pcm_t *slave = rate->gen.slave;
355	snd_pcm_rate_side_info_t *sinfo, *cinfo;
356	unsigned int channels, acc;
357	int need_src_buf, need_dst_buf;
358	int err = snd_pcm_hw_params_slave(pcm, params,
359					  snd_pcm_rate_hw_refine_cchange,
360					  snd_pcm_rate_hw_refine_sprepare,
361					  snd_pcm_rate_hw_refine_schange,
362					  snd_pcm_generic_hw_params);
363	if (err < 0)
364		return err;
365
366	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
367		cinfo = &rate->info.in;
368		sinfo = &rate->info.out;
369	} else {
370		sinfo = &rate->info.in;
371		cinfo = &rate->info.out;
372	}
373	err = INTERNAL(snd_pcm_hw_params_get_format)(params, &cinfo->format);
374	if (err < 0)
375		return err;
376	err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &cinfo->rate, 0);
377	if (err < 0)
378		return err;
379	err = INTERNAL(snd_pcm_hw_params_get_period_size)(params, &cinfo->period_size, 0);
380	if (err < 0)
381		return err;
382	err = INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &cinfo->buffer_size);
383	if (err < 0)
384		return err;
385	err = INTERNAL(snd_pcm_hw_params_get_channels)(params, &channels);
386	if (err < 0)
387		return err;
388	err = INTERNAL(snd_pcm_hw_params_get_access)(params, &acc);
389	if (err < 0)
390		return err;
391
392	rate->info.channels = channels;
393	sinfo->format = slave->format;
394	sinfo->rate = slave->rate;
395	sinfo->buffer_size = slave->buffer_size;
396	sinfo->period_size = slave->period_size;
397
398	if (CHECK_SANITY(rate->pareas)) {
399		SNDMSG("rate plugin already in use");
400		return -EBUSY;
401	}
402
403	rate->pareas = rate_alloc_tmp_buf(cinfo->format, channels,
404					  cinfo->period_size);
405	rate->sareas = rate_alloc_tmp_buf(sinfo->format, channels,
406					  sinfo->period_size);
407	if (!rate->pareas || !rate->sareas) {
408		err = -ENOMEM;
409		goto error_pareas;
410	}
411
412	rate->orig_in_format = rate->info.in.format;
413	rate->orig_out_format = rate->info.out.format;
414	if (choose_preferred_format(rate) < 0) {
415		SNDERR("No matching format in rate plugin");
416		err = -EINVAL;
417		goto error_pareas;
418	}
419
420	err = rate->ops.init(rate->obj, &rate->info);
421	if (err < 0)
422		goto error_init;
423
424	rate_free_tmp_buf(&rate->src_buf);
425	rate_free_tmp_buf(&rate->dst_buf);
426
427	need_src_buf = need_dst_buf = 0;
428
429	if ((rate->format_flags & SND_PCM_RATE_FLAG_INTERLEAVED) &&
430	    !(acc == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
431	      acc == SND_PCM_ACCESS_RW_INTERLEAVED)) {
432		need_src_buf = need_dst_buf = 1;
433	} else {
434		if (rate->orig_in_format != rate->info.in.format)
435			need_src_buf = 1;
436		if (rate->orig_out_format != rate->info.out.format)
437			need_dst_buf = 1;
438	}
439
440	if (need_src_buf) {
441		rate->src_conv_idx =
442			snd_pcm_linear_convert_index(rate->orig_in_format,
443						     rate->info.in.format);
444		rate->src_buf = rate_alloc_tmp_buf(rate->info.in.format,
445						   channels, rate->info.in.period_size);
446		if (!rate->src_buf) {
447			err = -ENOMEM;
448			goto error;
449		}
450	}
451
452	if (need_dst_buf) {
453		rate->dst_conv_idx =
454			snd_pcm_linear_convert_index(rate->info.out.format,
455						     rate->orig_out_format);
456		rate->dst_buf = rate_alloc_tmp_buf(rate->info.out.format,
457						   channels, rate->info.out.period_size);
458		if (!rate->dst_buf) {
459			err = -ENOMEM;
460			goto error;
461		}
462	}
463
464	return 0;
465
466 error:
467	rate_free_tmp_buf(&rate->src_buf);
468	rate_free_tmp_buf(&rate->dst_buf);
469 error_init:
470	if (rate->ops.free)
471		rate->ops.free(rate->obj);
472 error_pareas:
473	rate_free_tmp_buf(&rate->pareas);
474	rate_free_tmp_buf(&rate->sareas);
475	return err;
476}
477
478static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
479{
480	snd_pcm_rate_t *rate = pcm->private_data;
481
482	rate_free_tmp_buf(&rate->pareas);
483	rate_free_tmp_buf(&rate->sareas);
484	if (rate->ops.free)
485		rate->ops.free(rate->obj);
486	rate_free_tmp_buf(&rate->src_buf);
487	rate_free_tmp_buf(&rate->dst_buf);
488	return snd_pcm_hw_free(rate->gen.slave);
489}
490
491static void recalc(snd_pcm_t *pcm, snd_pcm_uframes_t *val)
492{
493	snd_pcm_rate_t *rate = pcm->private_data;
494	snd_pcm_t *slave = rate->gen.slave;
495	unsigned long div;
496
497	if (*val == pcm->buffer_size) {
498		*val = slave->buffer_size;
499	} else {
500		div = *val / pcm->period_size;
501		if (div * pcm->period_size == *val)
502			*val = div * slave->period_size;
503		else
504			*val = muldiv_near(*val, slave->period_size, pcm->period_size);
505	}
506}
507
508static int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
509{
510	snd_pcm_rate_t *rate = pcm->private_data;
511	snd_pcm_t *slave = rate->gen.slave;
512	snd_pcm_sw_params_t *sparams;
513	snd_pcm_uframes_t boundary1, boundary2, sboundary;
514	int err;
515
516	sparams = &rate->sw_params;
517	err = snd_pcm_sw_params_current(slave, sparams);
518	if (err < 0)
519		return err;
520	sboundary = sparams->boundary;
521	*sparams = *params;
522	boundary1 = pcm->buffer_size;
523	boundary2 = slave->buffer_size;
524	while (boundary1 * 2 <= LONG_MAX - pcm->buffer_size &&
525	       boundary2 * 2 <= LONG_MAX - slave->buffer_size) {
526		boundary1 *= 2;
527		boundary2 *= 2;
528	}
529	params->boundary = boundary1;
530	sparams->boundary = sboundary;
531
532	if (rate->ops.adjust_pitch)
533		rate->ops.adjust_pitch(rate->obj, &rate->info);
534
535	recalc(pcm, &sparams->avail_min);
536	rate->orig_avail_min = sparams->avail_min;
537	recalc(pcm, &sparams->start_threshold);
538	if (sparams->avail_min < 1) sparams->avail_min = 1;
539	if (sparams->start_threshold <= slave->buffer_size) {
540		if (sparams->start_threshold > (slave->buffer_size / sparams->avail_min) * sparams->avail_min)
541			sparams->start_threshold = (slave->buffer_size / sparams->avail_min) * sparams->avail_min;
542	}
543	if (sparams->stop_threshold >= params->boundary) {
544		sparams->stop_threshold = sparams->boundary;
545	} else {
546		recalc(pcm, &sparams->stop_threshold);
547	}
548	recalc(pcm, &sparams->silence_threshold);
549	if (sparams->silence_size >= params->boundary) {
550		sparams->silence_size = sparams->boundary;
551	} else {
552		recalc(pcm, &sparams->silence_size);
553	}
554	return snd_pcm_sw_params(slave, sparams);
555}
556
557static int snd_pcm_rate_init(snd_pcm_t *pcm)
558{
559	snd_pcm_rate_t *rate = pcm->private_data;
560
561	if (rate->ops.reset)
562		rate->ops.reset(rate->obj);
563	rate->last_commit_ptr = 0;
564	rate->start_pending = 0;
565	return 0;
566}
567
568static void do_convert(const snd_pcm_channel_area_t *dst_areas,
569		       snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
570		       const snd_pcm_channel_area_t *src_areas,
571		       snd_pcm_uframes_t src_offset, unsigned int src_frames,
572		       unsigned int channels,
573		       snd_pcm_rate_t *rate)
574{
575	const snd_pcm_channel_area_t *out_areas;
576	snd_pcm_uframes_t out_offset;
577
578	if (rate->dst_buf) {
579		out_areas = rate->dst_buf;
580		out_offset = 0;
581	} else {
582		out_areas = dst_areas;
583		out_offset = dst_offset;
584	}
585
586	if (rate->src_buf) {
587		snd_pcm_linear_convert(rate->src_buf, 0,
588				       src_areas, src_offset,
589				       channels, src_frames,
590				       rate->src_conv_idx);
591		src_areas = rate->src_buf;
592		src_offset = 0;
593	}
594
595	if (rate->ops.convert)
596		rate->ops.convert(rate->obj, out_areas, out_offset, dst_frames,
597				   src_areas, src_offset, src_frames);
598	else
599		rate->ops.convert_s16(rate->obj,
600				      snd_pcm_channel_area_addr(out_areas, out_offset),
601				      dst_frames,
602				      snd_pcm_channel_area_addr(src_areas, src_offset),
603				      src_frames);
604	if (rate->dst_buf)
605		snd_pcm_linear_convert(dst_areas, dst_offset,
606				       rate->dst_buf, 0,
607				       channels, dst_frames,
608				       rate->dst_conv_idx);
609}
610
611static inline void
612snd_pcm_rate_write_areas1(snd_pcm_t *pcm,
613			 const snd_pcm_channel_area_t *areas,
614			 snd_pcm_uframes_t offset,
615			 const snd_pcm_channel_area_t *slave_areas,
616			 snd_pcm_uframes_t slave_offset)
617{
618	snd_pcm_rate_t *rate = pcm->private_data;
619	do_convert(slave_areas, slave_offset, rate->gen.slave->period_size,
620		   areas, offset, pcm->period_size,
621		   pcm->channels, rate);
622}
623
624static inline void
625snd_pcm_rate_read_areas1(snd_pcm_t *pcm,
626			 const snd_pcm_channel_area_t *areas,
627			 snd_pcm_uframes_t offset,
628			 const snd_pcm_channel_area_t *slave_areas,
629			 snd_pcm_uframes_t slave_offset)
630{
631	snd_pcm_rate_t *rate = pcm->private_data;
632	do_convert(areas, offset, pcm->period_size,
633		   slave_areas, slave_offset, rate->gen.slave->period_size,
634		   pcm->channels, rate);
635}
636
637static inline void snd_pcm_rate_sync_hwptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr)
638{
639	snd_pcm_rate_t *rate;
640	snd_pcm_sframes_t slave_hw_ptr_diff;
641	snd_pcm_sframes_t last_slave_hw_ptr_frac;
642
643	if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
644		return;
645
646	rate = pcm->private_data;
647	slave_hw_ptr_diff = pcm_frame_diff(slave_hw_ptr, rate->last_slave_hw_ptr, rate->gen.slave->boundary);
648	if (slave_hw_ptr_diff == 0)
649		return;
650	last_slave_hw_ptr_frac = rate->last_slave_hw_ptr % rate->gen.slave->period_size;
651	/* While handling fraction part fo slave period, rounded value will be
652	 * introduced by input_frames().
653	 * To eliminate rounding issue on rate->hw_ptr, subtract last rounded
654	 * value from rate->hw_ptr and add new rounded value of present
655	 * slave_hw_ptr fraction part to rate->hw_ptr. Hence,
656	 * rate->hw_ptr += [ (no. of updated slave periods * pcm rate period size) -
657	 * 	fractional part of last_slave_hw_ptr rounded value +
658	 * 	fractional part of updated slave hw ptr's rounded value ]
659	 */
660	rate->hw_ptr += (
661			(((last_slave_hw_ptr_frac + slave_hw_ptr_diff) / rate->gen.slave->period_size) * pcm->period_size) -
662			rate->ops.input_frames(rate->obj, last_slave_hw_ptr_frac) +
663			rate->ops.input_frames(rate->obj, (last_slave_hw_ptr_frac + slave_hw_ptr_diff) % rate->gen.slave->period_size));
664	rate->last_slave_hw_ptr = slave_hw_ptr;
665
666	rate->hw_ptr %= pcm->boundary;
667}
668
669static inline void snd_pcm_rate_sync_hwptr(snd_pcm_t *pcm)
670{
671	snd_pcm_rate_t *rate = pcm->private_data;
672	snd_pcm_rate_sync_hwptr0(pcm, *rate->gen.slave->hw.ptr);
673}
674
675static int snd_pcm_rate_hwsync(snd_pcm_t *pcm)
676{
677	snd_pcm_rate_t *rate = pcm->private_data;
678	int err = snd_pcm_hwsync(rate->gen.slave);
679	if (err < 0)
680		return err;
681	snd_pcm_rate_sync_hwptr(pcm);
682	return 0;
683}
684
685static snd_pcm_uframes_t snd_pcm_rate_playback_internal_delay(snd_pcm_t *pcm)
686{
687	snd_pcm_rate_t *rate = pcm->private_data;
688
689	return pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary);
690}
691
692static int snd_pcm_rate_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
693{
694	snd_pcm_rate_t *rate = pcm->private_data;
695	snd_pcm_sframes_t slave_delay;
696	int err;
697
698	snd_pcm_rate_hwsync(pcm);
699
700	err = snd_pcm_delay(rate->gen.slave, &slave_delay);
701	if (err < 0) {
702		return err;
703	}
704
705	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
706		*delayp = rate->ops.input_frames(rate->obj, slave_delay)
707				+ snd_pcm_rate_playback_internal_delay(pcm);
708	} else {
709		*delayp = rate->ops.output_frames(rate->obj, slave_delay)
710				+ snd_pcm_mmap_capture_delay(pcm);
711	}
712	return 0;
713}
714
715static int snd_pcm_rate_prepare(snd_pcm_t *pcm)
716{
717	snd_pcm_rate_t *rate = pcm->private_data;
718	int err;
719
720	err = snd_pcm_prepare(rate->gen.slave);
721	if (err < 0)
722		return err;
723	*pcm->hw.ptr = 0;
724	*pcm->appl.ptr = 0;
725	rate->last_slave_hw_ptr = 0;
726	err = snd_pcm_rate_init(pcm);
727	if (err < 0)
728		return err;
729	return 0;
730}
731
732static int snd_pcm_rate_reset(snd_pcm_t *pcm)
733{
734	snd_pcm_rate_t *rate = pcm->private_data;
735	int err;
736	err = snd_pcm_reset(rate->gen.slave);
737	if (err < 0)
738		return err;
739	*pcm->hw.ptr = 0;
740	*pcm->appl.ptr = 0;
741	rate->last_slave_hw_ptr = 0;
742	err = snd_pcm_rate_init(pcm);
743	if (err < 0)
744		return err;
745	return 0;
746}
747
748static snd_pcm_sframes_t snd_pcm_rate_rewindable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
749{
750	return 0;
751}
752
753static snd_pcm_sframes_t snd_pcm_rate_forwardable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
754{
755	return 0;
756}
757
758static snd_pcm_sframes_t snd_pcm_rate_rewind(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
759                                             snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)
760{
761        return 0;
762}
763
764static snd_pcm_sframes_t snd_pcm_rate_forward(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
765                                              snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)
766{
767        return 0;
768}
769
770static int snd_pcm_rate_commit_area(snd_pcm_t *pcm, snd_pcm_rate_t *rate,
771				    snd_pcm_uframes_t appl_offset,
772				    snd_pcm_uframes_t size ATTRIBUTE_UNUSED,
773				    snd_pcm_uframes_t slave_size)
774{
775	snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
776	const snd_pcm_channel_area_t *areas;
777	const snd_pcm_channel_area_t *slave_areas;
778	snd_pcm_uframes_t slave_offset, xfer;
779	snd_pcm_uframes_t slave_frames = ULONG_MAX;
780	snd_pcm_sframes_t result;
781
782	areas = snd_pcm_mmap_areas(pcm);
783	/*
784	 * Because snd_pcm_rate_write_areas1() below will convert a full source period
785	 * then there had better be a full period available in the current buffer.
786	 */
787	if (cont >= pcm->period_size) {
788		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
789		if (result < 0)
790			return result;
791		/*
792		 * Because snd_pcm_rate_write_areas1() below will convert to a full slave period
793		 * then there had better be a full slave period available in the slave buffer.
794		 */
795		if (slave_frames < rate->gen.slave->period_size) {
796			snd_pcm_rate_write_areas1(pcm, areas, appl_offset, rate->sareas, 0);
797			goto __partial;
798		}
799		snd_pcm_rate_write_areas1(pcm, areas, appl_offset,
800					  slave_areas, slave_offset);
801		/* Only commit the requested slave_size, even if more was actually converted */
802		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, slave_size);
803		if (result < (snd_pcm_sframes_t)slave_size) {
804			if (result < 0)
805				return result;
806			result = snd_pcm_rewind(rate->gen.slave, result);
807			if (result < 0)
808				return result;
809			return 0;
810		}
811	} else {
812		snd_pcm_areas_copy(rate->pareas, 0,
813				   areas, appl_offset,
814				   pcm->channels, cont,
815				   pcm->format);
816		snd_pcm_areas_copy(rate->pareas, cont,
817				   areas, 0,
818				   pcm->channels, pcm->period_size - cont,
819				   pcm->format);
820
821		snd_pcm_rate_write_areas1(pcm, rate->pareas, 0, rate->sareas, 0);
822
823		/* ok, commit first fragment */
824		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
825		if (result < 0)
826			return result;
827	      __partial:
828		cont = slave_frames;
829		if (cont > slave_size)
830			cont = slave_size;
831		snd_pcm_areas_copy(slave_areas, slave_offset,
832				   rate->sareas, 0,
833				   pcm->channels, cont,
834				   rate->gen.slave->format);
835		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
836		if (result < (snd_pcm_sframes_t)cont) {
837			if (result < 0)
838				return result;
839			result = snd_pcm_rewind(rate->gen.slave, result);
840			if (result < 0)
841				return result;
842			return 0;
843		}
844		xfer = cont;
845
846		if (xfer == slave_size)
847			goto commit_done;
848
849		/* commit second fragment */
850		cont = slave_size - cont;
851		slave_frames = cont;
852		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
853		if (result < 0)
854			return result;
855#if 0
856		if (slave_offset) {
857			SNDERR("non-zero slave_offset %ld", slave_offset);
858			return -EIO;
859		}
860#endif
861		snd_pcm_areas_copy(slave_areas, slave_offset,
862				   rate->sareas, xfer,
863				   pcm->channels, cont,
864				   rate->gen.slave->format);
865		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
866		if (result < (snd_pcm_sframes_t)cont) {
867			if (result < 0)
868				return result;
869			result = snd_pcm_rewind(rate->gen.slave, result + xfer);
870			if (result < 0)
871				return result;
872			return 0;
873		}
874	}
875
876 commit_done:
877	if (rate->start_pending) {
878		/* we have pending start-trigger.  let's issue it now */
879		snd_pcm_start(rate->gen.slave);
880		rate->start_pending = 0;
881	}
882	return 1;
883}
884
885static int snd_pcm_rate_commit_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t appl_offset)
886{
887	snd_pcm_rate_t *rate = pcm->private_data;
888
889	return snd_pcm_rate_commit_area(pcm, rate, appl_offset, pcm->period_size,
890					rate->gen.slave->period_size);
891}
892
893static int snd_pcm_rate_grab_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t hw_offset)
894{
895	snd_pcm_rate_t *rate = pcm->private_data;
896	snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
897	const snd_pcm_channel_area_t *areas;
898	const snd_pcm_channel_area_t *slave_areas;
899	snd_pcm_uframes_t slave_offset, xfer;
900	snd_pcm_uframes_t slave_frames = ULONG_MAX;
901	snd_pcm_sframes_t result;
902
903	areas = snd_pcm_mmap_areas(pcm);
904	if (cont >= pcm->period_size) {
905		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
906		if (result < 0)
907			return result;
908		if (slave_frames < rate->gen.slave->period_size)
909			goto __partial;
910		snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
911					 slave_areas, slave_offset);
912		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, rate->gen.slave->period_size);
913		if (result < (snd_pcm_sframes_t)rate->gen.slave->period_size) {
914			if (result < 0)
915				return result;
916			result = snd_pcm_rewind(rate->gen.slave, result);
917			if (result < 0)
918				return result;
919			return 0;
920		}
921	} else {
922		/* ok, grab first fragment */
923		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
924		if (result < 0)
925			return result;
926	      __partial:
927		cont = slave_frames;
928		if (cont > rate->gen.slave->period_size)
929			cont = rate->gen.slave->period_size;
930		snd_pcm_areas_copy(rate->sareas, 0,
931				   slave_areas, slave_offset,
932				   pcm->channels, cont,
933				   rate->gen.slave->format);
934		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
935		if (result < (snd_pcm_sframes_t)cont) {
936			if (result < 0)
937				return result;
938			result = snd_pcm_rewind(rate->gen.slave, result);
939			if (result < 0)
940				return result;
941			return 0;
942		}
943		xfer = cont;
944
945		if (xfer == rate->gen.slave->period_size)
946			goto __transfer;
947
948		/* grab second fragment */
949		cont = rate->gen.slave->period_size - cont;
950		slave_frames = cont;
951		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
952		if (result < 0)
953			return result;
954#if 0
955		if (slave_offset) {
956			SNDERR("non-zero slave_offset %ld", slave_offset);
957			return -EIO;
958		}
959#endif
960		snd_pcm_areas_copy(rate->sareas, xfer,
961		                   slave_areas, slave_offset,
962				   pcm->channels, cont,
963				   rate->gen.slave->format);
964		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
965		if (result < (snd_pcm_sframes_t)cont) {
966			if (result < 0)
967				return result;
968			result = snd_pcm_rewind(rate->gen.slave, result + xfer);
969			if (result < 0)
970				return result;
971			return 0;
972		}
973
974	      __transfer:
975		cont = pcm->buffer_size - hw_offset;
976		if (cont >= pcm->period_size) {
977			snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
978						 rate->sareas, 0);
979		} else {
980			snd_pcm_rate_read_areas1(pcm,
981						 rate->pareas, 0,
982						 rate->sareas, 0);
983			snd_pcm_areas_copy(areas, hw_offset,
984					   rate->pareas, 0,
985					   pcm->channels, cont,
986					   pcm->format);
987			snd_pcm_areas_copy(areas, 0,
988					   rate->pareas, cont,
989					   pcm->channels, pcm->period_size - cont,
990					   pcm->format);
991		}
992	}
993	return 1;
994}
995
996static int snd_pcm_rate_sync_playback_area(snd_pcm_t *pcm, snd_pcm_uframes_t appl_ptr)
997{
998	snd_pcm_rate_t *rate = pcm->private_data;
999	snd_pcm_t *slave = rate->gen.slave;
1000	snd_pcm_uframes_t xfer;
1001	snd_pcm_sframes_t slave_size;
1002	int err;
1003
1004	slave_size = snd_pcm_avail_update(slave);
1005	if (slave_size < 0)
1006		return slave_size;
1007
1008	xfer = pcm_frame_diff(appl_ptr, rate->last_commit_ptr, pcm->boundary);
1009	while (xfer >= pcm->period_size &&
1010	       (snd_pcm_uframes_t)slave_size >= rate->gen.slave->period_size) {
1011		err = snd_pcm_rate_commit_next_period(pcm, rate->last_commit_ptr % pcm->buffer_size);
1012		if (err == 0)
1013			break;
1014		if (err < 0)
1015			return err;
1016		xfer -= pcm->period_size;
1017		slave_size -= rate->gen.slave->period_size;
1018		rate->last_commit_ptr += pcm->period_size;
1019		if (rate->last_commit_ptr >= pcm->boundary)
1020			rate->last_commit_ptr -= pcm->boundary;
1021	}
1022	return 0;
1023}
1024
1025static snd_pcm_sframes_t snd_pcm_rate_mmap_commit(snd_pcm_t *pcm,
1026						  snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
1027						  snd_pcm_uframes_t size)
1028{
1029	snd_pcm_rate_t *rate = pcm->private_data;
1030	int err;
1031
1032	if (size == 0)
1033		return 0;
1034	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1035		err = snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr + size);
1036		if (err < 0)
1037			return err;
1038	}
1039	snd_pcm_mmap_appl_forward(pcm, size);
1040	return size;
1041}
1042
1043static snd_pcm_sframes_t snd_pcm_rate_avail_update_capture(snd_pcm_t *pcm,
1044							   snd_pcm_sframes_t slave_size)
1045{
1046	snd_pcm_rate_t *rate = pcm->private_data;
1047	snd_pcm_t *slave = rate->gen.slave;
1048	snd_pcm_uframes_t xfer, hw_offset, size;
1049
1050	xfer = snd_pcm_mmap_capture_avail(pcm);
1051	size = pcm->buffer_size - xfer;
1052	hw_offset = snd_pcm_mmap_hw_offset(pcm);
1053	while (size >= pcm->period_size &&
1054	       (snd_pcm_uframes_t)slave_size >= slave->period_size) {
1055		int err = snd_pcm_rate_grab_next_period(pcm, hw_offset);
1056		if (err < 0)
1057			return err;
1058		if (err == 0)
1059			return (snd_pcm_sframes_t)xfer;
1060		xfer += pcm->period_size;
1061		size -= pcm->period_size;
1062		slave_size -= slave->period_size;
1063		hw_offset += pcm->period_size;
1064		hw_offset %= pcm->buffer_size;
1065		snd_pcm_mmap_hw_forward(pcm, pcm->period_size);
1066	}
1067	return (snd_pcm_sframes_t)xfer;
1068}
1069
1070static snd_pcm_sframes_t snd_pcm_rate_avail_update(snd_pcm_t *pcm)
1071{
1072	snd_pcm_rate_t *rate = pcm->private_data;
1073	snd_pcm_sframes_t slave_size;
1074
1075	slave_size = snd_pcm_avail_update(rate->gen.slave);
1076	if (slave_size < 0)
1077		return slave_size;
1078
1079	if (pcm->stream == SND_PCM_STREAM_CAPTURE)
1080		return snd_pcm_rate_avail_update_capture(pcm, slave_size);
1081
1082	snd_pcm_rate_sync_hwptr(pcm);
1083	snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr);
1084	return snd_pcm_mmap_avail(pcm);
1085}
1086
1087static int snd_pcm_rate_htimestamp(snd_pcm_t *pcm,
1088				   snd_pcm_uframes_t *avail,
1089				   snd_htimestamp_t *tstamp)
1090{
1091	snd_pcm_rate_t *rate = pcm->private_data;
1092	snd_pcm_sframes_t avail1;
1093	snd_pcm_uframes_t tmp;
1094	int ok = 0, err;
1095
1096	while (1) {
1097		/* the position is from this plugin itself */
1098		avail1 = snd_pcm_avail_update(pcm);
1099		if (avail1 < 0)
1100			return avail1;
1101		if (ok && (snd_pcm_uframes_t)avail1 == *avail)
1102			break;
1103		*avail = avail1;
1104		/* timestamp is taken from the slave PCM */
1105		err = snd_pcm_htimestamp(rate->gen.slave, &tmp, tstamp);
1106		if (err < 0)
1107			return err;
1108		ok = 1;
1109	}
1110	return 0;
1111}
1112
1113static int snd_pcm_rate_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
1114{
1115	snd_pcm_rate_t *rate = pcm->private_data;
1116	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1117		/* Try to sync as much as possible */
1118		snd_pcm_rate_hwsync(pcm);
1119		snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr);
1120	}
1121	return snd_pcm_poll_descriptors_revents(rate->gen.slave, pfds, nfds, revents);
1122}
1123
1124/* locking */
1125static int snd_pcm_rate_drain(snd_pcm_t *pcm)
1126{
1127	snd_pcm_rate_t *rate = pcm->private_data;
1128
1129	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1130		/* commit the remaining fraction (if any) */
1131		snd_pcm_uframes_t size, ofs, saved_avail_min;
1132		snd_pcm_sw_params_t sw_params;
1133		int commit_err = 0;
1134
1135		__snd_pcm_lock(pcm);
1136		/* temporarily set avail_min to one */
1137		sw_params = rate->sw_params;
1138		saved_avail_min = sw_params.avail_min;
1139		sw_params.avail_min = 1;
1140		snd_pcm_sw_params(rate->gen.slave, &sw_params);
1141
1142		size = pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary);
1143		ofs = rate->last_commit_ptr % pcm->buffer_size;
1144		while (size > 0) {
1145			snd_pcm_uframes_t psize, spsize;
1146			int err;
1147
1148			err = __snd_pcm_wait_in_lock(rate->gen.slave, SND_PCM_WAIT_DRAIN);
1149			if (err < 0)
1150				break;
1151			if (size > pcm->period_size) {
1152				psize = pcm->period_size;
1153				spsize = rate->gen.slave->period_size;
1154			} else {
1155				psize = size;
1156				spsize = rate->ops.output_frames(rate->obj, size);
1157				if (! spsize)
1158					break;
1159			}
1160			commit_err = snd_pcm_rate_commit_area(pcm, rate, ofs,
1161						 psize, spsize);
1162			if (commit_err == 1) {
1163				rate->last_commit_ptr += psize;
1164				if (rate->last_commit_ptr >= pcm->boundary)
1165					rate->last_commit_ptr -= pcm->boundary;
1166			} else if (commit_err == 0) {
1167				if (pcm->mode & SND_PCM_NONBLOCK) {
1168					commit_err = -EAGAIN;
1169					break;
1170				}
1171				continue;
1172			} else
1173				break;
1174
1175			ofs = (ofs + psize) % pcm->buffer_size;
1176			size -= psize;
1177		}
1178		sw_params.avail_min = saved_avail_min;
1179		snd_pcm_sw_params(rate->gen.slave, &sw_params);
1180		__snd_pcm_unlock(pcm);
1181		if (commit_err < 0)
1182			return commit_err;
1183	}
1184	return snd_pcm_drain(rate->gen.slave);
1185}
1186
1187static snd_pcm_state_t snd_pcm_rate_state(snd_pcm_t *pcm)
1188{
1189	snd_pcm_rate_t *rate = pcm->private_data;
1190	if (rate->start_pending) /* pseudo-state */
1191		return SND_PCM_STATE_RUNNING;
1192	return snd_pcm_state(rate->gen.slave);
1193}
1194
1195
1196static int snd_pcm_rate_start(snd_pcm_t *pcm)
1197{
1198	snd_pcm_rate_t *rate = pcm->private_data;
1199	snd_pcm_sframes_t avail;
1200
1201	if (pcm->stream == SND_PCM_STREAM_CAPTURE)
1202		return snd_pcm_start(rate->gen.slave);
1203
1204	if (snd_pcm_state(rate->gen.slave) != SND_PCM_STATE_PREPARED)
1205		return -EBADFD;
1206
1207	gettimestamp(&rate->trigger_tstamp, pcm->tstamp_type);
1208
1209	avail = snd_pcm_mmap_playback_hw_avail(rate->gen.slave);
1210	if (avail < 0) /* can't happen on healthy drivers */
1211		return -EBADFD;
1212
1213	if (avail == 0) {
1214		/* postpone the trigger since we have no data committed yet */
1215		rate->start_pending = 1;
1216		return 0;
1217	}
1218	rate->start_pending = 0;
1219	return snd_pcm_start(rate->gen.slave);
1220}
1221
1222static int snd_pcm_rate_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
1223{
1224	snd_pcm_rate_t *rate = pcm->private_data;
1225	snd_pcm_sframes_t err;
1226
1227	err = snd_pcm_status(rate->gen.slave, status);
1228	if (err < 0)
1229		return err;
1230	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1231		if (rate->start_pending)
1232			status->state = SND_PCM_STATE_RUNNING;
1233		status->trigger_tstamp = rate->trigger_tstamp;
1234	}
1235	snd_pcm_rate_sync_hwptr0(pcm, status->hw_ptr);
1236	status->appl_ptr = *pcm->appl.ptr;
1237	status->hw_ptr = *pcm->hw.ptr;
1238	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1239		status->delay = rate->ops.input_frames(rate->obj, status->delay)
1240					+ snd_pcm_rate_playback_internal_delay(pcm);
1241		status->avail = snd_pcm_mmap_playback_avail(pcm);
1242		status->avail_max = rate->ops.input_frames(rate->obj, status->avail_max);
1243	} else {
1244		status->delay = rate->ops.output_frames(rate->obj, status->delay)
1245					+ snd_pcm_mmap_capture_delay(pcm);
1246		status->avail = snd_pcm_mmap_capture_avail(pcm);
1247		status->avail_max = rate->ops.output_frames(rate->obj, status->avail_max);
1248	}
1249	return 0;
1250}
1251
1252static void snd_pcm_rate_dump(snd_pcm_t *pcm, snd_output_t *out)
1253{
1254	snd_pcm_rate_t *rate = pcm->private_data;
1255	if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
1256		snd_output_printf(out, "Rate conversion PCM (%d)\n",
1257			rate->srate);
1258	else
1259		snd_output_printf(out, "Rate conversion PCM (%d, sformat=%s)\n",
1260			rate->srate,
1261			snd_pcm_format_name(rate->sformat));
1262	if (rate->ops.dump)
1263		rate->ops.dump(rate->obj, out);
1264	snd_output_printf(out, "Protocol version: %x\n", rate->plugin_version);
1265	if (pcm->setup) {
1266		snd_output_printf(out, "Its setup is:\n");
1267		snd_pcm_dump_setup(pcm, out);
1268	}
1269	snd_output_printf(out, "Slave: ");
1270	snd_pcm_dump(rate->gen.slave, out);
1271}
1272
1273static int snd_pcm_rate_close(snd_pcm_t *pcm)
1274{
1275	snd_pcm_rate_t *rate = pcm->private_data;
1276
1277	if (rate->ops.close)
1278		rate->ops.close(rate->obj);
1279	if (rate->open_func)
1280		snd_dlobj_cache_put(rate->open_func);
1281	return snd_pcm_generic_close(pcm);
1282}
1283
1284/**
1285 * \brief Convert rate pcm frames to corresponding rate slave pcm frames
1286 * \param pcm PCM handle
1287 * \param frames Frames to be converted to slave frames
1288 * \retval Corresponding slave frames
1289*/
1290static snd_pcm_uframes_t snd_pcm_rate_slave_frames(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
1291{
1292	snd_pcm_uframes_t sframes;
1293	snd_pcm_rate_t *rate = pcm->private_data;
1294
1295	if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
1296		sframes = rate->ops.output_frames(rate->obj, frames);
1297	else
1298		sframes = rate->ops.input_frames(rate->obj, frames);
1299
1300	return sframes;
1301}
1302
1303static int snd_pcm_rate_may_wait_for_avail_min(snd_pcm_t *pcm,
1304					       snd_pcm_uframes_t avail)
1305{
1306	return snd_pcm_plugin_may_wait_for_avail_min_conv(pcm, avail,
1307							  snd_pcm_rate_slave_frames);
1308}
1309
1310static const snd_pcm_fast_ops_t snd_pcm_rate_fast_ops = {
1311	.status = snd_pcm_rate_status,
1312	.state = snd_pcm_rate_state,
1313	.hwsync = snd_pcm_rate_hwsync,
1314	.delay = snd_pcm_rate_delay,
1315	.prepare = snd_pcm_rate_prepare,
1316	.reset = snd_pcm_rate_reset,
1317	.start = snd_pcm_rate_start,
1318	.drop = snd_pcm_generic_drop,
1319	.drain = snd_pcm_rate_drain,
1320	.pause = snd_pcm_generic_pause,
1321	.rewindable = snd_pcm_rate_rewindable,
1322	.rewind = snd_pcm_rate_rewind,
1323	.forwardable = snd_pcm_rate_forwardable,
1324	.forward = snd_pcm_rate_forward,
1325	.resume = snd_pcm_generic_resume,
1326	.writei = snd_pcm_mmap_writei,
1327	.writen = snd_pcm_mmap_writen,
1328	.readi = snd_pcm_mmap_readi,
1329	.readn = snd_pcm_mmap_readn,
1330	.avail_update = snd_pcm_rate_avail_update,
1331	.mmap_commit = snd_pcm_rate_mmap_commit,
1332	.htimestamp = snd_pcm_rate_htimestamp,
1333	.poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
1334	.poll_descriptors = snd_pcm_generic_poll_descriptors,
1335	.poll_revents = snd_pcm_rate_poll_revents,
1336	.may_wait_for_avail_min = snd_pcm_rate_may_wait_for_avail_min,
1337};
1338
1339static const snd_pcm_ops_t snd_pcm_rate_ops = {
1340	.close = snd_pcm_rate_close,
1341	.info = snd_pcm_generic_info,
1342	.hw_refine = snd_pcm_rate_hw_refine,
1343	.hw_params = snd_pcm_rate_hw_params,
1344	.hw_free = snd_pcm_rate_hw_free,
1345	.sw_params = snd_pcm_rate_sw_params,
1346	.channel_info = snd_pcm_generic_channel_info,
1347	.dump = snd_pcm_rate_dump,
1348	.nonblock = snd_pcm_generic_nonblock,
1349	.async = snd_pcm_generic_async,
1350	.mmap = snd_pcm_generic_mmap,
1351	.munmap = snd_pcm_generic_munmap,
1352	.query_chmaps = snd_pcm_generic_query_chmaps,
1353	.get_chmap = snd_pcm_generic_get_chmap,
1354	.set_chmap = snd_pcm_generic_set_chmap,
1355};
1356
1357/**
1358 * \brief Get a default converter string
1359 * \param root Root configuration node
1360 * \retval A const config item if found, or NULL
1361 */
1362const snd_config_t *snd_pcm_rate_get_default_converter(snd_config_t *root)
1363{
1364	snd_config_t *n;
1365	/* look for default definition */
1366	if (snd_config_search(root, "defaults.pcm.rate_converter", &n) >= 0)
1367		return n;
1368	return NULL;
1369}
1370
1371static void rate_initial_setup(snd_pcm_rate_t *rate)
1372{
1373	if (rate->plugin_version == SND_PCM_RATE_PLUGIN_VERSION)
1374		rate->plugin_version = rate->ops.version;
1375
1376	if (rate->plugin_version >= 0x010002 &&
1377	    rate->ops.get_supported_rates)
1378		rate->ops.get_supported_rates(rate->obj,
1379					      &rate->rate_min,
1380					      &rate->rate_max);
1381
1382	if (rate->plugin_version >= 0x010003 &&
1383	    rate->ops.get_supported_formats) {
1384		rate->ops.get_supported_formats(rate->obj,
1385						&rate->in_formats,
1386						&rate->out_formats,
1387						&rate->format_flags);
1388	} else if (!rate->ops.convert && rate->ops.convert_s16) {
1389		rate->in_formats = rate->out_formats =
1390			1ULL << SND_PCM_FORMAT_S16;
1391		rate->format_flags = SND_PCM_RATE_FLAG_INTERLEAVED;
1392	}
1393}
1394
1395#ifdef PIC
1396static int is_builtin_plugin(const char *type)
1397{
1398	return strcmp(type, "linear") == 0;
1399}
1400
1401static const char *const default_rate_plugins[] = {
1402	"speexrate", "linear", NULL
1403};
1404
1405static int rate_open_func(snd_pcm_rate_t *rate, const char *type, const snd_config_t *converter_conf, int verbose)
1406{
1407	char open_name[64], open_conf_name[64], lib_name[64], *lib = NULL;
1408	snd_pcm_rate_open_func_t open_func;
1409	snd_pcm_rate_open_conf_func_t open_conf_func;
1410	int err;
1411
1412	snprintf(open_name, sizeof(open_name), "_snd_pcm_rate_%s_open", type);
1413	snprintf(open_conf_name, sizeof(open_conf_name), "_snd_pcm_rate_%s_open_conf", type);
1414	if (!is_builtin_plugin(type)) {
1415		snprintf(lib_name, sizeof(lib_name),
1416				 "libasound_module_rate_%s.so", type);
1417		lib = lib_name;
1418	}
1419
1420	open_conf_func = snd_dlobj_cache_get(lib, open_conf_name, NULL, verbose && converter_conf != NULL);
1421	if (open_conf_func) {
1422		err = open_conf_func(SND_PCM_RATE_PLUGIN_VERSION,
1423				     &rate->obj, &rate->ops, converter_conf);
1424		if (!err) {
1425			rate->open_func = open_conf_func;
1426			return 0;
1427		} else {
1428			snd_dlobj_cache_put(open_conf_func);
1429			return err;
1430		}
1431	}
1432
1433	open_func = snd_dlobj_cache_get(lib, open_name, NULL, verbose);
1434	if (!open_func)
1435		return -ENOENT;
1436
1437	rate->open_func = open_func;
1438
1439	err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
1440	if (!err)
1441		return 0;
1442
1443	/* try to open with the old protocol version */
1444	rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION_OLD;
1445	err = open_func(SND_PCM_RATE_PLUGIN_VERSION_OLD,
1446			&rate->obj, &rate->ops);
1447	if (!err)
1448		return 0;
1449
1450	snd_dlobj_cache_put(open_func);
1451	rate->open_func = NULL;
1452	return err;
1453}
1454#endif
1455
1456/*
1457 * If the conf is an array of alternatives then the id of
1458 * the first element will be "0" (or maybe NULL). Otherwise assume it is
1459 * a structure.
1460 */
1461static int is_string_array(const snd_config_t *conf)
1462{
1463	snd_config_iterator_t i;
1464
1465	if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND)
1466		return 0;
1467
1468	i = snd_config_iterator_first(conf);
1469	if (i && i != snd_config_iterator_end(conf)) {
1470		snd_config_t *n = snd_config_iterator_entry(i);
1471		const char *id;
1472		if (snd_config_get_id(n, &id) < 0)
1473			return 0;
1474		if (id && strcmp(id, "0") != 0)
1475			return 0;
1476	}
1477
1478	return 1;
1479}
1480
1481/**
1482 * \brief Creates a new rate PCM
1483 * \param pcmp Returns created PCM handle
1484 * \param name Name of PCM
1485 * \param sformat Slave format
1486 * \param srate Slave rate
1487 * \param converter SRC type string node
1488 * \param slave Slave PCM handle
1489 * \param close_slave When set, the slave PCM handle is closed with copy PCM
1490 * \retval zero on success otherwise a negative error code
1491 * \warning Using of this function might be dangerous in the sense
1492 *          of compatibility reasons. The prototype might be freely
1493 *          changed in future.
1494 */
1495int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
1496		      snd_pcm_format_t sformat, unsigned int srate,
1497		      const snd_config_t *converter,
1498		      snd_pcm_t *slave, int close_slave)
1499{
1500	snd_pcm_t *pcm;
1501	snd_pcm_rate_t *rate;
1502	const char *type = NULL;
1503	int err;
1504#ifndef PIC
1505	snd_pcm_rate_open_func_t open_func;
1506	extern int SND_PCM_RATE_PLUGIN_ENTRY(linear) (unsigned int version, void **objp, snd_pcm_rate_ops_t *ops);
1507#endif
1508
1509	assert(pcmp && slave);
1510	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1511	    snd_pcm_format_linear(sformat) != 1)
1512		return -EINVAL;
1513	rate = calloc(1, sizeof(snd_pcm_rate_t));
1514	if (!rate) {
1515		return -ENOMEM;
1516	}
1517	rate->gen.slave = slave;
1518	rate->gen.close_slave = close_slave;
1519	rate->srate = srate;
1520	rate->sformat = sformat;
1521
1522	rate->rate_min = SND_PCM_PLUGIN_RATE_MIN;
1523	rate->rate_max = SND_PCM_PLUGIN_RATE_MAX;
1524	rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION;
1525
1526	err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode);
1527	if (err < 0) {
1528		free(rate);
1529		return err;
1530	}
1531
1532#ifdef PIC
1533	err = -ENOENT;
1534	if (!converter) {
1535		const char *const *types;
1536		for (types = default_rate_plugins; *types; types++) {
1537			err = rate_open_func(rate, *types, NULL, 0);
1538			if (!err) {
1539				type = *types;
1540				break;
1541			}
1542		}
1543	} else if (!snd_config_get_string(converter, &type))
1544		err = rate_open_func(rate, type, NULL, 1);
1545	else if (is_string_array(converter)) {
1546		snd_config_iterator_t i, next;
1547		snd_config_for_each(i, next, converter) {
1548			snd_config_t *n = snd_config_iterator_entry(i);
1549			if (snd_config_get_string(n, &type) < 0)
1550				break;
1551			err = rate_open_func(rate, type, NULL, 0);
1552			if (!err)
1553				break;
1554		}
1555	} else if (snd_config_get_type(converter) == SND_CONFIG_TYPE_COMPOUND) {
1556		snd_config_iterator_t i, next;
1557		snd_config_for_each(i, next, converter) {
1558			snd_config_t *n = snd_config_iterator_entry(i);
1559			const char *id;
1560			if (snd_config_get_id(n, &id) < 0)
1561				continue;
1562			if (strcmp(id, "name") != 0)
1563				continue;
1564			snd_config_get_string(n, &type);
1565			break;
1566		}
1567		if (!type) {
1568			SNDERR("No name given for rate converter");
1569			snd_pcm_free(pcm);
1570			free(rate);
1571			return -EINVAL;
1572		}
1573		err = rate_open_func(rate, type, converter, 1);
1574	} else {
1575		SNDERR("Invalid type for rate converter");
1576		snd_pcm_free(pcm);
1577		free(rate);
1578		return -EINVAL;
1579	}
1580	if (err < 0) {
1581		SNDERR("Cannot find rate converter");
1582		snd_pcm_free(pcm);
1583		free(rate);
1584		return -ENOENT;
1585	}
1586#else
1587	type = "linear";
1588	open_func = SND_PCM_RATE_PLUGIN_ENTRY(linear);
1589	err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
1590	if (err < 0) {
1591		snd_pcm_free(pcm);
1592		free(rate);
1593		return err;
1594	}
1595#endif
1596
1597	if (! rate->ops.init || ! (rate->ops.convert || rate->ops.convert_s16) ||
1598	    ! rate->ops.input_frames || ! rate->ops.output_frames) {
1599		SNDERR("Inproper rate plugin %s initialization", type);
1600		snd_pcm_free(pcm);
1601		free(rate);
1602		return err;
1603	}
1604
1605	rate_initial_setup(rate);
1606
1607	pcm->ops = &snd_pcm_rate_ops;
1608	pcm->fast_ops = &snd_pcm_rate_fast_ops;
1609	pcm->private_data = rate;
1610	pcm->poll_fd = slave->poll_fd;
1611	pcm->poll_events = slave->poll_events;
1612	pcm->mmap_rw = 1;
1613	pcm->tstamp_type = slave->tstamp_type;
1614	snd_pcm_set_hw_ptr(pcm, &rate->hw_ptr, -1, 0);
1615	snd_pcm_set_appl_ptr(pcm, &rate->appl_ptr, -1, 0);
1616	*pcmp = pcm;
1617
1618	return 0;
1619}
1620
1621/*! \page pcm_plugins
1622
1623\section pcm_plugins_rate Plugin: Rate
1624
1625This plugin converts a stream rate. The input and output formats must be linear.
1626
1627\code
1628pcm.name {
1629	type rate               # Rate PCM
1630        slave STR               # Slave name
1631        # or
1632        slave {                 # Slave definition
1633                pcm STR         # Slave PCM name
1634                # or
1635                pcm { }         # Slave PCM definition
1636                rate INT        # Slave rate
1637                [format STR]    # Slave format
1638        }
1639	converter STR			# optional
1640	# or
1641	converter [ STR1 STR2 ... ]	# optional
1642				# Converter type, default is taken from
1643				# defaults.pcm.rate_converter
1644	# or
1645	converter {		# optional
1646		name STR	# Convertor type
1647		xxx yyy		# optional convertor-specific configuration
1648	}
1649}
1650\endcode
1651
1652\subsection pcm_plugins_rate_funcref Function reference
1653
1654<UL>
1655  <LI>snd_pcm_rate_open()
1656  <LI>_snd_pcm_rate_open()
1657</UL>
1658
1659*/
1660
1661/**
1662 * \brief Creates a new rate PCM
1663 * \param pcmp Returns created PCM handle
1664 * \param name Name of PCM
1665 * \param root Root configuration node
1666 * \param conf Configuration node with rate PCM description
1667 * \param stream Stream type
1668 * \param mode Stream mode
1669 * \retval zero on success otherwise a negative error code
1670 * \warning Using of this function might be dangerous in the sense
1671 *          of compatibility reasons. The prototype might be freely
1672 *          changed in future.
1673 */
1674int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
1675		       snd_config_t *root, snd_config_t *conf,
1676		       snd_pcm_stream_t stream, int mode)
1677{
1678	snd_config_iterator_t i, next;
1679	int err;
1680	snd_pcm_t *spcm;
1681	snd_config_t *slave = NULL, *sconf;
1682	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1683	int srate = -1;
1684	const snd_config_t *converter = NULL;
1685
1686	snd_config_for_each(i, next, conf) {
1687		snd_config_t *n = snd_config_iterator_entry(i);
1688		const char *id;
1689		if (snd_config_get_id(n, &id) < 0)
1690			continue;
1691		if (snd_pcm_conf_generic_id(id))
1692			continue;
1693		if (strcmp(id, "slave") == 0) {
1694			slave = n;
1695			continue;
1696		}
1697		if (strcmp(id, "converter") == 0) {
1698			converter = n;
1699			continue;
1700		}
1701		SNDERR("Unknown field %s", id);
1702		return -EINVAL;
1703	}
1704	if (!slave) {
1705		SNDERR("slave is not defined");
1706		return -EINVAL;
1707	}
1708
1709	err = snd_pcm_slave_conf(root, slave, &sconf, 2,
1710				 SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
1711				 SND_PCM_HW_PARAM_RATE, SCONF_MANDATORY, &srate);
1712	if (err < 0)
1713		return err;
1714	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1715	    snd_pcm_format_linear(sformat) != 1) {
1716	    	snd_config_delete(sconf);
1717		SNDERR("slave format is not linear");
1718		return -EINVAL;
1719	}
1720	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1721	snd_config_delete(sconf);
1722	if (err < 0)
1723		return err;
1724	err = snd_pcm_rate_open(pcmp, name, sformat, (unsigned int) srate,
1725				converter, spcm, 1);
1726	if (err < 0)
1727		snd_pcm_close(spcm);
1728	return err;
1729}
1730#ifndef DOC_HIDDEN
1731SND_DLSYM_BUILD_VERSION(_snd_pcm_rate_open, SND_PCM_DLSYM_VERSION);
1732#endif
1733