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