xref: /freebsd/sys/dev/sound/pcm/sound.c (revision 3fdb3676ba938c22fd21e8a70b07dfc7f5f8cbe4)
1098ca2bdSWarner Losh /*-
23f225978SCameron Grant  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3987e5972SCameron Grant  * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
4987e5972SCameron Grant  * All rights reserved.
5987e5972SCameron Grant  *
6987e5972SCameron Grant  * Redistribution and use in source and binary forms, with or without
7987e5972SCameron Grant  * modification, are permitted provided that the following conditions
8987e5972SCameron Grant  * are met:
9987e5972SCameron Grant  * 1. Redistributions of source code must retain the above copyright
10987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer.
11987e5972SCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
12987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer in the
13987e5972SCameron Grant  *    documentation and/or other materials provided with the distribution.
14987e5972SCameron Grant  *
15987e5972SCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16987e5972SCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17987e5972SCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18987e5972SCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19987e5972SCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20987e5972SCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21987e5972SCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22987e5972SCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23987e5972SCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24987e5972SCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25987e5972SCameron Grant  * SUCH DAMAGE.
26987e5972SCameron Grant  */
27987e5972SCameron Grant 
28ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
29285648f9SCameron Grant #include <dev/sound/pcm/vchan.h>
305ee30e27SMathew Kanner #include <dev/sound/pcm/dsp.h>
317c438dbeSCameron Grant #include <sys/sysctl.h>
32285648f9SCameron Grant 
3367b1dce3SCameron Grant #include "feeder_if.h"
3467b1dce3SCameron Grant 
3567b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$");
3667b1dce3SCameron Grant 
37d95502a8SCameron Grant devclass_t pcm_devclass;
3882db23e2SCameron Grant 
39b8a36395SCameron Grant int pcm_veto_load = 1;
40b8a36395SCameron Grant 
4182db23e2SCameron Grant #ifdef USING_DEVFS
42d95502a8SCameron Grant int snd_unit = 0;
4309786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit);
4482db23e2SCameron Grant #endif
45cbe7d6a3SCameron Grant 
4667b1dce3SCameron Grant int snd_maxautovchans = 0;
4767b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
48987e5972SCameron Grant 
4982db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
5082db23e2SCameron Grant 
5167b1dce3SCameron Grant static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
5267b1dce3SCameron Grant 
5367b1dce3SCameron Grant struct sysctl_ctx_list *
5467b1dce3SCameron Grant snd_sysctl_tree(device_t dev)
5567b1dce3SCameron Grant {
5667b1dce3SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
5767b1dce3SCameron Grant 
5867b1dce3SCameron Grant 	return &d->sysctl_tree;
5967b1dce3SCameron Grant }
6067b1dce3SCameron Grant 
6167b1dce3SCameron Grant struct sysctl_oid *
6267b1dce3SCameron Grant snd_sysctl_tree_top(device_t dev)
6367b1dce3SCameron Grant {
6467b1dce3SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
6567b1dce3SCameron Grant 
6667b1dce3SCameron Grant 	return d->sysctl_tree_top;
6767b1dce3SCameron Grant }
6867b1dce3SCameron Grant 
6937209180SCameron Grant void *
702c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type)
7137209180SCameron Grant {
7237209180SCameron Grant #ifdef USING_MUTEX
7337209180SCameron Grant 	struct mtx *m;
7437209180SCameron Grant 
75a163d034SWarner Losh 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
7637209180SCameron Grant 	if (m == NULL)
7737209180SCameron Grant 		return NULL;
7812e524a2SDon Lewis 	mtx_init(m, desc, type, MTX_DEF);
7912e524a2SDon Lewis 	return m;
8012e524a2SDon Lewis #else
8112e524a2SDon Lewis 	return (void *)0xcafebabe;
8212e524a2SDon Lewis #endif
8312e524a2SDon Lewis }
8412e524a2SDon Lewis 
8537209180SCameron Grant void
8637209180SCameron Grant snd_mtxfree(void *m)
8737209180SCameron Grant {
8837209180SCameron Grant #ifdef USING_MUTEX
8937209180SCameron Grant 	struct mtx *mtx = m;
9037209180SCameron Grant 
9167beb5a5SCameron Grant 	/* mtx_assert(mtx, MA_OWNED); */
9237209180SCameron Grant 	mtx_destroy(mtx);
9337209180SCameron Grant 	free(mtx, M_DEVBUF);
9437209180SCameron Grant #endif
9537209180SCameron Grant }
9637209180SCameron Grant 
9737209180SCameron Grant void
9837209180SCameron Grant snd_mtxassert(void *m)
9937209180SCameron Grant {
10037209180SCameron Grant #ifdef USING_MUTEX
101f00f162aSCameron Grant #ifdef INVARIANTS
10237209180SCameron Grant 	struct mtx *mtx = m;
10337209180SCameron Grant 
10437209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
10537209180SCameron Grant #endif
106f00f162aSCameron Grant #endif
10737209180SCameron Grant }
10867beb5a5SCameron Grant /*
10937209180SCameron Grant void
11037209180SCameron Grant snd_mtxlock(void *m)
11137209180SCameron Grant {
11237209180SCameron Grant #ifdef USING_MUTEX
11337209180SCameron Grant 	struct mtx *mtx = m;
11437209180SCameron Grant 
11537209180SCameron Grant 	mtx_lock(mtx);
11637209180SCameron Grant #endif
11737209180SCameron Grant }
11837209180SCameron Grant 
11937209180SCameron Grant void
12037209180SCameron Grant snd_mtxunlock(void *m)
12137209180SCameron Grant {
12237209180SCameron Grant #ifdef USING_MUTEX
12337209180SCameron Grant 	struct mtx *mtx = m;
12437209180SCameron Grant 
12537209180SCameron Grant 	mtx_unlock(mtx);
12637209180SCameron Grant #endif
12737209180SCameron Grant }
12867beb5a5SCameron Grant */
12937209180SCameron Grant int
13037209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
13137209180SCameron Grant {
13237209180SCameron Grant #ifdef USING_MUTEX
13337209180SCameron Grant 	flags &= INTR_MPSAFE;
13446700f12SPeter Wemm 	flags |= INTR_TYPE_AV;
13537209180SCameron Grant #else
13646700f12SPeter Wemm 	flags = INTR_TYPE_AV;
13737209180SCameron Grant #endif
13837209180SCameron Grant 	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
13937209180SCameron Grant }
14037209180SCameron Grant 
141a527dbc7SCameron Grant #ifndef	PCM_DEBUG_MTX
14267b1dce3SCameron Grant void
14367b1dce3SCameron Grant pcm_lock(struct snddev_info *d)
14467b1dce3SCameron Grant {
14567b1dce3SCameron Grant 	snd_mtxlock(d->lock);
14667b1dce3SCameron Grant }
14767b1dce3SCameron Grant 
14867b1dce3SCameron Grant void
14967b1dce3SCameron Grant pcm_unlock(struct snddev_info *d)
15067b1dce3SCameron Grant {
15167b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
15267b1dce3SCameron Grant }
153a527dbc7SCameron Grant #endif
15467b1dce3SCameron Grant 
15567b1dce3SCameron Grant struct pcm_channel *
15667b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d)
15767b1dce3SCameron Grant {
15867b1dce3SCameron Grant 	return d->fakechan;
15967b1dce3SCameron Grant }
16067b1dce3SCameron Grant 
1613fdb3676SAriff Abdullah /* return error status and a locked channel */
1623fdb3676SAriff Abdullah int
1633fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
1643fdb3676SAriff Abdullah 		pid_t pid, int chnum)
165285648f9SCameron Grant {
166285648f9SCameron Grant 	struct pcm_channel *c;
167285648f9SCameron Grant     	struct snddev_channel *sce;
1683fdb3676SAriff Abdullah 	int err, ret;
169285648f9SCameron Grant 
1703fdb3676SAriff Abdullah retry_chnalloc:
1713fdb3676SAriff Abdullah 	ret = ENODEV;
172f637a36cSCameron Grant 	/* scan for a free channel */
173285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
174285648f9SCameron Grant 		c = sce->channel;
17549c5e6e2SCameron Grant 		CHN_LOCK(c);
176285648f9SCameron Grant 		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
1773fdb3676SAriff Abdullah 			if (chnum < 0 || sce->chan_num == chnum) {
178285648f9SCameron Grant 				c->flags |= CHN_F_BUSY;
179b8f0d9e0SCameron Grant 				c->pid = pid;
1803fdb3676SAriff Abdullah 				*ch = c;
1813fdb3676SAriff Abdullah 				return 0;
182285648f9SCameron Grant 			}
183506a5308SCameron Grant 		}
1843fdb3676SAriff Abdullah 		if (sce->chan_num == chnum) {
1853fdb3676SAriff Abdullah 			if (c->direction != direction)
1863fdb3676SAriff Abdullah 				err = EOPNOTSUPP;
1873fdb3676SAriff Abdullah 			else if (c->flags & CHN_F_BUSY)
1883fdb3676SAriff Abdullah 				err = EBUSY;
1893fdb3676SAriff Abdullah 			else
1903fdb3676SAriff Abdullah 				err = EINVAL;
1913fdb3676SAriff Abdullah 			CHN_UNLOCK(c);
1923fdb3676SAriff Abdullah 			return err;
1933fdb3676SAriff Abdullah 		} else if (c->direction == direction && (c->flags & CHN_F_BUSY))
1943fdb3676SAriff Abdullah 			ret = EBUSY;
19549c5e6e2SCameron Grant 		CHN_UNLOCK(c);
196285648f9SCameron Grant 	}
197f637a36cSCameron Grant 
198f637a36cSCameron Grant 	/* no channel available */
1993fdb3676SAriff Abdullah 	if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 &&
2007982e7a4SAriff Abdullah 			d->vchancount < snd_maxautovchans &&
2017982e7a4SAriff Abdullah 			d->devcount <= PCMMAXCHAN) {
202f637a36cSCameron Grant 		/* try to create a vchan */
203f637a36cSCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
204f637a36cSCameron Grant 			c = sce->channel;
20512e524a2SDon Lewis 			CHN_LOCK(c);
20697d69a96SAlexander Leidinger 			if ((c->flags & CHN_F_HAS_VCHAN) &&
20797d69a96SAlexander Leidinger 					!SLIST_EMPTY(&c->children)) {
208f637a36cSCameron Grant 				err = vchan_create(c);
20912e524a2SDon Lewis 				CHN_UNLOCK(c);
2103fdb3676SAriff Abdullah 				if (!err) {
2113fdb3676SAriff Abdullah 					chnum = -2;
2123fdb3676SAriff Abdullah 					goto retry_chnalloc;
2133fdb3676SAriff Abdullah 				} else
2143fdb3676SAriff Abdullah 					device_printf(d->dev, "%s: vchan_create(%s) == %d\n", __func__, c->name, err);
21512e524a2SDon Lewis 			} else
21612e524a2SDon Lewis 				CHN_UNLOCK(c);
217f637a36cSCameron Grant 		}
218f637a36cSCameron Grant 	}
219f637a36cSCameron Grant 
2203fdb3676SAriff Abdullah 	return ret;
221285648f9SCameron Grant }
222285648f9SCameron Grant 
223b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
224285648f9SCameron Grant int
225b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
226285648f9SCameron Grant {
227b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
228285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
229b8f0d9e0SCameron Grant 	c->pid = -1;
23049c5e6e2SCameron Grant 	CHN_UNLOCK(c);
231285648f9SCameron Grant 	return 0;
232285648f9SCameron Grant }
233285648f9SCameron Grant 
234285648f9SCameron Grant int
235285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
236285648f9SCameron Grant {
23749c5e6e2SCameron Grant 	int r;
23849c5e6e2SCameron Grant 
239b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
240285648f9SCameron Grant 	c->refcount += ref;
24149c5e6e2SCameron Grant 	r = c->refcount;
24249c5e6e2SCameron Grant 	return r;
243285648f9SCameron Grant }
244285648f9SCameron Grant 
24567b1dce3SCameron Grant int
24667b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta)
24767b1dce3SCameron Grant {
248a527dbc7SCameron Grant 	int r;
249a527dbc7SCameron Grant 
250a527dbc7SCameron Grant 	if (delta == 0)
25167b1dce3SCameron Grant 		return d->inprog;
252a527dbc7SCameron Grant 
253a527dbc7SCameron Grant 	/* backtrace(); */
254a527dbc7SCameron Grant 	pcm_lock(d);
255a527dbc7SCameron Grant 	d->inprog += delta;
256a527dbc7SCameron Grant 	r = d->inprog;
257a527dbc7SCameron Grant 	pcm_unlock(d);
258a527dbc7SCameron Grant 	return r;
25967b1dce3SCameron Grant }
26067b1dce3SCameron Grant 
26167b1dce3SCameron Grant static void
26267b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num)
26367b1dce3SCameron Grant {
26497d69a96SAlexander Leidinger 	struct pcm_channel *c, *ch;
26567b1dce3SCameron Grant     	struct snddev_channel *sce;
26667b1dce3SCameron Grant 	int err, done;
26767b1dce3SCameron Grant 
26897d69a96SAlexander Leidinger 	/*
26997d69a96SAlexander Leidinger 	 * XXX WOAH... NEED SUPER CLEANUP!!!
27097d69a96SAlexander Leidinger 	 * Robust, yet confusing. Understanding these will
27197d69a96SAlexander Leidinger 	 * cause your brain spinning like a Doki Doki Dynamo.
27297d69a96SAlexander Leidinger 	 */
27367b1dce3SCameron Grant 	if (num > 0 && d->vchancount == 0) {
27467b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
27567b1dce3SCameron Grant 			c = sce->channel;
27612e524a2SDon Lewis 			CHN_LOCK(c);
27797d69a96SAlexander Leidinger 			if ((c->direction == PCMDIR_PLAY) &&
2783fdb3676SAriff Abdullah 					(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == 0 &&
27997d69a96SAlexander Leidinger 					SLIST_EMPTY(&c->children)) {
28067b1dce3SCameron Grant 				c->flags |= CHN_F_BUSY;
28167b1dce3SCameron Grant 				err = vchan_create(c);
28267b1dce3SCameron Grant 				if (err) {
28367b1dce3SCameron Grant 					c->flags &= ~CHN_F_BUSY;
2843fdb3676SAriff Abdullah 					device_printf(d->dev, "%s: vchan_create(%s) == %d\n", __func__, c->name, err);
2853fdb3676SAriff Abdullah 				} else {
28612e524a2SDon Lewis 					CHN_UNLOCK(c);
28767b1dce3SCameron Grant 					return;
28867b1dce3SCameron Grant 				}
2893fdb3676SAriff Abdullah 			}
29012e524a2SDon Lewis 			CHN_UNLOCK(c);
29167b1dce3SCameron Grant 		}
29297d69a96SAlexander Leidinger 		return;
29367b1dce3SCameron Grant 	}
29467b1dce3SCameron Grant 	if (num == 0 && d->vchancount > 0) {
29597d69a96SAlexander Leidinger 		/*
29697d69a96SAlexander Leidinger 		 * XXX Keep retrying...
29797d69a96SAlexander Leidinger 		 */
29897d69a96SAlexander Leidinger 		for (done = 0; done < 1024; done++) {
29997d69a96SAlexander Leidinger 			ch = NULL;
30067b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
30167b1dce3SCameron Grant 				c = sce->channel;
30297d69a96SAlexander Leidinger 				CHN_LOCK(c);
30397d69a96SAlexander Leidinger 				if (c->direction == PCMDIR_PLAY &&
30497d69a96SAlexander Leidinger 						!(c->flags & CHN_F_BUSY) &&
30597d69a96SAlexander Leidinger 						(c->flags & CHN_F_VIRTUAL)) {
30697d69a96SAlexander Leidinger 					ch = c;
30797d69a96SAlexander Leidinger 					break;
30897d69a96SAlexander Leidinger 				}
30997d69a96SAlexander Leidinger 				CHN_UNLOCK(c);
31097d69a96SAlexander Leidinger 			}
31197d69a96SAlexander Leidinger 			if (ch != NULL) {
31297d69a96SAlexander Leidinger 				CHN_UNLOCK(ch);
313a527dbc7SCameron Grant 				snd_mtxlock(d->lock);
31497d69a96SAlexander Leidinger 				err = vchan_destroy(ch);
31567b1dce3SCameron Grant 				if (err)
31697d69a96SAlexander Leidinger 					device_printf(d->dev, "vchan_destroy(%s) == %d\n",
31797d69a96SAlexander Leidinger 								ch->name, err);
31897d69a96SAlexander Leidinger 				snd_mtxunlock(d->lock);
31997d69a96SAlexander Leidinger 			} else
32097d69a96SAlexander Leidinger 				return;
32167b1dce3SCameron Grant 		}
32297d69a96SAlexander Leidinger 		return;
32367b1dce3SCameron Grant 	}
32467b1dce3SCameron Grant }
32567b1dce3SCameron Grant 
32682db23e2SCameron Grant #ifdef USING_DEVFS
32733dbf14aSCameron Grant static int
328cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
32933dbf14aSCameron Grant {
330b8f0d9e0SCameron Grant 	struct snddev_info *d;
33133dbf14aSCameron Grant 	int error, unit;
33233dbf14aSCameron Grant 
33333dbf14aSCameron Grant 	unit = snd_unit;
33433dbf14aSCameron Grant 	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
33533dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
33674ffd138SCameron Grant 		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
337b8f0d9e0SCameron Grant 			return EINVAL;
338b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
339faeebea2SCameron Grant 		if (d == NULL || SLIST_EMPTY(&d->channels))
340b8f0d9e0SCameron Grant 			return EINVAL;
34133dbf14aSCameron Grant 		snd_unit = unit;
34233dbf14aSCameron Grant 	}
34333dbf14aSCameron Grant 	return (error);
34433dbf14aSCameron Grant }
345b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
346cd9766c5SCameron Grant             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
34782db23e2SCameron Grant #endif
348987e5972SCameron Grant 
349cd9766c5SCameron Grant static int
35067b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
351cd9766c5SCameron Grant {
35267b1dce3SCameron Grant 	struct snddev_info *d;
35367b1dce3SCameron Grant 	int i, v, error;
354cd9766c5SCameron Grant 
35567b1dce3SCameron Grant 	v = snd_maxautovchans;
356cd9766c5SCameron Grant 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
357cd9766c5SCameron Grant 	if (error == 0 && req->newptr != NULL) {
3583fdb3676SAriff Abdullah 		if (v < 0 || v > (PCMMAXCHAN + 1))
359cd9766c5SCameron Grant 			return EINVAL;
3603fdb3676SAriff Abdullah 		if (pcm_devclass != NULL && v != snd_maxautovchans) {
36167b1dce3SCameron Grant 			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
36267b1dce3SCameron Grant 				d = devclass_get_softc(pcm_devclass, i);
36367b1dce3SCameron Grant 				if (!d)
36467b1dce3SCameron Grant 					continue;
36597d69a96SAlexander Leidinger 				if (d->flags & SD_F_AUTOVCHAN) {
36697d69a96SAlexander Leidinger 					if (pcm_inprog(d, 1) == 1)
36767b1dce3SCameron Grant 						pcm_setmaxautovchans(d, v);
36897d69a96SAlexander Leidinger 					pcm_inprog(d, -1);
36997d69a96SAlexander Leidinger 				}
37067b1dce3SCameron Grant 			}
37167b1dce3SCameron Grant 		}
37267b1dce3SCameron Grant 		snd_maxautovchans = v;
373cd9766c5SCameron Grant 	}
374cd9766c5SCameron Grant 	return (error);
375cd9766c5SCameron Grant }
37667b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
37767b1dce3SCameron Grant             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
378f637a36cSCameron Grant 
379285648f9SCameron Grant struct pcm_channel *
380285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
381987e5972SCameron Grant {
3823fdb3676SAriff Abdullah 	struct snddev_channel *sce;
3833fdb3676SAriff Abdullah 	struct pcm_channel *ch, *tmpch;
38433dbf14aSCameron Grant 	char *dirs;
3853fdb3676SAriff Abdullah 	u_int32_t flsearch = 0;
3863fdb3676SAriff Abdullah 	int direction, err, rpnum, *pnum;
387987e5972SCameron Grant 
388285648f9SCameron Grant 	switch(dir) {
389285648f9SCameron Grant 	case PCMDIR_PLAY:
390285648f9SCameron Grant 		dirs = "play";
391a3193a9cSDon Lewis 		direction = PCMDIR_PLAY;
392a67fe5c1SCameron Grant 		pnum = &d->playcount;
393285648f9SCameron Grant 		break;
394a67fe5c1SCameron Grant 
395285648f9SCameron Grant 	case PCMDIR_REC:
396285648f9SCameron Grant 		dirs = "record";
397a3193a9cSDon Lewis 		direction = PCMDIR_REC;
398a67fe5c1SCameron Grant 		pnum = &d->reccount;
399285648f9SCameron Grant 		break;
400a67fe5c1SCameron Grant 
401285648f9SCameron Grant 	case PCMDIR_VIRTUAL:
402285648f9SCameron Grant 		dirs = "virtual";
403a3193a9cSDon Lewis 		direction = PCMDIR_PLAY;
404a67fe5c1SCameron Grant 		pnum = &d->vchancount;
4053fdb3676SAriff Abdullah 		flsearch = CHN_F_VIRTUAL;
406285648f9SCameron Grant 		break;
407a67fe5c1SCameron Grant 
408285648f9SCameron Grant 	default:
409285648f9SCameron Grant 		return NULL;
4109c326820SCameron Grant 	}
411285648f9SCameron Grant 
412a163d034SWarner Losh 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
413285648f9SCameron Grant 	if (!ch)
414285648f9SCameron Grant 		return NULL;
415285648f9SCameron Grant 
416a163d034SWarner Losh 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
417285648f9SCameron Grant 	if (!ch->methods) {
418285648f9SCameron Grant 		free(ch, M_DEVBUF);
419a67fe5c1SCameron Grant 
420285648f9SCameron Grant 		return NULL;
421285648f9SCameron Grant 	}
422285648f9SCameron Grant 
42367beb5a5SCameron Grant 	snd_mtxlock(d->lock);
4243fdb3676SAriff Abdullah 	ch->num = 0;
4253fdb3676SAriff Abdullah 	rpnum = 0;
4263fdb3676SAriff Abdullah 	SLIST_FOREACH(sce, &d->channels, link) {
4273fdb3676SAriff Abdullah 		if (sce == NULL || sce->channel == NULL)
4283fdb3676SAriff Abdullah 			continue;
4293fdb3676SAriff Abdullah 		tmpch = sce->channel;
4303fdb3676SAriff Abdullah 		if (direction != tmpch->direction ||
4313fdb3676SAriff Abdullah 				(tmpch->flags & CHN_F_VIRTUAL) != flsearch)
4323fdb3676SAriff Abdullah 			continue;
4333fdb3676SAriff Abdullah 		if (ch->num == tmpch->num)
4343fdb3676SAriff Abdullah 			ch->num++;
4353fdb3676SAriff Abdullah 		else {
4363fdb3676SAriff Abdullah 			/*
4373fdb3676SAriff Abdullah 			 * Channel numbering screwed. Bail out, and do the
4383fdb3676SAriff Abdullah 			 * hard way lookup.
4393fdb3676SAriff Abdullah 			 */
4403fdb3676SAriff Abdullah 			device_printf(d->dev,
4413fdb3676SAriff Abdullah 				"%s: channel numbering dirs=%s screwed.\n",
4423fdb3676SAriff Abdullah 				__func__, dirs);
4433fdb3676SAriff Abdullah 			ch->num = 0;
4443fdb3676SAriff Abdullah 			goto retry_num_search;
4453fdb3676SAriff Abdullah 		}
4463fdb3676SAriff Abdullah 		rpnum++;
4473fdb3676SAriff Abdullah 	}
4483fdb3676SAriff Abdullah 	goto retry_num_search_out;
4493fdb3676SAriff Abdullah retry_num_search:
4503fdb3676SAriff Abdullah 	rpnum = 0;
4513fdb3676SAriff Abdullah 	SLIST_FOREACH(sce, &d->channels, link) {
4523fdb3676SAriff Abdullah 		if (sce == NULL || sce->channel == NULL)
4533fdb3676SAriff Abdullah 			continue;
4543fdb3676SAriff Abdullah 		tmpch = sce->channel;
4553fdb3676SAriff Abdullah 		if (direction != tmpch->direction ||
4563fdb3676SAriff Abdullah 				(tmpch->flags & CHN_F_VIRTUAL) != flsearch)
4573fdb3676SAriff Abdullah 			continue;
4583fdb3676SAriff Abdullah 		if (ch->num == tmpch->num) {
4593fdb3676SAriff Abdullah 			ch->num++;
4603fdb3676SAriff Abdullah 			goto retry_num_search;
4613fdb3676SAriff Abdullah 		}
4623fdb3676SAriff Abdullah 		rpnum++;
4633fdb3676SAriff Abdullah 	}
4643fdb3676SAriff Abdullah retry_num_search_out:
4653fdb3676SAriff Abdullah 	if (*pnum != rpnum) {
4663fdb3676SAriff Abdullah 		device_printf(d->dev,
4673fdb3676SAriff Abdullah 			"%s: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
4683fdb3676SAriff Abdullah 			__func__, dirs, *pnum, rpnum);
4693fdb3676SAriff Abdullah 		*pnum = rpnum;
4703fdb3676SAriff Abdullah 	}
4713fdb3676SAriff Abdullah 	(*pnum)++;
47267beb5a5SCameron Grant 	snd_mtxunlock(d->lock);
473a67fe5c1SCameron Grant 
474285648f9SCameron Grant 	ch->pid = -1;
475285648f9SCameron Grant 	ch->parentsnddev = d;
476285648f9SCameron Grant 	ch->parentchannel = parent;
477436c9b65SScott Long 	ch->dev = d->dev;
4783fdb3676SAriff Abdullah 	snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
479285648f9SCameron Grant 
480a3193a9cSDon Lewis 	err = chn_init(ch, devinfo, dir, direction);
4810f55ac6cSCameron Grant 	if (err) {
482a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
483285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
484285648f9SCameron Grant 		free(ch, M_DEVBUF);
48567beb5a5SCameron Grant 		snd_mtxlock(d->lock);
486a67fe5c1SCameron Grant 		(*pnum)--;
48767beb5a5SCameron Grant 		snd_mtxunlock(d->lock);
488a67fe5c1SCameron Grant 
489285648f9SCameron Grant 		return NULL;
490bbb5bf3dSCameron Grant 	}
491285648f9SCameron Grant 
492285648f9SCameron Grant 	return ch;
493285648f9SCameron Grant }
494285648f9SCameron Grant 
495285648f9SCameron Grant int
496285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
497285648f9SCameron Grant {
498a67fe5c1SCameron Grant 	struct snddev_info *d;
499285648f9SCameron Grant 	int err;
500285648f9SCameron Grant 
501a67fe5c1SCameron Grant 	d = ch->parentsnddev;
502285648f9SCameron Grant 	err = chn_kill(ch);
503285648f9SCameron Grant 	if (err) {
504a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
505285648f9SCameron Grant 		return err;
506285648f9SCameron Grant 	}
507285648f9SCameron Grant 
508285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
509285648f9SCameron Grant 	free(ch, M_DEVBUF);
510285648f9SCameron Grant 
511285648f9SCameron Grant 	return 0;
512285648f9SCameron Grant }
513285648f9SCameron Grant 
514285648f9SCameron Grant int
5155ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
516285648f9SCameron Grant {
51767b1dce3SCameron Grant     	struct snddev_channel *sce, *tmp, *after;
5183fdb3676SAriff Abdullah 	unsigned rdevcount;
5195ee30e27SMathew Kanner     	int device = device_get_unit(d->dev);
5203fdb3676SAriff Abdullah 	int stop;
5213fdb3676SAriff Abdullah 	size_t namelen;
5225ee30e27SMathew Kanner 
5235ee30e27SMathew Kanner 	/*
5245ee30e27SMathew Kanner 	 * Note it's confusing nomenclature.
5255ee30e27SMathew Kanner 	 * dev_t
5265ee30e27SMathew Kanner 	 * device -> pcm_device
5275ee30e27SMathew Kanner          * unit -> pcm_channel
5285ee30e27SMathew Kanner 	 * channel -> snddev_channel
5295ee30e27SMathew Kanner 	 * device_t
5305ee30e27SMathew Kanner 	 * unit -> pcm_device
5315ee30e27SMathew Kanner 	 */
532b8f0d9e0SCameron Grant 
533a163d034SWarner Losh 	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
534285648f9SCameron Grant 	if (!sce) {
535285648f9SCameron Grant 		return ENOMEM;
536285648f9SCameron Grant 	}
537285648f9SCameron Grant 
5387cf0e77aSOrion Hodson 	snd_mtxlock(d->lock);
539285648f9SCameron Grant 	sce->channel = ch;
5407982e7a4SAriff Abdullah 	sce->chan_num = 0;
5413fdb3676SAriff Abdullah 	after = NULL;
5423fdb3676SAriff Abdullah 	stop = 0;
5433fdb3676SAriff Abdullah retry_chan_num_search:
5443fdb3676SAriff Abdullah 	/*
5453fdb3676SAriff Abdullah 	 * Look for possible channel numbering collision. This may not
5463fdb3676SAriff Abdullah 	 * be optimized, but it will ensure that no collision occured.
5473fdb3676SAriff Abdullah 	 * Creating maximum possible channels (256 channels) will cost
5483fdb3676SAriff Abdullah 	 * us at most 32895 cycles, but this can be considered cheap
5493fdb3676SAriff Abdullah 	 * since none of the locking/unlocking operations involved.
5503fdb3676SAriff Abdullah 	 *
5513fdb3676SAriff Abdullah 	 * Micro optimization, channel ordering:
5523fdb3676SAriff Abdullah 	 * hw,hw,hw,vch,vch,vch,rec
5533fdb3676SAriff Abdullah 	 */
5543fdb3676SAriff Abdullah 	rdevcount = 0;
5557982e7a4SAriff Abdullah 	SLIST_FOREACH(tmp, &d->channels, link) {
5563fdb3676SAriff Abdullah 		if (tmp == NULL || tmp->channel == NULL)
5577982e7a4SAriff Abdullah 			continue;
5587982e7a4SAriff Abdullah 		if (sce->chan_num == tmp->chan_num) {
5597982e7a4SAriff Abdullah 			sce->chan_num++;
5603fdb3676SAriff Abdullah 			after = NULL;
5613fdb3676SAriff Abdullah 			stop = 0;
5623fdb3676SAriff Abdullah 			goto retry_chan_num_search;
5637982e7a4SAriff Abdullah 		}
5643fdb3676SAriff Abdullah 		if (stop == 0) {
5653fdb3676SAriff Abdullah 			if (ch->flags & CHN_F_VIRTUAL) {
5663fdb3676SAriff Abdullah 				if (tmp->channel->direction == PCMDIR_REC)
5673fdb3676SAriff Abdullah 					stop = 1;
5683fdb3676SAriff Abdullah 				else
5693fdb3676SAriff Abdullah 					after = tmp;
5703fdb3676SAriff Abdullah 			} else if (ch->direction == PCMDIR_REC) {
5713fdb3676SAriff Abdullah 				after = tmp;
5723fdb3676SAriff Abdullah 			} else {
5733fdb3676SAriff Abdullah 				if (tmp->channel->direction != PCMDIR_PLAY ||
5743fdb3676SAriff Abdullah 						(tmp->channel->flags & CHN_F_VIRTUAL)) {
5753fdb3676SAriff Abdullah 					stop = 1;
5763fdb3676SAriff Abdullah 				} else {
5773fdb3676SAriff Abdullah 					after = tmp;
5783fdb3676SAriff Abdullah 				}
5793fdb3676SAriff Abdullah 			}
5803fdb3676SAriff Abdullah 		}
5813fdb3676SAriff Abdullah 		rdevcount++;
5827982e7a4SAriff Abdullah 	}
5837982e7a4SAriff Abdullah 	/*
5847982e7a4SAriff Abdullah 	 * Don't overflow PCMMKMINOR / PCMMAXCHAN.
5857982e7a4SAriff Abdullah 	 */
5867982e7a4SAriff Abdullah 	if (sce->chan_num > PCMMAXCHAN) {
5877982e7a4SAriff Abdullah 		snd_mtxunlock(d->lock);
5887982e7a4SAriff Abdullah 		device_printf(d->dev,
5897982e7a4SAriff Abdullah 			"%s: WARNING: sce->chan_num overflow! (%d)\n",
5907982e7a4SAriff Abdullah 			__func__, sce->chan_num);
5917982e7a4SAriff Abdullah 		free(sce, M_DEVBUF);
5927982e7a4SAriff Abdullah 		return E2BIG;
5937982e7a4SAriff Abdullah 	}
5943fdb3676SAriff Abdullah 	if (d->devcount != rdevcount) {
5953fdb3676SAriff Abdullah 		device_printf(d->dev,
5963fdb3676SAriff Abdullah 			"%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n",
5973fdb3676SAriff Abdullah 			__func__, d->devcount, rdevcount);
5983fdb3676SAriff Abdullah 		d->devcount = rdevcount;
59997d69a96SAlexander Leidinger 	}
6003fdb3676SAriff Abdullah 	d->devcount++;
60197d69a96SAlexander Leidinger 	if (after == NULL) {
60297d69a96SAlexander Leidinger 		SLIST_INSERT_HEAD(&d->channels, sce, link);
60397d69a96SAlexander Leidinger 	} else {
60467b1dce3SCameron Grant 		SLIST_INSERT_AFTER(after, sce, link);
60567b1dce3SCameron Grant 	}
6063fdb3676SAriff Abdullah 
6073fdb3676SAriff Abdullah 	namelen = strlen(ch->name);
6083fdb3676SAriff Abdullah 	if ((CHN_NAMELEN - namelen) > 10) {	/* ":dspXX.YYY" */
6093fdb3676SAriff Abdullah 		snprintf(ch->name + namelen,
6103fdb3676SAriff Abdullah 			CHN_NAMELEN - namelen, ":dsp%d.%d",
6113fdb3676SAriff Abdullah 			device, sce->chan_num);
61297d69a96SAlexander Leidinger 	}
61367beb5a5SCameron Grant 	snd_mtxunlock(d->lock);
6145ee30e27SMathew Kanner 	sce->dsp_devt = make_dev(&dsp_cdevsw,
6155ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
6165ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
6175ee30e27SMathew Kanner 			device, sce->chan_num);
618285648f9SCameron Grant 
6195ee30e27SMathew Kanner 	sce->dspW_devt = make_dev(&dsp_cdevsw,
6205ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
6215ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
6225ee30e27SMathew Kanner 			device, sce->chan_num);
6235ee30e27SMathew Kanner 
6245ee30e27SMathew Kanner 	sce->audio_devt = make_dev(&dsp_cdevsw,
6255ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
6265ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
6275ee30e27SMathew Kanner 			device, sce->chan_num);
6285ee30e27SMathew Kanner 
629506a5308SCameron Grant 	if (ch->direction == PCMDIR_REC)
6305ee30e27SMathew Kanner 		sce->dspr_devt = make_dev(&dsp_cdevsw,
6315ee30e27SMathew Kanner 				PCMMKMINOR(device, SND_DEV_DSPREC,
6325ee30e27SMathew Kanner 					sce->chan_num), UID_ROOT, GID_WHEEL,
6335ee30e27SMathew Kanner 				0666, "dspr%d.%d", device, sce->chan_num);
634b8f0d9e0SCameron Grant 
63533dbf14aSCameron Grant 	return 0;
63633dbf14aSCameron Grant }
63733dbf14aSCameron Grant 
638285648f9SCameron Grant int
6395ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
64033dbf14aSCameron Grant {
641285648f9SCameron Grant     	struct snddev_channel *sce;
64245550658SPoul-Henning Kamp #if 0
643a527dbc7SCameron Grant 	int ourlock;
64433dbf14aSCameron Grant 
645a527dbc7SCameron Grant 	ourlock = 0;
646a527dbc7SCameron Grant 	if (!mtx_owned(d->lock)) {
64749c5e6e2SCameron Grant 		snd_mtxlock(d->lock);
648a527dbc7SCameron Grant 		ourlock = 1;
649a527dbc7SCameron Grant 	}
65045550658SPoul-Henning Kamp #endif
651a527dbc7SCameron Grant 
652285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
653285648f9SCameron Grant 		if (sce->channel == ch)
654285648f9SCameron Grant 			goto gotit;
65533dbf14aSCameron Grant 	}
65645550658SPoul-Henning Kamp #if 0
657a527dbc7SCameron Grant 	if (ourlock)
65849c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
65945550658SPoul-Henning Kamp #endif
660285648f9SCameron Grant 	return EINVAL;
661285648f9SCameron Grant gotit:
662285648f9SCameron Grant 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
66367beb5a5SCameron Grant 
66497d69a96SAlexander Leidinger 	if (ch->flags & CHN_F_VIRTUAL)
66567beb5a5SCameron Grant 		d->vchancount--;
66697d69a96SAlexander Leidinger 	else if (ch->direction == PCMDIR_REC)
66797d69a96SAlexander Leidinger 		d->reccount--;
66867beb5a5SCameron Grant 	else
66967beb5a5SCameron Grant 		d->playcount--;
67067beb5a5SCameron Grant 
67145550658SPoul-Henning Kamp #if 0
672a527dbc7SCameron Grant 	if (ourlock)
67349c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
67445550658SPoul-Henning Kamp #endif
67567beb5a5SCameron Grant 	free(sce, M_DEVBUF);
676285648f9SCameron Grant 
677987e5972SCameron Grant 	return 0;
678987e5972SCameron Grant }
679987e5972SCameron Grant 
680987e5972SCameron Grant int
681285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
682285648f9SCameron Grant {
683285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
68467b1dce3SCameron Grant 	struct pcm_channel *ch;
68567b1dce3SCameron Grant     	int err;
686285648f9SCameron Grant 
687285648f9SCameron Grant 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
688285648f9SCameron Grant 	if (!ch) {
689285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
690285648f9SCameron Grant 		return ENODEV;
691285648f9SCameron Grant 	}
692cd9766c5SCameron Grant 
6935ee30e27SMathew Kanner 	err = pcm_chn_add(d, ch);
694285648f9SCameron Grant 	if (err) {
695285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
696285648f9SCameron Grant 		pcm_chn_destroy(ch);
697cd9766c5SCameron Grant 		return err;
698cd9766c5SCameron Grant 	}
699cd9766c5SCameron Grant 
700285648f9SCameron Grant 	return err;
701285648f9SCameron Grant }
702285648f9SCameron Grant 
703285648f9SCameron Grant static int
704285648f9SCameron Grant pcm_killchan(device_t dev)
705285648f9SCameron Grant {
706285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
707285648f9SCameron Grant     	struct snddev_channel *sce;
708e33bee07SOlivier Houchard 	struct pcm_channel *ch;
709e33bee07SOlivier Houchard 	int error = 0;
710285648f9SCameron Grant 
711285648f9SCameron Grant 	sce = SLIST_FIRST(&d->channels);
712e33bee07SOlivier Houchard 	ch = sce->channel;
713285648f9SCameron Grant 
7145ee30e27SMathew Kanner 	error = pcm_chn_remove(d, sce->channel);
715e33bee07SOlivier Houchard 	if (error)
716e33bee07SOlivier Houchard 		return (error);
717e33bee07SOlivier Houchard 	return (pcm_chn_destroy(ch));
718285648f9SCameron Grant }
719285648f9SCameron Grant 
720285648f9SCameron Grant int
721987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
722987e5972SCameron Grant {
72366ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
7243fdb3676SAriff Abdullah 	struct snddev_channel *sce;
7253fdb3676SAriff Abdullah 	struct pcm_channel *ch;
7263fdb3676SAriff Abdullah 	int err;
72749c5e6e2SCameron Grant 
72849c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
729987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
73049c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
7313fdb3676SAriff Abdullah 	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
7323fdb3676SAriff Abdullah 			d->vchancount == 0) {
7333fdb3676SAriff Abdullah 		SLIST_FOREACH(sce, &d->channels, link) {
7343fdb3676SAriff Abdullah 			if (sce == NULL || sce->channel == NULL)
7353fdb3676SAriff Abdullah 				continue;
7363fdb3676SAriff Abdullah 			ch = sce->channel;
7373fdb3676SAriff Abdullah 			CHN_LOCK(ch);
7383fdb3676SAriff Abdullah 			if (ch->direction == PCMDIR_PLAY &&
7393fdb3676SAriff Abdullah 					(ch->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == 0 &&
7403fdb3676SAriff Abdullah 					SLIST_EMPTY(&ch->children)) {
7413fdb3676SAriff Abdullah 				ch->flags |= CHN_F_BUSY;
7423fdb3676SAriff Abdullah 				err = vchan_create(ch);
7433fdb3676SAriff Abdullah 				if (err) {
7443fdb3676SAriff Abdullah 					ch->flags &= ~CHN_F_BUSY;
7453fdb3676SAriff Abdullah 					device_printf(d->dev, "%s: vchan_create(%s) == %d\n",
7463fdb3676SAriff Abdullah 						__func__, ch->name, err);
7473fdb3676SAriff Abdullah 				} else {
7483fdb3676SAriff Abdullah 					CHN_UNLOCK(ch);
7493fdb3676SAriff Abdullah 					return 0;
7503fdb3676SAriff Abdullah 				}
7513fdb3676SAriff Abdullah 			}
7523fdb3676SAriff Abdullah 			CHN_UNLOCK(ch);
7533fdb3676SAriff Abdullah 		}
7543fdb3676SAriff Abdullah 	}
755987e5972SCameron Grant 	return 0;
756987e5972SCameron Grant }
757987e5972SCameron Grant 
758987e5972SCameron Grant u_int32_t
759987e5972SCameron Grant pcm_getflags(device_t dev)
760987e5972SCameron Grant {
76166ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
76249c5e6e2SCameron Grant 
763987e5972SCameron Grant 	return d->flags;
764987e5972SCameron Grant }
765987e5972SCameron Grant 
766987e5972SCameron Grant void
767987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
768987e5972SCameron Grant {
76966ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
770d95502a8SCameron Grant 
771987e5972SCameron Grant 	d->flags = val;
772987e5972SCameron Grant }
773987e5972SCameron Grant 
77439004e69SCameron Grant void *
77539004e69SCameron Grant pcm_getdevinfo(device_t dev)
77639004e69SCameron Grant {
77766ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
77849c5e6e2SCameron Grant 
77939004e69SCameron Grant 	return d->devinfo;
78039004e69SCameron Grant }
78139004e69SCameron Grant 
782a67fe5c1SCameron Grant unsigned int
783a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
784a67fe5c1SCameron Grant {
785a67fe5c1SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
7864e60be34SCameron Grant 	int sz, x;
787a67fe5c1SCameron Grant 
788a67fe5c1SCameron Grant 	sz = 0;
7894e60be34SCameron Grant 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
7904e60be34SCameron Grant 		x = sz;
791a67fe5c1SCameron Grant 		RANGE(sz, min, max);
7924e60be34SCameron Grant 		if (x != sz)
7934e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
7944e60be34SCameron Grant 		x = min;
7954e60be34SCameron Grant 		while (x < sz)
7964e60be34SCameron Grant 			x <<= 1;
7974e60be34SCameron Grant 		if (x > sz)
7984e60be34SCameron Grant 			x >>= 1;
7994e60be34SCameron Grant 		if (x != sz) {
8004e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
8014e60be34SCameron Grant 			sz = x;
8024e60be34SCameron Grant 		}
8034e60be34SCameron Grant 	} else {
804a67fe5c1SCameron Grant 		sz = deflt;
8054e60be34SCameron Grant 	}
8064e60be34SCameron Grant 
807a67fe5c1SCameron Grant 	d->bufsz = sz;
808a67fe5c1SCameron Grant 
809a67fe5c1SCameron Grant 	return sz;
810a67fe5c1SCameron Grant }
811a67fe5c1SCameron Grant 
812987e5972SCameron Grant int
813987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
814987e5972SCameron Grant {
81566ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
816987e5972SCameron Grant 
817b8a36395SCameron Grant 	if (pcm_veto_load) {
818b8a36395SCameron Grant 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
819b8a36395SCameron Grant 
820b8a36395SCameron Grant 		return EINVAL;
821b8a36395SCameron Grant 	}
822b8a36395SCameron Grant 
8232c69ba87SJohn Baldwin 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
824285648f9SCameron Grant 
8257233ababSAlexander Leidinger #if 0
8267233ababSAlexander Leidinger 	/*
8277233ababSAlexander Leidinger 	 * d->flags should be cleared by the allocator of the softc.
8287233ababSAlexander Leidinger 	 * We cannot clear this field here because several devices set
8297233ababSAlexander Leidinger 	 * this flag before calling pcm_register().
8307233ababSAlexander Leidinger 	 */
831cd9766c5SCameron Grant 	d->flags = 0;
8327233ababSAlexander Leidinger #endif
833e4d5b250SCameron Grant 	d->dev = dev;
834987e5972SCameron Grant 	d->devinfo = devinfo;
835f637a36cSCameron Grant 	d->devcount = 0;
836506a5308SCameron Grant 	d->reccount = 0;
837a67fe5c1SCameron Grant 	d->playcount = 0;
838f637a36cSCameron Grant 	d->vchancount = 0;
839d95502a8SCameron Grant 	d->inprog = 0;
840833f7023SCameron Grant 
84145550658SPoul-Henning Kamp 	SLIST_INIT(&d->channels);
84245550658SPoul-Henning Kamp 
843d95502a8SCameron Grant 	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
844285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
845d95502a8SCameron Grant 
846285648f9SCameron Grant 	d->fakechan = fkchan_setup(dev);
847a3193a9cSDon Lewis 	chn_init(d->fakechan, NULL, 0, 0);
848987e5972SCameron Grant 
84982db23e2SCameron Grant #ifdef SND_DYNSYSCTL
850cc486d80SJohn Baldwin 	sysctl_ctx_init(&d->sysctl_tree);
851cc486d80SJohn Baldwin 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
852a3e893e0SJohn Baldwin 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
853cc486d80SJohn Baldwin 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
854a3e893e0SJohn Baldwin 	if (d->sysctl_tree_top == NULL) {
855cc486d80SJohn Baldwin 		sysctl_ctx_free(&d->sysctl_tree);
856cc486d80SJohn Baldwin 		goto no;
857cc486d80SJohn Baldwin 	}
858a67fe5c1SCameron Grant 	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
859a67fe5c1SCameron Grant             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
86082db23e2SCameron Grant #endif
86197d69a96SAlexander Leidinger 	if (numplay > 0) {
862cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
8633fdb3676SAriff Abdullah 		vchan_initsys(dev);
86497d69a96SAlexander Leidinger 	}
865cd9766c5SCameron Grant 
86667b1dce3SCameron Grant 	sndstat_register(dev, d->status, sndstat_prepare_pcm);
867987e5972SCameron Grant     	return 0;
868987e5972SCameron Grant no:
86949c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
870987e5972SCameron Grant 	return ENXIO;
871987e5972SCameron Grant }
872987e5972SCameron Grant 
87333dbf14aSCameron Grant int
87433dbf14aSCameron Grant pcm_unregister(device_t dev)
8757c438dbeSCameron Grant {
87666ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
877285648f9SCameron Grant     	struct snddev_channel *sce;
878a67fe5c1SCameron Grant 	struct pcm_channel *ch;
8797c438dbeSCameron Grant 
88028ef3fb0SAlexander Leidinger 	if (sndstat_acquire() != 0) {
88128ef3fb0SAlexander Leidinger 		device_printf(dev, "unregister: sndstat busy\n");
88228ef3fb0SAlexander Leidinger 		return EBUSY;
88328ef3fb0SAlexander Leidinger 	}
88428ef3fb0SAlexander Leidinger 
88549c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
886d95502a8SCameron Grant 	if (d->inprog) {
8875c25132aSGeorge C A Reid 		device_printf(dev, "unregister: operation in progress\n");
8885c25132aSGeorge C A Reid 		snd_mtxunlock(d->lock);
88928ef3fb0SAlexander Leidinger 		sndstat_release();
8905c25132aSGeorge C A Reid 		return EBUSY;
8915c25132aSGeorge C A Reid 	}
8925ee30e27SMathew Kanner 
893285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
894a67fe5c1SCameron Grant 		ch = sce->channel;
895a67fe5c1SCameron Grant 		if (ch->refcount > 0) {
89621ed9908SCameron Grant 			device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
89749c5e6e2SCameron Grant 			snd_mtxunlock(d->lock);
89828ef3fb0SAlexander Leidinger 			sndstat_release();
899285648f9SCameron Grant 			return EBUSY;
900285648f9SCameron Grant 		}
901c9b53085SCameron Grant 	}
9025ee30e27SMathew Kanner 
9037233ababSAlexander Leidinger 	if (mixer_uninit(dev)) {
9047233ababSAlexander Leidinger 		device_printf(dev, "unregister: mixer busy\n");
9057233ababSAlexander Leidinger 		snd_mtxunlock(d->lock);
90628ef3fb0SAlexander Leidinger 		sndstat_release();
9077233ababSAlexander Leidinger 		return EBUSY;
9087233ababSAlexander Leidinger 	}
9097233ababSAlexander Leidinger 
9105ee30e27SMathew Kanner 	SLIST_FOREACH(sce, &d->channels, link) {
9117982e7a4SAriff Abdullah 		if (sce->dsp_devt) {
9125ee30e27SMathew Kanner 			destroy_dev(sce->dsp_devt);
9137982e7a4SAriff Abdullah 			sce->dsp_devt = NULL;
9147982e7a4SAriff Abdullah 		}
9157982e7a4SAriff Abdullah 		if (sce->dspW_devt) {
9165ee30e27SMathew Kanner 			destroy_dev(sce->dspW_devt);
9177982e7a4SAriff Abdullah 			sce->dspW_devt = NULL;
9187982e7a4SAriff Abdullah 		}
9197982e7a4SAriff Abdullah 		if (sce->audio_devt) {
9205ee30e27SMathew Kanner 			destroy_dev(sce->audio_devt);
9217982e7a4SAriff Abdullah 			sce->audio_devt = NULL;
9227982e7a4SAriff Abdullah 		}
9237982e7a4SAriff Abdullah 		if (sce->dspr_devt) {
9245ee30e27SMathew Kanner 			destroy_dev(sce->dspr_devt);
9257982e7a4SAriff Abdullah 			sce->dspr_devt = NULL;
9267982e7a4SAriff Abdullah 		}
9277982e7a4SAriff Abdullah 		d->devcount--;
9285ee30e27SMathew Kanner 	}
9295ee30e27SMathew Kanner 
93066ef8af5SCameron Grant #ifdef SND_DYNSYSCTL
93166ef8af5SCameron Grant 	d->sysctl_tree_top = NULL;
93266ef8af5SCameron Grant 	sysctl_ctx_free(&d->sysctl_tree);
93366ef8af5SCameron Grant #endif
934faeebea2SCameron Grant 	while (!SLIST_EMPTY(&d->channels))
935285648f9SCameron Grant 		pcm_killchan(dev);
9367c438dbeSCameron Grant 
93766ef8af5SCameron Grant 	chn_kill(d->fakechan);
93866ef8af5SCameron Grant 	fkchan_kill(d->fakechan);
93982db23e2SCameron Grant 
94045550658SPoul-Henning Kamp 	snd_mtxunlock(d->lock);
94149c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
94228ef3fb0SAlexander Leidinger 	sndstat_unregister(dev);
94328ef3fb0SAlexander Leidinger 	sndstat_release();
94433dbf14aSCameron Grant 	return 0;
94533dbf14aSCameron Grant }
9467c438dbeSCameron Grant 
94767b1dce3SCameron Grant /************************************************************************/
94867b1dce3SCameron Grant 
94967b1dce3SCameron Grant static int
95067b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
95167b1dce3SCameron Grant {
95267b1dce3SCameron Grant     	struct snddev_info *d;
95367b1dce3SCameron Grant     	struct snddev_channel *sce;
95467b1dce3SCameron Grant 	struct pcm_channel *c;
95567b1dce3SCameron Grant 	struct pcm_feeder *f;
95667b1dce3SCameron Grant     	int pc, rc, vc;
95767b1dce3SCameron Grant 
95867b1dce3SCameron Grant 	if (verbose < 1)
95967b1dce3SCameron Grant 		return 0;
96067b1dce3SCameron Grant 
96167b1dce3SCameron Grant 	d = device_get_softc(dev);
96267b1dce3SCameron Grant 	if (!d)
96367b1dce3SCameron Grant 		return ENXIO;
96467b1dce3SCameron Grant 
96567b1dce3SCameron Grant 	snd_mtxlock(d->lock);
96667b1dce3SCameron Grant 	if (!SLIST_EMPTY(&d->channels)) {
96767b1dce3SCameron Grant 		pc = rc = vc = 0;
96867b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
96967b1dce3SCameron Grant 			c = sce->channel;
97067b1dce3SCameron Grant 			if (c->direction == PCMDIR_PLAY) {
97167b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
97267b1dce3SCameron Grant 					vc++;
97367b1dce3SCameron Grant 				else
97467b1dce3SCameron Grant 					pc++;
97567b1dce3SCameron Grant 			} else
97667b1dce3SCameron Grant 				rc++;
97767b1dce3SCameron Grant 		}
978a67fe5c1SCameron Grant 		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
97967b1dce3SCameron Grant 				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
98067b1dce3SCameron Grant #ifdef USING_DEVFS
98167b1dce3SCameron Grant 				(device_get_unit(dev) == snd_unit)? " default" : ""
98267b1dce3SCameron Grant #else
98367b1dce3SCameron Grant 				""
98467b1dce3SCameron Grant #endif
98567b1dce3SCameron Grant 				);
986a527dbc7SCameron Grant 
987a527dbc7SCameron Grant 		if (verbose <= 1) {
988a527dbc7SCameron Grant 			snd_mtxunlock(d->lock);
989a527dbc7SCameron Grant 			return 0;
990a527dbc7SCameron Grant 		}
991a527dbc7SCameron Grant 
99267b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
99367b1dce3SCameron Grant 			c = sce->channel;
9949d978cc7SAlexander Leidinger 
9959d978cc7SAlexander Leidinger 			KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
9969d978cc7SAlexander Leidinger 				("hosed pcm channel setup"));
9979d978cc7SAlexander Leidinger 
998a3285889SCameron Grant 			sbuf_printf(s, "\n\t");
999a3285889SCameron Grant 
100045550658SPoul-Henning Kamp 			/* it would be better to indent child channels */
1001a3285889SCameron Grant 			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
10024c68642aSCameron Grant 			sbuf_printf(s, "spd %d", c->speed);
10034c68642aSCameron Grant 			if (c->speed != sndbuf_getspd(c->bufhard))
10044c68642aSCameron Grant 				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
10054c68642aSCameron Grant 			sbuf_printf(s, ", fmt 0x%08x", c->format);
10064c68642aSCameron Grant 			if (c->format != sndbuf_getfmt(c->bufhard))
10074c68642aSCameron Grant 				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1008a527dbc7SCameron Grant 			sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
100967b1dce3SCameron Grant 			if (c->pid != -1)
101067b1dce3SCameron Grant 				sbuf_printf(s, ", pid %d", c->pid);
101167b1dce3SCameron Grant 			sbuf_printf(s, "\n\t");
10124c68642aSCameron Grant 
1013a3285889SCameron Grant 			sbuf_printf(s, "interrupts %d, ", c->interrupts);
1014a3285889SCameron Grant 			if (c->direction == PCMDIR_REC)
101597d69a96SAlexander Leidinger 				sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
101697d69a96SAlexander Leidinger 					c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
101797d69a96SAlexander Leidinger 					sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
101897d69a96SAlexander Leidinger 					sndbuf_getblkcnt(c->bufhard),
101997d69a96SAlexander Leidinger 					sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
102097d69a96SAlexander Leidinger 					sndbuf_getblkcnt(c->bufsoft));
1021a3285889SCameron Grant 			else
102297d69a96SAlexander Leidinger 				sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
102397d69a96SAlexander Leidinger 					c->xruns, sndbuf_getready(c->bufsoft),
102497d69a96SAlexander Leidinger 					sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
102597d69a96SAlexander Leidinger 					sndbuf_getblkcnt(c->bufhard),
102697d69a96SAlexander Leidinger 					sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
102797d69a96SAlexander Leidinger 					sndbuf_getblkcnt(c->bufsoft));
1028a3285889SCameron Grant 			sbuf_printf(s, "\n\t");
10294c68642aSCameron Grant 
10304c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
10314c68642aSCameron Grant 			sbuf_printf(s, " -> ");
103267b1dce3SCameron Grant 			f = c->feeder;
10334c68642aSCameron Grant 			while (f->source != NULL)
10344c68642aSCameron Grant 				f = f->source;
10354c68642aSCameron Grant 			while (f != NULL) {
103667b1dce3SCameron Grant 				sbuf_printf(s, "%s", f->class->name);
103767b1dce3SCameron Grant 				if (f->desc->type == FEEDER_FMT)
10384c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
103967b1dce3SCameron Grant 				if (f->desc->type == FEEDER_RATE)
10404c68642aSCameron Grant 					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
104128ef3fb0SAlexander Leidinger 				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
104228ef3fb0SAlexander Leidinger 						f->desc->type == FEEDER_VOLUME)
10434c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x)", f->desc->out);
10444c68642aSCameron Grant 				sbuf_printf(s, " -> ");
10454c68642aSCameron Grant 				f = f->parent;
104667b1dce3SCameron Grant 			}
10474c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
104867b1dce3SCameron Grant 		}
104967b1dce3SCameron Grant 	} else
105067b1dce3SCameron Grant 		sbuf_printf(s, " (mixer only)");
105167b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
105267b1dce3SCameron Grant 
105367b1dce3SCameron Grant 	return 0;
105467b1dce3SCameron Grant }
105567b1dce3SCameron Grant 
105667b1dce3SCameron Grant /************************************************************************/
105767b1dce3SCameron Grant 
105867b1dce3SCameron Grant #ifdef SND_DYNSYSCTL
105967b1dce3SCameron Grant int
106067b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
106167b1dce3SCameron Grant {
106267b1dce3SCameron Grant 	struct snddev_info *d;
106367b1dce3SCameron Grant     	struct snddev_channel *sce;
106467b1dce3SCameron Grant 	struct pcm_channel *c;
106597d69a96SAlexander Leidinger 	int err, newcnt, cnt;
106667b1dce3SCameron Grant 
106797d69a96SAlexander Leidinger 	/*
106897d69a96SAlexander Leidinger 	 * XXX WOAH... NEED SUPER CLEANUP!!!
106997d69a96SAlexander Leidinger 	 * Robust, yet confusing. Understanding these will
107097d69a96SAlexander Leidinger 	 * cause your brain spinning like a Doki Doki Dynamo.
107197d69a96SAlexander Leidinger 	 */
107267b1dce3SCameron Grant 	d = oidp->oid_arg1;
107367b1dce3SCameron Grant 
10743fdb3676SAriff Abdullah 	if (!(d->flags & SD_F_AUTOVCHAN))
107597d69a96SAlexander Leidinger 		return EINVAL;
1076a527dbc7SCameron Grant 
107767b1dce3SCameron Grant 	cnt = 0;
107867b1dce3SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
107967b1dce3SCameron Grant 		c = sce->channel;
108012e524a2SDon Lewis 		CHN_LOCK(c);
1081a527dbc7SCameron Grant 		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
108267b1dce3SCameron Grant 			cnt++;
108397d69a96SAlexander Leidinger 			if (req->newptr != NULL && c->flags & CHN_F_BUSY) {
108497d69a96SAlexander Leidinger 				/* Better safe than sorry */
108597d69a96SAlexander Leidinger 				CHN_UNLOCK(c);
108697d69a96SAlexander Leidinger 				return EBUSY;
108797d69a96SAlexander Leidinger 			}
108867b1dce3SCameron Grant 		}
108912e524a2SDon Lewis 		CHN_UNLOCK(c);
1090a527dbc7SCameron Grant 	}
1091a527dbc7SCameron Grant 
109267b1dce3SCameron Grant 	newcnt = cnt;
109367b1dce3SCameron Grant 
109467b1dce3SCameron Grant 	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
1095a527dbc7SCameron Grant 
109667b1dce3SCameron Grant 	if (err == 0 && req->newptr != NULL) {
1097a527dbc7SCameron Grant 
10987982e7a4SAriff Abdullah 		if (newcnt < 0 || newcnt > (PCMMAXCHAN + 1) ||
10997982e7a4SAriff Abdullah 				(d->playcount + d->reccount + newcnt) > (PCMMAXCHAN + 1))
1100a527dbc7SCameron Grant 			return E2BIG;
110197d69a96SAlexander Leidinger 
110297d69a96SAlexander Leidinger 		if (pcm_inprog(d, 1) != 1) {
110397d69a96SAlexander Leidinger 			pcm_inprog(d, -1);
110497d69a96SAlexander Leidinger 			return EINPROGRESS;
110567b1dce3SCameron Grant 		}
110667b1dce3SCameron Grant 
110767b1dce3SCameron Grant 		if (newcnt > cnt) {
110867b1dce3SCameron Grant 			/* add new vchans - find a parent channel first */
110967b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
111067b1dce3SCameron Grant 				c = sce->channel;
111112e524a2SDon Lewis 				CHN_LOCK(c);
111267b1dce3SCameron Grant 				/* not a candidate if not a play channel */
111367b1dce3SCameron Grant 				if (c->direction != PCMDIR_PLAY)
111412e524a2SDon Lewis 					goto next;
111567b1dce3SCameron Grant 				/* not a candidate if a virtual channel */
111667b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
111712e524a2SDon Lewis 					goto next;
111867b1dce3SCameron Grant 				/* not a candidate if it's in use */
111912e524a2SDon Lewis 				if (!(c->flags & CHN_F_BUSY) ||
112012e524a2SDon Lewis 				    !(SLIST_EMPTY(&c->children)))
112167b1dce3SCameron Grant 					/*
112212e524a2SDon Lewis 					 * if we get here we're a nonvirtual
112312e524a2SDon Lewis 					 * play channel, and either
112467b1dce3SCameron Grant 					 * 1) not busy
112512e524a2SDon Lewis 					 * 2) busy with children, not directly
112612e524a2SDon Lewis 					 *    open
112767b1dce3SCameron Grant 					 *
112867b1dce3SCameron Grant 					 * thus we can add children
112967b1dce3SCameron Grant 					 */
113067b1dce3SCameron Grant 					goto addok;
113112e524a2SDon Lewis next:
113212e524a2SDon Lewis 				CHN_UNLOCK(c);
113367b1dce3SCameron Grant 			}
1134a527dbc7SCameron Grant 			pcm_inprog(d, -1);
113567b1dce3SCameron Grant 			return EBUSY;
113667b1dce3SCameron Grant addok:
113767b1dce3SCameron Grant 			c->flags |= CHN_F_BUSY;
113867b1dce3SCameron Grant 			while (err == 0 && newcnt > cnt) {
11397982e7a4SAriff Abdullah 				if (d->devcount > PCMMAXCHAN) {
11407982e7a4SAriff Abdullah 					device_printf(d->dev, "%s: Maximum channel reached.\n", __func__);
11417982e7a4SAriff Abdullah 					break;
11427982e7a4SAriff Abdullah 				}
114367b1dce3SCameron Grant 				err = vchan_create(c);
114467b1dce3SCameron Grant 				if (err == 0)
114567b1dce3SCameron Grant 					cnt++;
11467982e7a4SAriff Abdullah 				if (newcnt > cnt && err == E2BIG) {
11477982e7a4SAriff Abdullah 					device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err);
11487982e7a4SAriff Abdullah 					err = 0;
11497982e7a4SAriff Abdullah 					break;
11507982e7a4SAriff Abdullah 				}
115167b1dce3SCameron Grant 			}
115212e524a2SDon Lewis 			CHN_UNLOCK(c);
115367b1dce3SCameron Grant 		} else if (newcnt < cnt) {
1154a527dbc7SCameron Grant 			snd_mtxlock(d->lock);
115567b1dce3SCameron Grant 			while (err == 0 && newcnt < cnt) {
11567982e7a4SAriff Abdullah 				c = NULL;
115767b1dce3SCameron Grant 				SLIST_FOREACH(sce, &d->channels, link) {
11587982e7a4SAriff Abdullah 					CHN_LOCK(sce->channel);
11597982e7a4SAriff Abdullah 					if (sce->channel->direction == PCMDIR_PLAY &&
1160945510a0SAriff Abdullah 							(sce->channel->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
116167b1dce3SCameron Grant 						c = sce->channel;
11627982e7a4SAriff Abdullah 					CHN_UNLOCK(sce->channel);
116367b1dce3SCameron Grant 				}
11647982e7a4SAriff Abdullah 				if (c != NULL)
11657982e7a4SAriff Abdullah 					goto remok;
1166a527dbc7SCameron Grant 				snd_mtxunlock(d->lock);
1167a527dbc7SCameron Grant 				pcm_inprog(d, -1);
116867b1dce3SCameron Grant 				return EINVAL;
116967b1dce3SCameron Grant remok:
117067b1dce3SCameron Grant 				err = vchan_destroy(c);
117167b1dce3SCameron Grant 				if (err == 0)
117267b1dce3SCameron Grant 					cnt--;
117367b1dce3SCameron Grant 			}
1174a527dbc7SCameron Grant 			snd_mtxunlock(d->lock);
117567b1dce3SCameron Grant 		}
1176a527dbc7SCameron Grant 		pcm_inprog(d, -1);
117797d69a96SAlexander Leidinger 	}
117867b1dce3SCameron Grant 	return err;
117967b1dce3SCameron Grant }
118067b1dce3SCameron Grant #endif
118167b1dce3SCameron Grant 
118267b1dce3SCameron Grant /************************************************************************/
118367b1dce3SCameron Grant 
11840739ea1dSSeigo Tanimura static int
11850739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data)
11860739ea1dSSeigo Tanimura {
11877233ababSAlexander Leidinger #if 0
11880739ea1dSSeigo Tanimura 	return (midi_modevent(mod, type, data));
11897233ababSAlexander Leidinger #else
11907233ababSAlexander Leidinger 	return 0;
11917233ababSAlexander Leidinger #endif
11920739ea1dSSeigo Tanimura }
11930739ea1dSSeigo Tanimura 
11940739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL);
11950739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER);
1196