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) { 185 if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { 186 /* try to create a vchan */ 187 SLIST_FOREACH(sce, &d->channels, link) { 188 c = sce->channel; 189 CHN_LOCK(c); 190 if ((c->flags & CHN_F_HAS_VCHAN) && 191 !SLIST_EMPTY(&c->children)) { 192 err = vchan_create(c); 193 CHN_UNLOCK(c); 194 if (!err) 195 return pcm_chnalloc(d, direction, pid, -1); 196 else 197 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 198 } else 199 CHN_UNLOCK(c); 200 } 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 >= SND_MAXVCHANS || 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 sce->chan_num= d->devcount++; 471 if (SLIST_EMPTY(&d->channels)) { 472 SLIST_INSERT_HEAD(&d->channels, sce, link); 473 } else { 474 /* 475 * Micro optimization, channel ordering: 476 * hw,hw,hw,vch,vch,vch,rec 477 */ 478 after = NULL; 479 if (ch->flags & CHN_F_VIRTUAL) { 480 /* virtual channel to the end */ 481 SLIST_FOREACH(tmp, &d->channels, link) { 482 if (tmp->channel->direction == PCMDIR_REC) 483 break; 484 after = tmp; 485 } 486 } else { 487 if (ch->direction == PCMDIR_REC) { 488 SLIST_FOREACH(tmp, &d->channels, link) { 489 after = tmp; 490 } 491 } else { 492 SLIST_FOREACH(tmp, &d->channels, link) { 493 if (tmp->channel->direction == PCMDIR_REC) 494 break; 495 if (!(tmp->channel->flags & CHN_F_VIRTUAL)) 496 after = tmp; 497 } 498 } 499 } 500 if (after == NULL) { 501 SLIST_INSERT_HEAD(&d->channels, sce, link); 502 } else { 503 SLIST_INSERT_AFTER(after, sce, link); 504 } 505 } 506 snd_mtxunlock(d->lock); 507 sce->dsp_devt= make_dev(&dsp_cdevsw, 508 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), 509 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", 510 device, sce->chan_num); 511 512 sce->dspW_devt= make_dev(&dsp_cdevsw, 513 PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num), 514 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", 515 device, sce->chan_num); 516 517 sce->audio_devt= make_dev(&dsp_cdevsw, 518 PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num), 519 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", 520 device, sce->chan_num); 521 522 if (ch->direction == PCMDIR_REC) 523 sce->dspr_devt = make_dev(&dsp_cdevsw, 524 PCMMKMINOR(device, SND_DEV_DSPREC, 525 sce->chan_num), UID_ROOT, GID_WHEEL, 526 0666, "dspr%d.%d", device, sce->chan_num); 527 528 return 0; 529 } 530 531 int 532 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 533 { 534 struct snddev_channel *sce; 535 #if 0 536 int ourlock; 537 538 ourlock = 0; 539 if (!mtx_owned(d->lock)) { 540 snd_mtxlock(d->lock); 541 ourlock = 1; 542 } 543 #endif 544 545 SLIST_FOREACH(sce, &d->channels, link) { 546 if (sce->channel == ch) 547 goto gotit; 548 } 549 #if 0 550 if (ourlock) 551 snd_mtxunlock(d->lock); 552 #endif 553 return EINVAL; 554 gotit: 555 SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 556 557 if (ch->flags & CHN_F_VIRTUAL) 558 d->vchancount--; 559 else if (ch->direction == PCMDIR_REC) 560 d->reccount--; 561 else 562 d->playcount--; 563 564 #if 0 565 if (ourlock) 566 snd_mtxunlock(d->lock); 567 #endif 568 free(sce, M_DEVBUF); 569 570 return 0; 571 } 572 573 int 574 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 575 { 576 struct snddev_info *d = device_get_softc(dev); 577 struct pcm_channel *ch; 578 int err; 579 580 ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 581 if (!ch) { 582 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 583 return ENODEV; 584 } 585 586 err = pcm_chn_add(d, ch); 587 if (err) { 588 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 589 pcm_chn_destroy(ch); 590 return err; 591 } 592 593 CHN_LOCK(ch); 594 if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) && 595 ch->direction == PCMDIR_PLAY && d->vchancount == 0) { 596 ch->flags |= CHN_F_BUSY; 597 err = vchan_create(ch); 598 if (err) { 599 ch->flags &= ~CHN_F_BUSY; 600 CHN_UNLOCK(ch); 601 device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 602 return err; 603 } 604 } 605 CHN_UNLOCK(ch); 606 607 return err; 608 } 609 610 static int 611 pcm_killchan(device_t dev) 612 { 613 struct snddev_info *d = device_get_softc(dev); 614 struct snddev_channel *sce; 615 struct pcm_channel *ch; 616 int error = 0; 617 618 sce = SLIST_FIRST(&d->channels); 619 ch = sce->channel; 620 621 error = pcm_chn_remove(d, sce->channel); 622 if (error) 623 return (error); 624 return (pcm_chn_destroy(ch)); 625 } 626 627 int 628 pcm_setstatus(device_t dev, char *str) 629 { 630 struct snddev_info *d = device_get_softc(dev); 631 632 snd_mtxlock(d->lock); 633 strncpy(d->status, str, SND_STATUSLEN); 634 snd_mtxunlock(d->lock); 635 return 0; 636 } 637 638 u_int32_t 639 pcm_getflags(device_t dev) 640 { 641 struct snddev_info *d = device_get_softc(dev); 642 643 return d->flags; 644 } 645 646 void 647 pcm_setflags(device_t dev, u_int32_t val) 648 { 649 struct snddev_info *d = device_get_softc(dev); 650 651 d->flags = val; 652 } 653 654 void * 655 pcm_getdevinfo(device_t dev) 656 { 657 struct snddev_info *d = device_get_softc(dev); 658 659 return d->devinfo; 660 } 661 662 unsigned int 663 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 664 { 665 struct snddev_info *d = device_get_softc(dev); 666 int sz, x; 667 668 sz = 0; 669 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 670 x = sz; 671 RANGE(sz, min, max); 672 if (x != sz) 673 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 674 x = min; 675 while (x < sz) 676 x <<= 1; 677 if (x > sz) 678 x >>= 1; 679 if (x != sz) { 680 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 681 sz = x; 682 } 683 } else { 684 sz = deflt; 685 } 686 687 d->bufsz = sz; 688 689 return sz; 690 } 691 692 int 693 pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 694 { 695 struct snddev_info *d = device_get_softc(dev); 696 697 if (pcm_veto_load) { 698 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 699 700 return EINVAL; 701 } 702 703 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 704 705 #if 0 706 /* 707 * d->flags should be cleared by the allocator of the softc. 708 * We cannot clear this field here because several devices set 709 * this flag before calling pcm_register(). 710 */ 711 d->flags = 0; 712 #endif 713 d->dev = dev; 714 d->devinfo = devinfo; 715 d->devcount = 0; 716 d->reccount = 0; 717 d->playcount = 0; 718 d->vchancount = 0; 719 d->inprog = 0; 720 721 SLIST_INIT(&d->channels); 722 723 if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 724 d->flags |= SD_F_SIMPLEX; 725 726 d->fakechan = fkchan_setup(dev); 727 chn_init(d->fakechan, NULL, 0, 0); 728 729 #ifdef SND_DYNSYSCTL 730 sysctl_ctx_init(&d->sysctl_tree); 731 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 732 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 733 device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 734 if (d->sysctl_tree_top == NULL) { 735 sysctl_ctx_free(&d->sysctl_tree); 736 goto no; 737 } 738 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 739 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 740 #endif 741 if (numplay > 0) { 742 vchan_initsys(dev); 743 d->flags |= SD_F_AUTOVCHAN; 744 } 745 746 sndstat_register(dev, d->status, sndstat_prepare_pcm); 747 return 0; 748 no: 749 snd_mtxfree(d->lock); 750 return ENXIO; 751 } 752 753 int 754 pcm_unregister(device_t dev) 755 { 756 struct snddev_info *d = device_get_softc(dev); 757 struct snddev_channel *sce; 758 struct pcm_channel *ch; 759 760 if (sndstat_acquire() != 0) { 761 device_printf(dev, "unregister: sndstat busy\n"); 762 return EBUSY; 763 } 764 765 snd_mtxlock(d->lock); 766 if (d->inprog) { 767 device_printf(dev, "unregister: operation in progress\n"); 768 snd_mtxunlock(d->lock); 769 sndstat_release(); 770 return EBUSY; 771 } 772 773 SLIST_FOREACH(sce, &d->channels, link) { 774 ch = sce->channel; 775 if (ch->refcount > 0) { 776 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 777 snd_mtxunlock(d->lock); 778 sndstat_release(); 779 return EBUSY; 780 } 781 } 782 783 if (mixer_uninit(dev)) { 784 device_printf(dev, "unregister: mixer busy\n"); 785 snd_mtxunlock(d->lock); 786 sndstat_release(); 787 return EBUSY; 788 } 789 790 SLIST_FOREACH(sce, &d->channels, link) { 791 if (sce->dsp_devt) 792 destroy_dev(sce->dsp_devt); 793 if (sce->dspW_devt) 794 destroy_dev(sce->dspW_devt); 795 if (sce->audio_devt) 796 destroy_dev(sce->audio_devt); 797 if (sce->dspr_devt) 798 destroy_dev(sce->dspr_devt); 799 } 800 801 #ifdef SND_DYNSYSCTL 802 d->sysctl_tree_top = NULL; 803 sysctl_ctx_free(&d->sysctl_tree); 804 #endif 805 while (!SLIST_EMPTY(&d->channels)) 806 pcm_killchan(dev); 807 808 chn_kill(d->fakechan); 809 fkchan_kill(d->fakechan); 810 811 snd_mtxunlock(d->lock); 812 snd_mtxfree(d->lock); 813 sndstat_unregister(dev); 814 sndstat_release(); 815 return 0; 816 } 817 818 /************************************************************************/ 819 820 static int 821 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 822 { 823 struct snddev_info *d; 824 struct snddev_channel *sce; 825 struct pcm_channel *c; 826 struct pcm_feeder *f; 827 int pc, rc, vc; 828 829 if (verbose < 1) 830 return 0; 831 832 d = device_get_softc(dev); 833 if (!d) 834 return ENXIO; 835 836 snd_mtxlock(d->lock); 837 if (!SLIST_EMPTY(&d->channels)) { 838 pc = rc = vc = 0; 839 SLIST_FOREACH(sce, &d->channels, link) { 840 c = sce->channel; 841 if (c->direction == PCMDIR_PLAY) { 842 if (c->flags & CHN_F_VIRTUAL) 843 vc++; 844 else 845 pc++; 846 } else 847 rc++; 848 } 849 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 850 (d->flags & SD_F_SIMPLEX)? "" : " duplex", 851 #ifdef USING_DEVFS 852 (device_get_unit(dev) == snd_unit)? " default" : "" 853 #else 854 "" 855 #endif 856 ); 857 858 if (verbose <= 1) { 859 snd_mtxunlock(d->lock); 860 return 0; 861 } 862 863 SLIST_FOREACH(sce, &d->channels, link) { 864 c = sce->channel; 865 sbuf_printf(s, "\n\t"); 866 867 /* it would be better to indent child channels */ 868 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 869 sbuf_printf(s, "spd %d", c->speed); 870 if (c->speed != sndbuf_getspd(c->bufhard)) 871 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 872 sbuf_printf(s, ", fmt 0x%08x", c->format); 873 if (c->format != sndbuf_getfmt(c->bufhard)) 874 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 875 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 876 if (c->pid != -1) 877 sbuf_printf(s, ", pid %d", c->pid); 878 sbuf_printf(s, "\n\t"); 879 880 if (c->bufhard != NULL && c->bufsoft != NULL) { 881 sbuf_printf(s, "interrupts %d, ", c->interrupts); 882 if (c->direction == PCMDIR_REC) 883 sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 884 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), 885 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 886 sndbuf_getblkcnt(c->bufhard), 887 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 888 sndbuf_getblkcnt(c->bufsoft)); 889 else 890 sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", 891 c->xruns, sndbuf_getready(c->bufsoft), 892 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 893 sndbuf_getblkcnt(c->bufhard), 894 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 895 sndbuf_getblkcnt(c->bufsoft)); 896 sbuf_printf(s, "\n\t"); 897 } 898 899 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 900 sbuf_printf(s, " -> "); 901 f = c->feeder; 902 while (f->source != NULL) 903 f = f->source; 904 while (f != NULL) { 905 sbuf_printf(s, "%s", f->class->name); 906 if (f->desc->type == FEEDER_FMT) 907 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 908 if (f->desc->type == FEEDER_RATE) 909 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 910 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || 911 f->desc->type == FEEDER_VOLUME) 912 sbuf_printf(s, "(0x%08x)", f->desc->out); 913 sbuf_printf(s, " -> "); 914 f = f->parent; 915 } 916 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 917 } 918 } else 919 sbuf_printf(s, " (mixer only)"); 920 snd_mtxunlock(d->lock); 921 922 return 0; 923 } 924 925 /************************************************************************/ 926 927 #ifdef SND_DYNSYSCTL 928 int 929 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 930 { 931 struct snddev_info *d; 932 struct snddev_channel *sce; 933 struct pcm_channel *c; 934 int err, newcnt, cnt; 935 936 /* 937 * XXX WOAH... NEED SUPER CLEANUP!!! 938 * Robust, yet confusing. Understanding these will 939 * cause your brain spinning like a Doki Doki Dynamo. 940 */ 941 d = oidp->oid_arg1; 942 943 if (!(d->flags & SD_F_AUTOVCHAN)) { 944 pcm_inprog(d, -1); 945 return EINVAL; 946 } 947 948 cnt = 0; 949 SLIST_FOREACH(sce, &d->channels, link) { 950 c = sce->channel; 951 CHN_LOCK(c); 952 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) { 953 cnt++; 954 if (req->newptr != NULL && c->flags & CHN_F_BUSY) { 955 /* Better safe than sorry */ 956 CHN_UNLOCK(c); 957 return EBUSY; 958 } 959 } 960 CHN_UNLOCK(c); 961 } 962 963 newcnt = cnt; 964 965 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 966 967 if (err == 0 && req->newptr != NULL) { 968 969 if (newcnt < 0 || newcnt > SND_MAXVCHANS) 970 return E2BIG; 971 972 if (pcm_inprog(d, 1) != 1) { 973 pcm_inprog(d, -1); 974 return EINPROGRESS; 975 } 976 977 if (newcnt > cnt) { 978 /* add new vchans - find a parent channel first */ 979 SLIST_FOREACH(sce, &d->channels, link) { 980 c = sce->channel; 981 CHN_LOCK(c); 982 /* not a candidate if not a play channel */ 983 if (c->direction != PCMDIR_PLAY) 984 goto next; 985 /* not a candidate if a virtual channel */ 986 if (c->flags & CHN_F_VIRTUAL) 987 goto next; 988 /* not a candidate if it's in use */ 989 if (!(c->flags & CHN_F_BUSY) || 990 !(SLIST_EMPTY(&c->children))) 991 /* 992 * if we get here we're a nonvirtual 993 * play channel, and either 994 * 1) not busy 995 * 2) busy with children, not directly 996 * open 997 * 998 * thus we can add children 999 */ 1000 goto addok; 1001 next: 1002 CHN_UNLOCK(c); 1003 } 1004 pcm_inprog(d, -1); 1005 return EBUSY; 1006 addok: 1007 c->flags |= CHN_F_BUSY; 1008 while (err == 0 && newcnt > cnt) { 1009 err = vchan_create(c); 1010 if (err == 0) 1011 cnt++; 1012 } 1013 CHN_UNLOCK(c); 1014 } else if (newcnt < cnt) { 1015 snd_mtxlock(d->lock); 1016 while (err == 0 && newcnt < cnt) { 1017 SLIST_FOREACH(sce, &d->channels, link) { 1018 c = sce->channel; 1019 CHN_LOCK(c); 1020 if (c->direction == PCMDIR_PLAY && 1021 (c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 1022 goto remok; 1023 1024 CHN_UNLOCK(c); 1025 } 1026 snd_mtxunlock(d->lock); 1027 pcm_inprog(d, -1); 1028 return EINVAL; 1029 remok: 1030 CHN_UNLOCK(c); 1031 err = vchan_destroy(c); 1032 if (err == 0) 1033 cnt--; 1034 } 1035 snd_mtxunlock(d->lock); 1036 } 1037 pcm_inprog(d, -1); 1038 } 1039 return err; 1040 } 1041 #endif 1042 1043 /************************************************************************/ 1044 1045 static int 1046 sound_modevent(module_t mod, int type, void *data) 1047 { 1048 #if 0 1049 return (midi_modevent(mod, type, data)); 1050 #else 1051 return 0; 1052 #endif 1053 } 1054 1055 DEV_MODULE(sound, sound_modevent, NULL); 1056 MODULE_VERSION(sound, SOUND_MODVER); 1057