1098ca2bdSWarner Losh /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 490da2b28SAriff Abdullah * Copyright (c) 2006-2009 Ariff Abdullah <ariff@FreeBSD.org> 5a580b31aSAriff Abdullah * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org> 6285648f9SCameron Grant * All rights reserved. 7285648f9SCameron Grant * 8285648f9SCameron Grant * Redistribution and use in source and binary forms, with or without 9285648f9SCameron Grant * modification, are permitted provided that the following conditions 10285648f9SCameron Grant * are met: 11285648f9SCameron Grant * 1. Redistributions of source code must retain the above copyright 12285648f9SCameron Grant * notice, this list of conditions and the following disclaimer. 13285648f9SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 14285648f9SCameron Grant * notice, this list of conditions and the following disclaimer in the 15285648f9SCameron Grant * documentation and/or other materials provided with the distribution. 16285648f9SCameron Grant * 17285648f9SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18285648f9SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19285648f9SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20285648f9SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21285648f9SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22285648f9SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23285648f9SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24285648f9SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25285648f9SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26285648f9SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27285648f9SCameron Grant * SUCH DAMAGE. 28285648f9SCameron Grant */ 29285648f9SCameron Grant 30e9577a5cSJoel Dahl /* Almost entirely rewritten to add multi-format/channels mixing support. */ 31e9577a5cSJoel Dahl 3290da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 3390da2b28SAriff Abdullah #include "opt_snd.h" 3490da2b28SAriff Abdullah #endif 3590da2b28SAriff Abdullah 36285648f9SCameron Grant #include <dev/sound/pcm/sound.h> 37285648f9SCameron Grant #include <dev/sound/pcm/vchan.h> 38285648f9SCameron Grant 3967b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 4067b1dce3SCameron Grant 4190da2b28SAriff Abdullah /* 4290da2b28SAriff Abdullah * [ac3 , dts , linear , 0, linear, 0] 4390da2b28SAriff Abdullah */ 4490da2b28SAriff Abdullah #define FMTLIST_MAX 6 4590da2b28SAriff Abdullah #define FMTLIST_OFFSET 4 4690da2b28SAriff Abdullah #define DIGFMTS_MAX 2 47a580b31aSAriff Abdullah 4890da2b28SAriff Abdullah #ifdef SND_DEBUG 4990da2b28SAriff Abdullah static int snd_passthrough_verbose = 0; 5032a0e5d5SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RWTUN, 5190da2b28SAriff Abdullah &snd_passthrough_verbose, 0, "passthrough verbosity"); 5287506547SAlexander Leidinger 5390da2b28SAriff Abdullah #endif 5490da2b28SAriff Abdullah 5590da2b28SAriff Abdullah struct vchan_info { 56bba4862cSAriff Abdullah struct pcm_channel *channel; 57285648f9SCameron Grant struct pcmchan_caps caps; 5890da2b28SAriff Abdullah uint32_t fmtlist[FMTLIST_MAX]; 59bba4862cSAriff Abdullah int trigger; 60285648f9SCameron Grant }; 61285648f9SCameron Grant 62285648f9SCameron Grant static void * 63bba4862cSAriff Abdullah vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 64bba4862cSAriff Abdullah struct pcm_channel *c, int dir) 65285648f9SCameron Grant { 6690da2b28SAriff Abdullah struct vchan_info *info; 6790da2b28SAriff Abdullah struct pcm_channel *p; 6890da2b28SAriff Abdullah uint32_t i, j, *fmtlist; 69285648f9SCameron Grant 70bba4862cSAriff Abdullah KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC, 71bba4862cSAriff Abdullah ("vchan_init: bad direction")); 72bba4862cSAriff Abdullah KASSERT(c != NULL && c->parentchannel != NULL, 73bba4862cSAriff Abdullah ("vchan_init: bad channels")); 74bba4862cSAriff Abdullah 7590da2b28SAriff Abdullah info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO); 7690da2b28SAriff Abdullah info->channel = c; 7790da2b28SAriff Abdullah info->trigger = PCMTRIG_STOP; 7890da2b28SAriff Abdullah p = c->parentchannel; 7990da2b28SAriff Abdullah 8090da2b28SAriff Abdullah CHN_LOCK(p); 8190da2b28SAriff Abdullah 8290da2b28SAriff Abdullah fmtlist = chn_getcaps(p)->fmtlist; 8390da2b28SAriff Abdullah for (i = 0, j = 0; fmtlist[i] != 0 && j < DIGFMTS_MAX; i++) { 8490da2b28SAriff Abdullah if (fmtlist[i] & AFMT_PASSTHROUGH) 8590da2b28SAriff Abdullah info->fmtlist[j++] = fmtlist[i]; 8690da2b28SAriff Abdullah } 8790da2b28SAriff Abdullah if (p->format & AFMT_VCHAN) 8890da2b28SAriff Abdullah info->fmtlist[j] = p->format; 8990da2b28SAriff Abdullah else 9090da2b28SAriff Abdullah info->fmtlist[j] = VCHAN_DEFAULT_FORMAT; 9190da2b28SAriff Abdullah info->caps.fmtlist = info->fmtlist + 9290da2b28SAriff Abdullah ((p->flags & CHN_F_VCHAN_DYNAMIC) ? 0 : FMTLIST_OFFSET); 9390da2b28SAriff Abdullah 9490da2b28SAriff Abdullah CHN_UNLOCK(p); 95285648f9SCameron Grant 96285648f9SCameron Grant c->flags |= CHN_F_VIRTUAL; 97285648f9SCameron Grant 9890da2b28SAriff Abdullah return (info); 99285648f9SCameron Grant } 100285648f9SCameron Grant 101285648f9SCameron Grant static int 102285648f9SCameron Grant vchan_free(kobj_t obj, void *data) 103285648f9SCameron Grant { 10490da2b28SAriff Abdullah 105e444a209SAriff Abdullah free(data, M_DEVBUF); 106bba4862cSAriff Abdullah 107bba4862cSAriff Abdullah return (0); 108285648f9SCameron Grant } 109285648f9SCameron Grant 110285648f9SCameron Grant static int 111a580b31aSAriff Abdullah vchan_setformat(kobj_t obj, void *data, uint32_t format) 112285648f9SCameron Grant { 11390da2b28SAriff Abdullah struct vchan_info *info; 114285648f9SCameron Grant 11590da2b28SAriff Abdullah info = data; 11690da2b28SAriff Abdullah 11790da2b28SAriff Abdullah CHN_LOCKASSERT(info->channel); 11890da2b28SAriff Abdullah 11990da2b28SAriff Abdullah if (!snd_fmtvalid(format, info->caps.fmtlist)) 120bba4862cSAriff Abdullah return (-1); 121bba4862cSAriff Abdullah 122bba4862cSAriff Abdullah return (0); 123285648f9SCameron Grant } 124285648f9SCameron Grant 12590da2b28SAriff Abdullah static uint32_t 126a580b31aSAriff Abdullah vchan_setspeed(kobj_t obj, void *data, uint32_t speed) 127285648f9SCameron Grant { 12890da2b28SAriff Abdullah struct vchan_info *info; 129285648f9SCameron Grant 13090da2b28SAriff Abdullah info = data; 13190da2b28SAriff Abdullah 13290da2b28SAriff Abdullah CHN_LOCKASSERT(info->channel); 13390da2b28SAriff Abdullah 13490da2b28SAriff Abdullah return (info->caps.maxspeed); 135285648f9SCameron Grant } 136285648f9SCameron Grant 137285648f9SCameron Grant static int 138285648f9SCameron Grant vchan_trigger(kobj_t obj, void *data, int go) 139285648f9SCameron Grant { 14090da2b28SAriff Abdullah struct vchan_info *info; 141bba4862cSAriff Abdullah struct pcm_channel *c, *p; 14290da2b28SAriff Abdullah int ret, otrigger; 143285648f9SCameron Grant 14490da2b28SAriff Abdullah info = data; 14590da2b28SAriff Abdullah 14690da2b28SAriff Abdullah if (!PCMTRIG_COMMON(go) || go == info->trigger) 147bba4862cSAriff Abdullah return (0); 148285648f9SCameron Grant 14990da2b28SAriff Abdullah c = info->channel; 150bba4862cSAriff Abdullah p = c->parentchannel; 15190da2b28SAriff Abdullah otrigger = info->trigger; 15290da2b28SAriff Abdullah info->trigger = go; 15390da2b28SAriff Abdullah 15490da2b28SAriff Abdullah CHN_LOCKASSERT(c); 155285648f9SCameron Grant 156bba4862cSAriff Abdullah CHN_UNLOCK(c); 157bba4862cSAriff Abdullah CHN_LOCK(p); 158bba4862cSAriff Abdullah 159bba4862cSAriff Abdullah switch (go) { 160bba4862cSAriff Abdullah case PCMTRIG_START: 16190da2b28SAriff Abdullah if (otrigger != PCMTRIG_START) 162bba4862cSAriff Abdullah CHN_INSERT_HEAD(p, c, children.busy); 163bba4862cSAriff Abdullah break; 164bba4862cSAriff Abdullah case PCMTRIG_STOP: 165bba4862cSAriff Abdullah case PCMTRIG_ABORT: 16690da2b28SAriff Abdullah if (otrigger == PCMTRIG_START) 167bba4862cSAriff Abdullah CHN_REMOVE(p, c, children.busy); 168bba4862cSAriff Abdullah break; 169bba4862cSAriff Abdullah default: 170bba4862cSAriff Abdullah break; 171bba4862cSAriff Abdullah } 172bba4862cSAriff Abdullah 17390da2b28SAriff Abdullah ret = chn_notify(p, CHN_N_TRIGGER); 17490da2b28SAriff Abdullah 17590da2b28SAriff Abdullah CHN_LOCK(c); 17690da2b28SAriff Abdullah 17790da2b28SAriff Abdullah if (ret == 0 && go == PCMTRIG_START && VCHAN_SYNC_REQUIRED(c)) 17890da2b28SAriff Abdullah ret = vchan_sync(c); 17990da2b28SAriff Abdullah 18090da2b28SAriff Abdullah CHN_UNLOCK(c); 181bba4862cSAriff Abdullah CHN_UNLOCK(p); 182bba4862cSAriff Abdullah CHN_LOCK(c); 183bba4862cSAriff Abdullah 18490da2b28SAriff Abdullah return (ret); 185285648f9SCameron Grant } 186285648f9SCameron Grant 187285648f9SCameron Grant static struct pcmchan_caps * 188285648f9SCameron Grant vchan_getcaps(kobj_t obj, void *data) 189285648f9SCameron Grant { 19090da2b28SAriff Abdullah struct vchan_info *info; 19190da2b28SAriff Abdullah struct pcm_channel *c; 19290da2b28SAriff Abdullah uint32_t pformat, pspeed, pflags, i; 193285648f9SCameron Grant 19490da2b28SAriff Abdullah info = data; 19590da2b28SAriff Abdullah c = info->channel; 19690da2b28SAriff Abdullah pformat = c->parentchannel->format; 19790da2b28SAriff Abdullah pspeed = c->parentchannel->speed; 19890da2b28SAriff Abdullah pflags = c->parentchannel->flags; 19990da2b28SAriff Abdullah 20090da2b28SAriff Abdullah CHN_LOCKASSERT(c); 20190da2b28SAriff Abdullah 20290da2b28SAriff Abdullah if (pflags & CHN_F_VCHAN_DYNAMIC) { 20390da2b28SAriff Abdullah info->caps.fmtlist = info->fmtlist; 20490da2b28SAriff Abdullah if (pformat & AFMT_VCHAN) { 20590da2b28SAriff Abdullah for (i = 0; info->caps.fmtlist[i] != 0; i++) { 20690da2b28SAriff Abdullah if (info->caps.fmtlist[i] & AFMT_PASSTHROUGH) 20790da2b28SAriff Abdullah continue; 20890da2b28SAriff Abdullah break; 209a580b31aSAriff Abdullah } 21090da2b28SAriff Abdullah info->caps.fmtlist[i] = pformat; 21190da2b28SAriff Abdullah } 21290da2b28SAriff Abdullah if (c->format & AFMT_PASSTHROUGH) 21390da2b28SAriff Abdullah info->caps.minspeed = c->speed; 21490da2b28SAriff Abdullah else 21590da2b28SAriff Abdullah info->caps.minspeed = pspeed; 21690da2b28SAriff Abdullah info->caps.maxspeed = info->caps.minspeed; 21790da2b28SAriff Abdullah } else { 21890da2b28SAriff Abdullah info->caps.fmtlist = info->fmtlist + FMTLIST_OFFSET; 21990da2b28SAriff Abdullah if (pformat & AFMT_VCHAN) 22090da2b28SAriff Abdullah info->caps.fmtlist[0] = pformat; 22190da2b28SAriff Abdullah else { 22290da2b28SAriff Abdullah device_printf(c->dev, 22390da2b28SAriff Abdullah "%s(): invalid vchan format 0x%08x", 22490da2b28SAriff Abdullah __func__, pformat); 22590da2b28SAriff Abdullah info->caps.fmtlist[0] = VCHAN_DEFAULT_FORMAT; 22690da2b28SAriff Abdullah } 22790da2b28SAriff Abdullah info->caps.minspeed = pspeed; 22890da2b28SAriff Abdullah info->caps.maxspeed = info->caps.minspeed; 22990da2b28SAriff Abdullah } 230285648f9SCameron Grant 23190da2b28SAriff Abdullah return (&info->caps); 23290da2b28SAriff Abdullah } 23390da2b28SAriff Abdullah 23490da2b28SAriff Abdullah static struct pcmchan_matrix * 23590da2b28SAriff Abdullah vchan_getmatrix(kobj_t obj, void *data, uint32_t format) 23690da2b28SAriff Abdullah { 23790da2b28SAriff Abdullah 23890da2b28SAriff Abdullah return (feeder_matrix_format_map(format)); 239285648f9SCameron Grant } 240285648f9SCameron Grant 241285648f9SCameron Grant static kobj_method_t vchan_methods[] = { 242285648f9SCameron Grant KOBJMETHOD(channel_init, vchan_init), 243285648f9SCameron Grant KOBJMETHOD(channel_free, vchan_free), 244285648f9SCameron Grant KOBJMETHOD(channel_setformat, vchan_setformat), 245285648f9SCameron Grant KOBJMETHOD(channel_setspeed, vchan_setspeed), 246285648f9SCameron Grant KOBJMETHOD(channel_trigger, vchan_trigger), 247285648f9SCameron Grant KOBJMETHOD(channel_getcaps, vchan_getcaps), 24890da2b28SAriff Abdullah KOBJMETHOD(channel_getmatrix, vchan_getmatrix), 24990da2b28SAriff Abdullah KOBJMETHOD_END 250285648f9SCameron Grant }; 251285648f9SCameron Grant CHANNEL_DECLARE(vchan); 252285648f9SCameron Grant 25390da2b28SAriff Abdullah static void 25490da2b28SAriff Abdullah pcm_getparentchannel(struct snddev_info *d, 25590da2b28SAriff Abdullah struct pcm_channel **wrch, struct pcm_channel **rdch) 25690da2b28SAriff Abdullah { 25790da2b28SAriff Abdullah struct pcm_channel **ch, *wch, *rch, *c; 25890da2b28SAriff Abdullah 25990da2b28SAriff Abdullah KASSERT(d != NULL, ("%s(): NULL snddev_info", __func__)); 26090da2b28SAriff Abdullah 26190da2b28SAriff Abdullah PCM_BUSYASSERT(d); 26290da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 26390da2b28SAriff Abdullah 26490da2b28SAriff Abdullah wch = NULL; 26590da2b28SAriff Abdullah rch = NULL; 26690da2b28SAriff Abdullah 26790da2b28SAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 26890da2b28SAriff Abdullah CHN_LOCK(c); 26990da2b28SAriff Abdullah ch = (c->direction == PCMDIR_PLAY) ? &wch : &rch; 27090da2b28SAriff Abdullah if (c->flags & CHN_F_VIRTUAL) { 27190da2b28SAriff Abdullah /* Sanity check */ 27290da2b28SAriff Abdullah if (*ch != NULL && *ch != c->parentchannel) { 27390da2b28SAriff Abdullah CHN_UNLOCK(c); 27490da2b28SAriff Abdullah *ch = NULL; 27590da2b28SAriff Abdullah break; 27690da2b28SAriff Abdullah } 27790da2b28SAriff Abdullah } else if (c->flags & CHN_F_HAS_VCHAN) { 27890da2b28SAriff Abdullah /* No way!! */ 27990da2b28SAriff Abdullah if (*ch != NULL) { 28090da2b28SAriff Abdullah CHN_UNLOCK(c); 28190da2b28SAriff Abdullah *ch = NULL; 28290da2b28SAriff Abdullah break; 28390da2b28SAriff Abdullah } 28490da2b28SAriff Abdullah *ch = c; 28590da2b28SAriff Abdullah } 28690da2b28SAriff Abdullah CHN_UNLOCK(c); 28790da2b28SAriff Abdullah } 28890da2b28SAriff Abdullah 28990da2b28SAriff Abdullah if (wrch != NULL) 29090da2b28SAriff Abdullah *wrch = wch; 29190da2b28SAriff Abdullah if (rdch != NULL) 29290da2b28SAriff Abdullah *rdch = rch; 29390da2b28SAriff Abdullah } 29490da2b28SAriff Abdullah 29587506547SAlexander Leidinger static int 29690da2b28SAriff Abdullah sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS) 29787506547SAlexander Leidinger { 29887506547SAlexander Leidinger struct snddev_info *d; 29990da2b28SAriff Abdullah int direction, vchancount; 30090da2b28SAriff Abdullah int err, cnt; 30187506547SAlexander Leidinger 302bba4862cSAriff Abdullah d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 303e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 304bba4862cSAriff Abdullah return (EINVAL); 305bba4862cSAriff Abdullah 30690da2b28SAriff Abdullah PCM_LOCK(d); 30790da2b28SAriff Abdullah PCM_WAIT(d); 30890da2b28SAriff Abdullah 30990da2b28SAriff Abdullah switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 31090da2b28SAriff Abdullah case VCHAN_PLAY: 31190da2b28SAriff Abdullah direction = PCMDIR_PLAY; 31290da2b28SAriff Abdullah vchancount = d->pvchancount; 31390da2b28SAriff Abdullah cnt = d->playcount; 31490da2b28SAriff Abdullah break; 31590da2b28SAriff Abdullah case VCHAN_REC: 31690da2b28SAriff Abdullah direction = PCMDIR_REC; 31790da2b28SAriff Abdullah vchancount = d->rvchancount; 31890da2b28SAriff Abdullah cnt = d->reccount; 31990da2b28SAriff Abdullah break; 32090da2b28SAriff Abdullah default: 32190da2b28SAriff Abdullah PCM_UNLOCK(d); 32290da2b28SAriff Abdullah return (EINVAL); 32390da2b28SAriff Abdullah break; 32490da2b28SAriff Abdullah } 32590da2b28SAriff Abdullah 32690da2b28SAriff Abdullah if (cnt < 1) { 32790da2b28SAriff Abdullah PCM_UNLOCK(d); 32890da2b28SAriff Abdullah return (ENODEV); 32990da2b28SAriff Abdullah } 33090da2b28SAriff Abdullah 33190da2b28SAriff Abdullah PCM_ACQUIRE(d); 33290da2b28SAriff Abdullah PCM_UNLOCK(d); 33390da2b28SAriff Abdullah 33490da2b28SAriff Abdullah cnt = vchancount; 33590da2b28SAriff Abdullah err = sysctl_handle_int(oidp, &cnt, 0, req); 33690da2b28SAriff Abdullah 33790da2b28SAriff Abdullah if (err == 0 && req->newptr != NULL && vchancount != cnt) { 33890da2b28SAriff Abdullah if (cnt < 0) 33990da2b28SAriff Abdullah cnt = 0; 34090da2b28SAriff Abdullah if (cnt > SND_MAXVCHANS) 34190da2b28SAriff Abdullah cnt = SND_MAXVCHANS; 34290da2b28SAriff Abdullah err = pcm_setvchans(d, direction, cnt, -1); 34390da2b28SAriff Abdullah } 34490da2b28SAriff Abdullah 34590da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 34690da2b28SAriff Abdullah 34790da2b28SAriff Abdullah return err; 34890da2b28SAriff Abdullah } 34990da2b28SAriff Abdullah 35090da2b28SAriff Abdullah static int 35190da2b28SAriff Abdullah sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS) 35290da2b28SAriff Abdullah { 35390da2b28SAriff Abdullah struct snddev_info *d; 35490da2b28SAriff Abdullah struct pcm_channel *c; 35590da2b28SAriff Abdullah uint32_t dflags; 35690da2b28SAriff Abdullah int direction, ret; 35790da2b28SAriff Abdullah char dtype[16]; 35890da2b28SAriff Abdullah 35990da2b28SAriff Abdullah d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 36090da2b28SAriff Abdullah if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 36190da2b28SAriff Abdullah return (EINVAL); 36290da2b28SAriff Abdullah 36390da2b28SAriff Abdullah PCM_LOCK(d); 36490da2b28SAriff Abdullah PCM_WAIT(d); 36590da2b28SAriff Abdullah 36690da2b28SAriff Abdullah switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 36790da2b28SAriff Abdullah case VCHAN_PLAY: 36890da2b28SAriff Abdullah direction = PCMDIR_PLAY; 36990da2b28SAriff Abdullah break; 37090da2b28SAriff Abdullah case VCHAN_REC: 37190da2b28SAriff Abdullah direction = PCMDIR_REC; 37290da2b28SAriff Abdullah break; 37390da2b28SAriff Abdullah default: 37490da2b28SAriff Abdullah PCM_UNLOCK(d); 37590da2b28SAriff Abdullah return (EINVAL); 37690da2b28SAriff Abdullah break; 37790da2b28SAriff Abdullah } 37890da2b28SAriff Abdullah 37990da2b28SAriff Abdullah PCM_ACQUIRE(d); 38090da2b28SAriff Abdullah PCM_UNLOCK(d); 38190da2b28SAriff Abdullah 38290da2b28SAriff Abdullah if (direction == PCMDIR_PLAY) 38390da2b28SAriff Abdullah pcm_getparentchannel(d, &c, NULL); 38490da2b28SAriff Abdullah else 38590da2b28SAriff Abdullah pcm_getparentchannel(d, NULL, &c); 38690da2b28SAriff Abdullah 38790da2b28SAriff Abdullah if (c == NULL) { 38890da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 38990da2b28SAriff Abdullah return (EINVAL); 39090da2b28SAriff Abdullah } 39190da2b28SAriff Abdullah 39290da2b28SAriff Abdullah KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 39390da2b28SAriff Abdullah __func__, direction, c->direction)); 39490da2b28SAriff Abdullah 39590da2b28SAriff Abdullah CHN_LOCK(c); 39690da2b28SAriff Abdullah if (c->flags & CHN_F_VCHAN_PASSTHROUGH) 39790da2b28SAriff Abdullah strlcpy(dtype, "passthrough", sizeof(dtype)); 39890da2b28SAriff Abdullah else if (c->flags & CHN_F_VCHAN_ADAPTIVE) 39990da2b28SAriff Abdullah strlcpy(dtype, "adaptive", sizeof(dtype)); 40090da2b28SAriff Abdullah else 40190da2b28SAriff Abdullah strlcpy(dtype, "fixed", sizeof(dtype)); 40290da2b28SAriff Abdullah CHN_UNLOCK(c); 40390da2b28SAriff Abdullah 40490da2b28SAriff Abdullah ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req); 40590da2b28SAriff Abdullah if (ret == 0 && req->newptr != NULL) { 40690da2b28SAriff Abdullah if (strcasecmp(dtype, "passthrough") == 0 || 40790da2b28SAriff Abdullah strcmp(dtype, "1") == 0) 40890da2b28SAriff Abdullah dflags = CHN_F_VCHAN_PASSTHROUGH; 40990da2b28SAriff Abdullah else if (strcasecmp(dtype, "adaptive") == 0 || 41090da2b28SAriff Abdullah strcmp(dtype, "2") == 0) 41190da2b28SAriff Abdullah dflags = CHN_F_VCHAN_ADAPTIVE; 41290da2b28SAriff Abdullah else if (strcasecmp(dtype, "fixed") == 0 || 41390da2b28SAriff Abdullah strcmp(dtype, "0") == 0) 41490da2b28SAriff Abdullah dflags = 0; 41590da2b28SAriff Abdullah else { 41690da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 41790da2b28SAriff Abdullah return (EINVAL); 41890da2b28SAriff Abdullah } 41990da2b28SAriff Abdullah CHN_LOCK(c); 42090da2b28SAriff Abdullah if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) || 42190da2b28SAriff Abdullah (c->flags & CHN_F_PASSTHROUGH)) { 42290da2b28SAriff Abdullah CHN_UNLOCK(c); 42390da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 42490da2b28SAriff Abdullah return (0); 42590da2b28SAriff Abdullah } 42690da2b28SAriff Abdullah c->flags &= ~CHN_F_VCHAN_DYNAMIC; 42790da2b28SAriff Abdullah c->flags |= dflags; 42890da2b28SAriff Abdullah CHN_UNLOCK(c); 42990da2b28SAriff Abdullah } 43090da2b28SAriff Abdullah 43190da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 43290da2b28SAriff Abdullah 43390da2b28SAriff Abdullah return (ret); 43490da2b28SAriff Abdullah } 43590da2b28SAriff Abdullah 43690da2b28SAriff Abdullah /* 43790da2b28SAriff Abdullah * On the fly vchan rate/format settings 43890da2b28SAriff Abdullah */ 43990da2b28SAriff Abdullah 44090da2b28SAriff Abdullah #define VCHAN_ACCESSIBLE(c) (!((c)->flags & (CHN_F_PASSTHROUGH | \ 44190da2b28SAriff Abdullah CHN_F_EXCLUSIVE)) && \ 44290da2b28SAriff Abdullah (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \ 44390da2b28SAriff Abdullah CHN_STOPPED(c))) 44490da2b28SAriff Abdullah static int 44590da2b28SAriff Abdullah sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS) 44690da2b28SAriff Abdullah { 44790da2b28SAriff Abdullah struct snddev_info *d; 44890da2b28SAriff Abdullah struct pcm_channel *c, *ch; 44990da2b28SAriff Abdullah struct pcmchan_caps *caps; 45090da2b28SAriff Abdullah int *vchanrate, vchancount, direction, ret, newspd, restart; 45190da2b28SAriff Abdullah 45290da2b28SAriff Abdullah d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 45390da2b28SAriff Abdullah if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 45490da2b28SAriff Abdullah return (EINVAL); 45590da2b28SAriff Abdullah 45690da2b28SAriff Abdullah PCM_LOCK(d); 457e4e61333SAriff Abdullah PCM_WAIT(d); 458e4e61333SAriff Abdullah 459bba4862cSAriff Abdullah switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 460bba4862cSAriff Abdullah case VCHAN_PLAY: 461bba4862cSAriff Abdullah direction = PCMDIR_PLAY; 462bba4862cSAriff Abdullah vchancount = d->pvchancount; 463bba4862cSAriff Abdullah vchanrate = &d->pvchanrate; 464bba4862cSAriff Abdullah break; 465bba4862cSAriff Abdullah case VCHAN_REC: 466bba4862cSAriff Abdullah direction = PCMDIR_REC; 467bba4862cSAriff Abdullah vchancount = d->rvchancount; 468bba4862cSAriff Abdullah vchanrate = &d->rvchanrate; 469bba4862cSAriff Abdullah break; 470bba4862cSAriff Abdullah default: 47190da2b28SAriff Abdullah PCM_UNLOCK(d); 472bba4862cSAriff Abdullah return (EINVAL); 473bba4862cSAriff Abdullah break; 474bba4862cSAriff Abdullah } 475bba4862cSAriff Abdullah 476e4e61333SAriff Abdullah if (vchancount < 1) { 47790da2b28SAriff Abdullah PCM_UNLOCK(d); 478bba4862cSAriff Abdullah return (EINVAL); 4799c271ebaSAriff Abdullah } 480e4e61333SAriff Abdullah 481e4e61333SAriff Abdullah PCM_ACQUIRE(d); 48290da2b28SAriff Abdullah PCM_UNLOCK(d); 483e4e61333SAriff Abdullah 48490da2b28SAriff Abdullah if (direction == PCMDIR_PLAY) 48590da2b28SAriff Abdullah pcm_getparentchannel(d, &c, NULL); 48690da2b28SAriff Abdullah else 48790da2b28SAriff Abdullah pcm_getparentchannel(d, NULL, &c); 488e4e61333SAriff Abdullah 48990da2b28SAriff Abdullah if (c == NULL) { 49090da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 49190da2b28SAriff Abdullah return (EINVAL); 49290da2b28SAriff Abdullah } 49390da2b28SAriff Abdullah 49490da2b28SAriff Abdullah KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 49590da2b28SAriff Abdullah __func__, direction, c->direction)); 49690da2b28SAriff Abdullah 49787506547SAlexander Leidinger CHN_LOCK(c); 49890da2b28SAriff Abdullah newspd = c->speed; 4999c271ebaSAriff Abdullah CHN_UNLOCK(c); 50090da2b28SAriff Abdullah 50190da2b28SAriff Abdullah ret = sysctl_handle_int(oidp, &newspd, 0, req); 50290da2b28SAriff Abdullah if (ret != 0 || req->newptr == NULL) { 503e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 50490da2b28SAriff Abdullah return (ret); 50587506547SAlexander Leidinger } 506e4e61333SAriff Abdullah 507a580b31aSAriff Abdullah if (newspd < 1 || newspd < feeder_rate_min || 508a580b31aSAriff Abdullah newspd > feeder_rate_max) { 509e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 510bba4862cSAriff Abdullah return (EINVAL); 51197d69a96SAlexander Leidinger } 51290da2b28SAriff Abdullah 51390da2b28SAriff Abdullah CHN_LOCK(c); 51490da2b28SAriff Abdullah 51590da2b28SAriff Abdullah if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) { 51690da2b28SAriff Abdullah if (CHN_STARTED(c)) { 51790da2b28SAriff Abdullah chn_abort(c); 51890da2b28SAriff Abdullah restart = 1; 51990da2b28SAriff Abdullah } else 52090da2b28SAriff Abdullah restart = 0; 52190da2b28SAriff Abdullah 522a580b31aSAriff Abdullah if (feeder_rate_round) { 52390da2b28SAriff Abdullah caps = chn_getcaps(c); 52490da2b28SAriff Abdullah RANGE(newspd, caps->minspeed, caps->maxspeed); 52590da2b28SAriff Abdullah newspd = CHANNEL_SETSPEED(c->methods, 52690da2b28SAriff Abdullah c->devinfo, newspd); 52787506547SAlexander Leidinger } 52890da2b28SAriff Abdullah 52990da2b28SAriff Abdullah ret = chn_reset(c, c->format, newspd); 53090da2b28SAriff Abdullah if (ret == 0) { 53190da2b28SAriff Abdullah *vchanrate = c->speed; 53290da2b28SAriff Abdullah if (restart != 0) { 53390da2b28SAriff Abdullah CHN_FOREACH(ch, c, children.busy) { 53490da2b28SAriff Abdullah CHN_LOCK(ch); 53590da2b28SAriff Abdullah if (VCHAN_SYNC_REQUIRED(ch)) 53690da2b28SAriff Abdullah vchan_sync(ch); 53797d69a96SAlexander Leidinger CHN_UNLOCK(ch); 53897d69a96SAlexander Leidinger } 53990da2b28SAriff Abdullah c->flags |= CHN_F_DIRTY; 54090da2b28SAriff Abdullah ret = chn_start(c, 1); 54190da2b28SAriff Abdullah } 54290da2b28SAriff Abdullah } 54390da2b28SAriff Abdullah } 54490da2b28SAriff Abdullah 54590da2b28SAriff Abdullah CHN_UNLOCK(c); 546e4e61333SAriff Abdullah 547e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 548e4e61333SAriff Abdullah 54990da2b28SAriff Abdullah return (ret); 55087506547SAlexander Leidinger } 551a580b31aSAriff Abdullah 552a580b31aSAriff Abdullah static int 55390da2b28SAriff Abdullah sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) 554a580b31aSAriff Abdullah { 555a580b31aSAriff Abdullah struct snddev_info *d; 55690da2b28SAriff Abdullah struct pcm_channel *c, *ch; 55790da2b28SAriff Abdullah uint32_t newfmt; 55890da2b28SAriff Abdullah int *vchanformat, vchancount, direction, ret, restart; 55990da2b28SAriff Abdullah char fmtstr[AFMTSTR_LEN]; 560a580b31aSAriff Abdullah 561bba4862cSAriff Abdullah d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 562e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 563bba4862cSAriff Abdullah return (EINVAL); 564bba4862cSAriff Abdullah 56590da2b28SAriff Abdullah PCM_LOCK(d); 566e4e61333SAriff Abdullah PCM_WAIT(d); 567e4e61333SAriff Abdullah 568bba4862cSAriff Abdullah switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 569bba4862cSAriff Abdullah case VCHAN_PLAY: 570bba4862cSAriff Abdullah direction = PCMDIR_PLAY; 571bba4862cSAriff Abdullah vchancount = d->pvchancount; 572bba4862cSAriff Abdullah vchanformat = &d->pvchanformat; 573bba4862cSAriff Abdullah break; 574bba4862cSAriff Abdullah case VCHAN_REC: 575bba4862cSAriff Abdullah direction = PCMDIR_REC; 576bba4862cSAriff Abdullah vchancount = d->rvchancount; 577bba4862cSAriff Abdullah vchanformat = &d->rvchanformat; 578bba4862cSAriff Abdullah break; 579bba4862cSAriff Abdullah default: 58090da2b28SAriff Abdullah PCM_UNLOCK(d); 581bba4862cSAriff Abdullah return (EINVAL); 582bba4862cSAriff Abdullah break; 583bba4862cSAriff Abdullah } 584bba4862cSAriff Abdullah 585e4e61333SAriff Abdullah if (vchancount < 1) { 58690da2b28SAriff Abdullah PCM_UNLOCK(d); 587bba4862cSAriff Abdullah return (EINVAL); 588a580b31aSAriff Abdullah } 589e4e61333SAriff Abdullah 590e4e61333SAriff Abdullah PCM_ACQUIRE(d); 59190da2b28SAriff Abdullah PCM_UNLOCK(d); 592e4e61333SAriff Abdullah 59390da2b28SAriff Abdullah if (direction == PCMDIR_PLAY) 59490da2b28SAriff Abdullah pcm_getparentchannel(d, &c, NULL); 59590da2b28SAriff Abdullah else 59690da2b28SAriff Abdullah pcm_getparentchannel(d, NULL, &c); 59790da2b28SAriff Abdullah 59890da2b28SAriff Abdullah if (c == NULL) { 59990da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 60090da2b28SAriff Abdullah return (EINVAL); 60190da2b28SAriff Abdullah } 60290da2b28SAriff Abdullah 60390da2b28SAriff Abdullah KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 60490da2b28SAriff Abdullah __func__, direction, c->direction)); 60590da2b28SAriff Abdullah 606a580b31aSAriff Abdullah CHN_LOCK(c); 60790da2b28SAriff Abdullah 60890da2b28SAriff Abdullah bzero(fmtstr, sizeof(fmtstr)); 60990da2b28SAriff Abdullah 61090da2b28SAriff Abdullah if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format) 61190da2b28SAriff Abdullah strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr)); 61290da2b28SAriff Abdullah 613a580b31aSAriff Abdullah CHN_UNLOCK(c); 61490da2b28SAriff Abdullah 61590da2b28SAriff Abdullah ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 61690da2b28SAriff Abdullah if (ret != 0 || req->newptr == NULL) { 617e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 61890da2b28SAriff Abdullah return (ret); 619a580b31aSAriff Abdullah } 62090da2b28SAriff Abdullah 62190da2b28SAriff Abdullah newfmt = snd_str2afmt(fmtstr); 62290da2b28SAriff Abdullah if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) { 623e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 624bba4862cSAriff Abdullah return (EINVAL); 625a580b31aSAriff Abdullah } 626e4e61333SAriff Abdullah 62790da2b28SAriff Abdullah CHN_LOCK(c); 62890da2b28SAriff Abdullah 62990da2b28SAriff Abdullah if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) { 63090da2b28SAriff Abdullah if (CHN_STARTED(c)) { 63190da2b28SAriff Abdullah chn_abort(c); 63290da2b28SAriff Abdullah restart = 1; 63390da2b28SAriff Abdullah } else 63490da2b28SAriff Abdullah restart = 0; 63590da2b28SAriff Abdullah 63690da2b28SAriff Abdullah ret = chn_reset(c, newfmt, c->speed); 63790da2b28SAriff Abdullah if (ret == 0) { 63890da2b28SAriff Abdullah *vchanformat = c->format; 63990da2b28SAriff Abdullah if (restart != 0) { 64090da2b28SAriff Abdullah CHN_FOREACH(ch, c, children.busy) { 641a580b31aSAriff Abdullah CHN_LOCK(ch); 64290da2b28SAriff Abdullah if (VCHAN_SYNC_REQUIRED(ch)) 64390da2b28SAriff Abdullah vchan_sync(ch); 644a580b31aSAriff Abdullah CHN_UNLOCK(ch); 645a580b31aSAriff Abdullah } 64690da2b28SAriff Abdullah c->flags |= CHN_F_DIRTY; 64790da2b28SAriff Abdullah ret = chn_start(c, 1); 64890da2b28SAriff Abdullah } 64990da2b28SAriff Abdullah } 65090da2b28SAriff Abdullah } 65190da2b28SAriff Abdullah 65290da2b28SAriff Abdullah CHN_UNLOCK(c); 653e4e61333SAriff Abdullah 654e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 655e4e61333SAriff Abdullah 65690da2b28SAriff Abdullah return (ret); 657a580b31aSAriff Abdullah } 65887506547SAlexander Leidinger 659285648f9SCameron Grant /* virtual channel interface */ 660285648f9SCameron Grant 661bba4862cSAriff Abdullah #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 662bba4862cSAriff Abdullah "play.vchanformat" : "rec.vchanformat" 663bba4862cSAriff Abdullah #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 664bba4862cSAriff Abdullah "play.vchanrate" : "rec.vchanrate" 665bba4862cSAriff Abdullah 666285648f9SCameron Grant int 667bba4862cSAriff Abdullah vchan_create(struct pcm_channel *parent, int num) 668285648f9SCameron Grant { 66990da2b28SAriff Abdullah struct snddev_info *d; 67090da2b28SAriff Abdullah struct pcm_channel *ch; 67197d69a96SAlexander Leidinger struct pcmchan_caps *parent_caps; 67290da2b28SAriff Abdullah uint32_t vchanfmt, vchanspd; 67390da2b28SAriff Abdullah int ret, direction, r, save; 67490da2b28SAriff Abdullah 67590da2b28SAriff Abdullah d = parent->parentsnddev; 67697d69a96SAlexander Leidinger 677e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 67890da2b28SAriff Abdullah CHN_LOCKASSERT(parent); 679e4e61333SAriff Abdullah 68097d69a96SAlexander Leidinger if (!(parent->flags & CHN_F_BUSY)) 681bba4862cSAriff Abdullah return (EBUSY); 68297d69a96SAlexander Leidinger 68390da2b28SAriff Abdullah if (!(parent->direction == PCMDIR_PLAY || 68490da2b28SAriff Abdullah parent->direction == PCMDIR_REC)) 68590da2b28SAriff Abdullah return (EINVAL); 68690da2b28SAriff Abdullah 68790da2b28SAriff Abdullah d = parent->parentsnddev; 68890da2b28SAriff Abdullah 68990da2b28SAriff Abdullah CHN_UNLOCK(parent); 69090da2b28SAriff Abdullah PCM_LOCK(d); 69190da2b28SAriff Abdullah 692bba4862cSAriff Abdullah if (parent->direction == PCMDIR_PLAY) { 693bba4862cSAriff Abdullah direction = PCMDIR_PLAY_VIRTUAL; 694bba4862cSAriff Abdullah vchanfmt = d->pvchanformat; 69590da2b28SAriff Abdullah vchanspd = d->pvchanrate; 69690da2b28SAriff Abdullah } else { 697bba4862cSAriff Abdullah direction = PCMDIR_REC_VIRTUAL; 698bba4862cSAriff Abdullah vchanfmt = d->rvchanformat; 69990da2b28SAriff Abdullah vchanspd = d->rvchanrate; 70090da2b28SAriff Abdullah } 70112e524a2SDon Lewis 702285648f9SCameron Grant /* create a new playback channel */ 703bba4862cSAriff Abdullah ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent); 704bba4862cSAriff Abdullah if (ch == NULL) { 70590da2b28SAriff Abdullah PCM_UNLOCK(d); 70612e524a2SDon Lewis CHN_LOCK(parent); 707bba4862cSAriff Abdullah return (ENODEV); 708285648f9SCameron Grant } 709285648f9SCameron Grant 710285648f9SCameron Grant /* add us to our grandparent's channel list */ 71190da2b28SAriff Abdullah ret = pcm_chn_add(d, ch); 71290da2b28SAriff Abdullah PCM_UNLOCK(d); 71390da2b28SAriff Abdullah if (ret != 0) { 714bba4862cSAriff Abdullah pcm_chn_destroy(ch); 71597d69a96SAlexander Leidinger CHN_LOCK(parent); 71690da2b28SAriff Abdullah return (ret); 717285648f9SCameron Grant } 718285648f9SCameron Grant 71912e524a2SDon Lewis CHN_LOCK(parent); 72090da2b28SAriff Abdullah /* 72190da2b28SAriff Abdullah * Add us to our parent channel's children in reverse order 72290da2b28SAriff Abdullah * so future destruction will pick the last (biggest number) 72390da2b28SAriff Abdullah * channel. 72490da2b28SAriff Abdullah */ 72590da2b28SAriff Abdullah CHN_INSERT_SORT_DESCEND(parent, ch, children); 72690da2b28SAriff Abdullah 72790da2b28SAriff Abdullah if (parent->flags & CHN_F_HAS_VCHAN) 72890da2b28SAriff Abdullah return (0); 72990da2b28SAriff Abdullah 73097d69a96SAlexander Leidinger parent->flags |= CHN_F_HAS_VCHAN; 73187506547SAlexander Leidinger 73297d69a96SAlexander Leidinger parent_caps = chn_getcaps(parent); 73397d69a96SAlexander Leidinger if (parent_caps == NULL) 73490da2b28SAriff Abdullah ret = EINVAL; 73597d69a96SAlexander Leidinger 73690da2b28SAriff Abdullah save = 0; 73790da2b28SAriff Abdullah 73890da2b28SAriff Abdullah if (ret == 0 && vchanfmt == 0) { 739a580b31aSAriff Abdullah const char *vfmt; 740a580b31aSAriff Abdullah 741a580b31aSAriff Abdullah CHN_UNLOCK(parent); 74290da2b28SAriff Abdullah r = resource_string_value(device_get_name(parent->dev), 74390da2b28SAriff Abdullah device_get_unit(parent->dev), VCHAN_FMT_HINT(direction), 744bba4862cSAriff Abdullah &vfmt); 745a580b31aSAriff Abdullah CHN_LOCK(parent); 746a580b31aSAriff Abdullah if (r != 0) 747a580b31aSAriff Abdullah vfmt = NULL; 748a580b31aSAriff Abdullah if (vfmt != NULL) { 74990da2b28SAriff Abdullah vchanfmt = snd_str2afmt(vfmt); 75090da2b28SAriff Abdullah if (vchanfmt != 0 && !(vchanfmt & AFMT_VCHAN)) 75190da2b28SAriff Abdullah vchanfmt = 0; 752a580b31aSAriff Abdullah } 753a580b31aSAriff Abdullah if (vchanfmt == 0) 75490da2b28SAriff Abdullah vchanfmt = VCHAN_DEFAULT_FORMAT; 75590da2b28SAriff Abdullah save = 1; 756a580b31aSAriff Abdullah } 757a580b31aSAriff Abdullah 75890da2b28SAriff Abdullah if (ret == 0 && vchanspd == 0) { 75987506547SAlexander Leidinger /* 76087506547SAlexander Leidinger * This is very sad. Few soundcards advertised as being 76187506547SAlexander Leidinger * able to do (insanely) higher/lower speed, but in 76287506547SAlexander Leidinger * reality, they simply can't. At least, we give user chance 76397d69a96SAlexander Leidinger * to set sane value via kernel hints or sysctl. 76487506547SAlexander Leidinger */ 76597d69a96SAlexander Leidinger CHN_UNLOCK(parent); 76690da2b28SAriff Abdullah r = resource_int_value(device_get_name(parent->dev), 76790da2b28SAriff Abdullah device_get_unit(parent->dev), VCHAN_SPD_HINT(direction), 76890da2b28SAriff Abdullah &vchanspd); 76997d69a96SAlexander Leidinger CHN_LOCK(parent); 77097efeca3SAriff Abdullah if (r != 0) { 77197efeca3SAriff Abdullah /* 772bba4862cSAriff Abdullah * No saved value, no hint, NOTHING. 773a580b31aSAriff Abdullah * 77497efeca3SAriff Abdullah * Workaround for sb16 running 7759ca45dd7SAriff Abdullah * poorly at 45k / 49k. 77697efeca3SAriff Abdullah */ 7779ca45dd7SAriff Abdullah switch (parent_caps->maxspeed) { 7789ca45dd7SAriff Abdullah case 45000: 7799ca45dd7SAriff Abdullah case 49000: 78090da2b28SAriff Abdullah vchanspd = 44100; 7819ca45dd7SAriff Abdullah break; 7829ca45dd7SAriff Abdullah default: 78390da2b28SAriff Abdullah vchanspd = VCHAN_DEFAULT_RATE; 78490da2b28SAriff Abdullah if (vchanspd > parent_caps->maxspeed) 78590da2b28SAriff Abdullah vchanspd = parent_caps->maxspeed; 7869ca45dd7SAriff Abdullah break; 7879ca45dd7SAriff Abdullah } 78890da2b28SAriff Abdullah if (vchanspd < parent_caps->minspeed) 78990da2b28SAriff Abdullah vchanspd = parent_caps->minspeed; 79097efeca3SAriff Abdullah } 79190da2b28SAriff Abdullah save = 1; 79287506547SAlexander Leidinger } 79387506547SAlexander Leidinger 79490da2b28SAriff Abdullah if (ret == 0) { 79590da2b28SAriff Abdullah /* 79690da2b28SAriff Abdullah * Limit the speed between feeder_rate_min <-> feeder_rate_max. 79790da2b28SAriff Abdullah */ 79890da2b28SAriff Abdullah if (vchanspd < feeder_rate_min) 79990da2b28SAriff Abdullah vchanspd = feeder_rate_min; 80090da2b28SAriff Abdullah if (vchanspd > feeder_rate_max) 80190da2b28SAriff Abdullah vchanspd = feeder_rate_max; 80290da2b28SAriff Abdullah 803a580b31aSAriff Abdullah if (feeder_rate_round) { 80490da2b28SAriff Abdullah RANGE(vchanspd, parent_caps->minspeed, 80590da2b28SAriff Abdullah parent_caps->maxspeed); 80690da2b28SAriff Abdullah vchanspd = CHANNEL_SETSPEED(parent->methods, 80790da2b28SAriff Abdullah parent->devinfo, vchanspd); 808a580b31aSAriff Abdullah } 80987506547SAlexander Leidinger 81090da2b28SAriff Abdullah ret = chn_reset(parent, vchanfmt, vchanspd); 811d45d1f20SAriff Abdullah } 81297d69a96SAlexander Leidinger 81390da2b28SAriff Abdullah if (ret == 0 && save) { 81487506547SAlexander Leidinger /* 815bba4862cSAriff Abdullah * Save new value. 81687506547SAlexander Leidinger */ 817bba4862cSAriff Abdullah if (direction == PCMDIR_PLAY_VIRTUAL) { 81890da2b28SAriff Abdullah d->pvchanformat = parent->format; 81990da2b28SAriff Abdullah d->pvchanrate = parent->speed; 820bba4862cSAriff Abdullah } else { 82190da2b28SAriff Abdullah d->rvchanformat = parent->format; 82290da2b28SAriff Abdullah d->rvchanrate = parent->speed; 82387506547SAlexander Leidinger } 824285648f9SCameron Grant } 825285648f9SCameron Grant 82690da2b28SAriff Abdullah /* 82790da2b28SAriff Abdullah * If the parent channel supports digital format, 82890da2b28SAriff Abdullah * enable passthrough mode. 82990da2b28SAriff Abdullah */ 83090da2b28SAriff Abdullah if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) { 83190da2b28SAriff Abdullah parent->flags &= ~CHN_F_VCHAN_DYNAMIC; 83290da2b28SAriff Abdullah parent->flags |= CHN_F_VCHAN_PASSTHROUGH; 83390da2b28SAriff Abdullah } 83490da2b28SAriff Abdullah 83590da2b28SAriff Abdullah if (ret != 0) { 836bba4862cSAriff Abdullah CHN_REMOVE(parent, ch, children); 83797d69a96SAlexander Leidinger parent->flags &= ~CHN_F_HAS_VCHAN; 83897d69a96SAlexander Leidinger CHN_UNLOCK(parent); 83990da2b28SAriff Abdullah PCM_LOCK(d); 840bba4862cSAriff Abdullah if (pcm_chn_remove(d, ch) == 0) { 84190da2b28SAriff Abdullah PCM_UNLOCK(d); 842bba4862cSAriff Abdullah pcm_chn_destroy(ch); 843bba4862cSAriff Abdullah } else 84490da2b28SAriff Abdullah PCM_UNLOCK(d); 84597d69a96SAlexander Leidinger CHN_LOCK(parent); 84697d69a96SAlexander Leidinger } 84797d69a96SAlexander Leidinger 84890da2b28SAriff Abdullah return (ret); 84997d69a96SAlexander Leidinger } 850285648f9SCameron Grant 851285648f9SCameron Grant int 852285648f9SCameron Grant vchan_destroy(struct pcm_channel *c) 853285648f9SCameron Grant { 85490da2b28SAriff Abdullah struct pcm_channel *parent; 85590da2b28SAriff Abdullah struct snddev_info *d; 85690da2b28SAriff Abdullah int ret; 85790da2b28SAriff Abdullah 85890da2b28SAriff Abdullah KASSERT(c != NULL && c->parentchannel != NULL && 85990da2b28SAriff Abdullah c->parentsnddev != NULL, ("%s(): invalid channel=%p", 86090da2b28SAriff Abdullah __func__, c)); 86190da2b28SAriff Abdullah 86290da2b28SAriff Abdullah CHN_LOCKASSERT(c); 86390da2b28SAriff Abdullah 86490da2b28SAriff Abdullah d = c->parentsnddev; 86590da2b28SAriff Abdullah parent = c->parentchannel; 866285648f9SCameron Grant 867e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 86890da2b28SAriff Abdullah CHN_LOCKASSERT(parent); 869e4e61333SAriff Abdullah 87090da2b28SAriff Abdullah CHN_UNLOCK(c); 87190da2b28SAriff Abdullah 87290da2b28SAriff Abdullah if (!(parent->flags & CHN_F_BUSY)) 873bba4862cSAriff Abdullah return (EBUSY); 87490da2b28SAriff Abdullah 87590da2b28SAriff Abdullah if (CHN_EMPTY(parent, children)) 876bba4862cSAriff Abdullah return (EINVAL); 87749c5e6e2SCameron Grant 878285648f9SCameron Grant /* remove us from our parent's children list */ 879bba4862cSAriff Abdullah CHN_REMOVE(parent, c, children); 880285648f9SCameron Grant 881bba4862cSAriff Abdullah if (CHN_EMPTY(parent, children)) { 8829c271ebaSAriff Abdullah parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 88390da2b28SAriff Abdullah chn_reset(parent, parent->format, parent->speed); 88497d69a96SAlexander Leidinger } 885f637a36cSCameron Grant 88649c5e6e2SCameron Grant CHN_UNLOCK(parent); 887bba4862cSAriff Abdullah 888bba4862cSAriff Abdullah /* remove us from our grandparent's channel list */ 88990da2b28SAriff Abdullah PCM_LOCK(d); 89090da2b28SAriff Abdullah ret = pcm_chn_remove(d, c); 89190da2b28SAriff Abdullah PCM_UNLOCK(d); 892bba4862cSAriff Abdullah 893285648f9SCameron Grant /* destroy ourselves */ 89490da2b28SAriff Abdullah if (ret == 0) 89590da2b28SAriff Abdullah ret = pcm_chn_destroy(c); 896285648f9SCameron Grant 89790da2b28SAriff Abdullah CHN_LOCK(parent); 89890da2b28SAriff Abdullah 89990da2b28SAriff Abdullah return (ret); 900285648f9SCameron Grant } 901285648f9SCameron Grant 902285648f9SCameron Grant int 90390da2b28SAriff Abdullah #ifdef SND_DEBUG 90490da2b28SAriff Abdullah vchan_passthrough(struct pcm_channel *c, const char *caller) 90590da2b28SAriff Abdullah #else 90690da2b28SAriff Abdullah vchan_sync(struct pcm_channel *c) 90790da2b28SAriff Abdullah #endif 90890da2b28SAriff Abdullah { 90990da2b28SAriff Abdullah int ret; 91090da2b28SAriff Abdullah 91190da2b28SAriff Abdullah KASSERT(c != NULL && c->parentchannel != NULL && 91290da2b28SAriff Abdullah (c->flags & CHN_F_VIRTUAL), 91390da2b28SAriff Abdullah ("%s(): invalid passthrough", __func__)); 91490da2b28SAriff Abdullah CHN_LOCKASSERT(c); 91590da2b28SAriff Abdullah CHN_LOCKASSERT(c->parentchannel); 91690da2b28SAriff Abdullah 91790da2b28SAriff Abdullah sndbuf_setspd(c->bufhard, c->parentchannel->speed); 91890da2b28SAriff Abdullah c->flags |= CHN_F_PASSTHROUGH; 91990da2b28SAriff Abdullah ret = feeder_chain(c); 92090da2b28SAriff Abdullah c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH); 92190da2b28SAriff Abdullah if (ret != 0) 92290da2b28SAriff Abdullah c->flags |= CHN_F_DIRTY; 92390da2b28SAriff Abdullah 92490da2b28SAriff Abdullah #ifdef SND_DEBUG 92590da2b28SAriff Abdullah if (snd_passthrough_verbose != 0) { 92690da2b28SAriff Abdullah char *devname, buf[CHN_NAMELEN]; 92790da2b28SAriff Abdullah 92890da2b28SAriff Abdullah devname = dsp_unit2name(buf, sizeof(buf), c->unit); 92990da2b28SAriff Abdullah device_printf(c->dev, 93090da2b28SAriff Abdullah "%s(%s/%s) %s() -> re-sync err=%d\n", 93190da2b28SAriff Abdullah __func__, (devname != NULL) ? devname : "dspX", c->comm, 93290da2b28SAriff Abdullah caller, ret); 93390da2b28SAriff Abdullah } 93490da2b28SAriff Abdullah #endif 93590da2b28SAriff Abdullah 93690da2b28SAriff Abdullah return (ret); 93790da2b28SAriff Abdullah } 93890da2b28SAriff Abdullah 93990da2b28SAriff Abdullah void 94067b1dce3SCameron Grant vchan_initsys(device_t dev) 941285648f9SCameron Grant { 94267b1dce3SCameron Grant struct snddev_info *d; 943bba4862cSAriff Abdullah int unit; 94467b1dce3SCameron Grant 945bba4862cSAriff Abdullah unit = device_get_unit(dev); 94667b1dce3SCameron Grant d = device_get_softc(dev); 947bba4862cSAriff Abdullah 948bba4862cSAriff Abdullah /* Play */ 949bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 950bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree), 951*7029da5cSPawel Biernacki OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 952bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 95390da2b28SAriff Abdullah sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 95490da2b28SAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 95590da2b28SAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree), 956*7029da5cSPawel Biernacki OID_AUTO, "vchanmode", 957*7029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 95890da2b28SAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 95990da2b28SAriff Abdullah sysctl_dev_pcm_vchanmode, "A", 96090da2b28SAriff Abdullah "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 961bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 962bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree), 963*7029da5cSPawel Biernacki OID_AUTO, "vchanrate", 964*7029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 965bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 96690da2b28SAriff Abdullah sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 967bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 968bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree), 969*7029da5cSPawel Biernacki OID_AUTO, "vchanformat", 970*7029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 971bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 97290da2b28SAriff Abdullah sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 973bba4862cSAriff Abdullah /* Rec */ 974bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 975bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree), 976*7029da5cSPawel Biernacki OID_AUTO, "vchans", 977*7029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 978bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 97990da2b28SAriff Abdullah sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 98090da2b28SAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 98190da2b28SAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree), 982*7029da5cSPawel Biernacki OID_AUTO, "vchanmode", 983*7029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 98490da2b28SAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 98590da2b28SAriff Abdullah sysctl_dev_pcm_vchanmode, "A", 98690da2b28SAriff Abdullah "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 987bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 988bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree), 989*7029da5cSPawel Biernacki OID_AUTO, "vchanrate", 990*7029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 991bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 99290da2b28SAriff Abdullah sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 993bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 994bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree), 995*7029da5cSPawel Biernacki OID_AUTO, "vchanformat", 996*7029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 997bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 99890da2b28SAriff Abdullah sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 999285648f9SCameron Grant } 1000