• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * \file pcm/pcm_extplug.c
3  * \ingroup Plugin_SDK
4  * \brief External Filter Plugin SDK
5  * \author Takashi Iwai <tiwai@suse.de>
6  * \date 2005
7  */
8 /*
9  *  PCM - External Filter 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_plugin.h"
31 #include "pcm_extplug.h"
32 #include "pcm_ext_parm.h"
33 
34 #ifndef PIC
35 /* entry for static linking */
36 const char *_snd_module_pcm_extplug = "";
37 #endif
38 
39 #ifndef DOC_HIDDEN
40 
41 typedef struct snd_pcm_extplug_priv {
42 	snd_pcm_plugin_t plug;
43 	snd_pcm_extplug_t *data;
44 	struct snd_ext_parm params[SND_PCM_EXTPLUG_HW_PARAMS];
45 	struct snd_ext_parm sparams[SND_PCM_EXTPLUG_HW_PARAMS];
46 } extplug_priv_t;
47 
48 static const int hw_params_type[SND_PCM_EXTPLUG_HW_PARAMS] = {
49 	[SND_PCM_EXTPLUG_HW_FORMAT] = SND_PCM_HW_PARAM_FORMAT,
50 	[SND_PCM_EXTPLUG_HW_CHANNELS] = SND_PCM_HW_PARAM_CHANNELS
51 };
52 
53 #define is_mask_type(i) (hw_params_type[i] < SND_PCM_HW_PARAM_FIRST_INTERVAL)
54 
55 static const unsigned int excl_parbits[SND_PCM_EXTPLUG_HW_PARAMS] = {
56 	[SND_PCM_EXTPLUG_HW_FORMAT] = (SND_PCM_HW_PARBIT_FORMAT|
57 				       SND_PCM_HW_PARBIT_SUBFORMAT |
58 				       SND_PCM_HW_PARBIT_SAMPLE_BITS),
59 	[SND_PCM_EXTPLUG_HW_CHANNELS] = (SND_PCM_HW_PARBIT_CHANNELS|
60 					 SND_PCM_HW_PARBIT_FRAME_BITS),
61 };
62 
63 /*
64  * set min/max values for the given parameter
65  */
snd_ext_parm_set_minmax(struct snd_ext_parm * parm,unsigned int min,unsigned int max)66 int snd_ext_parm_set_minmax(struct snd_ext_parm *parm, unsigned int min, unsigned int max)
67 {
68 	parm->num_list = 0;
69 	free(parm->list);
70 	parm->list = NULL;
71 	parm->min = min;
72 	parm->max = max;
73 	parm->active = 1;
74 	return 0;
75 }
76 
77 /*
78  * set the list of available values for the given parameter
79  */
val_compar(const void * ap,const void * bp)80 static int val_compar(const void *ap, const void *bp)
81 {
82 	return *(const unsigned int *)ap - *(const unsigned int *)bp;
83 }
84 
snd_ext_parm_set_list(struct snd_ext_parm * parm,unsigned int num_list,const unsigned int * list)85 int snd_ext_parm_set_list(struct snd_ext_parm *parm, unsigned int num_list, const unsigned int *list)
86 {
87 	unsigned int *new_list;
88 
89 	new_list = malloc(sizeof(*new_list) * num_list);
90 	if (new_list == NULL)
91 		return -ENOMEM;
92 	memcpy(new_list, list, sizeof(*new_list) * num_list);
93 	qsort(new_list, num_list, sizeof(*new_list), val_compar);
94 
95 	free(parm->list);
96 	parm->num_list = num_list;
97 	parm->list = new_list;
98 	parm->active = 1;
99 	return 0;
100 }
101 
snd_ext_parm_clear(struct snd_ext_parm * parm)102 void snd_ext_parm_clear(struct snd_ext_parm *parm)
103 {
104 	free(parm->list);
105 	memset(parm, 0, sizeof(*parm));
106 }
107 
108 /*
109  * limit the interval to the given list
110  */
snd_interval_list(snd_interval_t * ival,int num_list,unsigned int * list)111 int snd_interval_list(snd_interval_t *ival, int num_list, unsigned int *list)
112 {
113 	int imin, imax;
114 	int changed = 0;
115 
116 	if (snd_interval_empty(ival))
117 		return -ENOENT;
118 	for (imin = 0; imin < num_list; imin++) {
119 		if (ival->min == list[imin] && ! ival->openmin)
120 			break;
121 		if (ival->min <= list[imin]) {
122 			ival->min = list[imin];
123 			ival->openmin = 0;
124 			changed = 1;
125 			break;
126 		}
127 	}
128 	if (imin >= num_list)
129 		return -EINVAL;
130 	for (imax = num_list - 1; imax >= imin; imax--) {
131 		if (ival->max == list[imax] && ! ival->openmax)
132 			break;
133 		if (ival->max >= list[imax]) {
134 			ival->max = list[imax];
135 			ival->openmax = 0;
136 			changed = 1;
137 			break;
138 		}
139 	}
140 	if (imax < imin)
141 		return -EINVAL;
142 	return changed;
143 }
144 
145 /*
146  * refine the interval parameter
147  */
snd_ext_parm_interval_refine(snd_interval_t * ival,struct snd_ext_parm * parm,int type)148 int snd_ext_parm_interval_refine(snd_interval_t *ival, struct snd_ext_parm *parm, int type)
149 {
150 	parm += type;
151 	if (! parm->active)
152 		return 0;
153 	ival->integer |= parm->integer;
154 	if (parm->num_list) {
155 		return snd_interval_list(ival, parm->num_list, parm->list);
156 	} else if (parm->min || parm->max) {
157 		snd_interval_t t;
158 		memset(&t, 0, sizeof(t));
159 		snd_interval_set_minmax(&t, parm->min, parm->max);
160 		t.integer = ival->integer;
161 		return snd_interval_refine(ival, &t);
162 	}
163 	return 0;
164 }
165 
166 /*
167  * refine the mask parameter
168  */
snd_ext_parm_mask_refine(snd_mask_t * mask,struct snd_ext_parm * parm,int type)169 int snd_ext_parm_mask_refine(snd_mask_t *mask, struct snd_ext_parm *parm, int type)
170 {
171 	snd_mask_t bits;
172 	unsigned int i;
173 
174 	parm += type;
175 	if (!parm->active)
176 		return 0;
177 	memset(&bits, 0, sizeof(bits));
178 	for (i = 0; i < parm->num_list; i++)
179 		bits.bits[parm->list[i] / 32] |= 1U << (parm->list[i] % 32);
180 	return snd_mask_refine(mask, &bits);
181 }
182 
183 
184 /*
185  * hw_refine callback
186  */
extplug_hw_refine(snd_pcm_hw_params_t * hw_params,struct snd_ext_parm * parm)187 static int extplug_hw_refine(snd_pcm_hw_params_t *hw_params,
188 			     struct snd_ext_parm *parm)
189 {
190 	int i, err, change = 0;
191 	for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
192 		int type = hw_params_type[i];
193 		if (is_mask_type(i))
194 			err = snd_ext_parm_mask_refine(hw_param_mask(hw_params, type),
195 						       parm, i);
196 		else
197 			err = snd_ext_parm_interval_refine(hw_param_interval(hw_params, type),
198 							   parm, i);
199 		if (err < 0)
200 			return err;
201 		change |= err;
202 	}
203 	return change;
204 }
205 
snd_pcm_extplug_hw_refine_cprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)206 static int snd_pcm_extplug_hw_refine_cprepare(snd_pcm_t *pcm,
207 					      snd_pcm_hw_params_t *params)
208 {
209 	extplug_priv_t *ext = pcm->private_data;
210 	int err;
211 	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
212 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
213 					 &access_mask);
214 	if (err < 0)
215 		return err;
216 	err = extplug_hw_refine(params, ext->params);
217 	if (err < 0)
218 		return err;
219 	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
220 	return 0;
221 }
222 
snd_pcm_extplug_hw_refine_sprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * sparams)223 static int snd_pcm_extplug_hw_refine_sprepare(snd_pcm_t *pcm,
224 					      snd_pcm_hw_params_t *sparams)
225 {
226 	extplug_priv_t *ext = pcm->private_data;
227 	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
228 	_snd_pcm_hw_params_any(sparams);
229 	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
230 				   &saccess_mask);
231 	extplug_hw_refine(sparams, ext->sparams);
232 	return 0;
233 }
234 
get_links(struct snd_ext_parm * params)235 static unsigned int get_links(struct snd_ext_parm *params)
236 {
237 	int i;
238 	unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
239 			      SND_PCM_HW_PARBIT_SUBFORMAT |
240 			      SND_PCM_HW_PARBIT_SAMPLE_BITS |
241 			      SND_PCM_HW_PARBIT_CHANNELS |
242 			      SND_PCM_HW_PARBIT_FRAME_BITS |
243 			      SND_PCM_HW_PARBIT_RATE |
244 			      SND_PCM_HW_PARBIT_PERIODS |
245 			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
246 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
247 			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
248 			      SND_PCM_HW_PARBIT_BUFFER_TIME |
249 			      SND_PCM_HW_PARBIT_TICK_TIME);
250 
251 	for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
252 		if (params[i].active && !params[i].keep_link)
253 			links &= ~excl_parbits[i];
254 	}
255 	return links;
256 }
257 
snd_pcm_extplug_hw_refine_schange(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)258 static int snd_pcm_extplug_hw_refine_schange(snd_pcm_t *pcm,
259 					     snd_pcm_hw_params_t *params,
260 					     snd_pcm_hw_params_t *sparams)
261 {
262 	extplug_priv_t *ext = pcm->private_data;
263 	unsigned int links = get_links(ext->sparams);
264 
265 	return _snd_pcm_hw_params_refine(sparams, links, params);
266 }
267 
snd_pcm_extplug_hw_refine_cchange(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)268 static int snd_pcm_extplug_hw_refine_cchange(snd_pcm_t *pcm,
269 					     snd_pcm_hw_params_t *params,
270 					     snd_pcm_hw_params_t *sparams)
271 {
272 	extplug_priv_t *ext = pcm->private_data;
273 	unsigned int links = get_links(ext->params);
274 
275 	return _snd_pcm_hw_params_refine(params, links, sparams);
276 }
277 
snd_pcm_extplug_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)278 static int snd_pcm_extplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
279 {
280 	int err = snd_pcm_hw_refine_slave(pcm, params,
281 				       snd_pcm_extplug_hw_refine_cprepare,
282 				       snd_pcm_extplug_hw_refine_cchange,
283 				       snd_pcm_extplug_hw_refine_sprepare,
284 				       snd_pcm_extplug_hw_refine_schange,
285 				       snd_pcm_generic_hw_refine);
286 	return err;
287 }
288 
289 /*
290  * hw_params callback
291  */
snd_pcm_extplug_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)292 static int snd_pcm_extplug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
293 {
294 
295 	extplug_priv_t *ext = pcm->private_data;
296 	snd_pcm_t *slave = ext->plug.gen.slave;
297 	int err = snd_pcm_hw_params_slave(pcm, params,
298 					  snd_pcm_extplug_hw_refine_cchange,
299 					  snd_pcm_extplug_hw_refine_sprepare,
300 					  snd_pcm_extplug_hw_refine_schange,
301 					  snd_pcm_generic_hw_params);
302 	if (err < 0)
303 		return err;
304 	ext->data->slave_format = slave->format;
305 	ext->data->slave_subformat = slave->subformat;
306 	ext->data->slave_channels = slave->channels;
307 	ext->data->rate = slave->rate;
308 	INTERNAL(snd_pcm_hw_params_get_format)(params, &ext->data->format);
309 	INTERNAL(snd_pcm_hw_params_get_subformat)(params, &ext->data->subformat);
310 	INTERNAL(snd_pcm_hw_params_get_channels)(params, &ext->data->channels);
311 
312 	if (ext->data->callback->hw_params) {
313 		err = ext->data->callback->hw_params(ext->data, params);
314 		if (err < 0)
315 			return err;
316 	}
317 	return 0;
318 }
319 
320 /*
321  * hw_free callback
322  */
snd_pcm_extplug_hw_free(snd_pcm_t * pcm)323 static int snd_pcm_extplug_hw_free(snd_pcm_t *pcm)
324 {
325 	extplug_priv_t *ext = pcm->private_data;
326 
327 	snd_pcm_hw_free(ext->plug.gen.slave);
328 	if (ext->data->callback->hw_free)
329 		return ext->data->callback->hw_free(ext->data);
330 	return 0;
331 }
332 
333 /*
334  * write_areas skeleton - call transfer callback
335  */
336 static snd_pcm_uframes_t
snd_pcm_extplug_write_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)337 snd_pcm_extplug_write_areas(snd_pcm_t *pcm,
338 			    const snd_pcm_channel_area_t *areas,
339 			    snd_pcm_uframes_t offset,
340 			    snd_pcm_uframes_t size,
341 			    const snd_pcm_channel_area_t *slave_areas,
342 			    snd_pcm_uframes_t slave_offset,
343 			    snd_pcm_uframes_t *slave_sizep)
344 {
345 	extplug_priv_t *ext = pcm->private_data;
346 
347 	if (size > *slave_sizep)
348 		size = *slave_sizep;
349 	size = ext->data->callback->transfer(ext->data, slave_areas, slave_offset,
350 					     areas, offset, size);
351 	*slave_sizep = size;
352 	return size;
353 }
354 
355 /*
356  * read_areas skeleton - call transfer callback
357  */
358 static snd_pcm_uframes_t
snd_pcm_extplug_read_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)359 snd_pcm_extplug_read_areas(snd_pcm_t *pcm,
360 			   const snd_pcm_channel_area_t *areas,
361 			   snd_pcm_uframes_t offset,
362 			   snd_pcm_uframes_t size,
363 			   const snd_pcm_channel_area_t *slave_areas,
364 			   snd_pcm_uframes_t slave_offset,
365 			   snd_pcm_uframes_t *slave_sizep)
366 {
367 	extplug_priv_t *ext = pcm->private_data;
368 
369 	if (size > *slave_sizep)
370 		size = *slave_sizep;
371 	size = ext->data->callback->transfer(ext->data, areas, offset,
372 					     slave_areas, slave_offset, size);
373 	*slave_sizep = size;
374 	return size;
375 }
376 
377 /*
378  * call init callback
379  */
snd_pcm_extplug_init(snd_pcm_t * pcm)380 static int snd_pcm_extplug_init(snd_pcm_t *pcm)
381 {
382 	extplug_priv_t *ext = pcm->private_data;
383 	return ext->data->callback->init(ext->data);
384 }
385 
386 /*
387  * dump setup
388  */
snd_pcm_extplug_dump(snd_pcm_t * pcm,snd_output_t * out)389 static void snd_pcm_extplug_dump(snd_pcm_t *pcm, snd_output_t *out)
390 {
391 	extplug_priv_t *ext = pcm->private_data;
392 
393 	if (ext->data->callback->dump)
394 		ext->data->callback->dump(ext->data, out);
395 	else {
396 		if (ext->data->name)
397 			snd_output_printf(out, "%s\n", ext->data->name);
398 		else
399 			snd_output_printf(out, "External PCM Plugin\n");
400 		if (pcm->setup) {
401 			snd_output_printf(out, "Its setup is:\n");
402 			snd_pcm_dump_setup(pcm, out);
403 		}
404 	}
405 	snd_output_printf(out, "Slave: ");
406 	snd_pcm_dump(ext->plug.gen.slave, out);
407 }
408 
clear_ext_params(extplug_priv_t * ext)409 static void clear_ext_params(extplug_priv_t *ext)
410 {
411 	int i;
412 	for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
413 		snd_ext_parm_clear(&ext->params[i]);
414 		snd_ext_parm_clear(&ext->sparams[i]);
415 	}
416 }
417 
snd_pcm_extplug_close(snd_pcm_t * pcm)418 static int snd_pcm_extplug_close(snd_pcm_t *pcm)
419 {
420 	extplug_priv_t *ext = pcm->private_data;
421 
422 	snd_pcm_close(ext->plug.gen.slave);
423 	clear_ext_params(ext);
424 	if (ext->data->callback->close)
425 		ext->data->callback->close(ext->data);
426 	free(ext);
427 	return 0;
428 }
429 
snd_pcm_extplug_query_chmaps(snd_pcm_t * pcm)430 static snd_pcm_chmap_query_t **snd_pcm_extplug_query_chmaps(snd_pcm_t *pcm)
431 {
432 	extplug_priv_t *ext = pcm->private_data;
433 
434 	if (ext->data->version >= 0x010002 &&
435 	    ext->data->callback->query_chmaps)
436 		return ext->data->callback->query_chmaps(ext->data);
437 	return snd_pcm_generic_query_chmaps(pcm);
438 }
439 
snd_pcm_extplug_get_chmap(snd_pcm_t * pcm)440 static snd_pcm_chmap_t *snd_pcm_extplug_get_chmap(snd_pcm_t *pcm)
441 {
442 	extplug_priv_t *ext = pcm->private_data;
443 
444 	if (ext->data->version >= 0x010002 &&
445 	    ext->data->callback->get_chmap)
446 		return ext->data->callback->get_chmap(ext->data);
447 	return snd_pcm_generic_get_chmap(pcm);
448 }
449 
snd_pcm_extplug_set_chmap(snd_pcm_t * pcm,const snd_pcm_chmap_t * map)450 static int snd_pcm_extplug_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
451 {
452 	extplug_priv_t *ext = pcm->private_data;
453 
454 	if (ext->data->version >= 0x010002 &&
455 	    ext->data->callback->set_chmap)
456 		return ext->data->callback->set_chmap(ext->data, map);
457 	return snd_pcm_generic_set_chmap(pcm, map);
458 }
459 
460 static const snd_pcm_ops_t snd_pcm_extplug_ops = {
461 	.close = snd_pcm_extplug_close,
462 	.info = snd_pcm_generic_info,
463 	.hw_refine = snd_pcm_extplug_hw_refine,
464 	.hw_params = snd_pcm_extplug_hw_params,
465 	.hw_free = snd_pcm_extplug_hw_free,
466 	.sw_params = snd_pcm_generic_sw_params,
467 	.channel_info = snd_pcm_generic_channel_info,
468 	.dump = snd_pcm_extplug_dump,
469 	.nonblock = snd_pcm_generic_nonblock,
470 	.async = snd_pcm_generic_async,
471 	.mmap = snd_pcm_generic_mmap,
472 	.munmap = snd_pcm_generic_munmap,
473 	.query_chmaps = snd_pcm_extplug_query_chmaps,
474 	.get_chmap = snd_pcm_extplug_get_chmap,
475 	.set_chmap = snd_pcm_extplug_set_chmap,
476 };
477 
478 #endif /* !DOC_HIDDEN */
479 
480 /*
481  * Exported functions
482  */
483 
484 /*! \page pcm_external_plugins PCM External Plugin SDK
485 
486 \section pcm_externals External Plugins
487 
488 The external plugins are implemented in a shared object file located
489 at /usr/lib/alsa-lib (the exact location depends on the build option
490 and asoundrc configuration).  It has to be the file like
491 libasound_module_pcm_MYPLUGIN.so, where MYPLUGIN corresponds to your
492 own plugin name.
493 
494 The entry point of the plugin is defined via
495 #SND_PCM_PLUGIN_DEFINE_FUNC() macro.  This macro defines the function
496 with a proper name to be referred from alsa-lib.  The function takes
497 the following 6 arguments:
498 \code
499 int (snd_pcm_t **pcmp, const char *name, snd_config_t *root,
500 	snd_config_t *conf, snd_pcm_stream_t stream, int mode)
501 \endcode
502 The first argument, pcmp, is the pointer to store the resultant PCM
503 handle.  The arguments name, root, stream and mode are the parameters
504 to be passed to the plugin constructor.  The conf is the configuration
505 tree for the plugin.  The arguments above are defined in the macro
506 itself, so don't use variables with the same names to shadow
507 parameters.
508 
509 After parsing the configuration parameters in the given conf tree,
510 usually you will call the external plugin API function,
511 #snd_pcm_extplug_create() or #snd_pcm_ioplug_create(), depending
512 on the plugin type.  The PCM handle must be filled *pcmp in return.
513 Then this function must return either a value 0 when succeeded, or a
514 negative value as the error code.
515 
516 Finally, add #SND_PCM_PLUGIN_SYMBOL() with the name of your
517 plugin as the argument at the end.  This defines the proper versioned
518 symbol as the reference.
519 
520 The typical code would look like below:
521 \code
522 struct myplug_info {
523 	snd_pcm_extplug_t ext;
524 	int my_own_data;
525 	...
526 };
527 
528 SND_PCM_PLUGIN_DEFINE_FUNC(myplug)
529 {
530 	snd_config_iterator_t i, next;
531 	snd_config_t *slave = NULL;
532 	struct myplug_info *myplug;
533 	int err;
534 
535 	snd_config_for_each(i, next, conf) {
536 		snd_config_t *n = snd_config_iterator_entry(i);
537 		const char *id;
538 		if (snd_config_get_id(n, &id) < 0)
539 			continue;
540 		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
541 			continue;
542 		if (strcmp(id, "slave") == 0) {
543 			slave = n;
544 			continue;
545 		}
546 		if (strcmp(id, "my_own_parameter") == 0) {
547 			....
548 			continue;
549 		}
550 		SNDERR("Unknown field %s", id);
551 		return -EINVAL;
552 	}
553 
554 	if (! slave) {
555 		SNDERR("No slave defined for myplug");
556 		return -EINVAL;
557 	}
558 
559 	myplug = calloc(1, sizeof(*myplug));
560 	if (myplug == NULL)
561 		return -ENOMEM;
562 
563 	myplug->ext.version = SND_PCM_EXTPLUG_VERSION;
564 	myplug->ext.name = "My Own Plugin";
565 	myplug->ext.callback = &my_own_callback;
566 	myplug->ext.private_data = myplug;
567 	....
568 
569 	err = snd_pcm_extplug_create(&myplug->ext, name, root, conf, stream, mode);
570 	if (err < 0) {
571 		myplug_free(myplug);
572 		return err;
573 	}
574 
575 	*pcmp = myplug->ext.pcm;
576 	return 0;
577 }
578 
579 SND_PCM_PLUGIN_SYMBOL(myplug);
580 \endcode
581 
582 Read the codes in alsa-plugins package for the real examples.
583 
584 
585 \section pcm_extplug External Plugin: Filter-Type Plugin
586 
587 The filter-type plugin is a plugin to convert the PCM signals from the input
588 and feeds to the output.  Thus, this plugin always needs a slave PCM as its output.
589 
590 The plugin can modify the format and the channels of the input/output PCM.
591 It can <i>not</i> modify the sample rate (because of simplicity reason).
592 
593 The following fields have to be filled in extplug record before calling
594 #snd_pcm_extplug_create() : version, name, callback.
595 Otherfields are optional and should be initialized with zero.
596 
597 The constant #SND_PCM_EXTPLUG_VERSION must be passed to the version
598 field for the version check in alsa-lib.  A non-NULL ASCII string
599 has to be passed to the name field.  The callback field contains the
600 table of callback functions for this plugin (defined as
601 #snd_pcm_extplug_callback_t).
602 
603 The driver can set an arbitrary value (pointer) to private_data
604 field to refer its own data in the callbacks.
605 
606 The rest fields are filled by #snd_pcm_extplug_create().  The pcm field
607 is the resultant PCM handle.  The others are the current status of the
608 PCM.
609 
610 The callback functions in #snd_pcm_extplug_callback_t define the real
611 behavior of the driver.
612 At least, transfer callback must be given.  This callback is called
613 at each time certain size of data block is transfered to the slave
614 PCM.  Other callbacks are optional.
615 
616 The close callback is called when the PCM is closed.  If the plugin
617 allocates private resources, this is the place to release them
618 again.  The hw_params and hw_free callbacks are called at
619 #snd_pcm_hw_params() and #snd_pcm_hw_free() API calls,
620 respectively.  The last, dump callback, is called for printing the
621 information of the given plugin.
622 
623 The init callback is called when the PCM is at prepare state or any
624 initialization is issued.  Use this callback to reset the PCM instance
625 to a sane initial state.
626 
627 The hw_params constraints can be defined via either
628 #snd_pcm_extplug_set_param_minmax() and #snd_pcm_extplug_set_param_list()
629 functions after calling #snd_pcm_extplug_create().
630 The former defines the minimal and maximal acceptable values for the
631 given hw_params parameter (SND_PCM_EXTPLUG_HW_XXX).
632 This function can't be used for the format parameter.  The latter
633 function specifies the available parameter values as the list.
634 As mentioned above, the rate can't be changed.  Only changeable
635 parameters are sample format and channels.
636 
637 To define the constraints of the slave PCM configuration, use
638 either #snd_pcm_extplug_set_slave_param_minmax() and
639 #snd_pcm_extplug_set_slave_param_list().  The arguments are as same
640 as former functions.
641 
642 To clear the parameter constraints, call #snd_pcm_extplug_params_reset()
643 function.
644 
645 When using snd_pcm_extplug_set_param_*() or snd_pcm_extplug_set_slave_param_*()
646 for any parameter. This parameter is no longer linked between the client and
647 slave PCM. Therefore it could differ and the extplug has to support conversion
648 between all valid parameter configurations. To keep the client and slave
649 parameter linked #snd_pcm_extplug_set_param_link() can be used for the
650 corresponding parameter. For example if the extplug does not support channel nor
651 format conversion the supported client parameters can be limited with
652 snd_pcm_extplug_set_param_*() and afterwards
653 #snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_FORMAT, 1) and
654 #snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1) should be
655 called to keep the client and slave parameters the same.
656 */
657 
658 /**
659  * \brief Create an extplug instance
660  * \param extplug the extplug handle
661  * \param name name of the PCM
662  * \param root configuration tree root
663  * \param slave_conf slave configuration root
664  * \param stream stream direction
665  * \param mode PCM open mode
666  * \return 0 if successful, or a negative error code
667  *
668  * Creates the extplug instance based on the given handle.
669  * The slave_conf argument is mandatory, and usually taken from the config tree of the
670  * PCM plugin as "slave" config value.
671  * name, root, stream and mode arguments are the values used for opening the PCM.
672  *
673  * The callback is the mandatory field of extplug handle.  At least, start, stop and
674  * pointer callbacks must be set before calling this function.
675  */
snd_pcm_extplug_create(snd_pcm_extplug_t * extplug,const char * name,snd_config_t * root,snd_config_t * slave_conf,snd_pcm_stream_t stream,int mode)676 int snd_pcm_extplug_create(snd_pcm_extplug_t *extplug, const char *name,
677 			   snd_config_t *root, snd_config_t *slave_conf,
678 			   snd_pcm_stream_t stream, int mode)
679 {
680 	extplug_priv_t *ext;
681 	int err;
682 	snd_pcm_t *spcm, *pcm;
683 	snd_config_t *sconf;
684 
685 	assert(root);
686 	assert(extplug && extplug->callback);
687 	assert(extplug->callback->transfer);
688 	assert(slave_conf);
689 
690 	/* We support 1.0.0 to current */
691 	if (extplug->version < 0x010000 ||
692 	    extplug->version > SND_PCM_EXTPLUG_VERSION) {
693 		SNDERR("extplug: Plugin version mismatch: 0x%x\n",
694 		       extplug->version);
695 		return -ENXIO;
696 	}
697 
698 	err = snd_pcm_slave_conf(root, slave_conf, &sconf, 0);
699 	if (err < 0)
700 		return err;
701 	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, NULL);
702 	snd_config_delete(sconf);
703 	if (err < 0)
704 		return err;
705 
706 	ext = calloc(1, sizeof(*ext));
707 	if (! ext)
708 		return -ENOMEM;
709 
710 	ext->data = extplug;
711 	extplug->stream = stream;
712 
713 	snd_pcm_plugin_init(&ext->plug);
714 	ext->plug.read = snd_pcm_extplug_read_areas;
715 	ext->plug.write = snd_pcm_extplug_write_areas;
716 	ext->plug.undo_read = snd_pcm_plugin_undo_read_generic;
717 	ext->plug.undo_write = snd_pcm_plugin_undo_write_generic;
718 	ext->plug.gen.slave = spcm;
719 	ext->plug.gen.close_slave = 1;
720 	if (extplug->version >= 0x010001 && extplug->callback->init)
721 		ext->plug.init = snd_pcm_extplug_init;
722 
723 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_EXTPLUG, name, stream, mode);
724 	if (err < 0) {
725 		free(ext);
726 		return err;
727 	}
728 
729 	extplug->pcm = pcm;
730 	pcm->ops = &snd_pcm_extplug_ops;
731 	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
732 	pcm->private_data = ext;
733 	pcm->poll_fd = spcm->poll_fd;
734 	pcm->poll_events = spcm->poll_events;
735 	pcm->tstamp_type = spcm->tstamp_type;
736 	snd_pcm_set_hw_ptr(pcm, &ext->plug.hw_ptr, -1, 0);
737 	snd_pcm_set_appl_ptr(pcm, &ext->plug.appl_ptr, -1, 0);
738 
739 	return 0;
740 }
741 
742 /**
743  * \brief Delete the extplug instance
744  * \param extplug the extplug handle to delete
745  * \return 0 if successful, or a negative error code
746  *
747  * The destructor of extplug instance.
748  * Closes the PCM and deletes the associated resources.
749  */
snd_pcm_extplug_delete(snd_pcm_extplug_t * extplug)750 int snd_pcm_extplug_delete(snd_pcm_extplug_t *extplug)
751 {
752 	return snd_pcm_close(extplug->pcm);
753 }
754 
755 
756 /**
757  * \brief Reset extplug parameters
758  * \param extplug the extplug handle
759  *
760  * Resets the all parameters for the given extplug handle.
761  */
snd_pcm_extplug_params_reset(snd_pcm_extplug_t * extplug)762 void snd_pcm_extplug_params_reset(snd_pcm_extplug_t *extplug)
763 {
764 	extplug_priv_t *ext = extplug->pcm->private_data;
765 	clear_ext_params(ext);
766 }
767 
768 /**
769  * \brief Set slave parameter as the list
770  * \param extplug the extplug handle
771  * \param type parameter type
772  * \param num_list number of available values
773  * \param list the list of available values
774  * \return 0 if successful, or a negative error code
775  *
776  * Sets the slave parameter as the list.
777  * The available values of the given parameter type of the slave PCM is restricted
778  * to the ones of the given list.
779  */
snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t * extplug,int type,unsigned int num_list,const unsigned int * list)780 int snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list)
781 {
782 	extplug_priv_t *ext = extplug->pcm->private_data;
783 	if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
784 		SNDERR("EXTPLUG: invalid parameter type %d", type);
785 		return -EINVAL;
786 	}
787 	return snd_ext_parm_set_list(&ext->sparams[type], num_list, list);
788 }
789 
790 /**
791  * \brief Set slave parameter as the min/max values
792  * \param extplug the extplug handle
793  * \param type parameter type
794  * \param min the minimum value
795  * \param max the maximum value
796  * \return 0 if successful, or a negative error code
797  *
798  * Sets the slave parameter as the min/max values.
799  * The available values of the given parameter type of the slave PCM is restricted
800  * between the given minimum and maximum values.
801  */
snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t * extplug,int type,unsigned int min,unsigned int max)802 int snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max)
803 {
804 	extplug_priv_t *ext = extplug->pcm->private_data;
805 	if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
806 		SNDERR("EXTPLUG: invalid parameter type %d", type);
807 		return -EINVAL;
808 	}
809 	if (is_mask_type(type)) {
810 		SNDERR("EXTPLUG: invalid parameter type %d", type);
811 		return -EINVAL;
812 	}
813 	return snd_ext_parm_set_minmax(&ext->sparams[type], min, max);
814 }
815 
816 /**
817  * \brief Set master parameter as the list
818  * \param extplug the extplug handle
819  * \param type parameter type
820  * \param num_list number of available values
821  * \param list the list of available values
822  * \return 0 if successful, or a negative error code
823  *
824  * Sets the master parameter as the list.
825  * The available values of the given parameter type of this PCM (as input) is restricted
826  * to the ones of the given list.
827  */
snd_pcm_extplug_set_param_list(snd_pcm_extplug_t * extplug,int type,unsigned int num_list,const unsigned int * list)828 int snd_pcm_extplug_set_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list)
829 {
830 	extplug_priv_t *ext = extplug->pcm->private_data;
831 	if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
832 		SNDERR("EXTPLUG: invalid parameter type %d", type);
833 		return -EINVAL;
834 	}
835 	return snd_ext_parm_set_list(&ext->params[type], num_list, list);
836 }
837 
838 /**
839  * \brief Set master parameter as the min/max values
840  * \param extplug the extplug handle
841  * \param type parameter type
842  * \param min the minimum value
843  * \param max the maximum value
844  * \return 0 if successful, or a negative error code
845  *
846  * Sets the master parameter as the min/max values.
847  * The available values of the given parameter type of this PCM (as input) is restricted
848  * between the given minimum and maximum values.
849  */
snd_pcm_extplug_set_param_minmax(snd_pcm_extplug_t * extplug,int type,unsigned int min,unsigned int max)850 int snd_pcm_extplug_set_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max)
851 {
852 	extplug_priv_t *ext = extplug->pcm->private_data;
853 	if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
854 		SNDERR("EXTPLUG: invalid parameter type %d", type);
855 		return -EINVAL;
856 	}
857 	if (is_mask_type(type)) {
858 		SNDERR("EXTPLUG: invalid parameter type %d", type);
859 		return -EINVAL;
860 	}
861 	return snd_ext_parm_set_minmax(&ext->params[type], min, max);
862 }
863 
864 /**
865  * @brief Keep the client and slave format/channels the same if requested. This
866  * is for example useful if this extplug does not support any channel
867  * conversion.
868  * @param extplug the extplug handle
869  * @param type parameter type
870  * @param keep_link if 1 the parameter identified by type will be kept the same
871  * for the client and slave PCM of this extplug
872  * @return 0 if successful, or a negative error code
873  */
snd_pcm_extplug_set_param_link(snd_pcm_extplug_t * extplug,int type,int keep_link)874 int snd_pcm_extplug_set_param_link(snd_pcm_extplug_t *extplug, int type,
875 				   int keep_link)
876 {
877 	extplug_priv_t *ext = extplug->pcm->private_data;
878 
879 	if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
880 		SNDERR("EXTPLUG: invalid parameter type %d", type);
881 		return -EINVAL;
882 	}
883 	ext->params[type].keep_link = keep_link ? 1 : 0;
884 	ext->sparams[type].keep_link = keep_link ? 1 : 0;
885 	return 0;
886 }
887