1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 5 * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 6 * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> 7 * Copyright (c) 1997 Luigi Rizzo 8 * All rights reserved. 9 * Copyright (c) 2024-2025 The FreeBSD Foundation 10 * 11 * Portions of this software were developed by Christos Margiolis 12 * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifdef HAVE_KERNEL_OPTION_HEADERS 37 #include "opt_snd.h" 38 #endif 39 40 #include <dev/sound/pcm/sound.h> 41 #include <dev/sound/pcm/ac97.h> 42 #include <dev/sound/pcm/vchan.h> 43 #include <dev/sound/pcm/dsp.h> 44 #include <sys/limits.h> 45 #include <sys/sysctl.h> 46 47 #include "feeder_if.h" 48 49 devclass_t pcm_devclass; 50 51 int snd_unit = -1; 52 53 static int snd_unit_auto = -1; 54 SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN, 55 &snd_unit_auto, 0, "assign default unit to a newly attached device"); 56 57 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 58 "Sound driver"); 59 60 /** 61 * @brief Unit number allocator for syncgroup IDs 62 */ 63 struct unrhdr *pcmsg_unrhdr = NULL; 64 65 int 66 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 67 { 68 struct snddev_info *d; 69 70 flags &= INTR_MPSAFE; 71 flags |= INTR_TYPE_AV; 72 d = device_get_softc(dev); 73 if (d != NULL && (flags & INTR_MPSAFE)) 74 d->flags |= SD_F_MPSAFE; 75 76 return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep); 77 } 78 79 static int 80 sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 81 { 82 struct snddev_info *d; 83 int error, unit; 84 85 unit = snd_unit; 86 error = sysctl_handle_int(oidp, &unit, 0, req); 87 if (error == 0 && req->newptr != NULL) { 88 bus_topo_lock(); 89 d = devclass_get_softc(pcm_devclass, unit); 90 if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) { 91 bus_topo_unlock(); 92 return EINVAL; 93 } 94 snd_unit = unit; 95 snd_unit_auto = 0; 96 bus_topo_unlock(); 97 } 98 return (error); 99 } 100 /* XXX: do we need a way to let the user change the default unit? */ 101 SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, 102 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE, 0, 103 sizeof(int), sysctl_hw_snd_default_unit, "I", 104 "default sound device"); 105 106 int 107 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 108 { 109 struct snddev_info *d = device_get_softc(dev); 110 struct pcm_channel *ch; 111 int err = 0; 112 113 PCM_LOCK(d); 114 PCM_WAIT(d); 115 PCM_ACQUIRE(d); 116 ch = chn_init(d, NULL, cls, dir, devinfo); 117 if (!ch) { 118 device_printf(d->dev, "chn_init(%s, %d, %p) failed\n", 119 cls->name, dir, devinfo); 120 err = ENODEV; 121 } 122 PCM_RELEASE(d); 123 PCM_UNLOCK(d); 124 125 return (err); 126 } 127 128 static void 129 pcm_killchans(struct snddev_info *d) 130 { 131 struct pcm_channel *ch; 132 bool again; 133 134 PCM_BUSYASSERT(d); 135 KASSERT(!PCM_REGISTERED(d), ("%s(): still registered\n", __func__)); 136 137 for (;;) { 138 again = false; 139 /* Make sure all channels are stopped. */ 140 CHN_FOREACH(ch, d, channels.pcm) { 141 CHN_LOCK(ch); 142 if (ch->inprog == 0 && ch->sleeping == 0 && 143 CHN_STOPPED(ch)) { 144 CHN_UNLOCK(ch); 145 continue; 146 } 147 chn_shutdown(ch); 148 if (ch->direction == PCMDIR_PLAY) 149 chn_flush(ch); 150 else 151 chn_abort(ch); 152 CHN_UNLOCK(ch); 153 again = true; 154 } 155 /* 156 * Some channels are still active. Sleep for a bit and try 157 * again. 158 */ 159 if (again) 160 pause_sbt("pcmkillchans", mstosbt(5), 0, 0); 161 else 162 break; 163 } 164 165 /* All channels are finally dead. */ 166 while (!CHN_EMPTY(d, channels.pcm)) { 167 ch = CHN_FIRST(d, channels.pcm); 168 chn_kill(ch); 169 } 170 171 if (d->p_unr != NULL) 172 delete_unrhdr(d->p_unr); 173 if (d->vp_unr != NULL) 174 delete_unrhdr(d->vp_unr); 175 if (d->r_unr != NULL) 176 delete_unrhdr(d->r_unr); 177 if (d->vr_unr != NULL) 178 delete_unrhdr(d->vr_unr); 179 } 180 181 static int 182 pcm_best_unit(int old) 183 { 184 struct snddev_info *d; 185 int i, best, bestprio, prio; 186 187 best = -1; 188 bestprio = -100; 189 bus_topo_lock(); 190 for (i = 0; pcm_devclass != NULL && 191 i < devclass_get_maxunit(pcm_devclass); i++) { 192 d = devclass_get_softc(pcm_devclass, i); 193 if (!PCM_REGISTERED(d)) 194 continue; 195 prio = 0; 196 if (d->playcount == 0) 197 prio -= 10; 198 if (d->reccount == 0) 199 prio -= 2; 200 if (prio > bestprio || (prio == bestprio && i == old)) { 201 best = i; 202 bestprio = prio; 203 } 204 } 205 bus_topo_unlock(); 206 207 return (best); 208 } 209 210 uint32_t 211 pcm_getflags(device_t dev) 212 { 213 struct snddev_info *d = device_get_softc(dev); 214 215 return d->flags; 216 } 217 218 void 219 pcm_setflags(device_t dev, uint32_t val) 220 { 221 struct snddev_info *d = device_get_softc(dev); 222 223 d->flags = val; 224 } 225 226 void * 227 pcm_getdevinfo(device_t dev) 228 { 229 struct snddev_info *d = device_get_softc(dev); 230 231 return d->devinfo; 232 } 233 234 unsigned int 235 pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 236 { 237 struct snddev_info *d = device_get_softc(dev); 238 int sz, x; 239 240 sz = 0; 241 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 242 x = sz; 243 RANGE(sz, minbufsz, maxbufsz); 244 if (x != sz) 245 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 246 x = minbufsz; 247 while (x < sz) 248 x <<= 1; 249 if (x > sz) 250 x >>= 1; 251 if (x != sz) { 252 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 253 sz = x; 254 } 255 } else { 256 sz = deflt; 257 } 258 259 d->bufsz = sz; 260 261 return sz; 262 } 263 264 static int 265 sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) 266 { 267 struct snddev_info *d; 268 int err, val; 269 270 d = oidp->oid_arg1; 271 if (!PCM_REGISTERED(d)) 272 return (ENODEV); 273 274 PCM_LOCK(d); 275 PCM_WAIT(d); 276 val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; 277 PCM_ACQUIRE(d); 278 PCM_UNLOCK(d); 279 280 err = sysctl_handle_int(oidp, &val, 0, req); 281 282 if (err == 0 && req->newptr != NULL) { 283 if (!(val == 0 || val == 1)) { 284 PCM_RELEASE_QUICK(d); 285 return (EINVAL); 286 } 287 288 PCM_LOCK(d); 289 290 d->flags &= ~SD_F_BITPERFECT; 291 d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; 292 293 PCM_RELEASE(d); 294 PCM_UNLOCK(d); 295 } else 296 PCM_RELEASE_QUICK(d); 297 298 return (err); 299 } 300 301 static int 302 sysctl_dev_pcm_mode(SYSCTL_HANDLER_ARGS) 303 { 304 struct snddev_info *d; 305 int mode = 0; 306 307 d = oidp->oid_arg1; 308 if (!PCM_REGISTERED(d)) 309 return (ENODEV); 310 311 PCM_LOCK(d); 312 if (d->playcount > 0) 313 mode |= PCM_MODE_PLAY; 314 if (d->reccount > 0) 315 mode |= PCM_MODE_REC; 316 if (d->mixer_dev != NULL) 317 mode |= PCM_MODE_MIXER; 318 PCM_UNLOCK(d); 319 320 return (sysctl_handle_int(oidp, &mode, 0, req)); 321 } 322 323 /* 324 * Basic initialization so that drivers can use pcm_addchan() before 325 * pcm_register(). 326 */ 327 void 328 pcm_init(device_t dev, void *devinfo) 329 { 330 struct snddev_info *d; 331 int i; 332 333 d = device_get_softc(dev); 334 d->dev = dev; 335 mtx_init(&d->lock, device_get_nameunit(dev), "sound cdev", MTX_DEF); 336 cv_init(&d->cv, device_get_nameunit(dev)); 337 338 i = 0; 339 if (resource_int_value(device_get_name(dev), device_get_unit(dev), 340 "vpc", &i) != 0 || i != 0) 341 d->flags |= SD_F_VPC; 342 343 if (resource_int_value(device_get_name(dev), device_get_unit(dev), 344 "bitperfect", &i) == 0 && i != 0) 345 d->flags |= SD_F_BITPERFECT; 346 347 d->devinfo = devinfo; 348 d->reccount = 0; 349 d->playcount = 0; 350 d->pvchancount = 0; 351 d->rvchancount = 0; 352 d->pvchanrate = 0; 353 d->pvchanformat = 0; 354 d->rvchanrate = 0; 355 d->rvchanformat = 0; 356 d->p_unr = new_unrhdr(0, INT_MAX, NULL); 357 d->vp_unr = new_unrhdr(0, INT_MAX, NULL); 358 d->r_unr = new_unrhdr(0, INT_MAX, NULL); 359 d->vr_unr = new_unrhdr(0, INT_MAX, NULL); 360 361 CHN_INIT(d, channels.pcm); 362 CHN_INIT(d, channels.pcm.busy); 363 CHN_INIT(d, channels.pcm.opened); 364 CHN_INIT(d, channels.pcm.primary); 365 } 366 367 int 368 pcm_register(device_t dev, char *str) 369 { 370 struct snddev_info *d = device_get_softc(dev); 371 372 /* should only be called once */ 373 if (d->flags & SD_F_REGISTERED) 374 return (EINVAL); 375 376 if (d->playcount == 0 || d->reccount == 0) 377 d->flags |= SD_F_SIMPLEX; 378 if (d->playcount > 0) 379 d->flags |= SD_F_PVCHANS; 380 if (d->reccount > 0) 381 d->flags |= SD_F_RVCHANS; 382 383 strlcpy(d->status, str, SND_STATUSLEN); 384 385 /* Done, we're ready.. */ 386 d->flags |= SD_F_REGISTERED; 387 388 /* 389 * Create all sysctls once SD_F_REGISTERED is set else 390 * tunable sysctls won't work: 391 */ 392 sysctl_ctx_init(&d->play_sysctl_ctx); 393 d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 394 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 395 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node"); 396 sysctl_ctx_init(&d->rec_sysctl_ctx); 397 d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 398 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 399 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node"); 400 401 /* XXX: a user should be able to set this with a control tool, the 402 sysadmin then needs min+max sysctls for this */ 403 SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 404 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 405 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, 406 "allocated buffer size"); 407 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 408 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 409 "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d, 410 sizeof(d), sysctl_dev_pcm_bitperfect, "I", 411 "bit-perfect playback/recording (0=disable, 1=enable)"); 412 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 413 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 414 "mode", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, d, sizeof(d), 415 sysctl_dev_pcm_mode, "I", 416 "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than " 417 "one mode is supported)"); 418 vchan_initsys(dev); 419 if (d->flags & SD_F_EQ) 420 feeder_eq_initsys(dev); 421 422 if (snd_unit_auto < 0) 423 snd_unit_auto = (snd_unit < 0) ? 1 : 0; 424 if (snd_unit < 0 || snd_unit_auto > 1) 425 snd_unit = device_get_unit(dev); 426 else if (snd_unit_auto == 1) 427 snd_unit = pcm_best_unit(snd_unit); 428 429 sndstat_register(dev, d->status); 430 431 return (dsp_make_dev(dev)); 432 } 433 434 int 435 pcm_unregister(device_t dev) 436 { 437 struct snddev_info *d; 438 439 d = device_get_softc(dev); 440 441 if (!PCM_REGISTERED(d)) { 442 device_printf(dev, "unregister: device not configured\n"); 443 return (0); 444 } 445 446 PCM_LOCK(d); 447 PCM_WAIT(d); 448 449 d->flags &= ~SD_F_REGISTERED; 450 451 PCM_ACQUIRE(d); 452 PCM_UNLOCK(d); 453 454 pcm_killchans(d); 455 456 PCM_RELEASE_QUICK(d); 457 458 if (d->play_sysctl_tree != NULL) { 459 sysctl_ctx_free(&d->play_sysctl_ctx); 460 d->play_sysctl_tree = NULL; 461 } 462 if (d->rec_sysctl_tree != NULL) { 463 sysctl_ctx_free(&d->rec_sysctl_ctx); 464 d->rec_sysctl_tree = NULL; 465 } 466 467 sndstat_unregister(dev); 468 mixer_uninit(dev); 469 dsp_destroy_dev(dev); 470 471 cv_destroy(&d->cv); 472 mtx_destroy(&d->lock); 473 474 if (snd_unit == device_get_unit(dev)) { 475 snd_unit = pcm_best_unit(-1); 476 if (snd_unit_auto == 0) 477 snd_unit_auto = 1; 478 } 479 480 return (0); 481 } 482 483 /************************************************************************/ 484 485 /** 486 * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 487 * 488 * @param si Pointer to oss_sysinfo struct where information about the 489 * sound subsystem will be written/copied. 490 * 491 * This routine returns information about the sound system, such as the 492 * current OSS version, number of audio, MIDI, and mixer drivers, etc. 493 * Also includes a bitmask showing which of the above types of devices 494 * are open (busy). 495 * 496 * @note 497 * Calling threads must not hold any snddev_info or pcm_channel locks. 498 * 499 * @author Ryan Beasley <ryanb@FreeBSD.org> 500 */ 501 void 502 sound_oss_sysinfo(oss_sysinfo *si) 503 { 504 static char si_product[] = "FreeBSD native OSS ABI"; 505 static char si_version[] = __XSTRING(__FreeBSD_version); 506 static char si_license[] = "BSD"; 507 static int intnbits = sizeof(int) * 8; /* Better suited as macro? 508 Must pester a C guru. */ 509 510 struct snddev_info *d; 511 struct pcm_channel *c; 512 int j; 513 size_t i; 514 515 strlcpy(si->product, si_product, sizeof(si->product)); 516 strlcpy(si->version, si_version, sizeof(si->version)); 517 si->versionnum = SOUND_VERSION; 518 strlcpy(si->license, si_license, sizeof(si->license)); 519 520 /* 521 * Iterate over PCM devices and their channels, gathering up data 522 * for the numaudioengines and openedaudio fields. 523 */ 524 si->numaudioengines = 0; 525 bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 526 527 j = 0; 528 529 bus_topo_lock(); 530 for (i = 0; pcm_devclass != NULL && 531 i < devclass_get_maxunit(pcm_devclass); i++) { 532 d = devclass_get_softc(pcm_devclass, i); 533 if (!PCM_REGISTERED(d)) 534 continue; 535 536 /* XXX Need Giant magic entry ??? */ 537 538 /* See note in function's docblock */ 539 PCM_UNLOCKASSERT(d); 540 PCM_LOCK(d); 541 542 si->numaudioengines += PCM_CHANCOUNT(d); 543 544 CHN_FOREACH(c, d, channels.pcm) { 545 CHN_UNLOCKASSERT(c); 546 CHN_LOCK(c); 547 if (c->flags & CHN_F_BUSY) 548 si->openedaudio[j / intnbits] |= 549 (1 << (j % intnbits)); 550 CHN_UNLOCK(c); 551 j++; 552 } 553 554 PCM_UNLOCK(d); 555 } 556 bus_topo_unlock(); 557 558 si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 559 /** 560 * @todo Collect num{midis,timers}. 561 * 562 * Need access to sound/midi/midi.c::midistat_lock in order 563 * to safely touch midi_devices and get a head count of, well, 564 * MIDI devices. midistat_lock is a global static (i.e., local to 565 * midi.c), but midi_devices is a regular global; should the mutex 566 * be publicized, or is there another way to get this information? 567 * 568 * NB: MIDI/sequencer stuff is currently on hold. 569 */ 570 si->nummidis = 0; 571 si->numtimers = 0; 572 /* 573 * Set this to the maximum unit number so that applications will not 574 * break if they try to loop through all mixers and some of them are 575 * not available. 576 */ 577 bus_topo_lock(); 578 si->nummixers = devclass_get_maxunit(pcm_devclass); 579 si->numcards = devclass_get_maxunit(pcm_devclass); 580 si->numaudios = devclass_get_maxunit(pcm_devclass); 581 bus_topo_unlock(); 582 /* OSSv4 docs: Intended only for test apps; API doesn't 583 really have much of a concept of cards. Shouldn't be 584 used by applications. */ 585 586 /** 587 * @todo Fill in "busy devices" fields. 588 * 589 * si->openedmidi = " MIDI devices 590 */ 591 bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 592 593 /* 594 * Si->filler is a reserved array, but according to docs each 595 * element should be set to -1. 596 */ 597 for (i = 0; i < nitems(si->filler); i++) 598 si->filler[i] = -1; 599 } 600 601 int 602 sound_oss_card_info(oss_card_info *si) 603 { 604 struct snddev_info *d; 605 int i; 606 607 bus_topo_lock(); 608 for (i = 0; pcm_devclass != NULL && 609 i < devclass_get_maxunit(pcm_devclass); i++) { 610 d = devclass_get_softc(pcm_devclass, i); 611 if (i != si->card) 612 continue; 613 614 if (!PCM_REGISTERED(d)) { 615 snprintf(si->shortname, sizeof(si->shortname), 616 "pcm%d (n/a)", i); 617 strlcpy(si->longname, "Device unavailable", 618 sizeof(si->longname)); 619 si->hw_info[0] = '\0'; 620 si->intr_count = si->ack_count = 0; 621 } else { 622 PCM_UNLOCKASSERT(d); 623 PCM_LOCK(d); 624 625 strlcpy(si->shortname, device_get_nameunit(d->dev), 626 sizeof(si->shortname)); 627 strlcpy(si->longname, device_get_desc(d->dev), 628 sizeof(si->longname)); 629 strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 630 si->intr_count = si->ack_count = 0; 631 632 PCM_UNLOCK(d); 633 } 634 635 bus_topo_unlock(); 636 return (0); 637 } 638 bus_topo_unlock(); 639 640 return (ENXIO); 641 } 642 643 /************************************************************************/ 644 645 static void 646 sound_global_init(void) 647 { 648 if (snd_verbose < 0 || snd_verbose > 4) 649 snd_verbose = 1; 650 651 if (snd_unit < 0) 652 snd_unit = -1; 653 654 snd_vchans_enable = true; 655 656 if (chn_latency < CHN_LATENCY_MIN || 657 chn_latency > CHN_LATENCY_MAX) 658 chn_latency = CHN_LATENCY_DEFAULT; 659 660 if (chn_latency_profile < CHN_LATENCY_PROFILE_MIN || 661 chn_latency_profile > CHN_LATENCY_PROFILE_MAX) 662 chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT; 663 664 if (feeder_rate_min < FEEDRATE_MIN || 665 feeder_rate_max < FEEDRATE_MIN || 666 feeder_rate_min > FEEDRATE_MAX || 667 feeder_rate_max > FEEDRATE_MAX || 668 !(feeder_rate_min < feeder_rate_max)) { 669 feeder_rate_min = FEEDRATE_RATEMIN; 670 feeder_rate_max = FEEDRATE_RATEMAX; 671 } 672 673 if (feeder_rate_round < FEEDRATE_ROUNDHZ_MIN || 674 feeder_rate_round > FEEDRATE_ROUNDHZ_MAX) 675 feeder_rate_round = FEEDRATE_ROUNDHZ; 676 677 if (bootverbose) 678 printf("%s: snd_unit=%d snd_vchans_enable=%d " 679 "latency=%d " 680 "feeder_rate_min=%d feeder_rate_max=%d " 681 "feeder_rate_round=%d\n", 682 __func__, snd_unit, snd_vchans_enable, 683 chn_latency, 684 feeder_rate_min, feeder_rate_max, 685 feeder_rate_round); 686 } 687 688 static int 689 sound_modevent(module_t mod, int type, void *data) 690 { 691 int ret; 692 693 ret = 0; 694 switch (type) { 695 case MOD_LOAD: 696 pcm_devclass = devclass_create("pcm"); 697 pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 698 sound_global_init(); 699 break; 700 case MOD_UNLOAD: 701 if (pcmsg_unrhdr != NULL) { 702 delete_unrhdr(pcmsg_unrhdr); 703 pcmsg_unrhdr = NULL; 704 } 705 break; 706 case MOD_SHUTDOWN: 707 break; 708 default: 709 ret = ENOTSUP; 710 } 711 712 return ret; 713 } 714 715 DEV_MODULE(sound, sound_modevent, NULL); 716 MODULE_VERSION(sound, SOUND_MODVER); 717