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