1098ca2bdSWarner Losh /*- 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 169f637a36cSCameron Grant /* scan for a free channel */ 170285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 171285648f9SCameron Grant c = sce->channel; 17249c5e6e2SCameron Grant CHN_LOCK(c); 173285648f9SCameron Grant if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 174506a5308SCameron Grant if (chnum == -1 || c->num == chnum) { 175285648f9SCameron Grant c->flags |= CHN_F_BUSY; 176b8f0d9e0SCameron Grant c->pid = pid; 177285648f9SCameron Grant return c; 178285648f9SCameron Grant } 179506a5308SCameron Grant } 18049c5e6e2SCameron Grant CHN_UNLOCK(c); 181285648f9SCameron Grant } 182f637a36cSCameron Grant 183f637a36cSCameron Grant /* no channel available */ 1847982e7a4SAriff Abdullah if (direction == PCMDIR_PLAY && d->vchancount > 0 && 1857982e7a4SAriff Abdullah d->vchancount < snd_maxautovchans && 1867982e7a4SAriff Abdullah d->devcount <= PCMMAXCHAN) { 187f637a36cSCameron Grant /* try to create a vchan */ 188f637a36cSCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 189f637a36cSCameron Grant c = sce->channel; 19012e524a2SDon Lewis CHN_LOCK(c); 19197d69a96SAlexander Leidinger if ((c->flags & CHN_F_HAS_VCHAN) && 19297d69a96SAlexander Leidinger !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 204285648f9SCameron Grant return NULL; 205285648f9SCameron Grant } 206285648f9SCameron Grant 207b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 208285648f9SCameron Grant int 209b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 210285648f9SCameron Grant { 211b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 212285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 213b8f0d9e0SCameron Grant c->pid = -1; 21449c5e6e2SCameron Grant CHN_UNLOCK(c); 215285648f9SCameron Grant return 0; 216285648f9SCameron Grant } 217285648f9SCameron Grant 218285648f9SCameron Grant int 219285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 220285648f9SCameron Grant { 22149c5e6e2SCameron Grant int r; 22249c5e6e2SCameron Grant 223b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 224285648f9SCameron Grant c->refcount += ref; 22549c5e6e2SCameron Grant r = c->refcount; 22649c5e6e2SCameron Grant return r; 227285648f9SCameron Grant } 228285648f9SCameron Grant 22967b1dce3SCameron Grant int 23067b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta) 23167b1dce3SCameron Grant { 232a527dbc7SCameron Grant int r; 233a527dbc7SCameron Grant 234a527dbc7SCameron Grant if (delta == 0) 23567b1dce3SCameron Grant return d->inprog; 236a527dbc7SCameron Grant 237a527dbc7SCameron Grant /* backtrace(); */ 238a527dbc7SCameron Grant pcm_lock(d); 239a527dbc7SCameron Grant d->inprog += delta; 240a527dbc7SCameron Grant r = d->inprog; 241a527dbc7SCameron Grant pcm_unlock(d); 242a527dbc7SCameron Grant return r; 24367b1dce3SCameron Grant } 24467b1dce3SCameron Grant 24567b1dce3SCameron Grant static void 24667b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 24767b1dce3SCameron Grant { 24897d69a96SAlexander Leidinger struct pcm_channel *c, *ch; 24967b1dce3SCameron Grant struct snddev_channel *sce; 25067b1dce3SCameron Grant int err, done; 25167b1dce3SCameron Grant 25297d69a96SAlexander Leidinger /* 25397d69a96SAlexander Leidinger * XXX WOAH... NEED SUPER CLEANUP!!! 25497d69a96SAlexander Leidinger * Robust, yet confusing. Understanding these will 25597d69a96SAlexander Leidinger * cause your brain spinning like a Doki Doki Dynamo. 25697d69a96SAlexander Leidinger */ 25767b1dce3SCameron Grant if (num > 0 && d->vchancount == 0) { 25867b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 25967b1dce3SCameron Grant c = sce->channel; 26012e524a2SDon Lewis CHN_LOCK(c); 26197d69a96SAlexander Leidinger if ((c->direction == PCMDIR_PLAY) && 26297d69a96SAlexander Leidinger !(c->flags & CHN_F_BUSY) && 26397d69a96SAlexander Leidinger SLIST_EMPTY(&c->children)) { 26467b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 26567b1dce3SCameron Grant err = vchan_create(c); 26667b1dce3SCameron Grant if (err) { 26767b1dce3SCameron Grant c->flags &= ~CHN_F_BUSY; 26812e524a2SDon Lewis device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 26997d69a96SAlexander Leidinger } 27012e524a2SDon Lewis CHN_UNLOCK(c); 27167b1dce3SCameron Grant return; 27267b1dce3SCameron Grant } 27312e524a2SDon Lewis CHN_UNLOCK(c); 27467b1dce3SCameron Grant } 27597d69a96SAlexander Leidinger return; 27667b1dce3SCameron Grant } 27767b1dce3SCameron Grant if (num == 0 && d->vchancount > 0) { 27897d69a96SAlexander Leidinger /* 27997d69a96SAlexander Leidinger * XXX Keep retrying... 28097d69a96SAlexander Leidinger */ 28197d69a96SAlexander Leidinger for (done = 0; done < 1024; done++) { 28297d69a96SAlexander Leidinger ch = NULL; 28367b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 28467b1dce3SCameron Grant c = sce->channel; 28597d69a96SAlexander Leidinger CHN_LOCK(c); 28697d69a96SAlexander Leidinger if (c->direction == PCMDIR_PLAY && 28797d69a96SAlexander Leidinger !(c->flags & CHN_F_BUSY) && 28897d69a96SAlexander Leidinger (c->flags & CHN_F_VIRTUAL)) { 28997d69a96SAlexander Leidinger ch = c; 29097d69a96SAlexander Leidinger break; 29197d69a96SAlexander Leidinger } 29297d69a96SAlexander Leidinger CHN_UNLOCK(c); 29397d69a96SAlexander Leidinger } 29497d69a96SAlexander Leidinger if (ch != NULL) { 29597d69a96SAlexander Leidinger CHN_UNLOCK(ch); 296a527dbc7SCameron Grant snd_mtxlock(d->lock); 29797d69a96SAlexander Leidinger err = vchan_destroy(ch); 29867b1dce3SCameron Grant if (err) 29997d69a96SAlexander Leidinger device_printf(d->dev, "vchan_destroy(%s) == %d\n", 30097d69a96SAlexander Leidinger ch->name, err); 30197d69a96SAlexander Leidinger snd_mtxunlock(d->lock); 30297d69a96SAlexander Leidinger } else 30397d69a96SAlexander Leidinger return; 30467b1dce3SCameron Grant } 30597d69a96SAlexander Leidinger return; 30667b1dce3SCameron Grant } 30767b1dce3SCameron Grant } 30867b1dce3SCameron Grant 30982db23e2SCameron Grant #ifdef USING_DEVFS 31033dbf14aSCameron Grant static int 311cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 31233dbf14aSCameron Grant { 313b8f0d9e0SCameron Grant struct snddev_info *d; 31433dbf14aSCameron Grant int error, unit; 31533dbf14aSCameron Grant 31633dbf14aSCameron Grant unit = snd_unit; 31733dbf14aSCameron Grant error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 31833dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 31974ffd138SCameron Grant if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 320b8f0d9e0SCameron Grant return EINVAL; 321b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 322faeebea2SCameron Grant if (d == NULL || SLIST_EMPTY(&d->channels)) 323b8f0d9e0SCameron Grant return EINVAL; 32433dbf14aSCameron Grant snd_unit = unit; 32533dbf14aSCameron Grant } 32633dbf14aSCameron Grant return (error); 32733dbf14aSCameron Grant } 328b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 329cd9766c5SCameron Grant 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 33082db23e2SCameron Grant #endif 331987e5972SCameron Grant 332cd9766c5SCameron Grant static int 33367b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 334cd9766c5SCameron Grant { 33567b1dce3SCameron Grant struct snddev_info *d; 33667b1dce3SCameron Grant int i, v, error; 337cd9766c5SCameron Grant 33867b1dce3SCameron Grant v = snd_maxautovchans; 339cd9766c5SCameron Grant error = sysctl_handle_int(oidp, &v, sizeof(v), req); 340cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 3417982e7a4SAriff Abdullah if (v < 0 || v > (PCMMAXCHAN + 1) || pcm_devclass == NULL) 342cd9766c5SCameron Grant return EINVAL; 34367b1dce3SCameron Grant if (v != snd_maxautovchans) { 34467b1dce3SCameron Grant for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 34567b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 34667b1dce3SCameron Grant if (!d) 34767b1dce3SCameron Grant continue; 34897d69a96SAlexander Leidinger if (d->flags & SD_F_AUTOVCHAN) { 34997d69a96SAlexander Leidinger if (pcm_inprog(d, 1) == 1) 35067b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 35197d69a96SAlexander Leidinger pcm_inprog(d, -1); 35297d69a96SAlexander Leidinger } 35367b1dce3SCameron Grant } 35467b1dce3SCameron Grant } 35567b1dce3SCameron Grant snd_maxautovchans = v; 356cd9766c5SCameron Grant } 357cd9766c5SCameron Grant return (error); 358cd9766c5SCameron Grant } 35967b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 36067b1dce3SCameron Grant 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 361f637a36cSCameron Grant 362285648f9SCameron Grant struct pcm_channel * 363285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 364987e5972SCameron Grant { 365285648f9SCameron Grant struct pcm_channel *ch; 36633dbf14aSCameron Grant char *dirs; 367a3193a9cSDon Lewis int direction, err, *pnum; 368987e5972SCameron Grant 369285648f9SCameron Grant switch(dir) { 370285648f9SCameron Grant case PCMDIR_PLAY: 371285648f9SCameron Grant dirs = "play"; 372a3193a9cSDon Lewis direction = PCMDIR_PLAY; 373a67fe5c1SCameron Grant pnum = &d->playcount; 374285648f9SCameron Grant break; 375a67fe5c1SCameron Grant 376285648f9SCameron Grant case PCMDIR_REC: 377285648f9SCameron Grant dirs = "record"; 378a3193a9cSDon Lewis direction = PCMDIR_REC; 379a67fe5c1SCameron Grant pnum = &d->reccount; 380285648f9SCameron Grant break; 381a67fe5c1SCameron Grant 382285648f9SCameron Grant case PCMDIR_VIRTUAL: 383285648f9SCameron Grant dirs = "virtual"; 384a3193a9cSDon Lewis direction = PCMDIR_PLAY; 385a67fe5c1SCameron Grant pnum = &d->vchancount; 386285648f9SCameron Grant break; 387a67fe5c1SCameron Grant 388285648f9SCameron Grant default: 389285648f9SCameron Grant return NULL; 3909c326820SCameron Grant } 391285648f9SCameron Grant 392a163d034SWarner Losh ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 393285648f9SCameron Grant if (!ch) 394285648f9SCameron Grant return NULL; 395285648f9SCameron Grant 396a163d034SWarner Losh ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 397285648f9SCameron Grant if (!ch->methods) { 398285648f9SCameron Grant free(ch, M_DEVBUF); 399a67fe5c1SCameron Grant 400285648f9SCameron Grant return NULL; 401285648f9SCameron Grant } 402285648f9SCameron Grant 40367beb5a5SCameron Grant snd_mtxlock(d->lock); 404a67fe5c1SCameron Grant ch->num = (*pnum)++; 40567beb5a5SCameron Grant snd_mtxunlock(d->lock); 406a67fe5c1SCameron Grant 407285648f9SCameron Grant ch->pid = -1; 408285648f9SCameron Grant ch->parentsnddev = d; 409285648f9SCameron Grant ch->parentchannel = parent; 410436c9b65SScott Long ch->dev = d->dev; 41167beb5a5SCameron Grant snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); 412285648f9SCameron Grant 413a3193a9cSDon Lewis err = chn_init(ch, devinfo, dir, direction); 4140f55ac6cSCameron Grant if (err) { 415a67fe5c1SCameron Grant device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 416285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 417285648f9SCameron Grant free(ch, M_DEVBUF); 41867beb5a5SCameron Grant snd_mtxlock(d->lock); 419a67fe5c1SCameron Grant (*pnum)--; 42067beb5a5SCameron Grant snd_mtxunlock(d->lock); 421a67fe5c1SCameron Grant 422285648f9SCameron Grant return NULL; 423bbb5bf3dSCameron Grant } 424285648f9SCameron Grant 425285648f9SCameron Grant return ch; 426285648f9SCameron Grant } 427285648f9SCameron Grant 428285648f9SCameron Grant int 429285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 430285648f9SCameron Grant { 431a67fe5c1SCameron Grant struct snddev_info *d; 432285648f9SCameron Grant int err; 433285648f9SCameron Grant 434a67fe5c1SCameron Grant d = ch->parentsnddev; 435285648f9SCameron Grant err = chn_kill(ch); 436285648f9SCameron Grant if (err) { 437a67fe5c1SCameron Grant device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 438285648f9SCameron Grant return err; 439285648f9SCameron Grant } 440285648f9SCameron Grant 441285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 442285648f9SCameron Grant free(ch, M_DEVBUF); 443285648f9SCameron Grant 444285648f9SCameron Grant return 0; 445285648f9SCameron Grant } 446285648f9SCameron Grant 447285648f9SCameron Grant int 4485ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 449285648f9SCameron Grant { 45067b1dce3SCameron Grant struct snddev_channel *sce, *tmp, *after; 4515ee30e27SMathew Kanner int device = device_get_unit(d->dev); 4525ee30e27SMathew Kanner 4535ee30e27SMathew Kanner /* 4545ee30e27SMathew Kanner * Note it's confusing nomenclature. 4555ee30e27SMathew Kanner * dev_t 4565ee30e27SMathew Kanner * device -> pcm_device 4575ee30e27SMathew Kanner * unit -> pcm_channel 4585ee30e27SMathew Kanner * channel -> snddev_channel 4595ee30e27SMathew Kanner * device_t 4605ee30e27SMathew Kanner * unit -> pcm_device 4615ee30e27SMathew Kanner */ 462b8f0d9e0SCameron Grant 463a163d034SWarner Losh sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 464285648f9SCameron Grant if (!sce) { 465285648f9SCameron Grant return ENOMEM; 466285648f9SCameron Grant } 467285648f9SCameron Grant 4687cf0e77aSOrion Hodson snd_mtxlock(d->lock); 469285648f9SCameron Grant sce->channel = ch; 47067b1dce3SCameron Grant if (SLIST_EMPTY(&d->channels)) { 471285648f9SCameron Grant SLIST_INSERT_HEAD(&d->channels, sce, link); 4727982e7a4SAriff Abdullah sce->chan_num = 0; 47367b1dce3SCameron Grant } else { 4747982e7a4SAriff Abdullah sce->chan_num = 0; 4757982e7a4SAriff Abdullah retry_search: 4767982e7a4SAriff Abdullah SLIST_FOREACH(tmp, &d->channels, link) { 4777982e7a4SAriff Abdullah if (tmp == NULL) 4787982e7a4SAriff Abdullah continue; 4797982e7a4SAriff Abdullah if (sce->chan_num == tmp->chan_num) { 4807982e7a4SAriff Abdullah sce->chan_num++; 4817982e7a4SAriff Abdullah goto retry_search; 4827982e7a4SAriff Abdullah } 4837982e7a4SAriff Abdullah } 4847982e7a4SAriff Abdullah /* 4857982e7a4SAriff Abdullah * Don't overflow PCMMKMINOR / PCMMAXCHAN. 4867982e7a4SAriff Abdullah */ 4877982e7a4SAriff Abdullah if (sce->chan_num > PCMMAXCHAN) { 4887982e7a4SAriff Abdullah snd_mtxunlock(d->lock); 4897982e7a4SAriff Abdullah device_printf(d->dev, 4907982e7a4SAriff Abdullah "%s: WARNING: sce->chan_num overflow! (%d)\n", 4917982e7a4SAriff Abdullah __func__, sce->chan_num); 4927982e7a4SAriff Abdullah free(sce, M_DEVBUF); 4937982e7a4SAriff Abdullah return E2BIG; 4947982e7a4SAriff Abdullah } 49597d69a96SAlexander Leidinger /* 49697d69a96SAlexander Leidinger * Micro optimization, channel ordering: 49797d69a96SAlexander Leidinger * hw,hw,hw,vch,vch,vch,rec 49897d69a96SAlexander Leidinger */ 49967b1dce3SCameron Grant after = NULL; 50097d69a96SAlexander Leidinger if (ch->flags & CHN_F_VIRTUAL) { 50197d69a96SAlexander Leidinger /* virtual channel to the end */ 50297d69a96SAlexander Leidinger SLIST_FOREACH(tmp, &d->channels, link) { 50397d69a96SAlexander Leidinger if (tmp->channel->direction == PCMDIR_REC) 50497d69a96SAlexander Leidinger break; 50597d69a96SAlexander Leidinger after = tmp; 50697d69a96SAlexander Leidinger } 50797d69a96SAlexander Leidinger } else { 50897d69a96SAlexander Leidinger if (ch->direction == PCMDIR_REC) { 50967b1dce3SCameron Grant SLIST_FOREACH(tmp, &d->channels, link) { 51067b1dce3SCameron Grant after = tmp; 51167b1dce3SCameron Grant } 51297d69a96SAlexander Leidinger } else { 51397d69a96SAlexander Leidinger SLIST_FOREACH(tmp, &d->channels, link) { 51497d69a96SAlexander Leidinger if (tmp->channel->direction == PCMDIR_REC) 51597d69a96SAlexander Leidinger break; 51697d69a96SAlexander Leidinger if (!(tmp->channel->flags & CHN_F_VIRTUAL)) 51797d69a96SAlexander Leidinger after = tmp; 51897d69a96SAlexander Leidinger } 51997d69a96SAlexander Leidinger } 52097d69a96SAlexander Leidinger } 52197d69a96SAlexander Leidinger if (after == NULL) { 52297d69a96SAlexander Leidinger SLIST_INSERT_HEAD(&d->channels, sce, link); 52397d69a96SAlexander Leidinger } else { 52467b1dce3SCameron Grant SLIST_INSERT_AFTER(after, sce, link); 52567b1dce3SCameron Grant } 52697d69a96SAlexander Leidinger } 5277982e7a4SAriff Abdullah d->devcount++; 52867beb5a5SCameron Grant snd_mtxunlock(d->lock); 5295ee30e27SMathew Kanner sce->dsp_devt= make_dev(&dsp_cdevsw, 5305ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), 5315ee30e27SMathew Kanner UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", 5325ee30e27SMathew Kanner device, sce->chan_num); 533285648f9SCameron Grant 5345ee30e27SMathew Kanner sce->dspW_devt= make_dev(&dsp_cdevsw, 5355ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num), 5365ee30e27SMathew Kanner UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", 5375ee30e27SMathew Kanner device, sce->chan_num); 5385ee30e27SMathew Kanner 5395ee30e27SMathew Kanner sce->audio_devt= make_dev(&dsp_cdevsw, 5405ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num), 5415ee30e27SMathew Kanner UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", 5425ee30e27SMathew Kanner device, sce->chan_num); 5435ee30e27SMathew Kanner 544506a5308SCameron Grant if (ch->direction == PCMDIR_REC) 5455ee30e27SMathew Kanner sce->dspr_devt = make_dev(&dsp_cdevsw, 5465ee30e27SMathew Kanner PCMMKMINOR(device, SND_DEV_DSPREC, 5475ee30e27SMathew Kanner sce->chan_num), UID_ROOT, GID_WHEEL, 5485ee30e27SMathew Kanner 0666, "dspr%d.%d", device, sce->chan_num); 549b8f0d9e0SCameron Grant 55033dbf14aSCameron Grant return 0; 55133dbf14aSCameron Grant } 55233dbf14aSCameron Grant 553285648f9SCameron Grant int 5545ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 55533dbf14aSCameron Grant { 556285648f9SCameron Grant struct snddev_channel *sce; 55745550658SPoul-Henning Kamp #if 0 558a527dbc7SCameron Grant int ourlock; 55933dbf14aSCameron Grant 560a527dbc7SCameron Grant ourlock = 0; 561a527dbc7SCameron Grant if (!mtx_owned(d->lock)) { 56249c5e6e2SCameron Grant snd_mtxlock(d->lock); 563a527dbc7SCameron Grant ourlock = 1; 564a527dbc7SCameron Grant } 56545550658SPoul-Henning Kamp #endif 566a527dbc7SCameron Grant 567285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 568285648f9SCameron Grant if (sce->channel == ch) 569285648f9SCameron Grant goto gotit; 57033dbf14aSCameron Grant } 57145550658SPoul-Henning Kamp #if 0 572a527dbc7SCameron Grant if (ourlock) 57349c5e6e2SCameron Grant snd_mtxunlock(d->lock); 57445550658SPoul-Henning Kamp #endif 575285648f9SCameron Grant return EINVAL; 576285648f9SCameron Grant gotit: 577285648f9SCameron Grant SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 57867beb5a5SCameron Grant 57997d69a96SAlexander Leidinger if (ch->flags & CHN_F_VIRTUAL) 58067beb5a5SCameron Grant d->vchancount--; 58197d69a96SAlexander Leidinger else if (ch->direction == PCMDIR_REC) 58297d69a96SAlexander Leidinger d->reccount--; 58367beb5a5SCameron Grant else 58467beb5a5SCameron Grant d->playcount--; 58567beb5a5SCameron Grant 58645550658SPoul-Henning Kamp #if 0 587a527dbc7SCameron Grant if (ourlock) 58849c5e6e2SCameron Grant snd_mtxunlock(d->lock); 58945550658SPoul-Henning Kamp #endif 59067beb5a5SCameron Grant free(sce, M_DEVBUF); 591285648f9SCameron Grant 592987e5972SCameron Grant return 0; 593987e5972SCameron Grant } 594987e5972SCameron Grant 595987e5972SCameron Grant int 596285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 597285648f9SCameron Grant { 598285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 59967b1dce3SCameron Grant struct pcm_channel *ch; 60067b1dce3SCameron Grant int err; 601285648f9SCameron Grant 602285648f9SCameron Grant ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 603285648f9SCameron Grant if (!ch) { 604285648f9SCameron Grant device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 605285648f9SCameron Grant return ENODEV; 606285648f9SCameron Grant } 607cd9766c5SCameron Grant 6085ee30e27SMathew Kanner err = pcm_chn_add(d, ch); 609285648f9SCameron Grant if (err) { 610285648f9SCameron Grant device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 611285648f9SCameron Grant pcm_chn_destroy(ch); 612cd9766c5SCameron Grant return err; 613cd9766c5SCameron Grant } 614cd9766c5SCameron Grant 61512e524a2SDon Lewis CHN_LOCK(ch); 61661698e0cSAlexander Kabaev if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) && 61761698e0cSAlexander Kabaev ch->direction == PCMDIR_PLAY && d->vchancount == 0) { 618cd9766c5SCameron Grant ch->flags |= CHN_F_BUSY; 619cd9766c5SCameron Grant err = vchan_create(ch); 620cd9766c5SCameron Grant if (err) { 62167b1dce3SCameron Grant ch->flags &= ~CHN_F_BUSY; 62212e524a2SDon Lewis CHN_UNLOCK(ch); 62312e524a2SDon Lewis device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 62412e524a2SDon Lewis return err; 625cd9766c5SCameron Grant } 626285648f9SCameron Grant } 62712e524a2SDon Lewis CHN_UNLOCK(ch); 628285648f9SCameron Grant 629285648f9SCameron Grant return err; 630285648f9SCameron Grant } 631285648f9SCameron Grant 632285648f9SCameron Grant static int 633285648f9SCameron Grant pcm_killchan(device_t dev) 634285648f9SCameron Grant { 635285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 636285648f9SCameron Grant struct snddev_channel *sce; 637e33bee07SOlivier Houchard struct pcm_channel *ch; 638e33bee07SOlivier Houchard int error = 0; 639285648f9SCameron Grant 640285648f9SCameron Grant sce = SLIST_FIRST(&d->channels); 641e33bee07SOlivier Houchard ch = sce->channel; 642285648f9SCameron Grant 6435ee30e27SMathew Kanner error = pcm_chn_remove(d, sce->channel); 644e33bee07SOlivier Houchard if (error) 645e33bee07SOlivier Houchard return (error); 646e33bee07SOlivier Houchard return (pcm_chn_destroy(ch)); 647285648f9SCameron Grant } 648285648f9SCameron Grant 649285648f9SCameron Grant int 650987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 651987e5972SCameron Grant { 65266ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 65349c5e6e2SCameron Grant 65449c5e6e2SCameron Grant snd_mtxlock(d->lock); 655987e5972SCameron Grant strncpy(d->status, str, SND_STATUSLEN); 65649c5e6e2SCameron Grant snd_mtxunlock(d->lock); 657987e5972SCameron Grant return 0; 658987e5972SCameron Grant } 659987e5972SCameron Grant 660987e5972SCameron Grant u_int32_t 661987e5972SCameron Grant pcm_getflags(device_t dev) 662987e5972SCameron Grant { 66366ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 66449c5e6e2SCameron Grant 665987e5972SCameron Grant return d->flags; 666987e5972SCameron Grant } 667987e5972SCameron Grant 668987e5972SCameron Grant void 669987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val) 670987e5972SCameron Grant { 67166ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 672d95502a8SCameron Grant 673987e5972SCameron Grant d->flags = val; 674987e5972SCameron Grant } 675987e5972SCameron Grant 67639004e69SCameron Grant void * 67739004e69SCameron Grant pcm_getdevinfo(device_t dev) 67839004e69SCameron Grant { 67966ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 68049c5e6e2SCameron Grant 68139004e69SCameron Grant return d->devinfo; 68239004e69SCameron Grant } 68339004e69SCameron Grant 684a67fe5c1SCameron Grant unsigned int 685a67fe5c1SCameron Grant pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 686a67fe5c1SCameron Grant { 687a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 6884e60be34SCameron Grant int sz, x; 689a67fe5c1SCameron Grant 690a67fe5c1SCameron Grant sz = 0; 6914e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 6924e60be34SCameron Grant x = sz; 693a67fe5c1SCameron Grant RANGE(sz, min, max); 6944e60be34SCameron Grant if (x != sz) 6954e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 6964e60be34SCameron Grant x = min; 6974e60be34SCameron Grant while (x < sz) 6984e60be34SCameron Grant x <<= 1; 6994e60be34SCameron Grant if (x > sz) 7004e60be34SCameron Grant x >>= 1; 7014e60be34SCameron Grant if (x != sz) { 7024e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 7034e60be34SCameron Grant sz = x; 7044e60be34SCameron Grant } 7054e60be34SCameron Grant } else { 706a67fe5c1SCameron Grant sz = deflt; 7074e60be34SCameron Grant } 7084e60be34SCameron Grant 709a67fe5c1SCameron Grant d->bufsz = sz; 710a67fe5c1SCameron Grant 711a67fe5c1SCameron Grant return sz; 712a67fe5c1SCameron Grant } 713a67fe5c1SCameron Grant 714987e5972SCameron Grant int 715987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 716987e5972SCameron Grant { 71766ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 718987e5972SCameron Grant 719b8a36395SCameron Grant if (pcm_veto_load) { 720b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 721b8a36395SCameron Grant 722b8a36395SCameron Grant return EINVAL; 723b8a36395SCameron Grant } 724b8a36395SCameron Grant 7252c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 726285648f9SCameron Grant 7277233ababSAlexander Leidinger #if 0 7287233ababSAlexander Leidinger /* 7297233ababSAlexander Leidinger * d->flags should be cleared by the allocator of the softc. 7307233ababSAlexander Leidinger * We cannot clear this field here because several devices set 7317233ababSAlexander Leidinger * this flag before calling pcm_register(). 7327233ababSAlexander Leidinger */ 733cd9766c5SCameron Grant d->flags = 0; 7347233ababSAlexander Leidinger #endif 735e4d5b250SCameron Grant d->dev = dev; 736987e5972SCameron Grant d->devinfo = devinfo; 737f637a36cSCameron Grant d->devcount = 0; 738506a5308SCameron Grant d->reccount = 0; 739a67fe5c1SCameron Grant d->playcount = 0; 740f637a36cSCameron Grant d->vchancount = 0; 741d95502a8SCameron Grant d->inprog = 0; 742833f7023SCameron Grant 74345550658SPoul-Henning Kamp SLIST_INIT(&d->channels); 74445550658SPoul-Henning Kamp 745d95502a8SCameron Grant if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 746285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 747d95502a8SCameron Grant 748285648f9SCameron Grant d->fakechan = fkchan_setup(dev); 749a3193a9cSDon Lewis chn_init(d->fakechan, NULL, 0, 0); 750987e5972SCameron Grant 75182db23e2SCameron Grant #ifdef SND_DYNSYSCTL 752cc486d80SJohn Baldwin sysctl_ctx_init(&d->sysctl_tree); 753cc486d80SJohn Baldwin d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 754a3e893e0SJohn Baldwin SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 755cc486d80SJohn Baldwin device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 756a3e893e0SJohn Baldwin if (d->sysctl_tree_top == NULL) { 757cc486d80SJohn Baldwin sysctl_ctx_free(&d->sysctl_tree); 758cc486d80SJohn Baldwin goto no; 759cc486d80SJohn Baldwin } 760a67fe5c1SCameron Grant SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 761a67fe5c1SCameron Grant OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 76282db23e2SCameron Grant #endif 76397d69a96SAlexander Leidinger if (numplay > 0) { 76467b1dce3SCameron Grant vchan_initsys(dev); 765cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 76697d69a96SAlexander Leidinger } 767cd9766c5SCameron Grant 76867b1dce3SCameron Grant sndstat_register(dev, d->status, sndstat_prepare_pcm); 769987e5972SCameron Grant return 0; 770987e5972SCameron Grant no: 77149c5e6e2SCameron Grant snd_mtxfree(d->lock); 772987e5972SCameron Grant return ENXIO; 773987e5972SCameron Grant } 774987e5972SCameron Grant 77533dbf14aSCameron Grant int 77633dbf14aSCameron Grant pcm_unregister(device_t dev) 7777c438dbeSCameron Grant { 77866ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 779285648f9SCameron Grant struct snddev_channel *sce; 780a67fe5c1SCameron Grant struct pcm_channel *ch; 7817c438dbeSCameron Grant 78228ef3fb0SAlexander Leidinger if (sndstat_acquire() != 0) { 78328ef3fb0SAlexander Leidinger device_printf(dev, "unregister: sndstat busy\n"); 78428ef3fb0SAlexander Leidinger return EBUSY; 78528ef3fb0SAlexander Leidinger } 78628ef3fb0SAlexander Leidinger 78749c5e6e2SCameron Grant snd_mtxlock(d->lock); 788d95502a8SCameron Grant if (d->inprog) { 7895c25132aSGeorge C A Reid device_printf(dev, "unregister: operation in progress\n"); 7905c25132aSGeorge C A Reid snd_mtxunlock(d->lock); 79128ef3fb0SAlexander Leidinger sndstat_release(); 7925c25132aSGeorge C A Reid return EBUSY; 7935c25132aSGeorge C A Reid } 7945ee30e27SMathew Kanner 795285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 796a67fe5c1SCameron Grant ch = sce->channel; 797a67fe5c1SCameron Grant if (ch->refcount > 0) { 79821ed9908SCameron Grant device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 79949c5e6e2SCameron Grant snd_mtxunlock(d->lock); 80028ef3fb0SAlexander Leidinger sndstat_release(); 801285648f9SCameron Grant return EBUSY; 802285648f9SCameron Grant } 803c9b53085SCameron Grant } 8045ee30e27SMathew Kanner 8057233ababSAlexander Leidinger if (mixer_uninit(dev)) { 8067233ababSAlexander Leidinger device_printf(dev, "unregister: mixer busy\n"); 8077233ababSAlexander Leidinger snd_mtxunlock(d->lock); 80828ef3fb0SAlexander Leidinger sndstat_release(); 8097233ababSAlexander Leidinger return EBUSY; 8107233ababSAlexander Leidinger } 8117233ababSAlexander Leidinger 8125ee30e27SMathew Kanner SLIST_FOREACH(sce, &d->channels, link) { 8137982e7a4SAriff Abdullah if (sce->dsp_devt) { 8145ee30e27SMathew Kanner destroy_dev(sce->dsp_devt); 8157982e7a4SAriff Abdullah sce->dsp_devt = NULL; 8167982e7a4SAriff Abdullah } 8177982e7a4SAriff Abdullah if (sce->dspW_devt) { 8185ee30e27SMathew Kanner destroy_dev(sce->dspW_devt); 8197982e7a4SAriff Abdullah sce->dspW_devt = NULL; 8207982e7a4SAriff Abdullah } 8217982e7a4SAriff Abdullah if (sce->audio_devt) { 8225ee30e27SMathew Kanner destroy_dev(sce->audio_devt); 8237982e7a4SAriff Abdullah sce->audio_devt = NULL; 8247982e7a4SAriff Abdullah } 8257982e7a4SAriff Abdullah if (sce->dspr_devt) { 8265ee30e27SMathew Kanner destroy_dev(sce->dspr_devt); 8277982e7a4SAriff Abdullah sce->dspr_devt = NULL; 8287982e7a4SAriff Abdullah } 8297982e7a4SAriff Abdullah d->devcount--; 8305ee30e27SMathew Kanner } 8315ee30e27SMathew Kanner 83266ef8af5SCameron Grant #ifdef SND_DYNSYSCTL 83366ef8af5SCameron Grant d->sysctl_tree_top = NULL; 83466ef8af5SCameron Grant sysctl_ctx_free(&d->sysctl_tree); 83566ef8af5SCameron Grant #endif 836faeebea2SCameron Grant while (!SLIST_EMPTY(&d->channels)) 837285648f9SCameron Grant pcm_killchan(dev); 8387c438dbeSCameron Grant 83966ef8af5SCameron Grant chn_kill(d->fakechan); 84066ef8af5SCameron Grant fkchan_kill(d->fakechan); 84182db23e2SCameron Grant 84245550658SPoul-Henning Kamp snd_mtxunlock(d->lock); 84349c5e6e2SCameron Grant snd_mtxfree(d->lock); 84428ef3fb0SAlexander Leidinger sndstat_unregister(dev); 84528ef3fb0SAlexander Leidinger sndstat_release(); 84633dbf14aSCameron Grant return 0; 84733dbf14aSCameron Grant } 8487c438dbeSCameron Grant 84967b1dce3SCameron Grant /************************************************************************/ 85067b1dce3SCameron Grant 85167b1dce3SCameron Grant static int 85267b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 85367b1dce3SCameron Grant { 85467b1dce3SCameron Grant struct snddev_info *d; 85567b1dce3SCameron Grant struct snddev_channel *sce; 85667b1dce3SCameron Grant struct pcm_channel *c; 85767b1dce3SCameron Grant struct pcm_feeder *f; 85867b1dce3SCameron Grant int pc, rc, vc; 85967b1dce3SCameron Grant 86067b1dce3SCameron Grant if (verbose < 1) 86167b1dce3SCameron Grant return 0; 86267b1dce3SCameron Grant 86367b1dce3SCameron Grant d = device_get_softc(dev); 86467b1dce3SCameron Grant if (!d) 86567b1dce3SCameron Grant return ENXIO; 86667b1dce3SCameron Grant 86767b1dce3SCameron Grant snd_mtxlock(d->lock); 86867b1dce3SCameron Grant if (!SLIST_EMPTY(&d->channels)) { 86967b1dce3SCameron Grant pc = rc = vc = 0; 87067b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 87167b1dce3SCameron Grant c = sce->channel; 87267b1dce3SCameron Grant if (c->direction == PCMDIR_PLAY) { 87367b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 87467b1dce3SCameron Grant vc++; 87567b1dce3SCameron Grant else 87667b1dce3SCameron Grant pc++; 87767b1dce3SCameron Grant } else 87867b1dce3SCameron Grant rc++; 87967b1dce3SCameron Grant } 880a67fe5c1SCameron Grant sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 88167b1dce3SCameron Grant (d->flags & SD_F_SIMPLEX)? "" : " duplex", 88267b1dce3SCameron Grant #ifdef USING_DEVFS 88367b1dce3SCameron Grant (device_get_unit(dev) == snd_unit)? " default" : "" 88467b1dce3SCameron Grant #else 88567b1dce3SCameron Grant "" 88667b1dce3SCameron Grant #endif 88767b1dce3SCameron Grant ); 888a527dbc7SCameron Grant 889a527dbc7SCameron Grant if (verbose <= 1) { 890a527dbc7SCameron Grant snd_mtxunlock(d->lock); 891a527dbc7SCameron Grant return 0; 892a527dbc7SCameron Grant } 893a527dbc7SCameron Grant 89467b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 89567b1dce3SCameron Grant c = sce->channel; 8969d978cc7SAlexander Leidinger 8979d978cc7SAlexander Leidinger KASSERT(c->bufhard != NULL && c->bufsoft != NULL, 8989d978cc7SAlexander Leidinger ("hosed pcm channel setup")); 8999d978cc7SAlexander Leidinger 900a3285889SCameron Grant sbuf_printf(s, "\n\t"); 901a3285889SCameron Grant 90245550658SPoul-Henning Kamp /* it would be better to indent child channels */ 903a3285889SCameron Grant sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 9044c68642aSCameron Grant sbuf_printf(s, "spd %d", c->speed); 9054c68642aSCameron Grant if (c->speed != sndbuf_getspd(c->bufhard)) 9064c68642aSCameron Grant sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 9074c68642aSCameron Grant sbuf_printf(s, ", fmt 0x%08x", c->format); 9084c68642aSCameron Grant if (c->format != sndbuf_getfmt(c->bufhard)) 9094c68642aSCameron Grant sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 910a527dbc7SCameron Grant sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 91167b1dce3SCameron Grant if (c->pid != -1) 91267b1dce3SCameron Grant sbuf_printf(s, ", pid %d", c->pid); 91367b1dce3SCameron Grant sbuf_printf(s, "\n\t"); 9144c68642aSCameron Grant 915a3285889SCameron Grant sbuf_printf(s, "interrupts %d, ", c->interrupts); 916a3285889SCameron Grant if (c->direction == PCMDIR_REC) 91797d69a96SAlexander Leidinger sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 91897d69a96SAlexander Leidinger c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), 91997d69a96SAlexander Leidinger sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 92097d69a96SAlexander Leidinger sndbuf_getblkcnt(c->bufhard), 92197d69a96SAlexander Leidinger sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 92297d69a96SAlexander Leidinger sndbuf_getblkcnt(c->bufsoft)); 923a3285889SCameron Grant else 92497d69a96SAlexander Leidinger sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", 92597d69a96SAlexander Leidinger c->xruns, sndbuf_getready(c->bufsoft), 92697d69a96SAlexander Leidinger sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 92797d69a96SAlexander Leidinger sndbuf_getblkcnt(c->bufhard), 92897d69a96SAlexander Leidinger sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 92997d69a96SAlexander Leidinger sndbuf_getblkcnt(c->bufsoft)); 930a3285889SCameron Grant sbuf_printf(s, "\n\t"); 9314c68642aSCameron Grant 9324c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 9334c68642aSCameron Grant sbuf_printf(s, " -> "); 93467b1dce3SCameron Grant f = c->feeder; 9354c68642aSCameron Grant while (f->source != NULL) 9364c68642aSCameron Grant f = f->source; 9374c68642aSCameron Grant while (f != NULL) { 93867b1dce3SCameron Grant sbuf_printf(s, "%s", f->class->name); 93967b1dce3SCameron Grant if (f->desc->type == FEEDER_FMT) 9404c68642aSCameron Grant sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 94167b1dce3SCameron Grant if (f->desc->type == FEEDER_RATE) 9424c68642aSCameron Grant sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 94328ef3fb0SAlexander Leidinger if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || 94428ef3fb0SAlexander Leidinger f->desc->type == FEEDER_VOLUME) 9454c68642aSCameron Grant sbuf_printf(s, "(0x%08x)", f->desc->out); 9464c68642aSCameron Grant sbuf_printf(s, " -> "); 9474c68642aSCameron Grant f = f->parent; 94867b1dce3SCameron Grant } 9494c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 95067b1dce3SCameron Grant } 95167b1dce3SCameron Grant } else 95267b1dce3SCameron Grant sbuf_printf(s, " (mixer only)"); 95367b1dce3SCameron Grant snd_mtxunlock(d->lock); 95467b1dce3SCameron Grant 95567b1dce3SCameron Grant return 0; 95667b1dce3SCameron Grant } 95767b1dce3SCameron Grant 95867b1dce3SCameron Grant /************************************************************************/ 95967b1dce3SCameron Grant 96067b1dce3SCameron Grant #ifdef SND_DYNSYSCTL 96167b1dce3SCameron Grant int 96267b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 96367b1dce3SCameron Grant { 96467b1dce3SCameron Grant struct snddev_info *d; 96567b1dce3SCameron Grant struct snddev_channel *sce; 96667b1dce3SCameron Grant struct pcm_channel *c; 96797d69a96SAlexander Leidinger int err, newcnt, cnt; 96867b1dce3SCameron Grant 96997d69a96SAlexander Leidinger /* 97097d69a96SAlexander Leidinger * XXX WOAH... NEED SUPER CLEANUP!!! 97197d69a96SAlexander Leidinger * Robust, yet confusing. Understanding these will 97297d69a96SAlexander Leidinger * cause your brain spinning like a Doki Doki Dynamo. 97397d69a96SAlexander Leidinger */ 97467b1dce3SCameron Grant d = oidp->oid_arg1; 97567b1dce3SCameron Grant 97697d69a96SAlexander Leidinger if (!(d->flags & SD_F_AUTOVCHAN)) { 977a527dbc7SCameron Grant pcm_inprog(d, -1); 97897d69a96SAlexander Leidinger return EINVAL; 979a527dbc7SCameron Grant } 980a527dbc7SCameron Grant 98167b1dce3SCameron Grant cnt = 0; 98267b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 98367b1dce3SCameron Grant c = sce->channel; 98412e524a2SDon Lewis CHN_LOCK(c); 985a527dbc7SCameron Grant if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) { 98667b1dce3SCameron Grant cnt++; 98797d69a96SAlexander Leidinger if (req->newptr != NULL && c->flags & CHN_F_BUSY) { 98897d69a96SAlexander Leidinger /* Better safe than sorry */ 98997d69a96SAlexander Leidinger CHN_UNLOCK(c); 99097d69a96SAlexander Leidinger return EBUSY; 99197d69a96SAlexander Leidinger } 99267b1dce3SCameron Grant } 99312e524a2SDon Lewis CHN_UNLOCK(c); 994a527dbc7SCameron Grant } 995a527dbc7SCameron Grant 99667b1dce3SCameron Grant newcnt = cnt; 99767b1dce3SCameron Grant 99867b1dce3SCameron Grant err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 999a527dbc7SCameron Grant 100067b1dce3SCameron Grant if (err == 0 && req->newptr != NULL) { 1001a527dbc7SCameron Grant 10027982e7a4SAriff Abdullah if (newcnt < 0 || newcnt > (PCMMAXCHAN + 1) || 10037982e7a4SAriff Abdullah (d->playcount + d->reccount + newcnt) > (PCMMAXCHAN + 1)) 1004a527dbc7SCameron Grant return E2BIG; 100597d69a96SAlexander Leidinger 100697d69a96SAlexander Leidinger if (pcm_inprog(d, 1) != 1) { 100797d69a96SAlexander Leidinger pcm_inprog(d, -1); 100897d69a96SAlexander Leidinger return EINPROGRESS; 100967b1dce3SCameron Grant } 101067b1dce3SCameron Grant 101167b1dce3SCameron Grant if (newcnt > cnt) { 101267b1dce3SCameron Grant /* add new vchans - find a parent channel first */ 101367b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 101467b1dce3SCameron Grant c = sce->channel; 101512e524a2SDon Lewis CHN_LOCK(c); 101667b1dce3SCameron Grant /* not a candidate if not a play channel */ 101767b1dce3SCameron Grant if (c->direction != PCMDIR_PLAY) 101812e524a2SDon Lewis goto next; 101967b1dce3SCameron Grant /* not a candidate if a virtual channel */ 102067b1dce3SCameron Grant if (c->flags & CHN_F_VIRTUAL) 102112e524a2SDon Lewis goto next; 102267b1dce3SCameron Grant /* not a candidate if it's in use */ 102312e524a2SDon Lewis if (!(c->flags & CHN_F_BUSY) || 102412e524a2SDon Lewis !(SLIST_EMPTY(&c->children))) 102567b1dce3SCameron Grant /* 102612e524a2SDon Lewis * if we get here we're a nonvirtual 102712e524a2SDon Lewis * play channel, and either 102867b1dce3SCameron Grant * 1) not busy 102912e524a2SDon Lewis * 2) busy with children, not directly 103012e524a2SDon Lewis * open 103167b1dce3SCameron Grant * 103267b1dce3SCameron Grant * thus we can add children 103367b1dce3SCameron Grant */ 103467b1dce3SCameron Grant goto addok; 103512e524a2SDon Lewis next: 103612e524a2SDon Lewis CHN_UNLOCK(c); 103767b1dce3SCameron Grant } 1038a527dbc7SCameron Grant pcm_inprog(d, -1); 103967b1dce3SCameron Grant return EBUSY; 104067b1dce3SCameron Grant addok: 104167b1dce3SCameron Grant c->flags |= CHN_F_BUSY; 104267b1dce3SCameron Grant while (err == 0 && newcnt > cnt) { 10437982e7a4SAriff Abdullah if (d->devcount > PCMMAXCHAN) { 10447982e7a4SAriff Abdullah device_printf(d->dev, "%s: Maximum channel reached.\n", __func__); 10457982e7a4SAriff Abdullah break; 10467982e7a4SAriff Abdullah } 104767b1dce3SCameron Grant err = vchan_create(c); 104867b1dce3SCameron Grant if (err == 0) 104967b1dce3SCameron Grant cnt++; 10507982e7a4SAriff Abdullah if (newcnt > cnt && err == E2BIG) { 10517982e7a4SAriff Abdullah device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err); 10527982e7a4SAriff Abdullah err = 0; 10537982e7a4SAriff Abdullah break; 10547982e7a4SAriff Abdullah } 105567b1dce3SCameron Grant } 105612e524a2SDon Lewis CHN_UNLOCK(c); 105767b1dce3SCameron Grant } else if (newcnt < cnt) { 1058a527dbc7SCameron Grant snd_mtxlock(d->lock); 105967b1dce3SCameron Grant while (err == 0 && newcnt < cnt) { 10607982e7a4SAriff Abdullah c = NULL; 106167b1dce3SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 10627982e7a4SAriff Abdullah CHN_LOCK(sce->channel); 10637982e7a4SAriff Abdullah if (sce->channel->direction == PCMDIR_PLAY && 10647982e7a4SAriff Abdullah (sce->channel->flags & CHN_F_VIRTUAL)) 106567b1dce3SCameron Grant c = sce->channel; 10667982e7a4SAriff Abdullah CHN_UNLOCK(sce->channel); 106767b1dce3SCameron Grant } 10687982e7a4SAriff Abdullah if (c != NULL) 10697982e7a4SAriff Abdullah goto remok; 1070a527dbc7SCameron Grant snd_mtxunlock(d->lock); 1071a527dbc7SCameron Grant pcm_inprog(d, -1); 107267b1dce3SCameron Grant return EINVAL; 107367b1dce3SCameron Grant remok: 107467b1dce3SCameron Grant err = vchan_destroy(c); 107567b1dce3SCameron Grant if (err == 0) 107667b1dce3SCameron Grant cnt--; 107767b1dce3SCameron Grant } 1078a527dbc7SCameron Grant snd_mtxunlock(d->lock); 107967b1dce3SCameron Grant } 1080a527dbc7SCameron Grant pcm_inprog(d, -1); 108197d69a96SAlexander Leidinger } 108267b1dce3SCameron Grant return err; 108367b1dce3SCameron Grant } 108467b1dce3SCameron Grant #endif 108567b1dce3SCameron Grant 108667b1dce3SCameron Grant /************************************************************************/ 108767b1dce3SCameron Grant 10880739ea1dSSeigo Tanimura static int 10890739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data) 10900739ea1dSSeigo Tanimura { 10917233ababSAlexander Leidinger #if 0 10920739ea1dSSeigo Tanimura return (midi_modevent(mod, type, data)); 10937233ababSAlexander Leidinger #else 10947233ababSAlexander Leidinger return 0; 10957233ababSAlexander Leidinger #endif 10960739ea1dSSeigo Tanimura } 10970739ea1dSSeigo Tanimura 10980739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL); 10990739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER); 1100