xref: /third_party/alsa-lib/src/pcm/pcm_plugin.c (revision d5ac70f0)
1/**
2 * \file pcm/pcm_plugin.c
3 * \ingroup PCM
4 * \brief PCM Interface
5 * \author Jaroslav Kysela <perex@perex.cz>
6 * \author Abramo Bagnara <abramo@alsa-project.org>
7 * \date 2000-2001
8 */
9/*
10 *  PCM - Common plugin code
11 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
12 *
13 *
14 *   This library is free software; you can redistribute it and/or modify
15 *   it under the terms of the GNU Lesser General Public License as
16 *   published by the Free Software Foundation; either version 2.1 of
17 *   the License, or (at your option) any later version.
18 *
19 *   This program is distributed in the hope that it will be useful,
20 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
21 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 *   GNU Lesser General Public License for more details.
23 *
24 *   You should have received a copy of the GNU Lesser General Public
25 *   License along with this library; if not, write to the Free Software
26 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
27 *
28 */
29
30/*!
31
32\page pcm_plugins PCM (digital audio) plugins
33
34PCM plugins extends functionality and features of PCM devices.
35The plugins take care about various sample conversions, sample
36copying among channels and so on.
37
38\section pcm_plugins_slave Slave definition
39
40The slave plugin can be specified directly with a string or the definition
41can be entered inside a compound configuration node. Some restrictions can
42be also specified (like static rate or count of channels).
43
44\code
45pcm_slave.NAME {
46	pcm STR		# PCM name
47	# or
48	pcm { }		# PCM definition
49	format STR	# Format or "unchanged"
50	channels INT	# Count of channels or "unchanged" string
51	rate INT	# Rate in Hz or "unchanged" string
52	period_time INT	# Period time in us or "unchanged" string
53	buffer_time INT # Buffer time in us or "unchanged" string
54}
55\endcode
56
57Example:
58
59\code
60pcm_slave.slave_rate44100Hz {
61	pcm "hw:0,0"
62	rate 44100
63}
64
65pcm.rate44100Hz {
66	type plug
67	slave slave_rate44100Hz
68}
69\endcode
70
71The equivalent configuration (in one compound):
72
73\code
74pcm.rate44100Hz {
75	type plug
76	slave {
77		pcm "hw:0,0"
78		rate 44100
79	}
80}
81\endcode
82
83*/
84
85#include "pcm_local.h"
86#include "pcm_plugin.h"
87#include <limits.h>
88
89#ifndef DOC_HIDDEN
90
91static snd_pcm_sframes_t
92snd_pcm_plugin_undo_read(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
93			 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
94			 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
95			 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
96			 snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
97{
98	return -EIO;
99}
100
101static snd_pcm_sframes_t
102snd_pcm_plugin_undo_write(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
103			  const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
104			  snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
105			  snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
106			  snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
107{
108	return -EIO;
109}
110
111snd_pcm_sframes_t
112snd_pcm_plugin_undo_read_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
113				 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
114				 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
115				 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
116				 snd_pcm_uframes_t slave_undo_size)
117{
118	return slave_undo_size;
119}
120
121snd_pcm_sframes_t
122snd_pcm_plugin_undo_write_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
123				  const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
124				  snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
125				  snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
126				  snd_pcm_uframes_t slave_undo_size)
127{
128	return slave_undo_size;
129}
130
131void snd_pcm_plugin_init(snd_pcm_plugin_t *plugin)
132{
133	memset(plugin, 0, sizeof(snd_pcm_plugin_t));
134	plugin->undo_read = snd_pcm_plugin_undo_read;
135	plugin->undo_write = snd_pcm_plugin_undo_write;
136}
137
138static int snd_pcm_plugin_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
139{
140	snd_pcm_plugin_t *plugin = pcm->private_data;
141	snd_pcm_sframes_t sd;
142	int err = snd_pcm_delay(plugin->gen.slave, &sd);
143	if (err < 0)
144		return err;
145	*delayp = sd;
146	return 0;
147}
148
149static int snd_pcm_plugin_call_init_cb(snd_pcm_t *pcm, snd_pcm_plugin_t *plugin)
150{
151	snd_pcm_t *slave = plugin->gen.slave;
152	int err;
153
154	assert(pcm->boundary == slave->boundary);
155	*pcm->hw.ptr = *slave->hw.ptr;
156	*pcm->appl.ptr = *slave->appl.ptr;
157	if (plugin->init) {
158		err = plugin->init(pcm);
159		if (err < 0)
160			return err;
161	}
162	return 0;
163}
164
165static int snd_pcm_plugin_prepare(snd_pcm_t *pcm)
166{
167	snd_pcm_plugin_t *plugin = pcm->private_data;
168	int err;
169	err = snd_pcm_prepare(plugin->gen.slave);
170	if (err < 0)
171		return err;
172	return snd_pcm_plugin_call_init_cb(pcm, plugin);
173}
174
175static int snd_pcm_plugin_reset(snd_pcm_t *pcm)
176{
177	snd_pcm_plugin_t *plugin = pcm->private_data;
178	int err;
179	err = snd_pcm_reset(plugin->gen.slave);
180	if (err < 0)
181		return err;
182	return snd_pcm_plugin_call_init_cb(pcm, plugin);
183}
184
185static snd_pcm_sframes_t snd_pcm_plugin_rewindable(snd_pcm_t *pcm)
186{
187	return snd_pcm_mmap_hw_rewindable(pcm);
188}
189
190snd_pcm_sframes_t snd_pcm_plugin_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
191{
192	snd_pcm_plugin_t *plugin = pcm->private_data;
193	snd_pcm_sframes_t n = snd_pcm_plugin_rewindable(pcm);
194	snd_pcm_sframes_t sframes;
195
196	if ((snd_pcm_uframes_t)n < frames)
197		frames = n;
198	if (frames == 0)
199		return 0;
200
201        sframes = frames;
202	sframes = snd_pcm_rewind(plugin->gen.slave, sframes);
203	if (sframes < 0)
204		return sframes;
205	snd_pcm_mmap_appl_backward(pcm, (snd_pcm_uframes_t) sframes);
206	return (snd_pcm_sframes_t) sframes;
207}
208
209static snd_pcm_sframes_t snd_pcm_plugin_forwardable(snd_pcm_t *pcm)
210{
211	return snd_pcm_mmap_avail(pcm);
212}
213
214snd_pcm_sframes_t snd_pcm_plugin_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
215{
216	snd_pcm_plugin_t *plugin = pcm->private_data;
217	snd_pcm_sframes_t n = snd_pcm_plugin_forwardable(pcm);
218	snd_pcm_sframes_t sframes;
219
220	if ((snd_pcm_uframes_t)n < frames)
221		frames = n;
222	if (frames == 0)
223		return 0;
224
225        sframes = frames;
226	sframes = INTERNAL(snd_pcm_forward)(plugin->gen.slave, sframes);
227	if (sframes < 0)
228		return sframes;
229	snd_pcm_mmap_appl_forward(pcm, (snd_pcm_uframes_t) frames);
230	return (snd_pcm_sframes_t) frames;
231}
232
233static snd_pcm_sframes_t snd_pcm_plugin_write_areas(snd_pcm_t *pcm,
234						    const snd_pcm_channel_area_t *areas,
235						    snd_pcm_uframes_t offset,
236						    snd_pcm_uframes_t size)
237{
238	snd_pcm_plugin_t *plugin = pcm->private_data;
239	snd_pcm_t *slave = plugin->gen.slave;
240	snd_pcm_uframes_t xfer = 0;
241	snd_pcm_sframes_t result;
242	int err;
243
244	while (size > 0) {
245		snd_pcm_uframes_t frames = size;
246		const snd_pcm_channel_area_t *slave_areas;
247		snd_pcm_uframes_t slave_offset;
248		snd_pcm_uframes_t slave_frames = ULONG_MAX;
249
250		result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
251		if (result < 0) {
252			err = result;
253			goto error;
254		}
255		if (slave_frames == 0)
256			break;
257		frames = plugin->write(pcm, areas, offset, frames,
258				       slave_areas, slave_offset, &slave_frames);
259		if (CHECK_SANITY(slave_frames > snd_pcm_mmap_playback_avail(slave))) {
260			SNDMSG("write overflow %ld > %ld", slave_frames,
261			       snd_pcm_mmap_playback_avail(slave));
262			err = -EPIPE;
263			goto error;
264		}
265		result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
266		if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
267			snd_pcm_sframes_t res;
268			res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
269			if (res < 0) {
270				err = res;
271				goto error;
272			}
273			frames -= res;
274		}
275		if (result <= 0) {
276			err = result;
277			goto error;
278		}
279		snd_pcm_mmap_appl_forward(pcm, frames);
280		offset += frames;
281		xfer += frames;
282		size -= frames;
283	}
284	return (snd_pcm_sframes_t)xfer;
285
286 error:
287	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
288}
289
290static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm,
291						   const snd_pcm_channel_area_t *areas,
292						   snd_pcm_uframes_t offset,
293						   snd_pcm_uframes_t size)
294{
295	snd_pcm_plugin_t *plugin = pcm->private_data;
296	snd_pcm_t *slave = plugin->gen.slave;
297	snd_pcm_uframes_t xfer = 0;
298	snd_pcm_sframes_t result;
299	int err;
300
301	while (size > 0) {
302		snd_pcm_uframes_t frames = size;
303		const snd_pcm_channel_area_t *slave_areas;
304		snd_pcm_uframes_t slave_offset;
305		snd_pcm_uframes_t slave_frames = ULONG_MAX;
306
307		result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
308		if (result < 0) {
309			err = result;
310			goto error;
311		}
312		if (slave_frames == 0)
313			break;
314		frames = (plugin->read)(pcm, areas, offset, frames,
315				      slave_areas, slave_offset, &slave_frames);
316		if (CHECK_SANITY(slave_frames > snd_pcm_mmap_capture_avail(slave))) {
317			SNDMSG("read overflow %ld > %ld", slave_frames,
318			       snd_pcm_mmap_playback_avail(slave));
319			err = -EPIPE;
320			goto error;
321		}
322		result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
323		if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
324			snd_pcm_sframes_t res;
325
326			res = plugin->undo_read(slave, areas, offset, frames, slave_frames - result);
327			if (res < 0) {
328				err = res;
329				goto error;
330			}
331			frames -= res;
332		}
333		if (result <= 0) {
334			err = result;
335			goto error;
336		}
337		snd_pcm_mmap_appl_forward(pcm, frames);
338		offset += frames;
339		xfer += frames;
340		size -= frames;
341	}
342	return (snd_pcm_sframes_t)xfer;
343
344 error:
345	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
346}
347
348
349static snd_pcm_sframes_t
350snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
351{
352	snd_pcm_channel_area_t areas[pcm->channels];
353	snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
354	return snd_pcm_write_areas(pcm, areas, 0, size,
355				   snd_pcm_plugin_write_areas);
356}
357
358static snd_pcm_sframes_t
359snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
360{
361	snd_pcm_channel_area_t areas[pcm->channels];
362	snd_pcm_areas_from_bufs(pcm, areas, bufs);
363	return snd_pcm_write_areas(pcm, areas, 0, size,
364				   snd_pcm_plugin_write_areas);
365}
366
367static snd_pcm_sframes_t
368snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
369{
370	snd_pcm_channel_area_t areas[pcm->channels];
371	snd_pcm_areas_from_buf(pcm, areas, buffer);
372	return snd_pcm_read_areas(pcm, areas, 0, size,
373				  snd_pcm_plugin_read_areas);
374}
375
376static snd_pcm_sframes_t
377snd_pcm_plugin_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
378{
379	snd_pcm_channel_area_t areas[pcm->channels];
380	snd_pcm_areas_from_bufs(pcm, areas, bufs);
381	return snd_pcm_read_areas(pcm, areas, 0, size,
382				  snd_pcm_plugin_read_areas);
383}
384
385static snd_pcm_sframes_t
386snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm,
387			   snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
388			   snd_pcm_uframes_t size)
389{
390	snd_pcm_plugin_t *plugin = pcm->private_data;
391	snd_pcm_t *slave = plugin->gen.slave;
392	const snd_pcm_channel_area_t *areas;
393	snd_pcm_uframes_t appl_offset;
394	snd_pcm_sframes_t slave_size;
395	snd_pcm_sframes_t xfer;
396	int err;
397
398	if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
399		snd_pcm_mmap_appl_forward(pcm, size);
400		return size;
401	}
402	slave_size = snd_pcm_avail_update(slave);
403	if (slave_size < 0)
404		return slave_size;
405	areas = snd_pcm_mmap_areas(pcm);
406	appl_offset = snd_pcm_mmap_offset(pcm);
407	xfer = 0;
408	while (size > 0 && slave_size > 0) {
409		snd_pcm_uframes_t frames = size;
410		snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
411		const snd_pcm_channel_area_t *slave_areas;
412		snd_pcm_uframes_t slave_offset;
413		snd_pcm_uframes_t slave_frames = ULONG_MAX;
414		snd_pcm_sframes_t result;
415
416		result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
417		if (result < 0) {
418			err = result;
419			goto error;
420		}
421		if (frames > cont)
422			frames = cont;
423		frames = plugin->write(pcm, areas, appl_offset, frames,
424				       slave_areas, slave_offset, &slave_frames);
425		result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
426		if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
427			snd_pcm_sframes_t res;
428
429			res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
430			if (res < 0) {
431				err = res;
432				goto error;
433			}
434			frames -= res;
435		}
436		if (result <= 0) {
437			err = result;
438			goto error;
439		}
440		snd_pcm_mmap_appl_forward(pcm, frames);
441		if (frames == cont)
442			appl_offset = 0;
443		else
444			appl_offset += result;
445		size -= frames;
446		slave_size -= frames;
447		xfer += frames;
448	}
449	if (CHECK_SANITY(size)) {
450		SNDMSG("short commit: %ld", size);
451		return -EPIPE;
452	}
453	return xfer;
454
455 error:
456	return xfer > 0 ? xfer : err;
457}
458
459static snd_pcm_sframes_t
460snd_pcm_plugin_sync_hw_ptr_capture(snd_pcm_t *pcm,
461				   snd_pcm_sframes_t slave_size)
462{
463	snd_pcm_plugin_t *plugin = pcm->private_data;
464	snd_pcm_t *slave = plugin->gen.slave;
465	const snd_pcm_channel_area_t *areas;
466	snd_pcm_uframes_t xfer, hw_offset, size;
467	int err;
468
469	xfer = snd_pcm_mmap_capture_avail(pcm);
470	size = pcm->buffer_size - xfer;
471	areas = snd_pcm_mmap_areas(pcm);
472	hw_offset = snd_pcm_mmap_hw_offset(pcm);
473	while (size > 0 && slave_size > 0) {
474		snd_pcm_uframes_t frames = size;
475		snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
476		const snd_pcm_channel_area_t *slave_areas;
477		snd_pcm_uframes_t slave_offset;
478		snd_pcm_uframes_t slave_frames = ULONG_MAX;
479		snd_pcm_sframes_t result;
480		/* As mentioned in the ALSA API (see pcm/pcm.c:942):
481		 * The function #snd_pcm_avail_update()
482		 * have to be called before any mmap begin+commit operation.
483		 * Otherwise the snd_pcm_areas_copy will not called a second time.
484		 * But this is needed, if the ring buffer wrap is reached and
485		 * there is more data available.
486		 */
487		slave_size = snd_pcm_avail_update(slave);
488		result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
489		if (result < 0) {
490			err = result;
491			goto error;
492		}
493		if (frames > cont)
494			frames = cont;
495		frames = (plugin->read)(pcm, areas, hw_offset, frames,
496					slave_areas, slave_offset, &slave_frames);
497		result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
498		if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
499			snd_pcm_sframes_t res;
500			res = plugin->undo_read(slave, areas, hw_offset, frames, slave_frames - result);
501			if (res < 0) {
502				err = res;
503				goto error;
504			}
505			frames -= res;
506		}
507		if (result <= 0) {
508			err = result;
509			goto error;
510		}
511		snd_pcm_mmap_hw_forward(pcm, frames);
512		if (frames == cont)
513			hw_offset = 0;
514		else
515			hw_offset += frames;
516		size -= frames;
517		slave_size -= slave_frames;
518		xfer += frames;
519	}
520	return (snd_pcm_sframes_t)xfer;
521error:
522	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
523}
524
525static snd_pcm_sframes_t snd_pcm_plugin_sync_hw_ptr(snd_pcm_t *pcm,
526						    snd_pcm_uframes_t slave_hw_ptr,
527						    snd_pcm_sframes_t slave_size)
528{
529	if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
530	    pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
531	    pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED)
532		return snd_pcm_plugin_sync_hw_ptr_capture(pcm, slave_size);
533        *pcm->hw.ptr = slave_hw_ptr;
534        return slave_size;
535}
536
537static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm)
538{
539	snd_pcm_plugin_t *plugin = pcm->private_data;
540	snd_pcm_t *slave = plugin->gen.slave;
541	snd_pcm_sframes_t slave_size;
542
543	slave_size = snd_pcm_avail_update(slave);
544	return snd_pcm_plugin_sync_hw_ptr(pcm, *slave->hw.ptr, slave_size);
545}
546
547static int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
548{
549	snd_pcm_plugin_t *plugin = pcm->private_data;
550	snd_pcm_sframes_t err, diff;
551
552	err = snd_pcm_status(plugin->gen.slave, status);
553	if (err < 0)
554		return err;
555	snd_pcm_plugin_sync_hw_ptr(pcm, status->hw_ptr, status->avail);
556	/*
557	 * For capture stream, the situation is more complicated, because
558	 * snd_pcm_plugin_avail_update() commits the data to the slave pcm.
559	 * It means that the slave appl_ptr is updated. Calculate diff and
560	 * update the delay and avail.
561	 *
562	 * This resolves the data inconsistency for immediate calls:
563	 *    snd_pcm_avail_update()
564	 *    snd_pcm_status()
565	 */
566	if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
567		diff = pcm_frame_diff(status->appl_ptr, *pcm->appl.ptr, pcm->boundary);
568		status->appl_ptr = *pcm->appl.ptr;
569		status->avail += diff;
570		status->delay += diff;
571	} else {
572		assert(status->appl_ptr == *pcm->appl.ptr);
573	}
574	return 0;
575}
576
577int snd_pcm_plugin_may_wait_for_avail_min_conv(
578				snd_pcm_t *pcm,
579				snd_pcm_uframes_t avail,
580				snd_pcm_uframes_t (*conv)(snd_pcm_t *, snd_pcm_uframes_t))
581{
582	if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
583	    pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
584	    pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
585		/* mmap access on capture device already consumes data from
586		 * slave in avail_update operation. Entering snd_pcm_wait after
587		 * having already consumed some fragments leads to waiting for
588		 * too long time, as slave will unnecessarily wait for avail_min
589		 * condition reached again. To avoid unnecessary wait times we
590		 * adapt the avail_min threshold on slave dynamically. Just
591		 * modifying slave->avail_min as a shortcut and lightweight
592		 * solution does not work for all slave plugin types and in
593		 * addition it will not propagate the change through all
594		 * downstream plugins, so we have to use the sw_params API.
595		 * note: reading fragmental parts from slave will only happen
596		 * in case
597		 * a) the slave can provide contineous hw_ptr between periods
598		 * b) avail_min does not match one slave_period
599		 */
600		snd_pcm_generic_t *generic = pcm->private_data;
601		/*
602		 * do not use snd_pcm_plugin_t pointer here
603		 * this code is used from the generic plugins, too
604		 */
605		snd_pcm_t *slave = generic->slave;
606		snd_pcm_uframes_t needed_slave_avail_min;
607		snd_pcm_sframes_t available;
608
609		/* update, as it might have changed. This will also call
610		 * avail_update on slave and also can return error
611		 */
612		available = snd_pcm_avail_update(pcm);
613		if (available < 0)
614			return 0;
615
616		if ((snd_pcm_uframes_t)available >= pcm->avail_min)
617			/* don't wait at all. As we can't configure avail_min
618			 * of slave to 0 return here
619			 */
620			return 0;
621
622		needed_slave_avail_min = pcm->avail_min - available;
623
624		/* proportional adaption if rate converter is in place..
625		 * Can happen only on built-in rate plugin.
626		 * This code is also used by extplug, but extplug does not allow to alter the sampling rate.
627		 */
628		if (conv)
629			needed_slave_avail_min = conv(pcm, needed_slave_avail_min);
630
631		if (slave->avail_min != needed_slave_avail_min) {
632			snd_pcm_sw_params_t *swparams;
633			snd_pcm_sw_params_alloca(&swparams);
634			/* pray that changing sw_params while running is
635			 * properly implemented in all downstream plugins...
636			 * it's legal but not commonly used.
637			 */
638			snd_pcm_sw_params_current(slave, swparams);
639			/* snd_pcm_sw_params_set_avail_min() restricts setting
640			 * to >= period size. This conflicts at least with our
641			 * dshare patch which allows combining multiple periods
642			 * or with slaves which return hw postions between
643			 * periods -> set directly in sw_param structure
644			 */
645			swparams->avail_min = needed_slave_avail_min;
646			snd_pcm_sw_params(slave, swparams);
647		}
648		avail = available;
649	}
650	return snd_pcm_generic_may_wait_for_avail_min(pcm, avail);
651}
652
653int snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t *pcm,
654					  snd_pcm_uframes_t avail)
655{
656	return snd_pcm_plugin_may_wait_for_avail_min_conv(pcm, avail, NULL);
657}
658
659const snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops = {
660	.status = snd_pcm_plugin_status,
661	.state = snd_pcm_generic_state,
662	.hwsync = snd_pcm_generic_hwsync,
663	.delay = snd_pcm_plugin_delay,
664	.prepare = snd_pcm_plugin_prepare,
665	.reset = snd_pcm_plugin_reset,
666	.start = snd_pcm_generic_start,
667	.drop = snd_pcm_generic_drop,
668	.drain = snd_pcm_generic_drain,
669	.pause = snd_pcm_generic_pause,
670	.rewindable = snd_pcm_plugin_rewindable,
671	.rewind = snd_pcm_plugin_rewind,
672	.forwardable = snd_pcm_plugin_forwardable,
673	.forward = snd_pcm_plugin_forward,
674	.resume = snd_pcm_generic_resume,
675	.link = snd_pcm_generic_link,
676	.link_slaves = snd_pcm_generic_link_slaves,
677	.unlink = snd_pcm_generic_unlink,
678	.writei = snd_pcm_plugin_writei,
679	.writen = snd_pcm_plugin_writen,
680	.readi = snd_pcm_plugin_readi,
681	.readn = snd_pcm_plugin_readn,
682	.avail_update = snd_pcm_plugin_avail_update,
683	.mmap_commit = snd_pcm_plugin_mmap_commit,
684	.htimestamp = snd_pcm_generic_htimestamp,
685	.poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
686	.poll_descriptors = snd_pcm_generic_poll_descriptors,
687	.poll_revents = snd_pcm_generic_poll_revents,
688	.may_wait_for_avail_min = snd_pcm_plugin_may_wait_for_avail_min,
689};
690
691#endif
692