xref: /freebsd/sys/dev/sound/pcm/sound.c (revision 7982e7a46543d924673edff37e1fe119a5ec5219)
1098ca2bdSWarner Losh /*-
23f225978SCameron Grant  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3987e5972SCameron Grant  * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
4987e5972SCameron Grant  * All rights reserved.
5987e5972SCameron Grant  *
6987e5972SCameron Grant  * Redistribution and use in source and binary forms, with or without
7987e5972SCameron Grant  * modification, are permitted provided that the following conditions
8987e5972SCameron Grant  * are met:
9987e5972SCameron Grant  * 1. Redistributions of source code must retain the above copyright
10987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer.
11987e5972SCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
12987e5972SCameron Grant  *    notice, this list of conditions and the following disclaimer in the
13987e5972SCameron Grant  *    documentation and/or other materials provided with the distribution.
14987e5972SCameron Grant  *
15987e5972SCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16987e5972SCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17987e5972SCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18987e5972SCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19987e5972SCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20987e5972SCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21987e5972SCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22987e5972SCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23987e5972SCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24987e5972SCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25987e5972SCameron Grant  * SUCH DAMAGE.
26987e5972SCameron Grant  */
27987e5972SCameron Grant 
28ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
29285648f9SCameron Grant #include <dev/sound/pcm/vchan.h>
305ee30e27SMathew Kanner #include <dev/sound/pcm/dsp.h>
317c438dbeSCameron Grant #include <sys/sysctl.h>
32285648f9SCameron Grant 
3367b1dce3SCameron Grant #include "feeder_if.h"
3467b1dce3SCameron Grant 
3567b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$");
3667b1dce3SCameron Grant 
37d95502a8SCameron Grant devclass_t pcm_devclass;
3882db23e2SCameron Grant 
39b8a36395SCameron Grant int pcm_veto_load = 1;
40b8a36395SCameron Grant 
4182db23e2SCameron Grant #ifdef USING_DEVFS
42d95502a8SCameron Grant int snd_unit = 0;
4309786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit);
4482db23e2SCameron Grant #endif
45cbe7d6a3SCameron Grant 
4667b1dce3SCameron Grant int snd_maxautovchans = 0;
4767b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
48987e5972SCameron Grant 
4982db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
5082db23e2SCameron Grant 
5167b1dce3SCameron Grant static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
5267b1dce3SCameron Grant 
5367b1dce3SCameron Grant struct sysctl_ctx_list *
5467b1dce3SCameron Grant snd_sysctl_tree(device_t dev)
5567b1dce3SCameron Grant {
5667b1dce3SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
5767b1dce3SCameron Grant 
5867b1dce3SCameron Grant 	return &d->sysctl_tree;
5967b1dce3SCameron Grant }
6067b1dce3SCameron Grant 
6167b1dce3SCameron Grant struct sysctl_oid *
6267b1dce3SCameron Grant snd_sysctl_tree_top(device_t dev)
6367b1dce3SCameron Grant {
6467b1dce3SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
6567b1dce3SCameron Grant 
6667b1dce3SCameron Grant 	return d->sysctl_tree_top;
6767b1dce3SCameron Grant }
6867b1dce3SCameron Grant 
6937209180SCameron Grant void *
702c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type)
7137209180SCameron Grant {
7237209180SCameron Grant #ifdef USING_MUTEX
7337209180SCameron Grant 	struct mtx *m;
7437209180SCameron Grant 
75a163d034SWarner Losh 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
7637209180SCameron Grant 	if (m == NULL)
7737209180SCameron Grant 		return NULL;
7812e524a2SDon Lewis 	mtx_init(m, desc, type, MTX_DEF);
7912e524a2SDon Lewis 	return m;
8012e524a2SDon Lewis #else
8112e524a2SDon Lewis 	return (void *)0xcafebabe;
8212e524a2SDon Lewis #endif
8312e524a2SDon Lewis }
8412e524a2SDon Lewis 
8537209180SCameron Grant void
8637209180SCameron Grant snd_mtxfree(void *m)
8737209180SCameron Grant {
8837209180SCameron Grant #ifdef USING_MUTEX
8937209180SCameron Grant 	struct mtx *mtx = m;
9037209180SCameron Grant 
9167beb5a5SCameron Grant 	/* mtx_assert(mtx, MA_OWNED); */
9237209180SCameron Grant 	mtx_destroy(mtx);
9337209180SCameron Grant 	free(mtx, M_DEVBUF);
9437209180SCameron Grant #endif
9537209180SCameron Grant }
9637209180SCameron Grant 
9737209180SCameron Grant void
9837209180SCameron Grant snd_mtxassert(void *m)
9937209180SCameron Grant {
10037209180SCameron Grant #ifdef USING_MUTEX
101f00f162aSCameron Grant #ifdef INVARIANTS
10237209180SCameron Grant 	struct mtx *mtx = m;
10337209180SCameron Grant 
10437209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
10537209180SCameron Grant #endif
106f00f162aSCameron Grant #endif
10737209180SCameron Grant }
10867beb5a5SCameron Grant /*
10937209180SCameron Grant void
11037209180SCameron Grant snd_mtxlock(void *m)
11137209180SCameron Grant {
11237209180SCameron Grant #ifdef USING_MUTEX
11337209180SCameron Grant 	struct mtx *mtx = m;
11437209180SCameron Grant 
11537209180SCameron Grant 	mtx_lock(mtx);
11637209180SCameron Grant #endif
11737209180SCameron Grant }
11837209180SCameron Grant 
11937209180SCameron Grant void
12037209180SCameron Grant snd_mtxunlock(void *m)
12137209180SCameron Grant {
12237209180SCameron Grant #ifdef USING_MUTEX
12337209180SCameron Grant 	struct mtx *mtx = m;
12437209180SCameron Grant 
12537209180SCameron Grant 	mtx_unlock(mtx);
12637209180SCameron Grant #endif
12737209180SCameron Grant }
12867beb5a5SCameron Grant */
12937209180SCameron Grant int
13037209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
13137209180SCameron Grant {
13237209180SCameron Grant #ifdef USING_MUTEX
13337209180SCameron Grant 	flags &= INTR_MPSAFE;
13446700f12SPeter Wemm 	flags |= INTR_TYPE_AV;
13537209180SCameron Grant #else
13646700f12SPeter Wemm 	flags = INTR_TYPE_AV;
13737209180SCameron Grant #endif
13837209180SCameron Grant 	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
13937209180SCameron Grant }
14037209180SCameron Grant 
141a527dbc7SCameron Grant #ifndef	PCM_DEBUG_MTX
14267b1dce3SCameron Grant void
14367b1dce3SCameron Grant pcm_lock(struct snddev_info *d)
14467b1dce3SCameron Grant {
14567b1dce3SCameron Grant 	snd_mtxlock(d->lock);
14667b1dce3SCameron Grant }
14767b1dce3SCameron Grant 
14867b1dce3SCameron Grant void
14967b1dce3SCameron Grant pcm_unlock(struct snddev_info *d)
15067b1dce3SCameron Grant {
15167b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
15267b1dce3SCameron Grant }
153a527dbc7SCameron Grant #endif
15467b1dce3SCameron Grant 
15567b1dce3SCameron Grant struct pcm_channel *
15667b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d)
15767b1dce3SCameron Grant {
15867b1dce3SCameron Grant 	return d->fakechan;
15967b1dce3SCameron Grant }
16067b1dce3SCameron Grant 
161b8f0d9e0SCameron Grant /* return a locked channel */
162285648f9SCameron Grant struct pcm_channel *
163506a5308SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
164285648f9SCameron Grant {
165285648f9SCameron Grant 	struct pcm_channel *c;
166285648f9SCameron Grant     	struct snddev_channel *sce;
167f637a36cSCameron Grant 	int err;
168285648f9SCameron Grant 
169f637a36cSCameron Grant 	/* scan for a free channel */
170285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
171285648f9SCameron Grant 		c = sce->channel;
17249c5e6e2SCameron Grant 		CHN_LOCK(c);
173285648f9SCameron Grant 		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
174506a5308SCameron Grant 			if (chnum == -1 || c->num == chnum) {
175285648f9SCameron Grant 				c->flags |= CHN_F_BUSY;
176b8f0d9e0SCameron Grant 				c->pid = pid;
177285648f9SCameron Grant 				return c;
178285648f9SCameron Grant 			}
179506a5308SCameron Grant 		}
18049c5e6e2SCameron Grant 		CHN_UNLOCK(c);
181285648f9SCameron Grant 	}
182f637a36cSCameron Grant 
183f637a36cSCameron Grant 	/* no channel available */
1847982e7a4SAriff Abdullah 	if (direction == PCMDIR_PLAY && d->vchancount > 0 &&
1857982e7a4SAriff Abdullah 			d->vchancount < snd_maxautovchans &&
1867982e7a4SAriff Abdullah 			d->devcount <= PCMMAXCHAN) {
187f637a36cSCameron Grant 		/* try to create a vchan */
188f637a36cSCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
189f637a36cSCameron Grant 			c = sce->channel;
19012e524a2SDon Lewis 			CHN_LOCK(c);
19197d69a96SAlexander Leidinger 			if ((c->flags & CHN_F_HAS_VCHAN) &&
19297d69a96SAlexander Leidinger 					!SLIST_EMPTY(&c->children)) {
193f637a36cSCameron Grant 				err = vchan_create(c);
19412e524a2SDon Lewis 				CHN_UNLOCK(c);
195f637a36cSCameron Grant 				if (!err)
196506a5308SCameron Grant 					return pcm_chnalloc(d, direction, pid, -1);
197f637a36cSCameron Grant 				else
198f637a36cSCameron Grant 					device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
19912e524a2SDon Lewis 			} else
20012e524a2SDon Lewis 				CHN_UNLOCK(c);
201f637a36cSCameron Grant 		}
202f637a36cSCameron Grant 	}
203f637a36cSCameron Grant 
204285648f9SCameron Grant 	return NULL;
205285648f9SCameron Grant }
206285648f9SCameron Grant 
207b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
208285648f9SCameron Grant int
209b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
210285648f9SCameron Grant {
211b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
212285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
213b8f0d9e0SCameron Grant 	c->pid = -1;
21449c5e6e2SCameron Grant 	CHN_UNLOCK(c);
215285648f9SCameron Grant 	return 0;
216285648f9SCameron Grant }
217285648f9SCameron Grant 
218285648f9SCameron Grant int
219285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
220285648f9SCameron Grant {
22149c5e6e2SCameron Grant 	int r;
22249c5e6e2SCameron Grant 
223b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
224285648f9SCameron Grant 	c->refcount += ref;
22549c5e6e2SCameron Grant 	r = c->refcount;
22649c5e6e2SCameron Grant 	return r;
227285648f9SCameron Grant }
228285648f9SCameron Grant 
22967b1dce3SCameron Grant int
23067b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta)
23167b1dce3SCameron Grant {
232a527dbc7SCameron Grant 	int r;
233a527dbc7SCameron Grant 
234a527dbc7SCameron Grant 	if (delta == 0)
23567b1dce3SCameron Grant 		return d->inprog;
236a527dbc7SCameron Grant 
237a527dbc7SCameron Grant 	/* backtrace(); */
238a527dbc7SCameron Grant 	pcm_lock(d);
239a527dbc7SCameron Grant 	d->inprog += delta;
240a527dbc7SCameron Grant 	r = d->inprog;
241a527dbc7SCameron Grant 	pcm_unlock(d);
242a527dbc7SCameron Grant 	return r;
24367b1dce3SCameron Grant }
24467b1dce3SCameron Grant 
24567b1dce3SCameron Grant static void
24667b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num)
24767b1dce3SCameron Grant {
24897d69a96SAlexander Leidinger 	struct pcm_channel *c, *ch;
24967b1dce3SCameron Grant     	struct snddev_channel *sce;
25067b1dce3SCameron Grant 	int err, done;
25167b1dce3SCameron Grant 
25297d69a96SAlexander Leidinger 	/*
25397d69a96SAlexander Leidinger 	 * XXX WOAH... NEED SUPER CLEANUP!!!
25497d69a96SAlexander Leidinger 	 * Robust, yet confusing. Understanding these will
25597d69a96SAlexander Leidinger 	 * cause your brain spinning like a Doki Doki Dynamo.
25697d69a96SAlexander Leidinger 	 */
25767b1dce3SCameron Grant 	if (num > 0 && d->vchancount == 0) {
25867b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
25967b1dce3SCameron Grant 			c = sce->channel;
26012e524a2SDon Lewis 			CHN_LOCK(c);
26197d69a96SAlexander Leidinger 			if ((c->direction == PCMDIR_PLAY) &&
26297d69a96SAlexander Leidinger 					!(c->flags & CHN_F_BUSY) &&
26397d69a96SAlexander Leidinger 					SLIST_EMPTY(&c->children)) {
26467b1dce3SCameron Grant 				c->flags |= CHN_F_BUSY;
26567b1dce3SCameron Grant 				err = vchan_create(c);
26667b1dce3SCameron Grant 				if (err) {
26767b1dce3SCameron Grant 					c->flags &= ~CHN_F_BUSY;
26812e524a2SDon Lewis 					device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
26997d69a96SAlexander Leidinger 				}
27012e524a2SDon Lewis 				CHN_UNLOCK(c);
27167b1dce3SCameron Grant 				return;
27267b1dce3SCameron Grant 			}
27312e524a2SDon Lewis 			CHN_UNLOCK(c);
27467b1dce3SCameron Grant 		}
27597d69a96SAlexander Leidinger 		return;
27667b1dce3SCameron Grant 	}
27767b1dce3SCameron Grant 	if (num == 0 && d->vchancount > 0) {
27897d69a96SAlexander Leidinger 		/*
27997d69a96SAlexander Leidinger 		 * XXX Keep retrying...
28097d69a96SAlexander Leidinger 		 */
28197d69a96SAlexander Leidinger 		for (done = 0; done < 1024; done++) {
28297d69a96SAlexander Leidinger 			ch = NULL;
28367b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
28467b1dce3SCameron Grant 				c = sce->channel;
28597d69a96SAlexander Leidinger 				CHN_LOCK(c);
28697d69a96SAlexander Leidinger 				if (c->direction == PCMDIR_PLAY &&
28797d69a96SAlexander Leidinger 						!(c->flags & CHN_F_BUSY) &&
28897d69a96SAlexander Leidinger 						(c->flags & CHN_F_VIRTUAL)) {
28997d69a96SAlexander Leidinger 					ch = c;
29097d69a96SAlexander Leidinger 					break;
29197d69a96SAlexander Leidinger 				}
29297d69a96SAlexander Leidinger 				CHN_UNLOCK(c);
29397d69a96SAlexander Leidinger 			}
29497d69a96SAlexander Leidinger 			if (ch != NULL) {
29597d69a96SAlexander Leidinger 				CHN_UNLOCK(ch);
296a527dbc7SCameron Grant 				snd_mtxlock(d->lock);
29797d69a96SAlexander Leidinger 				err = vchan_destroy(ch);
29867b1dce3SCameron Grant 				if (err)
29997d69a96SAlexander Leidinger 					device_printf(d->dev, "vchan_destroy(%s) == %d\n",
30097d69a96SAlexander Leidinger 								ch->name, err);
30197d69a96SAlexander Leidinger 				snd_mtxunlock(d->lock);
30297d69a96SAlexander Leidinger 			} else
30397d69a96SAlexander Leidinger 				return;
30467b1dce3SCameron Grant 		}
30597d69a96SAlexander Leidinger 		return;
30667b1dce3SCameron Grant 	}
30767b1dce3SCameron Grant }
30867b1dce3SCameron Grant 
30982db23e2SCameron Grant #ifdef USING_DEVFS
31033dbf14aSCameron Grant static int
311cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
31233dbf14aSCameron Grant {
313b8f0d9e0SCameron Grant 	struct snddev_info *d;
31433dbf14aSCameron Grant 	int error, unit;
31533dbf14aSCameron Grant 
31633dbf14aSCameron Grant 	unit = snd_unit;
31733dbf14aSCameron Grant 	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
31833dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
31974ffd138SCameron Grant 		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
320b8f0d9e0SCameron Grant 			return EINVAL;
321b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
322faeebea2SCameron Grant 		if (d == NULL || SLIST_EMPTY(&d->channels))
323b8f0d9e0SCameron Grant 			return EINVAL;
32433dbf14aSCameron Grant 		snd_unit = unit;
32533dbf14aSCameron Grant 	}
32633dbf14aSCameron Grant 	return (error);
32733dbf14aSCameron Grant }
328b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
329cd9766c5SCameron Grant             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
33082db23e2SCameron Grant #endif
331987e5972SCameron Grant 
332cd9766c5SCameron Grant static int
33367b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
334cd9766c5SCameron Grant {
33567b1dce3SCameron Grant 	struct snddev_info *d;
33667b1dce3SCameron Grant 	int i, v, error;
337cd9766c5SCameron Grant 
33867b1dce3SCameron Grant 	v = snd_maxautovchans;
339cd9766c5SCameron Grant 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
340cd9766c5SCameron Grant 	if (error == 0 && req->newptr != NULL) {
3417982e7a4SAriff Abdullah 		if (v < 0 || v > (PCMMAXCHAN + 1) || pcm_devclass == NULL)
342cd9766c5SCameron Grant 			return EINVAL;
34367b1dce3SCameron Grant 		if (v != snd_maxautovchans) {
34467b1dce3SCameron Grant 			for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
34567b1dce3SCameron Grant 				d = devclass_get_softc(pcm_devclass, i);
34667b1dce3SCameron Grant 				if (!d)
34767b1dce3SCameron Grant 					continue;
34897d69a96SAlexander Leidinger 				if (d->flags & SD_F_AUTOVCHAN) {
34997d69a96SAlexander Leidinger 					if (pcm_inprog(d, 1) == 1)
35067b1dce3SCameron Grant 						pcm_setmaxautovchans(d, v);
35197d69a96SAlexander Leidinger 					pcm_inprog(d, -1);
35297d69a96SAlexander Leidinger 				}
35367b1dce3SCameron Grant 			}
35467b1dce3SCameron Grant 		}
35567b1dce3SCameron Grant 		snd_maxautovchans = v;
356cd9766c5SCameron Grant 	}
357cd9766c5SCameron Grant 	return (error);
358cd9766c5SCameron Grant }
35967b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
36067b1dce3SCameron Grant             0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
361f637a36cSCameron Grant 
362285648f9SCameron Grant struct pcm_channel *
363285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
364987e5972SCameron Grant {
365285648f9SCameron Grant 	struct pcm_channel *ch;
36633dbf14aSCameron Grant 	char *dirs;
367a3193a9cSDon Lewis     	int direction, err, *pnum;
368987e5972SCameron Grant 
369285648f9SCameron Grant 	switch(dir) {
370285648f9SCameron Grant 	case PCMDIR_PLAY:
371285648f9SCameron Grant 		dirs = "play";
372a3193a9cSDon Lewis 		direction = PCMDIR_PLAY;
373a67fe5c1SCameron Grant 		pnum = &d->playcount;
374285648f9SCameron Grant 		break;
375a67fe5c1SCameron Grant 
376285648f9SCameron Grant 	case PCMDIR_REC:
377285648f9SCameron Grant 		dirs = "record";
378a3193a9cSDon Lewis 		direction = PCMDIR_REC;
379a67fe5c1SCameron Grant 		pnum = &d->reccount;
380285648f9SCameron Grant 		break;
381a67fe5c1SCameron Grant 
382285648f9SCameron Grant 	case PCMDIR_VIRTUAL:
383285648f9SCameron Grant 		dirs = "virtual";
384a3193a9cSDon Lewis 		direction = PCMDIR_PLAY;
385a67fe5c1SCameron Grant 		pnum = &d->vchancount;
386285648f9SCameron Grant 		break;
387a67fe5c1SCameron Grant 
388285648f9SCameron Grant 	default:
389285648f9SCameron Grant 		return NULL;
3909c326820SCameron Grant 	}
391285648f9SCameron Grant 
392a163d034SWarner Losh 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
393285648f9SCameron Grant 	if (!ch)
394285648f9SCameron Grant 		return NULL;
395285648f9SCameron Grant 
396a163d034SWarner Losh 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
397285648f9SCameron Grant 	if (!ch->methods) {
398285648f9SCameron Grant 		free(ch, M_DEVBUF);
399a67fe5c1SCameron Grant 
400285648f9SCameron Grant 		return NULL;
401285648f9SCameron Grant 	}
402285648f9SCameron Grant 
40367beb5a5SCameron Grant 	snd_mtxlock(d->lock);
404a67fe5c1SCameron Grant 	ch->num = (*pnum)++;
40567beb5a5SCameron Grant 	snd_mtxunlock(d->lock);
406a67fe5c1SCameron Grant 
407285648f9SCameron Grant 	ch->pid = -1;
408285648f9SCameron Grant 	ch->parentsnddev = d;
409285648f9SCameron Grant 	ch->parentchannel = parent;
410436c9b65SScott Long 	ch->dev = d->dev;
41167beb5a5SCameron Grant 	snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
412285648f9SCameron Grant 
413a3193a9cSDon Lewis 	err = chn_init(ch, devinfo, dir, direction);
4140f55ac6cSCameron Grant 	if (err) {
415a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
416285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
417285648f9SCameron Grant 		free(ch, M_DEVBUF);
41867beb5a5SCameron Grant 		snd_mtxlock(d->lock);
419a67fe5c1SCameron Grant 		(*pnum)--;
42067beb5a5SCameron Grant 		snd_mtxunlock(d->lock);
421a67fe5c1SCameron Grant 
422285648f9SCameron Grant 		return NULL;
423bbb5bf3dSCameron Grant 	}
424285648f9SCameron Grant 
425285648f9SCameron Grant 	return ch;
426285648f9SCameron Grant }
427285648f9SCameron Grant 
428285648f9SCameron Grant int
429285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
430285648f9SCameron Grant {
431a67fe5c1SCameron Grant 	struct snddev_info *d;
432285648f9SCameron Grant 	int err;
433285648f9SCameron Grant 
434a67fe5c1SCameron Grant 	d = ch->parentsnddev;
435285648f9SCameron Grant 	err = chn_kill(ch);
436285648f9SCameron Grant 	if (err) {
437a67fe5c1SCameron Grant 		device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
438285648f9SCameron Grant 		return err;
439285648f9SCameron Grant 	}
440285648f9SCameron Grant 
441285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
442285648f9SCameron Grant 	free(ch, M_DEVBUF);
443285648f9SCameron Grant 
444285648f9SCameron Grant 	return 0;
445285648f9SCameron Grant }
446285648f9SCameron Grant 
447285648f9SCameron Grant int
4485ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
449285648f9SCameron Grant {
45067b1dce3SCameron Grant     	struct snddev_channel *sce, *tmp, *after;
4515ee30e27SMathew Kanner     	int device = device_get_unit(d->dev);
4525ee30e27SMathew Kanner 
4535ee30e27SMathew Kanner 	/*
4545ee30e27SMathew Kanner 	 * Note it's confusing nomenclature.
4555ee30e27SMathew Kanner 	 * dev_t
4565ee30e27SMathew Kanner 	 * device -> pcm_device
4575ee30e27SMathew Kanner          * unit -> pcm_channel
4585ee30e27SMathew Kanner 	 * channel -> snddev_channel
4595ee30e27SMathew Kanner 	 * device_t
4605ee30e27SMathew Kanner 	 * unit -> pcm_device
4615ee30e27SMathew Kanner 	 */
462b8f0d9e0SCameron Grant 
463a163d034SWarner Losh 	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
464285648f9SCameron Grant 	if (!sce) {
465285648f9SCameron Grant 		return ENOMEM;
466285648f9SCameron Grant 	}
467285648f9SCameron Grant 
4687cf0e77aSOrion Hodson 	snd_mtxlock(d->lock);
469285648f9SCameron Grant 	sce->channel = ch;
47067b1dce3SCameron Grant 	if (SLIST_EMPTY(&d->channels)) {
471285648f9SCameron Grant 		SLIST_INSERT_HEAD(&d->channels, sce, link);
4727982e7a4SAriff Abdullah 		sce->chan_num = 0;
47367b1dce3SCameron Grant 	} else {
4747982e7a4SAriff Abdullah 		sce->chan_num = 0;
4757982e7a4SAriff Abdullah retry_search:
4767982e7a4SAriff Abdullah 		SLIST_FOREACH(tmp, &d->channels, link) {
4777982e7a4SAriff Abdullah 			if (tmp == NULL)
4787982e7a4SAriff Abdullah 				continue;
4797982e7a4SAriff Abdullah 			if (sce->chan_num == tmp->chan_num) {
4807982e7a4SAriff Abdullah 				sce->chan_num++;
4817982e7a4SAriff Abdullah 				goto retry_search;
4827982e7a4SAriff Abdullah 			}
4837982e7a4SAriff Abdullah 		}
4847982e7a4SAriff Abdullah 		/*
4857982e7a4SAriff Abdullah 		 * Don't overflow PCMMKMINOR / PCMMAXCHAN.
4867982e7a4SAriff Abdullah 		 */
4877982e7a4SAriff Abdullah 		if (sce->chan_num > PCMMAXCHAN) {
4887982e7a4SAriff Abdullah 			snd_mtxunlock(d->lock);
4897982e7a4SAriff Abdullah 			device_printf(d->dev,
4907982e7a4SAriff Abdullah 				"%s: WARNING: sce->chan_num overflow! (%d)\n",
4917982e7a4SAriff Abdullah 				__func__, sce->chan_num);
4927982e7a4SAriff Abdullah 			free(sce, M_DEVBUF);
4937982e7a4SAriff Abdullah 			return E2BIG;
4947982e7a4SAriff Abdullah 		}
49597d69a96SAlexander Leidinger 		/*
49697d69a96SAlexander Leidinger 		 * Micro optimization, channel ordering:
49797d69a96SAlexander Leidinger 		 * hw,hw,hw,vch,vch,vch,rec
49897d69a96SAlexander Leidinger 		 */
49967b1dce3SCameron Grant 		after = NULL;
50097d69a96SAlexander Leidinger 		if (ch->flags & CHN_F_VIRTUAL) {
50197d69a96SAlexander Leidinger 			/* virtual channel to the end */
50297d69a96SAlexander Leidinger 			SLIST_FOREACH(tmp, &d->channels, link) {
50397d69a96SAlexander Leidinger 				if (tmp->channel->direction == PCMDIR_REC)
50497d69a96SAlexander Leidinger 					break;
50597d69a96SAlexander Leidinger 				after = tmp;
50697d69a96SAlexander Leidinger 			}
50797d69a96SAlexander Leidinger 		} else {
50897d69a96SAlexander Leidinger 			if (ch->direction == PCMDIR_REC) {
50967b1dce3SCameron Grant 				SLIST_FOREACH(tmp, &d->channels, link) {
51067b1dce3SCameron Grant 					after = tmp;
51167b1dce3SCameron Grant 				}
51297d69a96SAlexander Leidinger 			} else {
51397d69a96SAlexander Leidinger 				SLIST_FOREACH(tmp, &d->channels, link) {
51497d69a96SAlexander Leidinger 					if (tmp->channel->direction == PCMDIR_REC)
51597d69a96SAlexander Leidinger 						break;
51697d69a96SAlexander Leidinger 					if (!(tmp->channel->flags & CHN_F_VIRTUAL))
51797d69a96SAlexander Leidinger 						after = tmp;
51897d69a96SAlexander Leidinger 				}
51997d69a96SAlexander Leidinger 			}
52097d69a96SAlexander Leidinger 		}
52197d69a96SAlexander Leidinger 		if (after == NULL) {
52297d69a96SAlexander Leidinger 			SLIST_INSERT_HEAD(&d->channels, sce, link);
52397d69a96SAlexander Leidinger 		} else {
52467b1dce3SCameron Grant 			SLIST_INSERT_AFTER(after, sce, link);
52567b1dce3SCameron Grant 		}
52697d69a96SAlexander Leidinger 	}
5277982e7a4SAriff Abdullah 	d->devcount++;
52867beb5a5SCameron Grant 	snd_mtxunlock(d->lock);
5295ee30e27SMathew Kanner 	sce->dsp_devt= make_dev(&dsp_cdevsw,
5305ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
5315ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
5325ee30e27SMathew Kanner 			device, sce->chan_num);
533285648f9SCameron Grant 
5345ee30e27SMathew Kanner 	sce->dspW_devt= make_dev(&dsp_cdevsw,
5355ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
5365ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
5375ee30e27SMathew Kanner 			device, sce->chan_num);
5385ee30e27SMathew Kanner 
5395ee30e27SMathew Kanner 	sce->audio_devt= make_dev(&dsp_cdevsw,
5405ee30e27SMathew Kanner 			PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
5415ee30e27SMathew Kanner 			UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
5425ee30e27SMathew Kanner 			device, sce->chan_num);
5435ee30e27SMathew Kanner 
544506a5308SCameron Grant 	if (ch->direction == PCMDIR_REC)
5455ee30e27SMathew Kanner 		sce->dspr_devt = make_dev(&dsp_cdevsw,
5465ee30e27SMathew Kanner 				PCMMKMINOR(device, SND_DEV_DSPREC,
5475ee30e27SMathew Kanner 					sce->chan_num), UID_ROOT, GID_WHEEL,
5485ee30e27SMathew Kanner 				0666, "dspr%d.%d", device, sce->chan_num);
549b8f0d9e0SCameron Grant 
55033dbf14aSCameron Grant 	return 0;
55133dbf14aSCameron Grant }
55233dbf14aSCameron Grant 
553285648f9SCameron Grant int
5545ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
55533dbf14aSCameron Grant {
556285648f9SCameron Grant     	struct snddev_channel *sce;
55745550658SPoul-Henning Kamp #if 0
558a527dbc7SCameron Grant 	int ourlock;
55933dbf14aSCameron Grant 
560a527dbc7SCameron Grant 	ourlock = 0;
561a527dbc7SCameron Grant 	if (!mtx_owned(d->lock)) {
56249c5e6e2SCameron Grant 		snd_mtxlock(d->lock);
563a527dbc7SCameron Grant 		ourlock = 1;
564a527dbc7SCameron Grant 	}
56545550658SPoul-Henning Kamp #endif
566a527dbc7SCameron Grant 
567285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
568285648f9SCameron Grant 		if (sce->channel == ch)
569285648f9SCameron Grant 			goto gotit;
57033dbf14aSCameron Grant 	}
57145550658SPoul-Henning Kamp #if 0
572a527dbc7SCameron Grant 	if (ourlock)
57349c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
57445550658SPoul-Henning Kamp #endif
575285648f9SCameron Grant 	return EINVAL;
576285648f9SCameron Grant gotit:
577285648f9SCameron Grant 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
57867beb5a5SCameron Grant 
57997d69a96SAlexander Leidinger 	if (ch->flags & CHN_F_VIRTUAL)
58067beb5a5SCameron Grant 		d->vchancount--;
58197d69a96SAlexander Leidinger 	else if (ch->direction == PCMDIR_REC)
58297d69a96SAlexander Leidinger 		d->reccount--;
58367beb5a5SCameron Grant 	else
58467beb5a5SCameron Grant 		d->playcount--;
58567beb5a5SCameron Grant 
58645550658SPoul-Henning Kamp #if 0
587a527dbc7SCameron Grant 	if (ourlock)
58849c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
58945550658SPoul-Henning Kamp #endif
59067beb5a5SCameron Grant 	free(sce, M_DEVBUF);
591285648f9SCameron Grant 
592987e5972SCameron Grant 	return 0;
593987e5972SCameron Grant }
594987e5972SCameron Grant 
595987e5972SCameron Grant int
596285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
597285648f9SCameron Grant {
598285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
59967b1dce3SCameron Grant 	struct pcm_channel *ch;
60067b1dce3SCameron Grant     	int err;
601285648f9SCameron Grant 
602285648f9SCameron Grant 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
603285648f9SCameron Grant 	if (!ch) {
604285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
605285648f9SCameron Grant 		return ENODEV;
606285648f9SCameron Grant 	}
607cd9766c5SCameron Grant 
6085ee30e27SMathew Kanner 	err = pcm_chn_add(d, ch);
609285648f9SCameron Grant 	if (err) {
610285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
611285648f9SCameron Grant 		pcm_chn_destroy(ch);
612cd9766c5SCameron Grant 		return err;
613cd9766c5SCameron Grant 	}
614cd9766c5SCameron Grant 
61512e524a2SDon Lewis 	CHN_LOCK(ch);
61661698e0cSAlexander Kabaev 	if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) &&
61761698e0cSAlexander Kabaev 	    ch->direction == PCMDIR_PLAY && d->vchancount == 0) {
618cd9766c5SCameron Grant 		ch->flags |= CHN_F_BUSY;
619cd9766c5SCameron Grant 		err = vchan_create(ch);
620cd9766c5SCameron Grant 		if (err) {
62167b1dce3SCameron Grant 			ch->flags &= ~CHN_F_BUSY;
62212e524a2SDon Lewis 			CHN_UNLOCK(ch);
62312e524a2SDon Lewis 			device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
62412e524a2SDon Lewis 			return err;
625cd9766c5SCameron Grant 		}
626285648f9SCameron Grant 	}
62712e524a2SDon Lewis 	CHN_UNLOCK(ch);
628285648f9SCameron Grant 
629285648f9SCameron Grant 	return err;
630285648f9SCameron Grant }
631285648f9SCameron Grant 
632285648f9SCameron Grant static int
633285648f9SCameron Grant pcm_killchan(device_t dev)
634285648f9SCameron Grant {
635285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
636285648f9SCameron Grant     	struct snddev_channel *sce;
637e33bee07SOlivier Houchard 	struct pcm_channel *ch;
638e33bee07SOlivier Houchard 	int error = 0;
639285648f9SCameron Grant 
640285648f9SCameron Grant 	sce = SLIST_FIRST(&d->channels);
641e33bee07SOlivier Houchard 	ch = sce->channel;
642285648f9SCameron Grant 
6435ee30e27SMathew Kanner 	error = pcm_chn_remove(d, sce->channel);
644e33bee07SOlivier Houchard 	if (error)
645e33bee07SOlivier Houchard 		return (error);
646e33bee07SOlivier Houchard 	return (pcm_chn_destroy(ch));
647285648f9SCameron Grant }
648285648f9SCameron Grant 
649285648f9SCameron Grant int
650987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
651987e5972SCameron Grant {
65266ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
65349c5e6e2SCameron Grant 
65449c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
655987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
65649c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
657987e5972SCameron Grant 	return 0;
658987e5972SCameron Grant }
659987e5972SCameron Grant 
660987e5972SCameron Grant u_int32_t
661987e5972SCameron Grant pcm_getflags(device_t dev)
662987e5972SCameron Grant {
66366ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
66449c5e6e2SCameron Grant 
665987e5972SCameron Grant 	return d->flags;
666987e5972SCameron Grant }
667987e5972SCameron Grant 
668987e5972SCameron Grant void
669987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
670987e5972SCameron Grant {
67166ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
672d95502a8SCameron Grant 
673987e5972SCameron Grant 	d->flags = val;
674987e5972SCameron Grant }
675987e5972SCameron Grant 
67639004e69SCameron Grant void *
67739004e69SCameron Grant pcm_getdevinfo(device_t dev)
67839004e69SCameron Grant {
67966ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
68049c5e6e2SCameron Grant 
68139004e69SCameron Grant 	return d->devinfo;
68239004e69SCameron Grant }
68339004e69SCameron Grant 
684a67fe5c1SCameron Grant unsigned int
685a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
686a67fe5c1SCameron Grant {
687a67fe5c1SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
6884e60be34SCameron Grant 	int sz, x;
689a67fe5c1SCameron Grant 
690a67fe5c1SCameron Grant 	sz = 0;
6914e60be34SCameron Grant 	if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
6924e60be34SCameron Grant 		x = sz;
693a67fe5c1SCameron Grant 		RANGE(sz, min, max);
6944e60be34SCameron Grant 		if (x != sz)
6954e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
6964e60be34SCameron Grant 		x = min;
6974e60be34SCameron Grant 		while (x < sz)
6984e60be34SCameron Grant 			x <<= 1;
6994e60be34SCameron Grant 		if (x > sz)
7004e60be34SCameron Grant 			x >>= 1;
7014e60be34SCameron Grant 		if (x != sz) {
7024e60be34SCameron Grant 			device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
7034e60be34SCameron Grant 			sz = x;
7044e60be34SCameron Grant 		}
7054e60be34SCameron Grant 	} else {
706a67fe5c1SCameron Grant 		sz = deflt;
7074e60be34SCameron Grant 	}
7084e60be34SCameron Grant 
709a67fe5c1SCameron Grant 	d->bufsz = sz;
710a67fe5c1SCameron Grant 
711a67fe5c1SCameron Grant 	return sz;
712a67fe5c1SCameron Grant }
713a67fe5c1SCameron Grant 
714987e5972SCameron Grant int
715987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
716987e5972SCameron Grant {
71766ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
718987e5972SCameron Grant 
719b8a36395SCameron Grant 	if (pcm_veto_load) {
720b8a36395SCameron Grant 		device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
721b8a36395SCameron Grant 
722b8a36395SCameron Grant 		return EINVAL;
723b8a36395SCameron Grant 	}
724b8a36395SCameron Grant 
7252c69ba87SJohn Baldwin 	d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
726285648f9SCameron Grant 
7277233ababSAlexander Leidinger #if 0
7287233ababSAlexander Leidinger 	/*
7297233ababSAlexander Leidinger 	 * d->flags should be cleared by the allocator of the softc.
7307233ababSAlexander Leidinger 	 * We cannot clear this field here because several devices set
7317233ababSAlexander Leidinger 	 * this flag before calling pcm_register().
7327233ababSAlexander Leidinger 	 */
733cd9766c5SCameron Grant 	d->flags = 0;
7347233ababSAlexander Leidinger #endif
735e4d5b250SCameron Grant 	d->dev = dev;
736987e5972SCameron Grant 	d->devinfo = devinfo;
737f637a36cSCameron Grant 	d->devcount = 0;
738506a5308SCameron Grant 	d->reccount = 0;
739a67fe5c1SCameron Grant 	d->playcount = 0;
740f637a36cSCameron Grant 	d->vchancount = 0;
741d95502a8SCameron Grant 	d->inprog = 0;
742833f7023SCameron Grant 
74345550658SPoul-Henning Kamp 	SLIST_INIT(&d->channels);
74445550658SPoul-Henning Kamp 
745d95502a8SCameron Grant 	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
746285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
747d95502a8SCameron Grant 
748285648f9SCameron Grant 	d->fakechan = fkchan_setup(dev);
749a3193a9cSDon Lewis 	chn_init(d->fakechan, NULL, 0, 0);
750987e5972SCameron Grant 
75182db23e2SCameron Grant #ifdef SND_DYNSYSCTL
752cc486d80SJohn Baldwin 	sysctl_ctx_init(&d->sysctl_tree);
753cc486d80SJohn Baldwin 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
754a3e893e0SJohn Baldwin 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
755cc486d80SJohn Baldwin 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
756a3e893e0SJohn Baldwin 	if (d->sysctl_tree_top == NULL) {
757cc486d80SJohn Baldwin 		sysctl_ctx_free(&d->sysctl_tree);
758cc486d80SJohn Baldwin 		goto no;
759cc486d80SJohn Baldwin 	}
760a67fe5c1SCameron Grant 	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
761a67fe5c1SCameron Grant             OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
76282db23e2SCameron Grant #endif
76397d69a96SAlexander Leidinger 	if (numplay > 0) {
76467b1dce3SCameron Grant 		vchan_initsys(dev);
765cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
76697d69a96SAlexander Leidinger 	}
767cd9766c5SCameron Grant 
76867b1dce3SCameron Grant 	sndstat_register(dev, d->status, sndstat_prepare_pcm);
769987e5972SCameron Grant     	return 0;
770987e5972SCameron Grant no:
77149c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
772987e5972SCameron Grant 	return ENXIO;
773987e5972SCameron Grant }
774987e5972SCameron Grant 
77533dbf14aSCameron Grant int
77633dbf14aSCameron Grant pcm_unregister(device_t dev)
7777c438dbeSCameron Grant {
77866ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
779285648f9SCameron Grant     	struct snddev_channel *sce;
780a67fe5c1SCameron Grant 	struct pcm_channel *ch;
7817c438dbeSCameron Grant 
78228ef3fb0SAlexander Leidinger 	if (sndstat_acquire() != 0) {
78328ef3fb0SAlexander Leidinger 		device_printf(dev, "unregister: sndstat busy\n");
78428ef3fb0SAlexander Leidinger 		return EBUSY;
78528ef3fb0SAlexander Leidinger 	}
78628ef3fb0SAlexander Leidinger 
78749c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
788d95502a8SCameron Grant 	if (d->inprog) {
7895c25132aSGeorge C A Reid 		device_printf(dev, "unregister: operation in progress\n");
7905c25132aSGeorge C A Reid 		snd_mtxunlock(d->lock);
79128ef3fb0SAlexander Leidinger 		sndstat_release();
7925c25132aSGeorge C A Reid 		return EBUSY;
7935c25132aSGeorge C A Reid 	}
7945ee30e27SMathew Kanner 
795285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
796a67fe5c1SCameron Grant 		ch = sce->channel;
797a67fe5c1SCameron Grant 		if (ch->refcount > 0) {
79821ed9908SCameron Grant 			device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
79949c5e6e2SCameron Grant 			snd_mtxunlock(d->lock);
80028ef3fb0SAlexander Leidinger 			sndstat_release();
801285648f9SCameron Grant 			return EBUSY;
802285648f9SCameron Grant 		}
803c9b53085SCameron Grant 	}
8045ee30e27SMathew Kanner 
8057233ababSAlexander Leidinger 	if (mixer_uninit(dev)) {
8067233ababSAlexander Leidinger 		device_printf(dev, "unregister: mixer busy\n");
8077233ababSAlexander Leidinger 		snd_mtxunlock(d->lock);
80828ef3fb0SAlexander Leidinger 		sndstat_release();
8097233ababSAlexander Leidinger 		return EBUSY;
8107233ababSAlexander Leidinger 	}
8117233ababSAlexander Leidinger 
8125ee30e27SMathew Kanner 	SLIST_FOREACH(sce, &d->channels, link) {
8137982e7a4SAriff Abdullah 		if (sce->dsp_devt) {
8145ee30e27SMathew Kanner 			destroy_dev(sce->dsp_devt);
8157982e7a4SAriff Abdullah 			sce->dsp_devt = NULL;
8167982e7a4SAriff Abdullah 		}
8177982e7a4SAriff Abdullah 		if (sce->dspW_devt) {
8185ee30e27SMathew Kanner 			destroy_dev(sce->dspW_devt);
8197982e7a4SAriff Abdullah 			sce->dspW_devt = NULL;
8207982e7a4SAriff Abdullah 		}
8217982e7a4SAriff Abdullah 		if (sce->audio_devt) {
8225ee30e27SMathew Kanner 			destroy_dev(sce->audio_devt);
8237982e7a4SAriff Abdullah 			sce->audio_devt = NULL;
8247982e7a4SAriff Abdullah 		}
8257982e7a4SAriff Abdullah 		if (sce->dspr_devt) {
8265ee30e27SMathew Kanner 			destroy_dev(sce->dspr_devt);
8277982e7a4SAriff Abdullah 			sce->dspr_devt = NULL;
8287982e7a4SAriff Abdullah 		}
8297982e7a4SAriff Abdullah 		d->devcount--;
8305ee30e27SMathew Kanner 	}
8315ee30e27SMathew Kanner 
83266ef8af5SCameron Grant #ifdef SND_DYNSYSCTL
83366ef8af5SCameron Grant 	d->sysctl_tree_top = NULL;
83466ef8af5SCameron Grant 	sysctl_ctx_free(&d->sysctl_tree);
83566ef8af5SCameron Grant #endif
836faeebea2SCameron Grant 	while (!SLIST_EMPTY(&d->channels))
837285648f9SCameron Grant 		pcm_killchan(dev);
8387c438dbeSCameron Grant 
83966ef8af5SCameron Grant 	chn_kill(d->fakechan);
84066ef8af5SCameron Grant 	fkchan_kill(d->fakechan);
84182db23e2SCameron Grant 
84245550658SPoul-Henning Kamp 	snd_mtxunlock(d->lock);
84349c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
84428ef3fb0SAlexander Leidinger 	sndstat_unregister(dev);
84528ef3fb0SAlexander Leidinger 	sndstat_release();
84633dbf14aSCameron Grant 	return 0;
84733dbf14aSCameron Grant }
8487c438dbeSCameron Grant 
84967b1dce3SCameron Grant /************************************************************************/
85067b1dce3SCameron Grant 
85167b1dce3SCameron Grant static int
85267b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
85367b1dce3SCameron Grant {
85467b1dce3SCameron Grant     	struct snddev_info *d;
85567b1dce3SCameron Grant     	struct snddev_channel *sce;
85667b1dce3SCameron Grant 	struct pcm_channel *c;
85767b1dce3SCameron Grant 	struct pcm_feeder *f;
85867b1dce3SCameron Grant     	int pc, rc, vc;
85967b1dce3SCameron Grant 
86067b1dce3SCameron Grant 	if (verbose < 1)
86167b1dce3SCameron Grant 		return 0;
86267b1dce3SCameron Grant 
86367b1dce3SCameron Grant 	d = device_get_softc(dev);
86467b1dce3SCameron Grant 	if (!d)
86567b1dce3SCameron Grant 		return ENXIO;
86667b1dce3SCameron Grant 
86767b1dce3SCameron Grant 	snd_mtxlock(d->lock);
86867b1dce3SCameron Grant 	if (!SLIST_EMPTY(&d->channels)) {
86967b1dce3SCameron Grant 		pc = rc = vc = 0;
87067b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
87167b1dce3SCameron Grant 			c = sce->channel;
87267b1dce3SCameron Grant 			if (c->direction == PCMDIR_PLAY) {
87367b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
87467b1dce3SCameron Grant 					vc++;
87567b1dce3SCameron Grant 				else
87667b1dce3SCameron Grant 					pc++;
87767b1dce3SCameron Grant 			} else
87867b1dce3SCameron Grant 				rc++;
87967b1dce3SCameron Grant 		}
880a67fe5c1SCameron Grant 		sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
88167b1dce3SCameron Grant 				(d->flags & SD_F_SIMPLEX)? "" : " duplex",
88267b1dce3SCameron Grant #ifdef USING_DEVFS
88367b1dce3SCameron Grant 				(device_get_unit(dev) == snd_unit)? " default" : ""
88467b1dce3SCameron Grant #else
88567b1dce3SCameron Grant 				""
88667b1dce3SCameron Grant #endif
88767b1dce3SCameron Grant 				);
888a527dbc7SCameron Grant 
889a527dbc7SCameron Grant 		if (verbose <= 1) {
890a527dbc7SCameron Grant 			snd_mtxunlock(d->lock);
891a527dbc7SCameron Grant 			return 0;
892a527dbc7SCameron Grant 		}
893a527dbc7SCameron Grant 
89467b1dce3SCameron Grant 		SLIST_FOREACH(sce, &d->channels, link) {
89567b1dce3SCameron Grant 			c = sce->channel;
8969d978cc7SAlexander Leidinger 
8979d978cc7SAlexander Leidinger 			KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
8989d978cc7SAlexander Leidinger 				("hosed pcm channel setup"));
8999d978cc7SAlexander Leidinger 
900a3285889SCameron Grant 			sbuf_printf(s, "\n\t");
901a3285889SCameron Grant 
90245550658SPoul-Henning Kamp 			/* it would be better to indent child channels */
903a3285889SCameron Grant 			sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
9044c68642aSCameron Grant 			sbuf_printf(s, "spd %d", c->speed);
9054c68642aSCameron Grant 			if (c->speed != sndbuf_getspd(c->bufhard))
9064c68642aSCameron Grant 				sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
9074c68642aSCameron Grant 			sbuf_printf(s, ", fmt 0x%08x", c->format);
9084c68642aSCameron Grant 			if (c->format != sndbuf_getfmt(c->bufhard))
9094c68642aSCameron Grant 				sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
910a527dbc7SCameron Grant 			sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
91167b1dce3SCameron Grant 			if (c->pid != -1)
91267b1dce3SCameron Grant 				sbuf_printf(s, ", pid %d", c->pid);
91367b1dce3SCameron Grant 			sbuf_printf(s, "\n\t");
9144c68642aSCameron Grant 
915a3285889SCameron Grant 			sbuf_printf(s, "interrupts %d, ", c->interrupts);
916a3285889SCameron Grant 			if (c->direction == PCMDIR_REC)
91797d69a96SAlexander Leidinger 				sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
91897d69a96SAlexander Leidinger 					c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
91997d69a96SAlexander Leidinger 					sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
92097d69a96SAlexander Leidinger 					sndbuf_getblkcnt(c->bufhard),
92197d69a96SAlexander Leidinger 					sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
92297d69a96SAlexander Leidinger 					sndbuf_getblkcnt(c->bufsoft));
923a3285889SCameron Grant 			else
92497d69a96SAlexander Leidinger 				sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
92597d69a96SAlexander Leidinger 					c->xruns, sndbuf_getready(c->bufsoft),
92697d69a96SAlexander Leidinger 					sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
92797d69a96SAlexander Leidinger 					sndbuf_getblkcnt(c->bufhard),
92897d69a96SAlexander Leidinger 					sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
92997d69a96SAlexander Leidinger 					sndbuf_getblkcnt(c->bufsoft));
930a3285889SCameron Grant 			sbuf_printf(s, "\n\t");
9314c68642aSCameron Grant 
9324c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
9334c68642aSCameron Grant 			sbuf_printf(s, " -> ");
93467b1dce3SCameron Grant 			f = c->feeder;
9354c68642aSCameron Grant 			while (f->source != NULL)
9364c68642aSCameron Grant 				f = f->source;
9374c68642aSCameron Grant 			while (f != NULL) {
93867b1dce3SCameron Grant 				sbuf_printf(s, "%s", f->class->name);
93967b1dce3SCameron Grant 				if (f->desc->type == FEEDER_FMT)
9404c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
94167b1dce3SCameron Grant 				if (f->desc->type == FEEDER_RATE)
9424c68642aSCameron Grant 					sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
94328ef3fb0SAlexander Leidinger 				if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
94428ef3fb0SAlexander Leidinger 						f->desc->type == FEEDER_VOLUME)
9454c68642aSCameron Grant 					sbuf_printf(s, "(0x%08x)", f->desc->out);
9464c68642aSCameron Grant 				sbuf_printf(s, " -> ");
9474c68642aSCameron Grant 				f = f->parent;
94867b1dce3SCameron Grant 			}
9494c68642aSCameron Grant 			sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
95067b1dce3SCameron Grant 		}
95167b1dce3SCameron Grant 	} else
95267b1dce3SCameron Grant 		sbuf_printf(s, " (mixer only)");
95367b1dce3SCameron Grant 	snd_mtxunlock(d->lock);
95467b1dce3SCameron Grant 
95567b1dce3SCameron Grant 	return 0;
95667b1dce3SCameron Grant }
95767b1dce3SCameron Grant 
95867b1dce3SCameron Grant /************************************************************************/
95967b1dce3SCameron Grant 
96067b1dce3SCameron Grant #ifdef SND_DYNSYSCTL
96167b1dce3SCameron Grant int
96267b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
96367b1dce3SCameron Grant {
96467b1dce3SCameron Grant 	struct snddev_info *d;
96567b1dce3SCameron Grant     	struct snddev_channel *sce;
96667b1dce3SCameron Grant 	struct pcm_channel *c;
96797d69a96SAlexander Leidinger 	int err, newcnt, cnt;
96867b1dce3SCameron Grant 
96997d69a96SAlexander Leidinger 	/*
97097d69a96SAlexander Leidinger 	 * XXX WOAH... NEED SUPER CLEANUP!!!
97197d69a96SAlexander Leidinger 	 * Robust, yet confusing. Understanding these will
97297d69a96SAlexander Leidinger 	 * cause your brain spinning like a Doki Doki Dynamo.
97397d69a96SAlexander Leidinger 	 */
97467b1dce3SCameron Grant 	d = oidp->oid_arg1;
97567b1dce3SCameron Grant 
97697d69a96SAlexander Leidinger 	if (!(d->flags & SD_F_AUTOVCHAN)) {
977a527dbc7SCameron Grant 		pcm_inprog(d, -1);
97897d69a96SAlexander Leidinger 		return EINVAL;
979a527dbc7SCameron Grant 	}
980a527dbc7SCameron Grant 
98167b1dce3SCameron Grant 	cnt = 0;
98267b1dce3SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
98367b1dce3SCameron Grant 		c = sce->channel;
98412e524a2SDon Lewis 		CHN_LOCK(c);
985a527dbc7SCameron Grant 		if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) {
98667b1dce3SCameron Grant 			cnt++;
98797d69a96SAlexander Leidinger 			if (req->newptr != NULL && c->flags & CHN_F_BUSY) {
98897d69a96SAlexander Leidinger 				/* Better safe than sorry */
98997d69a96SAlexander Leidinger 				CHN_UNLOCK(c);
99097d69a96SAlexander Leidinger 				return EBUSY;
99197d69a96SAlexander Leidinger 			}
99267b1dce3SCameron Grant 		}
99312e524a2SDon Lewis 		CHN_UNLOCK(c);
994a527dbc7SCameron Grant 	}
995a527dbc7SCameron Grant 
99667b1dce3SCameron Grant 	newcnt = cnt;
99767b1dce3SCameron Grant 
99867b1dce3SCameron Grant 	err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
999a527dbc7SCameron Grant 
100067b1dce3SCameron Grant 	if (err == 0 && req->newptr != NULL) {
1001a527dbc7SCameron Grant 
10027982e7a4SAriff Abdullah 		if (newcnt < 0 || newcnt > (PCMMAXCHAN + 1) ||
10037982e7a4SAriff Abdullah 				(d->playcount + d->reccount + newcnt) > (PCMMAXCHAN + 1))
1004a527dbc7SCameron Grant 			return E2BIG;
100597d69a96SAlexander Leidinger 
100697d69a96SAlexander Leidinger 		if (pcm_inprog(d, 1) != 1) {
100797d69a96SAlexander Leidinger 			pcm_inprog(d, -1);
100897d69a96SAlexander Leidinger 			return EINPROGRESS;
100967b1dce3SCameron Grant 		}
101067b1dce3SCameron Grant 
101167b1dce3SCameron Grant 		if (newcnt > cnt) {
101267b1dce3SCameron Grant 			/* add new vchans - find a parent channel first */
101367b1dce3SCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
101467b1dce3SCameron Grant 				c = sce->channel;
101512e524a2SDon Lewis 				CHN_LOCK(c);
101667b1dce3SCameron Grant 				/* not a candidate if not a play channel */
101767b1dce3SCameron Grant 				if (c->direction != PCMDIR_PLAY)
101812e524a2SDon Lewis 					goto next;
101967b1dce3SCameron Grant 				/* not a candidate if a virtual channel */
102067b1dce3SCameron Grant 				if (c->flags & CHN_F_VIRTUAL)
102112e524a2SDon Lewis 					goto next;
102267b1dce3SCameron Grant 				/* not a candidate if it's in use */
102312e524a2SDon Lewis 				if (!(c->flags & CHN_F_BUSY) ||
102412e524a2SDon Lewis 				    !(SLIST_EMPTY(&c->children)))
102567b1dce3SCameron Grant 					/*
102612e524a2SDon Lewis 					 * if we get here we're a nonvirtual
102712e524a2SDon Lewis 					 * play channel, and either
102867b1dce3SCameron Grant 					 * 1) not busy
102912e524a2SDon Lewis 					 * 2) busy with children, not directly
103012e524a2SDon Lewis 					 *    open
103167b1dce3SCameron Grant 					 *
103267b1dce3SCameron Grant 					 * thus we can add children
103367b1dce3SCameron Grant 					 */
103467b1dce3SCameron Grant 					goto addok;
103512e524a2SDon Lewis next:
103612e524a2SDon Lewis 				CHN_UNLOCK(c);
103767b1dce3SCameron Grant 			}
1038a527dbc7SCameron Grant 			pcm_inprog(d, -1);
103967b1dce3SCameron Grant 			return EBUSY;
104067b1dce3SCameron Grant addok:
104167b1dce3SCameron Grant 			c->flags |= CHN_F_BUSY;
104267b1dce3SCameron Grant 			while (err == 0 && newcnt > cnt) {
10437982e7a4SAriff Abdullah 				if (d->devcount > PCMMAXCHAN) {
10447982e7a4SAriff Abdullah 					device_printf(d->dev, "%s: Maximum channel reached.\n", __func__);
10457982e7a4SAriff Abdullah 					break;
10467982e7a4SAriff Abdullah 				}
104767b1dce3SCameron Grant 				err = vchan_create(c);
104867b1dce3SCameron Grant 				if (err == 0)
104967b1dce3SCameron Grant 					cnt++;
10507982e7a4SAriff Abdullah 				if (newcnt > cnt && err == E2BIG) {
10517982e7a4SAriff Abdullah 					device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err);
10527982e7a4SAriff Abdullah 					err = 0;
10537982e7a4SAriff Abdullah 					break;
10547982e7a4SAriff Abdullah 				}
105567b1dce3SCameron Grant 			}
105612e524a2SDon Lewis 			CHN_UNLOCK(c);
105767b1dce3SCameron Grant 		} else if (newcnt < cnt) {
1058a527dbc7SCameron Grant 			snd_mtxlock(d->lock);
105967b1dce3SCameron Grant 			while (err == 0 && newcnt < cnt) {
10607982e7a4SAriff Abdullah 				c = NULL;
106167b1dce3SCameron Grant 				SLIST_FOREACH(sce, &d->channels, link) {
10627982e7a4SAriff Abdullah 					CHN_LOCK(sce->channel);
10637982e7a4SAriff Abdullah 					if (sce->channel->direction == PCMDIR_PLAY &&
10647982e7a4SAriff Abdullah 							(sce->channel->flags & CHN_F_VIRTUAL))
106567b1dce3SCameron Grant 						c = sce->channel;
10667982e7a4SAriff Abdullah 					CHN_UNLOCK(sce->channel);
106767b1dce3SCameron Grant 				}
10687982e7a4SAriff Abdullah 				if (c != NULL)
10697982e7a4SAriff Abdullah 					goto remok;
1070a527dbc7SCameron Grant 				snd_mtxunlock(d->lock);
1071a527dbc7SCameron Grant 				pcm_inprog(d, -1);
107267b1dce3SCameron Grant 				return EINVAL;
107367b1dce3SCameron Grant remok:
107467b1dce3SCameron Grant 				err = vchan_destroy(c);
107567b1dce3SCameron Grant 				if (err == 0)
107667b1dce3SCameron Grant 					cnt--;
107767b1dce3SCameron Grant 			}
1078a527dbc7SCameron Grant 			snd_mtxunlock(d->lock);
107967b1dce3SCameron Grant 		}
1080a527dbc7SCameron Grant 		pcm_inprog(d, -1);
108197d69a96SAlexander Leidinger 	}
108267b1dce3SCameron Grant 	return err;
108367b1dce3SCameron Grant }
108467b1dce3SCameron Grant #endif
108567b1dce3SCameron Grant 
108667b1dce3SCameron Grant /************************************************************************/
108767b1dce3SCameron Grant 
10880739ea1dSSeigo Tanimura static int
10890739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data)
10900739ea1dSSeigo Tanimura {
10917233ababSAlexander Leidinger #if 0
10920739ea1dSSeigo Tanimura 	return (midi_modevent(mod, type, data));
10937233ababSAlexander Leidinger #else
10947233ababSAlexander Leidinger 	return 0;
10957233ababSAlexander Leidinger #endif
10960739ea1dSSeigo Tanimura }
10970739ea1dSSeigo Tanimura 
10980739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL);
10990739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER);
1100