1139749Simp/*- 2193640Sariff * Copyright (c) 2006-2009 Ariff Abdullah <ariff@FreeBSD.org> 3164614Sariff * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org> 477269Scg * All rights reserved. 577269Scg * 677269Scg * Redistribution and use in source and binary forms, with or without 777269Scg * modification, are permitted provided that the following conditions 877269Scg * are met: 977269Scg * 1. Redistributions of source code must retain the above copyright 1077269Scg * notice, this list of conditions and the following disclaimer. 1177269Scg * 2. Redistributions in binary form must reproduce the above copyright 1277269Scg * notice, this list of conditions and the following disclaimer in the 1377269Scg * documentation and/or other materials provided with the distribution. 1477269Scg * 1577269Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1677269Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1777269Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1877269Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1977269Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2077269Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2177269Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2277269Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2377269Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2477269Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2577269Scg * SUCH DAMAGE. 2677269Scg */ 2777269Scg 28170206Sjoel/* Almost entirely rewritten to add multi-format/channels mixing support. */ 29170206Sjoel 30193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 31193640Sariff#include "opt_snd.h" 32193640Sariff#endif 33193640Sariff 3477269Scg#include <dev/sound/pcm/sound.h> 3577269Scg#include <dev/sound/pcm/vchan.h> 3677269Scg 3782180ScgSND_DECLARE_FILE("$FreeBSD: releng/10.3/sys/dev/sound/pcm/vchan.c 193640 2009-06-07 19:12:08Z ariff $"); 3882180Scg 39193640Sariff/* 40193640Sariff * [ac3 , dts , linear , 0, linear, 0] 41193640Sariff */ 42193640Sariff#define FMTLIST_MAX 6 43193640Sariff#define FMTLIST_OFFSET 4 44193640Sariff#define DIGFMTS_MAX 2 45164614Sariff 46193640Sariff#ifdef SND_DEBUG 47193640Sariffstatic int snd_passthrough_verbose = 0; 48193640SariffSYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RW, 49193640Sariff &snd_passthrough_verbose, 0, "passthrough verbosity"); 50148606Snetchild 51193640Sariff#endif 52193640Sariff 53193640Sariffstruct vchan_info { 54170161Sariff struct pcm_channel *channel; 5577269Scg struct pcmchan_caps caps; 56193640Sariff uint32_t fmtlist[FMTLIST_MAX]; 57170161Sariff int trigger; 5877269Scg}; 5977269Scg 6077269Scgstatic void * 61170161Sariffvchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 62170161Sariff struct pcm_channel *c, int dir) 6377269Scg{ 64193640Sariff struct vchan_info *info; 65193640Sariff struct pcm_channel *p; 66193640Sariff uint32_t i, j, *fmtlist; 6777269Scg 68170161Sariff KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC, 69170161Sariff ("vchan_init: bad direction")); 70170161Sariff KASSERT(c != NULL && c->parentchannel != NULL, 71170161Sariff ("vchan_init: bad channels")); 72170161Sariff 73193640Sariff info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO); 74193640Sariff info->channel = c; 75193640Sariff info->trigger = PCMTRIG_STOP; 76193640Sariff p = c->parentchannel; 7777269Scg 78193640Sariff CHN_LOCK(p); 79193640Sariff 80193640Sariff fmtlist = chn_getcaps(p)->fmtlist; 81193640Sariff for (i = 0, j = 0; fmtlist[i] != 0 && j < DIGFMTS_MAX; i++) { 82193640Sariff if (fmtlist[i] & AFMT_PASSTHROUGH) 83193640Sariff info->fmtlist[j++] = fmtlist[i]; 84193640Sariff } 85193640Sariff if (p->format & AFMT_VCHAN) 86193640Sariff info->fmtlist[j] = p->format; 87193640Sariff else 88193640Sariff info->fmtlist[j] = VCHAN_DEFAULT_FORMAT; 89193640Sariff info->caps.fmtlist = info->fmtlist + 90193640Sariff ((p->flags & CHN_F_VCHAN_DYNAMIC) ? 0 : FMTLIST_OFFSET); 91193640Sariff 92193640Sariff CHN_UNLOCK(p); 93193640Sariff 9477269Scg c->flags |= CHN_F_VIRTUAL; 9577269Scg 96193640Sariff return (info); 9777269Scg} 9877269Scg 9977269Scgstatic int 10077269Scgvchan_free(kobj_t obj, void *data) 10177269Scg{ 102193640Sariff 103166392Sariff free(data, M_DEVBUF); 104170161Sariff 105170161Sariff return (0); 10677269Scg} 10777269Scg 10877269Scgstatic int 109164614Sariffvchan_setformat(kobj_t obj, void *data, uint32_t format) 11077269Scg{ 111193640Sariff struct vchan_info *info; 11277269Scg 113193640Sariff info = data; 114193640Sariff 115193640Sariff CHN_LOCKASSERT(info->channel); 116193640Sariff 117193640Sariff if (!snd_fmtvalid(format, info->caps.fmtlist)) 118170161Sariff return (-1); 119170161Sariff 120170161Sariff return (0); 12177269Scg} 12277269Scg 123193640Sariffstatic uint32_t 124164614Sariffvchan_setspeed(kobj_t obj, void *data, uint32_t speed) 12577269Scg{ 126193640Sariff struct vchan_info *info; 12777269Scg 128193640Sariff info = data; 129193640Sariff 130193640Sariff CHN_LOCKASSERT(info->channel); 131193640Sariff 132193640Sariff return (info->caps.maxspeed); 13377269Scg} 13477269Scg 13577269Scgstatic int 136170161Sariffvchan_trigger(kobj_t obj, void *data, int go) 13777269Scg{ 138193640Sariff struct vchan_info *info; 139170161Sariff struct pcm_channel *c, *p; 140193640Sariff int ret, otrigger; 14177269Scg 142193640Sariff info = data; 143193640Sariff 144193640Sariff if (!PCMTRIG_COMMON(go) || go == info->trigger) 145170161Sariff return (0); 14677882Scg 147193640Sariff c = info->channel; 148170161Sariff p = c->parentchannel; 149193640Sariff otrigger = info->trigger; 150193640Sariff info->trigger = go; 15177882Scg 152193640Sariff CHN_LOCKASSERT(c); 153193640Sariff 154170161Sariff CHN_UNLOCK(c); 155170161Sariff CHN_LOCK(p); 15677269Scg 157170161Sariff switch (go) { 158170161Sariff case PCMTRIG_START: 159193640Sariff if (otrigger != PCMTRIG_START) 160170161Sariff CHN_INSERT_HEAD(p, c, children.busy); 161170161Sariff break; 162170161Sariff case PCMTRIG_STOP: 163170161Sariff case PCMTRIG_ABORT: 164193640Sariff if (otrigger == PCMTRIG_START) 165170161Sariff CHN_REMOVE(p, c, children.busy); 166170161Sariff break; 167170161Sariff default: 168170161Sariff break; 169170161Sariff } 17077269Scg 171193640Sariff ret = chn_notify(p, CHN_N_TRIGGER); 172193640Sariff 173193640Sariff CHN_LOCK(c); 174193640Sariff 175193640Sariff if (ret == 0 && go == PCMTRIG_START && VCHAN_SYNC_REQUIRED(c)) 176193640Sariff ret = vchan_sync(c); 177193640Sariff 178193640Sariff CHN_UNLOCK(c); 179170161Sariff CHN_UNLOCK(p); 180170161Sariff CHN_LOCK(c); 18177269Scg 182193640Sariff return (ret); 18377269Scg} 18477269Scg 18577269Scgstatic struct pcmchan_caps * 18677269Scgvchan_getcaps(kobj_t obj, void *data) 18777269Scg{ 188193640Sariff struct vchan_info *info; 189193640Sariff struct pcm_channel *c; 190193640Sariff uint32_t pformat, pspeed, pflags, i; 19177269Scg 192193640Sariff info = data; 193193640Sariff c = info->channel; 194193640Sariff pformat = c->parentchannel->format; 195193640Sariff pspeed = c->parentchannel->speed; 196193640Sariff pflags = c->parentchannel->flags; 197193640Sariff 198193640Sariff CHN_LOCKASSERT(c); 199193640Sariff 200193640Sariff if (pflags & CHN_F_VCHAN_DYNAMIC) { 201193640Sariff info->caps.fmtlist = info->fmtlist; 202193640Sariff if (pformat & AFMT_VCHAN) { 203193640Sariff for (i = 0; info->caps.fmtlist[i] != 0; i++) { 204193640Sariff if (info->caps.fmtlist[i] & AFMT_PASSTHROUGH) 205193640Sariff continue; 206193640Sariff break; 207193640Sariff } 208193640Sariff info->caps.fmtlist[i] = pformat; 209193640Sariff } 210193640Sariff if (c->format & AFMT_PASSTHROUGH) 211193640Sariff info->caps.minspeed = c->speed; 212193640Sariff else 213193640Sariff info->caps.minspeed = pspeed; 214193640Sariff info->caps.maxspeed = info->caps.minspeed; 215193640Sariff } else { 216193640Sariff info->caps.fmtlist = info->fmtlist + FMTLIST_OFFSET; 217193640Sariff if (pformat & AFMT_VCHAN) 218193640Sariff info->caps.fmtlist[0] = pformat; 219193640Sariff else { 220193640Sariff device_printf(c->dev, 221193640Sariff "%s(): invalid vchan format 0x%08x", 222193640Sariff __func__, pformat); 223193640Sariff info->caps.fmtlist[0] = VCHAN_DEFAULT_FORMAT; 224193640Sariff } 225193640Sariff info->caps.minspeed = pspeed; 226193640Sariff info->caps.maxspeed = info->caps.minspeed; 227164614Sariff } 22877269Scg 229193640Sariff return (&info->caps); 23077269Scg} 23177269Scg 232193640Sariffstatic struct pcmchan_matrix * 233193640Sariffvchan_getmatrix(kobj_t obj, void *data, uint32_t format) 234193640Sariff{ 235193640Sariff 236193640Sariff return (feeder_matrix_format_map(format)); 237193640Sariff} 238193640Sariff 23977269Scgstatic kobj_method_t vchan_methods[] = { 240164614Sariff KOBJMETHOD(channel_init, vchan_init), 241164614Sariff KOBJMETHOD(channel_free, vchan_free), 242164614Sariff KOBJMETHOD(channel_setformat, vchan_setformat), 243164614Sariff KOBJMETHOD(channel_setspeed, vchan_setspeed), 244164614Sariff KOBJMETHOD(channel_trigger, vchan_trigger), 245164614Sariff KOBJMETHOD(channel_getcaps, vchan_getcaps), 246193640Sariff KOBJMETHOD(channel_getmatrix, vchan_getmatrix), 247193640Sariff KOBJMETHOD_END 24877269Scg}; 24977269ScgCHANNEL_DECLARE(vchan); 25077269Scg 251193640Sariffstatic void 252193640Sariffpcm_getparentchannel(struct snddev_info *d, 253193640Sariff struct pcm_channel **wrch, struct pcm_channel **rdch) 254193640Sariff{ 255193640Sariff struct pcm_channel **ch, *wch, *rch, *c; 256193640Sariff 257193640Sariff KASSERT(d != NULL, ("%s(): NULL snddev_info", __func__)); 258193640Sariff 259193640Sariff PCM_BUSYASSERT(d); 260193640Sariff PCM_UNLOCKASSERT(d); 261193640Sariff 262193640Sariff wch = NULL; 263193640Sariff rch = NULL; 264193640Sariff 265193640Sariff CHN_FOREACH(c, d, channels.pcm) { 266193640Sariff CHN_LOCK(c); 267193640Sariff ch = (c->direction == PCMDIR_PLAY) ? &wch : &rch; 268193640Sariff if (c->flags & CHN_F_VIRTUAL) { 269193640Sariff /* Sanity check */ 270193640Sariff if (*ch != NULL && *ch != c->parentchannel) { 271193640Sariff CHN_UNLOCK(c); 272193640Sariff *ch = NULL; 273193640Sariff break; 274193640Sariff } 275193640Sariff } else if (c->flags & CHN_F_HAS_VCHAN) { 276193640Sariff /* No way!! */ 277193640Sariff if (*ch != NULL) { 278193640Sariff CHN_UNLOCK(c); 279193640Sariff *ch = NULL; 280193640Sariff break; 281193640Sariff } 282193640Sariff *ch = c; 283193640Sariff } 284193640Sariff CHN_UNLOCK(c); 285193640Sariff } 286193640Sariff 287193640Sariff if (wrch != NULL) 288193640Sariff *wrch = wch; 289193640Sariff if (rdch != NULL) 290193640Sariff *rdch = rch; 291193640Sariff} 292193640Sariff 293193640Sariffstatic int 294193640Sariffsysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS) 295193640Sariff{ 296193640Sariff struct snddev_info *d; 297193640Sariff int direction, vchancount; 298193640Sariff int err, cnt; 299193640Sariff 300193640Sariff d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 301193640Sariff if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 302193640Sariff return (EINVAL); 303193640Sariff 304193640Sariff PCM_LOCK(d); 305193640Sariff PCM_WAIT(d); 306193640Sariff 307193640Sariff switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 308193640Sariff case VCHAN_PLAY: 309193640Sariff direction = PCMDIR_PLAY; 310193640Sariff vchancount = d->pvchancount; 311193640Sariff cnt = d->playcount; 312193640Sariff break; 313193640Sariff case VCHAN_REC: 314193640Sariff direction = PCMDIR_REC; 315193640Sariff vchancount = d->rvchancount; 316193640Sariff cnt = d->reccount; 317193640Sariff break; 318193640Sariff default: 319193640Sariff PCM_UNLOCK(d); 320193640Sariff return (EINVAL); 321193640Sariff break; 322193640Sariff } 323193640Sariff 324193640Sariff if (cnt < 1) { 325193640Sariff PCM_UNLOCK(d); 326193640Sariff return (ENODEV); 327193640Sariff } 328193640Sariff 329193640Sariff PCM_ACQUIRE(d); 330193640Sariff PCM_UNLOCK(d); 331193640Sariff 332193640Sariff cnt = vchancount; 333193640Sariff err = sysctl_handle_int(oidp, &cnt, 0, req); 334193640Sariff 335193640Sariff if (err == 0 && req->newptr != NULL && vchancount != cnt) { 336193640Sariff if (cnt < 0) 337193640Sariff cnt = 0; 338193640Sariff if (cnt > SND_MAXVCHANS) 339193640Sariff cnt = SND_MAXVCHANS; 340193640Sariff err = pcm_setvchans(d, direction, cnt, -1); 341193640Sariff } 342193640Sariff 343193640Sariff PCM_RELEASE_QUICK(d); 344193640Sariff 345193640Sariff return err; 346193640Sariff} 347193640Sariff 348193640Sariffstatic int 349193640Sariffsysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS) 350193640Sariff{ 351193640Sariff struct snddev_info *d; 352193640Sariff struct pcm_channel *c; 353193640Sariff uint32_t dflags; 354193640Sariff int direction, ret; 355193640Sariff char dtype[16]; 356193640Sariff 357193640Sariff d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 358193640Sariff if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 359193640Sariff return (EINVAL); 360193640Sariff 361193640Sariff PCM_LOCK(d); 362193640Sariff PCM_WAIT(d); 363193640Sariff 364193640Sariff switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 365193640Sariff case VCHAN_PLAY: 366193640Sariff direction = PCMDIR_PLAY; 367193640Sariff break; 368193640Sariff case VCHAN_REC: 369193640Sariff direction = PCMDIR_REC; 370193640Sariff break; 371193640Sariff default: 372193640Sariff PCM_UNLOCK(d); 373193640Sariff return (EINVAL); 374193640Sariff break; 375193640Sariff } 376193640Sariff 377193640Sariff PCM_ACQUIRE(d); 378193640Sariff PCM_UNLOCK(d); 379193640Sariff 380193640Sariff if (direction == PCMDIR_PLAY) 381193640Sariff pcm_getparentchannel(d, &c, NULL); 382193640Sariff else 383193640Sariff pcm_getparentchannel(d, NULL, &c); 384193640Sariff 385193640Sariff if (c == NULL) { 386193640Sariff PCM_RELEASE_QUICK(d); 387193640Sariff return (EINVAL); 388193640Sariff } 389193640Sariff 390193640Sariff KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 391193640Sariff __func__, direction, c->direction)); 392193640Sariff 393193640Sariff CHN_LOCK(c); 394193640Sariff if (c->flags & CHN_F_VCHAN_PASSTHROUGH) 395193640Sariff strlcpy(dtype, "passthrough", sizeof(dtype)); 396193640Sariff else if (c->flags & CHN_F_VCHAN_ADAPTIVE) 397193640Sariff strlcpy(dtype, "adaptive", sizeof(dtype)); 398193640Sariff else 399193640Sariff strlcpy(dtype, "fixed", sizeof(dtype)); 400193640Sariff CHN_UNLOCK(c); 401193640Sariff 402193640Sariff ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req); 403193640Sariff if (ret == 0 && req->newptr != NULL) { 404193640Sariff if (strcasecmp(dtype, "passthrough") == 0 || 405193640Sariff strcmp(dtype, "1") == 0) 406193640Sariff dflags = CHN_F_VCHAN_PASSTHROUGH; 407193640Sariff else if (strcasecmp(dtype, "adaptive") == 0 || 408193640Sariff strcmp(dtype, "2") == 0) 409193640Sariff dflags = CHN_F_VCHAN_ADAPTIVE; 410193640Sariff else if (strcasecmp(dtype, "fixed") == 0 || 411193640Sariff strcmp(dtype, "0") == 0) 412193640Sariff dflags = 0; 413193640Sariff else { 414193640Sariff PCM_RELEASE_QUICK(d); 415193640Sariff return (EINVAL); 416193640Sariff } 417193640Sariff CHN_LOCK(c); 418193640Sariff if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) || 419193640Sariff (c->flags & CHN_F_PASSTHROUGH)) { 420193640Sariff CHN_UNLOCK(c); 421193640Sariff PCM_RELEASE_QUICK(d); 422193640Sariff return (0); 423193640Sariff } 424193640Sariff c->flags &= ~CHN_F_VCHAN_DYNAMIC; 425193640Sariff c->flags |= dflags; 426193640Sariff CHN_UNLOCK(c); 427193640Sariff } 428193640Sariff 429193640Sariff PCM_RELEASE_QUICK(d); 430193640Sariff 431193640Sariff return (ret); 432193640Sariff} 433193640Sariff 434148606Snetchild/* 435193640Sariff * On the fly vchan rate/format settings 436148606Snetchild */ 437193640Sariff 438193640Sariff#define VCHAN_ACCESSIBLE(c) (!((c)->flags & (CHN_F_PASSTHROUGH | \ 439193640Sariff CHN_F_EXCLUSIVE)) && \ 440193640Sariff (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \ 441193640Sariff CHN_STOPPED(c))) 442148606Snetchildstatic int 443193640Sariffsysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS) 444148606Snetchild{ 445148606Snetchild struct snddev_info *d; 446193640Sariff struct pcm_channel *c, *ch; 447148606Snetchild struct pcmchan_caps *caps; 448193640Sariff int *vchanrate, vchancount, direction, ret, newspd, restart; 449148606Snetchild 450170161Sariff d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 451170815Sariff if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 452170161Sariff return (EINVAL); 453170161Sariff 454193640Sariff PCM_LOCK(d); 455170815Sariff PCM_WAIT(d); 456170815Sariff 457170161Sariff switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 458170161Sariff case VCHAN_PLAY: 459170161Sariff direction = PCMDIR_PLAY; 460170161Sariff vchancount = d->pvchancount; 461170161Sariff vchanrate = &d->pvchanrate; 462170161Sariff break; 463170161Sariff case VCHAN_REC: 464170161Sariff direction = PCMDIR_REC; 465170161Sariff vchancount = d->rvchancount; 466170161Sariff vchanrate = &d->rvchanrate; 467170161Sariff break; 468170161Sariff default: 469193640Sariff PCM_UNLOCK(d); 470170161Sariff return (EINVAL); 471170161Sariff break; 472170161Sariff } 473170161Sariff 474170815Sariff if (vchancount < 1) { 475193640Sariff PCM_UNLOCK(d); 476170161Sariff return (EINVAL); 477157330Sariff } 478170815Sariff 479170815Sariff PCM_ACQUIRE(d); 480193640Sariff PCM_UNLOCK(d); 481170815Sariff 482193640Sariff if (direction == PCMDIR_PLAY) 483193640Sariff pcm_getparentchannel(d, &c, NULL); 484193640Sariff else 485193640Sariff pcm_getparentchannel(d, NULL, &c); 486170815Sariff 487193640Sariff if (c == NULL) { 488193640Sariff PCM_RELEASE_QUICK(d); 489193640Sariff return (EINVAL); 490148606Snetchild } 491193640Sariff 492193640Sariff KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 493193640Sariff __func__, direction, c->direction)); 494193640Sariff 495193640Sariff CHN_LOCK(c); 496193640Sariff newspd = c->speed; 497193640Sariff CHN_UNLOCK(c); 498193640Sariff 499193640Sariff ret = sysctl_handle_int(oidp, &newspd, 0, req); 500193640Sariff if (ret != 0 || req->newptr == NULL) { 501170815Sariff PCM_RELEASE_QUICK(d); 502193640Sariff return (ret); 503193640Sariff } 504193640Sariff 505193640Sariff if (newspd < 1 || newspd < feeder_rate_min || 506193640Sariff newspd > feeder_rate_max) { 507193640Sariff PCM_RELEASE_QUICK(d); 508170161Sariff return (EINVAL); 509148606Snetchild } 510170815Sariff 511193640Sariff CHN_LOCK(c); 512193640Sariff 513193640Sariff if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) { 514193640Sariff if (CHN_STARTED(c)) { 515193640Sariff chn_abort(c); 516193640Sariff restart = 1; 517193640Sariff } else 518193640Sariff restart = 0; 519193640Sariff 520164614Sariff if (feeder_rate_round) { 521193640Sariff caps = chn_getcaps(c); 522193640Sariff RANGE(newspd, caps->minspeed, caps->maxspeed); 523193640Sariff newspd = CHANNEL_SETSPEED(c->methods, 524193640Sariff c->devinfo, newspd); 525148606Snetchild } 526193640Sariff 527193640Sariff ret = chn_reset(c, c->format, newspd); 528193640Sariff if (ret == 0) { 529193640Sariff *vchanrate = c->speed; 530193640Sariff if (restart != 0) { 531193640Sariff CHN_FOREACH(ch, c, children.busy) { 532193640Sariff CHN_LOCK(ch); 533193640Sariff if (VCHAN_SYNC_REQUIRED(ch)) 534193640Sariff vchan_sync(ch); 535193640Sariff CHN_UNLOCK(ch); 536193640Sariff } 537193640Sariff c->flags |= CHN_F_DIRTY; 538193640Sariff ret = chn_start(c, 1); 539151456Sariff } 540170815Sariff } 541148606Snetchild } 542170815Sariff 543193640Sariff CHN_UNLOCK(c); 544193640Sariff 545170815Sariff PCM_RELEASE_QUICK(d); 546170815Sariff 547193640Sariff return (ret); 548148606Snetchild} 549164614Sariff 550164614Sariffstatic int 551193640Sariffsysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) 552164614Sariff{ 553164614Sariff struct snddev_info *d; 554193640Sariff struct pcm_channel *c, *ch; 555193640Sariff uint32_t newfmt; 556193640Sariff int *vchanformat, vchancount, direction, ret, restart; 557193640Sariff char fmtstr[AFMTSTR_LEN]; 558164614Sariff 559170161Sariff d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 560170815Sariff if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 561170161Sariff return (EINVAL); 562170161Sariff 563193640Sariff PCM_LOCK(d); 564170815Sariff PCM_WAIT(d); 565170815Sariff 566170161Sariff switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 567170161Sariff case VCHAN_PLAY: 568170161Sariff direction = PCMDIR_PLAY; 569170161Sariff vchancount = d->pvchancount; 570170161Sariff vchanformat = &d->pvchanformat; 571170161Sariff break; 572170161Sariff case VCHAN_REC: 573170161Sariff direction = PCMDIR_REC; 574170161Sariff vchancount = d->rvchancount; 575170161Sariff vchanformat = &d->rvchanformat; 576170161Sariff break; 577170161Sariff default: 578193640Sariff PCM_UNLOCK(d); 579170161Sariff return (EINVAL); 580170161Sariff break; 581170161Sariff } 582170161Sariff 583170815Sariff if (vchancount < 1) { 584193640Sariff PCM_UNLOCK(d); 585170161Sariff return (EINVAL); 586164614Sariff } 587170815Sariff 588170815Sariff PCM_ACQUIRE(d); 589193640Sariff PCM_UNLOCK(d); 590170815Sariff 591193640Sariff if (direction == PCMDIR_PLAY) 592193640Sariff pcm_getparentchannel(d, &c, NULL); 593193640Sariff else 594193640Sariff pcm_getparentchannel(d, NULL, &c); 595193640Sariff 596193640Sariff if (c == NULL) { 597193640Sariff PCM_RELEASE_QUICK(d); 598193640Sariff return (EINVAL); 599164614Sariff } 600193640Sariff 601193640Sariff KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 602193640Sariff __func__, direction, c->direction)); 603193640Sariff 604193640Sariff CHN_LOCK(c); 605193640Sariff 606193640Sariff bzero(fmtstr, sizeof(fmtstr)); 607193640Sariff 608193640Sariff if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format) 609193640Sariff strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr)); 610193640Sariff 611193640Sariff CHN_UNLOCK(c); 612193640Sariff 613193640Sariff ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 614193640Sariff if (ret != 0 || req->newptr == NULL) { 615170815Sariff PCM_RELEASE_QUICK(d); 616193640Sariff return (ret); 617193640Sariff } 618193640Sariff 619193640Sariff newfmt = snd_str2afmt(fmtstr); 620193640Sariff if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) { 621193640Sariff PCM_RELEASE_QUICK(d); 622170161Sariff return (EINVAL); 623164614Sariff } 624170815Sariff 625193640Sariff CHN_LOCK(c); 626193640Sariff 627193640Sariff if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) { 628193640Sariff if (CHN_STARTED(c)) { 629193640Sariff chn_abort(c); 630193640Sariff restart = 1; 631193640Sariff } else 632193640Sariff restart = 0; 633193640Sariff 634193640Sariff ret = chn_reset(c, newfmt, c->speed); 635193640Sariff if (ret == 0) { 636193640Sariff *vchanformat = c->format; 637193640Sariff if (restart != 0) { 638193640Sariff CHN_FOREACH(ch, c, children.busy) { 639193640Sariff CHN_LOCK(ch); 640193640Sariff if (VCHAN_SYNC_REQUIRED(ch)) 641193640Sariff vchan_sync(ch); 642193640Sariff CHN_UNLOCK(ch); 643193640Sariff } 644193640Sariff c->flags |= CHN_F_DIRTY; 645193640Sariff ret = chn_start(c, 1); 646164614Sariff } 647164614Sariff } 648164614Sariff } 649170815Sariff 650193640Sariff CHN_UNLOCK(c); 651193640Sariff 652170815Sariff PCM_RELEASE_QUICK(d); 653170815Sariff 654193640Sariff return (ret); 655164614Sariff} 656148606Snetchild 65777269Scg/* virtual channel interface */ 65877269Scg 659170161Sariff#define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 660170161Sariff "play.vchanformat" : "rec.vchanformat" 661170161Sariff#define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 662170161Sariff "play.vchanrate" : "rec.vchanrate" 663170161Sariff 66477269Scgint 665170161Sariffvchan_create(struct pcm_channel *parent, int num) 66677269Scg{ 667193640Sariff struct snddev_info *d; 668193640Sariff struct pcm_channel *ch; 669149953Snetchild struct pcmchan_caps *parent_caps; 670193640Sariff uint32_t vchanfmt, vchanspd; 671193640Sariff int ret, direction, r, save; 67277269Scg 673193640Sariff d = parent->parentsnddev; 674193640Sariff 675170815Sariff PCM_BUSYASSERT(d); 676193640Sariff CHN_LOCKASSERT(parent); 677170815Sariff 678149953Snetchild if (!(parent->flags & CHN_F_BUSY)) 679170161Sariff return (EBUSY); 680125136Struckman 681193640Sariff if (!(parent->direction == PCMDIR_PLAY || 682193640Sariff parent->direction == PCMDIR_REC)) 683193640Sariff return (EINVAL); 684193640Sariff 685193640Sariff d = parent->parentsnddev; 686193640Sariff 687193640Sariff CHN_UNLOCK(parent); 688193640Sariff PCM_LOCK(d); 689193640Sariff 690170161Sariff if (parent->direction == PCMDIR_PLAY) { 691170161Sariff direction = PCMDIR_PLAY_VIRTUAL; 692170161Sariff vchanfmt = d->pvchanformat; 693193640Sariff vchanspd = d->pvchanrate; 694193640Sariff } else { 695170161Sariff direction = PCMDIR_REC_VIRTUAL; 696170161Sariff vchanfmt = d->rvchanformat; 697193640Sariff vchanspd = d->rvchanrate; 698193640Sariff } 699149953Snetchild 70077269Scg /* create a new playback channel */ 701170161Sariff ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent); 702170161Sariff if (ch == NULL) { 703193640Sariff PCM_UNLOCK(d); 704164614Sariff CHN_LOCK(parent); 705170161Sariff return (ENODEV); 70677269Scg } 70777269Scg 70877269Scg /* add us to our grandparent's channel list */ 709193640Sariff ret = pcm_chn_add(d, ch); 710193640Sariff PCM_UNLOCK(d); 711193640Sariff if (ret != 0) { 712170161Sariff pcm_chn_destroy(ch); 713149953Snetchild CHN_LOCK(parent); 714193640Sariff return (ret); 71577269Scg } 71677269Scg 717164614Sariff CHN_LOCK(parent); 718193640Sariff /* 719193640Sariff * Add us to our parent channel's children in reverse order 720193640Sariff * so future destruction will pick the last (biggest number) 721193640Sariff * channel. 722193640Sariff */ 723193640Sariff CHN_INSERT_SORT_DESCEND(parent, ch, children); 724193640Sariff 725193640Sariff if (parent->flags & CHN_F_HAS_VCHAN) 726193640Sariff return (0); 727193640Sariff 728149953Snetchild parent->flags |= CHN_F_HAS_VCHAN; 729148606Snetchild 730193640Sariff parent_caps = chn_getcaps(parent); 731193640Sariff if (parent_caps == NULL) 732193640Sariff ret = EINVAL; 733148606Snetchild 734193640Sariff save = 0; 735164614Sariff 736193640Sariff if (ret == 0 && vchanfmt == 0) { 737193640Sariff const char *vfmt; 738193640Sariff 739193640Sariff CHN_UNLOCK(parent); 740193640Sariff r = resource_string_value(device_get_name(parent->dev), 741193640Sariff device_get_unit(parent->dev), VCHAN_FMT_HINT(direction), 742193640Sariff &vfmt); 743193640Sariff CHN_LOCK(parent); 744193640Sariff if (r != 0) 745193640Sariff vfmt = NULL; 746193640Sariff if (vfmt != NULL) { 747193640Sariff vchanfmt = snd_str2afmt(vfmt); 748193640Sariff if (vchanfmt != 0 && !(vchanfmt & AFMT_VCHAN)) 749193640Sariff vchanfmt = 0; 750164614Sariff } 751193640Sariff if (vchanfmt == 0) 752193640Sariff vchanfmt = VCHAN_DEFAULT_FORMAT; 753193640Sariff save = 1; 754193640Sariff } 755149953Snetchild 756193640Sariff if (ret == 0 && vchanspd == 0) { 757193640Sariff /* 758193640Sariff * This is very sad. Few soundcards advertised as being 759193640Sariff * able to do (insanely) higher/lower speed, but in 760193640Sariff * reality, they simply can't. At least, we give user chance 761193640Sariff * to set sane value via kernel hints or sysctl. 762193640Sariff */ 763193640Sariff CHN_UNLOCK(parent); 764193640Sariff r = resource_int_value(device_get_name(parent->dev), 765193640Sariff device_get_unit(parent->dev), VCHAN_SPD_HINT(direction), 766193640Sariff &vchanspd); 767193640Sariff CHN_LOCK(parent); 768193640Sariff if (r != 0) { 769148606Snetchild /* 770193640Sariff * No saved value, no hint, NOTHING. 771193640Sariff * 772193640Sariff * Workaround for sb16 running 773193640Sariff * poorly at 45k / 49k. 774148606Snetchild */ 775193640Sariff switch (parent_caps->maxspeed) { 776193640Sariff case 45000: 777193640Sariff case 49000: 778193640Sariff vchanspd = 44100; 779193640Sariff break; 780193640Sariff default: 781193640Sariff vchanspd = VCHAN_DEFAULT_RATE; 782193640Sariff if (vchanspd > parent_caps->maxspeed) 783193640Sariff vchanspd = parent_caps->maxspeed; 784193640Sariff break; 785149953Snetchild } 786193640Sariff if (vchanspd < parent_caps->minspeed) 787193640Sariff vchanspd = parent_caps->minspeed; 788193640Sariff } 789193640Sariff save = 1; 790193640Sariff } 791148606Snetchild 792193640Sariff if (ret == 0) { 793193640Sariff /* 794193640Sariff * Limit the speed between feeder_rate_min <-> feeder_rate_max. 795193640Sariff */ 796193640Sariff if (vchanspd < feeder_rate_min) 797193640Sariff vchanspd = feeder_rate_min; 798193640Sariff if (vchanspd > feeder_rate_max) 799193640Sariff vchanspd = feeder_rate_max; 800148606Snetchild 801193640Sariff if (feeder_rate_round) { 802193640Sariff RANGE(vchanspd, parent_caps->minspeed, 803193640Sariff parent_caps->maxspeed); 804193640Sariff vchanspd = CHANNEL_SETSPEED(parent->methods, 805193640Sariff parent->devinfo, vchanspd); 806193640Sariff } 807148606Snetchild 808193640Sariff ret = chn_reset(parent, vchanfmt, vchanspd); 809193640Sariff } 810149953Snetchild 811193640Sariff if (ret == 0 && save) { 812193640Sariff /* 813193640Sariff * Save new value. 814193640Sariff */ 815193640Sariff if (direction == PCMDIR_PLAY_VIRTUAL) { 816193640Sariff d->pvchanformat = parent->format; 817193640Sariff d->pvchanrate = parent->speed; 818193640Sariff } else { 819193640Sariff d->rvchanformat = parent->format; 820193640Sariff d->rvchanrate = parent->speed; 821148606Snetchild } 82277269Scg } 823193640Sariff 824193640Sariff /* 825193640Sariff * If the parent channel supports digital format, 826193640Sariff * enable passthrough mode. 827193640Sariff */ 828193640Sariff if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) { 829193640Sariff parent->flags &= ~CHN_F_VCHAN_DYNAMIC; 830193640Sariff parent->flags |= CHN_F_VCHAN_PASSTHROUGH; 831193640Sariff } 83277269Scg 833193640Sariff if (ret != 0) { 834193640Sariff CHN_REMOVE(parent, ch, children); 835193640Sariff parent->flags &= ~CHN_F_HAS_VCHAN; 836193640Sariff CHN_UNLOCK(parent); 837193640Sariff PCM_LOCK(d); 838193640Sariff if (pcm_chn_remove(d, ch) == 0) { 839193640Sariff PCM_UNLOCK(d); 840193640Sariff pcm_chn_destroy(ch); 841193640Sariff } else 842193640Sariff PCM_UNLOCK(d); 843193640Sariff CHN_LOCK(parent); 844193640Sariff } 845193640Sariff 846193640Sariff return (ret); 84777269Scg} 84877269Scg 84977269Scgint 85077269Scgvchan_destroy(struct pcm_channel *c) 85177269Scg{ 852193640Sariff struct pcm_channel *parent; 853193640Sariff struct snddev_info *d; 854193640Sariff int ret; 85577269Scg 856193640Sariff KASSERT(c != NULL && c->parentchannel != NULL && 857193640Sariff c->parentsnddev != NULL, ("%s(): invalid channel=%p", 858193640Sariff __func__, c)); 859193640Sariff 860193640Sariff CHN_LOCKASSERT(c); 861193640Sariff 862193640Sariff d = c->parentsnddev; 863193640Sariff parent = c->parentchannel; 864193640Sariff 865170815Sariff PCM_BUSYASSERT(d); 866193640Sariff CHN_LOCKASSERT(parent); 867170815Sariff 868193640Sariff CHN_UNLOCK(c); 869193640Sariff 870193640Sariff if (!(parent->flags & CHN_F_BUSY)) 871170161Sariff return (EBUSY); 872193640Sariff 873193640Sariff if (CHN_EMPTY(parent, children)) 874170161Sariff return (EINVAL); 87577882Scg 87677269Scg /* remove us from our parent's children list */ 877170161Sariff CHN_REMOVE(parent, c, children); 87877269Scg 879170161Sariff if (CHN_EMPTY(parent, children)) { 880157330Sariff parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 881193640Sariff chn_reset(parent, parent->format, parent->speed); 882149953Snetchild } 88378895Scg 884170161Sariff CHN_UNLOCK(parent); 885170161Sariff 886124617Sphk /* remove us from our grandparent's channel list */ 887193640Sariff PCM_LOCK(d); 888193640Sariff ret = pcm_chn_remove(d, c); 889193640Sariff PCM_UNLOCK(d); 89078895Scg 89177269Scg /* destroy ourselves */ 892193640Sariff if (ret == 0) 893193640Sariff ret = pcm_chn_destroy(c); 89477269Scg 895193640Sariff CHN_LOCK(parent); 896193640Sariff 897193640Sariff return (ret); 89877269Scg} 89977269Scg 90082180Scgint 901193640Sariff#ifdef SND_DEBUG 902193640Sariffvchan_passthrough(struct pcm_channel *c, const char *caller) 903193640Sariff#else 904193640Sariffvchan_sync(struct pcm_channel *c) 905193640Sariff#endif 906193640Sariff{ 907193640Sariff int ret; 908193640Sariff 909193640Sariff KASSERT(c != NULL && c->parentchannel != NULL && 910193640Sariff (c->flags & CHN_F_VIRTUAL), 911193640Sariff ("%s(): invalid passthrough", __func__)); 912193640Sariff CHN_LOCKASSERT(c); 913193640Sariff CHN_LOCKASSERT(c->parentchannel); 914193640Sariff 915193640Sariff sndbuf_setspd(c->bufhard, c->parentchannel->speed); 916193640Sariff c->flags |= CHN_F_PASSTHROUGH; 917193640Sariff ret = feeder_chain(c); 918193640Sariff c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH); 919193640Sariff if (ret != 0) 920193640Sariff c->flags |= CHN_F_DIRTY; 921193640Sariff 922193640Sariff#ifdef SND_DEBUG 923193640Sariff if (snd_passthrough_verbose != 0) { 924193640Sariff char *devname, buf[CHN_NAMELEN]; 925193640Sariff 926193640Sariff devname = dsp_unit2name(buf, sizeof(buf), c->unit); 927193640Sariff device_printf(c->dev, 928193640Sariff "%s(%s/%s) %s() -> re-sync err=%d\n", 929193640Sariff __func__, (devname != NULL) ? devname : "dspX", c->comm, 930193640Sariff caller, ret); 931193640Sariff } 932193640Sariff#endif 933193640Sariff 934193640Sariff return (ret); 935193640Sariff} 936193640Sariff 937193640Sariffvoid 93882180Scgvchan_initsys(device_t dev) 93982180Scg{ 94077269Scg struct snddev_info *d; 941170161Sariff int unit; 94277269Scg 943170161Sariff unit = device_get_unit(dev); 944164614Sariff d = device_get_softc(dev); 945170161Sariff 946170161Sariff /* Play */ 947170161Sariff SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 948170161Sariff SYSCTL_CHILDREN(d->play_sysctl_tree), 949170161Sariff OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, 950170161Sariff VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 951193640Sariff sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 952170161Sariff SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 953170161Sariff SYSCTL_CHILDREN(d->play_sysctl_tree), 954193640Sariff OID_AUTO, "vchanmode", CTLTYPE_STRING | CTLFLAG_RW, 955193640Sariff VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 956193640Sariff sysctl_dev_pcm_vchanmode, "A", 957193640Sariff "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 958193640Sariff SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 959193640Sariff SYSCTL_CHILDREN(d->play_sysctl_tree), 960170161Sariff OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, 961170161Sariff VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 962193640Sariff sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 963170161Sariff SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 964170161Sariff SYSCTL_CHILDREN(d->play_sysctl_tree), 965170161Sariff OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, 966170161Sariff VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 967193640Sariff sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 968170161Sariff /* Rec */ 969170161Sariff SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 970170161Sariff SYSCTL_CHILDREN(d->rec_sysctl_tree), 971170161Sariff OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, 972170161Sariff VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 973193640Sariff sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 974170161Sariff SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 975170161Sariff SYSCTL_CHILDREN(d->rec_sysctl_tree), 976193640Sariff OID_AUTO, "vchanmode", CTLTYPE_STRING | CTLFLAG_RW, 977193640Sariff VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 978193640Sariff sysctl_dev_pcm_vchanmode, "A", 979193640Sariff "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 980193640Sariff SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 981193640Sariff SYSCTL_CHILDREN(d->rec_sysctl_tree), 982170161Sariff OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, 983170161Sariff VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 984193640Sariff sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 985170161Sariff SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 986170161Sariff SYSCTL_CHILDREN(d->rec_sysctl_tree), 987170161Sariff OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, 988170161Sariff VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 989193640Sariff sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 99077269Scg} 991