• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 ** Copyright 2010, The Android Open-Source Project
3 ** Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
4 **
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 
37 #define LOG_TAG "alsa_mixer"
38 #define LOG_NDEBUG 1
39 
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 
62 enum ctl_type {
63 	CTL_GLOBAL_VOLUME,
64 	CTL_PLAYBACK_VOLUME,
65 	CTL_CAPTURE_VOLUME,
66 };
67 
68 static 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 
is_volume(const char * name,enum ctl_type * type)78 static 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 
elem_iface_name(snd_ctl_elem_iface_t n)99 static 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 
elem_type_name(snd_ctl_elem_type_t n)113 static 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 
mixer_close(struct mixer * mixer)127 void mixer_close(struct mixer *mixer)
128 {
129     unsigned n,m;
130 
131     if (mixer->fd >= 0)
132         close(mixer->fd);
133 
134     if (mixer->ctl) {
135         for (n = 0; n < mixer->count; n++) {
136             if (mixer->ctl[n].ename) {
137                 unsigned max = mixer->ctl[n].info->value.enumerated.items;
138                 for (m = 0; m < max; m++)
139                     free(mixer->ctl[n].ename[m]);
140                 free(mixer->ctl[n].ename);
141             }
142         }
143         free(mixer->ctl);
144     }
145 
146     if (mixer->info)
147         free(mixer->info);
148 
149     free(mixer);
150 }
151 
mixer_open(const char * device)152 struct mixer *mixer_open(const char *device)
153 {
154     struct snd_ctl_elem_list elist;
155     struct snd_ctl_elem_info tmp;
156     struct snd_ctl_elem_id *eid = NULL;
157     struct mixer *mixer = NULL;
158     unsigned n, m;
159     int fd;
160 
161     fd = open(device, O_RDWR);
162     if (fd < 0) {
163         ALOGE("Control open failed\n");
164         return 0;
165     }
166 
167     memset(&elist, 0, sizeof(elist));
168     if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) {
169         ALOGE("SNDRV_CTL_IOCTL_ELEM_LIST failed\n");
170         goto fail;
171     }
172 
173     mixer = calloc(1, sizeof(*mixer));
174     if (!mixer)
175         goto fail;
176 
177     mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
178     mixer->info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
179     if (!mixer->ctl || !mixer->info)
180         goto fail;
181 
182     eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
183     if (!eid)
184         goto fail;
185 
186     mixer->count = elist.count;
187     mixer->fd = fd;
188     elist.space = mixer->count;
189     elist.pids = eid;
190     if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
191         goto fail;
192 
193     for (n = 0; n < mixer->count; n++) {
194         struct snd_ctl_elem_info *ei = mixer->info + n;
195         ei->id.numid = eid[n].numid;
196         if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
197             goto fail;
198         mixer->ctl[n].info = ei;
199         mixer->ctl[n].mixer = mixer;
200         if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
201             char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
202             if (!enames)
203                 goto fail;
204             mixer->ctl[n].ename = enames;
205             for (m = 0; m < ei->value.enumerated.items; m++) {
206                 memset(&tmp, 0, sizeof(tmp));
207                 tmp.id.numid = ei->id.numid;
208                 tmp.value.enumerated.item = m;
209                 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
210                     goto fail;
211                 enames[m] = strdup(tmp.value.enumerated.name);
212                 if (!enames[m])
213                     goto fail;
214             }
215         }
216     }
217 
218     free(eid);
219     return mixer;
220 
221 fail:
222     if (eid)
223         free(eid);
224     if (mixer)
225         mixer_close(mixer);
226     else if (fd >= 0)
227         close(fd);
228     return 0;
229 }
230 
mixer_dump(struct mixer * mixer)231 void mixer_dump(struct mixer *mixer)
232 {
233     unsigned n, m;
234 
235     ALOGV("  id iface dev sub idx num perms     type   isvolume  name\n");
236     for (n = 0; n < mixer->count; n++) {
237 	enum ctl_type type;
238         struct snd_ctl_elem_info *ei = mixer->info + n;
239 
240         ALOGV("%4d %5s %3d %3d %3d %3d %c%c%c%c%c%c%c%c%c %-6s %8d  %s",
241                ei->id.numid, elem_iface_name(ei->id.iface),
242                ei->id.device, ei->id.subdevice, ei->id.index,
243                ei->count,
244                (ei->access & SNDRV_CTL_ELEM_ACCESS_READ) ? 'r' : ' ',
245                (ei->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ? 'w' : ' ',
246                (ei->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE) ? 'V' : ' ',
247                (ei->access & SNDRV_CTL_ELEM_ACCESS_TIMESTAMP) ? 'T' : ' ',
248                (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) ? 'R' : ' ',
249                (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) ? 'W' : ' ',
250                (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) ? 'C' : ' ',
251                (ei->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) ? 'I' : ' ',
252                (ei->access & SNDRV_CTL_ELEM_ACCESS_LOCK) ? 'L' : ' ',
253                elem_type_name(ei->type),
254 	       (is_volume(ei->id.name, &type)) ? 1 : 0,
255                ei->id.name);
256         switch (ei->type) {
257         case SNDRV_CTL_ELEM_TYPE_INTEGER:
258             ALOGV(ei->value.integer.step ?
259                    " { %ld-%ld, %ld }\n" : " { %ld-%ld }",
260                    ei->value.integer.min,
261                    ei->value.integer.max,
262                    ei->value.integer.step);
263             break;
264         case SNDRV_CTL_ELEM_TYPE_INTEGER64:
265             ALOGV(ei->value.integer64.step ?
266                    " { %lld-%lld, %lld }\n" : " { %lld-%lld }",
267                    ei->value.integer64.min,
268                    ei->value.integer64.max,
269                    ei->value.integer64.step);
270             break;
271         case SNDRV_CTL_ELEM_TYPE_ENUMERATED: {
272             unsigned m;
273             ALOGV(" { %s=0", mixer->ctl[n].ename[0]);
274             for (m = 1; m < ei->value.enumerated.items; m++)
275                 ALOGV(", %s=%d", mixer->ctl[n].ename[m],m);
276             ALOGV(" }");
277             break;
278         }
279         }
280         ALOGV("\n");
281     }
282 }
283 
mixer_get_control(struct mixer * mixer,const char * name,unsigned index)284 struct mixer_ctl *mixer_get_control(struct mixer *mixer,
285                                     const char *name, unsigned index)
286 {
287     unsigned n;
288     for (n = 0; n < mixer->count; n++) {
289         if (mixer->info[n].id.index == index) {
290             if (!strncmp(name, (char*) mixer->info[n].id.name,
291 			sizeof(mixer->info[n].id.name))) {
292                 return mixer->ctl + n;
293             }
294         }
295     }
296     return 0;
297 }
298 
mixer_get_nth_control(struct mixer * mixer,unsigned n)299 struct mixer_ctl *mixer_get_nth_control(struct mixer *mixer, unsigned n)
300 {
301     if (n < mixer->count)
302         return mixer->ctl + n;
303     return 0;
304 }
305 
print_dB(long dB)306 static void print_dB(long dB)
307 {
308         ALOGV("%li.%02lidB", dB / 100, (dB < 0 ? -dB : dB) % 100);
309 }
310 
mixer_ctl_read_tlv(struct mixer_ctl * ctl,unsigned int * tlv,long * min,long * max,unsigned int * tlv_type)311 int mixer_ctl_read_tlv(struct mixer_ctl *ctl,
312                     unsigned int *tlv,
313 		    long *min, long *max, unsigned int *tlv_type)
314 {
315     unsigned int tlv_size = DEFAULT_TLV_SIZE;
316     unsigned int type;
317     unsigned int size;
318 
319     if(!!(ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)) {
320         struct snd_ctl_tlv *xtlv;
321         tlv[0] = -1;
322         tlv[1] = 0;
323         xtlv = calloc(1, sizeof(struct snd_ctl_tlv) + tlv_size);
324         if (xtlv == NULL)
325                 return -ENOMEM;
326         xtlv->numid = ctl->info->id.numid;
327         xtlv->length = tlv_size;
328         memcpy(xtlv->tlv, tlv, tlv_size);
329         if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, xtlv) < 0) {
330             fprintf( stderr, "SNDRV_CTL_IOCTL_TLV_READ failed\n");
331             free(xtlv);
332             return -errno;
333         }
334         if (xtlv->tlv[1] + 2 * sizeof(unsigned int) > tlv_size) {
335             free(xtlv);
336             return -EFAULT;
337         }
338         memcpy(tlv, xtlv->tlv, xtlv->tlv[1] + 2 * sizeof(unsigned int));
339         free(xtlv);
340 
341         type = tlv[0];
342 	*tlv_type = type;
343         size = tlv[1];
344         switch (type) {
345         case SNDRV_CTL_TLVT_DB_SCALE: {
346                 int idx = 2;
347                 int step;
348                 ALOGV("dBscale-");
349                 if (size != 2 * sizeof(unsigned int)) {
350                         while (size > 0) {
351                                 ALOGV("0x%08x,", tlv[idx++]);
352                                 size -= sizeof(unsigned int);
353                         }
354                 } else {
355                     ALOGV(" min=");
356                     print_dB((int)tlv[2]);
357                     *min = (long)tlv[2];
358                     ALOGV(" step=");
359                     step = (tlv[3] & 0xffff);
360                     print_dB(tlv[3] & 0xffff);
361                     ALOGV(" max=");
362                     *max = (ctl->info->value.integer.max);
363                     print_dB((long)ctl->info->value.integer.max);
364                     ALOGV(" mute=%i\n", (tlv[3] >> 16) & 1);
365                 }
366             break;
367         }
368         case SNDRV_CTL_TLVT_DB_LINEAR: {
369                 int idx = 2;
370                 ALOGV("dBLiner-");
371                 if (size != 2 * sizeof(unsigned int)) {
372                         while (size > 0) {
373                                 ALOGV("0x%08x,", tlv[idx++]);
374                                 size -= sizeof(unsigned int);
375                         }
376                 } else {
377                     ALOGV(" min=");
378                     *min = tlv[2];
379                     print_dB(tlv[2]);
380                     ALOGV(" max=");
381                     *max = tlv[3];
382                     print_dB(tlv[3]);
383                 }
384             break;
385         }
386         default:
387              break;
388         }
389         return 0;
390     }
391     return -EINVAL;
392 }
393 
mixer_ctl_get(struct mixer_ctl * ctl,unsigned * value)394 void mixer_ctl_get(struct mixer_ctl *ctl, unsigned *value)
395 {
396     struct snd_ctl_elem_value ev;
397     unsigned int n;
398     unsigned int *tlv = NULL;
399     enum ctl_type type;
400     unsigned int *tlv_type;
401     long min, max;
402 
403     if (is_volume(ctl->info->id.name, &type)) {
404        ALOGV("capability: volume\n");
405        tlv = calloc(1, DEFAULT_TLV_SIZE);
406        if (tlv == NULL) {
407            ALOGE("failed to allocate memory\n");
408        } else {
409 	   mixer_ctl_read_tlv(ctl, tlv, &min, &max, &tlv_type);
410            free(tlv);
411        }
412     }
413 
414     memset(&ev, 0, sizeof(ev));
415     ev.id.numid = ctl->info->id.numid;
416     if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev))
417         return;
418     ALOGV("%s:", ctl->info->id.name);
419 
420     switch (ctl->info->type) {
421     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
422         for (n = 0; n < ctl->info->count; n++)
423             ALOGV(" %s", ev.value.integer.value[n] ? "on" : "off");
424         *value = ev.value.integer.value[0];
425         break;
426     case SNDRV_CTL_ELEM_TYPE_INTEGER: {
427         for (n = 0; n < ctl->info->count; n++)
428             ALOGV(" %ld", ev.value.integer.value[n]);
429         *value = ev.value.integer.value[0];
430         break;
431     }
432     case SNDRV_CTL_ELEM_TYPE_INTEGER64:
433         for (n = 0; n < ctl->info->count; n++)
434             ALOGV(" %lld", ev.value.integer64.value[n]);
435         *value = ev.value.integer64.value[0];
436         break;
437     case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
438         for (n = 0; n < ctl->info->count; n++) {
439             unsigned v = ev.value.enumerated.item[n];
440             ALOGV(" %d (%s)", v,
441                    (v < ctl->info->value.enumerated.items) ? ctl->ename[v] : "???");
442         *value = ev.value.enumerated.item[0];
443         }
444         break;
445     default:
446         ALOGV(" ???");
447     }
448     ALOGV("\n");
449 }
450 
scale_int(struct snd_ctl_elem_info * ei,unsigned _percent)451 static long scale_int(struct snd_ctl_elem_info *ei, unsigned _percent)
452 {
453     long percent;
454 
455     if (_percent > 100)
456         percent = 100;
457     else
458         percent = (long) _percent;
459 
460     return (long)percent_to_index(percent, ei->value.integer.min, ei->value.integer.max);
461 }
462 
scale_int64(struct snd_ctl_elem_info * ei,unsigned _percent)463 static long long scale_int64(struct snd_ctl_elem_info *ei, unsigned _percent)
464 {
465     long long percent;
466 
467     if (_percent > 100)
468         percent = 100;
469     else
470         percent = (long) _percent;
471 
472     return (long long)percent_to_index(percent, ei->value.integer.min, ei->value.integer.max);
473 }
474 
475 /*
476  * Add support for controls taking more than one parameter as input value
477  * This is useful for volume controls which take two parameters as input value.
478  */
mixer_ctl_mulvalues(struct mixer_ctl * ctl,int count,char ** argv)479 int mixer_ctl_mulvalues(struct mixer_ctl *ctl, int count, char ** argv)
480 {
481     struct snd_ctl_elem_value ev;
482     unsigned n;
483 
484     if (!ctl) {
485         ALOGV("can't find control\n");
486         return -1;
487     }
488     if (count < ctl->info->count || count > ctl->info->count)
489         return -EINVAL;
490 
491 
492     memset(&ev, 0, sizeof(ev));
493     ev.id.numid = ctl->info->id.numid;
494     switch (ctl->info->type) {
495     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
496         for (n = 0; n < ctl->info->count; n++)
497             ev.value.integer.value[n] = !!atoi(argv[n]);
498         break;
499     case SNDRV_CTL_ELEM_TYPE_INTEGER: {
500         for (n = 0; n < ctl->info->count; n++) {
501              fprintf( stderr, "Value: %d idx:%d\n", atoi(argv[n]), n);
502              ev.value.integer.value[n] = atoi(argv[n]);
503         }
504         break;
505     }
506     case SNDRV_CTL_ELEM_TYPE_INTEGER64: {
507         for (n = 0; n < ctl->info->count; n++) {
508              long long value_ll = scale_int64(ctl->info, atoi(argv[n]));
509              fprintf( stderr, "ll_value = %lld\n", value_ll);
510              ev.value.integer64.value[n] = value_ll;
511         }
512         break;
513     }
514     default:
515         errno = EINVAL;
516         return errno;
517     }
518 
519     return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
520 }
521 
mixer_ctl_set(struct mixer_ctl * ctl,unsigned percent)522 int mixer_ctl_set(struct mixer_ctl *ctl, unsigned percent)
523 {
524     struct snd_ctl_elem_value ev;
525     unsigned n;
526     long min, max;
527     unsigned int *tlv = NULL;
528     enum ctl_type type;
529     int volume = 0;
530     unsigned int tlv_type;
531 
532     if (!ctl) {
533         ALOGV("can't find control\n");
534         return -1;
535     }
536 
537     if (is_volume(ctl->info->id.name, &type)) {
538         ALOGV("capability: volume\n");
539         tlv = calloc(1, DEFAULT_TLV_SIZE);
540         if (tlv == NULL) {
541             ALOGE("failed to allocate memory\n");
542         } else if (!mixer_ctl_read_tlv(ctl, tlv, &min, &max, &tlv_type)) {
543             switch(tlv_type) {
544             case SNDRV_CTL_TLVT_DB_LINEAR:
545                 ALOGV("tlv db linear: b4 %d\n", percent);
546 
547 		if (min < 0) {
548 			max = max - min;
549 			min = 0;
550 		}
551                 percent = check_range(percent, min, max);
552                 ALOGV("tlv db linear: %d %d %d\n", percent, min, max);
553                 volume = 1;
554                 break;
555             default:
556                 percent = (long)percent_to_index(percent, min, max);
557                 percent = check_range(percent, min, max);
558                 volume = 1;
559                 break;
560             }
561         } else
562             ALOGV("mixer_ctl_read_tlv failed\n");
563         free(tlv);
564     }
565     memset(&ev, 0, sizeof(ev));
566     ev.id.numid = ctl->info->id.numid;
567     switch (ctl->info->type) {
568     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
569         for (n = 0; n < ctl->info->count; n++)
570             ev.value.integer.value[n] = !!percent;
571         break;
572     case SNDRV_CTL_ELEM_TYPE_INTEGER: {
573         int value;
574         if (!volume)
575              value = scale_int(ctl->info, percent);
576         else
577              value = (int) percent;
578         for (n = 0; n < ctl->info->count; n++)
579             ev.value.integer.value[n] = value;
580         break;
581     }
582     case SNDRV_CTL_ELEM_TYPE_INTEGER64: {
583         long long value;
584         if (!volume)
585              value = scale_int64(ctl->info, percent);
586         else
587              value = (long long)percent;
588         for (n = 0; n < ctl->info->count; n++)
589             ev.value.integer64.value[n] = value;
590         break;
591     }
592     case SNDRV_CTL_ELEM_TYPE_IEC958: {
593         struct snd_aes_iec958 *iec958;
594         iec958 = (struct snd_aes_iec958 *)percent;
595         memcpy(ev.value.iec958.status,iec958->status,SPDIF_CHANNEL_STATUS_SIZE);
596         break;
597     }
598     default:
599         errno = EINVAL;
600         return errno;
601     }
602 
603     return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
604 }
605 
606 /* the api parses the mixer control input to extract
607  * the value of volume in any one of the following format
608  * <volume><%>
609  * <volume><dB>
610  * All remaining formats are currently ignored.
611  */
612 
set_volume_simple(struct mixer_ctl * ctl,char ** ptr,long pmin,long pmax,int count)613 static int set_volume_simple(struct mixer_ctl *ctl,
614     char **ptr, long pmin, long pmax, int count)
615 {
616     long val, orig;
617     char *p = *ptr, *s;
618     struct snd_ctl_elem_value ev;
619     unsigned n;
620 
621     if (*p == ':')
622         p++;
623     if (*p == '\0' || (!isdigit(*p) && *p != '-'))
624         goto skip;
625 
626     s = p;
627     val = strtol(s, &p, 10);
628     if (*p == '.') {
629         p++;
630         strtol(p, &p, 10);
631     }
632     if (*p == '%') {
633         val = (long)percent_to_index(strtod(s, NULL), pmin, pmax);
634         p++;
635     } else if (p[0] == 'd' && p[1] == 'B') {
636         val = (long)(strtod(s, NULL) * 100.0);
637         p += 2;
638     } else {
639         if (pmin < 0) {
640             pmax = pmax - pmin;
641             pmin = 0;
642         }
643     }
644     val = check_range(val, pmin, pmax);
645     ALOGV("val = %x", val);
646 
647     if (!ctl) {
648         ALOGV("can't find control\n");
649         return -EPERM;
650     }
651     if (count < ctl->info->count || count > ctl->info->count)
652         return -EINVAL;
653 
654     ALOGV("Value = ");
655 
656     memset(&ev, 0, sizeof(ev));
657     ev.id.numid = ctl->info->id.numid;
658     switch (ctl->info->type) {
659     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
660         for (n = 0; n < ctl->info->count; n++)
661             ev.value.integer.value[n] = !!val;
662         print_dB(val);
663         break;
664     case SNDRV_CTL_ELEM_TYPE_INTEGER: {
665         for (n = 0; n < ctl->info->count; n++)
666              ev.value.integer.value[n] = val;
667         print_dB(val);
668         break;
669     }
670     case SNDRV_CTL_ELEM_TYPE_INTEGER64: {
671         for (n = 0; n < ctl->info->count; n++) {
672              long long value_ll = scale_int64(ctl->info, val);
673              print_dB(value_ll);
674              ev.value.integer64.value[n] = value_ll;
675         }
676         break;
677     }
678     default:
679         errno = EINVAL;
680         return errno;
681     }
682 
683     ALOGV("\n");
684     return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
685 
686 skip:
687         if (*p == ',')
688                 p++;
689         *ptr = p;
690         return 0;
691 }
692 
mixer_ctl_set_value(struct mixer_ctl * ctl,int count,char ** argv)693 int mixer_ctl_set_value(struct mixer_ctl *ctl, int count, char ** argv)
694 {
695     unsigned int size;
696     unsigned int *tlv = NULL;
697     long min, max;
698     enum ctl_type type;
699     unsigned int tlv_type;
700 
701     if (is_volume(ctl->info->id.name, &type)) {
702         ALOGV("capability: volume\n");
703         tlv = calloc(1, DEFAULT_TLV_SIZE);
704         if (tlv == NULL) {
705             ALOGE("failed to allocate memory\n");
706         } else if (!mixer_ctl_read_tlv(ctl, tlv, &min, &max, &tlv_type)) {
707             ALOGV("min = %x max = %x", min, max);
708             if (set_volume_simple(ctl, argv, min, max, count))
709                 mixer_ctl_mulvalues(ctl, count, argv);
710         } else
711             ALOGV("mixer_ctl_read_tlv failed\n");
712         free(tlv);
713     } else {
714         mixer_ctl_mulvalues(ctl, count, argv);
715     }
716     return 0;
717 }
718 
719 
mixer_ctl_select(struct mixer_ctl * ctl,const char * value)720 int mixer_ctl_select(struct mixer_ctl *ctl, const char *value)
721 {
722     unsigned n, max;
723     struct snd_ctl_elem_value ev;
724     unsigned int  input_str_len, str_len;
725 
726     if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
727         errno = EINVAL;
728         return -1;
729     }
730 
731     input_str_len =  strnlen(value,64);
732 
733     max = ctl->info->value.enumerated.items;
734     for (n = 0; n < max; n++) {
735 
736         str_len = strnlen(ctl->ename[n], 64);
737         if (str_len < input_str_len)
738             str_len = input_str_len;
739 
740         if (!strncmp(value, ctl->ename[n], str_len)) {
741             memset(&ev, 0, sizeof(ev));
742             ev.value.enumerated.item[0] = n;
743             ev.id.numid = ctl->info->id.numid;
744             if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev) < 0)
745                 return -1;
746             return 0;
747         }
748     }
749 
750     errno = EINVAL;
751     return errno;
752 }
753