commit 8e12efdadf2dd82d43739a88f5da88c0d9188ac0 Author: Ingo Feinerer <feine...@logic.at> AuthorDate: Sat May 9 12:03:20 2020 +0200 Commit: Aaron Marcher <m...@drkhsh.at> CommitDate: Mon Nov 30 19:21:53 2020 +0100
Use the sioctl_open(3) OpenBSD API to access audio controls Starting with OpenBSD 6.7 regular users cannot access raw audio devices anymore, for improved security. Instead use the sioctl_open(3) API to access and manipulate audio controls exposed by sndiod(8). On the first call a permanent connection is established with the running sndiod daemon, and call-back functions are registered which are triggered when audio controls are changed (e.g., a USB headset is attached) or when the volume is modified. On subsequent calls we poll for changes; if there are no volume changes this costs virtually nothing. Joint work with Alexandre Ratchov. diff --git a/LICENSE b/LICENSE index 72b1389..3c1931f 100644 --- a/LICENSE +++ b/LICENSE @@ -19,10 +19,11 @@ Copyright 2018 David Demelier <mark...@malikania.fr> Copyright 2018-2019 Michael Buch <michaelbuc...@gmail.com> Copyright 2018 Ian Remmler <i...@remmler.org> Copyright 2016-2019 Joerg Jung <j...@openbsd.org> -Copyright 2019 Ingo Feinerer <feine...@logic.at> Copyright 2019 Ryan Kes <alray...@gmail.com> Copyright 2019 Cem Keylan <c...@ckyln.com> Copyright 2019 dsp <d...@2f30.org> +Copyright 2019-2020 Ingo Feinerer <feine...@logic.at> +Copyright 2020 Alexandre Ratchov <a...@caoua.org> Copyright 2020 Mart Lubbers <m...@martlubbers.net> Copyright 2020 Daniel Moch <dan...@danielmoch.com> diff --git a/components/volume.c b/components/volume.c index 61cec90..b6665da 100644 --- a/components/volume.c +++ b/components/volume.c @@ -8,69 +8,177 @@ #include "../util.h" #if defined(__OpenBSD__) - #include <sys/audioio.h> + #include <sys/queue.h> + #include <poll.h> + #include <sndio.h> + #include <stdlib.h> + + struct control { + LIST_ENTRY(control) next; + unsigned int addr; + #define CTRL_NONE 0 + #define CTRL_LEVEL 1 + #define CTRL_MUTE 2 + unsigned int type; + unsigned int maxval; + unsigned int val; + }; + + static LIST_HEAD(, control) controls = LIST_HEAD_INITIALIZER(controls); + static struct pollfd *pfds; + static struct sioctl_hdl *hdl; + static int initialized; + + /* + * Call-back to obtain the description of all audio controls. + */ + static void + ondesc(void *unused, struct sioctl_desc *desc, int val) + { + struct control *c, *ctmp; + unsigned int type = CTRL_NONE; + + if (desc == NULL) + return; + + /* Delete existing audio control with the same address. */ + LIST_FOREACH_SAFE(c, &controls, next, ctmp) { + if (desc->addr == c->addr) { + LIST_REMOVE(c, next); + free(c); + break; + } + } + + /* Only match output.level and output.mute audio controls. */ + if (desc->group[0] != 0 || + strcmp(desc->node0.name, "output") != 0) + return; + if (desc->type == SIOCTL_NUM && + strcmp(desc->func, "level") == 0) + type = CTRL_LEVEL; + else if (desc->type == SIOCTL_SW && + strcmp(desc->func, "mute") == 0) + type = CTRL_MUTE; + else + return; + + c = malloc(sizeof(struct control)); + if (c == NULL) { + warn("sndio: failed to allocate audio control\n"); + return; + } + + c->addr = desc->addr; + c->type = type; + c->maxval = desc->maxval; + c->val = val; + LIST_INSERT_HEAD(&controls, c, next); + } + + /* + * Call-back invoked whenever an audio control changes. + */ + static void + onval(void *unused, unsigned int addr, unsigned int val) + { + struct control *c; + + LIST_FOREACH(c, &controls, next) { + if (c->addr == addr) + break; + } + c->val = val; + } + + static void + cleanup(void) + { + struct control *c; + + if (hdl) { + sioctl_close(hdl); + hdl = NULL; + } + + free(pfds); + pfds = NULL; + + while (!LIST_EMPTY(&controls)) { + c = LIST_FIRST(&controls); + LIST_REMOVE(c, next); + free(c); + } + } + + static int + init(void) + { + hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0); + if (hdl == NULL) { + warn("sndio: cannot open device"); + goto failed; + } + + if (!sioctl_ondesc(hdl, ondesc, NULL)) { + warn("sndio: cannot set control description call-back"); + goto failed; + } + + if (!sioctl_onval(hdl, onval, NULL)) { + warn("sndio: cannot set control values call-back"); + goto failed; + } + + pfds = calloc(sioctl_nfds(hdl), sizeof(struct pollfd)); + if (pfds == NULL) { + warn("sndio: cannot allocate pollfd structures"); + goto failed; + } + + return 1; + failed: + cleanup(); + return 0; + } const char * - vol_perc(const char *card) + vol_perc(const char *unused) { - static int cls = -1; - mixer_devinfo_t mdi; - mixer_ctrl_t mc; - int afd = -1, m = -1, v = -1; + struct control *c; + int n, v, value; - if ((afd = open(card, O_RDONLY)) < 0) { - warn("open '%s':", card); + if (!initialized) + initialized = init(); + + if (hdl == NULL) return NULL; - } - for (mdi.index = 0; cls == -1; mdi.index++) { - if (ioctl(afd, AUDIO_MIXER_DEVINFO, &mdi) < 0) { - warn("ioctl 'AUDIO_MIXER_DEVINFO':"); - close(afd); - return NULL; - } - if (mdi.type == AUDIO_MIXER_CLASS && - !strncmp(mdi.label.name, - AudioCoutputs, - MAX_AUDIO_DEV_LEN)) - cls = mdi.index; - } - for (mdi.index = 0; v == -1 || m == -1; mdi.index++) { - if (ioctl(afd, AUDIO_MIXER_DEVINFO, &mdi) < 0) { - warn("ioctl 'AUDIO_MIXER_DEVINFO':"); - close(afd); - return NULL; - } - if (mdi.mixer_class == cls && - ((mdi.type == AUDIO_MIXER_VALUE && - !strncmp(mdi.label.name, - AudioNmaster, - MAX_AUDIO_DEV_LEN)) || - (mdi.type == AUDIO_MIXER_ENUM && - !strncmp(mdi.label.name, - AudioNmute, - MAX_AUDIO_DEV_LEN)))) { - mc.dev = mdi.index, mc.type = mdi.type; - if (ioctl(afd, AUDIO_MIXER_READ, &mc) < 0) { - warn("ioctl 'AUDIO_MIXER_READ':"); - close(afd); + n = sioctl_pollfd(hdl, pfds, POLLIN); + if (n > 0) { + n = poll(pfds, n, 0); + if (n > 0) { + if (sioctl_revents(hdl, pfds) & POLLHUP) { + warn("sndio: disconnected"); + cleanup(); return NULL; } - if (mc.type == AUDIO_MIXER_VALUE) - v = mc.un.value.num_channels == 1 ? - mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] : - (mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] > - mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] ? - mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] : - mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]); - else if (mc.type == AUDIO_MIXER_ENUM) - m = mc.un.ord; } } - close(afd); + value = 100; + LIST_FOREACH(c, &controls, next) { + if (c->type == CTRL_MUTE && c->val == 1) + value = 0; + else if (c->type == CTRL_LEVEL) { + v = (c->val * 100 + c->maxval / 2) / c->maxval; + /* For multiple channels return the minimum. */ + if (v < value) + value = v; + } + } - return bprintf("%d", m ? 0 : v * 100 / 255); + return bprintf("%d", value); } #else #include <sys/soundcard.h> diff --git a/config.def.h b/config.def.h index 0895f6a..93a875a 100644 --- a/config.def.h +++ b/config.def.h @@ -59,6 +59,7 @@ static const char unknown_str[] = "n/a"; * uptime system uptime NULL * username username of current user NULL * vol_perc OSS/ALSA volume in percent mixer file (/dev/mixer) + * NULL on OpenBSD * wifi_perc WiFi signal in percent interface name (wlan0) * wifi_essid WiFi ESSID interface name (wlan0) */ diff --git a/config.mk b/config.mk index 3b32b7c..d88695c 100644 --- a/config.mk +++ b/config.mk @@ -14,6 +14,7 @@ X11LIB = /usr/X11R6/lib CPPFLAGS = -I$(X11INC) -D_DEFAULT_SOURCE CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os LDFLAGS = -L$(X11LIB) -s +# OpenBSD: add -lsndio LDLIBS = -lX11 # compiler and linker