xref: /freebsd/sys/dev/sound/pcm/sound.c (revision cbe7d6a3edd202aa01c14ea264865d01ec0a268b)
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  *
2753c5a968SPeter Wemm  * $FreeBSD$
28987e5972SCameron Grant  */
29987e5972SCameron Grant 
30ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h>
31285648f9SCameron Grant #include <dev/sound/pcm/vchan.h>
327c438dbeSCameron Grant #include <sys/sysctl.h>
33285648f9SCameron Grant 
34d95502a8SCameron Grant devclass_t pcm_devclass;
3582db23e2SCameron Grant 
3682db23e2SCameron Grant #ifdef USING_DEVFS
37d95502a8SCameron Grant int snd_unit = 0;
3809786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit);
3982db23e2SCameron Grant #endif
40cbe7d6a3SCameron Grant 
41cd9766c5SCameron Grant int snd_autovchans = 0;
42f637a36cSCameron Grant int snd_maxvchans = 0;
43bc0e6469SBrian Feldman #if __FreeBSD_version > 500000
440cfa4761SBrian Feldman TUNABLE_INT("hw.snd.autovchans", &snd_autovchans);
45f637a36cSCameron Grant TUNABLE_INT("hw.snd.maxvchans", &snd_maxvchans);
460cfa4761SBrian Feldman #else
470cfa4761SBrian Feldman TUNABLE_INT("hw.snd.autovchans", 0, snd_autovchans);
480cfa4761SBrian Feldman TUNABLE_INT("hw.snd.maxvchans", 0, snd_maxvchans);
490cfa4761SBrian Feldman #endif
50987e5972SCameron Grant 
5182db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
5282db23e2SCameron Grant 
5337209180SCameron Grant void *
5437209180SCameron Grant snd_mtxcreate(const char *desc)
5537209180SCameron Grant {
5637209180SCameron Grant #ifdef USING_MUTEX
5737209180SCameron Grant 	struct mtx *m;
5837209180SCameron Grant 
5937209180SCameron Grant 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
6037209180SCameron Grant 	if (m == NULL)
6137209180SCameron Grant 		return NULL;
6237209180SCameron Grant 	mtx_init(m, desc, MTX_RECURSE);
6337209180SCameron Grant 	return m;
6437209180SCameron Grant #else
65a983d575SCameron Grant 	return (void *)0xcafebabe;
6637209180SCameron Grant #endif
6737209180SCameron Grant }
6837209180SCameron Grant 
6937209180SCameron Grant void
7037209180SCameron Grant snd_mtxfree(void *m)
7137209180SCameron Grant {
7237209180SCameron Grant #ifdef USING_MUTEX
7337209180SCameron Grant 	struct mtx *mtx = m;
7437209180SCameron Grant 
7537209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
7637209180SCameron Grant 	mtx_destroy(mtx);
7737209180SCameron Grant 	free(mtx, M_DEVBUF);
7837209180SCameron Grant #endif
7937209180SCameron Grant }
8037209180SCameron Grant 
8137209180SCameron Grant void
8237209180SCameron Grant snd_mtxassert(void *m)
8337209180SCameron Grant {
8437209180SCameron Grant #ifdef USING_MUTEX
85f00f162aSCameron Grant #ifdef INVARIANTS
8637209180SCameron Grant 	struct mtx *mtx = m;
8737209180SCameron Grant 
8837209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
8937209180SCameron Grant #endif
90f00f162aSCameron Grant #endif
9137209180SCameron Grant }
9237209180SCameron Grant 
9337209180SCameron Grant void
9437209180SCameron Grant snd_mtxlock(void *m)
9537209180SCameron Grant {
9637209180SCameron Grant #ifdef USING_MUTEX
9737209180SCameron Grant 	struct mtx *mtx = m;
9837209180SCameron Grant 
9937209180SCameron Grant 	mtx_lock(mtx);
10037209180SCameron Grant #endif
10137209180SCameron Grant }
10237209180SCameron Grant 
10337209180SCameron Grant void
10437209180SCameron Grant snd_mtxunlock(void *m)
10537209180SCameron Grant {
10637209180SCameron Grant #ifdef USING_MUTEX
10737209180SCameron Grant 	struct mtx *mtx = m;
10837209180SCameron Grant 
10937209180SCameron Grant 	mtx_unlock(mtx);
11037209180SCameron Grant #endif
11137209180SCameron Grant }
11237209180SCameron Grant 
11337209180SCameron Grant int
11437209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
11537209180SCameron Grant {
11637209180SCameron Grant #ifdef USING_MUTEX
11737209180SCameron Grant 	flags &= INTR_MPSAFE;
11846700f12SPeter Wemm 	flags |= INTR_TYPE_AV;
11937209180SCameron Grant #else
12046700f12SPeter Wemm 	flags = INTR_TYPE_AV;
12137209180SCameron Grant #endif
12237209180SCameron Grant 	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
12337209180SCameron Grant }
12437209180SCameron Grant 
125b8f0d9e0SCameron Grant /* return a locked channel */
126285648f9SCameron Grant struct pcm_channel *
127b8f0d9e0SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid)
128285648f9SCameron Grant {
129285648f9SCameron Grant 	struct pcm_channel *c;
130285648f9SCameron Grant     	struct snddev_channel *sce;
131f637a36cSCameron Grant 	int err;
132285648f9SCameron Grant 
133b8f0d9e0SCameron Grant 	snd_mtxassert(d->lock);
134f637a36cSCameron Grant 
135f637a36cSCameron Grant 	/* scan for a free channel */
136285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
137285648f9SCameron Grant 		c = sce->channel;
13849c5e6e2SCameron Grant 		CHN_LOCK(c);
139285648f9SCameron Grant 		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
140285648f9SCameron Grant 			c->flags |= CHN_F_BUSY;
141b8f0d9e0SCameron Grant 			c->pid = pid;
142285648f9SCameron Grant 			return c;
143285648f9SCameron Grant 		}
14449c5e6e2SCameron Grant 		CHN_UNLOCK(c);
145285648f9SCameron Grant 	}
146f637a36cSCameron Grant 
147f637a36cSCameron Grant 	/* no channel available */
148f637a36cSCameron Grant 	if (direction == PCMDIR_PLAY) {
149f637a36cSCameron Grant 		if ((d->vchancount > 0) && (d->vchancount < snd_maxvchans)) {
150f637a36cSCameron Grant 			/* try to create a vchan */
151f637a36cSCameron Grant 			SLIST_FOREACH(sce, &d->channels, link) {
152f637a36cSCameron Grant 				c = sce->channel;
153f637a36cSCameron Grant 				if (!SLIST_EMPTY(&c->children)) {
154f637a36cSCameron Grant 					err = vchan_create(c);
155f637a36cSCameron Grant 					if (!err)
156f637a36cSCameron Grant 						return pcm_chnalloc(d, direction, pid);
157f637a36cSCameron Grant 					else
158f637a36cSCameron Grant 						device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
159f637a36cSCameron Grant 				}
160f637a36cSCameron Grant 			}
161f637a36cSCameron Grant 		}
162f637a36cSCameron Grant 	}
163f637a36cSCameron Grant 
164285648f9SCameron Grant 	return NULL;
165285648f9SCameron Grant }
166285648f9SCameron Grant 
167b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
168285648f9SCameron Grant int
169b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
170285648f9SCameron Grant {
171b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
172285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
173b8f0d9e0SCameron Grant 	c->pid = -1;
17449c5e6e2SCameron Grant 	CHN_UNLOCK(c);
175285648f9SCameron Grant 	return 0;
176285648f9SCameron Grant }
177285648f9SCameron Grant 
178285648f9SCameron Grant int
179285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
180285648f9SCameron Grant {
18149c5e6e2SCameron Grant 	int r;
18249c5e6e2SCameron Grant 
183b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
184285648f9SCameron Grant 	c->refcount += ref;
18549c5e6e2SCameron Grant 	r = c->refcount;
18649c5e6e2SCameron Grant 	return r;
187285648f9SCameron Grant }
188285648f9SCameron Grant 
18982db23e2SCameron Grant #ifdef USING_DEVFS
19033dbf14aSCameron Grant static int
191cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
19233dbf14aSCameron Grant {
193b8f0d9e0SCameron Grant 	struct snddev_info *d;
19433dbf14aSCameron Grant 	int error, unit;
19533dbf14aSCameron Grant 
19633dbf14aSCameron Grant 	unit = snd_unit;
19733dbf14aSCameron Grant 	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
19833dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
19974ffd138SCameron Grant 		if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
200b8f0d9e0SCameron Grant 			return EINVAL;
201b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
202faeebea2SCameron Grant 		if (d == NULL || SLIST_EMPTY(&d->channels))
203b8f0d9e0SCameron Grant 			return EINVAL;
20433dbf14aSCameron Grant 		snd_unit = unit;
20533dbf14aSCameron Grant 	}
20633dbf14aSCameron Grant 	return (error);
20733dbf14aSCameron Grant }
208b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
209cd9766c5SCameron Grant             0, sizeof(int), sysctl_hw_snd_unit, "I", "");
21082db23e2SCameron Grant #endif
211987e5972SCameron Grant 
212cd9766c5SCameron Grant static int
213cd9766c5SCameron Grant sysctl_hw_snd_autovchans(SYSCTL_HANDLER_ARGS)
214cd9766c5SCameron Grant {
215cd9766c5SCameron Grant 	int v, error;
216cd9766c5SCameron Grant 
217cd9766c5SCameron Grant 	v = snd_autovchans;
218cd9766c5SCameron Grant 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
219cd9766c5SCameron Grant 	if (error == 0 && req->newptr != NULL) {
220cd9766c5SCameron Grant 		if (v < 0 || v >= SND_MAXVCHANS)
221cd9766c5SCameron Grant 			return EINVAL;
222cd9766c5SCameron Grant 		snd_autovchans = v;
223cd9766c5SCameron Grant 	}
224cd9766c5SCameron Grant 	return (error);
225cd9766c5SCameron Grant }
226cd9766c5SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, autovchans, CTLTYPE_INT | CTLFLAG_RW,
227cd9766c5SCameron Grant             0, sizeof(int), sysctl_hw_snd_autovchans, "I", "");
228cd9766c5SCameron Grant 
229f637a36cSCameron Grant static int
230f637a36cSCameron Grant sysctl_hw_snd_maxvchans(SYSCTL_HANDLER_ARGS)
231f637a36cSCameron Grant {
232f637a36cSCameron Grant 	int v, error;
233f637a36cSCameron Grant 
234f637a36cSCameron Grant 	v = snd_maxvchans;
235f637a36cSCameron Grant 	error = sysctl_handle_int(oidp, &v, sizeof(v), req);
236f637a36cSCameron Grant 	if (error == 0 && req->newptr != NULL) {
237f637a36cSCameron Grant 		if (v < 0 || v >= SND_MAXVCHANS)
238f637a36cSCameron Grant 			return EINVAL;
239f637a36cSCameron Grant 		snd_maxvchans = v;
240f637a36cSCameron Grant 	}
241f637a36cSCameron Grant 	return (error);
242f637a36cSCameron Grant }
243f637a36cSCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxvchans, CTLTYPE_INT | CTLFLAG_RW,
244f637a36cSCameron Grant             0, sizeof(int), sysctl_hw_snd_maxvchans, "I", "");
245f637a36cSCameron Grant 
246285648f9SCameron Grant struct pcm_channel *
247285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
248987e5972SCameron Grant {
249285648f9SCameron Grant 	struct pcm_channel *ch;
25033dbf14aSCameron Grant 	char *dirs;
2510f55ac6cSCameron Grant     	int err;
252987e5972SCameron Grant 
253285648f9SCameron Grant 	switch(dir) {
254285648f9SCameron Grant 	case PCMDIR_PLAY:
255285648f9SCameron Grant 		dirs = "play";
256285648f9SCameron Grant 		break;
257285648f9SCameron Grant 	case PCMDIR_REC:
258285648f9SCameron Grant 		dirs = "record";
259285648f9SCameron Grant 		break;
260285648f9SCameron Grant 	case PCMDIR_VIRTUAL:
261285648f9SCameron Grant 		dirs = "virtual";
262285648f9SCameron Grant 		dir = PCMDIR_PLAY;
263285648f9SCameron Grant 		break;
264285648f9SCameron Grant 	default:
265285648f9SCameron Grant 		return NULL;
2669c326820SCameron Grant 	}
267285648f9SCameron Grant 
268285648f9SCameron Grant 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
269285648f9SCameron Grant 	if (!ch)
270285648f9SCameron Grant 		return NULL;
271285648f9SCameron Grant 
2720f55ac6cSCameron Grant 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
273285648f9SCameron Grant 	if (!ch->methods) {
274285648f9SCameron Grant 		free(ch, M_DEVBUF);
275285648f9SCameron Grant 		return NULL;
276285648f9SCameron Grant 	}
277285648f9SCameron Grant 
278285648f9SCameron Grant 	ch->pid = -1;
279285648f9SCameron Grant 	ch->parentsnddev = d;
280285648f9SCameron Grant 	ch->parentchannel = parent;
281285648f9SCameron Grant 	snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs);
282285648f9SCameron Grant 
2830f55ac6cSCameron Grant 	err = chn_init(ch, devinfo, dir);
2840f55ac6cSCameron Grant 	if (err) {
285285648f9SCameron Grant 		device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err);
286285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
287285648f9SCameron Grant 		free(ch, M_DEVBUF);
288285648f9SCameron Grant 		return NULL;
289bbb5bf3dSCameron Grant 	}
290285648f9SCameron Grant 
291285648f9SCameron Grant 	return ch;
292285648f9SCameron Grant }
293285648f9SCameron Grant 
294285648f9SCameron Grant int
295285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
296285648f9SCameron Grant {
297285648f9SCameron Grant 	int err;
298285648f9SCameron Grant 
299285648f9SCameron Grant 	err = chn_kill(ch);
300285648f9SCameron Grant 	if (err) {
301285648f9SCameron Grant 		device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err);
302285648f9SCameron Grant 		return err;
303285648f9SCameron Grant 	}
304285648f9SCameron Grant 
305285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
306285648f9SCameron Grant 	free(ch, M_DEVBUF);
307285648f9SCameron Grant 
308285648f9SCameron Grant 	return 0;
309285648f9SCameron Grant }
310285648f9SCameron Grant 
311285648f9SCameron Grant int
312f637a36cSCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
313285648f9SCameron Grant {
314285648f9SCameron Grant     	struct snddev_channel *sce;
315285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
316b8f0d9e0SCameron Grant 
317b8f0d9e0SCameron Grant 	snd_mtxlock(d->lock);
318285648f9SCameron Grant 
319285648f9SCameron Grant 	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
320285648f9SCameron Grant 	if (!sce) {
321b8f0d9e0SCameron Grant 		snd_mtxunlock(d->lock);
322285648f9SCameron Grant 		return ENOMEM;
323285648f9SCameron Grant 	}
324285648f9SCameron Grant 
325285648f9SCameron Grant 	sce->channel = ch;
326285648f9SCameron Grant 	SLIST_INSERT_HEAD(&d->channels, sce, link);
327285648f9SCameron Grant 
328f637a36cSCameron Grant 	if (mkdev)
329f637a36cSCameron Grant 		dsp_register(unit, d->devcount++);
330d95502a8SCameron Grant     	d->chancount++;
331f637a36cSCameron Grant 	if (ch->flags & CHN_F_VIRTUAL)
332f637a36cSCameron Grant 		d->vchancount++;
333b8f0d9e0SCameron Grant 
33449c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
335285648f9SCameron Grant 
33633dbf14aSCameron Grant 	return 0;
33733dbf14aSCameron Grant }
33833dbf14aSCameron Grant 
339285648f9SCameron Grant int
340f637a36cSCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
34133dbf14aSCameron Grant {
342285648f9SCameron Grant     	struct snddev_channel *sce;
343285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
34433dbf14aSCameron Grant 
34549c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
346285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
347285648f9SCameron Grant 		if (sce->channel == ch)
348285648f9SCameron Grant 			goto gotit;
34933dbf14aSCameron Grant 	}
35049c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
351285648f9SCameron Grant 	return EINVAL;
352285648f9SCameron Grant gotit:
353f637a36cSCameron Grant 	if (ch->flags & CHN_F_VIRTUAL)
354f637a36cSCameron Grant 		d->vchancount--;
35533dbf14aSCameron Grant 	d->chancount--;
356285648f9SCameron Grant 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
357285648f9SCameron Grant 	free(sce, M_DEVBUF);
358285648f9SCameron Grant 
359f637a36cSCameron Grant 	if (rmdev)
360f637a36cSCameron Grant 		dsp_unregister(unit, --d->devcount);
36149c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
362285648f9SCameron Grant 
363987e5972SCameron Grant 	return 0;
364987e5972SCameron Grant }
365987e5972SCameron Grant 
366987e5972SCameron Grant int
367285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
368285648f9SCameron Grant {
369285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
370cd9766c5SCameron Grant 	struct pcm_channel *ch, *child;
371cd9766c5SCameron Grant 	struct pcmchan_children *pce;
372cd9766c5SCameron Grant     	int i, err;
373285648f9SCameron Grant 
374285648f9SCameron Grant 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
375285648f9SCameron Grant 	if (!ch) {
376285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
377285648f9SCameron Grant 		return ENODEV;
378285648f9SCameron Grant 	}
379cd9766c5SCameron Grant 
380f637a36cSCameron Grant 	err = pcm_chn_add(d, ch, 1);
381285648f9SCameron Grant 	if (err) {
382285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
383285648f9SCameron Grant 		pcm_chn_destroy(ch);
384cd9766c5SCameron Grant 		return err;
385cd9766c5SCameron Grant 	}
386cd9766c5SCameron Grant 
387f637a36cSCameron Grant 	if ((dir == PCMDIR_PLAY) && (d->flags & SD_F_AUTOVCHAN) && (snd_autovchans > 0)) {
388cd9766c5SCameron Grant 		ch->flags |= CHN_F_BUSY;
389cd9766c5SCameron Grant 		for (i = 0; err == 0 && i < snd_autovchans; i++)
390cd9766c5SCameron Grant 			err = vchan_create(ch);
391cd9766c5SCameron Grant 		if (err) {
392cd9766c5SCameron Grant 			device_printf(d->dev, "vchan_create(%d) failed, err=%d\n", i - 1, err);
393cd9766c5SCameron Grant 			SLIST_FOREACH(pce, &ch->children, link) {
394cd9766c5SCameron Grant 				child = pce->channel;
395cd9766c5SCameron Grant 				vchan_destroy(child);
396cd9766c5SCameron Grant 			}
397cd9766c5SCameron Grant 			return err;
398cd9766c5SCameron Grant 		}
399285648f9SCameron Grant 	}
400285648f9SCameron Grant 
401285648f9SCameron Grant 	return err;
402285648f9SCameron Grant }
403285648f9SCameron Grant 
404285648f9SCameron Grant static int
405285648f9SCameron Grant pcm_killchan(device_t dev)
406285648f9SCameron Grant {
407285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
408285648f9SCameron Grant     	struct snddev_channel *sce;
409285648f9SCameron Grant 
41049c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
411285648f9SCameron Grant 	sce = SLIST_FIRST(&d->channels);
41249c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
413285648f9SCameron Grant 
414f637a36cSCameron Grant 	return pcm_chn_remove(d, sce->channel, 1);
415285648f9SCameron Grant }
416285648f9SCameron Grant 
417285648f9SCameron Grant int
418987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
419987e5972SCameron Grant {
42066ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
42149c5e6e2SCameron Grant 
42249c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
423987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
42449c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
425987e5972SCameron Grant 	return 0;
426987e5972SCameron Grant }
427987e5972SCameron Grant 
428987e5972SCameron Grant u_int32_t
429987e5972SCameron Grant pcm_getflags(device_t dev)
430987e5972SCameron Grant {
43166ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
43249c5e6e2SCameron Grant 
433987e5972SCameron Grant 	return d->flags;
434987e5972SCameron Grant }
435987e5972SCameron Grant 
436987e5972SCameron Grant void
437987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
438987e5972SCameron Grant {
43966ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
440d95502a8SCameron Grant 
441987e5972SCameron Grant 	d->flags = val;
442987e5972SCameron Grant }
443987e5972SCameron Grant 
44439004e69SCameron Grant void *
44539004e69SCameron Grant pcm_getdevinfo(device_t dev)
44639004e69SCameron Grant {
44766ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
44849c5e6e2SCameron Grant 
44939004e69SCameron Grant 	return d->devinfo;
45039004e69SCameron Grant }
45139004e69SCameron Grant 
452987e5972SCameron Grant /* This is the generic init routine */
453987e5972SCameron Grant int
454987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
455987e5972SCameron Grant {
45666ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
457987e5972SCameron Grant 
45849c5e6e2SCameron Grant 	d->lock = snd_mtxcreate(device_get_nameunit(dev));
45949c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
460285648f9SCameron Grant 
461cd9766c5SCameron Grant 	d->flags = 0;
462e4d5b250SCameron Grant 	d->dev = dev;
463987e5972SCameron Grant 	d->devinfo = devinfo;
464f637a36cSCameron Grant 	d->devcount = 0;
465285648f9SCameron Grant 	d->chancount = 0;
466f637a36cSCameron Grant 	d->vchancount = 0;
467d95502a8SCameron Grant 	d->inprog = 0;
468833f7023SCameron Grant 
469d95502a8SCameron Grant 	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
470285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
471d95502a8SCameron Grant 
472285648f9SCameron Grant 	d->fakechan = fkchan_setup(dev);
473285648f9SCameron Grant 	chn_init(d->fakechan, NULL, 0);
474987e5972SCameron Grant 
47582db23e2SCameron Grant #ifdef SND_DYNSYSCTL
476cc486d80SJohn Baldwin 	sysctl_ctx_init(&d->sysctl_tree);
477cc486d80SJohn Baldwin 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
478a3e893e0SJohn Baldwin 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
479cc486d80SJohn Baldwin 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
480a3e893e0SJohn Baldwin 	if (d->sysctl_tree_top == NULL) {
481cc486d80SJohn Baldwin 		sysctl_ctx_free(&d->sysctl_tree);
482cc486d80SJohn Baldwin 		goto no;
483cc486d80SJohn Baldwin 	}
48482db23e2SCameron Grant #endif
485b8f0d9e0SCameron Grant 	if (numplay > 0)
486285648f9SCameron Grant 		vchan_initsys(d);
487cd9766c5SCameron Grant 	if (numplay == 1)
488cd9766c5SCameron Grant 		d->flags |= SD_F_AUTOVCHAN;
489cd9766c5SCameron Grant 
49049c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
491987e5972SCameron Grant     	return 0;
492987e5972SCameron Grant no:
49349c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
494987e5972SCameron Grant 	return ENXIO;
495987e5972SCameron Grant }
496987e5972SCameron Grant 
49733dbf14aSCameron Grant int
49833dbf14aSCameron Grant pcm_unregister(device_t dev)
4997c438dbeSCameron Grant {
50066ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
501285648f9SCameron Grant     	struct snddev_channel *sce;
5027c438dbeSCameron Grant 
50349c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
504d95502a8SCameron Grant 	if (d->inprog) {
505d95502a8SCameron Grant 		device_printf(dev, "unregister: operation in progress");
506d95502a8SCameron Grant 		snd_mtxunlock(d->lock);
507d95502a8SCameron Grant 		return EBUSY;
508d95502a8SCameron Grant 	}
509285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
510285648f9SCameron Grant 		if (sce->channel->refcount > 0) {
511c9b53085SCameron Grant 			device_printf(dev, "unregister: channel busy");
51249c5e6e2SCameron Grant 			snd_mtxunlock(d->lock);
513285648f9SCameron Grant 			return EBUSY;
514285648f9SCameron Grant 		}
515c9b53085SCameron Grant 	}
516d95502a8SCameron Grant 	if (mixer_uninit(dev)) {
517c9b53085SCameron Grant 		device_printf(dev, "unregister: mixer busy");
51849c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
519c9b53085SCameron Grant 		return EBUSY;
520c9b53085SCameron Grant 	}
5217c438dbeSCameron Grant 
52266ef8af5SCameron Grant #ifdef SND_DYNSYSCTL
52366ef8af5SCameron Grant 	d->sysctl_tree_top = NULL;
52466ef8af5SCameron Grant 	sysctl_ctx_free(&d->sysctl_tree);
52566ef8af5SCameron Grant #endif
526faeebea2SCameron Grant 	while (!SLIST_EMPTY(&d->channels))
527285648f9SCameron Grant 		pcm_killchan(dev);
5287c438dbeSCameron Grant 
52966ef8af5SCameron Grant 	chn_kill(d->fakechan);
53066ef8af5SCameron Grant 	fkchan_kill(d->fakechan);
53182db23e2SCameron Grant 
53249c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
53333dbf14aSCameron Grant 	return 0;
53433dbf14aSCameron Grant }
5357c438dbeSCameron Grant 
53633dbf14aSCameron Grant static moduledata_t sndpcm_mod = {
53733dbf14aSCameron Grant 	"snd_pcm",
538d95502a8SCameron Grant 	NULL,
53933dbf14aSCameron Grant 	NULL
54033dbf14aSCameron Grant };
54133dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
54233dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER);
543