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
38static int _mixer_readvol(struct mix_dev *);
39
40/*
41 * Fetch volume from the device.
42 */
43static int
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 */
64struct mixer *
65mixer_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 {
87dunit:
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);
135fail:
136	if (m != NULL)
137		(void)mixer_close(m);
138
139	return (NULL);
140}
141
142/*
143 * Free resources and close the mixer.
144 */
145int
146mixer_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 */
172struct mix_dev *
173mixer_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 */
195struct mix_dev *
196mixer_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 */
212int
213mixer_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 */
250int
251mixer_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 */
263int
264mixer_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 */
284mix_ctl_t *
285mixer_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 */
301mix_ctl_t *
302mixer_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 */
324int
325mixer_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 */
350int
351mixer_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 */
384int
385mixer_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 */
420int
421mixer_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 */
440int
441mixer_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 */
458int
459mixer_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 */
476int
477mixer_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 */
500int
501mixer_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