]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libmixer/mixer.c
Add a HISTORY section for memcpy(3) and mempcpy(3)
[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
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 static int _mixer_readvol(struct mix_dev *);
39
40 /*
41  * Fetch volume from the device.
42  */
43 static 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  */
64 struct mixer *
65 mixer_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 {
87 dunit:
88                 if ((m->unit = mixer_get_dunit()) < 0)
89                         goto fail;
90                 (void)snprintf(m->name, sizeof(m->name), "/dev/mixer%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);
135 fail:
136         if (m != NULL)
137                 (void)mixer_close(m);
138
139         return (NULL);
140 }
141
142 /*
143  * Free resources and close the mixer.
144  */
145 int
146 mixer_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  */
172 struct mix_dev *
173 mixer_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  */
195 struct mix_dev *
196 mixer_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  */
212 int
213 mixer_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  */
250 int
251 mixer_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  */
263 int
264 mixer_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  */
284 mix_ctl_t *
285 mixer_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  */
301 mix_ctl_t *
302 mixer_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  */
324 int
325 mixer_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  */
350 int
351 mixer_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  */
384 int
385 mixer_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  */
420 int
421 mixer_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  */
440 int
441 mixer_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  */
458 int
459 mixer_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  */
476 int
477 mixer_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 }