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