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 <stdio.h> 30 #include <stdlib.h> 31 #include <stdint.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <fcntl.h> 35 #include <errno.h> 36 #include <ctype.h> 37 38 #include <sys/ioctl.h> 39 40 #include <linux/ioctl.h> 41 #define __force 42 #define __bitwise 43 #define __user 44 #include <sound/asound.h> 45 46 #include <tinyalsa/asoundlib.h> 47 48 struct mixer_ctl { 49 struct mixer *mixer; 50 struct snd_ctl_elem_info *info; 51 char **ename; 52 }; 53 54 struct mixer { 55 int fd; 56 struct snd_ctl_card_info card_info; 57 struct snd_ctl_elem_info *elem_info; 58 struct mixer_ctl *ctl; 59 unsigned int count; 60 }; 61 62 void mixer_close(struct mixer *mixer) 63 { 64 unsigned int n,m; 65 66 if (!mixer) 67 return; 68 69 if (mixer->fd >= 0) 70 close(mixer->fd); 71 72 if (mixer->ctl) { 73 for (n = 0; n < mixer->count; n++) { 74 if (mixer->ctl[n].ename) { 75 unsigned int max = mixer->ctl[n].info->value.enumerated.items; 76 for (m = 0; m < max; m++) 77 free(mixer->ctl[n].ename[m]); 78 free(mixer->ctl[n].ename); 79 } 80 } 81 free(mixer->ctl); 82 } 83 84 if (mixer->elem_info) 85 free(mixer->elem_info); 86 87 free(mixer); 88 89 /* TODO: verify frees */ 90 } 91 92 struct mixer *mixer_open(unsigned int card) 93 { 94 struct snd_ctl_elem_list elist; 95 struct snd_ctl_elem_info tmp; 96 struct snd_ctl_elem_id *eid = NULL; 97 struct mixer *mixer = NULL; 98 unsigned int n, m; 99 int fd; 100 char fn[256]; 101 102 snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card); 103 fd = open(fn, O_RDWR); 104 if (fd < 0) 105 return 0; 106 107 memset(&elist, 0, sizeof(elist)); 108 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) 109 goto fail; 110 111 mixer = calloc(1, sizeof(*mixer)); 112 if (!mixer) 113 goto fail; 114 115 mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl)); 116 mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info)); 117 if (!mixer->ctl || !mixer->elem_info) 118 goto fail; 119 120 if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0) 121 goto fail; 122 123 eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id)); 124 if (!eid) 125 goto fail; 126 127 mixer->count = elist.count; 128 mixer->fd = fd; 129 elist.space = mixer->count; 130 elist.pids = eid; 131 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) 132 goto fail; 133 134 for (n = 0; n < mixer->count; n++) { 135 struct snd_ctl_elem_info *ei = mixer->elem_info + n; 136 ei->id.numid = eid[n].numid; 137 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0) 138 goto fail; 139 mixer->ctl[n].info = ei; 140 mixer->ctl[n].mixer = mixer; 141 if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { 142 char **enames = calloc(ei->value.enumerated.items, sizeof(char*)); 143 if (!enames) 144 goto fail; 145 mixer->ctl[n].ename = enames; 146 for (m = 0; m < ei->value.enumerated.items; m++) { 147 memset(&tmp, 0, sizeof(tmp)); 148 tmp.id.numid = ei->id.numid; 149 tmp.value.enumerated.item = m; 150 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0) 151 goto fail; 152 enames[m] = strdup(tmp.value.enumerated.name); 153 if (!enames[m]) 154 goto fail; 155 } 156 } 157 } 158 159 free(eid); 160 return mixer; 161 162 fail: 163 /* TODO: verify frees in failure case */ 164 if (eid) 165 free(eid); 166 if (mixer) 167 mixer_close(mixer); 168 else if (fd >= 0) 169 close(fd); 170 return 0; 171 } 172 173 const char *mixer_get_name(struct mixer *mixer) 174 { 175 return (const char *)mixer->card_info.name; 176 } 177 178 unsigned int mixer_get_num_ctls(struct mixer *mixer) 179 { 180 if (!mixer) 181 return 0; 182 183 return mixer->count; 184 } 185 186 struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id) 187 { 188 if (mixer && (id < mixer->count)) 189 return mixer->ctl + id; 190 191 return NULL; 192 } 193 194 struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name) 195 { 196 unsigned int n; 197 198 if (!mixer) 199 return NULL; 200 201 for (n = 0; n < mixer->count; n++) 202 if (!strcmp(name, (char*) mixer->elem_info[n].id.name)) 203 return mixer->ctl + n; 204 205 return NULL; 206 } 207 208 void mixer_ctl_update(struct mixer_ctl *ctl) 209 { 210 ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info); 211 } 212 213 const char *mixer_ctl_get_name(struct mixer_ctl *ctl) 214 { 215 if (!ctl) 216 return NULL; 217 218 return (const char *)ctl->info->id.name; 219 } 220 221 enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl) 222 { 223 if (!ctl) 224 return MIXER_CTL_TYPE_UNKNOWN; 225 226 switch (ctl->info->type) { 227 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL; 228 case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT; 229 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM; 230 case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE; 231 case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958; 232 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64; 233 default: return MIXER_CTL_TYPE_UNKNOWN; 234 }; 235 } 236 237 const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl) 238 { 239 if (!ctl) 240 return ""; 241 242 switch (ctl->info->type) { 243 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL"; 244 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT"; 245 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM"; 246 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE"; 247 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958"; 248 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64"; 249 default: return "Unknown"; 250 }; 251 } 252 253 unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl) 254 { 255 if (!ctl) 256 return 0; 257 258 return ctl->info->count; 259 } 260 261 static int percent_to_int(struct snd_ctl_elem_info *ei, int percent) 262 { 263 int range; 264 265 if (percent > 100) 266 percent = 100; 267 else if (percent < 0) 268 percent = 0; 269 270 range = (ei->value.integer.max - ei->value.integer.min); 271 272 return ei->value.integer.min + (range * percent) / 100; 273 } 274 275 static int int_to_percent(struct snd_ctl_elem_info *ei, int value) 276 { 277 int range = (ei->value.integer.max - ei->value.integer.min); 278 279 if (range == 0) 280 return 0; 281 282 return ((value - ei->value.integer.min) / range) * 100; 283 } 284 285 int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id) 286 { 287 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER)) 288 return -EINVAL; 289 290 return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id)); 291 } 292 293 int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent) 294 { 295 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER)) 296 return -EINVAL; 297 298 return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent)); 299 } 300 301 int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id) 302 { 303 struct snd_ctl_elem_value ev; 304 int ret; 305 306 if (!ctl || (id >= ctl->info->count)) 307 return -EINVAL; 308 309 memset(&ev, 0, sizeof(ev)); 310 ev.id.numid = ctl->info->id.numid; 311 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev); 312 if (ret < 0) 313 return ret; 314 315 switch (ctl->info->type) { 316 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 317 return !!ev.value.integer.value[id]; 318 319 case SNDRV_CTL_ELEM_TYPE_INTEGER: 320 return ev.value.integer.value[id]; 321 322 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 323 return ev.value.enumerated.item[id]; 324 325 case SNDRV_CTL_ELEM_TYPE_BYTES: 326 return ev.value.bytes.data[id]; 327 328 default: 329 return -EINVAL; 330 } 331 332 return 0; 333 } 334 335 int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count) 336 { 337 struct snd_ctl_elem_value ev; 338 int ret = 0; 339 size_t size; 340 void *source; 341 342 if (!ctl || (count > ctl->info->count) || !count || !array) 343 return -EINVAL; 344 345 memset(&ev, 0, sizeof(ev)); 346 ev.id.numid = ctl->info->id.numid; 347 348 switch (ctl->info->type) { 349 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 350 case SNDRV_CTL_ELEM_TYPE_INTEGER: 351 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev); 352 if (ret < 0) 353 return ret; 354 size = sizeof(ev.value.integer.value[0]); 355 source = ev.value.integer.value; 356 break; 357 358 case SNDRV_CTL_ELEM_TYPE_BYTES: 359 /* check if this is new bytes TLV */ 360 if (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) { 361 struct snd_ctl_tlv *tlv; 362 int ret; 363 364 if (count > SIZE_MAX - sizeof(*tlv)) 365 return -EINVAL; 366 tlv = calloc(1, sizeof(*tlv) + count); 367 if (!tlv) 368 return -ENOMEM; 369 tlv->numid = ctl->info->id.numid; 370 tlv->length = count; 371 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, tlv); 372 373 source = tlv->tlv; 374 memcpy(array, source, count); 375 376 free(tlv); 377 378 return ret; 379 } else { 380 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev); 381 if (ret < 0) 382 return ret; 383 size = sizeof(ev.value.bytes.data[0]); 384 source = ev.value.bytes.data; 385 break; 386 } 387 388 case SNDRV_CTL_ELEM_TYPE_IEC958: 389 size = sizeof(ev.value.iec958); 390 source = &ev.value.iec958; 391 break; 392 393 default: 394 return -EINVAL; 395 } 396 397 memcpy(array, source, size * count); 398 399 return 0; 400 } 401 402 int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value) 403 { 404 struct snd_ctl_elem_value ev; 405 int ret; 406 407 if (!ctl || (id >= ctl->info->count)) 408 return -EINVAL; 409 410 memset(&ev, 0, sizeof(ev)); 411 ev.id.numid = ctl->info->id.numid; 412 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev); 413 if (ret < 0) 414 return ret; 415 416 switch (ctl->info->type) { 417 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 418 ev.value.integer.value[id] = !!value; 419 break; 420 421 case SNDRV_CTL_ELEM_TYPE_INTEGER: 422 ev.value.integer.value[id] = value; 423 break; 424 425 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 426 ev.value.enumerated.item[id] = value; 427 break; 428 429 case SNDRV_CTL_ELEM_TYPE_BYTES: 430 ev.value.bytes.data[id] = value; 431 break; 432 433 default: 434 return -EINVAL; 435 } 436 437 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev); 438 } 439 440 int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count) 441 { 442 struct snd_ctl_elem_value ev; 443 size_t size; 444 void *dest; 445 446 if (!ctl || (count > ctl->info->count) || !count || !array) 447 return -EINVAL; 448 449 memset(&ev, 0, sizeof(ev)); 450 ev.id.numid = ctl->info->id.numid; 451 452 switch (ctl->info->type) { 453 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 454 case SNDRV_CTL_ELEM_TYPE_INTEGER: 455 size = sizeof(ev.value.integer.value[0]); 456 dest = ev.value.integer.value; 457 break; 458 459 case SNDRV_CTL_ELEM_TYPE_BYTES: 460 /* check if this is new bytes TLV */ 461 if (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) { 462 struct snd_ctl_tlv *tlv; 463 int ret = 0; 464 if (count > SIZE_MAX - sizeof(*tlv)) 465 return -EINVAL; 466 tlv = calloc(1, sizeof(*tlv) + count); 467 if (!tlv) 468 return -ENOMEM; 469 tlv->numid = ctl->info->id.numid; 470 tlv->length = count; 471 memcpy(tlv->tlv, array, count); 472 473 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_WRITE, tlv); 474 free(tlv); 475 476 return ret; 477 } else { 478 size = sizeof(ev.value.bytes.data[0]); 479 dest = ev.value.bytes.data; 480 } 481 break; 482 483 case SNDRV_CTL_ELEM_TYPE_IEC958: 484 size = sizeof(ev.value.iec958); 485 dest = &ev.value.iec958; 486 break; 487 488 default: 489 return -EINVAL; 490 } 491 492 memcpy(dest, array, size * count); 493 494 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev); 495 } 496 497 int mixer_ctl_get_range_min(struct mixer_ctl *ctl) 498 { 499 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER)) 500 return -EINVAL; 501 502 return ctl->info->value.integer.min; 503 } 504 505 int mixer_ctl_get_range_max(struct mixer_ctl *ctl) 506 { 507 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER)) 508 return -EINVAL; 509 510 return ctl->info->value.integer.max; 511 } 512 513 unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl) 514 { 515 if (!ctl) 516 return 0; 517 518 return ctl->info->value.enumerated.items; 519 } 520 521 const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl, 522 unsigned int enum_id) 523 { 524 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) || 525 (enum_id >= ctl->info->value.enumerated.items)) 526 return NULL; 527 528 return (const char *)ctl->ename[enum_id]; 529 } 530 531 int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string) 532 { 533 unsigned int i, num_enums; 534 struct snd_ctl_elem_value ev; 535 int ret; 536 537 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED)) 538 return -EINVAL; 539 540 num_enums = ctl->info->value.enumerated.items; 541 for (i = 0; i < num_enums; i++) { 542 if (!strcmp(string, ctl->ename[i])) { 543 memset(&ev, 0, sizeof(ev)); 544 ev.value.enumerated.item[0] = i; 545 ev.id.numid = ctl->info->id.numid; 546 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev); 547 if (ret < 0) 548 return ret; 549 return 0; 550 } 551 } 552 553 return -EINVAL; 554 } 555 556