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.default_unit", &snd_unit); 44 #endif 45 46 int snd_maxautovchans = 4; 47 /* XXX: a tunable implies that we may need more than one sound channel before 48 the system can change a sysctl (/etc/sysctl.conf), do we really need 49 this? */ 50 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 51 52 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 53 54 static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 55 56 struct sysctl_ctx_list * 57 snd_sysctl_tree(device_t dev) 58 { 59 struct snddev_info *d = device_get_softc(dev); 60 61 return &d->sysctl_tree; 62 } 63 64 struct sysctl_oid * 65 snd_sysctl_tree_top(device_t dev) 66 { 67 struct snddev_info *d = device_get_softc(dev); 68 69 return d->sysctl_tree_top; 70 } 71 72 void * 73 snd_mtxcreate(const char *desc, const char *type) 74 { 75 #ifdef USING_MUTEX 76 struct mtx *m; 77 78 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 79 if (m == NULL) 80 return NULL; 81 mtx_init(m, desc, type, MTX_DEF); 82 return m; 83 #else 84 return (void *)0xcafebabe; 85 #endif 86 } 87 88 void 89 snd_mtxfree(void *m) 90 { 91 #ifdef USING_MUTEX 92 struct mtx *mtx = m; 93 94 /* mtx_assert(mtx, MA_OWNED); */ 95 mtx_destroy(mtx); 96 free(mtx, M_DEVBUF); 97 #endif 98 } 99 100 void 101 snd_mtxassert(void *m) 102 { 103 #ifdef USING_MUTEX 104 #ifdef INVARIANTS 105 struct mtx *mtx = m; 106 107 mtx_assert(mtx, MA_OWNED); 108 #endif 109 #endif 110 } 111 /* 112 void 113 snd_mtxlock(void *m) 114 { 115 #ifdef USING_MUTEX 116 struct mtx *mtx = m; 117 118 mtx_lock(mtx); 119 #endif 120 } 121 122 void 123 snd_mtxunlock(void *m) 124 { 125 #ifdef USING_MUTEX 126 struct mtx *mtx = m; 127 128 mtx_unlock(mtx); 129 #endif 130 } 131 */ 132 int 133 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 134 { 135 #ifdef USING_MUTEX 136 flags &= INTR_MPSAFE; 137 flags |= INTR_TYPE_AV; 138 #else 139 flags = INTR_TYPE_AV; 140 #endif 141 return bus_setup_intr(dev, res, flags, hand, param, cookiep); 142 } 143 144 #ifndef PCM_DEBUG_MTX 145 void 146 pcm_lock(struct snddev_info *d) 147 { 148 snd_mtxlock(d->lock); 149 } 150 151 void 152 pcm_unlock(struct snddev_info *d) 153 { 154 snd_mtxunlock(d->lock); 155 } 156 #endif 157 158 struct pcm_channel * 159 pcm_getfakechan(struct snddev_info *d) 160 { 161 return d->fakechan; 162 } 163 164 static int 165 pcm_setvchans(struct snddev_info *d, int newcnt) 166 { 167 struct snddev_channel *sce = NULL; 168 struct pcm_channel *c = NULL; 169 int err = 0, vcnt, dcnt, i; 170 171 pcm_inprog(d, 1); 172 173 if (!(d->flags & SD_F_AUTOVCHAN)) { 174 err = EINVAL; 175 goto setvchans_out; 176 } 177 178 vcnt = d->vchancount; 179 dcnt = d->playcount + d->reccount; 180 181 if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) { 182 err = E2BIG; 183 goto setvchans_out; 184 } 185 186 dcnt += vcnt; 187 188 if (newcnt > vcnt) { 189 /* add new vchans - find a parent channel first */ 190 SLIST_FOREACH(sce, &d->channels, link) { 191 c = sce->channel; 192 CHN_LOCK(c); 193 if (c->direction == PCMDIR_PLAY && 194 ((c->flags & CHN_F_HAS_VCHAN) || 195 (vcnt == 0 && 196 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) 197 goto addok; 198 CHN_UNLOCK(c); 199 } 200 err = EBUSY; 201 goto setvchans_out; 202 addok: 203 c->flags |= CHN_F_BUSY; 204 while (err == 0 && newcnt > vcnt) { 205 if (dcnt > PCMMAXCHAN) { 206 device_printf(d->dev, "%s: Maximum channel reached.\n", __func__); 207 break; 208 } 209 err = vchan_create(c); 210 if (err == 0) { 211 vcnt++; 212 dcnt++; 213 } else if (err == E2BIG && newcnt > vcnt) 214 device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err); 215 } 216 if (vcnt == 0) 217 c->flags &= ~CHN_F_BUSY; 218 CHN_UNLOCK(c); 219 } else if (newcnt < vcnt) { 220 #define ORPHAN_CDEVT(cdevt) \ 221 ((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \ 222 (cdevt)->si_drv2 == NULL)) 223 while (err == 0 && newcnt < vcnt) { 224 i = 0; 225 SLIST_FOREACH(sce, &d->channels, link) { 226 c = sce->channel; 227 CHN_LOCK(c); 228 if (c->direction == PCMDIR_PLAY && 229 (c->flags & CHN_F_VIRTUAL) && 230 (i++ == newcnt)) { 231 if (!(c->flags & CHN_F_BUSY) && 232 ORPHAN_CDEVT(sce->dsp_devt) && 233 ORPHAN_CDEVT(sce->dspW_devt) && 234 ORPHAN_CDEVT(sce->audio_devt) && 235 ORPHAN_CDEVT(sce->dspr_devt)) 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 retry_chnalloc: 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 /* no channel available */ 308 if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 && 309 d->vchancount < snd_maxautovchans && 310 d->devcount <= PCMMAXCHAN) { 311 err = pcm_setvchans(d, d->vchancount + 1); 312 if (err == 0) { 313 chnum = -2; 314 goto retry_chnalloc; 315 } 316 } 317 318 return err; 319 } 320 321 /* release a locked channel and unlock it */ 322 int 323 pcm_chnrelease(struct pcm_channel *c) 324 { 325 CHN_LOCKASSERT(c); 326 c->flags &= ~CHN_F_BUSY; 327 c->pid = -1; 328 CHN_UNLOCK(c); 329 return 0; 330 } 331 332 int 333 pcm_chnref(struct pcm_channel *c, int ref) 334 { 335 int r; 336 337 CHN_LOCKASSERT(c); 338 c->refcount += ref; 339 r = c->refcount; 340 return r; 341 } 342 343 int 344 pcm_inprog(struct snddev_info *d, int delta) 345 { 346 int r; 347 348 if (delta == 0) 349 return d->inprog; 350 351 /* backtrace(); */ 352 pcm_lock(d); 353 d->inprog += delta; 354 r = d->inprog; 355 pcm_unlock(d); 356 return r; 357 } 358 359 static void 360 pcm_setmaxautovchans(struct snddev_info *d, int num) 361 { 362 if (num > 0 && d->vchancount == 0) 363 pcm_setvchans(d, 1); 364 else if (num == 0 && d->vchancount > 0) 365 pcm_setvchans(d, 0); 366 } 367 368 #ifdef USING_DEVFS 369 static int 370 sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 371 { 372 struct snddev_info *d; 373 int error, unit; 374 375 unit = snd_unit; 376 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 377 if (error == 0 && req->newptr != NULL) { 378 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 379 return EINVAL; 380 d = devclass_get_softc(pcm_devclass, unit); 381 if (d == NULL || SLIST_EMPTY(&d->channels)) 382 return EINVAL; 383 snd_unit = unit; 384 } 385 return (error); 386 } 387 /* XXX: do we need a way to let the user change the default unit? */ 388 SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW, 389 0, sizeof(int), sysctl_hw_snd_default_unit, "I", ""); 390 #endif 391 392 static int 393 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 394 { 395 struct snddev_info *d; 396 int i, v, error; 397 398 v = snd_maxautovchans; 399 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 400 if (error == 0 && req->newptr != NULL) { 401 if (v < 0 || v > PCMMAXCHAN) 402 return E2BIG; 403 if (pcm_devclass != NULL && v != snd_maxautovchans) { 404 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 405 d = devclass_get_softc(pcm_devclass, i); 406 if (!d) 407 continue; 408 pcm_setmaxautovchans(d, v); 409 } 410 } 411 snd_maxautovchans = v; 412 } 413 return (error); 414 } 415 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 416 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 417 418 struct pcm_channel * 419 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 420 { 421 struct snddev_channel *sce; 422 struct pcm_channel *ch, *c; 423 char *dirs; 424 uint32_t flsearch = 0; 425 int direction, err, rpnum, *pnum; 426 427 switch(dir) { 428 case PCMDIR_PLAY: 429 dirs = "play"; 430 direction = PCMDIR_PLAY; 431 pnum = &d->playcount; 432 break; 433 434 case PCMDIR_REC: 435 dirs = "record"; 436 direction = PCMDIR_REC; 437 pnum = &d->reccount; 438 break; 439 440 case PCMDIR_VIRTUAL: 441 dirs = "virtual"; 442 direction = PCMDIR_PLAY; 443 pnum = &d->vchancount; 444 flsearch = CHN_F_VIRTUAL; 445 break; 446 447 default: 448 return NULL; 449 } 450 451 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 452 if (!ch) 453 return NULL; 454 455 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 456 if (!ch->methods) { 457 free(ch, M_DEVBUF); 458 459 return NULL; 460 } 461 462 snd_mtxlock(d->lock); 463 ch->num = 0; 464 rpnum = 0; 465 SLIST_FOREACH(sce, &d->channels, link) { 466 c = sce->channel; 467 if (direction != c->direction || 468 (c->flags & CHN_F_VIRTUAL) != flsearch) 469 continue; 470 if (ch->num == c->num) 471 ch->num++; 472 else { 473 #if 0 474 device_printf(d->dev, 475 "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n", 476 __func__, dirs, ch->num, c->num); 477 #endif 478 goto retry_num_search; 479 } 480 rpnum++; 481 } 482 goto retry_num_search_out; 483 retry_num_search: 484 rpnum = 0; 485 SLIST_FOREACH(sce, &d->channels, link) { 486 c = sce->channel; 487 if (direction != c->direction || 488 (c->flags & CHN_F_VIRTUAL) != flsearch) 489 continue; 490 if (ch->num == c->num) { 491 ch->num++; 492 goto retry_num_search; 493 } 494 rpnum++; 495 } 496 retry_num_search_out: 497 if (*pnum != rpnum) { 498 device_printf(d->dev, 499 "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n", 500 __func__, dirs, *pnum, rpnum); 501 *pnum = rpnum; 502 } 503 (*pnum)++; 504 snd_mtxunlock(d->lock); 505 506 ch->pid = -1; 507 ch->parentsnddev = d; 508 ch->parentchannel = parent; 509 ch->dev = d->dev; 510 snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); 511 512 err = chn_init(ch, devinfo, dir, direction); 513 if (err) { 514 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 515 kobj_delete(ch->methods, M_DEVBUF); 516 free(ch, M_DEVBUF); 517 snd_mtxlock(d->lock); 518 (*pnum)--; 519 snd_mtxunlock(d->lock); 520 521 return NULL; 522 } 523 524 return ch; 525 } 526 527 int 528 pcm_chn_destroy(struct pcm_channel *ch) 529 { 530 struct snddev_info *d; 531 int err; 532 533 d = ch->parentsnddev; 534 err = chn_kill(ch); 535 if (err) { 536 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 537 return err; 538 } 539 540 kobj_delete(ch->methods, M_DEVBUF); 541 free(ch, M_DEVBUF); 542 543 return 0; 544 } 545 546 int 547 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 548 { 549 struct snddev_channel *sce, *tmp, *after; 550 unsigned rdevcount; 551 int device = device_get_unit(d->dev); 552 size_t namelen; 553 554 /* 555 * Note it's confusing nomenclature. 556 * dev_t 557 * device -> pcm_device 558 * unit -> pcm_channel 559 * channel -> snddev_channel 560 * device_t 561 * unit -> pcm_device 562 */ 563 564 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 565 if (!sce) { 566 return ENOMEM; 567 } 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 free(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 snprintf(ch->name + namelen, 647 CHN_NAMELEN - namelen, ":dsp%d.%d", 648 device, sce->chan_num); 649 } 650 snd_mtxunlock(d->lock); 651 652 /* 653 * I will revisit these someday, and nuke it mercilessly.. 654 */ 655 sce->dsp_devt = make_dev(&dsp_cdevsw, 656 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), 657 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", 658 device, sce->chan_num); 659 660 sce->dspW_devt = make_dev(&dsp_cdevsw, 661 PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num), 662 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", 663 device, sce->chan_num); 664 665 sce->audio_devt = make_dev(&dsp_cdevsw, 666 PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num), 667 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", 668 device, sce->chan_num); 669 670 if (ch->direction == PCMDIR_REC) 671 sce->dspr_devt = make_dev(&dsp_cdevsw, 672 PCMMKMINOR(device, SND_DEV_DSPREC, 673 sce->chan_num), UID_ROOT, GID_WHEEL, 674 0666, "dspr%d.%d", device, sce->chan_num); 675 676 return 0; 677 } 678 679 int 680 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 681 { 682 struct snddev_channel *sce; 683 #if 0 684 int ourlock; 685 686 ourlock = 0; 687 if (!mtx_owned(d->lock)) { 688 snd_mtxlock(d->lock); 689 ourlock = 1; 690 } 691 #endif 692 693 SLIST_FOREACH(sce, &d->channels, link) { 694 if (sce->channel == ch) 695 goto gotit; 696 } 697 #if 0 698 if (ourlock) 699 snd_mtxunlock(d->lock); 700 #endif 701 return EINVAL; 702 gotit: 703 SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 704 705 if (ch->flags & CHN_F_VIRTUAL) 706 d->vchancount--; 707 else if (ch->direction == PCMDIR_REC) 708 d->reccount--; 709 else 710 d->playcount--; 711 712 #if 0 713 if (ourlock) 714 snd_mtxunlock(d->lock); 715 #endif 716 free(sce, M_DEVBUF); 717 718 return 0; 719 } 720 721 int 722 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 723 { 724 struct snddev_info *d = device_get_softc(dev); 725 struct pcm_channel *ch; 726 int err; 727 728 ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 729 if (!ch) { 730 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 731 return ENODEV; 732 } 733 734 err = pcm_chn_add(d, ch); 735 if (err) { 736 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 737 pcm_chn_destroy(ch); 738 return err; 739 } 740 741 return err; 742 } 743 744 static int 745 pcm_killchan(device_t dev) 746 { 747 struct snddev_info *d = device_get_softc(dev); 748 struct snddev_channel *sce; 749 struct pcm_channel *ch; 750 int error = 0; 751 752 sce = SLIST_FIRST(&d->channels); 753 ch = sce->channel; 754 755 error = pcm_chn_remove(d, sce->channel); 756 if (error) 757 return (error); 758 return (pcm_chn_destroy(ch)); 759 } 760 761 int 762 pcm_setstatus(device_t dev, char *str) 763 { 764 struct snddev_info *d = device_get_softc(dev); 765 766 snd_mtxlock(d->lock); 767 strncpy(d->status, str, SND_STATUSLEN); 768 snd_mtxunlock(d->lock); 769 if (snd_maxautovchans > 0) 770 pcm_setvchans(d, 1); 771 return 0; 772 } 773 774 uint32_t 775 pcm_getflags(device_t dev) 776 { 777 struct snddev_info *d = device_get_softc(dev); 778 779 return d->flags; 780 } 781 782 void 783 pcm_setflags(device_t dev, uint32_t val) 784 { 785 struct snddev_info *d = device_get_softc(dev); 786 787 d->flags = val; 788 } 789 790 void * 791 pcm_getdevinfo(device_t dev) 792 { 793 struct snddev_info *d = device_get_softc(dev); 794 795 return d->devinfo; 796 } 797 798 unsigned int 799 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 800 { 801 struct snddev_info *d = device_get_softc(dev); 802 int sz, x; 803 804 sz = 0; 805 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 806 x = sz; 807 RANGE(sz, min, max); 808 if (x != sz) 809 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 810 x = min; 811 while (x < sz) 812 x <<= 1; 813 if (x > sz) 814 x >>= 1; 815 if (x != sz) { 816 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 817 sz = x; 818 } 819 } else { 820 sz = deflt; 821 } 822 823 d->bufsz = sz; 824 825 return sz; 826 } 827 828 int 829 pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 830 { 831 struct snddev_info *d = device_get_softc(dev); 832 833 if (pcm_veto_load) { 834 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 835 836 return EINVAL; 837 } 838 839 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 840 841 #if 0 842 /* 843 * d->flags should be cleared by the allocator of the softc. 844 * We cannot clear this field here because several devices set 845 * this flag before calling pcm_register(). 846 */ 847 d->flags = 0; 848 #endif 849 d->dev = dev; 850 d->devinfo = devinfo; 851 d->devcount = 0; 852 d->reccount = 0; 853 d->playcount = 0; 854 d->vchancount = 0; 855 d->inprog = 0; 856 857 SLIST_INIT(&d->channels); 858 859 if ((numplay == 0 || numrec == 0) && numplay != numrec) 860 d->flags |= SD_F_SIMPLEX; 861 862 d->fakechan = fkchan_setup(dev); 863 chn_init(d->fakechan, NULL, 0, 0); 864 865 #ifdef SND_DYNSYSCTL 866 sysctl_ctx_init(&d->sysctl_tree); 867 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 868 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 869 device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 870 if (d->sysctl_tree_top == NULL) { 871 sysctl_ctx_free(&d->sysctl_tree); 872 goto no; 873 } 874 /* XXX: an user should be able to set this with a control tool, the 875 sysadmin then needs min+max sysctls for this */ 876 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 877 OID_AUTO, "_buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 878 #endif 879 if (numplay > 0) { 880 d->flags |= SD_F_AUTOVCHAN; 881 vchan_initsys(dev); 882 } 883 884 sndstat_register(dev, d->status, sndstat_prepare_pcm); 885 return 0; 886 no: 887 snd_mtxfree(d->lock); 888 return ENXIO; 889 } 890 891 int 892 pcm_unregister(device_t dev) 893 { 894 struct snddev_info *d = device_get_softc(dev); 895 struct snddev_channel *sce; 896 struct pcmchan_children *pce; 897 struct pcm_channel *ch; 898 899 if (sndstat_acquire() != 0) { 900 device_printf(dev, "unregister: sndstat busy\n"); 901 return EBUSY; 902 } 903 904 snd_mtxlock(d->lock); 905 if (d->inprog) { 906 device_printf(dev, "unregister: operation in progress\n"); 907 snd_mtxunlock(d->lock); 908 sndstat_release(); 909 return EBUSY; 910 } 911 912 SLIST_FOREACH(sce, &d->channels, link) { 913 ch = sce->channel; 914 if (ch->refcount > 0) { 915 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 916 snd_mtxunlock(d->lock); 917 sndstat_release(); 918 return EBUSY; 919 } 920 } 921 922 if (mixer_uninit(dev) == EBUSY) { 923 device_printf(dev, "unregister: mixer busy\n"); 924 snd_mtxunlock(d->lock); 925 sndstat_release(); 926 return EBUSY; 927 } 928 929 SLIST_FOREACH(sce, &d->channels, link) { 930 if (sce->dsp_devt) { 931 destroy_dev(sce->dsp_devt); 932 sce->dsp_devt = NULL; 933 } 934 if (sce->dspW_devt) { 935 destroy_dev(sce->dspW_devt); 936 sce->dspW_devt = NULL; 937 } 938 if (sce->audio_devt) { 939 destroy_dev(sce->audio_devt); 940 sce->audio_devt = NULL; 941 } 942 if (sce->dspr_devt) { 943 destroy_dev(sce->dspr_devt); 944 sce->dspr_devt = NULL; 945 } 946 d->devcount--; 947 ch = sce->channel; 948 if (ch == NULL) 949 continue; 950 pce = SLIST_FIRST(&ch->children); 951 while (pce != NULL) { 952 #if 0 953 device_printf(d->dev, "<%s> removing <%s>\n", 954 ch->name, (pce->channel != NULL) ? 955 pce->channel->name : "unknown"); 956 #endif 957 SLIST_REMOVE(&ch->children, pce, pcmchan_children, link); 958 free(pce, M_DEVBUF); 959 pce = SLIST_FIRST(&ch->children); 960 } 961 } 962 963 #ifdef SND_DYNSYSCTL 964 d->sysctl_tree_top = NULL; 965 sysctl_ctx_free(&d->sysctl_tree); 966 #endif 967 968 #if 0 969 SLIST_FOREACH(sce, &d->channels, link) { 970 ch = sce->channel; 971 if (ch == NULL) 972 continue; 973 if (!SLIST_EMPTY(&ch->children)) 974 device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n", 975 __func__, ch->name); 976 } 977 #endif 978 while (!SLIST_EMPTY(&d->channels)) 979 pcm_killchan(dev); 980 981 chn_kill(d->fakechan); 982 fkchan_kill(d->fakechan); 983 984 #if 0 985 device_printf(d->dev, "%s: devcount=%u, playcount=%u, " 986 "reccount=%u, vchancount=%u\n", 987 __func__, d->devcount, d->playcount, d->reccount, 988 d->vchancount); 989 #endif 990 snd_mtxunlock(d->lock); 991 snd_mtxfree(d->lock); 992 sndstat_unregister(dev); 993 sndstat_release(); 994 return 0; 995 } 996 997 /************************************************************************/ 998 999 static int 1000 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 1001 { 1002 struct snddev_info *d; 1003 struct snddev_channel *sce; 1004 struct pcm_channel *c; 1005 struct pcm_feeder *f; 1006 int pc, rc, vc; 1007 1008 if (verbose < 1) 1009 return 0; 1010 1011 d = device_get_softc(dev); 1012 if (!d) 1013 return ENXIO; 1014 1015 snd_mtxlock(d->lock); 1016 if (!SLIST_EMPTY(&d->channels)) { 1017 pc = rc = vc = 0; 1018 SLIST_FOREACH(sce, &d->channels, link) { 1019 c = sce->channel; 1020 if (c->direction == PCMDIR_PLAY) { 1021 if (c->flags & CHN_F_VIRTUAL) 1022 vc++; 1023 else 1024 pc++; 1025 } else 1026 rc++; 1027 } 1028 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 1029 (d->flags & SD_F_SIMPLEX)? "" : " duplex", 1030 #ifdef USING_DEVFS 1031 (device_get_unit(dev) == snd_unit)? " default" : "" 1032 #else 1033 "" 1034 #endif 1035 ); 1036 1037 if (verbose <= 1) { 1038 snd_mtxunlock(d->lock); 1039 return 0; 1040 } 1041 1042 SLIST_FOREACH(sce, &d->channels, link) { 1043 c = sce->channel; 1044 1045 KASSERT(c->bufhard != NULL && c->bufsoft != NULL, 1046 ("hosed pcm channel setup")); 1047 1048 sbuf_printf(s, "\n\t"); 1049 1050 /* it would be better to indent child channels */ 1051 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 1052 sbuf_printf(s, "spd %d", c->speed); 1053 if (c->speed != sndbuf_getspd(c->bufhard)) 1054 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 1055 sbuf_printf(s, ", fmt 0x%08x", c->format); 1056 if (c->format != sndbuf_getfmt(c->bufhard)) 1057 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 1058 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 1059 if (c->pid != -1) 1060 sbuf_printf(s, ", pid %d", c->pid); 1061 sbuf_printf(s, "\n\t"); 1062 1063 sbuf_printf(s, "interrupts %d, ", c->interrupts); 1064 if (c->direction == PCMDIR_REC) 1065 sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 1066 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), 1067 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1068 sndbuf_getblkcnt(c->bufhard), 1069 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1070 sndbuf_getblkcnt(c->bufsoft)); 1071 else 1072 sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", 1073 c->xruns, sndbuf_getready(c->bufsoft), 1074 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1075 sndbuf_getblkcnt(c->bufhard), 1076 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1077 sndbuf_getblkcnt(c->bufsoft)); 1078 sbuf_printf(s, "\n\t"); 1079 1080 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 1081 sbuf_printf(s, " -> "); 1082 f = c->feeder; 1083 while (f->source != NULL) 1084 f = f->source; 1085 while (f != NULL) { 1086 sbuf_printf(s, "%s", f->class->name); 1087 if (f->desc->type == FEEDER_FMT) 1088 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 1089 if (f->desc->type == FEEDER_RATE) 1090 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 1091 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || 1092 f->desc->type == FEEDER_VOLUME) 1093 sbuf_printf(s, "(0x%08x)", f->desc->out); 1094 sbuf_printf(s, " -> "); 1095 f = f->parent; 1096 } 1097 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 1098 } 1099 } else 1100 sbuf_printf(s, " (mixer only)"); 1101 snd_mtxunlock(d->lock); 1102 1103 return 0; 1104 } 1105 1106 /************************************************************************/ 1107 1108 #ifdef SND_DYNSYSCTL 1109 int 1110 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 1111 { 1112 struct snddev_info *d; 1113 int err, newcnt; 1114 1115 d = oidp->oid_arg1; 1116 1117 newcnt = d->vchancount; 1118 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 1119 1120 if (err == 0 && req->newptr != NULL && d->vchancount != newcnt) 1121 err = pcm_setvchans(d, newcnt); 1122 1123 return err; 1124 } 1125 #endif 1126 1127 /************************************************************************/ 1128 1129 static int 1130 sound_modevent(module_t mod, int type, void *data) 1131 { 1132 #if 0 1133 return (midi_modevent(mod, type, data)); 1134 #else 1135 return 0; 1136 #endif 1137 } 1138 1139 DEV_MODULE(sound, sound_modevent, NULL); 1140 MODULE_VERSION(sound, SOUND_MODVER); 1141