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 27 #include <dev/sound/pcm/sound.h> 28 29 #include "mixer_if.h" 30 31 SND_DECLARE_FILE("$FreeBSD$"); 32 33 MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); 34 35 #define MIXER_NAMELEN 16 36 struct snd_mixer { 37 KOBJ_FIELDS; 38 const char *type; 39 void *devinfo; 40 int busy; 41 int hwvol_muted; 42 int hwvol_mixer; 43 int hwvol_step; 44 device_t dev; 45 u_int32_t hwvol_mute_level; 46 u_int32_t devs; 47 u_int32_t recdevs; 48 u_int32_t recsrc; 49 u_int16_t level[32]; 50 char name[MIXER_NAMELEN]; 51 struct mtx *lock; 52 }; 53 54 static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = { 55 [SOUND_MIXER_VOLUME] = 75, 56 [SOUND_MIXER_BASS] = 50, 57 [SOUND_MIXER_TREBLE] = 50, 58 [SOUND_MIXER_SYNTH] = 75, 59 [SOUND_MIXER_PCM] = 75, 60 [SOUND_MIXER_SPEAKER] = 75, 61 [SOUND_MIXER_LINE] = 75, 62 [SOUND_MIXER_MIC] = 0, 63 [SOUND_MIXER_CD] = 75, 64 [SOUND_MIXER_IGAIN] = 0, 65 [SOUND_MIXER_LINE1] = 75, 66 [SOUND_MIXER_VIDEO] = 75, 67 [SOUND_MIXER_RECLEV] = 0, 68 [SOUND_MIXER_OGAIN] = 50, 69 [SOUND_MIXER_MONITOR] = 75, 70 }; 71 72 static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; 73 74 static d_open_t mixer_open; 75 static d_close_t mixer_close; 76 77 static struct cdevsw mixer_cdevsw = { 78 .d_version = D_VERSION, 79 .d_flags = D_TRACKCLOSE | D_NEEDGIANT, 80 .d_open = mixer_open, 81 .d_close = mixer_close, 82 .d_ioctl = mixer_ioctl, 83 .d_name = "mixer", 84 }; 85 86 #ifdef USING_DEVFS 87 static eventhandler_tag mixer_ehtag; 88 #endif 89 90 static struct cdev * 91 mixer_get_devt(device_t dev) 92 { 93 struct snddev_info *snddev; 94 95 snddev = device_get_softc(dev); 96 97 return snddev->mixer_dev; 98 } 99 100 #ifdef SND_DYNSYSCTL 101 static int 102 mixer_lookup(char *devname) 103 { 104 int i; 105 106 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 107 if (strncmp(devname, snd_mixernames[i], 108 strlen(snd_mixernames[i])) == 0) 109 return i; 110 return -1; 111 } 112 #endif 113 114 static int 115 mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev) 116 { 117 struct snddev_info *d; 118 unsigned l, r; 119 int v; 120 121 if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev)))) 122 return -1; 123 124 l = min((lev & 0x00ff), 100); 125 r = min(((lev & 0xff00) >> 8), 100); 126 127 d = device_get_softc(mixer->dev); 128 if (dev == SOUND_MIXER_PCM && d && 129 (d->flags & SD_F_SOFTVOL)) { 130 struct snddev_channel *sce; 131 struct pcm_channel *ch; 132 #ifdef USING_MUTEX 133 int locked = (mixer->lock && mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0; 134 135 if (locked) 136 snd_mtxunlock(mixer->lock); 137 #endif 138 SLIST_FOREACH(sce, &d->channels, link) { 139 ch = sce->channel; 140 CHN_LOCK(ch); 141 if (ch->direction == PCMDIR_PLAY && 142 (ch->feederflags & (1 << FEEDER_VOLUME))) 143 chn_setvolume(ch, l, r); 144 CHN_UNLOCK(ch); 145 } 146 #ifdef USING_MUTEX 147 if (locked) 148 snd_mtxlock(mixer->lock); 149 #endif 150 } else { 151 v = MIXER_SET(mixer, dev, l, r); 152 if (v < 0) 153 return -1; 154 } 155 156 mixer->level[dev] = l | (r << 8); 157 return 0; 158 } 159 160 static int 161 mixer_get(struct snd_mixer *mixer, int dev) 162 { 163 if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) 164 return mixer->level[dev]; 165 else return -1; 166 } 167 168 static int 169 mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src) 170 { 171 src &= mixer->recdevs; 172 if (src == 0) 173 src = SOUND_MASK_MIC; 174 mixer->recsrc = MIXER_SETRECSRC(mixer, src); 175 return 0; 176 } 177 178 static int 179 mixer_getrecsrc(struct snd_mixer *mixer) 180 { 181 return mixer->recsrc; 182 } 183 184 void 185 mix_setdevs(struct snd_mixer *m, u_int32_t v) 186 { 187 struct snddev_info *d = device_get_softc(m->dev); 188 if (d && (d->flags & SD_F_SOFTVOL)) 189 v |= SOUND_MASK_PCM; 190 m->devs = v; 191 } 192 193 void 194 mix_setrecdevs(struct snd_mixer *m, u_int32_t v) 195 { 196 m->recdevs = v; 197 } 198 199 u_int32_t 200 mix_getdevs(struct snd_mixer *m) 201 { 202 return m->devs; 203 } 204 205 u_int32_t 206 mix_getrecdevs(struct snd_mixer *m) 207 { 208 return m->recdevs; 209 } 210 211 void * 212 mix_getdevinfo(struct snd_mixer *m) 213 { 214 return m->devinfo; 215 } 216 217 int 218 mixer_init(device_t dev, kobj_class_t cls, void *devinfo) 219 { 220 struct snddev_info *snddev; 221 struct snd_mixer *m; 222 u_int16_t v; 223 struct cdev *pdev; 224 int i, unit, val; 225 226 m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO); 227 snprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev)); 228 m->lock = snd_mtxcreate(m->name, "pcm mixer"); 229 m->type = cls->name; 230 m->devinfo = devinfo; 231 m->busy = 0; 232 m->dev = dev; 233 234 if (MIXER_INIT(m)) 235 goto bad; 236 237 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 238 v = snd_mixerdefaults[i]; 239 240 if (resource_int_value(device_get_name(dev), 241 device_get_unit(dev), snd_mixernames[i], &val) == 0) { 242 if (val >= 0 && val <= 100) { 243 v = (u_int16_t) val; 244 } 245 } 246 247 mixer_set(m, i, v | (v << 8)); 248 } 249 250 mixer_setrecsrc(m, SOUND_MASK_MIC); 251 252 unit = device_get_unit(dev); 253 pdev = make_dev(&mixer_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), 254 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 255 pdev->si_drv1 = m; 256 snddev = device_get_softc(dev); 257 snddev->mixer_dev = pdev; 258 259 return 0; 260 261 bad: 262 snd_mtxlock(m->lock); 263 snd_mtxfree(m->lock); 264 kobj_delete((kobj_t)m, M_MIXER); 265 return -1; 266 } 267 268 int 269 mixer_uninit(device_t dev) 270 { 271 int i; 272 struct snd_mixer *m; 273 struct cdev *pdev; 274 275 pdev = mixer_get_devt(dev); 276 m = pdev->si_drv1; 277 snd_mtxlock(m->lock); 278 279 if (m->busy) { 280 snd_mtxunlock(m->lock); 281 return EBUSY; 282 } 283 284 pdev->si_drv1 = NULL; 285 destroy_dev(pdev); 286 287 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 288 mixer_set(m, i, 0); 289 290 mixer_setrecsrc(m, SOUND_MASK_MIC); 291 292 MIXER_UNINIT(m); 293 294 snd_mtxfree(m->lock); 295 kobj_delete((kobj_t)m, M_MIXER); 296 297 return 0; 298 } 299 300 int 301 mixer_reinit(device_t dev) 302 { 303 struct snd_mixer *m; 304 struct cdev *pdev; 305 int i; 306 307 pdev = mixer_get_devt(dev); 308 m = pdev->si_drv1; 309 snd_mtxlock(m->lock); 310 311 i = MIXER_REINIT(m); 312 if (i) { 313 snd_mtxunlock(m->lock); 314 return i; 315 } 316 317 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 318 mixer_set(m, i, m->level[i]); 319 320 mixer_setrecsrc(m, m->recsrc); 321 snd_mtxunlock(m->lock); 322 323 return 0; 324 } 325 326 #ifdef SND_DYNSYSCTL 327 static int 328 sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) 329 { 330 char devname[32]; 331 int error, dev; 332 struct snd_mixer *m; 333 334 m = oidp->oid_arg1; 335 snd_mtxlock(m->lock); 336 strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); 337 snd_mtxunlock(m->lock); 338 error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req); 339 snd_mtxlock(m->lock); 340 if (error == 0 && req->newptr != NULL) { 341 dev = mixer_lookup(devname); 342 if (dev == -1) { 343 snd_mtxunlock(m->lock); 344 return EINVAL; 345 } 346 else if (dev != m->hwvol_mixer) { 347 m->hwvol_mixer = dev; 348 m->hwvol_muted = 0; 349 } 350 } 351 snd_mtxunlock(m->lock); 352 return error; 353 } 354 #endif 355 356 int 357 mixer_hwvol_init(device_t dev) 358 { 359 struct snd_mixer *m; 360 struct cdev *pdev; 361 362 pdev = mixer_get_devt(dev); 363 m = pdev->si_drv1; 364 365 m->hwvol_mixer = SOUND_MIXER_VOLUME; 366 m->hwvol_step = 5; 367 #ifdef SND_DYNSYSCTL 368 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 369 OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, ""); 370 SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 371 OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, 372 sysctl_hw_snd_hwvol_mixer, "A", ""); 373 #endif 374 return 0; 375 } 376 377 void 378 mixer_hwvol_mute(device_t dev) 379 { 380 struct snd_mixer *m; 381 struct cdev *pdev; 382 383 pdev = mixer_get_devt(dev); 384 m = pdev->si_drv1; 385 snd_mtxlock(m->lock); 386 if (m->hwvol_muted) { 387 m->hwvol_muted = 0; 388 mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level); 389 } else { 390 m->hwvol_muted++; 391 m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer); 392 mixer_set(m, m->hwvol_mixer, 0); 393 } 394 snd_mtxunlock(m->lock); 395 } 396 397 void 398 mixer_hwvol_step(device_t dev, int left_step, int right_step) 399 { 400 struct snd_mixer *m; 401 int level, left, right; 402 struct cdev *pdev; 403 404 pdev = mixer_get_devt(dev); 405 m = pdev->si_drv1; 406 snd_mtxlock(m->lock); 407 if (m->hwvol_muted) { 408 m->hwvol_muted = 0; 409 level = m->hwvol_mute_level; 410 } else 411 level = mixer_get(m, m->hwvol_mixer); 412 if (level != -1) { 413 left = level & 0xff; 414 right = level >> 8; 415 left += left_step * m->hwvol_step; 416 if (left < 0) 417 left = 0; 418 right += right_step * m->hwvol_step; 419 if (right < 0) 420 right = 0; 421 mixer_set(m, m->hwvol_mixer, left | right << 8); 422 } 423 snd_mtxunlock(m->lock); 424 } 425 426 /* ----------------------------------------------------------------------- */ 427 428 static int 429 mixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td) 430 { 431 struct snd_mixer *m; 432 433 m = i_dev->si_drv1; 434 snd_mtxlock(m->lock); 435 436 m->busy++; 437 438 snd_mtxunlock(m->lock); 439 return 0; 440 } 441 442 static int 443 mixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td) 444 { 445 struct snd_mixer *m; 446 447 m = i_dev->si_drv1; 448 snd_mtxlock(m->lock); 449 450 if (!m->busy) { 451 snd_mtxunlock(m->lock); 452 return EBADF; 453 } 454 m->busy--; 455 456 snd_mtxunlock(m->lock); 457 return 0; 458 } 459 460 int 461 mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) 462 { 463 struct snd_mixer *m; 464 int ret, *arg_i = (int *)arg; 465 int v = -1, j = cmd & 0xff; 466 467 m = i_dev->si_drv1; 468 if (mode != -1 && !m->busy) 469 return EBADF; 470 471 snd_mtxlock(m->lock); 472 if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { 473 if (j == SOUND_MIXER_RECSRC) 474 ret = mixer_setrecsrc(m, *arg_i); 475 else 476 ret = mixer_set(m, j, *arg_i); 477 snd_mtxunlock(m->lock); 478 return (ret == 0)? 0 : ENXIO; 479 } 480 481 if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { 482 switch (j) { 483 case SOUND_MIXER_DEVMASK: 484 case SOUND_MIXER_CAPS: 485 case SOUND_MIXER_STEREODEVS: 486 v = mix_getdevs(m); 487 break; 488 489 case SOUND_MIXER_RECMASK: 490 v = mix_getrecdevs(m); 491 break; 492 493 case SOUND_MIXER_RECSRC: 494 v = mixer_getrecsrc(m); 495 break; 496 497 default: 498 v = mixer_get(m, j); 499 } 500 *arg_i = v; 501 snd_mtxunlock(m->lock); 502 return (v != -1)? 0 : ENXIO; 503 } 504 snd_mtxunlock(m->lock); 505 return ENXIO; 506 } 507 508 #ifdef USING_DEVFS 509 static void 510 mixer_clone(void *arg, struct ucred *cred, char *name, int namelen, 511 struct cdev **dev) 512 { 513 struct snddev_info *sd; 514 515 if (*dev != NULL) 516 return; 517 if (strcmp(name, "mixer") == 0) { 518 sd = devclass_get_softc(pcm_devclass, snd_unit); 519 if (sd != NULL && sd->mixer_dev != NULL) { 520 *dev = sd->mixer_dev; 521 dev_ref(*dev); 522 } 523 } 524 } 525 526 static void 527 mixer_sysinit(void *p) 528 { 529 mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000); 530 } 531 532 static void 533 mixer_sysuninit(void *p) 534 { 535 if (mixer_ehtag != NULL) 536 EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag); 537 } 538 539 SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL); 540 SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL); 541 #endif 542 543 544