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 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 static void pcm_sysinit(device_t); 61 62 /** 63 * @brief Unit number allocator for syncgroup IDs 64 */ 65 struct unrhdr *pcmsg_unrhdr = NULL; 66 67 void * 68 snd_mtxcreate(const char *desc, const char *type) 69 { 70 struct mtx *m; 71 72 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 73 mtx_init(m, desc, type, MTX_DEF); 74 return m; 75 } 76 77 void 78 snd_mtxfree(void *m) 79 { 80 struct mtx *mtx = m; 81 82 mtx_destroy(mtx); 83 free(mtx, M_DEVBUF); 84 } 85 86 void 87 snd_mtxassert(void *m) 88 { 89 #ifdef INVARIANTS 90 struct mtx *mtx = m; 91 92 mtx_assert(mtx, MA_OWNED); 93 #endif 94 } 95 96 int 97 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 98 { 99 struct snddev_info *d; 100 101 flags &= INTR_MPSAFE; 102 flags |= INTR_TYPE_AV; 103 d = device_get_softc(dev); 104 if (d != NULL && (flags & INTR_MPSAFE)) 105 d->flags |= SD_F_MPSAFE; 106 107 return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep); 108 } 109 110 int 111 pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 112 pid_t pid, char *comm) 113 { 114 struct pcm_channel *c; 115 int err, vchancount; 116 bool retry; 117 118 KASSERT(d != NULL && ch != NULL && 119 (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 120 ("%s(): invalid d=%p ch=%p direction=%d pid=%d", 121 __func__, d, ch, direction, pid)); 122 PCM_BUSYASSERT(d); 123 124 *ch = NULL; 125 vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : 126 d->rvchancount; 127 retry = false; 128 129 retry_chnalloc: 130 /* Scan for a free channel. */ 131 CHN_FOREACH(c, d, channels.pcm) { 132 CHN_LOCK(c); 133 if (c->direction != direction) { 134 CHN_UNLOCK(c); 135 continue; 136 } 137 if (!(c->flags & CHN_F_BUSY)) { 138 c->flags |= CHN_F_BUSY; 139 c->pid = pid; 140 strlcpy(c->comm, (comm != NULL) ? comm : 141 CHN_COMM_UNKNOWN, sizeof(c->comm)); 142 *ch = c; 143 144 return (0); 145 } 146 CHN_UNLOCK(c); 147 } 148 /* Maybe next time... */ 149 if (retry) 150 return (EBUSY); 151 152 /* No channel available. We also cannot create more VCHANs. */ 153 if (!(vchancount > 0 && vchancount < snd_maxautovchans)) 154 return (ENOTSUP); 155 156 /* Increase the VCHAN count and try to get the new channel. */ 157 err = vchan_setnew(d, direction, vchancount + 1); 158 if (err == 0) { 159 retry = true; 160 goto retry_chnalloc; 161 } 162 163 return (err); 164 } 165 166 static int 167 sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 168 { 169 struct snddev_info *d; 170 int error, unit; 171 172 unit = snd_unit; 173 error = sysctl_handle_int(oidp, &unit, 0, req); 174 if (error == 0 && req->newptr != NULL) { 175 d = devclass_get_softc(pcm_devclass, unit); 176 if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 177 return EINVAL; 178 snd_unit = unit; 179 snd_unit_auto = 0; 180 } 181 return (error); 182 } 183 /* XXX: do we need a way to let the user change the default unit? */ 184 SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, 185 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0, 186 sizeof(int), sysctl_hw_snd_default_unit, "I", 187 "default sound device"); 188 189 int 190 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 191 { 192 struct snddev_info *d = device_get_softc(dev); 193 struct pcm_channel *ch; 194 195 PCM_BUSYASSERT(d); 196 197 PCM_LOCK(d); 198 ch = chn_init(d, NULL, cls, dir, devinfo); 199 if (!ch) { 200 device_printf(d->dev, "chn_init(%s, %d, %p) failed\n", 201 cls->name, dir, devinfo); 202 PCM_UNLOCK(d); 203 return (ENODEV); 204 } 205 PCM_UNLOCK(d); 206 207 return (0); 208 } 209 210 static void 211 pcm_killchans(struct snddev_info *d) 212 { 213 struct pcm_channel *ch; 214 bool again; 215 216 PCM_BUSYASSERT(d); 217 KASSERT(!PCM_REGISTERED(d), ("%s(): still registered\n", __func__)); 218 219 for (;;) { 220 again = false; 221 /* Make sure all channels are stopped. */ 222 CHN_FOREACH(ch, d, channels.pcm) { 223 CHN_LOCK(ch); 224 if (ch->inprog == 0 && ch->sleeping == 0 && 225 CHN_STOPPED(ch)) { 226 CHN_UNLOCK(ch); 227 continue; 228 } 229 chn_shutdown(ch); 230 if (ch->direction == PCMDIR_PLAY) 231 chn_flush(ch); 232 else 233 chn_abort(ch); 234 CHN_UNLOCK(ch); 235 again = true; 236 } 237 /* 238 * Some channels are still active. Sleep for a bit and try 239 * again. 240 */ 241 if (again) 242 pause_sbt("pcmkillchans", mstosbt(5), 0, 0); 243 else 244 break; 245 } 246 247 /* All channels are finally dead. */ 248 while (!CHN_EMPTY(d, channels.pcm)) { 249 ch = CHN_FIRST(d, channels.pcm); 250 chn_kill(ch); 251 } 252 253 if (d->p_unr != NULL) 254 delete_unrhdr(d->p_unr); 255 if (d->vp_unr != NULL) 256 delete_unrhdr(d->vp_unr); 257 if (d->r_unr != NULL) 258 delete_unrhdr(d->r_unr); 259 if (d->vr_unr != NULL) 260 delete_unrhdr(d->vr_unr); 261 } 262 263 static int 264 pcm_best_unit(int old) 265 { 266 struct snddev_info *d; 267 int i, best, bestprio, prio; 268 269 best = -1; 270 bestprio = -100; 271 for (i = 0; pcm_devclass != NULL && 272 i < devclass_get_maxunit(pcm_devclass); i++) { 273 d = devclass_get_softc(pcm_devclass, i); 274 if (!PCM_REGISTERED(d)) 275 continue; 276 prio = 0; 277 if (d->playcount == 0) 278 prio -= 10; 279 if (d->reccount == 0) 280 prio -= 2; 281 if (prio > bestprio || (prio == bestprio && i == old)) { 282 best = i; 283 bestprio = prio; 284 } 285 } 286 return (best); 287 } 288 289 uint32_t 290 pcm_getflags(device_t dev) 291 { 292 struct snddev_info *d = device_get_softc(dev); 293 294 return d->flags; 295 } 296 297 void 298 pcm_setflags(device_t dev, uint32_t val) 299 { 300 struct snddev_info *d = device_get_softc(dev); 301 302 d->flags = val; 303 } 304 305 void * 306 pcm_getdevinfo(device_t dev) 307 { 308 struct snddev_info *d = device_get_softc(dev); 309 310 return d->devinfo; 311 } 312 313 unsigned int 314 pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 315 { 316 struct snddev_info *d = device_get_softc(dev); 317 int sz, x; 318 319 sz = 0; 320 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 321 x = sz; 322 RANGE(sz, minbufsz, maxbufsz); 323 if (x != sz) 324 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 325 x = minbufsz; 326 while (x < sz) 327 x <<= 1; 328 if (x > sz) 329 x >>= 1; 330 if (x != sz) { 331 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 332 sz = x; 333 } 334 } else { 335 sz = deflt; 336 } 337 338 d->bufsz = sz; 339 340 return sz; 341 } 342 343 static int 344 sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) 345 { 346 struct snddev_info *d; 347 int err, val; 348 349 d = oidp->oid_arg1; 350 if (!PCM_REGISTERED(d)) 351 return (ENODEV); 352 353 PCM_LOCK(d); 354 PCM_WAIT(d); 355 val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; 356 PCM_ACQUIRE(d); 357 PCM_UNLOCK(d); 358 359 err = sysctl_handle_int(oidp, &val, 0, req); 360 361 if (err == 0 && req->newptr != NULL) { 362 if (!(val == 0 || val == 1)) { 363 PCM_RELEASE_QUICK(d); 364 return (EINVAL); 365 } 366 367 PCM_LOCK(d); 368 369 d->flags &= ~SD_F_BITPERFECT; 370 d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; 371 372 PCM_RELEASE(d); 373 PCM_UNLOCK(d); 374 } else 375 PCM_RELEASE_QUICK(d); 376 377 return (err); 378 } 379 380 static u_int8_t 381 pcm_mode_init(struct snddev_info *d) 382 { 383 u_int8_t mode = 0; 384 385 if (d->playcount > 0) 386 mode |= PCM_MODE_PLAY; 387 if (d->reccount > 0) 388 mode |= PCM_MODE_REC; 389 if (d->mixer_dev != NULL) 390 mode |= PCM_MODE_MIXER; 391 392 return (mode); 393 } 394 395 static void 396 pcm_sysinit(device_t dev) 397 { 398 struct snddev_info *d = device_get_softc(dev); 399 u_int8_t mode; 400 401 mode = pcm_mode_init(d); 402 403 sysctl_ctx_init(&d->play_sysctl_ctx); 404 d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 405 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 406 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node"); 407 sysctl_ctx_init(&d->rec_sysctl_ctx); 408 d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 409 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 410 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node"); 411 412 /* XXX: a user should be able to set this with a control tool, the 413 sysadmin then needs min+max sysctls for this */ 414 SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 415 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 416 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 417 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 418 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 419 "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d, 420 sizeof(d), sysctl_dev_pcm_bitperfect, "I", 421 "bit-perfect playback/recording (0=disable, 1=enable)"); 422 SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 423 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 424 OID_AUTO, "mode", CTLFLAG_RD, NULL, mode, 425 "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than " 426 "one mode is supported)"); 427 if (d->flags & SD_F_AUTOVCHAN) 428 vchan_initsys(dev); 429 if (d->flags & SD_F_EQ) 430 feeder_eq_initsys(dev); 431 } 432 433 /* 434 * Basic initialization so that drivers can use pcm_addchan() before 435 * pcm_register(). 436 */ 437 void 438 pcm_init(device_t dev, void *devinfo) 439 { 440 struct snddev_info *d; 441 int i; 442 443 d = device_get_softc(dev); 444 d->dev = dev; 445 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 446 cv_init(&d->cv, device_get_nameunit(dev)); 447 PCM_ACQUIRE_QUICK(d); 448 449 i = 0; 450 if (resource_int_value(device_get_name(dev), device_get_unit(dev), 451 "vpc", &i) != 0 || i != 0) 452 d->flags |= SD_F_VPC; 453 454 if (resource_int_value(device_get_name(dev), device_get_unit(dev), 455 "bitperfect", &i) == 0 && i != 0) 456 d->flags |= SD_F_BITPERFECT; 457 458 d->devinfo = devinfo; 459 d->reccount = 0; 460 d->playcount = 0; 461 d->pvchancount = 0; 462 d->rvchancount = 0; 463 d->pvchanrate = 0; 464 d->pvchanformat = 0; 465 d->rvchanrate = 0; 466 d->rvchanformat = 0; 467 d->p_unr = new_unrhdr(0, INT_MAX, NULL); 468 d->vp_unr = new_unrhdr(0, INT_MAX, NULL); 469 d->r_unr = new_unrhdr(0, INT_MAX, NULL); 470 d->vr_unr = new_unrhdr(0, INT_MAX, NULL); 471 472 CHN_INIT(d, channels.pcm); 473 CHN_INIT(d, channels.pcm.busy); 474 CHN_INIT(d, channels.pcm.opened); 475 } 476 477 int 478 pcm_register(device_t dev, char *str) 479 { 480 struct snddev_info *d = device_get_softc(dev); 481 482 /* should only be called once */ 483 if (d->flags & SD_F_REGISTERED) 484 return (EINVAL); 485 486 PCM_BUSYASSERT(d); 487 488 if (d->playcount == 0 || d->reccount == 0) 489 d->flags |= SD_F_SIMPLEX; 490 491 if (d->playcount > 0 || d->reccount > 0) 492 d->flags |= SD_F_AUTOVCHAN; 493 494 vchan_setmaxauto(d, snd_maxautovchans); 495 496 strlcpy(d->status, str, SND_STATUSLEN); 497 sndstat_register(dev, d->status); 498 499 PCM_LOCK(d); 500 501 /* Done, we're ready.. */ 502 d->flags |= SD_F_REGISTERED; 503 504 PCM_RELEASE(d); 505 506 PCM_UNLOCK(d); 507 508 /* 509 * Create all sysctls once SD_F_REGISTERED is set else 510 * tunable sysctls won't work: 511 */ 512 pcm_sysinit(dev); 513 514 if (snd_unit_auto < 0) 515 snd_unit_auto = (snd_unit < 0) ? 1 : 0; 516 if (snd_unit < 0 || snd_unit_auto > 1) 517 snd_unit = device_get_unit(dev); 518 else if (snd_unit_auto == 1) 519 snd_unit = pcm_best_unit(snd_unit); 520 521 return (dsp_make_dev(dev)); 522 } 523 524 int 525 pcm_unregister(device_t dev) 526 { 527 struct snddev_info *d; 528 529 d = device_get_softc(dev); 530 531 if (!PCM_ALIVE(d)) { 532 device_printf(dev, "unregister: device not configured\n"); 533 return (0); 534 } 535 536 PCM_LOCK(d); 537 PCM_WAIT(d); 538 539 d->flags &= ~SD_F_REGISTERED; 540 541 PCM_ACQUIRE(d); 542 PCM_UNLOCK(d); 543 544 pcm_killchans(d); 545 546 PCM_RELEASE_QUICK(d); 547 548 if (d->play_sysctl_tree != NULL) { 549 sysctl_ctx_free(&d->play_sysctl_ctx); 550 d->play_sysctl_tree = NULL; 551 } 552 if (d->rec_sysctl_tree != NULL) { 553 sysctl_ctx_free(&d->rec_sysctl_ctx); 554 d->rec_sysctl_tree = NULL; 555 } 556 557 sndstat_unregister(dev); 558 mixer_uninit(dev); 559 dsp_destroy_dev(dev); 560 561 cv_destroy(&d->cv); 562 snd_mtxfree(d->lock); 563 564 if (snd_unit == device_get_unit(dev)) { 565 snd_unit = pcm_best_unit(-1); 566 if (snd_unit_auto == 0) 567 snd_unit_auto = 1; 568 } 569 570 return (0); 571 } 572 573 /************************************************************************/ 574 575 /** 576 * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 577 * 578 * @param si Pointer to oss_sysinfo struct where information about the 579 * sound subsystem will be written/copied. 580 * 581 * This routine returns information about the sound system, such as the 582 * current OSS version, number of audio, MIDI, and mixer drivers, etc. 583 * Also includes a bitmask showing which of the above types of devices 584 * are open (busy). 585 * 586 * @note 587 * Calling threads must not hold any snddev_info or pcm_channel locks. 588 * 589 * @author Ryan Beasley <ryanb@FreeBSD.org> 590 */ 591 void 592 sound_oss_sysinfo(oss_sysinfo *si) 593 { 594 static char si_product[] = "FreeBSD native OSS ABI"; 595 static char si_version[] = __XSTRING(__FreeBSD_version); 596 static char si_license[] = "BSD"; 597 static int intnbits = sizeof(int) * 8; /* Better suited as macro? 598 Must pester a C guru. */ 599 600 struct snddev_info *d; 601 struct pcm_channel *c; 602 int j; 603 size_t i; 604 605 strlcpy(si->product, si_product, sizeof(si->product)); 606 strlcpy(si->version, si_version, sizeof(si->version)); 607 si->versionnum = SOUND_VERSION; 608 strlcpy(si->license, si_license, sizeof(si->license)); 609 610 /* 611 * Iterate over PCM devices and their channels, gathering up data 612 * for the numaudioengines and openedaudio fields. 613 */ 614 si->numaudioengines = 0; 615 bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 616 617 j = 0; 618 619 for (i = 0; pcm_devclass != NULL && 620 i < devclass_get_maxunit(pcm_devclass); i++) { 621 d = devclass_get_softc(pcm_devclass, i); 622 if (!PCM_REGISTERED(d)) 623 continue; 624 625 /* XXX Need Giant magic entry ??? */ 626 627 /* See note in function's docblock */ 628 PCM_UNLOCKASSERT(d); 629 PCM_LOCK(d); 630 631 si->numaudioengines += PCM_CHANCOUNT(d); 632 633 CHN_FOREACH(c, d, channels.pcm) { 634 CHN_UNLOCKASSERT(c); 635 CHN_LOCK(c); 636 if (c->flags & CHN_F_BUSY) 637 si->openedaudio[j / intnbits] |= 638 (1 << (j % intnbits)); 639 CHN_UNLOCK(c); 640 j++; 641 } 642 643 PCM_UNLOCK(d); 644 } 645 646 si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 647 /** 648 * @todo Collect num{midis,timers}. 649 * 650 * Need access to sound/midi/midi.c::midistat_lock in order 651 * to safely touch midi_devices and get a head count of, well, 652 * MIDI devices. midistat_lock is a global static (i.e., local to 653 * midi.c), but midi_devices is a regular global; should the mutex 654 * be publicized, or is there another way to get this information? 655 * 656 * NB: MIDI/sequencer stuff is currently on hold. 657 */ 658 si->nummidis = 0; 659 si->numtimers = 0; 660 /* 661 * Set this to the maximum unit number so that applications will not 662 * break if they try to loop through all mixers and some of them are 663 * not available. 664 */ 665 si->nummixers = devclass_get_maxunit(pcm_devclass); 666 si->numcards = devclass_get_maxunit(pcm_devclass); 667 si->numaudios = devclass_get_maxunit(pcm_devclass); 668 /* OSSv4 docs: Intended only for test apps; API doesn't 669 really have much of a concept of cards. Shouldn't be 670 used by applications. */ 671 672 /** 673 * @todo Fill in "busy devices" fields. 674 * 675 * si->openedmidi = " MIDI devices 676 */ 677 bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 678 679 /* 680 * Si->filler is a reserved array, but according to docs each 681 * element should be set to -1. 682 */ 683 for (i = 0; i < nitems(si->filler); i++) 684 si->filler[i] = -1; 685 } 686 687 int 688 sound_oss_card_info(oss_card_info *si) 689 { 690 struct snddev_info *d; 691 int i; 692 693 for (i = 0; pcm_devclass != NULL && 694 i < devclass_get_maxunit(pcm_devclass); i++) { 695 d = devclass_get_softc(pcm_devclass, i); 696 if (i != si->card) 697 continue; 698 699 if (!PCM_REGISTERED(d)) { 700 snprintf(si->shortname, sizeof(si->shortname), 701 "pcm%d (n/a)", i); 702 strlcpy(si->longname, "Device unavailable", 703 sizeof(si->longname)); 704 si->hw_info[0] = '\0'; 705 si->intr_count = si->ack_count = 0; 706 } else { 707 PCM_UNLOCKASSERT(d); 708 PCM_LOCK(d); 709 710 strlcpy(si->shortname, device_get_nameunit(d->dev), 711 sizeof(si->shortname)); 712 strlcpy(si->longname, device_get_desc(d->dev), 713 sizeof(si->longname)); 714 strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 715 si->intr_count = si->ack_count = 0; 716 717 PCM_UNLOCK(d); 718 } 719 720 return (0); 721 } 722 return (ENXIO); 723 } 724 725 /************************************************************************/ 726 727 static void 728 sound_global_init(void) 729 { 730 if (snd_verbose < 0 || snd_verbose > 4) 731 snd_verbose = 1; 732 733 if (snd_unit < 0) 734 snd_unit = -1; 735 736 if (snd_maxautovchans < 0 || 737 snd_maxautovchans > SND_MAXVCHANS) 738 snd_maxautovchans = 0; 739 740 if (chn_latency < CHN_LATENCY_MIN || 741 chn_latency > CHN_LATENCY_MAX) 742 chn_latency = CHN_LATENCY_DEFAULT; 743 744 if (chn_latency_profile < CHN_LATENCY_PROFILE_MIN || 745 chn_latency_profile > CHN_LATENCY_PROFILE_MAX) 746 chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT; 747 748 if (feeder_rate_min < FEEDRATE_MIN || 749 feeder_rate_max < FEEDRATE_MIN || 750 feeder_rate_min > FEEDRATE_MAX || 751 feeder_rate_max > FEEDRATE_MAX || 752 !(feeder_rate_min < feeder_rate_max)) { 753 feeder_rate_min = FEEDRATE_RATEMIN; 754 feeder_rate_max = FEEDRATE_RATEMAX; 755 } 756 757 if (feeder_rate_round < FEEDRATE_ROUNDHZ_MIN || 758 feeder_rate_round > FEEDRATE_ROUNDHZ_MAX) 759 feeder_rate_round = FEEDRATE_ROUNDHZ; 760 761 if (bootverbose) 762 printf("%s: snd_unit=%d snd_maxautovchans=%d " 763 "latency=%d " 764 "feeder_rate_min=%d feeder_rate_max=%d " 765 "feeder_rate_round=%d\n", 766 __func__, snd_unit, snd_maxautovchans, 767 chn_latency, 768 feeder_rate_min, feeder_rate_max, 769 feeder_rate_round); 770 } 771 772 static int 773 sound_modevent(module_t mod, int type, void *data) 774 { 775 int ret; 776 777 ret = 0; 778 switch (type) { 779 case MOD_LOAD: 780 pcm_devclass = devclass_create("pcm"); 781 pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 782 sound_global_init(); 783 break; 784 case MOD_UNLOAD: 785 if (pcmsg_unrhdr != NULL) { 786 delete_unrhdr(pcmsg_unrhdr); 787 pcmsg_unrhdr = NULL; 788 } 789 break; 790 case MOD_SHUTDOWN: 791 break; 792 default: 793 ret = ENOTSUP; 794 } 795 796 return ret; 797 } 798 799 DEV_MODULE(sound, sound_modevent, NULL); 800 MODULE_VERSION(sound, SOUND_MODVER); 801