sound.c revision 160439
1139749Simp/*-
2119853Scg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
350724Scg * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
450724Scg * All rights reserved.
550724Scg *
650724Scg * Redistribution and use in source and binary forms, with or without
750724Scg * modification, are permitted provided that the following conditions
850724Scg * are met:
950724Scg * 1. Redistributions of source code must retain the above copyright
1050724Scg *    notice, this list of conditions and the following disclaimer.
1150724Scg * 2. Redistributions in binary form must reproduce the above copyright
1250724Scg *    notice, this list of conditions and the following disclaimer in the
1350724Scg *    documentation and/or other materials provided with the distribution.
1450724Scg *
1550724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1650724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1750724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1850724Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1950724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2050724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2150724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2250724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2350724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2450724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2550724Scg * SUCH DAMAGE.
2650724Scg */
2750724Scg
2853465Scg#include <dev/sound/pcm/sound.h>
2977269Scg#include <dev/sound/pcm/vchan.h>
30124740Smatk#include <dev/sound/pcm/dsp.h>
3165207Scg#include <sys/sysctl.h>
3250724Scg
3382180Scg#include "feeder_if.h"
3482180Scg
3582180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 160439 2006-07-17 17:43:06Z netchild $");
3682180Scg
3778362Scgdevclass_t pcm_devclass;
3850724Scg
3989834Scgint pcm_veto_load = 1;
4089834Scg
4173127Scg#ifdef USING_DEVFS
4278362Scgint snd_unit = 0;
43159732SnetchildTUNABLE_INT("hw.snd.default_unit", &snd_unit);
4473127Scg#endif
4579141Scg
46159732Snetchildint snd_maxautovchans = 4;
47159732Snetchild/* XXX: a tunable implies that we may need more than one sound channel before
48159732Snetchild   the system can change a sysctl (/etc/sysctl.conf), do we really need
49159732Snetchild   this? */
5082180ScgTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
5150724Scg
5273127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
5373127Scg
5482180Scgstatic int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
5582180Scg
5682180Scgstruct sysctl_ctx_list *
5782180Scgsnd_sysctl_tree(device_t dev)
5882180Scg{
59157331Sariff	struct snddev_info *d = device_get_softc(dev);
6082180Scg
6182180Scg	return &d->sysctl_tree;
6282180Scg}
6382180Scg
6482180Scgstruct sysctl_oid *
6582180Scgsnd_sysctl_tree_top(device_t dev)
6682180Scg{
67157331Sariff	struct snddev_info *d = device_get_softc(dev);
6882180Scg
6982180Scg	return d->sysctl_tree_top;
7082180Scg}
7182180Scg
7273131Scgvoid *
7393814Sjhbsnd_mtxcreate(const char *desc, const char *type)
7473131Scg{
7573131Scg#ifdef USING_MUTEX
7673131Scg	struct mtx *m;
7773131Scg
78111119Simp	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
7973131Scg	if (m == NULL)
8073131Scg		return NULL;
81125136Struckman	mtx_init(m, desc, type, MTX_DEF);
8273131Scg	return m;
8373131Scg#else
8473757Scg	return (void *)0xcafebabe;
8573131Scg#endif
8673131Scg}
8773131Scg
8873131Scgvoid
8973131Scgsnd_mtxfree(void *m)
9073131Scg{
9173131Scg#ifdef USING_MUTEX
9273131Scg	struct mtx *mtx = m;
9373131Scg
94107237Scg	/* mtx_assert(mtx, MA_OWNED); */
9573131Scg	mtx_destroy(mtx);
9673131Scg	free(mtx, M_DEVBUF);
9773131Scg#endif
9873131Scg}
9973131Scg
10073131Scgvoid
10173131Scgsnd_mtxassert(void *m)
10273131Scg{
10373131Scg#ifdef USING_MUTEX
10478670Scg#ifdef INVARIANTS
10573131Scg	struct mtx *mtx = m;
10673131Scg
10773131Scg	mtx_assert(mtx, MA_OWNED);
10873131Scg#endif
10978670Scg#endif
11073131Scg}
111107237Scg/*
11273131Scgvoid
11373131Scgsnd_mtxlock(void *m)
11473131Scg{
11573131Scg#ifdef USING_MUTEX
11673131Scg	struct mtx *mtx = m;
11773131Scg
11873131Scg	mtx_lock(mtx);
11973131Scg#endif
12073131Scg}
12173131Scg
12273131Scgvoid
12373131Scgsnd_mtxunlock(void *m)
12473131Scg{
12573131Scg#ifdef USING_MUTEX
12673131Scg	struct mtx *mtx = m;
12773131Scg
12873131Scg	mtx_unlock(mtx);
12973131Scg#endif
13073131Scg}
131107237Scg*/
13273131Scgint
13373131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
13473131Scg{
13573131Scg#ifdef USING_MUTEX
13673131Scg	flags &= INTR_MPSAFE;
13778366Speter	flags |= INTR_TYPE_AV;
13873131Scg#else
13978366Speter	flags = INTR_TYPE_AV;
14073131Scg#endif
14173131Scg	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
14273131Scg}
14373131Scg
144119096Scg#ifndef	PCM_DEBUG_MTX
14582180Scgvoid
14682180Scgpcm_lock(struct snddev_info *d)
14782180Scg{
14882180Scg	snd_mtxlock(d->lock);
14982180Scg}
15082180Scg
15182180Scgvoid
15282180Scgpcm_unlock(struct snddev_info *d)
15382180Scg{
15482180Scg	snd_mtxunlock(d->lock);
15582180Scg}
156119096Scg#endif
15782180Scg
15882180Scgstruct pcm_channel *
15982180Scgpcm_getfakechan(struct snddev_info *d)
16082180Scg{
16182180Scg	return d->fakechan;
16282180Scg}
16382180Scg
164157331Sariffstatic int
165157331Sariffpcm_setvchans(struct snddev_info *d, int newcnt)
166157331Sariff{
167157331Sariff	struct snddev_channel *sce = NULL;
168157331Sariff	struct pcm_channel *c = NULL;
169157331Sariff	int err = 0, vcnt, dcnt, i;
170157331Sariff
171157331Sariff	pcm_inprog(d, 1);
172157331Sariff
173157331Sariff	if (!(d->flags & SD_F_AUTOVCHAN)) {
174157331Sariff		err = EINVAL;
175157331Sariff		goto setvchans_out;
176157331Sariff	}
177157331Sariff
178157331Sariff	vcnt = d->vchancount;
179157331Sariff	dcnt = d->playcount + d->reccount;
180157331Sariff
181157331Sariff	if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) {
182157331Sariff		err = E2BIG;
183157331Sariff		goto setvchans_out;
184157331Sariff	}
185157331Sariff
186157331Sariff	dcnt += vcnt;
187157331Sariff
188157331Sariff	if (newcnt > vcnt) {
189157331Sariff		/* add new vchans - find a parent channel first */
190157331Sariff		SLIST_FOREACH(sce, &d->channels, link) {
191157331Sariff			c = sce->channel;
192157331Sariff			CHN_LOCK(c);
193157331Sariff			if (c->direction == PCMDIR_PLAY &&
194157331Sariff					((c->flags & CHN_F_HAS_VCHAN) ||
195157331Sariff					(vcnt == 0 &&
196157331Sariff					!(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)))))
197157331Sariff				goto addok;
198157331Sariff			CHN_UNLOCK(c);
199157331Sariff		}
200157331Sariff		err = EBUSY;
201157331Sariff		goto setvchans_out;
202157331Sariffaddok:
203157331Sariff		c->flags |= CHN_F_BUSY;
204157331Sariff		while (err == 0 && newcnt > vcnt) {
205157331Sariff			if (dcnt > PCMMAXCHAN) {
206157331Sariff				device_printf(d->dev, "%s: Maximum channel reached.\n", __func__);
207157331Sariff				break;
208157331Sariff			}
209157331Sariff			err = vchan_create(c);
210157331Sariff			if (err == 0) {
211157331Sariff				vcnt++;
212157331Sariff				dcnt++;
213157331Sariff			} else if (err == E2BIG && newcnt > vcnt)
214157331Sariff				device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err);
215157331Sariff		}
216157331Sariff		if (vcnt == 0)
217157331Sariff			c->flags &= ~CHN_F_BUSY;
218157331Sariff		CHN_UNLOCK(c);
219157331Sariff	} else if (newcnt < vcnt) {
220157331Sariff#define ORPHAN_CDEVT(cdevt) \
221157331Sariff	((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \
222157331Sariff	(cdevt)->si_drv2 == NULL))
223157331Sariff		while (err == 0 && newcnt < vcnt) {
224157331Sariff			i = 0;
225157331Sariff			SLIST_FOREACH(sce, &d->channels, link) {
226157331Sariff				c = sce->channel;
227157331Sariff				CHN_LOCK(c);
228157331Sariff				if (c->direction == PCMDIR_PLAY &&
229157331Sariff						(c->flags & CHN_F_VIRTUAL) &&
230157331Sariff						(i++ == newcnt)) {
231157331Sariff					if (!(c->flags & CHN_F_BUSY) &&
232157331Sariff							ORPHAN_CDEVT(sce->dsp_devt) &&
233157331Sariff							ORPHAN_CDEVT(sce->dspW_devt) &&
234157331Sariff							ORPHAN_CDEVT(sce->audio_devt) &&
235157331Sariff							ORPHAN_CDEVT(sce->dspr_devt))
236157331Sariff						goto remok;
237157331Sariff					/*
238157331Sariff					 * Either we're busy, or our cdev
239157331Sariff					 * has been stolen by dsp_clone().
240157331Sariff					 * Skip, and increase newcnt.
241157331Sariff					 */
242157331Sariff					if (!(c->flags & CHN_F_BUSY))
243157331Sariff						device_printf(d->dev,
244157331Sariff							"%s: <%s> somebody steal my cdev!\n",
245157331Sariff							__func__, c->name);
246157331Sariff					newcnt++;
247157331Sariff				}
248157331Sariff				CHN_UNLOCK(c);
249157331Sariff			}
250157331Sariff			if (vcnt != newcnt)
251157331Sariff				err = EBUSY;
252157331Sariff			break;
253157331Sariffremok:
254157331Sariff			CHN_UNLOCK(c);
255157331Sariff			err = vchan_destroy(c);
256157331Sariff			if (err == 0)
257157331Sariff				vcnt--;
258157331Sariff			else
259157331Sariff				device_printf(d->dev,
260157331Sariff					"%s: WARNING: vchan_destroy() failed!",
261157331Sariff					__func__);
262157331Sariff		}
263157331Sariff	}
264157331Sariff
265157331Sariffsetvchans_out:
266157331Sariff	pcm_inprog(d, -1);
267157331Sariff	return err;
268157331Sariff}
269157331Sariff
270156929Sariff/* return error status and a locked channel */
271156929Sariffint
272156929Sariffpcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
273156929Sariff		pid_t pid, int chnum)
27477269Scg{
27577269Scg	struct pcm_channel *c;
276157331Sariff	struct snddev_channel *sce;
277157331Sariff	int err;
27877269Scg
279156929Sariffretry_chnalloc:
280157331Sariff	err = ENODEV;
28178895Scg	/* scan for a free channel */
28277269Scg	SLIST_FOREACH(sce, &d->channels, link) {
28377269Scg		c = sce->channel;
28477882Scg		CHN_LOCK(c);
285157331Sariff		if (c->direction == direction && !(c->flags & CHN_F_BUSY)) {
286156929Sariff			if (chnum < 0 || sce->chan_num == chnum) {
28783089Scg				c->flags |= CHN_F_BUSY;
28883089Scg				c->pid = pid;
289156929Sariff				*ch = c;
290156929Sariff				return 0;
29183089Scg			}
29277269Scg		}
293156929Sariff		if (sce->chan_num == chnum) {
294156929Sariff			if (c->direction != direction)
295156929Sariff				err = EOPNOTSUPP;
296156929Sariff			else if (c->flags & CHN_F_BUSY)
297156929Sariff				err = EBUSY;
298156929Sariff			else
299156929Sariff				err = EINVAL;
300156929Sariff			CHN_UNLOCK(c);
301156929Sariff			return err;
302156929Sariff		} else if (c->direction == direction && (c->flags & CHN_F_BUSY))
303157331Sariff			err = EBUSY;
30477882Scg		CHN_UNLOCK(c);
30577269Scg	}
30678895Scg
30778895Scg	/* no channel available */
308156929Sariff	if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 &&
309156762Sariff			d->vchancount < snd_maxautovchans &&
310156762Sariff			d->devcount <= PCMMAXCHAN) {
311157331Sariff		err = pcm_setvchans(d, d->vchancount + 1);
312157331Sariff		if (err == 0) {
313157331Sariff			chnum = -2;
314157331Sariff			goto retry_chnalloc;
31578895Scg		}
31678895Scg	}
31778895Scg
318157331Sariff	return err;
31977269Scg}
32077269Scg
32178214Scg/* release a locked channel and unlock it */
32277269Scgint
32378214Scgpcm_chnrelease(struct pcm_channel *c)
32477269Scg{
32578214Scg	CHN_LOCKASSERT(c);
32677269Scg	c->flags &= ~CHN_F_BUSY;
32778214Scg	c->pid = -1;
32877882Scg	CHN_UNLOCK(c);
32977269Scg	return 0;
33077269Scg}
33177269Scg
33277269Scgint
33377269Scgpcm_chnref(struct pcm_channel *c, int ref)
33477269Scg{
33577882Scg	int r;
33677882Scg
33778214Scg	CHN_LOCKASSERT(c);
33877269Scg	c->refcount += ref;
33977882Scg	r = c->refcount;
34077882Scg	return r;
34177269Scg}
34277269Scg
34382180Scgint
34482180Scgpcm_inprog(struct snddev_info *d, int delta)
34582180Scg{
346119096Scg	int r;
347119096Scg
348119096Scg	if (delta == 0)
349119096Scg		return d->inprog;
350119096Scg
351119096Scg	/* backtrace(); */
352119096Scg	pcm_lock(d);
35382180Scg	d->inprog += delta;
354119096Scg	r = d->inprog;
355119096Scg	pcm_unlock(d);
356119096Scg	return r;
35782180Scg}
35882180Scg
35982180Scgstatic void
36082180Scgpcm_setmaxautovchans(struct snddev_info *d, int num)
36182180Scg{
362157331Sariff	if (num > 0 && d->vchancount == 0)
363157331Sariff		pcm_setvchans(d, 1);
364157331Sariff	else if (num == 0 && d->vchancount > 0)
365157331Sariff		pcm_setvchans(d, 0);
36682180Scg}
36782180Scg
36873127Scg#ifdef USING_DEVFS
36965340Scgstatic int
370159732Snetchildsysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
37165340Scg{
37278214Scg	struct snddev_info *d;
37365340Scg	int error, unit;
37465340Scg
37565340Scg	unit = snd_unit;
37665340Scg	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
37765340Scg	if (error == 0 && req->newptr != NULL) {
37878396Scg		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
37978214Scg			return EINVAL;
38078214Scg		d = devclass_get_softc(pcm_devclass, unit);
38178395Scg		if (d == NULL || SLIST_EMPTY(&d->channels))
38278214Scg			return EINVAL;
38365340Scg		snd_unit = unit;
38465340Scg	}
38565340Scg	return (error);
38665340Scg}
387159732Snetchild/* XXX: do we need a way to let the user change the default unit? */
388159732SnetchildSYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW,
389159732Snetchild            0, sizeof(int), sysctl_hw_snd_default_unit, "I", "");
39073127Scg#endif
39165340Scg
39278853Scgstatic int
39382180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
39478853Scg{
39582180Scg	struct snddev_info *d;
39682180Scg	int i, v, error;
39778853Scg
39882180Scg	v = snd_maxautovchans;
39978853Scg	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
40078853Scg	if (error == 0 && req->newptr != NULL) {
401157331Sariff		if (v < 0 || v > PCMMAXCHAN)
402157331Sariff			return E2BIG;
403156929Sariff		if (pcm_devclass != NULL && v != snd_maxautovchans) {
40482180Scg			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
40582180Scg				d = devclass_get_softc(pcm_devclass, i);
40682180Scg				if (!d)
40782180Scg					continue;
408157331Sariff				pcm_setmaxautovchans(d, v);
40982180Scg			}
41082180Scg		}
41182180Scg		snd_maxautovchans = v;
41278853Scg	}
41378853Scg	return (error);
41478853Scg}
41582180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
41682180Scg            0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
41778853Scg
41877269Scgstruct pcm_channel *
41977269Scgpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
42050724Scg{
421156929Sariff	struct snddev_channel *sce;
422157331Sariff	struct pcm_channel *ch, *c;
42365340Scg	char *dirs;
424157331Sariff	uint32_t flsearch = 0;
425156929Sariff	int direction, err, rpnum, *pnum;
42650724Scg
42777269Scg	switch(dir) {
42877269Scg	case PCMDIR_PLAY:
42977269Scg		dirs = "play";
430126367Struckman		direction = PCMDIR_PLAY;
43183614Scg		pnum = &d->playcount;
43277269Scg		break;
43383614Scg
43477269Scg	case PCMDIR_REC:
43577269Scg		dirs = "record";
436126367Struckman		direction = PCMDIR_REC;
43783614Scg		pnum = &d->reccount;
43877269Scg		break;
43983614Scg
44077269Scg	case PCMDIR_VIRTUAL:
44177269Scg		dirs = "virtual";
442126367Struckman		direction = PCMDIR_PLAY;
44383614Scg		pnum = &d->vchancount;
444156929Sariff		flsearch = CHN_F_VIRTUAL;
44577269Scg		break;
44683614Scg
44777269Scg	default:
44877269Scg		return NULL;
44977269Scg	}
45065340Scg
451111119Simp	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
45277269Scg	if (!ch)
45377269Scg		return NULL;
45477269Scg
455111119Simp	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
45677269Scg	if (!ch->methods) {
45777269Scg		free(ch, M_DEVBUF);
45883614Scg
45977269Scg		return NULL;
46061344Scg	}
46177269Scg
462107237Scg	snd_mtxlock(d->lock);
463156929Sariff	ch->num = 0;
464156929Sariff	rpnum = 0;
465156929Sariff	SLIST_FOREACH(sce, &d->channels, link) {
466157331Sariff		c = sce->channel;
467157331Sariff		if (direction != c->direction ||
468157331Sariff				(c->flags & CHN_F_VIRTUAL) != flsearch)
469156929Sariff			continue;
470157331Sariff		if (ch->num == c->num)
471156929Sariff			ch->num++;
472156929Sariff		else {
473157331Sariff#if 0
474156929Sariff			device_printf(d->dev,
475157331Sariff				"%s: %s channel numbering screwed (Expect: %d, Got: %d)\n",
476157331Sariff				__func__, dirs, ch->num, c->num);
477157331Sariff#endif
478156929Sariff			goto retry_num_search;
479156929Sariff		}
480156929Sariff		rpnum++;
481156929Sariff	}
482156929Sariff	goto retry_num_search_out;
483156929Sariffretry_num_search:
484156929Sariff	rpnum = 0;
485156929Sariff	SLIST_FOREACH(sce, &d->channels, link) {
486157331Sariff		c = sce->channel;
487157331Sariff		if (direction != c->direction ||
488157331Sariff				(c->flags & CHN_F_VIRTUAL) != flsearch)
489156929Sariff			continue;
490157331Sariff		if (ch->num == c->num) {
491156929Sariff			ch->num++;
492156929Sariff			goto retry_num_search;
493156929Sariff		}
494156929Sariff		rpnum++;
495156929Sariff	}
496156929Sariffretry_num_search_out:
497156929Sariff	if (*pnum != rpnum) {
498156929Sariff		device_printf(d->dev,
499157331Sariff			"%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
500156929Sariff			__func__, dirs, *pnum, rpnum);
501156929Sariff		*pnum = rpnum;
502156929Sariff	}
503156929Sariff	(*pnum)++;
504107237Scg	snd_mtxunlock(d->lock);
50583614Scg
50677269Scg	ch->pid = -1;
50777269Scg	ch->parentsnddev = d;
50877269Scg	ch->parentchannel = parent;
50989774Sscottl	ch->dev = d->dev;
510156929Sariff	snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
51177269Scg
512126367Struckman	err = chn_init(ch, devinfo, dir, direction);
51370134Scg	if (err) {
51483614Scg		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
51577269Scg		kobj_delete(ch->methods, M_DEVBUF);
51677269Scg		free(ch, M_DEVBUF);
517107237Scg		snd_mtxlock(d->lock);
51883614Scg		(*pnum)--;
519107237Scg		snd_mtxunlock(d->lock);
52083614Scg
52177269Scg		return NULL;
52255483Scg	}
52377269Scg
52477269Scg	return ch;
52577269Scg}
52677269Scg
52777269Scgint
52877269Scgpcm_chn_destroy(struct pcm_channel *ch)
52977269Scg{
53083614Scg	struct snddev_info *d;
53177269Scg	int err;
53277269Scg
53383614Scg	d = ch->parentsnddev;
53477269Scg	err = chn_kill(ch);
53577269Scg	if (err) {
53683614Scg		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
53777269Scg		return err;
53877269Scg	}
53977269Scg
54077269Scg	kobj_delete(ch->methods, M_DEVBUF);
54177269Scg	free(ch, M_DEVBUF);
54277269Scg
54377269Scg	return 0;
54477269Scg}
54577269Scg
54677269Scgint
547124740Smatkpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
54877269Scg{
549157331Sariff	struct snddev_channel *sce, *tmp, *after;
550156929Sariff	unsigned rdevcount;
551157331Sariff	int device = device_get_unit(d->dev);
552156929Sariff	size_t namelen;
55377269Scg
554124740Smatk	/*
555124740Smatk	 * Note it's confusing nomenclature.
556124740Smatk	 * dev_t
557124740Smatk	 * device -> pcm_device
558157331Sariff	 * unit -> pcm_channel
559124740Smatk	 * channel -> snddev_channel
560124740Smatk	 * device_t
561124740Smatk	 * unit -> pcm_device
562124740Smatk	 */
563124740Smatk
564111119Simp	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
56577269Scg	if (!sce) {
56677269Scg		return ENOMEM;
56777269Scg	}
56877269Scg
569100478Sorion	snd_mtxlock(d->lock);
57077269Scg	sce->channel = ch;
571156929Sariff	sce->chan_num = 0;
572157331Sariff	rdevcount = 0;
573156929Sariff	after = NULL;
574157331Sariff	SLIST_FOREACH(tmp, &d->channels, link) {
575157331Sariff		if (sce->chan_num == tmp->chan_num)
576157331Sariff			sce->chan_num++;
577157331Sariff		else {
578157331Sariff#if 0
579157331Sariff			device_printf(d->dev,
580157331Sariff				"%s: cdev numbering screwed (Expect: %d, Got: %d)\n",
581157331Sariff				__func__, sce->chan_num, tmp->chan_num);
582157331Sariff#endif
583157331Sariff			goto retry_chan_num_search;
584157331Sariff		}
585157331Sariff		after = tmp;
586157331Sariff		rdevcount++;
587157331Sariff	}
588157331Sariff	goto retry_chan_num_search_out;
589156929Sariffretry_chan_num_search:
590156929Sariff	/*
591156929Sariff	 * Look for possible channel numbering collision. This may not
592156929Sariff	 * be optimized, but it will ensure that no collision occured.
593157331Sariff	 * Can be considered cheap since none of the locking/unlocking
594157331Sariff	 * operations involved.
595156929Sariff	 */
596156929Sariff	rdevcount = 0;
597157331Sariff	after = NULL;
598156929Sariff	SLIST_FOREACH(tmp, &d->channels, link) {
599156929Sariff		if (sce->chan_num == tmp->chan_num) {
600156929Sariff			sce->chan_num++;
601156929Sariff			goto retry_chan_num_search;
602156762Sariff		}
603157331Sariff		if (sce->chan_num > tmp->chan_num)
604157331Sariff			after = tmp;
605156929Sariff		rdevcount++;
60682180Scg	}
607157331Sariffretry_chan_num_search_out:
608156929Sariff	/*
609156929Sariff	 * Don't overflow PCMMKMINOR / PCMMAXCHAN.
610156929Sariff	 */
611156929Sariff	if (sce->chan_num > PCMMAXCHAN) {
612156929Sariff		snd_mtxunlock(d->lock);
613156929Sariff		device_printf(d->dev,
614156929Sariff			"%s: WARNING: sce->chan_num overflow! (%d)\n",
615156929Sariff			__func__, sce->chan_num);
616156929Sariff		free(sce, M_DEVBUF);
617156929Sariff		return E2BIG;
618156929Sariff	}
619156929Sariff	if (d->devcount != rdevcount) {
620156929Sariff		device_printf(d->dev,
621156929Sariff			"%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n",
622156929Sariff			__func__, d->devcount, rdevcount);
623156929Sariff		d->devcount = rdevcount;
624156929Sariff	}
625156762Sariff	d->devcount++;
626156929Sariff	if (after == NULL) {
627156929Sariff		SLIST_INSERT_HEAD(&d->channels, sce, link);
628156929Sariff	} else {
629156929Sariff		SLIST_INSERT_AFTER(after, sce, link);
630156929Sariff	}
631157331Sariff#if 0
632157331Sariff	if (1) {
633157331Sariff		int cnum = 0;
634157331Sariff		SLIST_FOREACH(tmp, &d->channels, link) {
635157331Sariff			if (cnum != tmp->chan_num)
636157331Sariff				device_printf(d->dev,
637157331Sariff					"%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n",
638157331Sariff					__func__, cnum, tmp->chan_num);
639157331Sariff			cnum++;
640157331Sariff		}
641157331Sariff	}
642157331Sariff#endif
643156929Sariff
644156929Sariff	namelen = strlen(ch->name);
645156929Sariff	if ((CHN_NAMELEN - namelen) > 10) {	/* ":dspXX.YYY" */
646156929Sariff		snprintf(ch->name + namelen,
647156929Sariff			CHN_NAMELEN - namelen, ":dsp%d.%d",
648156929Sariff			device, sce->chan_num);
649156929Sariff	}
650107237Scg	snd_mtxunlock(d->lock);
651157331Sariff
652157331Sariff	/*
653157331Sariff	 * I will revisit these someday, and nuke it mercilessly..
654157331Sariff	 */
655156929Sariff	sce->dsp_devt = make_dev(&dsp_cdevsw,
656124740Smatk			PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
657124740Smatk			UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
658124740Smatk			device, sce->chan_num);
65977269Scg
660156929Sariff	sce->dspW_devt = make_dev(&dsp_cdevsw,
661124740Smatk			PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
662124740Smatk			UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
663124740Smatk			device, sce->chan_num);
66477269Scg
665156929Sariff	sce->audio_devt = make_dev(&dsp_cdevsw,
666124740Smatk			PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
667124740Smatk			UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
668124740Smatk			device, sce->chan_num);
669124740Smatk
670124740Smatk	if (ch->direction == PCMDIR_REC)
671124740Smatk		sce->dspr_devt = make_dev(&dsp_cdevsw,
672124740Smatk				PCMMKMINOR(device, SND_DEV_DSPREC,
673124740Smatk					sce->chan_num), UID_ROOT, GID_WHEEL,
674124740Smatk				0666, "dspr%d.%d", device, sce->chan_num);
675124740Smatk
67650724Scg	return 0;
67750724Scg}
67850724Scg
67977269Scgint
680124740Smatkpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
68165340Scg{
682157331Sariff	struct snddev_channel *sce;
683124617Sphk#if 0
684119096Scg	int ourlock;
68565340Scg
686119096Scg	ourlock = 0;
687119096Scg	if (!mtx_owned(d->lock)) {
688119096Scg		snd_mtxlock(d->lock);
689119096Scg		ourlock = 1;
690119096Scg	}
691124617Sphk#endif
692119096Scg
69377269Scg	SLIST_FOREACH(sce, &d->channels, link) {
69477269Scg		if (sce->channel == ch)
69577269Scg			goto gotit;
69665340Scg	}
697124617Sphk#if 0
698119096Scg	if (ourlock)
699119096Scg		snd_mtxunlock(d->lock);
700124617Sphk#endif
70177269Scg	return EINVAL;
70277269Scggotit:
70377269Scg	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
704107237Scg
705149953Snetchild	if (ch->flags & CHN_F_VIRTUAL)
706149953Snetchild		d->vchancount--;
707149953Snetchild	else if (ch->direction == PCMDIR_REC)
708107237Scg		d->reccount--;
709107237Scg	else
710107237Scg		d->playcount--;
711107237Scg
712124617Sphk#if 0
713119096Scg	if (ourlock)
714119096Scg		snd_mtxunlock(d->lock);
715124617Sphk#endif
716107237Scg	free(sce, M_DEVBUF);
71777269Scg
71865340Scg	return 0;
71965340Scg}
72065340Scg
72150724Scgint
72277269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
72377269Scg{
724157331Sariff	struct snddev_info *d = device_get_softc(dev);
72582180Scg	struct pcm_channel *ch;
726157331Sariff	int err;
72777269Scg
72877269Scg	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
72977269Scg	if (!ch) {
73077269Scg		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
73177269Scg		return ENODEV;
73277269Scg	}
73378853Scg
734124740Smatk	err = pcm_chn_add(d, ch);
73577269Scg	if (err) {
73677269Scg		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
73777269Scg		pcm_chn_destroy(ch);
73878853Scg		return err;
73977269Scg	}
74077269Scg
74177269Scg	return err;
74277269Scg}
74377269Scg
74477269Scgstatic int
74577269Scgpcm_killchan(device_t dev)
74677269Scg{
747157331Sariff	struct snddev_info *d = device_get_softc(dev);
748157331Sariff	struct snddev_channel *sce;
749106420Scognet	struct pcm_channel *ch;
750106420Scognet	int error = 0;
75177269Scg
75277269Scg	sce = SLIST_FIRST(&d->channels);
753106420Scognet	ch = sce->channel;
75477269Scg
755124740Smatk	error = pcm_chn_remove(d, sce->channel);
756106420Scognet	if (error)
757106420Scognet		return (error);
758106420Scognet	return (pcm_chn_destroy(ch));
75977269Scg}
76077269Scg
76177269Scgint
76250724Scgpcm_setstatus(device_t dev, char *str)
76350724Scg{
764157331Sariff	struct snddev_info *d = device_get_softc(dev);
76577882Scg
76677882Scg	snd_mtxlock(d->lock);
76750724Scg	strncpy(d->status, str, SND_STATUSLEN);
76877882Scg	snd_mtxunlock(d->lock);
769157331Sariff	if (snd_maxautovchans > 0)
770157331Sariff		pcm_setvchans(d, 1);
77150724Scg	return 0;
77250724Scg}
77350724Scg
774157331Sariffuint32_t
77550724Scgpcm_getflags(device_t dev)
77650724Scg{
777157331Sariff	struct snddev_info *d = device_get_softc(dev);
77877882Scg
77950724Scg	return d->flags;
78050724Scg}
78150724Scg
78250724Scgvoid
783157331Sariffpcm_setflags(device_t dev, uint32_t val)
78450724Scg{
785157331Sariff	struct snddev_info *d = device_get_softc(dev);
78678362Scg
78750724Scg	d->flags = val;
78850724Scg}
78950724Scg
79058384Scgvoid *
79158384Scgpcm_getdevinfo(device_t dev)
79258384Scg{
793157331Sariff	struct snddev_info *d = device_get_softc(dev);
79477882Scg
79558384Scg	return d->devinfo;
79658384Scg}
79758384Scg
79883614Scgunsigned int
799160439Snetchildpcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
80083614Scg{
801157331Sariff	struct snddev_info *d = device_get_softc(dev);
80289690Scg	int sz, x;
80383614Scg
80483614Scg	sz = 0;
80589690Scg	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
80689690Scg		x = sz;
807160439Snetchild		RANGE(sz, minbufsz, maxbufsz);
80889690Scg		if (x != sz)
809160439Snetchild			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
810160439Snetchild		x = minbufsz;
81189690Scg		while (x < sz)
81289690Scg			x <<= 1;
81389690Scg		if (x > sz)
81489690Scg			x >>= 1;
81589690Scg		if (x != sz) {
81689690Scg			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
81789690Scg			sz = x;
81889690Scg		}
81989690Scg	} else {
82083614Scg		sz = deflt;
82189690Scg	}
82289690Scg
82383614Scg	d->bufsz = sz;
82483614Scg
82583614Scg	return sz;
82683614Scg}
82783614Scg
82850724Scgint
82950724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec)
83050724Scg{
831157331Sariff	struct snddev_info *d = device_get_softc(dev);
83250724Scg
83389834Scg	if (pcm_veto_load) {
83489834Scg		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
83589834Scg
83689834Scg		return EINVAL;
83789834Scg	}
83889834Scg
83993814Sjhb	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
84077269Scg
841148587Snetchild#if 0
842148587Snetchild	/*
843148587Snetchild	 * d->flags should be cleared by the allocator of the softc.
844148587Snetchild	 * We cannot clear this field here because several devices set
845148587Snetchild	 * this flag before calling pcm_register().
846148587Snetchild	 */
84778853Scg	d->flags = 0;
848148587Snetchild#endif
84961886Scg	d->dev = dev;
85050724Scg	d->devinfo = devinfo;
85178895Scg	d->devcount = 0;
85283089Scg	d->reccount = 0;
85383614Scg	d->playcount = 0;
85478895Scg	d->vchancount = 0;
85578362Scg	d->inprog = 0;
85650724Scg
857124617Sphk	SLIST_INIT(&d->channels);
858124617Sphk
859157331Sariff	if ((numplay == 0 || numrec == 0) && numplay != numrec)
86078214Scg		d->flags |= SD_F_SIMPLEX;
86178362Scg
86278214Scg	d->fakechan = fkchan_setup(dev);
863126367Struckman	chn_init(d->fakechan, NULL, 0, 0);
86455494Scg
86573127Scg#ifdef SND_DYNSYSCTL
86670680Sjhb	sysctl_ctx_init(&d->sysctl_tree);
86770680Sjhb	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
86870943Sjhb				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
86970680Sjhb				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
87070943Sjhb	if (d->sysctl_tree_top == NULL) {
87170680Sjhb		sysctl_ctx_free(&d->sysctl_tree);
87270680Sjhb		goto no;
87370680Sjhb	}
874159732Snetchild	/* XXX: an user should be able to set this with a control tool, the
875159732Snetchild	   sysadmin then needs min+max sysctls for this */
87683614Scg	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
877159732Snetchild            OID_AUTO, "_buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
87873127Scg#endif
879149953Snetchild	if (numplay > 0) {
880156929Sariff		d->flags |= SD_F_AUTOVCHAN;
88182180Scg		vchan_initsys(dev);
882149953Snetchild	}
88378853Scg
88482180Scg	sndstat_register(dev, d->status, sndstat_prepare_pcm);
885157331Sariff	return 0;
88650724Scgno:
88777882Scg	snd_mtxfree(d->lock);
88850724Scg	return ENXIO;
88950724Scg}
89050724Scg
89165340Scgint
89265340Scgpcm_unregister(device_t dev)
89365207Scg{
894157331Sariff	struct snddev_info *d = device_get_softc(dev);
895157331Sariff	struct snddev_channel *sce;
896157331Sariff	struct pcmchan_children *pce;
89783614Scg	struct pcm_channel *ch;
89865207Scg
899150827Snetchild	if (sndstat_acquire() != 0) {
900150827Snetchild		device_printf(dev, "unregister: sndstat busy\n");
901150827Snetchild		return EBUSY;
902150827Snetchild	}
903150827Snetchild
90477882Scg	snd_mtxlock(d->lock);
90578362Scg	if (d->inprog) {
90683476Sgreid		device_printf(dev, "unregister: operation in progress\n");
90778362Scg		snd_mtxunlock(d->lock);
908150827Snetchild		sndstat_release();
90978362Scg		return EBUSY;
91078362Scg	}
911124740Smatk
91277269Scg	SLIST_FOREACH(sce, &d->channels, link) {
91383614Scg		ch = sce->channel;
91483614Scg		if (ch->refcount > 0) {
91595684Scg			device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
91677882Scg			snd_mtxunlock(d->lock);
917150827Snetchild			sndstat_release();
91877269Scg			return EBUSY;
91977269Scg		}
92065487Scg	}
921124740Smatk
922157022Sariff	if (mixer_uninit(dev) == EBUSY) {
923148587Snetchild		device_printf(dev, "unregister: mixer busy\n");
924148587Snetchild		snd_mtxunlock(d->lock);
925150827Snetchild		sndstat_release();
926148587Snetchild		return EBUSY;
927148587Snetchild	}
928148587Snetchild
929124740Smatk	SLIST_FOREACH(sce, &d->channels, link) {
930156762Sariff		if (sce->dsp_devt) {
931149953Snetchild			destroy_dev(sce->dsp_devt);
932156762Sariff			sce->dsp_devt = NULL;
933156762Sariff		}
934156762Sariff		if (sce->dspW_devt) {
935149953Snetchild			destroy_dev(sce->dspW_devt);
936156762Sariff			sce->dspW_devt = NULL;
937156762Sariff		}
938156762Sariff		if (sce->audio_devt) {
939149953Snetchild			destroy_dev(sce->audio_devt);
940156762Sariff			sce->audio_devt = NULL;
941156762Sariff		}
942156762Sariff		if (sce->dspr_devt) {
943124740Smatk			destroy_dev(sce->dspr_devt);
944156762Sariff			sce->dspr_devt = NULL;
945156762Sariff		}
946156762Sariff		d->devcount--;
947157331Sariff		ch = sce->channel;
948157331Sariff		if (ch == NULL)
949157331Sariff			continue;
950157331Sariff		pce = SLIST_FIRST(&ch->children);
951157331Sariff		while (pce != NULL) {
952157331Sariff#if 0
953157331Sariff			device_printf(d->dev, "<%s> removing <%s>\n",
954157331Sariff				ch->name, (pce->channel != NULL) ?
955157331Sariff					pce->channel->name : "unknown");
956157331Sariff#endif
957157331Sariff			SLIST_REMOVE(&ch->children, pce, pcmchan_children, link);
958157331Sariff			free(pce, M_DEVBUF);
959157331Sariff			pce = SLIST_FIRST(&ch->children);
960157331Sariff		}
961124740Smatk	}
962124740Smatk
96374763Scg#ifdef SND_DYNSYSCTL
96474763Scg	d->sysctl_tree_top = NULL;
96574763Scg	sysctl_ctx_free(&d->sysctl_tree);
96674763Scg#endif
967157331Sariff
968157331Sariff#if 0
969157331Sariff	SLIST_FOREACH(sce, &d->channels, link) {
970157331Sariff		ch = sce->channel;
971157331Sariff		if (ch == NULL)
972157331Sariff			continue;
973157331Sariff		if (!SLIST_EMPTY(&ch->children))
974157331Sariff			device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n",
975157331Sariff				__func__, ch->name);
976157331Sariff	}
977157331Sariff#endif
97878395Scg	while (!SLIST_EMPTY(&d->channels))
97977269Scg		pcm_killchan(dev);
98065207Scg
98174763Scg	chn_kill(d->fakechan);
98274763Scg	fkchan_kill(d->fakechan);
98373127Scg
984157331Sariff#if 0
985157331Sariff	device_printf(d->dev, "%s: devcount=%u, playcount=%u, "
986157331Sariff		"reccount=%u, vchancount=%u\n",
987157331Sariff		__func__, d->devcount, d->playcount, d->reccount,
988157331Sariff		d->vchancount);
989157331Sariff#endif
990124617Sphk	snd_mtxunlock(d->lock);
99177882Scg	snd_mtxfree(d->lock);
992150827Snetchild	sndstat_unregister(dev);
993150827Snetchild	sndstat_release();
99465340Scg	return 0;
99565207Scg}
99665207Scg
99782180Scg/************************************************************************/
99882180Scg
99982180Scgstatic int
100082180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
100182180Scg{
1002157331Sariff	struct snddev_info *d;
1003157331Sariff	struct snddev_channel *sce;
100482180Scg	struct pcm_channel *c;
100582180Scg	struct pcm_feeder *f;
1006157331Sariff	int pc, rc, vc;
100782180Scg
100882180Scg	if (verbose < 1)
100982180Scg		return 0;
101082180Scg
101182180Scg	d = device_get_softc(dev);
101282180Scg	if (!d)
101382180Scg		return ENXIO;
101482180Scg
101582180Scg	snd_mtxlock(d->lock);
101682180Scg	if (!SLIST_EMPTY(&d->channels)) {
101782180Scg		pc = rc = vc = 0;
101882180Scg		SLIST_FOREACH(sce, &d->channels, link) {
101982180Scg			c = sce->channel;
102082180Scg			if (c->direction == PCMDIR_PLAY) {
102182180Scg				if (c->flags & CHN_F_VIRTUAL)
102282180Scg					vc++;
102382180Scg				else
102482180Scg					pc++;
102582180Scg			} else
102682180Scg				rc++;
102782180Scg		}
102883614Scg		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
102982180Scg				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
103082180Scg#ifdef USING_DEVFS
103182180Scg				(device_get_unit(dev) == snd_unit)? " default" : ""
103282180Scg#else
103382180Scg				""
103482180Scg#endif
103582180Scg				);
1036119096Scg
1037119096Scg		if (verbose <= 1) {
1038119096Scg			snd_mtxunlock(d->lock);
1039119096Scg			return 0;
1040119096Scg		}
1041119096Scg
104282180Scg		SLIST_FOREACH(sce, &d->channels, link) {
104382180Scg			c = sce->channel;
1044155342Snetchild
1045155342Snetchild			KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
1046155342Snetchild				("hosed pcm channel setup"));
1047155342Snetchild
104882479Scg			sbuf_printf(s, "\n\t");
104982479Scg
1050124617Sphk			/* it would be better to indent child channels */
105182479Scg			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
105289691Scg			sbuf_printf(s, "spd %d", c->speed);
105389691Scg			if (c->speed != sndbuf_getspd(c->bufhard))
105489691Scg				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
105589691Scg			sbuf_printf(s, ", fmt 0x%08x", c->format);
105689691Scg			if (c->format != sndbuf_getfmt(c->bufhard))
105789691Scg				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1058119096Scg			sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
105982180Scg			if (c->pid != -1)
106082180Scg				sbuf_printf(s, ", pid %d", c->pid);
106182180Scg			sbuf_printf(s, "\n\t");
106289691Scg
1063155342Snetchild			sbuf_printf(s, "interrupts %d, ", c->interrupts);
1064155342Snetchild			if (c->direction == PCMDIR_REC)
1065155342Snetchild				sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1066155342Snetchild					c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
1067155342Snetchild					sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1068155342Snetchild					sndbuf_getblkcnt(c->bufhard),
1069155342Snetchild					sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1070155342Snetchild					sndbuf_getblkcnt(c->bufsoft));
1071155342Snetchild			else
1072155342Snetchild				sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1073155342Snetchild					c->xruns, sndbuf_getready(c->bufsoft),
1074155342Snetchild					sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1075155342Snetchild					sndbuf_getblkcnt(c->bufhard),
1076155342Snetchild					sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1077155342Snetchild					sndbuf_getblkcnt(c->bufsoft));
1078155342Snetchild			sbuf_printf(s, "\n\t");
107989691Scg
108089691Scg			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
108189691Scg			sbuf_printf(s, " -> ");
108282180Scg			f = c->feeder;
108389691Scg			while (f->source != NULL)
108489691Scg				f = f->source;
108589691Scg			while (f != NULL) {
108682180Scg				sbuf_printf(s, "%s", f->class->name);
108782180Scg				if (f->desc->type == FEEDER_FMT)
108889691Scg					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
108982180Scg				if (f->desc->type == FEEDER_RATE)
109089691Scg					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
1091150827Snetchild				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
1092150827Snetchild						f->desc->type == FEEDER_VOLUME)
109389691Scg					sbuf_printf(s, "(0x%08x)", f->desc->out);
109489691Scg				sbuf_printf(s, " -> ");
109589691Scg				f = f->parent;
109682180Scg			}
109789691Scg			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
109882180Scg		}
109982180Scg	} else
110082180Scg		sbuf_printf(s, " (mixer only)");
110182180Scg	snd_mtxunlock(d->lock);
110282180Scg
110382180Scg	return 0;
110482180Scg}
110582180Scg
110682180Scg/************************************************************************/
110782180Scg
110882180Scg#ifdef SND_DYNSYSCTL
110982180Scgint
111082180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
111182180Scg{
111282180Scg	struct snddev_info *d;
1113157331Sariff	int err, newcnt;
111482180Scg
111582180Scg	d = oidp->oid_arg1;
111682180Scg
1117157331Sariff	newcnt = d->vchancount;
111882180Scg	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
1119119096Scg
1120157331Sariff	if (err == 0 && req->newptr != NULL && d->vchancount != newcnt)
1121157331Sariff		err = pcm_setvchans(d, newcnt);
1122119096Scg
112382180Scg	return err;
112482180Scg}
112582180Scg#endif
112682180Scg
112782180Scg/************************************************************************/
112882180Scg
1129132236Stanimurastatic int
1130132236Stanimurasound_modevent(module_t mod, int type, void *data)
1131132236Stanimura{
1132148587Snetchild#if 0
1133132236Stanimura	return (midi_modevent(mod, type, data));
1134148587Snetchild#else
1135148587Snetchild	return 0;
1136148587Snetchild#endif
1137132236Stanimura}
1138132236Stanimura
1139132236StanimuraDEV_MODULE(sound, sound_modevent, NULL);
1140132236StanimuraMODULE_VERSION(sound, SOUND_MODVER);
1141