1903873ceSHans Petter Selasky /*- 2903873ceSHans Petter Selasky * Copyright (c) 2021 Christos Margiolis <christos@FreeBSD.org> 3903873ceSHans Petter Selasky * 4903873ceSHans Petter Selasky * Permission is hereby granted, free of charge, to any person obtaining a copy 5903873ceSHans Petter Selasky * of this software and associated documentation files (the "Software"), to deal 6903873ceSHans Petter Selasky * in the Software without restriction, including without limitation the rights 7903873ceSHans Petter Selasky * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8903873ceSHans Petter Selasky * copies of the Software, and to permit persons to whom the Software is 9903873ceSHans Petter Selasky * furnished to do so, subject to the following conditions: 10903873ceSHans Petter Selasky * 11903873ceSHans Petter Selasky * The above copyright notice and this permission notice shall be included in 12903873ceSHans Petter Selasky * all copies or substantial portions of the Software. 13903873ceSHans Petter Selasky * 14903873ceSHans Petter Selasky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15903873ceSHans Petter Selasky * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16903873ceSHans Petter Selasky * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17903873ceSHans Petter Selasky * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18903873ceSHans Petter Selasky * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19903873ceSHans Petter Selasky * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20903873ceSHans Petter Selasky * THE SOFTWARE. 21903873ceSHans Petter Selasky * 22903873ceSHans Petter Selasky * $FreeBSD$ 23903873ceSHans Petter Selasky */ 24903873ceSHans Petter Selasky 25903873ceSHans Petter Selasky #include <sys/types.h> 26903873ceSHans Petter Selasky #include <sys/ioctl.h> 27903873ceSHans Petter Selasky #include <sys/sysctl.h> 28903873ceSHans Petter Selasky 29903873ceSHans Petter Selasky #include <errno.h> 30903873ceSHans Petter Selasky #include <fcntl.h> 31903873ceSHans Petter Selasky #include <stdio.h> 32903873ceSHans Petter Selasky #include <stdlib.h> 33903873ceSHans Petter Selasky #include <string.h> 34903873ceSHans Petter Selasky #include <unistd.h> 35903873ceSHans Petter Selasky 36903873ceSHans Petter Selasky #include "mixer.h" 37903873ceSHans Petter Selasky 38903873ceSHans Petter Selasky #define BASEPATH "/dev/mixer" 39903873ceSHans Petter Selasky 40*e5f5ca7fSChristos Margiolis static int _mixer_readvol(struct mix_dev *); 41903873ceSHans Petter Selasky 42903873ceSHans Petter Selasky /* 43903873ceSHans Petter Selasky * Fetch volume from the device. 44903873ceSHans Petter Selasky */ 45903873ceSHans Petter Selasky static int 46*e5f5ca7fSChristos Margiolis _mixer_readvol(struct mix_dev *dev) 47903873ceSHans Petter Selasky { 48903873ceSHans Petter Selasky int v; 49903873ceSHans Petter Selasky 50*e5f5ca7fSChristos Margiolis if (ioctl(dev->parent_mixer->fd, MIXER_READ(dev->devno), &v) < 0) 51903873ceSHans Petter Selasky return (-1); 52903873ceSHans Petter Selasky dev->vol.left = MIX_VOLNORM(v & 0x00ff); 53903873ceSHans Petter Selasky dev->vol.right = MIX_VOLNORM((v >> 8) & 0x00ff); 54903873ceSHans Petter Selasky 55903873ceSHans Petter Selasky return (0); 56903873ceSHans Petter Selasky } 57903873ceSHans Petter Selasky 58903873ceSHans Petter Selasky /* 59903873ceSHans Petter Selasky * Open a mixer device in `/dev/mixerN`, where N is the number of the mixer. 60903873ceSHans Petter Selasky * Each device maps to an actual pcm audio card, so `/dev/mixer0` is the 61903873ceSHans Petter Selasky * mixer for pcm0, and so on. 62903873ceSHans Petter Selasky * 63903873ceSHans Petter Selasky * @param name path to mixer device. NULL or "/dev/mixer" for the 64903873ceSHans Petter Selasky * the default mixer (i.e `hw.snd.default_unit`). 65903873ceSHans Petter Selasky */ 66903873ceSHans Petter Selasky struct mixer * 67903873ceSHans Petter Selasky mixer_open(const char *name) 68903873ceSHans Petter Selasky { 69903873ceSHans Petter Selasky struct mixer *m = NULL; 70903873ceSHans Petter Selasky struct mix_dev *dp; 71903873ceSHans Petter Selasky const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; 72903873ceSHans Petter Selasky int i; 73903873ceSHans Petter Selasky 74903873ceSHans Petter Selasky if ((m = calloc(1, sizeof(struct mixer))) == NULL) 75903873ceSHans Petter Selasky goto fail; 76903873ceSHans Petter Selasky 77903873ceSHans Petter Selasky if (name != NULL) { 78903873ceSHans Petter Selasky /* `name` does not start with "/dev/mixer". */ 79903873ceSHans Petter Selasky if (strncmp(name, BASEPATH, strlen(BASEPATH)) != 0) { 8039844001SHans Petter Selasky m->unit = -1; 8139844001SHans Petter Selasky } else { 82903873ceSHans Petter Selasky /* `name` is "/dev/mixer" so, we'll use the default unit. */ 83903873ceSHans Petter Selasky if (strncmp(name, BASEPATH, strlen(name)) == 0) 84903873ceSHans Petter Selasky goto dunit; 85903873ceSHans Petter Selasky m->unit = strtol(name + strlen(BASEPATH), NULL, 10); 8639844001SHans Petter Selasky } 87903873ceSHans Petter Selasky (void)strlcpy(m->name, name, sizeof(m->name)); 88903873ceSHans Petter Selasky } else { 89903873ceSHans Petter Selasky dunit: 90903873ceSHans Petter Selasky if ((m->unit = mixer_get_dunit()) < 0) 91903873ceSHans Petter Selasky goto fail; 92903873ceSHans Petter Selasky (void)snprintf(m->name, sizeof(m->name), "/dev/mixer%d", m->unit); 93903873ceSHans Petter Selasky } 94903873ceSHans Petter Selasky 95903873ceSHans Petter Selasky if ((m->fd = open(m->name, O_RDWR)) < 0) 96903873ceSHans Petter Selasky goto fail; 97903873ceSHans Petter Selasky 98903873ceSHans Petter Selasky m->devmask = m->recmask = m->recsrc = 0; 99903873ceSHans Petter Selasky m->f_default = m->unit == mixer_get_dunit(); 100903873ceSHans Petter Selasky m->mode = mixer_get_mode(m->unit); 101903873ceSHans Petter Selasky /* The unit number _must_ be set before the ioctl. */ 102903873ceSHans Petter Selasky m->mi.dev = m->unit; 103903873ceSHans Petter Selasky m->ci.card = m->unit; 10439844001SHans Petter Selasky if (ioctl(m->fd, SNDCTL_MIXERINFO, &m->mi) < 0) { 10539844001SHans Petter Selasky memset(&m->mi, 0, sizeof(m->mi)); 10639844001SHans Petter Selasky strlcpy(m->mi.name, m->name, sizeof(m->mi.name)); 10739844001SHans Petter Selasky } 10839844001SHans Petter Selasky if (ioctl(m->fd, SNDCTL_CARDINFO, &m->ci) < 0) 10939844001SHans Petter Selasky memset(&m->ci, 0, sizeof(m->ci)); 11039844001SHans Petter Selasky if (ioctl(m->fd, SOUND_MIXER_READ_DEVMASK, &m->devmask) < 0 || 111903873ceSHans Petter Selasky ioctl(m->fd, SOUND_MIXER_READ_MUTE, &m->mutemask) < 0 || 112903873ceSHans Petter Selasky ioctl(m->fd, SOUND_MIXER_READ_RECMASK, &m->recmask) < 0 || 113903873ceSHans Petter Selasky ioctl(m->fd, SOUND_MIXER_READ_RECSRC, &m->recsrc) < 0) 114903873ceSHans Petter Selasky goto fail; 115903873ceSHans Petter Selasky 116903873ceSHans Petter Selasky TAILQ_INIT(&m->devs); 117903873ceSHans Petter Selasky for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 118903873ceSHans Petter Selasky if (!MIX_ISDEV(m, i)) 119903873ceSHans Petter Selasky continue; 120903873ceSHans Petter Selasky if ((dp = calloc(1, sizeof(struct mix_dev))) == NULL) 121903873ceSHans Petter Selasky goto fail; 122903873ceSHans Petter Selasky dp->parent_mixer = m; 123903873ceSHans Petter Selasky dp->devno = i; 124903873ceSHans Petter Selasky dp->nctl = 0; 125*e5f5ca7fSChristos Margiolis if (_mixer_readvol(dp) < 0) 126903873ceSHans Petter Selasky goto fail; 127903873ceSHans Petter Selasky (void)strlcpy(dp->name, names[i], sizeof(dp->name)); 128903873ceSHans Petter Selasky TAILQ_INIT(&dp->ctls); 129903873ceSHans Petter Selasky TAILQ_INSERT_TAIL(&m->devs, dp, devs); 130903873ceSHans Petter Selasky m->ndev++; 131903873ceSHans Petter Selasky } 132903873ceSHans Petter Selasky 133903873ceSHans Petter Selasky /* The default device is always "vol". */ 134903873ceSHans Petter Selasky m->dev = TAILQ_FIRST(&m->devs); 135903873ceSHans Petter Selasky 136903873ceSHans Petter Selasky return (m); 137903873ceSHans Petter Selasky fail: 138903873ceSHans Petter Selasky if (m != NULL) 139903873ceSHans Petter Selasky (void)mixer_close(m); 140903873ceSHans Petter Selasky 141903873ceSHans Petter Selasky return (NULL); 142903873ceSHans Petter Selasky } 143903873ceSHans Petter Selasky 144903873ceSHans Petter Selasky /* 145903873ceSHans Petter Selasky * Free resources and close the mixer. 146903873ceSHans Petter Selasky */ 147903873ceSHans Petter Selasky int 148903873ceSHans Petter Selasky mixer_close(struct mixer *m) 149903873ceSHans Petter Selasky { 150903873ceSHans Petter Selasky struct mix_dev *dp; 151903873ceSHans Petter Selasky int r; 152903873ceSHans Petter Selasky 153903873ceSHans Petter Selasky r = close(m->fd); 154903873ceSHans Petter Selasky while (!TAILQ_EMPTY(&m->devs)) { 155903873ceSHans Petter Selasky dp = TAILQ_FIRST(&m->devs); 156903873ceSHans Petter Selasky TAILQ_REMOVE(&m->devs, dp, devs); 157903873ceSHans Petter Selasky while (!TAILQ_EMPTY(&dp->ctls)) 158903873ceSHans Petter Selasky (void)mixer_remove_ctl(TAILQ_FIRST(&dp->ctls)); 159903873ceSHans Petter Selasky free(dp); 160903873ceSHans Petter Selasky } 161903873ceSHans Petter Selasky free(m); 162903873ceSHans Petter Selasky 163903873ceSHans Petter Selasky return (r); 164903873ceSHans Petter Selasky } 165903873ceSHans Petter Selasky 166903873ceSHans Petter Selasky /* 167903873ceSHans Petter Selasky * Select a mixer device. The mixer structure keeps a list of all the devices 168903873ceSHans Petter Selasky * the mixer has, but only one can be manipulated at a time -- this is what 169903873ceSHans Petter Selasky * the `dev` in the mixer structure field is for. Each time a device is to be 170903873ceSHans Petter Selasky * manipulated, `dev` has to point to it first. 171903873ceSHans Petter Selasky * 172903873ceSHans Petter Selasky * The caller must manually assign the return value to `m->dev`. 173903873ceSHans Petter Selasky */ 174903873ceSHans Petter Selasky struct mix_dev * 175903873ceSHans Petter Selasky mixer_get_dev(struct mixer *m, int dev) 176903873ceSHans Petter Selasky { 177903873ceSHans Petter Selasky struct mix_dev *dp; 178903873ceSHans Petter Selasky 179903873ceSHans Petter Selasky if (dev < 0 || dev >= m->ndev) { 180903873ceSHans Petter Selasky errno = ERANGE; 181903873ceSHans Petter Selasky return (NULL); 182903873ceSHans Petter Selasky } 183903873ceSHans Petter Selasky TAILQ_FOREACH(dp, &m->devs, devs) { 184903873ceSHans Petter Selasky if (dp->devno == dev) 185903873ceSHans Petter Selasky return (dp); 186903873ceSHans Petter Selasky } 187903873ceSHans Petter Selasky errno = EINVAL; 188903873ceSHans Petter Selasky 189903873ceSHans Petter Selasky return (NULL); 190903873ceSHans Petter Selasky } 191903873ceSHans Petter Selasky 192903873ceSHans Petter Selasky /* 193903873ceSHans Petter Selasky * Select a device by name. 194903873ceSHans Petter Selasky * 195903873ceSHans Petter Selasky * @param name device name (e.g vol, pcm, ...) 196903873ceSHans Petter Selasky */ 197903873ceSHans Petter Selasky struct mix_dev * 198903873ceSHans Petter Selasky mixer_get_dev_byname(struct mixer *m, const char *name) 199903873ceSHans Petter Selasky { 200903873ceSHans Petter Selasky struct mix_dev *dp; 201903873ceSHans Petter Selasky 202903873ceSHans Petter Selasky TAILQ_FOREACH(dp, &m->devs, devs) { 203903873ceSHans Petter Selasky if (!strncmp(dp->name, name, sizeof(dp->name))) 204903873ceSHans Petter Selasky return (dp); 205903873ceSHans Petter Selasky } 206903873ceSHans Petter Selasky errno = EINVAL; 207903873ceSHans Petter Selasky 208903873ceSHans Petter Selasky return (NULL); 209903873ceSHans Petter Selasky } 210903873ceSHans Petter Selasky 211903873ceSHans Petter Selasky /* 212903873ceSHans Petter Selasky * Add a mixer control to a device. 213903873ceSHans Petter Selasky */ 214903873ceSHans Petter Selasky int 215903873ceSHans Petter Selasky mixer_add_ctl(struct mix_dev *parent_dev, int id, const char *name, 216903873ceSHans Petter Selasky int (*mod)(struct mix_dev *, void *), 217903873ceSHans Petter Selasky int (*print)(struct mix_dev *, void *)) 218903873ceSHans Petter Selasky { 219903873ceSHans Petter Selasky struct mix_dev *dp; 220903873ceSHans Petter Selasky mix_ctl_t *ctl, *cp; 221903873ceSHans Petter Selasky 222903873ceSHans Petter Selasky /* XXX: should we accept NULL name? */ 223903873ceSHans Petter Selasky if (parent_dev == NULL) { 224903873ceSHans Petter Selasky errno = EINVAL; 225903873ceSHans Petter Selasky return (-1); 226903873ceSHans Petter Selasky } 227903873ceSHans Petter Selasky if ((ctl = calloc(1, sizeof(mix_ctl_t))) == NULL) 228903873ceSHans Petter Selasky return (-1); 229903873ceSHans Petter Selasky ctl->parent_dev = parent_dev; 230903873ceSHans Petter Selasky ctl->id = id; 231903873ceSHans Petter Selasky if (name != NULL) 232903873ceSHans Petter Selasky (void)strlcpy(ctl->name, name, sizeof(ctl->name)); 233903873ceSHans Petter Selasky ctl->mod = mod; 234903873ceSHans Petter Selasky ctl->print = print; 235903873ceSHans Petter Selasky dp = ctl->parent_dev; 236903873ceSHans Petter Selasky /* Make sure the same ID or name doesn't exist already. */ 237903873ceSHans Petter Selasky TAILQ_FOREACH(cp, &dp->ctls, ctls) { 238903873ceSHans Petter Selasky if (!strncmp(cp->name, name, sizeof(cp->name)) || cp->id == id) { 239903873ceSHans Petter Selasky errno = EINVAL; 240903873ceSHans Petter Selasky return (-1); 241903873ceSHans Petter Selasky } 242903873ceSHans Petter Selasky } 243903873ceSHans Petter Selasky TAILQ_INSERT_TAIL(&dp->ctls, ctl, ctls); 244903873ceSHans Petter Selasky dp->nctl++; 245903873ceSHans Petter Selasky 246903873ceSHans Petter Selasky return (0); 247903873ceSHans Petter Selasky } 248903873ceSHans Petter Selasky 249903873ceSHans Petter Selasky /* 250903873ceSHans Petter Selasky * Same as `mixer_add_ctl`. 251903873ceSHans Petter Selasky */ 252903873ceSHans Petter Selasky int 253903873ceSHans Petter Selasky mixer_add_ctl_s(mix_ctl_t *ctl) 254903873ceSHans Petter Selasky { 255903873ceSHans Petter Selasky if (ctl == NULL) 256903873ceSHans Petter Selasky return (-1); 257903873ceSHans Petter Selasky 258903873ceSHans Petter Selasky return (mixer_add_ctl(ctl->parent_dev, ctl->id, ctl->name, 259903873ceSHans Petter Selasky ctl->mod, ctl->print)); 260903873ceSHans Petter Selasky } 261903873ceSHans Petter Selasky 262903873ceSHans Petter Selasky /* 263903873ceSHans Petter Selasky * Remove a mixer control from a device. 264903873ceSHans Petter Selasky */ 265903873ceSHans Petter Selasky int 266903873ceSHans Petter Selasky mixer_remove_ctl(mix_ctl_t *ctl) 267903873ceSHans Petter Selasky { 268903873ceSHans Petter Selasky struct mix_dev *p; 269903873ceSHans Petter Selasky 270903873ceSHans Petter Selasky if (ctl == NULL) { 271903873ceSHans Petter Selasky errno = EINVAL; 272903873ceSHans Petter Selasky return (-1); 273903873ceSHans Petter Selasky } 274903873ceSHans Petter Selasky p = ctl->parent_dev; 275903873ceSHans Petter Selasky if (!TAILQ_EMPTY(&p->ctls)) { 276903873ceSHans Petter Selasky TAILQ_REMOVE(&p->ctls, ctl, ctls); 277903873ceSHans Petter Selasky free(ctl); 278903873ceSHans Petter Selasky } 279903873ceSHans Petter Selasky 280903873ceSHans Petter Selasky return (0); 281903873ceSHans Petter Selasky } 282903873ceSHans Petter Selasky 283903873ceSHans Petter Selasky /* 284903873ceSHans Petter Selasky * Get a mixer control by id. 285903873ceSHans Petter Selasky */ 286903873ceSHans Petter Selasky mix_ctl_t * 287903873ceSHans Petter Selasky mixer_get_ctl(struct mix_dev *d, int id) 288903873ceSHans Petter Selasky { 289903873ceSHans Petter Selasky mix_ctl_t *cp; 290903873ceSHans Petter Selasky 291903873ceSHans Petter Selasky TAILQ_FOREACH(cp, &d->ctls, ctls) { 292903873ceSHans Petter Selasky if (cp->id == id) 293903873ceSHans Petter Selasky return (cp); 294903873ceSHans Petter Selasky } 295903873ceSHans Petter Selasky errno = EINVAL; 296903873ceSHans Petter Selasky 297903873ceSHans Petter Selasky return (NULL); 298903873ceSHans Petter Selasky } 299903873ceSHans Petter Selasky 300903873ceSHans Petter Selasky /* 301903873ceSHans Petter Selasky * Get a mixer control by name. 302903873ceSHans Petter Selasky */ 303903873ceSHans Petter Selasky mix_ctl_t * 304903873ceSHans Petter Selasky mixer_get_ctl_byname(struct mix_dev *d, const char *name) 305903873ceSHans Petter Selasky { 306903873ceSHans Petter Selasky mix_ctl_t *cp; 307903873ceSHans Petter Selasky 308903873ceSHans Petter Selasky TAILQ_FOREACH(cp, &d->ctls, ctls) { 309903873ceSHans Petter Selasky if (!strncmp(cp->name, name, sizeof(cp->name))) 310903873ceSHans Petter Selasky return (cp); 311903873ceSHans Petter Selasky } 312903873ceSHans Petter Selasky errno = EINVAL; 313903873ceSHans Petter Selasky 314903873ceSHans Petter Selasky return (NULL); 315903873ceSHans Petter Selasky } 316903873ceSHans Petter Selasky 317903873ceSHans Petter Selasky /* 318903873ceSHans Petter Selasky * Change the mixer's left and right volume. The allowed volume values are 319903873ceSHans Petter Selasky * between MIX_VOLMIN and MIX_VOLMAX. The `ioctl` for volume change requires 320903873ceSHans Petter Selasky * an integer value between 0 and 100 stored as `lvol | rvol << 8` -- for 321903873ceSHans Petter Selasky * that reason, we de-normalize the 32-bit float volume value, before 322903873ceSHans Petter Selasky * we pass it to the `ioctl`. 323903873ceSHans Petter Selasky * 324903873ceSHans Petter Selasky * Volume clumping should be done by the caller. 325903873ceSHans Petter Selasky */ 326903873ceSHans Petter Selasky int 327903873ceSHans Petter Selasky mixer_set_vol(struct mixer *m, mix_volume_t vol) 328903873ceSHans Petter Selasky { 329903873ceSHans Petter Selasky int v; 330903873ceSHans Petter Selasky 331903873ceSHans Petter Selasky if (vol.left < MIX_VOLMIN || vol.left > MIX_VOLMAX || 332903873ceSHans Petter Selasky vol.right < MIX_VOLMIN || vol.right > MIX_VOLMAX) { 333903873ceSHans Petter Selasky errno = ERANGE; 334903873ceSHans Petter Selasky return (-1); 335903873ceSHans Petter Selasky } 336903873ceSHans Petter Selasky v = MIX_VOLDENORM(vol.left) | MIX_VOLDENORM(vol.right) << 8; 337903873ceSHans Petter Selasky if (ioctl(m->fd, MIXER_WRITE(m->dev->devno), &v) < 0) 338903873ceSHans Petter Selasky return (-1); 339*e5f5ca7fSChristos Margiolis if (_mixer_readvol(m->dev) < 0) 340903873ceSHans Petter Selasky return (-1); 341903873ceSHans Petter Selasky 342903873ceSHans Petter Selasky return (0); 343903873ceSHans Petter Selasky } 344903873ceSHans Petter Selasky 345903873ceSHans Petter Selasky /* 346903873ceSHans Petter Selasky * Manipulate a device's mute. 347903873ceSHans Petter Selasky * 348903873ceSHans Petter Selasky * @param opt MIX_MUTE mute device 349903873ceSHans Petter Selasky * MIX_UNMUTE unmute device 350903873ceSHans Petter Selasky * MIX_TOGGLEMUTE toggle device's mute 351903873ceSHans Petter Selasky */ 352903873ceSHans Petter Selasky int 353903873ceSHans Petter Selasky mixer_set_mute(struct mixer *m, int opt) 354903873ceSHans Petter Selasky { 355903873ceSHans Petter Selasky switch (opt) { 356903873ceSHans Petter Selasky case MIX_MUTE: 357903873ceSHans Petter Selasky m->mutemask |= (1 << m->dev->devno); 358903873ceSHans Petter Selasky break; 359903873ceSHans Petter Selasky case MIX_UNMUTE: 360903873ceSHans Petter Selasky m->mutemask &= ~(1 << m->dev->devno); 361903873ceSHans Petter Selasky break; 362903873ceSHans Petter Selasky case MIX_TOGGLEMUTE: 363903873ceSHans Petter Selasky m->mutemask ^= (1 << m->dev->devno); 364903873ceSHans Petter Selasky break; 365903873ceSHans Petter Selasky default: 366903873ceSHans Petter Selasky errno = EINVAL; 367903873ceSHans Petter Selasky return (-1); 368903873ceSHans Petter Selasky } 369903873ceSHans Petter Selasky if (ioctl(m->fd, SOUND_MIXER_WRITE_MUTE, &m->mutemask) < 0) 370903873ceSHans Petter Selasky return (-1); 371903873ceSHans Petter Selasky if (ioctl(m->fd, SOUND_MIXER_READ_MUTE, &m->mutemask) < 0) 372903873ceSHans Petter Selasky return (-1); 373903873ceSHans Petter Selasky 374903873ceSHans Petter Selasky return 0; 375903873ceSHans Petter Selasky } 376903873ceSHans Petter Selasky 377903873ceSHans Petter Selasky /* 378903873ceSHans Petter Selasky * Modify a recording device. The selected device has to be a recording device, 379903873ceSHans Petter Selasky * otherwise the function will fail. 380903873ceSHans Petter Selasky * 381903873ceSHans Petter Selasky * @param opt MIX_ADDRECSRC add device to recording sources 382903873ceSHans Petter Selasky * MIX_REMOVERECSRC remove device from recording sources 383903873ceSHans Petter Selasky * MIX_SETRECSRC set device as the only recording source 384903873ceSHans Petter Selasky * MIX_TOGGLERECSRC toggle device from recording sources 385903873ceSHans Petter Selasky */ 386903873ceSHans Petter Selasky int 387903873ceSHans Petter Selasky mixer_mod_recsrc(struct mixer *m, int opt) 388903873ceSHans Petter Selasky { 389903873ceSHans Petter Selasky if (!m->recmask || !MIX_ISREC(m, m->dev->devno)) { 390903873ceSHans Petter Selasky errno = ENODEV; 391903873ceSHans Petter Selasky return (-1); 392903873ceSHans Petter Selasky } 393903873ceSHans Petter Selasky switch (opt) { 394903873ceSHans Petter Selasky case MIX_ADDRECSRC: 395903873ceSHans Petter Selasky m->recsrc |= (1 << m->dev->devno); 396903873ceSHans Petter Selasky break; 397903873ceSHans Petter Selasky case MIX_REMOVERECSRC: 398903873ceSHans Petter Selasky m->recsrc &= ~(1 << m->dev->devno); 399903873ceSHans Petter Selasky break; 400903873ceSHans Petter Selasky case MIX_SETRECSRC: 401903873ceSHans Petter Selasky m->recsrc = (1 << m->dev->devno); 402903873ceSHans Petter Selasky break; 403903873ceSHans Petter Selasky case MIX_TOGGLERECSRC: 404903873ceSHans Petter Selasky m->recsrc ^= (1 << m->dev->devno); 405903873ceSHans Petter Selasky break; 406903873ceSHans Petter Selasky default: 407903873ceSHans Petter Selasky errno = EINVAL; 408903873ceSHans Petter Selasky return (-1); 409903873ceSHans Petter Selasky } 410903873ceSHans Petter Selasky if (ioctl(m->fd, SOUND_MIXER_WRITE_RECSRC, &m->recsrc) < 0) 411903873ceSHans Petter Selasky return (-1); 412903873ceSHans Petter Selasky if (ioctl(m->fd, SOUND_MIXER_READ_RECSRC, &m->recsrc) < 0) 413903873ceSHans Petter Selasky return (-1); 414903873ceSHans Petter Selasky 415903873ceSHans Petter Selasky return (0); 416903873ceSHans Petter Selasky } 417903873ceSHans Petter Selasky 418903873ceSHans Petter Selasky /* 419903873ceSHans Petter Selasky * Get default audio card's number. This is used to open the default mixer 420903873ceSHans Petter Selasky * and set the mixer structure's `f_default` flag. 421903873ceSHans Petter Selasky */ 422903873ceSHans Petter Selasky int 423903873ceSHans Petter Selasky mixer_get_dunit(void) 424903873ceSHans Petter Selasky { 425903873ceSHans Petter Selasky size_t size; 426903873ceSHans Petter Selasky int unit; 427903873ceSHans Petter Selasky 428903873ceSHans Petter Selasky size = sizeof(int); 429903873ceSHans Petter Selasky if (sysctlbyname("hw.snd.default_unit", &unit, &size, NULL, 0) < 0) 430903873ceSHans Petter Selasky return (-1); 431903873ceSHans Petter Selasky 432903873ceSHans Petter Selasky return (unit); 433903873ceSHans Petter Selasky } 434903873ceSHans Petter Selasky 435903873ceSHans Petter Selasky /* 436903873ceSHans Petter Selasky * Change the default audio card. This is normally _not_ a mixer feature, but 437903873ceSHans Petter Selasky * it's useful to have, so the caller can avoid having to manually use 438903873ceSHans Petter Selasky * the sysctl API. 439903873ceSHans Petter Selasky * 440903873ceSHans Petter Selasky * @param unit the audio card number (e.g pcm0, pcm1, ...). 441903873ceSHans Petter Selasky */ 442903873ceSHans Petter Selasky int 443903873ceSHans Petter Selasky mixer_set_dunit(struct mixer *m, int unit) 444903873ceSHans Petter Selasky { 445903873ceSHans Petter Selasky size_t size; 446903873ceSHans Petter Selasky 447903873ceSHans Petter Selasky size = sizeof(int); 448903873ceSHans Petter Selasky if (sysctlbyname("hw.snd.default_unit", NULL, 0, &unit, size) < 0) 449903873ceSHans Petter Selasky return (-1); 450903873ceSHans Petter Selasky /* XXX: how will other mixers get updated? */ 451903873ceSHans Petter Selasky m->f_default = m->unit == unit; 452903873ceSHans Petter Selasky 453903873ceSHans Petter Selasky return (0); 454903873ceSHans Petter Selasky } 455903873ceSHans Petter Selasky 456903873ceSHans Petter Selasky /* 457903873ceSHans Petter Selasky * Get sound device mode (none, play, rec, play+rec). Userland programs can 458790b5264SHans Petter Selasky * use the MIX_MODE_* flags to determine the mode of the device. 459903873ceSHans Petter Selasky */ 460903873ceSHans Petter Selasky int 461903873ceSHans Petter Selasky mixer_get_mode(int unit) 462903873ceSHans Petter Selasky { 463903873ceSHans Petter Selasky char buf[64]; 464903873ceSHans Petter Selasky size_t size; 465903873ceSHans Petter Selasky unsigned int mode; 466903873ceSHans Petter Selasky 467903873ceSHans Petter Selasky (void)snprintf(buf, sizeof(buf), "dev.pcm.%d.mode", unit); 468903873ceSHans Petter Selasky size = sizeof(unsigned int); 469903873ceSHans Petter Selasky if (sysctlbyname(buf, &mode, &size, NULL, 0) < 0) 47039844001SHans Petter Selasky return (0); 471903873ceSHans Petter Selasky 472903873ceSHans Petter Selasky return (mode); 473903873ceSHans Petter Selasky } 474903873ceSHans Petter Selasky 475903873ceSHans Petter Selasky /* 476903873ceSHans Petter Selasky * Get the total number of mixers in the system. 477903873ceSHans Petter Selasky */ 478903873ceSHans Petter Selasky int 479903873ceSHans Petter Selasky mixer_get_nmixers(void) 480903873ceSHans Petter Selasky { 481903873ceSHans Petter Selasky struct mixer *m; 482903873ceSHans Petter Selasky oss_sysinfo si; 483903873ceSHans Petter Selasky 484903873ceSHans Petter Selasky /* 485903873ceSHans Petter Selasky * Open a dummy mixer because we need the `fd` field for the 486903873ceSHans Petter Selasky * `ioctl` to work. 487903873ceSHans Petter Selasky */ 488903873ceSHans Petter Selasky if ((m = mixer_open(NULL)) == NULL) 489903873ceSHans Petter Selasky return (-1); 490903873ceSHans Petter Selasky if (ioctl(m->fd, OSS_SYSINFO, &si) < 0) { 491903873ceSHans Petter Selasky (void)mixer_close(m); 492903873ceSHans Petter Selasky return (-1); 493903873ceSHans Petter Selasky } 494903873ceSHans Petter Selasky (void)mixer_close(m); 495903873ceSHans Petter Selasky 496903873ceSHans Petter Selasky return (si.nummixers); 497903873ceSHans Petter Selasky } 498