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