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