xref: /freebsd/sys/dev/sound/pcm/sound.c (revision 37209180a5d2421d660876d5e2d6a05b905ad9b2)
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>
317c438dbeSCameron Grant #include <sys/sysctl.h>
32987e5972SCameron Grant 
3333dbf14aSCameron Grant static dev_t 	status_dev = 0;
34987e5972SCameron Grant static int 	status_isopen = 0;
35987e5972SCameron Grant static int 	status_init(char *buf, int size);
36987e5972SCameron Grant static int 	status_read(struct uio *buf);
37987e5972SCameron Grant 
38987e5972SCameron Grant static d_open_t sndopen;
39987e5972SCameron Grant static d_close_t sndclose;
40987e5972SCameron Grant static d_ioctl_t sndioctl;
41987e5972SCameron Grant static d_read_t sndread;
42987e5972SCameron Grant static d_write_t sndwrite;
43987e5972SCameron Grant static d_mmap_t sndmmap;
44987e5972SCameron Grant static d_poll_t sndpoll;
45987e5972SCameron Grant 
46987e5972SCameron Grant #define CDEV_MAJOR 30
47987e5972SCameron Grant static struct cdevsw snd_cdevsw = {
48987e5972SCameron Grant 	/* open */	sndopen,
49987e5972SCameron Grant 	/* close */	sndclose,
50987e5972SCameron Grant 	/* read */	sndread,
51987e5972SCameron Grant 	/* write */	sndwrite,
52987e5972SCameron Grant 	/* ioctl */	sndioctl,
53987e5972SCameron Grant 	/* poll */	sndpoll,
54987e5972SCameron Grant 	/* mmap */	sndmmap,
55987e5972SCameron Grant 	/* strategy */	nostrategy,
56987e5972SCameron Grant 	/* name */	"snd",
57987e5972SCameron Grant 	/* maj */	CDEV_MAJOR,
58987e5972SCameron Grant 	/* dump */	nodump,
59987e5972SCameron Grant 	/* psize */	nopsize,
60987e5972SCameron Grant 	/* flags */	0,
61987e5972SCameron Grant 	/* bmaj */	-1
62987e5972SCameron Grant };
63987e5972SCameron Grant 
64dd186369SCameron Grant /*
65dd186369SCameron Grant PROPOSAL:
66987e5972SCameron Grant each unit needs:
67987e5972SCameron Grant status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
68987e5972SCameron Grant dspW and audio are deprecated.
69987e5972SCameron Grant dsp needs min 64 channels, will give it 256
70987e5972SCameron Grant 
71dd186369SCameron Grant minor = (unit << 20) + (dev << 16) + channel
72dd186369SCameron Grant currently minor = (channel << 16) + (unit << 4) + dev
73987e5972SCameron Grant 
74987e5972SCameron Grant nomenclature:
75987e5972SCameron Grant 	/dev/pcmX/dsp.(0..255)
76987e5972SCameron Grant 	/dev/pcmX/dspW
77987e5972SCameron Grant 	/dev/pcmX/audio
78987e5972SCameron Grant 	/dev/pcmX/status
79987e5972SCameron Grant 	/dev/pcmX/mixer
80987e5972SCameron Grant 	[etc.]
81987e5972SCameron Grant */
82987e5972SCameron Grant 
83987e5972SCameron Grant #define PCMMINOR(x) (minor(x))
84a618cffeSCameron Grant #define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16)
85987e5972SCameron Grant #define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
86987e5972SCameron Grant #define PCMDEV(x)   (PCMMINOR(x) & 0x0000000f)
87dd186369SCameron Grant #define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
88987e5972SCameron Grant 
89987e5972SCameron Grant static devclass_t pcm_devclass;
9082db23e2SCameron Grant 
9182db23e2SCameron Grant #ifdef USING_DEVFS
927c438dbeSCameron Grant int snd_unit;
93fa465e99SJohn Baldwin TUNABLE_INT_DECL("hw.snd.unit", 0, snd_unit);
9482db23e2SCameron Grant #endif
95987e5972SCameron Grant 
9682db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
9782db23e2SCameron Grant 
9837209180SCameron Grant #ifndef USING_MUTEX
9937209180SCameron Grant #define	SPLSTACK_MAX	32
10037209180SCameron Grant struct splstack {
10137209180SCameron Grant 	int depth;
10237209180SCameron Grant 	u_int32_t stack[SPLSTACK_MAX];
10337209180SCameron Grant };
10437209180SCameron Grant #endif
10537209180SCameron Grant 
10637209180SCameron Grant void *
10737209180SCameron Grant snd_mtxcreate(const char *desc)
10837209180SCameron Grant {
10937209180SCameron Grant #ifdef USING_MUTEX
11037209180SCameron Grant 	struct mtx *m;
11137209180SCameron Grant 
11237209180SCameron Grant 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
11337209180SCameron Grant 	if (m == NULL)
11437209180SCameron Grant 		return NULL;
11537209180SCameron Grant 	mtx_init(m, desc, MTX_RECURSE);
11637209180SCameron Grant 	return m;
11737209180SCameron Grant #else
11837209180SCameron Grant 	struct splstack *s;
11937209180SCameron Grant 
12037209180SCameron Grant 	s = malloc(sizeof(*s), M_DEVBUF, M_WAITOK | M_ZERO);
12137209180SCameron Grant 	s->depth = 0;
12237209180SCameron Grant 	return s;
12337209180SCameron Grant #endif
12437209180SCameron Grant }
12537209180SCameron Grant 
12637209180SCameron Grant void
12737209180SCameron Grant snd_mtxfree(void *m)
12837209180SCameron Grant {
12937209180SCameron Grant #ifdef USING_MUTEX
13037209180SCameron Grant 	struct mtx *mtx = m;
13137209180SCameron Grant 
13237209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
13337209180SCameron Grant 	mtx_destroy(mtx);
13437209180SCameron Grant 	free(mtx, M_DEVBUF);
13537209180SCameron Grant #else
13637209180SCameron Grant 	struct splstack *s = m;
13737209180SCameron Grant 
13837209180SCameron Grant 	free(s, M_DEVBUF);
13937209180SCameron Grant #endif
14037209180SCameron Grant }
14137209180SCameron Grant 
14237209180SCameron Grant void
14337209180SCameron Grant snd_mtxassert(void *m)
14437209180SCameron Grant {
14537209180SCameron Grant #ifdef USING_MUTEX
14637209180SCameron Grant 	struct mtx *mtx = m;
14737209180SCameron Grant 
14837209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
14937209180SCameron Grant #else
15037209180SCameron Grant 	KASSERT(((struct splstack *)s)->depth > 0, ("splstack depth <= 0"));
15137209180SCameron Grant #endif
15237209180SCameron Grant }
15337209180SCameron Grant 
15437209180SCameron Grant void
15537209180SCameron Grant snd_mtxlock(void *m)
15637209180SCameron Grant {
15737209180SCameron Grant #ifdef USING_MUTEX
15837209180SCameron Grant 	struct mtx *mtx = m;
15937209180SCameron Grant 
16037209180SCameron Grant 	mtx_lock(mtx);
16137209180SCameron Grant #else
16237209180SCameron Grant 	struct splstack *s = m;
16337209180SCameron Grant 
16437209180SCameron Grant 	KASSERT((s->depth >= 0) && (s->depth < SPLSTACK_MAX), ("bad depth %d", s->depth));
16537209180SCameron Grant 	s->stack[s->depth++] = spltty();
16637209180SCameron Grant #endif
16737209180SCameron Grant }
16837209180SCameron Grant 
16937209180SCameron Grant void
17037209180SCameron Grant snd_mtxunlock(void *m)
17137209180SCameron Grant {
17237209180SCameron Grant #ifdef USING_MUTEX
17337209180SCameron Grant 	struct mtx *mtx = m;
17437209180SCameron Grant 
17537209180SCameron Grant 	mtx_unlock(mtx);
17637209180SCameron Grant #else
17737209180SCameron Grant 	struct splstack *s = m;
17837209180SCameron Grant 
17937209180SCameron Grant 	KASSERT((s->depth >= 1) && (s->depth < SPLSTACK_MAX), ("bad depth %d", s->depth));
18037209180SCameron Grant 	splx(s->stack[s->depth--]);
18137209180SCameron Grant #endif
18237209180SCameron Grant }
18337209180SCameron Grant 
18437209180SCameron Grant int
18537209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
18637209180SCameron Grant {
18737209180SCameron Grant #ifdef USING_MUTEX
18837209180SCameron Grant 	flags &= INTR_MPSAFE;
18937209180SCameron Grant 	flags |= INTR_TYPE_TTY;
19037209180SCameron Grant #else
19137209180SCameron Grant 	flags = INTR_TYPE_TTY;
19237209180SCameron Grant #endif
19337209180SCameron Grant 	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
19437209180SCameron Grant }
19537209180SCameron Grant 
19682db23e2SCameron Grant #ifdef USING_DEVFS
19733dbf14aSCameron Grant static void
19833dbf14aSCameron Grant pcm_makelinks(void *dummy)
199987e5972SCameron Grant {
20033dbf14aSCameron Grant 	int unit;
20133dbf14aSCameron Grant 	dev_t pdev;
20233dbf14aSCameron Grant 	static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0;
20333dbf14aSCameron Grant 
204f776b5abSCameron Grant 	if (pcm_devclass == NULL || devfs_present == 0)
20533dbf14aSCameron Grant 		return;
20633dbf14aSCameron Grant 	if (dsp) {
20733dbf14aSCameron Grant 		destroy_dev(dsp);
20833dbf14aSCameron Grant 		dsp = 0;
209987e5972SCameron Grant 	}
21033dbf14aSCameron Grant 	if (dspW) {
21133dbf14aSCameron Grant 		destroy_dev(dspW);
21233dbf14aSCameron Grant 		dspW = 0;
21333dbf14aSCameron Grant 	}
21433dbf14aSCameron Grant 	if (audio) {
21533dbf14aSCameron Grant 		destroy_dev(audio);
21633dbf14aSCameron Grant 		audio = 0;
21733dbf14aSCameron Grant 	}
21833dbf14aSCameron Grant 	if (mixer) {
21933dbf14aSCameron Grant 		destroy_dev(mixer);
22033dbf14aSCameron Grant 		mixer = 0;
22133dbf14aSCameron Grant 	}
22233dbf14aSCameron Grant 
22333dbf14aSCameron Grant 	unit = snd_unit;
22433dbf14aSCameron Grant 	if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass))
22533dbf14aSCameron Grant 		return;
22633dbf14aSCameron Grant 	if (devclass_get_softc(pcm_devclass, unit) == NULL)
22733dbf14aSCameron Grant 		return;
22833dbf14aSCameron Grant 
22933dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0));
23033dbf14aSCameron Grant 	dsp = make_dev_alias(pdev, "dsp");
23133dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0));
23233dbf14aSCameron Grant 	dspW = make_dev_alias(pdev, "dspW");
23333dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0));
23433dbf14aSCameron Grant 	audio = make_dev_alias(pdev, "audio");
23533dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
23633dbf14aSCameron Grant 	mixer = make_dev_alias(pdev, "mixer");
23733dbf14aSCameron Grant }
23833dbf14aSCameron Grant 
23933dbf14aSCameron Grant static int
24033dbf14aSCameron Grant sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS)
24133dbf14aSCameron Grant {
24233dbf14aSCameron Grant 	int error, unit;
24333dbf14aSCameron Grant 
24433dbf14aSCameron Grant 	unit = snd_unit;
24533dbf14aSCameron Grant 	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
24633dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
24733dbf14aSCameron Grant 		snd_unit = unit;
24833dbf14aSCameron Grant 		pcm_makelinks(NULL);
24933dbf14aSCameron Grant 	}
25033dbf14aSCameron Grant 	return (error);
25133dbf14aSCameron Grant }
252b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
25333dbf14aSCameron Grant             0, sizeof(int), sysctl_hw_sndunit, "I", "");
25482db23e2SCameron Grant #endif
255987e5972SCameron Grant 
256987e5972SCameron Grant int
2570f55ac6cSCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
258987e5972SCameron Grant {
25933dbf14aSCameron Grant     	int unit = device_get_unit(dev), idx;
260987e5972SCameron Grant     	snddev_info *d = device_get_softc(dev);
26133dbf14aSCameron Grant 	pcm_channel *chns, *ch;
26233dbf14aSCameron Grant 	char *dirs;
2630f55ac6cSCameron Grant 	int err;
264987e5972SCameron Grant 
26533dbf14aSCameron Grant 	dirs = ((dir == PCMDIR_PLAY)? "play" : "record");
26633dbf14aSCameron Grant 	chns = ((dir == PCMDIR_PLAY)? d->play : d->rec);
26733dbf14aSCameron Grant 	idx = ((dir == PCMDIR_PLAY)? d->playcount++ : d->reccount++);
26833dbf14aSCameron Grant 
26933dbf14aSCameron Grant 	if (chns == NULL) {
27033dbf14aSCameron Grant 		device_printf(dev, "bad channel add (%s:%d)\n", dirs, idx);
2719c326820SCameron Grant 		return 1;
2729c326820SCameron Grant 	}
27333dbf14aSCameron Grant 	ch = &chns[idx];
2740f55ac6cSCameron Grant 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
275e4d5b250SCameron Grant 	ch->parent = d;
2760f55ac6cSCameron Grant 	err = chn_init(ch, devinfo, dir);
2770f55ac6cSCameron Grant 	if (err) {
2780f55ac6cSCameron Grant 		device_printf(dev, "chn_init() for (%s:%d) failed: err = %d\n", dirs, idx, err);
279bbb5bf3dSCameron Grant 		return 1;
280bbb5bf3dSCameron Grant 	}
2817207eca6SCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount),
2827207eca6SCameron Grant 		 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount);
2837207eca6SCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount),
2847207eca6SCameron Grant 		 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount);
28533dbf14aSCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount),
28633dbf14aSCameron Grant 		 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount);
2877207eca6SCameron Grant 	/* XXX SND_DEV_NORESET? */
288987e5972SCameron Grant 	d->chancount++;
28982db23e2SCameron Grant #ifdef USING_DEVFS
2906d97297aSJohn Baldwin     	if (d->chancount == 1)
29133dbf14aSCameron Grant 		pcm_makelinks(NULL);
29282db23e2SCameron Grant #endif
29333dbf14aSCameron Grant 	return 0;
29433dbf14aSCameron Grant }
29533dbf14aSCameron Grant 
29633dbf14aSCameron Grant static int
29733dbf14aSCameron Grant pcm_killchan(device_t dev, int dir)
29833dbf14aSCameron Grant {
29933dbf14aSCameron Grant     	int unit = device_get_unit(dev), idx;
30033dbf14aSCameron Grant     	snddev_info *d = device_get_softc(dev);
30133dbf14aSCameron Grant 	pcm_channel *chns, *ch;
30233dbf14aSCameron Grant 	char *dirs;
30333dbf14aSCameron Grant 	dev_t pdev;
30433dbf14aSCameron Grant 
30533dbf14aSCameron Grant 	dirs = ((dir == PCMDIR_PLAY)? "play" : "record");
30633dbf14aSCameron Grant 	chns = ((dir == PCMDIR_PLAY)? d->play : d->rec);
30733dbf14aSCameron Grant 	idx = ((dir == PCMDIR_PLAY)? --d->playcount : --d->reccount);
30833dbf14aSCameron Grant 
30933dbf14aSCameron Grant 	if (chns == NULL || idx < 0) {
31033dbf14aSCameron Grant 		device_printf(dev, "bad channel kill (%s:%d)\n", dirs, idx);
31133dbf14aSCameron Grant 		return 1;
31233dbf14aSCameron Grant 	}
31333dbf14aSCameron Grant 	ch = &chns[idx];
31433dbf14aSCameron Grant 	if (chn_kill(ch)) {
31533dbf14aSCameron Grant 		device_printf(dev, "chn_kill() for (%s:%d) failed\n", dirs, idx);
31633dbf14aSCameron Grant 		return 1;
31733dbf14aSCameron Grant 	}
3180f55ac6cSCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
3190f55ac6cSCameron Grant 	ch->methods = NULL;
32033dbf14aSCameron Grant 	d->chancount--;
32133dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount));
32233dbf14aSCameron Grant 	destroy_dev(pdev);
32333dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount));
32433dbf14aSCameron Grant 	destroy_dev(pdev);
32533dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount));
32633dbf14aSCameron Grant 	destroy_dev(pdev);
327987e5972SCameron Grant 	return 0;
328987e5972SCameron Grant }
329987e5972SCameron Grant 
330987e5972SCameron Grant int
331987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
332987e5972SCameron Grant {
333987e5972SCameron Grant     	snddev_info *d = device_get_softc(dev);
334987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
335987e5972SCameron Grant 	return 0;
336987e5972SCameron Grant }
337987e5972SCameron Grant 
338987e5972SCameron Grant u_int32_t
339987e5972SCameron Grant pcm_getflags(device_t dev)
340987e5972SCameron Grant {
341987e5972SCameron Grant     	snddev_info *d = device_get_softc(dev);
342987e5972SCameron Grant 	return d->flags;
343987e5972SCameron Grant }
344987e5972SCameron Grant 
345987e5972SCameron Grant void
346987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
347987e5972SCameron Grant {
348987e5972SCameron Grant     	snddev_info *d = device_get_softc(dev);
349987e5972SCameron Grant 	d->flags = val;
350987e5972SCameron Grant }
351987e5972SCameron Grant 
35239004e69SCameron Grant void *
35339004e69SCameron Grant pcm_getdevinfo(device_t dev)
35439004e69SCameron Grant {
35539004e69SCameron Grant     	snddev_info *d = device_get_softc(dev);
35639004e69SCameron Grant 	return d->devinfo;
35739004e69SCameron Grant }
35839004e69SCameron Grant 
359987e5972SCameron Grant /* This is the generic init routine */
360987e5972SCameron Grant int
361987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
362987e5972SCameron Grant {
363987e5972SCameron Grant     	int sz, unit = device_get_unit(dev);
364987e5972SCameron Grant     	snddev_info *d = device_get_softc(dev);
365987e5972SCameron Grant 
366987e5972SCameron Grant     	if (!pcm_devclass) {
367987e5972SCameron Grant     		pcm_devclass = device_get_devclass(dev);
36833dbf14aSCameron Grant 		status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0),
369083279e4SSeigo Tanimura 			 UID_ROOT, GID_WHEEL, 0444, "sndstat");
370083279e4SSeigo Tanimura 	}
3717207eca6SCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
37211346231SSeigo Tanimura 		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
373e4d5b250SCameron Grant 	d->dev = dev;
374987e5972SCameron Grant 	d->devinfo = devinfo;
375987e5972SCameron Grant 	d->chancount = d->playcount = d->reccount = 0;
37633dbf14aSCameron Grant 	d->maxchans = numplay + numrec;
377987e5972SCameron Grant     	sz = (numplay + numrec) * sizeof(pcm_channel *);
378833f7023SCameron Grant 
379833f7023SCameron Grant 	if (sz > 0) {
380987e5972SCameron Grant 		d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
381987e5972SCameron Grant     		if (!d->aplay) goto no;
382833f7023SCameron Grant     		bzero(d->aplay, sz);
383833f7023SCameron Grant 
384987e5972SCameron Grant     		d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
385987e5972SCameron Grant     		if (!d->arec) goto no;
386987e5972SCameron Grant     		bzero(d->arec, sz);
387bd18f334SCameron Grant 
388bd18f334SCameron Grant     		sz = (numplay + numrec) * sizeof(int);
389bd18f334SCameron Grant 		d->ref = (int *)malloc(sz, M_DEVBUF, M_NOWAIT);
390bd18f334SCameron Grant     		if (!d->ref) goto no;
391bd18f334SCameron Grant     		bzero(d->ref, sz);
392833f7023SCameron Grant 	}
393987e5972SCameron Grant 
394833f7023SCameron Grant 	if (numplay > 0) {
395987e5972SCameron Grant     		d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel),
396987e5972SCameron Grant 						M_DEVBUF, M_NOWAIT);
397987e5972SCameron Grant     		if (!d->play) goto no;
398833f7023SCameron Grant     		bzero(d->play, numplay * sizeof(pcm_channel));
3999c326820SCameron Grant 	} else
4009c326820SCameron Grant 		d->play = NULL;
401833f7023SCameron Grant 
402833f7023SCameron Grant 	if (numrec > 0) {
403987e5972SCameron Grant 	  	d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel),
404987e5972SCameron Grant 				       	M_DEVBUF, M_NOWAIT);
405987e5972SCameron Grant     		if (!d->rec) goto no;
406987e5972SCameron Grant     		bzero(d->rec, numrec * sizeof(pcm_channel));
4079c326820SCameron Grant 	} else
4089c326820SCameron Grant 		d->rec = NULL;
409987e5972SCameron Grant 
41082db23e2SCameron Grant #ifdef SND_DYNSYSCTL
411cc486d80SJohn Baldwin 	sysctl_ctx_init(&d->sysctl_tree);
412cc486d80SJohn Baldwin 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
413a3e893e0SJohn Baldwin 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
414cc486d80SJohn Baldwin 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
415a3e893e0SJohn Baldwin 	if (d->sysctl_tree_top == NULL) {
416cc486d80SJohn Baldwin 		sysctl_ctx_free(&d->sysctl_tree);
417cc486d80SJohn Baldwin 		goto no;
418cc486d80SJohn Baldwin 	}
41982db23e2SCameron Grant #endif
420cc486d80SJohn Baldwin 
42117dbf677SCameron Grant 	if (numplay == 0 || numrec == 0)
42217dbf677SCameron Grant 		d->flags |= SD_F_SIMPLEX;
42317dbf677SCameron Grant 
424987e5972SCameron Grant 	fkchan_setup(&d->fakechan);
425987e5972SCameron Grant 	chn_init(&d->fakechan, NULL, 0);
426987e5972SCameron Grant 	d->magic = MAGIC(unit); /* debugging... */
427987e5972SCameron Grant 
428987e5972SCameron Grant     	return 0;
429987e5972SCameron Grant no:
430987e5972SCameron Grant 	if (d->aplay) free(d->aplay, M_DEVBUF);
431987e5972SCameron Grant 	if (d->play) free(d->play, M_DEVBUF);
432987e5972SCameron Grant 	if (d->arec) free(d->arec, M_DEVBUF);
433987e5972SCameron Grant 	if (d->rec) free(d->rec, M_DEVBUF);
43433dbf14aSCameron Grant 	if (d->ref) free(d->ref, M_DEVBUF);
435987e5972SCameron Grant 	return ENXIO;
436987e5972SCameron Grant }
437987e5972SCameron Grant 
43833dbf14aSCameron Grant int
43933dbf14aSCameron Grant pcm_unregister(device_t dev)
4407c438dbeSCameron Grant {
44133dbf14aSCameron Grant     	int r, i, unit = device_get_unit(dev);
44233dbf14aSCameron Grant     	snddev_info *d = device_get_softc(dev);
4437c438dbeSCameron Grant 	dev_t pdev;
4447c438dbeSCameron Grant 
44582db23e2SCameron Grant #ifdef SND_DYNSYSCTL
446cc486d80SJohn Baldwin 	sysctl_remove_oid(d->sysctl_tree_top, 1, 1);
447cc486d80SJohn Baldwin 	d->sysctl_tree_top = NULL;
448cc486d80SJohn Baldwin 	sysctl_ctx_free(&d->sysctl_tree);
44982db23e2SCameron Grant #endif
450cc486d80SJohn Baldwin 
45133dbf14aSCameron Grant 	r = 0;
45233dbf14aSCameron Grant 	for (i = 0; i < d->chancount; i++)
45333dbf14aSCameron Grant 		if (d->ref[i]) r = EBUSY;
454c9b53085SCameron Grant 	if (r) {
455c9b53085SCameron Grant 		device_printf(dev, "unregister: channel busy");
456c9b53085SCameron Grant 		return r;
457c9b53085SCameron Grant 	}
4580f55ac6cSCameron Grant 	if (mixer_isbusy(d->mixer)) {
459c9b53085SCameron Grant 		device_printf(dev, "unregister: mixer busy");
460c9b53085SCameron Grant 		return EBUSY;
461c9b53085SCameron Grant 	}
4627c438dbeSCameron Grant 
4637c438dbeSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
46433dbf14aSCameron Grant 	destroy_dev(pdev);
46533dbf14aSCameron Grant 	mixer_uninit(dev);
4667c438dbeSCameron Grant 
46733dbf14aSCameron Grant 	while (d->playcount > 0)
46833dbf14aSCameron Grant 		pcm_killchan(dev, PCMDIR_PLAY);
46933dbf14aSCameron Grant 	while (d->reccount > 0)
47033dbf14aSCameron Grant 		pcm_killchan(dev, PCMDIR_REC);
47133dbf14aSCameron Grant 	d->magic = 0;
4727c438dbeSCameron Grant 
47333dbf14aSCameron Grant 	if (d->aplay) free(d->aplay, M_DEVBUF);
47433dbf14aSCameron Grant 	if (d->play) free(d->play, M_DEVBUF);
47533dbf14aSCameron Grant 	if (d->arec) free(d->arec, M_DEVBUF);
47633dbf14aSCameron Grant 	if (d->rec) free(d->rec, M_DEVBUF);
47733dbf14aSCameron Grant 	if (d->ref) free(d->ref, M_DEVBUF);
47833dbf14aSCameron Grant 
4790f55ac6cSCameron Grant 	fkchan_kill(&d->fakechan);
48082db23e2SCameron Grant 
48182db23e2SCameron Grant #ifdef USING_DEVFS
4827c438dbeSCameron Grant 	pcm_makelinks(NULL);
48382db23e2SCameron Grant #endif
48433dbf14aSCameron Grant 	return 0;
48533dbf14aSCameron Grant }
4867c438dbeSCameron Grant 
487987e5972SCameron Grant /*
488987e5972SCameron Grant  * a small utility function which, given a device number, returns
489987e5972SCameron Grant  * a pointer to the associated snddev_info struct, and sets the unit
490987e5972SCameron Grant  * number.
491987e5972SCameron Grant  */
492987e5972SCameron Grant static snddev_info *
493987e5972SCameron Grant get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
494987e5972SCameron Grant {
49533dbf14aSCameron Grant 	snddev_info *sc;
496987e5972SCameron Grant     	int u, d, c;
497987e5972SCameron Grant 
498987e5972SCameron Grant     	u = PCMUNIT(i_dev);
499987e5972SCameron Grant     	d = PCMDEV(i_dev);
500987e5972SCameron Grant     	c = PCMCHAN(i_dev);
501987e5972SCameron Grant     	if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
502987e5972SCameron Grant     	if (unit) *unit = u;
503987e5972SCameron Grant     	if (dev) *dev = d;
504987e5972SCameron Grant     	if (chan) *chan = c;
505987e5972SCameron Grant     	if (u < 0) return NULL;
506987e5972SCameron Grant 
50733dbf14aSCameron Grant 	sc = devclass_get_softc(pcm_devclass, u);
50833dbf14aSCameron Grant 	if (sc == NULL || sc->magic == 0) return NULL;
50933dbf14aSCameron Grant 
510987e5972SCameron Grant 	switch(d) {
511987e5972SCameron Grant     	case SND_DEV_CTL:	/* /dev/mixer handled by pcm */
512987e5972SCameron Grant     	case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
513987e5972SCameron Grant     	case SND_DEV_DSP:
514987e5972SCameron Grant     	case SND_DEV_DSP16:
515987e5972SCameron Grant     	case SND_DEV_AUDIO:
51633dbf14aSCameron Grant 		return sc;
517987e5972SCameron Grant 
518987e5972SCameron Grant     	case SND_DEV_SEQ: /* XXX when enabled... */
519987e5972SCameron Grant     	case SND_DEV_SEQ2:
520987e5972SCameron Grant     	case SND_DEV_MIDIN:
521987e5972SCameron Grant     	case SND_DEV_SNDPROC:	/* /dev/sndproc handled by pcm */
522987e5972SCameron Grant     	default:
523987e5972SCameron Grant 		printf("unsupported subdevice %d\n", d);
524987e5972SCameron Grant 		return NULL;
525987e5972SCameron Grant     	}
526987e5972SCameron Grant }
527987e5972SCameron Grant 
528987e5972SCameron Grant static int
529987e5972SCameron Grant sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
530987e5972SCameron Grant {
531987e5972SCameron Grant     	int dev, unit, chan;
532987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
533987e5972SCameron Grant 
534987e5972SCameron Grant     	DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
535987e5972SCameron Grant 		unit, dev, flags, mode));
536987e5972SCameron Grant 
537987e5972SCameron Grant     	switch(dev) {
538987e5972SCameron Grant     	case SND_DEV_STATUS:
539987e5972SCameron Grant 		if (status_isopen) return EBUSY;
540987e5972SCameron Grant 		status_isopen = 1;
541987e5972SCameron Grant 		return 0;
542987e5972SCameron Grant 
543987e5972SCameron Grant     	case SND_DEV_CTL:
5440f55ac6cSCameron Grant 		return d? mixer_busy(d->mixer, 1) : ENXIO;
545987e5972SCameron Grant 
546987e5972SCameron Grant     	case SND_DEV_AUDIO:
547987e5972SCameron Grant     	case SND_DEV_DSP:
548987e5972SCameron Grant     	case SND_DEV_DSP16:
5495b78a734SCameron Grant 	case SND_DEV_NORESET:
550987e5972SCameron Grant 		return d? dsp_open(d, chan, flags, dev) : ENXIO;
551987e5972SCameron Grant 
552987e5972SCameron Grant     	default:
553987e5972SCameron Grant     		return ENXIO;
554987e5972SCameron Grant     	}
555987e5972SCameron Grant }
556987e5972SCameron Grant 
557987e5972SCameron Grant static int
558987e5972SCameron Grant sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
559987e5972SCameron Grant {
560987e5972SCameron Grant     	int dev, unit, chan;
561987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
562987e5972SCameron Grant 
563987e5972SCameron Grant     	DEB(printf("close snd%d subdev %d\n", unit, dev));
564987e5972SCameron Grant 
565987e5972SCameron Grant     	switch(dev) { /* only those for which close makes sense */
566987e5972SCameron Grant     	case SND_DEV_STATUS:
567987e5972SCameron Grant 		if (!status_isopen) return EBADF;
568987e5972SCameron Grant 		status_isopen = 0;
569987e5972SCameron Grant 		return 0;
570987e5972SCameron Grant 
571987e5972SCameron Grant     	case SND_DEV_CTL:
5720f55ac6cSCameron Grant 		return d? mixer_busy(d->mixer, 0) : ENXIO;
573987e5972SCameron Grant 
574987e5972SCameron Grant     	case SND_DEV_AUDIO:
575987e5972SCameron Grant     	case SND_DEV_DSP:
576987e5972SCameron Grant     	case SND_DEV_DSP16:
577987e5972SCameron Grant 		return d? dsp_close(d, chan, dev) : ENXIO;
578987e5972SCameron Grant 
579987e5972SCameron Grant     	default:
580987e5972SCameron Grant 		return ENXIO;
581987e5972SCameron Grant     	}
582987e5972SCameron Grant }
583987e5972SCameron Grant 
584987e5972SCameron Grant static int
585987e5972SCameron Grant sndread(dev_t i_dev, struct uio *buf, int flag)
586987e5972SCameron Grant {
587987e5972SCameron Grant     	int dev, unit, chan;
588987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
589987e5972SCameron Grant     	DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
590987e5972SCameron Grant 
591987e5972SCameron Grant     	switch(dev) {
592987e5972SCameron Grant     	case SND_DEV_STATUS:
593987e5972SCameron Grant 		return status_isopen? status_read(buf) : EBADF;
594987e5972SCameron Grant 
595987e5972SCameron Grant     	case SND_DEV_AUDIO:
596987e5972SCameron Grant     	case SND_DEV_DSP:
597987e5972SCameron Grant     	case SND_DEV_DSP16:
598987e5972SCameron Grant         	return d? dsp_read(d, chan, buf, flag) : EBADF;
599987e5972SCameron Grant 
600987e5972SCameron Grant     	default:
601987e5972SCameron Grant     		return ENXIO;
602987e5972SCameron Grant     	}
603987e5972SCameron Grant }
604987e5972SCameron Grant 
605987e5972SCameron Grant static int
606987e5972SCameron Grant sndwrite(dev_t i_dev, struct uio *buf, int flag)
607987e5972SCameron Grant {
608987e5972SCameron Grant     	int dev, unit, chan;
609987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
610987e5972SCameron Grant 
611987e5972SCameron Grant     	DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
612987e5972SCameron Grant 
613987e5972SCameron Grant     	switch(dev) {	/* only writeable devices */
614987e5972SCameron Grant     	case SND_DEV_DSP:
615987e5972SCameron Grant     	case SND_DEV_DSP16:
616987e5972SCameron Grant     	case SND_DEV_AUDIO:
617987e5972SCameron Grant 		return d? dsp_write(d, chan, buf, flag) : EBADF;
618987e5972SCameron Grant 
619987e5972SCameron Grant     	default:
620987e5972SCameron Grant 		return EPERM; /* for non-writeable devices ; */
621987e5972SCameron Grant     	}
622987e5972SCameron Grant }
623987e5972SCameron Grant 
624987e5972SCameron Grant static int
625987e5972SCameron Grant sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
626987e5972SCameron Grant {
627987e5972SCameron Grant     	int dev, chan;
628987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
629987e5972SCameron Grant 
630987e5972SCameron Grant     	if (d == NULL) return ENXIO;
631987e5972SCameron Grant 
632987e5972SCameron Grant     	switch(dev) {
633987e5972SCameron Grant     	case SND_DEV_CTL:
634987e5972SCameron Grant 		return mixer_ioctl(d, cmd, arg);
635987e5972SCameron Grant 
636987e5972SCameron Grant     	case SND_DEV_AUDIO:
637987e5972SCameron Grant     	case SND_DEV_DSP:
638987e5972SCameron Grant     	case SND_DEV_DSP16:
6391ad869dbSCameron Grant 		if (IOCGROUP(cmd) == 'M')
6401ad869dbSCameron Grant 			return mixer_ioctl(d, cmd, arg);
6411ad869dbSCameron Grant 		else
642987e5972SCameron Grant 			return dsp_ioctl(d, chan, cmd, arg);
643987e5972SCameron Grant 
644987e5972SCameron Grant     	default:
645987e5972SCameron Grant     		return ENXIO;
646987e5972SCameron Grant     	}
647987e5972SCameron Grant }
648987e5972SCameron Grant 
649987e5972SCameron Grant static int
650987e5972SCameron Grant sndpoll(dev_t i_dev, int events, struct proc *p)
651987e5972SCameron Grant {
652987e5972SCameron Grant     	int dev, chan;
653987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
654987e5972SCameron Grant 
6550e25481fSCameron Grant 	DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
656987e5972SCameron Grant 
657987e5972SCameron Grant     	if (d == NULL) return ENXIO;
658987e5972SCameron Grant 
659987e5972SCameron Grant     	switch(dev) {
660987e5972SCameron Grant     	case SND_DEV_AUDIO:
661987e5972SCameron Grant     	case SND_DEV_DSP:
662987e5972SCameron Grant     	case SND_DEV_DSP16:
663987e5972SCameron Grant 		return dsp_poll(d, chan, events, p);
664987e5972SCameron Grant 
665987e5972SCameron Grant     	default:
666987e5972SCameron Grant     		return (events &
667987e5972SCameron Grant        		       (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
668987e5972SCameron Grant     	}
669987e5972SCameron Grant }
670987e5972SCameron Grant 
671987e5972SCameron Grant /*
672987e5972SCameron Grant  * The mmap interface allows access to the play and read buffer,
673987e5972SCameron Grant  * plus the device descriptor.
674987e5972SCameron Grant  * The various blocks are accessible at the following offsets:
675987e5972SCameron Grant  *
676987e5972SCameron Grant  * 0x00000000 ( 0   ) : write buffer ;
677987e5972SCameron Grant  * 0x01000000 (16 MB) : read buffer ;
678987e5972SCameron Grant  * 0x02000000 (32 MB) : device descriptor (dangerous!)
679987e5972SCameron Grant  *
680987e5972SCameron Grant  * WARNING: the mmap routines assume memory areas are aligned. This
681987e5972SCameron Grant  * is true (probably) for the dma buffers, but likely false for the
682987e5972SCameron Grant  * device descriptor. As a consequence, we do not know where it is
683987e5972SCameron Grant  * located in the requested area.
684987e5972SCameron Grant  */
685987e5972SCameron Grant static int
686987e5972SCameron Grant sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
687987e5972SCameron Grant {
688987e5972SCameron Grant     	int unit, dev, chan;
689987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
690987e5972SCameron Grant 
691987e5972SCameron Grant     	DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
692987e5972SCameron Grant 		   d, dev, offset, nprot));
693987e5972SCameron Grant 
694987e5972SCameron Grant     	if (d == NULL || nprot & PROT_EXEC)	return -1; /* forbidden */
695987e5972SCameron Grant 
696987e5972SCameron Grant     	switch(dev) {
697987e5972SCameron Grant     	case SND_DEV_AUDIO:
698987e5972SCameron Grant     	case SND_DEV_DSP:
699987e5972SCameron Grant     	case SND_DEV_DSP16:
700987e5972SCameron Grant 		return dsp_mmap(d, chan, offset, nprot);
701987e5972SCameron Grant 
702987e5972SCameron Grant     	default:
703987e5972SCameron Grant     		return -1;
704987e5972SCameron Grant     	}
705987e5972SCameron Grant }
706987e5972SCameron Grant 
707987e5972SCameron Grant static int
708987e5972SCameron Grant status_init(char *buf, int size)
709987e5972SCameron Grant {
710987e5972SCameron Grant     	int             i;
711987e5972SCameron Grant     	device_t	    dev;
712987e5972SCameron Grant     	snddev_info     *d;
713987e5972SCameron Grant 
714987e5972SCameron Grant     	snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n"
715987e5972SCameron Grant 		 "Installed devices:\n", __DATE__, __TIME__);
716987e5972SCameron Grant 
717987e5972SCameron Grant     	for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
71833dbf14aSCameron Grant 		d = devclass_get_softc(pcm_devclass, i);
719987e5972SCameron Grant 		if (!d) continue;
720987e5972SCameron Grant 		dev = devclass_get_device(pcm_devclass, i);
721bf8ca271SCameron Grant         	if (1) {
722bf8ca271SCameron Grant 			snprintf(buf + strlen(buf), size - strlen(buf),
723bf8ca271SCameron Grant 		            	"pcm%d: <%s> %s",
724bf8ca271SCameron Grant 		            	i, device_get_desc(dev), d->status);
725bf8ca271SCameron Grant 			if (d->chancount > 0)
726bf8ca271SCameron Grant 				snprintf(buf + strlen(buf), size - strlen(buf),
727bf8ca271SCameron Grant 				" (%dp/%dr channels%s)\n",
728987e5972SCameron Grant 			    	d->playcount, d->reccount,
729987e5972SCameron Grant 			    	(!(d->flags & SD_F_SIMPLEX))? " duplex" : "");
730bf8ca271SCameron Grant 			else
731bf8ca271SCameron Grant 				snprintf(buf + strlen(buf), size - strlen(buf),
7329bc50208SCameron Grant 				" (mixer only)\n");
733bf8ca271SCameron Grant 		}
734987e5972SCameron Grant     	}
735987e5972SCameron Grant     	return strlen(buf);
736987e5972SCameron Grant }
737987e5972SCameron Grant 
738987e5972SCameron Grant static int
739987e5972SCameron Grant status_read(struct uio *buf)
740987e5972SCameron Grant {
741987e5972SCameron Grant     	static char	status_buf[4096];
742987e5972SCameron Grant     	static int 	bufptr = 0, buflen = 0;
743987e5972SCameron Grant     	int l;
744987e5972SCameron Grant 
745987e5972SCameron Grant     	if (status_isopen == 1) {
746987e5972SCameron Grant 		status_isopen++;
747987e5972SCameron Grant 		bufptr = 0;
748987e5972SCameron Grant 		buflen = status_init(status_buf, sizeof status_buf);
749987e5972SCameron Grant     	}
750987e5972SCameron Grant 
751987e5972SCameron Grant     	l = min(buf->uio_resid, buflen - bufptr);
752987e5972SCameron Grant     	bufptr += l;
753987e5972SCameron Grant     	return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0;
754987e5972SCameron Grant }
75533dbf14aSCameron Grant 
75633dbf14aSCameron Grant static int
75733dbf14aSCameron Grant sndpcm_modevent(module_t mod, int type, void *data)
75833dbf14aSCameron Grant {
75933dbf14aSCameron Grant 
76033dbf14aSCameron Grant 	switch (type) {
76133dbf14aSCameron Grant 	case MOD_LOAD:
76233dbf14aSCameron Grant 		break;
76333dbf14aSCameron Grant 	case MOD_UNLOAD:
764c9b53085SCameron Grant 		if (status_isopen)
765c9b53085SCameron Grant 			return EBUSY;
76633dbf14aSCameron Grant 		if (status_dev)
76733dbf14aSCameron Grant 			destroy_dev(status_dev);
76833dbf14aSCameron Grant 		status_dev = 0;
76933dbf14aSCameron Grant 		break;
77033dbf14aSCameron Grant 	default:
77133dbf14aSCameron Grant 		break;
77233dbf14aSCameron Grant 	}
77333dbf14aSCameron Grant 	return 0;
77433dbf14aSCameron Grant }
77533dbf14aSCameron Grant 
77633dbf14aSCameron Grant static moduledata_t sndpcm_mod = {
77733dbf14aSCameron Grant 	"snd_pcm",
77833dbf14aSCameron Grant 	sndpcm_modevent,
77933dbf14aSCameron Grant 	NULL
78033dbf14aSCameron Grant };
78133dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
78233dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER);
783