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