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
mixer_close(struct mixer * mixer)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
mixer_open(unsigned int card)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
mixer_get_name(struct mixer * mixer)173 const char *mixer_get_name(struct mixer *mixer)
174 {
175 return (const char *)mixer->card_info.name;
176 }
177
mixer_get_num_ctls(struct mixer * mixer)178 unsigned int mixer_get_num_ctls(struct mixer *mixer)
179 {
180 if (!mixer)
181 return 0;
182
183 return mixer->count;
184 }
185
mixer_get_ctl(struct mixer * mixer,unsigned int id)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
mixer_get_ctl_by_name(struct mixer * mixer,const char * name)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
mixer_ctl_update(struct mixer_ctl * ctl)208 void mixer_ctl_update(struct mixer_ctl *ctl)
209 {
210 ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
211 }
212
mixer_ctl_get_name(struct mixer_ctl * ctl)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
mixer_ctl_get_type(struct mixer_ctl * ctl)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
mixer_ctl_get_type_string(struct mixer_ctl * ctl)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
mixer_ctl_get_num_values(struct mixer_ctl * ctl)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
percent_to_int(struct snd_ctl_elem_info * ei,int percent)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
int_to_percent(struct snd_ctl_elem_info * ei,int value)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
mixer_ctl_get_percent(struct mixer_ctl * ctl,unsigned int id)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
mixer_ctl_set_percent(struct mixer_ctl * ctl,unsigned int id,int percent)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
mixer_ctl_get_value(struct mixer_ctl * ctl,unsigned int id)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
mixer_ctl_get_array(struct mixer_ctl * ctl,void * array,size_t count)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
mixer_ctl_set_value(struct mixer_ctl * ctl,unsigned int id,int value)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
mixer_ctl_set_array(struct mixer_ctl * ctl,const void * array,size_t count)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
mixer_ctl_get_range_min(struct mixer_ctl * ctl)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
mixer_ctl_get_range_max(struct mixer_ctl * ctl)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
mixer_ctl_get_num_enums(struct mixer_ctl * ctl)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
mixer_ctl_get_enum_string(struct mixer_ctl * ctl,unsigned int enum_id)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
mixer_ctl_set_enum_by_string(struct mixer_ctl * ctl,const char * string)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