1 /*- 2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 3 * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) 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/vchan.h> 30 #include <dev/sound/pcm/dsp.h> 31 #include <sys/sysctl.h> 32 33 #include "feeder_if.h" 34 35 SND_DECLARE_FILE("$FreeBSD$"); 36 37 devclass_t pcm_devclass; 38 39 int pcm_veto_load = 1; 40 41 #ifdef USING_DEVFS 42 int snd_unit = 0; 43 TUNABLE_INT("hw.snd.unit", &snd_unit); 44 #endif 45 46 int snd_maxautovchans = 0; 47 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 48 49 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 50 51 static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 52 53 struct sysctl_ctx_list * 54 snd_sysctl_tree(device_t dev) 55 { 56 struct snddev_info *d = device_get_softc(dev); 57 58 return &d->sysctl_tree; 59 } 60 61 struct sysctl_oid * 62 snd_sysctl_tree_top(device_t dev) 63 { 64 struct snddev_info *d = device_get_softc(dev); 65 66 return d->sysctl_tree_top; 67 } 68 69 void * 70 snd_mtxcreate(const char *desc, const char *type) 71 { 72 #ifdef USING_MUTEX 73 struct mtx *m; 74 75 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 76 if (m == NULL) 77 return NULL; 78 mtx_init(m, desc, type, MTX_DEF); 79 return m; 80 #else 81 return (void *)0xcafebabe; 82 #endif 83 } 84 85 void 86 snd_mtxfree(void *m) 87 { 88 #ifdef USING_MUTEX 89 struct mtx *mtx = m; 90 91 /* mtx_assert(mtx, MA_OWNED); */ 92 mtx_destroy(mtx); 93 free(mtx, M_DEVBUF); 94 #endif 95 } 96 97 void 98 snd_mtxassert(void *m) 99 { 100 #ifdef USING_MUTEX 101 #ifdef INVARIANTS 102 struct mtx *mtx = m; 103 104 mtx_assert(mtx, MA_OWNED); 105 #endif 106 #endif 107 } 108 /* 109 void 110 snd_mtxlock(void *m) 111 { 112 #ifdef USING_MUTEX 113 struct mtx *mtx = m; 114 115 mtx_lock(mtx); 116 #endif 117 } 118 119 void 120 snd_mtxunlock(void *m) 121 { 122 #ifdef USING_MUTEX 123 struct mtx *mtx = m; 124 125 mtx_unlock(mtx); 126 #endif 127 } 128 */ 129 int 130 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 131 { 132 #ifdef USING_MUTEX 133 flags &= INTR_MPSAFE; 134 flags |= INTR_TYPE_AV; 135 #else 136 flags = INTR_TYPE_AV; 137 #endif 138 return bus_setup_intr(dev, res, flags, hand, param, cookiep); 139 } 140 141 #ifndef PCM_DEBUG_MTX 142 void 143 pcm_lock(struct snddev_info *d) 144 { 145 snd_mtxlock(d->lock); 146 } 147 148 void 149 pcm_unlock(struct snddev_info *d) 150 { 151 snd_mtxunlock(d->lock); 152 } 153 #endif 154 155 struct pcm_channel * 156 pcm_getfakechan(struct snddev_info *d) 157 { 158 return d->fakechan; 159 } 160 161 /* return a locked channel */ 162 struct pcm_channel * 163 pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum) 164 { 165 struct pcm_channel *c; 166 struct snddev_channel *sce; 167 int err; 168 169 /* scan for a free channel */ 170 SLIST_FOREACH(sce, &d->channels, link) { 171 c = sce->channel; 172 CHN_LOCK(c); 173 if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 174 if (chnum == -1 || c->num == chnum) { 175 c->flags |= CHN_F_BUSY; 176 c->pid = pid; 177 return c; 178 } 179 } 180 CHN_UNLOCK(c); 181 } 182 183 /* no channel available */ 184 if (direction == PCMDIR_PLAY && d->vchancount > 0 && 185 d->vchancount < snd_maxautovchans && 186 d->devcount <= PCMMAXCHAN) { 187 /* try to create a vchan */ 188 SLIST_FOREACH(sce, &d->channels, link) { 189 c = sce->channel; 190 CHN_LOCK(c); 191 if ((c->flags & CHN_F_HAS_VCHAN) && 192 !SLIST_EMPTY(&c->children)) { 193 err = vchan_create(c); 194 CHN_UNLOCK(c); 195 if (!err) 196 return pcm_chnalloc(d, direction, pid, -1); 197 else 198 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 199 } else 200 CHN_UNLOCK(c); 201 } 202 } 203 204 return NULL; 205 } 206 207 /* release a locked channel and unlock it */ 208 int 209 pcm_chnrelease(struct pcm_channel *c) 210 { 211 CHN_LOCKASSERT(c); 212 c->flags &= ~CHN_F_BUSY; 213 c->pid = -1; 214 CHN_UNLOCK(c); 215 return 0; 216 } 217 218 int 219 pcm_chnref(struct pcm_channel *c, int ref) 220 { 221 int r; 222 223 CHN_LOCKASSERT(c); 224 c->refcount += ref; 225 r = c->refcount; 226 return r; 227 } 228 229 int 230 pcm_inprog(struct snddev_info *d, int delta) 231 { 232 int r; 233 234 if (delta == 0) 235 return d->inprog; 236 237 /* backtrace(); */ 238 pcm_lock(d); 239 d->inprog += delta; 240 r = d->inprog; 241 pcm_unlock(d); 242 return r; 243 } 244 245 static void 246 pcm_setmaxautovchans(struct snddev_info *d, int num) 247 { 248 struct pcm_channel *c, *ch; 249 struct snddev_channel *sce; 250 int err, done; 251 252 /* 253 * XXX WOAH... NEED SUPER CLEANUP!!! 254 * Robust, yet confusing. Understanding these will 255 * cause your brain spinning like a Doki Doki Dynamo. 256 */ 257 if (num > 0 && d->vchancount == 0) { 258 SLIST_FOREACH(sce, &d->channels, link) { 259 c = sce->channel; 260 CHN_LOCK(c); 261 if ((c->direction == PCMDIR_PLAY) && 262 !(c->flags & CHN_F_BUSY) && 263 SLIST_EMPTY(&c->children)) { 264 c->flags |= CHN_F_BUSY; 265 err = vchan_create(c); 266 if (err) { 267 c->flags &= ~CHN_F_BUSY; 268 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 269 } 270 CHN_UNLOCK(c); 271 return; 272 } 273 CHN_UNLOCK(c); 274 } 275 return; 276 } 277 if (num == 0 && d->vchancount > 0) { 278 /* 279 * XXX Keep retrying... 280 */ 281 for (done = 0; done < 1024; done++) { 282 ch = NULL; 283 SLIST_FOREACH(sce, &d->channels, link) { 284 c = sce->channel; 285 CHN_LOCK(c); 286 if (c->direction == PCMDIR_PLAY && 287 !(c->flags & CHN_F_BUSY) && 288 (c->flags & CHN_F_VIRTUAL)) { 289 ch = c; 290 break; 291 } 292 CHN_UNLOCK(c); 293 } 294 if (ch != NULL) { 295 CHN_UNLOCK(ch); 296 snd_mtxlock(d->lock); 297 err = vchan_destroy(ch); 298 if (err) 299 device_printf(d->dev, "vchan_destroy(%s) == %d\n", 300 ch->name, err); 301 snd_mtxunlock(d->lock); 302 } else 303 return; 304 } 305 return; 306 } 307 } 308 309 #ifdef USING_DEVFS 310 static int 311 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 312 { 313 struct snddev_info *d; 314 int error, unit; 315 316 unit = snd_unit; 317 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 318 if (error == 0 && req->newptr != NULL) { 319 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 320 return EINVAL; 321 d = devclass_get_softc(pcm_devclass, unit); 322 if (d == NULL || SLIST_EMPTY(&d->channels)) 323 return EINVAL; 324 snd_unit = unit; 325 } 326 return (error); 327 } 328 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 329 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 330 #endif 331 332 static int 333 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 334 { 335 struct snddev_info *d; 336 int i, v, error; 337 338 v = snd_maxautovchans; 339 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 340 if (error == 0 && req->newptr != NULL) { 341 if (v < 0 || v > (PCMMAXCHAN + 1) || pcm_devclass == NULL) 342 return EINVAL; 343 if (v != snd_maxautovchans) { 344 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 345 d = devclass_get_softc(pcm_devclass, i); 346 if (!d) 347 continue; 348 if (d->flags & SD_F_AUTOVCHAN) { 349 if (pcm_inprog(d, 1) == 1) 350 pcm_setmaxautovchans(d, v); 351 pcm_inprog(d, -1); 352 } 353 } 354 } 355 snd_maxautovchans = v; 356 } 357 return (error); 358 } 359 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 360 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 361 362 struct pcm_channel * 363 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 364 { 365 struct pcm_channel *ch; 366 char *dirs; 367 int direction, err, *pnum; 368 369 switch(dir) { 370 case PCMDIR_PLAY: 371 dirs = "play"; 372 direction = PCMDIR_PLAY; 373 pnum = &d->playcount; 374 break; 375 376 case PCMDIR_REC: 377 dirs = "record"; 378 direction = PCMDIR_REC; 379 pnum = &d->reccount; 380 break; 381 382 case PCMDIR_VIRTUAL: 383 dirs = "virtual"; 384 direction = PCMDIR_PLAY; 385 pnum = &d->vchancount; 386 break; 387 388 default: 389 return NULL; 390 } 391 392 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 393 if (!ch) 394 return NULL; 395 396 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 397 if (!ch->methods) { 398 free(ch, M_DEVBUF); 399 400 return NULL; 401 } 402 403 snd_mtxlock(d->lock); 404 ch->num = (*pnum)++; 405 snd_mtxunlock(d->lock); 406 407 ch->pid = -1; 408 ch->parentsnddev = d; 409 ch->parentchannel = parent; 410 ch->dev = d->dev; 411 snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); 412 413 err = chn_init(ch, devinfo, dir, direction); 414 if (err) { 415 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 416 kobj_delete(ch->methods, M_DEVBUF); 417 free(ch, M_DEVBUF); 418 snd_mtxlock(d->lock); 419 (*pnum)--; 420 snd_mtxunlock(d->lock); 421 422 return NULL; 423 } 424 425 return ch; 426 } 427 428 int 429 pcm_chn_destroy(struct pcm_channel *ch) 430 { 431 struct snddev_info *d; 432 int err; 433 434 d = ch->parentsnddev; 435 err = chn_kill(ch); 436 if (err) { 437 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 438 return err; 439 } 440 441 kobj_delete(ch->methods, M_DEVBUF); 442 free(ch, M_DEVBUF); 443 444 return 0; 445 } 446 447 int 448 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 449 { 450 struct snddev_channel *sce, *tmp, *after; 451 int device = device_get_unit(d->dev); 452 453 /* 454 * Note it's confusing nomenclature. 455 * dev_t 456 * device -> pcm_device 457 * unit -> pcm_channel 458 * channel -> snddev_channel 459 * device_t 460 * unit -> pcm_device 461 */ 462 463 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 464 if (!sce) { 465 return ENOMEM; 466 } 467 468 snd_mtxlock(d->lock); 469 sce->channel = ch; 470 if (SLIST_EMPTY(&d->channels)) { 471 SLIST_INSERT_HEAD(&d->channels, sce, link); 472 sce->chan_num = 0; 473 } else { 474 sce->chan_num = 0; 475 retry_search: 476 SLIST_FOREACH(tmp, &d->channels, link) { 477 if (tmp == NULL) 478 continue; 479 if (sce->chan_num == tmp->chan_num) { 480 sce->chan_num++; 481 goto retry_search; 482 } 483 } 484 /* 485 * Don't overflow PCMMKMINOR / PCMMAXCHAN. 486 */ 487 if (sce->chan_num > PCMMAXCHAN) { 488 snd_mtxunlock(d->lock); 489 device_printf(d->dev, 490 "%s: WARNING: sce->chan_num overflow! (%d)\n", 491 __func__, sce->chan_num); 492 free(sce, M_DEVBUF); 493 return E2BIG; 494 } 495 /* 496 * Micro optimization, channel ordering: 497 * hw,hw,hw,vch,vch,vch,rec 498 */ 499 after = NULL; 500 if (ch->flags & CHN_F_VIRTUAL) { 501 /* virtual channel to the end */ 502 SLIST_FOREACH(tmp, &d->channels, link) { 503 if (tmp->channel->direction == PCMDIR_REC) 504 break; 505 after = tmp; 506 } 507 } else { 508 if (ch->direction == PCMDIR_REC) { 509 SLIST_FOREACH(tmp, &d->channels, link) { 510 after = tmp; 511 } 512 } else { 513 SLIST_FOREACH(tmp, &d->channels, link) { 514 if (tmp->channel->direction == PCMDIR_REC) 515 break; 516 if (!(tmp->channel->flags & CHN_F_VIRTUAL)) 517 after = tmp; 518 } 519 } 520 } 521 if (after == NULL) { 522 SLIST_INSERT_HEAD(&d->channels, sce, link); 523 } else { 524 SLIST_INSERT_AFTER(after, sce, link); 525 } 526 } 527 d->devcount++; 528 snd_mtxunlock(d->lock); 529 sce->dsp_devt= make_dev(&dsp_cdevsw, 530 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), 531 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", 532 device, sce->chan_num); 533 534 sce->dspW_devt= make_dev(&dsp_cdevsw, 535 PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num), 536 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", 537 device, sce->chan_num); 538 539 sce->audio_devt= make_dev(&dsp_cdevsw, 540 PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num), 541 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", 542 device, sce->chan_num); 543 544 if (ch->direction == PCMDIR_REC) 545 sce->dspr_devt = make_dev(&dsp_cdevsw, 546 PCMMKMINOR(device, SND_DEV_DSPREC, 547 sce->chan_num), UID_ROOT, GID_WHEEL, 548 0666, "dspr%d.%d", device, sce->chan_num); 549 550 return 0; 551 } 552 553 int 554 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 555 { 556 struct snddev_channel *sce; 557 #if 0 558 int ourlock; 559 560 ourlock = 0; 561 if (!mtx_owned(d->lock)) { 562 snd_mtxlock(d->lock); 563 ourlock = 1; 564 } 565 #endif 566 567 SLIST_FOREACH(sce, &d->channels, link) { 568 if (sce->channel == ch) 569 goto gotit; 570 } 571 #if 0 572 if (ourlock) 573 snd_mtxunlock(d->lock); 574 #endif 575 return EINVAL; 576 gotit: 577 SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 578 579 if (ch->flags & CHN_F_VIRTUAL) 580 d->vchancount--; 581 else if (ch->direction == PCMDIR_REC) 582 d->reccount--; 583 else 584 d->playcount--; 585 586 #if 0 587 if (ourlock) 588 snd_mtxunlock(d->lock); 589 #endif 590 free(sce, M_DEVBUF); 591 592 return 0; 593 } 594 595 int 596 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 597 { 598 struct snddev_info *d = device_get_softc(dev); 599 struct pcm_channel *ch; 600 int err; 601 602 ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 603 if (!ch) { 604 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 605 return ENODEV; 606 } 607 608 err = pcm_chn_add(d, ch); 609 if (err) { 610 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 611 pcm_chn_destroy(ch); 612 return err; 613 } 614 615 CHN_LOCK(ch); 616 if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) && 617 ch->direction == PCMDIR_PLAY && d->vchancount == 0) { 618 ch->flags |= CHN_F_BUSY; 619 err = vchan_create(ch); 620 if (err) { 621 ch->flags &= ~CHN_F_BUSY; 622 CHN_UNLOCK(ch); 623 device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 624 return err; 625 } 626 } 627 CHN_UNLOCK(ch); 628 629 return err; 630 } 631 632 static int 633 pcm_killchan(device_t dev) 634 { 635 struct snddev_info *d = device_get_softc(dev); 636 struct snddev_channel *sce; 637 struct pcm_channel *ch; 638 int error = 0; 639 640 sce = SLIST_FIRST(&d->channels); 641 ch = sce->channel; 642 643 error = pcm_chn_remove(d, sce->channel); 644 if (error) 645 return (error); 646 return (pcm_chn_destroy(ch)); 647 } 648 649 int 650 pcm_setstatus(device_t dev, char *str) 651 { 652 struct snddev_info *d = device_get_softc(dev); 653 654 snd_mtxlock(d->lock); 655 strncpy(d->status, str, SND_STATUSLEN); 656 snd_mtxunlock(d->lock); 657 return 0; 658 } 659 660 u_int32_t 661 pcm_getflags(device_t dev) 662 { 663 struct snddev_info *d = device_get_softc(dev); 664 665 return d->flags; 666 } 667 668 void 669 pcm_setflags(device_t dev, u_int32_t val) 670 { 671 struct snddev_info *d = device_get_softc(dev); 672 673 d->flags = val; 674 } 675 676 void * 677 pcm_getdevinfo(device_t dev) 678 { 679 struct snddev_info *d = device_get_softc(dev); 680 681 return d->devinfo; 682 } 683 684 unsigned int 685 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 686 { 687 struct snddev_info *d = device_get_softc(dev); 688 int sz, x; 689 690 sz = 0; 691 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 692 x = sz; 693 RANGE(sz, min, max); 694 if (x != sz) 695 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 696 x = min; 697 while (x < sz) 698 x <<= 1; 699 if (x > sz) 700 x >>= 1; 701 if (x != sz) { 702 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 703 sz = x; 704 } 705 } else { 706 sz = deflt; 707 } 708 709 d->bufsz = sz; 710 711 return sz; 712 } 713 714 int 715 pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 716 { 717 struct snddev_info *d = device_get_softc(dev); 718 719 if (pcm_veto_load) { 720 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 721 722 return EINVAL; 723 } 724 725 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 726 727 #if 0 728 /* 729 * d->flags should be cleared by the allocator of the softc. 730 * We cannot clear this field here because several devices set 731 * this flag before calling pcm_register(). 732 */ 733 d->flags = 0; 734 #endif 735 d->dev = dev; 736 d->devinfo = devinfo; 737 d->devcount = 0; 738 d->reccount = 0; 739 d->playcount = 0; 740 d->vchancount = 0; 741 d->inprog = 0; 742 743 SLIST_INIT(&d->channels); 744 745 if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 746 d->flags |= SD_F_SIMPLEX; 747 748 d->fakechan = fkchan_setup(dev); 749 chn_init(d->fakechan, NULL, 0, 0); 750 751 #ifdef SND_DYNSYSCTL 752 sysctl_ctx_init(&d->sysctl_tree); 753 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 754 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 755 device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 756 if (d->sysctl_tree_top == NULL) { 757 sysctl_ctx_free(&d->sysctl_tree); 758 goto no; 759 } 760 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 761 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 762 #endif 763 if (numplay > 0) { 764 vchan_initsys(dev); 765 d->flags |= SD_F_AUTOVCHAN; 766 } 767 768 sndstat_register(dev, d->status, sndstat_prepare_pcm); 769 return 0; 770 no: 771 snd_mtxfree(d->lock); 772 return ENXIO; 773 } 774 775 int 776 pcm_unregister(device_t dev) 777 { 778 struct snddev_info *d = device_get_softc(dev); 779 struct snddev_channel *sce; 780 struct pcm_channel *ch; 781 782 if (sndstat_acquire() != 0) { 783 device_printf(dev, "unregister: sndstat busy\n"); 784 return EBUSY; 785 } 786 787 snd_mtxlock(d->lock); 788 if (d->inprog) { 789 device_printf(dev, "unregister: operation in progress\n"); 790 snd_mtxunlock(d->lock); 791 sndstat_release(); 792 return EBUSY; 793 } 794 795 SLIST_FOREACH(sce, &d->channels, link) { 796 ch = sce->channel; 797 if (ch->refcount > 0) { 798 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 799 snd_mtxunlock(d->lock); 800 sndstat_release(); 801 return EBUSY; 802 } 803 } 804 805 if (mixer_uninit(dev)) { 806 device_printf(dev, "unregister: mixer busy\n"); 807 snd_mtxunlock(d->lock); 808 sndstat_release(); 809 return EBUSY; 810 } 811 812 SLIST_FOREACH(sce, &d->channels, link) { 813 if (sce->dsp_devt) { 814 destroy_dev(sce->dsp_devt); 815 sce->dsp_devt = NULL; 816 } 817 if (sce->dspW_devt) { 818 destroy_dev(sce->dspW_devt); 819 sce->dspW_devt = NULL; 820 } 821 if (sce->audio_devt) { 822 destroy_dev(sce->audio_devt); 823 sce->audio_devt = NULL; 824 } 825 if (sce->dspr_devt) { 826 destroy_dev(sce->dspr_devt); 827 sce->dspr_devt = NULL; 828 } 829 d->devcount--; 830 } 831 832 #ifdef SND_DYNSYSCTL 833 d->sysctl_tree_top = NULL; 834 sysctl_ctx_free(&d->sysctl_tree); 835 #endif 836 while (!SLIST_EMPTY(&d->channels)) 837 pcm_killchan(dev); 838 839 chn_kill(d->fakechan); 840 fkchan_kill(d->fakechan); 841 842 snd_mtxunlock(d->lock); 843 snd_mtxfree(d->lock); 844 sndstat_unregister(dev); 845 sndstat_release(); 846 return 0; 847 } 848 849 /************************************************************************/ 850 851 static int 852 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 853 { 854 struct snddev_info *d; 855 struct snddev_channel *sce; 856 struct pcm_channel *c; 857 struct pcm_feeder *f; 858 int pc, rc, vc; 859 860 if (verbose < 1) 861 return 0; 862 863 d = device_get_softc(dev); 864 if (!d) 865 return ENXIO; 866 867 snd_mtxlock(d->lock); 868 if (!SLIST_EMPTY(&d->channels)) { 869 pc = rc = vc = 0; 870 SLIST_FOREACH(sce, &d->channels, link) { 871 c = sce->channel; 872 if (c->direction == PCMDIR_PLAY) { 873 if (c->flags & CHN_F_VIRTUAL) 874 vc++; 875 else 876 pc++; 877 } else 878 rc++; 879 } 880 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 881 (d->flags & SD_F_SIMPLEX)? "" : " duplex", 882 #ifdef USING_DEVFS 883 (device_get_unit(dev) == snd_unit)? " default" : "" 884 #else 885 "" 886 #endif 887 ); 888 889 if (verbose <= 1) { 890 snd_mtxunlock(d->lock); 891 return 0; 892 } 893 894 SLIST_FOREACH(sce, &d->channels, link) { 895 c = sce->channel; 896 897 KASSERT(c->bufhard != NULL && c->bufsoft != NULL, 898 ("hosed pcm channel setup")); 899 900 sbuf_printf(s, "\n\t"); 901 902 /* it would be better to indent child channels */ 903 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 904 sbuf_printf(s, "spd %d", c->speed); 905 if (c->speed != sndbuf_getspd(c->bufhard)) 906 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 907 sbuf_printf(s, ", fmt 0x%08x", c->format); 908 if (c->format != sndbuf_getfmt(c->bufhard)) 909 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 910 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 911 if (c->pid != -1) 912 sbuf_printf(s, ", pid %d", c->pid); 913 sbuf_printf(s, "\n\t"); 914 915 sbuf_printf(s, "interrupts %d, ", c->interrupts); 916 if (c->direction == PCMDIR_REC) 917 sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 918 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), 919 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 920 sndbuf_getblkcnt(c->bufhard), 921 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 922 sndbuf_getblkcnt(c->bufsoft)); 923 else 924 sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", 925 c->xruns, sndbuf_getready(c->bufsoft), 926 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 927 sndbuf_getblkcnt(c->bufhard), 928 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 929 sndbuf_getblkcnt(c->bufsoft)); 930 sbuf_printf(s, "\n\t"); 931 932 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 933 sbuf_printf(s, " -> "); 934 f = c->feeder; 935 while (f->source != NULL) 936 f = f->source; 937 while (f != NULL) { 938 sbuf_printf(s, "%s", f->class->name); 939 if (f->desc->type == FEEDER_FMT) 940 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 941 if (f->desc->type == FEEDER_RATE) 942 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 943 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || 944 f->desc->type == FEEDER_VOLUME) 945 sbuf_printf(s, "(0x%08x)", f->desc->out); 946 sbuf_printf(s, " -> "); 947 f = f->parent; 948 } 949 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 950 } 951 } else 952 sbuf_printf(s, " (mixer only)"); 953 snd_mtxunlock(d->lock); 954 955 return 0; 956 } 957 958 /************************************************************************/ 959 960 #ifdef SND_DYNSYSCTL 961 int 962 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 963 { 964 struct snddev_info *d; 965 struct snddev_channel *sce; 966 struct pcm_channel *c; 967 int err, newcnt, cnt; 968 969 /* 970 * XXX WOAH... NEED SUPER CLEANUP!!! 971 * Robust, yet confusing. Understanding these will 972 * cause your brain spinning like a Doki Doki Dynamo. 973 */ 974 d = oidp->oid_arg1; 975 976 if (!(d->flags & SD_F_AUTOVCHAN)) { 977 pcm_inprog(d, -1); 978 return EINVAL; 979 } 980 981 cnt = 0; 982 SLIST_FOREACH(sce, &d->channels, link) { 983 c = sce->channel; 984 CHN_LOCK(c); 985 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) { 986 cnt++; 987 if (req->newptr != NULL && c->flags & CHN_F_BUSY) { 988 /* Better safe than sorry */ 989 CHN_UNLOCK(c); 990 return EBUSY; 991 } 992 } 993 CHN_UNLOCK(c); 994 } 995 996 newcnt = cnt; 997 998 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 999 1000 if (err == 0 && req->newptr != NULL) { 1001 1002 if (newcnt < 0 || newcnt > (PCMMAXCHAN + 1) || 1003 (d->playcount + d->reccount + newcnt) > (PCMMAXCHAN + 1)) 1004 return E2BIG; 1005 1006 if (pcm_inprog(d, 1) != 1) { 1007 pcm_inprog(d, -1); 1008 return EINPROGRESS; 1009 } 1010 1011 if (newcnt > cnt) { 1012 /* add new vchans - find a parent channel first */ 1013 SLIST_FOREACH(sce, &d->channels, link) { 1014 c = sce->channel; 1015 CHN_LOCK(c); 1016 /* not a candidate if not a play channel */ 1017 if (c->direction != PCMDIR_PLAY) 1018 goto next; 1019 /* not a candidate if a virtual channel */ 1020 if (c->flags & CHN_F_VIRTUAL) 1021 goto next; 1022 /* not a candidate if it's in use */ 1023 if (!(c->flags & CHN_F_BUSY) || 1024 !(SLIST_EMPTY(&c->children))) 1025 /* 1026 * if we get here we're a nonvirtual 1027 * play channel, and either 1028 * 1) not busy 1029 * 2) busy with children, not directly 1030 * open 1031 * 1032 * thus we can add children 1033 */ 1034 goto addok; 1035 next: 1036 CHN_UNLOCK(c); 1037 } 1038 pcm_inprog(d, -1); 1039 return EBUSY; 1040 addok: 1041 c->flags |= CHN_F_BUSY; 1042 while (err == 0 && newcnt > cnt) { 1043 if (d->devcount > PCMMAXCHAN) { 1044 device_printf(d->dev, "%s: Maximum channel reached.\n", __func__); 1045 break; 1046 } 1047 err = vchan_create(c); 1048 if (err == 0) 1049 cnt++; 1050 if (newcnt > cnt && err == E2BIG) { 1051 device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err); 1052 err = 0; 1053 break; 1054 } 1055 } 1056 CHN_UNLOCK(c); 1057 } else if (newcnt < cnt) { 1058 snd_mtxlock(d->lock); 1059 while (err == 0 && newcnt < cnt) { 1060 c = NULL; 1061 SLIST_FOREACH(sce, &d->channels, link) { 1062 CHN_LOCK(sce->channel); 1063 if (sce->channel->direction == PCMDIR_PLAY && 1064 (sce->channel->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 1065 c = sce->channel; 1066 CHN_UNLOCK(sce->channel); 1067 } 1068 if (c != NULL) 1069 goto remok; 1070 snd_mtxunlock(d->lock); 1071 pcm_inprog(d, -1); 1072 return EINVAL; 1073 remok: 1074 err = vchan_destroy(c); 1075 if (err == 0) 1076 cnt--; 1077 } 1078 snd_mtxunlock(d->lock); 1079 } 1080 pcm_inprog(d, -1); 1081 } 1082 return err; 1083 } 1084 #endif 1085 1086 /************************************************************************/ 1087 1088 static int 1089 sound_modevent(module_t mod, int type, void *data) 1090 { 1091 #if 0 1092 return (midi_modevent(mod, type, data)); 1093 #else 1094 return 0; 1095 #endif 1096 } 1097 1098 DEV_MODULE(sound, sound_modevent, NULL); 1099 MODULE_VERSION(sound, SOUND_MODVER); 1100