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