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