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