]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libmixer/mixer.c
mixer(3): Fix for header file pollution.
[FreeBSD/FreeBSD.git] / lib / libmixer / mixer.c
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  * $FreeBSD$
23  */
24
25 #include <sys/types.h>
26 #include <sys/ioctl.h>
27 #include <sys/sysctl.h>
28 #include <sys/soundcard.h>
29
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 #include "mixer.h"
38
39 #define BASEPATH "/dev/mixer"
40
41 static int _mixer_readvol(struct mixer *, struct mix_dev *);
42
43 /*
44  * Fetch volume from the device.
45  */
46 static int
47 _mixer_readvol(struct mixer *m, struct mix_dev *dev)
48 {
49         int v;
50
51         if (ioctl(m->fd, MIXER_READ(dev->devno), &v) < 0)
52                 return (-1);
53         dev->vol.left = MIX_VOLNORM(v & 0x00ff);
54         dev->vol.right = MIX_VOLNORM((v >> 8) & 0x00ff);
55
56         return (0);
57 }
58
59 /*
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.
63  *
64  * @param name          path to mixer device. NULL or "/dev/mixer" for the
65  *                      the default mixer (i.e `hw.snd.default_unit`).
66  */
67 struct mixer *
68 mixer_open(const char *name)
69 {
70         struct mixer *m = NULL;
71         struct mix_dev *dp;
72         const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
73         int i;
74
75         if ((m = calloc(1, sizeof(struct mixer))) == NULL)
76                 goto fail;
77
78         if (name != NULL) {
79                 /* `name` does not start with "/dev/mixer". */
80                 if (strncmp(name, BASEPATH, strlen(BASEPATH)) != 0) {
81                         m->unit = -1;
82                 } else {
83                         /* `name` is "/dev/mixer" so, we'll use the default unit. */
84                         if (strncmp(name, BASEPATH, strlen(name)) == 0)
85                                 goto dunit;
86                         m->unit = strtol(name + strlen(BASEPATH), NULL, 10);
87                 }
88                 (void)strlcpy(m->name, name, sizeof(m->name));
89         } else {
90 dunit:
91                 if ((m->unit = mixer_get_dunit()) < 0)
92                         goto fail;
93                 (void)snprintf(m->name, sizeof(m->name), "/dev/mixer%d", m->unit);
94         }
95
96         if ((m->fd = open(m->name, O_RDWR)) < 0)
97                 goto fail;
98
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. */
103         m->mi.dev = m->unit;
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));
108         }
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)
115                 goto fail;
116
117         TAILQ_INIT(&m->devs);
118         for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
119                 if (!MIX_ISDEV(m, i))
120                         continue;
121                 if ((dp = calloc(1, sizeof(struct mix_dev))) == NULL)
122                         goto fail;
123                 dp->parent_mixer = m;
124                 dp->devno = i;
125                 dp->nctl = 0;
126                 if (_mixer_readvol(m, dp) < 0)
127                         goto fail;
128                 (void)strlcpy(dp->name, names[i], sizeof(dp->name));
129                 TAILQ_INIT(&dp->ctls);
130                 TAILQ_INSERT_TAIL(&m->devs, dp, devs);
131                 m->ndev++;
132         }
133
134         /* The default device is always "vol". */
135         m->dev = TAILQ_FIRST(&m->devs);
136
137         return (m);
138 fail:
139         if (m != NULL)
140                 (void)mixer_close(m);
141
142         return (NULL);
143 }
144
145 /*
146  * Free resources and close the mixer.
147  */
148 int
149 mixer_close(struct mixer *m)
150 {
151         struct mix_dev *dp;
152         int r;
153
154         r = close(m->fd);
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));
160                 free(dp);
161         }
162         free(m);
163
164         return (r);
165 }
166
167 /*
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.
172  *
173  * The caller must manually assign the return value to `m->dev`.
174  */
175 struct mix_dev *
176 mixer_get_dev(struct mixer *m, int dev)
177 {
178         struct mix_dev *dp;
179
180         if (dev < 0 || dev >= m->ndev) {
181                 errno = ERANGE;
182                 return (NULL);
183         }
184         TAILQ_FOREACH(dp, &m->devs, devs) {
185                 if (dp->devno == dev)
186                         return (dp);
187         }
188         errno = EINVAL;
189
190         return (NULL);
191 }
192
193 /*
194  * Select a device by name.
195  *
196  * @param name          device name (e.g vol, pcm, ...)
197  */
198 struct mix_dev *
199 mixer_get_dev_byname(struct mixer *m, const char *name)
200 {
201         struct mix_dev *dp;
202
203         TAILQ_FOREACH(dp, &m->devs, devs) {
204                 if (!strncmp(dp->name, name, sizeof(dp->name)))
205                         return (dp);
206         }
207         errno = EINVAL;
208
209         return (NULL);
210 }
211
212 /*
213  * Add a mixer control to a device.
214  */
215 int
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 *))
219 {
220         struct mix_dev *dp;
221         mix_ctl_t *ctl, *cp;
222
223         /* XXX: should we accept NULL name? */
224         if (parent_dev == NULL) {
225                 errno = EINVAL;
226                 return (-1);
227         }
228         if ((ctl = calloc(1, sizeof(mix_ctl_t))) == NULL)
229                 return (-1);
230         ctl->parent_dev = parent_dev;
231         ctl->id = id;
232         if (name != NULL)
233                 (void)strlcpy(ctl->name, name, sizeof(ctl->name));
234         ctl->mod = mod;
235         ctl->print = print;
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) {
240                         errno = EINVAL;
241                         return (-1);
242                 }
243         }
244         TAILQ_INSERT_TAIL(&dp->ctls, ctl, ctls);
245         dp->nctl++;
246
247         return (0);
248 }
249
250 /*
251  * Same as `mixer_add_ctl`.
252  */
253 int
254 mixer_add_ctl_s(mix_ctl_t *ctl)
255 {
256         if (ctl == NULL)
257                 return (-1);
258
259         return (mixer_add_ctl(ctl->parent_dev, ctl->id, ctl->name,
260             ctl->mod, ctl->print));
261 }
262
263 /*
264  * Remove a mixer control from a device.
265  */
266 int
267 mixer_remove_ctl(mix_ctl_t *ctl)
268 {
269         struct mix_dev *p;
270
271         if (ctl == NULL) {
272                 errno = EINVAL;
273                 return (-1);
274         }
275         p = ctl->parent_dev;
276         if (!TAILQ_EMPTY(&p->ctls)) {
277                 TAILQ_REMOVE(&p->ctls, ctl, ctls);
278                 free(ctl);
279         }
280
281         return (0);
282 }
283
284 /*
285  * Get a mixer control by id.
286  */
287 mix_ctl_t *
288 mixer_get_ctl(struct mix_dev *d, int id)
289 {
290         mix_ctl_t *cp;
291
292         TAILQ_FOREACH(cp, &d->ctls, ctls) {
293                 if (cp->id == id)
294                         return (cp);
295         }
296         errno = EINVAL;
297
298         return (NULL);
299 }
300
301 /*
302  * Get a mixer control by name.
303  */
304 mix_ctl_t *
305 mixer_get_ctl_byname(struct mix_dev *d, const char *name)
306 {
307         mix_ctl_t *cp;
308
309         TAILQ_FOREACH(cp, &d->ctls, ctls) {
310                 if (!strncmp(cp->name, name, sizeof(cp->name)))
311                         return (cp);
312         }
313         errno = EINVAL;
314
315         return (NULL);
316 }
317
318 /*
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`.
324  *
325  * Volume clumping should be done by the caller.
326  */
327 int
328 mixer_set_vol(struct mixer *m, mix_volume_t vol)
329 {
330         int v;
331
332         if (vol.left < MIX_VOLMIN || vol.left > MIX_VOLMAX ||
333             vol.right < MIX_VOLMIN || vol.right > MIX_VOLMAX) {
334                 errno = ERANGE;
335                 return (-1);
336         }
337         v = MIX_VOLDENORM(vol.left) | MIX_VOLDENORM(vol.right) << 8;
338         if (ioctl(m->fd, MIXER_WRITE(m->dev->devno), &v) < 0)
339                 return (-1);
340         if (_mixer_readvol(m, m->dev) < 0)
341                 return (-1);
342
343         return (0);
344 }
345
346 /*
347  * Manipulate a device's mute.
348  *
349  * @param opt           MIX_MUTE mute device
350  *                      MIX_UNMUTE unmute device
351  *                      MIX_TOGGLEMUTE toggle device's mute
352  */
353 int
354 mixer_set_mute(struct mixer *m, int opt)
355 {
356         switch (opt) {
357         case MIX_MUTE:
358                 m->mutemask |= (1 << m->dev->devno);
359                 break;
360         case MIX_UNMUTE:
361                 m->mutemask &= ~(1 << m->dev->devno);
362                 break;
363         case MIX_TOGGLEMUTE:
364                 m->mutemask ^= (1 << m->dev->devno);
365                 break;
366         default:
367                 errno = EINVAL;
368                 return (-1);
369         }
370         if (ioctl(m->fd, SOUND_MIXER_WRITE_MUTE, &m->mutemask) < 0)
371                 return (-1);
372         if (ioctl(m->fd, SOUND_MIXER_READ_MUTE, &m->mutemask) < 0)
373                 return (-1);
374
375         return 0;
376 }
377
378 /*
379  * Modify a recording device. The selected device has to be a recording device,
380  * otherwise the function will fail.
381  *
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
386  */
387 int
388 mixer_mod_recsrc(struct mixer *m, int opt)
389 {
390         if (!m->recmask || !MIX_ISREC(m, m->dev->devno)) {
391                 errno = ENODEV;
392                 return (-1);
393         }
394         switch (opt) {
395         case MIX_ADDRECSRC:
396                 m->recsrc |= (1 << m->dev->devno);
397                 break;
398         case MIX_REMOVERECSRC:
399                 m->recsrc &= ~(1 << m->dev->devno);
400                 break;
401         case MIX_SETRECSRC:
402                 m->recsrc = (1 << m->dev->devno);
403                 break;
404         case MIX_TOGGLERECSRC:
405                 m->recsrc ^= (1 << m->dev->devno);
406                 break;
407         default:
408                 errno = EINVAL;
409                 return (-1);
410         }
411         if (ioctl(m->fd, SOUND_MIXER_WRITE_RECSRC, &m->recsrc) < 0)
412                 return (-1);
413         if (ioctl(m->fd, SOUND_MIXER_READ_RECSRC, &m->recsrc) < 0)
414                 return (-1);
415
416         return (0);
417 }
418
419 /*
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.
422  */
423 int
424 mixer_get_dunit(void)
425 {
426         size_t size;
427         int unit;
428
429         size = sizeof(int);
430         if (sysctlbyname("hw.snd.default_unit", &unit, &size, NULL, 0) < 0)
431                 return (-1);
432
433         return (unit);
434 }
435
436 /*
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
439  * the sysctl API.
440  *
441  * @param unit          the audio card number (e.g pcm0, pcm1, ...).
442  */
443 int
444 mixer_set_dunit(struct mixer *m, int unit)
445 {
446         size_t size;
447
448         size = sizeof(int);
449         if (sysctlbyname("hw.snd.default_unit", NULL, 0, &unit, size) < 0)
450                 return (-1);
451         /* XXX: how will other mixers get updated? */
452         m->f_default = m->unit == unit;
453
454         return (0);
455 }
456
457 /*
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.
460  */
461 int
462 mixer_get_mode(int unit)
463 {
464         char buf[64];
465         size_t size;
466         unsigned int mode;
467
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)
471                 return (0);
472
473         return (mode);
474 }
475
476 /*
477  * Get the total number of mixers in the system.
478  */
479 int
480 mixer_get_nmixers(void)
481 {
482         struct mixer *m;
483         oss_sysinfo si;
484
485         /*
486          * Open a dummy mixer because we need the `fd` field for the
487          * `ioctl` to work.
488          */
489         if ((m = mixer_open(NULL)) == NULL)
490                 return (-1);
491         if (ioctl(m->fd, OSS_SYSINFO, &si) < 0) {
492                 (void)mixer_close(m);
493                 return (-1);
494         }
495         (void)mixer_close(m);
496
497         return (si.nummixers);
498 }