xref: /freebsd/sys/dev/sound/pcm/sound.c (revision faeebea2b838205e057452094691ca3bed5b5215)
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
40987e5972SCameron Grant 
4182db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
4282db23e2SCameron Grant 
4337209180SCameron Grant void *
4437209180SCameron Grant snd_mtxcreate(const char *desc)
4537209180SCameron Grant {
4637209180SCameron Grant #ifdef USING_MUTEX
4737209180SCameron Grant 	struct mtx *m;
4837209180SCameron Grant 
4937209180SCameron Grant 	m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
5037209180SCameron Grant 	if (m == NULL)
5137209180SCameron Grant 		return NULL;
5237209180SCameron Grant 	mtx_init(m, desc, MTX_RECURSE);
5337209180SCameron Grant 	return m;
5437209180SCameron Grant #else
55a983d575SCameron Grant 	return (void *)0xcafebabe;
5637209180SCameron Grant #endif
5737209180SCameron Grant }
5837209180SCameron Grant 
5937209180SCameron Grant void
6037209180SCameron Grant snd_mtxfree(void *m)
6137209180SCameron Grant {
6237209180SCameron Grant #ifdef USING_MUTEX
6337209180SCameron Grant 	struct mtx *mtx = m;
6437209180SCameron Grant 
6537209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
6637209180SCameron Grant 	mtx_destroy(mtx);
6737209180SCameron Grant 	free(mtx, M_DEVBUF);
6837209180SCameron Grant #endif
6937209180SCameron Grant }
7037209180SCameron Grant 
7137209180SCameron Grant void
7237209180SCameron Grant snd_mtxassert(void *m)
7337209180SCameron Grant {
7437209180SCameron Grant #ifdef USING_MUTEX
7537209180SCameron Grant 	struct mtx *mtx = m;
7637209180SCameron Grant 
7737209180SCameron Grant 	mtx_assert(mtx, MA_OWNED);
7837209180SCameron Grant #endif
7937209180SCameron Grant }
8037209180SCameron Grant 
8137209180SCameron Grant void
8237209180SCameron Grant snd_mtxlock(void *m)
8337209180SCameron Grant {
8437209180SCameron Grant #ifdef USING_MUTEX
8537209180SCameron Grant 	struct mtx *mtx = m;
8637209180SCameron Grant 
8737209180SCameron Grant 	mtx_lock(mtx);
8837209180SCameron Grant #endif
8937209180SCameron Grant }
9037209180SCameron Grant 
9137209180SCameron Grant void
9237209180SCameron Grant snd_mtxunlock(void *m)
9337209180SCameron Grant {
9437209180SCameron Grant #ifdef USING_MUTEX
9537209180SCameron Grant 	struct mtx *mtx = m;
9637209180SCameron Grant 
9737209180SCameron Grant 	mtx_unlock(mtx);
9837209180SCameron Grant #endif
9937209180SCameron Grant }
10037209180SCameron Grant 
10137209180SCameron Grant int
10237209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
10337209180SCameron Grant {
10437209180SCameron Grant #ifdef USING_MUTEX
10537209180SCameron Grant 	flags &= INTR_MPSAFE;
10646700f12SPeter Wemm 	flags |= INTR_TYPE_AV;
10737209180SCameron Grant #else
10846700f12SPeter Wemm 	flags = INTR_TYPE_AV;
10937209180SCameron Grant #endif
11037209180SCameron Grant 	return bus_setup_intr(dev, res, flags, hand, param, cookiep);
11137209180SCameron Grant }
11237209180SCameron Grant 
113b8f0d9e0SCameron Grant /* return a locked channel */
114285648f9SCameron Grant struct pcm_channel *
115b8f0d9e0SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid)
116285648f9SCameron Grant {
117285648f9SCameron Grant 	struct pcm_channel *c;
118285648f9SCameron Grant     	struct snddev_channel *sce;
119285648f9SCameron Grant 
120b8f0d9e0SCameron Grant 	snd_mtxassert(d->lock);
121285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
122285648f9SCameron Grant 		c = sce->channel;
12349c5e6e2SCameron Grant 		CHN_LOCK(c);
124285648f9SCameron Grant 		if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
125285648f9SCameron Grant 			c->flags |= CHN_F_BUSY;
126b8f0d9e0SCameron Grant 			c->pid = pid;
127285648f9SCameron Grant 			return c;
128285648f9SCameron Grant 		}
12949c5e6e2SCameron Grant 		CHN_UNLOCK(c);
130285648f9SCameron Grant 	}
131285648f9SCameron Grant 	return NULL;
132285648f9SCameron Grant }
133285648f9SCameron Grant 
134b8f0d9e0SCameron Grant /* release a locked channel and unlock it */
135285648f9SCameron Grant int
136b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c)
137285648f9SCameron Grant {
138b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
139285648f9SCameron Grant 	c->flags &= ~CHN_F_BUSY;
140b8f0d9e0SCameron Grant 	c->pid = -1;
14149c5e6e2SCameron Grant 	CHN_UNLOCK(c);
142285648f9SCameron Grant 	return 0;
143285648f9SCameron Grant }
144285648f9SCameron Grant 
145285648f9SCameron Grant int
146285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref)
147285648f9SCameron Grant {
14849c5e6e2SCameron Grant 	int r;
14949c5e6e2SCameron Grant 
150b8f0d9e0SCameron Grant 	CHN_LOCKASSERT(c);
151285648f9SCameron Grant 	c->refcount += ref;
15249c5e6e2SCameron Grant 	r = c->refcount;
15349c5e6e2SCameron Grant 	return r;
154285648f9SCameron Grant }
155285648f9SCameron Grant 
15682db23e2SCameron Grant #ifdef USING_DEVFS
15733dbf14aSCameron Grant static int
15833dbf14aSCameron Grant sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS)
15933dbf14aSCameron Grant {
160b8f0d9e0SCameron Grant 	struct snddev_info *d;
16133dbf14aSCameron Grant 	int error, unit;
16233dbf14aSCameron Grant 
16333dbf14aSCameron Grant 	unit = snd_unit;
16433dbf14aSCameron Grant 	error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
16533dbf14aSCameron Grant 	if (error == 0 && req->newptr != NULL) {
166b8f0d9e0SCameron Grant 		if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass))
167b8f0d9e0SCameron Grant 			return EINVAL;
168b8f0d9e0SCameron Grant 		d = devclass_get_softc(pcm_devclass, unit);
169faeebea2SCameron Grant 		if (d == NULL || SLIST_EMPTY(&d->channels))
170b8f0d9e0SCameron Grant 			return EINVAL;
17133dbf14aSCameron Grant 		snd_unit = unit;
17233dbf14aSCameron Grant 	}
17333dbf14aSCameron Grant 	return (error);
17433dbf14aSCameron Grant }
175b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
17633dbf14aSCameron Grant             0, sizeof(int), sysctl_hw_sndunit, "I", "");
17782db23e2SCameron Grant #endif
178987e5972SCameron Grant 
179285648f9SCameron Grant struct pcm_channel *
180285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
181987e5972SCameron Grant {
182285648f9SCameron Grant 	struct pcm_channel *ch;
18333dbf14aSCameron Grant 	char *dirs;
1840f55ac6cSCameron Grant     	int err;
185987e5972SCameron Grant 
186285648f9SCameron Grant 	switch(dir) {
187285648f9SCameron Grant 	case PCMDIR_PLAY:
188285648f9SCameron Grant 		dirs = "play";
189285648f9SCameron Grant 		break;
190285648f9SCameron Grant 	case PCMDIR_REC:
191285648f9SCameron Grant 		dirs = "record";
192285648f9SCameron Grant 		break;
193285648f9SCameron Grant 	case PCMDIR_VIRTUAL:
194285648f9SCameron Grant 		dirs = "virtual";
195285648f9SCameron Grant 		dir = PCMDIR_PLAY;
196285648f9SCameron Grant 		break;
197285648f9SCameron Grant 	default:
198285648f9SCameron Grant 		return NULL;
1999c326820SCameron Grant 	}
200285648f9SCameron Grant 
201285648f9SCameron Grant 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
202285648f9SCameron Grant 	if (!ch)
203285648f9SCameron Grant 		return NULL;
204285648f9SCameron Grant 
2050f55ac6cSCameron Grant 	ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
206285648f9SCameron Grant 	if (!ch->methods) {
207285648f9SCameron Grant 		free(ch, M_DEVBUF);
208285648f9SCameron Grant 		return NULL;
209285648f9SCameron Grant 	}
210285648f9SCameron Grant 
211285648f9SCameron Grant 	ch->pid = -1;
212285648f9SCameron Grant 	ch->parentsnddev = d;
213285648f9SCameron Grant 	ch->parentchannel = parent;
214285648f9SCameron Grant 	snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs);
215285648f9SCameron Grant 
2160f55ac6cSCameron Grant 	err = chn_init(ch, devinfo, dir);
2170f55ac6cSCameron Grant 	if (err) {
218285648f9SCameron Grant 		device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err);
219285648f9SCameron Grant 		kobj_delete(ch->methods, M_DEVBUF);
220285648f9SCameron Grant 		free(ch, M_DEVBUF);
221285648f9SCameron Grant 		return NULL;
222bbb5bf3dSCameron Grant 	}
223285648f9SCameron Grant 
224285648f9SCameron Grant 	return ch;
225285648f9SCameron Grant }
226285648f9SCameron Grant 
227285648f9SCameron Grant int
228285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch)
229285648f9SCameron Grant {
230285648f9SCameron Grant 	int err;
231285648f9SCameron Grant 
232285648f9SCameron Grant 	err = chn_kill(ch);
233285648f9SCameron Grant 	if (err) {
234285648f9SCameron Grant 		device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err);
235285648f9SCameron Grant 		return err;
236285648f9SCameron Grant 	}
237285648f9SCameron Grant 
238285648f9SCameron Grant 	kobj_delete(ch->methods, M_DEVBUF);
239285648f9SCameron Grant 	free(ch, M_DEVBUF);
240285648f9SCameron Grant 
241285648f9SCameron Grant 	return 0;
242285648f9SCameron Grant }
243285648f9SCameron Grant 
244285648f9SCameron Grant int
245285648f9SCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
246285648f9SCameron Grant {
247285648f9SCameron Grant     	struct snddev_channel *sce;
248285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
249b8f0d9e0SCameron Grant 
250b8f0d9e0SCameron Grant 	snd_mtxlock(d->lock);
251285648f9SCameron Grant 
252285648f9SCameron Grant 	sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
253285648f9SCameron Grant 	if (!sce) {
254b8f0d9e0SCameron Grant 		snd_mtxunlock(d->lock);
255285648f9SCameron Grant 		return ENOMEM;
256285648f9SCameron Grant 	}
257285648f9SCameron Grant 
258285648f9SCameron Grant 	sce->channel = ch;
259285648f9SCameron Grant 	SLIST_INSERT_HEAD(&d->channels, sce, link);
260285648f9SCameron Grant 
261d95502a8SCameron Grant 	dsp_register(unit, d->chancount);
262d95502a8SCameron Grant     	d->chancount++;
263b8f0d9e0SCameron Grant 
26449c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
265285648f9SCameron Grant 
26633dbf14aSCameron Grant 	return 0;
26733dbf14aSCameron Grant }
26833dbf14aSCameron Grant 
269285648f9SCameron Grant int
270285648f9SCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
27133dbf14aSCameron Grant {
272285648f9SCameron Grant     	struct snddev_channel *sce;
273285648f9SCameron Grant     	int unit = device_get_unit(d->dev);
27433dbf14aSCameron Grant 
27549c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
276285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
277285648f9SCameron Grant 		if (sce->channel == ch)
278285648f9SCameron Grant 			goto gotit;
27933dbf14aSCameron Grant 	}
28049c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
281285648f9SCameron Grant 	return EINVAL;
282285648f9SCameron Grant gotit:
28333dbf14aSCameron Grant 	d->chancount--;
284285648f9SCameron Grant 	SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
285285648f9SCameron Grant 	free(sce, M_DEVBUF);
286285648f9SCameron Grant 
287d95502a8SCameron Grant 	dsp_unregister(unit, d->chancount);
28849c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
289285648f9SCameron Grant 
290987e5972SCameron Grant 	return 0;
291987e5972SCameron Grant }
292987e5972SCameron Grant 
293987e5972SCameron Grant int
294285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
295285648f9SCameron Grant {
296285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
297285648f9SCameron Grant 	struct pcm_channel *ch;
298285648f9SCameron Grant     	int err;
299285648f9SCameron Grant 
300285648f9SCameron Grant 	ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
301285648f9SCameron Grant 	if (!ch) {
302285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
303285648f9SCameron Grant 		return ENODEV;
304285648f9SCameron Grant 	}
305285648f9SCameron Grant 	err = pcm_chn_add(d, ch);
306285648f9SCameron Grant 	if (err) {
307285648f9SCameron Grant 		device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
308285648f9SCameron Grant 		pcm_chn_destroy(ch);
309285648f9SCameron Grant 	}
310285648f9SCameron Grant 
311285648f9SCameron Grant 	return err;
312285648f9SCameron Grant }
313285648f9SCameron Grant 
314285648f9SCameron Grant static int
315285648f9SCameron Grant pcm_killchan(device_t dev)
316285648f9SCameron Grant {
317285648f9SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
318285648f9SCameron Grant     	struct snddev_channel *sce;
319285648f9SCameron Grant 
32049c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
321285648f9SCameron Grant 	sce = SLIST_FIRST(&d->channels);
32249c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
323285648f9SCameron Grant 
324285648f9SCameron Grant 	return pcm_chn_remove(d, sce->channel);
325285648f9SCameron Grant }
326285648f9SCameron Grant 
327285648f9SCameron Grant int
328987e5972SCameron Grant pcm_setstatus(device_t dev, char *str)
329987e5972SCameron Grant {
33066ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
33149c5e6e2SCameron Grant 
33249c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
333987e5972SCameron Grant 	strncpy(d->status, str, SND_STATUSLEN);
33449c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
335987e5972SCameron Grant 	return 0;
336987e5972SCameron Grant }
337987e5972SCameron Grant 
338987e5972SCameron Grant u_int32_t
339987e5972SCameron Grant pcm_getflags(device_t dev)
340987e5972SCameron Grant {
34166ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
34249c5e6e2SCameron Grant 
343987e5972SCameron Grant 	return d->flags;
344987e5972SCameron Grant }
345987e5972SCameron Grant 
346987e5972SCameron Grant void
347987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val)
348987e5972SCameron Grant {
34966ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
350d95502a8SCameron Grant 
351987e5972SCameron Grant 	d->flags = val;
352987e5972SCameron Grant }
353987e5972SCameron Grant 
35439004e69SCameron Grant void *
35539004e69SCameron Grant pcm_getdevinfo(device_t dev)
35639004e69SCameron Grant {
35766ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
35849c5e6e2SCameron Grant 
35939004e69SCameron Grant 	return d->devinfo;
36039004e69SCameron Grant }
36139004e69SCameron Grant 
362987e5972SCameron Grant /* This is the generic init routine */
363987e5972SCameron Grant int
364987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
365987e5972SCameron Grant {
36666ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
367987e5972SCameron Grant 
36849c5e6e2SCameron Grant 	d->lock = snd_mtxcreate(device_get_nameunit(dev));
36949c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
370285648f9SCameron Grant 
371e4d5b250SCameron Grant 	d->dev = dev;
372987e5972SCameron Grant 	d->devinfo = devinfo;
373285648f9SCameron Grant 	d->chancount = 0;
374d95502a8SCameron Grant 	d->inprog = 0;
375833f7023SCameron Grant 
376d95502a8SCameron Grant 	if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
377285648f9SCameron Grant 		d->flags |= SD_F_SIMPLEX;
378d95502a8SCameron Grant 
379285648f9SCameron Grant 	d->fakechan = fkchan_setup(dev);
380285648f9SCameron Grant 	chn_init(d->fakechan, NULL, 0);
381987e5972SCameron Grant 
38282db23e2SCameron Grant #ifdef SND_DYNSYSCTL
383cc486d80SJohn Baldwin 	sysctl_ctx_init(&d->sysctl_tree);
384cc486d80SJohn Baldwin 	d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
385a3e893e0SJohn Baldwin 				 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
386cc486d80SJohn Baldwin 				 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
387a3e893e0SJohn Baldwin 	if (d->sysctl_tree_top == NULL) {
388cc486d80SJohn Baldwin 		sysctl_ctx_free(&d->sysctl_tree);
389cc486d80SJohn Baldwin 		goto no;
390cc486d80SJohn Baldwin 	}
39182db23e2SCameron Grant #endif
392b8f0d9e0SCameron Grant 	if (numplay > 0)
393285648f9SCameron Grant 		vchan_initsys(d);
39449c5e6e2SCameron Grant 	snd_mtxunlock(d->lock);
395987e5972SCameron Grant     	return 0;
396987e5972SCameron Grant no:
39749c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
398987e5972SCameron Grant 	return ENXIO;
399987e5972SCameron Grant }
400987e5972SCameron Grant 
40133dbf14aSCameron Grant int
40233dbf14aSCameron Grant pcm_unregister(device_t dev)
4037c438dbeSCameron Grant {
40466ef8af5SCameron Grant     	struct snddev_info *d = device_get_softc(dev);
405285648f9SCameron Grant     	struct snddev_channel *sce;
4067c438dbeSCameron Grant 
40749c5e6e2SCameron Grant 	snd_mtxlock(d->lock);
408d95502a8SCameron Grant 	if (d->inprog) {
409d95502a8SCameron Grant 		device_printf(dev, "unregister: operation in progress");
410d95502a8SCameron Grant 		snd_mtxunlock(d->lock);
411d95502a8SCameron Grant 		return EBUSY;
412d95502a8SCameron Grant 	}
413285648f9SCameron Grant 	SLIST_FOREACH(sce, &d->channels, link) {
414285648f9SCameron Grant 		if (sce->channel->refcount > 0) {
415c9b53085SCameron Grant 			device_printf(dev, "unregister: channel busy");
41649c5e6e2SCameron Grant 			snd_mtxunlock(d->lock);
417285648f9SCameron Grant 			return EBUSY;
418285648f9SCameron Grant 		}
419c9b53085SCameron Grant 	}
420d95502a8SCameron Grant 	if (mixer_uninit(dev)) {
421c9b53085SCameron Grant 		device_printf(dev, "unregister: mixer busy");
42249c5e6e2SCameron Grant 		snd_mtxunlock(d->lock);
423c9b53085SCameron Grant 		return EBUSY;
424c9b53085SCameron Grant 	}
4257c438dbeSCameron Grant 
42666ef8af5SCameron Grant #ifdef SND_DYNSYSCTL
42766ef8af5SCameron Grant 	d->sysctl_tree_top = NULL;
42866ef8af5SCameron Grant 	sysctl_ctx_free(&d->sysctl_tree);
42966ef8af5SCameron Grant #endif
430faeebea2SCameron Grant 	while (!SLIST_EMPTY(&d->channels))
431285648f9SCameron Grant 		pcm_killchan(dev);
4327c438dbeSCameron Grant 
43366ef8af5SCameron Grant 	chn_kill(d->fakechan);
43466ef8af5SCameron Grant 	fkchan_kill(d->fakechan);
43582db23e2SCameron Grant 
43649c5e6e2SCameron Grant 	snd_mtxfree(d->lock);
43733dbf14aSCameron Grant 	return 0;
43833dbf14aSCameron Grant }
4397c438dbeSCameron Grant 
44033dbf14aSCameron Grant static moduledata_t sndpcm_mod = {
44133dbf14aSCameron Grant 	"snd_pcm",
442d95502a8SCameron Grant 	NULL,
44333dbf14aSCameron Grant 	NULL
44433dbf14aSCameron Grant };
44533dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
44633dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER);
447