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