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 *
vchan_init(kobj_t obj,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir)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
vchan_free(kobj_t obj,void * data)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
vchan_setformat(kobj_t obj,void * data,uint32_t format)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
vchan_setspeed(kobj_t obj,void * data,uint32_t speed)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
vchan_trigger(kobj_t obj,void * data,int go)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 *
vchan_getcaps(kobj_t obj,void * data)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 *
vchan_getmatrix(kobj_t obj,void * data,uint32_t format)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
vchan_getparentchannel(struct snddev_info * d,struct pcm_channel ** wrch,struct pcm_channel ** rdch)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
sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS)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
sysctl_dev_pcm_vchanmode(SYSCTL_HANDLER_ARGS)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
sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS)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
sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS)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
vchan_create(struct pcm_channel * parent)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
vchan_destroy(struct pcm_channel * c)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
8379263f854SChristos 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
8639263f854SChristos Margiolis return (0);
864285648f9SCameron Grant }
865285648f9SCameron Grant
866285648f9SCameron Grant int
86790da2b28SAriff Abdullah #ifdef SND_DEBUG
vchan_passthrough(struct pcm_channel * c,const char * caller)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
889802c78f5SChristos Margiolis if (snd_passthrough_verbose) {
890802c78f5SChristos Margiolis device_printf(c->dev, "%s(%s/%s) %s() -> re-sync err=%d\n",
891802c78f5SChristos Margiolis __func__, c->name, c->comm, caller, ret);
89290da2b28SAriff Abdullah }
89390da2b28SAriff Abdullah #endif
89490da2b28SAriff Abdullah
89590da2b28SAriff Abdullah return (ret);
89690da2b28SAriff Abdullah }
89790da2b28SAriff Abdullah
8987ad5f383SChristos Margiolis int
vchan_setnew(struct snddev_info * d,int direction,int newcnt)8993af2beb8SChristos Margiolis vchan_setnew(struct snddev_info *d, int direction, int newcnt)
9007ad5f383SChristos Margiolis {
9017ad5f383SChristos Margiolis struct pcm_channel *c, *ch, *nch;
9027ad5f383SChristos Margiolis struct pcmchan_caps *caps;
9037ad5f383SChristos Margiolis int i, err, vcnt;
9047ad5f383SChristos Margiolis
9057ad5f383SChristos Margiolis PCM_BUSYASSERT(d);
9067ad5f383SChristos Margiolis
9077ad5f383SChristos Margiolis if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
9087ad5f383SChristos Margiolis (direction == PCMDIR_REC && d->reccount < 1))
9097ad5f383SChristos Margiolis return (ENODEV);
9107ad5f383SChristos Margiolis
9117ad5f383SChristos Margiolis if (!(d->flags & SD_F_AUTOVCHAN))
9127ad5f383SChristos Margiolis return (EINVAL);
9137ad5f383SChristos Margiolis
9147ad5f383SChristos Margiolis if (newcnt < 0 || newcnt > SND_MAXVCHANS)
9157ad5f383SChristos Margiolis return (E2BIG);
9167ad5f383SChristos Margiolis
9177ad5f383SChristos Margiolis if (direction == PCMDIR_PLAY)
9187ad5f383SChristos Margiolis vcnt = d->pvchancount;
9197ad5f383SChristos Margiolis else if (direction == PCMDIR_REC)
9207ad5f383SChristos Margiolis vcnt = d->rvchancount;
9217ad5f383SChristos Margiolis else
9227ad5f383SChristos Margiolis return (EINVAL);
9237ad5f383SChristos Margiolis
9247ad5f383SChristos Margiolis if (newcnt > vcnt) {
9257ad5f383SChristos Margiolis /* add new vchans - find a parent channel first */
9267ad5f383SChristos Margiolis ch = NULL;
9277ad5f383SChristos Margiolis CHN_FOREACH(c, d, channels.pcm) {
9287ad5f383SChristos Margiolis CHN_LOCK(c);
9297ad5f383SChristos Margiolis if (c->direction == direction &&
9307ad5f383SChristos Margiolis ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
9317ad5f383SChristos Margiolis !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
9327ad5f383SChristos Margiolis /*
9337ad5f383SChristos Margiolis * Reuse hw channel with vchans already
9347ad5f383SChristos Margiolis * created.
9357ad5f383SChristos Margiolis */
9367ad5f383SChristos Margiolis if (c->flags & CHN_F_HAS_VCHAN) {
9377ad5f383SChristos Margiolis ch = c;
9387ad5f383SChristos Margiolis break;
9397ad5f383SChristos Margiolis }
9407ad5f383SChristos Margiolis /*
9417ad5f383SChristos Margiolis * No vchans ever created, look for
9427ad5f383SChristos Margiolis * channels with supported formats.
9437ad5f383SChristos Margiolis */
9447ad5f383SChristos Margiolis caps = chn_getcaps(c);
9457ad5f383SChristos Margiolis if (caps == NULL) {
9467ad5f383SChristos Margiolis CHN_UNLOCK(c);
9477ad5f383SChristos Margiolis continue;
9487ad5f383SChristos Margiolis }
9497ad5f383SChristos Margiolis for (i = 0; caps->fmtlist[i] != 0; i++) {
9507ad5f383SChristos Margiolis if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
9517ad5f383SChristos Margiolis break;
9527ad5f383SChristos Margiolis }
9537ad5f383SChristos Margiolis if (caps->fmtlist[i] != 0) {
9547ad5f383SChristos Margiolis ch = c;
9557ad5f383SChristos Margiolis break;
9567ad5f383SChristos Margiolis }
9577ad5f383SChristos Margiolis }
9587ad5f383SChristos Margiolis CHN_UNLOCK(c);
9597ad5f383SChristos Margiolis }
9607ad5f383SChristos Margiolis if (ch == NULL)
9617ad5f383SChristos Margiolis return (EBUSY);
9627ad5f383SChristos Margiolis ch->flags |= CHN_F_BUSY;
9637ad5f383SChristos Margiolis err = 0;
9647ad5f383SChristos Margiolis while (err == 0 && newcnt > vcnt) {
9653af2beb8SChristos Margiolis err = vchan_create(ch);
9667ad5f383SChristos Margiolis if (err == 0)
9677ad5f383SChristos Margiolis vcnt++;
9687ad5f383SChristos Margiolis else if (err == E2BIG && newcnt > vcnt)
9697ad5f383SChristos Margiolis device_printf(d->dev,
9707ad5f383SChristos Margiolis "%s: err=%d Maximum channel reached.\n",
9717ad5f383SChristos Margiolis __func__, err);
9727ad5f383SChristos Margiolis }
9737ad5f383SChristos Margiolis if (vcnt == 0)
9747ad5f383SChristos Margiolis ch->flags &= ~CHN_F_BUSY;
9757ad5f383SChristos Margiolis CHN_UNLOCK(ch);
9767ad5f383SChristos Margiolis if (err != 0)
9777ad5f383SChristos Margiolis return (err);
9787ad5f383SChristos Margiolis } else if (newcnt < vcnt) {
9797ad5f383SChristos Margiolis CHN_FOREACH(c, d, channels.pcm) {
9807ad5f383SChristos Margiolis CHN_LOCK(c);
9817ad5f383SChristos Margiolis if (c->direction != direction ||
9827ad5f383SChristos Margiolis CHN_EMPTY(c, children) ||
9837ad5f383SChristos Margiolis !(c->flags & CHN_F_HAS_VCHAN)) {
9847ad5f383SChristos Margiolis CHN_UNLOCK(c);
9857ad5f383SChristos Margiolis continue;
9867ad5f383SChristos Margiolis }
9877ad5f383SChristos Margiolis CHN_FOREACH_SAFE(ch, c, nch, children) {
9887ad5f383SChristos Margiolis CHN_LOCK(ch);
989*43c0b593SChristos Margiolis if (vcnt == 1 && ch->flags & CHN_F_BUSY) {
9907ad5f383SChristos Margiolis CHN_UNLOCK(ch);
9917ad5f383SChristos Margiolis break;
9927ad5f383SChristos Margiolis }
993*43c0b593SChristos Margiolis if (!(ch->flags & CHN_F_BUSY)) {
9947ad5f383SChristos Margiolis err = vchan_destroy(ch);
9957ad5f383SChristos Margiolis if (err == 0)
9967ad5f383SChristos Margiolis vcnt--;
9977ad5f383SChristos Margiolis } else
9987ad5f383SChristos Margiolis CHN_UNLOCK(ch);
9997ad5f383SChristos Margiolis if (vcnt == newcnt)
10007ad5f383SChristos Margiolis break;
10017ad5f383SChristos Margiolis }
10027ad5f383SChristos Margiolis CHN_UNLOCK(c);
10037ad5f383SChristos Margiolis break;
10047ad5f383SChristos Margiolis }
10057ad5f383SChristos Margiolis }
10067ad5f383SChristos Margiolis
10077ad5f383SChristos Margiolis return (0);
10087ad5f383SChristos Margiolis }
10097ad5f383SChristos Margiolis
10107ad5f383SChristos Margiolis void
vchan_setmaxauto(struct snddev_info * d,int num)10117ad5f383SChristos Margiolis vchan_setmaxauto(struct snddev_info *d, int num)
10127ad5f383SChristos Margiolis {
10137ad5f383SChristos Margiolis PCM_BUSYASSERT(d);
10147ad5f383SChristos Margiolis
10157ad5f383SChristos Margiolis if (num < 0)
10167ad5f383SChristos Margiolis return;
10177ad5f383SChristos Margiolis
10187ad5f383SChristos Margiolis if (num >= 0 && d->pvchancount > num)
10193af2beb8SChristos Margiolis (void)vchan_setnew(d, PCMDIR_PLAY, num);
10207ad5f383SChristos Margiolis else if (num > 0 && d->pvchancount == 0)
10213af2beb8SChristos Margiolis (void)vchan_setnew(d, PCMDIR_PLAY, 1);
10227ad5f383SChristos Margiolis
10237ad5f383SChristos Margiolis if (num >= 0 && d->rvchancount > num)
10243af2beb8SChristos Margiolis (void)vchan_setnew(d, PCMDIR_REC, num);
10257ad5f383SChristos Margiolis else if (num > 0 && d->rvchancount == 0)
10263af2beb8SChristos Margiolis (void)vchan_setnew(d, PCMDIR_REC, 1);
10277ad5f383SChristos Margiolis }
10287ad5f383SChristos Margiolis
10297ad5f383SChristos Margiolis static int
sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)10307ad5f383SChristos Margiolis sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
10317ad5f383SChristos Margiolis {
10327ad5f383SChristos Margiolis struct snddev_info *d;
10337ad5f383SChristos Margiolis int i, v, error;
10347ad5f383SChristos Margiolis
10357ad5f383SChristos Margiolis v = snd_maxautovchans;
10367ad5f383SChristos Margiolis error = sysctl_handle_int(oidp, &v, 0, req);
10377ad5f383SChristos Margiolis if (error == 0 && req->newptr != NULL) {
10387ad5f383SChristos Margiolis if (v < 0)
10397ad5f383SChristos Margiolis v = 0;
10407ad5f383SChristos Margiolis if (v > SND_MAXVCHANS)
10417ad5f383SChristos Margiolis v = SND_MAXVCHANS;
10427ad5f383SChristos Margiolis snd_maxautovchans = v;
10437ad5f383SChristos Margiolis for (i = 0; pcm_devclass != NULL &&
10447ad5f383SChristos Margiolis i < devclass_get_maxunit(pcm_devclass); i++) {
10457ad5f383SChristos Margiolis d = devclass_get_softc(pcm_devclass, i);
10467ad5f383SChristos Margiolis if (!PCM_REGISTERED(d))
10477ad5f383SChristos Margiolis continue;
10487ad5f383SChristos Margiolis PCM_ACQUIRE_QUICK(d);
10497ad5f383SChristos Margiolis vchan_setmaxauto(d, v);
10507ad5f383SChristos Margiolis PCM_RELEASE_QUICK(d);
10517ad5f383SChristos Margiolis }
10527ad5f383SChristos Margiolis }
10537ad5f383SChristos Margiolis return (error);
10547ad5f383SChristos Margiolis }
10557ad5f383SChristos Margiolis SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans,
10567ad5f383SChristos Margiolis CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
10577ad5f383SChristos Margiolis sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
10587ad5f383SChristos Margiolis
105990da2b28SAriff Abdullah void
vchan_initsys(device_t dev)106067b1dce3SCameron Grant vchan_initsys(device_t dev)
1061285648f9SCameron Grant {
106267b1dce3SCameron Grant struct snddev_info *d;
1063bba4862cSAriff Abdullah int unit;
106467b1dce3SCameron Grant
1065bba4862cSAriff Abdullah unit = device_get_unit(dev);
106667b1dce3SCameron Grant d = device_get_softc(dev);
1067bba4862cSAriff Abdullah
1068bba4862cSAriff Abdullah /* Play */
1069bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1070bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree),
10717029da5cSPawel Biernacki OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1072bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
107390da2b28SAriff Abdullah sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
107490da2b28SAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
107590da2b28SAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree),
10767029da5cSPawel Biernacki OID_AUTO, "vchanmode",
10777029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
107890da2b28SAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
107990da2b28SAriff Abdullah sysctl_dev_pcm_vchanmode, "A",
108090da2b28SAriff Abdullah "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
1081bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1082bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree),
10837029da5cSPawel Biernacki OID_AUTO, "vchanrate",
10847029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1085bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
108690da2b28SAriff Abdullah sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
1087bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1088bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->play_sysctl_tree),
10897029da5cSPawel Biernacki OID_AUTO, "vchanformat",
10907029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1091bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
109290da2b28SAriff Abdullah sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
1093bba4862cSAriff Abdullah /* Rec */
1094bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1095bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree),
10967029da5cSPawel Biernacki OID_AUTO, "vchans",
10977029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1098bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
109990da2b28SAriff Abdullah sysctl_dev_pcm_vchans, "I", "total allocated virtual channel");
110090da2b28SAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
110190da2b28SAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree),
11027029da5cSPawel Biernacki OID_AUTO, "vchanmode",
11037029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
110490da2b28SAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
110590da2b28SAriff Abdullah sysctl_dev_pcm_vchanmode, "A",
110690da2b28SAriff Abdullah "vchan format/rate selection: 0=fixed, 1=passthrough, 2=adaptive");
1107bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1108bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree),
11097029da5cSPawel Biernacki OID_AUTO, "vchanrate",
11107029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1111bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
111290da2b28SAriff Abdullah sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate");
1113bba4862cSAriff Abdullah SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1114bba4862cSAriff Abdullah SYSCTL_CHILDREN(d->rec_sysctl_tree),
11157029da5cSPawel Biernacki OID_AUTO, "vchanformat",
11167029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1117bba4862cSAriff Abdullah VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
111890da2b28SAriff Abdullah sysctl_dev_pcm_vchanformat, "A", "virtual channel mixing format");
1119285648f9SCameron Grant }
1120