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 = ¶ms->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