1 /*
2 * Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
3 *
4 * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
5 * the the people's Republic of China and other countries.
6 * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
7 *
8 * DISCLAIMER
9 * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
10 * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
11 * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
12 * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
13 * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
14 * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
15 * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
16 *
17 *
18 * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
19 * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
20 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
21 * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
22 * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 
33 #include <limits.h>
34 #include <stdio.h>
35 #include "pcm_local.h"
36 #include "pcm_plugin_generic.h"
37 
38 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)39 snd_pcm_plugin_undo_read(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
40              const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
41              snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
42              snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
43              snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
44 {
45     return -EIO;
46 }
47 
48 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)49 snd_pcm_plugin_undo_write(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
50               const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
51               snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
52               snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
53               snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
54 {
55     return -EIO;
56 }
57 
58 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)59 snd_pcm_plugin_undo_read_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
60                  const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
61                  snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
62                  snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
63                  snd_pcm_uframes_t slave_undo_size)
64 {
65     return slave_undo_size;
66 }
67 
68 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)69 snd_pcm_plugin_undo_write_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
70                   const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
71                   snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
72                   snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
73                   snd_pcm_uframes_t slave_undo_size)
74 {
75     return slave_undo_size;
76 }
77 
snd_pcm_plugin_init(snd_pcm_plugin_t * plugin)78 void snd_pcm_plugin_init(snd_pcm_plugin_t *plugin)
79 {
80     awalsa_debug("\n");
81     memset(plugin, 0, sizeof(snd_pcm_plugin_t));
82     plugin->undo_read = snd_pcm_plugin_undo_read;
83     plugin->undo_write = snd_pcm_plugin_undo_write;
84 }
85 
snd_pcm_plugin_delay(snd_pcm_t * pcm,snd_pcm_sframes_t * delayp)86 static int snd_pcm_plugin_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
87 {
88     snd_pcm_plugin_t *plugin = pcm->private_data;
89     snd_pcm_sframes_t sd;
90 
91     awalsa_debug("\n");
92     int err = snd_pcm_delay(plugin->gen.slave, &sd);
93     if (err < 0)
94         return err;
95     if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
96             pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
97             pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
98         sd += snd_pcm_mmap_capture_avail(pcm);
99     }
100 
101     *delayp = sd;
102     return 0;
103 }
104 
snd_pcm_plugin_prepare(snd_pcm_t * pcm)105 static int snd_pcm_plugin_prepare(snd_pcm_t *pcm)
106 {
107     snd_pcm_plugin_t *plugin = pcm->private_data;
108     int err;
109 
110     awalsa_debug("\n");
111     err = snd_pcm_prepare(plugin->gen.slave);
112     if (err < 0)
113         return err;
114     *pcm->hw.ptr = 0;
115     *pcm->appl.ptr = 0;
116     if (plugin->init) {
117         err = plugin->init(pcm);
118         if (err < 0)
119             return err;
120     }
121     return 0;
122 }
123 
snd_pcm_plugin_reset(snd_pcm_t * pcm)124 static int snd_pcm_plugin_reset(snd_pcm_t *pcm)
125 {
126     snd_pcm_plugin_t *plugin = pcm->private_data;
127     int err;
128 
129     awalsa_debug("\n");
130     err = snd_pcm_reset(plugin->gen.slave);
131     if (err < 0)
132         return err;
133     *pcm->hw.ptr = 0;
134     *pcm->appl.ptr = 0;
135     if (plugin->init) {
136         err = plugin->init(pcm);
137         if (err < 0)
138             return err;
139     }
140     return 0;
141 }
142 
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)143 static snd_pcm_sframes_t snd_pcm_plugin_write_areas(snd_pcm_t *pcm,
144                             const snd_pcm_channel_area_t *areas,
145                             snd_pcm_uframes_t offset,
146                             snd_pcm_uframes_t size)
147 {
148     snd_pcm_plugin_t *plugin = pcm->private_data;
149     snd_pcm_t *slave = plugin->gen.slave;
150     snd_pcm_uframes_t xfer = 0;
151     snd_pcm_sframes_t result;
152     int err;
153 
154     awalsa_debug("\n");
155     while (size > 0) {
156         snd_pcm_uframes_t frames = size;
157         const snd_pcm_channel_area_t *slave_areas;
158         snd_pcm_uframes_t slave_offset;
159         snd_pcm_uframes_t slave_frames = ULONG_MAX;
160 
161         result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
162         if (result < 0) {
163             err = result;
164             goto error;
165         }
166         if (slave_frames == 0)
167             break;
168         frames = plugin->write(pcm, areas, offset, frames,
169                        slave_areas, slave_offset, &slave_frames);
170         if (slave_frames > snd_pcm_mmap_playback_avail(slave)) {
171             awalsa_err("write overflow %ld > %ld\n", slave_frames,
172                     snd_pcm_mmap_playback_avail(slave));
173             err = -EPIPE;
174             goto error;
175         }
176         result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
177         if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
178             snd_pcm_sframes_t res;
179             res = plugin->undo_write(pcm, slave_areas,
180                     slave_offset + result, slave_frames,
181                     slave_frames - result);
182             if (res < 0) {
183                 err = res;
184                 goto error;
185             }
186             frames -= res;
187         }
188         if (result <= 0) {
189             err = result;
190             goto error;
191         }
192         snd_pcm_mmap_appl_forward(pcm, frames);
193         offset += frames;
194         xfer += frames;
195         size -= frames;
196     }
197     return (snd_pcm_sframes_t)xfer;
198 
199  error:
200     return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
201 }
202 
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)203 static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm,
204                            const snd_pcm_channel_area_t *areas,
205                            snd_pcm_uframes_t offset,
206                            snd_pcm_uframes_t size)
207 {
208     snd_pcm_plugin_t *plugin = pcm->private_data;
209     snd_pcm_t *slave = plugin->gen.slave;
210     snd_pcm_uframes_t xfer = 0;
211     snd_pcm_sframes_t result;
212     int err;
213 
214     awalsa_debug("\n");
215     while (size > 0) {
216         snd_pcm_uframes_t frames = size;
217         const snd_pcm_channel_area_t *slave_areas;
218         snd_pcm_uframes_t slave_offset;
219         snd_pcm_uframes_t slave_frames = ULONG_MAX;
220 
221         result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
222         if (result < 0) {
223             err = result;
224             goto error;
225         }
226         if (slave_frames == 0)
227             break;
228         frames = (plugin->read)(pcm, areas, offset, frames,
229                       slave_areas, slave_offset, &slave_frames);
230         if (slave_frames > snd_pcm_mmap_capture_avail(slave)) {
231             awalsa_err("read overflow %ld > %ld\n", slave_frames,
232                     snd_pcm_mmap_playback_avail(slave));
233             err = -EPIPE;
234             goto error;
235         }
236         result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
237         if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
238             snd_pcm_sframes_t res;
239 
240             res = plugin->undo_read(slave, areas, offset, frames,
241                     slave_frames - result);
242             if (res < 0) {
243                 err = res;
244                 goto error;
245             }
246             frames -= res;
247         }
248         if (result <= 0) {
249             err = result;
250             goto error;
251         }
252         snd_pcm_mmap_appl_forward(pcm, frames);
253         offset += frames;
254         xfer += frames;
255         size -= frames;
256     }
257     return (snd_pcm_sframes_t)xfer;
258 
259  error:
260     return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
261 }
262 
263 static snd_pcm_sframes_t
snd_pcm_plugin_writei(snd_pcm_t * pcm,const void * buffer,snd_pcm_uframes_t size)264 snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
265 {
266     snd_pcm_channel_area_t areas[pcm->channels];
267     awalsa_debug("\n");
268     snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
269     return snd_pcm_write_areas(pcm, areas, 0, size,
270                    snd_pcm_plugin_write_areas);
271 }
272 
273 static snd_pcm_sframes_t
snd_pcm_plugin_readi(snd_pcm_t * pcm,void * buffer,snd_pcm_uframes_t size)274 snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
275 {
276     snd_pcm_channel_area_t areas[pcm->channels];
277     awalsa_debug("\n");
278     snd_pcm_areas_from_buf(pcm, areas, buffer);
279     return snd_pcm_read_areas(pcm, areas, 0, size,
280                   snd_pcm_plugin_read_areas);
281 }
282 
snd_pcm_plugin_avail_update(snd_pcm_t * pcm)283 static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm)
284 {
285     snd_pcm_plugin_t *plugin = pcm->private_data;
286     snd_pcm_t *slave = plugin->gen.slave;
287     snd_pcm_sframes_t slave_size;
288     int err;
289 
290     awalsa_debug("\n");
291     slave_size = snd_pcm_avail_update(slave);
292     if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
293         pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
294         pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED)
295         goto _capture;
296     *pcm->hw.ptr = *slave->hw.ptr;
297     return slave_size;
298  _capture:
299     {
300         const snd_pcm_channel_area_t *areas;
301         snd_pcm_uframes_t xfer, hw_offset, size;
302 
303         xfer = snd_pcm_mmap_capture_avail(pcm);
304         size = pcm->buffer_size - xfer;
305         areas = snd_pcm_mmap_areas(pcm);
306         hw_offset = snd_pcm_mmap_hw_offset(pcm);
307         while (size > 0 && slave_size > 0) {
308             snd_pcm_uframes_t frames = size;
309             snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
310             const snd_pcm_channel_area_t *slave_areas;
311             snd_pcm_uframes_t slave_offset;
312             snd_pcm_uframes_t slave_frames = ULONG_MAX;
313             snd_pcm_sframes_t result;
314             /* As mentioned in the ALSA API (see pcm/pcm.c:942):
315              * The function #snd_pcm_avail_update()
316              * have to be called before any mmap begin+commit operation.
317              * Otherwise the snd_pcm_areas_copy will not called a second time.
318              * But this is needed, if the ring buffer wrap is reached and
319              * there is more data available.
320              */
321             slave_size = snd_pcm_avail_update(slave);
322             result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
323             if (result < 0) {
324                 err = result;
325                 goto error;
326             }
327             if (frames > cont)
328                 frames = cont;
329             frames = (plugin->read)(pcm, areas, hw_offset, frames,
330                           slave_areas, slave_offset, &slave_frames);
331             result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
332             if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
333                 snd_pcm_sframes_t res;
334 
335                 res = plugin->undo_read(slave, areas, hw_offset, frames, slave_frames - result);
336                 if (res < 0) {
337                     err = res;
338                     goto error;
339                 }
340                 frames -= res;
341             }
342             if (result <= 0) {
343                 err = result;
344                 goto error;
345             }
346             snd_pcm_mmap_hw_forward(pcm, frames);
347             if (frames == cont)
348                 hw_offset = 0;
349             else
350                 hw_offset += frames;
351             size -= frames;
352             slave_size -= slave_frames;
353             xfer += frames;
354         }
355         return (snd_pcm_sframes_t)xfer;
356 
357     error:
358         return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
359     }
360 }
361 
362 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)363 snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm,
364                snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
365                snd_pcm_uframes_t size)
366 {
367     snd_pcm_plugin_t *plugin = pcm->private_data;
368     snd_pcm_t *slave = plugin->gen.slave;
369     const snd_pcm_channel_area_t *areas;
370     snd_pcm_uframes_t appl_offset;
371     snd_pcm_sframes_t slave_size;
372     snd_pcm_sframes_t xfer;
373     int err;
374 
375     awalsa_debug("\n");
376     if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
377         snd_pcm_mmap_appl_forward(pcm, size);
378         return size;
379     }
380     slave_size = snd_pcm_avail_update(slave);
381     if (slave_size < 0)
382         return slave_size;
383     areas = snd_pcm_mmap_areas(pcm);
384     appl_offset = snd_pcm_mmap_offset(pcm);
385     xfer = 0;
386     while (size > 0 && slave_size > 0) {
387         snd_pcm_uframes_t frames = size;
388         snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
389         const snd_pcm_channel_area_t *slave_areas;
390         snd_pcm_uframes_t slave_offset;
391         snd_pcm_uframes_t slave_frames = ULONG_MAX;
392         snd_pcm_sframes_t result;
393 
394         result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
395         if (result < 0) {
396             err = result;
397             goto error;
398         }
399         if (frames > cont)
400             frames = cont;
401         frames = plugin->write(pcm, areas, appl_offset, frames,
402                        slave_areas, slave_offset, &slave_frames);
403         result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
404         if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
405             snd_pcm_sframes_t res;
406 
407             res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
408             if (res < 0) {
409                 err = res;
410                 goto error;
411             }
412             frames -= res;
413         }
414         if (result <= 0) {
415             err = result;
416             goto error;
417         }
418         snd_pcm_mmap_appl_forward(pcm, frames);
419         if (frames == cont)
420             appl_offset = 0;
421         else
422             appl_offset += result;
423         size -= frames;
424         slave_size -= frames;
425         xfer += frames;
426     }
427     if (size) {
428         awalsa_err("short commit: %ld\n", size);
429         return -EPIPE;
430     }
431     return xfer;
432 
433  error:
434     return xfer > 0 ? xfer : err;
435 }
436 
snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t * pcm,snd_pcm_uframes_t avail)437 int snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t *pcm,
438                       snd_pcm_uframes_t avail)
439 {
440     awalsa_debug("\n");
441     if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
442         pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
443         pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
444         /* mmap access on capture device already consumes data from
445          * slave in avail_update operation. Entering snd_pcm_wait after
446          * having already consumed some fragments leads to waiting for
447          * too long time, as slave will unnecessarily wait for avail_min
448          * condition reached again. To avoid unnecessary wait times we
449          * adapt the avail_min threshold on slave dynamically. Just
450          * modifying slave->avail_min as a shortcut and lightweight
451          * solution does not work for all slave plugin types and in
452          * addition it will not propagate the change through all
453          * downstream plugins, so we have to use the sw_params API.
454          * note: reading fragmental parts from slave will only happen
455          * in case
456          * a) the slave can provide contineous hw_ptr between periods
457          * b) avail_min does not match one slave_period
458          */
459         snd_pcm_plugin_t *plugin = pcm->private_data;
460         snd_pcm_t *slave = plugin->gen.slave;
461         snd_pcm_uframes_t needed_slave_avail_min;
462         snd_pcm_sframes_t available;
463 
464         /* update, as it might have changed. This will also call
465          * avail_update on slave and also can return error
466          */
467         available = snd_pcm_avail_update(pcm);
468         if (available < 0)
469             return 0;
470 
471         if (available >= pcm->avail_min)
472             /* don't wait at all. As we can't configure avail_min
473              * of slave to 0 return here
474              */
475             return 0;
476 
477         needed_slave_avail_min = pcm->avail_min - available;
478         if (slave->avail_min != needed_slave_avail_min) {
479             snd_pcm_sw_params_t *swparams;
480             snd_pcm_sw_params_alloca(&swparams);
481             /* pray that changing sw_params while running is
482              * properly implemented in all downstream plugins...
483              * it's legal but not commonly used.
484              */
485             snd_pcm_sw_params_current(slave, swparams);
486             /* snd_pcm_sw_params_set_avail_min() restricts setting
487              * to >= period size. This conflicts at least with our
488              * dshare patch which allows combining multiple periods
489              * or with slaves which return hw postions between
490              * periods -> set directly in sw_param structure
491              */
492             swparams->avail_min = needed_slave_avail_min;
493             snd_pcm_sw_params(slave, swparams);
494         }
495         avail = available;
496     }
497     return snd_pcm_generic_may_wait_for_avail_min(pcm, avail);
498 }
499 
snd_pcm_plugin_wait(snd_pcm_t * pcm,int timeout)500 int snd_pcm_plugin_wait(snd_pcm_t *pcm, int timeout)
501 {
502     snd_pcm_plugin_t *plugin = pcm->private_data;
503     snd_pcm_t *spcm = plugin->gen.slave;
504 
505     return snd_pcm_wait_nocheck(spcm, timeout);
506 }
507 
508 const snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops = {
509     .state = snd_pcm_generic_state,
510     .hwsync = snd_pcm_generic_hwsync,
511     .delay = snd_pcm_plugin_delay,
512     .prepare = snd_pcm_plugin_prepare,
513     .reset = snd_pcm_plugin_reset,
514     .start = snd_pcm_generic_start,
515     .drop = snd_pcm_generic_drop,
516     .drain = snd_pcm_generic_drain,
517     .pause = snd_pcm_generic_pause,
518     //.rewindable = snd_pcm_plugin_rewindable,
519     //.rewind = snd_pcm_plugin_rewind,
520     //.forwardable = snd_pcm_plugin_forwardable,
521     //.forward = snd_pcm_plugin_forward,
522     .resume = snd_pcm_generic_resume,
523     //.link = snd_pcm_generic_link,
524     //.link_slaves = snd_pcm_generic_link_slaves,
525     //.unlink = snd_pcm_generic_unlink,
526     .writei = snd_pcm_plugin_writei,
527     //.writen = snd_pcm_plugin_writen,
528     .readi = snd_pcm_plugin_readi,
529     //.readn = snd_pcm_plugin_readn,
530     .avail_update = snd_pcm_plugin_avail_update,
531     .mmap_commit = snd_pcm_plugin_mmap_commit,
532     //.htimestamp = snd_pcm_generic_htimestamp,
533     //.poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
534     //.poll_descriptors = snd_pcm_generic_poll_descriptors,
535     //.poll_revents = snd_pcm_generic_poll_revents,
536     .may_wait_for_avail_min = snd_pcm_plugin_may_wait_for_avail_min,
537     .wait = snd_pcm_plugin_wait,
538 };
539