1 /* mixer_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 <stdarg.h>
35 #include <stdbool.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <ctype.h>
41 #include <poll.h>
42 #include <dlfcn.h>
43 #include <sys/eventfd.h>
44 #include <sys/ioctl.h>
45
46 #include <linux/ioctl.h>
47 #include <sound/asound.h>
48
49 #include <tinyalsa/asoundlib.h>
50 #include <tinyalsa/mixer_plugin.h>
51 #include "snd_utils.h"
52
53 #include "mixer_io.h"
54
55 struct mixer_plug_data {
56 int card;
57 void *mixer_node;
58
59 struct mixer_plugin *plugin;
60 void *dl_hdl;
61 MIXER_PLUGIN_OPEN_FN_PTR();
62 };
63
mixer_plug_get_elem_id(struct mixer_plug_data * plug_data,struct snd_ctl_elem_id * id,unsigned int offset)64 static int mixer_plug_get_elem_id(struct mixer_plug_data *plug_data,
65 struct snd_ctl_elem_id *id, unsigned int offset)
66 {
67 struct mixer_plugin *plugin = plug_data->plugin;
68 struct snd_control *ctl;
69
70 if (offset >= plugin->num_controls) {
71 printf("%s: invalid offset %u\n", __func__, offset);
72 return -EINVAL;
73 }
74
75 ctl = plugin->controls + offset;
76 id->numid = offset;
77 id->iface = ctl->iface;
78
79 strncpy((char *)id->name, (char *)ctl->name,
80 sizeof(id->name));
81
82 return 0;
83 }
84
mixer_plug_info_enum(struct snd_control * ctl,struct snd_ctl_elem_info * einfo)85 static int mixer_plug_info_enum(struct snd_control *ctl,
86 struct snd_ctl_elem_info *einfo)
87 {
88 struct snd_value_enum *val = ctl->value;
89
90 einfo->count = 1;
91 einfo->value.enumerated.items = val->items;
92
93 if (einfo->value.enumerated.item > val->items)
94 return -EINVAL;
95
96 strncpy(einfo->value.enumerated.name,
97 val->texts[einfo->value.enumerated.item],
98 sizeof(einfo->value.enumerated.name));
99
100 return 0;
101 }
102
mixer_plug_info_bytes(struct snd_control * ctl,struct snd_ctl_elem_info * einfo)103 static int mixer_plug_info_bytes(struct snd_control *ctl,
104 struct snd_ctl_elem_info *einfo)
105 {
106 struct snd_value_bytes *val;
107 struct snd_value_tlv_bytes *val_tlv;
108
109 if (ctl->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
110 val_tlv = ctl->value;
111 einfo->count = val_tlv->size;
112 } else {
113 val = ctl->value;
114 einfo->count = val->size;
115 }
116
117 return 0;
118 }
119
mixer_plug_info_integer(struct snd_control * ctl,struct snd_ctl_elem_info * einfo)120 static int mixer_plug_info_integer(struct snd_control *ctl,
121 struct snd_ctl_elem_info *einfo)
122 {
123 struct snd_value_int *val = ctl->value;
124
125 einfo->count = val->count;
126 einfo->value.integer.min = val->min;
127 einfo->value.integer.max = val->max;
128 einfo->value.integer.step = val->step;
129
130 return 0;
131 }
132
mixer_plug_notifier_cb(struct mixer_plugin * plugin)133 void mixer_plug_notifier_cb(struct mixer_plugin *plugin)
134 {
135 pthread_mutex_lock(&plugin->mutex);
136 plugin->event_cnt++;
137 pthread_mutex_unlock(&plugin->mutex);
138 eventfd_write(plugin->eventfd, 1);
139 }
140
141 /* In consume_event/read, do not call eventfd_read until all events are read from list.
142 This will make poll getting unblocked until all events are read */
mixer_plug_read_event(void * data,struct snd_ctl_event * ev,size_t size)143 static ssize_t mixer_plug_read_event(void *data, struct snd_ctl_event *ev, size_t size)
144 {
145 struct mixer_plug_data *plug_data = data;
146 struct mixer_plugin *plugin = plug_data->plugin;
147 eventfd_t evfd;
148 ssize_t result = 0;
149 unsigned int i, read_cnt;
150
151 result = plugin->ops->read_event(plugin, (struct ctl_event *)ev, size);
152
153 if (result > 0) {
154 read_cnt = result / sizeof(struct snd_ctl_event);
155 pthread_mutex_lock(&plugin->mutex);
156 plugin->event_cnt -= read_cnt;
157 if (plugin->event_cnt < 0) {
158 plugin->event_cnt = 0;
159 }
160 pthread_mutex_unlock(&plugin->mutex);
161
162 for (i = 0; i < read_cnt; ++i) {
163 eventfd_read(plugin->eventfd, &evfd);
164 }
165 }
166
167 return result;
168 }
169
mixer_plug_subscribe_events(struct mixer_plug_data * plug_data,int * subscribe)170 static int mixer_plug_subscribe_events(struct mixer_plug_data *plug_data,
171 int *subscribe)
172 {
173 struct mixer_plugin *plugin = plug_data->plugin;
174 eventfd_t evfd;
175 unsigned int count;
176
177 if (*subscribe < 0 || *subscribe > 1) {
178 *subscribe = plugin->subscribed;
179 return -EINVAL;
180 }
181
182 if (*subscribe && !plugin->subscribed) {
183 plugin->ops->subscribe_events(plugin, &mixer_plug_notifier_cb);
184 } else if (plugin->subscribed && !*subscribe) {
185 plugin->ops->subscribe_events(plugin, NULL);
186
187 pthread_mutex_lock(&plugin->mutex);
188 count = plugin->event_cnt;
189 plugin->event_cnt = 0;
190 pthread_mutex_unlock(&plugin->mutex);
191 for (int i = 0; i < count; ++i) {
192 eventfd_read(plugin->eventfd, &evfd);
193 }
194 }
195
196 plugin->subscribed = *subscribe;
197 return 0;
198 }
199
mixer_plug_get_poll_fd(void * data,struct pollfd * pfd,int count)200 static int mixer_plug_get_poll_fd(void *data, struct pollfd *pfd, int count)
201 {
202 struct mixer_plug_data *plug_data = data;
203 struct mixer_plugin *plugin = plug_data->plugin;
204
205 if (plugin->eventfd != -1) {
206 pfd[count].fd = plugin->eventfd;
207 return 0;
208 }
209 return -ENODEV;
210 }
211
mixer_plug_tlv_write(struct mixer_plug_data * plug_data,struct snd_ctl_tlv * tlv)212 static int mixer_plug_tlv_write(struct mixer_plug_data *plug_data,
213 struct snd_ctl_tlv *tlv)
214 {
215 struct mixer_plugin *plugin = plug_data->plugin;
216 struct snd_control *ctl;
217 struct snd_value_tlv_bytes *val_tlv;
218
219 ctl = plugin->controls + tlv->numid;
220 val_tlv = ctl->value;
221
222 return val_tlv->put(plugin, ctl, tlv);
223 }
224
mixer_plug_tlv_read(struct mixer_plug_data * plug_data,struct snd_ctl_tlv * tlv)225 static int mixer_plug_tlv_read(struct mixer_plug_data *plug_data,
226 struct snd_ctl_tlv *tlv)
227 {
228 struct mixer_plugin *plugin = plug_data->plugin;
229 struct snd_control *ctl;
230 struct snd_value_tlv_bytes *val_tlv;
231
232 ctl = plugin->controls + tlv->numid;
233 val_tlv = ctl->value;
234
235 return val_tlv->get(plugin, ctl, tlv);
236 }
237
mixer_plug_elem_write(struct mixer_plug_data * plug_data,struct snd_ctl_elem_value * ev)238 static int mixer_plug_elem_write(struct mixer_plug_data *plug_data,
239 struct snd_ctl_elem_value *ev)
240 {
241 struct mixer_plugin *plugin = plug_data->plugin;
242 struct snd_control *ctl;
243 int ret;
244
245 ret = mixer_plug_get_elem_id(plug_data, &ev->id, ev->id.numid);
246 if (ret < 0)
247 return ret;
248
249 ctl = plugin->controls + ev->id.numid;
250
251 return ctl->put(plugin, ctl, ev);
252 }
253
mixer_plug_elem_read(struct mixer_plug_data * plug_data,struct snd_ctl_elem_value * ev)254 static int mixer_plug_elem_read(struct mixer_plug_data *plug_data,
255 struct snd_ctl_elem_value *ev)
256 {
257 struct mixer_plugin *plugin = plug_data->plugin;
258 struct snd_control *ctl;
259 int ret;
260
261 ret = mixer_plug_get_elem_id(plug_data, &ev->id, ev->id.numid);
262 if (ret < 0)
263 return ret;
264
265 ctl = plugin->controls + ev->id.numid;
266
267 return ctl->get(plugin, ctl, ev);
268
269 }
270
mixer_plug_get_elem_info(struct mixer_plug_data * plug_data,struct snd_ctl_elem_info * einfo)271 static int mixer_plug_get_elem_info(struct mixer_plug_data *plug_data,
272 struct snd_ctl_elem_info *einfo)
273 {
274 struct mixer_plugin *plugin = plug_data->plugin;
275 struct snd_control *ctl;
276 int ret;
277
278 ret = mixer_plug_get_elem_id(plug_data, &einfo->id,
279 einfo->id.numid);
280 if (ret < 0)
281 return ret;
282
283 ctl = plugin->controls + einfo->id.numid;
284 einfo->type = ctl->type;
285 einfo->access = ctl->access;
286
287 switch (einfo->type) {
288 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
289 ret = mixer_plug_info_enum(ctl, einfo);
290 if (ret < 0)
291 return ret;
292 break;
293 case SNDRV_CTL_ELEM_TYPE_BYTES:
294 ret = mixer_plug_info_bytes(ctl, einfo);
295 if (ret < 0)
296 return ret;
297 break;
298 case SNDRV_CTL_ELEM_TYPE_INTEGER:
299 ret = mixer_plug_info_integer(ctl, einfo);
300 if (ret < 0)
301 return ret;
302 break;
303 default:
304 printf("%s: unknown type %d\n", __func__, einfo->type);
305 return -EINVAL;
306 }
307
308 return 0;
309 }
310
mixer_plug_get_elem_list(struct mixer_plug_data * plug_data,struct snd_ctl_elem_list * elist)311 static int mixer_plug_get_elem_list(struct mixer_plug_data *plug_data,
312 struct snd_ctl_elem_list *elist)
313 {
314 struct mixer_plugin *plugin = plug_data->plugin;
315 unsigned int avail;
316 struct snd_ctl_elem_id *id;
317 int ret;
318
319 elist->count = plugin->num_controls;
320 elist->used = 0;
321 avail = elist->space;
322
323 while (avail > 0) {
324 id = elist->pids + elist->used;
325 ret = mixer_plug_get_elem_id(plug_data, id, elist->used);
326 if (ret < 0)
327 return ret;
328
329 avail--;
330 elist->used++;
331 }
332
333 return 0;
334 }
335
mixer_plug_get_card_info(struct mixer_plug_data * plug_data,struct snd_ctl_card_info * card_info)336 static int mixer_plug_get_card_info(struct mixer_plug_data *plug_data,
337 struct snd_ctl_card_info *card_info)
338 {
339 /*TODO: Fill card_info here from snd-card-def */
340 memset(card_info, 0, sizeof(*card_info));
341 card_info->card = plug_data->card;
342 memcpy(card_info->id, "card_id", strlen("card_id") + 1);
343 memcpy(card_info->driver, "mymixer-so-name", strlen("mymixer-so-name") + 1);
344 memcpy(card_info->name, "card-name", strlen("card-name") + 1);
345 memcpy(card_info->longname, "card-name", strlen("card-name") + 1);
346 memcpy(card_info->mixername, "mixer-name", strlen("mixer-name") + 1);
347
348 printf("%s: card = %d\n", __func__, plug_data->card);
349
350 return 0;
351 }
352
mixer_plug_close(void * data)353 static void mixer_plug_close(void *data)
354 {
355 struct mixer_plug_data *plug_data = data;
356 struct mixer_plugin *plugin = plug_data->plugin;
357 eventfd_t evfd;
358 unsigned int count;
359
360 pthread_mutex_lock(&plugin->mutex);
361 count = plugin->event_cnt;
362 pthread_mutex_unlock(&plugin->mutex);
363 pthread_mutex_destroy(&plugin->mutex);
364
365 for (int i = 0; i < count; ++i) {
366 eventfd_read(plugin->eventfd, &evfd);
367 }
368
369 plugin->ops->close(&plugin);
370 dlclose(plug_data->dl_hdl);
371 snd_utils_put_dev_node(plug_data->mixer_node);
372 free(plug_data);
373 plug_data = NULL;
374 }
375
mixer_plug_ioctl(void * data,unsigned int cmd,...)376 static int mixer_plug_ioctl(void *data, unsigned int cmd, ...)
377 {
378 struct mixer_plug_data *plug_data = data;
379 int ret;
380 va_list ap;
381 void *arg;
382
383 va_start(ap, cmd);
384 arg = va_arg(ap, void *);
385 va_end(ap);
386
387 switch (cmd) {
388 case SNDRV_CTL_IOCTL_CARD_INFO:
389 ret = mixer_plug_get_card_info(plug_data, arg);
390 break;
391 case SNDRV_CTL_IOCTL_ELEM_LIST:
392 ret = mixer_plug_get_elem_list(plug_data, arg);
393 break;
394 case SNDRV_CTL_IOCTL_ELEM_INFO:
395 ret = mixer_plug_get_elem_info(plug_data, arg);
396 break;
397 case SNDRV_CTL_IOCTL_ELEM_READ:
398 ret = mixer_plug_elem_read(plug_data, arg);
399 break;
400 case SNDRV_CTL_IOCTL_ELEM_WRITE:
401 ret = mixer_plug_elem_write(plug_data, arg);
402 break;
403 case SNDRV_CTL_IOCTL_TLV_READ:
404 ret = mixer_plug_tlv_read(plug_data, arg);
405 break;
406 case SNDRV_CTL_IOCTL_TLV_WRITE:
407 ret = mixer_plug_tlv_write(plug_data, arg);
408 break;
409 case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
410 ret = mixer_plug_subscribe_events(plug_data, arg);
411 break;
412 default:
413 /* TODO: plugin should support ioctl */
414 ret = -EFAULT;
415 break;
416 }
417
418 return ret;
419 }
420
421 static struct mixer_ops mixer_plug_ops = {
422 .close = mixer_plug_close,
423 .get_poll_fd = mixer_plug_get_poll_fd,
424 .read_event = mixer_plug_read_event,
425 .ioctl = mixer_plug_ioctl,
426 };
427
mixer_plugin_open(unsigned int card,void ** data,struct mixer_ops ** ops)428 int mixer_plugin_open(unsigned int card, void **data,
429 struct mixer_ops **ops)
430 {
431 struct mixer_plug_data *plug_data;
432 struct mixer_plugin *plugin = NULL;
433 void *dl_hdl;
434 char *name, *so_name;
435 char *open_fn_name, token[80], *token_saveptr;
436 int ret;
437
438 plug_data = calloc(1, sizeof(*plug_data));
439 if (!plug_data)
440 return -ENOMEM;
441
442 /* mixer id is fixed to 1 in snd-card-def xml */
443 plug_data->mixer_node = snd_utils_get_dev_node(card, 1, NODE_MIXER);
444 if (!plug_data->mixer_node) {
445 /* Do not print error here.
446 * It is valid for card to not have virtual mixer node
447 */
448 goto err_free_plug_data;
449 }
450
451 ret = snd_utils_get_str(plug_data->mixer_node, "so-name",
452 &so_name);
453 if(ret) {
454 fprintf(stderr, "%s: mixer so-name not found for card %u\n",
455 __func__, card);
456 goto err_put_dev_node;
457
458 }
459
460 dl_hdl = dlopen(so_name, RTLD_NOW);
461 if (!dl_hdl) {
462 fprintf(stderr, "%s: unable to open %s\n",
463 __func__, so_name);
464 goto err_put_dev_node;
465 }
466
467 sscanf(so_name, "lib%s", token);
468 token_saveptr = token;
469 name = strtok_r(token, ".", &token_saveptr);
470 if (!name) {
471 fprintf(stderr, "%s: invalid library name\n", __func__);
472 goto err_dl_hdl;
473 }
474
475 open_fn_name = calloc(1, strlen(name) + strlen("_open") + 1);
476 if (!open_fn_name) {
477 ret = -ENOMEM;
478 goto err_dl_hdl;
479 }
480
481 strncpy(open_fn_name, name, strlen(name) + 1);
482 strcat(open_fn_name, "_open");
483
484 printf("%s - %s\n", __func__, open_fn_name);
485
486 plug_data->mixer_plugin_open_fn = dlsym(dl_hdl, open_fn_name);
487 if (!plug_data->mixer_plugin_open_fn) {
488 fprintf(stderr, "%s: dlsym open fn failed: %s\n",
489 __func__, dlerror());
490 goto err_open_fn_name;
491 }
492 ret = plug_data->mixer_plugin_open_fn(&plugin, card);
493 if (ret) {
494 fprintf(stderr, "%s: failed to open plugin, err: %d\n",
495 __func__, ret);
496 goto err_open_fn_name;
497 }
498
499 plug_data->plugin = plugin;
500 plug_data->card = card;
501 plug_data->dl_hdl = dl_hdl;
502 plugin->eventfd = eventfd(0, EFD_SEMAPHORE);
503 pthread_mutex_init(&plugin->mutex, NULL);
504
505 *data = plug_data;
506 *ops = &mixer_plug_ops;
507
508 printf("%s: card = %d\n", __func__, plug_data->card);
509
510 free(open_fn_name);
511 return 0;
512
513 err_open_fn_name:
514 free(open_fn_name);
515
516 err_dl_hdl:
517 dlclose(dl_hdl);
518
519 err_put_dev_node:
520 snd_utils_put_dev_node(plug_data->mixer_node);
521
522 err_free_plug_data:
523
524 free(plug_data);
525 return -1;
526 }
527