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