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> 305ee30e27SMathew Kanner #include <dev/sound/pcm/dsp.h> 317c438dbeSCameron Grant #include <sys/sysctl.h> 32285648f9SCameron Grant 3367b1dce3SCameron Grant #include "feeder_if.h" 3467b1dce3SCameron Grant 3567b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 3667b1dce3SCameron Grant 37d95502a8SCameron Grant devclass_t pcm_devclass; 3882db23e2SCameron Grant 39b8a36395SCameron Grant int pcm_veto_load = 1; 40b8a36395SCameron Grant 4182db23e2SCameron Grant #ifdef USING_DEVFS 42d95502a8SCameron Grant int snd_unit = 0; 4309786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit); 4482db23e2SCameron Grant #endif 45cbe7d6a3SCameron Grant 4667b1dce3SCameron Grant int snd_maxautovchans = 0; 4767b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 48987e5972SCameron Grant 4982db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 5082db23e2SCameron Grant 5167b1dce3SCameron Grant static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 5267b1dce3SCameron Grant 5367b1dce3SCameron Grant struct sysctl_ctx_list * 5467b1dce3SCameron Grant snd_sysctl_tree(device_t dev) 5567b1dce3SCameron Grant { 5667b1dce3SCameron Grant struct snddev_info *d = device_get_softc(dev); 5767b1dce3SCameron Grant 5867b1dce3SCameron Grant return &d->sysctl_tree; 5967b1dce3SCameron Grant } 6067b1dce3SCameron Grant 6167b1dce3SCameron Grant struct sysctl_oid * 6267b1dce3SCameron Grant snd_sysctl_tree_top(device_t dev) 6367b1dce3SCameron Grant { 6467b1dce3SCameron Grant struct snddev_info *d = device_get_softc(dev); 6567b1dce3SCameron Grant 6667b1dce3SCameron Grant return d->sysctl_tree_top; 6767b1dce3SCameron Grant } 6867b1dce3SCameron Grant 6937209180SCameron Grant void * 702c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type) 7137209180SCameron Grant { 7237209180SCameron Grant #ifdef USING_MUTEX 7337209180SCameron Grant struct mtx *m; 7437209180SCameron Grant 75a163d034SWarner Losh m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 7637209180SCameron Grant if (m == NULL) 7737209180SCameron Grant return NULL; 7812e524a2SDon Lewis mtx_init(m, desc, type, MTX_DEF); 7912e524a2SDon Lewis return m; 8012e524a2SDon Lewis #else 8112e524a2SDon Lewis return (void *)0xcafebabe; 8212e524a2SDon Lewis #endif 8312e524a2SDon Lewis } 8412e524a2SDon Lewis 8512e524a2SDon Lewis void * 8612e524a2SDon Lewis snd_chnmtxcreate(const char *desc, const char *type) 8712e524a2SDon Lewis { 8812e524a2SDon Lewis #ifdef USING_MUTEX 8912e524a2SDon Lewis struct mtx *m; 9012e524a2SDon Lewis 9112e524a2SDon Lewis m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 9212e524a2SDon Lewis if (m == NULL) 9312e524a2SDon Lewis return NULL; 9412e524a2SDon Lewis mtx_init(m, desc, type, MTX_DEF | MTX_DUPOK); 9537209180SCameron Grant return m; 9637209180SCameron Grant #else 97a983d575SCameron Grant return (void *)0xcafebabe; 9837209180SCameron Grant #endif 9937209180SCameron Grant } 10037209180SCameron Grant 10137209180SCameron Grant void 10237209180SCameron Grant snd_mtxfree(void *m) 10337209180SCameron Grant { 10437209180SCameron Grant #ifdef USING_MUTEX 10537209180SCameron Grant struct mtx *mtx = m; 10637209180SCameron Grant 10767beb5a5SCameron Grant /* mtx_assert(mtx, MA_OWNED); */ 10837209180SCameron Grant mtx_destroy(mtx); 10937209180SCameron Grant free(mtx, M_DEVBUF); 11037209180SCameron Grant #endif 11137209180SCameron Grant } 11237209180SCameron Grant 11337209180SCameron Grant void 11437209180SCameron Grant snd_mtxassert(void *m) 11537209180SCameron Grant { 11637209180SCameron Grant #ifdef USING_MUTEX 117f00f162aSCameron Grant #ifdef INVARIANTS 11837209180SCameron Grant struct mtx *mtx = m; 11937209180SCameron Grant 12037209180SCameron Grant mtx_assert(mtx, MA_OWNED); 12137209180SCameron Grant #endif 122f00f162aSCameron Grant #endif 12337209180SCameron Grant } 12467beb5a5SCameron Grant /* 12537209180SCameron Grant void 12637209180SCameron Grant snd_mtxlock(void *m) 12737209180SCameron Grant { 12837209180SCameron Grant #ifdef USING_MUTEX 12937209180SCameron Grant struct mtx *mtx = m; 13037209180SCameron Grant 13137209180SCameron Grant mtx_lock(mtx); 13237209180SCameron Grant #endif 13337209180SCameron Grant } 13437209180SCameron Grant 13537209180SCameron Grant void 13637209180SCameron Grant snd_mtxunlock(void *m) 13737209180SCameron Grant { 13837209180SCameron Grant #ifdef USING_MUTEX 13937209180SCameron Grant struct mtx *mtx = m; 14037209180SCameron Grant 14137209180SCameron Grant mtx_unlock(mtx); 14237209180SCameron Grant #endif 14337209180SCameron Grant } 14467beb5a5SCameron Grant */ 14537209180SCameron Grant int 14637209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 14737209180SCameron Grant { 14837209180SCameron Grant #ifdef USING_MUTEX 14937209180SCameron Grant flags &= INTR_MPSAFE; 15046700f12SPeter Wemm flags |= INTR_TYPE_AV; 15137209180SCameron Grant #else 15246700f12SPeter Wemm flags = INTR_TYPE_AV; 15337209180SCameron Grant #endif 15437209180SCameron Grant return bus_setup_intr(dev, res, flags, hand, param, cookiep); 15537209180SCameron Grant } 15637209180SCameron Grant 157a527dbc7SCameron Grant #ifndef PCM_DEBUG_MTX 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 } 169a527dbc7SCameron Grant #endif 17067b1dce3SCameron Grant 17167b1dce3SCameron Grant struct pcm_channel * 17267b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d) 17367b1dce3SCameron Grant { 17467b1dce3SCameron Grant return d->fakechan; 17567b1dce3SCameron Grant } 17667b1dce3SCameron Grant 177b8f0d9e0SCameron Grant /* return a locked channel */ 178285648f9SCameron Grant struct pcm_channel * 179506a5308SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum) 180285648f9SCameron Grant { 181285648f9SCameron Grant struct pcm_channel *c; 182285648f9SCameron Grant struct snddev_channel *sce; 183f637a36cSCameron Grant int err; 184285648f9SCameron Grant 185b8f0d9e0SCameron Grant snd_mtxassert(d->lock); 186f637a36cSCameron Grant 187f637a36cSCameron Grant /* scan for a free channel */ 188285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 189285648f9SCameron Grant c = sce->channel; 19049c5e6e2SCameron Grant CHN_LOCK(c); 191285648f9SCameron Grant if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 192506a5308SCameron Grant if (chnum == -1 || c->num == chnum) { 193285648f9SCameron Grant c->flags |= CHN_F_BUSY; 194b8f0d9e0SCameron Grant c->pid = pid; 195285648f9SCameron Grant return c; 196285648f9SCameron Grant } 197506a5308SCameron Grant } 19849c5e6e2SCameron Grant CHN_UNLOCK(c); 199285648f9SCameron Grant } 200f637a36cSCameron Grant 201f637a36cSCameron Grant /* no channel available */ 202f637a36cSCameron Grant if (direction == PCMDIR_PLAY) { 20367b1dce3SCameron Grant if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { 204f637a36cSCameron Grant /* try to create a vchan */ 205f637a36cSCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 206f637a36cSCameron Grant c = sce->channel; 20712e524a2SDon Lewis CHN_LOCK(c); 208f637a36cSCameron Grant if (!SLIST_EMPTY(&c->children)) { 209f637a36cSCameron Grant err = vchan_create(c); 21012e524a2SDon Lewis CHN_UNLOCK(c); 211f637a36cSCameron Grant if (!err) 212506a5308SCameron Grant return pcm_chnalloc(d, direction, pid, -1); 213f637a36cSCameron Grant else 214f637a36cSCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 21512e524a2SDon Lewis } else 21612e524a2SDon Lewis CHN_UNLOCK(c); 217f637a36cSCameron Grant } 218f637a36cSCameron Grant } 219f637a36cSCameron Grant } 220f637a36cSCameron Grant 221285648f9SCameron Grant return NULL; 222285648f9SCameron Grant } 223285648f9SCameron Grant 224b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 225285648f9SCameron Grant int 226b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 227285648f9SCameron Grant { 228b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 229285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 230b8f0d9e0SCameron Grant c->pid = -1; 23149c5e6e2SCameron Grant CHN_UNLOCK(c); 232285648f9SCameron Grant return 0; 233285648f9SCameron Grant } 234285648f9SCameron Grant 235285648f9SCameron Grant int 236285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 237285648f9SCameron Grant { 23849c5e6e2SCameron Grant int r; 23949c5e6e2SCameron Grant 240b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 241285648f9SCameron Grant c->refcount += ref; 24249c5e6e2SCameron Grant r = c->refcount; 24349c5e6e2SCameron Grant return r; 244285648f9SCameron Grant } 245285648f9SCameron Grant 24667b1dce3SCameron Grant int 24767b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta) 24867b1dce3SCameron Grant { 249a527dbc7SCameron Grant int r; 250a527dbc7SCameron Grant 251a527dbc7SCameron Grant if (delta == 0) 25267b1dce3SCameron Grant return d->inprog; 253a527dbc7SCameron Grant 254a527dbc7SCameron Grant /* backtrace(); */ 255a527dbc7SCameron Grant pcm_lock(d); 256a527dbc7SCameron Grant d->inprog += delta; 257a527dbc7SCameron Grant r = d->inprog; 258a527dbc7SCameron Grant pcm_unlock(d); 259a527dbc7SCameron Grant return r; 26067b1dce3SCameron Grant } 26167b1dce3SCameron Grant 26267b1dce3SCameron Grant static void 26367b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 26467b1dce3SCameron Grant { 26567b1dce3SCameron Grant struct pcm_channel *c; 26667b1dce3SCameron Grant struct snddev_channel *sce; 26767b1dce3SCameron Grant int err, done; 26867b1dce3SCameron Grant 26967b1dce3SCameron Grant if (num > 0 && d->vchancount == 0) { 27067b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 27167b1dce3SCameron Grant c = sce->channel; 27212e524a2SDon Lewis CHN_LOCK(c); 27367b1dce3SCameron Grant if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) { 27467b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 27567b1dce3SCameron Grant err = vchan_create(c); 27667b1dce3SCameron Grant if (err) { 27767b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 27812e524a2SDon Lewis CHN_UNLOCK(c); 27912e524a2SDon Lewis device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 28012e524a2SDon Lewis } else 28112e524a2SDon Lewis CHN_UNLOCK(c); 28267b1dce3SCameron Grant return; 28367b1dce3SCameron Grant } 28412e524a2SDon Lewis CHN_UNLOCK(c); 28567b1dce3SCameron Grant } 28667b1dce3SCameron Grant } 28767b1dce3SCameron Grant if (num == 0 && d->vchancount > 0) { 28867b1dce3SCameron Grant done = 0; 28967b1dce3SCameron Grant while (!done) { 29067b1dce3SCameron Grant done = 1; 29167b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 29267b1dce3SCameron Grant c = sce->channel; 29367b1dce3SCameron Grant if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) { 29467b1dce3SCameron Grant done = 0; 295a527dbc7SCameron Grant snd_mtxlock(d->lock); 29667b1dce3SCameron Grant err = vchan_destroy(c); 297a527dbc7SCameron Grant snd_mtxunlock(d->lock); 29867b1dce3SCameron Grant if (err) 29967b1dce3SCameron Grant device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err); 3009cfd8eb3SPeter Wemm break; /* restart */ 30167b1dce3SCameron Grant } 30267b1dce3SCameron Grant } 30367b1dce3SCameron Grant } 30467b1dce3SCameron Grant } 30567b1dce3SCameron Grant } 30667b1dce3SCameron Grant 30782db23e2SCameron Grant #ifdef USING_DEVFS 30833dbf14aSCameron Grant static int 309cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 31033dbf14aSCameron Grant { 311b8f0d9e0SCameron Grant struct snddev_info *d; 31233dbf14aSCameron Grant int error, unit; 31333dbf14aSCameron Grant 31433dbf14aSCameron Grant unit = snd_unit; 31533dbf14aSCameron Grant error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 31633dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 31774ffd138SCameron Grant if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 318b8f0d9e0SCameron Grant return EINVAL; 319b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 320faeebea2SCameron Grant if (d == NULL || SLIST_EMPTY(&d->channels)) 321b8f0d9e0SCameron Grant return EINVAL; 32233dbf14aSCameron Grant snd_unit = unit; 32333dbf14aSCameron Grant } 32433dbf14aSCameron Grant return (error); 32533dbf14aSCameron Grant } 326b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 327cd9766c5SCameron Grant 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 32882db23e2SCameron Grant #endif 329987e5972SCameron Grant 330cd9766c5SCameron Grant static int 33167b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 332cd9766c5SCameron Grant { 33367b1dce3SCameron Grant struct snddev_info *d; 33467b1dce3SCameron Grant int i, v, error; 335cd9766c5SCameron Grant 33667b1dce3SCameron Grant v = snd_maxautovchans; 337cd9766c5SCameron Grant error = sysctl_handle_int(oidp, &v, sizeof(v), req); 338cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 33912e524a2SDon Lewis if (v < 0 || v >= SND_MAXVCHANS || pcm_devclass == NULL) 340cd9766c5SCameron Grant return EINVAL; 34167b1dce3SCameron Grant if (v != snd_maxautovchans) { 34267b1dce3SCameron Grant for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 34367b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 34467b1dce3SCameron Grant if (!d) 34567b1dce3SCameron Grant continue; 34667b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 34767b1dce3SCameron Grant } 34867b1dce3SCameron Grant } 34967b1dce3SCameron Grant snd_maxautovchans = v; 350cd9766c5SCameron Grant } 351cd9766c5SCameron Grant return (error); 352cd9766c5SCameron Grant } 35367b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 35467b1dce3SCameron Grant 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 355f637a36cSCameron Grant 356285648f9SCameron Grant struct pcm_channel * 357285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 358987e5972SCameron Grant { 359285648f9SCameron Grant struct pcm_channel *ch; 36033dbf14aSCameron Grant char *dirs; 361a67fe5c1SCameron Grant int err, *pnum; 362987e5972SCameron Grant 363285648f9SCameron Grant switch(dir) { 364285648f9SCameron Grant case PCMDIR_PLAY: 365285648f9SCameron Grant dirs = "play"; 366a67fe5c1SCameron Grant pnum = &d->playcount; 367285648f9SCameron Grant break; 368a67fe5c1SCameron Grant 369285648f9SCameron Grant case PCMDIR_REC: 370285648f9SCameron Grant dirs = "record"; 371a67fe5c1SCameron Grant pnum = &d->reccount; 372285648f9SCameron Grant break; 373a67fe5c1SCameron Grant 374285648f9SCameron Grant case PCMDIR_VIRTUAL: 375285648f9SCameron Grant dirs = "virtual"; 376285648f9SCameron Grant dir = PCMDIR_PLAY; 377a67fe5c1SCameron Grant pnum = &d->vchancount; 378285648f9SCameron Grant break; 379a67fe5c1SCameron Grant 380285648f9SCameron Grant default: 381285648f9SCameron Grant return NULL; 3829c326820SCameron Grant } 383285648f9SCameron Grant 384a163d034SWarner Losh ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 385285648f9SCameron Grant if (!ch) 386285648f9SCameron Grant return NULL; 387285648f9SCameron Grant 388a163d034SWarner Losh ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 389285648f9SCameron Grant if (!ch->methods) { 390285648f9SCameron Grant free(ch, M_DEVBUF); 391a67fe5c1SCameron Grant 392285648f9SCameron Grant return NULL; 393285648f9SCameron Grant } 394285648f9SCameron Grant 39567beb5a5SCameron Grant snd_mtxlock(d->lock); 396a67fe5c1SCameron Grant ch->num = (*pnum)++; 39767beb5a5SCameron Grant snd_mtxunlock(d->lock); 398a67fe5c1SCameron Grant 399285648f9SCameron Grant ch->pid = -1; 400285648f9SCameron Grant ch->parentsnddev = d; 401285648f9SCameron Grant ch->parentchannel = parent; 402436c9b65SScott Long ch->dev = d->dev; 40367beb5a5SCameron Grant snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); 404285648f9SCameron Grant 4050f55ac6cSCameron Grant err = chn_init(ch, devinfo, dir); 4060f55ac6cSCameron Grant if (err) { 407a67fe5c1SCameron Grant device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 408285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 409285648f9SCameron Grant free(ch, M_DEVBUF); 41067beb5a5SCameron Grant snd_mtxlock(d->lock); 411a67fe5c1SCameron Grant (*pnum)--; 41267beb5a5SCameron Grant snd_mtxunlock(d->lock); 413a67fe5c1SCameron Grant 414285648f9SCameron Grant return NULL; 415bbb5bf3dSCameron Grant } 416285648f9SCameron Grant 417285648f9SCameron Grant return ch; 418285648f9SCameron Grant } 419285648f9SCameron Grant 420285648f9SCameron Grant int 421285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 422285648f9SCameron Grant { 423a67fe5c1SCameron Grant struct snddev_info *d; 424285648f9SCameron Grant int err; 425285648f9SCameron Grant 426a67fe5c1SCameron Grant d = ch->parentsnddev; 427285648f9SCameron Grant err = chn_kill(ch); 428285648f9SCameron Grant if (err) { 429a67fe5c1SCameron Grant device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 430285648f9SCameron Grant return err; 431285648f9SCameron Grant } 432285648f9SCameron Grant 433285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 434285648f9SCameron Grant free(ch, M_DEVBUF); 435285648f9SCameron Grant 436285648f9SCameron Grant return 0; 437285648f9SCameron Grant } 438285648f9SCameron Grant 439285648f9SCameron Grant int 4405ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 441285648f9SCameron Grant { 44267b1dce3SCameron Grant struct snddev_channel *sce, *tmp, *after; 4435ee30e27SMathew Kanner int device = device_get_unit(d->dev); 4445ee30e27SMathew Kanner 4455ee30e27SMathew Kanner /* 4465ee30e27SMathew Kanner * Note it's confusing nomenclature. 4475ee30e27SMathew Kanner * dev_t 4485ee30e27SMathew Kanner * device -> pcm_device 4495ee30e27SMathew Kanner * unit -> pcm_channel 4505ee30e27SMathew Kanner * channel -> snddev_channel 4515ee30e27SMathew Kanner * device_t 4525ee30e27SMathew Kanner * unit -> pcm_device 4535ee30e27SMathew Kanner */ 454b8f0d9e0SCameron Grant 455a163d034SWarner Losh sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 456285648f9SCameron Grant if (!sce) { 457285648f9SCameron Grant return ENOMEM; 458285648f9SCameron Grant } 459285648f9SCameron Grant 4607cf0e77aSOrion Hodson snd_mtxlock(d->lock); 461285648f9SCameron Grant sce->channel = ch; 4625ee30e27SMathew Kanner sce->chan_num= d->devcount++; 46367b1dce3SCameron Grant if (SLIST_EMPTY(&d->channels)) { 464285648f9SCameron Grant SLIST_INSERT_HEAD(&d->channels, sce, link); 46567b1dce3SCameron Grant } else { 46667b1dce3SCameron Grant after = NULL; 46767b1dce3SCameron Grant SLIST_FOREACH(tmp, &d->channels, link) { 46867b1dce3SCameron Grant after = tmp; 46967b1dce3SCameron Grant } 47067b1dce3SCameron Grant SLIST_INSERT_AFTER(after, sce, link); 47167b1dce3SCameron Grant } 47267beb5a5SCameron Grant snd_mtxunlock(d->lock); 4735ee30e27SMathew Kanner sce->dsp_devt= make_dev(&dsp_cdevsw, 4745ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), 4755ee30e27SMathew Kanner UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", 4765ee30e27SMathew Kanner device, sce->chan_num); 477285648f9SCameron Grant 4785ee30e27SMathew Kanner sce->dspW_devt= make_dev(&dsp_cdevsw, 4795ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num), 4805ee30e27SMathew Kanner UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", 4815ee30e27SMathew Kanner device, sce->chan_num); 4825ee30e27SMathew Kanner 4835ee30e27SMathew Kanner sce->audio_devt= make_dev(&dsp_cdevsw, 4845ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num), 4855ee30e27SMathew Kanner UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", 4865ee30e27SMathew Kanner device, sce->chan_num); 4875ee30e27SMathew Kanner 488506a5308SCameron Grant if (ch->direction == PCMDIR_REC) 4895ee30e27SMathew Kanner sce->dspr_devt = make_dev(&dsp_cdevsw, 4905ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_DSPREC, 4915ee30e27SMathew Kanner sce->chan_num), UID_ROOT, GID_WHEEL, 4925ee30e27SMathew Kanner 0666, "dspr%d.%d", device, sce->chan_num); 493b8f0d9e0SCameron Grant 49433dbf14aSCameron Grant return 0; 49533dbf14aSCameron Grant } 49633dbf14aSCameron Grant 497285648f9SCameron Grant int 4985ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 49933dbf14aSCameron Grant { 500285648f9SCameron Grant struct snddev_channel *sce; 50145550658SPoul-Henning Kamp #if 0 502a527dbc7SCameron Grant int ourlock; 50333dbf14aSCameron Grant 504a527dbc7SCameron Grant ourlock = 0; 505a527dbc7SCameron Grant if (!mtx_owned(d->lock)) { 50649c5e6e2SCameron Grant snd_mtxlock(d->lock); 507a527dbc7SCameron Grant ourlock = 1; 508a527dbc7SCameron Grant } 50945550658SPoul-Henning Kamp #endif 510a527dbc7SCameron Grant 511285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 512285648f9SCameron Grant if (sce->channel == ch) 513285648f9SCameron Grant goto gotit; 51433dbf14aSCameron Grant } 51545550658SPoul-Henning Kamp #if 0 516a527dbc7SCameron Grant if (ourlock) 51749c5e6e2SCameron Grant snd_mtxunlock(d->lock); 51845550658SPoul-Henning Kamp #endif 519285648f9SCameron Grant return EINVAL; 520285648f9SCameron Grant gotit: 521285648f9SCameron Grant SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 52267beb5a5SCameron Grant 52367beb5a5SCameron Grant if (ch->direction == PCMDIR_REC) 52467beb5a5SCameron Grant d->reccount--; 52567beb5a5SCameron Grant else if (ch->flags & CHN_F_VIRTUAL) 52667beb5a5SCameron Grant d->vchancount--; 52767beb5a5SCameron Grant else 52867beb5a5SCameron Grant d->playcount--; 52967beb5a5SCameron Grant 53045550658SPoul-Henning Kamp #if 0 531a527dbc7SCameron Grant if (ourlock) 53249c5e6e2SCameron Grant snd_mtxunlock(d->lock); 53345550658SPoul-Henning Kamp #endif 53467beb5a5SCameron Grant free(sce, M_DEVBUF); 535285648f9SCameron Grant 536987e5972SCameron Grant return 0; 537987e5972SCameron Grant } 538987e5972SCameron Grant 539987e5972SCameron Grant int 540285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 541285648f9SCameron Grant { 542285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 54367b1dce3SCameron Grant struct pcm_channel *ch; 54467b1dce3SCameron Grant int err; 545285648f9SCameron Grant 546285648f9SCameron Grant ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 547285648f9SCameron Grant if (!ch) { 548285648f9SCameron Grant device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 549285648f9SCameron Grant return ENODEV; 550285648f9SCameron Grant } 551cd9766c5SCameron Grant 5525ee30e27SMathew Kanner err = pcm_chn_add(d, ch); 553285648f9SCameron Grant if (err) { 554285648f9SCameron Grant device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 555285648f9SCameron Grant pcm_chn_destroy(ch); 556cd9766c5SCameron Grant return err; 557cd9766c5SCameron Grant } 558cd9766c5SCameron Grant 55912e524a2SDon Lewis CHN_LOCK(ch); 56061698e0cSAlexander Kabaev if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) && 56161698e0cSAlexander Kabaev ch->direction == PCMDIR_PLAY && d->vchancount == 0) { 562cd9766c5SCameron Grant ch->flags |= CHN_F_BUSY; 563cd9766c5SCameron Grant err = vchan_create(ch); 564cd9766c5SCameron Grant if (err) { 56567b1dce3SCameron Grant ch->flags &= ~CHN_F_BUSY; 56612e524a2SDon Lewis CHN_UNLOCK(ch); 56712e524a2SDon Lewis device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 56812e524a2SDon Lewis return err; 569cd9766c5SCameron Grant } 570285648f9SCameron Grant } 57112e524a2SDon Lewis CHN_UNLOCK(ch); 572285648f9SCameron Grant 573285648f9SCameron Grant return err; 574285648f9SCameron Grant } 575285648f9SCameron Grant 576285648f9SCameron Grant static int 577285648f9SCameron Grant pcm_killchan(device_t dev) 578285648f9SCameron Grant { 579285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 580285648f9SCameron Grant struct snddev_channel *sce; 581e33bee07SOlivier Houchard struct pcm_channel *ch; 582e33bee07SOlivier Houchard int error = 0; 583285648f9SCameron Grant 584285648f9SCameron Grant sce = SLIST_FIRST(&d->channels); 585e33bee07SOlivier Houchard ch = sce->channel; 586285648f9SCameron Grant 5875ee30e27SMathew Kanner error = pcm_chn_remove(d, sce->channel); 588e33bee07SOlivier Houchard if (error) 589e33bee07SOlivier Houchard return (error); 590e33bee07SOlivier Houchard return (pcm_chn_destroy(ch)); 591285648f9SCameron Grant } 592285648f9SCameron Grant 593285648f9SCameron Grant int 594987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 595987e5972SCameron Grant { 59666ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 59749c5e6e2SCameron Grant 59849c5e6e2SCameron Grant snd_mtxlock(d->lock); 599987e5972SCameron Grant strncpy(d->status, str, SND_STATUSLEN); 60049c5e6e2SCameron Grant snd_mtxunlock(d->lock); 601987e5972SCameron Grant return 0; 602987e5972SCameron Grant } 603987e5972SCameron Grant 604987e5972SCameron Grant u_int32_t 605987e5972SCameron Grant pcm_getflags(device_t dev) 606987e5972SCameron Grant { 60766ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 60849c5e6e2SCameron Grant 609987e5972SCameron Grant return d->flags; 610987e5972SCameron Grant } 611987e5972SCameron Grant 612987e5972SCameron Grant void 613987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val) 614987e5972SCameron Grant { 61566ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 616d95502a8SCameron Grant 617987e5972SCameron Grant d->flags = val; 618987e5972SCameron Grant } 619987e5972SCameron Grant 62039004e69SCameron Grant void * 62139004e69SCameron Grant pcm_getdevinfo(device_t dev) 62239004e69SCameron Grant { 62366ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 62449c5e6e2SCameron Grant 62539004e69SCameron Grant return d->devinfo; 62639004e69SCameron Grant } 62739004e69SCameron Grant 628a67fe5c1SCameron Grant unsigned int 629a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 630a67fe5c1SCameron Grant { 631a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 6324e60be34SCameron Grant int sz, x; 633a67fe5c1SCameron Grant 634a67fe5c1SCameron Grant sz = 0; 6354e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 6364e60be34SCameron Grant x = sz; 637a67fe5c1SCameron Grant RANGE(sz, min, max); 6384e60be34SCameron Grant if (x != sz) 6394e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 6404e60be34SCameron Grant x = min; 6414e60be34SCameron Grant while (x < sz) 6424e60be34SCameron Grant x <<= 1; 6434e60be34SCameron Grant if (x > sz) 6444e60be34SCameron Grant x >>= 1; 6454e60be34SCameron Grant if (x != sz) { 6464e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 6474e60be34SCameron Grant sz = x; 6484e60be34SCameron Grant } 6494e60be34SCameron Grant } else { 650a67fe5c1SCameron Grant sz = deflt; 6514e60be34SCameron Grant } 6524e60be34SCameron Grant 653a67fe5c1SCameron Grant d->bufsz = sz; 654a67fe5c1SCameron Grant 655a67fe5c1SCameron Grant return sz; 656a67fe5c1SCameron Grant } 657a67fe5c1SCameron Grant 658987e5972SCameron Grant int 659987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 660987e5972SCameron Grant { 66166ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 662987e5972SCameron Grant 663b8a36395SCameron Grant if (pcm_veto_load) { 664b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 665b8a36395SCameron Grant 666b8a36395SCameron Grant return EINVAL; 667b8a36395SCameron Grant } 668b8a36395SCameron Grant 6692c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 670285648f9SCameron Grant 671cd9766c5SCameron Grant d->flags = 0; 672e4d5b250SCameron Grant d->dev = dev; 673987e5972SCameron Grant d->devinfo = devinfo; 674f637a36cSCameron Grant d->devcount = 0; 675506a5308SCameron Grant d->reccount = 0; 676a67fe5c1SCameron Grant d->playcount = 0; 677f637a36cSCameron Grant d->vchancount = 0; 678d95502a8SCameron Grant d->inprog = 0; 679833f7023SCameron Grant 68045550658SPoul-Henning Kamp SLIST_INIT(&d->channels); 68145550658SPoul-Henning Kamp SLIST_INIT(&d->channels); 68245550658SPoul-Henning Kamp 683d95502a8SCameron Grant if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 684285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 685d95502a8SCameron Grant 686285648f9SCameron Grant d->fakechan = fkchan_setup(dev); 687285648f9SCameron Grant chn_init(d->fakechan, NULL, 0); 688987e5972SCameron Grant 68982db23e2SCameron Grant #ifdef SND_DYNSYSCTL 690cc486d80SJohn Baldwin sysctl_ctx_init(&d->sysctl_tree); 691cc486d80SJohn Baldwin d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 692a3e893e0SJohn Baldwin SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 693cc486d80SJohn Baldwin device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 694a3e893e0SJohn Baldwin if (d->sysctl_tree_top == NULL) { 695cc486d80SJohn Baldwin sysctl_ctx_free(&d->sysctl_tree); 696cc486d80SJohn Baldwin goto no; 697cc486d80SJohn Baldwin } 698a67fe5c1SCameron Grant SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 699a67fe5c1SCameron Grant OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 70082db23e2SCameron Grant #endif 701b8f0d9e0SCameron Grant if (numplay > 0) 70267b1dce3SCameron Grant vchan_initsys(dev); 703cd9766c5SCameron Grant if (numplay == 1) 704cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 705cd9766c5SCameron Grant 70667b1dce3SCameron Grant sndstat_register(dev, d->status, sndstat_prepare_pcm); 707987e5972SCameron Grant return 0; 708987e5972SCameron Grant no: 70949c5e6e2SCameron Grant snd_mtxfree(d->lock); 710987e5972SCameron Grant return ENXIO; 711987e5972SCameron Grant } 712987e5972SCameron Grant 71333dbf14aSCameron Grant int 71433dbf14aSCameron Grant pcm_unregister(device_t dev) 7157c438dbeSCameron Grant { 71666ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 717285648f9SCameron Grant struct snddev_channel *sce; 718a67fe5c1SCameron Grant struct pcm_channel *ch; 7197c438dbeSCameron Grant 72049c5e6e2SCameron Grant snd_mtxlock(d->lock); 721d95502a8SCameron Grant if (d->inprog) { 7225c25132aSGeorge C A Reid device_printf(dev, "unregister: operation in progress\n"); 7235c25132aSGeorge C A Reid snd_mtxunlock(d->lock); 7245c25132aSGeorge C A Reid return EBUSY; 7255c25132aSGeorge C A Reid } 7265c25132aSGeorge C A Reid if (sndstat_busy() != 0) { 7275c25132aSGeorge C A Reid device_printf(dev, "unregister: sndstat busy\n"); 728d95502a8SCameron Grant snd_mtxunlock(d->lock); 729d95502a8SCameron Grant return EBUSY; 730d95502a8SCameron Grant } 7315ee30e27SMathew Kanner 7325ee30e27SMathew Kanner 733285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 734a67fe5c1SCameron Grant ch = sce->channel; 735a67fe5c1SCameron Grant if (ch->refcount > 0) { 73621ed9908SCameron Grant device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 73749c5e6e2SCameron Grant snd_mtxunlock(d->lock); 738285648f9SCameron Grant return EBUSY; 739285648f9SCameron Grant } 740c9b53085SCameron Grant } 7415ee30e27SMathew Kanner 7425ee30e27SMathew Kanner SLIST_FOREACH(sce, &d->channels, link) { 7435ee30e27SMathew Kanner destroy_dev(sce->dsp_devt); 7445ee30e27SMathew Kanner destroy_dev(sce->dspW_devt); 7455ee30e27SMathew Kanner destroy_dev(sce->audio_devt); 7465ee30e27SMathew Kanner if (sce->dspr_devt) 7475ee30e27SMathew Kanner destroy_dev(sce->dspr_devt); 7485ee30e27SMathew Kanner } 7495ee30e27SMathew Kanner 750d95502a8SCameron Grant if (mixer_uninit(dev)) { 7515c25132aSGeorge C A Reid device_printf(dev, "unregister: mixer busy\n"); 75249c5e6e2SCameron Grant snd_mtxunlock(d->lock); 753c9b53085SCameron Grant return EBUSY; 754c9b53085SCameron Grant } 7557c438dbeSCameron Grant 75666ef8af5SCameron Grant #ifdef SND_DYNSYSCTL 75766ef8af5SCameron Grant d->sysctl_tree_top = NULL; 75866ef8af5SCameron Grant sysctl_ctx_free(&d->sysctl_tree); 75966ef8af5SCameron Grant #endif 760faeebea2SCameron Grant while (!SLIST_EMPTY(&d->channels)) 761285648f9SCameron Grant pcm_killchan(dev); 7627c438dbeSCameron Grant 76366ef8af5SCameron Grant chn_kill(d->fakechan); 76466ef8af5SCameron Grant fkchan_kill(d->fakechan); 76582db23e2SCameron Grant 76647172de8SNick Sayer sndstat_unregister(dev); 76745550658SPoul-Henning Kamp snd_mtxunlock(d->lock); 76849c5e6e2SCameron Grant snd_mtxfree(d->lock); 76933dbf14aSCameron Grant return 0; 77033dbf14aSCameron Grant } 7717c438dbeSCameron Grant 77267b1dce3SCameron Grant /************************************************************************/ 77367b1dce3SCameron Grant 77467b1dce3SCameron Grant static int 77567b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 77667b1dce3SCameron Grant { 77767b1dce3SCameron Grant struct snddev_info *d; 77867b1dce3SCameron Grant struct snddev_channel *sce; 77967b1dce3SCameron Grant struct pcm_channel *c; 78067b1dce3SCameron Grant struct pcm_feeder *f; 78167b1dce3SCameron Grant int pc, rc, vc; 78267b1dce3SCameron Grant 78367b1dce3SCameron Grant if (verbose < 1) 78467b1dce3SCameron Grant return 0; 78567b1dce3SCameron Grant 78667b1dce3SCameron Grant d = device_get_softc(dev); 78767b1dce3SCameron Grant if (!d) 78867b1dce3SCameron Grant return ENXIO; 78967b1dce3SCameron Grant 79067b1dce3SCameron Grant snd_mtxlock(d->lock); 79167b1dce3SCameron Grant if (!SLIST_EMPTY(&d->channels)) { 79267b1dce3SCameron Grant pc = rc = vc = 0; 79367b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 79467b1dce3SCameron Grant c = sce->channel; 79567b1dce3SCameron Grant if (c->direction == PCMDIR_PLAY) { 79667b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 79767b1dce3SCameron Grant vc++; 79867b1dce3SCameron Grant else 79967b1dce3SCameron Grant pc++; 80067b1dce3SCameron Grant } else 80167b1dce3SCameron Grant rc++; 80267b1dce3SCameron Grant } 803a67fe5c1SCameron Grant sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 80467b1dce3SCameron Grant (d->flags & SD_F_SIMPLEX)? "" : " duplex", 80567b1dce3SCameron Grant #ifdef USING_DEVFS 80667b1dce3SCameron Grant (device_get_unit(dev) == snd_unit)? " default" : "" 80767b1dce3SCameron Grant #else 80867b1dce3SCameron Grant "" 80967b1dce3SCameron Grant #endif 81067b1dce3SCameron Grant ); 811a527dbc7SCameron Grant 812a527dbc7SCameron Grant if (verbose <= 1) { 813a527dbc7SCameron Grant snd_mtxunlock(d->lock); 814a527dbc7SCameron Grant return 0; 815a527dbc7SCameron Grant } 816a527dbc7SCameron Grant 81767b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 81867b1dce3SCameron Grant c = sce->channel; 819a3285889SCameron Grant sbuf_printf(s, "\n\t"); 820a3285889SCameron Grant 82145550658SPoul-Henning Kamp /* it would be better to indent child channels */ 822a3285889SCameron Grant sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 8234c68642aSCameron Grant sbuf_printf(s, "spd %d", c->speed); 8244c68642aSCameron Grant if (c->speed != sndbuf_getspd(c->bufhard)) 8254c68642aSCameron Grant sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 8264c68642aSCameron Grant sbuf_printf(s, ", fmt 0x%08x", c->format); 8274c68642aSCameron Grant if (c->format != sndbuf_getfmt(c->bufhard)) 8284c68642aSCameron Grant sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 829a527dbc7SCameron Grant sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 83067b1dce3SCameron Grant if (c->pid != -1) 83167b1dce3SCameron Grant sbuf_printf(s, ", pid %d", c->pid); 83267b1dce3SCameron Grant sbuf_printf(s, "\n\t"); 8334c68642aSCameron Grant 834edecdda7SCameron Grant if (c->bufhard != NULL && c->bufsoft != NULL) { 835a3285889SCameron Grant sbuf_printf(s, "interrupts %d, ", c->interrupts); 836a3285889SCameron Grant if (c->direction == PCMDIR_REC) 837a3285889SCameron Grant sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 838a3285889SCameron Grant c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 839a3285889SCameron Grant else 840edecdda7SCameron Grant sbuf_printf(s, "underruns %d, ready %d", 841edecdda7SCameron Grant c->xruns, sndbuf_getready(c->bufsoft)); 842a3285889SCameron Grant sbuf_printf(s, "\n\t"); 843a3285889SCameron Grant } 8444c68642aSCameron Grant 8454c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 8464c68642aSCameron Grant sbuf_printf(s, " -> "); 84767b1dce3SCameron Grant f = c->feeder; 8484c68642aSCameron Grant while (f->source != NULL) 8494c68642aSCameron Grant f = f->source; 8504c68642aSCameron Grant while (f != NULL) { 85167b1dce3SCameron Grant sbuf_printf(s, "%s", f->class->name); 85267b1dce3SCameron Grant if (f->desc->type == FEEDER_FMT) 8534c68642aSCameron Grant sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 85467b1dce3SCameron Grant if (f->desc->type == FEEDER_RATE) 8554c68642aSCameron Grant sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 85667b1dce3SCameron Grant if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 8574c68642aSCameron Grant sbuf_printf(s, "(0x%08x)", f->desc->out); 8584c68642aSCameron Grant sbuf_printf(s, " -> "); 8594c68642aSCameron Grant f = f->parent; 86067b1dce3SCameron Grant } 8614c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 86267b1dce3SCameron Grant } 86367b1dce3SCameron Grant } else 86467b1dce3SCameron Grant sbuf_printf(s, " (mixer only)"); 86567b1dce3SCameron Grant snd_mtxunlock(d->lock); 86667b1dce3SCameron Grant 86767b1dce3SCameron Grant return 0; 86867b1dce3SCameron Grant } 86967b1dce3SCameron Grant 87067b1dce3SCameron Grant /************************************************************************/ 87167b1dce3SCameron Grant 87267b1dce3SCameron Grant #ifdef SND_DYNSYSCTL 87367b1dce3SCameron Grant int 87467b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 87567b1dce3SCameron Grant { 87667b1dce3SCameron Grant struct snddev_info *d; 87767b1dce3SCameron Grant struct snddev_channel *sce; 87867b1dce3SCameron Grant struct pcm_channel *c; 879a527dbc7SCameron Grant int err, newcnt, cnt, busy; 880a527dbc7SCameron Grant int x; 88167b1dce3SCameron Grant 88267b1dce3SCameron Grant d = oidp->oid_arg1; 88367b1dce3SCameron Grant 884a527dbc7SCameron Grant x = pcm_inprog(d, 1); 885a527dbc7SCameron Grant if (x != 1) { 886a527dbc7SCameron Grant printf("x: %d\n", x); 887a527dbc7SCameron Grant pcm_inprog(d, -1); 888a527dbc7SCameron Grant return EINPROGRESS; 889a527dbc7SCameron Grant } 890a527dbc7SCameron Grant 891a527dbc7SCameron Grant busy = 0; 89267b1dce3SCameron Grant cnt = 0; 89367b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 89467b1dce3SCameron Grant c = sce->channel; 89512e524a2SDon Lewis CHN_LOCK(c); 896a527dbc7SCameron Grant if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) { 89767b1dce3SCameron Grant cnt++; 898a527dbc7SCameron Grant if (c->flags & CHN_F_BUSY) 899a527dbc7SCameron Grant busy++; 90067b1dce3SCameron Grant } 90112e524a2SDon Lewis CHN_UNLOCK(c); 902a527dbc7SCameron Grant } 903a527dbc7SCameron Grant 90467b1dce3SCameron Grant newcnt = cnt; 90567b1dce3SCameron Grant 90667b1dce3SCameron Grant err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 907a527dbc7SCameron Grant 90867b1dce3SCameron Grant if (err == 0 && req->newptr != NULL) { 909a527dbc7SCameron Grant 91067b1dce3SCameron Grant if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 911a527dbc7SCameron Grant pcm_inprog(d, -1); 912a527dbc7SCameron Grant return E2BIG; 91367b1dce3SCameron Grant } 91467b1dce3SCameron Grant 91567b1dce3SCameron Grant if (newcnt > cnt) { 91667b1dce3SCameron Grant /* add new vchans - find a parent channel first */ 91767b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 91867b1dce3SCameron Grant c = sce->channel; 91912e524a2SDon Lewis CHN_LOCK(c); 92067b1dce3SCameron Grant /* not a candidate if not a play channel */ 92167b1dce3SCameron Grant if (c->direction != PCMDIR_PLAY) 92212e524a2SDon Lewis goto next; 92367b1dce3SCameron Grant /* not a candidate if a virtual channel */ 92467b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 92512e524a2SDon Lewis goto next; 92667b1dce3SCameron Grant /* not a candidate if it's in use */ 92712e524a2SDon Lewis if (!(c->flags & CHN_F_BUSY) || 92812e524a2SDon Lewis !(SLIST_EMPTY(&c->children))) 92967b1dce3SCameron Grant /* 93012e524a2SDon Lewis * if we get here we're a nonvirtual 93112e524a2SDon Lewis * play channel, and either 93267b1dce3SCameron Grant * 1) not busy 93312e524a2SDon Lewis * 2) busy with children, not directly 93412e524a2SDon Lewis * open 93567b1dce3SCameron Grant * 93667b1dce3SCameron Grant * thus we can add children 93767b1dce3SCameron Grant */ 93867b1dce3SCameron Grant goto addok; 93912e524a2SDon Lewis next: 94012e524a2SDon Lewis CHN_UNLOCK(c); 94167b1dce3SCameron Grant } 942a527dbc7SCameron Grant pcm_inprog(d, -1); 94367b1dce3SCameron Grant return EBUSY; 94467b1dce3SCameron Grant addok: 94567b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 94667b1dce3SCameron Grant while (err == 0 && newcnt > cnt) { 94767b1dce3SCameron Grant err = vchan_create(c); 94867b1dce3SCameron Grant if (err == 0) 94967b1dce3SCameron Grant cnt++; 95067b1dce3SCameron Grant } 95167b1dce3SCameron Grant if (SLIST_EMPTY(&c->children)) 95267b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 95312e524a2SDon Lewis CHN_UNLOCK(c); 95467b1dce3SCameron Grant } else if (newcnt < cnt) { 955a527dbc7SCameron Grant if (busy > newcnt) { 956a527dbc7SCameron Grant printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy); 957a527dbc7SCameron Grant pcm_inprog(d, -1); 958a527dbc7SCameron Grant return EBUSY; 959a527dbc7SCameron Grant } 960a527dbc7SCameron Grant 961a527dbc7SCameron Grant snd_mtxlock(d->lock); 96267b1dce3SCameron Grant while (err == 0 && newcnt < cnt) { 96367b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 96467b1dce3SCameron Grant c = sce->channel; 96512e524a2SDon Lewis CHN_LOCK(c); 96667b1dce3SCameron Grant if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 96767b1dce3SCameron Grant goto remok; 96812e524a2SDon Lewis 96912e524a2SDon Lewis CHN_UNLOCK(c); 97067b1dce3SCameron Grant } 971a527dbc7SCameron Grant snd_mtxunlock(d->lock); 972a527dbc7SCameron Grant pcm_inprog(d, -1); 97367b1dce3SCameron Grant return EINVAL; 97467b1dce3SCameron Grant remok: 97512e524a2SDon Lewis CHN_UNLOCK(c); 97667b1dce3SCameron Grant err = vchan_destroy(c); 97767b1dce3SCameron Grant if (err == 0) 97867b1dce3SCameron Grant cnt--; 97967b1dce3SCameron Grant } 980a527dbc7SCameron Grant snd_mtxunlock(d->lock); 98167b1dce3SCameron Grant } 98267b1dce3SCameron Grant } 983a527dbc7SCameron Grant pcm_inprog(d, -1); 98467b1dce3SCameron Grant return err; 98567b1dce3SCameron Grant } 98667b1dce3SCameron Grant #endif 98767b1dce3SCameron Grant 98867b1dce3SCameron Grant /************************************************************************/ 98967b1dce3SCameron Grant 99033dbf14aSCameron Grant static moduledata_t sndpcm_mod = { 99133dbf14aSCameron Grant "snd_pcm", 992d95502a8SCameron Grant NULL, 99333dbf14aSCameron Grant NULL 99433dbf14aSCameron Grant }; 99533dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 99633dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER); 997