1/** 2 * \file pcm/pcm_extplug.c 3 * \ingroup Plugin_SDK 4 * \brief External Filter Plugin SDK 5 * \author Takashi Iwai <tiwai@suse.de> 6 * \date 2005 7 */ 8/* 9 * PCM - External Filter Plugin SDK 10 * Copyright (c) 2005 by Takashi Iwai <tiwai@suse.de> 11 * 12 * 13 * This library is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU Lesser General Public License as 15 * published by the Free Software Foundation; either version 2.1 of 16 * the License, or (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU Lesser General Public License for more details. 22 * 23 * You should have received a copy of the GNU Lesser General Public 24 * License along with this library; if not, write to the Free Software 25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 26 * 27 */ 28 29#include "pcm_local.h" 30#include "pcm_plugin.h" 31#include "pcm_extplug.h" 32#include "pcm_ext_parm.h" 33 34#ifndef PIC 35/* entry for static linking */ 36const char *_snd_module_pcm_extplug = ""; 37#endif 38 39#ifndef DOC_HIDDEN 40 41typedef struct snd_pcm_extplug_priv { 42 snd_pcm_plugin_t plug; 43 snd_pcm_extplug_t *data; 44 struct snd_ext_parm params[SND_PCM_EXTPLUG_HW_PARAMS]; 45 struct snd_ext_parm sparams[SND_PCM_EXTPLUG_HW_PARAMS]; 46} extplug_priv_t; 47 48static const int hw_params_type[SND_PCM_EXTPLUG_HW_PARAMS] = { 49 [SND_PCM_EXTPLUG_HW_FORMAT] = SND_PCM_HW_PARAM_FORMAT, 50 [SND_PCM_EXTPLUG_HW_CHANNELS] = SND_PCM_HW_PARAM_CHANNELS 51}; 52 53#define is_mask_type(i) (hw_params_type[i] < SND_PCM_HW_PARAM_FIRST_INTERVAL) 54 55static const unsigned int excl_parbits[SND_PCM_EXTPLUG_HW_PARAMS] = { 56 [SND_PCM_EXTPLUG_HW_FORMAT] = (SND_PCM_HW_PARBIT_FORMAT| 57 SND_PCM_HW_PARBIT_SUBFORMAT | 58 SND_PCM_HW_PARBIT_SAMPLE_BITS), 59 [SND_PCM_EXTPLUG_HW_CHANNELS] = (SND_PCM_HW_PARBIT_CHANNELS| 60 SND_PCM_HW_PARBIT_FRAME_BITS), 61}; 62 63/* 64 * set min/max values for the given parameter 65 */ 66int snd_ext_parm_set_minmax(struct snd_ext_parm *parm, unsigned int min, unsigned int max) 67{ 68 parm->num_list = 0; 69 free(parm->list); 70 parm->list = NULL; 71 parm->min = min; 72 parm->max = max; 73 parm->active = 1; 74 return 0; 75} 76 77/* 78 * set the list of available values for the given parameter 79 */ 80static int val_compar(const void *ap, const void *bp) 81{ 82 return *(const unsigned int *)ap - *(const unsigned int *)bp; 83} 84 85int snd_ext_parm_set_list(struct snd_ext_parm *parm, unsigned int num_list, const unsigned int *list) 86{ 87 unsigned int *new_list; 88 89 new_list = malloc(sizeof(*new_list) * num_list); 90 if (new_list == NULL) 91 return -ENOMEM; 92 memcpy(new_list, list, sizeof(*new_list) * num_list); 93 qsort(new_list, num_list, sizeof(*new_list), val_compar); 94 95 free(parm->list); 96 parm->num_list = num_list; 97 parm->list = new_list; 98 parm->active = 1; 99 return 0; 100} 101 102void snd_ext_parm_clear(struct snd_ext_parm *parm) 103{ 104 free(parm->list); 105 memset(parm, 0, sizeof(*parm)); 106} 107 108/* 109 * limit the interval to the given list 110 */ 111int snd_interval_list(snd_interval_t *ival, int num_list, unsigned int *list) 112{ 113 int imin, imax; 114 int changed = 0; 115 116 if (snd_interval_empty(ival)) 117 return -ENOENT; 118 for (imin = 0; imin < num_list; imin++) { 119 if (ival->min == list[imin] && ! ival->openmin) 120 break; 121 if (ival->min <= list[imin]) { 122 ival->min = list[imin]; 123 ival->openmin = 0; 124 changed = 1; 125 break; 126 } 127 } 128 if (imin >= num_list) 129 return -EINVAL; 130 for (imax = num_list - 1; imax >= imin; imax--) { 131 if (ival->max == list[imax] && ! ival->openmax) 132 break; 133 if (ival->max >= list[imax]) { 134 ival->max = list[imax]; 135 ival->openmax = 0; 136 changed = 1; 137 break; 138 } 139 } 140 if (imax < imin) 141 return -EINVAL; 142 return changed; 143} 144 145/* 146 * refine the interval parameter 147 */ 148int snd_ext_parm_interval_refine(snd_interval_t *ival, struct snd_ext_parm *parm, int type) 149{ 150 parm += type; 151 if (! parm->active) 152 return 0; 153 ival->integer |= parm->integer; 154 if (parm->num_list) { 155 return snd_interval_list(ival, parm->num_list, parm->list); 156 } else if (parm->min || parm->max) { 157 snd_interval_t t; 158 memset(&t, 0, sizeof(t)); 159 snd_interval_set_minmax(&t, parm->min, parm->max); 160 t.integer = ival->integer; 161 return snd_interval_refine(ival, &t); 162 } 163 return 0; 164} 165 166/* 167 * refine the mask parameter 168 */ 169int snd_ext_parm_mask_refine(snd_mask_t *mask, struct snd_ext_parm *parm, int type) 170{ 171 snd_mask_t bits; 172 unsigned int i; 173 174 parm += type; 175 if (!parm->active) 176 return 0; 177 memset(&bits, 0, sizeof(bits)); 178 for (i = 0; i < parm->num_list; i++) 179 bits.bits[parm->list[i] / 32] |= 1U << (parm->list[i] % 32); 180 return snd_mask_refine(mask, &bits); 181} 182 183 184/* 185 * hw_refine callback 186 */ 187static int extplug_hw_refine(snd_pcm_hw_params_t *hw_params, 188 struct snd_ext_parm *parm) 189{ 190 int i, err, change = 0; 191 for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) { 192 int type = hw_params_type[i]; 193 if (is_mask_type(i)) 194 err = snd_ext_parm_mask_refine(hw_param_mask(hw_params, type), 195 parm, i); 196 else 197 err = snd_ext_parm_interval_refine(hw_param_interval(hw_params, type), 198 parm, i); 199 if (err < 0) 200 return err; 201 change |= err; 202 } 203 return change; 204} 205 206static int snd_pcm_extplug_hw_refine_cprepare(snd_pcm_t *pcm, 207 snd_pcm_hw_params_t *params) 208{ 209 extplug_priv_t *ext = pcm->private_data; 210 int err; 211 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM }; 212 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS, 213 &access_mask); 214 if (err < 0) 215 return err; 216 err = extplug_hw_refine(params, ext->params); 217 if (err < 0) 218 return err; 219 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); 220 return 0; 221} 222 223static int snd_pcm_extplug_hw_refine_sprepare(snd_pcm_t *pcm, 224 snd_pcm_hw_params_t *sparams) 225{ 226 extplug_priv_t *ext = pcm->private_data; 227 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP }; 228 _snd_pcm_hw_params_any(sparams); 229 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS, 230 &saccess_mask); 231 extplug_hw_refine(sparams, ext->sparams); 232 return 0; 233} 234 235static unsigned int get_links(struct snd_ext_parm *params) 236{ 237 int i; 238 unsigned int links = (SND_PCM_HW_PARBIT_FORMAT | 239 SND_PCM_HW_PARBIT_SUBFORMAT | 240 SND_PCM_HW_PARBIT_SAMPLE_BITS | 241 SND_PCM_HW_PARBIT_CHANNELS | 242 SND_PCM_HW_PARBIT_FRAME_BITS | 243 SND_PCM_HW_PARBIT_RATE | 244 SND_PCM_HW_PARBIT_PERIODS | 245 SND_PCM_HW_PARBIT_PERIOD_SIZE | 246 SND_PCM_HW_PARBIT_PERIOD_TIME | 247 SND_PCM_HW_PARBIT_BUFFER_SIZE | 248 SND_PCM_HW_PARBIT_BUFFER_TIME | 249 SND_PCM_HW_PARBIT_TICK_TIME); 250 251 for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) { 252 if (params[i].active && !params[i].keep_link) 253 links &= ~excl_parbits[i]; 254 } 255 return links; 256} 257 258static int snd_pcm_extplug_hw_refine_schange(snd_pcm_t *pcm, 259 snd_pcm_hw_params_t *params, 260 snd_pcm_hw_params_t *sparams) 261{ 262 extplug_priv_t *ext = pcm->private_data; 263 unsigned int links = get_links(ext->sparams); 264 265 return _snd_pcm_hw_params_refine(sparams, links, params); 266} 267 268static int snd_pcm_extplug_hw_refine_cchange(snd_pcm_t *pcm, 269 snd_pcm_hw_params_t *params, 270 snd_pcm_hw_params_t *sparams) 271{ 272 extplug_priv_t *ext = pcm->private_data; 273 unsigned int links = get_links(ext->params); 274 275 return _snd_pcm_hw_params_refine(params, links, sparams); 276} 277 278static int snd_pcm_extplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 279{ 280 int err = snd_pcm_hw_refine_slave(pcm, params, 281 snd_pcm_extplug_hw_refine_cprepare, 282 snd_pcm_extplug_hw_refine_cchange, 283 snd_pcm_extplug_hw_refine_sprepare, 284 snd_pcm_extplug_hw_refine_schange, 285 snd_pcm_generic_hw_refine); 286 return err; 287} 288 289/* 290 * hw_params callback 291 */ 292static int snd_pcm_extplug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 293{ 294 295 extplug_priv_t *ext = pcm->private_data; 296 snd_pcm_t *slave = ext->plug.gen.slave; 297 int err = snd_pcm_hw_params_slave(pcm, params, 298 snd_pcm_extplug_hw_refine_cchange, 299 snd_pcm_extplug_hw_refine_sprepare, 300 snd_pcm_extplug_hw_refine_schange, 301 snd_pcm_generic_hw_params); 302 if (err < 0) 303 return err; 304 ext->data->slave_format = slave->format; 305 ext->data->slave_subformat = slave->subformat; 306 ext->data->slave_channels = slave->channels; 307 ext->data->rate = slave->rate; 308 INTERNAL(snd_pcm_hw_params_get_format)(params, &ext->data->format); 309 INTERNAL(snd_pcm_hw_params_get_subformat)(params, &ext->data->subformat); 310 INTERNAL(snd_pcm_hw_params_get_channels)(params, &ext->data->channels); 311 312 if (ext->data->callback->hw_params) { 313 err = ext->data->callback->hw_params(ext->data, params); 314 if (err < 0) 315 return err; 316 } 317 return 0; 318} 319 320/* 321 * hw_free callback 322 */ 323static int snd_pcm_extplug_hw_free(snd_pcm_t *pcm) 324{ 325 extplug_priv_t *ext = pcm->private_data; 326 327 snd_pcm_hw_free(ext->plug.gen.slave); 328 if (ext->data->callback->hw_free) 329 return ext->data->callback->hw_free(ext->data); 330 return 0; 331} 332 333/* 334 * write_areas skeleton - call transfer callback 335 */ 336static snd_pcm_uframes_t 337snd_pcm_extplug_write_areas(snd_pcm_t *pcm, 338 const snd_pcm_channel_area_t *areas, 339 snd_pcm_uframes_t offset, 340 snd_pcm_uframes_t size, 341 const snd_pcm_channel_area_t *slave_areas, 342 snd_pcm_uframes_t slave_offset, 343 snd_pcm_uframes_t *slave_sizep) 344{ 345 extplug_priv_t *ext = pcm->private_data; 346 347 if (size > *slave_sizep) 348 size = *slave_sizep; 349 size = ext->data->callback->transfer(ext->data, slave_areas, slave_offset, 350 areas, offset, size); 351 *slave_sizep = size; 352 return size; 353} 354 355/* 356 * read_areas skeleton - call transfer callback 357 */ 358static snd_pcm_uframes_t 359snd_pcm_extplug_read_areas(snd_pcm_t *pcm, 360 const snd_pcm_channel_area_t *areas, 361 snd_pcm_uframes_t offset, 362 snd_pcm_uframes_t size, 363 const snd_pcm_channel_area_t *slave_areas, 364 snd_pcm_uframes_t slave_offset, 365 snd_pcm_uframes_t *slave_sizep) 366{ 367 extplug_priv_t *ext = pcm->private_data; 368 369 if (size > *slave_sizep) 370 size = *slave_sizep; 371 size = ext->data->callback->transfer(ext->data, areas, offset, 372 slave_areas, slave_offset, size); 373 *slave_sizep = size; 374 return size; 375} 376 377/* 378 * call init callback 379 */ 380static int snd_pcm_extplug_init(snd_pcm_t *pcm) 381{ 382 extplug_priv_t *ext = pcm->private_data; 383 return ext->data->callback->init(ext->data); 384} 385 386/* 387 * dump setup 388 */ 389static void snd_pcm_extplug_dump(snd_pcm_t *pcm, snd_output_t *out) 390{ 391 extplug_priv_t *ext = pcm->private_data; 392 393 if (ext->data->callback->dump) 394 ext->data->callback->dump(ext->data, out); 395 else { 396 if (ext->data->name) 397 snd_output_printf(out, "%s\n", ext->data->name); 398 else 399 snd_output_printf(out, "External PCM Plugin\n"); 400 if (pcm->setup) { 401 snd_output_printf(out, "Its setup is:\n"); 402 snd_pcm_dump_setup(pcm, out); 403 } 404 } 405 snd_output_printf(out, "Slave: "); 406 snd_pcm_dump(ext->plug.gen.slave, out); 407} 408 409static void clear_ext_params(extplug_priv_t *ext) 410{ 411 int i; 412 for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) { 413 snd_ext_parm_clear(&ext->params[i]); 414 snd_ext_parm_clear(&ext->sparams[i]); 415 } 416} 417 418static int snd_pcm_extplug_close(snd_pcm_t *pcm) 419{ 420 extplug_priv_t *ext = pcm->private_data; 421 422 snd_pcm_close(ext->plug.gen.slave); 423 clear_ext_params(ext); 424 if (ext->data->callback->close) 425 ext->data->callback->close(ext->data); 426 free(ext); 427 return 0; 428} 429 430static snd_pcm_chmap_query_t **snd_pcm_extplug_query_chmaps(snd_pcm_t *pcm) 431{ 432 extplug_priv_t *ext = pcm->private_data; 433 434 if (ext->data->version >= 0x010002 && 435 ext->data->callback->query_chmaps) 436 return ext->data->callback->query_chmaps(ext->data); 437 return snd_pcm_generic_query_chmaps(pcm); 438} 439 440static snd_pcm_chmap_t *snd_pcm_extplug_get_chmap(snd_pcm_t *pcm) 441{ 442 extplug_priv_t *ext = pcm->private_data; 443 444 if (ext->data->version >= 0x010002 && 445 ext->data->callback->get_chmap) 446 return ext->data->callback->get_chmap(ext->data); 447 return snd_pcm_generic_get_chmap(pcm); 448} 449 450static int snd_pcm_extplug_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) 451{ 452 extplug_priv_t *ext = pcm->private_data; 453 454 if (ext->data->version >= 0x010002 && 455 ext->data->callback->set_chmap) 456 return ext->data->callback->set_chmap(ext->data, map); 457 return snd_pcm_generic_set_chmap(pcm, map); 458} 459 460static const snd_pcm_ops_t snd_pcm_extplug_ops = { 461 .close = snd_pcm_extplug_close, 462 .info = snd_pcm_generic_info, 463 .hw_refine = snd_pcm_extplug_hw_refine, 464 .hw_params = snd_pcm_extplug_hw_params, 465 .hw_free = snd_pcm_extplug_hw_free, 466 .sw_params = snd_pcm_generic_sw_params, 467 .channel_info = snd_pcm_generic_channel_info, 468 .dump = snd_pcm_extplug_dump, 469 .nonblock = snd_pcm_generic_nonblock, 470 .async = snd_pcm_generic_async, 471 .mmap = snd_pcm_generic_mmap, 472 .munmap = snd_pcm_generic_munmap, 473 .query_chmaps = snd_pcm_extplug_query_chmaps, 474 .get_chmap = snd_pcm_extplug_get_chmap, 475 .set_chmap = snd_pcm_extplug_set_chmap, 476}; 477 478#endif /* !DOC_HIDDEN */ 479 480/* 481 * Exported functions 482 */ 483 484/*! \page pcm_external_plugins PCM External Plugin SDK 485 486\section pcm_externals External Plugins 487 488The external plugins are implemented in a shared object file located 489at /usr/lib/alsa-lib (the exact location depends on the build option 490and asoundrc configuration). It has to be the file like 491libasound_module_pcm_MYPLUGIN.so, where MYPLUGIN corresponds to your 492own plugin name. 493 494The entry point of the plugin is defined via 495#SND_PCM_PLUGIN_DEFINE_FUNC() macro. This macro defines the function 496with a proper name to be referred from alsa-lib. The function takes 497the following 6 arguments: 498\code 499int (snd_pcm_t **pcmp, const char *name, snd_config_t *root, 500 snd_config_t *conf, snd_pcm_stream_t stream, int mode) 501\endcode 502The first argument, pcmp, is the pointer to store the resultant PCM 503handle. The arguments name, root, stream and mode are the parameters 504to be passed to the plugin constructor. The conf is the configuration 505tree for the plugin. The arguments above are defined in the macro 506itself, so don't use variables with the same names to shadow 507parameters. 508 509After parsing the configuration parameters in the given conf tree, 510usually you will call the external plugin API function, 511#snd_pcm_extplug_create() or #snd_pcm_ioplug_create(), depending 512on the plugin type. The PCM handle must be filled *pcmp in return. 513Then this function must return either a value 0 when succeeded, or a 514negative value as the error code. 515 516Finally, add #SND_PCM_PLUGIN_SYMBOL() with the name of your 517plugin as the argument at the end. This defines the proper versioned 518symbol as the reference. 519 520The typical code would look like below: 521\code 522struct myplug_info { 523 snd_pcm_extplug_t ext; 524 int my_own_data; 525 ... 526}; 527 528SND_PCM_PLUGIN_DEFINE_FUNC(myplug) 529{ 530 snd_config_iterator_t i, next; 531 snd_config_t *slave = NULL; 532 struct myplug_info *myplug; 533 int err; 534 535 snd_config_for_each(i, next, conf) { 536 snd_config_t *n = snd_config_iterator_entry(i); 537 const char *id; 538 if (snd_config_get_id(n, &id) < 0) 539 continue; 540 if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) 541 continue; 542 if (strcmp(id, "slave") == 0) { 543 slave = n; 544 continue; 545 } 546 if (strcmp(id, "my_own_parameter") == 0) { 547 .... 548 continue; 549 } 550 SNDERR("Unknown field %s", id); 551 return -EINVAL; 552 } 553 554 if (! slave) { 555 SNDERR("No slave defined for myplug"); 556 return -EINVAL; 557 } 558 559 myplug = calloc(1, sizeof(*myplug)); 560 if (myplug == NULL) 561 return -ENOMEM; 562 563 myplug->ext.version = SND_PCM_EXTPLUG_VERSION; 564 myplug->ext.name = "My Own Plugin"; 565 myplug->ext.callback = &my_own_callback; 566 myplug->ext.private_data = myplug; 567 .... 568 569 err = snd_pcm_extplug_create(&myplug->ext, name, root, conf, stream, mode); 570 if (err < 0) { 571 myplug_free(myplug); 572 return err; 573 } 574 575 *pcmp = myplug->ext.pcm; 576 return 0; 577} 578 579SND_PCM_PLUGIN_SYMBOL(myplug); 580\endcode 581 582Read the codes in alsa-plugins package for the real examples. 583 584 585\section pcm_extplug External Plugin: Filter-Type Plugin 586 587The filter-type plugin is a plugin to convert the PCM signals from the input 588and feeds to the output. Thus, this plugin always needs a slave PCM as its output. 589 590The plugin can modify the format and the channels of the input/output PCM. 591It can <i>not</i> modify the sample rate (because of simplicity reason). 592 593The following fields have to be filled in extplug record before calling 594#snd_pcm_extplug_create() : version, name, callback. 595Otherfields are optional and should be initialized with zero. 596 597The constant #SND_PCM_EXTPLUG_VERSION must be passed to the version 598field for the version check in alsa-lib. A non-NULL ASCII string 599has to be passed to the name field. The callback field contains the 600table of callback functions for this plugin (defined as 601#snd_pcm_extplug_callback_t). 602 603The driver can set an arbitrary value (pointer) to private_data 604field to refer its own data in the callbacks. 605 606The rest fields are filled by #snd_pcm_extplug_create(). The pcm field 607is the resultant PCM handle. The others are the current status of the 608PCM. 609 610The callback functions in #snd_pcm_extplug_callback_t define the real 611behavior of the driver. 612At least, transfer callback must be given. This callback is called 613at each time certain size of data block is transfered to the slave 614PCM. Other callbacks are optional. 615 616The close callback is called when the PCM is closed. If the plugin 617allocates private resources, this is the place to release them 618again. The hw_params and hw_free callbacks are called at 619#snd_pcm_hw_params() and #snd_pcm_hw_free() API calls, 620respectively. The last, dump callback, is called for printing the 621information of the given plugin. 622 623The init callback is called when the PCM is at prepare state or any 624initialization is issued. Use this callback to reset the PCM instance 625to a sane initial state. 626 627The hw_params constraints can be defined via either 628#snd_pcm_extplug_set_param_minmax() and #snd_pcm_extplug_set_param_list() 629functions after calling #snd_pcm_extplug_create(). 630The former defines the minimal and maximal acceptable values for the 631given hw_params parameter (SND_PCM_EXTPLUG_HW_XXX). 632This function can't be used for the format parameter. The latter 633function specifies the available parameter values as the list. 634As mentioned above, the rate can't be changed. Only changeable 635parameters are sample format and channels. 636 637To define the constraints of the slave PCM configuration, use 638either #snd_pcm_extplug_set_slave_param_minmax() and 639#snd_pcm_extplug_set_slave_param_list(). The arguments are as same 640as former functions. 641 642To clear the parameter constraints, call #snd_pcm_extplug_params_reset() 643function. 644 645When using snd_pcm_extplug_set_param_*() or snd_pcm_extplug_set_slave_param_*() 646for any parameter. This parameter is no longer linked between the client and 647slave PCM. Therefore it could differ and the extplug has to support conversion 648between all valid parameter configurations. To keep the client and slave 649parameter linked #snd_pcm_extplug_set_param_link() can be used for the 650corresponding parameter. For example if the extplug does not support channel nor 651format conversion the supported client parameters can be limited with 652snd_pcm_extplug_set_param_*() and afterwards 653snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_FORMAT, 1) and 654snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1) should be 655called to keep the client and slave parameters the same. 656*/ 657 658/** 659 * \brief Create an extplug instance 660 * \param extplug the extplug handle 661 * \param name name of the PCM 662 * \param root configuration tree root 663 * \param slave_conf slave configuration root 664 * \param stream stream direction 665 * \param mode PCM open mode 666 * \return 0 if successful, or a negative error code 667 * 668 * Creates the extplug instance based on the given handle. 669 * The slave_conf argument is mandatory, and usually taken from the config tree of the 670 * PCM plugin as "slave" config value. 671 * name, root, stream and mode arguments are the values used for opening the PCM. 672 * 673 * The callback is the mandatory field of extplug handle. At least, start, stop and 674 * pointer callbacks must be set before calling this function. 675 */ 676int snd_pcm_extplug_create(snd_pcm_extplug_t *extplug, const char *name, 677 snd_config_t *root, snd_config_t *slave_conf, 678 snd_pcm_stream_t stream, int mode) 679{ 680 extplug_priv_t *ext; 681 int err; 682 snd_pcm_t *spcm, *pcm; 683 snd_config_t *sconf; 684 685 assert(root); 686 assert(extplug && extplug->callback); 687 assert(extplug->callback->transfer); 688 assert(slave_conf); 689 690 /* We support 1.0.0 to current */ 691 if (extplug->version < 0x010000 || 692 extplug->version > SND_PCM_EXTPLUG_VERSION) { 693 SNDERR("extplug: Plugin version mismatch: 0x%x", 694 extplug->version); 695 return -ENXIO; 696 } 697 698 err = snd_pcm_slave_conf(root, slave_conf, &sconf, 0); 699 if (err < 0) 700 return err; 701 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, NULL); 702 snd_config_delete(sconf); 703 if (err < 0) 704 return err; 705 706 ext = calloc(1, sizeof(*ext)); 707 if (! ext) 708 return -ENOMEM; 709 710 ext->data = extplug; 711 extplug->stream = stream; 712 713 snd_pcm_plugin_init(&ext->plug); 714 ext->plug.read = snd_pcm_extplug_read_areas; 715 ext->plug.write = snd_pcm_extplug_write_areas; 716 ext->plug.undo_read = snd_pcm_plugin_undo_read_generic; 717 ext->plug.undo_write = snd_pcm_plugin_undo_write_generic; 718 ext->plug.gen.slave = spcm; 719 ext->plug.gen.close_slave = 1; 720 if (extplug->version >= 0x010001 && extplug->callback->init) 721 ext->plug.init = snd_pcm_extplug_init; 722 723 err = snd_pcm_new(&pcm, SND_PCM_TYPE_EXTPLUG, name, stream, mode); 724 if (err < 0) { 725 free(ext); 726 return err; 727 } 728 729 extplug->pcm = pcm; 730 pcm->ops = &snd_pcm_extplug_ops; 731 pcm->fast_ops = &snd_pcm_plugin_fast_ops; 732 pcm->private_data = ext; 733 pcm->poll_fd = spcm->poll_fd; 734 pcm->poll_events = spcm->poll_events; 735 pcm->tstamp_type = spcm->tstamp_type; 736 snd_pcm_set_hw_ptr(pcm, &ext->plug.hw_ptr, -1, 0); 737 snd_pcm_set_appl_ptr(pcm, &ext->plug.appl_ptr, -1, 0); 738 739 return 0; 740} 741 742/** 743 * \brief Delete the extplug instance 744 * \param extplug the extplug handle to delete 745 * \return 0 if successful, or a negative error code 746 * 747 * The destructor of extplug instance. 748 * Closes the PCM and deletes the associated resources. 749 */ 750int snd_pcm_extplug_delete(snd_pcm_extplug_t *extplug) 751{ 752 return snd_pcm_close(extplug->pcm); 753} 754 755 756/** 757 * \brief Reset extplug parameters 758 * \param extplug the extplug handle 759 * 760 * Resets the all parameters for the given extplug handle. 761 */ 762void snd_pcm_extplug_params_reset(snd_pcm_extplug_t *extplug) 763{ 764 extplug_priv_t *ext = extplug->pcm->private_data; 765 clear_ext_params(ext); 766} 767 768/** 769 * \brief Set slave parameter as the list 770 * \param extplug the extplug handle 771 * \param type parameter type 772 * \param num_list number of available values 773 * \param list the list of available values 774 * \return 0 if successful, or a negative error code 775 * 776 * Sets the slave parameter as the list. 777 * The available values of the given parameter type of the slave PCM is restricted 778 * to the ones of the given list. 779 */ 780int snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list) 781{ 782 extplug_priv_t *ext = extplug->pcm->private_data; 783 if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) { 784 SNDERR("EXTPLUG: invalid parameter type %d", type); 785 return -EINVAL; 786 } 787 return snd_ext_parm_set_list(&ext->sparams[type], num_list, list); 788} 789 790/** 791 * \brief Set slave parameter as the min/max values 792 * \param extplug the extplug handle 793 * \param type parameter type 794 * \param min the minimum value 795 * \param max the maximum value 796 * \return 0 if successful, or a negative error code 797 * 798 * Sets the slave parameter as the min/max values. 799 * The available values of the given parameter type of the slave PCM is restricted 800 * between the given minimum and maximum values. 801 */ 802int snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max) 803{ 804 extplug_priv_t *ext = extplug->pcm->private_data; 805 if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) { 806 SNDERR("EXTPLUG: invalid parameter type %d", type); 807 return -EINVAL; 808 } 809 if (is_mask_type(type)) { 810 SNDERR("EXTPLUG: invalid parameter type %d", type); 811 return -EINVAL; 812 } 813 return snd_ext_parm_set_minmax(&ext->sparams[type], min, max); 814} 815 816/** 817 * \brief Set master parameter as the list 818 * \param extplug the extplug handle 819 * \param type parameter type 820 * \param num_list number of available values 821 * \param list the list of available values 822 * \return 0 if successful, or a negative error code 823 * 824 * Sets the master parameter as the list. 825 * The available values of the given parameter type of this PCM (as input) is restricted 826 * to the ones of the given list. 827 */ 828int snd_pcm_extplug_set_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list) 829{ 830 extplug_priv_t *ext = extplug->pcm->private_data; 831 if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) { 832 SNDERR("EXTPLUG: invalid parameter type %d", type); 833 return -EINVAL; 834 } 835 return snd_ext_parm_set_list(&ext->params[type], num_list, list); 836} 837 838/** 839 * \brief Set master parameter as the min/max values 840 * \param extplug the extplug handle 841 * \param type parameter type 842 * \param min the minimum value 843 * \param max the maximum value 844 * \return 0 if successful, or a negative error code 845 * 846 * Sets the master parameter as the min/max values. 847 * The available values of the given parameter type of this PCM (as input) is restricted 848 * between the given minimum and maximum values. 849 */ 850int snd_pcm_extplug_set_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max) 851{ 852 extplug_priv_t *ext = extplug->pcm->private_data; 853 if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) { 854 SNDERR("EXTPLUG: invalid parameter type %d", type); 855 return -EINVAL; 856 } 857 if (is_mask_type(type)) { 858 SNDERR("EXTPLUG: invalid parameter type %d", type); 859 return -EINVAL; 860 } 861 return snd_ext_parm_set_minmax(&ext->params[type], min, max); 862} 863 864/** 865 * @brief Keep the client and slave format/channels the same if requested. This 866 * is for example useful if this extplug does not support any channel 867 * conversion. 868 * @param extplug the extplug handle 869 * @param type parameter type 870 * @param keep_link if 1 the parameter identified by type will be kept the same 871 * for the client and slave PCM of this extplug 872 * @return 0 if successful, or a negative error code 873 */ 874int snd_pcm_extplug_set_param_link(snd_pcm_extplug_t *extplug, int type, 875 int keep_link) 876{ 877 extplug_priv_t *ext = extplug->pcm->private_data; 878 879 if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) { 880 SNDERR("EXTPLUG: invalid parameter type %d", type); 881 return -EINVAL; 882 } 883 ext->params[type].keep_link = keep_link ? 1 : 0; 884 ext->sparams[type].keep_link = keep_link ? 1 : 0; 885 return 0; 886} 887