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 #include <poll.h>
38
39 #include <sys/ioctl.h>
40
41 #include <linux/ioctl.h>
42 #define __force
43 #define __bitwise
44 #define __user
45 #include <sound/asound.h>
46
47 #include <tinyalsa/asoundlib.h>
48
49 struct mixer_ctl {
50 struct mixer *mixer;
51 struct snd_ctl_elem_info *info;
52 char **ename;
53 };
54
55 struct mixer {
56 int fd;
57 struct snd_ctl_card_info card_info;
58 struct snd_ctl_elem_info *elem_info;
59 struct mixer_ctl *ctl;
60 unsigned int count;
61 };
62
mixer_close(struct mixer * mixer)63 void mixer_close(struct mixer *mixer)
64 {
65 unsigned int n,m;
66
67 if (!mixer)
68 return;
69
70 if (mixer->fd >= 0)
71 close(mixer->fd);
72
73 if (mixer->ctl) {
74 for (n = 0; n < mixer->count; n++) {
75 if (mixer->ctl[n].ename) {
76 unsigned int max = mixer->ctl[n].info->value.enumerated.items;
77 for (m = 0; m < max; m++)
78 free(mixer->ctl[n].ename[m]);
79 free(mixer->ctl[n].ename);
80 }
81 }
82 free(mixer->ctl);
83 }
84
85 if (mixer->elem_info)
86 free(mixer->elem_info);
87
88 free(mixer);
89
90 /* TODO: verify frees */
91 }
92
mixer_open(unsigned int card)93 struct mixer *mixer_open(unsigned int card)
94 {
95 struct snd_ctl_elem_list elist;
96 struct snd_ctl_elem_info tmp;
97 struct snd_ctl_elem_id *eid = NULL;
98 struct mixer *mixer = NULL;
99 unsigned int n, m;
100 int fd;
101 char fn[256];
102
103 snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
104 fd = open(fn, O_RDWR);
105 if (fd < 0)
106 return 0;
107
108 memset(&elist, 0, sizeof(elist));
109 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
110 goto fail;
111
112 mixer = calloc(1, sizeof(*mixer));
113 if (!mixer)
114 goto fail;
115
116 mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
117 mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
118 if (!mixer->ctl || !mixer->elem_info)
119 goto fail;
120
121 if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
122 goto fail;
123
124 eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
125 if (!eid)
126 goto fail;
127
128 mixer->count = elist.count;
129 mixer->fd = fd;
130 elist.space = mixer->count;
131 elist.pids = eid;
132 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
133 goto fail;
134
135 for (n = 0; n < mixer->count; n++) {
136 struct snd_ctl_elem_info *ei = mixer->elem_info + n;
137 ei->id.numid = eid[n].numid;
138 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
139 goto fail;
140 mixer->ctl[n].info = ei;
141 mixer->ctl[n].mixer = mixer;
142 if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
143 char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
144 if (!enames)
145 goto fail;
146 mixer->ctl[n].ename = enames;
147 for (m = 0; m < ei->value.enumerated.items; m++) {
148 memset(&tmp, 0, sizeof(tmp));
149 tmp.id.numid = ei->id.numid;
150 tmp.value.enumerated.item = m;
151 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
152 goto fail;
153 enames[m] = strdup(tmp.value.enumerated.name);
154 if (!enames[m])
155 goto fail;
156 }
157 }
158 }
159
160 free(eid);
161 return mixer;
162
163 fail:
164 /* TODO: verify frees in failure case */
165 if (eid)
166 free(eid);
167 if (mixer)
168 mixer_close(mixer);
169 else if (fd >= 0)
170 close(fd);
171 return 0;
172 }
173
mixer_get_name(struct mixer * mixer)174 const char *mixer_get_name(struct mixer *mixer)
175 {
176 return (const char *)mixer->card_info.name;
177 }
178
mixer_get_num_ctls(struct mixer * mixer)179 unsigned int mixer_get_num_ctls(struct mixer *mixer)
180 {
181 if (!mixer)
182 return 0;
183
184 return mixer->count;
185 }
186
mixer_get_ctl(struct mixer * mixer,unsigned int id)187 struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
188 {
189 if (mixer && (id < mixer->count))
190 return mixer->ctl + id;
191
192 return NULL;
193 }
194
mixer_get_ctl_by_name(struct mixer * mixer,const char * name)195 struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
196 {
197 unsigned int n;
198
199 if (!mixer)
200 return NULL;
201
202 for (n = 0; n < mixer->count; n++)
203 if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
204 return mixer->ctl + n;
205
206 return NULL;
207 }
208
mixer_ctl_update(struct mixer_ctl * ctl)209 void mixer_ctl_update(struct mixer_ctl *ctl)
210 {
211 ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
212 }
213
mixer_ctl_get_name(struct mixer_ctl * ctl)214 const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
215 {
216 if (!ctl)
217 return NULL;
218
219 return (const char *)ctl->info->id.name;
220 }
221
mixer_ctl_get_type(struct mixer_ctl * ctl)222 enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
223 {
224 if (!ctl)
225 return MIXER_CTL_TYPE_UNKNOWN;
226
227 switch (ctl->info->type) {
228 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;
229 case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;
230 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
231 case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;
232 case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;
233 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;
234 default: return MIXER_CTL_TYPE_UNKNOWN;
235 };
236 }
237
mixer_ctl_get_type_string(struct mixer_ctl * ctl)238 const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
239 {
240 if (!ctl)
241 return "";
242
243 switch (ctl->info->type) {
244 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
245 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";
246 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
247 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";
248 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
249 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
250 default: return "Unknown";
251 };
252 }
253
mixer_ctl_get_num_values(struct mixer_ctl * ctl)254 unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
255 {
256 if (!ctl)
257 return 0;
258
259 return ctl->info->count;
260 }
261
percent_to_int(struct snd_ctl_elem_info * ei,int percent)262 static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
263 {
264 int range;
265
266 if (percent > 100)
267 percent = 100;
268 else if (percent < 0)
269 percent = 0;
270
271 range = (ei->value.integer.max - ei->value.integer.min);
272
273 return ei->value.integer.min + (range * percent) / 100;
274 }
275
int_to_percent(struct snd_ctl_elem_info * ei,int value)276 static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
277 {
278 int range = (ei->value.integer.max - ei->value.integer.min);
279
280 if (range == 0)
281 return 0;
282
283 return ((value - ei->value.integer.min) / range) * 100;
284 }
285
mixer_ctl_get_percent(struct mixer_ctl * ctl,unsigned int id)286 int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
287 {
288 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
289 return -EINVAL;
290
291 return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
292 }
293
mixer_ctl_set_percent(struct mixer_ctl * ctl,unsigned int id,int percent)294 int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
295 {
296 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
297 return -EINVAL;
298
299 return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
300 }
301
mixer_ctl_get_value(struct mixer_ctl * ctl,unsigned int id)302 int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
303 {
304 struct snd_ctl_elem_value ev;
305 int ret;
306
307 if (!ctl || (id >= ctl->info->count))
308 return -EINVAL;
309
310 memset(&ev, 0, sizeof(ev));
311 ev.id.numid = ctl->info->id.numid;
312 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
313 if (ret < 0)
314 return ret;
315
316 switch (ctl->info->type) {
317 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
318 return !!ev.value.integer.value[id];
319
320 case SNDRV_CTL_ELEM_TYPE_INTEGER:
321 return ev.value.integer.value[id];
322
323 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
324 return ev.value.enumerated.item[id];
325
326 case SNDRV_CTL_ELEM_TYPE_BYTES:
327 return ev.value.bytes.data[id];
328
329 default:
330 return -EINVAL;
331 }
332
333 return 0;
334 }
335
mixer_ctl_is_access_tlv_rw(struct mixer_ctl * ctl)336 int mixer_ctl_is_access_tlv_rw(struct mixer_ctl *ctl)
337 {
338 return (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
339 }
340
mixer_ctl_get_array(struct mixer_ctl * ctl,void * array,size_t count)341 int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
342 {
343 struct snd_ctl_elem_value ev;
344 int ret = 0;
345 size_t size;
346 void *source;
347 size_t total_count;
348
349 if ((!ctl) || !count || !array)
350 return -EINVAL;
351
352 total_count = ctl->info->count;
353
354 if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
355 mixer_ctl_is_access_tlv_rw(ctl)) {
356 /* Additional two words is for the TLV header */
357 total_count += TLV_HEADER_SIZE;
358 }
359
360 if (count > total_count)
361 return -EINVAL;
362
363 memset(&ev, 0, sizeof(ev));
364 ev.id.numid = ctl->info->id.numid;
365
366 switch (ctl->info->type) {
367 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
368 case SNDRV_CTL_ELEM_TYPE_INTEGER:
369 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
370 if (ret < 0)
371 return ret;
372 size = sizeof(ev.value.integer.value[0]);
373 source = ev.value.integer.value;
374 break;
375
376 case SNDRV_CTL_ELEM_TYPE_BYTES:
377 /* check if this is new bytes TLV */
378 if (mixer_ctl_is_access_tlv_rw(ctl)) {
379 struct snd_ctl_tlv *tlv;
380 int ret;
381
382 if (count > SIZE_MAX - sizeof(*tlv))
383 return -EINVAL;
384 tlv = calloc(1, sizeof(*tlv) + count);
385 if (!tlv)
386 return -ENOMEM;
387 tlv->numid = ctl->info->id.numid;
388 tlv->length = count;
389 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, tlv);
390
391 source = tlv->tlv;
392 memcpy(array, source, count);
393
394 free(tlv);
395
396 return ret;
397 } else {
398 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
399 if (ret < 0)
400 return ret;
401 size = sizeof(ev.value.bytes.data[0]);
402 source = ev.value.bytes.data;
403 break;
404 }
405
406 case SNDRV_CTL_ELEM_TYPE_IEC958:
407 size = sizeof(ev.value.iec958);
408 source = &ev.value.iec958;
409 break;
410
411 default:
412 return -EINVAL;
413 }
414
415 memcpy(array, source, size * count);
416
417 return 0;
418 }
419
mixer_ctl_set_value(struct mixer_ctl * ctl,unsigned int id,int value)420 int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
421 {
422 struct snd_ctl_elem_value ev;
423 int ret;
424
425 if (!ctl || (id >= ctl->info->count))
426 return -EINVAL;
427
428 memset(&ev, 0, sizeof(ev));
429 ev.id.numid = ctl->info->id.numid;
430 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
431 if (ret < 0)
432 return ret;
433
434 switch (ctl->info->type) {
435 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
436 ev.value.integer.value[id] = !!value;
437 break;
438
439 case SNDRV_CTL_ELEM_TYPE_INTEGER:
440 ev.value.integer.value[id] = value;
441 break;
442
443 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
444 ev.value.enumerated.item[id] = value;
445 break;
446
447 case SNDRV_CTL_ELEM_TYPE_BYTES:
448 ev.value.bytes.data[id] = value;
449 break;
450
451 default:
452 return -EINVAL;
453 }
454
455 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
456 }
457
mixer_ctl_set_array(struct mixer_ctl * ctl,const void * array,size_t count)458 int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
459 {
460 struct snd_ctl_elem_value ev;
461 size_t size;
462 void *dest;
463 size_t total_count;
464
465 if ((!ctl) || !count || !array)
466 return -EINVAL;
467
468 total_count = ctl->info->count;
469
470 if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
471 mixer_ctl_is_access_tlv_rw(ctl)) {
472 /* Additional two words is for the TLV header */
473 total_count += TLV_HEADER_SIZE;
474 }
475
476 if (count > total_count)
477 return -EINVAL;
478
479 memset(&ev, 0, sizeof(ev));
480 ev.id.numid = ctl->info->id.numid;
481
482 switch (ctl->info->type) {
483 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
484 case SNDRV_CTL_ELEM_TYPE_INTEGER:
485 size = sizeof(ev.value.integer.value[0]);
486 dest = ev.value.integer.value;
487 break;
488
489 case SNDRV_CTL_ELEM_TYPE_BYTES:
490 /* check if this is new bytes TLV */
491 if (mixer_ctl_is_access_tlv_rw(ctl)) {
492 struct snd_ctl_tlv *tlv;
493 int ret = 0;
494 if (count > SIZE_MAX - sizeof(*tlv))
495 return -EINVAL;
496 tlv = calloc(1, sizeof(*tlv) + count);
497 if (!tlv)
498 return -ENOMEM;
499 tlv->numid = ctl->info->id.numid;
500 tlv->length = count;
501 memcpy(tlv->tlv, array, count);
502
503 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
504 free(tlv);
505
506 return ret;
507 } else {
508 size = sizeof(ev.value.bytes.data[0]);
509 dest = ev.value.bytes.data;
510 }
511 break;
512
513 case SNDRV_CTL_ELEM_TYPE_IEC958:
514 size = sizeof(ev.value.iec958);
515 dest = &ev.value.iec958;
516 break;
517
518 default:
519 return -EINVAL;
520 }
521
522 memcpy(dest, array, size * count);
523
524 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
525 }
526
mixer_ctl_get_range_min(struct mixer_ctl * ctl)527 int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
528 {
529 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
530 return -EINVAL;
531
532 return ctl->info->value.integer.min;
533 }
534
mixer_ctl_get_range_max(struct mixer_ctl * ctl)535 int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
536 {
537 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
538 return -EINVAL;
539
540 return ctl->info->value.integer.max;
541 }
542
mixer_ctl_get_num_enums(struct mixer_ctl * ctl)543 unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
544 {
545 if (!ctl)
546 return 0;
547
548 return ctl->info->value.enumerated.items;
549 }
550
mixer_ctl_get_enum_string(struct mixer_ctl * ctl,unsigned int enum_id)551 const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
552 unsigned int enum_id)
553 {
554 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
555 (enum_id >= ctl->info->value.enumerated.items))
556 return NULL;
557
558 return (const char *)ctl->ename[enum_id];
559 }
560
mixer_ctl_set_enum_by_string(struct mixer_ctl * ctl,const char * string)561 int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
562 {
563 unsigned int i, num_enums;
564 struct snd_ctl_elem_value ev;
565 int ret;
566
567 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
568 return -EINVAL;
569
570 num_enums = ctl->info->value.enumerated.items;
571 for (i = 0; i < num_enums; i++) {
572 if (!strcmp(string, ctl->ename[i])) {
573 memset(&ev, 0, sizeof(ev));
574 ev.value.enumerated.item[0] = i;
575 ev.id.numid = ctl->info->id.numid;
576 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
577 if (ret < 0)
578 return ret;
579 return 0;
580 }
581 }
582
583 return -EINVAL;
584 }
585
586 /** Subscribes for the mixer events.
587 * @param mixer A mixer handle.
588 * @param subscribe value indicating subscribe or unsubscribe for events
589 * @returns On success, zero.
590 * On failure, non-zero.
591 * @ingroup libtinyalsa-mixer
592 */
mixer_subscribe_events(struct mixer * mixer,int subscribe)593 int mixer_subscribe_events(struct mixer *mixer, int subscribe)
594 {
595 if (ioctl(mixer->fd, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0) {
596 return -1;
597 }
598 return 0;
599 }
600
601 /** Wait for mixer events.
602 * @param mixer A mixer handle.
603 * @param timeout timout value
604 * @returns On success, 1.
605 * On failure, -errno.
606 * On timeout, 0
607 * @ingroup libtinyalsa-mixer
608 */
mixer_wait_event(struct mixer * mixer,int timeout)609 int mixer_wait_event(struct mixer *mixer, int timeout)
610 {
611 struct pollfd pfd;
612
613 pfd.fd = mixer->fd;
614 pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
615
616 for (;;) {
617 int err;
618 err = poll(&pfd, 1, timeout);
619 if (err < 0)
620 return -errno;
621 if (!err)
622 return 0;
623 if (pfd.revents & (POLLERR | POLLNVAL))
624 return -EIO;
625 if (pfd.revents & (POLLIN | POLLOUT))
626 return 1;
627 }
628 }
629