1 /*- 2 * Copyright (c) 2006-2009 Ariff Abdullah <ariff@FreeBSD.org> 3 * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org> 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 /* Almost entirely rewritten to add multi-format/channels mixing support. */ 29 30 #ifdef HAVE_KERNEL_OPTION_HEADERS 31 #include "opt_snd.h" 32 #endif 33 34 #include <dev/sound/pcm/sound.h> 35 #include <dev/sound/pcm/vchan.h> 36 37 SND_DECLARE_FILE("$FreeBSD$"); 38 39 /* 40 * [ac3 , dts , linear , 0, linear, 0] 41 */ 42 #define FMTLIST_MAX 6 43 #define FMTLIST_OFFSET 4 44 #define DIGFMTS_MAX 2 45 46 #ifdef SND_DEBUG 47 static int snd_passthrough_verbose = 0; 48 SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RWTUN, 49 &snd_passthrough_verbose, 0, "passthrough verbosity"); 50 51 #endif 52 53 struct vchan_info { 54 struct pcm_channel *channel; 55 struct pcmchan_caps caps; 56 uint32_t fmtlist[FMTLIST_MAX]; 57 int trigger; 58 }; 59 60 static void * 61 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 62 struct pcm_channel *c, int dir) 63 { 64 struct vchan_info *info; 65 struct pcm_channel *p; 66 uint32_t i, j, *fmtlist; 67 68 KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC, 69 ("vchan_init: bad direction")); 70 KASSERT(c != NULL && c->parentchannel != NULL, 71 ("vchan_init: bad channels")); 72 73 info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO); 74 info->channel = c; 75 info->trigger = PCMTRIG_STOP; 76 p = c->parentchannel; 77 78 CHN_LOCK(p); 79 80 fmtlist = chn_getcaps(p)->fmtlist; 81 for (i = 0, j = 0; fmtlist[i] != 0 && j < DIGFMTS_MAX; i++) { 82 if (fmtlist[i] & AFMT_PASSTHROUGH) 83 info->fmtlist[j++] = fmtlist[i]; 84 } 85 if (p->format & AFMT_VCHAN) 86 info->fmtlist[j] = p->format; 87 else 88 info->fmtlist[j] = VCHAN_DEFAULT_FORMAT; 89 info->caps.fmtlist = info->fmtlist + 90 ((p->flags & CHN_F_VCHAN_DYNAMIC) ? 0 : FMTLIST_OFFSET); 91 92 CHN_UNLOCK(p); 93 94 c->flags |= CHN_F_VIRTUAL; 95 96 return (info); 97 } 98 99 static int 100 vchan_free(kobj_t obj, void *data) 101 { 102 103 free(data, M_DEVBUF); 104 105 return (0); 106 } 107 108 static int 109 vchan_setformat(kobj_t obj, void *data, uint32_t format) 110 { 111 struct vchan_info *info; 112 113 info = data; 114 115 CHN_LOCKASSERT(info->channel); 116 117 if (!snd_fmtvalid(format, info->caps.fmtlist)) 118 return (-1); 119 120 return (0); 121 } 122 123 static uint32_t 124 vchan_setspeed(kobj_t obj, void *data, uint32_t speed) 125 { 126 struct vchan_info *info; 127 128 info = data; 129 130 CHN_LOCKASSERT(info->channel); 131 132 return (info->caps.maxspeed); 133 } 134 135 static int 136 vchan_trigger(kobj_t obj, void *data, int go) 137 { 138 struct vchan_info *info; 139 struct pcm_channel *c, *p; 140 int ret, otrigger; 141 142 info = data; 143 144 if (!PCMTRIG_COMMON(go) || go == info->trigger) 145 return (0); 146 147 c = info->channel; 148 p = c->parentchannel; 149 otrigger = info->trigger; 150 info->trigger = go; 151 152 CHN_LOCKASSERT(c); 153 154 CHN_UNLOCK(c); 155 CHN_LOCK(p); 156 157 switch (go) { 158 case PCMTRIG_START: 159 if (otrigger != PCMTRIG_START) 160 CHN_INSERT_HEAD(p, c, children.busy); 161 break; 162 case PCMTRIG_STOP: 163 case PCMTRIG_ABORT: 164 if (otrigger == PCMTRIG_START) 165 CHN_REMOVE(p, c, children.busy); 166 break; 167 default: 168 break; 169 } 170 171 ret = chn_notify(p, CHN_N_TRIGGER); 172 173 CHN_LOCK(c); 174 175 if (ret == 0 && go == PCMTRIG_START && VCHAN_SYNC_REQUIRED(c)) 176 ret = vchan_sync(c); 177 178 CHN_UNLOCK(c); 179 CHN_UNLOCK(p); 180 CHN_LOCK(c); 181 182 return (ret); 183 } 184 185 static struct pcmchan_caps * 186 vchan_getcaps(kobj_t obj, void *data) 187 { 188 struct vchan_info *info; 189 struct pcm_channel *c; 190 uint32_t pformat, pspeed, pflags, i; 191 192 info = data; 193 c = info->channel; 194 pformat = c->parentchannel->format; 195 pspeed = c->parentchannel->speed; 196 pflags = c->parentchannel->flags; 197 198 CHN_LOCKASSERT(c); 199 200 if (pflags & CHN_F_VCHAN_DYNAMIC) { 201 info->caps.fmtlist = info->fmtlist; 202 if (pformat & AFMT_VCHAN) { 203 for (i = 0; info->caps.fmtlist[i] != 0; i++) { 204 if (info->caps.fmtlist[i] & AFMT_PASSTHROUGH) 205 continue; 206 break; 207 } 208 info->caps.fmtlist[i] = pformat; 209 } 210 if (c->format & AFMT_PASSTHROUGH) 211 info->caps.minspeed = c->speed; 212 else 213 info->caps.minspeed = pspeed; 214 info->caps.maxspeed = info->caps.minspeed; 215 } else { 216 info->caps.fmtlist = info->fmtlist + FMTLIST_OFFSET; 217 if (pformat & AFMT_VCHAN) 218 info->caps.fmtlist[0] = pformat; 219 else { 220 device_printf(c->dev, 221 "%s(): invalid vchan format 0x%08x", 222 __func__, pformat); 223 info->caps.fmtlist[0] = VCHAN_DEFAULT_FORMAT; 224 } 225 info->caps.minspeed = pspeed; 226 info->caps.maxspeed = info->caps.minspeed; 227 } 228 229 return (&info->caps); 230 } 231 232 static struct pcmchan_matrix * 233 vchan_getmatrix(kobj_t obj, void *data, uint32_t format) 234 { 235 236 return (feeder_matrix_format_map(format)); 237 } 238 239 static kobj_method_t vchan_methods[] = { 240 KOBJMETHOD(channel_init, vchan_init), 241 KOBJMETHOD(channel_free, vchan_free), 242 KOBJMETHOD(channel_setformat, vchan_setformat), 243 KOBJMETHOD(channel_setspeed, vchan_setspeed), 244 KOBJMETHOD(channel_trigger, vchan_trigger), 245 KOBJMETHOD(channel_getcaps, vchan_getcaps), 246 KOBJMETHOD(channel_getmatrix, vchan_getmatrix), 247 KOBJMETHOD_END 248 }; 249 CHANNEL_DECLARE(vchan); 250 251 static void 252 pcm_getparentchannel(struct snddev_info *d, 253 struct pcm_channel **wrch, struct pcm_channel **rdch) 254 { 255 struct pcm_channel **ch, *wch, *rch, *c; 256 257 KASSERT(d != NULL, ("%s(): NULL snddev_info", __func__)); 258 259 PCM_BUSYASSERT(d); 260 PCM_UNLOCKASSERT(d); 261 262 wch = NULL; 263 rch = NULL; 264 265 CHN_FOREACH(c, d, channels.pcm) { 266 CHN_LOCK(c); 267 ch = (c->direction == PCMDIR_PLAY) ? &wch : &rch; 268 if (c->flags & CHN_F_VIRTUAL) { 269 /* Sanity check */ 270 if (*ch != NULL && *ch != c->parentchannel) { 271 CHN_UNLOCK(c); 272 *ch = NULL; 273 break; 274 } 275 } else if (c->flags & CHN_F_HAS_VCHAN) { 276 /* No way!! */ 277 if (*ch != NULL) { 278 CHN_UNLOCK(c); 279 *ch = NULL; 280 break; 281 } 282 *ch = c; 283 } 284 CHN_UNLOCK(c); 285 } 286 287 if (wrch != NULL) 288 *wrch = wch; 289 if (rdch != NULL) 290 *rdch = rch; 291 } 292 293 static int 294 sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS) 295 { 296 struct snddev_info *d; 297 int direction, vchancount; 298 int err, cnt; 299 300 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 301 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 302 return (EINVAL); 303 304 PCM_LOCK(d); 305 PCM_WAIT(d); 306 307 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 308 case VCHAN_PLAY: 309 direction = PCMDIR_PLAY; 310 vchancount = d->pvchancount; 311 cnt = d->playcount; 312 break; 313 case VCHAN_REC: 314 direction = PCMDIR_REC; 315 vchancount = d->rvchancount; 316 cnt = d->reccount; 317 break; 318 default: 319 PCM_UNLOCK(d); 320 return (EINVAL); 321 break; 322 } 323 324 if (cnt < 1) { 325 PCM_UNLOCK(d); 326 return (ENODEV); 327 } 328 329 PCM_ACQUIRE(d); 330 PCM_UNLOCK(d); 331 332 cnt = vchancount; 333 err = sysctl_handle_int(oidp, &cnt, 0, req); 334 335 if (err == 0 && req->newptr != NULL && vchancount != cnt) { 336 if (cnt < 0) 337 cnt = 0; 338 if (cnt > SND_MAXVCHANS) 339 cnt = SND_MAXVCHANS; 340 err = pcm_setvchans(d, direction, cnt, -1); 341 } 342 343 PCM_RELEASE_QUICK(d); 344 345 return err; 346 } 347 348 static int 349 sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS) 350 { 351 struct snddev_info *d; 352 struct pcm_channel *c; 353 uint32_t dflags; 354 int direction, ret; 355 char dtype[16]; 356 357 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 358 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 359 return (EINVAL); 360 361 PCM_LOCK(d); 362 PCM_WAIT(d); 363 364 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 365 case VCHAN_PLAY: 366 direction = PCMDIR_PLAY; 367 break; 368 case VCHAN_REC: 369 direction = PCMDIR_REC; 370 break; 371 default: 372 PCM_UNLOCK(d); 373 return (EINVAL); 374 break; 375 } 376 377 PCM_ACQUIRE(d); 378 PCM_UNLOCK(d); 379 380 if (direction == PCMDIR_PLAY) 381 pcm_getparentchannel(d, &c, NULL); 382 else 383 pcm_getparentchannel(d, NULL, &c); 384 385 if (c == NULL) { 386 PCM_RELEASE_QUICK(d); 387 return (EINVAL); 388 } 389 390 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 391 __func__, direction, c->direction)); 392 393 CHN_LOCK(c); 394 if (c->flags & CHN_F_VCHAN_PASSTHROUGH) 395 strlcpy(dtype, "passthrough", sizeof(dtype)); 396 else if (c->flags & CHN_F_VCHAN_ADAPTIVE) 397 strlcpy(dtype, "adaptive", sizeof(dtype)); 398 else 399 strlcpy(dtype, "fixed", sizeof(dtype)); 400 CHN_UNLOCK(c); 401 402 ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req); 403 if (ret == 0 && req->newptr != NULL) { 404 if (strcasecmp(dtype, "passthrough") == 0 || 405 strcmp(dtype, "1") == 0) 406 dflags = CHN_F_VCHAN_PASSTHROUGH; 407 else if (strcasecmp(dtype, "adaptive") == 0 || 408 strcmp(dtype, "2") == 0) 409 dflags = CHN_F_VCHAN_ADAPTIVE; 410 else if (strcasecmp(dtype, "fixed") == 0 || 411 strcmp(dtype, "0") == 0) 412 dflags = 0; 413 else { 414 PCM_RELEASE_QUICK(d); 415 return (EINVAL); 416 } 417 CHN_LOCK(c); 418 if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) || 419 (c->flags & CHN_F_PASSTHROUGH)) { 420 CHN_UNLOCK(c); 421 PCM_RELEASE_QUICK(d); 422 return (0); 423 } 424 c->flags &= ~CHN_F_VCHAN_DYNAMIC; 425 c->flags |= dflags; 426 CHN_UNLOCK(c); 427 } 428 429 PCM_RELEASE_QUICK(d); 430 431 return (ret); 432 } 433 434 /* 435 * On the fly vchan rate/format settings 436 */ 437 438 #define VCHAN_ACCESSIBLE(c) (!((c)->flags & (CHN_F_PASSTHROUGH | \ 439 CHN_F_EXCLUSIVE)) && \ 440 (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \ 441 CHN_STOPPED(c))) 442 static int 443 sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS) 444 { 445 struct snddev_info *d; 446 struct pcm_channel *c, *ch; 447 struct pcmchan_caps *caps; 448 int *vchanrate, vchancount, direction, ret, newspd, restart; 449 450 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 451 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 452 return (EINVAL); 453 454 PCM_LOCK(d); 455 PCM_WAIT(d); 456 457 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 458 case VCHAN_PLAY: 459 direction = PCMDIR_PLAY; 460 vchancount = d->pvchancount; 461 vchanrate = &d->pvchanrate; 462 break; 463 case VCHAN_REC: 464 direction = PCMDIR_REC; 465 vchancount = d->rvchancount; 466 vchanrate = &d->rvchanrate; 467 break; 468 default: 469 PCM_UNLOCK(d); 470 return (EINVAL); 471 break; 472 } 473 474 if (vchancount < 1) { 475 PCM_UNLOCK(d); 476 return (EINVAL); 477 } 478 479 PCM_ACQUIRE(d); 480 PCM_UNLOCK(d); 481 482 if (direction == PCMDIR_PLAY) 483 pcm_getparentchannel(d, &c, NULL); 484 else 485 pcm_getparentchannel(d, NULL, &c); 486 487 if (c == NULL) { 488 PCM_RELEASE_QUICK(d); 489 return (EINVAL); 490 } 491 492 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 493 __func__, direction, c->direction)); 494 495 CHN_LOCK(c); 496 newspd = c->speed; 497 CHN_UNLOCK(c); 498 499 ret = sysctl_handle_int(oidp, &newspd, 0, req); 500 if (ret != 0 || req->newptr == NULL) { 501 PCM_RELEASE_QUICK(d); 502 return (ret); 503 } 504 505 if (newspd < 1 || newspd < feeder_rate_min || 506 newspd > feeder_rate_max) { 507 PCM_RELEASE_QUICK(d); 508 return (EINVAL); 509 } 510 511 CHN_LOCK(c); 512 513 if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) { 514 if (CHN_STARTED(c)) { 515 chn_abort(c); 516 restart = 1; 517 } else 518 restart = 0; 519 520 if (feeder_rate_round) { 521 caps = chn_getcaps(c); 522 RANGE(newspd, caps->minspeed, caps->maxspeed); 523 newspd = CHANNEL_SETSPEED(c->methods, 524 c->devinfo, newspd); 525 } 526 527 ret = chn_reset(c, c->format, newspd); 528 if (ret == 0) { 529 *vchanrate = c->speed; 530 if (restart != 0) { 531 CHN_FOREACH(ch, c, children.busy) { 532 CHN_LOCK(ch); 533 if (VCHAN_SYNC_REQUIRED(ch)) 534 vchan_sync(ch); 535 CHN_UNLOCK(ch); 536 } 537 c->flags |= CHN_F_DIRTY; 538 ret = chn_start(c, 1); 539 } 540 } 541 } 542 543 CHN_UNLOCK(c); 544 545 PCM_RELEASE_QUICK(d); 546 547 return (ret); 548 } 549 550 static int 551 sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) 552 { 553 struct snddev_info *d; 554 struct pcm_channel *c, *ch; 555 uint32_t newfmt; 556 int *vchanformat, vchancount, direction, ret, restart; 557 char fmtstr[AFMTSTR_LEN]; 558 559 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 560 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 561 return (EINVAL); 562 563 PCM_LOCK(d); 564 PCM_WAIT(d); 565 566 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 567 case VCHAN_PLAY: 568 direction = PCMDIR_PLAY; 569 vchancount = d->pvchancount; 570 vchanformat = &d->pvchanformat; 571 break; 572 case VCHAN_REC: 573 direction = PCMDIR_REC; 574 vchancount = d->rvchancount; 575 vchanformat = &d->rvchanformat; 576 break; 577 default: 578 PCM_UNLOCK(d); 579 return (EINVAL); 580 break; 581 } 582 583 if (vchancount < 1) { 584 PCM_UNLOCK(d); 585 return (EINVAL); 586 } 587 588 PCM_ACQUIRE(d); 589 PCM_UNLOCK(d); 590 591 if (direction == PCMDIR_PLAY) 592 pcm_getparentchannel(d, &c, NULL); 593 else 594 pcm_getparentchannel(d, NULL, &c); 595 596 if (c == NULL) { 597 PCM_RELEASE_QUICK(d); 598 return (EINVAL); 599 } 600 601 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 602 __func__, direction, c->direction)); 603 604 CHN_LOCK(c); 605 606 bzero(fmtstr, sizeof(fmtstr)); 607 608 if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format) 609 strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr)); 610 611 CHN_UNLOCK(c); 612 613 ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 614 if (ret != 0 || req->newptr == NULL) { 615 PCM_RELEASE_QUICK(d); 616 return (ret); 617 } 618 619 newfmt = snd_str2afmt(fmtstr); 620 if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) { 621 PCM_RELEASE_QUICK(d); 622 return (EINVAL); 623 } 624 625 CHN_LOCK(c); 626 627 if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) { 628 if (CHN_STARTED(c)) { 629 chn_abort(c); 630 restart = 1; 631 } else 632 restart = 0; 633 634 ret = chn_reset(c, newfmt, c->speed); 635 if (ret == 0) { 636 *vchanformat = c->format; 637 if (restart != 0) { 638 CHN_FOREACH(ch, c, children.busy) { 639 CHN_LOCK(ch); 640 if (VCHAN_SYNC_REQUIRED(ch)) 641 vchan_sync(ch); 642 CHN_UNLOCK(ch); 643 } 644 c->flags |= CHN_F_DIRTY; 645 ret = chn_start(c, 1); 646 } 647 } 648 } 649 650 CHN_UNLOCK(c); 651 652 PCM_RELEASE_QUICK(d); 653 654 return (ret); 655 } 656 657 /* virtual channel interface */ 658 659 #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 660 "play.vchanformat" : "rec.vchanformat" 661 #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 662 "play.vchanrate" : "rec.vchanrate" 663 664 int 665 vchan_create(struct pcm_channel *parent, int num) 666 { 667 struct snddev_info *d; 668 struct pcm_channel *ch; 669 struct pcmchan_caps *parent_caps; 670 uint32_t vchanfmt, vchanspd; 671 int ret, direction, r, save; 672 673 d = parent->parentsnddev; 674 675 PCM_BUSYASSERT(d); 676 CHN_LOCKASSERT(parent); 677 678 if (!(parent->flags & CHN_F_BUSY)) 679 return (EBUSY); 680 681 if (!(parent->direction == PCMDIR_PLAY || 682 parent->direction == PCMDIR_REC)) 683 return (EINVAL); 684 685 d = parent->parentsnddev; 686 687 CHN_UNLOCK(parent); 688 PCM_LOCK(d); 689 690 if (parent->direction == PCMDIR_PLAY) { 691 direction = PCMDIR_PLAY_VIRTUAL; 692 vchanfmt = d->pvchanformat; 693 vchanspd = d->pvchanrate; 694 } else { 695 direction = PCMDIR_REC_VIRTUAL; 696 vchanfmt = d->rvchanformat; 697 vchanspd = d->rvchanrate; 698 } 699 700 /* create a new playback channel */ 701 ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent); 702 if (ch == NULL) { 703 PCM_UNLOCK(d); 704 CHN_LOCK(parent); 705 return (ENODEV); 706 } 707 708 /* add us to our grandparent's channel list */ 709 ret = pcm_chn_add(d, ch); 710 PCM_UNLOCK(d); 711 if (ret != 0) { 712 pcm_chn_destroy(ch); 713 CHN_LOCK(parent); 714 return (ret); 715 } 716 717 CHN_LOCK(parent); 718 /* 719 * Add us to our parent channel's children in reverse order 720 * so future destruction will pick the last (biggest number) 721 * channel. 722 */ 723 CHN_INSERT_SORT_DESCEND(parent, ch, children); 724 725 if (parent->flags & CHN_F_HAS_VCHAN) 726 return (0); 727 728 parent->flags |= CHN_F_HAS_VCHAN; 729 730 parent_caps = chn_getcaps(parent); 731 if (parent_caps == NULL) 732 ret = EINVAL; 733 734 save = 0; 735 736 if (ret == 0 && vchanfmt == 0) { 737 const char *vfmt; 738 739 CHN_UNLOCK(parent); 740 r = resource_string_value(device_get_name(parent->dev), 741 device_get_unit(parent->dev), VCHAN_FMT_HINT(direction), 742 &vfmt); 743 CHN_LOCK(parent); 744 if (r != 0) 745 vfmt = NULL; 746 if (vfmt != NULL) { 747 vchanfmt = snd_str2afmt(vfmt); 748 if (vchanfmt != 0 && !(vchanfmt & AFMT_VCHAN)) 749 vchanfmt = 0; 750 } 751 if (vchanfmt == 0) 752 vchanfmt = VCHAN_DEFAULT_FORMAT; 753 save = 1; 754 } 755 756 if (ret == 0 && vchanspd == 0) { 757 /* 758 * This is very sad. Few soundcards advertised as being 759 * able to do (insanely) higher/lower speed, but in 760 * reality, they simply can't. At least, we give user chance 761 * to set sane value via kernel hints or sysctl. 762 */ 763 CHN_UNLOCK(parent); 764 r = resource_int_value(device_get_name(parent->dev), 765 device_get_unit(parent->dev), VCHAN_SPD_HINT(direction), 766 &vchanspd); 767 CHN_LOCK(parent); 768 if (r != 0) { 769 /* 770 * No saved value, no hint, NOTHING. 771 * 772 * Workaround for sb16 running 773 * poorly at 45k / 49k. 774 */ 775 switch (parent_caps->maxspeed) { 776 case 45000: 777 case 49000: 778 vchanspd = 44100; 779 break; 780 default: 781 vchanspd = VCHAN_DEFAULT_RATE; 782 if (vchanspd > parent_caps->maxspeed) 783 vchanspd = parent_caps->maxspeed; 784 break; 785 } 786 if (vchanspd < parent_caps->minspeed) 787 vchanspd = parent_caps->minspeed; 788 } 789 save = 1; 790 } 791 792 if (ret == 0) { 793 /* 794 * Limit the speed between feeder_rate_min <-> feeder_rate_max. 795 */ 796 if (vchanspd < feeder_rate_min) 797 vchanspd = feeder_rate_min; 798 if (vchanspd > feeder_rate_max) 799 vchanspd = feeder_rate_max; 800 801 if (feeder_rate_round) { 802 RANGE(vchanspd, parent_caps->minspeed, 803 parent_caps->maxspeed); 804 vchanspd = CHANNEL_SETSPEED(parent->methods, 805 parent->devinfo, vchanspd); 806 } 807 808 ret = chn_reset(parent, vchanfmt, vchanspd); 809 } 810 811 if (ret == 0 && save) { 812 /* 813 * Save new value. 814 */ 815 if (direction == PCMDIR_PLAY_VIRTUAL) { 816 d->pvchanformat = parent->format; 817 d->pvchanrate = parent->speed; 818 } else { 819 d->rvchanformat = parent->format; 820 d->rvchanrate = parent->speed; 821 } 822 } 823 824 /* 825 * If the parent channel supports digital format, 826 * enable passthrough mode. 827 */ 828 if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) { 829 parent->flags &= ~CHN_F_VCHAN_DYNAMIC; 830 parent->flags |= CHN_F_VCHAN_PASSTHROUGH; 831 } 832 833 if (ret != 0) { 834 CHN_REMOVE(parent, ch, children); 835 parent->flags &= ~CHN_F_HAS_VCHAN; 836 CHN_UNLOCK(parent); 837 PCM_LOCK(d); 838 if (pcm_chn_remove(d, ch) == 0) { 839 PCM_UNLOCK(d); 840 pcm_chn_destroy(ch); 841 } else 842 PCM_UNLOCK(d); 843 CHN_LOCK(parent); 844 } 845 846 return (ret); 847 } 848 849 int 850 vchan_destroy(struct pcm_channel *c) 851 { 852 struct pcm_channel *parent; 853 struct snddev_info *d; 854 int ret; 855 856 KASSERT(c != NULL && c->parentchannel != NULL && 857 c->parentsnddev != NULL, ("%s(): invalid channel=%p", 858 __func__, c)); 859 860 CHN_LOCKASSERT(c); 861 862 d = c->parentsnddev; 863 parent = c->parentchannel; 864 865 PCM_BUSYASSERT(d); 866 CHN_LOCKASSERT(parent); 867 868 CHN_UNLOCK(c); 869 870 if (!(parent->flags & CHN_F_BUSY)) 871 return (EBUSY); 872 873 if (CHN_EMPTY(parent, children)) 874 return (EINVAL); 875 876 /* remove us from our parent's children list */ 877 CHN_REMOVE(parent, c, children); 878 879 if (CHN_EMPTY(parent, children)) { 880 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 881 chn_reset(parent, parent->format, parent->speed); 882 } 883 884 CHN_UNLOCK(parent); 885 886 /* remove us from our grandparent's channel list */ 887 PCM_LOCK(d); 888 ret = pcm_chn_remove(d, c); 889 PCM_UNLOCK(d); 890 891 /* destroy ourselves */ 892 if (ret == 0) 893 ret = pcm_chn_destroy(c); 894 895 CHN_LOCK(parent); 896 897 return (ret); 898 } 899 900 int 901 #ifdef SND_DEBUG 902 vchan_passthrough(struct pcm_channel *c, const char *caller) 903 #else 904 vchan_sync(struct pcm_channel *c) 905 #endif 906 { 907 int ret; 908 909 KASSERT(c != NULL && c->parentchannel != NULL && 910 (c->flags & CHN_F_VIRTUAL), 911 ("%s(): invalid passthrough", __func__)); 912 CHN_LOCKASSERT(c); 913 CHN_LOCKASSERT(c->parentchannel); 914 915 sndbuf_setspd(c->bufhard, c->parentchannel->speed); 916 c->flags |= CHN_F_PASSTHROUGH; 917 ret = feeder_chain(c); 918 c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH); 919 if (ret != 0) 920 c->flags |= CHN_F_DIRTY; 921 922 #ifdef SND_DEBUG 923 if (snd_passthrough_verbose != 0) { 924 char *devname, buf[CHN_NAMELEN]; 925 926 devname = dsp_unit2name(buf, sizeof(buf), c->unit); 927 device_printf(c->dev, 928 "%s(%s/%s) %s() -> re-sync err=%d\n", 929 __func__, (devname != NULL) ? devname : "dspX", c->comm, 930 caller, ret); 931 } 932 #endif 933 934 return (ret); 935 } 936 937 void 938 vchan_initsys(device_t dev) 939 { 940 struct snddev_info *d; 941 int unit; 942 943 unit = device_get_unit(dev); 944 d = device_get_softc(dev); 945 946 /* Play */ 947 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 948 SYSCTL_CHILDREN(d->play_sysctl_tree), 949 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN, 950 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 951 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 952 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 953 SYSCTL_CHILDREN(d->play_sysctl_tree), 954 OID_AUTO, "vchanmode", CTLTYPE_STRING | CTLFLAG_RWTUN, 955 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 956 sysctl_dev_pcm_vchanmode, "A", 957 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 958 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 959 SYSCTL_CHILDREN(d->play_sysctl_tree), 960 OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RWTUN, 961 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 962 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 963 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 964 SYSCTL_CHILDREN(d->play_sysctl_tree), 965 OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RWTUN, 966 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 967 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 968 /* Rec */ 969 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 970 SYSCTL_CHILDREN(d->rec_sysctl_tree), 971 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN, 972 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 973 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 974 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 975 SYSCTL_CHILDREN(d->rec_sysctl_tree), 976 OID_AUTO, "vchanmode", CTLTYPE_STRING | CTLFLAG_RWTUN, 977 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 978 sysctl_dev_pcm_vchanmode, "A", 979 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 980 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 981 SYSCTL_CHILDREN(d->rec_sysctl_tree), 982 OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RWTUN, 983 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 984 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 985 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 986 SYSCTL_CHILDREN(d->rec_sysctl_tree), 987 OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RWTUN, 988 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 989 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 990 } 991