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 4476aa86fe55ccbf2b34c84a194dbeb82331ac03
parent b47986a3584d1e38c6c8ff8a5ff47fbd580308bc
Author: Christos Margiolis <christos@margiolis.net>
Date:   Sat,  3 Jul 2021 00:41:13 +0300

implemented mute ioctl in the kernel

Diffstat:
Adiff/mixer_kern.diff | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adiff/mixer_rc.diff | 8++++++++
Mmixer_lib/mixer.c | 50++++++++++++++++++++++++++++++++++++++++----------
Mmixer_lib/mixer.h | 11+++++++++--
Mmixer_prog/mixer_prog.c | 4++++
Drc.d/mixer | 104-------------------------------------------------------------------------------
Drc.d/mixer.old | 104-------------------------------------------------------------------------------
7 files changed, 195 insertions(+), 220 deletions(-)

diff --git a/diff/mixer_kern.diff b/diff/mixer_kern.diff @@ -0,0 +1,134 @@ +diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c +index 92c5f3d613e..a15b59eb47f 100644 +--- a/sys/dev/sound/pcm/mixer.c ++++ b/sys/dev/sound/pcm/mixer.c +@@ -58,9 +58,11 @@ struct snd_mixer { + device_t dev; + u_int32_t hwvol_mute_level; + u_int32_t devs; ++ u_int32_t mutedevs; + u_int32_t recdevs; + u_int32_t recsrc; + u_int16_t level[32]; ++ u_int16_t level_muted[32]; + u_int8_t parent[32]; + u_int32_t child[32]; + u_int8_t realdev[32]; +@@ -338,6 +340,40 @@ mixer_get(struct snd_mixer *mixer, int dev) + return -1; + } + ++static int ++mixer_setmute(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 */ ++ ++ mutedevs &= mixer->devs; /* Filter out invalid values. */ ++ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { ++ if (!(mixer->devs & (1 << i))) ++ continue; ++ if ((mutedevs & (1 << i)) && mixer->level[i] != 0) { ++ mixer->level_muted[i] = mixer->level[i]; ++ v = 0; ++ } else if (!(mutedevs & (1 << i)) && mixer->level[i] == 0) ++ v = mixer->level_muted[i]; ++ else ++ continue; ++ ret += mixer_set(mixer, i, v); ++ } ++ mixer->mutedevs = mutedevs; ++ ++ return (ret); ++} ++ + static int + mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src) + { +@@ -598,6 +634,12 @@ mix_getdevs(struct snd_mixer *m) + return m->devs; + } + ++u_int32_t ++mix_getmutedevs(struct snd_mixer *m) ++{ ++ return m->mutedevs; ++} ++ + u_int32_t + mix_getrecdevs(struct snd_mixer *m) + { +@@ -711,6 +753,7 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo) + if (m == NULL) + return (-1); + ++ m->mutedevs = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + v = snd_mixerdefaults[i]; + +@@ -722,6 +765,11 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo) + } + + mixer_set(m, i, v | (v << 8)); ++ /* ++ * Make sure we don't use an uninitialized value in ++ * `mixer_setmute`. ++ */ ++ m->level_muted[i] = 0; + } + + mixer_setrecsrc(m, 0); /* Set default input. */ +@@ -1305,10 +1353,16 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, + goto done; + } + if ((cmd & ~0xff) == MIXER_WRITE(0)) { +- if (j == SOUND_MIXER_RECSRC) ++ switch (j) { ++ case SOUND_MIXER_RECSRC: + ret = mixer_setrecsrc(m, *arg_i); +- else ++ break; ++ case SOUND_MIXER_MUTE: ++ ret = mixer_setmute(m, *arg_i); ++ break; ++ default: + ret = mixer_set(m, j, *arg_i); ++ } + snd_mtxunlock(m->lock); + return ((ret == 0) ? 0 : ENXIO); + } +@@ -1319,6 +1373,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; ++ case SOUND_MIXER_MUTE: ++ v = mix_getmutedevs(m); ++ break; + case SOUND_MIXER_RECMASK: + v = mix_getrecdevs(m); + break; +diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h +index 8e11d553a3e..6d7d861e8a6 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); ++// XXX: int mix_setmutedevs(struct snd_mixer *m, u_int32_t mutedevs); + 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); + void mix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs); + void mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev); + u_int32_t mix_getparent(struct snd_mixer *m, u_int32_t dev); diff --git a/diff/mixer_rc.diff b/diff/mixer_rc.diff @@ -0,0 +1,8 @@ +61c61 +< /usr/sbin/mixer_prog -f ${dev} -o > /var/db/${1}-state 2>/dev/null +--- +> /usr/sbin/mixer -f ${dev} -s > /var/db/${1}-state 2>/dev/null +75c75 +< /usr/sbin/mixer_prog -f ${dev} `cat ${file}` > /dev/null +--- +> /usr/sbin/mixer -f ${dev} `cat ${file}` > /dev/null diff --git a/mixer_lib/mixer.c b/mixer_lib/mixer.c @@ -87,6 +87,7 @@ dunit: if (ioctl(m->fd, SNDCTL_MIXERINFO, &m->mi) < 0 || ioctl(m->fd, SNDCTL_CARDINFO, &m->ci) < 0 || ioctl(m->fd, SOUND_MIXER_READ_DEVMASK, &m->devmask) < 0 || + ioctl(m->fd, SOUND_MIXER_READ_MUTE, &m->mutemask) < 0 || ioctl(m->fd, SOUND_MIXER_READ_RECMASK, &m->recmask) < 0 || ioctl(m->fd, SOUND_MIXER_READ_RECSRC, &m->recsrc) < 0) goto fail; @@ -100,23 +101,21 @@ dunit: if ((dp = calloc(1, sizeof(struct mix_dev))) == NULL) goto fail; dp->devno = i; + + /* XXX: Make this a seperate function? */ dp->lvol = M_VOLNORM(v & 0x7f); dp->rvol = M_VOLNORM((v >> 8) & 0x7f); dp->pan = dp->rvol - dp->lvol; - /* - * TODO: find a way to know if it's already muted - * or not, this doesn't make sense - */ - dp->lmute = 0; - dp->rmute = 0; - /* XXX: is this correct? */ - dp->f_pbk = !M_ISREC(m, i); - dp->f_rec = M_ISREC(m, i); - dp->f_src = M_ISRECSRC(m, i); + + dp->f_mut = M_ISMUTE(m, i) ? 1 : 0; + dp->f_pbk = !M_ISREC(m, i) ? 1 : 0; + dp->f_rec = M_ISREC(m, i) ? 1 : 0; + dp->f_src = M_ISRECSRC(m, i) ? 1 : 0; (void)strlcpy(dp->name, names[i], sizeof(dp->name)); TAILQ_INSERT_TAIL(&m->devs, dp, devs); ndev++; } + /* The default device is always "vol". */ m->dev = TAILQ_FIRST(&m->devs); @@ -226,6 +225,37 @@ mixer_setpan(struct mixer *m, float pan) return (mixer_setvol(m, l, r)); } +int +mixer_setmute(struct mixer *m, int opt) +{ + int v; + + switch (opt) { + case M_MUTE: + m->mutemask |= (1 << m->dev->devno); + break; + case M_UNMUTE: + m->mutemask &= ~(1 << m->dev->devno); + break; + default: + errno = EINVAL; + return (-1); + } + if (ioctl(m->fd, SOUND_MIXER_WRITE_MUTE, &m->mutemask) < 0) + return (-1); + if (ioctl(m->fd, SOUND_MIXER_READ_MUTE, &m->mutemask) < 0) + return (-1); + /* Update the volume. */ + if (ioctl(m->fd, MIXER_READ(m->dev->devno), &v) < 0) + return (-1); + m->dev->lvol = M_VOLNORM(v & 0x7f); + m->dev->rvol = M_VOLNORM((v >> 8) & 0x7f); + m->dev->pan = m->dev->rvol - m->dev->lvol; + m->dev->f_mut = M_ISMUTE(m, m->dev->devno); + + return 0; +} + /* * Modify the mixer's selected device flags. The `recsrc` flag tells * us if a device is a recording source. diff --git a/mixer_lib/mixer.h b/mixer_lib/mixer.h @@ -39,6 +39,9 @@ __FBSDID("$FreeBSD$"); #define M_SETRECDEV 0x04 #define M_TOGGLERECDEV 0x08 +#define M_MUTE 0x01 +#define M_UNMUTE 0x02 + #define M_VOLMIN 0.0f #define M_VOLMAX 1.0f #define M_VOLNORM(v) ((v) / 100.0f) @@ -49,6 +52,7 @@ __FBSDID("$FreeBSD$"); #define M_ISSET(n, f) ((1 << (n)) & (f)) #define M_ISDEV(m, n) M_ISSET(n, (m)->devmask) +#define M_ISMUTE(m, n) M_ISSET(n, (m)->mutemask) #define M_ISREC(m, n) M_ISSET(n, (m)->recmask) #define M_ISRECSRC(m, n) M_ISSET(n, (m)->recsrc) @@ -58,10 +62,9 @@ struct mix_dev { float lvol; float rvol; float pan; - int lmute; - int rmute; //int rate; //int samples; + int f_mut; int f_pbk; int f_rec; int f_src; @@ -77,17 +80,21 @@ struct mixer { int fd; int unit; int devmask; + int mutemask; int recmask; int recsrc; int f_default; }; +/* XXX: move control handling here? */ + struct mixer *mixer_open(const char *); int mixer_close(struct mixer *); struct mix_dev *mixer_getdev(struct mixer *, int); struct mix_dev *mixer_getdevbyname(struct mixer *, const char *); int mixer_setvol(struct mixer *, float, float); int mixer_setpan(struct mixer *, float); +int mixer_setmute(struct mixer *, int); int mixer_modrecsrc(struct mixer *, int); int mixer_getdunit(void); int mixer_setdunit(struct mixer *, int); diff --git a/mixer_prog/mixer_prog.c b/mixer_prog/mixer_prog.c @@ -33,6 +33,7 @@ #define CTRL_VOL 0 #define CTRL_SRC 1 +#define CTRL_MUT 2 struct ctrl { char name[NAME_MAX]; @@ -55,6 +56,7 @@ static void usage(void) __dead2; static const struct ctrl ctrls[] = { [CTRL_VOL] = { "volume", modvol, printvol }, [CTRL_SRC] = { "recsrc", modrecsrc, printrec }, + /*[CTRL_MUT] = { "mute", modmute, printmute },*/ }; static void @@ -91,6 +93,8 @@ printdev(struct mix_dev *d, int oflag) printf(" rec"); if (d->f_src) printf(" src"); + if (d->f_mut) + printf(" mute"); printf("\n"); } else { printf("%s.%s=%.2f:%.2f\n", diff --git a/rc.d/mixer b/rc.d/mixer @@ -1,104 +0,0 @@ -#!/bin/sh - -# -# Copyright (c) 2004 The FreeBSD Project -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD: releng/12.2/libexec/rc/rc.d/mixer 298514 2016-04-23 16:10:54Z lme $ -# - -# PROVIDE: mixer -# REQUIRE: FILESYSTEMS -# KEYWORD: nojail shutdown - -. /etc/rc.subr - -name="mixer" -desc="Save and restore soundcard mixer values" -rcvar="mixer_enable" -stop_cmd="mixer_stop" -start_cmd="mixer_start" -reload_cmd="mixer_start" -extra_commands="reload" - -# -# List current mixer devices to stdout. -# -list_mixers() -{ - ( cd /dev ; ls mixer* 2>/dev/null ) -} - -# -# Save state of an individual mixer specified as $1 -# -mixer_save() -{ - local dev - - dev="/dev/${1}" - if [ -r ${dev} ]; then - /usr/sbin/mixer_prog -f ${dev} -o > /var/db/${1}-state 2>/dev/null - fi -} - -# -# Restore the state of an individual mixer specified as $1 -# -mixer_restore() -{ - local file dev - - dev="/dev/${1}" - file="/var/db/${1}-state" - if [ -r ${dev} -a -r ${file} ]; then - /usr/sbin/mixer_prog -f ${dev} `cat ${file}` > /dev/null - fi -} - -# -# Restore state of all mixers -# -mixer_start() -{ - local mixer - - for mixer in `list_mixers`; do - mixer_restore ${mixer} - done -} - -# -# Save the state of all mixers -# -mixer_stop() -{ - local mixer - - for mixer in `list_mixers`; do - mixer_save ${mixer} - done -} - -load_rc_config $name -run_rc_command "$1" diff --git a/rc.d/mixer.old b/rc.d/mixer.old @@ -1,104 +0,0 @@ -#!/bin/sh - -# -# Copyright (c) 2004 The FreeBSD Project -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD$ -# - -# PROVIDE: mixer -# REQUIRE: FILESYSTEMS -# KEYWORD: nojail shutdown - -. /etc/rc.subr - -name="mixer" -desc="Save and restore soundcard mixer values" -rcvar="mixer_enable" -stop_cmd="mixer_stop" -start_cmd="mixer_start" -reload_cmd="mixer_start" -extra_commands="reload" - -# -# List current mixer devices to stdout. -# -list_mixers() -{ - ( cd /dev ; ls mixer* 2>/dev/null ) -} - -# -# Save state of an individual mixer specified as $1 -# -mixer_save() -{ - local dev - - dev="/dev/${1}" - if [ -r ${dev} ]; then - /usr/sbin/mixer -f ${dev} -s > /var/db/${1}-state 2>/dev/null - fi -} - -# -# Restore the state of an individual mixer specified as $1 -# -mixer_restore() -{ - local file dev - - dev="/dev/${1}" - file="/var/db/${1}-state" - if [ -r ${dev} -a -r ${file} ]; then - /usr/sbin/mixer -f ${dev} `cat ${file}` > /dev/null - fi -} - -# -# Restore state of all mixers -# -mixer_start() -{ - local mixer - - for mixer in `list_mixers`; do - mixer_restore ${mixer} - done -} - -# -# Save the state of all mixers -# -mixer_stop() -{ - local mixer - - for mixer in `list_mixers`; do - mixer_save ${mixer} - done -} - -load_rc_config $name -run_rc_command "$1"