4ddf10c1989e4918325472976b85179fd503d7f9
[platform/upstream/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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 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 <sys/shm.h>
86 #include <limits.h>
87 #include "pcm_local.h"
88 #include "pcm_plugin.h"
89
90 #ifndef DOC_HIDDEN
91
92 static snd_pcm_sframes_t
93 snd_pcm_plugin_undo_read(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
94                          const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
95                          snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
96                          snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
97                          snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
98 {
99         return -EIO;
100 }
101
102 static snd_pcm_sframes_t
103 snd_pcm_plugin_undo_write(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
104                           const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
105                           snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
106                           snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
107                           snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
108 {
109         return -EIO;
110 }
111
112 snd_pcm_sframes_t
113 snd_pcm_plugin_undo_read_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
114                                  const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
115                                  snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
116                                  snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
117                                  snd_pcm_uframes_t slave_undo_size)
118 {
119         return slave_undo_size;
120 }
121
122 snd_pcm_sframes_t
123 snd_pcm_plugin_undo_write_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
124                                   const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
125                                   snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
126                                   snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
127                                   snd_pcm_uframes_t slave_undo_size)
128 {
129         return slave_undo_size;
130 }
131
132 void snd_pcm_plugin_init(snd_pcm_plugin_t *plugin)
133 {
134         memset(plugin, 0, sizeof(snd_pcm_plugin_t));
135         plugin->undo_read = snd_pcm_plugin_undo_read;
136         plugin->undo_write = snd_pcm_plugin_undo_write;
137         snd_atomic_write_init(&plugin->watom);
138 }
139
140 static int snd_pcm_plugin_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
141 {
142         snd_pcm_plugin_t *plugin = pcm->private_data;
143         snd_pcm_sframes_t sd;
144         int err = snd_pcm_delay(plugin->gen.slave, &sd);
145         if (err < 0)
146                 return err;
147         if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
148             pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
149             pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
150                 sd += snd_pcm_mmap_capture_avail(pcm);
151         }        
152
153         *delayp = sd;
154         return 0;
155 }
156
157 static int snd_pcm_plugin_prepare(snd_pcm_t *pcm)
158 {
159         snd_pcm_plugin_t *plugin = pcm->private_data;
160         int err;
161         snd_atomic_write_begin(&plugin->watom);
162         err = snd_pcm_prepare(plugin->gen.slave);
163         if (err < 0) {
164                 snd_atomic_write_end(&plugin->watom);
165                 return err;
166         }
167         *pcm->hw.ptr = 0;
168         *pcm->appl.ptr = 0;
169         snd_atomic_write_end(&plugin->watom);
170         if (plugin->init) {
171                 err = plugin->init(pcm);
172                 if (err < 0)
173                         return err;
174         }
175         return 0;
176 }
177
178 static int snd_pcm_plugin_reset(snd_pcm_t *pcm)
179 {
180         snd_pcm_plugin_t *plugin = pcm->private_data;
181         int err;
182         snd_atomic_write_begin(&plugin->watom);
183         err = snd_pcm_reset(plugin->gen.slave);
184         if (err < 0) {
185                 snd_atomic_write_end(&plugin->watom);
186                 return err;
187         }
188         *pcm->hw.ptr = 0;
189         *pcm->appl.ptr = 0;
190         snd_atomic_write_end(&plugin->watom);
191         if (plugin->init) {
192                 err = plugin->init(pcm);
193                 if (err < 0)
194                         return err;
195         }
196         return 0;
197 }
198
199 static snd_pcm_sframes_t snd_pcm_plugin_rewindable(snd_pcm_t *pcm)
200 {
201         return snd_pcm_mmap_hw_avail(pcm);
202 }
203
204 snd_pcm_sframes_t snd_pcm_plugin_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
205 {
206         snd_pcm_plugin_t *plugin = pcm->private_data;
207         snd_pcm_sframes_t n = snd_pcm_mmap_hw_avail(pcm);
208         snd_pcm_sframes_t sframes;
209
210         if ((snd_pcm_uframes_t)n < frames)
211                 frames = n;
212         if (frames == 0)
213                 return 0;
214         
215         sframes = frames;
216         snd_atomic_write_begin(&plugin->watom);
217         sframes = snd_pcm_rewind(plugin->gen.slave, sframes);
218         if (sframes < 0) {
219                 snd_atomic_write_end(&plugin->watom);
220                 return sframes;
221         }
222         snd_pcm_mmap_appl_backward(pcm, (snd_pcm_uframes_t) sframes);
223         snd_atomic_write_end(&plugin->watom);
224         return (snd_pcm_sframes_t) sframes;
225 }
226
227 static snd_pcm_sframes_t snd_pcm_plugin_forwardable(snd_pcm_t *pcm)
228 {
229         return snd_pcm_mmap_avail(pcm);
230 }
231
232 snd_pcm_sframes_t snd_pcm_plugin_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
233 {
234         snd_pcm_plugin_t *plugin = pcm->private_data;
235         snd_pcm_sframes_t n = snd_pcm_mmap_avail(pcm);
236         snd_pcm_sframes_t sframes;
237
238         if ((snd_pcm_uframes_t)n < frames)
239                 frames = n;
240         if (frames == 0)
241                 return 0;
242         
243         sframes = frames;
244         snd_atomic_write_begin(&plugin->watom);
245         sframes = INTERNAL(snd_pcm_forward)(plugin->gen.slave, sframes);
246         if (sframes < 0) {
247                 snd_atomic_write_end(&plugin->watom);
248                 return sframes;
249         }
250         snd_pcm_mmap_appl_forward(pcm, (snd_pcm_uframes_t) frames);
251         snd_atomic_write_end(&plugin->watom);
252         return (snd_pcm_sframes_t) frames;
253 }
254
255 static snd_pcm_sframes_t snd_pcm_plugin_write_areas(snd_pcm_t *pcm,
256                                                     const snd_pcm_channel_area_t *areas,
257                                                     snd_pcm_uframes_t offset,
258                                                     snd_pcm_uframes_t size)
259 {
260         snd_pcm_plugin_t *plugin = pcm->private_data;
261         snd_pcm_t *slave = plugin->gen.slave;
262         snd_pcm_uframes_t xfer = 0;
263         snd_pcm_sframes_t result;
264         int err;
265
266         while (size > 0) {
267                 snd_pcm_uframes_t frames = size;
268                 const snd_pcm_channel_area_t *slave_areas;
269                 snd_pcm_uframes_t slave_offset;
270                 snd_pcm_uframes_t slave_frames = ULONG_MAX;
271                 
272                 err = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
273                 if (err < 0 || slave_frames == 0)
274                         break;
275                 frames = plugin->write(pcm, areas, offset, frames,
276                                        slave_areas, slave_offset, &slave_frames);
277                 if (CHECK_SANITY(slave_frames > snd_pcm_mmap_playback_avail(slave))) {
278                         SNDMSG("write overflow %ld > %ld", slave_frames,
279                                snd_pcm_mmap_playback_avail(slave));
280                         return -EPIPE;
281                 }
282                 snd_atomic_write_begin(&plugin->watom);
283                 snd_pcm_mmap_appl_forward(pcm, frames);
284                 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
285                 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
286                         snd_pcm_sframes_t res;
287                         res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
288                         if (res < 0)
289                                 return xfer > 0 ? (snd_pcm_sframes_t)xfer : res;
290                         frames -= res;
291                 }
292                 snd_atomic_write_end(&plugin->watom);
293                 if (result <= 0)
294                         return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
295                 offset += frames;
296                 xfer += frames;
297                 size -= frames;
298         }
299         return (snd_pcm_sframes_t)xfer;
300 }
301
302 static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm,
303                                                    const snd_pcm_channel_area_t *areas,
304                                                    snd_pcm_uframes_t offset,
305                                                    snd_pcm_uframes_t size)
306 {
307         snd_pcm_plugin_t *plugin = pcm->private_data;
308         snd_pcm_t *slave = plugin->gen.slave;
309         snd_pcm_uframes_t xfer = 0;
310         snd_pcm_sframes_t result;
311         
312         while (size > 0) {
313                 snd_pcm_uframes_t frames = size;
314                 const snd_pcm_channel_area_t *slave_areas;
315                 snd_pcm_uframes_t slave_offset;
316                 snd_pcm_uframes_t slave_frames = ULONG_MAX;
317                 
318                 snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
319                 if (slave_frames == 0)
320                         break;
321                 frames = (plugin->read)(pcm, areas, offset, frames,
322                                       slave_areas, slave_offset, &slave_frames);
323                 if (CHECK_SANITY(slave_frames > snd_pcm_mmap_capture_avail(slave))) {
324                         SNDMSG("read overflow %ld > %ld", slave_frames,
325                                snd_pcm_mmap_playback_avail(slave));
326                         return -EPIPE;
327                 }
328                 snd_atomic_write_begin(&plugin->watom);
329                 snd_pcm_mmap_appl_forward(pcm, frames);
330                 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
331                 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
332                         snd_pcm_sframes_t res;
333                         
334                         res = plugin->undo_read(slave, areas, offset, frames, slave_frames - result);
335                         if (res < 0)
336                                 return xfer > 0 ? (snd_pcm_sframes_t)xfer : res;
337                         frames -= res;
338                 }
339                 snd_atomic_write_end(&plugin->watom);
340                 if (result <= 0)
341                         return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
342                 offset += frames;
343                 xfer += frames;
344                 size -= frames;
345         }
346         return (snd_pcm_sframes_t)xfer;
347 }
348
349
350 static snd_pcm_sframes_t
351 snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
352 {
353         snd_pcm_channel_area_t areas[pcm->channels];
354         snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
355         return snd_pcm_write_areas(pcm, areas, 0, size, 
356                                    snd_pcm_plugin_write_areas);
357 }
358
359 static snd_pcm_sframes_t
360 snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
361 {
362         snd_pcm_channel_area_t areas[pcm->channels];
363         snd_pcm_areas_from_bufs(pcm, areas, bufs);
364         return snd_pcm_write_areas(pcm, areas, 0, size,
365                                    snd_pcm_plugin_write_areas);
366 }
367
368 static snd_pcm_sframes_t
369 snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
370 {
371         snd_pcm_channel_area_t areas[pcm->channels];
372         snd_pcm_areas_from_buf(pcm, areas, buffer);
373         return snd_pcm_read_areas(pcm, areas, 0, size,
374                                   snd_pcm_plugin_read_areas);
375 }
376
377 static snd_pcm_sframes_t
378 snd_pcm_plugin_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
379 {
380         snd_pcm_channel_area_t areas[pcm->channels];
381         snd_pcm_areas_from_bufs(pcm, areas, bufs);
382         return snd_pcm_read_areas(pcm, areas, 0, size,
383                                   snd_pcm_plugin_read_areas);
384 }
385
386 static snd_pcm_sframes_t
387 snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm,
388                            snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
389                            snd_pcm_uframes_t size)
390 {
391         snd_pcm_plugin_t *plugin = pcm->private_data;
392         snd_pcm_t *slave = plugin->gen.slave;
393         const snd_pcm_channel_area_t *areas;
394         snd_pcm_uframes_t appl_offset;
395         snd_pcm_sframes_t slave_size;
396         snd_pcm_sframes_t xfer;
397
398         if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
399                 snd_atomic_write_begin(&plugin->watom);
400                 snd_pcm_mmap_appl_forward(pcm, size);
401                 snd_atomic_write_end(&plugin->watom);
402                 return size;
403         }
404         slave_size = snd_pcm_avail_update(slave);
405         if (slave_size < 0)
406                 return slave_size;
407         areas = snd_pcm_mmap_areas(pcm);
408         appl_offset = snd_pcm_mmap_offset(pcm);
409         xfer = 0;
410         while (size > 0 && slave_size > 0) {
411                 snd_pcm_uframes_t frames = size;
412                 snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
413                 const snd_pcm_channel_area_t *slave_areas;
414                 snd_pcm_uframes_t slave_offset;
415                 snd_pcm_uframes_t slave_frames = ULONG_MAX;
416                 snd_pcm_sframes_t result;
417                 int err;
418
419                 err = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
420                 if (err < 0)
421                         return xfer > 0 ? xfer : err;
422                 if (frames > cont)
423                         frames = cont;
424                 frames = plugin->write(pcm, areas, appl_offset, frames,
425                                        slave_areas, slave_offset, &slave_frames);
426                 snd_atomic_write_begin(&plugin->watom);
427                 snd_pcm_mmap_appl_forward(pcm, frames);
428                 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
429                 snd_atomic_write_end(&plugin->watom);
430                 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
431                         snd_pcm_sframes_t res;
432                         
433                         res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
434                         if (res < 0)
435                                 return xfer > 0 ? xfer : res;
436                         frames -= res;
437                 }
438                 if (result <= 0)
439                         return xfer > 0 ? xfer : result;
440                 if (frames == cont)
441                         appl_offset = 0;
442                 else
443                         appl_offset += result;
444                 size -= frames;
445                 slave_size -= frames;
446                 xfer += frames;
447         }
448         if (CHECK_SANITY(size)) {
449                 SNDMSG("short commit: %ld", size);
450                 return -EPIPE;
451         }
452         return xfer;
453 }
454
455 static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm)
456 {
457         snd_pcm_plugin_t *plugin = pcm->private_data;
458         snd_pcm_t *slave = plugin->gen.slave;
459         snd_pcm_sframes_t slave_size;
460
461         slave_size = snd_pcm_avail_update(slave);
462         if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
463             pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
464             pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED)
465                 goto _capture;
466         *pcm->hw.ptr = *slave->hw.ptr;
467         return slave_size;
468  _capture:
469         {
470                 const snd_pcm_channel_area_t *areas;
471                 snd_pcm_uframes_t xfer, hw_offset, size;
472                 
473                 xfer = snd_pcm_mmap_capture_avail(pcm);
474                 size = pcm->buffer_size - xfer;
475                 areas = snd_pcm_mmap_areas(pcm);
476                 hw_offset = snd_pcm_mmap_hw_offset(pcm);
477                 while (size > 0 && slave_size > 0) {
478                         snd_pcm_uframes_t frames = size;
479                         snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
480                         const snd_pcm_channel_area_t *slave_areas;
481                         snd_pcm_uframes_t slave_offset;
482                         snd_pcm_uframes_t slave_frames = ULONG_MAX;
483                         snd_pcm_sframes_t result;
484                         int err;
485
486                         err = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
487                         if (err < 0)
488                                 return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
489                         if (frames > cont)
490                                 frames = cont;
491                         frames = (plugin->read)(pcm, areas, hw_offset, frames,
492                                               slave_areas, slave_offset, &slave_frames);
493                         snd_atomic_write_begin(&plugin->watom);
494                         snd_pcm_mmap_hw_forward(pcm, frames);
495                         result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
496                         snd_atomic_write_end(&plugin->watom);
497                         if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
498                                 snd_pcm_sframes_t res;
499                                 
500                                 res = plugin->undo_read(slave, areas, hw_offset, frames, slave_frames - result);
501                                 if (res < 0)
502                                         return xfer > 0 ? (snd_pcm_sframes_t)xfer : res;
503                                 frames -= res;
504                         }
505                         if (result <= 0)
506                                 return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
507                         if (frames == cont)
508                                 hw_offset = 0;
509                         else
510                                 hw_offset += frames;
511                         size -= frames;
512                         slave_size -= slave_frames;
513                         xfer += frames;
514                 }
515                 return (snd_pcm_sframes_t)xfer;
516         }
517 }
518
519 static int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
520 {
521         snd_pcm_plugin_t *plugin = pcm->private_data;
522         snd_pcm_sframes_t err;
523         snd_atomic_read_t ratom;
524         snd_atomic_read_init(&ratom, &plugin->watom);
525  _again:
526         snd_atomic_read_begin(&ratom);
527         /* sync with the latest hw and appl ptrs */
528         snd_pcm_plugin_avail_update(pcm);
529
530         err = snd_pcm_status(plugin->gen.slave, status);
531         if (err < 0) {
532                 snd_atomic_read_ok(&ratom);
533                 return err;
534         }
535         status->appl_ptr = *pcm->appl.ptr;
536         status->hw_ptr = *pcm->hw.ptr;
537         if (!snd_atomic_read_ok(&ratom)) {
538                 snd_atomic_read_wait(&ratom);
539                 goto _again;
540         }
541         return 0;
542 }
543
544 const snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops = {
545         .status = snd_pcm_plugin_status,
546         .state = snd_pcm_generic_state,
547         .hwsync = snd_pcm_generic_hwsync,
548         .delay = snd_pcm_plugin_delay,
549         .prepare = snd_pcm_plugin_prepare,
550         .reset = snd_pcm_plugin_reset,
551         .start = snd_pcm_generic_start,
552         .drop = snd_pcm_generic_drop,
553         .drain = snd_pcm_generic_drain,
554         .pause = snd_pcm_generic_pause,
555         .rewindable = snd_pcm_plugin_rewindable,
556         .rewind = snd_pcm_plugin_rewind,
557         .forwardable = snd_pcm_plugin_forwardable,
558         .forward = snd_pcm_plugin_forward,
559         .resume = snd_pcm_generic_resume,
560         .link = snd_pcm_generic_link,
561         .link_slaves = snd_pcm_generic_link_slaves,
562         .unlink = snd_pcm_generic_unlink,
563         .writei = snd_pcm_plugin_writei,
564         .writen = snd_pcm_plugin_writen,
565         .readi = snd_pcm_plugin_readi,
566         .readn = snd_pcm_plugin_readn,
567         .avail_update = snd_pcm_plugin_avail_update,
568         .mmap_commit = snd_pcm_plugin_mmap_commit,
569         .htimestamp = snd_pcm_generic_htimestamp,
570         .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
571         .poll_descriptors = snd_pcm_generic_poll_descriptors,
572         .poll_revents = snd_pcm_generic_poll_revents,
573         .may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min,
574 };
575
576 #endif