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 != 0) { 890 char *devname, buf[CHN_NAMELEN]; 891 892 devname = dsp_unit2name(buf, sizeof(buf), c); 893 device_printf(c->dev, 894 "%s(%s/%s) %s() -> re-sync err=%d\n", 895 __func__, (devname != NULL) ? devname : "dspX", c->comm, 896 caller, ret); 897 } 898 #endif 899 900 return (ret); 901 } 902 903 int 904 vchan_setnew(struct snddev_info *d, int direction, int newcnt) 905 { 906 struct pcm_channel *c, *ch, *nch; 907 struct pcmchan_caps *caps; 908 int i, err, vcnt; 909 910 PCM_BUSYASSERT(d); 911 912 if ((direction == PCMDIR_PLAY && d->playcount < 1) || 913 (direction == PCMDIR_REC && d->reccount < 1)) 914 return (ENODEV); 915 916 if (!(d->flags & SD_F_AUTOVCHAN)) 917 return (EINVAL); 918 919 if (newcnt < 0 || newcnt > SND_MAXVCHANS) 920 return (E2BIG); 921 922 if (direction == PCMDIR_PLAY) 923 vcnt = d->pvchancount; 924 else if (direction == PCMDIR_REC) 925 vcnt = d->rvchancount; 926 else 927 return (EINVAL); 928 929 if (newcnt > vcnt) { 930 /* add new vchans - find a parent channel first */ 931 ch = NULL; 932 CHN_FOREACH(c, d, channels.pcm) { 933 CHN_LOCK(c); 934 if (c->direction == direction && 935 ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 936 c->refcount < 1 && 937 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 938 /* 939 * Reuse hw channel with vchans already 940 * created. 941 */ 942 if (c->flags & CHN_F_HAS_VCHAN) { 943 ch = c; 944 break; 945 } 946 /* 947 * No vchans ever created, look for 948 * channels with supported formats. 949 */ 950 caps = chn_getcaps(c); 951 if (caps == NULL) { 952 CHN_UNLOCK(c); 953 continue; 954 } 955 for (i = 0; caps->fmtlist[i] != 0; i++) { 956 if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 957 break; 958 } 959 if (caps->fmtlist[i] != 0) { 960 ch = c; 961 break; 962 } 963 } 964 CHN_UNLOCK(c); 965 } 966 if (ch == NULL) 967 return (EBUSY); 968 ch->flags |= CHN_F_BUSY; 969 err = 0; 970 while (err == 0 && newcnt > vcnt) { 971 err = vchan_create(ch); 972 if (err == 0) 973 vcnt++; 974 else if (err == E2BIG && newcnt > vcnt) 975 device_printf(d->dev, 976 "%s: err=%d Maximum channel reached.\n", 977 __func__, err); 978 } 979 if (vcnt == 0) 980 ch->flags &= ~CHN_F_BUSY; 981 CHN_UNLOCK(ch); 982 if (err != 0) 983 return (err); 984 } else if (newcnt < vcnt) { 985 CHN_FOREACH(c, d, channels.pcm) { 986 CHN_LOCK(c); 987 if (c->direction != direction || 988 CHN_EMPTY(c, children) || 989 !(c->flags & CHN_F_HAS_VCHAN)) { 990 CHN_UNLOCK(c); 991 continue; 992 } 993 CHN_FOREACH_SAFE(ch, c, nch, children) { 994 CHN_LOCK(ch); 995 if (vcnt == 1 && c->refcount > 0) { 996 CHN_UNLOCK(ch); 997 break; 998 } 999 if (!(ch->flags & CHN_F_BUSY) && 1000 ch->refcount < 1) { 1001 err = vchan_destroy(ch); 1002 if (err == 0) 1003 vcnt--; 1004 } else 1005 CHN_UNLOCK(ch); 1006 if (vcnt == newcnt) 1007 break; 1008 } 1009 CHN_UNLOCK(c); 1010 break; 1011 } 1012 } 1013 1014 return (0); 1015 } 1016 1017 void 1018 vchan_setmaxauto(struct snddev_info *d, int num) 1019 { 1020 PCM_BUSYASSERT(d); 1021 1022 if (num < 0) 1023 return; 1024 1025 if (num >= 0 && d->pvchancount > num) 1026 (void)vchan_setnew(d, PCMDIR_PLAY, num); 1027 else if (num > 0 && d->pvchancount == 0) 1028 (void)vchan_setnew(d, PCMDIR_PLAY, 1); 1029 1030 if (num >= 0 && d->rvchancount > num) 1031 (void)vchan_setnew(d, PCMDIR_REC, num); 1032 else if (num > 0 && d->rvchancount == 0) 1033 (void)vchan_setnew(d, PCMDIR_REC, 1); 1034 } 1035 1036 static int 1037 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 1038 { 1039 struct snddev_info *d; 1040 int i, v, error; 1041 1042 v = snd_maxautovchans; 1043 error = sysctl_handle_int(oidp, &v, 0, req); 1044 if (error == 0 && req->newptr != NULL) { 1045 if (v < 0) 1046 v = 0; 1047 if (v > SND_MAXVCHANS) 1048 v = SND_MAXVCHANS; 1049 snd_maxautovchans = v; 1050 for (i = 0; pcm_devclass != NULL && 1051 i < devclass_get_maxunit(pcm_devclass); i++) { 1052 d = devclass_get_softc(pcm_devclass, i); 1053 if (!PCM_REGISTERED(d)) 1054 continue; 1055 PCM_ACQUIRE_QUICK(d); 1056 vchan_setmaxauto(d, v); 1057 PCM_RELEASE_QUICK(d); 1058 } 1059 } 1060 return (error); 1061 } 1062 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, 1063 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 1064 sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 1065 1066 void 1067 vchan_initsys(device_t dev) 1068 { 1069 struct snddev_info *d; 1070 int unit; 1071 1072 unit = device_get_unit(dev); 1073 d = device_get_softc(dev); 1074 1075 /* Play */ 1076 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1077 SYSCTL_CHILDREN(d->play_sysctl_tree), 1078 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1079 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1080 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 1081 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1082 SYSCTL_CHILDREN(d->play_sysctl_tree), 1083 OID_AUTO, "vchanmode", 1084 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1085 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1086 sysctl_dev_pcm_vchanmode, "A", 1087 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1088 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1089 SYSCTL_CHILDREN(d->play_sysctl_tree), 1090 OID_AUTO, "vchanrate", 1091 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1092 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1093 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1094 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1095 SYSCTL_CHILDREN(d->play_sysctl_tree), 1096 OID_AUTO, "vchanformat", 1097 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1098 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1099 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1100 /* Rec */ 1101 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1102 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1103 OID_AUTO, "vchans", 1104 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1105 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1106 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 1107 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1108 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1109 OID_AUTO, "vchanmode", 1110 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1111 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1112 sysctl_dev_pcm_vchanmode, "A", 1113 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1114 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1115 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1116 OID_AUTO, "vchanrate", 1117 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1118 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1119 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1120 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1121 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1122 OID_AUTO, "vchanformat", 1123 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1124 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1125 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1126 } 1127