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