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