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 8537209180SCameron Grant void 8637209180SCameron Grant snd_mtxfree(void *m) 8737209180SCameron Grant { 8837209180SCameron Grant #ifdef USING_MUTEX 8937209180SCameron Grant struct mtx *mtx = m; 9037209180SCameron Grant 9167beb5a5SCameron Grant /* mtx_assert(mtx, MA_OWNED); */ 9237209180SCameron Grant mtx_destroy(mtx); 9337209180SCameron Grant free(mtx, M_DEVBUF); 9437209180SCameron Grant #endif 9537209180SCameron Grant } 9637209180SCameron Grant 9737209180SCameron Grant void 9837209180SCameron Grant snd_mtxassert(void *m) 9937209180SCameron Grant { 10037209180SCameron Grant #ifdef USING_MUTEX 101f00f162aSCameron Grant #ifdef INVARIANTS 10237209180SCameron Grant struct mtx *mtx = m; 10337209180SCameron Grant 10437209180SCameron Grant mtx_assert(mtx, MA_OWNED); 10537209180SCameron Grant #endif 106f00f162aSCameron Grant #endif 10737209180SCameron Grant } 10867beb5a5SCameron Grant /* 10937209180SCameron Grant void 11037209180SCameron Grant snd_mtxlock(void *m) 11137209180SCameron Grant { 11237209180SCameron Grant #ifdef USING_MUTEX 11337209180SCameron Grant struct mtx *mtx = m; 11437209180SCameron Grant 11537209180SCameron Grant mtx_lock(mtx); 11637209180SCameron Grant #endif 11737209180SCameron Grant } 11837209180SCameron Grant 11937209180SCameron Grant void 12037209180SCameron Grant snd_mtxunlock(void *m) 12137209180SCameron Grant { 12237209180SCameron Grant #ifdef USING_MUTEX 12337209180SCameron Grant struct mtx *mtx = m; 12437209180SCameron Grant 12537209180SCameron Grant mtx_unlock(mtx); 12637209180SCameron Grant #endif 12737209180SCameron Grant } 12867beb5a5SCameron Grant */ 12937209180SCameron Grant int 13037209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 13137209180SCameron Grant { 13237209180SCameron Grant #ifdef USING_MUTEX 13337209180SCameron Grant flags &= INTR_MPSAFE; 13446700f12SPeter Wemm flags |= INTR_TYPE_AV; 13537209180SCameron Grant #else 13646700f12SPeter Wemm flags = INTR_TYPE_AV; 13737209180SCameron Grant #endif 13837209180SCameron Grant return bus_setup_intr(dev, res, flags, hand, param, cookiep); 13937209180SCameron Grant } 14037209180SCameron Grant 141a527dbc7SCameron Grant #ifndef PCM_DEBUG_MTX 14267b1dce3SCameron Grant void 14367b1dce3SCameron Grant pcm_lock(struct snddev_info *d) 14467b1dce3SCameron Grant { 14567b1dce3SCameron Grant snd_mtxlock(d->lock); 14667b1dce3SCameron Grant } 14767b1dce3SCameron Grant 14867b1dce3SCameron Grant void 14967b1dce3SCameron Grant pcm_unlock(struct snddev_info *d) 15067b1dce3SCameron Grant { 15167b1dce3SCameron Grant snd_mtxunlock(d->lock); 15267b1dce3SCameron Grant } 153a527dbc7SCameron Grant #endif 15467b1dce3SCameron Grant 15567b1dce3SCameron Grant struct pcm_channel * 15667b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d) 15767b1dce3SCameron Grant { 15867b1dce3SCameron Grant return d->fakechan; 15967b1dce3SCameron Grant } 16067b1dce3SCameron Grant 161b8f0d9e0SCameron Grant /* return a locked channel */ 162285648f9SCameron Grant struct pcm_channel * 163506a5308SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum) 164285648f9SCameron Grant { 165285648f9SCameron Grant struct pcm_channel *c; 166285648f9SCameron Grant struct snddev_channel *sce; 167f637a36cSCameron Grant int err; 168285648f9SCameron Grant 169b8f0d9e0SCameron Grant snd_mtxassert(d->lock); 170f637a36cSCameron Grant 171f637a36cSCameron Grant /* scan for a free channel */ 172285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 173285648f9SCameron Grant c = sce->channel; 17449c5e6e2SCameron Grant CHN_LOCK(c); 175285648f9SCameron Grant if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 176506a5308SCameron Grant if (chnum == -1 || c->num == chnum) { 177285648f9SCameron Grant c->flags |= CHN_F_BUSY; 178b8f0d9e0SCameron Grant c->pid = pid; 179285648f9SCameron Grant return c; 180285648f9SCameron Grant } 181506a5308SCameron Grant } 18249c5e6e2SCameron Grant CHN_UNLOCK(c); 183285648f9SCameron Grant } 184f637a36cSCameron Grant 185f637a36cSCameron Grant /* no channel available */ 186f637a36cSCameron Grant if (direction == PCMDIR_PLAY) { 18767b1dce3SCameron Grant if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { 188f637a36cSCameron Grant /* try to create a vchan */ 189f637a36cSCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 190f637a36cSCameron Grant c = sce->channel; 19112e524a2SDon Lewis CHN_LOCK(c); 192f637a36cSCameron Grant if (!SLIST_EMPTY(&c->children)) { 193f637a36cSCameron Grant err = vchan_create(c); 19412e524a2SDon Lewis CHN_UNLOCK(c); 195f637a36cSCameron Grant if (!err) 196506a5308SCameron Grant return pcm_chnalloc(d, direction, pid, -1); 197f637a36cSCameron Grant else 198f637a36cSCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 19912e524a2SDon Lewis } else 20012e524a2SDon Lewis CHN_UNLOCK(c); 201f637a36cSCameron Grant } 202f637a36cSCameron Grant } 203f637a36cSCameron Grant } 204f637a36cSCameron Grant 205285648f9SCameron Grant return NULL; 206285648f9SCameron Grant } 207285648f9SCameron Grant 208b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 209285648f9SCameron Grant int 210b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 211285648f9SCameron Grant { 212b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 213285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 214b8f0d9e0SCameron Grant c->pid = -1; 21549c5e6e2SCameron Grant CHN_UNLOCK(c); 216285648f9SCameron Grant return 0; 217285648f9SCameron Grant } 218285648f9SCameron Grant 219285648f9SCameron Grant int 220285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 221285648f9SCameron Grant { 22249c5e6e2SCameron Grant int r; 22349c5e6e2SCameron Grant 224b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 225285648f9SCameron Grant c->refcount += ref; 22649c5e6e2SCameron Grant r = c->refcount; 22749c5e6e2SCameron Grant return r; 228285648f9SCameron Grant } 229285648f9SCameron Grant 23067b1dce3SCameron Grant int 23167b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta) 23267b1dce3SCameron Grant { 233a527dbc7SCameron Grant int r; 234a527dbc7SCameron Grant 235a527dbc7SCameron Grant if (delta == 0) 23667b1dce3SCameron Grant return d->inprog; 237a527dbc7SCameron Grant 238a527dbc7SCameron Grant /* backtrace(); */ 239a527dbc7SCameron Grant pcm_lock(d); 240a527dbc7SCameron Grant d->inprog += delta; 241a527dbc7SCameron Grant r = d->inprog; 242a527dbc7SCameron Grant pcm_unlock(d); 243a527dbc7SCameron Grant return r; 24467b1dce3SCameron Grant } 24567b1dce3SCameron Grant 24667b1dce3SCameron Grant static void 24767b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 24867b1dce3SCameron Grant { 24967b1dce3SCameron Grant struct pcm_channel *c; 25067b1dce3SCameron Grant struct snddev_channel *sce; 25167b1dce3SCameron Grant int err, done; 25267b1dce3SCameron Grant 25367b1dce3SCameron Grant if (num > 0 && d->vchancount == 0) { 25467b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 25567b1dce3SCameron Grant c = sce->channel; 25612e524a2SDon Lewis CHN_LOCK(c); 25767b1dce3SCameron Grant if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) { 25867b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 25967b1dce3SCameron Grant err = vchan_create(c); 26067b1dce3SCameron Grant if (err) { 26167b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 26212e524a2SDon Lewis CHN_UNLOCK(c); 26312e524a2SDon Lewis device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 26412e524a2SDon Lewis } else 26512e524a2SDon Lewis CHN_UNLOCK(c); 26667b1dce3SCameron Grant return; 26767b1dce3SCameron Grant } 26812e524a2SDon Lewis CHN_UNLOCK(c); 26967b1dce3SCameron Grant } 27067b1dce3SCameron Grant } 27167b1dce3SCameron Grant if (num == 0 && d->vchancount > 0) { 27267b1dce3SCameron Grant done = 0; 27367b1dce3SCameron Grant while (!done) { 27467b1dce3SCameron Grant done = 1; 27567b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 27667b1dce3SCameron Grant c = sce->channel; 27767b1dce3SCameron Grant if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) { 27867b1dce3SCameron Grant done = 0; 279a527dbc7SCameron Grant snd_mtxlock(d->lock); 28067b1dce3SCameron Grant err = vchan_destroy(c); 281a527dbc7SCameron Grant snd_mtxunlock(d->lock); 28267b1dce3SCameron Grant if (err) 28367b1dce3SCameron Grant device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err); 2849cfd8eb3SPeter Wemm break; /* restart */ 28567b1dce3SCameron Grant } 28667b1dce3SCameron Grant } 28767b1dce3SCameron Grant } 28867b1dce3SCameron Grant } 28967b1dce3SCameron Grant } 29067b1dce3SCameron Grant 29182db23e2SCameron Grant #ifdef USING_DEVFS 29233dbf14aSCameron Grant static int 293cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 29433dbf14aSCameron Grant { 295b8f0d9e0SCameron Grant struct snddev_info *d; 29633dbf14aSCameron Grant int error, unit; 29733dbf14aSCameron Grant 29833dbf14aSCameron Grant unit = snd_unit; 29933dbf14aSCameron Grant error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 30033dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 30174ffd138SCameron Grant if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 302b8f0d9e0SCameron Grant return EINVAL; 303b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 304faeebea2SCameron Grant if (d == NULL || SLIST_EMPTY(&d->channels)) 305b8f0d9e0SCameron Grant return EINVAL; 30633dbf14aSCameron Grant snd_unit = unit; 30733dbf14aSCameron Grant } 30833dbf14aSCameron Grant return (error); 30933dbf14aSCameron Grant } 310b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 311cd9766c5SCameron Grant 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 31282db23e2SCameron Grant #endif 313987e5972SCameron Grant 314cd9766c5SCameron Grant static int 31567b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 316cd9766c5SCameron Grant { 31767b1dce3SCameron Grant struct snddev_info *d; 31867b1dce3SCameron Grant int i, v, error; 319cd9766c5SCameron Grant 32067b1dce3SCameron Grant v = snd_maxautovchans; 321cd9766c5SCameron Grant error = sysctl_handle_int(oidp, &v, sizeof(v), req); 322cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 32312e524a2SDon Lewis if (v < 0 || v >= SND_MAXVCHANS || pcm_devclass == NULL) 324cd9766c5SCameron Grant return EINVAL; 32567b1dce3SCameron Grant if (v != snd_maxautovchans) { 32667b1dce3SCameron Grant for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 32767b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 32867b1dce3SCameron Grant if (!d) 32967b1dce3SCameron Grant continue; 33067b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 33167b1dce3SCameron Grant } 33267b1dce3SCameron Grant } 33367b1dce3SCameron Grant snd_maxautovchans = v; 334cd9766c5SCameron Grant } 335cd9766c5SCameron Grant return (error); 336cd9766c5SCameron Grant } 33767b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 33867b1dce3SCameron Grant 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 339f637a36cSCameron Grant 340285648f9SCameron Grant struct pcm_channel * 341285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 342987e5972SCameron Grant { 343285648f9SCameron Grant struct pcm_channel *ch; 34433dbf14aSCameron Grant char *dirs; 345a3193a9cSDon Lewis int direction, err, *pnum; 346987e5972SCameron Grant 347285648f9SCameron Grant switch(dir) { 348285648f9SCameron Grant case PCMDIR_PLAY: 349285648f9SCameron Grant dirs = "play"; 350a3193a9cSDon Lewis direction = PCMDIR_PLAY; 351a67fe5c1SCameron Grant pnum = &d->playcount; 352285648f9SCameron Grant break; 353a67fe5c1SCameron Grant 354285648f9SCameron Grant case PCMDIR_REC: 355285648f9SCameron Grant dirs = "record"; 356a3193a9cSDon Lewis direction = PCMDIR_REC; 357a67fe5c1SCameron Grant pnum = &d->reccount; 358285648f9SCameron Grant break; 359a67fe5c1SCameron Grant 360285648f9SCameron Grant case PCMDIR_VIRTUAL: 361285648f9SCameron Grant dirs = "virtual"; 362a3193a9cSDon Lewis direction = PCMDIR_PLAY; 363a67fe5c1SCameron Grant pnum = &d->vchancount; 364285648f9SCameron Grant break; 365a67fe5c1SCameron Grant 366285648f9SCameron Grant default: 367285648f9SCameron Grant return NULL; 3689c326820SCameron Grant } 369285648f9SCameron Grant 370a163d034SWarner Losh ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 371285648f9SCameron Grant if (!ch) 372285648f9SCameron Grant return NULL; 373285648f9SCameron Grant 374a163d034SWarner Losh ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 375285648f9SCameron Grant if (!ch->methods) { 376285648f9SCameron Grant free(ch, M_DEVBUF); 377a67fe5c1SCameron Grant 378285648f9SCameron Grant return NULL; 379285648f9SCameron Grant } 380285648f9SCameron Grant 38167beb5a5SCameron Grant snd_mtxlock(d->lock); 382a67fe5c1SCameron Grant ch->num = (*pnum)++; 38367beb5a5SCameron Grant snd_mtxunlock(d->lock); 384a67fe5c1SCameron Grant 385285648f9SCameron Grant ch->pid = -1; 386285648f9SCameron Grant ch->parentsnddev = d; 387285648f9SCameron Grant ch->parentchannel = parent; 388436c9b65SScott Long ch->dev = d->dev; 38967beb5a5SCameron Grant snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); 390285648f9SCameron Grant 391a3193a9cSDon Lewis err = chn_init(ch, devinfo, dir, direction); 3920f55ac6cSCameron Grant if (err) { 393a67fe5c1SCameron Grant device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 394285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 395285648f9SCameron Grant free(ch, M_DEVBUF); 39667beb5a5SCameron Grant snd_mtxlock(d->lock); 397a67fe5c1SCameron Grant (*pnum)--; 39867beb5a5SCameron Grant snd_mtxunlock(d->lock); 399a67fe5c1SCameron Grant 400285648f9SCameron Grant return NULL; 401bbb5bf3dSCameron Grant } 402285648f9SCameron Grant 403285648f9SCameron Grant return ch; 404285648f9SCameron Grant } 405285648f9SCameron Grant 406285648f9SCameron Grant int 407285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 408285648f9SCameron Grant { 409a67fe5c1SCameron Grant struct snddev_info *d; 410285648f9SCameron Grant int err; 411285648f9SCameron Grant 412a67fe5c1SCameron Grant d = ch->parentsnddev; 413285648f9SCameron Grant err = chn_kill(ch); 414285648f9SCameron Grant if (err) { 415a67fe5c1SCameron Grant device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 416285648f9SCameron Grant return err; 417285648f9SCameron Grant } 418285648f9SCameron 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 4265ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 427285648f9SCameron Grant { 42867b1dce3SCameron Grant struct snddev_channel *sce, *tmp, *after; 4295ee30e27SMathew Kanner int device = device_get_unit(d->dev); 4305ee30e27SMathew Kanner 4315ee30e27SMathew Kanner /* 4325ee30e27SMathew Kanner * Note it's confusing nomenclature. 4335ee30e27SMathew Kanner * dev_t 4345ee30e27SMathew Kanner * device -> pcm_device 4355ee30e27SMathew Kanner * unit -> pcm_channel 4365ee30e27SMathew Kanner * channel -> snddev_channel 4375ee30e27SMathew Kanner * device_t 4385ee30e27SMathew Kanner * unit -> pcm_device 4395ee30e27SMathew Kanner */ 440b8f0d9e0SCameron Grant 441a163d034SWarner Losh sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 442285648f9SCameron Grant if (!sce) { 443285648f9SCameron Grant return ENOMEM; 444285648f9SCameron Grant } 445285648f9SCameron Grant 4467cf0e77aSOrion Hodson snd_mtxlock(d->lock); 447285648f9SCameron Grant sce->channel = ch; 4485ee30e27SMathew Kanner sce->chan_num= d->devcount++; 44967b1dce3SCameron Grant if (SLIST_EMPTY(&d->channels)) { 450285648f9SCameron Grant SLIST_INSERT_HEAD(&d->channels, sce, link); 45167b1dce3SCameron Grant } else { 45267b1dce3SCameron Grant after = NULL; 45367b1dce3SCameron Grant SLIST_FOREACH(tmp, &d->channels, link) { 45467b1dce3SCameron Grant after = tmp; 45567b1dce3SCameron Grant } 45667b1dce3SCameron Grant SLIST_INSERT_AFTER(after, sce, link); 45767b1dce3SCameron Grant } 45867beb5a5SCameron Grant snd_mtxunlock(d->lock); 4595ee30e27SMathew Kanner sce->dsp_devt= make_dev(&dsp_cdevsw, 4605ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), 4615ee30e27SMathew Kanner UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", 4625ee30e27SMathew Kanner device, sce->chan_num); 463285648f9SCameron Grant 4645ee30e27SMathew Kanner sce->dspW_devt= make_dev(&dsp_cdevsw, 4655ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num), 4665ee30e27SMathew Kanner UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", 4675ee30e27SMathew Kanner device, sce->chan_num); 4685ee30e27SMathew Kanner 4695ee30e27SMathew Kanner sce->audio_devt= make_dev(&dsp_cdevsw, 4705ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num), 4715ee30e27SMathew Kanner UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", 4725ee30e27SMathew Kanner device, sce->chan_num); 4735ee30e27SMathew Kanner 474506a5308SCameron Grant if (ch->direction == PCMDIR_REC) 4755ee30e27SMathew Kanner sce->dspr_devt = make_dev(&dsp_cdevsw, 4765ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_DSPREC, 4775ee30e27SMathew Kanner sce->chan_num), UID_ROOT, GID_WHEEL, 4785ee30e27SMathew Kanner 0666, "dspr%d.%d", device, sce->chan_num); 479b8f0d9e0SCameron Grant 48033dbf14aSCameron Grant return 0; 48133dbf14aSCameron Grant } 48233dbf14aSCameron Grant 483285648f9SCameron Grant int 4845ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 48533dbf14aSCameron Grant { 486285648f9SCameron Grant struct snddev_channel *sce; 48745550658SPoul-Henning Kamp #if 0 488a527dbc7SCameron Grant int ourlock; 48933dbf14aSCameron Grant 490a527dbc7SCameron Grant ourlock = 0; 491a527dbc7SCameron Grant if (!mtx_owned(d->lock)) { 49249c5e6e2SCameron Grant snd_mtxlock(d->lock); 493a527dbc7SCameron Grant ourlock = 1; 494a527dbc7SCameron Grant } 49545550658SPoul-Henning Kamp #endif 496a527dbc7SCameron Grant 497285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 498285648f9SCameron Grant if (sce->channel == ch) 499285648f9SCameron Grant goto gotit; 50033dbf14aSCameron Grant } 50145550658SPoul-Henning Kamp #if 0 502a527dbc7SCameron Grant if (ourlock) 50349c5e6e2SCameron Grant snd_mtxunlock(d->lock); 50445550658SPoul-Henning Kamp #endif 505285648f9SCameron Grant return EINVAL; 506285648f9SCameron Grant gotit: 507285648f9SCameron Grant SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 50867beb5a5SCameron Grant 50967beb5a5SCameron Grant if (ch->direction == PCMDIR_REC) 51067beb5a5SCameron Grant d->reccount--; 51167beb5a5SCameron Grant else if (ch->flags & CHN_F_VIRTUAL) 51267beb5a5SCameron Grant d->vchancount--; 51367beb5a5SCameron Grant else 51467beb5a5SCameron Grant d->playcount--; 51567beb5a5SCameron Grant 51645550658SPoul-Henning Kamp #if 0 517a527dbc7SCameron Grant if (ourlock) 51849c5e6e2SCameron Grant snd_mtxunlock(d->lock); 51945550658SPoul-Henning Kamp #endif 52067beb5a5SCameron Grant free(sce, M_DEVBUF); 521285648f9SCameron Grant 522987e5972SCameron Grant return 0; 523987e5972SCameron Grant } 524987e5972SCameron Grant 525987e5972SCameron Grant int 526285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 527285648f9SCameron Grant { 528285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 52967b1dce3SCameron Grant struct pcm_channel *ch; 53067b1dce3SCameron Grant int err; 531285648f9SCameron Grant 532285648f9SCameron Grant ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 533285648f9SCameron Grant if (!ch) { 534285648f9SCameron Grant device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 535285648f9SCameron Grant return ENODEV; 536285648f9SCameron Grant } 537cd9766c5SCameron Grant 5385ee30e27SMathew Kanner err = pcm_chn_add(d, ch); 539285648f9SCameron Grant if (err) { 540285648f9SCameron Grant device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 541285648f9SCameron Grant pcm_chn_destroy(ch); 542cd9766c5SCameron Grant return err; 543cd9766c5SCameron Grant } 544cd9766c5SCameron Grant 54512e524a2SDon Lewis CHN_LOCK(ch); 54661698e0cSAlexander Kabaev if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) && 54761698e0cSAlexander Kabaev ch->direction == PCMDIR_PLAY && d->vchancount == 0) { 548cd9766c5SCameron Grant ch->flags |= CHN_F_BUSY; 549cd9766c5SCameron Grant err = vchan_create(ch); 550cd9766c5SCameron Grant if (err) { 55167b1dce3SCameron Grant ch->flags &= ~CHN_F_BUSY; 55212e524a2SDon Lewis CHN_UNLOCK(ch); 55312e524a2SDon Lewis device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 55412e524a2SDon Lewis return err; 555cd9766c5SCameron Grant } 556285648f9SCameron Grant } 55712e524a2SDon Lewis CHN_UNLOCK(ch); 558285648f9SCameron Grant 559285648f9SCameron Grant return err; 560285648f9SCameron Grant } 561285648f9SCameron Grant 562285648f9SCameron Grant static int 563285648f9SCameron Grant pcm_killchan(device_t dev) 564285648f9SCameron Grant { 565285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 566285648f9SCameron Grant struct snddev_channel *sce; 567e33bee07SOlivier Houchard struct pcm_channel *ch; 568e33bee07SOlivier Houchard int error = 0; 569285648f9SCameron Grant 570285648f9SCameron Grant sce = SLIST_FIRST(&d->channels); 571e33bee07SOlivier Houchard ch = sce->channel; 572285648f9SCameron Grant 5735ee30e27SMathew Kanner error = pcm_chn_remove(d, sce->channel); 574e33bee07SOlivier Houchard if (error) 575e33bee07SOlivier Houchard return (error); 576e33bee07SOlivier Houchard return (pcm_chn_destroy(ch)); 577285648f9SCameron Grant } 578285648f9SCameron Grant 579285648f9SCameron Grant int 580987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 581987e5972SCameron Grant { 58266ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 58349c5e6e2SCameron Grant 58449c5e6e2SCameron Grant snd_mtxlock(d->lock); 585987e5972SCameron Grant strncpy(d->status, str, SND_STATUSLEN); 58649c5e6e2SCameron Grant snd_mtxunlock(d->lock); 587987e5972SCameron Grant return 0; 588987e5972SCameron Grant } 589987e5972SCameron Grant 590987e5972SCameron Grant u_int32_t 591987e5972SCameron Grant pcm_getflags(device_t dev) 592987e5972SCameron Grant { 59366ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 59449c5e6e2SCameron Grant 595987e5972SCameron Grant return d->flags; 596987e5972SCameron Grant } 597987e5972SCameron Grant 598987e5972SCameron Grant void 599987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val) 600987e5972SCameron Grant { 60166ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 602d95502a8SCameron Grant 603987e5972SCameron Grant d->flags = val; 604987e5972SCameron Grant } 605987e5972SCameron Grant 60639004e69SCameron Grant void * 60739004e69SCameron Grant pcm_getdevinfo(device_t dev) 60839004e69SCameron Grant { 60966ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 61049c5e6e2SCameron Grant 61139004e69SCameron Grant return d->devinfo; 61239004e69SCameron Grant } 61339004e69SCameron Grant 614a67fe5c1SCameron Grant unsigned int 615a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 616a67fe5c1SCameron Grant { 617a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 6184e60be34SCameron Grant int sz, x; 619a67fe5c1SCameron Grant 620a67fe5c1SCameron Grant sz = 0; 6214e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 6224e60be34SCameron Grant x = sz; 623a67fe5c1SCameron Grant RANGE(sz, min, max); 6244e60be34SCameron Grant if (x != sz) 6254e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 6264e60be34SCameron Grant x = min; 6274e60be34SCameron Grant while (x < sz) 6284e60be34SCameron Grant x <<= 1; 6294e60be34SCameron Grant if (x > sz) 6304e60be34SCameron Grant x >>= 1; 6314e60be34SCameron Grant if (x != sz) { 6324e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 6334e60be34SCameron Grant sz = x; 6344e60be34SCameron Grant } 6354e60be34SCameron Grant } else { 636a67fe5c1SCameron Grant sz = deflt; 6374e60be34SCameron Grant } 6384e60be34SCameron Grant 639a67fe5c1SCameron Grant d->bufsz = sz; 640a67fe5c1SCameron Grant 641a67fe5c1SCameron Grant return sz; 642a67fe5c1SCameron Grant } 643a67fe5c1SCameron Grant 644987e5972SCameron Grant int 645987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 646987e5972SCameron Grant { 64766ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 648987e5972SCameron Grant 649b8a36395SCameron Grant if (pcm_veto_load) { 650b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 651b8a36395SCameron Grant 652b8a36395SCameron Grant return EINVAL; 653b8a36395SCameron Grant } 654b8a36395SCameron Grant 6552c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 656285648f9SCameron Grant 657cd9766c5SCameron Grant d->flags = 0; 658e4d5b250SCameron Grant d->dev = dev; 659987e5972SCameron Grant d->devinfo = devinfo; 660f637a36cSCameron Grant d->devcount = 0; 661506a5308SCameron Grant d->reccount = 0; 662a67fe5c1SCameron Grant d->playcount = 0; 663f637a36cSCameron Grant d->vchancount = 0; 664d95502a8SCameron Grant d->inprog = 0; 665833f7023SCameron Grant 66645550658SPoul-Henning Kamp SLIST_INIT(&d->channels); 66745550658SPoul-Henning Kamp SLIST_INIT(&d->channels); 66845550658SPoul-Henning Kamp 669d95502a8SCameron Grant if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 670285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 671d95502a8SCameron Grant 672285648f9SCameron Grant d->fakechan = fkchan_setup(dev); 673a3193a9cSDon Lewis chn_init(d->fakechan, NULL, 0, 0); 674987e5972SCameron Grant 67582db23e2SCameron Grant #ifdef SND_DYNSYSCTL 676cc486d80SJohn Baldwin sysctl_ctx_init(&d->sysctl_tree); 677cc486d80SJohn Baldwin d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 678a3e893e0SJohn Baldwin SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 679cc486d80SJohn Baldwin device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 680a3e893e0SJohn Baldwin if (d->sysctl_tree_top == NULL) { 681cc486d80SJohn Baldwin sysctl_ctx_free(&d->sysctl_tree); 682cc486d80SJohn Baldwin goto no; 683cc486d80SJohn Baldwin } 684a67fe5c1SCameron Grant SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 685a67fe5c1SCameron Grant OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 68682db23e2SCameron Grant #endif 687b8f0d9e0SCameron Grant if (numplay > 0) 68867b1dce3SCameron Grant vchan_initsys(dev); 689cd9766c5SCameron Grant if (numplay == 1) 690cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 691cd9766c5SCameron Grant 69267b1dce3SCameron Grant sndstat_register(dev, d->status, sndstat_prepare_pcm); 693987e5972SCameron Grant return 0; 694987e5972SCameron Grant no: 69549c5e6e2SCameron Grant snd_mtxfree(d->lock); 696987e5972SCameron Grant return ENXIO; 697987e5972SCameron Grant } 698987e5972SCameron Grant 69933dbf14aSCameron Grant int 70033dbf14aSCameron Grant pcm_unregister(device_t dev) 7017c438dbeSCameron Grant { 70266ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 703285648f9SCameron Grant struct snddev_channel *sce; 704a67fe5c1SCameron Grant struct pcm_channel *ch; 7057c438dbeSCameron Grant 70649c5e6e2SCameron Grant snd_mtxlock(d->lock); 707d95502a8SCameron Grant if (d->inprog) { 7085c25132aSGeorge C A Reid device_printf(dev, "unregister: operation in progress\n"); 7095c25132aSGeorge C A Reid snd_mtxunlock(d->lock); 7105c25132aSGeorge C A Reid return EBUSY; 7115c25132aSGeorge C A Reid } 7125c25132aSGeorge C A Reid if (sndstat_busy() != 0) { 7135c25132aSGeorge C A Reid device_printf(dev, "unregister: sndstat busy\n"); 714d95502a8SCameron Grant snd_mtxunlock(d->lock); 715d95502a8SCameron Grant return EBUSY; 716d95502a8SCameron Grant } 7175ee30e27SMathew Kanner 7185ee30e27SMathew Kanner 719285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 720a67fe5c1SCameron Grant ch = sce->channel; 721a67fe5c1SCameron Grant if (ch->refcount > 0) { 72221ed9908SCameron Grant device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 72349c5e6e2SCameron Grant snd_mtxunlock(d->lock); 724285648f9SCameron Grant return EBUSY; 725285648f9SCameron Grant } 726c9b53085SCameron Grant } 7275ee30e27SMathew Kanner 7285ee30e27SMathew Kanner SLIST_FOREACH(sce, &d->channels, link) { 7295ee30e27SMathew Kanner destroy_dev(sce->dsp_devt); 7305ee30e27SMathew Kanner destroy_dev(sce->dspW_devt); 7315ee30e27SMathew Kanner destroy_dev(sce->audio_devt); 7325ee30e27SMathew Kanner if (sce->dspr_devt) 7335ee30e27SMathew Kanner destroy_dev(sce->dspr_devt); 7345ee30e27SMathew Kanner } 7355ee30e27SMathew Kanner 736d95502a8SCameron Grant if (mixer_uninit(dev)) { 7375c25132aSGeorge C A Reid device_printf(dev, "unregister: mixer busy\n"); 73849c5e6e2SCameron Grant snd_mtxunlock(d->lock); 739c9b53085SCameron Grant return EBUSY; 740c9b53085SCameron Grant } 7417c438dbeSCameron Grant 74266ef8af5SCameron Grant #ifdef SND_DYNSYSCTL 74366ef8af5SCameron Grant d->sysctl_tree_top = NULL; 74466ef8af5SCameron Grant sysctl_ctx_free(&d->sysctl_tree); 74566ef8af5SCameron Grant #endif 746faeebea2SCameron Grant while (!SLIST_EMPTY(&d->channels)) 747285648f9SCameron Grant pcm_killchan(dev); 7487c438dbeSCameron Grant 74966ef8af5SCameron Grant chn_kill(d->fakechan); 75066ef8af5SCameron Grant fkchan_kill(d->fakechan); 75182db23e2SCameron Grant 75247172de8SNick Sayer sndstat_unregister(dev); 75345550658SPoul-Henning Kamp snd_mtxunlock(d->lock); 75449c5e6e2SCameron Grant snd_mtxfree(d->lock); 75533dbf14aSCameron Grant return 0; 75633dbf14aSCameron Grant } 7577c438dbeSCameron Grant 75867b1dce3SCameron Grant /************************************************************************/ 75967b1dce3SCameron Grant 76067b1dce3SCameron Grant static int 76167b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 76267b1dce3SCameron Grant { 76367b1dce3SCameron Grant struct snddev_info *d; 76467b1dce3SCameron Grant struct snddev_channel *sce; 76567b1dce3SCameron Grant struct pcm_channel *c; 76667b1dce3SCameron Grant struct pcm_feeder *f; 76767b1dce3SCameron Grant int pc, rc, vc; 76867b1dce3SCameron Grant 76967b1dce3SCameron Grant if (verbose < 1) 77067b1dce3SCameron Grant return 0; 77167b1dce3SCameron Grant 77267b1dce3SCameron Grant d = device_get_softc(dev); 77367b1dce3SCameron Grant if (!d) 77467b1dce3SCameron Grant return ENXIO; 77567b1dce3SCameron Grant 77667b1dce3SCameron Grant snd_mtxlock(d->lock); 77767b1dce3SCameron Grant if (!SLIST_EMPTY(&d->channels)) { 77867b1dce3SCameron Grant pc = rc = vc = 0; 77967b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 78067b1dce3SCameron Grant c = sce->channel; 78167b1dce3SCameron Grant if (c->direction == PCMDIR_PLAY) { 78267b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 78367b1dce3SCameron Grant vc++; 78467b1dce3SCameron Grant else 78567b1dce3SCameron Grant pc++; 78667b1dce3SCameron Grant } else 78767b1dce3SCameron Grant rc++; 78867b1dce3SCameron Grant } 789a67fe5c1SCameron Grant sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 79067b1dce3SCameron Grant (d->flags & SD_F_SIMPLEX)? "" : " duplex", 79167b1dce3SCameron Grant #ifdef USING_DEVFS 79267b1dce3SCameron Grant (device_get_unit(dev) == snd_unit)? " default" : "" 79367b1dce3SCameron Grant #else 79467b1dce3SCameron Grant "" 79567b1dce3SCameron Grant #endif 79667b1dce3SCameron Grant ); 797a527dbc7SCameron Grant 798a527dbc7SCameron Grant if (verbose <= 1) { 799a527dbc7SCameron Grant snd_mtxunlock(d->lock); 800a527dbc7SCameron Grant return 0; 801a527dbc7SCameron Grant } 802a527dbc7SCameron Grant 80367b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 80467b1dce3SCameron Grant c = sce->channel; 805a3285889SCameron Grant sbuf_printf(s, "\n\t"); 806a3285889SCameron Grant 80745550658SPoul-Henning Kamp /* it would be better to indent child channels */ 808a3285889SCameron Grant sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 8094c68642aSCameron Grant sbuf_printf(s, "spd %d", c->speed); 8104c68642aSCameron Grant if (c->speed != sndbuf_getspd(c->bufhard)) 8114c68642aSCameron Grant sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 8124c68642aSCameron Grant sbuf_printf(s, ", fmt 0x%08x", c->format); 8134c68642aSCameron Grant if (c->format != sndbuf_getfmt(c->bufhard)) 8144c68642aSCameron Grant sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 815a527dbc7SCameron Grant sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 81667b1dce3SCameron Grant if (c->pid != -1) 81767b1dce3SCameron Grant sbuf_printf(s, ", pid %d", c->pid); 81867b1dce3SCameron Grant sbuf_printf(s, "\n\t"); 8194c68642aSCameron Grant 820edecdda7SCameron Grant if (c->bufhard != NULL && c->bufsoft != NULL) { 821a3285889SCameron Grant sbuf_printf(s, "interrupts %d, ", c->interrupts); 822a3285889SCameron Grant if (c->direction == PCMDIR_REC) 823a3285889SCameron Grant sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 824a3285889SCameron Grant c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 825a3285889SCameron Grant else 826edecdda7SCameron Grant sbuf_printf(s, "underruns %d, ready %d", 827edecdda7SCameron Grant c->xruns, sndbuf_getready(c->bufsoft)); 828a3285889SCameron Grant sbuf_printf(s, "\n\t"); 829a3285889SCameron Grant } 8304c68642aSCameron Grant 8314c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 8324c68642aSCameron Grant sbuf_printf(s, " -> "); 83367b1dce3SCameron Grant f = c->feeder; 8344c68642aSCameron Grant while (f->source != NULL) 8354c68642aSCameron Grant f = f->source; 8364c68642aSCameron Grant while (f != NULL) { 83767b1dce3SCameron Grant sbuf_printf(s, "%s", f->class->name); 83867b1dce3SCameron Grant if (f->desc->type == FEEDER_FMT) 8394c68642aSCameron Grant sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 84067b1dce3SCameron Grant if (f->desc->type == FEEDER_RATE) 8414c68642aSCameron Grant sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 84267b1dce3SCameron Grant if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 8434c68642aSCameron Grant sbuf_printf(s, "(0x%08x)", f->desc->out); 8444c68642aSCameron Grant sbuf_printf(s, " -> "); 8454c68642aSCameron Grant f = f->parent; 84667b1dce3SCameron Grant } 8474c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 84867b1dce3SCameron Grant } 84967b1dce3SCameron Grant } else 85067b1dce3SCameron Grant sbuf_printf(s, " (mixer only)"); 85167b1dce3SCameron Grant snd_mtxunlock(d->lock); 85267b1dce3SCameron Grant 85367b1dce3SCameron Grant return 0; 85467b1dce3SCameron Grant } 85567b1dce3SCameron Grant 85667b1dce3SCameron Grant /************************************************************************/ 85767b1dce3SCameron Grant 85867b1dce3SCameron Grant #ifdef SND_DYNSYSCTL 85967b1dce3SCameron Grant int 86067b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 86167b1dce3SCameron Grant { 86267b1dce3SCameron Grant struct snddev_info *d; 86367b1dce3SCameron Grant struct snddev_channel *sce; 86467b1dce3SCameron Grant struct pcm_channel *c; 865a527dbc7SCameron Grant int err, newcnt, cnt, busy; 866a527dbc7SCameron Grant int x; 86767b1dce3SCameron Grant 86867b1dce3SCameron Grant d = oidp->oid_arg1; 86967b1dce3SCameron Grant 870a527dbc7SCameron Grant x = pcm_inprog(d, 1); 871a527dbc7SCameron Grant if (x != 1) { 872a527dbc7SCameron Grant printf("x: %d\n", x); 873a527dbc7SCameron Grant pcm_inprog(d, -1); 874a527dbc7SCameron Grant return EINPROGRESS; 875a527dbc7SCameron Grant } 876a527dbc7SCameron Grant 877a527dbc7SCameron Grant busy = 0; 87867b1dce3SCameron Grant cnt = 0; 87967b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 88067b1dce3SCameron Grant c = sce->channel; 88112e524a2SDon Lewis CHN_LOCK(c); 882a527dbc7SCameron Grant if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) { 88367b1dce3SCameron Grant cnt++; 884a527dbc7SCameron Grant if (c->flags & CHN_F_BUSY) 885a527dbc7SCameron Grant busy++; 88667b1dce3SCameron Grant } 88712e524a2SDon Lewis CHN_UNLOCK(c); 888a527dbc7SCameron Grant } 889a527dbc7SCameron Grant 89067b1dce3SCameron Grant newcnt = cnt; 89167b1dce3SCameron Grant 89267b1dce3SCameron Grant err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 893a527dbc7SCameron Grant 89467b1dce3SCameron Grant if (err == 0 && req->newptr != NULL) { 895a527dbc7SCameron Grant 89667b1dce3SCameron Grant if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 897a527dbc7SCameron Grant pcm_inprog(d, -1); 898a527dbc7SCameron Grant return E2BIG; 89967b1dce3SCameron Grant } 90067b1dce3SCameron Grant 90167b1dce3SCameron Grant if (newcnt > cnt) { 90267b1dce3SCameron Grant /* add new vchans - find a parent channel first */ 90367b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 90467b1dce3SCameron Grant c = sce->channel; 90512e524a2SDon Lewis CHN_LOCK(c); 90667b1dce3SCameron Grant /* not a candidate if not a play channel */ 90767b1dce3SCameron Grant if (c->direction != PCMDIR_PLAY) 90812e524a2SDon Lewis goto next; 90967b1dce3SCameron Grant /* not a candidate if a virtual channel */ 91067b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 91112e524a2SDon Lewis goto next; 91267b1dce3SCameron Grant /* not a candidate if it's in use */ 91312e524a2SDon Lewis if (!(c->flags & CHN_F_BUSY) || 91412e524a2SDon Lewis !(SLIST_EMPTY(&c->children))) 91567b1dce3SCameron Grant /* 91612e524a2SDon Lewis * if we get here we're a nonvirtual 91712e524a2SDon Lewis * play channel, and either 91867b1dce3SCameron Grant * 1) not busy 91912e524a2SDon Lewis * 2) busy with children, not directly 92012e524a2SDon Lewis * open 92167b1dce3SCameron Grant * 92267b1dce3SCameron Grant * thus we can add children 92367b1dce3SCameron Grant */ 92467b1dce3SCameron Grant goto addok; 92512e524a2SDon Lewis next: 92612e524a2SDon Lewis CHN_UNLOCK(c); 92767b1dce3SCameron Grant } 928a527dbc7SCameron Grant pcm_inprog(d, -1); 92967b1dce3SCameron Grant return EBUSY; 93067b1dce3SCameron Grant addok: 93167b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 93267b1dce3SCameron Grant while (err == 0 && newcnt > cnt) { 93367b1dce3SCameron Grant err = vchan_create(c); 93467b1dce3SCameron Grant if (err == 0) 93567b1dce3SCameron Grant cnt++; 93667b1dce3SCameron Grant } 93767b1dce3SCameron Grant if (SLIST_EMPTY(&c->children)) 93867b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 93912e524a2SDon Lewis CHN_UNLOCK(c); 94067b1dce3SCameron Grant } else if (newcnt < cnt) { 941a527dbc7SCameron Grant if (busy > newcnt) { 942a527dbc7SCameron Grant printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy); 943a527dbc7SCameron Grant pcm_inprog(d, -1); 944a527dbc7SCameron Grant return EBUSY; 945a527dbc7SCameron Grant } 946a527dbc7SCameron Grant 947a527dbc7SCameron Grant snd_mtxlock(d->lock); 94867b1dce3SCameron Grant while (err == 0 && newcnt < cnt) { 94967b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 95067b1dce3SCameron Grant c = sce->channel; 95112e524a2SDon Lewis CHN_LOCK(c); 95267b1dce3SCameron Grant if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 95367b1dce3SCameron Grant goto remok; 95412e524a2SDon Lewis 95512e524a2SDon Lewis CHN_UNLOCK(c); 95667b1dce3SCameron Grant } 957a527dbc7SCameron Grant snd_mtxunlock(d->lock); 958a527dbc7SCameron Grant pcm_inprog(d, -1); 95967b1dce3SCameron Grant return EINVAL; 96067b1dce3SCameron Grant remok: 96112e524a2SDon Lewis CHN_UNLOCK(c); 96267b1dce3SCameron Grant err = vchan_destroy(c); 96367b1dce3SCameron Grant if (err == 0) 96467b1dce3SCameron Grant cnt--; 96567b1dce3SCameron Grant } 966a527dbc7SCameron Grant snd_mtxunlock(d->lock); 96767b1dce3SCameron Grant } 96867b1dce3SCameron Grant } 969a527dbc7SCameron Grant pcm_inprog(d, -1); 97067b1dce3SCameron Grant return err; 97167b1dce3SCameron Grant } 97267b1dce3SCameron Grant #endif 97367b1dce3SCameron Grant 97467b1dce3SCameron Grant /************************************************************************/ 97567b1dce3SCameron Grant 97633dbf14aSCameron Grant static moduledata_t sndpcm_mod = { 97733dbf14aSCameron Grant "snd_pcm", 978d95502a8SCameron Grant NULL, 97933dbf14aSCameron Grant NULL 98033dbf14aSCameron Grant }; 98133dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 98233dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER); 983