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