1098ca2bdSWarner Losh /*- 23f225978SCameron Grant * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 3155414e2SJoel Dahl * (C) 1997 Luigi Rizzo 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> 29a580b31aSAriff Abdullah #include <dev/sound/pcm/ac97.h> 30285648f9SCameron Grant #include <dev/sound/pcm/vchan.h> 315ee30e27SMathew Kanner #include <dev/sound/pcm/dsp.h> 32bba4862cSAriff Abdullah #include <dev/sound/version.h> 33b611c801SAlexander Leidinger #include <sys/limits.h> 347c438dbeSCameron Grant #include <sys/sysctl.h> 35285648f9SCameron Grant 3667b1dce3SCameron Grant #include "feeder_if.h" 3767b1dce3SCameron Grant 3867b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 3967b1dce3SCameron Grant 40d95502a8SCameron Grant devclass_t pcm_devclass; 4182db23e2SCameron Grant 42b8a36395SCameron Grant int pcm_veto_load = 1; 43b8a36395SCameron Grant 4482db23e2SCameron Grant #ifdef USING_DEVFS 45d95502a8SCameron Grant int snd_unit = 0; 46851a904aSAlexander Leidinger TUNABLE_INT("hw.snd.default_unit", &snd_unit); 4782db23e2SCameron Grant #endif 48cbe7d6a3SCameron Grant 49bba4862cSAriff Abdullah int snd_maxautovchans = 16; 50851a904aSAlexander Leidinger /* XXX: a tunable implies that we may need more than one sound channel before 51851a904aSAlexander Leidinger the system can change a sysctl (/etc/sysctl.conf), do we really need 52851a904aSAlexander Leidinger this? */ 5367b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 54987e5972SCameron Grant 5582db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 5682db23e2SCameron Grant 57bba4862cSAriff Abdullah /* 58bba4862cSAriff Abdullah * XXX I've had enough with people not telling proper version/arch 59bba4862cSAriff Abdullah * while reporting problems, not after 387397913213th questions/requests. 60bba4862cSAriff Abdullah */ 61bba4862cSAriff Abdullah static const char snd_driver_version[] = 62bba4862cSAriff Abdullah __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH; 63bba4862cSAriff Abdullah SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version, 64bba4862cSAriff Abdullah 0, "Driver version/arch"); 65bba4862cSAriff Abdullah 66b611c801SAlexander Leidinger /** 67b611c801SAlexander Leidinger * @brief Unit number allocator for syncgroup IDs 68b611c801SAlexander Leidinger */ 69b611c801SAlexander Leidinger struct unrhdr *pcmsg_unrhdr = NULL; 70b611c801SAlexander Leidinger 7167b1dce3SCameron Grant static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 7267b1dce3SCameron Grant 7337209180SCameron Grant void * 742c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type) 7537209180SCameron Grant { 7637209180SCameron Grant #ifdef USING_MUTEX 7737209180SCameron Grant struct mtx *m; 7837209180SCameron Grant 79a163d034SWarner Losh m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 8012e524a2SDon Lewis mtx_init(m, desc, type, MTX_DEF); 8112e524a2SDon Lewis return m; 8212e524a2SDon Lewis #else 8312e524a2SDon Lewis return (void *)0xcafebabe; 8412e524a2SDon Lewis #endif 8512e524a2SDon Lewis } 8612e524a2SDon Lewis 8737209180SCameron Grant void 8837209180SCameron Grant snd_mtxfree(void *m) 8937209180SCameron Grant { 9037209180SCameron Grant #ifdef USING_MUTEX 9137209180SCameron Grant struct mtx *mtx = m; 9237209180SCameron Grant 9367beb5a5SCameron Grant /* mtx_assert(mtx, MA_OWNED); */ 9437209180SCameron Grant mtx_destroy(mtx); 9537209180SCameron Grant free(mtx, M_DEVBUF); 9637209180SCameron Grant #endif 9737209180SCameron Grant } 9837209180SCameron Grant 9937209180SCameron Grant void 10037209180SCameron Grant snd_mtxassert(void *m) 10137209180SCameron Grant { 10237209180SCameron Grant #ifdef USING_MUTEX 103f00f162aSCameron Grant #ifdef INVARIANTS 10437209180SCameron Grant struct mtx *mtx = m; 10537209180SCameron Grant 10637209180SCameron Grant mtx_assert(mtx, MA_OWNED); 10737209180SCameron Grant #endif 108f00f162aSCameron Grant #endif 10937209180SCameron Grant } 11067beb5a5SCameron Grant /* 11137209180SCameron Grant void 11237209180SCameron Grant snd_mtxlock(void *m) 11337209180SCameron Grant { 11437209180SCameron Grant #ifdef USING_MUTEX 11537209180SCameron Grant struct mtx *mtx = m; 11637209180SCameron Grant 11737209180SCameron Grant mtx_lock(mtx); 11837209180SCameron Grant #endif 11937209180SCameron Grant } 12037209180SCameron Grant 12137209180SCameron Grant void 12237209180SCameron Grant snd_mtxunlock(void *m) 12337209180SCameron Grant { 12437209180SCameron Grant #ifdef USING_MUTEX 12537209180SCameron Grant struct mtx *mtx = m; 12637209180SCameron Grant 12737209180SCameron Grant mtx_unlock(mtx); 12837209180SCameron Grant #endif 12937209180SCameron Grant } 13067beb5a5SCameron Grant */ 13137209180SCameron Grant int 13237209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 13337209180SCameron Grant { 134e4e61333SAriff Abdullah struct snddev_info *d; 13537209180SCameron Grant #ifdef USING_MUTEX 13637209180SCameron Grant flags &= INTR_MPSAFE; 13746700f12SPeter Wemm flags |= INTR_TYPE_AV; 13837209180SCameron Grant #else 13946700f12SPeter Wemm flags = INTR_TYPE_AV; 14037209180SCameron Grant #endif 141e4e61333SAriff Abdullah d = device_get_softc(dev); 142e4e61333SAriff Abdullah if (d != NULL && (flags & INTR_MPSAFE)) 143e4e61333SAriff Abdullah d->flags |= SD_F_MPSAFE; 144e4e61333SAriff Abdullah 1452cc08b74SAriff Abdullah return bus_setup_intr(dev, res, flags, 1462cc08b74SAriff Abdullah #if __FreeBSD_version >= 700031 1472cc08b74SAriff Abdullah NULL, 1482cc08b74SAriff Abdullah #endif 1492cc08b74SAriff Abdullah hand, param, cookiep); 15037209180SCameron Grant } 15137209180SCameron Grant 152a527dbc7SCameron Grant #ifndef PCM_DEBUG_MTX 15367b1dce3SCameron Grant void 15467b1dce3SCameron Grant pcm_lock(struct snddev_info *d) 15567b1dce3SCameron Grant { 15667b1dce3SCameron Grant snd_mtxlock(d->lock); 15767b1dce3SCameron Grant } 15867b1dce3SCameron Grant 15967b1dce3SCameron Grant void 16067b1dce3SCameron Grant pcm_unlock(struct snddev_info *d) 16167b1dce3SCameron Grant { 16267b1dce3SCameron Grant snd_mtxunlock(d->lock); 16367b1dce3SCameron Grant } 164a527dbc7SCameron Grant #endif 16567b1dce3SCameron Grant 16667b1dce3SCameron Grant struct pcm_channel * 16767b1dce3SCameron Grant pcm_getfakechan(struct snddev_info *d) 16867b1dce3SCameron Grant { 16967b1dce3SCameron Grant return d->fakechan; 17067b1dce3SCameron Grant } 17167b1dce3SCameron Grant 172bba4862cSAriff Abdullah static void 173bba4862cSAriff Abdullah pcm_clonereset(struct snddev_info *d) 174a1d444e1SAriff Abdullah { 175bba4862cSAriff Abdullah int cmax; 176bba4862cSAriff Abdullah 177e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 178bba4862cSAriff Abdullah 179bba4862cSAriff Abdullah cmax = d->playcount + d->reccount - 1; 180bba4862cSAriff Abdullah if (d->pvchancount > 0) 181bba4862cSAriff Abdullah cmax += MAX(d->pvchancount, snd_maxautovchans) - 1; 182bba4862cSAriff Abdullah if (d->rvchancount > 0) 183bba4862cSAriff Abdullah cmax += MAX(d->rvchancount, snd_maxautovchans) - 1; 184bba4862cSAriff Abdullah if (cmax > PCMMAXCLONE) 185bba4862cSAriff Abdullah cmax = PCMMAXCLONE; 186bba4862cSAriff Abdullah (void)snd_clone_gc(d->clones); 187bba4862cSAriff Abdullah (void)snd_clone_setmaxunit(d->clones, cmax); 188bba4862cSAriff Abdullah } 189bba4862cSAriff Abdullah 190bba4862cSAriff Abdullah static int 191bba4862cSAriff Abdullah pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) 192bba4862cSAriff Abdullah { 193bba4862cSAriff Abdullah struct pcm_channel *c, *ch, *nch; 194bba4862cSAriff Abdullah int err, vcnt; 195bba4862cSAriff Abdullah 196e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 197a1d444e1SAriff Abdullah 198bba4862cSAriff Abdullah if ((direction == PCMDIR_PLAY && d->playcount < 1) || 199e4e61333SAriff Abdullah (direction == PCMDIR_REC && d->reccount < 1)) 200e4e61333SAriff Abdullah return (ENODEV); 201a580b31aSAriff Abdullah 202e4e61333SAriff Abdullah if (!(d->flags & SD_F_AUTOVCHAN)) 203e4e61333SAriff Abdullah return (EINVAL); 204a1d444e1SAriff Abdullah 205e4e61333SAriff Abdullah if (newcnt < 0 || newcnt > SND_MAXVCHANS) 206e4e61333SAriff Abdullah return (E2BIG); 207a1d444e1SAriff Abdullah 208bba4862cSAriff Abdullah if (direction == PCMDIR_PLAY) 209bba4862cSAriff Abdullah vcnt = d->pvchancount; 210bba4862cSAriff Abdullah else if (direction == PCMDIR_REC) 211bba4862cSAriff Abdullah vcnt = d->rvchancount; 212e4e61333SAriff Abdullah else 213e4e61333SAriff Abdullah return (EINVAL); 214a1d444e1SAriff Abdullah 215a1d444e1SAriff Abdullah if (newcnt > vcnt) { 216bba4862cSAriff Abdullah KASSERT(num == -1 || 217bba4862cSAriff Abdullah (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt), 218bba4862cSAriff Abdullah ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d", 219bba4862cSAriff Abdullah num, newcnt, vcnt)); 220a1d444e1SAriff Abdullah /* add new vchans - find a parent channel first */ 221e4e61333SAriff Abdullah ch = NULL; 222bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 223a1d444e1SAriff Abdullah CHN_LOCK(c); 224bba4862cSAriff Abdullah if (c->direction == direction && 225bba4862cSAriff Abdullah ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 226e4e61333SAriff Abdullah !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 227e4e61333SAriff Abdullah ch = c; 228e4e61333SAriff Abdullah break; 229e4e61333SAriff Abdullah } 230a1d444e1SAriff Abdullah CHN_UNLOCK(c); 231a1d444e1SAriff Abdullah } 232e4e61333SAriff Abdullah if (ch == NULL) 233e4e61333SAriff Abdullah return (EBUSY); 234e4e61333SAriff Abdullah ch->flags |= CHN_F_BUSY; 235e4e61333SAriff Abdullah err = 0; 236a1d444e1SAriff Abdullah while (err == 0 && newcnt > vcnt) { 237e4e61333SAriff Abdullah err = vchan_create(ch, num); 238bba4862cSAriff Abdullah if (err == 0) 239a1d444e1SAriff Abdullah vcnt++; 240bba4862cSAriff Abdullah else if (err == E2BIG && newcnt > vcnt) 241bba4862cSAriff Abdullah device_printf(d->dev, 242bba4862cSAriff Abdullah "%s: err=%d Maximum channel reached.\n", 243bba4862cSAriff Abdullah __func__, err); 244a1d444e1SAriff Abdullah } 245a1d444e1SAriff Abdullah if (vcnt == 0) 246e4e61333SAriff Abdullah ch->flags &= ~CHN_F_BUSY; 247e4e61333SAriff Abdullah CHN_UNLOCK(ch); 248e4e61333SAriff Abdullah if (err != 0) 249e4e61333SAriff Abdullah return (err); 250e4e61333SAriff Abdullah else 251bba4862cSAriff Abdullah pcm_clonereset(d); 252a1d444e1SAriff Abdullah } else if (newcnt < vcnt) { 253bba4862cSAriff Abdullah KASSERT(num == -1, 254bba4862cSAriff Abdullah ("bogus vchan_destroy() request num=%d", num)); 255bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 256a1d444e1SAriff Abdullah CHN_LOCK(c); 257bba4862cSAriff Abdullah if (c->direction != direction || 258bba4862cSAriff Abdullah CHN_EMPTY(c, children) || 259bba4862cSAriff Abdullah !(c->flags & CHN_F_HAS_VCHAN)) { 260a1d444e1SAriff Abdullah CHN_UNLOCK(c); 261bba4862cSAriff Abdullah continue; 262a1d444e1SAriff Abdullah } 263bba4862cSAriff Abdullah CHN_FOREACH_SAFE(ch, c, nch, children) { 264bba4862cSAriff Abdullah CHN_LOCK(ch); 265bba4862cSAriff Abdullah if (!(ch->flags & CHN_F_BUSY)) { 266bba4862cSAriff Abdullah CHN_UNLOCK(ch); 267a1d444e1SAriff Abdullah CHN_UNLOCK(c); 268bba4862cSAriff Abdullah err = vchan_destroy(ch); 269bba4862cSAriff Abdullah CHN_LOCK(c); 270a1d444e1SAriff Abdullah if (err == 0) 271a1d444e1SAriff Abdullah vcnt--; 272bba4862cSAriff Abdullah } else 273bba4862cSAriff Abdullah CHN_UNLOCK(ch); 274e4e61333SAriff Abdullah if (vcnt == newcnt) 275bba4862cSAriff Abdullah break; 276a1d444e1SAriff Abdullah } 277bba4862cSAriff Abdullah CHN_UNLOCK(c); 278bba4862cSAriff Abdullah break; 279bba4862cSAriff Abdullah } 280bba4862cSAriff Abdullah pcm_clonereset(d); 281bba4862cSAriff Abdullah } 282a1d444e1SAriff Abdullah 283e4e61333SAriff Abdullah return (0); 284a1d444e1SAriff Abdullah } 285a1d444e1SAriff Abdullah 2863fdb3676SAriff Abdullah /* return error status and a locked channel */ 2873fdb3676SAriff Abdullah int 2883fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 289bba4862cSAriff Abdullah pid_t pid, int devunit) 290285648f9SCameron Grant { 291285648f9SCameron Grant struct pcm_channel *c; 292bba4862cSAriff Abdullah int err, vchancount; 293bba4862cSAriff Abdullah 294bba4862cSAriff Abdullah KASSERT(d != NULL && ch != NULL && (devunit == -1 || 295bba4862cSAriff Abdullah !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && 296bba4862cSAriff Abdullah (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 297e4e61333SAriff Abdullah ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d", 298bba4862cSAriff Abdullah __func__, d, ch, direction, pid, devunit)); 299e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 300bba4862cSAriff Abdullah 301bba4862cSAriff Abdullah /* Double check again. */ 302bba4862cSAriff Abdullah if (devunit != -1) { 303bba4862cSAriff Abdullah switch (snd_unit2d(devunit)) { 304bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 305bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 306bba4862cSAriff Abdullah if (direction != PCMDIR_PLAY) 307bba4862cSAriff Abdullah return (EOPNOTSUPP); 308bba4862cSAriff Abdullah break; 309bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 310bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 311bba4862cSAriff Abdullah if (direction != PCMDIR_REC) 312bba4862cSAriff Abdullah return (EOPNOTSUPP); 313bba4862cSAriff Abdullah break; 314bba4862cSAriff Abdullah default: 315bba4862cSAriff Abdullah if (!(direction == PCMDIR_PLAY || 316bba4862cSAriff Abdullah direction == PCMDIR_REC)) 317bba4862cSAriff Abdullah return (EOPNOTSUPP); 318bba4862cSAriff Abdullah break; 319bba4862cSAriff Abdullah } 320bba4862cSAriff Abdullah } 321285648f9SCameron Grant 3223fdb3676SAriff Abdullah retry_chnalloc: 323e4e61333SAriff Abdullah err = EOPNOTSUPP; 324f637a36cSCameron Grant /* scan for a free channel */ 325bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 32649c5e6e2SCameron Grant CHN_LOCK(c); 327bba4862cSAriff Abdullah if (c->direction == direction && !(c->flags & CHN_F_BUSY) && 328bba4862cSAriff Abdullah (devunit == -1 || devunit == -2 || c->unit == devunit)) { 329285648f9SCameron Grant c->flags |= CHN_F_BUSY; 330b8f0d9e0SCameron Grant c->pid = pid; 3313fdb3676SAriff Abdullah *ch = c; 332bba4862cSAriff Abdullah return (0); 333bba4862cSAriff Abdullah } else if (c->unit == devunit) { 3343fdb3676SAriff Abdullah if (c->direction != direction) 3353fdb3676SAriff Abdullah err = EOPNOTSUPP; 3363fdb3676SAriff Abdullah else if (c->flags & CHN_F_BUSY) 3373fdb3676SAriff Abdullah err = EBUSY; 3383fdb3676SAriff Abdullah else 3393fdb3676SAriff Abdullah err = EINVAL; 3403fdb3676SAriff Abdullah CHN_UNLOCK(c); 341bba4862cSAriff Abdullah return (err); 342bba4862cSAriff Abdullah } else if ((devunit == -1 || devunit == -2) && 343bba4862cSAriff Abdullah c->direction == direction && (c->flags & CHN_F_BUSY)) 344a1d444e1SAriff Abdullah err = EBUSY; 34549c5e6e2SCameron Grant CHN_UNLOCK(c); 346285648f9SCameron Grant } 347f637a36cSCameron Grant 348e4e61333SAriff Abdullah if (devunit == -2) 349e4e61333SAriff Abdullah return (err); 350e4e61333SAriff Abdullah 351f637a36cSCameron Grant /* no channel available */ 352e4e61333SAriff Abdullah if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || 353e4e61333SAriff Abdullah snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { 354bba4862cSAriff Abdullah if (direction == PCMDIR_PLAY) 355bba4862cSAriff Abdullah vchancount = d->pvchancount; 356bba4862cSAriff Abdullah else 357bba4862cSAriff Abdullah vchancount = d->rvchancount; 358bba4862cSAriff Abdullah if (!(vchancount > 0 && vchancount < snd_maxautovchans) && 359bba4862cSAriff Abdullah (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) 360bba4862cSAriff Abdullah return (err); 361bba4862cSAriff Abdullah err = pcm_setvchans(d, direction, vchancount + 1, 362bba4862cSAriff Abdullah (devunit == -1) ? -1 : snd_unit2c(devunit)); 363a1d444e1SAriff Abdullah if (err == 0) { 364bba4862cSAriff Abdullah if (devunit == -1) 365bba4862cSAriff Abdullah devunit = -2; 3663fdb3676SAriff Abdullah goto retry_chnalloc; 367f637a36cSCameron Grant } 368f637a36cSCameron Grant } 369f637a36cSCameron Grant 370bba4862cSAriff Abdullah return (err); 371285648f9SCameron Grant } 372285648f9SCameron Grant 373b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 374285648f9SCameron Grant int 375b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 376285648f9SCameron Grant { 377e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 378b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 379e4e61333SAriff Abdullah 380285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 381b8f0d9e0SCameron Grant c->pid = -1; 38249c5e6e2SCameron Grant CHN_UNLOCK(c); 383e4e61333SAriff Abdullah 384e4e61333SAriff Abdullah return (0); 385285648f9SCameron Grant } 386285648f9SCameron Grant 387285648f9SCameron Grant int 388285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 389285648f9SCameron Grant { 390e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 391b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 392e4e61333SAriff Abdullah 393285648f9SCameron Grant c->refcount += ref; 394e4e61333SAriff Abdullah 395e4e61333SAriff Abdullah return (c->refcount); 396285648f9SCameron Grant } 397285648f9SCameron Grant 39867b1dce3SCameron Grant int 39967b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta) 40067b1dce3SCameron Grant { 401e4e61333SAriff Abdullah snd_mtxassert(d->lock); 402a527dbc7SCameron Grant 403a527dbc7SCameron Grant d->inprog += delta; 404e4e61333SAriff Abdullah 405e4e61333SAriff Abdullah return (d->inprog); 40667b1dce3SCameron Grant } 40767b1dce3SCameron Grant 40867b1dce3SCameron Grant static void 40967b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 41067b1dce3SCameron Grant { 411e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 412e4e61333SAriff Abdullah 413bba4862cSAriff Abdullah if (num < 0) 414bba4862cSAriff Abdullah return; 415bba4862cSAriff Abdullah 416bba4862cSAriff Abdullah if (num >= 0 && d->pvchancount > num) 417bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1); 418bba4862cSAriff Abdullah else if (num > 0 && d->pvchancount == 0) 419bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1); 420bba4862cSAriff Abdullah 421bba4862cSAriff Abdullah if (num >= 0 && d->rvchancount > num) 422bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, num, -1); 423bba4862cSAriff Abdullah else if (num > 0 && d->rvchancount == 0) 424bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, 1, -1); 425bba4862cSAriff Abdullah 426bba4862cSAriff Abdullah pcm_clonereset(d); 42767b1dce3SCameron Grant } 42867b1dce3SCameron Grant 42982db23e2SCameron Grant #ifdef USING_DEVFS 43033dbf14aSCameron Grant static int 431851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 43233dbf14aSCameron Grant { 433b8f0d9e0SCameron Grant struct snddev_info *d; 43433dbf14aSCameron Grant int error, unit; 43533dbf14aSCameron Grant 43633dbf14aSCameron Grant unit = snd_unit; 437041b706bSDavid Malone error = sysctl_handle_int(oidp, &unit, 0, req); 43833dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 439b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 440e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 441b8f0d9e0SCameron Grant return EINVAL; 44233dbf14aSCameron Grant snd_unit = unit; 44333dbf14aSCameron Grant } 44433dbf14aSCameron Grant return (error); 44533dbf14aSCameron Grant } 446851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */ 447851a904aSAlexander Leidinger SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW, 448a580b31aSAriff Abdullah 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device"); 44982db23e2SCameron Grant #endif 450987e5972SCameron Grant 451cd9766c5SCameron Grant static int 45267b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 453cd9766c5SCameron Grant { 45467b1dce3SCameron Grant struct snddev_info *d; 45567b1dce3SCameron Grant int i, v, error; 456cd9766c5SCameron Grant 45767b1dce3SCameron Grant v = snd_maxautovchans; 458041b706bSDavid Malone error = sysctl_handle_int(oidp, &v, 0, req); 459cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 460bba4862cSAriff Abdullah if (v < 0) 461bba4862cSAriff Abdullah v = 0; 462bba4862cSAriff Abdullah if (v > SND_MAXVCHANS) 463bba4862cSAriff Abdullah v = SND_MAXVCHANS; 464bba4862cSAriff Abdullah snd_maxautovchans = v; 4659c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 4669c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 46767b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 468e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 46967b1dce3SCameron Grant continue; 470e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 47167b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 472e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 47367b1dce3SCameron Grant } 47467b1dce3SCameron Grant } 475cd9766c5SCameron Grant return (error); 476cd9766c5SCameron Grant } 47767b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 478a580b31aSAriff Abdullah 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 479f637a36cSCameron Grant 480285648f9SCameron Grant struct pcm_channel * 481bba4862cSAriff Abdullah pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo) 482987e5972SCameron Grant { 483bba4862cSAriff Abdullah struct pcm_channel *ch; 484bba4862cSAriff Abdullah int direction, err, rpnum, *pnum, max; 485bba4862cSAriff Abdullah int udc, device, chan; 486bba4862cSAriff Abdullah char *dirs, *devname, buf[CHN_NAMELEN]; 487bba4862cSAriff Abdullah 488e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 489e4e61333SAriff Abdullah snd_mtxassert(d->lock); 490bba4862cSAriff Abdullah KASSERT(num >= -1, ("invalid num=%d", num)); 491bba4862cSAriff Abdullah 492987e5972SCameron Grant 493285648f9SCameron Grant switch (dir) { 494285648f9SCameron Grant case PCMDIR_PLAY: 495285648f9SCameron Grant dirs = "play"; 496a3193a9cSDon Lewis direction = PCMDIR_PLAY; 497a67fe5c1SCameron Grant pnum = &d->playcount; 498bba4862cSAriff Abdullah device = SND_DEV_DSPHW_PLAY; 499bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 500285648f9SCameron Grant break; 501bba4862cSAriff Abdullah case PCMDIR_PLAY_VIRTUAL: 502bba4862cSAriff Abdullah dirs = "virtual"; 503bba4862cSAriff Abdullah direction = PCMDIR_PLAY; 504bba4862cSAriff Abdullah pnum = &d->pvchancount; 505bba4862cSAriff Abdullah device = SND_DEV_DSPHW_VPLAY; 506bba4862cSAriff Abdullah max = SND_MAXVCHANS; 507bba4862cSAriff Abdullah break; 508285648f9SCameron Grant case PCMDIR_REC: 509285648f9SCameron Grant dirs = "record"; 510a3193a9cSDon Lewis direction = PCMDIR_REC; 511a67fe5c1SCameron Grant pnum = &d->reccount; 512bba4862cSAriff Abdullah device = SND_DEV_DSPHW_REC; 513bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 514285648f9SCameron Grant break; 515bba4862cSAriff Abdullah case PCMDIR_REC_VIRTUAL: 516285648f9SCameron Grant dirs = "virtual"; 517bba4862cSAriff Abdullah direction = PCMDIR_REC; 518bba4862cSAriff Abdullah pnum = &d->rvchancount; 519bba4862cSAriff Abdullah device = SND_DEV_DSPHW_VREC; 520bba4862cSAriff Abdullah max = SND_MAXVCHANS; 521285648f9SCameron Grant break; 522285648f9SCameron Grant default: 523e4e61333SAriff Abdullah return (NULL); 5249c326820SCameron Grant } 525285648f9SCameron Grant 526bba4862cSAriff Abdullah chan = (num == -1) ? 0 : num; 527285648f9SCameron Grant 528e4e61333SAriff Abdullah if (*pnum >= max || chan >= max) 529e4e61333SAriff Abdullah return (NULL); 530bba4862cSAriff Abdullah 5313fdb3676SAriff Abdullah rpnum = 0; 532bba4862cSAriff Abdullah 533bba4862cSAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 534bba4862cSAriff Abdullah if (CHN_DEV(ch) != device) 5353fdb3676SAriff Abdullah continue; 536bba4862cSAriff Abdullah if (chan == CHN_CHAN(ch)) { 537bba4862cSAriff Abdullah if (num != -1) { 5383fdb3676SAriff Abdullah device_printf(d->dev, 539bba4862cSAriff Abdullah "channel num=%d allocated!\n", chan); 540e4e61333SAriff Abdullah return (NULL); 541bba4862cSAriff Abdullah } 542bba4862cSAriff Abdullah chan++; 543bba4862cSAriff Abdullah if (chan >= max) { 544bba4862cSAriff Abdullah device_printf(d->dev, 545bba4862cSAriff Abdullah "chan=%d > %d\n", chan, max); 546e4e61333SAriff Abdullah return (NULL); 547bba4862cSAriff Abdullah } 5483fdb3676SAriff Abdullah } 5493fdb3676SAriff Abdullah rpnum++; 5503fdb3676SAriff Abdullah } 551bba4862cSAriff Abdullah 5523fdb3676SAriff Abdullah if (*pnum != rpnum) { 5533fdb3676SAriff Abdullah device_printf(d->dev, 554bba4862cSAriff Abdullah "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", 5553fdb3676SAriff Abdullah __func__, dirs, *pnum, rpnum); 556e4e61333SAriff Abdullah return (NULL); 5573fdb3676SAriff Abdullah } 558a67fe5c1SCameron Grant 559bba4862cSAriff Abdullah udc = snd_mkunit(device_get_unit(d->dev), device, chan); 560bba4862cSAriff Abdullah devname = dsp_unit2name(buf, sizeof(buf), udc); 561bba4862cSAriff Abdullah 562bba4862cSAriff Abdullah if (devname == NULL) { 563bba4862cSAriff Abdullah device_printf(d->dev, 564bba4862cSAriff Abdullah "Failed to query device name udc=0x%08x\n", udc); 565e4e61333SAriff Abdullah return (NULL); 566bba4862cSAriff Abdullah } 567bba4862cSAriff Abdullah 568bba4862cSAriff Abdullah pcm_unlock(d); 569bba4862cSAriff Abdullah ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 570bba4862cSAriff Abdullah ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); 571bba4862cSAriff Abdullah ch->unit = udc; 572285648f9SCameron Grant ch->pid = -1; 573285648f9SCameron Grant ch->parentsnddev = d; 574285648f9SCameron Grant ch->parentchannel = parent; 575436c9b65SScott Long ch->dev = d->dev; 576bba4862cSAriff Abdullah ch->trigger = PCMTRIG_STOP; 577bba4862cSAriff Abdullah snprintf(ch->name, sizeof(ch->name), "%s:%s:%s", 578bba4862cSAriff Abdullah device_get_nameunit(ch->dev), dirs, devname); 579285648f9SCameron Grant 580a3193a9cSDon Lewis err = chn_init(ch, devinfo, dir, direction); 581e4e61333SAriff Abdullah pcm_lock(d); 5820f55ac6cSCameron Grant if (err) { 583bba4862cSAriff Abdullah device_printf(d->dev, "chn_init(%s) failed: err = %d\n", 584bba4862cSAriff Abdullah ch->name, err); 585285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 586285648f9SCameron Grant free(ch, M_DEVBUF); 587e4e61333SAriff Abdullah return (NULL); 588bbb5bf3dSCameron Grant } 589285648f9SCameron Grant 590e4e61333SAriff Abdullah return (ch); 591285648f9SCameron Grant } 592285648f9SCameron Grant 593285648f9SCameron Grant int 594285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 595285648f9SCameron Grant { 596a67fe5c1SCameron Grant struct snddev_info *d; 597285648f9SCameron Grant int err; 598285648f9SCameron Grant 599a67fe5c1SCameron Grant d = ch->parentsnddev; 600e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 601e4e61333SAriff Abdullah 602285648f9SCameron Grant err = chn_kill(ch); 603285648f9SCameron Grant if (err) { 604e4e61333SAriff Abdullah device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n", 605e4e61333SAriff Abdullah ch->name, err); 606e4e61333SAriff Abdullah return (err); 607285648f9SCameron Grant } 608285648f9SCameron Grant 609285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 610285648f9SCameron Grant free(ch, M_DEVBUF); 611285648f9SCameron Grant 612e4e61333SAriff Abdullah return (0); 613285648f9SCameron Grant } 614285648f9SCameron Grant 615285648f9SCameron Grant int 6165ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 617285648f9SCameron Grant { 618bba4862cSAriff Abdullah struct pcm_channel *tmp, *after; 619bba4862cSAriff Abdullah int num; 6205ee30e27SMathew Kanner 621e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 622e4e61333SAriff Abdullah snd_mtxassert(d->lock); 623bba4862cSAriff Abdullah KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 624bba4862cSAriff Abdullah ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 625285648f9SCameron Grant 6263fdb3676SAriff Abdullah after = NULL; 627bba4862cSAriff Abdullah tmp = NULL; 628bba4862cSAriff Abdullah num = 0; 629bba4862cSAriff Abdullah 6303fdb3676SAriff Abdullah /* 631bba4862cSAriff Abdullah * Look for possible device collision. 6323fdb3676SAriff Abdullah */ 633bba4862cSAriff Abdullah CHN_FOREACH(tmp, d, channels.pcm) { 634bba4862cSAriff Abdullah if (tmp->unit == ch->unit) { 635bba4862cSAriff Abdullah device_printf(d->dev, "%s(): Device collision " 636bba4862cSAriff Abdullah "old=%p new=%p devunit=0x%08x\n", 637bba4862cSAriff Abdullah __func__, tmp, ch, ch->unit); 638e4e61333SAriff Abdullah return (ENODEV); 6397982e7a4SAriff Abdullah } 640bba4862cSAriff Abdullah if (CHN_DEV(tmp) < CHN_DEV(ch)) { 641bba4862cSAriff Abdullah if (num == 0) 6423fdb3676SAriff Abdullah after = tmp; 643bba4862cSAriff Abdullah continue; 644bba4862cSAriff Abdullah } else if (CHN_DEV(tmp) > CHN_DEV(ch)) 645bba4862cSAriff Abdullah break; 646bba4862cSAriff Abdullah num++; 647bba4862cSAriff Abdullah if (CHN_CHAN(tmp) < CHN_CHAN(ch)) 648bba4862cSAriff Abdullah after = tmp; 649bba4862cSAriff Abdullah else if (CHN_CHAN(tmp) > CHN_CHAN(ch)) 650bba4862cSAriff Abdullah break; 6517982e7a4SAriff Abdullah } 652bba4862cSAriff Abdullah 653bba4862cSAriff Abdullah if (after != NULL) { 654bba4862cSAriff Abdullah CHN_INSERT_AFTER(after, ch, channels.pcm); 65597d69a96SAlexander Leidinger } else { 656bba4862cSAriff Abdullah CHN_INSERT_HEAD(d, ch, channels.pcm); 65767b1dce3SCameron Grant } 6583fdb3676SAriff Abdullah 659e4e61333SAriff Abdullah switch (CHN_DEV(ch)) { 660e4e61333SAriff Abdullah case SND_DEV_DSPHW_PLAY: 661e4e61333SAriff Abdullah d->playcount++; 662e4e61333SAriff Abdullah break; 663e4e61333SAriff Abdullah case SND_DEV_DSPHW_VPLAY: 664e4e61333SAriff Abdullah d->pvchancount++; 665e4e61333SAriff Abdullah break; 666e4e61333SAriff Abdullah case SND_DEV_DSPHW_REC: 667e4e61333SAriff Abdullah d->reccount++; 668e4e61333SAriff Abdullah break; 669e4e61333SAriff Abdullah case SND_DEV_DSPHW_VREC: 670e4e61333SAriff Abdullah d->rvchancount++; 671e4e61333SAriff Abdullah break; 672e4e61333SAriff Abdullah default: 673e4e61333SAriff Abdullah break; 674e4e61333SAriff Abdullah } 675b8f0d9e0SCameron Grant 676e4e61333SAriff Abdullah d->devcount++; 677e4e61333SAriff Abdullah 678e4e61333SAriff Abdullah return (0); 67933dbf14aSCameron Grant } 68033dbf14aSCameron Grant 681285648f9SCameron Grant int 6825ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 68333dbf14aSCameron Grant { 684bba4862cSAriff Abdullah struct pcm_channel *tmp; 68533dbf14aSCameron Grant 686e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 687e4e61333SAriff Abdullah snd_mtxassert(d->lock); 688e4e61333SAriff Abdullah 689bba4862cSAriff Abdullah tmp = NULL; 690a527dbc7SCameron Grant 691bba4862cSAriff Abdullah CHN_FOREACH(tmp, d, channels.pcm) { 692bba4862cSAriff Abdullah if (tmp == ch) 693bba4862cSAriff Abdullah break; 69433dbf14aSCameron Grant } 695bba4862cSAriff Abdullah 696bba4862cSAriff Abdullah if (tmp != ch) 697e4e61333SAriff Abdullah return (EINVAL); 69867beb5a5SCameron Grant 699bba4862cSAriff Abdullah CHN_REMOVE(d, ch, channels.pcm); 700e4e61333SAriff Abdullah 701bba4862cSAriff Abdullah switch (CHN_DEV(ch)) { 702bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 70367beb5a5SCameron Grant d->playcount--; 704bba4862cSAriff Abdullah break; 705bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 706bba4862cSAriff Abdullah d->pvchancount--; 707bba4862cSAriff Abdullah break; 708bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 709bba4862cSAriff Abdullah d->reccount--; 710bba4862cSAriff Abdullah break; 711bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 712bba4862cSAriff Abdullah d->rvchancount--; 713bba4862cSAriff Abdullah break; 714bba4862cSAriff Abdullah default: 715bba4862cSAriff Abdullah break; 716bba4862cSAriff Abdullah } 717285648f9SCameron Grant 718e4e61333SAriff Abdullah d->devcount--; 719e4e61333SAriff Abdullah 720e4e61333SAriff Abdullah return (0); 721987e5972SCameron Grant } 722987e5972SCameron Grant 723987e5972SCameron Grant int 724285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 725285648f9SCameron Grant { 726285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 72767b1dce3SCameron Grant struct pcm_channel *ch; 72867b1dce3SCameron Grant int err; 729285648f9SCameron Grant 730e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 731e4e61333SAriff Abdullah 732e4e61333SAriff Abdullah pcm_lock(d); 733bba4862cSAriff Abdullah ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); 734285648f9SCameron Grant if (!ch) { 735e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", 736e4e61333SAriff Abdullah cls->name, dir, devinfo); 737e4e61333SAriff Abdullah pcm_unlock(d); 738e4e61333SAriff Abdullah return (ENODEV); 739285648f9SCameron Grant } 740cd9766c5SCameron Grant 7415ee30e27SMathew Kanner err = pcm_chn_add(d, ch); 742e4e61333SAriff Abdullah pcm_unlock(d); 743285648f9SCameron Grant if (err) { 744e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", 745e4e61333SAriff Abdullah ch->name, err); 746285648f9SCameron Grant pcm_chn_destroy(ch); 747cd9766c5SCameron Grant } 748cd9766c5SCameron Grant 749e4e61333SAriff Abdullah return (err); 750285648f9SCameron Grant } 751285648f9SCameron Grant 752285648f9SCameron Grant static int 753285648f9SCameron Grant pcm_killchan(device_t dev) 754285648f9SCameron Grant { 755285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 756e33bee07SOlivier Houchard struct pcm_channel *ch; 757e4e61333SAriff Abdullah int error; 758e4e61333SAriff Abdullah 759e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 760285648f9SCameron Grant 761bba4862cSAriff Abdullah ch = CHN_FIRST(d, channels.pcm); 762285648f9SCameron Grant 763e4e61333SAriff Abdullah pcm_lock(d); 764bba4862cSAriff Abdullah error = pcm_chn_remove(d, ch); 765e4e61333SAriff Abdullah pcm_unlock(d); 766e33bee07SOlivier Houchard if (error) 767e33bee07SOlivier Houchard return (error); 768e33bee07SOlivier Houchard return (pcm_chn_destroy(ch)); 769285648f9SCameron Grant } 770285648f9SCameron Grant 771285648f9SCameron Grant int 772987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 773987e5972SCameron Grant { 77466ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 77549c5e6e2SCameron Grant 776e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 777bba4862cSAriff Abdullah 778e4e61333SAriff Abdullah if (d->playcount == 0 || d->reccount == 0) 779e4e61333SAriff Abdullah d->flags |= SD_F_SIMPLEX; 780e4e61333SAriff Abdullah 781e4e61333SAriff Abdullah if ((d->playcount > 0 || d->reccount > 0) && 782e4e61333SAriff Abdullah !(d->flags & SD_F_AUTOVCHAN)) { 783e4e61333SAriff Abdullah d->flags |= SD_F_AUTOVCHAN; 784e4e61333SAriff Abdullah vchan_initsys(dev); 785e4e61333SAriff Abdullah } 786e4e61333SAriff Abdullah 787e4e61333SAriff Abdullah pcm_setmaxautovchans(d, snd_maxautovchans); 788bba4862cSAriff Abdullah 789a580b31aSAriff Abdullah strlcpy(d->status, str, SND_STATUSLEN); 790bba4862cSAriff Abdullah 791e4e61333SAriff Abdullah pcm_lock(d); 792e4e61333SAriff Abdullah 793bba4862cSAriff Abdullah /* Last stage, enable cloning. */ 794e4e61333SAriff Abdullah if (d->clones != NULL) 795bba4862cSAriff Abdullah (void)snd_clone_enable(d->clones); 796e4e61333SAriff Abdullah 797e4e61333SAriff Abdullah /* Done, we're ready.. */ 798e4e61333SAriff Abdullah d->flags |= SD_F_REGISTERED; 799e4e61333SAriff Abdullah 800e4e61333SAriff Abdullah PCM_RELEASE(d); 801bba4862cSAriff Abdullah 802bba4862cSAriff Abdullah pcm_unlock(d); 803bba4862cSAriff Abdullah 804e4e61333SAriff Abdullah return (0); 805987e5972SCameron Grant } 806987e5972SCameron Grant 807a1d444e1SAriff Abdullah uint32_t 808987e5972SCameron Grant pcm_getflags(device_t dev) 809987e5972SCameron Grant { 81066ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 81149c5e6e2SCameron Grant 812987e5972SCameron Grant return d->flags; 813987e5972SCameron Grant } 814987e5972SCameron Grant 815987e5972SCameron Grant void 816a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val) 817987e5972SCameron Grant { 81866ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 819d95502a8SCameron Grant 820987e5972SCameron Grant d->flags = val; 821987e5972SCameron Grant } 822987e5972SCameron Grant 82339004e69SCameron Grant void * 82439004e69SCameron Grant pcm_getdevinfo(device_t dev) 82539004e69SCameron Grant { 82666ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 82749c5e6e2SCameron Grant 82839004e69SCameron Grant return d->devinfo; 82939004e69SCameron Grant } 83039004e69SCameron Grant 831a67fe5c1SCameron Grant unsigned int 832d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 833a67fe5c1SCameron Grant { 834a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 8354e60be34SCameron Grant int sz, x; 836a67fe5c1SCameron Grant 837a67fe5c1SCameron Grant sz = 0; 8384e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 8394e60be34SCameron Grant x = sz; 840d55d96f6SAlexander Leidinger RANGE(sz, minbufsz, maxbufsz); 8414e60be34SCameron Grant if (x != sz) 842d55d96f6SAlexander Leidinger device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 843d55d96f6SAlexander Leidinger x = minbufsz; 8444e60be34SCameron Grant while (x < sz) 8454e60be34SCameron Grant x <<= 1; 8464e60be34SCameron Grant if (x > sz) 8474e60be34SCameron Grant x >>= 1; 8484e60be34SCameron Grant if (x != sz) { 8494e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 8504e60be34SCameron Grant sz = x; 8514e60be34SCameron Grant } 8524e60be34SCameron Grant } else { 853a67fe5c1SCameron Grant sz = deflt; 8544e60be34SCameron Grant } 8554e60be34SCameron Grant 856a67fe5c1SCameron Grant d->bufsz = sz; 857a67fe5c1SCameron Grant 858a67fe5c1SCameron Grant return sz; 859a67fe5c1SCameron Grant } 860a67fe5c1SCameron Grant 861bba4862cSAriff Abdullah #if defined(SND_DYNSYSCTL) && defined(SND_DEBUG) 862bba4862cSAriff Abdullah static int 863bba4862cSAriff Abdullah sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS) 864bba4862cSAriff Abdullah { 865bba4862cSAriff Abdullah struct snddev_info *d; 866bba4862cSAriff Abdullah uint32_t flags; 867bba4862cSAriff Abdullah int err; 868bba4862cSAriff Abdullah 869bba4862cSAriff Abdullah d = oidp->oid_arg1; 870e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 871bba4862cSAriff Abdullah return (ENODEV); 872bba4862cSAriff Abdullah 873e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 874e4e61333SAriff Abdullah 875bba4862cSAriff Abdullah flags = snd_clone_getflags(d->clones); 876041b706bSDavid Malone err = sysctl_handle_int(oidp, &flags, 0, req); 877bba4862cSAriff Abdullah 878bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL) { 879e4e61333SAriff Abdullah if (flags & ~SND_CLONE_MASK) 880bba4862cSAriff Abdullah err = EINVAL; 881e4e61333SAriff Abdullah else 882bba4862cSAriff Abdullah (void)snd_clone_setflags(d->clones, flags); 883bba4862cSAriff Abdullah } 884e4e61333SAriff Abdullah 885e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 886bba4862cSAriff Abdullah 887bba4862cSAriff Abdullah return (err); 888bba4862cSAriff Abdullah } 889bba4862cSAriff Abdullah 890bba4862cSAriff Abdullah static int 891bba4862cSAriff Abdullah sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS) 892bba4862cSAriff Abdullah { 893bba4862cSAriff Abdullah struct snddev_info *d; 894bba4862cSAriff Abdullah int err, deadline; 895bba4862cSAriff Abdullah 896bba4862cSAriff Abdullah d = oidp->oid_arg1; 897e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 898bba4862cSAriff Abdullah return (ENODEV); 899bba4862cSAriff Abdullah 900e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 901e4e61333SAriff Abdullah 902bba4862cSAriff Abdullah deadline = snd_clone_getdeadline(d->clones); 903041b706bSDavid Malone err = sysctl_handle_int(oidp, &deadline, 0, req); 904bba4862cSAriff Abdullah 905bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL) { 906bba4862cSAriff Abdullah if (deadline < 0) 907bba4862cSAriff Abdullah err = EINVAL; 908e4e61333SAriff Abdullah else 909bba4862cSAriff Abdullah (void)snd_clone_setdeadline(d->clones, deadline); 910bba4862cSAriff Abdullah } 911e4e61333SAriff Abdullah 912e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 913bba4862cSAriff Abdullah 914bba4862cSAriff Abdullah return (err); 915bba4862cSAriff Abdullah } 916bba4862cSAriff Abdullah 917bba4862cSAriff Abdullah static int 918bba4862cSAriff Abdullah sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS) 919bba4862cSAriff Abdullah { 920bba4862cSAriff Abdullah struct snddev_info *d; 921bba4862cSAriff Abdullah int err, val; 922bba4862cSAriff Abdullah 923bba4862cSAriff Abdullah d = oidp->oid_arg1; 924e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 925bba4862cSAriff Abdullah return (ENODEV); 926bba4862cSAriff Abdullah 927bba4862cSAriff Abdullah val = 0; 928041b706bSDavid Malone err = sysctl_handle_int(oidp, &val, 0, req); 929bba4862cSAriff Abdullah 930bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL && val != 0) { 931e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 932e4e61333SAriff Abdullah val = snd_clone_gc(d->clones); 933e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 934e4e61333SAriff Abdullah if (bootverbose != 0 || snd_verbose > 3) 935e4e61333SAriff Abdullah device_printf(d->dev, "clone gc: pruned=%d\n", val); 936bba4862cSAriff Abdullah } 937bba4862cSAriff Abdullah 938bba4862cSAriff Abdullah return (err); 939bba4862cSAriff Abdullah } 940bba4862cSAriff Abdullah 941bba4862cSAriff Abdullah static int 942bba4862cSAriff Abdullah sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS) 943bba4862cSAriff Abdullah { 944bba4862cSAriff Abdullah struct snddev_info *d; 945bba4862cSAriff Abdullah int i, err, val; 946bba4862cSAriff Abdullah 947bba4862cSAriff Abdullah val = 0; 948041b706bSDavid Malone err = sysctl_handle_int(oidp, &val, 0, req); 949bba4862cSAriff Abdullah 950bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL && val != 0) { 9519c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 9529c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 953bba4862cSAriff Abdullah d = devclass_get_softc(pcm_devclass, i); 954e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 955bba4862cSAriff Abdullah continue; 956e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 957e4e61333SAriff Abdullah val = snd_clone_gc(d->clones); 958e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 959e4e61333SAriff Abdullah if (bootverbose != 0 || snd_verbose > 3) 960e4e61333SAriff Abdullah device_printf(d->dev, "clone gc: pruned=%d\n", 961e4e61333SAriff Abdullah val); 962bba4862cSAriff Abdullah } 963bba4862cSAriff Abdullah } 964bba4862cSAriff Abdullah 965bba4862cSAriff Abdullah return (err); 966bba4862cSAriff Abdullah } 967bba4862cSAriff Abdullah SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW, 968bba4862cSAriff Abdullah 0, sizeof(int), sysctl_hw_snd_clone_gc, "I", 969bba4862cSAriff Abdullah "global clone garbage collector"); 970bba4862cSAriff Abdullah #endif 971bba4862cSAriff Abdullah 972987e5972SCameron Grant int 973987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 974987e5972SCameron Grant { 975bba4862cSAriff Abdullah struct snddev_info *d; 976987e5972SCameron Grant 977b8a36395SCameron Grant if (pcm_veto_load) { 978b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 979b8a36395SCameron Grant 980b8a36395SCameron Grant return EINVAL; 981b8a36395SCameron Grant } 982b8a36395SCameron Grant 983bba4862cSAriff Abdullah if (device_get_unit(dev) > PCMMAXUNIT) { 984bba4862cSAriff Abdullah device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n", 985bba4862cSAriff Abdullah device_get_unit(dev), PCMMAXUNIT); 986bba4862cSAriff Abdullah device_printf(dev, 987bba4862cSAriff Abdullah "Use 'hw.snd.maxunit' tunable to raise the limit.\n"); 988bba4862cSAriff Abdullah return ENODEV; 989bba4862cSAriff Abdullah } 990bba4862cSAriff Abdullah 991bba4862cSAriff Abdullah d = device_get_softc(dev); 992e4e61333SAriff Abdullah d->dev = dev; 9932c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 994e4e61333SAriff Abdullah cv_init(&d->cv, device_get_nameunit(dev)); 995e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 996e4e61333SAriff Abdullah dsp_cdevinfo_init(d); 9977233ababSAlexander Leidinger #if 0 9987233ababSAlexander Leidinger /* 9997233ababSAlexander Leidinger * d->flags should be cleared by the allocator of the softc. 10007233ababSAlexander Leidinger * We cannot clear this field here because several devices set 10017233ababSAlexander Leidinger * this flag before calling pcm_register(). 10027233ababSAlexander Leidinger */ 1003cd9766c5SCameron Grant d->flags = 0; 10047233ababSAlexander Leidinger #endif 1005987e5972SCameron Grant d->devinfo = devinfo; 1006f637a36cSCameron Grant d->devcount = 0; 1007506a5308SCameron Grant d->reccount = 0; 1008a67fe5c1SCameron Grant d->playcount = 0; 1009bba4862cSAriff Abdullah d->pvchancount = 0; 1010bba4862cSAriff Abdullah d->rvchancount = 0; 1011bba4862cSAriff Abdullah d->pvchanrate = 0; 1012bba4862cSAriff Abdullah d->pvchanformat = 0; 1013bba4862cSAriff Abdullah d->rvchanrate = 0; 1014bba4862cSAriff Abdullah d->rvchanformat = 0; 1015d95502a8SCameron Grant d->inprog = 0; 1016833f7023SCameron Grant 1017bba4862cSAriff Abdullah /* 1018bba4862cSAriff Abdullah * Create clone manager, disabled by default. Cloning will be 1019bba4862cSAriff Abdullah * enabled during final stage of driver iniialization through 1020bba4862cSAriff Abdullah * pcm_setstatus(). 1021bba4862cSAriff Abdullah */ 1022e4e61333SAriff Abdullah d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE, 1023e4e61333SAriff Abdullah SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK | 1024bba4862cSAriff Abdullah SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF | 1025bba4862cSAriff Abdullah SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED); 1026bba4862cSAriff Abdullah 1027bba4862cSAriff Abdullah if (bootverbose != 0 || snd_verbose > 3) { 1028bba4862cSAriff Abdullah device_printf(dev, 1029bba4862cSAriff Abdullah "clone manager: deadline=%dms flags=0x%08x\n", 1030bba4862cSAriff Abdullah snd_clone_getdeadline(d->clones), 1031bba4862cSAriff Abdullah snd_clone_getflags(d->clones)); 1032bba4862cSAriff Abdullah } 1033bba4862cSAriff Abdullah 1034bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm); 1035bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm.busy); 103645550658SPoul-Henning Kamp 1037e4e61333SAriff Abdullah /* XXX This is incorrect, but lets play along for now. */ 1038a1d444e1SAriff Abdullah if ((numplay == 0 || numrec == 0) && numplay != numrec) 1039285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 1040d95502a8SCameron Grant 1041285648f9SCameron Grant d->fakechan = fkchan_setup(dev); 1042a3193a9cSDon Lewis chn_init(d->fakechan, NULL, 0, 0); 1043987e5972SCameron Grant 104482db23e2SCameron Grant #ifdef SND_DYNSYSCTL 1045bba4862cSAriff Abdullah sysctl_ctx_init(&d->play_sysctl_ctx); 1046bba4862cSAriff Abdullah d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 1047bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 1048bba4862cSAriff Abdullah CTLFLAG_RD, 0, "playback channels node"); 1049bba4862cSAriff Abdullah sysctl_ctx_init(&d->rec_sysctl_ctx); 1050bba4862cSAriff Abdullah d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 1051bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 1052bba4862cSAriff Abdullah CTLFLAG_RD, 0, "record channels node"); 1053851a904aSAlexander Leidinger /* XXX: an user should be able to set this with a control tool, the 1054851a904aSAlexander Leidinger sysadmin then needs min+max sysctls for this */ 1055a580b31aSAriff Abdullah SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 1056a580b31aSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 1057a580b31aSAriff Abdullah OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 1058bba4862cSAriff Abdullah #ifdef SND_DEBUG 1059bba4862cSAriff Abdullah SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1060bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1061bba4862cSAriff Abdullah "clone_flags", CTLTYPE_UINT | CTLFLAG_RW, d, sizeof(d), 1062bba4862cSAriff Abdullah sysctl_dev_pcm_clone_flags, "IU", 1063bba4862cSAriff Abdullah "clone flags"); 1064bba4862cSAriff Abdullah SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1065bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1066bba4862cSAriff Abdullah "clone_deadline", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 1067bba4862cSAriff Abdullah sysctl_dev_pcm_clone_deadline, "I", 1068bba4862cSAriff Abdullah "clone expiration deadline (ms)"); 1069bba4862cSAriff Abdullah SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1070bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1071bba4862cSAriff Abdullah "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 1072bba4862cSAriff Abdullah sysctl_dev_pcm_clone_gc, "I", 1073bba4862cSAriff Abdullah "clone garbage collector"); 107482db23e2SCameron Grant #endif 1075bba4862cSAriff Abdullah #endif 1076e4e61333SAriff Abdullah 1077bba4862cSAriff Abdullah if (numplay > 0 || numrec > 0) { 1078cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 10793fdb3676SAriff Abdullah vchan_initsys(dev); 108097d69a96SAlexander Leidinger } 1081cd9766c5SCameron Grant 108267b1dce3SCameron Grant sndstat_register(dev, d->status, sndstat_prepare_pcm); 1083e4e61333SAriff Abdullah 1084987e5972SCameron Grant return 0; 1085987e5972SCameron Grant } 1086987e5972SCameron Grant 108733dbf14aSCameron Grant int 108833dbf14aSCameron Grant pcm_unregister(device_t dev) 10897c438dbeSCameron Grant { 1090e4e61333SAriff Abdullah struct snddev_info *d; 1091a67fe5c1SCameron Grant struct pcm_channel *ch; 1092bba4862cSAriff Abdullah struct thread *td; 1093bba4862cSAriff Abdullah int i; 10947c438dbeSCameron Grant 1095bba4862cSAriff Abdullah td = curthread; 1096e4e61333SAriff Abdullah d = device_get_softc(dev); 1097e4e61333SAriff Abdullah 1098e4e61333SAriff Abdullah if (!PCM_ALIVE(d)) { 1099e4e61333SAriff Abdullah device_printf(dev, "unregister: device not configured\n"); 1100e4e61333SAriff Abdullah return (0); 1101e4e61333SAriff Abdullah } 1102bba4862cSAriff Abdullah 1103bba4862cSAriff Abdullah if (sndstat_acquire(td) != 0) { 110428ef3fb0SAlexander Leidinger device_printf(dev, "unregister: sndstat busy\n"); 1105e4e61333SAriff Abdullah return (EBUSY); 110628ef3fb0SAlexander Leidinger } 110728ef3fb0SAlexander Leidinger 1108bba4862cSAriff Abdullah pcm_lock(d); 1109e4e61333SAriff Abdullah PCM_WAIT(d); 1110e4e61333SAriff Abdullah 1111e4e61333SAriff Abdullah if (d->inprog != 0) { 11125c25132aSGeorge C A Reid device_printf(dev, "unregister: operation in progress\n"); 1113bba4862cSAriff Abdullah pcm_unlock(d); 1114bba4862cSAriff Abdullah sndstat_release(td); 1115e4e61333SAriff Abdullah return (EBUSY); 11165c25132aSGeorge C A Reid } 11175ee30e27SMathew Kanner 1118e4e61333SAriff Abdullah PCM_ACQUIRE(d); 1119bba4862cSAriff Abdullah pcm_unlock(d); 1120e4e61333SAriff Abdullah 1121e4e61333SAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 1122e4e61333SAriff Abdullah CHN_LOCK(ch); 1123e4e61333SAriff Abdullah if (ch->refcount > 0) { 1124e4e61333SAriff Abdullah device_printf(dev, 1125e4e61333SAriff Abdullah "unregister: channel %s busy (pid %d)\n", 1126e4e61333SAriff Abdullah ch->name, ch->pid); 1127e4e61333SAriff Abdullah CHN_UNLOCK(ch); 1128e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 1129bba4862cSAriff Abdullah sndstat_release(td); 1130e4e61333SAriff Abdullah return (EBUSY); 1131285648f9SCameron Grant } 1132e4e61333SAriff Abdullah CHN_UNLOCK(ch); 1133c9b53085SCameron Grant } 11345ee30e27SMathew Kanner 1135bba4862cSAriff Abdullah if (d->clones != NULL) { 1136bba4862cSAriff Abdullah if (snd_clone_busy(d->clones) != 0) { 1137bba4862cSAriff Abdullah device_printf(dev, "unregister: clone busy\n"); 1138e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 1139bba4862cSAriff Abdullah sndstat_release(td); 1140e4e61333SAriff Abdullah return (EBUSY); 1141e4e61333SAriff Abdullah } else { 1142e4e61333SAriff Abdullah pcm_lock(d); 1143bba4862cSAriff Abdullah (void)snd_clone_disable(d->clones); 1144e4e61333SAriff Abdullah pcm_unlock(d); 1145e4e61333SAriff Abdullah } 1146bba4862cSAriff Abdullah } 1147bba4862cSAriff Abdullah 1148b4221868SAriff Abdullah if (mixer_uninit(dev) == EBUSY) { 11497233ababSAlexander Leidinger device_printf(dev, "unregister: mixer busy\n"); 1150e4e61333SAriff Abdullah pcm_lock(d); 1151bba4862cSAriff Abdullah if (d->clones != NULL) 1152bba4862cSAriff Abdullah (void)snd_clone_enable(d->clones); 1153e4e61333SAriff Abdullah PCM_RELEASE(d); 1154bba4862cSAriff Abdullah pcm_unlock(d); 1155bba4862cSAriff Abdullah sndstat_release(td); 1156e4e61333SAriff Abdullah return (EBUSY); 11577233ababSAlexander Leidinger } 11587233ababSAlexander Leidinger 1159e4e61333SAriff Abdullah pcm_lock(d); 1160e4e61333SAriff Abdullah d->flags |= SD_F_DYING; 1161e4e61333SAriff Abdullah d->flags &= ~SD_F_REGISTERED; 1162e4e61333SAriff Abdullah pcm_unlock(d); 1163e4e61333SAriff Abdullah 1164e4e61333SAriff Abdullah /* 1165e4e61333SAriff Abdullah * No lock being held, so this thing can be flushed without 1166e4e61333SAriff Abdullah * stucking into devdrn oblivion. 1167e4e61333SAriff Abdullah */ 1168bba4862cSAriff Abdullah if (d->clones != NULL) { 1169bba4862cSAriff Abdullah snd_clone_destroy(d->clones); 1170bba4862cSAriff Abdullah d->clones = NULL; 11717982e7a4SAriff Abdullah } 1172bba4862cSAriff Abdullah 117366ef8af5SCameron Grant #ifdef SND_DYNSYSCTL 1174bba4862cSAriff Abdullah if (d->play_sysctl_tree != NULL) { 1175bba4862cSAriff Abdullah sysctl_ctx_free(&d->play_sysctl_ctx); 1176bba4862cSAriff Abdullah d->play_sysctl_tree = NULL; 1177bba4862cSAriff Abdullah } 1178bba4862cSAriff Abdullah if (d->rec_sysctl_tree != NULL) { 1179bba4862cSAriff Abdullah sysctl_ctx_free(&d->rec_sysctl_ctx); 1180bba4862cSAriff Abdullah d->rec_sysctl_tree = NULL; 1181a1d444e1SAriff Abdullah } 1182a1d444e1SAriff Abdullah #endif 1183bba4862cSAriff Abdullah 1184bba4862cSAriff Abdullah while (!CHN_EMPTY(d, channels.pcm)) 1185285648f9SCameron Grant pcm_killchan(dev); 11867c438dbeSCameron Grant 118766ef8af5SCameron Grant chn_kill(d->fakechan); 118866ef8af5SCameron Grant fkchan_kill(d->fakechan); 118982db23e2SCameron Grant 1190e4e61333SAriff Abdullah dsp_cdevinfo_flush(d); 1191e4e61333SAriff Abdullah 1192e4e61333SAriff Abdullah pcm_lock(d); 1193e4e61333SAriff Abdullah PCM_RELEASE(d); 1194e4e61333SAriff Abdullah cv_destroy(&d->cv); 1195bba4862cSAriff Abdullah pcm_unlock(d); 119649c5e6e2SCameron Grant snd_mtxfree(d->lock); 119728ef3fb0SAlexander Leidinger sndstat_unregister(dev); 1198bba4862cSAriff Abdullah sndstat_release(td); 1199bba4862cSAriff Abdullah 1200bba4862cSAriff Abdullah if (snd_unit == device_get_unit(dev)) { 1201bba4862cSAriff Abdullah /* 1202bba4862cSAriff Abdullah * Reassign default unit to the next available dev. 1203bba4862cSAriff Abdullah */ 12049c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 12059c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 1206e4e61333SAriff Abdullah if (device_get_unit(dev) == i) 1207bba4862cSAriff Abdullah continue; 1208e4e61333SAriff Abdullah d = devclass_get_softc(pcm_devclass, i); 1209e4e61333SAriff Abdullah if (PCM_REGISTERED(d)) { 1210bba4862cSAriff Abdullah snd_unit = i; 1211bba4862cSAriff Abdullah break; 1212bba4862cSAriff Abdullah } 1213bba4862cSAriff Abdullah } 1214e4e61333SAriff Abdullah } 1215bba4862cSAriff Abdullah 1216e4e61333SAriff Abdullah return (0); 121733dbf14aSCameron Grant } 12187c438dbeSCameron Grant 121967b1dce3SCameron Grant /************************************************************************/ 122067b1dce3SCameron Grant 122167b1dce3SCameron Grant static int 122267b1dce3SCameron Grant sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 122367b1dce3SCameron Grant { 122467b1dce3SCameron Grant struct snddev_info *d; 122567b1dce3SCameron Grant struct pcm_channel *c; 122667b1dce3SCameron Grant struct pcm_feeder *f; 122767b1dce3SCameron Grant 122867b1dce3SCameron Grant if (verbose < 1) 122967b1dce3SCameron Grant return 0; 123067b1dce3SCameron Grant 123167b1dce3SCameron Grant d = device_get_softc(dev); 123267b1dce3SCameron Grant if (!d) 123367b1dce3SCameron Grant return ENXIO; 123467b1dce3SCameron Grant 1235e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 1236e4e61333SAriff Abdullah 1237e4e61333SAriff Abdullah if (CHN_EMPTY(d, channels.pcm)) { 1238e4e61333SAriff Abdullah sbuf_printf(s, " (mixer only)"); 1239e4e61333SAriff Abdullah return 0; 1240e4e61333SAriff Abdullah } 1241e4e61333SAriff Abdullah 1242bba4862cSAriff Abdullah sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", 1243bba4862cSAriff Abdullah d->playcount, d->pvchancount, 1244bba4862cSAriff Abdullah d->reccount, d->rvchancount, 124567b1dce3SCameron Grant (d->flags & SD_F_SIMPLEX)? "" : " duplex", 124667b1dce3SCameron Grant #ifdef USING_DEVFS 124767b1dce3SCameron Grant (device_get_unit(dev) == snd_unit)? " default" : "" 124867b1dce3SCameron Grant #else 124967b1dce3SCameron Grant "" 125067b1dce3SCameron Grant #endif 125167b1dce3SCameron Grant ); 1252a527dbc7SCameron Grant 1253e4e61333SAriff Abdullah if (verbose <= 1) 1254a527dbc7SCameron Grant return 0; 1255a527dbc7SCameron Grant 1256bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 12579d978cc7SAlexander Leidinger 12589d978cc7SAlexander Leidinger KASSERT(c->bufhard != NULL && c->bufsoft != NULL, 12599d978cc7SAlexander Leidinger ("hosed pcm channel setup")); 12609d978cc7SAlexander Leidinger 1261a3285889SCameron Grant sbuf_printf(s, "\n\t"); 1262a3285889SCameron Grant 126345550658SPoul-Henning Kamp /* it would be better to indent child channels */ 1264a3285889SCameron Grant sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 12654c68642aSCameron Grant sbuf_printf(s, "spd %d", c->speed); 12664c68642aSCameron Grant if (c->speed != sndbuf_getspd(c->bufhard)) 12674c68642aSCameron Grant sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 12684c68642aSCameron Grant sbuf_printf(s, ", fmt 0x%08x", c->format); 12694c68642aSCameron Grant if (c->format != sndbuf_getfmt(c->bufhard)) 12704c68642aSCameron Grant sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 1271a527dbc7SCameron Grant sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 127267b1dce3SCameron Grant if (c->pid != -1) 127367b1dce3SCameron Grant sbuf_printf(s, ", pid %d", c->pid); 127467b1dce3SCameron Grant sbuf_printf(s, "\n\t"); 12754c68642aSCameron Grant 1276a3285889SCameron Grant sbuf_printf(s, "interrupts %d, ", c->interrupts); 1277a3285889SCameron Grant if (c->direction == PCMDIR_REC) 1278a580b31aSAriff Abdullah sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 1279a580b31aSAriff Abdullah c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), 128097d69a96SAlexander Leidinger sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 128197d69a96SAlexander Leidinger sndbuf_getblkcnt(c->bufhard), 128297d69a96SAlexander Leidinger sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 128397d69a96SAlexander Leidinger sndbuf_getblkcnt(c->bufsoft)); 1284a3285889SCameron Grant else 1285a580b31aSAriff Abdullah sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", 1286a580b31aSAriff Abdullah c->xruns, c->feedcount, sndbuf_getready(c->bufsoft), 128797d69a96SAlexander Leidinger sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 128897d69a96SAlexander Leidinger sndbuf_getblkcnt(c->bufhard), 128997d69a96SAlexander Leidinger sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 129097d69a96SAlexander Leidinger sndbuf_getblkcnt(c->bufsoft)); 1291a3285889SCameron Grant sbuf_printf(s, "\n\t"); 12924c68642aSCameron Grant 12934c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 12944c68642aSCameron Grant sbuf_printf(s, " -> "); 129567b1dce3SCameron Grant f = c->feeder; 12964c68642aSCameron Grant while (f->source != NULL) 12974c68642aSCameron Grant f = f->source; 12984c68642aSCameron Grant while (f != NULL) { 129967b1dce3SCameron Grant sbuf_printf(s, "%s", f->class->name); 130067b1dce3SCameron Grant if (f->desc->type == FEEDER_FMT) 13014c68642aSCameron Grant sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 130267b1dce3SCameron Grant if (f->desc->type == FEEDER_RATE) 13034c68642aSCameron Grant sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 130428ef3fb0SAlexander Leidinger if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || 130528ef3fb0SAlexander Leidinger f->desc->type == FEEDER_VOLUME) 13064c68642aSCameron Grant sbuf_printf(s, "(0x%08x)", f->desc->out); 13074c68642aSCameron Grant sbuf_printf(s, " -> "); 13084c68642aSCameron Grant f = f->parent; 130967b1dce3SCameron Grant } 13104c68642aSCameron Grant sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 131167b1dce3SCameron Grant } 131267b1dce3SCameron Grant 131367b1dce3SCameron Grant return 0; 131467b1dce3SCameron Grant } 131567b1dce3SCameron Grant 131667b1dce3SCameron Grant /************************************************************************/ 131767b1dce3SCameron Grant 131867b1dce3SCameron Grant #ifdef SND_DYNSYSCTL 131967b1dce3SCameron Grant int 132067b1dce3SCameron Grant sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 132167b1dce3SCameron Grant { 132267b1dce3SCameron Grant struct snddev_info *d; 1323bba4862cSAriff Abdullah int direction, vchancount; 1324e4e61333SAriff Abdullah int err, cnt; 132567b1dce3SCameron Grant 1326bba4862cSAriff Abdullah d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 1327e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 1328e4e61333SAriff Abdullah return (EINVAL); 1329e4e61333SAriff Abdullah 1330e4e61333SAriff Abdullah pcm_lock(d); 1331e4e61333SAriff Abdullah PCM_WAIT(d); 133267b1dce3SCameron Grant 1333bba4862cSAriff Abdullah switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 1334bba4862cSAriff Abdullah case VCHAN_PLAY: 1335bba4862cSAriff Abdullah direction = PCMDIR_PLAY; 1336bba4862cSAriff Abdullah vchancount = d->pvchancount; 1337e4e61333SAriff Abdullah cnt = d->playcount; 1338bba4862cSAriff Abdullah break; 1339bba4862cSAriff Abdullah case VCHAN_REC: 1340bba4862cSAriff Abdullah direction = PCMDIR_REC; 1341bba4862cSAriff Abdullah vchancount = d->rvchancount; 1342e4e61333SAriff Abdullah cnt = d->reccount; 1343bba4862cSAriff Abdullah break; 1344bba4862cSAriff Abdullah default: 1345e4e61333SAriff Abdullah pcm_unlock(d); 1346e4e61333SAriff Abdullah return (EINVAL); 1347bba4862cSAriff Abdullah break; 1348bba4862cSAriff Abdullah } 1349bba4862cSAriff Abdullah 1350e4e61333SAriff Abdullah if (cnt < 1) { 1351e4e61333SAriff Abdullah pcm_unlock(d); 1352e4e61333SAriff Abdullah return (ENODEV); 1353bba4862cSAriff Abdullah } 1354a527dbc7SCameron Grant 1355e4e61333SAriff Abdullah PCM_ACQUIRE(d); 1356e4e61333SAriff Abdullah pcm_unlock(d); 1357e4e61333SAriff Abdullah 1358e4e61333SAriff Abdullah cnt = vchancount; 1359e4e61333SAriff Abdullah err = sysctl_handle_int(oidp, &cnt, 0, req); 1360e4e61333SAriff Abdullah 1361e4e61333SAriff Abdullah if (err == 0 && req->newptr != NULL && vchancount != cnt) { 1362e4e61333SAriff Abdullah if (cnt < 0) 1363e4e61333SAriff Abdullah cnt = 0; 1364e4e61333SAriff Abdullah if (cnt > SND_MAXVCHANS) 1365e4e61333SAriff Abdullah cnt = SND_MAXVCHANS; 1366e4e61333SAriff Abdullah err = pcm_setvchans(d, direction, cnt, -1); 1367e4e61333SAriff Abdullah } 1368e4e61333SAriff Abdullah 1369e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 1370e4e61333SAriff Abdullah 137167b1dce3SCameron Grant return err; 137267b1dce3SCameron Grant } 137367b1dce3SCameron Grant #endif 137467b1dce3SCameron Grant 137567b1dce3SCameron Grant /************************************************************************/ 137667b1dce3SCameron Grant 1377b611c801SAlexander Leidinger /** 1378b611c801SAlexander Leidinger * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 1379b611c801SAlexander Leidinger * 1380b611c801SAlexander Leidinger * @param si Pointer to oss_sysinfo struct where information about the 1381b611c801SAlexander Leidinger * sound subsystem will be written/copied. 1382b611c801SAlexander Leidinger * 1383b611c801SAlexander Leidinger * This routine returns information about the sound system, such as the 1384b611c801SAlexander Leidinger * current OSS version, number of audio, MIDI, and mixer drivers, etc. 1385b611c801SAlexander Leidinger * Also includes a bitmask showing which of the above types of devices 1386b611c801SAlexander Leidinger * are open (busy). 1387b611c801SAlexander Leidinger * 1388b611c801SAlexander Leidinger * @note 1389b611c801SAlexander Leidinger * Calling threads must not hold any snddev_info or pcm_channel locks. 1390b611c801SAlexander Leidinger * 1391b611c801SAlexander Leidinger * @author Ryan Beasley <ryanb@FreeBSD.org> 1392b611c801SAlexander Leidinger */ 1393b611c801SAlexander Leidinger void 1394b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si) 1395b611c801SAlexander Leidinger { 1396b611c801SAlexander Leidinger static char si_product[] = "FreeBSD native OSS ABI"; 1397b611c801SAlexander Leidinger static char si_version[] = __XSTRING(__FreeBSD_version); 1398b611c801SAlexander Leidinger static int intnbits = sizeof(int) * 8; /* Better suited as macro? 1399b611c801SAlexander Leidinger Must pester a C guru. */ 1400b611c801SAlexander Leidinger 1401b611c801SAlexander Leidinger struct snddev_info *d; 1402b611c801SAlexander Leidinger struct pcm_channel *c; 1403b611c801SAlexander Leidinger int i, j, ncards; 1404b611c801SAlexander Leidinger 1405b611c801SAlexander Leidinger ncards = 0; 1406b611c801SAlexander Leidinger 1407b611c801SAlexander Leidinger strlcpy(si->product, si_product, sizeof(si->product)); 1408b611c801SAlexander Leidinger strlcpy(si->version, si_version, sizeof(si->version)); 1409b611c801SAlexander Leidinger si->versionnum = SOUND_VERSION; 1410b611c801SAlexander Leidinger 1411b611c801SAlexander Leidinger /* 1412b611c801SAlexander Leidinger * Iterate over PCM devices and their channels, gathering up data 1413b611c801SAlexander Leidinger * for the numaudios, ncards, and openedaudio fields. 1414b611c801SAlexander Leidinger */ 1415b611c801SAlexander Leidinger si->numaudios = 0; 1416b611c801SAlexander Leidinger bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 1417b611c801SAlexander Leidinger 1418b611c801SAlexander Leidinger j = 0; 1419b611c801SAlexander Leidinger 14209c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 14219c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 1422b611c801SAlexander Leidinger d = devclass_get_softc(pcm_devclass, i); 1423e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 1424b611c801SAlexander Leidinger continue; 1425b611c801SAlexander Leidinger 1426e4e61333SAriff Abdullah /* XXX Need Giant magic entry ??? */ 1427e4e61333SAriff Abdullah 1428b611c801SAlexander Leidinger /* See note in function's docblock */ 1429b611c801SAlexander Leidinger mtx_assert(d->lock, MA_NOTOWNED); 1430b611c801SAlexander Leidinger pcm_lock(d); 1431b611c801SAlexander Leidinger 1432b611c801SAlexander Leidinger si->numaudios += d->devcount; 1433b611c801SAlexander Leidinger ++ncards; 1434b611c801SAlexander Leidinger 1435bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 1436b611c801SAlexander Leidinger mtx_assert(c->lock, MA_NOTOWNED); 1437b611c801SAlexander Leidinger CHN_LOCK(c); 1438b611c801SAlexander Leidinger if (c->flags & CHN_F_BUSY) 1439b611c801SAlexander Leidinger si->openedaudio[j / intnbits] |= 1440b611c801SAlexander Leidinger (1 << (j % intnbits)); 1441b611c801SAlexander Leidinger CHN_UNLOCK(c); 1442b611c801SAlexander Leidinger j++; 1443b611c801SAlexander Leidinger } 1444b611c801SAlexander Leidinger 1445b611c801SAlexander Leidinger pcm_unlock(d); 1446b611c801SAlexander Leidinger } 1447b611c801SAlexander Leidinger 1448b611c801SAlexander Leidinger si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 1449b611c801SAlexander Leidinger /** 1450b611c801SAlexander Leidinger * @todo Collect num{midis,timers}. 1451b611c801SAlexander Leidinger * 1452b611c801SAlexander Leidinger * Need access to sound/midi/midi.c::midistat_lock in order 1453b611c801SAlexander Leidinger * to safely touch midi_devices and get a head count of, well, 1454b611c801SAlexander Leidinger * MIDI devices. midistat_lock is a global static (i.e., local to 1455b611c801SAlexander Leidinger * midi.c), but midi_devices is a regular global; should the mutex 1456b611c801SAlexander Leidinger * be publicized, or is there another way to get this information? 1457b611c801SAlexander Leidinger * 1458b611c801SAlexander Leidinger * NB: MIDI/sequencer stuff is currently on hold. 1459b611c801SAlexander Leidinger */ 1460b611c801SAlexander Leidinger si->nummidis = 0; 1461b611c801SAlexander Leidinger si->numtimers = 0; 1462b611c801SAlexander Leidinger si->nummixers = mixer_count; 1463b611c801SAlexander Leidinger si->numcards = ncards; 1464b611c801SAlexander Leidinger /* OSSv4 docs: Intended only for test apps; API doesn't 1465b611c801SAlexander Leidinger really have much of a concept of cards. Shouldn't be 1466b611c801SAlexander Leidinger used by applications. */ 1467b611c801SAlexander Leidinger 1468b611c801SAlexander Leidinger /** 1469b611c801SAlexander Leidinger * @todo Fill in "busy devices" fields. 1470b611c801SAlexander Leidinger * 1471b611c801SAlexander Leidinger * si->openedmidi = " MIDI devices 1472b611c801SAlexander Leidinger */ 1473b611c801SAlexander Leidinger bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 1474b611c801SAlexander Leidinger 1475b611c801SAlexander Leidinger /* 1476b611c801SAlexander Leidinger * Si->filler is a reserved array, but according to docs each 1477b611c801SAlexander Leidinger * element should be set to -1. 1478b611c801SAlexander Leidinger */ 1479b611c801SAlexander Leidinger for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++) 1480b611c801SAlexander Leidinger si->filler[i] = -1; 1481b611c801SAlexander Leidinger } 1482b611c801SAlexander Leidinger 1483b611c801SAlexander Leidinger /************************************************************************/ 1484b611c801SAlexander Leidinger 14850739ea1dSSeigo Tanimura static int 14860739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data) 14870739ea1dSSeigo Tanimura { 1488b611c801SAlexander Leidinger int ret; 14897233ababSAlexander Leidinger #if 0 14900739ea1dSSeigo Tanimura return (midi_modevent(mod, type, data)); 14917233ababSAlexander Leidinger #else 1492b611c801SAlexander Leidinger ret = 0; 1493b611c801SAlexander Leidinger 1494b611c801SAlexander Leidinger switch(type) { 1495b611c801SAlexander Leidinger case MOD_LOAD: 1496b611c801SAlexander Leidinger pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 1497b611c801SAlexander Leidinger break; 1498b611c801SAlexander Leidinger case MOD_UNLOAD: 1499b611c801SAlexander Leidinger case MOD_SHUTDOWN: 1500bba4862cSAriff Abdullah ret = sndstat_acquire(curthread); 1501bba4862cSAriff Abdullah if (ret != 0) 1502bba4862cSAriff Abdullah break; 1503b611c801SAlexander Leidinger if (pcmsg_unrhdr != NULL) { 1504b611c801SAlexander Leidinger delete_unrhdr(pcmsg_unrhdr); 1505b611c801SAlexander Leidinger pcmsg_unrhdr = NULL; 1506b611c801SAlexander Leidinger } 1507b611c801SAlexander Leidinger break; 1508b611c801SAlexander Leidinger default: 1509b611c801SAlexander Leidinger ret = EOPNOTSUPP; 1510b611c801SAlexander Leidinger } 1511b611c801SAlexander Leidinger 1512b611c801SAlexander Leidinger return ret; 15137233ababSAlexander Leidinger #endif 15140739ea1dSSeigo Tanimura } 15150739ea1dSSeigo Tanimura 15160739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL); 15170739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER); 1518