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