mixer

FreeBSD OSS mixer library implementation and a complete rewrite of mixer(8)
git clone git://git.margiolis.net/mixer.git
Log | Files | Refs | README

commit 5f4e95638a4240d096c78dd739bb2381fb0db381
parent 4a0eb57baa1b67bf889bf03df2570a42ce66ab97
Author: Christos Margiolis <christos@margiolis.net>
Date:   Wed, 21 Jul 2021 01:14:55 +0300

sound(4): added hans' changes

Diffstat:
Mdiff/mixer_kern.diff | 255+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mmixer_lib/mixer.h | 2+-
Mmixer_prog/mixer_prog.8 | 6++++++
Mmixer_prog/mixer_prog.c | 1+
4 files changed, 200 insertions(+), 64 deletions(-)

diff --git a/diff/mixer_kern.diff b/diff/mixer_kern.diff @@ -1,10 +1,43 @@ +commit 183292e718629ceaffd37e506b226c0f65ac0e43 +Author: Hans Petter Selasky <hselasky@FreeBSD.org> +Date: Tue Jul 20 19:02:41 2021 +0200 + + Implement the SOUND_MIXER_WRITE_MUTE and SOUND_MIXER_READ_MUTE ioctl(9). + + These two ioctls are not part of the current version of OSS and were + considered obsolete. However, their behaviour is not the same as their + old one, so this implementation is specific to FreeBSD. + + Older OSS versions had the MUTE ioctls take and return an integer with + a value of 0 or 1, which meant that the _whole_ mixer is unmuted or + muted respectively. In my implementation, the ioctl takes and returns + a bitmask that tells us which devices are muted. + + This allows us to (un)mute only the devices we want, instead of the + whole mixer. The bitmask works the same way as in DEVMASK, RECMASK and + RECSRC. + + Integrated the hardware volume feature with the new mute system. + + Submitted by: Christos Margiolis <christos@freebsd.org> + Differential Revision: https://reviews.freebsd.org/D31130 + MFC after: 1 week + Sponsored by: NVIDIA Networking + diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c -index 92c5f3d613e..b84396c0b8b 100644 +index 09b0bb8ab14..89e78b036e9 100644 --- a/sys/dev/sound/pcm/mixer.c +++ b/sys/dev/sound/pcm/mixer.c -@@ -58,9 +58,11 @@ struct snd_mixer { +@@ -51,16 +51,16 @@ struct snd_mixer { + KOBJ_FIELDS; + void *devinfo; + int busy; +- int hwvol_muted; + int hwvol_mixer; + int hwvol_step; + int type; device_t dev; - u_int32_t hwvol_mute_level; +- u_int32_t hwvol_mute_level; u_int32_t devs; + u_int32_t mutedevs; u_int32_t recdevs; @@ -14,7 +47,16 @@ index 92c5f3d613e..b84396c0b8b 100644 u_int8_t parent[32]; u_int32_t child[32]; u_int8_t realdev[32]; -@@ -254,7 +256,7 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) +@@ -244,7 +244,7 @@ mixer_set_eq(struct snd_mixer *m, struct snddev_info *d, + } + + static int +-mixer_set(struct snd_mixer *m, u_int dev, u_int lev) ++mixer_set(struct snd_mixer *m, u_int dev, u_int32_t muted, u_int lev) + { + struct snddev_info *d; + u_int l, r, tl, tr; +@@ -254,7 +254,7 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || (0 == (m->devs & (1 << dev)))) @@ -23,7 +65,7 @@ index 92c5f3d613e..b84396c0b8b 100644 l = min((lev & 0x00ff), 100); r = min(((lev & 0xff00) >> 8), 100); -@@ -262,7 +264,7 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) +@@ -262,7 +262,7 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) d = device_get_softc(m->dev); if (d == NULL) @@ -32,21 +74,19 @@ index 92c5f3d613e..b84396c0b8b 100644 /* It is safe to drop this mutex due to Giant. */ if (!(d->flags & SD_F_MPSAFE) && mtx_owned(m->lock) != 0) -@@ -272,6 +274,13 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) - - MIXER_SET_UNLOCK(m, dropmtx); +@@ -270,6 +270,11 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) + else + dropmtx = 0; + /* Allow the volume to be "changed" while muted. */ -+ if (m->mutedevs & (1 << dev)) { ++ if (muted & (1 << dev)) { + m->level_muted[dev] = l | (r << 8); -+ MIXER_SET_LOCK(m, dropmtx); + return (0); + } -+ + MIXER_SET_UNLOCK(m, dropmtx); + /* TODO: recursive handling */ - parent = m->parent[dev]; - if (parent >= SOUND_MIXER_NRDEVICES) -@@ -287,7 +296,7 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) +@@ -287,7 +292,7 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) else if (realdev != SOUND_MIXER_NONE && MIXER_SET(m, realdev, tl, tr) < 0) { MIXER_SET_LOCK(m, dropmtx); @@ -55,7 +95,7 @@ index 92c5f3d613e..b84396c0b8b 100644 } } else if (child != 0) { for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { -@@ -305,8 +314,8 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) +@@ -305,8 +310,8 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) realdev = m->realdev[dev]; if (realdev != SOUND_MIXER_NONE && MIXER_SET(m, realdev, l, r) < 0) { @@ -66,7 +106,7 @@ index 92c5f3d613e..b84396c0b8b 100644 } } else { if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) -@@ -317,7 +326,7 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) +@@ -317,7 +322,7 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) else if (realdev != SOUND_MIXER_NONE && MIXER_SET(m, realdev, l, r) < 0) { MIXER_SET_LOCK(m, dropmtx); @@ -75,7 +115,7 @@ index 92c5f3d613e..b84396c0b8b 100644 } } -@@ -326,16 +335,66 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) +@@ -326,16 +331,42 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) m->level[dev] = l | (r << 8); m->modify_counter++; @@ -88,65 +128,42 @@ index 92c5f3d613e..b84396c0b8b 100644 { - if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) - return mixer->level[dev]; +- else +- return -1; + if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) { + if (mixer->mutedevs & (1 << dev)) + return (mixer->level_muted[dev]); + else + return (mixer->level[dev]); -+ } - else -- return -1; ++ } else { + return (-1); ++ } +} + -+static int -+mixer_setmute(struct snd_mixer *mixer, u_int32_t mutedevs) ++void ++mix_setmutedevs(struct snd_mixer *mixer, u_int32_t mutedevs) +{ -+ struct snddev_info *d; -+ int dropmtx, i, ret = 0, v; -+ -+ d = device_get_softc(mixer->dev); -+ if (d == NULL) -+ return (-1); -+ if (!(d->flags & SD_F_MPSAFE) && mtx_owned(mixer->lock) != 0) -+ dropmtx = 1; -+ else -+ dropmtx = 0; -+ -+ /* FIXME: lock/unlock */ ++ u_int32_t delta; + + /* Filter out invalid values. */ + mutedevs &= mixer->devs; ++ delta = (mixer->mutedevs ^ mutedevs) & mixer->devs; ++ mixer->mutedevs = mutedevs; ++ + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { -+ if (!(mixer->devs & (1 << i))) ++ if (!(delta & (1 << i))) + continue; -+ if ((mutedevs & (1 << i)) && mixer->level[i] != 0) { ++ if (mutedevs & (1 << i)) { + mixer->level_muted[i] = mixer->level[i]; -+ v = 0; -+ } else if (!(mutedevs & (1 << i)) && mixer->level[i] == 0) { -+ /* -+ * `mixer_set` checks if the device is muted and, if -+ * yes, assigns the volume to `level_muted` and returns. -+ * In case of an unmute, we'll first UNSET the mute bit -+ * so that we bypass this check. -+ * -+ * This is probably a dirty hack, but assigning the -+ * flags before the loop would cause the same effect, -+ * this time when muting. -+ */ -+ mixer->mutedevs &= ~(1 << i); -+ v = mixer->level_muted[i]; -+ } else -+ continue; -+ ret += mixer_set(mixer, i, v); ++ mixer_set(mixer, i, 0, 0); ++ } else { ++ mixer_set(mixer, i, 0, mixer->level_muted[i]); ++ } + } -+ mixer->mutedevs = mutedevs; -+ -+ return (ret); } static int -@@ -598,6 +657,12 @@ mix_getdevs(struct snd_mixer *m) +@@ -598,6 +629,12 @@ mix_getdevs(struct snd_mixer *m) return m->devs; } @@ -159,7 +176,101 @@ index 92c5f3d613e..b84396c0b8b 100644 u_int32_t mix_getrecdevs(struct snd_mixer *m) { -@@ -1305,10 +1370,16 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, +@@ -721,7 +758,7 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo) + } + } + +- mixer_set(m, i, v | (v << 8)); ++ mixer_set(m, i, 0, v | (v << 8)); + } + + mixer_setrecsrc(m, 0); /* Set default input. */ +@@ -799,7 +836,7 @@ mixer_uninit(device_t dev) + snd_mtxlock(m->lock); + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) +- mixer_set(m, i, 0); ++ mixer_set(m, i, 0, 0); + + mixer_setrecsrc(m, SOUND_MASK_MIC); + +@@ -836,8 +873,12 @@ mixer_reinit(device_t dev) + return i; + } + +- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) +- mixer_set(m, i, m->level[i]); ++ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { ++ if (m->mutedevs & (1 << i)) ++ mixer_set(m, i, 0, 0); ++ else ++ mixer_set(m, i, 0, m->level[i]); ++ } + + mixer_setrecsrc(m, m->recsrc); + snd_mtxunlock(m->lock); +@@ -863,10 +904,8 @@ sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) + if (dev == -1) { + snd_mtxunlock(m->lock); + return EINVAL; +- } +- else if (dev != m->hwvol_mixer) { ++ } else { + m->hwvol_mixer = dev; +- m->hwvol_muted = 0; + } + } + snd_mtxunlock(m->lock); +@@ -897,14 +936,7 @@ mixer_hwvol_init(device_t dev) + void + mixer_hwvol_mute_locked(struct snd_mixer *m) + { +- if (m->hwvol_muted) { +- m->hwvol_muted = 0; +- mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level); +- } else { +- m->hwvol_muted++; +- m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer); +- mixer_set(m, m->hwvol_mixer, 0); +- } ++ mix_setmutedevs(m, m->mutedevs ^ (1 << m->hwvol_mixer)); + } + + void +@@ -925,11 +957,8 @@ mixer_hwvol_step_locked(struct snd_mixer *m, int left_step, int right_step) + { + int level, left, right; + +- if (m->hwvol_muted) { +- m->hwvol_muted = 0; +- level = m->hwvol_mute_level; +- } else +- level = mixer_get(m, m->hwvol_mixer); ++ level = mixer_get(m, m->hwvol_mixer); ++ + if (level != -1) { + left = level & 0xff; + right = (level >> 8) & 0xff; +@@ -943,7 +972,8 @@ mixer_hwvol_step_locked(struct snd_mixer *m, int left_step, int right_step) + right = 0; + else if (right > 100) + right = 100; +- mixer_set(m, m->hwvol_mixer, left | right << 8); ++ ++ mixer_set(m, m->hwvol_mixer, m->mutedevs, left | right << 8); + } + } + +@@ -976,7 +1006,7 @@ mix_set(struct snd_mixer *m, u_int dev, u_int left, u_int right) + KASSERT(m != NULL, ("NULL snd_mixer")); + + snd_mtxlock(m->lock); +- ret = mixer_set(m, dev, left | (right << 8)); ++ ret = mixer_set(m, dev, m->mutedevs, left | (right << 8)); + snd_mtxunlock(m->lock); + + return ((ret != 0) ? ENXIO : 0); +@@ -1304,10 +1334,18 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, goto done; } if ((cmd & ~0xff) == MIXER_WRITE(0)) { @@ -168,17 +279,20 @@ index 92c5f3d613e..b84396c0b8b 100644 + case SOUND_MIXER_RECSRC: ret = mixer_setrecsrc(m, *arg_i); - else +- ret = mixer_set(m, j, *arg_i); + break; + case SOUND_MIXER_MUTE: -+ ret = mixer_setmute(m, *arg_i); ++ mix_setmutedevs(m, *arg_i); ++ ret = 0; + break; + default: - ret = mixer_set(m, j, *arg_i); ++ ret = mixer_set(m, j, m->mutedevs, *arg_i); ++ break; + } snd_mtxunlock(m->lock); return ((ret == 0) ? 0 : ENXIO); } -@@ -1319,6 +1390,9 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, +@@ -1318,6 +1356,9 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, case SOUND_MIXER_STEREODEVS: v = mix_getdevs(m); break; @@ -188,15 +302,30 @@ index 92c5f3d613e..b84396c0b8b 100644 case SOUND_MIXER_RECMASK: v = mix_getrecdevs(m); break; +@@ -1326,6 +1367,7 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, + break; + default: + v = mixer_get(m, j); ++ break; + } + *arg_i = v; + snd_mtxunlock(m->lock); +@@ -1554,5 +1596,5 @@ mix_set_locked(struct snd_mixer *m, u_int dev, int left, int right) + + level = (left & 0xFF) | ((right & 0xFF) << 8); + +- return (mixer_set(m, dev, level)); ++ return (mixer_set(m, dev, m->mutedevs, level)); + } diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h -index 8e11d553a3e..626282c803e 100644 +index 8e11d553a3e..7857609b289 100644 --- a/sys/dev/sound/pcm/mixer.h +++ b/sys/dev/sound/pcm/mixer.h @@ -60,8 +60,10 @@ device_t mix_get_dev(struct snd_mixer *m); void mix_setdevs(struct snd_mixer *m, u_int32_t v); void mix_setrecdevs(struct snd_mixer *m, u_int32_t v); -+//void mix_setmutedevs(struct snd_mixer *m, u_int32_t mutedevs); ++void mix_setmutedevs(struct snd_mixer *m, u_int32_t v); u_int32_t mix_getdevs(struct snd_mixer *m); u_int32_t mix_getrecdevs(struct snd_mixer *m); +u_int32_t mix_getmutedevs(struct snd_mixer *m); diff --git a/mixer_lib/mixer.h b/mixer_lib/mixer.h @@ -55,7 +55,7 @@ struct mix_dev { }; struct mixer { - TAILQ_HEAD(head, mix_dev) devs; + TAILQ_HEAD(, mix_dev) devs; struct mix_dev *dev; oss_mixerinfo mi; oss_card_info ci; diff --git a/mixer_prog/mixer_prog.8 b/mixer_prog/mixer_prog.8 @@ -205,6 +205,12 @@ volume by 0.05: .It $ mixer mic.volume=+0.10:-0.05 .El .Pp +Toggle the mute for +.Ar vol : +.Bl -tag -width Ds -offset indent +.It $ mixer vol.mute=^ +.El +.Pp Set .Ar mic and toggle diff --git a/mixer_prog/mixer_prog.c b/mixer_prog/mixer_prog.c @@ -205,6 +205,7 @@ printall(struct mixer *m, int oflag) } } +/* TODO: print play/rec */ static void printminfo(struct mixer *m, int oflag) {