]> git.alsa-project.org Git - alsa-lib.git/blob - src/pcm/pcm_ioplug.c
aff4213d2ded9cb51f3a43d95b92ec9b088caa17
[alsa-lib.git] / src / pcm / pcm_ioplug.c
1 /**
2  * \file pcm/pcm_ioplug.c
3  * \ingroup Plugin_SDK
4  * \brief I/O Plugin SDK
5  * \author Takashi Iwai <tiwai@suse.de>
6  * \date 2005
7  */
8 /*
9  *  PCM - External I/O Plugin SDK
10  *  Copyright (c) 2005 by Takashi Iwai <tiwai@suse.de>
11  *
12  *
13  *   This library is free software; you can redistribute it and/or modify
14  *   it under the terms of the GNU Lesser General Public License as
15  *   published by the Free Software Foundation; either version 2.1 of
16  *   the License, or (at your option) any later version.
17  *
18  *   This program is distributed in the hope that it will be useful,
19  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *   GNU Lesser General Public License for more details.
22  *
23  *   You should have received a copy of the GNU Lesser General Public
24  *   License along with this library; if not, write to the Free Software
25  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26  *
27  */
28
29 #include "pcm_local.h"
30 #include "pcm_ioplug.h"
31 #include "pcm_ext_parm.h"
32 #include "pcm_generic.h"
33
34 #ifndef PIC
35 /* entry for static linking */
36 const char *_snd_module_pcm_ioplug = "";
37 #endif
38
39 #ifndef DOC_HIDDEN
40
41 /* hw_params */
42 typedef struct snd_pcm_ioplug_priv {
43         snd_pcm_ioplug_t *data;
44         struct snd_ext_parm params[SND_PCM_IOPLUG_HW_PARAMS];
45         snd_pcm_uframes_t last_hw;
46         snd_pcm_uframes_t avail_max;
47         snd_htimestamp_t trigger_tstamp;
48 } ioplug_priv_t;
49
50 static int snd_pcm_ioplug_drop(snd_pcm_t *pcm);
51 static int snd_pcm_ioplug_poll_descriptors_count(snd_pcm_t *pcm);
52 static int snd_pcm_ioplug_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
53 static int snd_pcm_ioplug_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
54
55 /* update the hw pointer */
56 /* called in lock */
57 static void snd_pcm_ioplug_hw_ptr_update(snd_pcm_t *pcm)
58 {
59         ioplug_priv_t *io = pcm->private_data;
60         snd_pcm_sframes_t hw;
61
62         hw = io->data->callback->pointer(io->data);
63         if (hw >= 0) {
64                 snd_pcm_uframes_t delta;
65                 snd_pcm_uframes_t avail;
66
67                 if ((snd_pcm_uframes_t)hw >= io->last_hw)
68                         delta = hw - io->last_hw;
69                 else {
70                         const snd_pcm_uframes_t wrap_point =
71                                 (io->data->flags & SND_PCM_IOPLUG_FLAG_BOUNDARY_WA) ?
72                                         pcm->boundary : pcm->buffer_size;
73                         delta = wrap_point + hw - io->last_hw;
74                 }
75                 snd_pcm_mmap_hw_forward(io->data->pcm, delta);
76                 /* stop the stream if all samples are drained */
77                 if (io->data->state == SND_PCM_STATE_DRAINING) {
78                         avail = snd_pcm_mmap_avail(pcm);
79                         if (avail >= pcm->buffer_size)
80                                 snd_pcm_ioplug_drop(pcm);
81                 }
82                 io->last_hw = (snd_pcm_uframes_t)hw;
83         } else {
84                 if (io->data->state == SND_PCM_STATE_DRAINING)
85                         snd_pcm_ioplug_drop(pcm);
86                 else
87                         io->data->state = SNDRV_PCM_STATE_XRUN;
88         }
89 }
90
91 static int snd_pcm_ioplug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
92 {
93         memset(info, 0, sizeof(*info));
94         info->stream = pcm->stream;
95         info->card = -1;
96         if (pcm->name) {
97                 snd_strlcpy((char *)info->id, pcm->name, sizeof(info->id));
98                 snd_strlcpy((char *)info->name, pcm->name, sizeof(info->name));
99                 snd_strlcpy((char *)info->subname, pcm->name, sizeof(info->subname));
100         }
101         info->subdevices_count = 1;
102         return 0;
103 }
104
105 static int snd_pcm_ioplug_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
106 {
107         return snd_pcm_channel_info_shm(pcm, info, -1);
108 }
109
110 static int snd_pcm_ioplug_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
111 {
112         ioplug_priv_t *io = pcm->private_data;
113
114         if (io->data->version >= 0x010001 &&
115             io->data->callback->delay)
116                 return io->data->callback->delay(io->data, delayp);
117         else {
118                 snd_pcm_ioplug_hw_ptr_update(pcm);
119                 *delayp = snd_pcm_mmap_delay(pcm);
120         }
121         return 0;
122 }
123
124 static int snd_pcm_ioplug_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
125 {
126         ioplug_priv_t *io = pcm->private_data;
127         snd_pcm_sframes_t sd;
128
129         memset(status, 0, sizeof(*status));
130         snd_pcm_ioplug_hw_ptr_update(pcm);
131         status->state = io->data->state;
132         status->trigger_tstamp = io->trigger_tstamp;
133         gettimestamp(&status->tstamp, pcm->tstamp_type);
134         status->avail = snd_pcm_mmap_avail(pcm);
135         status->avail_max = io->avail_max;
136         status->appl_ptr = *pcm->appl.ptr;
137         status->hw_ptr = *pcm->hw.ptr;
138         if (snd_pcm_ioplug_delay(pcm, &sd) < 0)
139                 sd = snd_pcm_mmap_delay(pcm);
140         status->delay = sd;
141         return 0;
142 }
143
144 static snd_pcm_state_t snd_pcm_ioplug_state(snd_pcm_t *pcm)
145 {
146         ioplug_priv_t *io = pcm->private_data;
147         return io->data->state;
148 }
149
150 static int snd_pcm_ioplug_hwsync(snd_pcm_t *pcm)
151 {
152         snd_pcm_ioplug_hw_ptr_update(pcm);
153         return 0;
154 }
155
156 static int snd_pcm_ioplug_reset(snd_pcm_t *pcm)
157 {
158         ioplug_priv_t *io = pcm->private_data;
159
160         io->data->appl_ptr = 0;
161         io->data->hw_ptr = 0;
162         io->last_hw = 0;
163         io->avail_max = 0;
164         return 0;
165 }
166
167 static int snd_pcm_ioplug_prepare(snd_pcm_t *pcm)
168 {
169         ioplug_priv_t *io = pcm->private_data;
170         int err = 0;
171
172         snd_pcm_ioplug_reset(pcm);
173         if (io->data->callback->prepare) {
174                 snd_pcm_unlock(pcm); /* to avoid deadlock */
175                 err = io->data->callback->prepare(io->data);
176                 snd_pcm_lock(pcm);
177         }
178         if (err < 0)
179                 return err;
180
181         io->data->state = SND_PCM_STATE_PREPARED;
182         return err;
183 }
184
185 static const int hw_params_type[SND_PCM_IOPLUG_HW_PARAMS] = {
186         [SND_PCM_IOPLUG_HW_ACCESS] = SND_PCM_HW_PARAM_ACCESS,
187         [SND_PCM_IOPLUG_HW_FORMAT] = SND_PCM_HW_PARAM_FORMAT,
188         [SND_PCM_IOPLUG_HW_CHANNELS] = SND_PCM_HW_PARAM_CHANNELS,
189         [SND_PCM_IOPLUG_HW_RATE] = SND_PCM_HW_PARAM_RATE,
190         [SND_PCM_IOPLUG_HW_PERIOD_BYTES] = SND_PCM_HW_PARAM_PERIOD_BYTES,
191         [SND_PCM_IOPLUG_HW_BUFFER_BYTES] = SND_PCM_HW_PARAM_BUFFER_BYTES,
192         [SND_PCM_IOPLUG_HW_PERIODS] = SND_PCM_HW_PARAM_PERIODS,
193 };
194
195 /* x = a * b */
196 static int rule_mul(snd_pcm_hw_params_t *params, int x, int a, int b)
197 {
198         snd_interval_t t;
199
200         snd_interval_mul(hw_param_interval(params, a),
201                          hw_param_interval(params, b), &t);
202         return snd_interval_refine(hw_param_interval(params, x), &t);
203 }
204
205 /* x = a / b */
206 static int rule_div(snd_pcm_hw_params_t *params, int x, int a, int b)
207 {
208         snd_interval_t t;
209
210         snd_interval_div(hw_param_interval(params, a),
211                          hw_param_interval(params, b), &t);
212         return snd_interval_refine(hw_param_interval(params, x), &t);
213 }
214
215 /* x = a * b / k */
216 static int rule_muldivk(snd_pcm_hw_params_t *params, int x, int a, int b, int k)
217 {
218         snd_interval_t t;
219
220         snd_interval_muldivk(hw_param_interval(params, a),
221                              hw_param_interval(params, b), k, &t);
222         return snd_interval_refine(hw_param_interval(params, x), &t);
223 }
224
225 /* x = a * k / b */
226 static int rule_mulkdiv(snd_pcm_hw_params_t *params, int x, int a, int k, int b)
227 {
228         snd_interval_t t;
229
230         snd_interval_mulkdiv(hw_param_interval(params, a), k,
231                              hw_param_interval(params, b), &t);
232         return snd_interval_refine(hw_param_interval(params, x), &t);
233 }
234
235 #if 0
236 static void dump_parm(snd_pcm_hw_params_t *params)
237 {
238         snd_output_t *log;
239         snd_output_stdio_attach(&log, stderr, 0);
240         snd_pcm_hw_params_dump(params, log);
241         snd_output_close(log);
242 }
243 #endif
244
245 /* refine *_TIME and *_SIZE, then update *_BYTES */
246 static int refine_time_and_size(snd_pcm_hw_params_t *params,
247                                 int time, int size, int bytes)
248 {
249         int err, change1 = 0;
250
251         /* size = time * rate / 1000000 */
252         err = rule_muldivk(params, size, time,
253                            SND_PCM_HW_PARAM_RATE, 1000000);
254         if (err < 0)
255                 return err;
256         change1 |= err;
257
258         /* bytes = size * framebits / 8 */
259         err = rule_muldivk(params, bytes, size,
260                            SND_PCM_HW_PARAM_FRAME_BITS, 8);
261         if (err < 0)
262                 return err;
263         change1 |= err;
264         return change1;
265 }
266
267 /* refine *_TIME and *_SIZE from *_BYTES */
268 static int refine_back_time_and_size(snd_pcm_hw_params_t *params,
269                                      int time, int size, int bytes)
270 {
271         int err;
272
273         /* size = bytes * 8 / framebits */
274         err = rule_mulkdiv(params, size, bytes, 8, SND_PCM_HW_PARAM_FRAME_BITS);
275         if (err < 0)
276                 return err;
277         /* time = size * 1000000 / rate */
278         err = rule_mulkdiv(params, time, size, 1000000, SND_PCM_HW_PARAM_RATE);
279         if (err < 0)
280                 return err;
281         return 0;
282 }
283
284
285 static int snd_pcm_ioplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
286 {
287         int change = 0, change1, change2, err;
288         ioplug_priv_t *io = pcm->private_data;
289         struct snd_ext_parm *p;
290         unsigned int i;
291
292         /* access, format */
293         for (i = SND_PCM_IOPLUG_HW_ACCESS; i <= SND_PCM_IOPLUG_HW_FORMAT; i++) {
294                 err = snd_ext_parm_mask_refine(hw_param_mask(params, hw_params_type[i]),
295                                                io->params, i);
296                 if (err < 0)
297                         return err;
298                 change |= err;
299         }
300         /* channels, rate */
301         for (; i <= SND_PCM_IOPLUG_HW_RATE; i++) {
302                 err = snd_ext_parm_interval_refine(hw_param_interval(params, hw_params_type[i]),
303                                                    io->params, i);
304                 if (err < 0)
305                         return err;
306                 change |= err;
307         }
308
309         if (params->rmask & ((1 << SND_PCM_HW_PARAM_ACCESS) |
310                              (1 << SND_PCM_HW_PARAM_FORMAT) |
311                              (1 << SND_PCM_HW_PARAM_SUBFORMAT) |
312                              (1 << SND_PCM_HW_PARAM_CHANNELS) |
313                              (1 << SND_PCM_HW_PARAM_RATE))) {
314                 err = snd_pcm_hw_refine_soft(pcm, params);
315                 if (err < 0)
316                         return err;
317                 change |= err;
318         }
319
320         change1 = refine_time_and_size(params, SND_PCM_HW_PARAM_PERIOD_TIME,
321                                        SND_PCM_HW_PARAM_PERIOD_SIZE,
322                                        SND_PCM_HW_PARAM_PERIOD_BYTES);
323         if (change1 < 0)
324                 return change1;
325         err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_BYTES),
326                                            io->params, SND_PCM_IOPLUG_HW_PERIOD_BYTES);
327         if (err < 0)
328                 return err;
329         change1 |= err;
330         if (change1) {
331                 change |= change1;
332                 err = refine_back_time_and_size(params, SND_PCM_HW_PARAM_PERIOD_TIME,
333                                                 SND_PCM_HW_PARAM_PERIOD_SIZE,
334                                                 SND_PCM_HW_PARAM_PERIOD_BYTES);
335                 if (err < 0)
336                         return err;
337         }
338
339         change1 = refine_time_and_size(params, SND_PCM_HW_PARAM_BUFFER_TIME,
340                                        SND_PCM_HW_PARAM_BUFFER_SIZE,
341                                        SND_PCM_HW_PARAM_BUFFER_BYTES);
342         if (change1 < 0)
343                 return change1;
344         change |= change1;
345
346         do {
347                 change2 = 0;
348                 err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_BUFFER_BYTES),
349                                                    io->params, SND_PCM_IOPLUG_HW_BUFFER_BYTES);
350                 if (err < 0)
351                         return err;
352                 change2 |= err;
353                 /* periods = buffer_bytes / period_bytes */
354                 err = rule_div(params, SND_PCM_HW_PARAM_PERIODS,
355                                SND_PCM_HW_PARAM_BUFFER_BYTES,
356                                SND_PCM_HW_PARAM_PERIOD_BYTES);
357                 if (err < 0)
358                         return err;
359                 change2 |= err;
360                 err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIODS),
361                                                    io->params, SND_PCM_IOPLUG_HW_PERIODS);
362                 if (err < 0)
363                         return err;
364                 change2 |= err;
365                 /* buffer_bytes = periods * period_bytes */
366                 err = rule_mul(params, SND_PCM_HW_PARAM_BUFFER_BYTES,
367                                SND_PCM_HW_PARAM_PERIOD_BYTES,
368                                SND_PCM_HW_PARAM_PERIODS);
369                 if (err < 0)
370                         return err;
371                 change2 |= err;
372                 change1 |= change2;
373         } while (change2);
374         change |= change1;
375
376         if (change1) {
377                 err = refine_back_time_and_size(params, SND_PCM_HW_PARAM_BUFFER_TIME,
378                                                 SND_PCM_HW_PARAM_BUFFER_SIZE,
379                                                 SND_PCM_HW_PARAM_BUFFER_BYTES);
380                 if (err < 0)
381                         return err;
382         }
383
384         /* period_bytes = buffer_bytes / periods */
385         err = rule_div(params, SND_PCM_HW_PARAM_PERIOD_BYTES,
386                        SND_PCM_HW_PARAM_BUFFER_BYTES,
387                        SND_PCM_HW_PARAM_PERIODS);
388         if (err < 0)
389                 return err;
390         if (err) {
391                 /* update period_size and period_time */
392                 change |= err;
393                 err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_BYTES),
394                                                    io->params, SND_PCM_IOPLUG_HW_PERIOD_BYTES);
395                 if (err < 0)
396                         return err;
397                 err = refine_back_time_and_size(params, SND_PCM_HW_PARAM_PERIOD_TIME,
398                                                 SND_PCM_HW_PARAM_PERIOD_SIZE,
399                                                 SND_PCM_HW_PARAM_PERIOD_BYTES);
400                 if (err < 0)
401                         return err;
402         }
403
404         params->info = SND_PCM_INFO_BLOCK_TRANSFER;
405         p = &io->params[SND_PCM_IOPLUG_HW_ACCESS];
406         if (p->active) {
407                 for (i = 0; i < p->num_list; i++)
408                         switch (p->list[i]) {
409                         case SND_PCM_ACCESS_MMAP_INTERLEAVED:
410                         case SND_PCM_ACCESS_RW_INTERLEAVED:
411                                 params->info |= SND_PCM_INFO_INTERLEAVED;
412                                 break;
413                         case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
414                         case SND_PCM_ACCESS_RW_NONINTERLEAVED:
415                                 params->info |= SND_PCM_INFO_NONINTERLEAVED;
416                                 break;
417                         }
418         }
419         if (io->data->callback->pause)
420                 params->info |= SND_PCM_INFO_PAUSE;
421         if (io->data->callback->resume)
422                 params->info |= SND_PCM_INFO_RESUME;
423
424 #if 0
425         fprintf(stderr, "XXX\n");
426         dump_parm(params);
427 #endif
428         return change;
429 }
430
431 static int snd_pcm_ioplug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
432 {
433         ioplug_priv_t *io = pcm->private_data;
434         int err;
435
436         INTERNAL(snd_pcm_hw_params_get_access)(params, &io->data->access);
437         INTERNAL(snd_pcm_hw_params_get_format)(params, &io->data->format);
438         INTERNAL(snd_pcm_hw_params_get_channels)(params, &io->data->channels);
439         INTERNAL(snd_pcm_hw_params_get_rate)(params, &io->data->rate, 0);
440         INTERNAL(snd_pcm_hw_params_get_period_size)(params, &io->data->period_size, 0);
441         INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &io->data->buffer_size);
442         if (io->data->callback->hw_params) {
443                 err = io->data->callback->hw_params(io->data, params);
444                 if (err < 0)
445                         return err;
446                 INTERNAL(snd_pcm_hw_params_get_access)(params, &io->data->access);
447                 INTERNAL(snd_pcm_hw_params_get_format)(params, &io->data->format);
448                 INTERNAL(snd_pcm_hw_params_get_channels)(params, &io->data->channels);
449                 INTERNAL(snd_pcm_hw_params_get_rate)(params, &io->data->rate, 0);
450                 INTERNAL(snd_pcm_hw_params_get_period_size)(params, &io->data->period_size, 0);
451                 INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &io->data->buffer_size);
452         }
453         return 0;
454 }
455
456 static int snd_pcm_ioplug_hw_free(snd_pcm_t *pcm)
457 {
458         ioplug_priv_t *io = pcm->private_data;
459
460         if (io->data->callback->hw_free)
461                 return io->data->callback->hw_free(io->data);
462         return 0;
463 }
464
465 static int snd_pcm_ioplug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
466 {
467         ioplug_priv_t *io = pcm->private_data;
468         int err;
469
470         if (!io->data->callback->sw_params)
471                 return 0;
472
473         snd_pcm_unlock(pcm); /* to avoid deadlock */
474         err = io->data->callback->sw_params(io->data, params);
475         snd_pcm_lock(pcm);
476
477         return err;
478 }
479
480
481 static int snd_pcm_ioplug_start(snd_pcm_t *pcm)
482 {
483         ioplug_priv_t *io = pcm->private_data;
484         int err;
485
486         if (io->data->state != SND_PCM_STATE_PREPARED)
487                 return -EBADFD;
488
489         err = io->data->callback->start(io->data);
490         if (err < 0)
491                 return err;
492
493         gettimestamp(&io->trigger_tstamp, pcm->tstamp_type);
494         io->data->state = SND_PCM_STATE_RUNNING;
495
496         return 0;
497 }
498
499 static int snd_pcm_ioplug_drop(snd_pcm_t *pcm)
500 {
501         ioplug_priv_t *io = pcm->private_data;
502
503         if (io->data->state == SND_PCM_STATE_OPEN)
504                 return -EBADFD;
505
506         io->data->callback->stop(io->data);
507
508         gettimestamp(&io->trigger_tstamp, pcm->tstamp_type);
509         io->data->state = SND_PCM_STATE_SETUP;
510
511         return 0;
512 }
513
514 static int ioplug_drain_via_poll(snd_pcm_t *pcm)
515 {
516         ioplug_priv_t *io = pcm->private_data;
517
518         while (io->data->state == SND_PCM_STATE_DRAINING) {
519                 snd_pcm_ioplug_hw_ptr_update(pcm);
520                 if (io->data->state != SND_PCM_STATE_DRAINING)
521                         break;
522                 /* in non-blocking mode, let application to poll() by itself */
523                 if (io->data->nonblock)
524                         return -EAGAIN;
525                 if (snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_DRAIN) < 0)
526                         break;
527         }
528
529         return 0; /* force to drop at error */
530 }
531
532 /* need own locking */
533 static int snd_pcm_ioplug_drain(snd_pcm_t *pcm)
534 {
535         ioplug_priv_t *io = pcm->private_data;
536         int err = 0;
537
538         snd_pcm_lock(pcm);
539         switch (io->data->state) {
540         case SND_PCM_STATE_OPEN:
541         case SND_PCM_STATE_DISCONNECTED:
542         case SND_PCM_STATE_SUSPENDED:
543                 snd_pcm_unlock(pcm);
544                 return -EBADFD;
545         case SND_PCM_STATE_PREPARED:
546                 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
547                         if (!io->data->callback->drain) {
548                                 err = snd_pcm_ioplug_start(pcm);
549                                 if (err < 0)
550                                         goto unlock;
551                         }
552                         io->data->state = SND_PCM_STATE_DRAINING;
553                 }
554                 break;
555         case SND_PCM_STATE_RUNNING:
556                 io->data->state = SND_PCM_STATE_DRAINING;
557                 break;
558         default:
559                 break;
560         }
561
562         if (io->data->state == SND_PCM_STATE_DRAINING) {
563                 if (io->data->callback->drain) {
564                         snd_pcm_unlock(pcm); /* let plugin own locking */
565                         err = io->data->callback->drain(io->data);
566                         snd_pcm_lock(pcm);
567                 } else {
568                         err = ioplug_drain_via_poll(pcm);
569                 }
570         }
571
572  unlock:
573         if (!err && io->data->state != SND_PCM_STATE_SETUP)
574                 snd_pcm_ioplug_drop(pcm);
575         snd_pcm_unlock(pcm);
576         return err;
577 }
578
579 static int snd_pcm_ioplug_pause(snd_pcm_t *pcm, int enable)
580 {
581         ioplug_priv_t *io = pcm->private_data;
582         static const snd_pcm_state_t states[2] = {
583                 SND_PCM_STATE_RUNNING, SND_PCM_STATE_PAUSED
584         };
585         int prev, err;
586
587         prev = !enable;
588         enable = !prev;
589         if (io->data->state != states[prev])
590                 return -EBADFD;
591         if (io->data->callback->pause) {
592                 err = io->data->callback->pause(io->data, enable);
593                 if (err < 0)
594                         return err;
595         }
596         io->data->state = states[enable];
597         return 0;
598 }
599
600 static snd_pcm_sframes_t snd_pcm_ioplug_rewindable(snd_pcm_t *pcm)
601 {
602         return snd_pcm_mmap_hw_rewindable(pcm);
603 }
604
605 static snd_pcm_sframes_t snd_pcm_ioplug_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
606 {
607         snd_pcm_mmap_appl_backward(pcm, frames);
608         return frames;
609 }
610
611 static snd_pcm_sframes_t snd_pcm_ioplug_forwardable(snd_pcm_t *pcm)
612 {
613         return snd_pcm_mmap_avail(pcm);
614 }
615
616 static snd_pcm_sframes_t snd_pcm_ioplug_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
617 {
618         snd_pcm_mmap_appl_forward(pcm, frames);
619         return frames;
620 }
621
622 /* need own locking */
623 static int snd_pcm_ioplug_resume(snd_pcm_t *pcm)
624 {
625         ioplug_priv_t *io = pcm->private_data;
626
627         if (io->data->callback->resume)
628                 io->data->callback->resume(io->data);
629         return 0;
630 }
631
632 /* called in lock */
633 static snd_pcm_sframes_t ioplug_priv_transfer_areas(snd_pcm_t *pcm,
634                                                        const snd_pcm_channel_area_t *areas,
635                                                        snd_pcm_uframes_t offset,
636                                                        snd_pcm_uframes_t size)
637 {
638         ioplug_priv_t *io = pcm->private_data;
639         snd_pcm_sframes_t result;
640
641         if (! size)
642                 return 0;
643         if (io->data->callback->transfer)
644                 result = io->data->callback->transfer(io->data, areas, offset, size);
645         else
646                 result = size;
647         if (result > 0)
648                 snd_pcm_mmap_appl_forward(pcm, result);
649         return result;
650 }
651
652 static snd_pcm_sframes_t snd_pcm_ioplug_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
653 {
654         if (pcm->mmap_rw)
655                 return snd_pcm_mmap_writei(pcm, buffer, size);
656         else {
657                 snd_pcm_channel_area_t areas[pcm->channels];
658                 snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
659                 return snd_pcm_write_areas(pcm, areas, 0, size,
660                                            ioplug_priv_transfer_areas);
661         }
662 }
663
664 static snd_pcm_sframes_t snd_pcm_ioplug_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
665 {
666         if (pcm->mmap_rw)
667                 return snd_pcm_mmap_writen(pcm, bufs, size);
668         else {
669                 snd_pcm_channel_area_t areas[pcm->channels];
670                 snd_pcm_areas_from_bufs(pcm, areas, bufs);
671                 return snd_pcm_write_areas(pcm, areas, 0, size,
672                                            ioplug_priv_transfer_areas);
673         }
674 }
675
676 static snd_pcm_sframes_t snd_pcm_ioplug_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
677 {
678         if (pcm->mmap_rw)
679                 return snd_pcm_mmap_readi(pcm, buffer, size);
680         else {
681                 snd_pcm_channel_area_t areas[pcm->channels];
682                 snd_pcm_areas_from_buf(pcm, areas, buffer);
683                 return snd_pcm_read_areas(pcm, areas, 0, size,
684                                           ioplug_priv_transfer_areas);
685         }
686 }
687
688 static snd_pcm_sframes_t snd_pcm_ioplug_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
689 {
690         if (pcm->mmap_rw)
691                 return snd_pcm_mmap_readn(pcm, bufs, size);
692         else {
693                 snd_pcm_channel_area_t areas[pcm->channels];
694                 snd_pcm_areas_from_bufs(pcm, areas, bufs);
695                 return snd_pcm_read_areas(pcm, areas, 0, size,
696                                           ioplug_priv_transfer_areas);
697         }
698 }
699
700 static int snd_pcm_ioplug_mmap_begin_capture(snd_pcm_t *pcm,
701                                              const snd_pcm_channel_area_t **areas,
702                                              snd_pcm_uframes_t *offset,
703                                              snd_pcm_uframes_t *frames)
704 {
705         ioplug_priv_t *io = pcm->private_data;
706         int err;
707
708         err = __snd_pcm_mmap_begin_generic(pcm, areas, offset, frames);
709         if (err < 0)
710                 return err;
711
712         if (io->data->callback->transfer &&
713             pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
714             pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
715                 snd_pcm_sframes_t result;
716                 result = io->data->callback->transfer(io->data, *areas, *offset, *frames);
717                 if (result < 0)
718                         return result;
719         }
720
721         return err;
722 }
723
724 static int snd_pcm_ioplug_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas,
725                                      snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames)
726 {
727         if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
728                 return __snd_pcm_mmap_begin_generic(pcm, areas, offset, frames);
729         return snd_pcm_ioplug_mmap_begin_capture(pcm, areas, offset, frames);
730 }
731
732 static snd_pcm_sframes_t snd_pcm_ioplug_mmap_commit(snd_pcm_t *pcm,
733                                                     snd_pcm_uframes_t offset,
734                                                     snd_pcm_uframes_t size)
735 {
736         if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
737             pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
738             pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
739                 const snd_pcm_channel_area_t *areas;
740                 snd_pcm_uframes_t ofs, frames = size;
741
742                 __snd_pcm_mmap_begin_generic(pcm, &areas, &ofs, &frames);
743                 if (ofs != offset)
744                         return -EIO;
745                 return ioplug_priv_transfer_areas(pcm, areas, offset, frames);
746         }
747
748         snd_pcm_mmap_appl_forward(pcm, size);
749         return size;
750 }
751
752 static snd_pcm_sframes_t snd_pcm_ioplug_avail_update(snd_pcm_t *pcm)
753 {
754         ioplug_priv_t *io = pcm->private_data;
755         snd_pcm_uframes_t avail;
756
757         snd_pcm_ioplug_hw_ptr_update(pcm);
758         if (io->data->state == SND_PCM_STATE_XRUN)
759                 return -EPIPE;
760
761         avail = snd_pcm_mmap_avail(pcm);
762         if (avail > io->avail_max)
763                 io->avail_max = avail;
764         return (snd_pcm_sframes_t)avail;
765 }
766
767 static int snd_pcm_ioplug_nonblock(snd_pcm_t *pcm, int nonblock)
768 {
769         ioplug_priv_t *io = pcm->private_data;
770
771         io->data->nonblock = nonblock;
772         return 0;
773 }
774
775 static int snd_pcm_ioplug_poll_descriptors_count(snd_pcm_t *pcm)
776 {
777         ioplug_priv_t *io = pcm->private_data;
778         int err = 1;
779
780         if (io->data->callback->poll_descriptors_count) {
781                 snd_pcm_unlock(pcm); /* to avoid deadlock */
782                 err = io->data->callback->poll_descriptors_count(io->data);
783                 snd_pcm_lock(pcm);
784         }
785         return err;
786 }
787
788 static int snd_pcm_ioplug_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
789 {
790         ioplug_priv_t *io = pcm->private_data;
791         int err;
792
793         if (io->data->callback->poll_descriptors) {
794                 snd_pcm_unlock(pcm); /* to avoid deadlock */
795                 err = io->data->callback->poll_descriptors(io->data, pfds, space);
796                 snd_pcm_lock(pcm);
797                 return err;
798         }
799         if (pcm->poll_fd < 0)
800                 return -EIO;
801         if (space >= 1 && pfds) {
802                 pfds->fd = pcm->poll_fd;
803                 pfds->events = pcm->poll_events | POLLERR | POLLNVAL;
804         } else {
805                 return 0;
806         }
807         return 1;
808 }
809
810 static int snd_pcm_ioplug_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
811 {
812         ioplug_priv_t *io = pcm->private_data;
813         int err;
814
815         if (io->data->callback->poll_revents) {
816                 snd_pcm_unlock(pcm); /* to avoid deadlock */
817                 err = io->data->callback->poll_revents(io->data, pfds, nfds, revents);
818                 snd_pcm_lock(pcm);
819         } else {
820                 *revents = pfds->revents;
821                 err = 0;
822         }
823         return err;
824 }
825
826 static int snd_pcm_ioplug_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
827 {
828         return 0;
829 }
830
831 static int snd_pcm_ioplug_async(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
832                                 int sig ATTRIBUTE_UNUSED,
833                                 pid_t pid ATTRIBUTE_UNUSED)
834 {
835         return -ENOSYS;
836 }
837
838 static int snd_pcm_ioplug_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
839 {
840         return 0;
841 }
842
843 static snd_pcm_chmap_query_t **snd_pcm_ioplug_query_chmaps(snd_pcm_t *pcm)
844 {
845         ioplug_priv_t *io = pcm->private_data;
846
847         if (io->data->version >= 0x010002 &&
848             io->data->callback->query_chmaps)
849                 return io->data->callback->query_chmaps(io->data);
850         return NULL;
851 }
852
853 static snd_pcm_chmap_t *snd_pcm_ioplug_get_chmap(snd_pcm_t *pcm)
854 {
855         ioplug_priv_t *io = pcm->private_data;
856
857         if (io->data->version >= 0x010002 &&
858             io->data->callback->get_chmap)
859                 return io->data->callback->get_chmap(io->data);
860         return NULL;
861 }
862
863 static int snd_pcm_ioplug_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
864 {
865         ioplug_priv_t *io = pcm->private_data;
866
867         if (io->data->version >= 0x010002 &&
868             io->data->callback->set_chmap)
869                 return io->data->callback->set_chmap(io->data, map);
870         return -ENXIO;
871 }
872
873 static void snd_pcm_ioplug_dump(snd_pcm_t *pcm, snd_output_t *out)
874 {
875         ioplug_priv_t *io = pcm->private_data;
876
877         if (io->data->callback->dump)
878                 io->data->callback->dump(io->data, out);
879         else {
880                 if (io->data->name)
881                         snd_output_printf(out, "%s\n", io->data->name);
882                 else
883                         snd_output_printf(out, "IO-PCM Plugin\n");
884                 if (pcm->setup) {
885                         snd_output_printf(out, "Its setup is:\n");
886                         snd_pcm_dump_setup(pcm, out);
887                 }
888         }
889 }
890
891 static void clear_io_params(ioplug_priv_t *io)
892 {
893         int i;
894         for (i = 0; i < SND_PCM_IOPLUG_HW_PARAMS; i++)
895                 snd_ext_parm_clear(&io->params[i]);
896 }
897
898 static int snd_pcm_ioplug_close(snd_pcm_t *pcm)
899 {
900         ioplug_priv_t *io = pcm->private_data;
901
902         clear_io_params(io);
903         if (io->data->callback->close)
904                 io->data->callback->close(io->data);
905         free(io);
906
907         return 0;
908 }
909
910 static const snd_pcm_ops_t snd_pcm_ioplug_ops = {
911         .close = snd_pcm_ioplug_close,
912         .nonblock = snd_pcm_ioplug_nonblock,
913         .async = snd_pcm_ioplug_async,
914         .info = snd_pcm_ioplug_info,
915         .hw_refine = snd_pcm_ioplug_hw_refine,
916         .hw_params = snd_pcm_ioplug_hw_params,
917         .hw_free = snd_pcm_ioplug_hw_free,
918         .sw_params = snd_pcm_ioplug_sw_params,
919         .channel_info = snd_pcm_ioplug_channel_info,
920         .dump = snd_pcm_ioplug_dump,
921         .mmap = snd_pcm_ioplug_mmap,
922         .munmap = snd_pcm_ioplug_munmap,
923         .query_chmaps = snd_pcm_ioplug_query_chmaps,
924         .get_chmap = snd_pcm_ioplug_get_chmap,
925         .set_chmap = snd_pcm_ioplug_set_chmap,
926 };
927
928 static const snd_pcm_fast_ops_t snd_pcm_ioplug_fast_ops = {
929         .status = snd_pcm_ioplug_status,
930         .prepare = snd_pcm_ioplug_prepare,
931         .reset = snd_pcm_ioplug_reset,
932         .start = snd_pcm_ioplug_start,
933         .drop = snd_pcm_ioplug_drop,
934         .drain = snd_pcm_ioplug_drain,
935         .pause = snd_pcm_ioplug_pause,
936         .state = snd_pcm_ioplug_state,
937         .hwsync = snd_pcm_ioplug_hwsync,
938         .delay = snd_pcm_ioplug_delay,
939         .resume = snd_pcm_ioplug_resume,
940         .link = NULL,
941         .link_slaves = NULL,
942         .unlink = NULL,
943         .rewindable = snd_pcm_ioplug_rewindable,
944         .rewind = snd_pcm_ioplug_rewind,
945         .forwardable = snd_pcm_ioplug_forwardable,
946         .forward = snd_pcm_ioplug_forward,
947         .writei = snd_pcm_ioplug_writei,
948         .writen = snd_pcm_ioplug_writen,
949         .readi = snd_pcm_ioplug_readi,
950         .readn = snd_pcm_ioplug_readn,
951         .avail_update = snd_pcm_ioplug_avail_update,
952         .mmap_commit = snd_pcm_ioplug_mmap_commit,
953         .htimestamp = snd_pcm_generic_real_htimestamp,
954         .poll_descriptors_count = snd_pcm_ioplug_poll_descriptors_count,
955         .poll_descriptors = snd_pcm_ioplug_poll_descriptors,
956         .poll_revents = snd_pcm_ioplug_poll_revents,
957         .mmap_begin = snd_pcm_ioplug_mmap_begin,
958 };
959
960 #endif /* !DOC_HIDDEN */
961
962 /*
963  * Exported functions
964  */
965
966 /*! \page pcm_external_plugins PCM External Plugin SDK
967
968 \section pcm_ioplug External Plugin: I/O Plugin
969
970 The I/O-type plugin is a PCM plugin to work as the input or output terminal point,
971 i.e. as a user-space PCM driver.
972
973 The new plugin is created via #snd_pcm_ioplug_create() function.
974 The first argument is a pointer of the pluging information.  Some of
975 this struct must be initialized in prior to call
976 #snd_pcm_ioplug_create().  Then the function fills other fields in
977 return.  The rest arguments, name, stream and mode, are usually
978 identical with the values passed from the ALSA plugin constructor.
979
980 The following fields are mandatory: version, name, callback.
981 Otherfields are optional and should be initialized with zero.
982
983 The constant #SND_PCM_IOPLUG_VERSION must be passed to the version
984 field for the version check in alsa-lib.  A non-NULL ASCII string
985 has to be passed to the name field.  The callback field contains the
986 table of callback functions for this plugin (defined as
987 #snd_pcm_ioplug_callback_t).
988
989 flags field specifies the optional bit-flags.  poll_fd and poll_events
990 specify the poll file descriptor and the corresponding poll events
991 (POLLIN, POLLOUT) for the plugin.  If the plugin requires multiple
992 poll descriptors or poll descriptor(s) dynamically varying, set
993 poll_descriptors and poll_descriptors_count callbacks to the callback
994 table.  Then the poll_fd and poll_events field are ignored.
995
996 mmap_rw specifies whether the plugin behaves in the pseudo mmap mode.
997 When this value is set to 1, the plugin creates always a local buffer
998 and performs read/write calls using this buffer as if it's mmapped.
999 The address of local buffer can be obtained via
1000 #snd_pcm_ioplug_mmap_areas() function.
1001 When poll_fd, poll_events and mmap_rw fields are changed after
1002 #snd_pcm_ioplug_create(), call #snd_pcm_ioplug_reinit_status() to
1003 reflect the changes.
1004
1005 The driver can set an arbitrary value (pointer) to private_data
1006 field to refer its own data in the callbacks.
1007
1008 The rest fields are filled by #snd_pcm_ioplug_create().  The pcm field
1009 is the resultant PCM handle.  The others are the current status of the
1010 PCM.
1011
1012 The callback functions in #snd_pcm_ioplug_callback_t define the real
1013 behavior of the driver.
1014 At least, start, stop and pointer callbacks must be given.  Other
1015 callbacks are optional.  The start and stop callbacks are called when
1016 the PCM stream is started and stopped, repsectively.  The pointer
1017 callback returns the current DMA position, which may be called at any
1018 time.
1019
1020 The transfer callback is called when any data transfer happens.  It
1021 receives the area array, offset and the size to transfer.  The area
1022 array contains the array of snd_pcm_channel_area_t with the elements
1023 of number of channels.
1024
1025 When the PCM is closed, close callback is called.  If the driver
1026 allocates any internal buffers, they should be released in this
1027 callback.  The hw_params and hw_free callbacks are called when
1028 hw_params are set and reset, respectively.  Note that they may be
1029 called multiple times according to the application.  Similarly,
1030 sw_params callback is called when sw_params is set or changed.
1031
1032 The prepare, drain, pause and resume callbacks are called when
1033 #snd_pcm_prepare(), #snd_pcm_drain(), #snd_pcm_pause(), and
1034 #snd_pcm_resume() are called.  The poll_descriptors_count and
1035 poll_descriptors callbacks are used to return the multiple or dynamic
1036 poll descriptors as mentioned above.  The poll_revents callback is
1037 used to modify poll events.  If the driver needs to mangle the native
1038 poll events to proper poll events for PCM, you can do it in this
1039 callback.
1040
1041 Finally, the dump callback is used to print the status of the plugin.
1042
1043 Note that some callbacks (start, stop, pointer, transfer and pause)
1044 may be called inside the internal pthread mutex, and they shouldn't
1045 call the PCM functions again unnecessarily from the callback itself;
1046 otherwise it may lead to a deadlock.
1047
1048 The hw_params constraints can be defined via either
1049 #snd_pcm_ioplug_set_param_minmax() and #snd_pcm_ioplug_set_param_list()
1050 functions after calling #snd_pcm_ioplug_create().
1051 The former defines the minimal and maximal acceptable values for the
1052 given hw_params parameter (SND_PCM_IOPLUG_HW_XXX).
1053 This function can't be used for the format parameter.  The latter
1054 function specifies the available parameter values as the list.
1055
1056 To clear the parameter constraints, call #snd_pcm_ioplug_params_reset() function.
1057
1058 */
1059
1060 /**
1061  * \brief Create an ioplug instance
1062  * \param ioplug the ioplug handle
1063  * \param name name of PCM
1064  * \param stream stream direction
1065  * \param mode PCM open mode
1066  * \return 0 if successful, or a negative error code
1067  *
1068  * Creates the ioplug instance.
1069  *
1070  * The callback is the mandatory field of ioplug handle.  At least, start, stop and
1071  * pointer callbacks must be set before calling this function.
1072  *
1073  */
1074 int snd_pcm_ioplug_create(snd_pcm_ioplug_t *ioplug, const char *name,
1075                           snd_pcm_stream_t stream, int mode)
1076 {
1077         ioplug_priv_t *io;
1078         int err;
1079         snd_pcm_t *pcm;
1080
1081         assert(ioplug && ioplug->callback);
1082         assert(ioplug->callback->start &&
1083                ioplug->callback->stop &&
1084                ioplug->callback->pointer);
1085
1086         /* We support 1.0.0 to current */
1087         if (ioplug->version < 0x010000 ||
1088             ioplug->version > SND_PCM_IOPLUG_VERSION) {
1089                 snd_error(PCM, "ioplug: Plugin version mismatch: 0x%x",
1090                                ioplug->version);
1091
1092                 return -ENXIO;
1093         }
1094
1095         io = calloc(1, sizeof(*io));
1096         if (! io)
1097                 return -ENOMEM;
1098
1099         io->data = ioplug;
1100         ioplug->state = SND_PCM_STATE_OPEN;
1101         ioplug->stream = stream;
1102
1103         err = snd_pcm_new(&pcm, SND_PCM_TYPE_IOPLUG, name, stream, mode);
1104         if (err < 0) {
1105                 free(io);
1106                 return err;
1107         }
1108
1109         ioplug->pcm = pcm;
1110         pcm->ops = &snd_pcm_ioplug_ops;
1111         pcm->fast_ops = &snd_pcm_ioplug_fast_ops;
1112         pcm->private_data = io;
1113
1114         snd_pcm_set_hw_ptr(pcm, &ioplug->hw_ptr, -1, 0);
1115         snd_pcm_set_appl_ptr(pcm, &ioplug->appl_ptr, -1, 0);
1116
1117         snd_pcm_ioplug_reinit_status(ioplug);
1118
1119         return 0;
1120 }
1121
1122 /**
1123  * \brief Delete the ioplug instance
1124  * \param ioplug the ioplug handle
1125  * \return 0 if successful, or a negative error code
1126  */
1127 int snd_pcm_ioplug_delete(snd_pcm_ioplug_t *ioplug)
1128 {
1129         return snd_pcm_close(ioplug->pcm);
1130 }
1131
1132
1133 /**
1134  * \brief Reset ioplug parameters
1135  * \param ioplug the ioplug handle
1136  *
1137  * Resets the all parameters for the given ioplug handle.
1138  */
1139 void snd_pcm_ioplug_params_reset(snd_pcm_ioplug_t *ioplug)
1140 {
1141         ioplug_priv_t *io = ioplug->pcm->private_data;
1142         clear_io_params(io);
1143 }
1144
1145 /**
1146  * \brief Set parameter as the list
1147  * \param ioplug the ioplug handle
1148  * \param type parameter type
1149  * \param num_list number of available values
1150  * \param list the list of available values
1151  * \return 0 if successful, or a negative error code
1152  *
1153  * Sets the parameter as the list.
1154  * The available values of the given parameter type is restricted to the ones of the given list.
1155  */
1156 int snd_pcm_ioplug_set_param_list(snd_pcm_ioplug_t *ioplug, int type, unsigned int num_list, const unsigned int *list)
1157 {
1158         ioplug_priv_t *io = ioplug->pcm->private_data;
1159         if (type < 0 || type >= SND_PCM_IOPLUG_HW_PARAMS) {
1160                 snd_error(PCM, "IOPLUG: invalid parameter type %d", type);
1161                 return -EINVAL;
1162         }
1163         if (type == SND_PCM_IOPLUG_HW_PERIODS)
1164                 io->params[type].integer = 1;
1165         return snd_ext_parm_set_list(&io->params[type], num_list, list);
1166 }
1167
1168 /**
1169  * \brief Set parameter as the min/max values
1170  * \param ioplug the ioplug handle
1171  * \param type parameter type
1172  * \param min the minimum value
1173  * \param max the maximum value
1174  * \return 0 if successful, or a negative error code
1175  *
1176  * Sets the parameter as the min/max values.
1177  * The available values of the given parameter type is restricted between the given
1178  * minimum and maximum values.
1179  */
1180 int snd_pcm_ioplug_set_param_minmax(snd_pcm_ioplug_t *ioplug, int type, unsigned int min, unsigned int max)
1181 {
1182         ioplug_priv_t *io = ioplug->pcm->private_data;
1183         if (type < 0 || type >= SND_PCM_IOPLUG_HW_PARAMS) {
1184                 snd_error(PCM, "IOPLUG: invalid parameter type %d", type);
1185                 return -EINVAL;
1186         }
1187         if (type == SND_PCM_IOPLUG_HW_ACCESS || type == SND_PCM_IOPLUG_HW_FORMAT) {
1188                 snd_error(PCM, "IOPLUG: invalid parameter type %d", type);
1189                 return -EINVAL;
1190         }
1191         if (type == SND_PCM_IOPLUG_HW_PERIODS)
1192                 io->params[type].integer = 1;
1193         return snd_ext_parm_set_minmax(&io->params[type], min, max);
1194 }
1195
1196 /**
1197  * \brief Reinitialize the poll and mmap status
1198  * \param ioplug the ioplug handle
1199  * \return 0 if successful, or a negative error code
1200  *
1201  * Reinitializes the poll and the mmap status of the PCM.
1202  * Call this function to propagate the status change in the ioplug instance to
1203  * its PCM internals.
1204  */
1205 int snd_pcm_ioplug_reinit_status(snd_pcm_ioplug_t *ioplug)
1206 {
1207         ioplug->pcm->poll_fd = ioplug->poll_fd;
1208         ioplug->pcm->poll_events = ioplug->poll_events;
1209         if (ioplug->flags & SND_PCM_IOPLUG_FLAG_MONOTONIC)
1210                 ioplug->pcm->tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC;
1211         else
1212                 ioplug->pcm->tstamp_type = SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
1213         ioplug->pcm->mmap_rw = ioplug->mmap_rw;
1214         return 0;
1215 }
1216
1217 /**
1218  * \brief Get mmap area of ioplug
1219  * \param ioplug the ioplug handle
1220  * \return the mmap channel areas if available, or NULL
1221  *
1222  * Returns the mmap channel areas if available.  When mmap_rw field is not set,
1223  * this function always returns NULL.
1224  */
1225 const snd_pcm_channel_area_t *snd_pcm_ioplug_mmap_areas(snd_pcm_ioplug_t *ioplug)
1226 {
1227         if (ioplug->mmap_rw)
1228                 return snd_pcm_mmap_areas(ioplug->pcm);
1229         return NULL;
1230 }
1231
1232 /**
1233  * \brief Change the ioplug PCM status
1234  * \param ioplug the ioplug handle
1235  * \param state the PCM status
1236  * \return zero if successful or a negative error code
1237  *
1238  * Changes the PCM status of the ioplug to the given value.
1239  * This function can be used for external plugins to notify the status
1240  * change, e.g. XRUN.
1241  */
1242 int snd_pcm_ioplug_set_state(snd_pcm_ioplug_t *ioplug, snd_pcm_state_t state)
1243 {
1244         ioplug->state = state;
1245         return 0;
1246 }
1247
1248 /**
1249  * \brief Get the available frames. This function can be used to calculate the
1250  * the available frames before calling #snd_pcm_avail_update()
1251  * \param ioplug the ioplug handle
1252  * \param hw_ptr hardware pointer in frames
1253  * \param appl_ptr application pointer in frames
1254  * \return available frames for the application
1255  */
1256 snd_pcm_uframes_t snd_pcm_ioplug_avail(const snd_pcm_ioplug_t * const ioplug,
1257                                        const snd_pcm_uframes_t hw_ptr,
1258                                        const snd_pcm_uframes_t appl_ptr)
1259 {
1260         return __snd_pcm_avail(ioplug->pcm, hw_ptr, appl_ptr);
1261 }
1262
1263 /**
1264  * \brief Get the available frames. This function can be used to calculate the
1265  * the available frames before calling #snd_pcm_avail_update()
1266  * \param ioplug the ioplug handle
1267  * \param hw_ptr hardware pointer in frames
1268  * \param appl_ptr application pointer in frames
1269  * \return available frames for the hardware
1270  */
1271 snd_pcm_uframes_t snd_pcm_ioplug_hw_avail(const snd_pcm_ioplug_t * const ioplug,
1272                                           const snd_pcm_uframes_t hw_ptr,
1273                                           const snd_pcm_uframes_t appl_ptr)
1274 {
1275         /* available data/space which can be transferred by the user
1276          * application
1277          */
1278         const snd_pcm_uframes_t user_avail = snd_pcm_ioplug_avail(ioplug,
1279                                                                   hw_ptr,
1280                                                                   appl_ptr);
1281
1282         if (user_avail > ioplug->pcm->buffer_size) {
1283                 /* there was an Xrun */
1284                 return 0;
1285         }
1286         /* available data/space which can be transferred by the DMA */
1287         return ioplug->pcm->buffer_size - user_avail;
1288 }