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