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