• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
34 PCM plugins extends functionality and features of PCM devices.
35 The plugins take care about various sample conversions, sample
36 copying among channels and so on.
37 
38 \section pcm_plugins_slave Slave definition
39 
40 The slave plugin can be specified directly with a string or the definition
41 can be entered inside a compound configuration node. Some restrictions can
42 be also specified (like static rate or count of channels).
43 
44 \code
45 pcm_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 
57 Example:
58 
59 \code
60 pcm_slave.slave_rate44100Hz {
61 	pcm "hw:0,0"
62 	rate 44100
63 }
64 
65 pcm.rate44100Hz {
66 	type plug
67 	slave slave_rate44100Hz
68 }
69 \endcode
70 
71 The equivalent configuration (in one compound):
72 
73 \code
74 pcm.rate44100Hz {
75 	type plug
76 	slave {
77 		pcm "hw:0,0"
78 		rate 44100
79 	}
80 }
81 \endcode
82 
83 */
84 
85 #include <limits.h>
86 #include "pcm_local.h"
87 #include "pcm_plugin.h"
88 
89 #ifndef DOC_HIDDEN
90 
91 static snd_pcm_sframes_t
snd_pcm_plugin_undo_read(snd_pcm_t * pcm ATTRIBUTE_UNUSED,const snd_pcm_channel_area_t * res_areas ATTRIBUTE_UNUSED,snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)92 snd_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 
101 static snd_pcm_sframes_t
snd_pcm_plugin_undo_write(snd_pcm_t * pcm ATTRIBUTE_UNUSED,const snd_pcm_channel_area_t * res_areas ATTRIBUTE_UNUSED,snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)102 snd_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 
111 snd_pcm_sframes_t
snd_pcm_plugin_undo_read_generic(snd_pcm_t * pcm ATTRIBUTE_UNUSED,const snd_pcm_channel_area_t * res_areas ATTRIBUTE_UNUSED,snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,snd_pcm_uframes_t slave_undo_size)112 snd_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 
121 snd_pcm_sframes_t
snd_pcm_plugin_undo_write_generic(snd_pcm_t * pcm ATTRIBUTE_UNUSED,const snd_pcm_channel_area_t * res_areas ATTRIBUTE_UNUSED,snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,snd_pcm_uframes_t slave_undo_size)122 snd_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 
snd_pcm_plugin_init(snd_pcm_plugin_t * plugin)131 void 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 
snd_pcm_plugin_delay(snd_pcm_t * pcm,snd_pcm_sframes_t * delayp)138 static 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 
snd_pcm_plugin_call_init_cb(snd_pcm_t * pcm,snd_pcm_plugin_t * plugin)149 static 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 
snd_pcm_plugin_prepare(snd_pcm_t * pcm)165 static 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 
snd_pcm_plugin_reset(snd_pcm_t * pcm)175 static 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 
snd_pcm_plugin_rewindable(snd_pcm_t * pcm)185 static snd_pcm_sframes_t snd_pcm_plugin_rewindable(snd_pcm_t *pcm)
186 {
187 	return snd_pcm_mmap_hw_rewindable(pcm);
188 }
189 
snd_pcm_plugin_rewind(snd_pcm_t * pcm,snd_pcm_uframes_t frames)190 snd_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 
snd_pcm_plugin_forwardable(snd_pcm_t * pcm)209 static snd_pcm_sframes_t snd_pcm_plugin_forwardable(snd_pcm_t *pcm)
210 {
211 	return snd_pcm_mmap_avail(pcm);
212 }
213 
snd_pcm_plugin_forward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)214 snd_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 
snd_pcm_plugin_write_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)233 static 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 
snd_pcm_plugin_read_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)290 static 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 
349 static snd_pcm_sframes_t
snd_pcm_plugin_writei(snd_pcm_t * pcm,const void * buffer,snd_pcm_uframes_t size)350 snd_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 
358 static snd_pcm_sframes_t
snd_pcm_plugin_writen(snd_pcm_t * pcm,void ** bufs,snd_pcm_uframes_t size)359 snd_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 
367 static snd_pcm_sframes_t
snd_pcm_plugin_readi(snd_pcm_t * pcm,void * buffer,snd_pcm_uframes_t size)368 snd_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 
376 static snd_pcm_sframes_t
snd_pcm_plugin_readn(snd_pcm_t * pcm,void ** bufs,snd_pcm_uframes_t size)377 snd_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 
385 static snd_pcm_sframes_t
snd_pcm_plugin_mmap_commit(snd_pcm_t * pcm,snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,snd_pcm_uframes_t size)386 snd_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 
459 static snd_pcm_sframes_t
snd_pcm_plugin_sync_hw_ptr_capture(snd_pcm_t * pcm,snd_pcm_sframes_t slave_size)460 snd_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;
521 error:
522 	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
523 }
524 
snd_pcm_plugin_sync_hw_ptr(snd_pcm_t * pcm,snd_pcm_uframes_t slave_hw_ptr,snd_pcm_sframes_t slave_size)525 static 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 
snd_pcm_plugin_avail_update(snd_pcm_t * pcm)537 static 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 
snd_pcm_plugin_status(snd_pcm_t * pcm,snd_pcm_status_t * status)547 static 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 
snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t * pcm,snd_pcm_uframes_t avail)577 int snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t *pcm,
578 					  snd_pcm_uframes_t avail)
579 {
580 	if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
581 	    pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
582 	    pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
583 		/* mmap access on capture device already consumes data from
584 		 * slave in avail_update operation. Entering snd_pcm_wait after
585 		 * having already consumed some fragments leads to waiting for
586 		 * too long time, as slave will unnecessarily wait for avail_min
587 		 * condition reached again. To avoid unnecessary wait times we
588 		 * adapt the avail_min threshold on slave dynamically. Just
589 		 * modifying slave->avail_min as a shortcut and lightweight
590 		 * solution does not work for all slave plugin types and in
591 		 * addition it will not propagate the change through all
592 		 * downstream plugins, so we have to use the sw_params API.
593 		 * note: reading fragmental parts from slave will only happen
594 		 * in case
595 		 * a) the slave can provide contineous hw_ptr between periods
596 		 * b) avail_min does not match one slave_period
597 		 */
598 		snd_pcm_plugin_t *plugin = pcm->private_data;
599 		snd_pcm_t *slave = plugin->gen.slave;
600 		snd_pcm_uframes_t needed_slave_avail_min;
601 		snd_pcm_sframes_t available;
602 
603 		/* update, as it might have changed. This will also call
604 		 * avail_update on slave and also can return error
605 		 */
606 		available = snd_pcm_avail_update(pcm);
607 		if (available < 0)
608 			return 0;
609 
610 		if ((snd_pcm_uframes_t)available >= pcm->avail_min)
611 			/* don't wait at all. As we can't configure avail_min
612 			 * of slave to 0 return here
613 			 */
614 			return 0;
615 
616 		needed_slave_avail_min = pcm->avail_min - available;
617 		if (slave->avail_min != needed_slave_avail_min) {
618 			snd_pcm_sw_params_t *swparams;
619 			snd_pcm_sw_params_alloca(&swparams);
620 			/* pray that changing sw_params while running is
621 			 * properly implemented in all downstream plugins...
622 			 * it's legal but not commonly used.
623 			 */
624 			snd_pcm_sw_params_current(slave, swparams);
625 			/* snd_pcm_sw_params_set_avail_min() restricts setting
626 			 * to >= period size. This conflicts at least with our
627 			 * dshare patch which allows combining multiple periods
628 			 * or with slaves which return hw postions between
629 			 * periods -> set directly in sw_param structure
630 			 */
631 			swparams->avail_min = needed_slave_avail_min;
632 			snd_pcm_sw_params(slave, swparams);
633 		}
634 		avail = available;
635 	}
636 	return snd_pcm_generic_may_wait_for_avail_min(pcm, avail);
637 }
638 
639 const snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops = {
640 	.status = snd_pcm_plugin_status,
641 	.state = snd_pcm_generic_state,
642 	.hwsync = snd_pcm_generic_hwsync,
643 	.delay = snd_pcm_plugin_delay,
644 	.prepare = snd_pcm_plugin_prepare,
645 	.reset = snd_pcm_plugin_reset,
646 	.start = snd_pcm_generic_start,
647 	.drop = snd_pcm_generic_drop,
648 	.drain = snd_pcm_generic_drain,
649 	.pause = snd_pcm_generic_pause,
650 	.rewindable = snd_pcm_plugin_rewindable,
651 	.rewind = snd_pcm_plugin_rewind,
652 	.forwardable = snd_pcm_plugin_forwardable,
653 	.forward = snd_pcm_plugin_forward,
654 	.resume = snd_pcm_generic_resume,
655 	.link = snd_pcm_generic_link,
656 	.link_slaves = snd_pcm_generic_link_slaves,
657 	.unlink = snd_pcm_generic_unlink,
658 	.writei = snd_pcm_plugin_writei,
659 	.writen = snd_pcm_plugin_writen,
660 	.readi = snd_pcm_plugin_readi,
661 	.readn = snd_pcm_plugin_readn,
662 	.avail_update = snd_pcm_plugin_avail_update,
663 	.mmap_commit = snd_pcm_plugin_mmap_commit,
664 	.htimestamp = snd_pcm_generic_htimestamp,
665 	.poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
666 	.poll_descriptors = snd_pcm_generic_poll_descriptors,
667 	.poll_revents = snd_pcm_generic_poll_revents,
668 	.may_wait_for_avail_min = snd_pcm_plugin_may_wait_for_avail_min,
669 };
670 
671 #endif
672