1139749Simp/*-
2193640Sariff * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
3193640Sariff * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
4193640Sariff * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
5192920Sjoel * Copyright (c) 1997 Luigi Rizzo
650724Scg * All rights reserved.
750724Scg *
850724Scg * Redistribution and use in source and binary forms, with or without
950724Scg * modification, are permitted provided that the following conditions
1050724Scg * are met:
1150724Scg * 1. Redistributions of source code must retain the above copyright
1250724Scg *    notice, this list of conditions and the following disclaimer.
1350724Scg * 2. Redistributions in binary form must reproduce the above copyright
1450724Scg *    notice, this list of conditions and the following disclaimer in the
1550724Scg *    documentation and/or other materials provided with the distribution.
1650724Scg *
1750724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1850724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1950724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2050724Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2150724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2250724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2350724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2450724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2550724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2650724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2750724Scg * SUCH DAMAGE.
2850724Scg */
2950724Scg
30193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS
31193640Sariff#include "opt_snd.h"
32193640Sariff#endif
33193640Sariff
3453465Scg#include <dev/sound/pcm/sound.h>
35164614Sariff#include <dev/sound/pcm/ac97.h>
3677269Scg#include <dev/sound/pcm/vchan.h>
37124740Smatk#include <dev/sound/pcm/dsp.h>
38193640Sariff#include <dev/sound/pcm/sndstat.h>
39170161Sariff#include <dev/sound/version.h>
40162588Snetchild#include <sys/limits.h>
4165207Scg#include <sys/sysctl.h>
4250724Scg
4382180Scg#include "feeder_if.h"
4482180Scg
4582180ScgSND_DECLARE_FILE("$FreeBSD: stable/10/sys/dev/sound/pcm/sound.c 358879 2020-03-11 08:26:11Z hselasky $");
4682180Scg
4778362Scgdevclass_t pcm_devclass;
4850724Scg
4989834Scgint pcm_veto_load = 1;
5089834Scg
51170884Sariffint snd_unit = -1;
52159732SnetchildTUNABLE_INT("hw.snd.default_unit", &snd_unit);
5379141Scg
54222826Smavstatic int snd_unit_auto = -1;
55170885SariffTUNABLE_INT("hw.snd.default_auto", &snd_unit_auto);
56170885SariffSYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RW,
57170893Sariff    &snd_unit_auto, 0, "assign default unit to a newly attached device");
58170885Sariff
59170161Sariffint snd_maxautovchans = 16;
60159732Snetchild/* XXX: a tunable implies that we may need more than one sound channel before
61159732Snetchild   the system can change a sysctl (/etc/sysctl.conf), do we really need
62159732Snetchild   this? */
6382180ScgTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
6450724Scg
6573127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
6673127Scg
67170161Sariff/*
68170161Sariff * XXX I've had enough with people not telling proper version/arch
69170161Sariff *     while reporting problems, not after 387397913213th questions/requests.
70170161Sariff */
71209193Savgstatic char snd_driver_version[] =
72170161Sariff    __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH;
73170161SariffSYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version,
74193640Sariff    0, "driver version/arch");
75170161Sariff
76162588Snetchild/**
77162588Snetchild * @brief Unit number allocator for syncgroup IDs
78162588Snetchild */
79162588Snetchildstruct unrhdr *pcmsg_unrhdr = NULL;
80162588Snetchild
81193640Sariffstatic int
82193640Sariffsndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS)
83193640Sariff{
84193640Sariff	SNDSTAT_PREPARE_PCM_BEGIN();
85193640Sariff	SNDSTAT_PREPARE_PCM_END();
86193640Sariff}
8782180Scg
8873131Scgvoid *
8993814Sjhbsnd_mtxcreate(const char *desc, const char *type)
9073131Scg{
9173131Scg	struct mtx *m;
9273131Scg
93111119Simp	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
94125136Struckman	mtx_init(m, desc, type, MTX_DEF);
9573131Scg	return m;
9673131Scg}
9773131Scg
9873131Scgvoid
9973131Scgsnd_mtxfree(void *m)
10073131Scg{
10173131Scg	struct mtx *mtx = m;
10273131Scg
10373131Scg	mtx_destroy(mtx);
10473131Scg	free(mtx, M_DEVBUF);
10573131Scg}
10673131Scg
10773131Scgvoid
10873131Scgsnd_mtxassert(void *m)
10973131Scg{
11078670Scg#ifdef INVARIANTS
11173131Scg	struct mtx *mtx = m;
11273131Scg
11373131Scg	mtx_assert(mtx, MA_OWNED);
11473131Scg#endif
11573131Scg}
11673131Scg
11773131Scgint
11873131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
11973131Scg{
120170815Sariff	struct snddev_info *d;
121193640Sariff
12273131Scg	flags &= INTR_MPSAFE;
12378366Speter	flags |= INTR_TYPE_AV;
124170815Sariff	d = device_get_softc(dev);
125170815Sariff	if (d != NULL && (flags & INTR_MPSAFE))
126170815Sariff		d->flags |= SD_F_MPSAFE;
127170815Sariff
128166918Sariff	return bus_setup_intr(dev, res, flags,
129166918Sariff#if __FreeBSD_version >= 700031
130166918Sariff			NULL,
131166918Sariff#endif
132166918Sariff			hand, param, cookiep);
13373131Scg}
13473131Scg
135170161Sariffstatic void
136170161Sariffpcm_clonereset(struct snddev_info *d)
137170161Sariff{
138170161Sariff	int cmax;
139170161Sariff
140170815Sariff	PCM_BUSYASSERT(d);
141170161Sariff
142170161Sariff	cmax = d->playcount + d->reccount - 1;
143170161Sariff	if (d->pvchancount > 0)
144193640Sariff		cmax += max(d->pvchancount, snd_maxautovchans) - 1;
145170161Sariff	if (d->rvchancount > 0)
146193640Sariff		cmax += max(d->rvchancount, snd_maxautovchans) - 1;
147170161Sariff	if (cmax > PCMMAXCLONE)
148170161Sariff		cmax = PCMMAXCLONE;
149170161Sariff	(void)snd_clone_gc(d->clones);
150170161Sariff	(void)snd_clone_setmaxunit(d->clones, cmax);
151170161Sariff}
152170161Sariff
153193640Sariffint
154170161Sariffpcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
155157331Sariff{
156170161Sariff	struct pcm_channel *c, *ch, *nch;
157193640Sariff	struct pcmchan_caps *caps;
158193640Sariff	int i, err, vcnt;
159157331Sariff
160170815Sariff	PCM_BUSYASSERT(d);
161170161Sariff
162170161Sariff	if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
163170815Sariff	    (direction == PCMDIR_REC && d->reccount < 1))
164170815Sariff		return (ENODEV);
165164614Sariff
166170815Sariff	if (!(d->flags & SD_F_AUTOVCHAN))
167170815Sariff		return (EINVAL);
168157331Sariff
169170815Sariff	if (newcnt < 0 || newcnt > SND_MAXVCHANS)
170170815Sariff		return (E2BIG);
171157331Sariff
172170161Sariff	if (direction == PCMDIR_PLAY)
173170161Sariff		vcnt = d->pvchancount;
174170161Sariff	else if (direction == PCMDIR_REC)
175170161Sariff		vcnt = d->rvchancount;
176170815Sariff	else
177170815Sariff		return (EINVAL);
178157331Sariff
179157331Sariff	if (newcnt > vcnt) {
180170161Sariff		KASSERT(num == -1 ||
181170161Sariff		    (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt),
182170161Sariff		    ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
183170161Sariff		    num, newcnt, vcnt));
184157331Sariff		/* add new vchans - find a parent channel first */
185170815Sariff		ch = NULL;
186170161Sariff		CHN_FOREACH(c, d, channels.pcm) {
187157331Sariff			CHN_LOCK(c);
188170161Sariff			if (c->direction == direction &&
189170161Sariff			    ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
190193640Sariff			    c->refcount < 1 &&
191170815Sariff			    !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) {
192193640Sariff				/*
193193640Sariff				 * Reuse hw channel with vchans already
194193640Sariff				 * created.
195193640Sariff				 */
196193640Sariff				if (c->flags & CHN_F_HAS_VCHAN) {
197193640Sariff					ch = c;
198193640Sariff					break;
199193640Sariff				}
200193640Sariff				/*
201193640Sariff				 * No vchans ever created, look for
202193640Sariff				 * channels with supported formats.
203193640Sariff				 */
204193640Sariff				caps = chn_getcaps(c);
205193640Sariff				if (caps == NULL) {
206193640Sariff					CHN_UNLOCK(c);
207193640Sariff					continue;
208193640Sariff				}
209193640Sariff				for (i = 0; caps->fmtlist[i] != 0; i++) {
210193640Sariff					if (caps->fmtlist[i] & AFMT_CONVERTIBLE)
211193640Sariff						break;
212193640Sariff				}
213193640Sariff				if (caps->fmtlist[i] != 0) {
214193640Sariff					ch = c;
215193640Sariff				    	break;
216193640Sariff				}
217170815Sariff			}
218157331Sariff			CHN_UNLOCK(c);
219157331Sariff		}
220170815Sariff		if (ch == NULL)
221170815Sariff			return (EBUSY);
222170815Sariff		ch->flags |= CHN_F_BUSY;
223170815Sariff		err = 0;
224157331Sariff		while (err == 0 && newcnt > vcnt) {
225170815Sariff			err = vchan_create(ch, num);
226170161Sariff			if (err == 0)
227157331Sariff				vcnt++;
228170161Sariff			else if (err == E2BIG && newcnt > vcnt)
229170161Sariff				device_printf(d->dev,
230170161Sariff				    "%s: err=%d Maximum channel reached.\n",
231170161Sariff				    __func__, err);
232157331Sariff		}
233157331Sariff		if (vcnt == 0)
234170815Sariff			ch->flags &= ~CHN_F_BUSY;
235170815Sariff		CHN_UNLOCK(ch);
236170815Sariff		if (err != 0)
237170815Sariff			return (err);
238170815Sariff		else
239170815Sariff			pcm_clonereset(d);
240157331Sariff	} else if (newcnt < vcnt) {
241170161Sariff		KASSERT(num == -1,
242170161Sariff		    ("bogus vchan_destroy() request num=%d", num));
243170161Sariff		CHN_FOREACH(c, d, channels.pcm) {
244170161Sariff			CHN_LOCK(c);
245170161Sariff			if (c->direction != direction ||
246170161Sariff			    CHN_EMPTY(c, children) ||
247170161Sariff			    !(c->flags & CHN_F_HAS_VCHAN)) {
248157331Sariff				CHN_UNLOCK(c);
249170161Sariff				continue;
250157331Sariff			}
251170161Sariff			CHN_FOREACH_SAFE(ch, c, nch, children) {
252170161Sariff				CHN_LOCK(ch);
253193640Sariff				if (vcnt == 1 && c->refcount > 0) {
254170161Sariff					CHN_UNLOCK(ch);
255193640Sariff					break;
256193640Sariff				}
257193640Sariff				if (!(ch->flags & CHN_F_BUSY) &&
258193640Sariff				    ch->refcount < 1) {
259170161Sariff					err = vchan_destroy(ch);
260170161Sariff					if (err == 0)
261170161Sariff						vcnt--;
262170161Sariff				} else
263170161Sariff					CHN_UNLOCK(ch);
264170815Sariff				if (vcnt == newcnt)
265170161Sariff					break;
266170161Sariff			}
267170161Sariff			CHN_UNLOCK(c);
268157331Sariff			break;
269157331Sariff		}
270170161Sariff		pcm_clonereset(d);
271157331Sariff	}
272157331Sariff
273170815Sariff	return (0);
274157331Sariff}
275157331Sariff
276156929Sariff/* return error status and a locked channel */
277156929Sariffint
278156929Sariffpcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
279193640Sariff    pid_t pid, char *comm, int devunit)
28077269Scg{
28177269Scg	struct pcm_channel *c;
282193640Sariff	int err, vchancount, vchan_num;
28377269Scg
284170161Sariff	KASSERT(d != NULL && ch != NULL && (devunit == -1 ||
285170161Sariff	    !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) &&
286170161Sariff	    (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
287170815Sariff	    ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d",
288170161Sariff	    __func__, d, ch, direction, pid, devunit));
289170815Sariff	PCM_BUSYASSERT(d);
290170161Sariff
291170161Sariff	/* Double check again. */
292170161Sariff	if (devunit != -1) {
293170161Sariff		switch (snd_unit2d(devunit)) {
294170161Sariff		case SND_DEV_DSPHW_PLAY:
295170161Sariff		case SND_DEV_DSPHW_VPLAY:
296170161Sariff			if (direction != PCMDIR_PLAY)
297193640Sariff				return (ENOTSUP);
298170161Sariff			break;
299170161Sariff		case SND_DEV_DSPHW_REC:
300170161Sariff		case SND_DEV_DSPHW_VREC:
301170161Sariff			if (direction != PCMDIR_REC)
302193640Sariff				return (ENOTSUP);
303170161Sariff			break;
304170161Sariff		default:
305170161Sariff			if (!(direction == PCMDIR_PLAY ||
306170161Sariff			    direction == PCMDIR_REC))
307193640Sariff				return (ENOTSUP);
308170161Sariff			break;
309170161Sariff		}
310170161Sariff	}
311170161Sariff
312193640Sariff	*ch = NULL;
313193640Sariff	vchan_num = 0;
314193640Sariff	vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount :
315193640Sariff	    d->rvchancount;
316193640Sariff
317156929Sariffretry_chnalloc:
318193640Sariff	err = ENOTSUP;
31978895Scg	/* scan for a free channel */
320170161Sariff	CHN_FOREACH(c, d, channels.pcm) {
32177882Scg		CHN_LOCK(c);
322193640Sariff		if (devunit == -1 && c->direction == direction &&
323193640Sariff		    (c->flags & CHN_F_VIRTUAL)) {
324193640Sariff			if (vchancount < snd_maxautovchans &&
325193640Sariff			    vchan_num < CHN_CHAN(c)) {
326193640Sariff			    	CHN_UNLOCK(c);
327193640Sariff				goto vchan_alloc;
328193640Sariff			}
329193640Sariff			vchan_num++;
330193640Sariff		}
331170161Sariff		if (c->direction == direction && !(c->flags & CHN_F_BUSY) &&
332170161Sariff		    (devunit == -1 || devunit == -2 || c->unit == devunit)) {
333170161Sariff			c->flags |= CHN_F_BUSY;
334170161Sariff			c->pid = pid;
335193640Sariff			strlcpy(c->comm, (comm != NULL) ? comm :
336193640Sariff			    CHN_COMM_UNKNOWN, sizeof(c->comm));
337170161Sariff			*ch = c;
338170161Sariff			return (0);
339170161Sariff		} else if (c->unit == devunit) {
340156929Sariff			if (c->direction != direction)
341193640Sariff				err = ENOTSUP;
342156929Sariff			else if (c->flags & CHN_F_BUSY)
343156929Sariff				err = EBUSY;
344156929Sariff			else
345156929Sariff				err = EINVAL;
346156929Sariff			CHN_UNLOCK(c);
347170161Sariff			return (err);
348170161Sariff		} else if ((devunit == -1 || devunit == -2) &&
349170161Sariff		    c->direction == direction && (c->flags & CHN_F_BUSY))
350157331Sariff			err = EBUSY;
35177882Scg		CHN_UNLOCK(c);
35277269Scg	}
35378895Scg
354170815Sariff	if (devunit == -2)
355170815Sariff		return (err);
356170815Sariff
357193640Sariffvchan_alloc:
35878895Scg	/* no channel available */
359170815Sariff	if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY ||
360170815Sariff	    snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) {
361170161Sariff		if (!(vchancount > 0 && vchancount < snd_maxautovchans) &&
362170161Sariff		    (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans))
363170161Sariff			return (err);
364170161Sariff		err = pcm_setvchans(d, direction, vchancount + 1,
365170161Sariff		    (devunit == -1) ? -1 : snd_unit2c(devunit));
366157331Sariff		if (err == 0) {
367170161Sariff			if (devunit == -1)
368170161Sariff				devunit = -2;
369157331Sariff			goto retry_chnalloc;
37078895Scg		}
37178895Scg	}
37278895Scg
373170161Sariff	return (err);
37477269Scg}
37577269Scg
37678214Scg/* release a locked channel and unlock it */
37777269Scgint
37878214Scgpcm_chnrelease(struct pcm_channel *c)
37977269Scg{
380170815Sariff	PCM_BUSYASSERT(c->parentsnddev);
38178214Scg	CHN_LOCKASSERT(c);
382170815Sariff
38377269Scg	c->flags &= ~CHN_F_BUSY;
38478214Scg	c->pid = -1;
385193640Sariff	strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm));
38677882Scg	CHN_UNLOCK(c);
387170815Sariff
388170815Sariff	return (0);
38977269Scg}
39077269Scg
39177269Scgint
39277269Scgpcm_chnref(struct pcm_channel *c, int ref)
39377269Scg{
394170815Sariff	PCM_BUSYASSERT(c->parentsnddev);
395170815Sariff	CHN_LOCKASSERT(c);
39677882Scg
39777269Scg	c->refcount += ref;
398170815Sariff
399170815Sariff	return (c->refcount);
40077269Scg}
40177269Scg
40282180Scgint
40382180Scgpcm_inprog(struct snddev_info *d, int delta)
40482180Scg{
405193640Sariff	PCM_LOCKASSERT(d);
406119096Scg
407170815Sariff	d->inprog += delta;
408119096Scg
409170815Sariff	return (d->inprog);
41082180Scg}
41182180Scg
41282180Scgstatic void
41382180Scgpcm_setmaxautovchans(struct snddev_info *d, int num)
41482180Scg{
415170815Sariff	PCM_BUSYASSERT(d);
416170815Sariff
417170161Sariff	if (num < 0)
418170161Sariff		return;
419170161Sariff
420170161Sariff	if (num >= 0 && d->pvchancount > num)
421170161Sariff		(void)pcm_setvchans(d, PCMDIR_PLAY, num, -1);
422170161Sariff	else if (num > 0 && d->pvchancount == 0)
423170161Sariff		(void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1);
424170161Sariff
425170161Sariff	if (num >= 0 && d->rvchancount > num)
426170161Sariff		(void)pcm_setvchans(d, PCMDIR_REC, num, -1);
427170161Sariff	else if (num > 0 && d->rvchancount == 0)
428170161Sariff		(void)pcm_setvchans(d, PCMDIR_REC, 1, -1);
429170161Sariff
430170161Sariff	pcm_clonereset(d);
43182180Scg}
43282180Scg
43365340Scgstatic int
434159732Snetchildsysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
43565340Scg{
43678214Scg	struct snddev_info *d;
43765340Scg	int error, unit;
43865340Scg
43965340Scg	unit = snd_unit;
440170289Sdwmalone	error = sysctl_handle_int(oidp, &unit, 0, req);
44165340Scg	if (error == 0 && req->newptr != NULL) {
44278214Scg		d = devclass_get_softc(pcm_devclass, unit);
443170815Sariff		if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
44478214Scg			return EINVAL;
44565340Scg		snd_unit = unit;
446222826Smav		snd_unit_auto = 0;
44765340Scg	}
44865340Scg	return (error);
44965340Scg}
450159732Snetchild/* XXX: do we need a way to let the user change the default unit? */
451159732SnetchildSYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW,
452164614Sariff            0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device");
45365340Scg
45478853Scgstatic int
45582180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
45678853Scg{
45782180Scg	struct snddev_info *d;
45882180Scg	int i, v, error;
45978853Scg
46082180Scg	v = snd_maxautovchans;
461170289Sdwmalone	error = sysctl_handle_int(oidp, &v, 0, req);
46278853Scg	if (error == 0 && req->newptr != NULL) {
463170161Sariff		if (v < 0)
464170161Sariff			v = 0;
465170161Sariff		if (v > SND_MAXVCHANS)
466170161Sariff			v = SND_MAXVCHANS;
467170161Sariff		snd_maxautovchans = v;
468170235Sariff		for (i = 0; pcm_devclass != NULL &&
469170235Sariff		    i < devclass_get_maxunit(pcm_devclass); i++) {
470170161Sariff			d = devclass_get_softc(pcm_devclass, i);
471170815Sariff			if (!PCM_REGISTERED(d))
472170161Sariff				continue;
473170815Sariff			PCM_ACQUIRE_QUICK(d);
474170161Sariff			pcm_setmaxautovchans(d, v);
475170815Sariff			PCM_RELEASE_QUICK(d);
47682180Scg		}
47778853Scg	}
47878853Scg	return (error);
47978853Scg}
48082180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
481164614Sariff            0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
48278853Scg
48377269Scgstruct pcm_channel *
484170161Sariffpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo)
48550724Scg{
486170161Sariff	struct pcm_channel *ch;
487170161Sariff	int direction, err, rpnum, *pnum, max;
488170161Sariff	int udc, device, chan;
489170161Sariff	char *dirs, *devname, buf[CHN_NAMELEN];
49050724Scg
491170815Sariff	PCM_BUSYASSERT(d);
492193640Sariff	PCM_LOCKASSERT(d);
493170161Sariff	KASSERT(num >= -1, ("invalid num=%d", num));
494170161Sariff
495170161Sariff
496170815Sariff	switch (dir) {
49777269Scg	case PCMDIR_PLAY:
49877269Scg		dirs = "play";
499126367Struckman		direction = PCMDIR_PLAY;
50083614Scg		pnum = &d->playcount;
501170161Sariff		device = SND_DEV_DSPHW_PLAY;
502170161Sariff		max = SND_MAXHWCHAN;
50377269Scg		break;
504170161Sariff	case PCMDIR_PLAY_VIRTUAL:
505170161Sariff		dirs = "virtual";
506170161Sariff		direction = PCMDIR_PLAY;
507170161Sariff		pnum = &d->pvchancount;
508170161Sariff		device = SND_DEV_DSPHW_VPLAY;
509170161Sariff		max = SND_MAXVCHANS;
510170161Sariff		break;
51177269Scg	case PCMDIR_REC:
51277269Scg		dirs = "record";
513126367Struckman		direction = PCMDIR_REC;
51483614Scg		pnum = &d->reccount;
515170161Sariff		device = SND_DEV_DSPHW_REC;
516170161Sariff		max = SND_MAXHWCHAN;
51777269Scg		break;
518170161Sariff	case PCMDIR_REC_VIRTUAL:
51977269Scg		dirs = "virtual";
520170161Sariff		direction = PCMDIR_REC;
521170161Sariff		pnum = &d->rvchancount;
522170161Sariff		device = SND_DEV_DSPHW_VREC;
523170161Sariff		max = SND_MAXVCHANS;
52477269Scg		break;
52577269Scg	default:
526170815Sariff		return (NULL);
52777269Scg	}
52865340Scg
529170161Sariff	chan = (num == -1) ? 0 : num;
53083614Scg
531170815Sariff	if (*pnum >= max || chan >= max)
532170815Sariff		return (NULL);
533170161Sariff
534156929Sariff	rpnum = 0;
535170161Sariff
536170161Sariff	CHN_FOREACH(ch, d, channels.pcm) {
537170161Sariff		if (CHN_DEV(ch) != device)
538156929Sariff			continue;
539170161Sariff		if (chan == CHN_CHAN(ch)) {
540170161Sariff			if (num != -1) {
541170161Sariff				device_printf(d->dev,
542170161Sariff				    "channel num=%d allocated!\n", chan);
543170815Sariff				return (NULL);
544170161Sariff			}
545170161Sariff			chan++;
546170161Sariff			if (chan >= max) {
547170161Sariff				device_printf(d->dev,
548170161Sariff				    "chan=%d > %d\n", chan, max);
549170815Sariff				return (NULL);
550170161Sariff			}
551156929Sariff		}
552156929Sariff		rpnum++;
553156929Sariff	}
554170161Sariff
555156929Sariff	if (*pnum != rpnum) {
556156929Sariff		device_printf(d->dev,
557170161Sariff		    "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n",
558170161Sariff		    __func__, dirs, *pnum, rpnum);
559170815Sariff		return (NULL);
560156929Sariff	}
561170161Sariff
562170161Sariff	udc = snd_mkunit(device_get_unit(d->dev), device, chan);
563170161Sariff	devname = dsp_unit2name(buf, sizeof(buf), udc);
564170161Sariff
565170161Sariff	if (devname == NULL) {
566170161Sariff		device_printf(d->dev,
567170161Sariff		    "Failed to query device name udc=0x%08x\n", udc);
568170815Sariff		return (NULL);
569170161Sariff	}
570170161Sariff
571193640Sariff	PCM_UNLOCK(d);
572170161Sariff	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
573170161Sariff	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
574170161Sariff	ch->unit = udc;
57577269Scg	ch->pid = -1;
576193640Sariff	strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm));
57777269Scg	ch->parentsnddev = d;
57877269Scg	ch->parentchannel = parent;
57989774Sscottl	ch->dev = d->dev;
580170161Sariff	ch->trigger = PCMTRIG_STOP;
581170161Sariff	snprintf(ch->name, sizeof(ch->name), "%s:%s:%s",
582170161Sariff	    device_get_nameunit(ch->dev), dirs, devname);
58377269Scg
584126367Struckman	err = chn_init(ch, devinfo, dir, direction);
585193640Sariff	PCM_LOCK(d);
58670134Scg	if (err) {
587170161Sariff		device_printf(d->dev, "chn_init(%s) failed: err = %d\n",
588170161Sariff		    ch->name, err);
58977269Scg		kobj_delete(ch->methods, M_DEVBUF);
59077269Scg		free(ch, M_DEVBUF);
591170815Sariff		return (NULL);
59255483Scg	}
59377269Scg
594170815Sariff	return (ch);
59577269Scg}
59677269Scg
59777269Scgint
59877269Scgpcm_chn_destroy(struct pcm_channel *ch)
59977269Scg{
60083614Scg	struct snddev_info *d;
60177269Scg	int err;
60277269Scg
60383614Scg	d = ch->parentsnddev;
604170815Sariff	PCM_BUSYASSERT(d);
605170815Sariff
60677269Scg	err = chn_kill(ch);
60777269Scg	if (err) {
608170815Sariff		device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n",
609170815Sariff		    ch->name, err);
610170815Sariff		return (err);
61177269Scg	}
61277269Scg
61377269Scg	kobj_delete(ch->methods, M_DEVBUF);
61477269Scg	free(ch, M_DEVBUF);
61577269Scg
616170815Sariff	return (0);
61777269Scg}
61877269Scg
61977269Scgint
620124740Smatkpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
62177269Scg{
622170815Sariff	PCM_BUSYASSERT(d);
623193640Sariff	PCM_LOCKASSERT(d);
624170161Sariff	KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
625170161Sariff	    ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
62677269Scg
627193640Sariff	CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm);
628170161Sariff
629170815Sariff	switch (CHN_DEV(ch)) {
630170815Sariff	case SND_DEV_DSPHW_PLAY:
631170815Sariff		d->playcount++;
632170815Sariff		break;
633170815Sariff	case SND_DEV_DSPHW_VPLAY:
634170815Sariff		d->pvchancount++;
635170815Sariff		break;
636170815Sariff	case SND_DEV_DSPHW_REC:
637170815Sariff		d->reccount++;
638170815Sariff		break;
639170815Sariff	case SND_DEV_DSPHW_VREC:
640170815Sariff		d->rvchancount++;
641170815Sariff		break;
642170815Sariff	default:
643170815Sariff		break;
644170815Sariff	}
645170815Sariff
646170161Sariff	d->devcount++;
647164614Sariff
648170815Sariff	return (0);
64950724Scg}
65050724Scg
65177269Scgint
652124740Smatkpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
65365340Scg{
654170161Sariff	struct pcm_channel *tmp;
65565340Scg
656170815Sariff	PCM_BUSYASSERT(d);
657193640Sariff	PCM_LOCKASSERT(d);
658170815Sariff
659170161Sariff	tmp = NULL;
660119096Scg
661170161Sariff	CHN_FOREACH(tmp, d, channels.pcm) {
662170161Sariff		if (tmp == ch)
663170161Sariff			break;
66465340Scg	}
665107237Scg
666170161Sariff	if (tmp != ch)
667170815Sariff		return (EINVAL);
668170161Sariff
669170161Sariff	CHN_REMOVE(d, ch, channels.pcm);
670170815Sariff
671170161Sariff	switch (CHN_DEV(ch)) {
672170161Sariff	case SND_DEV_DSPHW_PLAY:
673170161Sariff		d->playcount--;
674170161Sariff		break;
675170161Sariff	case SND_DEV_DSPHW_VPLAY:
676170161Sariff		d->pvchancount--;
677170161Sariff		break;
678170161Sariff	case SND_DEV_DSPHW_REC:
679107237Scg		d->reccount--;
680170161Sariff		break;
681170161Sariff	case SND_DEV_DSPHW_VREC:
682170161Sariff		d->rvchancount--;
683170161Sariff		break;
684170161Sariff	default:
685170161Sariff		break;
686170161Sariff	}
687107237Scg
688170815Sariff	d->devcount--;
689170815Sariff
690170815Sariff	return (0);
69165340Scg}
69265340Scg
69350724Scgint
69477269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
69577269Scg{
696157331Sariff	struct snddev_info *d = device_get_softc(dev);
69782180Scg	struct pcm_channel *ch;
698157331Sariff	int err;
69977269Scg
700170815Sariff	PCM_BUSYASSERT(d);
701170815Sariff
702193640Sariff	PCM_LOCK(d);
703170161Sariff	ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo);
70477269Scg	if (!ch) {
705170815Sariff		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n",
706170815Sariff		    cls->name, dir, devinfo);
707193640Sariff		PCM_UNLOCK(d);
708170815Sariff		return (ENODEV);
70977269Scg	}
71078853Scg
711124740Smatk	err = pcm_chn_add(d, ch);
712193640Sariff	PCM_UNLOCK(d);
71377269Scg	if (err) {
714170815Sariff		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n",
715170815Sariff		    ch->name, err);
71677269Scg		pcm_chn_destroy(ch);
71777269Scg	}
71877269Scg
719170815Sariff	return (err);
72077269Scg}
72177269Scg
72277269Scgstatic int
72377269Scgpcm_killchan(device_t dev)
72477269Scg{
725157331Sariff	struct snddev_info *d = device_get_softc(dev);
726106420Scognet	struct pcm_channel *ch;
727170815Sariff	int error;
72877269Scg
729170815Sariff	PCM_BUSYASSERT(d);
730170815Sariff
731170161Sariff	ch = CHN_FIRST(d, channels.pcm);
73277269Scg
733193640Sariff	PCM_LOCK(d);
734170161Sariff	error = pcm_chn_remove(d, ch);
735193640Sariff	PCM_UNLOCK(d);
736106420Scognet	if (error)
737106420Scognet		return (error);
738106420Scognet	return (pcm_chn_destroy(ch));
73977269Scg}
74077269Scg
741222826Smavstatic int
742222826Smavpcm_best_unit(int old)
743222826Smav{
744222826Smav	struct snddev_info *d;
745222826Smav	int i, best, bestprio, prio;
746222826Smav
747222826Smav	best = -1;
748222826Smav	bestprio = -100;
749222826Smav	for (i = 0; pcm_devclass != NULL &&
750222826Smav	    i < devclass_get_maxunit(pcm_devclass); i++) {
751222826Smav		d = devclass_get_softc(pcm_devclass, i);
752222826Smav		if (!PCM_REGISTERED(d))
753222826Smav			continue;
754222826Smav		prio = 0;
755222826Smav		if (d->playcount == 0)
756222826Smav			prio -= 10;
757222826Smav		if (d->reccount == 0)
758222826Smav			prio -= 2;
759222826Smav		if (prio > bestprio || (prio == bestprio && i == old)) {
760222826Smav			best = i;
761222826Smav			bestprio = prio;
762222826Smav		}
763222826Smav	}
764222826Smav	return (best);
765222826Smav}
766222826Smav
76777269Scgint
76850724Scgpcm_setstatus(device_t dev, char *str)
76950724Scg{
770157331Sariff	struct snddev_info *d = device_get_softc(dev);
77177882Scg
772170815Sariff	PCM_BUSYASSERT(d);
773170815Sariff
774170815Sariff	if (d->playcount == 0 || d->reccount == 0)
775170815Sariff		d->flags |= SD_F_SIMPLEX;
776170815Sariff
777170815Sariff	if ((d->playcount > 0 || d->reccount > 0) &&
778170815Sariff	    !(d->flags & SD_F_AUTOVCHAN)) {
779170815Sariff		d->flags |= SD_F_AUTOVCHAN;
780170815Sariff		vchan_initsys(dev);
781170815Sariff	}
782170815Sariff
783170161Sariff	pcm_setmaxautovchans(d, snd_maxautovchans);
784170161Sariff
785170815Sariff	strlcpy(d->status, str, SND_STATUSLEN);
786170815Sariff
787193640Sariff	PCM_LOCK(d);
788170161Sariff
789170161Sariff	/* Last stage, enable cloning. */
790170815Sariff	if (d->clones != NULL)
791170161Sariff		(void)snd_clone_enable(d->clones);
792170161Sariff
793170815Sariff	/* Done, we're ready.. */
794170815Sariff	d->flags |= SD_F_REGISTERED;
795170815Sariff
796170815Sariff	PCM_RELEASE(d);
797170815Sariff
798193640Sariff	PCM_UNLOCK(d);
799170161Sariff
800222826Smav	if (snd_unit_auto < 0)
801222826Smav		snd_unit_auto = (snd_unit < 0) ? 1 : 0;
802222826Smav	if (snd_unit < 0 || snd_unit_auto > 1)
803170884Sariff		snd_unit = device_get_unit(dev);
804222826Smav	else if (snd_unit_auto == 1)
805222826Smav		snd_unit = pcm_best_unit(snd_unit);
806170884Sariff
807170815Sariff	return (0);
80850724Scg}
80950724Scg
810157331Sariffuint32_t
81150724Scgpcm_getflags(device_t dev)
81250724Scg{
813157331Sariff	struct snddev_info *d = device_get_softc(dev);
81477882Scg
81550724Scg	return d->flags;
81650724Scg}
81750724Scg
81850724Scgvoid
819157331Sariffpcm_setflags(device_t dev, uint32_t val)
82050724Scg{
821157331Sariff	struct snddev_info *d = device_get_softc(dev);
82278362Scg
82350724Scg	d->flags = val;
82450724Scg}
82550724Scg
82658384Scgvoid *
82758384Scgpcm_getdevinfo(device_t dev)
82858384Scg{
829157331Sariff	struct snddev_info *d = device_get_softc(dev);
83077882Scg
83158384Scg	return d->devinfo;
83258384Scg}
83358384Scg
83483614Scgunsigned int
835160439Snetchildpcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
83683614Scg{
837157331Sariff	struct snddev_info *d = device_get_softc(dev);
83889690Scg	int sz, x;
83983614Scg
84083614Scg	sz = 0;
84189690Scg	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
84289690Scg		x = sz;
843160439Snetchild		RANGE(sz, minbufsz, maxbufsz);
84489690Scg		if (x != sz)
845160439Snetchild			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
846160439Snetchild		x = minbufsz;
84789690Scg		while (x < sz)
84889690Scg			x <<= 1;
84989690Scg		if (x > sz)
85089690Scg			x >>= 1;
85189690Scg		if (x != sz) {
85289690Scg			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
85389690Scg			sz = x;
85489690Scg		}
85589690Scg	} else {
85683614Scg		sz = deflt;
85789690Scg	}
85889690Scg
85983614Scg	d->bufsz = sz;
86083614Scg
86183614Scg	return sz;
86283614Scg}
86383614Scg
864170161Sariffstatic int
865193640Sariffsysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)
866193640Sariff{
867193640Sariff	struct snddev_info *d;
868193640Sariff	int err, val;
869193640Sariff
870193640Sariff	d = oidp->oid_arg1;
871193640Sariff	if (!PCM_REGISTERED(d))
872193640Sariff		return (ENODEV);
873193640Sariff
874193640Sariff	PCM_LOCK(d);
875193640Sariff	PCM_WAIT(d);
876193640Sariff	val = (d->flags & SD_F_BITPERFECT) ? 1 : 0;
877193640Sariff	PCM_ACQUIRE(d);
878193640Sariff	PCM_UNLOCK(d);
879193640Sariff
880193640Sariff	err = sysctl_handle_int(oidp, &val, 0, req);
881193640Sariff
882193640Sariff	if (err == 0 && req->newptr != NULL) {
883193640Sariff		if (!(val == 0 || val == 1)) {
884193640Sariff			PCM_RELEASE_QUICK(d);
885193640Sariff			return (EINVAL);
886193640Sariff		}
887193640Sariff
888193640Sariff		PCM_LOCK(d);
889193640Sariff
890193640Sariff		d->flags &= ~SD_F_BITPERFECT;
891193640Sariff		d->flags |= (val != 0) ? SD_F_BITPERFECT : 0;
892193640Sariff
893193640Sariff		PCM_RELEASE(d);
894193640Sariff		PCM_UNLOCK(d);
895193640Sariff	} else
896193640Sariff		PCM_RELEASE_QUICK(d);
897193640Sariff
898193640Sariff	return (err);
899193640Sariff}
900193640Sariff
901193640Sariff#ifdef SND_DEBUG
902193640Sariffstatic int
903170161Sariffsysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS)
904170161Sariff{
905170161Sariff	struct snddev_info *d;
906170161Sariff	uint32_t flags;
907170161Sariff	int err;
908170161Sariff
909170161Sariff	d = oidp->oid_arg1;
910170815Sariff	if (!PCM_REGISTERED(d) || d->clones == NULL)
911170161Sariff		return (ENODEV);
912170161Sariff
913170815Sariff	PCM_ACQUIRE_QUICK(d);
914170815Sariff
915170161Sariff	flags = snd_clone_getflags(d->clones);
916170289Sdwmalone	err = sysctl_handle_int(oidp, &flags, 0, req);
917170161Sariff
918170161Sariff	if (err == 0 && req->newptr != NULL) {
919170815Sariff		if (flags & ~SND_CLONE_MASK)
920170161Sariff			err = EINVAL;
921170815Sariff		else
922170161Sariff			(void)snd_clone_setflags(d->clones, flags);
923170161Sariff	}
924170161Sariff
925170815Sariff	PCM_RELEASE_QUICK(d);
926170815Sariff
927170161Sariff	return (err);
928170161Sariff}
929170161Sariff
930170161Sariffstatic int
931170161Sariffsysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS)
932170161Sariff{
933170161Sariff	struct snddev_info *d;
934170161Sariff	int err, deadline;
935170161Sariff
936170161Sariff	d = oidp->oid_arg1;
937170815Sariff	if (!PCM_REGISTERED(d) || d->clones == NULL)
938170161Sariff		return (ENODEV);
939170161Sariff
940170815Sariff	PCM_ACQUIRE_QUICK(d);
941170815Sariff
942170161Sariff	deadline = snd_clone_getdeadline(d->clones);
943170289Sdwmalone	err = sysctl_handle_int(oidp, &deadline, 0, req);
944170161Sariff
945170161Sariff	if (err == 0 && req->newptr != NULL) {
946170161Sariff		if (deadline < 0)
947170161Sariff			err = EINVAL;
948170815Sariff		else
949170161Sariff			(void)snd_clone_setdeadline(d->clones, deadline);
950170161Sariff	}
951170161Sariff
952170815Sariff	PCM_RELEASE_QUICK(d);
953170815Sariff
954170161Sariff	return (err);
955170161Sariff}
956170161Sariff
957170161Sariffstatic int
958170161Sariffsysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS)
959170161Sariff{
960170161Sariff	struct snddev_info *d;
961170161Sariff	int err, val;
962170161Sariff
963170161Sariff	d = oidp->oid_arg1;
964170815Sariff	if (!PCM_REGISTERED(d) || d->clones == NULL)
965170161Sariff		return (ENODEV);
966170161Sariff
967170161Sariff	val = 0;
968170289Sdwmalone	err = sysctl_handle_int(oidp, &val, 0, req);
969170161Sariff
970170161Sariff	if (err == 0 && req->newptr != NULL && val != 0) {
971170815Sariff		PCM_ACQUIRE_QUICK(d);
972170815Sariff		val = snd_clone_gc(d->clones);
973170815Sariff		PCM_RELEASE_QUICK(d);
974170815Sariff		if (bootverbose != 0 || snd_verbose > 3)
975170815Sariff			device_printf(d->dev, "clone gc: pruned=%d\n", val);
976170161Sariff	}
977170161Sariff
978170161Sariff	return (err);
979170161Sariff}
980170161Sariff
981170161Sariffstatic int
982170161Sariffsysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS)
983170161Sariff{
984170161Sariff	struct snddev_info *d;
985170161Sariff	int i, err, val;
986170161Sariff
987170161Sariff	val = 0;
988170289Sdwmalone	err = sysctl_handle_int(oidp, &val, 0, req);
989170161Sariff
990170161Sariff	if (err == 0 && req->newptr != NULL && val != 0) {
991170235Sariff		for (i = 0; pcm_devclass != NULL &&
992170235Sariff		    i < devclass_get_maxunit(pcm_devclass); i++) {
993170161Sariff			d = devclass_get_softc(pcm_devclass, i);
994170815Sariff			if (!PCM_REGISTERED(d) || d->clones == NULL)
995170161Sariff				continue;
996170815Sariff			PCM_ACQUIRE_QUICK(d);
997170815Sariff			val = snd_clone_gc(d->clones);
998170815Sariff			PCM_RELEASE_QUICK(d);
999170815Sariff			if (bootverbose != 0 || snd_verbose > 3)
1000170815Sariff				device_printf(d->dev, "clone gc: pruned=%d\n",
1001170815Sariff				    val);
1002170161Sariff		}
1003170161Sariff	}
1004170161Sariff
1005170161Sariff	return (err);
1006170161Sariff}
1007170161SariffSYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW,
1008170161Sariff    0, sizeof(int), sysctl_hw_snd_clone_gc, "I",
1009170161Sariff    "global clone garbage collector");
1010170161Sariff#endif
1011170161Sariff
101250724Scgint
101350724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec)
101450724Scg{
1015170161Sariff	struct snddev_info *d;
1016193640Sariff	int i;
101750724Scg
101889834Scg	if (pcm_veto_load) {
101989834Scg		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
102089834Scg
102189834Scg		return EINVAL;
102289834Scg	}
102389834Scg
1024170161Sariff	if (device_get_unit(dev) > PCMMAXUNIT) {
1025170161Sariff		device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n",
1026170161Sariff		    device_get_unit(dev), PCMMAXUNIT);
1027170161Sariff		device_printf(dev,
1028170161Sariff		    "Use 'hw.snd.maxunit' tunable to raise the limit.\n");
1029170161Sariff		return ENODEV;
1030170161Sariff	}
1031170161Sariff
1032170161Sariff	d = device_get_softc(dev);
1033170815Sariff	d->dev = dev;
103493814Sjhb	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
1035170815Sariff	cv_init(&d->cv, device_get_nameunit(dev));
1036170815Sariff	PCM_ACQUIRE_QUICK(d);
1037170815Sariff	dsp_cdevinfo_init(d);
1038148587Snetchild#if 0
1039148587Snetchild	/*
1040148587Snetchild	 * d->flags should be cleared by the allocator of the softc.
1041148587Snetchild	 * We cannot clear this field here because several devices set
1042148587Snetchild	 * this flag before calling pcm_register().
1043148587Snetchild	 */
104478853Scg	d->flags = 0;
1045148587Snetchild#endif
1046193640Sariff	i = 0;
1047193640Sariff	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
1048193640Sariff	    "vpc", &i) != 0 || i != 0)
1049193640Sariff		d->flags |= SD_F_VPC;
1050193640Sariff
1051193640Sariff	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
1052193640Sariff	    "bitperfect", &i) == 0 && i != 0)
1053193640Sariff		d->flags |= SD_F_BITPERFECT;
1054193640Sariff
105550724Scg	d->devinfo = devinfo;
105678895Scg	d->devcount = 0;
105783089Scg	d->reccount = 0;
105883614Scg	d->playcount = 0;
1059170161Sariff	d->pvchancount = 0;
1060170161Sariff	d->rvchancount = 0;
1061170161Sariff	d->pvchanrate = 0;
1062170161Sariff	d->pvchanformat = 0;
1063170161Sariff	d->rvchanrate = 0;
1064170161Sariff	d->rvchanformat = 0;
106578362Scg	d->inprog = 0;
106650724Scg
1067170161Sariff	/*
1068170161Sariff	 * Create clone manager, disabled by default. Cloning will be
1069193640Sariff	 * enabled during final stage of driver initialization through
1070170161Sariff	 * pcm_setstatus().
1071170161Sariff	 */
1072170815Sariff	d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE,
1073170815Sariff	    SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK |
1074170161Sariff	    SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF |
1075170161Sariff	    SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED);
1076124617Sphk
1077170161Sariff	CHN_INIT(d, channels.pcm);
1078170161Sariff	CHN_INIT(d, channels.pcm.busy);
1079193640Sariff	CHN_INIT(d, channels.pcm.opened);
1080170161Sariff
1081170815Sariff	/* XXX This is incorrect, but lets play along for now. */
1082157331Sariff	if ((numplay == 0 || numrec == 0) && numplay != numrec)
108378214Scg		d->flags |= SD_F_SIMPLEX;
108478362Scg
1085170161Sariff	sysctl_ctx_init(&d->play_sysctl_ctx);
1086170161Sariff	d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
1087170161Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
1088170161Sariff	    CTLFLAG_RD, 0, "playback channels node");
1089170161Sariff	sysctl_ctx_init(&d->rec_sysctl_ctx);
1090170161Sariff	d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
1091170161Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
1092170161Sariff	    CTLFLAG_RD, 0, "record channels node");
1093159732Snetchild	/* XXX: an user should be able to set this with a control tool, the
1094159732Snetchild	   sysadmin then needs min+max sysctls for this */
1095217323Smdf	SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
1096164614Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
1097164614Sariff            OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
1098193640Sariff	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1099193640Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1100193640Sariff	    "bitperfect", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
1101193640Sariff	    sysctl_dev_pcm_bitperfect, "I",
1102193640Sariff	    "bit-perfect playback/recording (0=disable, 1=enable)");
1103170161Sariff#ifdef SND_DEBUG
1104170161Sariff	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1105170161Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1106170161Sariff	    "clone_flags", CTLTYPE_UINT | CTLFLAG_RW, d, sizeof(d),
1107170161Sariff	    sysctl_dev_pcm_clone_flags, "IU",
1108170161Sariff	    "clone flags");
1109170161Sariff	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1110170161Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1111170161Sariff	    "clone_deadline", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
1112170161Sariff	    sysctl_dev_pcm_clone_deadline, "I",
1113170161Sariff	    "clone expiration deadline (ms)");
1114170161Sariff	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1115170161Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1116170161Sariff	    "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
1117170161Sariff	    sysctl_dev_pcm_clone_gc, "I",
1118170161Sariff	    "clone garbage collector");
111973127Scg#endif
1120170815Sariff
1121170161Sariff	if (numplay > 0 || numrec > 0) {
1122156929Sariff		d->flags |= SD_F_AUTOVCHAN;
112382180Scg		vchan_initsys(dev);
1124149953Snetchild	}
112578853Scg
1126193640Sariff	if (d->flags & SD_F_EQ)
1127193640Sariff		feeder_eq_initsys(dev);
1128193640Sariff
112982180Scg	sndstat_register(dev, d->status, sndstat_prepare_pcm);
1130170815Sariff
1131157331Sariff	return 0;
113250724Scg}
113350724Scg
113465340Scgint
113565340Scgpcm_unregister(device_t dev)
113665207Scg{
1137170815Sariff	struct snddev_info *d;
113883614Scg	struct pcm_channel *ch;
1139170161Sariff	struct thread *td;
114065207Scg
1141170161Sariff	td = curthread;
1142170815Sariff	d = device_get_softc(dev);
1143170161Sariff
1144170815Sariff	if (!PCM_ALIVE(d)) {
1145170815Sariff		device_printf(dev, "unregister: device not configured\n");
1146170815Sariff		return (0);
1147170815Sariff	}
1148170815Sariff
1149170161Sariff	if (sndstat_acquire(td) != 0) {
1150150827Snetchild		device_printf(dev, "unregister: sndstat busy\n");
1151170815Sariff		return (EBUSY);
1152150827Snetchild	}
1153150827Snetchild
1154193640Sariff	PCM_LOCK(d);
1155170815Sariff	PCM_WAIT(d);
1156170815Sariff
1157358879Shselasky	d->flags |= SD_F_DETACHING;
1158358879Shselasky
1159170815Sariff	if (d->inprog != 0) {
116083476Sgreid		device_printf(dev, "unregister: operation in progress\n");
1161193640Sariff		PCM_UNLOCK(d);
1162170161Sariff		sndstat_release(td);
1163170815Sariff		return (EBUSY);
116478362Scg	}
1165124740Smatk
1166170815Sariff	PCM_ACQUIRE(d);
1167193640Sariff	PCM_UNLOCK(d);
1168170815Sariff
1169170161Sariff	CHN_FOREACH(ch, d, channels.pcm) {
1170170815Sariff		CHN_LOCK(ch);
117183614Scg		if (ch->refcount > 0) {
1172170815Sariff			device_printf(dev,
1173170815Sariff			    "unregister: channel %s busy (pid %d)\n",
1174170815Sariff			    ch->name, ch->pid);
1175170815Sariff			CHN_UNLOCK(ch);
1176170815Sariff			PCM_RELEASE_QUICK(d);
1177170161Sariff			sndstat_release(td);
1178170815Sariff			return (EBUSY);
117977269Scg		}
1180170815Sariff		CHN_UNLOCK(ch);
118165487Scg	}
1182124740Smatk
1183170161Sariff	if (d->clones != NULL) {
1184170161Sariff		if (snd_clone_busy(d->clones) != 0) {
1185170161Sariff			device_printf(dev, "unregister: clone busy\n");
1186170815Sariff			PCM_RELEASE_QUICK(d);
1187170161Sariff			sndstat_release(td);
1188170815Sariff			return (EBUSY);
1189170815Sariff		} else {
1190193640Sariff			PCM_LOCK(d);
1191170161Sariff			(void)snd_clone_disable(d->clones);
1192193640Sariff			PCM_UNLOCK(d);
1193170815Sariff		}
1194170161Sariff	}
1195170161Sariff
1196157022Sariff	if (mixer_uninit(dev) == EBUSY) {
1197148587Snetchild		device_printf(dev, "unregister: mixer busy\n");
1198193640Sariff		PCM_LOCK(d);
1199170161Sariff		if (d->clones != NULL)
1200170161Sariff			(void)snd_clone_enable(d->clones);
1201170815Sariff		PCM_RELEASE(d);
1202193640Sariff		PCM_UNLOCK(d);
1203170161Sariff		sndstat_release(td);
1204170815Sariff		return (EBUSY);
1205148587Snetchild	}
1206148587Snetchild
1207193640Sariff	PCM_LOCK(d);
1208170815Sariff	d->flags |= SD_F_DYING;
1209170815Sariff	d->flags &= ~SD_F_REGISTERED;
1210193640Sariff	PCM_UNLOCK(d);
1211170815Sariff
1212170815Sariff	/*
1213170815Sariff	 * No lock being held, so this thing can be flushed without
1214170815Sariff	 * stucking into devdrn oblivion.
1215170815Sariff	 */
1216170161Sariff	if (d->clones != NULL) {
1217170161Sariff		snd_clone_destroy(d->clones);
1218170161Sariff		d->clones = NULL;
1219124740Smatk	}
1220124740Smatk
1221170161Sariff	if (d->play_sysctl_tree != NULL) {
1222170161Sariff		sysctl_ctx_free(&d->play_sysctl_ctx);
1223170161Sariff		d->play_sysctl_tree = NULL;
1224170161Sariff	}
1225170161Sariff	if (d->rec_sysctl_tree != NULL) {
1226170161Sariff		sysctl_ctx_free(&d->rec_sysctl_ctx);
1227170161Sariff		d->rec_sysctl_tree = NULL;
1228170161Sariff	}
1229157331Sariff
1230170161Sariff	while (!CHN_EMPTY(d, channels.pcm))
123177269Scg		pcm_killchan(dev);
123265207Scg
1233170815Sariff	dsp_cdevinfo_flush(d);
1234170815Sariff
1235193640Sariff	PCM_LOCK(d);
1236170815Sariff	PCM_RELEASE(d);
1237170815Sariff	cv_destroy(&d->cv);
1238193640Sariff	PCM_UNLOCK(d);
123977882Scg	snd_mtxfree(d->lock);
1240150827Snetchild	sndstat_unregister(dev);
1241170161Sariff	sndstat_release(td);
1242170161Sariff
1243170161Sariff	if (snd_unit == device_get_unit(dev)) {
1244222826Smav		snd_unit = pcm_best_unit(-1);
1245222826Smav		if (snd_unit_auto == 0)
1246222826Smav			snd_unit_auto = 1;
1247170161Sariff	}
1248170161Sariff
1249170815Sariff	return (0);
125065207Scg}
125165207Scg
125282180Scg/************************************************************************/
125382180Scg
1254162588Snetchild/**
1255162588Snetchild * @brief	Handle OSSv4 SNDCTL_SYSINFO ioctl.
1256162588Snetchild *
1257162588Snetchild * @param si	Pointer to oss_sysinfo struct where information about the
1258162588Snetchild * 		sound subsystem will be written/copied.
1259162588Snetchild *
1260162588Snetchild * This routine returns information about the sound system, such as the
1261162588Snetchild * current OSS version, number of audio, MIDI, and mixer drivers, etc.
1262162588Snetchild * Also includes a bitmask showing which of the above types of devices
1263162588Snetchild * are open (busy).
1264162588Snetchild *
1265162588Snetchild * @note
1266162588Snetchild * Calling threads must not hold any snddev_info or pcm_channel locks.
1267162588Snetchild *
1268162588Snetchild * @author	Ryan Beasley <ryanb@FreeBSD.org>
1269162588Snetchild */
1270162588Snetchildvoid
1271162588Snetchildsound_oss_sysinfo(oss_sysinfo *si)
1272162588Snetchild{
1273162588Snetchild	static char si_product[] = "FreeBSD native OSS ABI";
1274162588Snetchild	static char si_version[] = __XSTRING(__FreeBSD_version);
1275186875Smav	static char si_license[] = "BSD";
1276162588Snetchild	static int intnbits = sizeof(int) * 8;	/* Better suited as macro?
1277162588Snetchild						   Must pester a C guru. */
1278162588Snetchild
1279162588Snetchild	struct snddev_info *d;
1280162588Snetchild	struct pcm_channel *c;
1281162588Snetchild	int i, j, ncards;
1282162588Snetchild
1283162588Snetchild	ncards = 0;
1284162588Snetchild
1285162588Snetchild	strlcpy(si->product, si_product, sizeof(si->product));
1286162588Snetchild	strlcpy(si->version, si_version, sizeof(si->version));
1287162588Snetchild	si->versionnum = SOUND_VERSION;
1288186875Smav	strlcpy(si->license, si_license, sizeof(si->license));
1289162588Snetchild
1290162588Snetchild	/*
1291162588Snetchild	 * Iterate over PCM devices and their channels, gathering up data
1292162588Snetchild	 * for the numaudios, ncards, and openedaudio fields.
1293162588Snetchild	 */
1294162588Snetchild	si->numaudios = 0;
1295162588Snetchild	bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
1296162588Snetchild
1297170815Sariff	j = 0;
1298162588Snetchild
1299170815Sariff	for (i = 0; pcm_devclass != NULL &&
1300170815Sariff	    i < devclass_get_maxunit(pcm_devclass); i++) {
1301170815Sariff		d = devclass_get_softc(pcm_devclass, i);
1302170815Sariff		if (!PCM_REGISTERED(d))
1303170815Sariff			continue;
1304162588Snetchild
1305170815Sariff		/* XXX Need Giant magic entry ??? */
1306162588Snetchild
1307170815Sariff		/* See note in function's docblock */
1308193640Sariff		PCM_UNLOCKASSERT(d);
1309193640Sariff		PCM_LOCK(d);
1310162588Snetchild
1311170815Sariff		si->numaudios += d->devcount;
1312170815Sariff		++ncards;
1313162588Snetchild
1314170815Sariff		CHN_FOREACH(c, d, channels.pcm) {
1315193640Sariff			CHN_UNLOCKASSERT(c);
1316170815Sariff			CHN_LOCK(c);
1317170815Sariff			if (c->flags & CHN_F_BUSY)
1318170815Sariff				si->openedaudio[j / intnbits] |=
1319170815Sariff				    (1 << (j % intnbits));
1320170815Sariff			CHN_UNLOCK(c);
1321170815Sariff			j++;
1322162588Snetchild		}
1323170815Sariff
1324193640Sariff		PCM_UNLOCK(d);
1325162588Snetchild	}
1326186875Smav	si->numaudioengines = si->numaudios;
1327162588Snetchild
1328162588Snetchild	si->numsynths = 0;	/* OSSv4 docs:  this field is obsolete */
1329162588Snetchild	/**
1330162588Snetchild	 * @todo	Collect num{midis,timers}.
1331162588Snetchild	 *
1332162588Snetchild	 * Need access to sound/midi/midi.c::midistat_lock in order
1333162588Snetchild	 * to safely touch midi_devices and get a head count of, well,
1334162588Snetchild	 * MIDI devices.  midistat_lock is a global static (i.e., local to
1335162588Snetchild	 * midi.c), but midi_devices is a regular global; should the mutex
1336162588Snetchild	 * be publicized, or is there another way to get this information?
1337162588Snetchild	 *
1338162588Snetchild	 * NB:	MIDI/sequencer stuff is currently on hold.
1339162588Snetchild	 */
1340162588Snetchild	si->nummidis = 0;
1341162588Snetchild	si->numtimers = 0;
1342162588Snetchild	si->nummixers = mixer_count;
1343162588Snetchild	si->numcards = ncards;
1344162588Snetchild		/* OSSv4 docs:	Intended only for test apps; API doesn't
1345162588Snetchild		   really have much of a concept of cards.  Shouldn't be
1346162588Snetchild		   used by applications. */
1347162588Snetchild
1348162588Snetchild	/**
1349162588Snetchild	 * @todo	Fill in "busy devices" fields.
1350162588Snetchild	 *
1351162588Snetchild	 *  si->openedmidi = " MIDI devices
1352162588Snetchild	 */
1353162588Snetchild	bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
1354162588Snetchild
1355162588Snetchild	/*
1356162588Snetchild	 * Si->filler is a reserved array, but according to docs each
1357162588Snetchild	 * element should be set to -1.
1358162588Snetchild	 */
1359162588Snetchild	for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++)
1360162588Snetchild		si->filler[i] = -1;
1361162588Snetchild}
1362162588Snetchild
1363187030Smavint
1364187030Smavsound_oss_card_info(oss_card_info *si)
1365187030Smav{
1366187030Smav	struct snddev_info *d;
1367187030Smav	int i, ncards;
1368187030Smav
1369187030Smav	ncards = 0;
1370187030Smav
1371187030Smav	for (i = 0; pcm_devclass != NULL &&
1372187030Smav	    i < devclass_get_maxunit(pcm_devclass); i++) {
1373187030Smav		d = devclass_get_softc(pcm_devclass, i);
1374187030Smav		if (!PCM_REGISTERED(d))
1375187030Smav			continue;
1376187030Smav
1377187030Smav		if (ncards++ != si->card)
1378187030Smav			continue;
1379187030Smav
1380193640Sariff		PCM_UNLOCKASSERT(d);
1381193640Sariff		PCM_LOCK(d);
1382187030Smav
1383187030Smav		strlcpy(si->shortname, device_get_nameunit(d->dev),
1384187030Smav		    sizeof(si->shortname));
1385187030Smav		strlcpy(si->longname, device_get_desc(d->dev),
1386187030Smav		    sizeof(si->longname));
1387187030Smav		strlcpy(si->hw_info, d->status, sizeof(si->hw_info));
1388187030Smav		si->intr_count = si->ack_count = 0;
1389193640Sariff
1390193640Sariff		PCM_UNLOCK(d);
1391193640Sariff
1392187030Smav		return (0);
1393187030Smav	}
1394187030Smav	return (ENXIO);
1395187030Smav}
1396187030Smav
1397162588Snetchild/************************************************************************/
1398162588Snetchild
1399132236Stanimurastatic int
1400132236Stanimurasound_modevent(module_t mod, int type, void *data)
1401132236Stanimura{
1402162588Snetchild	int ret;
1403148587Snetchild#if 0
1404132236Stanimura	return (midi_modevent(mod, type, data));
1405148587Snetchild#else
1406162588Snetchild	ret = 0;
1407162588Snetchild
1408162588Snetchild	switch(type) {
1409162588Snetchild		case MOD_LOAD:
1410162588Snetchild			pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
1411162588Snetchild			break;
1412162588Snetchild		case MOD_UNLOAD:
1413170161Sariff			ret = sndstat_acquire(curthread);
1414170161Sariff			if (ret != 0)
1415170161Sariff				break;
1416162588Snetchild			if (pcmsg_unrhdr != NULL) {
1417162588Snetchild				delete_unrhdr(pcmsg_unrhdr);
1418162588Snetchild				pcmsg_unrhdr = NULL;
1419162588Snetchild			}
1420162588Snetchild			break;
1421231647Smav		case MOD_SHUTDOWN:
1422231647Smav			break;
1423162588Snetchild		default:
1424193640Sariff			ret = ENOTSUP;
1425162588Snetchild	}
1426162588Snetchild
1427162588Snetchild	return ret;
1428148587Snetchild#endif
1429132236Stanimura}
1430132236Stanimura
1431132236StanimuraDEV_MODULE(sound, sound_modevent, NULL);
1432132236StanimuraMODULE_VERSION(sound, SOUND_MODVER);
1433