xref: /freebsd/sys/dev/sound/pcm/sound.c (revision edecdda7b92e50d6f879d4d4f214fe19b9ff54d3)
1987e5972SCameron Grant /*
2987e5972SCameron Grant  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3987e5972SCameron Grant  * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
4987e5972SCameron Grant  * All rights reserved.
5987e5972SCameron Grant  *
6987e5972SCameron Grant  * Redistribution and use in source and binary forms, with or without
7987e5972SCameron Grant  * modification, are permitted provided that the following conditions
8987e5972SCameron Grant  * are met:
9987e5972SCameron Grant  * 1. Redistributions of source code must retain the above copyright
10987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer.
11987e5972SCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
12987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer in the
13987e5972SCameron Grant  *    documentation and/or other materials provided with the distribution.
14987e5972SCameron Grant  *
15987e5972SCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16987e5972SCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17987e5972SCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18987e5972SCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19987e5972SCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20987e5972SCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21987e5972SCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22987e5972SCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23987e5972SCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24987e5972SCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25987e5972SCameron Grant  * SUCH DAMAGE.
26987e5972SCameron Grant  */
27987e5972SCameron Grant 
28ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
29285648f9SCameron Grant #include <dev/sound/pcm/vchan.h>
307c438dbeSCameron Grant #include <sys/sysctl.h>
31285648f9SCameron Grant 
3267b1dce3SCameron Grant #include "feeder_if.h"
3367b1dce3SCameron Grant 
3467b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$");
3567b1dce3SCameron Grant 
3667b1dce3SCameron Grant struct snddev_channel {
3767b1dce3SCameron Grant 	SLIST_ENTRY(snddev_channel) link;
3867b1dce3SCameron Grant 	struct pcm_channel *channel;
3967b1dce3SCameron Grant };
4067b1dce3SCameron Grant 
4167b1dce3SCameron Grant struct snddev_info {
4267b1dce3SCameron Grant 	SLIST_HEAD(, snddev_channel) channels;
4367b1dce3SCameron Grant 	struct pcm_channel *fakechan;
4467b1dce3SCameron Grant 	unsigned devcount, chancount, vchancount;
4567b1dce3SCameron Grant 	unsigned flags;
4667b1dce3SCameron Grant 	int inprog;
4767b1dce3SCameron Grant 	void *devinfo;
4867b1dce3SCameron Grant 	device_t dev;
4967b1dce3SCameron Grant 	char status[SND_STATUSLEN];
5067b1dce3SCameron Grant 	struct sysctl_ctx_list sysctl_tree;
5167b1dce3SCameron Grant 	struct sysctl_oid *sysctl_tree_top;
5267b1dce3SCameron Grant 	void *lock;
5367b1dce3SCameron Grant };
5467b1dce3SCameron Grant 
55d95502a8SCameron Grant devclass_t pcm_devclass;
5682db23e2SCameron Grant 
5782db23e2SCameron Grant #ifdef USING_DEVFS
58d95502a8SCameron Grant int snd_unit = 0;
5909786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit);
6082db23e2SCameron Grant #endif
61cbe7d6a3SCameron Grant 
6267b1dce3SCameron Grant int snd_maxautovchans = 0;
6367b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
64987e5972SCameron Grant 
6582db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
6682db23e2SCameron Grant 
6767b1dce3SCameron Grant static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
6867b1dce3SCameron Grant 
6967b1dce3SCameron Grant struct sysctl_ctx_list *
7067b1dce3SCameron Grant snd_sysctl_tree(device_t dev)
7167b1dce3SCameron Grant {
7267b1dce3SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
7367b1dce3SCameron Grant 
7467b1dce3SCameron Grant 	return &d->sysctl_tree;
7567b1dce3SCameron Grant }
7667b1dce3SCameron Grant 
7767b1dce3SCameron Grant struct sysctl_oid *
7867b1dce3SCameron Grant snd_sysctl_tree_top(device_t dev)
7967b1dce3SCameron Grant {
8067b1dce3SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
8167b1dce3SCameron Grant 
8267b1dce3SCameron Grant 	return d->sysctl_tree_top;
8367b1dce3SCameron Grant }
8467b1dce3SCameron Grant 
8537209180SCameron Grant void *
8637209180SCameron Grant snd_mtxcreate(const char *desc)
8737209180SCameron Grant {
8837209180SCameron Grant #ifdef USING_MUTEX
8937209180SCameron Grant 	struct mtx *m;
9037209180SCameron Grant 
9137209180SCameron Grant 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
9237209180SCameron Grant 	if (m == NULL)
9337209180SCameron Grant 		return NULL;
9437209180SCameron Grant 	mtx_init(m, desc, MTX_RECURSE);
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 
10737209180SCameron 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 }
12437209180SCameron 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 }
14437209180SCameron 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 
15767b1dce3SCameron Grant void
15867b1dce3SCameron Grant pcm_lock(struct snddev_info *d)
15967b1dce3SCameron Grant {
16067b1dce3SCameron Grant 	snd_mtxlock(d->lock);
16167b1dce3SCameron Grant }
16267b1dce3SCameron Grant 
16367b1dce3SCameron Grant void
16467b1dce3SCameron Grant pcm_unlock(struct snddev_info *d)
16567b1dce3SCameron Grant {
16667b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
16767b1dce3SCameron Grant }
16867b1dce3SCameron Grant 
16967b1dce3SCameron Grant struct pcm_channel *
17067b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d)
17167b1dce3SCameron Grant {
17267b1dce3SCameron Grant 	return d->fakechan;
17367b1dce3SCameron Grant }
17467b1dce3SCameron Grant 
175b8f0d9e0SCameron Grant /* return a locked channel */
176285648f9SCameron Grant struct pcm_channel *
177b8f0d9e0SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid)
178285648f9SCameron Grant {
179285648f9SCameron Grant 	struct pcm_channel *c;
180285648f9SCameron Grant     	struct snddev_channel *sce;
181f637a36cSCameron Grant 	int err;
182285648f9SCameron Grant 
183b8f0d9e0SCameron Grant 	snd_mtxassert(d->lock);
184f637a36cSCameron Grant 
185f637a36cSCameron Grant 	/* scan for a free channel */
186285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
187285648f9SCameron Grant 		c = sce->channel;
18849c5e6e2SCameron Grant 		CHN_LOCK(c);
189285648f9SCameron Grant 		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
190285648f9SCameron Grant 			c->flags |= CHN_F_BUSY;
191b8f0d9e0SCameron Grant 			c->pid = pid;
192285648f9SCameron Grant 			return c;
193285648f9SCameron Grant 		}
19449c5e6e2SCameron Grant 		CHN_UNLOCK(c);
195285648f9SCameron Grant 	}
196f637a36cSCameron Grant 
197f637a36cSCameron Grant 	/* no channel available */
198f637a36cSCameron Grant 	if (direction == PCMDIR_PLAY) {
19967b1dce3SCameron Grant 		if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
200f637a36cSCameron Grant 			/* try to create a vchan */
201f637a36cSCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
202f637a36cSCameron Grant 				c = sce->channel;
203f637a36cSCameron Grant 				if (!SLIST_EMPTY(&c->children)) {
204f637a36cSCameron Grant 					err = vchan_create(c);
205f637a36cSCameron Grant 					if (!err)
206f637a36cSCameron Grant 						return pcm_chnalloc(d, direction, pid);
207f637a36cSCameron Grant 					else
208f637a36cSCameron Grant 						device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
209f637a36cSCameron Grant 				}
210f637a36cSCameron Grant 			}
211f637a36cSCameron Grant 		}
212f637a36cSCameron Grant 	}
213f637a36cSCameron Grant 
214285648f9SCameron Grant 	return NULL;
215285648f9SCameron Grant }
216285648f9SCameron Grant 
217b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
218285648f9SCameron Grant int
219b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
220285648f9SCameron Grant {
221b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
222285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
223b8f0d9e0SCameron Grant 	c->pid = -1;
22449c5e6e2SCameron Grant 	CHN_UNLOCK(c);
225285648f9SCameron Grant 	return 0;
226285648f9SCameron Grant }
227285648f9SCameron Grant 
228285648f9SCameron Grant int
229285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
230285648f9SCameron Grant {
23149c5e6e2SCameron Grant 	int r;
23249c5e6e2SCameron Grant 
233b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
234285648f9SCameron Grant 	c->refcount += ref;
23549c5e6e2SCameron Grant 	r = c->refcount;
23649c5e6e2SCameron Grant 	return r;
237285648f9SCameron Grant }
238285648f9SCameron Grant 
23967b1dce3SCameron Grant int
24067b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta)
24167b1dce3SCameron Grant {
24267b1dce3SCameron Grant 	d->inprog += delta;
24367b1dce3SCameron Grant 	return d->inprog;
24467b1dce3SCameron Grant }
24567b1dce3SCameron Grant 
24667b1dce3SCameron Grant static void
24767b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num)
24867b1dce3SCameron Grant {
24967b1dce3SCameron Grant 	struct pcm_channel *c;
25067b1dce3SCameron Grant     	struct snddev_channel *sce;
25167b1dce3SCameron Grant 	int err, done;
25267b1dce3SCameron Grant 
25367b1dce3SCameron Grant 	if (num > 0 && d->vchancount == 0) {
25467b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
25567b1dce3SCameron Grant 			c = sce->channel;
25667b1dce3SCameron Grant 			if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
25767b1dce3SCameron Grant 				c->flags |= CHN_F_BUSY;
25867b1dce3SCameron Grant 				err = vchan_create(c);
25967b1dce3SCameron Grant 				if (err) {
26067b1dce3SCameron Grant 					device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
26167b1dce3SCameron Grant 					c->flags &= ~CHN_F_BUSY;
26267b1dce3SCameron Grant 				}
26367b1dce3SCameron Grant 				return;
26467b1dce3SCameron Grant 			}
26567b1dce3SCameron Grant 		}
26667b1dce3SCameron Grant 	}
26767b1dce3SCameron Grant 	if (num == 0 && d->vchancount > 0) {
26867b1dce3SCameron Grant 		done = 0;
26967b1dce3SCameron Grant 		while (!done) {
27067b1dce3SCameron Grant 			done = 1;
27167b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
27267b1dce3SCameron Grant 				c = sce->channel;
27367b1dce3SCameron Grant 				if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
27467b1dce3SCameron Grant 					done = 0;
27567b1dce3SCameron Grant 					err = vchan_destroy(c);
27667b1dce3SCameron Grant 					if (err)
27767b1dce3SCameron Grant 						device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
27867b1dce3SCameron Grant 					goto restart;
27967b1dce3SCameron Grant 				}
28067b1dce3SCameron Grant 			}
28167b1dce3SCameron Grant restart:
28267b1dce3SCameron Grant 		}
28367b1dce3SCameron Grant 	}
28467b1dce3SCameron Grant }
28567b1dce3SCameron Grant 
28682db23e2SCameron Grant #ifdef USING_DEVFS
28733dbf14aSCameron Grant static int
288cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
28933dbf14aSCameron Grant {
290b8f0d9e0SCameron Grant 	struct snddev_info *d;
29133dbf14aSCameron Grant 	int error, unit;
29233dbf14aSCameron Grant 
29333dbf14aSCameron Grant 	unit = snd_unit;
29433dbf14aSCameron Grant 	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
29533dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
29674ffd138SCameron Grant 		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
297b8f0d9e0SCameron Grant 			return EINVAL;
298b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
299faeebea2SCameron Grant 		if (d == NULL || SLIST_EMPTY(&d->channels))
300b8f0d9e0SCameron Grant 			return EINVAL;
30133dbf14aSCameron Grant 		snd_unit = unit;
30233dbf14aSCameron Grant 	}
30333dbf14aSCameron Grant 	return (error);
30433dbf14aSCameron Grant }
305b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
306cd9766c5SCameron Grant             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
30782db23e2SCameron Grant #endif
308987e5972SCameron Grant 
309cd9766c5SCameron Grant static int
31067b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
311cd9766c5SCameron Grant {
31267b1dce3SCameron Grant 	struct snddev_info *d;
31367b1dce3SCameron Grant 	int i, v, error;
314cd9766c5SCameron Grant 
31567b1dce3SCameron Grant 	v = snd_maxautovchans;
316cd9766c5SCameron Grant 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
317cd9766c5SCameron Grant 	if (error == 0 && req->newptr != NULL) {
318cd9766c5SCameron Grant 		if (v < 0 || v >= SND_MAXVCHANS)
319cd9766c5SCameron Grant 			return EINVAL;
32067b1dce3SCameron Grant 		if (v != snd_maxautovchans) {
32167b1dce3SCameron Grant 			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
32267b1dce3SCameron Grant 				d = devclass_get_softc(pcm_devclass, i);
32367b1dce3SCameron Grant 				if (!d)
32467b1dce3SCameron Grant 					continue;
32567b1dce3SCameron Grant 				pcm_setmaxautovchans(d, v);
32667b1dce3SCameron Grant 			}
32767b1dce3SCameron Grant 		}
32867b1dce3SCameron Grant 		snd_maxautovchans = v;
329cd9766c5SCameron Grant 	}
330cd9766c5SCameron Grant 	return (error);
331cd9766c5SCameron Grant }
33267b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
33367b1dce3SCameron Grant             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
334f637a36cSCameron Grant 
335285648f9SCameron Grant struct pcm_channel *
336285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
337987e5972SCameron Grant {
338285648f9SCameron Grant 	struct pcm_channel *ch;
33933dbf14aSCameron Grant 	char *dirs;
3400f55ac6cSCameron Grant     	int err;
341987e5972SCameron Grant 
342285648f9SCameron Grant 	switch(dir) {
343285648f9SCameron Grant 	case PCMDIR_PLAY:
344285648f9SCameron Grant 		dirs = "play";
345285648f9SCameron Grant 		break;
346285648f9SCameron Grant 	case PCMDIR_REC:
347285648f9SCameron Grant 		dirs = "record";
348285648f9SCameron Grant 		break;
349285648f9SCameron Grant 	case PCMDIR_VIRTUAL:
350285648f9SCameron Grant 		dirs = "virtual";
351285648f9SCameron Grant 		dir = PCMDIR_PLAY;
352285648f9SCameron Grant 		break;
353285648f9SCameron Grant 	default:
354285648f9SCameron Grant 		return NULL;
3559c326820SCameron Grant 	}
356285648f9SCameron Grant 
357285648f9SCameron Grant 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
358285648f9SCameron Grant 	if (!ch)
359285648f9SCameron Grant 		return NULL;
360285648f9SCameron Grant 
3610f55ac6cSCameron Grant 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
362285648f9SCameron Grant 	if (!ch->methods) {
363285648f9SCameron Grant 		free(ch, M_DEVBUF);
364285648f9SCameron Grant 		return NULL;
365285648f9SCameron Grant 	}
366285648f9SCameron Grant 
367285648f9SCameron Grant 	ch->pid = -1;
368285648f9SCameron Grant 	ch->parentsnddev = d;
369285648f9SCameron Grant 	ch->parentchannel = parent;
370285648f9SCameron Grant 	snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs);
371285648f9SCameron Grant 
3720f55ac6cSCameron Grant 	err = chn_init(ch, devinfo, dir);
3730f55ac6cSCameron Grant 	if (err) {
374285648f9SCameron Grant 		device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err);
375285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
376285648f9SCameron Grant 		free(ch, M_DEVBUF);
377285648f9SCameron Grant 		return NULL;
378bbb5bf3dSCameron Grant 	}
379285648f9SCameron Grant 
380285648f9SCameron Grant 	return ch;
381285648f9SCameron Grant }
382285648f9SCameron Grant 
383285648f9SCameron Grant int
384285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
385285648f9SCameron Grant {
386285648f9SCameron Grant 	int err;
387285648f9SCameron Grant 
388285648f9SCameron Grant 	err = chn_kill(ch);
389285648f9SCameron Grant 	if (err) {
390285648f9SCameron Grant 		device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err);
391285648f9SCameron Grant 		return err;
392285648f9SCameron Grant 	}
393285648f9SCameron Grant 
394285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
395285648f9SCameron Grant 	free(ch, M_DEVBUF);
396285648f9SCameron Grant 
397285648f9SCameron Grant 	return 0;
398285648f9SCameron Grant }
399285648f9SCameron Grant 
400285648f9SCameron Grant int
401f637a36cSCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
402285648f9SCameron Grant {
40367b1dce3SCameron Grant     	struct snddev_channel *sce, *tmp, *after;
404285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
405b8f0d9e0SCameron Grant 
406b8f0d9e0SCameron Grant 	snd_mtxlock(d->lock);
407285648f9SCameron Grant 
408285648f9SCameron Grant 	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
409285648f9SCameron Grant 	if (!sce) {
410b8f0d9e0SCameron Grant 		snd_mtxunlock(d->lock);
411285648f9SCameron Grant 		return ENOMEM;
412285648f9SCameron Grant 	}
413285648f9SCameron Grant 
414285648f9SCameron Grant 	sce->channel = ch;
41567b1dce3SCameron Grant 	if (SLIST_EMPTY(&d->channels)) {
416285648f9SCameron Grant 		SLIST_INSERT_HEAD(&d->channels, sce, link);
41767b1dce3SCameron Grant 	} else {
41867b1dce3SCameron Grant 		after = NULL;
41967b1dce3SCameron Grant 		SLIST_FOREACH(tmp, &d->channels, link) {
42067b1dce3SCameron Grant 			after = tmp;
42167b1dce3SCameron Grant 		}
42267b1dce3SCameron Grant 		SLIST_INSERT_AFTER(after, sce, link);
42367b1dce3SCameron Grant 	}
424285648f9SCameron Grant 
425f637a36cSCameron Grant 	if (mkdev)
426f637a36cSCameron Grant 		dsp_register(unit, d->devcount++);
427d95502a8SCameron Grant     	d->chancount++;
428f637a36cSCameron Grant 	if (ch->flags & CHN_F_VIRTUAL)
429f637a36cSCameron Grant 		d->vchancount++;
430b8f0d9e0SCameron Grant 
43149c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
432285648f9SCameron Grant 
43333dbf14aSCameron Grant 	return 0;
43433dbf14aSCameron Grant }
43533dbf14aSCameron Grant 
436285648f9SCameron Grant int
437f637a36cSCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
43833dbf14aSCameron Grant {
439285648f9SCameron Grant     	struct snddev_channel *sce;
440285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
44133dbf14aSCameron Grant 
44249c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
443285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
444285648f9SCameron Grant 		if (sce->channel == ch)
445285648f9SCameron Grant 			goto gotit;
44633dbf14aSCameron Grant 	}
44749c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
448285648f9SCameron Grant 	return EINVAL;
449285648f9SCameron Grant gotit:
450f637a36cSCameron Grant 	if (ch->flags & CHN_F_VIRTUAL)
451f637a36cSCameron Grant 		d->vchancount--;
45233dbf14aSCameron Grant 	d->chancount--;
453285648f9SCameron Grant 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
454285648f9SCameron Grant 	free(sce, M_DEVBUF);
455285648f9SCameron Grant 
456f637a36cSCameron Grant 	if (rmdev)
457f637a36cSCameron Grant 		dsp_unregister(unit, --d->devcount);
45849c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
459285648f9SCameron Grant 
460987e5972SCameron Grant 	return 0;
461987e5972SCameron Grant }
462987e5972SCameron Grant 
463987e5972SCameron Grant int
464285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
465285648f9SCameron Grant {
466285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
46767b1dce3SCameron Grant 	struct pcm_channel *ch;
46867b1dce3SCameron Grant     	int err;
469285648f9SCameron Grant 
470285648f9SCameron Grant 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
471285648f9SCameron Grant 	if (!ch) {
472285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
473285648f9SCameron Grant 		return ENODEV;
474285648f9SCameron Grant 	}
475cd9766c5SCameron Grant 
476f637a36cSCameron Grant 	err = pcm_chn_add(d, ch, 1);
477285648f9SCameron Grant 	if (err) {
478285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
479285648f9SCameron Grant 		pcm_chn_destroy(ch);
480cd9766c5SCameron Grant 		return err;
481cd9766c5SCameron Grant 	}
482cd9766c5SCameron Grant 
48367b1dce3SCameron Grant 	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) {
484cd9766c5SCameron Grant 		ch->flags |= CHN_F_BUSY;
485cd9766c5SCameron Grant 		err = vchan_create(ch);
486cd9766c5SCameron Grant 		if (err) {
48767b1dce3SCameron Grant 			device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
48867b1dce3SCameron Grant 			ch->flags &= ~CHN_F_BUSY;
489cd9766c5SCameron Grant 		}
490285648f9SCameron Grant 	}
491285648f9SCameron Grant 
492285648f9SCameron Grant 	return err;
493285648f9SCameron Grant }
494285648f9SCameron Grant 
495285648f9SCameron Grant static int
496285648f9SCameron Grant pcm_killchan(device_t dev)
497285648f9SCameron Grant {
498285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
499285648f9SCameron Grant     	struct snddev_channel *sce;
500285648f9SCameron Grant 
50149c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
502285648f9SCameron Grant 	sce = SLIST_FIRST(&d->channels);
50349c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
504285648f9SCameron Grant 
505f637a36cSCameron Grant 	return pcm_chn_remove(d, sce->channel, 1);
506285648f9SCameron Grant }
507285648f9SCameron Grant 
508285648f9SCameron Grant int
509987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
510987e5972SCameron Grant {
51166ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
51249c5e6e2SCameron Grant 
51349c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
514987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
51549c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
516987e5972SCameron Grant 	return 0;
517987e5972SCameron Grant }
518987e5972SCameron Grant 
519987e5972SCameron Grant u_int32_t
520987e5972SCameron Grant pcm_getflags(device_t dev)
521987e5972SCameron Grant {
52266ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
52349c5e6e2SCameron Grant 
524987e5972SCameron Grant 	return d->flags;
525987e5972SCameron Grant }
526987e5972SCameron Grant 
527987e5972SCameron Grant void
528987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
529987e5972SCameron Grant {
53066ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
531d95502a8SCameron Grant 
532987e5972SCameron Grant 	d->flags = val;
533987e5972SCameron Grant }
534987e5972SCameron Grant 
53539004e69SCameron Grant void *
53639004e69SCameron Grant pcm_getdevinfo(device_t dev)
53739004e69SCameron Grant {
53866ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
53949c5e6e2SCameron Grant 
54039004e69SCameron Grant 	return d->devinfo;
54139004e69SCameron Grant }
54239004e69SCameron Grant 
543987e5972SCameron Grant int
544987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
545987e5972SCameron Grant {
54666ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
547987e5972SCameron Grant 
54849c5e6e2SCameron Grant 	d->lock = snd_mtxcreate(device_get_nameunit(dev));
54949c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
550285648f9SCameron Grant 
551cd9766c5SCameron Grant 	d->flags = 0;
552e4d5b250SCameron Grant 	d->dev = dev;
553987e5972SCameron Grant 	d->devinfo = devinfo;
554f637a36cSCameron Grant 	d->devcount = 0;
555285648f9SCameron Grant 	d->chancount = 0;
556f637a36cSCameron Grant 	d->vchancount = 0;
557d95502a8SCameron Grant 	d->inprog = 0;
558833f7023SCameron Grant 
559d95502a8SCameron Grant 	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
560285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
561d95502a8SCameron Grant 
562285648f9SCameron Grant 	d->fakechan = fkchan_setup(dev);
563285648f9SCameron Grant 	chn_init(d->fakechan, NULL, 0);
564987e5972SCameron Grant 
56582db23e2SCameron Grant #ifdef SND_DYNSYSCTL
566cc486d80SJohn Baldwin 	sysctl_ctx_init(&d->sysctl_tree);
567cc486d80SJohn Baldwin 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
568a3e893e0SJohn Baldwin 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
569cc486d80SJohn Baldwin 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
570a3e893e0SJohn Baldwin 	if (d->sysctl_tree_top == NULL) {
571cc486d80SJohn Baldwin 		sysctl_ctx_free(&d->sysctl_tree);
572cc486d80SJohn Baldwin 		goto no;
573cc486d80SJohn Baldwin 	}
57482db23e2SCameron Grant #endif
575b8f0d9e0SCameron Grant 	if (numplay > 0)
57667b1dce3SCameron Grant 		vchan_initsys(dev);
577cd9766c5SCameron Grant 	if (numplay == 1)
578cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
579cd9766c5SCameron Grant 
58049c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
58167b1dce3SCameron Grant 	sndstat_register(dev, d->status, sndstat_prepare_pcm);
582987e5972SCameron Grant     	return 0;
583987e5972SCameron Grant no:
58449c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
585987e5972SCameron Grant 	return ENXIO;
586987e5972SCameron Grant }
587987e5972SCameron Grant 
58833dbf14aSCameron Grant int
58933dbf14aSCameron Grant pcm_unregister(device_t dev)
5907c438dbeSCameron Grant {
59166ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
592285648f9SCameron Grant     	struct snddev_channel *sce;
5937c438dbeSCameron Grant 
59449c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
595d95502a8SCameron Grant 	if (d->inprog) {
596d95502a8SCameron Grant 		device_printf(dev, "unregister: operation in progress");
597d95502a8SCameron Grant 		snd_mtxunlock(d->lock);
598d95502a8SCameron Grant 		return EBUSY;
599d95502a8SCameron Grant 	}
600285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
601285648f9SCameron Grant 		if (sce->channel->refcount > 0) {
602c9b53085SCameron Grant 			device_printf(dev, "unregister: channel busy");
60349c5e6e2SCameron Grant 			snd_mtxunlock(d->lock);
604285648f9SCameron Grant 			return EBUSY;
605285648f9SCameron Grant 		}
606c9b53085SCameron Grant 	}
607d95502a8SCameron Grant 	if (mixer_uninit(dev)) {
608c9b53085SCameron Grant 		device_printf(dev, "unregister: mixer busy");
60949c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
610c9b53085SCameron Grant 		return EBUSY;
611c9b53085SCameron Grant 	}
6127c438dbeSCameron Grant 
61366ef8af5SCameron Grant #ifdef SND_DYNSYSCTL
61466ef8af5SCameron Grant 	d->sysctl_tree_top = NULL;
61566ef8af5SCameron Grant 	sysctl_ctx_free(&d->sysctl_tree);
61666ef8af5SCameron Grant #endif
617faeebea2SCameron Grant 	while (!SLIST_EMPTY(&d->channels))
618285648f9SCameron Grant 		pcm_killchan(dev);
6197c438dbeSCameron Grant 
62066ef8af5SCameron Grant 	chn_kill(d->fakechan);
62166ef8af5SCameron Grant 	fkchan_kill(d->fakechan);
62282db23e2SCameron Grant 
62349c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
62433dbf14aSCameron Grant 	return 0;
62533dbf14aSCameron Grant }
6267c438dbeSCameron Grant 
62767b1dce3SCameron Grant /************************************************************************/
62867b1dce3SCameron Grant 
62967b1dce3SCameron Grant static int
63067b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
63167b1dce3SCameron Grant {
63267b1dce3SCameron Grant     	struct snddev_info *d;
63367b1dce3SCameron Grant     	struct snddev_channel *sce;
63467b1dce3SCameron Grant 	struct pcm_channel *c;
63567b1dce3SCameron Grant 	struct pcm_feeder *f;
636a3285889SCameron Grant 	char *fsep;
63767b1dce3SCameron Grant     	int pc, rc, vc;
63867b1dce3SCameron Grant 
63967b1dce3SCameron Grant 	if (verbose < 1)
64067b1dce3SCameron Grant 		return 0;
64167b1dce3SCameron Grant 
64267b1dce3SCameron Grant 	d = device_get_softc(dev);
64367b1dce3SCameron Grant 	if (!d)
64467b1dce3SCameron Grant 		return ENXIO;
64567b1dce3SCameron Grant 
64667b1dce3SCameron Grant 	snd_mtxlock(d->lock);
64767b1dce3SCameron Grant 	if (!SLIST_EMPTY(&d->channels)) {
64867b1dce3SCameron Grant 		pc = rc = vc = 0;
64967b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
65067b1dce3SCameron Grant 			c = sce->channel;
65167b1dce3SCameron Grant 			if (c->direction == PCMDIR_PLAY) {
65267b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
65367b1dce3SCameron Grant 					vc++;
65467b1dce3SCameron Grant 				else
65567b1dce3SCameron Grant 					pc++;
65667b1dce3SCameron Grant 			} else
65767b1dce3SCameron Grant 				rc++;
65867b1dce3SCameron Grant 		}
65967b1dce3SCameron Grant 		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", pc, rc, vc,
66067b1dce3SCameron Grant 				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
66167b1dce3SCameron Grant #ifdef USING_DEVFS
66267b1dce3SCameron Grant 				(device_get_unit(dev) == snd_unit)? " default" : ""
66367b1dce3SCameron Grant #else
66467b1dce3SCameron Grant 				""
66567b1dce3SCameron Grant #endif
66667b1dce3SCameron Grant 				);
66767b1dce3SCameron Grant 		if (verbose <= 1)
66867b1dce3SCameron Grant 			goto skipverbose;
66967b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
67067b1dce3SCameron Grant 			c = sce->channel;
671a3285889SCameron Grant 			sbuf_printf(s, "\n\t");
672a3285889SCameron Grant 
673a3285889SCameron Grant 			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
674a3285889SCameron Grant 			sbuf_printf(s, "speed %d, format %08x, flags %08x", c->speed, c->format, c->flags);
67567b1dce3SCameron Grant 			if (c->pid != -1)
67667b1dce3SCameron Grant 				sbuf_printf(s, ", pid %d", c->pid);
67767b1dce3SCameron Grant 			sbuf_printf(s, "\n\t");
678edecdda7SCameron Grant 			if (c->bufhard != NULL && c->bufsoft != NULL) {
679a3285889SCameron Grant 				sbuf_printf(s, "interrupts %d, ", c->interrupts);
680a3285889SCameron Grant 				if (c->direction == PCMDIR_REC)
681a3285889SCameron Grant 					sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
682a3285889SCameron Grant 						c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
683a3285889SCameron Grant 				else
684edecdda7SCameron Grant 					sbuf_printf(s, "underruns %d, ready %d",
685edecdda7SCameron Grant 						c->xruns, sndbuf_getready(c->bufsoft));
686a3285889SCameron Grant 				sbuf_printf(s, "\n\t");
687a3285889SCameron Grant 			}
688a3285889SCameron Grant 			fsep = (c->direction == PCMDIR_REC)? " -> " : " <- ";
689edecdda7SCameron Grant 			sbuf_printf(s, "{hardware}%s", fsep);
69067b1dce3SCameron Grant 			f = c->feeder;
69167b1dce3SCameron Grant 			while (f) {
69267b1dce3SCameron Grant 				sbuf_printf(s, "%s", f->class->name);
69367b1dce3SCameron Grant 				if (f->desc->type == FEEDER_FMT)
694a3285889SCameron Grant 					sbuf_printf(s, "(%08x%s%08x)", f->desc->out, fsep, f->desc->in);
69567b1dce3SCameron Grant 				if (f->desc->type == FEEDER_RATE)
696a3285889SCameron Grant 					sbuf_printf(s, "(%d%s%d)", FEEDER_GET(f, FEEDRATE_DST), fsep, FEEDER_GET(f, FEEDRATE_SRC));
69767b1dce3SCameron Grant 				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
69867b1dce3SCameron Grant 					sbuf_printf(s, "(%08x)", f->desc->out);
699a3285889SCameron Grant 				sbuf_printf(s, "%s", fsep);
70067b1dce3SCameron Grant 				f = f->source;
70167b1dce3SCameron Grant 			}
702edecdda7SCameron Grant 			sbuf_printf(s, "{userland}");
70367b1dce3SCameron Grant 		}
70467b1dce3SCameron Grant skipverbose:
70567b1dce3SCameron Grant 	} else
70667b1dce3SCameron Grant 		sbuf_printf(s, " (mixer only)");
70767b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
70867b1dce3SCameron Grant 
70967b1dce3SCameron Grant 	return 0;
71067b1dce3SCameron Grant }
71167b1dce3SCameron Grant 
71267b1dce3SCameron Grant /************************************************************************/
71367b1dce3SCameron Grant 
71467b1dce3SCameron Grant #ifdef SND_DYNSYSCTL
71567b1dce3SCameron Grant int
71667b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
71767b1dce3SCameron Grant {
71867b1dce3SCameron Grant 	struct snddev_info *d;
71967b1dce3SCameron Grant     	struct snddev_channel *sce;
72067b1dce3SCameron Grant 	struct pcm_channel *c;
72167b1dce3SCameron Grant 	int err, oldcnt, newcnt, cnt;
72267b1dce3SCameron Grant 
72367b1dce3SCameron Grant 	d = oidp->oid_arg1;
72467b1dce3SCameron Grant 
72567b1dce3SCameron Grant 	pcm_lock(d);
72667b1dce3SCameron Grant 	cnt = 0;
72767b1dce3SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
72867b1dce3SCameron Grant 		c = sce->channel;
72967b1dce3SCameron Grant 		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
73067b1dce3SCameron Grant 			cnt++;
73167b1dce3SCameron Grant 	}
73267b1dce3SCameron Grant 	oldcnt = cnt;
73367b1dce3SCameron Grant 	newcnt = cnt;
73467b1dce3SCameron Grant 
73567b1dce3SCameron Grant 	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
73667b1dce3SCameron Grant 	if (err == 0 && req->newptr != NULL) {
73767b1dce3SCameron Grant 		if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
73867b1dce3SCameron Grant 			pcm_unlock(d);
73967b1dce3SCameron Grant 			return EINVAL;
74067b1dce3SCameron Grant 		}
74167b1dce3SCameron Grant 
74267b1dce3SCameron Grant 		if (newcnt > cnt) {
74367b1dce3SCameron Grant 			/* add new vchans - find a parent channel first */
74467b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
74567b1dce3SCameron Grant 				c = sce->channel;
74667b1dce3SCameron Grant 				/* not a candidate if not a play channel */
74767b1dce3SCameron Grant 				if (c->direction != PCMDIR_PLAY)
74867b1dce3SCameron Grant 					goto addskip;
74967b1dce3SCameron Grant 				/* not a candidate if a virtual channel */
75067b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
75167b1dce3SCameron Grant 					goto addskip;
75267b1dce3SCameron Grant 				/* not a candidate if it's in use */
75367b1dce3SCameron Grant 				if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
75467b1dce3SCameron Grant 					goto addskip;
75567b1dce3SCameron Grant 				/*
75667b1dce3SCameron Grant 				 * if we get here we're a nonvirtual play channel, and either
75767b1dce3SCameron Grant 				 * 1) not busy
75867b1dce3SCameron Grant 				 * 2) busy with children, not directly open
75967b1dce3SCameron Grant 				 *
76067b1dce3SCameron Grant 				 * thus we can add children
76167b1dce3SCameron Grant 				 */
76267b1dce3SCameron Grant 				goto addok;
76367b1dce3SCameron Grant addskip:
76467b1dce3SCameron Grant 			}
76567b1dce3SCameron Grant 			pcm_unlock(d);
76667b1dce3SCameron Grant 			return EBUSY;
76767b1dce3SCameron Grant addok:
76867b1dce3SCameron Grant 			c->flags |= CHN_F_BUSY;
76967b1dce3SCameron Grant 			while (err == 0 && newcnt > cnt) {
77067b1dce3SCameron Grant 				err = vchan_create(c);
77167b1dce3SCameron Grant 				if (err == 0)
77267b1dce3SCameron Grant 					cnt++;
77367b1dce3SCameron Grant 			}
77467b1dce3SCameron Grant 			if (SLIST_EMPTY(&c->children))
77567b1dce3SCameron Grant 				c->flags &= ~CHN_F_BUSY;
77667b1dce3SCameron Grant 		} else if (newcnt < cnt) {
77767b1dce3SCameron Grant 			while (err == 0 && newcnt < cnt) {
77867b1dce3SCameron Grant 				SLIST_FOREACH(sce, &d->channels, link) {
77967b1dce3SCameron Grant 					c = sce->channel;
78067b1dce3SCameron Grant 					if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
78167b1dce3SCameron Grant 						goto remok;
78267b1dce3SCameron Grant 				}
78367b1dce3SCameron Grant 				pcm_unlock(d);
78467b1dce3SCameron Grant 				return EINVAL;
78567b1dce3SCameron Grant remok:
78667b1dce3SCameron Grant 				err = vchan_destroy(c);
78767b1dce3SCameron Grant 				if (err == 0)
78867b1dce3SCameron Grant 					cnt--;
78967b1dce3SCameron Grant 			}
79067b1dce3SCameron Grant 		}
79167b1dce3SCameron Grant 	}
79267b1dce3SCameron Grant 
79367b1dce3SCameron Grant 	pcm_unlock(d);
79467b1dce3SCameron Grant 	return err;
79567b1dce3SCameron Grant }
79667b1dce3SCameron Grant #endif
79767b1dce3SCameron Grant 
79867b1dce3SCameron Grant /************************************************************************/
79967b1dce3SCameron Grant 
80033dbf14aSCameron Grant static moduledata_t sndpcm_mod = {
80133dbf14aSCameron Grant 	"snd_pcm",
802d95502a8SCameron Grant 	NULL,
80333dbf14aSCameron Grant 	NULL
80433dbf14aSCameron Grant };
80533dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
80633dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER);
807