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