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-2025 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 bus_topo_lock(); 263 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 264 if (!PCM_REGISTERED(d)) { 265 bus_topo_unlock(); 266 return (EINVAL); 267 } 268 bus_topo_unlock(); 269 270 PCM_LOCK(d); 271 PCM_WAIT(d); 272 273 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 274 case VCHAN_PLAY: 275 /* Exit if we do not support this direction. */ 276 if (d->playcount < 1) { 277 PCM_UNLOCK(d); 278 return (ENODEV); 279 } 280 flag = SD_F_PVCHANS; 281 break; 282 case VCHAN_REC: 283 if (d->reccount < 1) { 284 PCM_UNLOCK(d); 285 return (ENODEV); 286 } 287 flag = SD_F_RVCHANS; 288 break; 289 default: 290 PCM_UNLOCK(d); 291 return (EINVAL); 292 } 293 294 enabled = (d->flags & flag) != 0; 295 296 PCM_ACQUIRE(d); 297 PCM_UNLOCK(d); 298 299 err = sysctl_handle_int(oidp, &enabled, 0, req); 300 if (err != 0 || req->newptr == NULL) { 301 PCM_RELEASE_QUICK(d); 302 return (err); 303 } 304 305 if (enabled <= 0) 306 d->flags &= ~flag; 307 else 308 d->flags |= flag; 309 310 PCM_RELEASE_QUICK(d); 311 312 return (0); 313 } 314 315 static int 316 sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS) 317 { 318 struct snddev_info *d; 319 struct pcm_channel *c; 320 uint32_t dflags; 321 int *vchanmode, direction, ret; 322 char dtype[16]; 323 324 bus_topo_lock(); 325 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 326 if (!PCM_REGISTERED(d)) { 327 bus_topo_unlock(); 328 return (EINVAL); 329 } 330 bus_topo_unlock(); 331 332 PCM_LOCK(d); 333 PCM_WAIT(d); 334 335 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 336 case VCHAN_PLAY: 337 if ((d->flags & SD_F_PVCHANS) == 0) { 338 PCM_UNLOCK(d); 339 return (ENODEV); 340 } 341 direction = PCMDIR_PLAY; 342 vchanmode = &d->pvchanmode; 343 break; 344 case VCHAN_REC: 345 if ((d->flags & SD_F_RVCHANS) == 0) { 346 PCM_UNLOCK(d); 347 return (ENODEV); 348 } 349 direction = PCMDIR_REC; 350 vchanmode = &d->rvchanmode; 351 break; 352 default: 353 PCM_UNLOCK(d); 354 return (EINVAL); 355 } 356 357 PCM_ACQUIRE(d); 358 PCM_UNLOCK(d); 359 360 if (*vchanmode & CHN_F_VCHAN_PASSTHROUGH) 361 strlcpy(dtype, "passthrough", sizeof(dtype)); 362 else if (*vchanmode & CHN_F_VCHAN_ADAPTIVE) 363 strlcpy(dtype, "adaptive", sizeof(dtype)); 364 else 365 strlcpy(dtype, "fixed", sizeof(dtype)); 366 367 ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req); 368 if (ret != 0 || req->newptr == NULL) { 369 PCM_RELEASE_QUICK(d); 370 return (ret); 371 } 372 373 if (strcasecmp(dtype, "passthrough") == 0 || strcmp(dtype, "1") == 0) 374 dflags = CHN_F_VCHAN_PASSTHROUGH; 375 else if (strcasecmp(dtype, "adaptive") == 0 || strcmp(dtype, "2") == 0) 376 dflags = CHN_F_VCHAN_ADAPTIVE; 377 else if (strcasecmp(dtype, "fixed") == 0 || strcmp(dtype, "0") == 0) 378 dflags = 0; 379 else { 380 PCM_RELEASE_QUICK(d); 381 return (EINVAL); 382 } 383 384 CHN_FOREACH(c, d, channels.pcm.primary) { 385 CHN_LOCK(c); 386 if (c->direction != direction || 387 dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) || 388 (c->flags & CHN_F_PASSTHROUGH)) { 389 CHN_UNLOCK(c); 390 continue; 391 } 392 c->flags &= ~CHN_F_VCHAN_DYNAMIC; 393 c->flags |= dflags; 394 CHN_UNLOCK(c); 395 *vchanmode = dflags; 396 } 397 398 PCM_RELEASE_QUICK(d); 399 400 return (ret); 401 } 402 403 /* 404 * On the fly vchan rate/format settings 405 */ 406 407 #define VCHAN_ACCESSIBLE(c) (!((c)->flags & (CHN_F_PASSTHROUGH | \ 408 CHN_F_EXCLUSIVE)) && \ 409 (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \ 410 CHN_STOPPED(c))) 411 static int 412 sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS) 413 { 414 struct snddev_info *d; 415 struct pcm_channel *c, *ch; 416 int *vchanrate, direction, ret, newspd, restart; 417 418 bus_topo_lock(); 419 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 420 if (!PCM_REGISTERED(d)) { 421 bus_topo_unlock(); 422 return (EINVAL); 423 } 424 bus_topo_unlock(); 425 426 PCM_LOCK(d); 427 PCM_WAIT(d); 428 429 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 430 case VCHAN_PLAY: 431 if ((d->flags & SD_F_PVCHANS) == 0) { 432 PCM_UNLOCK(d); 433 return (ENODEV); 434 } 435 direction = PCMDIR_PLAY; 436 vchanrate = &d->pvchanrate; 437 break; 438 case VCHAN_REC: 439 if ((d->flags & SD_F_RVCHANS) == 0) { 440 PCM_UNLOCK(d); 441 return (ENODEV); 442 } 443 direction = PCMDIR_REC; 444 vchanrate = &d->rvchanrate; 445 break; 446 default: 447 PCM_UNLOCK(d); 448 return (EINVAL); 449 } 450 451 PCM_ACQUIRE(d); 452 PCM_UNLOCK(d); 453 454 newspd = *vchanrate; 455 456 ret = sysctl_handle_int(oidp, &newspd, 0, req); 457 if (ret != 0 || req->newptr == NULL) { 458 PCM_RELEASE_QUICK(d); 459 return (ret); 460 } 461 462 if (newspd < feeder_rate_min || newspd > feeder_rate_max) { 463 PCM_RELEASE_QUICK(d); 464 return (EINVAL); 465 } 466 467 CHN_FOREACH(c, d, channels.pcm.primary) { 468 CHN_LOCK(c); 469 if (c->direction != direction) { 470 CHN_UNLOCK(c); 471 continue; 472 } 473 474 if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) { 475 if (CHN_STARTED(c)) { 476 chn_abort(c); 477 restart = 1; 478 } else 479 restart = 0; 480 481 ret = chn_reset(c, c->format, newspd); 482 if (ret == 0) { 483 if (restart != 0) { 484 CHN_FOREACH(ch, c, children.busy) { 485 CHN_LOCK(ch); 486 if (VCHAN_SYNC_REQUIRED(ch)) 487 vchan_sync(ch); 488 CHN_UNLOCK(ch); 489 } 490 c->flags |= CHN_F_DIRTY; 491 ret = chn_start(c, 1); 492 } 493 } 494 } 495 *vchanrate = sndbuf_getspd(c->bufsoft); 496 497 CHN_UNLOCK(c); 498 } 499 500 PCM_RELEASE_QUICK(d); 501 502 return (ret); 503 } 504 505 static int 506 sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) 507 { 508 struct snddev_info *d; 509 struct pcm_channel *c, *ch; 510 uint32_t newfmt; 511 int *vchanformat, direction, ret, restart; 512 char fmtstr[AFMTSTR_LEN]; 513 514 bus_topo_lock(); 515 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 516 if (!PCM_REGISTERED(d)) { 517 bus_topo_unlock(); 518 return (EINVAL); 519 } 520 bus_topo_unlock(); 521 522 PCM_LOCK(d); 523 PCM_WAIT(d); 524 525 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 526 case VCHAN_PLAY: 527 if ((d->flags & SD_F_PVCHANS) == 0) { 528 PCM_UNLOCK(d); 529 return (ENODEV); 530 } 531 direction = PCMDIR_PLAY; 532 vchanformat = &d->pvchanformat; 533 break; 534 case VCHAN_REC: 535 if ((d->flags & SD_F_RVCHANS) == 0) { 536 PCM_UNLOCK(d); 537 return (ENODEV); 538 } 539 direction = PCMDIR_REC; 540 vchanformat = &d->rvchanformat; 541 break; 542 default: 543 PCM_UNLOCK(d); 544 return (EINVAL); 545 } 546 547 PCM_ACQUIRE(d); 548 PCM_UNLOCK(d); 549 550 bzero(fmtstr, sizeof(fmtstr)); 551 552 if (snd_afmt2str(*vchanformat, fmtstr, sizeof(fmtstr)) != *vchanformat) 553 strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr)); 554 555 ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 556 if (ret != 0 || req->newptr == NULL) { 557 PCM_RELEASE_QUICK(d); 558 return (ret); 559 } 560 561 newfmt = snd_str2afmt(fmtstr); 562 if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) { 563 PCM_RELEASE_QUICK(d); 564 return (EINVAL); 565 } 566 567 CHN_FOREACH(c, d, channels.pcm.primary) { 568 CHN_LOCK(c); 569 if (c->direction != direction) { 570 CHN_UNLOCK(c); 571 continue; 572 } 573 if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) { 574 if (CHN_STARTED(c)) { 575 chn_abort(c); 576 restart = 1; 577 } else 578 restart = 0; 579 580 ret = chn_reset(c, newfmt, c->speed); 581 if (ret == 0) { 582 if (restart != 0) { 583 CHN_FOREACH(ch, c, children.busy) { 584 CHN_LOCK(ch); 585 if (VCHAN_SYNC_REQUIRED(ch)) 586 vchan_sync(ch); 587 CHN_UNLOCK(ch); 588 } 589 c->flags |= CHN_F_DIRTY; 590 ret = chn_start(c, 1); 591 } 592 } 593 } 594 *vchanformat = sndbuf_getfmt(c->bufsoft); 595 596 CHN_UNLOCK(c); 597 } 598 599 PCM_RELEASE_QUICK(d); 600 601 return (ret); 602 } 603 604 /* virtual channel interface */ 605 606 #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 607 "play.vchanformat" : "rec.vchanformat" 608 #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 609 "play.vchanrate" : "rec.vchanrate" 610 611 int 612 vchan_create(struct pcm_channel *parent, struct pcm_channel **child) 613 { 614 struct snddev_info *d; 615 struct pcm_channel *ch; 616 struct pcmchan_caps *parent_caps; 617 uint32_t vchanfmt, vchanspd; 618 int ret, direction; 619 620 ret = 0; 621 d = parent->parentsnddev; 622 623 PCM_BUSYASSERT(d); 624 CHN_LOCKASSERT(parent); 625 626 if (!(parent->direction == PCMDIR_PLAY || 627 parent->direction == PCMDIR_REC)) 628 return (EINVAL); 629 630 CHN_UNLOCK(parent); 631 PCM_LOCK(d); 632 633 if (parent->direction == PCMDIR_PLAY) { 634 direction = PCMDIR_PLAY_VIRTUAL; 635 vchanfmt = d->pvchanformat; 636 vchanspd = d->pvchanrate; 637 } else { 638 direction = PCMDIR_REC_VIRTUAL; 639 vchanfmt = d->rvchanformat; 640 vchanspd = d->rvchanrate; 641 } 642 643 /* create a new playback channel */ 644 ch = chn_init(d, parent, &vchan_class, direction, parent); 645 if (ch == NULL) { 646 PCM_UNLOCK(d); 647 CHN_LOCK(parent); 648 return (ENODEV); 649 } 650 PCM_UNLOCK(d); 651 652 CHN_LOCK(parent); 653 CHN_INSERT_SORT_ASCEND(parent, ch, children); 654 655 *child = ch; 656 657 if (parent->flags & CHN_F_HAS_VCHAN) 658 return (0); 659 660 parent->flags |= CHN_F_HAS_VCHAN | CHN_F_BUSY; 661 662 parent_caps = chn_getcaps(parent); 663 if (parent_caps == NULL) { 664 ret = EINVAL; 665 goto fail; 666 } 667 668 if ((ret = chn_reset(parent, vchanfmt, vchanspd)) != 0) 669 goto fail; 670 671 /* 672 * If the parent channel supports digital format, 673 * enable passthrough mode. 674 */ 675 if (snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) { 676 parent->flags &= ~CHN_F_VCHAN_DYNAMIC; 677 parent->flags |= CHN_F_VCHAN_PASSTHROUGH; 678 } 679 680 return (ret); 681 682 fail: 683 CHN_LOCK(ch); 684 vchan_destroy(ch); 685 *child = NULL; 686 687 return (ret); 688 } 689 690 int 691 vchan_destroy(struct pcm_channel *c) 692 { 693 struct pcm_channel *parent; 694 695 KASSERT(c != NULL && c->parentchannel != NULL && 696 c->parentsnddev != NULL, ("%s(): invalid channel=%p", 697 __func__, c)); 698 699 CHN_LOCKASSERT(c); 700 701 parent = c->parentchannel; 702 703 PCM_BUSYASSERT(c->parentsnddev); 704 CHN_LOCKASSERT(parent); 705 706 CHN_UNLOCK(c); 707 708 /* remove us from our parent's children list */ 709 CHN_REMOVE(parent, c, children); 710 if (CHN_EMPTY(parent, children)) { 711 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 712 chn_reset(parent, parent->format, parent->speed); 713 } 714 715 CHN_UNLOCK(parent); 716 717 /* destroy ourselves */ 718 chn_kill(c); 719 720 CHN_LOCK(parent); 721 722 return (0); 723 } 724 725 int 726 #ifdef SND_DEBUG 727 vchan_passthrough(struct pcm_channel *c, const char *caller) 728 #else 729 vchan_sync(struct pcm_channel *c) 730 #endif 731 { 732 int ret; 733 734 KASSERT(c != NULL && c->parentchannel != NULL && 735 (c->flags & CHN_F_VIRTUAL), 736 ("%s(): invalid passthrough", __func__)); 737 CHN_LOCKASSERT(c); 738 CHN_LOCKASSERT(c->parentchannel); 739 740 sndbuf_setspd(c->bufhard, c->parentchannel->speed); 741 c->flags |= CHN_F_PASSTHROUGH; 742 ret = feeder_chain(c); 743 c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH); 744 if (ret != 0) 745 c->flags |= CHN_F_DIRTY; 746 747 #ifdef SND_DEBUG 748 if (snd_passthrough_verbose) { 749 device_printf(c->dev, "%s(%s/%s) %s() -> re-sync err=%d\n", 750 __func__, c->name, c->comm, caller, ret); 751 } 752 #endif 753 754 return (ret); 755 } 756 757 static int 758 sysctl_hw_snd_vchans_enable(SYSCTL_HANDLER_ARGS) 759 { 760 struct snddev_info *d; 761 int i, v, error; 762 763 v = snd_vchans_enable; 764 error = sysctl_handle_int(oidp, &v, 0, req); 765 if (error != 0 || req->newptr == NULL) 766 return (error); 767 768 bus_topo_lock(); 769 snd_vchans_enable = v >= 1; 770 771 for (i = 0; pcm_devclass != NULL && 772 i < devclass_get_maxunit(pcm_devclass); i++) { 773 d = devclass_get_softc(pcm_devclass, i); 774 if (!PCM_REGISTERED(d)) 775 continue; 776 PCM_ACQUIRE_QUICK(d); 777 if (snd_vchans_enable) { 778 if (d->playcount > 0) 779 d->flags |= SD_F_PVCHANS; 780 if (d->reccount > 0) 781 d->flags |= SD_F_RVCHANS; 782 } else 783 d->flags &= ~(SD_F_PVCHANS | SD_F_RVCHANS); 784 PCM_RELEASE_QUICK(d); 785 } 786 bus_topo_unlock(); 787 788 return (0); 789 } 790 SYSCTL_PROC(_hw_snd, OID_AUTO, vchans_enable, 791 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int), 792 sysctl_hw_snd_vchans_enable, "I", "global virtual channel switch"); 793 794 void 795 vchan_initsys(device_t dev) 796 { 797 struct snddev_info *d; 798 int unit; 799 800 unit = device_get_unit(dev); 801 d = device_get_softc(dev); 802 803 /* Play */ 804 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 805 SYSCTL_CHILDREN(d->play_sysctl_tree), 806 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 807 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 808 sysctl_dev_pcm_vchans, "I", "virtual channels enabled"); 809 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 810 SYSCTL_CHILDREN(d->play_sysctl_tree), 811 OID_AUTO, "vchanmode", 812 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 813 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 814 sysctl_dev_pcm_vchanmode, "A", 815 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 816 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 817 SYSCTL_CHILDREN(d->play_sysctl_tree), 818 OID_AUTO, "vchanrate", 819 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 820 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 821 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 822 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 823 SYSCTL_CHILDREN(d->play_sysctl_tree), 824 OID_AUTO, "vchanformat", 825 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 826 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 827 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 828 /* Rec */ 829 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 830 SYSCTL_CHILDREN(d->rec_sysctl_tree), 831 OID_AUTO, "vchans", 832 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 833 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 834 sysctl_dev_pcm_vchans, "I", "virtual channels enabled"); 835 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 836 SYSCTL_CHILDREN(d->rec_sysctl_tree), 837 OID_AUTO, "vchanmode", 838 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 839 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 840 sysctl_dev_pcm_vchanmode, "A", 841 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 842 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 843 SYSCTL_CHILDREN(d->rec_sysctl_tree), 844 OID_AUTO, "vchanrate", 845 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 846 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 847 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 848 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 849 SYSCTL_CHILDREN(d->rec_sysctl_tree), 850 OID_AUTO, "vchanformat", 851 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 852 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 853 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 854 } 855