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