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