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 int snd_maxautovchans = 16; 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 150 if (!PCMTRIG_COMMON(go) || go == info->trigger) 151 return (0); 152 153 c = info->channel; 154 p = c->parentchannel; 155 otrigger = info->trigger; 156 info->trigger = go; 157 158 CHN_LOCKASSERT(c); 159 160 CHN_UNLOCK(c); 161 CHN_LOCK(p); 162 163 switch (go) { 164 case PCMTRIG_START: 165 if (otrigger != PCMTRIG_START) 166 CHN_INSERT_HEAD(p, c, children.busy); 167 break; 168 case PCMTRIG_STOP: 169 case PCMTRIG_ABORT: 170 if (otrigger == PCMTRIG_START) 171 CHN_REMOVE(p, c, children.busy); 172 break; 173 default: 174 break; 175 } 176 177 ret = chn_notify(p, CHN_N_TRIGGER); 178 179 CHN_LOCK(c); 180 181 if (ret == 0 && go == PCMTRIG_START && VCHAN_SYNC_REQUIRED(c)) 182 ret = vchan_sync(c); 183 184 CHN_UNLOCK(c); 185 CHN_UNLOCK(p); 186 CHN_LOCK(c); 187 188 return (ret); 189 } 190 191 static struct pcmchan_caps * 192 vchan_getcaps(kobj_t obj, void *data) 193 { 194 struct vchan_info *info; 195 struct pcm_channel *c; 196 uint32_t pformat, pspeed, pflags, i; 197 198 info = data; 199 c = info->channel; 200 pformat = c->parentchannel->format; 201 pspeed = c->parentchannel->speed; 202 pflags = c->parentchannel->flags; 203 204 CHN_LOCKASSERT(c); 205 206 if (pflags & CHN_F_VCHAN_DYNAMIC) { 207 info->caps.fmtlist = info->fmtlist; 208 if (pformat & AFMT_VCHAN) { 209 for (i = 0; info->caps.fmtlist[i] != 0; i++) { 210 if (info->caps.fmtlist[i] & AFMT_PASSTHROUGH) 211 continue; 212 break; 213 } 214 info->caps.fmtlist[i] = pformat; 215 } 216 if (c->format & AFMT_PASSTHROUGH) 217 info->caps.minspeed = c->speed; 218 else 219 info->caps.minspeed = pspeed; 220 info->caps.maxspeed = info->caps.minspeed; 221 } else { 222 info->caps.fmtlist = info->fmtlist + FMTLIST_OFFSET; 223 if (pformat & AFMT_VCHAN) 224 info->caps.fmtlist[0] = pformat; 225 else { 226 device_printf(c->dev, 227 "%s(): invalid vchan format 0x%08x", 228 __func__, pformat); 229 info->caps.fmtlist[0] = VCHAN_DEFAULT_FORMAT; 230 } 231 info->caps.minspeed = pspeed; 232 info->caps.maxspeed = info->caps.minspeed; 233 } 234 235 return (&info->caps); 236 } 237 238 static struct pcmchan_matrix * 239 vchan_getmatrix(kobj_t obj, void *data, uint32_t format) 240 { 241 242 return (feeder_matrix_format_map(format)); 243 } 244 245 static kobj_method_t vchan_methods[] = { 246 KOBJMETHOD(channel_init, vchan_init), 247 KOBJMETHOD(channel_free, vchan_free), 248 KOBJMETHOD(channel_setformat, vchan_setformat), 249 KOBJMETHOD(channel_setspeed, vchan_setspeed), 250 KOBJMETHOD(channel_trigger, vchan_trigger), 251 KOBJMETHOD(channel_getcaps, vchan_getcaps), 252 KOBJMETHOD(channel_getmatrix, vchan_getmatrix), 253 KOBJMETHOD_END 254 }; 255 CHANNEL_DECLARE(vchan); 256 257 static void 258 vchan_getparentchannel(struct snddev_info *d, 259 struct pcm_channel **wrch, struct pcm_channel **rdch) 260 { 261 struct pcm_channel **ch, *wch, *rch, *c; 262 263 KASSERT(d != NULL, ("%s(): NULL snddev_info", __func__)); 264 265 PCM_BUSYASSERT(d); 266 PCM_UNLOCKASSERT(d); 267 268 wch = NULL; 269 rch = NULL; 270 271 CHN_FOREACH(c, d, channels.pcm) { 272 CHN_LOCK(c); 273 ch = (c->direction == PCMDIR_PLAY) ? &wch : &rch; 274 if (c->flags & CHN_F_VIRTUAL) { 275 /* Sanity check */ 276 if (*ch != NULL && *ch != c->parentchannel) { 277 CHN_UNLOCK(c); 278 *ch = NULL; 279 break; 280 } 281 } else if (c->flags & CHN_F_HAS_VCHAN) { 282 /* No way!! */ 283 if (*ch != NULL) { 284 CHN_UNLOCK(c); 285 *ch = NULL; 286 break; 287 } 288 *ch = c; 289 } 290 CHN_UNLOCK(c); 291 } 292 293 if (wrch != NULL) 294 *wrch = wch; 295 if (rdch != NULL) 296 *rdch = rch; 297 } 298 299 static int 300 sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS) 301 { 302 struct snddev_info *d; 303 int direction, vchancount; 304 int err, cnt; 305 306 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 307 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 308 return (EINVAL); 309 310 PCM_LOCK(d); 311 PCM_WAIT(d); 312 313 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 314 case VCHAN_PLAY: 315 direction = PCMDIR_PLAY; 316 vchancount = d->pvchancount; 317 cnt = d->playcount; 318 break; 319 case VCHAN_REC: 320 direction = PCMDIR_REC; 321 vchancount = d->rvchancount; 322 cnt = d->reccount; 323 break; 324 default: 325 PCM_UNLOCK(d); 326 return (EINVAL); 327 break; 328 } 329 330 if (cnt < 1) { 331 PCM_UNLOCK(d); 332 return (ENODEV); 333 } 334 335 PCM_ACQUIRE(d); 336 PCM_UNLOCK(d); 337 338 cnt = vchancount; 339 err = sysctl_handle_int(oidp, &cnt, 0, req); 340 341 if (err == 0 && req->newptr != NULL && vchancount != cnt) { 342 if (cnt < 0) 343 cnt = 0; 344 if (cnt > SND_MAXVCHANS) 345 cnt = SND_MAXVCHANS; 346 err = vchan_setnew(d, direction, cnt); 347 } 348 349 PCM_RELEASE_QUICK(d); 350 351 return err; 352 } 353 354 static int 355 sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS) 356 { 357 struct snddev_info *d; 358 struct pcm_channel *c; 359 uint32_t dflags; 360 int direction, ret; 361 char dtype[16]; 362 363 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 364 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 365 return (EINVAL); 366 367 PCM_LOCK(d); 368 PCM_WAIT(d); 369 370 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 371 case VCHAN_PLAY: 372 direction = PCMDIR_PLAY; 373 break; 374 case VCHAN_REC: 375 direction = PCMDIR_REC; 376 break; 377 default: 378 PCM_UNLOCK(d); 379 return (EINVAL); 380 break; 381 } 382 383 PCM_ACQUIRE(d); 384 PCM_UNLOCK(d); 385 386 if (direction == PCMDIR_PLAY) 387 vchan_getparentchannel(d, &c, NULL); 388 else 389 vchan_getparentchannel(d, NULL, &c); 390 391 if (c == NULL) { 392 PCM_RELEASE_QUICK(d); 393 return (EINVAL); 394 } 395 396 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 397 __func__, direction, c->direction)); 398 399 CHN_LOCK(c); 400 if (c->flags & CHN_F_VCHAN_PASSTHROUGH) 401 strlcpy(dtype, "passthrough", sizeof(dtype)); 402 else if (c->flags & CHN_F_VCHAN_ADAPTIVE) 403 strlcpy(dtype, "adaptive", sizeof(dtype)); 404 else 405 strlcpy(dtype, "fixed", sizeof(dtype)); 406 CHN_UNLOCK(c); 407 408 ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req); 409 if (ret == 0 && req->newptr != NULL) { 410 if (strcasecmp(dtype, "passthrough") == 0 || 411 strcmp(dtype, "1") == 0) 412 dflags = CHN_F_VCHAN_PASSTHROUGH; 413 else if (strcasecmp(dtype, "adaptive") == 0 || 414 strcmp(dtype, "2") == 0) 415 dflags = CHN_F_VCHAN_ADAPTIVE; 416 else if (strcasecmp(dtype, "fixed") == 0 || 417 strcmp(dtype, "0") == 0) 418 dflags = 0; 419 else { 420 PCM_RELEASE_QUICK(d); 421 return (EINVAL); 422 } 423 CHN_LOCK(c); 424 if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) || 425 (c->flags & CHN_F_PASSTHROUGH)) { 426 CHN_UNLOCK(c); 427 PCM_RELEASE_QUICK(d); 428 return (0); 429 } 430 c->flags &= ~CHN_F_VCHAN_DYNAMIC; 431 c->flags |= dflags; 432 CHN_UNLOCK(c); 433 } 434 435 PCM_RELEASE_QUICK(d); 436 437 return (ret); 438 } 439 440 /* 441 * On the fly vchan rate/format settings 442 */ 443 444 #define VCHAN_ACCESSIBLE(c) (!((c)->flags & (CHN_F_PASSTHROUGH | \ 445 CHN_F_EXCLUSIVE)) && \ 446 (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \ 447 CHN_STOPPED(c))) 448 static int 449 sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS) 450 { 451 struct snddev_info *d; 452 struct pcm_channel *c, *ch; 453 struct pcmchan_caps *caps; 454 int *vchanrate, vchancount, direction, ret, newspd, restart; 455 456 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 457 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 458 return (EINVAL); 459 460 PCM_LOCK(d); 461 PCM_WAIT(d); 462 463 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 464 case VCHAN_PLAY: 465 direction = PCMDIR_PLAY; 466 vchancount = d->pvchancount; 467 vchanrate = &d->pvchanrate; 468 break; 469 case VCHAN_REC: 470 direction = PCMDIR_REC; 471 vchancount = d->rvchancount; 472 vchanrate = &d->rvchanrate; 473 break; 474 default: 475 PCM_UNLOCK(d); 476 return (EINVAL); 477 break; 478 } 479 480 if (vchancount < 1) { 481 PCM_UNLOCK(d); 482 return (EINVAL); 483 } 484 485 PCM_ACQUIRE(d); 486 PCM_UNLOCK(d); 487 488 if (direction == PCMDIR_PLAY) 489 vchan_getparentchannel(d, &c, NULL); 490 else 491 vchan_getparentchannel(d, NULL, &c); 492 493 if (c == NULL) { 494 PCM_RELEASE_QUICK(d); 495 return (EINVAL); 496 } 497 498 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 499 __func__, direction, c->direction)); 500 501 CHN_LOCK(c); 502 newspd = c->speed; 503 CHN_UNLOCK(c); 504 505 ret = sysctl_handle_int(oidp, &newspd, 0, req); 506 if (ret != 0 || req->newptr == NULL) { 507 PCM_RELEASE_QUICK(d); 508 return (ret); 509 } 510 511 if (newspd < feeder_rate_min || newspd > feeder_rate_max) { 512 PCM_RELEASE_QUICK(d); 513 return (EINVAL); 514 } 515 516 CHN_LOCK(c); 517 518 if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) { 519 if (CHN_STARTED(c)) { 520 chn_abort(c); 521 restart = 1; 522 } else 523 restart = 0; 524 525 if (feeder_rate_round) { 526 caps = chn_getcaps(c); 527 RANGE(newspd, caps->minspeed, caps->maxspeed); 528 newspd = CHANNEL_SETSPEED(c->methods, 529 c->devinfo, newspd); 530 } 531 532 ret = chn_reset(c, c->format, newspd); 533 if (ret == 0) { 534 *vchanrate = c->speed; 535 if (restart != 0) { 536 CHN_FOREACH(ch, c, children.busy) { 537 CHN_LOCK(ch); 538 if (VCHAN_SYNC_REQUIRED(ch)) 539 vchan_sync(ch); 540 CHN_UNLOCK(ch); 541 } 542 c->flags |= CHN_F_DIRTY; 543 ret = chn_start(c, 1); 544 } 545 } 546 } 547 548 CHN_UNLOCK(c); 549 550 PCM_RELEASE_QUICK(d); 551 552 return (ret); 553 } 554 555 static int 556 sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) 557 { 558 struct snddev_info *d; 559 struct pcm_channel *c, *ch; 560 uint32_t newfmt; 561 int *vchanformat, vchancount, direction, ret, restart; 562 char fmtstr[AFMTSTR_LEN]; 563 564 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 565 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 566 return (EINVAL); 567 568 PCM_LOCK(d); 569 PCM_WAIT(d); 570 571 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 572 case VCHAN_PLAY: 573 direction = PCMDIR_PLAY; 574 vchancount = d->pvchancount; 575 vchanformat = &d->pvchanformat; 576 break; 577 case VCHAN_REC: 578 direction = PCMDIR_REC; 579 vchancount = d->rvchancount; 580 vchanformat = &d->rvchanformat; 581 break; 582 default: 583 PCM_UNLOCK(d); 584 return (EINVAL); 585 break; 586 } 587 588 if (vchancount < 1) { 589 PCM_UNLOCK(d); 590 return (EINVAL); 591 } 592 593 PCM_ACQUIRE(d); 594 PCM_UNLOCK(d); 595 596 if (direction == PCMDIR_PLAY) 597 vchan_getparentchannel(d, &c, NULL); 598 else 599 vchan_getparentchannel(d, NULL, &c); 600 601 if (c == NULL) { 602 PCM_RELEASE_QUICK(d); 603 return (EINVAL); 604 } 605 606 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 607 __func__, direction, c->direction)); 608 609 CHN_LOCK(c); 610 611 bzero(fmtstr, sizeof(fmtstr)); 612 613 if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format) 614 strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr)); 615 616 CHN_UNLOCK(c); 617 618 ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 619 if (ret != 0 || req->newptr == NULL) { 620 PCM_RELEASE_QUICK(d); 621 return (ret); 622 } 623 624 newfmt = snd_str2afmt(fmtstr); 625 if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) { 626 PCM_RELEASE_QUICK(d); 627 return (EINVAL); 628 } 629 630 CHN_LOCK(c); 631 632 if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) { 633 if (CHN_STARTED(c)) { 634 chn_abort(c); 635 restart = 1; 636 } else 637 restart = 0; 638 639 ret = chn_reset(c, newfmt, c->speed); 640 if (ret == 0) { 641 *vchanformat = c->format; 642 if (restart != 0) { 643 CHN_FOREACH(ch, c, children.busy) { 644 CHN_LOCK(ch); 645 if (VCHAN_SYNC_REQUIRED(ch)) 646 vchan_sync(ch); 647 CHN_UNLOCK(ch); 648 } 649 c->flags |= CHN_F_DIRTY; 650 ret = chn_start(c, 1); 651 } 652 } 653 } 654 655 CHN_UNLOCK(c); 656 657 PCM_RELEASE_QUICK(d); 658 659 return (ret); 660 } 661 662 /* virtual channel interface */ 663 664 #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 665 "play.vchanformat" : "rec.vchanformat" 666 #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 667 "play.vchanrate" : "rec.vchanrate" 668 669 int 670 vchan_create(struct pcm_channel *parent) 671 { 672 struct snddev_info *d; 673 struct pcm_channel *ch; 674 struct pcmchan_caps *parent_caps; 675 uint32_t vchanfmt, vchanspd; 676 int ret, direction, r; 677 bool save; 678 679 ret = 0; 680 save = false; 681 d = parent->parentsnddev; 682 683 PCM_BUSYASSERT(d); 684 CHN_LOCKASSERT(parent); 685 686 if (!(parent->flags & CHN_F_BUSY)) 687 return (EBUSY); 688 689 if (!(parent->direction == PCMDIR_PLAY || 690 parent->direction == PCMDIR_REC)) 691 return (EINVAL); 692 693 CHN_UNLOCK(parent); 694 PCM_LOCK(d); 695 696 if (parent->direction == PCMDIR_PLAY) { 697 direction = PCMDIR_PLAY_VIRTUAL; 698 vchanfmt = d->pvchanformat; 699 vchanspd = d->pvchanrate; 700 } else { 701 direction = PCMDIR_REC_VIRTUAL; 702 vchanfmt = d->rvchanformat; 703 vchanspd = d->rvchanrate; 704 } 705 706 /* create a new playback channel */ 707 ch = chn_init(d, parent, &vchan_class, direction, parent); 708 if (ch == NULL) { 709 PCM_UNLOCK(d); 710 CHN_LOCK(parent); 711 return (ENODEV); 712 } 713 PCM_UNLOCK(d); 714 715 CHN_LOCK(parent); 716 /* 717 * Add us to our parent channel's children in reverse order 718 * so future destruction will pick the last (biggest number) 719 * channel. 720 */ 721 CHN_INSERT_SORT_DESCEND(parent, ch, children); 722 723 if (parent->flags & CHN_F_HAS_VCHAN) 724 return (0); 725 726 parent->flags |= CHN_F_HAS_VCHAN; 727 728 parent_caps = chn_getcaps(parent); 729 if (parent_caps == NULL) { 730 ret = EINVAL; 731 goto fail; 732 } 733 734 if (vchanfmt == 0) { 735 const char *vfmt; 736 737 CHN_UNLOCK(parent); 738 r = resource_string_value(device_get_name(parent->dev), 739 device_get_unit(parent->dev), VCHAN_FMT_HINT(direction), 740 &vfmt); 741 CHN_LOCK(parent); 742 if (r != 0) 743 vfmt = NULL; 744 if (vfmt != NULL) { 745 vchanfmt = snd_str2afmt(vfmt); 746 if (vchanfmt != 0 && !(vchanfmt & AFMT_VCHAN)) 747 vchanfmt = 0; 748 } 749 if (vchanfmt == 0) 750 vchanfmt = VCHAN_DEFAULT_FORMAT; 751 save = true; 752 } 753 754 if (vchanspd == 0) { 755 /* 756 * This is very sad. Few soundcards advertised as being 757 * able to do (insanely) higher/lower speed, but in 758 * reality, they simply can't. At least, we give user chance 759 * to set sane value via kernel hints or sysctl. 760 */ 761 CHN_UNLOCK(parent); 762 r = resource_int_value(device_get_name(parent->dev), 763 device_get_unit(parent->dev), VCHAN_SPD_HINT(direction), 764 &vchanspd); 765 CHN_LOCK(parent); 766 if (r != 0) { 767 /* No saved value, no hint, NOTHING. */ 768 vchanspd = VCHAN_DEFAULT_RATE; 769 RANGE(vchanspd, parent_caps->minspeed, 770 parent_caps->maxspeed); 771 } 772 save = true; 773 } 774 775 /* 776 * Limit the speed between feeder_rate_min <-> feeder_rate_max. 777 */ 778 RANGE(vchanspd, feeder_rate_min, feeder_rate_max); 779 780 if (feeder_rate_round) { 781 RANGE(vchanspd, parent_caps->minspeed, 782 parent_caps->maxspeed); 783 vchanspd = CHANNEL_SETSPEED(parent->methods, 784 parent->devinfo, vchanspd); 785 } 786 787 if ((ret = chn_reset(parent, vchanfmt, vchanspd)) != 0) 788 goto fail; 789 790 if (save) { 791 /* 792 * Save new value. 793 */ 794 if (direction == PCMDIR_PLAY_VIRTUAL) { 795 d->pvchanformat = parent->format; 796 d->pvchanrate = parent->speed; 797 } else { 798 d->rvchanformat = parent->format; 799 d->rvchanrate = parent->speed; 800 } 801 } 802 803 /* 804 * If the parent channel supports digital format, 805 * enable passthrough mode. 806 */ 807 if (snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) { 808 parent->flags &= ~CHN_F_VCHAN_DYNAMIC; 809 parent->flags |= CHN_F_VCHAN_PASSTHROUGH; 810 } 811 812 return (ret); 813 814 fail: 815 CHN_REMOVE(parent, ch, children); 816 parent->flags &= ~CHN_F_HAS_VCHAN; 817 CHN_UNLOCK(parent); 818 chn_kill(ch); 819 CHN_LOCK(parent); 820 821 return (ret); 822 } 823 824 int 825 vchan_destroy(struct pcm_channel *c) 826 { 827 struct pcm_channel *parent; 828 829 KASSERT(c != NULL && c->parentchannel != NULL && 830 c->parentsnddev != NULL, ("%s(): invalid channel=%p", 831 __func__, c)); 832 833 CHN_LOCKASSERT(c); 834 835 parent = c->parentchannel; 836 837 PCM_BUSYASSERT(c->parentsnddev); 838 CHN_LOCKASSERT(parent); 839 840 CHN_UNLOCK(c); 841 842 if (!(parent->flags & CHN_F_BUSY)) 843 return (EBUSY); 844 845 if (CHN_EMPTY(parent, children)) 846 return (EINVAL); 847 848 /* remove us from our parent's children list */ 849 CHN_REMOVE(parent, c, children); 850 851 if (CHN_EMPTY(parent, children)) { 852 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 853 chn_reset(parent, parent->format, parent->speed); 854 } 855 856 CHN_UNLOCK(parent); 857 858 /* destroy ourselves */ 859 chn_kill(c); 860 861 CHN_LOCK(parent); 862 863 return (0); 864 } 865 866 int 867 #ifdef SND_DEBUG 868 vchan_passthrough(struct pcm_channel *c, const char *caller) 869 #else 870 vchan_sync(struct pcm_channel *c) 871 #endif 872 { 873 int ret; 874 875 KASSERT(c != NULL && c->parentchannel != NULL && 876 (c->flags & CHN_F_VIRTUAL), 877 ("%s(): invalid passthrough", __func__)); 878 CHN_LOCKASSERT(c); 879 CHN_LOCKASSERT(c->parentchannel); 880 881 sndbuf_setspd(c->bufhard, c->parentchannel->speed); 882 c->flags |= CHN_F_PASSTHROUGH; 883 ret = feeder_chain(c); 884 c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH); 885 if (ret != 0) 886 c->flags |= CHN_F_DIRTY; 887 888 #ifdef SND_DEBUG 889 if (snd_passthrough_verbose) { 890 device_printf(c->dev, "%s(%s/%s) %s() -> re-sync err=%d\n", 891 __func__, c->name, c->comm, caller, ret); 892 } 893 #endif 894 895 return (ret); 896 } 897 898 int 899 vchan_setnew(struct snddev_info *d, int direction, int newcnt) 900 { 901 struct pcm_channel *c, *ch, *nch; 902 struct pcmchan_caps *caps; 903 int i, err, vcnt; 904 905 PCM_BUSYASSERT(d); 906 907 if ((direction == PCMDIR_PLAY && d->playcount < 1) || 908 (direction == PCMDIR_REC && d->reccount < 1)) 909 return (ENODEV); 910 911 if (!(d->flags & SD_F_AUTOVCHAN)) 912 return (EINVAL); 913 914 if (newcnt < 0 || newcnt > SND_MAXVCHANS) 915 return (E2BIG); 916 917 if (direction == PCMDIR_PLAY) 918 vcnt = d->pvchancount; 919 else if (direction == PCMDIR_REC) 920 vcnt = d->rvchancount; 921 else 922 return (EINVAL); 923 924 if (newcnt > vcnt) { 925 /* add new vchans - find a parent channel first */ 926 ch = NULL; 927 CHN_FOREACH(c, d, channels.pcm) { 928 CHN_LOCK(c); 929 if (c->direction == direction && 930 ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 931 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 932 /* 933 * Reuse hw channel with vchans already 934 * created. 935 */ 936 if (c->flags & CHN_F_HAS_VCHAN) { 937 ch = c; 938 break; 939 } 940 /* 941 * No vchans ever created, look for 942 * channels with supported formats. 943 */ 944 caps = chn_getcaps(c); 945 if (caps == NULL) { 946 CHN_UNLOCK(c); 947 continue; 948 } 949 for (i = 0; caps->fmtlist[i] != 0; i++) { 950 if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 951 break; 952 } 953 if (caps->fmtlist[i] != 0) { 954 ch = c; 955 break; 956 } 957 } 958 CHN_UNLOCK(c); 959 } 960 if (ch == NULL) 961 return (EBUSY); 962 ch->flags |= CHN_F_BUSY; 963 err = 0; 964 while (err == 0 && newcnt > vcnt) { 965 err = vchan_create(ch); 966 if (err == 0) 967 vcnt++; 968 else if (err == E2BIG && newcnt > vcnt) 969 device_printf(d->dev, 970 "%s: err=%d Maximum channel reached.\n", 971 __func__, err); 972 } 973 if (vcnt == 0) 974 ch->flags &= ~CHN_F_BUSY; 975 CHN_UNLOCK(ch); 976 if (err != 0) 977 return (err); 978 } else if (newcnt < vcnt) { 979 CHN_FOREACH(c, d, channels.pcm) { 980 CHN_LOCK(c); 981 if (c->direction != direction || 982 CHN_EMPTY(c, children) || 983 !(c->flags & CHN_F_HAS_VCHAN)) { 984 CHN_UNLOCK(c); 985 continue; 986 } 987 CHN_FOREACH_SAFE(ch, c, nch, children) { 988 CHN_LOCK(ch); 989 if (vcnt == 1 && ch->flags & CHN_F_BUSY) { 990 CHN_UNLOCK(ch); 991 break; 992 } 993 if (!(ch->flags & CHN_F_BUSY)) { 994 err = vchan_destroy(ch); 995 if (err == 0) 996 vcnt--; 997 } else 998 CHN_UNLOCK(ch); 999 if (vcnt == newcnt) 1000 break; 1001 } 1002 CHN_UNLOCK(c); 1003 break; 1004 } 1005 } 1006 1007 return (0); 1008 } 1009 1010 void 1011 vchan_setmaxauto(struct snddev_info *d, int num) 1012 { 1013 PCM_BUSYASSERT(d); 1014 1015 if (num < 0) 1016 return; 1017 1018 if (num >= 0 && d->pvchancount > num) 1019 (void)vchan_setnew(d, PCMDIR_PLAY, num); 1020 else if (num > 0 && d->pvchancount == 0) 1021 (void)vchan_setnew(d, PCMDIR_PLAY, 1); 1022 1023 if (num >= 0 && d->rvchancount > num) 1024 (void)vchan_setnew(d, PCMDIR_REC, num); 1025 else if (num > 0 && d->rvchancount == 0) 1026 (void)vchan_setnew(d, PCMDIR_REC, 1); 1027 } 1028 1029 static int 1030 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 1031 { 1032 struct snddev_info *d; 1033 int i, v, error; 1034 1035 v = snd_maxautovchans; 1036 error = sysctl_handle_int(oidp, &v, 0, req); 1037 if (error == 0 && req->newptr != NULL) { 1038 if (v < 0) 1039 v = 0; 1040 if (v > SND_MAXVCHANS) 1041 v = SND_MAXVCHANS; 1042 snd_maxautovchans = v; 1043 for (i = 0; pcm_devclass != NULL && 1044 i < devclass_get_maxunit(pcm_devclass); i++) { 1045 d = devclass_get_softc(pcm_devclass, i); 1046 if (!PCM_REGISTERED(d)) 1047 continue; 1048 PCM_ACQUIRE_QUICK(d); 1049 vchan_setmaxauto(d, v); 1050 PCM_RELEASE_QUICK(d); 1051 } 1052 } 1053 return (error); 1054 } 1055 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, 1056 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 1057 sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 1058 1059 void 1060 vchan_initsys(device_t dev) 1061 { 1062 struct snddev_info *d; 1063 int unit; 1064 1065 unit = device_get_unit(dev); 1066 d = device_get_softc(dev); 1067 1068 /* Play */ 1069 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1070 SYSCTL_CHILDREN(d->play_sysctl_tree), 1071 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1072 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1073 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 1074 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1075 SYSCTL_CHILDREN(d->play_sysctl_tree), 1076 OID_AUTO, "vchanmode", 1077 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1078 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1079 sysctl_dev_pcm_vchanmode, "A", 1080 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1081 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1082 SYSCTL_CHILDREN(d->play_sysctl_tree), 1083 OID_AUTO, "vchanrate", 1084 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1085 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1086 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1087 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1088 SYSCTL_CHILDREN(d->play_sysctl_tree), 1089 OID_AUTO, "vchanformat", 1090 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1091 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1092 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1093 /* Rec */ 1094 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1095 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1096 OID_AUTO, "vchans", 1097 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1098 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1099 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 1100 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1101 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1102 OID_AUTO, "vchanmode", 1103 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1104 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1105 sysctl_dev_pcm_vchanmode, "A", 1106 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1107 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1108 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1109 OID_AUTO, "vchanrate", 1110 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1111 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1112 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1113 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1114 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1115 OID_AUTO, "vchanformat", 1116 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1117 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1118 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1119 } 1120