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