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 */
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 */
snd_ext_parm_set_minmax(struct snd_ext_parm * parm,unsigned int min,unsigned int max)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 */
val_compar(const void * ap,const void * bp)80 static int val_compar(const void *ap, const void *bp)
81 {
82 return *(const unsigned int *)ap - *(const unsigned int *)bp;
83 }
84
snd_ext_parm_set_list(struct snd_ext_parm * parm,unsigned int num_list,const unsigned int * list)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
snd_ext_parm_clear(struct snd_ext_parm * parm)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 */
snd_interval_list(snd_interval_t * ival,int num_list,unsigned int * list)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 */
snd_ext_parm_interval_refine(snd_interval_t * ival,struct snd_ext_parm * parm,int type)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 */
snd_ext_parm_mask_refine(snd_mask_t * mask,struct snd_ext_parm * parm,int type)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 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 */
extplug_hw_refine(snd_pcm_hw_params_t * hw_params,struct snd_ext_parm * parm)187 static 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
snd_pcm_extplug_hw_refine_cprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)206 static 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
snd_pcm_extplug_hw_refine_sprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * sparams)223 static 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
get_links(struct snd_ext_parm * params)235 static 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
snd_pcm_extplug_hw_refine_schange(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)258 static 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
snd_pcm_extplug_hw_refine_cchange(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)268 static 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
snd_pcm_extplug_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)278 static 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 */
snd_pcm_extplug_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)292 static 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 */
snd_pcm_extplug_hw_free(snd_pcm_t * pcm)323 static 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 */
336 static snd_pcm_uframes_t
snd_pcm_extplug_write_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)337 snd_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 */
358 static snd_pcm_uframes_t
snd_pcm_extplug_read_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)359 snd_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 */
snd_pcm_extplug_init(snd_pcm_t * pcm)380 static 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 */
snd_pcm_extplug_dump(snd_pcm_t * pcm,snd_output_t * out)389 static 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
clear_ext_params(extplug_priv_t * ext)409 static 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
snd_pcm_extplug_close(snd_pcm_t * pcm)418 static 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
snd_pcm_extplug_query_chmaps(snd_pcm_t * pcm)430 static 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
snd_pcm_extplug_get_chmap(snd_pcm_t * pcm)440 static 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
snd_pcm_extplug_set_chmap(snd_pcm_t * pcm,const snd_pcm_chmap_t * map)450 static 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
460 static 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
488 The external plugins are implemented in a shared object file located
489 at /usr/lib/alsa-lib (the exact location depends on the build option
490 and asoundrc configuration). It has to be the file like
491 libasound_module_pcm_MYPLUGIN.so, where MYPLUGIN corresponds to your
492 own plugin name.
493
494 The entry point of the plugin is defined via
495 #SND_PCM_PLUGIN_DEFINE_FUNC() macro. This macro defines the function
496 with a proper name to be referred from alsa-lib. The function takes
497 the following 6 arguments:
498 \code
499 int (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
502 The first argument, pcmp, is the pointer to store the resultant PCM
503 handle. The arguments name, root, stream and mode are the parameters
504 to be passed to the plugin constructor. The conf is the configuration
505 tree for the plugin. The arguments above are defined in the macro
506 itself, so don't use variables with the same names to shadow
507 parameters.
508
509 After parsing the configuration parameters in the given conf tree,
510 usually you will call the external plugin API function,
511 #snd_pcm_extplug_create() or #snd_pcm_ioplug_create(), depending
512 on the plugin type. The PCM handle must be filled *pcmp in return.
513 Then this function must return either a value 0 when succeeded, or a
514 negative value as the error code.
515
516 Finally, add #SND_PCM_PLUGIN_SYMBOL() with the name of your
517 plugin as the argument at the end. This defines the proper versioned
518 symbol as the reference.
519
520 The typical code would look like below:
521 \code
522 struct myplug_info {
523 snd_pcm_extplug_t ext;
524 int my_own_data;
525 ...
526 };
527
528 SND_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
579 SND_PCM_PLUGIN_SYMBOL(myplug);
580 \endcode
581
582 Read the codes in alsa-plugins package for the real examples.
583
584
585 \section pcm_extplug External Plugin: Filter-Type Plugin
586
587 The filter-type plugin is a plugin to convert the PCM signals from the input
588 and feeds to the output. Thus, this plugin always needs a slave PCM as its output.
589
590 The plugin can modify the format and the channels of the input/output PCM.
591 It can <i>not</i> modify the sample rate (because of simplicity reason).
592
593 The following fields have to be filled in extplug record before calling
594 #snd_pcm_extplug_create() : version, name, callback.
595 Otherfields are optional and should be initialized with zero.
596
597 The constant #SND_PCM_EXTPLUG_VERSION must be passed to the version
598 field for the version check in alsa-lib. A non-NULL ASCII string
599 has to be passed to the name field. The callback field contains the
600 table of callback functions for this plugin (defined as
601 #snd_pcm_extplug_callback_t).
602
603 The driver can set an arbitrary value (pointer) to private_data
604 field to refer its own data in the callbacks.
605
606 The rest fields are filled by #snd_pcm_extplug_create(). The pcm field
607 is the resultant PCM handle. The others are the current status of the
608 PCM.
609
610 The callback functions in #snd_pcm_extplug_callback_t define the real
611 behavior of the driver.
612 At least, transfer callback must be given. This callback is called
613 at each time certain size of data block is transfered to the slave
614 PCM. Other callbacks are optional.
615
616 The close callback is called when the PCM is closed. If the plugin
617 allocates private resources, this is the place to release them
618 again. The hw_params and hw_free callbacks are called at
619 #snd_pcm_hw_params() and #snd_pcm_hw_free() API calls,
620 respectively. The last, dump callback, is called for printing the
621 information of the given plugin.
622
623 The init callback is called when the PCM is at prepare state or any
624 initialization is issued. Use this callback to reset the PCM instance
625 to a sane initial state.
626
627 The hw_params constraints can be defined via either
628 #snd_pcm_extplug_set_param_minmax() and #snd_pcm_extplug_set_param_list()
629 functions after calling #snd_pcm_extplug_create().
630 The former defines the minimal and maximal acceptable values for the
631 given hw_params parameter (SND_PCM_EXTPLUG_HW_XXX).
632 This function can't be used for the format parameter. The latter
633 function specifies the available parameter values as the list.
634 As mentioned above, the rate can't be changed. Only changeable
635 parameters are sample format and channels.
636
637 To define the constraints of the slave PCM configuration, use
638 either #snd_pcm_extplug_set_slave_param_minmax() and
639 #snd_pcm_extplug_set_slave_param_list(). The arguments are as same
640 as former functions.
641
642 To clear the parameter constraints, call #snd_pcm_extplug_params_reset()
643 function.
644
645 When using snd_pcm_extplug_set_param_*() or snd_pcm_extplug_set_slave_param_*()
646 for any parameter. This parameter is no longer linked between the client and
647 slave PCM. Therefore it could differ and the extplug has to support conversion
648 between all valid parameter configurations. To keep the client and slave
649 parameter linked #snd_pcm_extplug_set_param_link() can be used for the
650 corresponding parameter. For example if the extplug does not support channel nor
651 format conversion the supported client parameters can be limited with
652 snd_pcm_extplug_set_param_*() and afterwards
653 #snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_FORMAT, 1) and
654 #snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1) should be
655 called 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 */
snd_pcm_extplug_create(snd_pcm_extplug_t * extplug,const char * name,snd_config_t * root,snd_config_t * slave_conf,snd_pcm_stream_t stream,int mode)676 int 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\n",
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 */
snd_pcm_extplug_delete(snd_pcm_extplug_t * extplug)750 int 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 */
snd_pcm_extplug_params_reset(snd_pcm_extplug_t * extplug)762 void 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 */
snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t * extplug,int type,unsigned int num_list,const unsigned int * list)780 int 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 */
snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t * extplug,int type,unsigned int min,unsigned int max)802 int 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 */
snd_pcm_extplug_set_param_list(snd_pcm_extplug_t * extplug,int type,unsigned int num_list,const unsigned int * list)828 int 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 */
snd_pcm_extplug_set_param_minmax(snd_pcm_extplug_t * extplug,int type,unsigned int min,unsigned int max)850 int 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 */
snd_pcm_extplug_set_param_link(snd_pcm_extplug_t * extplug,int type,int keep_link)874 int 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