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