blob: 47bf4a557939d74158d01ea3d2b0723146a3228b [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
Rohit kumar1ef2d452020-08-19 15:13:57 +053092static int convert_plugin_to_pcm_state(int plugin_state)
93{
94 switch (plugin_state) {
95 case PCM_PLUG_STATE_SETUP:
96 return PCM_STATE_SETUP;
97 case PCM_PLUG_STATE_RUNNING:
98 return PCM_STATE_RUNNING;
99 case PCM_PLUG_STATE_PREPARED:
100 return PCM_STATE_PREPARED;
101 case PCM_PLUG_STATE_OPEN:
102 return PCM_STATE_OPEN;
103 default:
104 break;
105 }
106
107 return PCM_STATE_OPEN;
108}
109
Bhalchandra Gajare986b8e32019-09-04 15:32:35 -0700110static void pcm_plug_close(void *data)
111{
112 struct pcm_plug_data *plug_data = data;
113 struct pcm_plugin *plugin = plug_data->plugin;
114
115 plug_data->ops->close(plugin);
116 dlclose(plug_data->dl_hdl);
117
118 free(plug_data);
119}
120
121static int pcm_plug_info(struct pcm_plug_data *plug_data,
122 struct snd_pcm_info *info)
123{
124 int stream = SNDRV_PCM_STREAM_PLAYBACK;
125 int ret = 0, val = -1;
126 char *name;
127
128 memset(info, 0, sizeof(*info));
129
130 if (plug_data->flags & PCM_IN) {
131 stream = SNDRV_PCM_STREAM_CAPTURE;
132 ret = snd_utils_get_int(plug_data->dev_node, "capture", &val);
133 if (ret || !val) {
134 fprintf(stderr, "%s: not a capture device\n", __func__);
135 return -EINVAL;
136 }
137 } else {
138 stream = SNDRV_PCM_STREAM_PLAYBACK;
139 ret = snd_utils_get_int(plug_data->dev_node, "playback", &val);
140 if (ret || !val) {
141 fprintf(stderr, "%s: not a playback device\n", __func__);
142 return -EINVAL;
143 }
144 }
145
146 info->stream = stream;
147 info->card = plug_data->card;
148 info->device = plug_data->device;
149
150 ret = snd_utils_get_str(plug_data->dev_node, "name", &name);
151 if (ret) {
152 fprintf(stderr, "%s: failed to get pcm device name\n", __func__);
153 return ret;
154 }
155
togeb3e379e2021-11-17 00:58:17 +0900156 strncpy((char *)info->id, name, sizeof(info->id) - 1);
togeafec7cf2021-11-23 22:53:50 +0900157 ((char *)info->id)[sizeof(info->id) - 1] = '\0';
togeb3e379e2021-11-17 00:58:17 +0900158 strncpy((char *)info->name, name, sizeof(info->name) - 1);
togeafec7cf2021-11-23 22:53:50 +0900159 ((char *)info->name)[sizeof(info->name) - 1] = '\0';
togeb3e379e2021-11-17 00:58:17 +0900160 strncpy((char *)info->subname, name, sizeof(info->subname) - 1);
togeafec7cf2021-11-23 22:53:50 +0900161 ((char *)info->subname)[sizeof(info->subname) - 1] = '\0';
Bhalchandra Gajare986b8e32019-09-04 15:32:35 -0700162
163 info->subdevices_count = 1;
164
165 return ret;
166}
167
168static void pcm_plug_set_mask(struct snd_pcm_hw_params *p, int n, uint64_t v)
169{
170 struct snd_mask *mask;
171
172 mask = PCM_PARAM_GET_MASK(p, n);
173
174 mask->bits[0] |= (v & 0xFFFFFFFF);
175 mask->bits[1] |= ((v >> 32) & 0xFFFFFFFF);
176 /*
177 * currently only supporting 64 bits, may need to update to support
178 * more than 64 bits
179 */
180}
181
182static void pcm_plug_set_interval(struct snd_pcm_hw_params *params,
183 int p, struct pcm_plugin_min_max *v, int is_integer)
184{
185 struct snd_interval *i;
186
187 i = &params->intervals[p - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
188
189 i->min = v->min;
190 i->max = v->max;
191
192 if (is_integer)
193 i->integer = 1;
194}
195
196static int pcm_plug_frames_to_bytes(unsigned int frames,
197 unsigned int frame_bits)
198{
199 return (frames * (frame_bits / 8));
200}
201
202static int pcm_plug_bytes_to_frames(unsigned int size,
203 unsigned int frame_bits)
204{
205 return (size * 8) / frame_bits;
206}
207
208static int pcm_plug_get_params(struct pcm_plugin *plugin,
209 struct snd_pcm_hw_params *params)
210{
211 struct pcm_plugin_min_max bw, ch, pb, periods;
212 struct pcm_plugin_min_max val;
213 struct pcm_plugin_min_max frame_bits, buffer_bytes;
214
215 /*
216 * populate the struct snd_pcm_hw_params structure
217 * using the hw_param constraints provided by plugin
218 * via the plugin->constraints
219 */
220
221 /* Set the mask params */
222 pcm_plug_set_mask(params, SNDRV_PCM_HW_PARAM_ACCESS,
223 plugin->constraints->access);
224 pcm_plug_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
225 plugin->constraints->format);
226 pcm_plug_set_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
227 SNDRV_PCM_SUBFORMAT_STD);
228
229 /* Set the standard interval params */
230 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
231 &plugin->constraints->bit_width, 1);
232 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS,
233 &plugin->constraints->channels, 1);
234 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_RATE,
235 &plugin->constraints->rate, 1);
236 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
237 &plugin->constraints->period_bytes, 0);
238 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_PERIODS,
239 &plugin->constraints->periods, 1);
240
241 /* set the calculated interval params */
242
243 bw.min = plugin->constraints->bit_width.min;
244 bw.max = plugin->constraints->bit_width.max;
245
246 ch.min = plugin->constraints->channels.min;
247 ch.max = plugin->constraints->channels.max;
248
249 pb.min = plugin->constraints->period_bytes.min;
250 pb.max = plugin->constraints->period_bytes.max;
251
252 periods.min = plugin->constraints->periods.min;
253 periods.max = plugin->constraints->periods.max;
254
255 /* Calculate and set frame bits */
256 frame_bits.min = bw.min * ch.min;
257 frame_bits.max = bw.max * ch.max;
258 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
259 &frame_bits, 1);
260
261
262 /* Calculate and set period_size in frames */
263 val.min = pcm_plug_bytes_to_frames(pb.min, frame_bits.min);
264 val.max = pcm_plug_bytes_to_frames(pb.max, frame_bits.min);
265 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
266 &val, 1);
267
268 /* Calculate and set buffer_bytes */
269 buffer_bytes.min = pb.min * periods.min;
270 buffer_bytes.max = pb.max * periods.max;
271 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
272 &buffer_bytes, 1);
273
274 /* Calculate and set buffer_size in frames */
275 val.min = pcm_plug_bytes_to_frames(buffer_bytes.min, frame_bits.min);
276 val.max = pcm_plug_bytes_to_frames(buffer_bytes.max, frame_bits.min);
277 pcm_plug_set_interval(params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
278 &val, 1);
279 return 0;
280}
281
282static int pcm_plug_masks_refine(struct snd_pcm_hw_params *p,
283 struct snd_pcm_hw_params *c)
284{
285 struct snd_mask *req_mask;
286 struct snd_mask *con_mask;
287 unsigned int idx, i, masks;
288
289 masks = SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK;
290
291 for (idx = 0; idx <= masks; idx++) {
292
293 if (!(p->rmask & (1 << (idx + SNDRV_PCM_HW_PARAM_FIRST_MASK))))
294 continue;
295
296 req_mask = PCM_PARAM_GET_MASK(p, idx);
297 con_mask = PCM_PARAM_GET_MASK(c, idx);
298
299 /*
300 * set the changed mask if requested mask value is not the same as
301 * constrained mask value
302 */
303 if (memcmp(req_mask, con_mask, PCM_MASK_SIZE * sizeof(uint32_t)))
304 p->cmask |= 1 << (idx + SNDRV_PCM_HW_PARAM_FIRST_MASK);
305
306 /* Actually change the requested mask to constrained mask */
307 for (i = 0; i < PCM_MASK_SIZE; i++)
308 req_mask->bits[i] &= con_mask->bits[i];
309 }
310
311 return 0;
312}
313
314static int pcm_plug_interval_refine(struct snd_pcm_hw_params *p,
315 struct snd_pcm_hw_params *c)
316{
317 struct snd_interval *ri;
318 struct snd_interval *ci;
319 unsigned int idx;
320 unsigned int intervals;
321 int changed = 0;
322
323 intervals = SNDRV_PCM_HW_PARAM_LAST_INTERVAL -
324 SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
325
326 for (idx = 0; idx <= intervals; idx++) {
327 ri = &p->intervals[idx];
328 ci = &c->intervals[idx];
329
330 if (!(p->rmask & (1 << (idx + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL)) ))
331 continue;
332
333 if (ri->min < ci->min) {
334 ri->min = ci->min;
335 ri->openmin = ci->openmin;
336 changed = 1;
337 } else if (ri->min == ci->min && !ri->openmin && ci->openmin) {
338 ri->openmin = 1;
339 changed = 1;
340 }
341
342 if (ri->max > ci->max) {
343 ri->max = ci->max;
344 ri->openmax = ci->openmax;
345 changed = 1;
346 } else if (ri->max == ci->max && !ri->openmax && ci->openmax) {
347 ri->openmax = 1;
348 changed = 1;
349 };
350
351 if (!ri->integer && ci->integer) {
352 ri->integer = 1;
353 changed = 1;
354 }
355
356 if (ri->integer) {
357 if (ri->openmin) {
358 ri->min++;
359 ri->openmin = 0;
360 }
361 if (ri->openmax) {
362 ri->max--;
363 ri->openmax = 0;
364 }
365 } else if (!ri->openmin && !ri->openmax && ri->min == ri->max) {
366 ri->integer = 1;
367 }
368
369 /* Set the changed mask */
370 if (changed)
371 p->cmask |= (1 << (idx + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL));
372 }
373
374 return 0;
375}
376
377
378static int pcm_plug_hw_params_refine(struct snd_pcm_hw_params *p,
379 struct snd_pcm_hw_params *c)
380{
381 int rc;
382
383 rc = pcm_plug_masks_refine(p, c);
384 if (rc) {
385 fprintf(stderr, "%s: masks refine failed %d\n", __func__, rc);
386 return rc;
387 }
388
389 rc = pcm_plug_interval_refine(p, c);
390 if (rc) {
391 fprintf(stderr, "%s: interval refine failed %d\n", __func__, rc);
392 return rc;
393 }
394
395 /* clear the requested params */
396 p->rmask = 0;
397
398 return rc;
399}
400
401static int __pcm_plug_hrefine(struct pcm_plug_data *plug_data,
402 struct snd_pcm_hw_params *params)
403{
404 struct pcm_plugin *plugin = plug_data->plugin;
405 struct snd_pcm_hw_params plug_params;
406 int rc;
407
408 memset(&plug_params, 0, sizeof(plug_params));
409 rc = pcm_plug_get_params(plugin, &plug_params);
410 if (rc) {
411 fprintf(stderr, "%s: pcm_plug_get_params failed %d\n",
412 __func__, rc);
413 return -EINVAL;
414 }
415
416 return pcm_plug_hw_params_refine(params, &plug_params);
417
418}
419
420static int pcm_plug_hrefine(struct pcm_plug_data *plug_data,
421 struct snd_pcm_hw_params *params)
422{
423 return __pcm_plug_hrefine(plug_data, params);
424}
425
426static int pcm_plug_interval_select(struct snd_pcm_hw_params *p,
427 unsigned int param, unsigned int select, unsigned int val)
428{
429 struct snd_interval *i;
430
431 if (param < SNDRV_PCM_HW_PARAM_FIRST_INTERVAL ||
432 param > SNDRV_PCM_HW_PARAM_LAST_INTERVAL)
433 return -EINVAL;
434
435 i = &p->intervals[param - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
436
437 if (!i->min)
438 return -EINVAL;
439
440 switch (select) {
441
442 case PCM_PLUG_HW_PARAM_SELECT_MIN:
443 i->max = i->min;
444 break;
445
446 case PCM_PLUG_HW_PARAM_SELECT_MAX:
447 i->min = i->max;
448 break;
449
450 case PCM_PLUG_HW_PARAM_SELECT_VAL:
451 i->min = i->max = val;
452 break;
453
454 default:
455 return -EINVAL;
456 }
457
458 return 0;
459}
460
461static void pcm_plug_hw_params_set(struct snd_pcm_hw_params *p)
462{
463 unsigned int i, select;
464 unsigned int bw, ch, period_sz, periods;
465 unsigned int val1, val2, offset;
466
467 offset = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
468
469 /* Select the min values first */
470 select = PCM_PLUG_HW_PARAM_SELECT_MIN;
471 for (i = 0; i < ARRAY_SIZE(param_list); i++)
472 pcm_plug_interval_select(p, param_list[i], select, 0);
473
474 /* Select calculated values */
475 select = PCM_PLUG_HW_PARAM_SELECT_VAL;
476 bw = (p->intervals[SNDRV_PCM_HW_PARAM_SAMPLE_BITS - offset]).min;
477 ch = (p->intervals[SNDRV_PCM_HW_PARAM_CHANNELS - offset]).min;
478 period_sz = (p->intervals[SNDRV_PCM_HW_PARAM_PERIOD_SIZE - offset]).min;
479 periods = (p->intervals[SNDRV_PCM_HW_PARAM_PERIODS - offset]).min;
480
481 val1 = bw * ch; // frame_bits;
482 pcm_plug_interval_select(p, SNDRV_PCM_HW_PARAM_FRAME_BITS, select, val1);
483
484 val2 = pcm_plug_frames_to_bytes(period_sz, val1); // period_bytes;
485 pcm_plug_interval_select(p, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, select,
486 val2);
487
488 val2 = period_sz * periods; //buffer_size;
489 pcm_plug_interval_select(p, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, select, val2);
490
491 val2 = pcm_plug_frames_to_bytes(period_sz * periods, val1); //buffer_bytes;
492 pcm_plug_interval_select(p, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, select, val2);
493}
494
495static int pcm_plug_hparams(struct pcm_plug_data *plug_data,
496 struct snd_pcm_hw_params *params)
497{
498 struct pcm_plugin *plugin = plug_data->plugin;
499 int rc;
500
501 if (plugin->state != PCM_PLUG_STATE_OPEN)
502 return -EBADFD;
503
504 params->rmask = ~0U;
505
506 rc = __pcm_plug_hrefine(plug_data, params);
507 if (rc) {
508 fprintf(stderr, "%s: __pcm_plug_hrefine failed %d\n",
509 __func__, rc);
510 return rc;
511 }
512
513 pcm_plug_hw_params_set(params);
514
515 rc = plug_data->ops->hw_params(plugin, params);
516 if (!rc)
517 plugin->state = PCM_PLUG_STATE_SETUP;
518
519 return rc;
520}
521
522static int pcm_plug_sparams(struct pcm_plug_data *plug_data,
523 struct snd_pcm_sw_params *params)
524{
525 struct pcm_plugin *plugin = plug_data->plugin;
526
527 if (plugin->state != PCM_PLUG_STATE_SETUP)
528 return -EBADFD;
529
530 return plug_data->ops->sw_params(plugin, params);
531}
532
533static int pcm_plug_sync_ptr(struct pcm_plug_data *plug_data,
534 struct snd_pcm_sync_ptr *sync_ptr)
535{
536 struct pcm_plugin *plugin = plug_data->plugin;
Rohit kumar1ef2d452020-08-19 15:13:57 +0530537 int ret = -EBADFD;
Bhalchandra Gajare986b8e32019-09-04 15:32:35 -0700538
Rohit kumar1ef2d452020-08-19 15:13:57 +0530539 if (plugin->state >= PCM_PLUG_STATE_SETUP) {
540 ret = plug_data->ops->sync_ptr(plugin, sync_ptr);
541 if (ret == 0)
542 sync_ptr->s.status.state = convert_plugin_to_pcm_state(plugin->state);
543 }
544
545 return ret;
Bhalchandra Gajare986b8e32019-09-04 15:32:35 -0700546}
547
548static int pcm_plug_writei_frames(struct pcm_plug_data *plug_data,
549 struct snd_xferi *x)
550{
551 struct pcm_plugin *plugin = plug_data->plugin;
552
553 if (plugin->state != PCM_PLUG_STATE_PREPARED &&
554 plugin->state != PCM_PLUG_STATE_RUNNING)
555 return -EBADFD;
556
557 return plug_data->ops->writei_frames(plugin, x);
558}
559
560static int pcm_plug_readi_frames(struct pcm_plug_data *plug_data,
561 struct snd_xferi *x)
562{
563 struct pcm_plugin *plugin = plug_data->plugin;
564
565 if (plugin->state != PCM_PLUG_STATE_RUNNING)
566 return -EBADFD;
567
568 return plug_data->ops->readi_frames(plugin, x);
569}
570
571static int pcm_plug_ttstamp(struct pcm_plug_data *plug_data,
572 int *tstamp)
573{
574 struct pcm_plugin *plugin = plug_data->plugin;
575
Rohit kumar9c7a3582020-08-19 15:21:07 +0530576 if (plugin->state < PCM_PLUG_STATE_SETUP)
Bhalchandra Gajare986b8e32019-09-04 15:32:35 -0700577 return -EBADFD;
578
579 return plug_data->ops->ttstamp(plugin, tstamp);
580}
581
582static int pcm_plug_prepare(struct pcm_plug_data *plug_data)
583{
584 struct pcm_plugin *plugin = plug_data->plugin;
585 int rc;
586
587 if (plugin->state != PCM_PLUG_STATE_SETUP)
588 return -EBADFD;
589
590 rc = plug_data->ops->prepare(plugin);
591 if (!rc)
592 plugin->state = PCM_PLUG_STATE_PREPARED;
593
594 return rc;
595}
596
597static int pcm_plug_start(struct pcm_plug_data *plug_data)
598{
599 struct pcm_plugin *plugin = plug_data->plugin;
600 int rc;
601
602 if (plugin->state != PCM_PLUG_STATE_PREPARED)
603 return -EBADFD;
604
605 rc = plug_data->ops->start(plugin);
606 if (!rc)
607 plugin->state = PCM_PLUG_STATE_RUNNING;
608
609 return rc;
610}
611
612static int pcm_plug_drop(struct pcm_plug_data *plug_data)
613{
614 struct pcm_plugin *plugin = plug_data->plugin;
615 int rc;
616
617 rc = plug_data->ops->drop(plugin);
618 if (!rc)
619 plugin->state = PCM_PLUG_STATE_SETUP;
620
621 return rc;
622}
623
624static int pcm_plug_ioctl(void *data, unsigned int cmd, ...)
625{
626 struct pcm_plug_data *plug_data = data;
627 struct pcm_plugin *plugin = plug_data->plugin;
628 int ret;
629 va_list ap;
630 void *arg;
631
632 va_start(ap, cmd);
633 arg = va_arg(ap, void *);
634 va_end(ap);
635
636 switch (cmd) {
637 case SNDRV_PCM_IOCTL_INFO:
638 ret = pcm_plug_info(plug_data, arg);
639 break;
640 case SNDRV_PCM_IOCTL_TTSTAMP:
641 ret = pcm_plug_ttstamp(plug_data, arg);
642 break;
643 case SNDRV_PCM_IOCTL_HW_REFINE:
644 ret = pcm_plug_hrefine(plug_data, arg);
645 break;
646 case SNDRV_PCM_IOCTL_HW_PARAMS:
647 ret = pcm_plug_hparams(plug_data, arg);
648 break;
649 case SNDRV_PCM_IOCTL_SW_PARAMS:
650 ret = pcm_plug_sparams(plug_data, arg);
651 break;
652 case SNDRV_PCM_IOCTL_SYNC_PTR:
653 ret = pcm_plug_sync_ptr(plug_data, arg);
654 break;
655 case SNDRV_PCM_IOCTL_PREPARE:
656 ret = pcm_plug_prepare(plug_data);
657 break;
658 case SNDRV_PCM_IOCTL_START:
659 ret = pcm_plug_start(plug_data);
660 break;
661 case SNDRV_PCM_IOCTL_DROP:
662 ret = pcm_plug_drop(plug_data);
663 break;
664 case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
665 ret = pcm_plug_writei_frames(plug_data, arg);
666 break;
667 case SNDRV_PCM_IOCTL_READI_FRAMES:
668 ret = pcm_plug_readi_frames(plug_data, arg);
669 break;
670 default:
671 ret = plug_data->ops->ioctl(plugin, cmd, arg);
672 break;
673 }
674
675 return ret;
676}
677
Rohit kumarf29b8df2020-08-19 15:19:33 +0530678static int pcm_plug_poll(void *data, struct pollfd *pfd, nfds_t nfds,
679 int timeout)
680{
681 struct pcm_plug_data *plug_data = data;
682 struct pcm_plugin *plugin = plug_data->plugin;
683
684 return plug_data->ops->poll(plugin, pfd, nfds, timeout);
685}
686
687static void *pcm_plug_mmap(void *data, void *addr, size_t length, int prot,
688 int flags, off_t offset)
689{
690 struct pcm_plug_data *plug_data = data;
691 struct pcm_plugin *plugin = plug_data->plugin;
692
693 if (plugin->state != PCM_PLUG_STATE_SETUP)
694 return NULL;
695
696 return plug_data->ops->mmap(plugin, addr, length, prot, flags, offset);
697}
698
699static int pcm_plug_munmap(void *data, void *addr, size_t length)
700{
701 struct pcm_plug_data *plug_data = data;
702 struct pcm_plugin *plugin = plug_data->plugin;
703
704 if (plugin->state != PCM_PLUG_STATE_SETUP)
705 return -EBADFD;
706
707 return plug_data->ops->munmap(plugin, addr, length);
708}
709
Bhalchandra Gajare986b8e32019-09-04 15:32:35 -0700710static int pcm_plug_open(unsigned int card, unsigned int device,
711 unsigned int flags, void **data, struct snd_node *pcm_node)
712{
713 struct pcm_plug_data *plug_data;
714 void *dl_hdl;
715 int rc = 0;
716 char *so_name;
717
718 plug_data = calloc(1, sizeof(*plug_data));
719 if (!plug_data) {
720 return -ENOMEM;
721 }
722
723 rc = snd_utils_get_str(pcm_node, "so-name", &so_name);
724 if (rc) {
725 fprintf(stderr, "%s: failed to get plugin lib name\n", __func__);
726 goto err_get_lib;
727 }
728
729 dl_hdl = dlopen(so_name, RTLD_NOW);
730 if (!dl_hdl) {
731 fprintf(stderr, "%s: unable to open %s\n", __func__, so_name);
732 goto err_dl_open;
733 } else {
734 fprintf(stderr, "%s: dlopen successful for %s\n", __func__, so_name);
735 }
736
737 dlerror();
738
739 plug_data->ops = dlsym(dl_hdl, "pcm_plugin_ops");
740 if (!plug_data->ops) {
741 fprintf(stderr, "%s: dlsym to open fn failed, err = '%s'\n",
742 __func__, dlerror());
743 goto err_dlsym;
744 }
745
746 rc = plug_data->ops->open(&plug_data->plugin, card, device, flags);
747 if (rc) {
748 fprintf(stderr, "%s: failed to open plugin\n", __func__);
749 goto err_open;
750 }
751
752 plug_data->dl_hdl = dl_hdl;
753 plug_data->card = card;
754 plug_data->device = device;
755 plug_data->dev_node = pcm_node;
756 plug_data->flags = flags;
757
758 *data = plug_data;
759
760 plug_data->plugin->state = PCM_PLUG_STATE_OPEN;
761
762 return 0;
763
764err_open:
765err_dlsym:
766 dlclose(dl_hdl);
767err_get_lib:
768err_dl_open:
769 free(plug_data);
770
771 return rc;
772}
773
774const struct pcm_ops plug_ops = {
775 .open = pcm_plug_open,
776 .close = pcm_plug_close,
777 .ioctl = pcm_plug_ioctl,
Rohit kumarf29b8df2020-08-19 15:19:33 +0530778 .mmap = pcm_plug_mmap,
779 .munmap = pcm_plug_munmap,
780 .poll = pcm_plug_poll,
Bhalchandra Gajare986b8e32019-09-04 15:32:35 -0700781};