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 96de46140806d36883615b001acff89452c1bd8d
parent 918ef4d18d728eb4f64c244d6ccc961c04074f23
Author: Christos Margiolis <christos@margiolis.net>
Date:   Tue, 27 Jul 2021 15:33:39 +0300

sound(4): implement playback/recording status sysctl

Diffstat:
Mdiff/mixer_kern.diff | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mmixer_lib/mixer.c | 22+++++++++++++++++++++-
Mmixer_lib/mixer.h | 5+++++
Mmixer_prog/mixer_prog.c | 8+++++++-
4 files changed, 118 insertions(+), 43 deletions(-)

diff --git a/diff/mixer_kern.diff b/diff/mixer_kern.diff @@ -1,31 +1,5 @@ -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 09b0bb8ab14..89e78b036e9 100644 +index 92c5f3d613e..8275d1a2684 100644 --- a/sys/dev/sound/pcm/mixer.c +++ b/sys/dev/sound/pcm/mixer.c @@ -51,16 +51,16 @@ struct snd_mixer { @@ -115,7 +89,7 @@ index 09b0bb8ab14..89e78b036e9 100644 } } -@@ -326,16 +331,42 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) +@@ -326,16 +331,43 @@ mixer_set(struct snd_mixer *m, u_int dev, u_int lev) m->level[dev] = l | (r << 8); m->modify_counter++; @@ -143,6 +117,7 @@ index 09b0bb8ab14..89e78b036e9 100644 +void +mix_setmutedevs(struct snd_mixer *mixer, u_int32_t mutedevs) +{ ++ int i; + u_int32_t delta; + + /* Filter out invalid values. */ @@ -163,7 +138,7 @@ index 09b0bb8ab14..89e78b036e9 100644 } static int -@@ -598,6 +629,12 @@ mix_getdevs(struct snd_mixer *m) +@@ -598,6 +630,12 @@ mix_getdevs(struct snd_mixer *m) return m->devs; } @@ -176,7 +151,7 @@ index 09b0bb8ab14..89e78b036e9 100644 u_int32_t mix_getrecdevs(struct snd_mixer *m) { -@@ -721,7 +758,7 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo) +@@ -721,7 +759,7 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo) } } @@ -185,7 +160,7 @@ index 09b0bb8ab14..89e78b036e9 100644 } mixer_setrecsrc(m, 0); /* Set default input. */ -@@ -799,7 +836,7 @@ mixer_uninit(device_t dev) +@@ -799,7 +837,7 @@ mixer_uninit(device_t dev) snd_mtxlock(m->lock); for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) @@ -194,7 +169,7 @@ index 09b0bb8ab14..89e78b036e9 100644 mixer_setrecsrc(m, SOUND_MASK_MIC); -@@ -836,8 +873,12 @@ mixer_reinit(device_t dev) +@@ -836,8 +874,12 @@ mixer_reinit(device_t dev) return i; } @@ -209,7 +184,7 @@ index 09b0bb8ab14..89e78b036e9 100644 mixer_setrecsrc(m, m->recsrc); snd_mtxunlock(m->lock); -@@ -863,10 +904,8 @@ sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) +@@ -863,10 +905,8 @@ sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) if (dev == -1) { snd_mtxunlock(m->lock); return EINVAL; @@ -221,7 +196,7 @@ index 09b0bb8ab14..89e78b036e9 100644 } } snd_mtxunlock(m->lock); -@@ -897,14 +936,7 @@ mixer_hwvol_init(device_t dev) +@@ -897,14 +937,7 @@ mixer_hwvol_init(device_t dev) void mixer_hwvol_mute_locked(struct snd_mixer *m) { @@ -237,7 +212,7 @@ index 09b0bb8ab14..89e78b036e9 100644 } void -@@ -925,11 +957,8 @@ mixer_hwvol_step_locked(struct snd_mixer *m, int left_step, int right_step) +@@ -925,11 +958,8 @@ mixer_hwvol_step_locked(struct snd_mixer *m, int left_step, int right_step) { int level, left, right; @@ -251,7 +226,7 @@ index 09b0bb8ab14..89e78b036e9 100644 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) +@@ -943,7 +973,8 @@ mixer_hwvol_step_locked(struct snd_mixer *m, int left_step, int right_step) right = 0; else if (right > 100) right = 100; @@ -261,7 +236,7 @@ index 09b0bb8ab14..89e78b036e9 100644 } } -@@ -976,7 +1006,7 @@ mix_set(struct snd_mixer *m, u_int dev, u_int left, u_int right) +@@ -976,7 +1007,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); @@ -270,7 +245,7 @@ index 09b0bb8ab14..89e78b036e9 100644 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, +@@ -1305,10 +1336,18 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, goto done; } if ((cmd & ~0xff) == MIXER_WRITE(0)) { @@ -292,7 +267,7 @@ index 09b0bb8ab14..89e78b036e9 100644 snd_mtxunlock(m->lock); return ((ret == 0) ? 0 : ENXIO); } -@@ -1318,6 +1356,9 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, +@@ -1319,6 +1358,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; @@ -302,7 +277,7 @@ index 09b0bb8ab14..89e78b036e9 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, +@@ -1327,6 +1369,7 @@ mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, break; default: v = mixer_get(m, j); @@ -310,7 +285,7 @@ index 09b0bb8ab14..89e78b036e9 100644 } *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) +@@ -1555,5 +1598,5 @@ mix_set_locked(struct snd_mixer *m, u_int dev, int left, int right) level = (left & 0xFF) | ((right & 0xFF) << 8); @@ -332,3 +307,72 @@ index 8e11d553a3e..7857609b289 100644 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/sound.c b/sys/dev/sound/pcm/sound.c +index b4be28aeff8..2638885d431 100644 +--- a/sys/dev/sound/pcm/sound.c ++++ b/sys/dev/sound/pcm/sound.c +@@ -1012,12 +1012,28 @@ SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RWTUN, + "global clone garbage collector"); + #endif + ++static u_int8_t ++pcm_status_init(struct snddev_info *d) ++{ ++ u_int8_t status = PCM_STATUS_NONE; ++ ++ if (d->playcount > 0) ++ status |= PCM_STATUS_PLAY; ++ if (d->reccount > 0) ++ status |= PCM_STATUS_REC; ++ ++ return (status); ++} ++ + static void + pcm_sysinit(device_t dev) + { + struct snddev_info *d = device_get_softc(dev); ++ u_int8_t status; ++ ++ status = pcm_status_init(d); + +- /* XXX: an user should be able to set this with a control tool, the ++ /* XXX: a user should be able to set this with a control tool, the + sysadmin then needs min+max sysctls for this */ + SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), +@@ -1027,6 +1043,10 @@ pcm_sysinit(device_t dev) + "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN, d, sizeof(d), + sysctl_dev_pcm_bitperfect, "I", + "bit-perfect playback/recording (0=disable, 1=enable)"); ++ SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), ++ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), ++ OID_AUTO, "status", CTLFLAG_RD, NULL, status, ++ "playback/recording status (0=none, 1=play, 2=rec, 3=play+rec)"); + #ifdef SND_DEBUG + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, +@@ -1130,7 +1150,7 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) + sysctl_ctx_init(&d->rec_sysctl_ctx); + d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", +- CTLFLAG_RD, 0, "record channels node"); ++ CTLFLAG_RD, 0, "recording channels node"); + + if (numplay > 0 || numrec > 0) + d->flags |= SD_F_AUTOVCHAN; +diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h +index d4b3a23e8eb..c16555f830a 100644 +--- a/sys/dev/sound/pcm/sound.h ++++ b/sys/dev/sound/pcm/sound.h +@@ -411,6 +411,10 @@ struct snddev_info { + void sound_oss_sysinfo(oss_sysinfo *); + int sound_oss_card_info(oss_card_info *); + ++#define PCM_STATUS_NONE 0x00 ++#define PCM_STATUS_PLAY 0x01 ++#define PCM_STATUS_REC 0x02 ++ + #define PCM_LOCKOWNED(d) mtx_owned((d)->lock) + #define PCM_LOCK(d) mtx_lock((d)->lock) + #define PCM_UNLOCK(d) mtx_unlock((d)->lock) diff --git a/mixer_lib/mixer.c b/mixer_lib/mixer.c @@ -96,6 +96,7 @@ dunit: m->devmask = m->recmask = m->recsrc = 0; m->f_default = m->unit == mixer_getdunit(); + m->status = mixer_getstatus(m->unit); /* The unit number _must_ be set before the ioctl. */ m->mi.dev = m->unit; m->ci.card = m->unit; @@ -305,8 +306,8 @@ mixer_modrecsrc(struct mixer *m, int opt) int mixer_getdunit(void) { - int unit; size_t size; + int unit; size = sizeof(int); if (sysctlbyname("hw.snd.default_unit", &unit, &size, NULL, 0) < 0) @@ -336,6 +337,25 @@ mixer_setdunit(struct mixer *m, int unit) } /* + * Get sound device status (none, play, rec, play+rec). Userland programs can + * use the MIX_STATUS_* flags to determine the status of the device. + */ +int +mixer_getstatus(int unit) +{ + char buf[BUFSIZ]; + size_t size; + unsigned int status; + + (void)snprintf(buf, sizeof(buf) - 1, "dev.pcm.%d.status", unit); + size = sizeof(unsigned int); + if (sysctlbyname(buf, &status, &size, NULL, 0) < 0) + return (-1); + + return (status); +} + +/* * Get the total number of mixers in the system. */ int diff --git a/mixer_lib/mixer.h b/mixer_lib/mixer.h @@ -75,6 +75,10 @@ struct mixer { #define MIX_TOGGLERECSRC 0x08 int recsrc; int f_default; +#define MIX_STATUS_NONE 0x00 +#define MIX_STATUS_PLAY 0x01 +#define MIX_STATUS_REC 0x02 + int status; }; typedef struct mix_volume mix_volume_t; @@ -88,6 +92,7 @@ int mixer_setmute(struct mixer *, int); int mixer_modrecsrc(struct mixer *, int); int mixer_getdunit(void); int mixer_setdunit(struct mixer *, int); +int mixer_getstatus(int); int mixer_getnmixers(void); __END_DECLS diff --git a/mixer_prog/mixer_prog.c b/mixer_prog/mixer_prog.c @@ -205,13 +205,19 @@ printall(struct mixer *m, int oflag) } } -/* TODO: print play/rec */ static void printminfo(struct mixer *m, int oflag) { if (oflag) return; printf("%s: <%s> %s", m->mi.name, m->ci.longname, m->ci.hw_info); + /* TODO: clean up the mess */ + if (m->status & MIX_STATUS_PLAY) + printf(" (play%s", m->status & MIX_STATUS_REC ? "" : ")"); + if (m->status == (MIX_STATUS_PLAY | MIX_STATUS_REC)) + printf("/"); + if (m->status & MIX_STATUS_REC) + printf("%srec)", m->status & MIX_STATUS_PLAY ? "" : " ("); if (m->f_default) printf(" (default)"); printf("\n");