1 /*
2 * Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
3 *
4 * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
5 * the the people's Republic of China and other countries.
6 * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
7 *
8 * DISCLAIMER
9 * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
10 * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
11 * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
12 * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
13 * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
14 * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
15 * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
16 *
17 *
18 * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
19 * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
20 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
21 * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
22 * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <aw-alsa-lib/pcm_extplug.h>
37 #include "pcm_local.h"
38 #include "pcm_plugin_generic.h"
39 #include "pcm_ext_parm.h"
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
49 static const int hw_params_type[SND_PCM_EXTPLUG_HW_PARAMS] = {
50 [SND_PCM_EXTPLUG_HW_FORMAT] = SND_PCM_HW_PARAM_FORMAT,
51 [SND_PCM_EXTPLUG_HW_CHANNELS] = SND_PCM_HW_PARAM_CHANNELS
52 };
53
54 #define is_mask_type(i) (hw_params_type[i] < SND_PCM_HW_PARAM_FIRST_RANGE)
55
56 static const unsigned int excl_parbits[SND_PCM_EXTPLUG_HW_PARAMS] = {
57 [SND_PCM_EXTPLUG_HW_FORMAT] = (SND_PCM_HW_PARBIT_FORMAT|
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 range to the given list
110 */
_snd_range_list(snd_interval_t * ival,int num_list,unsigned int * list)111 static int _snd_range_list(snd_interval_t *ival, int num_list, unsigned int *list)
112 {
113 int imin, imax;
114 int changed = 0;
115
116 if (snd_range_empty(ival))
117 return -ENOENT;
118 for (imin = 0; imin < num_list; imin++) {
119 if (ival->range.min == list[imin] && ! ival->range.openmin)
120 break;
121 if (ival->range.min <= list[imin]) {
122 ival->range.min = list[imin];
123 ival->range.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->range.max == list[imax] && ! ival->range.openmax)
132 break;
133 if (ival->range.max >= list[imax]) {
134 ival->range.max = list[imax];
135 ival->range.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 range parameter
147 */
snd_ext_parm_range_refine(snd_interval_t * ival,struct snd_ext_parm * parm,int type)148 int snd_ext_parm_range_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->range.integer |= parm->integer;
154 if (parm->num_list) {
155 return _snd_range_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_range_set_minmax(&t, parm->min, parm->max);
160 t.range.integer = ival->range.integer;
161 return snd_range_refine(ival, &t);
162 }
163 return 0;
164 }
165
166 /*
167 * refine the mask parameter
168 */
snd_ext_parm_mask_refine(snd_interval_t * mask,struct snd_ext_parm * parm,int type)169 int snd_ext_parm_mask_refine(snd_interval_t *mask, struct snd_ext_parm *parm, int type)
170 {
171 snd_interval_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.mask |= 1U << (parm->list[i] % 32);
180 return snd_mask_refine(mask, &bits);
181 }
182
183
snd_pcm_extplug_set_param_list(snd_pcm_extplug_t * extplug,int type,unsigned int num_list,const unsigned int * list)184 int snd_pcm_extplug_set_param_list(snd_pcm_extplug_t *extplug, int type,
185 unsigned int num_list, const unsigned int *list)
186 {
187 extplug_priv_t *ext = extplug->pcm->private_data;
188 if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
189 awalsa_err("EXTPLUG: invalid parameter type %d\n", type);
190 return -EINVAL;
191 }
192 return snd_ext_parm_set_list(&ext->params[type], num_list, list);
193 }
194
snd_pcm_extplug_set_param_minmax(snd_pcm_extplug_t * extplug,int type,unsigned int min,unsigned int max)195 int snd_pcm_extplug_set_param_minmax(snd_pcm_extplug_t *extplug, int type,
196 unsigned int min, unsigned int max)
197 {
198 extplug_priv_t *ext = extplug->pcm->private_data;
199 if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
200 awalsa_err("EXTPLUG: invalid parameter type %d\n", type);
201 return -EINVAL;
202 }
203 if (is_mask_type(type)) {
204 awalsa_err("EXTPLUG: invalid parameter type %d\n", type);
205 return -EINVAL;
206 }
207 return snd_ext_parm_set_minmax(&ext->params[type], min, max);
208 }
209
snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t * extplug,int type,unsigned int num_list,const unsigned int * list)210 int snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t *extplug, int type,
211 unsigned int num_list, const unsigned int *list)
212 {
213 extplug_priv_t *ext = extplug->pcm->private_data;
214 if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
215 awalsa_err("EXTPLUG: invalid parameter type %d\n", type);
216 return -EINVAL;
217 }
218 return snd_ext_parm_set_list(&ext->sparams[type], num_list, list);
219 }
220
snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t * extplug,int type,unsigned int min,unsigned int max)221 int snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t *extplug, int type,
222 unsigned int min, unsigned int max)
223 {
224 extplug_priv_t *ext = extplug->pcm->private_data;
225 if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
226 awalsa_err("EXTPLUG: invalid parameter type %d\n", type);
227 return -EINVAL;
228 }
229 if (is_mask_type(type)) {
230 awalsa_err("EXTPLUG: invalid parameter type %d\n", type);
231 return -EINVAL;
232 }
233 return snd_ext_parm_set_minmax(&ext->sparams[type], min, max);
234 }
235
clear_ext_params(extplug_priv_t * ext)236 static void clear_ext_params(extplug_priv_t *ext)
237 {
238 int i;
239 for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
240 snd_ext_parm_clear(&ext->params[i]);
241 snd_ext_parm_clear(&ext->sparams[i]);
242 }
243 }
244
snd_pcm_extplug_params_reset(snd_pcm_extplug_t * extplug)245 void snd_pcm_extplug_params_reset(snd_pcm_extplug_t *extplug)
246 {
247 extplug_priv_t *ext = extplug->pcm->private_data;
248 clear_ext_params(ext);
249 }
250
251 /*
252 * call init callback
253 */
snd_pcm_extplug_init(snd_pcm_t * pcm)254 static int snd_pcm_extplug_init(snd_pcm_t *pcm)
255 {
256 awalsa_debug("\n");
257 extplug_priv_t *ext = pcm->private_data;
258 return ext->data->callback->init(ext->data);
259 }
260
261 /*
262 * write_areas skeleton - call transfer callback
263 */
264 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)265 snd_pcm_extplug_write_areas(snd_pcm_t *pcm,
266 const snd_pcm_channel_area_t *areas,
267 snd_pcm_uframes_t offset,
268 snd_pcm_uframes_t size,
269 const snd_pcm_channel_area_t *slave_areas,
270 snd_pcm_uframes_t slave_offset,
271 snd_pcm_uframes_t *slave_sizep)
272 {
273 extplug_priv_t *ext = pcm->private_data;
274
275 if (size > *slave_sizep)
276 size = *slave_sizep;
277 size = ext->data->callback->transfer(ext->data, slave_areas, slave_offset,
278 areas, offset, size);
279 *slave_sizep = size;
280 return size;
281 }
282
283 /*
284 * read_areas skeleton - call transfer callback
285 */
286 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)287 snd_pcm_extplug_read_areas(snd_pcm_t *pcm,
288 const snd_pcm_channel_area_t *areas,
289 snd_pcm_uframes_t offset,
290 snd_pcm_uframes_t size,
291 const snd_pcm_channel_area_t *slave_areas,
292 snd_pcm_uframes_t slave_offset,
293 snd_pcm_uframes_t *slave_sizep)
294 {
295 extplug_priv_t *ext = pcm->private_data;
296
297 if (size > *slave_sizep)
298 size = *slave_sizep;
299 size = ext->data->callback->transfer(ext->data, areas, offset,
300 slave_areas, slave_offset, size);
301 *slave_sizep = size;
302 return size;
303 }
304
snd_pcm_extplug_close(snd_pcm_t * pcm)305 static int snd_pcm_extplug_close(snd_pcm_t *pcm)
306 {
307 extplug_priv_t *ext = pcm->private_data;
308
309 awalsa_debug("\n");
310 snd_pcm_close(ext->plug.gen.slave);
311 clear_ext_params(ext);
312 if (ext->data->callback->close)
313 ext->data->callback->close(ext->data);
314 free(ext);
315 return 0;
316 }
317
318 /*
319 * hw_refine callback
320 */
extplug_hw_refine(snd_pcm_hw_params_t * hw_params,struct snd_ext_parm * parm)321 static int extplug_hw_refine(snd_pcm_hw_params_t *hw_params,
322 struct snd_ext_parm *parm)
323 {
324 int i, err, change = 0;
325 for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
326 int type = hw_params_type[i];
327 if (is_mask_type(i))
328 err = snd_ext_parm_mask_refine(hw_param_interval(hw_params, type),
329 parm, i);
330 else
331 err = snd_ext_parm_range_refine(hw_param_interval(hw_params, type),
332 parm, i);
333 if (err < 0)
334 return err;
335 change |= err;
336 }
337 return change;
338 }
339
snd_pcm_extplug_hw_refine_cprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)340 static int snd_pcm_extplug_hw_refine_cprepare(snd_pcm_t *pcm,
341 snd_pcm_hw_params_t *params)
342 {
343 extplug_priv_t *ext = pcm->private_data;
344 int err;
345 snd_interval_t access_mask = { .mask = SND_PCM_ACCBIT_SHM };
346 awalsa_debug("\n");
347 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
348 &access_mask);
349 if (err < 0)
350 return err;
351 err = extplug_hw_refine(params, ext->params);
352 if (err < 0)
353 return err;
354 #if 0
355 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
356 #endif
357 return 0;
358 }
359
snd_pcm_extplug_hw_refine_sprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * sparams)360 static int snd_pcm_extplug_hw_refine_sprepare(snd_pcm_t *pcm,
361 snd_pcm_hw_params_t *sparams)
362 {
363 extplug_priv_t *ext = pcm->private_data;
364 snd_interval_t saccess_mask = { .mask = SND_PCM_ACCBIT_MMAP };
365 awalsa_debug("\n");
366 _snd_pcm_hw_params_any(sparams);
367 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
368 &saccess_mask);
369 extplug_hw_refine(sparams, ext->sparams);
370 return 0;
371 }
372
get_links(struct snd_ext_parm * params)373 static unsigned int get_links(struct snd_ext_parm *params)
374 {
375 int i;
376 unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
377 SND_PCM_HW_PARBIT_SAMPLE_BITS |
378 SND_PCM_HW_PARBIT_CHANNELS |
379 SND_PCM_HW_PARBIT_FRAME_BITS |
380 SND_PCM_HW_PARBIT_RATE |
381 SND_PCM_HW_PARBIT_PERIODS |
382 SND_PCM_HW_PARBIT_PERIOD_SIZE |
383 SND_PCM_HW_PARBIT_PERIOD_TIME |
384 SND_PCM_HW_PARBIT_BUFFER_SIZE |
385 SND_PCM_HW_PARBIT_BUFFER_TIME);
386
387 for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
388 if (params[i].active)
389 links &= ~excl_parbits[i];
390 }
391 return links;
392 }
393
snd_pcm_extplug_hw_refine_schange(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)394 static int snd_pcm_extplug_hw_refine_schange(snd_pcm_t *pcm,
395 snd_pcm_hw_params_t *params,
396 snd_pcm_hw_params_t *sparams)
397 {
398 extplug_priv_t *ext = pcm->private_data;
399 unsigned int links = get_links(ext->sparams);
400
401 awalsa_debug("\n");
402 return _snd_pcm_hw_params_refine(sparams, links, params);
403 }
404
snd_pcm_extplug_hw_refine_cchange(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)405 static int snd_pcm_extplug_hw_refine_cchange(snd_pcm_t *pcm,
406 snd_pcm_hw_params_t *params,
407 snd_pcm_hw_params_t *sparams)
408 {
409 extplug_priv_t *ext = pcm->private_data;
410 unsigned int links = get_links(ext->params);
411
412 awalsa_debug("\n");
413 return _snd_pcm_hw_params_refine(params, links, sparams);
414 }
415
snd_pcm_extplug_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)416 static int snd_pcm_extplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
417 {
418 #if 0
419 extplug_priv_t *ext = pcm->private_data;
420 snd_pcm_t *slave = ext->plug.gen.slave;
421 int ret;
422
423 awalsa_debug("\n");
424
425 // TODO: how to refine?
426 ret = snd_pcm_hw_refine(slave, params);
427 if (ret < 0)
428 return ret;
429
430 return 0;
431 #endif
432
433 awalsa_debug("\n");
434 int err = snd_pcm_hw_refine_slave(pcm, params,
435 snd_pcm_extplug_hw_refine_cprepare,
436 snd_pcm_extplug_hw_refine_cchange,
437 snd_pcm_extplug_hw_refine_sprepare,
438 snd_pcm_extplug_hw_refine_schange,
439 snd_pcm_generic_hw_refine);
440 return err;
441 }
442
snd_pcm_extplug_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)443 static int snd_pcm_extplug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
444 {
445 extplug_priv_t *ext = pcm->private_data;
446 snd_pcm_t *slave = ext->plug.gen.slave;
447 int err;
448
449 awalsa_debug("\n");
450
451 err = snd_pcm_hw_params_slave(pcm, params,
452 snd_pcm_extplug_hw_refine_cchange,
453 snd_pcm_extplug_hw_refine_sprepare,
454 snd_pcm_extplug_hw_refine_schange,
455 snd_pcm_generic_hw_params);
456 if (err < 0)
457 return err;
458
459 ext->data->slave_format = slave->format;
460 ext->data->slave_channels = slave->channels;
461 ext->data->rate = slave->rate;
462 snd_pcm_hw_params_get_format(params, &ext->data->format);
463 snd_pcm_hw_params_get_channels(params, &ext->data->channels);
464
465 if (ext->data->callback->hw_params) {
466 err = ext->data->callback->hw_params(ext->data, params);
467 if (err < 0)
468 return err;
469 }
470 return 0;
471 }
472
snd_pcm_extplug_hw_free(snd_pcm_t * pcm)473 static int snd_pcm_extplug_hw_free(snd_pcm_t *pcm)
474 {
475 extplug_priv_t *ext = pcm->private_data;
476
477 awalsa_debug("\n");
478 snd_pcm_hw_free(ext->plug.gen.slave);
479 if (ext->data->callback->hw_free)
480 return ext->data->callback->hw_free(ext->data);
481 return 0;
482 }
483
snd_pcm_extplug_dump(snd_pcm_t * pcm)484 static void snd_pcm_extplug_dump(snd_pcm_t *pcm)
485 {
486 extplug_priv_t *ext = pcm->private_data;
487 awalsa_debug("\n");
488
489 if (ext->data->callback->dump)
490 ext->data->callback->dump(ext->data);
491 else {
492 if (ext->data->name)
493 printf("%s\n", ext->data->name);
494 else
495 printf("External PCM Plugin\n");
496 if (pcm->setup) {
497 printf("Its setup is:\n");
498 snd_pcm_dump_setup(pcm);
499 }
500 }
501 printf("Slave: ");
502 snd_pcm_dump(ext->plug.gen.slave);
503 }
504
505 static const snd_pcm_ops_t snd_pcm_extplug_ops = {
506 .close = snd_pcm_extplug_close,
507 .hw_refine = snd_pcm_extplug_hw_refine,
508 .hw_params = snd_pcm_extplug_hw_params,
509 .hw_free = snd_pcm_extplug_hw_free,
510 .sw_params = snd_pcm_generic_sw_params,
511 .channel_info = snd_pcm_generic_channel_info,
512 .dump = snd_pcm_extplug_dump,
513 .mmap = snd_pcm_generic_mmap,
514 .munmap = snd_pcm_generic_munmap,
515 //.query_chmaps = snd_pcm_extplug_query_chmaps,
516 //.get_chmap = snd_pcm_extplug_get_chmap,
517 //.set_chmap = snd_pcm_extplug_set_chmap,
518 };
519
snd_pcm_extplug_create(snd_pcm_extplug_t * extplug,const char * name,const char * spcm_name,snd_pcm_stream_t stream,int mode)520 int snd_pcm_extplug_create(snd_pcm_extplug_t *extplug, const char *name,
521 const char *spcm_name, snd_pcm_stream_t stream, int mode)
522 {
523 int ret = 0;
524 extplug_priv_t *ext;
525 snd_pcm_t *spcm, *pcm;
526 const snd_pcm_config_t *sconf = NULL;
527
528 assert(extplug && extplug->callback);
529 assert(extplug->callback->transfer);
530
531 sconf = snd_pcm_config_get_config(spcm_name);
532 if (!sconf) {
533 awalsa_err("can't find pcm slave: %s\n", spcm_name);
534 ret = -EINVAL;
535 goto err_out;
536 }
537 ret = snd_pcm_open_config(&spcm, sconf, stream, mode);
538 if (ret < 0) {
539 awalsa_err("unable to open slave\n");
540 goto err_out;
541 }
542
543 ext = calloc(1, sizeof(*ext));
544 if (!ext) {
545 awalsa_err("no memory\n");
546 ret = -ENOMEM;
547 goto err_close_spcm;
548 }
549
550 ext->data = extplug;
551 extplug->stream = stream;
552
553 snd_pcm_plugin_init(&ext->plug);
554 ext->plug.read = snd_pcm_extplug_read_areas;
555 ext->plug.write = snd_pcm_extplug_write_areas;
556 ext->plug.undo_read = snd_pcm_plugin_undo_read_generic;
557 ext->plug.undo_write = snd_pcm_plugin_undo_write_generic;
558 ext->plug.gen.slave = spcm;
559 ext->plug.gen.close_slave = 1;
560 if (extplug->callback->init)
561 ext->plug.init = snd_pcm_extplug_init;
562
563 ret = snd_pcm_new(&pcm, SND_PCM_TYPE_EXTPLUG, name, stream, mode);
564 if (ret < 0) {
565 goto err_free_ext;
566 }
567
568 extplug->pcm = pcm;
569 pcm->ops = &snd_pcm_extplug_ops;
570 pcm->fast_ops = &snd_pcm_plugin_fast_ops;
571 pcm->private_data = ext;
572 snd_pcm_set_hw_ptr(pcm, &ext->plug.hw_ptr, -1, 0);
573 snd_pcm_set_appl_ptr(pcm, &ext->plug.appl_ptr, -1, 0);
574
575 return 0;
576
577 err_free_ext:
578 free(ext);
579 err_close_spcm:
580 snd_pcm_close(spcm);
581 err_out:
582 return ret;
583 }
584