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 /* No saved value, no hint, NOTHING. */ 772 vchanspd = VCHAN_DEFAULT_RATE; 773 RANGE(vchanspd, parent_caps->minspeed, 774 parent_caps->maxspeed); 775 } 776 save = 1; 777 } 778 779 if (ret == 0) { 780 /* 781 * Limit the speed between feeder_rate_min <-> feeder_rate_max. 782 */ 783 RANGE(vchanspd, feeder_rate_min, feeder_rate_max); 784 785 if (feeder_rate_round) { 786 RANGE(vchanspd, parent_caps->minspeed, 787 parent_caps->maxspeed); 788 vchanspd = CHANNEL_SETSPEED(parent->methods, 789 parent->devinfo, vchanspd); 790 } 791 792 ret = chn_reset(parent, vchanfmt, vchanspd); 793 } 794 795 if (ret == 0 && save) { 796 /* 797 * Save new value. 798 */ 799 if (direction == PCMDIR_PLAY_VIRTUAL) { 800 d->pvchanformat = parent->format; 801 d->pvchanrate = parent->speed; 802 } else { 803 d->rvchanformat = parent->format; 804 d->rvchanrate = parent->speed; 805 } 806 } 807 808 /* 809 * If the parent channel supports digital format, 810 * enable passthrough mode. 811 */ 812 if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) { 813 parent->flags &= ~CHN_F_VCHAN_DYNAMIC; 814 parent->flags |= CHN_F_VCHAN_PASSTHROUGH; 815 } 816 817 if (ret != 0) { 818 CHN_REMOVE(parent, ch, children); 819 parent->flags &= ~CHN_F_HAS_VCHAN; 820 CHN_UNLOCK(parent); 821 PCM_LOCK(d); 822 if (pcm_chn_remove(d, ch) == 0) { 823 PCM_UNLOCK(d); 824 chn_kill(ch); 825 } else 826 PCM_UNLOCK(d); 827 CHN_LOCK(parent); 828 } 829 830 return (ret); 831 } 832 833 int 834 vchan_destroy(struct pcm_channel *c) 835 { 836 struct pcm_channel *parent; 837 struct snddev_info *d; 838 int ret; 839 840 KASSERT(c != NULL && c->parentchannel != NULL && 841 c->parentsnddev != NULL, ("%s(): invalid channel=%p", 842 __func__, c)); 843 844 CHN_LOCKASSERT(c); 845 846 d = c->parentsnddev; 847 parent = c->parentchannel; 848 849 PCM_BUSYASSERT(d); 850 CHN_LOCKASSERT(parent); 851 852 CHN_UNLOCK(c); 853 854 if (!(parent->flags & CHN_F_BUSY)) 855 return (EBUSY); 856 857 if (CHN_EMPTY(parent, children)) 858 return (EINVAL); 859 860 /* remove us from our parent's children list */ 861 CHN_REMOVE(parent, c, children); 862 863 if (CHN_EMPTY(parent, children)) { 864 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 865 chn_reset(parent, parent->format, parent->speed); 866 } 867 868 CHN_UNLOCK(parent); 869 870 /* remove us from our grandparent's channel list */ 871 PCM_LOCK(d); 872 ret = pcm_chn_remove(d, c); 873 PCM_UNLOCK(d); 874 875 /* destroy ourselves */ 876 if (ret == 0) 877 chn_kill(c); 878 879 CHN_LOCK(parent); 880 881 return (ret); 882 } 883 884 int 885 #ifdef SND_DEBUG 886 vchan_passthrough(struct pcm_channel *c, const char *caller) 887 #else 888 vchan_sync(struct pcm_channel *c) 889 #endif 890 { 891 int ret; 892 893 KASSERT(c != NULL && c->parentchannel != NULL && 894 (c->flags & CHN_F_VIRTUAL), 895 ("%s(): invalid passthrough", __func__)); 896 CHN_LOCKASSERT(c); 897 CHN_LOCKASSERT(c->parentchannel); 898 899 sndbuf_setspd(c->bufhard, c->parentchannel->speed); 900 c->flags |= CHN_F_PASSTHROUGH; 901 ret = feeder_chain(c); 902 c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH); 903 if (ret != 0) 904 c->flags |= CHN_F_DIRTY; 905 906 #ifdef SND_DEBUG 907 if (snd_passthrough_verbose != 0) { 908 char *devname, buf[CHN_NAMELEN]; 909 910 devname = dsp_unit2name(buf, sizeof(buf), c); 911 device_printf(c->dev, 912 "%s(%s/%s) %s() -> re-sync err=%d\n", 913 __func__, (devname != NULL) ? devname : "dspX", c->comm, 914 caller, ret); 915 } 916 #endif 917 918 return (ret); 919 } 920 921 int 922 vchan_setnew(struct snddev_info *d, int direction, int newcnt) 923 { 924 struct pcm_channel *c, *ch, *nch; 925 struct pcmchan_caps *caps; 926 int i, err, vcnt; 927 928 PCM_BUSYASSERT(d); 929 930 if ((direction == PCMDIR_PLAY && d->playcount < 1) || 931 (direction == PCMDIR_REC && d->reccount < 1)) 932 return (ENODEV); 933 934 if (!(d->flags & SD_F_AUTOVCHAN)) 935 return (EINVAL); 936 937 if (newcnt < 0 || newcnt > SND_MAXVCHANS) 938 return (E2BIG); 939 940 if (direction == PCMDIR_PLAY) 941 vcnt = d->pvchancount; 942 else if (direction == PCMDIR_REC) 943 vcnt = d->rvchancount; 944 else 945 return (EINVAL); 946 947 if (newcnt > vcnt) { 948 KASSERT((newcnt - 1) == vcnt, 949 ("bogus vchan_create() request newcnt=%d vcnt=%d", 950 newcnt, vcnt)); 951 /* add new vchans - find a parent channel first */ 952 ch = NULL; 953 CHN_FOREACH(c, d, channels.pcm) { 954 CHN_LOCK(c); 955 if (c->direction == direction && 956 ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 957 c->refcount < 1 && 958 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 959 /* 960 * Reuse hw channel with vchans already 961 * created. 962 */ 963 if (c->flags & CHN_F_HAS_VCHAN) { 964 ch = c; 965 break; 966 } 967 /* 968 * No vchans ever created, look for 969 * channels with supported formats. 970 */ 971 caps = chn_getcaps(c); 972 if (caps == NULL) { 973 CHN_UNLOCK(c); 974 continue; 975 } 976 for (i = 0; caps->fmtlist[i] != 0; i++) { 977 if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 978 break; 979 } 980 if (caps->fmtlist[i] != 0) { 981 ch = c; 982 break; 983 } 984 } 985 CHN_UNLOCK(c); 986 } 987 if (ch == NULL) 988 return (EBUSY); 989 ch->flags |= CHN_F_BUSY; 990 err = 0; 991 while (err == 0 && newcnt > vcnt) { 992 err = vchan_create(ch); 993 if (err == 0) 994 vcnt++; 995 else if (err == E2BIG && newcnt > vcnt) 996 device_printf(d->dev, 997 "%s: err=%d Maximum channel reached.\n", 998 __func__, err); 999 } 1000 if (vcnt == 0) 1001 ch->flags &= ~CHN_F_BUSY; 1002 CHN_UNLOCK(ch); 1003 if (err != 0) 1004 return (err); 1005 } else if (newcnt < vcnt) { 1006 CHN_FOREACH(c, d, channels.pcm) { 1007 CHN_LOCK(c); 1008 if (c->direction != direction || 1009 CHN_EMPTY(c, children) || 1010 !(c->flags & CHN_F_HAS_VCHAN)) { 1011 CHN_UNLOCK(c); 1012 continue; 1013 } 1014 CHN_FOREACH_SAFE(ch, c, nch, children) { 1015 CHN_LOCK(ch); 1016 if (vcnt == 1 && c->refcount > 0) { 1017 CHN_UNLOCK(ch); 1018 break; 1019 } 1020 if (!(ch->flags & CHN_F_BUSY) && 1021 ch->refcount < 1) { 1022 err = vchan_destroy(ch); 1023 if (err == 0) 1024 vcnt--; 1025 } else 1026 CHN_UNLOCK(ch); 1027 if (vcnt == newcnt) 1028 break; 1029 } 1030 CHN_UNLOCK(c); 1031 break; 1032 } 1033 } 1034 1035 return (0); 1036 } 1037 1038 void 1039 vchan_setmaxauto(struct snddev_info *d, int num) 1040 { 1041 PCM_BUSYASSERT(d); 1042 1043 if (num < 0) 1044 return; 1045 1046 if (num >= 0 && d->pvchancount > num) 1047 (void)vchan_setnew(d, PCMDIR_PLAY, num); 1048 else if (num > 0 && d->pvchancount == 0) 1049 (void)vchan_setnew(d, PCMDIR_PLAY, 1); 1050 1051 if (num >= 0 && d->rvchancount > num) 1052 (void)vchan_setnew(d, PCMDIR_REC, num); 1053 else if (num > 0 && d->rvchancount == 0) 1054 (void)vchan_setnew(d, PCMDIR_REC, 1); 1055 } 1056 1057 static int 1058 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 1059 { 1060 struct snddev_info *d; 1061 int i, v, error; 1062 1063 v = snd_maxautovchans; 1064 error = sysctl_handle_int(oidp, &v, 0, req); 1065 if (error == 0 && req->newptr != NULL) { 1066 if (v < 0) 1067 v = 0; 1068 if (v > SND_MAXVCHANS) 1069 v = SND_MAXVCHANS; 1070 snd_maxautovchans = v; 1071 for (i = 0; pcm_devclass != NULL && 1072 i < devclass_get_maxunit(pcm_devclass); i++) { 1073 d = devclass_get_softc(pcm_devclass, i); 1074 if (!PCM_REGISTERED(d)) 1075 continue; 1076 PCM_ACQUIRE_QUICK(d); 1077 vchan_setmaxauto(d, v); 1078 PCM_RELEASE_QUICK(d); 1079 } 1080 } 1081 return (error); 1082 } 1083 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, 1084 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 1085 sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 1086 1087 void 1088 vchan_initsys(device_t dev) 1089 { 1090 struct snddev_info *d; 1091 int unit; 1092 1093 unit = device_get_unit(dev); 1094 d = device_get_softc(dev); 1095 1096 /* Play */ 1097 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1098 SYSCTL_CHILDREN(d->play_sysctl_tree), 1099 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1100 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1101 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 1102 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1103 SYSCTL_CHILDREN(d->play_sysctl_tree), 1104 OID_AUTO, "vchanmode", 1105 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1106 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1107 sysctl_dev_pcm_vchanmode, "A", 1108 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1109 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1110 SYSCTL_CHILDREN(d->play_sysctl_tree), 1111 OID_AUTO, "vchanrate", 1112 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1113 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1114 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1115 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1116 SYSCTL_CHILDREN(d->play_sysctl_tree), 1117 OID_AUTO, "vchanformat", 1118 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1119 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1120 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1121 /* Rec */ 1122 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1123 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1124 OID_AUTO, "vchans", 1125 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1126 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1127 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 1128 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1129 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1130 OID_AUTO, "vchanmode", 1131 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1132 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1133 sysctl_dev_pcm_vchanmode, "A", 1134 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1135 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1136 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1137 OID_AUTO, "vchanrate", 1138 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1139 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1140 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1141 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1142 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1143 OID_AUTO, "vchanformat", 1144 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1145 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1146 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1147 } 1148