xref: /freebsd/sys/dev/sound/pcm/sound.c (revision a527dbc77cedc9113c74eb4bef16ee0e006dbf76)
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 
36a527dbc7SCameron Grant #ifndef	PCM_DEBUG_MTX
3767b1dce3SCameron Grant struct snddev_channel {
3867b1dce3SCameron Grant 	SLIST_ENTRY(snddev_channel) link;
3967b1dce3SCameron Grant 	struct pcm_channel *channel;
4067b1dce3SCameron Grant };
4167b1dce3SCameron Grant 
4267b1dce3SCameron Grant struct snddev_info {
4367b1dce3SCameron Grant 	SLIST_HEAD(, snddev_channel) channels;
4467b1dce3SCameron Grant 	struct pcm_channel *fakechan;
45a67fe5c1SCameron Grant 	unsigned devcount, playcount, reccount, vchancount;
4667b1dce3SCameron Grant 	unsigned flags;
4767b1dce3SCameron Grant 	int inprog;
48a67fe5c1SCameron Grant 	unsigned int bufsz;
4967b1dce3SCameron Grant 	void *devinfo;
5067b1dce3SCameron Grant 	device_t dev;
5167b1dce3SCameron Grant 	char status[SND_STATUSLEN];
5267b1dce3SCameron Grant 	struct sysctl_ctx_list sysctl_tree;
5367b1dce3SCameron Grant 	struct sysctl_oid *sysctl_tree_top;
5400acb133SCameron Grant 	struct mtx *lock;
5567b1dce3SCameron Grant };
56a527dbc7SCameron Grant #endif
5767b1dce3SCameron Grant 
58d95502a8SCameron Grant devclass_t pcm_devclass;
5982db23e2SCameron Grant 
60b8a36395SCameron Grant int pcm_veto_load = 1;
61b8a36395SCameron Grant 
6282db23e2SCameron Grant #ifdef USING_DEVFS
63d95502a8SCameron Grant int snd_unit = 0;
6409786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit);
6582db23e2SCameron Grant #endif
66cbe7d6a3SCameron Grant 
6767b1dce3SCameron Grant int snd_maxautovchans = 0;
6867b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
69987e5972SCameron Grant 
7082db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
7182db23e2SCameron Grant 
7267b1dce3SCameron Grant static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
7367b1dce3SCameron Grant 
7467b1dce3SCameron Grant struct sysctl_ctx_list *
7567b1dce3SCameron Grant snd_sysctl_tree(device_t dev)
7667b1dce3SCameron Grant {
7767b1dce3SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
7867b1dce3SCameron Grant 
7967b1dce3SCameron Grant 	return &d->sysctl_tree;
8067b1dce3SCameron Grant }
8167b1dce3SCameron Grant 
8267b1dce3SCameron Grant struct sysctl_oid *
8367b1dce3SCameron Grant snd_sysctl_tree_top(device_t dev)
8467b1dce3SCameron Grant {
8567b1dce3SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
8667b1dce3SCameron Grant 
8767b1dce3SCameron Grant 	return d->sysctl_tree_top;
8867b1dce3SCameron Grant }
8967b1dce3SCameron Grant 
9037209180SCameron Grant void *
912c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type)
9237209180SCameron Grant {
9337209180SCameron Grant #ifdef USING_MUTEX
9437209180SCameron Grant 	struct mtx *m;
9537209180SCameron Grant 
96a163d034SWarner Losh 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
9737209180SCameron Grant 	if (m == NULL)
9837209180SCameron Grant 		return NULL;
992c69ba87SJohn Baldwin 	mtx_init(m, desc, type, MTX_RECURSE);
10037209180SCameron Grant 	return m;
10137209180SCameron Grant #else
102a983d575SCameron Grant 	return (void *)0xcafebabe;
10337209180SCameron Grant #endif
10437209180SCameron Grant }
10537209180SCameron Grant 
10637209180SCameron Grant void
10737209180SCameron Grant snd_mtxfree(void *m)
10837209180SCameron Grant {
10937209180SCameron Grant #ifdef USING_MUTEX
11037209180SCameron Grant 	struct mtx *mtx = m;
11137209180SCameron Grant 
11267beb5a5SCameron Grant 	/* mtx_assert(mtx, MA_OWNED); */
11337209180SCameron Grant 	mtx_destroy(mtx);
11437209180SCameron Grant 	free(mtx, M_DEVBUF);
11537209180SCameron Grant #endif
11637209180SCameron Grant }
11737209180SCameron Grant 
11837209180SCameron Grant void
11937209180SCameron Grant snd_mtxassert(void *m)
12037209180SCameron Grant {
12137209180SCameron Grant #ifdef USING_MUTEX
122f00f162aSCameron Grant #ifdef INVARIANTS
12337209180SCameron Grant 	struct mtx *mtx = m;
12437209180SCameron Grant 
12537209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
12637209180SCameron Grant #endif
127f00f162aSCameron Grant #endif
12837209180SCameron Grant }
12967beb5a5SCameron Grant /*
13037209180SCameron Grant void
13137209180SCameron Grant snd_mtxlock(void *m)
13237209180SCameron Grant {
13337209180SCameron Grant #ifdef USING_MUTEX
13437209180SCameron Grant 	struct mtx *mtx = m;
13537209180SCameron Grant 
13637209180SCameron Grant 	mtx_lock(mtx);
13737209180SCameron Grant #endif
13837209180SCameron Grant }
13937209180SCameron Grant 
14037209180SCameron Grant void
14137209180SCameron Grant snd_mtxunlock(void *m)
14237209180SCameron Grant {
14337209180SCameron Grant #ifdef USING_MUTEX
14437209180SCameron Grant 	struct mtx *mtx = m;
14537209180SCameron Grant 
14637209180SCameron Grant 	mtx_unlock(mtx);
14737209180SCameron Grant #endif
14837209180SCameron Grant }
14967beb5a5SCameron Grant */
15037209180SCameron Grant int
15137209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
15237209180SCameron Grant {
15337209180SCameron Grant #ifdef USING_MUTEX
15437209180SCameron Grant 	flags &= INTR_MPSAFE;
15546700f12SPeter Wemm 	flags |= INTR_TYPE_AV;
15637209180SCameron Grant #else
15746700f12SPeter Wemm 	flags = INTR_TYPE_AV;
15837209180SCameron Grant #endif
15937209180SCameron Grant 	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
16037209180SCameron Grant }
16137209180SCameron Grant 
162a527dbc7SCameron Grant #ifndef	PCM_DEBUG_MTX
16367b1dce3SCameron Grant void
16467b1dce3SCameron Grant pcm_lock(struct snddev_info *d)
16567b1dce3SCameron Grant {
16667b1dce3SCameron Grant 	snd_mtxlock(d->lock);
16767b1dce3SCameron Grant }
16867b1dce3SCameron Grant 
16967b1dce3SCameron Grant void
17067b1dce3SCameron Grant pcm_unlock(struct snddev_info *d)
17167b1dce3SCameron Grant {
17267b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
17367b1dce3SCameron Grant }
174a527dbc7SCameron Grant #endif
17567b1dce3SCameron Grant 
17667b1dce3SCameron Grant struct pcm_channel *
17767b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d)
17867b1dce3SCameron Grant {
17967b1dce3SCameron Grant 	return d->fakechan;
18067b1dce3SCameron Grant }
18167b1dce3SCameron Grant 
182b8f0d9e0SCameron Grant /* return a locked channel */
183285648f9SCameron Grant struct pcm_channel *
184506a5308SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
185285648f9SCameron Grant {
186285648f9SCameron Grant 	struct pcm_channel *c;
187285648f9SCameron Grant     	struct snddev_channel *sce;
188f637a36cSCameron Grant 	int err;
189285648f9SCameron Grant 
190b8f0d9e0SCameron Grant 	snd_mtxassert(d->lock);
191f637a36cSCameron Grant 
192f637a36cSCameron Grant 	/* scan for a free channel */
193285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
194285648f9SCameron Grant 		c = sce->channel;
19549c5e6e2SCameron Grant 		CHN_LOCK(c);
196285648f9SCameron Grant 		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
197506a5308SCameron Grant 			if (chnum == -1 || c->num == chnum) {
198285648f9SCameron Grant 				c->flags |= CHN_F_BUSY;
199b8f0d9e0SCameron Grant 				c->pid = pid;
200285648f9SCameron Grant 				return c;
201285648f9SCameron Grant 			}
202506a5308SCameron Grant 		}
20349c5e6e2SCameron Grant 		CHN_UNLOCK(c);
204285648f9SCameron Grant 	}
205f637a36cSCameron Grant 
206f637a36cSCameron Grant 	/* no channel available */
207f637a36cSCameron Grant 	if (direction == PCMDIR_PLAY) {
20867b1dce3SCameron Grant 		if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
209f637a36cSCameron Grant 			/* try to create a vchan */
210f637a36cSCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
211f637a36cSCameron Grant 				c = sce->channel;
212f637a36cSCameron Grant 				if (!SLIST_EMPTY(&c->children)) {
213f637a36cSCameron Grant 					err = vchan_create(c);
214f637a36cSCameron Grant 					if (!err)
215506a5308SCameron Grant 						return pcm_chnalloc(d, direction, pid, -1);
216f637a36cSCameron Grant 					else
217f637a36cSCameron Grant 						device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
218f637a36cSCameron Grant 				}
219f637a36cSCameron Grant 			}
220f637a36cSCameron Grant 		}
221f637a36cSCameron Grant 	}
222f637a36cSCameron Grant 
223285648f9SCameron Grant 	return NULL;
224285648f9SCameron Grant }
225285648f9SCameron Grant 
226b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
227285648f9SCameron Grant int
228b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
229285648f9SCameron Grant {
230b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
231285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
232b8f0d9e0SCameron Grant 	c->pid = -1;
23349c5e6e2SCameron Grant 	CHN_UNLOCK(c);
234285648f9SCameron Grant 	return 0;
235285648f9SCameron Grant }
236285648f9SCameron Grant 
237285648f9SCameron Grant int
238285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
239285648f9SCameron Grant {
24049c5e6e2SCameron Grant 	int r;
24149c5e6e2SCameron Grant 
242b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
243285648f9SCameron Grant 	c->refcount += ref;
24449c5e6e2SCameron Grant 	r = c->refcount;
24549c5e6e2SCameron Grant 	return r;
246285648f9SCameron Grant }
247285648f9SCameron Grant 
24867b1dce3SCameron Grant int
24967b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta)
25067b1dce3SCameron Grant {
251a527dbc7SCameron Grant 	int r;
252a527dbc7SCameron Grant 
253a527dbc7SCameron Grant 	if (delta == 0)
25467b1dce3SCameron Grant 		return d->inprog;
255a527dbc7SCameron Grant 
256a527dbc7SCameron Grant 	/* backtrace(); */
257a527dbc7SCameron Grant 	pcm_lock(d);
258a527dbc7SCameron Grant 	d->inprog += delta;
259a527dbc7SCameron Grant 	r = d->inprog;
260a527dbc7SCameron Grant 	pcm_unlock(d);
261a527dbc7SCameron Grant 	return r;
26267b1dce3SCameron Grant }
26367b1dce3SCameron Grant 
26467b1dce3SCameron Grant static void
26567b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num)
26667b1dce3SCameron Grant {
26767b1dce3SCameron Grant 	struct pcm_channel *c;
26867b1dce3SCameron Grant     	struct snddev_channel *sce;
26967b1dce3SCameron Grant 	int err, done;
27067b1dce3SCameron Grant 
27167b1dce3SCameron Grant 	if (num > 0 && d->vchancount == 0) {
27267b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
27367b1dce3SCameron Grant 			c = sce->channel;
27467b1dce3SCameron Grant 			if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
27567b1dce3SCameron Grant 				c->flags |= CHN_F_BUSY;
27667b1dce3SCameron Grant 				err = vchan_create(c);
27767b1dce3SCameron Grant 				if (err) {
27867b1dce3SCameron Grant 					device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
27967b1dce3SCameron Grant 					c->flags &= ~CHN_F_BUSY;
28067b1dce3SCameron Grant 				}
28167b1dce3SCameron Grant 				return;
28267b1dce3SCameron Grant 			}
28367b1dce3SCameron Grant 		}
28467b1dce3SCameron Grant 	}
28567b1dce3SCameron Grant 	if (num == 0 && d->vchancount > 0) {
28667b1dce3SCameron Grant 		done = 0;
28767b1dce3SCameron Grant 		while (!done) {
28867b1dce3SCameron Grant 			done = 1;
28967b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
29067b1dce3SCameron Grant 				c = sce->channel;
29167b1dce3SCameron Grant 				if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
29267b1dce3SCameron Grant 					done = 0;
293a527dbc7SCameron Grant 					snd_mtxlock(d->lock);
29467b1dce3SCameron Grant 					err = vchan_destroy(c);
295a527dbc7SCameron Grant 					snd_mtxunlock(d->lock);
29667b1dce3SCameron Grant 					if (err)
29767b1dce3SCameron Grant 						device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
2989cfd8eb3SPeter Wemm 					break;		/* restart */
29967b1dce3SCameron Grant 				}
30067b1dce3SCameron Grant 			}
30167b1dce3SCameron Grant 		}
30267b1dce3SCameron Grant 	}
30367b1dce3SCameron Grant }
30467b1dce3SCameron Grant 
30582db23e2SCameron Grant #ifdef USING_DEVFS
30633dbf14aSCameron Grant static int
307cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
30833dbf14aSCameron Grant {
309b8f0d9e0SCameron Grant 	struct snddev_info *d;
31033dbf14aSCameron Grant 	int error, unit;
31133dbf14aSCameron Grant 
31233dbf14aSCameron Grant 	unit = snd_unit;
31333dbf14aSCameron Grant 	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
31433dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
31574ffd138SCameron Grant 		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
316b8f0d9e0SCameron Grant 			return EINVAL;
317b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
318faeebea2SCameron Grant 		if (d == NULL || SLIST_EMPTY(&d->channels))
319b8f0d9e0SCameron Grant 			return EINVAL;
32033dbf14aSCameron Grant 		snd_unit = unit;
32133dbf14aSCameron Grant 	}
32233dbf14aSCameron Grant 	return (error);
32333dbf14aSCameron Grant }
324b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
325cd9766c5SCameron Grant             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
32682db23e2SCameron Grant #endif
327987e5972SCameron Grant 
328cd9766c5SCameron Grant static int
32967b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
330cd9766c5SCameron Grant {
33167b1dce3SCameron Grant 	struct snddev_info *d;
33267b1dce3SCameron Grant 	int i, v, error;
333cd9766c5SCameron Grant 
33467b1dce3SCameron Grant 	v = snd_maxautovchans;
335cd9766c5SCameron Grant 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
336cd9766c5SCameron Grant 	if (error == 0 && req->newptr != NULL) {
337cd9766c5SCameron Grant 		if (v < 0 || v >= SND_MAXVCHANS)
338cd9766c5SCameron Grant 			return EINVAL;
33967b1dce3SCameron Grant 		if (v != snd_maxautovchans) {
34067b1dce3SCameron Grant 			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
34167b1dce3SCameron Grant 				d = devclass_get_softc(pcm_devclass, i);
34267b1dce3SCameron Grant 				if (!d)
34367b1dce3SCameron Grant 					continue;
34467b1dce3SCameron Grant 				pcm_setmaxautovchans(d, v);
34567b1dce3SCameron Grant 			}
34667b1dce3SCameron Grant 		}
34767b1dce3SCameron Grant 		snd_maxautovchans = v;
348cd9766c5SCameron Grant 	}
349cd9766c5SCameron Grant 	return (error);
350cd9766c5SCameron Grant }
35167b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
35267b1dce3SCameron Grant             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
353f637a36cSCameron Grant 
354285648f9SCameron Grant struct pcm_channel *
355285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
356987e5972SCameron Grant {
357285648f9SCameron Grant 	struct pcm_channel *ch;
35833dbf14aSCameron Grant 	char *dirs;
359a67fe5c1SCameron Grant     	int err, *pnum;
360987e5972SCameron Grant 
361285648f9SCameron Grant 	switch(dir) {
362285648f9SCameron Grant 	case PCMDIR_PLAY:
363285648f9SCameron Grant 		dirs = "play";
364a67fe5c1SCameron Grant 		pnum = &d->playcount;
365285648f9SCameron Grant 		break;
366a67fe5c1SCameron Grant 
367285648f9SCameron Grant 	case PCMDIR_REC:
368285648f9SCameron Grant 		dirs = "record";
369a67fe5c1SCameron Grant 		pnum = &d->reccount;
370285648f9SCameron Grant 		break;
371a67fe5c1SCameron Grant 
372285648f9SCameron Grant 	case PCMDIR_VIRTUAL:
373285648f9SCameron Grant 		dirs = "virtual";
374285648f9SCameron Grant 		dir = PCMDIR_PLAY;
375a67fe5c1SCameron Grant 		pnum = &d->vchancount;
376285648f9SCameron Grant 		break;
377a67fe5c1SCameron Grant 
378285648f9SCameron Grant 	default:
379285648f9SCameron Grant 		return NULL;
3809c326820SCameron Grant 	}
381285648f9SCameron Grant 
382a163d034SWarner Losh 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
383285648f9SCameron Grant 	if (!ch)
384285648f9SCameron Grant 		return NULL;
385285648f9SCameron Grant 
386a163d034SWarner Losh 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
387285648f9SCameron Grant 	if (!ch->methods) {
388285648f9SCameron Grant 		free(ch, M_DEVBUF);
389a67fe5c1SCameron Grant 
390285648f9SCameron Grant 		return NULL;
391285648f9SCameron Grant 	}
392285648f9SCameron Grant 
39367beb5a5SCameron Grant 	snd_mtxlock(d->lock);
394a67fe5c1SCameron Grant 	ch->num = (*pnum)++;
39567beb5a5SCameron Grant 	snd_mtxunlock(d->lock);
396a67fe5c1SCameron Grant 
397285648f9SCameron Grant 	ch->pid = -1;
398285648f9SCameron Grant 	ch->parentsnddev = d;
399285648f9SCameron Grant 	ch->parentchannel = parent;
400436c9b65SScott Long 	ch->dev = d->dev;
40167beb5a5SCameron Grant 	snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
402285648f9SCameron Grant 
4030f55ac6cSCameron Grant 	err = chn_init(ch, devinfo, dir);
4040f55ac6cSCameron Grant 	if (err) {
405a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
406285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
407285648f9SCameron Grant 		free(ch, M_DEVBUF);
40867beb5a5SCameron Grant 		snd_mtxlock(d->lock);
409a67fe5c1SCameron Grant 		(*pnum)--;
41067beb5a5SCameron Grant 		snd_mtxunlock(d->lock);
411a67fe5c1SCameron Grant 
412285648f9SCameron Grant 		return NULL;
413bbb5bf3dSCameron Grant 	}
414285648f9SCameron Grant 
415285648f9SCameron Grant 	return ch;
416285648f9SCameron Grant }
417285648f9SCameron Grant 
418285648f9SCameron Grant int
419285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
420285648f9SCameron Grant {
421a67fe5c1SCameron Grant 	struct snddev_info *d;
422285648f9SCameron Grant 	int err;
423285648f9SCameron Grant 
424a67fe5c1SCameron Grant 	d = ch->parentsnddev;
425285648f9SCameron Grant 	err = chn_kill(ch);
426285648f9SCameron Grant 	if (err) {
427a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
428285648f9SCameron Grant 		return err;
429285648f9SCameron Grant 	}
430285648f9SCameron Grant 
431285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
432285648f9SCameron Grant 	free(ch, M_DEVBUF);
433285648f9SCameron Grant 
434285648f9SCameron Grant 	return 0;
435285648f9SCameron Grant }
436285648f9SCameron Grant 
437285648f9SCameron Grant int
438f637a36cSCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
439285648f9SCameron Grant {
44067b1dce3SCameron Grant     	struct snddev_channel *sce, *tmp, *after;
441285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
44267beb5a5SCameron Grant 	int x = -1;
443b8f0d9e0SCameron Grant 
444a163d034SWarner Losh 	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
445285648f9SCameron Grant 	if (!sce) {
446285648f9SCameron Grant 		return ENOMEM;
447285648f9SCameron Grant 	}
448285648f9SCameron Grant 
4497cf0e77aSOrion Hodson 	snd_mtxlock(d->lock);
450285648f9SCameron Grant 	sce->channel = ch;
45167b1dce3SCameron Grant 	if (SLIST_EMPTY(&d->channels)) {
452285648f9SCameron Grant 		SLIST_INSERT_HEAD(&d->channels, sce, link);
45367b1dce3SCameron Grant 	} else {
45467b1dce3SCameron Grant 		after = NULL;
45567b1dce3SCameron Grant 		SLIST_FOREACH(tmp, &d->channels, link) {
45667b1dce3SCameron Grant 			after = tmp;
45767b1dce3SCameron Grant 		}
45867b1dce3SCameron Grant 		SLIST_INSERT_AFTER(after, sce, link);
45967b1dce3SCameron Grant 	}
46067beb5a5SCameron Grant 	if (mkdev)
46167beb5a5SCameron Grant 		x = d->devcount++;
46267beb5a5SCameron Grant 	snd_mtxunlock(d->lock);
463285648f9SCameron Grant 
464506a5308SCameron Grant 	if (mkdev) {
46567beb5a5SCameron Grant 		dsp_register(unit, x);
466506a5308SCameron Grant 		if (ch->direction == PCMDIR_REC)
467506a5308SCameron Grant 			dsp_registerrec(unit, ch->num);
468506a5308SCameron Grant 	}
469b8f0d9e0SCameron Grant 
47033dbf14aSCameron Grant 	return 0;
47133dbf14aSCameron Grant }
47233dbf14aSCameron Grant 
473285648f9SCameron Grant int
474f637a36cSCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
47533dbf14aSCameron Grant {
476285648f9SCameron Grant     	struct snddev_channel *sce;
477285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
478a527dbc7SCameron Grant 	int ourlock;
47933dbf14aSCameron Grant 
480a527dbc7SCameron Grant 	ourlock = 0;
481a527dbc7SCameron Grant 	if (!mtx_owned(d->lock)) {
48249c5e6e2SCameron Grant 		snd_mtxlock(d->lock);
483a527dbc7SCameron Grant 		ourlock = 1;
484a527dbc7SCameron Grant 	}
485a527dbc7SCameron Grant 
486285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
487285648f9SCameron Grant 		if (sce->channel == ch)
488285648f9SCameron Grant 			goto gotit;
48933dbf14aSCameron Grant 	}
490a527dbc7SCameron Grant 	if (ourlock)
49149c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
492285648f9SCameron Grant 	return EINVAL;
493285648f9SCameron Grant gotit:
494285648f9SCameron Grant 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
495506a5308SCameron Grant 	if (rmdev) {
496f637a36cSCameron Grant 		dsp_unregister(unit, --d->devcount);
497506a5308SCameron Grant 		if (ch->direction == PCMDIR_REC)
498a67fe5c1SCameron Grant 			dsp_unregisterrec(unit, ch->num);
499506a5308SCameron Grant 	}
50067beb5a5SCameron Grant 
50167beb5a5SCameron Grant     	if (ch->direction == PCMDIR_REC)
50267beb5a5SCameron Grant 		d->reccount--;
50367beb5a5SCameron Grant 	else if (ch->flags & CHN_F_VIRTUAL)
50467beb5a5SCameron Grant 		d->vchancount--;
50567beb5a5SCameron Grant 	else
50667beb5a5SCameron Grant 		d->playcount--;
50767beb5a5SCameron Grant 
508a527dbc7SCameron Grant 	if (ourlock)
50949c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
51067beb5a5SCameron Grant 	free(sce, M_DEVBUF);
511285648f9SCameron Grant 
512987e5972SCameron Grant 	return 0;
513987e5972SCameron Grant }
514987e5972SCameron Grant 
515987e5972SCameron Grant int
516285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
517285648f9SCameron Grant {
518285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
51967b1dce3SCameron Grant 	struct pcm_channel *ch;
52067b1dce3SCameron Grant     	int err;
521285648f9SCameron Grant 
522285648f9SCameron Grant 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
523285648f9SCameron Grant 	if (!ch) {
524285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
525285648f9SCameron Grant 		return ENODEV;
526285648f9SCameron Grant 	}
527cd9766c5SCameron Grant 
528f637a36cSCameron Grant 	err = pcm_chn_add(d, ch, 1);
529285648f9SCameron Grant 	if (err) {
530285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
53167beb5a5SCameron Grant 		snd_mtxunlock(d->lock);
532285648f9SCameron Grant 		pcm_chn_destroy(ch);
533cd9766c5SCameron Grant 		return err;
534cd9766c5SCameron Grant 	}
535cd9766c5SCameron Grant 
53661698e0cSAlexander Kabaev 	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
53761698e0cSAlexander Kabaev 	    ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
538cd9766c5SCameron Grant 		ch->flags |= CHN_F_BUSY;
539cd9766c5SCameron Grant 		err = vchan_create(ch);
540cd9766c5SCameron Grant 		if (err) {
54167b1dce3SCameron Grant 			device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
54267b1dce3SCameron Grant 			ch->flags &= ~CHN_F_BUSY;
543cd9766c5SCameron Grant 		}
544285648f9SCameron Grant 	}
545285648f9SCameron Grant 
546285648f9SCameron Grant 	return err;
547285648f9SCameron Grant }
548285648f9SCameron Grant 
549285648f9SCameron Grant static int
550285648f9SCameron Grant pcm_killchan(device_t dev)
551285648f9SCameron Grant {
552285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
553285648f9SCameron Grant     	struct snddev_channel *sce;
554e33bee07SOlivier Houchard 	struct pcm_channel *ch;
555e33bee07SOlivier Houchard 	int error = 0;
556285648f9SCameron Grant 
55749c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
558285648f9SCameron Grant 	sce = SLIST_FIRST(&d->channels);
55949c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
560e33bee07SOlivier Houchard 	ch = sce->channel;
561285648f9SCameron Grant 
562e550089dSOlivier Houchard 	error = pcm_chn_remove(d, sce->channel, SLIST_EMPTY(&ch->children));
563e33bee07SOlivier Houchard 	if (error)
564e33bee07SOlivier Houchard 		return (error);
565e33bee07SOlivier Houchard 	return (pcm_chn_destroy(ch));
566285648f9SCameron Grant }
567285648f9SCameron Grant 
568285648f9SCameron Grant int
569987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
570987e5972SCameron Grant {
57166ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
57249c5e6e2SCameron Grant 
57349c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
574987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
57549c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
576987e5972SCameron Grant 	return 0;
577987e5972SCameron Grant }
578987e5972SCameron Grant 
579987e5972SCameron Grant u_int32_t
580987e5972SCameron Grant pcm_getflags(device_t dev)
581987e5972SCameron Grant {
58266ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
58349c5e6e2SCameron Grant 
584987e5972SCameron Grant 	return d->flags;
585987e5972SCameron Grant }
586987e5972SCameron Grant 
587987e5972SCameron Grant void
588987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
589987e5972SCameron Grant {
59066ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
591d95502a8SCameron Grant 
592987e5972SCameron Grant 	d->flags = val;
593987e5972SCameron Grant }
594987e5972SCameron Grant 
59539004e69SCameron Grant void *
59639004e69SCameron Grant pcm_getdevinfo(device_t dev)
59739004e69SCameron Grant {
59866ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
59949c5e6e2SCameron Grant 
60039004e69SCameron Grant 	return d->devinfo;
60139004e69SCameron Grant }
60239004e69SCameron Grant 
603a67fe5c1SCameron Grant unsigned int
604a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
605a67fe5c1SCameron Grant {
606a67fe5c1SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
6074e60be34SCameron Grant 	int sz, x;
608a67fe5c1SCameron Grant 
609a67fe5c1SCameron Grant 	sz = 0;
6104e60be34SCameron Grant 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
6114e60be34SCameron Grant 		x = sz;
612a67fe5c1SCameron Grant 		RANGE(sz, min, max);
6134e60be34SCameron Grant 		if (x != sz)
6144e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
6154e60be34SCameron Grant 		x = min;
6164e60be34SCameron Grant 		while (x < sz)
6174e60be34SCameron Grant 			x <<= 1;
6184e60be34SCameron Grant 		if (x > sz)
6194e60be34SCameron Grant 			x >>= 1;
6204e60be34SCameron Grant 		if (x != sz) {
6214e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
6224e60be34SCameron Grant 			sz = x;
6234e60be34SCameron Grant 		}
6244e60be34SCameron Grant 	} else {
625a67fe5c1SCameron Grant 		sz = deflt;
6264e60be34SCameron Grant 	}
6274e60be34SCameron Grant 
628a67fe5c1SCameron Grant 	d->bufsz = sz;
629a67fe5c1SCameron Grant 
630a67fe5c1SCameron Grant 	return sz;
631a67fe5c1SCameron Grant }
632a67fe5c1SCameron Grant 
633987e5972SCameron Grant int
634987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
635987e5972SCameron Grant {
63666ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
637987e5972SCameron Grant 
638b8a36395SCameron Grant 	if (pcm_veto_load) {
639b8a36395SCameron Grant 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
640b8a36395SCameron Grant 
641b8a36395SCameron Grant 		return EINVAL;
642b8a36395SCameron Grant 	}
643b8a36395SCameron Grant 
6442c69ba87SJohn Baldwin 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
645285648f9SCameron Grant 
646cd9766c5SCameron Grant 	d->flags = 0;
647e4d5b250SCameron Grant 	d->dev = dev;
648987e5972SCameron Grant 	d->devinfo = devinfo;
649f637a36cSCameron Grant 	d->devcount = 0;
650506a5308SCameron Grant 	d->reccount = 0;
651a67fe5c1SCameron Grant 	d->playcount = 0;
652f637a36cSCameron Grant 	d->vchancount = 0;
653d95502a8SCameron Grant 	d->inprog = 0;
654833f7023SCameron Grant 
655d95502a8SCameron Grant 	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
656285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
657d95502a8SCameron Grant 
658285648f9SCameron Grant 	d->fakechan = fkchan_setup(dev);
659285648f9SCameron Grant 	chn_init(d->fakechan, NULL, 0);
660987e5972SCameron Grant 
66182db23e2SCameron Grant #ifdef SND_DYNSYSCTL
662cc486d80SJohn Baldwin 	sysctl_ctx_init(&d->sysctl_tree);
663cc486d80SJohn Baldwin 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
664a3e893e0SJohn Baldwin 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
665cc486d80SJohn Baldwin 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
666a3e893e0SJohn Baldwin 	if (d->sysctl_tree_top == NULL) {
667cc486d80SJohn Baldwin 		sysctl_ctx_free(&d->sysctl_tree);
668cc486d80SJohn Baldwin 		goto no;
669cc486d80SJohn Baldwin 	}
670a67fe5c1SCameron Grant 	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
671a67fe5c1SCameron Grant             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
67282db23e2SCameron Grant #endif
673b8f0d9e0SCameron Grant 	if (numplay > 0)
67467b1dce3SCameron Grant 		vchan_initsys(dev);
675cd9766c5SCameron Grant 	if (numplay == 1)
676cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
677cd9766c5SCameron Grant 
67867b1dce3SCameron Grant 	sndstat_register(dev, d->status, sndstat_prepare_pcm);
679987e5972SCameron Grant     	return 0;
680987e5972SCameron Grant no:
68149c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
682987e5972SCameron Grant 	return ENXIO;
683987e5972SCameron Grant }
684987e5972SCameron Grant 
68533dbf14aSCameron Grant int
68633dbf14aSCameron Grant pcm_unregister(device_t dev)
6877c438dbeSCameron Grant {
68866ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
689285648f9SCameron Grant     	struct snddev_channel *sce;
690a67fe5c1SCameron Grant 	struct pcm_channel *ch;
6917c438dbeSCameron Grant 
69249c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
693d95502a8SCameron Grant 	if (d->inprog) {
6945c25132aSGeorge C A Reid 		device_printf(dev, "unregister: operation in progress\n");
6955c25132aSGeorge C A Reid 		snd_mtxunlock(d->lock);
6965c25132aSGeorge C A Reid 		return EBUSY;
6975c25132aSGeorge C A Reid 	}
6985c25132aSGeorge C A Reid 	if (sndstat_busy() != 0) {
6995c25132aSGeorge C A Reid 		device_printf(dev, "unregister: sndstat busy\n");
700d95502a8SCameron Grant 		snd_mtxunlock(d->lock);
701d95502a8SCameron Grant 		return EBUSY;
702d95502a8SCameron Grant 	}
703285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
704a67fe5c1SCameron Grant 		ch = sce->channel;
705a67fe5c1SCameron Grant 		if (ch->refcount > 0) {
70621ed9908SCameron Grant 			device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
70749c5e6e2SCameron Grant 			snd_mtxunlock(d->lock);
708285648f9SCameron Grant 			return EBUSY;
709285648f9SCameron Grant 		}
710c9b53085SCameron Grant 	}
711d95502a8SCameron Grant 	if (mixer_uninit(dev)) {
7125c25132aSGeorge C A Reid 		device_printf(dev, "unregister: mixer busy\n");
71349c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
714c9b53085SCameron Grant 		return EBUSY;
715c9b53085SCameron Grant 	}
7167c438dbeSCameron Grant 
71766ef8af5SCameron Grant #ifdef SND_DYNSYSCTL
71866ef8af5SCameron Grant 	d->sysctl_tree_top = NULL;
71966ef8af5SCameron Grant 	sysctl_ctx_free(&d->sysctl_tree);
72066ef8af5SCameron Grant #endif
721faeebea2SCameron Grant 	while (!SLIST_EMPTY(&d->channels))
722285648f9SCameron Grant 		pcm_killchan(dev);
7237c438dbeSCameron Grant 
72466ef8af5SCameron Grant 	chn_kill(d->fakechan);
72566ef8af5SCameron Grant 	fkchan_kill(d->fakechan);
72682db23e2SCameron Grant 
72747172de8SNick Sayer 	sndstat_unregister(dev);
72849c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
72933dbf14aSCameron Grant 	return 0;
73033dbf14aSCameron Grant }
7317c438dbeSCameron Grant 
73267b1dce3SCameron Grant /************************************************************************/
73367b1dce3SCameron Grant 
73467b1dce3SCameron Grant static int
73567b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
73667b1dce3SCameron Grant {
73767b1dce3SCameron Grant     	struct snddev_info *d;
73867b1dce3SCameron Grant     	struct snddev_channel *sce;
73967b1dce3SCameron Grant 	struct pcm_channel *c;
74067b1dce3SCameron Grant 	struct pcm_feeder *f;
74167b1dce3SCameron Grant     	int pc, rc, vc;
74267b1dce3SCameron Grant 
74367b1dce3SCameron Grant 	if (verbose < 1)
74467b1dce3SCameron Grant 		return 0;
74567b1dce3SCameron Grant 
74667b1dce3SCameron Grant 	d = device_get_softc(dev);
74767b1dce3SCameron Grant 	if (!d)
74867b1dce3SCameron Grant 		return ENXIO;
74967b1dce3SCameron Grant 
75067b1dce3SCameron Grant 	snd_mtxlock(d->lock);
75167b1dce3SCameron Grant 	if (!SLIST_EMPTY(&d->channels)) {
75267b1dce3SCameron Grant 		pc = rc = vc = 0;
75367b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
75467b1dce3SCameron Grant 			c = sce->channel;
75567b1dce3SCameron Grant 			if (c->direction == PCMDIR_PLAY) {
75667b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
75767b1dce3SCameron Grant 					vc++;
75867b1dce3SCameron Grant 				else
75967b1dce3SCameron Grant 					pc++;
76067b1dce3SCameron Grant 			} else
76167b1dce3SCameron Grant 				rc++;
76267b1dce3SCameron Grant 		}
763a67fe5c1SCameron Grant 		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
76467b1dce3SCameron Grant 				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
76567b1dce3SCameron Grant #ifdef USING_DEVFS
76667b1dce3SCameron Grant 				(device_get_unit(dev) == snd_unit)? " default" : ""
76767b1dce3SCameron Grant #else
76867b1dce3SCameron Grant 				""
76967b1dce3SCameron Grant #endif
77067b1dce3SCameron Grant 				);
771a527dbc7SCameron Grant 
772a527dbc7SCameron Grant 		if (verbose <= 1) {
773a527dbc7SCameron Grant 			snd_mtxunlock(d->lock);
774a527dbc7SCameron Grant 			return 0;
775a527dbc7SCameron Grant 		}
776a527dbc7SCameron Grant 
77767b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
77867b1dce3SCameron Grant 			c = sce->channel;
779a3285889SCameron Grant 			sbuf_printf(s, "\n\t");
780a3285889SCameron Grant 
781a527dbc7SCameron Grant 			/* it would be bettet to indent child channels */
782a3285889SCameron Grant 			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
7834c68642aSCameron Grant 			sbuf_printf(s, "spd %d", c->speed);
7844c68642aSCameron Grant 			if (c->speed != sndbuf_getspd(c->bufhard))
7854c68642aSCameron Grant 				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
7864c68642aSCameron Grant 			sbuf_printf(s, ", fmt 0x%08x", c->format);
7874c68642aSCameron Grant 			if (c->format != sndbuf_getfmt(c->bufhard))
7884c68642aSCameron Grant 				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
789a527dbc7SCameron Grant 			sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
79067b1dce3SCameron Grant 			if (c->pid != -1)
79167b1dce3SCameron Grant 				sbuf_printf(s, ", pid %d", c->pid);
79267b1dce3SCameron Grant 			sbuf_printf(s, "\n\t");
7934c68642aSCameron Grant 
794edecdda7SCameron Grant 			if (c->bufhard != NULL && c->bufsoft != NULL) {
795a3285889SCameron Grant 				sbuf_printf(s, "interrupts %d, ", c->interrupts);
796a3285889SCameron Grant 				if (c->direction == PCMDIR_REC)
797a3285889SCameron Grant 					sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
798a3285889SCameron Grant 						c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
799a3285889SCameron Grant 				else
800edecdda7SCameron Grant 					sbuf_printf(s, "underruns %d, ready %d",
801edecdda7SCameron Grant 						c->xruns, sndbuf_getready(c->bufsoft));
802a3285889SCameron Grant 				sbuf_printf(s, "\n\t");
803a3285889SCameron Grant 			}
8044c68642aSCameron Grant 
8054c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
8064c68642aSCameron Grant 			sbuf_printf(s, " -> ");
80767b1dce3SCameron Grant 			f = c->feeder;
8084c68642aSCameron Grant 			while (f->source != NULL)
8094c68642aSCameron Grant 				f = f->source;
8104c68642aSCameron Grant 			while (f != NULL) {
81167b1dce3SCameron Grant 				sbuf_printf(s, "%s", f->class->name);
81267b1dce3SCameron Grant 				if (f->desc->type == FEEDER_FMT)
8134c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
81467b1dce3SCameron Grant 				if (f->desc->type == FEEDER_RATE)
8154c68642aSCameron Grant 					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
81667b1dce3SCameron Grant 				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
8174c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x)", f->desc->out);
8184c68642aSCameron Grant 				sbuf_printf(s, " -> ");
8194c68642aSCameron Grant 				f = f->parent;
82067b1dce3SCameron Grant 			}
8214c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
82267b1dce3SCameron Grant 		}
82367b1dce3SCameron Grant 	} else
82467b1dce3SCameron Grant 		sbuf_printf(s, " (mixer only)");
82567b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
82667b1dce3SCameron Grant 
82767b1dce3SCameron Grant 	return 0;
82867b1dce3SCameron Grant }
82967b1dce3SCameron Grant 
83067b1dce3SCameron Grant /************************************************************************/
83167b1dce3SCameron Grant 
83267b1dce3SCameron Grant #ifdef SND_DYNSYSCTL
83367b1dce3SCameron Grant int
83467b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
83567b1dce3SCameron Grant {
83667b1dce3SCameron Grant 	struct snddev_info *d;
83767b1dce3SCameron Grant     	struct snddev_channel *sce;
83867b1dce3SCameron Grant 	struct pcm_channel *c;
839a527dbc7SCameron Grant 	int err, newcnt, cnt, busy;
840a527dbc7SCameron Grant 	int x;
84167b1dce3SCameron Grant 
84267b1dce3SCameron Grant 	d = oidp->oid_arg1;
84367b1dce3SCameron Grant 
844a527dbc7SCameron Grant 	x = pcm_inprog(d, 1);
845a527dbc7SCameron Grant 	if (x != 1) {
846a527dbc7SCameron Grant 		printf("x: %d\n", x);
847a527dbc7SCameron Grant 		pcm_inprog(d, -1);
848a527dbc7SCameron Grant 		return EINPROGRESS;
849a527dbc7SCameron Grant 	}
850a527dbc7SCameron Grant 
851a527dbc7SCameron Grant 	busy = 0;
85267b1dce3SCameron Grant 	cnt = 0;
85367b1dce3SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
85467b1dce3SCameron Grant 		c = sce->channel;
855a527dbc7SCameron Grant 		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
85667b1dce3SCameron Grant 			cnt++;
857a527dbc7SCameron Grant 			if (c->flags & CHN_F_BUSY)
858a527dbc7SCameron Grant 				busy++;
85967b1dce3SCameron Grant 		}
860a527dbc7SCameron Grant 	}
861a527dbc7SCameron Grant 
86267b1dce3SCameron Grant 	newcnt = cnt;
86367b1dce3SCameron Grant 
86467b1dce3SCameron Grant 	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
865a527dbc7SCameron Grant 
86667b1dce3SCameron Grant 	if (err == 0 && req->newptr != NULL) {
867a527dbc7SCameron Grant 
86867b1dce3SCameron Grant 		if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
869a527dbc7SCameron Grant 			pcm_inprog(d, -1);
870a527dbc7SCameron Grant 			return E2BIG;
87167b1dce3SCameron Grant 		}
87267b1dce3SCameron Grant 
87367b1dce3SCameron Grant 		if (newcnt > cnt) {
87467b1dce3SCameron Grant 			/* add new vchans - find a parent channel first */
87567b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
87667b1dce3SCameron Grant 				c = sce->channel;
87767b1dce3SCameron Grant 				/* not a candidate if not a play channel */
87867b1dce3SCameron Grant 				if (c->direction != PCMDIR_PLAY)
8799cfd8eb3SPeter Wemm 					continue;
88067b1dce3SCameron Grant 				/* not a candidate if a virtual channel */
88167b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
8829cfd8eb3SPeter Wemm 					continue;
88367b1dce3SCameron Grant 				/* not a candidate if it's in use */
88467b1dce3SCameron Grant 				if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
8859cfd8eb3SPeter Wemm 					continue;
88667b1dce3SCameron Grant 				/*
88767b1dce3SCameron Grant 				 * if we get here we're a nonvirtual play channel, and either
88867b1dce3SCameron Grant 				 * 1) not busy
88967b1dce3SCameron Grant 				 * 2) busy with children, not directly open
89067b1dce3SCameron Grant 				 *
89167b1dce3SCameron Grant 				 * thus we can add children
89267b1dce3SCameron Grant 				 */
89367b1dce3SCameron Grant 				goto addok;
89467b1dce3SCameron Grant 			}
895a527dbc7SCameron Grant 			pcm_inprog(d, -1);
89667b1dce3SCameron Grant 			return EBUSY;
89767b1dce3SCameron Grant addok:
89867b1dce3SCameron Grant 			c->flags |= CHN_F_BUSY;
89967b1dce3SCameron Grant 			while (err == 0 && newcnt > cnt) {
90067b1dce3SCameron Grant 				err = vchan_create(c);
90167b1dce3SCameron Grant 				if (err == 0)
90267b1dce3SCameron Grant 					cnt++;
90367b1dce3SCameron Grant 			}
90467b1dce3SCameron Grant 			if (SLIST_EMPTY(&c->children))
90567b1dce3SCameron Grant 				c->flags &= ~CHN_F_BUSY;
90667b1dce3SCameron Grant 		} else if (newcnt < cnt) {
907a527dbc7SCameron Grant 			if (busy > newcnt) {
908a527dbc7SCameron Grant 				printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy);
909a527dbc7SCameron Grant 				pcm_inprog(d, -1);
910a527dbc7SCameron Grant 				return EBUSY;
911a527dbc7SCameron Grant 			}
912a527dbc7SCameron Grant 
913a527dbc7SCameron Grant 			snd_mtxlock(d->lock);
91467b1dce3SCameron Grant 			while (err == 0 && newcnt < cnt) {
91567b1dce3SCameron Grant 				SLIST_FOREACH(sce, &d->channels, link) {
91667b1dce3SCameron Grant 					c = sce->channel;
91767b1dce3SCameron Grant 					if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
91867b1dce3SCameron Grant 						goto remok;
91967b1dce3SCameron Grant 				}
920a527dbc7SCameron Grant 				snd_mtxunlock(d->lock);
921a527dbc7SCameron Grant 				pcm_inprog(d, -1);
92267b1dce3SCameron Grant 				return EINVAL;
92367b1dce3SCameron Grant remok:
92467b1dce3SCameron Grant 				err = vchan_destroy(c);
92567b1dce3SCameron Grant 				if (err == 0)
92667b1dce3SCameron Grant 					cnt--;
92767b1dce3SCameron Grant 			}
928a527dbc7SCameron Grant 			snd_mtxunlock(d->lock);
92967b1dce3SCameron Grant 		}
93067b1dce3SCameron Grant 	}
931a527dbc7SCameron Grant 	pcm_inprog(d, -1);
93267b1dce3SCameron Grant 	return err;
93367b1dce3SCameron Grant }
93467b1dce3SCameron Grant #endif
93567b1dce3SCameron Grant 
93667b1dce3SCameron Grant /************************************************************************/
93767b1dce3SCameron Grant 
93833dbf14aSCameron Grant static moduledata_t sndpcm_mod = {
93933dbf14aSCameron Grant 	"snd_pcm",
940d95502a8SCameron Grant 	NULL,
94133dbf14aSCameron Grant 	NULL
94233dbf14aSCameron Grant };
94333dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
94433dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER);
945