1098ca2bdSWarner Losh /*- 290da2b28SAriff Abdullah * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 390da2b28SAriff Abdullah * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 490da2b28SAriff Abdullah * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> 540ac6e17SJoel Dahl * Copyright (c) 1997 Luigi Rizzo 6987e5972SCameron Grant * All rights reserved. 7987e5972SCameron Grant * 8987e5972SCameron Grant * Redistribution and use in source and binary forms, with or without 9987e5972SCameron Grant * modification, are permitted provided that the following conditions 10987e5972SCameron Grant * are met: 11987e5972SCameron Grant * 1. Redistributions of source code must retain the above copyright 12987e5972SCameron Grant * notice, this list of conditions and the following disclaimer. 13987e5972SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 14987e5972SCameron Grant * notice, this list of conditions and the following disclaimer in the 15987e5972SCameron Grant * documentation and/or other materials provided with the distribution. 16987e5972SCameron Grant * 17987e5972SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18987e5972SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19987e5972SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20987e5972SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21987e5972SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22987e5972SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23987e5972SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24987e5972SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25987e5972SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26987e5972SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27987e5972SCameron Grant * SUCH DAMAGE. 28987e5972SCameron Grant */ 29987e5972SCameron Grant 3090da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 3190da2b28SAriff Abdullah #include "opt_snd.h" 3290da2b28SAriff Abdullah #endif 3390da2b28SAriff Abdullah 34ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 35a580b31aSAriff Abdullah #include <dev/sound/pcm/ac97.h> 36285648f9SCameron Grant #include <dev/sound/pcm/vchan.h> 375ee30e27SMathew Kanner #include <dev/sound/pcm/dsp.h> 3890da2b28SAriff Abdullah #include <dev/sound/pcm/sndstat.h> 39bba4862cSAriff Abdullah #include <dev/sound/version.h> 40b611c801SAlexander Leidinger #include <sys/limits.h> 417c438dbeSCameron Grant #include <sys/sysctl.h> 42285648f9SCameron Grant 4367b1dce3SCameron Grant #include "feeder_if.h" 4467b1dce3SCameron Grant 4567b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 4667b1dce3SCameron Grant 47d95502a8SCameron Grant devclass_t pcm_devclass; 4882db23e2SCameron Grant 49b8a36395SCameron Grant int pcm_veto_load = 1; 50b8a36395SCameron Grant 51f3685841SAriff Abdullah int snd_unit = -1; 52cbe7d6a3SCameron Grant 53cbebc90dSAlexander Motin static int snd_unit_auto = -1; 54af3b2549SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN, 55838d3589SAriff Abdullah &snd_unit_auto, 0, "assign default unit to a newly attached device"); 56ad8612b9SAriff Abdullah 57bba4862cSAriff Abdullah int snd_maxautovchans = 16; 58987e5972SCameron Grant 5982db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 6082db23e2SCameron Grant 61*32a0e5d5SHans Petter Selasky static void pcm_sysinit(device_t); 62*32a0e5d5SHans Petter Selasky 63bba4862cSAriff Abdullah /* 64bba4862cSAriff Abdullah * XXX I've had enough with people not telling proper version/arch 65bba4862cSAriff Abdullah * while reporting problems, not after 387397913213th questions/requests. 66bba4862cSAriff Abdullah */ 67a9bdb5d3SAndriy Gapon static char snd_driver_version[] = 68bba4862cSAriff Abdullah __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH; 69bba4862cSAriff Abdullah SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version, 7090da2b28SAriff Abdullah 0, "driver version/arch"); 71bba4862cSAriff Abdullah 72b611c801SAlexander Leidinger /** 73b611c801SAlexander Leidinger * @brief Unit number allocator for syncgroup IDs 74b611c801SAlexander Leidinger */ 75b611c801SAlexander Leidinger struct unrhdr *pcmsg_unrhdr = NULL; 76b611c801SAlexander Leidinger 7790da2b28SAriff Abdullah static int 7890da2b28SAriff Abdullah sndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS) 7990da2b28SAriff Abdullah { 8090da2b28SAriff Abdullah SNDSTAT_PREPARE_PCM_BEGIN(); 8190da2b28SAriff Abdullah SNDSTAT_PREPARE_PCM_END(); 8290da2b28SAriff Abdullah } 8367b1dce3SCameron Grant 8437209180SCameron Grant void * 852c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type) 8637209180SCameron Grant { 8737209180SCameron Grant struct mtx *m; 8837209180SCameron Grant 89a163d034SWarner Losh m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 9012e524a2SDon Lewis mtx_init(m, desc, type, MTX_DEF); 9112e524a2SDon Lewis return m; 9212e524a2SDon Lewis } 9312e524a2SDon Lewis 9437209180SCameron Grant void 9537209180SCameron Grant snd_mtxfree(void *m) 9637209180SCameron Grant { 9737209180SCameron Grant struct mtx *mtx = m; 9837209180SCameron Grant 9937209180SCameron Grant mtx_destroy(mtx); 10037209180SCameron Grant free(mtx, M_DEVBUF); 10137209180SCameron Grant } 10237209180SCameron Grant 10337209180SCameron Grant void 10437209180SCameron Grant snd_mtxassert(void *m) 10537209180SCameron Grant { 106f00f162aSCameron Grant #ifdef INVARIANTS 10737209180SCameron Grant struct mtx *mtx = m; 10837209180SCameron Grant 10937209180SCameron Grant mtx_assert(mtx, MA_OWNED); 11037209180SCameron Grant #endif 11137209180SCameron Grant } 11237209180SCameron Grant 11337209180SCameron Grant int 11437209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 11537209180SCameron Grant { 116e4e61333SAriff Abdullah struct snddev_info *d; 11790da2b28SAriff Abdullah 11837209180SCameron Grant flags &= INTR_MPSAFE; 11946700f12SPeter Wemm flags |= INTR_TYPE_AV; 120e4e61333SAriff Abdullah d = device_get_softc(dev); 121e4e61333SAriff Abdullah if (d != NULL && (flags & INTR_MPSAFE)) 122e4e61333SAriff Abdullah d->flags |= SD_F_MPSAFE; 123e4e61333SAriff Abdullah 124775fcb6eSBaptiste Daroussin return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep); 12537209180SCameron Grant } 12637209180SCameron Grant 127bba4862cSAriff Abdullah static void 128bba4862cSAriff Abdullah pcm_clonereset(struct snddev_info *d) 129a1d444e1SAriff Abdullah { 130bba4862cSAriff Abdullah int cmax; 131bba4862cSAriff Abdullah 132e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 133bba4862cSAriff Abdullah 134bba4862cSAriff Abdullah cmax = d->playcount + d->reccount - 1; 135bba4862cSAriff Abdullah if (d->pvchancount > 0) 13690da2b28SAriff Abdullah cmax += max(d->pvchancount, snd_maxautovchans) - 1; 137bba4862cSAriff Abdullah if (d->rvchancount > 0) 13890da2b28SAriff Abdullah cmax += max(d->rvchancount, snd_maxautovchans) - 1; 139bba4862cSAriff Abdullah if (cmax > PCMMAXCLONE) 140bba4862cSAriff Abdullah cmax = PCMMAXCLONE; 141bba4862cSAriff Abdullah (void)snd_clone_gc(d->clones); 142bba4862cSAriff Abdullah (void)snd_clone_setmaxunit(d->clones, cmax); 143bba4862cSAriff Abdullah } 144bba4862cSAriff Abdullah 14590da2b28SAriff Abdullah int 146bba4862cSAriff Abdullah pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) 147bba4862cSAriff Abdullah { 148bba4862cSAriff Abdullah struct pcm_channel *c, *ch, *nch; 14990da2b28SAriff Abdullah struct pcmchan_caps *caps; 15090da2b28SAriff Abdullah int i, err, vcnt; 151bba4862cSAriff Abdullah 152e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 153a1d444e1SAriff Abdullah 154bba4862cSAriff Abdullah if ((direction == PCMDIR_PLAY && d->playcount < 1) || 155e4e61333SAriff Abdullah (direction == PCMDIR_REC && d->reccount < 1)) 156e4e61333SAriff Abdullah return (ENODEV); 157a580b31aSAriff Abdullah 158e4e61333SAriff Abdullah if (!(d->flags & SD_F_AUTOVCHAN)) 159e4e61333SAriff Abdullah return (EINVAL); 160a1d444e1SAriff Abdullah 161e4e61333SAriff Abdullah if (newcnt < 0 || newcnt > SND_MAXVCHANS) 162e4e61333SAriff Abdullah return (E2BIG); 163a1d444e1SAriff Abdullah 164bba4862cSAriff Abdullah if (direction == PCMDIR_PLAY) 165bba4862cSAriff Abdullah vcnt = d->pvchancount; 166bba4862cSAriff Abdullah else if (direction == PCMDIR_REC) 167bba4862cSAriff Abdullah vcnt = d->rvchancount; 168e4e61333SAriff Abdullah else 169e4e61333SAriff Abdullah return (EINVAL); 170a1d444e1SAriff Abdullah 171a1d444e1SAriff Abdullah if (newcnt > vcnt) { 172bba4862cSAriff Abdullah KASSERT(num == -1 || 173bba4862cSAriff Abdullah (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt), 174bba4862cSAriff Abdullah ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d", 175bba4862cSAriff Abdullah num, newcnt, vcnt)); 176a1d444e1SAriff Abdullah /* add new vchans - find a parent channel first */ 177e4e61333SAriff Abdullah ch = NULL; 178bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 179a1d444e1SAriff Abdullah CHN_LOCK(c); 180bba4862cSAriff Abdullah if (c->direction == direction && 181bba4862cSAriff Abdullah ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 18290da2b28SAriff Abdullah c->refcount < 1 && 183e4e61333SAriff Abdullah !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 18490da2b28SAriff Abdullah /* 18590da2b28SAriff Abdullah * Reuse hw channel with vchans already 18690da2b28SAriff Abdullah * created. 18790da2b28SAriff Abdullah */ 18890da2b28SAriff Abdullah if (c->flags & CHN_F_HAS_VCHAN) { 189e4e61333SAriff Abdullah ch = c; 190e4e61333SAriff Abdullah break; 191e4e61333SAriff Abdullah } 19290da2b28SAriff Abdullah /* 19390da2b28SAriff Abdullah * No vchans ever created, look for 19490da2b28SAriff Abdullah * channels with supported formats. 19590da2b28SAriff Abdullah */ 19690da2b28SAriff Abdullah caps = chn_getcaps(c); 19790da2b28SAriff Abdullah if (caps == NULL) { 19890da2b28SAriff Abdullah CHN_UNLOCK(c); 19990da2b28SAriff Abdullah continue; 20090da2b28SAriff Abdullah } 20190da2b28SAriff Abdullah for (i = 0; caps->fmtlist[i] != 0; i++) { 20290da2b28SAriff Abdullah if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 20390da2b28SAriff Abdullah break; 20490da2b28SAriff Abdullah } 20590da2b28SAriff Abdullah if (caps->fmtlist[i] != 0) { 20690da2b28SAriff Abdullah ch = c; 20790da2b28SAriff Abdullah break; 20890da2b28SAriff Abdullah } 20990da2b28SAriff Abdullah } 210a1d444e1SAriff Abdullah CHN_UNLOCK(c); 211a1d444e1SAriff Abdullah } 212e4e61333SAriff Abdullah if (ch == NULL) 213e4e61333SAriff Abdullah return (EBUSY); 214e4e61333SAriff Abdullah ch->flags |= CHN_F_BUSY; 215e4e61333SAriff Abdullah err = 0; 216a1d444e1SAriff Abdullah while (err == 0 && newcnt > vcnt) { 217e4e61333SAriff Abdullah err = vchan_create(ch, num); 218bba4862cSAriff Abdullah if (err == 0) 219a1d444e1SAriff Abdullah vcnt++; 220bba4862cSAriff Abdullah else if (err == E2BIG && newcnt > vcnt) 221bba4862cSAriff Abdullah device_printf(d->dev, 222bba4862cSAriff Abdullah "%s: err=%d Maximum channel reached.\n", 223bba4862cSAriff Abdullah __func__, err); 224a1d444e1SAriff Abdullah } 225a1d444e1SAriff Abdullah if (vcnt == 0) 226e4e61333SAriff Abdullah ch->flags &= ~CHN_F_BUSY; 227e4e61333SAriff Abdullah CHN_UNLOCK(ch); 228e4e61333SAriff Abdullah if (err != 0) 229e4e61333SAriff Abdullah return (err); 230e4e61333SAriff Abdullah else 231bba4862cSAriff Abdullah pcm_clonereset(d); 232a1d444e1SAriff Abdullah } else if (newcnt < vcnt) { 233bba4862cSAriff Abdullah KASSERT(num == -1, 234bba4862cSAriff Abdullah ("bogus vchan_destroy() request num=%d", num)); 235bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 236a1d444e1SAriff Abdullah CHN_LOCK(c); 237bba4862cSAriff Abdullah if (c->direction != direction || 238bba4862cSAriff Abdullah CHN_EMPTY(c, children) || 239bba4862cSAriff Abdullah !(c->flags & CHN_F_HAS_VCHAN)) { 240a1d444e1SAriff Abdullah CHN_UNLOCK(c); 241bba4862cSAriff Abdullah continue; 242a1d444e1SAriff Abdullah } 243bba4862cSAriff Abdullah CHN_FOREACH_SAFE(ch, c, nch, children) { 244bba4862cSAriff Abdullah CHN_LOCK(ch); 24590da2b28SAriff Abdullah if (vcnt == 1 && c->refcount > 0) { 246bba4862cSAriff Abdullah CHN_UNLOCK(ch); 24790da2b28SAriff Abdullah break; 24890da2b28SAriff Abdullah } 24990da2b28SAriff Abdullah if (!(ch->flags & CHN_F_BUSY) && 25090da2b28SAriff Abdullah ch->refcount < 1) { 251bba4862cSAriff Abdullah err = vchan_destroy(ch); 252a1d444e1SAriff Abdullah if (err == 0) 253a1d444e1SAriff Abdullah vcnt--; 254bba4862cSAriff Abdullah } else 255bba4862cSAriff Abdullah CHN_UNLOCK(ch); 256e4e61333SAriff Abdullah if (vcnt == newcnt) 257bba4862cSAriff Abdullah break; 258a1d444e1SAriff Abdullah } 259bba4862cSAriff Abdullah CHN_UNLOCK(c); 260bba4862cSAriff Abdullah break; 261bba4862cSAriff Abdullah } 262bba4862cSAriff Abdullah pcm_clonereset(d); 263bba4862cSAriff Abdullah } 264a1d444e1SAriff Abdullah 265e4e61333SAriff Abdullah return (0); 266a1d444e1SAriff Abdullah } 267a1d444e1SAriff Abdullah 2683fdb3676SAriff Abdullah /* return error status and a locked channel */ 2693fdb3676SAriff Abdullah int 2703fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 27190da2b28SAriff Abdullah pid_t pid, char *comm, int devunit) 272285648f9SCameron Grant { 273285648f9SCameron Grant struct pcm_channel *c; 27490da2b28SAriff Abdullah int err, vchancount, vchan_num; 275bba4862cSAriff Abdullah 276bba4862cSAriff Abdullah KASSERT(d != NULL && ch != NULL && (devunit == -1 || 277bba4862cSAriff Abdullah !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && 278bba4862cSAriff Abdullah (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 279e4e61333SAriff Abdullah ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d", 280bba4862cSAriff Abdullah __func__, d, ch, direction, pid, devunit)); 281e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 282bba4862cSAriff Abdullah 283bba4862cSAriff Abdullah /* Double check again. */ 284bba4862cSAriff Abdullah if (devunit != -1) { 285bba4862cSAriff Abdullah switch (snd_unit2d(devunit)) { 286bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 287bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 288bba4862cSAriff Abdullah if (direction != PCMDIR_PLAY) 28990da2b28SAriff Abdullah return (ENOTSUP); 290bba4862cSAriff Abdullah break; 291bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 292bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 293bba4862cSAriff Abdullah if (direction != PCMDIR_REC) 29490da2b28SAriff Abdullah return (ENOTSUP); 295bba4862cSAriff Abdullah break; 296bba4862cSAriff Abdullah default: 297bba4862cSAriff Abdullah if (!(direction == PCMDIR_PLAY || 298bba4862cSAriff Abdullah direction == PCMDIR_REC)) 29990da2b28SAriff Abdullah return (ENOTSUP); 300bba4862cSAriff Abdullah break; 301bba4862cSAriff Abdullah } 302bba4862cSAriff Abdullah } 303285648f9SCameron Grant 30490da2b28SAriff Abdullah *ch = NULL; 30590da2b28SAriff Abdullah vchan_num = 0; 30690da2b28SAriff Abdullah vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : 30790da2b28SAriff Abdullah d->rvchancount; 30890da2b28SAriff Abdullah 3093fdb3676SAriff Abdullah retry_chnalloc: 31090da2b28SAriff Abdullah err = ENOTSUP; 311f637a36cSCameron Grant /* scan for a free channel */ 312bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 31349c5e6e2SCameron Grant CHN_LOCK(c); 31490da2b28SAriff Abdullah if (devunit == -1 && c->direction == direction && 31590da2b28SAriff Abdullah (c->flags & CHN_F_VIRTUAL)) { 31690da2b28SAriff Abdullah if (vchancount < snd_maxautovchans && 31790da2b28SAriff Abdullah vchan_num < CHN_CHAN(c)) { 31890da2b28SAriff Abdullah CHN_UNLOCK(c); 31990da2b28SAriff Abdullah goto vchan_alloc; 32090da2b28SAriff Abdullah } 32190da2b28SAriff Abdullah vchan_num++; 32290da2b28SAriff Abdullah } 323bba4862cSAriff Abdullah if (c->direction == direction && !(c->flags & CHN_F_BUSY) && 324bba4862cSAriff Abdullah (devunit == -1 || devunit == -2 || c->unit == devunit)) { 325285648f9SCameron Grant c->flags |= CHN_F_BUSY; 326b8f0d9e0SCameron Grant c->pid = pid; 32790da2b28SAriff Abdullah strlcpy(c->comm, (comm != NULL) ? comm : 32890da2b28SAriff Abdullah CHN_COMM_UNKNOWN, sizeof(c->comm)); 3293fdb3676SAriff Abdullah *ch = c; 330bba4862cSAriff Abdullah return (0); 331bba4862cSAriff Abdullah } else if (c->unit == devunit) { 3323fdb3676SAriff Abdullah if (c->direction != direction) 33390da2b28SAriff Abdullah err = ENOTSUP; 3343fdb3676SAriff Abdullah else if (c->flags & CHN_F_BUSY) 3353fdb3676SAriff Abdullah err = EBUSY; 3363fdb3676SAriff Abdullah else 3373fdb3676SAriff Abdullah err = EINVAL; 3383fdb3676SAriff Abdullah CHN_UNLOCK(c); 339bba4862cSAriff Abdullah return (err); 340bba4862cSAriff Abdullah } else if ((devunit == -1 || devunit == -2) && 341bba4862cSAriff Abdullah c->direction == direction && (c->flags & CHN_F_BUSY)) 342a1d444e1SAriff Abdullah err = EBUSY; 34349c5e6e2SCameron Grant CHN_UNLOCK(c); 344285648f9SCameron Grant } 345f637a36cSCameron Grant 346e4e61333SAriff Abdullah if (devunit == -2) 347e4e61333SAriff Abdullah return (err); 348e4e61333SAriff Abdullah 34990da2b28SAriff Abdullah vchan_alloc: 350f637a36cSCameron Grant /* no channel available */ 351e4e61333SAriff Abdullah if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || 352e4e61333SAriff Abdullah snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { 353bba4862cSAriff Abdullah if (!(vchancount > 0 && vchancount < snd_maxautovchans) && 354bba4862cSAriff Abdullah (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) 355bba4862cSAriff Abdullah return (err); 356bba4862cSAriff Abdullah err = pcm_setvchans(d, direction, vchancount + 1, 357bba4862cSAriff Abdullah (devunit == -1) ? -1 : snd_unit2c(devunit)); 358a1d444e1SAriff Abdullah if (err == 0) { 359bba4862cSAriff Abdullah if (devunit == -1) 360bba4862cSAriff Abdullah devunit = -2; 3613fdb3676SAriff Abdullah goto retry_chnalloc; 362f637a36cSCameron Grant } 363f637a36cSCameron Grant } 364f637a36cSCameron Grant 365bba4862cSAriff Abdullah return (err); 366285648f9SCameron Grant } 367285648f9SCameron Grant 368b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 369285648f9SCameron Grant int 370b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 371285648f9SCameron Grant { 372e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 373b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 374e4e61333SAriff Abdullah 375285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 376b8f0d9e0SCameron Grant c->pid = -1; 37790da2b28SAriff Abdullah strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm)); 37849c5e6e2SCameron Grant CHN_UNLOCK(c); 379e4e61333SAriff Abdullah 380e4e61333SAriff Abdullah return (0); 381285648f9SCameron Grant } 382285648f9SCameron Grant 383285648f9SCameron Grant int 384285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 385285648f9SCameron Grant { 386e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 387b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 388e4e61333SAriff Abdullah 389285648f9SCameron Grant c->refcount += ref; 390e4e61333SAriff Abdullah 391e4e61333SAriff Abdullah return (c->refcount); 392285648f9SCameron Grant } 393285648f9SCameron Grant 39467b1dce3SCameron Grant int 39567b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta) 39667b1dce3SCameron Grant { 39790da2b28SAriff Abdullah PCM_LOCKASSERT(d); 398a527dbc7SCameron Grant 399a527dbc7SCameron Grant d->inprog += delta; 400e4e61333SAriff Abdullah 401e4e61333SAriff Abdullah return (d->inprog); 40267b1dce3SCameron Grant } 40367b1dce3SCameron Grant 40467b1dce3SCameron Grant static void 40567b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 40667b1dce3SCameron Grant { 407e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 408e4e61333SAriff Abdullah 409bba4862cSAriff Abdullah if (num < 0) 410bba4862cSAriff Abdullah return; 411bba4862cSAriff Abdullah 412bba4862cSAriff Abdullah if (num >= 0 && d->pvchancount > num) 413bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1); 414bba4862cSAriff Abdullah else if (num > 0 && d->pvchancount == 0) 415bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1); 416bba4862cSAriff Abdullah 417bba4862cSAriff Abdullah if (num >= 0 && d->rvchancount > num) 418bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, num, -1); 419bba4862cSAriff Abdullah else if (num > 0 && d->rvchancount == 0) 420bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, 1, -1); 421bba4862cSAriff Abdullah 422bba4862cSAriff Abdullah pcm_clonereset(d); 42367b1dce3SCameron Grant } 42467b1dce3SCameron Grant 42533dbf14aSCameron Grant static int 426851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 42733dbf14aSCameron Grant { 428b8f0d9e0SCameron Grant struct snddev_info *d; 42933dbf14aSCameron Grant int error, unit; 43033dbf14aSCameron Grant 43133dbf14aSCameron Grant unit = snd_unit; 432041b706bSDavid Malone error = sysctl_handle_int(oidp, &unit, 0, req); 43333dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 434b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 435e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 436b8f0d9e0SCameron Grant return EINVAL; 43733dbf14aSCameron Grant snd_unit = unit; 438cbebc90dSAlexander Motin snd_unit_auto = 0; 43933dbf14aSCameron Grant } 44033dbf14aSCameron Grant return (error); 44133dbf14aSCameron Grant } 442851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */ 443b7d6c6b5SEitan Adler SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, 444*32a0e5d5SHans Petter Selasky CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY, 445b7d6c6b5SEitan Adler 0, sizeof(int), sysctl_hw_snd_default_unit, "I", 446b7d6c6b5SEitan Adler "default sound device"); 447987e5972SCameron Grant 448cd9766c5SCameron Grant static int 44967b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 450cd9766c5SCameron Grant { 45167b1dce3SCameron Grant struct snddev_info *d; 45267b1dce3SCameron Grant int i, v, error; 453cd9766c5SCameron Grant 45467b1dce3SCameron Grant v = snd_maxautovchans; 455041b706bSDavid Malone error = sysctl_handle_int(oidp, &v, 0, req); 456cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 457bba4862cSAriff Abdullah if (v < 0) 458bba4862cSAriff Abdullah v = 0; 459bba4862cSAriff Abdullah if (v > SND_MAXVCHANS) 460bba4862cSAriff Abdullah v = SND_MAXVCHANS; 461bba4862cSAriff Abdullah snd_maxautovchans = v; 4629c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 4639c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 46467b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 465e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 46667b1dce3SCameron Grant continue; 467e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 46867b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 469e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 47067b1dce3SCameron Grant } 47167b1dce3SCameron Grant } 472cd9766c5SCameron Grant return (error); 473cd9766c5SCameron Grant } 474*32a0e5d5SHans Petter Selasky SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RWTUN, 475a580b31aSAriff Abdullah 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 476f637a36cSCameron Grant 477285648f9SCameron Grant struct pcm_channel * 478bba4862cSAriff Abdullah pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo) 479987e5972SCameron Grant { 480bba4862cSAriff Abdullah struct pcm_channel *ch; 481bba4862cSAriff Abdullah int direction, err, rpnum, *pnum, max; 482bba4862cSAriff Abdullah int udc, device, chan; 483bba4862cSAriff Abdullah char *dirs, *devname, buf[CHN_NAMELEN]; 484bba4862cSAriff Abdullah 485e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 48690da2b28SAriff Abdullah PCM_LOCKASSERT(d); 487bba4862cSAriff Abdullah KASSERT(num >= -1, ("invalid num=%d", num)); 488bba4862cSAriff Abdullah 489987e5972SCameron Grant 490285648f9SCameron Grant switch (dir) { 491285648f9SCameron Grant case PCMDIR_PLAY: 492285648f9SCameron Grant dirs = "play"; 493a3193a9cSDon Lewis direction = PCMDIR_PLAY; 494a67fe5c1SCameron Grant pnum = &d->playcount; 495bba4862cSAriff Abdullah device = SND_DEV_DSPHW_PLAY; 496bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 497285648f9SCameron Grant break; 498bba4862cSAriff Abdullah case PCMDIR_PLAY_VIRTUAL: 499bba4862cSAriff Abdullah dirs = "virtual"; 500bba4862cSAriff Abdullah direction = PCMDIR_PLAY; 501bba4862cSAriff Abdullah pnum = &d->pvchancount; 502bba4862cSAriff Abdullah device = SND_DEV_DSPHW_VPLAY; 503bba4862cSAriff Abdullah max = SND_MAXVCHANS; 504bba4862cSAriff Abdullah break; 505285648f9SCameron Grant case PCMDIR_REC: 506285648f9SCameron Grant dirs = "record"; 507a3193a9cSDon Lewis direction = PCMDIR_REC; 508a67fe5c1SCameron Grant pnum = &d->reccount; 509bba4862cSAriff Abdullah device = SND_DEV_DSPHW_REC; 510bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 511285648f9SCameron Grant break; 512bba4862cSAriff Abdullah case PCMDIR_REC_VIRTUAL: 513285648f9SCameron Grant dirs = "virtual"; 514bba4862cSAriff Abdullah direction = PCMDIR_REC; 515bba4862cSAriff Abdullah pnum = &d->rvchancount; 516bba4862cSAriff Abdullah device = SND_DEV_DSPHW_VREC; 517bba4862cSAriff Abdullah max = SND_MAXVCHANS; 518285648f9SCameron Grant break; 519285648f9SCameron Grant default: 520e4e61333SAriff Abdullah return (NULL); 5219c326820SCameron Grant } 522285648f9SCameron Grant 523bba4862cSAriff Abdullah chan = (num == -1) ? 0 : num; 524285648f9SCameron Grant 525e4e61333SAriff Abdullah if (*pnum >= max || chan >= max) 526e4e61333SAriff Abdullah return (NULL); 527bba4862cSAriff Abdullah 5283fdb3676SAriff Abdullah rpnum = 0; 529bba4862cSAriff Abdullah 530bba4862cSAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 531bba4862cSAriff Abdullah if (CHN_DEV(ch) != device) 5323fdb3676SAriff Abdullah continue; 533bba4862cSAriff Abdullah if (chan == CHN_CHAN(ch)) { 534bba4862cSAriff Abdullah if (num != -1) { 5353fdb3676SAriff Abdullah device_printf(d->dev, 536bba4862cSAriff Abdullah "channel num=%d allocated!\n", chan); 537e4e61333SAriff Abdullah return (NULL); 538bba4862cSAriff Abdullah } 539bba4862cSAriff Abdullah chan++; 540bba4862cSAriff Abdullah if (chan >= max) { 541bba4862cSAriff Abdullah device_printf(d->dev, 542bba4862cSAriff Abdullah "chan=%d > %d\n", chan, max); 543e4e61333SAriff Abdullah return (NULL); 544bba4862cSAriff Abdullah } 5453fdb3676SAriff Abdullah } 5463fdb3676SAriff Abdullah rpnum++; 5473fdb3676SAriff Abdullah } 548bba4862cSAriff Abdullah 5493fdb3676SAriff Abdullah if (*pnum != rpnum) { 5503fdb3676SAriff Abdullah device_printf(d->dev, 551bba4862cSAriff Abdullah "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", 5523fdb3676SAriff Abdullah __func__, dirs, *pnum, rpnum); 553e4e61333SAriff Abdullah return (NULL); 5543fdb3676SAriff Abdullah } 555a67fe5c1SCameron Grant 556bba4862cSAriff Abdullah udc = snd_mkunit(device_get_unit(d->dev), device, chan); 557bba4862cSAriff Abdullah devname = dsp_unit2name(buf, sizeof(buf), udc); 558bba4862cSAriff Abdullah 559bba4862cSAriff Abdullah if (devname == NULL) { 560bba4862cSAriff Abdullah device_printf(d->dev, 561bba4862cSAriff Abdullah "Failed to query device name udc=0x%08x\n", udc); 562e4e61333SAriff Abdullah return (NULL); 563bba4862cSAriff Abdullah } 564bba4862cSAriff Abdullah 56590da2b28SAriff Abdullah PCM_UNLOCK(d); 566bba4862cSAriff Abdullah ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 567bba4862cSAriff Abdullah ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); 568bba4862cSAriff Abdullah ch->unit = udc; 569285648f9SCameron Grant ch->pid = -1; 57090da2b28SAriff Abdullah strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm)); 571285648f9SCameron Grant ch->parentsnddev = d; 572285648f9SCameron Grant ch->parentchannel = parent; 573436c9b65SScott Long ch->dev = d->dev; 574bba4862cSAriff Abdullah ch->trigger = PCMTRIG_STOP; 575bba4862cSAriff Abdullah snprintf(ch->name, sizeof(ch->name), "%s:%s:%s", 576bba4862cSAriff Abdullah device_get_nameunit(ch->dev), dirs, devname); 577285648f9SCameron Grant 578a3193a9cSDon Lewis err = chn_init(ch, devinfo, dir, direction); 57990da2b28SAriff Abdullah PCM_LOCK(d); 5800f55ac6cSCameron Grant if (err) { 581bba4862cSAriff Abdullah device_printf(d->dev, "chn_init(%s) failed: err = %d\n", 582bba4862cSAriff Abdullah ch->name, err); 583285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 584285648f9SCameron Grant free(ch, M_DEVBUF); 585e4e61333SAriff Abdullah return (NULL); 586bbb5bf3dSCameron Grant } 587285648f9SCameron Grant 588e4e61333SAriff Abdullah return (ch); 589285648f9SCameron Grant } 590285648f9SCameron Grant 591285648f9SCameron Grant int 592285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 593285648f9SCameron Grant { 594a67fe5c1SCameron Grant struct snddev_info *d; 595285648f9SCameron Grant int err; 596285648f9SCameron Grant 597a67fe5c1SCameron Grant d = ch->parentsnddev; 598e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 599e4e61333SAriff Abdullah 600285648f9SCameron Grant err = chn_kill(ch); 601285648f9SCameron Grant if (err) { 602e4e61333SAriff Abdullah device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n", 603e4e61333SAriff Abdullah ch->name, err); 604e4e61333SAriff Abdullah return (err); 605285648f9SCameron Grant } 606285648f9SCameron Grant 607285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 608285648f9SCameron Grant free(ch, M_DEVBUF); 609285648f9SCameron Grant 610e4e61333SAriff Abdullah return (0); 611285648f9SCameron Grant } 612285648f9SCameron Grant 613285648f9SCameron Grant int 6145ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 615285648f9SCameron Grant { 616e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 61790da2b28SAriff Abdullah PCM_LOCKASSERT(d); 618bba4862cSAriff Abdullah KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 619bba4862cSAriff Abdullah ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 620285648f9SCameron Grant 62190da2b28SAriff Abdullah CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm); 6223fdb3676SAriff Abdullah 623e4e61333SAriff Abdullah switch (CHN_DEV(ch)) { 624e4e61333SAriff Abdullah case SND_DEV_DSPHW_PLAY: 625e4e61333SAriff Abdullah d->playcount++; 626e4e61333SAriff Abdullah break; 627e4e61333SAriff Abdullah case SND_DEV_DSPHW_VPLAY: 628e4e61333SAriff Abdullah d->pvchancount++; 629e4e61333SAriff Abdullah break; 630e4e61333SAriff Abdullah case SND_DEV_DSPHW_REC: 631e4e61333SAriff Abdullah d->reccount++; 632e4e61333SAriff Abdullah break; 633e4e61333SAriff Abdullah case SND_DEV_DSPHW_VREC: 634e4e61333SAriff Abdullah d->rvchancount++; 635e4e61333SAriff Abdullah break; 636e4e61333SAriff Abdullah default: 637e4e61333SAriff Abdullah break; 638e4e61333SAriff Abdullah } 639b8f0d9e0SCameron Grant 640e4e61333SAriff Abdullah d->devcount++; 641e4e61333SAriff Abdullah 642e4e61333SAriff Abdullah return (0); 64333dbf14aSCameron Grant } 64433dbf14aSCameron Grant 645285648f9SCameron Grant int 6465ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 64733dbf14aSCameron Grant { 648bba4862cSAriff Abdullah struct pcm_channel *tmp; 64933dbf14aSCameron Grant 650e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 65190da2b28SAriff Abdullah PCM_LOCKASSERT(d); 652e4e61333SAriff Abdullah 653bba4862cSAriff Abdullah tmp = NULL; 654a527dbc7SCameron Grant 655bba4862cSAriff Abdullah CHN_FOREACH(tmp, d, channels.pcm) { 656bba4862cSAriff Abdullah if (tmp == ch) 657bba4862cSAriff Abdullah break; 65833dbf14aSCameron Grant } 659bba4862cSAriff Abdullah 660bba4862cSAriff Abdullah if (tmp != ch) 661e4e61333SAriff Abdullah return (EINVAL); 66267beb5a5SCameron Grant 663bba4862cSAriff Abdullah CHN_REMOVE(d, ch, channels.pcm); 664e4e61333SAriff Abdullah 665bba4862cSAriff Abdullah switch (CHN_DEV(ch)) { 666bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 66767beb5a5SCameron Grant d->playcount--; 668bba4862cSAriff Abdullah break; 669bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 670bba4862cSAriff Abdullah d->pvchancount--; 671bba4862cSAriff Abdullah break; 672bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 673bba4862cSAriff Abdullah d->reccount--; 674bba4862cSAriff Abdullah break; 675bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 676bba4862cSAriff Abdullah d->rvchancount--; 677bba4862cSAriff Abdullah break; 678bba4862cSAriff Abdullah default: 679bba4862cSAriff Abdullah break; 680bba4862cSAriff Abdullah } 681285648f9SCameron Grant 682e4e61333SAriff Abdullah d->devcount--; 683e4e61333SAriff Abdullah 684e4e61333SAriff Abdullah return (0); 685987e5972SCameron Grant } 686987e5972SCameron Grant 687987e5972SCameron Grant int 688285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 689285648f9SCameron Grant { 690285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 69167b1dce3SCameron Grant struct pcm_channel *ch; 69267b1dce3SCameron Grant int err; 693285648f9SCameron Grant 694e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 695e4e61333SAriff Abdullah 69690da2b28SAriff Abdullah PCM_LOCK(d); 697bba4862cSAriff Abdullah ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); 698285648f9SCameron Grant if (!ch) { 699e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", 700e4e61333SAriff Abdullah cls->name, dir, devinfo); 70190da2b28SAriff Abdullah PCM_UNLOCK(d); 702e4e61333SAriff Abdullah return (ENODEV); 703285648f9SCameron Grant } 704cd9766c5SCameron Grant 7055ee30e27SMathew Kanner err = pcm_chn_add(d, ch); 70690da2b28SAriff Abdullah PCM_UNLOCK(d); 707285648f9SCameron Grant if (err) { 708e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", 709e4e61333SAriff Abdullah ch->name, err); 710285648f9SCameron Grant pcm_chn_destroy(ch); 711cd9766c5SCameron Grant } 712cd9766c5SCameron Grant 713e4e61333SAriff Abdullah return (err); 714285648f9SCameron Grant } 715285648f9SCameron Grant 716285648f9SCameron Grant static int 717285648f9SCameron Grant pcm_killchan(device_t dev) 718285648f9SCameron Grant { 719285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 720e33bee07SOlivier Houchard struct pcm_channel *ch; 721e4e61333SAriff Abdullah int error; 722e4e61333SAriff Abdullah 723e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 724285648f9SCameron Grant 725bba4862cSAriff Abdullah ch = CHN_FIRST(d, channels.pcm); 726285648f9SCameron Grant 72790da2b28SAriff Abdullah PCM_LOCK(d); 728bba4862cSAriff Abdullah error = pcm_chn_remove(d, ch); 72990da2b28SAriff Abdullah PCM_UNLOCK(d); 730e33bee07SOlivier Houchard if (error) 731e33bee07SOlivier Houchard return (error); 732e33bee07SOlivier Houchard return (pcm_chn_destroy(ch)); 733285648f9SCameron Grant } 734285648f9SCameron Grant 735cbebc90dSAlexander Motin static int 736cbebc90dSAlexander Motin pcm_best_unit(int old) 737cbebc90dSAlexander Motin { 738cbebc90dSAlexander Motin struct snddev_info *d; 739cbebc90dSAlexander Motin int i, best, bestprio, prio; 740cbebc90dSAlexander Motin 741cbebc90dSAlexander Motin best = -1; 742cbebc90dSAlexander Motin bestprio = -100; 743cbebc90dSAlexander Motin for (i = 0; pcm_devclass != NULL && 744cbebc90dSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 745cbebc90dSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 746cbebc90dSAlexander Motin if (!PCM_REGISTERED(d)) 747cbebc90dSAlexander Motin continue; 748cbebc90dSAlexander Motin prio = 0; 749cbebc90dSAlexander Motin if (d->playcount == 0) 750cbebc90dSAlexander Motin prio -= 10; 751cbebc90dSAlexander Motin if (d->reccount == 0) 752cbebc90dSAlexander Motin prio -= 2; 753cbebc90dSAlexander Motin if (prio > bestprio || (prio == bestprio && i == old)) { 754cbebc90dSAlexander Motin best = i; 755cbebc90dSAlexander Motin bestprio = prio; 756cbebc90dSAlexander Motin } 757cbebc90dSAlexander Motin } 758cbebc90dSAlexander Motin return (best); 759cbebc90dSAlexander Motin } 760cbebc90dSAlexander Motin 761285648f9SCameron Grant int 762987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 763987e5972SCameron Grant { 76466ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 76549c5e6e2SCameron Grant 766*32a0e5d5SHans Petter Selasky /* should only be called once */ 767*32a0e5d5SHans Petter Selasky if (d->flags & SD_F_REGISTERED) 768*32a0e5d5SHans Petter Selasky return (EINVAL); 769*32a0e5d5SHans Petter Selasky 770e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 771bba4862cSAriff Abdullah 772e4e61333SAriff Abdullah if (d->playcount == 0 || d->reccount == 0) 773e4e61333SAriff Abdullah d->flags |= SD_F_SIMPLEX; 774e4e61333SAriff Abdullah 775*32a0e5d5SHans Petter Selasky if (d->playcount > 0 || d->reccount > 0) 776e4e61333SAriff Abdullah d->flags |= SD_F_AUTOVCHAN; 777e4e61333SAriff Abdullah 778e4e61333SAriff Abdullah pcm_setmaxautovchans(d, snd_maxautovchans); 779bba4862cSAriff Abdullah 780a580b31aSAriff Abdullah strlcpy(d->status, str, SND_STATUSLEN); 781bba4862cSAriff Abdullah 78290da2b28SAriff Abdullah PCM_LOCK(d); 783e4e61333SAriff Abdullah 784bba4862cSAriff Abdullah /* Last stage, enable cloning. */ 785e4e61333SAriff Abdullah if (d->clones != NULL) 786bba4862cSAriff Abdullah (void)snd_clone_enable(d->clones); 787e4e61333SAriff Abdullah 788e4e61333SAriff Abdullah /* Done, we're ready.. */ 789e4e61333SAriff Abdullah d->flags |= SD_F_REGISTERED; 790e4e61333SAriff Abdullah 791e4e61333SAriff Abdullah PCM_RELEASE(d); 792bba4862cSAriff Abdullah 79390da2b28SAriff Abdullah PCM_UNLOCK(d); 794bba4862cSAriff Abdullah 795*32a0e5d5SHans Petter Selasky /* 796*32a0e5d5SHans Petter Selasky * Create all sysctls once SD_F_REGISTERED is set else 797*32a0e5d5SHans Petter Selasky * tunable sysctls won't work: 798*32a0e5d5SHans Petter Selasky */ 799*32a0e5d5SHans Petter Selasky pcm_sysinit(dev); 800*32a0e5d5SHans Petter Selasky 801cbebc90dSAlexander Motin if (snd_unit_auto < 0) 802cbebc90dSAlexander Motin snd_unit_auto = (snd_unit < 0) ? 1 : 0; 803cbebc90dSAlexander Motin if (snd_unit < 0 || snd_unit_auto > 1) 804f3685841SAriff Abdullah snd_unit = device_get_unit(dev); 805cbebc90dSAlexander Motin else if (snd_unit_auto == 1) 806cbebc90dSAlexander Motin snd_unit = pcm_best_unit(snd_unit); 807f3685841SAriff Abdullah 808e4e61333SAriff Abdullah return (0); 809987e5972SCameron Grant } 810987e5972SCameron Grant 811a1d444e1SAriff Abdullah uint32_t 812987e5972SCameron Grant pcm_getflags(device_t dev) 813987e5972SCameron Grant { 81466ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 81549c5e6e2SCameron Grant 816987e5972SCameron Grant return d->flags; 817987e5972SCameron Grant } 818987e5972SCameron Grant 819987e5972SCameron Grant void 820a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val) 821987e5972SCameron Grant { 82266ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 823d95502a8SCameron Grant 824987e5972SCameron Grant d->flags = val; 825987e5972SCameron Grant } 826987e5972SCameron Grant 82739004e69SCameron Grant void * 82839004e69SCameron Grant pcm_getdevinfo(device_t dev) 82939004e69SCameron Grant { 83066ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 83149c5e6e2SCameron Grant 83239004e69SCameron Grant return d->devinfo; 83339004e69SCameron Grant } 83439004e69SCameron Grant 835a67fe5c1SCameron Grant unsigned int 836d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 837a67fe5c1SCameron Grant { 838a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 8394e60be34SCameron Grant int sz, x; 840a67fe5c1SCameron Grant 841a67fe5c1SCameron Grant sz = 0; 8424e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 8434e60be34SCameron Grant x = sz; 844d55d96f6SAlexander Leidinger RANGE(sz, minbufsz, maxbufsz); 8454e60be34SCameron Grant if (x != sz) 846d55d96f6SAlexander Leidinger device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 847d55d96f6SAlexander Leidinger x = minbufsz; 8484e60be34SCameron Grant while (x < sz) 8494e60be34SCameron Grant x <<= 1; 8504e60be34SCameron Grant if (x > sz) 8514e60be34SCameron Grant x >>= 1; 8524e60be34SCameron Grant if (x != sz) { 8534e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 8544e60be34SCameron Grant sz = x; 8554e60be34SCameron Grant } 8564e60be34SCameron Grant } else { 857a67fe5c1SCameron Grant sz = deflt; 8584e60be34SCameron Grant } 8594e60be34SCameron Grant 860a67fe5c1SCameron Grant d->bufsz = sz; 861a67fe5c1SCameron Grant 862a67fe5c1SCameron Grant return sz; 863a67fe5c1SCameron Grant } 864a67fe5c1SCameron Grant 86590da2b28SAriff Abdullah static int 86690da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) 86790da2b28SAriff Abdullah { 86890da2b28SAriff Abdullah struct snddev_info *d; 86990da2b28SAriff Abdullah int err, val; 87090da2b28SAriff Abdullah 87190da2b28SAriff Abdullah d = oidp->oid_arg1; 87290da2b28SAriff Abdullah if (!PCM_REGISTERED(d)) 87390da2b28SAriff Abdullah return (ENODEV); 87490da2b28SAriff Abdullah 87590da2b28SAriff Abdullah PCM_LOCK(d); 87690da2b28SAriff Abdullah PCM_WAIT(d); 87790da2b28SAriff Abdullah val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; 87890da2b28SAriff Abdullah PCM_ACQUIRE(d); 87990da2b28SAriff Abdullah PCM_UNLOCK(d); 88090da2b28SAriff Abdullah 88190da2b28SAriff Abdullah err = sysctl_handle_int(oidp, &val, 0, req); 88290da2b28SAriff Abdullah 88390da2b28SAriff Abdullah if (err == 0 && req->newptr != NULL) { 88490da2b28SAriff Abdullah if (!(val == 0 || val == 1)) { 88590da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 88690da2b28SAriff Abdullah return (EINVAL); 88790da2b28SAriff Abdullah } 88890da2b28SAriff Abdullah 88990da2b28SAriff Abdullah PCM_LOCK(d); 89090da2b28SAriff Abdullah 89190da2b28SAriff Abdullah d->flags &= ~SD_F_BITPERFECT; 89290da2b28SAriff Abdullah d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; 89390da2b28SAriff Abdullah 89490da2b28SAriff Abdullah PCM_RELEASE(d); 89590da2b28SAriff Abdullah PCM_UNLOCK(d); 89690da2b28SAriff Abdullah } else 89790da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 89890da2b28SAriff Abdullah 89990da2b28SAriff Abdullah return (err); 90090da2b28SAriff Abdullah } 90190da2b28SAriff Abdullah 90290da2b28SAriff Abdullah #ifdef SND_DEBUG 903bba4862cSAriff Abdullah static int 904bba4862cSAriff Abdullah sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS) 905bba4862cSAriff Abdullah { 906bba4862cSAriff Abdullah struct snddev_info *d; 907bba4862cSAriff Abdullah uint32_t flags; 908bba4862cSAriff Abdullah int err; 909bba4862cSAriff Abdullah 910bba4862cSAriff Abdullah d = oidp->oid_arg1; 911e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 912bba4862cSAriff Abdullah return (ENODEV); 913bba4862cSAriff Abdullah 914e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 915e4e61333SAriff Abdullah 916bba4862cSAriff Abdullah flags = snd_clone_getflags(d->clones); 917041b706bSDavid Malone err = sysctl_handle_int(oidp, &flags, 0, req); 918bba4862cSAriff Abdullah 919bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL) { 920e4e61333SAriff Abdullah if (flags & ~SND_CLONE_MASK) 921bba4862cSAriff Abdullah err = EINVAL; 922e4e61333SAriff Abdullah else 923bba4862cSAriff Abdullah (void)snd_clone_setflags(d->clones, flags); 924bba4862cSAriff Abdullah } 925e4e61333SAriff Abdullah 926e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 927bba4862cSAriff Abdullah 928bba4862cSAriff Abdullah return (err); 929bba4862cSAriff Abdullah } 930bba4862cSAriff Abdullah 931bba4862cSAriff Abdullah static int 932bba4862cSAriff Abdullah sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS) 933bba4862cSAriff Abdullah { 934bba4862cSAriff Abdullah struct snddev_info *d; 935bba4862cSAriff Abdullah int err, deadline; 936bba4862cSAriff Abdullah 937bba4862cSAriff Abdullah d = oidp->oid_arg1; 938e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 939bba4862cSAriff Abdullah return (ENODEV); 940bba4862cSAriff Abdullah 941e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 942e4e61333SAriff Abdullah 943bba4862cSAriff Abdullah deadline = snd_clone_getdeadline(d->clones); 944041b706bSDavid Malone err = sysctl_handle_int(oidp, &deadline, 0, req); 945bba4862cSAriff Abdullah 946bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL) { 947bba4862cSAriff Abdullah if (deadline < 0) 948bba4862cSAriff Abdullah err = EINVAL; 949e4e61333SAriff Abdullah else 950bba4862cSAriff Abdullah (void)snd_clone_setdeadline(d->clones, deadline); 951bba4862cSAriff Abdullah } 952e4e61333SAriff Abdullah 953e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 954bba4862cSAriff Abdullah 955bba4862cSAriff Abdullah return (err); 956bba4862cSAriff Abdullah } 957bba4862cSAriff Abdullah 958bba4862cSAriff Abdullah static int 959bba4862cSAriff Abdullah sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS) 960bba4862cSAriff Abdullah { 961bba4862cSAriff Abdullah struct snddev_info *d; 962bba4862cSAriff Abdullah int err, val; 963bba4862cSAriff Abdullah 964bba4862cSAriff Abdullah d = oidp->oid_arg1; 965e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 966bba4862cSAriff Abdullah return (ENODEV); 967bba4862cSAriff Abdullah 968bba4862cSAriff Abdullah val = 0; 969041b706bSDavid Malone err = sysctl_handle_int(oidp, &val, 0, req); 970bba4862cSAriff Abdullah 971bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL && val != 0) { 972e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 973e4e61333SAriff Abdullah val = snd_clone_gc(d->clones); 974e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 975e4e61333SAriff Abdullah if (bootverbose != 0 || snd_verbose > 3) 976e4e61333SAriff Abdullah device_printf(d->dev, "clone gc: pruned=%d\n", val); 977bba4862cSAriff Abdullah } 978bba4862cSAriff Abdullah 979bba4862cSAriff Abdullah return (err); 980bba4862cSAriff Abdullah } 981bba4862cSAriff Abdullah 982bba4862cSAriff Abdullah static int 983bba4862cSAriff Abdullah sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS) 984bba4862cSAriff Abdullah { 985bba4862cSAriff Abdullah struct snddev_info *d; 986bba4862cSAriff Abdullah int i, err, val; 987bba4862cSAriff Abdullah 988bba4862cSAriff Abdullah val = 0; 989041b706bSDavid Malone err = sysctl_handle_int(oidp, &val, 0, req); 990bba4862cSAriff Abdullah 991bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL && val != 0) { 9929c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 9939c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 994bba4862cSAriff Abdullah d = devclass_get_softc(pcm_devclass, i); 995e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 996bba4862cSAriff Abdullah continue; 997e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 998e4e61333SAriff Abdullah val = snd_clone_gc(d->clones); 999e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 1000e4e61333SAriff Abdullah if (bootverbose != 0 || snd_verbose > 3) 1001e4e61333SAriff Abdullah device_printf(d->dev, "clone gc: pruned=%d\n", 1002e4e61333SAriff Abdullah val); 1003bba4862cSAriff Abdullah } 1004bba4862cSAriff Abdullah } 1005bba4862cSAriff Abdullah 1006bba4862cSAriff Abdullah return (err); 1007bba4862cSAriff Abdullah } 1008*32a0e5d5SHans Petter Selasky SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RWTUN, 1009bba4862cSAriff Abdullah 0, sizeof(int), sysctl_hw_snd_clone_gc, "I", 1010bba4862cSAriff Abdullah "global clone garbage collector"); 1011bba4862cSAriff Abdullah #endif 1012bba4862cSAriff Abdullah 1013*32a0e5d5SHans Petter Selasky static void 1014*32a0e5d5SHans Petter Selasky pcm_sysinit(device_t dev) 1015*32a0e5d5SHans Petter Selasky { 1016*32a0e5d5SHans Petter Selasky struct snddev_info *d = device_get_softc(dev); 1017*32a0e5d5SHans Petter Selasky 1018*32a0e5d5SHans Petter Selasky /* XXX: an user should be able to set this with a control tool, the 1019*32a0e5d5SHans Petter Selasky sysadmin then needs min+max sysctls for this */ 1020*32a0e5d5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 1021*32a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 1022*32a0e5d5SHans Petter Selasky OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 1023*32a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1024*32a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1025*32a0e5d5SHans Petter Selasky "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN, d, sizeof(d), 1026*32a0e5d5SHans Petter Selasky sysctl_dev_pcm_bitperfect, "I", 1027*32a0e5d5SHans Petter Selasky "bit-perfect playback/recording (0=disable, 1=enable)"); 1028*32a0e5d5SHans Petter Selasky #ifdef SND_DEBUG 1029*32a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1030*32a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1031*32a0e5d5SHans Petter Selasky "clone_flags", CTLTYPE_UINT | CTLFLAG_RWTUN, d, sizeof(d), 1032*32a0e5d5SHans Petter Selasky sysctl_dev_pcm_clone_flags, "IU", 1033*32a0e5d5SHans Petter Selasky "clone flags"); 1034*32a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1035*32a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1036*32a0e5d5SHans Petter Selasky "clone_deadline", CTLTYPE_INT | CTLFLAG_RWTUN, d, sizeof(d), 1037*32a0e5d5SHans Petter Selasky sysctl_dev_pcm_clone_deadline, "I", 1038*32a0e5d5SHans Petter Selasky "clone expiration deadline (ms)"); 1039*32a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1040*32a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1041*32a0e5d5SHans Petter Selasky "clone_gc", CTLTYPE_INT | CTLFLAG_RWTUN, d, sizeof(d), 1042*32a0e5d5SHans Petter Selasky sysctl_dev_pcm_clone_gc, "I", 1043*32a0e5d5SHans Petter Selasky "clone garbage collector"); 1044*32a0e5d5SHans Petter Selasky #endif 1045*32a0e5d5SHans Petter Selasky if (d->flags & SD_F_AUTOVCHAN) 1046*32a0e5d5SHans Petter Selasky vchan_initsys(dev); 1047*32a0e5d5SHans Petter Selasky if (d->flags & SD_F_EQ) 1048*32a0e5d5SHans Petter Selasky feeder_eq_initsys(dev); 1049*32a0e5d5SHans Petter Selasky } 1050*32a0e5d5SHans Petter Selasky 1051987e5972SCameron Grant int 1052987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 1053987e5972SCameron Grant { 1054bba4862cSAriff Abdullah struct snddev_info *d; 105590da2b28SAriff Abdullah int i; 1056987e5972SCameron Grant 1057b8a36395SCameron Grant if (pcm_veto_load) { 1058b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 1059b8a36395SCameron Grant 1060b8a36395SCameron Grant return EINVAL; 1061b8a36395SCameron Grant } 1062b8a36395SCameron Grant 1063bba4862cSAriff Abdullah if (device_get_unit(dev) > PCMMAXUNIT) { 1064bba4862cSAriff Abdullah device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n", 1065bba4862cSAriff Abdullah device_get_unit(dev), PCMMAXUNIT); 1066bba4862cSAriff Abdullah device_printf(dev, 1067bba4862cSAriff Abdullah "Use 'hw.snd.maxunit' tunable to raise the limit.\n"); 1068bba4862cSAriff Abdullah return ENODEV; 1069bba4862cSAriff Abdullah } 1070bba4862cSAriff Abdullah 1071bba4862cSAriff Abdullah d = device_get_softc(dev); 1072e4e61333SAriff Abdullah d->dev = dev; 10732c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 1074e4e61333SAriff Abdullah cv_init(&d->cv, device_get_nameunit(dev)); 1075e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 1076e4e61333SAriff Abdullah dsp_cdevinfo_init(d); 10777233ababSAlexander Leidinger #if 0 10787233ababSAlexander Leidinger /* 10797233ababSAlexander Leidinger * d->flags should be cleared by the allocator of the softc. 10807233ababSAlexander Leidinger * We cannot clear this field here because several devices set 10817233ababSAlexander Leidinger * this flag before calling pcm_register(). 10827233ababSAlexander Leidinger */ 1083cd9766c5SCameron Grant d->flags = 0; 10847233ababSAlexander Leidinger #endif 108590da2b28SAriff Abdullah i = 0; 108690da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 108790da2b28SAriff Abdullah "vpc", &i) != 0 || i != 0) 108890da2b28SAriff Abdullah d->flags |= SD_F_VPC; 108990da2b28SAriff Abdullah 109090da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 109190da2b28SAriff Abdullah "bitperfect", &i) == 0 && i != 0) 109290da2b28SAriff Abdullah d->flags |= SD_F_BITPERFECT; 109390da2b28SAriff Abdullah 1094987e5972SCameron Grant d->devinfo = devinfo; 1095f637a36cSCameron Grant d->devcount = 0; 1096506a5308SCameron Grant d->reccount = 0; 1097a67fe5c1SCameron Grant d->playcount = 0; 1098bba4862cSAriff Abdullah d->pvchancount = 0; 1099bba4862cSAriff Abdullah d->rvchancount = 0; 1100bba4862cSAriff Abdullah d->pvchanrate = 0; 1101bba4862cSAriff Abdullah d->pvchanformat = 0; 1102bba4862cSAriff Abdullah d->rvchanrate = 0; 1103bba4862cSAriff Abdullah d->rvchanformat = 0; 1104d95502a8SCameron Grant d->inprog = 0; 1105833f7023SCameron Grant 1106bba4862cSAriff Abdullah /* 1107bba4862cSAriff Abdullah * Create clone manager, disabled by default. Cloning will be 110890da2b28SAriff Abdullah * enabled during final stage of driver initialization through 1109bba4862cSAriff Abdullah * pcm_setstatus(). 1110bba4862cSAriff Abdullah */ 1111e4e61333SAriff Abdullah d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE, 1112e4e61333SAriff Abdullah SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK | 1113bba4862cSAriff Abdullah SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF | 1114bba4862cSAriff Abdullah SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED); 1115bba4862cSAriff Abdullah 1116bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm); 1117bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm.busy); 111890da2b28SAriff Abdullah CHN_INIT(d, channels.pcm.opened); 111945550658SPoul-Henning Kamp 1120e4e61333SAriff Abdullah /* XXX This is incorrect, but lets play along for now. */ 1121a1d444e1SAriff Abdullah if ((numplay == 0 || numrec == 0) && numplay != numrec) 1122285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 1123d95502a8SCameron Grant 1124bba4862cSAriff Abdullah sysctl_ctx_init(&d->play_sysctl_ctx); 1125bba4862cSAriff Abdullah d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 1126bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 1127bba4862cSAriff Abdullah CTLFLAG_RD, 0, "playback channels node"); 1128bba4862cSAriff Abdullah sysctl_ctx_init(&d->rec_sysctl_ctx); 1129bba4862cSAriff Abdullah d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 1130bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 1131bba4862cSAriff Abdullah CTLFLAG_RD, 0, "record channels node"); 1132e4e61333SAriff Abdullah 1133*32a0e5d5SHans Petter Selasky if (numplay > 0 || numrec > 0) 1134cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 113590da2b28SAriff Abdullah 113667b1dce3SCameron Grant sndstat_register(dev, d->status, sndstat_prepare_pcm); 1137e4e61333SAriff Abdullah 1138987e5972SCameron Grant return 0; 1139987e5972SCameron Grant } 1140987e5972SCameron Grant 114133dbf14aSCameron Grant int 114233dbf14aSCameron Grant pcm_unregister(device_t dev) 11437c438dbeSCameron Grant { 1144e4e61333SAriff Abdullah struct snddev_info *d; 1145a67fe5c1SCameron Grant struct pcm_channel *ch; 1146bba4862cSAriff Abdullah struct thread *td; 11477c438dbeSCameron Grant 1148bba4862cSAriff Abdullah td = curthread; 1149e4e61333SAriff Abdullah d = device_get_softc(dev); 1150e4e61333SAriff Abdullah 1151e4e61333SAriff Abdullah if (!PCM_ALIVE(d)) { 1152e4e61333SAriff Abdullah device_printf(dev, "unregister: device not configured\n"); 1153e4e61333SAriff Abdullah return (0); 1154e4e61333SAriff Abdullah } 1155bba4862cSAriff Abdullah 1156bba4862cSAriff Abdullah if (sndstat_acquire(td) != 0) { 115728ef3fb0SAlexander Leidinger device_printf(dev, "unregister: sndstat busy\n"); 1158e4e61333SAriff Abdullah return (EBUSY); 115928ef3fb0SAlexander Leidinger } 116028ef3fb0SAlexander Leidinger 116190da2b28SAriff Abdullah PCM_LOCK(d); 1162e4e61333SAriff Abdullah PCM_WAIT(d); 1163e4e61333SAriff Abdullah 1164e4e61333SAriff Abdullah if (d->inprog != 0) { 11655c25132aSGeorge C A Reid device_printf(dev, "unregister: operation in progress\n"); 116690da2b28SAriff Abdullah PCM_UNLOCK(d); 1167bba4862cSAriff Abdullah sndstat_release(td); 1168e4e61333SAriff Abdullah return (EBUSY); 11695c25132aSGeorge C A Reid } 11705ee30e27SMathew Kanner 1171e4e61333SAriff Abdullah PCM_ACQUIRE(d); 117290da2b28SAriff Abdullah PCM_UNLOCK(d); 1173e4e61333SAriff Abdullah 1174e4e61333SAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 1175e4e61333SAriff Abdullah CHN_LOCK(ch); 1176e4e61333SAriff Abdullah if (ch->refcount > 0) { 1177e4e61333SAriff Abdullah device_printf(dev, 1178e4e61333SAriff Abdullah "unregister: channel %s busy (pid %d)\n", 1179e4e61333SAriff Abdullah ch->name, ch->pid); 1180e4e61333SAriff Abdullah CHN_UNLOCK(ch); 1181e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 1182bba4862cSAriff Abdullah sndstat_release(td); 1183e4e61333SAriff Abdullah return (EBUSY); 1184285648f9SCameron Grant } 1185e4e61333SAriff Abdullah CHN_UNLOCK(ch); 1186c9b53085SCameron Grant } 11875ee30e27SMathew Kanner 1188bba4862cSAriff Abdullah if (d->clones != NULL) { 1189bba4862cSAriff Abdullah if (snd_clone_busy(d->clones) != 0) { 1190bba4862cSAriff Abdullah device_printf(dev, "unregister: clone busy\n"); 1191e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 1192bba4862cSAriff Abdullah sndstat_release(td); 1193e4e61333SAriff Abdullah return (EBUSY); 1194e4e61333SAriff Abdullah } else { 119590da2b28SAriff Abdullah PCM_LOCK(d); 1196bba4862cSAriff Abdullah (void)snd_clone_disable(d->clones); 119790da2b28SAriff Abdullah PCM_UNLOCK(d); 1198e4e61333SAriff Abdullah } 1199bba4862cSAriff Abdullah } 1200bba4862cSAriff Abdullah 1201b4221868SAriff Abdullah if (mixer_uninit(dev) == EBUSY) { 12027233ababSAlexander Leidinger device_printf(dev, "unregister: mixer busy\n"); 120390da2b28SAriff Abdullah PCM_LOCK(d); 1204bba4862cSAriff Abdullah if (d->clones != NULL) 1205bba4862cSAriff Abdullah (void)snd_clone_enable(d->clones); 1206e4e61333SAriff Abdullah PCM_RELEASE(d); 120790da2b28SAriff Abdullah PCM_UNLOCK(d); 1208bba4862cSAriff Abdullah sndstat_release(td); 1209e4e61333SAriff Abdullah return (EBUSY); 12107233ababSAlexander Leidinger } 12117233ababSAlexander Leidinger 121290da2b28SAriff Abdullah PCM_LOCK(d); 1213e4e61333SAriff Abdullah d->flags |= SD_F_DYING; 1214e4e61333SAriff Abdullah d->flags &= ~SD_F_REGISTERED; 121590da2b28SAriff Abdullah PCM_UNLOCK(d); 1216e4e61333SAriff Abdullah 1217e4e61333SAriff Abdullah /* 1218e4e61333SAriff Abdullah * No lock being held, so this thing can be flushed without 1219e4e61333SAriff Abdullah * stucking into devdrn oblivion. 1220e4e61333SAriff Abdullah */ 1221bba4862cSAriff Abdullah if (d->clones != NULL) { 1222bba4862cSAriff Abdullah snd_clone_destroy(d->clones); 1223bba4862cSAriff Abdullah d->clones = NULL; 12247982e7a4SAriff Abdullah } 1225bba4862cSAriff Abdullah 1226bba4862cSAriff Abdullah if (d->play_sysctl_tree != NULL) { 1227bba4862cSAriff Abdullah sysctl_ctx_free(&d->play_sysctl_ctx); 1228bba4862cSAriff Abdullah d->play_sysctl_tree = NULL; 1229bba4862cSAriff Abdullah } 1230bba4862cSAriff Abdullah if (d->rec_sysctl_tree != NULL) { 1231bba4862cSAriff Abdullah sysctl_ctx_free(&d->rec_sysctl_ctx); 1232bba4862cSAriff Abdullah d->rec_sysctl_tree = NULL; 1233a1d444e1SAriff Abdullah } 1234bba4862cSAriff Abdullah 1235bba4862cSAriff Abdullah while (!CHN_EMPTY(d, channels.pcm)) 1236285648f9SCameron Grant pcm_killchan(dev); 12377c438dbeSCameron Grant 1238e4e61333SAriff Abdullah dsp_cdevinfo_flush(d); 1239e4e61333SAriff Abdullah 124090da2b28SAriff Abdullah PCM_LOCK(d); 1241e4e61333SAriff Abdullah PCM_RELEASE(d); 1242e4e61333SAriff Abdullah cv_destroy(&d->cv); 124390da2b28SAriff Abdullah PCM_UNLOCK(d); 124449c5e6e2SCameron Grant snd_mtxfree(d->lock); 124528ef3fb0SAlexander Leidinger sndstat_unregister(dev); 1246bba4862cSAriff Abdullah sndstat_release(td); 1247bba4862cSAriff Abdullah 1248bba4862cSAriff Abdullah if (snd_unit == device_get_unit(dev)) { 1249cbebc90dSAlexander Motin snd_unit = pcm_best_unit(-1); 1250cbebc90dSAlexander Motin if (snd_unit_auto == 0) 1251cbebc90dSAlexander Motin snd_unit_auto = 1; 1252e4e61333SAriff Abdullah } 1253bba4862cSAriff Abdullah 1254e4e61333SAriff Abdullah return (0); 125533dbf14aSCameron Grant } 12567c438dbeSCameron Grant 125767b1dce3SCameron Grant /************************************************************************/ 125867b1dce3SCameron Grant 1259b611c801SAlexander Leidinger /** 1260b611c801SAlexander Leidinger * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 1261b611c801SAlexander Leidinger * 1262b611c801SAlexander Leidinger * @param si Pointer to oss_sysinfo struct where information about the 1263b611c801SAlexander Leidinger * sound subsystem will be written/copied. 1264b611c801SAlexander Leidinger * 1265b611c801SAlexander Leidinger * This routine returns information about the sound system, such as the 1266b611c801SAlexander Leidinger * current OSS version, number of audio, MIDI, and mixer drivers, etc. 1267b611c801SAlexander Leidinger * Also includes a bitmask showing which of the above types of devices 1268b611c801SAlexander Leidinger * are open (busy). 1269b611c801SAlexander Leidinger * 1270b611c801SAlexander Leidinger * @note 1271b611c801SAlexander Leidinger * Calling threads must not hold any snddev_info or pcm_channel locks. 1272b611c801SAlexander Leidinger * 1273b611c801SAlexander Leidinger * @author Ryan Beasley <ryanb@FreeBSD.org> 1274b611c801SAlexander Leidinger */ 1275b611c801SAlexander Leidinger void 1276b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si) 1277b611c801SAlexander Leidinger { 1278b611c801SAlexander Leidinger static char si_product[] = "FreeBSD native OSS ABI"; 1279b611c801SAlexander Leidinger static char si_version[] = __XSTRING(__FreeBSD_version); 1280c412d503SAlexander Motin static char si_license[] = "BSD"; 1281b611c801SAlexander Leidinger static int intnbits = sizeof(int) * 8; /* Better suited as macro? 1282b611c801SAlexander Leidinger Must pester a C guru. */ 1283b611c801SAlexander Leidinger 1284b611c801SAlexander Leidinger struct snddev_info *d; 1285b611c801SAlexander Leidinger struct pcm_channel *c; 1286b611c801SAlexander Leidinger int i, j, ncards; 1287b611c801SAlexander Leidinger 1288b611c801SAlexander Leidinger ncards = 0; 1289b611c801SAlexander Leidinger 1290b611c801SAlexander Leidinger strlcpy(si->product, si_product, sizeof(si->product)); 1291b611c801SAlexander Leidinger strlcpy(si->version, si_version, sizeof(si->version)); 1292b611c801SAlexander Leidinger si->versionnum = SOUND_VERSION; 1293c412d503SAlexander Motin strlcpy(si->license, si_license, sizeof(si->license)); 1294b611c801SAlexander Leidinger 1295b611c801SAlexander Leidinger /* 1296b611c801SAlexander Leidinger * Iterate over PCM devices and their channels, gathering up data 1297b611c801SAlexander Leidinger * for the numaudios, ncards, and openedaudio fields. 1298b611c801SAlexander Leidinger */ 1299b611c801SAlexander Leidinger si->numaudios = 0; 1300b611c801SAlexander Leidinger bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 1301b611c801SAlexander Leidinger 1302b611c801SAlexander Leidinger j = 0; 1303b611c801SAlexander Leidinger 13049c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 13059c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 1306b611c801SAlexander Leidinger d = devclass_get_softc(pcm_devclass, i); 1307e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 1308b611c801SAlexander Leidinger continue; 1309b611c801SAlexander Leidinger 1310e4e61333SAriff Abdullah /* XXX Need Giant magic entry ??? */ 1311e4e61333SAriff Abdullah 1312b611c801SAlexander Leidinger /* See note in function's docblock */ 131390da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 131490da2b28SAriff Abdullah PCM_LOCK(d); 1315b611c801SAlexander Leidinger 1316b611c801SAlexander Leidinger si->numaudios += d->devcount; 1317b611c801SAlexander Leidinger ++ncards; 1318b611c801SAlexander Leidinger 1319bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 132090da2b28SAriff Abdullah CHN_UNLOCKASSERT(c); 1321b611c801SAlexander Leidinger CHN_LOCK(c); 1322b611c801SAlexander Leidinger if (c->flags & CHN_F_BUSY) 1323b611c801SAlexander Leidinger si->openedaudio[j / intnbits] |= 1324b611c801SAlexander Leidinger (1 << (j % intnbits)); 1325b611c801SAlexander Leidinger CHN_UNLOCK(c); 1326b611c801SAlexander Leidinger j++; 1327b611c801SAlexander Leidinger } 1328b611c801SAlexander Leidinger 132990da2b28SAriff Abdullah PCM_UNLOCK(d); 1330b611c801SAlexander Leidinger } 1331c412d503SAlexander Motin si->numaudioengines = si->numaudios; 1332b611c801SAlexander Leidinger 1333b611c801SAlexander Leidinger si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 1334b611c801SAlexander Leidinger /** 1335b611c801SAlexander Leidinger * @todo Collect num{midis,timers}. 1336b611c801SAlexander Leidinger * 1337b611c801SAlexander Leidinger * Need access to sound/midi/midi.c::midistat_lock in order 1338b611c801SAlexander Leidinger * to safely touch midi_devices and get a head count of, well, 1339b611c801SAlexander Leidinger * MIDI devices. midistat_lock is a global static (i.e., local to 1340b611c801SAlexander Leidinger * midi.c), but midi_devices is a regular global; should the mutex 1341b611c801SAlexander Leidinger * be publicized, or is there another way to get this information? 1342b611c801SAlexander Leidinger * 1343b611c801SAlexander Leidinger * NB: MIDI/sequencer stuff is currently on hold. 1344b611c801SAlexander Leidinger */ 1345b611c801SAlexander Leidinger si->nummidis = 0; 1346b611c801SAlexander Leidinger si->numtimers = 0; 1347b611c801SAlexander Leidinger si->nummixers = mixer_count; 1348b611c801SAlexander Leidinger si->numcards = ncards; 1349b611c801SAlexander Leidinger /* OSSv4 docs: Intended only for test apps; API doesn't 1350b611c801SAlexander Leidinger really have much of a concept of cards. Shouldn't be 1351b611c801SAlexander Leidinger used by applications. */ 1352b611c801SAlexander Leidinger 1353b611c801SAlexander Leidinger /** 1354b611c801SAlexander Leidinger * @todo Fill in "busy devices" fields. 1355b611c801SAlexander Leidinger * 1356b611c801SAlexander Leidinger * si->openedmidi = " MIDI devices 1357b611c801SAlexander Leidinger */ 1358b611c801SAlexander Leidinger bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 1359b611c801SAlexander Leidinger 1360b611c801SAlexander Leidinger /* 1361b611c801SAlexander Leidinger * Si->filler is a reserved array, but according to docs each 1362b611c801SAlexander Leidinger * element should be set to -1. 1363b611c801SAlexander Leidinger */ 1364b611c801SAlexander Leidinger for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++) 1365b611c801SAlexander Leidinger si->filler[i] = -1; 1366b611c801SAlexander Leidinger } 1367b611c801SAlexander Leidinger 136852f6e09eSAlexander Motin int 136952f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si) 137052f6e09eSAlexander Motin { 137152f6e09eSAlexander Motin struct snddev_info *d; 137252f6e09eSAlexander Motin int i, ncards; 137352f6e09eSAlexander Motin 137452f6e09eSAlexander Motin ncards = 0; 137552f6e09eSAlexander Motin 137652f6e09eSAlexander Motin for (i = 0; pcm_devclass != NULL && 137752f6e09eSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 137852f6e09eSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 137952f6e09eSAlexander Motin if (!PCM_REGISTERED(d)) 138052f6e09eSAlexander Motin continue; 138152f6e09eSAlexander Motin 138252f6e09eSAlexander Motin if (ncards++ != si->card) 138352f6e09eSAlexander Motin continue; 138452f6e09eSAlexander Motin 138590da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 138690da2b28SAriff Abdullah PCM_LOCK(d); 138752f6e09eSAlexander Motin 138852f6e09eSAlexander Motin strlcpy(si->shortname, device_get_nameunit(d->dev), 138952f6e09eSAlexander Motin sizeof(si->shortname)); 139052f6e09eSAlexander Motin strlcpy(si->longname, device_get_desc(d->dev), 139152f6e09eSAlexander Motin sizeof(si->longname)); 139252f6e09eSAlexander Motin strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 139352f6e09eSAlexander Motin si->intr_count = si->ack_count = 0; 139490da2b28SAriff Abdullah 139590da2b28SAriff Abdullah PCM_UNLOCK(d); 139690da2b28SAriff Abdullah 139752f6e09eSAlexander Motin return (0); 139852f6e09eSAlexander Motin } 139952f6e09eSAlexander Motin return (ENXIO); 140052f6e09eSAlexander Motin } 140152f6e09eSAlexander Motin 1402b611c801SAlexander Leidinger /************************************************************************/ 1403b611c801SAlexander Leidinger 14040739ea1dSSeigo Tanimura static int 14050739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data) 14060739ea1dSSeigo Tanimura { 1407b611c801SAlexander Leidinger int ret; 14087233ababSAlexander Leidinger #if 0 14090739ea1dSSeigo Tanimura return (midi_modevent(mod, type, data)); 14107233ababSAlexander Leidinger #else 1411b611c801SAlexander Leidinger ret = 0; 1412b611c801SAlexander Leidinger 1413b611c801SAlexander Leidinger switch(type) { 1414b611c801SAlexander Leidinger case MOD_LOAD: 1415b611c801SAlexander Leidinger pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 1416b611c801SAlexander Leidinger break; 1417b611c801SAlexander Leidinger case MOD_UNLOAD: 1418bba4862cSAriff Abdullah ret = sndstat_acquire(curthread); 1419bba4862cSAriff Abdullah if (ret != 0) 1420bba4862cSAriff Abdullah break; 1421b611c801SAlexander Leidinger if (pcmsg_unrhdr != NULL) { 1422b611c801SAlexander Leidinger delete_unrhdr(pcmsg_unrhdr); 1423b611c801SAlexander Leidinger pcmsg_unrhdr = NULL; 1424b611c801SAlexander Leidinger } 1425b611c801SAlexander Leidinger break; 14265525b53dSAlexander Motin case MOD_SHUTDOWN: 14275525b53dSAlexander Motin break; 1428b611c801SAlexander Leidinger default: 142990da2b28SAriff Abdullah ret = ENOTSUP; 1430b611c801SAlexander Leidinger } 1431b611c801SAlexander Leidinger 1432b611c801SAlexander Leidinger return ret; 14337233ababSAlexander Leidinger #endif 14340739ea1dSSeigo Tanimura } 14350739ea1dSSeigo Tanimura 14360739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL); 14370739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER); 1438