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