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 static int 162 pcm_setvchans(struct snddev_info *d, int newcnt) 163 { 164 struct snddev_channel *sce = NULL; 165 struct pcm_channel *c = NULL; 166 int err = 0, vcnt, dcnt, i; 167 168 pcm_inprog(d, 1); 169 170 if (!(d->flags & SD_F_AUTOVCHAN)) { 171 err = EINVAL; 172 goto setvchans_out; 173 } 174 175 vcnt = d->vchancount; 176 dcnt = d->playcount + d->reccount; 177 178 if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) { 179 err = E2BIG; 180 goto setvchans_out; 181 } 182 183 dcnt += vcnt; 184 185 if (newcnt > vcnt) { 186 /* add new vchans - find a parent channel first */ 187 SLIST_FOREACH(sce, &d->channels, link) { 188 c = sce->channel; 189 CHN_LOCK(c); 190 if (c->direction == PCMDIR_PLAY && 191 ((c->flags & CHN_F_HAS_VCHAN) || 192 (vcnt == 0 && 193 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) 194 goto addok; 195 CHN_UNLOCK(c); 196 } 197 err = EBUSY; 198 goto setvchans_out; 199 addok: 200 c->flags |= CHN_F_BUSY; 201 while (err == 0 && newcnt > vcnt) { 202 if (dcnt > PCMMAXCHAN) { 203 device_printf(d->dev, "%s: Maximum channel reached.\n", __func__); 204 break; 205 } 206 err = vchan_create(c); 207 if (err == 0) { 208 vcnt++; 209 dcnt++; 210 } else if (err == E2BIG && newcnt > vcnt) 211 device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err); 212 } 213 if (vcnt == 0) 214 c->flags &= ~CHN_F_BUSY; 215 CHN_UNLOCK(c); 216 } else if (newcnt < vcnt) { 217 #define ORPHAN_CDEVT(cdevt) \ 218 ((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \ 219 (cdevt)->si_drv2 == NULL)) 220 while (err == 0 && newcnt < vcnt) { 221 i = 0; 222 SLIST_FOREACH(sce, &d->channels, link) { 223 c = sce->channel; 224 CHN_LOCK(c); 225 if (c->direction == PCMDIR_PLAY && 226 (c->flags & CHN_F_VIRTUAL) && 227 (i++ == newcnt)) { 228 if (!(c->flags & CHN_F_BUSY) && 229 ORPHAN_CDEVT(sce->dsp_devt) && 230 ORPHAN_CDEVT(sce->dspW_devt) && 231 ORPHAN_CDEVT(sce->audio_devt) && 232 ORPHAN_CDEVT(sce->dspr_devt)) 233 goto remok; 234 /* 235 * Either we're busy, or our cdev 236 * has been stolen by dsp_clone(). 237 * Skip, and increase newcnt. 238 */ 239 if (!(c->flags & CHN_F_BUSY)) 240 device_printf(d->dev, 241 "%s: <%s> somebody steal my cdev!\n", 242 __func__, c->name); 243 newcnt++; 244 } 245 CHN_UNLOCK(c); 246 } 247 if (vcnt != newcnt) 248 err = EBUSY; 249 break; 250 remok: 251 CHN_UNLOCK(c); 252 err = vchan_destroy(c); 253 if (err == 0) 254 vcnt--; 255 else 256 device_printf(d->dev, 257 "%s: WARNING: vchan_destroy() failed!", 258 __func__); 259 } 260 } 261 262 setvchans_out: 263 pcm_inprog(d, -1); 264 return err; 265 } 266 267 /* return error status and a locked channel */ 268 int 269 pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 270 pid_t pid, int chnum) 271 { 272 struct pcm_channel *c; 273 struct snddev_channel *sce; 274 int err; 275 276 retry_chnalloc: 277 err = ENODEV; 278 /* scan for a free channel */ 279 SLIST_FOREACH(sce, &d->channels, link) { 280 c = sce->channel; 281 CHN_LOCK(c); 282 if (c->direction == direction && !(c->flags & CHN_F_BUSY)) { 283 if (chnum < 0 || sce->chan_num == chnum) { 284 c->flags |= CHN_F_BUSY; 285 c->pid = pid; 286 *ch = c; 287 return 0; 288 } 289 } 290 if (sce->chan_num == chnum) { 291 if (c->direction != direction) 292 err = EOPNOTSUPP; 293 else if (c->flags & CHN_F_BUSY) 294 err = EBUSY; 295 else 296 err = EINVAL; 297 CHN_UNLOCK(c); 298 return err; 299 } else if (c->direction == direction && (c->flags & CHN_F_BUSY)) 300 err = EBUSY; 301 CHN_UNLOCK(c); 302 } 303 304 /* no channel available */ 305 if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 && 306 d->vchancount < snd_maxautovchans && 307 d->devcount <= PCMMAXCHAN) { 308 err = pcm_setvchans(d, d->vchancount + 1); 309 if (err == 0) { 310 chnum = -2; 311 goto retry_chnalloc; 312 } 313 } 314 315 return err; 316 } 317 318 /* release a locked channel and unlock it */ 319 int 320 pcm_chnrelease(struct pcm_channel *c) 321 { 322 CHN_LOCKASSERT(c); 323 c->flags &= ~CHN_F_BUSY; 324 c->pid = -1; 325 CHN_UNLOCK(c); 326 return 0; 327 } 328 329 int 330 pcm_chnref(struct pcm_channel *c, int ref) 331 { 332 int r; 333 334 CHN_LOCKASSERT(c); 335 c->refcount += ref; 336 r = c->refcount; 337 return r; 338 } 339 340 int 341 pcm_inprog(struct snddev_info *d, int delta) 342 { 343 int r; 344 345 if (delta == 0) 346 return d->inprog; 347 348 /* backtrace(); */ 349 pcm_lock(d); 350 d->inprog += delta; 351 r = d->inprog; 352 pcm_unlock(d); 353 return r; 354 } 355 356 static void 357 pcm_setmaxautovchans(struct snddev_info *d, int num) 358 { 359 if (num > 0 && d->vchancount == 0) 360 pcm_setvchans(d, 1); 361 else if (num == 0 && d->vchancount > 0) 362 pcm_setvchans(d, 0); 363 } 364 365 #ifdef USING_DEVFS 366 static int 367 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 368 { 369 struct snddev_info *d; 370 int error, unit; 371 372 unit = snd_unit; 373 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 374 if (error == 0 && req->newptr != NULL) { 375 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 376 return EINVAL; 377 d = devclass_get_softc(pcm_devclass, unit); 378 if (d == NULL || SLIST_EMPTY(&d->channels)) 379 return EINVAL; 380 snd_unit = unit; 381 } 382 return (error); 383 } 384 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 385 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 386 #endif 387 388 static int 389 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 390 { 391 struct snddev_info *d; 392 int i, v, error; 393 394 v = snd_maxautovchans; 395 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 396 if (error == 0 && req->newptr != NULL) { 397 if (v < 0 || v > PCMMAXCHAN) 398 return E2BIG; 399 if (pcm_devclass != NULL && v != snd_maxautovchans) { 400 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 401 d = devclass_get_softc(pcm_devclass, i); 402 if (!d) 403 continue; 404 pcm_setmaxautovchans(d, v); 405 } 406 } 407 snd_maxautovchans = v; 408 } 409 return (error); 410 } 411 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 412 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 413 414 struct pcm_channel * 415 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 416 { 417 struct snddev_channel *sce; 418 struct pcm_channel *ch, *c; 419 char *dirs; 420 uint32_t flsearch = 0; 421 int direction, err, rpnum, *pnum; 422 423 switch(dir) { 424 case PCMDIR_PLAY: 425 dirs = "play"; 426 direction = PCMDIR_PLAY; 427 pnum = &d->playcount; 428 break; 429 430 case PCMDIR_REC: 431 dirs = "record"; 432 direction = PCMDIR_REC; 433 pnum = &d->reccount; 434 break; 435 436 case PCMDIR_VIRTUAL: 437 dirs = "virtual"; 438 direction = PCMDIR_PLAY; 439 pnum = &d->vchancount; 440 flsearch = CHN_F_VIRTUAL; 441 break; 442 443 default: 444 return NULL; 445 } 446 447 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 448 if (!ch) 449 return NULL; 450 451 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 452 if (!ch->methods) { 453 free(ch, M_DEVBUF); 454 455 return NULL; 456 } 457 458 snd_mtxlock(d->lock); 459 ch->num = 0; 460 rpnum = 0; 461 SLIST_FOREACH(sce, &d->channels, link) { 462 c = sce->channel; 463 if (direction != c->direction || 464 (c->flags & CHN_F_VIRTUAL) != flsearch) 465 continue; 466 if (ch->num == c->num) 467 ch->num++; 468 else { 469 #if 0 470 device_printf(d->dev, 471 "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n", 472 __func__, dirs, ch->num, c->num); 473 #endif 474 goto retry_num_search; 475 } 476 rpnum++; 477 } 478 goto retry_num_search_out; 479 retry_num_search: 480 rpnum = 0; 481 SLIST_FOREACH(sce, &d->channels, link) { 482 c = sce->channel; 483 if (direction != c->direction || 484 (c->flags & CHN_F_VIRTUAL) != flsearch) 485 continue; 486 if (ch->num == c->num) { 487 ch->num++; 488 goto retry_num_search; 489 } 490 rpnum++; 491 } 492 retry_num_search_out: 493 if (*pnum != rpnum) { 494 device_printf(d->dev, 495 "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n", 496 __func__, dirs, *pnum, rpnum); 497 *pnum = rpnum; 498 } 499 (*pnum)++; 500 snd_mtxunlock(d->lock); 501 502 ch->pid = -1; 503 ch->parentsnddev = d; 504 ch->parentchannel = parent; 505 ch->dev = d->dev; 506 snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); 507 508 err = chn_init(ch, devinfo, dir, direction); 509 if (err) { 510 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 511 kobj_delete(ch->methods, M_DEVBUF); 512 free(ch, M_DEVBUF); 513 snd_mtxlock(d->lock); 514 (*pnum)--; 515 snd_mtxunlock(d->lock); 516 517 return NULL; 518 } 519 520 return ch; 521 } 522 523 int 524 pcm_chn_destroy(struct pcm_channel *ch) 525 { 526 struct snddev_info *d; 527 int err; 528 529 d = ch->parentsnddev; 530 err = chn_kill(ch); 531 if (err) { 532 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 533 return err; 534 } 535 536 kobj_delete(ch->methods, M_DEVBUF); 537 free(ch, M_DEVBUF); 538 539 return 0; 540 } 541 542 int 543 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 544 { 545 struct snddev_channel *sce, *tmp, *after; 546 unsigned rdevcount; 547 int device = device_get_unit(d->dev); 548 size_t namelen; 549 550 /* 551 * Note it's confusing nomenclature. 552 * dev_t 553 * device -> pcm_device 554 * unit -> pcm_channel 555 * channel -> snddev_channel 556 * device_t 557 * unit -> pcm_device 558 */ 559 560 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 561 if (!sce) { 562 return ENOMEM; 563 } 564 565 snd_mtxlock(d->lock); 566 sce->channel = ch; 567 sce->chan_num = 0; 568 rdevcount = 0; 569 after = NULL; 570 SLIST_FOREACH(tmp, &d->channels, link) { 571 if (sce->chan_num == tmp->chan_num) 572 sce->chan_num++; 573 else { 574 #if 0 575 device_printf(d->dev, 576 "%s: cdev numbering screwed (Expect: %d, Got: %d)\n", 577 __func__, sce->chan_num, tmp->chan_num); 578 #endif 579 goto retry_chan_num_search; 580 } 581 after = tmp; 582 rdevcount++; 583 } 584 goto retry_chan_num_search_out; 585 retry_chan_num_search: 586 /* 587 * Look for possible channel numbering collision. This may not 588 * be optimized, but it will ensure that no collision occured. 589 * Can be considered cheap since none of the locking/unlocking 590 * operations involved. 591 */ 592 rdevcount = 0; 593 after = NULL; 594 SLIST_FOREACH(tmp, &d->channels, link) { 595 if (sce->chan_num == tmp->chan_num) { 596 sce->chan_num++; 597 goto retry_chan_num_search; 598 } 599 if (sce->chan_num > tmp->chan_num) 600 after = tmp; 601 rdevcount++; 602 } 603 retry_chan_num_search_out: 604 /* 605 * Don't overflow PCMMKMINOR / PCMMAXCHAN. 606 */ 607 if (sce->chan_num > PCMMAXCHAN) { 608 snd_mtxunlock(d->lock); 609 device_printf(d->dev, 610 "%s: WARNING: sce->chan_num overflow! (%d)\n", 611 __func__, sce->chan_num); 612 free(sce, M_DEVBUF); 613 return E2BIG; 614 } 615 if (d->devcount != rdevcount) { 616 device_printf(d->dev, 617 "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n", 618 __func__, d->devcount, rdevcount); 619 d->devcount = rdevcount; 620 } 621 d->devcount++; 622 if (after == NULL) { 623 SLIST_INSERT_HEAD(&d->channels, sce, link); 624 } else { 625 SLIST_INSERT_AFTER(after, sce, link); 626 } 627 #if 0 628 if (1) { 629 int cnum = 0; 630 SLIST_FOREACH(tmp, &d->channels, link) { 631 if (cnum != tmp->chan_num) 632 device_printf(d->dev, 633 "%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n", 634 __func__, cnum, tmp->chan_num); 635 cnum++; 636 } 637 } 638 #endif 639 640 namelen = strlen(ch->name); 641 if ((CHN_NAMELEN - namelen) > 10) { /* ":dspXX.YYY" */ 642 snprintf(ch->name + namelen, 643 CHN_NAMELEN - namelen, ":dsp%d.%d", 644 device, sce->chan_num); 645 } 646 snd_mtxunlock(d->lock); 647 648 /* 649 * I will revisit these someday, and nuke it mercilessly.. 650 */ 651 sce->dsp_devt = make_dev(&dsp_cdevsw, 652 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), 653 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", 654 device, sce->chan_num); 655 656 sce->dspW_devt = make_dev(&dsp_cdevsw, 657 PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num), 658 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", 659 device, sce->chan_num); 660 661 sce->audio_devt = make_dev(&dsp_cdevsw, 662 PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num), 663 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", 664 device, sce->chan_num); 665 666 if (ch->direction == PCMDIR_REC) 667 sce->dspr_devt = make_dev(&dsp_cdevsw, 668 PCMMKMINOR(device, SND_DEV_DSPREC, 669 sce->chan_num), UID_ROOT, GID_WHEEL, 670 0666, "dspr%d.%d", device, sce->chan_num); 671 672 return 0; 673 } 674 675 int 676 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 677 { 678 struct snddev_channel *sce; 679 #if 0 680 int ourlock; 681 682 ourlock = 0; 683 if (!mtx_owned(d->lock)) { 684 snd_mtxlock(d->lock); 685 ourlock = 1; 686 } 687 #endif 688 689 SLIST_FOREACH(sce, &d->channels, link) { 690 if (sce->channel == ch) 691 goto gotit; 692 } 693 #if 0 694 if (ourlock) 695 snd_mtxunlock(d->lock); 696 #endif 697 return EINVAL; 698 gotit: 699 SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 700 701 if (ch->flags & CHN_F_VIRTUAL) 702 d->vchancount--; 703 else if (ch->direction == PCMDIR_REC) 704 d->reccount--; 705 else 706 d->playcount--; 707 708 #if 0 709 if (ourlock) 710 snd_mtxunlock(d->lock); 711 #endif 712 free(sce, M_DEVBUF); 713 714 return 0; 715 } 716 717 int 718 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 719 { 720 struct snddev_info *d = device_get_softc(dev); 721 struct pcm_channel *ch; 722 int err; 723 724 ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 725 if (!ch) { 726 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 727 return ENODEV; 728 } 729 730 err = pcm_chn_add(d, ch); 731 if (err) { 732 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 733 pcm_chn_destroy(ch); 734 return err; 735 } 736 737 return err; 738 } 739 740 static int 741 pcm_killchan(device_t dev) 742 { 743 struct snddev_info *d = device_get_softc(dev); 744 struct snddev_channel *sce; 745 struct pcm_channel *ch; 746 int error = 0; 747 748 sce = SLIST_FIRST(&d->channels); 749 ch = sce->channel; 750 751 error = pcm_chn_remove(d, sce->channel); 752 if (error) 753 return (error); 754 return (pcm_chn_destroy(ch)); 755 } 756 757 int 758 pcm_setstatus(device_t dev, char *str) 759 { 760 struct snddev_info *d = device_get_softc(dev); 761 762 snd_mtxlock(d->lock); 763 strncpy(d->status, str, SND_STATUSLEN); 764 snd_mtxunlock(d->lock); 765 if (snd_maxautovchans > 0) 766 pcm_setvchans(d, 1); 767 return 0; 768 } 769 770 uint32_t 771 pcm_getflags(device_t dev) 772 { 773 struct snddev_info *d = device_get_softc(dev); 774 775 return d->flags; 776 } 777 778 void 779 pcm_setflags(device_t dev, uint32_t val) 780 { 781 struct snddev_info *d = device_get_softc(dev); 782 783 d->flags = val; 784 } 785 786 void * 787 pcm_getdevinfo(device_t dev) 788 { 789 struct snddev_info *d = device_get_softc(dev); 790 791 return d->devinfo; 792 } 793 794 unsigned int 795 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 796 { 797 struct snddev_info *d = device_get_softc(dev); 798 int sz, x; 799 800 sz = 0; 801 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 802 x = sz; 803 RANGE(sz, min, max); 804 if (x != sz) 805 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 806 x = min; 807 while (x < sz) 808 x <<= 1; 809 if (x > sz) 810 x >>= 1; 811 if (x != sz) { 812 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 813 sz = x; 814 } 815 } else { 816 sz = deflt; 817 } 818 819 d->bufsz = sz; 820 821 return sz; 822 } 823 824 int 825 pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 826 { 827 struct snddev_info *d = device_get_softc(dev); 828 829 if (pcm_veto_load) { 830 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 831 832 return EINVAL; 833 } 834 835 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 836 837 #if 0 838 /* 839 * d->flags should be cleared by the allocator of the softc. 840 * We cannot clear this field here because several devices set 841 * this flag before calling pcm_register(). 842 */ 843 d->flags = 0; 844 #endif 845 d->dev = dev; 846 d->devinfo = devinfo; 847 d->devcount = 0; 848 d->reccount = 0; 849 d->playcount = 0; 850 d->vchancount = 0; 851 d->inprog = 0; 852 853 SLIST_INIT(&d->channels); 854 855 if ((numplay == 0 || numrec == 0) && numplay != numrec) 856 d->flags |= SD_F_SIMPLEX; 857 858 d->fakechan = fkchan_setup(dev); 859 chn_init(d->fakechan, NULL, 0, 0); 860 861 #ifdef SND_DYNSYSCTL 862 sysctl_ctx_init(&d->sysctl_tree); 863 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 864 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 865 device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 866 if (d->sysctl_tree_top == NULL) { 867 sysctl_ctx_free(&d->sysctl_tree); 868 goto no; 869 } 870 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 871 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 872 #endif 873 if (numplay > 0) { 874 d->flags |= SD_F_AUTOVCHAN; 875 vchan_initsys(dev); 876 } 877 878 sndstat_register(dev, d->status, sndstat_prepare_pcm); 879 return 0; 880 no: 881 snd_mtxfree(d->lock); 882 return ENXIO; 883 } 884 885 int 886 pcm_unregister(device_t dev) 887 { 888 struct snddev_info *d = device_get_softc(dev); 889 struct snddev_channel *sce; 890 struct pcmchan_children *pce; 891 struct pcm_channel *ch; 892 893 if (sndstat_acquire() != 0) { 894 device_printf(dev, "unregister: sndstat busy\n"); 895 return EBUSY; 896 } 897 898 snd_mtxlock(d->lock); 899 if (d->inprog) { 900 device_printf(dev, "unregister: operation in progress\n"); 901 snd_mtxunlock(d->lock); 902 sndstat_release(); 903 return EBUSY; 904 } 905 906 SLIST_FOREACH(sce, &d->channels, link) { 907 ch = sce->channel; 908 if (ch->refcount > 0) { 909 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 910 snd_mtxunlock(d->lock); 911 sndstat_release(); 912 return EBUSY; 913 } 914 } 915 916 if (mixer_uninit(dev) == EBUSY) { 917 device_printf(dev, "unregister: mixer busy\n"); 918 snd_mtxunlock(d->lock); 919 sndstat_release(); 920 return EBUSY; 921 } 922 923 SLIST_FOREACH(sce, &d->channels, link) { 924 if (sce->dsp_devt) { 925 destroy_dev(sce->dsp_devt); 926 sce->dsp_devt = NULL; 927 } 928 if (sce->dspW_devt) { 929 destroy_dev(sce->dspW_devt); 930 sce->dspW_devt = NULL; 931 } 932 if (sce->audio_devt) { 933 destroy_dev(sce->audio_devt); 934 sce->audio_devt = NULL; 935 } 936 if (sce->dspr_devt) { 937 destroy_dev(sce->dspr_devt); 938 sce->dspr_devt = NULL; 939 } 940 d->devcount--; 941 ch = sce->channel; 942 if (ch == NULL) 943 continue; 944 pce = SLIST_FIRST(&ch->children); 945 while (pce != NULL) { 946 #if 0 947 device_printf(d->dev, "<%s> removing <%s>\n", 948 ch->name, (pce->channel != NULL) ? 949 pce->channel->name : "unknown"); 950 #endif 951 SLIST_REMOVE(&ch->children, pce, pcmchan_children, link); 952 free(pce, M_DEVBUF); 953 pce = SLIST_FIRST(&ch->children); 954 } 955 } 956 957 #ifdef SND_DYNSYSCTL 958 d->sysctl_tree_top = NULL; 959 sysctl_ctx_free(&d->sysctl_tree); 960 #endif 961 962 #if 0 963 SLIST_FOREACH(sce, &d->channels, link) { 964 ch = sce->channel; 965 if (ch == NULL) 966 continue; 967 if (!SLIST_EMPTY(&ch->children)) 968 device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n", 969 __func__, ch->name); 970 } 971 #endif 972 while (!SLIST_EMPTY(&d->channels)) 973 pcm_killchan(dev); 974 975 chn_kill(d->fakechan); 976 fkchan_kill(d->fakechan); 977 978 #if 0 979 device_printf(d->dev, "%s: devcount=%u, playcount=%u, " 980 "reccount=%u, vchancount=%u\n", 981 __func__, d->devcount, d->playcount, d->reccount, 982 d->vchancount); 983 #endif 984 snd_mtxunlock(d->lock); 985 snd_mtxfree(d->lock); 986 sndstat_unregister(dev); 987 sndstat_release(); 988 return 0; 989 } 990 991 /************************************************************************/ 992 993 static int 994 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 995 { 996 struct snddev_info *d; 997 struct snddev_channel *sce; 998 struct pcm_channel *c; 999 struct pcm_feeder *f; 1000 int pc, rc, vc; 1001 1002 if (verbose < 1) 1003 return 0; 1004 1005 d = device_get_softc(dev); 1006 if (!d) 1007 return ENXIO; 1008 1009 snd_mtxlock(d->lock); 1010 if (!SLIST_EMPTY(&d->channels)) { 1011 pc = rc = vc = 0; 1012 SLIST_FOREACH(sce, &d->channels, link) { 1013 c = sce->channel; 1014 if (c->direction == PCMDIR_PLAY) { 1015 if (c->flags & CHN_F_VIRTUAL) 1016 vc++; 1017 else 1018 pc++; 1019 } else 1020 rc++; 1021 } 1022 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 1023 (d->flags & SD_F_SIMPLEX)? "" : " duplex", 1024 #ifdef USING_DEVFS 1025 (device_get_unit(dev) == snd_unit)? " default" : "" 1026 #else 1027 "" 1028 #endif 1029 ); 1030 1031 if (verbose <= 1) { 1032 snd_mtxunlock(d->lock); 1033 return 0; 1034 } 1035 1036 SLIST_FOREACH(sce, &d->channels, link) { 1037 c = sce->channel; 1038 1039 KASSERT(c->bufhard != NULL && c->bufsoft != NULL, 1040 ("hosed pcm channel setup")); 1041 1042 sbuf_printf(s, "\n\t"); 1043 1044 /* it would be better to indent child channels */ 1045 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 1046 sbuf_printf(s, "spd %d", c->speed); 1047 if (c->speed != sndbuf_getspd(c->bufhard)) 1048 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 1049 sbuf_printf(s, ", fmt 0x%08x", c->format); 1050 if (c->format != sndbuf_getfmt(c->bufhard)) 1051 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 1052 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 1053 if (c->pid != -1) 1054 sbuf_printf(s, ", pid %d", c->pid); 1055 sbuf_printf(s, "\n\t"); 1056 1057 sbuf_printf(s, "interrupts %d, ", c->interrupts); 1058 if (c->direction == PCMDIR_REC) 1059 sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 1060 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), 1061 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1062 sndbuf_getblkcnt(c->bufhard), 1063 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1064 sndbuf_getblkcnt(c->bufsoft)); 1065 else 1066 sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", 1067 c->xruns, sndbuf_getready(c->bufsoft), 1068 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1069 sndbuf_getblkcnt(c->bufhard), 1070 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1071 sndbuf_getblkcnt(c->bufsoft)); 1072 sbuf_printf(s, "\n\t"); 1073 1074 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 1075 sbuf_printf(s, " -> "); 1076 f = c->feeder; 1077 while (f->source != NULL) 1078 f = f->source; 1079 while (f != NULL) { 1080 sbuf_printf(s, "%s", f->class->name); 1081 if (f->desc->type == FEEDER_FMT) 1082 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 1083 if (f->desc->type == FEEDER_RATE) 1084 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 1085 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || 1086 f->desc->type == FEEDER_VOLUME) 1087 sbuf_printf(s, "(0x%08x)", f->desc->out); 1088 sbuf_printf(s, " -> "); 1089 f = f->parent; 1090 } 1091 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 1092 } 1093 } else 1094 sbuf_printf(s, " (mixer only)"); 1095 snd_mtxunlock(d->lock); 1096 1097 return 0; 1098 } 1099 1100 /************************************************************************/ 1101 1102 #ifdef SND_DYNSYSCTL 1103 int 1104 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 1105 { 1106 struct snddev_info *d; 1107 int err, newcnt; 1108 1109 d = oidp->oid_arg1; 1110 1111 newcnt = d->vchancount; 1112 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 1113 1114 if (err == 0 && req->newptr != NULL && d->vchancount != newcnt) 1115 err = pcm_setvchans(d, newcnt); 1116 1117 return err; 1118 } 1119 #endif 1120 1121 /************************************************************************/ 1122 1123 static int 1124 sound_modevent(module_t mod, int type, void *data) 1125 { 1126 #if 0 1127 return (midi_modevent(mod, type, data)); 1128 #else 1129 return 0; 1130 #endif 1131 } 1132 1133 DEV_MODULE(sound, sound_modevent, NULL); 1134 MODULE_VERSION(sound, SOUND_MODVER); 1135