OSDN Git Service

pcm: plugin - fix status code for capture
[android-x86/external-alsa-lib.git] / src / pcm / pcm_plugin.c
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
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
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
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
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
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
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
149 static int snd_pcm_plugin_prepare(snd_pcm_t *pcm)
150 {
151         snd_pcm_plugin_t *plugin = pcm->private_data;
152         int err;
153         err = snd_pcm_prepare(plugin->gen.slave);
154         if (err < 0)
155                 return err;
156         *pcm->hw.ptr = 0;
157         *pcm->appl.ptr = 0;
158         if (plugin->init) {
159                 err = plugin->init(pcm);
160                 if (err < 0)
161                         return err;
162         }
163         return 0;
164 }
165
166 static int snd_pcm_plugin_reset(snd_pcm_t *pcm)
167 {
168         snd_pcm_plugin_t *plugin = pcm->private_data;
169         int err;
170         err = snd_pcm_reset(plugin->gen.slave);
171         if (err < 0)
172                 return err;
173         *pcm->hw.ptr = 0;
174         *pcm->appl.ptr = 0;
175         if (plugin->init) {
176                 err = plugin->init(pcm);
177                 if (err < 0)
178                         return err;
179         }
180         return 0;
181 }
182
183 static snd_pcm_sframes_t snd_pcm_plugin_rewindable(snd_pcm_t *pcm)
184 {
185         return snd_pcm_mmap_hw_rewindable(pcm);
186 }
187
188 snd_pcm_sframes_t snd_pcm_plugin_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
189 {
190         snd_pcm_plugin_t *plugin = pcm->private_data;
191         snd_pcm_sframes_t n = snd_pcm_plugin_rewindable(pcm);
192         snd_pcm_sframes_t sframes;
193
194         if ((snd_pcm_uframes_t)n < frames)
195                 frames = n;
196         if (frames == 0)
197                 return 0;
198         
199         sframes = frames;
200         sframes = snd_pcm_rewind(plugin->gen.slave, sframes);
201         if (sframes < 0)
202                 return sframes;
203         snd_pcm_mmap_appl_backward(pcm, (snd_pcm_uframes_t) sframes);
204         return (snd_pcm_sframes_t) sframes;
205 }
206
207 static snd_pcm_sframes_t snd_pcm_plugin_forwardable(snd_pcm_t *pcm)
208 {
209         return snd_pcm_mmap_avail(pcm);
210 }
211
212 snd_pcm_sframes_t snd_pcm_plugin_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
213 {
214         snd_pcm_plugin_t *plugin = pcm->private_data;
215         snd_pcm_sframes_t n = snd_pcm_plugin_forwardable(pcm);
216         snd_pcm_sframes_t sframes;
217
218         if ((snd_pcm_uframes_t)n < frames)
219                 frames = n;
220         if (frames == 0)
221                 return 0;
222         
223         sframes = frames;
224         sframes = INTERNAL(snd_pcm_forward)(plugin->gen.slave, sframes);
225         if (sframes < 0)
226                 return sframes;
227         snd_pcm_mmap_appl_forward(pcm, (snd_pcm_uframes_t) frames);
228         return (snd_pcm_sframes_t) frames;
229 }
230
231 static snd_pcm_sframes_t snd_pcm_plugin_write_areas(snd_pcm_t *pcm,
232                                                     const snd_pcm_channel_area_t *areas,
233                                                     snd_pcm_uframes_t offset,
234                                                     snd_pcm_uframes_t size)
235 {
236         snd_pcm_plugin_t *plugin = pcm->private_data;
237         snd_pcm_t *slave = plugin->gen.slave;
238         snd_pcm_uframes_t xfer = 0;
239         snd_pcm_sframes_t result;
240         int err;
241
242         while (size > 0) {
243                 snd_pcm_uframes_t frames = size;
244                 const snd_pcm_channel_area_t *slave_areas;
245                 snd_pcm_uframes_t slave_offset;
246                 snd_pcm_uframes_t slave_frames = ULONG_MAX;
247                 
248                 result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
249                 if (result < 0) {
250                         err = result;
251                         goto error;
252                 }
253                 if (slave_frames == 0)
254                         break;
255                 frames = plugin->write(pcm, areas, offset, frames,
256                                        slave_areas, slave_offset, &slave_frames);
257                 if (CHECK_SANITY(slave_frames > snd_pcm_mmap_playback_avail(slave))) {
258                         SNDMSG("write overflow %ld > %ld", slave_frames,
259                                snd_pcm_mmap_playback_avail(slave));
260                         err = -EPIPE;
261                         goto error;
262                 }
263                 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
264                 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
265                         snd_pcm_sframes_t res;
266                         res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
267                         if (res < 0) {
268                                 err = res;
269                                 goto error;
270                         }
271                         frames -= res;
272                 }
273                 if (result <= 0) {
274                         err = result;
275                         goto error;
276                 }
277                 snd_pcm_mmap_appl_forward(pcm, frames);
278                 offset += frames;
279                 xfer += frames;
280                 size -= frames;
281         }
282         return (snd_pcm_sframes_t)xfer;
283
284  error:
285         return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
286 }
287
288 static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm,
289                                                    const snd_pcm_channel_area_t *areas,
290                                                    snd_pcm_uframes_t offset,
291                                                    snd_pcm_uframes_t size)
292 {
293         snd_pcm_plugin_t *plugin = pcm->private_data;
294         snd_pcm_t *slave = plugin->gen.slave;
295         snd_pcm_uframes_t xfer = 0;
296         snd_pcm_sframes_t result;
297         int err;
298         
299         while (size > 0) {
300                 snd_pcm_uframes_t frames = size;
301                 const snd_pcm_channel_area_t *slave_areas;
302                 snd_pcm_uframes_t slave_offset;
303                 snd_pcm_uframes_t slave_frames = ULONG_MAX;
304                 
305                 result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
306                 if (result < 0) {
307                         err = result;
308                         goto error;
309                 }
310                 if (slave_frames == 0)
311                         break;
312                 frames = (plugin->read)(pcm, areas, offset, frames,
313                                       slave_areas, slave_offset, &slave_frames);
314                 if (CHECK_SANITY(slave_frames > snd_pcm_mmap_capture_avail(slave))) {
315                         SNDMSG("read overflow %ld > %ld", slave_frames,
316                                snd_pcm_mmap_playback_avail(slave));
317                         err = -EPIPE;
318                         goto error;
319                 }
320                 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
321                 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
322                         snd_pcm_sframes_t res;
323                         
324                         res = plugin->undo_read(slave, areas, offset, frames, slave_frames - result);
325                         if (res < 0) {
326                                 err = res;
327                                 goto error;
328                         }
329                         frames -= res;
330                 }
331                 if (result <= 0) {
332                         err = result;
333                         goto error;
334                 }
335                 snd_pcm_mmap_appl_forward(pcm, frames);
336                 offset += frames;
337                 xfer += frames;
338                 size -= frames;
339         }
340         return (snd_pcm_sframes_t)xfer;
341
342  error:
343         return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
344 }
345
346
347 static snd_pcm_sframes_t
348 snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
349 {
350         snd_pcm_channel_area_t areas[pcm->channels];
351         snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
352         return snd_pcm_write_areas(pcm, areas, 0, size, 
353                                    snd_pcm_plugin_write_areas);
354 }
355
356 static snd_pcm_sframes_t
357 snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
358 {
359         snd_pcm_channel_area_t areas[pcm->channels];
360         snd_pcm_areas_from_bufs(pcm, areas, bufs);
361         return snd_pcm_write_areas(pcm, areas, 0, size,
362                                    snd_pcm_plugin_write_areas);
363 }
364
365 static snd_pcm_sframes_t
366 snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
367 {
368         snd_pcm_channel_area_t areas[pcm->channels];
369         snd_pcm_areas_from_buf(pcm, areas, buffer);
370         return snd_pcm_read_areas(pcm, areas, 0, size,
371                                   snd_pcm_plugin_read_areas);
372 }
373
374 static snd_pcm_sframes_t
375 snd_pcm_plugin_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
376 {
377         snd_pcm_channel_area_t areas[pcm->channels];
378         snd_pcm_areas_from_bufs(pcm, areas, bufs);
379         return snd_pcm_read_areas(pcm, areas, 0, size,
380                                   snd_pcm_plugin_read_areas);
381 }
382
383 static snd_pcm_sframes_t
384 snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm,
385                            snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
386                            snd_pcm_uframes_t size)
387 {
388         snd_pcm_plugin_t *plugin = pcm->private_data;
389         snd_pcm_t *slave = plugin->gen.slave;
390         const snd_pcm_channel_area_t *areas;
391         snd_pcm_uframes_t appl_offset;
392         snd_pcm_sframes_t slave_size;
393         snd_pcm_sframes_t xfer;
394         int err;
395
396         if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
397                 snd_pcm_mmap_appl_forward(pcm, size);
398                 return size;
399         }
400         slave_size = snd_pcm_avail_update(slave);
401         if (slave_size < 0)
402                 return slave_size;
403         areas = snd_pcm_mmap_areas(pcm);
404         appl_offset = snd_pcm_mmap_offset(pcm);
405         xfer = 0;
406         while (size > 0 && slave_size > 0) {
407                 snd_pcm_uframes_t frames = size;
408                 snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
409                 const snd_pcm_channel_area_t *slave_areas;
410                 snd_pcm_uframes_t slave_offset;
411                 snd_pcm_uframes_t slave_frames = ULONG_MAX;
412                 snd_pcm_sframes_t result;
413
414                 result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
415                 if (result < 0) {
416                         err = result;
417                         goto error;
418                 }
419                 if (frames > cont)
420                         frames = cont;
421                 frames = plugin->write(pcm, areas, appl_offset, frames,
422                                        slave_areas, slave_offset, &slave_frames);
423                 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
424                 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
425                         snd_pcm_sframes_t res;
426                         
427                         res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
428                         if (res < 0) {
429                                 err = res;
430                                 goto error;
431                         }
432                         frames -= res;
433                 }
434                 if (result <= 0) {
435                         err = result;
436                         goto error;
437                 }
438                 snd_pcm_mmap_appl_forward(pcm, frames);
439                 if (frames == cont)
440                         appl_offset = 0;
441                 else
442                         appl_offset += result;
443                 size -= frames;
444                 slave_size -= frames;
445                 xfer += frames;
446         }
447         if (CHECK_SANITY(size)) {
448                 SNDMSG("short commit: %ld", size);
449                 return -EPIPE;
450         }
451         return xfer;
452
453  error:
454         return xfer > 0 ? xfer : err;
455 }
456
457 static snd_pcm_sframes_t
458 snd_pcm_plugin_sync_hw_ptr_capture(snd_pcm_t *pcm,
459                                    snd_pcm_sframes_t slave_size)
460 {
461         snd_pcm_plugin_t *plugin = pcm->private_data;
462         snd_pcm_t *slave = plugin->gen.slave;
463         const snd_pcm_channel_area_t *areas;
464         snd_pcm_uframes_t xfer, hw_offset, size;
465         int err;
466
467         xfer = snd_pcm_mmap_capture_avail(pcm);
468         size = pcm->buffer_size - xfer;
469         areas = snd_pcm_mmap_areas(pcm);
470         hw_offset = snd_pcm_mmap_hw_offset(pcm);
471         while (size > 0 && slave_size > 0) {
472                 snd_pcm_uframes_t frames = size;
473                 snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
474                 const snd_pcm_channel_area_t *slave_areas;
475                 snd_pcm_uframes_t slave_offset;
476                 snd_pcm_uframes_t slave_frames = ULONG_MAX;
477                 snd_pcm_sframes_t result;
478                 /* As mentioned in the ALSA API (see pcm/pcm.c:942):
479                  * The function #snd_pcm_avail_update()
480                  * have to be called before any mmap begin+commit operation.
481                  * Otherwise the snd_pcm_areas_copy will not called a second time.
482                  * But this is needed, if the ring buffer wrap is reached and
483                  * there is more data available.
484                  */
485                 slave_size = snd_pcm_avail_update(slave);
486                 result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
487                 if (result < 0) {
488                         err = result;
489                         goto error;
490                 }
491                 if (frames > cont)
492                         frames = cont;
493                 frames = (plugin->read)(pcm, areas, hw_offset, frames,
494                                         slave_areas, slave_offset, &slave_frames);
495                 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
496                 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
497                         snd_pcm_sframes_t res;
498                         res = plugin->undo_read(slave, areas, hw_offset, frames, slave_frames - result);
499                         if (res < 0) {
500                                 err = res;
501                                 goto error;
502                         }
503                         frames -= res;
504                 }
505                 if (result <= 0) {
506                         err = result;
507                         goto error;
508                 }
509                 snd_pcm_mmap_hw_forward(pcm, frames);
510                 if (frames == cont)
511                         hw_offset = 0;
512                 else
513                         hw_offset += frames;
514                 size -= frames;
515                 slave_size -= slave_frames;
516                 xfer += frames;
517         }
518         return (snd_pcm_sframes_t)xfer;
519 error:
520         return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
521 }
522
523 static snd_pcm_sframes_t snd_pcm_plugin_sync_hw_ptr(snd_pcm_t *pcm,
524                                                     snd_pcm_uframes_t slave_hw_ptr,
525                                                     snd_pcm_sframes_t slave_size)
526 {
527         if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
528             pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
529             pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED)
530                 return snd_pcm_plugin_sync_hw_ptr_capture(pcm, slave_size);
531         *pcm->hw.ptr = slave_hw_ptr;
532         return slave_size;
533 }
534
535 static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm)
536 {
537         snd_pcm_plugin_t *plugin = pcm->private_data;
538         snd_pcm_t *slave = plugin->gen.slave;
539         snd_pcm_sframes_t slave_size;
540
541         slave_size = snd_pcm_avail_update(slave);
542         return snd_pcm_plugin_sync_hw_ptr(pcm, *slave->hw.ptr, slave_size);
543 }
544
545 static int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
546 {
547         snd_pcm_plugin_t *plugin = pcm->private_data;
548         snd_pcm_sframes_t err, diff;
549
550         err = snd_pcm_status(plugin->gen.slave, status);
551         if (err < 0)
552                 return err;
553         snd_pcm_plugin_sync_hw_ptr(pcm, status->hw_ptr, status->avail);
554         /*
555          * For capture stream, the situation is more complicated, because
556          * snd_pcm_plugin_avail_update() commits the data to the slave pcm.
557          * It means that the slave appl_ptr is updated. Calculate diff and
558          * update the delay and avail.
559          *
560          * This resolves the data inconsistency for immediate calls:
561          *    snd_pcm_avail_update()
562          *    snd_pcm_status()
563          */
564         if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
565                 status->appl_ptr = *pcm->appl.ptr;
566                 diff = pcm_frame_diff(status->appl_ptr, *pcm->appl.ptr, pcm->boundary);
567                 status->avail += diff;
568                 status->delay += diff;
569         } else {
570                 assert(status->appl_ptr == *pcm->appl.ptr);
571         }
572         return 0;
573 }
574
575 int snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t *pcm,
576                                           snd_pcm_uframes_t avail)
577 {
578         if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
579             pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
580             pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
581                 /* mmap access on capture device already consumes data from
582                  * slave in avail_update operation. Entering snd_pcm_wait after
583                  * having already consumed some fragments leads to waiting for
584                  * too long time, as slave will unnecessarily wait for avail_min
585                  * condition reached again. To avoid unnecessary wait times we
586                  * adapt the avail_min threshold on slave dynamically. Just
587                  * modifying slave->avail_min as a shortcut and lightweight
588                  * solution does not work for all slave plugin types and in
589                  * addition it will not propagate the change through all
590                  * downstream plugins, so we have to use the sw_params API.
591                  * note: reading fragmental parts from slave will only happen
592                  * in case
593                  * a) the slave can provide contineous hw_ptr between periods
594                  * b) avail_min does not match one slave_period
595                  */
596                 snd_pcm_plugin_t *plugin = pcm->private_data;
597                 snd_pcm_t *slave = plugin->gen.slave;
598                 snd_pcm_uframes_t needed_slave_avail_min;
599                 snd_pcm_sframes_t available;
600
601                 /* update, as it might have changed. This will also call
602                  * avail_update on slave and also can return error
603                  */
604                 available = snd_pcm_avail_update(pcm);
605                 if (available < 0)
606                         return 0;
607
608                 if ((snd_pcm_uframes_t)available >= pcm->avail_min)
609                         /* don't wait at all. As we can't configure avail_min
610                          * of slave to 0 return here
611                          */
612                         return 0;
613
614                 needed_slave_avail_min = pcm->avail_min - available;
615                 if (slave->avail_min != needed_slave_avail_min) {
616                         snd_pcm_sw_params_t *swparams;
617                         snd_pcm_sw_params_alloca(&swparams);
618                         /* pray that changing sw_params while running is
619                          * properly implemented in all downstream plugins...
620                          * it's legal but not commonly used.
621                          */
622                         snd_pcm_sw_params_current(slave, swparams);
623                         /* snd_pcm_sw_params_set_avail_min() restricts setting
624                          * to >= period size. This conflicts at least with our
625                          * dshare patch which allows combining multiple periods
626                          * or with slaves which return hw postions between
627                          * periods -> set directly in sw_param structure
628                          */
629                         swparams->avail_min = needed_slave_avail_min;
630                         snd_pcm_sw_params(slave, swparams);
631                 }
632                 avail = available;
633         }
634         return snd_pcm_generic_may_wait_for_avail_min(pcm, avail);
635 }
636
637 const snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops = {
638         .status = snd_pcm_plugin_status,
639         .state = snd_pcm_generic_state,
640         .hwsync = snd_pcm_generic_hwsync,
641         .delay = snd_pcm_plugin_delay,
642         .prepare = snd_pcm_plugin_prepare,
643         .reset = snd_pcm_plugin_reset,
644         .start = snd_pcm_generic_start,
645         .drop = snd_pcm_generic_drop,
646         .drain = snd_pcm_generic_drain,
647         .pause = snd_pcm_generic_pause,
648         .rewindable = snd_pcm_plugin_rewindable,
649         .rewind = snd_pcm_plugin_rewind,
650         .forwardable = snd_pcm_plugin_forwardable,
651         .forward = snd_pcm_plugin_forward,
652         .resume = snd_pcm_generic_resume,
653         .link = snd_pcm_generic_link,
654         .link_slaves = snd_pcm_generic_link_slaves,
655         .unlink = snd_pcm_generic_unlink,
656         .writei = snd_pcm_plugin_writei,
657         .writen = snd_pcm_plugin_writen,
658         .readi = snd_pcm_plugin_readi,
659         .readn = snd_pcm_plugin_readn,
660         .avail_update = snd_pcm_plugin_avail_update,
661         .mmap_commit = snd_pcm_plugin_mmap_commit,
662         .htimestamp = snd_pcm_generic_htimestamp,
663         .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
664         .poll_descriptors = snd_pcm_generic_poll_descriptors,
665         .poll_revents = snd_pcm_generic_poll_revents,
666         .may_wait_for_avail_min = snd_pcm_plugin_may_wait_for_avail_min,
667 };
668
669 #endif