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