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