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 found; 215 216 PCM_BUSYASSERT(d); 217 do { 218 found = false; 219 CHN_FOREACH(ch, d, channels.pcm) { 220 CHN_LOCK(ch); 221 /* 222 * Make sure no channel has went to sleep in the 223 * meantime. 224 */ 225 chn_shutdown(ch); 226 /* 227 * We have to give a thread sleeping in chn_sleep() a 228 * chance to observe that the channel is dead. 229 */ 230 if ((ch->flags & CHN_F_SLEEPING) == 0) { 231 found = true; 232 CHN_UNLOCK(ch); 233 break; 234 } 235 CHN_UNLOCK(ch); 236 } 237 238 /* 239 * All channels are still sleeping. Sleep for a bit and try 240 * again to see if any of them is awake now. 241 */ 242 if (!found) { 243 pause_sbt("pcmkillchans", SBT_1MS * 5, 0, 0); 244 continue; 245 } 246 chn_kill(ch); 247 } while (!CHN_EMPTY(d, channels.pcm)); 248 } 249 250 static int 251 pcm_best_unit(int old) 252 { 253 struct snddev_info *d; 254 int i, best, bestprio, prio; 255 256 best = -1; 257 bestprio = -100; 258 for (i = 0; pcm_devclass != NULL && 259 i < devclass_get_maxunit(pcm_devclass); i++) { 260 d = devclass_get_softc(pcm_devclass, i); 261 if (!PCM_REGISTERED(d)) 262 continue; 263 prio = 0; 264 if (d->playcount == 0) 265 prio -= 10; 266 if (d->reccount == 0) 267 prio -= 2; 268 if (prio > bestprio || (prio == bestprio && i == old)) { 269 best = i; 270 bestprio = prio; 271 } 272 } 273 return (best); 274 } 275 276 uint32_t 277 pcm_getflags(device_t dev) 278 { 279 struct snddev_info *d = device_get_softc(dev); 280 281 return d->flags; 282 } 283 284 void 285 pcm_setflags(device_t dev, uint32_t val) 286 { 287 struct snddev_info *d = device_get_softc(dev); 288 289 d->flags = val; 290 } 291 292 void * 293 pcm_getdevinfo(device_t dev) 294 { 295 struct snddev_info *d = device_get_softc(dev); 296 297 return d->devinfo; 298 } 299 300 unsigned int 301 pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 302 { 303 struct snddev_info *d = device_get_softc(dev); 304 int sz, x; 305 306 sz = 0; 307 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 308 x = sz; 309 RANGE(sz, minbufsz, maxbufsz); 310 if (x != sz) 311 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 312 x = minbufsz; 313 while (x < sz) 314 x <<= 1; 315 if (x > sz) 316 x >>= 1; 317 if (x != sz) { 318 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 319 sz = x; 320 } 321 } else { 322 sz = deflt; 323 } 324 325 d->bufsz = sz; 326 327 return sz; 328 } 329 330 static int 331 sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) 332 { 333 struct snddev_info *d; 334 int err, val; 335 336 d = oidp->oid_arg1; 337 if (!PCM_REGISTERED(d)) 338 return (ENODEV); 339 340 PCM_LOCK(d); 341 PCM_WAIT(d); 342 val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; 343 PCM_ACQUIRE(d); 344 PCM_UNLOCK(d); 345 346 err = sysctl_handle_int(oidp, &val, 0, req); 347 348 if (err == 0 && req->newptr != NULL) { 349 if (!(val == 0 || val == 1)) { 350 PCM_RELEASE_QUICK(d); 351 return (EINVAL); 352 } 353 354 PCM_LOCK(d); 355 356 d->flags &= ~SD_F_BITPERFECT; 357 d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; 358 359 PCM_RELEASE(d); 360 PCM_UNLOCK(d); 361 } else 362 PCM_RELEASE_QUICK(d); 363 364 return (err); 365 } 366 367 static u_int8_t 368 pcm_mode_init(struct snddev_info *d) 369 { 370 u_int8_t mode = 0; 371 372 if (d->playcount > 0) 373 mode |= PCM_MODE_PLAY; 374 if (d->reccount > 0) 375 mode |= PCM_MODE_REC; 376 if (d->mixer_dev != NULL) 377 mode |= PCM_MODE_MIXER; 378 379 return (mode); 380 } 381 382 static void 383 pcm_sysinit(device_t dev) 384 { 385 struct snddev_info *d = device_get_softc(dev); 386 u_int8_t mode; 387 388 mode = pcm_mode_init(d); 389 390 sysctl_ctx_init(&d->play_sysctl_ctx); 391 d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 392 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 393 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node"); 394 sysctl_ctx_init(&d->rec_sysctl_ctx); 395 d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 396 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 397 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node"); 398 399 /* XXX: a user should be able to set this with a control tool, the 400 sysadmin then needs min+max sysctls for this */ 401 SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 402 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 403 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 404 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 405 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 406 "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d, 407 sizeof(d), sysctl_dev_pcm_bitperfect, "I", 408 "bit-perfect playback/recording (0=disable, 1=enable)"); 409 SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 410 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 411 OID_AUTO, "mode", CTLFLAG_RD, NULL, mode, 412 "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than " 413 "one mode is supported)"); 414 if (d->flags & SD_F_AUTOVCHAN) 415 vchan_initsys(dev); 416 if (d->flags & SD_F_EQ) 417 feeder_eq_initsys(dev); 418 } 419 420 /* 421 * Basic initialization so that drivers can use pcm_addchan() before 422 * pcm_register(). 423 */ 424 void 425 pcm_init(device_t dev, void *devinfo) 426 { 427 struct snddev_info *d; 428 int i; 429 430 d = device_get_softc(dev); 431 d->dev = dev; 432 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 433 cv_init(&d->cv, device_get_nameunit(dev)); 434 PCM_ACQUIRE_QUICK(d); 435 436 i = 0; 437 if (resource_int_value(device_get_name(dev), device_get_unit(dev), 438 "vpc", &i) != 0 || i != 0) 439 d->flags |= SD_F_VPC; 440 441 if (resource_int_value(device_get_name(dev), device_get_unit(dev), 442 "bitperfect", &i) == 0 && i != 0) 443 d->flags |= SD_F_BITPERFECT; 444 445 d->devinfo = devinfo; 446 d->reccount = 0; 447 d->playcount = 0; 448 d->pvchancount = 0; 449 d->rvchancount = 0; 450 d->pvchanrate = 0; 451 d->pvchanformat = 0; 452 d->rvchanrate = 0; 453 d->rvchanformat = 0; 454 d->p_unr = new_unrhdr(0, INT_MAX, NULL); 455 d->vp_unr = new_unrhdr(0, INT_MAX, NULL); 456 d->r_unr = new_unrhdr(0, INT_MAX, NULL); 457 d->vr_unr = new_unrhdr(0, INT_MAX, NULL); 458 459 CHN_INIT(d, channels.pcm); 460 CHN_INIT(d, channels.pcm.busy); 461 CHN_INIT(d, channels.pcm.opened); 462 } 463 464 int 465 pcm_register(device_t dev, char *str) 466 { 467 struct snddev_info *d = device_get_softc(dev); 468 469 /* should only be called once */ 470 if (d->flags & SD_F_REGISTERED) 471 return (EINVAL); 472 473 PCM_BUSYASSERT(d); 474 475 if (d->playcount == 0 || d->reccount == 0) 476 d->flags |= SD_F_SIMPLEX; 477 478 if (d->playcount > 0 || d->reccount > 0) 479 d->flags |= SD_F_AUTOVCHAN; 480 481 vchan_setmaxauto(d, snd_maxautovchans); 482 483 strlcpy(d->status, str, SND_STATUSLEN); 484 sndstat_register(dev, d->status); 485 486 PCM_LOCK(d); 487 488 /* Done, we're ready.. */ 489 d->flags |= SD_F_REGISTERED; 490 491 PCM_RELEASE(d); 492 493 PCM_UNLOCK(d); 494 495 /* 496 * Create all sysctls once SD_F_REGISTERED is set else 497 * tunable sysctls won't work: 498 */ 499 pcm_sysinit(dev); 500 501 if (snd_unit_auto < 0) 502 snd_unit_auto = (snd_unit < 0) ? 1 : 0; 503 if (snd_unit < 0 || snd_unit_auto > 1) 504 snd_unit = device_get_unit(dev); 505 else if (snd_unit_auto == 1) 506 snd_unit = pcm_best_unit(snd_unit); 507 508 return (dsp_make_dev(dev)); 509 } 510 511 int 512 pcm_unregister(device_t dev) 513 { 514 struct snddev_info *d; 515 struct pcm_channel *ch; 516 517 d = device_get_softc(dev); 518 519 if (!PCM_ALIVE(d)) { 520 device_printf(dev, "unregister: device not configured\n"); 521 return (0); 522 } 523 524 PCM_LOCK(d); 525 PCM_WAIT(d); 526 527 d->flags |= SD_F_DETACHING; 528 529 PCM_ACQUIRE(d); 530 PCM_UNLOCK(d); 531 532 CHN_FOREACH(ch, d, channels.pcm) { 533 CHN_LOCK(ch); 534 /* 535 * Do not wait for the timeout in chn_read()/chn_write(). Wake 536 * up the sleeping thread and kill the channel. 537 */ 538 chn_shutdown(ch); 539 chn_abort(ch); 540 CHN_UNLOCK(ch); 541 } 542 543 /* remove /dev/sndstat entry first */ 544 sndstat_unregister(dev); 545 546 PCM_LOCK(d); 547 d->flags |= SD_F_DYING; 548 d->flags &= ~SD_F_REGISTERED; 549 PCM_UNLOCK(d); 550 551 if (d->play_sysctl_tree != NULL) { 552 sysctl_ctx_free(&d->play_sysctl_ctx); 553 d->play_sysctl_tree = NULL; 554 } 555 if (d->rec_sysctl_tree != NULL) { 556 sysctl_ctx_free(&d->rec_sysctl_ctx); 557 d->rec_sysctl_tree = NULL; 558 } 559 560 dsp_destroy_dev(dev); 561 (void)mixer_uninit(dev); 562 563 pcm_killchans(d); 564 565 PCM_LOCK(d); 566 PCM_RELEASE(d); 567 cv_destroy(&d->cv); 568 PCM_UNLOCK(d); 569 snd_mtxfree(d->lock); 570 if (d->p_unr != NULL) 571 delete_unrhdr(d->p_unr); 572 if (d->vp_unr != NULL) 573 delete_unrhdr(d->vp_unr); 574 if (d->r_unr != NULL) 575 delete_unrhdr(d->r_unr); 576 if (d->vr_unr != NULL) 577 delete_unrhdr(d->vr_unr); 578 579 if (snd_unit == device_get_unit(dev)) { 580 snd_unit = pcm_best_unit(-1); 581 if (snd_unit_auto == 0) 582 snd_unit_auto = 1; 583 } 584 585 return (0); 586 } 587 588 /************************************************************************/ 589 590 /** 591 * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 592 * 593 * @param si Pointer to oss_sysinfo struct where information about the 594 * sound subsystem will be written/copied. 595 * 596 * This routine returns information about the sound system, such as the 597 * current OSS version, number of audio, MIDI, and mixer drivers, etc. 598 * Also includes a bitmask showing which of the above types of devices 599 * are open (busy). 600 * 601 * @note 602 * Calling threads must not hold any snddev_info or pcm_channel locks. 603 * 604 * @author Ryan Beasley <ryanb@FreeBSD.org> 605 */ 606 void 607 sound_oss_sysinfo(oss_sysinfo *si) 608 { 609 static char si_product[] = "FreeBSD native OSS ABI"; 610 static char si_version[] = __XSTRING(__FreeBSD_version); 611 static char si_license[] = "BSD"; 612 static int intnbits = sizeof(int) * 8; /* Better suited as macro? 613 Must pester a C guru. */ 614 615 struct snddev_info *d; 616 struct pcm_channel *c; 617 int j; 618 size_t i; 619 620 strlcpy(si->product, si_product, sizeof(si->product)); 621 strlcpy(si->version, si_version, sizeof(si->version)); 622 si->versionnum = SOUND_VERSION; 623 strlcpy(si->license, si_license, sizeof(si->license)); 624 625 /* 626 * Iterate over PCM devices and their channels, gathering up data 627 * for the numaudioengines and openedaudio fields. 628 */ 629 si->numaudioengines = 0; 630 bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 631 632 j = 0; 633 634 for (i = 0; pcm_devclass != NULL && 635 i < devclass_get_maxunit(pcm_devclass); i++) { 636 d = devclass_get_softc(pcm_devclass, i); 637 if (!PCM_REGISTERED(d)) 638 continue; 639 640 /* XXX Need Giant magic entry ??? */ 641 642 /* See note in function's docblock */ 643 PCM_UNLOCKASSERT(d); 644 PCM_LOCK(d); 645 646 si->numaudioengines += PCM_CHANCOUNT(d); 647 648 CHN_FOREACH(c, d, channels.pcm) { 649 CHN_UNLOCKASSERT(c); 650 CHN_LOCK(c); 651 if (c->flags & CHN_F_BUSY) 652 si->openedaudio[j / intnbits] |= 653 (1 << (j % intnbits)); 654 CHN_UNLOCK(c); 655 j++; 656 } 657 658 PCM_UNLOCK(d); 659 } 660 661 si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 662 /** 663 * @todo Collect num{midis,timers}. 664 * 665 * Need access to sound/midi/midi.c::midistat_lock in order 666 * to safely touch midi_devices and get a head count of, well, 667 * MIDI devices. midistat_lock is a global static (i.e., local to 668 * midi.c), but midi_devices is a regular global; should the mutex 669 * be publicized, or is there another way to get this information? 670 * 671 * NB: MIDI/sequencer stuff is currently on hold. 672 */ 673 si->nummidis = 0; 674 si->numtimers = 0; 675 /* 676 * Set this to the maximum unit number so that applications will not 677 * break if they try to loop through all mixers and some of them are 678 * not available. 679 */ 680 si->nummixers = devclass_get_maxunit(pcm_devclass); 681 si->numcards = devclass_get_maxunit(pcm_devclass); 682 si->numaudios = devclass_get_maxunit(pcm_devclass); 683 /* OSSv4 docs: Intended only for test apps; API doesn't 684 really have much of a concept of cards. Shouldn't be 685 used by applications. */ 686 687 /** 688 * @todo Fill in "busy devices" fields. 689 * 690 * si->openedmidi = " MIDI devices 691 */ 692 bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 693 694 /* 695 * Si->filler is a reserved array, but according to docs each 696 * element should be set to -1. 697 */ 698 for (i = 0; i < nitems(si->filler); i++) 699 si->filler[i] = -1; 700 } 701 702 int 703 sound_oss_card_info(oss_card_info *si) 704 { 705 struct snddev_info *d; 706 int i; 707 708 for (i = 0; pcm_devclass != NULL && 709 i < devclass_get_maxunit(pcm_devclass); i++) { 710 d = devclass_get_softc(pcm_devclass, i); 711 if (i != si->card) 712 continue; 713 714 if (!PCM_REGISTERED(d)) { 715 snprintf(si->shortname, sizeof(si->shortname), 716 "pcm%d (n/a)", i); 717 strlcpy(si->longname, "Device unavailable", 718 sizeof(si->longname)); 719 si->hw_info[0] = '\0'; 720 si->intr_count = si->ack_count = 0; 721 } else { 722 PCM_UNLOCKASSERT(d); 723 PCM_LOCK(d); 724 725 strlcpy(si->shortname, device_get_nameunit(d->dev), 726 sizeof(si->shortname)); 727 strlcpy(si->longname, device_get_desc(d->dev), 728 sizeof(si->longname)); 729 strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 730 si->intr_count = si->ack_count = 0; 731 732 PCM_UNLOCK(d); 733 } 734 735 return (0); 736 } 737 return (ENXIO); 738 } 739 740 /************************************************************************/ 741 742 static void 743 sound_global_init(void) 744 { 745 if (snd_verbose < 0 || snd_verbose > 4) 746 snd_verbose = 1; 747 748 if (snd_unit < 0) 749 snd_unit = -1; 750 751 if (snd_maxautovchans < 0 || 752 snd_maxautovchans > SND_MAXVCHANS) 753 snd_maxautovchans = 0; 754 755 if (chn_latency < CHN_LATENCY_MIN || 756 chn_latency > CHN_LATENCY_MAX) 757 chn_latency = CHN_LATENCY_DEFAULT; 758 759 if (chn_latency_profile < CHN_LATENCY_PROFILE_MIN || 760 chn_latency_profile > CHN_LATENCY_PROFILE_MAX) 761 chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT; 762 763 if (feeder_rate_min < FEEDRATE_MIN || 764 feeder_rate_max < FEEDRATE_MIN || 765 feeder_rate_min > FEEDRATE_MAX || 766 feeder_rate_max > FEEDRATE_MAX || 767 !(feeder_rate_min < feeder_rate_max)) { 768 feeder_rate_min = FEEDRATE_RATEMIN; 769 feeder_rate_max = FEEDRATE_RATEMAX; 770 } 771 772 if (feeder_rate_round < FEEDRATE_ROUNDHZ_MIN || 773 feeder_rate_round > FEEDRATE_ROUNDHZ_MAX) 774 feeder_rate_round = FEEDRATE_ROUNDHZ; 775 776 if (bootverbose) 777 printf("%s: snd_unit=%d snd_maxautovchans=%d " 778 "latency=%d " 779 "feeder_rate_min=%d feeder_rate_max=%d " 780 "feeder_rate_round=%d\n", 781 __func__, snd_unit, snd_maxautovchans, 782 chn_latency, 783 feeder_rate_min, feeder_rate_max, 784 feeder_rate_round); 785 } 786 787 static int 788 sound_modevent(module_t mod, int type, void *data) 789 { 790 int ret; 791 792 ret = 0; 793 switch (type) { 794 case MOD_LOAD: 795 pcm_devclass = devclass_create("pcm"); 796 pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 797 sound_global_init(); 798 break; 799 case MOD_UNLOAD: 800 if (pcmsg_unrhdr != NULL) { 801 delete_unrhdr(pcmsg_unrhdr); 802 pcmsg_unrhdr = NULL; 803 } 804 break; 805 case MOD_SHUTDOWN: 806 break; 807 default: 808 ret = ENOTSUP; 809 } 810 811 return ret; 812 } 813 814 DEV_MODULE(sound, sound_modevent, NULL); 815 MODULE_VERSION(sound, SOUND_MODVER); 816