xref: /freebsd/sys/dev/sound/pcm/sound.c (revision 5129159789cc9d7bc514e4546b88e3427695002d)
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$
28  */
29 
30 #include <dev/sound/pcm/sound.h>
31 
32 static int 	status_isopen = 0;
33 static int 	status_init(char *buf, int size);
34 static int 	status_read(struct uio *buf);
35 
36 static d_open_t sndopen;
37 static d_close_t sndclose;
38 static d_ioctl_t sndioctl;
39 static d_read_t sndread;
40 static d_write_t sndwrite;
41 static d_mmap_t sndmmap;
42 static d_poll_t sndpoll;
43 
44 #define CDEV_MAJOR 30
45 static 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:
63 each unit needs:
64 status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
65 dspW and audio are deprecated.
66 dsp needs min 64 channels, will give it 256
67 
68 minor = (unit << 12) + (dev << 8) + channel
69 currently minor = (channel << 8) + (unit << 4) + dev
70 
71 nomenclature:
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 
79 currently:
80 minor = (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 
89 static devclass_t pcm_devclass;
90 
91 static snddev_info *
92 gsd(int unit)
93 {
94 	return devclass_get_softc(pcm_devclass, unit);
95 }
96 
97 int
98 pcm_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 	chn_init(ch, devinfo, dir);
107 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount),
108 		 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount);
109 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount),
110 		 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount);
111 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount),
112 		 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount);
113 	/* XXX SND_DEV_NORESET? */
114 	d->chancount++;
115 	return 0;
116 }
117 
118 int
119 pcm_setstatus(device_t dev, char *str)
120 {
121     	snddev_info *d = device_get_softc(dev);
122 	strncpy(d->status, str, SND_STATUSLEN);
123 	return 0;
124 }
125 
126 u_int32_t
127 pcm_getflags(device_t dev)
128 {
129     	snddev_info *d = device_get_softc(dev);
130 	return d->flags;
131 }
132 
133 void
134 pcm_setflags(device_t dev, u_int32_t val)
135 {
136     	snddev_info *d = device_get_softc(dev);
137 	d->flags = val;
138 }
139 
140 void
141 pcm_setswap(device_t dev, pcm_swap_t *swap)
142 {
143     	snddev_info *d = device_get_softc(dev);
144 	d->swap = swap;
145 }
146 /* This is the generic init routine */
147 int
148 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
149 {
150     	int sz, unit = device_get_unit(dev);
151     	snddev_info *d = device_get_softc(dev);
152 
153     	if (!pcm_devclass) {
154     		pcm_devclass = device_get_devclass(dev);
155 		make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0),
156 			 UID_ROOT, GID_WHEEL, 0444, "sndstat");
157 	}
158 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
159 		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
160 	d->devinfo = devinfo;
161 	d->chancount = d->playcount = d->reccount = 0;
162     	sz = (numplay + numrec) * sizeof(pcm_channel *);
163     	d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
164     	if (!d->aplay) goto no;
165     	d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
166     	if (!d->arec) goto no;
167     	bzero(d->aplay, sz);
168     	bzero(d->arec, sz);
169 
170     	d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel),
171 					M_DEVBUF, M_NOWAIT);
172     	if (!d->play) goto no;
173     	d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel),
174 				       M_DEVBUF, M_NOWAIT);
175     	if (!d->rec) goto no;
176     	bzero(d->play, numplay * sizeof(pcm_channel));
177     	bzero(d->rec, numrec * sizeof(pcm_channel));
178 
179 	fkchan_setup(&d->fakechan);
180 	chn_init(&d->fakechan, NULL, 0);
181 	d->magic = MAGIC(unit); /* debugging... */
182 	d->swap = NULL;
183 
184     	return 0;
185 no:
186 	if (d->aplay) free(d->aplay, M_DEVBUF);
187 	if (d->play) free(d->play, M_DEVBUF);
188 	if (d->arec) free(d->arec, M_DEVBUF);
189 	if (d->rec) free(d->rec, M_DEVBUF);
190 	return ENXIO;
191 }
192 
193 /*
194  * a small utility function which, given a device number, returns
195  * a pointer to the associated snddev_info struct, and sets the unit
196  * number.
197  */
198 static snddev_info *
199 get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
200 {
201     	int u, d, c;
202 
203     	u = PCMUNIT(i_dev);
204     	d = PCMDEV(i_dev);
205     	c = PCMCHAN(i_dev);
206     	if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
207     	if (unit) *unit = u;
208     	if (dev) *dev = d;
209     	if (chan) *chan = c;
210     	if (u < 0) return NULL;
211 
212     	switch(d) {
213     	case SND_DEV_CTL:	/* /dev/mixer handled by pcm */
214     	case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
215     	case SND_DEV_DSP:
216     	case SND_DEV_DSP16:
217     	case SND_DEV_AUDIO:
218 		return gsd(u);
219 
220     	case SND_DEV_SEQ: /* XXX when enabled... */
221     	case SND_DEV_SEQ2:
222     	case SND_DEV_MIDIN:
223     	case SND_DEV_SNDPROC:	/* /dev/sndproc handled by pcm */
224     	default:
225 		printf("unsupported subdevice %d\n", d);
226 		return NULL;
227     	}
228 }
229 
230 static int
231 sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
232 {
233     	int dev, unit, chan;
234     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
235 
236     	DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
237 		unit, dev, flags, mode));
238 
239     	switch(dev) {
240     	case SND_DEV_STATUS:
241 		if (status_isopen) return EBUSY;
242 		status_isopen = 1;
243 		return 0;
244 
245     	case SND_DEV_CTL:
246 		return d? 0 : ENXIO;
247 
248     	case SND_DEV_AUDIO:
249     	case SND_DEV_DSP:
250     	case SND_DEV_DSP16:
251 	case SND_DEV_NORESET:
252 		return d? dsp_open(d, chan, flags, dev) : ENXIO;
253 
254     	default:
255     		return ENXIO;
256     	}
257 }
258 
259 static int
260 sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
261 {
262     	int dev, unit, chan;
263     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
264 
265     	DEB(printf("close snd%d subdev %d\n", unit, dev));
266 
267     	switch(dev) { /* only those for which close makes sense */
268     	case SND_DEV_STATUS:
269 		if (!status_isopen) return EBADF;
270 		status_isopen = 0;
271 		return 0;
272 
273     	case SND_DEV_CTL:
274 		return d? 0 : ENXIO;
275 
276     	case SND_DEV_AUDIO:
277     	case SND_DEV_DSP:
278     	case SND_DEV_DSP16:
279 		return d? dsp_close(d, chan, dev) : ENXIO;
280 
281     	default:
282 		return ENXIO;
283     	}
284 }
285 
286 static int
287 sndread(dev_t i_dev, struct uio *buf, int flag)
288 {
289     	int dev, unit, chan;
290     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
291     	DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
292 
293     	switch(dev) {
294     	case SND_DEV_STATUS:
295 		return status_isopen? status_read(buf) : EBADF;
296 
297     	case SND_DEV_AUDIO:
298     	case SND_DEV_DSP:
299     	case SND_DEV_DSP16:
300         	return d? dsp_read(d, chan, buf, flag) : EBADF;
301 
302     	default:
303     		return ENXIO;
304     	}
305 }
306 
307 static int
308 sndwrite(dev_t i_dev, struct uio *buf, int flag)
309 {
310     	int dev, unit, chan;
311     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
312 
313     	DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
314 
315     	switch(dev) {	/* only writeable devices */
316     	case SND_DEV_DSP:
317     	case SND_DEV_DSP16:
318     	case SND_DEV_AUDIO:
319 		return d? dsp_write(d, chan, buf, flag) : EBADF;
320 
321     	default:
322 		return EPERM; /* for non-writeable devices ; */
323     	}
324 }
325 
326 static int
327 sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
328 {
329     	int dev, chan;
330     	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
331 
332     	if (d == NULL) return ENXIO;
333 
334     	switch(dev) {
335     	case SND_DEV_CTL:
336 		return mixer_ioctl(d, cmd, arg);
337 
338     	case SND_DEV_AUDIO:
339     	case SND_DEV_DSP:
340     	case SND_DEV_DSP16:
341 		if (IOCGROUP(cmd) == 'M')
342 			return mixer_ioctl(d, cmd, arg);
343 		else
344 			return dsp_ioctl(d, chan, cmd, arg);
345 
346     	default:
347     		return ENXIO;
348     	}
349 }
350 
351 static int
352 sndpoll(dev_t i_dev, int events, struct proc *p)
353 {
354     	int dev, chan;
355     	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
356 
357 	DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
358 
359     	if (d == NULL) return ENXIO;
360 
361     	switch(dev) {
362     	case SND_DEV_AUDIO:
363     	case SND_DEV_DSP:
364     	case SND_DEV_DSP16:
365 		return dsp_poll(d, chan, events, p);
366 
367     	default:
368     		return (events &
369        		       (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
370     	}
371 }
372 
373 /*
374  * The mmap interface allows access to the play and read buffer,
375  * plus the device descriptor.
376  * The various blocks are accessible at the following offsets:
377  *
378  * 0x00000000 ( 0   ) : write buffer ;
379  * 0x01000000 (16 MB) : read buffer ;
380  * 0x02000000 (32 MB) : device descriptor (dangerous!)
381  *
382  * WARNING: the mmap routines assume memory areas are aligned. This
383  * is true (probably) for the dma buffers, but likely false for the
384  * device descriptor. As a consequence, we do not know where it is
385  * located in the requested area.
386  */
387 static int
388 sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
389 {
390     	int unit, dev, chan;
391     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
392 
393     	DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
394 		   d, dev, offset, nprot));
395 
396     	if (d == NULL || nprot & PROT_EXEC)	return -1; /* forbidden */
397 
398     	switch(dev) {
399     	case SND_DEV_AUDIO:
400     	case SND_DEV_DSP:
401     	case SND_DEV_DSP16:
402 		return dsp_mmap(d, chan, offset, nprot);
403 
404     	default:
405     		return -1;
406     	}
407 }
408 
409 static int
410 status_init(char *buf, int size)
411 {
412     	int             i;
413     	device_t	    dev;
414     	snddev_info     *d;
415 
416     	snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n"
417 		 "Installed devices:\n", __DATE__, __TIME__);
418 
419     	for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
420 		d = gsd(i);
421 		if (!d) continue;
422 		dev = devclass_get_device(pcm_devclass, i);
423         	if (1) snprintf(buf + strlen(buf), size - strlen(buf),
424 		            	"pcm%d: <%s> %s (%d/%d channels%s)\n",
425 		            	i, device_get_desc(dev), d->status,
426 		            	d->playcount, d->reccount,
427 			    	(!(d->flags & SD_F_SIMPLEX))? " duplex" : "");
428     	}
429     	return strlen(buf);
430 }
431 
432 static int
433 status_read(struct uio *buf)
434 {
435     	static char	status_buf[4096];
436     	static int 	bufptr = 0, buflen = 0;
437     	int l;
438 
439     	if (status_isopen == 1) {
440 		status_isopen++;
441 		bufptr = 0;
442 		buflen = status_init(status_buf, sizeof status_buf);
443     	}
444 
445     	l = min(buf->uio_resid, buflen - bufptr);
446     	bufptr += l;
447     	return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0;
448 }
449