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