xref: /freebsd/sys/dev/sound/pcm/sound.c (revision bd18f3340844b7bf72499464ee51572b9a48c7e5)
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 	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 
124 int
125 pcm_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 
132 u_int32_t
133 pcm_getflags(device_t dev)
134 {
135     	snddev_info *d = device_get_softc(dev);
136 	return d->flags;
137 }
138 
139 void
140 pcm_setflags(device_t dev, u_int32_t val)
141 {
142     	snddev_info *d = device_get_softc(dev);
143 	d->flags = val;
144 }
145 
146 void *
147 pcm_getdevinfo(device_t dev)
148 {
149     	snddev_info *d = device_get_softc(dev);
150 	return d->devinfo;
151 }
152 
153 void
154 pcm_setswap(device_t dev, pcm_swap_t *swap)
155 {
156     	snddev_info *d = device_get_softc(dev);
157 	d->swap = swap;
158 }
159 /* This is the generic init routine */
160 int
161 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
162 {
163     	int sz, unit = device_get_unit(dev);
164     	snddev_info *d = device_get_softc(dev);
165 
166     	if (!pcm_devclass) {
167     		pcm_devclass = device_get_devclass(dev);
168 		make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0),
169 			 UID_ROOT, GID_WHEEL, 0444, "sndstat");
170 	}
171 	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
172 		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
173 
174 	d->devinfo = devinfo;
175 	d->chancount = d->playcount = d->reccount = 0;
176     	sz = (numplay + numrec) * sizeof(pcm_channel *);
177 
178 	if (sz > 0) {
179 		d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
180     		if (!d->aplay) goto no;
181     		bzero(d->aplay, sz);
182 
183     		d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
184     		if (!d->arec) goto no;
185     		bzero(d->arec, sz);
186 
187     		sz = (numplay + numrec) * sizeof(int);
188 		d->ref = (int *)malloc(sz, M_DEVBUF, M_NOWAIT);
189     		if (!d->ref) goto no;
190     		bzero(d->ref, sz);
191 	}
192 
193 	if (numplay > 0) {
194     		d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel),
195 						M_DEVBUF, M_NOWAIT);
196     		if (!d->play) goto no;
197     		bzero(d->play, numplay * sizeof(pcm_channel));
198 	}
199 
200 	if (numrec > 0) {
201 	  	d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel),
202 				       	M_DEVBUF, M_NOWAIT);
203     		if (!d->rec) goto no;
204     		bzero(d->rec, numrec * sizeof(pcm_channel));
205 	}
206 
207 	fkchan_setup(&d->fakechan);
208 	chn_init(&d->fakechan, NULL, 0);
209 	d->magic = MAGIC(unit); /* debugging... */
210 	d->swap = NULL;
211 
212     	return 0;
213 no:
214 	if (d->aplay) free(d->aplay, M_DEVBUF);
215 	if (d->play) free(d->play, M_DEVBUF);
216 	if (d->arec) free(d->arec, M_DEVBUF);
217 	if (d->rec) free(d->rec, M_DEVBUF);
218 	return ENXIO;
219 }
220 
221 /*
222  * a small utility function which, given a device number, returns
223  * a pointer to the associated snddev_info struct, and sets the unit
224  * number.
225  */
226 static snddev_info *
227 get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
228 {
229     	int u, d, c;
230 
231     	u = PCMUNIT(i_dev);
232     	d = PCMDEV(i_dev);
233     	c = PCMCHAN(i_dev);
234     	if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
235     	if (unit) *unit = u;
236     	if (dev) *dev = d;
237     	if (chan) *chan = c;
238     	if (u < 0) return NULL;
239 
240     	switch(d) {
241     	case SND_DEV_CTL:	/* /dev/mixer handled by pcm */
242     	case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
243     	case SND_DEV_DSP:
244     	case SND_DEV_DSP16:
245     	case SND_DEV_AUDIO:
246 		return gsd(u);
247 
248     	case SND_DEV_SEQ: /* XXX when enabled... */
249     	case SND_DEV_SEQ2:
250     	case SND_DEV_MIDIN:
251     	case SND_DEV_SNDPROC:	/* /dev/sndproc handled by pcm */
252     	default:
253 		printf("unsupported subdevice %d\n", d);
254 		return NULL;
255     	}
256 }
257 
258 static int
259 sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
260 {
261     	int dev, unit, chan;
262     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
263 
264     	DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
265 		unit, dev, flags, mode));
266 
267     	switch(dev) {
268     	case SND_DEV_STATUS:
269 		if (status_isopen) return EBUSY;
270 		status_isopen = 1;
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 	case SND_DEV_NORESET:
280 		return d? dsp_open(d, chan, flags, dev) : ENXIO;
281 
282     	default:
283     		return ENXIO;
284     	}
285 }
286 
287 static int
288 sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
289 {
290     	int dev, unit, chan;
291     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
292 
293     	DEB(printf("close snd%d subdev %d\n", unit, dev));
294 
295     	switch(dev) { /* only those for which close makes sense */
296     	case SND_DEV_STATUS:
297 		if (!status_isopen) return EBADF;
298 		status_isopen = 0;
299 		return 0;
300 
301     	case SND_DEV_CTL:
302 		return d? 0 : ENXIO;
303 
304     	case SND_DEV_AUDIO:
305     	case SND_DEV_DSP:
306     	case SND_DEV_DSP16:
307 		return d? dsp_close(d, chan, dev) : ENXIO;
308 
309     	default:
310 		return ENXIO;
311     	}
312 }
313 
314 static int
315 sndread(dev_t i_dev, struct uio *buf, int flag)
316 {
317     	int dev, unit, chan;
318     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
319     	DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
320 
321     	switch(dev) {
322     	case SND_DEV_STATUS:
323 		return status_isopen? status_read(buf) : EBADF;
324 
325     	case SND_DEV_AUDIO:
326     	case SND_DEV_DSP:
327     	case SND_DEV_DSP16:
328         	return d? dsp_read(d, chan, buf, flag) : EBADF;
329 
330     	default:
331     		return ENXIO;
332     	}
333 }
334 
335 static int
336 sndwrite(dev_t i_dev, struct uio *buf, int flag)
337 {
338     	int dev, unit, chan;
339     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
340 
341     	DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
342 
343     	switch(dev) {	/* only writeable devices */
344     	case SND_DEV_DSP:
345     	case SND_DEV_DSP16:
346     	case SND_DEV_AUDIO:
347 		return d? dsp_write(d, chan, buf, flag) : EBADF;
348 
349     	default:
350 		return EPERM; /* for non-writeable devices ; */
351     	}
352 }
353 
354 static int
355 sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
356 {
357     	int dev, chan;
358     	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
359 
360     	if (d == NULL) return ENXIO;
361 
362     	switch(dev) {
363     	case SND_DEV_CTL:
364 		return mixer_ioctl(d, cmd, arg);
365 
366     	case SND_DEV_AUDIO:
367     	case SND_DEV_DSP:
368     	case SND_DEV_DSP16:
369 		if (IOCGROUP(cmd) == 'M')
370 			return mixer_ioctl(d, cmd, arg);
371 		else
372 			return dsp_ioctl(d, chan, cmd, arg);
373 
374     	default:
375     		return ENXIO;
376     	}
377 }
378 
379 static int
380 sndpoll(dev_t i_dev, int events, struct proc *p)
381 {
382     	int dev, chan;
383     	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
384 
385 	DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
386 
387     	if (d == NULL) return ENXIO;
388 
389     	switch(dev) {
390     	case SND_DEV_AUDIO:
391     	case SND_DEV_DSP:
392     	case SND_DEV_DSP16:
393 		return dsp_poll(d, chan, events, p);
394 
395     	default:
396     		return (events &
397        		       (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
398     	}
399 }
400 
401 /*
402  * The mmap interface allows access to the play and read buffer,
403  * plus the device descriptor.
404  * The various blocks are accessible at the following offsets:
405  *
406  * 0x00000000 ( 0   ) : write buffer ;
407  * 0x01000000 (16 MB) : read buffer ;
408  * 0x02000000 (32 MB) : device descriptor (dangerous!)
409  *
410  * WARNING: the mmap routines assume memory areas are aligned. This
411  * is true (probably) for the dma buffers, but likely false for the
412  * device descriptor. As a consequence, we do not know where it is
413  * located in the requested area.
414  */
415 static int
416 sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
417 {
418     	int unit, dev, chan;
419     	snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
420 
421     	DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
422 		   d, dev, offset, nprot));
423 
424     	if (d == NULL || nprot & PROT_EXEC)	return -1; /* forbidden */
425 
426     	switch(dev) {
427     	case SND_DEV_AUDIO:
428     	case SND_DEV_DSP:
429     	case SND_DEV_DSP16:
430 		return dsp_mmap(d, chan, offset, nprot);
431 
432     	default:
433     		return -1;
434     	}
435 }
436 
437 static int
438 status_init(char *buf, int size)
439 {
440     	int             i;
441     	device_t	    dev;
442     	snddev_info     *d;
443 
444     	snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n"
445 		 "Installed devices:\n", __DATE__, __TIME__);
446 
447     	for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
448 		d = gsd(i);
449 		if (!d) continue;
450 		dev = devclass_get_device(pcm_devclass, i);
451         	if (1) {
452 			snprintf(buf + strlen(buf), size - strlen(buf),
453 		            	"pcm%d: <%s> %s",
454 		            	i, device_get_desc(dev), d->status);
455 			if (d->chancount > 0)
456 				snprintf(buf + strlen(buf), size - strlen(buf),
457 				" (%dp/%dr channels%s)\n",
458 			    	d->playcount, d->reccount,
459 			    	(!(d->flags & SD_F_SIMPLEX))? " duplex" : "");
460 			else
461 				snprintf(buf + strlen(buf), size - strlen(buf),
462 				" (mixer only)\n");
463 		}
464     	}
465     	return strlen(buf);
466 }
467 
468 static int
469 status_read(struct uio *buf)
470 {
471     	static char	status_buf[4096];
472     	static int 	bufptr = 0, buflen = 0;
473     	int l;
474 
475     	if (status_isopen == 1) {
476 		status_isopen++;
477 		bufptr = 0;
478 		buflen = status_init(status_buf, sizeof status_buf);
479     	}
480 
481     	l = min(buf->uio_resid, buflen - bufptr);
482     	bufptr += l;
483     	return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0;
484 }
485