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