xref: /freebsd/sys/dev/sound/pcm/sound.c (revision 12e524a290a2aa65e5c8192fb8ea4dff942da0f0)
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 
8512e524a2SDon Lewis void *
8612e524a2SDon Lewis snd_chnmtxcreate(const char *desc, const char *type)
8712e524a2SDon Lewis {
8812e524a2SDon Lewis #ifdef USING_MUTEX
8912e524a2SDon Lewis 	struct mtx *m;
9012e524a2SDon Lewis 
9112e524a2SDon Lewis 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
9212e524a2SDon Lewis 	if (m == NULL)
9312e524a2SDon Lewis 		return NULL;
9412e524a2SDon Lewis 	mtx_init(m, desc, type, MTX_DEF | MTX_DUPOK);
9537209180SCameron Grant 	return m;
9637209180SCameron Grant #else
97a983d575SCameron Grant 	return (void *)0xcafebabe;
9837209180SCameron Grant #endif
9937209180SCameron Grant }
10037209180SCameron Grant 
10137209180SCameron Grant void
10237209180SCameron Grant snd_mtxfree(void *m)
10337209180SCameron Grant {
10437209180SCameron Grant #ifdef USING_MUTEX
10537209180SCameron Grant 	struct mtx *mtx = m;
10637209180SCameron Grant 
10767beb5a5SCameron Grant 	/* mtx_assert(mtx, MA_OWNED); */
10837209180SCameron Grant 	mtx_destroy(mtx);
10937209180SCameron Grant 	free(mtx, M_DEVBUF);
11037209180SCameron Grant #endif
11137209180SCameron Grant }
11237209180SCameron Grant 
11337209180SCameron Grant void
11437209180SCameron Grant snd_mtxassert(void *m)
11537209180SCameron Grant {
11637209180SCameron Grant #ifdef USING_MUTEX
117f00f162aSCameron Grant #ifdef INVARIANTS
11837209180SCameron Grant 	struct mtx *mtx = m;
11937209180SCameron Grant 
12037209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
12137209180SCameron Grant #endif
122f00f162aSCameron Grant #endif
12337209180SCameron Grant }
12467beb5a5SCameron Grant /*
12537209180SCameron Grant void
12637209180SCameron Grant snd_mtxlock(void *m)
12737209180SCameron Grant {
12837209180SCameron Grant #ifdef USING_MUTEX
12937209180SCameron Grant 	struct mtx *mtx = m;
13037209180SCameron Grant 
13137209180SCameron Grant 	mtx_lock(mtx);
13237209180SCameron Grant #endif
13337209180SCameron Grant }
13437209180SCameron Grant 
13537209180SCameron Grant void
13637209180SCameron Grant snd_mtxunlock(void *m)
13737209180SCameron Grant {
13837209180SCameron Grant #ifdef USING_MUTEX
13937209180SCameron Grant 	struct mtx *mtx = m;
14037209180SCameron Grant 
14137209180SCameron Grant 	mtx_unlock(mtx);
14237209180SCameron Grant #endif
14337209180SCameron Grant }
14467beb5a5SCameron Grant */
14537209180SCameron Grant int
14637209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
14737209180SCameron Grant {
14837209180SCameron Grant #ifdef USING_MUTEX
14937209180SCameron Grant 	flags &= INTR_MPSAFE;
15046700f12SPeter Wemm 	flags |= INTR_TYPE_AV;
15137209180SCameron Grant #else
15246700f12SPeter Wemm 	flags = INTR_TYPE_AV;
15337209180SCameron Grant #endif
15437209180SCameron Grant 	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
15537209180SCameron Grant }
15637209180SCameron Grant 
157a527dbc7SCameron Grant #ifndef	PCM_DEBUG_MTX
15867b1dce3SCameron Grant void
15967b1dce3SCameron Grant pcm_lock(struct snddev_info *d)
16067b1dce3SCameron Grant {
16167b1dce3SCameron Grant 	snd_mtxlock(d->lock);
16267b1dce3SCameron Grant }
16367b1dce3SCameron Grant 
16467b1dce3SCameron Grant void
16567b1dce3SCameron Grant pcm_unlock(struct snddev_info *d)
16667b1dce3SCameron Grant {
16767b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
16867b1dce3SCameron Grant }
169a527dbc7SCameron Grant #endif
17067b1dce3SCameron Grant 
17167b1dce3SCameron Grant struct pcm_channel *
17267b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d)
17367b1dce3SCameron Grant {
17467b1dce3SCameron Grant 	return d->fakechan;
17567b1dce3SCameron Grant }
17667b1dce3SCameron Grant 
177b8f0d9e0SCameron Grant /* return a locked channel */
178285648f9SCameron Grant struct pcm_channel *
179506a5308SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
180285648f9SCameron Grant {
181285648f9SCameron Grant 	struct pcm_channel *c;
182285648f9SCameron Grant     	struct snddev_channel *sce;
183f637a36cSCameron Grant 	int err;
184285648f9SCameron Grant 
185b8f0d9e0SCameron Grant 	snd_mtxassert(d->lock);
186f637a36cSCameron Grant 
187f637a36cSCameron Grant 	/* scan for a free channel */
188285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
189285648f9SCameron Grant 		c = sce->channel;
19049c5e6e2SCameron Grant 		CHN_LOCK(c);
191285648f9SCameron Grant 		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
192506a5308SCameron Grant 			if (chnum == -1 || c->num == chnum) {
193285648f9SCameron Grant 				c->flags |= CHN_F_BUSY;
194b8f0d9e0SCameron Grant 				c->pid = pid;
195285648f9SCameron Grant 				return c;
196285648f9SCameron Grant 			}
197506a5308SCameron Grant 		}
19849c5e6e2SCameron Grant 		CHN_UNLOCK(c);
199285648f9SCameron Grant 	}
200f637a36cSCameron Grant 
201f637a36cSCameron Grant 	/* no channel available */
202f637a36cSCameron Grant 	if (direction == PCMDIR_PLAY) {
20367b1dce3SCameron Grant 		if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
204f637a36cSCameron Grant 			/* try to create a vchan */
205f637a36cSCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
206f637a36cSCameron Grant 				c = sce->channel;
20712e524a2SDon Lewis 				CHN_LOCK(c);
208f637a36cSCameron Grant 				if (!SLIST_EMPTY(&c->children)) {
209f637a36cSCameron Grant 					err = vchan_create(c);
21012e524a2SDon Lewis 					CHN_UNLOCK(c);
211f637a36cSCameron Grant 					if (!err)
212506a5308SCameron Grant 						return pcm_chnalloc(d, direction, pid, -1);
213f637a36cSCameron Grant 					else
214f637a36cSCameron Grant 						device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
21512e524a2SDon Lewis 				} else
21612e524a2SDon Lewis 					CHN_UNLOCK(c);
217f637a36cSCameron Grant 			}
218f637a36cSCameron Grant 		}
219f637a36cSCameron Grant 	}
220f637a36cSCameron Grant 
221285648f9SCameron Grant 	return NULL;
222285648f9SCameron Grant }
223285648f9SCameron Grant 
224b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
225285648f9SCameron Grant int
226b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
227285648f9SCameron Grant {
228b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
229285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
230b8f0d9e0SCameron Grant 	c->pid = -1;
23149c5e6e2SCameron Grant 	CHN_UNLOCK(c);
232285648f9SCameron Grant 	return 0;
233285648f9SCameron Grant }
234285648f9SCameron Grant 
235285648f9SCameron Grant int
236285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
237285648f9SCameron Grant {
23849c5e6e2SCameron Grant 	int r;
23949c5e6e2SCameron Grant 
240b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
241285648f9SCameron Grant 	c->refcount += ref;
24249c5e6e2SCameron Grant 	r = c->refcount;
24349c5e6e2SCameron Grant 	return r;
244285648f9SCameron Grant }
245285648f9SCameron Grant 
24667b1dce3SCameron Grant int
24767b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta)
24867b1dce3SCameron Grant {
249a527dbc7SCameron Grant 	int r;
250a527dbc7SCameron Grant 
251a527dbc7SCameron Grant 	if (delta == 0)
25267b1dce3SCameron Grant 		return d->inprog;
253a527dbc7SCameron Grant 
254a527dbc7SCameron Grant 	/* backtrace(); */
255a527dbc7SCameron Grant 	pcm_lock(d);
256a527dbc7SCameron Grant 	d->inprog += delta;
257a527dbc7SCameron Grant 	r = d->inprog;
258a527dbc7SCameron Grant 	pcm_unlock(d);
259a527dbc7SCameron Grant 	return r;
26067b1dce3SCameron Grant }
26167b1dce3SCameron Grant 
26267b1dce3SCameron Grant static void
26367b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num)
26467b1dce3SCameron Grant {
26567b1dce3SCameron Grant 	struct pcm_channel *c;
26667b1dce3SCameron Grant     	struct snddev_channel *sce;
26767b1dce3SCameron Grant 	int err, done;
26867b1dce3SCameron Grant 
26967b1dce3SCameron Grant 	if (num > 0 && d->vchancount == 0) {
27067b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
27167b1dce3SCameron Grant 			c = sce->channel;
27212e524a2SDon Lewis 			CHN_LOCK(c);
27367b1dce3SCameron Grant 			if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
27467b1dce3SCameron Grant 				c->flags |= CHN_F_BUSY;
27567b1dce3SCameron Grant 				err = vchan_create(c);
27667b1dce3SCameron Grant 				if (err) {
27767b1dce3SCameron Grant 					c->flags &= ~CHN_F_BUSY;
27812e524a2SDon Lewis 					CHN_UNLOCK(c);
27912e524a2SDon Lewis 					device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
28012e524a2SDon Lewis 				} else
28112e524a2SDon Lewis 					CHN_UNLOCK(c);
28267b1dce3SCameron Grant 				return;
28367b1dce3SCameron Grant 			}
28412e524a2SDon Lewis 			CHN_UNLOCK(c);
28567b1dce3SCameron Grant 		}
28667b1dce3SCameron Grant 	}
28767b1dce3SCameron Grant 	if (num == 0 && d->vchancount > 0) {
28867b1dce3SCameron Grant 		done = 0;
28967b1dce3SCameron Grant 		while (!done) {
29067b1dce3SCameron Grant 			done = 1;
29167b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
29267b1dce3SCameron Grant 				c = sce->channel;
29367b1dce3SCameron Grant 				if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
29467b1dce3SCameron Grant 					done = 0;
295a527dbc7SCameron Grant 					snd_mtxlock(d->lock);
29667b1dce3SCameron Grant 					err = vchan_destroy(c);
297a527dbc7SCameron Grant 					snd_mtxunlock(d->lock);
29867b1dce3SCameron Grant 					if (err)
29967b1dce3SCameron Grant 						device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
3009cfd8eb3SPeter Wemm 					break;		/* restart */
30167b1dce3SCameron Grant 				}
30267b1dce3SCameron Grant 			}
30367b1dce3SCameron Grant 		}
30467b1dce3SCameron Grant 	}
30567b1dce3SCameron Grant }
30667b1dce3SCameron Grant 
30782db23e2SCameron Grant #ifdef USING_DEVFS
30833dbf14aSCameron Grant static int
309cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
31033dbf14aSCameron Grant {
311b8f0d9e0SCameron Grant 	struct snddev_info *d;
31233dbf14aSCameron Grant 	int error, unit;
31333dbf14aSCameron Grant 
31433dbf14aSCameron Grant 	unit = snd_unit;
31533dbf14aSCameron Grant 	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
31633dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
31774ffd138SCameron Grant 		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
318b8f0d9e0SCameron Grant 			return EINVAL;
319b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
320faeebea2SCameron Grant 		if (d == NULL || SLIST_EMPTY(&d->channels))
321b8f0d9e0SCameron Grant 			return EINVAL;
32233dbf14aSCameron Grant 		snd_unit = unit;
32333dbf14aSCameron Grant 	}
32433dbf14aSCameron Grant 	return (error);
32533dbf14aSCameron Grant }
326b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
327cd9766c5SCameron Grant             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
32882db23e2SCameron Grant #endif
329987e5972SCameron Grant 
330cd9766c5SCameron Grant static int
33167b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
332cd9766c5SCameron Grant {
33367b1dce3SCameron Grant 	struct snddev_info *d;
33467b1dce3SCameron Grant 	int i, v, error;
335cd9766c5SCameron Grant 
33667b1dce3SCameron Grant 	v = snd_maxautovchans;
337cd9766c5SCameron Grant 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
338cd9766c5SCameron Grant 	if (error == 0 && req->newptr != NULL) {
33912e524a2SDon Lewis 		if (v < 0 || v >= SND_MAXVCHANS || pcm_devclass == NULL)
340cd9766c5SCameron Grant 			return EINVAL;
34167b1dce3SCameron Grant 		if (v != snd_maxautovchans) {
34267b1dce3SCameron Grant 			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
34367b1dce3SCameron Grant 				d = devclass_get_softc(pcm_devclass, i);
34467b1dce3SCameron Grant 				if (!d)
34567b1dce3SCameron Grant 					continue;
34667b1dce3SCameron Grant 				pcm_setmaxautovchans(d, v);
34767b1dce3SCameron Grant 			}
34867b1dce3SCameron Grant 		}
34967b1dce3SCameron Grant 		snd_maxautovchans = v;
350cd9766c5SCameron Grant 	}
351cd9766c5SCameron Grant 	return (error);
352cd9766c5SCameron Grant }
35367b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
35467b1dce3SCameron Grant             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
355f637a36cSCameron Grant 
356285648f9SCameron Grant struct pcm_channel *
357285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
358987e5972SCameron Grant {
359285648f9SCameron Grant 	struct pcm_channel *ch;
36033dbf14aSCameron Grant 	char *dirs;
361a67fe5c1SCameron Grant     	int err, *pnum;
362987e5972SCameron Grant 
363285648f9SCameron Grant 	switch(dir) {
364285648f9SCameron Grant 	case PCMDIR_PLAY:
365285648f9SCameron Grant 		dirs = "play";
366a67fe5c1SCameron Grant 		pnum = &d->playcount;
367285648f9SCameron Grant 		break;
368a67fe5c1SCameron Grant 
369285648f9SCameron Grant 	case PCMDIR_REC:
370285648f9SCameron Grant 		dirs = "record";
371a67fe5c1SCameron Grant 		pnum = &d->reccount;
372285648f9SCameron Grant 		break;
373a67fe5c1SCameron Grant 
374285648f9SCameron Grant 	case PCMDIR_VIRTUAL:
375285648f9SCameron Grant 		dirs = "virtual";
376285648f9SCameron Grant 		dir = PCMDIR_PLAY;
377a67fe5c1SCameron Grant 		pnum = &d->vchancount;
378285648f9SCameron Grant 		break;
379a67fe5c1SCameron Grant 
380285648f9SCameron Grant 	default:
381285648f9SCameron Grant 		return NULL;
3829c326820SCameron Grant 	}
383285648f9SCameron Grant 
384a163d034SWarner Losh 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
385285648f9SCameron Grant 	if (!ch)
386285648f9SCameron Grant 		return NULL;
387285648f9SCameron Grant 
388a163d034SWarner Losh 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
389285648f9SCameron Grant 	if (!ch->methods) {
390285648f9SCameron Grant 		free(ch, M_DEVBUF);
391a67fe5c1SCameron Grant 
392285648f9SCameron Grant 		return NULL;
393285648f9SCameron Grant 	}
394285648f9SCameron Grant 
39567beb5a5SCameron Grant 	snd_mtxlock(d->lock);
396a67fe5c1SCameron Grant 	ch->num = (*pnum)++;
39767beb5a5SCameron Grant 	snd_mtxunlock(d->lock);
398a67fe5c1SCameron Grant 
399285648f9SCameron Grant 	ch->pid = -1;
400285648f9SCameron Grant 	ch->parentsnddev = d;
401285648f9SCameron Grant 	ch->parentchannel = parent;
402436c9b65SScott Long 	ch->dev = d->dev;
40367beb5a5SCameron Grant 	snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
404285648f9SCameron Grant 
4050f55ac6cSCameron Grant 	err = chn_init(ch, devinfo, dir);
4060f55ac6cSCameron Grant 	if (err) {
407a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
408285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
409285648f9SCameron Grant 		free(ch, M_DEVBUF);
41067beb5a5SCameron Grant 		snd_mtxlock(d->lock);
411a67fe5c1SCameron Grant 		(*pnum)--;
41267beb5a5SCameron Grant 		snd_mtxunlock(d->lock);
413a67fe5c1SCameron Grant 
414285648f9SCameron Grant 		return NULL;
415bbb5bf3dSCameron Grant 	}
416285648f9SCameron Grant 
417285648f9SCameron Grant 	return ch;
418285648f9SCameron Grant }
419285648f9SCameron Grant 
420285648f9SCameron Grant int
421285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
422285648f9SCameron Grant {
423a67fe5c1SCameron Grant 	struct snddev_info *d;
424285648f9SCameron Grant 	int err;
425285648f9SCameron Grant 
426a67fe5c1SCameron Grant 	d = ch->parentsnddev;
427285648f9SCameron Grant 	err = chn_kill(ch);
428285648f9SCameron Grant 	if (err) {
429a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
430285648f9SCameron Grant 		return err;
431285648f9SCameron Grant 	}
432285648f9SCameron Grant 
433285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
434285648f9SCameron Grant 	free(ch, M_DEVBUF);
435285648f9SCameron Grant 
436285648f9SCameron Grant 	return 0;
437285648f9SCameron Grant }
438285648f9SCameron Grant 
439285648f9SCameron Grant int
4405ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
441285648f9SCameron Grant {
44267b1dce3SCameron Grant     	struct snddev_channel *sce, *tmp, *after;
4435ee30e27SMathew Kanner     	int device = device_get_unit(d->dev);
4445ee30e27SMathew Kanner 
4455ee30e27SMathew Kanner 	/*
4465ee30e27SMathew Kanner 	 * Note it's confusing nomenclature.
4475ee30e27SMathew Kanner 	 * dev_t
4485ee30e27SMathew Kanner 	 * device -> pcm_device
4495ee30e27SMathew Kanner          * unit -> pcm_channel
4505ee30e27SMathew Kanner 	 * channel -> snddev_channel
4515ee30e27SMathew Kanner 	 * device_t
4525ee30e27SMathew Kanner 	 * unit -> pcm_device
4535ee30e27SMathew Kanner 	 */
454b8f0d9e0SCameron Grant 
455a163d034SWarner Losh 	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
456285648f9SCameron Grant 	if (!sce) {
457285648f9SCameron Grant 		return ENOMEM;
458285648f9SCameron Grant 	}
459285648f9SCameron Grant 
4607cf0e77aSOrion Hodson 	snd_mtxlock(d->lock);
461285648f9SCameron Grant 	sce->channel = ch;
4625ee30e27SMathew Kanner 	sce->chan_num= d->devcount++;
46367b1dce3SCameron Grant 	if (SLIST_EMPTY(&d->channels)) {
464285648f9SCameron Grant 		SLIST_INSERT_HEAD(&d->channels, sce, link);
46567b1dce3SCameron Grant 	} else {
46667b1dce3SCameron Grant 		after = NULL;
46767b1dce3SCameron Grant 		SLIST_FOREACH(tmp, &d->channels, link) {
46867b1dce3SCameron Grant 			after = tmp;
46967b1dce3SCameron Grant 		}
47067b1dce3SCameron Grant 		SLIST_INSERT_AFTER(after, sce, link);
47167b1dce3SCameron Grant 	}
47267beb5a5SCameron Grant 	snd_mtxunlock(d->lock);
4735ee30e27SMathew Kanner 	sce->dsp_devt= make_dev(&dsp_cdevsw,
4745ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
4755ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
4765ee30e27SMathew Kanner 			device, sce->chan_num);
477285648f9SCameron Grant 
4785ee30e27SMathew Kanner 	sce->dspW_devt= make_dev(&dsp_cdevsw,
4795ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
4805ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
4815ee30e27SMathew Kanner 			device, sce->chan_num);
4825ee30e27SMathew Kanner 
4835ee30e27SMathew Kanner 	sce->audio_devt= make_dev(&dsp_cdevsw,
4845ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
4855ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
4865ee30e27SMathew Kanner 			device, sce->chan_num);
4875ee30e27SMathew Kanner 
488506a5308SCameron Grant 	if (ch->direction == PCMDIR_REC)
4895ee30e27SMathew Kanner 		sce->dspr_devt = make_dev(&dsp_cdevsw,
4905ee30e27SMathew Kanner 				PCMMKMINOR(device, SND_DEV_DSPREC,
4915ee30e27SMathew Kanner 					sce->chan_num), UID_ROOT, GID_WHEEL,
4925ee30e27SMathew Kanner 				0666, "dspr%d.%d", device, sce->chan_num);
493b8f0d9e0SCameron Grant 
49433dbf14aSCameron Grant 	return 0;
49533dbf14aSCameron Grant }
49633dbf14aSCameron Grant 
497285648f9SCameron Grant int
4985ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
49933dbf14aSCameron Grant {
500285648f9SCameron Grant     	struct snddev_channel *sce;
50145550658SPoul-Henning Kamp #if 0
502a527dbc7SCameron Grant 	int ourlock;
50333dbf14aSCameron Grant 
504a527dbc7SCameron Grant 	ourlock = 0;
505a527dbc7SCameron Grant 	if (!mtx_owned(d->lock)) {
50649c5e6e2SCameron Grant 		snd_mtxlock(d->lock);
507a527dbc7SCameron Grant 		ourlock = 1;
508a527dbc7SCameron Grant 	}
50945550658SPoul-Henning Kamp #endif
510a527dbc7SCameron Grant 
511285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
512285648f9SCameron Grant 		if (sce->channel == ch)
513285648f9SCameron Grant 			goto gotit;
51433dbf14aSCameron Grant 	}
51545550658SPoul-Henning Kamp #if 0
516a527dbc7SCameron Grant 	if (ourlock)
51749c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
51845550658SPoul-Henning Kamp #endif
519285648f9SCameron Grant 	return EINVAL;
520285648f9SCameron Grant gotit:
521285648f9SCameron Grant 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
52267beb5a5SCameron Grant 
52367beb5a5SCameron Grant     	if (ch->direction == PCMDIR_REC)
52467beb5a5SCameron Grant 		d->reccount--;
52567beb5a5SCameron Grant 	else if (ch->flags & CHN_F_VIRTUAL)
52667beb5a5SCameron Grant 		d->vchancount--;
52767beb5a5SCameron Grant 	else
52867beb5a5SCameron Grant 		d->playcount--;
52967beb5a5SCameron Grant 
53045550658SPoul-Henning Kamp #if 0
531a527dbc7SCameron Grant 	if (ourlock)
53249c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
53345550658SPoul-Henning Kamp #endif
53467beb5a5SCameron Grant 	free(sce, M_DEVBUF);
535285648f9SCameron Grant 
536987e5972SCameron Grant 	return 0;
537987e5972SCameron Grant }
538987e5972SCameron Grant 
539987e5972SCameron Grant int
540285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
541285648f9SCameron Grant {
542285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
54367b1dce3SCameron Grant 	struct pcm_channel *ch;
54467b1dce3SCameron Grant     	int err;
545285648f9SCameron Grant 
546285648f9SCameron Grant 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
547285648f9SCameron Grant 	if (!ch) {
548285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
549285648f9SCameron Grant 		return ENODEV;
550285648f9SCameron Grant 	}
551cd9766c5SCameron Grant 
5525ee30e27SMathew Kanner 	err = pcm_chn_add(d, ch);
553285648f9SCameron Grant 	if (err) {
554285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
555285648f9SCameron Grant 		pcm_chn_destroy(ch);
556cd9766c5SCameron Grant 		return err;
557cd9766c5SCameron Grant 	}
558cd9766c5SCameron Grant 
55912e524a2SDon Lewis 	CHN_LOCK(ch);
56061698e0cSAlexander Kabaev 	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
56161698e0cSAlexander Kabaev 	    ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
562cd9766c5SCameron Grant 		ch->flags |= CHN_F_BUSY;
563cd9766c5SCameron Grant 		err = vchan_create(ch);
564cd9766c5SCameron Grant 		if (err) {
56567b1dce3SCameron Grant 			ch->flags &= ~CHN_F_BUSY;
56612e524a2SDon Lewis 			CHN_UNLOCK(ch);
56712e524a2SDon Lewis 			device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
56812e524a2SDon Lewis 			return err;
569cd9766c5SCameron Grant 		}
570285648f9SCameron Grant 	}
57112e524a2SDon Lewis 	CHN_UNLOCK(ch);
572285648f9SCameron Grant 
573285648f9SCameron Grant 	return err;
574285648f9SCameron Grant }
575285648f9SCameron Grant 
576285648f9SCameron Grant static int
577285648f9SCameron Grant pcm_killchan(device_t dev)
578285648f9SCameron Grant {
579285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
580285648f9SCameron Grant     	struct snddev_channel *sce;
581e33bee07SOlivier Houchard 	struct pcm_channel *ch;
582e33bee07SOlivier Houchard 	int error = 0;
583285648f9SCameron Grant 
584285648f9SCameron Grant 	sce = SLIST_FIRST(&d->channels);
585e33bee07SOlivier Houchard 	ch = sce->channel;
586285648f9SCameron Grant 
5875ee30e27SMathew Kanner 	error = pcm_chn_remove(d, sce->channel);
588e33bee07SOlivier Houchard 	if (error)
589e33bee07SOlivier Houchard 		return (error);
590e33bee07SOlivier Houchard 	return (pcm_chn_destroy(ch));
591285648f9SCameron Grant }
592285648f9SCameron Grant 
593285648f9SCameron Grant int
594987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
595987e5972SCameron Grant {
59666ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
59749c5e6e2SCameron Grant 
59849c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
599987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
60049c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
601987e5972SCameron Grant 	return 0;
602987e5972SCameron Grant }
603987e5972SCameron Grant 
604987e5972SCameron Grant u_int32_t
605987e5972SCameron Grant pcm_getflags(device_t dev)
606987e5972SCameron Grant {
60766ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
60849c5e6e2SCameron Grant 
609987e5972SCameron Grant 	return d->flags;
610987e5972SCameron Grant }
611987e5972SCameron Grant 
612987e5972SCameron Grant void
613987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
614987e5972SCameron Grant {
61566ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
616d95502a8SCameron Grant 
617987e5972SCameron Grant 	d->flags = val;
618987e5972SCameron Grant }
619987e5972SCameron Grant 
62039004e69SCameron Grant void *
62139004e69SCameron Grant pcm_getdevinfo(device_t dev)
62239004e69SCameron Grant {
62366ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
62449c5e6e2SCameron Grant 
62539004e69SCameron Grant 	return d->devinfo;
62639004e69SCameron Grant }
62739004e69SCameron Grant 
628a67fe5c1SCameron Grant unsigned int
629a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
630a67fe5c1SCameron Grant {
631a67fe5c1SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
6324e60be34SCameron Grant 	int sz, x;
633a67fe5c1SCameron Grant 
634a67fe5c1SCameron Grant 	sz = 0;
6354e60be34SCameron Grant 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
6364e60be34SCameron Grant 		x = sz;
637a67fe5c1SCameron Grant 		RANGE(sz, min, max);
6384e60be34SCameron Grant 		if (x != sz)
6394e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
6404e60be34SCameron Grant 		x = min;
6414e60be34SCameron Grant 		while (x < sz)
6424e60be34SCameron Grant 			x <<= 1;
6434e60be34SCameron Grant 		if (x > sz)
6444e60be34SCameron Grant 			x >>= 1;
6454e60be34SCameron Grant 		if (x != sz) {
6464e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
6474e60be34SCameron Grant 			sz = x;
6484e60be34SCameron Grant 		}
6494e60be34SCameron Grant 	} else {
650a67fe5c1SCameron Grant 		sz = deflt;
6514e60be34SCameron Grant 	}
6524e60be34SCameron Grant 
653a67fe5c1SCameron Grant 	d->bufsz = sz;
654a67fe5c1SCameron Grant 
655a67fe5c1SCameron Grant 	return sz;
656a67fe5c1SCameron Grant }
657a67fe5c1SCameron Grant 
658987e5972SCameron Grant int
659987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
660987e5972SCameron Grant {
66166ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
662987e5972SCameron Grant 
663b8a36395SCameron Grant 	if (pcm_veto_load) {
664b8a36395SCameron Grant 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
665b8a36395SCameron Grant 
666b8a36395SCameron Grant 		return EINVAL;
667b8a36395SCameron Grant 	}
668b8a36395SCameron Grant 
6692c69ba87SJohn Baldwin 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
670285648f9SCameron Grant 
671cd9766c5SCameron Grant 	d->flags = 0;
672e4d5b250SCameron Grant 	d->dev = dev;
673987e5972SCameron Grant 	d->devinfo = devinfo;
674f637a36cSCameron Grant 	d->devcount = 0;
675506a5308SCameron Grant 	d->reccount = 0;
676a67fe5c1SCameron Grant 	d->playcount = 0;
677f637a36cSCameron Grant 	d->vchancount = 0;
678d95502a8SCameron Grant 	d->inprog = 0;
679833f7023SCameron Grant 
68045550658SPoul-Henning Kamp 	SLIST_INIT(&d->channels);
68145550658SPoul-Henning Kamp 	SLIST_INIT(&d->channels);
68245550658SPoul-Henning Kamp 
683d95502a8SCameron Grant 	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
684285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
685d95502a8SCameron Grant 
686285648f9SCameron Grant 	d->fakechan = fkchan_setup(dev);
687285648f9SCameron Grant 	chn_init(d->fakechan, NULL, 0);
688987e5972SCameron Grant 
68982db23e2SCameron Grant #ifdef SND_DYNSYSCTL
690cc486d80SJohn Baldwin 	sysctl_ctx_init(&d->sysctl_tree);
691cc486d80SJohn Baldwin 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
692a3e893e0SJohn Baldwin 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
693cc486d80SJohn Baldwin 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
694a3e893e0SJohn Baldwin 	if (d->sysctl_tree_top == NULL) {
695cc486d80SJohn Baldwin 		sysctl_ctx_free(&d->sysctl_tree);
696cc486d80SJohn Baldwin 		goto no;
697cc486d80SJohn Baldwin 	}
698a67fe5c1SCameron Grant 	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
699a67fe5c1SCameron Grant             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
70082db23e2SCameron Grant #endif
701b8f0d9e0SCameron Grant 	if (numplay > 0)
70267b1dce3SCameron Grant 		vchan_initsys(dev);
703cd9766c5SCameron Grant 	if (numplay == 1)
704cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
705cd9766c5SCameron Grant 
70667b1dce3SCameron Grant 	sndstat_register(dev, d->status, sndstat_prepare_pcm);
707987e5972SCameron Grant     	return 0;
708987e5972SCameron Grant no:
70949c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
710987e5972SCameron Grant 	return ENXIO;
711987e5972SCameron Grant }
712987e5972SCameron Grant 
71333dbf14aSCameron Grant int
71433dbf14aSCameron Grant pcm_unregister(device_t dev)
7157c438dbeSCameron Grant {
71666ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
717285648f9SCameron Grant     	struct snddev_channel *sce;
718a67fe5c1SCameron Grant 	struct pcm_channel *ch;
7197c438dbeSCameron Grant 
72049c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
721d95502a8SCameron Grant 	if (d->inprog) {
7225c25132aSGeorge C A Reid 		device_printf(dev, "unregister: operation in progress\n");
7235c25132aSGeorge C A Reid 		snd_mtxunlock(d->lock);
7245c25132aSGeorge C A Reid 		return EBUSY;
7255c25132aSGeorge C A Reid 	}
7265c25132aSGeorge C A Reid 	if (sndstat_busy() != 0) {
7275c25132aSGeorge C A Reid 		device_printf(dev, "unregister: sndstat busy\n");
728d95502a8SCameron Grant 		snd_mtxunlock(d->lock);
729d95502a8SCameron Grant 		return EBUSY;
730d95502a8SCameron Grant 	}
7315ee30e27SMathew Kanner 
7325ee30e27SMathew Kanner 
733285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
734a67fe5c1SCameron Grant 		ch = sce->channel;
735a67fe5c1SCameron Grant 		if (ch->refcount > 0) {
73621ed9908SCameron Grant 			device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
73749c5e6e2SCameron Grant 			snd_mtxunlock(d->lock);
738285648f9SCameron Grant 			return EBUSY;
739285648f9SCameron Grant 		}
740c9b53085SCameron Grant 	}
7415ee30e27SMathew Kanner 
7425ee30e27SMathew Kanner 	SLIST_FOREACH(sce, &d->channels, link) {
7435ee30e27SMathew Kanner 		destroy_dev(sce->dsp_devt);
7445ee30e27SMathew Kanner 		destroy_dev(sce->dspW_devt);
7455ee30e27SMathew Kanner 		destroy_dev(sce->audio_devt);
7465ee30e27SMathew Kanner 		if (sce->dspr_devt)
7475ee30e27SMathew Kanner 			destroy_dev(sce->dspr_devt);
7485ee30e27SMathew Kanner 	}
7495ee30e27SMathew Kanner 
750d95502a8SCameron Grant 	if (mixer_uninit(dev)) {
7515c25132aSGeorge C A Reid 		device_printf(dev, "unregister: mixer busy\n");
75249c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
753c9b53085SCameron Grant 		return EBUSY;
754c9b53085SCameron Grant 	}
7557c438dbeSCameron Grant 
75666ef8af5SCameron Grant #ifdef SND_DYNSYSCTL
75766ef8af5SCameron Grant 	d->sysctl_tree_top = NULL;
75866ef8af5SCameron Grant 	sysctl_ctx_free(&d->sysctl_tree);
75966ef8af5SCameron Grant #endif
760faeebea2SCameron Grant 	while (!SLIST_EMPTY(&d->channels))
761285648f9SCameron Grant 		pcm_killchan(dev);
7627c438dbeSCameron Grant 
76366ef8af5SCameron Grant 	chn_kill(d->fakechan);
76466ef8af5SCameron Grant 	fkchan_kill(d->fakechan);
76582db23e2SCameron Grant 
76647172de8SNick Sayer 	sndstat_unregister(dev);
76745550658SPoul-Henning Kamp 	snd_mtxunlock(d->lock);
76849c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
76933dbf14aSCameron Grant 	return 0;
77033dbf14aSCameron Grant }
7717c438dbeSCameron Grant 
77267b1dce3SCameron Grant /************************************************************************/
77367b1dce3SCameron Grant 
77467b1dce3SCameron Grant static int
77567b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
77667b1dce3SCameron Grant {
77767b1dce3SCameron Grant     	struct snddev_info *d;
77867b1dce3SCameron Grant     	struct snddev_channel *sce;
77967b1dce3SCameron Grant 	struct pcm_channel *c;
78067b1dce3SCameron Grant 	struct pcm_feeder *f;
78167b1dce3SCameron Grant     	int pc, rc, vc;
78267b1dce3SCameron Grant 
78367b1dce3SCameron Grant 	if (verbose < 1)
78467b1dce3SCameron Grant 		return 0;
78567b1dce3SCameron Grant 
78667b1dce3SCameron Grant 	d = device_get_softc(dev);
78767b1dce3SCameron Grant 	if (!d)
78867b1dce3SCameron Grant 		return ENXIO;
78967b1dce3SCameron Grant 
79067b1dce3SCameron Grant 	snd_mtxlock(d->lock);
79167b1dce3SCameron Grant 	if (!SLIST_EMPTY(&d->channels)) {
79267b1dce3SCameron Grant 		pc = rc = vc = 0;
79367b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
79467b1dce3SCameron Grant 			c = sce->channel;
79567b1dce3SCameron Grant 			if (c->direction == PCMDIR_PLAY) {
79667b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
79767b1dce3SCameron Grant 					vc++;
79867b1dce3SCameron Grant 				else
79967b1dce3SCameron Grant 					pc++;
80067b1dce3SCameron Grant 			} else
80167b1dce3SCameron Grant 				rc++;
80267b1dce3SCameron Grant 		}
803a67fe5c1SCameron Grant 		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
80467b1dce3SCameron Grant 				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
80567b1dce3SCameron Grant #ifdef USING_DEVFS
80667b1dce3SCameron Grant 				(device_get_unit(dev) == snd_unit)? " default" : ""
80767b1dce3SCameron Grant #else
80867b1dce3SCameron Grant 				""
80967b1dce3SCameron Grant #endif
81067b1dce3SCameron Grant 				);
811a527dbc7SCameron Grant 
812a527dbc7SCameron Grant 		if (verbose <= 1) {
813a527dbc7SCameron Grant 			snd_mtxunlock(d->lock);
814a527dbc7SCameron Grant 			return 0;
815a527dbc7SCameron Grant 		}
816a527dbc7SCameron Grant 
81767b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
81867b1dce3SCameron Grant 			c = sce->channel;
819a3285889SCameron Grant 			sbuf_printf(s, "\n\t");
820a3285889SCameron Grant 
82145550658SPoul-Henning Kamp 			/* it would be better to indent child channels */
822a3285889SCameron Grant 			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
8234c68642aSCameron Grant 			sbuf_printf(s, "spd %d", c->speed);
8244c68642aSCameron Grant 			if (c->speed != sndbuf_getspd(c->bufhard))
8254c68642aSCameron Grant 				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
8264c68642aSCameron Grant 			sbuf_printf(s, ", fmt 0x%08x", c->format);
8274c68642aSCameron Grant 			if (c->format != sndbuf_getfmt(c->bufhard))
8284c68642aSCameron Grant 				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
829a527dbc7SCameron Grant 			sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
83067b1dce3SCameron Grant 			if (c->pid != -1)
83167b1dce3SCameron Grant 				sbuf_printf(s, ", pid %d", c->pid);
83267b1dce3SCameron Grant 			sbuf_printf(s, "\n\t");
8334c68642aSCameron Grant 
834edecdda7SCameron Grant 			if (c->bufhard != NULL && c->bufsoft != NULL) {
835a3285889SCameron Grant 				sbuf_printf(s, "interrupts %d, ", c->interrupts);
836a3285889SCameron Grant 				if (c->direction == PCMDIR_REC)
837a3285889SCameron Grant 					sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
838a3285889SCameron Grant 						c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
839a3285889SCameron Grant 				else
840edecdda7SCameron Grant 					sbuf_printf(s, "underruns %d, ready %d",
841edecdda7SCameron Grant 						c->xruns, sndbuf_getready(c->bufsoft));
842a3285889SCameron Grant 				sbuf_printf(s, "\n\t");
843a3285889SCameron Grant 			}
8444c68642aSCameron Grant 
8454c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
8464c68642aSCameron Grant 			sbuf_printf(s, " -> ");
84767b1dce3SCameron Grant 			f = c->feeder;
8484c68642aSCameron Grant 			while (f->source != NULL)
8494c68642aSCameron Grant 				f = f->source;
8504c68642aSCameron Grant 			while (f != NULL) {
85167b1dce3SCameron Grant 				sbuf_printf(s, "%s", f->class->name);
85267b1dce3SCameron Grant 				if (f->desc->type == FEEDER_FMT)
8534c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
85467b1dce3SCameron Grant 				if (f->desc->type == FEEDER_RATE)
8554c68642aSCameron Grant 					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
85667b1dce3SCameron Grant 				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
8574c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x)", f->desc->out);
8584c68642aSCameron Grant 				sbuf_printf(s, " -> ");
8594c68642aSCameron Grant 				f = f->parent;
86067b1dce3SCameron Grant 			}
8614c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
86267b1dce3SCameron Grant 		}
86367b1dce3SCameron Grant 	} else
86467b1dce3SCameron Grant 		sbuf_printf(s, " (mixer only)");
86567b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
86667b1dce3SCameron Grant 
86767b1dce3SCameron Grant 	return 0;
86867b1dce3SCameron Grant }
86967b1dce3SCameron Grant 
87067b1dce3SCameron Grant /************************************************************************/
87167b1dce3SCameron Grant 
87267b1dce3SCameron Grant #ifdef SND_DYNSYSCTL
87367b1dce3SCameron Grant int
87467b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
87567b1dce3SCameron Grant {
87667b1dce3SCameron Grant 	struct snddev_info *d;
87767b1dce3SCameron Grant     	struct snddev_channel *sce;
87867b1dce3SCameron Grant 	struct pcm_channel *c;
879a527dbc7SCameron Grant 	int err, newcnt, cnt, busy;
880a527dbc7SCameron Grant 	int x;
88167b1dce3SCameron Grant 
88267b1dce3SCameron Grant 	d = oidp->oid_arg1;
88367b1dce3SCameron Grant 
884a527dbc7SCameron Grant 	x = pcm_inprog(d, 1);
885a527dbc7SCameron Grant 	if (x != 1) {
886a527dbc7SCameron Grant 		printf("x: %d\n", x);
887a527dbc7SCameron Grant 		pcm_inprog(d, -1);
888a527dbc7SCameron Grant 		return EINPROGRESS;
889a527dbc7SCameron Grant 	}
890a527dbc7SCameron Grant 
891a527dbc7SCameron Grant 	busy = 0;
89267b1dce3SCameron Grant 	cnt = 0;
89367b1dce3SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
89467b1dce3SCameron Grant 		c = sce->channel;
89512e524a2SDon Lewis 		CHN_LOCK(c);
896a527dbc7SCameron Grant 		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
89767b1dce3SCameron Grant 			cnt++;
898a527dbc7SCameron Grant 			if (c->flags & CHN_F_BUSY)
899a527dbc7SCameron Grant 				busy++;
90067b1dce3SCameron Grant 		}
90112e524a2SDon Lewis 		CHN_UNLOCK(c);
902a527dbc7SCameron Grant 	}
903a527dbc7SCameron Grant 
90467b1dce3SCameron Grant 	newcnt = cnt;
90567b1dce3SCameron Grant 
90667b1dce3SCameron Grant 	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
907a527dbc7SCameron Grant 
90867b1dce3SCameron Grant 	if (err == 0 && req->newptr != NULL) {
909a527dbc7SCameron Grant 
91067b1dce3SCameron Grant 		if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
911a527dbc7SCameron Grant 			pcm_inprog(d, -1);
912a527dbc7SCameron Grant 			return E2BIG;
91367b1dce3SCameron Grant 		}
91467b1dce3SCameron Grant 
91567b1dce3SCameron Grant 		if (newcnt > cnt) {
91667b1dce3SCameron Grant 			/* add new vchans - find a parent channel first */
91767b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
91867b1dce3SCameron Grant 				c = sce->channel;
91912e524a2SDon Lewis 				CHN_LOCK(c);
92067b1dce3SCameron Grant 				/* not a candidate if not a play channel */
92167b1dce3SCameron Grant 				if (c->direction != PCMDIR_PLAY)
92212e524a2SDon Lewis 					goto next;
92367b1dce3SCameron Grant 				/* not a candidate if a virtual channel */
92467b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
92512e524a2SDon Lewis 					goto next;
92667b1dce3SCameron Grant 				/* not a candidate if it's in use */
92712e524a2SDon Lewis 				if (!(c->flags & CHN_F_BUSY) ||
92812e524a2SDon Lewis 				    !(SLIST_EMPTY(&c->children)))
92967b1dce3SCameron Grant 					/*
93012e524a2SDon Lewis 					 * if we get here we're a nonvirtual
93112e524a2SDon Lewis 					 * play channel, and either
93267b1dce3SCameron Grant 					 * 1) not busy
93312e524a2SDon Lewis 					 * 2) busy with children, not directly
93412e524a2SDon Lewis 					 *    open
93567b1dce3SCameron Grant 					 *
93667b1dce3SCameron Grant 					 * thus we can add children
93767b1dce3SCameron Grant 					 */
93867b1dce3SCameron Grant 					goto addok;
93912e524a2SDon Lewis next:
94012e524a2SDon Lewis 				CHN_UNLOCK(c);
94167b1dce3SCameron Grant 			}
942a527dbc7SCameron Grant 			pcm_inprog(d, -1);
94367b1dce3SCameron Grant 			return EBUSY;
94467b1dce3SCameron Grant addok:
94567b1dce3SCameron Grant 			c->flags |= CHN_F_BUSY;
94667b1dce3SCameron Grant 			while (err == 0 && newcnt > cnt) {
94767b1dce3SCameron Grant 				err = vchan_create(c);
94867b1dce3SCameron Grant 				if (err == 0)
94967b1dce3SCameron Grant 					cnt++;
95067b1dce3SCameron Grant 			}
95167b1dce3SCameron Grant 			if (SLIST_EMPTY(&c->children))
95267b1dce3SCameron Grant 				c->flags &= ~CHN_F_BUSY;
95312e524a2SDon Lewis 			CHN_UNLOCK(c);
95467b1dce3SCameron Grant 		} else if (newcnt < cnt) {
955a527dbc7SCameron Grant 			if (busy > newcnt) {
956a527dbc7SCameron Grant 				printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy);
957a527dbc7SCameron Grant 				pcm_inprog(d, -1);
958a527dbc7SCameron Grant 				return EBUSY;
959a527dbc7SCameron Grant 			}
960a527dbc7SCameron Grant 
961a527dbc7SCameron Grant 			snd_mtxlock(d->lock);
96267b1dce3SCameron Grant 			while (err == 0 && newcnt < cnt) {
96367b1dce3SCameron Grant 				SLIST_FOREACH(sce, &d->channels, link) {
96467b1dce3SCameron Grant 					c = sce->channel;
96512e524a2SDon Lewis 					CHN_LOCK(c);
96667b1dce3SCameron Grant 					if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
96767b1dce3SCameron Grant 						goto remok;
96812e524a2SDon Lewis 
96912e524a2SDon Lewis 					CHN_UNLOCK(c);
97067b1dce3SCameron Grant 				}
971a527dbc7SCameron Grant 				snd_mtxunlock(d->lock);
972a527dbc7SCameron Grant 				pcm_inprog(d, -1);
97367b1dce3SCameron Grant 				return EINVAL;
97467b1dce3SCameron Grant remok:
97512e524a2SDon Lewis 				CHN_UNLOCK(c);
97667b1dce3SCameron Grant 				err = vchan_destroy(c);
97767b1dce3SCameron Grant 				if (err == 0)
97867b1dce3SCameron Grant 					cnt--;
97967b1dce3SCameron Grant 			}
980a527dbc7SCameron Grant 			snd_mtxunlock(d->lock);
98167b1dce3SCameron Grant 		}
98267b1dce3SCameron Grant 	}
983a527dbc7SCameron Grant 	pcm_inprog(d, -1);
98467b1dce3SCameron Grant 	return err;
98567b1dce3SCameron Grant }
98667b1dce3SCameron Grant #endif
98767b1dce3SCameron Grant 
98867b1dce3SCameron Grant /************************************************************************/
98967b1dce3SCameron Grant 
99033dbf14aSCameron Grant static moduledata_t sndpcm_mod = {
99133dbf14aSCameron Grant 	"snd_pcm",
992d95502a8SCameron Grant 	NULL,
99333dbf14aSCameron Grant 	NULL
99433dbf14aSCameron Grant };
99533dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
99633dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER);
997