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