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: src/sys/dev/sound/pcm/sound.c,v 1.17.2.14 2002/11/07 23:17:18 cognet Exp $"); 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 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 struct pcm_channel *ch; 527 int error; 528 529 snd_mtxlock(d->lock); 530 sce = SLIST_FIRST(&d->channels); 531 snd_mtxunlock(d->lock); 532 ch = sce->channel; 533 534 error = pcm_chn_remove(d, sce->channel, 1); 535 if (error) 536 return (error); 537 return (pcm_chn_destroy(ch)); 538 } 539 540 int 541 pcm_setstatus(device_t dev, char *str) 542 { 543 struct snddev_info *d = device_get_softc(dev); 544 545 snd_mtxlock(d->lock); 546 strncpy(d->status, str, SND_STATUSLEN); 547 snd_mtxunlock(d->lock); 548 return 0; 549 } 550 551 u_int32_t 552 pcm_getflags(device_t dev) 553 { 554 struct snddev_info *d = device_get_softc(dev); 555 556 return d->flags; 557 } 558 559 void 560 pcm_setflags(device_t dev, u_int32_t val) 561 { 562 struct snddev_info *d = device_get_softc(dev); 563 564 d->flags = val; 565 } 566 567 void * 568 pcm_getdevinfo(device_t dev) 569 { 570 struct snddev_info *d = device_get_softc(dev); 571 572 return d->devinfo; 573 } 574 575 unsigned int 576 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 577 { 578 struct snddev_info *d = device_get_softc(dev); 579 int sz, x; 580 581 sz = 0; 582 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 583 x = sz; 584 RANGE(sz, min, max); 585 if (x != sz) 586 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 587 x = min; 588 while (x < sz) 589 x <<= 1; 590 if (x > sz) 591 x >>= 1; 592 if (x != sz) { 593 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 594 sz = x; 595 } 596 } else { 597 sz = deflt; 598 } 599 600 d->bufsz = sz; 601 602 return sz; 603 } 604 605 int 606 pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 607 { 608 struct snddev_info *d = device_get_softc(dev); 609 610 if (pcm_veto_load) { 611 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 612 613 return EINVAL; 614 } 615 616 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 617 snd_mtxlock(d->lock); 618 619 d->flags = 0; 620 d->dev = dev; 621 d->devinfo = devinfo; 622 d->devcount = 0; 623 d->reccount = 0; 624 d->playcount = 0; 625 d->vchancount = 0; 626 d->inprog = 0; 627 628 if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 629 d->flags |= SD_F_SIMPLEX; 630 631 d->fakechan = fkchan_setup(dev); 632 chn_init(d->fakechan, NULL, 0); 633 634 #ifdef SND_DYNSYSCTL 635 sysctl_ctx_init(&d->sysctl_tree); 636 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 637 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 638 device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 639 if (d->sysctl_tree_top == NULL) { 640 sysctl_ctx_free(&d->sysctl_tree); 641 goto no; 642 } 643 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 644 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 645 #endif 646 if (numplay > 0) 647 vchan_initsys(dev); 648 if (numplay == 1) 649 d->flags |= SD_F_AUTOVCHAN; 650 651 snd_mtxunlock(d->lock); 652 sndstat_register(dev, d->status, sndstat_prepare_pcm); 653 return 0; 654 no: 655 snd_mtxfree(d->lock); 656 return ENXIO; 657 } 658 659 int 660 pcm_unregister(device_t dev) 661 { 662 struct snddev_info *d = device_get_softc(dev); 663 struct snddev_channel *sce; 664 struct pcm_channel *ch; 665 666 snd_mtxlock(d->lock); 667 if (d->inprog) { 668 device_printf(dev, "unregister: operation in progress\n"); 669 snd_mtxunlock(d->lock); 670 return EBUSY; 671 } 672 if (sndstat_busy() != 0) { 673 device_printf(dev, "unregister: sndstat busy\n"); 674 snd_mtxunlock(d->lock); 675 return EBUSY; 676 } 677 SLIST_FOREACH(sce, &d->channels, link) { 678 ch = sce->channel; 679 if (ch->refcount > 0) { 680 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 681 snd_mtxunlock(d->lock); 682 return EBUSY; 683 } 684 } 685 if (mixer_uninit(dev)) { 686 device_printf(dev, "unregister: mixer busy\n"); 687 snd_mtxunlock(d->lock); 688 return EBUSY; 689 } 690 691 #ifdef SND_DYNSYSCTL 692 d->sysctl_tree_top = NULL; 693 sysctl_ctx_free(&d->sysctl_tree); 694 #endif 695 while (!SLIST_EMPTY(&d->channels)) 696 pcm_killchan(dev); 697 698 chn_kill(d->fakechan); 699 fkchan_kill(d->fakechan); 700 701 snd_mtxfree(d->lock); 702 return 0; 703 } 704 705 /************************************************************************/ 706 707 static int 708 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 709 { 710 struct snddev_info *d; 711 struct snddev_channel *sce; 712 struct pcm_channel *c; 713 struct pcm_feeder *f; 714 int pc, rc, vc; 715 716 if (verbose < 1) 717 return 0; 718 719 d = device_get_softc(dev); 720 if (!d) 721 return ENXIO; 722 723 snd_mtxlock(d->lock); 724 if (!SLIST_EMPTY(&d->channels)) { 725 pc = rc = vc = 0; 726 SLIST_FOREACH(sce, &d->channels, link) { 727 c = sce->channel; 728 if (c->direction == PCMDIR_PLAY) { 729 if (c->flags & CHN_F_VIRTUAL) 730 vc++; 731 else 732 pc++; 733 } else 734 rc++; 735 } 736 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 737 (d->flags & SD_F_SIMPLEX)? "" : " duplex", 738 #ifdef USING_DEVFS 739 (device_get_unit(dev) == snd_unit)? " default" : "" 740 #else 741 "" 742 #endif 743 ); 744 if (verbose <= 1) 745 goto skipverbose; 746 SLIST_FOREACH(sce, &d->channels, link) { 747 c = sce->channel; 748 sbuf_printf(s, "\n\t"); 749 750 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 751 sbuf_printf(s, "spd %d", c->speed); 752 if (c->speed != sndbuf_getspd(c->bufhard)) 753 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 754 sbuf_printf(s, ", fmt 0x%08x", c->format); 755 if (c->format != sndbuf_getfmt(c->bufhard)) 756 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 757 sbuf_printf(s, ", flags %08x", c->flags); 758 if (c->pid != -1) 759 sbuf_printf(s, ", pid %d", c->pid); 760 sbuf_printf(s, "\n\t"); 761 762 if (c->bufhard != NULL && c->bufsoft != NULL) { 763 sbuf_printf(s, "interrupts %d, ", c->interrupts); 764 if (c->direction == PCMDIR_REC) 765 sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 766 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 767 else 768 sbuf_printf(s, "underruns %d, ready %d", 769 c->xruns, sndbuf_getready(c->bufsoft)); 770 sbuf_printf(s, "\n\t"); 771 } 772 773 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 774 sbuf_printf(s, " -> "); 775 f = c->feeder; 776 while (f->source != NULL) 777 f = f->source; 778 while (f != NULL) { 779 sbuf_printf(s, "%s", f->class->name); 780 if (f->desc->type == FEEDER_FMT) 781 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 782 if (f->desc->type == FEEDER_RATE) 783 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 784 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 785 sbuf_printf(s, "(0x%08x)", f->desc->out); 786 sbuf_printf(s, " -> "); 787 f = f->parent; 788 } 789 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 790 } 791 skipverbose: 792 } else 793 sbuf_printf(s, " (mixer only)"); 794 snd_mtxunlock(d->lock); 795 796 return 0; 797 } 798 799 /************************************************************************/ 800 801 #ifdef SND_DYNSYSCTL 802 int 803 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 804 { 805 struct snddev_info *d; 806 struct snddev_channel *sce; 807 struct pcm_channel *c; 808 int err, oldcnt, newcnt, cnt; 809 810 d = oidp->oid_arg1; 811 812 pcm_lock(d); 813 cnt = 0; 814 SLIST_FOREACH(sce, &d->channels, link) { 815 c = sce->channel; 816 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) 817 cnt++; 818 } 819 oldcnt = cnt; 820 newcnt = cnt; 821 822 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 823 if (err == 0 && req->newptr != NULL) { 824 if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 825 pcm_unlock(d); 826 return EINVAL; 827 } 828 829 if (newcnt > cnt) { 830 /* add new vchans - find a parent channel first */ 831 SLIST_FOREACH(sce, &d->channels, link) { 832 c = sce->channel; 833 /* not a candidate if not a play channel */ 834 if (c->direction != PCMDIR_PLAY) 835 goto addskip; 836 /* not a candidate if a virtual channel */ 837 if (c->flags & CHN_F_VIRTUAL) 838 goto addskip; 839 /* not a candidate if it's in use */ 840 if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 841 goto addskip; 842 /* 843 * if we get here we're a nonvirtual play channel, and either 844 * 1) not busy 845 * 2) busy with children, not directly open 846 * 847 * thus we can add children 848 */ 849 goto addok; 850 addskip: 851 } 852 pcm_unlock(d); 853 return EBUSY; 854 addok: 855 c->flags |= CHN_F_BUSY; 856 while (err == 0 && newcnt > cnt) { 857 err = vchan_create(c); 858 if (err == 0) 859 cnt++; 860 } 861 if (SLIST_EMPTY(&c->children)) 862 c->flags &= ~CHN_F_BUSY; 863 } else if (newcnt < cnt) { 864 while (err == 0 && newcnt < cnt) { 865 SLIST_FOREACH(sce, &d->channels, link) { 866 c = sce->channel; 867 if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 868 goto remok; 869 } 870 pcm_unlock(d); 871 return EINVAL; 872 remok: 873 err = vchan_destroy(c); 874 if (err == 0) 875 cnt--; 876 } 877 } 878 } 879 880 pcm_unlock(d); 881 return err; 882 } 883 #endif 884 885 /************************************************************************/ 886 887 static moduledata_t sndpcm_mod = { 888 "snd_pcm", 889 NULL, 890 NULL 891 }; 892 DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 893 MODULE_VERSION(snd_pcm, PCM_MODVER); 894