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