sound.c revision 156929
1139749Simp/*-
2119853Scg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
350724Scg * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
450724Scg * All rights reserved.
550724Scg *
650724Scg * Redistribution and use in source and binary forms, with or without
750724Scg * modification, are permitted provided that the following conditions
850724Scg * are met:
950724Scg * 1. Redistributions of source code must retain the above copyright
1050724Scg *    notice, this list of conditions and the following disclaimer.
1150724Scg * 2. Redistributions in binary form must reproduce the above copyright
1250724Scg *    notice, this list of conditions and the following disclaimer in the
1350724Scg *    documentation and/or other materials provided with the distribution.
1450724Scg *
1550724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1650724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1750724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1850724Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1950724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2050724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2150724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2250724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2350724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2450724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2550724Scg * SUCH DAMAGE.
2650724Scg */
2750724Scg
2853465Scg#include <dev/sound/pcm/sound.h>
2977269Scg#include <dev/sound/pcm/vchan.h>
30124740Smatk#include <dev/sound/pcm/dsp.h>
3165207Scg#include <sys/sysctl.h>
3250724Scg
3382180Scg#include "feeder_if.h"
3482180Scg
3582180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 156929 2006-03-21 06:35:48Z ariff $");
3682180Scg
3778362Scgdevclass_t pcm_devclass;
3850724Scg
3989834Scgint pcm_veto_load = 1;
4089834Scg
4173127Scg#ifdef USING_DEVFS
4278362Scgint snd_unit = 0;
4377900SpeterTUNABLE_INT("hw.snd.unit", &snd_unit);
4473127Scg#endif
4579141Scg
4682180Scgint snd_maxautovchans = 0;
4782180ScgTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
4850724Scg
4973127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
5073127Scg
5182180Scgstatic int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
5282180Scg
5382180Scgstruct sysctl_ctx_list *
5482180Scgsnd_sysctl_tree(device_t dev)
5582180Scg{
5682180Scg    	struct snddev_info *d = device_get_softc(dev);
5782180Scg
5882180Scg	return &d->sysctl_tree;
5982180Scg}
6082180Scg
6182180Scgstruct sysctl_oid *
6282180Scgsnd_sysctl_tree_top(device_t dev)
6382180Scg{
6482180Scg    	struct snddev_info *d = device_get_softc(dev);
6582180Scg
6682180Scg	return d->sysctl_tree_top;
6782180Scg}
6882180Scg
6973131Scgvoid *
7093814Sjhbsnd_mtxcreate(const char *desc, const char *type)
7173131Scg{
7273131Scg#ifdef USING_MUTEX
7373131Scg	struct mtx *m;
7473131Scg
75111119Simp	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
7673131Scg	if (m == NULL)
7773131Scg		return NULL;
78125136Struckman	mtx_init(m, desc, type, MTX_DEF);
7973131Scg	return m;
8073131Scg#else
8173757Scg	return (void *)0xcafebabe;
8273131Scg#endif
8373131Scg}
8473131Scg
8573131Scgvoid
8673131Scgsnd_mtxfree(void *m)
8773131Scg{
8873131Scg#ifdef USING_MUTEX
8973131Scg	struct mtx *mtx = m;
9073131Scg
91107237Scg	/* mtx_assert(mtx, MA_OWNED); */
9273131Scg	mtx_destroy(mtx);
9373131Scg	free(mtx, M_DEVBUF);
9473131Scg#endif
9573131Scg}
9673131Scg
9773131Scgvoid
9873131Scgsnd_mtxassert(void *m)
9973131Scg{
10073131Scg#ifdef USING_MUTEX
10178670Scg#ifdef INVARIANTS
10273131Scg	struct mtx *mtx = m;
10373131Scg
10473131Scg	mtx_assert(mtx, MA_OWNED);
10573131Scg#endif
10678670Scg#endif
10773131Scg}
108107237Scg/*
10973131Scgvoid
11073131Scgsnd_mtxlock(void *m)
11173131Scg{
11273131Scg#ifdef USING_MUTEX
11373131Scg	struct mtx *mtx = m;
11473131Scg
11573131Scg	mtx_lock(mtx);
11673131Scg#endif
11773131Scg}
11873131Scg
11973131Scgvoid
12073131Scgsnd_mtxunlock(void *m)
12173131Scg{
12273131Scg#ifdef USING_MUTEX
12373131Scg	struct mtx *mtx = m;
12473131Scg
12573131Scg	mtx_unlock(mtx);
12673131Scg#endif
12773131Scg}
128107237Scg*/
12973131Scgint
13073131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
13173131Scg{
13273131Scg#ifdef USING_MUTEX
13373131Scg	flags &= INTR_MPSAFE;
13478366Speter	flags |= INTR_TYPE_AV;
13573131Scg#else
13678366Speter	flags = INTR_TYPE_AV;
13773131Scg#endif
13873131Scg	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
13973131Scg}
14073131Scg
141119096Scg#ifndef	PCM_DEBUG_MTX
14282180Scgvoid
14382180Scgpcm_lock(struct snddev_info *d)
14482180Scg{
14582180Scg	snd_mtxlock(d->lock);
14682180Scg}
14782180Scg
14882180Scgvoid
14982180Scgpcm_unlock(struct snddev_info *d)
15082180Scg{
15182180Scg	snd_mtxunlock(d->lock);
15282180Scg}
153119096Scg#endif
15482180Scg
15582180Scgstruct pcm_channel *
15682180Scgpcm_getfakechan(struct snddev_info *d)
15782180Scg{
15882180Scg	return d->fakechan;
15982180Scg}
16082180Scg
161156929Sariff/* return error status and a locked channel */
162156929Sariffint
163156929Sariffpcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
164156929Sariff		pid_t pid, int chnum)
16577269Scg{
16677269Scg	struct pcm_channel *c;
16777269Scg    	struct snddev_channel *sce;
168156929Sariff	int err, ret;
16977269Scg
170156929Sariffretry_chnalloc:
171156929Sariff	ret = ENODEV;
17278895Scg	/* scan for a free channel */
17377269Scg	SLIST_FOREACH(sce, &d->channels, link) {
17477269Scg		c = sce->channel;
17577882Scg		CHN_LOCK(c);
17677269Scg		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
177156929Sariff			if (chnum < 0 || sce->chan_num == chnum) {
17883089Scg				c->flags |= CHN_F_BUSY;
17983089Scg				c->pid = pid;
180156929Sariff				*ch = c;
181156929Sariff				return 0;
18283089Scg			}
18377269Scg		}
184156929Sariff		if (sce->chan_num == chnum) {
185156929Sariff			if (c->direction != direction)
186156929Sariff				err = EOPNOTSUPP;
187156929Sariff			else if (c->flags & CHN_F_BUSY)
188156929Sariff				err = EBUSY;
189156929Sariff			else
190156929Sariff				err = EINVAL;
191156929Sariff			CHN_UNLOCK(c);
192156929Sariff			return err;
193156929Sariff		} else if (c->direction == direction && (c->flags & CHN_F_BUSY))
194156929Sariff			ret = EBUSY;
19577882Scg		CHN_UNLOCK(c);
19677269Scg	}
19778895Scg
19878895Scg	/* no channel available */
199156929Sariff	if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 &&
200156762Sariff			d->vchancount < snd_maxautovchans &&
201156762Sariff			d->devcount <= PCMMAXCHAN) {
202156762Sariff		/* try to create a vchan */
203156762Sariff		SLIST_FOREACH(sce, &d->channels, link) {
204156762Sariff			c = sce->channel;
205156762Sariff			CHN_LOCK(c);
206156762Sariff			if ((c->flags & CHN_F_HAS_VCHAN) &&
207156762Sariff					!SLIST_EMPTY(&c->children)) {
208156762Sariff				err = vchan_create(c);
209156762Sariff				CHN_UNLOCK(c);
210156929Sariff				if (!err) {
211156929Sariff					chnum = -2;
212156929Sariff					goto retry_chnalloc;
213156929Sariff				} else
214156929Sariff					device_printf(d->dev, "%s: vchan_create(%s) == %d\n", __func__, c->name, err);
215156762Sariff			} else
216156762Sariff				CHN_UNLOCK(c);
21778895Scg		}
21878895Scg	}
21978895Scg
220156929Sariff	return ret;
22177269Scg}
22277269Scg
22378214Scg/* release a locked channel and unlock it */
22477269Scgint
22578214Scgpcm_chnrelease(struct pcm_channel *c)
22677269Scg{
22778214Scg	CHN_LOCKASSERT(c);
22877269Scg	c->flags &= ~CHN_F_BUSY;
22978214Scg	c->pid = -1;
23077882Scg	CHN_UNLOCK(c);
23177269Scg	return 0;
23277269Scg}
23377269Scg
23477269Scgint
23577269Scgpcm_chnref(struct pcm_channel *c, int ref)
23677269Scg{
23777882Scg	int r;
23877882Scg
23978214Scg	CHN_LOCKASSERT(c);
24077269Scg	c->refcount += ref;
24177882Scg	r = c->refcount;
24277882Scg	return r;
24377269Scg}
24477269Scg
24582180Scgint
24682180Scgpcm_inprog(struct snddev_info *d, int delta)
24782180Scg{
248119096Scg	int r;
249119096Scg
250119096Scg	if (delta == 0)
251119096Scg		return d->inprog;
252119096Scg
253119096Scg	/* backtrace(); */
254119096Scg	pcm_lock(d);
25582180Scg	d->inprog += delta;
256119096Scg	r = d->inprog;
257119096Scg	pcm_unlock(d);
258119096Scg	return r;
25982180Scg}
26082180Scg
26182180Scgstatic void
26282180Scgpcm_setmaxautovchans(struct snddev_info *d, int num)
26382180Scg{
264149953Snetchild	struct pcm_channel *c, *ch;
26582180Scg    	struct snddev_channel *sce;
26682180Scg	int err, done;
26782180Scg
268149953Snetchild	/*
269149953Snetchild	 * XXX WOAH... NEED SUPER CLEANUP!!!
270149953Snetchild	 * Robust, yet confusing. Understanding these will
271149953Snetchild	 * cause your brain spinning like a Doki Doki Dynamo.
272149953Snetchild	 */
27382180Scg	if (num > 0 && d->vchancount == 0) {
27482180Scg		SLIST_FOREACH(sce, &d->channels, link) {
27582180Scg			c = sce->channel;
276125136Struckman			CHN_LOCK(c);
277149953Snetchild			if ((c->direction == PCMDIR_PLAY) &&
278156929Sariff					(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == 0 &&
279149953Snetchild					SLIST_EMPTY(&c->children)) {
28082180Scg				c->flags |= CHN_F_BUSY;
28182180Scg				err = vchan_create(c);
28282180Scg				if (err) {
283125136Struckman					c->flags &= ~CHN_F_BUSY;
284156929Sariff					device_printf(d->dev, "%s: vchan_create(%s) == %d\n", __func__, c->name, err);
285156929Sariff				} else {
286156929Sariff					CHN_UNLOCK(c);
287156929Sariff					return;
288149953Snetchild				}
28982180Scg			}
290125136Struckman			CHN_UNLOCK(c);
29182180Scg		}
292149953Snetchild		return;
29382180Scg	}
29482180Scg	if (num == 0 && d->vchancount > 0) {
295149953Snetchild		/*
296149953Snetchild		 * XXX Keep retrying...
297149953Snetchild		 */
298149953Snetchild		for (done = 0; done < 1024; done++) {
299149953Snetchild			ch = NULL;
30082180Scg			SLIST_FOREACH(sce, &d->channels, link) {
30182180Scg				c = sce->channel;
302149953Snetchild				CHN_LOCK(c);
303149953Snetchild				if (c->direction == PCMDIR_PLAY &&
304149953Snetchild						!(c->flags & CHN_F_BUSY) &&
305149953Snetchild						(c->flags & CHN_F_VIRTUAL)) {
306149953Snetchild					ch = c;
307149953Snetchild					break;
30882180Scg				}
309149953Snetchild				CHN_UNLOCK(c);
31082180Scg			}
311149953Snetchild			if (ch != NULL) {
312149953Snetchild				CHN_UNLOCK(ch);
313149953Snetchild				snd_mtxlock(d->lock);
314149953Snetchild				err = vchan_destroy(ch);
315149953Snetchild				if (err)
316149953Snetchild					device_printf(d->dev, "vchan_destroy(%s) == %d\n",
317149953Snetchild								ch->name, err);
318149953Snetchild				snd_mtxunlock(d->lock);
319149953Snetchild			} else
320149953Snetchild				return;
32182180Scg		}
322149953Snetchild		return;
32382180Scg	}
32482180Scg}
32582180Scg
32673127Scg#ifdef USING_DEVFS
32765340Scgstatic int
32878853Scgsysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
32965340Scg{
33078214Scg	struct snddev_info *d;
33165340Scg	int error, unit;
33265340Scg
33365340Scg	unit = snd_unit;
33465340Scg	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
33565340Scg	if (error == 0 && req->newptr != NULL) {
33678396Scg		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
33778214Scg			return EINVAL;
33878214Scg		d = devclass_get_softc(pcm_devclass, unit);
33978395Scg		if (d == NULL || SLIST_EMPTY(&d->channels))
34078214Scg			return EINVAL;
34165340Scg		snd_unit = unit;
34265340Scg	}
34365340Scg	return (error);
34465340Scg}
34570617SjhbSYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
34678853Scg            0, sizeof(int), sysctl_hw_snd_unit, "I", "");
34773127Scg#endif
34865340Scg
34978853Scgstatic int
35082180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
35178853Scg{
35282180Scg	struct snddev_info *d;
35382180Scg	int i, v, error;
35478853Scg
35582180Scg	v = snd_maxautovchans;
35678853Scg	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
35778853Scg	if (error == 0 && req->newptr != NULL) {
358156929Sariff		if (v < 0 || v > (PCMMAXCHAN + 1))
35978853Scg			return EINVAL;
360156929Sariff		if (pcm_devclass != NULL && v != snd_maxautovchans) {
36182180Scg			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
36282180Scg				d = devclass_get_softc(pcm_devclass, i);
36382180Scg				if (!d)
36482180Scg					continue;
365149953Snetchild				if (d->flags & SD_F_AUTOVCHAN) {
366149953Snetchild					if (pcm_inprog(d, 1) == 1)
367149953Snetchild						pcm_setmaxautovchans(d, v);
368149953Snetchild					pcm_inprog(d, -1);
369149953Snetchild				}
37082180Scg			}
37182180Scg		}
37282180Scg		snd_maxautovchans = v;
37378853Scg	}
37478853Scg	return (error);
37578853Scg}
37682180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
37782180Scg            0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
37878853Scg
37977269Scgstruct pcm_channel *
38077269Scgpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
38150724Scg{
382156929Sariff	struct snddev_channel *sce;
383156929Sariff	struct pcm_channel *ch, *tmpch;
38465340Scg	char *dirs;
385156929Sariff	u_int32_t flsearch = 0;
386156929Sariff	int direction, err, rpnum, *pnum;
38750724Scg
38877269Scg	switch(dir) {
38977269Scg	case PCMDIR_PLAY:
39077269Scg		dirs = "play";
391126367Struckman		direction = PCMDIR_PLAY;
39283614Scg		pnum = &d->playcount;
39377269Scg		break;
39483614Scg
39577269Scg	case PCMDIR_REC:
39677269Scg		dirs = "record";
397126367Struckman		direction = PCMDIR_REC;
39883614Scg		pnum = &d->reccount;
39977269Scg		break;
40083614Scg
40177269Scg	case PCMDIR_VIRTUAL:
40277269Scg		dirs = "virtual";
403126367Struckman		direction = PCMDIR_PLAY;
40483614Scg		pnum = &d->vchancount;
405156929Sariff		flsearch = CHN_F_VIRTUAL;
40677269Scg		break;
40783614Scg
40877269Scg	default:
40977269Scg		return NULL;
41077269Scg	}
41165340Scg
412111119Simp	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
41377269Scg	if (!ch)
41477269Scg		return NULL;
41577269Scg
416111119Simp	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
41777269Scg	if (!ch->methods) {
41877269Scg		free(ch, M_DEVBUF);
41983614Scg
42077269Scg		return NULL;
42161344Scg	}
42277269Scg
423107237Scg	snd_mtxlock(d->lock);
424156929Sariff	ch->num = 0;
425156929Sariff	rpnum = 0;
426156929Sariff	SLIST_FOREACH(sce, &d->channels, link) {
427156929Sariff		if (sce == NULL || sce->channel == NULL)
428156929Sariff			continue;
429156929Sariff		tmpch = sce->channel;
430156929Sariff		if (direction != tmpch->direction ||
431156929Sariff				(tmpch->flags & CHN_F_VIRTUAL) != flsearch)
432156929Sariff			continue;
433156929Sariff		if (ch->num == tmpch->num)
434156929Sariff			ch->num++;
435156929Sariff		else {
436156929Sariff			/*
437156929Sariff			 * Channel numbering screwed. Bail out, and do the
438156929Sariff			 * hard way lookup.
439156929Sariff			 */
440156929Sariff			device_printf(d->dev,
441156929Sariff				"%s: channel numbering dirs=%s screwed.\n",
442156929Sariff				__func__, dirs);
443156929Sariff			ch->num = 0;
444156929Sariff			goto retry_num_search;
445156929Sariff		}
446156929Sariff		rpnum++;
447156929Sariff	}
448156929Sariff	goto retry_num_search_out;
449156929Sariffretry_num_search:
450156929Sariff	rpnum = 0;
451156929Sariff	SLIST_FOREACH(sce, &d->channels, link) {
452156929Sariff		if (sce == NULL || sce->channel == NULL)
453156929Sariff			continue;
454156929Sariff		tmpch = sce->channel;
455156929Sariff		if (direction != tmpch->direction ||
456156929Sariff				(tmpch->flags & CHN_F_VIRTUAL) != flsearch)
457156929Sariff			continue;
458156929Sariff		if (ch->num == tmpch->num) {
459156929Sariff			ch->num++;
460156929Sariff			goto retry_num_search;
461156929Sariff		}
462156929Sariff		rpnum++;
463156929Sariff	}
464156929Sariffretry_num_search_out:
465156929Sariff	if (*pnum != rpnum) {
466156929Sariff		device_printf(d->dev,
467156929Sariff			"%s: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
468156929Sariff			__func__, dirs, *pnum, rpnum);
469156929Sariff		*pnum = rpnum;
470156929Sariff	}
471156929Sariff	(*pnum)++;
472107237Scg	snd_mtxunlock(d->lock);
47383614Scg
47477269Scg	ch->pid = -1;
47577269Scg	ch->parentsnddev = d;
47677269Scg	ch->parentchannel = parent;
47789774Sscottl	ch->dev = d->dev;
478156929Sariff	snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
47977269Scg
480126367Struckman	err = chn_init(ch, devinfo, dir, direction);
48170134Scg	if (err) {
48283614Scg		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
48377269Scg		kobj_delete(ch->methods, M_DEVBUF);
48477269Scg		free(ch, M_DEVBUF);
485107237Scg		snd_mtxlock(d->lock);
48683614Scg		(*pnum)--;
487107237Scg		snd_mtxunlock(d->lock);
48883614Scg
48977269Scg		return NULL;
49055483Scg	}
49177269Scg
49277269Scg	return ch;
49377269Scg}
49477269Scg
49577269Scgint
49677269Scgpcm_chn_destroy(struct pcm_channel *ch)
49777269Scg{
49883614Scg	struct snddev_info *d;
49977269Scg	int err;
50077269Scg
50183614Scg	d = ch->parentsnddev;
50277269Scg	err = chn_kill(ch);
50377269Scg	if (err) {
50483614Scg		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
50577269Scg		return err;
50677269Scg	}
50777269Scg
50877269Scg	kobj_delete(ch->methods, M_DEVBUF);
50977269Scg	free(ch, M_DEVBUF);
51077269Scg
51177269Scg	return 0;
51277269Scg}
51377269Scg
51477269Scgint
515124740Smatkpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
51677269Scg{
51782180Scg    	struct snddev_channel *sce, *tmp, *after;
518156929Sariff	unsigned rdevcount;
519124740Smatk    	int device = device_get_unit(d->dev);
520156929Sariff	int stop;
521156929Sariff	size_t namelen;
52277269Scg
523124740Smatk	/*
524124740Smatk	 * Note it's confusing nomenclature.
525124740Smatk	 * dev_t
526124740Smatk	 * device -> pcm_device
527124740Smatk         * unit -> pcm_channel
528124740Smatk	 * channel -> snddev_channel
529124740Smatk	 * device_t
530124740Smatk	 * unit -> pcm_device
531124740Smatk	 */
532124740Smatk
533111119Simp	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
53477269Scg	if (!sce) {
53577269Scg		return ENOMEM;
53677269Scg	}
53777269Scg
538100478Sorion	snd_mtxlock(d->lock);
53977269Scg	sce->channel = ch;
540156929Sariff	sce->chan_num = 0;
541156929Sariff	after = NULL;
542156929Sariff	stop = 0;
543156929Sariffretry_chan_num_search:
544156929Sariff	/*
545156929Sariff	 * Look for possible channel numbering collision. This may not
546156929Sariff	 * be optimized, but it will ensure that no collision occured.
547156929Sariff	 * Creating maximum possible channels (256 channels) will cost
548156929Sariff	 * us at most 32895 cycles, but this can be considered cheap
549156929Sariff	 * since none of the locking/unlocking operations involved.
550156929Sariff	 *
551156929Sariff	 * Micro optimization, channel ordering:
552156929Sariff	 * hw,hw,hw,vch,vch,vch,rec
553156929Sariff	 */
554156929Sariff	rdevcount = 0;
555156929Sariff	SLIST_FOREACH(tmp, &d->channels, link) {
556156929Sariff		if (tmp == NULL || tmp->channel == NULL)
557156929Sariff			continue;
558156929Sariff		if (sce->chan_num == tmp->chan_num) {
559156929Sariff			sce->chan_num++;
560156929Sariff			after = NULL;
561156929Sariff			stop = 0;
562156929Sariff			goto retry_chan_num_search;
563156762Sariff		}
564156929Sariff		if (stop == 0) {
565156929Sariff			if (ch->flags & CHN_F_VIRTUAL) {
566149953Snetchild				if (tmp->channel->direction == PCMDIR_REC)
567156929Sariff					stop = 1;
568156929Sariff				else
569156929Sariff					after = tmp;
570156929Sariff			} else if (ch->direction == PCMDIR_REC) {
571149953Snetchild				after = tmp;
572156929Sariff			} else {
573156929Sariff				if (tmp->channel->direction != PCMDIR_PLAY ||
574156929Sariff						(tmp->channel->flags & CHN_F_VIRTUAL)) {
575156929Sariff					stop = 1;
576156929Sariff				} else {
577149953Snetchild					after = tmp;
578149953Snetchild				}
579149953Snetchild			}
58082180Scg		}
581156929Sariff		rdevcount++;
58282180Scg	}
583156929Sariff	/*
584156929Sariff	 * Don't overflow PCMMKMINOR / PCMMAXCHAN.
585156929Sariff	 */
586156929Sariff	if (sce->chan_num > PCMMAXCHAN) {
587156929Sariff		snd_mtxunlock(d->lock);
588156929Sariff		device_printf(d->dev,
589156929Sariff			"%s: WARNING: sce->chan_num overflow! (%d)\n",
590156929Sariff			__func__, sce->chan_num);
591156929Sariff		free(sce, M_DEVBUF);
592156929Sariff		return E2BIG;
593156929Sariff	}
594156929Sariff	if (d->devcount != rdevcount) {
595156929Sariff		device_printf(d->dev,
596156929Sariff			"%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n",
597156929Sariff			__func__, d->devcount, rdevcount);
598156929Sariff		d->devcount = rdevcount;
599156929Sariff	}
600156762Sariff	d->devcount++;
601156929Sariff	if (after == NULL) {
602156929Sariff		SLIST_INSERT_HEAD(&d->channels, sce, link);
603156929Sariff	} else {
604156929Sariff		SLIST_INSERT_AFTER(after, sce, link);
605156929Sariff	}
606156929Sariff
607156929Sariff	namelen = strlen(ch->name);
608156929Sariff	if ((CHN_NAMELEN - namelen) > 10) {	/* ":dspXX.YYY" */
609156929Sariff		snprintf(ch->name + namelen,
610156929Sariff			CHN_NAMELEN - namelen, ":dsp%d.%d",
611156929Sariff			device, sce->chan_num);
612156929Sariff	}
613107237Scg	snd_mtxunlock(d->lock);
614156929Sariff	sce->dsp_devt = make_dev(&dsp_cdevsw,
615124740Smatk			PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
616124740Smatk			UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
617124740Smatk			device, sce->chan_num);
61877269Scg
619156929Sariff	sce->dspW_devt = make_dev(&dsp_cdevsw,
620124740Smatk			PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
621124740Smatk			UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
622124740Smatk			device, sce->chan_num);
62377269Scg
624156929Sariff	sce->audio_devt = make_dev(&dsp_cdevsw,
625124740Smatk			PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
626124740Smatk			UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
627124740Smatk			device, sce->chan_num);
628124740Smatk
629124740Smatk	if (ch->direction == PCMDIR_REC)
630124740Smatk		sce->dspr_devt = make_dev(&dsp_cdevsw,
631124740Smatk				PCMMKMINOR(device, SND_DEV_DSPREC,
632124740Smatk					sce->chan_num), UID_ROOT, GID_WHEEL,
633124740Smatk				0666, "dspr%d.%d", device, sce->chan_num);
634124740Smatk
63550724Scg	return 0;
63650724Scg}
63750724Scg
63877269Scgint
639124740Smatkpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
64065340Scg{
64177269Scg    	struct snddev_channel *sce;
642124617Sphk#if 0
643119096Scg	int ourlock;
64465340Scg
645119096Scg	ourlock = 0;
646119096Scg	if (!mtx_owned(d->lock)) {
647119096Scg		snd_mtxlock(d->lock);
648119096Scg		ourlock = 1;
649119096Scg	}
650124617Sphk#endif
651119096Scg
65277269Scg	SLIST_FOREACH(sce, &d->channels, link) {
65377269Scg		if (sce->channel == ch)
65477269Scg			goto gotit;
65565340Scg	}
656124617Sphk#if 0
657119096Scg	if (ourlock)
658119096Scg		snd_mtxunlock(d->lock);
659124617Sphk#endif
66077269Scg	return EINVAL;
66177269Scggotit:
66277269Scg	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
663107237Scg
664149953Snetchild	if (ch->flags & CHN_F_VIRTUAL)
665149953Snetchild		d->vchancount--;
666149953Snetchild	else if (ch->direction == PCMDIR_REC)
667107237Scg		d->reccount--;
668107237Scg	else
669107237Scg		d->playcount--;
670107237Scg
671124617Sphk#if 0
672119096Scg	if (ourlock)
673119096Scg		snd_mtxunlock(d->lock);
674124617Sphk#endif
675107237Scg	free(sce, M_DEVBUF);
67677269Scg
67765340Scg	return 0;
67865340Scg}
67965340Scg
68050724Scgint
68177269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
68277269Scg{
68377269Scg    	struct snddev_info *d = device_get_softc(dev);
68482180Scg	struct pcm_channel *ch;
68582180Scg    	int err;
68677269Scg
68777269Scg	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
68877269Scg	if (!ch) {
68977269Scg		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
69077269Scg		return ENODEV;
69177269Scg	}
69278853Scg
693124740Smatk	err = pcm_chn_add(d, ch);
69477269Scg	if (err) {
69577269Scg		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
69677269Scg		pcm_chn_destroy(ch);
69778853Scg		return err;
69877269Scg	}
69977269Scg
70077269Scg	return err;
70177269Scg}
70277269Scg
70377269Scgstatic int
70477269Scgpcm_killchan(device_t dev)
70577269Scg{
70677269Scg    	struct snddev_info *d = device_get_softc(dev);
70777269Scg    	struct snddev_channel *sce;
708106420Scognet	struct pcm_channel *ch;
709106420Scognet	int error = 0;
71077269Scg
71177269Scg	sce = SLIST_FIRST(&d->channels);
712106420Scognet	ch = sce->channel;
71377269Scg
714124740Smatk	error = pcm_chn_remove(d, sce->channel);
715106420Scognet	if (error)
716106420Scognet		return (error);
717106420Scognet	return (pcm_chn_destroy(ch));
71877269Scg}
71977269Scg
72077269Scgint
72150724Scgpcm_setstatus(device_t dev, char *str)
72250724Scg{
72374763Scg    	struct snddev_info *d = device_get_softc(dev);
724156929Sariff	struct snddev_channel *sce;
725156929Sariff	struct pcm_channel *ch;
726156929Sariff	int err;
72777882Scg
72877882Scg	snd_mtxlock(d->lock);
72950724Scg	strncpy(d->status, str, SND_STATUSLEN);
73077882Scg	snd_mtxunlock(d->lock);
731156929Sariff	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
732156929Sariff			d->vchancount == 0) {
733156929Sariff		SLIST_FOREACH(sce, &d->channels, link) {
734156929Sariff			if (sce == NULL || sce->channel == NULL)
735156929Sariff				continue;
736156929Sariff			ch = sce->channel;
737156929Sariff			CHN_LOCK(ch);
738156929Sariff			if (ch->direction == PCMDIR_PLAY &&
739156929Sariff					(ch->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == 0 &&
740156929Sariff					SLIST_EMPTY(&ch->children)) {
741156929Sariff				ch->flags |= CHN_F_BUSY;
742156929Sariff				err = vchan_create(ch);
743156929Sariff				if (err) {
744156929Sariff					ch->flags &= ~CHN_F_BUSY;
745156929Sariff					device_printf(d->dev, "%s: vchan_create(%s) == %d\n",
746156929Sariff						__func__, ch->name, err);
747156929Sariff				} else {
748156929Sariff					CHN_UNLOCK(ch);
749156929Sariff					return 0;
750156929Sariff				}
751156929Sariff			}
752156929Sariff			CHN_UNLOCK(ch);
753156929Sariff		}
754156929Sariff	}
75550724Scg	return 0;
75650724Scg}
75750724Scg
75850724Scgu_int32_t
75950724Scgpcm_getflags(device_t dev)
76050724Scg{
76174763Scg    	struct snddev_info *d = device_get_softc(dev);
76277882Scg
76350724Scg	return d->flags;
76450724Scg}
76550724Scg
76650724Scgvoid
76750724Scgpcm_setflags(device_t dev, u_int32_t val)
76850724Scg{
76974763Scg    	struct snddev_info *d = device_get_softc(dev);
77078362Scg
77150724Scg	d->flags = val;
77250724Scg}
77350724Scg
77458384Scgvoid *
77558384Scgpcm_getdevinfo(device_t dev)
77658384Scg{
77774763Scg    	struct snddev_info *d = device_get_softc(dev);
77877882Scg
77958384Scg	return d->devinfo;
78058384Scg}
78158384Scg
78283614Scgunsigned int
78383614Scgpcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
78483614Scg{
78583614Scg    	struct snddev_info *d = device_get_softc(dev);
78689690Scg	int sz, x;
78783614Scg
78883614Scg	sz = 0;
78989690Scg	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
79089690Scg		x = sz;
79183614Scg		RANGE(sz, min, max);
79289690Scg		if (x != sz)
79389690Scg			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
79489690Scg		x = min;
79589690Scg		while (x < sz)
79689690Scg			x <<= 1;
79789690Scg		if (x > sz)
79889690Scg			x >>= 1;
79989690Scg		if (x != sz) {
80089690Scg			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
80189690Scg			sz = x;
80289690Scg		}
80389690Scg	} else {
80483614Scg		sz = deflt;
80589690Scg	}
80689690Scg
80783614Scg	d->bufsz = sz;
80883614Scg
80983614Scg	return sz;
81083614Scg}
81183614Scg
81250724Scgint
81350724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec)
81450724Scg{
81574763Scg    	struct snddev_info *d = device_get_softc(dev);
81650724Scg
81789834Scg	if (pcm_veto_load) {
81889834Scg		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
81989834Scg
82089834Scg		return EINVAL;
82189834Scg	}
82289834Scg
82393814Sjhb	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
82477269Scg
825148587Snetchild#if 0
826148587Snetchild	/*
827148587Snetchild	 * d->flags should be cleared by the allocator of the softc.
828148587Snetchild	 * We cannot clear this field here because several devices set
829148587Snetchild	 * this flag before calling pcm_register().
830148587Snetchild	 */
83178853Scg	d->flags = 0;
832148587Snetchild#endif
83361886Scg	d->dev = dev;
83450724Scg	d->devinfo = devinfo;
83578895Scg	d->devcount = 0;
83683089Scg	d->reccount = 0;
83783614Scg	d->playcount = 0;
83878895Scg	d->vchancount = 0;
83978362Scg	d->inprog = 0;
84050724Scg
841124617Sphk	SLIST_INIT(&d->channels);
842124617Sphk
84378362Scg	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
84478214Scg		d->flags |= SD_F_SIMPLEX;
84578362Scg
84678214Scg	d->fakechan = fkchan_setup(dev);
847126367Struckman	chn_init(d->fakechan, NULL, 0, 0);
84855494Scg
84973127Scg#ifdef SND_DYNSYSCTL
85070680Sjhb	sysctl_ctx_init(&d->sysctl_tree);
85170680Sjhb	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
85270943Sjhb				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
85370680Sjhb				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
85470943Sjhb	if (d->sysctl_tree_top == NULL) {
85570680Sjhb		sysctl_ctx_free(&d->sysctl_tree);
85670680Sjhb		goto no;
85770680Sjhb	}
85883614Scg	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
85983614Scg            OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
86073127Scg#endif
861149953Snetchild	if (numplay > 0) {
862156929Sariff		d->flags |= SD_F_AUTOVCHAN;
86382180Scg		vchan_initsys(dev);
864149953Snetchild	}
86578853Scg
86682180Scg	sndstat_register(dev, d->status, sndstat_prepare_pcm);
86750724Scg    	return 0;
86850724Scgno:
86977882Scg	snd_mtxfree(d->lock);
87050724Scg	return ENXIO;
87150724Scg}
87250724Scg
87365340Scgint
87465340Scgpcm_unregister(device_t dev)
87565207Scg{
87674763Scg    	struct snddev_info *d = device_get_softc(dev);
87777269Scg    	struct snddev_channel *sce;
87883614Scg	struct pcm_channel *ch;
87965207Scg
880150827Snetchild	if (sndstat_acquire() != 0) {
881150827Snetchild		device_printf(dev, "unregister: sndstat busy\n");
882150827Snetchild		return EBUSY;
883150827Snetchild	}
884150827Snetchild
88577882Scg	snd_mtxlock(d->lock);
88678362Scg	if (d->inprog) {
88783476Sgreid		device_printf(dev, "unregister: operation in progress\n");
88878362Scg		snd_mtxunlock(d->lock);
889150827Snetchild		sndstat_release();
89078362Scg		return EBUSY;
89178362Scg	}
892124740Smatk
89377269Scg	SLIST_FOREACH(sce, &d->channels, link) {
89483614Scg		ch = sce->channel;
89583614Scg		if (ch->refcount > 0) {
89695684Scg			device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
89777882Scg			snd_mtxunlock(d->lock);
898150827Snetchild			sndstat_release();
89977269Scg			return EBUSY;
90077269Scg		}
90165487Scg	}
902124740Smatk
903148587Snetchild	if (mixer_uninit(dev)) {
904148587Snetchild		device_printf(dev, "unregister: mixer busy\n");
905148587Snetchild		snd_mtxunlock(d->lock);
906150827Snetchild		sndstat_release();
907148587Snetchild		return EBUSY;
908148587Snetchild	}
909148587Snetchild
910124740Smatk	SLIST_FOREACH(sce, &d->channels, link) {
911156762Sariff		if (sce->dsp_devt) {
912149953Snetchild			destroy_dev(sce->dsp_devt);
913156762Sariff			sce->dsp_devt = NULL;
914156762Sariff		}
915156762Sariff		if (sce->dspW_devt) {
916149953Snetchild			destroy_dev(sce->dspW_devt);
917156762Sariff			sce->dspW_devt = NULL;
918156762Sariff		}
919156762Sariff		if (sce->audio_devt) {
920149953Snetchild			destroy_dev(sce->audio_devt);
921156762Sariff			sce->audio_devt = NULL;
922156762Sariff		}
923156762Sariff		if (sce->dspr_devt) {
924124740Smatk			destroy_dev(sce->dspr_devt);
925156762Sariff			sce->dspr_devt = NULL;
926156762Sariff		}
927156762Sariff		d->devcount--;
928124740Smatk	}
929124740Smatk
93074763Scg#ifdef SND_DYNSYSCTL
93174763Scg	d->sysctl_tree_top = NULL;
93274763Scg	sysctl_ctx_free(&d->sysctl_tree);
93374763Scg#endif
93478395Scg	while (!SLIST_EMPTY(&d->channels))
93577269Scg		pcm_killchan(dev);
93665207Scg
93774763Scg	chn_kill(d->fakechan);
93874763Scg	fkchan_kill(d->fakechan);
93973127Scg
940124617Sphk	snd_mtxunlock(d->lock);
94177882Scg	snd_mtxfree(d->lock);
942150827Snetchild	sndstat_unregister(dev);
943150827Snetchild	sndstat_release();
94465340Scg	return 0;
94565207Scg}
94665207Scg
94782180Scg/************************************************************************/
94882180Scg
94982180Scgstatic int
95082180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
95182180Scg{
95282180Scg    	struct snddev_info *d;
95382180Scg    	struct snddev_channel *sce;
95482180Scg	struct pcm_channel *c;
95582180Scg	struct pcm_feeder *f;
95682180Scg    	int pc, rc, vc;
95782180Scg
95882180Scg	if (verbose < 1)
95982180Scg		return 0;
96082180Scg
96182180Scg	d = device_get_softc(dev);
96282180Scg	if (!d)
96382180Scg		return ENXIO;
96482180Scg
96582180Scg	snd_mtxlock(d->lock);
96682180Scg	if (!SLIST_EMPTY(&d->channels)) {
96782180Scg		pc = rc = vc = 0;
96882180Scg		SLIST_FOREACH(sce, &d->channels, link) {
96982180Scg			c = sce->channel;
97082180Scg			if (c->direction == PCMDIR_PLAY) {
97182180Scg				if (c->flags & CHN_F_VIRTUAL)
97282180Scg					vc++;
97382180Scg				else
97482180Scg					pc++;
97582180Scg			} else
97682180Scg				rc++;
97782180Scg		}
97883614Scg		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
97982180Scg				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
98082180Scg#ifdef USING_DEVFS
98182180Scg				(device_get_unit(dev) == snd_unit)? " default" : ""
98282180Scg#else
98382180Scg				""
98482180Scg#endif
98582180Scg				);
986119096Scg
987119096Scg		if (verbose <= 1) {
988119096Scg			snd_mtxunlock(d->lock);
989119096Scg			return 0;
990119096Scg		}
991119096Scg
99282180Scg		SLIST_FOREACH(sce, &d->channels, link) {
99382180Scg			c = sce->channel;
994155342Snetchild
995155342Snetchild			KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
996155342Snetchild				("hosed pcm channel setup"));
997155342Snetchild
99882479Scg			sbuf_printf(s, "\n\t");
99982479Scg
1000124617Sphk			/* it would be better to indent child channels */
100182479Scg			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
100289691Scg			sbuf_printf(s, "spd %d", c->speed);
100389691Scg			if (c->speed != sndbuf_getspd(c->bufhard))
100489691Scg				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
100589691Scg			sbuf_printf(s, ", fmt 0x%08x", c->format);
100689691Scg			if (c->format != sndbuf_getfmt(c->bufhard))
100789691Scg				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1008119096Scg			sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
100982180Scg			if (c->pid != -1)
101082180Scg				sbuf_printf(s, ", pid %d", c->pid);
101182180Scg			sbuf_printf(s, "\n\t");
101289691Scg
1013155342Snetchild			sbuf_printf(s, "interrupts %d, ", c->interrupts);
1014155342Snetchild			if (c->direction == PCMDIR_REC)
1015155342Snetchild				sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1016155342Snetchild					c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
1017155342Snetchild					sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1018155342Snetchild					sndbuf_getblkcnt(c->bufhard),
1019155342Snetchild					sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1020155342Snetchild					sndbuf_getblkcnt(c->bufsoft));
1021155342Snetchild			else
1022155342Snetchild				sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1023155342Snetchild					c->xruns, sndbuf_getready(c->bufsoft),
1024155342Snetchild					sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1025155342Snetchild					sndbuf_getblkcnt(c->bufhard),
1026155342Snetchild					sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1027155342Snetchild					sndbuf_getblkcnt(c->bufsoft));
1028155342Snetchild			sbuf_printf(s, "\n\t");
102989691Scg
103089691Scg			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
103189691Scg			sbuf_printf(s, " -> ");
103282180Scg			f = c->feeder;
103389691Scg			while (f->source != NULL)
103489691Scg				f = f->source;
103589691Scg			while (f != NULL) {
103682180Scg				sbuf_printf(s, "%s", f->class->name);
103782180Scg				if (f->desc->type == FEEDER_FMT)
103889691Scg					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
103982180Scg				if (f->desc->type == FEEDER_RATE)
104089691Scg					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
1041150827Snetchild				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
1042150827Snetchild						f->desc->type == FEEDER_VOLUME)
104389691Scg					sbuf_printf(s, "(0x%08x)", f->desc->out);
104489691Scg				sbuf_printf(s, " -> ");
104589691Scg				f = f->parent;
104682180Scg			}
104789691Scg			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
104882180Scg		}
104982180Scg	} else
105082180Scg		sbuf_printf(s, " (mixer only)");
105182180Scg	snd_mtxunlock(d->lock);
105282180Scg
105382180Scg	return 0;
105482180Scg}
105582180Scg
105682180Scg/************************************************************************/
105782180Scg
105882180Scg#ifdef SND_DYNSYSCTL
105982180Scgint
106082180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
106182180Scg{
106282180Scg	struct snddev_info *d;
106382180Scg    	struct snddev_channel *sce;
106482180Scg	struct pcm_channel *c;
1065149953Snetchild	int err, newcnt, cnt;
106682180Scg
1067149953Snetchild	/*
1068149953Snetchild	 * XXX WOAH... NEED SUPER CLEANUP!!!
1069149953Snetchild	 * Robust, yet confusing. Understanding these will
1070149953Snetchild	 * cause your brain spinning like a Doki Doki Dynamo.
1071149953Snetchild	 */
107282180Scg	d = oidp->oid_arg1;
107382180Scg
1074156929Sariff	if (!(d->flags & SD_F_AUTOVCHAN))
1075149953Snetchild		return EINVAL;
1076119096Scg
107782180Scg	cnt = 0;
107882180Scg	SLIST_FOREACH(sce, &d->channels, link) {
107982180Scg		c = sce->channel;
1080125136Struckman		CHN_LOCK(c);
1081119096Scg		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
108282180Scg			cnt++;
1083149953Snetchild			if (req->newptr != NULL && c->flags & CHN_F_BUSY) {
1084149953Snetchild				/* Better safe than sorry */
1085149953Snetchild				CHN_UNLOCK(c);
1086149953Snetchild				return EBUSY;
1087149953Snetchild			}
1088119096Scg		}
1089125136Struckman		CHN_UNLOCK(c);
109082180Scg	}
1091119096Scg
109282180Scg	newcnt = cnt;
109382180Scg
109482180Scg	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
1095119096Scg
109682180Scg	if (err == 0 && req->newptr != NULL) {
1097119096Scg
1098156762Sariff		if (newcnt < 0 || newcnt > (PCMMAXCHAN + 1) ||
1099156762Sariff				(d->playcount + d->reccount + newcnt) > (PCMMAXCHAN + 1))
1100149953Snetchild			return E2BIG;
1101149953Snetchild
1102149953Snetchild		if (pcm_inprog(d, 1) != 1) {
1103119096Scg			pcm_inprog(d, -1);
1104149953Snetchild			return EINPROGRESS;
110582180Scg		}
110682180Scg
110782180Scg		if (newcnt > cnt) {
110882180Scg			/* add new vchans - find a parent channel first */
110982180Scg			SLIST_FOREACH(sce, &d->channels, link) {
111082180Scg				c = sce->channel;
1111125136Struckman				CHN_LOCK(c);
111282180Scg				/* not a candidate if not a play channel */
111382180Scg				if (c->direction != PCMDIR_PLAY)
1114125136Struckman					goto next;
111582180Scg				/* not a candidate if a virtual channel */
111682180Scg				if (c->flags & CHN_F_VIRTUAL)
1117125136Struckman					goto next;
111882180Scg				/* not a candidate if it's in use */
1119125136Struckman				if (!(c->flags & CHN_F_BUSY) ||
1120125136Struckman				    !(SLIST_EMPTY(&c->children)))
1121125136Struckman					/*
1122125136Struckman					 * if we get here we're a nonvirtual
1123125136Struckman					 * play channel, and either
1124125136Struckman					 * 1) not busy
1125125136Struckman					 * 2) busy with children, not directly
1126125136Struckman					 *    open
1127125136Struckman					 *
1128125136Struckman					 * thus we can add children
1129125136Struckman					 */
1130125136Struckman					goto addok;
1131125136Struckmannext:
1132125136Struckman				CHN_UNLOCK(c);
113382180Scg			}
1134119096Scg			pcm_inprog(d, -1);
113582180Scg			return EBUSY;
113682180Scgaddok:
113782180Scg			c->flags |= CHN_F_BUSY;
113882180Scg			while (err == 0 && newcnt > cnt) {
1139156762Sariff				if (d->devcount > PCMMAXCHAN) {
1140156762Sariff					device_printf(d->dev, "%s: Maximum channel reached.\n", __func__);
1141156762Sariff					break;
1142156762Sariff				}
114382180Scg				err = vchan_create(c);
114482180Scg				if (err == 0)
114582180Scg					cnt++;
1146156762Sariff				if (newcnt > cnt && err == E2BIG) {
1147156762Sariff					device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err);
1148156762Sariff					err = 0;
1149156762Sariff					break;
1150156762Sariff				}
115182180Scg			}
1152125136Struckman			CHN_UNLOCK(c);
115382180Scg		} else if (newcnt < cnt) {
1154119096Scg			snd_mtxlock(d->lock);
115582180Scg			while (err == 0 && newcnt < cnt) {
1156156762Sariff				c = NULL;
115782180Scg				SLIST_FOREACH(sce, &d->channels, link) {
1158156762Sariff					CHN_LOCK(sce->channel);
1159156762Sariff					if (sce->channel->direction == PCMDIR_PLAY &&
1160156765Sariff							(sce->channel->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
1161156762Sariff						c = sce->channel;
1162156762Sariff					CHN_UNLOCK(sce->channel);
116382180Scg				}
1164156762Sariff				if (c != NULL)
1165156762Sariff					goto remok;
1166119096Scg				snd_mtxunlock(d->lock);
1167119096Scg				pcm_inprog(d, -1);
116882180Scg				return EINVAL;
116982180Scgremok:
117082180Scg				err = vchan_destroy(c);
117182180Scg				if (err == 0)
117282180Scg					cnt--;
117382180Scg			}
1174119096Scg			snd_mtxunlock(d->lock);
117582180Scg		}
1176149953Snetchild		pcm_inprog(d, -1);
117782180Scg	}
117882180Scg	return err;
117982180Scg}
118082180Scg#endif
118182180Scg
118282180Scg/************************************************************************/
118382180Scg
1184132236Stanimurastatic int
1185132236Stanimurasound_modevent(module_t mod, int type, void *data)
1186132236Stanimura{
1187148587Snetchild#if 0
1188132236Stanimura	return (midi_modevent(mod, type, data));
1189148587Snetchild#else
1190148587Snetchild	return 0;
1191148587Snetchild#endif
1192132236Stanimura}
1193132236Stanimura
1194132236StanimuraDEV_MODULE(sound, sound_modevent, NULL);
1195132236StanimuraMODULE_VERSION(sound, SOUND_MODVER);
1196