xref: /third_party/alsa-lib/src/pcm/pcm_ioplug.c (revision d5ac70f0)
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 */
36const char *_snd_module_pcm_ioplug = "";
37#endif
38
39#ifndef DOC_HIDDEN
40
41/* hw_params */
42typedef 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
50static int snd_pcm_ioplug_drop(snd_pcm_t *pcm);
51static int snd_pcm_ioplug_poll_descriptors_count(snd_pcm_t *pcm);
52static int snd_pcm_ioplug_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
53static 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 */
57static 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
91static 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
105static 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
110static 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
124static 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
144static 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
150static int snd_pcm_ioplug_hwsync(snd_pcm_t *pcm)
151{
152	snd_pcm_ioplug_hw_ptr_update(pcm);
153	return 0;
154}
155
156static 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
167static 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
185static 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 */
196static 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 */
206static 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 */
216static 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 */
226static 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
236static 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 */
246static 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 */
268static 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
285static 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
431static 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
456static 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
465static 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
481static 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
499static 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
514static 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 */
533static 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
579static 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
600static snd_pcm_sframes_t snd_pcm_ioplug_rewindable(snd_pcm_t *pcm)
601{
602	return snd_pcm_mmap_hw_rewindable(pcm);
603}
604
605static 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
611static snd_pcm_sframes_t snd_pcm_ioplug_forwardable(snd_pcm_t *pcm)
612{
613	return snd_pcm_mmap_avail(pcm);
614}
615
616static 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 */
623static 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 */
633static 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
652static 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
664static 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
676static 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
688static 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
700static 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
724static 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
732static 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
752static 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
767static 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
775static 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
788static 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
810static 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
826static int snd_pcm_ioplug_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
827{
828	return 0;
829}
830
831static 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
838static int snd_pcm_ioplug_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
839{
840	return 0;
841}
842
843static 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
853static 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
863static 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
873static 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
891static 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
898static 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
910static 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
928static 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
970The I/O-type plugin is a PCM plugin to work as the input or output terminal point,
971i.e. as a user-space PCM driver.
972
973The new plugin is created via #snd_pcm_ioplug_create() function.
974The first argument is a pointer of the pluging information.  Some of
975this struct must be initialized in prior to call
976#snd_pcm_ioplug_create().  Then the function fills other fields in
977return.  The rest arguments, name, stream and mode, are usually
978identical with the values passed from the ALSA plugin constructor.
979
980The following fields are mandatory: version, name, callback.
981Otherfields are optional and should be initialized with zero.
982
983The constant #SND_PCM_IOPLUG_VERSION must be passed to the version
984field for the version check in alsa-lib.  A non-NULL ASCII string
985has to be passed to the name field.  The callback field contains the
986table of callback functions for this plugin (defined as
987#snd_pcm_ioplug_callback_t).
988
989flags field specifies the optional bit-flags.  poll_fd and poll_events
990specify the poll file descriptor and the corresponding poll events
991(POLLIN, POLLOUT) for the plugin.  If the plugin requires multiple
992poll descriptors or poll descriptor(s) dynamically varying, set
993poll_descriptors and poll_descriptors_count callbacks to the callback
994table.  Then the poll_fd and poll_events field are ignored.
995
996mmap_rw specifies whether the plugin behaves in the pseudo mmap mode.
997When this value is set to 1, the plugin creates always a local buffer
998and performs read/write calls using this buffer as if it's mmapped.
999The address of local buffer can be obtained via
1000#snd_pcm_ioplug_mmap_areas() function.
1001When poll_fd, poll_events and mmap_rw fields are changed after
1002#snd_pcm_ioplug_create(), call #snd_pcm_ioplug_reinit_status() to
1003reflect the changes.
1004
1005The driver can set an arbitrary value (pointer) to private_data
1006field to refer its own data in the callbacks.
1007
1008The rest fields are filled by #snd_pcm_ioplug_create().  The pcm field
1009is the resultant PCM handle.  The others are the current status of the
1010PCM.
1011
1012The callback functions in #snd_pcm_ioplug_callback_t define the real
1013behavior of the driver.
1014At least, start, stop and pointer callbacks must be given.  Other
1015callbacks are optional.  The start and stop callbacks are called when
1016the PCM stream is started and stopped, repsectively.  The pointer
1017callback returns the current DMA position, which may be called at any
1018time.
1019
1020The transfer callback is called when any data transfer happens.  It
1021receives the area array, offset and the size to transfer.  The area
1022array contains the array of snd_pcm_channel_area_t with the elements
1023of number of channels.
1024
1025When the PCM is closed, close callback is called.  If the driver
1026allocates any internal buffers, they should be released in this
1027callback.  The hw_params and hw_free callbacks are called when
1028hw_params are set and reset, respectively.  Note that they may be
1029called multiple times according to the application.  Similarly,
1030sw_params callback is called when sw_params is set or changed.
1031
1032The 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
1035poll_descriptors callbacks are used to return the multiple or dynamic
1036poll descriptors as mentioned above.  The poll_revents callback is
1037used to modify poll events.  If the driver needs to mangle the native
1038poll events to proper poll events for PCM, you can do it in this
1039callback.
1040
1041Finally, the dump callback is used to print the status of the plugin.
1042
1043Note that some callbacks (start, stop, pointer, transfer and pause)
1044may be called inside the internal pthread mutex, and they shouldn't
1045call the PCM functions again unnecessarily from the callback itself;
1046otherwise it may lead to a deadlock.
1047
1048The hw_params constraints can be defined via either
1049#snd_pcm_ioplug_set_param_minmax() and #snd_pcm_ioplug_set_param_list()
1050functions after calling #snd_pcm_ioplug_create().
1051The former defines the minimal and maximal acceptable values for the
1052given hw_params parameter (SND_PCM_IOPLUG_HW_XXX).
1053This function can't be used for the format parameter.  The latter
1054function specifies the available parameter values as the list.
1055
1056To 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 */
1074int 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		SNDERR("ioplug: Plugin version mismatch: 0x%x",
1090		       ioplug->version);
1091		return -ENXIO;
1092	}
1093
1094	io = calloc(1, sizeof(*io));
1095	if (! io)
1096		return -ENOMEM;
1097
1098	io->data = ioplug;
1099	ioplug->state = SND_PCM_STATE_OPEN;
1100	ioplug->stream = stream;
1101
1102	err = snd_pcm_new(&pcm, SND_PCM_TYPE_IOPLUG, name, stream, mode);
1103	if (err < 0) {
1104		free(io);
1105		return err;
1106	}
1107
1108	ioplug->pcm = pcm;
1109	pcm->ops = &snd_pcm_ioplug_ops;
1110	pcm->fast_ops = &snd_pcm_ioplug_fast_ops;
1111	pcm->private_data = io;
1112
1113	snd_pcm_set_hw_ptr(pcm, &ioplug->hw_ptr, -1, 0);
1114	snd_pcm_set_appl_ptr(pcm, &ioplug->appl_ptr, -1, 0);
1115
1116	snd_pcm_ioplug_reinit_status(ioplug);
1117
1118	return 0;
1119}
1120
1121/**
1122 * \brief Delete the ioplug instance
1123 * \param ioplug the ioplug handle
1124 * \return 0 if successful, or a negative error code
1125 */
1126int snd_pcm_ioplug_delete(snd_pcm_ioplug_t *ioplug)
1127{
1128	return snd_pcm_close(ioplug->pcm);
1129}
1130
1131
1132/**
1133 * \brief Reset ioplug parameters
1134 * \param ioplug the ioplug handle
1135 *
1136 * Resets the all parameters for the given ioplug handle.
1137 */
1138void snd_pcm_ioplug_params_reset(snd_pcm_ioplug_t *ioplug)
1139{
1140	ioplug_priv_t *io = ioplug->pcm->private_data;
1141	clear_io_params(io);
1142}
1143
1144/**
1145 * \brief Set parameter as the list
1146 * \param ioplug the ioplug handle
1147 * \param type parameter type
1148 * \param num_list number of available values
1149 * \param list the list of available values
1150 * \return 0 if successful, or a negative error code
1151 *
1152 * Sets the parameter as the list.
1153 * The available values of the given parameter type is restricted to the ones of the given list.
1154 */
1155int snd_pcm_ioplug_set_param_list(snd_pcm_ioplug_t *ioplug, int type, unsigned int num_list, const unsigned int *list)
1156{
1157	ioplug_priv_t *io = ioplug->pcm->private_data;
1158	if (type < 0 || type >= SND_PCM_IOPLUG_HW_PARAMS) {
1159		SNDERR("IOPLUG: invalid parameter type %d", type);
1160		return -EINVAL;
1161	}
1162	if (type == SND_PCM_IOPLUG_HW_PERIODS)
1163		io->params[type].integer = 1;
1164	return snd_ext_parm_set_list(&io->params[type], num_list, list);
1165}
1166
1167/**
1168 * \brief Set parameter as the min/max values
1169 * \param ioplug the ioplug handle
1170 * \param type parameter type
1171 * \param min the minimum value
1172 * \param max the maximum value
1173 * \return 0 if successful, or a negative error code
1174 *
1175 * Sets the parameter as the min/max values.
1176 * The available values of the given parameter type is restricted between the given
1177 * minimum and maximum values.
1178 */
1179int snd_pcm_ioplug_set_param_minmax(snd_pcm_ioplug_t *ioplug, int type, unsigned int min, unsigned int max)
1180{
1181	ioplug_priv_t *io = ioplug->pcm->private_data;
1182	if (type < 0 || type >= SND_PCM_IOPLUG_HW_PARAMS) {
1183		SNDERR("IOPLUG: invalid parameter type %d", type);
1184		return -EINVAL;
1185	}
1186	if (type == SND_PCM_IOPLUG_HW_ACCESS || type == SND_PCM_IOPLUG_HW_FORMAT) {
1187		SNDERR("IOPLUG: invalid parameter type %d", type);
1188		return -EINVAL;
1189	}
1190	if (type == SND_PCM_IOPLUG_HW_PERIODS)
1191		io->params[type].integer = 1;
1192	return snd_ext_parm_set_minmax(&io->params[type], min, max);
1193}
1194
1195/**
1196 * \brief Reinitialize the poll and mmap status
1197 * \param ioplug the ioplug handle
1198 * \return 0 if successful, or a negative error code
1199 *
1200 * Reinitializes the poll and the mmap status of the PCM.
1201 * Call this function to propagate the status change in the ioplug instance to
1202 * its PCM internals.
1203 */
1204int snd_pcm_ioplug_reinit_status(snd_pcm_ioplug_t *ioplug)
1205{
1206	ioplug->pcm->poll_fd = ioplug->poll_fd;
1207	ioplug->pcm->poll_events = ioplug->poll_events;
1208	if (ioplug->flags & SND_PCM_IOPLUG_FLAG_MONOTONIC)
1209		ioplug->pcm->tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC;
1210	else
1211		ioplug->pcm->tstamp_type = SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
1212	ioplug->pcm->mmap_rw = ioplug->mmap_rw;
1213	return 0;
1214}
1215
1216/**
1217 * \brief Get mmap area of ioplug
1218 * \param ioplug the ioplug handle
1219 * \return the mmap channel areas if available, or NULL
1220 *
1221 * Returns the mmap channel areas if available.  When mmap_rw field is not set,
1222 * this function always returns NULL.
1223 */
1224const snd_pcm_channel_area_t *snd_pcm_ioplug_mmap_areas(snd_pcm_ioplug_t *ioplug)
1225{
1226	if (ioplug->mmap_rw)
1227		return snd_pcm_mmap_areas(ioplug->pcm);
1228	return NULL;
1229}
1230
1231/**
1232 * \brief Change the ioplug PCM status
1233 * \param ioplug the ioplug handle
1234 * \param state the PCM status
1235 * \return zero if successful or a negative error code
1236 *
1237 * Changes the PCM status of the ioplug to the given value.
1238 * This function can be used for external plugins to notify the status
1239 * change, e.g. XRUN.
1240 */
1241int snd_pcm_ioplug_set_state(snd_pcm_ioplug_t *ioplug, snd_pcm_state_t state)
1242{
1243	ioplug->state = state;
1244	return 0;
1245}
1246
1247/**
1248 * \brief Get the available frames. This function can be used to calculate the
1249 * the available frames before calling #snd_pcm_avail_update()
1250 * \param ioplug the ioplug handle
1251 * \param hw_ptr hardware pointer in frames
1252 * \param appl_ptr application pointer in frames
1253 * \return available frames for the application
1254 */
1255snd_pcm_uframes_t snd_pcm_ioplug_avail(const snd_pcm_ioplug_t * const ioplug,
1256				       const snd_pcm_uframes_t hw_ptr,
1257				       const snd_pcm_uframes_t appl_ptr)
1258{
1259	return __snd_pcm_avail(ioplug->pcm, hw_ptr, appl_ptr);
1260}
1261
1262/**
1263 * \brief Get the available frames. This function can be used to calculate the
1264 * the available frames before calling #snd_pcm_avail_update()
1265 * \param ioplug the ioplug handle
1266 * \param hw_ptr hardware pointer in frames
1267 * \param appl_ptr application pointer in frames
1268 * \return available frames for the hardware
1269 */
1270snd_pcm_uframes_t snd_pcm_ioplug_hw_avail(const snd_pcm_ioplug_t * const ioplug,
1271					  const snd_pcm_uframes_t hw_ptr,
1272					  const snd_pcm_uframes_t appl_ptr)
1273{
1274	/* available data/space which can be transferred by the user
1275	 * application
1276	 */
1277	const snd_pcm_uframes_t user_avail = snd_pcm_ioplug_avail(ioplug,
1278								  hw_ptr,
1279								  appl_ptr);
1280
1281	if (user_avail > ioplug->pcm->buffer_size) {
1282		/* there was an Xrun */
1283		return 0;
1284	}
1285	/* available data/space which can be transferred by the DMA */
1286	return ioplug->pcm->buffer_size - user_avail;
1287}
1288