Home | History | Annotate | Download | only in libalsa-intf
      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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    306 static void print_dB(long dB)
    307 {
    308         ALOGV("%li.%02lidB", dB / 100, (dB < 0 ? -dB : dB) % 100);
    309 }
    310 
    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 
    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 
    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 
    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  */
    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 
    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 
    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 
    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 
    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