sound.c revision 111119
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 111119 2003-02-19 05:47:46Z imp $");
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;
53107285Scg	struct mtx *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
94111119Simp	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
110107237Scg	/* 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}
127107237Scg/*
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}
147107237Scg*/
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
367111119Simp	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
36877269Scg	if (!ch)
36977269Scg		return NULL;
37077269Scg
371111119Simp	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
37277269Scg	if (!ch->methods) {
37377269Scg		free(ch, M_DEVBUF);
37483614Scg
37577269Scg		return NULL;
37661344Scg	}
37777269Scg
378107237Scg	snd_mtxlock(d->lock);
37983614Scg	ch->num = (*pnum)++;
380107237Scg	snd_mtxunlock(d->lock);
38183614Scg
38277269Scg	ch->pid = -1;
38377269Scg	ch->parentsnddev = d;
38477269Scg	ch->parentchannel = parent;
38589774Sscottl	ch->dev = d->dev;
386107237Scg	snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
38777269Scg
38870134Scg	err = chn_init(ch, devinfo, dir);
38970134Scg	if (err) {
39083614Scg		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
39177269Scg		kobj_delete(ch->methods, M_DEVBUF);
39277269Scg		free(ch, M_DEVBUF);
393107237Scg		snd_mtxlock(d->lock);
39483614Scg		(*pnum)--;
395107237Scg		snd_mtxunlock(d->lock);
39683614Scg
39777269Scg		return NULL;
39855483Scg	}
39977269Scg
40077269Scg	return ch;
40177269Scg}
40277269Scg
40377269Scgint
40477269Scgpcm_chn_destroy(struct pcm_channel *ch)
40577269Scg{
40683614Scg	struct snddev_info *d;
40777269Scg	int err;
40877269Scg
40983614Scg	d = ch->parentsnddev;
41077269Scg	err = chn_kill(ch);
41177269Scg	if (err) {
41283614Scg		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
41377269Scg		return err;
41477269Scg	}
41577269Scg
41677269Scg	kobj_delete(ch->methods, M_DEVBUF);
41777269Scg	free(ch, M_DEVBUF);
41877269Scg
41977269Scg	return 0;
42077269Scg}
42177269Scg
42277269Scgint
42378895Scgpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
42477269Scg{
42582180Scg    	struct snddev_channel *sce, *tmp, *after;
42677269Scg    	int unit = device_get_unit(d->dev);
427107237Scg	int x = -1;
42877269Scg
429111119Simp	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
43077269Scg	if (!sce) {
43177269Scg		return ENOMEM;
43277269Scg	}
43377269Scg
434100478Sorion	snd_mtxlock(d->lock);
43577269Scg	sce->channel = ch;
43682180Scg	if (SLIST_EMPTY(&d->channels)) {
43782180Scg		SLIST_INSERT_HEAD(&d->channels, sce, link);
43882180Scg	} else {
43982180Scg		after = NULL;
44082180Scg		SLIST_FOREACH(tmp, &d->channels, link) {
44182180Scg			after = tmp;
44282180Scg		}
44382180Scg		SLIST_INSERT_AFTER(after, sce, link);
44482180Scg	}
445107237Scg	if (mkdev)
446107237Scg		x = d->devcount++;
447107237Scg	snd_mtxunlock(d->lock);
44877269Scg
44983089Scg	if (mkdev) {
450107237Scg		dsp_register(unit, x);
45183089Scg		if (ch->direction == PCMDIR_REC)
45283089Scg			dsp_registerrec(unit, ch->num);
45383089Scg	}
45477269Scg
45550724Scg	return 0;
45650724Scg}
45750724Scg
45877269Scgint
45978895Scgpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
46065340Scg{
46177269Scg    	struct snddev_channel *sce;
46277269Scg    	int unit = device_get_unit(d->dev);
46365340Scg
46477882Scg	snd_mtxlock(d->lock);
46577269Scg	SLIST_FOREACH(sce, &d->channels, link) {
46677269Scg		if (sce->channel == ch)
46777269Scg			goto gotit;
46865340Scg	}
46977882Scg	snd_mtxunlock(d->lock);
47077269Scg	return EINVAL;
47177269Scggotit:
47277269Scg	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
47383089Scg	if (rmdev) {
47478895Scg		dsp_unregister(unit, --d->devcount);
47583089Scg		if (ch->direction == PCMDIR_REC)
47683614Scg			dsp_unregisterrec(unit, ch->num);
47783089Scg	}
478107237Scg
479107237Scg    	if (ch->direction == PCMDIR_REC)
480107237Scg		d->reccount--;
481107237Scg	else if (ch->flags & CHN_F_VIRTUAL)
482107237Scg		d->vchancount--;
483107237Scg	else
484107237Scg		d->playcount--;
485107237Scg
48677882Scg	snd_mtxunlock(d->lock);
487107237Scg	free(sce, M_DEVBUF);
48877269Scg
48965340Scg	return 0;
49065340Scg}
49165340Scg
49250724Scgint
49377269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
49477269Scg{
49577269Scg    	struct snddev_info *d = device_get_softc(dev);
49682180Scg	struct pcm_channel *ch;
49782180Scg    	int err;
49877269Scg
49977269Scg	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
50077269Scg	if (!ch) {
50177269Scg		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
50277269Scg		return ENODEV;
50377269Scg	}
50478853Scg
50578895Scg	err = pcm_chn_add(d, ch, 1);
50677269Scg	if (err) {
50777269Scg		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
508107237Scg		snd_mtxunlock(d->lock);
50977269Scg		pcm_chn_destroy(ch);
51078853Scg		return err;
51177269Scg	}
51277269Scg
513100576Skan	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
514100576Skan	    ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
51578853Scg		ch->flags |= CHN_F_BUSY;
51682180Scg		err = vchan_create(ch);
51778853Scg		if (err) {
51882180Scg			device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
51982180Scg			ch->flags &= ~CHN_F_BUSY;
52078853Scg		}
52178853Scg	}
52278853Scg
52377269Scg	return err;
52477269Scg}
52577269Scg
52677269Scgstatic int
52777269Scgpcm_killchan(device_t dev)
52877269Scg{
52977269Scg    	struct snddev_info *d = device_get_softc(dev);
53077269Scg    	struct snddev_channel *sce;
531106420Scognet	struct pcm_channel *ch;
532106420Scognet	int error = 0;
53377269Scg
53477882Scg	snd_mtxlock(d->lock);
53577269Scg	sce = SLIST_FIRST(&d->channels);
53677882Scg	snd_mtxunlock(d->lock);
537106420Scognet	ch = sce->channel;
53877269Scg
539109236Scognet	error = pcm_chn_remove(d, sce->channel, SLIST_EMPTY(&ch->children));
540106420Scognet	if (error)
541106420Scognet		return (error);
542106420Scognet	return (pcm_chn_destroy(ch));
54377269Scg}
54477269Scg
54577269Scgint
54650724Scgpcm_setstatus(device_t dev, char *str)
54750724Scg{
54874763Scg    	struct snddev_info *d = device_get_softc(dev);
54977882Scg
55077882Scg	snd_mtxlock(d->lock);
55150724Scg	strncpy(d->status, str, SND_STATUSLEN);
55277882Scg	snd_mtxunlock(d->lock);
55350724Scg	return 0;
55450724Scg}
55550724Scg
55650724Scgu_int32_t
55750724Scgpcm_getflags(device_t dev)
55850724Scg{
55974763Scg    	struct snddev_info *d = device_get_softc(dev);
56077882Scg
56150724Scg	return d->flags;
56250724Scg}
56350724Scg
56450724Scgvoid
56550724Scgpcm_setflags(device_t dev, u_int32_t val)
56650724Scg{
56774763Scg    	struct snddev_info *d = device_get_softc(dev);
56878362Scg
56950724Scg	d->flags = val;
57050724Scg}
57150724Scg
57258384Scgvoid *
57358384Scgpcm_getdevinfo(device_t dev)
57458384Scg{
57574763Scg    	struct snddev_info *d = device_get_softc(dev);
57677882Scg
57758384Scg	return d->devinfo;
57858384Scg}
57958384Scg
58083614Scgunsigned int
58183614Scgpcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
58283614Scg{
58383614Scg    	struct snddev_info *d = device_get_softc(dev);
58489690Scg	int sz, x;
58583614Scg
58683614Scg	sz = 0;
58789690Scg	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
58889690Scg		x = sz;
58983614Scg		RANGE(sz, min, max);
59089690Scg		if (x != sz)
59189690Scg			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
59289690Scg		x = min;
59389690Scg		while (x < sz)
59489690Scg			x <<= 1;
59589690Scg		if (x > sz)
59689690Scg			x >>= 1;
59789690Scg		if (x != sz) {
59889690Scg			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
59989690Scg			sz = x;
60089690Scg		}
60189690Scg	} else {
60283614Scg		sz = deflt;
60389690Scg	}
60489690Scg
60583614Scg	d->bufsz = sz;
60683614Scg
60783614Scg	return sz;
60883614Scg}
60983614Scg
61050724Scgint
61150724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec)
61250724Scg{
61374763Scg    	struct snddev_info *d = device_get_softc(dev);
61450724Scg
61589834Scg	if (pcm_veto_load) {
61689834Scg		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
61789834Scg
61889834Scg		return EINVAL;
61989834Scg	}
62089834Scg
62193814Sjhb	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
62277269Scg
62378853Scg	d->flags = 0;
62461886Scg	d->dev = dev;
62550724Scg	d->devinfo = devinfo;
62678895Scg	d->devcount = 0;
62783089Scg	d->reccount = 0;
62883614Scg	d->playcount = 0;
62978895Scg	d->vchancount = 0;
63078362Scg	d->inprog = 0;
63150724Scg
63278362Scg	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
63378214Scg		d->flags |= SD_F_SIMPLEX;
63478362Scg
63578214Scg	d->fakechan = fkchan_setup(dev);
63678214Scg	chn_init(d->fakechan, NULL, 0);
63755494Scg
63873127Scg#ifdef SND_DYNSYSCTL
63970680Sjhb	sysctl_ctx_init(&d->sysctl_tree);
64070680Sjhb	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
64170943Sjhb				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
64270680Sjhb				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
64370943Sjhb	if (d->sysctl_tree_top == NULL) {
64470680Sjhb		sysctl_ctx_free(&d->sysctl_tree);
64570680Sjhb		goto no;
64670680Sjhb	}
64783614Scg	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
64883614Scg            OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
64973127Scg#endif
65078214Scg	if (numplay > 0)
65182180Scg		vchan_initsys(dev);
65278853Scg	if (numplay == 1)
65378853Scg		d->flags |= SD_F_AUTOVCHAN;
65478853Scg
65582180Scg	sndstat_register(dev, d->status, sndstat_prepare_pcm);
65650724Scg    	return 0;
65750724Scgno:
65877882Scg	snd_mtxfree(d->lock);
65950724Scg	return ENXIO;
66050724Scg}
66150724Scg
66265340Scgint
66365340Scgpcm_unregister(device_t dev)
66465207Scg{
66574763Scg    	struct snddev_info *d = device_get_softc(dev);
66677269Scg    	struct snddev_channel *sce;
66783614Scg	struct pcm_channel *ch;
66865207Scg
66977882Scg	snd_mtxlock(d->lock);
67078362Scg	if (d->inprog) {
67183476Sgreid		device_printf(dev, "unregister: operation in progress\n");
67278362Scg		snd_mtxunlock(d->lock);
67378362Scg		return EBUSY;
67478362Scg	}
67583476Sgreid	if (sndstat_busy() != 0) {
67683476Sgreid		device_printf(dev, "unregister: sndstat busy\n");
67783476Sgreid		snd_mtxunlock(d->lock);
67883476Sgreid		return EBUSY;
67983476Sgreid	}
68077269Scg	SLIST_FOREACH(sce, &d->channels, link) {
68183614Scg		ch = sce->channel;
68283614Scg		if (ch->refcount > 0) {
68395684Scg			device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
68477882Scg			snd_mtxunlock(d->lock);
68577269Scg			return EBUSY;
68677269Scg		}
68765487Scg	}
68878362Scg	if (mixer_uninit(dev)) {
68983476Sgreid		device_printf(dev, "unregister: mixer busy\n");
69077882Scg		snd_mtxunlock(d->lock);
69165487Scg		return EBUSY;
69265487Scg	}
69365207Scg
69474763Scg#ifdef SND_DYNSYSCTL
69574763Scg	d->sysctl_tree_top = NULL;
69674763Scg	sysctl_ctx_free(&d->sysctl_tree);
69774763Scg#endif
69878395Scg	while (!SLIST_EMPTY(&d->channels))
69977269Scg		pcm_killchan(dev);
70065207Scg
70174763Scg	chn_kill(d->fakechan);
70274763Scg	fkchan_kill(d->fakechan);
70373127Scg
704102374Snsayer	sndstat_unregister(dev);
70577882Scg	snd_mtxfree(d->lock);
70665340Scg	return 0;
70765207Scg}
70865207Scg
70982180Scg/************************************************************************/
71082180Scg
71182180Scgstatic int
71282180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
71382180Scg{
71482180Scg    	struct snddev_info *d;
71582180Scg    	struct snddev_channel *sce;
71682180Scg	struct pcm_channel *c;
71782180Scg	struct pcm_feeder *f;
71882180Scg    	int pc, rc, vc;
71982180Scg
72082180Scg	if (verbose < 1)
72182180Scg		return 0;
72282180Scg
72382180Scg	d = device_get_softc(dev);
72482180Scg	if (!d)
72582180Scg		return ENXIO;
72682180Scg
72782180Scg	snd_mtxlock(d->lock);
72882180Scg	if (!SLIST_EMPTY(&d->channels)) {
72982180Scg		pc = rc = vc = 0;
73082180Scg		SLIST_FOREACH(sce, &d->channels, link) {
73182180Scg			c = sce->channel;
73282180Scg			if (c->direction == PCMDIR_PLAY) {
73382180Scg				if (c->flags & CHN_F_VIRTUAL)
73482180Scg					vc++;
73582180Scg				else
73682180Scg					pc++;
73782180Scg			} else
73882180Scg				rc++;
73982180Scg		}
74083614Scg		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
74182180Scg				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
74282180Scg#ifdef USING_DEVFS
74382180Scg				(device_get_unit(dev) == snd_unit)? " default" : ""
74482180Scg#else
74582180Scg				""
74682180Scg#endif
74782180Scg				);
74882180Scg		if (verbose <= 1)
74982180Scg			goto skipverbose;
75082180Scg		SLIST_FOREACH(sce, &d->channels, link) {
75182180Scg			c = sce->channel;
75282479Scg			sbuf_printf(s, "\n\t");
75382479Scg
75482479Scg			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
75589691Scg			sbuf_printf(s, "spd %d", c->speed);
75689691Scg			if (c->speed != sndbuf_getspd(c->bufhard))
75789691Scg				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
75889691Scg			sbuf_printf(s, ", fmt 0x%08x", c->format);
75989691Scg			if (c->format != sndbuf_getfmt(c->bufhard))
76089691Scg				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
76189691Scg			sbuf_printf(s, ", flags %08x", c->flags);
76282180Scg			if (c->pid != -1)
76382180Scg				sbuf_printf(s, ", pid %d", c->pid);
76482180Scg			sbuf_printf(s, "\n\t");
76589691Scg
76682492Scg			if (c->bufhard != NULL && c->bufsoft != NULL) {
76782479Scg				sbuf_printf(s, "interrupts %d, ", c->interrupts);
76882479Scg				if (c->direction == PCMDIR_REC)
76982479Scg					sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
77082479Scg						c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
77182479Scg				else
77282492Scg					sbuf_printf(s, "underruns %d, ready %d",
77382492Scg						c->xruns, sndbuf_getready(c->bufsoft));
77482479Scg				sbuf_printf(s, "\n\t");
77582479Scg			}
77689691Scg
77789691Scg			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
77889691Scg			sbuf_printf(s, " -> ");
77982180Scg			f = c->feeder;
78089691Scg			while (f->source != NULL)
78189691Scg				f = f->source;
78289691Scg			while (f != NULL) {
78382180Scg				sbuf_printf(s, "%s", f->class->name);
78482180Scg				if (f->desc->type == FEEDER_FMT)
78589691Scg					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
78682180Scg				if (f->desc->type == FEEDER_RATE)
78789691Scg					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
78882180Scg				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
78989691Scg					sbuf_printf(s, "(0x%08x)", f->desc->out);
79089691Scg				sbuf_printf(s, " -> ");
79189691Scg				f = f->parent;
79282180Scg			}
79389691Scg			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
79482180Scg		}
79582180Scg	} else
79682180Scg		sbuf_printf(s, " (mixer only)");
79796928Speterskipverbose:
79882180Scg	snd_mtxunlock(d->lock);
79982180Scg
80082180Scg	return 0;
80182180Scg}
80282180Scg
80382180Scg/************************************************************************/
80482180Scg
80582180Scg#ifdef SND_DYNSYSCTL
80682180Scgint
80782180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
80882180Scg{
80982180Scg	struct snddev_info *d;
81082180Scg    	struct snddev_channel *sce;
81182180Scg	struct pcm_channel *c;
812100654Sgreen	int err, newcnt, cnt;
81382180Scg
81482180Scg	d = oidp->oid_arg1;
81582180Scg
81682180Scg	pcm_lock(d);
81782180Scg	cnt = 0;
81882180Scg	SLIST_FOREACH(sce, &d->channels, link) {
81982180Scg		c = sce->channel;
82082180Scg		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
82182180Scg			cnt++;
82282180Scg	}
82382180Scg	newcnt = cnt;
82482180Scg
825100654Sgreen	pcm_unlock(d);
82682180Scg	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
827100654Sgreen	pcm_lock(d);
828100654Sgreen	/*
829100654Sgreen	 * Since we dropped the pcm_lock, reload cnt now as it may
830100654Sgreen	 * have changed.
831100654Sgreen	 */
832100654Sgreen	cnt = 0;
833100654Sgreen	SLIST_FOREACH(sce, &d->channels, link) {
834100654Sgreen		c = sce->channel;
835100654Sgreen		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
836100654Sgreen			cnt++;
837100654Sgreen	}
83882180Scg	if (err == 0 && req->newptr != NULL) {
83982180Scg		if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
84082180Scg			pcm_unlock(d);
84182180Scg			return EINVAL;
84282180Scg		}
84382180Scg
84482180Scg		if (newcnt > cnt) {
84582180Scg			/* add new vchans - find a parent channel first */
84682180Scg			SLIST_FOREACH(sce, &d->channels, link) {
84782180Scg				c = sce->channel;
84882180Scg				/* not a candidate if not a play channel */
84982180Scg				if (c->direction != PCMDIR_PLAY)
85096928Speter					continue;
85182180Scg				/* not a candidate if a virtual channel */
85282180Scg				if (c->flags & CHN_F_VIRTUAL)
85396928Speter					continue;
85482180Scg				/* not a candidate if it's in use */
85582180Scg				if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
85696928Speter					continue;
85782180Scg				/*
85882180Scg				 * if we get here we're a nonvirtual play channel, and either
85982180Scg				 * 1) not busy
86082180Scg				 * 2) busy with children, not directly open
86182180Scg				 *
86282180Scg				 * thus we can add children
86382180Scg				 */
86482180Scg				goto addok;
86582180Scg			}
86682180Scg			pcm_unlock(d);
86782180Scg			return EBUSY;
86882180Scgaddok:
86982180Scg			c->flags |= CHN_F_BUSY;
87082180Scg			while (err == 0 && newcnt > cnt) {
87182180Scg				err = vchan_create(c);
87282180Scg				if (err == 0)
87382180Scg					cnt++;
87482180Scg			}
87582180Scg			if (SLIST_EMPTY(&c->children))
87682180Scg				c->flags &= ~CHN_F_BUSY;
87782180Scg		} else if (newcnt < cnt) {
87882180Scg			while (err == 0 && newcnt < cnt) {
87982180Scg				SLIST_FOREACH(sce, &d->channels, link) {
88082180Scg					c = sce->channel;
88182180Scg					if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
88282180Scg						goto remok;
88382180Scg				}
88482180Scg				pcm_unlock(d);
88582180Scg				return EINVAL;
88682180Scgremok:
88782180Scg				err = vchan_destroy(c);
88882180Scg				if (err == 0)
88982180Scg					cnt--;
89082180Scg			}
89182180Scg		}
89282180Scg	}
89382180Scg
89482180Scg	pcm_unlock(d);
89582180Scg	return err;
89682180Scg}
89782180Scg#endif
89882180Scg
89982180Scg/************************************************************************/
90082180Scg
90165340Scgstatic moduledata_t sndpcm_mod = {
90265340Scg	"snd_pcm",
90378362Scg	NULL,
90465340Scg	NULL
90565340Scg};
90665340ScgDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
90765340ScgMODULE_VERSION(snd_pcm, PCM_MODVER);
908