xref: /freebsd/lib/libmixer/mixer.c (revision 67c89b21b95601c01bafe5a0c518d320a39111c0)
1 /*-
2  * Copyright (c) 2021 Christos Margiolis <christos@FreeBSD.org>
3  *
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:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
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
20  * THE SOFTWARE.
21  */
22 
23 #include <sys/types.h>
24 #include <sys/ioctl.h>
25 #include <sys/sysctl.h>
26 
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 #include "mixer.h"
35 
36 #define	BASEPATH "/dev/mixer"
37 
38 static int _mixer_readvol(struct mix_dev *);
39 
40 /*
41  * Fetch volume from the device.
42  */
43 static int
_mixer_readvol(struct mix_dev * dev)44 _mixer_readvol(struct mix_dev *dev)
45 {
46 	int v;
47 
48 	if (ioctl(dev->parent_mixer->fd, MIXER_READ(dev->devno), &v) < 0)
49 		return (-1);
50 	dev->vol.left = MIX_VOLNORM(v & 0x00ff);
51 	dev->vol.right = MIX_VOLNORM((v >> 8) & 0x00ff);
52 
53 	return (0);
54 }
55 
56 /*
57  * Open a mixer device in `/dev/mixerN`, where N is the number of the mixer.
58  * Each device maps to an actual pcm audio card, so `/dev/mixer0` is the
59  * mixer for pcm0, and so on.
60  *
61  * @param name		path to mixer device. NULL or "/dev/mixer" for the
62  *			the default mixer (i.e `hw.snd.default_unit`).
63  */
64 struct mixer *
mixer_open(const char * name)65 mixer_open(const char *name)
66 {
67 	struct mixer *m = NULL;
68 	struct mix_dev *dp;
69 	const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
70 	int i;
71 
72 	if ((m = calloc(1, sizeof(struct mixer))) == NULL)
73 		goto fail;
74 
75 	if (name != NULL) {
76 		/* `name` does not start with "/dev/mixer". */
77 		if (strncmp(name, BASEPATH, strlen(BASEPATH)) != 0) {
78 			m->unit = -1;
79 		} else {
80 			/* `name` is "/dev/mixer" so, we'll use the default unit. */
81 			if (strncmp(name, BASEPATH, strlen(name)) == 0)
82 				goto dunit;
83 			m->unit = strtol(name + strlen(BASEPATH), NULL, 10);
84 		}
85 		(void)strlcpy(m->name, name, sizeof(m->name));
86 	} else {
87 dunit:
88 		if ((m->unit = mixer_get_dunit()) < 0)
89 			goto fail;
90 		(void)snprintf(m->name, sizeof(m->name), BASEPATH "%d", m->unit);
91 	}
92 
93 	if ((m->fd = open(m->name, O_RDWR)) < 0)
94 		goto fail;
95 
96 	m->devmask = m->recmask = m->recsrc = 0;
97 	m->f_default = m->unit == mixer_get_dunit();
98 	m->mode = mixer_get_mode(m->unit);
99 	/* The unit number _must_ be set before the ioctl. */
100 	m->mi.dev = m->unit;
101 	m->ci.card = m->unit;
102 	if (ioctl(m->fd, SNDCTL_MIXERINFO, &m->mi) < 0) {
103 		memset(&m->mi, 0, sizeof(m->mi));
104 		strlcpy(m->mi.name, m->name, sizeof(m->mi.name));
105 	}
106 	if (ioctl(m->fd, SNDCTL_CARDINFO, &m->ci) < 0)
107 		memset(&m->ci, 0, sizeof(m->ci));
108 	if (ioctl(m->fd, SOUND_MIXER_READ_DEVMASK, &m->devmask) < 0 ||
109 	    ioctl(m->fd, SOUND_MIXER_READ_MUTE, &m->mutemask) < 0 ||
110 	    ioctl(m->fd, SOUND_MIXER_READ_RECMASK, &m->recmask) < 0 ||
111 	    ioctl(m->fd, SOUND_MIXER_READ_RECSRC, &m->recsrc) < 0)
112 		goto fail;
113 
114 	TAILQ_INIT(&m->devs);
115 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
116 		if (!MIX_ISDEV(m, i))
117 			continue;
118 		if ((dp = calloc(1, sizeof(struct mix_dev))) == NULL)
119 			goto fail;
120 		dp->parent_mixer = m;
121 		dp->devno = i;
122 		dp->nctl = 0;
123 		if (_mixer_readvol(dp) < 0)
124 			goto fail;
125 		(void)strlcpy(dp->name, names[i], sizeof(dp->name));
126 		TAILQ_INIT(&dp->ctls);
127 		TAILQ_INSERT_TAIL(&m->devs, dp, devs);
128 		m->ndev++;
129 	}
130 
131 	/* The default device is always "vol". */
132 	m->dev = TAILQ_FIRST(&m->devs);
133 
134 	return (m);
135 fail:
136 	if (m != NULL)
137 		(void)mixer_close(m);
138 
139 	return (NULL);
140 }
141 
142 /*
143  * Free resources and close the mixer.
144  */
145 int
mixer_close(struct mixer * m)146 mixer_close(struct mixer *m)
147 {
148 	struct mix_dev *dp;
149 	int r;
150 
151 	r = close(m->fd);
152 	while (!TAILQ_EMPTY(&m->devs)) {
153 		dp = TAILQ_FIRST(&m->devs);
154 		TAILQ_REMOVE(&m->devs, dp, devs);
155 		while (!TAILQ_EMPTY(&dp->ctls))
156 			(void)mixer_remove_ctl(TAILQ_FIRST(&dp->ctls));
157 		free(dp);
158 	}
159 	free(m);
160 
161 	return (r);
162 }
163 
164 /*
165  * Select a mixer device. The mixer structure keeps a list of all the devices
166  * the mixer has, but only one can be manipulated at a time -- this is what
167  * the `dev` in the mixer structure field is for. Each time a device is to be
168  * manipulated, `dev` has to point to it first.
169  *
170  * The caller must manually assign the return value to `m->dev`.
171  */
172 struct mix_dev *
mixer_get_dev(struct mixer * m,int dev)173 mixer_get_dev(struct mixer *m, int dev)
174 {
175 	struct mix_dev *dp;
176 
177 	if (dev < 0 || dev >= m->ndev) {
178 		errno = ERANGE;
179 		return (NULL);
180 	}
181 	TAILQ_FOREACH(dp, &m->devs, devs) {
182 		if (dp->devno == dev)
183 			return (dp);
184 	}
185 	errno = EINVAL;
186 
187 	return (NULL);
188 }
189 
190 /*
191  * Select a device by name.
192  *
193  * @param name		device name (e.g vol, pcm, ...)
194  */
195 struct mix_dev *
mixer_get_dev_byname(struct mixer * m,const char * name)196 mixer_get_dev_byname(struct mixer *m, const char *name)
197 {
198 	struct mix_dev *dp;
199 
200 	TAILQ_FOREACH(dp, &m->devs, devs) {
201 		if (!strncmp(dp->name, name, sizeof(dp->name)))
202 			return (dp);
203 	}
204 	errno = EINVAL;
205 
206 	return (NULL);
207 }
208 
209 /*
210  * Add a mixer control to a device.
211  */
212 int
mixer_add_ctl(struct mix_dev * parent_dev,int id,const char * name,int (* mod)(struct mix_dev *,void *),int (* print)(struct mix_dev *,void *))213 mixer_add_ctl(struct mix_dev *parent_dev, int id, const char *name,
214     int (*mod)(struct mix_dev *, void *),
215     int (*print)(struct mix_dev *, void *))
216 {
217 	struct mix_dev *dp;
218 	mix_ctl_t *ctl, *cp;
219 
220 	/* XXX: should we accept NULL name? */
221 	if (parent_dev == NULL) {
222 		errno = EINVAL;
223 		return (-1);
224 	}
225 	if ((ctl = calloc(1, sizeof(mix_ctl_t))) == NULL)
226 		return (-1);
227 	ctl->parent_dev = parent_dev;
228 	ctl->id = id;
229 	if (name != NULL)
230 		(void)strlcpy(ctl->name, name, sizeof(ctl->name));
231 	ctl->mod = mod;
232 	ctl->print = print;
233 	dp = ctl->parent_dev;
234 	/* Make sure the same ID or name doesn't exist already. */
235 	TAILQ_FOREACH(cp, &dp->ctls, ctls) {
236 		if (!strncmp(cp->name, name, sizeof(cp->name)) || cp->id == id) {
237 			errno = EINVAL;
238 			return (-1);
239 		}
240 	}
241 	TAILQ_INSERT_TAIL(&dp->ctls, ctl, ctls);
242 	dp->nctl++;
243 
244 	return (0);
245 }
246 
247 /*
248  * Same as `mixer_add_ctl`.
249  */
250 int
mixer_add_ctl_s(mix_ctl_t * ctl)251 mixer_add_ctl_s(mix_ctl_t *ctl)
252 {
253 	if (ctl == NULL)
254 		return (-1);
255 
256 	return (mixer_add_ctl(ctl->parent_dev, ctl->id, ctl->name,
257 	    ctl->mod, ctl->print));
258 }
259 
260 /*
261  * Remove a mixer control from a device.
262  */
263 int
mixer_remove_ctl(mix_ctl_t * ctl)264 mixer_remove_ctl(mix_ctl_t *ctl)
265 {
266 	struct mix_dev *p;
267 
268 	if (ctl == NULL) {
269 		errno = EINVAL;
270 		return (-1);
271 	}
272 	p = ctl->parent_dev;
273 	if (!TAILQ_EMPTY(&p->ctls)) {
274 		TAILQ_REMOVE(&p->ctls, ctl, ctls);
275 		free(ctl);
276 	}
277 
278 	return (0);
279 }
280 
281 /*
282  * Get a mixer control by id.
283  */
284 mix_ctl_t *
mixer_get_ctl(struct mix_dev * d,int id)285 mixer_get_ctl(struct mix_dev *d, int id)
286 {
287 	mix_ctl_t *cp;
288 
289 	TAILQ_FOREACH(cp, &d->ctls, ctls) {
290 		if (cp->id == id)
291 			return (cp);
292 	}
293 	errno = EINVAL;
294 
295 	return (NULL);
296 }
297 
298 /*
299  * Get a mixer control by name.
300  */
301 mix_ctl_t *
mixer_get_ctl_byname(struct mix_dev * d,const char * name)302 mixer_get_ctl_byname(struct mix_dev *d, const char *name)
303 {
304 	mix_ctl_t *cp;
305 
306 	TAILQ_FOREACH(cp, &d->ctls, ctls) {
307 		if (!strncmp(cp->name, name, sizeof(cp->name)))
308 			return (cp);
309 	}
310 	errno = EINVAL;
311 
312 	return (NULL);
313 }
314 
315 /*
316  * Change the mixer's left and right volume. The allowed volume values are
317  * between MIX_VOLMIN and MIX_VOLMAX. The `ioctl` for volume change requires
318  * an integer value between 0 and 100 stored as `lvol | rvol << 8` --  for
319  * that reason, we de-normalize the 32-bit float volume value, before
320  * we pass it to the `ioctl`.
321  *
322  * Volume clumping should be done by the caller.
323  */
324 int
mixer_set_vol(struct mixer * m,mix_volume_t vol)325 mixer_set_vol(struct mixer *m, mix_volume_t vol)
326 {
327 	int v;
328 
329 	if (vol.left < MIX_VOLMIN || vol.left > MIX_VOLMAX ||
330 	    vol.right < MIX_VOLMIN || vol.right > MIX_VOLMAX) {
331 		errno = ERANGE;
332 		return (-1);
333 	}
334 	v = MIX_VOLDENORM(vol.left) | MIX_VOLDENORM(vol.right) << 8;
335 	if (ioctl(m->fd, MIXER_WRITE(m->dev->devno), &v) < 0)
336 		return (-1);
337 	if (_mixer_readvol(m->dev) < 0)
338 		return (-1);
339 
340 	return (0);
341 }
342 
343 /*
344  * Manipulate a device's mute.
345  *
346  * @param opt		MIX_MUTE mute device
347  *			MIX_UNMUTE unmute device
348  *			MIX_TOGGLEMUTE toggle device's mute
349  */
350 int
mixer_set_mute(struct mixer * m,int opt)351 mixer_set_mute(struct mixer *m, int opt)
352 {
353 	switch (opt) {
354 	case MIX_MUTE:
355 		m->mutemask |= (1 << m->dev->devno);
356 		break;
357 	case MIX_UNMUTE:
358 		m->mutemask &= ~(1 << m->dev->devno);
359 		break;
360 	case MIX_TOGGLEMUTE:
361 		m->mutemask ^= (1 << m->dev->devno);
362 		break;
363 	default:
364 		errno = EINVAL;
365 		return (-1);
366 	}
367 	if (ioctl(m->fd, SOUND_MIXER_WRITE_MUTE, &m->mutemask) < 0)
368 		return (-1);
369 	if (ioctl(m->fd, SOUND_MIXER_READ_MUTE, &m->mutemask) < 0)
370 		return (-1);
371 
372 	return 0;
373 }
374 
375 /*
376  * Modify a recording device. The selected device has to be a recording device,
377  * otherwise the function will fail.
378  *
379  * @param opt		MIX_ADDRECSRC add device to recording sources
380  *			MIX_REMOVERECSRC remove device from recording sources
381  *			MIX_SETRECSRC set device as the only recording source
382  *			MIX_TOGGLERECSRC toggle device from recording sources
383  */
384 int
mixer_mod_recsrc(struct mixer * m,int opt)385 mixer_mod_recsrc(struct mixer *m, int opt)
386 {
387 	if (!m->recmask || !MIX_ISREC(m, m->dev->devno)) {
388 		errno = ENODEV;
389 		return (-1);
390 	}
391 	switch (opt) {
392 	case MIX_ADDRECSRC:
393 		m->recsrc |= (1 << m->dev->devno);
394 		break;
395 	case MIX_REMOVERECSRC:
396 		m->recsrc &= ~(1 << m->dev->devno);
397 		break;
398 	case MIX_SETRECSRC:
399 		m->recsrc = (1 << m->dev->devno);
400 		break;
401 	case MIX_TOGGLERECSRC:
402 		m->recsrc ^= (1 << m->dev->devno);
403 		break;
404 	default:
405 		errno = EINVAL;
406 		return (-1);
407 	}
408 	if (ioctl(m->fd, SOUND_MIXER_WRITE_RECSRC, &m->recsrc) < 0)
409 		return (-1);
410 	if (ioctl(m->fd, SOUND_MIXER_READ_RECSRC, &m->recsrc) < 0)
411 		return (-1);
412 
413 	return (0);
414 }
415 
416 /*
417  * Get default audio card's number. This is used to open the default mixer
418  * and set the mixer structure's `f_default` flag.
419  */
420 int
mixer_get_dunit(void)421 mixer_get_dunit(void)
422 {
423 	size_t size;
424 	int unit;
425 
426 	size = sizeof(int);
427 	if (sysctlbyname("hw.snd.default_unit", &unit, &size, NULL, 0) < 0)
428 		return (-1);
429 
430 	return (unit);
431 }
432 
433 /*
434  * Change the default audio card. This is normally _not_ a mixer feature, but
435  * it's useful to have, so the caller can avoid having to manually use
436  * the sysctl API.
437  *
438  * @param unit		the audio card number (e.g pcm0, pcm1, ...).
439  */
440 int
mixer_set_dunit(struct mixer * m,int unit)441 mixer_set_dunit(struct mixer *m, int unit)
442 {
443 	size_t size;
444 
445 	size = sizeof(int);
446 	if (sysctlbyname("hw.snd.default_unit", NULL, 0, &unit, size) < 0)
447 		return (-1);
448 	/* XXX: how will other mixers get updated? */
449 	m->f_default = m->unit == unit;
450 
451 	return (0);
452 }
453 
454 /*
455  * Get sound device mode (none, play, rec, play+rec). Userland programs can
456  * use the MIX_MODE_* flags to determine the mode of the device.
457  */
458 int
mixer_get_mode(int unit)459 mixer_get_mode(int unit)
460 {
461 	char buf[64];
462 	size_t size;
463 	unsigned int mode;
464 
465 	(void)snprintf(buf, sizeof(buf), "dev.pcm.%d.mode", unit);
466 	size = sizeof(unsigned int);
467 	if (sysctlbyname(buf, &mode, &size, NULL, 0) < 0)
468 		return (0);
469 
470 	return (mode);
471 }
472 
473 /*
474  * Get the total number of mixers in the system.
475  */
476 int
mixer_get_nmixers(void)477 mixer_get_nmixers(void)
478 {
479 	struct mixer *m;
480 	oss_sysinfo si;
481 
482 	/*
483 	 * Open a dummy mixer because we need the `fd` field for the
484 	 * `ioctl` to work.
485 	 */
486 	if ((m = mixer_open(NULL)) == NULL)
487 		return (-1);
488 	if (ioctl(m->fd, OSS_SYSINFO, &si) < 0) {
489 		(void)mixer_close(m);
490 		return (-1);
491 	}
492 	(void)mixer_close(m);
493 
494 	return (si.nummixers);
495 }
496 
497 /*
498  * Get the full path to a mixer device.
499  */
500 int
mixer_get_path(char * buf,size_t size,int unit)501 mixer_get_path(char *buf, size_t size, int unit)
502 {
503 	size_t n;
504 
505 	if (!(unit == -1 || (unit >= 0 && unit < mixer_get_nmixers()))) {
506 		errno = EINVAL;
507 		return (-1);
508 	}
509 	if (unit == -1)
510 		n = strlcpy(buf, BASEPATH, size);
511 	else
512 		n = snprintf(buf, size, BASEPATH "%d", unit);
513 
514 	if (n >= size) {
515 		errno = ENOMEM;
516 		return (-1);
517 	}
518 
519 	return (0);
520 }
521