Home | History | Annotate | Download | only in tinyalsa
      1 /* mixer.c
      2 **
      3 ** Copyright 2011, The Android Open Source Project
      4 **
      5 ** Redistribution and use in source and binary forms, with or without
      6 ** modification, are permitted provided that the following conditions are met:
      7 **     * Redistributions of source code must retain the above copyright
      8 **       notice, this list of conditions and the following disclaimer.
      9 **     * Redistributions in binary form must reproduce the above copyright
     10 **       notice, this list of conditions and the following disclaimer in the
     11 **       documentation and/or other materials provided with the distribution.
     12 **     * Neither the name of The Android Open Source Project nor the names of
     13 **       its contributors may be used to endorse or promote products derived
     14 **       from this software without specific prior written permission.
     15 **
     16 ** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
     17 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19 ** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
     20 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     22 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     23 ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
     26 ** DAMAGE.
     27 */
     28 
     29 #include <stdio.h>
     30 #include <stdlib.h>
     31 #include <stdint.h>
     32 #include <string.h>
     33 #include <unistd.h>
     34 #include <fcntl.h>
     35 #include <errno.h>
     36 #include <ctype.h>
     37 
     38 #include <sys/ioctl.h>
     39 
     40 #include <linux/ioctl.h>
     41 #define __force
     42 #define __bitwise
     43 #define __user
     44 #include <sound/asound.h>
     45 
     46 #include <tinyalsa/asoundlib.h>
     47 
     48 struct mixer_ctl {
     49     struct mixer *mixer;
     50     struct snd_ctl_elem_info *info;
     51     char **ename;
     52 };
     53 
     54 struct mixer {
     55     int fd;
     56     struct snd_ctl_card_info card_info;
     57     struct snd_ctl_elem_info *elem_info;
     58     struct mixer_ctl *ctl;
     59     unsigned int count;
     60 };
     61 
     62 void mixer_close(struct mixer *mixer)
     63 {
     64     unsigned int n,m;
     65 
     66     if (!mixer)
     67         return;
     68 
     69     if (mixer->fd >= 0)
     70         close(mixer->fd);
     71 
     72     if (mixer->ctl) {
     73         for (n = 0; n < mixer->count; n++) {
     74             if (mixer->ctl[n].ename) {
     75                 unsigned int max = mixer->ctl[n].info->value.enumerated.items;
     76                 for (m = 0; m < max; m++)
     77                     free(mixer->ctl[n].ename[m]);
     78                 free(mixer->ctl[n].ename);
     79             }
     80         }
     81         free(mixer->ctl);
     82     }
     83 
     84     if (mixer->elem_info)
     85         free(mixer->elem_info);
     86 
     87     free(mixer);
     88 
     89     /* TODO: verify frees */
     90 }
     91 
     92 struct mixer *mixer_open(unsigned int card)
     93 {
     94     struct snd_ctl_elem_list elist;
     95     struct snd_ctl_elem_info tmp;
     96     struct snd_ctl_elem_id *eid = NULL;
     97     struct mixer *mixer = NULL;
     98     unsigned int n, m;
     99     int fd;
    100     char fn[256];
    101 
    102     snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
    103     fd = open(fn, O_RDWR);
    104     if (fd < 0)
    105         return 0;
    106 
    107     memset(&elist, 0, sizeof(elist));
    108     if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
    109         goto fail;
    110 
    111     mixer = calloc(1, sizeof(*mixer));
    112     if (!mixer)
    113         goto fail;
    114 
    115     mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
    116     mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
    117     if (!mixer->ctl || !mixer->elem_info)
    118         goto fail;
    119 
    120     if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
    121         goto fail;
    122 
    123     eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
    124     if (!eid)
    125         goto fail;
    126 
    127     mixer->count = elist.count;
    128     mixer->fd = fd;
    129     elist.space = mixer->count;
    130     elist.pids = eid;
    131     if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
    132         goto fail;
    133 
    134     for (n = 0; n < mixer->count; n++) {
    135         struct snd_ctl_elem_info *ei = mixer->elem_info + n;
    136         ei->id.numid = eid[n].numid;
    137         if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
    138             goto fail;
    139         mixer->ctl[n].info = ei;
    140         mixer->ctl[n].mixer = mixer;
    141         if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
    142             char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
    143             if (!enames)
    144                 goto fail;
    145             mixer->ctl[n].ename = enames;
    146             for (m = 0; m < ei->value.enumerated.items; m++) {
    147                 memset(&tmp, 0, sizeof(tmp));
    148                 tmp.id.numid = ei->id.numid;
    149                 tmp.value.enumerated.item = m;
    150                 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
    151                     goto fail;
    152                 enames[m] = strdup(tmp.value.enumerated.name);
    153                 if (!enames[m])
    154                     goto fail;
    155             }
    156         }
    157     }
    158 
    159     free(eid);
    160     return mixer;
    161 
    162 fail:
    163     /* TODO: verify frees in failure case */
    164     if (eid)
    165         free(eid);
    166     if (mixer)
    167         mixer_close(mixer);
    168     else if (fd >= 0)
    169         close(fd);
    170     return 0;
    171 }
    172 
    173 const char *mixer_get_name(struct mixer *mixer)
    174 {
    175     return (const char *)mixer->card_info.name;
    176 }
    177 
    178 unsigned int mixer_get_num_ctls(struct mixer *mixer)
    179 {
    180     if (!mixer)
    181         return 0;
    182 
    183     return mixer->count;
    184 }
    185 
    186 struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
    187 {
    188     if (mixer && (id < mixer->count))
    189         return mixer->ctl + id;
    190 
    191     return NULL;
    192 }
    193 
    194 struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
    195 {
    196     unsigned int n;
    197 
    198     if (!mixer)
    199         return NULL;
    200 
    201     for (n = 0; n < mixer->count; n++)
    202         if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
    203             return mixer->ctl + n;
    204 
    205     return NULL;
    206 }
    207 
    208 void mixer_ctl_update(struct mixer_ctl *ctl)
    209 {
    210     ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
    211 }
    212 
    213 const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
    214 {
    215     if (!ctl)
    216         return NULL;
    217 
    218     return (const char *)ctl->info->id.name;
    219 }
    220 
    221 enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
    222 {
    223     if (!ctl)
    224         return MIXER_CTL_TYPE_UNKNOWN;
    225 
    226     switch (ctl->info->type) {
    227     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:    return MIXER_CTL_TYPE_BOOL;
    228     case SNDRV_CTL_ELEM_TYPE_INTEGER:    return MIXER_CTL_TYPE_INT;
    229     case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
    230     case SNDRV_CTL_ELEM_TYPE_BYTES:      return MIXER_CTL_TYPE_BYTE;
    231     case SNDRV_CTL_ELEM_TYPE_IEC958:     return MIXER_CTL_TYPE_IEC958;
    232     case SNDRV_CTL_ELEM_TYPE_INTEGER64:  return MIXER_CTL_TYPE_INT64;
    233     default:                             return MIXER_CTL_TYPE_UNKNOWN;
    234     };
    235 }
    236 
    237 const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
    238 {
    239     if (!ctl)
    240         return "";
    241 
    242     switch (ctl->info->type) {
    243     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:    return "BOOL";
    244     case SNDRV_CTL_ELEM_TYPE_INTEGER:    return "INT";
    245     case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
    246     case SNDRV_CTL_ELEM_TYPE_BYTES:      return "BYTE";
    247     case SNDRV_CTL_ELEM_TYPE_IEC958:     return "IEC958";
    248     case SNDRV_CTL_ELEM_TYPE_INTEGER64:  return "INT64";
    249     default:                             return "Unknown";
    250     };
    251 }
    252 
    253 unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
    254 {
    255     if (!ctl)
    256         return 0;
    257 
    258     return ctl->info->count;
    259 }
    260 
    261 static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
    262 {
    263     int range;
    264 
    265     if (percent > 100)
    266         percent = 100;
    267     else if (percent < 0)
    268         percent = 0;
    269 
    270     range = (ei->value.integer.max - ei->value.integer.min);
    271 
    272     return ei->value.integer.min + (range * percent) / 100;
    273 }
    274 
    275 static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
    276 {
    277     int range = (ei->value.integer.max - ei->value.integer.min);
    278 
    279     if (range == 0)
    280         return 0;
    281 
    282     return ((value - ei->value.integer.min) / range) * 100;
    283 }
    284 
    285 int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
    286 {
    287     if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
    288         return -EINVAL;
    289 
    290     return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
    291 }
    292 
    293 int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
    294 {
    295     if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
    296         return -EINVAL;
    297 
    298     return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
    299 }
    300 
    301 int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
    302 {
    303     struct snd_ctl_elem_value ev;
    304     int ret;
    305 
    306     if (!ctl || (id >= ctl->info->count))
    307         return -EINVAL;
    308 
    309     memset(&ev, 0, sizeof(ev));
    310     ev.id.numid = ctl->info->id.numid;
    311     ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
    312     if (ret < 0)
    313         return ret;
    314 
    315     switch (ctl->info->type) {
    316     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
    317         return !!ev.value.integer.value[id];
    318 
    319     case SNDRV_CTL_ELEM_TYPE_INTEGER:
    320         return ev.value.integer.value[id];
    321 
    322     case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
    323         return ev.value.enumerated.item[id];
    324 
    325     case SNDRV_CTL_ELEM_TYPE_BYTES:
    326         return ev.value.bytes.data[id];
    327 
    328     default:
    329         return -EINVAL;
    330     }
    331 
    332     return 0;
    333 }
    334 
    335 int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
    336 {
    337     struct snd_ctl_elem_value ev;
    338     int ret = 0;
    339     size_t size;
    340     void *source;
    341 
    342     if (!ctl || (count > ctl->info->count) || !count || !array)
    343         return -EINVAL;
    344 
    345     memset(&ev, 0, sizeof(ev));
    346     ev.id.numid = ctl->info->id.numid;
    347 
    348     switch (ctl->info->type) {
    349     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
    350     case SNDRV_CTL_ELEM_TYPE_INTEGER:
    351         ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
    352         if (ret < 0)
    353             return ret;
    354         size = sizeof(ev.value.integer.value[0]);
    355         source = ev.value.integer.value;
    356         break;
    357 
    358     case SNDRV_CTL_ELEM_TYPE_BYTES:
    359         /* check if this is new bytes TLV */
    360         if (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
    361             struct snd_ctl_tlv *tlv;
    362             int ret;
    363 
    364             if (count > SIZE_MAX - sizeof(*tlv))
    365                 return -EINVAL;
    366             tlv = calloc(1, sizeof(*tlv) + count);
    367             if (!tlv)
    368                 return -ENOMEM;
    369             tlv->numid = ctl->info->id.numid;
    370             tlv->length = count;
    371             ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, tlv);
    372 
    373             source = tlv->tlv;
    374             memcpy(array, source, count);
    375 
    376             free(tlv);
    377 
    378             return ret;
    379         } else {
    380             ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
    381             if (ret < 0)
    382                 return ret;
    383             size = sizeof(ev.value.bytes.data[0]);
    384             source = ev.value.bytes.data;
    385             break;
    386         }
    387 
    388     case SNDRV_CTL_ELEM_TYPE_IEC958:
    389         size = sizeof(ev.value.iec958);
    390         source = &ev.value.iec958;
    391         break;
    392 
    393     default:
    394         return -EINVAL;
    395     }
    396 
    397     memcpy(array, source, size * count);
    398 
    399     return 0;
    400 }
    401 
    402 int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
    403 {
    404     struct snd_ctl_elem_value ev;
    405     int ret;
    406 
    407     if (!ctl || (id >= ctl->info->count))
    408         return -EINVAL;
    409 
    410     memset(&ev, 0, sizeof(ev));
    411     ev.id.numid = ctl->info->id.numid;
    412     ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
    413     if (ret < 0)
    414         return ret;
    415 
    416     switch (ctl->info->type) {
    417     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
    418         ev.value.integer.value[id] = !!value;
    419         break;
    420 
    421     case SNDRV_CTL_ELEM_TYPE_INTEGER:
    422         ev.value.integer.value[id] = value;
    423         break;
    424 
    425     case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
    426         ev.value.enumerated.item[id] = value;
    427         break;
    428 
    429     case SNDRV_CTL_ELEM_TYPE_BYTES:
    430         ev.value.bytes.data[id] = value;
    431         break;
    432 
    433     default:
    434         return -EINVAL;
    435     }
    436 
    437     return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
    438 }
    439 
    440 int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
    441 {
    442     struct snd_ctl_elem_value ev;
    443     size_t size;
    444     void *dest;
    445 
    446     if (!ctl || (count > ctl->info->count) || !count || !array)
    447         return -EINVAL;
    448 
    449     memset(&ev, 0, sizeof(ev));
    450     ev.id.numid = ctl->info->id.numid;
    451 
    452     switch (ctl->info->type) {
    453     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
    454     case SNDRV_CTL_ELEM_TYPE_INTEGER:
    455         size = sizeof(ev.value.integer.value[0]);
    456         dest = ev.value.integer.value;
    457         break;
    458 
    459     case SNDRV_CTL_ELEM_TYPE_BYTES:
    460         /* check if this is new bytes TLV */
    461         if (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
    462             struct snd_ctl_tlv *tlv;
    463             int ret = 0;
    464             if (count > SIZE_MAX - sizeof(*tlv))
    465                 return -EINVAL;
    466             tlv = calloc(1, sizeof(*tlv) + count);
    467             if (!tlv)
    468                 return -ENOMEM;
    469             tlv->numid = ctl->info->id.numid;
    470             tlv->length = count;
    471             memcpy(tlv->tlv, array, count);
    472 
    473             ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
    474             free(tlv);
    475 
    476             return ret;
    477         } else {
    478             size = sizeof(ev.value.bytes.data[0]);
    479             dest = ev.value.bytes.data;
    480         }
    481         break;
    482 
    483     case SNDRV_CTL_ELEM_TYPE_IEC958:
    484         size = sizeof(ev.value.iec958);
    485         dest = &ev.value.iec958;
    486         break;
    487 
    488     default:
    489         return -EINVAL;
    490     }
    491 
    492     memcpy(dest, array, size * count);
    493 
    494     return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
    495 }
    496 
    497 int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
    498 {
    499     if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
    500         return -EINVAL;
    501 
    502     return ctl->info->value.integer.min;
    503 }
    504 
    505 int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
    506 {
    507     if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
    508         return -EINVAL;
    509 
    510     return ctl->info->value.integer.max;
    511 }
    512 
    513 unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
    514 {
    515     if (!ctl)
    516         return 0;
    517 
    518     return ctl->info->value.enumerated.items;
    519 }
    520 
    521 const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
    522                                       unsigned int enum_id)
    523 {
    524     if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
    525         (enum_id >= ctl->info->value.enumerated.items))
    526         return NULL;
    527 
    528     return (const char *)ctl->ename[enum_id];
    529 }
    530 
    531 int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
    532 {
    533     unsigned int i, num_enums;
    534     struct snd_ctl_elem_value ev;
    535     int ret;
    536 
    537     if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
    538         return -EINVAL;
    539 
    540     num_enums = ctl->info->value.enumerated.items;
    541     for (i = 0; i < num_enums; i++) {
    542         if (!strcmp(string, ctl->ename[i])) {
    543             memset(&ev, 0, sizeof(ev));
    544             ev.value.enumerated.item[0] = i;
    545             ev.id.numid = ctl->info->id.numid;
    546             ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
    547             if (ret < 0)
    548                 return ret;
    549             return 0;
    550         }
    551     }
    552 
    553     return -EINVAL;
    554 }
    555 
    556