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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 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	unsigned int last_hw;
46	snd_pcm_uframes_t avail_max;
47	snd_htimestamp_t trigger_tstamp;
48} ioplug_priv_t;
49
50/* update the hw pointer */
51static void snd_pcm_ioplug_hw_ptr_update(snd_pcm_t *pcm)
52{
53	ioplug_priv_t *io = pcm->private_data;
54	snd_pcm_sframes_t hw;
55
56	hw = io->data->callback->pointer(io->data);
57	if (hw >= 0) {
58		unsigned int delta;
59		if ((unsigned int)hw >= io->last_hw)
60			delta = hw - io->last_hw;
61		else
62			delta = pcm->buffer_size + hw - io->last_hw;
63		io->data->hw_ptr += delta;
64		io->last_hw = hw;
65	} else
66		io->data->state = SNDRV_PCM_STATE_XRUN;
67}
68
69static int snd_pcm_ioplug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
70{
71	memset(info, 0, sizeof(*info));
72	info->stream = pcm->stream;
73	info->card = -1;
74	if (pcm->name) {
75		strncpy((char *)info->id, pcm->name, sizeof(info->id));
76		strncpy((char *)info->name, pcm->name, sizeof(info->name));
77		strncpy((char *)info->subname, pcm->name, sizeof(info->subname));
78	}
79	info->subdevices_count = 1;
80	return 0;
81}
82
83static int snd_pcm_ioplug_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
84{
85	return snd_pcm_channel_info_shm(pcm, info, -1);
86}
87
88static int snd_pcm_ioplug_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
89{
90	ioplug_priv_t *io = pcm->private_data;
91
92	memset(status, 0, sizeof(*status));
93	snd_pcm_ioplug_hw_ptr_update(pcm);
94	status->state = io->data->state;
95	status->trigger_tstamp = io->trigger_tstamp;
96	status->avail = snd_pcm_mmap_avail(pcm);
97	status->avail_max = io->avail_max;
98	return 0;
99}
100
101static snd_pcm_state_t snd_pcm_ioplug_state(snd_pcm_t *pcm)
102{
103	ioplug_priv_t *io = pcm->private_data;
104	return io->data->state;
105}
106
107static int snd_pcm_ioplug_hwsync(snd_pcm_t *pcm)
108{
109	snd_pcm_ioplug_hw_ptr_update(pcm);
110	return 0;
111}
112
113static int snd_pcm_ioplug_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
114{
115	ioplug_priv_t *io = pcm->private_data;
116
117	if (io->data->version >= 0x010001 &&
118	    io->data->callback->delay)
119		return io->data->callback->delay(io->data, delayp);
120	else {
121		snd_pcm_ioplug_hw_ptr_update(pcm);
122		*delayp = snd_pcm_mmap_hw_avail(pcm);
123	}
124	return 0;
125}
126
127static int snd_pcm_ioplug_reset(snd_pcm_t *pcm)
128{
129	ioplug_priv_t *io = pcm->private_data;
130
131	io->data->appl_ptr = 0;
132	io->data->hw_ptr = 0;
133	io->last_hw = 0;
134	io->avail_max = 0;
135	return 0;
136}
137
138static int snd_pcm_ioplug_prepare(snd_pcm_t *pcm)
139{
140	ioplug_priv_t *io = pcm->private_data;
141
142	io->data->state = SND_PCM_STATE_PREPARED;
143	snd_pcm_ioplug_reset(pcm);
144	if (io->data->callback->prepare)
145		return io->data->callback->prepare(io->data);
146	return 0;
147}
148
149static const int hw_params_type[SND_PCM_IOPLUG_HW_PARAMS] = {
150	[SND_PCM_IOPLUG_HW_ACCESS] = SND_PCM_HW_PARAM_ACCESS,
151	[SND_PCM_IOPLUG_HW_FORMAT] = SND_PCM_HW_PARAM_FORMAT,
152	[SND_PCM_IOPLUG_HW_CHANNELS] = SND_PCM_HW_PARAM_CHANNELS,
153	[SND_PCM_IOPLUG_HW_RATE] = SND_PCM_HW_PARAM_RATE,
154	[SND_PCM_IOPLUG_HW_PERIOD_BYTES] = SND_PCM_HW_PARAM_PERIOD_BYTES,
155	[SND_PCM_IOPLUG_HW_BUFFER_BYTES] = SND_PCM_HW_PARAM_BUFFER_BYTES,
156	[SND_PCM_IOPLUG_HW_PERIODS] = SND_PCM_HW_PARAM_PERIODS,
157};
158
159/* x = a * b */
160static int rule_mul(snd_pcm_hw_params_t *params, int x, int a, int b)
161{
162	snd_interval_t t;
163
164	snd_interval_mul(hw_param_interval(params, a),
165			 hw_param_interval(params, b), &t);
166	return snd_interval_refine(hw_param_interval(params, x), &t);
167}
168
169/* x = a / b */
170static int rule_div(snd_pcm_hw_params_t *params, int x, int a, int b)
171{
172	snd_interval_t t;
173
174	snd_interval_div(hw_param_interval(params, a),
175			 hw_param_interval(params, b), &t);
176	return snd_interval_refine(hw_param_interval(params, x), &t);
177}
178
179/* x = a * b / k */
180static int rule_muldivk(snd_pcm_hw_params_t *params, int x, int a, int b, int k)
181{
182	snd_interval_t t;
183
184	snd_interval_muldivk(hw_param_interval(params, a),
185			     hw_param_interval(params, b), k, &t);
186	return snd_interval_refine(hw_param_interval(params, x), &t);
187}
188
189/* x = a * k / b */
190static int rule_mulkdiv(snd_pcm_hw_params_t *params, int x, int a, int k, int b)
191{
192	snd_interval_t t;
193
194	snd_interval_mulkdiv(hw_param_interval(params, a), k,
195			     hw_param_interval(params, b), &t);
196	return snd_interval_refine(hw_param_interval(params, x), &t);
197}
198
199#if 0
200static void dump_parm(snd_pcm_hw_params_t *params)
201{
202	snd_output_t *log;
203	snd_output_stdio_attach(&log, stderr, 0);
204	snd_pcm_hw_params_dump(params, log);
205	snd_output_close(log);
206}
207#endif
208
209/* refine *_TIME and *_SIZE, then update *_BYTES */
210static int refine_time_and_size(snd_pcm_hw_params_t *params,
211				int time, int size, int bytes)
212{
213	int err, change1 = 0;
214
215	/* size = time * rate / 1000000 */
216	err = rule_muldivk(params, size, time,
217			   SND_PCM_HW_PARAM_RATE, 1000000);
218	if (err < 0)
219		return err;
220	change1 |= err;
221
222	/* bytes = size * framebits / 8 */
223	err = rule_muldivk(params, bytes, size,
224			   SND_PCM_HW_PARAM_FRAME_BITS, 8);
225	if (err < 0)
226		return err;
227	change1 |= err;
228	return change1;
229}
230
231/* refine *_TIME and *_SIZE from *_BYTES */
232static int refine_back_time_and_size(snd_pcm_hw_params_t *params,
233				     int time, int size, int bytes)
234{
235	int err;
236
237	/* size = bytes * 8 / framebits */
238	err = rule_mulkdiv(params, size, bytes, 8, SND_PCM_HW_PARAM_FRAME_BITS);
239	if (err < 0)
240		return err;
241	/* time = size * 1000000 / rate */
242	err = rule_mulkdiv(params, time, size, 1000000, SND_PCM_HW_PARAM_RATE);
243	if (err < 0)
244		return err;
245	return 0;
246}
247
248
249static int snd_pcm_ioplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
250{
251	int change = 0, change1, change2, err;
252	ioplug_priv_t *io = pcm->private_data;
253	struct snd_ext_parm *p;
254	unsigned int i;
255
256	/* access, format */
257	for (i = SND_PCM_IOPLUG_HW_ACCESS; i <= SND_PCM_IOPLUG_HW_FORMAT; i++) {
258		err = snd_ext_parm_mask_refine(hw_param_mask(params, hw_params_type[i]),
259					       io->params, i);
260		if (err < 0)
261			return err;
262		change |= err;
263	}
264	/* channels, rate */
265	for (; i <= SND_PCM_IOPLUG_HW_RATE; i++) {
266		err = snd_ext_parm_interval_refine(hw_param_interval(params, hw_params_type[i]),
267						   io->params, i);
268		if (err < 0)
269			return err;
270		change |= err;
271	}
272
273	if (params->rmask & ((1 << SND_PCM_HW_PARAM_ACCESS) |
274			     (1 << SND_PCM_HW_PARAM_FORMAT) |
275			     (1 << SND_PCM_HW_PARAM_SUBFORMAT) |
276			     (1 << SND_PCM_HW_PARAM_CHANNELS) |
277			     (1 << SND_PCM_HW_PARAM_RATE))) {
278		err = snd_pcm_hw_refine_soft(pcm, params);
279		if (err < 0)
280			return err;
281		change |= err;
282	}
283
284	change1 = refine_time_and_size(params, SND_PCM_HW_PARAM_PERIOD_TIME,
285				       SND_PCM_HW_PARAM_PERIOD_SIZE,
286				       SND_PCM_HW_PARAM_PERIOD_BYTES);
287	if (change1 < 0)
288		return change1;
289	err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_BYTES),
290					   io->params, SND_PCM_IOPLUG_HW_PERIOD_BYTES);
291	if (err < 0)
292		return err;
293	change1 |= err;
294	if (change1) {
295		change |= change1;
296		err = refine_back_time_and_size(params, SND_PCM_HW_PARAM_PERIOD_TIME,
297						SND_PCM_HW_PARAM_PERIOD_SIZE,
298						SND_PCM_HW_PARAM_PERIOD_BYTES);
299		if (err < 0)
300			return err;
301	}
302
303	change1 = refine_time_and_size(params, SND_PCM_HW_PARAM_BUFFER_TIME,
304				       SND_PCM_HW_PARAM_BUFFER_SIZE,
305				       SND_PCM_HW_PARAM_BUFFER_BYTES);
306	if (change1 < 0)
307		return change1;
308	change |= change1;
309
310	do {
311		change2 = 0;
312		err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_BUFFER_BYTES),
313						   io->params, SND_PCM_IOPLUG_HW_BUFFER_BYTES);
314		if (err < 0)
315			return err;
316		change2 |= err;
317		/* periods = buffer_bytes / period_bytes */
318		err = rule_div(params, SND_PCM_HW_PARAM_PERIODS,
319			       SND_PCM_HW_PARAM_BUFFER_BYTES,
320			       SND_PCM_HW_PARAM_PERIOD_BYTES);
321		if (err < 0)
322			return err;
323		change2 |= err;
324		err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIODS),
325						   io->params, SND_PCM_IOPLUG_HW_PERIODS);
326		if (err < 0)
327			return err;
328		change2 |= err;
329		/* buffer_bytes = periods * period_bytes */
330		err = rule_mul(params, SND_PCM_HW_PARAM_BUFFER_BYTES,
331			       SND_PCM_HW_PARAM_PERIOD_BYTES,
332			       SND_PCM_HW_PARAM_PERIODS);
333		if (err < 0)
334			return err;
335		change2 |= err;
336		change1 |= change2;
337	} while (change2);
338	change |= change1;
339
340	if (change1) {
341		err = refine_back_time_and_size(params, SND_PCM_HW_PARAM_BUFFER_TIME,
342						SND_PCM_HW_PARAM_BUFFER_SIZE,
343						SND_PCM_HW_PARAM_BUFFER_BYTES);
344		if (err < 0)
345			return err;
346	}
347
348	/* period_bytes = buffer_bytes / periods */
349	err = rule_div(params, SND_PCM_HW_PARAM_PERIOD_BYTES,
350		       SND_PCM_HW_PARAM_BUFFER_BYTES,
351		       SND_PCM_HW_PARAM_PERIODS);
352	if (err < 0)
353		return err;
354	if (err) {
355		/* update period_size and period_time */
356		change |= err;
357		err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_BYTES),
358						   io->params, SND_PCM_IOPLUG_HW_PERIOD_BYTES);
359		if (err < 0)
360			return err;
361		err = refine_back_time_and_size(params, SND_PCM_HW_PARAM_PERIOD_TIME,
362						SND_PCM_HW_PARAM_PERIOD_SIZE,
363						SND_PCM_HW_PARAM_PERIOD_BYTES);
364		if (err < 0)
365			return err;
366	}
367
368	params->info = SND_PCM_INFO_BLOCK_TRANSFER;
369	p = &io->params[SND_PCM_IOPLUG_HW_ACCESS];
370	if (p->active) {
371		for (i = 0; i < p->num_list; i++)
372			switch (p->list[i]) {
373			case SND_PCM_ACCESS_MMAP_INTERLEAVED:
374			case SND_PCM_ACCESS_RW_INTERLEAVED:
375				params->info |= SND_PCM_INFO_INTERLEAVED;
376				break;
377			case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
378			case SND_PCM_ACCESS_RW_NONINTERLEAVED:
379				params->info |= SND_PCM_INFO_NONINTERLEAVED;
380				break;
381			}
382	}
383	if (io->data->callback->pause)
384		params->info |= SND_PCM_INFO_PAUSE;
385	if (io->data->callback->resume)
386		params->info |= SND_PCM_INFO_RESUME;
387
388#if 0
389	fprintf(stderr, "XXX\n");
390	dump_parm(params);
391#endif
392	return change;
393}
394
395static int snd_pcm_ioplug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
396{
397	ioplug_priv_t *io = pcm->private_data;
398	int err;
399
400	INTERNAL(snd_pcm_hw_params_get_access)(params, &io->data->access);
401	INTERNAL(snd_pcm_hw_params_get_format)(params, &io->data->format);
402	INTERNAL(snd_pcm_hw_params_get_channels)(params, &io->data->channels);
403	INTERNAL(snd_pcm_hw_params_get_rate)(params, &io->data->rate, 0);
404	INTERNAL(snd_pcm_hw_params_get_period_size)(params, &io->data->period_size, 0);
405	INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &io->data->buffer_size);
406	if (io->data->callback->hw_params) {
407		err = io->data->callback->hw_params(io->data, params);
408		if (err < 0)
409			return err;
410		INTERNAL(snd_pcm_hw_params_get_access)(params, &io->data->access);
411		INTERNAL(snd_pcm_hw_params_get_format)(params, &io->data->format);
412		INTERNAL(snd_pcm_hw_params_get_channels)(params, &io->data->channels);
413		INTERNAL(snd_pcm_hw_params_get_rate)(params, &io->data->rate, 0);
414		INTERNAL(snd_pcm_hw_params_get_period_size)(params, &io->data->period_size, 0);
415		INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &io->data->buffer_size);
416	}
417	return 0;
418}
419
420static int snd_pcm_ioplug_hw_free(snd_pcm_t *pcm)
421{
422	ioplug_priv_t *io = pcm->private_data;
423
424	if (io->data->callback->hw_free)
425		return io->data->callback->hw_free(io->data);
426	return 0;
427}
428
429static int snd_pcm_ioplug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
430{
431	ioplug_priv_t *io = pcm->private_data;
432
433	if (io->data->callback->sw_params)
434		return io->data->callback->sw_params(io->data, params);
435	return 0;
436}
437
438
439static int snd_pcm_ioplug_start(snd_pcm_t *pcm)
440{
441	ioplug_priv_t *io = pcm->private_data;
442	int err;
443
444	if (io->data->state != SND_PCM_STATE_PREPARED)
445		return -EBADFD;
446
447	err = io->data->callback->start(io->data);
448	if (err < 0)
449		return err;
450
451	gettimestamp(&io->trigger_tstamp, pcm->monotonic);
452	io->data->state = SND_PCM_STATE_RUNNING;
453
454	return 0;
455}
456
457static int snd_pcm_ioplug_drop(snd_pcm_t *pcm)
458{
459	ioplug_priv_t *io = pcm->private_data;
460
461	if (io->data->state == SND_PCM_STATE_OPEN)
462		return -EBADFD;
463
464	io->data->callback->stop(io->data);
465
466	gettimestamp(&io->trigger_tstamp, pcm->monotonic);
467	io->data->state = SND_PCM_STATE_SETUP;
468
469	return 0;
470}
471
472static int snd_pcm_ioplug_drain(snd_pcm_t *pcm)
473{
474	ioplug_priv_t *io = pcm->private_data;
475
476	if (io->data->state == SND_PCM_STATE_OPEN)
477		return -EBADFD;
478	if (io->data->callback->drain)
479		io->data->callback->drain(io->data);
480	return snd_pcm_ioplug_drop(pcm);
481}
482
483static int snd_pcm_ioplug_pause(snd_pcm_t *pcm, int enable)
484{
485	ioplug_priv_t *io = pcm->private_data;
486	static const snd_pcm_state_t states[2] = {
487		SND_PCM_STATE_RUNNING, SND_PCM_STATE_PAUSED
488	};
489	int prev, err;
490
491	prev = !enable;
492	enable = !prev;
493	if (io->data->state != states[prev])
494		return -EBADFD;
495	if (io->data->callback->pause) {
496		err = io->data->callback->pause(io->data, enable);
497		if (err < 0)
498			return err;
499	}
500	io->data->state = states[enable];
501	return 0;
502}
503
504static snd_pcm_sframes_t snd_pcm_ioplug_rewindable(snd_pcm_t *pcm)
505{
506	return snd_pcm_mmap_hw_avail(pcm);
507}
508
509static snd_pcm_sframes_t snd_pcm_ioplug_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
510{
511	snd_pcm_mmap_appl_backward(pcm, frames);
512	return frames;
513}
514
515static snd_pcm_sframes_t snd_pcm_ioplug_forwardable(snd_pcm_t *pcm)
516{
517	return snd_pcm_mmap_avail(pcm);
518}
519
520static snd_pcm_sframes_t snd_pcm_ioplug_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
521{
522	snd_pcm_mmap_appl_forward(pcm, frames);
523	return frames;
524}
525
526static int snd_pcm_ioplug_resume(snd_pcm_t *pcm)
527{
528	ioplug_priv_t *io = pcm->private_data;
529
530	if (io->data->callback->resume)
531		io->data->callback->resume(io->data);
532	return 0;
533}
534
535static snd_pcm_sframes_t ioplug_priv_transfer_areas(snd_pcm_t *pcm,
536						       const snd_pcm_channel_area_t *areas,
537						       snd_pcm_uframes_t offset,
538						       snd_pcm_uframes_t size)
539{
540	ioplug_priv_t *io = pcm->private_data;
541	snd_pcm_sframes_t result;
542
543	if (! size)
544		return 0;
545	if (io->data->callback->transfer)
546		result = io->data->callback->transfer(io->data, areas, offset, size);
547	else
548		result = size;
549	if (result > 0)
550		snd_pcm_mmap_appl_forward(pcm, result);
551	return result;
552}
553
554static snd_pcm_sframes_t snd_pcm_ioplug_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
555{
556	if (pcm->mmap_rw)
557		return snd_pcm_mmap_writei(pcm, buffer, size);
558	else {
559		snd_pcm_channel_area_t areas[pcm->channels];
560		snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
561		return snd_pcm_write_areas(pcm, areas, 0, size,
562					   ioplug_priv_transfer_areas);
563	}
564}
565
566static snd_pcm_sframes_t snd_pcm_ioplug_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
567{
568	if (pcm->mmap_rw)
569		return snd_pcm_mmap_writen(pcm, bufs, size);
570	else {
571		snd_pcm_channel_area_t areas[pcm->channels];
572		snd_pcm_areas_from_bufs(pcm, areas, bufs);
573		return snd_pcm_write_areas(pcm, areas, 0, size,
574					   ioplug_priv_transfer_areas);
575	}
576}
577
578static snd_pcm_sframes_t snd_pcm_ioplug_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
579{
580	if (pcm->mmap_rw)
581		return snd_pcm_mmap_readi(pcm, buffer, size);
582	else {
583		snd_pcm_channel_area_t areas[pcm->channels];
584		snd_pcm_areas_from_buf(pcm, areas, buffer);
585		return snd_pcm_read_areas(pcm, areas, 0, size,
586					  ioplug_priv_transfer_areas);
587	}
588}
589
590static snd_pcm_sframes_t snd_pcm_ioplug_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
591{
592	if (pcm->mmap_rw)
593		return snd_pcm_mmap_readn(pcm, bufs, size);
594	else {
595		snd_pcm_channel_area_t areas[pcm->channels];
596		snd_pcm_areas_from_bufs(pcm, areas, bufs);
597		return snd_pcm_read_areas(pcm, areas, 0, size,
598					  ioplug_priv_transfer_areas);
599	}
600}
601
602static snd_pcm_sframes_t snd_pcm_ioplug_mmap_commit(snd_pcm_t *pcm,
603						    snd_pcm_uframes_t offset,
604						    snd_pcm_uframes_t size)
605{
606	if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
607	    pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
608	    pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
609		const snd_pcm_channel_area_t *areas;
610		snd_pcm_uframes_t ofs, frames = size;
611
612		snd_pcm_mmap_begin(pcm, &areas, &ofs, &frames);
613		if (ofs != offset)
614			return -EIO;
615		return ioplug_priv_transfer_areas(pcm, areas, offset, frames);
616	}
617
618	snd_pcm_mmap_appl_forward(pcm, size);
619	return size;
620}
621
622static snd_pcm_sframes_t snd_pcm_ioplug_avail_update(snd_pcm_t *pcm)
623{
624	ioplug_priv_t *io = pcm->private_data;
625	snd_pcm_uframes_t avail;
626
627	snd_pcm_ioplug_hw_ptr_update(pcm);
628	if (io->data->state == SNDRV_PCM_STATE_XRUN)
629		return -EPIPE;
630	if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
631	    pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
632	    pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
633		if (io->data->callback->transfer) {
634			const snd_pcm_channel_area_t *areas;
635			snd_pcm_uframes_t offset, size = UINT_MAX;
636			snd_pcm_sframes_t result;
637
638			snd_pcm_mmap_begin(pcm, &areas, &offset, &size);
639			result = io->data->callback->transfer(io->data, areas, offset, size);
640			if (result < 0)
641				return result;
642		}
643	}
644	avail = snd_pcm_mmap_avail(pcm);
645	if (avail > io->avail_max)
646		io->avail_max = avail;
647	return (snd_pcm_sframes_t)avail;
648}
649
650static int snd_pcm_ioplug_nonblock(snd_pcm_t *pcm, int nonblock)
651{
652	ioplug_priv_t *io = pcm->private_data;
653
654	io->data->nonblock = nonblock;
655	return 0;
656}
657
658static int snd_pcm_ioplug_poll_descriptors_count(snd_pcm_t *pcm)
659{
660	ioplug_priv_t *io = pcm->private_data;
661
662	if (io->data->callback->poll_descriptors_count)
663		return io->data->callback->poll_descriptors_count(io->data);
664	else
665		return 1;
666}
667
668static int snd_pcm_ioplug_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
669{
670	ioplug_priv_t *io = pcm->private_data;
671
672	if (io->data->callback->poll_descriptors)
673		return io->data->callback->poll_descriptors(io->data, pfds, space);
674	if (pcm->poll_fd < 0)
675		return -EIO;
676	if (space >= 1 && pfds) {
677		pfds->fd = pcm->poll_fd;
678		pfds->events = pcm->poll_events | POLLERR | POLLNVAL;
679	} else {
680		return 0;
681	}
682	return 1;
683}
684
685static int snd_pcm_ioplug_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
686{
687	ioplug_priv_t *io = pcm->private_data;
688
689	if (io->data->callback->poll_revents)
690		return io->data->callback->poll_revents(io->data, pfds, nfds, revents);
691	else
692		*revents = pfds->revents;
693	return 0;
694}
695
696static int snd_pcm_ioplug_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
697{
698	return 0;
699}
700
701static int snd_pcm_ioplug_async(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
702				int sig ATTRIBUTE_UNUSED,
703				pid_t pid ATTRIBUTE_UNUSED)
704{
705	return -ENOSYS;
706}
707
708static int snd_pcm_ioplug_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
709{
710	return 0;
711}
712
713static void snd_pcm_ioplug_dump(snd_pcm_t *pcm, snd_output_t *out)
714{
715	ioplug_priv_t *io = pcm->private_data;
716
717	if (io->data->callback->dump)
718		io->data->callback->dump(io->data, out);
719	else {
720		if (io->data->name)
721			snd_output_printf(out, "%s\n", io->data->name);
722		else
723			snd_output_printf(out, "IO-PCM Plugin\n");
724		if (pcm->setup) {
725			snd_output_printf(out, "Its setup is:\n");
726			snd_pcm_dump_setup(pcm, out);
727		}
728	}
729}
730
731static void clear_io_params(ioplug_priv_t *io)
732{
733	int i;
734	for (i = 0; i < SND_PCM_IOPLUG_HW_PARAMS; i++)
735		snd_ext_parm_clear(&io->params[i]);
736}
737
738static int snd_pcm_ioplug_close(snd_pcm_t *pcm)
739{
740	ioplug_priv_t *io = pcm->private_data;
741
742	clear_io_params(io);
743	if (io->data->callback->close)
744		io->data->callback->close(io->data);
745	free(io);
746
747	return 0;
748}
749
750static const snd_pcm_ops_t snd_pcm_ioplug_ops = {
751	.close = snd_pcm_ioplug_close,
752	.nonblock = snd_pcm_ioplug_nonblock,
753	.async = snd_pcm_ioplug_async,
754	.info = snd_pcm_ioplug_info,
755	.hw_refine = snd_pcm_ioplug_hw_refine,
756	.hw_params = snd_pcm_ioplug_hw_params,
757	.hw_free = snd_pcm_ioplug_hw_free,
758	.sw_params = snd_pcm_ioplug_sw_params,
759	.channel_info = snd_pcm_ioplug_channel_info,
760	.dump = snd_pcm_ioplug_dump,
761	.mmap = snd_pcm_ioplug_mmap,
762	.munmap = snd_pcm_ioplug_munmap,
763};
764
765static const snd_pcm_fast_ops_t snd_pcm_ioplug_fast_ops = {
766	.status = snd_pcm_ioplug_status,
767	.prepare = snd_pcm_ioplug_prepare,
768	.reset = snd_pcm_ioplug_reset,
769	.start = snd_pcm_ioplug_start,
770	.drop = snd_pcm_ioplug_drop,
771	.drain = snd_pcm_ioplug_drain,
772	.pause = snd_pcm_ioplug_pause,
773	.state = snd_pcm_ioplug_state,
774	.hwsync = snd_pcm_ioplug_hwsync,
775	.delay = snd_pcm_ioplug_delay,
776	.resume = snd_pcm_ioplug_resume,
777	.link = NULL,
778	.link_slaves = NULL,
779	.unlink = NULL,
780	.rewindable = snd_pcm_ioplug_rewindable,
781	.rewind = snd_pcm_ioplug_rewind,
782	.forwardable = snd_pcm_ioplug_forwardable,
783	.forward = snd_pcm_ioplug_forward,
784	.writei = snd_pcm_ioplug_writei,
785	.writen = snd_pcm_ioplug_writen,
786	.readi = snd_pcm_ioplug_readi,
787	.readn = snd_pcm_ioplug_readn,
788	.avail_update = snd_pcm_ioplug_avail_update,
789	.mmap_commit = snd_pcm_ioplug_mmap_commit,
790	.htimestamp = snd_pcm_generic_real_htimestamp,
791	.poll_descriptors_count = snd_pcm_ioplug_poll_descriptors_count,
792	.poll_descriptors = snd_pcm_ioplug_poll_descriptors,
793	.poll_revents = snd_pcm_ioplug_poll_revents,
794};
795
796#endif /* !DOC_HIDDEN */
797
798/*
799 * Exported functions
800 */
801
802/*! \page pcm_external_plugins PCM External Plugin SDK
803
804\section pcm_ioplug External Plugin: I/O Plugin
805
806The I/O-type plugin is a PCM plugin to work as the input or output terminal point,
807i.e. as a user-space PCM driver.
808
809The new plugin is created via #snd_pcm_ioplug_create() function.
810The first argument is a pointer of the pluging information.  Some of
811this struct must be initialized in prior to call
812#snd_pcm_ioplug_create().  Then the function fills other fields in
813return.  The rest arguments, name, stream and mode, are usually
814identical with the values passed from the ALSA plugin constructor.
815
816The following fields are mandatory: version, name, callback.
817Otherfields are optional and should be initialized with zero.
818
819The constant #SND_PCM_IOPLUG_VERSION must be passed to the version
820field for the version check in alsa-lib.  A non-NULL ASCII string
821has to be passed to the name field.  The callback field contains the
822table of callback functions for this plugin (defined as
823#snd_pcm_ioplug_callback_t).
824
825flags field specifies the optional bit-flags.  poll_fd and poll_events
826specify the poll file descriptor and the corresponding poll events
827(POLLIN, POLLOUT) for the plugin.  If the plugin requires multiple
828poll descriptors or poll descriptor(s) dynamically varying, set
829poll_descriptors and poll_descriptors_count callbacks to the callback
830table.  Then the poll_fd and poll_events field are ignored.
831
832mmap_rw specifies whether the plugin behaves in the pseudo mmap mode.
833When this value is set to 1, the plugin creates always a local buffer
834and performs read/write calls using this buffer as if it's mmapped.
835The address of local buffer can be obtained via
836#snd_pcm_ioplug_mmap_areas() function.
837When poll_fd, poll_events and mmap_rw fields are changed after
838#snd_pcm_ioplug_create(), call #snd_pcm_ioplug_reinit_status() to
839reflect the changes.
840
841The driver can set an arbitrary value (pointer) to private_data
842field to refer its own data in the callbacks.
843
844The rest fields are filled by #snd_pcm_ioplug_create().  The pcm field
845is the resultant PCM handle.  The others are the current status of the
846PCM.
847
848The callback functions in #snd_pcm_ioplug_callback_t define the real
849behavior of the driver.
850At least, start, stop and pointer callbacks must be given.  Other
851callbacks are optional.  The start and stop callbacks are called when
852the PCM stream is started and stopped, repsectively.  The pointer
853callback returns the current DMA position, which may be called at any
854time.
855
856The transfer callback is called when any data transfer happens.  It
857receives the area array, offset and the size to transfer.  The area
858array contains the array of snd_pcm_channel_area_t with the elements
859of number of channels.
860
861When the PCM is closed, close callback is called.  If the driver
862allocates any internal buffers, they should be released in this
863callback.  The hw_params and hw_free callbacks are called when
864hw_params are set and reset, respectively.  Note that they may be
865called multiple times according to the application.  Similarly,
866sw_params callback is called when sw_params is set or changed.
867
868The prepare, drain, pause and resume callbacks are called when
869#snd_pcm_prepare(), #snd_pcm_drain(), #snd_pcm_pause(), and
870#snd_pcm_resume() are called.  The poll_descriptors_count and
871poll_descriptors callbacks are used to return the multiple or dynamic
872poll descriptors as mentioned above.  The poll_revents callback is
873used to modify poll events.  If the driver needs to mangle the native
874poll events to proper poll events for PCM, you can do it in this
875callback.
876
877Finally, the dump callback is used to print the status of the plugin.
878
879The hw_params constraints can be defined via either
880#snd_pcm_ioplug_set_param_minmax() and #snd_pcm_ioplug_set_param_list()
881functions after calling #snd_pcm_ioplug_create().
882The former defines the minimal and maximal acceptable values for the
883given hw_params parameter (SND_PCM_IOPLUG_HW_XXX).
884This function can't be used for the format parameter.  The latter
885function specifies the available parameter values as the list.
886
887To clear the parameter constraints, call #snd_pcm_ioplug_params_reset() function.
888
889*/
890
891/**
892 * \brief Create an ioplug instance
893 * \param ioplug the ioplug handle
894 * \param name name of PCM
895 * \param stream stream direction
896 * \param mode PCM open mode
897 * \return 0 if successful, or a negative error code
898 *
899 * Creates the ioplug instance.
900 *
901 * The callback is the mandatory field of ioplug handle.  At least, start, stop and
902 * pointer callbacks must be set before calling this function.
903 *
904 */
905int snd_pcm_ioplug_create(snd_pcm_ioplug_t *ioplug, const char *name,
906			  snd_pcm_stream_t stream, int mode)
907{
908	ioplug_priv_t *io;
909	int err;
910	snd_pcm_t *pcm;
911
912	assert(ioplug && ioplug->callback);
913	assert(ioplug->callback->start &&
914	       ioplug->callback->stop &&
915	       ioplug->callback->pointer);
916
917	/* We support 1.0.0 to current */
918	if (ioplug->version < 0x010000 ||
919	    ioplug->version > SND_PCM_IOPLUG_VERSION) {
920		SNDERR("ioplug: Plugin version mismatch: 0x%x\n",
921		       ioplug->version);
922		return -ENXIO;
923	}
924
925	io = calloc(1, sizeof(*io));
926	if (! io)
927		return -ENOMEM;
928
929	io->data = ioplug;
930	ioplug->state = SND_PCM_STATE_OPEN;
931	ioplug->stream = stream;
932
933	err = snd_pcm_new(&pcm, SND_PCM_TYPE_IOPLUG, name, stream, mode);
934	if (err < 0) {
935		free(io);
936		return err;
937	}
938
939	ioplug->pcm = pcm;
940	pcm->ops = &snd_pcm_ioplug_ops;
941	pcm->fast_ops = &snd_pcm_ioplug_fast_ops;
942	pcm->private_data = io;
943
944	snd_pcm_set_hw_ptr(pcm, &ioplug->hw_ptr, -1, 0);
945	snd_pcm_set_appl_ptr(pcm, &ioplug->appl_ptr, -1, 0);
946
947	snd_pcm_ioplug_reinit_status(ioplug);
948
949	return 0;
950}
951
952/**
953 * \brief Delete the ioplug instance
954 * \param ioplug the ioplug handle
955 * \return 0 if successful, or a negative error code
956 */
957int snd_pcm_ioplug_delete(snd_pcm_ioplug_t *ioplug)
958{
959	return snd_pcm_close(ioplug->pcm);
960}
961
962
963/**
964 * \brief Reset ioplug parameters
965 * \param ioplug the ioplug handle
966 *
967 * Resets the all parameters for the given ioplug handle.
968 */
969void snd_pcm_ioplug_params_reset(snd_pcm_ioplug_t *ioplug)
970{
971	ioplug_priv_t *io = ioplug->pcm->private_data;
972	clear_io_params(io);
973}
974
975/**
976 * \brief Set parameter as the list
977 * \param ioplug the ioplug handle
978 * \param type parameter type
979 * \param num_list number of available values
980 * \param list the list of available values
981 * \return 0 if successful, or a negative error code
982 *
983 * Sets the parameter as the list.
984 * The available values of the given parameter type is restricted to the ones of the given list.
985 */
986int snd_pcm_ioplug_set_param_list(snd_pcm_ioplug_t *ioplug, int type, unsigned int num_list, const unsigned int *list)
987{
988	ioplug_priv_t *io = ioplug->pcm->private_data;
989	if (type < 0 && type >= SND_PCM_IOPLUG_HW_PARAMS) {
990		SNDERR("IOPLUG: invalid parameter type %d", type);
991		return -EINVAL;
992	}
993	if (type == SND_PCM_IOPLUG_HW_PERIODS)
994		io->params[type].integer = 1;
995	return snd_ext_parm_set_list(&io->params[type], num_list, list);
996}
997
998/**
999 * \brief Set parameter as the min/max values
1000 * \param ioplug the ioplug handle
1001 * \param type parameter type
1002 * \param min the minimum value
1003 * \param max the maximum value
1004 * \return 0 if successful, or a negative error code
1005 *
1006 * Sets the parameter as the min/max values.
1007 * The available values of the given parameter type is restricted between the given
1008 * minimum and maximum values.
1009 */
1010int snd_pcm_ioplug_set_param_minmax(snd_pcm_ioplug_t *ioplug, int type, unsigned int min, unsigned int max)
1011{
1012	ioplug_priv_t *io = ioplug->pcm->private_data;
1013	if (type < 0 && type >= SND_PCM_IOPLUG_HW_PARAMS) {
1014		SNDERR("IOPLUG: invalid parameter type %d", type);
1015		return -EINVAL;
1016	}
1017	if (type == SND_PCM_IOPLUG_HW_ACCESS || type == SND_PCM_IOPLUG_HW_FORMAT) {
1018		SNDERR("IOPLUG: invalid parameter type %d", type);
1019		return -EINVAL;
1020	}
1021	if (type == SND_PCM_IOPLUG_HW_PERIODS)
1022		io->params[type].integer = 1;
1023	return snd_ext_parm_set_minmax(&io->params[type], min, max);
1024}
1025
1026/**
1027 * \brief Reinitialize the poll and mmap status
1028 * \param ioplug the ioplug handle
1029 * \return 0 if successful, or a negative error code
1030 *
1031 * Reinitializes the poll and the mmap status of the PCM.
1032 * Call this function to propagate the status change in the ioplug instance to
1033 * its PCM internals.
1034 */
1035int snd_pcm_ioplug_reinit_status(snd_pcm_ioplug_t *ioplug)
1036{
1037	ioplug->pcm->poll_fd = ioplug->poll_fd;
1038	ioplug->pcm->poll_events = ioplug->poll_events;
1039	ioplug->pcm->monotonic = (ioplug->flags & SND_PCM_IOPLUG_FLAG_MONOTONIC) != 0;
1040	ioplug->pcm->mmap_rw = ioplug->mmap_rw;
1041	return 0;
1042}
1043
1044/**
1045 * \brief Get mmap area of ioplug
1046 * \param ioplug the ioplug handle
1047 * \return the mmap channel areas if available, or NULL
1048 *
1049 * Returns the mmap channel areas if available.  When mmap_rw field is not set,
1050 * this function always returns NULL.
1051 */
1052const snd_pcm_channel_area_t *snd_pcm_ioplug_mmap_areas(snd_pcm_ioplug_t *ioplug)
1053{
1054	if (ioplug->mmap_rw)
1055		return snd_pcm_mmap_areas(ioplug->pcm);
1056	return NULL;
1057}
1058
1059/**
1060 * \brief Change the ioplug PCM status
1061 * \param ioplug the ioplug handle
1062 * \param state the PCM status
1063 * \return zero if successful or a negative error code
1064 *
1065 * Changes the PCM status of the ioplug to the given value.
1066 * This function can be used for external plugins to notify the status
1067 * change, e.g. XRUN.
1068 */
1069int snd_pcm_ioplug_set_state(snd_pcm_ioplug_t *ioplug, snd_pcm_state_t state)
1070{
1071	ioplug->state = state;
1072	return 0;
1073}
1074