1 /*- 2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 3 * (C) 1997 Luigi Rizzo 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <dev/sound/pcm/sound.h> 29 #include <dev/sound/pcm/ac97.h> 30 #include <dev/sound/pcm/vchan.h> 31 #include <dev/sound/pcm/dsp.h> 32 #include <dev/sound/version.h> 33 #include <sys/limits.h> 34 #include <sys/sysctl.h> 35 36 #include "feeder_if.h" 37 38 SND_DECLARE_FILE("$FreeBSD$"); 39 40 devclass_t pcm_devclass; 41 42 int pcm_veto_load = 1; 43 44 #ifdef USING_DEVFS 45 int snd_unit = -1; 46 TUNABLE_INT("hw.snd.default_unit", &snd_unit); 47 #endif 48 49 static int snd_unit_auto = 0; 50 TUNABLE_INT("hw.snd.default_auto", &snd_unit_auto); 51 SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RW, 52 &snd_unit_auto, 0, "assign default unit to a newly attached device"); 53 54 int snd_maxautovchans = 16; 55 /* XXX: a tunable implies that we may need more than one sound channel before 56 the system can change a sysctl (/etc/sysctl.conf), do we really need 57 this? */ 58 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 59 60 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 61 62 /* 63 * XXX I've had enough with people not telling proper version/arch 64 * while reporting problems, not after 387397913213th questions/requests. 65 */ 66 static const char snd_driver_version[] = 67 __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH; 68 SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version, 69 0, "Driver version/arch"); 70 71 /** 72 * @brief Unit number allocator for syncgroup IDs 73 */ 74 struct unrhdr *pcmsg_unrhdr = NULL; 75 76 static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 77 78 void * 79 snd_mtxcreate(const char *desc, const char *type) 80 { 81 #ifdef USING_MUTEX 82 struct mtx *m; 83 84 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 85 mtx_init(m, desc, type, MTX_DEF); 86 return m; 87 #else 88 return (void *)0xcafebabe; 89 #endif 90 } 91 92 void 93 snd_mtxfree(void *m) 94 { 95 #ifdef USING_MUTEX 96 struct mtx *mtx = m; 97 98 /* mtx_assert(mtx, MA_OWNED); */ 99 mtx_destroy(mtx); 100 free(mtx, M_DEVBUF); 101 #endif 102 } 103 104 void 105 snd_mtxassert(void *m) 106 { 107 #ifdef USING_MUTEX 108 #ifdef INVARIANTS 109 struct mtx *mtx = m; 110 111 mtx_assert(mtx, MA_OWNED); 112 #endif 113 #endif 114 } 115 /* 116 void 117 snd_mtxlock(void *m) 118 { 119 #ifdef USING_MUTEX 120 struct mtx *mtx = m; 121 122 mtx_lock(mtx); 123 #endif 124 } 125 126 void 127 snd_mtxunlock(void *m) 128 { 129 #ifdef USING_MUTEX 130 struct mtx *mtx = m; 131 132 mtx_unlock(mtx); 133 #endif 134 } 135 */ 136 int 137 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 138 { 139 struct snddev_info *d; 140 #ifdef USING_MUTEX 141 flags &= INTR_MPSAFE; 142 flags |= INTR_TYPE_AV; 143 #else 144 flags = INTR_TYPE_AV; 145 #endif 146 d = device_get_softc(dev); 147 if (d != NULL && (flags & INTR_MPSAFE)) 148 d->flags |= SD_F_MPSAFE; 149 150 return bus_setup_intr(dev, res, flags, 151 #if __FreeBSD_version >= 700031 152 NULL, 153 #endif 154 hand, param, cookiep); 155 } 156 157 #ifndef PCM_DEBUG_MTX 158 void 159 pcm_lock(struct snddev_info *d) 160 { 161 snd_mtxlock(d->lock); 162 } 163 164 void 165 pcm_unlock(struct snddev_info *d) 166 { 167 snd_mtxunlock(d->lock); 168 } 169 #endif 170 171 struct pcm_channel * 172 pcm_getfakechan(struct snddev_info *d) 173 { 174 return d->fakechan; 175 } 176 177 static void 178 pcm_clonereset(struct snddev_info *d) 179 { 180 int cmax; 181 182 PCM_BUSYASSERT(d); 183 184 cmax = d->playcount + d->reccount - 1; 185 if (d->pvchancount > 0) 186 cmax += MAX(d->pvchancount, snd_maxautovchans) - 1; 187 if (d->rvchancount > 0) 188 cmax += MAX(d->rvchancount, snd_maxautovchans) - 1; 189 if (cmax > PCMMAXCLONE) 190 cmax = PCMMAXCLONE; 191 (void)snd_clone_gc(d->clones); 192 (void)snd_clone_setmaxunit(d->clones, cmax); 193 } 194 195 static int 196 pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) 197 { 198 struct pcm_channel *c, *ch, *nch; 199 int err, vcnt; 200 201 PCM_BUSYASSERT(d); 202 203 if ((direction == PCMDIR_PLAY && d->playcount < 1) || 204 (direction == PCMDIR_REC && d->reccount < 1)) 205 return (ENODEV); 206 207 if (!(d->flags & SD_F_AUTOVCHAN)) 208 return (EINVAL); 209 210 if (newcnt < 0 || newcnt > SND_MAXVCHANS) 211 return (E2BIG); 212 213 if (direction == PCMDIR_PLAY) 214 vcnt = d->pvchancount; 215 else if (direction == PCMDIR_REC) 216 vcnt = d->rvchancount; 217 else 218 return (EINVAL); 219 220 if (newcnt > vcnt) { 221 KASSERT(num == -1 || 222 (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt), 223 ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d", 224 num, newcnt, vcnt)); 225 /* add new vchans - find a parent channel first */ 226 ch = NULL; 227 CHN_FOREACH(c, d, channels.pcm) { 228 CHN_LOCK(c); 229 if (c->direction == direction && 230 ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 231 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 232 ch = c; 233 break; 234 } 235 CHN_UNLOCK(c); 236 } 237 if (ch == NULL) 238 return (EBUSY); 239 ch->flags |= CHN_F_BUSY; 240 err = 0; 241 while (err == 0 && newcnt > vcnt) { 242 err = vchan_create(ch, num); 243 if (err == 0) 244 vcnt++; 245 else if (err == E2BIG && newcnt > vcnt) 246 device_printf(d->dev, 247 "%s: err=%d Maximum channel reached.\n", 248 __func__, err); 249 } 250 if (vcnt == 0) 251 ch->flags &= ~CHN_F_BUSY; 252 CHN_UNLOCK(ch); 253 if (err != 0) 254 return (err); 255 else 256 pcm_clonereset(d); 257 } else if (newcnt < vcnt) { 258 KASSERT(num == -1, 259 ("bogus vchan_destroy() request num=%d", num)); 260 CHN_FOREACH(c, d, channels.pcm) { 261 CHN_LOCK(c); 262 if (c->direction != direction || 263 CHN_EMPTY(c, children) || 264 !(c->flags & CHN_F_HAS_VCHAN)) { 265 CHN_UNLOCK(c); 266 continue; 267 } 268 CHN_FOREACH_SAFE(ch, c, nch, children) { 269 CHN_LOCK(ch); 270 if (!(ch->flags & CHN_F_BUSY)) { 271 CHN_UNLOCK(ch); 272 CHN_UNLOCK(c); 273 err = vchan_destroy(ch); 274 CHN_LOCK(c); 275 if (err == 0) 276 vcnt--; 277 } else 278 CHN_UNLOCK(ch); 279 if (vcnt == newcnt) 280 break; 281 } 282 CHN_UNLOCK(c); 283 break; 284 } 285 pcm_clonereset(d); 286 } 287 288 return (0); 289 } 290 291 /* return error status and a locked channel */ 292 int 293 pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 294 pid_t pid, int devunit) 295 { 296 struct pcm_channel *c; 297 int err, vchancount; 298 299 KASSERT(d != NULL && ch != NULL && (devunit == -1 || 300 !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && 301 (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 302 ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d", 303 __func__, d, ch, direction, pid, devunit)); 304 PCM_BUSYASSERT(d); 305 306 /* Double check again. */ 307 if (devunit != -1) { 308 switch (snd_unit2d(devunit)) { 309 case SND_DEV_DSPHW_PLAY: 310 case SND_DEV_DSPHW_VPLAY: 311 if (direction != PCMDIR_PLAY) 312 return (EOPNOTSUPP); 313 break; 314 case SND_DEV_DSPHW_REC: 315 case SND_DEV_DSPHW_VREC: 316 if (direction != PCMDIR_REC) 317 return (EOPNOTSUPP); 318 break; 319 default: 320 if (!(direction == PCMDIR_PLAY || 321 direction == PCMDIR_REC)) 322 return (EOPNOTSUPP); 323 break; 324 } 325 } 326 327 retry_chnalloc: 328 err = EOPNOTSUPP; 329 /* scan for a free channel */ 330 CHN_FOREACH(c, d, channels.pcm) { 331 CHN_LOCK(c); 332 if (c->direction == direction && !(c->flags & CHN_F_BUSY) && 333 (devunit == -1 || devunit == -2 || c->unit == devunit)) { 334 c->flags |= CHN_F_BUSY; 335 c->pid = pid; 336 *ch = c; 337 return (0); 338 } else if (c->unit == devunit) { 339 if (c->direction != direction) 340 err = EOPNOTSUPP; 341 else if (c->flags & CHN_F_BUSY) 342 err = EBUSY; 343 else 344 err = EINVAL; 345 CHN_UNLOCK(c); 346 return (err); 347 } else if ((devunit == -1 || devunit == -2) && 348 c->direction == direction && (c->flags & CHN_F_BUSY)) 349 err = EBUSY; 350 CHN_UNLOCK(c); 351 } 352 353 if (devunit == -2) 354 return (err); 355 356 /* no channel available */ 357 if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || 358 snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { 359 if (direction == PCMDIR_PLAY) 360 vchancount = d->pvchancount; 361 else 362 vchancount = d->rvchancount; 363 if (!(vchancount > 0 && vchancount < snd_maxautovchans) && 364 (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) 365 return (err); 366 err = pcm_setvchans(d, direction, vchancount + 1, 367 (devunit == -1) ? -1 : snd_unit2c(devunit)); 368 if (err == 0) { 369 if (devunit == -1) 370 devunit = -2; 371 goto retry_chnalloc; 372 } 373 } 374 375 return (err); 376 } 377 378 /* release a locked channel and unlock it */ 379 int 380 pcm_chnrelease(struct pcm_channel *c) 381 { 382 PCM_BUSYASSERT(c->parentsnddev); 383 CHN_LOCKASSERT(c); 384 385 c->flags &= ~CHN_F_BUSY; 386 c->pid = -1; 387 CHN_UNLOCK(c); 388 389 return (0); 390 } 391 392 int 393 pcm_chnref(struct pcm_channel *c, int ref) 394 { 395 PCM_BUSYASSERT(c->parentsnddev); 396 CHN_LOCKASSERT(c); 397 398 c->refcount += ref; 399 400 return (c->refcount); 401 } 402 403 int 404 pcm_inprog(struct snddev_info *d, int delta) 405 { 406 snd_mtxassert(d->lock); 407 408 d->inprog += delta; 409 410 return (d->inprog); 411 } 412 413 static void 414 pcm_setmaxautovchans(struct snddev_info *d, int num) 415 { 416 PCM_BUSYASSERT(d); 417 418 if (num < 0) 419 return; 420 421 if (num >= 0 && d->pvchancount > num) 422 (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1); 423 else if (num > 0 && d->pvchancount == 0) 424 (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1); 425 426 if (num >= 0 && d->rvchancount > num) 427 (void)pcm_setvchans(d, PCMDIR_REC, num, -1); 428 else if (num > 0 && d->rvchancount == 0) 429 (void)pcm_setvchans(d, PCMDIR_REC, 1, -1); 430 431 pcm_clonereset(d); 432 } 433 434 #ifdef USING_DEVFS 435 static int 436 sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 437 { 438 struct snddev_info *d; 439 int error, unit; 440 441 unit = snd_unit; 442 error = sysctl_handle_int(oidp, &unit, 0, req); 443 if (error == 0 && req->newptr != NULL) { 444 d = devclass_get_softc(pcm_devclass, unit); 445 if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 446 return EINVAL; 447 snd_unit = unit; 448 } 449 return (error); 450 } 451 /* XXX: do we need a way to let the user change the default unit? */ 452 SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW, 453 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device"); 454 #endif 455 456 static int 457 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 458 { 459 struct snddev_info *d; 460 int i, v, error; 461 462 v = snd_maxautovchans; 463 error = sysctl_handle_int(oidp, &v, 0, req); 464 if (error == 0 && req->newptr != NULL) { 465 if (v < 0) 466 v = 0; 467 if (v > SND_MAXVCHANS) 468 v = SND_MAXVCHANS; 469 snd_maxautovchans = v; 470 for (i = 0; pcm_devclass != NULL && 471 i < devclass_get_maxunit(pcm_devclass); i++) { 472 d = devclass_get_softc(pcm_devclass, i); 473 if (!PCM_REGISTERED(d)) 474 continue; 475 PCM_ACQUIRE_QUICK(d); 476 pcm_setmaxautovchans(d, v); 477 PCM_RELEASE_QUICK(d); 478 } 479 } 480 return (error); 481 } 482 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 483 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 484 485 struct pcm_channel * 486 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo) 487 { 488 struct pcm_channel *ch; 489 int direction, err, rpnum, *pnum, max; 490 int udc, device, chan; 491 char *dirs, *devname, buf[CHN_NAMELEN]; 492 493 PCM_BUSYASSERT(d); 494 snd_mtxassert(d->lock); 495 KASSERT(num >= -1, ("invalid num=%d", num)); 496 497 498 switch (dir) { 499 case PCMDIR_PLAY: 500 dirs = "play"; 501 direction = PCMDIR_PLAY; 502 pnum = &d->playcount; 503 device = SND_DEV_DSPHW_PLAY; 504 max = SND_MAXHWCHAN; 505 break; 506 case PCMDIR_PLAY_VIRTUAL: 507 dirs = "virtual"; 508 direction = PCMDIR_PLAY; 509 pnum = &d->pvchancount; 510 device = SND_DEV_DSPHW_VPLAY; 511 max = SND_MAXVCHANS; 512 break; 513 case PCMDIR_REC: 514 dirs = "record"; 515 direction = PCMDIR_REC; 516 pnum = &d->reccount; 517 device = SND_DEV_DSPHW_REC; 518 max = SND_MAXHWCHAN; 519 break; 520 case PCMDIR_REC_VIRTUAL: 521 dirs = "virtual"; 522 direction = PCMDIR_REC; 523 pnum = &d->rvchancount; 524 device = SND_DEV_DSPHW_VREC; 525 max = SND_MAXVCHANS; 526 break; 527 default: 528 return (NULL); 529 } 530 531 chan = (num == -1) ? 0 : num; 532 533 if (*pnum >= max || chan >= max) 534 return (NULL); 535 536 rpnum = 0; 537 538 CHN_FOREACH(ch, d, channels.pcm) { 539 if (CHN_DEV(ch) != device) 540 continue; 541 if (chan == CHN_CHAN(ch)) { 542 if (num != -1) { 543 device_printf(d->dev, 544 "channel num=%d allocated!\n", chan); 545 return (NULL); 546 } 547 chan++; 548 if (chan >= max) { 549 device_printf(d->dev, 550 "chan=%d > %d\n", chan, max); 551 return (NULL); 552 } 553 } 554 rpnum++; 555 } 556 557 if (*pnum != rpnum) { 558 device_printf(d->dev, 559 "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", 560 __func__, dirs, *pnum, rpnum); 561 return (NULL); 562 } 563 564 udc = snd_mkunit(device_get_unit(d->dev), device, chan); 565 devname = dsp_unit2name(buf, sizeof(buf), udc); 566 567 if (devname == NULL) { 568 device_printf(d->dev, 569 "Failed to query device name udc=0x%08x\n", udc); 570 return (NULL); 571 } 572 573 pcm_unlock(d); 574 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 575 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); 576 ch->unit = udc; 577 ch->pid = -1; 578 ch->parentsnddev = d; 579 ch->parentchannel = parent; 580 ch->dev = d->dev; 581 ch->trigger = PCMTRIG_STOP; 582 snprintf(ch->name, sizeof(ch->name), "%s:%s:%s", 583 device_get_nameunit(ch->dev), dirs, devname); 584 585 err = chn_init(ch, devinfo, dir, direction); 586 pcm_lock(d); 587 if (err) { 588 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", 589 ch->name, err); 590 kobj_delete(ch->methods, M_DEVBUF); 591 free(ch, M_DEVBUF); 592 return (NULL); 593 } 594 595 return (ch); 596 } 597 598 int 599 pcm_chn_destroy(struct pcm_channel *ch) 600 { 601 struct snddev_info *d; 602 int err; 603 604 d = ch->parentsnddev; 605 PCM_BUSYASSERT(d); 606 607 err = chn_kill(ch); 608 if (err) { 609 device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n", 610 ch->name, err); 611 return (err); 612 } 613 614 kobj_delete(ch->methods, M_DEVBUF); 615 free(ch, M_DEVBUF); 616 617 return (0); 618 } 619 620 int 621 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 622 { 623 struct pcm_channel *tmp, *after; 624 int num; 625 626 PCM_BUSYASSERT(d); 627 snd_mtxassert(d->lock); 628 KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 629 ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 630 631 after = NULL; 632 tmp = NULL; 633 num = 0; 634 635 /* 636 * Look for possible device collision. 637 */ 638 CHN_FOREACH(tmp, d, channels.pcm) { 639 if (tmp->unit == ch->unit) { 640 device_printf(d->dev, "%s(): Device collision " 641 "old=%p new=%p devunit=0x%08x\n", 642 __func__, tmp, ch, ch->unit); 643 return (ENODEV); 644 } 645 if (CHN_DEV(tmp) < CHN_DEV(ch)) { 646 if (num == 0) 647 after = tmp; 648 continue; 649 } else if (CHN_DEV(tmp) > CHN_DEV(ch)) 650 break; 651 num++; 652 if (CHN_CHAN(tmp) < CHN_CHAN(ch)) 653 after = tmp; 654 else if (CHN_CHAN(tmp) > CHN_CHAN(ch)) 655 break; 656 } 657 658 if (after != NULL) { 659 CHN_INSERT_AFTER(after, ch, channels.pcm); 660 } else { 661 CHN_INSERT_HEAD(d, ch, channels.pcm); 662 } 663 664 switch (CHN_DEV(ch)) { 665 case SND_DEV_DSPHW_PLAY: 666 d->playcount++; 667 break; 668 case SND_DEV_DSPHW_VPLAY: 669 d->pvchancount++; 670 break; 671 case SND_DEV_DSPHW_REC: 672 d->reccount++; 673 break; 674 case SND_DEV_DSPHW_VREC: 675 d->rvchancount++; 676 break; 677 default: 678 break; 679 } 680 681 d->devcount++; 682 683 return (0); 684 } 685 686 int 687 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 688 { 689 struct pcm_channel *tmp; 690 691 PCM_BUSYASSERT(d); 692 snd_mtxassert(d->lock); 693 694 tmp = NULL; 695 696 CHN_FOREACH(tmp, d, channels.pcm) { 697 if (tmp == ch) 698 break; 699 } 700 701 if (tmp != ch) 702 return (EINVAL); 703 704 CHN_REMOVE(d, ch, channels.pcm); 705 706 switch (CHN_DEV(ch)) { 707 case SND_DEV_DSPHW_PLAY: 708 d->playcount--; 709 break; 710 case SND_DEV_DSPHW_VPLAY: 711 d->pvchancount--; 712 break; 713 case SND_DEV_DSPHW_REC: 714 d->reccount--; 715 break; 716 case SND_DEV_DSPHW_VREC: 717 d->rvchancount--; 718 break; 719 default: 720 break; 721 } 722 723 d->devcount--; 724 725 return (0); 726 } 727 728 int 729 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 730 { 731 struct snddev_info *d = device_get_softc(dev); 732 struct pcm_channel *ch; 733 int err; 734 735 PCM_BUSYASSERT(d); 736 737 pcm_lock(d); 738 ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); 739 if (!ch) { 740 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", 741 cls->name, dir, devinfo); 742 pcm_unlock(d); 743 return (ENODEV); 744 } 745 746 err = pcm_chn_add(d, ch); 747 pcm_unlock(d); 748 if (err) { 749 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", 750 ch->name, err); 751 pcm_chn_destroy(ch); 752 } 753 754 return (err); 755 } 756 757 static int 758 pcm_killchan(device_t dev) 759 { 760 struct snddev_info *d = device_get_softc(dev); 761 struct pcm_channel *ch; 762 int error; 763 764 PCM_BUSYASSERT(d); 765 766 ch = CHN_FIRST(d, channels.pcm); 767 768 pcm_lock(d); 769 error = pcm_chn_remove(d, ch); 770 pcm_unlock(d); 771 if (error) 772 return (error); 773 return (pcm_chn_destroy(ch)); 774 } 775 776 int 777 pcm_setstatus(device_t dev, char *str) 778 { 779 struct snddev_info *d = device_get_softc(dev); 780 781 PCM_BUSYASSERT(d); 782 783 if (d->playcount == 0 || d->reccount == 0) 784 d->flags |= SD_F_SIMPLEX; 785 786 if ((d->playcount > 0 || d->reccount > 0) && 787 !(d->flags & SD_F_AUTOVCHAN)) { 788 d->flags |= SD_F_AUTOVCHAN; 789 vchan_initsys(dev); 790 } 791 792 pcm_setmaxautovchans(d, snd_maxautovchans); 793 794 strlcpy(d->status, str, SND_STATUSLEN); 795 796 pcm_lock(d); 797 798 /* Last stage, enable cloning. */ 799 if (d->clones != NULL) 800 (void)snd_clone_enable(d->clones); 801 802 /* Done, we're ready.. */ 803 d->flags |= SD_F_REGISTERED; 804 805 PCM_RELEASE(d); 806 807 pcm_unlock(d); 808 809 if (snd_unit < 0 || snd_unit_auto != 0) 810 snd_unit = device_get_unit(dev); 811 812 return (0); 813 } 814 815 uint32_t 816 pcm_getflags(device_t dev) 817 { 818 struct snddev_info *d = device_get_softc(dev); 819 820 return d->flags; 821 } 822 823 void 824 pcm_setflags(device_t dev, uint32_t val) 825 { 826 struct snddev_info *d = device_get_softc(dev); 827 828 d->flags = val; 829 } 830 831 void * 832 pcm_getdevinfo(device_t dev) 833 { 834 struct snddev_info *d = device_get_softc(dev); 835 836 return d->devinfo; 837 } 838 839 unsigned int 840 pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 841 { 842 struct snddev_info *d = device_get_softc(dev); 843 int sz, x; 844 845 sz = 0; 846 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 847 x = sz; 848 RANGE(sz, minbufsz, maxbufsz); 849 if (x != sz) 850 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 851 x = minbufsz; 852 while (x < sz) 853 x <<= 1; 854 if (x > sz) 855 x >>= 1; 856 if (x != sz) { 857 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 858 sz = x; 859 } 860 } else { 861 sz = deflt; 862 } 863 864 d->bufsz = sz; 865 866 return sz; 867 } 868 869 #if defined(SND_DYNSYSCTL) && defined(SND_DEBUG) 870 static int 871 sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS) 872 { 873 struct snddev_info *d; 874 uint32_t flags; 875 int err; 876 877 d = oidp->oid_arg1; 878 if (!PCM_REGISTERED(d) || d->clones == NULL) 879 return (ENODEV); 880 881 PCM_ACQUIRE_QUICK(d); 882 883 flags = snd_clone_getflags(d->clones); 884 err = sysctl_handle_int(oidp, &flags, 0, req); 885 886 if (err == 0 && req->newptr != NULL) { 887 if (flags & ~SND_CLONE_MASK) 888 err = EINVAL; 889 else 890 (void)snd_clone_setflags(d->clones, flags); 891 } 892 893 PCM_RELEASE_QUICK(d); 894 895 return (err); 896 } 897 898 static int 899 sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS) 900 { 901 struct snddev_info *d; 902 int err, deadline; 903 904 d = oidp->oid_arg1; 905 if (!PCM_REGISTERED(d) || d->clones == NULL) 906 return (ENODEV); 907 908 PCM_ACQUIRE_QUICK(d); 909 910 deadline = snd_clone_getdeadline(d->clones); 911 err = sysctl_handle_int(oidp, &deadline, 0, req); 912 913 if (err == 0 && req->newptr != NULL) { 914 if (deadline < 0) 915 err = EINVAL; 916 else 917 (void)snd_clone_setdeadline(d->clones, deadline); 918 } 919 920 PCM_RELEASE_QUICK(d); 921 922 return (err); 923 } 924 925 static int 926 sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS) 927 { 928 struct snddev_info *d; 929 int err, val; 930 931 d = oidp->oid_arg1; 932 if (!PCM_REGISTERED(d) || d->clones == NULL) 933 return (ENODEV); 934 935 val = 0; 936 err = sysctl_handle_int(oidp, &val, 0, req); 937 938 if (err == 0 && req->newptr != NULL && val != 0) { 939 PCM_ACQUIRE_QUICK(d); 940 val = snd_clone_gc(d->clones); 941 PCM_RELEASE_QUICK(d); 942 if (bootverbose != 0 || snd_verbose > 3) 943 device_printf(d->dev, "clone gc: pruned=%d\n", val); 944 } 945 946 return (err); 947 } 948 949 static int 950 sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS) 951 { 952 struct snddev_info *d; 953 int i, err, val; 954 955 val = 0; 956 err = sysctl_handle_int(oidp, &val, 0, req); 957 958 if (err == 0 && req->newptr != NULL && val != 0) { 959 for (i = 0; pcm_devclass != NULL && 960 i < devclass_get_maxunit(pcm_devclass); i++) { 961 d = devclass_get_softc(pcm_devclass, i); 962 if (!PCM_REGISTERED(d) || d->clones == NULL) 963 continue; 964 PCM_ACQUIRE_QUICK(d); 965 val = snd_clone_gc(d->clones); 966 PCM_RELEASE_QUICK(d); 967 if (bootverbose != 0 || snd_verbose > 3) 968 device_printf(d->dev, "clone gc: pruned=%d\n", 969 val); 970 } 971 } 972 973 return (err); 974 } 975 SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW, 976 0, sizeof(int), sysctl_hw_snd_clone_gc, "I", 977 "global clone garbage collector"); 978 #endif 979 980 int 981 pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 982 { 983 struct snddev_info *d; 984 985 if (pcm_veto_load) { 986 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 987 988 return EINVAL; 989 } 990 991 if (device_get_unit(dev) > PCMMAXUNIT) { 992 device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n", 993 device_get_unit(dev), PCMMAXUNIT); 994 device_printf(dev, 995 "Use 'hw.snd.maxunit' tunable to raise the limit.\n"); 996 return ENODEV; 997 } 998 999 d = device_get_softc(dev); 1000 d->dev = dev; 1001 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 1002 cv_init(&d->cv, device_get_nameunit(dev)); 1003 PCM_ACQUIRE_QUICK(d); 1004 dsp_cdevinfo_init(d); 1005 #if 0 1006 /* 1007 * d->flags should be cleared by the allocator of the softc. 1008 * We cannot clear this field here because several devices set 1009 * this flag before calling pcm_register(). 1010 */ 1011 d->flags = 0; 1012 #endif 1013 d->devinfo = devinfo; 1014 d->devcount = 0; 1015 d->reccount = 0; 1016 d->playcount = 0; 1017 d->pvchancount = 0; 1018 d->rvchancount = 0; 1019 d->pvchanrate = 0; 1020 d->pvchanformat = 0; 1021 d->rvchanrate = 0; 1022 d->rvchanformat = 0; 1023 d->inprog = 0; 1024 1025 /* 1026 * Create clone manager, disabled by default. Cloning will be 1027 * enabled during final stage of driver iniialization through 1028 * pcm_setstatus(). 1029 */ 1030 d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE, 1031 SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK | 1032 SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF | 1033 SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED); 1034 1035 if (bootverbose != 0 || snd_verbose > 3) { 1036 device_printf(dev, 1037 "clone manager: deadline=%dms flags=0x%08x\n", 1038 snd_clone_getdeadline(d->clones), 1039 snd_clone_getflags(d->clones)); 1040 } 1041 1042 CHN_INIT(d, channels.pcm); 1043 CHN_INIT(d, channels.pcm.busy); 1044 1045 /* XXX This is incorrect, but lets play along for now. */ 1046 if ((numplay == 0 || numrec == 0) && numplay != numrec) 1047 d->flags |= SD_F_SIMPLEX; 1048 1049 d->fakechan = fkchan_setup(dev); 1050 chn_init(d->fakechan, NULL, 0, 0); 1051 1052 #ifdef SND_DYNSYSCTL 1053 sysctl_ctx_init(&d->play_sysctl_ctx); 1054 d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 1055 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 1056 CTLFLAG_RD, 0, "playback channels node"); 1057 sysctl_ctx_init(&d->rec_sysctl_ctx); 1058 d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 1059 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 1060 CTLFLAG_RD, 0, "record channels node"); 1061 /* XXX: an user should be able to set this with a control tool, the 1062 sysadmin then needs min+max sysctls for this */ 1063 SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 1064 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 1065 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 1066 #ifdef SND_DEBUG 1067 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1068 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1069 "clone_flags", CTLTYPE_UINT | CTLFLAG_RW, d, sizeof(d), 1070 sysctl_dev_pcm_clone_flags, "IU", 1071 "clone flags"); 1072 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1073 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1074 "clone_deadline", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 1075 sysctl_dev_pcm_clone_deadline, "I", 1076 "clone expiration deadline (ms)"); 1077 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1078 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1079 "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 1080 sysctl_dev_pcm_clone_gc, "I", 1081 "clone garbage collector"); 1082 #endif 1083 #endif 1084 1085 if (numplay > 0 || numrec > 0) { 1086 d->flags |= SD_F_AUTOVCHAN; 1087 vchan_initsys(dev); 1088 } 1089 1090 sndstat_register(dev, d->status, sndstat_prepare_pcm); 1091 1092 return 0; 1093 } 1094 1095 int 1096 pcm_unregister(device_t dev) 1097 { 1098 struct snddev_info *d; 1099 struct pcm_channel *ch; 1100 struct thread *td; 1101 int i; 1102 1103 td = curthread; 1104 d = device_get_softc(dev); 1105 1106 if (!PCM_ALIVE(d)) { 1107 device_printf(dev, "unregister: device not configured\n"); 1108 return (0); 1109 } 1110 1111 if (sndstat_acquire(td) != 0) { 1112 device_printf(dev, "unregister: sndstat busy\n"); 1113 return (EBUSY); 1114 } 1115 1116 pcm_lock(d); 1117 PCM_WAIT(d); 1118 1119 if (d->inprog != 0) { 1120 device_printf(dev, "unregister: operation in progress\n"); 1121 pcm_unlock(d); 1122 sndstat_release(td); 1123 return (EBUSY); 1124 } 1125 1126 PCM_ACQUIRE(d); 1127 pcm_unlock(d); 1128 1129 CHN_FOREACH(ch, d, channels.pcm) { 1130 CHN_LOCK(ch); 1131 if (ch->refcount > 0) { 1132 device_printf(dev, 1133 "unregister: channel %s busy (pid %d)\n", 1134 ch->name, ch->pid); 1135 CHN_UNLOCK(ch); 1136 PCM_RELEASE_QUICK(d); 1137 sndstat_release(td); 1138 return (EBUSY); 1139 } 1140 CHN_UNLOCK(ch); 1141 } 1142 1143 if (d->clones != NULL) { 1144 if (snd_clone_busy(d->clones) != 0) { 1145 device_printf(dev, "unregister: clone busy\n"); 1146 PCM_RELEASE_QUICK(d); 1147 sndstat_release(td); 1148 return (EBUSY); 1149 } else { 1150 pcm_lock(d); 1151 (void)snd_clone_disable(d->clones); 1152 pcm_unlock(d); 1153 } 1154 } 1155 1156 if (mixer_uninit(dev) == EBUSY) { 1157 device_printf(dev, "unregister: mixer busy\n"); 1158 pcm_lock(d); 1159 if (d->clones != NULL) 1160 (void)snd_clone_enable(d->clones); 1161 PCM_RELEASE(d); 1162 pcm_unlock(d); 1163 sndstat_release(td); 1164 return (EBUSY); 1165 } 1166 1167 pcm_lock(d); 1168 d->flags |= SD_F_DYING; 1169 d->flags &= ~SD_F_REGISTERED; 1170 pcm_unlock(d); 1171 1172 /* 1173 * No lock being held, so this thing can be flushed without 1174 * stucking into devdrn oblivion. 1175 */ 1176 if (d->clones != NULL) { 1177 snd_clone_destroy(d->clones); 1178 d->clones = NULL; 1179 } 1180 1181 #ifdef SND_DYNSYSCTL 1182 if (d->play_sysctl_tree != NULL) { 1183 sysctl_ctx_free(&d->play_sysctl_ctx); 1184 d->play_sysctl_tree = NULL; 1185 } 1186 if (d->rec_sysctl_tree != NULL) { 1187 sysctl_ctx_free(&d->rec_sysctl_ctx); 1188 d->rec_sysctl_tree = NULL; 1189 } 1190 #endif 1191 1192 while (!CHN_EMPTY(d, channels.pcm)) 1193 pcm_killchan(dev); 1194 1195 chn_kill(d->fakechan); 1196 fkchan_kill(d->fakechan); 1197 1198 dsp_cdevinfo_flush(d); 1199 1200 pcm_lock(d); 1201 PCM_RELEASE(d); 1202 cv_destroy(&d->cv); 1203 pcm_unlock(d); 1204 snd_mtxfree(d->lock); 1205 sndstat_unregister(dev); 1206 sndstat_release(td); 1207 1208 if (snd_unit == device_get_unit(dev)) { 1209 /* 1210 * Reassign default unit to the next available dev, but 1211 * first, reset snd_unit to something ridiculous. 1212 */ 1213 snd_unit = -1; 1214 for (i = 0; pcm_devclass != NULL && 1215 i < devclass_get_maxunit(pcm_devclass); i++) { 1216 if (device_get_unit(dev) == i) 1217 continue; 1218 d = devclass_get_softc(pcm_devclass, i); 1219 if (PCM_REGISTERED(d)) { 1220 snd_unit = i; 1221 break; 1222 } 1223 } 1224 } 1225 1226 return (0); 1227 } 1228 1229 /************************************************************************/ 1230 1231 static int 1232 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 1233 { 1234 struct snddev_info *d; 1235 struct pcm_channel *c; 1236 struct pcm_feeder *f; 1237 1238 if (verbose < 1) 1239 return 0; 1240 1241 d = device_get_softc(dev); 1242 if (!d) 1243 return ENXIO; 1244 1245 PCM_BUSYASSERT(d); 1246 1247 if (CHN_EMPTY(d, channels.pcm)) { 1248 sbuf_printf(s, " (mixer only)"); 1249 return 0; 1250 } 1251 1252 sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", 1253 d->playcount, d->pvchancount, 1254 d->reccount, d->rvchancount, 1255 (d->flags & SD_F_SIMPLEX)? "" : " duplex", 1256 #ifdef USING_DEVFS 1257 (device_get_unit(dev) == snd_unit)? " default" : "" 1258 #else 1259 "" 1260 #endif 1261 ); 1262 1263 if (verbose <= 1) 1264 return 0; 1265 1266 CHN_FOREACH(c, d, channels.pcm) { 1267 1268 KASSERT(c->bufhard != NULL && c->bufsoft != NULL, 1269 ("hosed pcm channel setup")); 1270 1271 sbuf_printf(s, "\n\t"); 1272 1273 /* it would be better to indent child channels */ 1274 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 1275 sbuf_printf(s, "spd %d", c->speed); 1276 if (c->speed != sndbuf_getspd(c->bufhard)) 1277 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 1278 sbuf_printf(s, ", fmt 0x%08x", c->format); 1279 if (c->format != sndbuf_getfmt(c->bufhard)) 1280 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 1281 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 1282 if (c->pid != -1) 1283 sbuf_printf(s, ", pid %d", c->pid); 1284 sbuf_printf(s, "\n\t"); 1285 1286 sbuf_printf(s, "interrupts %d, ", c->interrupts); 1287 if (c->direction == PCMDIR_REC) 1288 sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 1289 c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), 1290 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1291 sndbuf_getblkcnt(c->bufhard), 1292 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1293 sndbuf_getblkcnt(c->bufsoft)); 1294 else 1295 sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", 1296 c->xruns, c->feedcount, sndbuf_getready(c->bufsoft), 1297 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1298 sndbuf_getblkcnt(c->bufhard), 1299 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1300 sndbuf_getblkcnt(c->bufsoft)); 1301 sbuf_printf(s, "\n\t"); 1302 1303 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 1304 sbuf_printf(s, " -> "); 1305 f = c->feeder; 1306 while (f->source != NULL) 1307 f = f->source; 1308 while (f != NULL) { 1309 sbuf_printf(s, "%s", f->class->name); 1310 if (f->desc->type == FEEDER_FMT) 1311 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 1312 if (f->desc->type == FEEDER_RATE) 1313 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 1314 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || 1315 f->desc->type == FEEDER_VOLUME) 1316 sbuf_printf(s, "(0x%08x)", f->desc->out); 1317 sbuf_printf(s, " -> "); 1318 f = f->parent; 1319 } 1320 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 1321 } 1322 1323 return 0; 1324 } 1325 1326 /************************************************************************/ 1327 1328 #ifdef SND_DYNSYSCTL 1329 int 1330 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 1331 { 1332 struct snddev_info *d; 1333 int direction, vchancount; 1334 int err, cnt; 1335 1336 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 1337 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 1338 return (EINVAL); 1339 1340 pcm_lock(d); 1341 PCM_WAIT(d); 1342 1343 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 1344 case VCHAN_PLAY: 1345 direction = PCMDIR_PLAY; 1346 vchancount = d->pvchancount; 1347 cnt = d->playcount; 1348 break; 1349 case VCHAN_REC: 1350 direction = PCMDIR_REC; 1351 vchancount = d->rvchancount; 1352 cnt = d->reccount; 1353 break; 1354 default: 1355 pcm_unlock(d); 1356 return (EINVAL); 1357 break; 1358 } 1359 1360 if (cnt < 1) { 1361 pcm_unlock(d); 1362 return (ENODEV); 1363 } 1364 1365 PCM_ACQUIRE(d); 1366 pcm_unlock(d); 1367 1368 cnt = vchancount; 1369 err = sysctl_handle_int(oidp, &cnt, 0, req); 1370 1371 if (err == 0 && req->newptr != NULL && vchancount != cnt) { 1372 if (cnt < 0) 1373 cnt = 0; 1374 if (cnt > SND_MAXVCHANS) 1375 cnt = SND_MAXVCHANS; 1376 err = pcm_setvchans(d, direction, cnt, -1); 1377 } 1378 1379 PCM_RELEASE_QUICK(d); 1380 1381 return err; 1382 } 1383 #endif 1384 1385 /************************************************************************/ 1386 1387 /** 1388 * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 1389 * 1390 * @param si Pointer to oss_sysinfo struct where information about the 1391 * sound subsystem will be written/copied. 1392 * 1393 * This routine returns information about the sound system, such as the 1394 * current OSS version, number of audio, MIDI, and mixer drivers, etc. 1395 * Also includes a bitmask showing which of the above types of devices 1396 * are open (busy). 1397 * 1398 * @note 1399 * Calling threads must not hold any snddev_info or pcm_channel locks. 1400 * 1401 * @author Ryan Beasley <ryanb@FreeBSD.org> 1402 */ 1403 void 1404 sound_oss_sysinfo(oss_sysinfo *si) 1405 { 1406 static char si_product[] = "FreeBSD native OSS ABI"; 1407 static char si_version[] = __XSTRING(__FreeBSD_version); 1408 static int intnbits = sizeof(int) * 8; /* Better suited as macro? 1409 Must pester a C guru. */ 1410 1411 struct snddev_info *d; 1412 struct pcm_channel *c; 1413 int i, j, ncards; 1414 1415 ncards = 0; 1416 1417 strlcpy(si->product, si_product, sizeof(si->product)); 1418 strlcpy(si->version, si_version, sizeof(si->version)); 1419 si->versionnum = SOUND_VERSION; 1420 1421 /* 1422 * Iterate over PCM devices and their channels, gathering up data 1423 * for the numaudios, ncards, and openedaudio fields. 1424 */ 1425 si->numaudios = 0; 1426 bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 1427 1428 j = 0; 1429 1430 for (i = 0; pcm_devclass != NULL && 1431 i < devclass_get_maxunit(pcm_devclass); i++) { 1432 d = devclass_get_softc(pcm_devclass, i); 1433 if (!PCM_REGISTERED(d)) 1434 continue; 1435 1436 /* XXX Need Giant magic entry ??? */ 1437 1438 /* See note in function's docblock */ 1439 mtx_assert(d->lock, MA_NOTOWNED); 1440 pcm_lock(d); 1441 1442 si->numaudios += d->devcount; 1443 ++ncards; 1444 1445 CHN_FOREACH(c, d, channels.pcm) { 1446 mtx_assert(c->lock, MA_NOTOWNED); 1447 CHN_LOCK(c); 1448 if (c->flags & CHN_F_BUSY) 1449 si->openedaudio[j / intnbits] |= 1450 (1 << (j % intnbits)); 1451 CHN_UNLOCK(c); 1452 j++; 1453 } 1454 1455 pcm_unlock(d); 1456 } 1457 1458 si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 1459 /** 1460 * @todo Collect num{midis,timers}. 1461 * 1462 * Need access to sound/midi/midi.c::midistat_lock in order 1463 * to safely touch midi_devices and get a head count of, well, 1464 * MIDI devices. midistat_lock is a global static (i.e., local to 1465 * midi.c), but midi_devices is a regular global; should the mutex 1466 * be publicized, or is there another way to get this information? 1467 * 1468 * NB: MIDI/sequencer stuff is currently on hold. 1469 */ 1470 si->nummidis = 0; 1471 si->numtimers = 0; 1472 si->nummixers = mixer_count; 1473 si->numcards = ncards; 1474 /* OSSv4 docs: Intended only for test apps; API doesn't 1475 really have much of a concept of cards. Shouldn't be 1476 used by applications. */ 1477 1478 /** 1479 * @todo Fill in "busy devices" fields. 1480 * 1481 * si->openedmidi = " MIDI devices 1482 */ 1483 bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 1484 1485 /* 1486 * Si->filler is a reserved array, but according to docs each 1487 * element should be set to -1. 1488 */ 1489 for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++) 1490 si->filler[i] = -1; 1491 } 1492 1493 /************************************************************************/ 1494 1495 static int 1496 sound_modevent(module_t mod, int type, void *data) 1497 { 1498 int ret; 1499 #if 0 1500 return (midi_modevent(mod, type, data)); 1501 #else 1502 ret = 0; 1503 1504 switch(type) { 1505 case MOD_LOAD: 1506 pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 1507 break; 1508 case MOD_UNLOAD: 1509 case MOD_SHUTDOWN: 1510 ret = sndstat_acquire(curthread); 1511 if (ret != 0) 1512 break; 1513 if (pcmsg_unrhdr != NULL) { 1514 delete_unrhdr(pcmsg_unrhdr); 1515 pcmsg_unrhdr = NULL; 1516 } 1517 break; 1518 default: 1519 ret = EOPNOTSUPP; 1520 } 1521 1522 return ret; 1523 #endif 1524 } 1525 1526 DEV_MODULE(sound, sound_modevent, NULL); 1527 MODULE_VERSION(sound, SOUND_MODVER); 1528