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