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