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 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 /* Almost entirely rewritten to add multi-format/channels mixing support. */ 31 32 #ifdef HAVE_KERNEL_OPTION_HEADERS 33 #include "opt_snd.h" 34 #endif 35 36 #include <dev/sound/pcm/sound.h> 37 #include <dev/sound/pcm/vchan.h> 38 39 SND_DECLARE_FILE("$FreeBSD$"); 40 41 /* 42 * [ac3 , dts , linear , 0, linear, 0] 43 */ 44 #define FMTLIST_MAX 6 45 #define FMTLIST_OFFSET 4 46 #define DIGFMTS_MAX 2 47 48 #ifdef SND_DEBUG 49 static int snd_passthrough_verbose = 0; 50 SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RWTUN, 51 &snd_passthrough_verbose, 0, "passthrough verbosity"); 52 53 #endif 54 55 struct vchan_info { 56 struct pcm_channel *channel; 57 struct pcmchan_caps caps; 58 uint32_t fmtlist[FMTLIST_MAX]; 59 int trigger; 60 }; 61 62 static void * 63 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 64 struct pcm_channel *c, int dir) 65 { 66 struct vchan_info *info; 67 struct pcm_channel *p; 68 uint32_t i, j, *fmtlist; 69 70 KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC, 71 ("vchan_init: bad direction")); 72 KASSERT(c != NULL && c->parentchannel != NULL, 73 ("vchan_init: bad channels")); 74 75 info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO); 76 info->channel = c; 77 info->trigger = PCMTRIG_STOP; 78 p = c->parentchannel; 79 80 CHN_LOCK(p); 81 82 fmtlist = chn_getcaps(p)->fmtlist; 83 for (i = 0, j = 0; fmtlist[i] != 0 && j < DIGFMTS_MAX; i++) { 84 if (fmtlist[i] & AFMT_PASSTHROUGH) 85 info->fmtlist[j++] = fmtlist[i]; 86 } 87 if (p->format & AFMT_VCHAN) 88 info->fmtlist[j] = p->format; 89 else 90 info->fmtlist[j] = VCHAN_DEFAULT_FORMAT; 91 info->caps.fmtlist = info->fmtlist + 92 ((p->flags & CHN_F_VCHAN_DYNAMIC) ? 0 : FMTLIST_OFFSET); 93 94 CHN_UNLOCK(p); 95 96 c->flags |= CHN_F_VIRTUAL; 97 98 return (info); 99 } 100 101 static int 102 vchan_free(kobj_t obj, void *data) 103 { 104 105 free(data, M_DEVBUF); 106 107 return (0); 108 } 109 110 static int 111 vchan_setformat(kobj_t obj, void *data, uint32_t format) 112 { 113 struct vchan_info *info; 114 115 info = data; 116 117 CHN_LOCKASSERT(info->channel); 118 119 if (!snd_fmtvalid(format, info->caps.fmtlist)) 120 return (-1); 121 122 return (0); 123 } 124 125 static uint32_t 126 vchan_setspeed(kobj_t obj, void *data, uint32_t speed) 127 { 128 struct vchan_info *info; 129 130 info = data; 131 132 CHN_LOCKASSERT(info->channel); 133 134 return (info->caps.maxspeed); 135 } 136 137 static int 138 vchan_trigger(kobj_t obj, void *data, int go) 139 { 140 struct vchan_info *info; 141 struct pcm_channel *c, *p; 142 int ret, otrigger; 143 144 info = data; 145 146 if (!PCMTRIG_COMMON(go) || go == info->trigger) 147 return (0); 148 149 c = info->channel; 150 p = c->parentchannel; 151 otrigger = info->trigger; 152 info->trigger = go; 153 154 CHN_LOCKASSERT(c); 155 156 CHN_UNLOCK(c); 157 CHN_LOCK(p); 158 159 switch (go) { 160 case PCMTRIG_START: 161 if (otrigger != PCMTRIG_START) 162 CHN_INSERT_HEAD(p, c, children.busy); 163 break; 164 case PCMTRIG_STOP: 165 case PCMTRIG_ABORT: 166 if (otrigger == PCMTRIG_START) 167 CHN_REMOVE(p, c, children.busy); 168 break; 169 default: 170 break; 171 } 172 173 ret = chn_notify(p, CHN_N_TRIGGER); 174 175 CHN_LOCK(c); 176 177 if (ret == 0 && go == PCMTRIG_START && VCHAN_SYNC_REQUIRED(c)) 178 ret = vchan_sync(c); 179 180 CHN_UNLOCK(c); 181 CHN_UNLOCK(p); 182 CHN_LOCK(c); 183 184 return (ret); 185 } 186 187 static struct pcmchan_caps * 188 vchan_getcaps(kobj_t obj, void *data) 189 { 190 struct vchan_info *info; 191 struct pcm_channel *c; 192 uint32_t pformat, pspeed, pflags, i; 193 194 info = data; 195 c = info->channel; 196 pformat = c->parentchannel->format; 197 pspeed = c->parentchannel->speed; 198 pflags = c->parentchannel->flags; 199 200 CHN_LOCKASSERT(c); 201 202 if (pflags & CHN_F_VCHAN_DYNAMIC) { 203 info->caps.fmtlist = info->fmtlist; 204 if (pformat & AFMT_VCHAN) { 205 for (i = 0; info->caps.fmtlist[i] != 0; i++) { 206 if (info->caps.fmtlist[i] & AFMT_PASSTHROUGH) 207 continue; 208 break; 209 } 210 info->caps.fmtlist[i] = pformat; 211 } 212 if (c->format & AFMT_PASSTHROUGH) 213 info->caps.minspeed = c->speed; 214 else 215 info->caps.minspeed = pspeed; 216 info->caps.maxspeed = info->caps.minspeed; 217 } else { 218 info->caps.fmtlist = info->fmtlist + FMTLIST_OFFSET; 219 if (pformat & AFMT_VCHAN) 220 info->caps.fmtlist[0] = pformat; 221 else { 222 device_printf(c->dev, 223 "%s(): invalid vchan format 0x%08x", 224 __func__, pformat); 225 info->caps.fmtlist[0] = VCHAN_DEFAULT_FORMAT; 226 } 227 info->caps.minspeed = pspeed; 228 info->caps.maxspeed = info->caps.minspeed; 229 } 230 231 return (&info->caps); 232 } 233 234 static struct pcmchan_matrix * 235 vchan_getmatrix(kobj_t obj, void *data, uint32_t format) 236 { 237 238 return (feeder_matrix_format_map(format)); 239 } 240 241 static kobj_method_t vchan_methods[] = { 242 KOBJMETHOD(channel_init, vchan_init), 243 KOBJMETHOD(channel_free, vchan_free), 244 KOBJMETHOD(channel_setformat, vchan_setformat), 245 KOBJMETHOD(channel_setspeed, vchan_setspeed), 246 KOBJMETHOD(channel_trigger, vchan_trigger), 247 KOBJMETHOD(channel_getcaps, vchan_getcaps), 248 KOBJMETHOD(channel_getmatrix, vchan_getmatrix), 249 KOBJMETHOD_END 250 }; 251 CHANNEL_DECLARE(vchan); 252 253 static void 254 pcm_getparentchannel(struct snddev_info *d, 255 struct pcm_channel **wrch, struct pcm_channel **rdch) 256 { 257 struct pcm_channel **ch, *wch, *rch, *c; 258 259 KASSERT(d != NULL, ("%s(): NULL snddev_info", __func__)); 260 261 PCM_BUSYASSERT(d); 262 PCM_UNLOCKASSERT(d); 263 264 wch = NULL; 265 rch = NULL; 266 267 CHN_FOREACH(c, d, channels.pcm) { 268 CHN_LOCK(c); 269 ch = (c->direction == PCMDIR_PLAY) ? &wch : &rch; 270 if (c->flags & CHN_F_VIRTUAL) { 271 /* Sanity check */ 272 if (*ch != NULL && *ch != c->parentchannel) { 273 CHN_UNLOCK(c); 274 *ch = NULL; 275 break; 276 } 277 } else if (c->flags & CHN_F_HAS_VCHAN) { 278 /* No way!! */ 279 if (*ch != NULL) { 280 CHN_UNLOCK(c); 281 *ch = NULL; 282 break; 283 } 284 *ch = c; 285 } 286 CHN_UNLOCK(c); 287 } 288 289 if (wrch != NULL) 290 *wrch = wch; 291 if (rdch != NULL) 292 *rdch = rch; 293 } 294 295 static int 296 sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS) 297 { 298 struct snddev_info *d; 299 int direction, vchancount; 300 int err, cnt; 301 302 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 303 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 304 return (EINVAL); 305 306 PCM_LOCK(d); 307 PCM_WAIT(d); 308 309 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 310 case VCHAN_PLAY: 311 direction = PCMDIR_PLAY; 312 vchancount = d->pvchancount; 313 cnt = d->playcount; 314 break; 315 case VCHAN_REC: 316 direction = PCMDIR_REC; 317 vchancount = d->rvchancount; 318 cnt = d->reccount; 319 break; 320 default: 321 PCM_UNLOCK(d); 322 return (EINVAL); 323 break; 324 } 325 326 if (cnt < 1) { 327 PCM_UNLOCK(d); 328 return (ENODEV); 329 } 330 331 PCM_ACQUIRE(d); 332 PCM_UNLOCK(d); 333 334 cnt = vchancount; 335 err = sysctl_handle_int(oidp, &cnt, 0, req); 336 337 if (err == 0 && req->newptr != NULL && vchancount != cnt) { 338 if (cnt < 0) 339 cnt = 0; 340 if (cnt > SND_MAXVCHANS) 341 cnt = SND_MAXVCHANS; 342 err = pcm_setvchans(d, direction, cnt, -1); 343 } 344 345 PCM_RELEASE_QUICK(d); 346 347 return err; 348 } 349 350 static int 351 sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS) 352 { 353 struct snddev_info *d; 354 struct pcm_channel *c; 355 uint32_t dflags; 356 int direction, ret; 357 char dtype[16]; 358 359 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 360 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 361 return (EINVAL); 362 363 PCM_LOCK(d); 364 PCM_WAIT(d); 365 366 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 367 case VCHAN_PLAY: 368 direction = PCMDIR_PLAY; 369 break; 370 case VCHAN_REC: 371 direction = PCMDIR_REC; 372 break; 373 default: 374 PCM_UNLOCK(d); 375 return (EINVAL); 376 break; 377 } 378 379 PCM_ACQUIRE(d); 380 PCM_UNLOCK(d); 381 382 if (direction == PCMDIR_PLAY) 383 pcm_getparentchannel(d, &c, NULL); 384 else 385 pcm_getparentchannel(d, NULL, &c); 386 387 if (c == NULL) { 388 PCM_RELEASE_QUICK(d); 389 return (EINVAL); 390 } 391 392 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 393 __func__, direction, c->direction)); 394 395 CHN_LOCK(c); 396 if (c->flags & CHN_F_VCHAN_PASSTHROUGH) 397 strlcpy(dtype, "passthrough", sizeof(dtype)); 398 else if (c->flags & CHN_F_VCHAN_ADAPTIVE) 399 strlcpy(dtype, "adaptive", sizeof(dtype)); 400 else 401 strlcpy(dtype, "fixed", sizeof(dtype)); 402 CHN_UNLOCK(c); 403 404 ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req); 405 if (ret == 0 && req->newptr != NULL) { 406 if (strcasecmp(dtype, "passthrough") == 0 || 407 strcmp(dtype, "1") == 0) 408 dflags = CHN_F_VCHAN_PASSTHROUGH; 409 else if (strcasecmp(dtype, "adaptive") == 0 || 410 strcmp(dtype, "2") == 0) 411 dflags = CHN_F_VCHAN_ADAPTIVE; 412 else if (strcasecmp(dtype, "fixed") == 0 || 413 strcmp(dtype, "0") == 0) 414 dflags = 0; 415 else { 416 PCM_RELEASE_QUICK(d); 417 return (EINVAL); 418 } 419 CHN_LOCK(c); 420 if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) || 421 (c->flags & CHN_F_PASSTHROUGH)) { 422 CHN_UNLOCK(c); 423 PCM_RELEASE_QUICK(d); 424 return (0); 425 } 426 c->flags &= ~CHN_F_VCHAN_DYNAMIC; 427 c->flags |= dflags; 428 CHN_UNLOCK(c); 429 } 430 431 PCM_RELEASE_QUICK(d); 432 433 return (ret); 434 } 435 436 /* 437 * On the fly vchan rate/format settings 438 */ 439 440 #define VCHAN_ACCESSIBLE(c) (!((c)->flags & (CHN_F_PASSTHROUGH | \ 441 CHN_F_EXCLUSIVE)) && \ 442 (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \ 443 CHN_STOPPED(c))) 444 static int 445 sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS) 446 { 447 struct snddev_info *d; 448 struct pcm_channel *c, *ch; 449 struct pcmchan_caps *caps; 450 int *vchanrate, vchancount, direction, ret, newspd, restart; 451 452 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 453 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 454 return (EINVAL); 455 456 PCM_LOCK(d); 457 PCM_WAIT(d); 458 459 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 460 case VCHAN_PLAY: 461 direction = PCMDIR_PLAY; 462 vchancount = d->pvchancount; 463 vchanrate = &d->pvchanrate; 464 break; 465 case VCHAN_REC: 466 direction = PCMDIR_REC; 467 vchancount = d->rvchancount; 468 vchanrate = &d->rvchanrate; 469 break; 470 default: 471 PCM_UNLOCK(d); 472 return (EINVAL); 473 break; 474 } 475 476 if (vchancount < 1) { 477 PCM_UNLOCK(d); 478 return (EINVAL); 479 } 480 481 PCM_ACQUIRE(d); 482 PCM_UNLOCK(d); 483 484 if (direction == PCMDIR_PLAY) 485 pcm_getparentchannel(d, &c, NULL); 486 else 487 pcm_getparentchannel(d, NULL, &c); 488 489 if (c == NULL) { 490 PCM_RELEASE_QUICK(d); 491 return (EINVAL); 492 } 493 494 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 495 __func__, direction, c->direction)); 496 497 CHN_LOCK(c); 498 newspd = c->speed; 499 CHN_UNLOCK(c); 500 501 ret = sysctl_handle_int(oidp, &newspd, 0, req); 502 if (ret != 0 || req->newptr == NULL) { 503 PCM_RELEASE_QUICK(d); 504 return (ret); 505 } 506 507 if (newspd < 1 || newspd < feeder_rate_min || 508 newspd > feeder_rate_max) { 509 PCM_RELEASE_QUICK(d); 510 return (EINVAL); 511 } 512 513 CHN_LOCK(c); 514 515 if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) { 516 if (CHN_STARTED(c)) { 517 chn_abort(c); 518 restart = 1; 519 } else 520 restart = 0; 521 522 if (feeder_rate_round) { 523 caps = chn_getcaps(c); 524 RANGE(newspd, caps->minspeed, caps->maxspeed); 525 newspd = CHANNEL_SETSPEED(c->methods, 526 c->devinfo, newspd); 527 } 528 529 ret = chn_reset(c, c->format, newspd); 530 if (ret == 0) { 531 *vchanrate = c->speed; 532 if (restart != 0) { 533 CHN_FOREACH(ch, c, children.busy) { 534 CHN_LOCK(ch); 535 if (VCHAN_SYNC_REQUIRED(ch)) 536 vchan_sync(ch); 537 CHN_UNLOCK(ch); 538 } 539 c->flags |= CHN_F_DIRTY; 540 ret = chn_start(c, 1); 541 } 542 } 543 } 544 545 CHN_UNLOCK(c); 546 547 PCM_RELEASE_QUICK(d); 548 549 return (ret); 550 } 551 552 static int 553 sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) 554 { 555 struct snddev_info *d; 556 struct pcm_channel *c, *ch; 557 uint32_t newfmt; 558 int *vchanformat, vchancount, direction, ret, restart; 559 char fmtstr[AFMTSTR_LEN]; 560 561 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 562 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 563 return (EINVAL); 564 565 PCM_LOCK(d); 566 PCM_WAIT(d); 567 568 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 569 case VCHAN_PLAY: 570 direction = PCMDIR_PLAY; 571 vchancount = d->pvchancount; 572 vchanformat = &d->pvchanformat; 573 break; 574 case VCHAN_REC: 575 direction = PCMDIR_REC; 576 vchancount = d->rvchancount; 577 vchanformat = &d->rvchanformat; 578 break; 579 default: 580 PCM_UNLOCK(d); 581 return (EINVAL); 582 break; 583 } 584 585 if (vchancount < 1) { 586 PCM_UNLOCK(d); 587 return (EINVAL); 588 } 589 590 PCM_ACQUIRE(d); 591 PCM_UNLOCK(d); 592 593 if (direction == PCMDIR_PLAY) 594 pcm_getparentchannel(d, &c, NULL); 595 else 596 pcm_getparentchannel(d, NULL, &c); 597 598 if (c == NULL) { 599 PCM_RELEASE_QUICK(d); 600 return (EINVAL); 601 } 602 603 KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 604 __func__, direction, c->direction)); 605 606 CHN_LOCK(c); 607 608 bzero(fmtstr, sizeof(fmtstr)); 609 610 if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format) 611 strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr)); 612 613 CHN_UNLOCK(c); 614 615 ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 616 if (ret != 0 || req->newptr == NULL) { 617 PCM_RELEASE_QUICK(d); 618 return (ret); 619 } 620 621 newfmt = snd_str2afmt(fmtstr); 622 if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) { 623 PCM_RELEASE_QUICK(d); 624 return (EINVAL); 625 } 626 627 CHN_LOCK(c); 628 629 if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) { 630 if (CHN_STARTED(c)) { 631 chn_abort(c); 632 restart = 1; 633 } else 634 restart = 0; 635 636 ret = chn_reset(c, newfmt, c->speed); 637 if (ret == 0) { 638 *vchanformat = c->format; 639 if (restart != 0) { 640 CHN_FOREACH(ch, c, children.busy) { 641 CHN_LOCK(ch); 642 if (VCHAN_SYNC_REQUIRED(ch)) 643 vchan_sync(ch); 644 CHN_UNLOCK(ch); 645 } 646 c->flags |= CHN_F_DIRTY; 647 ret = chn_start(c, 1); 648 } 649 } 650 } 651 652 CHN_UNLOCK(c); 653 654 PCM_RELEASE_QUICK(d); 655 656 return (ret); 657 } 658 659 /* virtual channel interface */ 660 661 #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 662 "play.vchanformat" : "rec.vchanformat" 663 #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 664 "play.vchanrate" : "rec.vchanrate" 665 666 int 667 vchan_create(struct pcm_channel *parent, int num) 668 { 669 struct snddev_info *d; 670 struct pcm_channel *ch; 671 struct pcmchan_caps *parent_caps; 672 uint32_t vchanfmt, vchanspd; 673 int ret, direction, r, save; 674 675 d = parent->parentsnddev; 676 677 PCM_BUSYASSERT(d); 678 CHN_LOCKASSERT(parent); 679 680 if (!(parent->flags & CHN_F_BUSY)) 681 return (EBUSY); 682 683 if (!(parent->direction == PCMDIR_PLAY || 684 parent->direction == PCMDIR_REC)) 685 return (EINVAL); 686 687 d = parent->parentsnddev; 688 689 CHN_UNLOCK(parent); 690 PCM_LOCK(d); 691 692 if (parent->direction == PCMDIR_PLAY) { 693 direction = PCMDIR_PLAY_VIRTUAL; 694 vchanfmt = d->pvchanformat; 695 vchanspd = d->pvchanrate; 696 } else { 697 direction = PCMDIR_REC_VIRTUAL; 698 vchanfmt = d->rvchanformat; 699 vchanspd = d->rvchanrate; 700 } 701 702 /* create a new playback channel */ 703 ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent); 704 if (ch == NULL) { 705 PCM_UNLOCK(d); 706 CHN_LOCK(parent); 707 return (ENODEV); 708 } 709 710 /* add us to our grandparent's channel list */ 711 ret = pcm_chn_add(d, ch); 712 PCM_UNLOCK(d); 713 if (ret != 0) { 714 pcm_chn_destroy(ch); 715 CHN_LOCK(parent); 716 return (ret); 717 } 718 719 CHN_LOCK(parent); 720 /* 721 * Add us to our parent channel's children in reverse order 722 * so future destruction will pick the last (biggest number) 723 * channel. 724 */ 725 CHN_INSERT_SORT_DESCEND(parent, ch, children); 726 727 if (parent->flags & CHN_F_HAS_VCHAN) 728 return (0); 729 730 parent->flags |= CHN_F_HAS_VCHAN; 731 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 pcm_chn_destroy(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 ret = pcm_chn_destroy(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->unit); 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 void 940 vchan_initsys(device_t dev) 941 { 942 struct snddev_info *d; 943 int unit; 944 945 unit = device_get_unit(dev); 946 d = device_get_softc(dev); 947 948 /* Play */ 949 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 950 SYSCTL_CHILDREN(d->play_sysctl_tree), 951 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 952 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 953 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 954 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 955 SYSCTL_CHILDREN(d->play_sysctl_tree), 956 OID_AUTO, "vchanmode", 957 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 958 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 959 sysctl_dev_pcm_vchanmode, "A", 960 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 961 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 962 SYSCTL_CHILDREN(d->play_sysctl_tree), 963 OID_AUTO, "vchanrate", 964 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 965 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 966 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 967 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 968 SYSCTL_CHILDREN(d->play_sysctl_tree), 969 OID_AUTO, "vchanformat", 970 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 971 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 972 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 973 /* Rec */ 974 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 975 SYSCTL_CHILDREN(d->rec_sysctl_tree), 976 OID_AUTO, "vchans", 977 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 978 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 979 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 980 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 981 SYSCTL_CHILDREN(d->rec_sysctl_tree), 982 OID_AUTO, "vchanmode", 983 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 984 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 985 sysctl_dev_pcm_vchanmode, "A", 986 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 987 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 988 SYSCTL_CHILDREN(d->rec_sysctl_tree), 989 OID_AUTO, "vchanrate", 990 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 991 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 992 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 993 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 994 SYSCTL_CHILDREN(d->rec_sysctl_tree), 995 OID_AUTO, "vchanformat", 996 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 997 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 998 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 999 } 1000