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; 52851a904aSAlexander Leidinger TUNABLE_INT("hw.snd.default_unit", &snd_unit); 53cbe7d6a3SCameron Grant 54ad8612b9SAriff Abdullah static int snd_unit_auto = 0; 55ad8612b9SAriff Abdullah TUNABLE_INT("hw.snd.default_auto", &snd_unit_auto); 56ad8612b9SAriff Abdullah SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RW, 57838d3589SAriff Abdullah &snd_unit_auto, 0, "assign default unit to a newly attached device"); 58ad8612b9SAriff Abdullah 59bba4862cSAriff Abdullah int snd_maxautovchans = 16; 60851a904aSAlexander Leidinger /* XXX: a tunable implies that we may need more than one sound channel before 61851a904aSAlexander Leidinger the system can change a sysctl (/etc/sysctl.conf), do we really need 62851a904aSAlexander Leidinger this? */ 6367b1dce3SCameron Grant TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 64987e5972SCameron Grant 6582db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 6682db23e2SCameron Grant 67bba4862cSAriff Abdullah /* 68bba4862cSAriff Abdullah * XXX I've had enough with people not telling proper version/arch 69bba4862cSAriff Abdullah * while reporting problems, not after 387397913213th questions/requests. 70bba4862cSAriff Abdullah */ 71bba4862cSAriff Abdullah static const char snd_driver_version[] = 72bba4862cSAriff Abdullah __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH; 73bba4862cSAriff Abdullah SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version, 7490da2b28SAriff Abdullah 0, "driver version/arch"); 75bba4862cSAriff Abdullah 76b611c801SAlexander Leidinger /** 77b611c801SAlexander Leidinger * @brief Unit number allocator for syncgroup IDs 78b611c801SAlexander Leidinger */ 79b611c801SAlexander Leidinger struct unrhdr *pcmsg_unrhdr = NULL; 80b611c801SAlexander Leidinger 8190da2b28SAriff Abdullah static int 8290da2b28SAriff Abdullah sndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS) 8390da2b28SAriff Abdullah { 8490da2b28SAriff Abdullah SNDSTAT_PREPARE_PCM_BEGIN(); 8590da2b28SAriff Abdullah SNDSTAT_PREPARE_PCM_END(); 8690da2b28SAriff Abdullah } 8767b1dce3SCameron Grant 8837209180SCameron Grant void * 892c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type) 9037209180SCameron Grant { 9137209180SCameron Grant struct mtx *m; 9237209180SCameron Grant 93a163d034SWarner Losh m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 9412e524a2SDon Lewis mtx_init(m, desc, type, MTX_DEF); 9512e524a2SDon Lewis return m; 9612e524a2SDon Lewis } 9712e524a2SDon Lewis 9837209180SCameron Grant void 9937209180SCameron Grant snd_mtxfree(void *m) 10037209180SCameron Grant { 10137209180SCameron Grant struct mtx *mtx = m; 10237209180SCameron Grant 10337209180SCameron Grant mtx_destroy(mtx); 10437209180SCameron Grant free(mtx, M_DEVBUF); 10537209180SCameron Grant } 10637209180SCameron Grant 10737209180SCameron Grant void 10837209180SCameron Grant snd_mtxassert(void *m) 10937209180SCameron Grant { 110f00f162aSCameron Grant #ifdef INVARIANTS 11137209180SCameron Grant struct mtx *mtx = m; 11237209180SCameron Grant 11337209180SCameron Grant mtx_assert(mtx, MA_OWNED); 11437209180SCameron Grant #endif 11537209180SCameron Grant } 11637209180SCameron Grant 11737209180SCameron Grant int 11837209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 11937209180SCameron Grant { 120e4e61333SAriff Abdullah struct snddev_info *d; 12190da2b28SAriff Abdullah 12237209180SCameron Grant flags &= INTR_MPSAFE; 12346700f12SPeter Wemm flags |= INTR_TYPE_AV; 124e4e61333SAriff Abdullah d = device_get_softc(dev); 125e4e61333SAriff Abdullah if (d != NULL && (flags & INTR_MPSAFE)) 126e4e61333SAriff Abdullah d->flags |= SD_F_MPSAFE; 127e4e61333SAriff Abdullah 1282cc08b74SAriff Abdullah return bus_setup_intr(dev, res, flags, 1292cc08b74SAriff Abdullah #if __FreeBSD_version >= 700031 1302cc08b74SAriff Abdullah NULL, 1312cc08b74SAriff Abdullah #endif 1322cc08b74SAriff Abdullah hand, param, cookiep); 13337209180SCameron Grant } 13437209180SCameron Grant 135bba4862cSAriff Abdullah static void 136bba4862cSAriff Abdullah pcm_clonereset(struct snddev_info *d) 137a1d444e1SAriff Abdullah { 138bba4862cSAriff Abdullah int cmax; 139bba4862cSAriff Abdullah 140e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 141bba4862cSAriff Abdullah 142bba4862cSAriff Abdullah cmax = d->playcount + d->reccount - 1; 143bba4862cSAriff Abdullah if (d->pvchancount > 0) 14490da2b28SAriff Abdullah cmax += max(d->pvchancount, snd_maxautovchans) - 1; 145bba4862cSAriff Abdullah if (d->rvchancount > 0) 14690da2b28SAriff Abdullah cmax += max(d->rvchancount, snd_maxautovchans) - 1; 147bba4862cSAriff Abdullah if (cmax > PCMMAXCLONE) 148bba4862cSAriff Abdullah cmax = PCMMAXCLONE; 149bba4862cSAriff Abdullah (void)snd_clone_gc(d->clones); 150bba4862cSAriff Abdullah (void)snd_clone_setmaxunit(d->clones, cmax); 151bba4862cSAriff Abdullah } 152bba4862cSAriff Abdullah 15390da2b28SAriff Abdullah int 154bba4862cSAriff Abdullah pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) 155bba4862cSAriff Abdullah { 156bba4862cSAriff Abdullah struct pcm_channel *c, *ch, *nch; 15790da2b28SAriff Abdullah struct pcmchan_caps *caps; 15890da2b28SAriff Abdullah int i, err, vcnt; 159bba4862cSAriff Abdullah 160e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 161a1d444e1SAriff Abdullah 162bba4862cSAriff Abdullah if ((direction == PCMDIR_PLAY && d->playcount < 1) || 163e4e61333SAriff Abdullah (direction == PCMDIR_REC && d->reccount < 1)) 164e4e61333SAriff Abdullah return (ENODEV); 165a580b31aSAriff Abdullah 166e4e61333SAriff Abdullah if (!(d->flags & SD_F_AUTOVCHAN)) 167e4e61333SAriff Abdullah return (EINVAL); 168a1d444e1SAriff Abdullah 169e4e61333SAriff Abdullah if (newcnt < 0 || newcnt > SND_MAXVCHANS) 170e4e61333SAriff Abdullah return (E2BIG); 171a1d444e1SAriff Abdullah 172bba4862cSAriff Abdullah if (direction == PCMDIR_PLAY) 173bba4862cSAriff Abdullah vcnt = d->pvchancount; 174bba4862cSAriff Abdullah else if (direction == PCMDIR_REC) 175bba4862cSAriff Abdullah vcnt = d->rvchancount; 176e4e61333SAriff Abdullah else 177e4e61333SAriff Abdullah return (EINVAL); 178a1d444e1SAriff Abdullah 179a1d444e1SAriff Abdullah if (newcnt > vcnt) { 180bba4862cSAriff Abdullah KASSERT(num == -1 || 181bba4862cSAriff Abdullah (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt), 182bba4862cSAriff Abdullah ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d", 183bba4862cSAriff Abdullah num, newcnt, vcnt)); 184a1d444e1SAriff Abdullah /* add new vchans - find a parent channel first */ 185e4e61333SAriff Abdullah ch = NULL; 186bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 187a1d444e1SAriff Abdullah CHN_LOCK(c); 188bba4862cSAriff Abdullah if (c->direction == direction && 189bba4862cSAriff Abdullah ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 19090da2b28SAriff Abdullah c->refcount < 1 && 191e4e61333SAriff Abdullah !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 19290da2b28SAriff Abdullah /* 19390da2b28SAriff Abdullah * Reuse hw channel with vchans already 19490da2b28SAriff Abdullah * created. 19590da2b28SAriff Abdullah */ 19690da2b28SAriff Abdullah if (c->flags & CHN_F_HAS_VCHAN) { 197e4e61333SAriff Abdullah ch = c; 198e4e61333SAriff Abdullah break; 199e4e61333SAriff Abdullah } 20090da2b28SAriff Abdullah /* 20190da2b28SAriff Abdullah * No vchans ever created, look for 20290da2b28SAriff Abdullah * channels with supported formats. 20390da2b28SAriff Abdullah */ 20490da2b28SAriff Abdullah caps = chn_getcaps(c); 20590da2b28SAriff Abdullah if (caps == NULL) { 20690da2b28SAriff Abdullah CHN_UNLOCK(c); 20790da2b28SAriff Abdullah continue; 20890da2b28SAriff Abdullah } 20990da2b28SAriff Abdullah for (i = 0; caps->fmtlist[i] != 0; i++) { 21090da2b28SAriff Abdullah if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 21190da2b28SAriff Abdullah break; 21290da2b28SAriff Abdullah } 21390da2b28SAriff Abdullah if (caps->fmtlist[i] != 0) { 21490da2b28SAriff Abdullah ch = c; 21590da2b28SAriff Abdullah break; 21690da2b28SAriff Abdullah } 21790da2b28SAriff Abdullah } 218a1d444e1SAriff Abdullah CHN_UNLOCK(c); 219a1d444e1SAriff Abdullah } 220e4e61333SAriff Abdullah if (ch == NULL) 221e4e61333SAriff Abdullah return (EBUSY); 222e4e61333SAriff Abdullah ch->flags |= CHN_F_BUSY; 223e4e61333SAriff Abdullah err = 0; 224a1d444e1SAriff Abdullah while (err == 0 && newcnt > vcnt) { 225e4e61333SAriff Abdullah err = vchan_create(ch, num); 226bba4862cSAriff Abdullah if (err == 0) 227a1d444e1SAriff Abdullah vcnt++; 228bba4862cSAriff Abdullah else if (err == E2BIG && newcnt > vcnt) 229bba4862cSAriff Abdullah device_printf(d->dev, 230bba4862cSAriff Abdullah "%s: err=%d Maximum channel reached.\n", 231bba4862cSAriff Abdullah __func__, err); 232a1d444e1SAriff Abdullah } 233a1d444e1SAriff Abdullah if (vcnt == 0) 234e4e61333SAriff Abdullah ch->flags &= ~CHN_F_BUSY; 235e4e61333SAriff Abdullah CHN_UNLOCK(ch); 236e4e61333SAriff Abdullah if (err != 0) 237e4e61333SAriff Abdullah return (err); 238e4e61333SAriff Abdullah else 239bba4862cSAriff Abdullah pcm_clonereset(d); 240a1d444e1SAriff Abdullah } else if (newcnt < vcnt) { 241bba4862cSAriff Abdullah KASSERT(num == -1, 242bba4862cSAriff Abdullah ("bogus vchan_destroy() request num=%d", num)); 243bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 244a1d444e1SAriff Abdullah CHN_LOCK(c); 245bba4862cSAriff Abdullah if (c->direction != direction || 246bba4862cSAriff Abdullah CHN_EMPTY(c, children) || 247bba4862cSAriff Abdullah !(c->flags & CHN_F_HAS_VCHAN)) { 248a1d444e1SAriff Abdullah CHN_UNLOCK(c); 249bba4862cSAriff Abdullah continue; 250a1d444e1SAriff Abdullah } 251bba4862cSAriff Abdullah CHN_FOREACH_SAFE(ch, c, nch, children) { 252bba4862cSAriff Abdullah CHN_LOCK(ch); 25390da2b28SAriff Abdullah if (vcnt == 1 && c->refcount > 0) { 254bba4862cSAriff Abdullah CHN_UNLOCK(ch); 25590da2b28SAriff Abdullah break; 25690da2b28SAriff Abdullah } 25790da2b28SAriff Abdullah if (!(ch->flags & CHN_F_BUSY) && 25890da2b28SAriff Abdullah ch->refcount < 1) { 259bba4862cSAriff Abdullah err = vchan_destroy(ch); 260a1d444e1SAriff Abdullah if (err == 0) 261a1d444e1SAriff Abdullah vcnt--; 262bba4862cSAriff Abdullah } else 263bba4862cSAriff Abdullah CHN_UNLOCK(ch); 264e4e61333SAriff Abdullah if (vcnt == newcnt) 265bba4862cSAriff Abdullah break; 266a1d444e1SAriff Abdullah } 267bba4862cSAriff Abdullah CHN_UNLOCK(c); 268bba4862cSAriff Abdullah break; 269bba4862cSAriff Abdullah } 270bba4862cSAriff Abdullah pcm_clonereset(d); 271bba4862cSAriff Abdullah } 272a1d444e1SAriff Abdullah 273e4e61333SAriff Abdullah return (0); 274a1d444e1SAriff Abdullah } 275a1d444e1SAriff Abdullah 2763fdb3676SAriff Abdullah /* return error status and a locked channel */ 2773fdb3676SAriff Abdullah int 2783fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 27990da2b28SAriff Abdullah pid_t pid, char *comm, int devunit) 280285648f9SCameron Grant { 281285648f9SCameron Grant struct pcm_channel *c; 28290da2b28SAriff Abdullah int err, vchancount, vchan_num; 283bba4862cSAriff Abdullah 284bba4862cSAriff Abdullah KASSERT(d != NULL && ch != NULL && (devunit == -1 || 285bba4862cSAriff Abdullah !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && 286bba4862cSAriff Abdullah (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 287e4e61333SAriff Abdullah ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d", 288bba4862cSAriff Abdullah __func__, d, ch, direction, pid, devunit)); 289e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 290bba4862cSAriff Abdullah 291bba4862cSAriff Abdullah /* Double check again. */ 292bba4862cSAriff Abdullah if (devunit != -1) { 293bba4862cSAriff Abdullah switch (snd_unit2d(devunit)) { 294bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 295bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 296bba4862cSAriff Abdullah if (direction != PCMDIR_PLAY) 29790da2b28SAriff Abdullah return (ENOTSUP); 298bba4862cSAriff Abdullah break; 299bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 300bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 301bba4862cSAriff Abdullah if (direction != PCMDIR_REC) 30290da2b28SAriff Abdullah return (ENOTSUP); 303bba4862cSAriff Abdullah break; 304bba4862cSAriff Abdullah default: 305bba4862cSAriff Abdullah if (!(direction == PCMDIR_PLAY || 306bba4862cSAriff Abdullah direction == PCMDIR_REC)) 30790da2b28SAriff Abdullah return (ENOTSUP); 308bba4862cSAriff Abdullah break; 309bba4862cSAriff Abdullah } 310bba4862cSAriff Abdullah } 311285648f9SCameron Grant 31290da2b28SAriff Abdullah *ch = NULL; 31390da2b28SAriff Abdullah vchan_num = 0; 31490da2b28SAriff Abdullah vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : 31590da2b28SAriff Abdullah d->rvchancount; 31690da2b28SAriff Abdullah 3173fdb3676SAriff Abdullah retry_chnalloc: 31890da2b28SAriff Abdullah err = ENOTSUP; 319f637a36cSCameron Grant /* scan for a free channel */ 320bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 32149c5e6e2SCameron Grant CHN_LOCK(c); 32290da2b28SAriff Abdullah if (devunit == -1 && c->direction == direction && 32390da2b28SAriff Abdullah (c->flags & CHN_F_VIRTUAL)) { 32490da2b28SAriff Abdullah if (vchancount < snd_maxautovchans && 32590da2b28SAriff Abdullah vchan_num < CHN_CHAN(c)) { 32690da2b28SAriff Abdullah CHN_UNLOCK(c); 32790da2b28SAriff Abdullah goto vchan_alloc; 32890da2b28SAriff Abdullah } 32990da2b28SAriff Abdullah vchan_num++; 33090da2b28SAriff Abdullah } 331bba4862cSAriff Abdullah if (c->direction == direction && !(c->flags & CHN_F_BUSY) && 332bba4862cSAriff Abdullah (devunit == -1 || devunit == -2 || c->unit == devunit)) { 333285648f9SCameron Grant c->flags |= CHN_F_BUSY; 334b8f0d9e0SCameron Grant c->pid = pid; 33590da2b28SAriff Abdullah strlcpy(c->comm, (comm != NULL) ? comm : 33690da2b28SAriff Abdullah CHN_COMM_UNKNOWN, sizeof(c->comm)); 3373fdb3676SAriff Abdullah *ch = c; 338bba4862cSAriff Abdullah return (0); 339bba4862cSAriff Abdullah } else if (c->unit == devunit) { 3403fdb3676SAriff Abdullah if (c->direction != direction) 34190da2b28SAriff Abdullah err = ENOTSUP; 3423fdb3676SAriff Abdullah else if (c->flags & CHN_F_BUSY) 3433fdb3676SAriff Abdullah err = EBUSY; 3443fdb3676SAriff Abdullah else 3453fdb3676SAriff Abdullah err = EINVAL; 3463fdb3676SAriff Abdullah CHN_UNLOCK(c); 347bba4862cSAriff Abdullah return (err); 348bba4862cSAriff Abdullah } else if ((devunit == -1 || devunit == -2) && 349bba4862cSAriff Abdullah c->direction == direction && (c->flags & CHN_F_BUSY)) 350a1d444e1SAriff Abdullah err = EBUSY; 35149c5e6e2SCameron Grant CHN_UNLOCK(c); 352285648f9SCameron Grant } 353f637a36cSCameron Grant 354e4e61333SAriff Abdullah if (devunit == -2) 355e4e61333SAriff Abdullah return (err); 356e4e61333SAriff Abdullah 35790da2b28SAriff Abdullah vchan_alloc: 358f637a36cSCameron Grant /* no channel available */ 359e4e61333SAriff Abdullah if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || 360e4e61333SAriff Abdullah snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { 361bba4862cSAriff Abdullah if (!(vchancount > 0 && vchancount < snd_maxautovchans) && 362bba4862cSAriff Abdullah (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) 363bba4862cSAriff Abdullah return (err); 364bba4862cSAriff Abdullah err = pcm_setvchans(d, direction, vchancount + 1, 365bba4862cSAriff Abdullah (devunit == -1) ? -1 : snd_unit2c(devunit)); 366a1d444e1SAriff Abdullah if (err == 0) { 367bba4862cSAriff Abdullah if (devunit == -1) 368bba4862cSAriff Abdullah devunit = -2; 3693fdb3676SAriff Abdullah goto retry_chnalloc; 370f637a36cSCameron Grant } 371f637a36cSCameron Grant } 372f637a36cSCameron Grant 373bba4862cSAriff Abdullah return (err); 374285648f9SCameron Grant } 375285648f9SCameron Grant 376b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 377285648f9SCameron Grant int 378b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 379285648f9SCameron Grant { 380e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 381b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 382e4e61333SAriff Abdullah 383285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 384b8f0d9e0SCameron Grant c->pid = -1; 38590da2b28SAriff Abdullah strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm)); 38649c5e6e2SCameron Grant CHN_UNLOCK(c); 387e4e61333SAriff Abdullah 388e4e61333SAriff Abdullah return (0); 389285648f9SCameron Grant } 390285648f9SCameron Grant 391285648f9SCameron Grant int 392285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 393285648f9SCameron Grant { 394e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 395b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 396e4e61333SAriff Abdullah 397285648f9SCameron Grant c->refcount += ref; 398e4e61333SAriff Abdullah 399e4e61333SAriff Abdullah return (c->refcount); 400285648f9SCameron Grant } 401285648f9SCameron Grant 40267b1dce3SCameron Grant int 40367b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta) 40467b1dce3SCameron Grant { 40590da2b28SAriff Abdullah PCM_LOCKASSERT(d); 406a527dbc7SCameron Grant 407a527dbc7SCameron Grant d->inprog += delta; 408e4e61333SAriff Abdullah 409e4e61333SAriff Abdullah return (d->inprog); 41067b1dce3SCameron Grant } 41167b1dce3SCameron Grant 41267b1dce3SCameron Grant static void 41367b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 41467b1dce3SCameron Grant { 415e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 416e4e61333SAriff Abdullah 417bba4862cSAriff Abdullah if (num < 0) 418bba4862cSAriff Abdullah return; 419bba4862cSAriff Abdullah 420bba4862cSAriff Abdullah if (num >= 0 && d->pvchancount > num) 421bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1); 422bba4862cSAriff Abdullah else if (num > 0 && d->pvchancount == 0) 423bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1); 424bba4862cSAriff Abdullah 425bba4862cSAriff Abdullah if (num >= 0 && d->rvchancount > num) 426bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, num, -1); 427bba4862cSAriff Abdullah else if (num > 0 && d->rvchancount == 0) 428bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, 1, -1); 429bba4862cSAriff Abdullah 430bba4862cSAriff Abdullah pcm_clonereset(d); 43167b1dce3SCameron Grant } 43267b1dce3SCameron Grant 43333dbf14aSCameron Grant static int 434851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 43533dbf14aSCameron Grant { 436b8f0d9e0SCameron Grant struct snddev_info *d; 43733dbf14aSCameron Grant int error, unit; 43833dbf14aSCameron Grant 43933dbf14aSCameron Grant unit = snd_unit; 440041b706bSDavid Malone error = sysctl_handle_int(oidp, &unit, 0, req); 44133dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 442b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 443e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 444b8f0d9e0SCameron Grant return EINVAL; 44533dbf14aSCameron Grant snd_unit = unit; 44633dbf14aSCameron Grant } 44733dbf14aSCameron Grant return (error); 44833dbf14aSCameron Grant } 449851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */ 450851a904aSAlexander Leidinger SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW, 451a580b31aSAriff Abdullah 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device"); 452987e5972SCameron Grant 453cd9766c5SCameron Grant static int 45467b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 455cd9766c5SCameron Grant { 45667b1dce3SCameron Grant struct snddev_info *d; 45767b1dce3SCameron Grant int i, v, error; 458cd9766c5SCameron Grant 45967b1dce3SCameron Grant v = snd_maxautovchans; 460041b706bSDavid Malone error = sysctl_handle_int(oidp, &v, 0, req); 461cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 462bba4862cSAriff Abdullah if (v < 0) 463bba4862cSAriff Abdullah v = 0; 464bba4862cSAriff Abdullah if (v > SND_MAXVCHANS) 465bba4862cSAriff Abdullah v = SND_MAXVCHANS; 466bba4862cSAriff Abdullah snd_maxautovchans = v; 4679c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 4689c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 46967b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 470e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 47167b1dce3SCameron Grant continue; 472e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 47367b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 474e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 47567b1dce3SCameron Grant } 47667b1dce3SCameron Grant } 477cd9766c5SCameron Grant return (error); 478cd9766c5SCameron Grant } 47967b1dce3SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 480a580b31aSAriff Abdullah 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 481f637a36cSCameron Grant 482285648f9SCameron Grant struct pcm_channel * 483bba4862cSAriff Abdullah pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo) 484987e5972SCameron Grant { 485bba4862cSAriff Abdullah struct pcm_channel *ch; 486bba4862cSAriff Abdullah int direction, err, rpnum, *pnum, max; 487bba4862cSAriff Abdullah int udc, device, chan; 488bba4862cSAriff Abdullah char *dirs, *devname, buf[CHN_NAMELEN]; 489bba4862cSAriff Abdullah 490e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 49190da2b28SAriff Abdullah PCM_LOCKASSERT(d); 492bba4862cSAriff Abdullah KASSERT(num >= -1, ("invalid num=%d", num)); 493bba4862cSAriff Abdullah 494987e5972SCameron Grant 495285648f9SCameron Grant switch (dir) { 496285648f9SCameron Grant case PCMDIR_PLAY: 497285648f9SCameron Grant dirs = "play"; 498a3193a9cSDon Lewis direction = PCMDIR_PLAY; 499a67fe5c1SCameron Grant pnum = &d->playcount; 500bba4862cSAriff Abdullah device = SND_DEV_DSPHW_PLAY; 501bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 502285648f9SCameron Grant break; 503bba4862cSAriff Abdullah case PCMDIR_PLAY_VIRTUAL: 504bba4862cSAriff Abdullah dirs = "virtual"; 505bba4862cSAriff Abdullah direction = PCMDIR_PLAY; 506bba4862cSAriff Abdullah pnum = &d->pvchancount; 507bba4862cSAriff Abdullah device = SND_DEV_DSPHW_VPLAY; 508bba4862cSAriff Abdullah max = SND_MAXVCHANS; 509bba4862cSAriff Abdullah break; 510285648f9SCameron Grant case PCMDIR_REC: 511285648f9SCameron Grant dirs = "record"; 512a3193a9cSDon Lewis direction = PCMDIR_REC; 513a67fe5c1SCameron Grant pnum = &d->reccount; 514bba4862cSAriff Abdullah device = SND_DEV_DSPHW_REC; 515bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 516285648f9SCameron Grant break; 517bba4862cSAriff Abdullah case PCMDIR_REC_VIRTUAL: 518285648f9SCameron Grant dirs = "virtual"; 519bba4862cSAriff Abdullah direction = PCMDIR_REC; 520bba4862cSAriff Abdullah pnum = &d->rvchancount; 521bba4862cSAriff Abdullah device = SND_DEV_DSPHW_VREC; 522bba4862cSAriff Abdullah max = SND_MAXVCHANS; 523285648f9SCameron Grant break; 524285648f9SCameron Grant default: 525e4e61333SAriff Abdullah return (NULL); 5269c326820SCameron Grant } 527285648f9SCameron Grant 528bba4862cSAriff Abdullah chan = (num == -1) ? 0 : num; 529285648f9SCameron Grant 530e4e61333SAriff Abdullah if (*pnum >= max || chan >= max) 531e4e61333SAriff Abdullah return (NULL); 532bba4862cSAriff Abdullah 5333fdb3676SAriff Abdullah rpnum = 0; 534bba4862cSAriff Abdullah 535bba4862cSAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 536bba4862cSAriff Abdullah if (CHN_DEV(ch) != device) 5373fdb3676SAriff Abdullah continue; 538bba4862cSAriff Abdullah if (chan == CHN_CHAN(ch)) { 539bba4862cSAriff Abdullah if (num != -1) { 5403fdb3676SAriff Abdullah device_printf(d->dev, 541bba4862cSAriff Abdullah "channel num=%d allocated!\n", chan); 542e4e61333SAriff Abdullah return (NULL); 543bba4862cSAriff Abdullah } 544bba4862cSAriff Abdullah chan++; 545bba4862cSAriff Abdullah if (chan >= max) { 546bba4862cSAriff Abdullah device_printf(d->dev, 547bba4862cSAriff Abdullah "chan=%d > %d\n", chan, max); 548e4e61333SAriff Abdullah return (NULL); 549bba4862cSAriff Abdullah } 5503fdb3676SAriff Abdullah } 5513fdb3676SAriff Abdullah rpnum++; 5523fdb3676SAriff Abdullah } 553bba4862cSAriff Abdullah 5543fdb3676SAriff Abdullah if (*pnum != rpnum) { 5553fdb3676SAriff Abdullah device_printf(d->dev, 556bba4862cSAriff Abdullah "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", 5573fdb3676SAriff Abdullah __func__, dirs, *pnum, rpnum); 558e4e61333SAriff Abdullah return (NULL); 5593fdb3676SAriff Abdullah } 560a67fe5c1SCameron Grant 561bba4862cSAriff Abdullah udc = snd_mkunit(device_get_unit(d->dev), device, chan); 562bba4862cSAriff Abdullah devname = dsp_unit2name(buf, sizeof(buf), udc); 563bba4862cSAriff Abdullah 564bba4862cSAriff Abdullah if (devname == NULL) { 565bba4862cSAriff Abdullah device_printf(d->dev, 566bba4862cSAriff Abdullah "Failed to query device name udc=0x%08x\n", udc); 567e4e61333SAriff Abdullah return (NULL); 568bba4862cSAriff Abdullah } 569bba4862cSAriff Abdullah 57090da2b28SAriff Abdullah PCM_UNLOCK(d); 571bba4862cSAriff Abdullah ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 572bba4862cSAriff Abdullah ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); 573bba4862cSAriff Abdullah ch->unit = udc; 574285648f9SCameron Grant ch->pid = -1; 57590da2b28SAriff Abdullah strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm)); 576285648f9SCameron Grant ch->parentsnddev = d; 577285648f9SCameron Grant ch->parentchannel = parent; 578436c9b65SScott Long ch->dev = d->dev; 579bba4862cSAriff Abdullah ch->trigger = PCMTRIG_STOP; 580bba4862cSAriff Abdullah snprintf(ch->name, sizeof(ch->name), "%s:%s:%s", 581bba4862cSAriff Abdullah device_get_nameunit(ch->dev), dirs, devname); 582285648f9SCameron Grant 583a3193a9cSDon Lewis err = chn_init(ch, devinfo, dir, direction); 58490da2b28SAriff Abdullah PCM_LOCK(d); 5850f55ac6cSCameron Grant if (err) { 586bba4862cSAriff Abdullah device_printf(d->dev, "chn_init(%s) failed: err = %d\n", 587bba4862cSAriff Abdullah ch->name, err); 588285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 589285648f9SCameron Grant free(ch, M_DEVBUF); 590e4e61333SAriff Abdullah return (NULL); 591bbb5bf3dSCameron Grant } 592285648f9SCameron Grant 593e4e61333SAriff Abdullah return (ch); 594285648f9SCameron Grant } 595285648f9SCameron Grant 596285648f9SCameron Grant int 597285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 598285648f9SCameron Grant { 599a67fe5c1SCameron Grant struct snddev_info *d; 600285648f9SCameron Grant int err; 601285648f9SCameron Grant 602a67fe5c1SCameron Grant d = ch->parentsnddev; 603e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 604e4e61333SAriff Abdullah 605285648f9SCameron Grant err = chn_kill(ch); 606285648f9SCameron Grant if (err) { 607e4e61333SAriff Abdullah device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n", 608e4e61333SAriff Abdullah ch->name, err); 609e4e61333SAriff Abdullah return (err); 610285648f9SCameron Grant } 611285648f9SCameron Grant 612285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 613285648f9SCameron Grant free(ch, M_DEVBUF); 614285648f9SCameron Grant 615e4e61333SAriff Abdullah return (0); 616285648f9SCameron Grant } 617285648f9SCameron Grant 618285648f9SCameron Grant int 6195ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 620285648f9SCameron Grant { 621e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 62290da2b28SAriff Abdullah PCM_LOCKASSERT(d); 623bba4862cSAriff Abdullah KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 624bba4862cSAriff Abdullah ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 625285648f9SCameron Grant 62690da2b28SAriff Abdullah CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm); 6273fdb3676SAriff Abdullah 628e4e61333SAriff Abdullah switch (CHN_DEV(ch)) { 629e4e61333SAriff Abdullah case SND_DEV_DSPHW_PLAY: 630e4e61333SAriff Abdullah d->playcount++; 631e4e61333SAriff Abdullah break; 632e4e61333SAriff Abdullah case SND_DEV_DSPHW_VPLAY: 633e4e61333SAriff Abdullah d->pvchancount++; 634e4e61333SAriff Abdullah break; 635e4e61333SAriff Abdullah case SND_DEV_DSPHW_REC: 636e4e61333SAriff Abdullah d->reccount++; 637e4e61333SAriff Abdullah break; 638e4e61333SAriff Abdullah case SND_DEV_DSPHW_VREC: 639e4e61333SAriff Abdullah d->rvchancount++; 640e4e61333SAriff Abdullah break; 641e4e61333SAriff Abdullah default: 642e4e61333SAriff Abdullah break; 643e4e61333SAriff Abdullah } 644b8f0d9e0SCameron Grant 645e4e61333SAriff Abdullah d->devcount++; 646e4e61333SAriff Abdullah 647e4e61333SAriff Abdullah return (0); 64833dbf14aSCameron Grant } 64933dbf14aSCameron Grant 650285648f9SCameron Grant int 6515ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 65233dbf14aSCameron Grant { 653bba4862cSAriff Abdullah struct pcm_channel *tmp; 65433dbf14aSCameron Grant 655e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 65690da2b28SAriff Abdullah PCM_LOCKASSERT(d); 657e4e61333SAriff Abdullah 658bba4862cSAriff Abdullah tmp = NULL; 659a527dbc7SCameron Grant 660bba4862cSAriff Abdullah CHN_FOREACH(tmp, d, channels.pcm) { 661bba4862cSAriff Abdullah if (tmp == ch) 662bba4862cSAriff Abdullah break; 66333dbf14aSCameron Grant } 664bba4862cSAriff Abdullah 665bba4862cSAriff Abdullah if (tmp != ch) 666e4e61333SAriff Abdullah return (EINVAL); 66767beb5a5SCameron Grant 668bba4862cSAriff Abdullah CHN_REMOVE(d, ch, channels.pcm); 669e4e61333SAriff Abdullah 670bba4862cSAriff Abdullah switch (CHN_DEV(ch)) { 671bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 67267beb5a5SCameron Grant d->playcount--; 673bba4862cSAriff Abdullah break; 674bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 675bba4862cSAriff Abdullah d->pvchancount--; 676bba4862cSAriff Abdullah break; 677bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 678bba4862cSAriff Abdullah d->reccount--; 679bba4862cSAriff Abdullah break; 680bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 681bba4862cSAriff Abdullah d->rvchancount--; 682bba4862cSAriff Abdullah break; 683bba4862cSAriff Abdullah default: 684bba4862cSAriff Abdullah break; 685bba4862cSAriff Abdullah } 686285648f9SCameron Grant 687e4e61333SAriff Abdullah d->devcount--; 688e4e61333SAriff Abdullah 689e4e61333SAriff Abdullah return (0); 690987e5972SCameron Grant } 691987e5972SCameron Grant 692987e5972SCameron Grant int 693285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 694285648f9SCameron Grant { 695285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 69667b1dce3SCameron Grant struct pcm_channel *ch; 69767b1dce3SCameron Grant int err; 698285648f9SCameron Grant 699e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 700e4e61333SAriff Abdullah 70190da2b28SAriff Abdullah PCM_LOCK(d); 702bba4862cSAriff Abdullah ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); 703285648f9SCameron Grant if (!ch) { 704e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", 705e4e61333SAriff Abdullah cls->name, dir, devinfo); 70690da2b28SAriff Abdullah PCM_UNLOCK(d); 707e4e61333SAriff Abdullah return (ENODEV); 708285648f9SCameron Grant } 709cd9766c5SCameron Grant 7105ee30e27SMathew Kanner err = pcm_chn_add(d, ch); 71190da2b28SAriff Abdullah PCM_UNLOCK(d); 712285648f9SCameron Grant if (err) { 713e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", 714e4e61333SAriff Abdullah ch->name, err); 715285648f9SCameron Grant pcm_chn_destroy(ch); 716cd9766c5SCameron Grant } 717cd9766c5SCameron Grant 718e4e61333SAriff Abdullah return (err); 719285648f9SCameron Grant } 720285648f9SCameron Grant 721285648f9SCameron Grant static int 722285648f9SCameron Grant pcm_killchan(device_t dev) 723285648f9SCameron Grant { 724285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 725e33bee07SOlivier Houchard struct pcm_channel *ch; 726e4e61333SAriff Abdullah int error; 727e4e61333SAriff Abdullah 728e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 729285648f9SCameron Grant 730bba4862cSAriff Abdullah ch = CHN_FIRST(d, channels.pcm); 731285648f9SCameron Grant 73290da2b28SAriff Abdullah PCM_LOCK(d); 733bba4862cSAriff Abdullah error = pcm_chn_remove(d, ch); 73490da2b28SAriff Abdullah PCM_UNLOCK(d); 735e33bee07SOlivier Houchard if (error) 736e33bee07SOlivier Houchard return (error); 737e33bee07SOlivier Houchard return (pcm_chn_destroy(ch)); 738285648f9SCameron Grant } 739285648f9SCameron Grant 740285648f9SCameron Grant int 741987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 742987e5972SCameron Grant { 74366ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 74449c5e6e2SCameron Grant 745e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 746bba4862cSAriff Abdullah 747e4e61333SAriff Abdullah if (d->playcount == 0 || d->reccount == 0) 748e4e61333SAriff Abdullah d->flags |= SD_F_SIMPLEX; 749e4e61333SAriff Abdullah 750e4e61333SAriff Abdullah if ((d->playcount > 0 || d->reccount > 0) && 751e4e61333SAriff Abdullah !(d->flags & SD_F_AUTOVCHAN)) { 752e4e61333SAriff Abdullah d->flags |= SD_F_AUTOVCHAN; 753e4e61333SAriff Abdullah vchan_initsys(dev); 754e4e61333SAriff Abdullah } 755e4e61333SAriff Abdullah 756e4e61333SAriff Abdullah pcm_setmaxautovchans(d, snd_maxautovchans); 757bba4862cSAriff Abdullah 758a580b31aSAriff Abdullah strlcpy(d->status, str, SND_STATUSLEN); 759bba4862cSAriff Abdullah 76090da2b28SAriff Abdullah PCM_LOCK(d); 761e4e61333SAriff Abdullah 762bba4862cSAriff Abdullah /* Last stage, enable cloning. */ 763e4e61333SAriff Abdullah if (d->clones != NULL) 764bba4862cSAriff Abdullah (void)snd_clone_enable(d->clones); 765e4e61333SAriff Abdullah 766e4e61333SAriff Abdullah /* Done, we're ready.. */ 767e4e61333SAriff Abdullah d->flags |= SD_F_REGISTERED; 768e4e61333SAriff Abdullah 769e4e61333SAriff Abdullah PCM_RELEASE(d); 770bba4862cSAriff Abdullah 77190da2b28SAriff Abdullah PCM_UNLOCK(d); 772bba4862cSAriff Abdullah 773ad8612b9SAriff Abdullah if (snd_unit < 0 || snd_unit_auto != 0) 774f3685841SAriff Abdullah snd_unit = device_get_unit(dev); 775f3685841SAriff Abdullah 776e4e61333SAriff Abdullah return (0); 777987e5972SCameron Grant } 778987e5972SCameron Grant 779a1d444e1SAriff Abdullah uint32_t 780987e5972SCameron Grant pcm_getflags(device_t dev) 781987e5972SCameron Grant { 78266ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 78349c5e6e2SCameron Grant 784987e5972SCameron Grant return d->flags; 785987e5972SCameron Grant } 786987e5972SCameron Grant 787987e5972SCameron Grant void 788a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val) 789987e5972SCameron Grant { 79066ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 791d95502a8SCameron Grant 792987e5972SCameron Grant d->flags = val; 793987e5972SCameron Grant } 794987e5972SCameron Grant 79539004e69SCameron Grant void * 79639004e69SCameron Grant pcm_getdevinfo(device_t dev) 79739004e69SCameron Grant { 79866ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 79949c5e6e2SCameron Grant 80039004e69SCameron Grant return d->devinfo; 80139004e69SCameron Grant } 80239004e69SCameron Grant 803a67fe5c1SCameron Grant unsigned int 804d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 805a67fe5c1SCameron Grant { 806a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 8074e60be34SCameron Grant int sz, x; 808a67fe5c1SCameron Grant 809a67fe5c1SCameron Grant sz = 0; 8104e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 8114e60be34SCameron Grant x = sz; 812d55d96f6SAlexander Leidinger RANGE(sz, minbufsz, maxbufsz); 8134e60be34SCameron Grant if (x != sz) 814d55d96f6SAlexander Leidinger device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 815d55d96f6SAlexander Leidinger x = minbufsz; 8164e60be34SCameron Grant while (x < sz) 8174e60be34SCameron Grant x <<= 1; 8184e60be34SCameron Grant if (x > sz) 8194e60be34SCameron Grant x >>= 1; 8204e60be34SCameron Grant if (x != sz) { 8214e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 8224e60be34SCameron Grant sz = x; 8234e60be34SCameron Grant } 8244e60be34SCameron Grant } else { 825a67fe5c1SCameron Grant sz = deflt; 8264e60be34SCameron Grant } 8274e60be34SCameron Grant 828a67fe5c1SCameron Grant d->bufsz = sz; 829a67fe5c1SCameron Grant 830a67fe5c1SCameron Grant return sz; 831a67fe5c1SCameron Grant } 832a67fe5c1SCameron Grant 83390da2b28SAriff Abdullah static int 83490da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) 83590da2b28SAriff Abdullah { 83690da2b28SAriff Abdullah struct snddev_info *d; 83790da2b28SAriff Abdullah int err, val; 83890da2b28SAriff Abdullah 83990da2b28SAriff Abdullah d = oidp->oid_arg1; 84090da2b28SAriff Abdullah if (!PCM_REGISTERED(d)) 84190da2b28SAriff Abdullah return (ENODEV); 84290da2b28SAriff Abdullah 84390da2b28SAriff Abdullah PCM_LOCK(d); 84490da2b28SAriff Abdullah PCM_WAIT(d); 84590da2b28SAriff Abdullah val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; 84690da2b28SAriff Abdullah PCM_ACQUIRE(d); 84790da2b28SAriff Abdullah PCM_UNLOCK(d); 84890da2b28SAriff Abdullah 84990da2b28SAriff Abdullah err = sysctl_handle_int(oidp, &val, 0, req); 85090da2b28SAriff Abdullah 85190da2b28SAriff Abdullah if (err == 0 && req->newptr != NULL) { 85290da2b28SAriff Abdullah if (!(val == 0 || val == 1)) { 85390da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 85490da2b28SAriff Abdullah return (EINVAL); 85590da2b28SAriff Abdullah } 85690da2b28SAriff Abdullah 85790da2b28SAriff Abdullah PCM_LOCK(d); 85890da2b28SAriff Abdullah 85990da2b28SAriff Abdullah d->flags &= ~SD_F_BITPERFECT; 86090da2b28SAriff Abdullah d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; 86190da2b28SAriff Abdullah 86290da2b28SAriff Abdullah PCM_RELEASE(d); 86390da2b28SAriff Abdullah PCM_UNLOCK(d); 86490da2b28SAriff Abdullah } else 86590da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 86690da2b28SAriff Abdullah 86790da2b28SAriff Abdullah return (err); 86890da2b28SAriff Abdullah } 86990da2b28SAriff Abdullah 87090da2b28SAriff Abdullah #ifdef SND_DEBUG 871bba4862cSAriff Abdullah static int 872bba4862cSAriff Abdullah sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS) 873bba4862cSAriff Abdullah { 874bba4862cSAriff Abdullah struct snddev_info *d; 875bba4862cSAriff Abdullah uint32_t flags; 876bba4862cSAriff Abdullah int err; 877bba4862cSAriff Abdullah 878bba4862cSAriff Abdullah d = oidp->oid_arg1; 879e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 880bba4862cSAriff Abdullah return (ENODEV); 881bba4862cSAriff Abdullah 882e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 883e4e61333SAriff Abdullah 884bba4862cSAriff Abdullah flags = snd_clone_getflags(d->clones); 885041b706bSDavid Malone err = sysctl_handle_int(oidp, &flags, 0, req); 886bba4862cSAriff Abdullah 887bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL) { 888e4e61333SAriff Abdullah if (flags & ~SND_CLONE_MASK) 889bba4862cSAriff Abdullah err = EINVAL; 890e4e61333SAriff Abdullah else 891bba4862cSAriff Abdullah (void)snd_clone_setflags(d->clones, flags); 892bba4862cSAriff Abdullah } 893e4e61333SAriff Abdullah 894e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 895bba4862cSAriff Abdullah 896bba4862cSAriff Abdullah return (err); 897bba4862cSAriff Abdullah } 898bba4862cSAriff Abdullah 899bba4862cSAriff Abdullah static int 900bba4862cSAriff Abdullah sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS) 901bba4862cSAriff Abdullah { 902bba4862cSAriff Abdullah struct snddev_info *d; 903bba4862cSAriff Abdullah int err, deadline; 904bba4862cSAriff Abdullah 905bba4862cSAriff Abdullah d = oidp->oid_arg1; 906e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 907bba4862cSAriff Abdullah return (ENODEV); 908bba4862cSAriff Abdullah 909e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 910e4e61333SAriff Abdullah 911bba4862cSAriff Abdullah deadline = snd_clone_getdeadline(d->clones); 912041b706bSDavid Malone err = sysctl_handle_int(oidp, &deadline, 0, req); 913bba4862cSAriff Abdullah 914bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL) { 915bba4862cSAriff Abdullah if (deadline < 0) 916bba4862cSAriff Abdullah err = EINVAL; 917e4e61333SAriff Abdullah else 918bba4862cSAriff Abdullah (void)snd_clone_setdeadline(d->clones, deadline); 919bba4862cSAriff Abdullah } 920e4e61333SAriff Abdullah 921e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 922bba4862cSAriff Abdullah 923bba4862cSAriff Abdullah return (err); 924bba4862cSAriff Abdullah } 925bba4862cSAriff Abdullah 926bba4862cSAriff Abdullah static int 927bba4862cSAriff Abdullah sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS) 928bba4862cSAriff Abdullah { 929bba4862cSAriff Abdullah struct snddev_info *d; 930bba4862cSAriff Abdullah int err, val; 931bba4862cSAriff Abdullah 932bba4862cSAriff Abdullah d = oidp->oid_arg1; 933e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 934bba4862cSAriff Abdullah return (ENODEV); 935bba4862cSAriff Abdullah 936bba4862cSAriff Abdullah val = 0; 937041b706bSDavid Malone err = sysctl_handle_int(oidp, &val, 0, req); 938bba4862cSAriff Abdullah 939bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL && val != 0) { 940e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 941e4e61333SAriff Abdullah val = snd_clone_gc(d->clones); 942e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 943e4e61333SAriff Abdullah if (bootverbose != 0 || snd_verbose > 3) 944e4e61333SAriff Abdullah device_printf(d->dev, "clone gc: pruned=%d\n", val); 945bba4862cSAriff Abdullah } 946bba4862cSAriff Abdullah 947bba4862cSAriff Abdullah return (err); 948bba4862cSAriff Abdullah } 949bba4862cSAriff Abdullah 950bba4862cSAriff Abdullah static int 951bba4862cSAriff Abdullah sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS) 952bba4862cSAriff Abdullah { 953bba4862cSAriff Abdullah struct snddev_info *d; 954bba4862cSAriff Abdullah int i, err, val; 955bba4862cSAriff Abdullah 956bba4862cSAriff Abdullah val = 0; 957041b706bSDavid Malone err = sysctl_handle_int(oidp, &val, 0, req); 958bba4862cSAriff Abdullah 959bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL && val != 0) { 9609c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 9619c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 962bba4862cSAriff Abdullah d = devclass_get_softc(pcm_devclass, i); 963e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 964bba4862cSAriff Abdullah continue; 965e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 966e4e61333SAriff Abdullah val = snd_clone_gc(d->clones); 967e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 968e4e61333SAriff Abdullah if (bootverbose != 0 || snd_verbose > 3) 969e4e61333SAriff Abdullah device_printf(d->dev, "clone gc: pruned=%d\n", 970e4e61333SAriff Abdullah val); 971bba4862cSAriff Abdullah } 972bba4862cSAriff Abdullah } 973bba4862cSAriff Abdullah 974bba4862cSAriff Abdullah return (err); 975bba4862cSAriff Abdullah } 976bba4862cSAriff Abdullah SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW, 977bba4862cSAriff Abdullah 0, sizeof(int), sysctl_hw_snd_clone_gc, "I", 978bba4862cSAriff Abdullah "global clone garbage collector"); 979bba4862cSAriff Abdullah #endif 980bba4862cSAriff Abdullah 981987e5972SCameron Grant int 982987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 983987e5972SCameron Grant { 984bba4862cSAriff Abdullah struct snddev_info *d; 98590da2b28SAriff Abdullah int i; 986987e5972SCameron Grant 987b8a36395SCameron Grant if (pcm_veto_load) { 988b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 989b8a36395SCameron Grant 990b8a36395SCameron Grant return EINVAL; 991b8a36395SCameron Grant } 992b8a36395SCameron Grant 993bba4862cSAriff Abdullah if (device_get_unit(dev) > PCMMAXUNIT) { 994bba4862cSAriff Abdullah device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n", 995bba4862cSAriff Abdullah device_get_unit(dev), PCMMAXUNIT); 996bba4862cSAriff Abdullah device_printf(dev, 997bba4862cSAriff Abdullah "Use 'hw.snd.maxunit' tunable to raise the limit.\n"); 998bba4862cSAriff Abdullah return ENODEV; 999bba4862cSAriff Abdullah } 1000bba4862cSAriff Abdullah 1001bba4862cSAriff Abdullah d = device_get_softc(dev); 1002e4e61333SAriff Abdullah d->dev = dev; 10032c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 1004e4e61333SAriff Abdullah cv_init(&d->cv, device_get_nameunit(dev)); 1005e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 1006e4e61333SAriff Abdullah dsp_cdevinfo_init(d); 10077233ababSAlexander Leidinger #if 0 10087233ababSAlexander Leidinger /* 10097233ababSAlexander Leidinger * d->flags should be cleared by the allocator of the softc. 10107233ababSAlexander Leidinger * We cannot clear this field here because several devices set 10117233ababSAlexander Leidinger * this flag before calling pcm_register(). 10127233ababSAlexander Leidinger */ 1013cd9766c5SCameron Grant d->flags = 0; 10147233ababSAlexander Leidinger #endif 101590da2b28SAriff Abdullah i = 0; 101690da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 101790da2b28SAriff Abdullah "vpc", &i) != 0 || i != 0) 101890da2b28SAriff Abdullah d->flags |= SD_F_VPC; 101990da2b28SAriff Abdullah 102090da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 102190da2b28SAriff Abdullah "bitperfect", &i) == 0 && i != 0) 102290da2b28SAriff Abdullah d->flags |= SD_F_BITPERFECT; 102390da2b28SAriff Abdullah 1024987e5972SCameron Grant d->devinfo = devinfo; 1025f637a36cSCameron Grant d->devcount = 0; 1026506a5308SCameron Grant d->reccount = 0; 1027a67fe5c1SCameron Grant d->playcount = 0; 1028bba4862cSAriff Abdullah d->pvchancount = 0; 1029bba4862cSAriff Abdullah d->rvchancount = 0; 1030bba4862cSAriff Abdullah d->pvchanrate = 0; 1031bba4862cSAriff Abdullah d->pvchanformat = 0; 1032bba4862cSAriff Abdullah d->rvchanrate = 0; 1033bba4862cSAriff Abdullah d->rvchanformat = 0; 1034d95502a8SCameron Grant d->inprog = 0; 1035833f7023SCameron Grant 1036bba4862cSAriff Abdullah /* 1037bba4862cSAriff Abdullah * Create clone manager, disabled by default. Cloning will be 103890da2b28SAriff Abdullah * enabled during final stage of driver initialization through 1039bba4862cSAriff Abdullah * pcm_setstatus(). 1040bba4862cSAriff Abdullah */ 1041e4e61333SAriff Abdullah d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE, 1042e4e61333SAriff Abdullah SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK | 1043bba4862cSAriff Abdullah SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF | 1044bba4862cSAriff Abdullah SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED); 1045bba4862cSAriff Abdullah 1046bba4862cSAriff Abdullah if (bootverbose != 0 || snd_verbose > 3) { 1047bba4862cSAriff Abdullah device_printf(dev, 1048bba4862cSAriff Abdullah "clone manager: deadline=%dms flags=0x%08x\n", 1049bba4862cSAriff Abdullah snd_clone_getdeadline(d->clones), 1050bba4862cSAriff Abdullah snd_clone_getflags(d->clones)); 1051bba4862cSAriff Abdullah } 1052bba4862cSAriff Abdullah 1053bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm); 1054bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm.busy); 105590da2b28SAriff Abdullah CHN_INIT(d, channels.pcm.opened); 105645550658SPoul-Henning Kamp 1057e4e61333SAriff Abdullah /* XXX This is incorrect, but lets play along for now. */ 1058a1d444e1SAriff Abdullah if ((numplay == 0 || numrec == 0) && numplay != numrec) 1059285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 1060d95502a8SCameron Grant 1061bba4862cSAriff Abdullah sysctl_ctx_init(&d->play_sysctl_ctx); 1062bba4862cSAriff Abdullah d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 1063bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 1064bba4862cSAriff Abdullah CTLFLAG_RD, 0, "playback channels node"); 1065bba4862cSAriff Abdullah sysctl_ctx_init(&d->rec_sysctl_ctx); 1066bba4862cSAriff Abdullah d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 1067bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 1068bba4862cSAriff Abdullah CTLFLAG_RD, 0, "record channels node"); 1069851a904aSAlexander Leidinger /* XXX: an user should be able to set this with a control tool, the 1070851a904aSAlexander Leidinger sysadmin then needs min+max sysctls for this */ 1071a580b31aSAriff Abdullah SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 1072a580b31aSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 1073a580b31aSAriff Abdullah OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 107490da2b28SAriff Abdullah SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 107590da2b28SAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 107690da2b28SAriff Abdullah "bitperfect", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 107790da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect, "I", 107890da2b28SAriff Abdullah "bit-perfect playback/recording (0=disable, 1=enable)"); 1079bba4862cSAriff Abdullah #ifdef SND_DEBUG 1080bba4862cSAriff Abdullah SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1081bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1082bba4862cSAriff Abdullah "clone_flags", CTLTYPE_UINT | CTLFLAG_RW, d, sizeof(d), 1083bba4862cSAriff Abdullah sysctl_dev_pcm_clone_flags, "IU", 1084bba4862cSAriff Abdullah "clone flags"); 1085bba4862cSAriff Abdullah SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1086bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1087bba4862cSAriff Abdullah "clone_deadline", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 1088bba4862cSAriff Abdullah sysctl_dev_pcm_clone_deadline, "I", 1089bba4862cSAriff Abdullah "clone expiration deadline (ms)"); 1090bba4862cSAriff Abdullah SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1091bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1092bba4862cSAriff Abdullah "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 1093bba4862cSAriff Abdullah sysctl_dev_pcm_clone_gc, "I", 1094bba4862cSAriff Abdullah "clone garbage collector"); 109582db23e2SCameron Grant #endif 1096e4e61333SAriff Abdullah 1097bba4862cSAriff Abdullah if (numplay > 0 || numrec > 0) { 1098cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 10993fdb3676SAriff Abdullah vchan_initsys(dev); 110097d69a96SAlexander Leidinger } 1101cd9766c5SCameron Grant 110290da2b28SAriff Abdullah if (d->flags & SD_F_EQ) 110390da2b28SAriff Abdullah feeder_eq_initsys(dev); 110490da2b28SAriff Abdullah 110567b1dce3SCameron Grant sndstat_register(dev, d->status, sndstat_prepare_pcm); 1106e4e61333SAriff Abdullah 1107987e5972SCameron Grant return 0; 1108987e5972SCameron Grant } 1109987e5972SCameron Grant 111033dbf14aSCameron Grant int 111133dbf14aSCameron Grant pcm_unregister(device_t dev) 11127c438dbeSCameron Grant { 1113e4e61333SAriff Abdullah struct snddev_info *d; 1114a67fe5c1SCameron Grant struct pcm_channel *ch; 1115bba4862cSAriff Abdullah struct thread *td; 1116bba4862cSAriff Abdullah int i; 11177c438dbeSCameron Grant 1118bba4862cSAriff Abdullah td = curthread; 1119e4e61333SAriff Abdullah d = device_get_softc(dev); 1120e4e61333SAriff Abdullah 1121e4e61333SAriff Abdullah if (!PCM_ALIVE(d)) { 1122e4e61333SAriff Abdullah device_printf(dev, "unregister: device not configured\n"); 1123e4e61333SAriff Abdullah return (0); 1124e4e61333SAriff Abdullah } 1125bba4862cSAriff Abdullah 1126bba4862cSAriff Abdullah if (sndstat_acquire(td) != 0) { 112728ef3fb0SAlexander Leidinger device_printf(dev, "unregister: sndstat busy\n"); 1128e4e61333SAriff Abdullah return (EBUSY); 112928ef3fb0SAlexander Leidinger } 113028ef3fb0SAlexander Leidinger 113190da2b28SAriff Abdullah PCM_LOCK(d); 1132e4e61333SAriff Abdullah PCM_WAIT(d); 1133e4e61333SAriff Abdullah 1134e4e61333SAriff Abdullah if (d->inprog != 0) { 11355c25132aSGeorge C A Reid device_printf(dev, "unregister: operation in progress\n"); 113690da2b28SAriff Abdullah PCM_UNLOCK(d); 1137bba4862cSAriff Abdullah sndstat_release(td); 1138e4e61333SAriff Abdullah return (EBUSY); 11395c25132aSGeorge C A Reid } 11405ee30e27SMathew Kanner 1141e4e61333SAriff Abdullah PCM_ACQUIRE(d); 114290da2b28SAriff Abdullah PCM_UNLOCK(d); 1143e4e61333SAriff Abdullah 1144e4e61333SAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 1145e4e61333SAriff Abdullah CHN_LOCK(ch); 1146e4e61333SAriff Abdullah if (ch->refcount > 0) { 1147e4e61333SAriff Abdullah device_printf(dev, 1148e4e61333SAriff Abdullah "unregister: channel %s busy (pid %d)\n", 1149e4e61333SAriff Abdullah ch->name, ch->pid); 1150e4e61333SAriff Abdullah CHN_UNLOCK(ch); 1151e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 1152bba4862cSAriff Abdullah sndstat_release(td); 1153e4e61333SAriff Abdullah return (EBUSY); 1154285648f9SCameron Grant } 1155e4e61333SAriff Abdullah CHN_UNLOCK(ch); 1156c9b53085SCameron Grant } 11575ee30e27SMathew Kanner 1158bba4862cSAriff Abdullah if (d->clones != NULL) { 1159bba4862cSAriff Abdullah if (snd_clone_busy(d->clones) != 0) { 1160bba4862cSAriff Abdullah device_printf(dev, "unregister: clone busy\n"); 1161e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 1162bba4862cSAriff Abdullah sndstat_release(td); 1163e4e61333SAriff Abdullah return (EBUSY); 1164e4e61333SAriff Abdullah } else { 116590da2b28SAriff Abdullah PCM_LOCK(d); 1166bba4862cSAriff Abdullah (void)snd_clone_disable(d->clones); 116790da2b28SAriff Abdullah PCM_UNLOCK(d); 1168e4e61333SAriff Abdullah } 1169bba4862cSAriff Abdullah } 1170bba4862cSAriff Abdullah 1171b4221868SAriff Abdullah if (mixer_uninit(dev) == EBUSY) { 11727233ababSAlexander Leidinger device_printf(dev, "unregister: mixer busy\n"); 117390da2b28SAriff Abdullah PCM_LOCK(d); 1174bba4862cSAriff Abdullah if (d->clones != NULL) 1175bba4862cSAriff Abdullah (void)snd_clone_enable(d->clones); 1176e4e61333SAriff Abdullah PCM_RELEASE(d); 117790da2b28SAriff Abdullah PCM_UNLOCK(d); 1178bba4862cSAriff Abdullah sndstat_release(td); 1179e4e61333SAriff Abdullah return (EBUSY); 11807233ababSAlexander Leidinger } 11817233ababSAlexander Leidinger 118290da2b28SAriff Abdullah PCM_LOCK(d); 1183e4e61333SAriff Abdullah d->flags |= SD_F_DYING; 1184e4e61333SAriff Abdullah d->flags &= ~SD_F_REGISTERED; 118590da2b28SAriff Abdullah PCM_UNLOCK(d); 1186e4e61333SAriff Abdullah 1187e4e61333SAriff Abdullah /* 1188e4e61333SAriff Abdullah * No lock being held, so this thing can be flushed without 1189e4e61333SAriff Abdullah * stucking into devdrn oblivion. 1190e4e61333SAriff Abdullah */ 1191bba4862cSAriff Abdullah if (d->clones != NULL) { 1192bba4862cSAriff Abdullah snd_clone_destroy(d->clones); 1193bba4862cSAriff Abdullah d->clones = NULL; 11947982e7a4SAriff Abdullah } 1195bba4862cSAriff Abdullah 1196bba4862cSAriff Abdullah if (d->play_sysctl_tree != NULL) { 1197bba4862cSAriff Abdullah sysctl_ctx_free(&d->play_sysctl_ctx); 1198bba4862cSAriff Abdullah d->play_sysctl_tree = NULL; 1199bba4862cSAriff Abdullah } 1200bba4862cSAriff Abdullah if (d->rec_sysctl_tree != NULL) { 1201bba4862cSAriff Abdullah sysctl_ctx_free(&d->rec_sysctl_ctx); 1202bba4862cSAriff Abdullah d->rec_sysctl_tree = NULL; 1203a1d444e1SAriff Abdullah } 1204bba4862cSAriff Abdullah 1205bba4862cSAriff Abdullah while (!CHN_EMPTY(d, channels.pcm)) 1206285648f9SCameron Grant pcm_killchan(dev); 12077c438dbeSCameron Grant 1208e4e61333SAriff Abdullah dsp_cdevinfo_flush(d); 1209e4e61333SAriff Abdullah 121090da2b28SAriff Abdullah PCM_LOCK(d); 1211e4e61333SAriff Abdullah PCM_RELEASE(d); 1212e4e61333SAriff Abdullah cv_destroy(&d->cv); 121390da2b28SAriff Abdullah PCM_UNLOCK(d); 121449c5e6e2SCameron Grant snd_mtxfree(d->lock); 121528ef3fb0SAlexander Leidinger sndstat_unregister(dev); 1216bba4862cSAriff Abdullah sndstat_release(td); 1217bba4862cSAriff Abdullah 1218bba4862cSAriff Abdullah if (snd_unit == device_get_unit(dev)) { 1219bba4862cSAriff Abdullah /* 1220f3685841SAriff Abdullah * Reassign default unit to the next available dev, but 1221f3685841SAriff Abdullah * first, reset snd_unit to something ridiculous. 1222bba4862cSAriff Abdullah */ 1223f3685841SAriff Abdullah snd_unit = -1; 12249c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 12259c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 1226e4e61333SAriff Abdullah if (device_get_unit(dev) == i) 1227bba4862cSAriff Abdullah continue; 1228e4e61333SAriff Abdullah d = devclass_get_softc(pcm_devclass, i); 1229e4e61333SAriff Abdullah if (PCM_REGISTERED(d)) { 1230bba4862cSAriff Abdullah snd_unit = i; 1231bba4862cSAriff Abdullah break; 1232bba4862cSAriff Abdullah } 1233bba4862cSAriff Abdullah } 1234e4e61333SAriff Abdullah } 1235bba4862cSAriff Abdullah 1236e4e61333SAriff Abdullah return (0); 123733dbf14aSCameron Grant } 12387c438dbeSCameron Grant 123967b1dce3SCameron Grant /************************************************************************/ 124067b1dce3SCameron Grant 1241b611c801SAlexander Leidinger /** 1242b611c801SAlexander Leidinger * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 1243b611c801SAlexander Leidinger * 1244b611c801SAlexander Leidinger * @param si Pointer to oss_sysinfo struct where information about the 1245b611c801SAlexander Leidinger * sound subsystem will be written/copied. 1246b611c801SAlexander Leidinger * 1247b611c801SAlexander Leidinger * This routine returns information about the sound system, such as the 1248b611c801SAlexander Leidinger * current OSS version, number of audio, MIDI, and mixer drivers, etc. 1249b611c801SAlexander Leidinger * Also includes a bitmask showing which of the above types of devices 1250b611c801SAlexander Leidinger * are open (busy). 1251b611c801SAlexander Leidinger * 1252b611c801SAlexander Leidinger * @note 1253b611c801SAlexander Leidinger * Calling threads must not hold any snddev_info or pcm_channel locks. 1254b611c801SAlexander Leidinger * 1255b611c801SAlexander Leidinger * @author Ryan Beasley <ryanb@FreeBSD.org> 1256b611c801SAlexander Leidinger */ 1257b611c801SAlexander Leidinger void 1258b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si) 1259b611c801SAlexander Leidinger { 1260b611c801SAlexander Leidinger static char si_product[] = "FreeBSD native OSS ABI"; 1261b611c801SAlexander Leidinger static char si_version[] = __XSTRING(__FreeBSD_version); 1262c412d503SAlexander Motin static char si_license[] = "BSD"; 1263b611c801SAlexander Leidinger static int intnbits = sizeof(int) * 8; /* Better suited as macro? 1264b611c801SAlexander Leidinger Must pester a C guru. */ 1265b611c801SAlexander Leidinger 1266b611c801SAlexander Leidinger struct snddev_info *d; 1267b611c801SAlexander Leidinger struct pcm_channel *c; 1268b611c801SAlexander Leidinger int i, j, ncards; 1269b611c801SAlexander Leidinger 1270b611c801SAlexander Leidinger ncards = 0; 1271b611c801SAlexander Leidinger 1272b611c801SAlexander Leidinger strlcpy(si->product, si_product, sizeof(si->product)); 1273b611c801SAlexander Leidinger strlcpy(si->version, si_version, sizeof(si->version)); 1274b611c801SAlexander Leidinger si->versionnum = SOUND_VERSION; 1275c412d503SAlexander Motin strlcpy(si->license, si_license, sizeof(si->license)); 1276b611c801SAlexander Leidinger 1277b611c801SAlexander Leidinger /* 1278b611c801SAlexander Leidinger * Iterate over PCM devices and their channels, gathering up data 1279b611c801SAlexander Leidinger * for the numaudios, ncards, and openedaudio fields. 1280b611c801SAlexander Leidinger */ 1281b611c801SAlexander Leidinger si->numaudios = 0; 1282b611c801SAlexander Leidinger bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 1283b611c801SAlexander Leidinger 1284b611c801SAlexander Leidinger j = 0; 1285b611c801SAlexander Leidinger 12869c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 12879c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 1288b611c801SAlexander Leidinger d = devclass_get_softc(pcm_devclass, i); 1289e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 1290b611c801SAlexander Leidinger continue; 1291b611c801SAlexander Leidinger 1292e4e61333SAriff Abdullah /* XXX Need Giant magic entry ??? */ 1293e4e61333SAriff Abdullah 1294b611c801SAlexander Leidinger /* See note in function's docblock */ 129590da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 129690da2b28SAriff Abdullah PCM_LOCK(d); 1297b611c801SAlexander Leidinger 1298b611c801SAlexander Leidinger si->numaudios += d->devcount; 1299b611c801SAlexander Leidinger ++ncards; 1300b611c801SAlexander Leidinger 1301bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 130290da2b28SAriff Abdullah CHN_UNLOCKASSERT(c); 1303b611c801SAlexander Leidinger CHN_LOCK(c); 1304b611c801SAlexander Leidinger if (c->flags & CHN_F_BUSY) 1305b611c801SAlexander Leidinger si->openedaudio[j / intnbits] |= 1306b611c801SAlexander Leidinger (1 << (j % intnbits)); 1307b611c801SAlexander Leidinger CHN_UNLOCK(c); 1308b611c801SAlexander Leidinger j++; 1309b611c801SAlexander Leidinger } 1310b611c801SAlexander Leidinger 131190da2b28SAriff Abdullah PCM_UNLOCK(d); 1312b611c801SAlexander Leidinger } 1313c412d503SAlexander Motin si->numaudioengines = si->numaudios; 1314b611c801SAlexander Leidinger 1315b611c801SAlexander Leidinger si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 1316b611c801SAlexander Leidinger /** 1317b611c801SAlexander Leidinger * @todo Collect num{midis,timers}. 1318b611c801SAlexander Leidinger * 1319b611c801SAlexander Leidinger * Need access to sound/midi/midi.c::midistat_lock in order 1320b611c801SAlexander Leidinger * to safely touch midi_devices and get a head count of, well, 1321b611c801SAlexander Leidinger * MIDI devices. midistat_lock is a global static (i.e., local to 1322b611c801SAlexander Leidinger * midi.c), but midi_devices is a regular global; should the mutex 1323b611c801SAlexander Leidinger * be publicized, or is there another way to get this information? 1324b611c801SAlexander Leidinger * 1325b611c801SAlexander Leidinger * NB: MIDI/sequencer stuff is currently on hold. 1326b611c801SAlexander Leidinger */ 1327b611c801SAlexander Leidinger si->nummidis = 0; 1328b611c801SAlexander Leidinger si->numtimers = 0; 1329b611c801SAlexander Leidinger si->nummixers = mixer_count; 1330b611c801SAlexander Leidinger si->numcards = ncards; 1331b611c801SAlexander Leidinger /* OSSv4 docs: Intended only for test apps; API doesn't 1332b611c801SAlexander Leidinger really have much of a concept of cards. Shouldn't be 1333b611c801SAlexander Leidinger used by applications. */ 1334b611c801SAlexander Leidinger 1335b611c801SAlexander Leidinger /** 1336b611c801SAlexander Leidinger * @todo Fill in "busy devices" fields. 1337b611c801SAlexander Leidinger * 1338b611c801SAlexander Leidinger * si->openedmidi = " MIDI devices 1339b611c801SAlexander Leidinger */ 1340b611c801SAlexander Leidinger bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 1341b611c801SAlexander Leidinger 1342b611c801SAlexander Leidinger /* 1343b611c801SAlexander Leidinger * Si->filler is a reserved array, but according to docs each 1344b611c801SAlexander Leidinger * element should be set to -1. 1345b611c801SAlexander Leidinger */ 1346b611c801SAlexander Leidinger for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++) 1347b611c801SAlexander Leidinger si->filler[i] = -1; 1348b611c801SAlexander Leidinger } 1349b611c801SAlexander Leidinger 135052f6e09eSAlexander Motin int 135152f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si) 135252f6e09eSAlexander Motin { 135352f6e09eSAlexander Motin struct snddev_info *d; 135452f6e09eSAlexander Motin int i, ncards; 135552f6e09eSAlexander Motin 135652f6e09eSAlexander Motin ncards = 0; 135752f6e09eSAlexander Motin 135852f6e09eSAlexander Motin for (i = 0; pcm_devclass != NULL && 135952f6e09eSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 136052f6e09eSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 136152f6e09eSAlexander Motin if (!PCM_REGISTERED(d)) 136252f6e09eSAlexander Motin continue; 136352f6e09eSAlexander Motin 136452f6e09eSAlexander Motin if (ncards++ != si->card) 136552f6e09eSAlexander Motin continue; 136652f6e09eSAlexander Motin 136790da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 136890da2b28SAriff Abdullah PCM_LOCK(d); 136952f6e09eSAlexander Motin 137052f6e09eSAlexander Motin strlcpy(si->shortname, device_get_nameunit(d->dev), 137152f6e09eSAlexander Motin sizeof(si->shortname)); 137252f6e09eSAlexander Motin strlcpy(si->longname, device_get_desc(d->dev), 137352f6e09eSAlexander Motin sizeof(si->longname)); 137452f6e09eSAlexander Motin strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 137552f6e09eSAlexander Motin si->intr_count = si->ack_count = 0; 137690da2b28SAriff Abdullah 137790da2b28SAriff Abdullah PCM_UNLOCK(d); 137890da2b28SAriff Abdullah 137952f6e09eSAlexander Motin return (0); 138052f6e09eSAlexander Motin } 138152f6e09eSAlexander Motin return (ENXIO); 138252f6e09eSAlexander Motin } 138352f6e09eSAlexander Motin 1384b611c801SAlexander Leidinger /************************************************************************/ 1385b611c801SAlexander Leidinger 13860739ea1dSSeigo Tanimura static int 13870739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data) 13880739ea1dSSeigo Tanimura { 1389b611c801SAlexander Leidinger int ret; 13907233ababSAlexander Leidinger #if 0 13910739ea1dSSeigo Tanimura return (midi_modevent(mod, type, data)); 13927233ababSAlexander Leidinger #else 1393b611c801SAlexander Leidinger ret = 0; 1394b611c801SAlexander Leidinger 1395b611c801SAlexander Leidinger switch(type) { 1396b611c801SAlexander Leidinger case MOD_LOAD: 1397b611c801SAlexander Leidinger pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 1398b611c801SAlexander Leidinger break; 1399b611c801SAlexander Leidinger case MOD_UNLOAD: 1400b611c801SAlexander Leidinger case MOD_SHUTDOWN: 1401bba4862cSAriff Abdullah ret = sndstat_acquire(curthread); 1402bba4862cSAriff Abdullah if (ret != 0) 1403bba4862cSAriff Abdullah break; 1404b611c801SAlexander Leidinger if (pcmsg_unrhdr != NULL) { 1405b611c801SAlexander Leidinger delete_unrhdr(pcmsg_unrhdr); 1406b611c801SAlexander Leidinger pcmsg_unrhdr = NULL; 1407b611c801SAlexander Leidinger } 1408b611c801SAlexander Leidinger break; 1409b611c801SAlexander Leidinger default: 141090da2b28SAriff Abdullah ret = ENOTSUP; 1411b611c801SAlexander Leidinger } 1412b611c801SAlexander Leidinger 1413b611c801SAlexander Leidinger return ret; 14147233ababSAlexander Leidinger #endif 14150739ea1dSSeigo Tanimura } 14160739ea1dSSeigo Tanimura 14170739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL); 14180739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER); 1419