1 /* mixer.c 2 ** 3 ** Copyright 2011, The Android Open Source Project 4 ** 5 ** Redistribution and use in source and binary forms, with or without 6 ** modification, are permitted provided that the following conditions are 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 copyright 10 ** notice, this list of conditions and the following disclaimer in the 11 ** documentation and/or other materials provided with the distribution. 12 ** * Neither the name of The Android Open Source Project nor the names of 13 ** its contributors may be used to endorse or promote products derived 14 ** from this software without specific prior written permission. 15 ** 16 ** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND 17 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 ** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE 20 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 ** DAMAGE. 27 */ 28 29 #include <stdbool.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <stdint.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <fcntl.h> 36 #include <errno.h> 37 #include <ctype.h> 38 #include <poll.h> 39 40 #include <sys/ioctl.h> 41 42 #include <linux/ioctl.h> 43 #define __force 44 #define __bitwise 45 #define __user 46 #include <sound/asound.h> 47 48 #ifndef SNDRV_CTL_ELEM_ID_NAME_MAXLEN 49 #define SNDRV_CTL_ELEM_ID_NAME_MAXLEN 44 50 #endif 51 52 #include <tinyalsa/asoundlib.h> 53 54 struct mixer_ctl { 55 struct mixer *mixer; 56 struct snd_ctl_elem_info *info; 57 char **ename; 58 bool info_retrieved; 59 }; 60 61 struct mixer { 62 int fd; 63 struct snd_ctl_card_info card_info; 64 struct snd_ctl_elem_info *elem_info; 65 struct mixer_ctl *ctl; 66 unsigned int count; 67 }; 68 69 void mixer_close(struct mixer *mixer) 70 { 71 unsigned int n,m; 72 73 if (!mixer) 74 return; 75 76 if (mixer->fd >= 0) 77 close(mixer->fd); 78 79 if (mixer->ctl) { 80 for (n = 0; n < mixer->count; n++) { 81 if (mixer->ctl[n].ename) { 82 unsigned int max = mixer->ctl[n].info->value.enumerated.items; 83 for (m = 0; m < max; m++) 84 free(mixer->ctl[n].ename[m]); 85 free(mixer->ctl[n].ename); 86 } 87 } 88 free(mixer->ctl); 89 } 90 91 if (mixer->elem_info) 92 free(mixer->elem_info); 93 94 free(mixer); 95 96 /* TODO: verify frees */ 97 } 98 99 struct mixer *mixer_open(unsigned int card) 100 { 101 struct snd_ctl_elem_list elist; 102 struct snd_ctl_elem_id *eid = NULL; 103 struct mixer *mixer = NULL; 104 unsigned int n; 105 int fd; 106 char fn[256]; 107 108 snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card); 109 fd = open(fn, O_RDWR); 110 if (fd < 0) 111 return 0; 112 113 memset(&elist, 0, sizeof(elist)); 114 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) 115 goto fail; 116 117 mixer = calloc(1, sizeof(*mixer)); 118 if (!mixer) 119 goto fail; 120 121 mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl)); 122 mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info)); 123 if (!mixer->ctl || !mixer->elem_info) 124 goto fail; 125 126 if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0) 127 goto fail; 128 129 eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id)); 130 if (!eid) 131 goto fail; 132 133 mixer->count = elist.count; 134 mixer->fd = fd; 135 elist.space = mixer->count; 136 elist.pids = eid; 137 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) 138 goto fail; 139 140 for (n = 0; n < mixer->count; n++) { 141 struct mixer_ctl *ctl = mixer->ctl + n; 142 143 ctl->mixer = mixer; 144 ctl->info = mixer->elem_info + n; 145 ctl->info->id.numid = eid[n].numid; 146 strncpy((char *)ctl->info->id.name, (char *)eid[n].name, 147 SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 148 ctl->info->id.name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0; 149 } 150 151 free(eid); 152 return mixer; 153 154 fail: 155 /* TODO: verify frees in failure case */ 156 if (eid) 157 free(eid); 158 if (mixer) 159 mixer_close(mixer); 160 else if (fd >= 0) 161 close(fd); 162 return 0; 163 } 164 165 static bool mixer_ctl_get_elem_info(struct mixer_ctl* ctl) 166 { 167 if (!ctl->info_retrieved) { 168 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info) < 0) 169 return false; 170 ctl->info_retrieved = true; 171 } 172 173 if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED || ctl->ename) 174 return true; 175 176 struct snd_ctl_elem_info tmp; 177 char** enames = calloc(ctl->info->value.enumerated.items, sizeof(char*)); 178 if (!enames) 179 return false; 180 181 for (unsigned int i = 0; i < ctl->info->value.enumerated.items; i++) { 182 memset(&tmp, 0, sizeof(tmp)); 183 tmp.id.numid = ctl->info->id.numid; 184 tmp.value.enumerated.item = i; 185 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0) 186 goto fail; 187 enames[i] = strdup(tmp.value.enumerated.name); 188 if (!enames[i]) 189 goto fail; 190 } 191 ctl->ename = enames; 192 return true; 193 194 fail: 195 free(enames); 196 return false; 197 } 198 199 const char *mixer_get_name(struct mixer *mixer) 200 { 201 return (const char *)mixer->card_info.name; 202 } 203 204 unsigned int mixer_get_num_ctls(struct mixer *mixer) 205 { 206 if (!mixer) 207 return 0; 208 209 return mixer->count; 210 } 211 212 struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id) 213 { 214 struct mixer_ctl *ctl; 215 216 if (!mixer || (id >= mixer->count)) 217 return NULL; 218 219 ctl = mixer->ctl + id; 220 if (!mixer_ctl_get_elem_info(ctl)) 221 return NULL; 222 223 return ctl; 224 } 225 226 struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name) 227 { 228 unsigned int n; 229 230 if (!mixer) 231 return NULL; 232 233 for (n = 0; n < mixer->count; n++) 234 if (!strcmp(name, (char*) mixer->elem_info[n].id.name)) 235 return mixer_get_ctl(mixer, n); 236 237 return NULL; 238 } 239 240 void mixer_ctl_update(struct mixer_ctl *ctl) 241 { 242 ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info); 243 } 244 245 const char *mixer_ctl_get_name(struct mixer_ctl *ctl) 246 { 247 if (!ctl) 248 return NULL; 249 250 return (const char *)ctl->info->id.name; 251 } 252 253 enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl) 254 { 255 if (!ctl) 256 return MIXER_CTL_TYPE_UNKNOWN; 257 258 switch (ctl->info->type) { 259 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL; 260 case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT; 261 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM; 262 case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE; 263 case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958; 264 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64; 265 default: return MIXER_CTL_TYPE_UNKNOWN; 266 }; 267 } 268 269 const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl) 270 { 271 if (!ctl) 272 return ""; 273 274 switch (ctl->info->type) { 275 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL"; 276 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT"; 277 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM"; 278 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE"; 279 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958"; 280 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64"; 281 default: return "Unknown"; 282 }; 283 } 284 285 unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl) 286 { 287 if (!ctl) 288 return 0; 289 290 return ctl->info->count; 291 } 292 293 static int percent_to_int(struct snd_ctl_elem_info *ei, int percent) 294 { 295 int range; 296 297 if (percent > 100) 298 percent = 100; 299 else if (percent < 0) 300 percent = 0; 301 302 range = (ei->value.integer.max - ei->value.integer.min); 303 304 return ei->value.integer.min + (range * percent) / 100; 305 } 306 307 static int int_to_percent(struct snd_ctl_elem_info *ei, int value) 308 { 309 int range = (ei->value.integer.max - ei->value.integer.min); 310 311 if (range == 0) 312 return 0; 313 314 return ((value - ei->value.integer.min) / range) * 100; 315 } 316 317 int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id) 318 { 319 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER)) 320 return -EINVAL; 321 322 return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id)); 323 } 324 325 int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent) 326 { 327 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER)) 328 return -EINVAL; 329 330 return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent)); 331 } 332 333 int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id) 334 { 335 struct snd_ctl_elem_value ev; 336 int ret; 337 338 if (!ctl || (id >= ctl->info->count)) 339 return -EINVAL; 340 341 memset(&ev, 0, sizeof(ev)); 342 ev.id.numid = ctl->info->id.numid; 343 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev); 344 if (ret < 0) 345 return ret; 346 347 switch (ctl->info->type) { 348 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 349 return !!ev.value.integer.value[id]; 350 351 case SNDRV_CTL_ELEM_TYPE_INTEGER: 352 return ev.value.integer.value[id]; 353 354 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 355 return ev.value.enumerated.item[id]; 356 357 case SNDRV_CTL_ELEM_TYPE_BYTES: 358 return ev.value.bytes.data[id]; 359 360 default: 361 return -EINVAL; 362 } 363 364 return 0; 365 } 366 367 int mixer_ctl_is_access_tlv_rw(struct mixer_ctl *ctl) 368 { 369 return (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE); 370 } 371 372 int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count) 373 { 374 struct snd_ctl_elem_value ev; 375 int ret = 0; 376 size_t size; 377 void *source; 378 size_t total_count; 379 380 if ((!ctl) || !count || !array) 381 return -EINVAL; 382 383 total_count = ctl->info->count; 384 385 if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) && 386 mixer_ctl_is_access_tlv_rw(ctl)) { 387 /* Additional two words is for the TLV header */ 388 total_count += TLV_HEADER_SIZE; 389 } 390 391 if (count > total_count) 392 return -EINVAL; 393 394 memset(&ev, 0, sizeof(ev)); 395 ev.id.numid = ctl->info->id.numid; 396 397 switch (ctl->info->type) { 398 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 399 case SNDRV_CTL_ELEM_TYPE_INTEGER: 400 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev); 401 if (ret < 0) 402 return ret; 403 size = sizeof(ev.value.integer.value[0]); 404 source = ev.value.integer.value; 405 break; 406 407 case SNDRV_CTL_ELEM_TYPE_BYTES: 408 /* check if this is new bytes TLV */ 409 if (mixer_ctl_is_access_tlv_rw(ctl)) { 410 struct snd_ctl_tlv *tlv; 411 int ret; 412 413 if (count > SIZE_MAX - sizeof(*tlv)) 414 return -EINVAL; 415 tlv = calloc(1, sizeof(*tlv) + count); 416 if (!tlv) 417 return -ENOMEM; 418 tlv->numid = ctl->info->id.numid; 419 tlv->length = count; 420 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, tlv); 421 422 source = tlv->tlv; 423 memcpy(array, source, count); 424 425 free(tlv); 426 427 return ret; 428 } else { 429 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev); 430 if (ret < 0) 431 return ret; 432 size = sizeof(ev.value.bytes.data[0]); 433 source = ev.value.bytes.data; 434 break; 435 } 436 437 case SNDRV_CTL_ELEM_TYPE_IEC958: 438 size = sizeof(ev.value.iec958); 439 source = &ev.value.iec958; 440 break; 441 442 default: 443 return -EINVAL; 444 } 445 446 memcpy(array, source, size * count); 447 448 return 0; 449 } 450 451 int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value) 452 { 453 struct snd_ctl_elem_value ev; 454 int ret; 455 456 if (!ctl || (id >= ctl->info->count)) 457 return -EINVAL; 458 459 memset(&ev, 0, sizeof(ev)); 460 ev.id.numid = ctl->info->id.numid; 461 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev); 462 if (ret < 0) 463 return ret; 464 465 switch (ctl->info->type) { 466 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 467 ev.value.integer.value[id] = !!value; 468 break; 469 470 case SNDRV_CTL_ELEM_TYPE_INTEGER: 471 ev.value.integer.value[id] = value; 472 break; 473 474 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 475 ev.value.enumerated.item[id] = value; 476 break; 477 478 case SNDRV_CTL_ELEM_TYPE_BYTES: 479 ev.value.bytes.data[id] = value; 480 break; 481 482 default: 483 return -EINVAL; 484 } 485 486 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev); 487 } 488 489 int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count) 490 { 491 struct snd_ctl_elem_value ev; 492 size_t size; 493 void *dest; 494 size_t total_count; 495 496 if ((!ctl) || !count || !array) 497 return -EINVAL; 498 499 total_count = ctl->info->count; 500 501 if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) && 502 mixer_ctl_is_access_tlv_rw(ctl)) { 503 /* Additional two words is for the TLV header */ 504 total_count += TLV_HEADER_SIZE; 505 } 506 507 if (count > total_count) 508 return -EINVAL; 509 510 memset(&ev, 0, sizeof(ev)); 511 ev.id.numid = ctl->info->id.numid; 512 513 switch (ctl->info->type) { 514 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 515 case SNDRV_CTL_ELEM_TYPE_INTEGER: 516 size = sizeof(ev.value.integer.value[0]); 517 dest = ev.value.integer.value; 518 break; 519 520 case SNDRV_CTL_ELEM_TYPE_BYTES: 521 /* check if this is new bytes TLV */ 522 if (mixer_ctl_is_access_tlv_rw(ctl)) { 523 struct snd_ctl_tlv *tlv; 524 int ret = 0; 525 if (count > SIZE_MAX - sizeof(*tlv)) 526 return -EINVAL; 527 tlv = calloc(1, sizeof(*tlv) + count); 528 if (!tlv) 529 return -ENOMEM; 530 tlv->numid = ctl->info->id.numid; 531 tlv->length = count; 532 memcpy(tlv->tlv, array, count); 533 534 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_WRITE, tlv); 535 free(tlv); 536 537 return ret; 538 } else { 539 size = sizeof(ev.value.bytes.data[0]); 540 dest = ev.value.bytes.data; 541 } 542 break; 543 544 case SNDRV_CTL_ELEM_TYPE_IEC958: 545 size = sizeof(ev.value.iec958); 546 dest = &ev.value.iec958; 547 break; 548 549 default: 550 return -EINVAL; 551 } 552 553 memcpy(dest, array, size * count); 554 555 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev); 556 } 557 558 int mixer_ctl_get_range_min(struct mixer_ctl *ctl) 559 { 560 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER)) 561 return -EINVAL; 562 563 return ctl->info->value.integer.min; 564 } 565 566 int mixer_ctl_get_range_max(struct mixer_ctl *ctl) 567 { 568 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER)) 569 return -EINVAL; 570 571 return ctl->info->value.integer.max; 572 } 573 574 unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl) 575 { 576 if (!ctl) 577 return 0; 578 579 return ctl->info->value.enumerated.items; 580 } 581 582 const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl, 583 unsigned int enum_id) 584 { 585 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) || 586 (enum_id >= ctl->info->value.enumerated.items)) 587 return NULL; 588 589 return (const char *)ctl->ename[enum_id]; 590 } 591 592 int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string) 593 { 594 unsigned int i, num_enums; 595 struct snd_ctl_elem_value ev; 596 int ret; 597 598 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED)) 599 return -EINVAL; 600 601 num_enums = ctl->info->value.enumerated.items; 602 for (i = 0; i < num_enums; i++) { 603 if (!strcmp(string, ctl->ename[i])) { 604 memset(&ev, 0, sizeof(ev)); 605 ev.value.enumerated.item[0] = i; 606 ev.id.numid = ctl->info->id.numid; 607 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev); 608 if (ret < 0) 609 return ret; 610 return 0; 611 } 612 } 613 614 return -EINVAL; 615 } 616 617 /** Subscribes for the mixer events. 618 * @param mixer A mixer handle. 619 * @param subscribe value indicating subscribe or unsubscribe for events 620 * @returns On success, zero. 621 * On failure, non-zero. 622 * @ingroup libtinyalsa-mixer 623 */ 624 int mixer_subscribe_events(struct mixer *mixer, int subscribe) 625 { 626 if (ioctl(mixer->fd, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0) { 627 return -1; 628 } 629 return 0; 630 } 631 632 /** Wait for mixer events. 633 * @param mixer A mixer handle. 634 * @param timeout timout value 635 * @returns On success, 1. 636 * On failure, -errno. 637 * On timeout, 0 638 * @ingroup libtinyalsa-mixer 639 */ 640 int mixer_wait_event(struct mixer *mixer, int timeout) 641 { 642 struct pollfd pfd; 643 644 pfd.fd = mixer->fd; 645 pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL; 646 647 for (;;) { 648 int err; 649 err = poll(&pfd, 1, timeout); 650 if (err < 0) 651 return -errno; 652 if (!err) 653 return 0; 654 if (pfd.revents & (POLLERR | POLLNVAL)) 655 return -EIO; 656 if (pfd.revents & (POLLIN | POLLOUT)) 657 return 1; 658 } 659 } 660 661 /** Consume a mixer event. 662 * If mixer_subscribe_events has been called, 663 * mixer_wait_event will identify when a control value has changed. 664 * This function will clear a single event from the mixer so that 665 * further events can be alerted. 666 * 667 * @param mixer A mixer handle. 668 * @returns 0 on success. -errno on failure. 669 * @ingroup libtinyalsa-mixer 670 */ 671 int mixer_consume_event(struct mixer *mixer) { 672 struct snd_ctl_event ev; 673 ssize_t count = read(mixer->fd, &ev, sizeof(ev)); 674 // Exporting the actual event would require exposing snd_ctl_event 675 // via the header file, and all associated structs. 676 // The events generally tell you exactly which value changed, 677 // but reading values you're interested isn't hard and simplifies 678 // the interface greatly. 679 return (count >= 0) ? 0 : -errno; 680 } 681