1987e5972SCameron Grant /* 23f225978SCameron Grant * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 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 36a527dbc7SCameron Grant #ifndef PCM_DEBUG_MTX 3767b1dce3SCameron Grant struct snddev_channel { 3867b1dce3SCameron Grant SLIST_ENTRY(snddev_channel) link; 3967b1dce3SCameron Grant struct pcm_channel *channel; 4067b1dce3SCameron Grant }; 4167b1dce3SCameron Grant 4267b1dce3SCameron Grant struct snddev_info { 4367b1dce3SCameron Grant SLIST_HEAD(, snddev_channel) channels; 4467b1dce3SCameron Grant struct pcm_channel *fakechan; 45a67fe5c1SCameron Grant unsigned devcount, playcount, reccount, vchancount; 4667b1dce3SCameron Grant unsigned flags; 4767b1dce3SCameron Grant int inprog; 48a67fe5c1SCameron Grant unsigned int bufsz; 4967b1dce3SCameron Grant void *devinfo; 5067b1dce3SCameron Grant device_t dev; 5167b1dce3SCameron Grant char status[SND_STATUSLEN]; 5267b1dce3SCameron Grant struct sysctl_ctx_list sysctl_tree; 5367b1dce3SCameron Grant struct sysctl_oid *sysctl_tree_top; 5400acb133SCameron Grant struct mtx *lock; 5567b1dce3SCameron Grant }; 56a527dbc7SCameron Grant #endif 5767b1dce3SCameron Grant 58d95502a8SCameron Grant devclass_t pcm_devclass; 5982db23e2SCameron Grant 60b8a36395SCameron Grant int pcm_veto_load = 1; 61b8a36395SCameron Grant 6282db23e2SCameron Grant #ifdef USING_DEVFS 63d95502a8SCameron Grant int snd_unit = 0; 6409786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit); 6582db23e2SCameron Grant #endif 66cbe7d6a3SCameron Grant 6767b1dce3SCameron Grant int snd_maxautovchans = 0; 6867b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 69987e5972SCameron Grant 7082db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 7182db23e2SCameron Grant 7267b1dce3SCameron Grant static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 7367b1dce3SCameron Grant 7467b1dce3SCameron Grant struct sysctl_ctx_list * 7567b1dce3SCameron Grant snd_sysctl_tree(device_t dev) 7667b1dce3SCameron Grant { 7767b1dce3SCameron Grant struct snddev_info *d = device_get_softc(dev); 7867b1dce3SCameron Grant 7967b1dce3SCameron Grant return &d->sysctl_tree; 8067b1dce3SCameron Grant } 8167b1dce3SCameron Grant 8267b1dce3SCameron Grant struct sysctl_oid * 8367b1dce3SCameron Grant snd_sysctl_tree_top(device_t dev) 8467b1dce3SCameron Grant { 8567b1dce3SCameron Grant struct snddev_info *d = device_get_softc(dev); 8667b1dce3SCameron Grant 8767b1dce3SCameron Grant return d->sysctl_tree_top; 8867b1dce3SCameron Grant } 8967b1dce3SCameron Grant 9037209180SCameron Grant void * 912c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type) 9237209180SCameron Grant { 9337209180SCameron Grant #ifdef USING_MUTEX 9437209180SCameron Grant struct mtx *m; 9537209180SCameron Grant 96a163d034SWarner Losh m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 9737209180SCameron Grant if (m == NULL) 9837209180SCameron Grant return NULL; 992c69ba87SJohn Baldwin mtx_init(m, desc, type, MTX_RECURSE); 10037209180SCameron Grant return m; 10137209180SCameron Grant #else 102a983d575SCameron Grant return (void *)0xcafebabe; 10337209180SCameron Grant #endif 10437209180SCameron Grant } 10537209180SCameron Grant 10637209180SCameron Grant void 10737209180SCameron Grant snd_mtxfree(void *m) 10837209180SCameron Grant { 10937209180SCameron Grant #ifdef USING_MUTEX 11037209180SCameron Grant struct mtx *mtx = m; 11137209180SCameron Grant 11267beb5a5SCameron Grant /* mtx_assert(mtx, MA_OWNED); */ 11337209180SCameron Grant mtx_destroy(mtx); 11437209180SCameron Grant free(mtx, M_DEVBUF); 11537209180SCameron Grant #endif 11637209180SCameron Grant } 11737209180SCameron Grant 11837209180SCameron Grant void 11937209180SCameron Grant snd_mtxassert(void *m) 12037209180SCameron Grant { 12137209180SCameron Grant #ifdef USING_MUTEX 122f00f162aSCameron Grant #ifdef INVARIANTS 12337209180SCameron Grant struct mtx *mtx = m; 12437209180SCameron Grant 12537209180SCameron Grant mtx_assert(mtx, MA_OWNED); 12637209180SCameron Grant #endif 127f00f162aSCameron Grant #endif 12837209180SCameron Grant } 12967beb5a5SCameron Grant /* 13037209180SCameron Grant void 13137209180SCameron Grant snd_mtxlock(void *m) 13237209180SCameron Grant { 13337209180SCameron Grant #ifdef USING_MUTEX 13437209180SCameron Grant struct mtx *mtx = m; 13537209180SCameron Grant 13637209180SCameron Grant mtx_lock(mtx); 13737209180SCameron Grant #endif 13837209180SCameron Grant } 13937209180SCameron Grant 14037209180SCameron Grant void 14137209180SCameron Grant snd_mtxunlock(void *m) 14237209180SCameron Grant { 14337209180SCameron Grant #ifdef USING_MUTEX 14437209180SCameron Grant struct mtx *mtx = m; 14537209180SCameron Grant 14637209180SCameron Grant mtx_unlock(mtx); 14737209180SCameron Grant #endif 14837209180SCameron Grant } 14967beb5a5SCameron Grant */ 15037209180SCameron Grant int 15137209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 15237209180SCameron Grant { 15337209180SCameron Grant #ifdef USING_MUTEX 15437209180SCameron Grant flags &= INTR_MPSAFE; 15546700f12SPeter Wemm flags |= INTR_TYPE_AV; 15637209180SCameron Grant #else 15746700f12SPeter Wemm flags = INTR_TYPE_AV; 15837209180SCameron Grant #endif 15937209180SCameron Grant return bus_setup_intr(dev, res, flags, hand, param, cookiep); 16037209180SCameron Grant } 16137209180SCameron Grant 162a527dbc7SCameron Grant #ifndef PCM_DEBUG_MTX 16367b1dce3SCameron Grant void 16467b1dce3SCameron Grant pcm_lock(struct snddev_info *d) 16567b1dce3SCameron Grant { 16667b1dce3SCameron Grant snd_mtxlock(d->lock); 16767b1dce3SCameron Grant } 16867b1dce3SCameron Grant 16967b1dce3SCameron Grant void 17067b1dce3SCameron Grant pcm_unlock(struct snddev_info *d) 17167b1dce3SCameron Grant { 17267b1dce3SCameron Grant snd_mtxunlock(d->lock); 17367b1dce3SCameron Grant } 174a527dbc7SCameron Grant #endif 17567b1dce3SCameron Grant 17667b1dce3SCameron Grant struct pcm_channel * 17767b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d) 17867b1dce3SCameron Grant { 17967b1dce3SCameron Grant return d->fakechan; 18067b1dce3SCameron Grant } 18167b1dce3SCameron Grant 182b8f0d9e0SCameron Grant /* return a locked channel */ 183285648f9SCameron Grant struct pcm_channel * 184506a5308SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum) 185285648f9SCameron Grant { 186285648f9SCameron Grant struct pcm_channel *c; 187285648f9SCameron Grant struct snddev_channel *sce; 188f637a36cSCameron Grant int err; 189285648f9SCameron Grant 190b8f0d9e0SCameron Grant snd_mtxassert(d->lock); 191f637a36cSCameron Grant 192f637a36cSCameron Grant /* scan for a free channel */ 193285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 194285648f9SCameron Grant c = sce->channel; 19549c5e6e2SCameron Grant CHN_LOCK(c); 196285648f9SCameron Grant if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 197506a5308SCameron Grant if (chnum == -1 || c->num == chnum) { 198285648f9SCameron Grant c->flags |= CHN_F_BUSY; 199b8f0d9e0SCameron Grant c->pid = pid; 200285648f9SCameron Grant return c; 201285648f9SCameron Grant } 202506a5308SCameron Grant } 20349c5e6e2SCameron Grant CHN_UNLOCK(c); 204285648f9SCameron Grant } 205f637a36cSCameron Grant 206f637a36cSCameron Grant /* no channel available */ 207f637a36cSCameron Grant if (direction == PCMDIR_PLAY) { 20867b1dce3SCameron Grant if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { 209f637a36cSCameron Grant /* try to create a vchan */ 210f637a36cSCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 211f637a36cSCameron Grant c = sce->channel; 212f637a36cSCameron Grant if (!SLIST_EMPTY(&c->children)) { 213f637a36cSCameron Grant err = vchan_create(c); 214f637a36cSCameron Grant if (!err) 215506a5308SCameron Grant return pcm_chnalloc(d, direction, pid, -1); 216f637a36cSCameron Grant else 217f637a36cSCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 218f637a36cSCameron Grant } 219f637a36cSCameron Grant } 220f637a36cSCameron Grant } 221f637a36cSCameron Grant } 222f637a36cSCameron Grant 223285648f9SCameron Grant return NULL; 224285648f9SCameron Grant } 225285648f9SCameron Grant 226b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 227285648f9SCameron Grant int 228b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 229285648f9SCameron Grant { 230b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 231285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 232b8f0d9e0SCameron Grant c->pid = -1; 23349c5e6e2SCameron Grant CHN_UNLOCK(c); 234285648f9SCameron Grant return 0; 235285648f9SCameron Grant } 236285648f9SCameron Grant 237285648f9SCameron Grant int 238285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 239285648f9SCameron Grant { 24049c5e6e2SCameron Grant int r; 24149c5e6e2SCameron Grant 242b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 243285648f9SCameron Grant c->refcount += ref; 24449c5e6e2SCameron Grant r = c->refcount; 24549c5e6e2SCameron Grant return r; 246285648f9SCameron Grant } 247285648f9SCameron Grant 24867b1dce3SCameron Grant int 24967b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta) 25067b1dce3SCameron Grant { 251a527dbc7SCameron Grant int r; 252a527dbc7SCameron Grant 253a527dbc7SCameron Grant if (delta == 0) 25467b1dce3SCameron Grant return d->inprog; 255a527dbc7SCameron Grant 256a527dbc7SCameron Grant /* backtrace(); */ 257a527dbc7SCameron Grant pcm_lock(d); 258a527dbc7SCameron Grant d->inprog += delta; 259a527dbc7SCameron Grant r = d->inprog; 260a527dbc7SCameron Grant pcm_unlock(d); 261a527dbc7SCameron Grant return r; 26267b1dce3SCameron Grant } 26367b1dce3SCameron Grant 26467b1dce3SCameron Grant static void 26567b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 26667b1dce3SCameron Grant { 26767b1dce3SCameron Grant struct pcm_channel *c; 26867b1dce3SCameron Grant struct snddev_channel *sce; 26967b1dce3SCameron Grant int err, done; 27067b1dce3SCameron Grant 27167b1dce3SCameron Grant if (num > 0 && d->vchancount == 0) { 27267b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 27367b1dce3SCameron Grant c = sce->channel; 27467b1dce3SCameron Grant if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) { 27567b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 27667b1dce3SCameron Grant err = vchan_create(c); 27767b1dce3SCameron Grant if (err) { 27867b1dce3SCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 27967b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 28067b1dce3SCameron Grant } 28167b1dce3SCameron Grant return; 28267b1dce3SCameron Grant } 28367b1dce3SCameron Grant } 28467b1dce3SCameron Grant } 28567b1dce3SCameron Grant if (num == 0 && d->vchancount > 0) { 28667b1dce3SCameron Grant done = 0; 28767b1dce3SCameron Grant while (!done) { 28867b1dce3SCameron Grant done = 1; 28967b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 29067b1dce3SCameron Grant c = sce->channel; 29167b1dce3SCameron Grant if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) { 29267b1dce3SCameron Grant done = 0; 293a527dbc7SCameron Grant snd_mtxlock(d->lock); 29467b1dce3SCameron Grant err = vchan_destroy(c); 295a527dbc7SCameron Grant snd_mtxunlock(d->lock); 29667b1dce3SCameron Grant if (err) 29767b1dce3SCameron Grant device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err); 2989cfd8eb3SPeter Wemm break; /* restart */ 29967b1dce3SCameron Grant } 30067b1dce3SCameron Grant } 30167b1dce3SCameron Grant } 30267b1dce3SCameron Grant } 30367b1dce3SCameron Grant } 30467b1dce3SCameron Grant 30582db23e2SCameron Grant #ifdef USING_DEVFS 30633dbf14aSCameron Grant static int 307cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 30833dbf14aSCameron Grant { 309b8f0d9e0SCameron Grant struct snddev_info *d; 31033dbf14aSCameron Grant int error, unit; 31133dbf14aSCameron Grant 31233dbf14aSCameron Grant unit = snd_unit; 31333dbf14aSCameron Grant error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 31433dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 31574ffd138SCameron Grant if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 316b8f0d9e0SCameron Grant return EINVAL; 317b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 318faeebea2SCameron Grant if (d == NULL || SLIST_EMPTY(&d->channels)) 319b8f0d9e0SCameron Grant return EINVAL; 32033dbf14aSCameron Grant snd_unit = unit; 32133dbf14aSCameron Grant } 32233dbf14aSCameron Grant return (error); 32333dbf14aSCameron Grant } 324b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 325cd9766c5SCameron Grant 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 32682db23e2SCameron Grant #endif 327987e5972SCameron Grant 328cd9766c5SCameron Grant static int 32967b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 330cd9766c5SCameron Grant { 33167b1dce3SCameron Grant struct snddev_info *d; 33267b1dce3SCameron Grant int i, v, error; 333cd9766c5SCameron Grant 33467b1dce3SCameron Grant v = snd_maxautovchans; 335cd9766c5SCameron Grant error = sysctl_handle_int(oidp, &v, sizeof(v), req); 336cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 337cd9766c5SCameron Grant if (v < 0 || v >= SND_MAXVCHANS) 338cd9766c5SCameron Grant return EINVAL; 33967b1dce3SCameron Grant if (v != snd_maxautovchans) { 34067b1dce3SCameron Grant for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 34167b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 34267b1dce3SCameron Grant if (!d) 34367b1dce3SCameron Grant continue; 34467b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 34567b1dce3SCameron Grant } 34667b1dce3SCameron Grant } 34767b1dce3SCameron Grant snd_maxautovchans = v; 348cd9766c5SCameron Grant } 349cd9766c5SCameron Grant return (error); 350cd9766c5SCameron Grant } 35167b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 35267b1dce3SCameron Grant 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 353f637a36cSCameron Grant 354285648f9SCameron Grant struct pcm_channel * 355285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 356987e5972SCameron Grant { 357285648f9SCameron Grant struct pcm_channel *ch; 35833dbf14aSCameron Grant char *dirs; 359a67fe5c1SCameron Grant int err, *pnum; 360987e5972SCameron Grant 361285648f9SCameron Grant switch(dir) { 362285648f9SCameron Grant case PCMDIR_PLAY: 363285648f9SCameron Grant dirs = "play"; 364a67fe5c1SCameron Grant pnum = &d->playcount; 365285648f9SCameron Grant break; 366a67fe5c1SCameron Grant 367285648f9SCameron Grant case PCMDIR_REC: 368285648f9SCameron Grant dirs = "record"; 369a67fe5c1SCameron Grant pnum = &d->reccount; 370285648f9SCameron Grant break; 371a67fe5c1SCameron Grant 372285648f9SCameron Grant case PCMDIR_VIRTUAL: 373285648f9SCameron Grant dirs = "virtual"; 374285648f9SCameron Grant dir = PCMDIR_PLAY; 375a67fe5c1SCameron Grant pnum = &d->vchancount; 376285648f9SCameron Grant break; 377a67fe5c1SCameron Grant 378285648f9SCameron Grant default: 379285648f9SCameron Grant return NULL; 3809c326820SCameron Grant } 381285648f9SCameron Grant 382a163d034SWarner Losh ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 383285648f9SCameron Grant if (!ch) 384285648f9SCameron Grant return NULL; 385285648f9SCameron Grant 386a163d034SWarner Losh ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 387285648f9SCameron Grant if (!ch->methods) { 388285648f9SCameron Grant free(ch, M_DEVBUF); 389a67fe5c1SCameron Grant 390285648f9SCameron Grant return NULL; 391285648f9SCameron Grant } 392285648f9SCameron Grant 39367beb5a5SCameron Grant snd_mtxlock(d->lock); 394a67fe5c1SCameron Grant ch->num = (*pnum)++; 39567beb5a5SCameron Grant snd_mtxunlock(d->lock); 396a67fe5c1SCameron Grant 397285648f9SCameron Grant ch->pid = -1; 398285648f9SCameron Grant ch->parentsnddev = d; 399285648f9SCameron Grant ch->parentchannel = parent; 400436c9b65SScott Long ch->dev = d->dev; 40167beb5a5SCameron Grant snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); 402285648f9SCameron Grant 4030f55ac6cSCameron Grant err = chn_init(ch, devinfo, dir); 4040f55ac6cSCameron Grant if (err) { 405a67fe5c1SCameron Grant device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 406285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 407285648f9SCameron Grant free(ch, M_DEVBUF); 40867beb5a5SCameron Grant snd_mtxlock(d->lock); 409a67fe5c1SCameron Grant (*pnum)--; 41067beb5a5SCameron Grant snd_mtxunlock(d->lock); 411a67fe5c1SCameron Grant 412285648f9SCameron Grant return NULL; 413bbb5bf3dSCameron Grant } 414285648f9SCameron Grant 415285648f9SCameron Grant return ch; 416285648f9SCameron Grant } 417285648f9SCameron Grant 418285648f9SCameron Grant int 419285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 420285648f9SCameron Grant { 421a67fe5c1SCameron Grant struct snddev_info *d; 422285648f9SCameron Grant int err; 423285648f9SCameron Grant 424a67fe5c1SCameron Grant d = ch->parentsnddev; 425285648f9SCameron Grant err = chn_kill(ch); 426285648f9SCameron Grant if (err) { 427a67fe5c1SCameron Grant device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 428285648f9SCameron Grant return err; 429285648f9SCameron Grant } 430285648f9SCameron Grant 431285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 432285648f9SCameron Grant free(ch, M_DEVBUF); 433285648f9SCameron Grant 434285648f9SCameron Grant return 0; 435285648f9SCameron Grant } 436285648f9SCameron Grant 437285648f9SCameron Grant int 438f637a36cSCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 439285648f9SCameron Grant { 44067b1dce3SCameron Grant struct snddev_channel *sce, *tmp, *after; 441285648f9SCameron Grant int unit = device_get_unit(d->dev); 44267beb5a5SCameron Grant int x = -1; 443b8f0d9e0SCameron Grant 444a163d034SWarner Losh sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 445285648f9SCameron Grant if (!sce) { 446285648f9SCameron Grant return ENOMEM; 447285648f9SCameron Grant } 448285648f9SCameron Grant 4497cf0e77aSOrion Hodson snd_mtxlock(d->lock); 450285648f9SCameron Grant sce->channel = ch; 45167b1dce3SCameron Grant if (SLIST_EMPTY(&d->channels)) { 452285648f9SCameron Grant SLIST_INSERT_HEAD(&d->channels, sce, link); 45367b1dce3SCameron Grant } else { 45467b1dce3SCameron Grant after = NULL; 45567b1dce3SCameron Grant SLIST_FOREACH(tmp, &d->channels, link) { 45667b1dce3SCameron Grant after = tmp; 45767b1dce3SCameron Grant } 45867b1dce3SCameron Grant SLIST_INSERT_AFTER(after, sce, link); 45967b1dce3SCameron Grant } 46067beb5a5SCameron Grant if (mkdev) 46167beb5a5SCameron Grant x = d->devcount++; 46267beb5a5SCameron Grant snd_mtxunlock(d->lock); 463285648f9SCameron Grant 464506a5308SCameron Grant if (mkdev) { 46567beb5a5SCameron Grant dsp_register(unit, x); 466506a5308SCameron Grant if (ch->direction == PCMDIR_REC) 467506a5308SCameron Grant dsp_registerrec(unit, ch->num); 468506a5308SCameron Grant } 469b8f0d9e0SCameron Grant 47033dbf14aSCameron Grant return 0; 47133dbf14aSCameron Grant } 47233dbf14aSCameron Grant 473285648f9SCameron Grant int 474f637a36cSCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 47533dbf14aSCameron Grant { 476285648f9SCameron Grant struct snddev_channel *sce; 477285648f9SCameron Grant int unit = device_get_unit(d->dev); 478a527dbc7SCameron Grant int ourlock; 47933dbf14aSCameron Grant 480a527dbc7SCameron Grant ourlock = 0; 481a527dbc7SCameron Grant if (!mtx_owned(d->lock)) { 48249c5e6e2SCameron Grant snd_mtxlock(d->lock); 483a527dbc7SCameron Grant ourlock = 1; 484a527dbc7SCameron Grant } 485a527dbc7SCameron Grant 486285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 487285648f9SCameron Grant if (sce->channel == ch) 488285648f9SCameron Grant goto gotit; 48933dbf14aSCameron Grant } 490a527dbc7SCameron Grant if (ourlock) 49149c5e6e2SCameron Grant snd_mtxunlock(d->lock); 492285648f9SCameron Grant return EINVAL; 493285648f9SCameron Grant gotit: 494285648f9SCameron Grant SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 495506a5308SCameron Grant if (rmdev) { 496f637a36cSCameron Grant dsp_unregister(unit, --d->devcount); 497506a5308SCameron Grant if (ch->direction == PCMDIR_REC) 498a67fe5c1SCameron Grant dsp_unregisterrec(unit, ch->num); 499506a5308SCameron Grant } 50067beb5a5SCameron Grant 50167beb5a5SCameron Grant if (ch->direction == PCMDIR_REC) 50267beb5a5SCameron Grant d->reccount--; 50367beb5a5SCameron Grant else if (ch->flags & CHN_F_VIRTUAL) 50467beb5a5SCameron Grant d->vchancount--; 50567beb5a5SCameron Grant else 50667beb5a5SCameron Grant d->playcount--; 50767beb5a5SCameron Grant 508a527dbc7SCameron Grant if (ourlock) 50949c5e6e2SCameron Grant snd_mtxunlock(d->lock); 51067beb5a5SCameron Grant free(sce, M_DEVBUF); 511285648f9SCameron Grant 512987e5972SCameron Grant return 0; 513987e5972SCameron Grant } 514987e5972SCameron Grant 515987e5972SCameron Grant int 516285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 517285648f9SCameron Grant { 518285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 51967b1dce3SCameron Grant struct pcm_channel *ch; 52067b1dce3SCameron Grant int err; 521285648f9SCameron Grant 522285648f9SCameron Grant ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 523285648f9SCameron Grant if (!ch) { 524285648f9SCameron Grant device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 525285648f9SCameron Grant return ENODEV; 526285648f9SCameron Grant } 527cd9766c5SCameron Grant 528f637a36cSCameron Grant err = pcm_chn_add(d, ch, 1); 529285648f9SCameron Grant if (err) { 530285648f9SCameron Grant device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 53167beb5a5SCameron Grant snd_mtxunlock(d->lock); 532285648f9SCameron Grant pcm_chn_destroy(ch); 533cd9766c5SCameron Grant return err; 534cd9766c5SCameron Grant } 535cd9766c5SCameron Grant 53661698e0cSAlexander Kabaev if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) && 53761698e0cSAlexander Kabaev ch->direction == PCMDIR_PLAY && d->vchancount == 0) { 538cd9766c5SCameron Grant ch->flags |= CHN_F_BUSY; 539cd9766c5SCameron Grant err = vchan_create(ch); 540cd9766c5SCameron Grant if (err) { 54167b1dce3SCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 54267b1dce3SCameron Grant ch->flags &= ~CHN_F_BUSY; 543cd9766c5SCameron Grant } 544285648f9SCameron Grant } 545285648f9SCameron Grant 546285648f9SCameron Grant return err; 547285648f9SCameron Grant } 548285648f9SCameron Grant 549285648f9SCameron Grant static int 550285648f9SCameron Grant pcm_killchan(device_t dev) 551285648f9SCameron Grant { 552285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 553285648f9SCameron Grant struct snddev_channel *sce; 554e33bee07SOlivier Houchard struct pcm_channel *ch; 555e33bee07SOlivier Houchard int error = 0; 556285648f9SCameron Grant 55749c5e6e2SCameron Grant snd_mtxlock(d->lock); 558285648f9SCameron Grant sce = SLIST_FIRST(&d->channels); 55949c5e6e2SCameron Grant snd_mtxunlock(d->lock); 560e33bee07SOlivier Houchard ch = sce->channel; 561285648f9SCameron Grant 562e550089dSOlivier Houchard error = pcm_chn_remove(d, sce->channel, SLIST_EMPTY(&ch->children)); 563e33bee07SOlivier Houchard if (error) 564e33bee07SOlivier Houchard return (error); 565e33bee07SOlivier Houchard return (pcm_chn_destroy(ch)); 566285648f9SCameron Grant } 567285648f9SCameron Grant 568285648f9SCameron Grant int 569987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 570987e5972SCameron Grant { 57166ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 57249c5e6e2SCameron Grant 57349c5e6e2SCameron Grant snd_mtxlock(d->lock); 574987e5972SCameron Grant strncpy(d->status, str, SND_STATUSLEN); 57549c5e6e2SCameron Grant snd_mtxunlock(d->lock); 576987e5972SCameron Grant return 0; 577987e5972SCameron Grant } 578987e5972SCameron Grant 579987e5972SCameron Grant u_int32_t 580987e5972SCameron Grant pcm_getflags(device_t dev) 581987e5972SCameron Grant { 58266ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 58349c5e6e2SCameron Grant 584987e5972SCameron Grant return d->flags; 585987e5972SCameron Grant } 586987e5972SCameron Grant 587987e5972SCameron Grant void 588987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val) 589987e5972SCameron Grant { 59066ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 591d95502a8SCameron Grant 592987e5972SCameron Grant d->flags = val; 593987e5972SCameron Grant } 594987e5972SCameron Grant 59539004e69SCameron Grant void * 59639004e69SCameron Grant pcm_getdevinfo(device_t dev) 59739004e69SCameron Grant { 59866ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 59949c5e6e2SCameron Grant 60039004e69SCameron Grant return d->devinfo; 60139004e69SCameron Grant } 60239004e69SCameron Grant 603a67fe5c1SCameron Grant unsigned int 604a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 605a67fe5c1SCameron Grant { 606a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 6074e60be34SCameron Grant int sz, x; 608a67fe5c1SCameron Grant 609a67fe5c1SCameron Grant sz = 0; 6104e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 6114e60be34SCameron Grant x = sz; 612a67fe5c1SCameron Grant RANGE(sz, min, max); 6134e60be34SCameron Grant if (x != sz) 6144e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 6154e60be34SCameron Grant x = min; 6164e60be34SCameron Grant while (x < sz) 6174e60be34SCameron Grant x <<= 1; 6184e60be34SCameron Grant if (x > sz) 6194e60be34SCameron Grant x >>= 1; 6204e60be34SCameron Grant if (x != sz) { 6214e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 6224e60be34SCameron Grant sz = x; 6234e60be34SCameron Grant } 6244e60be34SCameron Grant } else { 625a67fe5c1SCameron Grant sz = deflt; 6264e60be34SCameron Grant } 6274e60be34SCameron Grant 628a67fe5c1SCameron Grant d->bufsz = sz; 629a67fe5c1SCameron Grant 630a67fe5c1SCameron Grant return sz; 631a67fe5c1SCameron Grant } 632a67fe5c1SCameron Grant 633987e5972SCameron Grant int 634987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 635987e5972SCameron Grant { 63666ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 637987e5972SCameron Grant 638b8a36395SCameron Grant if (pcm_veto_load) { 639b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 640b8a36395SCameron Grant 641b8a36395SCameron Grant return EINVAL; 642b8a36395SCameron Grant } 643b8a36395SCameron Grant 6442c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 645285648f9SCameron Grant 646cd9766c5SCameron Grant d->flags = 0; 647e4d5b250SCameron Grant d->dev = dev; 648987e5972SCameron Grant d->devinfo = devinfo; 649f637a36cSCameron Grant d->devcount = 0; 650506a5308SCameron Grant d->reccount = 0; 651a67fe5c1SCameron Grant d->playcount = 0; 652f637a36cSCameron Grant d->vchancount = 0; 653d95502a8SCameron Grant d->inprog = 0; 654833f7023SCameron Grant 655d95502a8SCameron Grant if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 656285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 657d95502a8SCameron Grant 658285648f9SCameron Grant d->fakechan = fkchan_setup(dev); 659285648f9SCameron Grant chn_init(d->fakechan, NULL, 0); 660987e5972SCameron Grant 66182db23e2SCameron Grant #ifdef SND_DYNSYSCTL 662cc486d80SJohn Baldwin sysctl_ctx_init(&d->sysctl_tree); 663cc486d80SJohn Baldwin d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 664a3e893e0SJohn Baldwin SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 665cc486d80SJohn Baldwin device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 666a3e893e0SJohn Baldwin if (d->sysctl_tree_top == NULL) { 667cc486d80SJohn Baldwin sysctl_ctx_free(&d->sysctl_tree); 668cc486d80SJohn Baldwin goto no; 669cc486d80SJohn Baldwin } 670a67fe5c1SCameron Grant SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 671a67fe5c1SCameron Grant OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 67282db23e2SCameron Grant #endif 673b8f0d9e0SCameron Grant if (numplay > 0) 67467b1dce3SCameron Grant vchan_initsys(dev); 675cd9766c5SCameron Grant if (numplay == 1) 676cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 677cd9766c5SCameron Grant 67867b1dce3SCameron Grant sndstat_register(dev, d->status, sndstat_prepare_pcm); 679987e5972SCameron Grant return 0; 680987e5972SCameron Grant no: 68149c5e6e2SCameron Grant snd_mtxfree(d->lock); 682987e5972SCameron Grant return ENXIO; 683987e5972SCameron Grant } 684987e5972SCameron Grant 68533dbf14aSCameron Grant int 68633dbf14aSCameron Grant pcm_unregister(device_t dev) 6877c438dbeSCameron Grant { 68866ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 689285648f9SCameron Grant struct snddev_channel *sce; 690a67fe5c1SCameron Grant struct pcm_channel *ch; 6917c438dbeSCameron Grant 69249c5e6e2SCameron Grant snd_mtxlock(d->lock); 693d95502a8SCameron Grant if (d->inprog) { 6945c25132aSGeorge C A Reid device_printf(dev, "unregister: operation in progress\n"); 6955c25132aSGeorge C A Reid snd_mtxunlock(d->lock); 6965c25132aSGeorge C A Reid return EBUSY; 6975c25132aSGeorge C A Reid } 6985c25132aSGeorge C A Reid if (sndstat_busy() != 0) { 6995c25132aSGeorge C A Reid device_printf(dev, "unregister: sndstat busy\n"); 700d95502a8SCameron Grant snd_mtxunlock(d->lock); 701d95502a8SCameron Grant return EBUSY; 702d95502a8SCameron Grant } 703285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 704a67fe5c1SCameron Grant ch = sce->channel; 705a67fe5c1SCameron Grant if (ch->refcount > 0) { 70621ed9908SCameron Grant device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 70749c5e6e2SCameron Grant snd_mtxunlock(d->lock); 708285648f9SCameron Grant return EBUSY; 709285648f9SCameron Grant } 710c9b53085SCameron Grant } 711d95502a8SCameron Grant if (mixer_uninit(dev)) { 7125c25132aSGeorge C A Reid device_printf(dev, "unregister: mixer busy\n"); 71349c5e6e2SCameron Grant snd_mtxunlock(d->lock); 714c9b53085SCameron Grant return EBUSY; 715c9b53085SCameron Grant } 7167c438dbeSCameron Grant 71766ef8af5SCameron Grant #ifdef SND_DYNSYSCTL 71866ef8af5SCameron Grant d->sysctl_tree_top = NULL; 71966ef8af5SCameron Grant sysctl_ctx_free(&d->sysctl_tree); 72066ef8af5SCameron Grant #endif 721faeebea2SCameron Grant while (!SLIST_EMPTY(&d->channels)) 722285648f9SCameron Grant pcm_killchan(dev); 7237c438dbeSCameron Grant 72466ef8af5SCameron Grant chn_kill(d->fakechan); 72566ef8af5SCameron Grant fkchan_kill(d->fakechan); 72682db23e2SCameron Grant 72747172de8SNick Sayer sndstat_unregister(dev); 72849c5e6e2SCameron Grant snd_mtxfree(d->lock); 72933dbf14aSCameron Grant return 0; 73033dbf14aSCameron Grant } 7317c438dbeSCameron Grant 73267b1dce3SCameron Grant /************************************************************************/ 73367b1dce3SCameron Grant 73467b1dce3SCameron Grant static int 73567b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 73667b1dce3SCameron Grant { 73767b1dce3SCameron Grant struct snddev_info *d; 73867b1dce3SCameron Grant struct snddev_channel *sce; 73967b1dce3SCameron Grant struct pcm_channel *c; 74067b1dce3SCameron Grant struct pcm_feeder *f; 74167b1dce3SCameron Grant int pc, rc, vc; 74267b1dce3SCameron Grant 74367b1dce3SCameron Grant if (verbose < 1) 74467b1dce3SCameron Grant return 0; 74567b1dce3SCameron Grant 74667b1dce3SCameron Grant d = device_get_softc(dev); 74767b1dce3SCameron Grant if (!d) 74867b1dce3SCameron Grant return ENXIO; 74967b1dce3SCameron Grant 75067b1dce3SCameron Grant snd_mtxlock(d->lock); 75167b1dce3SCameron Grant if (!SLIST_EMPTY(&d->channels)) { 75267b1dce3SCameron Grant pc = rc = vc = 0; 75367b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 75467b1dce3SCameron Grant c = sce->channel; 75567b1dce3SCameron Grant if (c->direction == PCMDIR_PLAY) { 75667b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 75767b1dce3SCameron Grant vc++; 75867b1dce3SCameron Grant else 75967b1dce3SCameron Grant pc++; 76067b1dce3SCameron Grant } else 76167b1dce3SCameron Grant rc++; 76267b1dce3SCameron Grant } 763a67fe5c1SCameron Grant sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 76467b1dce3SCameron Grant (d->flags & SD_F_SIMPLEX)? "" : " duplex", 76567b1dce3SCameron Grant #ifdef USING_DEVFS 76667b1dce3SCameron Grant (device_get_unit(dev) == snd_unit)? " default" : "" 76767b1dce3SCameron Grant #else 76867b1dce3SCameron Grant "" 76967b1dce3SCameron Grant #endif 77067b1dce3SCameron Grant ); 771a527dbc7SCameron Grant 772a527dbc7SCameron Grant if (verbose <= 1) { 773a527dbc7SCameron Grant snd_mtxunlock(d->lock); 774a527dbc7SCameron Grant return 0; 775a527dbc7SCameron Grant } 776a527dbc7SCameron Grant 77767b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 77867b1dce3SCameron Grant c = sce->channel; 779a3285889SCameron Grant sbuf_printf(s, "\n\t"); 780a3285889SCameron Grant 781a527dbc7SCameron Grant /* it would be bettet to indent child channels */ 782a3285889SCameron Grant sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 7834c68642aSCameron Grant sbuf_printf(s, "spd %d", c->speed); 7844c68642aSCameron Grant if (c->speed != sndbuf_getspd(c->bufhard)) 7854c68642aSCameron Grant sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 7864c68642aSCameron Grant sbuf_printf(s, ", fmt 0x%08x", c->format); 7874c68642aSCameron Grant if (c->format != sndbuf_getfmt(c->bufhard)) 7884c68642aSCameron Grant sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 789a527dbc7SCameron Grant sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 79067b1dce3SCameron Grant if (c->pid != -1) 79167b1dce3SCameron Grant sbuf_printf(s, ", pid %d", c->pid); 79267b1dce3SCameron Grant sbuf_printf(s, "\n\t"); 7934c68642aSCameron Grant 794edecdda7SCameron Grant if (c->bufhard != NULL && c->bufsoft != NULL) { 795a3285889SCameron Grant sbuf_printf(s, "interrupts %d, ", c->interrupts); 796a3285889SCameron Grant if (c->direction == PCMDIR_REC) 797a3285889SCameron Grant sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 798a3285889SCameron Grant c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 799a3285889SCameron Grant else 800edecdda7SCameron Grant sbuf_printf(s, "underruns %d, ready %d", 801edecdda7SCameron Grant c->xruns, sndbuf_getready(c->bufsoft)); 802a3285889SCameron Grant sbuf_printf(s, "\n\t"); 803a3285889SCameron Grant } 8044c68642aSCameron Grant 8054c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 8064c68642aSCameron Grant sbuf_printf(s, " -> "); 80767b1dce3SCameron Grant f = c->feeder; 8084c68642aSCameron Grant while (f->source != NULL) 8094c68642aSCameron Grant f = f->source; 8104c68642aSCameron Grant while (f != NULL) { 81167b1dce3SCameron Grant sbuf_printf(s, "%s", f->class->name); 81267b1dce3SCameron Grant if (f->desc->type == FEEDER_FMT) 8134c68642aSCameron Grant sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 81467b1dce3SCameron Grant if (f->desc->type == FEEDER_RATE) 8154c68642aSCameron Grant sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 81667b1dce3SCameron Grant if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 8174c68642aSCameron Grant sbuf_printf(s, "(0x%08x)", f->desc->out); 8184c68642aSCameron Grant sbuf_printf(s, " -> "); 8194c68642aSCameron Grant f = f->parent; 82067b1dce3SCameron Grant } 8214c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 82267b1dce3SCameron Grant } 82367b1dce3SCameron Grant } else 82467b1dce3SCameron Grant sbuf_printf(s, " (mixer only)"); 82567b1dce3SCameron Grant snd_mtxunlock(d->lock); 82667b1dce3SCameron Grant 82767b1dce3SCameron Grant return 0; 82867b1dce3SCameron Grant } 82967b1dce3SCameron Grant 83067b1dce3SCameron Grant /************************************************************************/ 83167b1dce3SCameron Grant 83267b1dce3SCameron Grant #ifdef SND_DYNSYSCTL 83367b1dce3SCameron Grant int 83467b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 83567b1dce3SCameron Grant { 83667b1dce3SCameron Grant struct snddev_info *d; 83767b1dce3SCameron Grant struct snddev_channel *sce; 83867b1dce3SCameron Grant struct pcm_channel *c; 839a527dbc7SCameron Grant int err, newcnt, cnt, busy; 840a527dbc7SCameron Grant int x; 84167b1dce3SCameron Grant 84267b1dce3SCameron Grant d = oidp->oid_arg1; 84367b1dce3SCameron Grant 844a527dbc7SCameron Grant x = pcm_inprog(d, 1); 845a527dbc7SCameron Grant if (x != 1) { 846a527dbc7SCameron Grant printf("x: %d\n", x); 847a527dbc7SCameron Grant pcm_inprog(d, -1); 848a527dbc7SCameron Grant return EINPROGRESS; 849a527dbc7SCameron Grant } 850a527dbc7SCameron Grant 851a527dbc7SCameron Grant busy = 0; 85267b1dce3SCameron Grant cnt = 0; 85367b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 85467b1dce3SCameron Grant c = sce->channel; 855a527dbc7SCameron Grant if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) { 85667b1dce3SCameron Grant cnt++; 857a527dbc7SCameron Grant if (c->flags & CHN_F_BUSY) 858a527dbc7SCameron Grant busy++; 85967b1dce3SCameron Grant } 860a527dbc7SCameron Grant } 861a527dbc7SCameron Grant 86267b1dce3SCameron Grant newcnt = cnt; 86367b1dce3SCameron Grant 86467b1dce3SCameron Grant err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 865a527dbc7SCameron Grant 86667b1dce3SCameron Grant if (err == 0 && req->newptr != NULL) { 867a527dbc7SCameron Grant 86867b1dce3SCameron Grant if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 869a527dbc7SCameron Grant pcm_inprog(d, -1); 870a527dbc7SCameron Grant return E2BIG; 87167b1dce3SCameron Grant } 87267b1dce3SCameron Grant 87367b1dce3SCameron Grant if (newcnt > cnt) { 87467b1dce3SCameron Grant /* add new vchans - find a parent channel first */ 87567b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 87667b1dce3SCameron Grant c = sce->channel; 87767b1dce3SCameron Grant /* not a candidate if not a play channel */ 87867b1dce3SCameron Grant if (c->direction != PCMDIR_PLAY) 8799cfd8eb3SPeter Wemm continue; 88067b1dce3SCameron Grant /* not a candidate if a virtual channel */ 88167b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 8829cfd8eb3SPeter Wemm continue; 88367b1dce3SCameron Grant /* not a candidate if it's in use */ 88467b1dce3SCameron Grant if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 8859cfd8eb3SPeter Wemm continue; 88667b1dce3SCameron Grant /* 88767b1dce3SCameron Grant * if we get here we're a nonvirtual play channel, and either 88867b1dce3SCameron Grant * 1) not busy 88967b1dce3SCameron Grant * 2) busy with children, not directly open 89067b1dce3SCameron Grant * 89167b1dce3SCameron Grant * thus we can add children 89267b1dce3SCameron Grant */ 89367b1dce3SCameron Grant goto addok; 89467b1dce3SCameron Grant } 895a527dbc7SCameron Grant pcm_inprog(d, -1); 89667b1dce3SCameron Grant return EBUSY; 89767b1dce3SCameron Grant addok: 89867b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 89967b1dce3SCameron Grant while (err == 0 && newcnt > cnt) { 90067b1dce3SCameron Grant err = vchan_create(c); 90167b1dce3SCameron Grant if (err == 0) 90267b1dce3SCameron Grant cnt++; 90367b1dce3SCameron Grant } 90467b1dce3SCameron Grant if (SLIST_EMPTY(&c->children)) 90567b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 90667b1dce3SCameron Grant } else if (newcnt < cnt) { 907a527dbc7SCameron Grant if (busy > newcnt) { 908a527dbc7SCameron Grant printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy); 909a527dbc7SCameron Grant pcm_inprog(d, -1); 910a527dbc7SCameron Grant return EBUSY; 911a527dbc7SCameron Grant } 912a527dbc7SCameron Grant 913a527dbc7SCameron Grant snd_mtxlock(d->lock); 91467b1dce3SCameron Grant while (err == 0 && newcnt < cnt) { 91567b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 91667b1dce3SCameron Grant c = sce->channel; 91767b1dce3SCameron Grant if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 91867b1dce3SCameron Grant goto remok; 91967b1dce3SCameron Grant } 920a527dbc7SCameron Grant snd_mtxunlock(d->lock); 921a527dbc7SCameron Grant pcm_inprog(d, -1); 92267b1dce3SCameron Grant return EINVAL; 92367b1dce3SCameron Grant remok: 92467b1dce3SCameron Grant err = vchan_destroy(c); 92567b1dce3SCameron Grant if (err == 0) 92667b1dce3SCameron Grant cnt--; 92767b1dce3SCameron Grant } 928a527dbc7SCameron Grant snd_mtxunlock(d->lock); 92967b1dce3SCameron Grant } 93067b1dce3SCameron Grant } 931a527dbc7SCameron Grant pcm_inprog(d, -1); 93267b1dce3SCameron Grant return err; 93367b1dce3SCameron Grant } 93467b1dce3SCameron Grant #endif 93567b1dce3SCameron Grant 93667b1dce3SCameron Grant /************************************************************************/ 93767b1dce3SCameron Grant 93833dbf14aSCameron Grant static moduledata_t sndpcm_mod = { 93933dbf14aSCameron Grant "snd_pcm", 940d95502a8SCameron Grant NULL, 94133dbf14aSCameron Grant NULL 94233dbf14aSCameron Grant }; 94333dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 94433dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER); 945