1098ca2bdSWarner Losh /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 490da2b28SAriff Abdullah * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 590da2b28SAriff Abdullah * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 690da2b28SAriff Abdullah * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> 740ac6e17SJoel Dahl * Copyright (c) 1997 Luigi Rizzo 8987e5972SCameron Grant * All rights reserved. 9987e5972SCameron Grant * 10987e5972SCameron Grant * Redistribution and use in source and binary forms, with or without 11987e5972SCameron Grant * modification, are permitted provided that the following conditions 12987e5972SCameron Grant * are met: 13987e5972SCameron Grant * 1. Redistributions of source code must retain the above copyright 14987e5972SCameron Grant * notice, this list of conditions and the following disclaimer. 15987e5972SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 16987e5972SCameron Grant * notice, this list of conditions and the following disclaimer in the 17987e5972SCameron Grant * documentation and/or other materials provided with the distribution. 18987e5972SCameron Grant * 19987e5972SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20987e5972SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21987e5972SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22987e5972SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23987e5972SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24987e5972SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25987e5972SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26987e5972SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27987e5972SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28987e5972SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29987e5972SCameron Grant * SUCH DAMAGE. 30987e5972SCameron Grant */ 31987e5972SCameron Grant 3290da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 3390da2b28SAriff Abdullah #include "opt_snd.h" 3490da2b28SAriff Abdullah #endif 3590da2b28SAriff Abdullah 36ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 37a580b31aSAriff Abdullah #include <dev/sound/pcm/ac97.h> 38285648f9SCameron Grant #include <dev/sound/pcm/vchan.h> 395ee30e27SMathew Kanner #include <dev/sound/pcm/dsp.h> 4090da2b28SAriff Abdullah #include <dev/sound/pcm/sndstat.h> 41bba4862cSAriff Abdullah #include <dev/sound/version.h> 42b611c801SAlexander Leidinger #include <sys/limits.h> 437c438dbeSCameron Grant #include <sys/sysctl.h> 44285648f9SCameron Grant 4567b1dce3SCameron Grant #include "feeder_if.h" 4667b1dce3SCameron Grant 4767b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 4867b1dce3SCameron Grant 49d95502a8SCameron Grant devclass_t pcm_devclass; 5082db23e2SCameron Grant 51b8a36395SCameron Grant int pcm_veto_load = 1; 52b8a36395SCameron Grant 53f3685841SAriff Abdullah int snd_unit = -1; 54cbe7d6a3SCameron Grant 55cbebc90dSAlexander Motin static int snd_unit_auto = -1; 56af3b2549SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN, 57838d3589SAriff Abdullah &snd_unit_auto, 0, "assign default unit to a newly attached device"); 58ad8612b9SAriff Abdullah 59bba4862cSAriff Abdullah int snd_maxautovchans = 16; 60987e5972SCameron Grant 617029da5cSPawel Biernacki SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 627029da5cSPawel Biernacki "Sound driver"); 6382db23e2SCameron Grant 6432a0e5d5SHans Petter Selasky static void pcm_sysinit(device_t); 6532a0e5d5SHans Petter Selasky 66bba4862cSAriff Abdullah /* 67bba4862cSAriff Abdullah * XXX I've had enough with people not telling proper version/arch 68bba4862cSAriff Abdullah * while reporting problems, not after 387397913213th questions/requests. 69bba4862cSAriff Abdullah */ 70a9bdb5d3SAndriy Gapon static char snd_driver_version[] = 71bba4862cSAriff Abdullah __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH; 72bba4862cSAriff Abdullah SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version, 7390da2b28SAriff Abdullah 0, "driver version/arch"); 74bba4862cSAriff Abdullah 75b611c801SAlexander Leidinger /** 76b611c801SAlexander Leidinger * @brief Unit number allocator for syncgroup IDs 77b611c801SAlexander Leidinger */ 78b611c801SAlexander Leidinger struct unrhdr *pcmsg_unrhdr = NULL; 79b611c801SAlexander Leidinger 8090da2b28SAriff Abdullah static int 8190da2b28SAriff Abdullah sndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS) 8290da2b28SAriff Abdullah { 8390da2b28SAriff Abdullah SNDSTAT_PREPARE_PCM_BEGIN(); 8490da2b28SAriff Abdullah SNDSTAT_PREPARE_PCM_END(); 8590da2b28SAriff Abdullah } 8667b1dce3SCameron Grant 8737209180SCameron Grant void * 882c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type) 8937209180SCameron Grant { 9037209180SCameron Grant struct mtx *m; 9137209180SCameron Grant 92a163d034SWarner Losh m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 9312e524a2SDon Lewis mtx_init(m, desc, type, MTX_DEF); 9412e524a2SDon Lewis return m; 9512e524a2SDon Lewis } 9612e524a2SDon Lewis 9737209180SCameron Grant void 9837209180SCameron Grant snd_mtxfree(void *m) 9937209180SCameron Grant { 10037209180SCameron Grant struct mtx *mtx = m; 10137209180SCameron Grant 10237209180SCameron Grant mtx_destroy(mtx); 10337209180SCameron Grant free(mtx, M_DEVBUF); 10437209180SCameron Grant } 10537209180SCameron Grant 10637209180SCameron Grant void 10737209180SCameron Grant snd_mtxassert(void *m) 10837209180SCameron Grant { 109f00f162aSCameron Grant #ifdef INVARIANTS 11037209180SCameron Grant struct mtx *mtx = m; 11137209180SCameron Grant 11237209180SCameron Grant mtx_assert(mtx, MA_OWNED); 11337209180SCameron Grant #endif 11437209180SCameron Grant } 11537209180SCameron Grant 11637209180SCameron Grant int 11737209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 11837209180SCameron Grant { 119e4e61333SAriff Abdullah struct snddev_info *d; 12090da2b28SAriff Abdullah 12137209180SCameron Grant flags &= INTR_MPSAFE; 12246700f12SPeter Wemm flags |= INTR_TYPE_AV; 123e4e61333SAriff Abdullah d = device_get_softc(dev); 124e4e61333SAriff Abdullah if (d != NULL && (flags & INTR_MPSAFE)) 125e4e61333SAriff Abdullah d->flags |= SD_F_MPSAFE; 126e4e61333SAriff Abdullah 127775fcb6eSBaptiste Daroussin return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep); 12837209180SCameron Grant } 12937209180SCameron Grant 130bba4862cSAriff Abdullah static void 131bba4862cSAriff Abdullah pcm_clonereset(struct snddev_info *d) 132a1d444e1SAriff Abdullah { 133bba4862cSAriff Abdullah int cmax; 134bba4862cSAriff Abdullah 135e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 136bba4862cSAriff Abdullah 137bba4862cSAriff Abdullah cmax = d->playcount + d->reccount - 1; 138bba4862cSAriff Abdullah if (d->pvchancount > 0) 13990da2b28SAriff Abdullah cmax += max(d->pvchancount, snd_maxautovchans) - 1; 140bba4862cSAriff Abdullah if (d->rvchancount > 0) 14190da2b28SAriff Abdullah cmax += max(d->rvchancount, snd_maxautovchans) - 1; 142bba4862cSAriff Abdullah if (cmax > PCMMAXCLONE) 143bba4862cSAriff Abdullah cmax = PCMMAXCLONE; 144bba4862cSAriff Abdullah (void)snd_clone_gc(d->clones); 145bba4862cSAriff Abdullah (void)snd_clone_setmaxunit(d->clones, cmax); 146bba4862cSAriff Abdullah } 147bba4862cSAriff Abdullah 14890da2b28SAriff Abdullah int 149bba4862cSAriff Abdullah pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) 150bba4862cSAriff Abdullah { 151bba4862cSAriff Abdullah struct pcm_channel *c, *ch, *nch; 15290da2b28SAriff Abdullah struct pcmchan_caps *caps; 15390da2b28SAriff Abdullah int i, err, vcnt; 154bba4862cSAriff Abdullah 155e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 156a1d444e1SAriff Abdullah 157bba4862cSAriff Abdullah if ((direction == PCMDIR_PLAY && d->playcount < 1) || 158e4e61333SAriff Abdullah (direction == PCMDIR_REC && d->reccount < 1)) 159e4e61333SAriff Abdullah return (ENODEV); 160a580b31aSAriff Abdullah 161e4e61333SAriff Abdullah if (!(d->flags & SD_F_AUTOVCHAN)) 162e4e61333SAriff Abdullah return (EINVAL); 163a1d444e1SAriff Abdullah 164e4e61333SAriff Abdullah if (newcnt < 0 || newcnt > SND_MAXVCHANS) 165e4e61333SAriff Abdullah return (E2BIG); 166a1d444e1SAriff Abdullah 167bba4862cSAriff Abdullah if (direction == PCMDIR_PLAY) 168bba4862cSAriff Abdullah vcnt = d->pvchancount; 169bba4862cSAriff Abdullah else if (direction == PCMDIR_REC) 170bba4862cSAriff Abdullah vcnt = d->rvchancount; 171e4e61333SAriff Abdullah else 172e4e61333SAriff Abdullah return (EINVAL); 173a1d444e1SAriff Abdullah 174a1d444e1SAriff Abdullah if (newcnt > vcnt) { 175bba4862cSAriff Abdullah KASSERT(num == -1 || 176bba4862cSAriff Abdullah (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt), 177bba4862cSAriff Abdullah ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d", 178bba4862cSAriff Abdullah num, newcnt, vcnt)); 179a1d444e1SAriff Abdullah /* add new vchans - find a parent channel first */ 180e4e61333SAriff Abdullah ch = NULL; 181bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 182a1d444e1SAriff Abdullah CHN_LOCK(c); 183bba4862cSAriff Abdullah if (c->direction == direction && 184bba4862cSAriff Abdullah ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 18590da2b28SAriff Abdullah c->refcount < 1 && 186e4e61333SAriff Abdullah !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 18790da2b28SAriff Abdullah /* 18890da2b28SAriff Abdullah * Reuse hw channel with vchans already 18990da2b28SAriff Abdullah * created. 19090da2b28SAriff Abdullah */ 19190da2b28SAriff Abdullah if (c->flags & CHN_F_HAS_VCHAN) { 192e4e61333SAriff Abdullah ch = c; 193e4e61333SAriff Abdullah break; 194e4e61333SAriff Abdullah } 19590da2b28SAriff Abdullah /* 19690da2b28SAriff Abdullah * No vchans ever created, look for 19790da2b28SAriff Abdullah * channels with supported formats. 19890da2b28SAriff Abdullah */ 19990da2b28SAriff Abdullah caps = chn_getcaps(c); 20090da2b28SAriff Abdullah if (caps == NULL) { 20190da2b28SAriff Abdullah CHN_UNLOCK(c); 20290da2b28SAriff Abdullah continue; 20390da2b28SAriff Abdullah } 20490da2b28SAriff Abdullah for (i = 0; caps->fmtlist[i] != 0; i++) { 20590da2b28SAriff Abdullah if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 20690da2b28SAriff Abdullah break; 20790da2b28SAriff Abdullah } 20890da2b28SAriff Abdullah if (caps->fmtlist[i] != 0) { 20990da2b28SAriff Abdullah ch = c; 21090da2b28SAriff Abdullah break; 21190da2b28SAriff Abdullah } 21290da2b28SAriff Abdullah } 213a1d444e1SAriff Abdullah CHN_UNLOCK(c); 214a1d444e1SAriff Abdullah } 215e4e61333SAriff Abdullah if (ch == NULL) 216e4e61333SAriff Abdullah return (EBUSY); 217e4e61333SAriff Abdullah ch->flags |= CHN_F_BUSY; 218e4e61333SAriff Abdullah err = 0; 219a1d444e1SAriff Abdullah while (err == 0 && newcnt > vcnt) { 220e4e61333SAriff Abdullah err = vchan_create(ch, num); 221bba4862cSAriff Abdullah if (err == 0) 222a1d444e1SAriff Abdullah vcnt++; 223bba4862cSAriff Abdullah else if (err == E2BIG && newcnt > vcnt) 224bba4862cSAriff Abdullah device_printf(d->dev, 225bba4862cSAriff Abdullah "%s: err=%d Maximum channel reached.\n", 226bba4862cSAriff Abdullah __func__, err); 227a1d444e1SAriff Abdullah } 228a1d444e1SAriff Abdullah if (vcnt == 0) 229e4e61333SAriff Abdullah ch->flags &= ~CHN_F_BUSY; 230e4e61333SAriff Abdullah CHN_UNLOCK(ch); 231e4e61333SAriff Abdullah if (err != 0) 232e4e61333SAriff Abdullah return (err); 233e4e61333SAriff Abdullah else 234bba4862cSAriff Abdullah pcm_clonereset(d); 235a1d444e1SAriff Abdullah } else if (newcnt < vcnt) { 236bba4862cSAriff Abdullah KASSERT(num == -1, 237bba4862cSAriff Abdullah ("bogus vchan_destroy() request num=%d", num)); 238bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 239a1d444e1SAriff Abdullah CHN_LOCK(c); 240bba4862cSAriff Abdullah if (c->direction != direction || 241bba4862cSAriff Abdullah CHN_EMPTY(c, children) || 242bba4862cSAriff Abdullah !(c->flags & CHN_F_HAS_VCHAN)) { 243a1d444e1SAriff Abdullah CHN_UNLOCK(c); 244bba4862cSAriff Abdullah continue; 245a1d444e1SAriff Abdullah } 246bba4862cSAriff Abdullah CHN_FOREACH_SAFE(ch, c, nch, children) { 247bba4862cSAriff Abdullah CHN_LOCK(ch); 24890da2b28SAriff Abdullah if (vcnt == 1 && c->refcount > 0) { 249bba4862cSAriff Abdullah CHN_UNLOCK(ch); 25090da2b28SAriff Abdullah break; 25190da2b28SAriff Abdullah } 25290da2b28SAriff Abdullah if (!(ch->flags & CHN_F_BUSY) && 25390da2b28SAriff Abdullah ch->refcount < 1) { 254bba4862cSAriff Abdullah err = vchan_destroy(ch); 255a1d444e1SAriff Abdullah if (err == 0) 256a1d444e1SAriff Abdullah vcnt--; 257bba4862cSAriff Abdullah } else 258bba4862cSAriff Abdullah CHN_UNLOCK(ch); 259e4e61333SAriff Abdullah if (vcnt == newcnt) 260bba4862cSAriff Abdullah break; 261a1d444e1SAriff Abdullah } 262bba4862cSAriff Abdullah CHN_UNLOCK(c); 263bba4862cSAriff Abdullah break; 264bba4862cSAriff Abdullah } 265bba4862cSAriff Abdullah pcm_clonereset(d); 266bba4862cSAriff Abdullah } 267a1d444e1SAriff Abdullah 268e4e61333SAriff Abdullah return (0); 269a1d444e1SAriff Abdullah } 270a1d444e1SAriff Abdullah 2713fdb3676SAriff Abdullah /* return error status and a locked channel */ 2723fdb3676SAriff Abdullah int 2733fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 27490da2b28SAriff Abdullah pid_t pid, char *comm, int devunit) 275285648f9SCameron Grant { 276285648f9SCameron Grant struct pcm_channel *c; 27790da2b28SAriff Abdullah int err, vchancount, vchan_num; 278bba4862cSAriff Abdullah 279bba4862cSAriff Abdullah KASSERT(d != NULL && ch != NULL && (devunit == -1 || 280bba4862cSAriff Abdullah !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && 281bba4862cSAriff Abdullah (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 282e4e61333SAriff Abdullah ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d", 283bba4862cSAriff Abdullah __func__, d, ch, direction, pid, devunit)); 284e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 285bba4862cSAriff Abdullah 286bba4862cSAriff Abdullah /* Double check again. */ 287bba4862cSAriff Abdullah if (devunit != -1) { 288bba4862cSAriff Abdullah switch (snd_unit2d(devunit)) { 289bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 290bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 291bba4862cSAriff Abdullah if (direction != PCMDIR_PLAY) 29290da2b28SAriff Abdullah return (ENOTSUP); 293bba4862cSAriff Abdullah break; 294bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 295bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 296bba4862cSAriff Abdullah if (direction != PCMDIR_REC) 29790da2b28SAriff Abdullah return (ENOTSUP); 298bba4862cSAriff Abdullah break; 299bba4862cSAriff Abdullah default: 300bba4862cSAriff Abdullah if (!(direction == PCMDIR_PLAY || 301bba4862cSAriff Abdullah direction == PCMDIR_REC)) 30290da2b28SAriff Abdullah return (ENOTSUP); 303bba4862cSAriff Abdullah break; 304bba4862cSAriff Abdullah } 305bba4862cSAriff Abdullah } 306285648f9SCameron Grant 30790da2b28SAriff Abdullah *ch = NULL; 30890da2b28SAriff Abdullah vchan_num = 0; 30990da2b28SAriff Abdullah vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : 31090da2b28SAriff Abdullah d->rvchancount; 31190da2b28SAriff Abdullah 3123fdb3676SAriff Abdullah retry_chnalloc: 31390da2b28SAriff Abdullah err = ENOTSUP; 314f637a36cSCameron Grant /* scan for a free channel */ 315bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 31649c5e6e2SCameron Grant CHN_LOCK(c); 31790da2b28SAriff Abdullah if (devunit == -1 && c->direction == direction && 31890da2b28SAriff Abdullah (c->flags & CHN_F_VIRTUAL)) { 31990da2b28SAriff Abdullah if (vchancount < snd_maxautovchans && 32090da2b28SAriff Abdullah vchan_num < CHN_CHAN(c)) { 32190da2b28SAriff Abdullah CHN_UNLOCK(c); 32290da2b28SAriff Abdullah goto vchan_alloc; 32390da2b28SAriff Abdullah } 32490da2b28SAriff Abdullah vchan_num++; 32590da2b28SAriff Abdullah } 326bba4862cSAriff Abdullah if (c->direction == direction && !(c->flags & CHN_F_BUSY) && 327bba4862cSAriff Abdullah (devunit == -1 || devunit == -2 || c->unit == devunit)) { 328285648f9SCameron Grant c->flags |= CHN_F_BUSY; 329b8f0d9e0SCameron Grant c->pid = pid; 33090da2b28SAriff Abdullah strlcpy(c->comm, (comm != NULL) ? comm : 33190da2b28SAriff Abdullah CHN_COMM_UNKNOWN, sizeof(c->comm)); 3323fdb3676SAriff Abdullah *ch = c; 333bba4862cSAriff Abdullah return (0); 334bba4862cSAriff Abdullah } else if (c->unit == devunit) { 3353fdb3676SAriff Abdullah if (c->direction != direction) 33690da2b28SAriff Abdullah err = ENOTSUP; 3373fdb3676SAriff Abdullah else if (c->flags & CHN_F_BUSY) 3383fdb3676SAriff Abdullah err = EBUSY; 3393fdb3676SAriff Abdullah else 3403fdb3676SAriff Abdullah err = EINVAL; 3413fdb3676SAriff Abdullah CHN_UNLOCK(c); 342bba4862cSAriff Abdullah return (err); 343bba4862cSAriff Abdullah } else if ((devunit == -1 || devunit == -2) && 344bba4862cSAriff Abdullah c->direction == direction && (c->flags & CHN_F_BUSY)) 345a1d444e1SAriff Abdullah err = EBUSY; 34649c5e6e2SCameron Grant CHN_UNLOCK(c); 347285648f9SCameron Grant } 348f637a36cSCameron Grant 349e4e61333SAriff Abdullah if (devunit == -2) 350e4e61333SAriff Abdullah return (err); 351e4e61333SAriff Abdullah 35290da2b28SAriff Abdullah vchan_alloc: 353f637a36cSCameron Grant /* no channel available */ 354e4e61333SAriff Abdullah if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || 355e4e61333SAriff Abdullah snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { 356bba4862cSAriff Abdullah if (!(vchancount > 0 && vchancount < snd_maxautovchans) && 357bba4862cSAriff Abdullah (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) 358bba4862cSAriff Abdullah return (err); 359bba4862cSAriff Abdullah err = pcm_setvchans(d, direction, vchancount + 1, 360bba4862cSAriff Abdullah (devunit == -1) ? -1 : snd_unit2c(devunit)); 361a1d444e1SAriff Abdullah if (err == 0) { 362bba4862cSAriff Abdullah if (devunit == -1) 363bba4862cSAriff Abdullah devunit = -2; 3643fdb3676SAriff Abdullah goto retry_chnalloc; 365f637a36cSCameron Grant } 366f637a36cSCameron Grant } 367f637a36cSCameron Grant 368bba4862cSAriff Abdullah return (err); 369285648f9SCameron Grant } 370285648f9SCameron Grant 371b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 372285648f9SCameron Grant int 373b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 374285648f9SCameron Grant { 375e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 376b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 377e4e61333SAriff Abdullah 378285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 379b8f0d9e0SCameron Grant c->pid = -1; 38090da2b28SAriff Abdullah strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm)); 38149c5e6e2SCameron Grant CHN_UNLOCK(c); 382e4e61333SAriff Abdullah 383e4e61333SAriff Abdullah return (0); 384285648f9SCameron Grant } 385285648f9SCameron Grant 386285648f9SCameron Grant int 387285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 388285648f9SCameron Grant { 389e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 390b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 391e4e61333SAriff Abdullah 392285648f9SCameron Grant c->refcount += ref; 393e4e61333SAriff Abdullah 394e4e61333SAriff Abdullah return (c->refcount); 395285648f9SCameron Grant } 396285648f9SCameron Grant 39767b1dce3SCameron Grant int 39867b1dce3SCameron Grant pcm_inprog(struct snddev_info *d, int delta) 39967b1dce3SCameron Grant { 40090da2b28SAriff Abdullah PCM_LOCKASSERT(d); 401a527dbc7SCameron Grant 402a527dbc7SCameron Grant d->inprog += delta; 403e4e61333SAriff Abdullah 404e4e61333SAriff Abdullah return (d->inprog); 40567b1dce3SCameron Grant } 40667b1dce3SCameron Grant 40767b1dce3SCameron Grant static void 40867b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 40967b1dce3SCameron Grant { 410e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 411e4e61333SAriff Abdullah 412bba4862cSAriff Abdullah if (num < 0) 413bba4862cSAriff Abdullah return; 414bba4862cSAriff Abdullah 415bba4862cSAriff Abdullah if (num >= 0 && d->pvchancount > num) 416bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1); 417bba4862cSAriff Abdullah else if (num > 0 && d->pvchancount == 0) 418bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1); 419bba4862cSAriff Abdullah 420bba4862cSAriff Abdullah if (num >= 0 && d->rvchancount > num) 421bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, num, -1); 422bba4862cSAriff Abdullah else if (num > 0 && d->rvchancount == 0) 423bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, 1, -1); 424bba4862cSAriff Abdullah 425bba4862cSAriff Abdullah pcm_clonereset(d); 42667b1dce3SCameron Grant } 42767b1dce3SCameron Grant 42833dbf14aSCameron Grant static int 429851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 43033dbf14aSCameron Grant { 431b8f0d9e0SCameron Grant struct snddev_info *d; 43233dbf14aSCameron Grant int error, unit; 43333dbf14aSCameron Grant 43433dbf14aSCameron Grant unit = snd_unit; 435041b706bSDavid Malone error = sysctl_handle_int(oidp, &unit, 0, req); 43633dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 437b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 438e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 439b8f0d9e0SCameron Grant return EINVAL; 44033dbf14aSCameron Grant snd_unit = unit; 441cbebc90dSAlexander Motin snd_unit_auto = 0; 44233dbf14aSCameron Grant } 44333dbf14aSCameron Grant return (error); 44433dbf14aSCameron Grant } 445851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */ 446b7d6c6b5SEitan Adler SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, 4477029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0, 4487029da5cSPawel Biernacki sizeof(int), sysctl_hw_snd_default_unit, "I", 449b7d6c6b5SEitan Adler "default sound device"); 450987e5972SCameron Grant 451cd9766c5SCameron Grant static int 45267b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 453cd9766c5SCameron Grant { 45467b1dce3SCameron Grant struct snddev_info *d; 45567b1dce3SCameron Grant int i, v, error; 456cd9766c5SCameron Grant 45767b1dce3SCameron Grant v = snd_maxautovchans; 458041b706bSDavid Malone error = sysctl_handle_int(oidp, &v, 0, req); 459cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 460bba4862cSAriff Abdullah if (v < 0) 461bba4862cSAriff Abdullah v = 0; 462bba4862cSAriff Abdullah if (v > SND_MAXVCHANS) 463bba4862cSAriff Abdullah v = SND_MAXVCHANS; 464bba4862cSAriff Abdullah snd_maxautovchans = v; 4659c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 4669c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 46767b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 468e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 46967b1dce3SCameron Grant continue; 470e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 47167b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 472e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 47367b1dce3SCameron Grant } 47467b1dce3SCameron Grant } 475cd9766c5SCameron Grant return (error); 476cd9766c5SCameron Grant } 4777029da5cSPawel Biernacki SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, 4787029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 4797029da5cSPawel Biernacki sysctl_hw_snd_maxautovchans, "I", 4807029da5cSPawel Biernacki "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 494285648f9SCameron Grant switch (dir) { 495285648f9SCameron Grant case PCMDIR_PLAY: 496285648f9SCameron Grant dirs = "play"; 497a3193a9cSDon Lewis direction = PCMDIR_PLAY; 498a67fe5c1SCameron Grant pnum = &d->playcount; 499bba4862cSAriff Abdullah device = SND_DEV_DSPHW_PLAY; 500bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 501285648f9SCameron Grant break; 502bba4862cSAriff Abdullah case PCMDIR_PLAY_VIRTUAL: 503bba4862cSAriff Abdullah dirs = "virtual"; 504bba4862cSAriff Abdullah direction = PCMDIR_PLAY; 505bba4862cSAriff Abdullah pnum = &d->pvchancount; 506bba4862cSAriff Abdullah device = SND_DEV_DSPHW_VPLAY; 507bba4862cSAriff Abdullah max = SND_MAXVCHANS; 508bba4862cSAriff Abdullah break; 509285648f9SCameron Grant case PCMDIR_REC: 510285648f9SCameron Grant dirs = "record"; 511a3193a9cSDon Lewis direction = PCMDIR_REC; 512a67fe5c1SCameron Grant pnum = &d->reccount; 513bba4862cSAriff Abdullah device = SND_DEV_DSPHW_REC; 514bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 515285648f9SCameron Grant break; 516bba4862cSAriff Abdullah case PCMDIR_REC_VIRTUAL: 517285648f9SCameron Grant dirs = "virtual"; 518bba4862cSAriff Abdullah direction = PCMDIR_REC; 519bba4862cSAriff Abdullah pnum = &d->rvchancount; 520bba4862cSAriff Abdullah device = SND_DEV_DSPHW_VREC; 521bba4862cSAriff Abdullah max = SND_MAXVCHANS; 522285648f9SCameron Grant break; 523285648f9SCameron Grant default: 524e4e61333SAriff Abdullah return (NULL); 5259c326820SCameron Grant } 526285648f9SCameron Grant 527bba4862cSAriff Abdullah chan = (num == -1) ? 0 : num; 528285648f9SCameron Grant 529e4e61333SAriff Abdullah if (*pnum >= max || chan >= max) 530e4e61333SAriff Abdullah return (NULL); 531bba4862cSAriff Abdullah 5323fdb3676SAriff Abdullah rpnum = 0; 533bba4862cSAriff Abdullah 534bba4862cSAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 535bba4862cSAriff Abdullah if (CHN_DEV(ch) != device) 5363fdb3676SAriff Abdullah continue; 537bba4862cSAriff Abdullah if (chan == CHN_CHAN(ch)) { 538bba4862cSAriff Abdullah if (num != -1) { 5393fdb3676SAriff Abdullah device_printf(d->dev, 540bba4862cSAriff Abdullah "channel num=%d allocated!\n", chan); 541e4e61333SAriff Abdullah return (NULL); 542bba4862cSAriff Abdullah } 543bba4862cSAriff Abdullah chan++; 544bba4862cSAriff Abdullah if (chan >= max) { 545bba4862cSAriff Abdullah device_printf(d->dev, 546bba4862cSAriff Abdullah "chan=%d > %d\n", chan, max); 547e4e61333SAriff Abdullah return (NULL); 548bba4862cSAriff Abdullah } 5493fdb3676SAriff Abdullah } 5503fdb3676SAriff Abdullah rpnum++; 5513fdb3676SAriff Abdullah } 552bba4862cSAriff Abdullah 5533fdb3676SAriff Abdullah if (*pnum != rpnum) { 5543fdb3676SAriff Abdullah device_printf(d->dev, 555bba4862cSAriff Abdullah "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", 5563fdb3676SAriff Abdullah __func__, dirs, *pnum, rpnum); 557e4e61333SAriff Abdullah return (NULL); 5583fdb3676SAriff Abdullah } 559a67fe5c1SCameron Grant 560bba4862cSAriff Abdullah udc = snd_mkunit(device_get_unit(d->dev), device, chan); 561bba4862cSAriff Abdullah devname = dsp_unit2name(buf, sizeof(buf), udc); 562bba4862cSAriff Abdullah 563bba4862cSAriff Abdullah if (devname == NULL) { 564bba4862cSAriff Abdullah device_printf(d->dev, 565bba4862cSAriff Abdullah "Failed to query device name udc=0x%08x\n", udc); 566e4e61333SAriff Abdullah return (NULL); 567bba4862cSAriff Abdullah } 568bba4862cSAriff Abdullah 56990da2b28SAriff Abdullah PCM_UNLOCK(d); 570bba4862cSAriff Abdullah ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 571bba4862cSAriff Abdullah ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); 572bba4862cSAriff Abdullah ch->unit = udc; 573285648f9SCameron Grant ch->pid = -1; 57490da2b28SAriff Abdullah strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm)); 575285648f9SCameron Grant ch->parentsnddev = d; 576285648f9SCameron Grant ch->parentchannel = parent; 577436c9b65SScott Long ch->dev = d->dev; 578bba4862cSAriff Abdullah ch->trigger = PCMTRIG_STOP; 579bba4862cSAriff Abdullah snprintf(ch->name, sizeof(ch->name), "%s:%s:%s", 580bba4862cSAriff Abdullah device_get_nameunit(ch->dev), dirs, devname); 581285648f9SCameron Grant 582a3193a9cSDon Lewis err = chn_init(ch, devinfo, dir, direction); 58390da2b28SAriff Abdullah PCM_LOCK(d); 5840f55ac6cSCameron Grant if (err) { 585bba4862cSAriff Abdullah device_printf(d->dev, "chn_init(%s) failed: err = %d\n", 586bba4862cSAriff Abdullah ch->name, err); 587285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 588285648f9SCameron Grant free(ch, M_DEVBUF); 589e4e61333SAriff Abdullah return (NULL); 590bbb5bf3dSCameron Grant } 591285648f9SCameron Grant 592e4e61333SAriff Abdullah return (ch); 593285648f9SCameron Grant } 594285648f9SCameron Grant 595285648f9SCameron Grant int 596285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 597285648f9SCameron Grant { 598cf8e3ea2SMateusz Guzik struct snddev_info *d __diagused; 599285648f9SCameron Grant int err; 600285648f9SCameron Grant 601a67fe5c1SCameron Grant d = ch->parentsnddev; 602e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 603e4e61333SAriff Abdullah 604285648f9SCameron Grant err = chn_kill(ch); 605285648f9SCameron Grant if (err) { 606e4e61333SAriff Abdullah device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n", 607e4e61333SAriff Abdullah ch->name, err); 608e4e61333SAriff Abdullah return (err); 609285648f9SCameron Grant } 610285648f9SCameron Grant 611285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 612285648f9SCameron Grant free(ch, M_DEVBUF); 613285648f9SCameron Grant 614e4e61333SAriff Abdullah return (0); 615285648f9SCameron Grant } 616285648f9SCameron Grant 617285648f9SCameron Grant int 6185ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 619285648f9SCameron Grant { 620e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 62190da2b28SAriff Abdullah PCM_LOCKASSERT(d); 622bba4862cSAriff Abdullah KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 623bba4862cSAriff Abdullah ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 624285648f9SCameron Grant 62590da2b28SAriff Abdullah CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm); 6263fdb3676SAriff Abdullah 627e4e61333SAriff Abdullah switch (CHN_DEV(ch)) { 628e4e61333SAriff Abdullah case SND_DEV_DSPHW_PLAY: 629e4e61333SAriff Abdullah d->playcount++; 630e4e61333SAriff Abdullah break; 631e4e61333SAriff Abdullah case SND_DEV_DSPHW_VPLAY: 632e4e61333SAriff Abdullah d->pvchancount++; 633e4e61333SAriff Abdullah break; 634e4e61333SAriff Abdullah case SND_DEV_DSPHW_REC: 635e4e61333SAriff Abdullah d->reccount++; 636e4e61333SAriff Abdullah break; 637e4e61333SAriff Abdullah case SND_DEV_DSPHW_VREC: 638e4e61333SAriff Abdullah d->rvchancount++; 639e4e61333SAriff Abdullah break; 640e4e61333SAriff Abdullah default: 641e4e61333SAriff Abdullah break; 642e4e61333SAriff Abdullah } 643b8f0d9e0SCameron Grant 644e4e61333SAriff Abdullah d->devcount++; 645e4e61333SAriff Abdullah 646e4e61333SAriff Abdullah return (0); 64733dbf14aSCameron Grant } 64833dbf14aSCameron Grant 649285648f9SCameron Grant int 6505ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 65133dbf14aSCameron Grant { 652bba4862cSAriff Abdullah struct pcm_channel *tmp; 65333dbf14aSCameron Grant 654e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 65590da2b28SAriff Abdullah PCM_LOCKASSERT(d); 656e4e61333SAriff Abdullah 657bba4862cSAriff Abdullah tmp = NULL; 658a527dbc7SCameron Grant 659bba4862cSAriff Abdullah CHN_FOREACH(tmp, d, channels.pcm) { 660bba4862cSAriff Abdullah if (tmp == ch) 661bba4862cSAriff Abdullah break; 66233dbf14aSCameron Grant } 663bba4862cSAriff Abdullah 664bba4862cSAriff Abdullah if (tmp != ch) 665e4e61333SAriff Abdullah return (EINVAL); 66667beb5a5SCameron Grant 667bba4862cSAriff Abdullah CHN_REMOVE(d, ch, channels.pcm); 668e4e61333SAriff Abdullah 669bba4862cSAriff Abdullah switch (CHN_DEV(ch)) { 670bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 67167beb5a5SCameron Grant d->playcount--; 672bba4862cSAriff Abdullah break; 673bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 674bba4862cSAriff Abdullah d->pvchancount--; 675bba4862cSAriff Abdullah break; 676bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 677bba4862cSAriff Abdullah d->reccount--; 678bba4862cSAriff Abdullah break; 679bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 680bba4862cSAriff Abdullah d->rvchancount--; 681bba4862cSAriff Abdullah break; 682bba4862cSAriff Abdullah default: 683bba4862cSAriff Abdullah break; 684bba4862cSAriff Abdullah } 685285648f9SCameron Grant 686e4e61333SAriff Abdullah d->devcount--; 687e4e61333SAriff Abdullah 688e4e61333SAriff Abdullah return (0); 689987e5972SCameron Grant } 690987e5972SCameron Grant 691987e5972SCameron Grant int 692285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 693285648f9SCameron Grant { 694285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 69567b1dce3SCameron Grant struct pcm_channel *ch; 69667b1dce3SCameron Grant int err; 697285648f9SCameron Grant 698e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 699e4e61333SAriff Abdullah 70090da2b28SAriff Abdullah PCM_LOCK(d); 701bba4862cSAriff Abdullah ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); 702285648f9SCameron Grant if (!ch) { 703e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", 704e4e61333SAriff Abdullah cls->name, dir, devinfo); 70590da2b28SAriff Abdullah PCM_UNLOCK(d); 706e4e61333SAriff Abdullah return (ENODEV); 707285648f9SCameron Grant } 708cd9766c5SCameron Grant 7095ee30e27SMathew Kanner err = pcm_chn_add(d, ch); 71090da2b28SAriff Abdullah PCM_UNLOCK(d); 711285648f9SCameron Grant if (err) { 712e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", 713e4e61333SAriff Abdullah ch->name, err); 714285648f9SCameron Grant pcm_chn_destroy(ch); 715cd9766c5SCameron Grant } 716cd9766c5SCameron Grant 717e4e61333SAriff Abdullah return (err); 718285648f9SCameron Grant } 719285648f9SCameron Grant 720285648f9SCameron Grant static int 721285648f9SCameron Grant pcm_killchan(device_t dev) 722285648f9SCameron Grant { 723285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 724e33bee07SOlivier Houchard struct pcm_channel *ch; 725e4e61333SAriff Abdullah int error; 726e4e61333SAriff Abdullah 727e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 728285648f9SCameron Grant 729bba4862cSAriff Abdullah ch = CHN_FIRST(d, channels.pcm); 730285648f9SCameron Grant 73190da2b28SAriff Abdullah PCM_LOCK(d); 732bba4862cSAriff Abdullah error = pcm_chn_remove(d, ch); 73390da2b28SAriff Abdullah PCM_UNLOCK(d); 734e33bee07SOlivier Houchard if (error) 735e33bee07SOlivier Houchard return (error); 736e33bee07SOlivier Houchard return (pcm_chn_destroy(ch)); 737285648f9SCameron Grant } 738285648f9SCameron Grant 739cbebc90dSAlexander Motin static int 740cbebc90dSAlexander Motin pcm_best_unit(int old) 741cbebc90dSAlexander Motin { 742cbebc90dSAlexander Motin struct snddev_info *d; 743cbebc90dSAlexander Motin int i, best, bestprio, prio; 744cbebc90dSAlexander Motin 745cbebc90dSAlexander Motin best = -1; 746cbebc90dSAlexander Motin bestprio = -100; 747cbebc90dSAlexander Motin for (i = 0; pcm_devclass != NULL && 748cbebc90dSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 749cbebc90dSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 750cbebc90dSAlexander Motin if (!PCM_REGISTERED(d)) 751cbebc90dSAlexander Motin continue; 752cbebc90dSAlexander Motin prio = 0; 753cbebc90dSAlexander Motin if (d->playcount == 0) 754cbebc90dSAlexander Motin prio -= 10; 755cbebc90dSAlexander Motin if (d->reccount == 0) 756cbebc90dSAlexander Motin prio -= 2; 757cbebc90dSAlexander Motin if (prio > bestprio || (prio == bestprio && i == old)) { 758cbebc90dSAlexander Motin best = i; 759cbebc90dSAlexander Motin bestprio = prio; 760cbebc90dSAlexander Motin } 761cbebc90dSAlexander Motin } 762cbebc90dSAlexander Motin return (best); 763cbebc90dSAlexander Motin } 764cbebc90dSAlexander Motin 765285648f9SCameron Grant int 766987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 767987e5972SCameron Grant { 76866ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 76949c5e6e2SCameron Grant 77032a0e5d5SHans Petter Selasky /* should only be called once */ 77132a0e5d5SHans Petter Selasky if (d->flags & SD_F_REGISTERED) 77232a0e5d5SHans Petter Selasky return (EINVAL); 77332a0e5d5SHans Petter Selasky 774e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 775bba4862cSAriff Abdullah 776e4e61333SAriff Abdullah if (d->playcount == 0 || d->reccount == 0) 777e4e61333SAriff Abdullah d->flags |= SD_F_SIMPLEX; 778e4e61333SAriff Abdullah 77932a0e5d5SHans Petter Selasky if (d->playcount > 0 || d->reccount > 0) 780e4e61333SAriff Abdullah d->flags |= SD_F_AUTOVCHAN; 781e4e61333SAriff Abdullah 782e4e61333SAriff Abdullah pcm_setmaxautovchans(d, snd_maxautovchans); 783bba4862cSAriff Abdullah 784a580b31aSAriff Abdullah strlcpy(d->status, str, SND_STATUSLEN); 785bba4862cSAriff Abdullah 78690da2b28SAriff Abdullah PCM_LOCK(d); 787e4e61333SAriff Abdullah 788bba4862cSAriff Abdullah /* Last stage, enable cloning. */ 789e4e61333SAriff Abdullah if (d->clones != NULL) 790bba4862cSAriff Abdullah (void)snd_clone_enable(d->clones); 791e4e61333SAriff Abdullah 792e4e61333SAriff Abdullah /* Done, we're ready.. */ 793e4e61333SAriff Abdullah d->flags |= SD_F_REGISTERED; 794e4e61333SAriff Abdullah 795e4e61333SAriff Abdullah PCM_RELEASE(d); 796bba4862cSAriff Abdullah 79790da2b28SAriff Abdullah PCM_UNLOCK(d); 798bba4862cSAriff Abdullah 79932a0e5d5SHans Petter Selasky /* 80032a0e5d5SHans Petter Selasky * Create all sysctls once SD_F_REGISTERED is set else 80132a0e5d5SHans Petter Selasky * tunable sysctls won't work: 80232a0e5d5SHans Petter Selasky */ 80332a0e5d5SHans Petter Selasky pcm_sysinit(dev); 80432a0e5d5SHans Petter Selasky 805cbebc90dSAlexander Motin if (snd_unit_auto < 0) 806cbebc90dSAlexander Motin snd_unit_auto = (snd_unit < 0) ? 1 : 0; 807cbebc90dSAlexander Motin if (snd_unit < 0 || snd_unit_auto > 1) 808f3685841SAriff Abdullah snd_unit = device_get_unit(dev); 809cbebc90dSAlexander Motin else if (snd_unit_auto == 1) 810cbebc90dSAlexander Motin snd_unit = pcm_best_unit(snd_unit); 811f3685841SAriff Abdullah 812e4e61333SAriff Abdullah return (0); 813987e5972SCameron Grant } 814987e5972SCameron Grant 815a1d444e1SAriff Abdullah uint32_t 816987e5972SCameron Grant pcm_getflags(device_t dev) 817987e5972SCameron Grant { 81866ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 81949c5e6e2SCameron Grant 820987e5972SCameron Grant return d->flags; 821987e5972SCameron Grant } 822987e5972SCameron Grant 823987e5972SCameron Grant void 824a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val) 825987e5972SCameron Grant { 82666ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 827d95502a8SCameron Grant 828987e5972SCameron Grant d->flags = val; 829987e5972SCameron Grant } 830987e5972SCameron Grant 83139004e69SCameron Grant void * 83239004e69SCameron Grant pcm_getdevinfo(device_t dev) 83339004e69SCameron Grant { 83466ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 83549c5e6e2SCameron Grant 83639004e69SCameron Grant return d->devinfo; 83739004e69SCameron Grant } 83839004e69SCameron Grant 839a67fe5c1SCameron Grant unsigned int 840d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 841a67fe5c1SCameron Grant { 842a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 8434e60be34SCameron Grant int sz, x; 844a67fe5c1SCameron Grant 845a67fe5c1SCameron Grant sz = 0; 8464e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 8474e60be34SCameron Grant x = sz; 848d55d96f6SAlexander Leidinger RANGE(sz, minbufsz, maxbufsz); 8494e60be34SCameron Grant if (x != sz) 850d55d96f6SAlexander Leidinger device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 851d55d96f6SAlexander Leidinger x = minbufsz; 8524e60be34SCameron Grant while (x < sz) 8534e60be34SCameron Grant x <<= 1; 8544e60be34SCameron Grant if (x > sz) 8554e60be34SCameron Grant x >>= 1; 8564e60be34SCameron Grant if (x != sz) { 8574e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 8584e60be34SCameron Grant sz = x; 8594e60be34SCameron Grant } 8604e60be34SCameron Grant } else { 861a67fe5c1SCameron Grant sz = deflt; 8624e60be34SCameron Grant } 8634e60be34SCameron Grant 864a67fe5c1SCameron Grant d->bufsz = sz; 865a67fe5c1SCameron Grant 866a67fe5c1SCameron Grant return sz; 867a67fe5c1SCameron Grant } 868a67fe5c1SCameron Grant 86990da2b28SAriff Abdullah static int 87090da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) 87190da2b28SAriff Abdullah { 87290da2b28SAriff Abdullah struct snddev_info *d; 87390da2b28SAriff Abdullah int err, val; 87490da2b28SAriff Abdullah 87590da2b28SAriff Abdullah d = oidp->oid_arg1; 87690da2b28SAriff Abdullah if (!PCM_REGISTERED(d)) 87790da2b28SAriff Abdullah return (ENODEV); 87890da2b28SAriff Abdullah 87990da2b28SAriff Abdullah PCM_LOCK(d); 88090da2b28SAriff Abdullah PCM_WAIT(d); 88190da2b28SAriff Abdullah val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; 88290da2b28SAriff Abdullah PCM_ACQUIRE(d); 88390da2b28SAriff Abdullah PCM_UNLOCK(d); 88490da2b28SAriff Abdullah 88590da2b28SAriff Abdullah err = sysctl_handle_int(oidp, &val, 0, req); 88690da2b28SAriff Abdullah 88790da2b28SAriff Abdullah if (err == 0 && req->newptr != NULL) { 88890da2b28SAriff Abdullah if (!(val == 0 || val == 1)) { 88990da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 89090da2b28SAriff Abdullah return (EINVAL); 89190da2b28SAriff Abdullah } 89290da2b28SAriff Abdullah 89390da2b28SAriff Abdullah PCM_LOCK(d); 89490da2b28SAriff Abdullah 89590da2b28SAriff Abdullah d->flags &= ~SD_F_BITPERFECT; 89690da2b28SAriff Abdullah d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; 89790da2b28SAriff Abdullah 89890da2b28SAriff Abdullah PCM_RELEASE(d); 89990da2b28SAriff Abdullah PCM_UNLOCK(d); 90090da2b28SAriff Abdullah } else 90190da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 90290da2b28SAriff Abdullah 90390da2b28SAriff Abdullah return (err); 90490da2b28SAriff Abdullah } 90590da2b28SAriff Abdullah 90690da2b28SAriff Abdullah #ifdef SND_DEBUG 907bba4862cSAriff Abdullah static int 908bba4862cSAriff Abdullah sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS) 909bba4862cSAriff Abdullah { 910bba4862cSAriff Abdullah struct snddev_info *d; 911bba4862cSAriff Abdullah uint32_t flags; 912bba4862cSAriff Abdullah int err; 913bba4862cSAriff Abdullah 914bba4862cSAriff Abdullah d = oidp->oid_arg1; 915e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 916bba4862cSAriff Abdullah return (ENODEV); 917bba4862cSAriff Abdullah 918e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 919e4e61333SAriff Abdullah 920bba4862cSAriff Abdullah flags = snd_clone_getflags(d->clones); 921041b706bSDavid Malone err = sysctl_handle_int(oidp, &flags, 0, req); 922bba4862cSAriff Abdullah 923bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL) { 924e4e61333SAriff Abdullah if (flags & ~SND_CLONE_MASK) 925bba4862cSAriff Abdullah err = EINVAL; 926e4e61333SAriff Abdullah else 927bba4862cSAriff Abdullah (void)snd_clone_setflags(d->clones, flags); 928bba4862cSAriff Abdullah } 929e4e61333SAriff Abdullah 930e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 931bba4862cSAriff Abdullah 932bba4862cSAriff Abdullah return (err); 933bba4862cSAriff Abdullah } 934bba4862cSAriff Abdullah 935bba4862cSAriff Abdullah static int 936bba4862cSAriff Abdullah sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS) 937bba4862cSAriff Abdullah { 938bba4862cSAriff Abdullah struct snddev_info *d; 939bba4862cSAriff Abdullah int err, deadline; 940bba4862cSAriff Abdullah 941bba4862cSAriff Abdullah d = oidp->oid_arg1; 942e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 943bba4862cSAriff Abdullah return (ENODEV); 944bba4862cSAriff Abdullah 945e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 946e4e61333SAriff Abdullah 947bba4862cSAriff Abdullah deadline = snd_clone_getdeadline(d->clones); 948041b706bSDavid Malone err = sysctl_handle_int(oidp, &deadline, 0, req); 949bba4862cSAriff Abdullah 950bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL) { 951bba4862cSAriff Abdullah if (deadline < 0) 952bba4862cSAriff Abdullah err = EINVAL; 953e4e61333SAriff Abdullah else 954bba4862cSAriff Abdullah (void)snd_clone_setdeadline(d->clones, deadline); 955bba4862cSAriff Abdullah } 956e4e61333SAriff Abdullah 957e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 958bba4862cSAriff Abdullah 959bba4862cSAriff Abdullah return (err); 960bba4862cSAriff Abdullah } 961bba4862cSAriff Abdullah 962bba4862cSAriff Abdullah static int 963bba4862cSAriff Abdullah sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS) 964bba4862cSAriff Abdullah { 965bba4862cSAriff Abdullah struct snddev_info *d; 966bba4862cSAriff Abdullah int err, val; 967bba4862cSAriff Abdullah 968bba4862cSAriff Abdullah d = oidp->oid_arg1; 969e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 970bba4862cSAriff Abdullah return (ENODEV); 971bba4862cSAriff Abdullah 972bba4862cSAriff Abdullah val = 0; 973041b706bSDavid Malone err = sysctl_handle_int(oidp, &val, 0, req); 974bba4862cSAriff Abdullah 975bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL && val != 0) { 976e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 977e4e61333SAriff Abdullah val = snd_clone_gc(d->clones); 978e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 979e4e61333SAriff Abdullah if (bootverbose != 0 || snd_verbose > 3) 980e4e61333SAriff Abdullah device_printf(d->dev, "clone gc: pruned=%d\n", val); 981bba4862cSAriff Abdullah } 982bba4862cSAriff Abdullah 983bba4862cSAriff Abdullah return (err); 984bba4862cSAriff Abdullah } 985bba4862cSAriff Abdullah 986bba4862cSAriff Abdullah static int 987bba4862cSAriff Abdullah sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS) 988bba4862cSAriff Abdullah { 989bba4862cSAriff Abdullah struct snddev_info *d; 990bba4862cSAriff Abdullah int i, err, val; 991bba4862cSAriff Abdullah 992bba4862cSAriff Abdullah val = 0; 993041b706bSDavid Malone err = sysctl_handle_int(oidp, &val, 0, req); 994bba4862cSAriff Abdullah 995bba4862cSAriff Abdullah if (err == 0 && req->newptr != NULL && val != 0) { 9969c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 9979c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 998bba4862cSAriff Abdullah d = devclass_get_softc(pcm_devclass, i); 999e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || d->clones == NULL) 1000bba4862cSAriff Abdullah continue; 1001e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 1002e4e61333SAriff Abdullah val = snd_clone_gc(d->clones); 1003e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 1004e4e61333SAriff Abdullah if (bootverbose != 0 || snd_verbose > 3) 1005e4e61333SAriff Abdullah device_printf(d->dev, "clone gc: pruned=%d\n", 1006e4e61333SAriff Abdullah val); 1007bba4862cSAriff Abdullah } 1008bba4862cSAriff Abdullah } 1009bba4862cSAriff Abdullah 1010bba4862cSAriff Abdullah return (err); 1011bba4862cSAriff Abdullah } 10127029da5cSPawel Biernacki SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, 10137029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 10147029da5cSPawel Biernacki sysctl_hw_snd_clone_gc, "I", 1015bba4862cSAriff Abdullah "global clone garbage collector"); 1016bba4862cSAriff Abdullah #endif 1017bba4862cSAriff Abdullah 1018ed2196e5SHans Petter Selasky static u_int8_t 1019ed2196e5SHans Petter Selasky pcm_mode_init(struct snddev_info *d) 1020ed2196e5SHans Petter Selasky { 1021ed2196e5SHans Petter Selasky u_int8_t mode = 0; 1022ed2196e5SHans Petter Selasky 1023ed2196e5SHans Petter Selasky if (d->playcount > 0) 1024ed2196e5SHans Petter Selasky mode |= PCM_MODE_PLAY; 1025ed2196e5SHans Petter Selasky if (d->reccount > 0) 1026ed2196e5SHans Petter Selasky mode |= PCM_MODE_REC; 1027ed2196e5SHans Petter Selasky if (d->mixer_dev != NULL) 1028ed2196e5SHans Petter Selasky mode |= PCM_MODE_MIXER; 1029ed2196e5SHans Petter Selasky 1030ed2196e5SHans Petter Selasky return (mode); 1031ed2196e5SHans Petter Selasky } 1032ed2196e5SHans Petter Selasky 103332a0e5d5SHans Petter Selasky static void 103432a0e5d5SHans Petter Selasky pcm_sysinit(device_t dev) 103532a0e5d5SHans Petter Selasky { 103632a0e5d5SHans Petter Selasky struct snddev_info *d = device_get_softc(dev); 1037ed2196e5SHans Petter Selasky u_int8_t mode; 1038ed2196e5SHans Petter Selasky 1039ed2196e5SHans Petter Selasky mode = pcm_mode_init(d); 104032a0e5d5SHans Petter Selasky 1041132fca63SHans Petter Selasky /* XXX: a user should be able to set this with a control tool, the 104232a0e5d5SHans Petter Selasky sysadmin then needs min+max sysctls for this */ 104332a0e5d5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 104432a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 104532a0e5d5SHans Petter Selasky OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 104632a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 104732a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 10483b4c5433SAlexander Motin "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d, 10497029da5cSPawel Biernacki sizeof(d), sysctl_dev_pcm_bitperfect, "I", 105032a0e5d5SHans Petter Selasky "bit-perfect playback/recording (0=disable, 1=enable)"); 1051ed2196e5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 1052ed2196e5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 1053ed2196e5SHans Petter Selasky OID_AUTO, "mode", CTLFLAG_RD, NULL, mode, 1054ed2196e5SHans Petter Selasky "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than one" 1055ed2196e5SHans Petter Selasky "mode is supported)"); 105632a0e5d5SHans Petter Selasky #ifdef SND_DEBUG 105732a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 105832a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 10593b4c5433SAlexander Motin "clone_flags", CTLTYPE_UINT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 10607029da5cSPawel Biernacki d, sizeof(d), sysctl_dev_pcm_clone_flags, "IU", 106132a0e5d5SHans Petter Selasky "clone flags"); 106232a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 106332a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 10643b4c5433SAlexander Motin "clone_deadline", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 10657029da5cSPawel Biernacki d, sizeof(d), sysctl_dev_pcm_clone_deadline, "I", 106632a0e5d5SHans Petter Selasky "clone expiration deadline (ms)"); 106732a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 106832a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 10697029da5cSPawel Biernacki "clone_gc", 10703b4c5433SAlexander Motin CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d, sizeof(d), 10717029da5cSPawel Biernacki sysctl_dev_pcm_clone_gc, "I", "clone garbage collector"); 107232a0e5d5SHans Petter Selasky #endif 107332a0e5d5SHans Petter Selasky if (d->flags & SD_F_AUTOVCHAN) 107432a0e5d5SHans Petter Selasky vchan_initsys(dev); 107532a0e5d5SHans Petter Selasky if (d->flags & SD_F_EQ) 107632a0e5d5SHans Petter Selasky feeder_eq_initsys(dev); 107732a0e5d5SHans Petter Selasky } 107832a0e5d5SHans Petter Selasky 1079987e5972SCameron Grant int 1080987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 1081987e5972SCameron Grant { 1082bba4862cSAriff Abdullah struct snddev_info *d; 108390da2b28SAriff Abdullah int i; 1084987e5972SCameron Grant 1085b8a36395SCameron Grant if (pcm_veto_load) { 1086b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 1087b8a36395SCameron Grant 1088b8a36395SCameron Grant return EINVAL; 1089b8a36395SCameron Grant } 1090b8a36395SCameron Grant 1091bba4862cSAriff Abdullah if (device_get_unit(dev) > PCMMAXUNIT) { 1092bba4862cSAriff Abdullah device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n", 1093bba4862cSAriff Abdullah device_get_unit(dev), PCMMAXUNIT); 1094bba4862cSAriff Abdullah device_printf(dev, 1095bba4862cSAriff Abdullah "Use 'hw.snd.maxunit' tunable to raise the limit.\n"); 1096bba4862cSAriff Abdullah return ENODEV; 1097bba4862cSAriff Abdullah } 1098bba4862cSAriff Abdullah 1099bba4862cSAriff Abdullah d = device_get_softc(dev); 1100e4e61333SAriff Abdullah d->dev = dev; 11012c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 1102e4e61333SAriff Abdullah cv_init(&d->cv, device_get_nameunit(dev)); 1103e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 1104e4e61333SAriff Abdullah dsp_cdevinfo_init(d); 11057233ababSAlexander Leidinger #if 0 11067233ababSAlexander Leidinger /* 11077233ababSAlexander Leidinger * d->flags should be cleared by the allocator of the softc. 11087233ababSAlexander Leidinger * We cannot clear this field here because several devices set 11097233ababSAlexander Leidinger * this flag before calling pcm_register(). 11107233ababSAlexander Leidinger */ 1111cd9766c5SCameron Grant d->flags = 0; 11127233ababSAlexander Leidinger #endif 111390da2b28SAriff Abdullah i = 0; 111490da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 111590da2b28SAriff Abdullah "vpc", &i) != 0 || i != 0) 111690da2b28SAriff Abdullah d->flags |= SD_F_VPC; 111790da2b28SAriff Abdullah 111890da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 111990da2b28SAriff Abdullah "bitperfect", &i) == 0 && i != 0) 112090da2b28SAriff Abdullah d->flags |= SD_F_BITPERFECT; 112190da2b28SAriff Abdullah 1122987e5972SCameron Grant d->devinfo = devinfo; 1123f637a36cSCameron Grant d->devcount = 0; 1124506a5308SCameron Grant d->reccount = 0; 1125a67fe5c1SCameron Grant d->playcount = 0; 1126bba4862cSAriff Abdullah d->pvchancount = 0; 1127bba4862cSAriff Abdullah d->rvchancount = 0; 1128bba4862cSAriff Abdullah d->pvchanrate = 0; 1129bba4862cSAriff Abdullah d->pvchanformat = 0; 1130bba4862cSAriff Abdullah d->rvchanrate = 0; 1131bba4862cSAriff Abdullah d->rvchanformat = 0; 1132d95502a8SCameron Grant d->inprog = 0; 1133833f7023SCameron Grant 1134bba4862cSAriff Abdullah /* 1135bba4862cSAriff Abdullah * Create clone manager, disabled by default. Cloning will be 113690da2b28SAriff Abdullah * enabled during final stage of driver initialization through 1137bba4862cSAriff Abdullah * pcm_setstatus(). 1138bba4862cSAriff Abdullah */ 1139e4e61333SAriff Abdullah d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE, 1140e4e61333SAriff Abdullah SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK | 1141bba4862cSAriff Abdullah SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF | 1142bba4862cSAriff Abdullah SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED); 1143bba4862cSAriff Abdullah 1144bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm); 1145bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm.busy); 114690da2b28SAriff Abdullah CHN_INIT(d, channels.pcm.opened); 114745550658SPoul-Henning Kamp 1148e4e61333SAriff Abdullah /* XXX This is incorrect, but lets play along for now. */ 1149a1d444e1SAriff Abdullah if ((numplay == 0 || numrec == 0) && numplay != numrec) 1150285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 1151d95502a8SCameron Grant 1152bba4862cSAriff Abdullah sysctl_ctx_init(&d->play_sysctl_ctx); 1153bba4862cSAriff Abdullah d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 1154bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 11557029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node"); 1156bba4862cSAriff Abdullah sysctl_ctx_init(&d->rec_sysctl_ctx); 1157bba4862cSAriff Abdullah d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 1158bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 1159132fca63SHans Petter Selasky CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node"); 1160e4e61333SAriff Abdullah 116132a0e5d5SHans Petter Selasky if (numplay > 0 || numrec > 0) 1162cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 116390da2b28SAriff Abdullah 116467b1dce3SCameron Grant sndstat_register(dev, d->status, sndstat_prepare_pcm); 1165e4e61333SAriff Abdullah 1166987e5972SCameron Grant return 0; 1167987e5972SCameron Grant } 1168987e5972SCameron Grant 116933dbf14aSCameron Grant int 117033dbf14aSCameron Grant pcm_unregister(device_t dev) 11717c438dbeSCameron Grant { 1172e4e61333SAriff Abdullah struct snddev_info *d; 1173a67fe5c1SCameron Grant struct pcm_channel *ch; 11747c438dbeSCameron Grant 1175e4e61333SAriff Abdullah d = device_get_softc(dev); 1176e4e61333SAriff Abdullah 1177e4e61333SAriff Abdullah if (!PCM_ALIVE(d)) { 1178e4e61333SAriff Abdullah device_printf(dev, "unregister: device not configured\n"); 1179e4e61333SAriff Abdullah return (0); 1180e4e61333SAriff Abdullah } 1181bba4862cSAriff Abdullah 118290da2b28SAriff Abdullah PCM_LOCK(d); 1183e4e61333SAriff Abdullah PCM_WAIT(d); 1184e4e61333SAriff Abdullah 1185cc1efc23SHans Petter Selasky d->flags |= SD_F_DETACHING; 1186cc1efc23SHans Petter Selasky 1187e4e61333SAriff Abdullah if (d->inprog != 0) { 11885c25132aSGeorge C A Reid device_printf(dev, "unregister: operation in progress\n"); 118990da2b28SAriff Abdullah PCM_UNLOCK(d); 1190e4e61333SAriff Abdullah return (EBUSY); 11915c25132aSGeorge C A Reid } 11925ee30e27SMathew Kanner 1193e4e61333SAriff Abdullah PCM_ACQUIRE(d); 119490da2b28SAriff Abdullah PCM_UNLOCK(d); 1195e4e61333SAriff Abdullah 1196e4e61333SAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 1197e4e61333SAriff Abdullah CHN_LOCK(ch); 1198e4e61333SAriff Abdullah if (ch->refcount > 0) { 1199e4e61333SAriff Abdullah device_printf(dev, 1200e4e61333SAriff Abdullah "unregister: channel %s busy (pid %d)\n", 1201e4e61333SAriff Abdullah ch->name, ch->pid); 1202e4e61333SAriff Abdullah CHN_UNLOCK(ch); 1203e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 1204e4e61333SAriff Abdullah return (EBUSY); 1205285648f9SCameron Grant } 1206e4e61333SAriff Abdullah CHN_UNLOCK(ch); 1207c9b53085SCameron Grant } 12085ee30e27SMathew Kanner 1209bba4862cSAriff Abdullah if (d->clones != NULL) { 1210bba4862cSAriff Abdullah if (snd_clone_busy(d->clones) != 0) { 1211bba4862cSAriff Abdullah device_printf(dev, "unregister: clone busy\n"); 1212e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 1213e4e61333SAriff Abdullah return (EBUSY); 1214e4e61333SAriff Abdullah } else { 121590da2b28SAriff Abdullah PCM_LOCK(d); 1216bba4862cSAriff Abdullah (void)snd_clone_disable(d->clones); 121790da2b28SAriff Abdullah PCM_UNLOCK(d); 1218e4e61333SAriff Abdullah } 1219bba4862cSAriff Abdullah } 1220bba4862cSAriff Abdullah 1221b4221868SAriff Abdullah if (mixer_uninit(dev) == EBUSY) { 12227233ababSAlexander Leidinger device_printf(dev, "unregister: mixer busy\n"); 122390da2b28SAriff Abdullah PCM_LOCK(d); 1224bba4862cSAriff Abdullah if (d->clones != NULL) 1225bba4862cSAriff Abdullah (void)snd_clone_enable(d->clones); 1226e4e61333SAriff Abdullah PCM_RELEASE(d); 122790da2b28SAriff Abdullah PCM_UNLOCK(d); 1228e4e61333SAriff Abdullah return (EBUSY); 12297233ababSAlexander Leidinger } 12307233ababSAlexander Leidinger 12318461d581SHans Petter Selasky /* remove /dev/sndstat entry first */ 12328461d581SHans Petter Selasky sndstat_unregister(dev); 12338461d581SHans Petter Selasky 123490da2b28SAriff Abdullah PCM_LOCK(d); 1235e4e61333SAriff Abdullah d->flags |= SD_F_DYING; 1236e4e61333SAriff Abdullah d->flags &= ~SD_F_REGISTERED; 123790da2b28SAriff Abdullah PCM_UNLOCK(d); 1238e4e61333SAriff Abdullah 1239e4e61333SAriff Abdullah /* 1240e4e61333SAriff Abdullah * No lock being held, so this thing can be flushed without 1241e4e61333SAriff Abdullah * stucking into devdrn oblivion. 1242e4e61333SAriff Abdullah */ 1243bba4862cSAriff Abdullah if (d->clones != NULL) { 1244bba4862cSAriff Abdullah snd_clone_destroy(d->clones); 1245bba4862cSAriff Abdullah d->clones = NULL; 12467982e7a4SAriff Abdullah } 1247bba4862cSAriff Abdullah 1248bba4862cSAriff Abdullah if (d->play_sysctl_tree != NULL) { 1249bba4862cSAriff Abdullah sysctl_ctx_free(&d->play_sysctl_ctx); 1250bba4862cSAriff Abdullah d->play_sysctl_tree = NULL; 1251bba4862cSAriff Abdullah } 1252bba4862cSAriff Abdullah if (d->rec_sysctl_tree != NULL) { 1253bba4862cSAriff Abdullah sysctl_ctx_free(&d->rec_sysctl_ctx); 1254bba4862cSAriff Abdullah d->rec_sysctl_tree = NULL; 1255a1d444e1SAriff Abdullah } 1256bba4862cSAriff Abdullah 1257bba4862cSAriff Abdullah while (!CHN_EMPTY(d, channels.pcm)) 1258285648f9SCameron Grant pcm_killchan(dev); 12597c438dbeSCameron Grant 1260e4e61333SAriff Abdullah dsp_cdevinfo_flush(d); 1261e4e61333SAriff Abdullah 126290da2b28SAriff Abdullah PCM_LOCK(d); 1263e4e61333SAriff Abdullah PCM_RELEASE(d); 1264e4e61333SAriff Abdullah cv_destroy(&d->cv); 126590da2b28SAriff Abdullah PCM_UNLOCK(d); 126649c5e6e2SCameron Grant snd_mtxfree(d->lock); 1267bba4862cSAriff Abdullah 1268bba4862cSAriff Abdullah if (snd_unit == device_get_unit(dev)) { 1269cbebc90dSAlexander Motin snd_unit = pcm_best_unit(-1); 1270cbebc90dSAlexander Motin if (snd_unit_auto == 0) 1271cbebc90dSAlexander Motin snd_unit_auto = 1; 1272e4e61333SAriff Abdullah } 1273bba4862cSAriff Abdullah 1274e4e61333SAriff Abdullah return (0); 127533dbf14aSCameron Grant } 12767c438dbeSCameron Grant 127767b1dce3SCameron Grant /************************************************************************/ 127867b1dce3SCameron Grant 1279b611c801SAlexander Leidinger /** 1280b611c801SAlexander Leidinger * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 1281b611c801SAlexander Leidinger * 1282b611c801SAlexander Leidinger * @param si Pointer to oss_sysinfo struct where information about the 1283b611c801SAlexander Leidinger * sound subsystem will be written/copied. 1284b611c801SAlexander Leidinger * 1285b611c801SAlexander Leidinger * This routine returns information about the sound system, such as the 1286b611c801SAlexander Leidinger * current OSS version, number of audio, MIDI, and mixer drivers, etc. 1287b611c801SAlexander Leidinger * Also includes a bitmask showing which of the above types of devices 1288b611c801SAlexander Leidinger * are open (busy). 1289b611c801SAlexander Leidinger * 1290b611c801SAlexander Leidinger * @note 1291b611c801SAlexander Leidinger * Calling threads must not hold any snddev_info or pcm_channel locks. 1292b611c801SAlexander Leidinger * 1293b611c801SAlexander Leidinger * @author Ryan Beasley <ryanb@FreeBSD.org> 1294b611c801SAlexander Leidinger */ 1295b611c801SAlexander Leidinger void 1296b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si) 1297b611c801SAlexander Leidinger { 1298b611c801SAlexander Leidinger static char si_product[] = "FreeBSD native OSS ABI"; 1299b611c801SAlexander Leidinger static char si_version[] = __XSTRING(__FreeBSD_version); 1300c412d503SAlexander Motin static char si_license[] = "BSD"; 1301b611c801SAlexander Leidinger static int intnbits = sizeof(int) * 8; /* Better suited as macro? 1302b611c801SAlexander Leidinger Must pester a C guru. */ 1303b611c801SAlexander Leidinger 1304b611c801SAlexander Leidinger struct snddev_info *d; 1305b611c801SAlexander Leidinger struct pcm_channel *c; 1306b611c801SAlexander Leidinger int i, j, ncards; 1307b611c801SAlexander Leidinger 1308b611c801SAlexander Leidinger ncards = 0; 1309b611c801SAlexander Leidinger 1310b611c801SAlexander Leidinger strlcpy(si->product, si_product, sizeof(si->product)); 1311b611c801SAlexander Leidinger strlcpy(si->version, si_version, sizeof(si->version)); 1312b611c801SAlexander Leidinger si->versionnum = SOUND_VERSION; 1313c412d503SAlexander Motin strlcpy(si->license, si_license, sizeof(si->license)); 1314b611c801SAlexander Leidinger 1315b611c801SAlexander Leidinger /* 1316b611c801SAlexander Leidinger * Iterate over PCM devices and their channels, gathering up data 1317b611c801SAlexander Leidinger * for the numaudios, ncards, and openedaudio fields. 1318b611c801SAlexander Leidinger */ 1319b611c801SAlexander Leidinger si->numaudios = 0; 1320b611c801SAlexander Leidinger bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 1321b611c801SAlexander Leidinger 1322b611c801SAlexander Leidinger j = 0; 1323b611c801SAlexander Leidinger 13249c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 13259c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 1326b611c801SAlexander Leidinger d = devclass_get_softc(pcm_devclass, i); 1327e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 1328b611c801SAlexander Leidinger continue; 1329b611c801SAlexander Leidinger 1330e4e61333SAriff Abdullah /* XXX Need Giant magic entry ??? */ 1331e4e61333SAriff Abdullah 1332b611c801SAlexander Leidinger /* See note in function's docblock */ 133390da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 133490da2b28SAriff Abdullah PCM_LOCK(d); 1335b611c801SAlexander Leidinger 1336b611c801SAlexander Leidinger si->numaudios += d->devcount; 1337b611c801SAlexander Leidinger ++ncards; 1338b611c801SAlexander Leidinger 1339bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 134090da2b28SAriff Abdullah CHN_UNLOCKASSERT(c); 1341b611c801SAlexander Leidinger CHN_LOCK(c); 1342b611c801SAlexander Leidinger if (c->flags & CHN_F_BUSY) 1343b611c801SAlexander Leidinger si->openedaudio[j / intnbits] |= 1344b611c801SAlexander Leidinger (1 << (j % intnbits)); 1345b611c801SAlexander Leidinger CHN_UNLOCK(c); 1346b611c801SAlexander Leidinger j++; 1347b611c801SAlexander Leidinger } 1348b611c801SAlexander Leidinger 134990da2b28SAriff Abdullah PCM_UNLOCK(d); 1350b611c801SAlexander Leidinger } 1351c412d503SAlexander Motin si->numaudioengines = si->numaudios; 1352b611c801SAlexander Leidinger 1353b611c801SAlexander Leidinger si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 1354b611c801SAlexander Leidinger /** 1355b611c801SAlexander Leidinger * @todo Collect num{midis,timers}. 1356b611c801SAlexander Leidinger * 1357b611c801SAlexander Leidinger * Need access to sound/midi/midi.c::midistat_lock in order 1358b611c801SAlexander Leidinger * to safely touch midi_devices and get a head count of, well, 1359b611c801SAlexander Leidinger * MIDI devices. midistat_lock is a global static (i.e., local to 1360b611c801SAlexander Leidinger * midi.c), but midi_devices is a regular global; should the mutex 1361b611c801SAlexander Leidinger * be publicized, or is there another way to get this information? 1362b611c801SAlexander Leidinger * 1363b611c801SAlexander Leidinger * NB: MIDI/sequencer stuff is currently on hold. 1364b611c801SAlexander Leidinger */ 1365b611c801SAlexander Leidinger si->nummidis = 0; 1366b611c801SAlexander Leidinger si->numtimers = 0; 1367b611c801SAlexander Leidinger si->nummixers = mixer_count; 1368b611c801SAlexander Leidinger si->numcards = ncards; 1369b611c801SAlexander Leidinger /* OSSv4 docs: Intended only for test apps; API doesn't 1370b611c801SAlexander Leidinger really have much of a concept of cards. Shouldn't be 1371b611c801SAlexander Leidinger used by applications. */ 1372b611c801SAlexander Leidinger 1373b611c801SAlexander Leidinger /** 1374b611c801SAlexander Leidinger * @todo Fill in "busy devices" fields. 1375b611c801SAlexander Leidinger * 1376b611c801SAlexander Leidinger * si->openedmidi = " MIDI devices 1377b611c801SAlexander Leidinger */ 1378b611c801SAlexander Leidinger bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 1379b611c801SAlexander Leidinger 1380b611c801SAlexander Leidinger /* 1381b611c801SAlexander Leidinger * Si->filler is a reserved array, but according to docs each 1382b611c801SAlexander Leidinger * element should be set to -1. 1383b611c801SAlexander Leidinger */ 1384b611c801SAlexander Leidinger for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++) 1385b611c801SAlexander Leidinger si->filler[i] = -1; 1386b611c801SAlexander Leidinger } 1387b611c801SAlexander Leidinger 138852f6e09eSAlexander Motin int 138952f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si) 139052f6e09eSAlexander Motin { 139152f6e09eSAlexander Motin struct snddev_info *d; 139252f6e09eSAlexander Motin int i, ncards; 139352f6e09eSAlexander Motin 139452f6e09eSAlexander Motin ncards = 0; 139552f6e09eSAlexander Motin 139652f6e09eSAlexander Motin for (i = 0; pcm_devclass != NULL && 139752f6e09eSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 139852f6e09eSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 139952f6e09eSAlexander Motin if (!PCM_REGISTERED(d)) 140052f6e09eSAlexander Motin continue; 140152f6e09eSAlexander Motin 140252f6e09eSAlexander Motin if (ncards++ != si->card) 140352f6e09eSAlexander Motin continue; 140452f6e09eSAlexander Motin 140590da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 140690da2b28SAriff Abdullah PCM_LOCK(d); 140752f6e09eSAlexander Motin 140852f6e09eSAlexander Motin strlcpy(si->shortname, device_get_nameunit(d->dev), 140952f6e09eSAlexander Motin sizeof(si->shortname)); 141052f6e09eSAlexander Motin strlcpy(si->longname, device_get_desc(d->dev), 141152f6e09eSAlexander Motin sizeof(si->longname)); 141252f6e09eSAlexander Motin strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 141352f6e09eSAlexander Motin si->intr_count = si->ack_count = 0; 141490da2b28SAriff Abdullah 141590da2b28SAriff Abdullah PCM_UNLOCK(d); 141690da2b28SAriff Abdullah 141752f6e09eSAlexander Motin return (0); 141852f6e09eSAlexander Motin } 141952f6e09eSAlexander Motin return (ENXIO); 142052f6e09eSAlexander Motin } 142152f6e09eSAlexander Motin 1422b611c801SAlexander Leidinger /************************************************************************/ 1423b611c801SAlexander Leidinger 14240739ea1dSSeigo Tanimura static int 14250739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data) 14260739ea1dSSeigo Tanimura { 1427b611c801SAlexander Leidinger int ret; 1428b611c801SAlexander Leidinger 14298109ec9dSJohn Baldwin ret = 0; 1430b611c801SAlexander Leidinger switch (type) { 1431b611c801SAlexander Leidinger case MOD_LOAD: 1432*13bebcd3SJohn Baldwin pcm_devclass = devclass_create("pcm"); 1433b611c801SAlexander Leidinger pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 1434b611c801SAlexander Leidinger break; 1435b611c801SAlexander Leidinger case MOD_UNLOAD: 1436b611c801SAlexander Leidinger if (pcmsg_unrhdr != NULL) { 1437b611c801SAlexander Leidinger delete_unrhdr(pcmsg_unrhdr); 1438b611c801SAlexander Leidinger pcmsg_unrhdr = NULL; 1439b611c801SAlexander Leidinger } 1440b611c801SAlexander Leidinger break; 14415525b53dSAlexander Motin case MOD_SHUTDOWN: 14425525b53dSAlexander Motin break; 1443b611c801SAlexander Leidinger default: 144490da2b28SAriff Abdullah ret = ENOTSUP; 1445b611c801SAlexander Leidinger } 1446b611c801SAlexander Leidinger 1447b611c801SAlexander Leidinger return ret; 14480739ea1dSSeigo Tanimura } 14490739ea1dSSeigo Tanimura 14500739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL); 14510739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER); 1452