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