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