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 511a580b31aSAriff Abdullah if (newspd < 1 || newspd < feeder_rate_min || 512a580b31aSAriff Abdullah newspd > feeder_rate_max) { 513e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 514bba4862cSAriff Abdullah return (EINVAL); 51597d69a96SAlexander Leidinger } 51690da2b28SAriff Abdullah 51790da2b28SAriff Abdullah CHN_LOCK(c); 51890da2b28SAriff Abdullah 51990da2b28SAriff Abdullah if (newspd != c->speed && VCHAN_ACCESSIBLE(c)) { 52090da2b28SAriff Abdullah if (CHN_STARTED(c)) { 52190da2b28SAriff Abdullah chn_abort(c); 52290da2b28SAriff Abdullah restart = 1; 52390da2b28SAriff Abdullah } else 52490da2b28SAriff Abdullah restart = 0; 52590da2b28SAriff Abdullah 526a580b31aSAriff Abdullah if (feeder_rate_round) { 52790da2b28SAriff Abdullah caps = chn_getcaps(c); 52890da2b28SAriff Abdullah RANGE(newspd, caps->minspeed, caps->maxspeed); 52990da2b28SAriff Abdullah newspd = CHANNEL_SETSPEED(c->methods, 53090da2b28SAriff Abdullah c->devinfo, newspd); 53187506547SAlexander Leidinger } 53290da2b28SAriff Abdullah 53390da2b28SAriff Abdullah ret = chn_reset(c, c->format, newspd); 53490da2b28SAriff Abdullah if (ret == 0) { 53590da2b28SAriff Abdullah *vchanrate = c->speed; 53690da2b28SAriff Abdullah if (restart != 0) { 53790da2b28SAriff Abdullah CHN_FOREACH(ch, c, children.busy) { 53890da2b28SAriff Abdullah CHN_LOCK(ch); 53990da2b28SAriff Abdullah if (VCHAN_SYNC_REQUIRED(ch)) 54090da2b28SAriff Abdullah vchan_sync(ch); 54197d69a96SAlexander Leidinger CHN_UNLOCK(ch); 54297d69a96SAlexander Leidinger } 54390da2b28SAriff Abdullah c->flags |= CHN_F_DIRTY; 54490da2b28SAriff Abdullah ret = chn_start(c, 1); 54590da2b28SAriff Abdullah } 54690da2b28SAriff Abdullah } 54790da2b28SAriff Abdullah } 54890da2b28SAriff Abdullah 54990da2b28SAriff Abdullah CHN_UNLOCK(c); 550e4e61333SAriff Abdullah 551e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 552e4e61333SAriff Abdullah 55390da2b28SAriff Abdullah return (ret); 55487506547SAlexander Leidinger } 555a580b31aSAriff Abdullah 556a580b31aSAriff Abdullah static int 55790da2b28SAriff Abdullah sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) 558a580b31aSAriff Abdullah { 559a580b31aSAriff Abdullah struct snddev_info *d; 56090da2b28SAriff Abdullah struct pcm_channel *c, *ch; 56190da2b28SAriff Abdullah uint32_t newfmt; 56290da2b28SAriff Abdullah int *vchanformat, vchancount, direction, ret, restart; 56390da2b28SAriff Abdullah char fmtstr[AFMTSTR_LEN]; 564a580b31aSAriff Abdullah 565bba4862cSAriff Abdullah d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 566e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 567bba4862cSAriff Abdullah return (EINVAL); 568bba4862cSAriff Abdullah 56990da2b28SAriff Abdullah PCM_LOCK(d); 570e4e61333SAriff Abdullah PCM_WAIT(d); 571e4e61333SAriff Abdullah 572bba4862cSAriff Abdullah switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 573bba4862cSAriff Abdullah case VCHAN_PLAY: 574bba4862cSAriff Abdullah direction = PCMDIR_PLAY; 575bba4862cSAriff Abdullah vchancount = d->pvchancount; 576bba4862cSAriff Abdullah vchanformat = &d->pvchanformat; 577bba4862cSAriff Abdullah break; 578bba4862cSAriff Abdullah case VCHAN_REC: 579bba4862cSAriff Abdullah direction = PCMDIR_REC; 580bba4862cSAriff Abdullah vchancount = d->rvchancount; 581bba4862cSAriff Abdullah vchanformat = &d->rvchanformat; 582bba4862cSAriff Abdullah break; 583bba4862cSAriff Abdullah default: 58490da2b28SAriff Abdullah PCM_UNLOCK(d); 585bba4862cSAriff Abdullah return (EINVAL); 586bba4862cSAriff Abdullah break; 587bba4862cSAriff Abdullah } 588bba4862cSAriff Abdullah 589e4e61333SAriff Abdullah if (vchancount < 1) { 59090da2b28SAriff Abdullah PCM_UNLOCK(d); 591bba4862cSAriff Abdullah return (EINVAL); 592a580b31aSAriff Abdullah } 593e4e61333SAriff Abdullah 594e4e61333SAriff Abdullah PCM_ACQUIRE(d); 59590da2b28SAriff Abdullah PCM_UNLOCK(d); 596e4e61333SAriff Abdullah 59790da2b28SAriff Abdullah if (direction == PCMDIR_PLAY) 59877ab4263SChristos Margiolis vchan_getparentchannel(d, &c, NULL); 59990da2b28SAriff Abdullah else 60077ab4263SChristos Margiolis vchan_getparentchannel(d, NULL, &c); 60190da2b28SAriff Abdullah 60290da2b28SAriff Abdullah if (c == NULL) { 60390da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 60490da2b28SAriff Abdullah return (EINVAL); 60590da2b28SAriff Abdullah } 60690da2b28SAriff Abdullah 60790da2b28SAriff Abdullah KASSERT(direction == c->direction, ("%s(): invalid direction %d/%d", 60890da2b28SAriff Abdullah __func__, direction, c->direction)); 60990da2b28SAriff Abdullah 610a580b31aSAriff Abdullah CHN_LOCK(c); 61190da2b28SAriff Abdullah 61290da2b28SAriff Abdullah bzero(fmtstr, sizeof(fmtstr)); 61390da2b28SAriff Abdullah 61490da2b28SAriff Abdullah if (snd_afmt2str(c->format, fmtstr, sizeof(fmtstr)) != c->format) 61590da2b28SAriff Abdullah strlcpy(fmtstr, "<ERROR>", sizeof(fmtstr)); 61690da2b28SAriff Abdullah 617a580b31aSAriff Abdullah CHN_UNLOCK(c); 61890da2b28SAriff Abdullah 61990da2b28SAriff Abdullah ret = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); 62090da2b28SAriff Abdullah if (ret != 0 || req->newptr == NULL) { 621e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 62290da2b28SAriff Abdullah return (ret); 623a580b31aSAriff Abdullah } 62490da2b28SAriff Abdullah 62590da2b28SAriff Abdullah newfmt = snd_str2afmt(fmtstr); 62690da2b28SAriff Abdullah if (newfmt == 0 || !(newfmt & AFMT_VCHAN)) { 627e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 628bba4862cSAriff Abdullah return (EINVAL); 629a580b31aSAriff Abdullah } 630e4e61333SAriff Abdullah 63190da2b28SAriff Abdullah CHN_LOCK(c); 63290da2b28SAriff Abdullah 63390da2b28SAriff Abdullah if (newfmt != c->format && VCHAN_ACCESSIBLE(c)) { 63490da2b28SAriff Abdullah if (CHN_STARTED(c)) { 63590da2b28SAriff Abdullah chn_abort(c); 63690da2b28SAriff Abdullah restart = 1; 63790da2b28SAriff Abdullah } else 63890da2b28SAriff Abdullah restart = 0; 63990da2b28SAriff Abdullah 64090da2b28SAriff Abdullah ret = chn_reset(c, newfmt, c->speed); 64190da2b28SAriff Abdullah if (ret == 0) { 64290da2b28SAriff Abdullah *vchanformat = c->format; 64390da2b28SAriff Abdullah if (restart != 0) { 64490da2b28SAriff Abdullah CHN_FOREACH(ch, c, children.busy) { 645a580b31aSAriff Abdullah CHN_LOCK(ch); 64690da2b28SAriff Abdullah if (VCHAN_SYNC_REQUIRED(ch)) 64790da2b28SAriff Abdullah vchan_sync(ch); 648a580b31aSAriff Abdullah CHN_UNLOCK(ch); 649a580b31aSAriff Abdullah } 65090da2b28SAriff Abdullah c->flags |= CHN_F_DIRTY; 65190da2b28SAriff Abdullah ret = chn_start(c, 1); 65290da2b28SAriff Abdullah } 65390da2b28SAriff Abdullah } 65490da2b28SAriff Abdullah } 65590da2b28SAriff Abdullah 65690da2b28SAriff Abdullah CHN_UNLOCK(c); 657e4e61333SAriff Abdullah 658e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 659e4e61333SAriff Abdullah 66090da2b28SAriff Abdullah return (ret); 661a580b31aSAriff Abdullah } 66287506547SAlexander Leidinger 663285648f9SCameron Grant /* virtual channel interface */ 664285648f9SCameron Grant 665bba4862cSAriff Abdullah #define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 666bba4862cSAriff Abdullah "play.vchanformat" : "rec.vchanformat" 667bba4862cSAriff Abdullah #define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ 668bba4862cSAriff Abdullah "play.vchanrate" : "rec.vchanrate" 669bba4862cSAriff Abdullah 670285648f9SCameron Grant int 6713af2beb8SChristos Margiolis vchan_create(struct pcm_channel *parent) 672285648f9SCameron Grant { 67390da2b28SAriff Abdullah struct snddev_info *d; 67490da2b28SAriff Abdullah struct pcm_channel *ch; 67597d69a96SAlexander Leidinger struct pcmchan_caps *parent_caps; 67690da2b28SAriff Abdullah uint32_t vchanfmt, vchanspd; 67790da2b28SAriff Abdullah int ret, direction, r, save; 67890da2b28SAriff Abdullah 67990da2b28SAriff Abdullah d = parent->parentsnddev; 68097d69a96SAlexander Leidinger 681e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 68290da2b28SAriff Abdullah CHN_LOCKASSERT(parent); 683e4e61333SAriff Abdullah 68497d69a96SAlexander Leidinger if (!(parent->flags & CHN_F_BUSY)) 685bba4862cSAriff Abdullah return (EBUSY); 68697d69a96SAlexander Leidinger 68790da2b28SAriff Abdullah if (!(parent->direction == PCMDIR_PLAY || 68890da2b28SAriff Abdullah parent->direction == PCMDIR_REC)) 68990da2b28SAriff Abdullah return (EINVAL); 69090da2b28SAriff Abdullah 69190da2b28SAriff Abdullah d = parent->parentsnddev; 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 } 713285648f9SCameron Grant 714285648f9SCameron Grant /* add us to our grandparent's channel list */ 715139bcec8SChristos Margiolis pcm_chn_add(d, ch); 71690da2b28SAriff Abdullah PCM_UNLOCK(d); 717285648f9SCameron Grant 71812e524a2SDon Lewis CHN_LOCK(parent); 71990da2b28SAriff Abdullah /* 72090da2b28SAriff Abdullah * Add us to our parent channel's children in reverse order 72190da2b28SAriff Abdullah * so future destruction will pick the last (biggest number) 72290da2b28SAriff Abdullah * channel. 72390da2b28SAriff Abdullah */ 72490da2b28SAriff Abdullah CHN_INSERT_SORT_DESCEND(parent, ch, children); 72590da2b28SAriff Abdullah 72690da2b28SAriff Abdullah if (parent->flags & CHN_F_HAS_VCHAN) 72790da2b28SAriff Abdullah return (0); 72890da2b28SAriff Abdullah 72997d69a96SAlexander Leidinger parent->flags |= CHN_F_HAS_VCHAN; 73087506547SAlexander Leidinger 731139bcec8SChristos Margiolis ret = 0; 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) { 771*d6d4586bSChristos Margiolis /* No saved value, no hint, NOTHING. */ 77290da2b28SAriff Abdullah vchanspd = VCHAN_DEFAULT_RATE; 773*d6d4586bSChristos Margiolis RANGE(vchanspd, parent_caps->minspeed, 774*d6d4586bSChristos Margiolis parent_caps->maxspeed); 77597efeca3SAriff Abdullah } 77690da2b28SAriff Abdullah save = 1; 77787506547SAlexander Leidinger } 77887506547SAlexander Leidinger 77990da2b28SAriff Abdullah if (ret == 0) { 78090da2b28SAriff Abdullah /* 78190da2b28SAriff Abdullah * Limit the speed between feeder_rate_min <-> feeder_rate_max. 78290da2b28SAriff Abdullah */ 783*d6d4586bSChristos Margiolis RANGE(vchanspd, feeder_rate_min, feeder_rate_max); 78490da2b28SAriff Abdullah 785a580b31aSAriff Abdullah if (feeder_rate_round) { 78690da2b28SAriff Abdullah RANGE(vchanspd, parent_caps->minspeed, 78790da2b28SAriff Abdullah parent_caps->maxspeed); 78890da2b28SAriff Abdullah vchanspd = CHANNEL_SETSPEED(parent->methods, 78990da2b28SAriff Abdullah parent->devinfo, vchanspd); 790a580b31aSAriff Abdullah } 79187506547SAlexander Leidinger 79290da2b28SAriff Abdullah ret = chn_reset(parent, vchanfmt, vchanspd); 793d45d1f20SAriff Abdullah } 79497d69a96SAlexander Leidinger 79590da2b28SAriff Abdullah if (ret == 0 && save) { 79687506547SAlexander Leidinger /* 797bba4862cSAriff Abdullah * Save new value. 79887506547SAlexander Leidinger */ 799bba4862cSAriff Abdullah if (direction == PCMDIR_PLAY_VIRTUAL) { 80090da2b28SAriff Abdullah d->pvchanformat = parent->format; 80190da2b28SAriff Abdullah d->pvchanrate = parent->speed; 802bba4862cSAriff Abdullah } else { 80390da2b28SAriff Abdullah d->rvchanformat = parent->format; 80490da2b28SAriff Abdullah d->rvchanrate = parent->speed; 80587506547SAlexander Leidinger } 806285648f9SCameron Grant } 807285648f9SCameron Grant 80890da2b28SAriff Abdullah /* 80990da2b28SAriff Abdullah * If the parent channel supports digital format, 81090da2b28SAriff Abdullah * enable passthrough mode. 81190da2b28SAriff Abdullah */ 81290da2b28SAriff Abdullah if (ret == 0 && snd_fmtvalid(AFMT_PASSTHROUGH, parent_caps->fmtlist)) { 81390da2b28SAriff Abdullah parent->flags &= ~CHN_F_VCHAN_DYNAMIC; 81490da2b28SAriff Abdullah parent->flags |= CHN_F_VCHAN_PASSTHROUGH; 81590da2b28SAriff Abdullah } 81690da2b28SAriff Abdullah 81790da2b28SAriff Abdullah if (ret != 0) { 818bba4862cSAriff Abdullah CHN_REMOVE(parent, ch, children); 81997d69a96SAlexander Leidinger parent->flags &= ~CHN_F_HAS_VCHAN; 82097d69a96SAlexander Leidinger CHN_UNLOCK(parent); 82190da2b28SAriff Abdullah PCM_LOCK(d); 822bba4862cSAriff Abdullah if (pcm_chn_remove(d, ch) == 0) { 82390da2b28SAriff Abdullah PCM_UNLOCK(d); 824b3ea087cSChristos Margiolis chn_kill(ch); 825bba4862cSAriff Abdullah } else 82690da2b28SAriff Abdullah PCM_UNLOCK(d); 82797d69a96SAlexander Leidinger CHN_LOCK(parent); 82897d69a96SAlexander Leidinger } 82997d69a96SAlexander Leidinger 83090da2b28SAriff Abdullah return (ret); 83197d69a96SAlexander Leidinger } 832285648f9SCameron Grant 833285648f9SCameron Grant int 834285648f9SCameron Grant vchan_destroy(struct pcm_channel *c) 835285648f9SCameron Grant { 83690da2b28SAriff Abdullah struct pcm_channel *parent; 83790da2b28SAriff Abdullah struct snddev_info *d; 83890da2b28SAriff Abdullah int ret; 83990da2b28SAriff Abdullah 84090da2b28SAriff Abdullah KASSERT(c != NULL && c->parentchannel != NULL && 84190da2b28SAriff Abdullah c->parentsnddev != NULL, ("%s(): invalid channel=%p", 84290da2b28SAriff Abdullah __func__, c)); 84390da2b28SAriff Abdullah 84490da2b28SAriff Abdullah CHN_LOCKASSERT(c); 84590da2b28SAriff Abdullah 84690da2b28SAriff Abdullah d = c->parentsnddev; 84790da2b28SAriff Abdullah parent = c->parentchannel; 848285648f9SCameron Grant 849e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 85090da2b28SAriff Abdullah CHN_LOCKASSERT(parent); 851e4e61333SAriff Abdullah 85290da2b28SAriff Abdullah CHN_UNLOCK(c); 85390da2b28SAriff Abdullah 85490da2b28SAriff Abdullah if (!(parent->flags & CHN_F_BUSY)) 855bba4862cSAriff Abdullah return (EBUSY); 85690da2b28SAriff Abdullah 85790da2b28SAriff Abdullah if (CHN_EMPTY(parent, children)) 858bba4862cSAriff Abdullah return (EINVAL); 85949c5e6e2SCameron Grant 860285648f9SCameron Grant /* remove us from our parent's children list */ 861bba4862cSAriff Abdullah CHN_REMOVE(parent, c, children); 862285648f9SCameron Grant 863bba4862cSAriff Abdullah if (CHN_EMPTY(parent, children)) { 8649c271ebaSAriff Abdullah parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 86590da2b28SAriff Abdullah chn_reset(parent, parent->format, parent->speed); 86697d69a96SAlexander Leidinger } 867f637a36cSCameron Grant 86849c5e6e2SCameron Grant CHN_UNLOCK(parent); 869bba4862cSAriff Abdullah 870bba4862cSAriff Abdullah /* remove us from our grandparent's channel list */ 87190da2b28SAriff Abdullah PCM_LOCK(d); 87290da2b28SAriff Abdullah ret = pcm_chn_remove(d, c); 87390da2b28SAriff Abdullah PCM_UNLOCK(d); 874bba4862cSAriff Abdullah 875285648f9SCameron Grant /* destroy ourselves */ 87690da2b28SAriff Abdullah if (ret == 0) 877b3ea087cSChristos Margiolis chn_kill(c); 878285648f9SCameron Grant 87990da2b28SAriff Abdullah CHN_LOCK(parent); 88090da2b28SAriff Abdullah 88190da2b28SAriff Abdullah return (ret); 882285648f9SCameron Grant } 883285648f9SCameron Grant 884285648f9SCameron Grant int 88590da2b28SAriff Abdullah #ifdef SND_DEBUG 88690da2b28SAriff Abdullah vchan_passthrough(struct pcm_channel *c, const char *caller) 88790da2b28SAriff Abdullah #else 88890da2b28SAriff Abdullah vchan_sync(struct pcm_channel *c) 88990da2b28SAriff Abdullah #endif 89090da2b28SAriff Abdullah { 89190da2b28SAriff Abdullah int ret; 89290da2b28SAriff Abdullah 89390da2b28SAriff Abdullah KASSERT(c != NULL && c->parentchannel != NULL && 89490da2b28SAriff Abdullah (c->flags & CHN_F_VIRTUAL), 89590da2b28SAriff Abdullah ("%s(): invalid passthrough", __func__)); 89690da2b28SAriff Abdullah CHN_LOCKASSERT(c); 89790da2b28SAriff Abdullah CHN_LOCKASSERT(c->parentchannel); 89890da2b28SAriff Abdullah 89990da2b28SAriff Abdullah sndbuf_setspd(c->bufhard, c->parentchannel->speed); 90090da2b28SAriff Abdullah c->flags |= CHN_F_PASSTHROUGH; 90190da2b28SAriff Abdullah ret = feeder_chain(c); 90290da2b28SAriff Abdullah c->flags &= ~(CHN_F_DIRTY | CHN_F_PASSTHROUGH); 90390da2b28SAriff Abdullah if (ret != 0) 90490da2b28SAriff Abdullah c->flags |= CHN_F_DIRTY; 90590da2b28SAriff Abdullah 90690da2b28SAriff Abdullah #ifdef SND_DEBUG 90790da2b28SAriff Abdullah if (snd_passthrough_verbose != 0) { 90890da2b28SAriff Abdullah char *devname, buf[CHN_NAMELEN]; 90990da2b28SAriff Abdullah 91025723d66SChristos Margiolis devname = dsp_unit2name(buf, sizeof(buf), c); 91190da2b28SAriff Abdullah device_printf(c->dev, 91290da2b28SAriff Abdullah "%s(%s/%s) %s() -> re-sync err=%d\n", 91390da2b28SAriff Abdullah __func__, (devname != NULL) ? devname : "dspX", c->comm, 91490da2b28SAriff Abdullah caller, ret); 91590da2b28SAriff Abdullah } 91690da2b28SAriff Abdullah #endif 91790da2b28SAriff Abdullah 91890da2b28SAriff Abdullah return (ret); 91990da2b28SAriff Abdullah } 92090da2b28SAriff Abdullah 9217ad5f383SChristos Margiolis int 9223af2beb8SChristos Margiolis vchan_setnew(struct snddev_info *d, int direction, int newcnt) 9237ad5f383SChristos Margiolis { 9247ad5f383SChristos Margiolis struct pcm_channel *c, *ch, *nch; 9257ad5f383SChristos Margiolis struct pcmchan_caps *caps; 9267ad5f383SChristos Margiolis int i, err, vcnt; 9277ad5f383SChristos Margiolis 9287ad5f383SChristos Margiolis PCM_BUSYASSERT(d); 9297ad5f383SChristos Margiolis 9307ad5f383SChristos Margiolis if ((direction == PCMDIR_PLAY && d->playcount < 1) || 9317ad5f383SChristos Margiolis (direction == PCMDIR_REC && d->reccount < 1)) 9327ad5f383SChristos Margiolis return (ENODEV); 9337ad5f383SChristos Margiolis 9347ad5f383SChristos Margiolis if (!(d->flags & SD_F_AUTOVCHAN)) 9357ad5f383SChristos Margiolis return (EINVAL); 9367ad5f383SChristos Margiolis 9377ad5f383SChristos Margiolis if (newcnt < 0 || newcnt > SND_MAXVCHANS) 9387ad5f383SChristos Margiolis return (E2BIG); 9397ad5f383SChristos Margiolis 9407ad5f383SChristos Margiolis if (direction == PCMDIR_PLAY) 9417ad5f383SChristos Margiolis vcnt = d->pvchancount; 9427ad5f383SChristos Margiolis else if (direction == PCMDIR_REC) 9437ad5f383SChristos Margiolis vcnt = d->rvchancount; 9447ad5f383SChristos Margiolis else 9457ad5f383SChristos Margiolis return (EINVAL); 9467ad5f383SChristos Margiolis 9477ad5f383SChristos Margiolis if (newcnt > vcnt) { 9483af2beb8SChristos Margiolis KASSERT((newcnt - 1) == vcnt, 9493af2beb8SChristos Margiolis ("bogus vchan_create() request newcnt=%d vcnt=%d", 9503af2beb8SChristos Margiolis newcnt, vcnt)); 9517ad5f383SChristos Margiolis /* add new vchans - find a parent channel first */ 9527ad5f383SChristos Margiolis ch = NULL; 9537ad5f383SChristos Margiolis CHN_FOREACH(c, d, channels.pcm) { 9547ad5f383SChristos Margiolis CHN_LOCK(c); 9557ad5f383SChristos Margiolis if (c->direction == direction && 9567ad5f383SChristos Margiolis ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 9577ad5f383SChristos Margiolis c->refcount < 1 && 9587ad5f383SChristos Margiolis !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 9597ad5f383SChristos Margiolis /* 9607ad5f383SChristos Margiolis * Reuse hw channel with vchans already 9617ad5f383SChristos Margiolis * created. 9627ad5f383SChristos Margiolis */ 9637ad5f383SChristos Margiolis if (c->flags & CHN_F_HAS_VCHAN) { 9647ad5f383SChristos Margiolis ch = c; 9657ad5f383SChristos Margiolis break; 9667ad5f383SChristos Margiolis } 9677ad5f383SChristos Margiolis /* 9687ad5f383SChristos Margiolis * No vchans ever created, look for 9697ad5f383SChristos Margiolis * channels with supported formats. 9707ad5f383SChristos Margiolis */ 9717ad5f383SChristos Margiolis caps = chn_getcaps(c); 9727ad5f383SChristos Margiolis if (caps == NULL) { 9737ad5f383SChristos Margiolis CHN_UNLOCK(c); 9747ad5f383SChristos Margiolis continue; 9757ad5f383SChristos Margiolis } 9767ad5f383SChristos Margiolis for (i = 0; caps->fmtlist[i] != 0; i++) { 9777ad5f383SChristos Margiolis if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 9787ad5f383SChristos Margiolis break; 9797ad5f383SChristos Margiolis } 9807ad5f383SChristos Margiolis if (caps->fmtlist[i] != 0) { 9817ad5f383SChristos Margiolis ch = c; 9827ad5f383SChristos Margiolis break; 9837ad5f383SChristos Margiolis } 9847ad5f383SChristos Margiolis } 9857ad5f383SChristos Margiolis CHN_UNLOCK(c); 9867ad5f383SChristos Margiolis } 9877ad5f383SChristos Margiolis if (ch == NULL) 9887ad5f383SChristos Margiolis return (EBUSY); 9897ad5f383SChristos Margiolis ch->flags |= CHN_F_BUSY; 9907ad5f383SChristos Margiolis err = 0; 9917ad5f383SChristos Margiolis while (err == 0 && newcnt > vcnt) { 9923af2beb8SChristos Margiolis err = vchan_create(ch); 9937ad5f383SChristos Margiolis if (err == 0) 9947ad5f383SChristos Margiolis vcnt++; 9957ad5f383SChristos Margiolis else if (err == E2BIG && newcnt > vcnt) 9967ad5f383SChristos Margiolis device_printf(d->dev, 9977ad5f383SChristos Margiolis "%s: err=%d Maximum channel reached.\n", 9987ad5f383SChristos Margiolis __func__, err); 9997ad5f383SChristos Margiolis } 10007ad5f383SChristos Margiolis if (vcnt == 0) 10017ad5f383SChristos Margiolis ch->flags &= ~CHN_F_BUSY; 10027ad5f383SChristos Margiolis CHN_UNLOCK(ch); 10037ad5f383SChristos Margiolis if (err != 0) 10047ad5f383SChristos Margiolis return (err); 10057ad5f383SChristos Margiolis } else if (newcnt < vcnt) { 10067ad5f383SChristos Margiolis CHN_FOREACH(c, d, channels.pcm) { 10077ad5f383SChristos Margiolis CHN_LOCK(c); 10087ad5f383SChristos Margiolis if (c->direction != direction || 10097ad5f383SChristos Margiolis CHN_EMPTY(c, children) || 10107ad5f383SChristos Margiolis !(c->flags & CHN_F_HAS_VCHAN)) { 10117ad5f383SChristos Margiolis CHN_UNLOCK(c); 10127ad5f383SChristos Margiolis continue; 10137ad5f383SChristos Margiolis } 10147ad5f383SChristos Margiolis CHN_FOREACH_SAFE(ch, c, nch, children) { 10157ad5f383SChristos Margiolis CHN_LOCK(ch); 10167ad5f383SChristos Margiolis if (vcnt == 1 && c->refcount > 0) { 10177ad5f383SChristos Margiolis CHN_UNLOCK(ch); 10187ad5f383SChristos Margiolis break; 10197ad5f383SChristos Margiolis } 10207ad5f383SChristos Margiolis if (!(ch->flags & CHN_F_BUSY) && 10217ad5f383SChristos Margiolis ch->refcount < 1) { 10227ad5f383SChristos Margiolis err = vchan_destroy(ch); 10237ad5f383SChristos Margiolis if (err == 0) 10247ad5f383SChristos Margiolis vcnt--; 10257ad5f383SChristos Margiolis } else 10267ad5f383SChristos Margiolis CHN_UNLOCK(ch); 10277ad5f383SChristos Margiolis if (vcnt == newcnt) 10287ad5f383SChristos Margiolis break; 10297ad5f383SChristos Margiolis } 10307ad5f383SChristos Margiolis CHN_UNLOCK(c); 10317ad5f383SChristos Margiolis break; 10327ad5f383SChristos Margiolis } 10337ad5f383SChristos Margiolis } 10347ad5f383SChristos Margiolis 10357ad5f383SChristos Margiolis return (0); 10367ad5f383SChristos Margiolis } 10377ad5f383SChristos Margiolis 10387ad5f383SChristos Margiolis void 10397ad5f383SChristos Margiolis vchan_setmaxauto(struct snddev_info *d, int num) 10407ad5f383SChristos Margiolis { 10417ad5f383SChristos Margiolis PCM_BUSYASSERT(d); 10427ad5f383SChristos Margiolis 10437ad5f383SChristos Margiolis if (num < 0) 10447ad5f383SChristos Margiolis return; 10457ad5f383SChristos Margiolis 10467ad5f383SChristos Margiolis if (num >= 0 && d->pvchancount > num) 10473af2beb8SChristos Margiolis (void)vchan_setnew(d, PCMDIR_PLAY, num); 10487ad5f383SChristos Margiolis else if (num > 0 && d->pvchancount == 0) 10493af2beb8SChristos Margiolis (void)vchan_setnew(d, PCMDIR_PLAY, 1); 10507ad5f383SChristos Margiolis 10517ad5f383SChristos Margiolis if (num >= 0 && d->rvchancount > num) 10523af2beb8SChristos Margiolis (void)vchan_setnew(d, PCMDIR_REC, num); 10537ad5f383SChristos Margiolis else if (num > 0 && d->rvchancount == 0) 10543af2beb8SChristos Margiolis (void)vchan_setnew(d, PCMDIR_REC, 1); 10557ad5f383SChristos Margiolis } 10567ad5f383SChristos Margiolis 10577ad5f383SChristos Margiolis static int 10587ad5f383SChristos Margiolis sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 10597ad5f383SChristos Margiolis { 10607ad5f383SChristos Margiolis struct snddev_info *d; 10617ad5f383SChristos Margiolis int i, v, error; 10627ad5f383SChristos Margiolis 10637ad5f383SChristos Margiolis v = snd_maxautovchans; 10647ad5f383SChristos Margiolis error = sysctl_handle_int(oidp, &v, 0, req); 10657ad5f383SChristos Margiolis if (error == 0 && req->newptr != NULL) { 10667ad5f383SChristos Margiolis if (v < 0) 10677ad5f383SChristos Margiolis v = 0; 10687ad5f383SChristos Margiolis if (v > SND_MAXVCHANS) 10697ad5f383SChristos Margiolis v = SND_MAXVCHANS; 10707ad5f383SChristos Margiolis snd_maxautovchans = v; 10717ad5f383SChristos Margiolis for (i = 0; pcm_devclass != NULL && 10727ad5f383SChristos Margiolis i < devclass_get_maxunit(pcm_devclass); i++) { 10737ad5f383SChristos Margiolis d = devclass_get_softc(pcm_devclass, i); 10747ad5f383SChristos Margiolis if (!PCM_REGISTERED(d)) 10757ad5f383SChristos Margiolis continue; 10767ad5f383SChristos Margiolis PCM_ACQUIRE_QUICK(d); 10777ad5f383SChristos Margiolis vchan_setmaxauto(d, v); 10787ad5f383SChristos Margiolis PCM_RELEASE_QUICK(d); 10797ad5f383SChristos Margiolis } 10807ad5f383SChristos Margiolis } 10817ad5f383SChristos Margiolis return (error); 10827ad5f383SChristos Margiolis } 10837ad5f383SChristos Margiolis SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, 10847ad5f383SChristos Margiolis CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 10857ad5f383SChristos Margiolis sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 10867ad5f383SChristos Margiolis 108790da2b28SAriff Abdullah void 108867b1dce3SCameron Grant vchan_initsys(device_t dev) 1089285648f9SCameron Grant { 109067b1dce3SCameron Grant struct snddev_info *d; 1091bba4862cSAriff Abdullah int unit; 109267b1dce3SCameron Grant 1093bba4862cSAriff Abdullah unit = device_get_unit(dev); 109467b1dce3SCameron Grant d = device_get_softc(dev); 1095bba4862cSAriff Abdullah 1096bba4862cSAriff Abdullah /* Play */ 1097bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1098bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree), 10997029da5cSPawel Biernacki OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1100bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 110190da2b28SAriff Abdullah sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 110290da2b28SAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 110390da2b28SAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree), 11047029da5cSPawel Biernacki OID_AUTO, "vchanmode", 11057029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 110690da2b28SAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 110790da2b28SAriff Abdullah sysctl_dev_pcm_vchanmode, "A", 110890da2b28SAriff Abdullah "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1109bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1110bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree), 11117029da5cSPawel Biernacki OID_AUTO, "vchanrate", 11127029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1113bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 111490da2b28SAriff Abdullah sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1115bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx, 1116bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree), 11177029da5cSPawel Biernacki OID_AUTO, "vchanformat", 11187029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1119bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, 112090da2b28SAriff Abdullah sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1121bba4862cSAriff Abdullah /* Rec */ 1122bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1123bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree), 11247029da5cSPawel Biernacki OID_AUTO, "vchans", 11257029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1126bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 112790da2b28SAriff Abdullah sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); 112890da2b28SAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 112990da2b28SAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree), 11307029da5cSPawel Biernacki OID_AUTO, "vchanmode", 11317029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 113290da2b28SAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 113390da2b28SAriff Abdullah sysctl_dev_pcm_vchanmode, "A", 113490da2b28SAriff Abdullah "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive"); 1135bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1136bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree), 11377029da5cSPawel Biernacki OID_AUTO, "vchanrate", 11387029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1139bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 114090da2b28SAriff Abdullah sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); 1141bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, 1142bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree), 11437029da5cSPawel Biernacki OID_AUTO, "vchanformat", 11447029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1145bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, 114690da2b28SAriff Abdullah sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format"); 1147285648f9SCameron Grant } 1148