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 < 1 || newspd < feeder_rate_min || 512 newspd > feeder_rate_max) { 513 PCM_RELEASE_QUICK(d); 514 return (EINVAL); 515 } 516 517 CHN_LOCK(c); 518 519 if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) { 520 if (CHN_STARTED(c)) { 521 chn_abort(c); 522 restart = 1; 523 } else 524 restart = 0; 525 526 if (feeder_rate_round) { 527 caps = chn_getcaps(c); 528 RANGE(newspd, caps->minspeed, caps->maxspeed); 529 newspd = CHANNEL_SETSPEED(c->methods, 530 c->devinfo, newspd); 531 } 532 533 ret = chn_reset(c, c->format, newspd); 534 if (ret == 0) { 535 *vchanrate = c->speed; 536 if (restart != 0) { 537 CHN_FOREACH(ch, c, children.busy) { 538 CHN_LOCK(ch); 539 if (VCHAN_SYNC_REQUIRED(ch)) 540 vchan_sync(ch); 541 CHN_UNLOCK(ch); 542 } 543 c->flags |= CHN_F_DIRTY; 544 ret = chn_start(c, 1); 545 } 546 } 547 } 548 549 CHN_UNLOCK(c); 550 551 PCM_RELEASE_QUICK(d); 552 553 return (ret); 554 } 555 556 static int 557 sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) 558 { 559 struct snddev_info *d; 560 struct pcm_channel *c, *ch; 561 uint32_t newfmt; 562 int *vchanformat, vchancount, direction, ret, restart; 563 char fmtstr[AFMTSTR_LEN]; 564 565 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 566 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 567 return (EINVAL); 568 569 PCM_LOCK(d); 570 PCM_WAIT(d); 571 572 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 573 case VCHAN_PLAY: 574 direction = PCMDIR_PLAY; 575 vchancount = d->pvchancount; 576 vchanformat = &d->pvchanformat; 577 break; 578 case VCHAN_REC: 579 direction = PCMDIR_REC; 580 vchancount = d->rvchancount; 581 vchanformat = &d->rvchanformat; 582 break; 583 default: 584 PCM_UNLOCK(d); 585 return (EINVAL); 586 break; 587 } 588 589 if (vchancount < 1) { 590 PCM_UNLOCK(d); 591 return (EINVAL); 592 } 593 594 PCM_ACQUIRE(d); 595 PCM_UNLOCK(d); 596 597 if (direction == PCMDIR_PLAY) 598 vchan_getparentchannel(d, &c, NULL); 599 else 600 vchan_getparentchannel(d, NULL, &c); 601 602 if (c == NULL) { 603 PCM_RELEASE_QUICK(d); 604 return (EINVAL); 605 } 606 607 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 608 __func__, direction, c->direction)); 609 610 CHN_LOCK(c); 611 612 bzero(fmtstr, sizeof(fmtstr)); 613 614 if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format) 615 strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr)); 616 617 CHN_UNLOCK(c); 618 619 ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 620 if (ret != 0 || req->newptr == NULL) { 621 PCM_RELEASE_QUICK(d); 622 return (ret); 623 } 624 625 newfmt = snd_str2afmt(fmtstr); 626 if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) { 627 PCM_RELEASE_QUICK(d); 628 return (EINVAL); 629 } 630 631 CHN_LOCK(c); 632 633 if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) { 634 if (CHN_STARTED(c)) { 635 chn_abort(c); 636 restart = 1; 637 } else 638 restart = 0; 639 640 ret = chn_reset(c, newfmt, c->speed); 641 if (ret == 0) { 642 *vchanformat = c->format; 643 if (restart != 0) { 644 CHN_FOREACH(ch, c, children.busy) { 645 CHN_LOCK(ch); 646 if (VCHAN_SYNC_REQUIRED(ch)) 647 vchan_sync(ch); 648 CHN_UNLOCK(ch); 649 } 650 c->flags |= CHN_F_DIRTY; 651 ret = chn_start(c, 1); 652 } 653 } 654 } 655 656 CHN_UNLOCK(c); 657 658 PCM_RELEASE_QUICK(d); 659 660 return (ret); 661 } 662 663 /* virtual channel interface */ 664 665 #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 666 "play.vchanformat" : "rec.vchanformat" 667 #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 668 "play.vchanrate" : "rec.vchanrate" 669 670 int 671 vchan_create(struct pcm_channel *parent) 672 { 673 struct snddev_info *d; 674 struct pcm_channel *ch; 675 struct pcmchan_caps *parent_caps; 676 uint32_t vchanfmt, vchanspd; 677 int ret, direction, r, save; 678 679 d = parent->parentsnddev; 680 681 PCM_BUSYASSERT(d); 682 CHN_LOCKASSERT(parent); 683 684 if (!(parent->flags & CHN_F_BUSY)) 685 return (EBUSY); 686 687 if (!(parent->direction == PCMDIR_PLAY || 688 parent->direction == PCMDIR_REC)) 689 return (EINVAL); 690 691 d = parent->parentsnddev; 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 714 /* add us to our grandparent's channel list */ 715 pcm_chn_add(d, ch); 716 PCM_UNLOCK(d); 717 718 CHN_LOCK(parent); 719 /* 720 * Add us to our parent channel's children in reverse order 721 * so future destruction will pick the last (biggest number) 722 * channel. 723 */ 724 CHN_INSERT_SORT_DESCEND(parent, ch, children); 725 726 if (parent->flags & CHN_F_HAS_VCHAN) 727 return (0); 728 729 parent->flags |= CHN_F_HAS_VCHAN; 730 731 ret = 0; 732 parent_caps = chn_getcaps(parent); 733 if (parent_caps == NULL) 734 ret = EINVAL; 735 736 save = 0; 737 738 if (ret == 0 && vchanfmt == 0) { 739 const char *vfmt; 740 741 CHN_UNLOCK(parent); 742 r = resource_string_value(device_get_name(parent->dev), 743 device_get_unit(parent->dev), VCHAN_FMT_HINT(direction), 744 &vfmt); 745 CHN_LOCK(parent); 746 if (r != 0) 747 vfmt = NULL; 748 if (vfmt != NULL) { 749 vchanfmt = snd_str2afmt(vfmt); 750 if (vchanfmt != 0 && !(vchanfmt & AFMT_VCHAN)) 751 vchanfmt = 0; 752 } 753 if (vchanfmt == 0) 754 vchanfmt = VCHAN_DEFAULT_FORMAT; 755 save = 1; 756 } 757 758 if (ret == 0 && vchanspd == 0) { 759 /* 760 * This is very sad. Few soundcards advertised as being 761 * able to do (insanely) higher/lower speed, but in 762 * reality, they simply can't. At least, we give user chance 763 * to set sane value via kernel hints or sysctl. 764 */ 765 CHN_UNLOCK(parent); 766 r = resource_int_value(device_get_name(parent->dev), 767 device_get_unit(parent->dev), VCHAN_SPD_HINT(direction), 768 &vchanspd); 769 CHN_LOCK(parent); 770 if (r != 0) { 771 /* 772 * No saved value, no hint, NOTHING. 773 * 774 * Workaround for sb16 running 775 * poorly at 45k / 49k. 776 */ 777 switch (parent_caps->maxspeed) { 778 case 45000: 779 case 49000: 780 vchanspd = 44100; 781 break; 782 default: 783 vchanspd = VCHAN_DEFAULT_RATE; 784 if (vchanspd > parent_caps->maxspeed) 785 vchanspd = parent_caps->maxspeed; 786 break; 787 } 788 if (vchanspd < parent_caps->minspeed) 789 vchanspd = parent_caps->minspeed; 790 } 791 save = 1; 792 } 793 794 if (ret == 0) { 795 /* 796 * Limit the speed between feeder_rate_min <-> feeder_rate_max. 797 */ 798 if (vchanspd < feeder_rate_min) 799 vchanspd = feeder_rate_min; 800 if (vchanspd > feeder_rate_max) 801 vchanspd = feeder_rate_max; 802 803 if (feeder_rate_round) { 804 RANGE(vchanspd, parent_caps->minspeed, 805 parent_caps->maxspeed); 806 vchanspd = CHANNEL_SETSPEED(parent->methods, 807 parent->devinfo, vchanspd); 808 } 809 810 ret = chn_reset(parent, vchanfmt, vchanspd); 811 } 812 813 if (ret == 0 && save) { 814 /* 815 * Save new value. 816 */ 817 if (direction == PCMDIR_PLAY_VIRTUAL) { 818 d->pvchanformat = parent->format; 819 d->pvchanrate = parent->speed; 820 } else { 821 d->rvchanformat = parent->format; 822 d->rvchanrate = parent->speed; 823 } 824 } 825 826 /* 827 * If the parent channel supports digital format, 828 * enable passthrough mode. 829 */ 830 if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) { 831 parent->flags &= ~CHN_F_VCHAN_DYNAMIC; 832 parent->flags |= CHN_F_VCHAN_PASSTHROUGH; 833 } 834 835 if (ret != 0) { 836 CHN_REMOVE(parent, ch, children); 837 parent->flags &= ~CHN_F_HAS_VCHAN; 838 CHN_UNLOCK(parent); 839 PCM_LOCK(d); 840 if (pcm_chn_remove(d, ch) == 0) { 841 PCM_UNLOCK(d); 842 chn_kill(ch); 843 } else 844 PCM_UNLOCK(d); 845 CHN_LOCK(parent); 846 } 847 848 return (ret); 849 } 850 851 int 852 vchan_destroy(struct pcm_channel *c) 853 { 854 struct pcm_channel *parent; 855 struct snddev_info *d; 856 int ret; 857 858 KASSERT(c != NULL && c->parentchannel != NULL && 859 c->parentsnddev != NULL, ("%s(): invalid channel=%p", 860 __func__, c)); 861 862 CHN_LOCKASSERT(c); 863 864 d = c->parentsnddev; 865 parent = c->parentchannel; 866 867 PCM_BUSYASSERT(d); 868 CHN_LOCKASSERT(parent); 869 870 CHN_UNLOCK(c); 871 872 if (!(parent->flags & CHN_F_BUSY)) 873 return (EBUSY); 874 875 if (CHN_EMPTY(parent, children)) 876 return (EINVAL); 877 878 /* remove us from our parent's children list */ 879 CHN_REMOVE(parent, c, children); 880 881 if (CHN_EMPTY(parent, children)) { 882 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 883 chn_reset(parent, parent->format, parent->speed); 884 } 885 886 CHN_UNLOCK(parent); 887 888 /* remove us from our grandparent's channel list */ 889 PCM_LOCK(d); 890 ret = pcm_chn_remove(d, c); 891 PCM_UNLOCK(d); 892 893 /* destroy ourselves */ 894 if (ret == 0) 895 chn_kill(c); 896 897 CHN_LOCK(parent); 898 899 return (ret); 900 } 901 902 int 903 #ifdef SND_DEBUG 904 vchan_passthrough(struct pcm_channel *c, const char *caller) 905 #else 906 vchan_sync(struct pcm_channel *c) 907 #endif 908 { 909 int ret; 910 911 KASSERT(c != NULL && c->parentchannel != NULL && 912 (c->flags & CHN_F_VIRTUAL), 913 ("%s(): invalid passthrough", __func__)); 914 CHN_LOCKASSERT(c); 915 CHN_LOCKASSERT(c->parentchannel); 916 917 sndbuf_setspd(c->bufhard, c->parentchannel->speed); 918 c->flags |= CHN_F_PASSTHROUGH; 919 ret = feeder_chain(c); 920 c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH); 921 if (ret != 0) 922 c->flags |= CHN_F_DIRTY; 923 924 #ifdef SND_DEBUG 925 if (snd_passthrough_verbose != 0) { 926 char *devname, buf[CHN_NAMELEN]; 927 928 devname = dsp_unit2name(buf, sizeof(buf), c); 929 device_printf(c->dev, 930 "%s(%s/%s) %s() -> re-sync err=%d\n", 931 __func__, (devname != NULL) ? devname : "dspX", c->comm, 932 caller, ret); 933 } 934 #endif 935 936 return (ret); 937 } 938 939 int 940 vchan_setnew(struct snddev_info *d, int direction, int newcnt) 941 { 942 struct pcm_channel *c, *ch, *nch; 943 struct pcmchan_caps *caps; 944 int i, err, vcnt; 945 946 PCM_BUSYASSERT(d); 947 948 if ((direction == PCMDIR_PLAY && d->playcount < 1) || 949 (direction == PCMDIR_REC && d->reccount < 1)) 950 return (ENODEV); 951 952 if (!(d->flags & SD_F_AUTOVCHAN)) 953 return (EINVAL); 954 955 if (newcnt < 0 || newcnt > SND_MAXVCHANS) 956 return (E2BIG); 957 958 if (direction == PCMDIR_PLAY) 959 vcnt = d->pvchancount; 960 else if (direction == PCMDIR_REC) 961 vcnt = d->rvchancount; 962 else 963 return (EINVAL); 964 965 if (newcnt > vcnt) { 966 KASSERT((newcnt - 1) == vcnt, 967 ("bogus vchan_create() request newcnt=%d vcnt=%d", 968 newcnt, vcnt)); 969 /* add new vchans - find a parent channel first */ 970 ch = NULL; 971 CHN_FOREACH(c, d, channels.pcm) { 972 CHN_LOCK(c); 973 if (c->direction == direction && 974 ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 975 c->refcount < 1 && 976 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 977 /* 978 * Reuse hw channel with vchans already 979 * created. 980 */ 981 if (c->flags & CHN_F_HAS_VCHAN) { 982 ch = c; 983 break; 984 } 985 /* 986 * No vchans ever created, look for 987 * channels with supported formats. 988 */ 989 caps = chn_getcaps(c); 990 if (caps == NULL) { 991 CHN_UNLOCK(c); 992 continue; 993 } 994 for (i = 0; caps->fmtlist[i] != 0; i++) { 995 if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 996 break; 997 } 998 if (caps->fmtlist[i] != 0) { 999 ch = c; 1000 break; 1001 } 1002 } 1003 CHN_UNLOCK(c); 1004 } 1005 if (ch == NULL) 1006 return (EBUSY); 1007 ch->flags |= CHN_F_BUSY; 1008 err = 0; 1009 while (err == 0 && newcnt > vcnt) { 1010 err = vchan_create(ch); 1011 if (err == 0) 1012 vcnt++; 1013 else if (err == E2BIG && newcnt > vcnt) 1014 device_printf(d->dev, 1015 "%s: err=%d Maximum channel reached.\n", 1016 __func__, err); 1017 } 1018 if (vcnt == 0) 1019 ch->flags &= ~CHN_F_BUSY; 1020 CHN_UNLOCK(ch); 1021 if (err != 0) 1022 return (err); 1023 } else if (newcnt < vcnt) { 1024 CHN_FOREACH(c, d, channels.pcm) { 1025 CHN_LOCK(c); 1026 if (c->direction != direction || 1027 CHN_EMPTY(c, children) || 1028 !(c->flags & CHN_F_HAS_VCHAN)) { 1029 CHN_UNLOCK(c); 1030 continue; 1031 } 1032 CHN_FOREACH_SAFE(ch, c, nch, children) { 1033 CHN_LOCK(ch); 1034 if (vcnt == 1 && c->refcount > 0) { 1035 CHN_UNLOCK(ch); 1036 break; 1037 } 1038 if (!(ch->flags & CHN_F_BUSY) && 1039 ch->refcount < 1) { 1040 err = vchan_destroy(ch); 1041 if (err == 0) 1042 vcnt--; 1043 } else 1044 CHN_UNLOCK(ch); 1045 if (vcnt == newcnt) 1046 break; 1047 } 1048 CHN_UNLOCK(c); 1049 break; 1050 } 1051 } 1052 1053 return (0); 1054 } 1055 1056 void 1057 vchan_setmaxauto(struct snddev_info *d, int num) 1058 { 1059 PCM_BUSYASSERT(d); 1060 1061 if (num < 0) 1062 return; 1063 1064 if (num >= 0 && d->pvchancount > num) 1065 (void)vchan_setnew(d, PCMDIR_PLAY, num); 1066 else if (num > 0 && d->pvchancount == 0) 1067 (void)vchan_setnew(d, PCMDIR_PLAY, 1); 1068 1069 if (num >= 0 && d->rvchancount > num) 1070 (void)vchan_setnew(d, PCMDIR_REC, num); 1071 else if (num > 0 && d->rvchancount == 0) 1072 (void)vchan_setnew(d, PCMDIR_REC, 1); 1073 } 1074 1075 static int 1076 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 1077 { 1078 struct snddev_info *d; 1079 int i, v, error; 1080 1081 v = snd_maxautovchans; 1082 error = sysctl_handle_int(oidp, &v, 0, req); 1083 if (error == 0 && req->newptr != NULL) { 1084 if (v < 0) 1085 v = 0; 1086 if (v > SND_MAXVCHANS) 1087 v = SND_MAXVCHANS; 1088 snd_maxautovchans = v; 1089 for (i = 0; pcm_devclass != NULL && 1090 i < devclass_get_maxunit(pcm_devclass); i++) { 1091 d = devclass_get_softc(pcm_devclass, i); 1092 if (!PCM_REGISTERED(d)) 1093 continue; 1094 PCM_ACQUIRE_QUICK(d); 1095 vchan_setmaxauto(d, v); 1096 PCM_RELEASE_QUICK(d); 1097 } 1098 } 1099 return (error); 1100 } 1101 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, 1102 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 1103 sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 1104 1105 void 1106 vchan_initsys(device_t dev) 1107 { 1108 struct snddev_info *d; 1109 int unit; 1110 1111 unit = device_get_unit(dev); 1112 d = device_get_softc(dev); 1113 1114 /* Play */ 1115 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1116 SYSCTL_CHILDREN(d->play_sysctl_tree), 1117 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1118 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1119 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 1120 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1121 SYSCTL_CHILDREN(d->play_sysctl_tree), 1122 OID_AUTO, "vchanmode", 1123 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1124 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1125 sysctl_dev_pcm_vchanmode, "A", 1126 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1127 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1128 SYSCTL_CHILDREN(d->play_sysctl_tree), 1129 OID_AUTO, "vchanrate", 1130 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1131 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1132 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1133 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1134 SYSCTL_CHILDREN(d->play_sysctl_tree), 1135 OID_AUTO, "vchanformat", 1136 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1137 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1138 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1139 /* Rec */ 1140 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1141 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1142 OID_AUTO, "vchans", 1143 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1144 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1145 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 1146 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1147 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1148 OID_AUTO, "vchanmode", 1149 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1150 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1151 sysctl_dev_pcm_vchanmode, "A", 1152 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1153 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1154 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1155 OID_AUTO, "vchanrate", 1156 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1157 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1158 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1159 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1160 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1161 OID_AUTO, "vchanformat", 1162 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1163 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1164 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1165 } 1166