xref: /freebsd/sys/dev/sound/pcm/sound.c (revision 3af2beb8b24ba21fb7e7fbd165a79b8eeff37712)
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 /* return error status and a locked channel */
1133fdb3676SAriff Abdullah int
1143fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
11525723d66SChristos Margiolis     pid_t pid, char *comm)
116285648f9SCameron Grant {
117285648f9SCameron Grant 	struct pcm_channel *c;
11890da2b28SAriff Abdullah 	int err, vchancount, vchan_num;
11925723d66SChristos Margiolis 	bool retry;
120bba4862cSAriff Abdullah 
12125723d66SChristos Margiolis 	KASSERT(d != NULL && ch != NULL &&
122bba4862cSAriff Abdullah 	    (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
12325723d66SChristos Margiolis 	    ("%s(): invalid d=%p ch=%p direction=%d pid=%d",
12425723d66SChristos Margiolis 	    __func__, d, ch, direction, pid));
125e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
126bba4862cSAriff Abdullah 
12790da2b28SAriff Abdullah 	*ch = NULL;
12890da2b28SAriff Abdullah 	vchan_num = 0;
12990da2b28SAriff Abdullah 	vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount :
13090da2b28SAriff Abdullah 	    d->rvchancount;
13190da2b28SAriff Abdullah 
13225723d66SChristos Margiolis 	retry = false;
1333fdb3676SAriff Abdullah retry_chnalloc:
13490da2b28SAriff Abdullah 	err = ENOTSUP;
135f637a36cSCameron Grant 	/* scan for a free channel */
136bba4862cSAriff Abdullah 	CHN_FOREACH(c, d, channels.pcm) {
13749c5e6e2SCameron Grant 		CHN_LOCK(c);
13825723d66SChristos Margiolis 		if (c->direction == direction && (c->flags & CHN_F_VIRTUAL)) {
13990da2b28SAriff Abdullah 			if (vchancount < snd_maxautovchans &&
14025723d66SChristos Margiolis 			    vchan_num < c->unit) {
14190da2b28SAriff Abdullah 			    	CHN_UNLOCK(c);
14290da2b28SAriff Abdullah 				goto vchan_alloc;
14390da2b28SAriff Abdullah 			}
14490da2b28SAriff Abdullah 			vchan_num++;
14590da2b28SAriff Abdullah 		}
14625723d66SChristos Margiolis 		if (c->direction == direction && !(c->flags & CHN_F_BUSY)) {
147285648f9SCameron Grant 			c->flags |= CHN_F_BUSY;
148b8f0d9e0SCameron Grant 			c->pid = pid;
14990da2b28SAriff Abdullah 			strlcpy(c->comm, (comm != NULL) ? comm :
15090da2b28SAriff Abdullah 			    CHN_COMM_UNKNOWN, sizeof(c->comm));
1513fdb3676SAriff Abdullah 			*ch = c;
152bba4862cSAriff Abdullah 			return (0);
15325723d66SChristos Margiolis 		} else if (c->direction == direction && (c->flags & CHN_F_BUSY))
154a1d444e1SAriff Abdullah 			err = EBUSY;
15549c5e6e2SCameron Grant 		CHN_UNLOCK(c);
156285648f9SCameron Grant 	}
157f637a36cSCameron Grant 
15825723d66SChristos Margiolis 	/*
15925723d66SChristos Margiolis 	 * We came from retry_chnalloc and still didn't find a free channel.
16025723d66SChristos Margiolis 	 */
16125723d66SChristos Margiolis 	if (retry)
162e4e61333SAriff Abdullah 		return (err);
163e4e61333SAriff Abdullah 
16490da2b28SAriff Abdullah vchan_alloc:
165f637a36cSCameron Grant 	/* no channel available */
16625723d66SChristos Margiolis 	if (!(vchancount > 0 && vchancount < snd_maxautovchans))
167bba4862cSAriff Abdullah 		return (err);
168*3af2beb8SChristos Margiolis 	err = vchan_setnew(d, direction, vchancount + 1);
169a1d444e1SAriff Abdullah 	if (err == 0) {
17025723d66SChristos Margiolis 		retry = true;
1713fdb3676SAriff Abdullah 		goto retry_chnalloc;
172f637a36cSCameron Grant 	}
173f637a36cSCameron Grant 
174bba4862cSAriff Abdullah 	return (err);
175285648f9SCameron Grant }
176285648f9SCameron Grant 
17733dbf14aSCameron Grant static int
178851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
17933dbf14aSCameron Grant {
180b8f0d9e0SCameron Grant 	struct snddev_info *d;
18133dbf14aSCameron Grant 	int error, unit;
18233dbf14aSCameron Grant 
18333dbf14aSCameron Grant 	unit = snd_unit;
184041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &unit, 0, req);
18533dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
186b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
187e4e61333SAriff Abdullah 		if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
188b8f0d9e0SCameron Grant 			return EINVAL;
18933dbf14aSCameron Grant 		snd_unit = unit;
190cbebc90dSAlexander Motin 		snd_unit_auto = 0;
19133dbf14aSCameron Grant 	}
19233dbf14aSCameron Grant 	return (error);
19333dbf14aSCameron Grant }
194851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */
195b7d6c6b5SEitan Adler SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit,
1967029da5cSPawel Biernacki     CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0,
1977029da5cSPawel Biernacki     sizeof(int), sysctl_hw_snd_default_unit, "I",
198b7d6c6b5SEitan Adler     "default sound device");
199987e5972SCameron Grant 
200139bcec8SChristos Margiolis void
2015ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
202285648f9SCameron Grant {
203e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
20490da2b28SAriff Abdullah 	PCM_LOCKASSERT(d);
205bba4862cSAriff Abdullah 	KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
206bba4862cSAriff Abdullah 	    ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
207285648f9SCameron Grant 
20890da2b28SAriff Abdullah 	CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm);
2093fdb3676SAriff Abdullah 
21025723d66SChristos Margiolis 	switch (ch->type) {
211e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_PLAY:
212e4e61333SAriff Abdullah 		d->playcount++;
213e4e61333SAriff Abdullah 		break;
214e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_VPLAY:
215e4e61333SAriff Abdullah 		d->pvchancount++;
216e4e61333SAriff Abdullah 		break;
217e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_REC:
218e4e61333SAriff Abdullah 		d->reccount++;
219e4e61333SAriff Abdullah 		break;
220e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_VREC:
221e4e61333SAriff Abdullah 		d->rvchancount++;
222e4e61333SAriff Abdullah 		break;
223e4e61333SAriff Abdullah 	default:
22476f95baeSChristos Margiolis 		__assert_unreachable();
225e4e61333SAriff Abdullah 	}
22633dbf14aSCameron Grant }
22733dbf14aSCameron Grant 
228285648f9SCameron Grant int
2295ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
23033dbf14aSCameron Grant {
231bba4862cSAriff Abdullah 	struct pcm_channel *tmp;
23233dbf14aSCameron Grant 
233e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
23490da2b28SAriff Abdullah 	PCM_LOCKASSERT(d);
235e4e61333SAriff Abdullah 
236bba4862cSAriff Abdullah 	tmp = NULL;
237a527dbc7SCameron Grant 
238bba4862cSAriff Abdullah 	CHN_FOREACH(tmp, d, channels.pcm) {
239bba4862cSAriff Abdullah 		if (tmp == ch)
240bba4862cSAriff Abdullah 			break;
24133dbf14aSCameron Grant 	}
242bba4862cSAriff Abdullah 
243bba4862cSAriff Abdullah 	if (tmp != ch)
244e4e61333SAriff Abdullah 		return (EINVAL);
24567beb5a5SCameron Grant 
246bba4862cSAriff Abdullah 	CHN_REMOVE(d, ch, channels.pcm);
247e4e61333SAriff Abdullah 
24825723d66SChristos Margiolis 	switch (ch->type) {
249bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_PLAY:
25067beb5a5SCameron Grant 		d->playcount--;
251bba4862cSAriff Abdullah 		break;
252bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_VPLAY:
253bba4862cSAriff Abdullah 		d->pvchancount--;
254bba4862cSAriff Abdullah 		break;
255bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_REC:
256bba4862cSAriff Abdullah 		d->reccount--;
257bba4862cSAriff Abdullah 		break;
258bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_VREC:
259bba4862cSAriff Abdullah 		d->rvchancount--;
260bba4862cSAriff Abdullah 		break;
261bba4862cSAriff Abdullah 	default:
26276f95baeSChristos Margiolis 		__assert_unreachable();
263bba4862cSAriff Abdullah 	}
264285648f9SCameron Grant 
265e4e61333SAriff Abdullah 	return (0);
266987e5972SCameron Grant }
267987e5972SCameron Grant 
268987e5972SCameron Grant int
269285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
270285648f9SCameron Grant {
271285648f9SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
27267b1dce3SCameron Grant 	struct pcm_channel *ch;
273285648f9SCameron Grant 
274e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
275e4e61333SAriff Abdullah 
27690da2b28SAriff Abdullah 	PCM_LOCK(d);
277*3af2beb8SChristos Margiolis 	ch = chn_init(d, NULL, cls, dir, devinfo);
278285648f9SCameron Grant 	if (!ch) {
2792e9962efSChristos Margiolis 		device_printf(d->dev, "chn_init(%s, %d, %p) failed\n",
280e4e61333SAriff Abdullah 		    cls->name, dir, devinfo);
28190da2b28SAriff Abdullah 		PCM_UNLOCK(d);
282e4e61333SAriff Abdullah 		return (ENODEV);
283285648f9SCameron Grant 	}
284cd9766c5SCameron Grant 
285139bcec8SChristos Margiolis 	pcm_chn_add(d, ch);
28690da2b28SAriff Abdullah 	PCM_UNLOCK(d);
287cd9766c5SCameron Grant 
288139bcec8SChristos Margiolis 	return (0);
289285648f9SCameron Grant }
290285648f9SCameron Grant 
29103614fcbSChristos Margiolis static void
29203614fcbSChristos Margiolis pcm_killchans(struct snddev_info *d)
293285648f9SCameron Grant {
294e33bee07SOlivier Houchard 	struct pcm_channel *ch;
295e4e61333SAriff Abdullah 	int error;
29603614fcbSChristos Margiolis 	bool found;
297e4e61333SAriff Abdullah 
298e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
29903614fcbSChristos Margiolis 	do {
30003614fcbSChristos Margiolis 		found = false;
30103614fcbSChristos Margiolis 		CHN_FOREACH(ch, d, channels.pcm) {
30203614fcbSChristos Margiolis 			CHN_LOCK(ch);
30303614fcbSChristos Margiolis 			/*
30403614fcbSChristos Margiolis 			 * Make sure no channel has went to sleep in the
30503614fcbSChristos Margiolis 			 * meantime.
30603614fcbSChristos Margiolis 			 */
30703614fcbSChristos Margiolis 			chn_shutdown(ch);
30803614fcbSChristos Margiolis 			/*
30903614fcbSChristos Margiolis 			 * We have to give a thread sleeping in chn_sleep() a
31003614fcbSChristos Margiolis 			 * chance to observe that the channel is dead.
31103614fcbSChristos Margiolis 			 */
31203614fcbSChristos Margiolis 			if ((ch->flags & CHN_F_SLEEPING) == 0) {
31303614fcbSChristos Margiolis 				found = true;
31403614fcbSChristos Margiolis 				CHN_UNLOCK(ch);
31503614fcbSChristos Margiolis 				break;
31603614fcbSChristos Margiolis 			}
31703614fcbSChristos Margiolis 			CHN_UNLOCK(ch);
31803614fcbSChristos Margiolis 		}
319285648f9SCameron Grant 
32003614fcbSChristos Margiolis 		/*
32103614fcbSChristos Margiolis 		 * All channels are still sleeping. Sleep for a bit and try
32203614fcbSChristos Margiolis 		 * again to see if any of them is awake now.
32303614fcbSChristos Margiolis 		 */
32403614fcbSChristos Margiolis 		if (!found) {
32503614fcbSChristos Margiolis 			pause_sbt("pcmkillchans", SBT_1MS * 5, 0, 0);
32603614fcbSChristos Margiolis 			continue;
32703614fcbSChristos Margiolis 		}
328285648f9SCameron Grant 
32990da2b28SAriff Abdullah 		PCM_LOCK(d);
330bba4862cSAriff Abdullah 		error = pcm_chn_remove(d, ch);
33190da2b28SAriff Abdullah 		PCM_UNLOCK(d);
33203614fcbSChristos Margiolis 		if (error == 0)
333b3ea087cSChristos Margiolis 			chn_kill(ch);
33403614fcbSChristos Margiolis 	} while (!CHN_EMPTY(d, channels.pcm));
335285648f9SCameron Grant }
336285648f9SCameron Grant 
337cbebc90dSAlexander Motin static int
338cbebc90dSAlexander Motin pcm_best_unit(int old)
339cbebc90dSAlexander Motin {
340cbebc90dSAlexander Motin 	struct snddev_info *d;
341cbebc90dSAlexander Motin 	int i, best, bestprio, prio;
342cbebc90dSAlexander Motin 
343cbebc90dSAlexander Motin 	best = -1;
344cbebc90dSAlexander Motin 	bestprio = -100;
345cbebc90dSAlexander Motin 	for (i = 0; pcm_devclass != NULL &&
346cbebc90dSAlexander Motin 	    i < devclass_get_maxunit(pcm_devclass); i++) {
347cbebc90dSAlexander Motin 		d = devclass_get_softc(pcm_devclass, i);
348cbebc90dSAlexander Motin 		if (!PCM_REGISTERED(d))
349cbebc90dSAlexander Motin 			continue;
350cbebc90dSAlexander Motin 		prio = 0;
351cbebc90dSAlexander Motin 		if (d->playcount == 0)
352cbebc90dSAlexander Motin 			prio -= 10;
353cbebc90dSAlexander Motin 		if (d->reccount == 0)
354cbebc90dSAlexander Motin 			prio -= 2;
355cbebc90dSAlexander Motin 		if (prio > bestprio || (prio == bestprio && i == old)) {
356cbebc90dSAlexander Motin 			best = i;
357cbebc90dSAlexander Motin 			bestprio = prio;
358cbebc90dSAlexander Motin 		}
359cbebc90dSAlexander Motin 	}
360cbebc90dSAlexander Motin 	return (best);
361cbebc90dSAlexander Motin }
362cbebc90dSAlexander Motin 
363285648f9SCameron Grant int
364987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
365987e5972SCameron Grant {
36666ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
36749c5e6e2SCameron Grant 
36832a0e5d5SHans Petter Selasky 	/* should only be called once */
36932a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_REGISTERED)
37032a0e5d5SHans Petter Selasky 		return (EINVAL);
37132a0e5d5SHans Petter Selasky 
372e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
373bba4862cSAriff Abdullah 
374e4e61333SAriff Abdullah 	if (d->playcount == 0 || d->reccount == 0)
375e4e61333SAriff Abdullah 		d->flags |= SD_F_SIMPLEX;
376e4e61333SAriff Abdullah 
37732a0e5d5SHans Petter Selasky 	if (d->playcount > 0 || d->reccount > 0)
378e4e61333SAriff Abdullah 		d->flags |= SD_F_AUTOVCHAN;
379e4e61333SAriff Abdullah 
3807ad5f383SChristos Margiolis 	vchan_setmaxauto(d, snd_maxautovchans);
381bba4862cSAriff Abdullah 
382a580b31aSAriff Abdullah 	strlcpy(d->status, str, SND_STATUSLEN);
383bba4862cSAriff Abdullah 
38490da2b28SAriff Abdullah 	PCM_LOCK(d);
385e4e61333SAriff Abdullah 
386e4e61333SAriff Abdullah 	/* Done, we're ready.. */
387e4e61333SAriff Abdullah 	d->flags |= SD_F_REGISTERED;
388e4e61333SAriff Abdullah 
389e4e61333SAriff Abdullah 	PCM_RELEASE(d);
390bba4862cSAriff Abdullah 
39190da2b28SAriff Abdullah 	PCM_UNLOCK(d);
392bba4862cSAriff Abdullah 
39332a0e5d5SHans Petter Selasky 	/*
39432a0e5d5SHans Petter Selasky 	 * Create all sysctls once SD_F_REGISTERED is set else
39532a0e5d5SHans Petter Selasky 	 * tunable sysctls won't work:
39632a0e5d5SHans Petter Selasky 	 */
39732a0e5d5SHans Petter Selasky 	pcm_sysinit(dev);
39832a0e5d5SHans Petter Selasky 
399cbebc90dSAlexander Motin 	if (snd_unit_auto < 0)
400cbebc90dSAlexander Motin 		snd_unit_auto = (snd_unit < 0) ? 1 : 0;
401cbebc90dSAlexander Motin 	if (snd_unit < 0 || snd_unit_auto > 1)
402f3685841SAriff Abdullah 		snd_unit = device_get_unit(dev);
403cbebc90dSAlexander Motin 	else if (snd_unit_auto == 1)
404cbebc90dSAlexander Motin 		snd_unit = pcm_best_unit(snd_unit);
405f3685841SAriff Abdullah 
406e4e61333SAriff Abdullah 	return (0);
407987e5972SCameron Grant }
408987e5972SCameron Grant 
409a1d444e1SAriff Abdullah uint32_t
410987e5972SCameron Grant pcm_getflags(device_t dev)
411987e5972SCameron Grant {
41266ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
41349c5e6e2SCameron Grant 
414987e5972SCameron Grant 	return d->flags;
415987e5972SCameron Grant }
416987e5972SCameron Grant 
417987e5972SCameron Grant void
418a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val)
419987e5972SCameron Grant {
42066ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
421d95502a8SCameron Grant 
422987e5972SCameron Grant 	d->flags = val;
423987e5972SCameron Grant }
424987e5972SCameron Grant 
42539004e69SCameron Grant void *
42639004e69SCameron Grant pcm_getdevinfo(device_t dev)
42739004e69SCameron Grant {
42866ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
42949c5e6e2SCameron Grant 
43039004e69SCameron Grant 	return d->devinfo;
43139004e69SCameron Grant }
43239004e69SCameron Grant 
433a67fe5c1SCameron Grant unsigned int
434d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
435a67fe5c1SCameron Grant {
436a67fe5c1SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
4374e60be34SCameron Grant 	int sz, x;
438a67fe5c1SCameron Grant 
439a67fe5c1SCameron Grant 	sz = 0;
4404e60be34SCameron Grant 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
4414e60be34SCameron Grant 		x = sz;
442d55d96f6SAlexander Leidinger 		RANGE(sz, minbufsz, maxbufsz);
4434e60be34SCameron Grant 		if (x != sz)
444d55d96f6SAlexander Leidinger 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
445d55d96f6SAlexander Leidinger 		x = minbufsz;
4464e60be34SCameron Grant 		while (x < sz)
4474e60be34SCameron Grant 			x <<= 1;
4484e60be34SCameron Grant 		if (x > sz)
4494e60be34SCameron Grant 			x >>= 1;
4504e60be34SCameron Grant 		if (x != sz) {
4514e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
4524e60be34SCameron Grant 			sz = x;
4534e60be34SCameron Grant 		}
4544e60be34SCameron Grant 	} else {
455a67fe5c1SCameron Grant 		sz = deflt;
4564e60be34SCameron Grant 	}
4574e60be34SCameron Grant 
458a67fe5c1SCameron Grant 	d->bufsz = sz;
459a67fe5c1SCameron Grant 
460a67fe5c1SCameron Grant 	return sz;
461a67fe5c1SCameron Grant }
462a67fe5c1SCameron Grant 
46390da2b28SAriff Abdullah static int
46490da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)
46590da2b28SAriff Abdullah {
46690da2b28SAriff Abdullah 	struct snddev_info *d;
46790da2b28SAriff Abdullah 	int err, val;
46890da2b28SAriff Abdullah 
46990da2b28SAriff Abdullah 	d = oidp->oid_arg1;
47090da2b28SAriff Abdullah 	if (!PCM_REGISTERED(d))
47190da2b28SAriff Abdullah 		return (ENODEV);
47290da2b28SAriff Abdullah 
47390da2b28SAriff Abdullah 	PCM_LOCK(d);
47490da2b28SAriff Abdullah 	PCM_WAIT(d);
47590da2b28SAriff Abdullah 	val = (d->flags & SD_F_BITPERFECT) ? 1 : 0;
47690da2b28SAriff Abdullah 	PCM_ACQUIRE(d);
47790da2b28SAriff Abdullah 	PCM_UNLOCK(d);
47890da2b28SAriff Abdullah 
47990da2b28SAriff Abdullah 	err = sysctl_handle_int(oidp, &val, 0, req);
48090da2b28SAriff Abdullah 
48190da2b28SAriff Abdullah 	if (err == 0 && req->newptr != NULL) {
48290da2b28SAriff Abdullah 		if (!(val == 0 || val == 1)) {
48390da2b28SAriff Abdullah 			PCM_RELEASE_QUICK(d);
48490da2b28SAriff Abdullah 			return (EINVAL);
48590da2b28SAriff Abdullah 		}
48690da2b28SAriff Abdullah 
48790da2b28SAriff Abdullah 		PCM_LOCK(d);
48890da2b28SAriff Abdullah 
48990da2b28SAriff Abdullah 		d->flags &= ~SD_F_BITPERFECT;
49090da2b28SAriff Abdullah 		d->flags |= (val != 0) ? SD_F_BITPERFECT : 0;
49190da2b28SAriff Abdullah 
49290da2b28SAriff Abdullah 		PCM_RELEASE(d);
49390da2b28SAriff Abdullah 		PCM_UNLOCK(d);
49490da2b28SAriff Abdullah 	} else
49590da2b28SAriff Abdullah 		PCM_RELEASE_QUICK(d);
49690da2b28SAriff Abdullah 
49790da2b28SAriff Abdullah 	return (err);
49890da2b28SAriff Abdullah }
49990da2b28SAriff Abdullah 
500ed2196e5SHans Petter Selasky static u_int8_t
501ed2196e5SHans Petter Selasky pcm_mode_init(struct snddev_info *d)
502ed2196e5SHans Petter Selasky {
503ed2196e5SHans Petter Selasky 	u_int8_t mode = 0;
504ed2196e5SHans Petter Selasky 
505ed2196e5SHans Petter Selasky 	if (d->playcount > 0)
506ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_PLAY;
507ed2196e5SHans Petter Selasky 	if (d->reccount > 0)
508ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_REC;
509ed2196e5SHans Petter Selasky 	if (d->mixer_dev != NULL)
510ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_MIXER;
511ed2196e5SHans Petter Selasky 
512ed2196e5SHans Petter Selasky 	return (mode);
513ed2196e5SHans Petter Selasky }
514ed2196e5SHans Petter Selasky 
51532a0e5d5SHans Petter Selasky static void
51632a0e5d5SHans Petter Selasky pcm_sysinit(device_t dev)
51732a0e5d5SHans Petter Selasky {
51832a0e5d5SHans Petter Selasky   	struct snddev_info *d = device_get_softc(dev);
519ed2196e5SHans Petter Selasky 	u_int8_t mode;
520ed2196e5SHans Petter Selasky 
521ed2196e5SHans Petter Selasky 	mode = pcm_mode_init(d);
52232a0e5d5SHans Petter Selasky 
523132fca63SHans Petter Selasky 	/* XXX: a user should be able to set this with a control tool, the
52432a0e5d5SHans Petter Selasky 	   sysadmin then needs min+max sysctls for this */
52532a0e5d5SHans Petter Selasky 	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
52632a0e5d5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
52732a0e5d5SHans Petter Selasky             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
52832a0e5d5SHans Petter Selasky 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
52932a0e5d5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
5303b4c5433SAlexander Motin 	    "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d,
5317029da5cSPawel Biernacki 	    sizeof(d), sysctl_dev_pcm_bitperfect, "I",
53232a0e5d5SHans Petter Selasky 	    "bit-perfect playback/recording (0=disable, 1=enable)");
533ed2196e5SHans Petter Selasky 	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
534ed2196e5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
535ed2196e5SHans Petter Selasky 	    OID_AUTO, "mode", CTLFLAG_RD, NULL, mode,
536e56c8996SChristos Margiolis 	    "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than "
537e56c8996SChristos Margiolis 	    "one mode is supported)");
53832a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_AUTOVCHAN)
53932a0e5d5SHans Petter Selasky 		vchan_initsys(dev);
54032a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_EQ)
54132a0e5d5SHans Petter Selasky 		feeder_eq_initsys(dev);
54232a0e5d5SHans Petter Selasky }
54332a0e5d5SHans Petter Selasky 
544987e5972SCameron Grant int
545987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
546987e5972SCameron Grant {
547bba4862cSAriff Abdullah 	struct snddev_info *d;
54890da2b28SAriff Abdullah 	int i;
549987e5972SCameron Grant 
550b8a36395SCameron Grant 	if (pcm_veto_load) {
551b8a36395SCameron Grant 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
552b8a36395SCameron Grant 
553b8a36395SCameron Grant 		return EINVAL;
554b8a36395SCameron Grant 	}
555b8a36395SCameron Grant 
556bba4862cSAriff Abdullah 	d = device_get_softc(dev);
557e4e61333SAriff Abdullah 	d->dev = dev;
5582c69ba87SJohn Baldwin 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
559e4e61333SAriff Abdullah 	cv_init(&d->cv, device_get_nameunit(dev));
560e4e61333SAriff Abdullah 	PCM_ACQUIRE_QUICK(d);
5617233ababSAlexander Leidinger #if 0
5627233ababSAlexander Leidinger 	/*
5637233ababSAlexander Leidinger 	 * d->flags should be cleared by the allocator of the softc.
5647233ababSAlexander Leidinger 	 * We cannot clear this field here because several devices set
5657233ababSAlexander Leidinger 	 * this flag before calling pcm_register().
5667233ababSAlexander Leidinger 	 */
567cd9766c5SCameron Grant 	d->flags = 0;
5687233ababSAlexander Leidinger #endif
56990da2b28SAriff Abdullah 	i = 0;
57090da2b28SAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
57190da2b28SAriff Abdullah 	    "vpc", &i) != 0 || i != 0)
57290da2b28SAriff Abdullah 		d->flags |= SD_F_VPC;
57390da2b28SAriff Abdullah 
57490da2b28SAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
57590da2b28SAriff Abdullah 	    "bitperfect", &i) == 0 && i != 0)
57690da2b28SAriff Abdullah 		d->flags |= SD_F_BITPERFECT;
57790da2b28SAriff Abdullah 
578987e5972SCameron Grant 	d->devinfo = devinfo;
579506a5308SCameron Grant 	d->reccount = 0;
580a67fe5c1SCameron Grant 	d->playcount = 0;
581bba4862cSAriff Abdullah 	d->pvchancount = 0;
582bba4862cSAriff Abdullah 	d->rvchancount = 0;
583bba4862cSAriff Abdullah 	d->pvchanrate = 0;
584bba4862cSAriff Abdullah 	d->pvchanformat = 0;
585bba4862cSAriff Abdullah 	d->rvchanrate = 0;
586bba4862cSAriff Abdullah 	d->rvchanformat = 0;
587833f7023SCameron Grant 
588bba4862cSAriff Abdullah 	CHN_INIT(d, channels.pcm);
589bba4862cSAriff Abdullah 	CHN_INIT(d, channels.pcm.busy);
59090da2b28SAriff Abdullah 	CHN_INIT(d, channels.pcm.opened);
59145550658SPoul-Henning Kamp 
592e4e61333SAriff Abdullah 	/* XXX This is incorrect, but lets play along for now. */
593a1d444e1SAriff Abdullah 	if ((numplay == 0 || numrec == 0) && numplay != numrec)
594285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
595d95502a8SCameron Grant 
596bba4862cSAriff Abdullah 	sysctl_ctx_init(&d->play_sysctl_ctx);
597bba4862cSAriff Abdullah 	d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
598bba4862cSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
5997029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node");
600bba4862cSAriff Abdullah 	sysctl_ctx_init(&d->rec_sysctl_ctx);
601bba4862cSAriff Abdullah 	d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
602bba4862cSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
603132fca63SHans Petter Selasky 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node");
604e4e61333SAriff Abdullah 
60532a0e5d5SHans Petter Selasky 	if (numplay > 0 || numrec > 0)
606cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
60790da2b28SAriff Abdullah 
6089da3b645SChristos Margiolis 	sndstat_register(dev, d->status);
609e4e61333SAriff Abdullah 
610e8c0d15aSChristos Margiolis 	return (dsp_make_dev(dev));
611987e5972SCameron Grant }
612987e5972SCameron Grant 
61333dbf14aSCameron Grant int
61433dbf14aSCameron Grant pcm_unregister(device_t dev)
6157c438dbeSCameron Grant {
616e4e61333SAriff Abdullah 	struct snddev_info *d;
617a67fe5c1SCameron Grant 	struct pcm_channel *ch;
6187c438dbeSCameron Grant 
619e4e61333SAriff Abdullah 	d = device_get_softc(dev);
620e4e61333SAriff Abdullah 
621e4e61333SAriff Abdullah 	if (!PCM_ALIVE(d)) {
622e4e61333SAriff Abdullah 		device_printf(dev, "unregister: device not configured\n");
623e4e61333SAriff Abdullah 		return (0);
624e4e61333SAriff Abdullah 	}
625bba4862cSAriff Abdullah 
62690da2b28SAriff Abdullah 	PCM_LOCK(d);
627e4e61333SAriff Abdullah 	PCM_WAIT(d);
628e4e61333SAriff Abdullah 
629cc1efc23SHans Petter Selasky 	d->flags |= SD_F_DETACHING;
630cc1efc23SHans Petter Selasky 
631e4e61333SAriff Abdullah 	PCM_ACQUIRE(d);
63290da2b28SAriff Abdullah 	PCM_UNLOCK(d);
633e4e61333SAriff Abdullah 
634e4e61333SAriff Abdullah 	CHN_FOREACH(ch, d, channels.pcm) {
635e4e61333SAriff Abdullah 		CHN_LOCK(ch);
63644e128feSChristos Margiolis 		/*
63703614fcbSChristos Margiolis 		 * Do not wait for the timeout in chn_read()/chn_write(). Wake
63803614fcbSChristos Margiolis 		 * up the sleeping thread and kill the channel.
63944e128feSChristos Margiolis 		 */
64003614fcbSChristos Margiolis 		chn_shutdown(ch);
64144e128feSChristos Margiolis 		chn_abort(ch);
642e4e61333SAriff Abdullah 		CHN_UNLOCK(ch);
643c9b53085SCameron Grant 	}
6445ee30e27SMathew Kanner 
6458461d581SHans Petter Selasky 	/* remove /dev/sndstat entry first */
6468461d581SHans Petter Selasky 	sndstat_unregister(dev);
6478461d581SHans Petter Selasky 
64890da2b28SAriff Abdullah 	PCM_LOCK(d);
649e4e61333SAriff Abdullah 	d->flags |= SD_F_DYING;
650e4e61333SAriff Abdullah 	d->flags &= ~SD_F_REGISTERED;
65190da2b28SAriff Abdullah 	PCM_UNLOCK(d);
652e4e61333SAriff Abdullah 
653bba4862cSAriff Abdullah 	if (d->play_sysctl_tree != NULL) {
654bba4862cSAriff Abdullah 		sysctl_ctx_free(&d->play_sysctl_ctx);
655bba4862cSAriff Abdullah 		d->play_sysctl_tree = NULL;
656bba4862cSAriff Abdullah 	}
657bba4862cSAriff Abdullah 	if (d->rec_sysctl_tree != NULL) {
658bba4862cSAriff Abdullah 		sysctl_ctx_free(&d->rec_sysctl_ctx);
659bba4862cSAriff Abdullah 		d->rec_sysctl_tree = NULL;
660a1d444e1SAriff Abdullah 	}
661bba4862cSAriff Abdullah 
662074d6fbeSChristos Margiolis 	dsp_destroy_dev(dev);
663074d6fbeSChristos Margiolis 	(void)mixer_uninit(dev);
664074d6fbeSChristos Margiolis 
66503614fcbSChristos Margiolis 	pcm_killchans(d);
6667c438dbeSCameron Grant 
66790da2b28SAriff Abdullah 	PCM_LOCK(d);
668e4e61333SAriff Abdullah 	PCM_RELEASE(d);
669e4e61333SAriff Abdullah 	cv_destroy(&d->cv);
67090da2b28SAriff Abdullah 	PCM_UNLOCK(d);
67149c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
672bba4862cSAriff Abdullah 
673bba4862cSAriff Abdullah 	if (snd_unit == device_get_unit(dev)) {
674cbebc90dSAlexander Motin 		snd_unit = pcm_best_unit(-1);
675cbebc90dSAlexander Motin 		if (snd_unit_auto == 0)
676cbebc90dSAlexander Motin 			snd_unit_auto = 1;
677e4e61333SAriff Abdullah 	}
678bba4862cSAriff Abdullah 
679e4e61333SAriff Abdullah 	return (0);
68033dbf14aSCameron Grant }
6817c438dbeSCameron Grant 
68267b1dce3SCameron Grant /************************************************************************/
68367b1dce3SCameron Grant 
684b611c801SAlexander Leidinger /**
685b611c801SAlexander Leidinger  * @brief	Handle OSSv4 SNDCTL_SYSINFO ioctl.
686b611c801SAlexander Leidinger  *
687b611c801SAlexander Leidinger  * @param si	Pointer to oss_sysinfo struct where information about the
688b611c801SAlexander Leidinger  * 		sound subsystem will be written/copied.
689b611c801SAlexander Leidinger  *
690b611c801SAlexander Leidinger  * This routine returns information about the sound system, such as the
691b611c801SAlexander Leidinger  * current OSS version, number of audio, MIDI, and mixer drivers, etc.
692b611c801SAlexander Leidinger  * Also includes a bitmask showing which of the above types of devices
693b611c801SAlexander Leidinger  * are open (busy).
694b611c801SAlexander Leidinger  *
695b611c801SAlexander Leidinger  * @note
696b611c801SAlexander Leidinger  * Calling threads must not hold any snddev_info or pcm_channel locks.
697b611c801SAlexander Leidinger  *
698b611c801SAlexander Leidinger  * @author	Ryan Beasley <ryanb@FreeBSD.org>
699b611c801SAlexander Leidinger  */
700b611c801SAlexander Leidinger void
701b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si)
702b611c801SAlexander Leidinger {
703b611c801SAlexander Leidinger 	static char si_product[] = "FreeBSD native OSS ABI";
704b611c801SAlexander Leidinger 	static char si_version[] = __XSTRING(__FreeBSD_version);
705c412d503SAlexander Motin 	static char si_license[] = "BSD";
706b611c801SAlexander Leidinger 	static int intnbits = sizeof(int) * 8;	/* Better suited as macro?
707b611c801SAlexander Leidinger 						   Must pester a C guru. */
708b611c801SAlexander Leidinger 
709b611c801SAlexander Leidinger 	struct snddev_info *d;
710b611c801SAlexander Leidinger 	struct pcm_channel *c;
711c597c557SChristos Margiolis 	int j, ncards;
712c597c557SChristos Margiolis 	size_t i;
713b611c801SAlexander Leidinger 
714b611c801SAlexander Leidinger 	ncards = 0;
715b611c801SAlexander Leidinger 
716b611c801SAlexander Leidinger 	strlcpy(si->product, si_product, sizeof(si->product));
717b611c801SAlexander Leidinger 	strlcpy(si->version, si_version, sizeof(si->version));
718b611c801SAlexander Leidinger 	si->versionnum = SOUND_VERSION;
719c412d503SAlexander Motin 	strlcpy(si->license, si_license, sizeof(si->license));
720b611c801SAlexander Leidinger 
721b611c801SAlexander Leidinger 	/*
722b611c801SAlexander Leidinger 	 * Iterate over PCM devices and their channels, gathering up data
723b611c801SAlexander Leidinger 	 * for the numaudios, ncards, and openedaudio fields.
724b611c801SAlexander Leidinger 	 */
725b611c801SAlexander Leidinger 	si->numaudios = 0;
726b611c801SAlexander Leidinger 	bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
727b611c801SAlexander Leidinger 
728b611c801SAlexander Leidinger 	j = 0;
729b611c801SAlexander Leidinger 
7309c271f79SAriff Abdullah 	for (i = 0; pcm_devclass != NULL &&
7319c271f79SAriff Abdullah 	    i < devclass_get_maxunit(pcm_devclass); i++) {
732b611c801SAlexander Leidinger 		d = devclass_get_softc(pcm_devclass, i);
733e4e61333SAriff Abdullah 		if (!PCM_REGISTERED(d))
734b611c801SAlexander Leidinger 			continue;
735b611c801SAlexander Leidinger 
736e4e61333SAriff Abdullah 		/* XXX Need Giant magic entry ??? */
737e4e61333SAriff Abdullah 
738b611c801SAlexander Leidinger 		/* See note in function's docblock */
73990da2b28SAriff Abdullah 		PCM_UNLOCKASSERT(d);
74090da2b28SAriff Abdullah 		PCM_LOCK(d);
741b611c801SAlexander Leidinger 
7424d2be7beSChristos Margiolis 		si->numaudios += PCM_CHANCOUNT(d);
743b611c801SAlexander Leidinger 		++ncards;
744b611c801SAlexander Leidinger 
745bba4862cSAriff Abdullah 		CHN_FOREACH(c, d, channels.pcm) {
74690da2b28SAriff Abdullah 			CHN_UNLOCKASSERT(c);
747b611c801SAlexander Leidinger 			CHN_LOCK(c);
748b611c801SAlexander Leidinger 			if (c->flags & CHN_F_BUSY)
749b611c801SAlexander Leidinger 				si->openedaudio[j / intnbits] |=
750b611c801SAlexander Leidinger 				    (1 << (j % intnbits));
751b611c801SAlexander Leidinger 			CHN_UNLOCK(c);
752b611c801SAlexander Leidinger 			j++;
753b611c801SAlexander Leidinger 		}
754b611c801SAlexander Leidinger 
75590da2b28SAriff Abdullah 		PCM_UNLOCK(d);
756b611c801SAlexander Leidinger 	}
757c412d503SAlexander Motin 	si->numaudioengines = si->numaudios;
758b611c801SAlexander Leidinger 
759b611c801SAlexander Leidinger 	si->numsynths = 0;	/* OSSv4 docs:  this field is obsolete */
760b611c801SAlexander Leidinger 	/**
761b611c801SAlexander Leidinger 	 * @todo	Collect num{midis,timers}.
762b611c801SAlexander Leidinger 	 *
763b611c801SAlexander Leidinger 	 * Need access to sound/midi/midi.c::midistat_lock in order
764b611c801SAlexander Leidinger 	 * to safely touch midi_devices and get a head count of, well,
765b611c801SAlexander Leidinger 	 * MIDI devices.  midistat_lock is a global static (i.e., local to
766b611c801SAlexander Leidinger 	 * midi.c), but midi_devices is a regular global; should the mutex
767b611c801SAlexander Leidinger 	 * be publicized, or is there another way to get this information?
768b611c801SAlexander Leidinger 	 *
769b611c801SAlexander Leidinger 	 * NB:	MIDI/sequencer stuff is currently on hold.
770b611c801SAlexander Leidinger 	 */
771b611c801SAlexander Leidinger 	si->nummidis = 0;
772b611c801SAlexander Leidinger 	si->numtimers = 0;
773b611c801SAlexander Leidinger 	si->nummixers = mixer_count;
774b611c801SAlexander Leidinger 	si->numcards = ncards;
775b611c801SAlexander Leidinger 		/* OSSv4 docs:	Intended only for test apps; API doesn't
776b611c801SAlexander Leidinger 		   really have much of a concept of cards.  Shouldn't be
777b611c801SAlexander Leidinger 		   used by applications. */
778b611c801SAlexander Leidinger 
779b611c801SAlexander Leidinger 	/**
780b611c801SAlexander Leidinger 	 * @todo	Fill in "busy devices" fields.
781b611c801SAlexander Leidinger 	 *
782b611c801SAlexander Leidinger 	 *  si->openedmidi = " MIDI devices
783b611c801SAlexander Leidinger 	 */
784b611c801SAlexander Leidinger 	bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
785b611c801SAlexander Leidinger 
786b611c801SAlexander Leidinger 	/*
787b611c801SAlexander Leidinger 	 * Si->filler is a reserved array, but according to docs each
788b611c801SAlexander Leidinger 	 * element should be set to -1.
789b611c801SAlexander Leidinger 	 */
790c597c557SChristos Margiolis 	for (i = 0; i < nitems(si->filler); i++)
791b611c801SAlexander Leidinger 		si->filler[i] = -1;
792b611c801SAlexander Leidinger }
793b611c801SAlexander Leidinger 
79452f6e09eSAlexander Motin int
79552f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si)
79652f6e09eSAlexander Motin {
79752f6e09eSAlexander Motin 	struct snddev_info *d;
79852f6e09eSAlexander Motin 	int i, ncards;
79952f6e09eSAlexander Motin 
80052f6e09eSAlexander Motin 	ncards = 0;
80152f6e09eSAlexander Motin 
80252f6e09eSAlexander Motin 	for (i = 0; pcm_devclass != NULL &&
80352f6e09eSAlexander Motin 	    i < devclass_get_maxunit(pcm_devclass); i++) {
80452f6e09eSAlexander Motin 		d = devclass_get_softc(pcm_devclass, i);
80552f6e09eSAlexander Motin 		if (!PCM_REGISTERED(d))
80652f6e09eSAlexander Motin 			continue;
80752f6e09eSAlexander Motin 
80852f6e09eSAlexander Motin 		if (ncards++ != si->card)
80952f6e09eSAlexander Motin 			continue;
81052f6e09eSAlexander Motin 
81190da2b28SAriff Abdullah 		PCM_UNLOCKASSERT(d);
81290da2b28SAriff Abdullah 		PCM_LOCK(d);
81352f6e09eSAlexander Motin 
81452f6e09eSAlexander Motin 		strlcpy(si->shortname, device_get_nameunit(d->dev),
81552f6e09eSAlexander Motin 		    sizeof(si->shortname));
81652f6e09eSAlexander Motin 		strlcpy(si->longname, device_get_desc(d->dev),
81752f6e09eSAlexander Motin 		    sizeof(si->longname));
81852f6e09eSAlexander Motin 		strlcpy(si->hw_info, d->status, sizeof(si->hw_info));
81952f6e09eSAlexander Motin 		si->intr_count = si->ack_count = 0;
82090da2b28SAriff Abdullah 
82190da2b28SAriff Abdullah 		PCM_UNLOCK(d);
82290da2b28SAriff Abdullah 
82352f6e09eSAlexander Motin 		return (0);
82452f6e09eSAlexander Motin 	}
82552f6e09eSAlexander Motin 	return (ENXIO);
82652f6e09eSAlexander Motin }
82752f6e09eSAlexander Motin 
828b611c801SAlexander Leidinger /************************************************************************/
829b611c801SAlexander Leidinger 
8300739ea1dSSeigo Tanimura static int
8310739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data)
8320739ea1dSSeigo Tanimura {
833b611c801SAlexander Leidinger 	int ret;
834b611c801SAlexander Leidinger 
8358109ec9dSJohn Baldwin 	ret = 0;
836b611c801SAlexander Leidinger 	switch (type) {
837b611c801SAlexander Leidinger 		case MOD_LOAD:
83813bebcd3SJohn Baldwin 			pcm_devclass = devclass_create("pcm");
839b611c801SAlexander Leidinger 			pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
840b611c801SAlexander Leidinger 			break;
841b611c801SAlexander Leidinger 		case MOD_UNLOAD:
842b611c801SAlexander Leidinger 			if (pcmsg_unrhdr != NULL) {
843b611c801SAlexander Leidinger 				delete_unrhdr(pcmsg_unrhdr);
844b611c801SAlexander Leidinger 				pcmsg_unrhdr = NULL;
845b611c801SAlexander Leidinger 			}
846b611c801SAlexander Leidinger 			break;
8475525b53dSAlexander Motin 		case MOD_SHUTDOWN:
8485525b53dSAlexander Motin 			break;
849b611c801SAlexander Leidinger 		default:
85090da2b28SAriff Abdullah 			ret = ENOTSUP;
851b611c801SAlexander Leidinger 	}
852b611c801SAlexander Leidinger 
853b611c801SAlexander Leidinger 	return ret;
8540739ea1dSSeigo Tanimura }
8550739ea1dSSeigo Tanimura 
8560739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL);
8570739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER);
858