xref: /freebsd/sys/dev/sound/pcm/sound.c (revision 49c5e6e20a9646097a73154df257a160c0c4dc0f)
1987e5972SCameron Grant /*
2987e5972SCameron Grant  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3987e5972SCameron Grant  * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
4987e5972SCameron Grant  * All rights reserved.
5987e5972SCameron Grant  *
6987e5972SCameron Grant  * Redistribution and use in source and binary forms, with or without
7987e5972SCameron Grant  * modification, are permitted provided that the following conditions
8987e5972SCameron Grant  * are met:
9987e5972SCameron Grant  * 1. Redistributions of source code must retain the above copyright
10987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer.
11987e5972SCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
12987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer in the
13987e5972SCameron Grant  *    documentation and/or other materials provided with the distribution.
14987e5972SCameron Grant  *
15987e5972SCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16987e5972SCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17987e5972SCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18987e5972SCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19987e5972SCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20987e5972SCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21987e5972SCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22987e5972SCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23987e5972SCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24987e5972SCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25987e5972SCameron Grant  * SUCH DAMAGE.
26987e5972SCameron Grant  *
2753c5a968SPeter Wemm  * $FreeBSD$
28987e5972SCameron Grant  */
29987e5972SCameron Grant 
30ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
31285648f9SCameron Grant #include <dev/sound/pcm/vchan.h>
327c438dbeSCameron Grant #include <sys/sysctl.h>
33285648f9SCameron Grant #include <sys/sbuf.h>
34285648f9SCameron Grant 
35285648f9SCameron Grant #include "feeder_if.h"
36285648f9SCameron Grant 
37285648f9SCameron Grant #undef	SNDSTAT_VERBOSE
38987e5972SCameron Grant 
3933dbf14aSCameron Grant static dev_t status_dev = 0;
40285648f9SCameron Grant static int do_status(int action, struct uio *buf);
41987e5972SCameron Grant 
42987e5972SCameron Grant static d_open_t sndopen;
43987e5972SCameron Grant static d_close_t sndclose;
44987e5972SCameron Grant static d_ioctl_t sndioctl;
45987e5972SCameron Grant static d_read_t sndread;
46987e5972SCameron Grant static d_write_t sndwrite;
47987e5972SCameron Grant static d_mmap_t sndmmap;
48987e5972SCameron Grant static d_poll_t sndpoll;
49987e5972SCameron Grant 
50987e5972SCameron Grant #define CDEV_MAJOR 30
51987e5972SCameron Grant static struct cdevsw snd_cdevsw = {
52987e5972SCameron Grant 	/* open */	sndopen,
53987e5972SCameron Grant 	/* close */	sndclose,
54987e5972SCameron Grant 	/* read */	sndread,
55987e5972SCameron Grant 	/* write */	sndwrite,
56987e5972SCameron Grant 	/* ioctl */	sndioctl,
57987e5972SCameron Grant 	/* poll */	sndpoll,
58987e5972SCameron Grant 	/* mmap */	sndmmap,
59987e5972SCameron Grant 	/* strategy */	nostrategy,
60987e5972SCameron Grant 	/* name */	"snd",
61987e5972SCameron Grant 	/* maj */	CDEV_MAJOR,
62987e5972SCameron Grant 	/* dump */	nodump,
63987e5972SCameron Grant 	/* psize */	nopsize,
6466ef8af5SCameron Grant 	/* flags */	D_TRACKCLOSE,
65987e5972SCameron Grant };
66987e5972SCameron Grant 
67dd186369SCameron Grant /*
68dd186369SCameron Grant PROPOSAL:
69987e5972SCameron Grant each unit needs:
70987e5972SCameron Grant status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
71987e5972SCameron Grant dspW and audio are deprecated.
72987e5972SCameron Grant dsp needs min 64 channels, will give it 256
73987e5972SCameron Grant 
74dd186369SCameron Grant minor = (unit << 20) + (dev << 16) + channel
75dd186369SCameron Grant currently minor = (channel << 16) + (unit << 4) + dev
76987e5972SCameron Grant 
77987e5972SCameron Grant nomenclature:
78987e5972SCameron Grant 	/dev/pcmX/dsp.(0..255)
79987e5972SCameron Grant 	/dev/pcmX/dspW
80987e5972SCameron Grant 	/dev/pcmX/audio
81987e5972SCameron Grant 	/dev/pcmX/status
82987e5972SCameron Grant 	/dev/pcmX/mixer
83987e5972SCameron Grant 	[etc.]
84987e5972SCameron Grant */
85987e5972SCameron Grant 
86987e5972SCameron Grant #define PCMMINOR(x) (minor(x))
87a618cffeSCameron Grant #define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16)
88987e5972SCameron Grant #define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
89987e5972SCameron Grant #define PCMDEV(x)   (PCMMINOR(x) & 0x0000000f)
90dd186369SCameron Grant #define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
91987e5972SCameron Grant 
92987e5972SCameron Grant static devclass_t pcm_devclass;
9382db23e2SCameron Grant 
9482db23e2SCameron Grant #ifdef USING_DEVFS
9581930014SPeter Wemm static int snd_unit = 0;
964422746fSPeter Wemm TUNABLE_INT("hw.snd.unit", snd_unit);
9782db23e2SCameron Grant #endif
98987e5972SCameron Grant 
9982db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
10082db23e2SCameron Grant 
10137209180SCameron Grant void *
10237209180SCameron Grant snd_mtxcreate(const char *desc)
10337209180SCameron Grant {
10437209180SCameron Grant #ifdef USING_MUTEX
10537209180SCameron Grant 	struct mtx *m;
10637209180SCameron Grant 
10737209180SCameron Grant 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
10837209180SCameron Grant 	if (m == NULL)
10937209180SCameron Grant 		return NULL;
11037209180SCameron Grant 	mtx_init(m, desc, MTX_RECURSE);
11137209180SCameron Grant 	return m;
11237209180SCameron Grant #else
113a983d575SCameron Grant 	return (void *)0xcafebabe;
11437209180SCameron Grant #endif
11537209180SCameron Grant }
11637209180SCameron Grant 
11737209180SCameron Grant void
11837209180SCameron Grant snd_mtxfree(void *m)
11937209180SCameron Grant {
12037209180SCameron Grant #ifdef USING_MUTEX
12137209180SCameron Grant 	struct mtx *mtx = m;
12237209180SCameron Grant 
12337209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
12437209180SCameron Grant 	mtx_destroy(mtx);
12537209180SCameron Grant 	free(mtx, M_DEVBUF);
12637209180SCameron Grant #endif
12737209180SCameron Grant }
12837209180SCameron Grant 
12937209180SCameron Grant void
13037209180SCameron Grant snd_mtxassert(void *m)
13137209180SCameron Grant {
13237209180SCameron Grant #ifdef USING_MUTEX
13337209180SCameron Grant 	struct mtx *mtx = m;
13437209180SCameron Grant 
13537209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
13637209180SCameron Grant #endif
13737209180SCameron Grant }
13837209180SCameron Grant 
13937209180SCameron Grant void
14037209180SCameron Grant snd_mtxlock(void *m)
14137209180SCameron Grant {
14237209180SCameron Grant #ifdef USING_MUTEX
14337209180SCameron Grant 	struct mtx *mtx = m;
14437209180SCameron Grant 
14537209180SCameron Grant 	mtx_lock(mtx);
14637209180SCameron Grant #endif
14737209180SCameron Grant }
14837209180SCameron Grant 
14937209180SCameron Grant void
15037209180SCameron Grant snd_mtxunlock(void *m)
15137209180SCameron Grant {
15237209180SCameron Grant #ifdef USING_MUTEX
15337209180SCameron Grant 	struct mtx *mtx = m;
15437209180SCameron Grant 
15537209180SCameron Grant 	mtx_unlock(mtx);
15637209180SCameron Grant #endif
15737209180SCameron Grant }
15837209180SCameron Grant 
15937209180SCameron Grant int
16037209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
16137209180SCameron Grant {
16237209180SCameron Grant #ifdef USING_MUTEX
16337209180SCameron Grant 	flags &= INTR_MPSAFE;
16437209180SCameron Grant 	flags |= INTR_TYPE_TTY;
16537209180SCameron Grant #else
16637209180SCameron Grant 	flags = INTR_TYPE_TTY;
16737209180SCameron Grant #endif
16837209180SCameron Grant 	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
16937209180SCameron Grant }
17037209180SCameron Grant 
171285648f9SCameron Grant struct pcm_channel *
172285648f9SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction)
173285648f9SCameron Grant {
174285648f9SCameron Grant 	struct pcm_channel *c;
175285648f9SCameron Grant     	struct snddev_channel *sce;
176285648f9SCameron Grant 
17749c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
178285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
179285648f9SCameron Grant 		c = sce->channel;
18049c5e6e2SCameron Grant 		CHN_LOCK(c);
181285648f9SCameron Grant 		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
182285648f9SCameron Grant 			c->flags |= CHN_F_BUSY;
18349c5e6e2SCameron Grant 			CHN_UNLOCK(c);
18449c5e6e2SCameron Grant 			snd_mtxunlock(d->lock);
185285648f9SCameron Grant 			return c;
186285648f9SCameron Grant 		}
18749c5e6e2SCameron Grant 		CHN_UNLOCK(c);
188285648f9SCameron Grant 	}
18949c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
190285648f9SCameron Grant 	return NULL;
191285648f9SCameron Grant }
192285648f9SCameron Grant 
193285648f9SCameron Grant int
194285648f9SCameron Grant pcm_chnfree(struct pcm_channel *c)
195285648f9SCameron Grant {
19649c5e6e2SCameron Grant 	CHN_LOCK(c);
197285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
19849c5e6e2SCameron Grant 	CHN_UNLOCK(c);
199285648f9SCameron Grant 	return 0;
200285648f9SCameron Grant }
201285648f9SCameron Grant 
202285648f9SCameron Grant int
203285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
204285648f9SCameron Grant {
20549c5e6e2SCameron Grant 	int r;
20649c5e6e2SCameron Grant 
20749c5e6e2SCameron Grant 	CHN_LOCK(c);
208285648f9SCameron Grant 	c->refcount += ref;
20949c5e6e2SCameron Grant 	r = c->refcount;
21049c5e6e2SCameron Grant 	CHN_UNLOCK(c);
21149c5e6e2SCameron Grant 	return r;
212285648f9SCameron Grant }
213285648f9SCameron Grant 
21482db23e2SCameron Grant #ifdef USING_DEVFS
21533dbf14aSCameron Grant static void
21633dbf14aSCameron Grant pcm_makelinks(void *dummy)
217987e5972SCameron Grant {
21833dbf14aSCameron Grant 	int unit;
21933dbf14aSCameron Grant 	dev_t pdev;
22033dbf14aSCameron Grant 	static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0;
221285648f9SCameron Grant     	struct snddev_info *d;
22233dbf14aSCameron Grant 
223f776b5abSCameron Grant 	if (pcm_devclass == NULL || devfs_present == 0)
22433dbf14aSCameron Grant 		return;
22533dbf14aSCameron Grant 	if (dsp) {
22633dbf14aSCameron Grant 		destroy_dev(dsp);
22733dbf14aSCameron Grant 		dsp = 0;
228987e5972SCameron Grant 	}
22933dbf14aSCameron Grant 	if (dspW) {
23033dbf14aSCameron Grant 		destroy_dev(dspW);
23133dbf14aSCameron Grant 		dspW = 0;
23233dbf14aSCameron Grant 	}
23333dbf14aSCameron Grant 	if (audio) {
23433dbf14aSCameron Grant 		destroy_dev(audio);
23533dbf14aSCameron Grant 		audio = 0;
23633dbf14aSCameron Grant 	}
23733dbf14aSCameron Grant 	if (mixer) {
23833dbf14aSCameron Grant 		destroy_dev(mixer);
23933dbf14aSCameron Grant 		mixer = 0;
24033dbf14aSCameron Grant 	}
24133dbf14aSCameron Grant 
24233dbf14aSCameron Grant 	unit = snd_unit;
24333dbf14aSCameron Grant 	if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass))
24433dbf14aSCameron Grant 		return;
245285648f9SCameron Grant 	d = devclass_get_softc(pcm_devclass, unit);
246285648f9SCameron Grant 	if (d == NULL || d->chancount == 0)
24733dbf14aSCameron Grant 		return;
24833dbf14aSCameron Grant 
24933dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0));
25033dbf14aSCameron Grant 	dsp = make_dev_alias(pdev, "dsp");
25133dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0));
25233dbf14aSCameron Grant 	dspW = make_dev_alias(pdev, "dspW");
25333dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0));
25433dbf14aSCameron Grant 	audio = make_dev_alias(pdev, "audio");
25533dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
25633dbf14aSCameron Grant 	mixer = make_dev_alias(pdev, "mixer");
25733dbf14aSCameron Grant }
25833dbf14aSCameron Grant 
25933dbf14aSCameron Grant static int
26033dbf14aSCameron Grant sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS)
26133dbf14aSCameron Grant {
26233dbf14aSCameron Grant 	int error, unit;
26333dbf14aSCameron Grant 
26433dbf14aSCameron Grant 	unit = snd_unit;
26533dbf14aSCameron Grant 	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
26633dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
26733dbf14aSCameron Grant 		snd_unit = unit;
26833dbf14aSCameron Grant 		pcm_makelinks(NULL);
26933dbf14aSCameron Grant 	}
27033dbf14aSCameron Grant 	return (error);
27133dbf14aSCameron Grant }
272b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
27333dbf14aSCameron Grant             0, sizeof(int), sysctl_hw_sndunit, "I", "");
27482db23e2SCameron Grant #endif
275987e5972SCameron Grant 
276285648f9SCameron Grant struct pcm_channel *
277285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
278987e5972SCameron Grant {
279285648f9SCameron Grant 	struct pcm_channel *ch;
28033dbf14aSCameron Grant 	char *dirs;
2810f55ac6cSCameron Grant     	int err;
282987e5972SCameron Grant 
283285648f9SCameron Grant 	switch(dir) {
284285648f9SCameron Grant 	case PCMDIR_PLAY:
285285648f9SCameron Grant 		dirs = "play";
286285648f9SCameron Grant 		break;
287285648f9SCameron Grant 	case PCMDIR_REC:
288285648f9SCameron Grant 		dirs = "record";
289285648f9SCameron Grant 		break;
290285648f9SCameron Grant 	case PCMDIR_VIRTUAL:
291285648f9SCameron Grant 		dirs = "virtual";
292285648f9SCameron Grant 		dir = PCMDIR_PLAY;
293285648f9SCameron Grant 		break;
294285648f9SCameron Grant 	default:
295285648f9SCameron Grant 		return NULL;
2969c326820SCameron Grant 	}
297285648f9SCameron Grant 
298285648f9SCameron Grant 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
299285648f9SCameron Grant 	if (!ch)
300285648f9SCameron Grant 		return NULL;
301285648f9SCameron Grant 
3020f55ac6cSCameron Grant 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
303285648f9SCameron Grant 	if (!ch->methods) {
304285648f9SCameron Grant 		free(ch, M_DEVBUF);
305285648f9SCameron Grant 		return NULL;
306285648f9SCameron Grant 	}
307285648f9SCameron Grant 
308285648f9SCameron Grant 	ch->pid = -1;
309285648f9SCameron Grant 	ch->parentsnddev = d;
310285648f9SCameron Grant 	ch->parentchannel = parent;
311285648f9SCameron Grant 	snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs);
312285648f9SCameron Grant 
3130f55ac6cSCameron Grant 	err = chn_init(ch, devinfo, dir);
3140f55ac6cSCameron Grant 	if (err) {
315285648f9SCameron Grant 		device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err);
316285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
317285648f9SCameron Grant 		free(ch, M_DEVBUF);
318285648f9SCameron Grant 		return NULL;
319bbb5bf3dSCameron Grant 	}
320285648f9SCameron Grant 
321285648f9SCameron Grant 	return ch;
322285648f9SCameron Grant }
323285648f9SCameron Grant 
324285648f9SCameron Grant int
325285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
326285648f9SCameron Grant {
327285648f9SCameron Grant 	int err;
328285648f9SCameron Grant 
329285648f9SCameron Grant 	err = chn_kill(ch);
330285648f9SCameron Grant 	if (err) {
331285648f9SCameron Grant 		device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err);
332285648f9SCameron Grant 		return err;
333285648f9SCameron Grant 	}
334285648f9SCameron Grant 
335285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
336285648f9SCameron Grant 	free(ch, M_DEVBUF);
337285648f9SCameron Grant 
338285648f9SCameron Grant 	return 0;
339285648f9SCameron Grant }
340285648f9SCameron Grant 
341285648f9SCameron Grant int
342285648f9SCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
343285648f9SCameron Grant {
344285648f9SCameron Grant     	struct snddev_channel *sce;
345285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
346285648f9SCameron Grant 
347285648f9SCameron Grant 	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
348285648f9SCameron Grant 	if (!sce) {
349285648f9SCameron Grant 		free(ch, M_DEVBUF);
350285648f9SCameron Grant 		return ENOMEM;
351285648f9SCameron Grant 	}
352285648f9SCameron Grant 
35349c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
354285648f9SCameron Grant 	sce->channel = ch;
355285648f9SCameron Grant 	SLIST_INSERT_HEAD(&d->channels, sce, link);
356285648f9SCameron Grant 
3577207eca6SCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount),
3587207eca6SCameron Grant 		 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount);
3597207eca6SCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount),
3607207eca6SCameron Grant 		 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount);
36133dbf14aSCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount),
36233dbf14aSCameron Grant 		 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount);
3637207eca6SCameron Grant 	/* XXX SND_DEV_NORESET? */
364285648f9SCameron Grant 
36582db23e2SCameron Grant #ifdef USING_DEVFS
366285648f9SCameron Grant     	if (d->chancount++ == 0)
36733dbf14aSCameron Grant 		pcm_makelinks(NULL);
36882db23e2SCameron Grant #endif
36949c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
370285648f9SCameron Grant 
37133dbf14aSCameron Grant 	return 0;
37233dbf14aSCameron Grant }
37333dbf14aSCameron Grant 
374285648f9SCameron Grant int
375285648f9SCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
37633dbf14aSCameron Grant {
377285648f9SCameron Grant     	struct snddev_channel *sce;
378285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
37933dbf14aSCameron Grant 	dev_t pdev;
38033dbf14aSCameron Grant 
38149c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
382285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
383285648f9SCameron Grant 		if (sce->channel == ch)
384285648f9SCameron Grant 			goto gotit;
38533dbf14aSCameron Grant 	}
38649c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
387285648f9SCameron Grant 	return EINVAL;
388285648f9SCameron Grant gotit:
38933dbf14aSCameron Grant 	d->chancount--;
390285648f9SCameron Grant 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
391285648f9SCameron Grant 	free(sce, M_DEVBUF);
392285648f9SCameron Grant 
393285648f9SCameron Grant #ifdef USING_DEVFS
394285648f9SCameron Grant     	if (d->chancount == 0)
395285648f9SCameron Grant 		pcm_makelinks(NULL);
396285648f9SCameron Grant #endif
39733dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount));
39833dbf14aSCameron Grant 	destroy_dev(pdev);
39933dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount));
40033dbf14aSCameron Grant 	destroy_dev(pdev);
40133dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount));
40233dbf14aSCameron Grant 	destroy_dev(pdev);
40349c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
404285648f9SCameron Grant 
405987e5972SCameron Grant 	return 0;
406987e5972SCameron Grant }
407987e5972SCameron Grant 
408987e5972SCameron Grant int
409285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
410285648f9SCameron Grant {
411285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
412285648f9SCameron Grant 	struct pcm_channel *ch;
413285648f9SCameron Grant     	int err;
414285648f9SCameron Grant 
415285648f9SCameron Grant 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
416285648f9SCameron Grant 	if (!ch) {
417285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
418285648f9SCameron Grant 		return ENODEV;
419285648f9SCameron Grant 	}
420285648f9SCameron Grant 	err = pcm_chn_add(d, ch);
421285648f9SCameron Grant 	if (err) {
422285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
423285648f9SCameron Grant 		pcm_chn_destroy(ch);
424285648f9SCameron Grant 	}
425285648f9SCameron Grant 
426285648f9SCameron Grant 	return err;
427285648f9SCameron Grant }
428285648f9SCameron Grant 
429285648f9SCameron Grant static int
430285648f9SCameron Grant pcm_killchan(device_t dev)
431285648f9SCameron Grant {
432285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
433285648f9SCameron Grant     	struct snddev_channel *sce;
434285648f9SCameron Grant 
43549c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
436285648f9SCameron Grant 	sce = SLIST_FIRST(&d->channels);
43749c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
438285648f9SCameron Grant 
439285648f9SCameron Grant 	return pcm_chn_remove(d, sce->channel);
440285648f9SCameron Grant }
441285648f9SCameron Grant 
442285648f9SCameron Grant int
443987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
444987e5972SCameron Grant {
44566ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
44649c5e6e2SCameron Grant 
44749c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
448987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
44949c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
450987e5972SCameron Grant 	return 0;
451987e5972SCameron Grant }
452987e5972SCameron Grant 
453987e5972SCameron Grant u_int32_t
454987e5972SCameron Grant pcm_getflags(device_t dev)
455987e5972SCameron Grant {
45666ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
45749c5e6e2SCameron Grant 
458987e5972SCameron Grant 	return d->flags;
459987e5972SCameron Grant }
460987e5972SCameron Grant 
461987e5972SCameron Grant void
462987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
463987e5972SCameron Grant {
46466ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
46549c5e6e2SCameron Grant 
466987e5972SCameron Grant 	d->flags = val;
467987e5972SCameron Grant }
468987e5972SCameron Grant 
46939004e69SCameron Grant void *
47039004e69SCameron Grant pcm_getdevinfo(device_t dev)
47139004e69SCameron Grant {
47266ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
47349c5e6e2SCameron Grant 
47439004e69SCameron Grant 	return d->devinfo;
47539004e69SCameron Grant }
47639004e69SCameron Grant 
477987e5972SCameron Grant /* This is the generic init routine */
478987e5972SCameron Grant int
479987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
480987e5972SCameron Grant {
481987e5972SCameron Grant     	int sz, unit = device_get_unit(dev);
48266ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
483987e5972SCameron Grant 
48449c5e6e2SCameron Grant 	d->lock = snd_mtxcreate(device_get_nameunit(dev));
48549c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
486987e5972SCameron Grant     	if (!pcm_devclass) {
487987e5972SCameron Grant     		pcm_devclass = device_get_devclass(dev);
48833dbf14aSCameron Grant 		status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0),
489083279e4SSeigo Tanimura 			 UID_ROOT, GID_WHEEL, 0444, "sndstat");
490083279e4SSeigo Tanimura 	}
491285648f9SCameron Grant 
4927207eca6SCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
49311346231SSeigo Tanimura 		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
494285648f9SCameron Grant 
495e4d5b250SCameron Grant 	d->dev = dev;
496987e5972SCameron Grant 	d->devinfo = devinfo;
497285648f9SCameron Grant 	d->chancount = 0;
49833dbf14aSCameron Grant 	d->maxchans = numplay + numrec;
499285648f9SCameron Grant     	sz = d->maxchans * sizeof(struct pcm_channel *);
500833f7023SCameron Grant 
501833f7023SCameron Grant 	if (sz > 0) {
502285648f9SCameron Grant 		d->aplay = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
503285648f9SCameron Grant     		d->arec = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
504285648f9SCameron Grant     		if (!d->arec || !d->aplay) goto no;
505833f7023SCameron Grant 
506285648f9SCameron Grant 		if (numplay == 0 || numrec == 0)
507285648f9SCameron Grant 			d->flags |= SD_F_SIMPLEX;
508bd18f334SCameron Grant 
509285648f9SCameron Grant 		d->fakechan = fkchan_setup(dev);
510285648f9SCameron Grant 		chn_init(d->fakechan, NULL, 0);
511833f7023SCameron Grant 	}
512987e5972SCameron Grant 
51382db23e2SCameron Grant #ifdef SND_DYNSYSCTL
514cc486d80SJohn Baldwin 	sysctl_ctx_init(&d->sysctl_tree);
515cc486d80SJohn Baldwin 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
516a3e893e0SJohn Baldwin 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
517cc486d80SJohn Baldwin 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
518a3e893e0SJohn Baldwin 	if (d->sysctl_tree_top == NULL) {
519cc486d80SJohn Baldwin 		sysctl_ctx_free(&d->sysctl_tree);
520cc486d80SJohn Baldwin 		goto no;
521cc486d80SJohn Baldwin 	}
52282db23e2SCameron Grant #endif
52349c5e6e2SCameron Grant #if 1
524285648f9SCameron Grant 	vchan_initsys(d);
525285648f9SCameron Grant #endif
52649c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
527987e5972SCameron Grant     	return 0;
528987e5972SCameron Grant no:
529987e5972SCameron Grant 	if (d->aplay) free(d->aplay, M_DEVBUF);
530987e5972SCameron Grant 	if (d->arec) free(d->arec, M_DEVBUF);
53149c5e6e2SCameron Grant 	/* snd_mtxunlock(d->lock); */
53249c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
533987e5972SCameron Grant 	return ENXIO;
534987e5972SCameron Grant }
535987e5972SCameron Grant 
53633dbf14aSCameron Grant int
53733dbf14aSCameron Grant pcm_unregister(device_t dev)
5387c438dbeSCameron Grant {
539285648f9SCameron Grant     	int unit = device_get_unit(dev);
54066ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
541285648f9SCameron Grant     	struct snddev_channel *sce;
5427c438dbeSCameron Grant 	dev_t pdev;
5437c438dbeSCameron Grant 
54449c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
545285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
546285648f9SCameron Grant 		if (sce->channel->refcount > 0) {
547c9b53085SCameron Grant 			device_printf(dev, "unregister: channel busy");
54849c5e6e2SCameron Grant 			snd_mtxunlock(d->lock);
549285648f9SCameron Grant 			return EBUSY;
550285648f9SCameron Grant 		}
551c9b53085SCameron Grant 	}
5520f55ac6cSCameron Grant 	if (mixer_isbusy(d->mixer)) {
553c9b53085SCameron Grant 		device_printf(dev, "unregister: mixer busy");
55449c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
555c9b53085SCameron Grant 		return EBUSY;
556c9b53085SCameron Grant 	}
5577c438dbeSCameron Grant 
55866ef8af5SCameron Grant #ifdef SND_DYNSYSCTL
55966ef8af5SCameron Grant 	d->sysctl_tree_top = NULL;
56066ef8af5SCameron Grant 	sysctl_ctx_free(&d->sysctl_tree);
56166ef8af5SCameron Grant #endif
56266ef8af5SCameron Grant 
5637c438dbeSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
56433dbf14aSCameron Grant 	destroy_dev(pdev);
56533dbf14aSCameron Grant 	mixer_uninit(dev);
5667c438dbeSCameron Grant 
567285648f9SCameron Grant 	while (d->chancount > 0)
568285648f9SCameron Grant 		pcm_killchan(dev);
5697c438dbeSCameron Grant 
57033dbf14aSCameron Grant 	if (d->aplay) free(d->aplay, M_DEVBUF);
57133dbf14aSCameron Grant 	if (d->arec) free(d->arec, M_DEVBUF);
57233dbf14aSCameron Grant 
57366ef8af5SCameron Grant 	chn_kill(d->fakechan);
57466ef8af5SCameron Grant 	fkchan_kill(d->fakechan);
57582db23e2SCameron Grant 
57649c5e6e2SCameron Grant 	/* snd_mtxunlock(d->lock); */
57749c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
57833dbf14aSCameron Grant 	return 0;
57933dbf14aSCameron Grant }
5807c438dbeSCameron Grant 
581987e5972SCameron Grant /*
582987e5972SCameron Grant  * a small utility function which, given a device number, returns
58366ef8af5SCameron Grant  * a pointer to the associated struct snddev_info struct, and sets the unit
584987e5972SCameron Grant  * number.
585987e5972SCameron Grant  */
58666ef8af5SCameron Grant static struct snddev_info *
587987e5972SCameron Grant get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
588987e5972SCameron Grant {
58966ef8af5SCameron Grant 	struct snddev_info *sc;
590987e5972SCameron Grant     	int u, d, c;
591987e5972SCameron Grant 
592987e5972SCameron Grant     	u = PCMUNIT(i_dev);
593987e5972SCameron Grant     	d = PCMDEV(i_dev);
594987e5972SCameron Grant     	c = PCMCHAN(i_dev);
595987e5972SCameron Grant     	if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
596987e5972SCameron Grant     	if (unit) *unit = u;
597987e5972SCameron Grant     	if (dev) *dev = d;
598987e5972SCameron Grant     	if (chan) *chan = c;
599987e5972SCameron Grant     	if (u < 0) return NULL;
600987e5972SCameron Grant 
60133dbf14aSCameron Grant 	sc = devclass_get_softc(pcm_devclass, u);
60266ef8af5SCameron Grant 	if (sc == NULL) return NULL;
60333dbf14aSCameron Grant 
604987e5972SCameron Grant 	switch(d) {
605987e5972SCameron Grant     	case SND_DEV_CTL:	/* /dev/mixer handled by pcm */
606987e5972SCameron Grant     	case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
607987e5972SCameron Grant     	case SND_DEV_DSP:
608987e5972SCameron Grant     	case SND_DEV_DSP16:
609987e5972SCameron Grant     	case SND_DEV_AUDIO:
61033dbf14aSCameron Grant 		return sc;
611987e5972SCameron Grant 
612987e5972SCameron Grant     	case SND_DEV_SEQ: /* XXX when enabled... */
613987e5972SCameron Grant     	case SND_DEV_SEQ2:
614987e5972SCameron Grant     	case SND_DEV_MIDIN:
615987e5972SCameron Grant     	case SND_DEV_SNDPROC:	/* /dev/sndproc handled by pcm */
616987e5972SCameron Grant     	default:
617987e5972SCameron Grant 		printf("unsupported subdevice %d\n", d);
618987e5972SCameron Grant 		return NULL;
619987e5972SCameron Grant     	}
620987e5972SCameron Grant }
621987e5972SCameron Grant 
622987e5972SCameron Grant static int
623987e5972SCameron Grant sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
624987e5972SCameron Grant {
625987e5972SCameron Grant     	int dev, unit, chan;
62666ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
627987e5972SCameron Grant 
628987e5972SCameron Grant     	DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
629987e5972SCameron Grant 		unit, dev, flags, mode));
630987e5972SCameron Grant 
631987e5972SCameron Grant     	switch(dev) {
632987e5972SCameron Grant     	case SND_DEV_STATUS:
633285648f9SCameron Grant 		return do_status(0, NULL);
634987e5972SCameron Grant 
635987e5972SCameron Grant     	case SND_DEV_CTL:
6360f55ac6cSCameron Grant 		return d? mixer_busy(d->mixer, 1) : ENXIO;
637987e5972SCameron Grant 
638987e5972SCameron Grant     	case SND_DEV_AUDIO:
639987e5972SCameron Grant     	case SND_DEV_DSP:
640987e5972SCameron Grant     	case SND_DEV_DSP16:
6415b78a734SCameron Grant 	case SND_DEV_NORESET:
642285648f9SCameron Grant 		return d? dsp_open(d, chan, flags, dev, p->p_pid) : ENXIO;
643987e5972SCameron Grant 
644987e5972SCameron Grant     	default:
645987e5972SCameron Grant     		return ENXIO;
646987e5972SCameron Grant     	}
647987e5972SCameron Grant }
648987e5972SCameron Grant 
649987e5972SCameron Grant static int
650987e5972SCameron Grant sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
651987e5972SCameron Grant {
652987e5972SCameron Grant     	int dev, unit, chan;
65366ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
654987e5972SCameron Grant 
655987e5972SCameron Grant     	DEB(printf("close snd%d subdev %d\n", unit, dev));
656987e5972SCameron Grant 
657987e5972SCameron Grant     	switch(dev) { /* only those for which close makes sense */
658987e5972SCameron Grant     	case SND_DEV_STATUS:
659285648f9SCameron Grant 		return do_status(1, NULL);
660987e5972SCameron Grant 
661987e5972SCameron Grant     	case SND_DEV_CTL:
6620f55ac6cSCameron Grant 		return d? mixer_busy(d->mixer, 0) : ENXIO;
663987e5972SCameron Grant 
664987e5972SCameron Grant     	case SND_DEV_AUDIO:
665987e5972SCameron Grant     	case SND_DEV_DSP:
666987e5972SCameron Grant     	case SND_DEV_DSP16:
667987e5972SCameron Grant 		return d? dsp_close(d, chan, dev) : ENXIO;
668987e5972SCameron Grant 
669987e5972SCameron Grant     	default:
670987e5972SCameron Grant 		return ENXIO;
671987e5972SCameron Grant     	}
672987e5972SCameron Grant }
673987e5972SCameron Grant 
674987e5972SCameron Grant static int
675987e5972SCameron Grant sndread(dev_t i_dev, struct uio *buf, int flag)
676987e5972SCameron Grant {
677987e5972SCameron Grant     	int dev, unit, chan;
67866ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
679987e5972SCameron Grant     	DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
680987e5972SCameron Grant 
681987e5972SCameron Grant     	switch(dev) {
682987e5972SCameron Grant     	case SND_DEV_STATUS:
683285648f9SCameron Grant 		return do_status(2, buf);
684987e5972SCameron Grant 
685987e5972SCameron Grant     	case SND_DEV_AUDIO:
686987e5972SCameron Grant     	case SND_DEV_DSP:
687987e5972SCameron Grant     	case SND_DEV_DSP16:
688987e5972SCameron Grant         	return d? dsp_read(d, chan, buf, flag) : EBADF;
689987e5972SCameron Grant 
690987e5972SCameron Grant     	default:
691987e5972SCameron Grant     		return ENXIO;
692987e5972SCameron Grant     	}
693987e5972SCameron Grant }
694987e5972SCameron Grant 
695987e5972SCameron Grant static int
696987e5972SCameron Grant sndwrite(dev_t i_dev, struct uio *buf, int flag)
697987e5972SCameron Grant {
698987e5972SCameron Grant     	int dev, unit, chan;
69966ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
700987e5972SCameron Grant 
701987e5972SCameron Grant     	DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
702987e5972SCameron Grant 
703987e5972SCameron Grant     	switch(dev) {	/* only writeable devices */
704987e5972SCameron Grant     	case SND_DEV_DSP:
705987e5972SCameron Grant     	case SND_DEV_DSP16:
706987e5972SCameron Grant     	case SND_DEV_AUDIO:
707987e5972SCameron Grant 		return d? dsp_write(d, chan, buf, flag) : EBADF;
708987e5972SCameron Grant 
709987e5972SCameron Grant     	default:
710987e5972SCameron Grant 		return EPERM; /* for non-writeable devices ; */
711987e5972SCameron Grant     	}
712987e5972SCameron Grant }
713987e5972SCameron Grant 
714987e5972SCameron Grant static int
715987e5972SCameron Grant sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
716987e5972SCameron Grant {
717987e5972SCameron Grant     	int dev, chan;
71866ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
719987e5972SCameron Grant 
720987e5972SCameron Grant     	if (d == NULL) return ENXIO;
721987e5972SCameron Grant 
722987e5972SCameron Grant     	switch(dev) {
723987e5972SCameron Grant     	case SND_DEV_CTL:
724987e5972SCameron Grant 		return mixer_ioctl(d, cmd, arg);
725987e5972SCameron Grant 
726987e5972SCameron Grant     	case SND_DEV_AUDIO:
727987e5972SCameron Grant     	case SND_DEV_DSP:
728987e5972SCameron Grant     	case SND_DEV_DSP16:
7291ad869dbSCameron Grant 		if (IOCGROUP(cmd) == 'M')
7301ad869dbSCameron Grant 			return mixer_ioctl(d, cmd, arg);
7311ad869dbSCameron Grant 		else
732987e5972SCameron Grant 			return dsp_ioctl(d, chan, cmd, arg);
733987e5972SCameron Grant 
734987e5972SCameron Grant     	default:
735987e5972SCameron Grant     		return ENXIO;
736987e5972SCameron Grant     	}
737987e5972SCameron Grant }
738987e5972SCameron Grant 
739987e5972SCameron Grant static int
740987e5972SCameron Grant sndpoll(dev_t i_dev, int events, struct proc *p)
741987e5972SCameron Grant {
742987e5972SCameron Grant     	int dev, chan;
74366ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
744987e5972SCameron Grant 
7450e25481fSCameron Grant 	DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
746987e5972SCameron Grant 
747987e5972SCameron Grant     	if (d == NULL) return ENXIO;
748987e5972SCameron Grant 
749987e5972SCameron Grant     	switch(dev) {
750987e5972SCameron Grant     	case SND_DEV_AUDIO:
751987e5972SCameron Grant     	case SND_DEV_DSP:
752987e5972SCameron Grant     	case SND_DEV_DSP16:
753987e5972SCameron Grant 		return dsp_poll(d, chan, events, p);
754987e5972SCameron Grant 
755987e5972SCameron Grant     	default:
756987e5972SCameron Grant     		return (events &
757987e5972SCameron Grant        		       (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
758987e5972SCameron Grant     	}
759987e5972SCameron Grant }
760987e5972SCameron Grant 
761987e5972SCameron Grant /*
762987e5972SCameron Grant  * The mmap interface allows access to the play and read buffer,
763987e5972SCameron Grant  * plus the device descriptor.
764987e5972SCameron Grant  * The various blocks are accessible at the following offsets:
765987e5972SCameron Grant  *
766987e5972SCameron Grant  * 0x00000000 ( 0   ) : write buffer ;
767987e5972SCameron Grant  * 0x01000000 (16 MB) : read buffer ;
768987e5972SCameron Grant  * 0x02000000 (32 MB) : device descriptor (dangerous!)
769987e5972SCameron Grant  *
770987e5972SCameron Grant  * WARNING: the mmap routines assume memory areas are aligned. This
771987e5972SCameron Grant  * is true (probably) for the dma buffers, but likely false for the
772987e5972SCameron Grant  * device descriptor. As a consequence, we do not know where it is
773987e5972SCameron Grant  * located in the requested area.
774987e5972SCameron Grant  */
775987e5972SCameron Grant static int
776987e5972SCameron Grant sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
777987e5972SCameron Grant {
778987e5972SCameron Grant     	int unit, dev, chan;
77966ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
780987e5972SCameron Grant 
781987e5972SCameron Grant     	DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
782987e5972SCameron Grant 		   d, dev, offset, nprot));
783987e5972SCameron Grant 
784987e5972SCameron Grant     	if (d == NULL || nprot & PROT_EXEC)	return -1; /* forbidden */
785987e5972SCameron Grant 
786987e5972SCameron Grant     	switch(dev) {
787987e5972SCameron Grant     	case SND_DEV_AUDIO:
788987e5972SCameron Grant     	case SND_DEV_DSP:
789987e5972SCameron Grant     	case SND_DEV_DSP16:
790987e5972SCameron Grant 		return dsp_mmap(d, chan, offset, nprot);
791987e5972SCameron Grant 
792987e5972SCameron Grant     	default:
793987e5972SCameron Grant     		return -1;
794987e5972SCameron Grant     	}
795987e5972SCameron Grant }
796987e5972SCameron Grant 
797987e5972SCameron Grant static int
798285648f9SCameron Grant status_init(struct sbuf *s)
799987e5972SCameron Grant {
800285648f9SCameron Grant     	int i, pc, rc, vc;
801987e5972SCameron Grant     	device_t dev;
80266ef8af5SCameron Grant     	struct snddev_info *d;
803285648f9SCameron Grant     	struct snddev_channel *sce;
804285648f9SCameron Grant 	struct pcm_channel *c;
80549c5e6e2SCameron Grant #ifdef SNDSTAT_VERBOSE
806285648f9SCameron Grant 	struct pcm_feeder *f;
80749c5e6e2SCameron Grant #endif
808987e5972SCameron Grant 
809285648f9SCameron Grant 	sbuf_printf(s, "FreeBSD Audio Driver (newpcm) %s %s\nInstalled devices:\n",
810285648f9SCameron Grant 		 	__DATE__, __TIME__);
811987e5972SCameron Grant 
812987e5972SCameron Grant     	for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
81333dbf14aSCameron Grant 		d = devclass_get_softc(pcm_devclass, i);
814285648f9SCameron Grant 		if (!d)
815285648f9SCameron Grant 			continue;
81649c5e6e2SCameron Grant 		snd_mtxlock(d->lock);
817987e5972SCameron Grant 		dev = devclass_get_device(pcm_devclass, i);
818285648f9SCameron Grant 		sbuf_printf(s, "pcm%d: <%s> %s", i, device_get_desc(dev), d->status);
819285648f9SCameron Grant 		if (d->chancount > 0) {
820285648f9SCameron Grant 			pc = rc = vc = 0;
821285648f9SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
822285648f9SCameron Grant 				c = sce->channel;
823285648f9SCameron Grant 				if (c->direction == PCMDIR_PLAY) {
824285648f9SCameron Grant 					if (c->flags & CHN_F_VIRTUAL)
825285648f9SCameron Grant 						vc++;
826bf8ca271SCameron Grant 					else
827285648f9SCameron Grant 						pc++;
828285648f9SCameron Grant 				} else
829285648f9SCameron Grant 					rc++;
830bf8ca271SCameron Grant 			}
831285648f9SCameron Grant 			sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)\n", pc, rc, vc,
832285648f9SCameron Grant 					(d->flags & SD_F_SIMPLEX)? "" : " duplex",
833285648f9SCameron Grant #ifdef USING_DEVFS
834285648f9SCameron Grant 					(i == snd_unit)? " default" : ""
835285648f9SCameron Grant #else
836285648f9SCameron Grant 					""
837285648f9SCameron Grant #endif
838285648f9SCameron Grant 					);
839285648f9SCameron Grant #ifdef SNDSTAT_VERBOSE
840285648f9SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
841285648f9SCameron Grant 				c = sce->channel;
842285648f9SCameron Grant 				sbuf_printf(s, "\t%s[%s]: speed %d, format %08x, flags %08x",
843285648f9SCameron Grant 					c->parentchannel? c->parentchannel->name : "",
844285648f9SCameron Grant 					c->name, c->speed, c->format, c->flags);
845285648f9SCameron Grant 				if (c->pid != -1)
846285648f9SCameron Grant 					sbuf_printf(s, ", pid %d", c->pid);
847285648f9SCameron Grant 				sbuf_printf(s, "\n\t");
848285648f9SCameron Grant 				f = c->feeder;
849285648f9SCameron Grant 				while (f) {
850285648f9SCameron Grant 					sbuf_printf(s, "%s", f->class->name);
851285648f9SCameron Grant 					if (f->desc->type == FEEDER_FMT)
852285648f9SCameron Grant 						sbuf_printf(s, "(%08x <- %08x)", f->desc->out, f->desc->in);
853285648f9SCameron Grant 					if (f->desc->type == FEEDER_RATE)
854285648f9SCameron Grant 						sbuf_printf(s, "(%d <- %d)", FEEDER_GET(f, FEEDRATE_DST), FEEDER_GET(f, FEEDRATE_SRC));
855285648f9SCameron Grant 					if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
856285648f9SCameron Grant 						sbuf_printf(s, "(%08x)", f->desc->out);
857285648f9SCameron Grant 					if (f->source)
858285648f9SCameron Grant 						sbuf_printf(s, " <- ");
859285648f9SCameron Grant 					f = f->source;
860987e5972SCameron Grant 				}
861285648f9SCameron Grant 				sbuf_printf(s, "\n");
862285648f9SCameron Grant 			}
863285648f9SCameron Grant #endif
864285648f9SCameron Grant 		} else
865285648f9SCameron Grant 			sbuf_printf(s, " (mixer only)\n");
86649c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
867285648f9SCameron Grant     	}
868285648f9SCameron Grant 	sbuf_finish(s);
869285648f9SCameron Grant     	return sbuf_len(s);
870987e5972SCameron Grant }
871987e5972SCameron Grant 
872987e5972SCameron Grant static int
873285648f9SCameron Grant do_status(int action, struct uio *buf)
874987e5972SCameron Grant {
875285648f9SCameron Grant 	static struct sbuf s;
876285648f9SCameron Grant     	static int bufptr = 0;
877285648f9SCameron Grant 	static int status_open = 0;
878285648f9SCameron Grant     	int l, err;
879987e5972SCameron Grant 
880285648f9SCameron Grant 	switch(action) {
881285648f9SCameron Grant 	case 0: /* open */
882285648f9SCameron Grant 		if (status_open)
883285648f9SCameron Grant 			return EBUSY;
884285648f9SCameron Grant 		if (sbuf_new(&s, NULL, 4096, 0))
885285648f9SCameron Grant 			return ENXIO;
886987e5972SCameron Grant 		bufptr = 0;
887285648f9SCameron Grant 		err = (status_init(&s) > 0)? 0 : ENOMEM;
888285648f9SCameron Grant 		if (!err)
889285648f9SCameron Grant 			status_open = 1;
890285648f9SCameron Grant 		return err;
891285648f9SCameron Grant 
892285648f9SCameron Grant 	case 1: /* close */
893285648f9SCameron Grant 		if (!status_open)
894285648f9SCameron Grant 			return EBADF;
895285648f9SCameron Grant 		sbuf_delete(&s);
896285648f9SCameron Grant 		status_open = 0;
897285648f9SCameron Grant 		return 0;
898285648f9SCameron Grant 
899285648f9SCameron Grant 	case 2:
900285648f9SCameron Grant 		if (!status_open)
901285648f9SCameron Grant 			return EBADF;
902285648f9SCameron Grant 	    	l = min(buf->uio_resid, sbuf_len(&s) - bufptr);
903285648f9SCameron Grant 		err = (l > 0)? uiomove(sbuf_data(&s) + bufptr, l, buf) : 0;
904285648f9SCameron Grant     		bufptr += l;
905285648f9SCameron Grant     		return err;
906285648f9SCameron Grant 
907285648f9SCameron Grant 	case 3:
908285648f9SCameron Grant 		return status_open;
909987e5972SCameron Grant 	}
910987e5972SCameron Grant 
911285648f9SCameron Grant 	return EBADF;
912987e5972SCameron Grant }
91333dbf14aSCameron Grant 
91433dbf14aSCameron Grant static int
91533dbf14aSCameron Grant sndpcm_modevent(module_t mod, int type, void *data)
91633dbf14aSCameron Grant {
91733dbf14aSCameron Grant 
91833dbf14aSCameron Grant 	switch (type) {
91933dbf14aSCameron Grant 	case MOD_LOAD:
92033dbf14aSCameron Grant 		break;
92133dbf14aSCameron Grant 	case MOD_UNLOAD:
922285648f9SCameron Grant 		if (do_status(3, NULL))
923c9b53085SCameron Grant 			return EBUSY;
92433dbf14aSCameron Grant 		if (status_dev)
92533dbf14aSCameron Grant 			destroy_dev(status_dev);
92633dbf14aSCameron Grant 		status_dev = 0;
92733dbf14aSCameron Grant 		break;
92833dbf14aSCameron Grant 	default:
92933dbf14aSCameron Grant 		break;
93033dbf14aSCameron Grant 	}
93133dbf14aSCameron Grant 	return 0;
93233dbf14aSCameron Grant }
93333dbf14aSCameron Grant 
93433dbf14aSCameron Grant static moduledata_t sndpcm_mod = {
93533dbf14aSCameron Grant 	"snd_pcm",
93633dbf14aSCameron Grant 	sndpcm_modevent,
93733dbf14aSCameron Grant 	NULL
93833dbf14aSCameron Grant };
93933dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
94033dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER);
941