diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h --- a/sys/dev/sound/pcm/mixer.h +++ b/sys/dev/sound/pcm/mixer.h @@ -60,8 +60,10 @@ 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); 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/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c --- a/sys/dev/sound/pcm/mixer.c +++ b/sys/dev/sound/pcm/mixer.c @@ -58,9 +58,11 @@ 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]; @@ -254,7 +256,7 @@ if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || (0 == (m->devs & (1 << dev)))) - return -1; + return (-1); l = min((lev & 0x00ff), 100); r = min(((lev & 0xff00) >> 8), 100); @@ -262,7 +264,7 @@ d = device_get_softc(m->dev); if (d == NULL) - return -1; + return (-1); /* 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_UNLOCK(m, dropmtx); + /* Allow the volume to be "changed" while muted. */ + if (m->mutedevs & (1 << dev)) { + m->level_muted[dev] = l | (r << 8); + MIXER_SET_LOCK(m, dropmtx); + return (0); + } + /* TODO: recursive handling */ parent = m->parent[dev]; if (parent >= SOUND_MIXER_NRDEVICES) @@ -287,7 +296,7 @@ else if (realdev != SOUND_MIXER_NONE && MIXER_SET(m, realdev, tl, tr) < 0) { MIXER_SET_LOCK(m, dropmtx); - return -1; + return (-1); } } else if (child != 0) { for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { @@ -305,8 +314,8 @@ realdev = m->realdev[dev]; if (realdev != SOUND_MIXER_NONE && MIXER_SET(m, realdev, l, r) < 0) { - MIXER_SET_LOCK(m, dropmtx); - return -1; + MIXER_SET_LOCK(m, dropmtx); + return (-1); } } else { if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) @@ -317,7 +326,7 @@ else if (realdev != SOUND_MIXER_NONE && MIXER_SET(m, realdev, l, r) < 0) { MIXER_SET_LOCK(m, dropmtx); - return -1; + return (-1); } } @@ -326,16 +335,66 @@ m->level[dev] = l | (r << 8); m->modify_counter++; - return 0; + return (0); } static int mixer_get(struct snd_mixer *mixer, int dev) { - if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) - return mixer->level[dev]; + 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; + 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 */ + + /* Filter out invalid values. */ + mutedevs &= mixer->devs; + 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) { + /* + * `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->mutedevs = mutedevs; + + return (ret); } static int @@ -598,6 +657,12 @@ 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 +776,7 @@ if (m == NULL) return (-1); + m->mutedevs = 0; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { v = snd_mixerdefaults[i]; @@ -722,6 +788,11 @@ } 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 +1376,16 @@ 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 +1396,9 @@ 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;