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