sound.c revision 100576
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 100576 2002-07-23 14:50:51Z kan $");
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);
28396928Speter					break;		/* restart */
28482180Scg				}
28582180Scg			}
28682180Scg		}
28782180Scg	}
28882180Scg}
28982180Scg
29073127Scg#ifdef USING_DEVFS
29165340Scgstatic int
29278853Scgsysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
29365340Scg{
29478214Scg	struct snddev_info *d;
29565340Scg	int error, unit;
29665340Scg
29765340Scg	unit = snd_unit;
29865340Scg	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
29965340Scg	if (error == 0 && req->newptr != NULL) {
30078396Scg		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
30178214Scg			return EINVAL;
30278214Scg		d = devclass_get_softc(pcm_devclass, unit);
30378395Scg		if (d == NULL || SLIST_EMPTY(&d->channels))
30478214Scg			return EINVAL;
30565340Scg		snd_unit = unit;
30665340Scg	}
30765340Scg	return (error);
30865340Scg}
30970617SjhbSYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
31078853Scg            0, sizeof(int), sysctl_hw_snd_unit, "I", "");
31173127Scg#endif
31265340Scg
31378853Scgstatic int
31482180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
31578853Scg{
31682180Scg	struct snddev_info *d;
31782180Scg	int i, v, error;
31878853Scg
31982180Scg	v = snd_maxautovchans;
32078853Scg	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
32178853Scg	if (error == 0 && req->newptr != NULL) {
32278853Scg		if (v < 0 || v >= SND_MAXVCHANS)
32378853Scg			return EINVAL;
32482180Scg		if (v != snd_maxautovchans) {
32582180Scg			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
32682180Scg				d = devclass_get_softc(pcm_devclass, i);
32782180Scg				if (!d)
32882180Scg					continue;
32982180Scg				pcm_setmaxautovchans(d, v);
33082180Scg			}
33182180Scg		}
33282180Scg		snd_maxautovchans = v;
33378853Scg	}
33478853Scg	return (error);
33578853Scg}
33682180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
33782180Scg            0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
33878853Scg
33977269Scgstruct pcm_channel *
34077269Scgpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
34150724Scg{
34277269Scg	struct pcm_channel *ch;
34365340Scg	char *dirs;
34483614Scg    	int err, *pnum;
34550724Scg
34677269Scg	switch(dir) {
34777269Scg	case PCMDIR_PLAY:
34877269Scg		dirs = "play";
34983614Scg		pnum = &d->playcount;
35077269Scg		break;
35183614Scg
35277269Scg	case PCMDIR_REC:
35377269Scg		dirs = "record";
35483614Scg		pnum = &d->reccount;
35577269Scg		break;
35683614Scg
35777269Scg	case PCMDIR_VIRTUAL:
35877269Scg		dirs = "virtual";
35977269Scg		dir = PCMDIR_PLAY;
36083614Scg		pnum = &d->vchancount;
36177269Scg		break;
36283614Scg
36377269Scg	default:
36477269Scg		return NULL;
36577269Scg	}
36665340Scg
36777269Scg	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
36877269Scg	if (!ch)
36977269Scg		return NULL;
37077269Scg
37177269Scg	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
37277269Scg	if (!ch->methods) {
37377269Scg		free(ch, M_DEVBUF);
37483614Scg
37577269Scg		return NULL;
37661344Scg	}
37777269Scg
37883614Scg	ch->num = (*pnum)++;
37983614Scg
38077269Scg	ch->pid = -1;
38177269Scg	ch->parentsnddev = d;
38277269Scg	ch->parentchannel = parent;
38389774Sscottl	ch->dev = d->dev;
38483614Scg	snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num);
38577269Scg
38670134Scg	err = chn_init(ch, devinfo, dir);
38770134Scg	if (err) {
38883614Scg		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
38977269Scg		kobj_delete(ch->methods, M_DEVBUF);
39077269Scg		free(ch, M_DEVBUF);
39183614Scg		(*pnum)--;
39283614Scg
39377269Scg		return NULL;
39455483Scg	}
39577269Scg
39677269Scg	return ch;
39777269Scg}
39877269Scg
39977269Scgint
40077269Scgpcm_chn_destroy(struct pcm_channel *ch)
40177269Scg{
40283614Scg	struct snddev_info *d;
40377269Scg	int err;
40477269Scg
40583614Scg	d = ch->parentsnddev;
40677269Scg	err = chn_kill(ch);
40777269Scg	if (err) {
40883614Scg		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
40977269Scg		return err;
41077269Scg	}
41177269Scg
41283614Scg	if (ch->direction == PCMDIR_REC)
41383614Scg		d->reccount--;
41483614Scg	else if (ch->flags & CHN_F_VIRTUAL)
41583614Scg		d->vchancount--;
41683614Scg	else
41783614Scg		d->playcount--;
41883614Scg
41977269Scg	kobj_delete(ch->methods, M_DEVBUF);
42077269Scg	free(ch, M_DEVBUF);
42177269Scg
42277269Scg	return 0;
42377269Scg}
42477269Scg
42577269Scgint
42678895Scgpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
42777269Scg{
42882180Scg    	struct snddev_channel *sce, *tmp, *after;
42977269Scg    	int unit = device_get_unit(d->dev);
43077269Scg
43177269Scg	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
43277269Scg	if (!sce) {
43377269Scg		return ENOMEM;
43477269Scg	}
43577269Scg
436100478Sorion	snd_mtxlock(d->lock);
437100478Sorion
43877269Scg	sce->channel = ch;
43982180Scg	if (SLIST_EMPTY(&d->channels)) {
44082180Scg		SLIST_INSERT_HEAD(&d->channels, sce, link);
44182180Scg	} else {
44282180Scg		after = NULL;
44382180Scg		SLIST_FOREACH(tmp, &d->channels, link) {
44482180Scg			after = tmp;
44582180Scg		}
44682180Scg		SLIST_INSERT_AFTER(after, sce, link);
44782180Scg	}
44877269Scg
44983089Scg	if (mkdev) {
45078895Scg		dsp_register(unit, d->devcount++);
45183089Scg		if (ch->direction == PCMDIR_REC)
45283089Scg			dsp_registerrec(unit, ch->num);
45383089Scg	}
45477269Scg
45577882Scg	snd_mtxunlock(d->lock);
45677269Scg
45750724Scg	return 0;
45850724Scg}
45950724Scg
46077269Scgint
46178895Scgpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
46265340Scg{
46377269Scg    	struct snddev_channel *sce;
46477269Scg    	int unit = device_get_unit(d->dev);
46565340Scg
46677882Scg	snd_mtxlock(d->lock);
46777269Scg	SLIST_FOREACH(sce, &d->channels, link) {
46877269Scg		if (sce->channel == ch)
46977269Scg			goto gotit;
47065340Scg	}
47177882Scg	snd_mtxunlock(d->lock);
47277269Scg	return EINVAL;
47377269Scggotit:
47477269Scg	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
47577269Scg	free(sce, M_DEVBUF);
47677269Scg
47783089Scg	if (rmdev) {
47878895Scg		dsp_unregister(unit, --d->devcount);
47983089Scg		if (ch->direction == PCMDIR_REC)
48083614Scg			dsp_unregisterrec(unit, ch->num);
48183089Scg	}
48277882Scg	snd_mtxunlock(d->lock);
48377269Scg
48465340Scg	return 0;
48565340Scg}
48665340Scg
48750724Scgint
48877269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
48977269Scg{
49077269Scg    	struct snddev_info *d = device_get_softc(dev);
49182180Scg	struct pcm_channel *ch;
49282180Scg    	int err;
49377269Scg
49477269Scg	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
49577269Scg	if (!ch) {
49677269Scg		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
49777269Scg		return ENODEV;
49877269Scg	}
49978853Scg
50078895Scg	err = pcm_chn_add(d, ch, 1);
50177269Scg	if (err) {
50277269Scg		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
50377269Scg		pcm_chn_destroy(ch);
50478853Scg		return err;
50577269Scg	}
50677269Scg
507100576Skan	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
508100576Skan	    ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
50978853Scg		ch->flags |= CHN_F_BUSY;
51082180Scg		err = vchan_create(ch);
51178853Scg		if (err) {
51282180Scg			device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
51382180Scg			ch->flags &= ~CHN_F_BUSY;
51478853Scg		}
51578853Scg	}
51678853Scg
51777269Scg	return err;
51877269Scg}
51977269Scg
52077269Scgstatic int
52177269Scgpcm_killchan(device_t dev)
52277269Scg{
52377269Scg    	struct snddev_info *d = device_get_softc(dev);
52477269Scg    	struct snddev_channel *sce;
52577269Scg
52677882Scg	snd_mtxlock(d->lock);
52777269Scg	sce = SLIST_FIRST(&d->channels);
52877882Scg	snd_mtxunlock(d->lock);
52977269Scg
53078895Scg	return pcm_chn_remove(d, sce->channel, 1);
53177269Scg}
53277269Scg
53377269Scgint
53450724Scgpcm_setstatus(device_t dev, char *str)
53550724Scg{
53674763Scg    	struct snddev_info *d = device_get_softc(dev);
53777882Scg
53877882Scg	snd_mtxlock(d->lock);
53950724Scg	strncpy(d->status, str, SND_STATUSLEN);
54077882Scg	snd_mtxunlock(d->lock);
54150724Scg	return 0;
54250724Scg}
54350724Scg
54450724Scgu_int32_t
54550724Scgpcm_getflags(device_t dev)
54650724Scg{
54774763Scg    	struct snddev_info *d = device_get_softc(dev);
54877882Scg
54950724Scg	return d->flags;
55050724Scg}
55150724Scg
55250724Scgvoid
55350724Scgpcm_setflags(device_t dev, u_int32_t val)
55450724Scg{
55574763Scg    	struct snddev_info *d = device_get_softc(dev);
55678362Scg
55750724Scg	d->flags = val;
55850724Scg}
55950724Scg
56058384Scgvoid *
56158384Scgpcm_getdevinfo(device_t dev)
56258384Scg{
56374763Scg    	struct snddev_info *d = device_get_softc(dev);
56477882Scg
56558384Scg	return d->devinfo;
56658384Scg}
56758384Scg
56883614Scgunsigned int
56983614Scgpcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
57083614Scg{
57183614Scg    	struct snddev_info *d = device_get_softc(dev);
57289690Scg	int sz, x;
57383614Scg
57483614Scg	sz = 0;
57589690Scg	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
57689690Scg		x = sz;
57783614Scg		RANGE(sz, min, max);
57889690Scg		if (x != sz)
57989690Scg			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
58089690Scg		x = min;
58189690Scg		while (x < sz)
58289690Scg			x <<= 1;
58389690Scg		if (x > sz)
58489690Scg			x >>= 1;
58589690Scg		if (x != sz) {
58689690Scg			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
58789690Scg			sz = x;
58889690Scg		}
58989690Scg	} else {
59083614Scg		sz = deflt;
59189690Scg	}
59289690Scg
59383614Scg	d->bufsz = sz;
59483614Scg
59583614Scg	return sz;
59683614Scg}
59783614Scg
59850724Scgint
59950724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec)
60050724Scg{
60174763Scg    	struct snddev_info *d = device_get_softc(dev);
60250724Scg
60389834Scg	if (pcm_veto_load) {
60489834Scg		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
60589834Scg
60689834Scg		return EINVAL;
60789834Scg	}
60889834Scg
60993814Sjhb	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
61077882Scg	snd_mtxlock(d->lock);
61177269Scg
61278853Scg	d->flags = 0;
61361886Scg	d->dev = dev;
61450724Scg	d->devinfo = devinfo;
61578895Scg	d->devcount = 0;
61683089Scg	d->reccount = 0;
61783614Scg	d->playcount = 0;
61878895Scg	d->vchancount = 0;
61978362Scg	d->inprog = 0;
62050724Scg
62178362Scg	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
62278214Scg		d->flags |= SD_F_SIMPLEX;
62378362Scg
62478214Scg	d->fakechan = fkchan_setup(dev);
62578214Scg	chn_init(d->fakechan, NULL, 0);
62655494Scg
62773127Scg#ifdef SND_DYNSYSCTL
62870680Sjhb	sysctl_ctx_init(&d->sysctl_tree);
62970680Sjhb	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
63070943Sjhb				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
63170680Sjhb				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
63270943Sjhb	if (d->sysctl_tree_top == NULL) {
63370680Sjhb		sysctl_ctx_free(&d->sysctl_tree);
63470680Sjhb		goto no;
63570680Sjhb	}
63683614Scg	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
63783614Scg            OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
63873127Scg#endif
63978214Scg	if (numplay > 0)
64082180Scg		vchan_initsys(dev);
64178853Scg	if (numplay == 1)
64278853Scg		d->flags |= SD_F_AUTOVCHAN;
64378853Scg
64477882Scg	snd_mtxunlock(d->lock);
64582180Scg	sndstat_register(dev, d->status, sndstat_prepare_pcm);
64650724Scg    	return 0;
64750724Scgno:
64877882Scg	snd_mtxfree(d->lock);
64950724Scg	return ENXIO;
65050724Scg}
65150724Scg
65265340Scgint
65365340Scgpcm_unregister(device_t dev)
65465207Scg{
65574763Scg    	struct snddev_info *d = device_get_softc(dev);
65677269Scg    	struct snddev_channel *sce;
65783614Scg	struct pcm_channel *ch;
65865207Scg
65977882Scg	snd_mtxlock(d->lock);
66078362Scg	if (d->inprog) {
66183476Sgreid		device_printf(dev, "unregister: operation in progress\n");
66278362Scg		snd_mtxunlock(d->lock);
66378362Scg		return EBUSY;
66478362Scg	}
66583476Sgreid	if (sndstat_busy() != 0) {
66683476Sgreid		device_printf(dev, "unregister: sndstat busy\n");
66783476Sgreid		snd_mtxunlock(d->lock);
66883476Sgreid		return EBUSY;
66983476Sgreid	}
67077269Scg	SLIST_FOREACH(sce, &d->channels, link) {
67183614Scg		ch = sce->channel;
67283614Scg		if (ch->refcount > 0) {
67395684Scg			device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
67477882Scg			snd_mtxunlock(d->lock);
67577269Scg			return EBUSY;
67677269Scg		}
67765487Scg	}
67878362Scg	if (mixer_uninit(dev)) {
67983476Sgreid		device_printf(dev, "unregister: mixer busy\n");
68077882Scg		snd_mtxunlock(d->lock);
68165487Scg		return EBUSY;
68265487Scg	}
68365207Scg
68474763Scg#ifdef SND_DYNSYSCTL
68574763Scg	d->sysctl_tree_top = NULL;
68674763Scg	sysctl_ctx_free(&d->sysctl_tree);
68774763Scg#endif
68878395Scg	while (!SLIST_EMPTY(&d->channels))
68977269Scg		pcm_killchan(dev);
69065207Scg
69174763Scg	chn_kill(d->fakechan);
69274763Scg	fkchan_kill(d->fakechan);
69373127Scg
69477882Scg	snd_mtxfree(d->lock);
69565340Scg	return 0;
69665207Scg}
69765207Scg
69882180Scg/************************************************************************/
69982180Scg
70082180Scgstatic int
70182180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
70282180Scg{
70382180Scg    	struct snddev_info *d;
70482180Scg    	struct snddev_channel *sce;
70582180Scg	struct pcm_channel *c;
70682180Scg	struct pcm_feeder *f;
70782180Scg    	int pc, rc, vc;
70882180Scg
70982180Scg	if (verbose < 1)
71082180Scg		return 0;
71182180Scg
71282180Scg	d = device_get_softc(dev);
71382180Scg	if (!d)
71482180Scg		return ENXIO;
71582180Scg
71682180Scg	snd_mtxlock(d->lock);
71782180Scg	if (!SLIST_EMPTY(&d->channels)) {
71882180Scg		pc = rc = vc = 0;
71982180Scg		SLIST_FOREACH(sce, &d->channels, link) {
72082180Scg			c = sce->channel;
72182180Scg			if (c->direction == PCMDIR_PLAY) {
72282180Scg				if (c->flags & CHN_F_VIRTUAL)
72382180Scg					vc++;
72482180Scg				else
72582180Scg					pc++;
72682180Scg			} else
72782180Scg				rc++;
72882180Scg		}
72983614Scg		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
73082180Scg				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
73182180Scg#ifdef USING_DEVFS
73282180Scg				(device_get_unit(dev) == snd_unit)? " default" : ""
73382180Scg#else
73482180Scg				""
73582180Scg#endif
73682180Scg				);
73782180Scg		if (verbose <= 1)
73882180Scg			goto skipverbose;
73982180Scg		SLIST_FOREACH(sce, &d->channels, link) {
74082180Scg			c = sce->channel;
74182479Scg			sbuf_printf(s, "\n\t");
74282479Scg
74382479Scg			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
74489691Scg			sbuf_printf(s, "spd %d", c->speed);
74589691Scg			if (c->speed != sndbuf_getspd(c->bufhard))
74689691Scg				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
74789691Scg			sbuf_printf(s, ", fmt 0x%08x", c->format);
74889691Scg			if (c->format != sndbuf_getfmt(c->bufhard))
74989691Scg				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
75089691Scg			sbuf_printf(s, ", flags %08x", c->flags);
75182180Scg			if (c->pid != -1)
75282180Scg				sbuf_printf(s, ", pid %d", c->pid);
75382180Scg			sbuf_printf(s, "\n\t");
75489691Scg
75582492Scg			if (c->bufhard != NULL && c->bufsoft != NULL) {
75682479Scg				sbuf_printf(s, "interrupts %d, ", c->interrupts);
75782479Scg				if (c->direction == PCMDIR_REC)
75882479Scg					sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
75982479Scg						c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
76082479Scg				else
76182492Scg					sbuf_printf(s, "underruns %d, ready %d",
76282492Scg						c->xruns, sndbuf_getready(c->bufsoft));
76382479Scg				sbuf_printf(s, "\n\t");
76482479Scg			}
76589691Scg
76689691Scg			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
76789691Scg			sbuf_printf(s, " -> ");
76882180Scg			f = c->feeder;
76989691Scg			while (f->source != NULL)
77089691Scg				f = f->source;
77189691Scg			while (f != NULL) {
77282180Scg				sbuf_printf(s, "%s", f->class->name);
77382180Scg				if (f->desc->type == FEEDER_FMT)
77489691Scg					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
77582180Scg				if (f->desc->type == FEEDER_RATE)
77689691Scg					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
77782180Scg				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
77889691Scg					sbuf_printf(s, "(0x%08x)", f->desc->out);
77989691Scg				sbuf_printf(s, " -> ");
78089691Scg				f = f->parent;
78182180Scg			}
78289691Scg			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
78382180Scg		}
78482180Scg	} else
78582180Scg		sbuf_printf(s, " (mixer only)");
78696928Speterskipverbose:
78782180Scg	snd_mtxunlock(d->lock);
78882180Scg
78982180Scg	return 0;
79082180Scg}
79182180Scg
79282180Scg/************************************************************************/
79382180Scg
79482180Scg#ifdef SND_DYNSYSCTL
79582180Scgint
79682180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
79782180Scg{
79882180Scg	struct snddev_info *d;
79982180Scg    	struct snddev_channel *sce;
80082180Scg	struct pcm_channel *c;
80182180Scg	int err, oldcnt, newcnt, cnt;
80282180Scg
80382180Scg	d = oidp->oid_arg1;
80482180Scg
80582180Scg	pcm_lock(d);
80682180Scg	cnt = 0;
80782180Scg	SLIST_FOREACH(sce, &d->channels, link) {
80882180Scg		c = sce->channel;
80982180Scg		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
81082180Scg			cnt++;
81182180Scg	}
81282180Scg	oldcnt = cnt;
81382180Scg	newcnt = cnt;
81482180Scg
81582180Scg	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
81682180Scg	if (err == 0 && req->newptr != NULL) {
81782180Scg		if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
81882180Scg			pcm_unlock(d);
81982180Scg			return EINVAL;
82082180Scg		}
82182180Scg
82282180Scg		if (newcnt > cnt) {
82382180Scg			/* add new vchans - find a parent channel first */
82482180Scg			SLIST_FOREACH(sce, &d->channels, link) {
82582180Scg				c = sce->channel;
82682180Scg				/* not a candidate if not a play channel */
82782180Scg				if (c->direction != PCMDIR_PLAY)
82896928Speter					continue;
82982180Scg				/* not a candidate if a virtual channel */
83082180Scg				if (c->flags & CHN_F_VIRTUAL)
83196928Speter					continue;
83282180Scg				/* not a candidate if it's in use */
83382180Scg				if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
83496928Speter					continue;
83582180Scg				/*
83682180Scg				 * if we get here we're a nonvirtual play channel, and either
83782180Scg				 * 1) not busy
83882180Scg				 * 2) busy with children, not directly open
83982180Scg				 *
84082180Scg				 * thus we can add children
84182180Scg				 */
84282180Scg				goto addok;
84382180Scg			}
84482180Scg			pcm_unlock(d);
84582180Scg			return EBUSY;
84682180Scgaddok:
84782180Scg			c->flags |= CHN_F_BUSY;
84882180Scg			while (err == 0 && newcnt > cnt) {
84982180Scg				err = vchan_create(c);
85082180Scg				if (err == 0)
85182180Scg					cnt++;
85282180Scg			}
85382180Scg			if (SLIST_EMPTY(&c->children))
85482180Scg				c->flags &= ~CHN_F_BUSY;
85582180Scg		} else if (newcnt < cnt) {
85682180Scg			while (err == 0 && newcnt < cnt) {
85782180Scg				SLIST_FOREACH(sce, &d->channels, link) {
85882180Scg					c = sce->channel;
85982180Scg					if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
86082180Scg						goto remok;
86182180Scg				}
86282180Scg				pcm_unlock(d);
86382180Scg				return EINVAL;
86482180Scgremok:
86582180Scg				err = vchan_destroy(c);
86682180Scg				if (err == 0)
86782180Scg					cnt--;
86882180Scg			}
86982180Scg		}
87082180Scg	}
87182180Scg
87282180Scg	pcm_unlock(d);
87382180Scg	return err;
87482180Scg}
87582180Scg#endif
87682180Scg
87782180Scg/************************************************************************/
87882180Scg
87965340Scgstatic moduledata_t sndpcm_mod = {
88065340Scg	"snd_pcm",
88178362Scg	NULL,
88265340Scg	NULL
88365340Scg};
88465340ScgDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
88565340ScgMODULE_VERSION(snd_pcm, PCM_MODVER);
886