xref: /third_party/alsa-lib/src/pcm/pcm_extplug.c (revision d5ac70f0)
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 */
36const char *_snd_module_pcm_extplug = "";
37#endif
38
39#ifndef DOC_HIDDEN
40
41typedef 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
48static 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
55static 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 */
66int 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 */
80static int val_compar(const void *ap, const void *bp)
81{
82	return *(const unsigned int *)ap - *(const unsigned int *)bp;
83}
84
85int 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
102void 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 */
111int 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 */
148int 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 */
169int 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 */
187static 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
206static 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
223static 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
235static 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
258static 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
268static 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
278static 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 */
292static 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 */
323static 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 */
336static snd_pcm_uframes_t
337snd_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 */
358static snd_pcm_uframes_t
359snd_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 */
380static 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 */
389static 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
409static 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
418static 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
430static 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
440static 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
450static 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
460static 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
488The external plugins are implemented in a shared object file located
489at /usr/lib/alsa-lib (the exact location depends on the build option
490and asoundrc configuration).  It has to be the file like
491libasound_module_pcm_MYPLUGIN.so, where MYPLUGIN corresponds to your
492own plugin name.
493
494The entry point of the plugin is defined via
495#SND_PCM_PLUGIN_DEFINE_FUNC() macro.  This macro defines the function
496with a proper name to be referred from alsa-lib.  The function takes
497the following 6 arguments:
498\code
499int (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
502The first argument, pcmp, is the pointer to store the resultant PCM
503handle.  The arguments name, root, stream and mode are the parameters
504to be passed to the plugin constructor.  The conf is the configuration
505tree for the plugin.  The arguments above are defined in the macro
506itself, so don't use variables with the same names to shadow
507parameters.
508
509After parsing the configuration parameters in the given conf tree,
510usually you will call the external plugin API function,
511#snd_pcm_extplug_create() or #snd_pcm_ioplug_create(), depending
512on the plugin type.  The PCM handle must be filled *pcmp in return.
513Then this function must return either a value 0 when succeeded, or a
514negative value as the error code.
515
516Finally, add #SND_PCM_PLUGIN_SYMBOL() with the name of your
517plugin as the argument at the end.  This defines the proper versioned
518symbol as the reference.
519
520The typical code would look like below:
521\code
522struct myplug_info {
523	snd_pcm_extplug_t ext;
524	int my_own_data;
525	...
526};
527
528SND_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
579SND_PCM_PLUGIN_SYMBOL(myplug);
580\endcode
581
582Read the codes in alsa-plugins package for the real examples.
583
584
585\section pcm_extplug External Plugin: Filter-Type Plugin
586
587The filter-type plugin is a plugin to convert the PCM signals from the input
588and feeds to the output.  Thus, this plugin always needs a slave PCM as its output.
589
590The plugin can modify the format and the channels of the input/output PCM.
591It can <i>not</i> modify the sample rate (because of simplicity reason).
592
593The following fields have to be filled in extplug record before calling
594#snd_pcm_extplug_create() : version, name, callback.
595Otherfields are optional and should be initialized with zero.
596
597The constant #SND_PCM_EXTPLUG_VERSION must be passed to the version
598field for the version check in alsa-lib.  A non-NULL ASCII string
599has to be passed to the name field.  The callback field contains the
600table of callback functions for this plugin (defined as
601#snd_pcm_extplug_callback_t).
602
603The driver can set an arbitrary value (pointer) to private_data
604field to refer its own data in the callbacks.
605
606The rest fields are filled by #snd_pcm_extplug_create().  The pcm field
607is the resultant PCM handle.  The others are the current status of the
608PCM.
609
610The callback functions in #snd_pcm_extplug_callback_t define the real
611behavior of the driver.
612At least, transfer callback must be given.  This callback is called
613at each time certain size of data block is transfered to the slave
614PCM.  Other callbacks are optional.
615
616The close callback is called when the PCM is closed.  If the plugin
617allocates private resources, this is the place to release them
618again.  The hw_params and hw_free callbacks are called at
619#snd_pcm_hw_params() and #snd_pcm_hw_free() API calls,
620respectively.  The last, dump callback, is called for printing the
621information of the given plugin.
622
623The init callback is called when the PCM is at prepare state or any
624initialization is issued.  Use this callback to reset the PCM instance
625to a sane initial state.
626
627The hw_params constraints can be defined via either
628#snd_pcm_extplug_set_param_minmax() and #snd_pcm_extplug_set_param_list()
629functions after calling #snd_pcm_extplug_create().
630The former defines the minimal and maximal acceptable values for the
631given hw_params parameter (SND_PCM_EXTPLUG_HW_XXX).
632This function can't be used for the format parameter.  The latter
633function specifies the available parameter values as the list.
634As mentioned above, the rate can't be changed.  Only changeable
635parameters are sample format and channels.
636
637To define the constraints of the slave PCM configuration, use
638either #snd_pcm_extplug_set_slave_param_minmax() and
639#snd_pcm_extplug_set_slave_param_list().  The arguments are as same
640as former functions.
641
642To clear the parameter constraints, call #snd_pcm_extplug_params_reset()
643function.
644
645When using snd_pcm_extplug_set_param_*() or snd_pcm_extplug_set_slave_param_*()
646for any parameter. This parameter is no longer linked between the client and
647slave PCM. Therefore it could differ and the extplug has to support conversion
648between all valid parameter configurations. To keep the client and slave
649parameter linked #snd_pcm_extplug_set_param_link() can be used for the
650corresponding parameter. For example if the extplug does not support channel nor
651format conversion the supported client parameters can be limited with
652snd_pcm_extplug_set_param_*() and afterwards
653snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_FORMAT, 1) and
654snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1) should be
655called 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 */
676int 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",
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 */
750int 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 */
762void 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 */
780int 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 */
802int 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 */
828int 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 */
850int 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 */
874int 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