xref: /aosp_15_r20/external/tinyalsa/mixer_plugin.c (revision d0c94b832dfb3062bf15d9baaf64123fc670b06c)
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