xref: /freebsd/sys/dev/sound/pcm/sound.c (revision ad4c8671bddaa30bf0413089c74dde6a618d9021)
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 
51b8a36395SCameron Grant int pcm_veto_load = 1;
52b8a36395SCameron Grant 
53f3685841SAriff Abdullah int snd_unit = -1;
54cbe7d6a3SCameron Grant 
55cbebc90dSAlexander Motin static int snd_unit_auto = -1;
56af3b2549SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN,
57838d3589SAriff Abdullah     &snd_unit_auto, 0, "assign default unit to a newly attached device");
58ad8612b9SAriff Abdullah 
597029da5cSPawel Biernacki SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
607029da5cSPawel Biernacki     "Sound driver");
6182db23e2SCameron Grant 
6232a0e5d5SHans Petter Selasky static void pcm_sysinit(device_t);
6332a0e5d5SHans Petter Selasky 
64b611c801SAlexander Leidinger /**
65b611c801SAlexander Leidinger  * @brief Unit number allocator for syncgroup IDs
66b611c801SAlexander Leidinger  */
67b611c801SAlexander Leidinger struct unrhdr *pcmsg_unrhdr = NULL;
68b611c801SAlexander Leidinger 
6937209180SCameron Grant void *
702c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type)
7137209180SCameron Grant {
7237209180SCameron Grant 	struct mtx *m;
7337209180SCameron Grant 
74a163d034SWarner Losh 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
7512e524a2SDon Lewis 	mtx_init(m, desc, type, MTX_DEF);
7612e524a2SDon Lewis 	return m;
7712e524a2SDon Lewis }
7812e524a2SDon Lewis 
7937209180SCameron Grant void
8037209180SCameron Grant snd_mtxfree(void *m)
8137209180SCameron Grant {
8237209180SCameron Grant 	struct mtx *mtx = m;
8337209180SCameron Grant 
8437209180SCameron Grant 	mtx_destroy(mtx);
8537209180SCameron Grant 	free(mtx, M_DEVBUF);
8637209180SCameron Grant }
8737209180SCameron Grant 
8837209180SCameron Grant void
8937209180SCameron Grant snd_mtxassert(void *m)
9037209180SCameron Grant {
91f00f162aSCameron Grant #ifdef INVARIANTS
9237209180SCameron Grant 	struct mtx *mtx = m;
9337209180SCameron Grant 
9437209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
9537209180SCameron Grant #endif
9637209180SCameron Grant }
9737209180SCameron Grant 
9837209180SCameron Grant int
9937209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
10037209180SCameron Grant {
101e4e61333SAriff Abdullah 	struct snddev_info *d;
10290da2b28SAriff Abdullah 
10337209180SCameron Grant 	flags &= INTR_MPSAFE;
10446700f12SPeter Wemm 	flags |= INTR_TYPE_AV;
105e4e61333SAriff Abdullah 	d = device_get_softc(dev);
106e4e61333SAriff Abdullah 	if (d != NULL && (flags & INTR_MPSAFE))
107e4e61333SAriff Abdullah 		d->flags |= SD_F_MPSAFE;
108e4e61333SAriff Abdullah 
109775fcb6eSBaptiste Daroussin 	return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep);
11037209180SCameron Grant }
11137209180SCameron Grant 
1123fdb3676SAriff Abdullah int
1133fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
11425723d66SChristos Margiolis     pid_t pid, char *comm)
115285648f9SCameron Grant {
116285648f9SCameron Grant 	struct pcm_channel *c;
117b973a14dSChristos Margiolis 	int err, vchancount;
11825723d66SChristos Margiolis 	bool retry;
119bba4862cSAriff Abdullah 
12025723d66SChristos Margiolis 	KASSERT(d != NULL && ch != NULL &&
121bba4862cSAriff Abdullah 	    (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
12225723d66SChristos Margiolis 	    ("%s(): invalid d=%p ch=%p direction=%d pid=%d",
12325723d66SChristos Margiolis 	    __func__, d, ch, direction, pid));
124e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
125bba4862cSAriff Abdullah 
12690da2b28SAriff Abdullah 	*ch = NULL;
12790da2b28SAriff Abdullah 	vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount :
12890da2b28SAriff Abdullah 	    d->rvchancount;
12925723d66SChristos Margiolis 	retry = false;
130b973a14dSChristos Margiolis 
1313fdb3676SAriff Abdullah retry_chnalloc:
132b973a14dSChristos Margiolis 	/* Scan for a free channel. */
133bba4862cSAriff Abdullah 	CHN_FOREACH(c, d, channels.pcm) {
13449c5e6e2SCameron Grant 		CHN_LOCK(c);
135b973a14dSChristos Margiolis 		if (c->direction != direction) {
13690da2b28SAriff Abdullah 			CHN_UNLOCK(c);
137b973a14dSChristos Margiolis 			continue;
13890da2b28SAriff Abdullah 		}
139b973a14dSChristos Margiolis 		if (!(c->flags & CHN_F_BUSY)) {
140285648f9SCameron Grant 			c->flags |= CHN_F_BUSY;
141b8f0d9e0SCameron Grant 			c->pid = pid;
14290da2b28SAriff Abdullah 			strlcpy(c->comm, (comm != NULL) ? comm :
14390da2b28SAriff Abdullah 			    CHN_COMM_UNKNOWN, sizeof(c->comm));
1443fdb3676SAriff Abdullah 			*ch = c;
145b973a14dSChristos Margiolis 
146bba4862cSAriff Abdullah 			return (0);
147b973a14dSChristos Margiolis 		}
14849c5e6e2SCameron Grant 		CHN_UNLOCK(c);
149285648f9SCameron Grant 	}
150b973a14dSChristos Margiolis 	/* Maybe next time... */
15125723d66SChristos Margiolis 	if (retry)
152b973a14dSChristos Margiolis 		return (EBUSY);
153e4e61333SAriff Abdullah 
154b973a14dSChristos Margiolis 	/* No channel available. We also cannot create more VCHANs. */
15525723d66SChristos Margiolis 	if (!(vchancount > 0 && vchancount < snd_maxautovchans))
156b973a14dSChristos Margiolis 		return (ENOTSUP);
157b973a14dSChristos Margiolis 
158b973a14dSChristos Margiolis 	/* Increase the VCHAN count and try to get the new channel. */
1593af2beb8SChristos Margiolis 	err = vchan_setnew(d, direction, vchancount + 1);
160a1d444e1SAriff Abdullah 	if (err == 0) {
16125723d66SChristos Margiolis 		retry = true;
1623fdb3676SAriff Abdullah 		goto retry_chnalloc;
163f637a36cSCameron Grant 	}
164f637a36cSCameron Grant 
165bba4862cSAriff Abdullah 	return (err);
166285648f9SCameron Grant }
167285648f9SCameron Grant 
16833dbf14aSCameron Grant static int
169851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
17033dbf14aSCameron Grant {
171b8f0d9e0SCameron Grant 	struct snddev_info *d;
17233dbf14aSCameron Grant 	int error, unit;
17333dbf14aSCameron Grant 
17433dbf14aSCameron Grant 	unit = snd_unit;
175041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &unit, 0, req);
17633dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
177b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
178e4e61333SAriff Abdullah 		if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
179b8f0d9e0SCameron Grant 			return EINVAL;
18033dbf14aSCameron Grant 		snd_unit = unit;
181cbebc90dSAlexander Motin 		snd_unit_auto = 0;
18233dbf14aSCameron Grant 	}
18333dbf14aSCameron Grant 	return (error);
18433dbf14aSCameron Grant }
185851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */
186b7d6c6b5SEitan Adler SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit,
1877029da5cSPawel Biernacki     CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0,
1887029da5cSPawel Biernacki     sizeof(int), sysctl_hw_snd_default_unit, "I",
189b7d6c6b5SEitan Adler     "default sound device");
190987e5972SCameron Grant 
191139bcec8SChristos Margiolis void
1925ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
193285648f9SCameron Grant {
194e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
19590da2b28SAriff Abdullah 	PCM_LOCKASSERT(d);
196bba4862cSAriff Abdullah 	KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
197bba4862cSAriff Abdullah 	    ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
198285648f9SCameron Grant 
19990da2b28SAriff Abdullah 	CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm);
2003fdb3676SAriff Abdullah 
20125723d66SChristos Margiolis 	switch (ch->type) {
202e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_PLAY:
203e4e61333SAriff Abdullah 		d->playcount++;
204e4e61333SAriff Abdullah 		break;
205e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_VPLAY:
206e4e61333SAriff Abdullah 		d->pvchancount++;
207e4e61333SAriff Abdullah 		break;
208e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_REC:
209e4e61333SAriff Abdullah 		d->reccount++;
210e4e61333SAriff Abdullah 		break;
211e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_VREC:
212e4e61333SAriff Abdullah 		d->rvchancount++;
213e4e61333SAriff Abdullah 		break;
214e4e61333SAriff Abdullah 	default:
21576f95baeSChristos Margiolis 		__assert_unreachable();
216e4e61333SAriff Abdullah 	}
21733dbf14aSCameron Grant }
21833dbf14aSCameron Grant 
219285648f9SCameron Grant int
2205ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
22133dbf14aSCameron Grant {
222bba4862cSAriff Abdullah 	struct pcm_channel *tmp;
22333dbf14aSCameron Grant 
224e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
22590da2b28SAriff Abdullah 	PCM_LOCKASSERT(d);
226e4e61333SAriff Abdullah 
227bba4862cSAriff Abdullah 	tmp = NULL;
228a527dbc7SCameron Grant 
229bba4862cSAriff Abdullah 	CHN_FOREACH(tmp, d, channels.pcm) {
230bba4862cSAriff Abdullah 		if (tmp == ch)
231bba4862cSAriff Abdullah 			break;
23233dbf14aSCameron Grant 	}
233bba4862cSAriff Abdullah 
234bba4862cSAriff Abdullah 	if (tmp != ch)
235e4e61333SAriff Abdullah 		return (EINVAL);
23667beb5a5SCameron Grant 
237bba4862cSAriff Abdullah 	CHN_REMOVE(d, ch, channels.pcm);
238e4e61333SAriff Abdullah 
23925723d66SChristos Margiolis 	switch (ch->type) {
240bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_PLAY:
24167beb5a5SCameron Grant 		d->playcount--;
242bba4862cSAriff Abdullah 		break;
243bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_VPLAY:
244bba4862cSAriff Abdullah 		d->pvchancount--;
245bba4862cSAriff Abdullah 		break;
246bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_REC:
247bba4862cSAriff Abdullah 		d->reccount--;
248bba4862cSAriff Abdullah 		break;
249bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_VREC:
250bba4862cSAriff Abdullah 		d->rvchancount--;
251bba4862cSAriff Abdullah 		break;
252bba4862cSAriff Abdullah 	default:
25376f95baeSChristos Margiolis 		__assert_unreachable();
254bba4862cSAriff Abdullah 	}
255285648f9SCameron Grant 
256e4e61333SAriff Abdullah 	return (0);
257987e5972SCameron Grant }
258987e5972SCameron Grant 
259987e5972SCameron Grant int
260285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
261285648f9SCameron Grant {
262285648f9SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
26367b1dce3SCameron Grant 	struct pcm_channel *ch;
264285648f9SCameron Grant 
265e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
266e4e61333SAriff Abdullah 
26790da2b28SAriff Abdullah 	PCM_LOCK(d);
2683af2beb8SChristos Margiolis 	ch = chn_init(d, NULL, cls, dir, devinfo);
269285648f9SCameron Grant 	if (!ch) {
2702e9962efSChristos Margiolis 		device_printf(d->dev, "chn_init(%s, %d, %p) failed\n",
271e4e61333SAriff Abdullah 		    cls->name, dir, devinfo);
27290da2b28SAriff Abdullah 		PCM_UNLOCK(d);
273e4e61333SAriff Abdullah 		return (ENODEV);
274285648f9SCameron Grant 	}
275cd9766c5SCameron Grant 
276139bcec8SChristos Margiolis 	pcm_chn_add(d, ch);
27790da2b28SAriff Abdullah 	PCM_UNLOCK(d);
278cd9766c5SCameron Grant 
279139bcec8SChristos Margiolis 	return (0);
280285648f9SCameron Grant }
281285648f9SCameron Grant 
28203614fcbSChristos Margiolis static void
28303614fcbSChristos Margiolis pcm_killchans(struct snddev_info *d)
284285648f9SCameron Grant {
285e33bee07SOlivier Houchard 	struct pcm_channel *ch;
286e4e61333SAriff Abdullah 	int error;
28703614fcbSChristos Margiolis 	bool found;
288e4e61333SAriff Abdullah 
289e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
29003614fcbSChristos Margiolis 	do {
29103614fcbSChristos Margiolis 		found = false;
29203614fcbSChristos Margiolis 		CHN_FOREACH(ch, d, channels.pcm) {
29303614fcbSChristos Margiolis 			CHN_LOCK(ch);
29403614fcbSChristos Margiolis 			/*
29503614fcbSChristos Margiolis 			 * Make sure no channel has went to sleep in the
29603614fcbSChristos Margiolis 			 * meantime.
29703614fcbSChristos Margiolis 			 */
29803614fcbSChristos Margiolis 			chn_shutdown(ch);
29903614fcbSChristos Margiolis 			/*
30003614fcbSChristos Margiolis 			 * We have to give a thread sleeping in chn_sleep() a
30103614fcbSChristos Margiolis 			 * chance to observe that the channel is dead.
30203614fcbSChristos Margiolis 			 */
30303614fcbSChristos Margiolis 			if ((ch->flags & CHN_F_SLEEPING) == 0) {
30403614fcbSChristos Margiolis 				found = true;
30503614fcbSChristos Margiolis 				CHN_UNLOCK(ch);
30603614fcbSChristos Margiolis 				break;
30703614fcbSChristos Margiolis 			}
30803614fcbSChristos Margiolis 			CHN_UNLOCK(ch);
30903614fcbSChristos Margiolis 		}
310285648f9SCameron Grant 
31103614fcbSChristos Margiolis 		/*
31203614fcbSChristos Margiolis 		 * All channels are still sleeping. Sleep for a bit and try
31303614fcbSChristos Margiolis 		 * again to see if any of them is awake now.
31403614fcbSChristos Margiolis 		 */
31503614fcbSChristos Margiolis 		if (!found) {
31603614fcbSChristos Margiolis 			pause_sbt("pcmkillchans", SBT_1MS * 5, 0, 0);
31703614fcbSChristos Margiolis 			continue;
31803614fcbSChristos Margiolis 		}
319285648f9SCameron Grant 
32090da2b28SAriff Abdullah 		PCM_LOCK(d);
321bba4862cSAriff Abdullah 		error = pcm_chn_remove(d, ch);
32290da2b28SAriff Abdullah 		PCM_UNLOCK(d);
32303614fcbSChristos Margiolis 		if (error == 0)
324b3ea087cSChristos Margiolis 			chn_kill(ch);
32503614fcbSChristos Margiolis 	} while (!CHN_EMPTY(d, channels.pcm));
326285648f9SCameron Grant }
327285648f9SCameron Grant 
328cbebc90dSAlexander Motin static int
329cbebc90dSAlexander Motin pcm_best_unit(int old)
330cbebc90dSAlexander Motin {
331cbebc90dSAlexander Motin 	struct snddev_info *d;
332cbebc90dSAlexander Motin 	int i, best, bestprio, prio;
333cbebc90dSAlexander Motin 
334cbebc90dSAlexander Motin 	best = -1;
335cbebc90dSAlexander Motin 	bestprio = -100;
336cbebc90dSAlexander Motin 	for (i = 0; pcm_devclass != NULL &&
337cbebc90dSAlexander Motin 	    i < devclass_get_maxunit(pcm_devclass); i++) {
338cbebc90dSAlexander Motin 		d = devclass_get_softc(pcm_devclass, i);
339cbebc90dSAlexander Motin 		if (!PCM_REGISTERED(d))
340cbebc90dSAlexander Motin 			continue;
341cbebc90dSAlexander Motin 		prio = 0;
342cbebc90dSAlexander Motin 		if (d->playcount == 0)
343cbebc90dSAlexander Motin 			prio -= 10;
344cbebc90dSAlexander Motin 		if (d->reccount == 0)
345cbebc90dSAlexander Motin 			prio -= 2;
346cbebc90dSAlexander Motin 		if (prio > bestprio || (prio == bestprio && i == old)) {
347cbebc90dSAlexander Motin 			best = i;
348cbebc90dSAlexander Motin 			bestprio = prio;
349cbebc90dSAlexander Motin 		}
350cbebc90dSAlexander Motin 	}
351cbebc90dSAlexander Motin 	return (best);
352cbebc90dSAlexander Motin }
353cbebc90dSAlexander Motin 
354285648f9SCameron Grant int
355987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
356987e5972SCameron Grant {
35766ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
35849c5e6e2SCameron Grant 
35932a0e5d5SHans Petter Selasky 	/* should only be called once */
36032a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_REGISTERED)
36132a0e5d5SHans Petter Selasky 		return (EINVAL);
36232a0e5d5SHans Petter Selasky 
363e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
364bba4862cSAriff Abdullah 
365e4e61333SAriff Abdullah 	if (d->playcount == 0 || d->reccount == 0)
366e4e61333SAriff Abdullah 		d->flags |= SD_F_SIMPLEX;
367e4e61333SAriff Abdullah 
36832a0e5d5SHans Petter Selasky 	if (d->playcount > 0 || d->reccount > 0)
369e4e61333SAriff Abdullah 		d->flags |= SD_F_AUTOVCHAN;
370e4e61333SAriff Abdullah 
3717ad5f383SChristos Margiolis 	vchan_setmaxauto(d, snd_maxautovchans);
372bba4862cSAriff Abdullah 
373a580b31aSAriff Abdullah 	strlcpy(d->status, str, SND_STATUSLEN);
374bba4862cSAriff Abdullah 
37590da2b28SAriff Abdullah 	PCM_LOCK(d);
376e4e61333SAriff Abdullah 
377e4e61333SAriff Abdullah 	/* Done, we're ready.. */
378e4e61333SAriff Abdullah 	d->flags |= SD_F_REGISTERED;
379e4e61333SAriff Abdullah 
380e4e61333SAriff Abdullah 	PCM_RELEASE(d);
381bba4862cSAriff Abdullah 
38290da2b28SAriff Abdullah 	PCM_UNLOCK(d);
383bba4862cSAriff Abdullah 
38432a0e5d5SHans Petter Selasky 	/*
38532a0e5d5SHans Petter Selasky 	 * Create all sysctls once SD_F_REGISTERED is set else
38632a0e5d5SHans Petter Selasky 	 * tunable sysctls won't work:
38732a0e5d5SHans Petter Selasky 	 */
38832a0e5d5SHans Petter Selasky 	pcm_sysinit(dev);
38932a0e5d5SHans Petter Selasky 
390cbebc90dSAlexander Motin 	if (snd_unit_auto < 0)
391cbebc90dSAlexander Motin 		snd_unit_auto = (snd_unit < 0) ? 1 : 0;
392cbebc90dSAlexander Motin 	if (snd_unit < 0 || snd_unit_auto > 1)
393f3685841SAriff Abdullah 		snd_unit = device_get_unit(dev);
394cbebc90dSAlexander Motin 	else if (snd_unit_auto == 1)
395cbebc90dSAlexander Motin 		snd_unit = pcm_best_unit(snd_unit);
396f3685841SAriff Abdullah 
397e4e61333SAriff Abdullah 	return (0);
398987e5972SCameron Grant }
399987e5972SCameron Grant 
400a1d444e1SAriff Abdullah uint32_t
401987e5972SCameron Grant pcm_getflags(device_t dev)
402987e5972SCameron Grant {
40366ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
40449c5e6e2SCameron Grant 
405987e5972SCameron Grant 	return d->flags;
406987e5972SCameron Grant }
407987e5972SCameron Grant 
408987e5972SCameron Grant void
409a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val)
410987e5972SCameron Grant {
41166ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
412d95502a8SCameron Grant 
413987e5972SCameron Grant 	d->flags = val;
414987e5972SCameron Grant }
415987e5972SCameron Grant 
41639004e69SCameron Grant void *
41739004e69SCameron Grant pcm_getdevinfo(device_t dev)
41839004e69SCameron Grant {
41966ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
42049c5e6e2SCameron Grant 
42139004e69SCameron Grant 	return d->devinfo;
42239004e69SCameron Grant }
42339004e69SCameron Grant 
424a67fe5c1SCameron Grant unsigned int
425d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
426a67fe5c1SCameron Grant {
427a67fe5c1SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
4284e60be34SCameron Grant 	int sz, x;
429a67fe5c1SCameron Grant 
430a67fe5c1SCameron Grant 	sz = 0;
4314e60be34SCameron Grant 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
4324e60be34SCameron Grant 		x = sz;
433d55d96f6SAlexander Leidinger 		RANGE(sz, minbufsz, maxbufsz);
4344e60be34SCameron Grant 		if (x != sz)
435d55d96f6SAlexander Leidinger 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
436d55d96f6SAlexander Leidinger 		x = minbufsz;
4374e60be34SCameron Grant 		while (x < sz)
4384e60be34SCameron Grant 			x <<= 1;
4394e60be34SCameron Grant 		if (x > sz)
4404e60be34SCameron Grant 			x >>= 1;
4414e60be34SCameron Grant 		if (x != sz) {
4424e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
4434e60be34SCameron Grant 			sz = x;
4444e60be34SCameron Grant 		}
4454e60be34SCameron Grant 	} else {
446a67fe5c1SCameron Grant 		sz = deflt;
4474e60be34SCameron Grant 	}
4484e60be34SCameron Grant 
449a67fe5c1SCameron Grant 	d->bufsz = sz;
450a67fe5c1SCameron Grant 
451a67fe5c1SCameron Grant 	return sz;
452a67fe5c1SCameron Grant }
453a67fe5c1SCameron Grant 
45490da2b28SAriff Abdullah static int
45590da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)
45690da2b28SAriff Abdullah {
45790da2b28SAriff Abdullah 	struct snddev_info *d;
45890da2b28SAriff Abdullah 	int err, val;
45990da2b28SAriff Abdullah 
46090da2b28SAriff Abdullah 	d = oidp->oid_arg1;
46190da2b28SAriff Abdullah 	if (!PCM_REGISTERED(d))
46290da2b28SAriff Abdullah 		return (ENODEV);
46390da2b28SAriff Abdullah 
46490da2b28SAriff Abdullah 	PCM_LOCK(d);
46590da2b28SAriff Abdullah 	PCM_WAIT(d);
46690da2b28SAriff Abdullah 	val = (d->flags & SD_F_BITPERFECT) ? 1 : 0;
46790da2b28SAriff Abdullah 	PCM_ACQUIRE(d);
46890da2b28SAriff Abdullah 	PCM_UNLOCK(d);
46990da2b28SAriff Abdullah 
47090da2b28SAriff Abdullah 	err = sysctl_handle_int(oidp, &val, 0, req);
47190da2b28SAriff Abdullah 
47290da2b28SAriff Abdullah 	if (err == 0 && req->newptr != NULL) {
47390da2b28SAriff Abdullah 		if (!(val == 0 || val == 1)) {
47490da2b28SAriff Abdullah 			PCM_RELEASE_QUICK(d);
47590da2b28SAriff Abdullah 			return (EINVAL);
47690da2b28SAriff Abdullah 		}
47790da2b28SAriff Abdullah 
47890da2b28SAriff Abdullah 		PCM_LOCK(d);
47990da2b28SAriff Abdullah 
48090da2b28SAriff Abdullah 		d->flags &= ~SD_F_BITPERFECT;
48190da2b28SAriff Abdullah 		d->flags |= (val != 0) ? SD_F_BITPERFECT : 0;
48290da2b28SAriff Abdullah 
48390da2b28SAriff Abdullah 		PCM_RELEASE(d);
48490da2b28SAriff Abdullah 		PCM_UNLOCK(d);
48590da2b28SAriff Abdullah 	} else
48690da2b28SAriff Abdullah 		PCM_RELEASE_QUICK(d);
48790da2b28SAriff Abdullah 
48890da2b28SAriff Abdullah 	return (err);
48990da2b28SAriff Abdullah }
49090da2b28SAriff Abdullah 
491ed2196e5SHans Petter Selasky static u_int8_t
492ed2196e5SHans Petter Selasky pcm_mode_init(struct snddev_info *d)
493ed2196e5SHans Petter Selasky {
494ed2196e5SHans Petter Selasky 	u_int8_t mode = 0;
495ed2196e5SHans Petter Selasky 
496ed2196e5SHans Petter Selasky 	if (d->playcount > 0)
497ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_PLAY;
498ed2196e5SHans Petter Selasky 	if (d->reccount > 0)
499ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_REC;
500ed2196e5SHans Petter Selasky 	if (d->mixer_dev != NULL)
501ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_MIXER;
502ed2196e5SHans Petter Selasky 
503ed2196e5SHans Petter Selasky 	return (mode);
504ed2196e5SHans Petter Selasky }
505ed2196e5SHans Petter Selasky 
50632a0e5d5SHans Petter Selasky static void
50732a0e5d5SHans Petter Selasky pcm_sysinit(device_t dev)
50832a0e5d5SHans Petter Selasky {
50932a0e5d5SHans Petter Selasky   	struct snddev_info *d = device_get_softc(dev);
510ed2196e5SHans Petter Selasky 	u_int8_t mode;
511ed2196e5SHans Petter Selasky 
512ed2196e5SHans Petter Selasky 	mode = pcm_mode_init(d);
51332a0e5d5SHans Petter Selasky 
514132fca63SHans Petter Selasky 	/* XXX: a user should be able to set this with a control tool, the
51532a0e5d5SHans Petter Selasky 	   sysadmin then needs min+max sysctls for this */
51632a0e5d5SHans Petter Selasky 	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
51732a0e5d5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
51832a0e5d5SHans Petter Selasky             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
51932a0e5d5SHans Petter Selasky 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
52032a0e5d5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
5213b4c5433SAlexander Motin 	    "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d,
5227029da5cSPawel Biernacki 	    sizeof(d), sysctl_dev_pcm_bitperfect, "I",
52332a0e5d5SHans Petter Selasky 	    "bit-perfect playback/recording (0=disable, 1=enable)");
524ed2196e5SHans Petter Selasky 	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
525ed2196e5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
526ed2196e5SHans Petter Selasky 	    OID_AUTO, "mode", CTLFLAG_RD, NULL, mode,
527e56c8996SChristos Margiolis 	    "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than "
528e56c8996SChristos Margiolis 	    "one mode is supported)");
52932a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_AUTOVCHAN)
53032a0e5d5SHans Petter Selasky 		vchan_initsys(dev);
53132a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_EQ)
53232a0e5d5SHans Petter Selasky 		feeder_eq_initsys(dev);
53332a0e5d5SHans Petter Selasky }
53432a0e5d5SHans Petter Selasky 
535987e5972SCameron Grant int
536987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
537987e5972SCameron Grant {
538bba4862cSAriff Abdullah 	struct snddev_info *d;
53990da2b28SAriff Abdullah 	int i;
540987e5972SCameron Grant 
541b8a36395SCameron Grant 	if (pcm_veto_load) {
542b8a36395SCameron Grant 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
543b8a36395SCameron Grant 
544b8a36395SCameron Grant 		return EINVAL;
545b8a36395SCameron Grant 	}
546b8a36395SCameron Grant 
547bba4862cSAriff Abdullah 	d = device_get_softc(dev);
548e4e61333SAriff Abdullah 	d->dev = dev;
5492c69ba87SJohn Baldwin 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
550e4e61333SAriff Abdullah 	cv_init(&d->cv, device_get_nameunit(dev));
551e4e61333SAriff Abdullah 	PCM_ACQUIRE_QUICK(d);
552a9f08df3SChristos Margiolis 
55390da2b28SAriff Abdullah 	i = 0;
55490da2b28SAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
55590da2b28SAriff Abdullah 	    "vpc", &i) != 0 || i != 0)
55690da2b28SAriff Abdullah 		d->flags |= SD_F_VPC;
55790da2b28SAriff Abdullah 
55890da2b28SAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
55990da2b28SAriff Abdullah 	    "bitperfect", &i) == 0 && i != 0)
56090da2b28SAriff Abdullah 		d->flags |= SD_F_BITPERFECT;
56190da2b28SAriff Abdullah 
562987e5972SCameron Grant 	d->devinfo = devinfo;
563506a5308SCameron Grant 	d->reccount = 0;
564a67fe5c1SCameron Grant 	d->playcount = 0;
565bba4862cSAriff Abdullah 	d->pvchancount = 0;
566bba4862cSAriff Abdullah 	d->rvchancount = 0;
567bba4862cSAriff Abdullah 	d->pvchanrate = 0;
568bba4862cSAriff Abdullah 	d->pvchanformat = 0;
569bba4862cSAriff Abdullah 	d->rvchanrate = 0;
570bba4862cSAriff Abdullah 	d->rvchanformat = 0;
571*ad4c8671SChristos Margiolis 	d->p_unr = new_unrhdr(0, INT_MAX, NULL);
572*ad4c8671SChristos Margiolis 	d->vp_unr = new_unrhdr(0, INT_MAX, NULL);
573*ad4c8671SChristos Margiolis 	d->r_unr = new_unrhdr(0, INT_MAX, NULL);
574*ad4c8671SChristos Margiolis 	d->vr_unr = new_unrhdr(0, INT_MAX, NULL);
575833f7023SCameron Grant 
576bba4862cSAriff Abdullah 	CHN_INIT(d, channels.pcm);
577bba4862cSAriff Abdullah 	CHN_INIT(d, channels.pcm.busy);
57890da2b28SAriff Abdullah 	CHN_INIT(d, channels.pcm.opened);
57945550658SPoul-Henning Kamp 
580e4e61333SAriff Abdullah 	/* XXX This is incorrect, but lets play along for now. */
581a1d444e1SAriff Abdullah 	if ((numplay == 0 || numrec == 0) && numplay != numrec)
582285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
583d95502a8SCameron Grant 
584bba4862cSAriff Abdullah 	sysctl_ctx_init(&d->play_sysctl_ctx);
585bba4862cSAriff Abdullah 	d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
586bba4862cSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
5877029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node");
588bba4862cSAriff Abdullah 	sysctl_ctx_init(&d->rec_sysctl_ctx);
589bba4862cSAriff Abdullah 	d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
590bba4862cSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
591132fca63SHans Petter Selasky 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node");
592e4e61333SAriff Abdullah 
59332a0e5d5SHans Petter Selasky 	if (numplay > 0 || numrec > 0)
594cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
59590da2b28SAriff Abdullah 
5969da3b645SChristos Margiolis 	sndstat_register(dev, d->status);
597e4e61333SAriff Abdullah 
598e8c0d15aSChristos Margiolis 	return (dsp_make_dev(dev));
599987e5972SCameron Grant }
600987e5972SCameron Grant 
60133dbf14aSCameron Grant int
60233dbf14aSCameron Grant pcm_unregister(device_t dev)
6037c438dbeSCameron Grant {
604e4e61333SAriff Abdullah 	struct snddev_info *d;
605a67fe5c1SCameron Grant 	struct pcm_channel *ch;
6067c438dbeSCameron Grant 
607e4e61333SAriff Abdullah 	d = device_get_softc(dev);
608e4e61333SAriff Abdullah 
609e4e61333SAriff Abdullah 	if (!PCM_ALIVE(d)) {
610e4e61333SAriff Abdullah 		device_printf(dev, "unregister: device not configured\n");
611e4e61333SAriff Abdullah 		return (0);
612e4e61333SAriff Abdullah 	}
613bba4862cSAriff Abdullah 
61490da2b28SAriff Abdullah 	PCM_LOCK(d);
615e4e61333SAriff Abdullah 	PCM_WAIT(d);
616e4e61333SAriff Abdullah 
617cc1efc23SHans Petter Selasky 	d->flags |= SD_F_DETACHING;
618cc1efc23SHans Petter Selasky 
619e4e61333SAriff Abdullah 	PCM_ACQUIRE(d);
62090da2b28SAriff Abdullah 	PCM_UNLOCK(d);
621e4e61333SAriff Abdullah 
622e4e61333SAriff Abdullah 	CHN_FOREACH(ch, d, channels.pcm) {
623e4e61333SAriff Abdullah 		CHN_LOCK(ch);
62444e128feSChristos Margiolis 		/*
62503614fcbSChristos Margiolis 		 * Do not wait for the timeout in chn_read()/chn_write(). Wake
62603614fcbSChristos Margiolis 		 * up the sleeping thread and kill the channel.
62744e128feSChristos Margiolis 		 */
62803614fcbSChristos Margiolis 		chn_shutdown(ch);
62944e128feSChristos Margiolis 		chn_abort(ch);
630e4e61333SAriff Abdullah 		CHN_UNLOCK(ch);
631c9b53085SCameron Grant 	}
6325ee30e27SMathew Kanner 
6338461d581SHans Petter Selasky 	/* remove /dev/sndstat entry first */
6348461d581SHans Petter Selasky 	sndstat_unregister(dev);
6358461d581SHans Petter Selasky 
63690da2b28SAriff Abdullah 	PCM_LOCK(d);
637e4e61333SAriff Abdullah 	d->flags |= SD_F_DYING;
638e4e61333SAriff Abdullah 	d->flags &= ~SD_F_REGISTERED;
63990da2b28SAriff Abdullah 	PCM_UNLOCK(d);
640e4e61333SAriff Abdullah 
641bba4862cSAriff Abdullah 	if (d->play_sysctl_tree != NULL) {
642bba4862cSAriff Abdullah 		sysctl_ctx_free(&d->play_sysctl_ctx);
643bba4862cSAriff Abdullah 		d->play_sysctl_tree = NULL;
644bba4862cSAriff Abdullah 	}
645bba4862cSAriff Abdullah 	if (d->rec_sysctl_tree != NULL) {
646bba4862cSAriff Abdullah 		sysctl_ctx_free(&d->rec_sysctl_ctx);
647bba4862cSAriff Abdullah 		d->rec_sysctl_tree = NULL;
648a1d444e1SAriff Abdullah 	}
649bba4862cSAriff Abdullah 
650074d6fbeSChristos Margiolis 	dsp_destroy_dev(dev);
651074d6fbeSChristos Margiolis 	(void)mixer_uninit(dev);
652074d6fbeSChristos Margiolis 
65303614fcbSChristos Margiolis 	pcm_killchans(d);
6547c438dbeSCameron Grant 
65590da2b28SAriff Abdullah 	PCM_LOCK(d);
656e4e61333SAriff Abdullah 	PCM_RELEASE(d);
657e4e61333SAriff Abdullah 	cv_destroy(&d->cv);
65890da2b28SAriff Abdullah 	PCM_UNLOCK(d);
65949c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
660*ad4c8671SChristos Margiolis 	if (d->p_unr != NULL)
661*ad4c8671SChristos Margiolis 		delete_unrhdr(d->p_unr);
662*ad4c8671SChristos Margiolis 	if (d->vp_unr != NULL)
663*ad4c8671SChristos Margiolis 		delete_unrhdr(d->vp_unr);
664*ad4c8671SChristos Margiolis 	if (d->r_unr != NULL)
665*ad4c8671SChristos Margiolis 		delete_unrhdr(d->r_unr);
666*ad4c8671SChristos Margiolis 	if (d->vr_unr != NULL)
667*ad4c8671SChristos Margiolis 		delete_unrhdr(d->vr_unr);
668bba4862cSAriff Abdullah 
669bba4862cSAriff Abdullah 	if (snd_unit == device_get_unit(dev)) {
670cbebc90dSAlexander Motin 		snd_unit = pcm_best_unit(-1);
671cbebc90dSAlexander Motin 		if (snd_unit_auto == 0)
672cbebc90dSAlexander Motin 			snd_unit_auto = 1;
673e4e61333SAriff Abdullah 	}
674bba4862cSAriff Abdullah 
675e4e61333SAriff Abdullah 	return (0);
67633dbf14aSCameron Grant }
6777c438dbeSCameron Grant 
67867b1dce3SCameron Grant /************************************************************************/
67967b1dce3SCameron Grant 
680b611c801SAlexander Leidinger /**
681b611c801SAlexander Leidinger  * @brief	Handle OSSv4 SNDCTL_SYSINFO ioctl.
682b611c801SAlexander Leidinger  *
683b611c801SAlexander Leidinger  * @param si	Pointer to oss_sysinfo struct where information about the
684b611c801SAlexander Leidinger  * 		sound subsystem will be written/copied.
685b611c801SAlexander Leidinger  *
686b611c801SAlexander Leidinger  * This routine returns information about the sound system, such as the
687b611c801SAlexander Leidinger  * current OSS version, number of audio, MIDI, and mixer drivers, etc.
688b611c801SAlexander Leidinger  * Also includes a bitmask showing which of the above types of devices
689b611c801SAlexander Leidinger  * are open (busy).
690b611c801SAlexander Leidinger  *
691b611c801SAlexander Leidinger  * @note
692b611c801SAlexander Leidinger  * Calling threads must not hold any snddev_info or pcm_channel locks.
693b611c801SAlexander Leidinger  *
694b611c801SAlexander Leidinger  * @author	Ryan Beasley <ryanb@FreeBSD.org>
695b611c801SAlexander Leidinger  */
696b611c801SAlexander Leidinger void
697b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si)
698b611c801SAlexander Leidinger {
699b611c801SAlexander Leidinger 	static char si_product[] = "FreeBSD native OSS ABI";
700b611c801SAlexander Leidinger 	static char si_version[] = __XSTRING(__FreeBSD_version);
701c412d503SAlexander Motin 	static char si_license[] = "BSD";
702b611c801SAlexander Leidinger 	static int intnbits = sizeof(int) * 8;	/* Better suited as macro?
703b611c801SAlexander Leidinger 						   Must pester a C guru. */
704b611c801SAlexander Leidinger 
705b611c801SAlexander Leidinger 	struct snddev_info *d;
706b611c801SAlexander Leidinger 	struct pcm_channel *c;
70759d98edaSChristos Margiolis 	int j;
708c597c557SChristos Margiolis 	size_t i;
709b611c801SAlexander Leidinger 
710b611c801SAlexander Leidinger 	strlcpy(si->product, si_product, sizeof(si->product));
711b611c801SAlexander Leidinger 	strlcpy(si->version, si_version, sizeof(si->version));
712b611c801SAlexander Leidinger 	si->versionnum = SOUND_VERSION;
713c412d503SAlexander Motin 	strlcpy(si->license, si_license, sizeof(si->license));
714b611c801SAlexander Leidinger 
715b611c801SAlexander Leidinger 	/*
716b611c801SAlexander Leidinger 	 * Iterate over PCM devices and their channels, gathering up data
717e07f9178SChristos Margiolis 	 * for the numaudioengines and openedaudio fields.
718b611c801SAlexander Leidinger 	 */
719e07f9178SChristos Margiolis 	si->numaudioengines = 0;
720b611c801SAlexander Leidinger 	bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
721b611c801SAlexander Leidinger 
722b611c801SAlexander Leidinger 	j = 0;
723b611c801SAlexander Leidinger 
7249c271f79SAriff Abdullah 	for (i = 0; pcm_devclass != NULL &&
7259c271f79SAriff Abdullah 	    i < devclass_get_maxunit(pcm_devclass); i++) {
726b611c801SAlexander Leidinger 		d = devclass_get_softc(pcm_devclass, i);
727e4e61333SAriff Abdullah 		if (!PCM_REGISTERED(d))
728b611c801SAlexander Leidinger 			continue;
729b611c801SAlexander Leidinger 
730e4e61333SAriff Abdullah 		/* XXX Need Giant magic entry ??? */
731e4e61333SAriff Abdullah 
732b611c801SAlexander Leidinger 		/* See note in function's docblock */
73390da2b28SAriff Abdullah 		PCM_UNLOCKASSERT(d);
73490da2b28SAriff Abdullah 		PCM_LOCK(d);
735b611c801SAlexander Leidinger 
736e07f9178SChristos Margiolis 		si->numaudioengines += PCM_CHANCOUNT(d);
737b611c801SAlexander Leidinger 
738bba4862cSAriff Abdullah 		CHN_FOREACH(c, d, channels.pcm) {
73990da2b28SAriff Abdullah 			CHN_UNLOCKASSERT(c);
740b611c801SAlexander Leidinger 			CHN_LOCK(c);
741b611c801SAlexander Leidinger 			if (c->flags & CHN_F_BUSY)
742b611c801SAlexander Leidinger 				si->openedaudio[j / intnbits] |=
743b611c801SAlexander Leidinger 				    (1 << (j % intnbits));
744b611c801SAlexander Leidinger 			CHN_UNLOCK(c);
745b611c801SAlexander Leidinger 			j++;
746b611c801SAlexander Leidinger 		}
747b611c801SAlexander Leidinger 
74890da2b28SAriff Abdullah 		PCM_UNLOCK(d);
749b611c801SAlexander Leidinger 	}
750b611c801SAlexander Leidinger 
751b611c801SAlexander Leidinger 	si->numsynths = 0;	/* OSSv4 docs:  this field is obsolete */
752b611c801SAlexander Leidinger 	/**
753b611c801SAlexander Leidinger 	 * @todo	Collect num{midis,timers}.
754b611c801SAlexander Leidinger 	 *
755b611c801SAlexander Leidinger 	 * Need access to sound/midi/midi.c::midistat_lock in order
756b611c801SAlexander Leidinger 	 * to safely touch midi_devices and get a head count of, well,
757b611c801SAlexander Leidinger 	 * MIDI devices.  midistat_lock is a global static (i.e., local to
758b611c801SAlexander Leidinger 	 * midi.c), but midi_devices is a regular global; should the mutex
759b611c801SAlexander Leidinger 	 * be publicized, or is there another way to get this information?
760b611c801SAlexander Leidinger 	 *
761b611c801SAlexander Leidinger 	 * NB:	MIDI/sequencer stuff is currently on hold.
762b611c801SAlexander Leidinger 	 */
763b611c801SAlexander Leidinger 	si->nummidis = 0;
764b611c801SAlexander Leidinger 	si->numtimers = 0;
7655d980fadSChristos Margiolis 	/*
7665d980fadSChristos Margiolis 	 * Set this to the maximum unit number so that applications will not
7675d980fadSChristos Margiolis 	 * break if they try to loop through all mixers and some of them are
7685d980fadSChristos Margiolis 	 * not available.
7695d980fadSChristos Margiolis 	 */
7705d980fadSChristos Margiolis 	si->nummixers = devclass_get_maxunit(pcm_devclass);
77159d98edaSChristos Margiolis 	si->numcards = devclass_get_maxunit(pcm_devclass);
772e07f9178SChristos Margiolis 	si->numaudios = devclass_get_maxunit(pcm_devclass);
773b611c801SAlexander Leidinger 		/* OSSv4 docs:	Intended only for test apps; API doesn't
774b611c801SAlexander Leidinger 		   really have much of a concept of cards.  Shouldn't be
775b611c801SAlexander Leidinger 		   used by applications. */
776b611c801SAlexander Leidinger 
777b611c801SAlexander Leidinger 	/**
778b611c801SAlexander Leidinger 	 * @todo	Fill in "busy devices" fields.
779b611c801SAlexander Leidinger 	 *
780b611c801SAlexander Leidinger 	 *  si->openedmidi = " MIDI devices
781b611c801SAlexander Leidinger 	 */
782b611c801SAlexander Leidinger 	bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
783b611c801SAlexander Leidinger 
784b611c801SAlexander Leidinger 	/*
785b611c801SAlexander Leidinger 	 * Si->filler is a reserved array, but according to docs each
786b611c801SAlexander Leidinger 	 * element should be set to -1.
787b611c801SAlexander Leidinger 	 */
788c597c557SChristos Margiolis 	for (i = 0; i < nitems(si->filler); i++)
789b611c801SAlexander Leidinger 		si->filler[i] = -1;
790b611c801SAlexander Leidinger }
791b611c801SAlexander Leidinger 
79252f6e09eSAlexander Motin int
79352f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si)
79452f6e09eSAlexander Motin {
79552f6e09eSAlexander Motin 	struct snddev_info *d;
796305db91dSChristos Margiolis 	int i;
79752f6e09eSAlexander Motin 
79852f6e09eSAlexander Motin 	for (i = 0; pcm_devclass != NULL &&
79952f6e09eSAlexander Motin 	    i < devclass_get_maxunit(pcm_devclass); i++) {
80052f6e09eSAlexander Motin 		d = devclass_get_softc(pcm_devclass, i);
801305db91dSChristos Margiolis 		if (i != si->card)
80252f6e09eSAlexander Motin 			continue;
80352f6e09eSAlexander Motin 
8045d980fadSChristos Margiolis 		if (!PCM_REGISTERED(d)) {
8055d980fadSChristos Margiolis 			snprintf(si->shortname, sizeof(si->shortname),
8065d980fadSChristos Margiolis 			    "pcm%d (n/a)", i);
8075d980fadSChristos Margiolis 			strlcpy(si->longname, "Device unavailable",
8085d980fadSChristos Margiolis 			    sizeof(si->longname));
8095d980fadSChristos Margiolis 			si->hw_info[0] = '\0';
8105d980fadSChristos Margiolis 			si->intr_count = si->ack_count = 0;
8115d980fadSChristos Margiolis 		} else {
81290da2b28SAriff Abdullah 			PCM_UNLOCKASSERT(d);
81390da2b28SAriff Abdullah 			PCM_LOCK(d);
81452f6e09eSAlexander Motin 
81552f6e09eSAlexander Motin 			strlcpy(si->shortname, device_get_nameunit(d->dev),
81652f6e09eSAlexander Motin 			    sizeof(si->shortname));
81752f6e09eSAlexander Motin 			strlcpy(si->longname, device_get_desc(d->dev),
81852f6e09eSAlexander Motin 			    sizeof(si->longname));
81952f6e09eSAlexander Motin 			strlcpy(si->hw_info, d->status, sizeof(si->hw_info));
82052f6e09eSAlexander Motin 			si->intr_count = si->ack_count = 0;
82190da2b28SAriff Abdullah 
82290da2b28SAriff Abdullah 			PCM_UNLOCK(d);
8235d980fadSChristos Margiolis 		}
82490da2b28SAriff Abdullah 
82552f6e09eSAlexander Motin 		return (0);
82652f6e09eSAlexander Motin 	}
82752f6e09eSAlexander Motin 	return (ENXIO);
82852f6e09eSAlexander Motin }
82952f6e09eSAlexander Motin 
830b611c801SAlexander Leidinger /************************************************************************/
831b611c801SAlexander Leidinger 
8320739ea1dSSeigo Tanimura static int
8330739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data)
8340739ea1dSSeigo Tanimura {
835b611c801SAlexander Leidinger 	int ret;
836b611c801SAlexander Leidinger 
8378109ec9dSJohn Baldwin 	ret = 0;
838b611c801SAlexander Leidinger 	switch (type) {
839b611c801SAlexander Leidinger 		case MOD_LOAD:
84013bebcd3SJohn Baldwin 			pcm_devclass = devclass_create("pcm");
841b611c801SAlexander Leidinger 			pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
842b611c801SAlexander Leidinger 			break;
843b611c801SAlexander Leidinger 		case MOD_UNLOAD:
844b611c801SAlexander Leidinger 			if (pcmsg_unrhdr != NULL) {
845b611c801SAlexander Leidinger 				delete_unrhdr(pcmsg_unrhdr);
846b611c801SAlexander Leidinger 				pcmsg_unrhdr = NULL;
847b611c801SAlexander Leidinger 			}
848b611c801SAlexander Leidinger 			break;
8495525b53dSAlexander Motin 		case MOD_SHUTDOWN:
8505525b53dSAlexander Motin 			break;
851b611c801SAlexander Leidinger 		default:
85290da2b28SAriff Abdullah 			ret = ENOTSUP;
853b611c801SAlexander Leidinger 	}
854b611c801SAlexander Leidinger 
855b611c801SAlexander Leidinger 	return ret;
8560739ea1dSSeigo Tanimura }
8570739ea1dSSeigo Tanimura 
8580739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL);
8590739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER);
860