summaryrefslogtreecommitdiff
path: root/sys/dev/audio.c
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2006-05-29 20:23:14 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2006-05-29 20:23:14 +0000
commit286c2bdbdf6b926fab7e57f77cbce263c0f5f1c2 (patch)
tree792f5442439ff9f581370324be7709c7a0087680 /sys/dev/audio.c
parent2a1119bdf41232e0fe0fe1f92771c4546b3d61a1 (diff)
Add support for the audio volume keys found on many laptops' builtin
keyboard. These specific keys are posted to a kernel thread which will issue mixer commands if an audio device exists. Written by Alexey Vatchenko <avv , mail zp ua> with tweaks by deraadt@ and I.
Diffstat (limited to 'sys/dev/audio.c')
-rw-r--r--sys/dev/audio.c169
1 files changed, 168 insertions, 1 deletions
diff --git a/sys/dev/audio.c b/sys/dev/audio.c
index 7ceb6743ca7..69d15fdb1da 100644
--- a/sys/dev/audio.c
+++ b/sys/dev/audio.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: audio.c,v 1.49 2006/03/12 10:34:50 jakemsr Exp $ */
+/* $OpenBSD: audio.c,v 1.50 2006/05/29 20:23:13 miod Exp $ */
/* $NetBSD: audio.c,v 1.119 1999/11/09 16:50:47 augustss Exp $ */
/*
@@ -87,6 +87,8 @@
#include <machine/endian.h>
+#include "wskbd.h" /* NWSKBD (mixer tuning using keyboard) */
+
#ifdef AUDIO_DEBUG
#define DPRINTF(x) if (audiodebug) printf x
#define DPRINTFN(n,x) if (audiodebug>(n)) printf x
@@ -208,6 +210,12 @@ int filt_audioread(struct knote *kn, long hint);
struct filterops audioread_filtops =
{ 1, NULL, filt_audiordetach, filt_audioread};
+#if NWSKBD > 0
+/* Mixer manipulation using keyboard */
+int wskbd_get_mixerdev(struct audio_softc *sc, int dir, int *index);
+int wskbd_set_mixervolume(int dir);
+#endif
+
int
audioprobe(parent, match, aux)
struct device *parent;
@@ -3096,3 +3104,162 @@ filt_audiowrite(struct knote *kn, long hint)
return AUDIO_FILTWRITE(sc);
}
+
+#if NAUDIO > 0 && NWSKBD > 0
+int
+wskbd_get_mixerdev(struct audio_softc *sc, int dir, int *index)
+{
+ mixer_devinfo_t mi;
+ int mixer_class;
+ int error;
+
+ /* looking for ``outputs'' */
+ for (mi.index = 0; ; mi.index++) {
+ error = sc->hw_if->query_devinfo(sc->hw_hdl, &mi);
+ if (error != 0)
+ return (-1);
+
+ if (mi.type == AUDIO_MIXER_CLASS &&
+ strcmp(mi.label.name, AudioCoutputs) == 0) {
+ mixer_class = mi.mixer_class;
+ break;
+ }
+ }
+
+ /*
+ * looking for ``outputs.master''
+ * start mi.index from 0 because ''outputs.master'' can precede
+ * ''outputs''.
+ */
+ for (mi.index = 0; ; mi.index++) {
+ error = sc->hw_if->query_devinfo(sc->hw_hdl, &mi);
+ if (error != 0)
+ return (-1);
+
+ if (mi.type == AUDIO_MIXER_VALUE &&
+ mi.mixer_class == mixer_class &&
+ strcmp(mi.label.name, AudioNmaster) == 0) {
+ if (dir == 0) {
+ /* looking for ``outputs.master.mute'' */
+ if (mi.next < 0)
+ return (-1);
+
+ mi.index = mi.next;
+ error = sc->hw_if->query_devinfo(sc->hw_hdl,
+ &mi);
+ if (error != 0)
+ return (-1);
+
+ if (mi.type != AUDIO_MIXER_ENUM ||
+ strcmp(mi.label.name, AudioNmute) != 0)
+ return (-1);
+ }
+
+ *index = mi.index;
+ return (0);
+ }
+ }
+
+ return (-1);
+}
+
+int
+wskbd_set_mixervolume(int dir)
+{
+ struct audio_softc *sc;
+ mixer_devinfo_t mi;
+ mixer_ctrl_t ct;
+ int l, r;
+ int error;
+
+ if (audio_cd.cd_ndevs == 0 || (sc = audio_cd.cd_devs[0]) == NULL) {
+ DPRINTF(("wskbd_set_mixervolume: audio_cd\n"));
+ return (ENXIO);
+ }
+
+ error = wskbd_get_mixerdev(sc, dir, &ct.dev);
+ if (error == -1) {
+ DPRINTF(("wskbd_set_mixervolume: wskbd_get_mixerdev\n"));
+ return (ENXIO);
+ }
+
+ if (dir == 0) {
+ /*
+ * Mute.
+ * Use mixer_ioctl() for writing. It does many things for us.
+ */
+ ct.type = AUDIO_MIXER_ENUM;
+ error = sc->hw_if->get_port(sc->hw_hdl, &ct);
+ if (error != 0) {
+ DPRINTF(("wskbd_set_mixervolume:"
+ " get_port: %d\n", error));
+ return (error);
+ }
+
+ ct.un.ord = !ct.un.ord; /* toggle */
+
+ error = mixer_ioctl(MIXER_DEVICE,
+ AUDIO_MIXER_WRITE, (caddr_t)&ct, FWRITE, curproc);
+ if (error != 0) {
+ DPRINTF(("wskbd_set_mixervolume:"
+ " mixer_ioctl: %d\n", error));
+ return (error);
+ }
+ } else {
+ mi.index = ct.dev;
+ error = sc->hw_if->query_devinfo(sc->hw_hdl, &mi);
+ if (error != 0) {
+ DPRINTF(("wskbd_set_mixervolume:"
+ " query_devinfo: %d\n", error));
+ return (error);
+ }
+
+ ct.type = AUDIO_MIXER_VALUE;
+
+ error = au_get_lr_value(sc, &ct, &l, &r);
+ if (error != 0) {
+ DPRINTF(("wskbd_set_mixervolume:"
+ " au_get_lr_value: %d\n", error));
+ return (error);
+ }
+
+ if (dir > 0) {
+ /*
+ * Raise volume
+ */
+ if (l > AUDIO_MAX_GAIN - mi.un.v.delta)
+ l = AUDIO_MAX_GAIN;
+ else
+ l += mi.un.v.delta;
+
+ if (r > AUDIO_MAX_GAIN - mi.un.v.delta)
+ r = AUDIO_MAX_GAIN;
+ else
+ r += mi.un.v.delta;
+
+ } else {
+ /*
+ * Lower volume
+ */
+ if (l < AUDIO_MIN_GAIN + mi.un.v.delta)
+ l = AUDIO_MIN_GAIN;
+ else
+ l -= mi.un.v.delta;
+
+ if (r < AUDIO_MIN_GAIN + mi.un.v.delta)
+ r = AUDIO_MIN_GAIN;
+ else
+ r -= mi.un.v.delta;
+ }
+
+ error = au_set_lr_value(sc, &ct, l, r);
+ if (error != 0) {
+ DPRINTF(("wskbd_set_mixervolume:"
+ " au_set_lr_value: %d\n", error));
+ return (error);
+ }
+ }
+
+ return (0);
+}
+#endif /* NAUDIO > 0 && NWSKBD > 0 */