sound.c revision 65340
150724Scg/*
250724Scg * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
350724Scg * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
450724Scg * All rights reserved.
550724Scg *
650724Scg * Redistribution and use in source and binary forms, with or without
750724Scg * modification, are permitted provided that the following conditions
850724Scg * are met:
950724Scg * 1. Redistributions of source code must retain the above copyright
1050724Scg *    notice, this list of conditions and the following disclaimer.
1150724Scg * 2. Redistributions in binary form must reproduce the above copyright
1250724Scg *    notice, this list of conditions and the following disclaimer in the
1350724Scg *    documentation and/or other materials provided with the distribution.
1450724Scg *
1550724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1650724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1750724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1850724Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1950724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2050724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2150724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2250724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2350724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2450724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2550724Scg * SUCH DAMAGE.
2650724Scg *
2750733Speter * $FreeBSD: head/sys/dev/sound/pcm/sound.c 65340 2000-09-01 20:09:24Z cg $
2850724Scg */
2950724Scg
3053465Scg#include <dev/sound/pcm/sound.h>
3165207Scg#include <sys/sysctl.h>
3265207Scg#include "opt_devfs.h"
3350724Scg
3465340Scgstatic dev_t 	status_dev = 0;
3550724Scgstatic int 	status_isopen = 0;
3650724Scgstatic int 	status_init(char *buf, int size);
3750724Scgstatic int 	status_read(struct uio *buf);
3850724Scg
3950724Scgstatic d_open_t sndopen;
4050724Scgstatic d_close_t sndclose;
4150724Scgstatic d_ioctl_t sndioctl;
4250724Scgstatic d_read_t sndread;
4350724Scgstatic d_write_t sndwrite;
4450724Scgstatic d_mmap_t sndmmap;
4550724Scgstatic d_poll_t sndpoll;
4650724Scg
4750724Scg#define CDEV_MAJOR 30
4850724Scgstatic struct cdevsw snd_cdevsw = {
4950724Scg	/* open */	sndopen,
5050724Scg	/* close */	sndclose,
5150724Scg	/* read */	sndread,
5250724Scg	/* write */	sndwrite,
5350724Scg	/* ioctl */	sndioctl,
5450724Scg	/* poll */	sndpoll,
5550724Scg	/* mmap */	sndmmap,
5650724Scg	/* strategy */	nostrategy,
5750724Scg	/* name */	"snd",
5850724Scg	/* maj */	CDEV_MAJOR,
5950724Scg	/* dump */	nodump,
6050724Scg	/* psize */	nopsize,
6150724Scg	/* flags */	0,
6250724Scg	/* bmaj */	-1
6350724Scg};
6450724Scg
6559660Scg/*
6659660ScgPROPOSAL:
6750724Scgeach unit needs:
6850724Scgstatus, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
6950724ScgdspW and audio are deprecated.
7050724Scgdsp needs min 64 channels, will give it 256
7150724Scg
7259660Scgminor = (unit << 20) + (dev << 16) + channel
7359660Scgcurrently minor = (channel << 16) + (unit << 4) + dev
7450724Scg
7550724Scgnomenclature:
7650724Scg	/dev/pcmX/dsp.(0..255)
7750724Scg	/dev/pcmX/dspW
7850724Scg	/dev/pcmX/audio
7950724Scg	/dev/pcmX/status
8050724Scg	/dev/pcmX/mixer
8150724Scg	[etc.]
8250724Scg*/
8350724Scg
8450724Scg#define PCMMINOR(x) (minor(x))
8559665Scg#define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16)
8650724Scg#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
8750724Scg#define PCMDEV(x)   (PCMMINOR(x) & 0x0000000f)
8859660Scg#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
8950724Scg
9050724Scgstatic devclass_t pcm_devclass;
9165207Scg#ifdef DEVFS
9265207Scgint snd_unit;
9365207ScgTUNABLE_INT_DECL("hw.sndunit", 0, snd_unit);
9465207Scg#endif
9550724Scg
9665340Scg#ifdef DEVFS
9765340Scgstatic void
9865340Scgpcm_makelinks(void *dummy)
9950724Scg{
10065340Scg	int unit;
10165340Scg	dev_t pdev;
10265340Scg	static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0;
10365340Scg
10465340Scg	if (pcm_devclass == NULL)
10565340Scg		return;
10665340Scg	if (dsp) {
10765340Scg		destroy_dev(dsp);
10865340Scg		dsp = 0;
10965340Scg	}
11065340Scg	if (dspW) {
11165340Scg		destroy_dev(dspW);
11265340Scg		dspW = 0;
11365340Scg	}
11465340Scg	if (audio) {
11565340Scg		destroy_dev(audio);
11665340Scg		audio = 0;
11765340Scg	}
11865340Scg	if (mixer) {
11965340Scg		destroy_dev(mixer);
12065340Scg		mixer = 0;
12165340Scg	}
12265340Scg
12365340Scg	unit = snd_unit;
12465340Scg	if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass))
12565340Scg		return;
12665340Scg	if (devclass_get_softc(pcm_devclass, unit) == NULL)
12765340Scg		return;
12865340Scg
12965340Scg	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0));
13065340Scg	dsp = make_dev_alias(pdev, "dsp");
13165340Scg	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0));
13265340Scg	dspW = make_dev_alias(pdev, "dspW");
13365340Scg	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0));
13465340Scg	audio = make_dev_alias(pdev, "audio");
13565340Scg	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
13665340Scg	mixer = make_dev_alias(pdev, "mixer");
13750724Scg}
13850724Scg
13965340Scgstatic int
14065340Scgsysctl_hw_sndunit(SYSCTL_HANDLER_ARGS)
14165340Scg{
14265340Scg	int error, unit;
14365340Scg
14465340Scg	unit = snd_unit;
14565340Scg	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
14665340Scg	if (error == 0 && req->newptr != NULL) {
14765340Scg		snd_unit = unit;
14865340Scg		pcm_makelinks(NULL);
14965340Scg	}
15065340Scg	return (error);
15165340Scg}
15265340ScgSYSCTL_PROC(_hw, OID_AUTO, sndunit, CTLTYPE_INT | CTLFLAG_RW,
15365340Scg            0, sizeof(int), sysctl_hw_sndunit, "I", "");
15465340Scg#endif
15565340Scg
15650724Scgint
15750724Scgpcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo)
15850724Scg{
15965340Scg    	int unit = device_get_unit(dev), idx;
16050724Scg    	snddev_info *d = device_get_softc(dev);
16165340Scg	pcm_channel *chns, *ch;
16265340Scg	char *dirs;
16350724Scg
16465340Scg	dirs = ((dir == PCMDIR_PLAY)? "play" : "record");
16565340Scg	chns = ((dir == PCMDIR_PLAY)? d->play : d->rec);
16665340Scg	idx = ((dir == PCMDIR_PLAY)? d->playcount++ : d->reccount++);
16765340Scg
16865340Scg	if (chns == NULL) {
16965340Scg		device_printf(dev, "bad channel add (%s:%d)\n", dirs, idx);
17061344Scg		return 1;
17161344Scg	}
17265340Scg	ch = &chns[idx];
17350724Scg	*ch = *templ;
17461886Scg	ch->parent = d;
17555483Scg	if (chn_init(ch, devinfo, dir)) {
17665340Scg		device_printf(dev, "chn_init() for (%s:%d) failed\n", dirs, idx);
17755483Scg		return 1;
17855483Scg	}
17954826Scg	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount),
18054826Scg		 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount);
18165340Scg	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount),
18265340Scg		 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount);
18354826Scg	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount),
18454826Scg		 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount);
18554826Scg	/* XXX SND_DEV_NORESET? */
18650724Scg	d->chancount++;
18765340Scg#ifdef DEVFS
18865340Scg    	if (d->chancount == d->maxchans)
18965340Scg		pcm_makelinks(NULL);
19065340Scg#endif
19150724Scg	return 0;
19250724Scg}
19350724Scg
19465340Scgstatic int
19565340Scgpcm_killchan(device_t dev, int dir)
19665340Scg{
19765340Scg    	int unit = device_get_unit(dev), idx;
19865340Scg    	snddev_info *d = device_get_softc(dev);
19965340Scg	pcm_channel *chns, *ch;
20065340Scg	char *dirs;
20165340Scg	dev_t pdev;
20265340Scg
20365340Scg	dirs = ((dir == PCMDIR_PLAY)? "play" : "record");
20465340Scg	chns = ((dir == PCMDIR_PLAY)? d->play : d->rec);
20565340Scg	idx = ((dir == PCMDIR_PLAY)? --d->playcount : --d->reccount);
20665340Scg
20765340Scg	if (chns == NULL || idx < 0) {
20865340Scg		device_printf(dev, "bad channel kill (%s:%d)\n", dirs, idx);
20965340Scg		return 1;
21065340Scg	}
21165340Scg	ch = &chns[idx];
21265340Scg	if (chn_kill(ch)) {
21365340Scg		device_printf(dev, "chn_kill() for (%s:%d) failed\n", dirs, idx);
21465340Scg		return 1;
21565340Scg	}
21665340Scg	d->chancount--;
21765340Scg	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount));
21865340Scg	destroy_dev(pdev);
21965340Scg	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount));
22065340Scg	destroy_dev(pdev);
22165340Scg	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount));
22265340Scg	destroy_dev(pdev);
22365340Scg	return 0;
22465340Scg}
22565340Scg
22650724Scgint
22750724Scgpcm_setstatus(device_t dev, char *str)
22850724Scg{
22950724Scg    	snddev_info *d = device_get_softc(dev);
23050724Scg	strncpy(d->status, str, SND_STATUSLEN);
23150724Scg	return 0;
23250724Scg}
23350724Scg
23450724Scgu_int32_t
23550724Scgpcm_getflags(device_t dev)
23650724Scg{
23750724Scg    	snddev_info *d = device_get_softc(dev);
23850724Scg	return d->flags;
23950724Scg}
24050724Scg
24150724Scgvoid
24250724Scgpcm_setflags(device_t dev, u_int32_t val)
24350724Scg{
24450724Scg    	snddev_info *d = device_get_softc(dev);
24550724Scg	d->flags = val;
24650724Scg}
24750724Scg
24858384Scgvoid *
24958384Scgpcm_getdevinfo(device_t dev)
25058384Scg{
25158384Scg    	snddev_info *d = device_get_softc(dev);
25258384Scg	return d->devinfo;
25358384Scg}
25458384Scg
25554460Scgvoid
25654460Scgpcm_setswap(device_t dev, pcm_swap_t *swap)
25754460Scg{
25854460Scg    	snddev_info *d = device_get_softc(dev);
25954460Scg	d->swap = swap;
26054460Scg}
26165340Scg
26250724Scg/* This is the generic init routine */
26350724Scgint
26450724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec)
26550724Scg{
26650724Scg    	int sz, unit = device_get_unit(dev);
26750724Scg    	snddev_info *d = device_get_softc(dev);
26850724Scg
26950724Scg    	if (!pcm_devclass) {
27050724Scg    		pcm_devclass = device_get_devclass(dev);
27165340Scg		status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0),
27252755Stanimura			 UID_ROOT, GID_WHEEL, 0444, "sndstat");
27352823Stanimura	}
27454826Scg	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
27552823Stanimura		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
27661886Scg	d->dev = dev;
27750724Scg	d->devinfo = devinfo;
27850724Scg	d->chancount = d->playcount = d->reccount = 0;
27965340Scg	d->maxchans = numplay + numrec;
28050724Scg    	sz = (numplay + numrec) * sizeof(pcm_channel *);
28150724Scg
28255494Scg	if (sz > 0) {
28355494Scg		d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
28455494Scg    		if (!d->aplay) goto no;
28555494Scg    		bzero(d->aplay, sz);
28650724Scg
28755494Scg    		d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
28855494Scg    		if (!d->arec) goto no;
28955494Scg    		bzero(d->arec, sz);
29059021Scg
29159021Scg    		sz = (numplay + numrec) * sizeof(int);
29259021Scg		d->ref = (int *)malloc(sz, M_DEVBUF, M_NOWAIT);
29359021Scg    		if (!d->ref) goto no;
29459021Scg    		bzero(d->ref, sz);
29555494Scg	}
29655494Scg
29755494Scg	if (numplay > 0) {
29855494Scg    		d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel),
29955494Scg						M_DEVBUF, M_NOWAIT);
30055494Scg    		if (!d->play) goto no;
30155494Scg    		bzero(d->play, numplay * sizeof(pcm_channel));
30261344Scg	} else
30361344Scg		d->play = NULL;
30455494Scg
30555494Scg	if (numrec > 0) {
30655494Scg	  	d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel),
30755494Scg				       	M_DEVBUF, M_NOWAIT);
30855494Scg    		if (!d->rec) goto no;
30955494Scg    		bzero(d->rec, numrec * sizeof(pcm_channel));
31061344Scg	} else
31161344Scg		d->rec = NULL;
31255494Scg
31361144Scg	if (numplay == 0 || numrec == 0)
31461144Scg		d->flags |= SD_F_SIMPLEX;
31561144Scg
31650724Scg	fkchan_setup(&d->fakechan);
31750724Scg	chn_init(&d->fakechan, NULL, 0);
31850724Scg	d->magic = MAGIC(unit); /* debugging... */
31954460Scg	d->swap = NULL;
32050724Scg
32150724Scg    	return 0;
32250724Scgno:
32350724Scg	if (d->aplay) free(d->aplay, M_DEVBUF);
32450724Scg	if (d->play) free(d->play, M_DEVBUF);
32550724Scg	if (d->arec) free(d->arec, M_DEVBUF);
32650724Scg	if (d->rec) free(d->rec, M_DEVBUF);
32765340Scg	if (d->ref) free(d->ref, M_DEVBUF);
32850724Scg	return ENXIO;
32950724Scg}
33050724Scg
33165340Scgint
33265340Scgpcm_unregister(device_t dev)
33365207Scg{
33465340Scg    	int r, i, unit = device_get_unit(dev);
33565340Scg    	snddev_info *d = device_get_softc(dev);
33665207Scg	dev_t pdev;
33765207Scg
33865340Scg	r = 0;
33965340Scg	for (i = 0; i < d->chancount; i++)
34065340Scg		if (d->ref[i]) r = EBUSY;
34165340Scg	if (r) return r;
34265340Scg	if (mixer_isbusy(d) || status_isopen) return EBUSY;
34365207Scg
34465340Scg	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
34565340Scg	destroy_dev(pdev);
34665340Scg	mixer_uninit(dev);
34765207Scg
34865340Scg	while (d->playcount > 0)
34965340Scg		pcm_killchan(dev, PCMDIR_PLAY);
35065340Scg	while (d->reccount > 0)
35165340Scg		pcm_killchan(dev, PCMDIR_REC);
35265340Scg	d->magic = 0;
35365207Scg
35465340Scg	if (d->aplay) free(d->aplay, M_DEVBUF);
35565340Scg	if (d->play) free(d->play, M_DEVBUF);
35665340Scg	if (d->arec) free(d->arec, M_DEVBUF);
35765340Scg	if (d->rec) free(d->rec, M_DEVBUF);
35865340Scg	if (d->ref) free(d->ref, M_DEVBUF);
35965207Scg
36065340Scg#ifdef DEVFS
36165340Scg	pcm_makelinks(NULL);
36265340Scg#endif
36365340Scg	return 0;
36465207Scg}
36565207Scg
36650724Scg/*
36750724Scg * a small utility function which, given a device number, returns
36850724Scg * a pointer to the associated snddev_info struct, and sets the unit
36950724Scg * number.
37050724Scg */
37150724Scgstatic snddev_info *
37250724Scgget_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
37350724Scg{
37465340Scg	snddev_info *sc;
37550724Scg    	int u, d, c;
37650724Scg
37750724Scg    	u = PCMUNIT(i_dev);
37850724Scg    	d = PCMDEV(i_dev);
37950724Scg    	c = PCMCHAN(i_dev);
38050724Scg    	if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
38150724Scg    	if (unit) *unit = u;
38250724Scg    	if (dev) *dev = d;
38350724Scg    	if (chan) *chan = c;
38450724Scg    	if (u < 0) return NULL;
38550724Scg
38665340Scg	sc = devclass_get_softc(pcm_devclass, u);
38765340Scg	if (sc == NULL || sc->magic == 0) return NULL;
38865340Scg
38965340Scg	switch(d) {
39050724Scg    	case SND_DEV_CTL:	/* /dev/mixer handled by pcm */
39150724Scg    	case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
39250724Scg    	case SND_DEV_DSP:
39350724Scg    	case SND_DEV_DSP16:
39450724Scg    	case SND_DEV_AUDIO:
39565340Scg		return sc;
39650724Scg
39750724Scg    	case SND_DEV_SEQ: /* XXX when enabled... */
39850724Scg    	case SND_DEV_SEQ2:
39950724Scg    	case SND_DEV_MIDIN:
40050724Scg    	case SND_DEV_SNDPROC:	/* /dev/sndproc handled by pcm */
40150724Scg    	default:
40250724Scg		printf("unsupported subdevice %d\n", d);
40350724Scg		return NULL;
40450724Scg    	}
40550724Scg}
40650724Scg
40750724Scgstatic int
40850724Scgsndopen(dev_t i_dev, int flags, int mode, struct proc *p)
40950724Scg{
41050724Scg    	int dev, unit, chan;
41150724Scg    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
41250724Scg
41350724Scg    	DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
41450724Scg		unit, dev, flags, mode));
41550724Scg
41650724Scg    	switch(dev) {
41750724Scg    	case SND_DEV_STATUS:
41850724Scg		if (status_isopen) return EBUSY;
41950724Scg		status_isopen = 1;
42050724Scg		return 0;
42150724Scg
42250724Scg    	case SND_DEV_CTL:
42365340Scg		return d? mixer_busy(d, 1) : ENXIO;
42450724Scg
42550724Scg    	case SND_DEV_AUDIO:
42650724Scg    	case SND_DEV_DSP:
42750724Scg    	case SND_DEV_DSP16:
42851769Scg	case SND_DEV_NORESET:
42950724Scg		return d? dsp_open(d, chan, flags, dev) : ENXIO;
43050724Scg
43150724Scg    	default:
43250724Scg    		return ENXIO;
43350724Scg    	}
43450724Scg}
43550724Scg
43650724Scgstatic int
43750724Scgsndclose(dev_t i_dev, int flags, int mode, struct proc *p)
43850724Scg{
43950724Scg    	int dev, unit, chan;
44050724Scg    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
44150724Scg
44250724Scg    	DEB(printf("close snd%d subdev %d\n", unit, dev));
44350724Scg
44450724Scg    	switch(dev) { /* only those for which close makes sense */
44550724Scg    	case SND_DEV_STATUS:
44650724Scg		if (!status_isopen) return EBADF;
44750724Scg		status_isopen = 0;
44850724Scg		return 0;
44950724Scg
45050724Scg    	case SND_DEV_CTL:
45165340Scg		return d? mixer_busy(d, 0) : ENXIO;
45250724Scg
45350724Scg    	case SND_DEV_AUDIO:
45450724Scg    	case SND_DEV_DSP:
45550724Scg    	case SND_DEV_DSP16:
45650724Scg		return d? dsp_close(d, chan, dev) : ENXIO;
45750724Scg
45850724Scg    	default:
45950724Scg		return ENXIO;
46050724Scg    	}
46150724Scg}
46250724Scg
46350724Scgstatic int
46450724Scgsndread(dev_t i_dev, struct uio *buf, int flag)
46550724Scg{
46650724Scg    	int dev, unit, chan;
46750724Scg    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
46850724Scg    	DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
46950724Scg
47050724Scg    	switch(dev) {
47150724Scg    	case SND_DEV_STATUS:
47250724Scg		return status_isopen? status_read(buf) : EBADF;
47350724Scg
47450724Scg    	case SND_DEV_AUDIO:
47550724Scg    	case SND_DEV_DSP:
47650724Scg    	case SND_DEV_DSP16:
47750724Scg        	return d? dsp_read(d, chan, buf, flag) : EBADF;
47850724Scg
47950724Scg    	default:
48050724Scg    		return ENXIO;
48150724Scg    	}
48250724Scg}
48350724Scg
48450724Scgstatic int
48550724Scgsndwrite(dev_t i_dev, struct uio *buf, int flag)
48650724Scg{
48750724Scg    	int dev, unit, chan;
48850724Scg    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
48950724Scg
49050724Scg    	DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
49150724Scg
49250724Scg    	switch(dev) {	/* only writeable devices */
49350724Scg    	case SND_DEV_DSP:
49450724Scg    	case SND_DEV_DSP16:
49550724Scg    	case SND_DEV_AUDIO:
49650724Scg		return d? dsp_write(d, chan, buf, flag) : EBADF;
49750724Scg
49850724Scg    	default:
49950724Scg		return EPERM; /* for non-writeable devices ; */
50050724Scg    	}
50150724Scg}
50250724Scg
50350724Scgstatic int
50450724Scgsndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
50550724Scg{
50650724Scg    	int dev, chan;
50750724Scg    	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
50850724Scg
50950724Scg    	if (d == NULL) return ENXIO;
51050724Scg
51150724Scg    	switch(dev) {
51250724Scg    	case SND_DEV_CTL:
51350724Scg		return mixer_ioctl(d, cmd, arg);
51450724Scg
51550724Scg    	case SND_DEV_AUDIO:
51650724Scg    	case SND_DEV_DSP:
51750724Scg    	case SND_DEV_DSP16:
51854857Scg		if (IOCGROUP(cmd) == 'M')
51954857Scg			return mixer_ioctl(d, cmd, arg);
52054857Scg		else
52154857Scg			return dsp_ioctl(d, chan, cmd, arg);
52250724Scg
52350724Scg    	default:
52450724Scg    		return ENXIO;
52550724Scg    	}
52650724Scg}
52750724Scg
52850724Scgstatic int
52950724Scgsndpoll(dev_t i_dev, int events, struct proc *p)
53050724Scg{
53150724Scg    	int dev, chan;
53250724Scg    	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
53350724Scg
53454155Scg	DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
53550724Scg
53650724Scg    	if (d == NULL) return ENXIO;
53750724Scg
53850724Scg    	switch(dev) {
53950724Scg    	case SND_DEV_AUDIO:
54050724Scg    	case SND_DEV_DSP:
54150724Scg    	case SND_DEV_DSP16:
54250724Scg		return dsp_poll(d, chan, events, p);
54350724Scg
54450724Scg    	default:
54550724Scg    		return (events &
54650724Scg       		       (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
54750724Scg    	}
54850724Scg}
54950724Scg
55050724Scg/*
55150724Scg * The mmap interface allows access to the play and read buffer,
55250724Scg * plus the device descriptor.
55350724Scg * The various blocks are accessible at the following offsets:
55450724Scg *
55550724Scg * 0x00000000 ( 0   ) : write buffer ;
55650724Scg * 0x01000000 (16 MB) : read buffer ;
55750724Scg * 0x02000000 (32 MB) : device descriptor (dangerous!)
55850724Scg *
55950724Scg * WARNING: the mmap routines assume memory areas are aligned. This
56050724Scg * is true (probably) for the dma buffers, but likely false for the
56150724Scg * device descriptor. As a consequence, we do not know where it is
56250724Scg * located in the requested area.
56350724Scg */
56450724Scgstatic int
56550724Scgsndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
56650724Scg{
56750724Scg    	int unit, dev, chan;
56850724Scg    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
56950724Scg
57050724Scg    	DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
57150724Scg		   d, dev, offset, nprot));
57250724Scg
57350724Scg    	if (d == NULL || nprot & PROT_EXEC)	return -1; /* forbidden */
57450724Scg
57550724Scg    	switch(dev) {
57650724Scg    	case SND_DEV_AUDIO:
57750724Scg    	case SND_DEV_DSP:
57850724Scg    	case SND_DEV_DSP16:
57950724Scg		return dsp_mmap(d, chan, offset, nprot);
58050724Scg
58150724Scg    	default:
58250724Scg    		return -1;
58350724Scg    	}
58450724Scg}
58550724Scg
58650724Scgstatic int
58750724Scgstatus_init(char *buf, int size)
58850724Scg{
58950724Scg    	int             i;
59050724Scg    	device_t	    dev;
59150724Scg    	snddev_info     *d;
59250724Scg
59350724Scg    	snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n"
59450724Scg		 "Installed devices:\n", __DATE__, __TIME__);
59550724Scg
59650724Scg    	for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
59765340Scg		d = devclass_get_softc(pcm_devclass, i);
59850724Scg		if (!d) continue;
59950724Scg		dev = devclass_get_device(pcm_devclass, i);
60055638Scg        	if (1) {
60155638Scg			snprintf(buf + strlen(buf), size - strlen(buf),
60255638Scg		            	"pcm%d: <%s> %s",
60355638Scg		            	i, device_get_desc(dev), d->status);
60455638Scg			if (d->chancount > 0)
60555638Scg				snprintf(buf + strlen(buf), size - strlen(buf),
60655638Scg				" (%dp/%dr channels%s)\n",
60755638Scg			    	d->playcount, d->reccount,
60850724Scg			    	(!(d->flags & SD_F_SIMPLEX))? " duplex" : "");
60955638Scg			else
61055638Scg				snprintf(buf + strlen(buf), size - strlen(buf),
61156110Scg				" (mixer only)\n");
61255638Scg		}
61350724Scg    	}
61450724Scg    	return strlen(buf);
61550724Scg}
61650724Scg
61750724Scgstatic int
61850724Scgstatus_read(struct uio *buf)
61950724Scg{
62050724Scg    	static char	status_buf[4096];
62150724Scg    	static int 	bufptr = 0, buflen = 0;
62250724Scg    	int l;
62350724Scg
62450724Scg    	if (status_isopen == 1) {
62550724Scg		status_isopen++;
62650724Scg		bufptr = 0;
62750724Scg		buflen = status_init(status_buf, sizeof status_buf);
62850724Scg    	}
62950724Scg
63050724Scg    	l = min(buf->uio_resid, buflen - bufptr);
63150724Scg    	bufptr += l;
63250724Scg    	return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0;
63350724Scg}
63465340Scg
63565340Scgstatic int
63665340Scgsndpcm_modevent(module_t mod, int type, void *data)
63765340Scg{
63865340Scg
63965340Scg	switch (type) {
64065340Scg	case MOD_LOAD:
64165340Scg		break;
64265340Scg	case MOD_UNLOAD:
64365340Scg		if (status_dev)
64465340Scg			destroy_dev(status_dev);
64565340Scg		status_dev = 0;
64665340Scg		break;
64765340Scg	default:
64865340Scg		break;
64965340Scg	}
65065340Scg	return 0;
65165340Scg}
65265340Scg
65365340Scgstatic moduledata_t sndpcm_mod = {
65465340Scg	"snd_pcm",
65565340Scg	sndpcm_modevent,
65665340Scg	NULL
65765340Scg};
65865340ScgDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
65965340ScgMODULE_VERSION(snd_pcm, PCM_MODVER);
660