xref: /freebsd/sys/dev/sound/pcm/vchan.c (revision 43c0b593c2c3b2c07009c031a0e7e8190a45b31a)
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