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) 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, 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 goto restart; 284 } 285 } 286 restart: 287 } 288 } 289 } 290 291 #ifdef USING_DEVFS 292 static int 293 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 294 { 295 struct snddev_info *d; 296 int error, unit; 297 298 unit = snd_unit; 299 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 300 if (error == 0 && req->newptr != NULL) { 301 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 302 return EINVAL; 303 d = devclass_get_softc(pcm_devclass, unit); 304 if (d == NULL || SLIST_EMPTY(&d->channels)) 305 return EINVAL; 306 snd_unit = unit; 307 } 308 return (error); 309 } 310 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 311 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 312 #endif 313 314 static int 315 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 316 { 317 struct snddev_info *d; 318 int i, v, error; 319 320 v = snd_maxautovchans; 321 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 322 if (error == 0 && req->newptr != NULL) { 323 if (v < 0 || v >= SND_MAXVCHANS) 324 return EINVAL; 325 if (v != snd_maxautovchans) { 326 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 327 d = devclass_get_softc(pcm_devclass, i); 328 if (!d) 329 continue; 330 pcm_setmaxautovchans(d, v); 331 } 332 } 333 snd_maxautovchans = v; 334 } 335 return (error); 336 } 337 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 338 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 339 340 struct pcm_channel * 341 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 342 { 343 struct pcm_channel *ch; 344 char *dirs; 345 int err, *pnum; 346 347 switch(dir) { 348 case PCMDIR_PLAY: 349 dirs = "play"; 350 pnum = &d->playcount; 351 break; 352 353 case PCMDIR_REC: 354 dirs = "record"; 355 pnum = &d->reccount; 356 break; 357 358 case PCMDIR_VIRTUAL: 359 dirs = "virtual"; 360 dir = PCMDIR_PLAY; 361 pnum = &d->vchancount; 362 break; 363 364 default: 365 return NULL; 366 } 367 368 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 369 if (!ch) 370 return NULL; 371 372 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 373 if (!ch->methods) { 374 free(ch, M_DEVBUF); 375 376 return NULL; 377 } 378 379 ch->num = (*pnum)++; 380 381 ch->pid = -1; 382 ch->parentsnddev = d; 383 ch->parentchannel = parent; 384 ch->dev = d->dev; 385 snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num); 386 387 err = chn_init(ch, devinfo, dir); 388 if (err) { 389 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 390 kobj_delete(ch->methods, M_DEVBUF); 391 free(ch, M_DEVBUF); 392 (*pnum)--; 393 394 return NULL; 395 } 396 397 return ch; 398 } 399 400 int 401 pcm_chn_destroy(struct pcm_channel *ch) 402 { 403 struct snddev_info *d; 404 int err; 405 406 d = ch->parentsnddev; 407 err = chn_kill(ch); 408 if (err) { 409 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 410 return err; 411 } 412 413 if (ch->direction == PCMDIR_REC) 414 d->reccount--; 415 else if (ch->flags & CHN_F_VIRTUAL) 416 d->vchancount--; 417 else 418 d->playcount--; 419 420 kobj_delete(ch->methods, M_DEVBUF); 421 free(ch, M_DEVBUF); 422 423 return 0; 424 } 425 426 int 427 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 428 { 429 struct snddev_channel *sce, *tmp, *after; 430 int unit = device_get_unit(d->dev); 431 432 snd_mtxlock(d->lock); 433 434 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 435 if (!sce) { 436 snd_mtxunlock(d->lock); 437 return ENOMEM; 438 } 439 440 sce->channel = ch; 441 if (SLIST_EMPTY(&d->channels)) { 442 SLIST_INSERT_HEAD(&d->channels, sce, link); 443 } else { 444 after = NULL; 445 SLIST_FOREACH(tmp, &d->channels, link) { 446 after = tmp; 447 } 448 SLIST_INSERT_AFTER(after, sce, link); 449 } 450 451 if (mkdev) { 452 dsp_register(unit, d->devcount++); 453 if (ch->direction == PCMDIR_REC) 454 dsp_registerrec(unit, ch->num); 455 } 456 457 snd_mtxunlock(d->lock); 458 459 return 0; 460 } 461 462 int 463 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 464 { 465 struct snddev_channel *sce; 466 int unit = device_get_unit(d->dev); 467 468 snd_mtxlock(d->lock); 469 SLIST_FOREACH(sce, &d->channels, link) { 470 if (sce->channel == ch) 471 goto gotit; 472 } 473 snd_mtxunlock(d->lock); 474 return EINVAL; 475 gotit: 476 SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 477 free(sce, M_DEVBUF); 478 479 if (rmdev) { 480 dsp_unregister(unit, --d->devcount); 481 if (ch->direction == PCMDIR_REC) 482 dsp_unregisterrec(unit, ch->num); 483 } 484 snd_mtxunlock(d->lock); 485 486 return 0; 487 } 488 489 int 490 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 491 { 492 struct snddev_info *d = device_get_softc(dev); 493 struct pcm_channel *ch; 494 int err; 495 496 ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 497 if (!ch) { 498 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 499 return ENODEV; 500 } 501 502 err = pcm_chn_add(d, ch, 1); 503 if (err) { 504 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 505 pcm_chn_destroy(ch); 506 return err; 507 } 508 509 if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) { 510 ch->flags |= CHN_F_BUSY; 511 err = vchan_create(ch); 512 if (err) { 513 device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 514 ch->flags &= ~CHN_F_BUSY; 515 } 516 } 517 518 return err; 519 } 520 521 static int 522 pcm_killchan(device_t dev) 523 { 524 struct snddev_info *d = device_get_softc(dev); 525 struct snddev_channel *sce; 526 527 snd_mtxlock(d->lock); 528 sce = SLIST_FIRST(&d->channels); 529 snd_mtxunlock(d->lock); 530 531 return pcm_chn_remove(d, sce->channel, 1); 532 } 533 534 int 535 pcm_setstatus(device_t dev, char *str) 536 { 537 struct snddev_info *d = device_get_softc(dev); 538 539 snd_mtxlock(d->lock); 540 strncpy(d->status, str, SND_STATUSLEN); 541 snd_mtxunlock(d->lock); 542 return 0; 543 } 544 545 u_int32_t 546 pcm_getflags(device_t dev) 547 { 548 struct snddev_info *d = device_get_softc(dev); 549 550 return d->flags; 551 } 552 553 void 554 pcm_setflags(device_t dev, u_int32_t val) 555 { 556 struct snddev_info *d = device_get_softc(dev); 557 558 d->flags = val; 559 } 560 561 void * 562 pcm_getdevinfo(device_t dev) 563 { 564 struct snddev_info *d = device_get_softc(dev); 565 566 return d->devinfo; 567 } 568 569 unsigned int 570 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 571 { 572 struct snddev_info *d = device_get_softc(dev); 573 int sz, x; 574 575 sz = 0; 576 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 577 x = sz; 578 RANGE(sz, min, max); 579 if (x != sz) 580 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 581 x = min; 582 while (x < sz) 583 x <<= 1; 584 if (x > sz) 585 x >>= 1; 586 if (x != sz) { 587 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 588 sz = x; 589 } 590 } else { 591 sz = deflt; 592 } 593 594 d->bufsz = sz; 595 596 return sz; 597 } 598 599 int 600 pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 601 { 602 struct snddev_info *d = device_get_softc(dev); 603 604 if (pcm_veto_load) { 605 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 606 607 return EINVAL; 608 } 609 610 d->lock = snd_mtxcreate(device_get_nameunit(dev)); 611 snd_mtxlock(d->lock); 612 613 d->flags = 0; 614 d->dev = dev; 615 d->devinfo = devinfo; 616 d->devcount = 0; 617 d->reccount = 0; 618 d->playcount = 0; 619 d->vchancount = 0; 620 d->inprog = 0; 621 622 if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 623 d->flags |= SD_F_SIMPLEX; 624 625 d->fakechan = fkchan_setup(dev); 626 chn_init(d->fakechan, NULL, 0); 627 628 #ifdef SND_DYNSYSCTL 629 sysctl_ctx_init(&d->sysctl_tree); 630 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 631 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 632 device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 633 if (d->sysctl_tree_top == NULL) { 634 sysctl_ctx_free(&d->sysctl_tree); 635 goto no; 636 } 637 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 638 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 639 #endif 640 if (numplay > 0) 641 vchan_initsys(dev); 642 if (numplay == 1) 643 d->flags |= SD_F_AUTOVCHAN; 644 645 snd_mtxunlock(d->lock); 646 sndstat_register(dev, d->status, sndstat_prepare_pcm); 647 return 0; 648 no: 649 snd_mtxfree(d->lock); 650 return ENXIO; 651 } 652 653 int 654 pcm_unregister(device_t dev) 655 { 656 struct snddev_info *d = device_get_softc(dev); 657 struct snddev_channel *sce; 658 struct pcm_channel *ch; 659 660 snd_mtxlock(d->lock); 661 if (d->inprog) { 662 device_printf(dev, "unregister: operation in progress\n"); 663 snd_mtxunlock(d->lock); 664 return EBUSY; 665 } 666 if (sndstat_busy() != 0) { 667 device_printf(dev, "unregister: sndstat busy\n"); 668 snd_mtxunlock(d->lock); 669 return EBUSY; 670 } 671 SLIST_FOREACH(sce, &d->channels, link) { 672 ch = sce->channel; 673 if (ch->refcount > 0) { 674 device_printf(dev, "unregister: channel %s busy (pid %d)", ch->name, ch->pid); 675 snd_mtxunlock(d->lock); 676 return EBUSY; 677 } 678 } 679 if (mixer_uninit(dev)) { 680 device_printf(dev, "unregister: mixer busy\n"); 681 snd_mtxunlock(d->lock); 682 return EBUSY; 683 } 684 685 #ifdef SND_DYNSYSCTL 686 d->sysctl_tree_top = NULL; 687 sysctl_ctx_free(&d->sysctl_tree); 688 #endif 689 while (!SLIST_EMPTY(&d->channels)) 690 pcm_killchan(dev); 691 692 chn_kill(d->fakechan); 693 fkchan_kill(d->fakechan); 694 695 snd_mtxfree(d->lock); 696 return 0; 697 } 698 699 /************************************************************************/ 700 701 static int 702 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 703 { 704 struct snddev_info *d; 705 struct snddev_channel *sce; 706 struct pcm_channel *c; 707 struct pcm_feeder *f; 708 int pc, rc, vc; 709 710 if (verbose < 1) 711 return 0; 712 713 d = device_get_softc(dev); 714 if (!d) 715 return ENXIO; 716 717 snd_mtxlock(d->lock); 718 if (!SLIST_EMPTY(&d->channels)) { 719 pc = rc = vc = 0; 720 SLIST_FOREACH(sce, &d->channels, link) { 721 c = sce->channel; 722 if (c->direction == PCMDIR_PLAY) { 723 if (c->flags & CHN_F_VIRTUAL) 724 vc++; 725 else 726 pc++; 727 } else 728 rc++; 729 } 730 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 731 (d->flags & SD_F_SIMPLEX)? "" : " duplex", 732 #ifdef USING_DEVFS 733 (device_get_unit(dev) == snd_unit)? " default" : "" 734 #else 735 "" 736 #endif 737 ); 738 if (verbose <= 1) 739 goto skipverbose; 740 SLIST_FOREACH(sce, &d->channels, link) { 741 c = sce->channel; 742 sbuf_printf(s, "\n\t"); 743 744 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 745 sbuf_printf(s, "spd %d", c->speed); 746 if (c->speed != sndbuf_getspd(c->bufhard)) 747 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 748 sbuf_printf(s, ", fmt 0x%08x", c->format); 749 if (c->format != sndbuf_getfmt(c->bufhard)) 750 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 751 sbuf_printf(s, ", flags %08x", c->flags); 752 if (c->pid != -1) 753 sbuf_printf(s, ", pid %d", c->pid); 754 sbuf_printf(s, "\n\t"); 755 756 if (c->bufhard != NULL && c->bufsoft != NULL) { 757 sbuf_printf(s, "interrupts %d, ", c->interrupts); 758 if (c->direction == PCMDIR_REC) 759 sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 760 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 761 else 762 sbuf_printf(s, "underruns %d, ready %d", 763 c->xruns, sndbuf_getready(c->bufsoft)); 764 sbuf_printf(s, "\n\t"); 765 } 766 767 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 768 sbuf_printf(s, " -> "); 769 f = c->feeder; 770 while (f->source != NULL) 771 f = f->source; 772 while (f != NULL) { 773 sbuf_printf(s, "%s", f->class->name); 774 if (f->desc->type == FEEDER_FMT) 775 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 776 if (f->desc->type == FEEDER_RATE) 777 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 778 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 779 sbuf_printf(s, "(0x%08x)", f->desc->out); 780 sbuf_printf(s, " -> "); 781 f = f->parent; 782 } 783 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 784 } 785 skipverbose: 786 } else 787 sbuf_printf(s, " (mixer only)"); 788 snd_mtxunlock(d->lock); 789 790 return 0; 791 } 792 793 /************************************************************************/ 794 795 #ifdef SND_DYNSYSCTL 796 int 797 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 798 { 799 struct snddev_info *d; 800 struct snddev_channel *sce; 801 struct pcm_channel *c; 802 int err, oldcnt, newcnt, cnt; 803 804 d = oidp->oid_arg1; 805 806 pcm_lock(d); 807 cnt = 0; 808 SLIST_FOREACH(sce, &d->channels, link) { 809 c = sce->channel; 810 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) 811 cnt++; 812 } 813 oldcnt = cnt; 814 newcnt = cnt; 815 816 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 817 if (err == 0 && req->newptr != NULL) { 818 if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 819 pcm_unlock(d); 820 return EINVAL; 821 } 822 823 if (newcnt > cnt) { 824 /* add new vchans - find a parent channel first */ 825 SLIST_FOREACH(sce, &d->channels, link) { 826 c = sce->channel; 827 /* not a candidate if not a play channel */ 828 if (c->direction != PCMDIR_PLAY) 829 goto addskip; 830 /* not a candidate if a virtual channel */ 831 if (c->flags & CHN_F_VIRTUAL) 832 goto addskip; 833 /* not a candidate if it's in use */ 834 if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 835 goto addskip; 836 /* 837 * if we get here we're a nonvirtual play channel, and either 838 * 1) not busy 839 * 2) busy with children, not directly open 840 * 841 * thus we can add children 842 */ 843 goto addok; 844 addskip: 845 } 846 pcm_unlock(d); 847 return EBUSY; 848 addok: 849 c->flags |= CHN_F_BUSY; 850 while (err == 0 && newcnt > cnt) { 851 err = vchan_create(c); 852 if (err == 0) 853 cnt++; 854 } 855 if (SLIST_EMPTY(&c->children)) 856 c->flags &= ~CHN_F_BUSY; 857 } else if (newcnt < cnt) { 858 while (err == 0 && newcnt < cnt) { 859 SLIST_FOREACH(sce, &d->channels, link) { 860 c = sce->channel; 861 if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 862 goto remok; 863 } 864 pcm_unlock(d); 865 return EINVAL; 866 remok: 867 err = vchan_destroy(c); 868 if (err == 0) 869 cnt--; 870 } 871 } 872 } 873 874 pcm_unlock(d); 875 return err; 876 } 877 #endif 878 879 /************************************************************************/ 880 881 static moduledata_t sndpcm_mod = { 882 "snd_pcm", 883 NULL, 884 NULL 885 }; 886 DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 887 MODULE_VERSION(snd_pcm, PCM_MODVER); 888