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