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 #if 0 562 /* 563 * d->flags should be cleared by the allocator of the softc. 564 * We cannot clear this field here because several devices set 565 * this flag before calling pcm_register(). 566 */ 567 d->flags = 0; 568 #endif 569 i = 0; 570 if (resource_int_value(device_get_name(dev), device_get_unit(dev), 571 "vpc", &i) != 0 || i != 0) 572 d->flags |= SD_F_VPC; 573 574 if (resource_int_value(device_get_name(dev), device_get_unit(dev), 575 "bitperfect", &i) == 0 && i != 0) 576 d->flags |= SD_F_BITPERFECT; 577 578 d->devinfo = devinfo; 579 d->reccount = 0; 580 d->playcount = 0; 581 d->pvchancount = 0; 582 d->rvchancount = 0; 583 d->pvchanrate = 0; 584 d->pvchanformat = 0; 585 d->rvchanrate = 0; 586 d->rvchanformat = 0; 587 588 CHN_INIT(d, channels.pcm); 589 CHN_INIT(d, channels.pcm.busy); 590 CHN_INIT(d, channels.pcm.opened); 591 592 /* XXX This is incorrect, but lets play along for now. */ 593 if ((numplay == 0 || numrec == 0) && numplay != numrec) 594 d->flags |= SD_F_SIMPLEX; 595 596 sysctl_ctx_init(&d->play_sysctl_ctx); 597 d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 598 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 599 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node"); 600 sysctl_ctx_init(&d->rec_sysctl_ctx); 601 d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 602 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 603 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node"); 604 605 if (numplay > 0 || numrec > 0) 606 d->flags |= SD_F_AUTOVCHAN; 607 608 sndstat_register(dev, d->status); 609 610 return (dsp_make_dev(dev)); 611 } 612 613 int 614 pcm_unregister(device_t dev) 615 { 616 struct snddev_info *d; 617 struct pcm_channel *ch; 618 619 d = device_get_softc(dev); 620 621 if (!PCM_ALIVE(d)) { 622 device_printf(dev, "unregister: device not configured\n"); 623 return (0); 624 } 625 626 PCM_LOCK(d); 627 PCM_WAIT(d); 628 629 d->flags |= SD_F_DETACHING; 630 631 PCM_ACQUIRE(d); 632 PCM_UNLOCK(d); 633 634 CHN_FOREACH(ch, d, channels.pcm) { 635 CHN_LOCK(ch); 636 /* 637 * Do not wait for the timeout in chn_read()/chn_write(). Wake 638 * up the sleeping thread and kill the channel. 639 */ 640 chn_shutdown(ch); 641 chn_abort(ch); 642 CHN_UNLOCK(ch); 643 } 644 645 /* remove /dev/sndstat entry first */ 646 sndstat_unregister(dev); 647 648 PCM_LOCK(d); 649 d->flags |= SD_F_DYING; 650 d->flags &= ~SD_F_REGISTERED; 651 PCM_UNLOCK(d); 652 653 if (d->play_sysctl_tree != NULL) { 654 sysctl_ctx_free(&d->play_sysctl_ctx); 655 d->play_sysctl_tree = NULL; 656 } 657 if (d->rec_sysctl_tree != NULL) { 658 sysctl_ctx_free(&d->rec_sysctl_ctx); 659 d->rec_sysctl_tree = NULL; 660 } 661 662 dsp_destroy_dev(dev); 663 (void)mixer_uninit(dev); 664 665 pcm_killchans(d); 666 667 PCM_LOCK(d); 668 PCM_RELEASE(d); 669 cv_destroy(&d->cv); 670 PCM_UNLOCK(d); 671 snd_mtxfree(d->lock); 672 673 if (snd_unit == device_get_unit(dev)) { 674 snd_unit = pcm_best_unit(-1); 675 if (snd_unit_auto == 0) 676 snd_unit_auto = 1; 677 } 678 679 return (0); 680 } 681 682 /************************************************************************/ 683 684 /** 685 * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 686 * 687 * @param si Pointer to oss_sysinfo struct where information about the 688 * sound subsystem will be written/copied. 689 * 690 * This routine returns information about the sound system, such as the 691 * current OSS version, number of audio, MIDI, and mixer drivers, etc. 692 * Also includes a bitmask showing which of the above types of devices 693 * are open (busy). 694 * 695 * @note 696 * Calling threads must not hold any snddev_info or pcm_channel locks. 697 * 698 * @author Ryan Beasley <ryanb@FreeBSD.org> 699 */ 700 void 701 sound_oss_sysinfo(oss_sysinfo *si) 702 { 703 static char si_product[] = "FreeBSD native OSS ABI"; 704 static char si_version[] = __XSTRING(__FreeBSD_version); 705 static char si_license[] = "BSD"; 706 static int intnbits = sizeof(int) * 8; /* Better suited as macro? 707 Must pester a C guru. */ 708 709 struct snddev_info *d; 710 struct pcm_channel *c; 711 int j, ncards; 712 size_t i; 713 714 ncards = 0; 715 716 strlcpy(si->product, si_product, sizeof(si->product)); 717 strlcpy(si->version, si_version, sizeof(si->version)); 718 si->versionnum = SOUND_VERSION; 719 strlcpy(si->license, si_license, sizeof(si->license)); 720 721 /* 722 * Iterate over PCM devices and their channels, gathering up data 723 * for the numaudios, ncards, and openedaudio fields. 724 */ 725 si->numaudios = 0; 726 bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 727 728 j = 0; 729 730 for (i = 0; pcm_devclass != NULL && 731 i < devclass_get_maxunit(pcm_devclass); i++) { 732 d = devclass_get_softc(pcm_devclass, i); 733 if (!PCM_REGISTERED(d)) 734 continue; 735 736 /* XXX Need Giant magic entry ??? */ 737 738 /* See note in function's docblock */ 739 PCM_UNLOCKASSERT(d); 740 PCM_LOCK(d); 741 742 si->numaudios += PCM_CHANCOUNT(d); 743 ++ncards; 744 745 CHN_FOREACH(c, d, channels.pcm) { 746 CHN_UNLOCKASSERT(c); 747 CHN_LOCK(c); 748 if (c->flags & CHN_F_BUSY) 749 si->openedaudio[j / intnbits] |= 750 (1 << (j % intnbits)); 751 CHN_UNLOCK(c); 752 j++; 753 } 754 755 PCM_UNLOCK(d); 756 } 757 si->numaudioengines = si->numaudios; 758 759 si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 760 /** 761 * @todo Collect num{midis,timers}. 762 * 763 * Need access to sound/midi/midi.c::midistat_lock in order 764 * to safely touch midi_devices and get a head count of, well, 765 * MIDI devices. midistat_lock is a global static (i.e., local to 766 * midi.c), but midi_devices is a regular global; should the mutex 767 * be publicized, or is there another way to get this information? 768 * 769 * NB: MIDI/sequencer stuff is currently on hold. 770 */ 771 si->nummidis = 0; 772 si->numtimers = 0; 773 si->nummixers = mixer_count; 774 si->numcards = ncards; 775 /* OSSv4 docs: Intended only for test apps; API doesn't 776 really have much of a concept of cards. Shouldn't be 777 used by applications. */ 778 779 /** 780 * @todo Fill in "busy devices" fields. 781 * 782 * si->openedmidi = " MIDI devices 783 */ 784 bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 785 786 /* 787 * Si->filler is a reserved array, but according to docs each 788 * element should be set to -1. 789 */ 790 for (i = 0; i < nitems(si->filler); i++) 791 si->filler[i] = -1; 792 } 793 794 int 795 sound_oss_card_info(oss_card_info *si) 796 { 797 struct snddev_info *d; 798 int i, ncards; 799 800 ncards = 0; 801 802 for (i = 0; pcm_devclass != NULL && 803 i < devclass_get_maxunit(pcm_devclass); i++) { 804 d = devclass_get_softc(pcm_devclass, i); 805 if (!PCM_REGISTERED(d)) 806 continue; 807 808 if (ncards++ != si->card) 809 continue; 810 811 PCM_UNLOCKASSERT(d); 812 PCM_LOCK(d); 813 814 strlcpy(si->shortname, device_get_nameunit(d->dev), 815 sizeof(si->shortname)); 816 strlcpy(si->longname, device_get_desc(d->dev), 817 sizeof(si->longname)); 818 strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 819 si->intr_count = si->ack_count = 0; 820 821 PCM_UNLOCK(d); 822 823 return (0); 824 } 825 return (ENXIO); 826 } 827 828 /************************************************************************/ 829 830 static int 831 sound_modevent(module_t mod, int type, void *data) 832 { 833 int ret; 834 835 ret = 0; 836 switch (type) { 837 case MOD_LOAD: 838 pcm_devclass = devclass_create("pcm"); 839 pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 840 break; 841 case MOD_UNLOAD: 842 if (pcmsg_unrhdr != NULL) { 843 delete_unrhdr(pcmsg_unrhdr); 844 pcmsg_unrhdr = NULL; 845 } 846 break; 847 case MOD_SHUTDOWN: 848 break; 849 default: 850 ret = ENOTSUP; 851 } 852 853 return ret; 854 } 855 856 DEV_MODULE(sound, sound_modevent, NULL); 857 MODULE_VERSION(sound, SOUND_MODVER); 858