xref: /freebsd/sys/dev/sound/pcm/sound.c (revision e4e61333ffa4e90360de2dd1e4e0146f7cbf0afb)
1098ca2bdSWarner Losh /*-
23f225978SCameron Grant  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3155414e2SJoel Dahl  * (C) 1997 Luigi Rizzo
4987e5972SCameron Grant  * All rights reserved.
5987e5972SCameron Grant  *
6987e5972SCameron Grant  * Redistribution and use in source and binary forms, with or without
7987e5972SCameron Grant  * modification, are permitted provided that the following conditions
8987e5972SCameron Grant  * are met:
9987e5972SCameron Grant  * 1. Redistributions of source code must retain the above copyright
10987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer.
11987e5972SCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
12987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer in the
13987e5972SCameron Grant  *    documentation and/or other materials provided with the distribution.
14987e5972SCameron Grant  *
15987e5972SCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16987e5972SCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17987e5972SCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18987e5972SCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19987e5972SCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20987e5972SCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21987e5972SCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22987e5972SCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23987e5972SCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24987e5972SCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25987e5972SCameron Grant  * SUCH DAMAGE.
26987e5972SCameron Grant  */
27987e5972SCameron Grant 
28ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
29a580b31aSAriff Abdullah #include <dev/sound/pcm/ac97.h>
30285648f9SCameron Grant #include <dev/sound/pcm/vchan.h>
315ee30e27SMathew Kanner #include <dev/sound/pcm/dsp.h>
32bba4862cSAriff Abdullah #include <dev/sound/version.h>
33b611c801SAlexander Leidinger #include <sys/limits.h>
347c438dbeSCameron Grant #include <sys/sysctl.h>
35285648f9SCameron Grant 
3667b1dce3SCameron Grant #include "feeder_if.h"
3767b1dce3SCameron Grant 
3867b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$");
3967b1dce3SCameron Grant 
40d95502a8SCameron Grant devclass_t pcm_devclass;
4182db23e2SCameron Grant 
42b8a36395SCameron Grant int pcm_veto_load = 1;
43b8a36395SCameron Grant 
4482db23e2SCameron Grant #ifdef USING_DEVFS
45d95502a8SCameron Grant int snd_unit = 0;
46851a904aSAlexander Leidinger TUNABLE_INT("hw.snd.default_unit", &snd_unit);
4782db23e2SCameron Grant #endif
48cbe7d6a3SCameron Grant 
49bba4862cSAriff Abdullah int snd_maxautovchans = 16;
50851a904aSAlexander Leidinger /* XXX: a tunable implies that we may need more than one sound channel before
51851a904aSAlexander Leidinger    the system can change a sysctl (/etc/sysctl.conf), do we really need
52851a904aSAlexander Leidinger    this? */
5367b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
54987e5972SCameron Grant 
5582db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
5682db23e2SCameron Grant 
57bba4862cSAriff Abdullah /*
58bba4862cSAriff Abdullah  * XXX I've had enough with people not telling proper version/arch
59bba4862cSAriff Abdullah  *     while reporting problems, not after 387397913213th questions/requests.
60bba4862cSAriff Abdullah  */
61bba4862cSAriff Abdullah static const char snd_driver_version[] =
62bba4862cSAriff Abdullah     __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH;
63bba4862cSAriff Abdullah SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version,
64bba4862cSAriff Abdullah     0, "Driver version/arch");
65bba4862cSAriff Abdullah 
66b611c801SAlexander Leidinger /**
67b611c801SAlexander Leidinger  * @brief Unit number allocator for syncgroup IDs
68b611c801SAlexander Leidinger  */
69b611c801SAlexander Leidinger struct unrhdr *pcmsg_unrhdr = NULL;
70b611c801SAlexander Leidinger 
7167b1dce3SCameron Grant static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
7267b1dce3SCameron Grant 
7337209180SCameron Grant void *
742c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type)
7537209180SCameron Grant {
7637209180SCameron Grant #ifdef USING_MUTEX
7737209180SCameron Grant 	struct mtx *m;
7837209180SCameron Grant 
79a163d034SWarner Losh 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
8012e524a2SDon Lewis 	mtx_init(m, desc, type, MTX_DEF);
8112e524a2SDon Lewis 	return m;
8212e524a2SDon Lewis #else
8312e524a2SDon Lewis 	return (void *)0xcafebabe;
8412e524a2SDon Lewis #endif
8512e524a2SDon Lewis }
8612e524a2SDon Lewis 
8737209180SCameron Grant void
8837209180SCameron Grant snd_mtxfree(void *m)
8937209180SCameron Grant {
9037209180SCameron Grant #ifdef USING_MUTEX
9137209180SCameron Grant 	struct mtx *mtx = m;
9237209180SCameron Grant 
9367beb5a5SCameron Grant 	/* mtx_assert(mtx, MA_OWNED); */
9437209180SCameron Grant 	mtx_destroy(mtx);
9537209180SCameron Grant 	free(mtx, M_DEVBUF);
9637209180SCameron Grant #endif
9737209180SCameron Grant }
9837209180SCameron Grant 
9937209180SCameron Grant void
10037209180SCameron Grant snd_mtxassert(void *m)
10137209180SCameron Grant {
10237209180SCameron Grant #ifdef USING_MUTEX
103f00f162aSCameron Grant #ifdef INVARIANTS
10437209180SCameron Grant 	struct mtx *mtx = m;
10537209180SCameron Grant 
10637209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
10737209180SCameron Grant #endif
108f00f162aSCameron Grant #endif
10937209180SCameron Grant }
11067beb5a5SCameron Grant /*
11137209180SCameron Grant void
11237209180SCameron Grant snd_mtxlock(void *m)
11337209180SCameron Grant {
11437209180SCameron Grant #ifdef USING_MUTEX
11537209180SCameron Grant 	struct mtx *mtx = m;
11637209180SCameron Grant 
11737209180SCameron Grant 	mtx_lock(mtx);
11837209180SCameron Grant #endif
11937209180SCameron Grant }
12037209180SCameron Grant 
12137209180SCameron Grant void
12237209180SCameron Grant snd_mtxunlock(void *m)
12337209180SCameron Grant {
12437209180SCameron Grant #ifdef USING_MUTEX
12537209180SCameron Grant 	struct mtx *mtx = m;
12637209180SCameron Grant 
12737209180SCameron Grant 	mtx_unlock(mtx);
12837209180SCameron Grant #endif
12937209180SCameron Grant }
13067beb5a5SCameron Grant */
13137209180SCameron Grant int
13237209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
13337209180SCameron Grant {
134e4e61333SAriff Abdullah 	struct snddev_info *d;
13537209180SCameron Grant #ifdef USING_MUTEX
13637209180SCameron Grant 	flags &= INTR_MPSAFE;
13746700f12SPeter Wemm 	flags |= INTR_TYPE_AV;
13837209180SCameron Grant #else
13946700f12SPeter Wemm 	flags = INTR_TYPE_AV;
14037209180SCameron Grant #endif
141e4e61333SAriff Abdullah 	d = device_get_softc(dev);
142e4e61333SAriff Abdullah 	if (d != NULL && (flags & INTR_MPSAFE))
143e4e61333SAriff Abdullah 		d->flags |= SD_F_MPSAFE;
144e4e61333SAriff Abdullah 
1452cc08b74SAriff Abdullah 	return bus_setup_intr(dev, res, flags,
1462cc08b74SAriff Abdullah #if __FreeBSD_version >= 700031
1472cc08b74SAriff Abdullah 			NULL,
1482cc08b74SAriff Abdullah #endif
1492cc08b74SAriff Abdullah 			hand, param, cookiep);
15037209180SCameron Grant }
15137209180SCameron Grant 
152a527dbc7SCameron Grant #ifndef	PCM_DEBUG_MTX
15367b1dce3SCameron Grant void
15467b1dce3SCameron Grant pcm_lock(struct snddev_info *d)
15567b1dce3SCameron Grant {
15667b1dce3SCameron Grant 	snd_mtxlock(d->lock);
15767b1dce3SCameron Grant }
15867b1dce3SCameron Grant 
15967b1dce3SCameron Grant void
16067b1dce3SCameron Grant pcm_unlock(struct snddev_info *d)
16167b1dce3SCameron Grant {
16267b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
16367b1dce3SCameron Grant }
164a527dbc7SCameron Grant #endif
16567b1dce3SCameron Grant 
16667b1dce3SCameron Grant struct pcm_channel *
16767b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d)
16867b1dce3SCameron Grant {
16967b1dce3SCameron Grant 	return d->fakechan;
17067b1dce3SCameron Grant }
17167b1dce3SCameron Grant 
172bba4862cSAriff Abdullah static void
173bba4862cSAriff Abdullah pcm_clonereset(struct snddev_info *d)
174a1d444e1SAriff Abdullah {
175bba4862cSAriff Abdullah 	int cmax;
176bba4862cSAriff Abdullah 
177e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
178bba4862cSAriff Abdullah 
179bba4862cSAriff Abdullah 	cmax = d->playcount + d->reccount - 1;
180bba4862cSAriff Abdullah 	if (d->pvchancount > 0)
181bba4862cSAriff Abdullah 		cmax += MAX(d->pvchancount, snd_maxautovchans) - 1;
182bba4862cSAriff Abdullah 	if (d->rvchancount > 0)
183bba4862cSAriff Abdullah 		cmax += MAX(d->rvchancount, snd_maxautovchans) - 1;
184bba4862cSAriff Abdullah 	if (cmax > PCMMAXCLONE)
185bba4862cSAriff Abdullah 		cmax = PCMMAXCLONE;
186bba4862cSAriff Abdullah 	(void)snd_clone_gc(d->clones);
187bba4862cSAriff Abdullah 	(void)snd_clone_setmaxunit(d->clones, cmax);
188bba4862cSAriff Abdullah }
189bba4862cSAriff Abdullah 
190bba4862cSAriff Abdullah static int
191bba4862cSAriff Abdullah pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
192bba4862cSAriff Abdullah {
193bba4862cSAriff Abdullah 	struct pcm_channel *c, *ch, *nch;
194bba4862cSAriff Abdullah 	int err, vcnt;
195bba4862cSAriff Abdullah 
196e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
197a1d444e1SAriff Abdullah 
198bba4862cSAriff Abdullah 	if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
199e4e61333SAriff Abdullah 	    (direction == PCMDIR_REC && d->reccount < 1))
200e4e61333SAriff Abdullah 		return (ENODEV);
201a580b31aSAriff Abdullah 
202e4e61333SAriff Abdullah 	if (!(d->flags & SD_F_AUTOVCHAN))
203e4e61333SAriff Abdullah 		return (EINVAL);
204a1d444e1SAriff Abdullah 
205e4e61333SAriff Abdullah 	if (newcnt < 0 || newcnt > SND_MAXVCHANS)
206e4e61333SAriff Abdullah 		return (E2BIG);
207a1d444e1SAriff Abdullah 
208bba4862cSAriff Abdullah 	if (direction == PCMDIR_PLAY)
209bba4862cSAriff Abdullah 		vcnt = d->pvchancount;
210bba4862cSAriff Abdullah 	else if (direction == PCMDIR_REC)
211bba4862cSAriff Abdullah 		vcnt = d->rvchancount;
212e4e61333SAriff Abdullah 	else
213e4e61333SAriff Abdullah 		return (EINVAL);
214a1d444e1SAriff Abdullah 
215a1d444e1SAriff Abdullah 	if (newcnt > vcnt) {
216bba4862cSAriff Abdullah 		KASSERT(num == -1 ||
217bba4862cSAriff Abdullah 		    (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt),
218bba4862cSAriff Abdullah 		    ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
219bba4862cSAriff Abdullah 		    num, newcnt, vcnt));
220a1d444e1SAriff Abdullah 		/* add new vchans - find a parent channel first */
221e4e61333SAriff Abdullah 		ch = NULL;
222bba4862cSAriff Abdullah 		CHN_FOREACH(c, d, channels.pcm) {
223a1d444e1SAriff Abdullah 			CHN_LOCK(c);
224bba4862cSAriff Abdullah 			if (c->direction == direction &&
225bba4862cSAriff Abdullah 			    ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
226e4e61333SAriff Abdullah 			    !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
227e4e61333SAriff Abdullah 				ch = c;
228e4e61333SAriff Abdullah 			    	break;
229e4e61333SAriff Abdullah 			}
230a1d444e1SAriff Abdullah 			CHN_UNLOCK(c);
231a1d444e1SAriff Abdullah 		}
232e4e61333SAriff Abdullah 		if (ch == NULL)
233e4e61333SAriff Abdullah 			return (EBUSY);
234e4e61333SAriff Abdullah 		ch->flags |= CHN_F_BUSY;
235e4e61333SAriff Abdullah 		err = 0;
236a1d444e1SAriff Abdullah 		while (err == 0 && newcnt > vcnt) {
237e4e61333SAriff Abdullah 			err = vchan_create(ch, num);
238bba4862cSAriff Abdullah 			if (err == 0)
239a1d444e1SAriff Abdullah 				vcnt++;
240bba4862cSAriff Abdullah 			else if (err == E2BIG && newcnt > vcnt)
241bba4862cSAriff Abdullah 				device_printf(d->dev,
242bba4862cSAriff Abdullah 				    "%s: err=%d Maximum channel reached.\n",
243bba4862cSAriff Abdullah 				    __func__, err);
244a1d444e1SAriff Abdullah 		}
245a1d444e1SAriff Abdullah 		if (vcnt == 0)
246e4e61333SAriff Abdullah 			ch->flags &= ~CHN_F_BUSY;
247e4e61333SAriff Abdullah 		CHN_UNLOCK(ch);
248e4e61333SAriff Abdullah 		if (err != 0)
249e4e61333SAriff Abdullah 			return (err);
250e4e61333SAriff Abdullah 		else
251bba4862cSAriff Abdullah 			pcm_clonereset(d);
252a1d444e1SAriff Abdullah 	} else if (newcnt < vcnt) {
253bba4862cSAriff Abdullah 		KASSERT(num == -1,
254bba4862cSAriff Abdullah 		    ("bogus vchan_destroy() request num=%d", num));
255bba4862cSAriff Abdullah 		CHN_FOREACH(c, d, channels.pcm) {
256a1d444e1SAriff Abdullah 			CHN_LOCK(c);
257bba4862cSAriff Abdullah 			if (c->direction != direction ||
258bba4862cSAriff Abdullah 			    CHN_EMPTY(c, children) ||
259bba4862cSAriff Abdullah 			    !(c->flags & CHN_F_HAS_VCHAN)) {
260a1d444e1SAriff Abdullah 				CHN_UNLOCK(c);
261bba4862cSAriff Abdullah 				continue;
262a1d444e1SAriff Abdullah 			}
263bba4862cSAriff Abdullah 			CHN_FOREACH_SAFE(ch, c, nch, children) {
264bba4862cSAriff Abdullah 				CHN_LOCK(ch);
265bba4862cSAriff Abdullah 				if (!(ch->flags & CHN_F_BUSY)) {
266bba4862cSAriff Abdullah 					CHN_UNLOCK(ch);
267a1d444e1SAriff Abdullah 					CHN_UNLOCK(c);
268bba4862cSAriff Abdullah 					err = vchan_destroy(ch);
269bba4862cSAriff Abdullah 					CHN_LOCK(c);
270a1d444e1SAriff Abdullah 					if (err == 0)
271a1d444e1SAriff Abdullah 						vcnt--;
272bba4862cSAriff Abdullah 				} else
273bba4862cSAriff Abdullah 					CHN_UNLOCK(ch);
274e4e61333SAriff Abdullah 				if (vcnt == newcnt)
275bba4862cSAriff Abdullah 					break;
276a1d444e1SAriff Abdullah 			}
277bba4862cSAriff Abdullah 			CHN_UNLOCK(c);
278bba4862cSAriff Abdullah 			break;
279bba4862cSAriff Abdullah 		}
280bba4862cSAriff Abdullah 		pcm_clonereset(d);
281bba4862cSAriff Abdullah 	}
282a1d444e1SAriff Abdullah 
283e4e61333SAriff Abdullah 	return (0);
284a1d444e1SAriff Abdullah }
285a1d444e1SAriff Abdullah 
2863fdb3676SAriff Abdullah /* return error status and a locked channel */
2873fdb3676SAriff Abdullah int
2883fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
289bba4862cSAriff Abdullah     pid_t pid, int devunit)
290285648f9SCameron Grant {
291285648f9SCameron Grant 	struct pcm_channel *c;
292bba4862cSAriff Abdullah 	int err, vchancount;
293bba4862cSAriff Abdullah 
294bba4862cSAriff Abdullah 	KASSERT(d != NULL && ch != NULL && (devunit == -1 ||
295bba4862cSAriff Abdullah 	    !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) &&
296bba4862cSAriff Abdullah 	    (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
297e4e61333SAriff Abdullah 	    ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d",
298bba4862cSAriff Abdullah 	    __func__, d, ch, direction, pid, devunit));
299e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
300bba4862cSAriff Abdullah 
301bba4862cSAriff Abdullah 	/* Double check again. */
302bba4862cSAriff Abdullah 	if (devunit != -1) {
303bba4862cSAriff Abdullah 		switch (snd_unit2d(devunit)) {
304bba4862cSAriff Abdullah 		case SND_DEV_DSPHW_PLAY:
305bba4862cSAriff Abdullah 		case SND_DEV_DSPHW_VPLAY:
306bba4862cSAriff Abdullah 			if (direction != PCMDIR_PLAY)
307bba4862cSAriff Abdullah 				return (EOPNOTSUPP);
308bba4862cSAriff Abdullah 			break;
309bba4862cSAriff Abdullah 		case SND_DEV_DSPHW_REC:
310bba4862cSAriff Abdullah 		case SND_DEV_DSPHW_VREC:
311bba4862cSAriff Abdullah 			if (direction != PCMDIR_REC)
312bba4862cSAriff Abdullah 				return (EOPNOTSUPP);
313bba4862cSAriff Abdullah 			break;
314bba4862cSAriff Abdullah 		default:
315bba4862cSAriff Abdullah 			if (!(direction == PCMDIR_PLAY ||
316bba4862cSAriff Abdullah 			    direction == PCMDIR_REC))
317bba4862cSAriff Abdullah 				return (EOPNOTSUPP);
318bba4862cSAriff Abdullah 			break;
319bba4862cSAriff Abdullah 		}
320bba4862cSAriff Abdullah 	}
321285648f9SCameron Grant 
3223fdb3676SAriff Abdullah retry_chnalloc:
323e4e61333SAriff Abdullah 	err = EOPNOTSUPP;
324f637a36cSCameron Grant 	/* scan for a free channel */
325bba4862cSAriff Abdullah 	CHN_FOREACH(c, d, channels.pcm) {
32649c5e6e2SCameron Grant 		CHN_LOCK(c);
327bba4862cSAriff Abdullah 		if (c->direction == direction && !(c->flags & CHN_F_BUSY) &&
328bba4862cSAriff Abdullah 		    (devunit == -1 || devunit == -2 || c->unit == devunit)) {
329285648f9SCameron Grant 			c->flags |= CHN_F_BUSY;
330b8f0d9e0SCameron Grant 			c->pid = pid;
3313fdb3676SAriff Abdullah 			*ch = c;
332bba4862cSAriff Abdullah 			return (0);
333bba4862cSAriff Abdullah 		} else if (c->unit == devunit) {
3343fdb3676SAriff Abdullah 			if (c->direction != direction)
3353fdb3676SAriff Abdullah 				err = EOPNOTSUPP;
3363fdb3676SAriff Abdullah 			else if (c->flags & CHN_F_BUSY)
3373fdb3676SAriff Abdullah 				err = EBUSY;
3383fdb3676SAriff Abdullah 			else
3393fdb3676SAriff Abdullah 				err = EINVAL;
3403fdb3676SAriff Abdullah 			CHN_UNLOCK(c);
341bba4862cSAriff Abdullah 			return (err);
342bba4862cSAriff Abdullah 		} else if ((devunit == -1 || devunit == -2) &&
343bba4862cSAriff Abdullah 		    c->direction == direction && (c->flags & CHN_F_BUSY))
344a1d444e1SAriff Abdullah 			err = EBUSY;
34549c5e6e2SCameron Grant 		CHN_UNLOCK(c);
346285648f9SCameron Grant 	}
347f637a36cSCameron Grant 
348e4e61333SAriff Abdullah 	if (devunit == -2)
349e4e61333SAriff Abdullah 		return (err);
350e4e61333SAriff Abdullah 
351f637a36cSCameron Grant 	/* no channel available */
352e4e61333SAriff Abdullah 	if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY ||
353e4e61333SAriff Abdullah 	    snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) {
354bba4862cSAriff Abdullah 		if (direction == PCMDIR_PLAY)
355bba4862cSAriff Abdullah 			vchancount = d->pvchancount;
356bba4862cSAriff Abdullah 		else
357bba4862cSAriff Abdullah 			vchancount = d->rvchancount;
358bba4862cSAriff Abdullah 		if (!(vchancount > 0 && vchancount < snd_maxautovchans) &&
359bba4862cSAriff Abdullah 		    (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans))
360bba4862cSAriff Abdullah 			return (err);
361bba4862cSAriff Abdullah 		err = pcm_setvchans(d, direction, vchancount + 1,
362bba4862cSAriff Abdullah 		    (devunit == -1) ? -1 : snd_unit2c(devunit));
363a1d444e1SAriff Abdullah 		if (err == 0) {
364bba4862cSAriff Abdullah 			if (devunit == -1)
365bba4862cSAriff Abdullah 				devunit = -2;
3663fdb3676SAriff Abdullah 			goto retry_chnalloc;
367f637a36cSCameron Grant 		}
368f637a36cSCameron Grant 	}
369f637a36cSCameron Grant 
370bba4862cSAriff Abdullah 	return (err);
371285648f9SCameron Grant }
372285648f9SCameron Grant 
373b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
374285648f9SCameron Grant int
375b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
376285648f9SCameron Grant {
377e4e61333SAriff Abdullah 	PCM_BUSYASSERT(c->parentsnddev);
378b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
379e4e61333SAriff Abdullah 
380285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
381b8f0d9e0SCameron Grant 	c->pid = -1;
38249c5e6e2SCameron Grant 	CHN_UNLOCK(c);
383e4e61333SAriff Abdullah 
384e4e61333SAriff Abdullah 	return (0);
385285648f9SCameron Grant }
386285648f9SCameron Grant 
387285648f9SCameron Grant int
388285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
389285648f9SCameron Grant {
390e4e61333SAriff Abdullah 	PCM_BUSYASSERT(c->parentsnddev);
391b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
392e4e61333SAriff Abdullah 
393285648f9SCameron Grant 	c->refcount += ref;
394e4e61333SAriff Abdullah 
395e4e61333SAriff Abdullah 	return (c->refcount);
396285648f9SCameron Grant }
397285648f9SCameron Grant 
39867b1dce3SCameron Grant int
39967b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta)
40067b1dce3SCameron Grant {
401e4e61333SAriff Abdullah 	snd_mtxassert(d->lock);
402a527dbc7SCameron Grant 
403a527dbc7SCameron Grant 	d->inprog += delta;
404e4e61333SAriff Abdullah 
405e4e61333SAriff Abdullah 	return (d->inprog);
40667b1dce3SCameron Grant }
40767b1dce3SCameron Grant 
40867b1dce3SCameron Grant static void
40967b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num)
41067b1dce3SCameron Grant {
411e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
412e4e61333SAriff Abdullah 
413bba4862cSAriff Abdullah 	if (num < 0)
414bba4862cSAriff Abdullah 		return;
415bba4862cSAriff Abdullah 
416bba4862cSAriff Abdullah 	if (num >= 0 && d->pvchancount > num)
417bba4862cSAriff Abdullah 		(void)pcm_setvchans(d, PCMDIR_PLAY, num, -1);
418bba4862cSAriff Abdullah 	else if (num > 0 && d->pvchancount == 0)
419bba4862cSAriff Abdullah 		(void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1);
420bba4862cSAriff Abdullah 
421bba4862cSAriff Abdullah 	if (num >= 0 && d->rvchancount > num)
422bba4862cSAriff Abdullah 		(void)pcm_setvchans(d, PCMDIR_REC, num, -1);
423bba4862cSAriff Abdullah 	else if (num > 0 && d->rvchancount == 0)
424bba4862cSAriff Abdullah 		(void)pcm_setvchans(d, PCMDIR_REC, 1, -1);
425bba4862cSAriff Abdullah 
426bba4862cSAriff Abdullah 	pcm_clonereset(d);
42767b1dce3SCameron Grant }
42867b1dce3SCameron Grant 
42982db23e2SCameron Grant #ifdef USING_DEVFS
43033dbf14aSCameron Grant static int
431851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
43233dbf14aSCameron Grant {
433b8f0d9e0SCameron Grant 	struct snddev_info *d;
43433dbf14aSCameron Grant 	int error, unit;
43533dbf14aSCameron Grant 
43633dbf14aSCameron Grant 	unit = snd_unit;
437041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &unit, 0, req);
43833dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
439b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
440e4e61333SAriff Abdullah 		if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
441b8f0d9e0SCameron Grant 			return EINVAL;
44233dbf14aSCameron Grant 		snd_unit = unit;
44333dbf14aSCameron Grant 	}
44433dbf14aSCameron Grant 	return (error);
44533dbf14aSCameron Grant }
446851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */
447851a904aSAlexander Leidinger SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW,
448a580b31aSAriff Abdullah             0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device");
44982db23e2SCameron Grant #endif
450987e5972SCameron Grant 
451cd9766c5SCameron Grant static int
45267b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
453cd9766c5SCameron Grant {
45467b1dce3SCameron Grant 	struct snddev_info *d;
45567b1dce3SCameron Grant 	int i, v, error;
456cd9766c5SCameron Grant 
45767b1dce3SCameron Grant 	v = snd_maxautovchans;
458041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &v, 0, req);
459cd9766c5SCameron Grant 	if (error == 0 && req->newptr != NULL) {
460bba4862cSAriff Abdullah 		if (v < 0)
461bba4862cSAriff Abdullah 			v = 0;
462bba4862cSAriff Abdullah 		if (v > SND_MAXVCHANS)
463bba4862cSAriff Abdullah 			v = SND_MAXVCHANS;
464bba4862cSAriff Abdullah 		snd_maxautovchans = v;
4659c271f79SAriff Abdullah 		for (i = 0; pcm_devclass != NULL &&
4669c271f79SAriff Abdullah 		    i < devclass_get_maxunit(pcm_devclass); i++) {
46767b1dce3SCameron Grant 			d = devclass_get_softc(pcm_devclass, i);
468e4e61333SAriff Abdullah 			if (!PCM_REGISTERED(d))
46967b1dce3SCameron Grant 				continue;
470e4e61333SAriff Abdullah 			PCM_ACQUIRE_QUICK(d);
47167b1dce3SCameron Grant 			pcm_setmaxautovchans(d, v);
472e4e61333SAriff Abdullah 			PCM_RELEASE_QUICK(d);
47367b1dce3SCameron Grant 		}
47467b1dce3SCameron Grant 	}
475cd9766c5SCameron Grant 	return (error);
476cd9766c5SCameron Grant }
47767b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
478a580b31aSAriff Abdullah             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
479f637a36cSCameron Grant 
480285648f9SCameron Grant struct pcm_channel *
481bba4862cSAriff Abdullah pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo)
482987e5972SCameron Grant {
483bba4862cSAriff Abdullah 	struct pcm_channel *ch;
484bba4862cSAriff Abdullah 	int direction, err, rpnum, *pnum, max;
485bba4862cSAriff Abdullah 	int udc, device, chan;
486bba4862cSAriff Abdullah 	char *dirs, *devname, buf[CHN_NAMELEN];
487bba4862cSAriff Abdullah 
488e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
489e4e61333SAriff Abdullah 	snd_mtxassert(d->lock);
490bba4862cSAriff Abdullah 	KASSERT(num >= -1, ("invalid num=%d", num));
491bba4862cSAriff Abdullah 
492987e5972SCameron Grant 
493285648f9SCameron Grant 	switch (dir) {
494285648f9SCameron Grant 	case PCMDIR_PLAY:
495285648f9SCameron Grant 		dirs = "play";
496a3193a9cSDon Lewis 		direction = PCMDIR_PLAY;
497a67fe5c1SCameron Grant 		pnum = &d->playcount;
498bba4862cSAriff Abdullah 		device = SND_DEV_DSPHW_PLAY;
499bba4862cSAriff Abdullah 		max = SND_MAXHWCHAN;
500285648f9SCameron Grant 		break;
501bba4862cSAriff Abdullah 	case PCMDIR_PLAY_VIRTUAL:
502bba4862cSAriff Abdullah 		dirs = "virtual";
503bba4862cSAriff Abdullah 		direction = PCMDIR_PLAY;
504bba4862cSAriff Abdullah 		pnum = &d->pvchancount;
505bba4862cSAriff Abdullah 		device = SND_DEV_DSPHW_VPLAY;
506bba4862cSAriff Abdullah 		max = SND_MAXVCHANS;
507bba4862cSAriff Abdullah 		break;
508285648f9SCameron Grant 	case PCMDIR_REC:
509285648f9SCameron Grant 		dirs = "record";
510a3193a9cSDon Lewis 		direction = PCMDIR_REC;
511a67fe5c1SCameron Grant 		pnum = &d->reccount;
512bba4862cSAriff Abdullah 		device = SND_DEV_DSPHW_REC;
513bba4862cSAriff Abdullah 		max = SND_MAXHWCHAN;
514285648f9SCameron Grant 		break;
515bba4862cSAriff Abdullah 	case PCMDIR_REC_VIRTUAL:
516285648f9SCameron Grant 		dirs = "virtual";
517bba4862cSAriff Abdullah 		direction = PCMDIR_REC;
518bba4862cSAriff Abdullah 		pnum = &d->rvchancount;
519bba4862cSAriff Abdullah 		device = SND_DEV_DSPHW_VREC;
520bba4862cSAriff Abdullah 		max = SND_MAXVCHANS;
521285648f9SCameron Grant 		break;
522285648f9SCameron Grant 	default:
523e4e61333SAriff Abdullah 		return (NULL);
5249c326820SCameron Grant 	}
525285648f9SCameron Grant 
526bba4862cSAriff Abdullah 	chan = (num == -1) ? 0 : num;
527285648f9SCameron Grant 
528e4e61333SAriff Abdullah 	if (*pnum >= max || chan >= max)
529e4e61333SAriff Abdullah 		return (NULL);
530bba4862cSAriff Abdullah 
5313fdb3676SAriff Abdullah 	rpnum = 0;
532bba4862cSAriff Abdullah 
533bba4862cSAriff Abdullah 	CHN_FOREACH(ch, d, channels.pcm) {
534bba4862cSAriff Abdullah 		if (CHN_DEV(ch) != device)
5353fdb3676SAriff Abdullah 			continue;
536bba4862cSAriff Abdullah 		if (chan == CHN_CHAN(ch)) {
537bba4862cSAriff Abdullah 			if (num != -1) {
5383fdb3676SAriff Abdullah 				device_printf(d->dev,
539bba4862cSAriff Abdullah 				    "channel num=%d allocated!\n", chan);
540e4e61333SAriff Abdullah 				return (NULL);
541bba4862cSAriff Abdullah 			}
542bba4862cSAriff Abdullah 			chan++;
543bba4862cSAriff Abdullah 			if (chan >= max) {
544bba4862cSAriff Abdullah 				device_printf(d->dev,
545bba4862cSAriff Abdullah 				    "chan=%d > %d\n", chan, max);
546e4e61333SAriff Abdullah 				return (NULL);
547bba4862cSAriff Abdullah 			}
5483fdb3676SAriff Abdullah 		}
5493fdb3676SAriff Abdullah 		rpnum++;
5503fdb3676SAriff Abdullah 	}
551bba4862cSAriff Abdullah 
5523fdb3676SAriff Abdullah 	if (*pnum != rpnum) {
5533fdb3676SAriff Abdullah 		device_printf(d->dev,
554bba4862cSAriff Abdullah 		    "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n",
5553fdb3676SAriff Abdullah 		    __func__, dirs, *pnum, rpnum);
556e4e61333SAriff Abdullah 		return (NULL);
5573fdb3676SAriff Abdullah 	}
558a67fe5c1SCameron Grant 
559bba4862cSAriff Abdullah 	udc = snd_mkunit(device_get_unit(d->dev), device, chan);
560bba4862cSAriff Abdullah 	devname = dsp_unit2name(buf, sizeof(buf), udc);
561bba4862cSAriff Abdullah 
562bba4862cSAriff Abdullah 	if (devname == NULL) {
563bba4862cSAriff Abdullah 		device_printf(d->dev,
564bba4862cSAriff Abdullah 		    "Failed to query device name udc=0x%08x\n", udc);
565e4e61333SAriff Abdullah 		return (NULL);
566bba4862cSAriff Abdullah 	}
567bba4862cSAriff Abdullah 
568bba4862cSAriff Abdullah 	pcm_unlock(d);
569bba4862cSAriff Abdullah 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
570bba4862cSAriff Abdullah 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
571bba4862cSAriff Abdullah 	ch->unit = udc;
572285648f9SCameron Grant 	ch->pid = -1;
573285648f9SCameron Grant 	ch->parentsnddev = d;
574285648f9SCameron Grant 	ch->parentchannel = parent;
575436c9b65SScott Long 	ch->dev = d->dev;
576bba4862cSAriff Abdullah 	ch->trigger = PCMTRIG_STOP;
577bba4862cSAriff Abdullah 	snprintf(ch->name, sizeof(ch->name), "%s:%s:%s",
578bba4862cSAriff Abdullah 	    device_get_nameunit(ch->dev), dirs, devname);
579285648f9SCameron Grant 
580a3193a9cSDon Lewis 	err = chn_init(ch, devinfo, dir, direction);
581e4e61333SAriff Abdullah 	pcm_lock(d);
5820f55ac6cSCameron Grant 	if (err) {
583bba4862cSAriff Abdullah 		device_printf(d->dev, "chn_init(%s) failed: err = %d\n",
584bba4862cSAriff Abdullah 		    ch->name, err);
585285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
586285648f9SCameron Grant 		free(ch, M_DEVBUF);
587e4e61333SAriff Abdullah 		return (NULL);
588bbb5bf3dSCameron Grant 	}
589285648f9SCameron Grant 
590e4e61333SAriff Abdullah 	return (ch);
591285648f9SCameron Grant }
592285648f9SCameron Grant 
593285648f9SCameron Grant int
594285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
595285648f9SCameron Grant {
596a67fe5c1SCameron Grant 	struct snddev_info *d;
597285648f9SCameron Grant 	int err;
598285648f9SCameron Grant 
599a67fe5c1SCameron Grant 	d = ch->parentsnddev;
600e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
601e4e61333SAriff Abdullah 
602285648f9SCameron Grant 	err = chn_kill(ch);
603285648f9SCameron Grant 	if (err) {
604e4e61333SAriff Abdullah 		device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n",
605e4e61333SAriff Abdullah 		    ch->name, err);
606e4e61333SAriff Abdullah 		return (err);
607285648f9SCameron Grant 	}
608285648f9SCameron Grant 
609285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
610285648f9SCameron Grant 	free(ch, M_DEVBUF);
611285648f9SCameron Grant 
612e4e61333SAriff Abdullah 	return (0);
613285648f9SCameron Grant }
614285648f9SCameron Grant 
615285648f9SCameron Grant int
6165ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
617285648f9SCameron Grant {
618bba4862cSAriff Abdullah 	struct pcm_channel *tmp, *after;
619bba4862cSAriff Abdullah 	int num;
6205ee30e27SMathew Kanner 
621e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
622e4e61333SAriff Abdullah 	snd_mtxassert(d->lock);
623bba4862cSAriff Abdullah 	KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
624bba4862cSAriff Abdullah 	    ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
625285648f9SCameron Grant 
6263fdb3676SAriff Abdullah 	after = NULL;
627bba4862cSAriff Abdullah 	tmp = NULL;
628bba4862cSAriff Abdullah 	num = 0;
629bba4862cSAriff Abdullah 
6303fdb3676SAriff Abdullah 	/*
631bba4862cSAriff Abdullah 	 * Look for possible device collision.
6323fdb3676SAriff Abdullah 	 */
633bba4862cSAriff Abdullah 	CHN_FOREACH(tmp, d, channels.pcm) {
634bba4862cSAriff Abdullah 		if (tmp->unit == ch->unit) {
635bba4862cSAriff Abdullah 			device_printf(d->dev, "%s(): Device collision "
636bba4862cSAriff Abdullah 			    "old=%p new=%p devunit=0x%08x\n",
637bba4862cSAriff Abdullah 			    __func__, tmp, ch, ch->unit);
638e4e61333SAriff Abdullah 			return (ENODEV);
6397982e7a4SAriff Abdullah 		}
640bba4862cSAriff Abdullah 		if (CHN_DEV(tmp) < CHN_DEV(ch)) {
641bba4862cSAriff Abdullah 			if (num == 0)
6423fdb3676SAriff Abdullah 				after = tmp;
643bba4862cSAriff Abdullah 			continue;
644bba4862cSAriff Abdullah 		} else if (CHN_DEV(tmp) > CHN_DEV(ch))
645bba4862cSAriff Abdullah 			break;
646bba4862cSAriff Abdullah 		num++;
647bba4862cSAriff Abdullah 		if (CHN_CHAN(tmp) < CHN_CHAN(ch))
648bba4862cSAriff Abdullah 			after = tmp;
649bba4862cSAriff Abdullah 		else if (CHN_CHAN(tmp) > CHN_CHAN(ch))
650bba4862cSAriff Abdullah 			break;
6517982e7a4SAriff Abdullah 	}
652bba4862cSAriff Abdullah 
653bba4862cSAriff Abdullah 	if (after != NULL) {
654bba4862cSAriff Abdullah 		CHN_INSERT_AFTER(after, ch, channels.pcm);
65597d69a96SAlexander Leidinger 	} else {
656bba4862cSAriff Abdullah 		CHN_INSERT_HEAD(d, ch, channels.pcm);
65767b1dce3SCameron Grant 	}
6583fdb3676SAriff Abdullah 
659e4e61333SAriff Abdullah 	switch (CHN_DEV(ch)) {
660e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_PLAY:
661e4e61333SAriff Abdullah 		d->playcount++;
662e4e61333SAriff Abdullah 		break;
663e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_VPLAY:
664e4e61333SAriff Abdullah 		d->pvchancount++;
665e4e61333SAriff Abdullah 		break;
666e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_REC:
667e4e61333SAriff Abdullah 		d->reccount++;
668e4e61333SAriff Abdullah 		break;
669e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_VREC:
670e4e61333SAriff Abdullah 		d->rvchancount++;
671e4e61333SAriff Abdullah 		break;
672e4e61333SAriff Abdullah 	default:
673e4e61333SAriff Abdullah 		break;
674e4e61333SAriff Abdullah 	}
675b8f0d9e0SCameron Grant 
676e4e61333SAriff Abdullah 	d->devcount++;
677e4e61333SAriff Abdullah 
678e4e61333SAriff Abdullah 	return (0);
67933dbf14aSCameron Grant }
68033dbf14aSCameron Grant 
681285648f9SCameron Grant int
6825ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
68333dbf14aSCameron Grant {
684bba4862cSAriff Abdullah 	struct pcm_channel *tmp;
68533dbf14aSCameron Grant 
686e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
687e4e61333SAriff Abdullah 	snd_mtxassert(d->lock);
688e4e61333SAriff Abdullah 
689bba4862cSAriff Abdullah 	tmp = NULL;
690a527dbc7SCameron Grant 
691bba4862cSAriff Abdullah 	CHN_FOREACH(tmp, d, channels.pcm) {
692bba4862cSAriff Abdullah 		if (tmp == ch)
693bba4862cSAriff Abdullah 			break;
69433dbf14aSCameron Grant 	}
695bba4862cSAriff Abdullah 
696bba4862cSAriff Abdullah 	if (tmp != ch)
697e4e61333SAriff Abdullah 		return (EINVAL);
69867beb5a5SCameron Grant 
699bba4862cSAriff Abdullah 	CHN_REMOVE(d, ch, channels.pcm);
700e4e61333SAriff Abdullah 
701bba4862cSAriff Abdullah 	switch (CHN_DEV(ch)) {
702bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_PLAY:
70367beb5a5SCameron Grant 		d->playcount--;
704bba4862cSAriff Abdullah 		break;
705bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_VPLAY:
706bba4862cSAriff Abdullah 		d->pvchancount--;
707bba4862cSAriff Abdullah 		break;
708bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_REC:
709bba4862cSAriff Abdullah 		d->reccount--;
710bba4862cSAriff Abdullah 		break;
711bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_VREC:
712bba4862cSAriff Abdullah 		d->rvchancount--;
713bba4862cSAriff Abdullah 		break;
714bba4862cSAriff Abdullah 	default:
715bba4862cSAriff Abdullah 		break;
716bba4862cSAriff Abdullah 	}
717285648f9SCameron Grant 
718e4e61333SAriff Abdullah 	d->devcount--;
719e4e61333SAriff Abdullah 
720e4e61333SAriff Abdullah 	return (0);
721987e5972SCameron Grant }
722987e5972SCameron Grant 
723987e5972SCameron Grant int
724285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
725285648f9SCameron Grant {
726285648f9SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
72767b1dce3SCameron Grant 	struct pcm_channel *ch;
72867b1dce3SCameron Grant 	int err;
729285648f9SCameron Grant 
730e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
731e4e61333SAriff Abdullah 
732e4e61333SAriff Abdullah 	pcm_lock(d);
733bba4862cSAriff Abdullah 	ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo);
734285648f9SCameron Grant 	if (!ch) {
735e4e61333SAriff Abdullah 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n",
736e4e61333SAriff Abdullah 		    cls->name, dir, devinfo);
737e4e61333SAriff Abdullah 		pcm_unlock(d);
738e4e61333SAriff Abdullah 		return (ENODEV);
739285648f9SCameron Grant 	}
740cd9766c5SCameron Grant 
7415ee30e27SMathew Kanner 	err = pcm_chn_add(d, ch);
742e4e61333SAriff Abdullah 	pcm_unlock(d);
743285648f9SCameron Grant 	if (err) {
744e4e61333SAriff Abdullah 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n",
745e4e61333SAriff Abdullah 		    ch->name, err);
746285648f9SCameron Grant 		pcm_chn_destroy(ch);
747cd9766c5SCameron Grant 	}
748cd9766c5SCameron Grant 
749e4e61333SAriff Abdullah 	return (err);
750285648f9SCameron Grant }
751285648f9SCameron Grant 
752285648f9SCameron Grant static int
753285648f9SCameron Grant pcm_killchan(device_t dev)
754285648f9SCameron Grant {
755285648f9SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
756e33bee07SOlivier Houchard 	struct pcm_channel *ch;
757e4e61333SAriff Abdullah 	int error;
758e4e61333SAriff Abdullah 
759e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
760285648f9SCameron Grant 
761bba4862cSAriff Abdullah 	ch = CHN_FIRST(d, channels.pcm);
762285648f9SCameron Grant 
763e4e61333SAriff Abdullah 	pcm_lock(d);
764bba4862cSAriff Abdullah 	error = pcm_chn_remove(d, ch);
765e4e61333SAriff Abdullah 	pcm_unlock(d);
766e33bee07SOlivier Houchard 	if (error)
767e33bee07SOlivier Houchard 		return (error);
768e33bee07SOlivier Houchard 	return (pcm_chn_destroy(ch));
769285648f9SCameron Grant }
770285648f9SCameron Grant 
771285648f9SCameron Grant int
772987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
773987e5972SCameron Grant {
77466ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
77549c5e6e2SCameron Grant 
776e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
777bba4862cSAriff Abdullah 
778e4e61333SAriff Abdullah 	if (d->playcount == 0 || d->reccount == 0)
779e4e61333SAriff Abdullah 		d->flags |= SD_F_SIMPLEX;
780e4e61333SAriff Abdullah 
781e4e61333SAriff Abdullah 	if ((d->playcount > 0 || d->reccount > 0) &&
782e4e61333SAriff Abdullah 	    !(d->flags & SD_F_AUTOVCHAN)) {
783e4e61333SAriff Abdullah 		d->flags |= SD_F_AUTOVCHAN;
784e4e61333SAriff Abdullah 		vchan_initsys(dev);
785e4e61333SAriff Abdullah 	}
786e4e61333SAriff Abdullah 
787e4e61333SAriff Abdullah 	pcm_setmaxautovchans(d, snd_maxautovchans);
788bba4862cSAriff Abdullah 
789a580b31aSAriff Abdullah 	strlcpy(d->status, str, SND_STATUSLEN);
790bba4862cSAriff Abdullah 
791e4e61333SAriff Abdullah 	pcm_lock(d);
792e4e61333SAriff Abdullah 
793bba4862cSAriff Abdullah 	/* Last stage, enable cloning. */
794e4e61333SAriff Abdullah 	if (d->clones != NULL)
795bba4862cSAriff Abdullah 		(void)snd_clone_enable(d->clones);
796e4e61333SAriff Abdullah 
797e4e61333SAriff Abdullah 	/* Done, we're ready.. */
798e4e61333SAriff Abdullah 	d->flags |= SD_F_REGISTERED;
799e4e61333SAriff Abdullah 
800e4e61333SAriff Abdullah 	PCM_RELEASE(d);
801bba4862cSAriff Abdullah 
802bba4862cSAriff Abdullah 	pcm_unlock(d);
803bba4862cSAriff Abdullah 
804e4e61333SAriff Abdullah 	return (0);
805987e5972SCameron Grant }
806987e5972SCameron Grant 
807a1d444e1SAriff Abdullah uint32_t
808987e5972SCameron Grant pcm_getflags(device_t dev)
809987e5972SCameron Grant {
81066ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
81149c5e6e2SCameron Grant 
812987e5972SCameron Grant 	return d->flags;
813987e5972SCameron Grant }
814987e5972SCameron Grant 
815987e5972SCameron Grant void
816a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val)
817987e5972SCameron Grant {
81866ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
819d95502a8SCameron Grant 
820987e5972SCameron Grant 	d->flags = val;
821987e5972SCameron Grant }
822987e5972SCameron Grant 
82339004e69SCameron Grant void *
82439004e69SCameron Grant pcm_getdevinfo(device_t dev)
82539004e69SCameron Grant {
82666ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
82749c5e6e2SCameron Grant 
82839004e69SCameron Grant 	return d->devinfo;
82939004e69SCameron Grant }
83039004e69SCameron Grant 
831a67fe5c1SCameron Grant unsigned int
832d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
833a67fe5c1SCameron Grant {
834a67fe5c1SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
8354e60be34SCameron Grant 	int sz, x;
836a67fe5c1SCameron Grant 
837a67fe5c1SCameron Grant 	sz = 0;
8384e60be34SCameron Grant 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
8394e60be34SCameron Grant 		x = sz;
840d55d96f6SAlexander Leidinger 		RANGE(sz, minbufsz, maxbufsz);
8414e60be34SCameron Grant 		if (x != sz)
842d55d96f6SAlexander Leidinger 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
843d55d96f6SAlexander Leidinger 		x = minbufsz;
8444e60be34SCameron Grant 		while (x < sz)
8454e60be34SCameron Grant 			x <<= 1;
8464e60be34SCameron Grant 		if (x > sz)
8474e60be34SCameron Grant 			x >>= 1;
8484e60be34SCameron Grant 		if (x != sz) {
8494e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
8504e60be34SCameron Grant 			sz = x;
8514e60be34SCameron Grant 		}
8524e60be34SCameron Grant 	} else {
853a67fe5c1SCameron Grant 		sz = deflt;
8544e60be34SCameron Grant 	}
8554e60be34SCameron Grant 
856a67fe5c1SCameron Grant 	d->bufsz = sz;
857a67fe5c1SCameron Grant 
858a67fe5c1SCameron Grant 	return sz;
859a67fe5c1SCameron Grant }
860a67fe5c1SCameron Grant 
861bba4862cSAriff Abdullah #if defined(SND_DYNSYSCTL) && defined(SND_DEBUG)
862bba4862cSAriff Abdullah static int
863bba4862cSAriff Abdullah sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS)
864bba4862cSAriff Abdullah {
865bba4862cSAriff Abdullah 	struct snddev_info *d;
866bba4862cSAriff Abdullah 	uint32_t flags;
867bba4862cSAriff Abdullah 	int err;
868bba4862cSAriff Abdullah 
869bba4862cSAriff Abdullah 	d = oidp->oid_arg1;
870e4e61333SAriff Abdullah 	if (!PCM_REGISTERED(d) || d->clones == NULL)
871bba4862cSAriff Abdullah 		return (ENODEV);
872bba4862cSAriff Abdullah 
873e4e61333SAriff Abdullah 	PCM_ACQUIRE_QUICK(d);
874e4e61333SAriff Abdullah 
875bba4862cSAriff Abdullah 	flags = snd_clone_getflags(d->clones);
876041b706bSDavid Malone 	err = sysctl_handle_int(oidp, &flags, 0, req);
877bba4862cSAriff Abdullah 
878bba4862cSAriff Abdullah 	if (err == 0 && req->newptr != NULL) {
879e4e61333SAriff Abdullah 		if (flags & ~SND_CLONE_MASK)
880bba4862cSAriff Abdullah 			err = EINVAL;
881e4e61333SAriff Abdullah 		else
882bba4862cSAriff Abdullah 			(void)snd_clone_setflags(d->clones, flags);
883bba4862cSAriff Abdullah 	}
884e4e61333SAriff Abdullah 
885e4e61333SAriff Abdullah 	PCM_RELEASE_QUICK(d);
886bba4862cSAriff Abdullah 
887bba4862cSAriff Abdullah 	return (err);
888bba4862cSAriff Abdullah }
889bba4862cSAriff Abdullah 
890bba4862cSAriff Abdullah static int
891bba4862cSAriff Abdullah sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS)
892bba4862cSAriff Abdullah {
893bba4862cSAriff Abdullah 	struct snddev_info *d;
894bba4862cSAriff Abdullah 	int err, deadline;
895bba4862cSAriff Abdullah 
896bba4862cSAriff Abdullah 	d = oidp->oid_arg1;
897e4e61333SAriff Abdullah 	if (!PCM_REGISTERED(d) || d->clones == NULL)
898bba4862cSAriff Abdullah 		return (ENODEV);
899bba4862cSAriff Abdullah 
900e4e61333SAriff Abdullah 	PCM_ACQUIRE_QUICK(d);
901e4e61333SAriff Abdullah 
902bba4862cSAriff Abdullah 	deadline = snd_clone_getdeadline(d->clones);
903041b706bSDavid Malone 	err = sysctl_handle_int(oidp, &deadline, 0, req);
904bba4862cSAriff Abdullah 
905bba4862cSAriff Abdullah 	if (err == 0 && req->newptr != NULL) {
906bba4862cSAriff Abdullah 		if (deadline < 0)
907bba4862cSAriff Abdullah 			err = EINVAL;
908e4e61333SAriff Abdullah 		else
909bba4862cSAriff Abdullah 			(void)snd_clone_setdeadline(d->clones, deadline);
910bba4862cSAriff Abdullah 	}
911e4e61333SAriff Abdullah 
912e4e61333SAriff Abdullah 	PCM_RELEASE_QUICK(d);
913bba4862cSAriff Abdullah 
914bba4862cSAriff Abdullah 	return (err);
915bba4862cSAriff Abdullah }
916bba4862cSAriff Abdullah 
917bba4862cSAriff Abdullah static int
918bba4862cSAriff Abdullah sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS)
919bba4862cSAriff Abdullah {
920bba4862cSAriff Abdullah 	struct snddev_info *d;
921bba4862cSAriff Abdullah 	int err, val;
922bba4862cSAriff Abdullah 
923bba4862cSAriff Abdullah 	d = oidp->oid_arg1;
924e4e61333SAriff Abdullah 	if (!PCM_REGISTERED(d) || d->clones == NULL)
925bba4862cSAriff Abdullah 		return (ENODEV);
926bba4862cSAriff Abdullah 
927bba4862cSAriff Abdullah 	val = 0;
928041b706bSDavid Malone 	err = sysctl_handle_int(oidp, &val, 0, req);
929bba4862cSAriff Abdullah 
930bba4862cSAriff Abdullah 	if (err == 0 && req->newptr != NULL && val != 0) {
931e4e61333SAriff Abdullah 		PCM_ACQUIRE_QUICK(d);
932e4e61333SAriff Abdullah 		val = snd_clone_gc(d->clones);
933e4e61333SAriff Abdullah 		PCM_RELEASE_QUICK(d);
934e4e61333SAriff Abdullah 		if (bootverbose != 0 || snd_verbose > 3)
935e4e61333SAriff Abdullah 			device_printf(d->dev, "clone gc: pruned=%d\n", val);
936bba4862cSAriff Abdullah 	}
937bba4862cSAriff Abdullah 
938bba4862cSAriff Abdullah 	return (err);
939bba4862cSAriff Abdullah }
940bba4862cSAriff Abdullah 
941bba4862cSAriff Abdullah static int
942bba4862cSAriff Abdullah sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS)
943bba4862cSAriff Abdullah {
944bba4862cSAriff Abdullah 	struct snddev_info *d;
945bba4862cSAriff Abdullah 	int i, err, val;
946bba4862cSAriff Abdullah 
947bba4862cSAriff Abdullah 	val = 0;
948041b706bSDavid Malone 	err = sysctl_handle_int(oidp, &val, 0, req);
949bba4862cSAriff Abdullah 
950bba4862cSAriff Abdullah 	if (err == 0 && req->newptr != NULL && val != 0) {
9519c271f79SAriff Abdullah 		for (i = 0; pcm_devclass != NULL &&
9529c271f79SAriff Abdullah 		    i < devclass_get_maxunit(pcm_devclass); i++) {
953bba4862cSAriff Abdullah 			d = devclass_get_softc(pcm_devclass, i);
954e4e61333SAriff Abdullah 			if (!PCM_REGISTERED(d) || d->clones == NULL)
955bba4862cSAriff Abdullah 				continue;
956e4e61333SAriff Abdullah 			PCM_ACQUIRE_QUICK(d);
957e4e61333SAriff Abdullah 			val = snd_clone_gc(d->clones);
958e4e61333SAriff Abdullah 			PCM_RELEASE_QUICK(d);
959e4e61333SAriff Abdullah 			if (bootverbose != 0 || snd_verbose > 3)
960e4e61333SAriff Abdullah 				device_printf(d->dev, "clone gc: pruned=%d\n",
961e4e61333SAriff Abdullah 				    val);
962bba4862cSAriff Abdullah 		}
963bba4862cSAriff Abdullah 	}
964bba4862cSAriff Abdullah 
965bba4862cSAriff Abdullah 	return (err);
966bba4862cSAriff Abdullah }
967bba4862cSAriff Abdullah SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW,
968bba4862cSAriff Abdullah     0, sizeof(int), sysctl_hw_snd_clone_gc, "I",
969bba4862cSAriff Abdullah     "global clone garbage collector");
970bba4862cSAriff Abdullah #endif
971bba4862cSAriff Abdullah 
972987e5972SCameron Grant int
973987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
974987e5972SCameron Grant {
975bba4862cSAriff Abdullah 	struct snddev_info *d;
976987e5972SCameron Grant 
977b8a36395SCameron Grant 	if (pcm_veto_load) {
978b8a36395SCameron Grant 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
979b8a36395SCameron Grant 
980b8a36395SCameron Grant 		return EINVAL;
981b8a36395SCameron Grant 	}
982b8a36395SCameron Grant 
983bba4862cSAriff Abdullah 	if (device_get_unit(dev) > PCMMAXUNIT) {
984bba4862cSAriff Abdullah 		device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n",
985bba4862cSAriff Abdullah 		    device_get_unit(dev), PCMMAXUNIT);
986bba4862cSAriff Abdullah 		device_printf(dev,
987bba4862cSAriff Abdullah 		    "Use 'hw.snd.maxunit' tunable to raise the limit.\n");
988bba4862cSAriff Abdullah 		return ENODEV;
989bba4862cSAriff Abdullah 	}
990bba4862cSAriff Abdullah 
991bba4862cSAriff Abdullah 	d = device_get_softc(dev);
992e4e61333SAriff Abdullah 	d->dev = dev;
9932c69ba87SJohn Baldwin 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
994e4e61333SAriff Abdullah 	cv_init(&d->cv, device_get_nameunit(dev));
995e4e61333SAriff Abdullah 	PCM_ACQUIRE_QUICK(d);
996e4e61333SAriff Abdullah 	dsp_cdevinfo_init(d);
9977233ababSAlexander Leidinger #if 0
9987233ababSAlexander Leidinger 	/*
9997233ababSAlexander Leidinger 	 * d->flags should be cleared by the allocator of the softc.
10007233ababSAlexander Leidinger 	 * We cannot clear this field here because several devices set
10017233ababSAlexander Leidinger 	 * this flag before calling pcm_register().
10027233ababSAlexander Leidinger 	 */
1003cd9766c5SCameron Grant 	d->flags = 0;
10047233ababSAlexander Leidinger #endif
1005987e5972SCameron Grant 	d->devinfo = devinfo;
1006f637a36cSCameron Grant 	d->devcount = 0;
1007506a5308SCameron Grant 	d->reccount = 0;
1008a67fe5c1SCameron Grant 	d->playcount = 0;
1009bba4862cSAriff Abdullah 	d->pvchancount = 0;
1010bba4862cSAriff Abdullah 	d->rvchancount = 0;
1011bba4862cSAriff Abdullah 	d->pvchanrate = 0;
1012bba4862cSAriff Abdullah 	d->pvchanformat = 0;
1013bba4862cSAriff Abdullah 	d->rvchanrate = 0;
1014bba4862cSAriff Abdullah 	d->rvchanformat = 0;
1015d95502a8SCameron Grant 	d->inprog = 0;
1016833f7023SCameron Grant 
1017bba4862cSAriff Abdullah 	/*
1018bba4862cSAriff Abdullah 	 * Create clone manager, disabled by default. Cloning will be
1019bba4862cSAriff Abdullah 	 * enabled during final stage of driver iniialization through
1020bba4862cSAriff Abdullah 	 * pcm_setstatus().
1021bba4862cSAriff Abdullah 	 */
1022e4e61333SAriff Abdullah 	d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE,
1023e4e61333SAriff Abdullah 	    SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK |
1024bba4862cSAriff Abdullah 	    SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF |
1025bba4862cSAriff Abdullah 	    SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED);
1026bba4862cSAriff Abdullah 
1027bba4862cSAriff Abdullah 	if (bootverbose != 0 || snd_verbose > 3) {
1028bba4862cSAriff Abdullah 		device_printf(dev,
1029bba4862cSAriff Abdullah 		    "clone manager: deadline=%dms flags=0x%08x\n",
1030bba4862cSAriff Abdullah 		    snd_clone_getdeadline(d->clones),
1031bba4862cSAriff Abdullah 		    snd_clone_getflags(d->clones));
1032bba4862cSAriff Abdullah 	}
1033bba4862cSAriff Abdullah 
1034bba4862cSAriff Abdullah 	CHN_INIT(d, channels.pcm);
1035bba4862cSAriff Abdullah 	CHN_INIT(d, channels.pcm.busy);
103645550658SPoul-Henning Kamp 
1037e4e61333SAriff Abdullah 	/* XXX This is incorrect, but lets play along for now. */
1038a1d444e1SAriff Abdullah 	if ((numplay == 0 || numrec == 0) && numplay != numrec)
1039285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
1040d95502a8SCameron Grant 
1041285648f9SCameron Grant 	d->fakechan = fkchan_setup(dev);
1042a3193a9cSDon Lewis 	chn_init(d->fakechan, NULL, 0, 0);
1043987e5972SCameron Grant 
104482db23e2SCameron Grant #ifdef SND_DYNSYSCTL
1045bba4862cSAriff Abdullah 	sysctl_ctx_init(&d->play_sysctl_ctx);
1046bba4862cSAriff Abdullah 	d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
1047bba4862cSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
1048bba4862cSAriff Abdullah 	    CTLFLAG_RD, 0, "playback channels node");
1049bba4862cSAriff Abdullah 	sysctl_ctx_init(&d->rec_sysctl_ctx);
1050bba4862cSAriff Abdullah 	d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
1051bba4862cSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
1052bba4862cSAriff Abdullah 	    CTLFLAG_RD, 0, "record channels node");
1053851a904aSAlexander Leidinger 	/* XXX: an user should be able to set this with a control tool, the
1054851a904aSAlexander Leidinger 	   sysadmin then needs min+max sysctls for this */
1055a580b31aSAriff Abdullah 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
1056a580b31aSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
1057a580b31aSAriff Abdullah             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
1058bba4862cSAriff Abdullah #ifdef SND_DEBUG
1059bba4862cSAriff Abdullah 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1060bba4862cSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1061bba4862cSAriff Abdullah 	    "clone_flags", CTLTYPE_UINT | CTLFLAG_RW, d, sizeof(d),
1062bba4862cSAriff Abdullah 	    sysctl_dev_pcm_clone_flags, "IU",
1063bba4862cSAriff Abdullah 	    "clone flags");
1064bba4862cSAriff Abdullah 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1065bba4862cSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1066bba4862cSAriff Abdullah 	    "clone_deadline", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
1067bba4862cSAriff Abdullah 	    sysctl_dev_pcm_clone_deadline, "I",
1068bba4862cSAriff Abdullah 	    "clone expiration deadline (ms)");
1069bba4862cSAriff Abdullah 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1070bba4862cSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1071bba4862cSAriff Abdullah 	    "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
1072bba4862cSAriff Abdullah 	    sysctl_dev_pcm_clone_gc, "I",
1073bba4862cSAriff Abdullah 	    "clone garbage collector");
107482db23e2SCameron Grant #endif
1075bba4862cSAriff Abdullah #endif
1076e4e61333SAriff Abdullah 
1077bba4862cSAriff Abdullah 	if (numplay > 0 || numrec > 0) {
1078cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
10793fdb3676SAriff Abdullah 		vchan_initsys(dev);
108097d69a96SAlexander Leidinger 	}
1081cd9766c5SCameron Grant 
108267b1dce3SCameron Grant 	sndstat_register(dev, d->status, sndstat_prepare_pcm);
1083e4e61333SAriff Abdullah 
1084987e5972SCameron Grant 	return 0;
1085987e5972SCameron Grant }
1086987e5972SCameron Grant 
108733dbf14aSCameron Grant int
108833dbf14aSCameron Grant pcm_unregister(device_t dev)
10897c438dbeSCameron Grant {
1090e4e61333SAriff Abdullah 	struct snddev_info *d;
1091a67fe5c1SCameron Grant 	struct pcm_channel *ch;
1092bba4862cSAriff Abdullah 	struct thread *td;
1093bba4862cSAriff Abdullah 	int i;
10947c438dbeSCameron Grant 
1095bba4862cSAriff Abdullah 	td = curthread;
1096e4e61333SAriff Abdullah 	d = device_get_softc(dev);
1097e4e61333SAriff Abdullah 
1098e4e61333SAriff Abdullah 	if (!PCM_ALIVE(d)) {
1099e4e61333SAriff Abdullah 		device_printf(dev, "unregister: device not configured\n");
1100e4e61333SAriff Abdullah 		return (0);
1101e4e61333SAriff Abdullah 	}
1102bba4862cSAriff Abdullah 
1103bba4862cSAriff Abdullah 	if (sndstat_acquire(td) != 0) {
110428ef3fb0SAlexander Leidinger 		device_printf(dev, "unregister: sndstat busy\n");
1105e4e61333SAriff Abdullah 		return (EBUSY);
110628ef3fb0SAlexander Leidinger 	}
110728ef3fb0SAlexander Leidinger 
1108bba4862cSAriff Abdullah 	pcm_lock(d);
1109e4e61333SAriff Abdullah 	PCM_WAIT(d);
1110e4e61333SAriff Abdullah 
1111e4e61333SAriff Abdullah 	if (d->inprog != 0) {
11125c25132aSGeorge C A Reid 		device_printf(dev, "unregister: operation in progress\n");
1113bba4862cSAriff Abdullah 		pcm_unlock(d);
1114bba4862cSAriff Abdullah 		sndstat_release(td);
1115e4e61333SAriff Abdullah 		return (EBUSY);
11165c25132aSGeorge C A Reid 	}
11175ee30e27SMathew Kanner 
1118e4e61333SAriff Abdullah 	PCM_ACQUIRE(d);
1119bba4862cSAriff Abdullah 	pcm_unlock(d);
1120e4e61333SAriff Abdullah 
1121e4e61333SAriff Abdullah 	CHN_FOREACH(ch, d, channels.pcm) {
1122e4e61333SAriff Abdullah 		CHN_LOCK(ch);
1123e4e61333SAriff Abdullah 		if (ch->refcount > 0) {
1124e4e61333SAriff Abdullah 			device_printf(dev,
1125e4e61333SAriff Abdullah 			    "unregister: channel %s busy (pid %d)\n",
1126e4e61333SAriff Abdullah 			    ch->name, ch->pid);
1127e4e61333SAriff Abdullah 			CHN_UNLOCK(ch);
1128e4e61333SAriff Abdullah 			PCM_RELEASE_QUICK(d);
1129bba4862cSAriff Abdullah 			sndstat_release(td);
1130e4e61333SAriff Abdullah 			return (EBUSY);
1131285648f9SCameron Grant 		}
1132e4e61333SAriff Abdullah 		CHN_UNLOCK(ch);
1133c9b53085SCameron Grant 	}
11345ee30e27SMathew Kanner 
1135bba4862cSAriff Abdullah 	if (d->clones != NULL) {
1136bba4862cSAriff Abdullah 		if (snd_clone_busy(d->clones) != 0) {
1137bba4862cSAriff Abdullah 			device_printf(dev, "unregister: clone busy\n");
1138e4e61333SAriff Abdullah 			PCM_RELEASE_QUICK(d);
1139bba4862cSAriff Abdullah 			sndstat_release(td);
1140e4e61333SAriff Abdullah 			return (EBUSY);
1141e4e61333SAriff Abdullah 		} else {
1142e4e61333SAriff Abdullah 			pcm_lock(d);
1143bba4862cSAriff Abdullah 			(void)snd_clone_disable(d->clones);
1144e4e61333SAriff Abdullah 			pcm_unlock(d);
1145e4e61333SAriff Abdullah 		}
1146bba4862cSAriff Abdullah 	}
1147bba4862cSAriff Abdullah 
1148b4221868SAriff Abdullah 	if (mixer_uninit(dev) == EBUSY) {
11497233ababSAlexander Leidinger 		device_printf(dev, "unregister: mixer busy\n");
1150e4e61333SAriff Abdullah 		pcm_lock(d);
1151bba4862cSAriff Abdullah 		if (d->clones != NULL)
1152bba4862cSAriff Abdullah 			(void)snd_clone_enable(d->clones);
1153e4e61333SAriff Abdullah 		PCM_RELEASE(d);
1154bba4862cSAriff Abdullah 		pcm_unlock(d);
1155bba4862cSAriff Abdullah 		sndstat_release(td);
1156e4e61333SAriff Abdullah 		return (EBUSY);
11577233ababSAlexander Leidinger 	}
11587233ababSAlexander Leidinger 
1159e4e61333SAriff Abdullah 	pcm_lock(d);
1160e4e61333SAriff Abdullah 	d->flags |= SD_F_DYING;
1161e4e61333SAriff Abdullah 	d->flags &= ~SD_F_REGISTERED;
1162e4e61333SAriff Abdullah 	pcm_unlock(d);
1163e4e61333SAriff Abdullah 
1164e4e61333SAriff Abdullah 	/*
1165e4e61333SAriff Abdullah 	 * No lock being held, so this thing can be flushed without
1166e4e61333SAriff Abdullah 	 * stucking into devdrn oblivion.
1167e4e61333SAriff Abdullah 	 */
1168bba4862cSAriff Abdullah 	if (d->clones != NULL) {
1169bba4862cSAriff Abdullah 		snd_clone_destroy(d->clones);
1170bba4862cSAriff Abdullah 		d->clones = NULL;
11717982e7a4SAriff Abdullah 	}
1172bba4862cSAriff Abdullah 
117366ef8af5SCameron Grant #ifdef SND_DYNSYSCTL
1174bba4862cSAriff Abdullah 	if (d->play_sysctl_tree != NULL) {
1175bba4862cSAriff Abdullah 		sysctl_ctx_free(&d->play_sysctl_ctx);
1176bba4862cSAriff Abdullah 		d->play_sysctl_tree = NULL;
1177bba4862cSAriff Abdullah 	}
1178bba4862cSAriff Abdullah 	if (d->rec_sysctl_tree != NULL) {
1179bba4862cSAriff Abdullah 		sysctl_ctx_free(&d->rec_sysctl_ctx);
1180bba4862cSAriff Abdullah 		d->rec_sysctl_tree = NULL;
1181a1d444e1SAriff Abdullah 	}
1182a1d444e1SAriff Abdullah #endif
1183bba4862cSAriff Abdullah 
1184bba4862cSAriff Abdullah 	while (!CHN_EMPTY(d, channels.pcm))
1185285648f9SCameron Grant 		pcm_killchan(dev);
11867c438dbeSCameron Grant 
118766ef8af5SCameron Grant 	chn_kill(d->fakechan);
118866ef8af5SCameron Grant 	fkchan_kill(d->fakechan);
118982db23e2SCameron Grant 
1190e4e61333SAriff Abdullah 	dsp_cdevinfo_flush(d);
1191e4e61333SAriff Abdullah 
1192e4e61333SAriff Abdullah 	pcm_lock(d);
1193e4e61333SAriff Abdullah 	PCM_RELEASE(d);
1194e4e61333SAriff Abdullah 	cv_destroy(&d->cv);
1195bba4862cSAriff Abdullah 	pcm_unlock(d);
119649c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
119728ef3fb0SAlexander Leidinger 	sndstat_unregister(dev);
1198bba4862cSAriff Abdullah 	sndstat_release(td);
1199bba4862cSAriff Abdullah 
1200bba4862cSAriff Abdullah 	if (snd_unit == device_get_unit(dev)) {
1201bba4862cSAriff Abdullah 		/*
1202bba4862cSAriff Abdullah 		 * Reassign default unit to the next available dev.
1203bba4862cSAriff Abdullah 		 */
12049c271f79SAriff Abdullah 		for (i = 0; pcm_devclass != NULL &&
12059c271f79SAriff Abdullah 		    i < devclass_get_maxunit(pcm_devclass); i++) {
1206e4e61333SAriff Abdullah 			if (device_get_unit(dev) == i)
1207bba4862cSAriff Abdullah 				continue;
1208e4e61333SAriff Abdullah 			d = devclass_get_softc(pcm_devclass, i);
1209e4e61333SAriff Abdullah 			if (PCM_REGISTERED(d)) {
1210bba4862cSAriff Abdullah 				snd_unit = i;
1211bba4862cSAriff Abdullah 				break;
1212bba4862cSAriff Abdullah 			}
1213bba4862cSAriff Abdullah 		}
1214e4e61333SAriff Abdullah 	}
1215bba4862cSAriff Abdullah 
1216e4e61333SAriff Abdullah 	return (0);
121733dbf14aSCameron Grant }
12187c438dbeSCameron Grant 
121967b1dce3SCameron Grant /************************************************************************/
122067b1dce3SCameron Grant 
122167b1dce3SCameron Grant static int
122267b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
122367b1dce3SCameron Grant {
122467b1dce3SCameron Grant 	struct snddev_info *d;
122567b1dce3SCameron Grant 	struct pcm_channel *c;
122667b1dce3SCameron Grant 	struct pcm_feeder *f;
122767b1dce3SCameron Grant 
122867b1dce3SCameron Grant 	if (verbose < 1)
122967b1dce3SCameron Grant 		return 0;
123067b1dce3SCameron Grant 
123167b1dce3SCameron Grant 	d = device_get_softc(dev);
123267b1dce3SCameron Grant 	if (!d)
123367b1dce3SCameron Grant 		return ENXIO;
123467b1dce3SCameron Grant 
1235e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
1236e4e61333SAriff Abdullah 
1237e4e61333SAriff Abdullah 	if (CHN_EMPTY(d, channels.pcm)) {
1238e4e61333SAriff Abdullah 		sbuf_printf(s, " (mixer only)");
1239e4e61333SAriff Abdullah 		return 0;
1240e4e61333SAriff Abdullah 	}
1241e4e61333SAriff Abdullah 
1242bba4862cSAriff Abdullah 	sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)",
1243bba4862cSAriff Abdullah 			d->playcount, d->pvchancount,
1244bba4862cSAriff Abdullah 			d->reccount, d->rvchancount,
124567b1dce3SCameron Grant 			(d->flags & SD_F_SIMPLEX)? "" : " duplex",
124667b1dce3SCameron Grant #ifdef USING_DEVFS
124767b1dce3SCameron Grant 			(device_get_unit(dev) == snd_unit)? " default" : ""
124867b1dce3SCameron Grant #else
124967b1dce3SCameron Grant 			""
125067b1dce3SCameron Grant #endif
125167b1dce3SCameron Grant 			);
1252a527dbc7SCameron Grant 
1253e4e61333SAriff Abdullah 	if (verbose <= 1)
1254a527dbc7SCameron Grant 		return 0;
1255a527dbc7SCameron Grant 
1256bba4862cSAriff Abdullah 	CHN_FOREACH(c, d, channels.pcm) {
12579d978cc7SAlexander Leidinger 
12589d978cc7SAlexander Leidinger 		KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
12599d978cc7SAlexander Leidinger 			("hosed pcm channel setup"));
12609d978cc7SAlexander Leidinger 
1261a3285889SCameron Grant 		sbuf_printf(s, "\n\t");
1262a3285889SCameron Grant 
126345550658SPoul-Henning Kamp 		/* it would be better to indent child channels */
1264a3285889SCameron Grant 		sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
12654c68642aSCameron Grant 		sbuf_printf(s, "spd %d", c->speed);
12664c68642aSCameron Grant 		if (c->speed != sndbuf_getspd(c->bufhard))
12674c68642aSCameron Grant 			sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
12684c68642aSCameron Grant 		sbuf_printf(s, ", fmt 0x%08x", c->format);
12694c68642aSCameron Grant 		if (c->format != sndbuf_getfmt(c->bufhard))
12704c68642aSCameron Grant 			sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1271a527dbc7SCameron Grant 		sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
127267b1dce3SCameron Grant 		if (c->pid != -1)
127367b1dce3SCameron Grant 			sbuf_printf(s, ", pid %d", c->pid);
127467b1dce3SCameron Grant 		sbuf_printf(s, "\n\t");
12754c68642aSCameron Grant 
1276a3285889SCameron Grant 		sbuf_printf(s, "interrupts %d, ", c->interrupts);
1277a3285889SCameron Grant 		if (c->direction == PCMDIR_REC)
1278a580b31aSAriff Abdullah 			sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1279a580b31aSAriff Abdullah 				c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
128097d69a96SAlexander Leidinger 				sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
128197d69a96SAlexander Leidinger 				sndbuf_getblkcnt(c->bufhard),
128297d69a96SAlexander Leidinger 				sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
128397d69a96SAlexander Leidinger 				sndbuf_getblkcnt(c->bufsoft));
1284a3285889SCameron Grant 		else
1285a580b31aSAriff Abdullah 			sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1286a580b31aSAriff Abdullah 				c->xruns, c->feedcount, sndbuf_getready(c->bufsoft),
128797d69a96SAlexander Leidinger 				sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
128897d69a96SAlexander Leidinger 				sndbuf_getblkcnt(c->bufhard),
128997d69a96SAlexander Leidinger 				sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
129097d69a96SAlexander Leidinger 				sndbuf_getblkcnt(c->bufsoft));
1291a3285889SCameron Grant 		sbuf_printf(s, "\n\t");
12924c68642aSCameron Grant 
12934c68642aSCameron Grant 		sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
12944c68642aSCameron Grant 		sbuf_printf(s, " -> ");
129567b1dce3SCameron Grant 		f = c->feeder;
12964c68642aSCameron Grant 		while (f->source != NULL)
12974c68642aSCameron Grant 			f = f->source;
12984c68642aSCameron Grant 		while (f != NULL) {
129967b1dce3SCameron Grant 			sbuf_printf(s, "%s", f->class->name);
130067b1dce3SCameron Grant 			if (f->desc->type == FEEDER_FMT)
13014c68642aSCameron Grant 				sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
130267b1dce3SCameron Grant 			if (f->desc->type == FEEDER_RATE)
13034c68642aSCameron Grant 				sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
130428ef3fb0SAlexander Leidinger 			if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
130528ef3fb0SAlexander Leidinger 					f->desc->type == FEEDER_VOLUME)
13064c68642aSCameron Grant 				sbuf_printf(s, "(0x%08x)", f->desc->out);
13074c68642aSCameron Grant 			sbuf_printf(s, " -> ");
13084c68642aSCameron Grant 			f = f->parent;
130967b1dce3SCameron Grant 		}
13104c68642aSCameron Grant 		sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
131167b1dce3SCameron Grant 	}
131267b1dce3SCameron Grant 
131367b1dce3SCameron Grant 	return 0;
131467b1dce3SCameron Grant }
131567b1dce3SCameron Grant 
131667b1dce3SCameron Grant /************************************************************************/
131767b1dce3SCameron Grant 
131867b1dce3SCameron Grant #ifdef SND_DYNSYSCTL
131967b1dce3SCameron Grant int
132067b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
132167b1dce3SCameron Grant {
132267b1dce3SCameron Grant 	struct snddev_info *d;
1323bba4862cSAriff Abdullah 	int direction, vchancount;
1324e4e61333SAriff Abdullah 	int err, cnt;
132567b1dce3SCameron Grant 
1326bba4862cSAriff Abdullah 	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
1327e4e61333SAriff Abdullah 	if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN))
1328e4e61333SAriff Abdullah 		return (EINVAL);
1329e4e61333SAriff Abdullah 
1330e4e61333SAriff Abdullah 	pcm_lock(d);
1331e4e61333SAriff Abdullah 	PCM_WAIT(d);
133267b1dce3SCameron Grant 
1333bba4862cSAriff Abdullah 	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
1334bba4862cSAriff Abdullah 	case VCHAN_PLAY:
1335bba4862cSAriff Abdullah 		direction = PCMDIR_PLAY;
1336bba4862cSAriff Abdullah 		vchancount = d->pvchancount;
1337e4e61333SAriff Abdullah 		cnt = d->playcount;
1338bba4862cSAriff Abdullah 		break;
1339bba4862cSAriff Abdullah 	case VCHAN_REC:
1340bba4862cSAriff Abdullah 		direction = PCMDIR_REC;
1341bba4862cSAriff Abdullah 		vchancount = d->rvchancount;
1342e4e61333SAriff Abdullah 		cnt = d->reccount;
1343bba4862cSAriff Abdullah 		break;
1344bba4862cSAriff Abdullah 	default:
1345e4e61333SAriff Abdullah 		pcm_unlock(d);
1346e4e61333SAriff Abdullah 		return (EINVAL);
1347bba4862cSAriff Abdullah 		break;
1348bba4862cSAriff Abdullah 	}
1349bba4862cSAriff Abdullah 
1350e4e61333SAriff Abdullah 	if (cnt < 1) {
1351e4e61333SAriff Abdullah 		pcm_unlock(d);
1352e4e61333SAriff Abdullah 		return (ENODEV);
1353bba4862cSAriff Abdullah 	}
1354a527dbc7SCameron Grant 
1355e4e61333SAriff Abdullah 	PCM_ACQUIRE(d);
1356e4e61333SAriff Abdullah 	pcm_unlock(d);
1357e4e61333SAriff Abdullah 
1358e4e61333SAriff Abdullah 	cnt = vchancount;
1359e4e61333SAriff Abdullah 	err = sysctl_handle_int(oidp, &cnt, 0, req);
1360e4e61333SAriff Abdullah 
1361e4e61333SAriff Abdullah 	if (err == 0 && req->newptr != NULL && vchancount != cnt) {
1362e4e61333SAriff Abdullah 		if (cnt < 0)
1363e4e61333SAriff Abdullah 			cnt = 0;
1364e4e61333SAriff Abdullah 		if (cnt > SND_MAXVCHANS)
1365e4e61333SAriff Abdullah 			cnt = SND_MAXVCHANS;
1366e4e61333SAriff Abdullah 		err = pcm_setvchans(d, direction, cnt, -1);
1367e4e61333SAriff Abdullah 	}
1368e4e61333SAriff Abdullah 
1369e4e61333SAriff Abdullah 	PCM_RELEASE_QUICK(d);
1370e4e61333SAriff Abdullah 
137167b1dce3SCameron Grant 	return err;
137267b1dce3SCameron Grant }
137367b1dce3SCameron Grant #endif
137467b1dce3SCameron Grant 
137567b1dce3SCameron Grant /************************************************************************/
137667b1dce3SCameron Grant 
1377b611c801SAlexander Leidinger /**
1378b611c801SAlexander Leidinger  * @brief	Handle OSSv4 SNDCTL_SYSINFO ioctl.
1379b611c801SAlexander Leidinger  *
1380b611c801SAlexander Leidinger  * @param si	Pointer to oss_sysinfo struct where information about the
1381b611c801SAlexander Leidinger  * 		sound subsystem will be written/copied.
1382b611c801SAlexander Leidinger  *
1383b611c801SAlexander Leidinger  * This routine returns information about the sound system, such as the
1384b611c801SAlexander Leidinger  * current OSS version, number of audio, MIDI, and mixer drivers, etc.
1385b611c801SAlexander Leidinger  * Also includes a bitmask showing which of the above types of devices
1386b611c801SAlexander Leidinger  * are open (busy).
1387b611c801SAlexander Leidinger  *
1388b611c801SAlexander Leidinger  * @note
1389b611c801SAlexander Leidinger  * Calling threads must not hold any snddev_info or pcm_channel locks.
1390b611c801SAlexander Leidinger  *
1391b611c801SAlexander Leidinger  * @author	Ryan Beasley <ryanb@FreeBSD.org>
1392b611c801SAlexander Leidinger  */
1393b611c801SAlexander Leidinger void
1394b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si)
1395b611c801SAlexander Leidinger {
1396b611c801SAlexander Leidinger 	static char si_product[] = "FreeBSD native OSS ABI";
1397b611c801SAlexander Leidinger 	static char si_version[] = __XSTRING(__FreeBSD_version);
1398b611c801SAlexander Leidinger 	static int intnbits = sizeof(int) * 8;	/* Better suited as macro?
1399b611c801SAlexander Leidinger 						   Must pester a C guru. */
1400b611c801SAlexander Leidinger 
1401b611c801SAlexander Leidinger 	struct snddev_info *d;
1402b611c801SAlexander Leidinger 	struct pcm_channel *c;
1403b611c801SAlexander Leidinger 	int i, j, ncards;
1404b611c801SAlexander Leidinger 
1405b611c801SAlexander Leidinger 	ncards = 0;
1406b611c801SAlexander Leidinger 
1407b611c801SAlexander Leidinger 	strlcpy(si->product, si_product, sizeof(si->product));
1408b611c801SAlexander Leidinger 	strlcpy(si->version, si_version, sizeof(si->version));
1409b611c801SAlexander Leidinger 	si->versionnum = SOUND_VERSION;
1410b611c801SAlexander Leidinger 
1411b611c801SAlexander Leidinger 	/*
1412b611c801SAlexander Leidinger 	 * Iterate over PCM devices and their channels, gathering up data
1413b611c801SAlexander Leidinger 	 * for the numaudios, ncards, and openedaudio fields.
1414b611c801SAlexander Leidinger 	 */
1415b611c801SAlexander Leidinger 	si->numaudios = 0;
1416b611c801SAlexander Leidinger 	bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
1417b611c801SAlexander Leidinger 
1418b611c801SAlexander Leidinger 	j = 0;
1419b611c801SAlexander Leidinger 
14209c271f79SAriff Abdullah 	for (i = 0; pcm_devclass != NULL &&
14219c271f79SAriff Abdullah 	    i < devclass_get_maxunit(pcm_devclass); i++) {
1422b611c801SAlexander Leidinger 		d = devclass_get_softc(pcm_devclass, i);
1423e4e61333SAriff Abdullah 		if (!PCM_REGISTERED(d))
1424b611c801SAlexander Leidinger 			continue;
1425b611c801SAlexander Leidinger 
1426e4e61333SAriff Abdullah 		/* XXX Need Giant magic entry ??? */
1427e4e61333SAriff Abdullah 
1428b611c801SAlexander Leidinger 		/* See note in function's docblock */
1429b611c801SAlexander Leidinger 		mtx_assert(d->lock, MA_NOTOWNED);
1430b611c801SAlexander Leidinger 		pcm_lock(d);
1431b611c801SAlexander Leidinger 
1432b611c801SAlexander Leidinger 		si->numaudios += d->devcount;
1433b611c801SAlexander Leidinger 		++ncards;
1434b611c801SAlexander Leidinger 
1435bba4862cSAriff Abdullah 		CHN_FOREACH(c, d, channels.pcm) {
1436b611c801SAlexander Leidinger 			mtx_assert(c->lock, MA_NOTOWNED);
1437b611c801SAlexander Leidinger 			CHN_LOCK(c);
1438b611c801SAlexander Leidinger 			if (c->flags & CHN_F_BUSY)
1439b611c801SAlexander Leidinger 				si->openedaudio[j / intnbits] |=
1440b611c801SAlexander Leidinger 				    (1 << (j % intnbits));
1441b611c801SAlexander Leidinger 			CHN_UNLOCK(c);
1442b611c801SAlexander Leidinger 			j++;
1443b611c801SAlexander Leidinger 		}
1444b611c801SAlexander Leidinger 
1445b611c801SAlexander Leidinger 		pcm_unlock(d);
1446b611c801SAlexander Leidinger 	}
1447b611c801SAlexander Leidinger 
1448b611c801SAlexander Leidinger 	si->numsynths = 0;	/* OSSv4 docs:  this field is obsolete */
1449b611c801SAlexander Leidinger 	/**
1450b611c801SAlexander Leidinger 	 * @todo	Collect num{midis,timers}.
1451b611c801SAlexander Leidinger 	 *
1452b611c801SAlexander Leidinger 	 * Need access to sound/midi/midi.c::midistat_lock in order
1453b611c801SAlexander Leidinger 	 * to safely touch midi_devices and get a head count of, well,
1454b611c801SAlexander Leidinger 	 * MIDI devices.  midistat_lock is a global static (i.e., local to
1455b611c801SAlexander Leidinger 	 * midi.c), but midi_devices is a regular global; should the mutex
1456b611c801SAlexander Leidinger 	 * be publicized, or is there another way to get this information?
1457b611c801SAlexander Leidinger 	 *
1458b611c801SAlexander Leidinger 	 * NB:	MIDI/sequencer stuff is currently on hold.
1459b611c801SAlexander Leidinger 	 */
1460b611c801SAlexander Leidinger 	si->nummidis = 0;
1461b611c801SAlexander Leidinger 	si->numtimers = 0;
1462b611c801SAlexander Leidinger 	si->nummixers = mixer_count;
1463b611c801SAlexander Leidinger 	si->numcards = ncards;
1464b611c801SAlexander Leidinger 		/* OSSv4 docs:	Intended only for test apps; API doesn't
1465b611c801SAlexander Leidinger 		   really have much of a concept of cards.  Shouldn't be
1466b611c801SAlexander Leidinger 		   used by applications. */
1467b611c801SAlexander Leidinger 
1468b611c801SAlexander Leidinger 	/**
1469b611c801SAlexander Leidinger 	 * @todo	Fill in "busy devices" fields.
1470b611c801SAlexander Leidinger 	 *
1471b611c801SAlexander Leidinger 	 *  si->openedmidi = " MIDI devices
1472b611c801SAlexander Leidinger 	 */
1473b611c801SAlexander Leidinger 	bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
1474b611c801SAlexander Leidinger 
1475b611c801SAlexander Leidinger 	/*
1476b611c801SAlexander Leidinger 	 * Si->filler is a reserved array, but according to docs each
1477b611c801SAlexander Leidinger 	 * element should be set to -1.
1478b611c801SAlexander Leidinger 	 */
1479b611c801SAlexander Leidinger 	for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++)
1480b611c801SAlexander Leidinger 		si->filler[i] = -1;
1481b611c801SAlexander Leidinger }
1482b611c801SAlexander Leidinger 
1483b611c801SAlexander Leidinger /************************************************************************/
1484b611c801SAlexander Leidinger 
14850739ea1dSSeigo Tanimura static int
14860739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data)
14870739ea1dSSeigo Tanimura {
1488b611c801SAlexander Leidinger 	int ret;
14897233ababSAlexander Leidinger #if 0
14900739ea1dSSeigo Tanimura 	return (midi_modevent(mod, type, data));
14917233ababSAlexander Leidinger #else
1492b611c801SAlexander Leidinger 	ret = 0;
1493b611c801SAlexander Leidinger 
1494b611c801SAlexander Leidinger 	switch(type) {
1495b611c801SAlexander Leidinger 		case MOD_LOAD:
1496b611c801SAlexander Leidinger 			pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
1497b611c801SAlexander Leidinger 			break;
1498b611c801SAlexander Leidinger 		case MOD_UNLOAD:
1499b611c801SAlexander Leidinger 		case MOD_SHUTDOWN:
1500bba4862cSAriff Abdullah 			ret = sndstat_acquire(curthread);
1501bba4862cSAriff Abdullah 			if (ret != 0)
1502bba4862cSAriff Abdullah 				break;
1503b611c801SAlexander Leidinger 			if (pcmsg_unrhdr != NULL) {
1504b611c801SAlexander Leidinger 				delete_unrhdr(pcmsg_unrhdr);
1505b611c801SAlexander Leidinger 				pcmsg_unrhdr = NULL;
1506b611c801SAlexander Leidinger 			}
1507b611c801SAlexander Leidinger 			break;
1508b611c801SAlexander Leidinger 		default:
1509b611c801SAlexander Leidinger 			ret = EOPNOTSUPP;
1510b611c801SAlexander Leidinger 	}
1511b611c801SAlexander Leidinger 
1512b611c801SAlexander Leidinger 	return ret;
15137233ababSAlexander Leidinger #endif
15140739ea1dSSeigo Tanimura }
15150739ea1dSSeigo Tanimura 
15160739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL);
15170739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER);
1518