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