sound.c revision 55483
1/*
2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3 * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/dev/sound/pcm/sound.c 55483 2000-01-05 20:44:41Z cg $
28 */
29
30#include <dev/sound/pcm/sound.h>
31
32static int 	status_isopen = 0;
33static int 	status_init(char *buf, int size);
34static int 	status_read(struct uio *buf);
35
36static d_open_t sndopen;
37static d_close_t sndclose;
38static d_ioctl_t sndioctl;
39static d_read_t sndread;
40static d_write_t sndwrite;
41static d_mmap_t sndmmap;
42static d_poll_t sndpoll;
43
44#define CDEV_MAJOR 30
45static struct cdevsw snd_cdevsw = {
46	/* open */	sndopen,
47	/* close */	sndclose,
48	/* read */	sndread,
49	/* write */	sndwrite,
50	/* ioctl */	sndioctl,
51	/* poll */	sndpoll,
52	/* mmap */	sndmmap,
53	/* strategy */	nostrategy,
54	/* name */	"snd",
55	/* maj */	CDEV_MAJOR,
56	/* dump */	nodump,
57	/* psize */	nopsize,
58	/* flags */	0,
59	/* bmaj */	-1
60};
61
62/* PROPOSAL:
63each unit needs:
64status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
65dspW and audio are deprecated.
66dsp needs min 64 channels, will give it 256
67
68minor = (unit << 12) + (dev << 8) + channel
69currently minor = (channel << 8) + (unit << 4) + dev
70
71nomenclature:
72	/dev/pcmX/dsp.(0..255)
73	/dev/pcmX/dspW
74	/dev/pcmX/audio
75	/dev/pcmX/status
76	/dev/pcmX/mixer
77	[etc.]
78
79currently:
80minor = (channel << 8) + (unit << 4) + dev
81*/
82
83#define PCMMINOR(x) (minor(x))
84#define PCMCHAN(x) ((PCMMINOR(x) & 0x0000ff00) >> 8)
85#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
86#define PCMDEV(x)   (PCMMINOR(x) & 0x0000000f)
87#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 8) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
88
89static devclass_t pcm_devclass;
90
91static snddev_info *
92gsd(int unit)
93{
94	return devclass_get_softc(pcm_devclass, unit);
95}
96
97int
98pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo)
99{
100    	int unit = device_get_unit(dev);
101    	snddev_info *d = device_get_softc(dev);
102	pcm_channel *ch;
103
104	ch = (dir == PCMDIR_PLAY)? &d->play[d->playcount] : &d->rec[d->reccount];
105	*ch = *templ;
106	if (chn_init(ch, devinfo, dir)) {
107		device_printf(dev, "chn_init() for %s:%d failed\n",
108		              (dir == PCMDIR_PLAY)? "play" : "record",
109			      (dir == PCMDIR_PLAY)? d->playcount : d->reccount);
110		return 1;
111	}
112	if (dir == PCMDIR_PLAY) d->playcount++; else d->reccount++;
113	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount),
114		 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount);
115	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount),
116		 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount);
117	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount),
118		 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount);
119	/* XXX SND_DEV_NORESET? */
120	d->chancount++;
121	return 0;
122}
123
124int
125pcm_setstatus(device_t dev, char *str)
126{
127    	snddev_info *d = device_get_softc(dev);
128	strncpy(d->status, str, SND_STATUSLEN);
129	return 0;
130}
131
132u_int32_t
133pcm_getflags(device_t dev)
134{
135    	snddev_info *d = device_get_softc(dev);
136	return d->flags;
137}
138
139void
140pcm_setflags(device_t dev, u_int32_t val)
141{
142    	snddev_info *d = device_get_softc(dev);
143	d->flags = val;
144}
145
146void
147pcm_setswap(device_t dev, pcm_swap_t *swap)
148{
149    	snddev_info *d = device_get_softc(dev);
150	d->swap = swap;
151}
152/* This is the generic init routine */
153int
154pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
155{
156    	int sz, unit = device_get_unit(dev);
157    	snddev_info *d = device_get_softc(dev);
158
159    	if (!pcm_devclass) {
160    		pcm_devclass = device_get_devclass(dev);
161		make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0),
162			 UID_ROOT, GID_WHEEL, 0444, "sndstat");
163	}
164	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
165		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
166	d->devinfo = devinfo;
167	d->chancount = d->playcount = d->reccount = 0;
168    	sz = (numplay + numrec) * sizeof(pcm_channel *);
169    	d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
170    	if (!d->aplay) goto no;
171    	d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
172    	if (!d->arec) goto no;
173    	bzero(d->aplay, sz);
174    	bzero(d->arec, sz);
175
176    	d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel),
177					M_DEVBUF, M_NOWAIT);
178    	if (!d->play) goto no;
179    	d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel),
180				       M_DEVBUF, M_NOWAIT);
181    	if (!d->rec) goto no;
182    	bzero(d->play, numplay * sizeof(pcm_channel));
183    	bzero(d->rec, numrec * sizeof(pcm_channel));
184
185	fkchan_setup(&d->fakechan);
186	chn_init(&d->fakechan, NULL, 0);
187	d->magic = MAGIC(unit); /* debugging... */
188	d->swap = NULL;
189
190    	return 0;
191no:
192	if (d->aplay) free(d->aplay, M_DEVBUF);
193	if (d->play) free(d->play, M_DEVBUF);
194	if (d->arec) free(d->arec, M_DEVBUF);
195	if (d->rec) free(d->rec, M_DEVBUF);
196	return ENXIO;
197}
198
199/*
200 * a small utility function which, given a device number, returns
201 * a pointer to the associated snddev_info struct, and sets the unit
202 * number.
203 */
204static snddev_info *
205get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
206{
207    	int u, d, c;
208
209    	u = PCMUNIT(i_dev);
210    	d = PCMDEV(i_dev);
211    	c = PCMCHAN(i_dev);
212    	if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
213    	if (unit) *unit = u;
214    	if (dev) *dev = d;
215    	if (chan) *chan = c;
216    	if (u < 0) return NULL;
217
218    	switch(d) {
219    	case SND_DEV_CTL:	/* /dev/mixer handled by pcm */
220    	case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
221    	case SND_DEV_DSP:
222    	case SND_DEV_DSP16:
223    	case SND_DEV_AUDIO:
224		return gsd(u);
225
226    	case SND_DEV_SEQ: /* XXX when enabled... */
227    	case SND_DEV_SEQ2:
228    	case SND_DEV_MIDIN:
229    	case SND_DEV_SNDPROC:	/* /dev/sndproc handled by pcm */
230    	default:
231		printf("unsupported subdevice %d\n", d);
232		return NULL;
233    	}
234}
235
236static int
237sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
238{
239    	int dev, unit, chan;
240    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
241
242    	DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
243		unit, dev, flags, mode));
244
245    	switch(dev) {
246    	case SND_DEV_STATUS:
247		if (status_isopen) return EBUSY;
248		status_isopen = 1;
249		return 0;
250
251    	case SND_DEV_CTL:
252		return d? 0 : ENXIO;
253
254    	case SND_DEV_AUDIO:
255    	case SND_DEV_DSP:
256    	case SND_DEV_DSP16:
257	case SND_DEV_NORESET:
258		return d? dsp_open(d, chan, flags, dev) : ENXIO;
259
260    	default:
261    		return ENXIO;
262    	}
263}
264
265static int
266sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
267{
268    	int dev, unit, chan;
269    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
270
271    	DEB(printf("close snd%d subdev %d\n", unit, dev));
272
273    	switch(dev) { /* only those for which close makes sense */
274    	case SND_DEV_STATUS:
275		if (!status_isopen) return EBADF;
276		status_isopen = 0;
277		return 0;
278
279    	case SND_DEV_CTL:
280		return d? 0 : ENXIO;
281
282    	case SND_DEV_AUDIO:
283    	case SND_DEV_DSP:
284    	case SND_DEV_DSP16:
285		return d? dsp_close(d, chan, dev) : ENXIO;
286
287    	default:
288		return ENXIO;
289    	}
290}
291
292static int
293sndread(dev_t i_dev, struct uio *buf, int flag)
294{
295    	int dev, unit, chan;
296    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
297    	DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
298
299    	switch(dev) {
300    	case SND_DEV_STATUS:
301		return status_isopen? status_read(buf) : EBADF;
302
303    	case SND_DEV_AUDIO:
304    	case SND_DEV_DSP:
305    	case SND_DEV_DSP16:
306        	return d? dsp_read(d, chan, buf, flag) : EBADF;
307
308    	default:
309    		return ENXIO;
310    	}
311}
312
313static int
314sndwrite(dev_t i_dev, struct uio *buf, int flag)
315{
316    	int dev, unit, chan;
317    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
318
319    	DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
320
321    	switch(dev) {	/* only writeable devices */
322    	case SND_DEV_DSP:
323    	case SND_DEV_DSP16:
324    	case SND_DEV_AUDIO:
325		return d? dsp_write(d, chan, buf, flag) : EBADF;
326
327    	default:
328		return EPERM; /* for non-writeable devices ; */
329    	}
330}
331
332static int
333sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
334{
335    	int dev, chan;
336    	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
337
338    	if (d == NULL) return ENXIO;
339
340    	switch(dev) {
341    	case SND_DEV_CTL:
342		return mixer_ioctl(d, cmd, arg);
343
344    	case SND_DEV_AUDIO:
345    	case SND_DEV_DSP:
346    	case SND_DEV_DSP16:
347		if (IOCGROUP(cmd) == 'M')
348			return mixer_ioctl(d, cmd, arg);
349		else
350			return dsp_ioctl(d, chan, cmd, arg);
351
352    	default:
353    		return ENXIO;
354    	}
355}
356
357static int
358sndpoll(dev_t i_dev, int events, struct proc *p)
359{
360    	int dev, chan;
361    	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
362
363	DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
364
365    	if (d == NULL) return ENXIO;
366
367    	switch(dev) {
368    	case SND_DEV_AUDIO:
369    	case SND_DEV_DSP:
370    	case SND_DEV_DSP16:
371		return dsp_poll(d, chan, events, p);
372
373    	default:
374    		return (events &
375       		       (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
376    	}
377}
378
379/*
380 * The mmap interface allows access to the play and read buffer,
381 * plus the device descriptor.
382 * The various blocks are accessible at the following offsets:
383 *
384 * 0x00000000 ( 0   ) : write buffer ;
385 * 0x01000000 (16 MB) : read buffer ;
386 * 0x02000000 (32 MB) : device descriptor (dangerous!)
387 *
388 * WARNING: the mmap routines assume memory areas are aligned. This
389 * is true (probably) for the dma buffers, but likely false for the
390 * device descriptor. As a consequence, we do not know where it is
391 * located in the requested area.
392 */
393static int
394sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
395{
396    	int unit, dev, chan;
397    	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
398
399    	DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
400		   d, dev, offset, nprot));
401
402    	if (d == NULL || nprot & PROT_EXEC)	return -1; /* forbidden */
403
404    	switch(dev) {
405    	case SND_DEV_AUDIO:
406    	case SND_DEV_DSP:
407    	case SND_DEV_DSP16:
408		return dsp_mmap(d, chan, offset, nprot);
409
410    	default:
411    		return -1;
412    	}
413}
414
415static int
416status_init(char *buf, int size)
417{
418    	int             i;
419    	device_t	    dev;
420    	snddev_info     *d;
421
422    	snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n"
423		 "Installed devices:\n", __DATE__, __TIME__);
424
425    	for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
426		d = gsd(i);
427		if (!d) continue;
428		dev = devclass_get_device(pcm_devclass, i);
429        	if (1) snprintf(buf + strlen(buf), size - strlen(buf),
430		            	"pcm%d: <%s> %s (%d/%d channels%s)\n",
431		            	i, device_get_desc(dev), d->status,
432		            	d->playcount, d->reccount,
433			    	(!(d->flags & SD_F_SIMPLEX))? " duplex" : "");
434    	}
435    	return strlen(buf);
436}
437
438static int
439status_read(struct uio *buf)
440{
441    	static char	status_buf[4096];
442    	static int 	bufptr = 0, buflen = 0;
443    	int l;
444
445    	if (status_isopen == 1) {
446		status_isopen++;
447		bufptr = 0;
448		buflen = status_init(status_buf, sizeof status_buf);
449    	}
450
451    	l = min(buf->uio_resid, buflen - bufptr);
452    	bufptr += l;
453    	return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0;
454}
455