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