1/* 2** Copyright 2010, The Android Open-Source Project 3** Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18#include <stdio.h> 19#include <stdlib.h> 20#include <string.h> 21#include <unistd.h> 22#include <stdint.h> 23#include <fcntl.h> 24#include <errno.h> 25#include <ctype.h> 26#include <math.h> 27 28#include <linux/ioctl.h> 29#define __force 30#define __bitwise 31#define __user 32#include <sound/asound.h> 33#include <sound/tlv.h> 34 35#include "alsa_audio.h" 36 37#define LOG_TAG "alsa_mixer" 38#define LOG_NDEBUG 1 39 40#ifdef ANDROID 41/* definitions for Android logging */ 42#include <utils/Log.h> 43#else /* ANDROID */ 44#include <math.h> 45#define ALOGI(...) fprintf(stdout, __VA_ARGS__) 46#define ALOGE(...) fprintf(stderr, __VA_ARGS__) 47#define ALOGV(...) fprintf(stderr, __VA_ARGS__) 48#endif /* ANDROID */ 49 50#define check_range(val, min, max) \ 51 (((val < min) ? (min) : (val > max) ? (max) : (val))) 52 53/* .5 for rounding before casting to non-decmal value */ 54/* Should not be used if you need decmal values */ 55/* or are expecting negitive indexes */ 56#define percent_to_index(val, min, max) \ 57 ((val) * ((max) - (min)) * 0.01 + (min) + .5) 58 59#define DEFAULT_TLV_SIZE 4096 60#define SPDIF_CHANNEL_STATUS_SIZE 24 61 62enum ctl_type { 63 CTL_GLOBAL_VOLUME, 64 CTL_PLAYBACK_VOLUME, 65 CTL_CAPTURE_VOLUME, 66}; 67 68static const struct suf { 69 const char *suffix; 70 snd_ctl_elem_iface_t type; 71} suffixes[] = { 72 {" Playback Volume", CTL_PLAYBACK_VOLUME}, 73 {" Capture Volume", CTL_CAPTURE_VOLUME}, 74 {" Volume", CTL_GLOBAL_VOLUME}, 75 {NULL, 0} 76}; 77 78static int is_volume(const char *name, enum ctl_type *type) 79{ 80 const struct suf *p; 81 size_t nlen = strlen(name); 82 p = suffixes; 83 while (p->suffix) { 84 size_t slen = strnlen(p->suffix, 44); 85 size_t l; 86 if (nlen > slen) { 87 l = nlen - slen; 88 if (strncmp(name + l, p->suffix, slen) == 0 && 89 (l < 1 || name[l-1] != '-')) { /* 3D Control - Switch */ 90 *type = p->type; 91 return l; 92 } 93 } 94 p++; 95 } 96 return 0; 97} 98 99static const char *elem_iface_name(snd_ctl_elem_iface_t n) 100{ 101 switch (n) { 102 case SNDRV_CTL_ELEM_IFACE_CARD: return "CARD"; 103 case SNDRV_CTL_ELEM_IFACE_HWDEP: return "HWDEP"; 104 case SNDRV_CTL_ELEM_IFACE_MIXER: return "MIXER"; 105 case SNDRV_CTL_ELEM_IFACE_PCM: return "PCM"; 106 case SNDRV_CTL_ELEM_IFACE_RAWMIDI: return "MIDI"; 107 case SNDRV_CTL_ELEM_IFACE_TIMER: return "TIMER"; 108 case SNDRV_CTL_ELEM_IFACE_SEQUENCER: return "SEQ"; 109 default: return "???"; 110 } 111} 112 113static const char *elem_type_name(snd_ctl_elem_type_t n) 114{ 115 switch (n) { 116 case SNDRV_CTL_ELEM_TYPE_NONE: return "NONE"; 117 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL"; 118 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT32"; 119 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM"; 120 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTES"; 121 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958"; 122 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64"; 123 default: return "???"; 124 } 125} 126 127void mixer_close(struct mixer *mixer) 128{ 129 unsigned n,m; 130 131 if (mixer->fd >= 0) 132 close(mixer->fd); 133 134 if (mixer->ctl) { 135 for (n = 0; n < mixer->count; n++) { 136 if (mixer->ctl[n].ename) { 137 unsigned max = mixer->ctl[n].info->value.enumerated.items; 138 for (m = 0; m < max; m++) 139 free(mixer->ctl[n].ename[m]); 140 free(mixer->ctl[n].ename); 141 } 142 } 143 free(mixer->ctl); 144 } 145 146 if (mixer->info) 147 free(mixer->info); 148 149 free(mixer); 150} 151 152struct mixer *mixer_open(const char *device) 153{ 154 struct snd_ctl_elem_list elist; 155 struct snd_ctl_elem_info tmp; 156 struct snd_ctl_elem_id *eid = NULL; 157 struct mixer *mixer = NULL; 158 unsigned n, m; 159 int fd; 160 161 fd = open(device, O_RDWR); 162 if (fd < 0) { 163 ALOGE("Control open failed\n"); 164 return 0; 165 } 166 167 memset(&elist, 0, sizeof(elist)); 168 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) { 169 ALOGE("SNDRV_CTL_IOCTL_ELEM_LIST failed\n"); 170 goto fail; 171 } 172 173 mixer = calloc(1, sizeof(*mixer)); 174 if (!mixer) 175 goto fail; 176 177 mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl)); 178 mixer->info = calloc(elist.count, sizeof(struct snd_ctl_elem_info)); 179 if (!mixer->ctl || !mixer->info) 180 goto fail; 181 182 eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id)); 183 if (!eid) 184 goto fail; 185 186 mixer->count = elist.count; 187 mixer->fd = fd; 188 elist.space = mixer->count; 189 elist.pids = eid; 190 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) 191 goto fail; 192 193 for (n = 0; n < mixer->count; n++) { 194 struct snd_ctl_elem_info *ei = mixer->info + n; 195 ei->id.numid = eid[n].numid; 196 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0) 197 goto fail; 198 mixer->ctl[n].info = ei; 199 mixer->ctl[n].mixer = mixer; 200 if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { 201 char **enames = calloc(ei->value.enumerated.items, sizeof(char*)); 202 if (!enames) 203 goto fail; 204 mixer->ctl[n].ename = enames; 205 for (m = 0; m < ei->value.enumerated.items; m++) { 206 memset(&tmp, 0, sizeof(tmp)); 207 tmp.id.numid = ei->id.numid; 208 tmp.value.enumerated.item = m; 209 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0) 210 goto fail; 211 enames[m] = strdup(tmp.value.enumerated.name); 212 if (!enames[m]) 213 goto fail; 214 } 215 } 216 } 217 218 free(eid); 219 return mixer; 220 221fail: 222 if (eid) 223 free(eid); 224 if (mixer) 225 mixer_close(mixer); 226 else if (fd >= 0) 227 close(fd); 228 return 0; 229} 230 231void mixer_dump(struct mixer *mixer) 232{ 233 unsigned n, m; 234 235 ALOGV(" id iface dev sub idx num perms type isvolume name\n"); 236 for (n = 0; n < mixer->count; n++) { 237 enum ctl_type type; 238 struct snd_ctl_elem_info *ei = mixer->info + n; 239 240 ALOGV("%4d %5s %3d %3d %3d %3d %c%c%c%c%c%c%c%c%c %-6s %8d %s", 241 ei->id.numid, elem_iface_name(ei->id.iface), 242 ei->id.device, ei->id.subdevice, ei->id.index, 243 ei->count, 244 (ei->access & SNDRV_CTL_ELEM_ACCESS_READ) ? 'r' : ' ', 245 (ei->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ? 'w' : ' ', 246 (ei->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE) ? 'V' : ' ', 247 (ei->access & SNDRV_CTL_ELEM_ACCESS_TIMESTAMP) ? 'T' : ' ', 248 (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) ? 'R' : ' ', 249 (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) ? 'W' : ' ', 250 (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) ? 'C' : ' ', 251 (ei->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) ? 'I' : ' ', 252 (ei->access & SNDRV_CTL_ELEM_ACCESS_LOCK) ? 'L' : ' ', 253 elem_type_name(ei->type), 254 (is_volume(ei->id.name, &type)) ? 1 : 0, 255 ei->id.name); 256 switch (ei->type) { 257 case SNDRV_CTL_ELEM_TYPE_INTEGER: 258 ALOGV(ei->value.integer.step ? 259 " { %ld-%ld, %ld }\n" : " { %ld-%ld }", 260 ei->value.integer.min, 261 ei->value.integer.max, 262 ei->value.integer.step); 263 break; 264 case SNDRV_CTL_ELEM_TYPE_INTEGER64: 265 ALOGV(ei->value.integer64.step ? 266 " { %lld-%lld, %lld }\n" : " { %lld-%lld }", 267 ei->value.integer64.min, 268 ei->value.integer64.max, 269 ei->value.integer64.step); 270 break; 271 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: { 272 unsigned m; 273 ALOGV(" { %s=0", mixer->ctl[n].ename[0]); 274 for (m = 1; m < ei->value.enumerated.items; m++) 275 ALOGV(", %s=%d", mixer->ctl[n].ename[m],m); 276 ALOGV(" }"); 277 break; 278 } 279 } 280 ALOGV("\n"); 281 } 282} 283 284struct mixer_ctl *mixer_get_control(struct mixer *mixer, 285 const char *name, unsigned index) 286{ 287 unsigned n; 288 for (n = 0; n < mixer->count; n++) { 289 if (mixer->info[n].id.index == index) { 290 if (!strncmp(name, (char*) mixer->info[n].id.name, 291 sizeof(mixer->info[n].id.name))) { 292 return mixer->ctl + n; 293 } 294 } 295 } 296 return 0; 297} 298 299struct mixer_ctl *mixer_get_nth_control(struct mixer *mixer, unsigned n) 300{ 301 if (n < mixer->count) 302 return mixer->ctl + n; 303 return 0; 304} 305 306static void print_dB(long dB) 307{ 308 ALOGV("%li.%02lidB", dB / 100, (dB < 0 ? -dB : dB) % 100); 309} 310 311int mixer_ctl_read_tlv(struct mixer_ctl *ctl, 312 unsigned int *tlv, 313 long *min, long *max, unsigned int *tlv_type) 314{ 315 unsigned int tlv_size = DEFAULT_TLV_SIZE; 316 unsigned int type; 317 unsigned int size; 318 319 if(!!(ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)) { 320 struct snd_ctl_tlv *xtlv; 321 tlv[0] = -1; 322 tlv[1] = 0; 323 xtlv = calloc(1, sizeof(struct snd_ctl_tlv) + tlv_size); 324 if (xtlv == NULL) 325 return -ENOMEM; 326 xtlv->numid = ctl->info->id.numid; 327 xtlv->length = tlv_size; 328 memcpy(xtlv->tlv, tlv, tlv_size); 329 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, xtlv) < 0) { 330 fprintf( stderr, "SNDRV_CTL_IOCTL_TLV_READ failed\n"); 331 free(xtlv); 332 return -errno; 333 } 334 if (xtlv->tlv[1] + 2 * sizeof(unsigned int) > tlv_size) { 335 free(xtlv); 336 return -EFAULT; 337 } 338 memcpy(tlv, xtlv->tlv, xtlv->tlv[1] + 2 * sizeof(unsigned int)); 339 free(xtlv); 340 341 type = tlv[0]; 342 *tlv_type = type; 343 size = tlv[1]; 344 switch (type) { 345 case SNDRV_CTL_TLVT_DB_SCALE: { 346 int idx = 2; 347 int step; 348 ALOGV("dBscale-"); 349 if (size != 2 * sizeof(unsigned int)) { 350 while (size > 0) { 351 ALOGV("0x%08x,", tlv[idx++]); 352 size -= sizeof(unsigned int); 353 } 354 } else { 355 ALOGV(" min="); 356 print_dB((int)tlv[2]); 357 *min = (long)tlv[2]; 358 ALOGV(" step="); 359 step = (tlv[3] & 0xffff); 360 print_dB(tlv[3] & 0xffff); 361 ALOGV(" max="); 362 *max = (ctl->info->value.integer.max); 363 print_dB((long)ctl->info->value.integer.max); 364 ALOGV(" mute=%i\n", (tlv[3] >> 16) & 1); 365 } 366 break; 367 } 368 case SNDRV_CTL_TLVT_DB_LINEAR: { 369 int idx = 2; 370 ALOGV("dBLiner-"); 371 if (size != 2 * sizeof(unsigned int)) { 372 while (size > 0) { 373 ALOGV("0x%08x,", tlv[idx++]); 374 size -= sizeof(unsigned int); 375 } 376 } else { 377 ALOGV(" min="); 378 *min = tlv[2]; 379 print_dB(tlv[2]); 380 ALOGV(" max="); 381 *max = tlv[3]; 382 print_dB(tlv[3]); 383 } 384 break; 385 } 386 default: 387 break; 388 } 389 return 0; 390 } 391 return -EINVAL; 392} 393 394void mixer_ctl_get(struct mixer_ctl *ctl, unsigned *value) 395{ 396 struct snd_ctl_elem_value ev; 397 unsigned int n; 398 unsigned int *tlv = NULL; 399 enum ctl_type type; 400 unsigned int *tlv_type; 401 long min, max; 402 403 if (is_volume(ctl->info->id.name, &type)) { 404 ALOGV("capability: volume\n"); 405 tlv = calloc(1, DEFAULT_TLV_SIZE); 406 if (tlv == NULL) { 407 ALOGE("failed to allocate memory\n"); 408 } else { 409 mixer_ctl_read_tlv(ctl, tlv, &min, &max, &tlv_type); 410 free(tlv); 411 } 412 } 413 414 memset(&ev, 0, sizeof(ev)); 415 ev.id.numid = ctl->info->id.numid; 416 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev)) 417 return; 418 ALOGV("%s:", ctl->info->id.name); 419 420 switch (ctl->info->type) { 421 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 422 for (n = 0; n < ctl->info->count; n++) 423 ALOGV(" %s", ev.value.integer.value[n] ? "on" : "off"); 424 *value = ev.value.integer.value[0]; 425 break; 426 case SNDRV_CTL_ELEM_TYPE_INTEGER: { 427 for (n = 0; n < ctl->info->count; n++) 428 ALOGV(" %ld", ev.value.integer.value[n]); 429 *value = ev.value.integer.value[0]; 430 break; 431 } 432 case SNDRV_CTL_ELEM_TYPE_INTEGER64: 433 for (n = 0; n < ctl->info->count; n++) 434 ALOGV(" %lld", ev.value.integer64.value[n]); 435 *value = ev.value.integer64.value[0]; 436 break; 437 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 438 for (n = 0; n < ctl->info->count; n++) { 439 unsigned v = ev.value.enumerated.item[n]; 440 ALOGV(" %d (%s)", v, 441 (v < ctl->info->value.enumerated.items) ? ctl->ename[v] : "???"); 442 *value = ev.value.enumerated.item[0]; 443 } 444 break; 445 default: 446 ALOGV(" ???"); 447 } 448 ALOGV("\n"); 449} 450 451static long scale_int(struct snd_ctl_elem_info *ei, unsigned _percent) 452{ 453 long percent; 454 455 if (_percent > 100) 456 percent = 100; 457 else 458 percent = (long) _percent; 459 460 return (long)percent_to_index(percent, ei->value.integer.min, ei->value.integer.max); 461} 462 463static long long scale_int64(struct snd_ctl_elem_info *ei, unsigned _percent) 464{ 465 long long percent; 466 467 if (_percent > 100) 468 percent = 100; 469 else 470 percent = (long) _percent; 471 472 return (long long)percent_to_index(percent, ei->value.integer.min, ei->value.integer.max); 473} 474 475/* 476 * Add support for controls taking more than one parameter as input value 477 * This is useful for volume controls which take two parameters as input value. 478 */ 479int mixer_ctl_mulvalues(struct mixer_ctl *ctl, int count, char ** argv) 480{ 481 struct snd_ctl_elem_value ev; 482 unsigned n; 483 484 if (!ctl) { 485 ALOGV("can't find control\n"); 486 return -1; 487 } 488 if (count < ctl->info->count || count > ctl->info->count) 489 return -EINVAL; 490 491 492 memset(&ev, 0, sizeof(ev)); 493 ev.id.numid = ctl->info->id.numid; 494 switch (ctl->info->type) { 495 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 496 for (n = 0; n < ctl->info->count; n++) 497 ev.value.integer.value[n] = !!atoi(argv[n]); 498 break; 499 case SNDRV_CTL_ELEM_TYPE_INTEGER: { 500 for (n = 0; n < ctl->info->count; n++) { 501 fprintf( stderr, "Value: %d idx:%d\n", atoi(argv[n]), n); 502 ev.value.integer.value[n] = atoi(argv[n]); 503 } 504 break; 505 } 506 case SNDRV_CTL_ELEM_TYPE_INTEGER64: { 507 for (n = 0; n < ctl->info->count; n++) { 508 long long value_ll = scale_int64(ctl->info, atoi(argv[n])); 509 fprintf( stderr, "ll_value = %lld\n", value_ll); 510 ev.value.integer64.value[n] = value_ll; 511 } 512 break; 513 } 514 default: 515 errno = EINVAL; 516 return errno; 517 } 518 519 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev); 520} 521 522int mixer_ctl_set(struct mixer_ctl *ctl, unsigned percent) 523{ 524 struct snd_ctl_elem_value ev; 525 unsigned n; 526 long min, max; 527 unsigned int *tlv = NULL; 528 enum ctl_type type; 529 int volume = 0; 530 unsigned int tlv_type; 531 532 if (!ctl) { 533 ALOGV("can't find control\n"); 534 return -1; 535 } 536 537 if (is_volume(ctl->info->id.name, &type)) { 538 ALOGV("capability: volume\n"); 539 tlv = calloc(1, DEFAULT_TLV_SIZE); 540 if (tlv == NULL) { 541 ALOGE("failed to allocate memory\n"); 542 } else if (!mixer_ctl_read_tlv(ctl, tlv, &min, &max, &tlv_type)) { 543 switch(tlv_type) { 544 case SNDRV_CTL_TLVT_DB_LINEAR: 545 ALOGV("tlv db linear: b4 %d\n", percent); 546 547 if (min < 0) { 548 max = max - min; 549 min = 0; 550 } 551 percent = check_range(percent, min, max); 552 ALOGV("tlv db linear: %d %d %d\n", percent, min, max); 553 volume = 1; 554 break; 555 default: 556 percent = (long)percent_to_index(percent, min, max); 557 percent = check_range(percent, min, max); 558 volume = 1; 559 break; 560 } 561 } else 562 ALOGV("mixer_ctl_read_tlv failed\n"); 563 free(tlv); 564 } 565 memset(&ev, 0, sizeof(ev)); 566 ev.id.numid = ctl->info->id.numid; 567 switch (ctl->info->type) { 568 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 569 for (n = 0; n < ctl->info->count; n++) 570 ev.value.integer.value[n] = !!percent; 571 break; 572 case SNDRV_CTL_ELEM_TYPE_INTEGER: { 573 int value; 574 if (!volume) 575 value = scale_int(ctl->info, percent); 576 else 577 value = (int) percent; 578 for (n = 0; n < ctl->info->count; n++) 579 ev.value.integer.value[n] = value; 580 break; 581 } 582 case SNDRV_CTL_ELEM_TYPE_INTEGER64: { 583 long long value; 584 if (!volume) 585 value = scale_int64(ctl->info, percent); 586 else 587 value = (long long)percent; 588 for (n = 0; n < ctl->info->count; n++) 589 ev.value.integer64.value[n] = value; 590 break; 591 } 592 case SNDRV_CTL_ELEM_TYPE_IEC958: { 593 struct snd_aes_iec958 *iec958; 594 iec958 = (struct snd_aes_iec958 *)percent; 595 memcpy(ev.value.iec958.status,iec958->status,SPDIF_CHANNEL_STATUS_SIZE); 596 break; 597 } 598 default: 599 errno = EINVAL; 600 return errno; 601 } 602 603 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev); 604} 605 606/* the api parses the mixer control input to extract 607 * the value of volume in any one of the following format 608 * <volume><%> 609 * <volume><dB> 610 * All remaining formats are currently ignored. 611 */ 612 613static int set_volume_simple(struct mixer_ctl *ctl, 614 char **ptr, long pmin, long pmax, int count) 615{ 616 long val, orig; 617 char *p = *ptr, *s; 618 struct snd_ctl_elem_value ev; 619 unsigned n; 620 621 if (*p == ':') 622 p++; 623 if (*p == '\0' || (!isdigit(*p) && *p != '-')) 624 goto skip; 625 626 s = p; 627 val = strtol(s, &p, 10); 628 if (*p == '.') { 629 p++; 630 strtol(p, &p, 10); 631 } 632 if (*p == '%') { 633 val = (long)percent_to_index(strtod(s, NULL), pmin, pmax); 634 p++; 635 } else if (p[0] == 'd' && p[1] == 'B') { 636 val = (long)(strtod(s, NULL) * 100.0); 637 p += 2; 638 } else { 639 if (pmin < 0) { 640 pmax = pmax - pmin; 641 pmin = 0; 642 } 643 } 644 val = check_range(val, pmin, pmax); 645 ALOGV("val = %x", val); 646 647 if (!ctl) { 648 ALOGV("can't find control\n"); 649 return -EPERM; 650 } 651 if (count < ctl->info->count || count > ctl->info->count) 652 return -EINVAL; 653 654 ALOGV("Value = "); 655 656 memset(&ev, 0, sizeof(ev)); 657 ev.id.numid = ctl->info->id.numid; 658 switch (ctl->info->type) { 659 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 660 for (n = 0; n < ctl->info->count; n++) 661 ev.value.integer.value[n] = !!val; 662 print_dB(val); 663 break; 664 case SNDRV_CTL_ELEM_TYPE_INTEGER: { 665 for (n = 0; n < ctl->info->count; n++) 666 ev.value.integer.value[n] = val; 667 print_dB(val); 668 break; 669 } 670 case SNDRV_CTL_ELEM_TYPE_INTEGER64: { 671 for (n = 0; n < ctl->info->count; n++) { 672 long long value_ll = scale_int64(ctl->info, val); 673 print_dB(value_ll); 674 ev.value.integer64.value[n] = value_ll; 675 } 676 break; 677 } 678 default: 679 errno = EINVAL; 680 return errno; 681 } 682 683 ALOGV("\n"); 684 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev); 685 686skip: 687 if (*p == ',') 688 p++; 689 *ptr = p; 690 return 0; 691} 692 693int mixer_ctl_set_value(struct mixer_ctl *ctl, int count, char ** argv) 694{ 695 unsigned int size; 696 unsigned int *tlv = NULL; 697 long min, max; 698 enum ctl_type type; 699 unsigned int tlv_type; 700 701 if (is_volume(ctl->info->id.name, &type)) { 702 ALOGV("capability: volume\n"); 703 tlv = calloc(1, DEFAULT_TLV_SIZE); 704 if (tlv == NULL) { 705 ALOGE("failed to allocate memory\n"); 706 } else if (!mixer_ctl_read_tlv(ctl, tlv, &min, &max, &tlv_type)) { 707 ALOGV("min = %x max = %x", min, max); 708 if (set_volume_simple(ctl, argv, min, max, count)) 709 mixer_ctl_mulvalues(ctl, count, argv); 710 } else 711 ALOGV("mixer_ctl_read_tlv failed\n"); 712 free(tlv); 713 } else { 714 mixer_ctl_mulvalues(ctl, count, argv); 715 } 716 return 0; 717} 718 719 720int mixer_ctl_select(struct mixer_ctl *ctl, const char *value) 721{ 722 unsigned n, max; 723 struct snd_ctl_elem_value ev; 724 unsigned int input_str_len, str_len; 725 726 if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) { 727 errno = EINVAL; 728 return -1; 729 } 730 731 input_str_len = strnlen(value,64); 732 733 max = ctl->info->value.enumerated.items; 734 for (n = 0; n < max; n++) { 735 736 str_len = strnlen(ctl->ename[n], 64); 737 if (str_len < input_str_len) 738 str_len = input_str_len; 739 740 if (!strncmp(value, ctl->ename[n], str_len)) { 741 memset(&ev, 0, sizeof(ev)); 742 ev.value.enumerated.item[0] = n; 743 ev.id.numid = ctl->info->id.numid; 744 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev) < 0) 745 return -1; 746 return 0; 747 } 748 } 749 750 errno = EINVAL; 751 return errno; 752} 753