1 /* 2 ** Copyright 2010, The Android Open-Source Project 3 ** 4 ** Licensed under the Apache License, Version 2.0 (the "License"); 5 ** you may not use this file except in compliance with the License. 6 ** You may obtain a copy of the License at 7 ** 8 ** http://www.apache.org/licenses/LICENSE-2.0 9 ** 10 ** Unless required by applicable law or agreed to in writing, software 11 ** distributed under the License is distributed on an "AS IS" BASIS, 12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 ** See the License for the specific language governing permissions and 14 ** limitations under the License. 15 */ 16 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <unistd.h> 21 #include <fcntl.h> 22 #include <errno.h> 23 #include <ctype.h> 24 25 #include <linux/ioctl.h> 26 #define __force 27 #define __bitwise 28 #define __user 29 #include "asound.h" 30 31 #include "alsa_audio.h" 32 33 static const char *elem_iface_name(snd_ctl_elem_iface_t n) 34 { 35 switch (n) { 36 case SNDRV_CTL_ELEM_IFACE_CARD: return "CARD"; 37 case SNDRV_CTL_ELEM_IFACE_HWDEP: return "HWDEP"; 38 case SNDRV_CTL_ELEM_IFACE_MIXER: return "MIXER"; 39 case SNDRV_CTL_ELEM_IFACE_PCM: return "PCM"; 40 case SNDRV_CTL_ELEM_IFACE_RAWMIDI: return "MIDI"; 41 case SNDRV_CTL_ELEM_IFACE_TIMER: return "TIMER"; 42 case SNDRV_CTL_ELEM_IFACE_SEQUENCER: return "SEQ"; 43 default: return "???"; 44 } 45 } 46 47 static const char *elem_type_name(snd_ctl_elem_type_t n) 48 { 49 switch (n) { 50 case SNDRV_CTL_ELEM_TYPE_NONE: return "NONE"; 51 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL"; 52 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT32"; 53 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM"; 54 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTES"; 55 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958"; 56 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64"; 57 default: return "???"; 58 } 59 } 60 61 62 struct mixer_ctl { 63 struct mixer *mixer; 64 struct snd_ctl_elem_info *info; 65 char **ename; 66 }; 67 68 struct mixer { 69 int fd; 70 struct snd_ctl_elem_info *info; 71 struct mixer_ctl *ctl; 72 unsigned count; 73 }; 74 75 void mixer_close(struct mixer *mixer) 76 { 77 unsigned n,m; 78 79 if (mixer->fd >= 0) 80 close(mixer->fd); 81 82 if (mixer->ctl) { 83 for (n = 0; n < mixer->count; n++) { 84 if (mixer->ctl[n].ename) { 85 unsigned max = mixer->ctl[n].info->value.enumerated.items; 86 for (m = 0; m < max; m++) 87 free(mixer->ctl[n].ename[m]); 88 free(mixer->ctl[n].ename); 89 } 90 } 91 free(mixer->ctl); 92 } 93 94 if (mixer->info) 95 free(mixer->info); 96 97 free(mixer); 98 } 99 100 struct mixer *mixer_open(void) 101 { 102 struct snd_ctl_elem_list elist; 103 struct snd_ctl_elem_info tmp; 104 struct snd_ctl_elem_id *eid = NULL; 105 struct mixer *mixer = NULL; 106 unsigned n, m; 107 int fd; 108 109 fd = open("/dev/snd/controlC0", O_RDWR); 110 if (fd < 0) 111 return 0; 112 113 memset(&elist, 0, sizeof(elist)); 114 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) 115 goto fail; 116 117 mixer = calloc(1, sizeof(*mixer)); 118 if (!mixer) 119 goto fail; 120 121 mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl)); 122 mixer->info = calloc(elist.count, sizeof(struct snd_ctl_elem_info)); 123 if (!mixer->ctl || !mixer->info) 124 goto fail; 125 126 eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id)); 127 if (!eid) 128 goto fail; 129 130 mixer->count = elist.count; 131 mixer->fd = fd; 132 elist.space = mixer->count; 133 elist.pids = eid; 134 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) 135 goto fail; 136 137 for (n = 0; n < mixer->count; n++) { 138 struct snd_ctl_elem_info *ei = mixer->info + n; 139 ei->id.numid = eid[n].numid; 140 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0) 141 goto fail; 142 mixer->ctl[n].info = ei; 143 mixer->ctl[n].mixer = mixer; 144 if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { 145 char **enames = calloc(ei->value.enumerated.items, sizeof(char*)); 146 if (!enames) 147 goto fail; 148 mixer->ctl[n].ename = enames; 149 for (m = 0; m < ei->value.enumerated.items; m++) { 150 memset(&tmp, 0, sizeof(tmp)); 151 tmp.id.numid = ei->id.numid; 152 tmp.value.enumerated.item = m; 153 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0) 154 goto fail; 155 enames[m] = strdup(tmp.value.enumerated.name); 156 if (!enames[m]) 157 goto fail; 158 } 159 } 160 } 161 162 free(eid); 163 return mixer; 164 165 fail: 166 if (eid) 167 free(eid); 168 if (mixer) 169 mixer_close(mixer); 170 else if (fd >= 0) 171 close(fd); 172 return 0; 173 } 174 175 void mixer_dump(struct mixer *mixer) 176 { 177 unsigned n, m; 178 179 printf(" id iface dev sub idx num perms type name\n"); 180 for (n = 0; n < mixer->count; n++) { 181 struct snd_ctl_elem_info *ei = mixer->info + n; 182 183 printf("%4d %5s %3d %3d %3d %3d %c%c%c%c%c%c%c%c%c %-6s %s", 184 ei->id.numid, elem_iface_name(ei->id.iface), 185 ei->id.device, ei->id.subdevice, ei->id.index, 186 ei->count, 187 (ei->access & SNDRV_CTL_ELEM_ACCESS_READ) ? 'r' : ' ', 188 (ei->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ? 'w' : ' ', 189 (ei->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE) ? 'V' : ' ', 190 (ei->access & SNDRV_CTL_ELEM_ACCESS_TIMESTAMP) ? 'T' : ' ', 191 (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) ? 'R' : ' ', 192 (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) ? 'W' : ' ', 193 (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) ? 'C' : ' ', 194 (ei->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) ? 'I' : ' ', 195 (ei->access & SNDRV_CTL_ELEM_ACCESS_LOCK) ? 'L' : ' ', 196 elem_type_name(ei->type), 197 ei->id.name); 198 switch (ei->type) { 199 case SNDRV_CTL_ELEM_TYPE_INTEGER: 200 printf(ei->value.integer.step ? 201 " { %ld-%ld, %ld }\n" : " { %ld-%ld }", 202 ei->value.integer.min, 203 ei->value.integer.max, 204 ei->value.integer.step); 205 break; 206 case SNDRV_CTL_ELEM_TYPE_INTEGER64: 207 printf(ei->value.integer64.step ? 208 " { %lld-%lld, %lld }\n" : " { %lld-%lld }", 209 ei->value.integer64.min, 210 ei->value.integer64.max, 211 ei->value.integer64.step); 212 break; 213 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: { 214 unsigned m; 215 printf(" { %s=0", mixer->ctl[n].ename[0]); 216 for (m = 1; m < ei->value.enumerated.items; m++) 217 printf(", %s=%d", mixer->ctl[n].ename[m],m); 218 printf(" }"); 219 break; 220 } 221 } 222 printf("\n"); 223 } 224 } 225 226 struct mixer_ctl *mixer_get_control(struct mixer *mixer, 227 const char *name, unsigned index) 228 { 229 unsigned n; 230 for (n = 0; n < mixer->count; n++) { 231 if (mixer->info[n].id.index == index) { 232 if (!strcmp(name, (char*) mixer->info[n].id.name)) { 233 return mixer->ctl + n; 234 } 235 } 236 } 237 return 0; 238 } 239 240 struct mixer_ctl *mixer_get_nth_control(struct mixer *mixer, unsigned n) 241 { 242 if (n < mixer->count) 243 return mixer->ctl + n; 244 return 0; 245 } 246 247 void mixer_ctl_print(struct mixer_ctl *ctl) 248 { 249 struct snd_ctl_elem_value ev; 250 unsigned n; 251 252 memset(&ev, 0, sizeof(ev)); 253 ev.id.numid = ctl->info->id.numid; 254 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev)) 255 return; 256 printf("%s:", ctl->info->id.name); 257 258 switch (ctl->info->type) { 259 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 260 for (n = 0; n < ctl->info->count; n++) 261 printf(" %s", ev.value.integer.value[n] ? "ON" : "OFF"); 262 break; 263 case SNDRV_CTL_ELEM_TYPE_INTEGER: { 264 for (n = 0; n < ctl->info->count; n++) 265 printf(" %ld", ev.value.integer.value[n]); 266 break; 267 } 268 case SNDRV_CTL_ELEM_TYPE_INTEGER64: 269 for (n = 0; n < ctl->info->count; n++) 270 printf(" %lld", ev.value.integer64.value[n]); 271 break; 272 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 273 for (n = 0; n < ctl->info->count; n++) { 274 unsigned v = ev.value.enumerated.item[n]; 275 printf(" %d (%s)", v, 276 (v < ctl->info->value.enumerated.items) ? ctl->ename[v] : "???"); 277 } 278 break; 279 default: 280 printf(" ???"); 281 } 282 printf("\n"); 283 } 284 285 static long scale_int(struct snd_ctl_elem_info *ei, unsigned _percent) 286 { 287 long percent; 288 long range; 289 290 if (_percent > 100) 291 percent = 100; 292 else 293 percent = (long) _percent; 294 295 range = (ei->value.integer.max - ei->value.integer.min); 296 297 return ei->value.integer.min + (range * percent) / 100LL; 298 } 299 300 static long long scale_int64(struct snd_ctl_elem_info *ei, unsigned _percent) 301 { 302 long long percent; 303 long long range; 304 305 if (_percent > 100) 306 percent = 100; 307 else 308 percent = (long) _percent; 309 310 range = (ei->value.integer.max - ei->value.integer.min) * 100LL; 311 312 return ei->value.integer.min + (range / percent); 313 } 314 315 int mixer_ctl_set(struct mixer_ctl *ctl, unsigned percent) 316 { 317 struct snd_ctl_elem_value ev; 318 unsigned n; 319 320 memset(&ev, 0, sizeof(ev)); 321 ev.id.numid = ctl->info->id.numid; 322 switch (ctl->info->type) { 323 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 324 for (n = 0; n < ctl->info->count; n++) 325 ev.value.integer.value[n] = !!percent; 326 break; 327 case SNDRV_CTL_ELEM_TYPE_INTEGER: { 328 long value = scale_int(ctl->info, percent); 329 for (n = 0; n < ctl->info->count; n++) 330 ev.value.integer.value[n] = value; 331 break; 332 } 333 case SNDRV_CTL_ELEM_TYPE_INTEGER64: { 334 long long value = scale_int64(ctl->info, percent); 335 for (n = 0; n < ctl->info->count; n++) 336 ev.value.integer64.value[n] = value; 337 break; 338 } 339 default: 340 errno = EINVAL; 341 return -1; 342 } 343 344 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev); 345 } 346 347 int mixer_ctl_select(struct mixer_ctl *ctl, const char *value) 348 { 349 unsigned n, max; 350 struct snd_ctl_elem_value ev; 351 352 if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) { 353 errno = EINVAL; 354 return -1; 355 } 356 357 max = ctl->info->value.enumerated.items; 358 for (n = 0; n < max; n++) { 359 if (!strcmp(value, ctl->ename[n])) { 360 memset(&ev, 0, sizeof(ev)); 361 ev.value.enumerated.item[0] = n; 362 ev.id.numid = ctl->info->id.numid; 363 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev) < 0) 364 return -1; 365 return 0; 366 } 367 } 368 369 errno = EINVAL; 370 return -1; 371 } 372