xref: /freebsd/sys/dev/sound/pcm/sound.c (revision b8f0d9e0b27eae76792126c3c9676a115c0763a5)
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
38b8f0d9e0SCameron Grant #define	PCM_MAXCHANS	256
39987e5972SCameron Grant 
4033dbf14aSCameron Grant static dev_t status_dev = 0;
41285648f9SCameron Grant static int do_status(int action, struct uio *buf);
42987e5972SCameron Grant 
43987e5972SCameron Grant static d_open_t sndopen;
44987e5972SCameron Grant static d_close_t sndclose;
45987e5972SCameron Grant static d_ioctl_t sndioctl;
46987e5972SCameron Grant static d_read_t sndread;
47987e5972SCameron Grant static d_write_t sndwrite;
48987e5972SCameron Grant static d_mmap_t sndmmap;
49987e5972SCameron Grant static d_poll_t sndpoll;
50987e5972SCameron Grant 
51987e5972SCameron Grant #define CDEV_MAJOR 30
52987e5972SCameron Grant static struct cdevsw snd_cdevsw = {
53987e5972SCameron Grant 	/* open */	sndopen,
54987e5972SCameron Grant 	/* close */	sndclose,
55987e5972SCameron Grant 	/* read */	sndread,
56987e5972SCameron Grant 	/* write */	sndwrite,
57987e5972SCameron Grant 	/* ioctl */	sndioctl,
58987e5972SCameron Grant 	/* poll */	sndpoll,
59987e5972SCameron Grant 	/* mmap */	sndmmap,
60987e5972SCameron Grant 	/* strategy */	nostrategy,
61987e5972SCameron Grant 	/* name */	"snd",
62987e5972SCameron Grant 	/* maj */	CDEV_MAJOR,
63987e5972SCameron Grant 	/* dump */	nodump,
64987e5972SCameron Grant 	/* psize */	nopsize,
6566ef8af5SCameron Grant 	/* flags */	D_TRACKCLOSE,
66987e5972SCameron Grant };
67987e5972SCameron Grant 
68dd186369SCameron Grant /*
69dd186369SCameron Grant PROPOSAL:
70987e5972SCameron Grant each unit needs:
71987e5972SCameron Grant status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
72987e5972SCameron Grant dspW and audio are deprecated.
73987e5972SCameron Grant dsp needs min 64 channels, will give it 256
74987e5972SCameron Grant 
75dd186369SCameron Grant minor = (unit << 20) + (dev << 16) + channel
76dd186369SCameron Grant currently minor = (channel << 16) + (unit << 4) + dev
77987e5972SCameron Grant 
78987e5972SCameron Grant nomenclature:
79987e5972SCameron Grant 	/dev/pcmX/dsp.(0..255)
80987e5972SCameron Grant 	/dev/pcmX/dspW
81987e5972SCameron Grant 	/dev/pcmX/audio
82987e5972SCameron Grant 	/dev/pcmX/status
83987e5972SCameron Grant 	/dev/pcmX/mixer
84987e5972SCameron Grant 	[etc.]
85987e5972SCameron Grant */
86987e5972SCameron Grant 
87987e5972SCameron Grant #define PCMMINOR(x) (minor(x))
88a618cffeSCameron Grant #define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16)
89987e5972SCameron Grant #define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
90987e5972SCameron Grant #define PCMDEV(x)   (PCMMINOR(x) & 0x0000000f)
91dd186369SCameron Grant #define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
92987e5972SCameron Grant 
93987e5972SCameron Grant static devclass_t pcm_devclass;
9482db23e2SCameron Grant 
9582db23e2SCameron Grant #ifdef USING_DEVFS
9681930014SPeter Wemm static int snd_unit = 0;
9709786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit);
9882db23e2SCameron Grant #endif
99987e5972SCameron Grant 
10082db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
10182db23e2SCameron Grant 
10237209180SCameron Grant void *
10337209180SCameron Grant snd_mtxcreate(const char *desc)
10437209180SCameron Grant {
10537209180SCameron Grant #ifdef USING_MUTEX
10637209180SCameron Grant 	struct mtx *m;
10737209180SCameron Grant 
10837209180SCameron Grant 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
10937209180SCameron Grant 	if (m == NULL)
11037209180SCameron Grant 		return NULL;
11137209180SCameron Grant 	mtx_init(m, desc, MTX_RECURSE);
11237209180SCameron Grant 	return m;
11337209180SCameron Grant #else
114a983d575SCameron Grant 	return (void *)0xcafebabe;
11537209180SCameron Grant #endif
11637209180SCameron Grant }
11737209180SCameron Grant 
11837209180SCameron Grant void
11937209180SCameron Grant snd_mtxfree(void *m)
12037209180SCameron Grant {
12137209180SCameron Grant #ifdef USING_MUTEX
12237209180SCameron Grant 	struct mtx *mtx = m;
12337209180SCameron Grant 
12437209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
12537209180SCameron Grant 	mtx_destroy(mtx);
12637209180SCameron Grant 	free(mtx, M_DEVBUF);
12737209180SCameron Grant #endif
12837209180SCameron Grant }
12937209180SCameron Grant 
13037209180SCameron Grant void
13137209180SCameron Grant snd_mtxassert(void *m)
13237209180SCameron Grant {
13337209180SCameron Grant #ifdef USING_MUTEX
13437209180SCameron Grant 	struct mtx *mtx = m;
13537209180SCameron Grant 
13637209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
13737209180SCameron Grant #endif
13837209180SCameron Grant }
13937209180SCameron Grant 
14037209180SCameron Grant void
14137209180SCameron Grant snd_mtxlock(void *m)
14237209180SCameron Grant {
14337209180SCameron Grant #ifdef USING_MUTEX
14437209180SCameron Grant 	struct mtx *mtx = m;
14537209180SCameron Grant 
14637209180SCameron Grant 	mtx_lock(mtx);
14737209180SCameron Grant #endif
14837209180SCameron Grant }
14937209180SCameron Grant 
15037209180SCameron Grant void
15137209180SCameron Grant snd_mtxunlock(void *m)
15237209180SCameron Grant {
15337209180SCameron Grant #ifdef USING_MUTEX
15437209180SCameron Grant 	struct mtx *mtx = m;
15537209180SCameron Grant 
15637209180SCameron Grant 	mtx_unlock(mtx);
15737209180SCameron Grant #endif
15837209180SCameron Grant }
15937209180SCameron Grant 
16037209180SCameron Grant int
16137209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
16237209180SCameron Grant {
16337209180SCameron Grant #ifdef USING_MUTEX
16437209180SCameron Grant 	flags &= INTR_MPSAFE;
16537209180SCameron Grant 	flags |= INTR_TYPE_TTY;
16637209180SCameron Grant #else
16737209180SCameron Grant 	flags = INTR_TYPE_TTY;
16837209180SCameron Grant #endif
16937209180SCameron Grant 	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
17037209180SCameron Grant }
17137209180SCameron Grant 
172b8f0d9e0SCameron Grant /* return a locked channel */
173285648f9SCameron Grant struct pcm_channel *
174b8f0d9e0SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid)
175285648f9SCameron Grant {
176285648f9SCameron Grant 	struct pcm_channel *c;
177285648f9SCameron Grant     	struct snddev_channel *sce;
178285648f9SCameron Grant 
179b8f0d9e0SCameron Grant 	snd_mtxassert(d->lock);
180285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
181285648f9SCameron Grant 		c = sce->channel;
18249c5e6e2SCameron Grant 		CHN_LOCK(c);
183285648f9SCameron Grant 		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
184285648f9SCameron Grant 			c->flags |= CHN_F_BUSY;
185b8f0d9e0SCameron Grant 			c->pid = pid;
186285648f9SCameron Grant 			return c;
187285648f9SCameron Grant 		}
18849c5e6e2SCameron Grant 		CHN_UNLOCK(c);
189285648f9SCameron Grant 	}
190285648f9SCameron Grant 	return NULL;
191285648f9SCameron Grant }
192285648f9SCameron Grant 
193b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
194285648f9SCameron Grant int
195b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
196285648f9SCameron Grant {
197b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
198285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
199b8f0d9e0SCameron Grant 	c->pid = -1;
20049c5e6e2SCameron Grant 	CHN_UNLOCK(c);
201285648f9SCameron Grant 	return 0;
202285648f9SCameron Grant }
203285648f9SCameron Grant 
204285648f9SCameron Grant int
205285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
206285648f9SCameron Grant {
20749c5e6e2SCameron Grant 	int r;
20849c5e6e2SCameron Grant 
209b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
210285648f9SCameron Grant 	c->refcount += ref;
21149c5e6e2SCameron Grant 	r = c->refcount;
21249c5e6e2SCameron Grant 	return r;
213285648f9SCameron Grant }
214285648f9SCameron Grant 
21582db23e2SCameron Grant #ifdef USING_DEVFS
21633dbf14aSCameron Grant static void
217b8f0d9e0SCameron Grant snd_setdefaultunit(struct snddev_info *d)
218987e5972SCameron Grant {
21933dbf14aSCameron Grant 	static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0;
22033dbf14aSCameron Grant 
22133dbf14aSCameron Grant 	if (dsp) {
22233dbf14aSCameron Grant 		destroy_dev(dsp);
22333dbf14aSCameron Grant 		dsp = 0;
224987e5972SCameron Grant 	}
22533dbf14aSCameron Grant 	if (dspW) {
22633dbf14aSCameron Grant 		destroy_dev(dspW);
22733dbf14aSCameron Grant 		dspW = 0;
22833dbf14aSCameron Grant 	}
22933dbf14aSCameron Grant 	if (audio) {
23033dbf14aSCameron Grant 		destroy_dev(audio);
23133dbf14aSCameron Grant 		audio = 0;
23233dbf14aSCameron Grant 	}
23333dbf14aSCameron Grant 	if (mixer) {
23433dbf14aSCameron Grant 		destroy_dev(mixer);
23533dbf14aSCameron Grant 		mixer = 0;
23633dbf14aSCameron Grant 	}
23733dbf14aSCameron Grant 
238b8f0d9e0SCameron Grant 	if (d == NULL)
23933dbf14aSCameron Grant 		return;
24033dbf14aSCameron Grant 
241b8f0d9e0SCameron Grant 	if (d->dspdev)
242b8f0d9e0SCameron Grant 		dsp = make_dev_alias(d->dspdev, "dsp");
243b8f0d9e0SCameron Grant 	if (d->dspWdev)
244b8f0d9e0SCameron Grant 		dspW = make_dev_alias(d->dspWdev, "dspW");
245b8f0d9e0SCameron Grant 	if (d->audiodev)
246b8f0d9e0SCameron Grant 		audio = make_dev_alias(d->audiodev, "audio");
247b8f0d9e0SCameron Grant 	if (d->mixerdev)
248b8f0d9e0SCameron Grant 		mixer = make_dev_alias(d->mixerdev, "mixer");
249b8f0d9e0SCameron Grant }
250b8f0d9e0SCameron Grant #endif
251b8f0d9e0SCameron Grant 
252b8f0d9e0SCameron Grant static void
253b8f0d9e0SCameron Grant pcm_relinkdspunit(struct snddev_info *d)
254b8f0d9e0SCameron Grant {
255b8f0d9e0SCameron Grant #ifdef USING_DEVFS
256b8f0d9e0SCameron Grant 	int unit = device_get_unit(d->dev);
257b8f0d9e0SCameron Grant 	dev_t pdev;
258b8f0d9e0SCameron Grant 
259b8f0d9e0SCameron Grant 	if (d->dspdev) {
260b8f0d9e0SCameron Grant 		destroy_dev(d->dspdev);
261b8f0d9e0SCameron Grant 		d->dspdev = 0;
262b8f0d9e0SCameron Grant 	}
263b8f0d9e0SCameron Grant 	if (d->dspWdev) {
264b8f0d9e0SCameron Grant 		destroy_dev(d->dspWdev);
265b8f0d9e0SCameron Grant 		d->dspWdev = 0;
266b8f0d9e0SCameron Grant 	}
267b8f0d9e0SCameron Grant 	if (d->audiodev) {
268b8f0d9e0SCameron Grant 		destroy_dev(d->audiodev);
269b8f0d9e0SCameron Grant 		d->audiodev = 0;
27033dbf14aSCameron Grant 	}
27133dbf14aSCameron Grant 
272b8f0d9e0SCameron Grant 	if (d->defaultchan < d->chancount) {
273b8f0d9e0SCameron Grant 		pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->defaultchan));
274b8f0d9e0SCameron Grant 		d->dspdev = make_dev_alias(pdev, "dsp%d", unit);
275b8f0d9e0SCameron Grant 
276b8f0d9e0SCameron Grant 		pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->defaultchan));
277b8f0d9e0SCameron Grant 		d->dspWdev = make_dev_alias(pdev, "dspW%d", unit);
278b8f0d9e0SCameron Grant 
279b8f0d9e0SCameron Grant 		pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->defaultchan));
280b8f0d9e0SCameron Grant 		d->audiodev = make_dev_alias(pdev, "audio%d", unit);
281b8f0d9e0SCameron Grant 	}
282b8f0d9e0SCameron Grant 
283b8f0d9e0SCameron Grant 	if (unit == snd_unit)
284b8f0d9e0SCameron Grant 		snd_setdefaultunit(d);
285b8f0d9e0SCameron Grant #endif
286b8f0d9e0SCameron Grant }
287b8f0d9e0SCameron Grant 
288b8f0d9e0SCameron Grant #ifdef USING_DEVFS
28933dbf14aSCameron Grant static int
29033dbf14aSCameron Grant sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS)
29133dbf14aSCameron Grant {
292b8f0d9e0SCameron Grant 	struct snddev_info *d;
29333dbf14aSCameron Grant 	int error, unit;
29433dbf14aSCameron Grant 
29533dbf14aSCameron Grant 	unit = snd_unit;
29633dbf14aSCameron Grant 	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
29733dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
298b8f0d9e0SCameron Grant 		if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass))
299b8f0d9e0SCameron Grant 			return EINVAL;
300b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
301b8f0d9e0SCameron Grant 		if (d == NULL || d->chancount == 0)
302b8f0d9e0SCameron Grant 			return EINVAL;
30333dbf14aSCameron Grant 		snd_unit = unit;
304b8f0d9e0SCameron Grant 		snd_setdefaultunit(d);
30533dbf14aSCameron Grant 	}
30633dbf14aSCameron Grant 	return (error);
30733dbf14aSCameron Grant }
308b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
30933dbf14aSCameron Grant             0, sizeof(int), sysctl_hw_sndunit, "I", "");
31082db23e2SCameron Grant #endif
311987e5972SCameron Grant 
312285648f9SCameron Grant struct pcm_channel *
313285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
314987e5972SCameron Grant {
315285648f9SCameron Grant 	struct pcm_channel *ch;
31633dbf14aSCameron Grant 	char *dirs;
3170f55ac6cSCameron Grant     	int err;
318987e5972SCameron Grant 
319285648f9SCameron Grant 	switch(dir) {
320285648f9SCameron Grant 	case PCMDIR_PLAY:
321285648f9SCameron Grant 		dirs = "play";
322285648f9SCameron Grant 		break;
323285648f9SCameron Grant 	case PCMDIR_REC:
324285648f9SCameron Grant 		dirs = "record";
325285648f9SCameron Grant 		break;
326285648f9SCameron Grant 	case PCMDIR_VIRTUAL:
327285648f9SCameron Grant 		dirs = "virtual";
328285648f9SCameron Grant 		dir = PCMDIR_PLAY;
329285648f9SCameron Grant 		break;
330285648f9SCameron Grant 	default:
331285648f9SCameron Grant 		return NULL;
3329c326820SCameron Grant 	}
333285648f9SCameron Grant 
334285648f9SCameron Grant 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
335285648f9SCameron Grant 	if (!ch)
336285648f9SCameron Grant 		return NULL;
337285648f9SCameron Grant 
3380f55ac6cSCameron Grant 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
339285648f9SCameron Grant 	if (!ch->methods) {
340285648f9SCameron Grant 		free(ch, M_DEVBUF);
341285648f9SCameron Grant 		return NULL;
342285648f9SCameron Grant 	}
343285648f9SCameron Grant 
344285648f9SCameron Grant 	ch->pid = -1;
345285648f9SCameron Grant 	ch->parentsnddev = d;
346285648f9SCameron Grant 	ch->parentchannel = parent;
347285648f9SCameron Grant 	snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs);
348285648f9SCameron Grant 
3490f55ac6cSCameron Grant 	err = chn_init(ch, devinfo, dir);
3500f55ac6cSCameron Grant 	if (err) {
351285648f9SCameron Grant 		device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err);
352285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
353285648f9SCameron Grant 		free(ch, M_DEVBUF);
354285648f9SCameron Grant 		return NULL;
355bbb5bf3dSCameron Grant 	}
356285648f9SCameron Grant 
357285648f9SCameron Grant 	return ch;
358285648f9SCameron Grant }
359285648f9SCameron Grant 
360285648f9SCameron Grant int
361285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
362285648f9SCameron Grant {
363285648f9SCameron Grant 	int err;
364285648f9SCameron Grant 
365285648f9SCameron Grant 	err = chn_kill(ch);
366285648f9SCameron Grant 	if (err) {
367285648f9SCameron Grant 		device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err);
368285648f9SCameron Grant 		return err;
369285648f9SCameron Grant 	}
370285648f9SCameron Grant 
371285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
372285648f9SCameron Grant 	free(ch, M_DEVBUF);
373285648f9SCameron Grant 
374285648f9SCameron Grant 	return 0;
375285648f9SCameron Grant }
376285648f9SCameron Grant 
377285648f9SCameron Grant int
378285648f9SCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
379285648f9SCameron Grant {
380285648f9SCameron Grant     	struct snddev_channel *sce;
381b8f0d9e0SCameron Grant 	struct pcm_channel **aplay, **arec;
382285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
383b8f0d9e0SCameron Grant 	int cc, sz;
384b8f0d9e0SCameron Grant 
385b8f0d9e0SCameron Grant 	snd_mtxlock(d->lock);
386b8f0d9e0SCameron Grant 	if (d->chancount == d->maxchans) {
387b8f0d9e0SCameron Grant 		cc = d->maxchans? d->maxchans * 2 : 2;
388b8f0d9e0SCameron Grant 	    	sz = cc * sizeof(struct pcm_channel *);
389b8f0d9e0SCameron Grant 		aplay = malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
390b8f0d9e0SCameron Grant     		arec = malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
391b8f0d9e0SCameron Grant 		if (aplay == NULL || arec == NULL) {
392b8f0d9e0SCameron Grant 			if (aplay)
393b8f0d9e0SCameron Grant 				free(aplay, M_DEVBUF);
394b8f0d9e0SCameron Grant 			if (arec)
395b8f0d9e0SCameron Grant 				free(arec, M_DEVBUF);
396b8f0d9e0SCameron Grant 			snd_mtxunlock(d->lock);
397b8f0d9e0SCameron Grant 			return EINVAL;
398b8f0d9e0SCameron Grant 		}
399b8f0d9e0SCameron Grant 		if (d->aplay) {
400b8f0d9e0SCameron Grant 			bcopy(d->aplay, aplay, d->maxchans * sizeof(struct pcm_channel *));
401b8f0d9e0SCameron Grant 			free(d->aplay, M_DEVBUF);
402b8f0d9e0SCameron Grant 		}
403b8f0d9e0SCameron Grant 		d->aplay = aplay;
404b8f0d9e0SCameron Grant 		if (d->arec) {
405b8f0d9e0SCameron Grant 			bcopy(d->arec, arec, d->maxchans * sizeof(struct pcm_channel *));
406b8f0d9e0SCameron Grant 			free(d->arec, M_DEVBUF);
407b8f0d9e0SCameron Grant 		}
408b8f0d9e0SCameron Grant 		d->arec = arec;
409b8f0d9e0SCameron Grant 		d->maxchans = cc;
410b8f0d9e0SCameron Grant 	}
411285648f9SCameron Grant 
412285648f9SCameron Grant 	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
413285648f9SCameron Grant 	if (!sce) {
414b8f0d9e0SCameron Grant 		snd_mtxunlock(d->lock);
415285648f9SCameron Grant 		return ENOMEM;
416285648f9SCameron Grant 	}
417285648f9SCameron Grant 
418285648f9SCameron Grant 	sce->channel = ch;
419285648f9SCameron Grant 	SLIST_INSERT_HEAD(&d->channels, sce, link);
420285648f9SCameron Grant 
4217207eca6SCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount),
4227207eca6SCameron Grant 		 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount);
4237207eca6SCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount),
4247207eca6SCameron Grant 		 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount);
42533dbf14aSCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount),
42633dbf14aSCameron Grant 		 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount);
4277207eca6SCameron Grant 	/* XXX SND_DEV_NORESET? */
428285648f9SCameron Grant 
429285648f9SCameron Grant     	if (d->chancount++ == 0)
430b8f0d9e0SCameron Grant 		pcm_relinkdspunit(d);
431b8f0d9e0SCameron Grant 
43249c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
433285648f9SCameron Grant 
43433dbf14aSCameron Grant 	return 0;
43533dbf14aSCameron Grant }
43633dbf14aSCameron Grant 
437285648f9SCameron Grant int
438285648f9SCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
43933dbf14aSCameron Grant {
440285648f9SCameron Grant     	struct snddev_channel *sce;
441285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
44233dbf14aSCameron Grant 	dev_t pdev;
44333dbf14aSCameron Grant 
44449c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
445285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
446285648f9SCameron Grant 		if (sce->channel == ch)
447285648f9SCameron Grant 			goto gotit;
44833dbf14aSCameron Grant 	}
44949c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
450285648f9SCameron Grant 	return EINVAL;
451285648f9SCameron Grant gotit:
45233dbf14aSCameron Grant 	d->chancount--;
453285648f9SCameron Grant 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
454285648f9SCameron Grant 	free(sce, M_DEVBUF);
455285648f9SCameron Grant 
456285648f9SCameron Grant     	if (d->chancount == 0)
457b8f0d9e0SCameron Grant 		pcm_relinkdspunit(d);
45833dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount));
45933dbf14aSCameron Grant 	destroy_dev(pdev);
46033dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount));
46133dbf14aSCameron Grant 	destroy_dev(pdev);
46233dbf14aSCameron Grant 	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount));
46333dbf14aSCameron Grant 	destroy_dev(pdev);
46449c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
465285648f9SCameron Grant 
466987e5972SCameron Grant 	return 0;
467987e5972SCameron Grant }
468987e5972SCameron Grant 
469987e5972SCameron Grant int
470285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
471285648f9SCameron Grant {
472285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
473285648f9SCameron Grant 	struct pcm_channel *ch;
474285648f9SCameron Grant     	int err;
475285648f9SCameron Grant 
476285648f9SCameron Grant 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
477285648f9SCameron Grant 	if (!ch) {
478285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
479285648f9SCameron Grant 		return ENODEV;
480285648f9SCameron Grant 	}
481285648f9SCameron Grant 	err = pcm_chn_add(d, ch);
482285648f9SCameron Grant 	if (err) {
483285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
484285648f9SCameron Grant 		pcm_chn_destroy(ch);
485285648f9SCameron Grant 	}
486285648f9SCameron Grant 
487285648f9SCameron Grant 	return err;
488285648f9SCameron Grant }
489285648f9SCameron Grant 
490285648f9SCameron Grant static int
491285648f9SCameron Grant pcm_killchan(device_t dev)
492285648f9SCameron Grant {
493285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
494285648f9SCameron Grant     	struct snddev_channel *sce;
495285648f9SCameron Grant 
49649c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
497285648f9SCameron Grant 	sce = SLIST_FIRST(&d->channels);
49849c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
499285648f9SCameron Grant 
500285648f9SCameron Grant 	return pcm_chn_remove(d, sce->channel);
501285648f9SCameron Grant }
502285648f9SCameron Grant 
503285648f9SCameron Grant int
504987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
505987e5972SCameron Grant {
50666ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
50749c5e6e2SCameron Grant 
50849c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
509987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
51049c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
511987e5972SCameron Grant 	return 0;
512987e5972SCameron Grant }
513987e5972SCameron Grant 
514987e5972SCameron Grant u_int32_t
515987e5972SCameron Grant pcm_getflags(device_t dev)
516987e5972SCameron Grant {
51766ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
51849c5e6e2SCameron Grant 
519987e5972SCameron Grant 	return d->flags;
520987e5972SCameron Grant }
521987e5972SCameron Grant 
522987e5972SCameron Grant void
523987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
524987e5972SCameron Grant {
52566ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
526b8f0d9e0SCameron Grant /*
527b8f0d9e0SCameron Grant 	if ((val & SD_F_SIMPLEX) && (d->fakechan == NULL)) {
528b8f0d9e0SCameron Grant 		device_printf(dev, "set simplex mode\n");
529b8f0d9e0SCameron Grant 		d->fakechan = fkchan_setup(dev);
530b8f0d9e0SCameron Grant 		chn_init(d->fakechan, NULL, 0);
531b8f0d9e0SCameron Grant 	}
532b8f0d9e0SCameron Grant */
533987e5972SCameron Grant 	d->flags = val;
534987e5972SCameron Grant }
535987e5972SCameron Grant 
53639004e69SCameron Grant void *
53739004e69SCameron Grant pcm_getdevinfo(device_t dev)
53839004e69SCameron Grant {
53966ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
54049c5e6e2SCameron Grant 
54139004e69SCameron Grant 	return d->devinfo;
54239004e69SCameron Grant }
54339004e69SCameron Grant 
544987e5972SCameron Grant /* This is the generic init routine */
545987e5972SCameron Grant int
546987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
547987e5972SCameron Grant {
548b8f0d9e0SCameron Grant     	int unit = device_get_unit(dev);
54966ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
550987e5972SCameron Grant 
55149c5e6e2SCameron Grant 	d->lock = snd_mtxcreate(device_get_nameunit(dev));
55249c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
553987e5972SCameron Grant     	if (!pcm_devclass) {
554987e5972SCameron Grant     		pcm_devclass = device_get_devclass(dev);
55533dbf14aSCameron Grant 		status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0),
556083279e4SSeigo Tanimura 			 UID_ROOT, GID_WHEEL, 0444, "sndstat");
557083279e4SSeigo Tanimura 	}
558285648f9SCameron Grant 
559b8f0d9e0SCameron Grant 	d->mixerdev = make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
56011346231SSeigo Tanimura 		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
561285648f9SCameron Grant 
562b8f0d9e0SCameron Grant 	d->dspdev = 0;
563b8f0d9e0SCameron Grant 	d->dspWdev = 0;
564b8f0d9e0SCameron Grant 	d->audiodev = 0;
565e4d5b250SCameron Grant 	d->dev = dev;
566987e5972SCameron Grant 	d->devinfo = devinfo;
567285648f9SCameron Grant 	d->chancount = 0;
568b8f0d9e0SCameron Grant 	d->defaultchan = 0;
569b8f0d9e0SCameron Grant 	d->maxchans = 0;
570b8f0d9e0SCameron Grant 	d->aplay = NULL;
571b8f0d9e0SCameron Grant 	d->arec = NULL;
572b8f0d9e0SCameron Grant /*
573285648f9SCameron Grant     	sz = d->maxchans * sizeof(struct pcm_channel *);
574833f7023SCameron Grant 
575833f7023SCameron Grant 	if (sz > 0) {
576285648f9SCameron Grant 		d->aplay = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
577285648f9SCameron Grant     		d->arec = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO);
578285648f9SCameron Grant     		if (!d->arec || !d->aplay) goto no;
579b8f0d9e0SCameron Grant 	}
580b8f0d9e0SCameron Grant */
581b8f0d9e0SCameron Grant 	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) {
582285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
583b8f0d9e0SCameron Grant 	}
584285648f9SCameron Grant 	d->fakechan = fkchan_setup(dev);
585285648f9SCameron Grant 	chn_init(d->fakechan, NULL, 0);
586987e5972SCameron Grant 
58782db23e2SCameron Grant #ifdef SND_DYNSYSCTL
588cc486d80SJohn Baldwin 	sysctl_ctx_init(&d->sysctl_tree);
589cc486d80SJohn Baldwin 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
590a3e893e0SJohn Baldwin 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
591cc486d80SJohn Baldwin 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
592a3e893e0SJohn Baldwin 	if (d->sysctl_tree_top == NULL) {
593cc486d80SJohn Baldwin 		sysctl_ctx_free(&d->sysctl_tree);
594cc486d80SJohn Baldwin 		goto no;
595cc486d80SJohn Baldwin 	}
59682db23e2SCameron Grant #endif
59749c5e6e2SCameron Grant #if 1
598b8f0d9e0SCameron Grant 	if (numplay > 0)
599285648f9SCameron Grant 		vchan_initsys(d);
600285648f9SCameron Grant #endif
601b8f0d9e0SCameron Grant 	snd_setdefaultunit(d);
60249c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
603987e5972SCameron Grant     	return 0;
604987e5972SCameron Grant no:
605b8f0d9e0SCameron Grant /*
606987e5972SCameron Grant 	if (d->aplay) free(d->aplay, M_DEVBUF);
607987e5972SCameron Grant 	if (d->arec) free(d->arec, M_DEVBUF);
608b8f0d9e0SCameron Grant */
60949c5e6e2SCameron Grant 	/* snd_mtxunlock(d->lock); */
61049c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
611987e5972SCameron Grant 	return ENXIO;
612987e5972SCameron Grant }
613987e5972SCameron Grant 
61433dbf14aSCameron Grant int
61533dbf14aSCameron Grant pcm_unregister(device_t dev)
6167c438dbeSCameron Grant {
617285648f9SCameron Grant     	int unit = device_get_unit(dev);
61866ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
619285648f9SCameron Grant     	struct snddev_channel *sce;
6207c438dbeSCameron Grant 
62149c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
622285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
623285648f9SCameron Grant 		if (sce->channel->refcount > 0) {
624c9b53085SCameron Grant 			device_printf(dev, "unregister: channel busy");
62549c5e6e2SCameron Grant 			snd_mtxunlock(d->lock);
626285648f9SCameron Grant 			return EBUSY;
627285648f9SCameron Grant 		}
628c9b53085SCameron Grant 	}
6290f55ac6cSCameron Grant 	if (mixer_isbusy(d->mixer)) {
630c9b53085SCameron Grant 		device_printf(dev, "unregister: mixer busy");
63149c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
632c9b53085SCameron Grant 		return EBUSY;
633c9b53085SCameron Grant 	}
6347c438dbeSCameron Grant 
63566ef8af5SCameron Grant #ifdef SND_DYNSYSCTL
63666ef8af5SCameron Grant 	d->sysctl_tree_top = NULL;
63766ef8af5SCameron Grant 	sysctl_ctx_free(&d->sysctl_tree);
63866ef8af5SCameron Grant #endif
639b8f0d9e0SCameron Grant 	if (unit == snd_unit)
640b8f0d9e0SCameron Grant 		snd_setdefaultunit(NULL);
641b8f0d9e0SCameron Grant 	destroy_dev(d->mixerdev);
64233dbf14aSCameron Grant 	mixer_uninit(dev);
6437c438dbeSCameron Grant 
644285648f9SCameron Grant 	while (d->chancount > 0)
645285648f9SCameron Grant 		pcm_killchan(dev);
6467c438dbeSCameron Grant 
64733dbf14aSCameron Grant 	if (d->aplay) free(d->aplay, M_DEVBUF);
64833dbf14aSCameron Grant 	if (d->arec) free(d->arec, M_DEVBUF);
64933dbf14aSCameron Grant 
65066ef8af5SCameron Grant 	chn_kill(d->fakechan);
65166ef8af5SCameron Grant 	fkchan_kill(d->fakechan);
65282db23e2SCameron Grant 
65349c5e6e2SCameron Grant 	/* snd_mtxunlock(d->lock); */
65449c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
65533dbf14aSCameron Grant 	return 0;
65633dbf14aSCameron Grant }
6577c438dbeSCameron Grant 
658987e5972SCameron Grant /*
659987e5972SCameron Grant  * a small utility function which, given a device number, returns
66066ef8af5SCameron Grant  * a pointer to the associated struct snddev_info struct, and sets the unit
661987e5972SCameron Grant  * number.
662987e5972SCameron Grant  */
66366ef8af5SCameron Grant static struct snddev_info *
664987e5972SCameron Grant get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
665987e5972SCameron Grant {
66666ef8af5SCameron Grant 	struct snddev_info *sc;
667987e5972SCameron Grant     	int u, d, c;
668987e5972SCameron Grant 
669987e5972SCameron Grant     	u = PCMUNIT(i_dev);
670987e5972SCameron Grant     	d = PCMDEV(i_dev);
671987e5972SCameron Grant     	c = PCMCHAN(i_dev);
672987e5972SCameron Grant     	if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
673987e5972SCameron Grant     	if (unit) *unit = u;
674987e5972SCameron Grant     	if (dev) *dev = d;
675987e5972SCameron Grant     	if (chan) *chan = c;
676987e5972SCameron Grant     	if (u < 0) return NULL;
677987e5972SCameron Grant 
67833dbf14aSCameron Grant 	sc = devclass_get_softc(pcm_devclass, u);
67966ef8af5SCameron Grant 	if (sc == NULL) return NULL;
68033dbf14aSCameron Grant 
681987e5972SCameron Grant 	switch(d) {
682987e5972SCameron Grant     	case SND_DEV_CTL:	/* /dev/mixer handled by pcm */
683987e5972SCameron Grant     	case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
684987e5972SCameron Grant     	case SND_DEV_DSP:
685987e5972SCameron Grant     	case SND_DEV_DSP16:
686987e5972SCameron Grant     	case SND_DEV_AUDIO:
68733dbf14aSCameron Grant 		return sc;
688987e5972SCameron Grant 
689987e5972SCameron Grant     	case SND_DEV_SEQ: /* XXX when enabled... */
690987e5972SCameron Grant     	case SND_DEV_SEQ2:
691987e5972SCameron Grant     	case SND_DEV_MIDIN:
692987e5972SCameron Grant     	case SND_DEV_SNDPROC:	/* /dev/sndproc handled by pcm */
693987e5972SCameron Grant     	default:
694987e5972SCameron Grant 		printf("unsupported subdevice %d\n", d);
695987e5972SCameron Grant 		return NULL;
696987e5972SCameron Grant     	}
697987e5972SCameron Grant }
698987e5972SCameron Grant 
699987e5972SCameron Grant static int
700987e5972SCameron Grant sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
701987e5972SCameron Grant {
702987e5972SCameron Grant     	int dev, unit, chan;
70366ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
704987e5972SCameron Grant 
705987e5972SCameron Grant     	DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
706987e5972SCameron Grant 		unit, dev, flags, mode));
707987e5972SCameron Grant 
708987e5972SCameron Grant     	switch(dev) {
709987e5972SCameron Grant     	case SND_DEV_STATUS:
710285648f9SCameron Grant 		return do_status(0, NULL);
711987e5972SCameron Grant 
712987e5972SCameron Grant     	case SND_DEV_CTL:
7130f55ac6cSCameron Grant 		return d? mixer_busy(d->mixer, 1) : ENXIO;
714987e5972SCameron Grant 
715987e5972SCameron Grant     	case SND_DEV_AUDIO:
716987e5972SCameron Grant     	case SND_DEV_DSP:
717987e5972SCameron Grant     	case SND_DEV_DSP16:
7185b78a734SCameron Grant 	case SND_DEV_NORESET:
719285648f9SCameron Grant 		return d? dsp_open(d, chan, flags, dev, p->p_pid) : ENXIO;
720987e5972SCameron Grant 
721987e5972SCameron Grant     	default:
722987e5972SCameron Grant     		return ENXIO;
723987e5972SCameron Grant     	}
724987e5972SCameron Grant }
725987e5972SCameron Grant 
726987e5972SCameron Grant static int
727987e5972SCameron Grant sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
728987e5972SCameron Grant {
729987e5972SCameron Grant     	int dev, unit, chan;
73066ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
731987e5972SCameron Grant 
732987e5972SCameron Grant     	DEB(printf("close snd%d subdev %d\n", unit, dev));
733987e5972SCameron Grant 
734987e5972SCameron Grant     	switch(dev) { /* only those for which close makes sense */
735987e5972SCameron Grant     	case SND_DEV_STATUS:
736285648f9SCameron Grant 		return do_status(1, NULL);
737987e5972SCameron Grant 
738987e5972SCameron Grant     	case SND_DEV_CTL:
7390f55ac6cSCameron Grant 		return d? mixer_busy(d->mixer, 0) : ENXIO;
740987e5972SCameron Grant 
741987e5972SCameron Grant     	case SND_DEV_AUDIO:
742987e5972SCameron Grant     	case SND_DEV_DSP:
743987e5972SCameron Grant     	case SND_DEV_DSP16:
744987e5972SCameron Grant 		return d? dsp_close(d, chan, dev) : ENXIO;
745987e5972SCameron Grant 
746987e5972SCameron Grant     	default:
747987e5972SCameron Grant 		return ENXIO;
748987e5972SCameron Grant     	}
749987e5972SCameron Grant }
750987e5972SCameron Grant 
751987e5972SCameron Grant static int
752987e5972SCameron Grant sndread(dev_t i_dev, struct uio *buf, int flag)
753987e5972SCameron Grant {
754987e5972SCameron Grant     	int dev, unit, chan;
75566ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
756987e5972SCameron Grant     	DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
757987e5972SCameron Grant 
758987e5972SCameron Grant     	switch(dev) {
759987e5972SCameron Grant     	case SND_DEV_STATUS:
760285648f9SCameron Grant 		return do_status(2, buf);
761987e5972SCameron Grant 
762987e5972SCameron Grant     	case SND_DEV_AUDIO:
763987e5972SCameron Grant     	case SND_DEV_DSP:
764987e5972SCameron Grant     	case SND_DEV_DSP16:
765987e5972SCameron Grant         	return d? dsp_read(d, chan, buf, flag) : EBADF;
766987e5972SCameron Grant 
767987e5972SCameron Grant     	default:
768987e5972SCameron Grant     		return ENXIO;
769987e5972SCameron Grant     	}
770987e5972SCameron Grant }
771987e5972SCameron Grant 
772987e5972SCameron Grant static int
773987e5972SCameron Grant sndwrite(dev_t i_dev, struct uio *buf, int flag)
774987e5972SCameron Grant {
775987e5972SCameron Grant     	int dev, unit, chan;
77666ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
777987e5972SCameron Grant 
778987e5972SCameron Grant     	DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
779987e5972SCameron Grant 
780987e5972SCameron Grant     	switch(dev) {	/* only writeable devices */
781987e5972SCameron Grant     	case SND_DEV_DSP:
782987e5972SCameron Grant     	case SND_DEV_DSP16:
783987e5972SCameron Grant     	case SND_DEV_AUDIO:
784987e5972SCameron Grant 		return d? dsp_write(d, chan, buf, flag) : EBADF;
785987e5972SCameron Grant 
786987e5972SCameron Grant     	default:
787987e5972SCameron Grant 		return EPERM; /* for non-writeable devices ; */
788987e5972SCameron Grant     	}
789987e5972SCameron Grant }
790987e5972SCameron Grant 
791987e5972SCameron Grant static int
792987e5972SCameron Grant sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
793987e5972SCameron Grant {
794987e5972SCameron Grant     	int dev, chan;
79566ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
796987e5972SCameron Grant 
797987e5972SCameron Grant     	if (d == NULL) return ENXIO;
798987e5972SCameron Grant 
799987e5972SCameron Grant     	switch(dev) {
800987e5972SCameron Grant     	case SND_DEV_CTL:
801987e5972SCameron Grant 		return mixer_ioctl(d, cmd, arg);
802987e5972SCameron Grant 
803987e5972SCameron Grant     	case SND_DEV_AUDIO:
804987e5972SCameron Grant     	case SND_DEV_DSP:
805987e5972SCameron Grant     	case SND_DEV_DSP16:
8061ad869dbSCameron Grant 		if (IOCGROUP(cmd) == 'M')
8071ad869dbSCameron Grant 			return mixer_ioctl(d, cmd, arg);
8081ad869dbSCameron Grant 		else
809987e5972SCameron Grant 			return dsp_ioctl(d, chan, cmd, arg);
810987e5972SCameron Grant 
811987e5972SCameron Grant     	default:
812987e5972SCameron Grant     		return ENXIO;
813987e5972SCameron Grant     	}
814987e5972SCameron Grant }
815987e5972SCameron Grant 
816987e5972SCameron Grant static int
817987e5972SCameron Grant sndpoll(dev_t i_dev, int events, struct proc *p)
818987e5972SCameron Grant {
819987e5972SCameron Grant     	int dev, chan;
82066ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
821987e5972SCameron Grant 
8220e25481fSCameron Grant 	DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
823987e5972SCameron Grant 
824987e5972SCameron Grant     	if (d == NULL) return ENXIO;
825987e5972SCameron Grant 
826987e5972SCameron Grant     	switch(dev) {
827987e5972SCameron Grant     	case SND_DEV_AUDIO:
828987e5972SCameron Grant     	case SND_DEV_DSP:
829987e5972SCameron Grant     	case SND_DEV_DSP16:
830987e5972SCameron Grant 		return dsp_poll(d, chan, events, p);
831987e5972SCameron Grant 
832987e5972SCameron Grant     	default:
833987e5972SCameron Grant     		return (events &
834987e5972SCameron Grant        		       (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
835987e5972SCameron Grant     	}
836987e5972SCameron Grant }
837987e5972SCameron Grant 
838987e5972SCameron Grant /*
839987e5972SCameron Grant  * The mmap interface allows access to the play and read buffer,
840987e5972SCameron Grant  * plus the device descriptor.
841987e5972SCameron Grant  * The various blocks are accessible at the following offsets:
842987e5972SCameron Grant  *
843987e5972SCameron Grant  * 0x00000000 ( 0   ) : write buffer ;
844987e5972SCameron Grant  * 0x01000000 (16 MB) : read buffer ;
845987e5972SCameron Grant  * 0x02000000 (32 MB) : device descriptor (dangerous!)
846987e5972SCameron Grant  *
847987e5972SCameron Grant  * WARNING: the mmap routines assume memory areas are aligned. This
848987e5972SCameron Grant  * is true (probably) for the dma buffers, but likely false for the
849987e5972SCameron Grant  * device descriptor. As a consequence, we do not know where it is
850987e5972SCameron Grant  * located in the requested area.
851987e5972SCameron Grant  */
852987e5972SCameron Grant static int
853987e5972SCameron Grant sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
854987e5972SCameron Grant {
855987e5972SCameron Grant     	int unit, dev, chan;
85666ef8af5SCameron Grant     	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
857987e5972SCameron Grant 
858987e5972SCameron Grant     	DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
859987e5972SCameron Grant 		   d, dev, offset, nprot));
860987e5972SCameron Grant 
861987e5972SCameron Grant     	if (d == NULL || nprot & PROT_EXEC)	return -1; /* forbidden */
862987e5972SCameron Grant 
863987e5972SCameron Grant     	switch(dev) {
864987e5972SCameron Grant     	case SND_DEV_AUDIO:
865987e5972SCameron Grant     	case SND_DEV_DSP:
866987e5972SCameron Grant     	case SND_DEV_DSP16:
867987e5972SCameron Grant 		return dsp_mmap(d, chan, offset, nprot);
868987e5972SCameron Grant 
869987e5972SCameron Grant     	default:
870987e5972SCameron Grant     		return -1;
871987e5972SCameron Grant     	}
872987e5972SCameron Grant }
873987e5972SCameron Grant 
874987e5972SCameron Grant static int
875285648f9SCameron Grant status_init(struct sbuf *s)
876987e5972SCameron Grant {
877285648f9SCameron Grant     	int i, pc, rc, vc;
878987e5972SCameron Grant     	device_t dev;
87966ef8af5SCameron Grant     	struct snddev_info *d;
880285648f9SCameron Grant     	struct snddev_channel *sce;
881285648f9SCameron Grant 	struct pcm_channel *c;
88249c5e6e2SCameron Grant #ifdef SNDSTAT_VERBOSE
883285648f9SCameron Grant 	struct pcm_feeder *f;
88449c5e6e2SCameron Grant #endif
885987e5972SCameron Grant 
886285648f9SCameron Grant 	sbuf_printf(s, "FreeBSD Audio Driver (newpcm) %s %s\nInstalled devices:\n",
887285648f9SCameron Grant 		 	__DATE__, __TIME__);
888987e5972SCameron Grant 
889987e5972SCameron Grant     	for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
89033dbf14aSCameron Grant 		d = devclass_get_softc(pcm_devclass, i);
891285648f9SCameron Grant 		if (!d)
892285648f9SCameron Grant 			continue;
89349c5e6e2SCameron Grant 		snd_mtxlock(d->lock);
894987e5972SCameron Grant 		dev = devclass_get_device(pcm_devclass, i);
895285648f9SCameron Grant 		sbuf_printf(s, "pcm%d: <%s> %s", i, device_get_desc(dev), d->status);
896285648f9SCameron Grant 		if (d->chancount > 0) {
897285648f9SCameron Grant 			pc = rc = vc = 0;
898285648f9SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
899285648f9SCameron Grant 				c = sce->channel;
900285648f9SCameron Grant 				if (c->direction == PCMDIR_PLAY) {
901285648f9SCameron Grant 					if (c->flags & CHN_F_VIRTUAL)
902285648f9SCameron Grant 						vc++;
903bf8ca271SCameron Grant 					else
904285648f9SCameron Grant 						pc++;
905285648f9SCameron Grant 				} else
906285648f9SCameron Grant 					rc++;
907bf8ca271SCameron Grant 			}
908285648f9SCameron Grant 			sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)\n", pc, rc, vc,
909285648f9SCameron Grant 					(d->flags & SD_F_SIMPLEX)? "" : " duplex",
910285648f9SCameron Grant #ifdef USING_DEVFS
911285648f9SCameron Grant 					(i == snd_unit)? " default" : ""
912285648f9SCameron Grant #else
913285648f9SCameron Grant 					""
914285648f9SCameron Grant #endif
915285648f9SCameron Grant 					);
916285648f9SCameron Grant #ifdef SNDSTAT_VERBOSE
917285648f9SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
918285648f9SCameron Grant 				c = sce->channel;
919285648f9SCameron Grant 				sbuf_printf(s, "\t%s[%s]: speed %d, format %08x, flags %08x",
920285648f9SCameron Grant 					c->parentchannel? c->parentchannel->name : "",
921285648f9SCameron Grant 					c->name, c->speed, c->format, c->flags);
922285648f9SCameron Grant 				if (c->pid != -1)
923285648f9SCameron Grant 					sbuf_printf(s, ", pid %d", c->pid);
924285648f9SCameron Grant 				sbuf_printf(s, "\n\t");
925285648f9SCameron Grant 				f = c->feeder;
926285648f9SCameron Grant 				while (f) {
927285648f9SCameron Grant 					sbuf_printf(s, "%s", f->class->name);
928285648f9SCameron Grant 					if (f->desc->type == FEEDER_FMT)
929285648f9SCameron Grant 						sbuf_printf(s, "(%08x <- %08x)", f->desc->out, f->desc->in);
930285648f9SCameron Grant 					if (f->desc->type == FEEDER_RATE)
931285648f9SCameron Grant 						sbuf_printf(s, "(%d <- %d)", FEEDER_GET(f, FEEDRATE_DST), FEEDER_GET(f, FEEDRATE_SRC));
932285648f9SCameron Grant 					if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
933285648f9SCameron Grant 						sbuf_printf(s, "(%08x)", f->desc->out);
934285648f9SCameron Grant 					if (f->source)
935285648f9SCameron Grant 						sbuf_printf(s, " <- ");
936285648f9SCameron Grant 					f = f->source;
937987e5972SCameron Grant 				}
938285648f9SCameron Grant 				sbuf_printf(s, "\n");
939285648f9SCameron Grant 			}
940285648f9SCameron Grant #endif
941285648f9SCameron Grant 		} else
942285648f9SCameron Grant 			sbuf_printf(s, " (mixer only)\n");
94349c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
944285648f9SCameron Grant     	}
945285648f9SCameron Grant 	sbuf_finish(s);
946285648f9SCameron Grant     	return sbuf_len(s);
947987e5972SCameron Grant }
948987e5972SCameron Grant 
949987e5972SCameron Grant static int
950285648f9SCameron Grant do_status(int action, struct uio *buf)
951987e5972SCameron Grant {
952285648f9SCameron Grant 	static struct sbuf s;
953285648f9SCameron Grant     	static int bufptr = 0;
954285648f9SCameron Grant 	static int status_open = 0;
955285648f9SCameron Grant     	int l, err;
956987e5972SCameron Grant 
957285648f9SCameron Grant 	switch(action) {
958285648f9SCameron Grant 	case 0: /* open */
959285648f9SCameron Grant 		if (status_open)
960285648f9SCameron Grant 			return EBUSY;
961d6479358SDag-Erling Smørgrav 		if (sbuf_new(&s, NULL, 4096, 0) == NULL)
962285648f9SCameron Grant 			return ENXIO;
963987e5972SCameron Grant 		bufptr = 0;
964285648f9SCameron Grant 		err = (status_init(&s) > 0)? 0 : ENOMEM;
965285648f9SCameron Grant 		if (!err)
966285648f9SCameron Grant 			status_open = 1;
967285648f9SCameron Grant 		return err;
968285648f9SCameron Grant 
969285648f9SCameron Grant 	case 1: /* close */
970285648f9SCameron Grant 		if (!status_open)
971285648f9SCameron Grant 			return EBADF;
972285648f9SCameron Grant 		sbuf_delete(&s);
973285648f9SCameron Grant 		status_open = 0;
974285648f9SCameron Grant 		return 0;
975285648f9SCameron Grant 
976285648f9SCameron Grant 	case 2:
977285648f9SCameron Grant 		if (!status_open)
978285648f9SCameron Grant 			return EBADF;
979285648f9SCameron Grant 	    	l = min(buf->uio_resid, sbuf_len(&s) - bufptr);
980285648f9SCameron Grant 		err = (l > 0)? uiomove(sbuf_data(&s) + bufptr, l, buf) : 0;
981285648f9SCameron Grant     		bufptr += l;
982285648f9SCameron Grant     		return err;
983285648f9SCameron Grant 
984285648f9SCameron Grant 	case 3:
985285648f9SCameron Grant 		return status_open;
986987e5972SCameron Grant 	}
987987e5972SCameron Grant 
988285648f9SCameron Grant 	return EBADF;
989987e5972SCameron Grant }
99033dbf14aSCameron Grant 
99133dbf14aSCameron Grant static int
99233dbf14aSCameron Grant sndpcm_modevent(module_t mod, int type, void *data)
99333dbf14aSCameron Grant {
99433dbf14aSCameron Grant 
99533dbf14aSCameron Grant 	switch (type) {
99633dbf14aSCameron Grant 	case MOD_LOAD:
99733dbf14aSCameron Grant 		break;
99833dbf14aSCameron Grant 	case MOD_UNLOAD:
999285648f9SCameron Grant 		if (do_status(3, NULL))
1000c9b53085SCameron Grant 			return EBUSY;
100133dbf14aSCameron Grant 		if (status_dev)
100233dbf14aSCameron Grant 			destroy_dev(status_dev);
100333dbf14aSCameron Grant 		status_dev = 0;
100433dbf14aSCameron Grant 		break;
100533dbf14aSCameron Grant 	default:
100633dbf14aSCameron Grant 		break;
100733dbf14aSCameron Grant 	}
100833dbf14aSCameron Grant 	return 0;
100933dbf14aSCameron Grant }
101033dbf14aSCameron Grant 
101133dbf14aSCameron Grant static moduledata_t sndpcm_mod = {
101233dbf14aSCameron Grant 	"snd_pcm",
101333dbf14aSCameron Grant 	sndpcm_modevent,
101433dbf14aSCameron Grant 	NULL
101533dbf14aSCameron Grant };
101633dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
101733dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER);
1018