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