1 /*- 2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: src/sys/dev/sound/pcm/mixer.c,v 1.43.2.4 2006/04/04 17:43:48 ariff Exp $ 27 * $DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.14 2007/01/04 21:47:03 corecode Exp $ 28 */ 29 30 #include <dev/sound/pcm/sound.h> 31 32 #include "mixer_if.h" 33 34 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.14 2007/01/04 21:47:03 corecode Exp $"); 35 36 MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); 37 38 #define MIXER_NAMELEN 16 39 struct snd_mixer { 40 KOBJ_FIELDS; 41 const char *type; 42 void *devinfo; 43 int busy; 44 int hwvol_muted; 45 int hwvol_mixer; 46 int hwvol_step; 47 device_t dev; 48 u_int32_t hwvol_mute_level; 49 u_int32_t devs; 50 u_int32_t recdevs; 51 u_int32_t recsrc; 52 u_int16_t level[32]; 53 char name[MIXER_NAMELEN]; 54 struct spinlock *lock; 55 }; 56 57 static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = { 58 [SOUND_MIXER_VOLUME] = 75, 59 [SOUND_MIXER_BASS] = 50, 60 [SOUND_MIXER_TREBLE] = 50, 61 [SOUND_MIXER_SYNTH] = 75, 62 [SOUND_MIXER_PCM] = 75, 63 [SOUND_MIXER_SPEAKER] = 75, 64 [SOUND_MIXER_LINE] = 75, 65 [SOUND_MIXER_MIC] = 0, 66 [SOUND_MIXER_CD] = 75, 67 [SOUND_MIXER_IGAIN] = 0, 68 [SOUND_MIXER_LINE1] = 75, 69 [SOUND_MIXER_VIDEO] = 75, 70 [SOUND_MIXER_RECLEV] = 0, 71 [SOUND_MIXER_OGAIN] = 50, 72 [SOUND_MIXER_MONITOR] = 75, 73 }; 74 75 static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; 76 77 static d_open_t mixer_open; 78 static d_close_t mixer_close; 79 80 static struct dev_ops mixer_cdevsw = { 81 { "mixer", SND_CDEV_MAJOR, D_TRACKCLOSE }, 82 /* .d_flags = D_TRACKCLOSE | D_NEEDGIANT, */ 83 .d_open = mixer_open, 84 .d_close = mixer_close, 85 .d_ioctl = mixer_ioctl, 86 }; 87 88 #ifdef USING_DEVFS 89 static eventhandler_tag mixer_ehtag; 90 #endif 91 92 static struct cdev * 93 mixer_get_devt(device_t dev) 94 { 95 struct snddev_info *snddev; 96 97 snddev = device_get_softc(dev); 98 99 return snddev->mixer_dev; 100 } 101 102 #ifdef SND_DYNSYSCTL 103 static int 104 mixer_lookup(char *devname) 105 { 106 int i; 107 108 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 109 if (strncmp(devname, snd_mixernames[i], 110 strlen(snd_mixernames[i])) == 0) 111 return i; 112 return -1; 113 } 114 #endif 115 116 /* 117 * Always called with mixer->lock held. 118 */ 119 static int 120 mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev) 121 { 122 struct snddev_info *d; 123 unsigned l, r; 124 int v; 125 126 if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev)))) 127 return -1; 128 129 l = min((lev & 0x00ff), 100); 130 r = min(((lev & 0xff00) >> 8), 100); 131 132 d = device_get_softc(mixer->dev); 133 if (dev == SOUND_MIXER_PCM && d && 134 (d->flags & SD_F_SOFTVOL)) { 135 struct snddev_channel *sce; 136 struct pcm_channel *ch; 137 138 snd_mtxunlock(mixer->lock); 139 SLIST_FOREACH(sce, &d->channels, link) { 140 ch = sce->channel; 141 CHN_LOCK(ch); 142 if (ch->direction == PCMDIR_PLAY && 143 (ch->feederflags & (1 << FEEDER_VOLUME))) 144 chn_setvolume(ch, l, r); 145 CHN_UNLOCK(ch); 146 } 147 snd_mtxlock(mixer->lock); 148 } else { 149 v = MIXER_SET(mixer, dev, l, r); 150 if (v < 0) 151 return -1; 152 } 153 154 mixer->level[dev] = l | (r << 8); 155 return 0; 156 } 157 158 static int 159 mixer_get(struct snd_mixer *mixer, int dev) 160 { 161 if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) 162 return mixer->level[dev]; 163 else return -1; 164 } 165 166 static int 167 mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src) 168 { 169 src &= mixer->recdevs; 170 if (src == 0) 171 src = SOUND_MASK_MIC; 172 mixer->recsrc = MIXER_SETRECSRC(mixer, src); 173 return 0; 174 } 175 176 static int 177 mixer_getrecsrc(struct snd_mixer *mixer) 178 { 179 return mixer->recsrc; 180 } 181 182 void 183 mix_setdevs(struct snd_mixer *m, u_int32_t v) 184 { 185 struct snddev_info *d = device_get_softc(m->dev); 186 if (d && (d->flags & SD_F_SOFTVOL)) 187 v |= SOUND_MASK_PCM; 188 m->devs = v; 189 } 190 191 void 192 mix_setrecdevs(struct snd_mixer *m, u_int32_t v) 193 { 194 m->recdevs = v; 195 } 196 197 u_int32_t 198 mix_getdevs(struct snd_mixer *m) 199 { 200 return m->devs; 201 } 202 203 u_int32_t 204 mix_getrecdevs(struct snd_mixer *m) 205 { 206 return m->recdevs; 207 } 208 209 void * 210 mix_getdevinfo(struct snd_mixer *m) 211 { 212 return m->devinfo; 213 } 214 215 int 216 mixer_init(device_t dev, kobj_class_t cls, void *devinfo) 217 { 218 struct snddev_info *snddev; 219 struct snd_mixer *m; 220 u_int16_t v; 221 struct cdev *pdev; 222 int i, unit, val; 223 224 m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO); 225 ksnprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev)); 226 m->lock = snd_mtxcreate(m->name, "pcm mixer"); 227 m->type = cls->name; 228 m->devinfo = devinfo; 229 m->busy = 0; 230 m->dev = dev; 231 232 if (MIXER_INIT(m)) 233 goto bad; 234 235 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 236 v = snd_mixerdefaults[i]; 237 238 if (resource_int_value(device_get_name(dev), 239 device_get_unit(dev), snd_mixernames[i], &val) == 0) { 240 if (val >= 0 && val <= 100) { 241 v = (u_int16_t) val; 242 } 243 } 244 245 snd_mtxlock(m->lock); 246 mixer_set(m, i, v | (v << 8)); 247 snd_mtxunlock(m->lock); 248 } 249 250 mixer_setrecsrc(m, SOUND_MASK_MIC); 251 252 unit = device_get_unit(dev); 253 dev_ops_add(&mixer_cdevsw, -1, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 254 pdev = make_dev(&mixer_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), 255 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 256 reference_dev(pdev); 257 pdev->si_drv1 = m; 258 snddev = device_get_softc(dev); 259 snddev->mixer_dev = pdev; 260 261 return 0; 262 263 bad: 264 snd_mtxfree(m->lock); 265 kobj_delete((kobj_t)m, M_MIXER); 266 return -1; 267 } 268 269 int 270 mixer_uninit(device_t dev) 271 { 272 int i; 273 int unit; 274 struct snddev_info *d; 275 struct snd_mixer *m; 276 struct cdev *pdev; 277 278 d = device_get_softc(dev); 279 pdev = mixer_get_devt(dev); 280 if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL) 281 return EBADF; 282 m = pdev->si_drv1; 283 snd_mtxlock(m->lock); 284 285 if (m->busy) { 286 snd_mtxunlock(m->lock); 287 return EBUSY; 288 } 289 290 pdev->si_drv1 = NULL; 291 release_dev(pdev); 292 unit = device_get_unit(dev); 293 dev_ops_remove(&mixer_cdevsw, -1, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 294 295 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 296 mixer_set(m, i, 0); 297 298 mixer_setrecsrc(m, SOUND_MASK_MIC); 299 300 MIXER_UNINIT(m); 301 302 snd_mtxunlock(m->lock); 303 snd_mtxfree(m->lock); 304 kobj_delete((kobj_t)m, M_MIXER); 305 306 d->mixer_dev = NULL; 307 308 return 0; 309 } 310 311 int 312 mixer_reinit(device_t dev) 313 { 314 struct snd_mixer *m; 315 struct cdev *pdev; 316 int i; 317 318 pdev = mixer_get_devt(dev); 319 m = pdev->si_drv1; 320 snd_mtxlock(m->lock); 321 322 i = MIXER_REINIT(m); 323 if (i) { 324 snd_mtxunlock(m->lock); 325 return i; 326 } 327 328 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 329 mixer_set(m, i, m->level[i]); 330 331 mixer_setrecsrc(m, m->recsrc); 332 snd_mtxunlock(m->lock); 333 334 return 0; 335 } 336 337 #ifdef SND_DYNSYSCTL 338 static int 339 sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) 340 { 341 char devname[32]; 342 int error, dev; 343 struct snd_mixer *m; 344 345 m = oidp->oid_arg1; 346 snd_mtxlock(m->lock); 347 strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); 348 snd_mtxunlock(m->lock); 349 error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req); 350 snd_mtxlock(m->lock); 351 if (error == 0 && req->newptr != NULL) { 352 dev = mixer_lookup(devname); 353 if (dev == -1) { 354 snd_mtxunlock(m->lock); 355 return EINVAL; 356 } 357 else if (dev != m->hwvol_mixer) { 358 m->hwvol_mixer = dev; 359 m->hwvol_muted = 0; 360 } 361 } 362 snd_mtxunlock(m->lock); 363 return error; 364 } 365 #endif 366 367 int 368 mixer_hwvol_init(device_t dev) 369 { 370 struct snd_mixer *m; 371 struct cdev *pdev; 372 373 pdev = mixer_get_devt(dev); 374 m = pdev->si_drv1; 375 376 m->hwvol_mixer = SOUND_MIXER_VOLUME; 377 m->hwvol_step = 5; 378 #ifdef SND_DYNSYSCTL 379 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 380 OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, ""); 381 SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 382 OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, 383 sysctl_hw_snd_hwvol_mixer, "A", ""); 384 #endif 385 return 0; 386 } 387 388 void 389 mixer_hwvol_mute(device_t dev) 390 { 391 struct snd_mixer *m; 392 struct cdev *pdev; 393 394 pdev = mixer_get_devt(dev); 395 m = pdev->si_drv1; 396 snd_mtxlock(m->lock); 397 if (m->hwvol_muted) { 398 m->hwvol_muted = 0; 399 mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level); 400 } else { 401 m->hwvol_muted++; 402 m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer); 403 mixer_set(m, m->hwvol_mixer, 0); 404 } 405 snd_mtxunlock(m->lock); 406 } 407 408 void 409 mixer_hwvol_step(device_t dev, int left_step, int right_step) 410 { 411 struct snd_mixer *m; 412 int level, left, right; 413 struct cdev *pdev; 414 415 pdev = mixer_get_devt(dev); 416 m = pdev->si_drv1; 417 snd_mtxlock(m->lock); 418 if (m->hwvol_muted) { 419 m->hwvol_muted = 0; 420 level = m->hwvol_mute_level; 421 } else 422 level = mixer_get(m, m->hwvol_mixer); 423 if (level != -1) { 424 left = level & 0xff; 425 right = level >> 8; 426 left += left_step * m->hwvol_step; 427 if (left < 0) 428 left = 0; 429 right += right_step * m->hwvol_step; 430 if (right < 0) 431 right = 0; 432 mixer_set(m, m->hwvol_mixer, left | right << 8); 433 } 434 snd_mtxunlock(m->lock); 435 } 436 437 /* ----------------------------------------------------------------------- */ 438 439 static int 440 mixer_open(struct dev_open_args *ap) 441 { 442 struct cdev *i_dev = ap->a_head.a_dev; 443 struct snd_mixer *m; 444 445 m = i_dev->si_drv1; 446 snd_mtxlock(m->lock); 447 448 m->busy++; 449 450 snd_mtxunlock(m->lock); 451 return 0; 452 } 453 454 static int 455 mixer_close(struct dev_close_args *ap) 456 { 457 struct cdev *i_dev = ap->a_head.a_dev; 458 struct snd_mixer *m; 459 460 m = i_dev->si_drv1; 461 snd_mtxlock(m->lock); 462 463 if (!m->busy) { 464 snd_mtxunlock(m->lock); 465 return EBADF; 466 } 467 m->busy--; 468 469 snd_mtxunlock(m->lock); 470 return 0; 471 } 472 473 int 474 mixer_ioctl(struct dev_ioctl_args *ap) 475 { 476 struct cdev *i_dev = ap->a_head.a_dev; 477 u_long cmd = ap->a_cmd; 478 caddr_t arg = ap->a_data; 479 int mode = ap->a_fflag; 480 struct snd_mixer *m; 481 int ret, *arg_i = (int *)arg; 482 int v = -1, j = cmd & 0xff; 483 484 m = i_dev->si_drv1; 485 486 if (m == NULL) 487 return EBADF; 488 489 snd_mtxlock(m->lock); 490 if (mode != -1 && !m->busy) { 491 snd_mtxunlock(m->lock); 492 return EBADF; 493 } 494 495 if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { 496 if (j == SOUND_MIXER_RECSRC) 497 ret = mixer_setrecsrc(m, *arg_i); 498 else 499 ret = mixer_set(m, j, *arg_i); 500 snd_mtxunlock(m->lock); 501 return (ret == 0)? 0 : ENXIO; 502 } 503 504 if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { 505 switch (j) { 506 case SOUND_MIXER_DEVMASK: 507 case SOUND_MIXER_CAPS: 508 case SOUND_MIXER_STEREODEVS: 509 v = mix_getdevs(m); 510 break; 511 512 case SOUND_MIXER_RECMASK: 513 v = mix_getrecdevs(m); 514 break; 515 516 case SOUND_MIXER_RECSRC: 517 v = mixer_getrecsrc(m); 518 break; 519 520 default: 521 v = mixer_get(m, j); 522 } 523 *arg_i = v; 524 snd_mtxunlock(m->lock); 525 return (v != -1)? 0 : ENXIO; 526 } 527 snd_mtxunlock(m->lock); 528 return ENXIO; 529 } 530 531 #ifdef USING_DEVFS 532 static void 533 mixer_clone(void *arg, struct ucred *cred, char *name, int namelen, 534 struct cdev **dev) 535 { 536 struct snddev_info *sd; 537 538 if (*dev != NULL) 539 return; 540 if (strcmp(name, "mixer") == 0) { 541 sd = devclass_get_softc(pcm_devclass, snd_unit); 542 if (sd != NULL && sd->mixer_dev != NULL) { 543 *dev = sd->mixer_dev; 544 dev_ref(*dev); 545 } 546 } 547 } 548 549 static void 550 mixer_sysinit(void *p) 551 { 552 mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000); 553 } 554 555 static void 556 mixer_sysuninit(void *p) 557 { 558 if (mixer_ehtag != NULL) 559 EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag); 560 } 561 562 SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL); 563 SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL); 564 #endif 565 566 567