xref: /freebsd/sys/dev/sound/pcm/sound.c (revision 4f854658c5d6ca98c822491991052574d948617d)
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.
9*4f854658SChristos Margiolis  * Copyright (c) 2024 The FreeBSD Foundation
10*4f854658SChristos Margiolis  *
11*4f854658SChristos Margiolis  * Portions of this software were developed by Christos Margiolis
12*4f854658SChristos 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>
44bba4862cSAriff Abdullah #include <dev/sound/version.h>
45b611c801SAlexander Leidinger #include <sys/limits.h>
467c438dbeSCameron Grant #include <sys/sysctl.h>
47285648f9SCameron Grant 
4867b1dce3SCameron Grant #include "feeder_if.h"
4967b1dce3SCameron Grant 
50d95502a8SCameron Grant devclass_t pcm_devclass;
5182db23e2SCameron Grant 
52b8a36395SCameron Grant int pcm_veto_load = 1;
53b8a36395SCameron Grant 
54f3685841SAriff Abdullah int snd_unit = -1;
55cbe7d6a3SCameron Grant 
56cbebc90dSAlexander Motin static int snd_unit_auto = -1;
57af3b2549SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN,
58838d3589SAriff Abdullah     &snd_unit_auto, 0, "assign default unit to a newly attached device");
59ad8612b9SAriff Abdullah 
60bba4862cSAriff Abdullah int snd_maxautovchans = 16;
61987e5972SCameron Grant 
627029da5cSPawel Biernacki SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
637029da5cSPawel Biernacki     "Sound driver");
6482db23e2SCameron Grant 
6532a0e5d5SHans Petter Selasky static void pcm_sysinit(device_t);
6632a0e5d5SHans Petter Selasky 
67bba4862cSAriff Abdullah /*
68bba4862cSAriff Abdullah  * XXX I've had enough with people not telling proper version/arch
69bba4862cSAriff Abdullah  *     while reporting problems, not after 387397913213th questions/requests.
70bba4862cSAriff Abdullah  */
71a9bdb5d3SAndriy Gapon static char snd_driver_version[] =
72bba4862cSAriff Abdullah     __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH;
73bba4862cSAriff Abdullah SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version,
7490da2b28SAriff Abdullah     0, "driver version/arch");
75bba4862cSAriff Abdullah 
76b611c801SAlexander Leidinger /**
77b611c801SAlexander Leidinger  * @brief Unit number allocator for syncgroup IDs
78b611c801SAlexander Leidinger  */
79b611c801SAlexander Leidinger struct unrhdr *pcmsg_unrhdr = NULL;
80b611c801SAlexander Leidinger 
8137209180SCameron Grant void *
822c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type)
8337209180SCameron Grant {
8437209180SCameron Grant 	struct mtx *m;
8537209180SCameron Grant 
86a163d034SWarner Losh 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
8712e524a2SDon Lewis 	mtx_init(m, desc, type, MTX_DEF);
8812e524a2SDon Lewis 	return m;
8912e524a2SDon Lewis }
9012e524a2SDon Lewis 
9137209180SCameron Grant void
9237209180SCameron Grant snd_mtxfree(void *m)
9337209180SCameron Grant {
9437209180SCameron Grant 	struct mtx *mtx = m;
9537209180SCameron Grant 
9637209180SCameron Grant 	mtx_destroy(mtx);
9737209180SCameron Grant 	free(mtx, M_DEVBUF);
9837209180SCameron Grant }
9937209180SCameron Grant 
10037209180SCameron Grant void
10137209180SCameron Grant snd_mtxassert(void *m)
10237209180SCameron Grant {
103f00f162aSCameron Grant #ifdef INVARIANTS
10437209180SCameron Grant 	struct mtx *mtx = m;
10537209180SCameron Grant 
10637209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
10737209180SCameron Grant #endif
10837209180SCameron Grant }
10937209180SCameron Grant 
11037209180SCameron Grant int
11137209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
11237209180SCameron Grant {
113e4e61333SAriff Abdullah 	struct snddev_info *d;
11490da2b28SAriff Abdullah 
11537209180SCameron Grant 	flags &= INTR_MPSAFE;
11646700f12SPeter Wemm 	flags |= INTR_TYPE_AV;
117e4e61333SAriff Abdullah 	d = device_get_softc(dev);
118e4e61333SAriff Abdullah 	if (d != NULL && (flags & INTR_MPSAFE))
119e4e61333SAriff Abdullah 		d->flags |= SD_F_MPSAFE;
120e4e61333SAriff Abdullah 
121775fcb6eSBaptiste Daroussin 	return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep);
12237209180SCameron Grant }
12337209180SCameron Grant 
12490da2b28SAriff Abdullah int
125bba4862cSAriff Abdullah pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
126bba4862cSAriff Abdullah {
127bba4862cSAriff Abdullah 	struct pcm_channel *c, *ch, *nch;
12890da2b28SAriff Abdullah 	struct pcmchan_caps *caps;
12990da2b28SAriff Abdullah 	int i, err, vcnt;
130bba4862cSAriff Abdullah 
131e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
132a1d444e1SAriff Abdullah 
133bba4862cSAriff Abdullah 	if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
134e4e61333SAriff Abdullah 	    (direction == PCMDIR_REC && d->reccount < 1))
135e4e61333SAriff Abdullah 		return (ENODEV);
136a580b31aSAriff Abdullah 
137e4e61333SAriff Abdullah 	if (!(d->flags & SD_F_AUTOVCHAN))
138e4e61333SAriff Abdullah 		return (EINVAL);
139a1d444e1SAriff Abdullah 
140e4e61333SAriff Abdullah 	if (newcnt < 0 || newcnt > SND_MAXVCHANS)
141e4e61333SAriff Abdullah 		return (E2BIG);
142a1d444e1SAriff Abdullah 
143bba4862cSAriff Abdullah 	if (direction == PCMDIR_PLAY)
144bba4862cSAriff Abdullah 		vcnt = d->pvchancount;
145bba4862cSAriff Abdullah 	else if (direction == PCMDIR_REC)
146bba4862cSAriff Abdullah 		vcnt = d->rvchancount;
147e4e61333SAriff Abdullah 	else
148e4e61333SAriff Abdullah 		return (EINVAL);
149a1d444e1SAriff Abdullah 
150a1d444e1SAriff Abdullah 	if (newcnt > vcnt) {
151bba4862cSAriff Abdullah 		KASSERT(num == -1 ||
152bba4862cSAriff Abdullah 		    (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt),
153bba4862cSAriff Abdullah 		    ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
154bba4862cSAriff Abdullah 		    num, newcnt, vcnt));
155a1d444e1SAriff Abdullah 		/* add new vchans - find a parent channel first */
156e4e61333SAriff Abdullah 		ch = NULL;
157bba4862cSAriff Abdullah 		CHN_FOREACH(c, d, channels.pcm) {
158a1d444e1SAriff Abdullah 			CHN_LOCK(c);
159bba4862cSAriff Abdullah 			if (c->direction == direction &&
160bba4862cSAriff Abdullah 			    ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
16190da2b28SAriff Abdullah 			    c->refcount < 1 &&
162e4e61333SAriff Abdullah 			    !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
16390da2b28SAriff Abdullah 				/*
16490da2b28SAriff Abdullah 				 * Reuse hw channel with vchans already
16590da2b28SAriff Abdullah 				 * created.
16690da2b28SAriff Abdullah 				 */
16790da2b28SAriff Abdullah 				if (c->flags & CHN_F_HAS_VCHAN) {
168e4e61333SAriff Abdullah 					ch = c;
169e4e61333SAriff Abdullah 					break;
170e4e61333SAriff Abdullah 				}
17190da2b28SAriff Abdullah 				/*
17290da2b28SAriff Abdullah 				 * No vchans ever created, look for
17390da2b28SAriff Abdullah 				 * channels with supported formats.
17490da2b28SAriff Abdullah 				 */
17590da2b28SAriff Abdullah 				caps = chn_getcaps(c);
17690da2b28SAriff Abdullah 				if (caps == NULL) {
17790da2b28SAriff Abdullah 					CHN_UNLOCK(c);
17890da2b28SAriff Abdullah 					continue;
17990da2b28SAriff Abdullah 				}
18090da2b28SAriff Abdullah 				for (i = 0; caps->fmtlist[i] != 0; i++) {
18190da2b28SAriff Abdullah 					if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
18290da2b28SAriff Abdullah 						break;
18390da2b28SAriff Abdullah 				}
18490da2b28SAriff Abdullah 				if (caps->fmtlist[i] != 0) {
18590da2b28SAriff Abdullah 					ch = c;
18690da2b28SAriff Abdullah 				    	break;
18790da2b28SAriff Abdullah 				}
18890da2b28SAriff Abdullah 			}
189a1d444e1SAriff Abdullah 			CHN_UNLOCK(c);
190a1d444e1SAriff Abdullah 		}
191e4e61333SAriff Abdullah 		if (ch == NULL)
192e4e61333SAriff Abdullah 			return (EBUSY);
193e4e61333SAriff Abdullah 		ch->flags |= CHN_F_BUSY;
194e4e61333SAriff Abdullah 		err = 0;
195a1d444e1SAriff Abdullah 		while (err == 0 && newcnt > vcnt) {
196e4e61333SAriff Abdullah 			err = vchan_create(ch, num);
197bba4862cSAriff Abdullah 			if (err == 0)
198a1d444e1SAriff Abdullah 				vcnt++;
199bba4862cSAriff Abdullah 			else if (err == E2BIG && newcnt > vcnt)
200bba4862cSAriff Abdullah 				device_printf(d->dev,
201bba4862cSAriff Abdullah 				    "%s: err=%d Maximum channel reached.\n",
202bba4862cSAriff Abdullah 				    __func__, err);
203a1d444e1SAriff Abdullah 		}
204a1d444e1SAriff Abdullah 		if (vcnt == 0)
205e4e61333SAriff Abdullah 			ch->flags &= ~CHN_F_BUSY;
206e4e61333SAriff Abdullah 		CHN_UNLOCK(ch);
207e4e61333SAriff Abdullah 		if (err != 0)
208e4e61333SAriff Abdullah 			return (err);
209a1d444e1SAriff Abdullah 	} else if (newcnt < vcnt) {
210bba4862cSAriff Abdullah 		KASSERT(num == -1,
211bba4862cSAriff Abdullah 		    ("bogus vchan_destroy() request num=%d", num));
212bba4862cSAriff Abdullah 		CHN_FOREACH(c, d, channels.pcm) {
213a1d444e1SAriff Abdullah 			CHN_LOCK(c);
214bba4862cSAriff Abdullah 			if (c->direction != direction ||
215bba4862cSAriff Abdullah 			    CHN_EMPTY(c, children) ||
216bba4862cSAriff Abdullah 			    !(c->flags & CHN_F_HAS_VCHAN)) {
217a1d444e1SAriff Abdullah 				CHN_UNLOCK(c);
218bba4862cSAriff Abdullah 				continue;
219a1d444e1SAriff Abdullah 			}
220bba4862cSAriff Abdullah 			CHN_FOREACH_SAFE(ch, c, nch, children) {
221bba4862cSAriff Abdullah 				CHN_LOCK(ch);
22290da2b28SAriff Abdullah 				if (vcnt == 1 && c->refcount > 0) {
223bba4862cSAriff Abdullah 					CHN_UNLOCK(ch);
22490da2b28SAriff Abdullah 					break;
22590da2b28SAriff Abdullah 				}
22690da2b28SAriff Abdullah 				if (!(ch->flags & CHN_F_BUSY) &&
22790da2b28SAriff Abdullah 				    ch->refcount < 1) {
228bba4862cSAriff Abdullah 					err = vchan_destroy(ch);
229a1d444e1SAriff Abdullah 					if (err == 0)
230a1d444e1SAriff Abdullah 						vcnt--;
231bba4862cSAriff Abdullah 				} else
232bba4862cSAriff Abdullah 					CHN_UNLOCK(ch);
233e4e61333SAriff Abdullah 				if (vcnt == newcnt)
234bba4862cSAriff Abdullah 					break;
235a1d444e1SAriff Abdullah 			}
236bba4862cSAriff Abdullah 			CHN_UNLOCK(c);
237bba4862cSAriff Abdullah 			break;
238bba4862cSAriff Abdullah 		}
239bba4862cSAriff Abdullah 	}
240a1d444e1SAriff Abdullah 
241e4e61333SAriff Abdullah 	return (0);
242a1d444e1SAriff Abdullah }
243a1d444e1SAriff Abdullah 
2443fdb3676SAriff Abdullah /* return error status and a locked channel */
2453fdb3676SAriff Abdullah int
2463fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
24790da2b28SAriff Abdullah     pid_t pid, char *comm, int devunit)
248285648f9SCameron Grant {
249285648f9SCameron Grant 	struct pcm_channel *c;
25090da2b28SAriff Abdullah 	int err, vchancount, vchan_num;
251bba4862cSAriff Abdullah 
252bba4862cSAriff Abdullah 	KASSERT(d != NULL && ch != NULL && (devunit == -1 ||
253bba4862cSAriff Abdullah 	    !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) &&
254bba4862cSAriff Abdullah 	    (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
255e4e61333SAriff Abdullah 	    ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d",
256bba4862cSAriff Abdullah 	    __func__, d, ch, direction, pid, devunit));
257e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
258bba4862cSAriff Abdullah 
259bba4862cSAriff Abdullah 	/* Double check again. */
260bba4862cSAriff Abdullah 	if (devunit != -1) {
261bba4862cSAriff Abdullah 		switch (snd_unit2d(devunit)) {
262bba4862cSAriff Abdullah 		case SND_DEV_DSPHW_PLAY:
263bba4862cSAriff Abdullah 		case SND_DEV_DSPHW_VPLAY:
264bba4862cSAriff Abdullah 			if (direction != PCMDIR_PLAY)
26590da2b28SAriff Abdullah 				return (ENOTSUP);
266bba4862cSAriff Abdullah 			break;
267bba4862cSAriff Abdullah 		case SND_DEV_DSPHW_REC:
268bba4862cSAriff Abdullah 		case SND_DEV_DSPHW_VREC:
269bba4862cSAriff Abdullah 			if (direction != PCMDIR_REC)
27090da2b28SAriff Abdullah 				return (ENOTSUP);
271bba4862cSAriff Abdullah 			break;
272bba4862cSAriff Abdullah 		default:
273bba4862cSAriff Abdullah 			if (!(direction == PCMDIR_PLAY ||
274bba4862cSAriff Abdullah 			    direction == PCMDIR_REC))
27590da2b28SAriff Abdullah 				return (ENOTSUP);
276bba4862cSAriff Abdullah 			break;
277bba4862cSAriff Abdullah 		}
278bba4862cSAriff Abdullah 	}
279285648f9SCameron Grant 
28090da2b28SAriff Abdullah 	*ch = NULL;
28190da2b28SAriff Abdullah 	vchan_num = 0;
28290da2b28SAriff Abdullah 	vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount :
28390da2b28SAriff Abdullah 	    d->rvchancount;
28490da2b28SAriff Abdullah 
2853fdb3676SAriff Abdullah retry_chnalloc:
28690da2b28SAriff Abdullah 	err = ENOTSUP;
287f637a36cSCameron Grant 	/* scan for a free channel */
288bba4862cSAriff Abdullah 	CHN_FOREACH(c, d, channels.pcm) {
28949c5e6e2SCameron Grant 		CHN_LOCK(c);
29090da2b28SAriff Abdullah 		if (devunit == -1 && c->direction == direction &&
29190da2b28SAriff Abdullah 		    (c->flags & CHN_F_VIRTUAL)) {
29290da2b28SAriff Abdullah 			if (vchancount < snd_maxautovchans &&
29390da2b28SAriff Abdullah 			    vchan_num < CHN_CHAN(c)) {
29490da2b28SAriff Abdullah 			    	CHN_UNLOCK(c);
29590da2b28SAriff Abdullah 				goto vchan_alloc;
29690da2b28SAriff Abdullah 			}
29790da2b28SAriff Abdullah 			vchan_num++;
29890da2b28SAriff Abdullah 		}
299bba4862cSAriff Abdullah 		if (c->direction == direction && !(c->flags & CHN_F_BUSY) &&
300bba4862cSAriff Abdullah 		    (devunit == -1 || devunit == -2 || c->unit == devunit)) {
301285648f9SCameron Grant 			c->flags |= CHN_F_BUSY;
302b8f0d9e0SCameron Grant 			c->pid = pid;
30390da2b28SAriff Abdullah 			strlcpy(c->comm, (comm != NULL) ? comm :
30490da2b28SAriff Abdullah 			    CHN_COMM_UNKNOWN, sizeof(c->comm));
3053fdb3676SAriff Abdullah 			*ch = c;
306bba4862cSAriff Abdullah 			return (0);
307bba4862cSAriff Abdullah 		} else if (c->unit == devunit) {
3083fdb3676SAriff Abdullah 			if (c->direction != direction)
30990da2b28SAriff Abdullah 				err = ENOTSUP;
3103fdb3676SAriff Abdullah 			else if (c->flags & CHN_F_BUSY)
3113fdb3676SAriff Abdullah 				err = EBUSY;
3123fdb3676SAriff Abdullah 			else
3133fdb3676SAriff Abdullah 				err = EINVAL;
3143fdb3676SAriff Abdullah 			CHN_UNLOCK(c);
315bba4862cSAriff Abdullah 			return (err);
316bba4862cSAriff Abdullah 		} else if ((devunit == -1 || devunit == -2) &&
317bba4862cSAriff Abdullah 		    c->direction == direction && (c->flags & CHN_F_BUSY))
318a1d444e1SAriff Abdullah 			err = EBUSY;
31949c5e6e2SCameron Grant 		CHN_UNLOCK(c);
320285648f9SCameron Grant 	}
321f637a36cSCameron Grant 
322e4e61333SAriff Abdullah 	if (devunit == -2)
323e4e61333SAriff Abdullah 		return (err);
324e4e61333SAriff Abdullah 
32590da2b28SAriff Abdullah vchan_alloc:
326f637a36cSCameron Grant 	/* no channel available */
327e4e61333SAriff Abdullah 	if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY ||
328e4e61333SAriff Abdullah 	    snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) {
329bba4862cSAriff Abdullah 		if (!(vchancount > 0 && vchancount < snd_maxautovchans) &&
330bba4862cSAriff Abdullah 		    (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans))
331bba4862cSAriff Abdullah 			return (err);
332bba4862cSAriff Abdullah 		err = pcm_setvchans(d, direction, vchancount + 1,
333bba4862cSAriff Abdullah 		    (devunit == -1) ? -1 : snd_unit2c(devunit));
334a1d444e1SAriff Abdullah 		if (err == 0) {
335bba4862cSAriff Abdullah 			if (devunit == -1)
336bba4862cSAriff Abdullah 				devunit = -2;
3373fdb3676SAriff Abdullah 			goto retry_chnalloc;
338f637a36cSCameron Grant 		}
339f637a36cSCameron Grant 	}
340f637a36cSCameron Grant 
341bba4862cSAriff Abdullah 	return (err);
342285648f9SCameron Grant }
343285648f9SCameron Grant 
344b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
345285648f9SCameron Grant int
346b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
347285648f9SCameron Grant {
348e4e61333SAriff Abdullah 	PCM_BUSYASSERT(c->parentsnddev);
349b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
350e4e61333SAriff Abdullah 
351285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
352b8f0d9e0SCameron Grant 	c->pid = -1;
35390da2b28SAriff Abdullah 	strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm));
35449c5e6e2SCameron Grant 	CHN_UNLOCK(c);
355e4e61333SAriff Abdullah 
356e4e61333SAriff Abdullah 	return (0);
357285648f9SCameron Grant }
358285648f9SCameron Grant 
359285648f9SCameron Grant int
360285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
361285648f9SCameron Grant {
362e4e61333SAriff Abdullah 	PCM_BUSYASSERT(c->parentsnddev);
363b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
364e4e61333SAriff Abdullah 
365285648f9SCameron Grant 	c->refcount += ref;
366e4e61333SAriff Abdullah 
367e4e61333SAriff Abdullah 	return (c->refcount);
368285648f9SCameron Grant }
369285648f9SCameron Grant 
37067b1dce3SCameron Grant static void
37167b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num)
37267b1dce3SCameron Grant {
373e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
374e4e61333SAriff Abdullah 
375bba4862cSAriff Abdullah 	if (num < 0)
376bba4862cSAriff Abdullah 		return;
377bba4862cSAriff Abdullah 
378bba4862cSAriff Abdullah 	if (num >= 0 && d->pvchancount > num)
379bba4862cSAriff Abdullah 		(void)pcm_setvchans(d, PCMDIR_PLAY, num, -1);
380bba4862cSAriff Abdullah 	else if (num > 0 && d->pvchancount == 0)
381bba4862cSAriff Abdullah 		(void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1);
382bba4862cSAriff Abdullah 
383bba4862cSAriff Abdullah 	if (num >= 0 && d->rvchancount > num)
384bba4862cSAriff Abdullah 		(void)pcm_setvchans(d, PCMDIR_REC, num, -1);
385bba4862cSAriff Abdullah 	else if (num > 0 && d->rvchancount == 0)
386bba4862cSAriff Abdullah 		(void)pcm_setvchans(d, PCMDIR_REC, 1, -1);
38767b1dce3SCameron Grant }
38867b1dce3SCameron Grant 
38933dbf14aSCameron Grant static int
390851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
39133dbf14aSCameron Grant {
392b8f0d9e0SCameron Grant 	struct snddev_info *d;
39333dbf14aSCameron Grant 	int error, unit;
39433dbf14aSCameron Grant 
39533dbf14aSCameron Grant 	unit = snd_unit;
396041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &unit, 0, req);
39733dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
398b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
399e4e61333SAriff Abdullah 		if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
400b8f0d9e0SCameron Grant 			return EINVAL;
40133dbf14aSCameron Grant 		snd_unit = unit;
402cbebc90dSAlexander Motin 		snd_unit_auto = 0;
40333dbf14aSCameron Grant 	}
40433dbf14aSCameron Grant 	return (error);
40533dbf14aSCameron Grant }
406851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */
407b7d6c6b5SEitan Adler SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit,
4087029da5cSPawel Biernacki     CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0,
4097029da5cSPawel Biernacki     sizeof(int), sysctl_hw_snd_default_unit, "I",
410b7d6c6b5SEitan Adler     "default sound device");
411987e5972SCameron Grant 
412cd9766c5SCameron Grant static int
41367b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
414cd9766c5SCameron Grant {
41567b1dce3SCameron Grant 	struct snddev_info *d;
41667b1dce3SCameron Grant 	int i, v, error;
417cd9766c5SCameron Grant 
41867b1dce3SCameron Grant 	v = snd_maxautovchans;
419041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &v, 0, req);
420cd9766c5SCameron Grant 	if (error == 0 && req->newptr != NULL) {
421bba4862cSAriff Abdullah 		if (v < 0)
422bba4862cSAriff Abdullah 			v = 0;
423bba4862cSAriff Abdullah 		if (v > SND_MAXVCHANS)
424bba4862cSAriff Abdullah 			v = SND_MAXVCHANS;
425bba4862cSAriff Abdullah 		snd_maxautovchans = v;
4269c271f79SAriff Abdullah 		for (i = 0; pcm_devclass != NULL &&
4279c271f79SAriff Abdullah 		    i < devclass_get_maxunit(pcm_devclass); i++) {
42867b1dce3SCameron Grant 			d = devclass_get_softc(pcm_devclass, i);
429e4e61333SAriff Abdullah 			if (!PCM_REGISTERED(d))
43067b1dce3SCameron Grant 				continue;
431e4e61333SAriff Abdullah 			PCM_ACQUIRE_QUICK(d);
43267b1dce3SCameron Grant 			pcm_setmaxautovchans(d, v);
433e4e61333SAriff Abdullah 			PCM_RELEASE_QUICK(d);
43467b1dce3SCameron Grant 		}
43567b1dce3SCameron Grant 	}
436cd9766c5SCameron Grant 	return (error);
437cd9766c5SCameron Grant }
4387029da5cSPawel Biernacki SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans,
4397029da5cSPawel Biernacki     CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
4407029da5cSPawel Biernacki     sysctl_hw_snd_maxautovchans, "I",
4417029da5cSPawel Biernacki     "maximum virtual channel");
442f637a36cSCameron Grant 
443285648f9SCameron Grant struct pcm_channel *
444bba4862cSAriff Abdullah pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo)
445987e5972SCameron Grant {
446bba4862cSAriff Abdullah 	struct pcm_channel *ch;
447bba4862cSAriff Abdullah 	int direction, err, rpnum, *pnum, max;
448bba4862cSAriff Abdullah 	int udc, device, chan;
449bba4862cSAriff Abdullah 	char *dirs, *devname, buf[CHN_NAMELEN];
450bba4862cSAriff Abdullah 
451e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
45290da2b28SAriff Abdullah 	PCM_LOCKASSERT(d);
453bba4862cSAriff Abdullah 	KASSERT(num >= -1, ("invalid num=%d", num));
454bba4862cSAriff Abdullah 
455285648f9SCameron Grant 	switch (dir) {
456285648f9SCameron Grant 	case PCMDIR_PLAY:
457285648f9SCameron Grant 		dirs = "play";
458a3193a9cSDon Lewis 		direction = PCMDIR_PLAY;
459a67fe5c1SCameron Grant 		pnum = &d->playcount;
460bba4862cSAriff Abdullah 		device = SND_DEV_DSPHW_PLAY;
461bba4862cSAriff Abdullah 		max = SND_MAXHWCHAN;
462285648f9SCameron Grant 		break;
463bba4862cSAriff Abdullah 	case PCMDIR_PLAY_VIRTUAL:
464bba4862cSAriff Abdullah 		dirs = "virtual";
465bba4862cSAriff Abdullah 		direction = PCMDIR_PLAY;
466bba4862cSAriff Abdullah 		pnum = &d->pvchancount;
467bba4862cSAriff Abdullah 		device = SND_DEV_DSPHW_VPLAY;
468bba4862cSAriff Abdullah 		max = SND_MAXVCHANS;
469bba4862cSAriff Abdullah 		break;
470285648f9SCameron Grant 	case PCMDIR_REC:
471285648f9SCameron Grant 		dirs = "record";
472a3193a9cSDon Lewis 		direction = PCMDIR_REC;
473a67fe5c1SCameron Grant 		pnum = &d->reccount;
474bba4862cSAriff Abdullah 		device = SND_DEV_DSPHW_REC;
475bba4862cSAriff Abdullah 		max = SND_MAXHWCHAN;
476285648f9SCameron Grant 		break;
477bba4862cSAriff Abdullah 	case PCMDIR_REC_VIRTUAL:
478285648f9SCameron Grant 		dirs = "virtual";
479bba4862cSAriff Abdullah 		direction = PCMDIR_REC;
480bba4862cSAriff Abdullah 		pnum = &d->rvchancount;
481bba4862cSAriff Abdullah 		device = SND_DEV_DSPHW_VREC;
482bba4862cSAriff Abdullah 		max = SND_MAXVCHANS;
483285648f9SCameron Grant 		break;
484285648f9SCameron Grant 	default:
485e4e61333SAriff Abdullah 		return (NULL);
4869c326820SCameron Grant 	}
487285648f9SCameron Grant 
488bba4862cSAriff Abdullah 	chan = (num == -1) ? 0 : num;
489285648f9SCameron Grant 
490e4e61333SAriff Abdullah 	if (*pnum >= max || chan >= max)
491e4e61333SAriff Abdullah 		return (NULL);
492bba4862cSAriff Abdullah 
4933fdb3676SAriff Abdullah 	rpnum = 0;
494bba4862cSAriff Abdullah 
495bba4862cSAriff Abdullah 	CHN_FOREACH(ch, d, channels.pcm) {
496bba4862cSAriff Abdullah 		if (CHN_DEV(ch) != device)
4973fdb3676SAriff Abdullah 			continue;
498bba4862cSAriff Abdullah 		if (chan == CHN_CHAN(ch)) {
499bba4862cSAriff Abdullah 			if (num != -1) {
5003fdb3676SAriff Abdullah 				device_printf(d->dev,
501bba4862cSAriff Abdullah 				    "channel num=%d allocated!\n", chan);
502e4e61333SAriff Abdullah 				return (NULL);
503bba4862cSAriff Abdullah 			}
504bba4862cSAriff Abdullah 			chan++;
505bba4862cSAriff Abdullah 			if (chan >= max) {
506bba4862cSAriff Abdullah 				device_printf(d->dev,
507bba4862cSAriff Abdullah 				    "chan=%d > %d\n", chan, max);
508e4e61333SAriff Abdullah 				return (NULL);
509bba4862cSAriff Abdullah 			}
5103fdb3676SAriff Abdullah 		}
5113fdb3676SAriff Abdullah 		rpnum++;
5123fdb3676SAriff Abdullah 	}
513bba4862cSAriff Abdullah 
5143fdb3676SAriff Abdullah 	if (*pnum != rpnum) {
5153fdb3676SAriff Abdullah 		device_printf(d->dev,
516bba4862cSAriff Abdullah 		    "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n",
5173fdb3676SAriff Abdullah 		    __func__, dirs, *pnum, rpnum);
518e4e61333SAriff Abdullah 		return (NULL);
5193fdb3676SAriff Abdullah 	}
520a67fe5c1SCameron Grant 
521bba4862cSAriff Abdullah 	udc = snd_mkunit(device_get_unit(d->dev), device, chan);
522bba4862cSAriff Abdullah 	devname = dsp_unit2name(buf, sizeof(buf), udc);
523bba4862cSAriff Abdullah 
524bba4862cSAriff Abdullah 	if (devname == NULL) {
525bba4862cSAriff Abdullah 		device_printf(d->dev,
526bba4862cSAriff Abdullah 		    "Failed to query device name udc=0x%08x\n", udc);
527e4e61333SAriff Abdullah 		return (NULL);
528bba4862cSAriff Abdullah 	}
529bba4862cSAriff Abdullah 
53090da2b28SAriff Abdullah 	PCM_UNLOCK(d);
531bba4862cSAriff Abdullah 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
532bba4862cSAriff Abdullah 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
533bba4862cSAriff Abdullah 	ch->unit = udc;
534285648f9SCameron Grant 	ch->pid = -1;
53590da2b28SAriff Abdullah 	strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm));
536285648f9SCameron Grant 	ch->parentsnddev = d;
537285648f9SCameron Grant 	ch->parentchannel = parent;
538436c9b65SScott Long 	ch->dev = d->dev;
539bba4862cSAriff Abdullah 	ch->trigger = PCMTRIG_STOP;
540bba4862cSAriff Abdullah 	snprintf(ch->name, sizeof(ch->name), "%s:%s:%s",
541bba4862cSAriff Abdullah 	    device_get_nameunit(ch->dev), dirs, devname);
542285648f9SCameron Grant 
543a3193a9cSDon Lewis 	err = chn_init(ch, devinfo, dir, direction);
54490da2b28SAriff Abdullah 	PCM_LOCK(d);
5450f55ac6cSCameron Grant 	if (err) {
546bba4862cSAriff Abdullah 		device_printf(d->dev, "chn_init(%s) failed: err = %d\n",
547bba4862cSAriff Abdullah 		    ch->name, err);
548285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
549285648f9SCameron Grant 		free(ch, M_DEVBUF);
550e4e61333SAriff Abdullah 		return (NULL);
551bbb5bf3dSCameron Grant 	}
552285648f9SCameron Grant 
553e4e61333SAriff Abdullah 	return (ch);
554285648f9SCameron Grant }
555285648f9SCameron Grant 
556285648f9SCameron Grant int
557285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
558285648f9SCameron Grant {
559cf8e3ea2SMateusz Guzik 	struct snddev_info *d __diagused;
560285648f9SCameron Grant 	int err;
561285648f9SCameron Grant 
562a67fe5c1SCameron Grant 	d = ch->parentsnddev;
563e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
564e4e61333SAriff Abdullah 
565285648f9SCameron Grant 	err = chn_kill(ch);
566285648f9SCameron Grant 	if (err) {
567e4e61333SAriff Abdullah 		device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n",
568e4e61333SAriff Abdullah 		    ch->name, err);
569e4e61333SAriff Abdullah 		return (err);
570285648f9SCameron Grant 	}
571285648f9SCameron Grant 
572285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
573285648f9SCameron Grant 	free(ch, M_DEVBUF);
574285648f9SCameron Grant 
575e4e61333SAriff Abdullah 	return (0);
576285648f9SCameron Grant }
577285648f9SCameron Grant 
578285648f9SCameron Grant int
5795ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
580285648f9SCameron Grant {
581e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
58290da2b28SAriff Abdullah 	PCM_LOCKASSERT(d);
583bba4862cSAriff Abdullah 	KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
584bba4862cSAriff Abdullah 	    ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
585285648f9SCameron Grant 
58690da2b28SAriff Abdullah 	CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm);
5873fdb3676SAriff Abdullah 
588e4e61333SAriff Abdullah 	switch (CHN_DEV(ch)) {
589e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_PLAY:
590e4e61333SAriff Abdullah 		d->playcount++;
591e4e61333SAriff Abdullah 		break;
592e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_VPLAY:
593e4e61333SAriff Abdullah 		d->pvchancount++;
594e4e61333SAriff Abdullah 		break;
595e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_REC:
596e4e61333SAriff Abdullah 		d->reccount++;
597e4e61333SAriff Abdullah 		break;
598e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_VREC:
599e4e61333SAriff Abdullah 		d->rvchancount++;
600e4e61333SAriff Abdullah 		break;
601e4e61333SAriff Abdullah 	default:
602e4e61333SAriff Abdullah 		break;
603e4e61333SAriff Abdullah 	}
604b8f0d9e0SCameron Grant 
605e4e61333SAriff Abdullah 	d->devcount++;
606e4e61333SAriff Abdullah 
607e4e61333SAriff Abdullah 	return (0);
60833dbf14aSCameron Grant }
60933dbf14aSCameron Grant 
610285648f9SCameron Grant int
6115ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
61233dbf14aSCameron Grant {
613bba4862cSAriff Abdullah 	struct pcm_channel *tmp;
61433dbf14aSCameron Grant 
615e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
61690da2b28SAriff Abdullah 	PCM_LOCKASSERT(d);
617e4e61333SAriff Abdullah 
618bba4862cSAriff Abdullah 	tmp = NULL;
619a527dbc7SCameron Grant 
620bba4862cSAriff Abdullah 	CHN_FOREACH(tmp, d, channels.pcm) {
621bba4862cSAriff Abdullah 		if (tmp == ch)
622bba4862cSAriff Abdullah 			break;
62333dbf14aSCameron Grant 	}
624bba4862cSAriff Abdullah 
625bba4862cSAriff Abdullah 	if (tmp != ch)
626e4e61333SAriff Abdullah 		return (EINVAL);
62767beb5a5SCameron Grant 
628bba4862cSAriff Abdullah 	CHN_REMOVE(d, ch, channels.pcm);
629e4e61333SAriff Abdullah 
630bba4862cSAriff Abdullah 	switch (CHN_DEV(ch)) {
631bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_PLAY:
63267beb5a5SCameron Grant 		d->playcount--;
633bba4862cSAriff Abdullah 		break;
634bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_VPLAY:
635bba4862cSAriff Abdullah 		d->pvchancount--;
636bba4862cSAriff Abdullah 		break;
637bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_REC:
638bba4862cSAriff Abdullah 		d->reccount--;
639bba4862cSAriff Abdullah 		break;
640bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_VREC:
641bba4862cSAriff Abdullah 		d->rvchancount--;
642bba4862cSAriff Abdullah 		break;
643bba4862cSAriff Abdullah 	default:
644bba4862cSAriff Abdullah 		break;
645bba4862cSAriff Abdullah 	}
646285648f9SCameron Grant 
647e4e61333SAriff Abdullah 	d->devcount--;
648e4e61333SAriff Abdullah 
649e4e61333SAriff Abdullah 	return (0);
650987e5972SCameron Grant }
651987e5972SCameron Grant 
652987e5972SCameron Grant int
653285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
654285648f9SCameron Grant {
655285648f9SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
65667b1dce3SCameron Grant 	struct pcm_channel *ch;
65767b1dce3SCameron Grant 	int err;
658285648f9SCameron Grant 
659e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
660e4e61333SAriff Abdullah 
66190da2b28SAriff Abdullah 	PCM_LOCK(d);
662bba4862cSAriff Abdullah 	ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo);
663285648f9SCameron Grant 	if (!ch) {
664e4e61333SAriff Abdullah 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n",
665e4e61333SAriff Abdullah 		    cls->name, dir, devinfo);
66690da2b28SAriff Abdullah 		PCM_UNLOCK(d);
667e4e61333SAriff Abdullah 		return (ENODEV);
668285648f9SCameron Grant 	}
669cd9766c5SCameron Grant 
6705ee30e27SMathew Kanner 	err = pcm_chn_add(d, ch);
67190da2b28SAriff Abdullah 	PCM_UNLOCK(d);
672285648f9SCameron Grant 	if (err) {
673e4e61333SAriff Abdullah 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n",
674e4e61333SAriff Abdullah 		    ch->name, err);
675285648f9SCameron Grant 		pcm_chn_destroy(ch);
676cd9766c5SCameron Grant 	}
677cd9766c5SCameron Grant 
678e4e61333SAriff Abdullah 	return (err);
679285648f9SCameron Grant }
680285648f9SCameron Grant 
681285648f9SCameron Grant static int
682285648f9SCameron Grant pcm_killchan(device_t dev)
683285648f9SCameron Grant {
684285648f9SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
685e33bee07SOlivier Houchard 	struct pcm_channel *ch;
686e4e61333SAriff Abdullah 	int error;
687e4e61333SAriff Abdullah 
688e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
689285648f9SCameron Grant 
690bba4862cSAriff Abdullah 	ch = CHN_FIRST(d, channels.pcm);
691285648f9SCameron Grant 
69290da2b28SAriff Abdullah 	PCM_LOCK(d);
693bba4862cSAriff Abdullah 	error = pcm_chn_remove(d, ch);
69490da2b28SAriff Abdullah 	PCM_UNLOCK(d);
695e33bee07SOlivier Houchard 	if (error)
696e33bee07SOlivier Houchard 		return (error);
697e33bee07SOlivier Houchard 	return (pcm_chn_destroy(ch));
698285648f9SCameron Grant }
699285648f9SCameron Grant 
700cbebc90dSAlexander Motin static int
701cbebc90dSAlexander Motin pcm_best_unit(int old)
702cbebc90dSAlexander Motin {
703cbebc90dSAlexander Motin 	struct snddev_info *d;
704cbebc90dSAlexander Motin 	int i, best, bestprio, prio;
705cbebc90dSAlexander Motin 
706cbebc90dSAlexander Motin 	best = -1;
707cbebc90dSAlexander Motin 	bestprio = -100;
708cbebc90dSAlexander Motin 	for (i = 0; pcm_devclass != NULL &&
709cbebc90dSAlexander Motin 	    i < devclass_get_maxunit(pcm_devclass); i++) {
710cbebc90dSAlexander Motin 		d = devclass_get_softc(pcm_devclass, i);
711cbebc90dSAlexander Motin 		if (!PCM_REGISTERED(d))
712cbebc90dSAlexander Motin 			continue;
713cbebc90dSAlexander Motin 		prio = 0;
714cbebc90dSAlexander Motin 		if (d->playcount == 0)
715cbebc90dSAlexander Motin 			prio -= 10;
716cbebc90dSAlexander Motin 		if (d->reccount == 0)
717cbebc90dSAlexander Motin 			prio -= 2;
718cbebc90dSAlexander Motin 		if (prio > bestprio || (prio == bestprio && i == old)) {
719cbebc90dSAlexander Motin 			best = i;
720cbebc90dSAlexander Motin 			bestprio = prio;
721cbebc90dSAlexander Motin 		}
722cbebc90dSAlexander Motin 	}
723cbebc90dSAlexander Motin 	return (best);
724cbebc90dSAlexander Motin }
725cbebc90dSAlexander Motin 
726285648f9SCameron Grant int
727987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
728987e5972SCameron Grant {
72966ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
73049c5e6e2SCameron Grant 
73132a0e5d5SHans Petter Selasky 	/* should only be called once */
73232a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_REGISTERED)
73332a0e5d5SHans Petter Selasky 		return (EINVAL);
73432a0e5d5SHans Petter Selasky 
735e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
736bba4862cSAriff Abdullah 
737e4e61333SAriff Abdullah 	if (d->playcount == 0 || d->reccount == 0)
738e4e61333SAriff Abdullah 		d->flags |= SD_F_SIMPLEX;
739e4e61333SAriff Abdullah 
74032a0e5d5SHans Petter Selasky 	if (d->playcount > 0 || d->reccount > 0)
741e4e61333SAriff Abdullah 		d->flags |= SD_F_AUTOVCHAN;
742e4e61333SAriff Abdullah 
743e4e61333SAriff Abdullah 	pcm_setmaxautovchans(d, snd_maxautovchans);
744bba4862cSAriff Abdullah 
745a580b31aSAriff Abdullah 	strlcpy(d->status, str, SND_STATUSLEN);
746bba4862cSAriff Abdullah 
74790da2b28SAriff Abdullah 	PCM_LOCK(d);
748e4e61333SAriff Abdullah 
749e4e61333SAriff Abdullah 	/* Done, we're ready.. */
750e4e61333SAriff Abdullah 	d->flags |= SD_F_REGISTERED;
751e4e61333SAriff Abdullah 
752e4e61333SAriff Abdullah 	PCM_RELEASE(d);
753bba4862cSAriff Abdullah 
75490da2b28SAriff Abdullah 	PCM_UNLOCK(d);
755bba4862cSAriff Abdullah 
75632a0e5d5SHans Petter Selasky 	/*
75732a0e5d5SHans Petter Selasky 	 * Create all sysctls once SD_F_REGISTERED is set else
75832a0e5d5SHans Petter Selasky 	 * tunable sysctls won't work:
75932a0e5d5SHans Petter Selasky 	 */
76032a0e5d5SHans Petter Selasky 	pcm_sysinit(dev);
76132a0e5d5SHans Petter Selasky 
762cbebc90dSAlexander Motin 	if (snd_unit_auto < 0)
763cbebc90dSAlexander Motin 		snd_unit_auto = (snd_unit < 0) ? 1 : 0;
764cbebc90dSAlexander Motin 	if (snd_unit < 0 || snd_unit_auto > 1)
765f3685841SAriff Abdullah 		snd_unit = device_get_unit(dev);
766cbebc90dSAlexander Motin 	else if (snd_unit_auto == 1)
767cbebc90dSAlexander Motin 		snd_unit = pcm_best_unit(snd_unit);
768f3685841SAriff Abdullah 
769e4e61333SAriff Abdullah 	return (0);
770987e5972SCameron Grant }
771987e5972SCameron Grant 
772a1d444e1SAriff Abdullah uint32_t
773987e5972SCameron Grant pcm_getflags(device_t dev)
774987e5972SCameron Grant {
77566ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
77649c5e6e2SCameron Grant 
777987e5972SCameron Grant 	return d->flags;
778987e5972SCameron Grant }
779987e5972SCameron Grant 
780987e5972SCameron Grant void
781a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val)
782987e5972SCameron Grant {
78366ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
784d95502a8SCameron Grant 
785987e5972SCameron Grant 	d->flags = val;
786987e5972SCameron Grant }
787987e5972SCameron Grant 
78839004e69SCameron Grant void *
78939004e69SCameron Grant pcm_getdevinfo(device_t dev)
79039004e69SCameron Grant {
79166ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
79249c5e6e2SCameron Grant 
79339004e69SCameron Grant 	return d->devinfo;
79439004e69SCameron Grant }
79539004e69SCameron Grant 
796a67fe5c1SCameron Grant unsigned int
797d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
798a67fe5c1SCameron Grant {
799a67fe5c1SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
8004e60be34SCameron Grant 	int sz, x;
801a67fe5c1SCameron Grant 
802a67fe5c1SCameron Grant 	sz = 0;
8034e60be34SCameron Grant 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
8044e60be34SCameron Grant 		x = sz;
805d55d96f6SAlexander Leidinger 		RANGE(sz, minbufsz, maxbufsz);
8064e60be34SCameron Grant 		if (x != sz)
807d55d96f6SAlexander Leidinger 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
808d55d96f6SAlexander Leidinger 		x = minbufsz;
8094e60be34SCameron Grant 		while (x < sz)
8104e60be34SCameron Grant 			x <<= 1;
8114e60be34SCameron Grant 		if (x > sz)
8124e60be34SCameron Grant 			x >>= 1;
8134e60be34SCameron Grant 		if (x != sz) {
8144e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
8154e60be34SCameron Grant 			sz = x;
8164e60be34SCameron Grant 		}
8174e60be34SCameron Grant 	} else {
818a67fe5c1SCameron Grant 		sz = deflt;
8194e60be34SCameron Grant 	}
8204e60be34SCameron Grant 
821a67fe5c1SCameron Grant 	d->bufsz = sz;
822a67fe5c1SCameron Grant 
823a67fe5c1SCameron Grant 	return sz;
824a67fe5c1SCameron Grant }
825a67fe5c1SCameron Grant 
82690da2b28SAriff Abdullah static int
82790da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)
82890da2b28SAriff Abdullah {
82990da2b28SAriff Abdullah 	struct snddev_info *d;
83090da2b28SAriff Abdullah 	int err, val;
83190da2b28SAriff Abdullah 
83290da2b28SAriff Abdullah 	d = oidp->oid_arg1;
83390da2b28SAriff Abdullah 	if (!PCM_REGISTERED(d))
83490da2b28SAriff Abdullah 		return (ENODEV);
83590da2b28SAriff Abdullah 
83690da2b28SAriff Abdullah 	PCM_LOCK(d);
83790da2b28SAriff Abdullah 	PCM_WAIT(d);
83890da2b28SAriff Abdullah 	val = (d->flags & SD_F_BITPERFECT) ? 1 : 0;
83990da2b28SAriff Abdullah 	PCM_ACQUIRE(d);
84090da2b28SAriff Abdullah 	PCM_UNLOCK(d);
84190da2b28SAriff Abdullah 
84290da2b28SAriff Abdullah 	err = sysctl_handle_int(oidp, &val, 0, req);
84390da2b28SAriff Abdullah 
84490da2b28SAriff Abdullah 	if (err == 0 && req->newptr != NULL) {
84590da2b28SAriff Abdullah 		if (!(val == 0 || val == 1)) {
84690da2b28SAriff Abdullah 			PCM_RELEASE_QUICK(d);
84790da2b28SAriff Abdullah 			return (EINVAL);
84890da2b28SAriff Abdullah 		}
84990da2b28SAriff Abdullah 
85090da2b28SAriff Abdullah 		PCM_LOCK(d);
85190da2b28SAriff Abdullah 
85290da2b28SAriff Abdullah 		d->flags &= ~SD_F_BITPERFECT;
85390da2b28SAriff Abdullah 		d->flags |= (val != 0) ? SD_F_BITPERFECT : 0;
85490da2b28SAriff Abdullah 
85590da2b28SAriff Abdullah 		PCM_RELEASE(d);
85690da2b28SAriff Abdullah 		PCM_UNLOCK(d);
85790da2b28SAriff Abdullah 	} else
85890da2b28SAriff Abdullah 		PCM_RELEASE_QUICK(d);
85990da2b28SAriff Abdullah 
86090da2b28SAriff Abdullah 	return (err);
86190da2b28SAriff Abdullah }
86290da2b28SAriff Abdullah 
863ed2196e5SHans Petter Selasky static u_int8_t
864ed2196e5SHans Petter Selasky pcm_mode_init(struct snddev_info *d)
865ed2196e5SHans Petter Selasky {
866ed2196e5SHans Petter Selasky 	u_int8_t mode = 0;
867ed2196e5SHans Petter Selasky 
868ed2196e5SHans Petter Selasky 	if (d->playcount > 0)
869ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_PLAY;
870ed2196e5SHans Petter Selasky 	if (d->reccount > 0)
871ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_REC;
872ed2196e5SHans Petter Selasky 	if (d->mixer_dev != NULL)
873ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_MIXER;
874ed2196e5SHans Petter Selasky 
875ed2196e5SHans Petter Selasky 	return (mode);
876ed2196e5SHans Petter Selasky }
877ed2196e5SHans Petter Selasky 
87832a0e5d5SHans Petter Selasky static void
87932a0e5d5SHans Petter Selasky pcm_sysinit(device_t dev)
88032a0e5d5SHans Petter Selasky {
88132a0e5d5SHans Petter Selasky   	struct snddev_info *d = device_get_softc(dev);
882ed2196e5SHans Petter Selasky 	u_int8_t mode;
883ed2196e5SHans Petter Selasky 
884ed2196e5SHans Petter Selasky 	mode = pcm_mode_init(d);
88532a0e5d5SHans Petter Selasky 
886132fca63SHans Petter Selasky 	/* XXX: a user should be able to set this with a control tool, the
88732a0e5d5SHans Petter Selasky 	   sysadmin then needs min+max sysctls for this */
88832a0e5d5SHans Petter Selasky 	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
88932a0e5d5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
89032a0e5d5SHans Petter Selasky             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
89132a0e5d5SHans Petter Selasky 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
89232a0e5d5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
8933b4c5433SAlexander Motin 	    "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d,
8947029da5cSPawel Biernacki 	    sizeof(d), sysctl_dev_pcm_bitperfect, "I",
89532a0e5d5SHans Petter Selasky 	    "bit-perfect playback/recording (0=disable, 1=enable)");
896ed2196e5SHans Petter Selasky 	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
897ed2196e5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
898ed2196e5SHans Petter Selasky 	    OID_AUTO, "mode", CTLFLAG_RD, NULL, mode,
899ed2196e5SHans Petter Selasky 	    "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than one"
900ed2196e5SHans Petter Selasky 	    "mode is supported)");
90132a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_AUTOVCHAN)
90232a0e5d5SHans Petter Selasky 		vchan_initsys(dev);
90332a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_EQ)
90432a0e5d5SHans Petter Selasky 		feeder_eq_initsys(dev);
90532a0e5d5SHans Petter Selasky }
90632a0e5d5SHans Petter Selasky 
907987e5972SCameron Grant int
908987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
909987e5972SCameron Grant {
910bba4862cSAriff Abdullah 	struct snddev_info *d;
91190da2b28SAriff Abdullah 	int i;
912987e5972SCameron Grant 
913b8a36395SCameron Grant 	if (pcm_veto_load) {
914b8a36395SCameron Grant 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
915b8a36395SCameron Grant 
916b8a36395SCameron Grant 		return EINVAL;
917b8a36395SCameron Grant 	}
918b8a36395SCameron Grant 
919bba4862cSAriff Abdullah 	if (device_get_unit(dev) > PCMMAXUNIT) {
920bba4862cSAriff Abdullah 		device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n",
921bba4862cSAriff Abdullah 		    device_get_unit(dev), PCMMAXUNIT);
922bba4862cSAriff Abdullah 		device_printf(dev,
923bba4862cSAriff Abdullah 		    "Use 'hw.snd.maxunit' tunable to raise the limit.\n");
924bba4862cSAriff Abdullah 		return ENODEV;
925bba4862cSAriff Abdullah 	}
926bba4862cSAriff Abdullah 
927bba4862cSAriff Abdullah 	d = device_get_softc(dev);
928e4e61333SAriff Abdullah 	d->dev = dev;
9292c69ba87SJohn Baldwin 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
930e4e61333SAriff Abdullah 	cv_init(&d->cv, device_get_nameunit(dev));
931e4e61333SAriff Abdullah 	PCM_ACQUIRE_QUICK(d);
9327233ababSAlexander Leidinger #if 0
9337233ababSAlexander Leidinger 	/*
9347233ababSAlexander Leidinger 	 * d->flags should be cleared by the allocator of the softc.
9357233ababSAlexander Leidinger 	 * We cannot clear this field here because several devices set
9367233ababSAlexander Leidinger 	 * this flag before calling pcm_register().
9377233ababSAlexander Leidinger 	 */
938cd9766c5SCameron Grant 	d->flags = 0;
9397233ababSAlexander Leidinger #endif
94090da2b28SAriff Abdullah 	i = 0;
94190da2b28SAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
94290da2b28SAriff Abdullah 	    "vpc", &i) != 0 || i != 0)
94390da2b28SAriff Abdullah 		d->flags |= SD_F_VPC;
94490da2b28SAriff Abdullah 
94590da2b28SAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
94690da2b28SAriff Abdullah 	    "bitperfect", &i) == 0 && i != 0)
94790da2b28SAriff Abdullah 		d->flags |= SD_F_BITPERFECT;
94890da2b28SAriff Abdullah 
949987e5972SCameron Grant 	d->devinfo = devinfo;
950f637a36cSCameron Grant 	d->devcount = 0;
951506a5308SCameron Grant 	d->reccount = 0;
952a67fe5c1SCameron Grant 	d->playcount = 0;
953bba4862cSAriff Abdullah 	d->pvchancount = 0;
954bba4862cSAriff Abdullah 	d->rvchancount = 0;
955bba4862cSAriff Abdullah 	d->pvchanrate = 0;
956bba4862cSAriff Abdullah 	d->pvchanformat = 0;
957bba4862cSAriff Abdullah 	d->rvchanrate = 0;
958bba4862cSAriff Abdullah 	d->rvchanformat = 0;
959833f7023SCameron Grant 
960bba4862cSAriff Abdullah 	CHN_INIT(d, channels.pcm);
961bba4862cSAriff Abdullah 	CHN_INIT(d, channels.pcm.busy);
96290da2b28SAriff Abdullah 	CHN_INIT(d, channels.pcm.opened);
96345550658SPoul-Henning Kamp 
964e4e61333SAriff Abdullah 	/* XXX This is incorrect, but lets play along for now. */
965a1d444e1SAriff Abdullah 	if ((numplay == 0 || numrec == 0) && numplay != numrec)
966285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
967d95502a8SCameron Grant 
968bba4862cSAriff Abdullah 	sysctl_ctx_init(&d->play_sysctl_ctx);
969bba4862cSAriff Abdullah 	d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
970bba4862cSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
9717029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node");
972bba4862cSAriff Abdullah 	sysctl_ctx_init(&d->rec_sysctl_ctx);
973bba4862cSAriff Abdullah 	d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
974bba4862cSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
975132fca63SHans Petter Selasky 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node");
976e4e61333SAriff Abdullah 
97732a0e5d5SHans Petter Selasky 	if (numplay > 0 || numrec > 0)
978cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
97990da2b28SAriff Abdullah 
9809da3b645SChristos Margiolis 	sndstat_register(dev, d->status);
981e4e61333SAriff Abdullah 
982e8c0d15aSChristos Margiolis 	return (dsp_make_dev(dev));
983987e5972SCameron Grant }
984987e5972SCameron Grant 
98533dbf14aSCameron Grant int
98633dbf14aSCameron Grant pcm_unregister(device_t dev)
9877c438dbeSCameron Grant {
988e4e61333SAriff Abdullah 	struct snddev_info *d;
989a67fe5c1SCameron Grant 	struct pcm_channel *ch;
9907c438dbeSCameron Grant 
991e4e61333SAriff Abdullah 	d = device_get_softc(dev);
992e4e61333SAriff Abdullah 
993e4e61333SAriff Abdullah 	if (!PCM_ALIVE(d)) {
994e4e61333SAriff Abdullah 		device_printf(dev, "unregister: device not configured\n");
995e4e61333SAriff Abdullah 		return (0);
996e4e61333SAriff Abdullah 	}
997bba4862cSAriff Abdullah 
99890da2b28SAriff Abdullah 	PCM_LOCK(d);
999e4e61333SAriff Abdullah 	PCM_WAIT(d);
1000e4e61333SAriff Abdullah 
1001cc1efc23SHans Petter Selasky 	d->flags |= SD_F_DETACHING;
1002cc1efc23SHans Petter Selasky 
1003e4e61333SAriff Abdullah 	PCM_ACQUIRE(d);
100490da2b28SAriff Abdullah 	PCM_UNLOCK(d);
1005e4e61333SAriff Abdullah 
1006e4e61333SAriff Abdullah 	CHN_FOREACH(ch, d, channels.pcm) {
1007e4e61333SAriff Abdullah 		CHN_LOCK(ch);
100844e128feSChristos Margiolis 		if (ch->flags & CHN_F_SLEEPING) {
100944e128feSChristos Margiolis 			/*
101044e128feSChristos Margiolis 			 * We are detaching, so do not wait for the timeout in
101144e128feSChristos Margiolis 			 * chn_read()/chn_write(). Wake up the thread and kill
101244e128feSChristos Margiolis 			 * the channel immediately.
101344e128feSChristos Margiolis 			 */
101444e128feSChristos Margiolis 			CHN_BROADCAST(&ch->intr_cv);
101544e128feSChristos Margiolis 			ch->flags |= CHN_F_DEAD;
1016285648f9SCameron Grant 		}
101744e128feSChristos Margiolis 		chn_abort(ch);
1018e4e61333SAriff Abdullah 		CHN_UNLOCK(ch);
1019c9b53085SCameron Grant 	}
10205ee30e27SMathew Kanner 
1021e8c0d15aSChristos Margiolis 	dsp_destroy_dev(dev);
1022bba4862cSAriff Abdullah 
102344e128feSChristos Margiolis 	(void)mixer_uninit(dev);
10247233ababSAlexander Leidinger 
10258461d581SHans Petter Selasky 	/* remove /dev/sndstat entry first */
10268461d581SHans Petter Selasky 	sndstat_unregister(dev);
10278461d581SHans Petter Selasky 
102890da2b28SAriff Abdullah 	PCM_LOCK(d);
1029e4e61333SAriff Abdullah 	d->flags |= SD_F_DYING;
1030e4e61333SAriff Abdullah 	d->flags &= ~SD_F_REGISTERED;
103190da2b28SAriff Abdullah 	PCM_UNLOCK(d);
1032e4e61333SAriff Abdullah 
1033bba4862cSAriff Abdullah 	if (d->play_sysctl_tree != NULL) {
1034bba4862cSAriff Abdullah 		sysctl_ctx_free(&d->play_sysctl_ctx);
1035bba4862cSAriff Abdullah 		d->play_sysctl_tree = NULL;
1036bba4862cSAriff Abdullah 	}
1037bba4862cSAriff Abdullah 	if (d->rec_sysctl_tree != NULL) {
1038bba4862cSAriff Abdullah 		sysctl_ctx_free(&d->rec_sysctl_ctx);
1039bba4862cSAriff Abdullah 		d->rec_sysctl_tree = NULL;
1040a1d444e1SAriff Abdullah 	}
1041bba4862cSAriff Abdullah 
1042bba4862cSAriff Abdullah 	while (!CHN_EMPTY(d, channels.pcm))
1043285648f9SCameron Grant 		pcm_killchan(dev);
10447c438dbeSCameron Grant 
104590da2b28SAriff Abdullah 	PCM_LOCK(d);
1046e4e61333SAriff Abdullah 	PCM_RELEASE(d);
1047e4e61333SAriff Abdullah 	cv_destroy(&d->cv);
104890da2b28SAriff Abdullah 	PCM_UNLOCK(d);
104949c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
1050bba4862cSAriff Abdullah 
1051bba4862cSAriff Abdullah 	if (snd_unit == device_get_unit(dev)) {
1052cbebc90dSAlexander Motin 		snd_unit = pcm_best_unit(-1);
1053cbebc90dSAlexander Motin 		if (snd_unit_auto == 0)
1054cbebc90dSAlexander Motin 			snd_unit_auto = 1;
1055e4e61333SAriff Abdullah 	}
1056bba4862cSAriff Abdullah 
1057e4e61333SAriff Abdullah 	return (0);
105833dbf14aSCameron Grant }
10597c438dbeSCameron Grant 
106067b1dce3SCameron Grant /************************************************************************/
106167b1dce3SCameron Grant 
1062b611c801SAlexander Leidinger /**
1063b611c801SAlexander Leidinger  * @brief	Handle OSSv4 SNDCTL_SYSINFO ioctl.
1064b611c801SAlexander Leidinger  *
1065b611c801SAlexander Leidinger  * @param si	Pointer to oss_sysinfo struct where information about the
1066b611c801SAlexander Leidinger  * 		sound subsystem will be written/copied.
1067b611c801SAlexander Leidinger  *
1068b611c801SAlexander Leidinger  * This routine returns information about the sound system, such as the
1069b611c801SAlexander Leidinger  * current OSS version, number of audio, MIDI, and mixer drivers, etc.
1070b611c801SAlexander Leidinger  * Also includes a bitmask showing which of the above types of devices
1071b611c801SAlexander Leidinger  * are open (busy).
1072b611c801SAlexander Leidinger  *
1073b611c801SAlexander Leidinger  * @note
1074b611c801SAlexander Leidinger  * Calling threads must not hold any snddev_info or pcm_channel locks.
1075b611c801SAlexander Leidinger  *
1076b611c801SAlexander Leidinger  * @author	Ryan Beasley <ryanb@FreeBSD.org>
1077b611c801SAlexander Leidinger  */
1078b611c801SAlexander Leidinger void
1079b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si)
1080b611c801SAlexander Leidinger {
1081b611c801SAlexander Leidinger 	static char si_product[] = "FreeBSD native OSS ABI";
1082b611c801SAlexander Leidinger 	static char si_version[] = __XSTRING(__FreeBSD_version);
1083c412d503SAlexander Motin 	static char si_license[] = "BSD";
1084b611c801SAlexander Leidinger 	static int intnbits = sizeof(int) * 8;	/* Better suited as macro?
1085b611c801SAlexander Leidinger 						   Must pester a C guru. */
1086b611c801SAlexander Leidinger 
1087b611c801SAlexander Leidinger 	struct snddev_info *d;
1088b611c801SAlexander Leidinger 	struct pcm_channel *c;
1089b611c801SAlexander Leidinger 	int i, j, ncards;
1090b611c801SAlexander Leidinger 
1091b611c801SAlexander Leidinger 	ncards = 0;
1092b611c801SAlexander Leidinger 
1093b611c801SAlexander Leidinger 	strlcpy(si->product, si_product, sizeof(si->product));
1094b611c801SAlexander Leidinger 	strlcpy(si->version, si_version, sizeof(si->version));
1095b611c801SAlexander Leidinger 	si->versionnum = SOUND_VERSION;
1096c412d503SAlexander Motin 	strlcpy(si->license, si_license, sizeof(si->license));
1097b611c801SAlexander Leidinger 
1098b611c801SAlexander Leidinger 	/*
1099b611c801SAlexander Leidinger 	 * Iterate over PCM devices and their channels, gathering up data
1100b611c801SAlexander Leidinger 	 * for the numaudios, ncards, and openedaudio fields.
1101b611c801SAlexander Leidinger 	 */
1102b611c801SAlexander Leidinger 	si->numaudios = 0;
1103b611c801SAlexander Leidinger 	bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
1104b611c801SAlexander Leidinger 
1105b611c801SAlexander Leidinger 	j = 0;
1106b611c801SAlexander Leidinger 
11079c271f79SAriff Abdullah 	for (i = 0; pcm_devclass != NULL &&
11089c271f79SAriff Abdullah 	    i < devclass_get_maxunit(pcm_devclass); i++) {
1109b611c801SAlexander Leidinger 		d = devclass_get_softc(pcm_devclass, i);
1110e4e61333SAriff Abdullah 		if (!PCM_REGISTERED(d))
1111b611c801SAlexander Leidinger 			continue;
1112b611c801SAlexander Leidinger 
1113e4e61333SAriff Abdullah 		/* XXX Need Giant magic entry ??? */
1114e4e61333SAriff Abdullah 
1115b611c801SAlexander Leidinger 		/* See note in function's docblock */
111690da2b28SAriff Abdullah 		PCM_UNLOCKASSERT(d);
111790da2b28SAriff Abdullah 		PCM_LOCK(d);
1118b611c801SAlexander Leidinger 
1119b611c801SAlexander Leidinger 		si->numaudios += d->devcount;
1120b611c801SAlexander Leidinger 		++ncards;
1121b611c801SAlexander Leidinger 
1122bba4862cSAriff Abdullah 		CHN_FOREACH(c, d, channels.pcm) {
112390da2b28SAriff Abdullah 			CHN_UNLOCKASSERT(c);
1124b611c801SAlexander Leidinger 			CHN_LOCK(c);
1125b611c801SAlexander Leidinger 			if (c->flags & CHN_F_BUSY)
1126b611c801SAlexander Leidinger 				si->openedaudio[j / intnbits] |=
1127b611c801SAlexander Leidinger 				    (1 << (j % intnbits));
1128b611c801SAlexander Leidinger 			CHN_UNLOCK(c);
1129b611c801SAlexander Leidinger 			j++;
1130b611c801SAlexander Leidinger 		}
1131b611c801SAlexander Leidinger 
113290da2b28SAriff Abdullah 		PCM_UNLOCK(d);
1133b611c801SAlexander Leidinger 	}
1134c412d503SAlexander Motin 	si->numaudioengines = si->numaudios;
1135b611c801SAlexander Leidinger 
1136b611c801SAlexander Leidinger 	si->numsynths = 0;	/* OSSv4 docs:  this field is obsolete */
1137b611c801SAlexander Leidinger 	/**
1138b611c801SAlexander Leidinger 	 * @todo	Collect num{midis,timers}.
1139b611c801SAlexander Leidinger 	 *
1140b611c801SAlexander Leidinger 	 * Need access to sound/midi/midi.c::midistat_lock in order
1141b611c801SAlexander Leidinger 	 * to safely touch midi_devices and get a head count of, well,
1142b611c801SAlexander Leidinger 	 * MIDI devices.  midistat_lock is a global static (i.e., local to
1143b611c801SAlexander Leidinger 	 * midi.c), but midi_devices is a regular global; should the mutex
1144b611c801SAlexander Leidinger 	 * be publicized, or is there another way to get this information?
1145b611c801SAlexander Leidinger 	 *
1146b611c801SAlexander Leidinger 	 * NB:	MIDI/sequencer stuff is currently on hold.
1147b611c801SAlexander Leidinger 	 */
1148b611c801SAlexander Leidinger 	si->nummidis = 0;
1149b611c801SAlexander Leidinger 	si->numtimers = 0;
1150b611c801SAlexander Leidinger 	si->nummixers = mixer_count;
1151b611c801SAlexander Leidinger 	si->numcards = ncards;
1152b611c801SAlexander Leidinger 		/* OSSv4 docs:	Intended only for test apps; API doesn't
1153b611c801SAlexander Leidinger 		   really have much of a concept of cards.  Shouldn't be
1154b611c801SAlexander Leidinger 		   used by applications. */
1155b611c801SAlexander Leidinger 
1156b611c801SAlexander Leidinger 	/**
1157b611c801SAlexander Leidinger 	 * @todo	Fill in "busy devices" fields.
1158b611c801SAlexander Leidinger 	 *
1159b611c801SAlexander Leidinger 	 *  si->openedmidi = " MIDI devices
1160b611c801SAlexander Leidinger 	 */
1161b611c801SAlexander Leidinger 	bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
1162b611c801SAlexander Leidinger 
1163b611c801SAlexander Leidinger 	/*
1164b611c801SAlexander Leidinger 	 * Si->filler is a reserved array, but according to docs each
1165b611c801SAlexander Leidinger 	 * element should be set to -1.
1166b611c801SAlexander Leidinger 	 */
1167b611c801SAlexander Leidinger 	for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++)
1168b611c801SAlexander Leidinger 		si->filler[i] = -1;
1169b611c801SAlexander Leidinger }
1170b611c801SAlexander Leidinger 
117152f6e09eSAlexander Motin int
117252f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si)
117352f6e09eSAlexander Motin {
117452f6e09eSAlexander Motin 	struct snddev_info *d;
117552f6e09eSAlexander Motin 	int i, ncards;
117652f6e09eSAlexander Motin 
117752f6e09eSAlexander Motin 	ncards = 0;
117852f6e09eSAlexander Motin 
117952f6e09eSAlexander Motin 	for (i = 0; pcm_devclass != NULL &&
118052f6e09eSAlexander Motin 	    i < devclass_get_maxunit(pcm_devclass); i++) {
118152f6e09eSAlexander Motin 		d = devclass_get_softc(pcm_devclass, i);
118252f6e09eSAlexander Motin 		if (!PCM_REGISTERED(d))
118352f6e09eSAlexander Motin 			continue;
118452f6e09eSAlexander Motin 
118552f6e09eSAlexander Motin 		if (ncards++ != si->card)
118652f6e09eSAlexander Motin 			continue;
118752f6e09eSAlexander Motin 
118890da2b28SAriff Abdullah 		PCM_UNLOCKASSERT(d);
118990da2b28SAriff Abdullah 		PCM_LOCK(d);
119052f6e09eSAlexander Motin 
119152f6e09eSAlexander Motin 		strlcpy(si->shortname, device_get_nameunit(d->dev),
119252f6e09eSAlexander Motin 		    sizeof(si->shortname));
119352f6e09eSAlexander Motin 		strlcpy(si->longname, device_get_desc(d->dev),
119452f6e09eSAlexander Motin 		    sizeof(si->longname));
119552f6e09eSAlexander Motin 		strlcpy(si->hw_info, d->status, sizeof(si->hw_info));
119652f6e09eSAlexander Motin 		si->intr_count = si->ack_count = 0;
119790da2b28SAriff Abdullah 
119890da2b28SAriff Abdullah 		PCM_UNLOCK(d);
119990da2b28SAriff Abdullah 
120052f6e09eSAlexander Motin 		return (0);
120152f6e09eSAlexander Motin 	}
120252f6e09eSAlexander Motin 	return (ENXIO);
120352f6e09eSAlexander Motin }
120452f6e09eSAlexander Motin 
1205b611c801SAlexander Leidinger /************************************************************************/
1206b611c801SAlexander Leidinger 
12070739ea1dSSeigo Tanimura static int
12080739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data)
12090739ea1dSSeigo Tanimura {
1210b611c801SAlexander Leidinger 	int ret;
1211b611c801SAlexander Leidinger 
12128109ec9dSJohn Baldwin 	ret = 0;
1213b611c801SAlexander Leidinger 	switch (type) {
1214b611c801SAlexander Leidinger 		case MOD_LOAD:
121513bebcd3SJohn Baldwin 			pcm_devclass = devclass_create("pcm");
1216b611c801SAlexander Leidinger 			pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
1217b611c801SAlexander Leidinger 			break;
1218b611c801SAlexander Leidinger 		case MOD_UNLOAD:
1219b611c801SAlexander Leidinger 			if (pcmsg_unrhdr != NULL) {
1220b611c801SAlexander Leidinger 				delete_unrhdr(pcmsg_unrhdr);
1221b611c801SAlexander Leidinger 				pcmsg_unrhdr = NULL;
1222b611c801SAlexander Leidinger 			}
1223b611c801SAlexander Leidinger 			break;
12245525b53dSAlexander Motin 		case MOD_SHUTDOWN:
12255525b53dSAlexander Motin 			break;
1226b611c801SAlexander Leidinger 		default:
122790da2b28SAriff Abdullah 			ret = ENOTSUP;
1228b611c801SAlexander Leidinger 	}
1229b611c801SAlexander Leidinger 
1230b611c801SAlexander Leidinger 	return ret;
12310739ea1dSSeigo Tanimura }
12320739ea1dSSeigo Tanimura 
12330739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL);
12340739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER);
1235