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.2 2003/06/17 04:28:31 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.2 2003/06/17 04:28:31 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 goto restart; 287 } 288 } 289 restart: 290 } 291 } 292 } 293 294 #ifdef USING_DEVFS 295 static int 296 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 297 { 298 struct snddev_info *d; 299 int error, unit; 300 301 unit = snd_unit; 302 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 303 if (error == 0 && req->newptr != NULL) { 304 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 305 return EINVAL; 306 d = devclass_get_softc(pcm_devclass, unit); 307 if (d == NULL || SLIST_EMPTY(&d->channels)) 308 return EINVAL; 309 snd_unit = unit; 310 } 311 return (error); 312 } 313 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 314 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 315 #endif 316 317 static int 318 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 319 { 320 struct snddev_info *d; 321 int i, v, error; 322 323 v = snd_maxautovchans; 324 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 325 if (error == 0 && req->newptr != NULL) { 326 if (v < 0 || v >= SND_MAXVCHANS) 327 return EINVAL; 328 if (v != snd_maxautovchans) { 329 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 330 d = devclass_get_softc(pcm_devclass, i); 331 if (!d) 332 continue; 333 pcm_setmaxautovchans(d, v); 334 } 335 } 336 snd_maxautovchans = v; 337 } 338 return (error); 339 } 340 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 341 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 342 343 struct pcm_channel * 344 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 345 { 346 struct pcm_channel *ch; 347 char *dirs; 348 int err, *pnum; 349 350 switch(dir) { 351 case PCMDIR_PLAY: 352 dirs = "play"; 353 pnum = &d->playcount; 354 break; 355 356 case PCMDIR_REC: 357 dirs = "record"; 358 pnum = &d->reccount; 359 break; 360 361 case PCMDIR_VIRTUAL: 362 dirs = "virtual"; 363 dir = PCMDIR_PLAY; 364 pnum = &d->vchancount; 365 break; 366 367 default: 368 return NULL; 369 } 370 371 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 372 if (!ch) 373 return NULL; 374 375 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 376 if (!ch->methods) { 377 free(ch, M_DEVBUF); 378 379 return NULL; 380 } 381 382 ch->num = (*pnum)++; 383 384 ch->pid = -1; 385 ch->parentsnddev = d; 386 ch->parentchannel = parent; 387 ch->dev = d->dev; 388 snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num); 389 390 err = chn_init(ch, devinfo, dir); 391 if (err) { 392 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 393 kobj_delete(ch->methods, M_DEVBUF); 394 free(ch, M_DEVBUF); 395 (*pnum)--; 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 if (ch->direction == PCMDIR_REC) 417 d->reccount--; 418 else if (ch->flags & CHN_F_VIRTUAL) 419 d->vchancount--; 420 else 421 d->playcount--; 422 423 kobj_delete(ch->methods, M_DEVBUF); 424 free(ch, M_DEVBUF); 425 426 return 0; 427 } 428 429 int 430 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 431 { 432 struct snddev_channel *sce, *tmp, *after; 433 int unit = device_get_unit(d->dev); 434 435 snd_mtxlock(d->lock); 436 437 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 438 if (!sce) { 439 snd_mtxunlock(d->lock); 440 return ENOMEM; 441 } 442 443 sce->channel = ch; 444 if (SLIST_EMPTY(&d->channels)) { 445 SLIST_INSERT_HEAD(&d->channels, sce, link); 446 } else { 447 after = NULL; 448 SLIST_FOREACH(tmp, &d->channels, link) { 449 after = tmp; 450 } 451 SLIST_INSERT_AFTER(after, sce, link); 452 } 453 454 if (mkdev) { 455 dsp_register(unit, d->devcount++); 456 if (ch->direction == PCMDIR_REC) 457 dsp_registerrec(unit, ch->num); 458 } 459 460 snd_mtxunlock(d->lock); 461 462 return 0; 463 } 464 465 int 466 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 467 { 468 struct snddev_channel *sce; 469 int unit = device_get_unit(d->dev); 470 471 snd_mtxlock(d->lock); 472 SLIST_FOREACH(sce, &d->channels, link) { 473 if (sce->channel == ch) 474 goto gotit; 475 } 476 snd_mtxunlock(d->lock); 477 return EINVAL; 478 gotit: 479 SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 480 free(sce, M_DEVBUF); 481 482 if (rmdev) { 483 dsp_unregister(unit, --d->devcount); 484 if (ch->direction == PCMDIR_REC) 485 dsp_unregisterrec(unit, ch->num); 486 } 487 snd_mtxunlock(d->lock); 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 pcm_chn_destroy(ch); 509 return err; 510 } 511 512 if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) { 513 ch->flags |= CHN_F_BUSY; 514 err = vchan_create(ch); 515 if (err) { 516 device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 517 ch->flags &= ~CHN_F_BUSY; 518 } 519 } 520 521 return err; 522 } 523 524 static int 525 pcm_killchan(device_t dev) 526 { 527 struct snddev_info *d = device_get_softc(dev); 528 struct snddev_channel *sce; 529 struct pcm_channel *ch; 530 int error; 531 532 snd_mtxlock(d->lock); 533 sce = SLIST_FIRST(&d->channels); 534 snd_mtxunlock(d->lock); 535 ch = sce->channel; 536 537 error = pcm_chn_remove(d, sce->channel, 1); 538 if (error) 539 return (error); 540 return (pcm_chn_destroy(ch)); 541 } 542 543 int 544 pcm_setstatus(device_t dev, char *str) 545 { 546 struct snddev_info *d = device_get_softc(dev); 547 548 snd_mtxlock(d->lock); 549 strncpy(d->status, str, SND_STATUSLEN); 550 snd_mtxunlock(d->lock); 551 return 0; 552 } 553 554 u_int32_t 555 pcm_getflags(device_t dev) 556 { 557 struct snddev_info *d = device_get_softc(dev); 558 559 return d->flags; 560 } 561 562 void 563 pcm_setflags(device_t dev, u_int32_t val) 564 { 565 struct snddev_info *d = device_get_softc(dev); 566 567 d->flags = val; 568 } 569 570 void * 571 pcm_getdevinfo(device_t dev) 572 { 573 struct snddev_info *d = device_get_softc(dev); 574 575 return d->devinfo; 576 } 577 578 unsigned int 579 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 580 { 581 struct snddev_info *d = device_get_softc(dev); 582 int sz, x; 583 584 sz = 0; 585 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 586 x = sz; 587 RANGE(sz, min, max); 588 if (x != sz) 589 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 590 x = min; 591 while (x < sz) 592 x <<= 1; 593 if (x > sz) 594 x >>= 1; 595 if (x != sz) { 596 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 597 sz = x; 598 } 599 } else { 600 sz = deflt; 601 } 602 603 d->bufsz = sz; 604 605 return sz; 606 } 607 608 int 609 pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 610 { 611 struct snddev_info *d = device_get_softc(dev); 612 613 if (pcm_veto_load) { 614 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 615 616 return EINVAL; 617 } 618 619 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 620 snd_mtxlock(d->lock); 621 622 d->flags = 0; 623 d->dev = dev; 624 d->devinfo = devinfo; 625 d->devcount = 0; 626 d->reccount = 0; 627 d->playcount = 0; 628 d->vchancount = 0; 629 d->inprog = 0; 630 631 if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 632 d->flags |= SD_F_SIMPLEX; 633 634 d->fakechan = fkchan_setup(dev); 635 chn_init(d->fakechan, NULL, 0); 636 637 #ifdef SND_DYNSYSCTL 638 sysctl_ctx_init(&d->sysctl_tree); 639 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 640 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 641 device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 642 if (d->sysctl_tree_top == NULL) { 643 sysctl_ctx_free(&d->sysctl_tree); 644 goto no; 645 } 646 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 647 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 648 #endif 649 if (numplay > 0) 650 vchan_initsys(dev); 651 if (numplay == 1) 652 d->flags |= SD_F_AUTOVCHAN; 653 654 snd_mtxunlock(d->lock); 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 snd_mtxfree(d->lock); 705 return 0; 706 } 707 708 /************************************************************************/ 709 710 static int 711 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 712 { 713 struct snddev_info *d; 714 struct snddev_channel *sce; 715 struct pcm_channel *c; 716 struct pcm_feeder *f; 717 int pc, rc, vc; 718 719 if (verbose < 1) 720 return 0; 721 722 d = device_get_softc(dev); 723 if (!d) 724 return ENXIO; 725 726 snd_mtxlock(d->lock); 727 if (!SLIST_EMPTY(&d->channels)) { 728 pc = rc = vc = 0; 729 SLIST_FOREACH(sce, &d->channels, link) { 730 c = sce->channel; 731 if (c->direction == PCMDIR_PLAY) { 732 if (c->flags & CHN_F_VIRTUAL) 733 vc++; 734 else 735 pc++; 736 } else 737 rc++; 738 } 739 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 740 (d->flags & SD_F_SIMPLEX)? "" : " duplex", 741 #ifdef USING_DEVFS 742 (device_get_unit(dev) == snd_unit)? " default" : "" 743 #else 744 "" 745 #endif 746 ); 747 if (verbose <= 1) 748 goto skipverbose; 749 SLIST_FOREACH(sce, &d->channels, link) { 750 c = sce->channel; 751 sbuf_printf(s, "\n\t"); 752 753 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 754 sbuf_printf(s, "spd %d", c->speed); 755 if (c->speed != sndbuf_getspd(c->bufhard)) 756 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 757 sbuf_printf(s, ", fmt 0x%08x", c->format); 758 if (c->format != sndbuf_getfmt(c->bufhard)) 759 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 760 sbuf_printf(s, ", flags %08x", c->flags); 761 if (c->pid != -1) 762 sbuf_printf(s, ", pid %d", c->pid); 763 sbuf_printf(s, "\n\t"); 764 765 if (c->bufhard != NULL && c->bufsoft != NULL) { 766 sbuf_printf(s, "interrupts %d, ", c->interrupts); 767 if (c->direction == PCMDIR_REC) 768 sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 769 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 770 else 771 sbuf_printf(s, "underruns %d, ready %d", 772 c->xruns, sndbuf_getready(c->bufsoft)); 773 sbuf_printf(s, "\n\t"); 774 } 775 776 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 777 sbuf_printf(s, " -> "); 778 f = c->feeder; 779 while (f->source != NULL) 780 f = f->source; 781 while (f != NULL) { 782 sbuf_printf(s, "%s", f->class->name); 783 if (f->desc->type == FEEDER_FMT) 784 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 785 if (f->desc->type == FEEDER_RATE) 786 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 787 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 788 sbuf_printf(s, "(0x%08x)", f->desc->out); 789 sbuf_printf(s, " -> "); 790 f = f->parent; 791 } 792 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 793 } 794 skipverbose: 795 } else 796 sbuf_printf(s, " (mixer only)"); 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 goto addskip; 839 /* not a candidate if a virtual channel */ 840 if (c->flags & CHN_F_VIRTUAL) 841 goto addskip; 842 /* not a candidate if it's in use */ 843 if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 844 goto addskip; 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 addskip: 854 } 855 pcm_unlock(d); 856 return EBUSY; 857 addok: 858 c->flags |= CHN_F_BUSY; 859 while (err == 0 && newcnt > cnt) { 860 err = vchan_create(c); 861 if (err == 0) 862 cnt++; 863 } 864 if (SLIST_EMPTY(&c->children)) 865 c->flags &= ~CHN_F_BUSY; 866 } else if (newcnt < cnt) { 867 while (err == 0 && newcnt < cnt) { 868 SLIST_FOREACH(sce, &d->channels, link) { 869 c = sce->channel; 870 if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 871 goto remok; 872 } 873 pcm_unlock(d); 874 return EINVAL; 875 remok: 876 err = vchan_destroy(c); 877 if (err == 0) 878 cnt--; 879 } 880 } 881 } 882 883 pcm_unlock(d); 884 return err; 885 } 886 #endif 887 888 /************************************************************************/ 889 890 static moduledata_t sndpcm_mod = { 891 "snd_pcm", 892 NULL, 893 NULL 894 }; 895 DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 896 MODULE_VERSION(snd_pcm, PCM_MODVER); 897