xref: /freebsd/sys/dev/sound/pcm/sound.c (revision 44e128fe9d92c1a544b801cb56e907a66ef34691)
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.
9987e5972SCameron Grant  *
10987e5972SCameron Grant  * Redistribution and use in source and binary forms, with or without
11987e5972SCameron Grant  * modification, are permitted provided that the following conditions
12987e5972SCameron Grant  * are met:
13987e5972SCameron Grant  * 1. Redistributions of source code must retain the above copyright
14987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer.
15987e5972SCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
16987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer in the
17987e5972SCameron Grant  *    documentation and/or other materials provided with the distribution.
18987e5972SCameron Grant  *
19987e5972SCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20987e5972SCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21987e5972SCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22987e5972SCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23987e5972SCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24987e5972SCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25987e5972SCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26987e5972SCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27987e5972SCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28987e5972SCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29987e5972SCameron Grant  * SUCH DAMAGE.
30987e5972SCameron Grant  */
31987e5972SCameron Grant 
3290da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS
3390da2b28SAriff Abdullah #include "opt_snd.h"
3490da2b28SAriff Abdullah #endif
3590da2b28SAriff Abdullah 
36ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
37a580b31aSAriff Abdullah #include <dev/sound/pcm/ac97.h>
38285648f9SCameron Grant #include <dev/sound/pcm/vchan.h>
395ee30e27SMathew Kanner #include <dev/sound/pcm/dsp.h>
40bba4862cSAriff Abdullah #include <dev/sound/version.h>
41b611c801SAlexander Leidinger #include <sys/limits.h>
427c438dbeSCameron Grant #include <sys/sysctl.h>
43285648f9SCameron Grant 
4467b1dce3SCameron Grant #include "feeder_if.h"
4567b1dce3SCameron Grant 
46d95502a8SCameron Grant devclass_t pcm_devclass;
4782db23e2SCameron Grant 
48b8a36395SCameron Grant int pcm_veto_load = 1;
49b8a36395SCameron Grant 
50f3685841SAriff Abdullah int snd_unit = -1;
51cbe7d6a3SCameron Grant 
52cbebc90dSAlexander Motin static int snd_unit_auto = -1;
53af3b2549SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN,
54838d3589SAriff Abdullah     &snd_unit_auto, 0, "assign default unit to a newly attached device");
55ad8612b9SAriff Abdullah 
56bba4862cSAriff Abdullah int snd_maxautovchans = 16;
57987e5972SCameron Grant 
587029da5cSPawel Biernacki SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
597029da5cSPawel Biernacki     "Sound driver");
6082db23e2SCameron Grant 
6132a0e5d5SHans Petter Selasky static void pcm_sysinit(device_t);
6232a0e5d5SHans Petter Selasky 
63bba4862cSAriff Abdullah /*
64bba4862cSAriff Abdullah  * XXX I've had enough with people not telling proper version/arch
65bba4862cSAriff Abdullah  *     while reporting problems, not after 387397913213th questions/requests.
66bba4862cSAriff Abdullah  */
67a9bdb5d3SAndriy Gapon static char snd_driver_version[] =
68bba4862cSAriff Abdullah     __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH;
69bba4862cSAriff Abdullah SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version,
7090da2b28SAriff Abdullah     0, "driver version/arch");
71bba4862cSAriff Abdullah 
72b611c801SAlexander Leidinger /**
73b611c801SAlexander Leidinger  * @brief Unit number allocator for syncgroup IDs
74b611c801SAlexander Leidinger  */
75b611c801SAlexander Leidinger struct unrhdr *pcmsg_unrhdr = NULL;
76b611c801SAlexander Leidinger 
7737209180SCameron Grant void *
782c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type)
7937209180SCameron Grant {
8037209180SCameron Grant 	struct mtx *m;
8137209180SCameron Grant 
82a163d034SWarner Losh 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
8312e524a2SDon Lewis 	mtx_init(m, desc, type, MTX_DEF);
8412e524a2SDon Lewis 	return m;
8512e524a2SDon Lewis }
8612e524a2SDon Lewis 
8737209180SCameron Grant void
8837209180SCameron Grant snd_mtxfree(void *m)
8937209180SCameron Grant {
9037209180SCameron Grant 	struct mtx *mtx = m;
9137209180SCameron Grant 
9237209180SCameron Grant 	mtx_destroy(mtx);
9337209180SCameron Grant 	free(mtx, M_DEVBUF);
9437209180SCameron Grant }
9537209180SCameron Grant 
9637209180SCameron Grant void
9737209180SCameron Grant snd_mtxassert(void *m)
9837209180SCameron Grant {
99f00f162aSCameron Grant #ifdef INVARIANTS
10037209180SCameron Grant 	struct mtx *mtx = m;
10137209180SCameron Grant 
10237209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
10337209180SCameron Grant #endif
10437209180SCameron Grant }
10537209180SCameron Grant 
10637209180SCameron Grant int
10737209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
10837209180SCameron Grant {
109e4e61333SAriff Abdullah 	struct snddev_info *d;
11090da2b28SAriff Abdullah 
11137209180SCameron Grant 	flags &= INTR_MPSAFE;
11246700f12SPeter Wemm 	flags |= INTR_TYPE_AV;
113e4e61333SAriff Abdullah 	d = device_get_softc(dev);
114e4e61333SAriff Abdullah 	if (d != NULL && (flags & INTR_MPSAFE))
115e4e61333SAriff Abdullah 		d->flags |= SD_F_MPSAFE;
116e4e61333SAriff Abdullah 
117775fcb6eSBaptiste Daroussin 	return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep);
11837209180SCameron Grant }
11937209180SCameron Grant 
12090da2b28SAriff Abdullah int
121bba4862cSAriff Abdullah pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
122bba4862cSAriff Abdullah {
123bba4862cSAriff Abdullah 	struct pcm_channel *c, *ch, *nch;
12490da2b28SAriff Abdullah 	struct pcmchan_caps *caps;
12590da2b28SAriff Abdullah 	int i, err, vcnt;
126bba4862cSAriff Abdullah 
127e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
128a1d444e1SAriff Abdullah 
129bba4862cSAriff Abdullah 	if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
130e4e61333SAriff Abdullah 	    (direction == PCMDIR_REC && d->reccount < 1))
131e4e61333SAriff Abdullah 		return (ENODEV);
132a580b31aSAriff Abdullah 
133e4e61333SAriff Abdullah 	if (!(d->flags & SD_F_AUTOVCHAN))
134e4e61333SAriff Abdullah 		return (EINVAL);
135a1d444e1SAriff Abdullah 
136e4e61333SAriff Abdullah 	if (newcnt < 0 || newcnt > SND_MAXVCHANS)
137e4e61333SAriff Abdullah 		return (E2BIG);
138a1d444e1SAriff Abdullah 
139bba4862cSAriff Abdullah 	if (direction == PCMDIR_PLAY)
140bba4862cSAriff Abdullah 		vcnt = d->pvchancount;
141bba4862cSAriff Abdullah 	else if (direction == PCMDIR_REC)
142bba4862cSAriff Abdullah 		vcnt = d->rvchancount;
143e4e61333SAriff Abdullah 	else
144e4e61333SAriff Abdullah 		return (EINVAL);
145a1d444e1SAriff Abdullah 
146a1d444e1SAriff Abdullah 	if (newcnt > vcnt) {
147bba4862cSAriff Abdullah 		KASSERT(num == -1 ||
148bba4862cSAriff Abdullah 		    (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt),
149bba4862cSAriff Abdullah 		    ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
150bba4862cSAriff Abdullah 		    num, newcnt, vcnt));
151a1d444e1SAriff Abdullah 		/* add new vchans - find a parent channel first */
152e4e61333SAriff Abdullah 		ch = NULL;
153bba4862cSAriff Abdullah 		CHN_FOREACH(c, d, channels.pcm) {
154a1d444e1SAriff Abdullah 			CHN_LOCK(c);
155bba4862cSAriff Abdullah 			if (c->direction == direction &&
156bba4862cSAriff Abdullah 			    ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
15790da2b28SAriff Abdullah 			    c->refcount < 1 &&
158e4e61333SAriff Abdullah 			    !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
15990da2b28SAriff Abdullah 				/*
16090da2b28SAriff Abdullah 				 * Reuse hw channel with vchans already
16190da2b28SAriff Abdullah 				 * created.
16290da2b28SAriff Abdullah 				 */
16390da2b28SAriff Abdullah 				if (c->flags & CHN_F_HAS_VCHAN) {
164e4e61333SAriff Abdullah 					ch = c;
165e4e61333SAriff Abdullah 					break;
166e4e61333SAriff Abdullah 				}
16790da2b28SAriff Abdullah 				/*
16890da2b28SAriff Abdullah 				 * No vchans ever created, look for
16990da2b28SAriff Abdullah 				 * channels with supported formats.
17090da2b28SAriff Abdullah 				 */
17190da2b28SAriff Abdullah 				caps = chn_getcaps(c);
17290da2b28SAriff Abdullah 				if (caps == NULL) {
17390da2b28SAriff Abdullah 					CHN_UNLOCK(c);
17490da2b28SAriff Abdullah 					continue;
17590da2b28SAriff Abdullah 				}
17690da2b28SAriff Abdullah 				for (i = 0; caps->fmtlist[i] != 0; i++) {
17790da2b28SAriff Abdullah 					if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
17890da2b28SAriff Abdullah 						break;
17990da2b28SAriff Abdullah 				}
18090da2b28SAriff Abdullah 				if (caps->fmtlist[i] != 0) {
18190da2b28SAriff Abdullah 					ch = c;
18290da2b28SAriff Abdullah 				    	break;
18390da2b28SAriff Abdullah 				}
18490da2b28SAriff Abdullah 			}
185a1d444e1SAriff Abdullah 			CHN_UNLOCK(c);
186a1d444e1SAriff Abdullah 		}
187e4e61333SAriff Abdullah 		if (ch == NULL)
188e4e61333SAriff Abdullah 			return (EBUSY);
189e4e61333SAriff Abdullah 		ch->flags |= CHN_F_BUSY;
190e4e61333SAriff Abdullah 		err = 0;
191a1d444e1SAriff Abdullah 		while (err == 0 && newcnt > vcnt) {
192e4e61333SAriff Abdullah 			err = vchan_create(ch, num);
193bba4862cSAriff Abdullah 			if (err == 0)
194a1d444e1SAriff Abdullah 				vcnt++;
195bba4862cSAriff Abdullah 			else if (err == E2BIG && newcnt > vcnt)
196bba4862cSAriff Abdullah 				device_printf(d->dev,
197bba4862cSAriff Abdullah 				    "%s: err=%d Maximum channel reached.\n",
198bba4862cSAriff Abdullah 				    __func__, err);
199a1d444e1SAriff Abdullah 		}
200a1d444e1SAriff Abdullah 		if (vcnt == 0)
201e4e61333SAriff Abdullah 			ch->flags &= ~CHN_F_BUSY;
202e4e61333SAriff Abdullah 		CHN_UNLOCK(ch);
203e4e61333SAriff Abdullah 		if (err != 0)
204e4e61333SAriff Abdullah 			return (err);
205a1d444e1SAriff Abdullah 	} else if (newcnt < vcnt) {
206bba4862cSAriff Abdullah 		KASSERT(num == -1,
207bba4862cSAriff Abdullah 		    ("bogus vchan_destroy() request num=%d", num));
208bba4862cSAriff Abdullah 		CHN_FOREACH(c, d, channels.pcm) {
209a1d444e1SAriff Abdullah 			CHN_LOCK(c);
210bba4862cSAriff Abdullah 			if (c->direction != direction ||
211bba4862cSAriff Abdullah 			    CHN_EMPTY(c, children) ||
212bba4862cSAriff Abdullah 			    !(c->flags & CHN_F_HAS_VCHAN)) {
213a1d444e1SAriff Abdullah 				CHN_UNLOCK(c);
214bba4862cSAriff Abdullah 				continue;
215a1d444e1SAriff Abdullah 			}
216bba4862cSAriff Abdullah 			CHN_FOREACH_SAFE(ch, c, nch, children) {
217bba4862cSAriff Abdullah 				CHN_LOCK(ch);
21890da2b28SAriff Abdullah 				if (vcnt == 1 && c->refcount > 0) {
219bba4862cSAriff Abdullah 					CHN_UNLOCK(ch);
22090da2b28SAriff Abdullah 					break;
22190da2b28SAriff Abdullah 				}
22290da2b28SAriff Abdullah 				if (!(ch->flags & CHN_F_BUSY) &&
22390da2b28SAriff Abdullah 				    ch->refcount < 1) {
224bba4862cSAriff Abdullah 					err = vchan_destroy(ch);
225a1d444e1SAriff Abdullah 					if (err == 0)
226a1d444e1SAriff Abdullah 						vcnt--;
227bba4862cSAriff Abdullah 				} else
228bba4862cSAriff Abdullah 					CHN_UNLOCK(ch);
229e4e61333SAriff Abdullah 				if (vcnt == newcnt)
230bba4862cSAriff Abdullah 					break;
231a1d444e1SAriff Abdullah 			}
232bba4862cSAriff Abdullah 			CHN_UNLOCK(c);
233bba4862cSAriff Abdullah 			break;
234bba4862cSAriff Abdullah 		}
235bba4862cSAriff Abdullah 	}
236a1d444e1SAriff Abdullah 
237e4e61333SAriff Abdullah 	return (0);
238a1d444e1SAriff Abdullah }
239a1d444e1SAriff Abdullah 
2403fdb3676SAriff Abdullah /* return error status and a locked channel */
2413fdb3676SAriff Abdullah int
2423fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
24390da2b28SAriff Abdullah     pid_t pid, char *comm, int devunit)
244285648f9SCameron Grant {
245285648f9SCameron Grant 	struct pcm_channel *c;
24690da2b28SAriff Abdullah 	int err, vchancount, vchan_num;
247bba4862cSAriff Abdullah 
248bba4862cSAriff Abdullah 	KASSERT(d != NULL && ch != NULL && (devunit == -1 ||
249bba4862cSAriff Abdullah 	    !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) &&
250bba4862cSAriff Abdullah 	    (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
251e4e61333SAriff Abdullah 	    ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d",
252bba4862cSAriff Abdullah 	    __func__, d, ch, direction, pid, devunit));
253e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
254bba4862cSAriff Abdullah 
255bba4862cSAriff Abdullah 	/* Double check again. */
256bba4862cSAriff Abdullah 	if (devunit != -1) {
257bba4862cSAriff Abdullah 		switch (snd_unit2d(devunit)) {
258bba4862cSAriff Abdullah 		case SND_DEV_DSPHW_PLAY:
259bba4862cSAriff Abdullah 		case SND_DEV_DSPHW_VPLAY:
260bba4862cSAriff Abdullah 			if (direction != PCMDIR_PLAY)
26190da2b28SAriff Abdullah 				return (ENOTSUP);
262bba4862cSAriff Abdullah 			break;
263bba4862cSAriff Abdullah 		case SND_DEV_DSPHW_REC:
264bba4862cSAriff Abdullah 		case SND_DEV_DSPHW_VREC:
265bba4862cSAriff Abdullah 			if (direction != PCMDIR_REC)
26690da2b28SAriff Abdullah 				return (ENOTSUP);
267bba4862cSAriff Abdullah 			break;
268bba4862cSAriff Abdullah 		default:
269bba4862cSAriff Abdullah 			if (!(direction == PCMDIR_PLAY ||
270bba4862cSAriff Abdullah 			    direction == PCMDIR_REC))
27190da2b28SAriff Abdullah 				return (ENOTSUP);
272bba4862cSAriff Abdullah 			break;
273bba4862cSAriff Abdullah 		}
274bba4862cSAriff Abdullah 	}
275285648f9SCameron Grant 
27690da2b28SAriff Abdullah 	*ch = NULL;
27790da2b28SAriff Abdullah 	vchan_num = 0;
27890da2b28SAriff Abdullah 	vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount :
27990da2b28SAriff Abdullah 	    d->rvchancount;
28090da2b28SAriff Abdullah 
2813fdb3676SAriff Abdullah retry_chnalloc:
28290da2b28SAriff Abdullah 	err = ENOTSUP;
283f637a36cSCameron Grant 	/* scan for a free channel */
284bba4862cSAriff Abdullah 	CHN_FOREACH(c, d, channels.pcm) {
28549c5e6e2SCameron Grant 		CHN_LOCK(c);
28690da2b28SAriff Abdullah 		if (devunit == -1 && c->direction == direction &&
28790da2b28SAriff Abdullah 		    (c->flags & CHN_F_VIRTUAL)) {
28890da2b28SAriff Abdullah 			if (vchancount < snd_maxautovchans &&
28990da2b28SAriff Abdullah 			    vchan_num < CHN_CHAN(c)) {
29090da2b28SAriff Abdullah 			    	CHN_UNLOCK(c);
29190da2b28SAriff Abdullah 				goto vchan_alloc;
29290da2b28SAriff Abdullah 			}
29390da2b28SAriff Abdullah 			vchan_num++;
29490da2b28SAriff Abdullah 		}
295bba4862cSAriff Abdullah 		if (c->direction == direction && !(c->flags & CHN_F_BUSY) &&
296bba4862cSAriff Abdullah 		    (devunit == -1 || devunit == -2 || c->unit == devunit)) {
297285648f9SCameron Grant 			c->flags |= CHN_F_BUSY;
298b8f0d9e0SCameron Grant 			c->pid = pid;
29990da2b28SAriff Abdullah 			strlcpy(c->comm, (comm != NULL) ? comm :
30090da2b28SAriff Abdullah 			    CHN_COMM_UNKNOWN, sizeof(c->comm));
3013fdb3676SAriff Abdullah 			*ch = c;
302bba4862cSAriff Abdullah 			return (0);
303bba4862cSAriff Abdullah 		} else if (c->unit == devunit) {
3043fdb3676SAriff Abdullah 			if (c->direction != direction)
30590da2b28SAriff Abdullah 				err = ENOTSUP;
3063fdb3676SAriff Abdullah 			else if (c->flags & CHN_F_BUSY)
3073fdb3676SAriff Abdullah 				err = EBUSY;
3083fdb3676SAriff Abdullah 			else
3093fdb3676SAriff Abdullah 				err = EINVAL;
3103fdb3676SAriff Abdullah 			CHN_UNLOCK(c);
311bba4862cSAriff Abdullah 			return (err);
312bba4862cSAriff Abdullah 		} else if ((devunit == -1 || devunit == -2) &&
313bba4862cSAriff Abdullah 		    c->direction == direction && (c->flags & CHN_F_BUSY))
314a1d444e1SAriff Abdullah 			err = EBUSY;
31549c5e6e2SCameron Grant 		CHN_UNLOCK(c);
316285648f9SCameron Grant 	}
317f637a36cSCameron Grant 
318e4e61333SAriff Abdullah 	if (devunit == -2)
319e4e61333SAriff Abdullah 		return (err);
320e4e61333SAriff Abdullah 
32190da2b28SAriff Abdullah vchan_alloc:
322f637a36cSCameron Grant 	/* no channel available */
323e4e61333SAriff Abdullah 	if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY ||
324e4e61333SAriff Abdullah 	    snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) {
325bba4862cSAriff Abdullah 		if (!(vchancount > 0 && vchancount < snd_maxautovchans) &&
326bba4862cSAriff Abdullah 		    (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans))
327bba4862cSAriff Abdullah 			return (err);
328bba4862cSAriff Abdullah 		err = pcm_setvchans(d, direction, vchancount + 1,
329bba4862cSAriff Abdullah 		    (devunit == -1) ? -1 : snd_unit2c(devunit));
330a1d444e1SAriff Abdullah 		if (err == 0) {
331bba4862cSAriff Abdullah 			if (devunit == -1)
332bba4862cSAriff Abdullah 				devunit = -2;
3333fdb3676SAriff Abdullah 			goto retry_chnalloc;
334f637a36cSCameron Grant 		}
335f637a36cSCameron Grant 	}
336f637a36cSCameron Grant 
337bba4862cSAriff Abdullah 	return (err);
338285648f9SCameron Grant }
339285648f9SCameron Grant 
340b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
341285648f9SCameron Grant int
342b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
343285648f9SCameron Grant {
344e4e61333SAriff Abdullah 	PCM_BUSYASSERT(c->parentsnddev);
345b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
346e4e61333SAriff Abdullah 
347285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
348b8f0d9e0SCameron Grant 	c->pid = -1;
34990da2b28SAriff Abdullah 	strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm));
35049c5e6e2SCameron Grant 	CHN_UNLOCK(c);
351e4e61333SAriff Abdullah 
352e4e61333SAriff Abdullah 	return (0);
353285648f9SCameron Grant }
354285648f9SCameron Grant 
355285648f9SCameron Grant int
356285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
357285648f9SCameron Grant {
358e4e61333SAriff Abdullah 	PCM_BUSYASSERT(c->parentsnddev);
359b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
360e4e61333SAriff Abdullah 
361285648f9SCameron Grant 	c->refcount += ref;
362e4e61333SAriff Abdullah 
363e4e61333SAriff Abdullah 	return (c->refcount);
364285648f9SCameron Grant }
365285648f9SCameron Grant 
36667b1dce3SCameron Grant static void
36767b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num)
36867b1dce3SCameron Grant {
369e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
370e4e61333SAriff Abdullah 
371bba4862cSAriff Abdullah 	if (num < 0)
372bba4862cSAriff Abdullah 		return;
373bba4862cSAriff Abdullah 
374bba4862cSAriff Abdullah 	if (num >= 0 && d->pvchancount > num)
375bba4862cSAriff Abdullah 		(void)pcm_setvchans(d, PCMDIR_PLAY, num, -1);
376bba4862cSAriff Abdullah 	else if (num > 0 && d->pvchancount == 0)
377bba4862cSAriff Abdullah 		(void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1);
378bba4862cSAriff Abdullah 
379bba4862cSAriff Abdullah 	if (num >= 0 && d->rvchancount > num)
380bba4862cSAriff Abdullah 		(void)pcm_setvchans(d, PCMDIR_REC, num, -1);
381bba4862cSAriff Abdullah 	else if (num > 0 && d->rvchancount == 0)
382bba4862cSAriff Abdullah 		(void)pcm_setvchans(d, PCMDIR_REC, 1, -1);
38367b1dce3SCameron Grant }
38467b1dce3SCameron Grant 
38533dbf14aSCameron Grant static int
386851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
38733dbf14aSCameron Grant {
388b8f0d9e0SCameron Grant 	struct snddev_info *d;
38933dbf14aSCameron Grant 	int error, unit;
39033dbf14aSCameron Grant 
39133dbf14aSCameron Grant 	unit = snd_unit;
392041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &unit, 0, req);
39333dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
394b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
395e4e61333SAriff Abdullah 		if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
396b8f0d9e0SCameron Grant 			return EINVAL;
39733dbf14aSCameron Grant 		snd_unit = unit;
398cbebc90dSAlexander Motin 		snd_unit_auto = 0;
39933dbf14aSCameron Grant 	}
40033dbf14aSCameron Grant 	return (error);
40133dbf14aSCameron Grant }
402851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */
403b7d6c6b5SEitan Adler SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit,
4047029da5cSPawel Biernacki     CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0,
4057029da5cSPawel Biernacki     sizeof(int), sysctl_hw_snd_default_unit, "I",
406b7d6c6b5SEitan Adler     "default sound device");
407987e5972SCameron Grant 
408cd9766c5SCameron Grant static int
40967b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
410cd9766c5SCameron Grant {
41167b1dce3SCameron Grant 	struct snddev_info *d;
41267b1dce3SCameron Grant 	int i, v, error;
413cd9766c5SCameron Grant 
41467b1dce3SCameron Grant 	v = snd_maxautovchans;
415041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &v, 0, req);
416cd9766c5SCameron Grant 	if (error == 0 && req->newptr != NULL) {
417bba4862cSAriff Abdullah 		if (v < 0)
418bba4862cSAriff Abdullah 			v = 0;
419bba4862cSAriff Abdullah 		if (v > SND_MAXVCHANS)
420bba4862cSAriff Abdullah 			v = SND_MAXVCHANS;
421bba4862cSAriff Abdullah 		snd_maxautovchans = v;
4229c271f79SAriff Abdullah 		for (i = 0; pcm_devclass != NULL &&
4239c271f79SAriff Abdullah 		    i < devclass_get_maxunit(pcm_devclass); i++) {
42467b1dce3SCameron Grant 			d = devclass_get_softc(pcm_devclass, i);
425e4e61333SAriff Abdullah 			if (!PCM_REGISTERED(d))
42667b1dce3SCameron Grant 				continue;
427e4e61333SAriff Abdullah 			PCM_ACQUIRE_QUICK(d);
42867b1dce3SCameron Grant 			pcm_setmaxautovchans(d, v);
429e4e61333SAriff Abdullah 			PCM_RELEASE_QUICK(d);
43067b1dce3SCameron Grant 		}
43167b1dce3SCameron Grant 	}
432cd9766c5SCameron Grant 	return (error);
433cd9766c5SCameron Grant }
4347029da5cSPawel Biernacki SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans,
4357029da5cSPawel Biernacki     CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
4367029da5cSPawel Biernacki     sysctl_hw_snd_maxautovchans, "I",
4377029da5cSPawel Biernacki     "maximum virtual channel");
438f637a36cSCameron Grant 
439285648f9SCameron Grant struct pcm_channel *
440bba4862cSAriff Abdullah pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo)
441987e5972SCameron Grant {
442bba4862cSAriff Abdullah 	struct pcm_channel *ch;
443bba4862cSAriff Abdullah 	int direction, err, rpnum, *pnum, max;
444bba4862cSAriff Abdullah 	int udc, device, chan;
445bba4862cSAriff Abdullah 	char *dirs, *devname, buf[CHN_NAMELEN];
446bba4862cSAriff Abdullah 
447e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
44890da2b28SAriff Abdullah 	PCM_LOCKASSERT(d);
449bba4862cSAriff Abdullah 	KASSERT(num >= -1, ("invalid num=%d", num));
450bba4862cSAriff Abdullah 
451285648f9SCameron Grant 	switch (dir) {
452285648f9SCameron Grant 	case PCMDIR_PLAY:
453285648f9SCameron Grant 		dirs = "play";
454a3193a9cSDon Lewis 		direction = PCMDIR_PLAY;
455a67fe5c1SCameron Grant 		pnum = &d->playcount;
456bba4862cSAriff Abdullah 		device = SND_DEV_DSPHW_PLAY;
457bba4862cSAriff Abdullah 		max = SND_MAXHWCHAN;
458285648f9SCameron Grant 		break;
459bba4862cSAriff Abdullah 	case PCMDIR_PLAY_VIRTUAL:
460bba4862cSAriff Abdullah 		dirs = "virtual";
461bba4862cSAriff Abdullah 		direction = PCMDIR_PLAY;
462bba4862cSAriff Abdullah 		pnum = &d->pvchancount;
463bba4862cSAriff Abdullah 		device = SND_DEV_DSPHW_VPLAY;
464bba4862cSAriff Abdullah 		max = SND_MAXVCHANS;
465bba4862cSAriff Abdullah 		break;
466285648f9SCameron Grant 	case PCMDIR_REC:
467285648f9SCameron Grant 		dirs = "record";
468a3193a9cSDon Lewis 		direction = PCMDIR_REC;
469a67fe5c1SCameron Grant 		pnum = &d->reccount;
470bba4862cSAriff Abdullah 		device = SND_DEV_DSPHW_REC;
471bba4862cSAriff Abdullah 		max = SND_MAXHWCHAN;
472285648f9SCameron Grant 		break;
473bba4862cSAriff Abdullah 	case PCMDIR_REC_VIRTUAL:
474285648f9SCameron Grant 		dirs = "virtual";
475bba4862cSAriff Abdullah 		direction = PCMDIR_REC;
476bba4862cSAriff Abdullah 		pnum = &d->rvchancount;
477bba4862cSAriff Abdullah 		device = SND_DEV_DSPHW_VREC;
478bba4862cSAriff Abdullah 		max = SND_MAXVCHANS;
479285648f9SCameron Grant 		break;
480285648f9SCameron Grant 	default:
481e4e61333SAriff Abdullah 		return (NULL);
4829c326820SCameron Grant 	}
483285648f9SCameron Grant 
484bba4862cSAriff Abdullah 	chan = (num == -1) ? 0 : num;
485285648f9SCameron Grant 
486e4e61333SAriff Abdullah 	if (*pnum >= max || chan >= max)
487e4e61333SAriff Abdullah 		return (NULL);
488bba4862cSAriff Abdullah 
4893fdb3676SAriff Abdullah 	rpnum = 0;
490bba4862cSAriff Abdullah 
491bba4862cSAriff Abdullah 	CHN_FOREACH(ch, d, channels.pcm) {
492bba4862cSAriff Abdullah 		if (CHN_DEV(ch) != device)
4933fdb3676SAriff Abdullah 			continue;
494bba4862cSAriff Abdullah 		if (chan == CHN_CHAN(ch)) {
495bba4862cSAriff Abdullah 			if (num != -1) {
4963fdb3676SAriff Abdullah 				device_printf(d->dev,
497bba4862cSAriff Abdullah 				    "channel num=%d allocated!\n", chan);
498e4e61333SAriff Abdullah 				return (NULL);
499bba4862cSAriff Abdullah 			}
500bba4862cSAriff Abdullah 			chan++;
501bba4862cSAriff Abdullah 			if (chan >= max) {
502bba4862cSAriff Abdullah 				device_printf(d->dev,
503bba4862cSAriff Abdullah 				    "chan=%d > %d\n", chan, max);
504e4e61333SAriff Abdullah 				return (NULL);
505bba4862cSAriff Abdullah 			}
5063fdb3676SAriff Abdullah 		}
5073fdb3676SAriff Abdullah 		rpnum++;
5083fdb3676SAriff Abdullah 	}
509bba4862cSAriff Abdullah 
5103fdb3676SAriff Abdullah 	if (*pnum != rpnum) {
5113fdb3676SAriff Abdullah 		device_printf(d->dev,
512bba4862cSAriff Abdullah 		    "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n",
5133fdb3676SAriff Abdullah 		    __func__, dirs, *pnum, rpnum);
514e4e61333SAriff Abdullah 		return (NULL);
5153fdb3676SAriff Abdullah 	}
516a67fe5c1SCameron Grant 
517bba4862cSAriff Abdullah 	udc = snd_mkunit(device_get_unit(d->dev), device, chan);
518bba4862cSAriff Abdullah 	devname = dsp_unit2name(buf, sizeof(buf), udc);
519bba4862cSAriff Abdullah 
520bba4862cSAriff Abdullah 	if (devname == NULL) {
521bba4862cSAriff Abdullah 		device_printf(d->dev,
522bba4862cSAriff Abdullah 		    "Failed to query device name udc=0x%08x\n", udc);
523e4e61333SAriff Abdullah 		return (NULL);
524bba4862cSAriff Abdullah 	}
525bba4862cSAriff Abdullah 
52690da2b28SAriff Abdullah 	PCM_UNLOCK(d);
527bba4862cSAriff Abdullah 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
528bba4862cSAriff Abdullah 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
529bba4862cSAriff Abdullah 	ch->unit = udc;
530285648f9SCameron Grant 	ch->pid = -1;
53190da2b28SAriff Abdullah 	strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm));
532285648f9SCameron Grant 	ch->parentsnddev = d;
533285648f9SCameron Grant 	ch->parentchannel = parent;
534436c9b65SScott Long 	ch->dev = d->dev;
535bba4862cSAriff Abdullah 	ch->trigger = PCMTRIG_STOP;
536bba4862cSAriff Abdullah 	snprintf(ch->name, sizeof(ch->name), "%s:%s:%s",
537bba4862cSAriff Abdullah 	    device_get_nameunit(ch->dev), dirs, devname);
538285648f9SCameron Grant 
539a3193a9cSDon Lewis 	err = chn_init(ch, devinfo, dir, direction);
54090da2b28SAriff Abdullah 	PCM_LOCK(d);
5410f55ac6cSCameron Grant 	if (err) {
542bba4862cSAriff Abdullah 		device_printf(d->dev, "chn_init(%s) failed: err = %d\n",
543bba4862cSAriff Abdullah 		    ch->name, err);
544285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
545285648f9SCameron Grant 		free(ch, M_DEVBUF);
546e4e61333SAriff Abdullah 		return (NULL);
547bbb5bf3dSCameron Grant 	}
548285648f9SCameron Grant 
549e4e61333SAriff Abdullah 	return (ch);
550285648f9SCameron Grant }
551285648f9SCameron Grant 
552285648f9SCameron Grant int
553285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
554285648f9SCameron Grant {
555cf8e3ea2SMateusz Guzik 	struct snddev_info *d __diagused;
556285648f9SCameron Grant 	int err;
557285648f9SCameron Grant 
558a67fe5c1SCameron Grant 	d = ch->parentsnddev;
559e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
560e4e61333SAriff Abdullah 
561285648f9SCameron Grant 	err = chn_kill(ch);
562285648f9SCameron Grant 	if (err) {
563e4e61333SAriff Abdullah 		device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n",
564e4e61333SAriff Abdullah 		    ch->name, err);
565e4e61333SAriff Abdullah 		return (err);
566285648f9SCameron Grant 	}
567285648f9SCameron Grant 
568285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
569285648f9SCameron Grant 	free(ch, M_DEVBUF);
570285648f9SCameron Grant 
571e4e61333SAriff Abdullah 	return (0);
572285648f9SCameron Grant }
573285648f9SCameron Grant 
574285648f9SCameron Grant int
5755ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
576285648f9SCameron Grant {
577e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
57890da2b28SAriff Abdullah 	PCM_LOCKASSERT(d);
579bba4862cSAriff Abdullah 	KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
580bba4862cSAriff Abdullah 	    ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
581285648f9SCameron Grant 
58290da2b28SAriff Abdullah 	CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm);
5833fdb3676SAriff Abdullah 
584e4e61333SAriff Abdullah 	switch (CHN_DEV(ch)) {
585e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_PLAY:
586e4e61333SAriff Abdullah 		d->playcount++;
587e4e61333SAriff Abdullah 		break;
588e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_VPLAY:
589e4e61333SAriff Abdullah 		d->pvchancount++;
590e4e61333SAriff Abdullah 		break;
591e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_REC:
592e4e61333SAriff Abdullah 		d->reccount++;
593e4e61333SAriff Abdullah 		break;
594e4e61333SAriff Abdullah 	case SND_DEV_DSPHW_VREC:
595e4e61333SAriff Abdullah 		d->rvchancount++;
596e4e61333SAriff Abdullah 		break;
597e4e61333SAriff Abdullah 	default:
598e4e61333SAriff Abdullah 		break;
599e4e61333SAriff Abdullah 	}
600b8f0d9e0SCameron Grant 
601e4e61333SAriff Abdullah 	d->devcount++;
602e4e61333SAriff Abdullah 
603e4e61333SAriff Abdullah 	return (0);
60433dbf14aSCameron Grant }
60533dbf14aSCameron Grant 
606285648f9SCameron Grant int
6075ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
60833dbf14aSCameron Grant {
609bba4862cSAriff Abdullah 	struct pcm_channel *tmp;
61033dbf14aSCameron Grant 
611e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
61290da2b28SAriff Abdullah 	PCM_LOCKASSERT(d);
613e4e61333SAriff Abdullah 
614bba4862cSAriff Abdullah 	tmp = NULL;
615a527dbc7SCameron Grant 
616bba4862cSAriff Abdullah 	CHN_FOREACH(tmp, d, channels.pcm) {
617bba4862cSAriff Abdullah 		if (tmp == ch)
618bba4862cSAriff Abdullah 			break;
61933dbf14aSCameron Grant 	}
620bba4862cSAriff Abdullah 
621bba4862cSAriff Abdullah 	if (tmp != ch)
622e4e61333SAriff Abdullah 		return (EINVAL);
62367beb5a5SCameron Grant 
624bba4862cSAriff Abdullah 	CHN_REMOVE(d, ch, channels.pcm);
625e4e61333SAriff Abdullah 
626bba4862cSAriff Abdullah 	switch (CHN_DEV(ch)) {
627bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_PLAY:
62867beb5a5SCameron Grant 		d->playcount--;
629bba4862cSAriff Abdullah 		break;
630bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_VPLAY:
631bba4862cSAriff Abdullah 		d->pvchancount--;
632bba4862cSAriff Abdullah 		break;
633bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_REC:
634bba4862cSAriff Abdullah 		d->reccount--;
635bba4862cSAriff Abdullah 		break;
636bba4862cSAriff Abdullah 	case SND_DEV_DSPHW_VREC:
637bba4862cSAriff Abdullah 		d->rvchancount--;
638bba4862cSAriff Abdullah 		break;
639bba4862cSAriff Abdullah 	default:
640bba4862cSAriff Abdullah 		break;
641bba4862cSAriff Abdullah 	}
642285648f9SCameron Grant 
643e4e61333SAriff Abdullah 	d->devcount--;
644e4e61333SAriff Abdullah 
645e4e61333SAriff Abdullah 	return (0);
646987e5972SCameron Grant }
647987e5972SCameron Grant 
648987e5972SCameron Grant int
649285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
650285648f9SCameron Grant {
651285648f9SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
65267b1dce3SCameron Grant 	struct pcm_channel *ch;
65367b1dce3SCameron Grant 	int err;
654285648f9SCameron Grant 
655e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
656e4e61333SAriff Abdullah 
65790da2b28SAriff Abdullah 	PCM_LOCK(d);
658bba4862cSAriff Abdullah 	ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo);
659285648f9SCameron Grant 	if (!ch) {
660e4e61333SAriff Abdullah 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n",
661e4e61333SAriff Abdullah 		    cls->name, dir, devinfo);
66290da2b28SAriff Abdullah 		PCM_UNLOCK(d);
663e4e61333SAriff Abdullah 		return (ENODEV);
664285648f9SCameron Grant 	}
665cd9766c5SCameron Grant 
6665ee30e27SMathew Kanner 	err = pcm_chn_add(d, ch);
66790da2b28SAriff Abdullah 	PCM_UNLOCK(d);
668285648f9SCameron Grant 	if (err) {
669e4e61333SAriff Abdullah 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n",
670e4e61333SAriff Abdullah 		    ch->name, err);
671285648f9SCameron Grant 		pcm_chn_destroy(ch);
672cd9766c5SCameron Grant 	}
673cd9766c5SCameron Grant 
674e4e61333SAriff Abdullah 	return (err);
675285648f9SCameron Grant }
676285648f9SCameron Grant 
677285648f9SCameron Grant static int
678285648f9SCameron Grant pcm_killchan(device_t dev)
679285648f9SCameron Grant {
680285648f9SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
681e33bee07SOlivier Houchard 	struct pcm_channel *ch;
682e4e61333SAriff Abdullah 	int error;
683e4e61333SAriff Abdullah 
684e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
685285648f9SCameron Grant 
686bba4862cSAriff Abdullah 	ch = CHN_FIRST(d, channels.pcm);
687285648f9SCameron Grant 
68890da2b28SAriff Abdullah 	PCM_LOCK(d);
689bba4862cSAriff Abdullah 	error = pcm_chn_remove(d, ch);
69090da2b28SAriff Abdullah 	PCM_UNLOCK(d);
691e33bee07SOlivier Houchard 	if (error)
692e33bee07SOlivier Houchard 		return (error);
693e33bee07SOlivier Houchard 	return (pcm_chn_destroy(ch));
694285648f9SCameron Grant }
695285648f9SCameron Grant 
696cbebc90dSAlexander Motin static int
697cbebc90dSAlexander Motin pcm_best_unit(int old)
698cbebc90dSAlexander Motin {
699cbebc90dSAlexander Motin 	struct snddev_info *d;
700cbebc90dSAlexander Motin 	int i, best, bestprio, prio;
701cbebc90dSAlexander Motin 
702cbebc90dSAlexander Motin 	best = -1;
703cbebc90dSAlexander Motin 	bestprio = -100;
704cbebc90dSAlexander Motin 	for (i = 0; pcm_devclass != NULL &&
705cbebc90dSAlexander Motin 	    i < devclass_get_maxunit(pcm_devclass); i++) {
706cbebc90dSAlexander Motin 		d = devclass_get_softc(pcm_devclass, i);
707cbebc90dSAlexander Motin 		if (!PCM_REGISTERED(d))
708cbebc90dSAlexander Motin 			continue;
709cbebc90dSAlexander Motin 		prio = 0;
710cbebc90dSAlexander Motin 		if (d->playcount == 0)
711cbebc90dSAlexander Motin 			prio -= 10;
712cbebc90dSAlexander Motin 		if (d->reccount == 0)
713cbebc90dSAlexander Motin 			prio -= 2;
714cbebc90dSAlexander Motin 		if (prio > bestprio || (prio == bestprio && i == old)) {
715cbebc90dSAlexander Motin 			best = i;
716cbebc90dSAlexander Motin 			bestprio = prio;
717cbebc90dSAlexander Motin 		}
718cbebc90dSAlexander Motin 	}
719cbebc90dSAlexander Motin 	return (best);
720cbebc90dSAlexander Motin }
721cbebc90dSAlexander Motin 
722285648f9SCameron Grant int
723987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
724987e5972SCameron Grant {
72566ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
72649c5e6e2SCameron Grant 
72732a0e5d5SHans Petter Selasky 	/* should only be called once */
72832a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_REGISTERED)
72932a0e5d5SHans Petter Selasky 		return (EINVAL);
73032a0e5d5SHans Petter Selasky 
731e4e61333SAriff Abdullah 	PCM_BUSYASSERT(d);
732bba4862cSAriff Abdullah 
733e4e61333SAriff Abdullah 	if (d->playcount == 0 || d->reccount == 0)
734e4e61333SAriff Abdullah 		d->flags |= SD_F_SIMPLEX;
735e4e61333SAriff Abdullah 
73632a0e5d5SHans Petter Selasky 	if (d->playcount > 0 || d->reccount > 0)
737e4e61333SAriff Abdullah 		d->flags |= SD_F_AUTOVCHAN;
738e4e61333SAriff Abdullah 
739e4e61333SAriff Abdullah 	pcm_setmaxautovchans(d, snd_maxautovchans);
740bba4862cSAriff Abdullah 
741a580b31aSAriff Abdullah 	strlcpy(d->status, str, SND_STATUSLEN);
742bba4862cSAriff Abdullah 
74390da2b28SAriff Abdullah 	PCM_LOCK(d);
744e4e61333SAriff Abdullah 
745e4e61333SAriff Abdullah 	/* Done, we're ready.. */
746e4e61333SAriff Abdullah 	d->flags |= SD_F_REGISTERED;
747e4e61333SAriff Abdullah 
748e4e61333SAriff Abdullah 	PCM_RELEASE(d);
749bba4862cSAriff Abdullah 
75090da2b28SAriff Abdullah 	PCM_UNLOCK(d);
751bba4862cSAriff Abdullah 
75232a0e5d5SHans Petter Selasky 	/*
75332a0e5d5SHans Petter Selasky 	 * Create all sysctls once SD_F_REGISTERED is set else
75432a0e5d5SHans Petter Selasky 	 * tunable sysctls won't work:
75532a0e5d5SHans Petter Selasky 	 */
75632a0e5d5SHans Petter Selasky 	pcm_sysinit(dev);
75732a0e5d5SHans Petter Selasky 
758cbebc90dSAlexander Motin 	if (snd_unit_auto < 0)
759cbebc90dSAlexander Motin 		snd_unit_auto = (snd_unit < 0) ? 1 : 0;
760cbebc90dSAlexander Motin 	if (snd_unit < 0 || snd_unit_auto > 1)
761f3685841SAriff Abdullah 		snd_unit = device_get_unit(dev);
762cbebc90dSAlexander Motin 	else if (snd_unit_auto == 1)
763cbebc90dSAlexander Motin 		snd_unit = pcm_best_unit(snd_unit);
764f3685841SAriff Abdullah 
765e4e61333SAriff Abdullah 	return (0);
766987e5972SCameron Grant }
767987e5972SCameron Grant 
768a1d444e1SAriff Abdullah uint32_t
769987e5972SCameron Grant pcm_getflags(device_t dev)
770987e5972SCameron Grant {
77166ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
77249c5e6e2SCameron Grant 
773987e5972SCameron Grant 	return d->flags;
774987e5972SCameron Grant }
775987e5972SCameron Grant 
776987e5972SCameron Grant void
777a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val)
778987e5972SCameron Grant {
77966ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
780d95502a8SCameron Grant 
781987e5972SCameron Grant 	d->flags = val;
782987e5972SCameron Grant }
783987e5972SCameron Grant 
78439004e69SCameron Grant void *
78539004e69SCameron Grant pcm_getdevinfo(device_t dev)
78639004e69SCameron Grant {
78766ef8af5SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
78849c5e6e2SCameron Grant 
78939004e69SCameron Grant 	return d->devinfo;
79039004e69SCameron Grant }
79139004e69SCameron Grant 
792a67fe5c1SCameron Grant unsigned int
793d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
794a67fe5c1SCameron Grant {
795a67fe5c1SCameron Grant 	struct snddev_info *d = device_get_softc(dev);
7964e60be34SCameron Grant 	int sz, x;
797a67fe5c1SCameron Grant 
798a67fe5c1SCameron Grant 	sz = 0;
7994e60be34SCameron Grant 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
8004e60be34SCameron Grant 		x = sz;
801d55d96f6SAlexander Leidinger 		RANGE(sz, minbufsz, maxbufsz);
8024e60be34SCameron Grant 		if (x != sz)
803d55d96f6SAlexander Leidinger 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
804d55d96f6SAlexander Leidinger 		x = minbufsz;
8054e60be34SCameron Grant 		while (x < sz)
8064e60be34SCameron Grant 			x <<= 1;
8074e60be34SCameron Grant 		if (x > sz)
8084e60be34SCameron Grant 			x >>= 1;
8094e60be34SCameron Grant 		if (x != sz) {
8104e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
8114e60be34SCameron Grant 			sz = x;
8124e60be34SCameron Grant 		}
8134e60be34SCameron Grant 	} else {
814a67fe5c1SCameron Grant 		sz = deflt;
8154e60be34SCameron Grant 	}
8164e60be34SCameron Grant 
817a67fe5c1SCameron Grant 	d->bufsz = sz;
818a67fe5c1SCameron Grant 
819a67fe5c1SCameron Grant 	return sz;
820a67fe5c1SCameron Grant }
821a67fe5c1SCameron Grant 
82290da2b28SAriff Abdullah static int
82390da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)
82490da2b28SAriff Abdullah {
82590da2b28SAriff Abdullah 	struct snddev_info *d;
82690da2b28SAriff Abdullah 	int err, val;
82790da2b28SAriff Abdullah 
82890da2b28SAriff Abdullah 	d = oidp->oid_arg1;
82990da2b28SAriff Abdullah 	if (!PCM_REGISTERED(d))
83090da2b28SAriff Abdullah 		return (ENODEV);
83190da2b28SAriff Abdullah 
83290da2b28SAriff Abdullah 	PCM_LOCK(d);
83390da2b28SAriff Abdullah 	PCM_WAIT(d);
83490da2b28SAriff Abdullah 	val = (d->flags & SD_F_BITPERFECT) ? 1 : 0;
83590da2b28SAriff Abdullah 	PCM_ACQUIRE(d);
83690da2b28SAriff Abdullah 	PCM_UNLOCK(d);
83790da2b28SAriff Abdullah 
83890da2b28SAriff Abdullah 	err = sysctl_handle_int(oidp, &val, 0, req);
83990da2b28SAriff Abdullah 
84090da2b28SAriff Abdullah 	if (err == 0 && req->newptr != NULL) {
84190da2b28SAriff Abdullah 		if (!(val == 0 || val == 1)) {
84290da2b28SAriff Abdullah 			PCM_RELEASE_QUICK(d);
84390da2b28SAriff Abdullah 			return (EINVAL);
84490da2b28SAriff Abdullah 		}
84590da2b28SAriff Abdullah 
84690da2b28SAriff Abdullah 		PCM_LOCK(d);
84790da2b28SAriff Abdullah 
84890da2b28SAriff Abdullah 		d->flags &= ~SD_F_BITPERFECT;
84990da2b28SAriff Abdullah 		d->flags |= (val != 0) ? SD_F_BITPERFECT : 0;
85090da2b28SAriff Abdullah 
85190da2b28SAriff Abdullah 		PCM_RELEASE(d);
85290da2b28SAriff Abdullah 		PCM_UNLOCK(d);
85390da2b28SAriff Abdullah 	} else
85490da2b28SAriff Abdullah 		PCM_RELEASE_QUICK(d);
85590da2b28SAriff Abdullah 
85690da2b28SAriff Abdullah 	return (err);
85790da2b28SAriff Abdullah }
85890da2b28SAriff Abdullah 
859ed2196e5SHans Petter Selasky static u_int8_t
860ed2196e5SHans Petter Selasky pcm_mode_init(struct snddev_info *d)
861ed2196e5SHans Petter Selasky {
862ed2196e5SHans Petter Selasky 	u_int8_t mode = 0;
863ed2196e5SHans Petter Selasky 
864ed2196e5SHans Petter Selasky 	if (d->playcount > 0)
865ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_PLAY;
866ed2196e5SHans Petter Selasky 	if (d->reccount > 0)
867ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_REC;
868ed2196e5SHans Petter Selasky 	if (d->mixer_dev != NULL)
869ed2196e5SHans Petter Selasky 		mode |= PCM_MODE_MIXER;
870ed2196e5SHans Petter Selasky 
871ed2196e5SHans Petter Selasky 	return (mode);
872ed2196e5SHans Petter Selasky }
873ed2196e5SHans Petter Selasky 
87432a0e5d5SHans Petter Selasky static void
87532a0e5d5SHans Petter Selasky pcm_sysinit(device_t dev)
87632a0e5d5SHans Petter Selasky {
87732a0e5d5SHans Petter Selasky   	struct snddev_info *d = device_get_softc(dev);
878ed2196e5SHans Petter Selasky 	u_int8_t mode;
879ed2196e5SHans Petter Selasky 
880ed2196e5SHans Petter Selasky 	mode = pcm_mode_init(d);
88132a0e5d5SHans Petter Selasky 
882132fca63SHans Petter Selasky 	/* XXX: a user should be able to set this with a control tool, the
88332a0e5d5SHans Petter Selasky 	   sysadmin then needs min+max sysctls for this */
88432a0e5d5SHans Petter Selasky 	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
88532a0e5d5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
88632a0e5d5SHans Petter Selasky             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
88732a0e5d5SHans Petter Selasky 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
88832a0e5d5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
8893b4c5433SAlexander Motin 	    "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d,
8907029da5cSPawel Biernacki 	    sizeof(d), sysctl_dev_pcm_bitperfect, "I",
89132a0e5d5SHans Petter Selasky 	    "bit-perfect playback/recording (0=disable, 1=enable)");
892ed2196e5SHans Petter Selasky 	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
893ed2196e5SHans Petter Selasky 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
894ed2196e5SHans Petter Selasky 	    OID_AUTO, "mode", CTLFLAG_RD, NULL, mode,
895ed2196e5SHans Petter Selasky 	    "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than one"
896ed2196e5SHans Petter Selasky 	    "mode is supported)");
89732a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_AUTOVCHAN)
89832a0e5d5SHans Petter Selasky 		vchan_initsys(dev);
89932a0e5d5SHans Petter Selasky 	if (d->flags & SD_F_EQ)
90032a0e5d5SHans Petter Selasky 		feeder_eq_initsys(dev);
90132a0e5d5SHans Petter Selasky }
90232a0e5d5SHans Petter Selasky 
903987e5972SCameron Grant int
904987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
905987e5972SCameron Grant {
906bba4862cSAriff Abdullah 	struct snddev_info *d;
90790da2b28SAriff Abdullah 	int i;
908987e5972SCameron Grant 
909b8a36395SCameron Grant 	if (pcm_veto_load) {
910b8a36395SCameron Grant 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
911b8a36395SCameron Grant 
912b8a36395SCameron Grant 		return EINVAL;
913b8a36395SCameron Grant 	}
914b8a36395SCameron Grant 
915bba4862cSAriff Abdullah 	if (device_get_unit(dev) > PCMMAXUNIT) {
916bba4862cSAriff Abdullah 		device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n",
917bba4862cSAriff Abdullah 		    device_get_unit(dev), PCMMAXUNIT);
918bba4862cSAriff Abdullah 		device_printf(dev,
919bba4862cSAriff Abdullah 		    "Use 'hw.snd.maxunit' tunable to raise the limit.\n");
920bba4862cSAriff Abdullah 		return ENODEV;
921bba4862cSAriff Abdullah 	}
922bba4862cSAriff Abdullah 
923bba4862cSAriff Abdullah 	d = device_get_softc(dev);
924e4e61333SAriff Abdullah 	d->dev = dev;
9252c69ba87SJohn Baldwin 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
926e4e61333SAriff Abdullah 	cv_init(&d->cv, device_get_nameunit(dev));
927e4e61333SAriff Abdullah 	PCM_ACQUIRE_QUICK(d);
9287233ababSAlexander Leidinger #if 0
9297233ababSAlexander Leidinger 	/*
9307233ababSAlexander Leidinger 	 * d->flags should be cleared by the allocator of the softc.
9317233ababSAlexander Leidinger 	 * We cannot clear this field here because several devices set
9327233ababSAlexander Leidinger 	 * this flag before calling pcm_register().
9337233ababSAlexander Leidinger 	 */
934cd9766c5SCameron Grant 	d->flags = 0;
9357233ababSAlexander Leidinger #endif
93690da2b28SAriff Abdullah 	i = 0;
93790da2b28SAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
93890da2b28SAriff Abdullah 	    "vpc", &i) != 0 || i != 0)
93990da2b28SAriff Abdullah 		d->flags |= SD_F_VPC;
94090da2b28SAriff Abdullah 
94190da2b28SAriff Abdullah 	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
94290da2b28SAriff Abdullah 	    "bitperfect", &i) == 0 && i != 0)
94390da2b28SAriff Abdullah 		d->flags |= SD_F_BITPERFECT;
94490da2b28SAriff Abdullah 
945987e5972SCameron Grant 	d->devinfo = devinfo;
946f637a36cSCameron Grant 	d->devcount = 0;
947506a5308SCameron Grant 	d->reccount = 0;
948a67fe5c1SCameron Grant 	d->playcount = 0;
949bba4862cSAriff Abdullah 	d->pvchancount = 0;
950bba4862cSAriff Abdullah 	d->rvchancount = 0;
951bba4862cSAriff Abdullah 	d->pvchanrate = 0;
952bba4862cSAriff Abdullah 	d->pvchanformat = 0;
953bba4862cSAriff Abdullah 	d->rvchanrate = 0;
954bba4862cSAriff Abdullah 	d->rvchanformat = 0;
955833f7023SCameron Grant 
956bba4862cSAriff Abdullah 	CHN_INIT(d, channels.pcm);
957bba4862cSAriff Abdullah 	CHN_INIT(d, channels.pcm.busy);
95890da2b28SAriff Abdullah 	CHN_INIT(d, channels.pcm.opened);
95945550658SPoul-Henning Kamp 
960e4e61333SAriff Abdullah 	/* XXX This is incorrect, but lets play along for now. */
961a1d444e1SAriff Abdullah 	if ((numplay == 0 || numrec == 0) && numplay != numrec)
962285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
963d95502a8SCameron Grant 
964bba4862cSAriff Abdullah 	sysctl_ctx_init(&d->play_sysctl_ctx);
965bba4862cSAriff Abdullah 	d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
966bba4862cSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
9677029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node");
968bba4862cSAriff Abdullah 	sysctl_ctx_init(&d->rec_sysctl_ctx);
969bba4862cSAriff Abdullah 	d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
970bba4862cSAriff Abdullah 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
971132fca63SHans Petter Selasky 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node");
972e4e61333SAriff Abdullah 
97332a0e5d5SHans Petter Selasky 	if (numplay > 0 || numrec > 0)
974cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
97590da2b28SAriff Abdullah 
9769da3b645SChristos Margiolis 	sndstat_register(dev, d->status);
977e4e61333SAriff Abdullah 
978e8c0d15aSChristos Margiolis 	return (dsp_make_dev(dev));
979987e5972SCameron Grant }
980987e5972SCameron Grant 
98133dbf14aSCameron Grant int
98233dbf14aSCameron Grant pcm_unregister(device_t dev)
9837c438dbeSCameron Grant {
984e4e61333SAriff Abdullah 	struct snddev_info *d;
985a67fe5c1SCameron Grant 	struct pcm_channel *ch;
9867c438dbeSCameron Grant 
987e4e61333SAriff Abdullah 	d = device_get_softc(dev);
988e4e61333SAriff Abdullah 
989e4e61333SAriff Abdullah 	if (!PCM_ALIVE(d)) {
990e4e61333SAriff Abdullah 		device_printf(dev, "unregister: device not configured\n");
991e4e61333SAriff Abdullah 		return (0);
992e4e61333SAriff Abdullah 	}
993bba4862cSAriff Abdullah 
99490da2b28SAriff Abdullah 	PCM_LOCK(d);
995e4e61333SAriff Abdullah 	PCM_WAIT(d);
996e4e61333SAriff Abdullah 
997cc1efc23SHans Petter Selasky 	d->flags |= SD_F_DETACHING;
998cc1efc23SHans Petter Selasky 
999e4e61333SAriff Abdullah 	PCM_ACQUIRE(d);
100090da2b28SAriff Abdullah 	PCM_UNLOCK(d);
1001e4e61333SAriff Abdullah 
1002e4e61333SAriff Abdullah 	CHN_FOREACH(ch, d, channels.pcm) {
1003e4e61333SAriff Abdullah 		CHN_LOCK(ch);
1004*44e128feSChristos Margiolis 		if (ch->flags & CHN_F_SLEEPING) {
1005*44e128feSChristos Margiolis 			/*
1006*44e128feSChristos Margiolis 			 * We are detaching, so do not wait for the timeout in
1007*44e128feSChristos Margiolis 			 * chn_read()/chn_write(). Wake up the thread and kill
1008*44e128feSChristos Margiolis 			 * the channel immediately.
1009*44e128feSChristos Margiolis 			 */
1010*44e128feSChristos Margiolis 			CHN_BROADCAST(&ch->intr_cv);
1011*44e128feSChristos Margiolis 			ch->flags |= CHN_F_DEAD;
1012285648f9SCameron Grant 		}
1013*44e128feSChristos Margiolis 		chn_abort(ch);
1014e4e61333SAriff Abdullah 		CHN_UNLOCK(ch);
1015c9b53085SCameron Grant 	}
10165ee30e27SMathew Kanner 
1017e8c0d15aSChristos Margiolis 	dsp_destroy_dev(dev);
1018bba4862cSAriff Abdullah 
1019*44e128feSChristos Margiolis 	(void)mixer_uninit(dev);
10207233ababSAlexander Leidinger 
10218461d581SHans Petter Selasky 	/* remove /dev/sndstat entry first */
10228461d581SHans Petter Selasky 	sndstat_unregister(dev);
10238461d581SHans Petter Selasky 
102490da2b28SAriff Abdullah 	PCM_LOCK(d);
1025e4e61333SAriff Abdullah 	d->flags |= SD_F_DYING;
1026e4e61333SAriff Abdullah 	d->flags &= ~SD_F_REGISTERED;
102790da2b28SAriff Abdullah 	PCM_UNLOCK(d);
1028e4e61333SAriff Abdullah 
1029bba4862cSAriff Abdullah 	if (d->play_sysctl_tree != NULL) {
1030bba4862cSAriff Abdullah 		sysctl_ctx_free(&d->play_sysctl_ctx);
1031bba4862cSAriff Abdullah 		d->play_sysctl_tree = NULL;
1032bba4862cSAriff Abdullah 	}
1033bba4862cSAriff Abdullah 	if (d->rec_sysctl_tree != NULL) {
1034bba4862cSAriff Abdullah 		sysctl_ctx_free(&d->rec_sysctl_ctx);
1035bba4862cSAriff Abdullah 		d->rec_sysctl_tree = NULL;
1036a1d444e1SAriff Abdullah 	}
1037bba4862cSAriff Abdullah 
1038bba4862cSAriff Abdullah 	while (!CHN_EMPTY(d, channels.pcm))
1039285648f9SCameron Grant 		pcm_killchan(dev);
10407c438dbeSCameron Grant 
104190da2b28SAriff Abdullah 	PCM_LOCK(d);
1042e4e61333SAriff Abdullah 	PCM_RELEASE(d);
1043e4e61333SAriff Abdullah 	cv_destroy(&d->cv);
104490da2b28SAriff Abdullah 	PCM_UNLOCK(d);
104549c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
1046bba4862cSAriff Abdullah 
1047bba4862cSAriff Abdullah 	if (snd_unit == device_get_unit(dev)) {
1048cbebc90dSAlexander Motin 		snd_unit = pcm_best_unit(-1);
1049cbebc90dSAlexander Motin 		if (snd_unit_auto == 0)
1050cbebc90dSAlexander Motin 			snd_unit_auto = 1;
1051e4e61333SAriff Abdullah 	}
1052bba4862cSAriff Abdullah 
1053e4e61333SAriff Abdullah 	return (0);
105433dbf14aSCameron Grant }
10557c438dbeSCameron Grant 
105667b1dce3SCameron Grant /************************************************************************/
105767b1dce3SCameron Grant 
1058b611c801SAlexander Leidinger /**
1059b611c801SAlexander Leidinger  * @brief	Handle OSSv4 SNDCTL_SYSINFO ioctl.
1060b611c801SAlexander Leidinger  *
1061b611c801SAlexander Leidinger  * @param si	Pointer to oss_sysinfo struct where information about the
1062b611c801SAlexander Leidinger  * 		sound subsystem will be written/copied.
1063b611c801SAlexander Leidinger  *
1064b611c801SAlexander Leidinger  * This routine returns information about the sound system, such as the
1065b611c801SAlexander Leidinger  * current OSS version, number of audio, MIDI, and mixer drivers, etc.
1066b611c801SAlexander Leidinger  * Also includes a bitmask showing which of the above types of devices
1067b611c801SAlexander Leidinger  * are open (busy).
1068b611c801SAlexander Leidinger  *
1069b611c801SAlexander Leidinger  * @note
1070b611c801SAlexander Leidinger  * Calling threads must not hold any snddev_info or pcm_channel locks.
1071b611c801SAlexander Leidinger  *
1072b611c801SAlexander Leidinger  * @author	Ryan Beasley <ryanb@FreeBSD.org>
1073b611c801SAlexander Leidinger  */
1074b611c801SAlexander Leidinger void
1075b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si)
1076b611c801SAlexander Leidinger {
1077b611c801SAlexander Leidinger 	static char si_product[] = "FreeBSD native OSS ABI";
1078b611c801SAlexander Leidinger 	static char si_version[] = __XSTRING(__FreeBSD_version);
1079c412d503SAlexander Motin 	static char si_license[] = "BSD";
1080b611c801SAlexander Leidinger 	static int intnbits = sizeof(int) * 8;	/* Better suited as macro?
1081b611c801SAlexander Leidinger 						   Must pester a C guru. */
1082b611c801SAlexander Leidinger 
1083b611c801SAlexander Leidinger 	struct snddev_info *d;
1084b611c801SAlexander Leidinger 	struct pcm_channel *c;
1085b611c801SAlexander Leidinger 	int i, j, ncards;
1086b611c801SAlexander Leidinger 
1087b611c801SAlexander Leidinger 	ncards = 0;
1088b611c801SAlexander Leidinger 
1089b611c801SAlexander Leidinger 	strlcpy(si->product, si_product, sizeof(si->product));
1090b611c801SAlexander Leidinger 	strlcpy(si->version, si_version, sizeof(si->version));
1091b611c801SAlexander Leidinger 	si->versionnum = SOUND_VERSION;
1092c412d503SAlexander Motin 	strlcpy(si->license, si_license, sizeof(si->license));
1093b611c801SAlexander Leidinger 
1094b611c801SAlexander Leidinger 	/*
1095b611c801SAlexander Leidinger 	 * Iterate over PCM devices and their channels, gathering up data
1096b611c801SAlexander Leidinger 	 * for the numaudios, ncards, and openedaudio fields.
1097b611c801SAlexander Leidinger 	 */
1098b611c801SAlexander Leidinger 	si->numaudios = 0;
1099b611c801SAlexander Leidinger 	bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
1100b611c801SAlexander Leidinger 
1101b611c801SAlexander Leidinger 	j = 0;
1102b611c801SAlexander Leidinger 
11039c271f79SAriff Abdullah 	for (i = 0; pcm_devclass != NULL &&
11049c271f79SAriff Abdullah 	    i < devclass_get_maxunit(pcm_devclass); i++) {
1105b611c801SAlexander Leidinger 		d = devclass_get_softc(pcm_devclass, i);
1106e4e61333SAriff Abdullah 		if (!PCM_REGISTERED(d))
1107b611c801SAlexander Leidinger 			continue;
1108b611c801SAlexander Leidinger 
1109e4e61333SAriff Abdullah 		/* XXX Need Giant magic entry ??? */
1110e4e61333SAriff Abdullah 
1111b611c801SAlexander Leidinger 		/* See note in function's docblock */
111290da2b28SAriff Abdullah 		PCM_UNLOCKASSERT(d);
111390da2b28SAriff Abdullah 		PCM_LOCK(d);
1114b611c801SAlexander Leidinger 
1115b611c801SAlexander Leidinger 		si->numaudios += d->devcount;
1116b611c801SAlexander Leidinger 		++ncards;
1117b611c801SAlexander Leidinger 
1118bba4862cSAriff Abdullah 		CHN_FOREACH(c, d, channels.pcm) {
111990da2b28SAriff Abdullah 			CHN_UNLOCKASSERT(c);
1120b611c801SAlexander Leidinger 			CHN_LOCK(c);
1121b611c801SAlexander Leidinger 			if (c->flags & CHN_F_BUSY)
1122b611c801SAlexander Leidinger 				si->openedaudio[j / intnbits] |=
1123b611c801SAlexander Leidinger 				    (1 << (j % intnbits));
1124b611c801SAlexander Leidinger 			CHN_UNLOCK(c);
1125b611c801SAlexander Leidinger 			j++;
1126b611c801SAlexander Leidinger 		}
1127b611c801SAlexander Leidinger 
112890da2b28SAriff Abdullah 		PCM_UNLOCK(d);
1129b611c801SAlexander Leidinger 	}
1130c412d503SAlexander Motin 	si->numaudioengines = si->numaudios;
1131b611c801SAlexander Leidinger 
1132b611c801SAlexander Leidinger 	si->numsynths = 0;	/* OSSv4 docs:  this field is obsolete */
1133b611c801SAlexander Leidinger 	/**
1134b611c801SAlexander Leidinger 	 * @todo	Collect num{midis,timers}.
1135b611c801SAlexander Leidinger 	 *
1136b611c801SAlexander Leidinger 	 * Need access to sound/midi/midi.c::midistat_lock in order
1137b611c801SAlexander Leidinger 	 * to safely touch midi_devices and get a head count of, well,
1138b611c801SAlexander Leidinger 	 * MIDI devices.  midistat_lock is a global static (i.e., local to
1139b611c801SAlexander Leidinger 	 * midi.c), but midi_devices is a regular global; should the mutex
1140b611c801SAlexander Leidinger 	 * be publicized, or is there another way to get this information?
1141b611c801SAlexander Leidinger 	 *
1142b611c801SAlexander Leidinger 	 * NB:	MIDI/sequencer stuff is currently on hold.
1143b611c801SAlexander Leidinger 	 */
1144b611c801SAlexander Leidinger 	si->nummidis = 0;
1145b611c801SAlexander Leidinger 	si->numtimers = 0;
1146b611c801SAlexander Leidinger 	si->nummixers = mixer_count;
1147b611c801SAlexander Leidinger 	si->numcards = ncards;
1148b611c801SAlexander Leidinger 		/* OSSv4 docs:	Intended only for test apps; API doesn't
1149b611c801SAlexander Leidinger 		   really have much of a concept of cards.  Shouldn't be
1150b611c801SAlexander Leidinger 		   used by applications. */
1151b611c801SAlexander Leidinger 
1152b611c801SAlexander Leidinger 	/**
1153b611c801SAlexander Leidinger 	 * @todo	Fill in "busy devices" fields.
1154b611c801SAlexander Leidinger 	 *
1155b611c801SAlexander Leidinger 	 *  si->openedmidi = " MIDI devices
1156b611c801SAlexander Leidinger 	 */
1157b611c801SAlexander Leidinger 	bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
1158b611c801SAlexander Leidinger 
1159b611c801SAlexander Leidinger 	/*
1160b611c801SAlexander Leidinger 	 * Si->filler is a reserved array, but according to docs each
1161b611c801SAlexander Leidinger 	 * element should be set to -1.
1162b611c801SAlexander Leidinger 	 */
1163b611c801SAlexander Leidinger 	for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++)
1164b611c801SAlexander Leidinger 		si->filler[i] = -1;
1165b611c801SAlexander Leidinger }
1166b611c801SAlexander Leidinger 
116752f6e09eSAlexander Motin int
116852f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si)
116952f6e09eSAlexander Motin {
117052f6e09eSAlexander Motin 	struct snddev_info *d;
117152f6e09eSAlexander Motin 	int i, ncards;
117252f6e09eSAlexander Motin 
117352f6e09eSAlexander Motin 	ncards = 0;
117452f6e09eSAlexander Motin 
117552f6e09eSAlexander Motin 	for (i = 0; pcm_devclass != NULL &&
117652f6e09eSAlexander Motin 	    i < devclass_get_maxunit(pcm_devclass); i++) {
117752f6e09eSAlexander Motin 		d = devclass_get_softc(pcm_devclass, i);
117852f6e09eSAlexander Motin 		if (!PCM_REGISTERED(d))
117952f6e09eSAlexander Motin 			continue;
118052f6e09eSAlexander Motin 
118152f6e09eSAlexander Motin 		if (ncards++ != si->card)
118252f6e09eSAlexander Motin 			continue;
118352f6e09eSAlexander Motin 
118490da2b28SAriff Abdullah 		PCM_UNLOCKASSERT(d);
118590da2b28SAriff Abdullah 		PCM_LOCK(d);
118652f6e09eSAlexander Motin 
118752f6e09eSAlexander Motin 		strlcpy(si->shortname, device_get_nameunit(d->dev),
118852f6e09eSAlexander Motin 		    sizeof(si->shortname));
118952f6e09eSAlexander Motin 		strlcpy(si->longname, device_get_desc(d->dev),
119052f6e09eSAlexander Motin 		    sizeof(si->longname));
119152f6e09eSAlexander Motin 		strlcpy(si->hw_info, d->status, sizeof(si->hw_info));
119252f6e09eSAlexander Motin 		si->intr_count = si->ack_count = 0;
119390da2b28SAriff Abdullah 
119490da2b28SAriff Abdullah 		PCM_UNLOCK(d);
119590da2b28SAriff Abdullah 
119652f6e09eSAlexander Motin 		return (0);
119752f6e09eSAlexander Motin 	}
119852f6e09eSAlexander Motin 	return (ENXIO);
119952f6e09eSAlexander Motin }
120052f6e09eSAlexander Motin 
1201b611c801SAlexander Leidinger /************************************************************************/
1202b611c801SAlexander Leidinger 
12030739ea1dSSeigo Tanimura static int
12040739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data)
12050739ea1dSSeigo Tanimura {
1206b611c801SAlexander Leidinger 	int ret;
1207b611c801SAlexander Leidinger 
12088109ec9dSJohn Baldwin 	ret = 0;
1209b611c801SAlexander Leidinger 	switch (type) {
1210b611c801SAlexander Leidinger 		case MOD_LOAD:
121113bebcd3SJohn Baldwin 			pcm_devclass = devclass_create("pcm");
1212b611c801SAlexander Leidinger 			pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
1213b611c801SAlexander Leidinger 			break;
1214b611c801SAlexander Leidinger 		case MOD_UNLOAD:
1215b611c801SAlexander Leidinger 			if (pcmsg_unrhdr != NULL) {
1216b611c801SAlexander Leidinger 				delete_unrhdr(pcmsg_unrhdr);
1217b611c801SAlexander Leidinger 				pcmsg_unrhdr = NULL;
1218b611c801SAlexander Leidinger 			}
1219b611c801SAlexander Leidinger 			break;
12205525b53dSAlexander Motin 		case MOD_SHUTDOWN:
12215525b53dSAlexander Motin 			break;
1222b611c801SAlexander Leidinger 		default:
122390da2b28SAriff Abdullah 			ret = ENOTSUP;
1224b611c801SAlexander Leidinger 	}
1225b611c801SAlexander Leidinger 
1226b611c801SAlexander Leidinger 	return ret;
12270739ea1dSSeigo Tanimura }
12280739ea1dSSeigo Tanimura 
12290739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL);
12300739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER);
1231