xref: /freebsd/sys/dev/sound/pcm/sound.c (revision e4d5b2502d711e398d560170691177154de1ac74)
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>
31987e5972SCameron Grant 
32987e5972SCameron Grant static int 	status_isopen = 0;
33987e5972SCameron Grant static int 	status_init(char *buf, int size);
34987e5972SCameron Grant static int 	status_read(struct uio *buf);
35987e5972SCameron Grant 
36987e5972SCameron Grant static d_open_t sndopen;
37987e5972SCameron Grant static d_close_t sndclose;
38987e5972SCameron Grant static d_ioctl_t sndioctl;
39987e5972SCameron Grant static d_read_t sndread;
40987e5972SCameron Grant static d_write_t sndwrite;
41987e5972SCameron Grant static d_mmap_t sndmmap;
42987e5972SCameron Grant static d_poll_t sndpoll;
43987e5972SCameron Grant 
44987e5972SCameron Grant #define CDEV_MAJOR 30
45987e5972SCameron Grant static struct cdevsw snd_cdevsw = {
46987e5972SCameron Grant 	/* open */	sndopen,
47987e5972SCameron Grant 	/* close */	sndclose,
48987e5972SCameron Grant 	/* read */	sndread,
49987e5972SCameron Grant 	/* write */	sndwrite,
50987e5972SCameron Grant 	/* ioctl */	sndioctl,
51987e5972SCameron Grant 	/* poll */	sndpoll,
52987e5972SCameron Grant 	/* mmap */	sndmmap,
53987e5972SCameron Grant 	/* strategy */	nostrategy,
54987e5972SCameron Grant 	/* name */	"snd",
55987e5972SCameron Grant 	/* maj */	CDEV_MAJOR,
56987e5972SCameron Grant 	/* dump */	nodump,
57987e5972SCameron Grant 	/* psize */	nopsize,
58987e5972SCameron Grant 	/* flags */	0,
59987e5972SCameron Grant 	/* bmaj */	-1
60987e5972SCameron Grant };
61987e5972SCameron Grant 
62dd186369SCameron Grant /*
63dd186369SCameron Grant PROPOSAL:
64987e5972SCameron Grant each unit needs:
65987e5972SCameron Grant status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
66987e5972SCameron Grant dspW and audio are deprecated.
67987e5972SCameron Grant dsp needs min 64 channels, will give it 256
68987e5972SCameron Grant 
69dd186369SCameron Grant minor = (unit << 20) + (dev << 16) + channel
70dd186369SCameron Grant currently minor = (channel << 16) + (unit << 4) + dev
71987e5972SCameron Grant 
72987e5972SCameron Grant nomenclature:
73987e5972SCameron Grant 	/dev/pcmX/dsp.(0..255)
74987e5972SCameron Grant 	/dev/pcmX/dspW
75987e5972SCameron Grant 	/dev/pcmX/audio
76987e5972SCameron Grant 	/dev/pcmX/status
77987e5972SCameron Grant 	/dev/pcmX/mixer
78987e5972SCameron Grant 	[etc.]
79987e5972SCameron Grant */
80987e5972SCameron Grant 
81987e5972SCameron Grant #define PCMMINOR(x) (minor(x))
82a618cffeSCameron Grant #define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16)
83987e5972SCameron Grant #define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
84987e5972SCameron Grant #define PCMDEV(x)   (PCMMINOR(x) & 0x0000000f)
85dd186369SCameron Grant #define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
86987e5972SCameron Grant 
87987e5972SCameron Grant static devclass_t pcm_devclass;
88987e5972SCameron Grant 
89987e5972SCameron Grant static snddev_info *
90987e5972SCameron Grant gsd(int unit)
91987e5972SCameron Grant {
92987e5972SCameron Grant 	return devclass_get_softc(pcm_devclass, unit);
93987e5972SCameron Grant }
94987e5972SCameron Grant 
95987e5972SCameron Grant int
96987e5972SCameron Grant pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo)
97987e5972SCameron Grant {
987207eca6SCameron Grant     	int unit = device_get_unit(dev);
99987e5972SCameron Grant     	snddev_info *d = device_get_softc(dev);
100987e5972SCameron Grant 	pcm_channel *ch;
101987e5972SCameron Grant 
1029c326820SCameron Grant 	if (((dir == PCMDIR_PLAY)? d->play : d->rec) == NULL) {
1039c326820SCameron Grant 		device_printf(dev, "bad channel add (%s)\n",
1049c326820SCameron Grant 		              (dir == PCMDIR_PLAY)? "play" : "record");
1059c326820SCameron Grant 		return 1;
1069c326820SCameron Grant 	}
107bbb5bf3dSCameron Grant 	ch = (dir == PCMDIR_PLAY)? &d->play[d->playcount] : &d->rec[d->reccount];
108987e5972SCameron Grant 	*ch = *templ;
109e4d5b250SCameron Grant 	ch->parent = d;
110bbb5bf3dSCameron Grant 	if (chn_init(ch, devinfo, dir)) {
111bbb5bf3dSCameron Grant 		device_printf(dev, "chn_init() for %s:%d failed\n",
112bbb5bf3dSCameron Grant 		              (dir == PCMDIR_PLAY)? "play" : "record",
113bbb5bf3dSCameron Grant 			      (dir == PCMDIR_PLAY)? d->playcount : d->reccount);
114bbb5bf3dSCameron Grant 		return 1;
115bbb5bf3dSCameron Grant 	}
116bbb5bf3dSCameron Grant 	if (dir == PCMDIR_PLAY) d->playcount++; else d->reccount++;
1177207eca6SCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount),
1187207eca6SCameron Grant 		 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount);
1197207eca6SCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount),
1207207eca6SCameron Grant 		 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount);
1217207eca6SCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount),
1227207eca6SCameron Grant 		 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount);
1237207eca6SCameron Grant 	/* XXX SND_DEV_NORESET? */
124987e5972SCameron Grant 	d->chancount++;
125987e5972SCameron Grant 	return 0;
126987e5972SCameron Grant }
127987e5972SCameron Grant 
128987e5972SCameron Grant int
129987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
130987e5972SCameron Grant {
131987e5972SCameron Grant     	snddev_info *d = device_get_softc(dev);
132987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
133987e5972SCameron Grant 	return 0;
134987e5972SCameron Grant }
135987e5972SCameron Grant 
136987e5972SCameron Grant u_int32_t
137987e5972SCameron Grant pcm_getflags(device_t dev)
138987e5972SCameron Grant {
139987e5972SCameron Grant     	snddev_info *d = device_get_softc(dev);
140987e5972SCameron Grant 	return d->flags;
141987e5972SCameron Grant }
142987e5972SCameron Grant 
143987e5972SCameron Grant void
144987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
145987e5972SCameron Grant {
146987e5972SCameron Grant     	snddev_info *d = device_get_softc(dev);
147987e5972SCameron Grant 	d->flags = val;
148987e5972SCameron Grant }
149987e5972SCameron Grant 
15039004e69SCameron Grant void *
15139004e69SCameron Grant pcm_getdevinfo(device_t dev)
15239004e69SCameron Grant {
15339004e69SCameron Grant     	snddev_info *d = device_get_softc(dev);
15439004e69SCameron Grant 	return d->devinfo;
15539004e69SCameron Grant }
15639004e69SCameron Grant 
1570927bf43SCameron Grant void
1580927bf43SCameron Grant pcm_setswap(device_t dev, pcm_swap_t *swap)
1590927bf43SCameron Grant {
1600927bf43SCameron Grant     	snddev_info *d = device_get_softc(dev);
1610927bf43SCameron Grant 	d->swap = swap;
1620927bf43SCameron Grant }
163987e5972SCameron Grant /* This is the generic init routine */
164987e5972SCameron Grant int
165987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
166987e5972SCameron Grant {
167987e5972SCameron Grant     	int sz, unit = device_get_unit(dev);
168987e5972SCameron Grant     	snddev_info *d = device_get_softc(dev);
169987e5972SCameron Grant 
170987e5972SCameron Grant     	if (!pcm_devclass) {
171987e5972SCameron Grant     		pcm_devclass = device_get_devclass(dev);
1727207eca6SCameron Grant 		make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0),
173083279e4SSeigo Tanimura 			 UID_ROOT, GID_WHEEL, 0444, "sndstat");
174083279e4SSeigo Tanimura 	}
1757207eca6SCameron Grant 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
17611346231SSeigo Tanimura 		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
177833f7023SCameron Grant 
178e4d5b250SCameron Grant 	d->dev = dev;
179987e5972SCameron Grant 	d->devinfo = devinfo;
180987e5972SCameron Grant 	d->chancount = d->playcount = d->reccount = 0;
181987e5972SCameron Grant     	sz = (numplay + numrec) * sizeof(pcm_channel *);
182833f7023SCameron Grant 
183833f7023SCameron Grant 	if (sz > 0) {
184987e5972SCameron Grant 		d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
185987e5972SCameron Grant     		if (!d->aplay) goto no;
186833f7023SCameron Grant     		bzero(d->aplay, sz);
187833f7023SCameron Grant 
188987e5972SCameron Grant     		d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
189987e5972SCameron Grant     		if (!d->arec) goto no;
190987e5972SCameron Grant     		bzero(d->arec, sz);
191bd18f334SCameron Grant 
192bd18f334SCameron Grant     		sz = (numplay + numrec) * sizeof(int);
193bd18f334SCameron Grant 		d->ref = (int *)malloc(sz, M_DEVBUF, M_NOWAIT);
194bd18f334SCameron Grant     		if (!d->ref) goto no;
195bd18f334SCameron Grant     		bzero(d->ref, sz);
196833f7023SCameron Grant 	}
197987e5972SCameron Grant 
198833f7023SCameron Grant 	if (numplay > 0) {
199987e5972SCameron Grant     		d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel),
200987e5972SCameron Grant 						M_DEVBUF, M_NOWAIT);
201987e5972SCameron Grant     		if (!d->play) goto no;
202833f7023SCameron Grant     		bzero(d->play, numplay * sizeof(pcm_channel));
2039c326820SCameron Grant 	} else
2049c326820SCameron Grant 		d->play = NULL;
205833f7023SCameron Grant 
206833f7023SCameron Grant 	if (numrec > 0) {
207987e5972SCameron Grant 	  	d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel),
208987e5972SCameron Grant 				       	M_DEVBUF, M_NOWAIT);
209987e5972SCameron Grant     		if (!d->rec) goto no;
210987e5972SCameron Grant     		bzero(d->rec, numrec * sizeof(pcm_channel));
2119c326820SCameron Grant 	} else
2129c326820SCameron Grant 		d->rec = NULL;
213987e5972SCameron Grant 
21417dbf677SCameron Grant 	if (numplay == 0 || numrec == 0)
21517dbf677SCameron Grant 		d->flags |= SD_F_SIMPLEX;
21617dbf677SCameron Grant 
217987e5972SCameron Grant 	fkchan_setup(&d->fakechan);
218987e5972SCameron Grant 	chn_init(&d->fakechan, NULL, 0);
219987e5972SCameron Grant 	d->magic = MAGIC(unit); /* debugging... */
2200927bf43SCameron Grant 	d->swap = NULL;
221987e5972SCameron Grant 
222987e5972SCameron Grant     	return 0;
223987e5972SCameron Grant no:
224987e5972SCameron Grant 	if (d->aplay) free(d->aplay, M_DEVBUF);
225987e5972SCameron Grant 	if (d->play) free(d->play, M_DEVBUF);
226987e5972SCameron Grant 	if (d->arec) free(d->arec, M_DEVBUF);
227987e5972SCameron Grant 	if (d->rec) free(d->rec, M_DEVBUF);
228987e5972SCameron Grant 	return ENXIO;
229987e5972SCameron Grant }
230987e5972SCameron Grant 
231987e5972SCameron Grant /*
232987e5972SCameron Grant  * a small utility function which, given a device number, returns
233987e5972SCameron Grant  * a pointer to the associated snddev_info struct, and sets the unit
234987e5972SCameron Grant  * number.
235987e5972SCameron Grant  */
236987e5972SCameron Grant static snddev_info *
237987e5972SCameron Grant get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
238987e5972SCameron Grant {
239987e5972SCameron Grant     	int u, d, c;
240987e5972SCameron Grant 
241987e5972SCameron Grant     	u = PCMUNIT(i_dev);
242987e5972SCameron Grant     	d = PCMDEV(i_dev);
243987e5972SCameron Grant     	c = PCMCHAN(i_dev);
244987e5972SCameron Grant     	if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
245987e5972SCameron Grant     	if (unit) *unit = u;
246987e5972SCameron Grant     	if (dev) *dev = d;
247987e5972SCameron Grant     	if (chan) *chan = c;
248987e5972SCameron Grant     	if (u < 0) return NULL;
249987e5972SCameron Grant 
250987e5972SCameron Grant     	switch(d) {
251987e5972SCameron Grant     	case SND_DEV_CTL:	/* /dev/mixer handled by pcm */
252987e5972SCameron Grant     	case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
253987e5972SCameron Grant     	case SND_DEV_DSP:
254987e5972SCameron Grant     	case SND_DEV_DSP16:
255987e5972SCameron Grant     	case SND_DEV_AUDIO:
256987e5972SCameron Grant 		return gsd(u);
257987e5972SCameron Grant 
258987e5972SCameron Grant     	case SND_DEV_SEQ: /* XXX when enabled... */
259987e5972SCameron Grant     	case SND_DEV_SEQ2:
260987e5972SCameron Grant     	case SND_DEV_MIDIN:
261987e5972SCameron Grant     	case SND_DEV_SNDPROC:	/* /dev/sndproc handled by pcm */
262987e5972SCameron Grant     	default:
263987e5972SCameron Grant 		printf("unsupported subdevice %d\n", d);
264987e5972SCameron Grant 		return NULL;
265987e5972SCameron Grant     	}
266987e5972SCameron Grant }
267987e5972SCameron Grant 
268987e5972SCameron Grant static int
269987e5972SCameron Grant sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
270987e5972SCameron Grant {
271987e5972SCameron Grant     	int dev, unit, chan;
272987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
273987e5972SCameron Grant 
274987e5972SCameron Grant     	DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
275987e5972SCameron Grant 		unit, dev, flags, mode));
276987e5972SCameron Grant 
277987e5972SCameron Grant     	switch(dev) {
278987e5972SCameron Grant     	case SND_DEV_STATUS:
279987e5972SCameron Grant 		if (status_isopen) return EBUSY;
280987e5972SCameron Grant 		status_isopen = 1;
281987e5972SCameron Grant 		return 0;
282987e5972SCameron Grant 
283987e5972SCameron Grant     	case SND_DEV_CTL:
284987e5972SCameron Grant 		return d? 0 : ENXIO;
285987e5972SCameron Grant 
286987e5972SCameron Grant     	case SND_DEV_AUDIO:
287987e5972SCameron Grant     	case SND_DEV_DSP:
288987e5972SCameron Grant     	case SND_DEV_DSP16:
2895b78a734SCameron Grant 	case SND_DEV_NORESET:
290987e5972SCameron Grant 		return d? dsp_open(d, chan, flags, dev) : ENXIO;
291987e5972SCameron Grant 
292987e5972SCameron Grant     	default:
293987e5972SCameron Grant     		return ENXIO;
294987e5972SCameron Grant     	}
295987e5972SCameron Grant }
296987e5972SCameron Grant 
297987e5972SCameron Grant static int
298987e5972SCameron Grant sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
299987e5972SCameron Grant {
300987e5972SCameron Grant     	int dev, unit, chan;
301987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
302987e5972SCameron Grant 
303987e5972SCameron Grant     	DEB(printf("close snd%d subdev %d\n", unit, dev));
304987e5972SCameron Grant 
305987e5972SCameron Grant     	switch(dev) { /* only those for which close makes sense */
306987e5972SCameron Grant     	case SND_DEV_STATUS:
307987e5972SCameron Grant 		if (!status_isopen) return EBADF;
308987e5972SCameron Grant 		status_isopen = 0;
309987e5972SCameron Grant 		return 0;
310987e5972SCameron Grant 
311987e5972SCameron Grant     	case SND_DEV_CTL:
312987e5972SCameron Grant 		return d? 0 : ENXIO;
313987e5972SCameron Grant 
314987e5972SCameron Grant     	case SND_DEV_AUDIO:
315987e5972SCameron Grant     	case SND_DEV_DSP:
316987e5972SCameron Grant     	case SND_DEV_DSP16:
317987e5972SCameron Grant 		return d? dsp_close(d, chan, dev) : ENXIO;
318987e5972SCameron Grant 
319987e5972SCameron Grant     	default:
320987e5972SCameron Grant 		return ENXIO;
321987e5972SCameron Grant     	}
322987e5972SCameron Grant }
323987e5972SCameron Grant 
324987e5972SCameron Grant static int
325987e5972SCameron Grant sndread(dev_t i_dev, struct uio *buf, int flag)
326987e5972SCameron Grant {
327987e5972SCameron Grant     	int dev, unit, chan;
328987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
329987e5972SCameron Grant     	DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
330987e5972SCameron Grant 
331987e5972SCameron Grant     	switch(dev) {
332987e5972SCameron Grant     	case SND_DEV_STATUS:
333987e5972SCameron Grant 		return status_isopen? status_read(buf) : EBADF;
334987e5972SCameron Grant 
335987e5972SCameron Grant     	case SND_DEV_AUDIO:
336987e5972SCameron Grant     	case SND_DEV_DSP:
337987e5972SCameron Grant     	case SND_DEV_DSP16:
338987e5972SCameron Grant         	return d? dsp_read(d, chan, buf, flag) : EBADF;
339987e5972SCameron Grant 
340987e5972SCameron Grant     	default:
341987e5972SCameron Grant     		return ENXIO;
342987e5972SCameron Grant     	}
343987e5972SCameron Grant }
344987e5972SCameron Grant 
345987e5972SCameron Grant static int
346987e5972SCameron Grant sndwrite(dev_t i_dev, struct uio *buf, int flag)
347987e5972SCameron Grant {
348987e5972SCameron Grant     	int dev, unit, chan;
349987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
350987e5972SCameron Grant 
351987e5972SCameron Grant     	DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
352987e5972SCameron Grant 
353987e5972SCameron Grant     	switch(dev) {	/* only writeable devices */
354987e5972SCameron Grant     	case SND_DEV_DSP:
355987e5972SCameron Grant     	case SND_DEV_DSP16:
356987e5972SCameron Grant     	case SND_DEV_AUDIO:
357987e5972SCameron Grant 		return d? dsp_write(d, chan, buf, flag) : EBADF;
358987e5972SCameron Grant 
359987e5972SCameron Grant     	default:
360987e5972SCameron Grant 		return EPERM; /* for non-writeable devices ; */
361987e5972SCameron Grant     	}
362987e5972SCameron Grant }
363987e5972SCameron Grant 
364987e5972SCameron Grant static int
365987e5972SCameron Grant sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
366987e5972SCameron Grant {
367987e5972SCameron Grant     	int dev, chan;
368987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
369987e5972SCameron Grant 
370987e5972SCameron Grant     	if (d == NULL) return ENXIO;
371987e5972SCameron Grant 
372987e5972SCameron Grant     	switch(dev) {
373987e5972SCameron Grant     	case SND_DEV_CTL:
374987e5972SCameron Grant 		return mixer_ioctl(d, cmd, arg);
375987e5972SCameron Grant 
376987e5972SCameron Grant     	case SND_DEV_AUDIO:
377987e5972SCameron Grant     	case SND_DEV_DSP:
378987e5972SCameron Grant     	case SND_DEV_DSP16:
3791ad869dbSCameron Grant 		if (IOCGROUP(cmd) == 'M')
3801ad869dbSCameron Grant 			return mixer_ioctl(d, cmd, arg);
3811ad869dbSCameron Grant 		else
382987e5972SCameron Grant 			return dsp_ioctl(d, chan, cmd, arg);
383987e5972SCameron Grant 
384987e5972SCameron Grant     	default:
385987e5972SCameron Grant     		return ENXIO;
386987e5972SCameron Grant     	}
387987e5972SCameron Grant }
388987e5972SCameron Grant 
389987e5972SCameron Grant static int
390987e5972SCameron Grant sndpoll(dev_t i_dev, int events, struct proc *p)
391987e5972SCameron Grant {
392987e5972SCameron Grant     	int dev, chan;
393987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
394987e5972SCameron Grant 
3950e25481fSCameron Grant 	DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
396987e5972SCameron Grant 
397987e5972SCameron Grant     	if (d == NULL) return ENXIO;
398987e5972SCameron Grant 
399987e5972SCameron Grant     	switch(dev) {
400987e5972SCameron Grant     	case SND_DEV_AUDIO:
401987e5972SCameron Grant     	case SND_DEV_DSP:
402987e5972SCameron Grant     	case SND_DEV_DSP16:
403987e5972SCameron Grant 		return dsp_poll(d, chan, events, p);
404987e5972SCameron Grant 
405987e5972SCameron Grant     	default:
406987e5972SCameron Grant     		return (events &
407987e5972SCameron Grant        		       (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
408987e5972SCameron Grant     	}
409987e5972SCameron Grant }
410987e5972SCameron Grant 
411987e5972SCameron Grant /*
412987e5972SCameron Grant  * The mmap interface allows access to the play and read buffer,
413987e5972SCameron Grant  * plus the device descriptor.
414987e5972SCameron Grant  * The various blocks are accessible at the following offsets:
415987e5972SCameron Grant  *
416987e5972SCameron Grant  * 0x00000000 ( 0   ) : write buffer ;
417987e5972SCameron Grant  * 0x01000000 (16 MB) : read buffer ;
418987e5972SCameron Grant  * 0x02000000 (32 MB) : device descriptor (dangerous!)
419987e5972SCameron Grant  *
420987e5972SCameron Grant  * WARNING: the mmap routines assume memory areas are aligned. This
421987e5972SCameron Grant  * is true (probably) for the dma buffers, but likely false for the
422987e5972SCameron Grant  * device descriptor. As a consequence, we do not know where it is
423987e5972SCameron Grant  * located in the requested area.
424987e5972SCameron Grant  */
425987e5972SCameron Grant static int
426987e5972SCameron Grant sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
427987e5972SCameron Grant {
428987e5972SCameron Grant     	int unit, dev, chan;
429987e5972SCameron Grant     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
430987e5972SCameron Grant 
431987e5972SCameron Grant     	DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
432987e5972SCameron Grant 		   d, dev, offset, nprot));
433987e5972SCameron Grant 
434987e5972SCameron Grant     	if (d == NULL || nprot & PROT_EXEC)	return -1; /* forbidden */
435987e5972SCameron Grant 
436987e5972SCameron Grant     	switch(dev) {
437987e5972SCameron Grant     	case SND_DEV_AUDIO:
438987e5972SCameron Grant     	case SND_DEV_DSP:
439987e5972SCameron Grant     	case SND_DEV_DSP16:
440987e5972SCameron Grant 		return dsp_mmap(d, chan, offset, nprot);
441987e5972SCameron Grant 
442987e5972SCameron Grant     	default:
443987e5972SCameron Grant     		return -1;
444987e5972SCameron Grant     	}
445987e5972SCameron Grant }
446987e5972SCameron Grant 
447987e5972SCameron Grant static int
448987e5972SCameron Grant status_init(char *buf, int size)
449987e5972SCameron Grant {
450987e5972SCameron Grant     	int             i;
451987e5972SCameron Grant     	device_t	    dev;
452987e5972SCameron Grant     	snddev_info     *d;
453987e5972SCameron Grant 
454987e5972SCameron Grant     	snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n"
455987e5972SCameron Grant 		 "Installed devices:\n", __DATE__, __TIME__);
456987e5972SCameron Grant 
457987e5972SCameron Grant     	for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
458987e5972SCameron Grant 		d = gsd(i);
459987e5972SCameron Grant 		if (!d) continue;
460987e5972SCameron Grant 		dev = devclass_get_device(pcm_devclass, i);
461bf8ca271SCameron Grant         	if (1) {
462bf8ca271SCameron Grant 			snprintf(buf + strlen(buf), size - strlen(buf),
463bf8ca271SCameron Grant 		            	"pcm%d: <%s> %s",
464bf8ca271SCameron Grant 		            	i, device_get_desc(dev), d->status);
465bf8ca271SCameron Grant 			if (d->chancount > 0)
466bf8ca271SCameron Grant 				snprintf(buf + strlen(buf), size - strlen(buf),
467bf8ca271SCameron Grant 				" (%dp/%dr channels%s)\n",
468987e5972SCameron Grant 			    	d->playcount, d->reccount,
469987e5972SCameron Grant 			    	(!(d->flags & SD_F_SIMPLEX))? " duplex" : "");
470bf8ca271SCameron Grant 			else
471bf8ca271SCameron Grant 				snprintf(buf + strlen(buf), size - strlen(buf),
4729bc50208SCameron Grant 				" (mixer only)\n");
473bf8ca271SCameron Grant 		}
474987e5972SCameron Grant     	}
475987e5972SCameron Grant     	return strlen(buf);
476987e5972SCameron Grant }
477987e5972SCameron Grant 
478987e5972SCameron Grant static int
479987e5972SCameron Grant status_read(struct uio *buf)
480987e5972SCameron Grant {
481987e5972SCameron Grant     	static char	status_buf[4096];
482987e5972SCameron Grant     	static int 	bufptr = 0, buflen = 0;
483987e5972SCameron Grant     	int l;
484987e5972SCameron Grant 
485987e5972SCameron Grant     	if (status_isopen == 1) {
486987e5972SCameron Grant 		status_isopen++;
487987e5972SCameron Grant 		bufptr = 0;
488987e5972SCameron Grant 		buflen = status_init(status_buf, sizeof status_buf);
489987e5972SCameron Grant     	}
490987e5972SCameron Grant 
491987e5972SCameron Grant     	l = min(buf->uio_resid, buflen - bufptr);
492987e5972SCameron Grant     	bufptr += l;
493987e5972SCameron Grant     	return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0;
494987e5972SCameron Grant }
495