Home | History | Annotate | Download | only in pcm
      1 /**
      2  * \file pcm/pcm_extplug.c
      3  * \ingroup Plugin_SDK
      4  * \brief External Filter Plugin SDK
      5  * \author Takashi Iwai <tiwai (at) suse.de>
      6  * \date 2005
      7  */
      8 /*
      9  *  PCM - External Filter Plugin SDK
     10  *  Copyright (c) 2005 by Takashi Iwai <tiwai (at) 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 */
     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  */
     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  */
     80 static int val_compar(const void *ap, const void *bp)
     81 {
     82 	return *(const unsigned int *)ap - *(const unsigned int *)bp;
     83 }
     84 
     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 
    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  */
    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  */
    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  */
    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 	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  */
    185 static 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 
    204 static 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 
    221 static 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 
    233 static 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 
    256 static 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 
    266 static 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 
    276 static 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  */
    290 static 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  */
    321 static 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  */
    334 static snd_pcm_uframes_t
    335 snd_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  */
    356 static snd_pcm_uframes_t
    357 snd_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  */
    378 static 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  */
    387 static 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 
    407 static 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 
    416 static 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 
    428 static 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 
    453 The external plugins are implemented in a shared object file located
    454 at /usr/lib/alsa-lib (the exact location depends on the build option
    455 and asoundrc configuration).  It has to be the file like
    456 libasound_module_pcm_MYPLUGIN.so, where MYPLUGIN corresponds to your
    457 own plugin name.
    458 
    459 The entry point of the plugin is defined via
    460 #SND_PCM_PLUGIN_DEFINE_FUNC() macro.  This macro defines the function
    461 with a proper name to be referred from alsa-lib.  The function takes
    462 the following 6 arguments:
    463 \code
    464 int (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
    467 The first argument, pcmp, is the pointer to store the resultant PCM
    468 handle.  The arguments name, root, stream and mode are the parameters
    469 to be passed to the plugin constructor.  The conf is the configuration
    470 tree for the plugin.  The arguments above are defined in the macro
    471 itself, so don't use variables with the same names to shadow
    472 parameters.
    473 
    474 After parsing the configuration parameters in the given conf tree,
    475 usually you will call the external plugin API function,
    476 #snd_pcm_extplug_create() or #snd_pcm_ioplug_create(), depending
    477 on the plugin type.  The PCM handle must be filled *pcmp in return.
    478 Then this function must return either a value 0 when succeeded, or a
    479 negative value as the error code.
    480 
    481 Finally, add #SND_PCM_PLUGIN_SYMBOL() with the name of your
    482 plugin as the argument at the end.  This defines the proper versioned
    483 symbol as the reference.
    484 
    485 The typical code would look like below:
    486 \code
    487 struct myplug_info {
    488 	snd_pcm_extplug_t ext;
    489 	int my_own_data;
    490 	...
    491 };
    492 
    493 SND_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 
    544 SND_PCM_PLUGIN_SYMBOL(myplug);
    545 \endcode
    546 
    547 Read the codes in alsa-plugins package for the real examples.
    548 
    549 
    550 \section pcm_extplug External Plugin: Filter-Type Plugin
    551 
    552 The filter-type plugin is a plugin to convert the PCM signals from the input
    553 and feeds to the output.  Thus, this plugin always needs a slave PCM as its output.
    554 
    555 The plugin can modify the format and the channels of the input/output PCM.
    556 It can <i>not</i> modify the sample rate (because of simplicity reason).
    557 
    558 The following fields have to be filled in extplug record before calling
    559 #snd_pcm_extplug_create() : version, name, callback.
    560 Otherfields are optional and should be initialized with zero.
    561 
    562 The constant #SND_PCM_EXTPLUG_VERSION must be passed to the version
    563 field for the version check in alsa-lib.  A non-NULL ASCII string
    564 has to be passed to the name field.  The callback field contains the
    565 table of callback functions for this plugin (defined as
    566 #snd_pcm_extplug_callback_t).
    567 
    568 The driver can set an arbitrary value (pointer) to private_data
    569 field to refer its own data in the callbacks.
    570 
    571 The rest fields are filled by #snd_pcm_extplug_create().  The pcm field
    572 is the resultant PCM handle.  The others are the current status of the
    573 PCM.
    574 
    575 The callback functions in #snd_pcm_extplug_callback_t define the real
    576 behavior of the driver.
    577 At least, transfer callback must be given.  This callback is called
    578 at each time certain size of data block is transfered to the slave
    579 PCM.  Other callbacks are optional.
    580 
    581 The close callback is called when the PCM is closed.  If the plugin
    582 allocates private resources, this is the place to release them
    583 again.  The hw_params and hw_free callbacks are called at
    584 #snd_pcm_hw_params() and #snd_pcm_hw_free() API calls,
    585 respectively.  The last, dump callback, is called for printing the
    586 information of the given plugin.
    587 
    588 The init callback is called when the PCM is at prepare state or any
    589 initialization is issued.  Use this callback to reset the PCM instance
    590 to a sane initial state.
    591 
    592 The hw_params constraints can be defined via either
    593 #snd_pcm_extplug_set_param_minmax() and #snd_pcm_extplug_set_param_list()
    594 functions after calling #snd_pcm_extplug_create().
    595 The former defines the minimal and maximal acceptable values for the
    596 given hw_params parameter (SND_PCM_EXTPLUG_HW_XXX).
    597 This function can't be used for the format parameter.  The latter
    598 function specifies the available parameter values as the list.
    599 As mentioned above, the rate can't be changed.  Only changeable
    600 parameters are sample format and channels.
    601 
    602 To define the constraints of the slave PCM configuration, use
    603 either #snd_pcm_extplug_set_slave_param_minmax() and
    604 #snd_pcm_extplug_set_slave_param_list().  The arguments are as same
    605 as former functions.
    606 
    607 To clear the parameter constraints, call #snd_pcm_extplug_params_reset()
    608 function.
    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  */
    630 int 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 	if (extplug->version != SND_PCM_EXTPLUG_VERSION) {
    645 		SNDERR("extplug: Plugin version mismatch\n");
    646 		return -ENXIO;
    647 	}
    648 
    649 	err = snd_pcm_slave_conf(root, slave_conf, &sconf, 0);
    650 	if (err < 0)
    651 		return err;
    652 	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, NULL);
    653 	snd_config_delete(sconf);
    654 	if (err < 0)
    655 		return err;
    656 
    657 	ext = calloc(1, sizeof(*ext));
    658 	if (! ext)
    659 		return -ENOMEM;
    660 
    661 	ext->data = extplug;
    662 	extplug->stream = stream;
    663 
    664 	snd_pcm_plugin_init(&ext->plug);
    665 	ext->plug.read = snd_pcm_extplug_read_areas;
    666 	ext->plug.write = snd_pcm_extplug_write_areas;
    667 	ext->plug.undo_read = snd_pcm_plugin_undo_read_generic;
    668 	ext->plug.undo_write = snd_pcm_plugin_undo_write_generic;
    669 	ext->plug.gen.slave = spcm;
    670 	ext->plug.gen.close_slave = 1;
    671 	if (extplug->callback->init)
    672 		ext->plug.init = snd_pcm_extplug_init;
    673 
    674 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_EXTPLUG, name, stream, mode);
    675 	if (err < 0) {
    676 		free(ext);
    677 		return err;
    678 	}
    679 
    680 	extplug->pcm = pcm;
    681 	pcm->ops = &snd_pcm_extplug_ops;
    682 	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
    683 	pcm->private_data = ext;
    684 	pcm->poll_fd = spcm->poll_fd;
    685 	pcm->poll_events = spcm->poll_events;
    686 	snd_pcm_set_hw_ptr(pcm, &ext->plug.hw_ptr, -1, 0);
    687 	snd_pcm_set_appl_ptr(pcm, &ext->plug.appl_ptr, -1, 0);
    688 
    689 	return 0;
    690 }
    691 
    692 /**
    693  * \brief Delete the extplug instance
    694  * \param extplug the extplug handle to delete
    695  * \return 0 if successful, or a negative error code
    696  *
    697  * The destructor of extplug instance.
    698  * Closes the PCM and deletes the associated resources.
    699  */
    700 int snd_pcm_extplug_delete(snd_pcm_extplug_t *extplug)
    701 {
    702 	return snd_pcm_close(extplug->pcm);
    703 }
    704 
    705 
    706 /**
    707  * \brief Reset extplug parameters
    708  * \param extplug the extplug handle
    709  *
    710  * Resets the all parameters for the given extplug handle.
    711  */
    712 void snd_pcm_extplug_params_reset(snd_pcm_extplug_t *extplug)
    713 {
    714 	extplug_priv_t *ext = extplug->pcm->private_data;
    715 	clear_ext_params(ext);
    716 }
    717 
    718 /**
    719  * \brief Set slave parameter as the list
    720  * \param extplug the extplug handle
    721  * \param type parameter type
    722  * \param num_list number of available values
    723  * \param list the list of available values
    724  * \return 0 if successful, or a negative error code
    725  *
    726  * Sets the slave parameter as the list.
    727  * The available values of the given parameter type of the slave PCM is restricted
    728  * to the ones of the given list.
    729  */
    730 int snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list)
    731 {
    732 	extplug_priv_t *ext = extplug->pcm->private_data;
    733 	if (type < 0 && type >= SND_PCM_EXTPLUG_HW_PARAMS) {
    734 		SNDERR("EXTPLUG: invalid parameter type %d", type);
    735 		return -EINVAL;
    736 	}
    737 	return snd_ext_parm_set_list(&ext->sparams[type], num_list, list);
    738 }
    739 
    740 /**
    741  * \brief Set slave parameter as the min/max values
    742  * \param extplug the extplug handle
    743  * \param type parameter type
    744  * \param min the minimum value
    745  * \param max the maximum value
    746  * \return 0 if successful, or a negative error code
    747  *
    748  * Sets the slave parameter as the min/max values.
    749  * The available values of the given parameter type of the slave PCM is restricted
    750  * between the given minimum and maximum values.
    751  */
    752 int snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max)
    753 {
    754 	extplug_priv_t *ext = extplug->pcm->private_data;
    755 	if (type < 0 && type >= SND_PCM_EXTPLUG_HW_PARAMS) {
    756 		SNDERR("EXTPLUG: invalid parameter type %d", type);
    757 		return -EINVAL;
    758 	}
    759 	if (is_mask_type(type)) {
    760 		SNDERR("EXTPLUG: invalid parameter type %d", type);
    761 		return -EINVAL;
    762 	}
    763 	return snd_ext_parm_set_minmax(&ext->sparams[type], min, max);
    764 }
    765 
    766 /**
    767  * \brief Set master parameter as the list
    768  * \param extplug the extplug handle
    769  * \param type parameter type
    770  * \param num_list number of available values
    771  * \param list the list of available values
    772  * \return 0 if successful, or a negative error code
    773  *
    774  * Sets the master parameter as the list.
    775  * The available values of the given parameter type of this PCM (as input) is restricted
    776  * to the ones of the given list.
    777  */
    778 int snd_pcm_extplug_set_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list)
    779 {
    780 	extplug_priv_t *ext = extplug->pcm->private_data;
    781 	if (type < 0 && type >= SND_PCM_EXTPLUG_HW_PARAMS) {
    782 		SNDERR("EXTPLUG: invalid parameter type %d", type);
    783 		return -EINVAL;
    784 	}
    785 	return snd_ext_parm_set_list(&ext->params[type], num_list, list);
    786 }
    787 
    788 /**
    789  * \brief Set master parameter as the min/max values
    790  * \param extplug the extplug handle
    791  * \param type parameter type
    792  * \param min the minimum value
    793  * \param max the maximum value
    794  * \return 0 if successful, or a negative error code
    795  *
    796  * Sets the master parameter as the min/max values.
    797  * The available values of the given parameter type of this PCM (as input) is restricted
    798  * between the given minimum and maximum values.
    799  */
    800 int snd_pcm_extplug_set_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max)
    801 {
    802 	extplug_priv_t *ext = extplug->pcm->private_data;
    803 	if (type < 0 && type >= SND_PCM_EXTPLUG_HW_PARAMS) {
    804 		SNDERR("EXTPLUG: invalid parameter type %d", type);
    805 		return -EINVAL;
    806 	}
    807 	if (is_mask_type(type)) {
    808 		SNDERR("EXTPLUG: invalid parameter type %d", type);
    809 		return -EINVAL;
    810 	}
    811 	return snd_ext_parm_set_minmax(&ext->params[type], min, max);
    812 }
    813 
    814