blob: 695ad2cb47e11913e4581db3b8ca3414fbbd1825 [file] [log] [blame]
Bhalchandra Gajare986b8e32019-09-04 15:32:35 -07001/* pcm_plugin.c
2** Copyright (c) 2019, The Linux Foundation.
3**
4** Redistribution and use in source and binary forms, with or without
5** modification, are permitted provided that the following conditions are
6** met:
7** * Redistributions of source code must retain the above copyright
8** notice, this list of conditions and the following disclaimer.
9** * Redistributions in binary form must reproduce the above
10** copyright notice, this list of conditions and the following
11** disclaimer in the documentation and/or other materials provided
12** with the distribution.
13** * Neither the name of The Linux Foundation nor the names of its
14** contributors may be used to endorse or promote products derived
15** from this software without specific prior written permission.
16**
17** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28**/
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <stdint.h>
33#include <fcntl.h>
34#include <stdarg.h>
35#include <string.h>
36#include <errno.h>
37#include <unistd.h>
38#include <poll.h>
39#include <dlfcn.h>
40
41#include <sys/ioctl.h>
42#include <linux/ioctl.h>
43#include <sound/asound.h>
44#include <tinyalsa/asoundlib.h>
45#include <tinyalsa/plugin.h>
46
47#include "pcm_io.h"
48#include "snd_card_plugin.h"
49
50/* 2 words of uint32_t = 64 bits of mask */
51#define PCM_MASK_SIZE (2)
52#define ARRAY_SIZE(a) \
53 (sizeof(a) / sizeof(a[0]))
54
55#define PCM_PARAM_GET_MASK(p, n) \
56 &p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK];
57
58enum {
59 PCM_PLUG_HW_PARAM_SELECT_MIN,
60 PCM_PLUG_HW_PARAM_SELECT_MAX,
61 PCM_PLUG_HW_PARAM_SELECT_VAL,
62};
63
64enum {
65 PCM_PLUG_STATE_OPEN,
66 PCM_PLUG_STATE_SETUP,
67 PCM_PLUG_STATE_PREPARED,
68 PCM_PLUG_STATE_RUNNING,
69};
70
71struct pcm_plug_data {
72 unsigned int card;
73 unsigned int device;
74 unsigned int fd;
75 unsigned int flags;
76
77 void *dl_hdl;
78 /** pointer to plugin operation */
79 const struct pcm_plugin_ops *ops;
80 struct pcm_plugin *plugin;
81 void *dev_node;
82};
83
84static unsigned int param_list[] = {
85 SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
86 SNDRV_PCM_HW_PARAM_CHANNELS,
87 SNDRV_PCM_HW_PARAM_RATE,
88 SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
89 SNDRV_PCM_HW_PARAM_PERIODS,
90};
91
92static void pcm_plug_close(void *data)
93{
94 struct pcm_plug_data *plug_data = data;
95 struct pcm_plugin *plugin = plug_data->plugin;
96
97 plug_data->ops->close(plugin);
98 dlclose(plug_data->dl_hdl);
99
100 free(plug_data);
101}
102
103static int pcm_plug_info(struct pcm_plug_data *plug_data,
104 struct snd_pcm_info *info)
105{
106 int stream = SNDRV_PCM_STREAM_PLAYBACK;
107 int ret = 0, val = -1;
108 char *name;
109
110 memset(info, 0, sizeof(*info));
111
112 if (plug_data->flags & PCM_IN) {
113 stream = SNDRV_PCM_STREAM_CAPTURE;
114 ret = snd_utils_get_int(plug_data->dev_node, "capture", &val);
115 if (ret || !val) {
116 fprintf(stderr, "%s: not a capture device\n", __func__);
117 return -EINVAL;
118 }
119 } else {
120 stream = SNDRV_PCM_STREAM_PLAYBACK;
121 ret = snd_utils_get_int(plug_data->dev_node, "playback", &val);
122 if (ret || !val) {
123 fprintf(stderr, "%s: not a playback device\n", __func__);
124 return -EINVAL;
125 }
126 }
127
128 info->stream = stream;
129 info->card = plug_data->card;
130 info->device = plug_data->device;
131
132 ret = snd_utils_get_str(plug_data->dev_node, "name", &name);
133 if (ret) {
134 fprintf(stderr, "%s: failed to get pcm device name\n", __func__);
135 return ret;
136 }
137
138 strncpy((char *)info->id, name, sizeof(info->id));
139 strncpy((char *)info->name, name, sizeof(info->name));
140 strncpy((char *)info->subname, name, sizeof(info->subname));
141
142 info->subdevices_count = 1;
143
144 return ret;
145}
146
147static void pcm_plug_set_mask(struct snd_pcm_hw_params *p, int n, uint64_t v)
148{
149 struct snd_mask *mask;
150
151 mask = PCM_PARAM_GET_MASK(p, n);
152
153 mask->bits[0] |= (v & 0xFFFFFFFF);
154 mask->bits[1] |= ((v >> 32) & 0xFFFFFFFF);
155 /*
156 * currently only supporting 64 bits, may need to update to support
157 * more than 64 bits
158 */
159}
160
161static void pcm_plug_set_interval(struct snd_pcm_hw_params *params,
162 int p, struct pcm_plugin_min_max *v, int is_integer)
163{
164 struct snd_interval *i;
165
166 i = &params->intervals[p - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
167
168 i->min = v->min;
169 i->max = v->max;
170
171 if (is_integer)
172 i->integer = 1;
173}
174
175static int pcm_plug_frames_to_bytes(unsigned int frames,
176 unsigned int frame_bits)
177{
178 return (frames * (frame_bits / 8));
179}
180
181static int pcm_plug_bytes_to_frames(unsigned int size,
182 unsigned int frame_bits)
183{
184 return (size * 8) / frame_bits;
185}
186
187static int pcm_plug_get_params(struct pcm_plugin *plugin,
188 struct snd_pcm_hw_params *params)
189{
190 struct pcm_plugin_min_max bw, ch, pb, periods;
191 struct pcm_plugin_min_max val;
192 struct pcm_plugin_min_max frame_bits, buffer_bytes;
193
194 /*
195 * populate the struct snd_pcm_hw_params structure
196 * using the hw_param constraints provided by plugin
197 * via the plugin->constraints
198 */
199
200 /* Set the mask params */
201 pcm_plug_set_mask(params, SNDRV_PCM_HW_PARAM_ACCESS,
202 plugin->constraints->access);
203 pcm_plug_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
204 plugin->constraints->format);
205 pcm_plug_set_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
206 SNDRV_PCM_SUBFORMAT_STD);
207
208 /* Set the standard interval params */
209 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
210 &plugin->constraints->bit_width, 1);
211 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS,
212 &plugin->constraints->channels, 1);
213 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_RATE,
214 &plugin->constraints->rate, 1);
215 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
216 &plugin->constraints->period_bytes, 0);
217 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_PERIODS,
218 &plugin->constraints->periods, 1);
219
220 /* set the calculated interval params */
221
222 bw.min = plugin->constraints->bit_width.min;
223 bw.max = plugin->constraints->bit_width.max;
224
225 ch.min = plugin->constraints->channels.min;
226 ch.max = plugin->constraints->channels.max;
227
228 pb.min = plugin->constraints->period_bytes.min;
229 pb.max = plugin->constraints->period_bytes.max;
230
231 periods.min = plugin->constraints->periods.min;
232 periods.max = plugin->constraints->periods.max;
233
234 /* Calculate and set frame bits */
235 frame_bits.min = bw.min * ch.min;
236 frame_bits.max = bw.max * ch.max;
237 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
238 &frame_bits, 1);
239
240
241 /* Calculate and set period_size in frames */
242 val.min = pcm_plug_bytes_to_frames(pb.min, frame_bits.min);
243 val.max = pcm_plug_bytes_to_frames(pb.max, frame_bits.min);
244 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
245 &val, 1);
246
247 /* Calculate and set buffer_bytes */
248 buffer_bytes.min = pb.min * periods.min;
249 buffer_bytes.max = pb.max * periods.max;
250 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
251 &buffer_bytes, 1);
252
253 /* Calculate and set buffer_size in frames */
254 val.min = pcm_plug_bytes_to_frames(buffer_bytes.min, frame_bits.min);
255 val.max = pcm_plug_bytes_to_frames(buffer_bytes.max, frame_bits.min);
256 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
257 &val, 1);
258 return 0;
259}
260
261static int pcm_plug_masks_refine(struct snd_pcm_hw_params *p,
262 struct snd_pcm_hw_params *c)
263{
264 struct snd_mask *req_mask;
265 struct snd_mask *con_mask;
266 unsigned int idx, i, masks;
267
268 masks = SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK;
269
270 for (idx = 0; idx <= masks; idx++) {
271
272 if (!(p->rmask & (1 << (idx + SNDRV_PCM_HW_PARAM_FIRST_MASK))))
273 continue;
274
275 req_mask = PCM_PARAM_GET_MASK(p, idx);
276 con_mask = PCM_PARAM_GET_MASK(c, idx);
277
278 /*
279 * set the changed mask if requested mask value is not the same as
280 * constrained mask value
281 */
282 if (memcmp(req_mask, con_mask, PCM_MASK_SIZE * sizeof(uint32_t)))
283 p->cmask |= 1 << (idx + SNDRV_PCM_HW_PARAM_FIRST_MASK);
284
285 /* Actually change the requested mask to constrained mask */
286 for (i = 0; i < PCM_MASK_SIZE; i++)
287 req_mask->bits[i] &= con_mask->bits[i];
288 }
289
290 return 0;
291}
292
293static int pcm_plug_interval_refine(struct snd_pcm_hw_params *p,
294 struct snd_pcm_hw_params *c)
295{
296 struct snd_interval *ri;
297 struct snd_interval *ci;
298 unsigned int idx;
299 unsigned int intervals;
300 int changed = 0;
301
302 intervals = SNDRV_PCM_HW_PARAM_LAST_INTERVAL -
303 SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
304
305 for (idx = 0; idx <= intervals; idx++) {
306 ri = &p->intervals[idx];
307 ci = &c->intervals[idx];
308
309 if (!(p->rmask & (1 << (idx + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL)) ))
310 continue;
311
312 if (ri->min < ci->min) {
313 ri->min = ci->min;
314 ri->openmin = ci->openmin;
315 changed = 1;
316 } else if (ri->min == ci->min && !ri->openmin && ci->openmin) {
317 ri->openmin = 1;
318 changed = 1;
319 }
320
321 if (ri->max > ci->max) {
322 ri->max = ci->max;
323 ri->openmax = ci->openmax;
324 changed = 1;
325 } else if (ri->max == ci->max && !ri->openmax && ci->openmax) {
326 ri->openmax = 1;
327 changed = 1;
328 };
329
330 if (!ri->integer && ci->integer) {
331 ri->integer = 1;
332 changed = 1;
333 }
334
335 if (ri->integer) {
336 if (ri->openmin) {
337 ri->min++;
338 ri->openmin = 0;
339 }
340 if (ri->openmax) {
341 ri->max--;
342 ri->openmax = 0;
343 }
344 } else if (!ri->openmin && !ri->openmax && ri->min == ri->max) {
345 ri->integer = 1;
346 }
347
348 /* Set the changed mask */
349 if (changed)
350 p->cmask |= (1 << (idx + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL));
351 }
352
353 return 0;
354}
355
356
357static int pcm_plug_hw_params_refine(struct snd_pcm_hw_params *p,
358 struct snd_pcm_hw_params *c)
359{
360 int rc;
361
362 rc = pcm_plug_masks_refine(p, c);
363 if (rc) {
364 fprintf(stderr, "%s: masks refine failed %d\n", __func__, rc);
365 return rc;
366 }
367
368 rc = pcm_plug_interval_refine(p, c);
369 if (rc) {
370 fprintf(stderr, "%s: interval refine failed %d\n", __func__, rc);
371 return rc;
372 }
373
374 /* clear the requested params */
375 p->rmask = 0;
376
377 return rc;
378}
379
380static int __pcm_plug_hrefine(struct pcm_plug_data *plug_data,
381 struct snd_pcm_hw_params *params)
382{
383 struct pcm_plugin *plugin = plug_data->plugin;
384 struct snd_pcm_hw_params plug_params;
385 int rc;
386
387 memset(&plug_params, 0, sizeof(plug_params));
388 rc = pcm_plug_get_params(plugin, &plug_params);
389 if (rc) {
390 fprintf(stderr, "%s: pcm_plug_get_params failed %d\n",
391 __func__, rc);
392 return -EINVAL;
393 }
394
395 return pcm_plug_hw_params_refine(params, &plug_params);
396
397}
398
399static int pcm_plug_hrefine(struct pcm_plug_data *plug_data,
400 struct snd_pcm_hw_params *params)
401{
402 return __pcm_plug_hrefine(plug_data, params);
403}
404
405static int pcm_plug_interval_select(struct snd_pcm_hw_params *p,
406 unsigned int param, unsigned int select, unsigned int val)
407{
408 struct snd_interval *i;
409
410 if (param < SNDRV_PCM_HW_PARAM_FIRST_INTERVAL ||
411 param > SNDRV_PCM_HW_PARAM_LAST_INTERVAL)
412 return -EINVAL;
413
414 i = &p->intervals[param - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
415
416 if (!i->min)
417 return -EINVAL;
418
419 switch (select) {
420
421 case PCM_PLUG_HW_PARAM_SELECT_MIN:
422 i->max = i->min;
423 break;
424
425 case PCM_PLUG_HW_PARAM_SELECT_MAX:
426 i->min = i->max;
427 break;
428
429 case PCM_PLUG_HW_PARAM_SELECT_VAL:
430 i->min = i->max = val;
431 break;
432
433 default:
434 return -EINVAL;
435 }
436
437 return 0;
438}
439
440static void pcm_plug_hw_params_set(struct snd_pcm_hw_params *p)
441{
442 unsigned int i, select;
443 unsigned int bw, ch, period_sz, periods;
444 unsigned int val1, val2, offset;
445
446 offset = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
447
448 /* Select the min values first */
449 select = PCM_PLUG_HW_PARAM_SELECT_MIN;
450 for (i = 0; i < ARRAY_SIZE(param_list); i++)
451 pcm_plug_interval_select(p, param_list[i], select, 0);
452
453 /* Select calculated values */
454 select = PCM_PLUG_HW_PARAM_SELECT_VAL;
455 bw = (p->intervals[SNDRV_PCM_HW_PARAM_SAMPLE_BITS - offset]).min;
456 ch = (p->intervals[SNDRV_PCM_HW_PARAM_CHANNELS - offset]).min;
457 period_sz = (p->intervals[SNDRV_PCM_HW_PARAM_PERIOD_SIZE - offset]).min;
458 periods = (p->intervals[SNDRV_PCM_HW_PARAM_PERIODS - offset]).min;
459
460 val1 = bw * ch; // frame_bits;
461 pcm_plug_interval_select(p, SNDRV_PCM_HW_PARAM_FRAME_BITS, select, val1);
462
463 val2 = pcm_plug_frames_to_bytes(period_sz, val1); // period_bytes;
464 pcm_plug_interval_select(p, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, select,
465 val2);
466
467 val2 = period_sz * periods; //buffer_size;
468 pcm_plug_interval_select(p, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, select, val2);
469
470 val2 = pcm_plug_frames_to_bytes(period_sz * periods, val1); //buffer_bytes;
471 pcm_plug_interval_select(p, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, select, val2);
472}
473
474static int pcm_plug_hparams(struct pcm_plug_data *plug_data,
475 struct snd_pcm_hw_params *params)
476{
477 struct pcm_plugin *plugin = plug_data->plugin;
478 int rc;
479
480 if (plugin->state != PCM_PLUG_STATE_OPEN)
481 return -EBADFD;
482
483 params->rmask = ~0U;
484
485 rc = __pcm_plug_hrefine(plug_data, params);
486 if (rc) {
487 fprintf(stderr, "%s: __pcm_plug_hrefine failed %d\n",
488 __func__, rc);
489 return rc;
490 }
491
492 pcm_plug_hw_params_set(params);
493
494 rc = plug_data->ops->hw_params(plugin, params);
495 if (!rc)
496 plugin->state = PCM_PLUG_STATE_SETUP;
497
498 return rc;
499}
500
501static int pcm_plug_sparams(struct pcm_plug_data *plug_data,
502 struct snd_pcm_sw_params *params)
503{
504 struct pcm_plugin *plugin = plug_data->plugin;
505
506 if (plugin->state != PCM_PLUG_STATE_SETUP)
507 return -EBADFD;
508
509 return plug_data->ops->sw_params(plugin, params);
510}
511
512static int pcm_plug_sync_ptr(struct pcm_plug_data *plug_data,
513 struct snd_pcm_sync_ptr *sync_ptr)
514{
515 struct pcm_plugin *plugin = plug_data->plugin;
516
517 return plug_data->ops->sync_ptr(plugin, sync_ptr);
518}
519
520static int pcm_plug_writei_frames(struct pcm_plug_data *plug_data,
521 struct snd_xferi *x)
522{
523 struct pcm_plugin *plugin = plug_data->plugin;
524
525 if (plugin->state != PCM_PLUG_STATE_PREPARED &&
526 plugin->state != PCM_PLUG_STATE_RUNNING)
527 return -EBADFD;
528
529 return plug_data->ops->writei_frames(plugin, x);
530}
531
532static int pcm_plug_readi_frames(struct pcm_plug_data *plug_data,
533 struct snd_xferi *x)
534{
535 struct pcm_plugin *plugin = plug_data->plugin;
536
537 if (plugin->state != PCM_PLUG_STATE_RUNNING)
538 return -EBADFD;
539
540 return plug_data->ops->readi_frames(plugin, x);
541}
542
543static int pcm_plug_ttstamp(struct pcm_plug_data *plug_data,
544 int *tstamp)
545{
546 struct pcm_plugin *plugin = plug_data->plugin;
547
548 if (plugin->state != PCM_PLUG_STATE_RUNNING)
549 return -EBADFD;
550
551 return plug_data->ops->ttstamp(plugin, tstamp);
552}
553
554static int pcm_plug_prepare(struct pcm_plug_data *plug_data)
555{
556 struct pcm_plugin *plugin = plug_data->plugin;
557 int rc;
558
559 if (plugin->state != PCM_PLUG_STATE_SETUP)
560 return -EBADFD;
561
562 rc = plug_data->ops->prepare(plugin);
563 if (!rc)
564 plugin->state = PCM_PLUG_STATE_PREPARED;
565
566 return rc;
567}
568
569static int pcm_plug_start(struct pcm_plug_data *plug_data)
570{
571 struct pcm_plugin *plugin = plug_data->plugin;
572 int rc;
573
574 if (plugin->state != PCM_PLUG_STATE_PREPARED)
575 return -EBADFD;
576
577 rc = plug_data->ops->start(plugin);
578 if (!rc)
579 plugin->state = PCM_PLUG_STATE_RUNNING;
580
581 return rc;
582}
583
584static int pcm_plug_drop(struct pcm_plug_data *plug_data)
585{
586 struct pcm_plugin *plugin = plug_data->plugin;
587 int rc;
588
589 rc = plug_data->ops->drop(plugin);
590 if (!rc)
591 plugin->state = PCM_PLUG_STATE_SETUP;
592
593 return rc;
594}
595
596static int pcm_plug_ioctl(void *data, unsigned int cmd, ...)
597{
598 struct pcm_plug_data *plug_data = data;
599 struct pcm_plugin *plugin = plug_data->plugin;
600 int ret;
601 va_list ap;
602 void *arg;
603
604 va_start(ap, cmd);
605 arg = va_arg(ap, void *);
606 va_end(ap);
607
608 switch (cmd) {
609 case SNDRV_PCM_IOCTL_INFO:
610 ret = pcm_plug_info(plug_data, arg);
611 break;
612 case SNDRV_PCM_IOCTL_TTSTAMP:
613 ret = pcm_plug_ttstamp(plug_data, arg);
614 break;
615 case SNDRV_PCM_IOCTL_HW_REFINE:
616 ret = pcm_plug_hrefine(plug_data, arg);
617 break;
618 case SNDRV_PCM_IOCTL_HW_PARAMS:
619 ret = pcm_plug_hparams(plug_data, arg);
620 break;
621 case SNDRV_PCM_IOCTL_SW_PARAMS:
622 ret = pcm_plug_sparams(plug_data, arg);
623 break;
624 case SNDRV_PCM_IOCTL_SYNC_PTR:
625 ret = pcm_plug_sync_ptr(plug_data, arg);
626 break;
627 case SNDRV_PCM_IOCTL_PREPARE:
628 ret = pcm_plug_prepare(plug_data);
629 break;
630 case SNDRV_PCM_IOCTL_START:
631 ret = pcm_plug_start(plug_data);
632 break;
633 case SNDRV_PCM_IOCTL_DROP:
634 ret = pcm_plug_drop(plug_data);
635 break;
636 case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
637 ret = pcm_plug_writei_frames(plug_data, arg);
638 break;
639 case SNDRV_PCM_IOCTL_READI_FRAMES:
640 ret = pcm_plug_readi_frames(plug_data, arg);
641 break;
642 default:
643 ret = plug_data->ops->ioctl(plugin, cmd, arg);
644 break;
645 }
646
647 return ret;
648}
649
650static int pcm_plug_open(unsigned int card, unsigned int device,
651 unsigned int flags, void **data, struct snd_node *pcm_node)
652{
653 struct pcm_plug_data *plug_data;
654 void *dl_hdl;
655 int rc = 0;
656 char *so_name;
657
658 plug_data = calloc(1, sizeof(*plug_data));
659 if (!plug_data) {
660 return -ENOMEM;
661 }
662
663 rc = snd_utils_get_str(pcm_node, "so-name", &so_name);
664 if (rc) {
665 fprintf(stderr, "%s: failed to get plugin lib name\n", __func__);
666 goto err_get_lib;
667 }
668
669 dl_hdl = dlopen(so_name, RTLD_NOW);
670 if (!dl_hdl) {
671 fprintf(stderr, "%s: unable to open %s\n", __func__, so_name);
672 goto err_dl_open;
673 } else {
674 fprintf(stderr, "%s: dlopen successful for %s\n", __func__, so_name);
675 }
676
677 dlerror();
678
679 plug_data->ops = dlsym(dl_hdl, "pcm_plugin_ops");
680 if (!plug_data->ops) {
681 fprintf(stderr, "%s: dlsym to open fn failed, err = '%s'\n",
682 __func__, dlerror());
683 goto err_dlsym;
684 }
685
686 rc = plug_data->ops->open(&plug_data->plugin, card, device, flags);
687 if (rc) {
688 fprintf(stderr, "%s: failed to open plugin\n", __func__);
689 goto err_open;
690 }
691
692 plug_data->dl_hdl = dl_hdl;
693 plug_data->card = card;
694 plug_data->device = device;
695 plug_data->dev_node = pcm_node;
696 plug_data->flags = flags;
697
698 *data = plug_data;
699
700 plug_data->plugin->state = PCM_PLUG_STATE_OPEN;
701
702 return 0;
703
704err_open:
705err_dlsym:
706 dlclose(dl_hdl);
707err_get_lib:
708err_dl_open:
709 free(plug_data);
710
711 return rc;
712}
713
714const struct pcm_ops plug_ops = {
715 .open = pcm_plug_open,
716 .close = pcm_plug_close,
717 .ioctl = pcm_plug_ioctl,
718};