blob: 015120aa1fa2d62d2a5215c1f2c77d6a2d8b3268 [file] [log] [blame]
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -07001/* mixer_plugin.c
2**
Akhil Karuturia9724402020-01-17 13:53:14 -08003** Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -07004**
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>
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -070043#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
55struct 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
64static 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
85static 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
103static 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
120static 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
133void mixer_plug_notifier_cb(struct mixer_plugin *plugin)
134{
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700135 plugin->event_cnt++;
Vidyakumar Athota5c4b56d2020-04-15 21:25:49 -0700136 eventfd_write(plugin->eventfd, 1);
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700137}
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 */
141static 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);
Vidyakumar Athota5c4b56d2020-04-15 21:25:49 -0700152 if (plugin->event_cnt <= 0) {
153 plugin->event_cnt = 0;
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700154 eventfd_read(plugin->eventfd, &evfd);
Vidyakumar Athota5c4b56d2020-04-15 21:25:49 -0700155 }
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700156 }
157
158 return result;
159}
160
161static 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
187static 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
199static 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
212static 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
225static 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
241static 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
258static 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
298static 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
323static 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;
Akhil Karuturia9724402020-01-17 13:53:14 -0800329 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);
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700334
335 printf("%s: card = %d\n", __func__, plug_data->card);
336
337 return 0;
338}
339
340static 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
356static 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
401static 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
408int 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;
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700413 void *dl_hdl;
414 char *name, *so_name;
Akhil Karuturia9724402020-01-17 13:53:14 -0800415 char *open_fn_name, token[80], *token_saveptr;
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700416 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 */
Akhil Karuturia9724402020-01-17 13:53:14 -0800428 goto err_free_plug_data;
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700429 }
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);
Akhil Karuturia9724402020-01-17 13:53:14 -0800436 goto err_put_dev_node;
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700437
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);
Akhil Karuturia9724402020-01-17 13:53:14 -0800444 goto err_put_dev_node;
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700445 }
446
447 sscanf(so_name, "lib%s", token);
Akhil Karuturia9724402020-01-17 13:53:14 -0800448 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 }
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700454
455 open_fn_name = calloc(1, strlen(name) + strlen("_open") + 1);
456 if (!open_fn_name) {
457 ret = -ENOMEM;
Akhil Karuturia9724402020-01-17 13:53:14 -0800458 goto err_dl_hdl;
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700459 }
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
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700466 plug_data->mixer_plugin_open_fn = dlsym(dl_hdl, open_fn_name);
Akhil Karuturia9724402020-01-17 13:53:14 -0800467 if (!plug_data->mixer_plugin_open_fn) {
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700468 fprintf(stderr, "%s: dlsym open fn failed: %s\n",
Akhil Karuturia9724402020-01-17 13:53:14 -0800469 __func__, dlerror());
470 goto err_open_fn_name;
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700471 }
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);
Akhil Karuturia9724402020-01-17 13:53:14 -0800476 goto err_open_fn_name;
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700477 }
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
Akhil Karuturia9724402020-01-17 13:53:14 -0800489 free(open_fn_name);
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700490 return 0;
491
Akhil Karuturia9724402020-01-17 13:53:14 -0800492err_open_fn_name:
493 free(open_fn_name);
494
495err_dl_hdl:
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700496 dlclose(dl_hdl);
497
Akhil Karuturia9724402020-01-17 13:53:14 -0800498err_put_dev_node:
499 snd_utils_put_dev_node(plug_data->mixer_node);
500
501err_free_plug_data:
Bhalchandra Gajare6b2537f2019-08-21 15:09:43 -0700502
503 free(plug_data);
504 return -1;
505}