sound.c revision 358878
1/*-
2 * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
3 * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
4 * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
5 * Copyright (c) 1997 Luigi Rizzo
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifdef HAVE_KERNEL_OPTION_HEADERS
31#include "opt_snd.h"
32#endif
33
34#include <dev/sound/pcm/sound.h>
35#include <dev/sound/pcm/ac97.h>
36#include <dev/sound/pcm/vchan.h>
37#include <dev/sound/pcm/dsp.h>
38#include <dev/sound/pcm/sndstat.h>
39#include <dev/sound/version.h>
40#include <sys/limits.h>
41#include <sys/sysctl.h>
42
43#include "feeder_if.h"
44
45SND_DECLARE_FILE("$FreeBSD: stable/11/sys/dev/sound/pcm/sound.c 358878 2020-03-11 08:25:33Z hselasky $");
46
47devclass_t pcm_devclass;
48
49int pcm_veto_load = 1;
50
51int snd_unit = -1;
52
53static int snd_unit_auto = -1;
54SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN,
55    &snd_unit_auto, 0, "assign default unit to a newly attached device");
56
57int snd_maxautovchans = 16;
58
59SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
60
61static void pcm_sysinit(device_t);
62
63/*
64 * XXX I've had enough with people not telling proper version/arch
65 *     while reporting problems, not after 387397913213th questions/requests.
66 */
67static char snd_driver_version[] =
68    __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH;
69SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version,
70    0, "driver version/arch");
71
72/**
73 * @brief Unit number allocator for syncgroup IDs
74 */
75struct unrhdr *pcmsg_unrhdr = NULL;
76
77static int
78sndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS)
79{
80	SNDSTAT_PREPARE_PCM_BEGIN();
81	SNDSTAT_PREPARE_PCM_END();
82}
83
84void *
85snd_mtxcreate(const char *desc, const char *type)
86{
87	struct mtx *m;
88
89	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
90	mtx_init(m, desc, type, MTX_DEF);
91	return m;
92}
93
94void
95snd_mtxfree(void *m)
96{
97	struct mtx *mtx = m;
98
99	mtx_destroy(mtx);
100	free(mtx, M_DEVBUF);
101}
102
103void
104snd_mtxassert(void *m)
105{
106#ifdef INVARIANTS
107	struct mtx *mtx = m;
108
109	mtx_assert(mtx, MA_OWNED);
110#endif
111}
112
113int
114snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
115{
116	struct snddev_info *d;
117
118	flags &= INTR_MPSAFE;
119	flags |= INTR_TYPE_AV;
120	d = device_get_softc(dev);
121	if (d != NULL && (flags & INTR_MPSAFE))
122		d->flags |= SD_F_MPSAFE;
123
124	return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep);
125}
126
127static void
128pcm_clonereset(struct snddev_info *d)
129{
130	int cmax;
131
132	PCM_BUSYASSERT(d);
133
134	cmax = d->playcount + d->reccount - 1;
135	if (d->pvchancount > 0)
136		cmax += max(d->pvchancount, snd_maxautovchans) - 1;
137	if (d->rvchancount > 0)
138		cmax += max(d->rvchancount, snd_maxautovchans) - 1;
139	if (cmax > PCMMAXCLONE)
140		cmax = PCMMAXCLONE;
141	(void)snd_clone_gc(d->clones);
142	(void)snd_clone_setmaxunit(d->clones, cmax);
143}
144
145int
146pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
147{
148	struct pcm_channel *c, *ch, *nch;
149	struct pcmchan_caps *caps;
150	int i, err, vcnt;
151
152	PCM_BUSYASSERT(d);
153
154	if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
155	    (direction == PCMDIR_REC && d->reccount < 1))
156		return (ENODEV);
157
158	if (!(d->flags & SD_F_AUTOVCHAN))
159		return (EINVAL);
160
161	if (newcnt < 0 || newcnt > SND_MAXVCHANS)
162		return (E2BIG);
163
164	if (direction == PCMDIR_PLAY)
165		vcnt = d->pvchancount;
166	else if (direction == PCMDIR_REC)
167		vcnt = d->rvchancount;
168	else
169		return (EINVAL);
170
171	if (newcnt > vcnt) {
172		KASSERT(num == -1 ||
173		    (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt),
174		    ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
175		    num, newcnt, vcnt));
176		/* add new vchans - find a parent channel first */
177		ch = NULL;
178		CHN_FOREACH(c, d, channels.pcm) {
179			CHN_LOCK(c);
180			if (c->direction == direction &&
181			    ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
182			    c->refcount < 1 &&
183			    !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
184				/*
185				 * Reuse hw channel with vchans already
186				 * created.
187				 */
188				if (c->flags & CHN_F_HAS_VCHAN) {
189					ch = c;
190					break;
191				}
192				/*
193				 * No vchans ever created, look for
194				 * channels with supported formats.
195				 */
196				caps = chn_getcaps(c);
197				if (caps == NULL) {
198					CHN_UNLOCK(c);
199					continue;
200				}
201				for (i = 0; caps->fmtlist[i] != 0; i++) {
202					if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
203						break;
204				}
205				if (caps->fmtlist[i] != 0) {
206					ch = c;
207				    	break;
208				}
209			}
210			CHN_UNLOCK(c);
211		}
212		if (ch == NULL)
213			return (EBUSY);
214		ch->flags |= CHN_F_BUSY;
215		err = 0;
216		while (err == 0 && newcnt > vcnt) {
217			err = vchan_create(ch, num);
218			if (err == 0)
219				vcnt++;
220			else if (err == E2BIG && newcnt > vcnt)
221				device_printf(d->dev,
222				    "%s: err=%d Maximum channel reached.\n",
223				    __func__, err);
224		}
225		if (vcnt == 0)
226			ch->flags &= ~CHN_F_BUSY;
227		CHN_UNLOCK(ch);
228		if (err != 0)
229			return (err);
230		else
231			pcm_clonereset(d);
232	} else if (newcnt < vcnt) {
233		KASSERT(num == -1,
234		    ("bogus vchan_destroy() request num=%d", num));
235		CHN_FOREACH(c, d, channels.pcm) {
236			CHN_LOCK(c);
237			if (c->direction != direction ||
238			    CHN_EMPTY(c, children) ||
239			    !(c->flags & CHN_F_HAS_VCHAN)) {
240				CHN_UNLOCK(c);
241				continue;
242			}
243			CHN_FOREACH_SAFE(ch, c, nch, children) {
244				CHN_LOCK(ch);
245				if (vcnt == 1 && c->refcount > 0) {
246					CHN_UNLOCK(ch);
247					break;
248				}
249				if (!(ch->flags & CHN_F_BUSY) &&
250				    ch->refcount < 1) {
251					err = vchan_destroy(ch);
252					if (err == 0)
253						vcnt--;
254				} else
255					CHN_UNLOCK(ch);
256				if (vcnt == newcnt)
257					break;
258			}
259			CHN_UNLOCK(c);
260			break;
261		}
262		pcm_clonereset(d);
263	}
264
265	return (0);
266}
267
268/* return error status and a locked channel */
269int
270pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
271    pid_t pid, char *comm, int devunit)
272{
273	struct pcm_channel *c;
274	int err, vchancount, vchan_num;
275
276	KASSERT(d != NULL && ch != NULL && (devunit == -1 ||
277	    !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) &&
278	    (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
279	    ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d",
280	    __func__, d, ch, direction, pid, devunit));
281	PCM_BUSYASSERT(d);
282
283	/* Double check again. */
284	if (devunit != -1) {
285		switch (snd_unit2d(devunit)) {
286		case SND_DEV_DSPHW_PLAY:
287		case SND_DEV_DSPHW_VPLAY:
288			if (direction != PCMDIR_PLAY)
289				return (ENOTSUP);
290			break;
291		case SND_DEV_DSPHW_REC:
292		case SND_DEV_DSPHW_VREC:
293			if (direction != PCMDIR_REC)
294				return (ENOTSUP);
295			break;
296		default:
297			if (!(direction == PCMDIR_PLAY ||
298			    direction == PCMDIR_REC))
299				return (ENOTSUP);
300			break;
301		}
302	}
303
304	*ch = NULL;
305	vchan_num = 0;
306	vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount :
307	    d->rvchancount;
308
309retry_chnalloc:
310	err = ENOTSUP;
311	/* scan for a free channel */
312	CHN_FOREACH(c, d, channels.pcm) {
313		CHN_LOCK(c);
314		if (devunit == -1 && c->direction == direction &&
315		    (c->flags & CHN_F_VIRTUAL)) {
316			if (vchancount < snd_maxautovchans &&
317			    vchan_num < CHN_CHAN(c)) {
318			    	CHN_UNLOCK(c);
319				goto vchan_alloc;
320			}
321			vchan_num++;
322		}
323		if (c->direction == direction && !(c->flags & CHN_F_BUSY) &&
324		    (devunit == -1 || devunit == -2 || c->unit == devunit)) {
325			c->flags |= CHN_F_BUSY;
326			c->pid = pid;
327			strlcpy(c->comm, (comm != NULL) ? comm :
328			    CHN_COMM_UNKNOWN, sizeof(c->comm));
329			*ch = c;
330			return (0);
331		} else if (c->unit == devunit) {
332			if (c->direction != direction)
333				err = ENOTSUP;
334			else if (c->flags & CHN_F_BUSY)
335				err = EBUSY;
336			else
337				err = EINVAL;
338			CHN_UNLOCK(c);
339			return (err);
340		} else if ((devunit == -1 || devunit == -2) &&
341		    c->direction == direction && (c->flags & CHN_F_BUSY))
342			err = EBUSY;
343		CHN_UNLOCK(c);
344	}
345
346	if (devunit == -2)
347		return (err);
348
349vchan_alloc:
350	/* no channel available */
351	if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY ||
352	    snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) {
353		if (!(vchancount > 0 && vchancount < snd_maxautovchans) &&
354		    (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans))
355			return (err);
356		err = pcm_setvchans(d, direction, vchancount + 1,
357		    (devunit == -1) ? -1 : snd_unit2c(devunit));
358		if (err == 0) {
359			if (devunit == -1)
360				devunit = -2;
361			goto retry_chnalloc;
362		}
363	}
364
365	return (err);
366}
367
368/* release a locked channel and unlock it */
369int
370pcm_chnrelease(struct pcm_channel *c)
371{
372	PCM_BUSYASSERT(c->parentsnddev);
373	CHN_LOCKASSERT(c);
374
375	c->flags &= ~CHN_F_BUSY;
376	c->pid = -1;
377	strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm));
378	CHN_UNLOCK(c);
379
380	return (0);
381}
382
383int
384pcm_chnref(struct pcm_channel *c, int ref)
385{
386	PCM_BUSYASSERT(c->parentsnddev);
387	CHN_LOCKASSERT(c);
388
389	c->refcount += ref;
390
391	return (c->refcount);
392}
393
394int
395pcm_inprog(struct snddev_info *d, int delta)
396{
397	PCM_LOCKASSERT(d);
398
399	d->inprog += delta;
400
401	return (d->inprog);
402}
403
404static void
405pcm_setmaxautovchans(struct snddev_info *d, int num)
406{
407	PCM_BUSYASSERT(d);
408
409	if (num < 0)
410		return;
411
412	if (num >= 0 && d->pvchancount > num)
413		(void)pcm_setvchans(d, PCMDIR_PLAY, num, -1);
414	else if (num > 0 && d->pvchancount == 0)
415		(void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1);
416
417	if (num >= 0 && d->rvchancount > num)
418		(void)pcm_setvchans(d, PCMDIR_REC, num, -1);
419	else if (num > 0 && d->rvchancount == 0)
420		(void)pcm_setvchans(d, PCMDIR_REC, 1, -1);
421
422	pcm_clonereset(d);
423}
424
425static int
426sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
427{
428	struct snddev_info *d;
429	int error, unit;
430
431	unit = snd_unit;
432	error = sysctl_handle_int(oidp, &unit, 0, req);
433	if (error == 0 && req->newptr != NULL) {
434		d = devclass_get_softc(pcm_devclass, unit);
435		if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
436			return EINVAL;
437		snd_unit = unit;
438		snd_unit_auto = 0;
439	}
440	return (error);
441}
442/* XXX: do we need a way to let the user change the default unit? */
443SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit,
444	    CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY,
445	    0, sizeof(int), sysctl_hw_snd_default_unit, "I",
446	    "default sound device");
447
448static int
449sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
450{
451	struct snddev_info *d;
452	int i, v, error;
453
454	v = snd_maxautovchans;
455	error = sysctl_handle_int(oidp, &v, 0, req);
456	if (error == 0 && req->newptr != NULL) {
457		if (v < 0)
458			v = 0;
459		if (v > SND_MAXVCHANS)
460			v = SND_MAXVCHANS;
461		snd_maxautovchans = v;
462		for (i = 0; pcm_devclass != NULL &&
463		    i < devclass_get_maxunit(pcm_devclass); i++) {
464			d = devclass_get_softc(pcm_devclass, i);
465			if (!PCM_REGISTERED(d))
466				continue;
467			PCM_ACQUIRE_QUICK(d);
468			pcm_setmaxautovchans(d, v);
469			PCM_RELEASE_QUICK(d);
470		}
471	}
472	return (error);
473}
474SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RWTUN,
475            0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
476
477struct pcm_channel *
478pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo)
479{
480	struct pcm_channel *ch;
481	int direction, err, rpnum, *pnum, max;
482	int udc, device, chan;
483	char *dirs, *devname, buf[CHN_NAMELEN];
484
485	PCM_BUSYASSERT(d);
486	PCM_LOCKASSERT(d);
487	KASSERT(num >= -1, ("invalid num=%d", num));
488
489
490	switch (dir) {
491	case PCMDIR_PLAY:
492		dirs = "play";
493		direction = PCMDIR_PLAY;
494		pnum = &d->playcount;
495		device = SND_DEV_DSPHW_PLAY;
496		max = SND_MAXHWCHAN;
497		break;
498	case PCMDIR_PLAY_VIRTUAL:
499		dirs = "virtual";
500		direction = PCMDIR_PLAY;
501		pnum = &d->pvchancount;
502		device = SND_DEV_DSPHW_VPLAY;
503		max = SND_MAXVCHANS;
504		break;
505	case PCMDIR_REC:
506		dirs = "record";
507		direction = PCMDIR_REC;
508		pnum = &d->reccount;
509		device = SND_DEV_DSPHW_REC;
510		max = SND_MAXHWCHAN;
511		break;
512	case PCMDIR_REC_VIRTUAL:
513		dirs = "virtual";
514		direction = PCMDIR_REC;
515		pnum = &d->rvchancount;
516		device = SND_DEV_DSPHW_VREC;
517		max = SND_MAXVCHANS;
518		break;
519	default:
520		return (NULL);
521	}
522
523	chan = (num == -1) ? 0 : num;
524
525	if (*pnum >= max || chan >= max)
526		return (NULL);
527
528	rpnum = 0;
529
530	CHN_FOREACH(ch, d, channels.pcm) {
531		if (CHN_DEV(ch) != device)
532			continue;
533		if (chan == CHN_CHAN(ch)) {
534			if (num != -1) {
535				device_printf(d->dev,
536				    "channel num=%d allocated!\n", chan);
537				return (NULL);
538			}
539			chan++;
540			if (chan >= max) {
541				device_printf(d->dev,
542				    "chan=%d > %d\n", chan, max);
543				return (NULL);
544			}
545		}
546		rpnum++;
547	}
548
549	if (*pnum != rpnum) {
550		device_printf(d->dev,
551		    "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n",
552		    __func__, dirs, *pnum, rpnum);
553		return (NULL);
554	}
555
556	udc = snd_mkunit(device_get_unit(d->dev), device, chan);
557	devname = dsp_unit2name(buf, sizeof(buf), udc);
558
559	if (devname == NULL) {
560		device_printf(d->dev,
561		    "Failed to query device name udc=0x%08x\n", udc);
562		return (NULL);
563	}
564
565	PCM_UNLOCK(d);
566	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
567	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
568	ch->unit = udc;
569	ch->pid = -1;
570	strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm));
571	ch->parentsnddev = d;
572	ch->parentchannel = parent;
573	ch->dev = d->dev;
574	ch->trigger = PCMTRIG_STOP;
575	snprintf(ch->name, sizeof(ch->name), "%s:%s:%s",
576	    device_get_nameunit(ch->dev), dirs, devname);
577
578	err = chn_init(ch, devinfo, dir, direction);
579	PCM_LOCK(d);
580	if (err) {
581		device_printf(d->dev, "chn_init(%s) failed: err = %d\n",
582		    ch->name, err);
583		kobj_delete(ch->methods, M_DEVBUF);
584		free(ch, M_DEVBUF);
585		return (NULL);
586	}
587
588	return (ch);
589}
590
591int
592pcm_chn_destroy(struct pcm_channel *ch)
593{
594	struct snddev_info *d;
595	int err;
596
597	d = ch->parentsnddev;
598	PCM_BUSYASSERT(d);
599
600	err = chn_kill(ch);
601	if (err) {
602		device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n",
603		    ch->name, err);
604		return (err);
605	}
606
607	kobj_delete(ch->methods, M_DEVBUF);
608	free(ch, M_DEVBUF);
609
610	return (0);
611}
612
613int
614pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
615{
616	PCM_BUSYASSERT(d);
617	PCM_LOCKASSERT(d);
618	KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
619	    ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
620
621	CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm);
622
623	switch (CHN_DEV(ch)) {
624	case SND_DEV_DSPHW_PLAY:
625		d->playcount++;
626		break;
627	case SND_DEV_DSPHW_VPLAY:
628		d->pvchancount++;
629		break;
630	case SND_DEV_DSPHW_REC:
631		d->reccount++;
632		break;
633	case SND_DEV_DSPHW_VREC:
634		d->rvchancount++;
635		break;
636	default:
637		break;
638	}
639
640	d->devcount++;
641
642	return (0);
643}
644
645int
646pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
647{
648	struct pcm_channel *tmp;
649
650	PCM_BUSYASSERT(d);
651	PCM_LOCKASSERT(d);
652
653	tmp = NULL;
654
655	CHN_FOREACH(tmp, d, channels.pcm) {
656		if (tmp == ch)
657			break;
658	}
659
660	if (tmp != ch)
661		return (EINVAL);
662
663	CHN_REMOVE(d, ch, channels.pcm);
664
665	switch (CHN_DEV(ch)) {
666	case SND_DEV_DSPHW_PLAY:
667		d->playcount--;
668		break;
669	case SND_DEV_DSPHW_VPLAY:
670		d->pvchancount--;
671		break;
672	case SND_DEV_DSPHW_REC:
673		d->reccount--;
674		break;
675	case SND_DEV_DSPHW_VREC:
676		d->rvchancount--;
677		break;
678	default:
679		break;
680	}
681
682	d->devcount--;
683
684	return (0);
685}
686
687int
688pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
689{
690	struct snddev_info *d = device_get_softc(dev);
691	struct pcm_channel *ch;
692	int err;
693
694	PCM_BUSYASSERT(d);
695
696	PCM_LOCK(d);
697	ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo);
698	if (!ch) {
699		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n",
700		    cls->name, dir, devinfo);
701		PCM_UNLOCK(d);
702		return (ENODEV);
703	}
704
705	err = pcm_chn_add(d, ch);
706	PCM_UNLOCK(d);
707	if (err) {
708		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n",
709		    ch->name, err);
710		pcm_chn_destroy(ch);
711	}
712
713	return (err);
714}
715
716static int
717pcm_killchan(device_t dev)
718{
719	struct snddev_info *d = device_get_softc(dev);
720	struct pcm_channel *ch;
721	int error;
722
723	PCM_BUSYASSERT(d);
724
725	ch = CHN_FIRST(d, channels.pcm);
726
727	PCM_LOCK(d);
728	error = pcm_chn_remove(d, ch);
729	PCM_UNLOCK(d);
730	if (error)
731		return (error);
732	return (pcm_chn_destroy(ch));
733}
734
735static int
736pcm_best_unit(int old)
737{
738	struct snddev_info *d;
739	int i, best, bestprio, prio;
740
741	best = -1;
742	bestprio = -100;
743	for (i = 0; pcm_devclass != NULL &&
744	    i < devclass_get_maxunit(pcm_devclass); i++) {
745		d = devclass_get_softc(pcm_devclass, i);
746		if (!PCM_REGISTERED(d))
747			continue;
748		prio = 0;
749		if (d->playcount == 0)
750			prio -= 10;
751		if (d->reccount == 0)
752			prio -= 2;
753		if (prio > bestprio || (prio == bestprio && i == old)) {
754			best = i;
755			bestprio = prio;
756		}
757	}
758	return (best);
759}
760
761int
762pcm_setstatus(device_t dev, char *str)
763{
764	struct snddev_info *d = device_get_softc(dev);
765
766	/* should only be called once */
767	if (d->flags & SD_F_REGISTERED)
768		return (EINVAL);
769
770	PCM_BUSYASSERT(d);
771
772	if (d->playcount == 0 || d->reccount == 0)
773		d->flags |= SD_F_SIMPLEX;
774
775	if (d->playcount > 0 || d->reccount > 0)
776		d->flags |= SD_F_AUTOVCHAN;
777
778	pcm_setmaxautovchans(d, snd_maxautovchans);
779
780	strlcpy(d->status, str, SND_STATUSLEN);
781
782	PCM_LOCK(d);
783
784	/* Last stage, enable cloning. */
785	if (d->clones != NULL)
786		(void)snd_clone_enable(d->clones);
787
788	/* Done, we're ready.. */
789	d->flags |= SD_F_REGISTERED;
790
791	PCM_RELEASE(d);
792
793	PCM_UNLOCK(d);
794
795	/*
796	 * Create all sysctls once SD_F_REGISTERED is set else
797	 * tunable sysctls won't work:
798	 */
799	pcm_sysinit(dev);
800
801	if (snd_unit_auto < 0)
802		snd_unit_auto = (snd_unit < 0) ? 1 : 0;
803	if (snd_unit < 0 || snd_unit_auto > 1)
804		snd_unit = device_get_unit(dev);
805	else if (snd_unit_auto == 1)
806		snd_unit = pcm_best_unit(snd_unit);
807
808	return (0);
809}
810
811uint32_t
812pcm_getflags(device_t dev)
813{
814	struct snddev_info *d = device_get_softc(dev);
815
816	return d->flags;
817}
818
819void
820pcm_setflags(device_t dev, uint32_t val)
821{
822	struct snddev_info *d = device_get_softc(dev);
823
824	d->flags = val;
825}
826
827void *
828pcm_getdevinfo(device_t dev)
829{
830	struct snddev_info *d = device_get_softc(dev);
831
832	return d->devinfo;
833}
834
835unsigned int
836pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
837{
838	struct snddev_info *d = device_get_softc(dev);
839	int sz, x;
840
841	sz = 0;
842	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
843		x = sz;
844		RANGE(sz, minbufsz, maxbufsz);
845		if (x != sz)
846			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
847		x = minbufsz;
848		while (x < sz)
849			x <<= 1;
850		if (x > sz)
851			x >>= 1;
852		if (x != sz) {
853			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
854			sz = x;
855		}
856	} else {
857		sz = deflt;
858	}
859
860	d->bufsz = sz;
861
862	return sz;
863}
864
865static int
866sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)
867{
868	struct snddev_info *d;
869	int err, val;
870
871	d = oidp->oid_arg1;
872	if (!PCM_REGISTERED(d))
873		return (ENODEV);
874
875	PCM_LOCK(d);
876	PCM_WAIT(d);
877	val = (d->flags & SD_F_BITPERFECT) ? 1 : 0;
878	PCM_ACQUIRE(d);
879	PCM_UNLOCK(d);
880
881	err = sysctl_handle_int(oidp, &val, 0, req);
882
883	if (err == 0 && req->newptr != NULL) {
884		if (!(val == 0 || val == 1)) {
885			PCM_RELEASE_QUICK(d);
886			return (EINVAL);
887		}
888
889		PCM_LOCK(d);
890
891		d->flags &= ~SD_F_BITPERFECT;
892		d->flags |= (val != 0) ? SD_F_BITPERFECT : 0;
893
894		PCM_RELEASE(d);
895		PCM_UNLOCK(d);
896	} else
897		PCM_RELEASE_QUICK(d);
898
899	return (err);
900}
901
902#ifdef SND_DEBUG
903static int
904sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS)
905{
906	struct snddev_info *d;
907	uint32_t flags;
908	int err;
909
910	d = oidp->oid_arg1;
911	if (!PCM_REGISTERED(d) || d->clones == NULL)
912		return (ENODEV);
913
914	PCM_ACQUIRE_QUICK(d);
915
916	flags = snd_clone_getflags(d->clones);
917	err = sysctl_handle_int(oidp, &flags, 0, req);
918
919	if (err == 0 && req->newptr != NULL) {
920		if (flags & ~SND_CLONE_MASK)
921			err = EINVAL;
922		else
923			(void)snd_clone_setflags(d->clones, flags);
924	}
925
926	PCM_RELEASE_QUICK(d);
927
928	return (err);
929}
930
931static int
932sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS)
933{
934	struct snddev_info *d;
935	int err, deadline;
936
937	d = oidp->oid_arg1;
938	if (!PCM_REGISTERED(d) || d->clones == NULL)
939		return (ENODEV);
940
941	PCM_ACQUIRE_QUICK(d);
942
943	deadline = snd_clone_getdeadline(d->clones);
944	err = sysctl_handle_int(oidp, &deadline, 0, req);
945
946	if (err == 0 && req->newptr != NULL) {
947		if (deadline < 0)
948			err = EINVAL;
949		else
950			(void)snd_clone_setdeadline(d->clones, deadline);
951	}
952
953	PCM_RELEASE_QUICK(d);
954
955	return (err);
956}
957
958static int
959sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS)
960{
961	struct snddev_info *d;
962	int err, val;
963
964	d = oidp->oid_arg1;
965	if (!PCM_REGISTERED(d) || d->clones == NULL)
966		return (ENODEV);
967
968	val = 0;
969	err = sysctl_handle_int(oidp, &val, 0, req);
970
971	if (err == 0 && req->newptr != NULL && val != 0) {
972		PCM_ACQUIRE_QUICK(d);
973		val = snd_clone_gc(d->clones);
974		PCM_RELEASE_QUICK(d);
975		if (bootverbose != 0 || snd_verbose > 3)
976			device_printf(d->dev, "clone gc: pruned=%d\n", val);
977	}
978
979	return (err);
980}
981
982static int
983sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS)
984{
985	struct snddev_info *d;
986	int i, err, val;
987
988	val = 0;
989	err = sysctl_handle_int(oidp, &val, 0, req);
990
991	if (err == 0 && req->newptr != NULL && val != 0) {
992		for (i = 0; pcm_devclass != NULL &&
993		    i < devclass_get_maxunit(pcm_devclass); i++) {
994			d = devclass_get_softc(pcm_devclass, i);
995			if (!PCM_REGISTERED(d) || d->clones == NULL)
996				continue;
997			PCM_ACQUIRE_QUICK(d);
998			val = snd_clone_gc(d->clones);
999			PCM_RELEASE_QUICK(d);
1000			if (bootverbose != 0 || snd_verbose > 3)
1001				device_printf(d->dev, "clone gc: pruned=%d\n",
1002				    val);
1003		}
1004	}
1005
1006	return (err);
1007}
1008SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RWTUN,
1009    0, sizeof(int), sysctl_hw_snd_clone_gc, "I",
1010    "global clone garbage collector");
1011#endif
1012
1013static void
1014pcm_sysinit(device_t dev)
1015{
1016  	struct snddev_info *d = device_get_softc(dev);
1017
1018  	/* XXX: an user should be able to set this with a control tool, the
1019	   sysadmin then needs min+max sysctls for this */
1020	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
1021	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
1022            OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
1023	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1024	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1025	    "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN, d, sizeof(d),
1026	    sysctl_dev_pcm_bitperfect, "I",
1027	    "bit-perfect playback/recording (0=disable, 1=enable)");
1028#ifdef SND_DEBUG
1029	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1030	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1031	    "clone_flags", CTLTYPE_UINT | CTLFLAG_RWTUN, d, sizeof(d),
1032	    sysctl_dev_pcm_clone_flags, "IU",
1033	    "clone flags");
1034	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1035	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1036	    "clone_deadline", CTLTYPE_INT | CTLFLAG_RWTUN, d, sizeof(d),
1037	    sysctl_dev_pcm_clone_deadline, "I",
1038	    "clone expiration deadline (ms)");
1039	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1040	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1041	    "clone_gc", CTLTYPE_INT | CTLFLAG_RWTUN, d, sizeof(d),
1042	    sysctl_dev_pcm_clone_gc, "I",
1043	    "clone garbage collector");
1044#endif
1045	if (d->flags & SD_F_AUTOVCHAN)
1046		vchan_initsys(dev);
1047	if (d->flags & SD_F_EQ)
1048		feeder_eq_initsys(dev);
1049}
1050
1051int
1052pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
1053{
1054	struct snddev_info *d;
1055	int i;
1056
1057	if (pcm_veto_load) {
1058		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
1059
1060		return EINVAL;
1061	}
1062
1063	if (device_get_unit(dev) > PCMMAXUNIT) {
1064		device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n",
1065		    device_get_unit(dev), PCMMAXUNIT);
1066		device_printf(dev,
1067		    "Use 'hw.snd.maxunit' tunable to raise the limit.\n");
1068		return ENODEV;
1069	}
1070
1071	d = device_get_softc(dev);
1072	d->dev = dev;
1073	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
1074	cv_init(&d->cv, device_get_nameunit(dev));
1075	PCM_ACQUIRE_QUICK(d);
1076	dsp_cdevinfo_init(d);
1077#if 0
1078	/*
1079	 * d->flags should be cleared by the allocator of the softc.
1080	 * We cannot clear this field here because several devices set
1081	 * this flag before calling pcm_register().
1082	 */
1083	d->flags = 0;
1084#endif
1085	i = 0;
1086	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
1087	    "vpc", &i) != 0 || i != 0)
1088		d->flags |= SD_F_VPC;
1089
1090	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
1091	    "bitperfect", &i) == 0 && i != 0)
1092		d->flags |= SD_F_BITPERFECT;
1093
1094	d->devinfo = devinfo;
1095	d->devcount = 0;
1096	d->reccount = 0;
1097	d->playcount = 0;
1098	d->pvchancount = 0;
1099	d->rvchancount = 0;
1100	d->pvchanrate = 0;
1101	d->pvchanformat = 0;
1102	d->rvchanrate = 0;
1103	d->rvchanformat = 0;
1104	d->inprog = 0;
1105
1106	/*
1107	 * Create clone manager, disabled by default. Cloning will be
1108	 * enabled during final stage of driver initialization through
1109	 * pcm_setstatus().
1110	 */
1111	d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE,
1112	    SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK |
1113	    SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF |
1114	    SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED);
1115
1116	CHN_INIT(d, channels.pcm);
1117	CHN_INIT(d, channels.pcm.busy);
1118	CHN_INIT(d, channels.pcm.opened);
1119
1120	/* XXX This is incorrect, but lets play along for now. */
1121	if ((numplay == 0 || numrec == 0) && numplay != numrec)
1122		d->flags |= SD_F_SIMPLEX;
1123
1124	sysctl_ctx_init(&d->play_sysctl_ctx);
1125	d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
1126	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
1127	    CTLFLAG_RD, 0, "playback channels node");
1128	sysctl_ctx_init(&d->rec_sysctl_ctx);
1129	d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
1130	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
1131	    CTLFLAG_RD, 0, "record channels node");
1132
1133	if (numplay > 0 || numrec > 0)
1134		d->flags |= SD_F_AUTOVCHAN;
1135
1136	sndstat_register(dev, d->status, sndstat_prepare_pcm);
1137
1138	return 0;
1139}
1140
1141int
1142pcm_unregister(device_t dev)
1143{
1144	struct snddev_info *d;
1145	struct pcm_channel *ch;
1146	struct thread *td;
1147
1148	td = curthread;
1149	d = device_get_softc(dev);
1150
1151	if (!PCM_ALIVE(d)) {
1152		device_printf(dev, "unregister: device not configured\n");
1153		return (0);
1154	}
1155
1156	PCM_LOCK(d);
1157	PCM_WAIT(d);
1158
1159	d->flags |= SD_F_DETACHING;
1160
1161	if (d->inprog != 0) {
1162		device_printf(dev, "unregister: operation in progress\n");
1163		PCM_UNLOCK(d);
1164		return (EBUSY);
1165	}
1166
1167	PCM_ACQUIRE(d);
1168	PCM_UNLOCK(d);
1169
1170	CHN_FOREACH(ch, d, channels.pcm) {
1171		CHN_LOCK(ch);
1172		if (ch->refcount > 0) {
1173			device_printf(dev,
1174			    "unregister: channel %s busy (pid %d)\n",
1175			    ch->name, ch->pid);
1176			CHN_UNLOCK(ch);
1177			PCM_RELEASE_QUICK(d);
1178			return (EBUSY);
1179		}
1180		CHN_UNLOCK(ch);
1181	}
1182
1183	if (d->clones != NULL) {
1184		if (snd_clone_busy(d->clones) != 0) {
1185			device_printf(dev, "unregister: clone busy\n");
1186			PCM_RELEASE_QUICK(d);
1187			return (EBUSY);
1188		} else {
1189			PCM_LOCK(d);
1190			(void)snd_clone_disable(d->clones);
1191			PCM_UNLOCK(d);
1192		}
1193	}
1194
1195	if (mixer_uninit(dev) == EBUSY) {
1196		device_printf(dev, "unregister: mixer busy\n");
1197		PCM_LOCK(d);
1198		if (d->clones != NULL)
1199			(void)snd_clone_enable(d->clones);
1200		PCM_RELEASE(d);
1201		PCM_UNLOCK(d);
1202		return (EBUSY);
1203	}
1204
1205	/* remove /dev/sndstat entry first */
1206	sndstat_unregister(dev);
1207
1208	PCM_LOCK(d);
1209	d->flags |= SD_F_DYING;
1210	d->flags &= ~SD_F_REGISTERED;
1211	PCM_UNLOCK(d);
1212
1213	/*
1214	 * No lock being held, so this thing can be flushed without
1215	 * stucking into devdrn oblivion.
1216	 */
1217	if (d->clones != NULL) {
1218		snd_clone_destroy(d->clones);
1219		d->clones = NULL;
1220	}
1221
1222	if (d->play_sysctl_tree != NULL) {
1223		sysctl_ctx_free(&d->play_sysctl_ctx);
1224		d->play_sysctl_tree = NULL;
1225	}
1226	if (d->rec_sysctl_tree != NULL) {
1227		sysctl_ctx_free(&d->rec_sysctl_ctx);
1228		d->rec_sysctl_tree = NULL;
1229	}
1230
1231	while (!CHN_EMPTY(d, channels.pcm))
1232		pcm_killchan(dev);
1233
1234	dsp_cdevinfo_flush(d);
1235
1236	PCM_LOCK(d);
1237	PCM_RELEASE(d);
1238	cv_destroy(&d->cv);
1239	PCM_UNLOCK(d);
1240	snd_mtxfree(d->lock);
1241
1242	if (snd_unit == device_get_unit(dev)) {
1243		snd_unit = pcm_best_unit(-1);
1244		if (snd_unit_auto == 0)
1245			snd_unit_auto = 1;
1246	}
1247
1248	return (0);
1249}
1250
1251/************************************************************************/
1252
1253/**
1254 * @brief	Handle OSSv4 SNDCTL_SYSINFO ioctl.
1255 *
1256 * @param si	Pointer to oss_sysinfo struct where information about the
1257 * 		sound subsystem will be written/copied.
1258 *
1259 * This routine returns information about the sound system, such as the
1260 * current OSS version, number of audio, MIDI, and mixer drivers, etc.
1261 * Also includes a bitmask showing which of the above types of devices
1262 * are open (busy).
1263 *
1264 * @note
1265 * Calling threads must not hold any snddev_info or pcm_channel locks.
1266 *
1267 * @author	Ryan Beasley <ryanb@FreeBSD.org>
1268 */
1269void
1270sound_oss_sysinfo(oss_sysinfo *si)
1271{
1272	static char si_product[] = "FreeBSD native OSS ABI";
1273	static char si_version[] = __XSTRING(__FreeBSD_version);
1274	static char si_license[] = "BSD";
1275	static int intnbits = sizeof(int) * 8;	/* Better suited as macro?
1276						   Must pester a C guru. */
1277
1278	struct snddev_info *d;
1279	struct pcm_channel *c;
1280	int i, j, ncards;
1281
1282	ncards = 0;
1283
1284	strlcpy(si->product, si_product, sizeof(si->product));
1285	strlcpy(si->version, si_version, sizeof(si->version));
1286	si->versionnum = SOUND_VERSION;
1287	strlcpy(si->license, si_license, sizeof(si->license));
1288
1289	/*
1290	 * Iterate over PCM devices and their channels, gathering up data
1291	 * for the numaudios, ncards, and openedaudio fields.
1292	 */
1293	si->numaudios = 0;
1294	bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
1295
1296	j = 0;
1297
1298	for (i = 0; pcm_devclass != NULL &&
1299	    i < devclass_get_maxunit(pcm_devclass); i++) {
1300		d = devclass_get_softc(pcm_devclass, i);
1301		if (!PCM_REGISTERED(d))
1302			continue;
1303
1304		/* XXX Need Giant magic entry ??? */
1305
1306		/* See note in function's docblock */
1307		PCM_UNLOCKASSERT(d);
1308		PCM_LOCK(d);
1309
1310		si->numaudios += d->devcount;
1311		++ncards;
1312
1313		CHN_FOREACH(c, d, channels.pcm) {
1314			CHN_UNLOCKASSERT(c);
1315			CHN_LOCK(c);
1316			if (c->flags & CHN_F_BUSY)
1317				si->openedaudio[j / intnbits] |=
1318				    (1 << (j % intnbits));
1319			CHN_UNLOCK(c);
1320			j++;
1321		}
1322
1323		PCM_UNLOCK(d);
1324	}
1325	si->numaudioengines = si->numaudios;
1326
1327	si->numsynths = 0;	/* OSSv4 docs:  this field is obsolete */
1328	/**
1329	 * @todo	Collect num{midis,timers}.
1330	 *
1331	 * Need access to sound/midi/midi.c::midistat_lock in order
1332	 * to safely touch midi_devices and get a head count of, well,
1333	 * MIDI devices.  midistat_lock is a global static (i.e., local to
1334	 * midi.c), but midi_devices is a regular global; should the mutex
1335	 * be publicized, or is there another way to get this information?
1336	 *
1337	 * NB:	MIDI/sequencer stuff is currently on hold.
1338	 */
1339	si->nummidis = 0;
1340	si->numtimers = 0;
1341	si->nummixers = mixer_count;
1342	si->numcards = ncards;
1343		/* OSSv4 docs:	Intended only for test apps; API doesn't
1344		   really have much of a concept of cards.  Shouldn't be
1345		   used by applications. */
1346
1347	/**
1348	 * @todo	Fill in "busy devices" fields.
1349	 *
1350	 *  si->openedmidi = " MIDI devices
1351	 */
1352	bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
1353
1354	/*
1355	 * Si->filler is a reserved array, but according to docs each
1356	 * element should be set to -1.
1357	 */
1358	for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++)
1359		si->filler[i] = -1;
1360}
1361
1362int
1363sound_oss_card_info(oss_card_info *si)
1364{
1365	struct snddev_info *d;
1366	int i, ncards;
1367
1368	ncards = 0;
1369
1370	for (i = 0; pcm_devclass != NULL &&
1371	    i < devclass_get_maxunit(pcm_devclass); i++) {
1372		d = devclass_get_softc(pcm_devclass, i);
1373		if (!PCM_REGISTERED(d))
1374			continue;
1375
1376		if (ncards++ != si->card)
1377			continue;
1378
1379		PCM_UNLOCKASSERT(d);
1380		PCM_LOCK(d);
1381
1382		strlcpy(si->shortname, device_get_nameunit(d->dev),
1383		    sizeof(si->shortname));
1384		strlcpy(si->longname, device_get_desc(d->dev),
1385		    sizeof(si->longname));
1386		strlcpy(si->hw_info, d->status, sizeof(si->hw_info));
1387		si->intr_count = si->ack_count = 0;
1388
1389		PCM_UNLOCK(d);
1390
1391		return (0);
1392	}
1393	return (ENXIO);
1394}
1395
1396/************************************************************************/
1397
1398static int
1399sound_modevent(module_t mod, int type, void *data)
1400{
1401	int ret;
1402#if 0
1403	return (midi_modevent(mod, type, data));
1404#else
1405	ret = 0;
1406
1407	switch(type) {
1408		case MOD_LOAD:
1409			pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
1410			break;
1411		case MOD_UNLOAD:
1412			if (pcmsg_unrhdr != NULL) {
1413				delete_unrhdr(pcmsg_unrhdr);
1414				pcmsg_unrhdr = NULL;
1415			}
1416			break;
1417		case MOD_SHUTDOWN:
1418			break;
1419		default:
1420			ret = ENOTSUP;
1421	}
1422
1423	return ret;
1424#endif
1425}
1426
1427DEV_MODULE(sound, sound_modevent, NULL);
1428MODULE_VERSION(sound, SOUND_MODVER);
1429