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 } 494