xref: /freebsd/sys/dev/sound/pcm/sound.c (revision 5ee30e277a97679dd1cbd4a1746339a5f14546aa)
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;
7855a38451SDon Lewis 	mtx_init(m, desc, type, MTX_DEF | MTX_RECURSE);
7937209180SCameron Grant 	return m;
8037209180SCameron Grant #else
81a983d575SCameron Grant 	return (void *)0xcafebabe;
8237209180SCameron Grant #endif
8337209180SCameron Grant }
8437209180SCameron Grant 
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;
191f637a36cSCameron Grant 				if (!SLIST_EMPTY(&c->children)) {
192f637a36cSCameron Grant 					err = vchan_create(c);
193f637a36cSCameron Grant 					if (!err)
194506a5308SCameron Grant 						return pcm_chnalloc(d, direction, pid, -1);
195f637a36cSCameron Grant 					else
196f637a36cSCameron Grant 						device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
197f637a36cSCameron Grant 				}
198f637a36cSCameron Grant 			}
199f637a36cSCameron Grant 		}
200f637a36cSCameron Grant 	}
201f637a36cSCameron Grant 
202285648f9SCameron Grant 	return NULL;
203285648f9SCameron Grant }
204285648f9SCameron Grant 
205b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
206285648f9SCameron Grant int
207b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
208285648f9SCameron Grant {
209b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
210285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
211b8f0d9e0SCameron Grant 	c->pid = -1;
21249c5e6e2SCameron Grant 	CHN_UNLOCK(c);
213285648f9SCameron Grant 	return 0;
214285648f9SCameron Grant }
215285648f9SCameron Grant 
216285648f9SCameron Grant int
217285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
218285648f9SCameron Grant {
21949c5e6e2SCameron Grant 	int r;
22049c5e6e2SCameron Grant 
221b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
222285648f9SCameron Grant 	c->refcount += ref;
22349c5e6e2SCameron Grant 	r = c->refcount;
22449c5e6e2SCameron Grant 	return r;
225285648f9SCameron Grant }
226285648f9SCameron Grant 
22767b1dce3SCameron Grant int
22867b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta)
22967b1dce3SCameron Grant {
230a527dbc7SCameron Grant 	int r;
231a527dbc7SCameron Grant 
232a527dbc7SCameron Grant 	if (delta == 0)
23367b1dce3SCameron Grant 		return d->inprog;
234a527dbc7SCameron Grant 
235a527dbc7SCameron Grant 	/* backtrace(); */
236a527dbc7SCameron Grant 	pcm_lock(d);
237a527dbc7SCameron Grant 	d->inprog += delta;
238a527dbc7SCameron Grant 	r = d->inprog;
239a527dbc7SCameron Grant 	pcm_unlock(d);
240a527dbc7SCameron Grant 	return r;
24167b1dce3SCameron Grant }
24267b1dce3SCameron Grant 
24367b1dce3SCameron Grant static void
24467b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num)
24567b1dce3SCameron Grant {
24667b1dce3SCameron Grant 	struct pcm_channel *c;
24767b1dce3SCameron Grant     	struct snddev_channel *sce;
24867b1dce3SCameron Grant 	int err, done;
24967b1dce3SCameron Grant 
25067b1dce3SCameron Grant 	if (num > 0 && d->vchancount == 0) {
25167b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
25267b1dce3SCameron Grant 			c = sce->channel;
25367b1dce3SCameron Grant 			if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
25467b1dce3SCameron Grant 				c->flags |= CHN_F_BUSY;
25567b1dce3SCameron Grant 				err = vchan_create(c);
25667b1dce3SCameron Grant 				if (err) {
25767b1dce3SCameron Grant 					device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
25867b1dce3SCameron Grant 					c->flags &= ~CHN_F_BUSY;
25967b1dce3SCameron Grant 				}
26067b1dce3SCameron Grant 				return;
26167b1dce3SCameron Grant 			}
26267b1dce3SCameron Grant 		}
26367b1dce3SCameron Grant 	}
26467b1dce3SCameron Grant 	if (num == 0 && d->vchancount > 0) {
26567b1dce3SCameron Grant 		done = 0;
26667b1dce3SCameron Grant 		while (!done) {
26767b1dce3SCameron Grant 			done = 1;
26867b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
26967b1dce3SCameron Grant 				c = sce->channel;
27067b1dce3SCameron Grant 				if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
27167b1dce3SCameron Grant 					done = 0;
272a527dbc7SCameron Grant 					snd_mtxlock(d->lock);
27367b1dce3SCameron Grant 					err = vchan_destroy(c);
274a527dbc7SCameron Grant 					snd_mtxunlock(d->lock);
27567b1dce3SCameron Grant 					if (err)
27667b1dce3SCameron Grant 						device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
2779cfd8eb3SPeter Wemm 					break;		/* restart */
27867b1dce3SCameron Grant 				}
27967b1dce3SCameron Grant 			}
28067b1dce3SCameron Grant 		}
28167b1dce3SCameron Grant 	}
28267b1dce3SCameron Grant }
28367b1dce3SCameron Grant 
28482db23e2SCameron Grant #ifdef USING_DEVFS
28533dbf14aSCameron Grant static int
286cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
28733dbf14aSCameron Grant {
288b8f0d9e0SCameron Grant 	struct snddev_info *d;
28933dbf14aSCameron Grant 	int error, unit;
29033dbf14aSCameron Grant 
29133dbf14aSCameron Grant 	unit = snd_unit;
29233dbf14aSCameron Grant 	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
29333dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
29474ffd138SCameron Grant 		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
295b8f0d9e0SCameron Grant 			return EINVAL;
296b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
297faeebea2SCameron Grant 		if (d == NULL || SLIST_EMPTY(&d->channels))
298b8f0d9e0SCameron Grant 			return EINVAL;
29933dbf14aSCameron Grant 		snd_unit = unit;
30033dbf14aSCameron Grant 	}
30133dbf14aSCameron Grant 	return (error);
30233dbf14aSCameron Grant }
303b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
304cd9766c5SCameron Grant             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
30582db23e2SCameron Grant #endif
306987e5972SCameron Grant 
307cd9766c5SCameron Grant static int
30867b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
309cd9766c5SCameron Grant {
31067b1dce3SCameron Grant 	struct snddev_info *d;
31167b1dce3SCameron Grant 	int i, v, error;
312cd9766c5SCameron Grant 
31367b1dce3SCameron Grant 	v = snd_maxautovchans;
314cd9766c5SCameron Grant 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
315cd9766c5SCameron Grant 	if (error == 0 && req->newptr != NULL) {
316cd9766c5SCameron Grant 		if (v < 0 || v >= SND_MAXVCHANS)
317cd9766c5SCameron Grant 			return EINVAL;
31867b1dce3SCameron Grant 		if (v != snd_maxautovchans) {
31967b1dce3SCameron Grant 			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
32067b1dce3SCameron Grant 				d = devclass_get_softc(pcm_devclass, i);
32167b1dce3SCameron Grant 				if (!d)
32267b1dce3SCameron Grant 					continue;
32367b1dce3SCameron Grant 				pcm_setmaxautovchans(d, v);
32467b1dce3SCameron Grant 			}
32567b1dce3SCameron Grant 		}
32667b1dce3SCameron Grant 		snd_maxautovchans = v;
327cd9766c5SCameron Grant 	}
328cd9766c5SCameron Grant 	return (error);
329cd9766c5SCameron Grant }
33067b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
33167b1dce3SCameron Grant             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
332f637a36cSCameron Grant 
333285648f9SCameron Grant struct pcm_channel *
334285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
335987e5972SCameron Grant {
336285648f9SCameron Grant 	struct pcm_channel *ch;
33733dbf14aSCameron Grant 	char *dirs;
338a67fe5c1SCameron Grant     	int err, *pnum;
339987e5972SCameron Grant 
340285648f9SCameron Grant 	switch(dir) {
341285648f9SCameron Grant 	case PCMDIR_PLAY:
342285648f9SCameron Grant 		dirs = "play";
343a67fe5c1SCameron Grant 		pnum = &d->playcount;
344285648f9SCameron Grant 		break;
345a67fe5c1SCameron Grant 
346285648f9SCameron Grant 	case PCMDIR_REC:
347285648f9SCameron Grant 		dirs = "record";
348a67fe5c1SCameron Grant 		pnum = &d->reccount;
349285648f9SCameron Grant 		break;
350a67fe5c1SCameron Grant 
351285648f9SCameron Grant 	case PCMDIR_VIRTUAL:
352285648f9SCameron Grant 		dirs = "virtual";
353285648f9SCameron Grant 		dir = PCMDIR_PLAY;
354a67fe5c1SCameron Grant 		pnum = &d->vchancount;
355285648f9SCameron Grant 		break;
356a67fe5c1SCameron Grant 
357285648f9SCameron Grant 	default:
358285648f9SCameron Grant 		return NULL;
3599c326820SCameron Grant 	}
360285648f9SCameron Grant 
361a163d034SWarner Losh 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
362285648f9SCameron Grant 	if (!ch)
363285648f9SCameron Grant 		return NULL;
364285648f9SCameron Grant 
365a163d034SWarner Losh 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
366285648f9SCameron Grant 	if (!ch->methods) {
367285648f9SCameron Grant 		free(ch, M_DEVBUF);
368a67fe5c1SCameron Grant 
369285648f9SCameron Grant 		return NULL;
370285648f9SCameron Grant 	}
371285648f9SCameron Grant 
37267beb5a5SCameron Grant 	snd_mtxlock(d->lock);
373a67fe5c1SCameron Grant 	ch->num = (*pnum)++;
37467beb5a5SCameron Grant 	snd_mtxunlock(d->lock);
375a67fe5c1SCameron Grant 
376285648f9SCameron Grant 	ch->pid = -1;
377285648f9SCameron Grant 	ch->parentsnddev = d;
378285648f9SCameron Grant 	ch->parentchannel = parent;
379436c9b65SScott Long 	ch->dev = d->dev;
38067beb5a5SCameron Grant 	snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
381285648f9SCameron Grant 
3820f55ac6cSCameron Grant 	err = chn_init(ch, devinfo, dir);
3830f55ac6cSCameron Grant 	if (err) {
384a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
385285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
386285648f9SCameron Grant 		free(ch, M_DEVBUF);
38767beb5a5SCameron Grant 		snd_mtxlock(d->lock);
388a67fe5c1SCameron Grant 		(*pnum)--;
38967beb5a5SCameron Grant 		snd_mtxunlock(d->lock);
390a67fe5c1SCameron Grant 
391285648f9SCameron Grant 		return NULL;
392bbb5bf3dSCameron Grant 	}
393285648f9SCameron Grant 
394285648f9SCameron Grant 	return ch;
395285648f9SCameron Grant }
396285648f9SCameron Grant 
397285648f9SCameron Grant int
398285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
399285648f9SCameron Grant {
400a67fe5c1SCameron Grant 	struct snddev_info *d;
401285648f9SCameron Grant 	int err;
402285648f9SCameron Grant 
403a67fe5c1SCameron Grant 	d = ch->parentsnddev;
404285648f9SCameron Grant 	err = chn_kill(ch);
405285648f9SCameron Grant 	if (err) {
406a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
407285648f9SCameron Grant 		return err;
408285648f9SCameron Grant 	}
409285648f9SCameron Grant 
410285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
411285648f9SCameron Grant 	free(ch, M_DEVBUF);
412285648f9SCameron Grant 
413285648f9SCameron Grant 	return 0;
414285648f9SCameron Grant }
415285648f9SCameron Grant 
416285648f9SCameron Grant int
4175ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
418285648f9SCameron Grant {
41967b1dce3SCameron Grant     	struct snddev_channel *sce, *tmp, *after;
4205ee30e27SMathew Kanner     	int device = device_get_unit(d->dev);
4215ee30e27SMathew Kanner 
4225ee30e27SMathew Kanner 	/*
4235ee30e27SMathew Kanner 	 * Note it's confusing nomenclature.
4245ee30e27SMathew Kanner 	 * dev_t
4255ee30e27SMathew Kanner 	 * device -> pcm_device
4265ee30e27SMathew Kanner          * unit -> pcm_channel
4275ee30e27SMathew Kanner 	 * channel -> snddev_channel
4285ee30e27SMathew Kanner 	 * device_t
4295ee30e27SMathew Kanner 	 * unit -> pcm_device
4305ee30e27SMathew Kanner 	 */
431b8f0d9e0SCameron Grant 
432a163d034SWarner Losh 	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
433285648f9SCameron Grant 	if (!sce) {
434285648f9SCameron Grant 		return ENOMEM;
435285648f9SCameron Grant 	}
436285648f9SCameron Grant 
4377cf0e77aSOrion Hodson 	snd_mtxlock(d->lock);
438285648f9SCameron Grant 	sce->channel = ch;
4395ee30e27SMathew Kanner 	sce->chan_num= d->devcount++;
44067b1dce3SCameron Grant 	if (SLIST_EMPTY(&d->channels)) {
441285648f9SCameron Grant 		SLIST_INSERT_HEAD(&d->channels, sce, link);
44267b1dce3SCameron Grant 	} else {
44367b1dce3SCameron Grant 		after = NULL;
44467b1dce3SCameron Grant 		SLIST_FOREACH(tmp, &d->channels, link) {
44567b1dce3SCameron Grant 			after = tmp;
44667b1dce3SCameron Grant 		}
44767b1dce3SCameron Grant 		SLIST_INSERT_AFTER(after, sce, link);
44867b1dce3SCameron Grant 	}
44967beb5a5SCameron Grant 	snd_mtxunlock(d->lock);
4505ee30e27SMathew Kanner 	sce->dsp_devt= make_dev(&dsp_cdevsw,
4515ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
4525ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
4535ee30e27SMathew Kanner 			device, sce->chan_num);
454285648f9SCameron Grant 
4555ee30e27SMathew Kanner 	sce->dspW_devt= make_dev(&dsp_cdevsw,
4565ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
4575ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
4585ee30e27SMathew Kanner 			device, sce->chan_num);
4595ee30e27SMathew Kanner 
4605ee30e27SMathew Kanner 	sce->audio_devt= make_dev(&dsp_cdevsw,
4615ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
4625ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
4635ee30e27SMathew Kanner 			device, sce->chan_num);
4645ee30e27SMathew Kanner 
465506a5308SCameron Grant 	if (ch->direction == PCMDIR_REC)
4665ee30e27SMathew Kanner 		sce->dspr_devt = make_dev(&dsp_cdevsw,
4675ee30e27SMathew Kanner 				PCMMKMINOR(device, SND_DEV_DSPREC,
4685ee30e27SMathew Kanner 					sce->chan_num), UID_ROOT, GID_WHEEL,
4695ee30e27SMathew Kanner 				0666, "dspr%d.%d", device, sce->chan_num);
470b8f0d9e0SCameron Grant 
47133dbf14aSCameron Grant 	return 0;
47233dbf14aSCameron Grant }
47333dbf14aSCameron Grant 
474285648f9SCameron Grant int
4755ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
47633dbf14aSCameron Grant {
477285648f9SCameron Grant     	struct snddev_channel *sce;
47845550658SPoul-Henning Kamp #if 0
479a527dbc7SCameron Grant 	int ourlock;
48033dbf14aSCameron Grant 
481a527dbc7SCameron Grant 	ourlock = 0;
482a527dbc7SCameron Grant 	if (!mtx_owned(d->lock)) {
48349c5e6e2SCameron Grant 		snd_mtxlock(d->lock);
484a527dbc7SCameron Grant 		ourlock = 1;
485a527dbc7SCameron Grant 	}
48645550658SPoul-Henning Kamp #endif
487a527dbc7SCameron Grant 
488285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
489285648f9SCameron Grant 		if (sce->channel == ch)
490285648f9SCameron Grant 			goto gotit;
49133dbf14aSCameron Grant 	}
49245550658SPoul-Henning Kamp #if 0
493a527dbc7SCameron Grant 	if (ourlock)
49449c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
49545550658SPoul-Henning Kamp #endif
496285648f9SCameron Grant 	return EINVAL;
497285648f9SCameron Grant gotit:
498285648f9SCameron Grant 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
49967beb5a5SCameron Grant 
50067beb5a5SCameron Grant     	if (ch->direction == PCMDIR_REC)
50167beb5a5SCameron Grant 		d->reccount--;
50267beb5a5SCameron Grant 	else if (ch->flags & CHN_F_VIRTUAL)
50367beb5a5SCameron Grant 		d->vchancount--;
50467beb5a5SCameron Grant 	else
50567beb5a5SCameron Grant 		d->playcount--;
50667beb5a5SCameron Grant 
50745550658SPoul-Henning Kamp #if 0
508a527dbc7SCameron Grant 	if (ourlock)
50949c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
51045550658SPoul-Henning Kamp #endif
51167beb5a5SCameron Grant 	free(sce, M_DEVBUF);
512285648f9SCameron Grant 
513987e5972SCameron Grant 	return 0;
514987e5972SCameron Grant }
515987e5972SCameron Grant 
516987e5972SCameron Grant int
517285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
518285648f9SCameron Grant {
519285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
52067b1dce3SCameron Grant 	struct pcm_channel *ch;
52167b1dce3SCameron Grant     	int err;
522285648f9SCameron Grant 
523285648f9SCameron Grant 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
524285648f9SCameron Grant 	if (!ch) {
525285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
526285648f9SCameron Grant 		return ENODEV;
527285648f9SCameron Grant 	}
528cd9766c5SCameron Grant 
5295ee30e27SMathew Kanner 	err = pcm_chn_add(d, ch);
530285648f9SCameron Grant 	if (err) {
531285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
53267beb5a5SCameron Grant 		snd_mtxunlock(d->lock);
533285648f9SCameron Grant 		pcm_chn_destroy(ch);
534cd9766c5SCameron Grant 		return err;
535cd9766c5SCameron Grant 	}
536cd9766c5SCameron Grant 
53761698e0cSAlexander Kabaev 	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
53861698e0cSAlexander Kabaev 	    ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
539cd9766c5SCameron Grant 		ch->flags |= CHN_F_BUSY;
540cd9766c5SCameron Grant 		err = vchan_create(ch);
541cd9766c5SCameron Grant 		if (err) {
54267b1dce3SCameron Grant 			device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
54367b1dce3SCameron Grant 			ch->flags &= ~CHN_F_BUSY;
544cd9766c5SCameron Grant 		}
545285648f9SCameron Grant 	}
546285648f9SCameron Grant 
547285648f9SCameron Grant 	return err;
548285648f9SCameron Grant }
549285648f9SCameron Grant 
550285648f9SCameron Grant static int
551285648f9SCameron Grant pcm_killchan(device_t dev)
552285648f9SCameron Grant {
553285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
554285648f9SCameron Grant     	struct snddev_channel *sce;
555e33bee07SOlivier Houchard 	struct pcm_channel *ch;
556e33bee07SOlivier Houchard 	int error = 0;
557285648f9SCameron Grant 
558285648f9SCameron Grant 	sce = SLIST_FIRST(&d->channels);
559e33bee07SOlivier Houchard 	ch = sce->channel;
560285648f9SCameron Grant 
5615ee30e27SMathew Kanner 	error = pcm_chn_remove(d, sce->channel);
562e33bee07SOlivier Houchard 	if (error)
563e33bee07SOlivier Houchard 		return (error);
564e33bee07SOlivier Houchard 	return (pcm_chn_destroy(ch));
565285648f9SCameron Grant }
566285648f9SCameron Grant 
567285648f9SCameron Grant int
568987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
569987e5972SCameron Grant {
57066ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
57149c5e6e2SCameron Grant 
57249c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
573987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
57449c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
575987e5972SCameron Grant 	return 0;
576987e5972SCameron Grant }
577987e5972SCameron Grant 
578987e5972SCameron Grant u_int32_t
579987e5972SCameron Grant pcm_getflags(device_t dev)
580987e5972SCameron Grant {
58166ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
58249c5e6e2SCameron Grant 
583987e5972SCameron Grant 	return d->flags;
584987e5972SCameron Grant }
585987e5972SCameron Grant 
586987e5972SCameron Grant void
587987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
588987e5972SCameron Grant {
58966ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
590d95502a8SCameron Grant 
591987e5972SCameron Grant 	d->flags = val;
592987e5972SCameron Grant }
593987e5972SCameron Grant 
59439004e69SCameron Grant void *
59539004e69SCameron Grant pcm_getdevinfo(device_t dev)
59639004e69SCameron Grant {
59766ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
59849c5e6e2SCameron Grant 
59939004e69SCameron Grant 	return d->devinfo;
60039004e69SCameron Grant }
60139004e69SCameron Grant 
602a67fe5c1SCameron Grant unsigned int
603a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
604a67fe5c1SCameron Grant {
605a67fe5c1SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
6064e60be34SCameron Grant 	int sz, x;
607a67fe5c1SCameron Grant 
608a67fe5c1SCameron Grant 	sz = 0;
6094e60be34SCameron Grant 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
6104e60be34SCameron Grant 		x = sz;
611a67fe5c1SCameron Grant 		RANGE(sz, min, max);
6124e60be34SCameron Grant 		if (x != sz)
6134e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
6144e60be34SCameron Grant 		x = min;
6154e60be34SCameron Grant 		while (x < sz)
6164e60be34SCameron Grant 			x <<= 1;
6174e60be34SCameron Grant 		if (x > sz)
6184e60be34SCameron Grant 			x >>= 1;
6194e60be34SCameron Grant 		if (x != sz) {
6204e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
6214e60be34SCameron Grant 			sz = x;
6224e60be34SCameron Grant 		}
6234e60be34SCameron Grant 	} else {
624a67fe5c1SCameron Grant 		sz = deflt;
6254e60be34SCameron Grant 	}
6264e60be34SCameron Grant 
627a67fe5c1SCameron Grant 	d->bufsz = sz;
628a67fe5c1SCameron Grant 
629a67fe5c1SCameron Grant 	return sz;
630a67fe5c1SCameron Grant }
631a67fe5c1SCameron Grant 
632987e5972SCameron Grant int
633987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
634987e5972SCameron Grant {
63566ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
636987e5972SCameron Grant 
637b8a36395SCameron Grant 	if (pcm_veto_load) {
638b8a36395SCameron Grant 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
639b8a36395SCameron Grant 
640b8a36395SCameron Grant 		return EINVAL;
641b8a36395SCameron Grant 	}
642b8a36395SCameron Grant 
6432c69ba87SJohn Baldwin 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
644285648f9SCameron Grant 
645cd9766c5SCameron Grant 	d->flags = 0;
646e4d5b250SCameron Grant 	d->dev = dev;
647987e5972SCameron Grant 	d->devinfo = devinfo;
648f637a36cSCameron Grant 	d->devcount = 0;
649506a5308SCameron Grant 	d->reccount = 0;
650a67fe5c1SCameron Grant 	d->playcount = 0;
651f637a36cSCameron Grant 	d->vchancount = 0;
652d95502a8SCameron Grant 	d->inprog = 0;
653833f7023SCameron Grant 
65445550658SPoul-Henning Kamp 	SLIST_INIT(&d->channels);
65545550658SPoul-Henning Kamp 	SLIST_INIT(&d->channels);
65645550658SPoul-Henning Kamp 
657d95502a8SCameron Grant 	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
658285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
659d95502a8SCameron Grant 
660285648f9SCameron Grant 	d->fakechan = fkchan_setup(dev);
661285648f9SCameron Grant 	chn_init(d->fakechan, NULL, 0);
662987e5972SCameron Grant 
66382db23e2SCameron Grant #ifdef SND_DYNSYSCTL
664cc486d80SJohn Baldwin 	sysctl_ctx_init(&d->sysctl_tree);
665cc486d80SJohn Baldwin 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
666a3e893e0SJohn Baldwin 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
667cc486d80SJohn Baldwin 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
668a3e893e0SJohn Baldwin 	if (d->sysctl_tree_top == NULL) {
669cc486d80SJohn Baldwin 		sysctl_ctx_free(&d->sysctl_tree);
670cc486d80SJohn Baldwin 		goto no;
671cc486d80SJohn Baldwin 	}
672a67fe5c1SCameron Grant 	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
673a67fe5c1SCameron Grant             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
67482db23e2SCameron Grant #endif
675b8f0d9e0SCameron Grant 	if (numplay > 0)
67667b1dce3SCameron Grant 		vchan_initsys(dev);
677cd9766c5SCameron Grant 	if (numplay == 1)
678cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
679cd9766c5SCameron Grant 
68067b1dce3SCameron Grant 	sndstat_register(dev, d->status, sndstat_prepare_pcm);
681987e5972SCameron Grant     	return 0;
682987e5972SCameron Grant no:
68349c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
684987e5972SCameron Grant 	return ENXIO;
685987e5972SCameron Grant }
686987e5972SCameron Grant 
68733dbf14aSCameron Grant int
68833dbf14aSCameron Grant pcm_unregister(device_t dev)
6897c438dbeSCameron Grant {
69066ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
691285648f9SCameron Grant     	struct snddev_channel *sce;
692a67fe5c1SCameron Grant 	struct pcm_channel *ch;
6937c438dbeSCameron Grant 
69449c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
695d95502a8SCameron Grant 	if (d->inprog) {
6965c25132aSGeorge C A Reid 		device_printf(dev, "unregister: operation in progress\n");
6975c25132aSGeorge C A Reid 		snd_mtxunlock(d->lock);
6985c25132aSGeorge C A Reid 		return EBUSY;
6995c25132aSGeorge C A Reid 	}
7005c25132aSGeorge C A Reid 	if (sndstat_busy() != 0) {
7015c25132aSGeorge C A Reid 		device_printf(dev, "unregister: sndstat busy\n");
702d95502a8SCameron Grant 		snd_mtxunlock(d->lock);
703d95502a8SCameron Grant 		return EBUSY;
704d95502a8SCameron Grant 	}
7055ee30e27SMathew Kanner 
7065ee30e27SMathew Kanner 
707285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
708a67fe5c1SCameron Grant 		ch = sce->channel;
709a67fe5c1SCameron Grant 		if (ch->refcount > 0) {
71021ed9908SCameron Grant 			device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
71149c5e6e2SCameron Grant 			snd_mtxunlock(d->lock);
712285648f9SCameron Grant 			return EBUSY;
713285648f9SCameron Grant 		}
714c9b53085SCameron Grant 	}
7155ee30e27SMathew Kanner 
7165ee30e27SMathew Kanner 	SLIST_FOREACH(sce, &d->channels, link) {
7175ee30e27SMathew Kanner 		destroy_dev(sce->dsp_devt);
7185ee30e27SMathew Kanner 		destroy_dev(sce->dspW_devt);
7195ee30e27SMathew Kanner 		destroy_dev(sce->audio_devt);
7205ee30e27SMathew Kanner 		if (sce->dspr_devt)
7215ee30e27SMathew Kanner 			destroy_dev(sce->dspr_devt);
7225ee30e27SMathew Kanner 	}
7235ee30e27SMathew Kanner 
724d95502a8SCameron Grant 	if (mixer_uninit(dev)) {
7255c25132aSGeorge C A Reid 		device_printf(dev, "unregister: mixer busy\n");
72649c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
727c9b53085SCameron Grant 		return EBUSY;
728c9b53085SCameron Grant 	}
7297c438dbeSCameron Grant 
73066ef8af5SCameron Grant #ifdef SND_DYNSYSCTL
73166ef8af5SCameron Grant 	d->sysctl_tree_top = NULL;
73266ef8af5SCameron Grant 	sysctl_ctx_free(&d->sysctl_tree);
73366ef8af5SCameron Grant #endif
734faeebea2SCameron Grant 	while (!SLIST_EMPTY(&d->channels))
735285648f9SCameron Grant 		pcm_killchan(dev);
7367c438dbeSCameron Grant 
73766ef8af5SCameron Grant 	chn_kill(d->fakechan);
73866ef8af5SCameron Grant 	fkchan_kill(d->fakechan);
73982db23e2SCameron Grant 
74047172de8SNick Sayer 	sndstat_unregister(dev);
74145550658SPoul-Henning Kamp 	snd_mtxunlock(d->lock);
74249c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
74333dbf14aSCameron Grant 	return 0;
74433dbf14aSCameron Grant }
7457c438dbeSCameron Grant 
74667b1dce3SCameron Grant /************************************************************************/
74767b1dce3SCameron Grant 
74867b1dce3SCameron Grant static int
74967b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
75067b1dce3SCameron Grant {
75167b1dce3SCameron Grant     	struct snddev_info *d;
75267b1dce3SCameron Grant     	struct snddev_channel *sce;
75367b1dce3SCameron Grant 	struct pcm_channel *c;
75467b1dce3SCameron Grant 	struct pcm_feeder *f;
75567b1dce3SCameron Grant     	int pc, rc, vc;
75667b1dce3SCameron Grant 
75767b1dce3SCameron Grant 	if (verbose < 1)
75867b1dce3SCameron Grant 		return 0;
75967b1dce3SCameron Grant 
76067b1dce3SCameron Grant 	d = device_get_softc(dev);
76167b1dce3SCameron Grant 	if (!d)
76267b1dce3SCameron Grant 		return ENXIO;
76367b1dce3SCameron Grant 
76467b1dce3SCameron Grant 	snd_mtxlock(d->lock);
76567b1dce3SCameron Grant 	if (!SLIST_EMPTY(&d->channels)) {
76667b1dce3SCameron Grant 		pc = rc = vc = 0;
76767b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
76867b1dce3SCameron Grant 			c = sce->channel;
76967b1dce3SCameron Grant 			if (c->direction == PCMDIR_PLAY) {
77067b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
77167b1dce3SCameron Grant 					vc++;
77267b1dce3SCameron Grant 				else
77367b1dce3SCameron Grant 					pc++;
77467b1dce3SCameron Grant 			} else
77567b1dce3SCameron Grant 				rc++;
77667b1dce3SCameron Grant 		}
777a67fe5c1SCameron Grant 		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
77867b1dce3SCameron Grant 				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
77967b1dce3SCameron Grant #ifdef USING_DEVFS
78067b1dce3SCameron Grant 				(device_get_unit(dev) == snd_unit)? " default" : ""
78167b1dce3SCameron Grant #else
78267b1dce3SCameron Grant 				""
78367b1dce3SCameron Grant #endif
78467b1dce3SCameron Grant 				);
785a527dbc7SCameron Grant 
786a527dbc7SCameron Grant 		if (verbose <= 1) {
787a527dbc7SCameron Grant 			snd_mtxunlock(d->lock);
788a527dbc7SCameron Grant 			return 0;
789a527dbc7SCameron Grant 		}
790a527dbc7SCameron Grant 
79167b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
79267b1dce3SCameron Grant 			c = sce->channel;
793a3285889SCameron Grant 			sbuf_printf(s, "\n\t");
794a3285889SCameron Grant 
79545550658SPoul-Henning Kamp 			/* it would be better to indent child channels */
796a3285889SCameron Grant 			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
7974c68642aSCameron Grant 			sbuf_printf(s, "spd %d", c->speed);
7984c68642aSCameron Grant 			if (c->speed != sndbuf_getspd(c->bufhard))
7994c68642aSCameron Grant 				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
8004c68642aSCameron Grant 			sbuf_printf(s, ", fmt 0x%08x", c->format);
8014c68642aSCameron Grant 			if (c->format != sndbuf_getfmt(c->bufhard))
8024c68642aSCameron Grant 				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
803a527dbc7SCameron Grant 			sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
80467b1dce3SCameron Grant 			if (c->pid != -1)
80567b1dce3SCameron Grant 				sbuf_printf(s, ", pid %d", c->pid);
80667b1dce3SCameron Grant 			sbuf_printf(s, "\n\t");
8074c68642aSCameron Grant 
808edecdda7SCameron Grant 			if (c->bufhard != NULL && c->bufsoft != NULL) {
809a3285889SCameron Grant 				sbuf_printf(s, "interrupts %d, ", c->interrupts);
810a3285889SCameron Grant 				if (c->direction == PCMDIR_REC)
811a3285889SCameron Grant 					sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
812a3285889SCameron Grant 						c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
813a3285889SCameron Grant 				else
814edecdda7SCameron Grant 					sbuf_printf(s, "underruns %d, ready %d",
815edecdda7SCameron Grant 						c->xruns, sndbuf_getready(c->bufsoft));
816a3285889SCameron Grant 				sbuf_printf(s, "\n\t");
817a3285889SCameron Grant 			}
8184c68642aSCameron Grant 
8194c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
8204c68642aSCameron Grant 			sbuf_printf(s, " -> ");
82167b1dce3SCameron Grant 			f = c->feeder;
8224c68642aSCameron Grant 			while (f->source != NULL)
8234c68642aSCameron Grant 				f = f->source;
8244c68642aSCameron Grant 			while (f != NULL) {
82567b1dce3SCameron Grant 				sbuf_printf(s, "%s", f->class->name);
82667b1dce3SCameron Grant 				if (f->desc->type == FEEDER_FMT)
8274c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
82867b1dce3SCameron Grant 				if (f->desc->type == FEEDER_RATE)
8294c68642aSCameron Grant 					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
83067b1dce3SCameron Grant 				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
8314c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x)", f->desc->out);
8324c68642aSCameron Grant 				sbuf_printf(s, " -> ");
8334c68642aSCameron Grant 				f = f->parent;
83467b1dce3SCameron Grant 			}
8354c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
83667b1dce3SCameron Grant 		}
83767b1dce3SCameron Grant 	} else
83867b1dce3SCameron Grant 		sbuf_printf(s, " (mixer only)");
83967b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
84067b1dce3SCameron Grant 
84167b1dce3SCameron Grant 	return 0;
84267b1dce3SCameron Grant }
84367b1dce3SCameron Grant 
84467b1dce3SCameron Grant /************************************************************************/
84567b1dce3SCameron Grant 
84667b1dce3SCameron Grant #ifdef SND_DYNSYSCTL
84767b1dce3SCameron Grant int
84867b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
84967b1dce3SCameron Grant {
85067b1dce3SCameron Grant 	struct snddev_info *d;
85167b1dce3SCameron Grant     	struct snddev_channel *sce;
85267b1dce3SCameron Grant 	struct pcm_channel *c;
853a527dbc7SCameron Grant 	int err, newcnt, cnt, busy;
854a527dbc7SCameron Grant 	int x;
85567b1dce3SCameron Grant 
85667b1dce3SCameron Grant 	d = oidp->oid_arg1;
85767b1dce3SCameron Grant 
858a527dbc7SCameron Grant 	x = pcm_inprog(d, 1);
859a527dbc7SCameron Grant 	if (x != 1) {
860a527dbc7SCameron Grant 		printf("x: %d\n", x);
861a527dbc7SCameron Grant 		pcm_inprog(d, -1);
862a527dbc7SCameron Grant 		return EINPROGRESS;
863a527dbc7SCameron Grant 	}
864a527dbc7SCameron Grant 
865a527dbc7SCameron Grant 	busy = 0;
86667b1dce3SCameron Grant 	cnt = 0;
86767b1dce3SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
86867b1dce3SCameron Grant 		c = sce->channel;
869a527dbc7SCameron Grant 		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
87067b1dce3SCameron Grant 			cnt++;
871a527dbc7SCameron Grant 			if (c->flags & CHN_F_BUSY)
872a527dbc7SCameron Grant 				busy++;
87367b1dce3SCameron Grant 		}
874a527dbc7SCameron Grant 	}
875a527dbc7SCameron Grant 
87667b1dce3SCameron Grant 	newcnt = cnt;
87767b1dce3SCameron Grant 
87867b1dce3SCameron Grant 	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
879a527dbc7SCameron Grant 
88067b1dce3SCameron Grant 	if (err == 0 && req->newptr != NULL) {
881a527dbc7SCameron Grant 
88267b1dce3SCameron Grant 		if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
883a527dbc7SCameron Grant 			pcm_inprog(d, -1);
884a527dbc7SCameron Grant 			return E2BIG;
88567b1dce3SCameron Grant 		}
88667b1dce3SCameron Grant 
88767b1dce3SCameron Grant 		if (newcnt > cnt) {
88867b1dce3SCameron Grant 			/* add new vchans - find a parent channel first */
88967b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
89067b1dce3SCameron Grant 				c = sce->channel;
89167b1dce3SCameron Grant 				/* not a candidate if not a play channel */
89267b1dce3SCameron Grant 				if (c->direction != PCMDIR_PLAY)
8939cfd8eb3SPeter Wemm 					continue;
89467b1dce3SCameron Grant 				/* not a candidate if a virtual channel */
89567b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
8969cfd8eb3SPeter Wemm 					continue;
89767b1dce3SCameron Grant 				/* not a candidate if it's in use */
89867b1dce3SCameron Grant 				if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
8999cfd8eb3SPeter Wemm 					continue;
90067b1dce3SCameron Grant 				/*
90167b1dce3SCameron Grant 				 * if we get here we're a nonvirtual play channel, and either
90267b1dce3SCameron Grant 				 * 1) not busy
90367b1dce3SCameron Grant 				 * 2) busy with children, not directly open
90467b1dce3SCameron Grant 				 *
90567b1dce3SCameron Grant 				 * thus we can add children
90667b1dce3SCameron Grant 				 */
90767b1dce3SCameron Grant 				goto addok;
90867b1dce3SCameron Grant 			}
909a527dbc7SCameron Grant 			pcm_inprog(d, -1);
91067b1dce3SCameron Grant 			return EBUSY;
91167b1dce3SCameron Grant addok:
91267b1dce3SCameron Grant 			c->flags |= CHN_F_BUSY;
91367b1dce3SCameron Grant 			while (err == 0 && newcnt > cnt) {
91467b1dce3SCameron Grant 				err = vchan_create(c);
91567b1dce3SCameron Grant 				if (err == 0)
91667b1dce3SCameron Grant 					cnt++;
91767b1dce3SCameron Grant 			}
91867b1dce3SCameron Grant 			if (SLIST_EMPTY(&c->children))
91967b1dce3SCameron Grant 				c->flags &= ~CHN_F_BUSY;
92067b1dce3SCameron Grant 		} else if (newcnt < cnt) {
921a527dbc7SCameron Grant 			if (busy > newcnt) {
922a527dbc7SCameron Grant 				printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy);
923a527dbc7SCameron Grant 				pcm_inprog(d, -1);
924a527dbc7SCameron Grant 				return EBUSY;
925a527dbc7SCameron Grant 			}
926a527dbc7SCameron Grant 
927a527dbc7SCameron Grant 			snd_mtxlock(d->lock);
92867b1dce3SCameron Grant 			while (err == 0 && newcnt < cnt) {
92967b1dce3SCameron Grant 				SLIST_FOREACH(sce, &d->channels, link) {
93067b1dce3SCameron Grant 					c = sce->channel;
93167b1dce3SCameron Grant 					if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
93267b1dce3SCameron Grant 						goto remok;
93367b1dce3SCameron Grant 				}
934a527dbc7SCameron Grant 				snd_mtxunlock(d->lock);
935a527dbc7SCameron Grant 				pcm_inprog(d, -1);
93667b1dce3SCameron Grant 				return EINVAL;
93767b1dce3SCameron Grant remok:
93867b1dce3SCameron Grant 				err = vchan_destroy(c);
93967b1dce3SCameron Grant 				if (err == 0)
94067b1dce3SCameron Grant 					cnt--;
94167b1dce3SCameron Grant 			}
942a527dbc7SCameron Grant 			snd_mtxunlock(d->lock);
94367b1dce3SCameron Grant 		}
94467b1dce3SCameron Grant 	}
945a527dbc7SCameron Grant 	pcm_inprog(d, -1);
94667b1dce3SCameron Grant 	return err;
94767b1dce3SCameron Grant }
94867b1dce3SCameron Grant #endif
94967b1dce3SCameron Grant 
95067b1dce3SCameron Grant /************************************************************************/
95167b1dce3SCameron Grant 
95233dbf14aSCameron Grant static moduledata_t sndpcm_mod = {
95333dbf14aSCameron Grant 	"snd_pcm",
954d95502a8SCameron Grant 	NULL,
95533dbf14aSCameron Grant 	NULL
95633dbf14aSCameron Grant };
95733dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
95833dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER);
959