sound.c revision 74763
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 74763 2001-03-24 23:10:29Z cg $
28 */
29
30#include <dev/sound/pcm/sound.h>
31#include <sys/sysctl.h>
32
33static dev_t 	status_dev = 0;
34static int 	status_isopen = 0;
35static int 	status_init(char *buf, int size);
36static int 	status_read(struct uio *buf);
37
38static d_open_t sndopen;
39static d_close_t sndclose;
40static d_ioctl_t sndioctl;
41static d_read_t sndread;
42static d_write_t sndwrite;
43static d_mmap_t sndmmap;
44static d_poll_t sndpoll;
45
46#define CDEV_MAJOR 30
47static 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 */	D_TRACKCLOSE,
61	/* bmaj */	-1
62};
63
64/*
65PROPOSAL:
66each unit needs:
67status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
68dspW and audio are deprecated.
69dsp needs min 64 channels, will give it 256
70
71minor = (unit << 20) + (dev << 16) + channel
72currently minor = (channel << 16) + (unit << 4) + dev
73
74nomenclature:
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
89static devclass_t pcm_devclass;
90
91#ifdef USING_DEVFS
92int snd_unit;
93TUNABLE_INT_DECL("hw.snd.unit", 0, snd_unit);
94#endif
95
96SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
97
98void *
99snd_mtxcreate(const char *desc)
100{
101#ifdef USING_MUTEX
102	struct mtx *m;
103
104	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
105	if (m == NULL)
106		return NULL;
107	mtx_init(m, desc, MTX_RECURSE);
108	return m;
109#else
110	return (void *)0xcafebabe;
111#endif
112}
113
114void
115snd_mtxfree(void *m)
116{
117#ifdef USING_MUTEX
118	struct mtx *mtx = m;
119
120	mtx_assert(mtx, MA_OWNED);
121	mtx_destroy(mtx);
122	free(mtx, M_DEVBUF);
123#endif
124}
125
126void
127snd_mtxassert(void *m)
128{
129#ifdef USING_MUTEX
130	struct mtx *mtx = m;
131
132	mtx_assert(mtx, MA_OWNED);
133#endif
134}
135
136void
137snd_mtxlock(void *m)
138{
139#ifdef USING_MUTEX
140	struct mtx *mtx = m;
141
142	mtx_lock(mtx);
143#endif
144}
145
146void
147snd_mtxunlock(void *m)
148{
149#ifdef USING_MUTEX
150	struct mtx *mtx = m;
151
152	mtx_unlock(mtx);
153#endif
154}
155
156int
157snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
158{
159#ifdef USING_MUTEX
160	flags &= INTR_MPSAFE;
161	flags |= INTR_TYPE_TTY;
162#else
163	flags = INTR_TYPE_TTY;
164#endif
165	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
166}
167
168#ifdef USING_DEVFS
169static void
170pcm_makelinks(void *dummy)
171{
172	int unit;
173	dev_t pdev;
174	static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0;
175
176	if (pcm_devclass == NULL || devfs_present == 0)
177		return;
178	if (dsp) {
179		destroy_dev(dsp);
180		dsp = 0;
181	}
182	if (dspW) {
183		destroy_dev(dspW);
184		dspW = 0;
185	}
186	if (audio) {
187		destroy_dev(audio);
188		audio = 0;
189	}
190	if (mixer) {
191		destroy_dev(mixer);
192		mixer = 0;
193	}
194
195	unit = snd_unit;
196	if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass))
197		return;
198	if (devclass_get_softc(pcm_devclass, unit) == NULL)
199		return;
200
201	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0));
202	dsp = make_dev_alias(pdev, "dsp");
203	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0));
204	dspW = make_dev_alias(pdev, "dspW");
205	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0));
206	audio = make_dev_alias(pdev, "audio");
207	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
208	mixer = make_dev_alias(pdev, "mixer");
209}
210
211static int
212sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS)
213{
214	int error, unit;
215
216	unit = snd_unit;
217	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
218	if (error == 0 && req->newptr != NULL) {
219		snd_unit = unit;
220		pcm_makelinks(NULL);
221	}
222	return (error);
223}
224SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
225            0, sizeof(int), sysctl_hw_sndunit, "I", "");
226#endif
227
228int
229pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
230{
231    	int unit = device_get_unit(dev), idx;
232    	struct snddev_info *d = device_get_softc(dev);
233	struct pcm_channel *chns, *ch;
234	char *dirs;
235	int err;
236
237	dirs = ((dir == PCMDIR_PLAY)? "play" : "record");
238	chns = ((dir == PCMDIR_PLAY)? d->play : d->rec);
239	idx = ((dir == PCMDIR_PLAY)? d->playcount++ : d->reccount++);
240
241	if (chns == NULL) {
242		device_printf(dev, "bad channel add (%s:%d)\n", dirs, idx);
243		return 1;
244	}
245	ch = &chns[idx];
246	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
247	ch->parent = d;
248	snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(dev), dirs, idx);
249	err = chn_init(ch, devinfo, dir);
250	if (err) {
251		device_printf(dev, "chn_init() for (%s:%d) failed: err = %d\n", dirs, idx, err);
252		return 1;
253	}
254	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount),
255		 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount);
256	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount),
257		 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount);
258	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount),
259		 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount);
260	/* XXX SND_DEV_NORESET? */
261	d->chancount++;
262#ifdef USING_DEVFS
263    	if (d->chancount == 1)
264		pcm_makelinks(NULL);
265#endif
266	return 0;
267}
268
269static int
270pcm_killchan(device_t dev, int dir)
271{
272    	int unit = device_get_unit(dev), idx;
273    	struct snddev_info *d = device_get_softc(dev);
274	struct pcm_channel *chns, *ch;
275	char *dirs;
276	dev_t pdev;
277
278	dirs = ((dir == PCMDIR_PLAY)? "play" : "record");
279	chns = ((dir == PCMDIR_PLAY)? d->play : d->rec);
280	idx = ((dir == PCMDIR_PLAY)? --d->playcount : --d->reccount);
281
282	if (chns == NULL || idx < 0) {
283		device_printf(dev, "bad channel kill (%s:%d)\n", dirs, idx);
284		return 1;
285	}
286	ch = &chns[idx];
287	if (chn_kill(ch)) {
288		device_printf(dev, "chn_kill() for (%s:%d) failed\n", dirs, idx);
289		return 1;
290	}
291	kobj_delete(ch->methods, M_DEVBUF);
292	ch->methods = NULL;
293	d->chancount--;
294	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount));
295	destroy_dev(pdev);
296	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount));
297	destroy_dev(pdev);
298	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount));
299	destroy_dev(pdev);
300	return 0;
301}
302
303int
304pcm_setstatus(device_t dev, char *str)
305{
306    	struct snddev_info *d = device_get_softc(dev);
307	strncpy(d->status, str, SND_STATUSLEN);
308	return 0;
309}
310
311u_int32_t
312pcm_getflags(device_t dev)
313{
314    	struct snddev_info *d = device_get_softc(dev);
315	return d->flags;
316}
317
318void
319pcm_setflags(device_t dev, u_int32_t val)
320{
321    	struct snddev_info *d = device_get_softc(dev);
322	d->flags = val;
323}
324
325void *
326pcm_getdevinfo(device_t dev)
327{
328    	struct snddev_info *d = device_get_softc(dev);
329	return d->devinfo;
330}
331
332/* This is the generic init routine */
333int
334pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
335{
336    	int sz, unit = device_get_unit(dev);
337    	struct snddev_info *d = device_get_softc(dev);
338
339    	if (!pcm_devclass) {
340    		pcm_devclass = device_get_devclass(dev);
341		status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0),
342			 UID_ROOT, GID_WHEEL, 0444, "sndstat");
343	}
344	make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
345		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
346	d->dev = dev;
347	d->devinfo = devinfo;
348	d->chancount = d->playcount = d->reccount = 0;
349	d->maxchans = numplay + numrec;
350    	sz = (numplay + numrec) * sizeof(struct pcm_channel *);
351
352	if (sz > 0) {
353		d->aplay = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
354    		if (!d->aplay) goto no;
355    		bzero(d->aplay, sz);
356
357    		d->arec = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
358    		if (!d->arec) goto no;
359    		bzero(d->arec, sz);
360
361    		sz = (numplay + numrec) * sizeof(int);
362		d->ref = (int *)malloc(sz, M_DEVBUF, M_NOWAIT);
363    		if (!d->ref) goto no;
364    		bzero(d->ref, sz);
365	}
366
367	if (numplay > 0) {
368    		d->play = (struct pcm_channel *)malloc(numplay * sizeof(struct pcm_channel),
369						M_DEVBUF, M_NOWAIT);
370    		if (!d->play) goto no;
371    		bzero(d->play, numplay * sizeof(struct pcm_channel));
372	} else
373		d->play = NULL;
374
375	if (numrec > 0) {
376	  	d->rec = (struct pcm_channel *)malloc(numrec * sizeof(struct pcm_channel),
377				       	M_DEVBUF, M_NOWAIT);
378    		if (!d->rec) goto no;
379    		bzero(d->rec, numrec * sizeof(struct pcm_channel));
380	} else
381		d->rec = NULL;
382
383#ifdef SND_DYNSYSCTL
384	sysctl_ctx_init(&d->sysctl_tree);
385	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
386				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
387				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
388	if (d->sysctl_tree_top == NULL) {
389		sysctl_ctx_free(&d->sysctl_tree);
390		goto no;
391	}
392#endif
393
394	if (numplay == 0 || numrec == 0)
395		d->flags |= SD_F_SIMPLEX;
396
397	d->fakechan = fkchan_setup(dev);
398	chn_init(d->fakechan, NULL, 0);
399
400    	return 0;
401no:
402	if (d->aplay) free(d->aplay, M_DEVBUF);
403	if (d->play) free(d->play, M_DEVBUF);
404	if (d->arec) free(d->arec, M_DEVBUF);
405	if (d->rec) free(d->rec, M_DEVBUF);
406	if (d->ref) free(d->ref, M_DEVBUF);
407	return ENXIO;
408}
409
410int
411pcm_unregister(device_t dev)
412{
413    	int r, i, unit = device_get_unit(dev);
414    	struct snddev_info *d = device_get_softc(dev);
415	dev_t pdev;
416
417	r = 0;
418	for (i = 0; i < d->chancount; i++)
419		if (d->ref[i]) r = EBUSY;
420	if (r) {
421		device_printf(dev, "unregister: channel busy");
422		return r;
423	}
424	if (mixer_isbusy(d->mixer)) {
425		device_printf(dev, "unregister: mixer busy");
426		return EBUSY;
427	}
428
429#ifdef SND_DYNSYSCTL
430	d->sysctl_tree_top = NULL;
431	sysctl_ctx_free(&d->sysctl_tree);
432#endif
433
434	pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
435	destroy_dev(pdev);
436	mixer_uninit(dev);
437
438	while (d->playcount > 0)
439		pcm_killchan(dev, PCMDIR_PLAY);
440	while (d->reccount > 0)
441		pcm_killchan(dev, PCMDIR_REC);
442
443	if (d->aplay) free(d->aplay, M_DEVBUF);
444	if (d->play) free(d->play, M_DEVBUF);
445	if (d->arec) free(d->arec, M_DEVBUF);
446	if (d->rec) free(d->rec, M_DEVBUF);
447	if (d->ref) free(d->ref, M_DEVBUF);
448
449	chn_kill(d->fakechan);
450	fkchan_kill(d->fakechan);
451
452#ifdef USING_DEVFS
453	pcm_makelinks(NULL);
454#endif
455	return 0;
456}
457
458/*
459 * a small utility function which, given a device number, returns
460 * a pointer to the associated struct snddev_info struct, and sets the unit
461 * number.
462 */
463static struct snddev_info *
464get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
465{
466	struct snddev_info *sc;
467    	int u, d, c;
468
469    	u = PCMUNIT(i_dev);
470    	d = PCMDEV(i_dev);
471    	c = PCMCHAN(i_dev);
472    	if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
473    	if (unit) *unit = u;
474    	if (dev) *dev = d;
475    	if (chan) *chan = c;
476    	if (u < 0) return NULL;
477
478	sc = devclass_get_softc(pcm_devclass, u);
479	if (sc == NULL) return NULL;
480
481	switch(d) {
482    	case SND_DEV_CTL:	/* /dev/mixer handled by pcm */
483    	case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
484    	case SND_DEV_DSP:
485    	case SND_DEV_DSP16:
486    	case SND_DEV_AUDIO:
487		return sc;
488
489    	case SND_DEV_SEQ: /* XXX when enabled... */
490    	case SND_DEV_SEQ2:
491    	case SND_DEV_MIDIN:
492    	case SND_DEV_SNDPROC:	/* /dev/sndproc handled by pcm */
493    	default:
494		printf("unsupported subdevice %d\n", d);
495		return NULL;
496    	}
497}
498
499static int
500sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
501{
502    	int dev, unit, chan;
503    	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
504
505    	DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
506		unit, dev, flags, mode));
507
508    	switch(dev) {
509    	case SND_DEV_STATUS:
510		if (status_isopen) return EBUSY;
511		status_isopen = 1;
512		return 0;
513
514    	case SND_DEV_CTL:
515		return d? mixer_busy(d->mixer, 1) : ENXIO;
516
517    	case SND_DEV_AUDIO:
518    	case SND_DEV_DSP:
519    	case SND_DEV_DSP16:
520	case SND_DEV_NORESET:
521		return d? dsp_open(d, chan, flags, dev) : ENXIO;
522
523    	default:
524    		return ENXIO;
525    	}
526}
527
528static int
529sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
530{
531    	int dev, unit, chan;
532    	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
533
534    	DEB(printf("close snd%d subdev %d\n", unit, dev));
535
536    	switch(dev) { /* only those for which close makes sense */
537    	case SND_DEV_STATUS:
538		if (!status_isopen) return EBADF;
539		status_isopen = 0;
540		return 0;
541
542    	case SND_DEV_CTL:
543		return d? mixer_busy(d->mixer, 0) : ENXIO;
544
545    	case SND_DEV_AUDIO:
546    	case SND_DEV_DSP:
547    	case SND_DEV_DSP16:
548		return d? dsp_close(d, chan, dev) : ENXIO;
549
550    	default:
551		return ENXIO;
552    	}
553}
554
555static int
556sndread(dev_t i_dev, struct uio *buf, int flag)
557{
558    	int dev, unit, chan;
559    	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
560    	DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
561
562    	switch(dev) {
563    	case SND_DEV_STATUS:
564		return status_isopen? status_read(buf) : EBADF;
565
566    	case SND_DEV_AUDIO:
567    	case SND_DEV_DSP:
568    	case SND_DEV_DSP16:
569        	return d? dsp_read(d, chan, buf, flag) : EBADF;
570
571    	default:
572    		return ENXIO;
573    	}
574}
575
576static int
577sndwrite(dev_t i_dev, struct uio *buf, int flag)
578{
579    	int dev, unit, chan;
580    	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
581
582    	DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
583
584    	switch(dev) {	/* only writeable devices */
585    	case SND_DEV_DSP:
586    	case SND_DEV_DSP16:
587    	case SND_DEV_AUDIO:
588		return d? dsp_write(d, chan, buf, flag) : EBADF;
589
590    	default:
591		return EPERM; /* for non-writeable devices ; */
592    	}
593}
594
595static int
596sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
597{
598    	int dev, chan;
599    	struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
600
601    	if (d == NULL) return ENXIO;
602
603    	switch(dev) {
604    	case SND_DEV_CTL:
605		return mixer_ioctl(d, cmd, arg);
606
607    	case SND_DEV_AUDIO:
608    	case SND_DEV_DSP:
609    	case SND_DEV_DSP16:
610		if (IOCGROUP(cmd) == 'M')
611			return mixer_ioctl(d, cmd, arg);
612		else
613			return dsp_ioctl(d, chan, cmd, arg);
614
615    	default:
616    		return ENXIO;
617    	}
618}
619
620static int
621sndpoll(dev_t i_dev, int events, struct proc *p)
622{
623    	int dev, chan;
624    	struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
625
626	DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
627
628    	if (d == NULL) return ENXIO;
629
630    	switch(dev) {
631    	case SND_DEV_AUDIO:
632    	case SND_DEV_DSP:
633    	case SND_DEV_DSP16:
634		return dsp_poll(d, chan, events, p);
635
636    	default:
637    		return (events &
638       		       (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
639    	}
640}
641
642/*
643 * The mmap interface allows access to the play and read buffer,
644 * plus the device descriptor.
645 * The various blocks are accessible at the following offsets:
646 *
647 * 0x00000000 ( 0   ) : write buffer ;
648 * 0x01000000 (16 MB) : read buffer ;
649 * 0x02000000 (32 MB) : device descriptor (dangerous!)
650 *
651 * WARNING: the mmap routines assume memory areas are aligned. This
652 * is true (probably) for the dma buffers, but likely false for the
653 * device descriptor. As a consequence, we do not know where it is
654 * located in the requested area.
655 */
656static int
657sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
658{
659    	int unit, dev, chan;
660    	struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
661
662    	DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
663		   d, dev, offset, nprot));
664
665    	if (d == NULL || nprot & PROT_EXEC)	return -1; /* forbidden */
666
667    	switch(dev) {
668    	case SND_DEV_AUDIO:
669    	case SND_DEV_DSP:
670    	case SND_DEV_DSP16:
671		return dsp_mmap(d, chan, offset, nprot);
672
673    	default:
674    		return -1;
675    	}
676}
677
678static int
679status_init(char *buf, int size)
680{
681    	int             i;
682    	device_t	    dev;
683    	struct snddev_info     *d;
684
685    	snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n"
686		 "Installed devices:\n", __DATE__, __TIME__);
687
688    	for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
689		d = devclass_get_softc(pcm_devclass, i);
690		if (!d) continue;
691		dev = devclass_get_device(pcm_devclass, i);
692        	if (1) {
693			snprintf(buf + strlen(buf), size - strlen(buf),
694		            	"pcm%d: <%s> %s",
695		            	i, device_get_desc(dev), d->status);
696			if (d->chancount > 0)
697				snprintf(buf + strlen(buf), size - strlen(buf),
698				" (%dp/%dr channels%s)\n",
699			    	d->playcount, d->reccount,
700			    	(!(d->flags & SD_F_SIMPLEX))? " duplex" : "");
701			else
702				snprintf(buf + strlen(buf), size - strlen(buf),
703				" (mixer only)\n");
704		}
705    	}
706    	return strlen(buf);
707}
708
709static int
710status_read(struct uio *buf)
711{
712    	static char	status_buf[4096];
713    	static int 	bufptr = 0, buflen = 0;
714    	int l;
715
716    	if (status_isopen == 1) {
717		status_isopen++;
718		bufptr = 0;
719		buflen = status_init(status_buf, sizeof status_buf);
720    	}
721
722    	l = min(buf->uio_resid, buflen - bufptr);
723    	bufptr += l;
724    	return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0;
725}
726
727static int
728sndpcm_modevent(module_t mod, int type, void *data)
729{
730
731	switch (type) {
732	case MOD_LOAD:
733		break;
734	case MOD_UNLOAD:
735		if (status_isopen)
736			return EBUSY;
737		if (status_dev)
738			destroy_dev(status_dev);
739		status_dev = 0;
740		break;
741	default:
742		break;
743	}
744	return 0;
745}
746
747static moduledata_t sndpcm_mod = {
748	"snd_pcm",
749	sndpcm_modevent,
750	NULL
751};
752DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
753MODULE_VERSION(snd_pcm, PCM_MODVER);
754