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; 44a67fe5c1SCameron Grant unsigned devcount, playcount, reccount, vchancount; 4567b1dce3SCameron Grant unsigned flags; 4667b1dce3SCameron Grant int inprog; 47a67fe5c1SCameron Grant unsigned int bufsz; 4867b1dce3SCameron Grant void *devinfo; 4967b1dce3SCameron Grant device_t dev; 5067b1dce3SCameron Grant char status[SND_STATUSLEN]; 5167b1dce3SCameron Grant struct sysctl_ctx_list sysctl_tree; 5267b1dce3SCameron Grant struct sysctl_oid *sysctl_tree_top; 5367b1dce3SCameron Grant void *lock; 5467b1dce3SCameron Grant }; 5567b1dce3SCameron Grant 56d95502a8SCameron Grant devclass_t pcm_devclass; 5782db23e2SCameron Grant 5882db23e2SCameron Grant #ifdef USING_DEVFS 59d95502a8SCameron Grant int snd_unit = 0; 6009786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit); 6182db23e2SCameron Grant #endif 62cbe7d6a3SCameron Grant 6367b1dce3SCameron Grant int snd_maxautovchans = 0; 6467b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 65987e5972SCameron Grant 6682db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 6782db23e2SCameron Grant 6867b1dce3SCameron Grant static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 6967b1dce3SCameron Grant 7067b1dce3SCameron Grant struct sysctl_ctx_list * 7167b1dce3SCameron Grant snd_sysctl_tree(device_t dev) 7267b1dce3SCameron Grant { 7367b1dce3SCameron Grant struct snddev_info *d = device_get_softc(dev); 7467b1dce3SCameron Grant 7567b1dce3SCameron Grant return &d->sysctl_tree; 7667b1dce3SCameron Grant } 7767b1dce3SCameron Grant 7867b1dce3SCameron Grant struct sysctl_oid * 7967b1dce3SCameron Grant snd_sysctl_tree_top(device_t dev) 8067b1dce3SCameron Grant { 8167b1dce3SCameron Grant struct snddev_info *d = device_get_softc(dev); 8267b1dce3SCameron Grant 8367b1dce3SCameron Grant return d->sysctl_tree_top; 8467b1dce3SCameron Grant } 8567b1dce3SCameron Grant 8637209180SCameron Grant void * 8737209180SCameron Grant snd_mtxcreate(const char *desc) 8837209180SCameron Grant { 8937209180SCameron Grant #ifdef USING_MUTEX 9037209180SCameron Grant struct mtx *m; 9137209180SCameron Grant 9237209180SCameron Grant m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 9337209180SCameron Grant if (m == NULL) 9437209180SCameron Grant return NULL; 9537209180SCameron Grant mtx_init(m, desc, MTX_RECURSE); 9637209180SCameron Grant return m; 9737209180SCameron Grant #else 98a983d575SCameron Grant return (void *)0xcafebabe; 9937209180SCameron Grant #endif 10037209180SCameron Grant } 10137209180SCameron Grant 10237209180SCameron Grant void 10337209180SCameron Grant snd_mtxfree(void *m) 10437209180SCameron Grant { 10537209180SCameron Grant #ifdef USING_MUTEX 10637209180SCameron Grant struct mtx *mtx = m; 10737209180SCameron Grant 10837209180SCameron Grant mtx_assert(mtx, MA_OWNED); 10937209180SCameron Grant mtx_destroy(mtx); 11037209180SCameron Grant free(mtx, M_DEVBUF); 11137209180SCameron Grant #endif 11237209180SCameron Grant } 11337209180SCameron Grant 11437209180SCameron Grant void 11537209180SCameron Grant snd_mtxassert(void *m) 11637209180SCameron Grant { 11737209180SCameron Grant #ifdef USING_MUTEX 118f00f162aSCameron Grant #ifdef INVARIANTS 11937209180SCameron Grant struct mtx *mtx = m; 12037209180SCameron Grant 12137209180SCameron Grant mtx_assert(mtx, MA_OWNED); 12237209180SCameron Grant #endif 123f00f162aSCameron Grant #endif 12437209180SCameron Grant } 12537209180SCameron Grant 12637209180SCameron Grant void 12737209180SCameron Grant snd_mtxlock(void *m) 12837209180SCameron Grant { 12937209180SCameron Grant #ifdef USING_MUTEX 13037209180SCameron Grant struct mtx *mtx = m; 13137209180SCameron Grant 13237209180SCameron Grant mtx_lock(mtx); 13337209180SCameron Grant #endif 13437209180SCameron Grant } 13537209180SCameron Grant 13637209180SCameron Grant void 13737209180SCameron Grant snd_mtxunlock(void *m) 13837209180SCameron Grant { 13937209180SCameron Grant #ifdef USING_MUTEX 14037209180SCameron Grant struct mtx *mtx = m; 14137209180SCameron Grant 14237209180SCameron Grant mtx_unlock(mtx); 14337209180SCameron Grant #endif 14437209180SCameron Grant } 14537209180SCameron Grant 14637209180SCameron Grant int 14737209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 14837209180SCameron Grant { 14937209180SCameron Grant #ifdef USING_MUTEX 15037209180SCameron Grant flags &= INTR_MPSAFE; 15146700f12SPeter Wemm flags |= INTR_TYPE_AV; 15237209180SCameron Grant #else 15346700f12SPeter Wemm flags = INTR_TYPE_AV; 15437209180SCameron Grant #endif 15537209180SCameron Grant return bus_setup_intr(dev, res, flags, hand, param, cookiep); 15637209180SCameron Grant } 15737209180SCameron Grant 15867b1dce3SCameron Grant void 15967b1dce3SCameron Grant pcm_lock(struct snddev_info *d) 16067b1dce3SCameron Grant { 16167b1dce3SCameron Grant snd_mtxlock(d->lock); 16267b1dce3SCameron Grant } 16367b1dce3SCameron Grant 16467b1dce3SCameron Grant void 16567b1dce3SCameron Grant pcm_unlock(struct snddev_info *d) 16667b1dce3SCameron Grant { 16767b1dce3SCameron Grant snd_mtxunlock(d->lock); 16867b1dce3SCameron Grant } 16967b1dce3SCameron Grant 17067b1dce3SCameron Grant struct pcm_channel * 17167b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d) 17267b1dce3SCameron Grant { 17367b1dce3SCameron Grant return d->fakechan; 17467b1dce3SCameron Grant } 17567b1dce3SCameron Grant 176b8f0d9e0SCameron Grant /* return a locked channel */ 177285648f9SCameron Grant struct pcm_channel * 178506a5308SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum) 179285648f9SCameron Grant { 180285648f9SCameron Grant struct pcm_channel *c; 181285648f9SCameron Grant struct snddev_channel *sce; 182f637a36cSCameron Grant int err; 183285648f9SCameron Grant 184b8f0d9e0SCameron Grant snd_mtxassert(d->lock); 185f637a36cSCameron Grant 186f637a36cSCameron Grant /* scan for a free channel */ 187285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 188285648f9SCameron Grant c = sce->channel; 18949c5e6e2SCameron Grant CHN_LOCK(c); 190285648f9SCameron Grant if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 191506a5308SCameron Grant if (chnum == -1 || c->num == chnum) { 192285648f9SCameron Grant c->flags |= CHN_F_BUSY; 193b8f0d9e0SCameron Grant c->pid = pid; 194285648f9SCameron Grant return c; 195285648f9SCameron Grant } 196506a5308SCameron Grant } 19749c5e6e2SCameron Grant CHN_UNLOCK(c); 198285648f9SCameron Grant } 199f637a36cSCameron Grant 200f637a36cSCameron Grant /* no channel available */ 201f637a36cSCameron Grant if (direction == PCMDIR_PLAY) { 20267b1dce3SCameron Grant if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { 203f637a36cSCameron Grant /* try to create a vchan */ 204f637a36cSCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 205f637a36cSCameron Grant c = sce->channel; 206f637a36cSCameron Grant if (!SLIST_EMPTY(&c->children)) { 207f637a36cSCameron Grant err = vchan_create(c); 208f637a36cSCameron Grant if (!err) 209506a5308SCameron Grant return pcm_chnalloc(d, direction, pid, -1); 210f637a36cSCameron Grant else 211f637a36cSCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 212f637a36cSCameron Grant } 213f637a36cSCameron Grant } 214f637a36cSCameron Grant } 215f637a36cSCameron Grant } 216f637a36cSCameron Grant 217285648f9SCameron Grant return NULL; 218285648f9SCameron Grant } 219285648f9SCameron Grant 220b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 221285648f9SCameron Grant int 222b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 223285648f9SCameron Grant { 224b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 225285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 226b8f0d9e0SCameron Grant c->pid = -1; 22749c5e6e2SCameron Grant CHN_UNLOCK(c); 228285648f9SCameron Grant return 0; 229285648f9SCameron Grant } 230285648f9SCameron Grant 231285648f9SCameron Grant int 232285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 233285648f9SCameron Grant { 23449c5e6e2SCameron Grant int r; 23549c5e6e2SCameron Grant 236b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 237285648f9SCameron Grant c->refcount += ref; 23849c5e6e2SCameron Grant r = c->refcount; 23949c5e6e2SCameron Grant return r; 240285648f9SCameron Grant } 241285648f9SCameron Grant 24267b1dce3SCameron Grant int 24367b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta) 24467b1dce3SCameron Grant { 24567b1dce3SCameron Grant d->inprog += delta; 24667b1dce3SCameron Grant return d->inprog; 24767b1dce3SCameron Grant } 24867b1dce3SCameron Grant 24967b1dce3SCameron Grant static void 25067b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 25167b1dce3SCameron Grant { 25267b1dce3SCameron Grant struct pcm_channel *c; 25367b1dce3SCameron Grant struct snddev_channel *sce; 25467b1dce3SCameron Grant int err, done; 25567b1dce3SCameron Grant 25667b1dce3SCameron Grant if (num > 0 && d->vchancount == 0) { 25767b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 25867b1dce3SCameron Grant c = sce->channel; 25967b1dce3SCameron Grant if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) { 26067b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 26167b1dce3SCameron Grant err = vchan_create(c); 26267b1dce3SCameron Grant if (err) { 26367b1dce3SCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 26467b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 26567b1dce3SCameron Grant } 26667b1dce3SCameron Grant return; 26767b1dce3SCameron Grant } 26867b1dce3SCameron Grant } 26967b1dce3SCameron Grant } 27067b1dce3SCameron Grant if (num == 0 && d->vchancount > 0) { 27167b1dce3SCameron Grant done = 0; 27267b1dce3SCameron Grant while (!done) { 27367b1dce3SCameron Grant done = 1; 27467b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 27567b1dce3SCameron Grant c = sce->channel; 27667b1dce3SCameron Grant if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) { 27767b1dce3SCameron Grant done = 0; 27867b1dce3SCameron Grant err = vchan_destroy(c); 27967b1dce3SCameron Grant if (err) 28067b1dce3SCameron Grant device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err); 28167b1dce3SCameron Grant goto restart; 28267b1dce3SCameron Grant } 28367b1dce3SCameron Grant } 28467b1dce3SCameron Grant restart: 28567b1dce3SCameron Grant } 28667b1dce3SCameron Grant } 28767b1dce3SCameron Grant } 28867b1dce3SCameron Grant 28982db23e2SCameron Grant #ifdef USING_DEVFS 29033dbf14aSCameron Grant static int 291cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 29233dbf14aSCameron Grant { 293b8f0d9e0SCameron Grant struct snddev_info *d; 29433dbf14aSCameron Grant int error, unit; 29533dbf14aSCameron Grant 29633dbf14aSCameron Grant unit = snd_unit; 29733dbf14aSCameron Grant error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 29833dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 29974ffd138SCameron Grant if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 300b8f0d9e0SCameron Grant return EINVAL; 301b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 302faeebea2SCameron Grant if (d == NULL || SLIST_EMPTY(&d->channels)) 303b8f0d9e0SCameron Grant return EINVAL; 30433dbf14aSCameron Grant snd_unit = unit; 30533dbf14aSCameron Grant } 30633dbf14aSCameron Grant return (error); 30733dbf14aSCameron Grant } 308b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 309cd9766c5SCameron Grant 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 31082db23e2SCameron Grant #endif 311987e5972SCameron Grant 312cd9766c5SCameron Grant static int 31367b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 314cd9766c5SCameron Grant { 31567b1dce3SCameron Grant struct snddev_info *d; 31667b1dce3SCameron Grant int i, v, error; 317cd9766c5SCameron Grant 31867b1dce3SCameron Grant v = snd_maxautovchans; 319cd9766c5SCameron Grant error = sysctl_handle_int(oidp, &v, sizeof(v), req); 320cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 321cd9766c5SCameron Grant if (v < 0 || v >= SND_MAXVCHANS) 322cd9766c5SCameron Grant return EINVAL; 32367b1dce3SCameron Grant if (v != snd_maxautovchans) { 32467b1dce3SCameron Grant for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 32567b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 32667b1dce3SCameron Grant if (!d) 32767b1dce3SCameron Grant continue; 32867b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 32967b1dce3SCameron Grant } 33067b1dce3SCameron Grant } 33167b1dce3SCameron Grant snd_maxautovchans = v; 332cd9766c5SCameron Grant } 333cd9766c5SCameron Grant return (error); 334cd9766c5SCameron Grant } 33567b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 33667b1dce3SCameron Grant 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 337f637a36cSCameron Grant 338285648f9SCameron Grant struct pcm_channel * 339285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 340987e5972SCameron Grant { 341285648f9SCameron Grant struct pcm_channel *ch; 34233dbf14aSCameron Grant char *dirs; 343a67fe5c1SCameron Grant int err, *pnum; 344987e5972SCameron Grant 345285648f9SCameron Grant switch(dir) { 346285648f9SCameron Grant case PCMDIR_PLAY: 347285648f9SCameron Grant dirs = "play"; 348a67fe5c1SCameron Grant pnum = &d->playcount; 349285648f9SCameron Grant break; 350a67fe5c1SCameron Grant 351285648f9SCameron Grant case PCMDIR_REC: 352285648f9SCameron Grant dirs = "record"; 353a67fe5c1SCameron Grant pnum = &d->reccount; 354285648f9SCameron Grant break; 355a67fe5c1SCameron Grant 356285648f9SCameron Grant case PCMDIR_VIRTUAL: 357285648f9SCameron Grant dirs = "virtual"; 358285648f9SCameron Grant dir = PCMDIR_PLAY; 359a67fe5c1SCameron Grant pnum = &d->vchancount; 360285648f9SCameron Grant break; 361a67fe5c1SCameron Grant 362285648f9SCameron Grant default: 363285648f9SCameron Grant return NULL; 3649c326820SCameron Grant } 365285648f9SCameron Grant 366285648f9SCameron Grant ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 367285648f9SCameron Grant if (!ch) 368285648f9SCameron Grant return NULL; 369285648f9SCameron Grant 3700f55ac6cSCameron Grant ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 371285648f9SCameron Grant if (!ch->methods) { 372285648f9SCameron Grant free(ch, M_DEVBUF); 373a67fe5c1SCameron Grant 374285648f9SCameron Grant return NULL; 375285648f9SCameron Grant } 376285648f9SCameron Grant 377a67fe5c1SCameron Grant ch->num = (*pnum)++; 378a67fe5c1SCameron Grant 379285648f9SCameron Grant ch->pid = -1; 380285648f9SCameron Grant ch->parentsnddev = d; 381285648f9SCameron Grant ch->parentchannel = parent; 382a67fe5c1SCameron Grant snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num); 383285648f9SCameron Grant 3840f55ac6cSCameron Grant err = chn_init(ch, devinfo, dir); 3850f55ac6cSCameron Grant if (err) { 386a67fe5c1SCameron Grant device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 387285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 388285648f9SCameron Grant free(ch, M_DEVBUF); 389a67fe5c1SCameron Grant (*pnum)--; 390a67fe5c1SCameron Grant 391285648f9SCameron Grant return NULL; 392bbb5bf3dSCameron Grant } 393285648f9SCameron Grant 394285648f9SCameron Grant return ch; 395285648f9SCameron Grant } 396285648f9SCameron Grant 397285648f9SCameron Grant int 398285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 399285648f9SCameron Grant { 400a67fe5c1SCameron Grant struct snddev_info *d; 401285648f9SCameron Grant int err; 402285648f9SCameron Grant 403a67fe5c1SCameron Grant d = ch->parentsnddev; 404285648f9SCameron Grant err = chn_kill(ch); 405285648f9SCameron Grant if (err) { 406a67fe5c1SCameron Grant device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 407285648f9SCameron Grant return err; 408285648f9SCameron Grant } 409285648f9SCameron Grant 410a67fe5c1SCameron Grant if (ch->direction == PCMDIR_REC) 411a67fe5c1SCameron Grant d->reccount--; 412a67fe5c1SCameron Grant else if (ch->flags & CHN_F_VIRTUAL) 413a67fe5c1SCameron Grant d->vchancount--; 414a67fe5c1SCameron Grant else 415a67fe5c1SCameron Grant d->playcount--; 416a67fe5c1SCameron Grant 417285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 418285648f9SCameron Grant free(ch, M_DEVBUF); 419285648f9SCameron Grant 420285648f9SCameron Grant return 0; 421285648f9SCameron Grant } 422285648f9SCameron Grant 423285648f9SCameron Grant int 424f637a36cSCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 425285648f9SCameron Grant { 42667b1dce3SCameron Grant struct snddev_channel *sce, *tmp, *after; 427285648f9SCameron Grant int unit = device_get_unit(d->dev); 428b8f0d9e0SCameron Grant 429b8f0d9e0SCameron Grant snd_mtxlock(d->lock); 430285648f9SCameron Grant 431285648f9SCameron Grant sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 432285648f9SCameron Grant if (!sce) { 433b8f0d9e0SCameron Grant snd_mtxunlock(d->lock); 434285648f9SCameron Grant return ENOMEM; 435285648f9SCameron Grant } 436285648f9SCameron Grant 437285648f9SCameron Grant sce->channel = ch; 43867b1dce3SCameron Grant if (SLIST_EMPTY(&d->channels)) { 439285648f9SCameron Grant SLIST_INSERT_HEAD(&d->channels, sce, link); 44067b1dce3SCameron Grant } else { 44167b1dce3SCameron Grant after = NULL; 44267b1dce3SCameron Grant SLIST_FOREACH(tmp, &d->channels, link) { 44367b1dce3SCameron Grant after = tmp; 44467b1dce3SCameron Grant } 44567b1dce3SCameron Grant SLIST_INSERT_AFTER(after, sce, link); 44667b1dce3SCameron Grant } 447285648f9SCameron Grant 448506a5308SCameron Grant if (mkdev) { 449f637a36cSCameron Grant dsp_register(unit, d->devcount++); 450506a5308SCameron Grant if (ch->direction == PCMDIR_REC) 451506a5308SCameron Grant dsp_registerrec(unit, ch->num); 452506a5308SCameron Grant } 453b8f0d9e0SCameron Grant 45449c5e6e2SCameron Grant snd_mtxunlock(d->lock); 455285648f9SCameron Grant 45633dbf14aSCameron Grant return 0; 45733dbf14aSCameron Grant } 45833dbf14aSCameron Grant 459285648f9SCameron Grant int 460f637a36cSCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 46133dbf14aSCameron Grant { 462285648f9SCameron Grant struct snddev_channel *sce; 463285648f9SCameron Grant int unit = device_get_unit(d->dev); 46433dbf14aSCameron Grant 46549c5e6e2SCameron Grant snd_mtxlock(d->lock); 466285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 467285648f9SCameron Grant if (sce->channel == ch) 468285648f9SCameron Grant goto gotit; 46933dbf14aSCameron Grant } 47049c5e6e2SCameron Grant snd_mtxunlock(d->lock); 471285648f9SCameron Grant return EINVAL; 472285648f9SCameron Grant gotit: 473285648f9SCameron Grant SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 474285648f9SCameron Grant free(sce, M_DEVBUF); 475285648f9SCameron Grant 476506a5308SCameron Grant if (rmdev) { 477f637a36cSCameron Grant dsp_unregister(unit, --d->devcount); 478506a5308SCameron Grant if (ch->direction == PCMDIR_REC) 479a67fe5c1SCameron Grant dsp_unregisterrec(unit, ch->num); 480506a5308SCameron Grant } 48149c5e6e2SCameron Grant snd_mtxunlock(d->lock); 482285648f9SCameron Grant 483987e5972SCameron Grant return 0; 484987e5972SCameron Grant } 485987e5972SCameron Grant 486987e5972SCameron Grant int 487285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 488285648f9SCameron Grant { 489285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 49067b1dce3SCameron Grant struct pcm_channel *ch; 49167b1dce3SCameron Grant int err; 492285648f9SCameron Grant 493285648f9SCameron Grant ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 494285648f9SCameron Grant if (!ch) { 495285648f9SCameron Grant device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 496285648f9SCameron Grant return ENODEV; 497285648f9SCameron Grant } 498cd9766c5SCameron Grant 499f637a36cSCameron Grant err = pcm_chn_add(d, ch, 1); 500285648f9SCameron Grant if (err) { 501285648f9SCameron Grant device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 502285648f9SCameron Grant pcm_chn_destroy(ch); 503cd9766c5SCameron Grant return err; 504cd9766c5SCameron Grant } 505cd9766c5SCameron Grant 50667b1dce3SCameron Grant if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) { 507cd9766c5SCameron Grant ch->flags |= CHN_F_BUSY; 508cd9766c5SCameron Grant err = vchan_create(ch); 509cd9766c5SCameron Grant if (err) { 51067b1dce3SCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 51167b1dce3SCameron Grant ch->flags &= ~CHN_F_BUSY; 512cd9766c5SCameron Grant } 513285648f9SCameron Grant } 514285648f9SCameron Grant 515285648f9SCameron Grant return err; 516285648f9SCameron Grant } 517285648f9SCameron Grant 518285648f9SCameron Grant static int 519285648f9SCameron Grant pcm_killchan(device_t dev) 520285648f9SCameron Grant { 521285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 522285648f9SCameron Grant struct snddev_channel *sce; 523285648f9SCameron Grant 52449c5e6e2SCameron Grant snd_mtxlock(d->lock); 525285648f9SCameron Grant sce = SLIST_FIRST(&d->channels); 52649c5e6e2SCameron Grant snd_mtxunlock(d->lock); 527285648f9SCameron Grant 528f637a36cSCameron Grant return pcm_chn_remove(d, sce->channel, 1); 529285648f9SCameron Grant } 530285648f9SCameron Grant 531285648f9SCameron Grant int 532987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 533987e5972SCameron Grant { 53466ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 53549c5e6e2SCameron Grant 53649c5e6e2SCameron Grant snd_mtxlock(d->lock); 537987e5972SCameron Grant strncpy(d->status, str, SND_STATUSLEN); 53849c5e6e2SCameron Grant snd_mtxunlock(d->lock); 539987e5972SCameron Grant return 0; 540987e5972SCameron Grant } 541987e5972SCameron Grant 542987e5972SCameron Grant u_int32_t 543987e5972SCameron Grant pcm_getflags(device_t dev) 544987e5972SCameron Grant { 54566ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 54649c5e6e2SCameron Grant 547987e5972SCameron Grant return d->flags; 548987e5972SCameron Grant } 549987e5972SCameron Grant 550987e5972SCameron Grant void 551987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val) 552987e5972SCameron Grant { 55366ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 554d95502a8SCameron Grant 555987e5972SCameron Grant d->flags = val; 556987e5972SCameron Grant } 557987e5972SCameron Grant 55839004e69SCameron Grant void * 55939004e69SCameron Grant pcm_getdevinfo(device_t dev) 56039004e69SCameron Grant { 56166ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 56249c5e6e2SCameron Grant 56339004e69SCameron Grant return d->devinfo; 56439004e69SCameron Grant } 56539004e69SCameron Grant 566a67fe5c1SCameron Grant unsigned int 567a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 568a67fe5c1SCameron Grant { 569a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 570a67fe5c1SCameron Grant int sz; 571a67fe5c1SCameron Grant 572a67fe5c1SCameron Grant sz = 0; 573a67fe5c1SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) 574a67fe5c1SCameron Grant RANGE(sz, min, max); 575a67fe5c1SCameron Grant else 576a67fe5c1SCameron Grant sz = deflt; 577a67fe5c1SCameron Grant d->bufsz = sz; 578a67fe5c1SCameron Grant 579a67fe5c1SCameron Grant return sz; 580a67fe5c1SCameron Grant } 581a67fe5c1SCameron Grant 582987e5972SCameron Grant int 583987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 584987e5972SCameron Grant { 58566ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 586987e5972SCameron Grant 58749c5e6e2SCameron Grant d->lock = snd_mtxcreate(device_get_nameunit(dev)); 58849c5e6e2SCameron Grant snd_mtxlock(d->lock); 589285648f9SCameron Grant 590cd9766c5SCameron Grant d->flags = 0; 591e4d5b250SCameron Grant d->dev = dev; 592987e5972SCameron Grant d->devinfo = devinfo; 593f637a36cSCameron Grant d->devcount = 0; 594506a5308SCameron Grant d->reccount = 0; 595a67fe5c1SCameron Grant d->playcount = 0; 596f637a36cSCameron Grant d->vchancount = 0; 597d95502a8SCameron Grant d->inprog = 0; 598833f7023SCameron Grant 599d95502a8SCameron Grant if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 600285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 601d95502a8SCameron Grant 602285648f9SCameron Grant d->fakechan = fkchan_setup(dev); 603285648f9SCameron Grant chn_init(d->fakechan, NULL, 0); 604987e5972SCameron Grant 60582db23e2SCameron Grant #ifdef SND_DYNSYSCTL 606cc486d80SJohn Baldwin sysctl_ctx_init(&d->sysctl_tree); 607cc486d80SJohn Baldwin d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 608a3e893e0SJohn Baldwin SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 609cc486d80SJohn Baldwin device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 610a3e893e0SJohn Baldwin if (d->sysctl_tree_top == NULL) { 611cc486d80SJohn Baldwin sysctl_ctx_free(&d->sysctl_tree); 612cc486d80SJohn Baldwin goto no; 613cc486d80SJohn Baldwin } 614a67fe5c1SCameron Grant SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 615a67fe5c1SCameron Grant OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 61682db23e2SCameron Grant #endif 617b8f0d9e0SCameron Grant if (numplay > 0) 61867b1dce3SCameron Grant vchan_initsys(dev); 619cd9766c5SCameron Grant if (numplay == 1) 620cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 621cd9766c5SCameron Grant 62249c5e6e2SCameron Grant snd_mtxunlock(d->lock); 62367b1dce3SCameron Grant sndstat_register(dev, d->status, sndstat_prepare_pcm); 624987e5972SCameron Grant return 0; 625987e5972SCameron Grant no: 62649c5e6e2SCameron Grant snd_mtxfree(d->lock); 627987e5972SCameron Grant return ENXIO; 628987e5972SCameron Grant } 629987e5972SCameron Grant 63033dbf14aSCameron Grant int 63133dbf14aSCameron Grant pcm_unregister(device_t dev) 6327c438dbeSCameron Grant { 63366ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 634285648f9SCameron Grant struct snddev_channel *sce; 635a67fe5c1SCameron Grant struct pcm_channel *ch; 6367c438dbeSCameron Grant 63749c5e6e2SCameron Grant snd_mtxlock(d->lock); 638d95502a8SCameron Grant if (d->inprog) { 6395c25132aSGeorge C A Reid device_printf(dev, "unregister: operation in progress\n"); 6405c25132aSGeorge C A Reid snd_mtxunlock(d->lock); 6415c25132aSGeorge C A Reid return EBUSY; 6425c25132aSGeorge C A Reid } 6435c25132aSGeorge C A Reid if (sndstat_busy() != 0) { 6445c25132aSGeorge C A Reid device_printf(dev, "unregister: sndstat busy\n"); 645d95502a8SCameron Grant snd_mtxunlock(d->lock); 646d95502a8SCameron Grant return EBUSY; 647d95502a8SCameron Grant } 648285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 649a67fe5c1SCameron Grant ch = sce->channel; 650a67fe5c1SCameron Grant if (ch->refcount > 0) { 651a67fe5c1SCameron Grant device_printf(dev, "unregister: channel %s busy (pid %d)", ch->name, ch->pid); 65249c5e6e2SCameron Grant snd_mtxunlock(d->lock); 653285648f9SCameron Grant return EBUSY; 654285648f9SCameron Grant } 655c9b53085SCameron Grant } 656d95502a8SCameron Grant if (mixer_uninit(dev)) { 6575c25132aSGeorge C A Reid device_printf(dev, "unregister: mixer busy\n"); 65849c5e6e2SCameron Grant snd_mtxunlock(d->lock); 659c9b53085SCameron Grant return EBUSY; 660c9b53085SCameron Grant } 6617c438dbeSCameron Grant 66266ef8af5SCameron Grant #ifdef SND_DYNSYSCTL 66366ef8af5SCameron Grant d->sysctl_tree_top = NULL; 66466ef8af5SCameron Grant sysctl_ctx_free(&d->sysctl_tree); 66566ef8af5SCameron Grant #endif 666faeebea2SCameron Grant while (!SLIST_EMPTY(&d->channels)) 667285648f9SCameron Grant pcm_killchan(dev); 6687c438dbeSCameron Grant 66966ef8af5SCameron Grant chn_kill(d->fakechan); 67066ef8af5SCameron Grant fkchan_kill(d->fakechan); 67182db23e2SCameron Grant 67249c5e6e2SCameron Grant snd_mtxfree(d->lock); 67333dbf14aSCameron Grant return 0; 67433dbf14aSCameron Grant } 6757c438dbeSCameron Grant 67667b1dce3SCameron Grant /************************************************************************/ 67767b1dce3SCameron Grant 67867b1dce3SCameron Grant static int 67967b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 68067b1dce3SCameron Grant { 68167b1dce3SCameron Grant struct snddev_info *d; 68267b1dce3SCameron Grant struct snddev_channel *sce; 68367b1dce3SCameron Grant struct pcm_channel *c; 68467b1dce3SCameron Grant struct pcm_feeder *f; 685a3285889SCameron Grant char *fsep; 68667b1dce3SCameron Grant int pc, rc, vc; 68767b1dce3SCameron Grant 68867b1dce3SCameron Grant if (verbose < 1) 68967b1dce3SCameron Grant return 0; 69067b1dce3SCameron Grant 69167b1dce3SCameron Grant d = device_get_softc(dev); 69267b1dce3SCameron Grant if (!d) 69367b1dce3SCameron Grant return ENXIO; 69467b1dce3SCameron Grant 69567b1dce3SCameron Grant snd_mtxlock(d->lock); 69667b1dce3SCameron Grant if (!SLIST_EMPTY(&d->channels)) { 69767b1dce3SCameron Grant pc = rc = vc = 0; 69867b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 69967b1dce3SCameron Grant c = sce->channel; 70067b1dce3SCameron Grant if (c->direction == PCMDIR_PLAY) { 70167b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 70267b1dce3SCameron Grant vc++; 70367b1dce3SCameron Grant else 70467b1dce3SCameron Grant pc++; 70567b1dce3SCameron Grant } else 70667b1dce3SCameron Grant rc++; 70767b1dce3SCameron Grant } 708a67fe5c1SCameron Grant sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 70967b1dce3SCameron Grant (d->flags & SD_F_SIMPLEX)? "" : " duplex", 71067b1dce3SCameron Grant #ifdef USING_DEVFS 71167b1dce3SCameron Grant (device_get_unit(dev) == snd_unit)? " default" : "" 71267b1dce3SCameron Grant #else 71367b1dce3SCameron Grant "" 71467b1dce3SCameron Grant #endif 71567b1dce3SCameron Grant ); 71667b1dce3SCameron Grant if (verbose <= 1) 71767b1dce3SCameron Grant goto skipverbose; 71867b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 71967b1dce3SCameron Grant c = sce->channel; 720a3285889SCameron Grant sbuf_printf(s, "\n\t"); 721a3285889SCameron Grant 722a3285889SCameron Grant sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 723a3285889SCameron Grant sbuf_printf(s, "speed %d, format %08x, flags %08x", c->speed, c->format, c->flags); 72467b1dce3SCameron Grant if (c->pid != -1) 72567b1dce3SCameron Grant sbuf_printf(s, ", pid %d", c->pid); 72667b1dce3SCameron Grant sbuf_printf(s, "\n\t"); 727edecdda7SCameron Grant if (c->bufhard != NULL && c->bufsoft != NULL) { 728a3285889SCameron Grant sbuf_printf(s, "interrupts %d, ", c->interrupts); 729a3285889SCameron Grant if (c->direction == PCMDIR_REC) 730a3285889SCameron Grant sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 731a3285889SCameron Grant c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 732a3285889SCameron Grant else 733edecdda7SCameron Grant sbuf_printf(s, "underruns %d, ready %d", 734edecdda7SCameron Grant c->xruns, sndbuf_getready(c->bufsoft)); 735a3285889SCameron Grant sbuf_printf(s, "\n\t"); 736a3285889SCameron Grant } 737a3285889SCameron Grant fsep = (c->direction == PCMDIR_REC)? " -> " : " <- "; 738edecdda7SCameron Grant sbuf_printf(s, "{hardware}%s", fsep); 73967b1dce3SCameron Grant f = c->feeder; 74067b1dce3SCameron Grant while (f) { 74167b1dce3SCameron Grant sbuf_printf(s, "%s", f->class->name); 74267b1dce3SCameron Grant if (f->desc->type == FEEDER_FMT) 743a3285889SCameron Grant sbuf_printf(s, "(%08x%s%08x)", f->desc->out, fsep, f->desc->in); 74467b1dce3SCameron Grant if (f->desc->type == FEEDER_RATE) 745a3285889SCameron Grant sbuf_printf(s, "(%d%s%d)", FEEDER_GET(f, FEEDRATE_DST), fsep, FEEDER_GET(f, FEEDRATE_SRC)); 74667b1dce3SCameron Grant if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 74767b1dce3SCameron Grant sbuf_printf(s, "(%08x)", f->desc->out); 748a3285889SCameron Grant sbuf_printf(s, "%s", fsep); 74967b1dce3SCameron Grant f = f->source; 75067b1dce3SCameron Grant } 751edecdda7SCameron Grant sbuf_printf(s, "{userland}"); 75267b1dce3SCameron Grant } 75367b1dce3SCameron Grant skipverbose: 75467b1dce3SCameron Grant } else 75567b1dce3SCameron Grant sbuf_printf(s, " (mixer only)"); 75667b1dce3SCameron Grant snd_mtxunlock(d->lock); 75767b1dce3SCameron Grant 75867b1dce3SCameron Grant return 0; 75967b1dce3SCameron Grant } 76067b1dce3SCameron Grant 76167b1dce3SCameron Grant /************************************************************************/ 76267b1dce3SCameron Grant 76367b1dce3SCameron Grant #ifdef SND_DYNSYSCTL 76467b1dce3SCameron Grant int 76567b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 76667b1dce3SCameron Grant { 76767b1dce3SCameron Grant struct snddev_info *d; 76867b1dce3SCameron Grant struct snddev_channel *sce; 76967b1dce3SCameron Grant struct pcm_channel *c; 77067b1dce3SCameron Grant int err, oldcnt, newcnt, cnt; 77167b1dce3SCameron Grant 77267b1dce3SCameron Grant d = oidp->oid_arg1; 77367b1dce3SCameron Grant 77467b1dce3SCameron Grant pcm_lock(d); 77567b1dce3SCameron Grant cnt = 0; 77667b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 77767b1dce3SCameron Grant c = sce->channel; 77867b1dce3SCameron Grant if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) 77967b1dce3SCameron Grant cnt++; 78067b1dce3SCameron Grant } 78167b1dce3SCameron Grant oldcnt = cnt; 78267b1dce3SCameron Grant newcnt = cnt; 78367b1dce3SCameron Grant 78467b1dce3SCameron Grant err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 78567b1dce3SCameron Grant if (err == 0 && req->newptr != NULL) { 78667b1dce3SCameron Grant if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 78767b1dce3SCameron Grant pcm_unlock(d); 78867b1dce3SCameron Grant return EINVAL; 78967b1dce3SCameron Grant } 79067b1dce3SCameron Grant 79167b1dce3SCameron Grant if (newcnt > cnt) { 79267b1dce3SCameron Grant /* add new vchans - find a parent channel first */ 79367b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 79467b1dce3SCameron Grant c = sce->channel; 79567b1dce3SCameron Grant /* not a candidate if not a play channel */ 79667b1dce3SCameron Grant if (c->direction != PCMDIR_PLAY) 79767b1dce3SCameron Grant goto addskip; 79867b1dce3SCameron Grant /* not a candidate if a virtual channel */ 79967b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 80067b1dce3SCameron Grant goto addskip; 80167b1dce3SCameron Grant /* not a candidate if it's in use */ 80267b1dce3SCameron Grant if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 80367b1dce3SCameron Grant goto addskip; 80467b1dce3SCameron Grant /* 80567b1dce3SCameron Grant * if we get here we're a nonvirtual play channel, and either 80667b1dce3SCameron Grant * 1) not busy 80767b1dce3SCameron Grant * 2) busy with children, not directly open 80867b1dce3SCameron Grant * 80967b1dce3SCameron Grant * thus we can add children 81067b1dce3SCameron Grant */ 81167b1dce3SCameron Grant goto addok; 81267b1dce3SCameron Grant addskip: 81367b1dce3SCameron Grant } 81467b1dce3SCameron Grant pcm_unlock(d); 81567b1dce3SCameron Grant return EBUSY; 81667b1dce3SCameron Grant addok: 81767b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 81867b1dce3SCameron Grant while (err == 0 && newcnt > cnt) { 81967b1dce3SCameron Grant err = vchan_create(c); 82067b1dce3SCameron Grant if (err == 0) 82167b1dce3SCameron Grant cnt++; 82267b1dce3SCameron Grant } 82367b1dce3SCameron Grant if (SLIST_EMPTY(&c->children)) 82467b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 82567b1dce3SCameron Grant } else if (newcnt < cnt) { 82667b1dce3SCameron Grant while (err == 0 && newcnt < cnt) { 82767b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 82867b1dce3SCameron Grant c = sce->channel; 82967b1dce3SCameron Grant if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 83067b1dce3SCameron Grant goto remok; 83167b1dce3SCameron Grant } 83267b1dce3SCameron Grant pcm_unlock(d); 83367b1dce3SCameron Grant return EINVAL; 83467b1dce3SCameron Grant remok: 83567b1dce3SCameron Grant err = vchan_destroy(c); 83667b1dce3SCameron Grant if (err == 0) 83767b1dce3SCameron Grant cnt--; 83867b1dce3SCameron Grant } 83967b1dce3SCameron Grant } 84067b1dce3SCameron Grant } 84167b1dce3SCameron Grant 84267b1dce3SCameron Grant pcm_unlock(d); 84367b1dce3SCameron Grant return err; 84467b1dce3SCameron Grant } 84567b1dce3SCameron Grant #endif 84667b1dce3SCameron Grant 84767b1dce3SCameron Grant /************************************************************************/ 84867b1dce3SCameron Grant 84933dbf14aSCameron Grant static moduledata_t sndpcm_mod = { 85033dbf14aSCameron Grant "snd_pcm", 851d95502a8SCameron Grant NULL, 85233dbf14aSCameron Grant NULL 85333dbf14aSCameron Grant }; 85433dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 85533dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER); 856