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 /* 40 * [ac3 , dts , linear , 0, linear, 0] 41 */ 42 #define FMTLIST_MAX 6 43 #define FMTLIST_OFFSET 4 44 #define DIGFMTS_MAX 2 45 46 #ifdef SND_DEBUG 47 static int snd_passthrough_verbose = 0; 48 SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RWTUN, 49 &snd_passthrough_verbose, 0, "passthrough verbosity"); 50 51 #endif 52 53 struct vchan_info { 54 struct pcm_channel *channel; 55 struct pcmchan_caps caps; 56 uint32_t fmtlist[FMTLIST_MAX]; 57 int trigger; 58 }; 59 60 int snd_maxautovchans = 16; 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 vchan_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 = vchan_setnew(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 vchan_getparentchannel(d, &c, NULL); 384 else 385 vchan_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 vchan_getparentchannel(d, &c, NULL); 486 else 487 vchan_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 vchan_getparentchannel(d, &c, NULL); 595 else 596 vchan_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 = chn_init(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 pcm_chn_add(d, ch); 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 ret = 0; 728 parent_caps = chn_getcaps(parent); 729 if (parent_caps == NULL) 730 ret = EINVAL; 731 732 save = 0; 733 734 if (ret == 0 && vchanfmt == 0) { 735 const char *vfmt; 736 737 CHN_UNLOCK(parent); 738 r = resource_string_value(device_get_name(parent->dev), 739 device_get_unit(parent->dev), VCHAN_FMT_HINT(direction), 740 &vfmt); 741 CHN_LOCK(parent); 742 if (r != 0) 743 vfmt = NULL; 744 if (vfmt != NULL) { 745 vchanfmt = snd_str2afmt(vfmt); 746 if (vchanfmt != 0 && !(vchanfmt & AFMT_VCHAN)) 747 vchanfmt = 0; 748 } 749 if (vchanfmt == 0) 750 vchanfmt = VCHAN_DEFAULT_FORMAT; 751 save = 1; 752 } 753 754 if (ret == 0 && vchanspd == 0) { 755 /* 756 * This is very sad. Few soundcards advertised as being 757 * able to do (insanely) higher/lower speed, but in 758 * reality, they simply can't. At least, we give user chance 759 * to set sane value via kernel hints or sysctl. 760 */ 761 CHN_UNLOCK(parent); 762 r = resource_int_value(device_get_name(parent->dev), 763 device_get_unit(parent->dev), VCHAN_SPD_HINT(direction), 764 &vchanspd); 765 CHN_LOCK(parent); 766 if (r != 0) { 767 /* 768 * No saved value, no hint, NOTHING. 769 * 770 * Workaround for sb16 running 771 * poorly at 45k / 49k. 772 */ 773 switch (parent_caps->maxspeed) { 774 case 45000: 775 case 49000: 776 vchanspd = 44100; 777 break; 778 default: 779 vchanspd = VCHAN_DEFAULT_RATE; 780 if (vchanspd > parent_caps->maxspeed) 781 vchanspd = parent_caps->maxspeed; 782 break; 783 } 784 if (vchanspd < parent_caps->minspeed) 785 vchanspd = parent_caps->minspeed; 786 } 787 save = 1; 788 } 789 790 if (ret == 0) { 791 /* 792 * Limit the speed between feeder_rate_min <-> feeder_rate_max. 793 */ 794 if (vchanspd < feeder_rate_min) 795 vchanspd = feeder_rate_min; 796 if (vchanspd > feeder_rate_max) 797 vchanspd = feeder_rate_max; 798 799 if (feeder_rate_round) { 800 RANGE(vchanspd, parent_caps->minspeed, 801 parent_caps->maxspeed); 802 vchanspd = CHANNEL_SETSPEED(parent->methods, 803 parent->devinfo, vchanspd); 804 } 805 806 ret = chn_reset(parent, vchanfmt, vchanspd); 807 } 808 809 if (ret == 0 && save) { 810 /* 811 * Save new value. 812 */ 813 if (direction == PCMDIR_PLAY_VIRTUAL) { 814 d->pvchanformat = parent->format; 815 d->pvchanrate = parent->speed; 816 } else { 817 d->rvchanformat = parent->format; 818 d->rvchanrate = parent->speed; 819 } 820 } 821 822 /* 823 * If the parent channel supports digital format, 824 * enable passthrough mode. 825 */ 826 if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) { 827 parent->flags &= ~CHN_F_VCHAN_DYNAMIC; 828 parent->flags |= CHN_F_VCHAN_PASSTHROUGH; 829 } 830 831 if (ret != 0) { 832 CHN_REMOVE(parent, ch, children); 833 parent->flags &= ~CHN_F_HAS_VCHAN; 834 CHN_UNLOCK(parent); 835 PCM_LOCK(d); 836 if (pcm_chn_remove(d, ch) == 0) { 837 PCM_UNLOCK(d); 838 chn_kill(ch); 839 } else 840 PCM_UNLOCK(d); 841 CHN_LOCK(parent); 842 } 843 844 return (ret); 845 } 846 847 int 848 vchan_destroy(struct pcm_channel *c) 849 { 850 struct pcm_channel *parent; 851 struct snddev_info *d; 852 int ret; 853 854 KASSERT(c != NULL && c->parentchannel != NULL && 855 c->parentsnddev != NULL, ("%s(): invalid channel=%p", 856 __func__, c)); 857 858 CHN_LOCKASSERT(c); 859 860 d = c->parentsnddev; 861 parent = c->parentchannel; 862 863 PCM_BUSYASSERT(d); 864 CHN_LOCKASSERT(parent); 865 866 CHN_UNLOCK(c); 867 868 if (!(parent->flags & CHN_F_BUSY)) 869 return (EBUSY); 870 871 if (CHN_EMPTY(parent, children)) 872 return (EINVAL); 873 874 /* remove us from our parent's children list */ 875 CHN_REMOVE(parent, c, children); 876 877 if (CHN_EMPTY(parent, children)) { 878 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 879 chn_reset(parent, parent->format, parent->speed); 880 } 881 882 CHN_UNLOCK(parent); 883 884 /* remove us from our grandparent's channel list */ 885 PCM_LOCK(d); 886 ret = pcm_chn_remove(d, c); 887 PCM_UNLOCK(d); 888 889 /* destroy ourselves */ 890 if (ret == 0) 891 chn_kill(c); 892 893 CHN_LOCK(parent); 894 895 return (ret); 896 } 897 898 int 899 #ifdef SND_DEBUG 900 vchan_passthrough(struct pcm_channel *c, const char *caller) 901 #else 902 vchan_sync(struct pcm_channel *c) 903 #endif 904 { 905 int ret; 906 907 KASSERT(c != NULL && c->parentchannel != NULL && 908 (c->flags & CHN_F_VIRTUAL), 909 ("%s(): invalid passthrough", __func__)); 910 CHN_LOCKASSERT(c); 911 CHN_LOCKASSERT(c->parentchannel); 912 913 sndbuf_setspd(c->bufhard, c->parentchannel->speed); 914 c->flags |= CHN_F_PASSTHROUGH; 915 ret = feeder_chain(c); 916 c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH); 917 if (ret != 0) 918 c->flags |= CHN_F_DIRTY; 919 920 #ifdef SND_DEBUG 921 if (snd_passthrough_verbose != 0) { 922 char *devname, buf[CHN_NAMELEN]; 923 924 devname = dsp_unit2name(buf, sizeof(buf), c); 925 device_printf(c->dev, 926 "%s(%s/%s) %s() -> re-sync err=%d\n", 927 __func__, (devname != NULL) ? devname : "dspX", c->comm, 928 caller, ret); 929 } 930 #endif 931 932 return (ret); 933 } 934 935 int 936 vchan_setnew(struct snddev_info *d, int direction, int newcnt, int num) 937 { 938 struct pcm_channel *c, *ch, *nch; 939 struct pcmchan_caps *caps; 940 int i, err, vcnt; 941 942 PCM_BUSYASSERT(d); 943 944 if ((direction == PCMDIR_PLAY && d->playcount < 1) || 945 (direction == PCMDIR_REC && d->reccount < 1)) 946 return (ENODEV); 947 948 if (!(d->flags & SD_F_AUTOVCHAN)) 949 return (EINVAL); 950 951 if (newcnt < 0 || newcnt > SND_MAXVCHANS) 952 return (E2BIG); 953 954 if (direction == PCMDIR_PLAY) 955 vcnt = d->pvchancount; 956 else if (direction == PCMDIR_REC) 957 vcnt = d->rvchancount; 958 else 959 return (EINVAL); 960 961 if (newcnt > vcnt) { 962 KASSERT(num == -1 || 963 (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt), 964 ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d", 965 num, newcnt, vcnt)); 966 /* add new vchans - find a parent channel first */ 967 ch = NULL; 968 CHN_FOREACH(c, d, channels.pcm) { 969 CHN_LOCK(c); 970 if (c->direction == direction && 971 ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 972 c->refcount < 1 && 973 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 974 /* 975 * Reuse hw channel with vchans already 976 * created. 977 */ 978 if (c->flags & CHN_F_HAS_VCHAN) { 979 ch = c; 980 break; 981 } 982 /* 983 * No vchans ever created, look for 984 * channels with supported formats. 985 */ 986 caps = chn_getcaps(c); 987 if (caps == NULL) { 988 CHN_UNLOCK(c); 989 continue; 990 } 991 for (i = 0; caps->fmtlist[i] != 0; i++) { 992 if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 993 break; 994 } 995 if (caps->fmtlist[i] != 0) { 996 ch = c; 997 break; 998 } 999 } 1000 CHN_UNLOCK(c); 1001 } 1002 if (ch == NULL) 1003 return (EBUSY); 1004 ch->flags |= CHN_F_BUSY; 1005 err = 0; 1006 while (err == 0 && newcnt > vcnt) { 1007 err = vchan_create(ch, num); 1008 if (err == 0) 1009 vcnt++; 1010 else if (err == E2BIG && newcnt > vcnt) 1011 device_printf(d->dev, 1012 "%s: err=%d Maximum channel reached.\n", 1013 __func__, err); 1014 } 1015 if (vcnt == 0) 1016 ch->flags &= ~CHN_F_BUSY; 1017 CHN_UNLOCK(ch); 1018 if (err != 0) 1019 return (err); 1020 } else if (newcnt < vcnt) { 1021 KASSERT(num == -1, 1022 ("bogus vchan_destroy() request num=%d", num)); 1023 CHN_FOREACH(c, d, channels.pcm) { 1024 CHN_LOCK(c); 1025 if (c->direction != direction || 1026 CHN_EMPTY(c, children) || 1027 !(c->flags & CHN_F_HAS_VCHAN)) { 1028 CHN_UNLOCK(c); 1029 continue; 1030 } 1031 CHN_FOREACH_SAFE(ch, c, nch, children) { 1032 CHN_LOCK(ch); 1033 if (vcnt == 1 && c->refcount > 0) { 1034 CHN_UNLOCK(ch); 1035 break; 1036 } 1037 if (!(ch->flags & CHN_F_BUSY) && 1038 ch->refcount < 1) { 1039 err = vchan_destroy(ch); 1040 if (err == 0) 1041 vcnt--; 1042 } else 1043 CHN_UNLOCK(ch); 1044 if (vcnt == newcnt) 1045 break; 1046 } 1047 CHN_UNLOCK(c); 1048 break; 1049 } 1050 } 1051 1052 return (0); 1053 } 1054 1055 void 1056 vchan_setmaxauto(struct snddev_info *d, int num) 1057 { 1058 PCM_BUSYASSERT(d); 1059 1060 if (num < 0) 1061 return; 1062 1063 if (num >= 0 && d->pvchancount > num) 1064 (void)vchan_setnew(d, PCMDIR_PLAY, num, -1); 1065 else if (num > 0 && d->pvchancount == 0) 1066 (void)vchan_setnew(d, PCMDIR_PLAY, 1, -1); 1067 1068 if (num >= 0 && d->rvchancount > num) 1069 (void)vchan_setnew(d, PCMDIR_REC, num, -1); 1070 else if (num > 0 && d->rvchancount == 0) 1071 (void)vchan_setnew(d, PCMDIR_REC, 1, -1); 1072 } 1073 1074 static int 1075 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 1076 { 1077 struct snddev_info *d; 1078 int i, v, error; 1079 1080 v = snd_maxautovchans; 1081 error = sysctl_handle_int(oidp, &v, 0, req); 1082 if (error == 0 && req->newptr != NULL) { 1083 if (v < 0) 1084 v = 0; 1085 if (v > SND_MAXVCHANS) 1086 v = SND_MAXVCHANS; 1087 snd_maxautovchans = v; 1088 for (i = 0; pcm_devclass != NULL && 1089 i < devclass_get_maxunit(pcm_devclass); i++) { 1090 d = devclass_get_softc(pcm_devclass, i); 1091 if (!PCM_REGISTERED(d)) 1092 continue; 1093 PCM_ACQUIRE_QUICK(d); 1094 vchan_setmaxauto(d, v); 1095 PCM_RELEASE_QUICK(d); 1096 } 1097 } 1098 return (error); 1099 } 1100 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, 1101 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 1102 sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 1103 1104 void 1105 vchan_initsys(device_t dev) 1106 { 1107 struct snddev_info *d; 1108 int unit; 1109 1110 unit = device_get_unit(dev); 1111 d = device_get_softc(dev); 1112 1113 /* Play */ 1114 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1115 SYSCTL_CHILDREN(d->play_sysctl_tree), 1116 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1117 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1118 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 1119 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1120 SYSCTL_CHILDREN(d->play_sysctl_tree), 1121 OID_AUTO, "vchanmode", 1122 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1123 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1124 sysctl_dev_pcm_vchanmode, "A", 1125 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1126 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1127 SYSCTL_CHILDREN(d->play_sysctl_tree), 1128 OID_AUTO, "vchanrate", 1129 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1130 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1131 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1132 SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1133 SYSCTL_CHILDREN(d->play_sysctl_tree), 1134 OID_AUTO, "vchanformat", 1135 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1136 VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 1137 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1138 /* Rec */ 1139 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1140 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1141 OID_AUTO, "vchans", 1142 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1143 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1144 sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 1145 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1146 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1147 OID_AUTO, "vchanmode", 1148 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1149 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1150 sysctl_dev_pcm_vchanmode, "A", 1151 "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1152 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1153 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1154 OID_AUTO, "vchanrate", 1155 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1156 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1157 sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1158 SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1159 SYSCTL_CHILDREN(d->rec_sysctl_tree), 1160 OID_AUTO, "vchanformat", 1161 CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1162 VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 1163 sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1164 } 1165