2 * Copyright (c) 2021 Christos Margiolis <christos@FreeBSD.org>
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 #include <sys/types.h>
26 #include <sys/ioctl.h>
27 #include <sys/sysctl.h>
28 #include <sys/soundcard.h>
39 #define BASEPATH "/dev/mixer"
41 static int _mixer_readvol(struct mixer *, struct mix_dev *);
44 * Fetch volume from the device.
47 _mixer_readvol(struct mixer *m, struct mix_dev *dev)
51 if (ioctl(m->fd, MIXER_READ(dev->devno), &v) < 0)
53 dev->vol.left = MIX_VOLNORM(v & 0x00ff);
54 dev->vol.right = MIX_VOLNORM((v >> 8) & 0x00ff);
60 * Open a mixer device in `/dev/mixerN`, where N is the number of the mixer.
61 * Each device maps to an actual pcm audio card, so `/dev/mixer0` is the
62 * mixer for pcm0, and so on.
64 * @param name path to mixer device. NULL or "/dev/mixer" for the
65 * the default mixer (i.e `hw.snd.default_unit`).
68 mixer_open(const char *name)
70 struct mixer *m = NULL;
72 const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
75 if ((m = calloc(1, sizeof(struct mixer))) == NULL)
79 /* `name` does not start with "/dev/mixer". */
80 if (strncmp(name, BASEPATH, strlen(BASEPATH)) != 0) {
83 /* `name` is "/dev/mixer" so, we'll use the default unit. */
84 if (strncmp(name, BASEPATH, strlen(name)) == 0)
86 m->unit = strtol(name + strlen(BASEPATH), NULL, 10);
88 (void)strlcpy(m->name, name, sizeof(m->name));
91 if ((m->unit = mixer_get_dunit()) < 0)
93 (void)snprintf(m->name, sizeof(m->name), "/dev/mixer%d", m->unit);
96 if ((m->fd = open(m->name, O_RDWR)) < 0)
99 m->devmask = m->recmask = m->recsrc = 0;
100 m->f_default = m->unit == mixer_get_dunit();
101 m->mode = mixer_get_mode(m->unit);
102 /* The unit number _must_ be set before the ioctl. */
104 m->ci.card = m->unit;
105 if (ioctl(m->fd, SNDCTL_MIXERINFO, &m->mi) < 0) {
106 memset(&m->mi, 0, sizeof(m->mi));
107 strlcpy(m->mi.name, m->name, sizeof(m->mi.name));
109 if (ioctl(m->fd, SNDCTL_CARDINFO, &m->ci) < 0)
110 memset(&m->ci, 0, sizeof(m->ci));
111 if (ioctl(m->fd, SOUND_MIXER_READ_DEVMASK, &m->devmask) < 0 ||
112 ioctl(m->fd, SOUND_MIXER_READ_MUTE, &m->mutemask) < 0 ||
113 ioctl(m->fd, SOUND_MIXER_READ_RECMASK, &m->recmask) < 0 ||
114 ioctl(m->fd, SOUND_MIXER_READ_RECSRC, &m->recsrc) < 0)
117 TAILQ_INIT(&m->devs);
118 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
119 if (!MIX_ISDEV(m, i))
121 if ((dp = calloc(1, sizeof(struct mix_dev))) == NULL)
123 dp->parent_mixer = m;
126 if (_mixer_readvol(m, dp) < 0)
128 (void)strlcpy(dp->name, names[i], sizeof(dp->name));
129 TAILQ_INIT(&dp->ctls);
130 TAILQ_INSERT_TAIL(&m->devs, dp, devs);
134 /* The default device is always "vol". */
135 m->dev = TAILQ_FIRST(&m->devs);
140 (void)mixer_close(m);
146 * Free resources and close the mixer.
149 mixer_close(struct mixer *m)
155 while (!TAILQ_EMPTY(&m->devs)) {
156 dp = TAILQ_FIRST(&m->devs);
157 TAILQ_REMOVE(&m->devs, dp, devs);
158 while (!TAILQ_EMPTY(&dp->ctls))
159 (void)mixer_remove_ctl(TAILQ_FIRST(&dp->ctls));
168 * Select a mixer device. The mixer structure keeps a list of all the devices
169 * the mixer has, but only one can be manipulated at a time -- this is what
170 * the `dev` in the mixer structure field is for. Each time a device is to be
171 * manipulated, `dev` has to point to it first.
173 * The caller must manually assign the return value to `m->dev`.
176 mixer_get_dev(struct mixer *m, int dev)
180 if (dev < 0 || dev >= m->ndev) {
184 TAILQ_FOREACH(dp, &m->devs, devs) {
185 if (dp->devno == dev)
194 * Select a device by name.
196 * @param name device name (e.g vol, pcm, ...)
199 mixer_get_dev_byname(struct mixer *m, const char *name)
203 TAILQ_FOREACH(dp, &m->devs, devs) {
204 if (!strncmp(dp->name, name, sizeof(dp->name)))
213 * Add a mixer control to a device.
216 mixer_add_ctl(struct mix_dev *parent_dev, int id, const char *name,
217 int (*mod)(struct mix_dev *, void *),
218 int (*print)(struct mix_dev *, void *))
223 /* XXX: should we accept NULL name? */
224 if (parent_dev == NULL) {
228 if ((ctl = calloc(1, sizeof(mix_ctl_t))) == NULL)
230 ctl->parent_dev = parent_dev;
233 (void)strlcpy(ctl->name, name, sizeof(ctl->name));
236 dp = ctl->parent_dev;
237 /* Make sure the same ID or name doesn't exist already. */
238 TAILQ_FOREACH(cp, &dp->ctls, ctls) {
239 if (!strncmp(cp->name, name, sizeof(cp->name)) || cp->id == id) {
244 TAILQ_INSERT_TAIL(&dp->ctls, ctl, ctls);
251 * Same as `mixer_add_ctl`.
254 mixer_add_ctl_s(mix_ctl_t *ctl)
259 return (mixer_add_ctl(ctl->parent_dev, ctl->id, ctl->name,
260 ctl->mod, ctl->print));
264 * Remove a mixer control from a device.
267 mixer_remove_ctl(mix_ctl_t *ctl)
276 if (!TAILQ_EMPTY(&p->ctls)) {
277 TAILQ_REMOVE(&p->ctls, ctl, ctls);
285 * Get a mixer control by id.
288 mixer_get_ctl(struct mix_dev *d, int id)
292 TAILQ_FOREACH(cp, &d->ctls, ctls) {
302 * Get a mixer control by name.
305 mixer_get_ctl_byname(struct mix_dev *d, const char *name)
309 TAILQ_FOREACH(cp, &d->ctls, ctls) {
310 if (!strncmp(cp->name, name, sizeof(cp->name)))
319 * Change the mixer's left and right volume. The allowed volume values are
320 * between MIX_VOLMIN and MIX_VOLMAX. The `ioctl` for volume change requires
321 * an integer value between 0 and 100 stored as `lvol | rvol << 8` -- for
322 * that reason, we de-normalize the 32-bit float volume value, before
323 * we pass it to the `ioctl`.
325 * Volume clumping should be done by the caller.
328 mixer_set_vol(struct mixer *m, mix_volume_t vol)
332 if (vol.left < MIX_VOLMIN || vol.left > MIX_VOLMAX ||
333 vol.right < MIX_VOLMIN || vol.right > MIX_VOLMAX) {
337 v = MIX_VOLDENORM(vol.left) | MIX_VOLDENORM(vol.right) << 8;
338 if (ioctl(m->fd, MIXER_WRITE(m->dev->devno), &v) < 0)
340 if (_mixer_readvol(m, m->dev) < 0)
347 * Manipulate a device's mute.
349 * @param opt MIX_MUTE mute device
350 * MIX_UNMUTE unmute device
351 * MIX_TOGGLEMUTE toggle device's mute
354 mixer_set_mute(struct mixer *m, int opt)
358 m->mutemask |= (1 << m->dev->devno);
361 m->mutemask &= ~(1 << m->dev->devno);
364 m->mutemask ^= (1 << m->dev->devno);
370 if (ioctl(m->fd, SOUND_MIXER_WRITE_MUTE, &m->mutemask) < 0)
372 if (ioctl(m->fd, SOUND_MIXER_READ_MUTE, &m->mutemask) < 0)
379 * Modify a recording device. The selected device has to be a recording device,
380 * otherwise the function will fail.
382 * @param opt MIX_ADDRECSRC add device to recording sources
383 * MIX_REMOVERECSRC remove device from recording sources
384 * MIX_SETRECSRC set device as the only recording source
385 * MIX_TOGGLERECSRC toggle device from recording sources
388 mixer_mod_recsrc(struct mixer *m, int opt)
390 if (!m->recmask || !MIX_ISREC(m, m->dev->devno)) {
396 m->recsrc |= (1 << m->dev->devno);
398 case MIX_REMOVERECSRC:
399 m->recsrc &= ~(1 << m->dev->devno);
402 m->recsrc = (1 << m->dev->devno);
404 case MIX_TOGGLERECSRC:
405 m->recsrc ^= (1 << m->dev->devno);
411 if (ioctl(m->fd, SOUND_MIXER_WRITE_RECSRC, &m->recsrc) < 0)
413 if (ioctl(m->fd, SOUND_MIXER_READ_RECSRC, &m->recsrc) < 0)
420 * Get default audio card's number. This is used to open the default mixer
421 * and set the mixer structure's `f_default` flag.
424 mixer_get_dunit(void)
430 if (sysctlbyname("hw.snd.default_unit", &unit, &size, NULL, 0) < 0)
437 * Change the default audio card. This is normally _not_ a mixer feature, but
438 * it's useful to have, so the caller can avoid having to manually use
441 * @param unit the audio card number (e.g pcm0, pcm1, ...).
444 mixer_set_dunit(struct mixer *m, int unit)
449 if (sysctlbyname("hw.snd.default_unit", NULL, 0, &unit, size) < 0)
451 /* XXX: how will other mixers get updated? */
452 m->f_default = m->unit == unit;
458 * Get sound device mode (none, play, rec, play+rec). Userland programs can
459 * use the MIX_MODE_* flags to determine the mode of the device.
462 mixer_get_mode(int unit)
468 (void)snprintf(buf, sizeof(buf), "dev.pcm.%d.mode", unit);
469 size = sizeof(unsigned int);
470 if (sysctlbyname(buf, &mode, &size, NULL, 0) < 0)
477 * Get the total number of mixers in the system.
480 mixer_get_nmixers(void)
486 * Open a dummy mixer because we need the `fd` field for the
489 if ((m = mixer_open(NULL)) == NULL)
491 if (ioctl(m->fd, OSS_SYSINFO, &si) < 0) {
492 (void)mixer_close(m);
495 (void)mixer_close(m);
497 return (si.nummixers);