1987e5972SCameron Grant /* 2987e5972SCameron Grant * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3987e5972SCameron Grant * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) 4987e5972SCameron Grant * All rights reserved. 5987e5972SCameron Grant * 6987e5972SCameron Grant * Redistribution and use in source and binary forms, with or without 7987e5972SCameron Grant * modification, are permitted provided that the following conditions 8987e5972SCameron Grant * are met: 9987e5972SCameron Grant * 1. Redistributions of source code must retain the above copyright 10987e5972SCameron Grant * notice, this list of conditions and the following disclaimer. 11987e5972SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 12987e5972SCameron Grant * notice, this list of conditions and the following disclaimer in the 13987e5972SCameron Grant * documentation and/or other materials provided with the distribution. 14987e5972SCameron Grant * 15987e5972SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16987e5972SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17987e5972SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18987e5972SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19987e5972SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20987e5972SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21987e5972SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22987e5972SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23987e5972SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24987e5972SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25987e5972SCameron Grant * SUCH DAMAGE. 26987e5972SCameron Grant */ 27987e5972SCameron Grant 28ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 29285648f9SCameron Grant #include <dev/sound/pcm/vchan.h> 307c438dbeSCameron Grant #include <sys/sysctl.h> 31285648f9SCameron Grant 3267b1dce3SCameron Grant #include "feeder_if.h" 3367b1dce3SCameron Grant 3467b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 3567b1dce3SCameron Grant 3667b1dce3SCameron Grant struct snddev_channel { 3767b1dce3SCameron Grant SLIST_ENTRY(snddev_channel) link; 3867b1dce3SCameron Grant struct pcm_channel *channel; 3967b1dce3SCameron Grant }; 4067b1dce3SCameron Grant 4167b1dce3SCameron Grant struct snddev_info { 4267b1dce3SCameron Grant SLIST_HEAD(, snddev_channel) channels; 4367b1dce3SCameron Grant struct pcm_channel *fakechan; 44506a5308SCameron Grant unsigned devcount, reccount, chancount, vchancount; 4567b1dce3SCameron Grant unsigned flags; 4667b1dce3SCameron Grant int inprog; 4767b1dce3SCameron Grant void *devinfo; 4867b1dce3SCameron Grant device_t dev; 4967b1dce3SCameron Grant char status[SND_STATUSLEN]; 5067b1dce3SCameron Grant struct sysctl_ctx_list sysctl_tree; 5167b1dce3SCameron Grant struct sysctl_oid *sysctl_tree_top; 5267b1dce3SCameron Grant void *lock; 5367b1dce3SCameron Grant }; 5467b1dce3SCameron Grant 55d95502a8SCameron Grant devclass_t pcm_devclass; 5682db23e2SCameron Grant 5782db23e2SCameron Grant #ifdef USING_DEVFS 58d95502a8SCameron Grant int snd_unit = 0; 5909786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit); 6082db23e2SCameron Grant #endif 61cbe7d6a3SCameron Grant 6267b1dce3SCameron Grant int snd_maxautovchans = 0; 6367b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 64987e5972SCameron Grant 6582db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 6682db23e2SCameron Grant 6767b1dce3SCameron Grant static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 6867b1dce3SCameron Grant 6967b1dce3SCameron Grant struct sysctl_ctx_list * 7067b1dce3SCameron Grant snd_sysctl_tree(device_t dev) 7167b1dce3SCameron Grant { 7267b1dce3SCameron Grant struct snddev_info *d = device_get_softc(dev); 7367b1dce3SCameron Grant 7467b1dce3SCameron Grant return &d->sysctl_tree; 7567b1dce3SCameron Grant } 7667b1dce3SCameron Grant 7767b1dce3SCameron Grant struct sysctl_oid * 7867b1dce3SCameron Grant snd_sysctl_tree_top(device_t dev) 7967b1dce3SCameron Grant { 8067b1dce3SCameron Grant struct snddev_info *d = device_get_softc(dev); 8167b1dce3SCameron Grant 8267b1dce3SCameron Grant return d->sysctl_tree_top; 8367b1dce3SCameron Grant } 8467b1dce3SCameron Grant 8537209180SCameron Grant void * 8637209180SCameron Grant snd_mtxcreate(const char *desc) 8737209180SCameron Grant { 8837209180SCameron Grant #ifdef USING_MUTEX 8937209180SCameron Grant struct mtx *m; 9037209180SCameron Grant 9137209180SCameron Grant m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 9237209180SCameron Grant if (m == NULL) 9337209180SCameron Grant return NULL; 9437209180SCameron Grant mtx_init(m, desc, MTX_RECURSE); 9537209180SCameron Grant return m; 9637209180SCameron Grant #else 97a983d575SCameron Grant return (void *)0xcafebabe; 9837209180SCameron Grant #endif 9937209180SCameron Grant } 10037209180SCameron Grant 10137209180SCameron Grant void 10237209180SCameron Grant snd_mtxfree(void *m) 10337209180SCameron Grant { 10437209180SCameron Grant #ifdef USING_MUTEX 10537209180SCameron Grant struct mtx *mtx = m; 10637209180SCameron Grant 10737209180SCameron Grant mtx_assert(mtx, MA_OWNED); 10837209180SCameron Grant mtx_destroy(mtx); 10937209180SCameron Grant free(mtx, M_DEVBUF); 11037209180SCameron Grant #endif 11137209180SCameron Grant } 11237209180SCameron Grant 11337209180SCameron Grant void 11437209180SCameron Grant snd_mtxassert(void *m) 11537209180SCameron Grant { 11637209180SCameron Grant #ifdef USING_MUTEX 117f00f162aSCameron Grant #ifdef INVARIANTS 11837209180SCameron Grant struct mtx *mtx = m; 11937209180SCameron Grant 12037209180SCameron Grant mtx_assert(mtx, MA_OWNED); 12137209180SCameron Grant #endif 122f00f162aSCameron Grant #endif 12337209180SCameron Grant } 12437209180SCameron Grant 12537209180SCameron Grant void 12637209180SCameron Grant snd_mtxlock(void *m) 12737209180SCameron Grant { 12837209180SCameron Grant #ifdef USING_MUTEX 12937209180SCameron Grant struct mtx *mtx = m; 13037209180SCameron Grant 13137209180SCameron Grant mtx_lock(mtx); 13237209180SCameron Grant #endif 13337209180SCameron Grant } 13437209180SCameron Grant 13537209180SCameron Grant void 13637209180SCameron Grant snd_mtxunlock(void *m) 13737209180SCameron Grant { 13837209180SCameron Grant #ifdef USING_MUTEX 13937209180SCameron Grant struct mtx *mtx = m; 14037209180SCameron Grant 14137209180SCameron Grant mtx_unlock(mtx); 14237209180SCameron Grant #endif 14337209180SCameron Grant } 14437209180SCameron Grant 14537209180SCameron Grant int 14637209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 14737209180SCameron Grant { 14837209180SCameron Grant #ifdef USING_MUTEX 14937209180SCameron Grant flags &= INTR_MPSAFE; 15046700f12SPeter Wemm flags |= INTR_TYPE_AV; 15137209180SCameron Grant #else 15246700f12SPeter Wemm flags = INTR_TYPE_AV; 15337209180SCameron Grant #endif 15437209180SCameron Grant return bus_setup_intr(dev, res, flags, hand, param, cookiep); 15537209180SCameron Grant } 15637209180SCameron Grant 15767b1dce3SCameron Grant void 15867b1dce3SCameron Grant pcm_lock(struct snddev_info *d) 15967b1dce3SCameron Grant { 16067b1dce3SCameron Grant snd_mtxlock(d->lock); 16167b1dce3SCameron Grant } 16267b1dce3SCameron Grant 16367b1dce3SCameron Grant void 16467b1dce3SCameron Grant pcm_unlock(struct snddev_info *d) 16567b1dce3SCameron Grant { 16667b1dce3SCameron Grant snd_mtxunlock(d->lock); 16767b1dce3SCameron Grant } 16867b1dce3SCameron Grant 16967b1dce3SCameron Grant struct pcm_channel * 17067b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d) 17167b1dce3SCameron Grant { 17267b1dce3SCameron Grant return d->fakechan; 17367b1dce3SCameron Grant } 17467b1dce3SCameron Grant 175b8f0d9e0SCameron Grant /* return a locked channel */ 176285648f9SCameron Grant struct pcm_channel * 177506a5308SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum) 178285648f9SCameron Grant { 179285648f9SCameron Grant struct pcm_channel *c; 180285648f9SCameron Grant struct snddev_channel *sce; 181f637a36cSCameron Grant int err; 182285648f9SCameron Grant 183b8f0d9e0SCameron Grant snd_mtxassert(d->lock); 184f637a36cSCameron Grant 185f637a36cSCameron Grant /* scan for a free channel */ 186285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 187285648f9SCameron Grant c = sce->channel; 18849c5e6e2SCameron Grant CHN_LOCK(c); 189285648f9SCameron Grant if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 190506a5308SCameron Grant if (chnum == -1 || c->num == chnum) { 191285648f9SCameron Grant c->flags |= CHN_F_BUSY; 192b8f0d9e0SCameron Grant c->pid = pid; 193285648f9SCameron Grant return c; 194285648f9SCameron Grant } 195506a5308SCameron Grant } 19649c5e6e2SCameron Grant CHN_UNLOCK(c); 197285648f9SCameron Grant } 198f637a36cSCameron Grant 199f637a36cSCameron Grant /* no channel available */ 200f637a36cSCameron Grant if (direction == PCMDIR_PLAY) { 20167b1dce3SCameron Grant if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { 202f637a36cSCameron Grant /* try to create a vchan */ 203f637a36cSCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 204f637a36cSCameron Grant c = sce->channel; 205f637a36cSCameron Grant if (!SLIST_EMPTY(&c->children)) { 206f637a36cSCameron Grant err = vchan_create(c); 207f637a36cSCameron Grant if (!err) 208506a5308SCameron Grant return pcm_chnalloc(d, direction, pid, -1); 209f637a36cSCameron Grant else 210f637a36cSCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 211f637a36cSCameron Grant } 212f637a36cSCameron Grant } 213f637a36cSCameron Grant } 214f637a36cSCameron Grant } 215f637a36cSCameron Grant 216285648f9SCameron Grant return NULL; 217285648f9SCameron Grant } 218285648f9SCameron Grant 219b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 220285648f9SCameron Grant int 221b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 222285648f9SCameron Grant { 223b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 224285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 225b8f0d9e0SCameron Grant c->pid = -1; 22649c5e6e2SCameron Grant CHN_UNLOCK(c); 227285648f9SCameron Grant return 0; 228285648f9SCameron Grant } 229285648f9SCameron Grant 230285648f9SCameron Grant int 231285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 232285648f9SCameron Grant { 23349c5e6e2SCameron Grant int r; 23449c5e6e2SCameron Grant 235b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 236285648f9SCameron Grant c->refcount += ref; 23749c5e6e2SCameron Grant r = c->refcount; 23849c5e6e2SCameron Grant return r; 239285648f9SCameron Grant } 240285648f9SCameron Grant 24167b1dce3SCameron Grant int 24267b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta) 24367b1dce3SCameron Grant { 24467b1dce3SCameron Grant d->inprog += delta; 24567b1dce3SCameron Grant return d->inprog; 24667b1dce3SCameron Grant } 24767b1dce3SCameron Grant 24867b1dce3SCameron Grant static void 24967b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 25067b1dce3SCameron Grant { 25167b1dce3SCameron Grant struct pcm_channel *c; 25267b1dce3SCameron Grant struct snddev_channel *sce; 25367b1dce3SCameron Grant int err, done; 25467b1dce3SCameron Grant 25567b1dce3SCameron Grant if (num > 0 && d->vchancount == 0) { 25667b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 25767b1dce3SCameron Grant c = sce->channel; 25867b1dce3SCameron Grant if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) { 25967b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 26067b1dce3SCameron Grant err = vchan_create(c); 26167b1dce3SCameron Grant if (err) { 26267b1dce3SCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 26367b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 26467b1dce3SCameron Grant } 26567b1dce3SCameron Grant return; 26667b1dce3SCameron Grant } 26767b1dce3SCameron Grant } 26867b1dce3SCameron Grant } 26967b1dce3SCameron Grant if (num == 0 && d->vchancount > 0) { 27067b1dce3SCameron Grant done = 0; 27167b1dce3SCameron Grant while (!done) { 27267b1dce3SCameron Grant done = 1; 27367b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 27467b1dce3SCameron Grant c = sce->channel; 27567b1dce3SCameron Grant if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) { 27667b1dce3SCameron Grant done = 0; 27767b1dce3SCameron Grant err = vchan_destroy(c); 27867b1dce3SCameron Grant if (err) 27967b1dce3SCameron Grant device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err); 28067b1dce3SCameron Grant goto restart; 28167b1dce3SCameron Grant } 28267b1dce3SCameron Grant } 28367b1dce3SCameron Grant restart: 28467b1dce3SCameron Grant } 28567b1dce3SCameron Grant } 28667b1dce3SCameron Grant } 28767b1dce3SCameron Grant 28882db23e2SCameron Grant #ifdef USING_DEVFS 28933dbf14aSCameron Grant static int 290cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 29133dbf14aSCameron Grant { 292b8f0d9e0SCameron Grant struct snddev_info *d; 29333dbf14aSCameron Grant int error, unit; 29433dbf14aSCameron Grant 29533dbf14aSCameron Grant unit = snd_unit; 29633dbf14aSCameron Grant error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 29733dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 29874ffd138SCameron Grant if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 299b8f0d9e0SCameron Grant return EINVAL; 300b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 301faeebea2SCameron Grant if (d == NULL || SLIST_EMPTY(&d->channels)) 302b8f0d9e0SCameron Grant return EINVAL; 30333dbf14aSCameron Grant snd_unit = unit; 30433dbf14aSCameron Grant } 30533dbf14aSCameron Grant return (error); 30633dbf14aSCameron Grant } 307b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 308cd9766c5SCameron Grant 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 30982db23e2SCameron Grant #endif 310987e5972SCameron Grant 311cd9766c5SCameron Grant static int 31267b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 313cd9766c5SCameron Grant { 31467b1dce3SCameron Grant struct snddev_info *d; 31567b1dce3SCameron Grant int i, v, error; 316cd9766c5SCameron Grant 31767b1dce3SCameron Grant v = snd_maxautovchans; 318cd9766c5SCameron Grant error = sysctl_handle_int(oidp, &v, sizeof(v), req); 319cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 320cd9766c5SCameron Grant if (v < 0 || v >= SND_MAXVCHANS) 321cd9766c5SCameron Grant return EINVAL; 32267b1dce3SCameron Grant if (v != snd_maxautovchans) { 32367b1dce3SCameron Grant for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 32467b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 32567b1dce3SCameron Grant if (!d) 32667b1dce3SCameron Grant continue; 32767b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 32867b1dce3SCameron Grant } 32967b1dce3SCameron Grant } 33067b1dce3SCameron Grant snd_maxautovchans = v; 331cd9766c5SCameron Grant } 332cd9766c5SCameron Grant return (error); 333cd9766c5SCameron Grant } 33467b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 33567b1dce3SCameron Grant 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 336f637a36cSCameron Grant 337285648f9SCameron Grant struct pcm_channel * 338285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 339987e5972SCameron Grant { 340285648f9SCameron Grant struct pcm_channel *ch; 34133dbf14aSCameron Grant char *dirs; 3420f55ac6cSCameron Grant int err; 343987e5972SCameron Grant 344285648f9SCameron Grant switch(dir) { 345285648f9SCameron Grant case PCMDIR_PLAY: 346285648f9SCameron Grant dirs = "play"; 347285648f9SCameron Grant break; 348285648f9SCameron Grant case PCMDIR_REC: 349285648f9SCameron Grant dirs = "record"; 350285648f9SCameron Grant break; 351285648f9SCameron Grant case PCMDIR_VIRTUAL: 352285648f9SCameron Grant dirs = "virtual"; 353285648f9SCameron Grant dir = PCMDIR_PLAY; 354285648f9SCameron Grant break; 355285648f9SCameron Grant default: 356285648f9SCameron Grant return NULL; 3579c326820SCameron Grant } 358285648f9SCameron Grant 359285648f9SCameron Grant ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 360285648f9SCameron Grant if (!ch) 361285648f9SCameron Grant return NULL; 362285648f9SCameron Grant 3630f55ac6cSCameron Grant ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 364285648f9SCameron Grant if (!ch->methods) { 365285648f9SCameron Grant free(ch, M_DEVBUF); 366285648f9SCameron Grant return NULL; 367285648f9SCameron Grant } 368285648f9SCameron Grant 369285648f9SCameron Grant ch->pid = -1; 370285648f9SCameron Grant ch->parentsnddev = d; 371285648f9SCameron Grant ch->parentchannel = parent; 372285648f9SCameron Grant snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs); 373285648f9SCameron Grant 3740f55ac6cSCameron Grant err = chn_init(ch, devinfo, dir); 3750f55ac6cSCameron Grant if (err) { 376285648f9SCameron Grant device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err); 377285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 378285648f9SCameron Grant free(ch, M_DEVBUF); 379285648f9SCameron Grant return NULL; 380bbb5bf3dSCameron Grant } 381285648f9SCameron Grant 382285648f9SCameron Grant return ch; 383285648f9SCameron Grant } 384285648f9SCameron Grant 385285648f9SCameron Grant int 386285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 387285648f9SCameron Grant { 388285648f9SCameron Grant int err; 389285648f9SCameron Grant 390285648f9SCameron Grant err = chn_kill(ch); 391285648f9SCameron Grant if (err) { 392285648f9SCameron Grant device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err); 393285648f9SCameron Grant return err; 394285648f9SCameron Grant } 395285648f9SCameron Grant 396285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 397285648f9SCameron Grant free(ch, M_DEVBUF); 398285648f9SCameron Grant 399285648f9SCameron Grant return 0; 400285648f9SCameron Grant } 401285648f9SCameron Grant 402285648f9SCameron Grant int 403f637a36cSCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 404285648f9SCameron Grant { 40567b1dce3SCameron Grant struct snddev_channel *sce, *tmp, *after; 406285648f9SCameron Grant int unit = device_get_unit(d->dev); 407b8f0d9e0SCameron Grant 408b8f0d9e0SCameron Grant snd_mtxlock(d->lock); 409285648f9SCameron Grant 410285648f9SCameron Grant sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 411285648f9SCameron Grant if (!sce) { 412b8f0d9e0SCameron Grant snd_mtxunlock(d->lock); 413285648f9SCameron Grant return ENOMEM; 414285648f9SCameron Grant } 415285648f9SCameron Grant 416285648f9SCameron Grant sce->channel = ch; 41767b1dce3SCameron Grant if (SLIST_EMPTY(&d->channels)) { 418285648f9SCameron Grant SLIST_INSERT_HEAD(&d->channels, sce, link); 41967b1dce3SCameron Grant } else { 42067b1dce3SCameron Grant after = NULL; 42167b1dce3SCameron Grant SLIST_FOREACH(tmp, &d->channels, link) { 42267b1dce3SCameron Grant after = tmp; 42367b1dce3SCameron Grant } 42467b1dce3SCameron Grant SLIST_INSERT_AFTER(after, sce, link); 42567b1dce3SCameron Grant } 426285648f9SCameron Grant 427506a5308SCameron Grant if (ch->direction == PCMDIR_REC) 428506a5308SCameron Grant ch->num = d->reccount++; 429506a5308SCameron Grant /* 430506a5308SCameron Grant else 431506a5308SCameron Grant ch->num = d->playcount++; 432506a5308SCameron Grant */ 433506a5308SCameron Grant 434506a5308SCameron Grant if (mkdev) { 435f637a36cSCameron Grant dsp_register(unit, d->devcount++); 436506a5308SCameron Grant if (ch->direction == PCMDIR_REC) 437506a5308SCameron Grant dsp_registerrec(unit, ch->num); 438506a5308SCameron Grant } 439d95502a8SCameron Grant d->chancount++; 440f637a36cSCameron Grant if (ch->flags & CHN_F_VIRTUAL) 441f637a36cSCameron Grant d->vchancount++; 442b8f0d9e0SCameron Grant 44349c5e6e2SCameron Grant snd_mtxunlock(d->lock); 444285648f9SCameron Grant 44533dbf14aSCameron Grant return 0; 44633dbf14aSCameron Grant } 44733dbf14aSCameron Grant 448285648f9SCameron Grant int 449f637a36cSCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 45033dbf14aSCameron Grant { 451285648f9SCameron Grant struct snddev_channel *sce; 452285648f9SCameron Grant int unit = device_get_unit(d->dev); 45333dbf14aSCameron Grant 45449c5e6e2SCameron Grant snd_mtxlock(d->lock); 455285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 456285648f9SCameron Grant if (sce->channel == ch) 457285648f9SCameron Grant goto gotit; 45833dbf14aSCameron Grant } 45949c5e6e2SCameron Grant snd_mtxunlock(d->lock); 460285648f9SCameron Grant return EINVAL; 461285648f9SCameron Grant gotit: 462f637a36cSCameron Grant if (ch->flags & CHN_F_VIRTUAL) 463f637a36cSCameron Grant d->vchancount--; 46433dbf14aSCameron Grant d->chancount--; 465285648f9SCameron Grant SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 466285648f9SCameron Grant free(sce, M_DEVBUF); 467285648f9SCameron Grant 468506a5308SCameron Grant if (rmdev) { 469f637a36cSCameron Grant dsp_unregister(unit, --d->devcount); 470506a5308SCameron Grant if (ch->direction == PCMDIR_REC) 471506a5308SCameron Grant dsp_unregisterrec(unit, --d->reccount); 472506a5308SCameron Grant } 47349c5e6e2SCameron Grant snd_mtxunlock(d->lock); 474285648f9SCameron Grant 475987e5972SCameron Grant return 0; 476987e5972SCameron Grant } 477987e5972SCameron Grant 478987e5972SCameron Grant int 479285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 480285648f9SCameron Grant { 481285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 48267b1dce3SCameron Grant struct pcm_channel *ch; 48367b1dce3SCameron Grant int err; 484285648f9SCameron Grant 485285648f9SCameron Grant ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 486285648f9SCameron Grant if (!ch) { 487285648f9SCameron Grant device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 488285648f9SCameron Grant return ENODEV; 489285648f9SCameron Grant } 490cd9766c5SCameron Grant 491f637a36cSCameron Grant err = pcm_chn_add(d, ch, 1); 492285648f9SCameron Grant if (err) { 493285648f9SCameron Grant device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 494285648f9SCameron Grant pcm_chn_destroy(ch); 495cd9766c5SCameron Grant return err; 496cd9766c5SCameron Grant } 497cd9766c5SCameron Grant 49867b1dce3SCameron Grant if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) { 499cd9766c5SCameron Grant ch->flags |= CHN_F_BUSY; 500cd9766c5SCameron Grant err = vchan_create(ch); 501cd9766c5SCameron Grant if (err) { 50267b1dce3SCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 50367b1dce3SCameron Grant ch->flags &= ~CHN_F_BUSY; 504cd9766c5SCameron Grant } 505285648f9SCameron Grant } 506285648f9SCameron Grant 507285648f9SCameron Grant return err; 508285648f9SCameron Grant } 509285648f9SCameron Grant 510285648f9SCameron Grant static int 511285648f9SCameron Grant pcm_killchan(device_t dev) 512285648f9SCameron Grant { 513285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 514285648f9SCameron Grant struct snddev_channel *sce; 515285648f9SCameron Grant 51649c5e6e2SCameron Grant snd_mtxlock(d->lock); 517285648f9SCameron Grant sce = SLIST_FIRST(&d->channels); 51849c5e6e2SCameron Grant snd_mtxunlock(d->lock); 519285648f9SCameron Grant 520f637a36cSCameron Grant return pcm_chn_remove(d, sce->channel, 1); 521285648f9SCameron Grant } 522285648f9SCameron Grant 523285648f9SCameron Grant int 524987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 525987e5972SCameron Grant { 52666ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 52749c5e6e2SCameron Grant 52849c5e6e2SCameron Grant snd_mtxlock(d->lock); 529987e5972SCameron Grant strncpy(d->status, str, SND_STATUSLEN); 53049c5e6e2SCameron Grant snd_mtxunlock(d->lock); 531987e5972SCameron Grant return 0; 532987e5972SCameron Grant } 533987e5972SCameron Grant 534987e5972SCameron Grant u_int32_t 535987e5972SCameron Grant pcm_getflags(device_t dev) 536987e5972SCameron Grant { 53766ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 53849c5e6e2SCameron Grant 539987e5972SCameron Grant return d->flags; 540987e5972SCameron Grant } 541987e5972SCameron Grant 542987e5972SCameron Grant void 543987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val) 544987e5972SCameron Grant { 54566ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 546d95502a8SCameron Grant 547987e5972SCameron Grant d->flags = val; 548987e5972SCameron Grant } 549987e5972SCameron Grant 55039004e69SCameron Grant void * 55139004e69SCameron Grant pcm_getdevinfo(device_t dev) 55239004e69SCameron Grant { 55366ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 55449c5e6e2SCameron Grant 55539004e69SCameron Grant return d->devinfo; 55639004e69SCameron Grant } 55739004e69SCameron Grant 558987e5972SCameron Grant int 559987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 560987e5972SCameron Grant { 56166ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 562987e5972SCameron Grant 56349c5e6e2SCameron Grant d->lock = snd_mtxcreate(device_get_nameunit(dev)); 56449c5e6e2SCameron Grant snd_mtxlock(d->lock); 565285648f9SCameron Grant 566cd9766c5SCameron Grant d->flags = 0; 567e4d5b250SCameron Grant d->dev = dev; 568987e5972SCameron Grant d->devinfo = devinfo; 569f637a36cSCameron Grant d->devcount = 0; 570506a5308SCameron Grant d->reccount = 0; 571285648f9SCameron Grant d->chancount = 0; 572f637a36cSCameron Grant d->vchancount = 0; 573d95502a8SCameron Grant d->inprog = 0; 574833f7023SCameron Grant 575d95502a8SCameron Grant if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 576285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 577d95502a8SCameron Grant 578285648f9SCameron Grant d->fakechan = fkchan_setup(dev); 579285648f9SCameron Grant chn_init(d->fakechan, NULL, 0); 580987e5972SCameron Grant 58182db23e2SCameron Grant #ifdef SND_DYNSYSCTL 582cc486d80SJohn Baldwin sysctl_ctx_init(&d->sysctl_tree); 583cc486d80SJohn Baldwin d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 584a3e893e0SJohn Baldwin SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 585cc486d80SJohn Baldwin device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 586a3e893e0SJohn Baldwin if (d->sysctl_tree_top == NULL) { 587cc486d80SJohn Baldwin sysctl_ctx_free(&d->sysctl_tree); 588cc486d80SJohn Baldwin goto no; 589cc486d80SJohn Baldwin } 59082db23e2SCameron Grant #endif 591b8f0d9e0SCameron Grant if (numplay > 0) 59267b1dce3SCameron Grant vchan_initsys(dev); 593cd9766c5SCameron Grant if (numplay == 1) 594cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 595cd9766c5SCameron Grant 59649c5e6e2SCameron Grant snd_mtxunlock(d->lock); 59767b1dce3SCameron Grant sndstat_register(dev, d->status, sndstat_prepare_pcm); 598987e5972SCameron Grant return 0; 599987e5972SCameron Grant no: 60049c5e6e2SCameron Grant snd_mtxfree(d->lock); 601987e5972SCameron Grant return ENXIO; 602987e5972SCameron Grant } 603987e5972SCameron Grant 60433dbf14aSCameron Grant int 60533dbf14aSCameron Grant pcm_unregister(device_t dev) 6067c438dbeSCameron Grant { 60766ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 608285648f9SCameron Grant struct snddev_channel *sce; 6097c438dbeSCameron Grant 61049c5e6e2SCameron Grant snd_mtxlock(d->lock); 611d95502a8SCameron Grant if (d->inprog) { 6125c25132aSGeorge C A Reid device_printf(dev, "unregister: operation in progress\n"); 6135c25132aSGeorge C A Reid snd_mtxunlock(d->lock); 6145c25132aSGeorge C A Reid return EBUSY; 6155c25132aSGeorge C A Reid } 6165c25132aSGeorge C A Reid if (sndstat_busy() != 0) { 6175c25132aSGeorge C A Reid device_printf(dev, "unregister: sndstat busy\n"); 618d95502a8SCameron Grant snd_mtxunlock(d->lock); 619d95502a8SCameron Grant return EBUSY; 620d95502a8SCameron Grant } 621285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 622285648f9SCameron Grant if (sce->channel->refcount > 0) { 6235c25132aSGeorge C A Reid device_printf(dev, "unregister: channel busy\n"); 62449c5e6e2SCameron Grant snd_mtxunlock(d->lock); 625285648f9SCameron Grant return EBUSY; 626285648f9SCameron Grant } 627c9b53085SCameron Grant } 628d95502a8SCameron Grant if (mixer_uninit(dev)) { 6295c25132aSGeorge C A Reid device_printf(dev, "unregister: mixer busy\n"); 63049c5e6e2SCameron Grant snd_mtxunlock(d->lock); 631c9b53085SCameron Grant return EBUSY; 632c9b53085SCameron Grant } 6337c438dbeSCameron Grant 63466ef8af5SCameron Grant #ifdef SND_DYNSYSCTL 63566ef8af5SCameron Grant d->sysctl_tree_top = NULL; 63666ef8af5SCameron Grant sysctl_ctx_free(&d->sysctl_tree); 63766ef8af5SCameron Grant #endif 638faeebea2SCameron Grant while (!SLIST_EMPTY(&d->channels)) 639285648f9SCameron Grant pcm_killchan(dev); 6407c438dbeSCameron Grant 64166ef8af5SCameron Grant chn_kill(d->fakechan); 64266ef8af5SCameron Grant fkchan_kill(d->fakechan); 64382db23e2SCameron Grant 64449c5e6e2SCameron Grant snd_mtxfree(d->lock); 64533dbf14aSCameron Grant return 0; 64633dbf14aSCameron Grant } 6477c438dbeSCameron Grant 64867b1dce3SCameron Grant /************************************************************************/ 64967b1dce3SCameron Grant 65067b1dce3SCameron Grant static int 65167b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 65267b1dce3SCameron Grant { 65367b1dce3SCameron Grant struct snddev_info *d; 65467b1dce3SCameron Grant struct snddev_channel *sce; 65567b1dce3SCameron Grant struct pcm_channel *c; 65667b1dce3SCameron Grant struct pcm_feeder *f; 657a3285889SCameron Grant char *fsep; 65867b1dce3SCameron Grant int pc, rc, vc; 65967b1dce3SCameron Grant 66067b1dce3SCameron Grant if (verbose < 1) 66167b1dce3SCameron Grant return 0; 66267b1dce3SCameron Grant 66367b1dce3SCameron Grant d = device_get_softc(dev); 66467b1dce3SCameron Grant if (!d) 66567b1dce3SCameron Grant return ENXIO; 66667b1dce3SCameron Grant 66767b1dce3SCameron Grant snd_mtxlock(d->lock); 66867b1dce3SCameron Grant if (!SLIST_EMPTY(&d->channels)) { 66967b1dce3SCameron Grant pc = rc = vc = 0; 67067b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 67167b1dce3SCameron Grant c = sce->channel; 67267b1dce3SCameron Grant if (c->direction == PCMDIR_PLAY) { 67367b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 67467b1dce3SCameron Grant vc++; 67567b1dce3SCameron Grant else 67667b1dce3SCameron Grant pc++; 67767b1dce3SCameron Grant } else 67867b1dce3SCameron Grant rc++; 67967b1dce3SCameron Grant } 68067b1dce3SCameron Grant sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", pc, rc, vc, 68167b1dce3SCameron Grant (d->flags & SD_F_SIMPLEX)? "" : " duplex", 68267b1dce3SCameron Grant #ifdef USING_DEVFS 68367b1dce3SCameron Grant (device_get_unit(dev) == snd_unit)? " default" : "" 68467b1dce3SCameron Grant #else 68567b1dce3SCameron Grant "" 68667b1dce3SCameron Grant #endif 68767b1dce3SCameron Grant ); 68867b1dce3SCameron Grant if (verbose <= 1) 68967b1dce3SCameron Grant goto skipverbose; 69067b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 69167b1dce3SCameron Grant c = sce->channel; 692a3285889SCameron Grant sbuf_printf(s, "\n\t"); 693a3285889SCameron Grant 694a3285889SCameron Grant sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 695a3285889SCameron Grant sbuf_printf(s, "speed %d, format %08x, flags %08x", c->speed, c->format, c->flags); 69667b1dce3SCameron Grant if (c->pid != -1) 69767b1dce3SCameron Grant sbuf_printf(s, ", pid %d", c->pid); 69867b1dce3SCameron Grant sbuf_printf(s, "\n\t"); 699edecdda7SCameron Grant if (c->bufhard != NULL && c->bufsoft != NULL) { 700a3285889SCameron Grant sbuf_printf(s, "interrupts %d, ", c->interrupts); 701a3285889SCameron Grant if (c->direction == PCMDIR_REC) 702a3285889SCameron Grant sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 703a3285889SCameron Grant c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 704a3285889SCameron Grant else 705edecdda7SCameron Grant sbuf_printf(s, "underruns %d, ready %d", 706edecdda7SCameron Grant c->xruns, sndbuf_getready(c->bufsoft)); 707a3285889SCameron Grant sbuf_printf(s, "\n\t"); 708a3285889SCameron Grant } 709a3285889SCameron Grant fsep = (c->direction == PCMDIR_REC)? " -> " : " <- "; 710edecdda7SCameron Grant sbuf_printf(s, "{hardware}%s", fsep); 71167b1dce3SCameron Grant f = c->feeder; 71267b1dce3SCameron Grant while (f) { 71367b1dce3SCameron Grant sbuf_printf(s, "%s", f->class->name); 71467b1dce3SCameron Grant if (f->desc->type == FEEDER_FMT) 715a3285889SCameron Grant sbuf_printf(s, "(%08x%s%08x)", f->desc->out, fsep, f->desc->in); 71667b1dce3SCameron Grant if (f->desc->type == FEEDER_RATE) 717a3285889SCameron Grant sbuf_printf(s, "(%d%s%d)", FEEDER_GET(f, FEEDRATE_DST), fsep, FEEDER_GET(f, FEEDRATE_SRC)); 71867b1dce3SCameron Grant if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 71967b1dce3SCameron Grant sbuf_printf(s, "(%08x)", f->desc->out); 720a3285889SCameron Grant sbuf_printf(s, "%s", fsep); 72167b1dce3SCameron Grant f = f->source; 72267b1dce3SCameron Grant } 723edecdda7SCameron Grant sbuf_printf(s, "{userland}"); 72467b1dce3SCameron Grant } 72567b1dce3SCameron Grant skipverbose: 72667b1dce3SCameron Grant } else 72767b1dce3SCameron Grant sbuf_printf(s, " (mixer only)"); 72867b1dce3SCameron Grant snd_mtxunlock(d->lock); 72967b1dce3SCameron Grant 73067b1dce3SCameron Grant return 0; 73167b1dce3SCameron Grant } 73267b1dce3SCameron Grant 73367b1dce3SCameron Grant /************************************************************************/ 73467b1dce3SCameron Grant 73567b1dce3SCameron Grant #ifdef SND_DYNSYSCTL 73667b1dce3SCameron Grant int 73767b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 73867b1dce3SCameron Grant { 73967b1dce3SCameron Grant struct snddev_info *d; 74067b1dce3SCameron Grant struct snddev_channel *sce; 74167b1dce3SCameron Grant struct pcm_channel *c; 74267b1dce3SCameron Grant int err, oldcnt, newcnt, cnt; 74367b1dce3SCameron Grant 74467b1dce3SCameron Grant d = oidp->oid_arg1; 74567b1dce3SCameron Grant 74667b1dce3SCameron Grant pcm_lock(d); 74767b1dce3SCameron Grant cnt = 0; 74867b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 74967b1dce3SCameron Grant c = sce->channel; 75067b1dce3SCameron Grant if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) 75167b1dce3SCameron Grant cnt++; 75267b1dce3SCameron Grant } 75367b1dce3SCameron Grant oldcnt = cnt; 75467b1dce3SCameron Grant newcnt = cnt; 75567b1dce3SCameron Grant 75667b1dce3SCameron Grant err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 75767b1dce3SCameron Grant if (err == 0 && req->newptr != NULL) { 75867b1dce3SCameron Grant if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 75967b1dce3SCameron Grant pcm_unlock(d); 76067b1dce3SCameron Grant return EINVAL; 76167b1dce3SCameron Grant } 76267b1dce3SCameron Grant 76367b1dce3SCameron Grant if (newcnt > cnt) { 76467b1dce3SCameron Grant /* add new vchans - find a parent channel first */ 76567b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 76667b1dce3SCameron Grant c = sce->channel; 76767b1dce3SCameron Grant /* not a candidate if not a play channel */ 76867b1dce3SCameron Grant if (c->direction != PCMDIR_PLAY) 76967b1dce3SCameron Grant goto addskip; 77067b1dce3SCameron Grant /* not a candidate if a virtual channel */ 77167b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 77267b1dce3SCameron Grant goto addskip; 77367b1dce3SCameron Grant /* not a candidate if it's in use */ 77467b1dce3SCameron Grant if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 77567b1dce3SCameron Grant goto addskip; 77667b1dce3SCameron Grant /* 77767b1dce3SCameron Grant * if we get here we're a nonvirtual play channel, and either 77867b1dce3SCameron Grant * 1) not busy 77967b1dce3SCameron Grant * 2) busy with children, not directly open 78067b1dce3SCameron Grant * 78167b1dce3SCameron Grant * thus we can add children 78267b1dce3SCameron Grant */ 78367b1dce3SCameron Grant goto addok; 78467b1dce3SCameron Grant addskip: 78567b1dce3SCameron Grant } 78667b1dce3SCameron Grant pcm_unlock(d); 78767b1dce3SCameron Grant return EBUSY; 78867b1dce3SCameron Grant addok: 78967b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 79067b1dce3SCameron Grant while (err == 0 && newcnt > cnt) { 79167b1dce3SCameron Grant err = vchan_create(c); 79267b1dce3SCameron Grant if (err == 0) 79367b1dce3SCameron Grant cnt++; 79467b1dce3SCameron Grant } 79567b1dce3SCameron Grant if (SLIST_EMPTY(&c->children)) 79667b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 79767b1dce3SCameron Grant } else if (newcnt < cnt) { 79867b1dce3SCameron Grant while (err == 0 && newcnt < cnt) { 79967b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 80067b1dce3SCameron Grant c = sce->channel; 80167b1dce3SCameron Grant if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 80267b1dce3SCameron Grant goto remok; 80367b1dce3SCameron Grant } 80467b1dce3SCameron Grant pcm_unlock(d); 80567b1dce3SCameron Grant return EINVAL; 80667b1dce3SCameron Grant remok: 80767b1dce3SCameron Grant err = vchan_destroy(c); 80867b1dce3SCameron Grant if (err == 0) 80967b1dce3SCameron Grant cnt--; 81067b1dce3SCameron Grant } 81167b1dce3SCameron Grant } 81267b1dce3SCameron Grant } 81367b1dce3SCameron Grant 81467b1dce3SCameron Grant pcm_unlock(d); 81567b1dce3SCameron Grant return err; 81667b1dce3SCameron Grant } 81767b1dce3SCameron Grant #endif 81867b1dce3SCameron Grant 81967b1dce3SCameron Grant /************************************************************************/ 82067b1dce3SCameron Grant 82133dbf14aSCameron Grant static moduledata_t sndpcm_mod = { 82233dbf14aSCameron Grant "snd_pcm", 823d95502a8SCameron Grant NULL, 82433dbf14aSCameron Grant NULL 82533dbf14aSCameron Grant }; 82633dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 82733dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER); 828