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 58b8a36395SCameron Grant int pcm_veto_load = 1; 59b8a36395SCameron Grant 6082db23e2SCameron Grant #ifdef USING_DEVFS 61d95502a8SCameron Grant int snd_unit = 0; 6209786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit); 6382db23e2SCameron Grant #endif 64cbe7d6a3SCameron Grant 6567b1dce3SCameron Grant int snd_maxautovchans = 0; 6667b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 67987e5972SCameron Grant 6882db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 6982db23e2SCameron Grant 7067b1dce3SCameron Grant static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 7167b1dce3SCameron Grant 7267b1dce3SCameron Grant struct sysctl_ctx_list * 7367b1dce3SCameron Grant snd_sysctl_tree(device_t dev) 7467b1dce3SCameron Grant { 7567b1dce3SCameron Grant struct snddev_info *d = device_get_softc(dev); 7667b1dce3SCameron Grant 7767b1dce3SCameron Grant return &d->sysctl_tree; 7867b1dce3SCameron Grant } 7967b1dce3SCameron Grant 8067b1dce3SCameron Grant struct sysctl_oid * 8167b1dce3SCameron Grant snd_sysctl_tree_top(device_t dev) 8267b1dce3SCameron Grant { 8367b1dce3SCameron Grant struct snddev_info *d = device_get_softc(dev); 8467b1dce3SCameron Grant 8567b1dce3SCameron Grant return d->sysctl_tree_top; 8667b1dce3SCameron Grant } 8767b1dce3SCameron Grant 8837209180SCameron Grant void * 892c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type) 9037209180SCameron Grant { 9137209180SCameron Grant #ifdef USING_MUTEX 9237209180SCameron Grant struct mtx *m; 9337209180SCameron Grant 9437209180SCameron Grant m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 9537209180SCameron Grant if (m == NULL) 9637209180SCameron Grant return NULL; 972c69ba87SJohn Baldwin mtx_init(m, desc, type, MTX_RECURSE); 9837209180SCameron Grant return m; 9937209180SCameron Grant #else 100a983d575SCameron Grant return (void *)0xcafebabe; 10137209180SCameron Grant #endif 10237209180SCameron Grant } 10337209180SCameron Grant 10437209180SCameron Grant void 10537209180SCameron Grant snd_mtxfree(void *m) 10637209180SCameron Grant { 10737209180SCameron Grant #ifdef USING_MUTEX 10837209180SCameron Grant struct mtx *mtx = m; 10937209180SCameron Grant 11037209180SCameron Grant mtx_assert(mtx, MA_OWNED); 11137209180SCameron Grant mtx_destroy(mtx); 11237209180SCameron Grant free(mtx, M_DEVBUF); 11337209180SCameron Grant #endif 11437209180SCameron Grant } 11537209180SCameron Grant 11637209180SCameron Grant void 11737209180SCameron Grant snd_mtxassert(void *m) 11837209180SCameron Grant { 11937209180SCameron Grant #ifdef USING_MUTEX 120f00f162aSCameron Grant #ifdef INVARIANTS 12137209180SCameron Grant struct mtx *mtx = m; 12237209180SCameron Grant 12337209180SCameron Grant mtx_assert(mtx, MA_OWNED); 12437209180SCameron Grant #endif 125f00f162aSCameron Grant #endif 12637209180SCameron Grant } 12737209180SCameron Grant 12837209180SCameron Grant void 12937209180SCameron Grant snd_mtxlock(void *m) 13037209180SCameron Grant { 13137209180SCameron Grant #ifdef USING_MUTEX 13237209180SCameron Grant struct mtx *mtx = m; 13337209180SCameron Grant 13437209180SCameron Grant mtx_lock(mtx); 13537209180SCameron Grant #endif 13637209180SCameron Grant } 13737209180SCameron Grant 13837209180SCameron Grant void 13937209180SCameron Grant snd_mtxunlock(void *m) 14037209180SCameron Grant { 14137209180SCameron Grant #ifdef USING_MUTEX 14237209180SCameron Grant struct mtx *mtx = m; 14337209180SCameron Grant 14437209180SCameron Grant mtx_unlock(mtx); 14537209180SCameron Grant #endif 14637209180SCameron Grant } 14737209180SCameron Grant 14837209180SCameron Grant int 14937209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 15037209180SCameron Grant { 15137209180SCameron Grant #ifdef USING_MUTEX 15237209180SCameron Grant flags &= INTR_MPSAFE; 15346700f12SPeter Wemm flags |= INTR_TYPE_AV; 15437209180SCameron Grant #else 15546700f12SPeter Wemm flags = INTR_TYPE_AV; 15637209180SCameron Grant #endif 15737209180SCameron Grant return bus_setup_intr(dev, res, flags, hand, param, cookiep); 15837209180SCameron Grant } 15937209180SCameron Grant 16067b1dce3SCameron Grant void 16167b1dce3SCameron Grant pcm_lock(struct snddev_info *d) 16267b1dce3SCameron Grant { 16367b1dce3SCameron Grant snd_mtxlock(d->lock); 16467b1dce3SCameron Grant } 16567b1dce3SCameron Grant 16667b1dce3SCameron Grant void 16767b1dce3SCameron Grant pcm_unlock(struct snddev_info *d) 16867b1dce3SCameron Grant { 16967b1dce3SCameron Grant snd_mtxunlock(d->lock); 17067b1dce3SCameron Grant } 17167b1dce3SCameron Grant 17267b1dce3SCameron Grant struct pcm_channel * 17367b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d) 17467b1dce3SCameron Grant { 17567b1dce3SCameron Grant return d->fakechan; 17667b1dce3SCameron Grant } 17767b1dce3SCameron Grant 178b8f0d9e0SCameron Grant /* return a locked channel */ 179285648f9SCameron Grant struct pcm_channel * 180506a5308SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum) 181285648f9SCameron Grant { 182285648f9SCameron Grant struct pcm_channel *c; 183285648f9SCameron Grant struct snddev_channel *sce; 184f637a36cSCameron Grant int err; 185285648f9SCameron Grant 186b8f0d9e0SCameron Grant snd_mtxassert(d->lock); 187f637a36cSCameron Grant 188f637a36cSCameron Grant /* scan for a free channel */ 189285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 190285648f9SCameron Grant c = sce->channel; 19149c5e6e2SCameron Grant CHN_LOCK(c); 192285648f9SCameron Grant if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 193506a5308SCameron Grant if (chnum == -1 || c->num == chnum) { 194285648f9SCameron Grant c->flags |= CHN_F_BUSY; 195b8f0d9e0SCameron Grant c->pid = pid; 196285648f9SCameron Grant return c; 197285648f9SCameron Grant } 198506a5308SCameron Grant } 19949c5e6e2SCameron Grant CHN_UNLOCK(c); 200285648f9SCameron Grant } 201f637a36cSCameron Grant 202f637a36cSCameron Grant /* no channel available */ 203f637a36cSCameron Grant if (direction == PCMDIR_PLAY) { 20467b1dce3SCameron Grant if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { 205f637a36cSCameron Grant /* try to create a vchan */ 206f637a36cSCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 207f637a36cSCameron Grant c = sce->channel; 208f637a36cSCameron Grant if (!SLIST_EMPTY(&c->children)) { 209f637a36cSCameron Grant err = vchan_create(c); 210f637a36cSCameron Grant if (!err) 211506a5308SCameron Grant return pcm_chnalloc(d, direction, pid, -1); 212f637a36cSCameron Grant else 213f637a36cSCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 214f637a36cSCameron Grant } 215f637a36cSCameron Grant } 216f637a36cSCameron Grant } 217f637a36cSCameron Grant } 218f637a36cSCameron Grant 219285648f9SCameron Grant return NULL; 220285648f9SCameron Grant } 221285648f9SCameron Grant 222b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 223285648f9SCameron Grant int 224b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 225285648f9SCameron Grant { 226b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 227285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 228b8f0d9e0SCameron Grant c->pid = -1; 22949c5e6e2SCameron Grant CHN_UNLOCK(c); 230285648f9SCameron Grant return 0; 231285648f9SCameron Grant } 232285648f9SCameron Grant 233285648f9SCameron Grant int 234285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 235285648f9SCameron Grant { 23649c5e6e2SCameron Grant int r; 23749c5e6e2SCameron Grant 238b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 239285648f9SCameron Grant c->refcount += ref; 24049c5e6e2SCameron Grant r = c->refcount; 24149c5e6e2SCameron Grant return r; 242285648f9SCameron Grant } 243285648f9SCameron Grant 24467b1dce3SCameron Grant int 24567b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta) 24667b1dce3SCameron Grant { 24767b1dce3SCameron Grant d->inprog += delta; 24867b1dce3SCameron Grant return d->inprog; 24967b1dce3SCameron Grant } 25067b1dce3SCameron Grant 25167b1dce3SCameron Grant static void 25267b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 25367b1dce3SCameron Grant { 25467b1dce3SCameron Grant struct pcm_channel *c; 25567b1dce3SCameron Grant struct snddev_channel *sce; 25667b1dce3SCameron Grant int err, done; 25767b1dce3SCameron Grant 25867b1dce3SCameron Grant if (num > 0 && d->vchancount == 0) { 25967b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 26067b1dce3SCameron Grant c = sce->channel; 26167b1dce3SCameron Grant if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) { 26267b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 26367b1dce3SCameron Grant err = vchan_create(c); 26467b1dce3SCameron Grant if (err) { 26567b1dce3SCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 26667b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 26767b1dce3SCameron Grant } 26867b1dce3SCameron Grant return; 26967b1dce3SCameron Grant } 27067b1dce3SCameron Grant } 27167b1dce3SCameron Grant } 27267b1dce3SCameron Grant if (num == 0 && d->vchancount > 0) { 27367b1dce3SCameron Grant done = 0; 27467b1dce3SCameron Grant while (!done) { 27567b1dce3SCameron Grant done = 1; 27667b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 27767b1dce3SCameron Grant c = sce->channel; 27867b1dce3SCameron Grant if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) { 27967b1dce3SCameron Grant done = 0; 28067b1dce3SCameron Grant err = vchan_destroy(c); 28167b1dce3SCameron Grant if (err) 28267b1dce3SCameron Grant device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err); 2839cfd8eb3SPeter Wemm break; /* restart */ 28467b1dce3SCameron Grant } 28567b1dce3SCameron Grant } 28667b1dce3SCameron Grant } 28767b1dce3SCameron Grant } 28867b1dce3SCameron Grant } 28967b1dce3SCameron Grant 29082db23e2SCameron Grant #ifdef USING_DEVFS 29133dbf14aSCameron Grant static int 292cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 29333dbf14aSCameron Grant { 294b8f0d9e0SCameron Grant struct snddev_info *d; 29533dbf14aSCameron Grant int error, unit; 29633dbf14aSCameron Grant 29733dbf14aSCameron Grant unit = snd_unit; 29833dbf14aSCameron Grant error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 29933dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 30074ffd138SCameron Grant if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 301b8f0d9e0SCameron Grant return EINVAL; 302b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 303faeebea2SCameron Grant if (d == NULL || SLIST_EMPTY(&d->channels)) 304b8f0d9e0SCameron Grant return EINVAL; 30533dbf14aSCameron Grant snd_unit = unit; 30633dbf14aSCameron Grant } 30733dbf14aSCameron Grant return (error); 30833dbf14aSCameron Grant } 309b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 310cd9766c5SCameron Grant 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 31182db23e2SCameron Grant #endif 312987e5972SCameron Grant 313cd9766c5SCameron Grant static int 31467b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 315cd9766c5SCameron Grant { 31667b1dce3SCameron Grant struct snddev_info *d; 31767b1dce3SCameron Grant int i, v, error; 318cd9766c5SCameron Grant 31967b1dce3SCameron Grant v = snd_maxautovchans; 320cd9766c5SCameron Grant error = sysctl_handle_int(oidp, &v, sizeof(v), req); 321cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 322cd9766c5SCameron Grant if (v < 0 || v >= SND_MAXVCHANS) 323cd9766c5SCameron Grant return EINVAL; 32467b1dce3SCameron Grant if (v != snd_maxautovchans) { 32567b1dce3SCameron Grant for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 32667b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 32767b1dce3SCameron Grant if (!d) 32867b1dce3SCameron Grant continue; 32967b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 33067b1dce3SCameron Grant } 33167b1dce3SCameron Grant } 33267b1dce3SCameron Grant snd_maxautovchans = v; 333cd9766c5SCameron Grant } 334cd9766c5SCameron Grant return (error); 335cd9766c5SCameron Grant } 33667b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 33767b1dce3SCameron Grant 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 338f637a36cSCameron Grant 339285648f9SCameron Grant struct pcm_channel * 340285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 341987e5972SCameron Grant { 342285648f9SCameron Grant struct pcm_channel *ch; 34333dbf14aSCameron Grant char *dirs; 344a67fe5c1SCameron Grant int err, *pnum; 345987e5972SCameron Grant 346285648f9SCameron Grant switch(dir) { 347285648f9SCameron Grant case PCMDIR_PLAY: 348285648f9SCameron Grant dirs = "play"; 349a67fe5c1SCameron Grant pnum = &d->playcount; 350285648f9SCameron Grant break; 351a67fe5c1SCameron Grant 352285648f9SCameron Grant case PCMDIR_REC: 353285648f9SCameron Grant dirs = "record"; 354a67fe5c1SCameron Grant pnum = &d->reccount; 355285648f9SCameron Grant break; 356a67fe5c1SCameron Grant 357285648f9SCameron Grant case PCMDIR_VIRTUAL: 358285648f9SCameron Grant dirs = "virtual"; 359285648f9SCameron Grant dir = PCMDIR_PLAY; 360a67fe5c1SCameron Grant pnum = &d->vchancount; 361285648f9SCameron Grant break; 362a67fe5c1SCameron Grant 363285648f9SCameron Grant default: 364285648f9SCameron Grant return NULL; 3659c326820SCameron Grant } 366285648f9SCameron Grant 367285648f9SCameron Grant ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 368285648f9SCameron Grant if (!ch) 369285648f9SCameron Grant return NULL; 370285648f9SCameron Grant 3710f55ac6cSCameron Grant ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 372285648f9SCameron Grant if (!ch->methods) { 373285648f9SCameron Grant free(ch, M_DEVBUF); 374a67fe5c1SCameron Grant 375285648f9SCameron Grant return NULL; 376285648f9SCameron Grant } 377285648f9SCameron Grant 378a67fe5c1SCameron Grant ch->num = (*pnum)++; 379a67fe5c1SCameron Grant 380285648f9SCameron Grant ch->pid = -1; 381285648f9SCameron Grant ch->parentsnddev = d; 382285648f9SCameron Grant ch->parentchannel = parent; 383436c9b65SScott Long ch->dev = d->dev; 384a67fe5c1SCameron Grant snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num); 385285648f9SCameron Grant 3860f55ac6cSCameron Grant err = chn_init(ch, devinfo, dir); 3870f55ac6cSCameron Grant if (err) { 388a67fe5c1SCameron Grant device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 389285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 390285648f9SCameron Grant free(ch, M_DEVBUF); 391a67fe5c1SCameron Grant (*pnum)--; 392a67fe5c1SCameron Grant 393285648f9SCameron Grant return NULL; 394bbb5bf3dSCameron Grant } 395285648f9SCameron Grant 396285648f9SCameron Grant return ch; 397285648f9SCameron Grant } 398285648f9SCameron Grant 399285648f9SCameron Grant int 400285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 401285648f9SCameron Grant { 402a67fe5c1SCameron Grant struct snddev_info *d; 403285648f9SCameron Grant int err; 404285648f9SCameron Grant 405a67fe5c1SCameron Grant d = ch->parentsnddev; 406285648f9SCameron Grant err = chn_kill(ch); 407285648f9SCameron Grant if (err) { 408a67fe5c1SCameron Grant device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 409285648f9SCameron Grant return err; 410285648f9SCameron Grant } 411285648f9SCameron Grant 412a67fe5c1SCameron Grant if (ch->direction == PCMDIR_REC) 413a67fe5c1SCameron Grant d->reccount--; 414a67fe5c1SCameron Grant else if (ch->flags & CHN_F_VIRTUAL) 415a67fe5c1SCameron Grant d->vchancount--; 416a67fe5c1SCameron Grant else 417a67fe5c1SCameron Grant d->playcount--; 418a67fe5c1SCameron Grant 419285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 420285648f9SCameron Grant free(ch, M_DEVBUF); 421285648f9SCameron Grant 422285648f9SCameron Grant return 0; 423285648f9SCameron Grant } 424285648f9SCameron Grant 425285648f9SCameron Grant int 426f637a36cSCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 427285648f9SCameron Grant { 42867b1dce3SCameron Grant struct snddev_channel *sce, *tmp, *after; 429285648f9SCameron Grant int unit = device_get_unit(d->dev); 430b8f0d9e0SCameron Grant 431b8f0d9e0SCameron Grant snd_mtxlock(d->lock); 432285648f9SCameron Grant 433285648f9SCameron Grant sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 434285648f9SCameron Grant if (!sce) { 435b8f0d9e0SCameron Grant snd_mtxunlock(d->lock); 436285648f9SCameron Grant return ENOMEM; 437285648f9SCameron Grant } 438285648f9SCameron Grant 439285648f9SCameron Grant sce->channel = ch; 44067b1dce3SCameron Grant if (SLIST_EMPTY(&d->channels)) { 441285648f9SCameron Grant SLIST_INSERT_HEAD(&d->channels, sce, link); 44267b1dce3SCameron Grant } else { 44367b1dce3SCameron Grant after = NULL; 44467b1dce3SCameron Grant SLIST_FOREACH(tmp, &d->channels, link) { 44567b1dce3SCameron Grant after = tmp; 44667b1dce3SCameron Grant } 44767b1dce3SCameron Grant SLIST_INSERT_AFTER(after, sce, link); 44867b1dce3SCameron Grant } 449285648f9SCameron Grant 450506a5308SCameron Grant if (mkdev) { 451f637a36cSCameron Grant dsp_register(unit, d->devcount++); 452506a5308SCameron Grant if (ch->direction == PCMDIR_REC) 453506a5308SCameron Grant dsp_registerrec(unit, ch->num); 454506a5308SCameron Grant } 455b8f0d9e0SCameron Grant 45649c5e6e2SCameron Grant snd_mtxunlock(d->lock); 457285648f9SCameron Grant 45833dbf14aSCameron Grant return 0; 45933dbf14aSCameron Grant } 46033dbf14aSCameron Grant 461285648f9SCameron Grant int 462f637a36cSCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 46333dbf14aSCameron Grant { 464285648f9SCameron Grant struct snddev_channel *sce; 465285648f9SCameron Grant int unit = device_get_unit(d->dev); 46633dbf14aSCameron Grant 46749c5e6e2SCameron Grant snd_mtxlock(d->lock); 468285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 469285648f9SCameron Grant if (sce->channel == ch) 470285648f9SCameron Grant goto gotit; 47133dbf14aSCameron Grant } 47249c5e6e2SCameron Grant snd_mtxunlock(d->lock); 473285648f9SCameron Grant return EINVAL; 474285648f9SCameron Grant gotit: 475285648f9SCameron Grant SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 476285648f9SCameron Grant free(sce, M_DEVBUF); 477285648f9SCameron Grant 478506a5308SCameron Grant if (rmdev) { 479f637a36cSCameron Grant dsp_unregister(unit, --d->devcount); 480506a5308SCameron Grant if (ch->direction == PCMDIR_REC) 481a67fe5c1SCameron Grant dsp_unregisterrec(unit, ch->num); 482506a5308SCameron Grant } 48349c5e6e2SCameron Grant snd_mtxunlock(d->lock); 484285648f9SCameron Grant 485987e5972SCameron Grant return 0; 486987e5972SCameron Grant } 487987e5972SCameron Grant 488987e5972SCameron Grant int 489285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 490285648f9SCameron Grant { 491285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 49267b1dce3SCameron Grant struct pcm_channel *ch; 49367b1dce3SCameron Grant int err; 494285648f9SCameron Grant 495285648f9SCameron Grant ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 496285648f9SCameron Grant if (!ch) { 497285648f9SCameron Grant device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 498285648f9SCameron Grant return ENODEV; 499285648f9SCameron Grant } 500cd9766c5SCameron Grant 501f637a36cSCameron Grant err = pcm_chn_add(d, ch, 1); 502285648f9SCameron Grant if (err) { 503285648f9SCameron Grant device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 504285648f9SCameron Grant pcm_chn_destroy(ch); 505cd9766c5SCameron Grant return err; 506cd9766c5SCameron Grant } 507cd9766c5SCameron Grant 50867b1dce3SCameron Grant if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) { 509cd9766c5SCameron Grant ch->flags |= CHN_F_BUSY; 510cd9766c5SCameron Grant err = vchan_create(ch); 511cd9766c5SCameron Grant if (err) { 51267b1dce3SCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 51367b1dce3SCameron Grant ch->flags &= ~CHN_F_BUSY; 514cd9766c5SCameron Grant } 515285648f9SCameron Grant } 516285648f9SCameron Grant 517285648f9SCameron Grant return err; 518285648f9SCameron Grant } 519285648f9SCameron Grant 520285648f9SCameron Grant static int 521285648f9SCameron Grant pcm_killchan(device_t dev) 522285648f9SCameron Grant { 523285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 524285648f9SCameron Grant struct snddev_channel *sce; 525285648f9SCameron Grant 52649c5e6e2SCameron Grant snd_mtxlock(d->lock); 527285648f9SCameron Grant sce = SLIST_FIRST(&d->channels); 52849c5e6e2SCameron Grant snd_mtxunlock(d->lock); 529285648f9SCameron Grant 530f637a36cSCameron Grant return pcm_chn_remove(d, sce->channel, 1); 531285648f9SCameron Grant } 532285648f9SCameron Grant 533285648f9SCameron Grant int 534987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 535987e5972SCameron Grant { 53666ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 53749c5e6e2SCameron Grant 53849c5e6e2SCameron Grant snd_mtxlock(d->lock); 539987e5972SCameron Grant strncpy(d->status, str, SND_STATUSLEN); 54049c5e6e2SCameron Grant snd_mtxunlock(d->lock); 541987e5972SCameron Grant return 0; 542987e5972SCameron Grant } 543987e5972SCameron Grant 544987e5972SCameron Grant u_int32_t 545987e5972SCameron Grant pcm_getflags(device_t dev) 546987e5972SCameron Grant { 54766ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 54849c5e6e2SCameron Grant 549987e5972SCameron Grant return d->flags; 550987e5972SCameron Grant } 551987e5972SCameron Grant 552987e5972SCameron Grant void 553987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val) 554987e5972SCameron Grant { 55566ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 556d95502a8SCameron Grant 557987e5972SCameron Grant d->flags = val; 558987e5972SCameron Grant } 559987e5972SCameron Grant 56039004e69SCameron Grant void * 56139004e69SCameron Grant pcm_getdevinfo(device_t dev) 56239004e69SCameron Grant { 56366ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 56449c5e6e2SCameron Grant 56539004e69SCameron Grant return d->devinfo; 56639004e69SCameron Grant } 56739004e69SCameron Grant 568a67fe5c1SCameron Grant unsigned int 569a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 570a67fe5c1SCameron Grant { 571a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 5724e60be34SCameron Grant int sz, x; 573a67fe5c1SCameron Grant 574a67fe5c1SCameron Grant sz = 0; 5754e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 5764e60be34SCameron Grant x = sz; 577a67fe5c1SCameron Grant RANGE(sz, min, max); 5784e60be34SCameron Grant if (x != sz) 5794e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 5804e60be34SCameron Grant x = min; 5814e60be34SCameron Grant while (x < sz) 5824e60be34SCameron Grant x <<= 1; 5834e60be34SCameron Grant if (x > sz) 5844e60be34SCameron Grant x >>= 1; 5854e60be34SCameron Grant if (x != sz) { 5864e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 5874e60be34SCameron Grant sz = x; 5884e60be34SCameron Grant } 5894e60be34SCameron Grant } else { 590a67fe5c1SCameron Grant sz = deflt; 5914e60be34SCameron Grant } 5924e60be34SCameron Grant 593a67fe5c1SCameron Grant d->bufsz = sz; 594a67fe5c1SCameron Grant 595a67fe5c1SCameron Grant return sz; 596a67fe5c1SCameron Grant } 597a67fe5c1SCameron Grant 598987e5972SCameron Grant int 599987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 600987e5972SCameron Grant { 60166ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 602987e5972SCameron Grant 603b8a36395SCameron Grant if (pcm_veto_load) { 604b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 605b8a36395SCameron Grant 606b8a36395SCameron Grant return EINVAL; 607b8a36395SCameron Grant } 608b8a36395SCameron Grant 6092c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 61049c5e6e2SCameron Grant snd_mtxlock(d->lock); 611285648f9SCameron Grant 612cd9766c5SCameron Grant d->flags = 0; 613e4d5b250SCameron Grant d->dev = dev; 614987e5972SCameron Grant d->devinfo = devinfo; 615f637a36cSCameron Grant d->devcount = 0; 616506a5308SCameron Grant d->reccount = 0; 617a67fe5c1SCameron Grant d->playcount = 0; 618f637a36cSCameron Grant d->vchancount = 0; 619d95502a8SCameron Grant d->inprog = 0; 620833f7023SCameron Grant 621d95502a8SCameron Grant if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 622285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 623d95502a8SCameron Grant 624285648f9SCameron Grant d->fakechan = fkchan_setup(dev); 625285648f9SCameron Grant chn_init(d->fakechan, NULL, 0); 626987e5972SCameron Grant 62782db23e2SCameron Grant #ifdef SND_DYNSYSCTL 628cc486d80SJohn Baldwin sysctl_ctx_init(&d->sysctl_tree); 629cc486d80SJohn Baldwin d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 630a3e893e0SJohn Baldwin SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 631cc486d80SJohn Baldwin device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 632a3e893e0SJohn Baldwin if (d->sysctl_tree_top == NULL) { 633cc486d80SJohn Baldwin sysctl_ctx_free(&d->sysctl_tree); 634cc486d80SJohn Baldwin goto no; 635cc486d80SJohn Baldwin } 636a67fe5c1SCameron Grant SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 637a67fe5c1SCameron Grant OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 63882db23e2SCameron Grant #endif 639b8f0d9e0SCameron Grant if (numplay > 0) 64067b1dce3SCameron Grant vchan_initsys(dev); 641cd9766c5SCameron Grant if (numplay == 1) 642cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 643cd9766c5SCameron Grant 64449c5e6e2SCameron Grant snd_mtxunlock(d->lock); 64567b1dce3SCameron Grant sndstat_register(dev, d->status, sndstat_prepare_pcm); 646987e5972SCameron Grant return 0; 647987e5972SCameron Grant no: 64849c5e6e2SCameron Grant snd_mtxfree(d->lock); 649987e5972SCameron Grant return ENXIO; 650987e5972SCameron Grant } 651987e5972SCameron Grant 65233dbf14aSCameron Grant int 65333dbf14aSCameron Grant pcm_unregister(device_t dev) 6547c438dbeSCameron Grant { 65566ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 656285648f9SCameron Grant struct snddev_channel *sce; 657a67fe5c1SCameron Grant struct pcm_channel *ch; 6587c438dbeSCameron Grant 65949c5e6e2SCameron Grant snd_mtxlock(d->lock); 660d95502a8SCameron Grant if (d->inprog) { 6615c25132aSGeorge C A Reid device_printf(dev, "unregister: operation in progress\n"); 6625c25132aSGeorge C A Reid snd_mtxunlock(d->lock); 6635c25132aSGeorge C A Reid return EBUSY; 6645c25132aSGeorge C A Reid } 6655c25132aSGeorge C A Reid if (sndstat_busy() != 0) { 6665c25132aSGeorge C A Reid device_printf(dev, "unregister: sndstat busy\n"); 667d95502a8SCameron Grant snd_mtxunlock(d->lock); 668d95502a8SCameron Grant return EBUSY; 669d95502a8SCameron Grant } 670285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 671a67fe5c1SCameron Grant ch = sce->channel; 672a67fe5c1SCameron Grant if (ch->refcount > 0) { 67321ed9908SCameron Grant device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 67449c5e6e2SCameron Grant snd_mtxunlock(d->lock); 675285648f9SCameron Grant return EBUSY; 676285648f9SCameron Grant } 677c9b53085SCameron Grant } 678d95502a8SCameron Grant if (mixer_uninit(dev)) { 6795c25132aSGeorge C A Reid device_printf(dev, "unregister: mixer busy\n"); 68049c5e6e2SCameron Grant snd_mtxunlock(d->lock); 681c9b53085SCameron Grant return EBUSY; 682c9b53085SCameron Grant } 6837c438dbeSCameron Grant 68466ef8af5SCameron Grant #ifdef SND_DYNSYSCTL 68566ef8af5SCameron Grant d->sysctl_tree_top = NULL; 68666ef8af5SCameron Grant sysctl_ctx_free(&d->sysctl_tree); 68766ef8af5SCameron Grant #endif 688faeebea2SCameron Grant while (!SLIST_EMPTY(&d->channels)) 689285648f9SCameron Grant pcm_killchan(dev); 6907c438dbeSCameron Grant 69166ef8af5SCameron Grant chn_kill(d->fakechan); 69266ef8af5SCameron Grant fkchan_kill(d->fakechan); 69382db23e2SCameron Grant 69449c5e6e2SCameron Grant snd_mtxfree(d->lock); 69533dbf14aSCameron Grant return 0; 69633dbf14aSCameron Grant } 6977c438dbeSCameron Grant 69867b1dce3SCameron Grant /************************************************************************/ 69967b1dce3SCameron Grant 70067b1dce3SCameron Grant static int 70167b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 70267b1dce3SCameron Grant { 70367b1dce3SCameron Grant struct snddev_info *d; 70467b1dce3SCameron Grant struct snddev_channel *sce; 70567b1dce3SCameron Grant struct pcm_channel *c; 70667b1dce3SCameron Grant struct pcm_feeder *f; 70767b1dce3SCameron Grant int pc, rc, vc; 70867b1dce3SCameron Grant 70967b1dce3SCameron Grant if (verbose < 1) 71067b1dce3SCameron Grant return 0; 71167b1dce3SCameron Grant 71267b1dce3SCameron Grant d = device_get_softc(dev); 71367b1dce3SCameron Grant if (!d) 71467b1dce3SCameron Grant return ENXIO; 71567b1dce3SCameron Grant 71667b1dce3SCameron Grant snd_mtxlock(d->lock); 71767b1dce3SCameron Grant if (!SLIST_EMPTY(&d->channels)) { 71867b1dce3SCameron Grant pc = rc = vc = 0; 71967b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 72067b1dce3SCameron Grant c = sce->channel; 72167b1dce3SCameron Grant if (c->direction == PCMDIR_PLAY) { 72267b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 72367b1dce3SCameron Grant vc++; 72467b1dce3SCameron Grant else 72567b1dce3SCameron Grant pc++; 72667b1dce3SCameron Grant } else 72767b1dce3SCameron Grant rc++; 72867b1dce3SCameron Grant } 729a67fe5c1SCameron Grant sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 73067b1dce3SCameron Grant (d->flags & SD_F_SIMPLEX)? "" : " duplex", 73167b1dce3SCameron Grant #ifdef USING_DEVFS 73267b1dce3SCameron Grant (device_get_unit(dev) == snd_unit)? " default" : "" 73367b1dce3SCameron Grant #else 73467b1dce3SCameron Grant "" 73567b1dce3SCameron Grant #endif 73667b1dce3SCameron Grant ); 73767b1dce3SCameron Grant if (verbose <= 1) 73867b1dce3SCameron Grant goto skipverbose; 73967b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 74067b1dce3SCameron Grant c = sce->channel; 741a3285889SCameron Grant sbuf_printf(s, "\n\t"); 742a3285889SCameron Grant 743a3285889SCameron Grant sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 7444c68642aSCameron Grant sbuf_printf(s, "spd %d", c->speed); 7454c68642aSCameron Grant if (c->speed != sndbuf_getspd(c->bufhard)) 7464c68642aSCameron Grant sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 7474c68642aSCameron Grant sbuf_printf(s, ", fmt 0x%08x", c->format); 7484c68642aSCameron Grant if (c->format != sndbuf_getfmt(c->bufhard)) 7494c68642aSCameron Grant sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 7504c68642aSCameron Grant sbuf_printf(s, ", flags %08x", c->flags); 75167b1dce3SCameron Grant if (c->pid != -1) 75267b1dce3SCameron Grant sbuf_printf(s, ", pid %d", c->pid); 75367b1dce3SCameron Grant sbuf_printf(s, "\n\t"); 7544c68642aSCameron Grant 755edecdda7SCameron Grant if (c->bufhard != NULL && c->bufsoft != NULL) { 756a3285889SCameron Grant sbuf_printf(s, "interrupts %d, ", c->interrupts); 757a3285889SCameron Grant if (c->direction == PCMDIR_REC) 758a3285889SCameron Grant sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 759a3285889SCameron Grant c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 760a3285889SCameron Grant else 761edecdda7SCameron Grant sbuf_printf(s, "underruns %d, ready %d", 762edecdda7SCameron Grant c->xruns, sndbuf_getready(c->bufsoft)); 763a3285889SCameron Grant sbuf_printf(s, "\n\t"); 764a3285889SCameron Grant } 7654c68642aSCameron Grant 7664c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 7674c68642aSCameron Grant sbuf_printf(s, " -> "); 76867b1dce3SCameron Grant f = c->feeder; 7694c68642aSCameron Grant while (f->source != NULL) 7704c68642aSCameron Grant f = f->source; 7714c68642aSCameron Grant while (f != NULL) { 77267b1dce3SCameron Grant sbuf_printf(s, "%s", f->class->name); 77367b1dce3SCameron Grant if (f->desc->type == FEEDER_FMT) 7744c68642aSCameron Grant sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 77567b1dce3SCameron Grant if (f->desc->type == FEEDER_RATE) 7764c68642aSCameron Grant sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 77767b1dce3SCameron Grant if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 7784c68642aSCameron Grant sbuf_printf(s, "(0x%08x)", f->desc->out); 7794c68642aSCameron Grant sbuf_printf(s, " -> "); 7804c68642aSCameron Grant f = f->parent; 78167b1dce3SCameron Grant } 7824c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 78367b1dce3SCameron Grant } 78467b1dce3SCameron Grant } else 78567b1dce3SCameron Grant sbuf_printf(s, " (mixer only)"); 7869cfd8eb3SPeter Wemm skipverbose: 78767b1dce3SCameron Grant snd_mtxunlock(d->lock); 78867b1dce3SCameron Grant 78967b1dce3SCameron Grant return 0; 79067b1dce3SCameron Grant } 79167b1dce3SCameron Grant 79267b1dce3SCameron Grant /************************************************************************/ 79367b1dce3SCameron Grant 79467b1dce3SCameron Grant #ifdef SND_DYNSYSCTL 79567b1dce3SCameron Grant int 79667b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 79767b1dce3SCameron Grant { 79867b1dce3SCameron Grant struct snddev_info *d; 79967b1dce3SCameron Grant struct snddev_channel *sce; 80067b1dce3SCameron Grant struct pcm_channel *c; 80167b1dce3SCameron Grant int err, oldcnt, newcnt, cnt; 80267b1dce3SCameron Grant 80367b1dce3SCameron Grant d = oidp->oid_arg1; 80467b1dce3SCameron Grant 80567b1dce3SCameron Grant pcm_lock(d); 80667b1dce3SCameron Grant cnt = 0; 80767b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 80867b1dce3SCameron Grant c = sce->channel; 80967b1dce3SCameron Grant if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) 81067b1dce3SCameron Grant cnt++; 81167b1dce3SCameron Grant } 81267b1dce3SCameron Grant oldcnt = cnt; 81367b1dce3SCameron Grant newcnt = cnt; 81467b1dce3SCameron Grant 81567b1dce3SCameron Grant err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 81667b1dce3SCameron Grant if (err == 0 && req->newptr != NULL) { 81767b1dce3SCameron Grant if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 81867b1dce3SCameron Grant pcm_unlock(d); 81967b1dce3SCameron Grant return EINVAL; 82067b1dce3SCameron Grant } 82167b1dce3SCameron Grant 82267b1dce3SCameron Grant if (newcnt > cnt) { 82367b1dce3SCameron Grant /* add new vchans - find a parent channel first */ 82467b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 82567b1dce3SCameron Grant c = sce->channel; 82667b1dce3SCameron Grant /* not a candidate if not a play channel */ 82767b1dce3SCameron Grant if (c->direction != PCMDIR_PLAY) 8289cfd8eb3SPeter Wemm continue; 82967b1dce3SCameron Grant /* not a candidate if a virtual channel */ 83067b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 8319cfd8eb3SPeter Wemm continue; 83267b1dce3SCameron Grant /* not a candidate if it's in use */ 83367b1dce3SCameron Grant if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 8349cfd8eb3SPeter Wemm continue; 83567b1dce3SCameron Grant /* 83667b1dce3SCameron Grant * if we get here we're a nonvirtual play channel, and either 83767b1dce3SCameron Grant * 1) not busy 83867b1dce3SCameron Grant * 2) busy with children, not directly open 83967b1dce3SCameron Grant * 84067b1dce3SCameron Grant * thus we can add children 84167b1dce3SCameron Grant */ 84267b1dce3SCameron Grant goto addok; 84367b1dce3SCameron Grant } 84467b1dce3SCameron Grant pcm_unlock(d); 84567b1dce3SCameron Grant return EBUSY; 84667b1dce3SCameron Grant addok: 84767b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 84867b1dce3SCameron Grant while (err == 0 && newcnt > cnt) { 84967b1dce3SCameron Grant err = vchan_create(c); 85067b1dce3SCameron Grant if (err == 0) 85167b1dce3SCameron Grant cnt++; 85267b1dce3SCameron Grant } 85367b1dce3SCameron Grant if (SLIST_EMPTY(&c->children)) 85467b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 85567b1dce3SCameron Grant } else if (newcnt < cnt) { 85667b1dce3SCameron Grant while (err == 0 && newcnt < cnt) { 85767b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 85867b1dce3SCameron Grant c = sce->channel; 85967b1dce3SCameron Grant if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 86067b1dce3SCameron Grant goto remok; 86167b1dce3SCameron Grant } 86267b1dce3SCameron Grant pcm_unlock(d); 86367b1dce3SCameron Grant return EINVAL; 86467b1dce3SCameron Grant remok: 86567b1dce3SCameron Grant err = vchan_destroy(c); 86667b1dce3SCameron Grant if (err == 0) 86767b1dce3SCameron Grant cnt--; 86867b1dce3SCameron Grant } 86967b1dce3SCameron Grant } 87067b1dce3SCameron Grant } 87167b1dce3SCameron Grant 87267b1dce3SCameron Grant pcm_unlock(d); 87367b1dce3SCameron Grant return err; 87467b1dce3SCameron Grant } 87567b1dce3SCameron Grant #endif 87667b1dce3SCameron Grant 87767b1dce3SCameron Grant /************************************************************************/ 87867b1dce3SCameron Grant 87933dbf14aSCameron Grant static moduledata_t sndpcm_mod = { 88033dbf14aSCameron Grant "snd_pcm", 881d95502a8SCameron Grant NULL, 88233dbf14aSCameron Grant NULL 88333dbf14aSCameron Grant }; 88433dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 88533dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER); 886