sound.c revision 83476
150724Scg/*
250724Scg * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
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>
3065207Scg#include <sys/sysctl.h>
3150724Scg
3282180Scg#include "feeder_if.h"
3382180Scg
3482180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 83476 2001-09-14 20:26:03Z greid $");
3582180Scg
3682180Scgstruct snddev_channel {
3782180Scg	SLIST_ENTRY(snddev_channel) link;
3882180Scg	struct pcm_channel *channel;
3982180Scg};
4082180Scg
4182180Scgstruct snddev_info {
4282180Scg	SLIST_HEAD(, snddev_channel) channels;
4382180Scg	struct pcm_channel *fakechan;
4483089Scg	unsigned devcount, reccount, chancount, vchancount;
4582180Scg	unsigned flags;
4682180Scg	int inprog;
4782180Scg	void *devinfo;
4882180Scg	device_t dev;
4982180Scg	char status[SND_STATUSLEN];
5082180Scg	struct sysctl_ctx_list sysctl_tree;
5182180Scg	struct sysctl_oid *sysctl_tree_top;
5282180Scg	void *lock;
5382180Scg};
5482180Scg
5578362Scgdevclass_t pcm_devclass;
5650724Scg
5773127Scg#ifdef USING_DEVFS
5878362Scgint snd_unit = 0;
5977900SpeterTUNABLE_INT("hw.snd.unit", &snd_unit);
6073127Scg#endif
6179141Scg
6282180Scgint snd_maxautovchans = 0;
6382180ScgTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
6450724Scg
6573127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
6673127Scg
6782180Scgstatic int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
6882180Scg
6982180Scgstruct sysctl_ctx_list *
7082180Scgsnd_sysctl_tree(device_t dev)
7182180Scg{
7282180Scg    	struct snddev_info *d = device_get_softc(dev);
7382180Scg
7482180Scg	return &d->sysctl_tree;
7582180Scg}
7682180Scg
7782180Scgstruct sysctl_oid *
7882180Scgsnd_sysctl_tree_top(device_t dev)
7982180Scg{
8082180Scg    	struct snddev_info *d = device_get_softc(dev);
8182180Scg
8282180Scg	return d->sysctl_tree_top;
8382180Scg}
8482180Scg
8573131Scgvoid *
8673131Scgsnd_mtxcreate(const char *desc)
8773131Scg{
8873131Scg#ifdef USING_MUTEX
8973131Scg	struct mtx *m;
9073131Scg
9173131Scg	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
9273131Scg	if (m == NULL)
9373131Scg		return NULL;
9473131Scg	mtx_init(m, desc, MTX_RECURSE);
9573131Scg	return m;
9673131Scg#else
9773757Scg	return (void *)0xcafebabe;
9873131Scg#endif
9973131Scg}
10073131Scg
10173131Scgvoid
10273131Scgsnd_mtxfree(void *m)
10373131Scg{
10473131Scg#ifdef USING_MUTEX
10573131Scg	struct mtx *mtx = m;
10673131Scg
10773131Scg	mtx_assert(mtx, MA_OWNED);
10873131Scg	mtx_destroy(mtx);
10973131Scg	free(mtx, M_DEVBUF);
11073131Scg#endif
11173131Scg}
11273131Scg
11373131Scgvoid
11473131Scgsnd_mtxassert(void *m)
11573131Scg{
11673131Scg#ifdef USING_MUTEX
11778670Scg#ifdef INVARIANTS
11873131Scg	struct mtx *mtx = m;
11973131Scg
12073131Scg	mtx_assert(mtx, MA_OWNED);
12173131Scg#endif
12278670Scg#endif
12373131Scg}
12473131Scg
12573131Scgvoid
12673131Scgsnd_mtxlock(void *m)
12773131Scg{
12873131Scg#ifdef USING_MUTEX
12973131Scg	struct mtx *mtx = m;
13073131Scg
13173131Scg	mtx_lock(mtx);
13273131Scg#endif
13373131Scg}
13473131Scg
13573131Scgvoid
13673131Scgsnd_mtxunlock(void *m)
13773131Scg{
13873131Scg#ifdef USING_MUTEX
13973131Scg	struct mtx *mtx = m;
14073131Scg
14173131Scg	mtx_unlock(mtx);
14273131Scg#endif
14373131Scg}
14473131Scg
14573131Scgint
14673131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
14773131Scg{
14873131Scg#ifdef USING_MUTEX
14973131Scg	flags &= INTR_MPSAFE;
15078366Speter	flags |= INTR_TYPE_AV;
15173131Scg#else
15278366Speter	flags = INTR_TYPE_AV;
15373131Scg#endif
15473131Scg	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
15573131Scg}
15673131Scg
15782180Scgvoid
15882180Scgpcm_lock(struct snddev_info *d)
15982180Scg{
16082180Scg	snd_mtxlock(d->lock);
16182180Scg}
16282180Scg
16382180Scgvoid
16482180Scgpcm_unlock(struct snddev_info *d)
16582180Scg{
16682180Scg	snd_mtxunlock(d->lock);
16782180Scg}
16882180Scg
16982180Scgstruct pcm_channel *
17082180Scgpcm_getfakechan(struct snddev_info *d)
17182180Scg{
17282180Scg	return d->fakechan;
17382180Scg}
17482180Scg
17578214Scg/* return a locked channel */
17677269Scgstruct pcm_channel *
17783089Scgpcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
17877269Scg{
17977269Scg	struct pcm_channel *c;
18077269Scg    	struct snddev_channel *sce;
18178895Scg	int err;
18277269Scg
18378214Scg	snd_mtxassert(d->lock);
18478895Scg
18578895Scg	/* scan for a free channel */
18677269Scg	SLIST_FOREACH(sce, &d->channels, link) {
18777269Scg		c = sce->channel;
18877882Scg		CHN_LOCK(c);
18977269Scg		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
19083089Scg			if (chnum == -1 || c->num == chnum) {
19183089Scg				c->flags |= CHN_F_BUSY;
19283089Scg				c->pid = pid;
19383089Scg				return c;
19483089Scg			}
19577269Scg		}
19677882Scg		CHN_UNLOCK(c);
19777269Scg	}
19878895Scg
19978895Scg	/* no channel available */
20078895Scg	if (direction == PCMDIR_PLAY) {
20182180Scg		if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
20278895Scg			/* try to create a vchan */
20378895Scg			SLIST_FOREACH(sce, &d->channels, link) {
20478895Scg				c = sce->channel;
20578895Scg				if (!SLIST_EMPTY(&c->children)) {
20678895Scg					err = vchan_create(c);
20778895Scg					if (!err)
20883089Scg						return pcm_chnalloc(d, direction, pid, -1);
20978895Scg					else
21078895Scg						device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
21178895Scg				}
21278895Scg			}
21378895Scg		}
21478895Scg	}
21578895Scg
21677269Scg	return NULL;
21777269Scg}
21877269Scg
21978214Scg/* release a locked channel and unlock it */
22077269Scgint
22178214Scgpcm_chnrelease(struct pcm_channel *c)
22277269Scg{
22378214Scg	CHN_LOCKASSERT(c);
22477269Scg	c->flags &= ~CHN_F_BUSY;
22578214Scg	c->pid = -1;
22677882Scg	CHN_UNLOCK(c);
22777269Scg	return 0;
22877269Scg}
22977269Scg
23077269Scgint
23177269Scgpcm_chnref(struct pcm_channel *c, int ref)
23277269Scg{
23377882Scg	int r;
23477882Scg
23578214Scg	CHN_LOCKASSERT(c);
23677269Scg	c->refcount += ref;
23777882Scg	r = c->refcount;
23877882Scg	return r;
23977269Scg}
24077269Scg
24182180Scgint
24282180Scgpcm_inprog(struct snddev_info *d, int delta)
24382180Scg{
24482180Scg	d->inprog += delta;
24582180Scg	return d->inprog;
24682180Scg}
24782180Scg
24882180Scgstatic void
24982180Scgpcm_setmaxautovchans(struct snddev_info *d, int num)
25082180Scg{
25182180Scg	struct pcm_channel *c;
25282180Scg    	struct snddev_channel *sce;
25382180Scg	int err, done;
25482180Scg
25582180Scg	if (num > 0 && d->vchancount == 0) {
25682180Scg		SLIST_FOREACH(sce, &d->channels, link) {
25782180Scg			c = sce->channel;
25882180Scg			if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
25982180Scg				c->flags |= CHN_F_BUSY;
26082180Scg				err = vchan_create(c);
26182180Scg				if (err) {
26282180Scg					device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
26382180Scg					c->flags &= ~CHN_F_BUSY;
26482180Scg				}
26582180Scg				return;
26682180Scg			}
26782180Scg		}
26882180Scg	}
26982180Scg	if (num == 0 && d->vchancount > 0) {
27082180Scg		done = 0;
27182180Scg		while (!done) {
27282180Scg			done = 1;
27382180Scg			SLIST_FOREACH(sce, &d->channels, link) {
27482180Scg				c = sce->channel;
27582180Scg				if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
27682180Scg					done = 0;
27782180Scg					err = vchan_destroy(c);
27882180Scg					if (err)
27982180Scg						device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
28082180Scg					goto restart;
28182180Scg				}
28282180Scg			}
28382180Scgrestart:
28482180Scg		}
28582180Scg	}
28682180Scg}
28782180Scg
28873127Scg#ifdef USING_DEVFS
28965340Scgstatic int
29078853Scgsysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
29165340Scg{
29278214Scg	struct snddev_info *d;
29365340Scg	int error, unit;
29465340Scg
29565340Scg	unit = snd_unit;
29665340Scg	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
29765340Scg	if (error == 0 && req->newptr != NULL) {
29878396Scg		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
29978214Scg			return EINVAL;
30078214Scg		d = devclass_get_softc(pcm_devclass, unit);
30178395Scg		if (d == NULL || SLIST_EMPTY(&d->channels))
30278214Scg			return EINVAL;
30365340Scg		snd_unit = unit;
30465340Scg	}
30565340Scg	return (error);
30665340Scg}
30770617SjhbSYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
30878853Scg            0, sizeof(int), sysctl_hw_snd_unit, "I", "");
30973127Scg#endif
31065340Scg
31178853Scgstatic int
31282180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
31378853Scg{
31482180Scg	struct snddev_info *d;
31582180Scg	int i, v, error;
31678853Scg
31782180Scg	v = snd_maxautovchans;
31878853Scg	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
31978853Scg	if (error == 0 && req->newptr != NULL) {
32078853Scg		if (v < 0 || v >= SND_MAXVCHANS)
32178853Scg			return EINVAL;
32282180Scg		if (v != snd_maxautovchans) {
32382180Scg			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
32482180Scg				d = devclass_get_softc(pcm_devclass, i);
32582180Scg				if (!d)
32682180Scg					continue;
32782180Scg				pcm_setmaxautovchans(d, v);
32882180Scg			}
32982180Scg		}
33082180Scg		snd_maxautovchans = v;
33178853Scg	}
33278853Scg	return (error);
33378853Scg}
33482180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
33582180Scg            0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
33678853Scg
33777269Scgstruct pcm_channel *
33877269Scgpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
33950724Scg{
34077269Scg	struct pcm_channel *ch;
34165340Scg	char *dirs;
34277269Scg    	int err;
34350724Scg
34477269Scg	switch(dir) {
34577269Scg	case PCMDIR_PLAY:
34677269Scg		dirs = "play";
34777269Scg		break;
34877269Scg	case PCMDIR_REC:
34977269Scg		dirs = "record";
35077269Scg		break;
35177269Scg	case PCMDIR_VIRTUAL:
35277269Scg		dirs = "virtual";
35377269Scg		dir = PCMDIR_PLAY;
35477269Scg		break;
35577269Scg	default:
35677269Scg		return NULL;
35777269Scg	}
35865340Scg
35977269Scg	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
36077269Scg	if (!ch)
36177269Scg		return NULL;
36277269Scg
36377269Scg	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
36477269Scg	if (!ch->methods) {
36577269Scg		free(ch, M_DEVBUF);
36677269Scg		return NULL;
36761344Scg	}
36877269Scg
36977269Scg	ch->pid = -1;
37077269Scg	ch->parentsnddev = d;
37177269Scg	ch->parentchannel = parent;
37277269Scg	snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs);
37377269Scg
37470134Scg	err = chn_init(ch, devinfo, dir);
37570134Scg	if (err) {
37677269Scg		device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err);
37777269Scg		kobj_delete(ch->methods, M_DEVBUF);
37877269Scg		free(ch, M_DEVBUF);
37977269Scg		return NULL;
38055483Scg	}
38177269Scg
38277269Scg	return ch;
38377269Scg}
38477269Scg
38577269Scgint
38677269Scgpcm_chn_destroy(struct pcm_channel *ch)
38777269Scg{
38877269Scg	int err;
38977269Scg
39077269Scg	err = chn_kill(ch);
39177269Scg	if (err) {
39277269Scg		device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err);
39377269Scg		return err;
39477269Scg	}
39577269Scg
39677269Scg	kobj_delete(ch->methods, M_DEVBUF);
39777269Scg	free(ch, M_DEVBUF);
39877269Scg
39977269Scg	return 0;
40077269Scg}
40177269Scg
40277269Scgint
40378895Scgpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
40477269Scg{
40582180Scg    	struct snddev_channel *sce, *tmp, *after;
40677269Scg    	int unit = device_get_unit(d->dev);
40777269Scg
40878214Scg	snd_mtxlock(d->lock);
40978214Scg
41077269Scg	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
41177269Scg	if (!sce) {
41278214Scg		snd_mtxunlock(d->lock);
41377269Scg		return ENOMEM;
41477269Scg	}
41577269Scg
41677269Scg	sce->channel = ch;
41782180Scg	if (SLIST_EMPTY(&d->channels)) {
41882180Scg		SLIST_INSERT_HEAD(&d->channels, sce, link);
41982180Scg	} else {
42082180Scg		after = NULL;
42182180Scg		SLIST_FOREACH(tmp, &d->channels, link) {
42282180Scg			after = tmp;
42382180Scg		}
42482180Scg		SLIST_INSERT_AFTER(after, sce, link);
42582180Scg	}
42677269Scg
42783089Scg	if (ch->direction == PCMDIR_REC)
42883089Scg		ch->num = d->reccount++;
42983089Scg/*
43083089Scg	else
43183089Scg		ch->num = d->playcount++;
43283089Scg*/
43383089Scg
43483089Scg	if (mkdev) {
43578895Scg		dsp_register(unit, d->devcount++);
43683089Scg		if (ch->direction == PCMDIR_REC)
43783089Scg			dsp_registerrec(unit, ch->num);
43883089Scg	}
43978362Scg    	d->chancount++;
44078895Scg	if (ch->flags & CHN_F_VIRTUAL)
44178895Scg		d->vchancount++;
44277269Scg
44377882Scg	snd_mtxunlock(d->lock);
44477269Scg
44550724Scg	return 0;
44650724Scg}
44750724Scg
44877269Scgint
44978895Scgpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
45065340Scg{
45177269Scg    	struct snddev_channel *sce;
45277269Scg    	int unit = device_get_unit(d->dev);
45365340Scg
45477882Scg	snd_mtxlock(d->lock);
45577269Scg	SLIST_FOREACH(sce, &d->channels, link) {
45677269Scg		if (sce->channel == ch)
45777269Scg			goto gotit;
45865340Scg	}
45977882Scg	snd_mtxunlock(d->lock);
46077269Scg	return EINVAL;
46177269Scggotit:
46278895Scg	if (ch->flags & CHN_F_VIRTUAL)
46378895Scg		d->vchancount--;
46465340Scg	d->chancount--;
46577269Scg	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
46677269Scg	free(sce, M_DEVBUF);
46777269Scg
46883089Scg	if (rmdev) {
46978895Scg		dsp_unregister(unit, --d->devcount);
47083089Scg		if (ch->direction == PCMDIR_REC)
47183089Scg			dsp_unregisterrec(unit, --d->reccount);
47283089Scg	}
47377882Scg	snd_mtxunlock(d->lock);
47477269Scg
47565340Scg	return 0;
47665340Scg}
47765340Scg
47850724Scgint
47977269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
48077269Scg{
48177269Scg    	struct snddev_info *d = device_get_softc(dev);
48282180Scg	struct pcm_channel *ch;
48382180Scg    	int err;
48477269Scg
48577269Scg	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
48677269Scg	if (!ch) {
48777269Scg		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
48877269Scg		return ENODEV;
48977269Scg	}
49078853Scg
49178895Scg	err = pcm_chn_add(d, ch, 1);
49277269Scg	if (err) {
49377269Scg		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
49477269Scg		pcm_chn_destroy(ch);
49578853Scg		return err;
49677269Scg	}
49777269Scg
49882180Scg	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) {
49978853Scg		ch->flags |= CHN_F_BUSY;
50082180Scg		err = vchan_create(ch);
50178853Scg		if (err) {
50282180Scg			device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
50382180Scg			ch->flags &= ~CHN_F_BUSY;
50478853Scg		}
50578853Scg	}
50678853Scg
50777269Scg	return err;
50877269Scg}
50977269Scg
51077269Scgstatic int
51177269Scgpcm_killchan(device_t dev)
51277269Scg{
51377269Scg    	struct snddev_info *d = device_get_softc(dev);
51477269Scg    	struct snddev_channel *sce;
51577269Scg
51677882Scg	snd_mtxlock(d->lock);
51777269Scg	sce = SLIST_FIRST(&d->channels);
51877882Scg	snd_mtxunlock(d->lock);
51977269Scg
52078895Scg	return pcm_chn_remove(d, sce->channel, 1);
52177269Scg}
52277269Scg
52377269Scgint
52450724Scgpcm_setstatus(device_t dev, char *str)
52550724Scg{
52674763Scg    	struct snddev_info *d = device_get_softc(dev);
52777882Scg
52877882Scg	snd_mtxlock(d->lock);
52950724Scg	strncpy(d->status, str, SND_STATUSLEN);
53077882Scg	snd_mtxunlock(d->lock);
53150724Scg	return 0;
53250724Scg}
53350724Scg
53450724Scgu_int32_t
53550724Scgpcm_getflags(device_t dev)
53650724Scg{
53774763Scg    	struct snddev_info *d = device_get_softc(dev);
53877882Scg
53950724Scg	return d->flags;
54050724Scg}
54150724Scg
54250724Scgvoid
54350724Scgpcm_setflags(device_t dev, u_int32_t val)
54450724Scg{
54574763Scg    	struct snddev_info *d = device_get_softc(dev);
54678362Scg
54750724Scg	d->flags = val;
54850724Scg}
54950724Scg
55058384Scgvoid *
55158384Scgpcm_getdevinfo(device_t dev)
55258384Scg{
55374763Scg    	struct snddev_info *d = device_get_softc(dev);
55477882Scg
55558384Scg	return d->devinfo;
55658384Scg}
55758384Scg
55850724Scgint
55950724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec)
56050724Scg{
56174763Scg    	struct snddev_info *d = device_get_softc(dev);
56250724Scg
56377882Scg	d->lock = snd_mtxcreate(device_get_nameunit(dev));
56477882Scg	snd_mtxlock(d->lock);
56577269Scg
56678853Scg	d->flags = 0;
56761886Scg	d->dev = dev;
56850724Scg	d->devinfo = devinfo;
56978895Scg	d->devcount = 0;
57083089Scg	d->reccount = 0;
57177269Scg	d->chancount = 0;
57278895Scg	d->vchancount = 0;
57378362Scg	d->inprog = 0;
57450724Scg
57578362Scg	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
57678214Scg		d->flags |= SD_F_SIMPLEX;
57778362Scg
57878214Scg	d->fakechan = fkchan_setup(dev);
57978214Scg	chn_init(d->fakechan, NULL, 0);
58055494Scg
58173127Scg#ifdef SND_DYNSYSCTL
58270680Sjhb	sysctl_ctx_init(&d->sysctl_tree);
58370680Sjhb	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
58470943Sjhb				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
58570680Sjhb				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
58670943Sjhb	if (d->sysctl_tree_top == NULL) {
58770680Sjhb		sysctl_ctx_free(&d->sysctl_tree);
58870680Sjhb		goto no;
58970680Sjhb	}
59073127Scg#endif
59178214Scg	if (numplay > 0)
59282180Scg		vchan_initsys(dev);
59378853Scg	if (numplay == 1)
59478853Scg		d->flags |= SD_F_AUTOVCHAN;
59578853Scg
59677882Scg	snd_mtxunlock(d->lock);
59782180Scg	sndstat_register(dev, d->status, sndstat_prepare_pcm);
59850724Scg    	return 0;
59950724Scgno:
60077882Scg	snd_mtxfree(d->lock);
60150724Scg	return ENXIO;
60250724Scg}
60350724Scg
60465340Scgint
60565340Scgpcm_unregister(device_t dev)
60665207Scg{
60774763Scg    	struct snddev_info *d = device_get_softc(dev);
60877269Scg    	struct snddev_channel *sce;
60965207Scg
61077882Scg	snd_mtxlock(d->lock);
61178362Scg	if (d->inprog) {
61283476Sgreid		device_printf(dev, "unregister: operation in progress\n");
61378362Scg		snd_mtxunlock(d->lock);
61478362Scg		return EBUSY;
61578362Scg	}
61683476Sgreid	if (sndstat_busy() != 0) {
61783476Sgreid		device_printf(dev, "unregister: sndstat busy\n");
61883476Sgreid		snd_mtxunlock(d->lock);
61983476Sgreid		return EBUSY;
62083476Sgreid	}
62177269Scg	SLIST_FOREACH(sce, &d->channels, link) {
62277269Scg		if (sce->channel->refcount > 0) {
62383476Sgreid			device_printf(dev, "unregister: channel busy\n");
62477882Scg			snd_mtxunlock(d->lock);
62577269Scg			return EBUSY;
62677269Scg		}
62765487Scg	}
62878362Scg	if (mixer_uninit(dev)) {
62983476Sgreid		device_printf(dev, "unregister: mixer busy\n");
63077882Scg		snd_mtxunlock(d->lock);
63165487Scg		return EBUSY;
63265487Scg	}
63365207Scg
63474763Scg#ifdef SND_DYNSYSCTL
63574763Scg	d->sysctl_tree_top = NULL;
63674763Scg	sysctl_ctx_free(&d->sysctl_tree);
63774763Scg#endif
63878395Scg	while (!SLIST_EMPTY(&d->channels))
63977269Scg		pcm_killchan(dev);
64065207Scg
64174763Scg	chn_kill(d->fakechan);
64274763Scg	fkchan_kill(d->fakechan);
64373127Scg
64477882Scg	snd_mtxfree(d->lock);
64565340Scg	return 0;
64665207Scg}
64765207Scg
64882180Scg/************************************************************************/
64982180Scg
65082180Scgstatic int
65182180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
65282180Scg{
65382180Scg    	struct snddev_info *d;
65482180Scg    	struct snddev_channel *sce;
65582180Scg	struct pcm_channel *c;
65682180Scg	struct pcm_feeder *f;
65782479Scg	char *fsep;
65882180Scg    	int pc, rc, vc;
65982180Scg
66082180Scg	if (verbose < 1)
66182180Scg		return 0;
66282180Scg
66382180Scg	d = device_get_softc(dev);
66482180Scg	if (!d)
66582180Scg		return ENXIO;
66682180Scg
66782180Scg	snd_mtxlock(d->lock);
66882180Scg	if (!SLIST_EMPTY(&d->channels)) {
66982180Scg		pc = rc = vc = 0;
67082180Scg		SLIST_FOREACH(sce, &d->channels, link) {
67182180Scg			c = sce->channel;
67282180Scg			if (c->direction == PCMDIR_PLAY) {
67382180Scg				if (c->flags & CHN_F_VIRTUAL)
67482180Scg					vc++;
67582180Scg				else
67682180Scg					pc++;
67782180Scg			} else
67882180Scg				rc++;
67982180Scg		}
68082180Scg		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", pc, rc, vc,
68182180Scg				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
68282180Scg#ifdef USING_DEVFS
68382180Scg				(device_get_unit(dev) == snd_unit)? " default" : ""
68482180Scg#else
68582180Scg				""
68682180Scg#endif
68782180Scg				);
68882180Scg		if (verbose <= 1)
68982180Scg			goto skipverbose;
69082180Scg		SLIST_FOREACH(sce, &d->channels, link) {
69182180Scg			c = sce->channel;
69282479Scg			sbuf_printf(s, "\n\t");
69382479Scg
69482479Scg			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
69582479Scg			sbuf_printf(s, "speed %d, format %08x, flags %08x", c->speed, c->format, c->flags);
69682180Scg			if (c->pid != -1)
69782180Scg				sbuf_printf(s, ", pid %d", c->pid);
69882180Scg			sbuf_printf(s, "\n\t");
69982492Scg			if (c->bufhard != NULL && c->bufsoft != NULL) {
70082479Scg				sbuf_printf(s, "interrupts %d, ", c->interrupts);
70182479Scg				if (c->direction == PCMDIR_REC)
70282479Scg					sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
70382479Scg						c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
70482479Scg				else
70582492Scg					sbuf_printf(s, "underruns %d, ready %d",
70682492Scg						c->xruns, sndbuf_getready(c->bufsoft));
70782479Scg				sbuf_printf(s, "\n\t");
70882479Scg			}
70982479Scg			fsep = (c->direction == PCMDIR_REC)? " -> " : " <- ";
71082492Scg			sbuf_printf(s, "{hardware}%s", fsep);
71182180Scg			f = c->feeder;
71282180Scg			while (f) {
71382180Scg				sbuf_printf(s, "%s", f->class->name);
71482180Scg				if (f->desc->type == FEEDER_FMT)
71582479Scg					sbuf_printf(s, "(%08x%s%08x)", f->desc->out, fsep, f->desc->in);
71682180Scg				if (f->desc->type == FEEDER_RATE)
71782479Scg					sbuf_printf(s, "(%d%s%d)", FEEDER_GET(f, FEEDRATE_DST), fsep, FEEDER_GET(f, FEEDRATE_SRC));
71882180Scg				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
71982180Scg					sbuf_printf(s, "(%08x)", f->desc->out);
72082479Scg				sbuf_printf(s, "%s", fsep);
72182180Scg				f = f->source;
72282180Scg			}
72382492Scg			sbuf_printf(s, "{userland}");
72482180Scg		}
72582180Scgskipverbose:
72682180Scg	} else
72782180Scg		sbuf_printf(s, " (mixer only)");
72882180Scg	snd_mtxunlock(d->lock);
72982180Scg
73082180Scg	return 0;
73182180Scg}
73282180Scg
73382180Scg/************************************************************************/
73482180Scg
73582180Scg#ifdef SND_DYNSYSCTL
73682180Scgint
73782180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
73882180Scg{
73982180Scg	struct snddev_info *d;
74082180Scg    	struct snddev_channel *sce;
74182180Scg	struct pcm_channel *c;
74282180Scg	int err, oldcnt, newcnt, cnt;
74382180Scg
74482180Scg	d = oidp->oid_arg1;
74582180Scg
74682180Scg	pcm_lock(d);
74782180Scg	cnt = 0;
74882180Scg	SLIST_FOREACH(sce, &d->channels, link) {
74982180Scg		c = sce->channel;
75082180Scg		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
75182180Scg			cnt++;
75282180Scg	}
75382180Scg	oldcnt = cnt;
75482180Scg	newcnt = cnt;
75582180Scg
75682180Scg	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
75782180Scg	if (err == 0 && req->newptr != NULL) {
75882180Scg		if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
75982180Scg			pcm_unlock(d);
76082180Scg			return EINVAL;
76182180Scg		}
76282180Scg
76382180Scg		if (newcnt > cnt) {
76482180Scg			/* add new vchans - find a parent channel first */
76582180Scg			SLIST_FOREACH(sce, &d->channels, link) {
76682180Scg				c = sce->channel;
76782180Scg				/* not a candidate if not a play channel */
76882180Scg				if (c->direction != PCMDIR_PLAY)
76982180Scg					goto addskip;
77082180Scg				/* not a candidate if a virtual channel */
77182180Scg				if (c->flags & CHN_F_VIRTUAL)
77282180Scg					goto addskip;
77382180Scg				/* not a candidate if it's in use */
77482180Scg				if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
77582180Scg					goto addskip;
77682180Scg				/*
77782180Scg				 * if we get here we're a nonvirtual play channel, and either
77882180Scg				 * 1) not busy
77982180Scg				 * 2) busy with children, not directly open
78082180Scg				 *
78182180Scg				 * thus we can add children
78282180Scg				 */
78382180Scg				goto addok;
78482180Scgaddskip:
78582180Scg			}
78682180Scg			pcm_unlock(d);
78782180Scg			return EBUSY;
78882180Scgaddok:
78982180Scg			c->flags |= CHN_F_BUSY;
79082180Scg			while (err == 0 && newcnt > cnt) {
79182180Scg				err = vchan_create(c);
79282180Scg				if (err == 0)
79382180Scg					cnt++;
79482180Scg			}
79582180Scg			if (SLIST_EMPTY(&c->children))
79682180Scg				c->flags &= ~CHN_F_BUSY;
79782180Scg		} else if (newcnt < cnt) {
79882180Scg			while (err == 0 && newcnt < cnt) {
79982180Scg				SLIST_FOREACH(sce, &d->channels, link) {
80082180Scg					c = sce->channel;
80182180Scg					if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
80282180Scg						goto remok;
80382180Scg				}
80482180Scg				pcm_unlock(d);
80582180Scg				return EINVAL;
80682180Scgremok:
80782180Scg				err = vchan_destroy(c);
80882180Scg				if (err == 0)
80982180Scg					cnt--;
81082180Scg			}
81182180Scg		}
81282180Scg	}
81382180Scg
81482180Scg	pcm_unlock(d);
81582180Scg	return err;
81682180Scg}
81782180Scg#endif
81882180Scg
81982180Scg/************************************************************************/
82082180Scg
82165340Scgstatic moduledata_t sndpcm_mod = {
82265340Scg	"snd_pcm",
82378362Scg	NULL,
82465340Scg	NULL
82565340Scg};
82665340ScgDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
82765340ScgMODULE_VERSION(snd_pcm, PCM_MODVER);
828