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