]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libmixer/mixer.c
Implement and use new mixer(3) library for FreeBSD.
[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                         errno = EINVAL;
81                         goto fail;
82                 }
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                 (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             ioctl(m->fd, SNDCTL_CARDINFO, &m->ci) < 0 ||
106             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))
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 (_mixer_readvol(m, 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
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 *
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 *
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
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
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
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 *
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 *
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
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, 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
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
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
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
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_STATUS_* flags to determine the mode of the device.
455  */
456 int
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 (-1);
467
468         return (mode);
469 }
470
471 /*
472  * Get the total number of mixers in the system.
473  */
474 int
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 }