Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/next/external/cache/sources/tinyalsa/mixer.c
Views: 3959
/* mixer.c1**2** Copyright 2011, The Android Open Source Project3**4** Redistribution and use in source and binary forms, with or without5** modification, are permitted provided that the following conditions are met:6** * Redistributions of source code must retain the above copyright7** notice, this list of conditions and the following disclaimer.8** * Redistributions in binary form must reproduce the above copyright9** notice, this list of conditions and the following disclaimer in the10** documentation and/or other materials provided with the distribution.11** * Neither the name of The Android Open Source Project nor the names of12** its contributors may be used to endorse or promote products derived13** from this software without specific prior written permission.14**15** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND16** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE19** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR21** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER22** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH25** DAMAGE.26*/2728#include <stdio.h>29#include <stdlib.h>30#include <string.h>31#include <unistd.h>32#include <fcntl.h>33#include <errno.h>34#include <ctype.h>3536#include <sys/ioctl.h>3738#include <linux/ioctl.h>39#define __force40#define __bitwise41#define __user42#include <sound/asound.h>4344#include <tinyalsa/asoundlib.h>4546struct mixer_ctl {47struct mixer *mixer;48struct snd_ctl_elem_info *info;49char **ename;50};5152struct mixer {53int fd;54struct snd_ctl_card_info card_info;55struct snd_ctl_elem_info *elem_info;56struct mixer_ctl *ctl;57unsigned int count;58};5960void mixer_close(struct mixer *mixer)61{62unsigned int n,m;6364if (!mixer)65return;6667if (mixer->fd >= 0)68close(mixer->fd);6970if (mixer->ctl) {71for (n = 0; n < mixer->count; n++) {72if (mixer->ctl[n].ename) {73unsigned int max = mixer->ctl[n].info->value.enumerated.items;74for (m = 0; m < max; m++)75free(mixer->ctl[n].ename[m]);76free(mixer->ctl[n].ename);77}78}79free(mixer->ctl);80}8182if (mixer->elem_info)83free(mixer->elem_info);8485free(mixer);8687/* TODO: verify frees */88}8990struct mixer *mixer_open(unsigned int card)91{92struct snd_ctl_elem_list elist;93struct snd_ctl_elem_info tmp;94struct snd_ctl_elem_id *eid = NULL;95struct mixer *mixer = NULL;96unsigned int n, m;97int fd;98char fn[256];99100snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);101fd = open(fn, O_RDWR);102if (fd < 0)103return 0;104105memset(&elist, 0, sizeof(elist));106if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)107goto fail;108109mixer = calloc(1, sizeof(*mixer));110if (!mixer)111goto fail;112113mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));114mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));115if (!mixer->ctl || !mixer->elem_info)116goto fail;117118if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)119goto fail;120121eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));122if (!eid)123goto fail;124125mixer->count = elist.count;126mixer->fd = fd;127elist.space = mixer->count;128elist.pids = eid;129if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)130goto fail;131132for (n = 0; n < mixer->count; n++) {133struct snd_ctl_elem_info *ei = mixer->elem_info + n;134ei->id.numid = eid[n].numid;135if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)136goto fail;137mixer->ctl[n].info = ei;138mixer->ctl[n].mixer = mixer;139if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {140char **enames = calloc(ei->value.enumerated.items, sizeof(char*));141if (!enames)142goto fail;143mixer->ctl[n].ename = enames;144for (m = 0; m < ei->value.enumerated.items; m++) {145memset(&tmp, 0, sizeof(tmp));146tmp.id.numid = ei->id.numid;147tmp.value.enumerated.item = m;148if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)149goto fail;150enames[m] = strdup(tmp.value.enumerated.name);151if (!enames[m])152goto fail;153}154}155}156157free(eid);158return mixer;159160fail:161/* TODO: verify frees in failure case */162if (eid)163free(eid);164if (mixer)165mixer_close(mixer);166else if (fd >= 0)167close(fd);168return 0;169}170171const char *mixer_get_name(struct mixer *mixer)172{173return (const char *)mixer->card_info.name;174}175176unsigned int mixer_get_num_ctls(struct mixer *mixer)177{178if (!mixer)179return 0;180181return mixer->count;182}183184struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)185{186if (mixer && (id < mixer->count))187return mixer->ctl + id;188189return NULL;190}191192struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)193{194unsigned int n;195196if (!mixer)197return NULL;198199for (n = 0; n < mixer->count; n++)200if (!strcmp(name, (char*) mixer->elem_info[n].id.name))201return mixer->ctl + n;202203return NULL;204}205206void mixer_ctl_update(struct mixer_ctl *ctl)207{208ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);209}210211const char *mixer_ctl_get_name(struct mixer_ctl *ctl)212{213if (!ctl)214return NULL;215216return (const char *)ctl->info->id.name;217}218219enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)220{221if (!ctl)222return MIXER_CTL_TYPE_UNKNOWN;223224switch (ctl->info->type) {225case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;226case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;227case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;228case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;229case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;230case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;231default: return MIXER_CTL_TYPE_UNKNOWN;232};233}234235const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)236{237if (!ctl)238return "";239240switch (ctl->info->type) {241case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";242case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";243case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";244case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";245case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";246case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";247default: return "Unknown";248};249}250251unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)252{253if (!ctl)254return 0;255256return ctl->info->count;257}258259static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)260{261if ((percent > 100) || (percent < 0)) {262return -EINVAL;263}264265int range = (ei->value.integer.max - ei->value.integer.min);266267return ei->value.integer.min + (range * percent) / 100;268}269270static int int_to_percent(struct snd_ctl_elem_info *ei, int value)271{272int range = (ei->value.integer.max - ei->value.integer.min);273274if (range == 0)275return 0;276277return ((value - ei->value.integer.min) / range) * 100;278}279280int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)281{282if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))283return -EINVAL;284285return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));286}287288int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)289{290if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))291return -EINVAL;292293return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));294}295296int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)297{298struct snd_ctl_elem_value ev;299int ret;300301if (!ctl || (id >= ctl->info->count))302return -EINVAL;303304memset(&ev, 0, sizeof(ev));305ev.id.numid = ctl->info->id.numid;306ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);307if (ret < 0)308return ret;309310switch (ctl->info->type) {311case SNDRV_CTL_ELEM_TYPE_BOOLEAN:312return !!ev.value.integer.value[id];313314case SNDRV_CTL_ELEM_TYPE_INTEGER:315return ev.value.integer.value[id];316317case SNDRV_CTL_ELEM_TYPE_ENUMERATED:318return ev.value.enumerated.item[id];319320case SNDRV_CTL_ELEM_TYPE_BYTES:321return ev.value.bytes.data[id];322323default:324return -EINVAL;325}326327return 0;328}329330int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)331{332struct snd_ctl_elem_value ev;333int ret;334size_t size;335void *source;336337if (!ctl || (count > ctl->info->count) || !count || !array)338return -EINVAL;339340memset(&ev, 0, sizeof(ev));341ev.id.numid = ctl->info->id.numid;342343ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);344if (ret < 0)345return ret;346347switch (ctl->info->type) {348case SNDRV_CTL_ELEM_TYPE_BOOLEAN:349case SNDRV_CTL_ELEM_TYPE_INTEGER:350size = sizeof(ev.value.integer.value[0]);351source = ev.value.integer.value;352break;353354case SNDRV_CTL_ELEM_TYPE_BYTES:355size = sizeof(ev.value.bytes.data[0]);356source = ev.value.bytes.data;357break;358359default:360return -EINVAL;361}362363memcpy(array, source, size * count);364365return 0;366}367368int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)369{370struct snd_ctl_elem_value ev;371int ret;372373if (!ctl || (id >= ctl->info->count))374return -EINVAL;375376memset(&ev, 0, sizeof(ev));377ev.id.numid = ctl->info->id.numid;378ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);379if (ret < 0)380return ret;381382switch (ctl->info->type) {383case SNDRV_CTL_ELEM_TYPE_BOOLEAN:384ev.value.integer.value[id] = !!value;385break;386387case SNDRV_CTL_ELEM_TYPE_INTEGER:388if ((value < mixer_ctl_get_range_min(ctl)) ||389(value > mixer_ctl_get_range_max(ctl))) {390return -EINVAL;391}392393ev.value.integer.value[id] = value;394break;395396case SNDRV_CTL_ELEM_TYPE_ENUMERATED:397ev.value.enumerated.item[id] = value;398break;399400case SNDRV_CTL_ELEM_TYPE_BYTES:401ev.value.bytes.data[id] = value;402break;403404default:405return -EINVAL;406}407408return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);409}410411int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)412{413struct snd_ctl_elem_value ev;414size_t size;415void *dest;416417if (!ctl || (count > ctl->info->count) || !count || !array)418return -EINVAL;419420memset(&ev, 0, sizeof(ev));421ev.id.numid = ctl->info->id.numid;422423switch (ctl->info->type) {424case SNDRV_CTL_ELEM_TYPE_BOOLEAN:425case SNDRV_CTL_ELEM_TYPE_INTEGER:426size = sizeof(ev.value.integer.value[0]);427dest = ev.value.integer.value;428break;429430case SNDRV_CTL_ELEM_TYPE_BYTES:431size = sizeof(ev.value.bytes.data[0]);432dest = ev.value.bytes.data;433break;434435default:436return -EINVAL;437}438439memcpy(dest, array, size * count);440441return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);442}443444int mixer_ctl_get_range_min(struct mixer_ctl *ctl)445{446if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))447return -EINVAL;448449return ctl->info->value.integer.min;450}451452int mixer_ctl_get_range_max(struct mixer_ctl *ctl)453{454if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))455return -EINVAL;456457return ctl->info->value.integer.max;458}459460unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)461{462if (!ctl)463return 0;464465return ctl->info->value.enumerated.items;466}467468const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,469unsigned int enum_id)470{471if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||472(enum_id >= ctl->info->value.enumerated.items))473return NULL;474475return (const char *)ctl->ename[enum_id];476}477478int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)479{480unsigned int i, num_enums;481struct snd_ctl_elem_value ev;482int ret;483484if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))485return -EINVAL;486487num_enums = ctl->info->value.enumerated.items;488for (i = 0; i < num_enums; i++) {489if (!strcmp(string, ctl->ename[i])) {490memset(&ev, 0, sizeof(ev));491ev.value.enumerated.item[0] = i;492ev.id.numid = ctl->info->id.numid;493ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);494if (ret < 0)495return ret;496return 0;497}498}499500return -EINVAL;501}502503504505