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