xref: /freebsd/sys/dev/sound/pcm/sound.c (revision b8a3639565c4a8c5ea3653412ac25f6ab5ae467f)
1987e5972SCameron Grant /*
2987e5972SCameron Grant  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3987e5972SCameron Grant  * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
4987e5972SCameron Grant  * All rights reserved.
5987e5972SCameron Grant  *
6987e5972SCameron Grant  * Redistribution and use in source and binary forms, with or without
7987e5972SCameron Grant  * modification, are permitted provided that the following conditions
8987e5972SCameron Grant  * are met:
9987e5972SCameron Grant  * 1. Redistributions of source code must retain the above copyright
10987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer.
11987e5972SCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
12987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer in the
13987e5972SCameron Grant  *    documentation and/or other materials provided with the distribution.
14987e5972SCameron Grant  *
15987e5972SCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16987e5972SCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17987e5972SCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18987e5972SCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19987e5972SCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20987e5972SCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21987e5972SCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22987e5972SCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23987e5972SCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24987e5972SCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25987e5972SCameron Grant  * SUCH DAMAGE.
26987e5972SCameron Grant  */
27987e5972SCameron Grant 
28ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
29285648f9SCameron Grant #include <dev/sound/pcm/vchan.h>
307c438dbeSCameron Grant #include <sys/sysctl.h>
31285648f9SCameron Grant 
3267b1dce3SCameron Grant #include "feeder_if.h"
3367b1dce3SCameron Grant 
3467b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$");
3567b1dce3SCameron Grant 
3667b1dce3SCameron Grant struct snddev_channel {
3767b1dce3SCameron Grant 	SLIST_ENTRY(snddev_channel) link;
3867b1dce3SCameron Grant 	struct pcm_channel *channel;
3967b1dce3SCameron Grant };
4067b1dce3SCameron Grant 
4167b1dce3SCameron Grant struct snddev_info {
4267b1dce3SCameron Grant 	SLIST_HEAD(, snddev_channel) channels;
4367b1dce3SCameron Grant 	struct pcm_channel *fakechan;
44a67fe5c1SCameron Grant 	unsigned devcount, playcount, reccount, vchancount;
4567b1dce3SCameron Grant 	unsigned flags;
4667b1dce3SCameron Grant 	int inprog;
47a67fe5c1SCameron Grant 	unsigned int bufsz;
4867b1dce3SCameron Grant 	void *devinfo;
4967b1dce3SCameron Grant 	device_t dev;
5067b1dce3SCameron Grant 	char status[SND_STATUSLEN];
5167b1dce3SCameron Grant 	struct sysctl_ctx_list sysctl_tree;
5267b1dce3SCameron Grant 	struct sysctl_oid *sysctl_tree_top;
5367b1dce3SCameron Grant 	void *lock;
5467b1dce3SCameron Grant };
5567b1dce3SCameron Grant 
56d95502a8SCameron Grant devclass_t pcm_devclass;
5782db23e2SCameron Grant 
58b8a36395SCameron Grant int pcm_veto_load = 1;
59b8a36395SCameron Grant 
6082db23e2SCameron Grant #ifdef USING_DEVFS
61d95502a8SCameron Grant int snd_unit = 0;
6209786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit);
6382db23e2SCameron Grant #endif
64cbe7d6a3SCameron Grant 
6567b1dce3SCameron Grant int snd_maxautovchans = 0;
6667b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
67987e5972SCameron Grant 
6882db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
6982db23e2SCameron Grant 
7067b1dce3SCameron Grant static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
7167b1dce3SCameron Grant 
7267b1dce3SCameron Grant struct sysctl_ctx_list *
7367b1dce3SCameron Grant snd_sysctl_tree(device_t dev)
7467b1dce3SCameron Grant {
7567b1dce3SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
7667b1dce3SCameron Grant 
7767b1dce3SCameron Grant 	return &d->sysctl_tree;
7867b1dce3SCameron Grant }
7967b1dce3SCameron Grant 
8067b1dce3SCameron Grant struct sysctl_oid *
8167b1dce3SCameron Grant snd_sysctl_tree_top(device_t dev)
8267b1dce3SCameron Grant {
8367b1dce3SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
8467b1dce3SCameron Grant 
8567b1dce3SCameron Grant 	return d->sysctl_tree_top;
8667b1dce3SCameron Grant }
8767b1dce3SCameron Grant 
8837209180SCameron Grant void *
8937209180SCameron Grant snd_mtxcreate(const char *desc)
9037209180SCameron Grant {
9137209180SCameron Grant #ifdef USING_MUTEX
9237209180SCameron Grant 	struct mtx *m;
9337209180SCameron Grant 
9437209180SCameron Grant 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
9537209180SCameron Grant 	if (m == NULL)
9637209180SCameron Grant 		return NULL;
9737209180SCameron Grant 	mtx_init(m, desc, MTX_RECURSE);
9837209180SCameron Grant 	return m;
9937209180SCameron Grant #else
100a983d575SCameron Grant 	return (void *)0xcafebabe;
10137209180SCameron Grant #endif
10237209180SCameron Grant }
10337209180SCameron Grant 
10437209180SCameron Grant void
10537209180SCameron Grant snd_mtxfree(void *m)
10637209180SCameron Grant {
10737209180SCameron Grant #ifdef USING_MUTEX
10837209180SCameron Grant 	struct mtx *mtx = m;
10937209180SCameron Grant 
11037209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
11137209180SCameron Grant 	mtx_destroy(mtx);
11237209180SCameron Grant 	free(mtx, M_DEVBUF);
11337209180SCameron Grant #endif
11437209180SCameron Grant }
11537209180SCameron Grant 
11637209180SCameron Grant void
11737209180SCameron Grant snd_mtxassert(void *m)
11837209180SCameron Grant {
11937209180SCameron Grant #ifdef USING_MUTEX
120f00f162aSCameron Grant #ifdef INVARIANTS
12137209180SCameron Grant 	struct mtx *mtx = m;
12237209180SCameron Grant 
12337209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
12437209180SCameron Grant #endif
125f00f162aSCameron Grant #endif
12637209180SCameron Grant }
12737209180SCameron Grant 
12837209180SCameron Grant void
12937209180SCameron Grant snd_mtxlock(void *m)
13037209180SCameron Grant {
13137209180SCameron Grant #ifdef USING_MUTEX
13237209180SCameron Grant 	struct mtx *mtx = m;
13337209180SCameron Grant 
13437209180SCameron Grant 	mtx_lock(mtx);
13537209180SCameron Grant #endif
13637209180SCameron Grant }
13737209180SCameron Grant 
13837209180SCameron Grant void
13937209180SCameron Grant snd_mtxunlock(void *m)
14037209180SCameron Grant {
14137209180SCameron Grant #ifdef USING_MUTEX
14237209180SCameron Grant 	struct mtx *mtx = m;
14337209180SCameron Grant 
14437209180SCameron Grant 	mtx_unlock(mtx);
14537209180SCameron Grant #endif
14637209180SCameron Grant }
14737209180SCameron Grant 
14837209180SCameron Grant int
14937209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
15037209180SCameron Grant {
15137209180SCameron Grant #ifdef USING_MUTEX
15237209180SCameron Grant 	flags &= INTR_MPSAFE;
15346700f12SPeter Wemm 	flags |= INTR_TYPE_AV;
15437209180SCameron Grant #else
15546700f12SPeter Wemm 	flags = INTR_TYPE_AV;
15637209180SCameron Grant #endif
15737209180SCameron Grant 	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
15837209180SCameron Grant }
15937209180SCameron Grant 
16067b1dce3SCameron Grant void
16167b1dce3SCameron Grant pcm_lock(struct snddev_info *d)
16267b1dce3SCameron Grant {
16367b1dce3SCameron Grant 	snd_mtxlock(d->lock);
16467b1dce3SCameron Grant }
16567b1dce3SCameron Grant 
16667b1dce3SCameron Grant void
16767b1dce3SCameron Grant pcm_unlock(struct snddev_info *d)
16867b1dce3SCameron Grant {
16967b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
17067b1dce3SCameron Grant }
17167b1dce3SCameron Grant 
17267b1dce3SCameron Grant struct pcm_channel *
17367b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d)
17467b1dce3SCameron Grant {
17567b1dce3SCameron Grant 	return d->fakechan;
17667b1dce3SCameron Grant }
17767b1dce3SCameron Grant 
178b8f0d9e0SCameron Grant /* return a locked channel */
179285648f9SCameron Grant struct pcm_channel *
180506a5308SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
181285648f9SCameron Grant {
182285648f9SCameron Grant 	struct pcm_channel *c;
183285648f9SCameron Grant     	struct snddev_channel *sce;
184f637a36cSCameron Grant 	int err;
185285648f9SCameron Grant 
186b8f0d9e0SCameron Grant 	snd_mtxassert(d->lock);
187f637a36cSCameron Grant 
188f637a36cSCameron Grant 	/* scan for a free channel */
189285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
190285648f9SCameron Grant 		c = sce->channel;
19149c5e6e2SCameron Grant 		CHN_LOCK(c);
192285648f9SCameron Grant 		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
193506a5308SCameron Grant 			if (chnum == -1 || c->num == chnum) {
194285648f9SCameron Grant 				c->flags |= CHN_F_BUSY;
195b8f0d9e0SCameron Grant 				c->pid = pid;
196285648f9SCameron Grant 				return c;
197285648f9SCameron Grant 			}
198506a5308SCameron Grant 		}
19949c5e6e2SCameron Grant 		CHN_UNLOCK(c);
200285648f9SCameron Grant 	}
201f637a36cSCameron Grant 
202f637a36cSCameron Grant 	/* no channel available */
203f637a36cSCameron Grant 	if (direction == PCMDIR_PLAY) {
20467b1dce3SCameron Grant 		if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
205f637a36cSCameron Grant 			/* try to create a vchan */
206f637a36cSCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
207f637a36cSCameron Grant 				c = sce->channel;
208f637a36cSCameron Grant 				if (!SLIST_EMPTY(&c->children)) {
209f637a36cSCameron Grant 					err = vchan_create(c);
210f637a36cSCameron Grant 					if (!err)
211506a5308SCameron Grant 						return pcm_chnalloc(d, direction, pid, -1);
212f637a36cSCameron Grant 					else
213f637a36cSCameron Grant 						device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
214f637a36cSCameron Grant 				}
215f637a36cSCameron Grant 			}
216f637a36cSCameron Grant 		}
217f637a36cSCameron Grant 	}
218f637a36cSCameron Grant 
219285648f9SCameron Grant 	return NULL;
220285648f9SCameron Grant }
221285648f9SCameron Grant 
222b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
223285648f9SCameron Grant int
224b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
225285648f9SCameron Grant {
226b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
227285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
228b8f0d9e0SCameron Grant 	c->pid = -1;
22949c5e6e2SCameron Grant 	CHN_UNLOCK(c);
230285648f9SCameron Grant 	return 0;
231285648f9SCameron Grant }
232285648f9SCameron Grant 
233285648f9SCameron Grant int
234285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
235285648f9SCameron Grant {
23649c5e6e2SCameron Grant 	int r;
23749c5e6e2SCameron Grant 
238b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
239285648f9SCameron Grant 	c->refcount += ref;
24049c5e6e2SCameron Grant 	r = c->refcount;
24149c5e6e2SCameron Grant 	return r;
242285648f9SCameron Grant }
243285648f9SCameron Grant 
24467b1dce3SCameron Grant int
24567b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta)
24667b1dce3SCameron Grant {
24767b1dce3SCameron Grant 	d->inprog += delta;
24867b1dce3SCameron Grant 	return d->inprog;
24967b1dce3SCameron Grant }
25067b1dce3SCameron Grant 
25167b1dce3SCameron Grant static void
25267b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num)
25367b1dce3SCameron Grant {
25467b1dce3SCameron Grant 	struct pcm_channel *c;
25567b1dce3SCameron Grant     	struct snddev_channel *sce;
25667b1dce3SCameron Grant 	int err, done;
25767b1dce3SCameron Grant 
25867b1dce3SCameron Grant 	if (num > 0 && d->vchancount == 0) {
25967b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
26067b1dce3SCameron Grant 			c = sce->channel;
26167b1dce3SCameron Grant 			if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
26267b1dce3SCameron Grant 				c->flags |= CHN_F_BUSY;
26367b1dce3SCameron Grant 				err = vchan_create(c);
26467b1dce3SCameron Grant 				if (err) {
26567b1dce3SCameron Grant 					device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
26667b1dce3SCameron Grant 					c->flags &= ~CHN_F_BUSY;
26767b1dce3SCameron Grant 				}
26867b1dce3SCameron Grant 				return;
26967b1dce3SCameron Grant 			}
27067b1dce3SCameron Grant 		}
27167b1dce3SCameron Grant 	}
27267b1dce3SCameron Grant 	if (num == 0 && d->vchancount > 0) {
27367b1dce3SCameron Grant 		done = 0;
27467b1dce3SCameron Grant 		while (!done) {
27567b1dce3SCameron Grant 			done = 1;
27667b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
27767b1dce3SCameron Grant 				c = sce->channel;
27867b1dce3SCameron Grant 				if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
27967b1dce3SCameron Grant 					done = 0;
28067b1dce3SCameron Grant 					err = vchan_destroy(c);
28167b1dce3SCameron Grant 					if (err)
28267b1dce3SCameron Grant 						device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
28367b1dce3SCameron Grant 					goto restart;
28467b1dce3SCameron Grant 				}
28567b1dce3SCameron Grant 			}
28667b1dce3SCameron Grant restart:
28767b1dce3SCameron Grant 		}
28867b1dce3SCameron Grant 	}
28967b1dce3SCameron Grant }
29067b1dce3SCameron Grant 
29182db23e2SCameron Grant #ifdef USING_DEVFS
29233dbf14aSCameron Grant static int
293cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
29433dbf14aSCameron Grant {
295b8f0d9e0SCameron Grant 	struct snddev_info *d;
29633dbf14aSCameron Grant 	int error, unit;
29733dbf14aSCameron Grant 
29833dbf14aSCameron Grant 	unit = snd_unit;
29933dbf14aSCameron Grant 	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
30033dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
30174ffd138SCameron Grant 		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
302b8f0d9e0SCameron Grant 			return EINVAL;
303b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
304faeebea2SCameron Grant 		if (d == NULL || SLIST_EMPTY(&d->channels))
305b8f0d9e0SCameron Grant 			return EINVAL;
30633dbf14aSCameron Grant 		snd_unit = unit;
30733dbf14aSCameron Grant 	}
30833dbf14aSCameron Grant 	return (error);
30933dbf14aSCameron Grant }
310b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
311cd9766c5SCameron Grant             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
31282db23e2SCameron Grant #endif
313987e5972SCameron Grant 
314cd9766c5SCameron Grant static int
31567b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
316cd9766c5SCameron Grant {
31767b1dce3SCameron Grant 	struct snddev_info *d;
31867b1dce3SCameron Grant 	int i, v, error;
319cd9766c5SCameron Grant 
32067b1dce3SCameron Grant 	v = snd_maxautovchans;
321cd9766c5SCameron Grant 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
322cd9766c5SCameron Grant 	if (error == 0 && req->newptr != NULL) {
323cd9766c5SCameron Grant 		if (v < 0 || v >= SND_MAXVCHANS)
324cd9766c5SCameron Grant 			return EINVAL;
32567b1dce3SCameron Grant 		if (v != snd_maxautovchans) {
32667b1dce3SCameron Grant 			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
32767b1dce3SCameron Grant 				d = devclass_get_softc(pcm_devclass, i);
32867b1dce3SCameron Grant 				if (!d)
32967b1dce3SCameron Grant 					continue;
33067b1dce3SCameron Grant 				pcm_setmaxautovchans(d, v);
33167b1dce3SCameron Grant 			}
33267b1dce3SCameron Grant 		}
33367b1dce3SCameron Grant 		snd_maxautovchans = v;
334cd9766c5SCameron Grant 	}
335cd9766c5SCameron Grant 	return (error);
336cd9766c5SCameron Grant }
33767b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
33867b1dce3SCameron Grant             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
339f637a36cSCameron Grant 
340285648f9SCameron Grant struct pcm_channel *
341285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
342987e5972SCameron Grant {
343285648f9SCameron Grant 	struct pcm_channel *ch;
34433dbf14aSCameron Grant 	char *dirs;
345a67fe5c1SCameron Grant     	int err, *pnum;
346987e5972SCameron Grant 
347285648f9SCameron Grant 	switch(dir) {
348285648f9SCameron Grant 	case PCMDIR_PLAY:
349285648f9SCameron Grant 		dirs = "play";
350a67fe5c1SCameron Grant 		pnum = &d->playcount;
351285648f9SCameron Grant 		break;
352a67fe5c1SCameron Grant 
353285648f9SCameron Grant 	case PCMDIR_REC:
354285648f9SCameron Grant 		dirs = "record";
355a67fe5c1SCameron Grant 		pnum = &d->reccount;
356285648f9SCameron Grant 		break;
357a67fe5c1SCameron Grant 
358285648f9SCameron Grant 	case PCMDIR_VIRTUAL:
359285648f9SCameron Grant 		dirs = "virtual";
360285648f9SCameron Grant 		dir = PCMDIR_PLAY;
361a67fe5c1SCameron Grant 		pnum = &d->vchancount;
362285648f9SCameron Grant 		break;
363a67fe5c1SCameron Grant 
364285648f9SCameron Grant 	default:
365285648f9SCameron Grant 		return NULL;
3669c326820SCameron Grant 	}
367285648f9SCameron Grant 
368285648f9SCameron Grant 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
369285648f9SCameron Grant 	if (!ch)
370285648f9SCameron Grant 		return NULL;
371285648f9SCameron Grant 
3720f55ac6cSCameron Grant 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
373285648f9SCameron Grant 	if (!ch->methods) {
374285648f9SCameron Grant 		free(ch, M_DEVBUF);
375a67fe5c1SCameron Grant 
376285648f9SCameron Grant 		return NULL;
377285648f9SCameron Grant 	}
378285648f9SCameron Grant 
379a67fe5c1SCameron Grant 	ch->num = (*pnum)++;
380a67fe5c1SCameron Grant 
381285648f9SCameron Grant 	ch->pid = -1;
382285648f9SCameron Grant 	ch->parentsnddev = d;
383285648f9SCameron Grant 	ch->parentchannel = parent;
384436c9b65SScott Long 	ch->dev = d->dev;
385a67fe5c1SCameron Grant 	snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num);
386285648f9SCameron Grant 
3870f55ac6cSCameron Grant 	err = chn_init(ch, devinfo, dir);
3880f55ac6cSCameron Grant 	if (err) {
389a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
390285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
391285648f9SCameron Grant 		free(ch, M_DEVBUF);
392a67fe5c1SCameron Grant 		(*pnum)--;
393a67fe5c1SCameron Grant 
394285648f9SCameron Grant 		return NULL;
395bbb5bf3dSCameron Grant 	}
396285648f9SCameron Grant 
397285648f9SCameron Grant 	return ch;
398285648f9SCameron Grant }
399285648f9SCameron Grant 
400285648f9SCameron Grant int
401285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
402285648f9SCameron Grant {
403a67fe5c1SCameron Grant 	struct snddev_info *d;
404285648f9SCameron Grant 	int err;
405285648f9SCameron Grant 
406a67fe5c1SCameron Grant 	d = ch->parentsnddev;
407285648f9SCameron Grant 	err = chn_kill(ch);
408285648f9SCameron Grant 	if (err) {
409a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
410285648f9SCameron Grant 		return err;
411285648f9SCameron Grant 	}
412285648f9SCameron Grant 
413a67fe5c1SCameron Grant 	if (ch->direction == PCMDIR_REC)
414a67fe5c1SCameron Grant 		d->reccount--;
415a67fe5c1SCameron Grant 	else if (ch->flags & CHN_F_VIRTUAL)
416a67fe5c1SCameron Grant 		d->vchancount--;
417a67fe5c1SCameron Grant 	else
418a67fe5c1SCameron Grant 		d->playcount--;
419a67fe5c1SCameron Grant 
420285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
421285648f9SCameron Grant 	free(ch, M_DEVBUF);
422285648f9SCameron Grant 
423285648f9SCameron Grant 	return 0;
424285648f9SCameron Grant }
425285648f9SCameron Grant 
426285648f9SCameron Grant int
427f637a36cSCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
428285648f9SCameron Grant {
42967b1dce3SCameron Grant     	struct snddev_channel *sce, *tmp, *after;
430285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
431b8f0d9e0SCameron Grant 
432b8f0d9e0SCameron Grant 	snd_mtxlock(d->lock);
433285648f9SCameron Grant 
434285648f9SCameron Grant 	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
435285648f9SCameron Grant 	if (!sce) {
436b8f0d9e0SCameron Grant 		snd_mtxunlock(d->lock);
437285648f9SCameron Grant 		return ENOMEM;
438285648f9SCameron Grant 	}
439285648f9SCameron Grant 
440285648f9SCameron Grant 	sce->channel = ch;
44167b1dce3SCameron Grant 	if (SLIST_EMPTY(&d->channels)) {
442285648f9SCameron Grant 		SLIST_INSERT_HEAD(&d->channels, sce, link);
44367b1dce3SCameron Grant 	} else {
44467b1dce3SCameron Grant 		after = NULL;
44567b1dce3SCameron Grant 		SLIST_FOREACH(tmp, &d->channels, link) {
44667b1dce3SCameron Grant 			after = tmp;
44767b1dce3SCameron Grant 		}
44867b1dce3SCameron Grant 		SLIST_INSERT_AFTER(after, sce, link);
44967b1dce3SCameron Grant 	}
450285648f9SCameron Grant 
451506a5308SCameron Grant 	if (mkdev) {
452f637a36cSCameron Grant 		dsp_register(unit, d->devcount++);
453506a5308SCameron Grant 		if (ch->direction == PCMDIR_REC)
454506a5308SCameron Grant 			dsp_registerrec(unit, ch->num);
455506a5308SCameron Grant 	}
456b8f0d9e0SCameron Grant 
45749c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
458285648f9SCameron Grant 
45933dbf14aSCameron Grant 	return 0;
46033dbf14aSCameron Grant }
46133dbf14aSCameron Grant 
462285648f9SCameron Grant int
463f637a36cSCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
46433dbf14aSCameron Grant {
465285648f9SCameron Grant     	struct snddev_channel *sce;
466285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
46733dbf14aSCameron Grant 
46849c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
469285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
470285648f9SCameron Grant 		if (sce->channel == ch)
471285648f9SCameron Grant 			goto gotit;
47233dbf14aSCameron Grant 	}
47349c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
474285648f9SCameron Grant 	return EINVAL;
475285648f9SCameron Grant gotit:
476285648f9SCameron Grant 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
477285648f9SCameron Grant 	free(sce, M_DEVBUF);
478285648f9SCameron Grant 
479506a5308SCameron Grant 	if (rmdev) {
480f637a36cSCameron Grant 		dsp_unregister(unit, --d->devcount);
481506a5308SCameron Grant 		if (ch->direction == PCMDIR_REC)
482a67fe5c1SCameron Grant 			dsp_unregisterrec(unit, ch->num);
483506a5308SCameron Grant 	}
48449c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
485285648f9SCameron Grant 
486987e5972SCameron Grant 	return 0;
487987e5972SCameron Grant }
488987e5972SCameron Grant 
489987e5972SCameron Grant int
490285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
491285648f9SCameron Grant {
492285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
49367b1dce3SCameron Grant 	struct pcm_channel *ch;
49467b1dce3SCameron Grant     	int err;
495285648f9SCameron Grant 
496285648f9SCameron Grant 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
497285648f9SCameron Grant 	if (!ch) {
498285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
499285648f9SCameron Grant 		return ENODEV;
500285648f9SCameron Grant 	}
501cd9766c5SCameron Grant 
502f637a36cSCameron Grant 	err = pcm_chn_add(d, ch, 1);
503285648f9SCameron Grant 	if (err) {
504285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
505285648f9SCameron Grant 		pcm_chn_destroy(ch);
506cd9766c5SCameron Grant 		return err;
507cd9766c5SCameron Grant 	}
508cd9766c5SCameron Grant 
50967b1dce3SCameron Grant 	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) {
510cd9766c5SCameron Grant 		ch->flags |= CHN_F_BUSY;
511cd9766c5SCameron Grant 		err = vchan_create(ch);
512cd9766c5SCameron Grant 		if (err) {
51367b1dce3SCameron Grant 			device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
51467b1dce3SCameron Grant 			ch->flags &= ~CHN_F_BUSY;
515cd9766c5SCameron Grant 		}
516285648f9SCameron Grant 	}
517285648f9SCameron Grant 
518285648f9SCameron Grant 	return err;
519285648f9SCameron Grant }
520285648f9SCameron Grant 
521285648f9SCameron Grant static int
522285648f9SCameron Grant pcm_killchan(device_t dev)
523285648f9SCameron Grant {
524285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
525285648f9SCameron Grant     	struct snddev_channel *sce;
526285648f9SCameron Grant 
52749c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
528285648f9SCameron Grant 	sce = SLIST_FIRST(&d->channels);
52949c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
530285648f9SCameron Grant 
531f637a36cSCameron Grant 	return pcm_chn_remove(d, sce->channel, 1);
532285648f9SCameron Grant }
533285648f9SCameron Grant 
534285648f9SCameron Grant int
535987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
536987e5972SCameron Grant {
53766ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
53849c5e6e2SCameron Grant 
53949c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
540987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
54149c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
542987e5972SCameron Grant 	return 0;
543987e5972SCameron Grant }
544987e5972SCameron Grant 
545987e5972SCameron Grant u_int32_t
546987e5972SCameron Grant pcm_getflags(device_t dev)
547987e5972SCameron Grant {
54866ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
54949c5e6e2SCameron Grant 
550987e5972SCameron Grant 	return d->flags;
551987e5972SCameron Grant }
552987e5972SCameron Grant 
553987e5972SCameron Grant void
554987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
555987e5972SCameron Grant {
55666ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
557d95502a8SCameron Grant 
558987e5972SCameron Grant 	d->flags = val;
559987e5972SCameron Grant }
560987e5972SCameron Grant 
56139004e69SCameron Grant void *
56239004e69SCameron Grant pcm_getdevinfo(device_t dev)
56339004e69SCameron Grant {
56466ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
56549c5e6e2SCameron Grant 
56639004e69SCameron Grant 	return d->devinfo;
56739004e69SCameron Grant }
56839004e69SCameron Grant 
569a67fe5c1SCameron Grant unsigned int
570a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
571a67fe5c1SCameron Grant {
572a67fe5c1SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
5734e60be34SCameron Grant 	int sz, x;
574a67fe5c1SCameron Grant 
575a67fe5c1SCameron Grant 	sz = 0;
5764e60be34SCameron Grant 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
5774e60be34SCameron Grant 		x = sz;
578a67fe5c1SCameron Grant 		RANGE(sz, min, max);
5794e60be34SCameron Grant 		if (x != sz)
5804e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
5814e60be34SCameron Grant 		x = min;
5824e60be34SCameron Grant 		while (x < sz)
5834e60be34SCameron Grant 			x <<= 1;
5844e60be34SCameron Grant 		if (x > sz)
5854e60be34SCameron Grant 			x >>= 1;
5864e60be34SCameron Grant 		if (x != sz) {
5874e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
5884e60be34SCameron Grant 			sz = x;
5894e60be34SCameron Grant 		}
5904e60be34SCameron Grant 	} else {
591a67fe5c1SCameron Grant 		sz = deflt;
5924e60be34SCameron Grant 	}
5934e60be34SCameron Grant 
594a67fe5c1SCameron Grant 	d->bufsz = sz;
595a67fe5c1SCameron Grant 
596a67fe5c1SCameron Grant 	return sz;
597a67fe5c1SCameron Grant }
598a67fe5c1SCameron Grant 
599987e5972SCameron Grant int
600987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
601987e5972SCameron Grant {
60266ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
603987e5972SCameron Grant 
604b8a36395SCameron Grant 	if (pcm_veto_load) {
605b8a36395SCameron Grant 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
606b8a36395SCameron Grant 
607b8a36395SCameron Grant 		return EINVAL;
608b8a36395SCameron Grant 	}
609b8a36395SCameron Grant 
61049c5e6e2SCameron Grant 	d->lock = snd_mtxcreate(device_get_nameunit(dev));
61149c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
612285648f9SCameron Grant 
613cd9766c5SCameron Grant 	d->flags = 0;
614e4d5b250SCameron Grant 	d->dev = dev;
615987e5972SCameron Grant 	d->devinfo = devinfo;
616f637a36cSCameron Grant 	d->devcount = 0;
617506a5308SCameron Grant 	d->reccount = 0;
618a67fe5c1SCameron Grant 	d->playcount = 0;
619f637a36cSCameron Grant 	d->vchancount = 0;
620d95502a8SCameron Grant 	d->inprog = 0;
621833f7023SCameron Grant 
622d95502a8SCameron Grant 	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
623285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
624d95502a8SCameron Grant 
625285648f9SCameron Grant 	d->fakechan = fkchan_setup(dev);
626285648f9SCameron Grant 	chn_init(d->fakechan, NULL, 0);
627987e5972SCameron Grant 
62882db23e2SCameron Grant #ifdef SND_DYNSYSCTL
629cc486d80SJohn Baldwin 	sysctl_ctx_init(&d->sysctl_tree);
630cc486d80SJohn Baldwin 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
631a3e893e0SJohn Baldwin 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
632cc486d80SJohn Baldwin 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
633a3e893e0SJohn Baldwin 	if (d->sysctl_tree_top == NULL) {
634cc486d80SJohn Baldwin 		sysctl_ctx_free(&d->sysctl_tree);
635cc486d80SJohn Baldwin 		goto no;
636cc486d80SJohn Baldwin 	}
637a67fe5c1SCameron Grant 	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
638a67fe5c1SCameron Grant             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
63982db23e2SCameron Grant #endif
640b8f0d9e0SCameron Grant 	if (numplay > 0)
64167b1dce3SCameron Grant 		vchan_initsys(dev);
642cd9766c5SCameron Grant 	if (numplay == 1)
643cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
644cd9766c5SCameron Grant 
64549c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
64667b1dce3SCameron Grant 	sndstat_register(dev, d->status, sndstat_prepare_pcm);
647987e5972SCameron Grant     	return 0;
648987e5972SCameron Grant no:
64949c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
650987e5972SCameron Grant 	return ENXIO;
651987e5972SCameron Grant }
652987e5972SCameron Grant 
65333dbf14aSCameron Grant int
65433dbf14aSCameron Grant pcm_unregister(device_t dev)
6557c438dbeSCameron Grant {
65666ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
657285648f9SCameron Grant     	struct snddev_channel *sce;
658a67fe5c1SCameron Grant 	struct pcm_channel *ch;
6597c438dbeSCameron Grant 
66049c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
661d95502a8SCameron Grant 	if (d->inprog) {
6625c25132aSGeorge C A Reid 		device_printf(dev, "unregister: operation in progress\n");
6635c25132aSGeorge C A Reid 		snd_mtxunlock(d->lock);
6645c25132aSGeorge C A Reid 		return EBUSY;
6655c25132aSGeorge C A Reid 	}
6665c25132aSGeorge C A Reid 	if (sndstat_busy() != 0) {
6675c25132aSGeorge C A Reid 		device_printf(dev, "unregister: sndstat busy\n");
668d95502a8SCameron Grant 		snd_mtxunlock(d->lock);
669d95502a8SCameron Grant 		return EBUSY;
670d95502a8SCameron Grant 	}
671285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
672a67fe5c1SCameron Grant 		ch = sce->channel;
673a67fe5c1SCameron Grant 		if (ch->refcount > 0) {
674a67fe5c1SCameron Grant 			device_printf(dev, "unregister: channel %s busy (pid %d)", ch->name, ch->pid);
67549c5e6e2SCameron Grant 			snd_mtxunlock(d->lock);
676285648f9SCameron Grant 			return EBUSY;
677285648f9SCameron Grant 		}
678c9b53085SCameron Grant 	}
679d95502a8SCameron Grant 	if (mixer_uninit(dev)) {
6805c25132aSGeorge C A Reid 		device_printf(dev, "unregister: mixer busy\n");
68149c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
682c9b53085SCameron Grant 		return EBUSY;
683c9b53085SCameron Grant 	}
6847c438dbeSCameron Grant 
68566ef8af5SCameron Grant #ifdef SND_DYNSYSCTL
68666ef8af5SCameron Grant 	d->sysctl_tree_top = NULL;
68766ef8af5SCameron Grant 	sysctl_ctx_free(&d->sysctl_tree);
68866ef8af5SCameron Grant #endif
689faeebea2SCameron Grant 	while (!SLIST_EMPTY(&d->channels))
690285648f9SCameron Grant 		pcm_killchan(dev);
6917c438dbeSCameron Grant 
69266ef8af5SCameron Grant 	chn_kill(d->fakechan);
69366ef8af5SCameron Grant 	fkchan_kill(d->fakechan);
69482db23e2SCameron Grant 
69549c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
69633dbf14aSCameron Grant 	return 0;
69733dbf14aSCameron Grant }
6987c438dbeSCameron Grant 
69967b1dce3SCameron Grant /************************************************************************/
70067b1dce3SCameron Grant 
70167b1dce3SCameron Grant static int
70267b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
70367b1dce3SCameron Grant {
70467b1dce3SCameron Grant     	struct snddev_info *d;
70567b1dce3SCameron Grant     	struct snddev_channel *sce;
70667b1dce3SCameron Grant 	struct pcm_channel *c;
70767b1dce3SCameron Grant 	struct pcm_feeder *f;
70867b1dce3SCameron Grant     	int pc, rc, vc;
70967b1dce3SCameron Grant 
71067b1dce3SCameron Grant 	if (verbose < 1)
71167b1dce3SCameron Grant 		return 0;
71267b1dce3SCameron Grant 
71367b1dce3SCameron Grant 	d = device_get_softc(dev);
71467b1dce3SCameron Grant 	if (!d)
71567b1dce3SCameron Grant 		return ENXIO;
71667b1dce3SCameron Grant 
71767b1dce3SCameron Grant 	snd_mtxlock(d->lock);
71867b1dce3SCameron Grant 	if (!SLIST_EMPTY(&d->channels)) {
71967b1dce3SCameron Grant 		pc = rc = vc = 0;
72067b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
72167b1dce3SCameron Grant 			c = sce->channel;
72267b1dce3SCameron Grant 			if (c->direction == PCMDIR_PLAY) {
72367b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
72467b1dce3SCameron Grant 					vc++;
72567b1dce3SCameron Grant 				else
72667b1dce3SCameron Grant 					pc++;
72767b1dce3SCameron Grant 			} else
72867b1dce3SCameron Grant 				rc++;
72967b1dce3SCameron Grant 		}
730a67fe5c1SCameron Grant 		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
73167b1dce3SCameron Grant 				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
73267b1dce3SCameron Grant #ifdef USING_DEVFS
73367b1dce3SCameron Grant 				(device_get_unit(dev) == snd_unit)? " default" : ""
73467b1dce3SCameron Grant #else
73567b1dce3SCameron Grant 				""
73667b1dce3SCameron Grant #endif
73767b1dce3SCameron Grant 				);
73867b1dce3SCameron Grant 		if (verbose <= 1)
73967b1dce3SCameron Grant 			goto skipverbose;
74067b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
74167b1dce3SCameron Grant 			c = sce->channel;
742a3285889SCameron Grant 			sbuf_printf(s, "\n\t");
743a3285889SCameron Grant 
744a3285889SCameron Grant 			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
7454c68642aSCameron Grant 			sbuf_printf(s, "spd %d", c->speed);
7464c68642aSCameron Grant 			if (c->speed != sndbuf_getspd(c->bufhard))
7474c68642aSCameron Grant 				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
7484c68642aSCameron Grant 			sbuf_printf(s, ", fmt 0x%08x", c->format);
7494c68642aSCameron Grant 			if (c->format != sndbuf_getfmt(c->bufhard))
7504c68642aSCameron Grant 				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
7514c68642aSCameron Grant 			sbuf_printf(s, ", flags %08x", c->flags);
75267b1dce3SCameron Grant 			if (c->pid != -1)
75367b1dce3SCameron Grant 				sbuf_printf(s, ", pid %d", c->pid);
75467b1dce3SCameron Grant 			sbuf_printf(s, "\n\t");
7554c68642aSCameron Grant 
756edecdda7SCameron Grant 			if (c->bufhard != NULL && c->bufsoft != NULL) {
757a3285889SCameron Grant 				sbuf_printf(s, "interrupts %d, ", c->interrupts);
758a3285889SCameron Grant 				if (c->direction == PCMDIR_REC)
759a3285889SCameron Grant 					sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
760a3285889SCameron Grant 						c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
761a3285889SCameron Grant 				else
762edecdda7SCameron Grant 					sbuf_printf(s, "underruns %d, ready %d",
763edecdda7SCameron Grant 						c->xruns, sndbuf_getready(c->bufsoft));
764a3285889SCameron Grant 				sbuf_printf(s, "\n\t");
765a3285889SCameron Grant 			}
7664c68642aSCameron Grant 
7674c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
7684c68642aSCameron Grant 			sbuf_printf(s, " -> ");
76967b1dce3SCameron Grant 			f = c->feeder;
7704c68642aSCameron Grant 			while (f->source != NULL)
7714c68642aSCameron Grant 				f = f->source;
7724c68642aSCameron Grant 			while (f != NULL) {
77367b1dce3SCameron Grant 				sbuf_printf(s, "%s", f->class->name);
77467b1dce3SCameron Grant 				if (f->desc->type == FEEDER_FMT)
7754c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
77667b1dce3SCameron Grant 				if (f->desc->type == FEEDER_RATE)
7774c68642aSCameron Grant 					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
77867b1dce3SCameron Grant 				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
7794c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x)", f->desc->out);
7804c68642aSCameron Grant 				sbuf_printf(s, " -> ");
7814c68642aSCameron Grant 				f = f->parent;
78267b1dce3SCameron Grant 			}
7834c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
78467b1dce3SCameron Grant 		}
78567b1dce3SCameron Grant skipverbose:
78667b1dce3SCameron Grant 	} else
78767b1dce3SCameron Grant 		sbuf_printf(s, " (mixer only)");
78867b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
78967b1dce3SCameron Grant 
79067b1dce3SCameron Grant 	return 0;
79167b1dce3SCameron Grant }
79267b1dce3SCameron Grant 
79367b1dce3SCameron Grant /************************************************************************/
79467b1dce3SCameron Grant 
79567b1dce3SCameron Grant #ifdef SND_DYNSYSCTL
79667b1dce3SCameron Grant int
79767b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
79867b1dce3SCameron Grant {
79967b1dce3SCameron Grant 	struct snddev_info *d;
80067b1dce3SCameron Grant     	struct snddev_channel *sce;
80167b1dce3SCameron Grant 	struct pcm_channel *c;
80267b1dce3SCameron Grant 	int err, oldcnt, newcnt, cnt;
80367b1dce3SCameron Grant 
80467b1dce3SCameron Grant 	d = oidp->oid_arg1;
80567b1dce3SCameron Grant 
80667b1dce3SCameron Grant 	pcm_lock(d);
80767b1dce3SCameron Grant 	cnt = 0;
80867b1dce3SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
80967b1dce3SCameron Grant 		c = sce->channel;
81067b1dce3SCameron Grant 		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
81167b1dce3SCameron Grant 			cnt++;
81267b1dce3SCameron Grant 	}
81367b1dce3SCameron Grant 	oldcnt = cnt;
81467b1dce3SCameron Grant 	newcnt = cnt;
81567b1dce3SCameron Grant 
81667b1dce3SCameron Grant 	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
81767b1dce3SCameron Grant 	if (err == 0 && req->newptr != NULL) {
81867b1dce3SCameron Grant 		if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
81967b1dce3SCameron Grant 			pcm_unlock(d);
82067b1dce3SCameron Grant 			return EINVAL;
82167b1dce3SCameron Grant 		}
82267b1dce3SCameron Grant 
82367b1dce3SCameron Grant 		if (newcnt > cnt) {
82467b1dce3SCameron Grant 			/* add new vchans - find a parent channel first */
82567b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
82667b1dce3SCameron Grant 				c = sce->channel;
82767b1dce3SCameron Grant 				/* not a candidate if not a play channel */
82867b1dce3SCameron Grant 				if (c->direction != PCMDIR_PLAY)
82967b1dce3SCameron Grant 					goto addskip;
83067b1dce3SCameron Grant 				/* not a candidate if a virtual channel */
83167b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
83267b1dce3SCameron Grant 					goto addskip;
83367b1dce3SCameron Grant 				/* not a candidate if it's in use */
83467b1dce3SCameron Grant 				if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
83567b1dce3SCameron Grant 					goto addskip;
83667b1dce3SCameron Grant 				/*
83767b1dce3SCameron Grant 				 * if we get here we're a nonvirtual play channel, and either
83867b1dce3SCameron Grant 				 * 1) not busy
83967b1dce3SCameron Grant 				 * 2) busy with children, not directly open
84067b1dce3SCameron Grant 				 *
84167b1dce3SCameron Grant 				 * thus we can add children
84267b1dce3SCameron Grant 				 */
84367b1dce3SCameron Grant 				goto addok;
84467b1dce3SCameron Grant addskip:
84567b1dce3SCameron Grant 			}
84667b1dce3SCameron Grant 			pcm_unlock(d);
84767b1dce3SCameron Grant 			return EBUSY;
84867b1dce3SCameron Grant addok:
84967b1dce3SCameron Grant 			c->flags |= CHN_F_BUSY;
85067b1dce3SCameron Grant 			while (err == 0 && newcnt > cnt) {
85167b1dce3SCameron Grant 				err = vchan_create(c);
85267b1dce3SCameron Grant 				if (err == 0)
85367b1dce3SCameron Grant 					cnt++;
85467b1dce3SCameron Grant 			}
85567b1dce3SCameron Grant 			if (SLIST_EMPTY(&c->children))
85667b1dce3SCameron Grant 				c->flags &= ~CHN_F_BUSY;
85767b1dce3SCameron Grant 		} else if (newcnt < cnt) {
85867b1dce3SCameron Grant 			while (err == 0 && newcnt < cnt) {
85967b1dce3SCameron Grant 				SLIST_FOREACH(sce, &d->channels, link) {
86067b1dce3SCameron Grant 					c = sce->channel;
86167b1dce3SCameron Grant 					if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
86267b1dce3SCameron Grant 						goto remok;
86367b1dce3SCameron Grant 				}
86467b1dce3SCameron Grant 				pcm_unlock(d);
86567b1dce3SCameron Grant 				return EINVAL;
86667b1dce3SCameron Grant remok:
86767b1dce3SCameron Grant 				err = vchan_destroy(c);
86867b1dce3SCameron Grant 				if (err == 0)
86967b1dce3SCameron Grant 					cnt--;
87067b1dce3SCameron Grant 			}
87167b1dce3SCameron Grant 		}
87267b1dce3SCameron Grant 	}
87367b1dce3SCameron Grant 
87467b1dce3SCameron Grant 	pcm_unlock(d);
87567b1dce3SCameron Grant 	return err;
87667b1dce3SCameron Grant }
87767b1dce3SCameron Grant #endif
87867b1dce3SCameron Grant 
87967b1dce3SCameron Grant /************************************************************************/
88067b1dce3SCameron Grant 
88133dbf14aSCameron Grant static moduledata_t sndpcm_mod = {
88233dbf14aSCameron Grant 	"snd_pcm",
883d95502a8SCameron Grant 	NULL,
88433dbf14aSCameron Grant 	NULL
88533dbf14aSCameron Grant };
88633dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
88733dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER);
888