mixer.c (11357B)
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 <libgen.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 char *p = NULL; 74 int i; 75 76 if ((m = calloc(1, sizeof(struct mixer))) == NULL) 77 goto fail; 78 79 if (name != NULL) { 80 /* XXX: should we remove `const` altogether? */ 81 if ((p = strdup(basename((char *)name))) == NULL) 82 goto fail; 83 if (strncmp(p, "mixer", 5) == 0 && p[5] == '\0') 84 goto dunit; 85 (void)sscanf(p, "%*[^0123456789]%d", &m->unit); 86 (void)strlcpy(m->name, name, sizeof(m->name)); 87 } else { 88 dunit: 89 if ((m->unit = mixer_get_dunit()) < 0) 90 goto fail; 91 (void)snprintf(m->name, sizeof(m->name), "/dev/mixer%d", m->unit); 92 } 93 94 if ((m->fd = open(m->name, O_RDWR)) < 0) 95 goto fail; 96 97 m->devmask = m->recmask = m->recsrc = 0; 98 m->f_default = m->unit == mixer_get_dunit(); 99 m->mode = mixer_get_mode(m->unit); 100 /* The unit number _must_ be set before the ioctl. */ 101 m->mi.dev = m->unit; 102 m->ci.card = m->unit; 103 if (ioctl(m->fd, SNDCTL_MIXERINFO, &m->mi) < 0) { 104 memset(&m->mi, 0, sizeof(m->mi)); 105 strlcpy(m->mi.name, m->name, sizeof(m->mi.name)); 106 } 107 if (ioctl(m->fd, SNDCTL_CARDINFO, &m->ci) < 0) 108 memset(&m->ci, 0, sizeof(m->ci)); 109 if(ioctl(m->fd, SOUND_MIXER_READ_DEVMASK, &m->devmask) < 0 || 110 ioctl(m->fd, SOUND_MIXER_READ_MUTE, &m->mutemask) < 0 || 111 ioctl(m->fd, SOUND_MIXER_READ_RECMASK, &m->recmask) < 0 || 112 ioctl(m->fd, SOUND_MIXER_READ_RECSRC, &m->recsrc) < 0) 113 goto fail; 114 115 TAILQ_INIT(&m->devs); 116 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 117 if (!MIX_ISDEV(m, i)) 118 continue; 119 if ((dp = calloc(1, sizeof(struct mix_dev))) == NULL) 120 goto fail; 121 dp->parent_mixer = m; 122 dp->devno = i; 123 dp->nctl = 0; 124 if (_mixer_readvol(m, dp) < 0) 125 goto fail; 126 (void)strlcpy(dp->name, names[i], sizeof(dp->name)); 127 TAILQ_INIT(&dp->ctls); 128 TAILQ_INSERT_TAIL(&m->devs, dp, devs); 129 m->ndev++; 130 } 131 m->dev = TAILQ_FIRST(&m->devs); 132 133 return (m); 134 fail: 135 if (p != NULL) 136 free(p); 137 /* XXX: do we need this? */ 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_STATUS_* 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 }