• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
snd_pcm_ioplug_hw_ptr_update(snd_pcm_t * pcm)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 
snd_pcm_ioplug_info(snd_pcm_t * pcm,snd_pcm_info_t * info)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 
snd_pcm_ioplug_channel_info(snd_pcm_t * pcm,snd_pcm_channel_info_t * info)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 
snd_pcm_ioplug_delay(snd_pcm_t * pcm,snd_pcm_sframes_t * delayp)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 
snd_pcm_ioplug_status(snd_pcm_t * pcm,snd_pcm_status_t * status)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 
snd_pcm_ioplug_state(snd_pcm_t * pcm)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 
snd_pcm_ioplug_hwsync(snd_pcm_t * pcm)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 
snd_pcm_ioplug_reset(snd_pcm_t * pcm)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 
snd_pcm_ioplug_prepare(snd_pcm_t * pcm)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 */
rule_mul(snd_pcm_hw_params_t * params,int x,int a,int 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 */
rule_div(snd_pcm_hw_params_t * params,int x,int a,int 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 */
rule_muldivk(snd_pcm_hw_params_t * params,int x,int a,int b,int 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 */
rule_mulkdiv(snd_pcm_hw_params_t * params,int x,int a,int k,int 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 */
refine_time_and_size(snd_pcm_hw_params_t * params,int time,int size,int 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 */
refine_back_time_and_size(snd_pcm_hw_params_t * params,int time,int size,int 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 
snd_pcm_ioplug_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)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 
snd_pcm_ioplug_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)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 
snd_pcm_ioplug_hw_free(snd_pcm_t * pcm)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 
snd_pcm_ioplug_sw_params(snd_pcm_t * pcm,snd_pcm_sw_params_t * params)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 
snd_pcm_ioplug_start(snd_pcm_t * pcm)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 
snd_pcm_ioplug_drop(snd_pcm_t * pcm)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 
ioplug_drain_via_poll(snd_pcm_t * pcm)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, -1) < 0)
526 			break;
527 	}
528 
529 	return 0; /* force to drop at error */
530 }
531 
532 /* need own locking */
snd_pcm_ioplug_drain(snd_pcm_t * pcm)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 
snd_pcm_ioplug_pause(snd_pcm_t * pcm,int enable)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 
snd_pcm_ioplug_rewindable(snd_pcm_t * pcm)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 
snd_pcm_ioplug_rewind(snd_pcm_t * pcm,snd_pcm_uframes_t frames)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 
snd_pcm_ioplug_forwardable(snd_pcm_t * pcm)611 static snd_pcm_sframes_t snd_pcm_ioplug_forwardable(snd_pcm_t *pcm)
612 {
613 	return snd_pcm_mmap_avail(pcm);
614 }
615 
snd_pcm_ioplug_forward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)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 */
snd_pcm_ioplug_resume(snd_pcm_t * pcm)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 */
ioplug_priv_transfer_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)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 
snd_pcm_ioplug_writei(snd_pcm_t * pcm,const void * buffer,snd_pcm_uframes_t size)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 
snd_pcm_ioplug_writen(snd_pcm_t * pcm,void ** bufs,snd_pcm_uframes_t size)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 
snd_pcm_ioplug_readi(snd_pcm_t * pcm,void * buffer,snd_pcm_uframes_t size)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 
snd_pcm_ioplug_readn(snd_pcm_t * pcm,void ** bufs,snd_pcm_uframes_t size)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 
snd_pcm_ioplug_mmap_begin_capture(snd_pcm_t * pcm,const snd_pcm_channel_area_t ** areas,snd_pcm_uframes_t * offset,snd_pcm_uframes_t * frames)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 
snd_pcm_ioplug_mmap_begin(snd_pcm_t * pcm,const snd_pcm_channel_area_t ** areas,snd_pcm_uframes_t * offset,snd_pcm_uframes_t * frames)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 
snd_pcm_ioplug_mmap_commit(snd_pcm_t * pcm,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)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 
snd_pcm_ioplug_avail_update(snd_pcm_t * pcm)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 
snd_pcm_ioplug_nonblock(snd_pcm_t * pcm,int nonblock)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 
snd_pcm_ioplug_poll_descriptors_count(snd_pcm_t * pcm)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 
snd_pcm_ioplug_poll_descriptors(snd_pcm_t * pcm,struct pollfd * pfds,unsigned int space)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 
snd_pcm_ioplug_poll_revents(snd_pcm_t * pcm,struct pollfd * pfds,unsigned int nfds,unsigned short * revents)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 
snd_pcm_ioplug_mmap(snd_pcm_t * pcm ATTRIBUTE_UNUSED)826 static int snd_pcm_ioplug_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
827 {
828 	return 0;
829 }
830 
snd_pcm_ioplug_async(snd_pcm_t * pcm ATTRIBUTE_UNUSED,int sig ATTRIBUTE_UNUSED,pid_t pid ATTRIBUTE_UNUSED)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 
snd_pcm_ioplug_munmap(snd_pcm_t * pcm ATTRIBUTE_UNUSED)838 static int snd_pcm_ioplug_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
839 {
840 	return 0;
841 }
842 
snd_pcm_ioplug_query_chmaps(snd_pcm_t * pcm)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 
snd_pcm_ioplug_get_chmap(snd_pcm_t * pcm)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 
snd_pcm_ioplug_set_chmap(snd_pcm_t * pcm,const snd_pcm_chmap_t * map)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 
snd_pcm_ioplug_dump(snd_pcm_t * pcm,snd_output_t * out)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 
clear_io_params(ioplug_priv_t * io)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 
snd_pcm_ioplug_close(snd_pcm_t * pcm)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  */
snd_pcm_ioplug_create(snd_pcm_ioplug_t * ioplug,const char * name,snd_pcm_stream_t stream,int mode)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 		SNDERR("ioplug: Plugin version mismatch: 0x%x\n",
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  */
snd_pcm_ioplug_delete(snd_pcm_ioplug_t * ioplug)1126 int 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  */
snd_pcm_ioplug_params_reset(snd_pcm_ioplug_t * ioplug)1138 void 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  */
snd_pcm_ioplug_set_param_list(snd_pcm_ioplug_t * ioplug,int type,unsigned int num_list,const unsigned int * list)1155 int 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  */
snd_pcm_ioplug_set_param_minmax(snd_pcm_ioplug_t * ioplug,int type,unsigned int min,unsigned int max)1179 int 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  */
snd_pcm_ioplug_reinit_status(snd_pcm_ioplug_t * ioplug)1204 int 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  */
snd_pcm_ioplug_mmap_areas(snd_pcm_ioplug_t * ioplug)1224 const 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  */
snd_pcm_ioplug_set_state(snd_pcm_ioplug_t * ioplug,snd_pcm_state_t state)1241 int 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  */
snd_pcm_ioplug_avail(const snd_pcm_ioplug_t * const ioplug,const snd_pcm_uframes_t hw_ptr,const snd_pcm_uframes_t appl_ptr)1255 snd_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  */
snd_pcm_ioplug_hw_avail(const snd_pcm_ioplug_t * const ioplug,const snd_pcm_uframes_t hw_ptr,const snd_pcm_uframes_t appl_ptr)1270 snd_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