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