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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 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	memset(&bits, 0, sizeof(bits));
176	for (i = 0; i < parm->num_list; i++)
177		bits.bits[parm->list[i] / 32] |= 1U << (parm->list[i] % 32);
178	return snd_mask_refine(mask, &bits);
179}
180
181
182/*
183 * hw_refine callback
184 */
185static int extplug_hw_refine(snd_pcm_hw_params_t *hw_params,
186			     struct snd_ext_parm *parm)
187{
188	int i, err, change = 0;
189	for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
190		int type = hw_params_type[i];
191		if (is_mask_type(i))
192			err = snd_ext_parm_mask_refine(hw_param_mask(hw_params, type),
193						       parm, i);
194		else
195			err = snd_ext_parm_interval_refine(hw_param_interval(hw_params, type),
196							   parm, i);
197		if (err < 0)
198			return err;
199		change |= err;
200	}
201	return change;
202}
203
204static int snd_pcm_extplug_hw_refine_cprepare(snd_pcm_t *pcm,
205					      snd_pcm_hw_params_t *params)
206{
207	extplug_priv_t *ext = pcm->private_data;
208	int err;
209	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
210	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
211					 &access_mask);
212	if (err < 0)
213		return err;
214	err = extplug_hw_refine(params, ext->params);
215	if (err < 0)
216		return err;
217	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
218	return 0;
219}
220
221static int snd_pcm_extplug_hw_refine_sprepare(snd_pcm_t *pcm,
222					      snd_pcm_hw_params_t *sparams)
223{
224	extplug_priv_t *ext = pcm->private_data;
225	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
226	_snd_pcm_hw_params_any(sparams);
227	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
228				   &saccess_mask);
229	extplug_hw_refine(sparams, ext->sparams);
230	return 0;
231}
232
233static unsigned int get_links(struct snd_ext_parm *params)
234{
235	int i;
236	unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
237			      SND_PCM_HW_PARBIT_SUBFORMAT |
238			      SND_PCM_HW_PARBIT_SAMPLE_BITS |
239			      SND_PCM_HW_PARBIT_CHANNELS |
240			      SND_PCM_HW_PARBIT_FRAME_BITS |
241			      SND_PCM_HW_PARBIT_RATE |
242			      SND_PCM_HW_PARBIT_PERIODS |
243			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
244			      SND_PCM_HW_PARBIT_PERIOD_TIME |
245			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
246			      SND_PCM_HW_PARBIT_BUFFER_TIME |
247			      SND_PCM_HW_PARBIT_TICK_TIME);
248
249	for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
250		if (params[i].active)
251			links &= ~excl_parbits[i];
252	}
253	return links;
254}
255
256static int snd_pcm_extplug_hw_refine_schange(snd_pcm_t *pcm,
257					     snd_pcm_hw_params_t *params,
258					     snd_pcm_hw_params_t *sparams)
259{
260	extplug_priv_t *ext = pcm->private_data;
261	unsigned int links = get_links(ext->sparams);
262
263	return _snd_pcm_hw_params_refine(sparams, links, params);
264}
265
266static int snd_pcm_extplug_hw_refine_cchange(snd_pcm_t *pcm,
267					     snd_pcm_hw_params_t *params,
268					     snd_pcm_hw_params_t *sparams)
269{
270	extplug_priv_t *ext = pcm->private_data;
271	unsigned int links = get_links(ext->params);
272
273	return _snd_pcm_hw_params_refine(params, links, sparams);
274}
275
276static int snd_pcm_extplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
277{
278	int err = snd_pcm_hw_refine_slave(pcm, params,
279				       snd_pcm_extplug_hw_refine_cprepare,
280				       snd_pcm_extplug_hw_refine_cchange,
281				       snd_pcm_extplug_hw_refine_sprepare,
282				       snd_pcm_extplug_hw_refine_schange,
283				       snd_pcm_generic_hw_refine);
284	return err;
285}
286
287/*
288 * hw_params callback
289 */
290static int snd_pcm_extplug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
291{
292
293	extplug_priv_t *ext = pcm->private_data;
294	snd_pcm_t *slave = ext->plug.gen.slave;
295	int err = snd_pcm_hw_params_slave(pcm, params,
296					  snd_pcm_extplug_hw_refine_cchange,
297					  snd_pcm_extplug_hw_refine_sprepare,
298					  snd_pcm_extplug_hw_refine_schange,
299					  snd_pcm_generic_hw_params);
300	if (err < 0)
301		return err;
302	ext->data->slave_format = slave->format;
303	ext->data->slave_subformat = slave->subformat;
304	ext->data->slave_channels = slave->channels;
305	ext->data->rate = slave->rate;
306	INTERNAL(snd_pcm_hw_params_get_format)(params, &ext->data->format);
307	INTERNAL(snd_pcm_hw_params_get_subformat)(params, &ext->data->subformat);
308	INTERNAL(snd_pcm_hw_params_get_channels)(params, &ext->data->channels);
309
310	if (ext->data->callback->hw_params) {
311		err = ext->data->callback->hw_params(ext->data, params);
312		if (err < 0)
313			return err;
314	}
315	return 0;
316}
317
318/*
319 * hw_free callback
320 */
321static int snd_pcm_extplug_hw_free(snd_pcm_t *pcm)
322{
323	extplug_priv_t *ext = pcm->private_data;
324
325	snd_pcm_hw_free(ext->plug.gen.slave);
326	if (ext->data->callback->hw_free)
327		return ext->data->callback->hw_free(ext->data);
328	return 0;
329}
330
331/*
332 * write_areas skeleton - call transfer callback
333 */
334static snd_pcm_uframes_t
335snd_pcm_extplug_write_areas(snd_pcm_t *pcm,
336			    const snd_pcm_channel_area_t *areas,
337			    snd_pcm_uframes_t offset,
338			    snd_pcm_uframes_t size,
339			    const snd_pcm_channel_area_t *slave_areas,
340			    snd_pcm_uframes_t slave_offset,
341			    snd_pcm_uframes_t *slave_sizep)
342{
343	extplug_priv_t *ext = pcm->private_data;
344
345	if (size > *slave_sizep)
346		size = *slave_sizep;
347	size = ext->data->callback->transfer(ext->data, slave_areas, slave_offset,
348					     areas, offset, size);
349	*slave_sizep = size;
350	return size;
351}
352
353/*
354 * read_areas skeleton - call transfer callback
355 */
356static snd_pcm_uframes_t
357snd_pcm_extplug_read_areas(snd_pcm_t *pcm,
358			   const snd_pcm_channel_area_t *areas,
359			   snd_pcm_uframes_t offset,
360			   snd_pcm_uframes_t size,
361			   const snd_pcm_channel_area_t *slave_areas,
362			   snd_pcm_uframes_t slave_offset,
363			   snd_pcm_uframes_t *slave_sizep)
364{
365	extplug_priv_t *ext = pcm->private_data;
366
367	if (size > *slave_sizep)
368		size = *slave_sizep;
369	size = ext->data->callback->transfer(ext->data, areas, offset,
370					     slave_areas, slave_offset, size);
371	*slave_sizep = size;
372	return size;
373}
374
375/*
376 * call init callback
377 */
378static int snd_pcm_extplug_init(snd_pcm_t *pcm)
379{
380	extplug_priv_t *ext = pcm->private_data;
381	return ext->data->callback->init(ext->data);
382}
383
384/*
385 * dump setup
386 */
387static void snd_pcm_extplug_dump(snd_pcm_t *pcm, snd_output_t *out)
388{
389	extplug_priv_t *ext = pcm->private_data;
390
391	if (ext->data->callback->dump)
392		ext->data->callback->dump(ext->data, out);
393	else {
394		if (ext->data->name)
395			snd_output_printf(out, "%s\n", ext->data->name);
396		else
397			snd_output_printf(out, "External PCM Plugin\n");
398		if (pcm->setup) {
399			snd_output_printf(out, "Its setup is:\n");
400			snd_pcm_dump_setup(pcm, out);
401		}
402	}
403	snd_output_printf(out, "Slave: ");
404	snd_pcm_dump(ext->plug.gen.slave, out);
405}
406
407static void clear_ext_params(extplug_priv_t *ext)
408{
409	int i;
410	for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
411		snd_ext_parm_clear(&ext->params[i]);
412		snd_ext_parm_clear(&ext->sparams[i]);
413	}
414}
415
416static int snd_pcm_extplug_close(snd_pcm_t *pcm)
417{
418	extplug_priv_t *ext = pcm->private_data;
419
420	snd_pcm_close(ext->plug.gen.slave);
421	clear_ext_params(ext);
422	if (ext->data->callback->close)
423		ext->data->callback->close(ext->data);
424	free(ext);
425	return 0;
426}
427
428static const snd_pcm_ops_t snd_pcm_extplug_ops = {
429	.close = snd_pcm_extplug_close,
430	.info = snd_pcm_generic_info,
431	.hw_refine = snd_pcm_extplug_hw_refine,
432	.hw_params = snd_pcm_extplug_hw_params,
433	.hw_free = snd_pcm_extplug_hw_free,
434	.sw_params = snd_pcm_generic_sw_params,
435	.channel_info = snd_pcm_generic_channel_info,
436	.dump = snd_pcm_extplug_dump,
437	.nonblock = snd_pcm_generic_nonblock,
438	.async = snd_pcm_generic_async,
439	.mmap = snd_pcm_generic_mmap,
440	.munmap = snd_pcm_generic_munmap,
441};
442
443#endif /* !DOC_HIDDEN */
444
445/*
446 * Exported functions
447 */
448
449/*! \page pcm_external_plugins PCM External Plugin SDK
450
451\section pcm_externals External Plugins
452
453The external plugins are implemented in a shared object file located
454at /usr/lib/alsa-lib (the exact location depends on the build option
455and asoundrc configuration).  It has to be the file like
456libasound_module_pcm_MYPLUGIN.so, where MYPLUGIN corresponds to your
457own plugin name.
458
459The entry point of the plugin is defined via
460#SND_PCM_PLUGIN_DEFINE_FUNC() macro.  This macro defines the function
461with a proper name to be referred from alsa-lib.  The function takes
462the following 6 arguments:
463\code
464int (snd_pcm_t **pcmp, const char *name, snd_config_t *root,
465	snd_config_t *conf, snd_pcm_stream_t stream, int mode)
466\endcode
467The first argument, pcmp, is the pointer to store the resultant PCM
468handle.  The arguments name, root, stream and mode are the parameters
469to be passed to the plugin constructor.  The conf is the configuration
470tree for the plugin.  The arguments above are defined in the macro
471itself, so don't use variables with the same names to shadow
472parameters.
473
474After parsing the configuration parameters in the given conf tree,
475usually you will call the external plugin API function,
476#snd_pcm_extplug_create() or #snd_pcm_ioplug_create(), depending
477on the plugin type.  The PCM handle must be filled *pcmp in return.
478Then this function must return either a value 0 when succeeded, or a
479negative value as the error code.
480
481Finally, add #SND_PCM_PLUGIN_SYMBOL() with the name of your
482plugin as the argument at the end.  This defines the proper versioned
483symbol as the reference.
484
485The typical code would look like below:
486\code
487struct myplug_info {
488	snd_pcm_extplug_t ext;
489	int my_own_data;
490	...
491};
492
493SND_PCM_PLUGIN_DEFINE_FUNC(myplug)
494{
495	snd_config_iterator_t i, next;
496	snd_config_t *slave = NULL;
497	struct myplug_info *myplug;
498	int err;
499
500	snd_config_for_each(i, next, conf) {
501		snd_config_t *n = snd_config_iterator_entry(i);
502		const char *id;
503		if (snd_config_get_id(n, &id) < 0)
504			continue;
505		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
506			continue;
507		if (strcmp(id, "slave") == 0) {
508			slave = n;
509			continue;
510		}
511		if (strcmp(id, "my_own_parameter") == 0) {
512			....
513			continue;
514		}
515		SNDERR("Unknown field %s", id);
516		return -EINVAL;
517	}
518
519	if (! slave) {
520		SNDERR("No slave defined for myplug");
521		return -EINVAL;
522	}
523
524	myplug = calloc(1, sizeof(*myplug));
525	if (myplug == NULL)
526		return -ENOMEM;
527
528	myplug->ext.version = SND_PCM_EXTPLUG_VERSION;
529	myplug->ext.name = "My Own Plugin";
530	myplug->ext.callback = &my_own_callback;
531	myplug->ext.private_data = myplug;
532	....
533
534	err = snd_pcm_extplug_create(&myplug->ext, name, root, conf, stream, mode);
535	if (err < 0) {
536		myplug_free(myplug);
537		return err;
538	}
539
540	*pcmp = myplug->ext.pcm;
541	return 0;
542}
543
544SND_PCM_PLUGIN_SYMBOL(myplug);
545\endcode
546
547Read the codes in alsa-plugins package for the real examples.
548
549
550\section pcm_extplug External Plugin: Filter-Type Plugin
551
552The filter-type plugin is a plugin to convert the PCM signals from the input
553and feeds to the output.  Thus, this plugin always needs a slave PCM as its output.
554
555The plugin can modify the format and the channels of the input/output PCM.
556It can <i>not</i> modify the sample rate (because of simplicity reason).
557
558The following fields have to be filled in extplug record before calling
559#snd_pcm_extplug_create() : version, name, callback.
560Otherfields are optional and should be initialized with zero.
561
562The constant #SND_PCM_EXTPLUG_VERSION must be passed to the version
563field for the version check in alsa-lib.  A non-NULL ASCII string
564has to be passed to the name field.  The callback field contains the
565table of callback functions for this plugin (defined as
566#snd_pcm_extplug_callback_t).
567
568The driver can set an arbitrary value (pointer) to private_data
569field to refer its own data in the callbacks.
570
571The rest fields are filled by #snd_pcm_extplug_create().  The pcm field
572is the resultant PCM handle.  The others are the current status of the
573PCM.
574
575The callback functions in #snd_pcm_extplug_callback_t define the real
576behavior of the driver.
577At least, transfer callback must be given.  This callback is called
578at each time certain size of data block is transfered to the slave
579PCM.  Other callbacks are optional.
580
581The close callback is called when the PCM is closed.  If the plugin
582allocates private resources, this is the place to release them
583again.  The hw_params and hw_free callbacks are called at
584#snd_pcm_hw_params() and #snd_pcm_hw_free() API calls,
585respectively.  The last, dump callback, is called for printing the
586information of the given plugin.
587
588The init callback is called when the PCM is at prepare state or any
589initialization is issued.  Use this callback to reset the PCM instance
590to a sane initial state.
591
592The hw_params constraints can be defined via either
593#snd_pcm_extplug_set_param_minmax() and #snd_pcm_extplug_set_param_list()
594functions after calling #snd_pcm_extplug_create().
595The former defines the minimal and maximal acceptable values for the
596given hw_params parameter (SND_PCM_EXTPLUG_HW_XXX).
597This function can't be used for the format parameter.  The latter
598function specifies the available parameter values as the list.
599As mentioned above, the rate can't be changed.  Only changeable
600parameters are sample format and channels.
601
602To define the constraints of the slave PCM configuration, use
603either #snd_pcm_extplug_set_slave_param_minmax() and
604#snd_pcm_extplug_set_slave_param_list().  The arguments are as same
605as former functions.
606
607To clear the parameter constraints, call #snd_pcm_extplug_params_reset()
608function.
609
610*/
611
612/**
613 * \brief Create an extplug instance
614 * \param extplug the extplug handle
615 * \param name name of the PCM
616 * \param root configuration tree root
617 * \param slave_conf slave configuration root
618 * \param stream stream direction
619 * \param mode PCM open mode
620 * \return 0 if successful, or a negative error code
621 *
622 * Creates the extplug instance based on the given handle.
623 * The slave_conf argument is mandatory, and usually taken from the config tree of the
624 * PCM plugin as "slave" config value.
625 * name, root, stream and mode arguments are the values used for opening the PCM.
626 *
627 * The callback is the mandatory field of extplug handle.  At least, start, stop and
628 * pointer callbacks must be set before calling this function.
629 */
630int snd_pcm_extplug_create(snd_pcm_extplug_t *extplug, const char *name,
631			   snd_config_t *root, snd_config_t *slave_conf,
632			   snd_pcm_stream_t stream, int mode)
633{
634	extplug_priv_t *ext;
635	int err;
636	snd_pcm_t *spcm, *pcm;
637	snd_config_t *sconf;
638
639	assert(root);
640	assert(extplug && extplug->callback);
641	assert(extplug->callback->transfer);
642	assert(slave_conf);
643
644	/* We support 1.0.0 to current */
645	if (extplug->version < 0x010000 ||
646	    extplug->version > SND_PCM_EXTPLUG_VERSION) {
647		SNDERR("extplug: Plugin version mismatch: 0x%x\n",
648		       extplug->version);
649		return -ENXIO;
650	}
651
652	err = snd_pcm_slave_conf(root, slave_conf, &sconf, 0);
653	if (err < 0)
654		return err;
655	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, NULL);
656	snd_config_delete(sconf);
657	if (err < 0)
658		return err;
659
660	ext = calloc(1, sizeof(*ext));
661	if (! ext)
662		return -ENOMEM;
663
664	ext->data = extplug;
665	extplug->stream = stream;
666
667	snd_pcm_plugin_init(&ext->plug);
668	ext->plug.read = snd_pcm_extplug_read_areas;
669	ext->plug.write = snd_pcm_extplug_write_areas;
670	ext->plug.undo_read = snd_pcm_plugin_undo_read_generic;
671	ext->plug.undo_write = snd_pcm_plugin_undo_write_generic;
672	ext->plug.gen.slave = spcm;
673	ext->plug.gen.close_slave = 1;
674	if (extplug->version >= 0x010001 && extplug->callback->init)
675		ext->plug.init = snd_pcm_extplug_init;
676
677	err = snd_pcm_new(&pcm, SND_PCM_TYPE_EXTPLUG, name, stream, mode);
678	if (err < 0) {
679		free(ext);
680		return err;
681	}
682
683	extplug->pcm = pcm;
684	pcm->ops = &snd_pcm_extplug_ops;
685	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
686	pcm->private_data = ext;
687	pcm->poll_fd = spcm->poll_fd;
688	pcm->poll_events = spcm->poll_events;
689	snd_pcm_set_hw_ptr(pcm, &ext->plug.hw_ptr, -1, 0);
690	snd_pcm_set_appl_ptr(pcm, &ext->plug.appl_ptr, -1, 0);
691
692	return 0;
693}
694
695/**
696 * \brief Delete the extplug instance
697 * \param extplug the extplug handle to delete
698 * \return 0 if successful, or a negative error code
699 *
700 * The destructor of extplug instance.
701 * Closes the PCM and deletes the associated resources.
702 */
703int snd_pcm_extplug_delete(snd_pcm_extplug_t *extplug)
704{
705	return snd_pcm_close(extplug->pcm);
706}
707
708
709/**
710 * \brief Reset extplug parameters
711 * \param extplug the extplug handle
712 *
713 * Resets the all parameters for the given extplug handle.
714 */
715void snd_pcm_extplug_params_reset(snd_pcm_extplug_t *extplug)
716{
717	extplug_priv_t *ext = extplug->pcm->private_data;
718	clear_ext_params(ext);
719}
720
721/**
722 * \brief Set slave parameter as the list
723 * \param extplug the extplug handle
724 * \param type parameter type
725 * \param num_list number of available values
726 * \param list the list of available values
727 * \return 0 if successful, or a negative error code
728 *
729 * Sets the slave parameter as the list.
730 * The available values of the given parameter type of the slave PCM is restricted
731 * to the ones of the given list.
732 */
733int snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list)
734{
735	extplug_priv_t *ext = extplug->pcm->private_data;
736	if (type < 0 && type >= SND_PCM_EXTPLUG_HW_PARAMS) {
737		SNDERR("EXTPLUG: invalid parameter type %d", type);
738		return -EINVAL;
739	}
740	return snd_ext_parm_set_list(&ext->sparams[type], num_list, list);
741}
742
743/**
744 * \brief Set slave parameter as the min/max values
745 * \param extplug the extplug handle
746 * \param type parameter type
747 * \param min the minimum value
748 * \param max the maximum value
749 * \return 0 if successful, or a negative error code
750 *
751 * Sets the slave parameter as the min/max values.
752 * The available values of the given parameter type of the slave PCM is restricted
753 * between the given minimum and maximum values.
754 */
755int snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max)
756{
757	extplug_priv_t *ext = extplug->pcm->private_data;
758	if (type < 0 && type >= SND_PCM_EXTPLUG_HW_PARAMS) {
759		SNDERR("EXTPLUG: invalid parameter type %d", type);
760		return -EINVAL;
761	}
762	if (is_mask_type(type)) {
763		SNDERR("EXTPLUG: invalid parameter type %d", type);
764		return -EINVAL;
765	}
766	return snd_ext_parm_set_minmax(&ext->sparams[type], min, max);
767}
768
769/**
770 * \brief Set master parameter as the list
771 * \param extplug the extplug handle
772 * \param type parameter type
773 * \param num_list number of available values
774 * \param list the list of available values
775 * \return 0 if successful, or a negative error code
776 *
777 * Sets the master parameter as the list.
778 * The available values of the given parameter type of this PCM (as input) is restricted
779 * to the ones of the given list.
780 */
781int snd_pcm_extplug_set_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list)
782{
783	extplug_priv_t *ext = extplug->pcm->private_data;
784	if (type < 0 && type >= SND_PCM_EXTPLUG_HW_PARAMS) {
785		SNDERR("EXTPLUG: invalid parameter type %d", type);
786		return -EINVAL;
787	}
788	return snd_ext_parm_set_list(&ext->params[type], num_list, list);
789}
790
791/**
792 * \brief Set master parameter as the min/max values
793 * \param extplug the extplug handle
794 * \param type parameter type
795 * \param min the minimum value
796 * \param max the maximum value
797 * \return 0 if successful, or a negative error code
798 *
799 * Sets the master parameter as the min/max values.
800 * The available values of the given parameter type of this PCM (as input) is restricted
801 * between the given minimum and maximum values.
802 */
803int snd_pcm_extplug_set_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max)
804{
805	extplug_priv_t *ext = extplug->pcm->private_data;
806	if (type < 0 && type >= SND_PCM_EXTPLUG_HW_PARAMS) {
807		SNDERR("EXTPLUG: invalid parameter type %d", type);
808		return -EINVAL;
809	}
810	if (is_mask_type(type)) {
811		SNDERR("EXTPLUG: invalid parameter type %d", type);
812		return -EINVAL;
813	}
814	return snd_ext_parm_set_minmax(&ext->params[type], min, max);
815}
816
817