summaryrefslogtreecommitdiff
path: root/lib/libossaudio
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libossaudio')
-rw-r--r--lib/libossaudio/Makefile6
-rw-r--r--lib/libossaudio/ossaudio.c473
2 files changed, 207 insertions, 272 deletions
diff --git a/lib/libossaudio/Makefile b/lib/libossaudio/Makefile
index de1d0e7deeb..ee99b60db10 100644
--- a/lib/libossaudio/Makefile
+++ b/lib/libossaudio/Makefile
@@ -1,13 +1,15 @@
-# $OpenBSD: Makefile,v 1.5 2014/07/16 20:02:17 okan Exp $
+# $OpenBSD: Makefile,v 1.6 2020/04/02 19:57:10 ratchov Exp $
# $NetBSD: Makefile,v 1.5 1998/04/13 14:18:45 lukem Exp $
LIB= ossaudio
MAN= ossaudio.3
-SRCS= ossaudio.c
+SRCS= ossaudio.c aucat.c debug.c sioctl.c sioctl_aucat.c sioctl_sun.c
CPPFLAGS+= -I${.CURDIR}
+.PATH: ${.CURDIR}/../libsndio
+
includes:
@cd ${.CURDIR}; cmp -s soundcard.h ${DESTDIR}/usr/include/soundcard.h || \
${INSTALL} ${INSTALL_COPY} -m 444 -o $(BINOWN) -g $(BINGRP) soundcard.h \
diff --git a/lib/libossaudio/ossaudio.c b/lib/libossaudio/ossaudio.c
index 8374af54830..bad73ac0c33 100644
--- a/lib/libossaudio/ossaudio.c
+++ b/lib/libossaudio/ossaudio.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ossaudio.c,v 1.20 2019/06/28 13:32:42 deraadt Exp $ */
+/* $OpenBSD: ossaudio.c,v 1.21 2020/04/02 19:57:10 ratchov Exp $ */
/* $NetBSD: ossaudio.c,v 1.14 2001/05/10 01:53:48 augustss Exp $ */
/*-
@@ -36,26 +36,38 @@
#include <string.h>
#include <sys/types.h>
#include <sys/ioctl.h>
-#include <sys/audioio.h>
-#include <sys/stat.h>
#include <errno.h>
-
+#include <poll.h>
+#include <sndio.h>
+#include <stdlib.h>
+#include <stdio.h>
#include "soundcard.h"
-#undef ioctl
-#define GET_DEV(com) ((com) & 0xff)
+#ifdef DEBUG
+#define DPRINTF(...) do { fprintf(stderr, __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(...) do {} while (0)
+#endif
-#define TO_OSSVOL(x) (((x) * 100 + 127) / 255)
-#define FROM_OSSVOL(x) ((((x) > 100 ? 100 : (x)) * 255 + 50) / 100)
+#define GET_DEV(com) ((com) & 0xff)
+#define INTARG (*(int*)argp)
-static struct audiodevinfo *getdevinfo(int);
+struct control {
+ struct control *next;
+ int type; /* one of SOUND_MIXER_xxx */
+ int chan; /* 0 -> left, 1 -> right, -1 -> mono */
+ int addr; /* sioctl control id */
+ int value; /* current value */
+ int max;
+};
static int mixer_ioctl(int, unsigned long, void *);
-static int opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq);
-static int enum_to_ord(struct audiodevinfo *di, int enm);
-static int enum_to_mask(struct audiodevinfo *di, int enm);
-#define INTARG (*(int*)argp)
+static int initialized;
+static struct control *controls;
+static struct sioctl_hdl *hdl;
+static char *dev_name = SIO_DEVANY;
+static struct pollfd *pfds;
int
_oss_ioctl(int fd, unsigned long com, ...)
@@ -71,201 +83,163 @@ _oss_ioctl(int fd, unsigned long com, ...)
else if (IOCGROUP(com) == 'M')
return mixer_ioctl(fd, com, argp);
else
- return ioctl(fd, com, argp);
+ return (ioctl)(fd, com, argp);
}
-/* If the mixer device should have more than MAX_MIXER_DEVS devices
- * some will not be available to Linux */
-#define MAX_MIXER_DEVS 64
-struct audiodevinfo {
- int done;
- dev_t dev;
- ino_t ino;
- int16_t devmap[SOUND_MIXER_NRDEVICES],
- rdevmap[MAX_MIXER_DEVS];
- char names[MAX_MIXER_DEVS][MAX_AUDIO_DEV_LEN];
- int enum2opaque[MAX_MIXER_DEVS];
- u_long devmask, recmask, stereomask;
- u_long caps, recsource;
-};
-
-static int
-opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq)
+/*
+ * new control
+ */
+static void
+mixer_ondesc(void *unused, struct sioctl_desc *d, int val)
{
- int i, o;
+ struct control *i, **pi;
+ int type;
- for (i = 0; i < MAX_MIXER_DEVS; i++) {
- o = di->enum2opaque[i];
- if (o == opq)
- break;
- if (o == -1 && label != NULL &&
- !strncmp(di->names[i], label->name, sizeof di->names[i])) {
- di->enum2opaque[i] = opq;
+ if (d == NULL)
+ return;
+
+ /*
+ * delete existing control with the same address
+ */
+ for (pi = &controls; (i = *pi) != NULL; pi = &i->next) {
+ if (d->addr == i->addr) {
+ *pi = i->next;
+ free(i);
break;
}
}
- if (i >= MAX_MIXER_DEVS)
- i = -1;
- /*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/
- return (i);
+
+ /*
+ * we support only numeric "level" controls, first 2 channels
+ */
+ if (d->type != SIOCTL_NUM || d->node0.unit >= 2 ||
+ strcmp(d->func, "level") != 0)
+ return;
+
+ /*
+ * We expose top-level input.level and output.level as OSS
+ * volume and microphone knobs. By default sndiod exposes
+ * the underlying hardware knobs as hw/input.level and
+ * hw/output.level that we map to OSS gain controls. This
+ * ensures useful knobs are exposed no matter if sndiod
+ * is running or not.
+ */
+ if (d->group[0] == 0) {
+ if (strcmp(d->node0.name, "output") == 0)
+ type = SOUND_MIXER_VOLUME;
+ else if (strcmp(d->node0.name, "input") == 0)
+ type = SOUND_MIXER_MIC;
+ else
+ return;
+ } else if (strcmp(d->group, "hw") == 0) {
+ if (strcmp(d->node0.name, "output") == 0)
+ type = SOUND_MIXER_OGAIN;
+ else if (strcmp(d->node0.name, "input") == 0)
+ type = SOUND_MIXER_IGAIN;
+ else
+ return;
+ } else
+ return;
+
+ i = malloc(sizeof(struct control));
+ if (i == NULL) {
+ DPRINTF("%s: cannot allocate control\n", __func__);
+ return;
+ }
+
+ i->addr = d->addr;
+ i->chan = d->node0.unit;
+ i->max = d->maxval;
+ i->value = val;
+ i->type = type;
+ i->next = controls;
+ controls = i;
+ DPRINTF("%s: %d: used as %d, chan = %d, value = %d\n", __func__,
+ i->addr, i->type, i->chan, i->value);
}
-static int
-enum_to_ord(struct audiodevinfo *di, int enm)
+/*
+ * control value changed
+ */
+static void
+mixer_onval(void *unused, unsigned int addr, unsigned int value)
{
- if (enm >= MAX_MIXER_DEVS)
- return (-1);
+ struct control *c;
+
+ for (c = controls; ; c = c->next) {
+ if (c == NULL) {
+ DPRINTF("%s: %d: change ignored\n", __func__, addr);
+ return;
+ }
+ if (c->addr == addr)
+ break;
+ }
- /*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/
- return (di->enum2opaque[enm]);
+ DPRINTF("%s: %d: changed to %d\n", __func__, addr, value);
+ c->value = value;
}
static int
-enum_to_mask(struct audiodevinfo *di, int enm)
+mixer_init(void)
{
- int m;
- if (enm >= MAX_MIXER_DEVS)
- return (0);
-
- m = di->enum2opaque[enm];
- if (m == -1)
- m = 0;
- /*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/
- return (m);
-}
+ if (initialized)
+ return hdl != NULL;
-/*
- * Collect the audio device information to allow faster
- * emulation of the Linux mixer ioctls. Cache the information
- * to eliminate the overhead of repeating all the ioctls needed
- * to collect the information.
- */
-static struct audiodevinfo *
-getdevinfo(int fd)
-{
- mixer_devinfo_t mi, cl;
- int i, j, e;
- static struct {
- char *name;
- int code;
- } *dp, devs[] = {
- { AudioNmicrophone, SOUND_MIXER_MIC },
- { AudioNline, SOUND_MIXER_LINE },
- { AudioNcd, SOUND_MIXER_CD },
- { AudioNdac, SOUND_MIXER_PCM },
- { AudioNaux, SOUND_MIXER_LINE1 },
- { AudioNrecord, SOUND_MIXER_IMIX },
- { AudioNmaster, SOUND_MIXER_VOLUME },
- { AudioNtreble, SOUND_MIXER_TREBLE },
- { AudioNbass, SOUND_MIXER_BASS },
- { AudioNspeaker, SOUND_MIXER_SPEAKER },
- { AudioNoutput, SOUND_MIXER_OGAIN },
- { AudioNinput, SOUND_MIXER_IGAIN },
- { AudioNfmsynth, SOUND_MIXER_SYNTH },
- { AudioNmidi, SOUND_MIXER_SYNTH },
- { 0, -1 }
- };
- static struct audiodevinfo devcache = { 0 };
- struct audiodevinfo *di = &devcache;
- struct stat sb;
-
- /* Figure out what device it is so we can check if the
- * cached data is valid.
- */
- if (fstat(fd, &sb) < 0)
+ initialized = 1;
+
+ hdl = sioctl_open(dev_name, SIOCTL_READ | SIOCTL_WRITE, 0);
+ if (hdl == NULL) {
+ DPRINTF("%s: cannot open audio control device\n", __func__);
return 0;
- if (di->done && (di->dev == sb.st_dev && di->ino == sb.st_ino))
- return di;
-
- di->done = 1;
- di->dev = sb.st_dev;
- di->ino = sb.st_ino;
- di->devmask = 0;
- di->recmask = 0;
- di->stereomask = 0;
- di->recsource = ~0;
- di->caps = 0;
- for(i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- di->devmap[i] = -1;
- for(i = 0; i < MAX_MIXER_DEVS; i++) {
- di->rdevmap[i] = -1;
- di->names[i][0] = '\0';
- di->enum2opaque[i] = -1;
}
- for(i = 0; i < MAX_MIXER_DEVS; i++) {
- mi.index = i;
- if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) == -1)
- break;
- switch(mi.type) {
- case AUDIO_MIXER_VALUE:
- for(dp = devs; dp->name; dp++)
- if (strcmp(dp->name, mi.label.name) == 0)
- break;
- if (dp->code >= 0) {
- di->devmap[dp->code] = i;
- di->rdevmap[i] = dp->code;
- di->devmask |= 1 << dp->code;
- if (mi.un.v.num_channels == 2)
- di->stereomask |= 1 << dp->code;
- strncpy(di->names[i], mi.label.name,
- sizeof di->names[i]);
- }
- break;
- }
+
+ pfds = calloc(sioctl_nfds(hdl), sizeof(struct pollfd));
+ if (pfds == NULL) {
+ DPRINTF("%s: cannot allocate pfds\n", __func__);
+ goto bad_close;
}
- for(i = 0; i < MAX_MIXER_DEVS; i++) {
- mi.index = i;
- if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) == -1)
- break;
- if (strcmp(mi.label.name, AudioNsource) != 0)
- continue;
- cl.index = mi.mixer_class;
- if (ioctl(fd, AUDIO_MIXER_DEVINFO, &cl) == -1)
- break;
- if ((cl.type != AUDIO_MIXER_CLASS) ||
- (strcmp(cl.label.name, AudioCrecord) != 0))
- continue;
- di->recsource = i;
- switch(mi.type) {
- case AUDIO_MIXER_ENUM:
- for(j = 0; j < mi.un.e.num_mem; j++) {
- e = opaque_to_enum(di,
- &mi.un.e.member[j].label,
- mi.un.e.member[j].ord);
- if (e >= 0)
- di->recmask |= 1 << di->rdevmap[e];
- }
- di->caps = SOUND_CAP_EXCL_INPUT;
- break;
- case AUDIO_MIXER_SET:
- for(j = 0; j < mi.un.s.num_mem; j++) {
- e = opaque_to_enum(di,
- &mi.un.s.member[j].label,
- mi.un.s.member[j].mask);
- if (e >= 0)
- di->recmask |= 1 << di->rdevmap[e];
- }
- break;
- }
+
+ if (!sioctl_ondesc(hdl, mixer_ondesc, NULL)) {
+ DPRINTF("%s: cannot get controls descriptions\n", __func__);
+ goto bad_free;
}
- return di;
+
+ if (!sioctl_onval(hdl, mixer_onval, NULL)) {
+ DPRINTF("%s: cannot get controls values\n", __func__);
+ goto bad_free;
+ }
+
+ return 1;
+
+bad_free:
+ free(pfds);
+bad_close:
+ sioctl_close(hdl);
+ return 0;
}
-int
+static int
mixer_ioctl(int fd, unsigned long com, void *argp)
{
- struct audiodevinfo *di;
+ struct control *c;
struct mixer_info *omi;
- struct audio_device adev;
- mixer_ctrl_t mc;
int idat = 0;
- int i;
- int retval;
- int l, r, n, error, e;
+ int v, n;
- di = getdevinfo(fd);
- if (di == 0)
+ if (!mixer_init()) {
+ DPRINTF("%s: not initialized\n", __func__);
+ errno = EIO;
return -1;
+ }
+
+ n = sioctl_pollfd(hdl, pfds, POLLIN);
+ if (n > 0) {
+ n = poll(pfds, n, 0);
+ if (n == -1)
+ return -1;
+ if (n > 0)
+ sioctl_revents(hdl, pfds);
+ }
switch (com) {
case OSS_GETVERSION:
@@ -273,122 +247,80 @@ mixer_ioctl(int fd, unsigned long com, void *argp)
break;
case SOUND_MIXER_INFO:
case SOUND_OLD_MIXER_INFO:
- error = ioctl(fd, AUDIO_GETDEV, &adev);
- if (error == -1)
- return (error);
omi = argp;
if (com == SOUND_MIXER_INFO)
omi->modify_counter = 1;
- strncpy(omi->id, adev.name, sizeof omi->id);
- strncpy(omi->name, adev.name, sizeof omi->name);
+ strlcpy(omi->id, dev_name, sizeof omi->id);
+ strlcpy(omi->name, dev_name, sizeof omi->name);
return 0;
case SOUND_MIXER_READ_RECSRC:
- if (di->recsource == -1)
- return EINVAL;
- mc.dev = di->recsource;
- if (di->caps & SOUND_CAP_EXCL_INPUT) {
- mc.type = AUDIO_MIXER_ENUM;
- retval = ioctl(fd, AUDIO_MIXER_READ, &mc);
- if (retval == -1)
- return retval;
- e = opaque_to_enum(di, NULL, mc.un.ord);
- if (e >= 0)
- idat = 1 << di->rdevmap[e];
- } else {
- mc.type = AUDIO_MIXER_SET;
- retval = ioctl(fd, AUDIO_MIXER_READ, &mc);
- if (retval == -1)
- return retval;
- e = opaque_to_enum(di, NULL, mc.un.mask);
- if (e >= 0)
- idat = 1 << di->rdevmap[e];
- }
+ case SOUND_MIXER_READ_RECMASK:
+ idat = 0;
+ for (c = controls; c != NULL; c = c->next)
+ idat |= 1 << c->type;
+ idat &= (1 << SOUND_MIXER_MIC) | (1 << SOUND_MIXER_IGAIN);
+ DPRINTF("%s: SOUND_MIXER_READ_RECSRC: %d\n", __func__, idat);
break;
case SOUND_MIXER_READ_DEVMASK:
- idat = di->devmask;
- break;
- case SOUND_MIXER_READ_RECMASK:
- idat = di->recmask;
+ idat = 0;
+ for (c = controls; c != NULL; c = c->next)
+ idat |= 1 << c->type;
+ DPRINTF("%s: SOUND_MIXER_READ_DEVMASK: %d\n", __func__, idat);
break;
case SOUND_MIXER_READ_STEREODEVS:
- idat = di->stereomask;
+ idat = 0;
+ for (c = controls; c != NULL; c = c->next) {
+ if (c->chan == 1)
+ idat |= 1 << c->type;
+ }
+ DPRINTF("%s: SOUND_MIXER_STEREODEVS: %d\n", __func__, idat);
break;
case SOUND_MIXER_READ_CAPS:
- idat = di->caps;
+ idat = 0;
+ DPRINTF("%s: SOUND_MIXER_READ_CAPS: %d\n", __func__, idat);
break;
case SOUND_MIXER_WRITE_RECSRC:
case SOUND_MIXER_WRITE_R_RECSRC:
- if (di->recsource == -1)
- return EINVAL;
- mc.dev = di->recsource;
- idat = INTARG;
- if (di->caps & SOUND_CAP_EXCL_INPUT) {
- mc.type = AUDIO_MIXER_ENUM;
- for(i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- if (idat & (1 << i))
- break;
- if (i >= SOUND_MIXER_NRDEVICES ||
- di->devmap[i] == -1)
- return EINVAL;
- mc.un.ord = enum_to_ord(di, di->devmap[i]);
- } else {
- mc.type = AUDIO_MIXER_SET;
- mc.un.mask = 0;
- for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if (idat & (1 << i)) {
- if (di->devmap[i] == -1)
- return EINVAL;
- mc.un.mask |= enum_to_mask(di, di->devmap[i]);
- }
- }
- }
- return ioctl(fd, AUDIO_MIXER_WRITE, &mc);
+ DPRINTF("%s: SOUND_MIXER_WRITE_RECSRC\n", __func__);
+ errno = EINVAL;
+ return -1;
default:
if (MIXER_READ(SOUND_MIXER_FIRST) <= com &&
com < MIXER_READ(SOUND_MIXER_NRDEVICES)) {
+ doread:
+ idat = 0;
n = GET_DEV(com);
- if (di->devmap[n] == -1)
- return EINVAL;
- mc.dev = di->devmap[n];
- mc.type = AUDIO_MIXER_VALUE;
- doread:
- mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1;
- retval = ioctl(fd, AUDIO_MIXER_READ, &mc);
- if (retval == -1)
- return retval;
- if (mc.type != AUDIO_MIXER_VALUE)
- return EINVAL;
- if (mc.un.value.num_channels != 2) {
- l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO];
- } else {
- l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT];
- r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
+ for (c = controls; c != NULL; c = c->next) {
+ if (c->type != n)
+ continue;
+ v = (c->value * 100 + c->max / 2) / c->max;
+ if (c->chan == 1)
+ v <<= 8;
+ idat |= v;
}
- idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8);
+ DPRINTF("%s: MIXER_READ: %d: 0x%04x\n",
+ __func__, n, idat);
break;
} else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com &&
com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) ||
(MIXER_WRITE(SOUND_MIXER_FIRST) <= com &&
com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) {
- n = GET_DEV(com);
- if (di->devmap[n] == -1)
- return EINVAL;
idat = INTARG;
- l = FROM_OSSVOL( idat & 0xff);
- r = FROM_OSSVOL((idat >> 8) & 0xff);
- mc.dev = di->devmap[n];
- mc.type = AUDIO_MIXER_VALUE;
- if (di->stereomask & (1<<n)) {
- mc.un.value.num_channels = 2;
- mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
- mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
- } else {
- mc.un.value.num_channels = 1;
- mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2;
+ n = GET_DEV(com);
+ for (c = controls; c != NULL; c = c->next) {
+ if (c->type != n)
+ continue;
+ v = idat;
+ if (c->chan == 1)
+ v >>= 8;
+ v &= 0xff;
+ if (v > 100)
+ v = 100;
+ v = (v * c->max + 50) / 100;
+ sioctl_setval(hdl, c->addr, v);
+ DPRINTF("%s: MIXER_WRITE: %d: %d\n",
+ __func__, n, v);
}
- retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc);
- if (retval == -1)
- return retval;
if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com &&
com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))
return 0;
@@ -398,6 +330,7 @@ mixer_ioctl(int fd, unsigned long com, void *argp)
return -1;
}
}
+
INTARG = idat;
return 0;
}