• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     plugin->event_cnt++;
136     eventfd_write(plugin->eventfd, 1);
137 }
138 
139 /* In consume_event/read, do not call eventfd_read until all events are read from list.
140    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)141 static ssize_t mixer_plug_read_event(void *data, struct snd_ctl_event *ev, size_t size)
142 {
143     struct mixer_plug_data *plug_data = data;
144     struct mixer_plugin *plugin = plug_data->plugin;
145     eventfd_t evfd;
146     ssize_t result = 0;
147 
148     result = plugin->ops->read_event(plugin, (struct ctl_event *)ev, size);
149 
150     if (result > 0) {
151         plugin->event_cnt -=  result / sizeof(struct snd_ctl_event);
152         if (plugin->event_cnt <= 0) {
153             plugin->event_cnt = 0;
154             eventfd_read(plugin->eventfd, &evfd);
155         }
156     }
157 
158     return result;
159 }
160 
mixer_plug_subscribe_events(struct mixer_plug_data * plug_data,int * subscribe)161 static int mixer_plug_subscribe_events(struct mixer_plug_data *plug_data,
162                 int *subscribe)
163 {
164     struct mixer_plugin *plugin = plug_data->plugin;
165     eventfd_t evfd;
166 
167     if (*subscribe < 0 || *subscribe > 1) {
168         *subscribe = plugin->subscribed;
169         return -EINVAL;
170     }
171 
172     if (*subscribe && !plugin->subscribed) {
173         plugin->ops->subscribe_events(plugin, &mixer_plug_notifier_cb);
174     } else if (plugin->subscribed && !*subscribe) {
175         plugin->ops->subscribe_events(plugin, NULL);
176 
177         if (plugin->event_cnt)
178             eventfd_read(plugin->eventfd, &evfd);
179 
180         plugin->event_cnt = 0;
181     }
182 
183     plugin->subscribed = *subscribe;
184     return 0;
185 }
186 
mixer_plug_get_poll_fd(void * data,struct pollfd * pfd,int count)187 static int mixer_plug_get_poll_fd(void *data, struct pollfd *pfd, int count)
188 {
189     struct mixer_plug_data *plug_data = data;
190     struct mixer_plugin *plugin = plug_data->plugin;
191 
192     if (plugin->eventfd != -1) {
193         pfd[count].fd = plugin->eventfd;
194         return 0;
195     }
196     return -ENODEV;
197 }
198 
mixer_plug_tlv_write(struct mixer_plug_data * plug_data,struct snd_ctl_tlv * tlv)199 static int mixer_plug_tlv_write(struct mixer_plug_data *plug_data,
200                 struct snd_ctl_tlv *tlv)
201 {
202     struct mixer_plugin *plugin = plug_data->plugin;
203     struct snd_control *ctl;
204     struct snd_value_tlv_bytes *val_tlv;
205 
206     ctl = plugin->controls + tlv->numid;
207     val_tlv = ctl->value;
208 
209     return val_tlv->put(plugin, ctl, tlv);
210 }
211 
mixer_plug_tlv_read(struct mixer_plug_data * plug_data,struct snd_ctl_tlv * tlv)212 static int mixer_plug_tlv_read(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->get(plugin, ctl, tlv);
223 }
224 
mixer_plug_elem_write(struct mixer_plug_data * plug_data,struct snd_ctl_elem_value * ev)225 static int mixer_plug_elem_write(struct mixer_plug_data *plug_data,
226                 struct snd_ctl_elem_value *ev)
227 {
228     struct mixer_plugin *plugin = plug_data->plugin;
229     struct snd_control *ctl;
230     int ret;
231 
232     ret = mixer_plug_get_elem_id(plug_data, &ev->id, ev->id.numid);
233     if (ret < 0)
234         return ret;
235 
236     ctl = plugin->controls + ev->id.numid;
237 
238     return ctl->put(plugin, ctl, ev);
239 }
240 
mixer_plug_elem_read(struct mixer_plug_data * plug_data,struct snd_ctl_elem_value * ev)241 static int mixer_plug_elem_read(struct mixer_plug_data *plug_data,
242                 struct snd_ctl_elem_value *ev)
243 {
244     struct mixer_plugin *plugin = plug_data->plugin;
245     struct snd_control *ctl;
246     int ret;
247 
248     ret = mixer_plug_get_elem_id(plug_data, &ev->id, ev->id.numid);
249     if (ret < 0)
250         return ret;
251 
252     ctl = plugin->controls + ev->id.numid;
253 
254     return ctl->get(plugin, ctl, ev);
255 
256 }
257 
mixer_plug_get_elem_info(struct mixer_plug_data * plug_data,struct snd_ctl_elem_info * einfo)258 static int mixer_plug_get_elem_info(struct mixer_plug_data *plug_data,
259                 struct snd_ctl_elem_info *einfo)
260 {
261     struct mixer_plugin *plugin = plug_data->plugin;
262     struct snd_control *ctl;
263     int ret;
264 
265     ret = mixer_plug_get_elem_id(plug_data, &einfo->id,
266                     einfo->id.numid);
267     if (ret < 0)
268         return ret;
269 
270     ctl = plugin->controls + einfo->id.numid;
271     einfo->type = ctl->type;
272     einfo->access = ctl->access;
273 
274     switch (einfo->type) {
275     case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
276         ret = mixer_plug_info_enum(ctl, einfo);
277         if (ret < 0)
278             return ret;
279         break;
280     case SNDRV_CTL_ELEM_TYPE_BYTES:
281         ret = mixer_plug_info_bytes(ctl, einfo);
282         if (ret < 0)
283             return ret;
284         break;
285     case SNDRV_CTL_ELEM_TYPE_INTEGER:
286         ret = mixer_plug_info_integer(ctl, einfo);
287         if (ret < 0)
288             return ret;
289         break;
290     default:
291         printf("%s: unknown type %d\n", __func__, einfo->type);
292         return -EINVAL;
293     }
294 
295     return 0;
296 }
297 
mixer_plug_get_elem_list(struct mixer_plug_data * plug_data,struct snd_ctl_elem_list * elist)298 static int mixer_plug_get_elem_list(struct mixer_plug_data *plug_data,
299                 struct snd_ctl_elem_list *elist)
300 {
301     struct mixer_plugin *plugin = plug_data->plugin;
302     unsigned int avail;
303     struct snd_ctl_elem_id *id;
304     int ret;
305 
306     elist->count = plugin->num_controls;
307     elist->used = 0;
308     avail = elist->space;
309 
310     while (avail > 0) {
311         id = elist->pids + elist->used;
312         ret = mixer_plug_get_elem_id(plug_data, id, elist->used);
313         if (ret < 0)
314             return ret;
315 
316         avail--;
317         elist->used++;
318     }
319 
320     return 0;
321 }
322 
mixer_plug_get_card_info(struct mixer_plug_data * plug_data,struct snd_ctl_card_info * card_info)323 static int mixer_plug_get_card_info(struct mixer_plug_data *plug_data,
324                 struct snd_ctl_card_info *card_info)
325 {
326     /*TODO: Fill card_info here from snd-card-def */
327     memset(card_info, 0, sizeof(*card_info));
328     card_info->card = plug_data->card;
329     memcpy(card_info->id, "card_id", strlen("card_id") + 1);
330     memcpy(card_info->driver, "mymixer-so-name", strlen("mymixer-so-name") + 1);
331     memcpy(card_info->name, "card-name", strlen("card-name") + 1);
332     memcpy(card_info->longname, "card-name", strlen("card-name") + 1);
333     memcpy(card_info->mixername, "mixer-name", strlen("mixer-name") + 1);
334 
335     printf("%s: card = %d\n", __func__, plug_data->card);
336 
337     return 0;
338 }
339 
mixer_plug_close(void * data)340 static void mixer_plug_close(void *data)
341 {
342     struct mixer_plug_data *plug_data = data;
343     struct mixer_plugin *plugin = plug_data->plugin;
344     eventfd_t evfd;
345 
346     if (plugin->event_cnt)
347         eventfd_read(plugin->eventfd, &evfd);
348 
349     plugin->ops->close(&plugin);
350     dlclose(plug_data->dl_hdl);
351     snd_utils_put_dev_node(plug_data->mixer_node);
352     free(plug_data);
353     plug_data = NULL;
354 }
355 
mixer_plug_ioctl(void * data,unsigned int cmd,...)356 static int mixer_plug_ioctl(void *data, unsigned int cmd, ...)
357 {
358     struct mixer_plug_data *plug_data = data;
359     int ret;
360     va_list ap;
361     void *arg;
362 
363     va_start(ap, cmd);
364     arg = va_arg(ap, void *);
365     va_end(ap);
366 
367     switch (cmd) {
368     case SNDRV_CTL_IOCTL_CARD_INFO:
369         ret = mixer_plug_get_card_info(plug_data, arg);
370         break;
371     case SNDRV_CTL_IOCTL_ELEM_LIST:
372         ret = mixer_plug_get_elem_list(plug_data, arg);
373         break;
374     case SNDRV_CTL_IOCTL_ELEM_INFO:
375         ret = mixer_plug_get_elem_info(plug_data, arg);
376         break;
377     case SNDRV_CTL_IOCTL_ELEM_READ:
378         ret = mixer_plug_elem_read(plug_data, arg);
379         break;
380     case SNDRV_CTL_IOCTL_ELEM_WRITE:
381         ret = mixer_plug_elem_write(plug_data, arg);
382         break;
383     case SNDRV_CTL_IOCTL_TLV_READ:
384         ret = mixer_plug_tlv_read(plug_data, arg);
385         break;
386     case SNDRV_CTL_IOCTL_TLV_WRITE:
387         ret = mixer_plug_tlv_write(plug_data, arg);
388         break;
389     case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
390         ret = mixer_plug_subscribe_events(plug_data, arg);
391         break;
392     default:
393         /* TODO: plugin should support ioctl */
394         ret = -EFAULT;
395         break;
396     }
397 
398     return ret;
399 }
400 
401 static struct mixer_ops mixer_plug_ops = {
402     .close = mixer_plug_close,
403     .get_poll_fd = mixer_plug_get_poll_fd,
404     .read_event = mixer_plug_read_event,
405     .ioctl = mixer_plug_ioctl,
406 };
407 
mixer_plugin_open(unsigned int card,void ** data,struct mixer_ops ** ops)408 int mixer_plugin_open(unsigned int card, void **data,
409                       struct mixer_ops **ops)
410 {
411     struct mixer_plug_data *plug_data;
412     struct mixer_plugin *plugin = NULL;
413     void *dl_hdl;
414     char *name, *so_name;
415     char *open_fn_name, token[80], *token_saveptr;
416     int ret;
417 
418     plug_data = calloc(1, sizeof(*plug_data));
419     if (!plug_data)
420         return -ENOMEM;
421 
422     /* mixer id is fixed to 1 in snd-card-def xml */
423     plug_data->mixer_node = snd_utils_get_dev_node(card, 1, NODE_MIXER);
424     if (!plug_data->mixer_node) {
425         /* Do not print error here.
426          * It is valid for card to not have virtual mixer node
427          */
428         goto err_free_plug_data;
429     }
430 
431     ret = snd_utils_get_str(plug_data->mixer_node, "so-name",
432                                &so_name);
433     if(ret) {
434         fprintf(stderr, "%s: mixer so-name not found for card %u\n",
435                 __func__, card);
436         goto err_put_dev_node;
437 
438     }
439 
440     dl_hdl = dlopen(so_name, RTLD_NOW);
441     if (!dl_hdl) {
442         fprintf(stderr, "%s: unable to open %s\n",
443                 __func__, so_name);
444         goto err_put_dev_node;
445     }
446 
447     sscanf(so_name, "lib%s", token);
448     token_saveptr = token;
449     name = strtok_r(token, ".", &token_saveptr);
450     if (!name) {
451         fprintf(stderr, "%s: invalid library name\n", __func__);
452         goto err_dl_hdl;
453     }
454 
455     open_fn_name = calloc(1, strlen(name) + strlen("_open") + 1);
456     if (!open_fn_name) {
457         ret = -ENOMEM;
458         goto err_dl_hdl;
459     }
460 
461     strncpy(open_fn_name, name, strlen(name) + 1);
462     strcat(open_fn_name, "_open");
463 
464     printf("%s - %s\n", __func__, open_fn_name);
465 
466     plug_data->mixer_plugin_open_fn = dlsym(dl_hdl, open_fn_name);
467     if (!plug_data->mixer_plugin_open_fn) {
468         fprintf(stderr, "%s: dlsym open fn failed: %s\n",
469                 __func__, dlerror());
470         goto err_open_fn_name;
471     }
472     ret = plug_data->mixer_plugin_open_fn(&plugin, card);
473     if (ret) {
474         fprintf(stderr, "%s: failed to open plugin, err: %d\n",
475                 __func__, ret);
476         goto err_open_fn_name;
477     }
478 
479     plug_data->plugin = plugin;
480     plug_data->card = card;
481     plug_data->dl_hdl = dl_hdl;
482     plugin->eventfd = eventfd(0, 0);
483 
484     *data = plug_data;
485     *ops = &mixer_plug_ops;
486 
487     printf("%s: card = %d\n", __func__, plug_data->card);
488 
489     free(open_fn_name);
490     return 0;
491 
492 err_open_fn_name:
493     free(open_fn_name);
494 
495 err_dl_hdl:
496     dlclose(dl_hdl);
497 
498 err_put_dev_node:
499     snd_utils_put_dev_node(plug_data->mixer_node);
500 
501 err_free_plug_data:
502 
503     free(plug_data);
504     return -1;
505 }
506