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