sound.c revision 78670
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 *
2750733Speter * $FreeBSD: head/sys/dev/sound/pcm/sound.c 78670 2001-06-23 17:36:51Z cg $
2850724Scg */
2950724Scg
3053465Scg#include <dev/sound/pcm/sound.h>
3177269Scg#include <dev/sound/pcm/vchan.h>
3265207Scg#include <sys/sysctl.h>
3350724Scg
3478362Scgdevclass_t pcm_devclass;
3550724Scg
3673127Scg#ifdef USING_DEVFS
3778362Scgint snd_unit = 0;
3877900SpeterTUNABLE_INT("hw.snd.unit", &snd_unit);
3973127Scg#endif
4050724Scg
4173127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
4273127Scg
4373131Scgvoid *
4473131Scgsnd_mtxcreate(const char *desc)
4573131Scg{
4673131Scg#ifdef USING_MUTEX
4773131Scg	struct mtx *m;
4873131Scg
4973131Scg	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
5073131Scg	if (m == NULL)
5173131Scg		return NULL;
5273131Scg	mtx_init(m, desc, MTX_RECURSE);
5373131Scg	return m;
5473131Scg#else
5573757Scg	return (void *)0xcafebabe;
5673131Scg#endif
5773131Scg}
5873131Scg
5973131Scgvoid
6073131Scgsnd_mtxfree(void *m)
6173131Scg{
6273131Scg#ifdef USING_MUTEX
6373131Scg	struct mtx *mtx = m;
6473131Scg
6573131Scg	mtx_assert(mtx, MA_OWNED);
6673131Scg	mtx_destroy(mtx);
6773131Scg	free(mtx, M_DEVBUF);
6873131Scg#endif
6973131Scg}
7073131Scg
7173131Scgvoid
7273131Scgsnd_mtxassert(void *m)
7373131Scg{
7473131Scg#ifdef USING_MUTEX
7578670Scg#ifdef INVARIANTS
7673131Scg	struct mtx *mtx = m;
7773131Scg
7873131Scg	mtx_assert(mtx, MA_OWNED);
7973131Scg#endif
8078670Scg#endif
8173131Scg}
8273131Scg
8373131Scgvoid
8473131Scgsnd_mtxlock(void *m)
8573131Scg{
8673131Scg#ifdef USING_MUTEX
8773131Scg	struct mtx *mtx = m;
8873131Scg
8973131Scg	mtx_lock(mtx);
9073131Scg#endif
9173131Scg}
9273131Scg
9373131Scgvoid
9473131Scgsnd_mtxunlock(void *m)
9573131Scg{
9673131Scg#ifdef USING_MUTEX
9773131Scg	struct mtx *mtx = m;
9873131Scg
9973131Scg	mtx_unlock(mtx);
10073131Scg#endif
10173131Scg}
10273131Scg
10373131Scgint
10473131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
10573131Scg{
10673131Scg#ifdef USING_MUTEX
10773131Scg	flags &= INTR_MPSAFE;
10878366Speter	flags |= INTR_TYPE_AV;
10973131Scg#else
11078366Speter	flags = INTR_TYPE_AV;
11173131Scg#endif
11273131Scg	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
11373131Scg}
11473131Scg
11578214Scg/* return a locked channel */
11677269Scgstruct pcm_channel *
11778214Scgpcm_chnalloc(struct snddev_info *d, int direction, pid_t pid)
11877269Scg{
11977269Scg	struct pcm_channel *c;
12077269Scg    	struct snddev_channel *sce;
12177269Scg
12278214Scg	snd_mtxassert(d->lock);
12377269Scg	SLIST_FOREACH(sce, &d->channels, link) {
12477269Scg		c = sce->channel;
12577882Scg		CHN_LOCK(c);
12677269Scg		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
12777269Scg			c->flags |= CHN_F_BUSY;
12878214Scg			c->pid = pid;
12977269Scg			return c;
13077269Scg		}
13177882Scg		CHN_UNLOCK(c);
13277269Scg	}
13377269Scg	return NULL;
13477269Scg}
13577269Scg
13678214Scg/* release a locked channel and unlock it */
13777269Scgint
13878214Scgpcm_chnrelease(struct pcm_channel *c)
13977269Scg{
14078214Scg	CHN_LOCKASSERT(c);
14177269Scg	c->flags &= ~CHN_F_BUSY;
14278214Scg	c->pid = -1;
14377882Scg	CHN_UNLOCK(c);
14477269Scg	return 0;
14577269Scg}
14677269Scg
14777269Scgint
14877269Scgpcm_chnref(struct pcm_channel *c, int ref)
14977269Scg{
15077882Scg	int r;
15177882Scg
15278214Scg	CHN_LOCKASSERT(c);
15377269Scg	c->refcount += ref;
15477882Scg	r = c->refcount;
15577882Scg	return r;
15677269Scg}
15777269Scg
15873127Scg#ifdef USING_DEVFS
15965340Scgstatic int
16065340Scgsysctl_hw_sndunit(SYSCTL_HANDLER_ARGS)
16165340Scg{
16278214Scg	struct snddev_info *d;
16365340Scg	int error, unit;
16465340Scg
16565340Scg	unit = snd_unit;
16665340Scg	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
16765340Scg	if (error == 0 && req->newptr != NULL) {
16878396Scg		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
16978214Scg			return EINVAL;
17078214Scg		d = devclass_get_softc(pcm_devclass, unit);
17178395Scg		if (d == NULL || SLIST_EMPTY(&d->channels))
17278214Scg			return EINVAL;
17365340Scg		snd_unit = unit;
17465340Scg	}
17565340Scg	return (error);
17665340Scg}
17770617SjhbSYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
17865340Scg            0, sizeof(int), sysctl_hw_sndunit, "I", "");
17973127Scg#endif
18065340Scg
18177269Scgstruct pcm_channel *
18277269Scgpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
18350724Scg{
18477269Scg	struct pcm_channel *ch;
18565340Scg	char *dirs;
18677269Scg    	int err;
18750724Scg
18877269Scg	switch(dir) {
18977269Scg	case PCMDIR_PLAY:
19077269Scg		dirs = "play";
19177269Scg		break;
19277269Scg	case PCMDIR_REC:
19377269Scg		dirs = "record";
19477269Scg		break;
19577269Scg	case PCMDIR_VIRTUAL:
19677269Scg		dirs = "virtual";
19777269Scg		dir = PCMDIR_PLAY;
19877269Scg		break;
19977269Scg	default:
20077269Scg		return NULL;
20177269Scg	}
20265340Scg
20377269Scg	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
20477269Scg	if (!ch)
20577269Scg		return NULL;
20677269Scg
20777269Scg	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
20877269Scg	if (!ch->methods) {
20977269Scg		free(ch, M_DEVBUF);
21077269Scg		return NULL;
21161344Scg	}
21277269Scg
21377269Scg	ch->pid = -1;
21477269Scg	ch->parentsnddev = d;
21577269Scg	ch->parentchannel = parent;
21677269Scg	snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs);
21777269Scg
21870134Scg	err = chn_init(ch, devinfo, dir);
21970134Scg	if (err) {
22077269Scg		device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err);
22177269Scg		kobj_delete(ch->methods, M_DEVBUF);
22277269Scg		free(ch, M_DEVBUF);
22377269Scg		return NULL;
22455483Scg	}
22577269Scg
22677269Scg	return ch;
22777269Scg}
22877269Scg
22977269Scgint
23077269Scgpcm_chn_destroy(struct pcm_channel *ch)
23177269Scg{
23277269Scg	int err;
23377269Scg
23477269Scg	err = chn_kill(ch);
23577269Scg	if (err) {
23677269Scg		device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err);
23777269Scg		return err;
23877269Scg	}
23977269Scg
24077269Scg	kobj_delete(ch->methods, M_DEVBUF);
24177269Scg	free(ch, M_DEVBUF);
24277269Scg
24377269Scg	return 0;
24477269Scg}
24577269Scg
24677269Scgint
24777269Scgpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
24877269Scg{
24977269Scg    	struct snddev_channel *sce;
25077269Scg    	int unit = device_get_unit(d->dev);
25177269Scg
25278214Scg	snd_mtxlock(d->lock);
25378214Scg
25477269Scg	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
25577269Scg	if (!sce) {
25678214Scg		snd_mtxunlock(d->lock);
25777269Scg		return ENOMEM;
25877269Scg	}
25977269Scg
26077269Scg	sce->channel = ch;
26177269Scg	SLIST_INSERT_HEAD(&d->channels, sce, link);
26277269Scg
26378362Scg	dsp_register(unit, d->chancount);
26478362Scg    	d->chancount++;
26577269Scg
26677882Scg	snd_mtxunlock(d->lock);
26777269Scg
26850724Scg	return 0;
26950724Scg}
27050724Scg
27177269Scgint
27277269Scgpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
27365340Scg{
27477269Scg    	struct snddev_channel *sce;
27577269Scg    	int unit = device_get_unit(d->dev);
27665340Scg
27777882Scg	snd_mtxlock(d->lock);
27877269Scg	SLIST_FOREACH(sce, &d->channels, link) {
27977269Scg		if (sce->channel == ch)
28077269Scg			goto gotit;
28165340Scg	}
28277882Scg	snd_mtxunlock(d->lock);
28377269Scg	return EINVAL;
28477269Scggotit:
28565340Scg	d->chancount--;
28677269Scg	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
28777269Scg	free(sce, M_DEVBUF);
28877269Scg
28978362Scg	dsp_unregister(unit, d->chancount);
29077882Scg	snd_mtxunlock(d->lock);
29177269Scg
29265340Scg	return 0;
29365340Scg}
29465340Scg
29550724Scgint
29677269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
29777269Scg{
29877269Scg    	struct snddev_info *d = device_get_softc(dev);
29977269Scg	struct pcm_channel *ch;
30077269Scg    	int err;
30177269Scg
30277269Scg	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
30377269Scg	if (!ch) {
30477269Scg		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
30577269Scg		return ENODEV;
30677269Scg	}
30777269Scg	err = pcm_chn_add(d, ch);
30877269Scg	if (err) {
30977269Scg		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
31077269Scg		pcm_chn_destroy(ch);
31177269Scg	}
31277269Scg
31377269Scg	return err;
31477269Scg}
31577269Scg
31677269Scgstatic int
31777269Scgpcm_killchan(device_t dev)
31877269Scg{
31977269Scg    	struct snddev_info *d = device_get_softc(dev);
32077269Scg    	struct snddev_channel *sce;
32177269Scg
32277882Scg	snd_mtxlock(d->lock);
32377269Scg	sce = SLIST_FIRST(&d->channels);
32477882Scg	snd_mtxunlock(d->lock);
32577269Scg
32677269Scg	return pcm_chn_remove(d, sce->channel);
32777269Scg}
32877269Scg
32977269Scgint
33050724Scgpcm_setstatus(device_t dev, char *str)
33150724Scg{
33274763Scg    	struct snddev_info *d = device_get_softc(dev);
33377882Scg
33477882Scg	snd_mtxlock(d->lock);
33550724Scg	strncpy(d->status, str, SND_STATUSLEN);
33677882Scg	snd_mtxunlock(d->lock);
33750724Scg	return 0;
33850724Scg}
33950724Scg
34050724Scgu_int32_t
34150724Scgpcm_getflags(device_t dev)
34250724Scg{
34374763Scg    	struct snddev_info *d = device_get_softc(dev);
34477882Scg
34550724Scg	return d->flags;
34650724Scg}
34750724Scg
34850724Scgvoid
34950724Scgpcm_setflags(device_t dev, u_int32_t val)
35050724Scg{
35174763Scg    	struct snddev_info *d = device_get_softc(dev);
35278362Scg
35350724Scg	d->flags = val;
35450724Scg}
35550724Scg
35658384Scgvoid *
35758384Scgpcm_getdevinfo(device_t dev)
35858384Scg{
35974763Scg    	struct snddev_info *d = device_get_softc(dev);
36077882Scg
36158384Scg	return d->devinfo;
36258384Scg}
36358384Scg
36450724Scg/* This is the generic init routine */
36550724Scgint
36650724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec)
36750724Scg{
36874763Scg    	struct snddev_info *d = device_get_softc(dev);
36950724Scg
37077882Scg	d->lock = snd_mtxcreate(device_get_nameunit(dev));
37177882Scg	snd_mtxlock(d->lock);
37277269Scg
37361886Scg	d->dev = dev;
37450724Scg	d->devinfo = devinfo;
37577269Scg	d->chancount = 0;
37678362Scg	d->inprog = 0;
37750724Scg
37878362Scg	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
37978214Scg		d->flags |= SD_F_SIMPLEX;
38078362Scg
38178214Scg	d->fakechan = fkchan_setup(dev);
38278214Scg	chn_init(d->fakechan, NULL, 0);
38355494Scg
38473127Scg#ifdef SND_DYNSYSCTL
38570680Sjhb	sysctl_ctx_init(&d->sysctl_tree);
38670680Sjhb	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
38770943Sjhb				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
38870680Sjhb				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
38970943Sjhb	if (d->sysctl_tree_top == NULL) {
39070680Sjhb		sysctl_ctx_free(&d->sysctl_tree);
39170680Sjhb		goto no;
39270680Sjhb	}
39373127Scg#endif
39478214Scg	if (numplay > 0)
39578214Scg		vchan_initsys(d);
39677882Scg	snd_mtxunlock(d->lock);
39750724Scg    	return 0;
39850724Scgno:
39977882Scg	snd_mtxfree(d->lock);
40050724Scg	return ENXIO;
40150724Scg}
40250724Scg
40365340Scgint
40465340Scgpcm_unregister(device_t dev)
40565207Scg{
40674763Scg    	struct snddev_info *d = device_get_softc(dev);
40777269Scg    	struct snddev_channel *sce;
40865207Scg
40977882Scg	snd_mtxlock(d->lock);
41078362Scg	if (d->inprog) {
41178362Scg		device_printf(dev, "unregister: operation in progress");
41278362Scg		snd_mtxunlock(d->lock);
41378362Scg		return EBUSY;
41478362Scg	}
41577269Scg	SLIST_FOREACH(sce, &d->channels, link) {
41677269Scg		if (sce->channel->refcount > 0) {
41777269Scg			device_printf(dev, "unregister: channel busy");
41877882Scg			snd_mtxunlock(d->lock);
41977269Scg			return EBUSY;
42077269Scg		}
42165487Scg	}
42278362Scg	if (mixer_uninit(dev)) {
42365487Scg		device_printf(dev, "unregister: mixer busy");
42477882Scg		snd_mtxunlock(d->lock);
42565487Scg		return EBUSY;
42665487Scg	}
42765207Scg
42874763Scg#ifdef SND_DYNSYSCTL
42974763Scg	d->sysctl_tree_top = NULL;
43074763Scg	sysctl_ctx_free(&d->sysctl_tree);
43174763Scg#endif
43278395Scg	while (!SLIST_EMPTY(&d->channels))
43377269Scg		pcm_killchan(dev);
43465207Scg
43574763Scg	chn_kill(d->fakechan);
43674763Scg	fkchan_kill(d->fakechan);
43773127Scg
43877882Scg	snd_mtxfree(d->lock);
43965340Scg	return 0;
44065207Scg}
44165207Scg
44265340Scgstatic moduledata_t sndpcm_mod = {
44365340Scg	"snd_pcm",
44478362Scg	NULL,
44565340Scg	NULL
44665340Scg};
44765340ScgDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
44865340ScgMODULE_VERSION(snd_pcm, PCM_MODVER);
449