blob: 5fd1839212c6dd191b8ceed14dabec2bebf95eb4 [file] [log] [blame]
Iliyan Malchev4765c432012-06-11 14:36:16 -07001/*
2** Copyright 2010, The Android Open-Source Project
Subhash Chandra Bose Naripeddy6000eb52013-01-05 18:02:44 -08003** Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
Iliyan Malchev4765c432012-06-11 14:36:16 -07004**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <unistd.h>
22#include <stdint.h>
23#include <fcntl.h>
24#include <errno.h>
25#include <ctype.h>
26#include <math.h>
27
28#include <linux/ioctl.h>
29#define __force
30#define __bitwise
31#define __user
32#include <sound/asound.h>
33#include <sound/tlv.h>
34
35#include "alsa_audio.h"
36
Ajay Dudani9746c472012-06-18 16:01:16 -070037#define LOG_TAG "alsa_mixer"
38#define LOG_NDEBUG 1
Iliyan Malchev4765c432012-06-11 14:36:16 -070039
40#ifdef ANDROID
41/* definitions for Android logging */
42#include <utils/Log.h>
43#else /* ANDROID */
44#include <math.h>
45#define ALOGI(...) fprintf(stdout, __VA_ARGS__)
46#define ALOGE(...) fprintf(stderr, __VA_ARGS__)
47#define ALOGV(...) fprintf(stderr, __VA_ARGS__)
48#endif /* ANDROID */
49
50#define check_range(val, min, max) \
51 (((val < min) ? (min) : (val > max) ? (max) : (val)))
52
53/* .5 for rounding before casting to non-decmal value */
54/* Should not be used if you need decmal values */
55/* or are expecting negitive indexes */
56#define percent_to_index(val, min, max) \
57 ((val) * ((max) - (min)) * 0.01 + (min) + .5)
58
59#define DEFAULT_TLV_SIZE 4096
60#define SPDIF_CHANNEL_STATUS_SIZE 24
61
62enum ctl_type {
63 CTL_GLOBAL_VOLUME,
64 CTL_PLAYBACK_VOLUME,
65 CTL_CAPTURE_VOLUME,
66};
67
68static const struct suf {
69 const char *suffix;
70 snd_ctl_elem_iface_t type;
71} suffixes[] = {
72 {" Playback Volume", CTL_PLAYBACK_VOLUME},
73 {" Capture Volume", CTL_CAPTURE_VOLUME},
74 {" Volume", CTL_GLOBAL_VOLUME},
75 {NULL, 0}
76};
77
78static int is_volume(const char *name, enum ctl_type *type)
79{
80 const struct suf *p;
81 size_t nlen = strlen(name);
82 p = suffixes;
83 while (p->suffix) {
84 size_t slen = strnlen(p->suffix, 44);
85 size_t l;
86 if (nlen > slen) {
87 l = nlen - slen;
88 if (strncmp(name + l, p->suffix, slen) == 0 &&
89 (l < 1 || name[l-1] != '-')) { /* 3D Control - Switch */
90 *type = p->type;
91 return l;
92 }
93 }
94 p++;
95 }
96 return 0;
97}
98
99static const char *elem_iface_name(snd_ctl_elem_iface_t n)
100{
101 switch (n) {
102 case SNDRV_CTL_ELEM_IFACE_CARD: return "CARD";
103 case SNDRV_CTL_ELEM_IFACE_HWDEP: return "HWDEP";
104 case SNDRV_CTL_ELEM_IFACE_MIXER: return "MIXER";
105 case SNDRV_CTL_ELEM_IFACE_PCM: return "PCM";
106 case SNDRV_CTL_ELEM_IFACE_RAWMIDI: return "MIDI";
107 case SNDRV_CTL_ELEM_IFACE_TIMER: return "TIMER";
108 case SNDRV_CTL_ELEM_IFACE_SEQUENCER: return "SEQ";
109 default: return "???";
110 }
111}
112
113static const char *elem_type_name(snd_ctl_elem_type_t n)
114{
115 switch (n) {
116 case SNDRV_CTL_ELEM_TYPE_NONE: return "NONE";
117 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
118 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT32";
119 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
120 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTES";
121 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
122 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
123 default: return "???";
124 }
125}
126
127void mixer_close(struct mixer *mixer)
128{
129 unsigned n,m;
130
Shreyas Nagasandra Chandrasekhar7a4d50e2013-08-02 16:32:48 +0530131 if (!mixer) {
132 ALOGE("mixer_close:Invalid Mixer Control");
133 return;
134 }
Iliyan Malchev4765c432012-06-11 14:36:16 -0700135 if (mixer->fd >= 0)
136 close(mixer->fd);
137
138 if (mixer->ctl) {
139 for (n = 0; n < mixer->count; n++) {
140 if (mixer->ctl[n].ename) {
141 unsigned max = mixer->ctl[n].info->value.enumerated.items;
142 for (m = 0; m < max; m++)
143 free(mixer->ctl[n].ename[m]);
144 free(mixer->ctl[n].ename);
145 }
146 }
147 free(mixer->ctl);
148 }
149
150 if (mixer->info)
151 free(mixer->info);
152
153 free(mixer);
154}
155
156struct mixer *mixer_open(const char *device)
157{
158 struct snd_ctl_elem_list elist;
159 struct snd_ctl_elem_info tmp;
160 struct snd_ctl_elem_id *eid = NULL;
161 struct mixer *mixer = NULL;
162 unsigned n, m;
163 int fd;
164
165 fd = open(device, O_RDWR);
166 if (fd < 0) {
167 ALOGE("Control open failed\n");
168 return 0;
169 }
170
171 memset(&elist, 0, sizeof(elist));
172 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) {
173 ALOGE("SNDRV_CTL_IOCTL_ELEM_LIST failed\n");
174 goto fail;
175 }
176
177 mixer = calloc(1, sizeof(*mixer));
178 if (!mixer)
179 goto fail;
180
181 mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
182 mixer->info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
183 if (!mixer->ctl || !mixer->info)
184 goto fail;
185
186 eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
187 if (!eid)
188 goto fail;
189
190 mixer->count = elist.count;
191 mixer->fd = fd;
192 elist.space = mixer->count;
193 elist.pids = eid;
194 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
195 goto fail;
196
197 for (n = 0; n < mixer->count; n++) {
198 struct snd_ctl_elem_info *ei = mixer->info + n;
199 ei->id.numid = eid[n].numid;
200 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
201 goto fail;
202 mixer->ctl[n].info = ei;
203 mixer->ctl[n].mixer = mixer;
204 if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
205 char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
206 if (!enames)
207 goto fail;
208 mixer->ctl[n].ename = enames;
209 for (m = 0; m < ei->value.enumerated.items; m++) {
210 memset(&tmp, 0, sizeof(tmp));
211 tmp.id.numid = ei->id.numid;
212 tmp.value.enumerated.item = m;
213 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
214 goto fail;
215 enames[m] = strdup(tmp.value.enumerated.name);
216 if (!enames[m])
217 goto fail;
218 }
219 }
220 }
221
222 free(eid);
223 return mixer;
224
225fail:
226 if (eid)
227 free(eid);
228 if (mixer)
229 mixer_close(mixer);
230 else if (fd >= 0)
231 close(fd);
232 return 0;
233}
234
235void mixer_dump(struct mixer *mixer)
236{
237 unsigned n, m;
238
239 ALOGV(" id iface dev sub idx num perms type isvolume name\n");
240 for (n = 0; n < mixer->count; n++) {
241 enum ctl_type type;
242 struct snd_ctl_elem_info *ei = mixer->info + n;
243
244 ALOGV("%4d %5s %3d %3d %3d %3d %c%c%c%c%c%c%c%c%c %-6s %8d %s",
245 ei->id.numid, elem_iface_name(ei->id.iface),
246 ei->id.device, ei->id.subdevice, ei->id.index,
247 ei->count,
248 (ei->access & SNDRV_CTL_ELEM_ACCESS_READ) ? 'r' : ' ',
249 (ei->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ? 'w' : ' ',
250 (ei->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE) ? 'V' : ' ',
251 (ei->access & SNDRV_CTL_ELEM_ACCESS_TIMESTAMP) ? 'T' : ' ',
252 (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) ? 'R' : ' ',
253 (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) ? 'W' : ' ',
254 (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) ? 'C' : ' ',
255 (ei->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) ? 'I' : ' ',
256 (ei->access & SNDRV_CTL_ELEM_ACCESS_LOCK) ? 'L' : ' ',
257 elem_type_name(ei->type),
258 (is_volume(ei->id.name, &type)) ? 1 : 0,
259 ei->id.name);
260 switch (ei->type) {
261 case SNDRV_CTL_ELEM_TYPE_INTEGER:
262 ALOGV(ei->value.integer.step ?
263 " { %ld-%ld, %ld }\n" : " { %ld-%ld }",
264 ei->value.integer.min,
265 ei->value.integer.max,
266 ei->value.integer.step);
267 break;
268 case SNDRV_CTL_ELEM_TYPE_INTEGER64:
269 ALOGV(ei->value.integer64.step ?
270 " { %lld-%lld, %lld }\n" : " { %lld-%lld }",
271 ei->value.integer64.min,
272 ei->value.integer64.max,
273 ei->value.integer64.step);
274 break;
275 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: {
276 unsigned m;
277 ALOGV(" { %s=0", mixer->ctl[n].ename[0]);
278 for (m = 1; m < ei->value.enumerated.items; m++)
279 ALOGV(", %s=%d", mixer->ctl[n].ename[m],m);
280 ALOGV(" }");
281 break;
282 }
283 }
284 ALOGV("\n");
285 }
286}
287
288struct mixer_ctl *mixer_get_control(struct mixer *mixer,
289 const char *name, unsigned index)
290{
291 unsigned n;
292 for (n = 0; n < mixer->count; n++) {
293 if (mixer->info[n].id.index == index) {
294 if (!strncmp(name, (char*) mixer->info[n].id.name,
295 sizeof(mixer->info[n].id.name))) {
296 return mixer->ctl + n;
297 }
298 }
299 }
300 return 0;
301}
302
303struct mixer_ctl *mixer_get_nth_control(struct mixer *mixer, unsigned n)
304{
305 if (n < mixer->count)
306 return mixer->ctl + n;
307 return 0;
308}
309
310static void print_dB(long dB)
311{
312 ALOGV("%li.%02lidB", dB / 100, (dB < 0 ? -dB : dB) % 100);
313}
314
315int mixer_ctl_read_tlv(struct mixer_ctl *ctl,
316 unsigned int *tlv,
317 long *min, long *max, unsigned int *tlv_type)
318{
319 unsigned int tlv_size = DEFAULT_TLV_SIZE;
320 unsigned int type;
321 unsigned int size;
322
323 if(!!(ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)) {
324 struct snd_ctl_tlv *xtlv;
325 tlv[0] = -1;
326 tlv[1] = 0;
327 xtlv = calloc(1, sizeof(struct snd_ctl_tlv) + tlv_size);
328 if (xtlv == NULL)
329 return -ENOMEM;
330 xtlv->numid = ctl->info->id.numid;
331 xtlv->length = tlv_size;
332 memcpy(xtlv->tlv, tlv, tlv_size);
333 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, xtlv) < 0) {
334 fprintf( stderr, "SNDRV_CTL_IOCTL_TLV_READ failed\n");
335 free(xtlv);
336 return -errno;
337 }
338 if (xtlv->tlv[1] + 2 * sizeof(unsigned int) > tlv_size) {
339 free(xtlv);
340 return -EFAULT;
341 }
342 memcpy(tlv, xtlv->tlv, xtlv->tlv[1] + 2 * sizeof(unsigned int));
343 free(xtlv);
344
345 type = tlv[0];
346 *tlv_type = type;
347 size = tlv[1];
348 switch (type) {
349 case SNDRV_CTL_TLVT_DB_SCALE: {
350 int idx = 2;
351 int step;
352 ALOGV("dBscale-");
353 if (size != 2 * sizeof(unsigned int)) {
354 while (size > 0) {
355 ALOGV("0x%08x,", tlv[idx++]);
356 size -= sizeof(unsigned int);
357 }
358 } else {
359 ALOGV(" min=");
360 print_dB((int)tlv[2]);
361 *min = (long)tlv[2];
362 ALOGV(" step=");
363 step = (tlv[3] & 0xffff);
364 print_dB(tlv[3] & 0xffff);
365 ALOGV(" max=");
366 *max = (ctl->info->value.integer.max);
367 print_dB((long)ctl->info->value.integer.max);
368 ALOGV(" mute=%i\n", (tlv[3] >> 16) & 1);
369 }
370 break;
371 }
Deepa Madiregamacbb2dc02013-05-01 13:10:29 +0530372 case SNDRV_CTL_TLVT_DB_MINMAX:
Iliyan Malchev4765c432012-06-11 14:36:16 -0700373 case SNDRV_CTL_TLVT_DB_LINEAR: {
374 int idx = 2;
Deepa Madiregamacbb2dc02013-05-01 13:10:29 +0530375 ALOGV("dBLiner-/dbminmax");
Iliyan Malchev4765c432012-06-11 14:36:16 -0700376 if (size != 2 * sizeof(unsigned int)) {
377 while (size > 0) {
378 ALOGV("0x%08x,", tlv[idx++]);
379 size -= sizeof(unsigned int);
380 }
381 } else {
382 ALOGV(" min=");
383 *min = tlv[2];
384 print_dB(tlv[2]);
385 ALOGV(" max=");
386 *max = tlv[3];
387 print_dB(tlv[3]);
388 }
389 break;
390 }
391 default:
392 break;
393 }
394 return 0;
395 }
396 return -EINVAL;
397}
398
399void mixer_ctl_get(struct mixer_ctl *ctl, unsigned *value)
400{
401 struct snd_ctl_elem_value ev;
402 unsigned int n;
403 unsigned int *tlv = NULL;
404 enum ctl_type type;
405 unsigned int *tlv_type;
406 long min, max;
407
408 if (is_volume(ctl->info->id.name, &type)) {
409 ALOGV("capability: volume\n");
410 tlv = calloc(1, DEFAULT_TLV_SIZE);
411 if (tlv == NULL) {
412 ALOGE("failed to allocate memory\n");
413 } else {
414 mixer_ctl_read_tlv(ctl, tlv, &min, &max, &tlv_type);
415 free(tlv);
416 }
417 }
418
419 memset(&ev, 0, sizeof(ev));
420 ev.id.numid = ctl->info->id.numid;
421 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev))
422 return;
423 ALOGV("%s:", ctl->info->id.name);
424
425 switch (ctl->info->type) {
426 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
427 for (n = 0; n < ctl->info->count; n++)
428 ALOGV(" %s", ev.value.integer.value[n] ? "on" : "off");
429 *value = ev.value.integer.value[0];
430 break;
431 case SNDRV_CTL_ELEM_TYPE_INTEGER: {
432 for (n = 0; n < ctl->info->count; n++)
433 ALOGV(" %ld", ev.value.integer.value[n]);
434 *value = ev.value.integer.value[0];
435 break;
436 }
437 case SNDRV_CTL_ELEM_TYPE_INTEGER64:
438 for (n = 0; n < ctl->info->count; n++)
439 ALOGV(" %lld", ev.value.integer64.value[n]);
440 *value = ev.value.integer64.value[0];
441 break;
442 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
443 for (n = 0; n < ctl->info->count; n++) {
444 unsigned v = ev.value.enumerated.item[n];
445 ALOGV(" %d (%s)", v,
446 (v < ctl->info->value.enumerated.items) ? ctl->ename[v] : "???");
447 *value = ev.value.enumerated.item[0];
448 }
449 break;
450 default:
451 ALOGV(" ???");
452 }
453 ALOGV("\n");
454}
455
Subhash Chandra Bose Naripeddy6000eb52013-01-05 18:02:44 -0800456void mixer_ctl_get_mulvalues(struct mixer_ctl *ctl, unsigned **value, unsigned *count)
457{
458 struct snd_ctl_elem_value ev;
459 unsigned int n;
460
461 memset(&ev, 0, sizeof(ev));
462 ev.id.numid = ctl->info->id.numid;
463 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev))
464 return;
465 ALOGV("%s:", ctl->info->id.name);
466
467 switch (ctl->info->type) {
468 case SNDRV_CTL_ELEM_TYPE_BYTES:
469 for (n = 0; n < ctl->info->count; n++)
470 ALOGV(" %ld", ev.value.bytes.data[n]);
471 for (n = 0; n < ctl->info->count; n++)
472 *(value[n]) = ev.value.bytes.data[n];
473 *count = ctl->info->count;
474 break;
475 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
476 for (n = 0; n < ctl->info->count; n++)
477 ALOGV(" %s", ev.value.integer.value[n] ? "on" : "off");
478 for (n=0; n < ctl->info->count; n++)
479 *(value[n]) = ev.value.integer.value[n];
480 *count = ctl->info->count;
481 break;
482 case SNDRV_CTL_ELEM_TYPE_INTEGER: {
483 for (n = 0; n < ctl->info->count; n++)
484 ALOGV(" %ld", ev.value.integer.value[n]);
485 for (n = 0; n < ctl->info->count; n++)
486 *(value[n]) = ev.value.integer.value[n];
487 *count = ctl->info->count;
488 break;
489 }
490 case SNDRV_CTL_ELEM_TYPE_INTEGER64:
491 for (n = 0; n < ctl->info->count; n++)
492 ALOGV(" %lld", ev.value.integer64.value[n]);
493 for (n = 0; n < ctl->info->count; n++)
494 *(value[n]) = ev.value.integer64.value[n];
495 *count = ctl->info->count;
496 break;
497 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
498 for (n = 0; n < ctl->info->count; n++) {
499 unsigned v = ev.value.enumerated.item[n];
500 ALOGV(" %d (%s)", v,
501 (v < ctl->info->value.enumerated.items) ? ctl->ename[v] : "???");
502 }
503 for (n = 0; n < ctl->info->count; n++)
504 *(value[n]) = ev.value.enumerated.item[n];
505 *count = ctl->info->count;
506 break;
507 default:
508 *count = 0;
509 ALOGV(" ???");
510 }
511 ALOGV("\n");
512}
513
Iliyan Malchev4765c432012-06-11 14:36:16 -0700514static long scale_int(struct snd_ctl_elem_info *ei, unsigned _percent)
515{
516 long percent;
517
518 if (_percent > 100)
519 percent = 100;
520 else
521 percent = (long) _percent;
522
523 return (long)percent_to_index(percent, ei->value.integer.min, ei->value.integer.max);
524}
525
526static long long scale_int64(struct snd_ctl_elem_info *ei, unsigned _percent)
527{
528 long long percent;
529
530 if (_percent > 100)
531 percent = 100;
532 else
533 percent = (long) _percent;
534
535 return (long long)percent_to_index(percent, ei->value.integer.min, ei->value.integer.max);
536}
537
538/*
539 * Add support for controls taking more than one parameter as input value
540 * This is useful for volume controls which take two parameters as input value.
541 */
542int mixer_ctl_mulvalues(struct mixer_ctl *ctl, int count, char ** argv)
543{
544 struct snd_ctl_elem_value ev;
545 unsigned n;
546
547 if (!ctl) {
548 ALOGV("can't find control\n");
549 return -1;
550 }
551 if (count < ctl->info->count || count > ctl->info->count)
552 return -EINVAL;
553
Iliyan Malchev4765c432012-06-11 14:36:16 -0700554 memset(&ev, 0, sizeof(ev));
555 ev.id.numid = ctl->info->id.numid;
556 switch (ctl->info->type) {
557 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
558 for (n = 0; n < ctl->info->count; n++)
559 ev.value.integer.value[n] = !!atoi(argv[n]);
560 break;
561 case SNDRV_CTL_ELEM_TYPE_INTEGER: {
562 for (n = 0; n < ctl->info->count; n++) {
563 fprintf( stderr, "Value: %d idx:%d\n", atoi(argv[n]), n);
564 ev.value.integer.value[n] = atoi(argv[n]);
565 }
566 break;
567 }
568 case SNDRV_CTL_ELEM_TYPE_INTEGER64: {
569 for (n = 0; n < ctl->info->count; n++) {
570 long long value_ll = scale_int64(ctl->info, atoi(argv[n]));
571 fprintf( stderr, "ll_value = %lld\n", value_ll);
572 ev.value.integer64.value[n] = value_ll;
573 }
574 break;
575 }
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800576 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: {
577 for (n = 0; n < ctl->info->count; n++) {
578 fprintf( stderr, "Value: %d idx:%d\n", atoi(argv[n]), n);
579 ev.value.enumerated.item[n] = (unsigned int)atoi(argv[n]);
580 }
581 break;
582 }
Iliyan Malchev4765c432012-06-11 14:36:16 -0700583 default:
584 errno = EINVAL;
585 return errno;
586 }
587
588 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
589}
590
591int mixer_ctl_set(struct mixer_ctl *ctl, unsigned percent)
592{
593 struct snd_ctl_elem_value ev;
594 unsigned n;
595 long min, max;
596 unsigned int *tlv = NULL;
597 enum ctl_type type;
598 int volume = 0;
599 unsigned int tlv_type;
600
601 if (!ctl) {
602 ALOGV("can't find control\n");
603 return -1;
604 }
605
606 if (is_volume(ctl->info->id.name, &type)) {
607 ALOGV("capability: volume\n");
608 tlv = calloc(1, DEFAULT_TLV_SIZE);
609 if (tlv == NULL) {
610 ALOGE("failed to allocate memory\n");
611 } else if (!mixer_ctl_read_tlv(ctl, tlv, &min, &max, &tlv_type)) {
612 switch(tlv_type) {
613 case SNDRV_CTL_TLVT_DB_LINEAR:
Deepa Madiregamacbb2dc02013-05-01 13:10:29 +0530614 case SNDRV_CTL_TLVT_DB_MINMAX:
615 ALOGV("tlv db linear/db minmax: b4 %d\n", percent);
Iliyan Malchev4765c432012-06-11 14:36:16 -0700616
617 if (min < 0) {
618 max = max - min;
619 min = 0;
620 }
621 percent = check_range(percent, min, max);
622 ALOGV("tlv db linear: %d %d %d\n", percent, min, max);
623 volume = 1;
624 break;
625 default:
626 percent = (long)percent_to_index(percent, min, max);
627 percent = check_range(percent, min, max);
628 volume = 1;
629 break;
630 }
631 } else
632 ALOGV("mixer_ctl_read_tlv failed\n");
633 free(tlv);
634 }
635 memset(&ev, 0, sizeof(ev));
636 ev.id.numid = ctl->info->id.numid;
637 switch (ctl->info->type) {
638 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
639 for (n = 0; n < ctl->info->count; n++)
640 ev.value.integer.value[n] = !!percent;
641 break;
642 case SNDRV_CTL_ELEM_TYPE_INTEGER: {
643 int value;
644 if (!volume)
645 value = scale_int(ctl->info, percent);
646 else
647 value = (int) percent;
648 for (n = 0; n < ctl->info->count; n++)
649 ev.value.integer.value[n] = value;
650 break;
651 }
652 case SNDRV_CTL_ELEM_TYPE_INTEGER64: {
653 long long value;
654 if (!volume)
655 value = scale_int64(ctl->info, percent);
656 else
657 value = (long long)percent;
658 for (n = 0; n < ctl->info->count; n++)
659 ev.value.integer64.value[n] = value;
660 break;
661 }
662 case SNDRV_CTL_ELEM_TYPE_IEC958: {
663 struct snd_aes_iec958 *iec958;
664 iec958 = (struct snd_aes_iec958 *)percent;
665 memcpy(ev.value.iec958.status,iec958->status,SPDIF_CHANNEL_STATUS_SIZE);
666 break;
667 }
Damir Didjustoeae2d492013-02-12 17:56:38 -0800668 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: {
669 for (n = 0; n < ctl->info->count; n++) {
670 ev.value.enumerated.item[n] = (unsigned int)percent;
671 }
672 break;
673 }
Iliyan Malchev4765c432012-06-11 14:36:16 -0700674 default:
675 errno = EINVAL;
676 return errno;
677 }
678
679 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
680}
681
682/* the api parses the mixer control input to extract
683 * the value of volume in any one of the following format
684 * <volume><%>
685 * <volume><dB>
686 * All remaining formats are currently ignored.
687 */
688
689static int set_volume_simple(struct mixer_ctl *ctl,
690 char **ptr, long pmin, long pmax, int count)
691{
692 long val, orig;
693 char *p = *ptr, *s;
694 struct snd_ctl_elem_value ev;
695 unsigned n;
696
697 if (*p == ':')
698 p++;
699 if (*p == '\0' || (!isdigit(*p) && *p != '-'))
700 goto skip;
701
702 s = p;
703 val = strtol(s, &p, 10);
704 if (*p == '.') {
705 p++;
706 strtol(p, &p, 10);
707 }
708 if (*p == '%') {
709 val = (long)percent_to_index(strtod(s, NULL), pmin, pmax);
710 p++;
711 } else if (p[0] == 'd' && p[1] == 'B') {
712 val = (long)(strtod(s, NULL) * 100.0);
713 p += 2;
714 } else {
715 if (pmin < 0) {
716 pmax = pmax - pmin;
717 pmin = 0;
718 }
719 }
720 val = check_range(val, pmin, pmax);
721 ALOGV("val = %x", val);
722
723 if (!ctl) {
724 ALOGV("can't find control\n");
725 return -EPERM;
726 }
727 if (count < ctl->info->count || count > ctl->info->count)
728 return -EINVAL;
729
730 ALOGV("Value = ");
731
732 memset(&ev, 0, sizeof(ev));
733 ev.id.numid = ctl->info->id.numid;
734 switch (ctl->info->type) {
735 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
736 for (n = 0; n < ctl->info->count; n++)
737 ev.value.integer.value[n] = !!val;
738 print_dB(val);
739 break;
740 case SNDRV_CTL_ELEM_TYPE_INTEGER: {
741 for (n = 0; n < ctl->info->count; n++)
742 ev.value.integer.value[n] = val;
743 print_dB(val);
744 break;
745 }
746 case SNDRV_CTL_ELEM_TYPE_INTEGER64: {
747 for (n = 0; n < ctl->info->count; n++) {
748 long long value_ll = scale_int64(ctl->info, val);
749 print_dB(value_ll);
750 ev.value.integer64.value[n] = value_ll;
751 }
752 break;
753 }
754 default:
755 errno = EINVAL;
756 return errno;
757 }
758
759 ALOGV("\n");
760 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
761
762skip:
763 if (*p == ',')
764 p++;
765 *ptr = p;
766 return 0;
767}
768
769int mixer_ctl_set_value(struct mixer_ctl *ctl, int count, char ** argv)
770{
771 unsigned int size;
772 unsigned int *tlv = NULL;
773 long min, max;
774 enum ctl_type type;
775 unsigned int tlv_type;
776
777 if (is_volume(ctl->info->id.name, &type)) {
778 ALOGV("capability: volume\n");
779 tlv = calloc(1, DEFAULT_TLV_SIZE);
780 if (tlv == NULL) {
781 ALOGE("failed to allocate memory\n");
782 } else if (!mixer_ctl_read_tlv(ctl, tlv, &min, &max, &tlv_type)) {
783 ALOGV("min = %x max = %x", min, max);
784 if (set_volume_simple(ctl, argv, min, max, count))
785 mixer_ctl_mulvalues(ctl, count, argv);
786 } else
787 ALOGV("mixer_ctl_read_tlv failed\n");
788 free(tlv);
789 } else {
790 mixer_ctl_mulvalues(ctl, count, argv);
791 }
792 return 0;
793}
794
795
796int mixer_ctl_select(struct mixer_ctl *ctl, const char *value)
797{
798 unsigned n, max;
799 struct snd_ctl_elem_value ev;
800 unsigned int input_str_len, str_len;
801
802 if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
803 errno = EINVAL;
804 return -1;
805 }
806
807 input_str_len = strnlen(value,64);
808
809 max = ctl->info->value.enumerated.items;
810 for (n = 0; n < max; n++) {
811
812 str_len = strnlen(ctl->ename[n], 64);
813 if (str_len < input_str_len)
814 str_len = input_str_len;
815
816 if (!strncmp(value, ctl->ename[n], str_len)) {
817 memset(&ev, 0, sizeof(ev));
818 ev.value.enumerated.item[0] = n;
819 ev.id.numid = ctl->info->id.numid;
820 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev) < 0)
821 return -1;
822 return 0;
823 }
824 }
825
826 errno = EINVAL;
827 return errno;
828}