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; 7855a38451SDon Lewis mtx_init(m, desc, type, MTX_DEF | MTX_RECURSE); 7937209180SCameron Grant return m; 8037209180SCameron Grant #else 81a983d575SCameron Grant return (void *)0xcafebabe; 8237209180SCameron Grant #endif 8337209180SCameron Grant } 8437209180SCameron Grant 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; 191f637a36cSCameron Grant if (!SLIST_EMPTY(&c->children)) { 192f637a36cSCameron Grant err = vchan_create(c); 193f637a36cSCameron Grant if (!err) 194506a5308SCameron Grant return pcm_chnalloc(d, direction, pid, -1); 195f637a36cSCameron Grant else 196f637a36cSCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 197f637a36cSCameron Grant } 198f637a36cSCameron Grant } 199f637a36cSCameron Grant } 200f637a36cSCameron Grant } 201f637a36cSCameron Grant 202285648f9SCameron Grant return NULL; 203285648f9SCameron Grant } 204285648f9SCameron Grant 205b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 206285648f9SCameron Grant int 207b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 208285648f9SCameron Grant { 209b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 210285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 211b8f0d9e0SCameron Grant c->pid = -1; 21249c5e6e2SCameron Grant CHN_UNLOCK(c); 213285648f9SCameron Grant return 0; 214285648f9SCameron Grant } 215285648f9SCameron Grant 216285648f9SCameron Grant int 217285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 218285648f9SCameron Grant { 21949c5e6e2SCameron Grant int r; 22049c5e6e2SCameron Grant 221b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 222285648f9SCameron Grant c->refcount += ref; 22349c5e6e2SCameron Grant r = c->refcount; 22449c5e6e2SCameron Grant return r; 225285648f9SCameron Grant } 226285648f9SCameron Grant 22767b1dce3SCameron Grant int 22867b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta) 22967b1dce3SCameron Grant { 230a527dbc7SCameron Grant int r; 231a527dbc7SCameron Grant 232a527dbc7SCameron Grant if (delta == 0) 23367b1dce3SCameron Grant return d->inprog; 234a527dbc7SCameron Grant 235a527dbc7SCameron Grant /* backtrace(); */ 236a527dbc7SCameron Grant pcm_lock(d); 237a527dbc7SCameron Grant d->inprog += delta; 238a527dbc7SCameron Grant r = d->inprog; 239a527dbc7SCameron Grant pcm_unlock(d); 240a527dbc7SCameron Grant return r; 24167b1dce3SCameron Grant } 24267b1dce3SCameron Grant 24367b1dce3SCameron Grant static void 24467b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 24567b1dce3SCameron Grant { 24667b1dce3SCameron Grant struct pcm_channel *c; 24767b1dce3SCameron Grant struct snddev_channel *sce; 24867b1dce3SCameron Grant int err, done; 24967b1dce3SCameron Grant 25067b1dce3SCameron Grant if (num > 0 && d->vchancount == 0) { 25167b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 25267b1dce3SCameron Grant c = sce->channel; 25367b1dce3SCameron Grant if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) { 25467b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 25567b1dce3SCameron Grant err = vchan_create(c); 25667b1dce3SCameron Grant if (err) { 25767b1dce3SCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 25867b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 25967b1dce3SCameron Grant } 26067b1dce3SCameron Grant return; 26167b1dce3SCameron Grant } 26267b1dce3SCameron Grant } 26367b1dce3SCameron Grant } 26467b1dce3SCameron Grant if (num == 0 && d->vchancount > 0) { 26567b1dce3SCameron Grant done = 0; 26667b1dce3SCameron Grant while (!done) { 26767b1dce3SCameron Grant done = 1; 26867b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 26967b1dce3SCameron Grant c = sce->channel; 27067b1dce3SCameron Grant if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) { 27167b1dce3SCameron Grant done = 0; 272a527dbc7SCameron Grant snd_mtxlock(d->lock); 27367b1dce3SCameron Grant err = vchan_destroy(c); 274a527dbc7SCameron Grant snd_mtxunlock(d->lock); 27567b1dce3SCameron Grant if (err) 27667b1dce3SCameron Grant device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err); 2779cfd8eb3SPeter Wemm break; /* restart */ 27867b1dce3SCameron Grant } 27967b1dce3SCameron Grant } 28067b1dce3SCameron Grant } 28167b1dce3SCameron Grant } 28267b1dce3SCameron Grant } 28367b1dce3SCameron Grant 28482db23e2SCameron Grant #ifdef USING_DEVFS 28533dbf14aSCameron Grant static int 286cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 28733dbf14aSCameron Grant { 288b8f0d9e0SCameron Grant struct snddev_info *d; 28933dbf14aSCameron Grant int error, unit; 29033dbf14aSCameron Grant 29133dbf14aSCameron Grant unit = snd_unit; 29233dbf14aSCameron Grant error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 29333dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 29474ffd138SCameron Grant if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 295b8f0d9e0SCameron Grant return EINVAL; 296b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 297faeebea2SCameron Grant if (d == NULL || SLIST_EMPTY(&d->channels)) 298b8f0d9e0SCameron Grant return EINVAL; 29933dbf14aSCameron Grant snd_unit = unit; 30033dbf14aSCameron Grant } 30133dbf14aSCameron Grant return (error); 30233dbf14aSCameron Grant } 303b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 304cd9766c5SCameron Grant 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 30582db23e2SCameron Grant #endif 306987e5972SCameron Grant 307cd9766c5SCameron Grant static int 30867b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 309cd9766c5SCameron Grant { 31067b1dce3SCameron Grant struct snddev_info *d; 31167b1dce3SCameron Grant int i, v, error; 312cd9766c5SCameron Grant 31367b1dce3SCameron Grant v = snd_maxautovchans; 314cd9766c5SCameron Grant error = sysctl_handle_int(oidp, &v, sizeof(v), req); 315cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 316cd9766c5SCameron Grant if (v < 0 || v >= SND_MAXVCHANS) 317cd9766c5SCameron Grant return EINVAL; 31867b1dce3SCameron Grant if (v != snd_maxautovchans) { 31967b1dce3SCameron Grant for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 32067b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 32167b1dce3SCameron Grant if (!d) 32267b1dce3SCameron Grant continue; 32367b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 32467b1dce3SCameron Grant } 32567b1dce3SCameron Grant } 32667b1dce3SCameron Grant snd_maxautovchans = v; 327cd9766c5SCameron Grant } 328cd9766c5SCameron Grant return (error); 329cd9766c5SCameron Grant } 33067b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 33167b1dce3SCameron Grant 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 332f637a36cSCameron Grant 333285648f9SCameron Grant struct pcm_channel * 334285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 335987e5972SCameron Grant { 336285648f9SCameron Grant struct pcm_channel *ch; 33733dbf14aSCameron Grant char *dirs; 338a67fe5c1SCameron Grant int err, *pnum; 339987e5972SCameron Grant 340285648f9SCameron Grant switch(dir) { 341285648f9SCameron Grant case PCMDIR_PLAY: 342285648f9SCameron Grant dirs = "play"; 343a67fe5c1SCameron Grant pnum = &d->playcount; 344285648f9SCameron Grant break; 345a67fe5c1SCameron Grant 346285648f9SCameron Grant case PCMDIR_REC: 347285648f9SCameron Grant dirs = "record"; 348a67fe5c1SCameron Grant pnum = &d->reccount; 349285648f9SCameron Grant break; 350a67fe5c1SCameron Grant 351285648f9SCameron Grant case PCMDIR_VIRTUAL: 352285648f9SCameron Grant dirs = "virtual"; 353285648f9SCameron Grant dir = PCMDIR_PLAY; 354a67fe5c1SCameron Grant pnum = &d->vchancount; 355285648f9SCameron Grant break; 356a67fe5c1SCameron Grant 357285648f9SCameron Grant default: 358285648f9SCameron Grant return NULL; 3599c326820SCameron Grant } 360285648f9SCameron Grant 361a163d034SWarner Losh ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 362285648f9SCameron Grant if (!ch) 363285648f9SCameron Grant return NULL; 364285648f9SCameron Grant 365a163d034SWarner Losh ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 366285648f9SCameron Grant if (!ch->methods) { 367285648f9SCameron Grant free(ch, M_DEVBUF); 368a67fe5c1SCameron Grant 369285648f9SCameron Grant return NULL; 370285648f9SCameron Grant } 371285648f9SCameron Grant 37267beb5a5SCameron Grant snd_mtxlock(d->lock); 373a67fe5c1SCameron Grant ch->num = (*pnum)++; 37467beb5a5SCameron Grant snd_mtxunlock(d->lock); 375a67fe5c1SCameron Grant 376285648f9SCameron Grant ch->pid = -1; 377285648f9SCameron Grant ch->parentsnddev = d; 378285648f9SCameron Grant ch->parentchannel = parent; 379436c9b65SScott Long ch->dev = d->dev; 38067beb5a5SCameron Grant snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); 381285648f9SCameron Grant 3820f55ac6cSCameron Grant err = chn_init(ch, devinfo, dir); 3830f55ac6cSCameron Grant if (err) { 384a67fe5c1SCameron Grant device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 385285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 386285648f9SCameron Grant free(ch, M_DEVBUF); 38767beb5a5SCameron Grant snd_mtxlock(d->lock); 388a67fe5c1SCameron Grant (*pnum)--; 38967beb5a5SCameron Grant snd_mtxunlock(d->lock); 390a67fe5c1SCameron Grant 391285648f9SCameron Grant return NULL; 392bbb5bf3dSCameron Grant } 393285648f9SCameron Grant 394285648f9SCameron Grant return ch; 395285648f9SCameron Grant } 396285648f9SCameron Grant 397285648f9SCameron Grant int 398285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 399285648f9SCameron Grant { 400a67fe5c1SCameron Grant struct snddev_info *d; 401285648f9SCameron Grant int err; 402285648f9SCameron Grant 403a67fe5c1SCameron Grant d = ch->parentsnddev; 404285648f9SCameron Grant err = chn_kill(ch); 405285648f9SCameron Grant if (err) { 406a67fe5c1SCameron Grant device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 407285648f9SCameron Grant return err; 408285648f9SCameron Grant } 409285648f9SCameron Grant 410285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 411285648f9SCameron Grant free(ch, M_DEVBUF); 412285648f9SCameron Grant 413285648f9SCameron Grant return 0; 414285648f9SCameron Grant } 415285648f9SCameron Grant 416285648f9SCameron Grant int 4175ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 418285648f9SCameron Grant { 41967b1dce3SCameron Grant struct snddev_channel *sce, *tmp, *after; 4205ee30e27SMathew Kanner int device = device_get_unit(d->dev); 4215ee30e27SMathew Kanner 4225ee30e27SMathew Kanner /* 4235ee30e27SMathew Kanner * Note it's confusing nomenclature. 4245ee30e27SMathew Kanner * dev_t 4255ee30e27SMathew Kanner * device -> pcm_device 4265ee30e27SMathew Kanner * unit -> pcm_channel 4275ee30e27SMathew Kanner * channel -> snddev_channel 4285ee30e27SMathew Kanner * device_t 4295ee30e27SMathew Kanner * unit -> pcm_device 4305ee30e27SMathew Kanner */ 431b8f0d9e0SCameron Grant 432a163d034SWarner Losh sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 433285648f9SCameron Grant if (!sce) { 434285648f9SCameron Grant return ENOMEM; 435285648f9SCameron Grant } 436285648f9SCameron Grant 4377cf0e77aSOrion Hodson snd_mtxlock(d->lock); 438285648f9SCameron Grant sce->channel = ch; 4395ee30e27SMathew Kanner sce->chan_num= d->devcount++; 44067b1dce3SCameron Grant if (SLIST_EMPTY(&d->channels)) { 441285648f9SCameron Grant SLIST_INSERT_HEAD(&d->channels, sce, link); 44267b1dce3SCameron Grant } else { 44367b1dce3SCameron Grant after = NULL; 44467b1dce3SCameron Grant SLIST_FOREACH(tmp, &d->channels, link) { 44567b1dce3SCameron Grant after = tmp; 44667b1dce3SCameron Grant } 44767b1dce3SCameron Grant SLIST_INSERT_AFTER(after, sce, link); 44867b1dce3SCameron Grant } 44967beb5a5SCameron Grant snd_mtxunlock(d->lock); 4505ee30e27SMathew Kanner sce->dsp_devt= make_dev(&dsp_cdevsw, 4515ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), 4525ee30e27SMathew Kanner UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", 4535ee30e27SMathew Kanner device, sce->chan_num); 454285648f9SCameron Grant 4555ee30e27SMathew Kanner sce->dspW_devt= make_dev(&dsp_cdevsw, 4565ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num), 4575ee30e27SMathew Kanner UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", 4585ee30e27SMathew Kanner device, sce->chan_num); 4595ee30e27SMathew Kanner 4605ee30e27SMathew Kanner sce->audio_devt= make_dev(&dsp_cdevsw, 4615ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num), 4625ee30e27SMathew Kanner UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", 4635ee30e27SMathew Kanner device, sce->chan_num); 4645ee30e27SMathew Kanner 465506a5308SCameron Grant if (ch->direction == PCMDIR_REC) 4665ee30e27SMathew Kanner sce->dspr_devt = make_dev(&dsp_cdevsw, 4675ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_DSPREC, 4685ee30e27SMathew Kanner sce->chan_num), UID_ROOT, GID_WHEEL, 4695ee30e27SMathew Kanner 0666, "dspr%d.%d", device, sce->chan_num); 470b8f0d9e0SCameron Grant 47133dbf14aSCameron Grant return 0; 47233dbf14aSCameron Grant } 47333dbf14aSCameron Grant 474285648f9SCameron Grant int 4755ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 47633dbf14aSCameron Grant { 477285648f9SCameron Grant struct snddev_channel *sce; 47845550658SPoul-Henning Kamp #if 0 479a527dbc7SCameron Grant int ourlock; 48033dbf14aSCameron Grant 481a527dbc7SCameron Grant ourlock = 0; 482a527dbc7SCameron Grant if (!mtx_owned(d->lock)) { 48349c5e6e2SCameron Grant snd_mtxlock(d->lock); 484a527dbc7SCameron Grant ourlock = 1; 485a527dbc7SCameron Grant } 48645550658SPoul-Henning Kamp #endif 487a527dbc7SCameron Grant 488285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 489285648f9SCameron Grant if (sce->channel == ch) 490285648f9SCameron Grant goto gotit; 49133dbf14aSCameron Grant } 49245550658SPoul-Henning Kamp #if 0 493a527dbc7SCameron Grant if (ourlock) 49449c5e6e2SCameron Grant snd_mtxunlock(d->lock); 49545550658SPoul-Henning Kamp #endif 496285648f9SCameron Grant return EINVAL; 497285648f9SCameron Grant gotit: 498285648f9SCameron Grant SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 49967beb5a5SCameron Grant 50067beb5a5SCameron Grant if (ch->direction == PCMDIR_REC) 50167beb5a5SCameron Grant d->reccount--; 50267beb5a5SCameron Grant else if (ch->flags & CHN_F_VIRTUAL) 50367beb5a5SCameron Grant d->vchancount--; 50467beb5a5SCameron Grant else 50567beb5a5SCameron Grant d->playcount--; 50667beb5a5SCameron Grant 50745550658SPoul-Henning Kamp #if 0 508a527dbc7SCameron Grant if (ourlock) 50949c5e6e2SCameron Grant snd_mtxunlock(d->lock); 51045550658SPoul-Henning Kamp #endif 51167beb5a5SCameron Grant free(sce, M_DEVBUF); 512285648f9SCameron Grant 513987e5972SCameron Grant return 0; 514987e5972SCameron Grant } 515987e5972SCameron Grant 516987e5972SCameron Grant int 517285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 518285648f9SCameron Grant { 519285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 52067b1dce3SCameron Grant struct pcm_channel *ch; 52167b1dce3SCameron Grant int err; 522285648f9SCameron Grant 523285648f9SCameron Grant ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 524285648f9SCameron Grant if (!ch) { 525285648f9SCameron Grant device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 526285648f9SCameron Grant return ENODEV; 527285648f9SCameron Grant } 528cd9766c5SCameron Grant 5295ee30e27SMathew Kanner err = pcm_chn_add(d, ch); 530285648f9SCameron Grant if (err) { 531285648f9SCameron Grant device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 53267beb5a5SCameron Grant snd_mtxunlock(d->lock); 533285648f9SCameron Grant pcm_chn_destroy(ch); 534cd9766c5SCameron Grant return err; 535cd9766c5SCameron Grant } 536cd9766c5SCameron Grant 53761698e0cSAlexander Kabaev if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) && 53861698e0cSAlexander Kabaev ch->direction == PCMDIR_PLAY && d->vchancount == 0) { 539cd9766c5SCameron Grant ch->flags |= CHN_F_BUSY; 540cd9766c5SCameron Grant err = vchan_create(ch); 541cd9766c5SCameron Grant if (err) { 54267b1dce3SCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 54367b1dce3SCameron Grant ch->flags &= ~CHN_F_BUSY; 544cd9766c5SCameron Grant } 545285648f9SCameron Grant } 546285648f9SCameron Grant 547285648f9SCameron Grant return err; 548285648f9SCameron Grant } 549285648f9SCameron Grant 550285648f9SCameron Grant static int 551285648f9SCameron Grant pcm_killchan(device_t dev) 552285648f9SCameron Grant { 553285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 554285648f9SCameron Grant struct snddev_channel *sce; 555e33bee07SOlivier Houchard struct pcm_channel *ch; 556e33bee07SOlivier Houchard int error = 0; 557285648f9SCameron Grant 558285648f9SCameron Grant sce = SLIST_FIRST(&d->channels); 559e33bee07SOlivier Houchard ch = sce->channel; 560285648f9SCameron Grant 5615ee30e27SMathew Kanner error = pcm_chn_remove(d, sce->channel); 562e33bee07SOlivier Houchard if (error) 563e33bee07SOlivier Houchard return (error); 564e33bee07SOlivier Houchard return (pcm_chn_destroy(ch)); 565285648f9SCameron Grant } 566285648f9SCameron Grant 567285648f9SCameron Grant int 568987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 569987e5972SCameron Grant { 57066ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 57149c5e6e2SCameron Grant 57249c5e6e2SCameron Grant snd_mtxlock(d->lock); 573987e5972SCameron Grant strncpy(d->status, str, SND_STATUSLEN); 57449c5e6e2SCameron Grant snd_mtxunlock(d->lock); 575987e5972SCameron Grant return 0; 576987e5972SCameron Grant } 577987e5972SCameron Grant 578987e5972SCameron Grant u_int32_t 579987e5972SCameron Grant pcm_getflags(device_t dev) 580987e5972SCameron Grant { 58166ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 58249c5e6e2SCameron Grant 583987e5972SCameron Grant return d->flags; 584987e5972SCameron Grant } 585987e5972SCameron Grant 586987e5972SCameron Grant void 587987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val) 588987e5972SCameron Grant { 58966ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 590d95502a8SCameron Grant 591987e5972SCameron Grant d->flags = val; 592987e5972SCameron Grant } 593987e5972SCameron Grant 59439004e69SCameron Grant void * 59539004e69SCameron Grant pcm_getdevinfo(device_t dev) 59639004e69SCameron Grant { 59766ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 59849c5e6e2SCameron Grant 59939004e69SCameron Grant return d->devinfo; 60039004e69SCameron Grant } 60139004e69SCameron Grant 602a67fe5c1SCameron Grant unsigned int 603a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 604a67fe5c1SCameron Grant { 605a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 6064e60be34SCameron Grant int sz, x; 607a67fe5c1SCameron Grant 608a67fe5c1SCameron Grant sz = 0; 6094e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 6104e60be34SCameron Grant x = sz; 611a67fe5c1SCameron Grant RANGE(sz, min, max); 6124e60be34SCameron Grant if (x != sz) 6134e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 6144e60be34SCameron Grant x = min; 6154e60be34SCameron Grant while (x < sz) 6164e60be34SCameron Grant x <<= 1; 6174e60be34SCameron Grant if (x > sz) 6184e60be34SCameron Grant x >>= 1; 6194e60be34SCameron Grant if (x != sz) { 6204e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 6214e60be34SCameron Grant sz = x; 6224e60be34SCameron Grant } 6234e60be34SCameron Grant } else { 624a67fe5c1SCameron Grant sz = deflt; 6254e60be34SCameron Grant } 6264e60be34SCameron Grant 627a67fe5c1SCameron Grant d->bufsz = sz; 628a67fe5c1SCameron Grant 629a67fe5c1SCameron Grant return sz; 630a67fe5c1SCameron Grant } 631a67fe5c1SCameron Grant 632987e5972SCameron Grant int 633987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 634987e5972SCameron Grant { 63566ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 636987e5972SCameron Grant 637b8a36395SCameron Grant if (pcm_veto_load) { 638b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 639b8a36395SCameron Grant 640b8a36395SCameron Grant return EINVAL; 641b8a36395SCameron Grant } 642b8a36395SCameron Grant 6432c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 644285648f9SCameron Grant 645cd9766c5SCameron Grant d->flags = 0; 646e4d5b250SCameron Grant d->dev = dev; 647987e5972SCameron Grant d->devinfo = devinfo; 648f637a36cSCameron Grant d->devcount = 0; 649506a5308SCameron Grant d->reccount = 0; 650a67fe5c1SCameron Grant d->playcount = 0; 651f637a36cSCameron Grant d->vchancount = 0; 652d95502a8SCameron Grant d->inprog = 0; 653833f7023SCameron Grant 65445550658SPoul-Henning Kamp SLIST_INIT(&d->channels); 65545550658SPoul-Henning Kamp SLIST_INIT(&d->channels); 65645550658SPoul-Henning Kamp 657d95502a8SCameron Grant if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 658285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 659d95502a8SCameron Grant 660285648f9SCameron Grant d->fakechan = fkchan_setup(dev); 661285648f9SCameron Grant chn_init(d->fakechan, NULL, 0); 662987e5972SCameron Grant 66382db23e2SCameron Grant #ifdef SND_DYNSYSCTL 664cc486d80SJohn Baldwin sysctl_ctx_init(&d->sysctl_tree); 665cc486d80SJohn Baldwin d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 666a3e893e0SJohn Baldwin SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 667cc486d80SJohn Baldwin device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 668a3e893e0SJohn Baldwin if (d->sysctl_tree_top == NULL) { 669cc486d80SJohn Baldwin sysctl_ctx_free(&d->sysctl_tree); 670cc486d80SJohn Baldwin goto no; 671cc486d80SJohn Baldwin } 672a67fe5c1SCameron Grant SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 673a67fe5c1SCameron Grant OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 67482db23e2SCameron Grant #endif 675b8f0d9e0SCameron Grant if (numplay > 0) 67667b1dce3SCameron Grant vchan_initsys(dev); 677cd9766c5SCameron Grant if (numplay == 1) 678cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 679cd9766c5SCameron Grant 68067b1dce3SCameron Grant sndstat_register(dev, d->status, sndstat_prepare_pcm); 681987e5972SCameron Grant return 0; 682987e5972SCameron Grant no: 68349c5e6e2SCameron Grant snd_mtxfree(d->lock); 684987e5972SCameron Grant return ENXIO; 685987e5972SCameron Grant } 686987e5972SCameron Grant 68733dbf14aSCameron Grant int 68833dbf14aSCameron Grant pcm_unregister(device_t dev) 6897c438dbeSCameron Grant { 69066ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 691285648f9SCameron Grant struct snddev_channel *sce; 692a67fe5c1SCameron Grant struct pcm_channel *ch; 6937c438dbeSCameron Grant 69449c5e6e2SCameron Grant snd_mtxlock(d->lock); 695d95502a8SCameron Grant if (d->inprog) { 6965c25132aSGeorge C A Reid device_printf(dev, "unregister: operation in progress\n"); 6975c25132aSGeorge C A Reid snd_mtxunlock(d->lock); 6985c25132aSGeorge C A Reid return EBUSY; 6995c25132aSGeorge C A Reid } 7005c25132aSGeorge C A Reid if (sndstat_busy() != 0) { 7015c25132aSGeorge C A Reid device_printf(dev, "unregister: sndstat busy\n"); 702d95502a8SCameron Grant snd_mtxunlock(d->lock); 703d95502a8SCameron Grant return EBUSY; 704d95502a8SCameron Grant } 7055ee30e27SMathew Kanner 7065ee30e27SMathew Kanner 707285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 708a67fe5c1SCameron Grant ch = sce->channel; 709a67fe5c1SCameron Grant if (ch->refcount > 0) { 71021ed9908SCameron Grant device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 71149c5e6e2SCameron Grant snd_mtxunlock(d->lock); 712285648f9SCameron Grant return EBUSY; 713285648f9SCameron Grant } 714c9b53085SCameron Grant } 7155ee30e27SMathew Kanner 7165ee30e27SMathew Kanner SLIST_FOREACH(sce, &d->channels, link) { 7175ee30e27SMathew Kanner destroy_dev(sce->dsp_devt); 7185ee30e27SMathew Kanner destroy_dev(sce->dspW_devt); 7195ee30e27SMathew Kanner destroy_dev(sce->audio_devt); 7205ee30e27SMathew Kanner if (sce->dspr_devt) 7215ee30e27SMathew Kanner destroy_dev(sce->dspr_devt); 7225ee30e27SMathew Kanner } 7235ee30e27SMathew Kanner 724d95502a8SCameron Grant if (mixer_uninit(dev)) { 7255c25132aSGeorge C A Reid device_printf(dev, "unregister: mixer busy\n"); 72649c5e6e2SCameron Grant snd_mtxunlock(d->lock); 727c9b53085SCameron Grant return EBUSY; 728c9b53085SCameron Grant } 7297c438dbeSCameron Grant 73066ef8af5SCameron Grant #ifdef SND_DYNSYSCTL 73166ef8af5SCameron Grant d->sysctl_tree_top = NULL; 73266ef8af5SCameron Grant sysctl_ctx_free(&d->sysctl_tree); 73366ef8af5SCameron Grant #endif 734faeebea2SCameron Grant while (!SLIST_EMPTY(&d->channels)) 735285648f9SCameron Grant pcm_killchan(dev); 7367c438dbeSCameron Grant 73766ef8af5SCameron Grant chn_kill(d->fakechan); 73866ef8af5SCameron Grant fkchan_kill(d->fakechan); 73982db23e2SCameron Grant 74047172de8SNick Sayer sndstat_unregister(dev); 74145550658SPoul-Henning Kamp snd_mtxunlock(d->lock); 74249c5e6e2SCameron Grant snd_mtxfree(d->lock); 74333dbf14aSCameron Grant return 0; 74433dbf14aSCameron Grant } 7457c438dbeSCameron Grant 74667b1dce3SCameron Grant /************************************************************************/ 74767b1dce3SCameron Grant 74867b1dce3SCameron Grant static int 74967b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 75067b1dce3SCameron Grant { 75167b1dce3SCameron Grant struct snddev_info *d; 75267b1dce3SCameron Grant struct snddev_channel *sce; 75367b1dce3SCameron Grant struct pcm_channel *c; 75467b1dce3SCameron Grant struct pcm_feeder *f; 75567b1dce3SCameron Grant int pc, rc, vc; 75667b1dce3SCameron Grant 75767b1dce3SCameron Grant if (verbose < 1) 75867b1dce3SCameron Grant return 0; 75967b1dce3SCameron Grant 76067b1dce3SCameron Grant d = device_get_softc(dev); 76167b1dce3SCameron Grant if (!d) 76267b1dce3SCameron Grant return ENXIO; 76367b1dce3SCameron Grant 76467b1dce3SCameron Grant snd_mtxlock(d->lock); 76567b1dce3SCameron Grant if (!SLIST_EMPTY(&d->channels)) { 76667b1dce3SCameron Grant pc = rc = vc = 0; 76767b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 76867b1dce3SCameron Grant c = sce->channel; 76967b1dce3SCameron Grant if (c->direction == PCMDIR_PLAY) { 77067b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 77167b1dce3SCameron Grant vc++; 77267b1dce3SCameron Grant else 77367b1dce3SCameron Grant pc++; 77467b1dce3SCameron Grant } else 77567b1dce3SCameron Grant rc++; 77667b1dce3SCameron Grant } 777a67fe5c1SCameron Grant sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 77867b1dce3SCameron Grant (d->flags & SD_F_SIMPLEX)? "" : " duplex", 77967b1dce3SCameron Grant #ifdef USING_DEVFS 78067b1dce3SCameron Grant (device_get_unit(dev) == snd_unit)? " default" : "" 78167b1dce3SCameron Grant #else 78267b1dce3SCameron Grant "" 78367b1dce3SCameron Grant #endif 78467b1dce3SCameron Grant ); 785a527dbc7SCameron Grant 786a527dbc7SCameron Grant if (verbose <= 1) { 787a527dbc7SCameron Grant snd_mtxunlock(d->lock); 788a527dbc7SCameron Grant return 0; 789a527dbc7SCameron Grant } 790a527dbc7SCameron Grant 79167b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 79267b1dce3SCameron Grant c = sce->channel; 793a3285889SCameron Grant sbuf_printf(s, "\n\t"); 794a3285889SCameron Grant 79545550658SPoul-Henning Kamp /* it would be better to indent child channels */ 796a3285889SCameron Grant sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 7974c68642aSCameron Grant sbuf_printf(s, "spd %d", c->speed); 7984c68642aSCameron Grant if (c->speed != sndbuf_getspd(c->bufhard)) 7994c68642aSCameron Grant sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 8004c68642aSCameron Grant sbuf_printf(s, ", fmt 0x%08x", c->format); 8014c68642aSCameron Grant if (c->format != sndbuf_getfmt(c->bufhard)) 8024c68642aSCameron Grant sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 803a527dbc7SCameron Grant sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 80467b1dce3SCameron Grant if (c->pid != -1) 80567b1dce3SCameron Grant sbuf_printf(s, ", pid %d", c->pid); 80667b1dce3SCameron Grant sbuf_printf(s, "\n\t"); 8074c68642aSCameron Grant 808edecdda7SCameron Grant if (c->bufhard != NULL && c->bufsoft != NULL) { 809a3285889SCameron Grant sbuf_printf(s, "interrupts %d, ", c->interrupts); 810a3285889SCameron Grant if (c->direction == PCMDIR_REC) 811a3285889SCameron Grant sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 812a3285889SCameron Grant c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 813a3285889SCameron Grant else 814edecdda7SCameron Grant sbuf_printf(s, "underruns %d, ready %d", 815edecdda7SCameron Grant c->xruns, sndbuf_getready(c->bufsoft)); 816a3285889SCameron Grant sbuf_printf(s, "\n\t"); 817a3285889SCameron Grant } 8184c68642aSCameron Grant 8194c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 8204c68642aSCameron Grant sbuf_printf(s, " -> "); 82167b1dce3SCameron Grant f = c->feeder; 8224c68642aSCameron Grant while (f->source != NULL) 8234c68642aSCameron Grant f = f->source; 8244c68642aSCameron Grant while (f != NULL) { 82567b1dce3SCameron Grant sbuf_printf(s, "%s", f->class->name); 82667b1dce3SCameron Grant if (f->desc->type == FEEDER_FMT) 8274c68642aSCameron Grant sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 82867b1dce3SCameron Grant if (f->desc->type == FEEDER_RATE) 8294c68642aSCameron Grant sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 83067b1dce3SCameron Grant if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 8314c68642aSCameron Grant sbuf_printf(s, "(0x%08x)", f->desc->out); 8324c68642aSCameron Grant sbuf_printf(s, " -> "); 8334c68642aSCameron Grant f = f->parent; 83467b1dce3SCameron Grant } 8354c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 83667b1dce3SCameron Grant } 83767b1dce3SCameron Grant } else 83867b1dce3SCameron Grant sbuf_printf(s, " (mixer only)"); 83967b1dce3SCameron Grant snd_mtxunlock(d->lock); 84067b1dce3SCameron Grant 84167b1dce3SCameron Grant return 0; 84267b1dce3SCameron Grant } 84367b1dce3SCameron Grant 84467b1dce3SCameron Grant /************************************************************************/ 84567b1dce3SCameron Grant 84667b1dce3SCameron Grant #ifdef SND_DYNSYSCTL 84767b1dce3SCameron Grant int 84867b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 84967b1dce3SCameron Grant { 85067b1dce3SCameron Grant struct snddev_info *d; 85167b1dce3SCameron Grant struct snddev_channel *sce; 85267b1dce3SCameron Grant struct pcm_channel *c; 853a527dbc7SCameron Grant int err, newcnt, cnt, busy; 854a527dbc7SCameron Grant int x; 85567b1dce3SCameron Grant 85667b1dce3SCameron Grant d = oidp->oid_arg1; 85767b1dce3SCameron Grant 858a527dbc7SCameron Grant x = pcm_inprog(d, 1); 859a527dbc7SCameron Grant if (x != 1) { 860a527dbc7SCameron Grant printf("x: %d\n", x); 861a527dbc7SCameron Grant pcm_inprog(d, -1); 862a527dbc7SCameron Grant return EINPROGRESS; 863a527dbc7SCameron Grant } 864a527dbc7SCameron Grant 865a527dbc7SCameron Grant busy = 0; 86667b1dce3SCameron Grant cnt = 0; 86767b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 86867b1dce3SCameron Grant c = sce->channel; 869a527dbc7SCameron Grant if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) { 87067b1dce3SCameron Grant cnt++; 871a527dbc7SCameron Grant if (c->flags & CHN_F_BUSY) 872a527dbc7SCameron Grant busy++; 87367b1dce3SCameron Grant } 874a527dbc7SCameron Grant } 875a527dbc7SCameron Grant 87667b1dce3SCameron Grant newcnt = cnt; 87767b1dce3SCameron Grant 87867b1dce3SCameron Grant err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 879a527dbc7SCameron Grant 88067b1dce3SCameron Grant if (err == 0 && req->newptr != NULL) { 881a527dbc7SCameron Grant 88267b1dce3SCameron Grant if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 883a527dbc7SCameron Grant pcm_inprog(d, -1); 884a527dbc7SCameron Grant return E2BIG; 88567b1dce3SCameron Grant } 88667b1dce3SCameron Grant 88767b1dce3SCameron Grant if (newcnt > cnt) { 88867b1dce3SCameron Grant /* add new vchans - find a parent channel first */ 88967b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 89067b1dce3SCameron Grant c = sce->channel; 89167b1dce3SCameron Grant /* not a candidate if not a play channel */ 89267b1dce3SCameron Grant if (c->direction != PCMDIR_PLAY) 8939cfd8eb3SPeter Wemm continue; 89467b1dce3SCameron Grant /* not a candidate if a virtual channel */ 89567b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 8969cfd8eb3SPeter Wemm continue; 89767b1dce3SCameron Grant /* not a candidate if it's in use */ 89867b1dce3SCameron Grant if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 8999cfd8eb3SPeter Wemm continue; 90067b1dce3SCameron Grant /* 90167b1dce3SCameron Grant * if we get here we're a nonvirtual play channel, and either 90267b1dce3SCameron Grant * 1) not busy 90367b1dce3SCameron Grant * 2) busy with children, not directly open 90467b1dce3SCameron Grant * 90567b1dce3SCameron Grant * thus we can add children 90667b1dce3SCameron Grant */ 90767b1dce3SCameron Grant goto addok; 90867b1dce3SCameron Grant } 909a527dbc7SCameron Grant pcm_inprog(d, -1); 91067b1dce3SCameron Grant return EBUSY; 91167b1dce3SCameron Grant addok: 91267b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 91367b1dce3SCameron Grant while (err == 0 && newcnt > cnt) { 91467b1dce3SCameron Grant err = vchan_create(c); 91567b1dce3SCameron Grant if (err == 0) 91667b1dce3SCameron Grant cnt++; 91767b1dce3SCameron Grant } 91867b1dce3SCameron Grant if (SLIST_EMPTY(&c->children)) 91967b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 92067b1dce3SCameron Grant } else if (newcnt < cnt) { 921a527dbc7SCameron Grant if (busy > newcnt) { 922a527dbc7SCameron Grant printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy); 923a527dbc7SCameron Grant pcm_inprog(d, -1); 924a527dbc7SCameron Grant return EBUSY; 925a527dbc7SCameron Grant } 926a527dbc7SCameron Grant 927a527dbc7SCameron Grant snd_mtxlock(d->lock); 92867b1dce3SCameron Grant while (err == 0 && newcnt < cnt) { 92967b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 93067b1dce3SCameron Grant c = sce->channel; 93167b1dce3SCameron Grant if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 93267b1dce3SCameron Grant goto remok; 93367b1dce3SCameron Grant } 934a527dbc7SCameron Grant snd_mtxunlock(d->lock); 935a527dbc7SCameron Grant pcm_inprog(d, -1); 93667b1dce3SCameron Grant return EINVAL; 93767b1dce3SCameron Grant remok: 93867b1dce3SCameron Grant err = vchan_destroy(c); 93967b1dce3SCameron Grant if (err == 0) 94067b1dce3SCameron Grant cnt--; 94167b1dce3SCameron Grant } 942a527dbc7SCameron Grant snd_mtxunlock(d->lock); 94367b1dce3SCameron Grant } 94467b1dce3SCameron Grant } 945a527dbc7SCameron Grant pcm_inprog(d, -1); 94667b1dce3SCameron Grant return err; 94767b1dce3SCameron Grant } 94867b1dce3SCameron Grant #endif 94967b1dce3SCameron Grant 95067b1dce3SCameron Grant /************************************************************************/ 95167b1dce3SCameron Grant 95233dbf14aSCameron Grant static moduledata_t sndpcm_mod = { 95333dbf14aSCameron Grant "snd_pcm", 954d95502a8SCameron Grant NULL, 95533dbf14aSCameron Grant NULL 95633dbf14aSCameron Grant }; 95733dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 95833dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER); 959