1098ca2bdSWarner Losh /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 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. 72b14465fSChristos Margiolis * Copyright (c) 2024 The FreeBSD Foundation 82b14465fSChristos Margiolis * 92b14465fSChristos Margiolis * Portions of this software were developed by Christos Margiolis 102b14465fSChristos Margiolis * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation. 11285648f9SCameron Grant * 12285648f9SCameron Grant * Redistribution and use in source and binary forms, with or without 13285648f9SCameron Grant * modification, are permitted provided that the following conditions 14285648f9SCameron Grant * are met: 15285648f9SCameron Grant * 1. Redistributions of source code must retain the above copyright 16285648f9SCameron Grant * notice, this list of conditions and the following disclaimer. 17285648f9SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 18285648f9SCameron Grant * notice, this list of conditions and the following disclaimer in the 19285648f9SCameron Grant * documentation and/or other materials provided with the distribution. 20285648f9SCameron Grant * 21285648f9SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22285648f9SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23285648f9SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24285648f9SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25285648f9SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26285648f9SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27285648f9SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28285648f9SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29285648f9SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30285648f9SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31285648f9SCameron Grant * SUCH DAMAGE. 32285648f9SCameron Grant */ 33285648f9SCameron Grant 34e9577a5cSJoel Dahl /* Almost entirely rewritten to add multi-format/channels mixing support. */ 35e9577a5cSJoel Dahl 3690da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 3790da2b28SAriff Abdullah #include "opt_snd.h" 3890da2b28SAriff Abdullah #endif 3990da2b28SAriff Abdullah 40285648f9SCameron Grant #include <dev/sound/pcm/sound.h> 41285648f9SCameron Grant #include <dev/sound/pcm/vchan.h> 42285648f9SCameron Grant 4390da2b28SAriff Abdullah /* 4490da2b28SAriff Abdullah * [ac3 , dts , linear , 0, linear, 0] 4590da2b28SAriff Abdullah */ 4690da2b28SAriff Abdullah #define FMTLIST_MAX 6 4790da2b28SAriff Abdullah #define FMTLIST_OFFSET 4 4890da2b28SAriff Abdullah #define DIGFMTS_MAX 2 49a580b31aSAriff Abdullah 5090da2b28SAriff Abdullah #ifdef SND_DEBUG 5190da2b28SAriff Abdullah static int snd_passthrough_verbose = 0; 5232a0e5d5SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, passthrough_verbose, CTLFLAG_RWTUN, 5390da2b28SAriff Abdullah &snd_passthrough_verbose, 0, "passthrough verbosity"); 5487506547SAlexander Leidinger 5590da2b28SAriff Abdullah #endif 5690da2b28SAriff Abdullah 5790da2b28SAriff Abdullah struct vchan_info { 58bba4862cSAriff Abdullah struct pcm_channel *channel; 59285648f9SCameron Grant struct pcmchan_caps caps; 6090da2b28SAriff Abdullah uint32_t fmtlist[FMTLIST_MAX]; 61bba4862cSAriff Abdullah int trigger; 62285648f9SCameron Grant }; 63285648f9SCameron Grant 647ad5f383SChristos Margiolis int snd_maxautovchans = 16; 657ad5f383SChristos Margiolis 66285648f9SCameron Grant static void * 67bba4862cSAriff Abdullah vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 68bba4862cSAriff Abdullah struct pcm_channel *c, int dir) 69285648f9SCameron Grant { 7090da2b28SAriff Abdullah struct vchan_info *info; 7190da2b28SAriff Abdullah struct pcm_channel *p; 7290da2b28SAriff Abdullah uint32_t i, j, *fmtlist; 73285648f9SCameron Grant 74bba4862cSAriff Abdullah KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC, 75bba4862cSAriff Abdullah ("vchan_init: bad direction")); 76bba4862cSAriff Abdullah KASSERT(c != NULL && c->parentchannel != NULL, 77bba4862cSAriff Abdullah ("vchan_init: bad channels")); 78bba4862cSAriff Abdullah 7990da2b28SAriff Abdullah info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO); 8090da2b28SAriff Abdullah info->channel = c; 8190da2b28SAriff Abdullah info->trigger = PCMTRIG_STOP; 8290da2b28SAriff Abdullah p = c->parentchannel; 8390da2b28SAriff Abdullah 8490da2b28SAriff Abdullah CHN_LOCK(p); 8590da2b28SAriff Abdullah 8690da2b28SAriff Abdullah fmtlist = chn_getcaps(p)->fmtlist; 8790da2b28SAriff Abdullah for (i = 0, j = 0; fmtlist[i] != 0 && j < DIGFMTS_MAX; i++) { 8890da2b28SAriff Abdullah if (fmtlist[i] & AFMT_PASSTHROUGH) 8990da2b28SAriff Abdullah info->fmtlist[j++] = fmtlist[i]; 9090da2b28SAriff Abdullah } 9190da2b28SAriff Abdullah if (p->format & AFMT_VCHAN) 9290da2b28SAriff Abdullah info->fmtlist[j] = p->format; 9390da2b28SAriff Abdullah else 9490da2b28SAriff Abdullah info->fmtlist[j] = VCHAN_DEFAULT_FORMAT; 9590da2b28SAriff Abdullah info->caps.fmtlist = info->fmtlist + 9690da2b28SAriff Abdullah ((p->flags & CHN_F_VCHAN_DYNAMIC) ? 0 : FMTLIST_OFFSET); 9790da2b28SAriff Abdullah 9890da2b28SAriff Abdullah CHN_UNLOCK(p); 99285648f9SCameron Grant 100285648f9SCameron Grant c->flags |= CHN_F_VIRTUAL; 101285648f9SCameron Grant 10290da2b28SAriff Abdullah return (info); 103285648f9SCameron Grant } 104285648f9SCameron Grant 105285648f9SCameron Grant static int 106285648f9SCameron Grant vchan_free(kobj_t obj, void *data) 107285648f9SCameron Grant { 10890da2b28SAriff Abdullah 109e444a209SAriff Abdullah free(data, M_DEVBUF); 110bba4862cSAriff Abdullah 111bba4862cSAriff Abdullah return (0); 112285648f9SCameron Grant } 113285648f9SCameron Grant 114285648f9SCameron Grant static int 115a580b31aSAriff Abdullah vchan_setformat(kobj_t obj, void *data, uint32_t format) 116285648f9SCameron Grant { 11790da2b28SAriff Abdullah struct vchan_info *info; 118285648f9SCameron Grant 11990da2b28SAriff Abdullah info = data; 12090da2b28SAriff Abdullah 12190da2b28SAriff Abdullah CHN_LOCKASSERT(info->channel); 12290da2b28SAriff Abdullah 12390da2b28SAriff Abdullah if (!snd_fmtvalid(format, info->caps.fmtlist)) 124bba4862cSAriff Abdullah return (-1); 125bba4862cSAriff Abdullah 126bba4862cSAriff Abdullah return (0); 127285648f9SCameron Grant } 128285648f9SCameron Grant 12990da2b28SAriff Abdullah static uint32_t 130a580b31aSAriff Abdullah vchan_setspeed(kobj_t obj, void *data, uint32_t speed) 131285648f9SCameron Grant { 13290da2b28SAriff Abdullah struct vchan_info *info; 133285648f9SCameron Grant 13490da2b28SAriff Abdullah info = data; 13590da2b28SAriff Abdullah 13690da2b28SAriff Abdullah CHN_LOCKASSERT(info->channel); 13790da2b28SAriff Abdullah 13890da2b28SAriff Abdullah return (info->caps.maxspeed); 139285648f9SCameron Grant } 140285648f9SCameron Grant 141285648f9SCameron Grant static int 142285648f9SCameron Grant vchan_trigger(kobj_t obj, void *data, int go) 143285648f9SCameron Grant { 14490da2b28SAriff Abdullah struct vchan_info *info; 145bba4862cSAriff Abdullah struct pcm_channel *c, *p; 14690da2b28SAriff Abdullah int ret, otrigger; 147285648f9SCameron Grant 14890da2b28SAriff Abdullah info = data; 14990da2b28SAriff Abdullah 15090da2b28SAriff Abdullah if (!PCMTRIG_COMMON(go) || go == info->trigger) 151bba4862cSAriff Abdullah return (0); 152285648f9SCameron Grant 15390da2b28SAriff Abdullah c = info->channel; 154bba4862cSAriff Abdullah p = c->parentchannel; 15590da2b28SAriff Abdullah otrigger = info->trigger; 15690da2b28SAriff Abdullah info->trigger = go; 15790da2b28SAriff Abdullah 15890da2b28SAriff Abdullah CHN_LOCKASSERT(c); 159285648f9SCameron Grant 160bba4862cSAriff Abdullah CHN_UNLOCK(c); 161bba4862cSAriff Abdullah CHN_LOCK(p); 162bba4862cSAriff Abdullah 163bba4862cSAriff Abdullah switch (go) { 164bba4862cSAriff Abdullah case PCMTRIG_START: 16590da2b28SAriff Abdullah if (otrigger != PCMTRIG_START) 166bba4862cSAriff Abdullah CHN_INSERT_HEAD(p, c, children.busy); 167bba4862cSAriff Abdullah break; 168bba4862cSAriff Abdullah case PCMTRIG_STOP: 169bba4862cSAriff Abdullah case PCMTRIG_ABORT: 17090da2b28SAriff Abdullah if (otrigger == PCMTRIG_START) 171bba4862cSAriff Abdullah CHN_REMOVE(p, c, children.busy); 172bba4862cSAriff Abdullah break; 173bba4862cSAriff Abdullah default: 174bba4862cSAriff Abdullah break; 175bba4862cSAriff Abdullah } 176bba4862cSAriff Abdullah 17790da2b28SAriff Abdullah ret = chn_notify(p, CHN_N_TRIGGER); 17890da2b28SAriff Abdullah 17990da2b28SAriff Abdullah CHN_LOCK(c); 18090da2b28SAriff Abdullah 18190da2b28SAriff Abdullah if (ret == 0 && go == PCMTRIG_START && VCHAN_SYNC_REQUIRED(c)) 18290da2b28SAriff Abdullah ret = vchan_sync(c); 18390da2b28SAriff Abdullah 18490da2b28SAriff Abdullah CHN_UNLOCK(c); 185bba4862cSAriff Abdullah CHN_UNLOCK(p); 186bba4862cSAriff Abdullah CHN_LOCK(c); 187bba4862cSAriff Abdullah 18890da2b28SAriff Abdullah return (ret); 189285648f9SCameron Grant } 190285648f9SCameron Grant 191285648f9SCameron Grant static struct pcmchan_caps * 192285648f9SCameron Grant vchan_getcaps(kobj_t obj, void *data) 193285648f9SCameron Grant { 19490da2b28SAriff Abdullah struct vchan_info *info; 19590da2b28SAriff Abdullah struct pcm_channel *c; 19690da2b28SAriff Abdullah uint32_t pformat, pspeed, pflags, i; 197285648f9SCameron Grant 19890da2b28SAriff Abdullah info = data; 19990da2b28SAriff Abdullah c = info->channel; 20090da2b28SAriff Abdullah pformat = c->parentchannel->format; 20190da2b28SAriff Abdullah pspeed = c->parentchannel->speed; 20290da2b28SAriff Abdullah pflags = c->parentchannel->flags; 20390da2b28SAriff Abdullah 20490da2b28SAriff Abdullah CHN_LOCKASSERT(c); 20590da2b28SAriff Abdullah 20690da2b28SAriff Abdullah if (pflags & CHN_F_VCHAN_DYNAMIC) { 20790da2b28SAriff Abdullah info->caps.fmtlist = info->fmtlist; 20890da2b28SAriff Abdullah if (pformat & AFMT_VCHAN) { 20990da2b28SAriff Abdullah for (i = 0; info->caps.fmtlist[i] != 0; i++) { 21090da2b28SAriff Abdullah if (info->caps.fmtlist[i] & AFMT_PASSTHROUGH) 21190da2b28SAriff Abdullah continue; 21290da2b28SAriff Abdullah break; 213a580b31aSAriff Abdullah } 21490da2b28SAriff Abdullah info->caps.fmtlist[i] = pformat; 21590da2b28SAriff Abdullah } 21690da2b28SAriff Abdullah if (c->format & AFMT_PASSTHROUGH) 21790da2b28SAriff Abdullah info->caps.minspeed = c->speed; 21890da2b28SAriff Abdullah else 21990da2b28SAriff Abdullah info->caps.minspeed = pspeed; 22090da2b28SAriff Abdullah info->caps.maxspeed = info->caps.minspeed; 22190da2b28SAriff Abdullah } else { 22290da2b28SAriff Abdullah info->caps.fmtlist = info->fmtlist + FMTLIST_OFFSET; 22390da2b28SAriff Abdullah if (pformat & AFMT_VCHAN) 22490da2b28SAriff Abdullah info->caps.fmtlist[0] = pformat; 22590da2b28SAriff Abdullah else { 22690da2b28SAriff Abdullah device_printf(c->dev, 22790da2b28SAriff Abdullah "%s(): invalid vchan format 0x%08x", 22890da2b28SAriff Abdullah __func__, pformat); 22990da2b28SAriff Abdullah info->caps.fmtlist[0] = VCHAN_DEFAULT_FORMAT; 23090da2b28SAriff Abdullah } 23190da2b28SAriff Abdullah info->caps.minspeed = pspeed; 23290da2b28SAriff Abdullah info->caps.maxspeed = info->caps.minspeed; 23390da2b28SAriff Abdullah } 234285648f9SCameron Grant 23590da2b28SAriff Abdullah return (&info->caps); 23690da2b28SAriff Abdullah } 23790da2b28SAriff Abdullah 23890da2b28SAriff Abdullah static struct pcmchan_matrix * 23990da2b28SAriff Abdullah vchan_getmatrix(kobj_t obj, void *data, uint32_t format) 24090da2b28SAriff Abdullah { 24190da2b28SAriff Abdullah 24290da2b28SAriff Abdullah return (feeder_matrix_format_map(format)); 243285648f9SCameron Grant } 244285648f9SCameron Grant 245285648f9SCameron Grant static kobj_method_t vchan_methods[] = { 246285648f9SCameron Grant KOBJMETHOD(channel_init, vchan_init), 247285648f9SCameron Grant KOBJMETHOD(channel_free, vchan_free), 248285648f9SCameron Grant KOBJMETHOD(channel_setformat, vchan_setformat), 249285648f9SCameron Grant KOBJMETHOD(channel_setspeed, vchan_setspeed), 250285648f9SCameron Grant KOBJMETHOD(channel_trigger, vchan_trigger), 251285648f9SCameron Grant KOBJMETHOD(channel_getcaps, vchan_getcaps), 25290da2b28SAriff Abdullah KOBJMETHOD(channel_getmatrix, vchan_getmatrix), 25390da2b28SAriff Abdullah KOBJMETHOD_END 254285648f9SCameron Grant }; 255285648f9SCameron Grant CHANNEL_DECLARE(vchan); 256285648f9SCameron Grant 25790da2b28SAriff Abdullah static void 25877ab4263SChristos Margiolis vchan_getparentchannel(struct snddev_info *d, 25990da2b28SAriff Abdullah struct pcm_channel **wrch, struct pcm_channel **rdch) 26090da2b28SAriff Abdullah { 26190da2b28SAriff Abdullah struct pcm_channel **ch, *wch, *rch, *c; 26290da2b28SAriff Abdullah 26390da2b28SAriff Abdullah KASSERT(d != NULL, ("%s(): NULL snddev_info", __func__)); 26490da2b28SAriff Abdullah 26590da2b28SAriff Abdullah PCM_BUSYASSERT(d); 26690da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 26790da2b28SAriff Abdullah 26890da2b28SAriff Abdullah wch = NULL; 26990da2b28SAriff Abdullah rch = NULL; 27090da2b28SAriff Abdullah 27190da2b28SAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 27290da2b28SAriff Abdullah CHN_LOCK(c); 27390da2b28SAriff Abdullah ch = (c->direction == PCMDIR_PLAY) ? &wch : &rch; 27490da2b28SAriff Abdullah if (c->flags & CHN_F_VIRTUAL) { 27590da2b28SAriff Abdullah /* Sanity check */ 27690da2b28SAriff Abdullah if (*ch != NULL && *ch != c->parentchannel) { 27790da2b28SAriff Abdullah CHN_UNLOCK(c); 27890da2b28SAriff Abdullah *ch = NULL; 27990da2b28SAriff Abdullah break; 28090da2b28SAriff Abdullah } 28190da2b28SAriff Abdullah } else if (c->flags & CHN_F_HAS_VCHAN) { 28290da2b28SAriff Abdullah /* No way!! */ 28390da2b28SAriff Abdullah if (*ch != NULL) { 28490da2b28SAriff Abdullah CHN_UNLOCK(c); 28590da2b28SAriff Abdullah *ch = NULL; 28690da2b28SAriff Abdullah break; 28790da2b28SAriff Abdullah } 28890da2b28SAriff Abdullah *ch = c; 28990da2b28SAriff Abdullah } 29090da2b28SAriff Abdullah CHN_UNLOCK(c); 29190da2b28SAriff Abdullah } 29290da2b28SAriff Abdullah 29390da2b28SAriff Abdullah if (wrch != NULL) 29490da2b28SAriff Abdullah *wrch = wch; 29590da2b28SAriff Abdullah if (rdch != NULL) 29690da2b28SAriff Abdullah *rdch = rch; 29790da2b28SAriff Abdullah } 29890da2b28SAriff Abdullah 29987506547SAlexander Leidinger static int 30090da2b28SAriff Abdullah sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS) 30187506547SAlexander Leidinger { 30287506547SAlexander Leidinger struct snddev_info *d; 30390da2b28SAriff Abdullah int direction, vchancount; 30490da2b28SAriff Abdullah int err, cnt; 30587506547SAlexander Leidinger 306bba4862cSAriff Abdullah d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 307e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 308bba4862cSAriff Abdullah return (EINVAL); 309bba4862cSAriff Abdullah 31090da2b28SAriff Abdullah PCM_LOCK(d); 31190da2b28SAriff Abdullah PCM_WAIT(d); 31290da2b28SAriff Abdullah 31390da2b28SAriff Abdullah switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 31490da2b28SAriff Abdullah case VCHAN_PLAY: 31590da2b28SAriff Abdullah direction = PCMDIR_PLAY; 31690da2b28SAriff Abdullah vchancount = d->pvchancount; 31790da2b28SAriff Abdullah cnt = d->playcount; 31890da2b28SAriff Abdullah break; 31990da2b28SAriff Abdullah case VCHAN_REC: 32090da2b28SAriff Abdullah direction = PCMDIR_REC; 32190da2b28SAriff Abdullah vchancount = d->rvchancount; 32290da2b28SAriff Abdullah cnt = d->reccount; 32390da2b28SAriff Abdullah break; 32490da2b28SAriff Abdullah default: 32590da2b28SAriff Abdullah PCM_UNLOCK(d); 32690da2b28SAriff Abdullah return (EINVAL); 32790da2b28SAriff Abdullah break; 32890da2b28SAriff Abdullah } 32990da2b28SAriff Abdullah 33090da2b28SAriff Abdullah if (cnt < 1) { 33190da2b28SAriff Abdullah PCM_UNLOCK(d); 33290da2b28SAriff Abdullah return (ENODEV); 33390da2b28SAriff Abdullah } 33490da2b28SAriff Abdullah 33590da2b28SAriff Abdullah PCM_ACQUIRE(d); 33690da2b28SAriff Abdullah PCM_UNLOCK(d); 33790da2b28SAriff Abdullah 33890da2b28SAriff Abdullah cnt = vchancount; 33990da2b28SAriff Abdullah err = sysctl_handle_int(oidp, &cnt, 0, req); 34090da2b28SAriff Abdullah 34190da2b28SAriff Abdullah if (err == 0 && req->newptr != NULL && vchancount != cnt) { 34290da2b28SAriff Abdullah if (cnt < 0) 34390da2b28SAriff Abdullah cnt = 0; 34490da2b28SAriff Abdullah if (cnt > SND_MAXVCHANS) 34590da2b28SAriff Abdullah cnt = SND_MAXVCHANS; 3463af2beb8SChristos Margiolis err = vchan_setnew(d, direction, cnt); 34790da2b28SAriff Abdullah } 34890da2b28SAriff Abdullah 34990da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 35090da2b28SAriff Abdullah 35190da2b28SAriff Abdullah return err; 35290da2b28SAriff Abdullah } 35390da2b28SAriff Abdullah 35490da2b28SAriff Abdullah static int 35590da2b28SAriff Abdullah sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS) 35690da2b28SAriff Abdullah { 35790da2b28SAriff Abdullah struct snddev_info *d; 35890da2b28SAriff Abdullah struct pcm_channel *c; 35990da2b28SAriff Abdullah uint32_t dflags; 36090da2b28SAriff Abdullah int direction, ret; 36190da2b28SAriff Abdullah char dtype[16]; 36290da2b28SAriff Abdullah 36390da2b28SAriff Abdullah d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 36490da2b28SAriff Abdullah if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 36590da2b28SAriff Abdullah return (EINVAL); 36690da2b28SAriff Abdullah 36790da2b28SAriff Abdullah PCM_LOCK(d); 36890da2b28SAriff Abdullah PCM_WAIT(d); 36990da2b28SAriff Abdullah 37090da2b28SAriff Abdullah switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 37190da2b28SAriff Abdullah case VCHAN_PLAY: 37290da2b28SAriff Abdullah direction = PCMDIR_PLAY; 37390da2b28SAriff Abdullah break; 37490da2b28SAriff Abdullah case VCHAN_REC: 37590da2b28SAriff Abdullah direction = PCMDIR_REC; 37690da2b28SAriff Abdullah break; 37790da2b28SAriff Abdullah default: 37890da2b28SAriff Abdullah PCM_UNLOCK(d); 37990da2b28SAriff Abdullah return (EINVAL); 38090da2b28SAriff Abdullah break; 38190da2b28SAriff Abdullah } 38290da2b28SAriff Abdullah 38390da2b28SAriff Abdullah PCM_ACQUIRE(d); 38490da2b28SAriff Abdullah PCM_UNLOCK(d); 38590da2b28SAriff Abdullah 38690da2b28SAriff Abdullah if (direction == PCMDIR_PLAY) 38777ab4263SChristos Margiolis vchan_getparentchannel(d, &c, NULL); 38890da2b28SAriff Abdullah else 38977ab4263SChristos Margiolis vchan_getparentchannel(d, NULL, &c); 39090da2b28SAriff Abdullah 39190da2b28SAriff Abdullah if (c == NULL) { 39290da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 39390da2b28SAriff Abdullah return (EINVAL); 39490da2b28SAriff Abdullah } 39590da2b28SAriff Abdullah 39690da2b28SAriff Abdullah KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 39790da2b28SAriff Abdullah __func__, direction, c->direction)); 39890da2b28SAriff Abdullah 39990da2b28SAriff Abdullah CHN_LOCK(c); 40090da2b28SAriff Abdullah if (c->flags & CHN_F_VCHAN_PASSTHROUGH) 40190da2b28SAriff Abdullah strlcpy(dtype, "passthrough", sizeof(dtype)); 40290da2b28SAriff Abdullah else if (c->flags & CHN_F_VCHAN_ADAPTIVE) 40390da2b28SAriff Abdullah strlcpy(dtype, "adaptive", sizeof(dtype)); 40490da2b28SAriff Abdullah else 40590da2b28SAriff Abdullah strlcpy(dtype, "fixed", sizeof(dtype)); 40690da2b28SAriff Abdullah CHN_UNLOCK(c); 40790da2b28SAriff Abdullah 40890da2b28SAriff Abdullah ret = sysctl_handle_string(oidp, dtype, sizeof(dtype), req); 40990da2b28SAriff Abdullah if (ret == 0 && req->newptr != NULL) { 41090da2b28SAriff Abdullah if (strcasecmp(dtype, "passthrough") == 0 || 41190da2b28SAriff Abdullah strcmp(dtype, "1") == 0) 41290da2b28SAriff Abdullah dflags = CHN_F_VCHAN_PASSTHROUGH; 41390da2b28SAriff Abdullah else if (strcasecmp(dtype, "adaptive") == 0 || 41490da2b28SAriff Abdullah strcmp(dtype, "2") == 0) 41590da2b28SAriff Abdullah dflags = CHN_F_VCHAN_ADAPTIVE; 41690da2b28SAriff Abdullah else if (strcasecmp(dtype, "fixed") == 0 || 41790da2b28SAriff Abdullah strcmp(dtype, "0") == 0) 41890da2b28SAriff Abdullah dflags = 0; 41990da2b28SAriff Abdullah else { 42090da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 42190da2b28SAriff Abdullah return (EINVAL); 42290da2b28SAriff Abdullah } 42390da2b28SAriff Abdullah CHN_LOCK(c); 42490da2b28SAriff Abdullah if (dflags == (c->flags & CHN_F_VCHAN_DYNAMIC) || 42590da2b28SAriff Abdullah (c->flags & CHN_F_PASSTHROUGH)) { 42690da2b28SAriff Abdullah CHN_UNLOCK(c); 42790da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 42890da2b28SAriff Abdullah return (0); 42990da2b28SAriff Abdullah } 43090da2b28SAriff Abdullah c->flags &= ~CHN_F_VCHAN_DYNAMIC; 43190da2b28SAriff Abdullah c->flags |= dflags; 43290da2b28SAriff Abdullah CHN_UNLOCK(c); 43390da2b28SAriff Abdullah } 43490da2b28SAriff Abdullah 43590da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 43690da2b28SAriff Abdullah 43790da2b28SAriff Abdullah return (ret); 43890da2b28SAriff Abdullah } 43990da2b28SAriff Abdullah 44090da2b28SAriff Abdullah /* 44190da2b28SAriff Abdullah * On the fly vchan rate/format settings 44290da2b28SAriff Abdullah */ 44390da2b28SAriff Abdullah 44490da2b28SAriff Abdullah #define VCHAN_ACCESSIBLE(c) (!((c)->flags & (CHN_F_PASSTHROUGH | \ 44590da2b28SAriff Abdullah CHN_F_EXCLUSIVE)) && \ 44690da2b28SAriff Abdullah (((c)->flags & CHN_F_VCHAN_DYNAMIC) || \ 44790da2b28SAriff Abdullah CHN_STOPPED(c))) 44890da2b28SAriff Abdullah static int 44990da2b28SAriff Abdullah sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS) 45090da2b28SAriff Abdullah { 45190da2b28SAriff Abdullah struct snddev_info *d; 45290da2b28SAriff Abdullah struct pcm_channel *c, *ch; 45390da2b28SAriff Abdullah struct pcmchan_caps *caps; 45490da2b28SAriff Abdullah int *vchanrate, vchancount, direction, ret, newspd, restart; 45590da2b28SAriff Abdullah 45690da2b28SAriff Abdullah d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 45790da2b28SAriff Abdullah if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 45890da2b28SAriff Abdullah return (EINVAL); 45990da2b28SAriff Abdullah 46090da2b28SAriff Abdullah PCM_LOCK(d); 461e4e61333SAriff Abdullah PCM_WAIT(d); 462e4e61333SAriff Abdullah 463bba4862cSAriff Abdullah switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 464bba4862cSAriff Abdullah case VCHAN_PLAY: 465bba4862cSAriff Abdullah direction = PCMDIR_PLAY; 466bba4862cSAriff Abdullah vchancount = d->pvchancount; 467bba4862cSAriff Abdullah vchanrate = &d->pvchanrate; 468bba4862cSAriff Abdullah break; 469bba4862cSAriff Abdullah case VCHAN_REC: 470bba4862cSAriff Abdullah direction = PCMDIR_REC; 471bba4862cSAriff Abdullah vchancount = d->rvchancount; 472bba4862cSAriff Abdullah vchanrate = &d->rvchanrate; 473bba4862cSAriff Abdullah break; 474bba4862cSAriff Abdullah default: 47590da2b28SAriff Abdullah PCM_UNLOCK(d); 476bba4862cSAriff Abdullah return (EINVAL); 477bba4862cSAriff Abdullah break; 478bba4862cSAriff Abdullah } 479bba4862cSAriff Abdullah 480e4e61333SAriff Abdullah if (vchancount < 1) { 48190da2b28SAriff Abdullah PCM_UNLOCK(d); 482bba4862cSAriff Abdullah return (EINVAL); 4839c271ebaSAriff Abdullah } 484e4e61333SAriff Abdullah 485e4e61333SAriff Abdullah PCM_ACQUIRE(d); 48690da2b28SAriff Abdullah PCM_UNLOCK(d); 487e4e61333SAriff Abdullah 48890da2b28SAriff Abdullah if (direction == PCMDIR_PLAY) 48977ab4263SChristos Margiolis vchan_getparentchannel(d, &c, NULL); 49090da2b28SAriff Abdullah else 49177ab4263SChristos Margiolis vchan_getparentchannel(d, NULL, &c); 492e4e61333SAriff Abdullah 49390da2b28SAriff Abdullah if (c == NULL) { 49490da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 49590da2b28SAriff Abdullah return (EINVAL); 49690da2b28SAriff Abdullah } 49790da2b28SAriff Abdullah 49890da2b28SAriff Abdullah KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 49990da2b28SAriff Abdullah __func__, direction, c->direction)); 50090da2b28SAriff Abdullah 50187506547SAlexander Leidinger CHN_LOCK(c); 50290da2b28SAriff Abdullah newspd = c->speed; 5039c271ebaSAriff Abdullah CHN_UNLOCK(c); 50490da2b28SAriff Abdullah 50590da2b28SAriff Abdullah ret = sysctl_handle_int(oidp, &newspd, 0, req); 50690da2b28SAriff Abdullah if (ret != 0 || req->newptr == NULL) { 507e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 50890da2b28SAriff Abdullah return (ret); 50987506547SAlexander Leidinger } 510e4e61333SAriff Abdullah 511f3092614SChristos Margiolis if (newspd < feeder_rate_min || newspd > feeder_rate_max) { 512e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 513bba4862cSAriff Abdullah return (EINVAL); 51497d69a96SAlexander Leidinger } 51590da2b28SAriff Abdullah 51690da2b28SAriff Abdullah CHN_LOCK(c); 51790da2b28SAriff Abdullah 51890da2b28SAriff Abdullah if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) { 51990da2b28SAriff Abdullah if (CHN_STARTED(c)) { 52090da2b28SAriff Abdullah chn_abort(c); 52190da2b28SAriff Abdullah restart = 1; 52290da2b28SAriff Abdullah } else 52390da2b28SAriff Abdullah restart = 0; 52490da2b28SAriff Abdullah 525a580b31aSAriff Abdullah if (feeder_rate_round) { 52690da2b28SAriff Abdullah caps = chn_getcaps(c); 52790da2b28SAriff Abdullah RANGE(newspd, caps->minspeed, caps->maxspeed); 52890da2b28SAriff Abdullah newspd = CHANNEL_SETSPEED(c->methods, 52990da2b28SAriff Abdullah c->devinfo, newspd); 53087506547SAlexander Leidinger } 53190da2b28SAriff Abdullah 53290da2b28SAriff Abdullah ret = chn_reset(c, c->format, newspd); 53390da2b28SAriff Abdullah if (ret == 0) { 53490da2b28SAriff Abdullah *vchanrate = c->speed; 53590da2b28SAriff Abdullah if (restart != 0) { 53690da2b28SAriff Abdullah CHN_FOREACH(ch, c, children.busy) { 53790da2b28SAriff Abdullah CHN_LOCK(ch); 53890da2b28SAriff Abdullah if (VCHAN_SYNC_REQUIRED(ch)) 53990da2b28SAriff Abdullah vchan_sync(ch); 54097d69a96SAlexander Leidinger CHN_UNLOCK(ch); 54197d69a96SAlexander Leidinger } 54290da2b28SAriff Abdullah c->flags |= CHN_F_DIRTY; 54390da2b28SAriff Abdullah ret = chn_start(c, 1); 54490da2b28SAriff Abdullah } 54590da2b28SAriff Abdullah } 54690da2b28SAriff Abdullah } 54790da2b28SAriff Abdullah 54890da2b28SAriff Abdullah CHN_UNLOCK(c); 549e4e61333SAriff Abdullah 550e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 551e4e61333SAriff Abdullah 55290da2b28SAriff Abdullah return (ret); 55387506547SAlexander Leidinger } 554a580b31aSAriff Abdullah 555a580b31aSAriff Abdullah static int 55690da2b28SAriff Abdullah sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) 557a580b31aSAriff Abdullah { 558a580b31aSAriff Abdullah struct snddev_info *d; 55990da2b28SAriff Abdullah struct pcm_channel *c, *ch; 56090da2b28SAriff Abdullah uint32_t newfmt; 56190da2b28SAriff Abdullah int *vchanformat, vchancount, direction, ret, restart; 56290da2b28SAriff Abdullah char fmtstr[AFMTSTR_LEN]; 563a580b31aSAriff Abdullah 564bba4862cSAriff Abdullah d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 565e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 566bba4862cSAriff Abdullah return (EINVAL); 567bba4862cSAriff Abdullah 56890da2b28SAriff Abdullah PCM_LOCK(d); 569e4e61333SAriff Abdullah PCM_WAIT(d); 570e4e61333SAriff Abdullah 571bba4862cSAriff Abdullah switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 572bba4862cSAriff Abdullah case VCHAN_PLAY: 573bba4862cSAriff Abdullah direction = PCMDIR_PLAY; 574bba4862cSAriff Abdullah vchancount = d->pvchancount; 575bba4862cSAriff Abdullah vchanformat = &d->pvchanformat; 576bba4862cSAriff Abdullah break; 577bba4862cSAriff Abdullah case VCHAN_REC: 578bba4862cSAriff Abdullah direction = PCMDIR_REC; 579bba4862cSAriff Abdullah vchancount = d->rvchancount; 580bba4862cSAriff Abdullah vchanformat = &d->rvchanformat; 581bba4862cSAriff Abdullah break; 582bba4862cSAriff Abdullah default: 58390da2b28SAriff Abdullah PCM_UNLOCK(d); 584bba4862cSAriff Abdullah return (EINVAL); 585bba4862cSAriff Abdullah break; 586bba4862cSAriff Abdullah } 587bba4862cSAriff Abdullah 588e4e61333SAriff Abdullah if (vchancount < 1) { 58990da2b28SAriff Abdullah PCM_UNLOCK(d); 590bba4862cSAriff Abdullah return (EINVAL); 591a580b31aSAriff Abdullah } 592e4e61333SAriff Abdullah 593e4e61333SAriff Abdullah PCM_ACQUIRE(d); 59490da2b28SAriff Abdullah PCM_UNLOCK(d); 595e4e61333SAriff Abdullah 59690da2b28SAriff Abdullah if (direction == PCMDIR_PLAY) 59777ab4263SChristos Margiolis vchan_getparentchannel(d, &c, NULL); 59890da2b28SAriff Abdullah else 59977ab4263SChristos Margiolis vchan_getparentchannel(d, NULL, &c); 60090da2b28SAriff Abdullah 60190da2b28SAriff Abdullah if (c == NULL) { 60290da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 60390da2b28SAriff Abdullah return (EINVAL); 60490da2b28SAriff Abdullah } 60590da2b28SAriff Abdullah 60690da2b28SAriff Abdullah KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 60790da2b28SAriff Abdullah __func__, direction, c->direction)); 60890da2b28SAriff Abdullah 609a580b31aSAriff Abdullah CHN_LOCK(c); 61090da2b28SAriff Abdullah 61190da2b28SAriff Abdullah bzero(fmtstr, sizeof(fmtstr)); 61290da2b28SAriff Abdullah 61390da2b28SAriff Abdullah if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format) 61490da2b28SAriff Abdullah strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr)); 61590da2b28SAriff Abdullah 616a580b31aSAriff Abdullah CHN_UNLOCK(c); 61790da2b28SAriff Abdullah 61890da2b28SAriff Abdullah ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 61990da2b28SAriff Abdullah if (ret != 0 || req->newptr == NULL) { 620e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 62190da2b28SAriff Abdullah return (ret); 622a580b31aSAriff Abdullah } 62390da2b28SAriff Abdullah 62490da2b28SAriff Abdullah newfmt = snd_str2afmt(fmtstr); 62590da2b28SAriff Abdullah if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) { 626e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 627bba4862cSAriff Abdullah return (EINVAL); 628a580b31aSAriff Abdullah } 629e4e61333SAriff Abdullah 63090da2b28SAriff Abdullah CHN_LOCK(c); 63190da2b28SAriff Abdullah 63290da2b28SAriff Abdullah if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) { 63390da2b28SAriff Abdullah if (CHN_STARTED(c)) { 63490da2b28SAriff Abdullah chn_abort(c); 63590da2b28SAriff Abdullah restart = 1; 63690da2b28SAriff Abdullah } else 63790da2b28SAriff Abdullah restart = 0; 63890da2b28SAriff Abdullah 63990da2b28SAriff Abdullah ret = chn_reset(c, newfmt, c->speed); 64090da2b28SAriff Abdullah if (ret == 0) { 64190da2b28SAriff Abdullah *vchanformat = c->format; 64290da2b28SAriff Abdullah if (restart != 0) { 64390da2b28SAriff Abdullah CHN_FOREACH(ch, c, children.busy) { 644a580b31aSAriff Abdullah CHN_LOCK(ch); 64590da2b28SAriff Abdullah if (VCHAN_SYNC_REQUIRED(ch)) 64690da2b28SAriff Abdullah vchan_sync(ch); 647a580b31aSAriff Abdullah CHN_UNLOCK(ch); 648a580b31aSAriff Abdullah } 64990da2b28SAriff Abdullah c->flags |= CHN_F_DIRTY; 65090da2b28SAriff Abdullah ret = chn_start(c, 1); 65190da2b28SAriff Abdullah } 65290da2b28SAriff Abdullah } 65390da2b28SAriff Abdullah } 65490da2b28SAriff Abdullah 65590da2b28SAriff Abdullah CHN_UNLOCK(c); 656e4e61333SAriff Abdullah 657e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 658e4e61333SAriff Abdullah 65990da2b28SAriff Abdullah return (ret); 660a580b31aSAriff Abdullah } 66187506547SAlexander Leidinger 662285648f9SCameron Grant /* virtual channel interface */ 663285648f9SCameron Grant 664bba4862cSAriff Abdullah #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 665bba4862cSAriff Abdullah "play.vchanformat" : "rec.vchanformat" 666bba4862cSAriff Abdullah #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 667bba4862cSAriff Abdullah "play.vchanrate" : "rec.vchanrate" 668bba4862cSAriff Abdullah 669285648f9SCameron Grant int 6703af2beb8SChristos Margiolis vchan_create(struct pcm_channel *parent) 671285648f9SCameron Grant { 67290da2b28SAriff Abdullah struct snddev_info *d; 67390da2b28SAriff Abdullah struct pcm_channel *ch; 67497d69a96SAlexander Leidinger struct pcmchan_caps *parent_caps; 67590da2b28SAriff Abdullah uint32_t vchanfmt, vchanspd; 6763cab66d1SChristos Margiolis int ret, direction, r; 6773cab66d1SChristos Margiolis bool save; 67890da2b28SAriff Abdullah 6793cab66d1SChristos Margiolis ret = 0; 6803cab66d1SChristos Margiolis save = false; 68190da2b28SAriff Abdullah d = parent->parentsnddev; 68297d69a96SAlexander Leidinger 683e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 68490da2b28SAriff Abdullah CHN_LOCKASSERT(parent); 685e4e61333SAriff Abdullah 68697d69a96SAlexander Leidinger if (!(parent->flags & CHN_F_BUSY)) 687bba4862cSAriff Abdullah return (EBUSY); 68897d69a96SAlexander Leidinger 68990da2b28SAriff Abdullah if (!(parent->direction == PCMDIR_PLAY || 69090da2b28SAriff Abdullah parent->direction == PCMDIR_REC)) 69190da2b28SAriff Abdullah return (EINVAL); 69290da2b28SAriff Abdullah 69390da2b28SAriff Abdullah CHN_UNLOCK(parent); 69490da2b28SAriff Abdullah PCM_LOCK(d); 69590da2b28SAriff Abdullah 696bba4862cSAriff Abdullah if (parent->direction == PCMDIR_PLAY) { 697bba4862cSAriff Abdullah direction = PCMDIR_PLAY_VIRTUAL; 698bba4862cSAriff Abdullah vchanfmt = d->pvchanformat; 69990da2b28SAriff Abdullah vchanspd = d->pvchanrate; 70090da2b28SAriff Abdullah } else { 701bba4862cSAriff Abdullah direction = PCMDIR_REC_VIRTUAL; 702bba4862cSAriff Abdullah vchanfmt = d->rvchanformat; 70390da2b28SAriff Abdullah vchanspd = d->rvchanrate; 70490da2b28SAriff Abdullah } 70512e524a2SDon Lewis 706285648f9SCameron Grant /* create a new playback channel */ 7073af2beb8SChristos Margiolis ch = chn_init(d, parent, &vchan_class, direction, parent); 708bba4862cSAriff Abdullah if (ch == NULL) { 70990da2b28SAriff Abdullah PCM_UNLOCK(d); 71012e524a2SDon Lewis CHN_LOCK(parent); 711bba4862cSAriff Abdullah return (ENODEV); 712285648f9SCameron Grant } 71390da2b28SAriff Abdullah PCM_UNLOCK(d); 714285648f9SCameron Grant 71512e524a2SDon Lewis CHN_LOCK(parent); 71690da2b28SAriff Abdullah /* 71790da2b28SAriff Abdullah * Add us to our parent channel's children in reverse order 71890da2b28SAriff Abdullah * so future destruction will pick the last (biggest number) 71990da2b28SAriff Abdullah * channel. 72090da2b28SAriff Abdullah */ 72190da2b28SAriff Abdullah CHN_INSERT_SORT_DESCEND(parent, ch, children); 72290da2b28SAriff Abdullah 72390da2b28SAriff Abdullah if (parent->flags & CHN_F_HAS_VCHAN) 72490da2b28SAriff Abdullah return (0); 72590da2b28SAriff Abdullah 72697d69a96SAlexander Leidinger parent->flags |= CHN_F_HAS_VCHAN; 72787506547SAlexander Leidinger 72897d69a96SAlexander Leidinger parent_caps = chn_getcaps(parent); 7293cab66d1SChristos Margiolis if (parent_caps == NULL) { 73090da2b28SAriff Abdullah ret = EINVAL; 7313cab66d1SChristos Margiolis goto fail; 7323cab66d1SChristos Margiolis } 73397d69a96SAlexander Leidinger 7343cab66d1SChristos Margiolis if (vchanfmt == 0) { 735a580b31aSAriff Abdullah const char *vfmt; 736a580b31aSAriff Abdullah 737a580b31aSAriff Abdullah CHN_UNLOCK(parent); 73890da2b28SAriff Abdullah r = resource_string_value(device_get_name(parent->dev), 73990da2b28SAriff Abdullah device_get_unit(parent->dev), VCHAN_FMT_HINT(direction), 740bba4862cSAriff Abdullah &vfmt); 741a580b31aSAriff Abdullah CHN_LOCK(parent); 742a580b31aSAriff Abdullah if (r != 0) 743a580b31aSAriff Abdullah vfmt = NULL; 744a580b31aSAriff Abdullah if (vfmt != NULL) { 74590da2b28SAriff Abdullah vchanfmt = snd_str2afmt(vfmt); 74690da2b28SAriff Abdullah if (vchanfmt != 0 && !(vchanfmt & AFMT_VCHAN)) 74790da2b28SAriff Abdullah vchanfmt = 0; 748a580b31aSAriff Abdullah } 749a580b31aSAriff Abdullah if (vchanfmt == 0) 75090da2b28SAriff Abdullah vchanfmt = VCHAN_DEFAULT_FORMAT; 7513cab66d1SChristos Margiolis save = true; 752a580b31aSAriff Abdullah } 753a580b31aSAriff Abdullah 7543cab66d1SChristos Margiolis if (vchanspd == 0) { 75587506547SAlexander Leidinger /* 75687506547SAlexander Leidinger * This is very sad. Few soundcards advertised as being 75787506547SAlexander Leidinger * able to do (insanely) higher/lower speed, but in 75887506547SAlexander Leidinger * reality, they simply can't. At least, we give user chance 75997d69a96SAlexander Leidinger * to set sane value via kernel hints or sysctl. 76087506547SAlexander Leidinger */ 76197d69a96SAlexander Leidinger CHN_UNLOCK(parent); 76290da2b28SAriff Abdullah r = resource_int_value(device_get_name(parent->dev), 76390da2b28SAriff Abdullah device_get_unit(parent->dev), VCHAN_SPD_HINT(direction), 76490da2b28SAriff Abdullah &vchanspd); 76597d69a96SAlexander Leidinger CHN_LOCK(parent); 76697efeca3SAriff Abdullah if (r != 0) { 767d6d4586bSChristos Margiolis /* No saved value, no hint, NOTHING. */ 76890da2b28SAriff Abdullah vchanspd = VCHAN_DEFAULT_RATE; 769d6d4586bSChristos Margiolis RANGE(vchanspd, parent_caps->minspeed, 770d6d4586bSChristos Margiolis parent_caps->maxspeed); 77197efeca3SAriff Abdullah } 7723cab66d1SChristos Margiolis save = true; 77387506547SAlexander Leidinger } 77487506547SAlexander Leidinger 77590da2b28SAriff Abdullah /* 77690da2b28SAriff Abdullah * Limit the speed between feeder_rate_min <-> feeder_rate_max. 77790da2b28SAriff Abdullah */ 778d6d4586bSChristos Margiolis RANGE(vchanspd, feeder_rate_min, feeder_rate_max); 77990da2b28SAriff Abdullah 780a580b31aSAriff Abdullah if (feeder_rate_round) { 78190da2b28SAriff Abdullah RANGE(vchanspd, parent_caps->minspeed, 78290da2b28SAriff Abdullah parent_caps->maxspeed); 78390da2b28SAriff Abdullah vchanspd = CHANNEL_SETSPEED(parent->methods, 78490da2b28SAriff Abdullah parent->devinfo, vchanspd); 785a580b31aSAriff Abdullah } 78687506547SAlexander Leidinger 7873cab66d1SChristos Margiolis if ((ret = chn_reset(parent, vchanfmt, vchanspd)) != 0) 7883cab66d1SChristos Margiolis goto fail; 78997d69a96SAlexander Leidinger 7903cab66d1SChristos Margiolis if (save) { 79187506547SAlexander Leidinger /* 792bba4862cSAriff Abdullah * Save new value. 79387506547SAlexander Leidinger */ 794bba4862cSAriff Abdullah if (direction == PCMDIR_PLAY_VIRTUAL) { 79590da2b28SAriff Abdullah d->pvchanformat = parent->format; 79690da2b28SAriff Abdullah d->pvchanrate = parent->speed; 797bba4862cSAriff Abdullah } else { 79890da2b28SAriff Abdullah d->rvchanformat = parent->format; 79990da2b28SAriff Abdullah d->rvchanrate = parent->speed; 80087506547SAlexander Leidinger } 801285648f9SCameron Grant } 802285648f9SCameron Grant 80390da2b28SAriff Abdullah /* 80490da2b28SAriff Abdullah * If the parent channel supports digital format, 80590da2b28SAriff Abdullah * enable passthrough mode. 80690da2b28SAriff Abdullah */ 8073cab66d1SChristos Margiolis if (snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) { 80890da2b28SAriff Abdullah parent->flags &= ~CHN_F_VCHAN_DYNAMIC; 80990da2b28SAriff Abdullah parent->flags |= CHN_F_VCHAN_PASSTHROUGH; 81090da2b28SAriff Abdullah } 81190da2b28SAriff Abdullah 8123cab66d1SChristos Margiolis return (ret); 8133cab66d1SChristos Margiolis 8143cab66d1SChristos Margiolis fail: 815bba4862cSAriff Abdullah CHN_REMOVE(parent, ch, children); 81697d69a96SAlexander Leidinger parent->flags &= ~CHN_F_HAS_VCHAN; 81797d69a96SAlexander Leidinger CHN_UNLOCK(parent); 818b3ea087cSChristos Margiolis chn_kill(ch); 81997d69a96SAlexander Leidinger CHN_LOCK(parent); 82097d69a96SAlexander Leidinger 82190da2b28SAriff Abdullah return (ret); 82297d69a96SAlexander Leidinger } 823285648f9SCameron Grant 824285648f9SCameron Grant int 825285648f9SCameron Grant vchan_destroy(struct pcm_channel *c) 826285648f9SCameron Grant { 82790da2b28SAriff Abdullah struct pcm_channel *parent; 82890da2b28SAriff Abdullah 82990da2b28SAriff Abdullah KASSERT(c != NULL && c->parentchannel != NULL && 83090da2b28SAriff Abdullah c->parentsnddev != NULL, ("%s(): invalid channel=%p", 83190da2b28SAriff Abdullah __func__, c)); 83290da2b28SAriff Abdullah 83390da2b28SAriff Abdullah CHN_LOCKASSERT(c); 83490da2b28SAriff Abdullah 83590da2b28SAriff Abdullah parent = c->parentchannel; 836285648f9SCameron Grant 837*9263f854SChristos Margiolis PCM_BUSYASSERT(c->parentsnddev); 83890da2b28SAriff Abdullah CHN_LOCKASSERT(parent); 839e4e61333SAriff Abdullah 84090da2b28SAriff Abdullah CHN_UNLOCK(c); 84190da2b28SAriff Abdullah 84290da2b28SAriff Abdullah if (!(parent->flags & CHN_F_BUSY)) 843bba4862cSAriff Abdullah return (EBUSY); 84490da2b28SAriff Abdullah 84590da2b28SAriff Abdullah if (CHN_EMPTY(parent, children)) 846bba4862cSAriff Abdullah return (EINVAL); 84749c5e6e2SCameron Grant 848285648f9SCameron Grant /* remove us from our parent's children list */ 849bba4862cSAriff Abdullah CHN_REMOVE(parent, c, children); 850285648f9SCameron Grant 851bba4862cSAriff Abdullah if (CHN_EMPTY(parent, children)) { 8529c271ebaSAriff Abdullah parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 85390da2b28SAriff Abdullah chn_reset(parent, parent->format, parent->speed); 85497d69a96SAlexander Leidinger } 855f637a36cSCameron Grant 85649c5e6e2SCameron Grant CHN_UNLOCK(parent); 857bba4862cSAriff Abdullah 858285648f9SCameron Grant /* destroy ourselves */ 859b3ea087cSChristos Margiolis chn_kill(c); 860285648f9SCameron Grant 86190da2b28SAriff Abdullah CHN_LOCK(parent); 86290da2b28SAriff Abdullah 863*9263f854SChristos Margiolis return (0); 864285648f9SCameron Grant } 865285648f9SCameron Grant 866285648f9SCameron Grant int 86790da2b28SAriff Abdullah #ifdef SND_DEBUG 86890da2b28SAriff Abdullah vchan_passthrough(struct pcm_channel *c, const char *caller) 86990da2b28SAriff Abdullah #else 87090da2b28SAriff Abdullah vchan_sync(struct pcm_channel *c) 87190da2b28SAriff Abdullah #endif 87290da2b28SAriff Abdullah { 87390da2b28SAriff Abdullah int ret; 87490da2b28SAriff Abdullah 87590da2b28SAriff Abdullah KASSERT(c != NULL && c->parentchannel != NULL && 87690da2b28SAriff Abdullah (c->flags & CHN_F_VIRTUAL), 87790da2b28SAriff Abdullah ("%s(): invalid passthrough", __func__)); 87890da2b28SAriff Abdullah CHN_LOCKASSERT(c); 87990da2b28SAriff Abdullah CHN_LOCKASSERT(c->parentchannel); 88090da2b28SAriff Abdullah 88190da2b28SAriff Abdullah sndbuf_setspd(c->bufhard, c->parentchannel->speed); 88290da2b28SAriff Abdullah c->flags |= CHN_F_PASSTHROUGH; 88390da2b28SAriff Abdullah ret = feeder_chain(c); 88490da2b28SAriff Abdullah c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH); 88590da2b28SAriff Abdullah if (ret != 0) 88690da2b28SAriff Abdullah c->flags |= CHN_F_DIRTY; 88790da2b28SAriff Abdullah 88890da2b28SAriff Abdullah #ifdef SND_DEBUG 88990da2b28SAriff Abdullah if (snd_passthrough_verbose != 0) { 89090da2b28SAriff Abdullah char *devname, buf[CHN_NAMELEN]; 89190da2b28SAriff Abdullah 89225723d66SChristos Margiolis devname = dsp_unit2name(buf, sizeof(buf), c); 89390da2b28SAriff Abdullah device_printf(c->dev, 89490da2b28SAriff Abdullah "%s(%s/%s) %s() -> re-sync err=%d\n", 89590da2b28SAriff Abdullah __func__, (devname != NULL) ? devname : "dspX", c->comm, 89690da2b28SAriff Abdullah caller, ret); 89790da2b28SAriff Abdullah } 89890da2b28SAriff Abdullah #endif 89990da2b28SAriff Abdullah 90090da2b28SAriff Abdullah return (ret); 90190da2b28SAriff Abdullah } 90290da2b28SAriff Abdullah 9037ad5f383SChristos Margiolis int 9043af2beb8SChristos Margiolis vchan_setnew(struct snddev_info *d, int direction, int newcnt) 9057ad5f383SChristos Margiolis { 9067ad5f383SChristos Margiolis struct pcm_channel *c, *ch, *nch; 9077ad5f383SChristos Margiolis struct pcmchan_caps *caps; 9087ad5f383SChristos Margiolis int i, err, vcnt; 9097ad5f383SChristos Margiolis 9107ad5f383SChristos Margiolis PCM_BUSYASSERT(d); 9117ad5f383SChristos Margiolis 9127ad5f383SChristos Margiolis if ((direction == PCMDIR_PLAY && d->playcount < 1) || 9137ad5f383SChristos Margiolis (direction == PCMDIR_REC && d->reccount < 1)) 9147ad5f383SChristos Margiolis return (ENODEV); 9157ad5f383SChristos Margiolis 9167ad5f383SChristos Margiolis if (!(d->flags & SD_F_AUTOVCHAN)) 9177ad5f383SChristos Margiolis return (EINVAL); 9187ad5f383SChristos Margiolis 9197ad5f383SChristos Margiolis if (newcnt < 0 || newcnt > SND_MAXVCHANS) 9207ad5f383SChristos Margiolis return (E2BIG); 9217ad5f383SChristos Margiolis 9227ad5f383SChristos Margiolis if (direction == PCMDIR_PLAY) 9237ad5f383SChristos Margiolis vcnt = d->pvchancount; 9247ad5f383SChristos Margiolis else if (direction == PCMDIR_REC) 9257ad5f383SChristos Margiolis vcnt = d->rvchancount; 9267ad5f383SChristos Margiolis else 9277ad5f383SChristos Margiolis return (EINVAL); 9287ad5f383SChristos Margiolis 9297ad5f383SChristos Margiolis if (newcnt > vcnt) { 9307ad5f383SChristos Margiolis /* add new vchans - find a parent channel first */ 9317ad5f383SChristos Margiolis ch = NULL; 9327ad5f383SChristos Margiolis CHN_FOREACH(c, d, channels.pcm) { 9337ad5f383SChristos Margiolis CHN_LOCK(c); 9347ad5f383SChristos Margiolis if (c->direction == direction && 9357ad5f383SChristos Margiolis ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 9367ad5f383SChristos Margiolis c->refcount < 1 && 9377ad5f383SChristos Margiolis !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 9387ad5f383SChristos Margiolis /* 9397ad5f383SChristos Margiolis * Reuse hw channel with vchans already 9407ad5f383SChristos Margiolis * created. 9417ad5f383SChristos Margiolis */ 9427ad5f383SChristos Margiolis if (c->flags & CHN_F_HAS_VCHAN) { 9437ad5f383SChristos Margiolis ch = c; 9447ad5f383SChristos Margiolis break; 9457ad5f383SChristos Margiolis } 9467ad5f383SChristos Margiolis /* 9477ad5f383SChristos Margiolis * No vchans ever created, look for 9487ad5f383SChristos Margiolis * channels with supported formats. 9497ad5f383SChristos Margiolis */ 9507ad5f383SChristos Margiolis caps = chn_getcaps(c); 9517ad5f383SChristos Margiolis if (caps == NULL) { 9527ad5f383SChristos Margiolis CHN_UNLOCK(c); 9537ad5f383SChristos Margiolis continue; 9547ad5f383SChristos Margiolis } 9557ad5f383SChristos Margiolis for (i = 0; caps->fmtlist[i] != 0; i++) { 9567ad5f383SChristos Margiolis if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 9577ad5f383SChristos Margiolis break; 9587ad5f383SChristos Margiolis } 9597ad5f383SChristos Margiolis if (caps->fmtlist[i] != 0) { 9607ad5f383SChristos Margiolis ch = c; 9617ad5f383SChristos Margiolis break; 9627ad5f383SChristos Margiolis } 9637ad5f383SChristos Margiolis } 9647ad5f383SChristos Margiolis CHN_UNLOCK(c); 9657ad5f383SChristos Margiolis } 9667ad5f383SChristos Margiolis if (ch == NULL) 9677ad5f383SChristos Margiolis return (EBUSY); 9687ad5f383SChristos Margiolis ch->flags |= CHN_F_BUSY; 9697ad5f383SChristos Margiolis err = 0; 9707ad5f383SChristos Margiolis while (err == 0 && newcnt > vcnt) { 9713af2beb8SChristos Margiolis err = vchan_create(ch); 9727ad5f383SChristos Margiolis if (err == 0) 9737ad5f383SChristos Margiolis vcnt++; 9747ad5f383SChristos Margiolis else if (err == E2BIG && newcnt > vcnt) 9757ad5f383SChristos Margiolis device_printf(d->dev, 9767ad5f383SChristos Margiolis "%s: err=%d Maximum channel reached.\n", 9777ad5f383SChristos Margiolis __func__, err); 9787ad5f383SChristos Margiolis } 9797ad5f383SChristos Margiolis if (vcnt == 0) 9807ad5f383SChristos Margiolis ch->flags &= ~CHN_F_BUSY; 9817ad5f383SChristos Margiolis CHN_UNLOCK(ch); 9827ad5f383SChristos Margiolis if (err != 0) 9837ad5f383SChristos Margiolis return (err); 9847ad5f383SChristos Margiolis } else if (newcnt < vcnt) { 9857ad5f383SChristos Margiolis CHN_FOREACH(c, d, channels.pcm) { 9867ad5f383SChristos Margiolis CHN_LOCK(c); 9877ad5f383SChristos Margiolis if (c->direction != direction || 9887ad5f383SChristos Margiolis CHN_EMPTY(c, children) || 9897ad5f383SChristos Margiolis !(c->flags & CHN_F_HAS_VCHAN)) { 9907ad5f383SChristos Margiolis CHN_UNLOCK(c); 9917ad5f383SChristos Margiolis continue; 9927ad5f383SChristos Margiolis } 9937ad5f383SChristos Margiolis CHN_FOREACH_SAFE(ch, c, nch, children) { 9947ad5f383SChristos Margiolis CHN_LOCK(ch); 9957ad5f383SChristos Margiolis if (vcnt == 1 && c->refcount > 0) { 9967ad5f383SChristos Margiolis CHN_UNLOCK(ch); 9977ad5f383SChristos Margiolis break; 9987ad5f383SChristos Margiolis } 9997ad5f383SChristos Margiolis if (!(ch->flags & CHN_F_BUSY) && 10007ad5f383SChristos Margiolis ch->refcount < 1) { 10017ad5f383SChristos Margiolis err = vchan_destroy(ch); 10027ad5f383SChristos Margiolis if (err == 0) 10037ad5f383SChristos Margiolis vcnt--; 10047ad5f383SChristos Margiolis } else 10057ad5f383SChristos Margiolis CHN_UNLOCK(ch); 10067ad5f383SChristos Margiolis if (vcnt == newcnt) 10077ad5f383SChristos Margiolis break; 10087ad5f383SChristos Margiolis } 10097ad5f383SChristos Margiolis CHN_UNLOCK(c); 10107ad5f383SChristos Margiolis break; 10117ad5f383SChristos Margiolis } 10127ad5f383SChristos Margiolis } 10137ad5f383SChristos Margiolis 10147ad5f383SChristos Margiolis return (0); 10157ad5f383SChristos Margiolis } 10167ad5f383SChristos Margiolis 10177ad5f383SChristos Margiolis void 10187ad5f383SChristos Margiolis vchan_setmaxauto(struct snddev_info *d, int num) 10197ad5f383SChristos Margiolis { 10207ad5f383SChristos Margiolis PCM_BUSYASSERT(d); 10217ad5f383SChristos Margiolis 10227ad5f383SChristos Margiolis if (num < 0) 10237ad5f383SChristos Margiolis return; 10247ad5f383SChristos Margiolis 10257ad5f383SChristos Margiolis if (num >= 0 && d->pvchancount > num) 10263af2beb8SChristos Margiolis (void)vchan_setnew(d, PCMDIR_PLAY, num); 10277ad5f383SChristos Margiolis else if (num > 0 && d->pvchancount == 0) 10283af2beb8SChristos Margiolis (void)vchan_setnew(d, PCMDIR_PLAY, 1); 10297ad5f383SChristos Margiolis 10307ad5f383SChristos Margiolis if (num >= 0 && d->rvchancount > num) 10313af2beb8SChristos Margiolis (void)vchan_setnew(d, PCMDIR_REC, num); 10327ad5f383SChristos Margiolis else if (num > 0 && d->rvchancount == 0) 10333af2beb8SChristos Margiolis (void)vchan_setnew(d, PCMDIR_REC, 1); 10347ad5f383SChristos Margiolis } 10357ad5f383SChristos Margiolis 10367ad5f383SChristos Margiolis static int 10377ad5f383SChristos Margiolis sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 10387ad5f383SChristos Margiolis { 10397ad5f383SChristos Margiolis struct snddev_info *d; 10407ad5f383SChristos Margiolis int i, v, error; 10417ad5f383SChristos Margiolis 10427ad5f383SChristos Margiolis v = snd_maxautovchans; 10437ad5f383SChristos Margiolis error = sysctl_handle_int(oidp, &v, 0, req); 10447ad5f383SChristos Margiolis if (error == 0 && req->newptr != NULL) { 10457ad5f383SChristos Margiolis if (v < 0) 10467ad5f383SChristos Margiolis v = 0; 10477ad5f383SChristos Margiolis if (v > SND_MAXVCHANS) 10487ad5f383SChristos Margiolis v = SND_MAXVCHANS; 10497ad5f383SChristos Margiolis snd_maxautovchans = v; 10507ad5f383SChristos Margiolis for (i = 0; pcm_devclass != NULL && 10517ad5f383SChristos Margiolis i < devclass_get_maxunit(pcm_devclass); i++) { 10527ad5f383SChristos Margiolis d = devclass_get_softc(pcm_devclass, i); 10537ad5f383SChristos Margiolis if (!PCM_REGISTERED(d)) 10547ad5f383SChristos Margiolis continue; 10557ad5f383SChristos Margiolis PCM_ACQUIRE_QUICK(d); 10567ad5f383SChristos Margiolis vchan_setmaxauto(d, v); 10577ad5f383SChristos Margiolis PCM_RELEASE_QUICK(d); 10587ad5f383SChristos Margiolis } 10597ad5f383SChristos Margiolis } 10607ad5f383SChristos Margiolis return (error); 10617ad5f383SChristos Margiolis } 10627ad5f383SChristos Margiolis SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, 10637ad5f383SChristos Margiolis CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 10647ad5f383SChristos Margiolis sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 10657ad5f383SChristos Margiolis 106690da2b28SAriff Abdullah void 106767b1dce3SCameron Grant vchan_initsys(device_t dev) 1068285648f9SCameron Grant { 106967b1dce3SCameron Grant struct snddev_info *d; 1070bba4862cSAriff Abdullah int unit; 107167b1dce3SCameron Grant 1072bba4862cSAriff Abdullah unit = device_get_unit(dev); 107367b1dce3SCameron Grant d = device_get_softc(dev); 1074bba4862cSAriff Abdullah 1075bba4862cSAriff Abdullah /* Play */ 1076bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1077bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree), 10787029da5cSPawel Biernacki OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1079bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 108090da2b28SAriff Abdullah sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 108190da2b28SAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 108290da2b28SAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree), 10837029da5cSPawel Biernacki OID_AUTO, "vchanmode", 10847029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 108590da2b28SAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 108690da2b28SAriff Abdullah sysctl_dev_pcm_vchanmode, "A", 108790da2b28SAriff Abdullah "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1088bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1089bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree), 10907029da5cSPawel Biernacki OID_AUTO, "vchanrate", 10917029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1092bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 109390da2b28SAriff Abdullah sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1094bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1095bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree), 10967029da5cSPawel Biernacki OID_AUTO, "vchanformat", 10977029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1098bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 109990da2b28SAriff Abdullah sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1100bba4862cSAriff Abdullah /* Rec */ 1101bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1102bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree), 11037029da5cSPawel Biernacki OID_AUTO, "vchans", 11047029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1105bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 110690da2b28SAriff Abdullah sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 110790da2b28SAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 110890da2b28SAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree), 11097029da5cSPawel Biernacki OID_AUTO, "vchanmode", 11107029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 111190da2b28SAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 111290da2b28SAriff Abdullah sysctl_dev_pcm_vchanmode, "A", 111390da2b28SAriff Abdullah "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1114bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1115bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree), 11167029da5cSPawel Biernacki OID_AUTO, "vchanrate", 11177029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1118bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 111990da2b28SAriff Abdullah sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1120bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1121bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree), 11227029da5cSPawel Biernacki OID_AUTO, "vchanformat", 11237029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1124bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 112590da2b28SAriff Abdullah sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1126285648f9SCameron Grant } 1127