xref: /freebsd/sys/dev/sound/pcm/sound.c (revision 516a9c0212b003e1da0c6f4476dbe4f3f431606c)
1098ca2bdSWarner Losh /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
490da2b28SAriff Abdullah  * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
590da2b28SAriff Abdullah  * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
690da2b28SAriff Abdullah  * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
740ac6e17SJoel Dahl  * Copyright (c) 1997 Luigi Rizzo
8987e5972SCameron Grant  * All rights reserved.
94f854658SChristos Margiolis  * Copyright (c) 2024 The FreeBSD Foundation
104f854658SChristos Margiolis  *
114f854658SChristos Margiolis  * Portions of this software were developed by Christos Margiolis
124f854658SChristos Margiolis  * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
13987e5972SCameron Grant  *
14987e5972SCameron Grant  * Redistribution and use in source and binary forms, with or without
15987e5972SCameron Grant  * modification, are permitted provided that the following conditions
16987e5972SCameron Grant  * are met:
17987e5972SCameron Grant  * 1. Redistributions of source code must retain the above copyright
18987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer.
19987e5972SCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
20987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer in the
21987e5972SCameron Grant  *    documentation and/or other materials provided with the distribution.
22987e5972SCameron Grant  *
23987e5972SCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24987e5972SCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25987e5972SCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26987e5972SCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27987e5972SCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28987e5972SCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29987e5972SCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30987e5972SCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31987e5972SCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32987e5972SCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33987e5972SCameron Grant  * SUCH DAMAGE.
34987e5972SCameron Grant  */
35987e5972SCameron Grant 
3690da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS
3790da2b28SAriff Abdullah #include "opt_snd.h"
3890da2b28SAriff Abdullah #endif
3990da2b28SAriff Abdullah 
40ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
41a580b31aSAriff Abdullah #include <dev/sound/pcm/ac97.h>
42285648f9SCameron Grant #include <dev/sound/pcm/vchan.h>
435ee30e27SMathew Kanner #include <dev/sound/pcm/dsp.h>
44b611c801SAlexander Leidinger #include <sys/limits.h>
457c438dbeSCameron Grant #include <sys/sysctl.h>
46285648f9SCameron Grant 
4767b1dce3SCameron Grant #include "feeder_if.h"
4867b1dce3SCameron Grant 
49d95502a8SCameron Grant devclass_t pcm_devclass;
5082db23e2SCameron Grant 
51f3685841SAriff Abdullah int snd_unit = -1;
52cbe7d6a3SCameron Grant 
53cbebc90dSAlexander Motin static int snd_unit_auto = -1;
54af3b2549SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN,
55838d3589SAriff Abdullah     &snd_unit_auto, 0, "assign default unit to a newly attached device");
56ad8612b9SAriff Abdullah 
577029da5cSPawel Biernacki SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
587029da5cSPawel Biernacki     "Sound driver");
5982db23e2SCameron Grant 
6032a0e5d5SHans Petter Selasky static void pcm_sysinit(device_t);
6132a0e5d5SHans Petter Selasky 
62b611c801SAlexander Leidinger /**
63b611c801SAlexander Leidinger  * @brief Unit number allocator for syncgroup IDs
64b611c801SAlexander Leidinger  */
65b611c801SAlexander Leidinger struct unrhdr *pcmsg_unrhdr = NULL;
66b611c801SAlexander Leidinger 
6737209180SCameron Grant void *
snd_mtxcreate(const char * desc,const char * type)682c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type)
6937209180SCameron Grant {
7037209180SCameron Grant 	struct mtx *m;
7137209180SCameron Grant 
72a163d034SWarner Losh 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
7312e524a2SDon Lewis 	mtx_init(m, desc, type, MTX_DEF);
7412e524a2SDon Lewis 	return m;
7512e524a2SDon Lewis }
7612e524a2SDon Lewis 
7737209180SCameron Grant void
snd_mtxfree(void * m)7837209180SCameron Grant snd_mtxfree(void *m)
7937209180SCameron Grant {
8037209180SCameron Grant 	struct mtx *mtx = m;
8137209180SCameron Grant 
8237209180SCameron Grant 	mtx_destroy(mtx);
8337209180SCameron Grant 	free(mtx, M_DEVBUF);
8437209180SCameron Grant }
8537209180SCameron Grant 
8637209180SCameron Grant void
snd_mtxassert(void * m)8737209180SCameron Grant snd_mtxassert(void *m)
8837209180SCameron Grant {
89f00f162aSCameron Grant #ifdef INVARIANTS
9037209180SCameron Grant 	struct mtx *mtx = m;
9137209180SCameron Grant 
9237209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
9337209180SCameron Grant #endif
9437209180SCameron Grant }
9537209180SCameron Grant 
9637209180SCameron Grant int
snd_setup_intr(device_t dev,struct resource * res,int flags,driver_intr_t hand,void * param,void ** cookiep)9737209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
9837209180SCameron Grant {
99e4e61333SAriff Abdullah 	struct snddev_info *d;
10090da2b28SAriff Abdullah 
10137209180SCameron Grant 	flags &= INTR_MPSAFE;
10246700f12SPeter Wemm 	flags |= INTR_TYPE_AV;
103e4e61333SAriff Abdullah 	d = device_get_softc(dev);
104e4e61333SAriff Abdullah 	if (d != NULL && (flags & INTR_MPSAFE))
105e4e61333SAriff Abdullah 		d->flags |= SD_F_MPSAFE;
106e4e61333SAriff Abdullah 
107775fcb6eSBaptiste Daroussin 	return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep);
10837209180SCameron Grant }
10937209180SCameron Grant 
1103fdb3676SAriff Abdullah int
pcm_chnalloc(struct snddev_info * d,struct pcm_channel ** ch,int direction,pid_t pid,char * comm)1113fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
11225723d66SChristos Margiolis     pid_t pid, char *comm)
113285648f9SCameron Grant {
114285648f9SCameron Grant 	struct pcm_channel *c;
115b973a14dSChristos Margiolis 	int err, vchancount;
11625723d66SChristos Margiolis 	bool retry;
117bba4862cSAriff Abdullah 
11825723d66SChristos Margiolis 	KASSERT(d != NULL && ch != NULL &&
119bba4862cSAriff Abdullah 	    (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
12025723d66SChristos Margiolis 	    ("%s(): invalid d=%p ch=%p direction=%d pid=%d",
12125723d66SChristos Margiolis 	    __func__, d, ch, direction, pid));
122e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
123bba4862cSAriff Abdullah 
12490da2b28SAriff Abdullah 	*ch = NULL;
12590da2b28SAriff Abdullah 	vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount :
12690da2b28SAriff Abdullah 	    d->rvchancount;
12725723d66SChristos Margiolis 	retry = false;
128b973a14dSChristos Margiolis 
1293fdb3676SAriff Abdullah retry_chnalloc:
130b973a14dSChristos Margiolis 	/* Scan for a free channel. */
131bba4862cSAriff Abdullah 	CHN_FOREACH(c, d, channels.pcm) {
13249c5e6e2SCameron Grant 		CHN_LOCK(c);
133b973a14dSChristos Margiolis 		if (c->direction != direction) {
13490da2b28SAriff Abdullah 			CHN_UNLOCK(c);
135b973a14dSChristos Margiolis 			continue;
13690da2b28SAriff Abdullah 		}
137b973a14dSChristos Margiolis 		if (!(c->flags & CHN_F_BUSY)) {
138285648f9SCameron Grant 			c->flags |= CHN_F_BUSY;
139b8f0d9e0SCameron Grant 			c->pid = pid;
14090da2b28SAriff Abdullah 			strlcpy(c->comm, (comm != NULL) ? comm :
14190da2b28SAriff Abdullah 			    CHN_COMM_UNKNOWN, sizeof(c->comm));
1423fdb3676SAriff Abdullah 			*ch = c;
143b973a14dSChristos Margiolis 
144bba4862cSAriff Abdullah 			return (0);
145b973a14dSChristos Margiolis 		}
14649c5e6e2SCameron Grant 		CHN_UNLOCK(c);
147285648f9SCameron Grant 	}
148b973a14dSChristos Margiolis 	/* Maybe next time... */
14925723d66SChristos Margiolis 	if (retry)
150b973a14dSChristos Margiolis 		return (EBUSY);
151e4e61333SAriff Abdullah 
152b973a14dSChristos Margiolis 	/* No channel available. We also cannot create more VCHANs. */
15325723d66SChristos Margiolis 	if (!(vchancount > 0 && vchancount < snd_maxautovchans))
154b973a14dSChristos Margiolis 		return (ENOTSUP);
155b973a14dSChristos Margiolis 
156b973a14dSChristos Margiolis 	/* Increase the VCHAN count and try to get the new channel. */
1573af2beb8SChristos Margiolis 	err = vchan_setnew(d, direction, vchancount + 1);
158a1d444e1SAriff Abdullah 	if (err == 0) {
15925723d66SChristos Margiolis 		retry = true;
1603fdb3676SAriff Abdullah 		goto retry_chnalloc;
161f637a36cSCameron Grant 	}
162f637a36cSCameron Grant 
163bba4862cSAriff Abdullah 	return (err);
164285648f9SCameron Grant }
165285648f9SCameron Grant 
16633dbf14aSCameron Grant static int
sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)167851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
16833dbf14aSCameron Grant {
169b8f0d9e0SCameron Grant 	struct snddev_info *d;
17033dbf14aSCameron Grant 	int error, unit;
17133dbf14aSCameron Grant 
17233dbf14aSCameron Grant 	unit = snd_unit;
173041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &unit, 0, req);
17433dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
175b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
176e4e61333SAriff Abdullah 		if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
177b8f0d9e0SCameron Grant 			return EINVAL;
17833dbf14aSCameron Grant 		snd_unit = unit;
179cbebc90dSAlexander Motin 		snd_unit_auto = 0;
18033dbf14aSCameron Grant 	}
18133dbf14aSCameron Grant 	return (error);
18233dbf14aSCameron Grant }
183851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */
184b7d6c6b5SEitan Adler SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit,
1857029da5cSPawel Biernacki     CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0,
1867029da5cSPawel Biernacki     sizeof(int), sysctl_hw_snd_default_unit, "I",
187b7d6c6b5SEitan Adler     "default sound device");
188987e5972SCameron Grant 
189987e5972SCameron Grant int
pcm_addchan(device_t dev,int dir,kobj_class_t cls,void * devinfo)190285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
191285648f9SCameron Grant {
192285648f9SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
19367b1dce3SCameron Grant 	struct pcm_channel *ch;
194285648f9SCameron Grant 
195e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
196e4e61333SAriff Abdullah 
19790da2b28SAriff Abdullah 	PCM_LOCK(d);
1983af2beb8SChristos Margiolis 	ch = chn_init(d, NULL, cls, dir, devinfo);
199285648f9SCameron Grant 	if (!ch) {
2002e9962efSChristos Margiolis 		device_printf(d->dev, "chn_init(%s, %d, %p) failed\n",
201e4e61333SAriff Abdullah 		    cls->name, dir, devinfo);
20290da2b28SAriff Abdullah 		PCM_UNLOCK(d);
203e4e61333SAriff Abdullah 		return (ENODEV);
204285648f9SCameron Grant 	}
20590da2b28SAriff Abdullah 	PCM_UNLOCK(d);
206cd9766c5SCameron Grant 
207139bcec8SChristos Margiolis 	return (0);
208285648f9SCameron Grant }
209285648f9SCameron Grant 
21003614fcbSChristos Margiolis static void
pcm_killchans(struct snddev_info * d)21103614fcbSChristos Margiolis pcm_killchans(struct snddev_info *d)
212285648f9SCameron Grant {
213e33bee07SOlivier Houchard 	struct pcm_channel *ch;
21403614fcbSChristos Margiolis 	bool found;
215e4e61333SAriff Abdullah 
216e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
21703614fcbSChristos Margiolis 	do {
21803614fcbSChristos Margiolis 		found = false;
21903614fcbSChristos Margiolis 		CHN_FOREACH(ch, d, channels.pcm) {
22003614fcbSChristos Margiolis 			CHN_LOCK(ch);
22103614fcbSChristos Margiolis 			/*
22203614fcbSChristos Margiolis 			 * Make sure no channel has went to sleep in the
22303614fcbSChristos Margiolis 			 * meantime.
22403614fcbSChristos Margiolis 			 */
22503614fcbSChristos Margiolis 			chn_shutdown(ch);
22603614fcbSChristos Margiolis 			/*
22703614fcbSChristos Margiolis 			 * We have to give a thread sleeping in chn_sleep() a
22803614fcbSChristos Margiolis 			 * chance to observe that the channel is dead.
22903614fcbSChristos Margiolis 			 */
23003614fcbSChristos Margiolis 			if ((ch->flags & CHN_F_SLEEPING) == 0) {
23103614fcbSChristos Margiolis 				found = true;
23203614fcbSChristos Margiolis 				CHN_UNLOCK(ch);
23303614fcbSChristos Margiolis 				break;
23403614fcbSChristos Margiolis 			}
23503614fcbSChristos Margiolis 			CHN_UNLOCK(ch);
23603614fcbSChristos Margiolis 		}
237285648f9SCameron Grant 
23803614fcbSChristos Margiolis 		/*
23903614fcbSChristos Margiolis 		 * All channels are still sleeping. Sleep for a bit and try
24003614fcbSChristos Margiolis 		 * again to see if any of them is awake now.
24103614fcbSChristos Margiolis 		 */
24203614fcbSChristos Margiolis 		if (!found) {
24303614fcbSChristos Margiolis 			pause_sbt("pcmkillchans", SBT_1MS * 5, 0, 0);
24403614fcbSChristos Margiolis 			continue;
24503614fcbSChristos Margiolis 		}
246b3ea087cSChristos Margiolis 		chn_kill(ch);
24703614fcbSChristos Margiolis 	} while (!CHN_EMPTY(d, channels.pcm));
248285648f9SCameron Grant }
249285648f9SCameron Grant 
250cbebc90dSAlexander Motin static int
pcm_best_unit(int old)251cbebc90dSAlexander Motin pcm_best_unit(int old)
252cbebc90dSAlexander Motin {
253cbebc90dSAlexander Motin 	struct snddev_info *d;
254cbebc90dSAlexander Motin 	int i, best, bestprio, prio;
255cbebc90dSAlexander Motin 
256cbebc90dSAlexander Motin 	best = -1;
257cbebc90dSAlexander Motin 	bestprio = -100;
258cbebc90dSAlexander Motin 	for (i = 0; pcm_devclass != NULL &&
259cbebc90dSAlexander Motin 	    i < devclass_get_maxunit(pcm_devclass); i++) {
260cbebc90dSAlexander Motin 		d = devclass_get_softc(pcm_devclass, i);
261cbebc90dSAlexander Motin 		if (!PCM_REGISTERED(d))
262cbebc90dSAlexander Motin 			continue;
263cbebc90dSAlexander Motin 		prio = 0;
264cbebc90dSAlexander Motin 		if (d->playcount == 0)
265cbebc90dSAlexander Motin 			prio -= 10;
266cbebc90dSAlexander Motin 		if (d->reccount == 0)
267cbebc90dSAlexander Motin 			prio -= 2;
268cbebc90dSAlexander Motin 		if (prio > bestprio || (prio == bestprio && i == old)) {
269cbebc90dSAlexander Motin 			best = i;
270cbebc90dSAlexander Motin 			bestprio = prio;
271cbebc90dSAlexander Motin 		}
272cbebc90dSAlexander Motin 	}
273cbebc90dSAlexander Motin 	return (best);
274cbebc90dSAlexander Motin }
275cbebc90dSAlexander Motin 
276a1d444e1SAriff Abdullah uint32_t
pcm_getflags(device_t dev)277987e5972SCameron Grant pcm_getflags(device_t dev)
278987e5972SCameron Grant {
27966ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
28049c5e6e2SCameron Grant 
281987e5972SCameron Grant 	return d->flags;
282987e5972SCameron Grant }
283987e5972SCameron Grant 
284987e5972SCameron Grant void
pcm_setflags(device_t dev,uint32_t val)285a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val)
286987e5972SCameron Grant {
28766ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
288d95502a8SCameron Grant 
289987e5972SCameron Grant 	d->flags = val;
290987e5972SCameron Grant }
291987e5972SCameron Grant 
29239004e69SCameron Grant void *
pcm_getdevinfo(device_t dev)29339004e69SCameron Grant pcm_getdevinfo(device_t dev)
29439004e69SCameron Grant {
29566ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
29649c5e6e2SCameron Grant 
29739004e69SCameron Grant 	return d->devinfo;
29839004e69SCameron Grant }
29939004e69SCameron Grant 
300a67fe5c1SCameron Grant unsigned int
pcm_getbuffersize(device_t dev,unsigned int minbufsz,unsigned int deflt,unsigned int maxbufsz)301d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
302a67fe5c1SCameron Grant {
303a67fe5c1SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
3044e60be34SCameron Grant 	int sz, x;
305a67fe5c1SCameron Grant 
306a67fe5c1SCameron Grant 	sz = 0;
3074e60be34SCameron Grant 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
3084e60be34SCameron Grant 		x = sz;
309d55d96f6SAlexander Leidinger 		RANGE(sz, minbufsz, maxbufsz);
3104e60be34SCameron Grant 		if (x != sz)
311d55d96f6SAlexander Leidinger 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
312d55d96f6SAlexander Leidinger 		x = minbufsz;
3134e60be34SCameron Grant 		while (x < sz)
3144e60be34SCameron Grant 			x <<= 1;
3154e60be34SCameron Grant 		if (x > sz)
3164e60be34SCameron Grant 			x >>= 1;
3174e60be34SCameron Grant 		if (x != sz) {
3184e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
3194e60be34SCameron Grant 			sz = x;
3204e60be34SCameron Grant 		}
3214e60be34SCameron Grant 	} else {
322a67fe5c1SCameron Grant 		sz = deflt;
3234e60be34SCameron Grant 	}
3244e60be34SCameron Grant 
325a67fe5c1SCameron Grant 	d->bufsz = sz;
326a67fe5c1SCameron Grant 
327a67fe5c1SCameron Grant 	return sz;
328a67fe5c1SCameron Grant }
329a67fe5c1SCameron Grant 
33090da2b28SAriff Abdullah static int
sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)33190da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)
33290da2b28SAriff Abdullah {
33390da2b28SAriff Abdullah 	struct snddev_info *d;
33490da2b28SAriff Abdullah 	int err, val;
33590da2b28SAriff Abdullah 
33690da2b28SAriff Abdullah 	d = oidp->oid_arg1;
33790da2b28SAriff Abdullah 	if (!PCM_REGISTERED(d))
33890da2b28SAriff Abdullah 		return (ENODEV);
33990da2b28SAriff Abdullah 
34090da2b28SAriff Abdullah 	PCM_LOCK(d);
34190da2b28SAriff Abdullah 	PCM_WAIT(d);
34290da2b28SAriff Abdullah 	val = (d->flags & SD_F_BITPERFECT) ? 1 : 0;
34390da2b28SAriff Abdullah 	PCM_ACQUIRE(d);
34490da2b28SAriff Abdullah 	PCM_UNLOCK(d);
34590da2b28SAriff Abdullah 
34690da2b28SAriff Abdullah 	err = sysctl_handle_int(oidp, &val, 0, req);
34790da2b28SAriff Abdullah 
34890da2b28SAriff Abdullah 	if (err == 0 && req->newptr != NULL) {
34990da2b28SAriff Abdullah 		if (!(val == 0 || val == 1)) {
35090da2b28SAriff Abdullah 			PCM_RELEASE_QUICK(d);
35190da2b28SAriff Abdullah 			return (EINVAL);
35290da2b28SAriff Abdullah 		}
35390da2b28SAriff Abdullah 
35490da2b28SAriff Abdullah 		PCM_LOCK(d);
35590da2b28SAriff Abdullah 
35690da2b28SAriff Abdullah 		d->flags &= ~SD_F_BITPERFECT;
35790da2b28SAriff Abdullah 		d->flags |= (val != 0) ? SD_F_BITPERFECT : 0;
35890da2b28SAriff Abdullah 
35990da2b28SAriff Abdullah 		PCM_RELEASE(d);
36090da2b28SAriff Abdullah 		PCM_UNLOCK(d);
36190da2b28SAriff Abdullah 	} else
36290da2b28SAriff Abdullah 		PCM_RELEASE_QUICK(d);
36390da2b28SAriff Abdullah 
36490da2b28SAriff Abdullah 	return (err);
36590da2b28SAriff Abdullah }
36690da2b28SAriff Abdullah 
367ed2196e5SHans Petter Selasky static u_int8_t
pcm_mode_init(struct snddev_info * d)368ed2196e5SHans Petter Selasky pcm_mode_init(struct snddev_info *d)
369ed2196e5SHans Petter Selasky {
370ed2196e5SHans Petter Selasky 	u_int8_t mode = 0;
371ed2196e5SHans Petter Selasky 
372ed2196e5SHans Petter Selasky 	if (d->playcount > 0)
373ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_PLAY;
374ed2196e5SHans Petter Selasky 	if (d->reccount > 0)
375ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_REC;
376ed2196e5SHans Petter Selasky 	if (d->mixer_dev != NULL)
377ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_MIXER;
378ed2196e5SHans Petter Selasky 
379ed2196e5SHans Petter Selasky 	return (mode);
380ed2196e5SHans Petter Selasky }
381ed2196e5SHans Petter Selasky 
38232a0e5d5SHans Petter Selasky static void
pcm_sysinit(device_t dev)38332a0e5d5SHans Petter Selasky pcm_sysinit(device_t dev)
38432a0e5d5SHans Petter Selasky {
38532a0e5d5SHans Petter Selasky   	struct snddev_info *d = device_get_softc(dev);
386ed2196e5SHans Petter Selasky 	u_int8_t mode;
387ed2196e5SHans Petter Selasky 
388ed2196e5SHans Petter Selasky 	mode = pcm_mode_init(d);
38932a0e5d5SHans Petter Selasky 
39066f3eb14SChristos Margiolis 	sysctl_ctx_init(&d->play_sysctl_ctx);
39166f3eb14SChristos Margiolis 	d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
39266f3eb14SChristos Margiolis 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
39366f3eb14SChristos Margiolis 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node");
39466f3eb14SChristos Margiolis 	sysctl_ctx_init(&d->rec_sysctl_ctx);
39566f3eb14SChristos Margiolis 	d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
39666f3eb14SChristos Margiolis 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
39766f3eb14SChristos Margiolis 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node");
39866f3eb14SChristos Margiolis 
399132fca63SHans Petter Selasky 	/* XXX: a user should be able to set this with a control tool, the
40032a0e5d5SHans Petter Selasky 	   sysadmin then needs min+max sysctls for this */
40132a0e5d5SHans Petter Selasky 	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
40232a0e5d5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
40332a0e5d5SHans Petter Selasky             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
40432a0e5d5SHans Petter Selasky 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
40532a0e5d5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
4063b4c5433SAlexander Motin 	    "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d,
4077029da5cSPawel Biernacki 	    sizeof(d), sysctl_dev_pcm_bitperfect, "I",
40832a0e5d5SHans Petter Selasky 	    "bit-perfect playback/recording (0=disable, 1=enable)");
409ed2196e5SHans Petter Selasky 	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
410ed2196e5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
411ed2196e5SHans Petter Selasky 	    OID_AUTO, "mode", CTLFLAG_RD, NULL, mode,
412e56c8996SChristos Margiolis 	    "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than "
413e56c8996SChristos Margiolis 	    "one mode is supported)");
41432a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_AUTOVCHAN)
41532a0e5d5SHans Petter Selasky 		vchan_initsys(dev);
41632a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_EQ)
41732a0e5d5SHans Petter Selasky 		feeder_eq_initsys(dev);
41832a0e5d5SHans Petter Selasky }
41932a0e5d5SHans Petter Selasky 
420*516a9c02SChristos Margiolis /*
421*516a9c02SChristos Margiolis  * Basic initialization so that drivers can use pcm_addchan() before
422*516a9c02SChristos Margiolis  * pcm_register().
423*516a9c02SChristos Margiolis  */
424*516a9c02SChristos Margiolis void
pcm_init(device_t dev,void * devinfo)425*516a9c02SChristos Margiolis pcm_init(device_t dev, void *devinfo)
426987e5972SCameron Grant {
427bba4862cSAriff Abdullah 	struct snddev_info *d;
42890da2b28SAriff Abdullah 	int i;
429987e5972SCameron Grant 
430bba4862cSAriff Abdullah 	d = device_get_softc(dev);
431e4e61333SAriff Abdullah 	d->dev = dev;
4322c69ba87SJohn Baldwin 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
433e4e61333SAriff Abdullah 	cv_init(&d->cv, device_get_nameunit(dev));
434e4e61333SAriff Abdullah 	PCM_ACQUIRE_QUICK(d);
435a9f08df3SChristos Margiolis 
43690da2b28SAriff Abdullah 	i = 0;
43790da2b28SAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
43890da2b28SAriff Abdullah 	    "vpc", &i) != 0 || i != 0)
43990da2b28SAriff Abdullah 		d->flags |= SD_F_VPC;
44090da2b28SAriff Abdullah 
44190da2b28SAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
44290da2b28SAriff Abdullah 	    "bitperfect", &i) == 0 && i != 0)
44390da2b28SAriff Abdullah 		d->flags |= SD_F_BITPERFECT;
44490da2b28SAriff Abdullah 
445987e5972SCameron Grant 	d->devinfo = devinfo;
446506a5308SCameron Grant 	d->reccount = 0;
447a67fe5c1SCameron Grant 	d->playcount = 0;
448bba4862cSAriff Abdullah 	d->pvchancount = 0;
449bba4862cSAriff Abdullah 	d->rvchancount = 0;
450bba4862cSAriff Abdullah 	d->pvchanrate = 0;
451bba4862cSAriff Abdullah 	d->pvchanformat = 0;
452bba4862cSAriff Abdullah 	d->rvchanrate = 0;
453bba4862cSAriff Abdullah 	d->rvchanformat = 0;
454ad4c8671SChristos Margiolis 	d->p_unr = new_unrhdr(0, INT_MAX, NULL);
455ad4c8671SChristos Margiolis 	d->vp_unr = new_unrhdr(0, INT_MAX, NULL);
456ad4c8671SChristos Margiolis 	d->r_unr = new_unrhdr(0, INT_MAX, NULL);
457ad4c8671SChristos Margiolis 	d->vr_unr = new_unrhdr(0, INT_MAX, NULL);
458833f7023SCameron Grant 
459bba4862cSAriff Abdullah 	CHN_INIT(d, channels.pcm);
460bba4862cSAriff Abdullah 	CHN_INIT(d, channels.pcm.busy);
46190da2b28SAriff Abdullah 	CHN_INIT(d, channels.pcm.opened);
462*516a9c02SChristos Margiolis }
46345550658SPoul-Henning Kamp 
464*516a9c02SChristos Margiolis int
pcm_register(device_t dev,char * str)465*516a9c02SChristos Margiolis pcm_register(device_t dev, char *str)
466*516a9c02SChristos Margiolis {
467*516a9c02SChristos Margiolis 	struct snddev_info *d = device_get_softc(dev);
468*516a9c02SChristos Margiolis 
469*516a9c02SChristos Margiolis 	/* should only be called once */
470*516a9c02SChristos Margiolis 	if (d->flags & SD_F_REGISTERED)
471*516a9c02SChristos Margiolis 		return (EINVAL);
472*516a9c02SChristos Margiolis 
473*516a9c02SChristos Margiolis 	PCM_BUSYASSERT(d);
474*516a9c02SChristos Margiolis 
475*516a9c02SChristos Margiolis 	if (d->playcount == 0 || d->reccount == 0)
476*516a9c02SChristos Margiolis 		d->flags |= SD_F_SIMPLEX;
477*516a9c02SChristos Margiolis 
478*516a9c02SChristos Margiolis 	if (d->playcount > 0 || d->reccount > 0)
479*516a9c02SChristos Margiolis 		d->flags |= SD_F_AUTOVCHAN;
480*516a9c02SChristos Margiolis 
481*516a9c02SChristos Margiolis 	vchan_setmaxauto(d, snd_maxautovchans);
482*516a9c02SChristos Margiolis 
483*516a9c02SChristos Margiolis 	strlcpy(d->status, str, SND_STATUSLEN);
484*516a9c02SChristos Margiolis 	sndstat_register(dev, d->status);
485*516a9c02SChristos Margiolis 
486*516a9c02SChristos Margiolis 	PCM_LOCK(d);
487*516a9c02SChristos Margiolis 
488*516a9c02SChristos Margiolis 	/* Done, we're ready.. */
489*516a9c02SChristos Margiolis 	d->flags |= SD_F_REGISTERED;
490*516a9c02SChristos Margiolis 
491*516a9c02SChristos Margiolis 	PCM_RELEASE(d);
492*516a9c02SChristos Margiolis 
493*516a9c02SChristos Margiolis 	PCM_UNLOCK(d);
494*516a9c02SChristos Margiolis 
495*516a9c02SChristos Margiolis 	/*
496*516a9c02SChristos Margiolis 	 * Create all sysctls once SD_F_REGISTERED is set else
497*516a9c02SChristos Margiolis 	 * tunable sysctls won't work:
498*516a9c02SChristos Margiolis 	 */
499*516a9c02SChristos Margiolis 	pcm_sysinit(dev);
500*516a9c02SChristos Margiolis 
501*516a9c02SChristos Margiolis 	if (snd_unit_auto < 0)
502*516a9c02SChristos Margiolis 		snd_unit_auto = (snd_unit < 0) ? 1 : 0;
503*516a9c02SChristos Margiolis 	if (snd_unit < 0 || snd_unit_auto > 1)
504*516a9c02SChristos Margiolis 		snd_unit = device_get_unit(dev);
505*516a9c02SChristos Margiolis 	else if (snd_unit_auto == 1)
506*516a9c02SChristos Margiolis 		snd_unit = pcm_best_unit(snd_unit);
507*516a9c02SChristos Margiolis 
508*516a9c02SChristos Margiolis 	return (dsp_make_dev(dev));
509987e5972SCameron Grant }
510987e5972SCameron Grant 
51133dbf14aSCameron Grant int
pcm_unregister(device_t dev)51233dbf14aSCameron Grant pcm_unregister(device_t dev)
5137c438dbeSCameron Grant {
514e4e61333SAriff Abdullah 	struct snddev_info *d;
515a67fe5c1SCameron Grant 	struct pcm_channel *ch;
5167c438dbeSCameron Grant 
517e4e61333SAriff Abdullah 	d = device_get_softc(dev);
518e4e61333SAriff Abdullah 
519e4e61333SAriff Abdullah 	if (!PCM_ALIVE(d)) {
520e4e61333SAriff Abdullah 		device_printf(dev, "unregister: device not configured\n");
521e4e61333SAriff Abdullah 		return (0);
522e4e61333SAriff Abdullah 	}
523bba4862cSAriff Abdullah 
52490da2b28SAriff Abdullah 	PCM_LOCK(d);
525e4e61333SAriff Abdullah 	PCM_WAIT(d);
526e4e61333SAriff Abdullah 
527cc1efc23SHans Petter Selasky 	d->flags |= SD_F_DETACHING;
528cc1efc23SHans Petter Selasky 
529e4e61333SAriff Abdullah 	PCM_ACQUIRE(d);
53090da2b28SAriff Abdullah 	PCM_UNLOCK(d);
531e4e61333SAriff Abdullah 
532e4e61333SAriff Abdullah 	CHN_FOREACH(ch, d, channels.pcm) {
533e4e61333SAriff Abdullah 		CHN_LOCK(ch);
53444e128feSChristos Margiolis 		/*
53503614fcbSChristos Margiolis 		 * Do not wait for the timeout in chn_read()/chn_write(). Wake
53603614fcbSChristos Margiolis 		 * up the sleeping thread and kill the channel.
53744e128feSChristos Margiolis 		 */
53803614fcbSChristos Margiolis 		chn_shutdown(ch);
53944e128feSChristos Margiolis 		chn_abort(ch);
540e4e61333SAriff Abdullah 		CHN_UNLOCK(ch);
541c9b53085SCameron Grant 	}
5425ee30e27SMathew Kanner 
5438461d581SHans Petter Selasky 	/* remove /dev/sndstat entry first */
5448461d581SHans Petter Selasky 	sndstat_unregister(dev);
5458461d581SHans Petter Selasky 
54690da2b28SAriff Abdullah 	PCM_LOCK(d);
547e4e61333SAriff Abdullah 	d->flags |= SD_F_DYING;
548e4e61333SAriff Abdullah 	d->flags &= ~SD_F_REGISTERED;
54990da2b28SAriff Abdullah 	PCM_UNLOCK(d);
550e4e61333SAriff Abdullah 
551bba4862cSAriff Abdullah 	if (d->play_sysctl_tree != NULL) {
552bba4862cSAriff Abdullah 		sysctl_ctx_free(&d->play_sysctl_ctx);
553bba4862cSAriff Abdullah 		d->play_sysctl_tree = NULL;
554bba4862cSAriff Abdullah 	}
555bba4862cSAriff Abdullah 	if (d->rec_sysctl_tree != NULL) {
556bba4862cSAriff Abdullah 		sysctl_ctx_free(&d->rec_sysctl_ctx);
557bba4862cSAriff Abdullah 		d->rec_sysctl_tree = NULL;
558a1d444e1SAriff Abdullah 	}
559bba4862cSAriff Abdullah 
560074d6fbeSChristos Margiolis 	dsp_destroy_dev(dev);
561074d6fbeSChristos Margiolis 	(void)mixer_uninit(dev);
562074d6fbeSChristos Margiolis 
56303614fcbSChristos Margiolis 	pcm_killchans(d);
5647c438dbeSCameron Grant 
56590da2b28SAriff Abdullah 	PCM_LOCK(d);
566e4e61333SAriff Abdullah 	PCM_RELEASE(d);
567e4e61333SAriff Abdullah 	cv_destroy(&d->cv);
56890da2b28SAriff Abdullah 	PCM_UNLOCK(d);
56949c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
570ad4c8671SChristos Margiolis 	if (d->p_unr != NULL)
571ad4c8671SChristos Margiolis 		delete_unrhdr(d->p_unr);
572ad4c8671SChristos Margiolis 	if (d->vp_unr != NULL)
573ad4c8671SChristos Margiolis 		delete_unrhdr(d->vp_unr);
574ad4c8671SChristos Margiolis 	if (d->r_unr != NULL)
575ad4c8671SChristos Margiolis 		delete_unrhdr(d->r_unr);
576ad4c8671SChristos Margiolis 	if (d->vr_unr != NULL)
577ad4c8671SChristos Margiolis 		delete_unrhdr(d->vr_unr);
578bba4862cSAriff Abdullah 
579bba4862cSAriff Abdullah 	if (snd_unit == device_get_unit(dev)) {
580cbebc90dSAlexander Motin 		snd_unit = pcm_best_unit(-1);
581cbebc90dSAlexander Motin 		if (snd_unit_auto == 0)
582cbebc90dSAlexander Motin 			snd_unit_auto = 1;
583e4e61333SAriff Abdullah 	}
584bba4862cSAriff Abdullah 
585e4e61333SAriff Abdullah 	return (0);
58633dbf14aSCameron Grant }
5877c438dbeSCameron Grant 
58867b1dce3SCameron Grant /************************************************************************/
58967b1dce3SCameron Grant 
590b611c801SAlexander Leidinger /**
591b611c801SAlexander Leidinger  * @brief	Handle OSSv4 SNDCTL_SYSINFO ioctl.
592b611c801SAlexander Leidinger  *
593b611c801SAlexander Leidinger  * @param si	Pointer to oss_sysinfo struct where information about the
594b611c801SAlexander Leidinger  * 		sound subsystem will be written/copied.
595b611c801SAlexander Leidinger  *
596b611c801SAlexander Leidinger  * This routine returns information about the sound system, such as the
597b611c801SAlexander Leidinger  * current OSS version, number of audio, MIDI, and mixer drivers, etc.
598b611c801SAlexander Leidinger  * Also includes a bitmask showing which of the above types of devices
599b611c801SAlexander Leidinger  * are open (busy).
600b611c801SAlexander Leidinger  *
601b611c801SAlexander Leidinger  * @note
602b611c801SAlexander Leidinger  * Calling threads must not hold any snddev_info or pcm_channel locks.
603b611c801SAlexander Leidinger  *
604b611c801SAlexander Leidinger  * @author	Ryan Beasley <ryanb@FreeBSD.org>
605b611c801SAlexander Leidinger  */
606b611c801SAlexander Leidinger void
sound_oss_sysinfo(oss_sysinfo * si)607b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si)
608b611c801SAlexander Leidinger {
609b611c801SAlexander Leidinger 	static char si_product[] = "FreeBSD native OSS ABI";
610b611c801SAlexander Leidinger 	static char si_version[] = __XSTRING(__FreeBSD_version);
611c412d503SAlexander Motin 	static char si_license[] = "BSD";
612b611c801SAlexander Leidinger 	static int intnbits = sizeof(int) * 8;	/* Better suited as macro?
613b611c801SAlexander Leidinger 						   Must pester a C guru. */
614b611c801SAlexander Leidinger 
615b611c801SAlexander Leidinger 	struct snddev_info *d;
616b611c801SAlexander Leidinger 	struct pcm_channel *c;
61759d98edaSChristos Margiolis 	int j;
618c597c557SChristos Margiolis 	size_t i;
619b611c801SAlexander Leidinger 
620b611c801SAlexander Leidinger 	strlcpy(si->product, si_product, sizeof(si->product));
621b611c801SAlexander Leidinger 	strlcpy(si->version, si_version, sizeof(si->version));
622b611c801SAlexander Leidinger 	si->versionnum = SOUND_VERSION;
623c412d503SAlexander Motin 	strlcpy(si->license, si_license, sizeof(si->license));
624b611c801SAlexander Leidinger 
625b611c801SAlexander Leidinger 	/*
626b611c801SAlexander Leidinger 	 * Iterate over PCM devices and their channels, gathering up data
627e07f9178SChristos Margiolis 	 * for the numaudioengines and openedaudio fields.
628b611c801SAlexander Leidinger 	 */
629e07f9178SChristos Margiolis 	si->numaudioengines = 0;
630b611c801SAlexander Leidinger 	bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
631b611c801SAlexander Leidinger 
632b611c801SAlexander Leidinger 	j = 0;
633b611c801SAlexander Leidinger 
6349c271f79SAriff Abdullah 	for (i = 0; pcm_devclass != NULL &&
6359c271f79SAriff Abdullah 	    i < devclass_get_maxunit(pcm_devclass); i++) {
636b611c801SAlexander Leidinger 		d = devclass_get_softc(pcm_devclass, i);
637e4e61333SAriff Abdullah 		if (!PCM_REGISTERED(d))
638b611c801SAlexander Leidinger 			continue;
639b611c801SAlexander Leidinger 
640e4e61333SAriff Abdullah 		/* XXX Need Giant magic entry ??? */
641e4e61333SAriff Abdullah 
642b611c801SAlexander Leidinger 		/* See note in function's docblock */
64390da2b28SAriff Abdullah 		PCM_UNLOCKASSERT(d);
64490da2b28SAriff Abdullah 		PCM_LOCK(d);
645b611c801SAlexander Leidinger 
646e07f9178SChristos Margiolis 		si->numaudioengines += PCM_CHANCOUNT(d);
647b611c801SAlexander Leidinger 
648bba4862cSAriff Abdullah 		CHN_FOREACH(c, d, channels.pcm) {
64990da2b28SAriff Abdullah 			CHN_UNLOCKASSERT(c);
650b611c801SAlexander Leidinger 			CHN_LOCK(c);
651b611c801SAlexander Leidinger 			if (c->flags & CHN_F_BUSY)
652b611c801SAlexander Leidinger 				si->openedaudio[j / intnbits] |=
653b611c801SAlexander Leidinger 				    (1 << (j % intnbits));
654b611c801SAlexander Leidinger 			CHN_UNLOCK(c);
655b611c801SAlexander Leidinger 			j++;
656b611c801SAlexander Leidinger 		}
657b611c801SAlexander Leidinger 
65890da2b28SAriff Abdullah 		PCM_UNLOCK(d);
659b611c801SAlexander Leidinger 	}
660b611c801SAlexander Leidinger 
661b611c801SAlexander Leidinger 	si->numsynths = 0;	/* OSSv4 docs:  this field is obsolete */
662b611c801SAlexander Leidinger 	/**
663b611c801SAlexander Leidinger 	 * @todo	Collect num{midis,timers}.
664b611c801SAlexander Leidinger 	 *
665b611c801SAlexander Leidinger 	 * Need access to sound/midi/midi.c::midistat_lock in order
666b611c801SAlexander Leidinger 	 * to safely touch midi_devices and get a head count of, well,
667b611c801SAlexander Leidinger 	 * MIDI devices.  midistat_lock is a global static (i.e., local to
668b611c801SAlexander Leidinger 	 * midi.c), but midi_devices is a regular global; should the mutex
669b611c801SAlexander Leidinger 	 * be publicized, or is there another way to get this information?
670b611c801SAlexander Leidinger 	 *
671b611c801SAlexander Leidinger 	 * NB:	MIDI/sequencer stuff is currently on hold.
672b611c801SAlexander Leidinger 	 */
673b611c801SAlexander Leidinger 	si->nummidis = 0;
674b611c801SAlexander Leidinger 	si->numtimers = 0;
6755d980fadSChristos Margiolis 	/*
6765d980fadSChristos Margiolis 	 * Set this to the maximum unit number so that applications will not
6775d980fadSChristos Margiolis 	 * break if they try to loop through all mixers and some of them are
6785d980fadSChristos Margiolis 	 * not available.
6795d980fadSChristos Margiolis 	 */
6805d980fadSChristos Margiolis 	si->nummixers = devclass_get_maxunit(pcm_devclass);
68159d98edaSChristos Margiolis 	si->numcards = devclass_get_maxunit(pcm_devclass);
682e07f9178SChristos Margiolis 	si->numaudios = devclass_get_maxunit(pcm_devclass);
683b611c801SAlexander Leidinger 		/* OSSv4 docs:	Intended only for test apps; API doesn't
684b611c801SAlexander Leidinger 		   really have much of a concept of cards.  Shouldn't be
685b611c801SAlexander Leidinger 		   used by applications. */
686b611c801SAlexander Leidinger 
687b611c801SAlexander Leidinger 	/**
688b611c801SAlexander Leidinger 	 * @todo	Fill in "busy devices" fields.
689b611c801SAlexander Leidinger 	 *
690b611c801SAlexander Leidinger 	 *  si->openedmidi = " MIDI devices
691b611c801SAlexander Leidinger 	 */
692b611c801SAlexander Leidinger 	bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
693b611c801SAlexander Leidinger 
694b611c801SAlexander Leidinger 	/*
695b611c801SAlexander Leidinger 	 * Si->filler is a reserved array, but according to docs each
696b611c801SAlexander Leidinger 	 * element should be set to -1.
697b611c801SAlexander Leidinger 	 */
698c597c557SChristos Margiolis 	for (i = 0; i < nitems(si->filler); i++)
699b611c801SAlexander Leidinger 		si->filler[i] = -1;
700b611c801SAlexander Leidinger }
701b611c801SAlexander Leidinger 
70252f6e09eSAlexander Motin int
sound_oss_card_info(oss_card_info * si)70352f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si)
70452f6e09eSAlexander Motin {
70552f6e09eSAlexander Motin 	struct snddev_info *d;
706305db91dSChristos Margiolis 	int i;
70752f6e09eSAlexander Motin 
70852f6e09eSAlexander Motin 	for (i = 0; pcm_devclass != NULL &&
70952f6e09eSAlexander Motin 	    i < devclass_get_maxunit(pcm_devclass); i++) {
71052f6e09eSAlexander Motin 		d = devclass_get_softc(pcm_devclass, i);
711305db91dSChristos Margiolis 		if (i != si->card)
71252f6e09eSAlexander Motin 			continue;
71352f6e09eSAlexander Motin 
7145d980fadSChristos Margiolis 		if (!PCM_REGISTERED(d)) {
7155d980fadSChristos Margiolis 			snprintf(si->shortname, sizeof(si->shortname),
7165d980fadSChristos Margiolis 			    "pcm%d (n/a)", i);
7175d980fadSChristos Margiolis 			strlcpy(si->longname, "Device unavailable",
7185d980fadSChristos Margiolis 			    sizeof(si->longname));
7195d980fadSChristos Margiolis 			si->hw_info[0] = '\0';
7205d980fadSChristos Margiolis 			si->intr_count = si->ack_count = 0;
7215d980fadSChristos Margiolis 		} else {
72290da2b28SAriff Abdullah 			PCM_UNLOCKASSERT(d);
72390da2b28SAriff Abdullah 			PCM_LOCK(d);
72452f6e09eSAlexander Motin 
72552f6e09eSAlexander Motin 			strlcpy(si->shortname, device_get_nameunit(d->dev),
72652f6e09eSAlexander Motin 			    sizeof(si->shortname));
72752f6e09eSAlexander Motin 			strlcpy(si->longname, device_get_desc(d->dev),
72852f6e09eSAlexander Motin 			    sizeof(si->longname));
72952f6e09eSAlexander Motin 			strlcpy(si->hw_info, d->status, sizeof(si->hw_info));
73052f6e09eSAlexander Motin 			si->intr_count = si->ack_count = 0;
73190da2b28SAriff Abdullah 
73290da2b28SAriff Abdullah 			PCM_UNLOCK(d);
7335d980fadSChristos Margiolis 		}
73490da2b28SAriff Abdullah 
73552f6e09eSAlexander Motin 		return (0);
73652f6e09eSAlexander Motin 	}
73752f6e09eSAlexander Motin 	return (ENXIO);
73852f6e09eSAlexander Motin }
73952f6e09eSAlexander Motin 
740b611c801SAlexander Leidinger /************************************************************************/
741b611c801SAlexander Leidinger 
7420a0301deSChristos Margiolis static void
sound_global_init(void)7430a0301deSChristos Margiolis sound_global_init(void)
7440a0301deSChristos Margiolis {
7450a0301deSChristos Margiolis 	if (snd_verbose < 0 || snd_verbose > 4)
7460a0301deSChristos Margiolis 		snd_verbose = 1;
7470a0301deSChristos Margiolis 
7480a0301deSChristos Margiolis 	if (snd_unit < 0)
7490a0301deSChristos Margiolis 		snd_unit = -1;
7500a0301deSChristos Margiolis 
7510a0301deSChristos Margiolis 	if (snd_maxautovchans < 0 ||
7520a0301deSChristos Margiolis 	    snd_maxautovchans > SND_MAXVCHANS)
7530a0301deSChristos Margiolis 		snd_maxautovchans = 0;
7540a0301deSChristos Margiolis 
7550a0301deSChristos Margiolis 	if (chn_latency < CHN_LATENCY_MIN ||
7560a0301deSChristos Margiolis 	    chn_latency > CHN_LATENCY_MAX)
7570a0301deSChristos Margiolis 		chn_latency = CHN_LATENCY_DEFAULT;
7580a0301deSChristos Margiolis 
7590a0301deSChristos Margiolis 	if (chn_latency_profile < CHN_LATENCY_PROFILE_MIN ||
7600a0301deSChristos Margiolis 	    chn_latency_profile > CHN_LATENCY_PROFILE_MAX)
7610a0301deSChristos Margiolis 		chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT;
7620a0301deSChristos Margiolis 
7630a0301deSChristos Margiolis 	if (feeder_rate_min < FEEDRATE_MIN ||
7640a0301deSChristos Margiolis 		    feeder_rate_max < FEEDRATE_MIN ||
7650a0301deSChristos Margiolis 		    feeder_rate_min > FEEDRATE_MAX ||
7660a0301deSChristos Margiolis 		    feeder_rate_max > FEEDRATE_MAX ||
7670a0301deSChristos Margiolis 		    !(feeder_rate_min < feeder_rate_max)) {
7680a0301deSChristos Margiolis 		feeder_rate_min = FEEDRATE_RATEMIN;
7690a0301deSChristos Margiolis 		feeder_rate_max = FEEDRATE_RATEMAX;
7700a0301deSChristos Margiolis 	}
7710a0301deSChristos Margiolis 
7720a0301deSChristos Margiolis 	if (feeder_rate_round < FEEDRATE_ROUNDHZ_MIN ||
7730a0301deSChristos Margiolis 		    feeder_rate_round > FEEDRATE_ROUNDHZ_MAX)
7740a0301deSChristos Margiolis 		feeder_rate_round = FEEDRATE_ROUNDHZ;
7750a0301deSChristos Margiolis 
7760a0301deSChristos Margiolis 	if (bootverbose)
7770a0301deSChristos Margiolis 		printf("%s: snd_unit=%d snd_maxautovchans=%d "
7780a0301deSChristos Margiolis 		    "latency=%d "
7790a0301deSChristos Margiolis 		    "feeder_rate_min=%d feeder_rate_max=%d "
7800a0301deSChristos Margiolis 		    "feeder_rate_round=%d\n",
7810a0301deSChristos Margiolis 		    __func__, snd_unit, snd_maxautovchans,
7820a0301deSChristos Margiolis 		    chn_latency,
7830a0301deSChristos Margiolis 		    feeder_rate_min, feeder_rate_max,
7840a0301deSChristos Margiolis 		    feeder_rate_round);
7850a0301deSChristos Margiolis }
7860a0301deSChristos Margiolis 
7870739ea1dSSeigo Tanimura static int
sound_modevent(module_t mod,int type,void * data)7880739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data)
7890739ea1dSSeigo Tanimura {
790b611c801SAlexander Leidinger 	int ret;
791b611c801SAlexander Leidinger 
7928109ec9dSJohn Baldwin 	ret = 0;
793b611c801SAlexander Leidinger 	switch (type) {
794b611c801SAlexander Leidinger 	case MOD_LOAD:
79513bebcd3SJohn Baldwin 		pcm_devclass = devclass_create("pcm");
796b611c801SAlexander Leidinger 		pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
7970a0301deSChristos Margiolis 		sound_global_init();
798b611c801SAlexander Leidinger 		break;
799b611c801SAlexander Leidinger 	case MOD_UNLOAD:
800b611c801SAlexander Leidinger 		if (pcmsg_unrhdr != NULL) {
801b611c801SAlexander Leidinger 			delete_unrhdr(pcmsg_unrhdr);
802b611c801SAlexander Leidinger 			pcmsg_unrhdr = NULL;
803b611c801SAlexander Leidinger 		}
804b611c801SAlexander Leidinger 		break;
8055525b53dSAlexander Motin 	case MOD_SHUTDOWN:
8065525b53dSAlexander Motin 		break;
807b611c801SAlexander Leidinger 	default:
80890da2b28SAriff Abdullah 		ret = ENOTSUP;
809b611c801SAlexander Leidinger 	}
810b611c801SAlexander Leidinger 
811b611c801SAlexander Leidinger 	return ret;
8120739ea1dSSeigo Tanimura }
8130739ea1dSSeigo Tanimura 
8140739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL);
8150739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER);
816