xref: /freebsd/sys/dev/sound/pcm/sound.c (revision a3193a9ca383c30f70f98a39cb55e19b382cf211)
1987e5972SCameron Grant /*
23f225978SCameron Grant  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
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>
305ee30e27SMathew Kanner #include <dev/sound/pcm/dsp.h>
317c438dbeSCameron Grant #include <sys/sysctl.h>
32285648f9SCameron Grant 
3367b1dce3SCameron Grant #include "feeder_if.h"
3467b1dce3SCameron Grant 
3567b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$");
3667b1dce3SCameron Grant 
37d95502a8SCameron Grant devclass_t pcm_devclass;
3882db23e2SCameron Grant 
39b8a36395SCameron Grant int pcm_veto_load = 1;
40b8a36395SCameron Grant 
4182db23e2SCameron Grant #ifdef USING_DEVFS
42d95502a8SCameron Grant int snd_unit = 0;
4309786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit);
4482db23e2SCameron Grant #endif
45cbe7d6a3SCameron Grant 
4667b1dce3SCameron Grant int snd_maxautovchans = 0;
4767b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
48987e5972SCameron Grant 
4982db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
5082db23e2SCameron Grant 
5167b1dce3SCameron Grant static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
5267b1dce3SCameron Grant 
5367b1dce3SCameron Grant struct sysctl_ctx_list *
5467b1dce3SCameron Grant snd_sysctl_tree(device_t dev)
5567b1dce3SCameron Grant {
5667b1dce3SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
5767b1dce3SCameron Grant 
5867b1dce3SCameron Grant 	return &d->sysctl_tree;
5967b1dce3SCameron Grant }
6067b1dce3SCameron Grant 
6167b1dce3SCameron Grant struct sysctl_oid *
6267b1dce3SCameron Grant snd_sysctl_tree_top(device_t dev)
6367b1dce3SCameron Grant {
6467b1dce3SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
6567b1dce3SCameron Grant 
6667b1dce3SCameron Grant 	return d->sysctl_tree_top;
6767b1dce3SCameron Grant }
6867b1dce3SCameron Grant 
6937209180SCameron Grant void *
702c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type)
7137209180SCameron Grant {
7237209180SCameron Grant #ifdef USING_MUTEX
7337209180SCameron Grant 	struct mtx *m;
7437209180SCameron Grant 
75a163d034SWarner Losh 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
7637209180SCameron Grant 	if (m == NULL)
7737209180SCameron Grant 		return NULL;
7812e524a2SDon Lewis 	mtx_init(m, desc, type, MTX_DEF);
7912e524a2SDon Lewis 	return m;
8012e524a2SDon Lewis #else
8112e524a2SDon Lewis 	return (void *)0xcafebabe;
8212e524a2SDon Lewis #endif
8312e524a2SDon Lewis }
8412e524a2SDon Lewis 
8537209180SCameron Grant void
8637209180SCameron Grant snd_mtxfree(void *m)
8737209180SCameron Grant {
8837209180SCameron Grant #ifdef USING_MUTEX
8937209180SCameron Grant 	struct mtx *mtx = m;
9037209180SCameron Grant 
9167beb5a5SCameron Grant 	/* mtx_assert(mtx, MA_OWNED); */
9237209180SCameron Grant 	mtx_destroy(mtx);
9337209180SCameron Grant 	free(mtx, M_DEVBUF);
9437209180SCameron Grant #endif
9537209180SCameron Grant }
9637209180SCameron Grant 
9737209180SCameron Grant void
9837209180SCameron Grant snd_mtxassert(void *m)
9937209180SCameron Grant {
10037209180SCameron Grant #ifdef USING_MUTEX
101f00f162aSCameron Grant #ifdef INVARIANTS
10237209180SCameron Grant 	struct mtx *mtx = m;
10337209180SCameron Grant 
10437209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
10537209180SCameron Grant #endif
106f00f162aSCameron Grant #endif
10737209180SCameron Grant }
10867beb5a5SCameron Grant /*
10937209180SCameron Grant void
11037209180SCameron Grant snd_mtxlock(void *m)
11137209180SCameron Grant {
11237209180SCameron Grant #ifdef USING_MUTEX
11337209180SCameron Grant 	struct mtx *mtx = m;
11437209180SCameron Grant 
11537209180SCameron Grant 	mtx_lock(mtx);
11637209180SCameron Grant #endif
11737209180SCameron Grant }
11837209180SCameron Grant 
11937209180SCameron Grant void
12037209180SCameron Grant snd_mtxunlock(void *m)
12137209180SCameron Grant {
12237209180SCameron Grant #ifdef USING_MUTEX
12337209180SCameron Grant 	struct mtx *mtx = m;
12437209180SCameron Grant 
12537209180SCameron Grant 	mtx_unlock(mtx);
12637209180SCameron Grant #endif
12737209180SCameron Grant }
12867beb5a5SCameron Grant */
12937209180SCameron Grant int
13037209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
13137209180SCameron Grant {
13237209180SCameron Grant #ifdef USING_MUTEX
13337209180SCameron Grant 	flags &= INTR_MPSAFE;
13446700f12SPeter Wemm 	flags |= INTR_TYPE_AV;
13537209180SCameron Grant #else
13646700f12SPeter Wemm 	flags = INTR_TYPE_AV;
13737209180SCameron Grant #endif
13837209180SCameron Grant 	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
13937209180SCameron Grant }
14037209180SCameron Grant 
141a527dbc7SCameron Grant #ifndef	PCM_DEBUG_MTX
14267b1dce3SCameron Grant void
14367b1dce3SCameron Grant pcm_lock(struct snddev_info *d)
14467b1dce3SCameron Grant {
14567b1dce3SCameron Grant 	snd_mtxlock(d->lock);
14667b1dce3SCameron Grant }
14767b1dce3SCameron Grant 
14867b1dce3SCameron Grant void
14967b1dce3SCameron Grant pcm_unlock(struct snddev_info *d)
15067b1dce3SCameron Grant {
15167b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
15267b1dce3SCameron Grant }
153a527dbc7SCameron Grant #endif
15467b1dce3SCameron Grant 
15567b1dce3SCameron Grant struct pcm_channel *
15667b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d)
15767b1dce3SCameron Grant {
15867b1dce3SCameron Grant 	return d->fakechan;
15967b1dce3SCameron Grant }
16067b1dce3SCameron Grant 
161b8f0d9e0SCameron Grant /* return a locked channel */
162285648f9SCameron Grant struct pcm_channel *
163506a5308SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
164285648f9SCameron Grant {
165285648f9SCameron Grant 	struct pcm_channel *c;
166285648f9SCameron Grant     	struct snddev_channel *sce;
167f637a36cSCameron Grant 	int err;
168285648f9SCameron Grant 
169b8f0d9e0SCameron Grant 	snd_mtxassert(d->lock);
170f637a36cSCameron Grant 
171f637a36cSCameron Grant 	/* scan for a free channel */
172285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
173285648f9SCameron Grant 		c = sce->channel;
17449c5e6e2SCameron Grant 		CHN_LOCK(c);
175285648f9SCameron Grant 		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
176506a5308SCameron Grant 			if (chnum == -1 || c->num == chnum) {
177285648f9SCameron Grant 				c->flags |= CHN_F_BUSY;
178b8f0d9e0SCameron Grant 				c->pid = pid;
179285648f9SCameron Grant 				return c;
180285648f9SCameron Grant 			}
181506a5308SCameron Grant 		}
18249c5e6e2SCameron Grant 		CHN_UNLOCK(c);
183285648f9SCameron Grant 	}
184f637a36cSCameron Grant 
185f637a36cSCameron Grant 	/* no channel available */
186f637a36cSCameron Grant 	if (direction == PCMDIR_PLAY) {
18767b1dce3SCameron Grant 		if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
188f637a36cSCameron Grant 			/* try to create a vchan */
189f637a36cSCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
190f637a36cSCameron Grant 				c = sce->channel;
19112e524a2SDon Lewis 				CHN_LOCK(c);
192f637a36cSCameron Grant 				if (!SLIST_EMPTY(&c->children)) {
193f637a36cSCameron Grant 					err = vchan_create(c);
19412e524a2SDon Lewis 					CHN_UNLOCK(c);
195f637a36cSCameron Grant 					if (!err)
196506a5308SCameron Grant 						return pcm_chnalloc(d, direction, pid, -1);
197f637a36cSCameron Grant 					else
198f637a36cSCameron Grant 						device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
19912e524a2SDon Lewis 				} else
20012e524a2SDon Lewis 					CHN_UNLOCK(c);
201f637a36cSCameron Grant 			}
202f637a36cSCameron Grant 		}
203f637a36cSCameron Grant 	}
204f637a36cSCameron Grant 
205285648f9SCameron Grant 	return NULL;
206285648f9SCameron Grant }
207285648f9SCameron Grant 
208b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
209285648f9SCameron Grant int
210b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
211285648f9SCameron Grant {
212b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
213285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
214b8f0d9e0SCameron Grant 	c->pid = -1;
21549c5e6e2SCameron Grant 	CHN_UNLOCK(c);
216285648f9SCameron Grant 	return 0;
217285648f9SCameron Grant }
218285648f9SCameron Grant 
219285648f9SCameron Grant int
220285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
221285648f9SCameron Grant {
22249c5e6e2SCameron Grant 	int r;
22349c5e6e2SCameron Grant 
224b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
225285648f9SCameron Grant 	c->refcount += ref;
22649c5e6e2SCameron Grant 	r = c->refcount;
22749c5e6e2SCameron Grant 	return r;
228285648f9SCameron Grant }
229285648f9SCameron Grant 
23067b1dce3SCameron Grant int
23167b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta)
23267b1dce3SCameron Grant {
233a527dbc7SCameron Grant 	int r;
234a527dbc7SCameron Grant 
235a527dbc7SCameron Grant 	if (delta == 0)
23667b1dce3SCameron Grant 		return d->inprog;
237a527dbc7SCameron Grant 
238a527dbc7SCameron Grant 	/* backtrace(); */
239a527dbc7SCameron Grant 	pcm_lock(d);
240a527dbc7SCameron Grant 	d->inprog += delta;
241a527dbc7SCameron Grant 	r = d->inprog;
242a527dbc7SCameron Grant 	pcm_unlock(d);
243a527dbc7SCameron Grant 	return r;
24467b1dce3SCameron Grant }
24567b1dce3SCameron Grant 
24667b1dce3SCameron Grant static void
24767b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num)
24867b1dce3SCameron Grant {
24967b1dce3SCameron Grant 	struct pcm_channel *c;
25067b1dce3SCameron Grant     	struct snddev_channel *sce;
25167b1dce3SCameron Grant 	int err, done;
25267b1dce3SCameron Grant 
25367b1dce3SCameron Grant 	if (num > 0 && d->vchancount == 0) {
25467b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
25567b1dce3SCameron Grant 			c = sce->channel;
25612e524a2SDon Lewis 			CHN_LOCK(c);
25767b1dce3SCameron Grant 			if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
25867b1dce3SCameron Grant 				c->flags |= CHN_F_BUSY;
25967b1dce3SCameron Grant 				err = vchan_create(c);
26067b1dce3SCameron Grant 				if (err) {
26167b1dce3SCameron Grant 					c->flags &= ~CHN_F_BUSY;
26212e524a2SDon Lewis 					CHN_UNLOCK(c);
26312e524a2SDon Lewis 					device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
26412e524a2SDon Lewis 				} else
26512e524a2SDon Lewis 					CHN_UNLOCK(c);
26667b1dce3SCameron Grant 				return;
26767b1dce3SCameron Grant 			}
26812e524a2SDon Lewis 			CHN_UNLOCK(c);
26967b1dce3SCameron Grant 		}
27067b1dce3SCameron Grant 	}
27167b1dce3SCameron Grant 	if (num == 0 && d->vchancount > 0) {
27267b1dce3SCameron Grant 		done = 0;
27367b1dce3SCameron Grant 		while (!done) {
27467b1dce3SCameron Grant 			done = 1;
27567b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
27667b1dce3SCameron Grant 				c = sce->channel;
27767b1dce3SCameron Grant 				if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
27867b1dce3SCameron Grant 					done = 0;
279a527dbc7SCameron Grant 					snd_mtxlock(d->lock);
28067b1dce3SCameron Grant 					err = vchan_destroy(c);
281a527dbc7SCameron Grant 					snd_mtxunlock(d->lock);
28267b1dce3SCameron Grant 					if (err)
28367b1dce3SCameron Grant 						device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
2849cfd8eb3SPeter Wemm 					break;		/* restart */
28567b1dce3SCameron Grant 				}
28667b1dce3SCameron Grant 			}
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) {
32312e524a2SDon Lewis 		if (v < 0 || v >= SND_MAXVCHANS || pcm_devclass == NULL)
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;
345a3193a9cSDon Lewis     	int direction, err, *pnum;
346987e5972SCameron Grant 
347285648f9SCameron Grant 	switch(dir) {
348285648f9SCameron Grant 	case PCMDIR_PLAY:
349285648f9SCameron Grant 		dirs = "play";
350a3193a9cSDon Lewis 		direction = PCMDIR_PLAY;
351a67fe5c1SCameron Grant 		pnum = &d->playcount;
352285648f9SCameron Grant 		break;
353a67fe5c1SCameron Grant 
354285648f9SCameron Grant 	case PCMDIR_REC:
355285648f9SCameron Grant 		dirs = "record";
356a3193a9cSDon Lewis 		direction = PCMDIR_REC;
357a67fe5c1SCameron Grant 		pnum = &d->reccount;
358285648f9SCameron Grant 		break;
359a67fe5c1SCameron Grant 
360285648f9SCameron Grant 	case PCMDIR_VIRTUAL:
361285648f9SCameron Grant 		dirs = "virtual";
362a3193a9cSDon Lewis 		direction = PCMDIR_PLAY;
363a67fe5c1SCameron Grant 		pnum = &d->vchancount;
364285648f9SCameron Grant 		break;
365a67fe5c1SCameron Grant 
366285648f9SCameron Grant 	default:
367285648f9SCameron Grant 		return NULL;
3689c326820SCameron Grant 	}
369285648f9SCameron Grant 
370a163d034SWarner Losh 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
371285648f9SCameron Grant 	if (!ch)
372285648f9SCameron Grant 		return NULL;
373285648f9SCameron Grant 
374a163d034SWarner Losh 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
375285648f9SCameron Grant 	if (!ch->methods) {
376285648f9SCameron Grant 		free(ch, M_DEVBUF);
377a67fe5c1SCameron Grant 
378285648f9SCameron Grant 		return NULL;
379285648f9SCameron Grant 	}
380285648f9SCameron Grant 
38167beb5a5SCameron Grant 	snd_mtxlock(d->lock);
382a67fe5c1SCameron Grant 	ch->num = (*pnum)++;
38367beb5a5SCameron Grant 	snd_mtxunlock(d->lock);
384a67fe5c1SCameron Grant 
385285648f9SCameron Grant 	ch->pid = -1;
386285648f9SCameron Grant 	ch->parentsnddev = d;
387285648f9SCameron Grant 	ch->parentchannel = parent;
388436c9b65SScott Long 	ch->dev = d->dev;
38967beb5a5SCameron Grant 	snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
390285648f9SCameron Grant 
391a3193a9cSDon Lewis 	err = chn_init(ch, devinfo, dir, direction);
3920f55ac6cSCameron Grant 	if (err) {
393a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
394285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
395285648f9SCameron Grant 		free(ch, M_DEVBUF);
39667beb5a5SCameron Grant 		snd_mtxlock(d->lock);
397a67fe5c1SCameron Grant 		(*pnum)--;
39867beb5a5SCameron Grant 		snd_mtxunlock(d->lock);
399a67fe5c1SCameron Grant 
400285648f9SCameron Grant 		return NULL;
401bbb5bf3dSCameron Grant 	}
402285648f9SCameron Grant 
403285648f9SCameron Grant 	return ch;
404285648f9SCameron Grant }
405285648f9SCameron Grant 
406285648f9SCameron Grant int
407285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
408285648f9SCameron Grant {
409a67fe5c1SCameron Grant 	struct snddev_info *d;
410285648f9SCameron Grant 	int err;
411285648f9SCameron Grant 
412a67fe5c1SCameron Grant 	d = ch->parentsnddev;
413285648f9SCameron Grant 	err = chn_kill(ch);
414285648f9SCameron Grant 	if (err) {
415a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
416285648f9SCameron Grant 		return err;
417285648f9SCameron Grant 	}
418285648f9SCameron Grant 
419285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
420285648f9SCameron Grant 	free(ch, M_DEVBUF);
421285648f9SCameron Grant 
422285648f9SCameron Grant 	return 0;
423285648f9SCameron Grant }
424285648f9SCameron Grant 
425285648f9SCameron Grant int
4265ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
427285648f9SCameron Grant {
42867b1dce3SCameron Grant     	struct snddev_channel *sce, *tmp, *after;
4295ee30e27SMathew Kanner     	int device = device_get_unit(d->dev);
4305ee30e27SMathew Kanner 
4315ee30e27SMathew Kanner 	/*
4325ee30e27SMathew Kanner 	 * Note it's confusing nomenclature.
4335ee30e27SMathew Kanner 	 * dev_t
4345ee30e27SMathew Kanner 	 * device -> pcm_device
4355ee30e27SMathew Kanner          * unit -> pcm_channel
4365ee30e27SMathew Kanner 	 * channel -> snddev_channel
4375ee30e27SMathew Kanner 	 * device_t
4385ee30e27SMathew Kanner 	 * unit -> pcm_device
4395ee30e27SMathew Kanner 	 */
440b8f0d9e0SCameron Grant 
441a163d034SWarner Losh 	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
442285648f9SCameron Grant 	if (!sce) {
443285648f9SCameron Grant 		return ENOMEM;
444285648f9SCameron Grant 	}
445285648f9SCameron Grant 
4467cf0e77aSOrion Hodson 	snd_mtxlock(d->lock);
447285648f9SCameron Grant 	sce->channel = ch;
4485ee30e27SMathew Kanner 	sce->chan_num= d->devcount++;
44967b1dce3SCameron Grant 	if (SLIST_EMPTY(&d->channels)) {
450285648f9SCameron Grant 		SLIST_INSERT_HEAD(&d->channels, sce, link);
45167b1dce3SCameron Grant 	} else {
45267b1dce3SCameron Grant 		after = NULL;
45367b1dce3SCameron Grant 		SLIST_FOREACH(tmp, &d->channels, link) {
45467b1dce3SCameron Grant 			after = tmp;
45567b1dce3SCameron Grant 		}
45667b1dce3SCameron Grant 		SLIST_INSERT_AFTER(after, sce, link);
45767b1dce3SCameron Grant 	}
45867beb5a5SCameron Grant 	snd_mtxunlock(d->lock);
4595ee30e27SMathew Kanner 	sce->dsp_devt= make_dev(&dsp_cdevsw,
4605ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
4615ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
4625ee30e27SMathew Kanner 			device, sce->chan_num);
463285648f9SCameron Grant 
4645ee30e27SMathew Kanner 	sce->dspW_devt= make_dev(&dsp_cdevsw,
4655ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
4665ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
4675ee30e27SMathew Kanner 			device, sce->chan_num);
4685ee30e27SMathew Kanner 
4695ee30e27SMathew Kanner 	sce->audio_devt= make_dev(&dsp_cdevsw,
4705ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
4715ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
4725ee30e27SMathew Kanner 			device, sce->chan_num);
4735ee30e27SMathew Kanner 
474506a5308SCameron Grant 	if (ch->direction == PCMDIR_REC)
4755ee30e27SMathew Kanner 		sce->dspr_devt = make_dev(&dsp_cdevsw,
4765ee30e27SMathew Kanner 				PCMMKMINOR(device, SND_DEV_DSPREC,
4775ee30e27SMathew Kanner 					sce->chan_num), UID_ROOT, GID_WHEEL,
4785ee30e27SMathew Kanner 				0666, "dspr%d.%d", device, sce->chan_num);
479b8f0d9e0SCameron Grant 
48033dbf14aSCameron Grant 	return 0;
48133dbf14aSCameron Grant }
48233dbf14aSCameron Grant 
483285648f9SCameron Grant int
4845ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
48533dbf14aSCameron Grant {
486285648f9SCameron Grant     	struct snddev_channel *sce;
48745550658SPoul-Henning Kamp #if 0
488a527dbc7SCameron Grant 	int ourlock;
48933dbf14aSCameron Grant 
490a527dbc7SCameron Grant 	ourlock = 0;
491a527dbc7SCameron Grant 	if (!mtx_owned(d->lock)) {
49249c5e6e2SCameron Grant 		snd_mtxlock(d->lock);
493a527dbc7SCameron Grant 		ourlock = 1;
494a527dbc7SCameron Grant 	}
49545550658SPoul-Henning Kamp #endif
496a527dbc7SCameron Grant 
497285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
498285648f9SCameron Grant 		if (sce->channel == ch)
499285648f9SCameron Grant 			goto gotit;
50033dbf14aSCameron Grant 	}
50145550658SPoul-Henning Kamp #if 0
502a527dbc7SCameron Grant 	if (ourlock)
50349c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
50445550658SPoul-Henning Kamp #endif
505285648f9SCameron Grant 	return EINVAL;
506285648f9SCameron Grant gotit:
507285648f9SCameron Grant 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
50867beb5a5SCameron Grant 
50967beb5a5SCameron Grant     	if (ch->direction == PCMDIR_REC)
51067beb5a5SCameron Grant 		d->reccount--;
51167beb5a5SCameron Grant 	else if (ch->flags & CHN_F_VIRTUAL)
51267beb5a5SCameron Grant 		d->vchancount--;
51367beb5a5SCameron Grant 	else
51467beb5a5SCameron Grant 		d->playcount--;
51567beb5a5SCameron Grant 
51645550658SPoul-Henning Kamp #if 0
517a527dbc7SCameron Grant 	if (ourlock)
51849c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
51945550658SPoul-Henning Kamp #endif
52067beb5a5SCameron Grant 	free(sce, M_DEVBUF);
521285648f9SCameron Grant 
522987e5972SCameron Grant 	return 0;
523987e5972SCameron Grant }
524987e5972SCameron Grant 
525987e5972SCameron Grant int
526285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
527285648f9SCameron Grant {
528285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
52967b1dce3SCameron Grant 	struct pcm_channel *ch;
53067b1dce3SCameron Grant     	int err;
531285648f9SCameron Grant 
532285648f9SCameron Grant 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
533285648f9SCameron Grant 	if (!ch) {
534285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
535285648f9SCameron Grant 		return ENODEV;
536285648f9SCameron Grant 	}
537cd9766c5SCameron Grant 
5385ee30e27SMathew Kanner 	err = pcm_chn_add(d, ch);
539285648f9SCameron Grant 	if (err) {
540285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
541285648f9SCameron Grant 		pcm_chn_destroy(ch);
542cd9766c5SCameron Grant 		return err;
543cd9766c5SCameron Grant 	}
544cd9766c5SCameron Grant 
54512e524a2SDon Lewis 	CHN_LOCK(ch);
54661698e0cSAlexander Kabaev 	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
54761698e0cSAlexander Kabaev 	    ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
548cd9766c5SCameron Grant 		ch->flags |= CHN_F_BUSY;
549cd9766c5SCameron Grant 		err = vchan_create(ch);
550cd9766c5SCameron Grant 		if (err) {
55167b1dce3SCameron Grant 			ch->flags &= ~CHN_F_BUSY;
55212e524a2SDon Lewis 			CHN_UNLOCK(ch);
55312e524a2SDon Lewis 			device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
55412e524a2SDon Lewis 			return err;
555cd9766c5SCameron Grant 		}
556285648f9SCameron Grant 	}
55712e524a2SDon Lewis 	CHN_UNLOCK(ch);
558285648f9SCameron Grant 
559285648f9SCameron Grant 	return err;
560285648f9SCameron Grant }
561285648f9SCameron Grant 
562285648f9SCameron Grant static int
563285648f9SCameron Grant pcm_killchan(device_t dev)
564285648f9SCameron Grant {
565285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
566285648f9SCameron Grant     	struct snddev_channel *sce;
567e33bee07SOlivier Houchard 	struct pcm_channel *ch;
568e33bee07SOlivier Houchard 	int error = 0;
569285648f9SCameron Grant 
570285648f9SCameron Grant 	sce = SLIST_FIRST(&d->channels);
571e33bee07SOlivier Houchard 	ch = sce->channel;
572285648f9SCameron Grant 
5735ee30e27SMathew Kanner 	error = pcm_chn_remove(d, sce->channel);
574e33bee07SOlivier Houchard 	if (error)
575e33bee07SOlivier Houchard 		return (error);
576e33bee07SOlivier Houchard 	return (pcm_chn_destroy(ch));
577285648f9SCameron Grant }
578285648f9SCameron Grant 
579285648f9SCameron Grant int
580987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
581987e5972SCameron Grant {
58266ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
58349c5e6e2SCameron Grant 
58449c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
585987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
58649c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
587987e5972SCameron Grant 	return 0;
588987e5972SCameron Grant }
589987e5972SCameron Grant 
590987e5972SCameron Grant u_int32_t
591987e5972SCameron Grant pcm_getflags(device_t dev)
592987e5972SCameron Grant {
59366ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
59449c5e6e2SCameron Grant 
595987e5972SCameron Grant 	return d->flags;
596987e5972SCameron Grant }
597987e5972SCameron Grant 
598987e5972SCameron Grant void
599987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
600987e5972SCameron Grant {
60166ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
602d95502a8SCameron Grant 
603987e5972SCameron Grant 	d->flags = val;
604987e5972SCameron Grant }
605987e5972SCameron Grant 
60639004e69SCameron Grant void *
60739004e69SCameron Grant pcm_getdevinfo(device_t dev)
60839004e69SCameron Grant {
60966ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
61049c5e6e2SCameron Grant 
61139004e69SCameron Grant 	return d->devinfo;
61239004e69SCameron Grant }
61339004e69SCameron Grant 
614a67fe5c1SCameron Grant unsigned int
615a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
616a67fe5c1SCameron Grant {
617a67fe5c1SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
6184e60be34SCameron Grant 	int sz, x;
619a67fe5c1SCameron Grant 
620a67fe5c1SCameron Grant 	sz = 0;
6214e60be34SCameron Grant 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
6224e60be34SCameron Grant 		x = sz;
623a67fe5c1SCameron Grant 		RANGE(sz, min, max);
6244e60be34SCameron Grant 		if (x != sz)
6254e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
6264e60be34SCameron Grant 		x = min;
6274e60be34SCameron Grant 		while (x < sz)
6284e60be34SCameron Grant 			x <<= 1;
6294e60be34SCameron Grant 		if (x > sz)
6304e60be34SCameron Grant 			x >>= 1;
6314e60be34SCameron Grant 		if (x != sz) {
6324e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
6334e60be34SCameron Grant 			sz = x;
6344e60be34SCameron Grant 		}
6354e60be34SCameron Grant 	} else {
636a67fe5c1SCameron Grant 		sz = deflt;
6374e60be34SCameron Grant 	}
6384e60be34SCameron Grant 
639a67fe5c1SCameron Grant 	d->bufsz = sz;
640a67fe5c1SCameron Grant 
641a67fe5c1SCameron Grant 	return sz;
642a67fe5c1SCameron Grant }
643a67fe5c1SCameron Grant 
644987e5972SCameron Grant int
645987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
646987e5972SCameron Grant {
64766ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
648987e5972SCameron Grant 
649b8a36395SCameron Grant 	if (pcm_veto_load) {
650b8a36395SCameron Grant 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
651b8a36395SCameron Grant 
652b8a36395SCameron Grant 		return EINVAL;
653b8a36395SCameron Grant 	}
654b8a36395SCameron Grant 
6552c69ba87SJohn Baldwin 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
656285648f9SCameron Grant 
657cd9766c5SCameron Grant 	d->flags = 0;
658e4d5b250SCameron Grant 	d->dev = dev;
659987e5972SCameron Grant 	d->devinfo = devinfo;
660f637a36cSCameron Grant 	d->devcount = 0;
661506a5308SCameron Grant 	d->reccount = 0;
662a67fe5c1SCameron Grant 	d->playcount = 0;
663f637a36cSCameron Grant 	d->vchancount = 0;
664d95502a8SCameron Grant 	d->inprog = 0;
665833f7023SCameron Grant 
66645550658SPoul-Henning Kamp 	SLIST_INIT(&d->channels);
66745550658SPoul-Henning Kamp 	SLIST_INIT(&d->channels);
66845550658SPoul-Henning Kamp 
669d95502a8SCameron Grant 	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
670285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
671d95502a8SCameron Grant 
672285648f9SCameron Grant 	d->fakechan = fkchan_setup(dev);
673a3193a9cSDon Lewis 	chn_init(d->fakechan, NULL, 0, 0);
674987e5972SCameron Grant 
67582db23e2SCameron Grant #ifdef SND_DYNSYSCTL
676cc486d80SJohn Baldwin 	sysctl_ctx_init(&d->sysctl_tree);
677cc486d80SJohn Baldwin 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
678a3e893e0SJohn Baldwin 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
679cc486d80SJohn Baldwin 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
680a3e893e0SJohn Baldwin 	if (d->sysctl_tree_top == NULL) {
681cc486d80SJohn Baldwin 		sysctl_ctx_free(&d->sysctl_tree);
682cc486d80SJohn Baldwin 		goto no;
683cc486d80SJohn Baldwin 	}
684a67fe5c1SCameron Grant 	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
685a67fe5c1SCameron Grant             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
68682db23e2SCameron Grant #endif
687b8f0d9e0SCameron Grant 	if (numplay > 0)
68867b1dce3SCameron Grant 		vchan_initsys(dev);
689cd9766c5SCameron Grant 	if (numplay == 1)
690cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
691cd9766c5SCameron Grant 
69267b1dce3SCameron Grant 	sndstat_register(dev, d->status, sndstat_prepare_pcm);
693987e5972SCameron Grant     	return 0;
694987e5972SCameron Grant no:
69549c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
696987e5972SCameron Grant 	return ENXIO;
697987e5972SCameron Grant }
698987e5972SCameron Grant 
69933dbf14aSCameron Grant int
70033dbf14aSCameron Grant pcm_unregister(device_t dev)
7017c438dbeSCameron Grant {
70266ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
703285648f9SCameron Grant     	struct snddev_channel *sce;
704a67fe5c1SCameron Grant 	struct pcm_channel *ch;
7057c438dbeSCameron Grant 
70649c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
707d95502a8SCameron Grant 	if (d->inprog) {
7085c25132aSGeorge C A Reid 		device_printf(dev, "unregister: operation in progress\n");
7095c25132aSGeorge C A Reid 		snd_mtxunlock(d->lock);
7105c25132aSGeorge C A Reid 		return EBUSY;
7115c25132aSGeorge C A Reid 	}
7125c25132aSGeorge C A Reid 	if (sndstat_busy() != 0) {
7135c25132aSGeorge C A Reid 		device_printf(dev, "unregister: sndstat busy\n");
714d95502a8SCameron Grant 		snd_mtxunlock(d->lock);
715d95502a8SCameron Grant 		return EBUSY;
716d95502a8SCameron Grant 	}
7175ee30e27SMathew Kanner 
7185ee30e27SMathew Kanner 
719285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
720a67fe5c1SCameron Grant 		ch = sce->channel;
721a67fe5c1SCameron Grant 		if (ch->refcount > 0) {
72221ed9908SCameron Grant 			device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
72349c5e6e2SCameron Grant 			snd_mtxunlock(d->lock);
724285648f9SCameron Grant 			return EBUSY;
725285648f9SCameron Grant 		}
726c9b53085SCameron Grant 	}
7275ee30e27SMathew Kanner 
7285ee30e27SMathew Kanner 	SLIST_FOREACH(sce, &d->channels, link) {
7295ee30e27SMathew Kanner 		destroy_dev(sce->dsp_devt);
7305ee30e27SMathew Kanner 		destroy_dev(sce->dspW_devt);
7315ee30e27SMathew Kanner 		destroy_dev(sce->audio_devt);
7325ee30e27SMathew Kanner 		if (sce->dspr_devt)
7335ee30e27SMathew Kanner 			destroy_dev(sce->dspr_devt);
7345ee30e27SMathew Kanner 	}
7355ee30e27SMathew Kanner 
736d95502a8SCameron Grant 	if (mixer_uninit(dev)) {
7375c25132aSGeorge C A Reid 		device_printf(dev, "unregister: mixer busy\n");
73849c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
739c9b53085SCameron Grant 		return EBUSY;
740c9b53085SCameron Grant 	}
7417c438dbeSCameron Grant 
74266ef8af5SCameron Grant #ifdef SND_DYNSYSCTL
74366ef8af5SCameron Grant 	d->sysctl_tree_top = NULL;
74466ef8af5SCameron Grant 	sysctl_ctx_free(&d->sysctl_tree);
74566ef8af5SCameron Grant #endif
746faeebea2SCameron Grant 	while (!SLIST_EMPTY(&d->channels))
747285648f9SCameron Grant 		pcm_killchan(dev);
7487c438dbeSCameron Grant 
74966ef8af5SCameron Grant 	chn_kill(d->fakechan);
75066ef8af5SCameron Grant 	fkchan_kill(d->fakechan);
75182db23e2SCameron Grant 
75247172de8SNick Sayer 	sndstat_unregister(dev);
75345550658SPoul-Henning Kamp 	snd_mtxunlock(d->lock);
75449c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
75533dbf14aSCameron Grant 	return 0;
75633dbf14aSCameron Grant }
7577c438dbeSCameron Grant 
75867b1dce3SCameron Grant /************************************************************************/
75967b1dce3SCameron Grant 
76067b1dce3SCameron Grant static int
76167b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
76267b1dce3SCameron Grant {
76367b1dce3SCameron Grant     	struct snddev_info *d;
76467b1dce3SCameron Grant     	struct snddev_channel *sce;
76567b1dce3SCameron Grant 	struct pcm_channel *c;
76667b1dce3SCameron Grant 	struct pcm_feeder *f;
76767b1dce3SCameron Grant     	int pc, rc, vc;
76867b1dce3SCameron Grant 
76967b1dce3SCameron Grant 	if (verbose < 1)
77067b1dce3SCameron Grant 		return 0;
77167b1dce3SCameron Grant 
77267b1dce3SCameron Grant 	d = device_get_softc(dev);
77367b1dce3SCameron Grant 	if (!d)
77467b1dce3SCameron Grant 		return ENXIO;
77567b1dce3SCameron Grant 
77667b1dce3SCameron Grant 	snd_mtxlock(d->lock);
77767b1dce3SCameron Grant 	if (!SLIST_EMPTY(&d->channels)) {
77867b1dce3SCameron Grant 		pc = rc = vc = 0;
77967b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
78067b1dce3SCameron Grant 			c = sce->channel;
78167b1dce3SCameron Grant 			if (c->direction == PCMDIR_PLAY) {
78267b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
78367b1dce3SCameron Grant 					vc++;
78467b1dce3SCameron Grant 				else
78567b1dce3SCameron Grant 					pc++;
78667b1dce3SCameron Grant 			} else
78767b1dce3SCameron Grant 				rc++;
78867b1dce3SCameron Grant 		}
789a67fe5c1SCameron Grant 		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
79067b1dce3SCameron Grant 				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
79167b1dce3SCameron Grant #ifdef USING_DEVFS
79267b1dce3SCameron Grant 				(device_get_unit(dev) == snd_unit)? " default" : ""
79367b1dce3SCameron Grant #else
79467b1dce3SCameron Grant 				""
79567b1dce3SCameron Grant #endif
79667b1dce3SCameron Grant 				);
797a527dbc7SCameron Grant 
798a527dbc7SCameron Grant 		if (verbose <= 1) {
799a527dbc7SCameron Grant 			snd_mtxunlock(d->lock);
800a527dbc7SCameron Grant 			return 0;
801a527dbc7SCameron Grant 		}
802a527dbc7SCameron Grant 
80367b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
80467b1dce3SCameron Grant 			c = sce->channel;
805a3285889SCameron Grant 			sbuf_printf(s, "\n\t");
806a3285889SCameron Grant 
80745550658SPoul-Henning Kamp 			/* it would be better to indent child channels */
808a3285889SCameron Grant 			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
8094c68642aSCameron Grant 			sbuf_printf(s, "spd %d", c->speed);
8104c68642aSCameron Grant 			if (c->speed != sndbuf_getspd(c->bufhard))
8114c68642aSCameron Grant 				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
8124c68642aSCameron Grant 			sbuf_printf(s, ", fmt 0x%08x", c->format);
8134c68642aSCameron Grant 			if (c->format != sndbuf_getfmt(c->bufhard))
8144c68642aSCameron Grant 				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
815a527dbc7SCameron Grant 			sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
81667b1dce3SCameron Grant 			if (c->pid != -1)
81767b1dce3SCameron Grant 				sbuf_printf(s, ", pid %d", c->pid);
81867b1dce3SCameron Grant 			sbuf_printf(s, "\n\t");
8194c68642aSCameron Grant 
820edecdda7SCameron Grant 			if (c->bufhard != NULL && c->bufsoft != NULL) {
821a3285889SCameron Grant 				sbuf_printf(s, "interrupts %d, ", c->interrupts);
822a3285889SCameron Grant 				if (c->direction == PCMDIR_REC)
823a3285889SCameron Grant 					sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
824a3285889SCameron Grant 						c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
825a3285889SCameron Grant 				else
826edecdda7SCameron Grant 					sbuf_printf(s, "underruns %d, ready %d",
827edecdda7SCameron Grant 						c->xruns, sndbuf_getready(c->bufsoft));
828a3285889SCameron Grant 				sbuf_printf(s, "\n\t");
829a3285889SCameron Grant 			}
8304c68642aSCameron Grant 
8314c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
8324c68642aSCameron Grant 			sbuf_printf(s, " -> ");
83367b1dce3SCameron Grant 			f = c->feeder;
8344c68642aSCameron Grant 			while (f->source != NULL)
8354c68642aSCameron Grant 				f = f->source;
8364c68642aSCameron Grant 			while (f != NULL) {
83767b1dce3SCameron Grant 				sbuf_printf(s, "%s", f->class->name);
83867b1dce3SCameron Grant 				if (f->desc->type == FEEDER_FMT)
8394c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
84067b1dce3SCameron Grant 				if (f->desc->type == FEEDER_RATE)
8414c68642aSCameron Grant 					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
84267b1dce3SCameron Grant 				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
8434c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x)", f->desc->out);
8444c68642aSCameron Grant 				sbuf_printf(s, " -> ");
8454c68642aSCameron Grant 				f = f->parent;
84667b1dce3SCameron Grant 			}
8474c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
84867b1dce3SCameron Grant 		}
84967b1dce3SCameron Grant 	} else
85067b1dce3SCameron Grant 		sbuf_printf(s, " (mixer only)");
85167b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
85267b1dce3SCameron Grant 
85367b1dce3SCameron Grant 	return 0;
85467b1dce3SCameron Grant }
85567b1dce3SCameron Grant 
85667b1dce3SCameron Grant /************************************************************************/
85767b1dce3SCameron Grant 
85867b1dce3SCameron Grant #ifdef SND_DYNSYSCTL
85967b1dce3SCameron Grant int
86067b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
86167b1dce3SCameron Grant {
86267b1dce3SCameron Grant 	struct snddev_info *d;
86367b1dce3SCameron Grant     	struct snddev_channel *sce;
86467b1dce3SCameron Grant 	struct pcm_channel *c;
865a527dbc7SCameron Grant 	int err, newcnt, cnt, busy;
866a527dbc7SCameron Grant 	int x;
86767b1dce3SCameron Grant 
86867b1dce3SCameron Grant 	d = oidp->oid_arg1;
86967b1dce3SCameron Grant 
870a527dbc7SCameron Grant 	x = pcm_inprog(d, 1);
871a527dbc7SCameron Grant 	if (x != 1) {
872a527dbc7SCameron Grant 		printf("x: %d\n", x);
873a527dbc7SCameron Grant 		pcm_inprog(d, -1);
874a527dbc7SCameron Grant 		return EINPROGRESS;
875a527dbc7SCameron Grant 	}
876a527dbc7SCameron Grant 
877a527dbc7SCameron Grant 	busy = 0;
87867b1dce3SCameron Grant 	cnt = 0;
87967b1dce3SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
88067b1dce3SCameron Grant 		c = sce->channel;
88112e524a2SDon Lewis 		CHN_LOCK(c);
882a527dbc7SCameron Grant 		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
88367b1dce3SCameron Grant 			cnt++;
884a527dbc7SCameron Grant 			if (c->flags & CHN_F_BUSY)
885a527dbc7SCameron Grant 				busy++;
88667b1dce3SCameron Grant 		}
88712e524a2SDon Lewis 		CHN_UNLOCK(c);
888a527dbc7SCameron Grant 	}
889a527dbc7SCameron Grant 
89067b1dce3SCameron Grant 	newcnt = cnt;
89167b1dce3SCameron Grant 
89267b1dce3SCameron Grant 	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
893a527dbc7SCameron Grant 
89467b1dce3SCameron Grant 	if (err == 0 && req->newptr != NULL) {
895a527dbc7SCameron Grant 
89667b1dce3SCameron Grant 		if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
897a527dbc7SCameron Grant 			pcm_inprog(d, -1);
898a527dbc7SCameron Grant 			return E2BIG;
89967b1dce3SCameron Grant 		}
90067b1dce3SCameron Grant 
90167b1dce3SCameron Grant 		if (newcnt > cnt) {
90267b1dce3SCameron Grant 			/* add new vchans - find a parent channel first */
90367b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
90467b1dce3SCameron Grant 				c = sce->channel;
90512e524a2SDon Lewis 				CHN_LOCK(c);
90667b1dce3SCameron Grant 				/* not a candidate if not a play channel */
90767b1dce3SCameron Grant 				if (c->direction != PCMDIR_PLAY)
90812e524a2SDon Lewis 					goto next;
90967b1dce3SCameron Grant 				/* not a candidate if a virtual channel */
91067b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
91112e524a2SDon Lewis 					goto next;
91267b1dce3SCameron Grant 				/* not a candidate if it's in use */
91312e524a2SDon Lewis 				if (!(c->flags & CHN_F_BUSY) ||
91412e524a2SDon Lewis 				    !(SLIST_EMPTY(&c->children)))
91567b1dce3SCameron Grant 					/*
91612e524a2SDon Lewis 					 * if we get here we're a nonvirtual
91712e524a2SDon Lewis 					 * play channel, and either
91867b1dce3SCameron Grant 					 * 1) not busy
91912e524a2SDon Lewis 					 * 2) busy with children, not directly
92012e524a2SDon Lewis 					 *    open
92167b1dce3SCameron Grant 					 *
92267b1dce3SCameron Grant 					 * thus we can add children
92367b1dce3SCameron Grant 					 */
92467b1dce3SCameron Grant 					goto addok;
92512e524a2SDon Lewis next:
92612e524a2SDon Lewis 				CHN_UNLOCK(c);
92767b1dce3SCameron Grant 			}
928a527dbc7SCameron Grant 			pcm_inprog(d, -1);
92967b1dce3SCameron Grant 			return EBUSY;
93067b1dce3SCameron Grant addok:
93167b1dce3SCameron Grant 			c->flags |= CHN_F_BUSY;
93267b1dce3SCameron Grant 			while (err == 0 && newcnt > cnt) {
93367b1dce3SCameron Grant 				err = vchan_create(c);
93467b1dce3SCameron Grant 				if (err == 0)
93567b1dce3SCameron Grant 					cnt++;
93667b1dce3SCameron Grant 			}
93767b1dce3SCameron Grant 			if (SLIST_EMPTY(&c->children))
93867b1dce3SCameron Grant 				c->flags &= ~CHN_F_BUSY;
93912e524a2SDon Lewis 			CHN_UNLOCK(c);
94067b1dce3SCameron Grant 		} else if (newcnt < cnt) {
941a527dbc7SCameron Grant 			if (busy > newcnt) {
942a527dbc7SCameron Grant 				printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy);
943a527dbc7SCameron Grant 				pcm_inprog(d, -1);
944a527dbc7SCameron Grant 				return EBUSY;
945a527dbc7SCameron Grant 			}
946a527dbc7SCameron Grant 
947a527dbc7SCameron Grant 			snd_mtxlock(d->lock);
94867b1dce3SCameron Grant 			while (err == 0 && newcnt < cnt) {
94967b1dce3SCameron Grant 				SLIST_FOREACH(sce, &d->channels, link) {
95067b1dce3SCameron Grant 					c = sce->channel;
95112e524a2SDon Lewis 					CHN_LOCK(c);
95267b1dce3SCameron Grant 					if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
95367b1dce3SCameron Grant 						goto remok;
95412e524a2SDon Lewis 
95512e524a2SDon Lewis 					CHN_UNLOCK(c);
95667b1dce3SCameron Grant 				}
957a527dbc7SCameron Grant 				snd_mtxunlock(d->lock);
958a527dbc7SCameron Grant 				pcm_inprog(d, -1);
95967b1dce3SCameron Grant 				return EINVAL;
96067b1dce3SCameron Grant remok:
96112e524a2SDon Lewis 				CHN_UNLOCK(c);
96267b1dce3SCameron Grant 				err = vchan_destroy(c);
96367b1dce3SCameron Grant 				if (err == 0)
96467b1dce3SCameron Grant 					cnt--;
96567b1dce3SCameron Grant 			}
966a527dbc7SCameron Grant 			snd_mtxunlock(d->lock);
96767b1dce3SCameron Grant 		}
96867b1dce3SCameron Grant 	}
969a527dbc7SCameron Grant 	pcm_inprog(d, -1);
97067b1dce3SCameron Grant 	return err;
97167b1dce3SCameron Grant }
97267b1dce3SCameron Grant #endif
97367b1dce3SCameron Grant 
97467b1dce3SCameron Grant /************************************************************************/
97567b1dce3SCameron Grant 
97633dbf14aSCameron Grant static moduledata_t sndpcm_mod = {
97733dbf14aSCameron Grant 	"snd_pcm",
978d95502a8SCameron Grant 	NULL,
97933dbf14aSCameron Grant 	NULL
98033dbf14aSCameron Grant };
98133dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
98233dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER);
983