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