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