sound.c revision 170893
1139749Simp/*-
2119853Scg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3166427Sjoel * (C) 1997 Luigi Rizzo
450724Scg * All rights reserved.
550724Scg *
650724Scg * Redistribution and use in source and binary forms, with or without
750724Scg * modification, are permitted provided that the following conditions
850724Scg * are met:
950724Scg * 1. Redistributions of source code must retain the above copyright
1050724Scg *    notice, this list of conditions and the following disclaimer.
1150724Scg * 2. Redistributions in binary form must reproduce the above copyright
1250724Scg *    notice, this list of conditions and the following disclaimer in the
1350724Scg *    documentation and/or other materials provided with the distribution.
1450724Scg *
1550724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1650724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1750724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1850724Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1950724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2050724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2150724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2250724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2350724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2450724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2550724Scg * SUCH DAMAGE.
2650724Scg */
2750724Scg
2853465Scg#include <dev/sound/pcm/sound.h>
29164614Sariff#include <dev/sound/pcm/ac97.h>
3077269Scg#include <dev/sound/pcm/vchan.h>
31124740Smatk#include <dev/sound/pcm/dsp.h>
32170161Sariff#include <dev/sound/version.h>
33162588Snetchild#include <sys/limits.h>
3465207Scg#include <sys/sysctl.h>
3550724Scg
3682180Scg#include "feeder_if.h"
3782180Scg
3882180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 170893 2007-06-17 19:02:05Z ariff $");
3982180Scg
4078362Scgdevclass_t pcm_devclass;
4150724Scg
4289834Scgint pcm_veto_load = 1;
4389834Scg
4473127Scg#ifdef USING_DEVFS
45170884Sariffint snd_unit = -1;
46159732SnetchildTUNABLE_INT("hw.snd.default_unit", &snd_unit);
4773127Scg#endif
4879141Scg
49170885Sariffstatic int snd_unit_auto = 0;
50170885SariffTUNABLE_INT("hw.snd.default_auto", &snd_unit_auto);
51170885SariffSYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RW,
52170893Sariff    &snd_unit_auto, 0, "assign default unit to a newly attached device");
53170885Sariff
54170161Sariffint snd_maxautovchans = 16;
55159732Snetchild/* XXX: a tunable implies that we may need more than one sound channel before
56159732Snetchild   the system can change a sysctl (/etc/sysctl.conf), do we really need
57159732Snetchild   this? */
5882180ScgTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
5950724Scg
6073127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
6173127Scg
62170161Sariff/*
63170161Sariff * XXX I've had enough with people not telling proper version/arch
64170161Sariff *     while reporting problems, not after 387397913213th questions/requests.
65170161Sariff */
66170161Sariffstatic const char snd_driver_version[] =
67170161Sariff    __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH;
68170161SariffSYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version,
69170161Sariff    0, "Driver version/arch");
70170161Sariff
71162588Snetchild/**
72162588Snetchild * @brief Unit number allocator for syncgroup IDs
73162588Snetchild */
74162588Snetchildstruct unrhdr *pcmsg_unrhdr = NULL;
75162588Snetchild
7682180Scgstatic int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
7782180Scg
7873131Scgvoid *
7993814Sjhbsnd_mtxcreate(const char *desc, const char *type)
8073131Scg{
8173131Scg#ifdef USING_MUTEX
8273131Scg	struct mtx *m;
8373131Scg
84111119Simp	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
85125136Struckman	mtx_init(m, desc, type, MTX_DEF);
8673131Scg	return m;
8773131Scg#else
8873757Scg	return (void *)0xcafebabe;
8973131Scg#endif
9073131Scg}
9173131Scg
9273131Scgvoid
9373131Scgsnd_mtxfree(void *m)
9473131Scg{
9573131Scg#ifdef USING_MUTEX
9673131Scg	struct mtx *mtx = m;
9773131Scg
98107237Scg	/* mtx_assert(mtx, MA_OWNED); */
9973131Scg	mtx_destroy(mtx);
10073131Scg	free(mtx, M_DEVBUF);
10173131Scg#endif
10273131Scg}
10373131Scg
10473131Scgvoid
10573131Scgsnd_mtxassert(void *m)
10673131Scg{
10773131Scg#ifdef USING_MUTEX
10878670Scg#ifdef INVARIANTS
10973131Scg	struct mtx *mtx = m;
11073131Scg
11173131Scg	mtx_assert(mtx, MA_OWNED);
11273131Scg#endif
11378670Scg#endif
11473131Scg}
115107237Scg/*
11673131Scgvoid
11773131Scgsnd_mtxlock(void *m)
11873131Scg{
11973131Scg#ifdef USING_MUTEX
12073131Scg	struct mtx *mtx = m;
12173131Scg
12273131Scg	mtx_lock(mtx);
12373131Scg#endif
12473131Scg}
12573131Scg
12673131Scgvoid
12773131Scgsnd_mtxunlock(void *m)
12873131Scg{
12973131Scg#ifdef USING_MUTEX
13073131Scg	struct mtx *mtx = m;
13173131Scg
13273131Scg	mtx_unlock(mtx);
13373131Scg#endif
13473131Scg}
135107237Scg*/
13673131Scgint
13773131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
13873131Scg{
139170815Sariff	struct snddev_info *d;
14073131Scg#ifdef USING_MUTEX
14173131Scg	flags &= INTR_MPSAFE;
14278366Speter	flags |= INTR_TYPE_AV;
14373131Scg#else
14478366Speter	flags = INTR_TYPE_AV;
14573131Scg#endif
146170815Sariff	d = device_get_softc(dev);
147170815Sariff	if (d != NULL && (flags & INTR_MPSAFE))
148170815Sariff		d->flags |= SD_F_MPSAFE;
149170815Sariff
150166918Sariff	return bus_setup_intr(dev, res, flags,
151166918Sariff#if __FreeBSD_version >= 700031
152166918Sariff			NULL,
153166918Sariff#endif
154166918Sariff			hand, param, cookiep);
15573131Scg}
15673131Scg
157119096Scg#ifndef	PCM_DEBUG_MTX
15882180Scgvoid
15982180Scgpcm_lock(struct snddev_info *d)
16082180Scg{
16182180Scg	snd_mtxlock(d->lock);
16282180Scg}
16382180Scg
16482180Scgvoid
16582180Scgpcm_unlock(struct snddev_info *d)
16682180Scg{
16782180Scg	snd_mtxunlock(d->lock);
16882180Scg}
169119096Scg#endif
17082180Scg
17182180Scgstruct pcm_channel *
17282180Scgpcm_getfakechan(struct snddev_info *d)
17382180Scg{
17482180Scg	return d->fakechan;
17582180Scg}
17682180Scg
177170161Sariffstatic void
178170161Sariffpcm_clonereset(struct snddev_info *d)
179170161Sariff{
180170161Sariff	int cmax;
181170161Sariff
182170815Sariff	PCM_BUSYASSERT(d);
183170161Sariff
184170161Sariff	cmax = d->playcount + d->reccount - 1;
185170161Sariff	if (d->pvchancount > 0)
186170161Sariff		cmax += MAX(d->pvchancount, snd_maxautovchans) - 1;
187170161Sariff	if (d->rvchancount > 0)
188170161Sariff		cmax += MAX(d->rvchancount, snd_maxautovchans) - 1;
189170161Sariff	if (cmax > PCMMAXCLONE)
190170161Sariff		cmax = PCMMAXCLONE;
191170161Sariff	(void)snd_clone_gc(d->clones);
192170161Sariff	(void)snd_clone_setmaxunit(d->clones, cmax);
193170161Sariff}
194170161Sariff
195157331Sariffstatic int
196170161Sariffpcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
197157331Sariff{
198170161Sariff	struct pcm_channel *c, *ch, *nch;
199170161Sariff	int err, vcnt;
200157331Sariff
201170815Sariff	PCM_BUSYASSERT(d);
202170161Sariff
203170161Sariff	if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
204170815Sariff	    (direction == PCMDIR_REC && d->reccount < 1))
205170815Sariff		return (ENODEV);
206164614Sariff
207170815Sariff	if (!(d->flags & SD_F_AUTOVCHAN))
208170815Sariff		return (EINVAL);
209157331Sariff
210170815Sariff	if (newcnt < 0 || newcnt > SND_MAXVCHANS)
211170815Sariff		return (E2BIG);
212157331Sariff
213170161Sariff	if (direction == PCMDIR_PLAY)
214170161Sariff		vcnt = d->pvchancount;
215170161Sariff	else if (direction == PCMDIR_REC)
216170161Sariff		vcnt = d->rvchancount;
217170815Sariff	else
218170815Sariff		return (EINVAL);
219157331Sariff
220157331Sariff	if (newcnt > vcnt) {
221170161Sariff		KASSERT(num == -1 ||
222170161Sariff		    (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt),
223170161Sariff		    ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
224170161Sariff		    num, newcnt, vcnt));
225157331Sariff		/* add new vchans - find a parent channel first */
226170815Sariff		ch = NULL;
227170161Sariff		CHN_FOREACH(c, d, channels.pcm) {
228157331Sariff			CHN_LOCK(c);
229170161Sariff			if (c->direction == direction &&
230170161Sariff			    ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
231170815Sariff			    !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
232170815Sariff				ch = c;
233170815Sariff			    	break;
234170815Sariff			}
235157331Sariff			CHN_UNLOCK(c);
236157331Sariff		}
237170815Sariff		if (ch == NULL)
238170815Sariff			return (EBUSY);
239170815Sariff		ch->flags |= CHN_F_BUSY;
240170815Sariff		err = 0;
241157331Sariff		while (err == 0 && newcnt > vcnt) {
242170815Sariff			err = vchan_create(ch, num);
243170161Sariff			if (err == 0)
244157331Sariff				vcnt++;
245170161Sariff			else if (err == E2BIG && newcnt > vcnt)
246170161Sariff				device_printf(d->dev,
247170161Sariff				    "%s: err=%d Maximum channel reached.\n",
248170161Sariff				    __func__, err);
249157331Sariff		}
250157331Sariff		if (vcnt == 0)
251170815Sariff			ch->flags &= ~CHN_F_BUSY;
252170815Sariff		CHN_UNLOCK(ch);
253170815Sariff		if (err != 0)
254170815Sariff			return (err);
255170815Sariff		else
256170815Sariff			pcm_clonereset(d);
257157331Sariff	} else if (newcnt < vcnt) {
258170161Sariff		KASSERT(num == -1,
259170161Sariff		    ("bogus vchan_destroy() request num=%d", num));
260170161Sariff		CHN_FOREACH(c, d, channels.pcm) {
261170161Sariff			CHN_LOCK(c);
262170161Sariff			if (c->direction != direction ||
263170161Sariff			    CHN_EMPTY(c, children) ||
264170161Sariff			    !(c->flags & CHN_F_HAS_VCHAN)) {
265157331Sariff				CHN_UNLOCK(c);
266170161Sariff				continue;
267157331Sariff			}
268170161Sariff			CHN_FOREACH_SAFE(ch, c, nch, children) {
269170161Sariff				CHN_LOCK(ch);
270170161Sariff				if (!(ch->flags & CHN_F_BUSY)) {
271170161Sariff					CHN_UNLOCK(ch);
272170161Sariff					CHN_UNLOCK(c);
273170161Sariff					err = vchan_destroy(ch);
274170161Sariff					CHN_LOCK(c);
275170161Sariff					if (err == 0)
276170161Sariff						vcnt--;
277170161Sariff				} else
278170161Sariff					CHN_UNLOCK(ch);
279170815Sariff				if (vcnt == newcnt)
280170161Sariff					break;
281170161Sariff			}
282170161Sariff			CHN_UNLOCK(c);
283157331Sariff			break;
284157331Sariff		}
285170161Sariff		pcm_clonereset(d);
286157331Sariff	}
287157331Sariff
288170815Sariff	return (0);
289157331Sariff}
290157331Sariff
291156929Sariff/* return error status and a locked channel */
292156929Sariffint
293156929Sariffpcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
294170161Sariff    pid_t pid, int devunit)
29577269Scg{
29677269Scg	struct pcm_channel *c;
297170161Sariff	int err, vchancount;
29877269Scg
299170161Sariff	KASSERT(d != NULL && ch != NULL && (devunit == -1 ||
300170161Sariff	    !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) &&
301170161Sariff	    (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
302170815Sariff	    ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d",
303170161Sariff	    __func__, d, ch, direction, pid, devunit));
304170815Sariff	PCM_BUSYASSERT(d);
305170161Sariff
306170161Sariff	/* Double check again. */
307170161Sariff	if (devunit != -1) {
308170161Sariff		switch (snd_unit2d(devunit)) {
309170161Sariff		case SND_DEV_DSPHW_PLAY:
310170161Sariff		case SND_DEV_DSPHW_VPLAY:
311170161Sariff			if (direction != PCMDIR_PLAY)
312170161Sariff				return (EOPNOTSUPP);
313170161Sariff			break;
314170161Sariff		case SND_DEV_DSPHW_REC:
315170161Sariff		case SND_DEV_DSPHW_VREC:
316170161Sariff			if (direction != PCMDIR_REC)
317170161Sariff				return (EOPNOTSUPP);
318170161Sariff			break;
319170161Sariff		default:
320170161Sariff			if (!(direction == PCMDIR_PLAY ||
321170161Sariff			    direction == PCMDIR_REC))
322170161Sariff				return (EOPNOTSUPP);
323170161Sariff			break;
324170161Sariff		}
325170161Sariff	}
326170161Sariff
327156929Sariffretry_chnalloc:
328170815Sariff	err = EOPNOTSUPP;
32978895Scg	/* scan for a free channel */
330170161Sariff	CHN_FOREACH(c, d, channels.pcm) {
33177882Scg		CHN_LOCK(c);
332170161Sariff		if (c->direction == direction && !(c->flags & CHN_F_BUSY) &&
333170161Sariff		    (devunit == -1 || devunit == -2 || c->unit == devunit)) {
334170161Sariff			c->flags |= CHN_F_BUSY;
335170161Sariff			c->pid = pid;
336170161Sariff			*ch = c;
337170161Sariff			return (0);
338170161Sariff		} else if (c->unit == devunit) {
339156929Sariff			if (c->direction != direction)
340156929Sariff				err = EOPNOTSUPP;
341156929Sariff			else if (c->flags & CHN_F_BUSY)
342156929Sariff				err = EBUSY;
343156929Sariff			else
344156929Sariff				err = EINVAL;
345156929Sariff			CHN_UNLOCK(c);
346170161Sariff			return (err);
347170161Sariff		} else if ((devunit == -1 || devunit == -2) &&
348170161Sariff		    c->direction == direction && (c->flags & CHN_F_BUSY))
349157331Sariff			err = EBUSY;
35077882Scg		CHN_UNLOCK(c);
35177269Scg	}
35278895Scg
353170815Sariff	if (devunit == -2)
354170815Sariff		return (err);
355170815Sariff
35678895Scg	/* no channel available */
357170815Sariff	if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY ||
358170815Sariff	    snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) {
359170161Sariff		if (direction == PCMDIR_PLAY)
360170161Sariff			vchancount = d->pvchancount;
361170161Sariff		else
362170161Sariff			vchancount = d->rvchancount;
363170161Sariff		if (!(vchancount > 0 && vchancount < snd_maxautovchans) &&
364170161Sariff		    (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans))
365170161Sariff			return (err);
366170161Sariff		err = pcm_setvchans(d, direction, vchancount + 1,
367170161Sariff		    (devunit == -1) ? -1 : snd_unit2c(devunit));
368157331Sariff		if (err == 0) {
369170161Sariff			if (devunit == -1)
370170161Sariff				devunit = -2;
371157331Sariff			goto retry_chnalloc;
37278895Scg		}
37378895Scg	}
37478895Scg
375170161Sariff	return (err);
37677269Scg}
37777269Scg
37878214Scg/* release a locked channel and unlock it */
37977269Scgint
38078214Scgpcm_chnrelease(struct pcm_channel *c)
38177269Scg{
382170815Sariff	PCM_BUSYASSERT(c->parentsnddev);
38378214Scg	CHN_LOCKASSERT(c);
384170815Sariff
38577269Scg	c->flags &= ~CHN_F_BUSY;
38678214Scg	c->pid = -1;
38777882Scg	CHN_UNLOCK(c);
388170815Sariff
389170815Sariff	return (0);
39077269Scg}
39177269Scg
39277269Scgint
39377269Scgpcm_chnref(struct pcm_channel *c, int ref)
39477269Scg{
395170815Sariff	PCM_BUSYASSERT(c->parentsnddev);
396170815Sariff	CHN_LOCKASSERT(c);
39777882Scg
39877269Scg	c->refcount += ref;
399170815Sariff
400170815Sariff	return (c->refcount);
40177269Scg}
40277269Scg
40382180Scgint
40482180Scgpcm_inprog(struct snddev_info *d, int delta)
40582180Scg{
406170815Sariff	snd_mtxassert(d->lock);
407119096Scg
408170815Sariff	d->inprog += delta;
409119096Scg
410170815Sariff	return (d->inprog);
41182180Scg}
41282180Scg
41382180Scgstatic void
41482180Scgpcm_setmaxautovchans(struct snddev_info *d, int num)
41582180Scg{
416170815Sariff	PCM_BUSYASSERT(d);
417170815Sariff
418170161Sariff	if (num < 0)
419170161Sariff		return;
420170161Sariff
421170161Sariff	if (num >= 0 && d->pvchancount > num)
422170161Sariff		(void)pcm_setvchans(d, PCMDIR_PLAY, num, -1);
423170161Sariff	else if (num > 0 && d->pvchancount == 0)
424170161Sariff		(void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1);
425170161Sariff
426170161Sariff	if (num >= 0 && d->rvchancount > num)
427170161Sariff		(void)pcm_setvchans(d, PCMDIR_REC, num, -1);
428170161Sariff	else if (num > 0 && d->rvchancount == 0)
429170161Sariff		(void)pcm_setvchans(d, PCMDIR_REC, 1, -1);
430170161Sariff
431170161Sariff	pcm_clonereset(d);
43282180Scg}
43382180Scg
43473127Scg#ifdef USING_DEVFS
43565340Scgstatic int
436159732Snetchildsysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
43765340Scg{
43878214Scg	struct snddev_info *d;
43965340Scg	int error, unit;
44065340Scg
44165340Scg	unit = snd_unit;
442170289Sdwmalone	error = sysctl_handle_int(oidp, &unit, 0, req);
44365340Scg	if (error == 0 && req->newptr != NULL) {
44478214Scg		d = devclass_get_softc(pcm_devclass, unit);
445170815Sariff		if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
44678214Scg			return EINVAL;
44765340Scg		snd_unit = unit;
44865340Scg	}
44965340Scg	return (error);
45065340Scg}
451159732Snetchild/* XXX: do we need a way to let the user change the default unit? */
452159732SnetchildSYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW,
453164614Sariff            0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device");
45473127Scg#endif
45565340Scg
45678853Scgstatic int
45782180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
45878853Scg{
45982180Scg	struct snddev_info *d;
46082180Scg	int i, v, error;
46178853Scg
46282180Scg	v = snd_maxautovchans;
463170289Sdwmalone	error = sysctl_handle_int(oidp, &v, 0, req);
46478853Scg	if (error == 0 && req->newptr != NULL) {
465170161Sariff		if (v < 0)
466170161Sariff			v = 0;
467170161Sariff		if (v > SND_MAXVCHANS)
468170161Sariff			v = SND_MAXVCHANS;
469170161Sariff		snd_maxautovchans = v;
470170235Sariff		for (i = 0; pcm_devclass != NULL &&
471170235Sariff		    i < devclass_get_maxunit(pcm_devclass); i++) {
472170161Sariff			d = devclass_get_softc(pcm_devclass, i);
473170815Sariff			if (!PCM_REGISTERED(d))
474170161Sariff				continue;
475170815Sariff			PCM_ACQUIRE_QUICK(d);
476170161Sariff			pcm_setmaxautovchans(d, v);
477170815Sariff			PCM_RELEASE_QUICK(d);
47882180Scg		}
47978853Scg	}
48078853Scg	return (error);
48178853Scg}
48282180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
483164614Sariff            0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
48478853Scg
48577269Scgstruct pcm_channel *
486170161Sariffpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo)
48750724Scg{
488170161Sariff	struct pcm_channel *ch;
489170161Sariff	int direction, err, rpnum, *pnum, max;
490170161Sariff	int udc, device, chan;
491170161Sariff	char *dirs, *devname, buf[CHN_NAMELEN];
49250724Scg
493170815Sariff	PCM_BUSYASSERT(d);
494170815Sariff	snd_mtxassert(d->lock);
495170161Sariff	KASSERT(num >= -1, ("invalid num=%d", num));
496170161Sariff
497170161Sariff
498170815Sariff	switch (dir) {
49977269Scg	case PCMDIR_PLAY:
50077269Scg		dirs = "play";
501126367Struckman		direction = PCMDIR_PLAY;
50283614Scg		pnum = &d->playcount;
503170161Sariff		device = SND_DEV_DSPHW_PLAY;
504170161Sariff		max = SND_MAXHWCHAN;
50577269Scg		break;
506170161Sariff	case PCMDIR_PLAY_VIRTUAL:
507170161Sariff		dirs = "virtual";
508170161Sariff		direction = PCMDIR_PLAY;
509170161Sariff		pnum = &d->pvchancount;
510170161Sariff		device = SND_DEV_DSPHW_VPLAY;
511170161Sariff		max = SND_MAXVCHANS;
512170161Sariff		break;
51377269Scg	case PCMDIR_REC:
51477269Scg		dirs = "record";
515126367Struckman		direction = PCMDIR_REC;
51683614Scg		pnum = &d->reccount;
517170161Sariff		device = SND_DEV_DSPHW_REC;
518170161Sariff		max = SND_MAXHWCHAN;
51977269Scg		break;
520170161Sariff	case PCMDIR_REC_VIRTUAL:
52177269Scg		dirs = "virtual";
522170161Sariff		direction = PCMDIR_REC;
523170161Sariff		pnum = &d->rvchancount;
524170161Sariff		device = SND_DEV_DSPHW_VREC;
525170161Sariff		max = SND_MAXVCHANS;
52677269Scg		break;
52777269Scg	default:
528170815Sariff		return (NULL);
52977269Scg	}
53065340Scg
531170161Sariff	chan = (num == -1) ? 0 : num;
53283614Scg
533170815Sariff	if (*pnum >= max || chan >= max)
534170815Sariff		return (NULL);
535170161Sariff
536156929Sariff	rpnum = 0;
537170161Sariff
538170161Sariff	CHN_FOREACH(ch, d, channels.pcm) {
539170161Sariff		if (CHN_DEV(ch) != device)
540156929Sariff			continue;
541170161Sariff		if (chan == CHN_CHAN(ch)) {
542170161Sariff			if (num != -1) {
543170161Sariff				device_printf(d->dev,
544170161Sariff				    "channel num=%d allocated!\n", chan);
545170815Sariff				return (NULL);
546170161Sariff			}
547170161Sariff			chan++;
548170161Sariff			if (chan >= max) {
549170161Sariff				device_printf(d->dev,
550170161Sariff				    "chan=%d > %d\n", chan, max);
551170815Sariff				return (NULL);
552170161Sariff			}
553156929Sariff		}
554156929Sariff		rpnum++;
555156929Sariff	}
556170161Sariff
557156929Sariff	if (*pnum != rpnum) {
558156929Sariff		device_printf(d->dev,
559170161Sariff		    "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n",
560170161Sariff		    __func__, dirs, *pnum, rpnum);
561170815Sariff		return (NULL);
562156929Sariff	}
563170161Sariff
564170161Sariff	udc = snd_mkunit(device_get_unit(d->dev), device, chan);
565170161Sariff	devname = dsp_unit2name(buf, sizeof(buf), udc);
566170161Sariff
567170161Sariff	if (devname == NULL) {
568170161Sariff		device_printf(d->dev,
569170161Sariff		    "Failed to query device name udc=0x%08x\n", udc);
570170815Sariff		return (NULL);
571170161Sariff	}
572170161Sariff
573170161Sariff	pcm_unlock(d);
574170161Sariff	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
575170161Sariff	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
576170161Sariff	ch->unit = udc;
57777269Scg	ch->pid = -1;
57877269Scg	ch->parentsnddev = d;
57977269Scg	ch->parentchannel = parent;
58089774Sscottl	ch->dev = d->dev;
581170161Sariff	ch->trigger = PCMTRIG_STOP;
582170161Sariff	snprintf(ch->name, sizeof(ch->name), "%s:%s:%s",
583170161Sariff	    device_get_nameunit(ch->dev), dirs, devname);
58477269Scg
585126367Struckman	err = chn_init(ch, devinfo, dir, direction);
586170815Sariff	pcm_lock(d);
58770134Scg	if (err) {
588170161Sariff		device_printf(d->dev, "chn_init(%s) failed: err = %d\n",
589170161Sariff		    ch->name, err);
59077269Scg		kobj_delete(ch->methods, M_DEVBUF);
59177269Scg		free(ch, M_DEVBUF);
592170815Sariff		return (NULL);
59355483Scg	}
59477269Scg
595170815Sariff	return (ch);
59677269Scg}
59777269Scg
59877269Scgint
59977269Scgpcm_chn_destroy(struct pcm_channel *ch)
60077269Scg{
60183614Scg	struct snddev_info *d;
60277269Scg	int err;
60377269Scg
60483614Scg	d = ch->parentsnddev;
605170815Sariff	PCM_BUSYASSERT(d);
606170815Sariff
60777269Scg	err = chn_kill(ch);
60877269Scg	if (err) {
609170815Sariff		device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n",
610170815Sariff		    ch->name, err);
611170815Sariff		return (err);
61277269Scg	}
61377269Scg
61477269Scg	kobj_delete(ch->methods, M_DEVBUF);
61577269Scg	free(ch, M_DEVBUF);
61677269Scg
617170815Sariff	return (0);
61877269Scg}
61977269Scg
62077269Scgint
621124740Smatkpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
62277269Scg{
623170161Sariff	struct pcm_channel *tmp, *after;
624170161Sariff	int num;
62577269Scg
626170815Sariff	PCM_BUSYASSERT(d);
627170815Sariff	snd_mtxassert(d->lock);
628170161Sariff	KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
629170161Sariff	    ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
63077269Scg
631156929Sariff	after = NULL;
632170161Sariff	tmp = NULL;
633170161Sariff	num = 0;
634170161Sariff
635156929Sariff	/*
636170161Sariff	 * Look for possible device collision.
637156929Sariff	 */
638170161Sariff	CHN_FOREACH(tmp, d, channels.pcm) {
639170161Sariff		if (tmp->unit == ch->unit) {
640170161Sariff			device_printf(d->dev, "%s(): Device collision "
641170161Sariff			    "old=%p new=%p devunit=0x%08x\n",
642170161Sariff			    __func__, tmp, ch, ch->unit);
643170815Sariff			return (ENODEV);
644156762Sariff		}
645170161Sariff		if (CHN_DEV(tmp) < CHN_DEV(ch)) {
646170161Sariff			if (num == 0)
647170161Sariff				after = tmp;
648170161Sariff			continue;
649170161Sariff		} else if (CHN_DEV(tmp) > CHN_DEV(ch))
650170161Sariff			break;
651170161Sariff		num++;
652170161Sariff		if (CHN_CHAN(tmp) < CHN_CHAN(ch))
653157331Sariff			after = tmp;
654170161Sariff		else if (CHN_CHAN(tmp) > CHN_CHAN(ch))
655170161Sariff			break;
65682180Scg	}
657170161Sariff
658170161Sariff	if (after != NULL) {
659170161Sariff		CHN_INSERT_AFTER(after, ch, channels.pcm);
660156929Sariff	} else {
661170161Sariff		CHN_INSERT_HEAD(d, ch, channels.pcm);
662156929Sariff	}
663156929Sariff
664170815Sariff	switch (CHN_DEV(ch)) {
665170815Sariff	case SND_DEV_DSPHW_PLAY:
666170815Sariff		d->playcount++;
667170815Sariff		break;
668170815Sariff	case SND_DEV_DSPHW_VPLAY:
669170815Sariff		d->pvchancount++;
670170815Sariff		break;
671170815Sariff	case SND_DEV_DSPHW_REC:
672170815Sariff		d->reccount++;
673170815Sariff		break;
674170815Sariff	case SND_DEV_DSPHW_VREC:
675170815Sariff		d->rvchancount++;
676170815Sariff		break;
677170815Sariff	default:
678170815Sariff		break;
679170815Sariff	}
680170815Sariff
681170161Sariff	d->devcount++;
682164614Sariff
683170815Sariff	return (0);
68450724Scg}
68550724Scg
68677269Scgint
687124740Smatkpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
68865340Scg{
689170161Sariff	struct pcm_channel *tmp;
69065340Scg
691170815Sariff	PCM_BUSYASSERT(d);
692170815Sariff	snd_mtxassert(d->lock);
693170815Sariff
694170161Sariff	tmp = NULL;
695119096Scg
696170161Sariff	CHN_FOREACH(tmp, d, channels.pcm) {
697170161Sariff		if (tmp == ch)
698170161Sariff			break;
69965340Scg	}
700107237Scg
701170161Sariff	if (tmp != ch)
702170815Sariff		return (EINVAL);
703170161Sariff
704170161Sariff	CHN_REMOVE(d, ch, channels.pcm);
705170815Sariff
706170161Sariff	switch (CHN_DEV(ch)) {
707170161Sariff	case SND_DEV_DSPHW_PLAY:
708170161Sariff		d->playcount--;
709170161Sariff		break;
710170161Sariff	case SND_DEV_DSPHW_VPLAY:
711170161Sariff		d->pvchancount--;
712170161Sariff		break;
713170161Sariff	case SND_DEV_DSPHW_REC:
714107237Scg		d->reccount--;
715170161Sariff		break;
716170161Sariff	case SND_DEV_DSPHW_VREC:
717170161Sariff		d->rvchancount--;
718170161Sariff		break;
719170161Sariff	default:
720170161Sariff		break;
721170161Sariff	}
722107237Scg
723170815Sariff	d->devcount--;
724170815Sariff
725170815Sariff	return (0);
72665340Scg}
72765340Scg
72850724Scgint
72977269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
73077269Scg{
731157331Sariff	struct snddev_info *d = device_get_softc(dev);
73282180Scg	struct pcm_channel *ch;
733157331Sariff	int err;
73477269Scg
735170815Sariff	PCM_BUSYASSERT(d);
736170815Sariff
737170815Sariff	pcm_lock(d);
738170161Sariff	ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo);
73977269Scg	if (!ch) {
740170815Sariff		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n",
741170815Sariff		    cls->name, dir, devinfo);
742170815Sariff		pcm_unlock(d);
743170815Sariff		return (ENODEV);
74477269Scg	}
74578853Scg
746124740Smatk	err = pcm_chn_add(d, ch);
747170815Sariff	pcm_unlock(d);
74877269Scg	if (err) {
749170815Sariff		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n",
750170815Sariff		    ch->name, err);
75177269Scg		pcm_chn_destroy(ch);
75277269Scg	}
75377269Scg
754170815Sariff	return (err);
75577269Scg}
75677269Scg
75777269Scgstatic int
75877269Scgpcm_killchan(device_t dev)
75977269Scg{
760157331Sariff	struct snddev_info *d = device_get_softc(dev);
761106420Scognet	struct pcm_channel *ch;
762170815Sariff	int error;
76377269Scg
764170815Sariff	PCM_BUSYASSERT(d);
765170815Sariff
766170161Sariff	ch = CHN_FIRST(d, channels.pcm);
76777269Scg
768170815Sariff	pcm_lock(d);
769170161Sariff	error = pcm_chn_remove(d, ch);
770170815Sariff	pcm_unlock(d);
771106420Scognet	if (error)
772106420Scognet		return (error);
773106420Scognet	return (pcm_chn_destroy(ch));
77477269Scg}
77577269Scg
77677269Scgint
77750724Scgpcm_setstatus(device_t dev, char *str)
77850724Scg{
779157331Sariff	struct snddev_info *d = device_get_softc(dev);
78077882Scg
781170815Sariff	PCM_BUSYASSERT(d);
782170815Sariff
783170815Sariff	if (d->playcount == 0 || d->reccount == 0)
784170815Sariff		d->flags |= SD_F_SIMPLEX;
785170815Sariff
786170815Sariff	if ((d->playcount > 0 || d->reccount > 0) &&
787170815Sariff	    !(d->flags & SD_F_AUTOVCHAN)) {
788170815Sariff		d->flags |= SD_F_AUTOVCHAN;
789170815Sariff		vchan_initsys(dev);
790170815Sariff	}
791170815Sariff
792170161Sariff	pcm_setmaxautovchans(d, snd_maxautovchans);
793170161Sariff
794170815Sariff	strlcpy(d->status, str, SND_STATUSLEN);
795170815Sariff
796170161Sariff	pcm_lock(d);
797170161Sariff
798170161Sariff	/* Last stage, enable cloning. */
799170815Sariff	if (d->clones != NULL)
800170161Sariff		(void)snd_clone_enable(d->clones);
801170161Sariff
802170815Sariff	/* Done, we're ready.. */
803170815Sariff	d->flags |= SD_F_REGISTERED;
804170815Sariff
805170815Sariff	PCM_RELEASE(d);
806170815Sariff
807170161Sariff	pcm_unlock(d);
808170161Sariff
809170885Sariff	if (snd_unit < 0 || snd_unit_auto != 0)
810170884Sariff		snd_unit = device_get_unit(dev);
811170884Sariff
812170815Sariff	return (0);
81350724Scg}
81450724Scg
815157331Sariffuint32_t
81650724Scgpcm_getflags(device_t dev)
81750724Scg{
818157331Sariff	struct snddev_info *d = device_get_softc(dev);
81977882Scg
82050724Scg	return d->flags;
82150724Scg}
82250724Scg
82350724Scgvoid
824157331Sariffpcm_setflags(device_t dev, uint32_t val)
82550724Scg{
826157331Sariff	struct snddev_info *d = device_get_softc(dev);
82778362Scg
82850724Scg	d->flags = val;
82950724Scg}
83050724Scg
83158384Scgvoid *
83258384Scgpcm_getdevinfo(device_t dev)
83358384Scg{
834157331Sariff	struct snddev_info *d = device_get_softc(dev);
83577882Scg
83658384Scg	return d->devinfo;
83758384Scg}
83858384Scg
83983614Scgunsigned int
840160439Snetchildpcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
84183614Scg{
842157331Sariff	struct snddev_info *d = device_get_softc(dev);
84389690Scg	int sz, x;
84483614Scg
84583614Scg	sz = 0;
84689690Scg	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
84789690Scg		x = sz;
848160439Snetchild		RANGE(sz, minbufsz, maxbufsz);
84989690Scg		if (x != sz)
850160439Snetchild			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
851160439Snetchild		x = minbufsz;
85289690Scg		while (x < sz)
85389690Scg			x <<= 1;
85489690Scg		if (x > sz)
85589690Scg			x >>= 1;
85689690Scg		if (x != sz) {
85789690Scg			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
85889690Scg			sz = x;
85989690Scg		}
86089690Scg	} else {
86183614Scg		sz = deflt;
86289690Scg	}
86389690Scg
86483614Scg	d->bufsz = sz;
86583614Scg
86683614Scg	return sz;
86783614Scg}
86883614Scg
869170161Sariff#if defined(SND_DYNSYSCTL) && defined(SND_DEBUG)
870170161Sariffstatic int
871170161Sariffsysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS)
872170161Sariff{
873170161Sariff	struct snddev_info *d;
874170161Sariff	uint32_t flags;
875170161Sariff	int err;
876170161Sariff
877170161Sariff	d = oidp->oid_arg1;
878170815Sariff	if (!PCM_REGISTERED(d) || d->clones == NULL)
879170161Sariff		return (ENODEV);
880170161Sariff
881170815Sariff	PCM_ACQUIRE_QUICK(d);
882170815Sariff
883170161Sariff	flags = snd_clone_getflags(d->clones);
884170289Sdwmalone	err = sysctl_handle_int(oidp, &flags, 0, req);
885170161Sariff
886170161Sariff	if (err == 0 && req->newptr != NULL) {
887170815Sariff		if (flags & ~SND_CLONE_MASK)
888170161Sariff			err = EINVAL;
889170815Sariff		else
890170161Sariff			(void)snd_clone_setflags(d->clones, flags);
891170161Sariff	}
892170161Sariff
893170815Sariff	PCM_RELEASE_QUICK(d);
894170815Sariff
895170161Sariff	return (err);
896170161Sariff}
897170161Sariff
898170161Sariffstatic int
899170161Sariffsysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS)
900170161Sariff{
901170161Sariff	struct snddev_info *d;
902170161Sariff	int err, deadline;
903170161Sariff
904170161Sariff	d = oidp->oid_arg1;
905170815Sariff	if (!PCM_REGISTERED(d) || d->clones == NULL)
906170161Sariff		return (ENODEV);
907170161Sariff
908170815Sariff	PCM_ACQUIRE_QUICK(d);
909170815Sariff
910170161Sariff	deadline = snd_clone_getdeadline(d->clones);
911170289Sdwmalone	err = sysctl_handle_int(oidp, &deadline, 0, req);
912170161Sariff
913170161Sariff	if (err == 0 && req->newptr != NULL) {
914170161Sariff		if (deadline < 0)
915170161Sariff			err = EINVAL;
916170815Sariff		else
917170161Sariff			(void)snd_clone_setdeadline(d->clones, deadline);
918170161Sariff	}
919170161Sariff
920170815Sariff	PCM_RELEASE_QUICK(d);
921170815Sariff
922170161Sariff	return (err);
923170161Sariff}
924170161Sariff
925170161Sariffstatic int
926170161Sariffsysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS)
927170161Sariff{
928170161Sariff	struct snddev_info *d;
929170161Sariff	int err, val;
930170161Sariff
931170161Sariff	d = oidp->oid_arg1;
932170815Sariff	if (!PCM_REGISTERED(d) || d->clones == NULL)
933170161Sariff		return (ENODEV);
934170161Sariff
935170161Sariff	val = 0;
936170289Sdwmalone	err = sysctl_handle_int(oidp, &val, 0, req);
937170161Sariff
938170161Sariff	if (err == 0 && req->newptr != NULL && val != 0) {
939170815Sariff		PCM_ACQUIRE_QUICK(d);
940170815Sariff		val = snd_clone_gc(d->clones);
941170815Sariff		PCM_RELEASE_QUICK(d);
942170815Sariff		if (bootverbose != 0 || snd_verbose > 3)
943170815Sariff			device_printf(d->dev, "clone gc: pruned=%d\n", val);
944170161Sariff	}
945170161Sariff
946170161Sariff	return (err);
947170161Sariff}
948170161Sariff
949170161Sariffstatic int
950170161Sariffsysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS)
951170161Sariff{
952170161Sariff	struct snddev_info *d;
953170161Sariff	int i, err, val;
954170161Sariff
955170161Sariff	val = 0;
956170289Sdwmalone	err = sysctl_handle_int(oidp, &val, 0, req);
957170161Sariff
958170161Sariff	if (err == 0 && req->newptr != NULL && val != 0) {
959170235Sariff		for (i = 0; pcm_devclass != NULL &&
960170235Sariff		    i < devclass_get_maxunit(pcm_devclass); i++) {
961170161Sariff			d = devclass_get_softc(pcm_devclass, i);
962170815Sariff			if (!PCM_REGISTERED(d) || d->clones == NULL)
963170161Sariff				continue;
964170815Sariff			PCM_ACQUIRE_QUICK(d);
965170815Sariff			val = snd_clone_gc(d->clones);
966170815Sariff			PCM_RELEASE_QUICK(d);
967170815Sariff			if (bootverbose != 0 || snd_verbose > 3)
968170815Sariff				device_printf(d->dev, "clone gc: pruned=%d\n",
969170815Sariff				    val);
970170161Sariff		}
971170161Sariff	}
972170161Sariff
973170161Sariff	return (err);
974170161Sariff}
975170161SariffSYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW,
976170161Sariff    0, sizeof(int), sysctl_hw_snd_clone_gc, "I",
977170161Sariff    "global clone garbage collector");
978170161Sariff#endif
979170161Sariff
98050724Scgint
98150724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec)
98250724Scg{
983170161Sariff	struct snddev_info *d;
98450724Scg
98589834Scg	if (pcm_veto_load) {
98689834Scg		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
98789834Scg
98889834Scg		return EINVAL;
98989834Scg	}
99089834Scg
991170161Sariff	if (device_get_unit(dev) > PCMMAXUNIT) {
992170161Sariff		device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n",
993170161Sariff		    device_get_unit(dev), PCMMAXUNIT);
994170161Sariff		device_printf(dev,
995170161Sariff		    "Use 'hw.snd.maxunit' tunable to raise the limit.\n");
996170161Sariff		return ENODEV;
997170161Sariff	}
998170161Sariff
999170161Sariff	d = device_get_softc(dev);
1000170815Sariff	d->dev = dev;
100193814Sjhb	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
1002170815Sariff	cv_init(&d->cv, device_get_nameunit(dev));
1003170815Sariff	PCM_ACQUIRE_QUICK(d);
1004170815Sariff	dsp_cdevinfo_init(d);
1005148587Snetchild#if 0
1006148587Snetchild	/*
1007148587Snetchild	 * d->flags should be cleared by the allocator of the softc.
1008148587Snetchild	 * We cannot clear this field here because several devices set
1009148587Snetchild	 * this flag before calling pcm_register().
1010148587Snetchild	 */
101178853Scg	d->flags = 0;
1012148587Snetchild#endif
101350724Scg	d->devinfo = devinfo;
101478895Scg	d->devcount = 0;
101583089Scg	d->reccount = 0;
101683614Scg	d->playcount = 0;
1017170161Sariff	d->pvchancount = 0;
1018170161Sariff	d->rvchancount = 0;
1019170161Sariff	d->pvchanrate = 0;
1020170161Sariff	d->pvchanformat = 0;
1021170161Sariff	d->rvchanrate = 0;
1022170161Sariff	d->rvchanformat = 0;
102378362Scg	d->inprog = 0;
102450724Scg
1025170161Sariff	/*
1026170161Sariff	 * Create clone manager, disabled by default. Cloning will be
1027170161Sariff	 * enabled during final stage of driver iniialization through
1028170161Sariff	 * pcm_setstatus().
1029170161Sariff	 */
1030170815Sariff	d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE,
1031170815Sariff	    SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK |
1032170161Sariff	    SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF |
1033170161Sariff	    SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED);
1034124617Sphk
1035170161Sariff	if (bootverbose != 0 || snd_verbose > 3) {
1036170161Sariff		device_printf(dev,
1037170161Sariff		    "clone manager: deadline=%dms flags=0x%08x\n",
1038170161Sariff		    snd_clone_getdeadline(d->clones),
1039170161Sariff		    snd_clone_getflags(d->clones));
1040170161Sariff	}
1041170161Sariff
1042170161Sariff	CHN_INIT(d, channels.pcm);
1043170161Sariff	CHN_INIT(d, channels.pcm.busy);
1044170161Sariff
1045170815Sariff	/* XXX This is incorrect, but lets play along for now. */
1046157331Sariff	if ((numplay == 0 || numrec == 0) && numplay != numrec)
104778214Scg		d->flags |= SD_F_SIMPLEX;
104878362Scg
104978214Scg	d->fakechan = fkchan_setup(dev);
1050126367Struckman	chn_init(d->fakechan, NULL, 0, 0);
105155494Scg
105273127Scg#ifdef SND_DYNSYSCTL
1053170161Sariff	sysctl_ctx_init(&d->play_sysctl_ctx);
1054170161Sariff	d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
1055170161Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
1056170161Sariff	    CTLFLAG_RD, 0, "playback channels node");
1057170161Sariff	sysctl_ctx_init(&d->rec_sysctl_ctx);
1058170161Sariff	d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
1059170161Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
1060170161Sariff	    CTLFLAG_RD, 0, "record channels node");
1061159732Snetchild	/* XXX: an user should be able to set this with a control tool, the
1062159732Snetchild	   sysadmin then needs min+max sysctls for this */
1063164614Sariff	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
1064164614Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
1065164614Sariff            OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
1066170161Sariff#ifdef SND_DEBUG
1067170161Sariff	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1068170161Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1069170161Sariff	    "clone_flags", CTLTYPE_UINT | CTLFLAG_RW, d, sizeof(d),
1070170161Sariff	    sysctl_dev_pcm_clone_flags, "IU",
1071170161Sariff	    "clone flags");
1072170161Sariff	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1073170161Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1074170161Sariff	    "clone_deadline", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
1075170161Sariff	    sysctl_dev_pcm_clone_deadline, "I",
1076170161Sariff	    "clone expiration deadline (ms)");
1077170161Sariff	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1078170161Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1079170161Sariff	    "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
1080170161Sariff	    sysctl_dev_pcm_clone_gc, "I",
1081170161Sariff	    "clone garbage collector");
108273127Scg#endif
1083170161Sariff#endif
1084170815Sariff
1085170161Sariff	if (numplay > 0 || numrec > 0) {
1086156929Sariff		d->flags |= SD_F_AUTOVCHAN;
108782180Scg		vchan_initsys(dev);
1088149953Snetchild	}
108978853Scg
109082180Scg	sndstat_register(dev, d->status, sndstat_prepare_pcm);
1091170815Sariff
1092157331Sariff	return 0;
109350724Scg}
109450724Scg
109565340Scgint
109665340Scgpcm_unregister(device_t dev)
109765207Scg{
1098170815Sariff	struct snddev_info *d;
109983614Scg	struct pcm_channel *ch;
1100170161Sariff	struct thread *td;
1101170161Sariff	int i;
110265207Scg
1103170161Sariff	td = curthread;
1104170815Sariff	d = device_get_softc(dev);
1105170161Sariff
1106170815Sariff	if (!PCM_ALIVE(d)) {
1107170815Sariff		device_printf(dev, "unregister: device not configured\n");
1108170815Sariff		return (0);
1109170815Sariff	}
1110170815Sariff
1111170161Sariff	if (sndstat_acquire(td) != 0) {
1112150827Snetchild		device_printf(dev, "unregister: sndstat busy\n");
1113170815Sariff		return (EBUSY);
1114150827Snetchild	}
1115150827Snetchild
1116170161Sariff	pcm_lock(d);
1117170815Sariff	PCM_WAIT(d);
1118170815Sariff
1119170815Sariff	if (d->inprog != 0) {
112083476Sgreid		device_printf(dev, "unregister: operation in progress\n");
1121170161Sariff		pcm_unlock(d);
1122170161Sariff		sndstat_release(td);
1123170815Sariff		return (EBUSY);
112478362Scg	}
1125124740Smatk
1126170815Sariff	PCM_ACQUIRE(d);
1127170815Sariff	pcm_unlock(d);
1128170815Sariff
1129170161Sariff	CHN_FOREACH(ch, d, channels.pcm) {
1130170815Sariff		CHN_LOCK(ch);
113183614Scg		if (ch->refcount > 0) {
1132170815Sariff			device_printf(dev,
1133170815Sariff			    "unregister: channel %s busy (pid %d)\n",
1134170815Sariff			    ch->name, ch->pid);
1135170815Sariff			CHN_UNLOCK(ch);
1136170815Sariff			PCM_RELEASE_QUICK(d);
1137170161Sariff			sndstat_release(td);
1138170815Sariff			return (EBUSY);
113977269Scg		}
1140170815Sariff		CHN_UNLOCK(ch);
114165487Scg	}
1142124740Smatk
1143170161Sariff	if (d->clones != NULL) {
1144170161Sariff		if (snd_clone_busy(d->clones) != 0) {
1145170161Sariff			device_printf(dev, "unregister: clone busy\n");
1146170815Sariff			PCM_RELEASE_QUICK(d);
1147170161Sariff			sndstat_release(td);
1148170815Sariff			return (EBUSY);
1149170815Sariff		} else {
1150170815Sariff			pcm_lock(d);
1151170161Sariff			(void)snd_clone_disable(d->clones);
1152170815Sariff			pcm_unlock(d);
1153170815Sariff		}
1154170161Sariff	}
1155170161Sariff
1156157022Sariff	if (mixer_uninit(dev) == EBUSY) {
1157148587Snetchild		device_printf(dev, "unregister: mixer busy\n");
1158170815Sariff		pcm_lock(d);
1159170161Sariff		if (d->clones != NULL)
1160170161Sariff			(void)snd_clone_enable(d->clones);
1161170815Sariff		PCM_RELEASE(d);
1162170161Sariff		pcm_unlock(d);
1163170161Sariff		sndstat_release(td);
1164170815Sariff		return (EBUSY);
1165148587Snetchild	}
1166148587Snetchild
1167170815Sariff	pcm_lock(d);
1168170815Sariff	d->flags |= SD_F_DYING;
1169170815Sariff	d->flags &= ~SD_F_REGISTERED;
1170170815Sariff	pcm_unlock(d);
1171170815Sariff
1172170815Sariff	/*
1173170815Sariff	 * No lock being held, so this thing can be flushed without
1174170815Sariff	 * stucking into devdrn oblivion.
1175170815Sariff	 */
1176170161Sariff	if (d->clones != NULL) {
1177170161Sariff		snd_clone_destroy(d->clones);
1178170161Sariff		d->clones = NULL;
1179124740Smatk	}
1180124740Smatk
118174763Scg#ifdef SND_DYNSYSCTL
1182170161Sariff	if (d->play_sysctl_tree != NULL) {
1183170161Sariff		sysctl_ctx_free(&d->play_sysctl_ctx);
1184170161Sariff		d->play_sysctl_tree = NULL;
1185170161Sariff	}
1186170161Sariff	if (d->rec_sysctl_tree != NULL) {
1187170161Sariff		sysctl_ctx_free(&d->rec_sysctl_ctx);
1188170161Sariff		d->rec_sysctl_tree = NULL;
1189170161Sariff	}
119074763Scg#endif
1191157331Sariff
1192170161Sariff	while (!CHN_EMPTY(d, channels.pcm))
119377269Scg		pcm_killchan(dev);
119465207Scg
119574763Scg	chn_kill(d->fakechan);
119674763Scg	fkchan_kill(d->fakechan);
119773127Scg
1198170815Sariff	dsp_cdevinfo_flush(d);
1199170815Sariff
1200170815Sariff	pcm_lock(d);
1201170815Sariff	PCM_RELEASE(d);
1202170815Sariff	cv_destroy(&d->cv);
1203170161Sariff	pcm_unlock(d);
120477882Scg	snd_mtxfree(d->lock);
1205150827Snetchild	sndstat_unregister(dev);
1206170161Sariff	sndstat_release(td);
1207170161Sariff
1208170161Sariff	if (snd_unit == device_get_unit(dev)) {
1209170161Sariff		/*
1210170884Sariff		 * Reassign default unit to the next available dev, but
1211170884Sariff		 * first, reset snd_unit to something ridiculous.
1212170161Sariff		 */
1213170884Sariff		snd_unit = -1;
1214170235Sariff		for (i = 0; pcm_devclass != NULL &&
1215170235Sariff		    i < devclass_get_maxunit(pcm_devclass); i++) {
1216170815Sariff			if (device_get_unit(dev) == i)
1217170161Sariff				continue;
1218170815Sariff			d = devclass_get_softc(pcm_devclass, i);
1219170815Sariff			if (PCM_REGISTERED(d)) {
1220170815Sariff				snd_unit = i;
1221170815Sariff				break;
1222170815Sariff			}
1223170161Sariff		}
1224170161Sariff	}
1225170161Sariff
1226170815Sariff	return (0);
122765207Scg}
122865207Scg
122982180Scg/************************************************************************/
123082180Scg
123182180Scgstatic int
123282180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
123382180Scg{
1234157331Sariff	struct snddev_info *d;
123582180Scg	struct pcm_channel *c;
123682180Scg	struct pcm_feeder *f;
123782180Scg
123882180Scg	if (verbose < 1)
123982180Scg		return 0;
124082180Scg
124182180Scg	d = device_get_softc(dev);
124282180Scg	if (!d)
124382180Scg		return ENXIO;
124482180Scg
1245170815Sariff	PCM_BUSYASSERT(d);
1246170815Sariff
1247170815Sariff	if (CHN_EMPTY(d, channels.pcm)) {
1248170815Sariff		sbuf_printf(s, " (mixer only)");
1249170815Sariff		return 0;
1250170815Sariff	}
1251170815Sariff
1252170815Sariff	sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)",
1253170815Sariff			d->playcount, d->pvchancount,
1254170815Sariff			d->reccount, d->rvchancount,
1255170815Sariff			(d->flags & SD_F_SIMPLEX)? "" : " duplex",
125682180Scg#ifdef USING_DEVFS
1257170815Sariff			(device_get_unit(dev) == snd_unit)? " default" : ""
125882180Scg#else
1259170815Sariff			""
126082180Scg#endif
1261170815Sariff			);
1262119096Scg
1263170815Sariff	if (verbose <= 1)
1264170815Sariff		return 0;
1265119096Scg
1266170815Sariff	CHN_FOREACH(c, d, channels.pcm) {
1267155342Snetchild
1268170815Sariff		KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
1269170815Sariff			("hosed pcm channel setup"));
1270155342Snetchild
1271170815Sariff		sbuf_printf(s, "\n\t");
127282479Scg
1273170815Sariff		/* it would be better to indent child channels */
1274170815Sariff		sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
1275170815Sariff		sbuf_printf(s, "spd %d", c->speed);
1276170815Sariff		if (c->speed != sndbuf_getspd(c->bufhard))
1277170815Sariff			sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
1278170815Sariff		sbuf_printf(s, ", fmt 0x%08x", c->format);
1279170815Sariff		if (c->format != sndbuf_getfmt(c->bufhard))
1280170815Sariff			sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1281170815Sariff		sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
1282170815Sariff		if (c->pid != -1)
1283170815Sariff			sbuf_printf(s, ", pid %d", c->pid);
1284170815Sariff		sbuf_printf(s, "\n\t");
128589691Scg
1286170815Sariff		sbuf_printf(s, "interrupts %d, ", c->interrupts);
1287170815Sariff		if (c->direction == PCMDIR_REC)
1288170815Sariff			sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1289170815Sariff				c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
1290170815Sariff				sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1291170815Sariff				sndbuf_getblkcnt(c->bufhard),
1292170815Sariff				sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1293170815Sariff				sndbuf_getblkcnt(c->bufsoft));
1294170815Sariff		else
1295170815Sariff			sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1296170815Sariff				c->xruns, c->feedcount, sndbuf_getready(c->bufsoft),
1297170815Sariff				sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1298170815Sariff				sndbuf_getblkcnt(c->bufhard),
1299170815Sariff				sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1300170815Sariff				sndbuf_getblkcnt(c->bufsoft));
1301170815Sariff		sbuf_printf(s, "\n\t");
130289691Scg
1303170815Sariff		sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
1304170815Sariff		sbuf_printf(s, " -> ");
1305170815Sariff		f = c->feeder;
1306170815Sariff		while (f->source != NULL)
1307170815Sariff			f = f->source;
1308170815Sariff		while (f != NULL) {
1309170815Sariff			sbuf_printf(s, "%s", f->class->name);
1310170815Sariff			if (f->desc->type == FEEDER_FMT)
1311170815Sariff				sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
1312170815Sariff			if (f->desc->type == FEEDER_RATE)
1313170815Sariff				sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
1314170815Sariff			if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
1315170815Sariff					f->desc->type == FEEDER_VOLUME)
1316170815Sariff				sbuf_printf(s, "(0x%08x)", f->desc->out);
131789691Scg			sbuf_printf(s, " -> ");
1318170815Sariff			f = f->parent;
131982180Scg		}
1320170815Sariff		sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
1321170815Sariff	}
132282180Scg
132382180Scg	return 0;
132482180Scg}
132582180Scg
132682180Scg/************************************************************************/
132782180Scg
132882180Scg#ifdef SND_DYNSYSCTL
132982180Scgint
133082180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
133182180Scg{
133282180Scg	struct snddev_info *d;
1333170161Sariff	int direction, vchancount;
1334170815Sariff	int err, cnt;
133582180Scg
1336170161Sariff	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
1337170815Sariff	if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
1338170815Sariff		return (EINVAL);
133982180Scg
1340170815Sariff	pcm_lock(d);
1341170815Sariff	PCM_WAIT(d);
1342170815Sariff
1343170161Sariff	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
1344170161Sariff	case VCHAN_PLAY:
1345170161Sariff		direction = PCMDIR_PLAY;
1346170161Sariff		vchancount = d->pvchancount;
1347170815Sariff		cnt = d->playcount;
1348170161Sariff		break;
1349170161Sariff	case VCHAN_REC:
1350170161Sariff		direction = PCMDIR_REC;
1351170161Sariff		vchancount = d->rvchancount;
1352170815Sariff		cnt = d->reccount;
1353170161Sariff		break;
1354170161Sariff	default:
1355170815Sariff		pcm_unlock(d);
1356170815Sariff		return (EINVAL);
1357170161Sariff		break;
1358170161Sariff	}
1359170161Sariff
1360170815Sariff	if (cnt < 1) {
1361170815Sariff		pcm_unlock(d);
1362170815Sariff		return (ENODEV);
1363170815Sariff	}
1364119096Scg
1365170815Sariff	PCM_ACQUIRE(d);
1366170815Sariff	pcm_unlock(d);
1367170815Sariff
1368170815Sariff	cnt = vchancount;
1369170815Sariff	err = sysctl_handle_int(oidp, &cnt, 0, req);
1370170815Sariff
1371170815Sariff	if (err == 0 && req->newptr != NULL && vchancount != cnt) {
1372170815Sariff		if (cnt < 0)
1373170815Sariff			cnt = 0;
1374170815Sariff		if (cnt > SND_MAXVCHANS)
1375170815Sariff			cnt = SND_MAXVCHANS;
1376170815Sariff		err = pcm_setvchans(d, direction, cnt, -1);
1377170161Sariff	}
1378119096Scg
1379170815Sariff	PCM_RELEASE_QUICK(d);
1380170815Sariff
138182180Scg	return err;
138282180Scg}
138382180Scg#endif
138482180Scg
138582180Scg/************************************************************************/
138682180Scg
1387162588Snetchild/**
1388162588Snetchild * @brief	Handle OSSv4 SNDCTL_SYSINFO ioctl.
1389162588Snetchild *
1390162588Snetchild * @param si	Pointer to oss_sysinfo struct where information about the
1391162588Snetchild * 		sound subsystem will be written/copied.
1392162588Snetchild *
1393162588Snetchild * This routine returns information about the sound system, such as the
1394162588Snetchild * current OSS version, number of audio, MIDI, and mixer drivers, etc.
1395162588Snetchild * Also includes a bitmask showing which of the above types of devices
1396162588Snetchild * are open (busy).
1397162588Snetchild *
1398162588Snetchild * @note
1399162588Snetchild * Calling threads must not hold any snddev_info or pcm_channel locks.
1400162588Snetchild *
1401162588Snetchild * @author	Ryan Beasley <ryanb@FreeBSD.org>
1402162588Snetchild */
1403162588Snetchildvoid
1404162588Snetchildsound_oss_sysinfo(oss_sysinfo *si)
1405162588Snetchild{
1406162588Snetchild	static char si_product[] = "FreeBSD native OSS ABI";
1407162588Snetchild	static char si_version[] = __XSTRING(__FreeBSD_version);
1408162588Snetchild	static int intnbits = sizeof(int) * 8;	/* Better suited as macro?
1409162588Snetchild						   Must pester a C guru. */
1410162588Snetchild
1411162588Snetchild	struct snddev_info *d;
1412162588Snetchild	struct pcm_channel *c;
1413162588Snetchild	int i, j, ncards;
1414162588Snetchild
1415162588Snetchild	ncards = 0;
1416162588Snetchild
1417162588Snetchild	strlcpy(si->product, si_product, sizeof(si->product));
1418162588Snetchild	strlcpy(si->version, si_version, sizeof(si->version));
1419162588Snetchild	si->versionnum = SOUND_VERSION;
1420162588Snetchild
1421162588Snetchild	/*
1422162588Snetchild	 * Iterate over PCM devices and their channels, gathering up data
1423162588Snetchild	 * for the numaudios, ncards, and openedaudio fields.
1424162588Snetchild	 */
1425162588Snetchild	si->numaudios = 0;
1426162588Snetchild	bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
1427162588Snetchild
1428170815Sariff	j = 0;
1429162588Snetchild
1430170815Sariff	for (i = 0; pcm_devclass != NULL &&
1431170815Sariff	    i < devclass_get_maxunit(pcm_devclass); i++) {
1432170815Sariff		d = devclass_get_softc(pcm_devclass, i);
1433170815Sariff		if (!PCM_REGISTERED(d))
1434170815Sariff			continue;
1435162588Snetchild
1436170815Sariff		/* XXX Need Giant magic entry ??? */
1437162588Snetchild
1438170815Sariff		/* See note in function's docblock */
1439170815Sariff		mtx_assert(d->lock, MA_NOTOWNED);
1440170815Sariff		pcm_lock(d);
1441162588Snetchild
1442170815Sariff		si->numaudios += d->devcount;
1443170815Sariff		++ncards;
1444162588Snetchild
1445170815Sariff		CHN_FOREACH(c, d, channels.pcm) {
1446170815Sariff			mtx_assert(c->lock, MA_NOTOWNED);
1447170815Sariff			CHN_LOCK(c);
1448170815Sariff			if (c->flags & CHN_F_BUSY)
1449170815Sariff				si->openedaudio[j / intnbits] |=
1450170815Sariff				    (1 << (j % intnbits));
1451170815Sariff			CHN_UNLOCK(c);
1452170815Sariff			j++;
1453162588Snetchild		}
1454170815Sariff
1455170815Sariff		pcm_unlock(d);
1456162588Snetchild	}
1457162588Snetchild
1458162588Snetchild	si->numsynths = 0;	/* OSSv4 docs:  this field is obsolete */
1459162588Snetchild	/**
1460162588Snetchild	 * @todo	Collect num{midis,timers}.
1461162588Snetchild	 *
1462162588Snetchild	 * Need access to sound/midi/midi.c::midistat_lock in order
1463162588Snetchild	 * to safely touch midi_devices and get a head count of, well,
1464162588Snetchild	 * MIDI devices.  midistat_lock is a global static (i.e., local to
1465162588Snetchild	 * midi.c), but midi_devices is a regular global; should the mutex
1466162588Snetchild	 * be publicized, or is there another way to get this information?
1467162588Snetchild	 *
1468162588Snetchild	 * NB:	MIDI/sequencer stuff is currently on hold.
1469162588Snetchild	 */
1470162588Snetchild	si->nummidis = 0;
1471162588Snetchild	si->numtimers = 0;
1472162588Snetchild	si->nummixers = mixer_count;
1473162588Snetchild	si->numcards = ncards;
1474162588Snetchild		/* OSSv4 docs:	Intended only for test apps; API doesn't
1475162588Snetchild		   really have much of a concept of cards.  Shouldn't be
1476162588Snetchild		   used by applications. */
1477162588Snetchild
1478162588Snetchild	/**
1479162588Snetchild	 * @todo	Fill in "busy devices" fields.
1480162588Snetchild	 *
1481162588Snetchild	 *  si->openedmidi = " MIDI devices
1482162588Snetchild	 */
1483162588Snetchild	bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
1484162588Snetchild
1485162588Snetchild	/*
1486162588Snetchild	 * Si->filler is a reserved array, but according to docs each
1487162588Snetchild	 * element should be set to -1.
1488162588Snetchild	 */
1489162588Snetchild	for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++)
1490162588Snetchild		si->filler[i] = -1;
1491162588Snetchild}
1492162588Snetchild
1493162588Snetchild/************************************************************************/
1494162588Snetchild
1495132236Stanimurastatic int
1496132236Stanimurasound_modevent(module_t mod, int type, void *data)
1497132236Stanimura{
1498162588Snetchild	int ret;
1499148587Snetchild#if 0
1500132236Stanimura	return (midi_modevent(mod, type, data));
1501148587Snetchild#else
1502162588Snetchild	ret = 0;
1503162588Snetchild
1504162588Snetchild	switch(type) {
1505162588Snetchild		case MOD_LOAD:
1506162588Snetchild			pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
1507162588Snetchild			break;
1508162588Snetchild		case MOD_UNLOAD:
1509162588Snetchild		case MOD_SHUTDOWN:
1510170161Sariff			ret = sndstat_acquire(curthread);
1511170161Sariff			if (ret != 0)
1512170161Sariff				break;
1513162588Snetchild			if (pcmsg_unrhdr != NULL) {
1514162588Snetchild				delete_unrhdr(pcmsg_unrhdr);
1515162588Snetchild				pcmsg_unrhdr = NULL;
1516162588Snetchild			}
1517162588Snetchild			break;
1518162588Snetchild		default:
1519162588Snetchild			ret = EOPNOTSUPP;
1520162588Snetchild	}
1521162588Snetchild
1522162588Snetchild	return ret;
1523148587Snetchild#endif
1524132236Stanimura}
1525132236Stanimura
1526132236StanimuraDEV_MODULE(sound, sound_modevent, NULL);
1527132236StanimuraMODULE_VERSION(sound, SOUND_MODVER);
1528