sound.c revision 93814
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 93814 2002-04-04 20:54:27Z jhb $");
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;
4483614Scg	unsigned devcount, playcount, reccount, vchancount;
4582180Scg	unsigned flags;
4682180Scg	int inprog;
4783614Scg	unsigned int bufsz;
4882180Scg	void *devinfo;
4982180Scg	device_t dev;
5082180Scg	char status[SND_STATUSLEN];
5182180Scg	struct sysctl_ctx_list sysctl_tree;
5282180Scg	struct sysctl_oid *sysctl_tree_top;
5382180Scg	void *lock;
5482180Scg};
5582180Scg
5678362Scgdevclass_t pcm_devclass;
5750724Scg
5889834Scgint pcm_veto_load = 1;
5989834Scg
6073127Scg#ifdef USING_DEVFS
6178362Scgint snd_unit = 0;
6277900SpeterTUNABLE_INT("hw.snd.unit", &snd_unit);
6373127Scg#endif
6479141Scg
6582180Scgint snd_maxautovchans = 0;
6682180ScgTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
6750724Scg
6873127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
6973127Scg
7082180Scgstatic int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
7182180Scg
7282180Scgstruct sysctl_ctx_list *
7382180Scgsnd_sysctl_tree(device_t dev)
7482180Scg{
7582180Scg    	struct snddev_info *d = device_get_softc(dev);
7682180Scg
7782180Scg	return &d->sysctl_tree;
7882180Scg}
7982180Scg
8082180Scgstruct sysctl_oid *
8182180Scgsnd_sysctl_tree_top(device_t dev)
8282180Scg{
8382180Scg    	struct snddev_info *d = device_get_softc(dev);
8482180Scg
8582180Scg	return d->sysctl_tree_top;
8682180Scg}
8782180Scg
8873131Scgvoid *
8993814Sjhbsnd_mtxcreate(const char *desc, const char *type)
9073131Scg{
9173131Scg#ifdef USING_MUTEX
9273131Scg	struct mtx *m;
9373131Scg
9473131Scg	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
9573131Scg	if (m == NULL)
9673131Scg		return NULL;
9793814Sjhb	mtx_init(m, desc, type, MTX_RECURSE);
9873131Scg	return m;
9973131Scg#else
10073757Scg	return (void *)0xcafebabe;
10173131Scg#endif
10273131Scg}
10373131Scg
10473131Scgvoid
10573131Scgsnd_mtxfree(void *m)
10673131Scg{
10773131Scg#ifdef USING_MUTEX
10873131Scg	struct mtx *mtx = m;
10973131Scg
11073131Scg	mtx_assert(mtx, MA_OWNED);
11173131Scg	mtx_destroy(mtx);
11273131Scg	free(mtx, M_DEVBUF);
11373131Scg#endif
11473131Scg}
11573131Scg
11673131Scgvoid
11773131Scgsnd_mtxassert(void *m)
11873131Scg{
11973131Scg#ifdef USING_MUTEX
12078670Scg#ifdef INVARIANTS
12173131Scg	struct mtx *mtx = m;
12273131Scg
12373131Scg	mtx_assert(mtx, MA_OWNED);
12473131Scg#endif
12578670Scg#endif
12673131Scg}
12773131Scg
12873131Scgvoid
12973131Scgsnd_mtxlock(void *m)
13073131Scg{
13173131Scg#ifdef USING_MUTEX
13273131Scg	struct mtx *mtx = m;
13373131Scg
13473131Scg	mtx_lock(mtx);
13573131Scg#endif
13673131Scg}
13773131Scg
13873131Scgvoid
13973131Scgsnd_mtxunlock(void *m)
14073131Scg{
14173131Scg#ifdef USING_MUTEX
14273131Scg	struct mtx *mtx = m;
14373131Scg
14473131Scg	mtx_unlock(mtx);
14573131Scg#endif
14673131Scg}
14773131Scg
14873131Scgint
14973131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
15073131Scg{
15173131Scg#ifdef USING_MUTEX
15273131Scg	flags &= INTR_MPSAFE;
15378366Speter	flags |= INTR_TYPE_AV;
15473131Scg#else
15578366Speter	flags = INTR_TYPE_AV;
15673131Scg#endif
15773131Scg	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
15873131Scg}
15973131Scg
16082180Scgvoid
16182180Scgpcm_lock(struct snddev_info *d)
16282180Scg{
16382180Scg	snd_mtxlock(d->lock);
16482180Scg}
16582180Scg
16682180Scgvoid
16782180Scgpcm_unlock(struct snddev_info *d)
16882180Scg{
16982180Scg	snd_mtxunlock(d->lock);
17082180Scg}
17182180Scg
17282180Scgstruct pcm_channel *
17382180Scgpcm_getfakechan(struct snddev_info *d)
17482180Scg{
17582180Scg	return d->fakechan;
17682180Scg}
17782180Scg
17878214Scg/* return a locked channel */
17977269Scgstruct pcm_channel *
18083089Scgpcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
18177269Scg{
18277269Scg	struct pcm_channel *c;
18377269Scg    	struct snddev_channel *sce;
18478895Scg	int err;
18577269Scg
18678214Scg	snd_mtxassert(d->lock);
18778895Scg
18878895Scg	/* scan for a free channel */
18977269Scg	SLIST_FOREACH(sce, &d->channels, link) {
19077269Scg		c = sce->channel;
19177882Scg		CHN_LOCK(c);
19277269Scg		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
19383089Scg			if (chnum == -1 || c->num == chnum) {
19483089Scg				c->flags |= CHN_F_BUSY;
19583089Scg				c->pid = pid;
19683089Scg				return c;
19783089Scg			}
19877269Scg		}
19977882Scg		CHN_UNLOCK(c);
20077269Scg	}
20178895Scg
20278895Scg	/* no channel available */
20378895Scg	if (direction == PCMDIR_PLAY) {
20482180Scg		if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
20578895Scg			/* try to create a vchan */
20678895Scg			SLIST_FOREACH(sce, &d->channels, link) {
20778895Scg				c = sce->channel;
20878895Scg				if (!SLIST_EMPTY(&c->children)) {
20978895Scg					err = vchan_create(c);
21078895Scg					if (!err)
21183089Scg						return pcm_chnalloc(d, direction, pid, -1);
21278895Scg					else
21378895Scg						device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
21478895Scg				}
21578895Scg			}
21678895Scg		}
21778895Scg	}
21878895Scg
21977269Scg	return NULL;
22077269Scg}
22177269Scg
22278214Scg/* release a locked channel and unlock it */
22377269Scgint
22478214Scgpcm_chnrelease(struct pcm_channel *c)
22577269Scg{
22678214Scg	CHN_LOCKASSERT(c);
22777269Scg	c->flags &= ~CHN_F_BUSY;
22878214Scg	c->pid = -1;
22977882Scg	CHN_UNLOCK(c);
23077269Scg	return 0;
23177269Scg}
23277269Scg
23377269Scgint
23477269Scgpcm_chnref(struct pcm_channel *c, int ref)
23577269Scg{
23677882Scg	int r;
23777882Scg
23878214Scg	CHN_LOCKASSERT(c);
23977269Scg	c->refcount += ref;
24077882Scg	r = c->refcount;
24177882Scg	return r;
24277269Scg}
24377269Scg
24482180Scgint
24582180Scgpcm_inprog(struct snddev_info *d, int delta)
24682180Scg{
24782180Scg	d->inprog += delta;
24882180Scg	return d->inprog;
24982180Scg}
25082180Scg
25182180Scgstatic void
25282180Scgpcm_setmaxautovchans(struct snddev_info *d, int num)
25382180Scg{
25482180Scg	struct pcm_channel *c;
25582180Scg    	struct snddev_channel *sce;
25682180Scg	int err, done;
25782180Scg
25882180Scg	if (num > 0 && d->vchancount == 0) {
25982180Scg		SLIST_FOREACH(sce, &d->channels, link) {
26082180Scg			c = sce->channel;
26182180Scg			if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
26282180Scg				c->flags |= CHN_F_BUSY;
26382180Scg				err = vchan_create(c);
26482180Scg				if (err) {
26582180Scg					device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
26682180Scg					c->flags &= ~CHN_F_BUSY;
26782180Scg				}
26882180Scg				return;
26982180Scg			}
27082180Scg		}
27182180Scg	}
27282180Scg	if (num == 0 && d->vchancount > 0) {
27382180Scg		done = 0;
27482180Scg		while (!done) {
27582180Scg			done = 1;
27682180Scg			SLIST_FOREACH(sce, &d->channels, link) {
27782180Scg				c = sce->channel;
27882180Scg				if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
27982180Scg					done = 0;
28082180Scg					err = vchan_destroy(c);
28182180Scg					if (err)
28282180Scg						device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
28382180Scg					goto restart;
28482180Scg				}
28582180Scg			}
28682180Scgrestart:
28782180Scg		}
28882180Scg	}
28982180Scg}
29082180Scg
29173127Scg#ifdef USING_DEVFS
29265340Scgstatic int
29378853Scgsysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
29465340Scg{
29578214Scg	struct snddev_info *d;
29665340Scg	int error, unit;
29765340Scg
29865340Scg	unit = snd_unit;
29965340Scg	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
30065340Scg	if (error == 0 && req->newptr != NULL) {
30178396Scg		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
30278214Scg			return EINVAL;
30378214Scg		d = devclass_get_softc(pcm_devclass, unit);
30478395Scg		if (d == NULL || SLIST_EMPTY(&d->channels))
30578214Scg			return EINVAL;
30665340Scg		snd_unit = unit;
30765340Scg	}
30865340Scg	return (error);
30965340Scg}
31070617SjhbSYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
31178853Scg            0, sizeof(int), sysctl_hw_snd_unit, "I", "");
31273127Scg#endif
31365340Scg
31478853Scgstatic int
31582180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
31678853Scg{
31782180Scg	struct snddev_info *d;
31882180Scg	int i, v, error;
31978853Scg
32082180Scg	v = snd_maxautovchans;
32178853Scg	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
32278853Scg	if (error == 0 && req->newptr != NULL) {
32378853Scg		if (v < 0 || v >= SND_MAXVCHANS)
32478853Scg			return EINVAL;
32582180Scg		if (v != snd_maxautovchans) {
32682180Scg			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
32782180Scg				d = devclass_get_softc(pcm_devclass, i);
32882180Scg				if (!d)
32982180Scg					continue;
33082180Scg				pcm_setmaxautovchans(d, v);
33182180Scg			}
33282180Scg		}
33382180Scg		snd_maxautovchans = v;
33478853Scg	}
33578853Scg	return (error);
33678853Scg}
33782180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
33882180Scg            0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
33978853Scg
34077269Scgstruct pcm_channel *
34177269Scgpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
34250724Scg{
34377269Scg	struct pcm_channel *ch;
34465340Scg	char *dirs;
34583614Scg    	int err, *pnum;
34650724Scg
34777269Scg	switch(dir) {
34877269Scg	case PCMDIR_PLAY:
34977269Scg		dirs = "play";
35083614Scg		pnum = &d->playcount;
35177269Scg		break;
35283614Scg
35377269Scg	case PCMDIR_REC:
35477269Scg		dirs = "record";
35583614Scg		pnum = &d->reccount;
35677269Scg		break;
35783614Scg
35877269Scg	case PCMDIR_VIRTUAL:
35977269Scg		dirs = "virtual";
36077269Scg		dir = PCMDIR_PLAY;
36183614Scg		pnum = &d->vchancount;
36277269Scg		break;
36383614Scg
36477269Scg	default:
36577269Scg		return NULL;
36677269Scg	}
36765340Scg
36877269Scg	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
36977269Scg	if (!ch)
37077269Scg		return NULL;
37177269Scg
37277269Scg	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
37377269Scg	if (!ch->methods) {
37477269Scg		free(ch, M_DEVBUF);
37583614Scg
37677269Scg		return NULL;
37761344Scg	}
37877269Scg
37983614Scg	ch->num = (*pnum)++;
38083614Scg
38177269Scg	ch->pid = -1;
38277269Scg	ch->parentsnddev = d;
38377269Scg	ch->parentchannel = parent;
38489774Sscottl	ch->dev = d->dev;
38583614Scg	snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num);
38677269Scg
38770134Scg	err = chn_init(ch, devinfo, dir);
38870134Scg	if (err) {
38983614Scg		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
39077269Scg		kobj_delete(ch->methods, M_DEVBUF);
39177269Scg		free(ch, M_DEVBUF);
39283614Scg		(*pnum)--;
39383614Scg
39477269Scg		return NULL;
39555483Scg	}
39677269Scg
39777269Scg	return ch;
39877269Scg}
39977269Scg
40077269Scgint
40177269Scgpcm_chn_destroy(struct pcm_channel *ch)
40277269Scg{
40383614Scg	struct snddev_info *d;
40477269Scg	int err;
40577269Scg
40683614Scg	d = ch->parentsnddev;
40777269Scg	err = chn_kill(ch);
40877269Scg	if (err) {
40983614Scg		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
41077269Scg		return err;
41177269Scg	}
41277269Scg
41383614Scg	if (ch->direction == PCMDIR_REC)
41483614Scg		d->reccount--;
41583614Scg	else if (ch->flags & CHN_F_VIRTUAL)
41683614Scg		d->vchancount--;
41783614Scg	else
41883614Scg		d->playcount--;
41983614Scg
42077269Scg	kobj_delete(ch->methods, M_DEVBUF);
42177269Scg	free(ch, M_DEVBUF);
42277269Scg
42377269Scg	return 0;
42477269Scg}
42577269Scg
42677269Scgint
42778895Scgpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
42877269Scg{
42982180Scg    	struct snddev_channel *sce, *tmp, *after;
43077269Scg    	int unit = device_get_unit(d->dev);
43177269Scg
43278214Scg	snd_mtxlock(d->lock);
43378214Scg
43477269Scg	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
43577269Scg	if (!sce) {
43678214Scg		snd_mtxunlock(d->lock);
43777269Scg		return ENOMEM;
43877269Scg	}
43977269Scg
44077269Scg	sce->channel = ch;
44182180Scg	if (SLIST_EMPTY(&d->channels)) {
44282180Scg		SLIST_INSERT_HEAD(&d->channels, sce, link);
44382180Scg	} else {
44482180Scg		after = NULL;
44582180Scg		SLIST_FOREACH(tmp, &d->channels, link) {
44682180Scg			after = tmp;
44782180Scg		}
44882180Scg		SLIST_INSERT_AFTER(after, sce, link);
44982180Scg	}
45077269Scg
45183089Scg	if (mkdev) {
45278895Scg		dsp_register(unit, d->devcount++);
45383089Scg		if (ch->direction == PCMDIR_REC)
45483089Scg			dsp_registerrec(unit, ch->num);
45583089Scg	}
45677269Scg
45777882Scg	snd_mtxunlock(d->lock);
45877269Scg
45950724Scg	return 0;
46050724Scg}
46150724Scg
46277269Scgint
46378895Scgpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
46465340Scg{
46577269Scg    	struct snddev_channel *sce;
46677269Scg    	int unit = device_get_unit(d->dev);
46765340Scg
46877882Scg	snd_mtxlock(d->lock);
46977269Scg	SLIST_FOREACH(sce, &d->channels, link) {
47077269Scg		if (sce->channel == ch)
47177269Scg			goto gotit;
47265340Scg	}
47377882Scg	snd_mtxunlock(d->lock);
47477269Scg	return EINVAL;
47577269Scggotit:
47677269Scg	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
47777269Scg	free(sce, M_DEVBUF);
47877269Scg
47983089Scg	if (rmdev) {
48078895Scg		dsp_unregister(unit, --d->devcount);
48183089Scg		if (ch->direction == PCMDIR_REC)
48283614Scg			dsp_unregisterrec(unit, ch->num);
48383089Scg	}
48477882Scg	snd_mtxunlock(d->lock);
48577269Scg
48665340Scg	return 0;
48765340Scg}
48865340Scg
48950724Scgint
49077269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
49177269Scg{
49277269Scg    	struct snddev_info *d = device_get_softc(dev);
49382180Scg	struct pcm_channel *ch;
49482180Scg    	int err;
49577269Scg
49677269Scg	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
49777269Scg	if (!ch) {
49877269Scg		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
49977269Scg		return ENODEV;
50077269Scg	}
50178853Scg
50278895Scg	err = pcm_chn_add(d, ch, 1);
50377269Scg	if (err) {
50477269Scg		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
50577269Scg		pcm_chn_destroy(ch);
50678853Scg		return err;
50777269Scg	}
50877269Scg
50982180Scg	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) {
51078853Scg		ch->flags |= CHN_F_BUSY;
51182180Scg		err = vchan_create(ch);
51278853Scg		if (err) {
51382180Scg			device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
51482180Scg			ch->flags &= ~CHN_F_BUSY;
51578853Scg		}
51678853Scg	}
51778853Scg
51877269Scg	return err;
51977269Scg}
52077269Scg
52177269Scgstatic int
52277269Scgpcm_killchan(device_t dev)
52377269Scg{
52477269Scg    	struct snddev_info *d = device_get_softc(dev);
52577269Scg    	struct snddev_channel *sce;
52677269Scg
52777882Scg	snd_mtxlock(d->lock);
52877269Scg	sce = SLIST_FIRST(&d->channels);
52977882Scg	snd_mtxunlock(d->lock);
53077269Scg
53178895Scg	return pcm_chn_remove(d, sce->channel, 1);
53277269Scg}
53377269Scg
53477269Scgint
53550724Scgpcm_setstatus(device_t dev, char *str)
53650724Scg{
53774763Scg    	struct snddev_info *d = device_get_softc(dev);
53877882Scg
53977882Scg	snd_mtxlock(d->lock);
54050724Scg	strncpy(d->status, str, SND_STATUSLEN);
54177882Scg	snd_mtxunlock(d->lock);
54250724Scg	return 0;
54350724Scg}
54450724Scg
54550724Scgu_int32_t
54650724Scgpcm_getflags(device_t dev)
54750724Scg{
54874763Scg    	struct snddev_info *d = device_get_softc(dev);
54977882Scg
55050724Scg	return d->flags;
55150724Scg}
55250724Scg
55350724Scgvoid
55450724Scgpcm_setflags(device_t dev, u_int32_t val)
55550724Scg{
55674763Scg    	struct snddev_info *d = device_get_softc(dev);
55778362Scg
55850724Scg	d->flags = val;
55950724Scg}
56050724Scg
56158384Scgvoid *
56258384Scgpcm_getdevinfo(device_t dev)
56358384Scg{
56474763Scg    	struct snddev_info *d = device_get_softc(dev);
56577882Scg
56658384Scg	return d->devinfo;
56758384Scg}
56858384Scg
56983614Scgunsigned int
57083614Scgpcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
57183614Scg{
57283614Scg    	struct snddev_info *d = device_get_softc(dev);
57389690Scg	int sz, x;
57483614Scg
57583614Scg	sz = 0;
57689690Scg	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
57789690Scg		x = sz;
57883614Scg		RANGE(sz, min, max);
57989690Scg		if (x != sz)
58089690Scg			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
58189690Scg		x = min;
58289690Scg		while (x < sz)
58389690Scg			x <<= 1;
58489690Scg		if (x > sz)
58589690Scg			x >>= 1;
58689690Scg		if (x != sz) {
58789690Scg			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
58889690Scg			sz = x;
58989690Scg		}
59089690Scg	} else {
59183614Scg		sz = deflt;
59289690Scg	}
59389690Scg
59483614Scg	d->bufsz = sz;
59583614Scg
59683614Scg	return sz;
59783614Scg}
59883614Scg
59950724Scgint
60050724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec)
60150724Scg{
60274763Scg    	struct snddev_info *d = device_get_softc(dev);
60350724Scg
60489834Scg	if (pcm_veto_load) {
60589834Scg		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
60689834Scg
60789834Scg		return EINVAL;
60889834Scg	}
60989834Scg
61093814Sjhb	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
61177882Scg	snd_mtxlock(d->lock);
61277269Scg
61378853Scg	d->flags = 0;
61461886Scg	d->dev = dev;
61550724Scg	d->devinfo = devinfo;
61678895Scg	d->devcount = 0;
61783089Scg	d->reccount = 0;
61883614Scg	d->playcount = 0;
61978895Scg	d->vchancount = 0;
62078362Scg	d->inprog = 0;
62150724Scg
62278362Scg	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
62378214Scg		d->flags |= SD_F_SIMPLEX;
62478362Scg
62578214Scg	d->fakechan = fkchan_setup(dev);
62678214Scg	chn_init(d->fakechan, NULL, 0);
62755494Scg
62873127Scg#ifdef SND_DYNSYSCTL
62970680Sjhb	sysctl_ctx_init(&d->sysctl_tree);
63070680Sjhb	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
63170943Sjhb				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
63270680Sjhb				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
63370943Sjhb	if (d->sysctl_tree_top == NULL) {
63470680Sjhb		sysctl_ctx_free(&d->sysctl_tree);
63570680Sjhb		goto no;
63670680Sjhb	}
63783614Scg	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
63883614Scg            OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
63973127Scg#endif
64078214Scg	if (numplay > 0)
64182180Scg		vchan_initsys(dev);
64278853Scg	if (numplay == 1)
64378853Scg		d->flags |= SD_F_AUTOVCHAN;
64478853Scg
64577882Scg	snd_mtxunlock(d->lock);
64682180Scg	sndstat_register(dev, d->status, sndstat_prepare_pcm);
64750724Scg    	return 0;
64850724Scgno:
64977882Scg	snd_mtxfree(d->lock);
65050724Scg	return ENXIO;
65150724Scg}
65250724Scg
65365340Scgint
65465340Scgpcm_unregister(device_t dev)
65565207Scg{
65674763Scg    	struct snddev_info *d = device_get_softc(dev);
65777269Scg    	struct snddev_channel *sce;
65883614Scg	struct pcm_channel *ch;
65965207Scg
66077882Scg	snd_mtxlock(d->lock);
66178362Scg	if (d->inprog) {
66283476Sgreid		device_printf(dev, "unregister: operation in progress\n");
66378362Scg		snd_mtxunlock(d->lock);
66478362Scg		return EBUSY;
66578362Scg	}
66683476Sgreid	if (sndstat_busy() != 0) {
66783476Sgreid		device_printf(dev, "unregister: sndstat busy\n");
66883476Sgreid		snd_mtxunlock(d->lock);
66983476Sgreid		return EBUSY;
67083476Sgreid	}
67177269Scg	SLIST_FOREACH(sce, &d->channels, link) {
67283614Scg		ch = sce->channel;
67383614Scg		if (ch->refcount > 0) {
67483614Scg			device_printf(dev, "unregister: channel %s busy (pid %d)", ch->name, ch->pid);
67577882Scg			snd_mtxunlock(d->lock);
67677269Scg			return EBUSY;
67777269Scg		}
67865487Scg	}
67978362Scg	if (mixer_uninit(dev)) {
68083476Sgreid		device_printf(dev, "unregister: mixer busy\n");
68177882Scg		snd_mtxunlock(d->lock);
68265487Scg		return EBUSY;
68365487Scg	}
68465207Scg
68574763Scg#ifdef SND_DYNSYSCTL
68674763Scg	d->sysctl_tree_top = NULL;
68774763Scg	sysctl_ctx_free(&d->sysctl_tree);
68874763Scg#endif
68978395Scg	while (!SLIST_EMPTY(&d->channels))
69077269Scg		pcm_killchan(dev);
69165207Scg
69274763Scg	chn_kill(d->fakechan);
69374763Scg	fkchan_kill(d->fakechan);
69473127Scg
69577882Scg	snd_mtxfree(d->lock);
69665340Scg	return 0;
69765207Scg}
69865207Scg
69982180Scg/************************************************************************/
70082180Scg
70182180Scgstatic int
70282180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
70382180Scg{
70482180Scg    	struct snddev_info *d;
70582180Scg    	struct snddev_channel *sce;
70682180Scg	struct pcm_channel *c;
70782180Scg	struct pcm_feeder *f;
70882180Scg    	int pc, rc, vc;
70982180Scg
71082180Scg	if (verbose < 1)
71182180Scg		return 0;
71282180Scg
71382180Scg	d = device_get_softc(dev);
71482180Scg	if (!d)
71582180Scg		return ENXIO;
71682180Scg
71782180Scg	snd_mtxlock(d->lock);
71882180Scg	if (!SLIST_EMPTY(&d->channels)) {
71982180Scg		pc = rc = vc = 0;
72082180Scg		SLIST_FOREACH(sce, &d->channels, link) {
72182180Scg			c = sce->channel;
72282180Scg			if (c->direction == PCMDIR_PLAY) {
72382180Scg				if (c->flags & CHN_F_VIRTUAL)
72482180Scg					vc++;
72582180Scg				else
72682180Scg					pc++;
72782180Scg			} else
72882180Scg				rc++;
72982180Scg		}
73083614Scg		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
73182180Scg				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
73282180Scg#ifdef USING_DEVFS
73382180Scg				(device_get_unit(dev) == snd_unit)? " default" : ""
73482180Scg#else
73582180Scg				""
73682180Scg#endif
73782180Scg				);
73882180Scg		if (verbose <= 1)
73982180Scg			goto skipverbose;
74082180Scg		SLIST_FOREACH(sce, &d->channels, link) {
74182180Scg			c = sce->channel;
74282479Scg			sbuf_printf(s, "\n\t");
74382479Scg
74482479Scg			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
74589691Scg			sbuf_printf(s, "spd %d", c->speed);
74689691Scg			if (c->speed != sndbuf_getspd(c->bufhard))
74789691Scg				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
74889691Scg			sbuf_printf(s, ", fmt 0x%08x", c->format);
74989691Scg			if (c->format != sndbuf_getfmt(c->bufhard))
75089691Scg				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
75189691Scg			sbuf_printf(s, ", flags %08x", c->flags);
75282180Scg			if (c->pid != -1)
75382180Scg				sbuf_printf(s, ", pid %d", c->pid);
75482180Scg			sbuf_printf(s, "\n\t");
75589691Scg
75682492Scg			if (c->bufhard != NULL && c->bufsoft != NULL) {
75782479Scg				sbuf_printf(s, "interrupts %d, ", c->interrupts);
75882479Scg				if (c->direction == PCMDIR_REC)
75982479Scg					sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
76082479Scg						c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
76182479Scg				else
76282492Scg					sbuf_printf(s, "underruns %d, ready %d",
76382492Scg						c->xruns, sndbuf_getready(c->bufsoft));
76482479Scg				sbuf_printf(s, "\n\t");
76582479Scg			}
76689691Scg
76789691Scg			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
76889691Scg			sbuf_printf(s, " -> ");
76982180Scg			f = c->feeder;
77089691Scg			while (f->source != NULL)
77189691Scg				f = f->source;
77289691Scg			while (f != NULL) {
77382180Scg				sbuf_printf(s, "%s", f->class->name);
77482180Scg				if (f->desc->type == FEEDER_FMT)
77589691Scg					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
77682180Scg				if (f->desc->type == FEEDER_RATE)
77789691Scg					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
77882180Scg				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
77989691Scg					sbuf_printf(s, "(0x%08x)", f->desc->out);
78089691Scg				sbuf_printf(s, " -> ");
78189691Scg				f = f->parent;
78282180Scg			}
78389691Scg			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
78482180Scg		}
78582180Scgskipverbose:
78682180Scg	} else
78782180Scg		sbuf_printf(s, " (mixer only)");
78882180Scg	snd_mtxunlock(d->lock);
78982180Scg
79082180Scg	return 0;
79182180Scg}
79282180Scg
79382180Scg/************************************************************************/
79482180Scg
79582180Scg#ifdef SND_DYNSYSCTL
79682180Scgint
79782180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
79882180Scg{
79982180Scg	struct snddev_info *d;
80082180Scg    	struct snddev_channel *sce;
80182180Scg	struct pcm_channel *c;
80282180Scg	int err, oldcnt, newcnt, cnt;
80382180Scg
80482180Scg	d = oidp->oid_arg1;
80582180Scg
80682180Scg	pcm_lock(d);
80782180Scg	cnt = 0;
80882180Scg	SLIST_FOREACH(sce, &d->channels, link) {
80982180Scg		c = sce->channel;
81082180Scg		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
81182180Scg			cnt++;
81282180Scg	}
81382180Scg	oldcnt = cnt;
81482180Scg	newcnt = cnt;
81582180Scg
81682180Scg	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
81782180Scg	if (err == 0 && req->newptr != NULL) {
81882180Scg		if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
81982180Scg			pcm_unlock(d);
82082180Scg			return EINVAL;
82182180Scg		}
82282180Scg
82382180Scg		if (newcnt > cnt) {
82482180Scg			/* add new vchans - find a parent channel first */
82582180Scg			SLIST_FOREACH(sce, &d->channels, link) {
82682180Scg				c = sce->channel;
82782180Scg				/* not a candidate if not a play channel */
82882180Scg				if (c->direction != PCMDIR_PLAY)
82982180Scg					goto addskip;
83082180Scg				/* not a candidate if a virtual channel */
83182180Scg				if (c->flags & CHN_F_VIRTUAL)
83282180Scg					goto addskip;
83382180Scg				/* not a candidate if it's in use */
83482180Scg				if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
83582180Scg					goto addskip;
83682180Scg				/*
83782180Scg				 * if we get here we're a nonvirtual play channel, and either
83882180Scg				 * 1) not busy
83982180Scg				 * 2) busy with children, not directly open
84082180Scg				 *
84182180Scg				 * thus we can add children
84282180Scg				 */
84382180Scg				goto addok;
84482180Scgaddskip:
84582180Scg			}
84682180Scg			pcm_unlock(d);
84782180Scg			return EBUSY;
84882180Scgaddok:
84982180Scg			c->flags |= CHN_F_BUSY;
85082180Scg			while (err == 0 && newcnt > cnt) {
85182180Scg				err = vchan_create(c);
85282180Scg				if (err == 0)
85382180Scg					cnt++;
85482180Scg			}
85582180Scg			if (SLIST_EMPTY(&c->children))
85682180Scg				c->flags &= ~CHN_F_BUSY;
85782180Scg		} else if (newcnt < cnt) {
85882180Scg			while (err == 0 && newcnt < cnt) {
85982180Scg				SLIST_FOREACH(sce, &d->channels, link) {
86082180Scg					c = sce->channel;
86182180Scg					if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
86282180Scg						goto remok;
86382180Scg				}
86482180Scg				pcm_unlock(d);
86582180Scg				return EINVAL;
86682180Scgremok:
86782180Scg				err = vchan_destroy(c);
86882180Scg				if (err == 0)
86982180Scg					cnt--;
87082180Scg			}
87182180Scg		}
87282180Scg	}
87382180Scg
87482180Scg	pcm_unlock(d);
87582180Scg	return err;
87682180Scg}
87782180Scg#endif
87882180Scg
87982180Scg/************************************************************************/
88082180Scg
88165340Scgstatic moduledata_t sndpcm_mod = {
88265340Scg	"snd_pcm",
88378362Scg	NULL,
88465340Scg	NULL
88565340Scg};
88665340ScgDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
88765340ScgMODULE_VERSION(snd_pcm, PCM_MODVER);
888