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 struct vchan_info { 51 struct pcm_channel *channel; 52 struct pcmchan_caps caps; 53 uint32_t fmtlist[FMTLIST_MAX]; 54 int trigger; 55 }; 56 57 bool snd_vchans_enable = true; 58 59 static void * 60 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 61 struct pcm_channel *c, int dir) 62 { 63 struct vchan_info *info; 64 struct pcm_channel *p; 65 uint32_t i, j, *fmtlist; 66 67 KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC, 68 ("vchan_init: bad direction")); 69 KASSERT(c != NULL && c->parentchannel != NULL, 70 ("vchan_init: bad channels")); 71 72 info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO); 73 info->channel = c; 74 info->trigger = PCMTRIG_STOP; 75 p = c->parentchannel; 76 77 CHN_LOCK(p); 78 79 fmtlist = chn_getcaps(p)->fmtlist; 80 for (i = 0, j = 0; fmtlist[i] != 0 && j < DIGFMTS_MAX; i++) { 81 if (fmtlist[i] & AFMT_PASSTHROUGH) 82 info->fmtlist[j++] = fmtlist[i]; 83 } 84 if (p->format & AFMT_VCHAN) 85 info->fmtlist[j] = p->format; 86 else 87 info->fmtlist[j] = VCHAN_DEFAULT_FORMAT; 88 info->caps.fmtlist = info->fmtlist + 89 ((p->flags & CHN_F_VCHAN_DYNAMIC) ? 0 : FMTLIST_OFFSET); 90 91 CHN_UNLOCK(p); 92 93 c->flags |= CHN_F_VIRTUAL; 94 95 return (info); 96 } 97 98 static int 99 vchan_free(kobj_t obj, void *data) 100 { 101 102 free(data, M_DEVBUF); 103 104 return (0); 105 } 106 107 static int 108 vchan_setformat(kobj_t obj, void *data, uint32_t format) 109 { 110 struct vchan_info *info; 111 112 info = data; 113 114 CHN_LOCKASSERT(info->channel); 115 116 if (!snd_fmtvalid(format, info->caps.fmtlist)) 117 return (-1); 118 119 return (0); 120 } 121 122 static uint32_t 123 vchan_setspeed(kobj_t obj, void *data, uint32_t speed) 124 { 125 struct vchan_info *info; 126 127 info = data; 128 129 CHN_LOCKASSERT(info->channel); 130 131 return (info->caps.maxspeed); 132 } 133 134 static int 135 vchan_trigger(kobj_t obj, void *data, int go) 136 { 137 struct vchan_info *info; 138 struct pcm_channel *c, *p; 139 int ret, otrigger; 140 141 info = data; 142 c = info->channel; 143 p = c->parentchannel; 144 145 CHN_LOCKASSERT(c); 146 if (!PCMTRIG_COMMON(go) || go == info->trigger) 147 return (0); 148 149 CHN_UNLOCK(c); 150 CHN_LOCK(p); 151 152 otrigger = info->trigger; 153 info->trigger = go; 154 155 switch (go) { 156 case PCMTRIG_START: 157 if (otrigger != PCMTRIG_START) 158 CHN_INSERT_HEAD(p, c, children.busy); 159 break; 160 case PCMTRIG_STOP: 161 case PCMTRIG_ABORT: 162 if (otrigger == PCMTRIG_START) 163 CHN_REMOVE(p, c, children.busy); 164 break; 165 default: 166 break; 167 } 168 169 ret = chn_notify(p, CHN_N_TRIGGER); 170 171 CHN_LOCK(c); 172 173 if (ret == 0 && go == PCMTRIG_START && VCHAN_SYNC_REQUIRED(c)) 174 ret = vchan_sync(c); 175 176 CHN_UNLOCK(c); 177 CHN_UNLOCK(p); 178 CHN_LOCK(c); 179 180 return (ret); 181 } 182 183 static struct pcmchan_caps * 184 vchan_getcaps(kobj_t obj, void *data) 185 { 186 struct vchan_info *info; 187 struct pcm_channel *c; 188 uint32_t pformat, pspeed, pflags, i; 189 190 info = data; 191 c = info->channel; 192 pformat = c->parentchannel->format; 193 pspeed = c->parentchannel->speed; 194 pflags = c->parentchannel->flags; 195 196 CHN_LOCKASSERT(c); 197 198 if (pflags & CHN_F_VCHAN_DYNAMIC) { 199 info->caps.fmtlist = info->fmtlist; 200 if (pformat & AFMT_VCHAN) { 201 for (i = 0; info->caps.fmtlist[i] != 0; i++) { 202 if (info->caps.fmtlist[i] & AFMT_PASSTHROUGH) 203 continue; 204 break; 205 } 206 info->caps.fmtlist[i] = pformat; 207 } 208 if (c->format & AFMT_PASSTHROUGH) 209 info->caps.minspeed = c->speed; 210 else 211 info->caps.minspeed = pspeed; 212 info->caps.maxspeed = info->caps.minspeed; 213 } else { 214 info->caps.fmtlist = info->fmtlist + FMTLIST_OFFSET; 215 if (pformat & AFMT_VCHAN) 216 info->caps.fmtlist[0] = pformat; 217 else { 218 device_printf(c->dev, 219 "%s(): invalid vchan format 0x%08x", 220 __func__, pformat); 221 info->caps.fmtlist[0] = VCHAN_DEFAULT_FORMAT; 222 } 223 info->caps.minspeed = pspeed; 224 info->caps.maxspeed = info->caps.minspeed; 225 } 226 227 return (&info->caps); 228 } 229 230 static struct pcmchan_matrix * 231 vchan_getmatrix(kobj_t obj, void *data, uint32_t format) 232 { 233 234 return (feeder_matrix_format_map(format)); 235 } 236 237 static kobj_method_t vchan_methods[] = { 238 KOBJMETHOD(channel_init, vchan_init), 239 KOBJMETHOD(channel_free, vchan_free), 240 KOBJMETHOD(channel_setformat, vchan_setformat), 241 KOBJMETHOD(channel_setspeed, vchan_setspeed), 242 KOBJMETHOD(channel_trigger, vchan_trigger), 243 KOBJMETHOD(channel_getcaps, vchan_getcaps), 244 KOBJMETHOD(channel_getmatrix, vchan_getmatrix), 245 KOBJMETHOD_END 246 }; 247 CHANNEL_DECLARE(vchan); 248 249 static int 250 sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS) 251 { 252 struct snddev_info *d; 253 int err, enabled, flag; 254 255 bus_topo_lock(); 256 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 257 if (!PCM_REGISTERED(d)) { 258 bus_topo_unlock(); 259 return (EINVAL); 260 } 261 bus_topo_unlock(); 262 263 PCM_LOCK(d); 264 PCM_WAIT(d); 265 266 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 267 case VCHAN_PLAY: 268 /* Exit if we do not support this direction. */ 269 if (d->playcount < 1) { 270 PCM_UNLOCK(d); 271 return (ENODEV); 272 } 273 flag = SD_F_PVCHANS; 274 break; 275 case VCHAN_REC: 276 if (d->reccount < 1) { 277 PCM_UNLOCK(d); 278 return (ENODEV); 279 } 280 flag = SD_F_RVCHANS; 281 break; 282 default: 283 PCM_UNLOCK(d); 284 return (EINVAL); 285 } 286 287 enabled = (d->flags & flag) != 0; 288 289 PCM_ACQUIRE(d); 290 PCM_UNLOCK(d); 291 292 err = sysctl_handle_int(oidp, &enabled, 0, req); 293 if (err != 0 || req->newptr == NULL) { 294 PCM_RELEASE_QUICK(d); 295 return (err); 296 } 297 298 if (enabled <= 0) 299 d->flags &= ~flag; 300 else 301 d->flags |= flag; 302 303 PCM_RELEASE_QUICK(d); 304 305 return (0); 306 } 307 308 static int 309 sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS) 310 { 311 struct snddev_info *d; 312 struct pcm_channel *c; 313 uint32_t dflags; 314 int *vchanmode, direction, ret; 315 char dtype[16]; 316 317 bus_topo_lock(); 318 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 319 if (!PCM_REGISTERED(d)) { 320 bus_topo_unlock(); 321 return (EINVAL); 322 } 323 bus_topo_unlock(); 324 325 PCM_LOCK(d); 326 PCM_WAIT(d); 327 328 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 329 case VCHAN_PLAY: 330 if ((d->flags & SD_F_PVCHANS) == 0) { 331 PCM_UNLOCK(d); 332 return (ENODEV); 333 } 334 direction = PCMDIR_PLAY; 335 vchanmode = &d->pvchanmode; 336 break; 337 case VCHAN_REC: 338 if ((d->flags & SD_F_RVCHANS) == 0) { 339 PCM_UNLOCK(d); 340 return (ENODEV); 341 } 342 direction = PCMDIR_REC; 343 vchanmode = &d->rvchanmode; 344 break; 345 default: 346 PCM_UNLOCK(d); 347 return (EINVAL); 348 } 349 350 PCM_ACQUIRE(d); 351 PCM_UNLOCK(d); 352 353 if (*vchanmode & CHN_F_VCHAN_PASSTHROUGH) 354 strlcpy(dtype, "passthrough", sizeof(dtype)); 355 else if (*vchanmode & CHN_F_VCHAN_ADAPTIVE) 356 strlcpy(dtype, "adaptive", sizeof(dtype)); 357 else 358 strlcpy(dtype, "fixed", sizeof(dtype)); 359 360 ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req); 361 if (ret != 0 || req->newptr == NULL) { 362 PCM_RELEASE_QUICK(d); 363 return (ret); 364 } 365 366 if (strcasecmp(dtype, "passthrough") == 0 || strcmp(dtype, "1") == 0) 367 dflags = CHN_F_VCHAN_PASSTHROUGH; 368 else if (strcasecmp(dtype, "adaptive") == 0 || strcmp(dtype, "2") == 0) 369 dflags = CHN_F_VCHAN_ADAPTIVE; 370 else if (strcasecmp(dtype, "fixed") == 0 || strcmp(dtype, "0") == 0) 371 dflags = 0; 372 else { 373 PCM_RELEASE_QUICK(d); 374 return (EINVAL); 375 } 376 377 CHN_FOREACH(c, d, channels.pcm.primary) { 378 CHN_LOCK(c); 379 if (c->direction != direction || 380 dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) || 381 (c->flags & CHN_F_PASSTHROUGH)) { 382 CHN_UNLOCK(c); 383 continue; 384 } 385 c->flags &= ~CHN_F_VCHAN_DYNAMIC; 386 c->flags |= dflags; 387 CHN_UNLOCK(c); 388 *vchanmode = dflags; 389 } 390 391 PCM_RELEASE_QUICK(d); 392 393 return (ret); 394 } 395 396 /* 397 * On the fly vchan rate/format settings 398 */ 399 400 #define VCHAN_ACCESSIBLE(c) (!((c)->flags & (CHN_F_PASSTHROUGH | \ 401 CHN_F_EXCLUSIVE)) && \ 402 (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \ 403 CHN_STOPPED(c))) 404 static int 405 sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS) 406 { 407 struct snddev_info *d; 408 struct pcm_channel *c, *ch; 409 int *vchanrate, direction, ret, newspd, restart; 410 411 bus_topo_lock(); 412 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 413 if (!PCM_REGISTERED(d)) { 414 bus_topo_unlock(); 415 return (EINVAL); 416 } 417 bus_topo_unlock(); 418 419 PCM_LOCK(d); 420 PCM_WAIT(d); 421 422 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 423 case VCHAN_PLAY: 424 if ((d->flags & SD_F_PVCHANS) == 0) { 425 PCM_UNLOCK(d); 426 return (ENODEV); 427 } 428 direction = PCMDIR_PLAY; 429 vchanrate = &d->pvchanrate; 430 break; 431 case VCHAN_REC: 432 if ((d->flags & SD_F_RVCHANS) == 0) { 433 PCM_UNLOCK(d); 434 return (ENODEV); 435 } 436 direction = PCMDIR_REC; 437 vchanrate = &d->rvchanrate; 438 break; 439 default: 440 PCM_UNLOCK(d); 441 return (EINVAL); 442 } 443 444 PCM_ACQUIRE(d); 445 PCM_UNLOCK(d); 446 447 newspd = *vchanrate; 448 449 ret = sysctl_handle_int(oidp, &newspd, 0, req); 450 if (ret != 0 || req->newptr == NULL) { 451 PCM_RELEASE_QUICK(d); 452 return (ret); 453 } 454 455 if (newspd < feeder_rate_min || newspd > feeder_rate_max) { 456 PCM_RELEASE_QUICK(d); 457 return (EINVAL); 458 } 459 460 CHN_FOREACH(c, d, channels.pcm.primary) { 461 CHN_LOCK(c); 462 if (c->direction != direction) { 463 CHN_UNLOCK(c); 464 continue; 465 } 466 467 if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) { 468 if (CHN_STARTED(c)) { 469 chn_abort(c); 470 restart = 1; 471 } else 472 restart = 0; 473 474 ret = chn_reset(c, c->format, newspd); 475 if (ret == 0) { 476 if (restart != 0) { 477 CHN_FOREACH(ch, c, children.busy) { 478 CHN_LOCK(ch); 479 if (VCHAN_SYNC_REQUIRED(ch)) 480 vchan_sync(ch); 481 CHN_UNLOCK(ch); 482 } 483 c->flags |= CHN_F_DIRTY; 484 ret = chn_start(c, 1); 485 } 486 } 487 } 488 *vchanrate = c->bufsoft->spd; 489 490 CHN_UNLOCK(c); 491 } 492 493 PCM_RELEASE_QUICK(d); 494 495 return (ret); 496 } 497 498 static int 499 sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) 500 { 501 struct snddev_info *d; 502 struct pcm_channel *c, *ch; 503 uint32_t newfmt; 504 int *vchanformat, direction, ret, restart; 505 char fmtstr[AFMTSTR_LEN]; 506 507 bus_topo_lock(); 508 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 509 if (!PCM_REGISTERED(d)) { 510 bus_topo_unlock(); 511 return (EINVAL); 512 } 513 bus_topo_unlock(); 514 515 PCM_LOCK(d); 516 PCM_WAIT(d); 517 518 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 519 case VCHAN_PLAY: 520 if ((d->flags & SD_F_PVCHANS) == 0) { 521 PCM_UNLOCK(d); 522 return (ENODEV); 523 } 524 direction = PCMDIR_PLAY; 525 vchanformat = &d->pvchanformat; 526 break; 527 case VCHAN_REC: 528 if ((d->flags & SD_F_RVCHANS) == 0) { 529 PCM_UNLOCK(d); 530 return (ENODEV); 531 } 532 direction = PCMDIR_REC; 533 vchanformat = &d->rvchanformat; 534 break; 535 default: 536 PCM_UNLOCK(d); 537 return (EINVAL); 538 } 539 540 PCM_ACQUIRE(d); 541 PCM_UNLOCK(d); 542 543 bzero(fmtstr, sizeof(fmtstr)); 544 545 if (snd_afmt2str(*vchanformat, fmtstr, sizeof(fmtstr)) != *vchanformat) 546 strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr)); 547 548 ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 549 if (ret != 0 || req->newptr == NULL) { 550 PCM_RELEASE_QUICK(d); 551 return (ret); 552 } 553 554 newfmt = snd_str2afmt(fmtstr); 555 if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) { 556 PCM_RELEASE_QUICK(d); 557 return (EINVAL); 558 } 559 560 CHN_FOREACH(c, d, channels.pcm.primary) { 561 CHN_LOCK(c); 562 if (c->direction != direction) { 563 CHN_UNLOCK(c); 564 continue; 565 } 566 if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) { 567 if (CHN_STARTED(c)) { 568 chn_abort(c); 569 restart = 1; 570 } else 571 restart = 0; 572 573 ret = chn_reset(c, newfmt, c->speed); 574 if (ret == 0) { 575 if (restart != 0) { 576 CHN_FOREACH(ch, c, children.busy) { 577 CHN_LOCK(ch); 578 if (VCHAN_SYNC_REQUIRED(ch)) 579 vchan_sync(ch); 580 CHN_UNLOCK(ch); 581 } 582 c->flags |= CHN_F_DIRTY; 583 ret = chn_start(c, 1); 584 } 585 } 586 } 587 *vchanformat = c->bufsoft->fmt; 588 589 CHN_UNLOCK(c); 590 } 591 592 PCM_RELEASE_QUICK(d); 593 594 return (ret); 595 } 596 597 /* virtual channel interface */ 598 599 #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 600 "play.vchanformat" : "rec.vchanformat" 601 #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 602 "play.vchanrate" : "rec.vchanrate" 603 604 int 605 vchan_create(struct pcm_channel *parent, struct pcm_channel **child) 606 { 607 struct snddev_info *d; 608 struct pcm_channel *ch; 609 struct pcmchan_caps *parent_caps; 610 uint32_t vchanfmt, vchanspd; 611 int ret, direction; 612 613 ret = 0; 614 d = parent->parentsnddev; 615 616 PCM_BUSYASSERT(d); 617 CHN_LOCKASSERT(parent); 618 619 if (!(parent->direction == PCMDIR_PLAY || 620 parent->direction == PCMDIR_REC)) 621 return (EINVAL); 622 623 CHN_UNLOCK(parent); 624 PCM_LOCK(d); 625 626 if (parent->direction == PCMDIR_PLAY) { 627 direction = PCMDIR_PLAY_VIRTUAL; 628 vchanfmt = d->pvchanformat; 629 vchanspd = d->pvchanrate; 630 } else { 631 direction = PCMDIR_REC_VIRTUAL; 632 vchanfmt = d->rvchanformat; 633 vchanspd = d->rvchanrate; 634 } 635 636 /* create a new playback channel */ 637 ch = chn_init(d, parent, &vchan_class, direction, parent); 638 if (ch == NULL) { 639 PCM_UNLOCK(d); 640 CHN_LOCK(parent); 641 return (ENODEV); 642 } 643 PCM_UNLOCK(d); 644 645 CHN_LOCK(parent); 646 CHN_INSERT_SORT_ASCEND(parent, ch, children); 647 648 *child = ch; 649 650 if (parent->flags & CHN_F_HAS_VCHAN) 651 return (0); 652 653 parent->flags |= CHN_F_HAS_VCHAN | CHN_F_BUSY; 654 655 parent_caps = chn_getcaps(parent); 656 if (parent_caps == NULL) { 657 ret = EINVAL; 658 goto fail; 659 } 660 661 if ((ret = chn_reset(parent, vchanfmt, vchanspd)) != 0) 662 goto fail; 663 664 /* 665 * If the parent channel supports digital format, 666 * enable passthrough mode. 667 */ 668 if (snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) { 669 parent->flags &= ~CHN_F_VCHAN_DYNAMIC; 670 parent->flags |= CHN_F_VCHAN_PASSTHROUGH; 671 } 672 673 return (ret); 674 675 fail: 676 CHN_LOCK(ch); 677 vchan_destroy(ch); 678 *child = NULL; 679 680 return (ret); 681 } 682 683 int 684 vchan_destroy(struct pcm_channel *c) 685 { 686 struct pcm_channel *parent; 687 688 KASSERT(c != NULL && c->parentchannel != NULL && 689 c->parentsnddev != NULL, ("%s(): invalid channel=%p", 690 __func__, c)); 691 692 CHN_LOCKASSERT(c); 693 694 parent = c->parentchannel; 695 696 PCM_BUSYASSERT(c->parentsnddev); 697 CHN_LOCKASSERT(parent); 698 699 CHN_UNLOCK(c); 700 701 /* remove us from our parent's children list */ 702 CHN_REMOVE(parent, c, children); 703 if (CHN_EMPTY(parent, children)) { 704 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 705 chn_reset(parent, parent->format, parent->speed); 706 } 707 708 CHN_UNLOCK(parent); 709 710 /* destroy ourselves */ 711 chn_kill(c); 712 713 CHN_LOCK(parent); 714 715 return (0); 716 } 717 718 int 719 vchan_sync(struct pcm_channel *c) 720 { 721 int ret; 722 723 KASSERT(c != NULL && c->parentchannel != NULL && 724 (c->flags & CHN_F_VIRTUAL), 725 ("%s(): invalid passthrough", __func__)); 726 CHN_LOCKASSERT(c); 727 CHN_LOCKASSERT(c->parentchannel); 728 729 sndbuf_setspd(c->bufhard, c->parentchannel->speed); 730 c->flags |= CHN_F_PASSTHROUGH; 731 ret = feeder_chain(c); 732 c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH); 733 if (ret != 0) 734 c->flags |= CHN_F_DIRTY; 735 736 return (ret); 737 } 738 739 static int 740 sysctl_hw_snd_vchans_enable(SYSCTL_HANDLER_ARGS) 741 { 742 struct snddev_info *d; 743 int i, v, error; 744 745 v = snd_vchans_enable; 746 error = sysctl_handle_int(oidp, &v, 0, req); 747 if (error != 0 || req->newptr == NULL) 748 return (error); 749 750 bus_topo_lock(); 751 snd_vchans_enable = v >= 1; 752 753 for (i = 0; pcm_devclass != NULL && 754 i < devclass_get_maxunit(pcm_devclass); i++) { 755 d = devclass_get_softc(pcm_devclass, i); 756 if (!PCM_REGISTERED(d)) 757 continue; 758 PCM_ACQUIRE_QUICK(d); 759 if (snd_vchans_enable) { 760 if (d->playcount > 0) 761 d->flags |= SD_F_PVCHANS; 762 if (d->reccount > 0) 763 d->flags |= SD_F_RVCHANS; 764 } else 765 d->flags &= ~(SD_F_PVCHANS | SD_F_RVCHANS); 766 PCM_RELEASE_QUICK(d); 767 } 768 bus_topo_unlock(); 769 770 return (0); 771 } 772 SYSCTL_PROC(_hw_snd, OID_AUTO, vchans_enable, 773 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, sizeof(int), 774 sysctl_hw_snd_vchans_enable, "I", "global virtual channel switch"); 775 776 void 777 vchan_initsys(device_t dev) 778 { 779 struct snddev_info *d; 780 int unit; 781 782 unit = device_get_unit(dev); 783 d = device_get_softc(dev); 784 785 /* Play */ 786 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 787 SYSCTL_CHILDREN(d->play_sysctl_tree), 788 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 789 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 790 sysctl_dev_pcm_vchans, "I", "virtual channels enabled"); 791 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 792 SYSCTL_CHILDREN(d->play_sysctl_tree), 793 OID_AUTO, "vchanmode", 794 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 795 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 796 sysctl_dev_pcm_vchanmode, "A", 797 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 798 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 799 SYSCTL_CHILDREN(d->play_sysctl_tree), 800 OID_AUTO, "vchanrate", 801 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 802 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 803 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 804 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 805 SYSCTL_CHILDREN(d->play_sysctl_tree), 806 OID_AUTO, "vchanformat", 807 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 808 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 809 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 810 /* Rec */ 811 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 812 SYSCTL_CHILDREN(d->rec_sysctl_tree), 813 OID_AUTO, "vchans", 814 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 815 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 816 sysctl_dev_pcm_vchans, "I", "virtual channels enabled"); 817 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 818 SYSCTL_CHILDREN(d->rec_sysctl_tree), 819 OID_AUTO, "vchanmode", 820 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 821 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 822 sysctl_dev_pcm_vchanmode, "A", 823 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 824 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 825 SYSCTL_CHILDREN(d->rec_sysctl_tree), 826 OID_AUTO, "vchanrate", 827 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 828 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 829 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 830 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 831 SYSCTL_CHILDREN(d->rec_sysctl_tree), 832 OID_AUTO, "vchanformat", 833 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 834 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 835 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 836 } 837