1 /* 2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 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 <sys/sysctl.h> 31 32 #include "feeder_if.h" 33 34 SND_DECLARE_FILE("$FreeBSD$"); 35 36 struct snddev_channel { 37 SLIST_ENTRY(snddev_channel) link; 38 struct pcm_channel *channel; 39 }; 40 41 struct snddev_info { 42 SLIST_HEAD(, snddev_channel) channels; 43 struct pcm_channel *fakechan; 44 unsigned devcount, playcount, reccount, vchancount; 45 unsigned flags; 46 int inprog; 47 unsigned int bufsz; 48 void *devinfo; 49 device_t dev; 50 char status[SND_STATUSLEN]; 51 struct sysctl_ctx_list sysctl_tree; 52 struct sysctl_oid *sysctl_tree_top; 53 void *lock; 54 }; 55 56 devclass_t pcm_devclass; 57 58 #ifdef USING_DEVFS 59 int snd_unit = 0; 60 TUNABLE_INT("hw.snd.unit", &snd_unit); 61 #endif 62 63 int snd_maxautovchans = 0; 64 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 65 66 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 67 68 static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 69 70 struct sysctl_ctx_list * 71 snd_sysctl_tree(device_t dev) 72 { 73 struct snddev_info *d = device_get_softc(dev); 74 75 return &d->sysctl_tree; 76 } 77 78 struct sysctl_oid * 79 snd_sysctl_tree_top(device_t dev) 80 { 81 struct snddev_info *d = device_get_softc(dev); 82 83 return d->sysctl_tree_top; 84 } 85 86 void * 87 snd_mtxcreate(const char *desc) 88 { 89 #ifdef USING_MUTEX 90 struct mtx *m; 91 92 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 93 if (m == NULL) 94 return NULL; 95 mtx_init(m, desc, MTX_RECURSE); 96 return m; 97 #else 98 return (void *)0xcafebabe; 99 #endif 100 } 101 102 void 103 snd_mtxfree(void *m) 104 { 105 #ifdef USING_MUTEX 106 struct mtx *mtx = m; 107 108 mtx_assert(mtx, MA_OWNED); 109 mtx_destroy(mtx); 110 free(mtx, M_DEVBUF); 111 #endif 112 } 113 114 void 115 snd_mtxassert(void *m) 116 { 117 #ifdef USING_MUTEX 118 #ifdef INVARIANTS 119 struct mtx *mtx = m; 120 121 mtx_assert(mtx, MA_OWNED); 122 #endif 123 #endif 124 } 125 126 void 127 snd_mtxlock(void *m) 128 { 129 #ifdef USING_MUTEX 130 struct mtx *mtx = m; 131 132 mtx_lock(mtx); 133 #endif 134 } 135 136 void 137 snd_mtxunlock(void *m) 138 { 139 #ifdef USING_MUTEX 140 struct mtx *mtx = m; 141 142 mtx_unlock(mtx); 143 #endif 144 } 145 146 int 147 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 148 { 149 #ifdef USING_MUTEX 150 flags &= INTR_MPSAFE; 151 flags |= INTR_TYPE_AV; 152 #else 153 flags = INTR_TYPE_AV; 154 #endif 155 return bus_setup_intr(dev, res, flags, hand, param, cookiep); 156 } 157 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 170 struct pcm_channel * 171 pcm_getfakechan(struct snddev_info *d) 172 { 173 return d->fakechan; 174 } 175 176 /* return a locked channel */ 177 struct pcm_channel * 178 pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum) 179 { 180 struct pcm_channel *c; 181 struct snddev_channel *sce; 182 int err; 183 184 snd_mtxassert(d->lock); 185 186 /* scan for a free channel */ 187 SLIST_FOREACH(sce, &d->channels, link) { 188 c = sce->channel; 189 CHN_LOCK(c); 190 if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 191 if (chnum == -1 || c->num == chnum) { 192 c->flags |= CHN_F_BUSY; 193 c->pid = pid; 194 return c; 195 } 196 } 197 CHN_UNLOCK(c); 198 } 199 200 /* no channel available */ 201 if (direction == PCMDIR_PLAY) { 202 if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { 203 /* try to create a vchan */ 204 SLIST_FOREACH(sce, &d->channels, link) { 205 c = sce->channel; 206 if (!SLIST_EMPTY(&c->children)) { 207 err = vchan_create(c); 208 if (!err) 209 return pcm_chnalloc(d, direction, pid, -1); 210 else 211 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 212 } 213 } 214 } 215 } 216 217 return NULL; 218 } 219 220 /* release a locked channel and unlock it */ 221 int 222 pcm_chnrelease(struct pcm_channel *c) 223 { 224 CHN_LOCKASSERT(c); 225 c->flags &= ~CHN_F_BUSY; 226 c->pid = -1; 227 CHN_UNLOCK(c); 228 return 0; 229 } 230 231 int 232 pcm_chnref(struct pcm_channel *c, int ref) 233 { 234 int r; 235 236 CHN_LOCKASSERT(c); 237 c->refcount += ref; 238 r = c->refcount; 239 return r; 240 } 241 242 int 243 pcm_inprog(struct snddev_info *d, int delta) 244 { 245 d->inprog += delta; 246 return d->inprog; 247 } 248 249 static void 250 pcm_setmaxautovchans(struct snddev_info *d, int num) 251 { 252 struct pcm_channel *c; 253 struct snddev_channel *sce; 254 int err, done; 255 256 if (num > 0 && d->vchancount == 0) { 257 SLIST_FOREACH(sce, &d->channels, link) { 258 c = sce->channel; 259 if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) { 260 c->flags |= CHN_F_BUSY; 261 err = vchan_create(c); 262 if (err) { 263 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 264 c->flags &= ~CHN_F_BUSY; 265 } 266 return; 267 } 268 } 269 } 270 if (num == 0 && d->vchancount > 0) { 271 done = 0; 272 while (!done) { 273 done = 1; 274 SLIST_FOREACH(sce, &d->channels, link) { 275 c = sce->channel; 276 if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) { 277 done = 0; 278 err = vchan_destroy(c); 279 if (err) 280 device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err); 281 goto restart; 282 } 283 } 284 restart: 285 } 286 } 287 } 288 289 #ifdef USING_DEVFS 290 static int 291 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 292 { 293 struct snddev_info *d; 294 int error, unit; 295 296 unit = snd_unit; 297 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 298 if (error == 0 && req->newptr != NULL) { 299 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 300 return EINVAL; 301 d = devclass_get_softc(pcm_devclass, unit); 302 if (d == NULL || SLIST_EMPTY(&d->channels)) 303 return EINVAL; 304 snd_unit = unit; 305 } 306 return (error); 307 } 308 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 309 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 310 #endif 311 312 static int 313 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 314 { 315 struct snddev_info *d; 316 int i, v, error; 317 318 v = snd_maxautovchans; 319 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 320 if (error == 0 && req->newptr != NULL) { 321 if (v < 0 || v >= SND_MAXVCHANS) 322 return EINVAL; 323 if (v != snd_maxautovchans) { 324 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 325 d = devclass_get_softc(pcm_devclass, i); 326 if (!d) 327 continue; 328 pcm_setmaxautovchans(d, v); 329 } 330 } 331 snd_maxautovchans = v; 332 } 333 return (error); 334 } 335 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 336 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 337 338 struct pcm_channel * 339 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 340 { 341 struct pcm_channel *ch; 342 char *dirs; 343 int err, *pnum; 344 345 switch(dir) { 346 case PCMDIR_PLAY: 347 dirs = "play"; 348 pnum = &d->playcount; 349 break; 350 351 case PCMDIR_REC: 352 dirs = "record"; 353 pnum = &d->reccount; 354 break; 355 356 case PCMDIR_VIRTUAL: 357 dirs = "virtual"; 358 dir = PCMDIR_PLAY; 359 pnum = &d->vchancount; 360 break; 361 362 default: 363 return NULL; 364 } 365 366 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 367 if (!ch) 368 return NULL; 369 370 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 371 if (!ch->methods) { 372 free(ch, M_DEVBUF); 373 374 return NULL; 375 } 376 377 ch->num = (*pnum)++; 378 379 ch->pid = -1; 380 ch->parentsnddev = d; 381 ch->parentchannel = parent; 382 snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num); 383 384 err = chn_init(ch, devinfo, dir); 385 if (err) { 386 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 387 kobj_delete(ch->methods, M_DEVBUF); 388 free(ch, M_DEVBUF); 389 (*pnum)--; 390 391 return NULL; 392 } 393 394 return ch; 395 } 396 397 int 398 pcm_chn_destroy(struct pcm_channel *ch) 399 { 400 struct snddev_info *d; 401 int err; 402 403 d = ch->parentsnddev; 404 err = chn_kill(ch); 405 if (err) { 406 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 407 return err; 408 } 409 410 if (ch->direction == PCMDIR_REC) 411 d->reccount--; 412 else if (ch->flags & CHN_F_VIRTUAL) 413 d->vchancount--; 414 else 415 d->playcount--; 416 417 kobj_delete(ch->methods, M_DEVBUF); 418 free(ch, M_DEVBUF); 419 420 return 0; 421 } 422 423 int 424 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 425 { 426 struct snddev_channel *sce, *tmp, *after; 427 int unit = device_get_unit(d->dev); 428 429 snd_mtxlock(d->lock); 430 431 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 432 if (!sce) { 433 snd_mtxunlock(d->lock); 434 return ENOMEM; 435 } 436 437 sce->channel = ch; 438 if (SLIST_EMPTY(&d->channels)) { 439 SLIST_INSERT_HEAD(&d->channels, sce, link); 440 } else { 441 after = NULL; 442 SLIST_FOREACH(tmp, &d->channels, link) { 443 after = tmp; 444 } 445 SLIST_INSERT_AFTER(after, sce, link); 446 } 447 448 if (mkdev) { 449 dsp_register(unit, d->devcount++); 450 if (ch->direction == PCMDIR_REC) 451 dsp_registerrec(unit, ch->num); 452 } 453 454 snd_mtxunlock(d->lock); 455 456 return 0; 457 } 458 459 int 460 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 461 { 462 struct snddev_channel *sce; 463 int unit = device_get_unit(d->dev); 464 465 snd_mtxlock(d->lock); 466 SLIST_FOREACH(sce, &d->channels, link) { 467 if (sce->channel == ch) 468 goto gotit; 469 } 470 snd_mtxunlock(d->lock); 471 return EINVAL; 472 gotit: 473 SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 474 free(sce, M_DEVBUF); 475 476 if (rmdev) { 477 dsp_unregister(unit, --d->devcount); 478 if (ch->direction == PCMDIR_REC) 479 dsp_unregisterrec(unit, ch->num); 480 } 481 snd_mtxunlock(d->lock); 482 483 return 0; 484 } 485 486 int 487 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 488 { 489 struct snddev_info *d = device_get_softc(dev); 490 struct pcm_channel *ch; 491 int err; 492 493 ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 494 if (!ch) { 495 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 496 return ENODEV; 497 } 498 499 err = pcm_chn_add(d, ch, 1); 500 if (err) { 501 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 502 pcm_chn_destroy(ch); 503 return err; 504 } 505 506 if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) { 507 ch->flags |= CHN_F_BUSY; 508 err = vchan_create(ch); 509 if (err) { 510 device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 511 ch->flags &= ~CHN_F_BUSY; 512 } 513 } 514 515 return err; 516 } 517 518 static int 519 pcm_killchan(device_t dev) 520 { 521 struct snddev_info *d = device_get_softc(dev); 522 struct snddev_channel *sce; 523 524 snd_mtxlock(d->lock); 525 sce = SLIST_FIRST(&d->channels); 526 snd_mtxunlock(d->lock); 527 528 return pcm_chn_remove(d, sce->channel, 1); 529 } 530 531 int 532 pcm_setstatus(device_t dev, char *str) 533 { 534 struct snddev_info *d = device_get_softc(dev); 535 536 snd_mtxlock(d->lock); 537 strncpy(d->status, str, SND_STATUSLEN); 538 snd_mtxunlock(d->lock); 539 return 0; 540 } 541 542 u_int32_t 543 pcm_getflags(device_t dev) 544 { 545 struct snddev_info *d = device_get_softc(dev); 546 547 return d->flags; 548 } 549 550 void 551 pcm_setflags(device_t dev, u_int32_t val) 552 { 553 struct snddev_info *d = device_get_softc(dev); 554 555 d->flags = val; 556 } 557 558 void * 559 pcm_getdevinfo(device_t dev) 560 { 561 struct snddev_info *d = device_get_softc(dev); 562 563 return d->devinfo; 564 } 565 566 unsigned int 567 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 568 { 569 struct snddev_info *d = device_get_softc(dev); 570 int sz; 571 572 sz = 0; 573 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) 574 RANGE(sz, min, max); 575 else 576 sz = deflt; 577 d->bufsz = sz; 578 579 return sz; 580 } 581 582 int 583 pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 584 { 585 struct snddev_info *d = device_get_softc(dev); 586 587 d->lock = snd_mtxcreate(device_get_nameunit(dev)); 588 snd_mtxlock(d->lock); 589 590 d->flags = 0; 591 d->dev = dev; 592 d->devinfo = devinfo; 593 d->devcount = 0; 594 d->reccount = 0; 595 d->playcount = 0; 596 d->vchancount = 0; 597 d->inprog = 0; 598 599 if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 600 d->flags |= SD_F_SIMPLEX; 601 602 d->fakechan = fkchan_setup(dev); 603 chn_init(d->fakechan, NULL, 0); 604 605 #ifdef SND_DYNSYSCTL 606 sysctl_ctx_init(&d->sysctl_tree); 607 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 608 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 609 device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 610 if (d->sysctl_tree_top == NULL) { 611 sysctl_ctx_free(&d->sysctl_tree); 612 goto no; 613 } 614 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 615 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 616 #endif 617 if (numplay > 0) 618 vchan_initsys(dev); 619 if (numplay == 1) 620 d->flags |= SD_F_AUTOVCHAN; 621 622 snd_mtxunlock(d->lock); 623 sndstat_register(dev, d->status, sndstat_prepare_pcm); 624 return 0; 625 no: 626 snd_mtxfree(d->lock); 627 return ENXIO; 628 } 629 630 int 631 pcm_unregister(device_t dev) 632 { 633 struct snddev_info *d = device_get_softc(dev); 634 struct snddev_channel *sce; 635 struct pcm_channel *ch; 636 637 snd_mtxlock(d->lock); 638 if (d->inprog) { 639 device_printf(dev, "unregister: operation in progress\n"); 640 snd_mtxunlock(d->lock); 641 return EBUSY; 642 } 643 if (sndstat_busy() != 0) { 644 device_printf(dev, "unregister: sndstat busy\n"); 645 snd_mtxunlock(d->lock); 646 return EBUSY; 647 } 648 SLIST_FOREACH(sce, &d->channels, link) { 649 ch = sce->channel; 650 if (ch->refcount > 0) { 651 device_printf(dev, "unregister: channel %s busy (pid %d)", ch->name, ch->pid); 652 snd_mtxunlock(d->lock); 653 return EBUSY; 654 } 655 } 656 if (mixer_uninit(dev)) { 657 device_printf(dev, "unregister: mixer busy\n"); 658 snd_mtxunlock(d->lock); 659 return EBUSY; 660 } 661 662 #ifdef SND_DYNSYSCTL 663 d->sysctl_tree_top = NULL; 664 sysctl_ctx_free(&d->sysctl_tree); 665 #endif 666 while (!SLIST_EMPTY(&d->channels)) 667 pcm_killchan(dev); 668 669 chn_kill(d->fakechan); 670 fkchan_kill(d->fakechan); 671 672 snd_mtxfree(d->lock); 673 return 0; 674 } 675 676 /************************************************************************/ 677 678 static int 679 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 680 { 681 struct snddev_info *d; 682 struct snddev_channel *sce; 683 struct pcm_channel *c; 684 struct pcm_feeder *f; 685 char *fsep; 686 int pc, rc, vc; 687 688 if (verbose < 1) 689 return 0; 690 691 d = device_get_softc(dev); 692 if (!d) 693 return ENXIO; 694 695 snd_mtxlock(d->lock); 696 if (!SLIST_EMPTY(&d->channels)) { 697 pc = rc = vc = 0; 698 SLIST_FOREACH(sce, &d->channels, link) { 699 c = sce->channel; 700 if (c->direction == PCMDIR_PLAY) { 701 if (c->flags & CHN_F_VIRTUAL) 702 vc++; 703 else 704 pc++; 705 } else 706 rc++; 707 } 708 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 709 (d->flags & SD_F_SIMPLEX)? "" : " duplex", 710 #ifdef USING_DEVFS 711 (device_get_unit(dev) == snd_unit)? " default" : "" 712 #else 713 "" 714 #endif 715 ); 716 if (verbose <= 1) 717 goto skipverbose; 718 SLIST_FOREACH(sce, &d->channels, link) { 719 c = sce->channel; 720 sbuf_printf(s, "\n\t"); 721 722 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 723 sbuf_printf(s, "speed %d, format %08x, flags %08x", c->speed, c->format, c->flags); 724 if (c->pid != -1) 725 sbuf_printf(s, ", pid %d", c->pid); 726 sbuf_printf(s, "\n\t"); 727 if (c->bufhard != NULL && c->bufsoft != NULL) { 728 sbuf_printf(s, "interrupts %d, ", c->interrupts); 729 if (c->direction == PCMDIR_REC) 730 sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 731 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 732 else 733 sbuf_printf(s, "underruns %d, ready %d", 734 c->xruns, sndbuf_getready(c->bufsoft)); 735 sbuf_printf(s, "\n\t"); 736 } 737 fsep = (c->direction == PCMDIR_REC)? " -> " : " <- "; 738 sbuf_printf(s, "{hardware}%s", fsep); 739 f = c->feeder; 740 while (f) { 741 sbuf_printf(s, "%s", f->class->name); 742 if (f->desc->type == FEEDER_FMT) 743 sbuf_printf(s, "(%08x%s%08x)", f->desc->out, fsep, f->desc->in); 744 if (f->desc->type == FEEDER_RATE) 745 sbuf_printf(s, "(%d%s%d)", FEEDER_GET(f, FEEDRATE_DST), fsep, FEEDER_GET(f, FEEDRATE_SRC)); 746 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 747 sbuf_printf(s, "(%08x)", f->desc->out); 748 sbuf_printf(s, "%s", fsep); 749 f = f->source; 750 } 751 sbuf_printf(s, "{userland}"); 752 } 753 skipverbose: 754 } else 755 sbuf_printf(s, " (mixer only)"); 756 snd_mtxunlock(d->lock); 757 758 return 0; 759 } 760 761 /************************************************************************/ 762 763 #ifdef SND_DYNSYSCTL 764 int 765 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 766 { 767 struct snddev_info *d; 768 struct snddev_channel *sce; 769 struct pcm_channel *c; 770 int err, oldcnt, newcnt, cnt; 771 772 d = oidp->oid_arg1; 773 774 pcm_lock(d); 775 cnt = 0; 776 SLIST_FOREACH(sce, &d->channels, link) { 777 c = sce->channel; 778 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) 779 cnt++; 780 } 781 oldcnt = cnt; 782 newcnt = cnt; 783 784 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 785 if (err == 0 && req->newptr != NULL) { 786 if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 787 pcm_unlock(d); 788 return EINVAL; 789 } 790 791 if (newcnt > cnt) { 792 /* add new vchans - find a parent channel first */ 793 SLIST_FOREACH(sce, &d->channels, link) { 794 c = sce->channel; 795 /* not a candidate if not a play channel */ 796 if (c->direction != PCMDIR_PLAY) 797 goto addskip; 798 /* not a candidate if a virtual channel */ 799 if (c->flags & CHN_F_VIRTUAL) 800 goto addskip; 801 /* not a candidate if it's in use */ 802 if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 803 goto addskip; 804 /* 805 * if we get here we're a nonvirtual play channel, and either 806 * 1) not busy 807 * 2) busy with children, not directly open 808 * 809 * thus we can add children 810 */ 811 goto addok; 812 addskip: 813 } 814 pcm_unlock(d); 815 return EBUSY; 816 addok: 817 c->flags |= CHN_F_BUSY; 818 while (err == 0 && newcnt > cnt) { 819 err = vchan_create(c); 820 if (err == 0) 821 cnt++; 822 } 823 if (SLIST_EMPTY(&c->children)) 824 c->flags &= ~CHN_F_BUSY; 825 } else if (newcnt < cnt) { 826 while (err == 0 && newcnt < cnt) { 827 SLIST_FOREACH(sce, &d->channels, link) { 828 c = sce->channel; 829 if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 830 goto remok; 831 } 832 pcm_unlock(d); 833 return EINVAL; 834 remok: 835 err = vchan_destroy(c); 836 if (err == 0) 837 cnt--; 838 } 839 } 840 } 841 842 pcm_unlock(d); 843 return err; 844 } 845 #endif 846 847 /************************************************************************/ 848 849 static moduledata_t sndpcm_mod = { 850 "snd_pcm", 851 NULL, 852 NULL 853 }; 854 DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 855 MODULE_VERSION(snd_pcm, PCM_MODVER); 856